| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- 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<unknown>;
- readonly events: ReadonlyArray<unknown>;
- };
- }>;
- readonly createTools?: (projectRoot: string) => Promise<InteractionRuntimeTools>;
- readonly readInput?: () => Promise<string>;
- }
- async function readInteractionInput(
- args: ReadonlyArray<string>,
- explicitMessage: string | undefined,
- readInput?: () => Promise<string>,
- ): Promise<string> {
- 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 <text>", "Explicit natural-language message")
- .option("--book <bookId>", "Bind a specific active book for this interaction")
- .option("--json", "Emit structured JSON for external agents")
- .action(async (messageArgs: ReadonlyArray<string>, 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, unknown>)
- ? String((result.session.messages.at(-1) as Record<string, unknown>).content)
- : "");
- if (text) {
- process.stdout.write(`${text}\n`);
- }
- });
- }
|