#!/usr/bin/env node import { Command } from 'commander'; import { buildPrdyCli } from '@mosaic/prdy'; import { createQualityRailsCli } from '@mosaic/quality-rails'; const program = new Command(); program.name('mosaic').description('Mosaic Stack CLI').version('0.0.0'); // ─── login ────────────────────────────────────────────────────────────── program .command('login') .description('Sign in to a Mosaic gateway') .option('-g, --gateway ', 'Gateway URL', 'http://localhost:4000') .option('-e, --email ', 'Email address') .option('-p, --password ', 'Password') .action(async (opts: { gateway: string; email?: string; password?: string }) => { const { signIn, saveSession } = await import('./auth.js'); let email = opts.email; let password = opts.password; if (!email || !password) { const readline = await import('node:readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const ask = (q: string): Promise => new Promise((resolve) => rl.question(q, resolve)); if (!email) email = await ask('Email: '); if (!password) password = await ask('Password: '); rl.close(); } try { const auth = await signIn(opts.gateway, email, password); saveSession(opts.gateway, auth); console.log(`Signed in as ${auth.email} (${opts.gateway})`); } catch (err) { console.error(err instanceof Error ? err.message : String(err)); process.exit(1); } }); // ─── tui ──────────────────────────────────────────────────────────────── program .command('tui') .description('Launch interactive TUI connected to the gateway') .option('-g, --gateway ', 'Gateway URL', 'http://localhost:4000') .option('-c, --conversation ', 'Resume a conversation by ID') .action(async (opts: { gateway: string; conversation?: string }) => { const { loadSession, validateSession, signIn, saveSession } = await import('./auth.js'); // Try loading saved session let session = loadSession(opts.gateway); if (session) { const valid = await validateSession(opts.gateway, session.cookie); if (!valid) { console.log('Session expired. Please sign in again.'); session = null; } } // No valid session — prompt for credentials if (!session) { const readline = await import('node:readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const ask = (q: string): Promise => new Promise((resolve) => rl.question(q, resolve)); console.log(`Sign in to ${opts.gateway}`); const email = await ask('Email: '); const password = await ask('Password: '); rl.close(); try { const auth = await signIn(opts.gateway, email, password); saveSession(opts.gateway, auth); session = auth; console.log(`Signed in as ${auth.email}\n`); } catch (err) { console.error(err instanceof Error ? err.message : String(err)); process.exit(1); } } // Dynamic import to avoid loading React/Ink for other commands const { render } = await import('ink'); const React = await import('react'); const { TuiApp } = await import('./tui/app.js'); render( React.createElement(TuiApp, { gatewayUrl: opts.gateway, conversationId: opts.conversation, sessionCookie: session.cookie, }), ); }); // ─── prdy ─────────────────────────────────────────────────────────────── const prdyWrapper = buildPrdyCli(); const prdyCmd = prdyWrapper.commands.find((c) => c.name() === 'prdy'); if (prdyCmd !== undefined) { program.addCommand(prdyCmd as unknown as Command); } // ─── quality-rails ────────────────────────────────────────────────────── const qrWrapper = createQualityRailsCli(); const qrCmd = qrWrapper.commands.find((c) => c.name() === 'quality-rails'); if (qrCmd !== undefined) { program.addCommand(qrCmd as unknown as Command); } // ─── wizard ───────────────────────────────────────────────────────────── program .command('wizard') .description('Run the Mosaic installation wizard') .option('--non-interactive', 'Run without prompts (uses defaults + flags)') .option('--source-dir ', 'Source directory for framework files') .option('--mosaic-home ', 'Target config directory') .option('--name ', 'Agent name') .option('--role ', 'Agent role description') .option('--style