fix: rename all packages from @mosaic/* to @mosaicstack/*
- Updated all package.json name fields and dependency references - Updated all TypeScript/JavaScript imports - Updated .woodpecker/publish.yml filters and registry paths - Updated tools/install.sh scope default - Updated .npmrc registry paths (worktree + host) - Enhanced update-checker.ts with checkForAllUpdates() multi-package support - Updated CLI update command to show table of all packages - Added KNOWN_PACKAGES, formatAllPackagesTable, getInstallAllCommand - Marked checkForUpdate() with @deprecated JSDoc Closes #391
This commit is contained in:
@@ -88,17 +88,17 @@ describe('formatUpdateNotice', () => {
|
||||
expect(notice).toContain('Update available');
|
||||
});
|
||||
|
||||
it('uses @mosaic/mosaic for installs', async () => {
|
||||
it('uses @mosaicstack/mosaic for installs', async () => {
|
||||
execSyncMock.mockImplementation((command: string) => {
|
||||
if (command.includes('ls -g --depth=0 --json')) {
|
||||
return JSON.stringify({
|
||||
dependencies: {
|
||||
'@mosaic/mosaic': { version: '0.0.19' },
|
||||
'@mosaicstack/mosaic': { version: '0.0.19' },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (command.includes('view @mosaic/mosaic version')) {
|
||||
if (command.includes('view @mosaicstack/mosaic version')) {
|
||||
return '0.0.20';
|
||||
}
|
||||
|
||||
@@ -111,26 +111,26 @@ describe('formatUpdateNotice', () => {
|
||||
|
||||
expect(result.current).toBe('0.0.19');
|
||||
expect(result.latest).toBe('0.0.20');
|
||||
expect(result.currentPackage).toBe('@mosaic/mosaic');
|
||||
expect(result.targetPackage).toBe('@mosaic/mosaic');
|
||||
expect(notice).toContain('@mosaic/mosaic@latest');
|
||||
expect(result.currentPackage).toBe('@mosaicstack/mosaic');
|
||||
expect(result.targetPackage).toBe('@mosaicstack/mosaic');
|
||||
expect(notice).toContain('@mosaicstack/mosaic@latest');
|
||||
});
|
||||
|
||||
it('does not query legacy @mosaic/cli package', async () => {
|
||||
it('does not query legacy @mosaicstack/cli package', async () => {
|
||||
execSyncMock.mockImplementation((command: string) => {
|
||||
if (command.includes('view @mosaic/cli')) {
|
||||
throw new Error('Should not query @mosaic/cli');
|
||||
if (command.includes('view @mosaicstack/cli')) {
|
||||
throw new Error('Should not query @mosaicstack/cli');
|
||||
}
|
||||
|
||||
if (command.includes('ls -g --depth=0 --json')) {
|
||||
return JSON.stringify({
|
||||
dependencies: {
|
||||
'@mosaic/mosaic': { version: '0.0.19' },
|
||||
'@mosaicstack/mosaic': { version: '0.0.19' },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (command.includes('view @mosaic/mosaic version')) {
|
||||
if (command.includes('view @mosaicstack/mosaic version')) {
|
||||
return '0.0.20';
|
||||
}
|
||||
|
||||
@@ -140,11 +140,11 @@ describe('formatUpdateNotice', () => {
|
||||
const { checkForUpdate } = await importUpdateChecker();
|
||||
const result = checkForUpdate({ skipCache: true });
|
||||
|
||||
expect(result.targetPackage).toBe('@mosaic/mosaic');
|
||||
expect(result.targetPackage).toBe('@mosaicstack/mosaic');
|
||||
expect(result.latest).toBe('0.0.20');
|
||||
// Verify no @mosaic/cli queries were made
|
||||
// Verify no @mosaicstack/cli queries were made
|
||||
const calls = execSyncMock.mock.calls.map((c: any[]) => c[0] as string);
|
||||
expect(calls.some((c) => c.includes('@mosaic/cli'))).toBe(false);
|
||||
expect(calls.some((c) => c.includes('@mosaicstack/cli'))).toBe(false);
|
||||
});
|
||||
|
||||
it('returns empty result when package is not installed', async () => {
|
||||
@@ -153,7 +153,7 @@ describe('formatUpdateNotice', () => {
|
||||
return JSON.stringify({ dependencies: {} });
|
||||
}
|
||||
|
||||
if (command.includes('view @mosaic/mosaic version')) {
|
||||
if (command.includes('view @mosaicstack/mosaic version')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ The installer:
|
||||
|
||||
- Downloads the framework from the monorepo archive
|
||||
- Installs it to `~/.config/mosaic/`
|
||||
- Installs `@mosaic/cli` globally via npm (TUI, gateway client, wizard)
|
||||
- Installs `@mosaicstack/cli` globally via npm (TUI, gateway client, wizard)
|
||||
- Adds `~/.config/mosaic/bin` to your PATH
|
||||
- Syncs runtime adapters and skills
|
||||
- Runs a health audit
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@mosaic/mosaic",
|
||||
"name": "@mosaicstack/mosaic",
|
||||
"version": "0.0.19",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -27,12 +27,12 @@
|
||||
"test": "vitest run --passWithNoTests"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mosaic/config": "workspace:*",
|
||||
"@mosaic/forge": "workspace:*",
|
||||
"@mosaic/macp": "workspace:*",
|
||||
"@mosaic/prdy": "workspace:*",
|
||||
"@mosaic/quality-rails": "workspace:*",
|
||||
"@mosaic/types": "workspace:*",
|
||||
"@mosaicstack/config": "workspace:*",
|
||||
"@mosaicstack/forge": "workspace:*",
|
||||
"@mosaicstack/macp": "workspace:*",
|
||||
"@mosaicstack/prdy": "workspace:*",
|
||||
"@mosaicstack/quality-rails": "workspace:*",
|
||||
"@mosaicstack/types": "workspace:*",
|
||||
"@clack/prompts": "^0.9.1",
|
||||
"commander": "^13.0.0",
|
||||
"ink": "^5.0.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { createRequire } from 'module';
|
||||
import { Command } from 'commander';
|
||||
import { registerQualityRails } from '@mosaic/quality-rails';
|
||||
import { registerQualityRails } from '@mosaicstack/quality-rails';
|
||||
import { registerAgentCommand } from './commands/agent.js';
|
||||
import { registerMissionCommand } from './commands/mission.js';
|
||||
// prdy is registered via launch.ts
|
||||
@@ -10,9 +10,9 @@ import { registerLaunchCommands } from './commands/launch.js';
|
||||
import { registerGatewayCommand } from './commands/gateway.js';
|
||||
import {
|
||||
backgroundUpdateCheck,
|
||||
checkForUpdate,
|
||||
formatUpdateNotice,
|
||||
getInstallCommand,
|
||||
checkForAllUpdates,
|
||||
formatAllPackagesTable,
|
||||
getInstallAllCommand,
|
||||
} from './runtime/update-checker.js';
|
||||
import { runWizard } from './wizard.js';
|
||||
import { ClackPrompter } from './prompter/clack-prompter.js';
|
||||
@@ -325,37 +325,35 @@ program
|
||||
.description('Check for and install Mosaic CLI updates')
|
||||
.option('--check', 'Check only, do not install')
|
||||
.action(async (opts: { check?: boolean }) => {
|
||||
// checkForUpdate and formatUpdateNotice imported statically above
|
||||
// checkForAllUpdates imported statically above
|
||||
const { execSync } = await import('node:child_process');
|
||||
|
||||
console.log('Checking for updates…');
|
||||
const result = checkForUpdate({ skipCache: true });
|
||||
const results = checkForAllUpdates({ skipCache: true });
|
||||
|
||||
if (!result.latest) {
|
||||
console.error('Could not reach the Mosaic registry.');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('');
|
||||
console.log(formatAllPackagesTable(results));
|
||||
|
||||
console.log(` Installed: ${result.current || '(none)'}`);
|
||||
console.log(` Latest: ${result.latest}`);
|
||||
|
||||
if (!result.updateAvailable) {
|
||||
console.log('\n✔ Up to date.');
|
||||
const outdated = results.filter((r: { updateAvailable: boolean }) => r.updateAvailable);
|
||||
if (outdated.length === 0) {
|
||||
const anyInstalled = results.some((r: { current: string }) => r.current);
|
||||
if (!anyInstalled) {
|
||||
console.error('No @mosaicstack/* packages are installed.');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('\n✔ All packages up to date.');
|
||||
return;
|
||||
}
|
||||
|
||||
const notice = formatUpdateNotice(result);
|
||||
if (notice) console.log(notice);
|
||||
|
||||
if (opts.check) {
|
||||
process.exit(2); // Signal to callers that an update exists
|
||||
}
|
||||
|
||||
console.log('Installing update…');
|
||||
console.log(`\nInstalling ${outdated.length} update(s)…`);
|
||||
try {
|
||||
// Relies on @mosaic:registry in ~/.npmrc — do NOT pass --registry
|
||||
// globally or non-@mosaic deps will 404 against the Gitea registry.
|
||||
execSync(getInstallCommand(result), {
|
||||
// Relies on @mosaicstack:registry in ~/.npmrc
|
||||
const cmd = getInstallAllCommand(outdated);
|
||||
execSync(cmd, {
|
||||
stdio: 'inherit',
|
||||
timeout: 60_000,
|
||||
});
|
||||
|
||||
@@ -91,10 +91,10 @@ export function resolveGatewayEntry(): string {
|
||||
return meta.entryPoint;
|
||||
}
|
||||
|
||||
// Try to resolve from globally installed @mosaic/gateway
|
||||
// Try to resolve from globally installed @mosaicstack/gateway
|
||||
try {
|
||||
const req = createRequire(import.meta.url);
|
||||
const pkgPath = req.resolve('@mosaic/gateway/package.json');
|
||||
const pkgPath = req.resolve('@mosaicstack/gateway/package.json');
|
||||
const mainEntry = join(resolve(pkgPath, '..'), 'dist', 'main.js');
|
||||
if (existsSync(mainEntry)) return mainEntry;
|
||||
} catch {
|
||||
@@ -210,8 +210,8 @@ function sleep(ms: number): Promise<void> {
|
||||
const GITEA_REGISTRY = 'https://git.mosaicstack.dev/api/packages/mosaic/npm/';
|
||||
|
||||
export function installGatewayPackage(): void {
|
||||
console.log('Installing @mosaic/gateway from Gitea registry...');
|
||||
execSync(`npm install -g @mosaic/gateway@latest --@mosaic:registry=${GITEA_REGISTRY}`, {
|
||||
console.log('Installing @mosaicstack/gateway from Gitea registry...');
|
||||
execSync(`npm install -g @mosaicstack/gateway@latest --@mosaic:registry=${GITEA_REGISTRY}`, {
|
||||
stdio: 'inherit',
|
||||
timeout: 120_000,
|
||||
});
|
||||
@@ -219,7 +219,7 @@ export function installGatewayPackage(): void {
|
||||
|
||||
export function uninstallGatewayPackage(): void {
|
||||
try {
|
||||
execSync('npm uninstall -g @mosaic/gateway', {
|
||||
execSync('npm uninstall -g @mosaicstack/gateway', {
|
||||
stdio: 'inherit',
|
||||
timeout: 60_000,
|
||||
});
|
||||
@@ -230,15 +230,15 @@ export function uninstallGatewayPackage(): void {
|
||||
|
||||
export function getInstalledGatewayVersion(): string | null {
|
||||
try {
|
||||
const output = execSync('npm ls -g @mosaic/gateway --json --depth=0', {
|
||||
const output = execSync('npm ls -g @mosaicstack/gateway --json --depth=0', {
|
||||
encoding: 'utf-8',
|
||||
timeout: 15_000,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
const data = JSON.parse(output) as {
|
||||
dependencies?: { '@mosaic/gateway'?: { version?: string } };
|
||||
dependencies?: { '@mosaicstack/gateway'?: { version?: string } };
|
||||
};
|
||||
return data.dependencies?.['@mosaic/gateway']?.version ?? null;
|
||||
return data.dependencies?.['@mosaicstack/gateway']?.version ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ async function doInstall(rl: ReturnType<typeof createInterface>, opts: InstallOp
|
||||
entryPoint = resolveGatewayEntry();
|
||||
} catch {
|
||||
console.error('Error: Gateway package not found after install.');
|
||||
console.error('Check that @mosaic/gateway installed correctly.');
|
||||
console.error('Check that @mosaicstack/gateway installed correctly.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -494,13 +494,13 @@ function delegateToScript(scriptPath: string, args: string[], env?: Record<strin
|
||||
|
||||
/**
|
||||
* Resolve a path under the framework tools directory. Prefers the version
|
||||
* bundled in the @mosaic/mosaic npm package (always matches the installed
|
||||
* bundled in the @mosaicstack/mosaic npm package (always matches the installed
|
||||
* CLI version) over the deployed copy in ~/.config/mosaic/ (may be stale).
|
||||
*/
|
||||
function resolveTool(...segments: string[]): string {
|
||||
try {
|
||||
const req = createRequire(import.meta.url);
|
||||
const mosaicPkg = dirname(req.resolve('@mosaic/mosaic/package.json'));
|
||||
const mosaicPkg = dirname(req.resolve('@mosaicstack/mosaic/package.json'));
|
||||
const bundled = join(mosaicPkg, 'framework', 'tools', ...segments);
|
||||
if (existsSync(bundled)) return bundled;
|
||||
} catch {
|
||||
|
||||
@@ -255,7 +255,7 @@ async function planMission(
|
||||
console.log(`Planning mission: ${mission.name}\n`);
|
||||
|
||||
try {
|
||||
const { runPrdWizard } = await import('@mosaic/prdy');
|
||||
const { runPrdWizard } = await import('@mosaicstack/prdy');
|
||||
await runPrdWizard({
|
||||
name: mission.name,
|
||||
projectPath: process.cwd(),
|
||||
|
||||
@@ -32,7 +32,7 @@ export function registerPrdyCommand(program: Command) {
|
||||
}
|
||||
|
||||
try {
|
||||
const { runPrdWizard } = await import('@mosaic/prdy');
|
||||
const { runPrdWizard } = await import('@mosaicstack/prdy');
|
||||
const name =
|
||||
typeof opts.init === 'string'
|
||||
? opts.init
|
||||
|
||||
@@ -3,9 +3,14 @@ export const VERSION = '0.0.0';
|
||||
export {
|
||||
backgroundUpdateCheck,
|
||||
checkForUpdate,
|
||||
checkForAllUpdates,
|
||||
formatUpdateNotice,
|
||||
formatAllPackagesTable,
|
||||
getInstallCommand,
|
||||
getInstallAllCommand,
|
||||
getInstalledVersion,
|
||||
getLatestVersion,
|
||||
semverLt,
|
||||
type UpdateCheckResult,
|
||||
type PackageUpdateResult,
|
||||
} from './runtime/update-checker.js';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Mosaic update checker — compares the installed @mosaic/mosaic package
|
||||
* Mosaic update checker — compares the installed @mosaicstack/mosaic package
|
||||
* against the Gitea npm registry and reports when an upgrade is available.
|
||||
*
|
||||
* Used by:
|
||||
@@ -39,8 +39,8 @@ export interface UpdateCheckResult {
|
||||
|
||||
// ─── Constants ──────────────────────────────────────────────────────────────
|
||||
|
||||
const REGISTRY = 'https://git.mosaicstack.dev/api/packages/mosaic/npm/';
|
||||
const PKG = '@mosaic/mosaic';
|
||||
const REGISTRY = 'https://git.mosaicstack.dev/api/packages/mosaicstack/npm/';
|
||||
const PKG = '@mosaicstack/mosaic';
|
||||
const CACHE_DIR = join(homedir(), '.cache', 'mosaic');
|
||||
const CACHE_FILE = join(CACHE_DIR, 'update-check.json');
|
||||
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
||||
@@ -124,20 +124,42 @@ export function semverLt(a: string, b: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ─── Known packages for checkForAllUpdates() ──────────────────────────────
|
||||
|
||||
const KNOWN_PACKAGES = [
|
||||
'@mosaicstack/mosaic',
|
||||
'@mosaicstack/cli',
|
||||
'@mosaicstack/gateway',
|
||||
'@mosaicstack/quality-rails',
|
||||
];
|
||||
|
||||
// ─── Multi-package types ──────────────────────────────────────────────────
|
||||
|
||||
export interface PackageUpdateResult {
|
||||
/** Package name */
|
||||
package: string;
|
||||
/** Currently installed version (empty if not installed) */
|
||||
current: string;
|
||||
/** Latest published version (empty if check failed) */
|
||||
latest: string;
|
||||
/** True when a newer version is available */
|
||||
updateAvailable: boolean;
|
||||
}
|
||||
|
||||
// ─── Cache ──────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Cache stores only the latest registry version (the expensive network call).
|
||||
/** Cache stores the latest registry versions for all checked packages.
|
||||
* The installed version is always checked fresh — it's a local `npm ls`. */
|
||||
interface RegistryCache {
|
||||
latest: string;
|
||||
interface AllPackagesCache {
|
||||
packages: Record<string, { latest: string }>;
|
||||
checkedAt: string;
|
||||
registry: string;
|
||||
}
|
||||
|
||||
function readCache(): RegistryCache | null {
|
||||
function readAllCache(): AllPackagesCache | null {
|
||||
try {
|
||||
if (!existsSync(CACHE_FILE)) return null;
|
||||
const raw = JSON.parse(readFileSync(CACHE_FILE, 'utf-8')) as RegistryCache;
|
||||
const raw = JSON.parse(readFileSync(CACHE_FILE, 'utf-8')) as AllPackagesCache;
|
||||
const age = Date.now() - new Date(raw.checkedAt).getTime();
|
||||
if (age > CACHE_TTL_MS) return null;
|
||||
return raw;
|
||||
@@ -146,7 +168,7 @@ function readCache(): RegistryCache | null {
|
||||
}
|
||||
}
|
||||
|
||||
function writeCache(entry: RegistryCache): void {
|
||||
function writeAllCache(entry: AllPackagesCache): void {
|
||||
try {
|
||||
mkdirSync(CACHE_DIR, { recursive: true });
|
||||
writeFileSync(CACHE_FILE, JSON.stringify(entry, null, 2) + '\n', 'utf-8');
|
||||
@@ -155,10 +177,28 @@ function writeCache(entry: RegistryCache): void {
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy single-package cache (backward compat)
|
||||
type RegistryCache = { latest: string; checkedAt: string; registry: string };
|
||||
|
||||
function readCache(): RegistryCache | null {
|
||||
const c = readAllCache();
|
||||
if (!c) return null;
|
||||
const mosaicLatest = c.packages[PKG]?.latest;
|
||||
if (!mosaicLatest) return null;
|
||||
return { latest: mosaicLatest, checkedAt: c.checkedAt, registry: c.registry };
|
||||
}
|
||||
|
||||
function writeCache(entry: RegistryCache): void {
|
||||
const existing = readAllCache();
|
||||
const packages = { ...(existing?.packages ?? {}) };
|
||||
packages[PKG] = { latest: entry.latest };
|
||||
writeAllCache({ packages, checkedAt: entry.checkedAt, registry: entry.registry });
|
||||
}
|
||||
|
||||
// ─── Public API ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Get the currently installed @mosaic/mosaic version.
|
||||
* Get the currently installed @mosaicstack/mosaic version.
|
||||
*/
|
||||
export function getInstalledVersion(): { name: string; version: string } {
|
||||
try {
|
||||
@@ -179,7 +219,7 @@ export function getInstalledVersion(): { name: string; version: string } {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the latest published @mosaic/mosaic version from the Gitea npm registry.
|
||||
* Fetch the latest published @mosaicstack/mosaic version from the Gitea npm registry.
|
||||
* Returns empty string on failure.
|
||||
*/
|
||||
export function getLatestVersion(): { name: string; version: string } {
|
||||
@@ -199,6 +239,9 @@ export function getInstallCommand(result: Pick<UpdateCheckResult, 'targetPackage
|
||||
* installed version fresh (local npm ls is cheap, caching it causes stale
|
||||
* "update available" banners after an upgrade).
|
||||
* Never throws.
|
||||
*
|
||||
* @deprecated Use checkForAllUpdates() for multi-package checking.
|
||||
* This function is kept for backward compatibility.
|
||||
*/
|
||||
export function checkForUpdate(options?: { skipCache?: boolean }): UpdateCheckResult {
|
||||
const currentInfo = getInstalledVersion();
|
||||
@@ -284,3 +327,146 @@ export function backgroundUpdateCheck(): void {
|
||||
// Silently ignore — never block the user
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Multi-package update check ────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Get the currently installed versions of all globally-installed @mosaicstack/* packages.
|
||||
*/
|
||||
function getInstalledVersions(): Record<string, string> {
|
||||
const result: Record<string, string> = {};
|
||||
try {
|
||||
const raw = npmExec('ls -g --depth=0 --json 2>/dev/null', 3000);
|
||||
if (raw) {
|
||||
const data = JSON.parse(raw) as {
|
||||
dependencies?: Record<string, { version?: string }>;
|
||||
};
|
||||
for (const [name, info] of Object.entries(data?.dependencies ?? {})) {
|
||||
if (name.startsWith('@mosaicstack/') && info.version) {
|
||||
result[name] = info.version;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// fall through
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the latest published version of a single package from the registry.
|
||||
*/
|
||||
function getLatestVersionFor(pkgName: string): string {
|
||||
return npmExec(`view ${pkgName} version --registry=${REGISTRY}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all known @mosaicstack/* packages for updates.
|
||||
* Returns an array of per-package results, sorted by package name.
|
||||
* Never throws.
|
||||
*/
|
||||
export function checkForAllUpdates(options?: { skipCache?: boolean }): PackageUpdateResult[] {
|
||||
const installed = getInstalledVersions();
|
||||
const checkedAt = new Date().toISOString();
|
||||
|
||||
// Resolve latest versions (from cache or network)
|
||||
let latestVersions: Record<string, string>;
|
||||
|
||||
if (!options?.skipCache) {
|
||||
const cached = readAllCache();
|
||||
if (cached) {
|
||||
latestVersions = {};
|
||||
for (const pkg of KNOWN_PACKAGES) {
|
||||
const cachedLatest = cached.packages[pkg]?.latest;
|
||||
if (cachedLatest) {
|
||||
latestVersions[pkg] = cachedLatest;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
latestVersions = {};
|
||||
for (const pkg of KNOWN_PACKAGES) {
|
||||
const v = getLatestVersionFor(pkg);
|
||||
if (v) latestVersions[pkg] = v;
|
||||
}
|
||||
writeAllCache({
|
||||
packages: Object.fromEntries(
|
||||
Object.entries(latestVersions).map(([k, v]) => [k, { latest: v }]),
|
||||
),
|
||||
checkedAt,
|
||||
registry: REGISTRY,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
latestVersions = {};
|
||||
for (const pkg of KNOWN_PACKAGES) {
|
||||
const v = getLatestVersionFor(pkg);
|
||||
if (v) latestVersions[pkg] = v;
|
||||
}
|
||||
writeAllCache({
|
||||
packages: Object.fromEntries(
|
||||
Object.entries(latestVersions).map(([k, v]) => [k, { latest: v }]),
|
||||
),
|
||||
checkedAt,
|
||||
registry: REGISTRY,
|
||||
});
|
||||
}
|
||||
|
||||
const results: PackageUpdateResult[] = [];
|
||||
for (const pkg of KNOWN_PACKAGES) {
|
||||
const current = installed[pkg] ?? '';
|
||||
const latest = latestVersions[pkg] ?? '';
|
||||
results.push({
|
||||
package: pkg,
|
||||
current,
|
||||
latest,
|
||||
updateAvailable: !!(current && latest && semverLt(current, latest)),
|
||||
});
|
||||
}
|
||||
|
||||
return results.sort((a, b) => a.package.localeCompare(b.package));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the install command for all outdated packages.
|
||||
*/
|
||||
export function getInstallAllCommand(outdated: PackageUpdateResult[]): string {
|
||||
const pkgs = outdated.filter((r) => r.updateAvailable).map((r) => `${r.package}@latest`);
|
||||
if (pkgs.length === 0) return '';
|
||||
return `npm i -g ${pkgs.join(' ')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a table showing all packages with their current/latest versions.
|
||||
*/
|
||||
export function formatAllPackagesTable(results: PackageUpdateResult[]): string {
|
||||
if (results.length === 0) return 'No @mosaicstack/* packages found.';
|
||||
|
||||
const hasUpdate = results.some((r) => r.updateAvailable);
|
||||
const nameWidth = Math.max(...results.map((r) => r.package.length), 10);
|
||||
const verWidth = 10;
|
||||
|
||||
const header =
|
||||
' ' +
|
||||
'Package'.padEnd(nameWidth + 2) +
|
||||
'Current'.padEnd(verWidth + 2) +
|
||||
'Latest'.padEnd(verWidth + 2) +
|
||||
'Status';
|
||||
const sep = ' ' + '-'.repeat(header.length - 2);
|
||||
|
||||
const rows = results.map((r) => {
|
||||
const status = !r.current
|
||||
? 'not installed'
|
||||
: r.updateAvailable
|
||||
? '⬆ update available'
|
||||
: '✔ up to date';
|
||||
return (
|
||||
' ' +
|
||||
r.package.padEnd(nameWidth + 2) +
|
||||
(r.current || '-').padEnd(verWidth + 2) +
|
||||
(r.latest || '-').padEnd(verWidth + 2) +
|
||||
status
|
||||
);
|
||||
});
|
||||
|
||||
return [header, sep, ...rows].join('\n');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { Box, useApp, useInput } from 'ink';
|
||||
import type { ParsedCommand } from '@mosaic/types';
|
||||
import type { ParsedCommand } from '@mosaicstack/types';
|
||||
import { TopBar } from './components/top-bar.js';
|
||||
import { BottomBar } from './components/bottom-bar.js';
|
||||
import { MessageList } from './components/message-list.js';
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { parseSlashCommand } from './parse.js';
|
||||
import { CommandRegistry } from './registry.js';
|
||||
import type { CommandDef } from '@mosaic/types';
|
||||
import type { CommandDef } from '@mosaicstack/types';
|
||||
|
||||
// ─── Parse + Registry Round-trip ─────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParsedCommand } from '@mosaic/types';
|
||||
import type { ParsedCommand } from '@mosaicstack/types';
|
||||
import { commandRegistry } from '../registry.js';
|
||||
|
||||
export function executeHelp(_parsed: ParsedCommand): string {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParsedCommand } from '@mosaic/types';
|
||||
import type { ParsedCommand } from '@mosaicstack/types';
|
||||
|
||||
export interface StatusContext {
|
||||
connected: boolean;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParsedCommand } from '@mosaic/types';
|
||||
import type { ParsedCommand } from '@mosaicstack/types';
|
||||
|
||||
export function parseSlashCommand(input: string): ParsedCommand | null {
|
||||
const match = input.match(/^\/([a-z][a-z0-9:_-]*)\s*(.*)?$/i);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CommandDef, CommandManifest } from '@mosaic/types';
|
||||
import type { CommandDef, CommandManifest } from '@mosaicstack/types';
|
||||
|
||||
// Local-only commands (work even when gateway is disconnected)
|
||||
const LOCAL_COMMANDS: CommandDef[] = [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import type { RoutingDecisionInfo } from '@mosaic/types';
|
||||
import type { RoutingDecisionInfo } from '@mosaicstack/types';
|
||||
import type { TokenUsage } from '../hooks/use-socket.js';
|
||||
import type { GitInfo } from '../hooks/use-git-info.js';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import type { CommandDef, CommandArgDef } from '@mosaic/types';
|
||||
import type { CommandDef, CommandArgDef } from '@mosaicstack/types';
|
||||
|
||||
interface CommandAutocompleteProps {
|
||||
commands: CommandDef[];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Box, Text, useInput } from 'ink';
|
||||
import TextInput from 'ink-text-input';
|
||||
import type { ParsedCommand, CommandDef } from '@mosaic/types';
|
||||
import type { ParsedCommand, CommandDef } from '@mosaicstack/types';
|
||||
import { parseSlashCommand, commandRegistry } from '../commands/index.js';
|
||||
import { CommandAutocomplete } from './command-autocomplete.js';
|
||||
import { useInputHistory } from '../hooks/use-input-history.js';
|
||||
|
||||
@@ -15,7 +15,7 @@ import type {
|
||||
SlashCommandResultPayload,
|
||||
SystemReloadPayload,
|
||||
RoutingDecisionInfo,
|
||||
} from '@mosaic/types';
|
||||
} from '@mosaicstack/types';
|
||||
import { commandRegistry } from '../commands/index.js';
|
||||
|
||||
export interface ToolCall {
|
||||
|
||||
Reference in New Issue
Block a user