浏览代码

fix demo readline terminal editing

Yichen Jiang 3 月之前
父节点
当前提交
5189c99543

+ 2 - 0
.gitignore

@@ -12,3 +12,5 @@ coverage/
 .doc-typecheck-*/
 .vscode/
 .DS_Store
+.idea
+mise.toml

+ 5 - 1
examples/coding-agent/src/stdio-chat.ts

@@ -53,7 +53,11 @@ export function apply(ctx: Context) {
   })
 
   ctx.effect(() => {
-    const reader = createInterface({ input: process.stdin })
+    const reader = createInterface({
+      input: process.stdin,
+      output: process.stdout,
+      terminal: process.stdin.isTTY && process.stdout.isTTY,
+    })
     // Piped-input exit, once stdin reaches EOF:
     //  - If no line ever submitted work (empty stdin, blank-only lines), exit
     //    immediately — no turn will ever start, so there is nothing to wait

+ 33 - 0
examples/coding-agent/tests/stdio-chat.spec.ts

@@ -0,0 +1,33 @@
+import { EventEmitter } from 'node:events'
+import type { Context } from 'cordis'
+import { describe, expect, test, vi } from 'vitest'
+
+const createInterface = vi.hoisted(() => vi.fn(() => {
+  const reader = new EventEmitter() as EventEmitter & { close(): void }
+  reader.close = vi.fn()
+  return reader
+}))
+
+vi.mock('node:readline', () => ({ createInterface }))
+
+function fakeContext(): Context {
+  return {
+    agents: { get: vi.fn() },
+    on: vi.fn(() => vi.fn()),
+    effect: vi.fn((callback: () => () => void) => callback()),
+  } as unknown as Context
+}
+
+describe('coding-agent stdio chat', () => {
+  test('creates a terminal readline interface so TTY editing keys work', async () => {
+    const { apply } = await import('../src/stdio-chat.ts')
+
+    apply(fakeContext())
+
+    expect(createInterface).toHaveBeenCalledWith({
+      input: process.stdin,
+      output: process.stdout,
+      terminal: process.stdin.isTTY && process.stdout.isTTY,
+    })
+  })
+})

+ 5 - 1
examples/echo-agent/src/stdio-chat.ts

@@ -35,7 +35,11 @@ export function apply(ctx: Context) {
   })
 
   ctx.effect(() => {
-    const reader = createInterface({ input: process.stdin })
+    const reader = createInterface({
+      input: process.stdin,
+      output: process.stdout,
+      terminal: process.stdin.isTTY && process.stdout.isTTY,
+    })
     reader.on('line', (line) => {
       const text = line.trim()
       if (!text) return

+ 33 - 0
examples/echo-agent/tests/stdio-chat.spec.ts

@@ -0,0 +1,33 @@
+import { EventEmitter } from 'node:events'
+import type { Context } from 'cordis'
+import { describe, expect, test, vi } from 'vitest'
+
+const createInterface = vi.hoisted(() => vi.fn(() => {
+  const reader = new EventEmitter() as EventEmitter & { close(): void }
+  reader.close = vi.fn()
+  return reader
+}))
+
+vi.mock('node:readline', () => ({ createInterface }))
+
+function fakeContext(): Context {
+  return {
+    agents: { get: vi.fn() },
+    on: vi.fn(() => vi.fn()),
+    effect: vi.fn((callback: () => () => void) => callback()),
+  } as unknown as Context
+}
+
+describe('echo-agent stdio chat', () => {
+  test('creates a terminal readline interface so TTY editing keys work', async () => {
+    const { apply } = await import('../src/stdio-chat.ts')
+
+    apply(fakeContext())
+
+    expect(createInterface).toHaveBeenCalledWith({
+      input: process.stdin,
+      output: process.stdout,
+      terminal: process.stdin.isTTY && process.stdout.isTTY,
+    })
+  })
+})

+ 1 - 1
vitest.config.ts

@@ -19,7 +19,7 @@ export default defineConfig({
   // instead applies the one root map to every importer.
   plugins: [tsconfigPaths({ projects: ['./tsconfig.test.json'] })],
   test: {
-    include: ['packages/*/tests/**/*.spec.ts'],
+    include: ['packages/*/tests/**/*.spec.ts', 'examples/*/tests/**/*.spec.ts'],
     coverage: {
       provider: 'v8',
       // Coverage measures OUR runtime source. Types-only files carry no