feat(cli): command architecture — agents, missions, gateway-aware prdy (#158)
Some checks failed
ci/woodpecker/push/ci Pipeline failed
Some checks failed
ci/woodpecker/push/ci Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #158.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Command } from 'commander';
|
||||
import { buildPrdyCli } from '@mosaic/prdy';
|
||||
import { createQualityRailsCli } from '@mosaic/quality-rails';
|
||||
import { registerAgentCommand } from './commands/agent.js';
|
||||
import { registerMissionCommand } from './commands/mission.js';
|
||||
import { registerPrdyCommand } from './commands/prdy.js';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
@@ -51,8 +53,17 @@ program
|
||||
.option('-c, --conversation <id>', 'Resume a conversation by ID')
|
||||
.option('-m, --model <modelId>', 'Model ID to use (e.g. gpt-4o, llama3.2)')
|
||||
.option('-p, --provider <provider>', 'Provider to use (e.g. openai, ollama)')
|
||||
.option('--agent <idOrName>', 'Connect to a specific agent')
|
||||
.option('--project <idOrName>', 'Scope session to project')
|
||||
.action(
|
||||
async (opts: { gateway: string; conversation?: string; model?: string; provider?: string }) => {
|
||||
async (opts: {
|
||||
gateway: string;
|
||||
conversation?: string;
|
||||
model?: string;
|
||||
provider?: string;
|
||||
agent?: string;
|
||||
project?: string;
|
||||
}) => {
|
||||
const { loadSession, validateSession, signIn, saveSession } = await import('./auth.js');
|
||||
|
||||
// Try loading saved session
|
||||
@@ -89,6 +100,50 @@ program
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve agent ID if --agent was passed by name
|
||||
let agentId: string | undefined;
|
||||
let agentName: string | undefined;
|
||||
if (opts.agent) {
|
||||
try {
|
||||
const { fetchAgentConfigs } = await import('./tui/gateway-api.js');
|
||||
const agents = await fetchAgentConfigs(opts.gateway, session.cookie);
|
||||
const match = agents.find((a) => a.id === opts.agent || a.name === opts.agent);
|
||||
if (match) {
|
||||
agentId = match.id;
|
||||
agentName = match.name;
|
||||
} else {
|
||||
console.error(`Agent "${opts.agent}" not found.`);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`Failed to resolve agent: ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve project ID if --project was passed by name
|
||||
let projectId: string | undefined;
|
||||
if (opts.project) {
|
||||
try {
|
||||
const { fetchProjects } = await import('./tui/gateway-api.js');
|
||||
const projects = await fetchProjects(opts.gateway, session.cookie);
|
||||
const match = projects.find((p) => p.id === opts.project || p.name === opts.project);
|
||||
if (match) {
|
||||
projectId = match.id;
|
||||
} else {
|
||||
console.error(`Project "${opts.project}" not found.`);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`Failed to resolve project: ${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');
|
||||
@@ -101,6 +156,9 @@ program
|
||||
sessionCookie: session.cookie,
|
||||
initialModel: opts.model,
|
||||
initialProvider: opts.provider,
|
||||
agentId,
|
||||
agentName: agentName ?? undefined,
|
||||
projectId,
|
||||
}),
|
||||
);
|
||||
},
|
||||
@@ -115,23 +173,12 @@ sessionsCmd
|
||||
.description('List active agent sessions')
|
||||
.option('-g, --gateway <url>', 'Gateway URL', 'http://localhost:4000')
|
||||
.action(async (opts: { gateway: string }) => {
|
||||
const { loadSession, validateSession } = await import('./auth.js');
|
||||
const { withAuth } = await import('./commands/with-auth.js');
|
||||
const auth = await withAuth(opts.gateway);
|
||||
const { fetchSessions } = await import('./tui/gateway-api.js');
|
||||
|
||||
const session = loadSession(opts.gateway);
|
||||
if (!session) {
|
||||
console.error('Not signed in. Run `mosaic login` first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const valid = await validateSession(opts.gateway, session.cookie);
|
||||
if (!valid) {
|
||||
console.error('Session expired. Run `mosaic login` again.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await fetchSessions(opts.gateway, session.cookie);
|
||||
const result = await fetchSessions(auth.gateway, auth.cookie);
|
||||
if (result.total === 0) {
|
||||
console.log('No active sessions.');
|
||||
return;
|
||||
@@ -193,23 +240,12 @@ sessionsCmd
|
||||
.description('Terminate an active agent session')
|
||||
.option('-g, --gateway <url>', 'Gateway URL', 'http://localhost:4000')
|
||||
.action(async (id: string, opts: { gateway: string }) => {
|
||||
const { loadSession, validateSession } = await import('./auth.js');
|
||||
const { withAuth } = await import('./commands/with-auth.js');
|
||||
const auth = await withAuth(opts.gateway);
|
||||
const { deleteSession } = await import('./tui/gateway-api.js');
|
||||
|
||||
const session = loadSession(opts.gateway);
|
||||
if (!session) {
|
||||
console.error('Not signed in. Run `mosaic login` first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const valid = await validateSession(opts.gateway, session.cookie);
|
||||
if (!valid) {
|
||||
console.error('Session expired. Run `mosaic login` again.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteSession(opts.gateway, session.cookie, id);
|
||||
await deleteSession(auth.gateway, auth.cookie, id);
|
||||
console.log(`Session ${id} destroyed.`);
|
||||
} catch (err) {
|
||||
console.error(err instanceof Error ? err.message : String(err));
|
||||
@@ -217,13 +253,17 @@ sessionsCmd
|
||||
}
|
||||
});
|
||||
|
||||
// ─── prdy ───────────────────────────────────────────────────────────────
|
||||
// ─── agent ─────────────────────────────────────────────────────────────
|
||||
|
||||
const prdyWrapper = buildPrdyCli();
|
||||
const prdyCmd = prdyWrapper.commands.find((c) => c.name() === 'prdy');
|
||||
if (prdyCmd !== undefined) {
|
||||
program.addCommand(prdyCmd as unknown as Command);
|
||||
}
|
||||
registerAgentCommand(program);
|
||||
|
||||
// ─── mission ───────────────────────────────────────────────────────────
|
||||
|
||||
registerMissionCommand(program);
|
||||
|
||||
// ─── prdy ──────────────────────────────────────────────────────────────
|
||||
|
||||
registerPrdyCommand(program);
|
||||
|
||||
// ─── quality-rails ──────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user