feat(cli): add --server / AGENTHUB_SERVER remote mode wiring
This commit is contained in:
parent
fb3da93f59
commit
407202f092
190
src/cli/index.ts
190
src/cli/index.ts
@ -7,24 +7,46 @@ import { handoffCreate, handoffRead, handoffList } from './commands/handoff.js';
|
|||||||
import { decisionCreate, decisionList } from './commands/decision.js';
|
import { decisionCreate, decisionList } from './commands/decision.js';
|
||||||
import { delegate } from './commands/delegate.js';
|
import { delegate } from './commands/delegate.js';
|
||||||
import { serverStart } from './commands/server.js';
|
import { serverStart } from './commands/server.js';
|
||||||
|
import { remoteClient } from './remoteClient.js';
|
||||||
|
|
||||||
|
function getServerUrl(program: Command): string | undefined {
|
||||||
|
return (program.opts().server as string | undefined) || process.env.AGENTHUB_SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remoteOnly(): never {
|
||||||
|
console.error('Remote mode is not supported for this command. Run it locally or omit --server.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
export function createProgram(cwd: string): Command {
|
export function createProgram(cwd: string): Command {
|
||||||
const program = new Command('agenthub')
|
const program = new Command('agenthub')
|
||||||
.description('Local coordination layer for AI coding agents')
|
.description('Local coordination layer for AI coding agents')
|
||||||
.version('0.1.0');
|
.version('0.1.0')
|
||||||
|
.option('--server <url>', 'AgentHub server URL (env: AGENTHUB_SERVER)');
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('init')
|
.command('init')
|
||||||
.description('Initialize AgentHub in the current directory')
|
.description('Initialize AgentHub in the current directory')
|
||||||
.option('-n, --project-name <name>', 'Project name')
|
.option('-n, --project-name <name>', 'Project name')
|
||||||
.option('-y, --yes', 'Use defaults without prompts')
|
.option('-y, --yes', 'Use defaults without prompts')
|
||||||
.action((options) => init(cwd, options));
|
.action((options) => {
|
||||||
|
if (getServerUrl(program)) remoteOnly();
|
||||||
|
init(cwd, options);
|
||||||
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('status')
|
.command('status')
|
||||||
.description('Show project status')
|
.description('Show project status')
|
||||||
.option('-u, --update', 'Regenerate status before showing')
|
.option('-u, --update', 'Regenerate status before showing')
|
||||||
.action((options) => status(cwd, options));
|
.action(async (options) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const body = options.update ? await remoteClient.updateStatus(serverUrl) : await remoteClient.getStatus(serverUrl);
|
||||||
|
console.log(body);
|
||||||
|
} else {
|
||||||
|
status(cwd, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const memoryCmd = new Command('memory').description('Manage memory entries');
|
const memoryCmd = new Command('memory').description('Manage memory entries');
|
||||||
memoryCmd
|
memoryCmd
|
||||||
@ -33,15 +55,41 @@ export function createProgram(cwd: string): Command {
|
|||||||
.option('--title <title>', 'Title')
|
.option('--title <title>', 'Title')
|
||||||
.option('--category <category>', 'Category')
|
.option('--category <category>', 'Category')
|
||||||
.option('--content <content>', 'Content')
|
.option('--content <content>', 'Content')
|
||||||
.action((options) => memoryAdd(cwd, options));
|
.action(async (options) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const memory = await remoteClient.addMemory(serverUrl, options);
|
||||||
|
console.log(`Memory saved as ${memory.id}.`);
|
||||||
|
} else {
|
||||||
|
await memoryAdd(cwd, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
memoryCmd
|
memoryCmd
|
||||||
.command('search <query>')
|
.command('search <query>')
|
||||||
.description('Search memory and tasks')
|
.description('Search memory and tasks')
|
||||||
.action((query) => memorySearch(cwd, query));
|
.action(async (query) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const results = await remoteClient.searchMemory(serverUrl, query);
|
||||||
|
if (results.length === 0) { console.log('No results found.'); return; }
|
||||||
|
for (const r of results) console.log(`[${r.type}] ${r.id}: ${r.title}`);
|
||||||
|
} else {
|
||||||
|
memorySearch(cwd, query);
|
||||||
|
}
|
||||||
|
});
|
||||||
memoryCmd
|
memoryCmd
|
||||||
.command('list')
|
.command('list')
|
||||||
.description('List memory entries')
|
.description('List memory entries')
|
||||||
.action(() => memoryList(cwd));
|
.action(async () => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const memories = await remoteClient.listMemory(serverUrl);
|
||||||
|
if (memories.length === 0) { console.log('No memory entries found.'); return; }
|
||||||
|
for (const m of memories) console.log(`${m.id}: ${m.title}`);
|
||||||
|
} else {
|
||||||
|
memoryList(cwd);
|
||||||
|
}
|
||||||
|
});
|
||||||
program.addCommand(memoryCmd);
|
program.addCommand(memoryCmd);
|
||||||
|
|
||||||
const taskCmd = new Command('task').description('Manage tasks');
|
const taskCmd = new Command('task').description('Manage tasks');
|
||||||
@ -51,26 +99,69 @@ export function createProgram(cwd: string): Command {
|
|||||||
.option('--title <title>', 'Title')
|
.option('--title <title>', 'Title')
|
||||||
.option('--role <role>', 'Role')
|
.option('--role <role>', 'Role')
|
||||||
.option('--priority <priority>', 'Priority')
|
.option('--priority <priority>', 'Priority')
|
||||||
.action((options) => taskCreate(cwd, options));
|
.action(async (options) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const task = await remoteClient.createTask(serverUrl, options);
|
||||||
|
console.log(`Created ${task.id}: ${task.title}`);
|
||||||
|
} else {
|
||||||
|
await taskCreate(cwd, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
taskCmd
|
taskCmd
|
||||||
.command('list')
|
.command('list')
|
||||||
.description('List tasks')
|
.description('List tasks')
|
||||||
.option('--status <status>', 'Filter by status')
|
.option('--status <status>', 'Filter by status')
|
||||||
.option('--role <role>', 'Filter by role')
|
.option('--role <role>', 'Filter by role')
|
||||||
.action((options) => taskList(cwd, options));
|
.action(async (options) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const tasks = await remoteClient.listTasks(serverUrl, options);
|
||||||
|
if (tasks.length === 0) { console.log('No tasks found.'); return; }
|
||||||
|
for (const t of tasks) console.log(`${t.id} [${t.status}] (${t.role ?? 'unassigned'}) ${t.title}`);
|
||||||
|
} else {
|
||||||
|
taskList(cwd, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
taskCmd
|
taskCmd
|
||||||
.command('show <id>')
|
.command('show <id>')
|
||||||
.description('Show a task')
|
.description('Show a task')
|
||||||
.action((id) => taskShow(cwd, id));
|
.action(async (id) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const { task, body } = await remoteClient.getTask(serverUrl, id);
|
||||||
|
console.log(`# ${task.title}`);
|
||||||
|
console.log(`Status: ${task.status} | Role: ${task.role ?? '-'} | Priority: ${task.priority}`);
|
||||||
|
console.log('\n' + body);
|
||||||
|
} else {
|
||||||
|
taskShow(cwd, id);
|
||||||
|
}
|
||||||
|
});
|
||||||
taskCmd
|
taskCmd
|
||||||
.command('claim <id>')
|
.command('claim <id>')
|
||||||
.description('Claim a task')
|
.description('Claim a task')
|
||||||
.requiredOption('--agent <agent>', 'Agent name')
|
.requiredOption('--agent <agent>', 'Agent name')
|
||||||
.action((id, options) => taskClaim(cwd, id, options.agent));
|
.action(async (id, options) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
await remoteClient.claimTask(serverUrl, id, options.agent);
|
||||||
|
console.log(`${id} claimed by ${options.agent}.`);
|
||||||
|
} else {
|
||||||
|
taskClaim(cwd, id, options.agent);
|
||||||
|
}
|
||||||
|
});
|
||||||
taskCmd
|
taskCmd
|
||||||
.command('done <id>')
|
.command('done <id>')
|
||||||
.description('Mark task as done')
|
.description('Mark a task as done')
|
||||||
.action((id) => taskDone(cwd, id));
|
.action(async (id) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
await remoteClient.doneTask(serverUrl, id);
|
||||||
|
console.log(`${id} marked as done.`);
|
||||||
|
} else {
|
||||||
|
taskDone(cwd, id);
|
||||||
|
}
|
||||||
|
});
|
||||||
program.addCommand(taskCmd);
|
program.addCommand(taskCmd);
|
||||||
|
|
||||||
const handoffCmd = new Command('handoff').description('Manage handoffs');
|
const handoffCmd = new Command('handoff').description('Manage handoffs');
|
||||||
@ -82,15 +173,43 @@ export function createProgram(cwd: string): Command {
|
|||||||
.option('--taskId <id>', 'Related task id')
|
.option('--taskId <id>', 'Related task id')
|
||||||
.option('--summary <summary>', 'Summary')
|
.option('--summary <summary>', 'Summary')
|
||||||
.option('--context <context>', 'Context')
|
.option('--context <context>', 'Context')
|
||||||
.action((options) => handoffCreate(cwd, options));
|
.action(async (options) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const handoff = await remoteClient.createHandoff(serverUrl, options);
|
||||||
|
console.log(`Handoff created: ${handoff.id}`);
|
||||||
|
} else {
|
||||||
|
await handoffCreate(cwd, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
handoffCmd
|
handoffCmd
|
||||||
.command('read <id>')
|
.command('read <id>')
|
||||||
.description('Read a handoff')
|
.description('Read a handoff')
|
||||||
.action((id) => handoffRead(cwd, id));
|
.action(async (id) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const { handoff, body } = await remoteClient.getHandoff(serverUrl, id);
|
||||||
|
console.log(`# ${handoff.summary}`);
|
||||||
|
console.log(`From: ${handoff.fromRole} → ${handoff.toRole}`);
|
||||||
|
if (handoff.taskId) console.log(`Task: ${handoff.taskId}`);
|
||||||
|
console.log('\n' + body);
|
||||||
|
} else {
|
||||||
|
handoffRead(cwd, id);
|
||||||
|
}
|
||||||
|
});
|
||||||
handoffCmd
|
handoffCmd
|
||||||
.command('list')
|
.command('list')
|
||||||
.description('List handoffs')
|
.description('List handoffs')
|
||||||
.action(() => handoffList(cwd));
|
.action(async () => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const handoffs = await remoteClient.listHandoffs(serverUrl);
|
||||||
|
if (handoffs.length === 0) { console.log('No handoffs found.'); return; }
|
||||||
|
for (const h of handoffs) console.log(`${h.id}: ${h.title}`);
|
||||||
|
} else {
|
||||||
|
handoffList(cwd);
|
||||||
|
}
|
||||||
|
});
|
||||||
program.addCommand(handoffCmd);
|
program.addCommand(handoffCmd);
|
||||||
|
|
||||||
const decisionCmd = new Command('decision').description('Manage decisions');
|
const decisionCmd = new Command('decision').description('Manage decisions');
|
||||||
@ -100,25 +219,58 @@ export function createProgram(cwd: string): Command {
|
|||||||
.option('--title <title>', 'Title')
|
.option('--title <title>', 'Title')
|
||||||
.option('--context <context>', 'Context')
|
.option('--context <context>', 'Context')
|
||||||
.option('--decision <decision>', 'Decision')
|
.option('--decision <decision>', 'Decision')
|
||||||
.action((options) => decisionCreate(cwd, options));
|
.action(async (options) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const decision = await remoteClient.createDecision(serverUrl, options);
|
||||||
|
console.log(`Decision recorded: ${decision.id}`);
|
||||||
|
} else {
|
||||||
|
await decisionCreate(cwd, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
decisionCmd
|
decisionCmd
|
||||||
.command('list')
|
.command('list')
|
||||||
.description('List decisions')
|
.description('List decisions')
|
||||||
.action(() => decisionList(cwd));
|
.action(async () => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const decisions = await remoteClient.listDecisions(serverUrl);
|
||||||
|
if (decisions.length === 0) { console.log('No decisions found.'); return; }
|
||||||
|
for (const d of decisions) console.log(`${d.id}: ${d.title}`);
|
||||||
|
} else {
|
||||||
|
decisionList(cwd);
|
||||||
|
}
|
||||||
|
});
|
||||||
program.addCommand(decisionCmd);
|
program.addCommand(decisionCmd);
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('delegate')
|
.command('delegate')
|
||||||
.description('Suggest or auto-delegate open tasks')
|
.description('Suggest or auto-delegate open tasks')
|
||||||
.option('--auto', 'Create handoff automatically')
|
.option('--auto', 'Create handoff automatically')
|
||||||
.action((options) => delegate(cwd, options));
|
.action(async (options) => {
|
||||||
|
const serverUrl = getServerUrl(program);
|
||||||
|
if (serverUrl) {
|
||||||
|
const result = await remoteClient.delegate(serverUrl, options.auto ?? false);
|
||||||
|
if (!result.suggestion) { console.log('No open tasks to delegate.'); return; }
|
||||||
|
const s = result.suggestion;
|
||||||
|
console.log('Suggested delegation:');
|
||||||
|
console.log(` Task: ${s.task.id} — ${s.task.title}`);
|
||||||
|
console.log(` Role: ${s.role}`);
|
||||||
|
console.log(` Preferred agent: ${s.preferredAgent}`);
|
||||||
|
if (result.handoff) console.log('Handoff created automatically.');
|
||||||
|
else console.log('Run with --auto to create the handoff.');
|
||||||
|
} else {
|
||||||
|
await delegate(cwd, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const serverCmd = new Command('server').description('Optional local API server');
|
const serverCmd = new Command('server').description('Optional local API server');
|
||||||
serverCmd
|
serverCmd
|
||||||
.command('start')
|
.command('start')
|
||||||
.description('Start the optional AgentHub API server')
|
.description('Start the optional AgentHub API server')
|
||||||
.option('-p, --port <port>', 'Port', '3377')
|
.option('-p, --port <port>', 'Port', '3377')
|
||||||
.action((options) => serverStart(cwd, { port: parseInt(options.port, 10) }));
|
.option('-h, --host <host>', 'Host to bind to', '127.0.0.1')
|
||||||
|
.action((options) => serverStart(cwd, { port: parseInt(options.port, 10), host: options.host }));
|
||||||
program.addCommand(serverCmd);
|
program.addCommand(serverCmd);
|
||||||
|
|
||||||
return program;
|
return program;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user