diff --git a/src/liveTurn.ts b/src/liveTurn.ts index 3edac9e..a6b7a5d 100644 --- a/src/liveTurn.ts +++ b/src/liveTurn.ts @@ -64,6 +64,25 @@ export async function runEditTurn( providerId: "claude-code", modelId, systemPrompt: SYSTEM_PROMPT, + // The claude-code provider spawns the bundled `claude` CLI. On macOS, when + // that process is launched from inside VS Code it performs IDE auto-connect, + // which shells out to `osascript` (`tell application …`) to identify/activate + // the editor — raising the macOS "control other applications" (Apple Events) + // permission prompt (#59). We only consume the turn's text result and never + // use the IDE connection, so we disable it: `CLAUDE_CODE_AUTO_CONNECT_IDE=0` + // hard-returns out of the auto-connect path in the binary, and + // `CLAUDE_CODE_IDE_SKIP_AUTO_INSTALL=1` skips the extension-install deep link. + // Seeding with the full `process.env` keeps the default inheritance intact + // (login under HOME, proxy/CA vars) — we only add the two switches. The + // provider forwards `options.env` into the spawned child (verified end to end + // through @cline/sdk → ai-sdk-provider-claude-code → @anthropic-ai/claude-agent-sdk). + options: { + env: { + ...process.env, + CLAUDE_CODE_AUTO_CONNECT_IDE: "0", + CLAUDE_CODE_IDE_SKIP_AUTO_INSTALL: "1", + }, + }, }); // Stream reduced progress snapshots out (INV-44 additive) and wire cancellation diff --git a/test/liveTurn.test.ts b/test/liveTurn.test.ts index 32f022d..290abec 100644 --- a/test/liveTurn.test.ts +++ b/test/liveTurn.test.ts @@ -52,6 +52,44 @@ describe("extractReplacement", () => { }); }); +describe("runEditTurn macOS automation prompt (#59)", () => { + // The claude-code provider spawns the bundled `claude` CLI, which on macOS + // performs IDE auto-connect when launched inside VS Code — sending Apple Events + // that raise the "control other applications" prompt. The extension only + // consumes the text result and never needs that connection, so we disable it. + it("spawns the agent with IDE auto-connect + auto-install disabled, preserving the rest of the environment", async () => { + vi.resetModules(); + const cfgs: any[] = []; + vi.doMock("@cline/sdk", () => ({ + Agent: class { + constructor(cfg: any) { + cfgs.push(cfg); + } + subscribe() { + return () => {}; + } + abort() {} + async run() { + return { status: "completed", outputText: "X", runId: "r" }; + } + }, + })); + process.env.__WGL_TEST_SENTINEL__ = "keep-me"; + await runEditTurn("do it", "old"); + const cfg = cfgs[0]; + // mK("0") === true in the claude binary → hard-disables IDE auto-connect, + // so no osascript / Apple Event fires. + expect(cfg.options?.env?.CLAUDE_CODE_AUTO_CONNECT_IDE).toBe("0"); + // Belt-and-suspenders: skip the IDE-extension auto-install (deep-link path). + expect(cfg.options?.env?.CLAUDE_CODE_IDE_SKIP_AUTO_INSTALL).toBe("1"); + // The full process environment must survive (login under HOME, proxy/CA vars). + expect(cfg.options?.env?.__WGL_TEST_SENTINEL__).toBe("keep-me"); + delete process.env.__WGL_TEST_SENTINEL__; + vi.doUnmock("@cline/sdk"); + vi.resetModules(); + }); +}); + describe("runEditTurn progress + cancel", () => { it("emits progress snapshots and returns the replacement unchanged (INV-44)", async () => { vi.resetModules();