import type { Command } from 'commander'; import { createDb, type DbHandle } from '@mosaicstack/db'; import { createBrain } from './brain.js'; /** * Build and attach the `brain` subcommand tree onto an existing Commander program. * Uses the caller's Command instance to avoid cross-package Commander version mismatches. */ export function registerBrainCommand(parent: Command): void { const brain = parent.command('brain').description('Inspect and manage brain data stores'); // ─── shared DB option helper ───────────────────────────────────────────── function addDbOption(cmd: Command): Command { return cmd.option( '--db ', 'PostgreSQL connection string (overrides MOSAIC_DB_URL)', ); } function resolveDb(opts: { db?: string }): ReturnType { const connectionString = opts.db ?? process.env['MOSAIC_DB_URL']; if (!connectionString) { console.error('No DB connection string provided. Pass --db or set MOSAIC_DB_URL.'); process.exit(1); } const handle: DbHandle = createDb(connectionString); return createBrain(handle.db); } // ─── projects ──────────────────────────────────────────────────────────── const projects = brain.command('projects').description('Manage projects'); addDbOption( projects .command('list') .description('List all projects') .option('--limit ', 'Maximum number of results', '50'), ).action(async (opts: { db?: string; limit: string }) => { const b = resolveDb(opts); const limit = parseInt(opts.limit, 10); const rows = await b.projects.findAll(); const sliced = rows.slice(0, limit); if (sliced.length === 0) { console.log('No projects found.'); return; } for (const p of sliced) { console.log(`${p.id} ${p.name}`); } }); addDbOption( projects .command('create ') .description('Create a new project') .requiredOption('--owner-id ', 'Owner user ID'), ).action(async (name: string, opts: { db?: string; ownerId: string }) => { const b = resolveDb(opts); const created = await b.projects.create({ name, ownerId: opts.ownerId, ownerType: 'user', }); console.log(`Created project: ${created.id} ${created.name}`); }); // ─── missions ──────────────────────────────────────────────────────────── const missions = brain.command('missions').description('Manage missions'); addDbOption( missions .command('list') .description('List all missions') .option('--limit ', 'Maximum number of results', '50') .option('--project ', 'Filter by project ID'), ).action(async (opts: { db?: string; limit: string; project?: string }) => { const b = resolveDb(opts); const limit = parseInt(opts.limit, 10); const rows = opts.project ? await b.missions.findByProject(opts.project) : await b.missions.findAll(); const sliced = rows.slice(0, limit); if (sliced.length === 0) { console.log('No missions found.'); return; } for (const m of sliced) { console.log(`${m.id} ${m.name}`); } }); // ─── tasks ──────────────────────────────────────────────────────────────── const tasks = brain.command('tasks').description('Manage generic tasks'); addDbOption( tasks .command('list') .description('List all tasks') .option('--limit ', 'Maximum number of results', '50') .option('--project ', 'Filter by project ID'), ).action(async (opts: { db?: string; limit: string; project?: string }) => { const b = resolveDb(opts); const limit = parseInt(opts.limit, 10); const rows = opts.project ? await b.tasks.findByProject(opts.project) : await b.tasks.findAll(); const sliced = rows.slice(0, limit); if (sliced.length === 0) { console.log('No tasks found.'); return; } for (const t of sliced) { console.log(`${t.id} ${t.title} [${t.status}]`); } }); // ─── conversations ──────────────────────────────────────────────────────── const conversations = brain.command('conversations').description('Manage conversations'); addDbOption( conversations .command('list') .description('List conversations for a user') .option('--limit ', 'Maximum number of results', '50') .requiredOption('--user-id ', 'User ID to scope the query'), ).action(async (opts: { db?: string; limit: string; userId: string }) => { const b = resolveDb(opts); const limit = parseInt(opts.limit, 10); const rows = await b.conversations.findAll(opts.userId); const sliced = rows.slice(0, limit); if (sliced.length === 0) { console.log('No conversations found.'); return; } for (const c of sliced) { console.log(`${c.id} ${c.title ?? '(untitled)'}`); } }); }