diff --git a/src/renderer/src/hooks/useComposerState.ts b/src/renderer/src/hooks/useComposerState.ts index 3dd8822eb04..f6d19f12604 100644 --- a/src/renderer/src/hooks/useComposerState.ts +++ b/src/renderer/src/hooks/useComposerState.ts @@ -1496,7 +1496,8 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS agent, draft: quickDraftPrompt, cmdOverrides: settings?.agentCmdOverrides ?? {}, - platform: CLIENT_PLATFORM + platform: CLIENT_PLATFORM, + windowsShell: settings?.terminalWindowsShell }) let startupPlan: ReturnType = null @@ -1589,6 +1590,7 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS resolvedSetupDecision, selectedRepo, settings?.agentCmdOverrides, + settings?.terminalWindowsShell, settings?.rightSidebarOpenByDefault, setRightSidebarOpen, setRightSidebarTab, diff --git a/src/renderer/src/lib/launch-work-item-direct.ts b/src/renderer/src/lib/launch-work-item-direct.ts index 85aaf9ef879..e6aea6aecf5 100644 --- a/src/renderer/src/lib/launch-work-item-direct.ts +++ b/src/renderer/src/lib/launch-work-item-direct.ts @@ -286,7 +286,8 @@ export async function launchWorkItemDirect(args: LaunchWorkItemDirectArgs): Prom agent: effectiveAgent, draft: draftContent, cmdOverrides: settings?.agentCmdOverrides ?? {}, - platform: CLIENT_PLATFORM + platform: CLIENT_PLATFORM, + windowsShell: settings?.terminalWindowsShell }) if (draftLaunchPlan) { startupPlan = { diff --git a/src/renderer/src/lib/tui-agent-startup.test.ts b/src/renderer/src/lib/tui-agent-startup.test.ts index 1adc0dd4a7b..1b1190820f1 100644 --- a/src/renderer/src/lib/tui-agent-startup.test.ts +++ b/src/renderer/src/lib/tui-agent-startup.test.ts @@ -194,6 +194,60 @@ describe('buildAgentDraftLaunchPlan', () => { }) }) + it('uses cmd.exe syntax to clear the pi prefill var when Windows shell is cmd', () => { + expect( + buildAgentDraftLaunchPlan({ + agent: 'pi', + draft: 'https://github.com/acme/repo/issues/42', + cmdOverrides: {}, + platform: 'win32', + windowsShell: 'cmd.exe' + }) + ).toEqual({ + agent: 'pi', + launchCommand: 'pi; set "ORCA_PI_PREFILL="', + expectedProcess: 'pi', + env: { ORCA_PI_PREFILL: 'https://github.com/acme/repo/issues/42' } + }) + }) + + it('uses Remove-Item Env: to clear the pi prefill var on PowerShell', () => { + // Why: `set "FOO="` is cmd-only; PowerShell parses it as the Set-Variable + // alias and never clears the env var, so re-running pi re-prefills with + // the stale URL. Use the portable PowerShell form instead. + expect( + buildAgentDraftLaunchPlan({ + agent: 'pi', + draft: 'https://github.com/acme/repo/issues/42', + cmdOverrides: {}, + platform: 'win32', + windowsShell: 'powershell.exe' + }) + ).toEqual({ + agent: 'pi', + launchCommand: 'pi; Remove-Item Env:ORCA_PI_PREFILL -ErrorAction SilentlyContinue', + expectedProcess: 'pi', + env: { ORCA_PI_PREFILL: 'https://github.com/acme/repo/issues/42' } + }) + }) + + it('treats pwsh.exe as PowerShell for the prefill clear-var', () => { + expect( + buildAgentDraftLaunchPlan({ + agent: 'pi', + draft: 'https://github.com/acme/repo/issues/42', + cmdOverrides: {}, + platform: 'win32', + windowsShell: 'pwsh.exe' + }) + ).toEqual({ + agent: 'pi', + launchCommand: 'pi; Remove-Item Env:ORCA_PI_PREFILL -ErrorAction SilentlyContinue', + expectedProcess: 'pi', + env: { ORCA_PI_PREFILL: 'https://github.com/acme/repo/issues/42' } + }) + }) + it('returns null for an empty draft so callers fall back cleanly', () => { expect( buildAgentDraftLaunchPlan({ diff --git a/src/renderer/src/lib/tui-agent-startup.ts b/src/renderer/src/lib/tui-agent-startup.ts index 65964af5ee4..9259442e4b1 100644 --- a/src/renderer/src/lib/tui-agent-startup.ts +++ b/src/renderer/src/lib/tui-agent-startup.ts @@ -23,6 +23,14 @@ export type AgentStartupPlan = { env?: Record } +function isPowerShellLike(shell: string | undefined): boolean { + if (!shell) { + return false + } + const normalized = shell.toLowerCase().replace(/\\/g, '/').split('/').pop() ?? '' + return normalized === 'powershell.exe' || normalized === 'pwsh.exe' || normalized === 'pwsh' +} + function quoteStartupArg(value: string, platform: NodeJS.Platform): string { if (platform === 'win32') { return `"${value.replace(/"/g, '""')}"` @@ -133,8 +141,15 @@ export function buildAgentDraftLaunchPlan(args: { draft: string cmdOverrides: Partial> platform: NodeJS.Platform + /** Why: the post-exit clear-var command differs per Windows shell. cmd.exe + * uses `set "FOO="`, but PowerShell parses that as the `Set-Variable` + * alias and never clears the env var — so re-running the agent re-prefills + * with the stale URL. Callers on Windows pass the configured shell + * (`terminalWindowsShell`) so we can emit `Remove-Item Env:FOO` for + * PowerShell/pwsh. POSIX platforms ignore this. */ + windowsShell?: string }): AgentDraftLaunchPlan | null { - const { agent, draft, cmdOverrides, platform } = args + const { agent, draft, cmdOverrides, platform, windowsShell } = args const config = TUI_AGENT_CONFIG[agent] const trimmed = draft.trim() if (!trimmed) { @@ -158,7 +173,9 @@ export function buildAgentDraftLaunchPlan(args: { // terminal would inherit the stale value and re-prefill with the old URL. const clearVar = platform === 'win32' - ? `set "${config.draftPromptEnvVar}="` + ? isPowerShellLike(windowsShell) + ? `Remove-Item Env:${config.draftPromptEnvVar} -ErrorAction SilentlyContinue` + : `set "${config.draftPromptEnvVar}="` : `unset ${config.draftPromptEnvVar}` return { agent,