import { Command } from "commander"; import { processProjectInteractionInput, type InteractionRuntimeTools, } from "@actalk/inkos-core"; import { createInteractionTools } from "../interaction/tools.js"; export interface InteractCommandHooks { readonly runInteraction?: (params: { readonly projectRoot: string; readonly input: string; readonly activeBookId?: string; readonly tools: InteractionRuntimeTools; }) => Promise<{ readonly request: unknown; readonly responseText?: string; readonly session: { readonly automationMode: string; readonly activeBookId?: string; readonly currentExecution?: unknown; readonly pendingDecision?: unknown; readonly messages: ReadonlyArray; readonly events: ReadonlyArray; }; }>; readonly createTools?: (projectRoot: string) => Promise; readonly readInput?: () => Promise; } async function readInteractionInput( args: ReadonlyArray, explicitMessage: string | undefined, readInput?: () => Promise, ): Promise { const explicit = explicitMessage?.trim(); if (explicit) { return explicit; } const inline = args.join(" ").trim(); if (inline) { return inline; } if (readInput) { const provided = (await readInput()).trim(); if (provided) { return provided; } } if (!process.stdin.isTTY) { const chunks: Buffer[] = []; for await (const chunk of process.stdin) { chunks.push(chunk as Buffer); } const piped = Buffer.concat(chunks).toString("utf-8").trim(); if (piped) { return piped; } } throw new Error("Interaction message is required. Pass text arguments or pipe input via stdin."); } export function createInteractCommand(hooks: InteractCommandHooks = {}): Command { return new Command("interact") .description("Run a shared natural-language interaction against the current project") .argument("[message...]", "Natural-language message") .option("--message ", "Explicit natural-language message") .option("--book ", "Bind a specific active book for this interaction") .option("--json", "Emit structured JSON for external agents") .action(async (messageArgs: ReadonlyArray, opts) => { const input = await readInteractionInput(messageArgs, opts.message, hooks.readInput); const projectRoot = process.cwd(); const tools = hooks.createTools ? await hooks.createTools(projectRoot) : hooks.runInteraction ? ({} as InteractionRuntimeTools) : await createInteractionTools(projectRoot, undefined, { requireApiKey: false }); const runInteraction = hooks.runInteraction ?? processProjectInteractionInput; const result = await runInteraction({ projectRoot, input, activeBookId: opts.book, tools, }); if (opts.json) { process.stdout.write(`${JSON.stringify({ request: result.request, responseText: result.responseText, session: result.session, currentExecution: result.session.currentExecution ?? null, pendingDecision: result.session.pendingDecision ?? null, events: result.session.events, }, null, 2)}\n`); return; } const text = result.responseText ?? (result.session.messages.at(-1) && "content" in (result.session.messages.at(-1) as Record) ? String((result.session.messages.at(-1) as Record).content) : ""); if (text) { process.stdout.write(`${text}\n`); } }); }