Просмотр исходного кода

feat(tui): execute routed actions and report results

Ma 7 месяцев назад
Родитель
Сommit
49119c0e0d

+ 18 - 0
packages/cli/src/__tests__/tui-control-flow.test.ts

@@ -55,4 +55,22 @@ describe("tui control flow", () => {
 
     expect(tools.updateCurrentFocus).toHaveBeenCalledWith("harbor", "把 focus 拉回旧案线");
   });
+
+  it("persists automation mode changes through the session", async () => {
+    await persistProjectSession(projectRoot, {
+      ...createProjectSession(projectRoot),
+      activeBookId: "harbor",
+    });
+
+    const tools = {
+      writeNextChapter: vi.fn(async () => ({ ok: true })),
+      reviseDraft: vi.fn(async () => ({ ok: true })),
+      updateCurrentFocus: vi.fn(async () => ({ ok: true })),
+      updateAuthorIntent: vi.fn(async () => ({ ok: true })),
+    };
+
+    const result = await processTuiInput(projectRoot, "切换到全自动", tools);
+
+    expect(result.session.automationMode).toBe("auto");
+  });
 });

+ 20 - 0
packages/cli/src/__tests__/tui-output.test.ts

@@ -0,0 +1,20 @@
+import { describe, expect, it } from "vitest";
+import { formatTuiResult } from "../tui/output.js";
+
+describe("tui output", () => {
+  it("summarizes a completed write flow", () => {
+    expect(formatTuiResult({
+      intent: "write_next",
+      status: "completed",
+      bookId: "harbor",
+    })).toContain("Completed write_next for harbor");
+  });
+
+  it("summarizes a mode switch", () => {
+    expect(formatTuiResult({
+      intent: "switch_mode",
+      status: "completed",
+      mode: "auto",
+    })).toContain("Switched mode to auto");
+  });
+});

+ 9 - 2
packages/cli/src/tui/app.ts

@@ -14,6 +14,7 @@ import {
   resolveSessionActiveBook,
 } from "./session-store.js";
 import { createInteractionTools } from "./tools.js";
+import { formatTuiResult } from "./output.js";
 
 export interface TuiFrameState {
   readonly projectName: string;
@@ -54,7 +55,7 @@ export async function processTuiInput(
     tools,
   });
   await persistProjectSession(projectRoot, result.session);
-  return result;
+  return { ...result, request };
 }
 
 export async function launchTui(
@@ -86,7 +87,13 @@ export async function launchTui(
     if (!input.trim()) {
       return;
     }
-    await processTuiInput(projectRoot, input, tools ?? await createInteractionTools(projectRoot));
+    const result = await processTuiInput(projectRoot, input, tools ?? await createInteractionTools(projectRoot));
+    process.stdout.write(`\n${formatTuiResult({
+      intent: result.request.intent,
+      status: result.session.currentExecution?.status ?? "completed",
+      bookId: result.session.activeBookId,
+      mode: result.request.mode,
+    })}\n`);
   } finally {
     rl.close();
   }

+ 18 - 0
packages/cli/src/tui/output.ts

@@ -0,0 +1,18 @@
+import type { AutomationMode, ExecutionStatus, InteractionIntentType } from "@actalk/inkos-core";
+
+export function formatTuiResult(params: {
+  readonly intent: InteractionIntentType;
+  readonly status: ExecutionStatus;
+  readonly bookId?: string;
+  readonly mode?: AutomationMode;
+}): string {
+  if (params.intent === "switch_mode" && params.mode) {
+    return `Switched mode to ${params.mode}.`;
+  }
+
+  if (params.bookId) {
+    return `Completed ${params.intent} for ${params.bookId}.`;
+  }
+
+  return `Completed ${params.intent}.`;
+}

+ 15 - 0
packages/core/src/__tests__/interaction-nl-router.test.ts

@@ -57,4 +57,19 @@ describe("interaction natural-language router", () => {
       instruction: "为什么主角名字没按设定走",
     });
   });
+
+  it("maps mode switch requests to switch_mode", () => {
+    expect(routeNaturalLanguageIntent("切换到全自动", { activeBookId: "harbor" })).toEqual({
+      intent: "switch_mode",
+      mode: "auto",
+    });
+    expect(routeNaturalLanguageIntent("半自动", { activeBookId: "harbor" })).toEqual({
+      intent: "switch_mode",
+      mode: "semi",
+    });
+    expect(routeNaturalLanguageIntent("切到全自主", { activeBookId: "harbor" })).toEqual({
+      intent: "switch_mode",
+      mode: "manual",
+    });
+  });
 });

+ 21 - 0
packages/core/src/interaction/nl-router.ts

@@ -26,6 +26,27 @@ export function routeNaturalLanguageIntent(
     };
   }
 
+  if (/(全自动|auto mode|switch to auto|切换到全自动)/i.test(trimmed)) {
+    return {
+      intent: "switch_mode",
+      mode: "auto",
+    };
+  }
+
+  if (/(半自动|semi mode|switch to semi)/i.test(trimmed)) {
+    return {
+      intent: "switch_mode",
+      mode: "semi",
+    };
+  }
+
+  if (/(全自主|手动模式|manual mode|switch to manual)/i.test(trimmed)) {
+    return {
+      intent: "switch_mode",
+      mode: "manual",
+    };
+  }
+
   const rewriteMatch = trimmed.match(/(?:rewrite chapter|重写第)\s*(\d+)\s*(?:章)?/i);
   if (rewriteMatch) {
     return {