Brings the deterministic Han-flow conductor into BooCode: launch any read-only flow from BooChat or BooCoder, watch each agent stream live in a Paseo-style run pane, get an evidence-disciplined report — on local Qwen, persisted and resumable. Read-only enforced hard via qwen --approval-mode plan (orchestrator tasks fail closed if qwen is unavailable; never fall to write-capable native). Backend (apps/coder): re-homed conductor defs, flow_runs/flow_steps schema, flow-runner + dispatcher onTaskTerminal hook, restart-resume, runs routes (launch/list/get/cancel), user-channel WS. Contracts: two flow_run_* frames. Web: orchestrator pane kind + OrchestratorPane, Workflow button + slash flows (BooChat/BooCoder parity), FlowLauncherDialog, "New Orchestrator" in the + and split menus, runs history + export. Plan: openspec/changes/orchestrator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
40 lines
1.7 KiB
TypeScript
40 lines
1.7 KiB
TypeScript
/** CLI entry: run a Han-style flow through the deterministic code conductor. */
|
|
import { writeFile } from 'node:fs/promises';
|
|
import { runFlow } from './flow.js';
|
|
import { getFlow, describeFlows, FLOW_NAMES } from './flows/index.js';
|
|
import type { Band } from './types.js';
|
|
|
|
const argv = process.argv.slice(2);
|
|
const positional = argv.filter((a: string) => !a.startsWith('--'));
|
|
const flowName = positional[0];
|
|
const question = positional.slice(1).join(' ').trim();
|
|
|
|
const flow = flowName ? getFlow(flowName) : undefined;
|
|
if (!flow || !question) {
|
|
console.error('usage: tsx src/run.ts <flow> "<question>" [--size=small|medium|large] [--repo=/abs/path] [--fast]\n');
|
|
console.error('flows:');
|
|
console.error(describeFlows());
|
|
if (flowName && !flow) console.error(`\nunknown flow "${flowName}" — choose one of: ${FLOW_NAMES.join(', ')}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const repoArg = argv.find((a: string) => a.startsWith('--repo='));
|
|
const sizeArg = argv.find((a: string) => a.startsWith('--size='));
|
|
const concise = argv.includes('--fast') || argv.includes('--concise');
|
|
const band = (sizeArg ? sizeArg.slice('--size='.length) : 'small') as Band;
|
|
|
|
const input = {
|
|
question,
|
|
band,
|
|
...(repoArg ? { repoPath: repoArg.slice('--repo='.length) } : {}),
|
|
...(concise ? { concise: true } : {}),
|
|
};
|
|
|
|
const started = Date.now();
|
|
console.error(`conductor: "${flow.name}" — band=${band}${concise ? ' fast' : ''}`);
|
|
const { outputPath, artifact } = await runFlow(flow, input, { onLog: (m) => console.error(m) });
|
|
const path = outputPath ?? `conductor-report-${flow.name}.md`;
|
|
await writeFile(path, artifact, 'utf8');
|
|
console.error(`\n✓ conductor done in ${Math.round((Date.now() - started) / 1000)}s → ${path}`);
|
|
console.log(path);
|