diff --git a/src/run/ralph-process.ts b/src/run/ralph-process.ts index e3ac703..d508c0e 100644 --- a/src/run/ralph-process.ts +++ b/src/run/ralph-process.ts @@ -213,7 +213,7 @@ export function spawnRalphLoop( const child = spawn(cachedBashCommand ?? "bash", [BASH_RALPH_LOOP_PATH], { cwd: projectDir, env, - stdio: options.inheritStdio ? "inherit" : ["ignore", "pipe", "pipe"], + stdio: options.inheritStdio ? "inherit" : ["ignore", "ignore", "ignore"], detached: process.platform !== "win32", windowsHide: true, }); diff --git a/tests/run/ralph-process.test.ts b/tests/run/ralph-process.test.ts index 5c0317c..505ff77 100644 --- a/tests/run/ralph-process.test.ts +++ b/tests/run/ralph-process.test.ts @@ -393,7 +393,7 @@ describe("spawnRalphLoop", () => { ); }); - it("uses piped stdio when inheritStdio is false", async () => { + it("uses ignored stdio when inheritStdio is false", async () => { const mockChild = createMockChild(); mockSpawn.mockReturnValue(mockChild); @@ -404,11 +404,24 @@ describe("spawnRalphLoop", () => { "bash", expect.any(Array), expect.objectContaining({ - stdio: ["ignore", "pipe", "pipe"], + stdio: ["ignore", "ignore", "ignore"], }) ); }); + it("detach does not throw when stdout/stderr are null (ignore stdio mode)", async () => { + const mockChild = createMockChild(); + mockSpawn.mockReturnValue(mockChild); + + const { spawnRalphLoop } = await import("../../src/run/ralph-process.js"); + const rp = spawnRalphLoop("/project", "claude-code", { inheritStdio: false }); + + // child.stdout and child.stderr are null when stdio is "ignore" — detach() must not throw + expect(() => rp.detach()).not.toThrow(); + expect(mockChild.unref).toHaveBeenCalled(); + expect(rp.state).toBe("detached"); + }); + it("tracks exit code and updates state on child exit", async () => { const mockChild = createMockChild(); mockSpawn.mockReturnValue(mockChild);