fix: pr-bug-scan findings from #1565 (#1613)

Added windowsShell parameter to buildAgentDraftLaunchPlan so the post-exit clear-var uses Remove-Item Env: on PowerShell/pwsh and set "FOO=" on cmd.exe; threaded settings.terminalWindowsShell through both call sites.

### Findings addressed
-  **[high]** `src/renderer/src/lib/tui-agent-startup.ts:160-163` — Windows clear-var command only works in cmd.exe, breaks in PowerShell

Rebased onto current main; added settings?.terminalWindowsShell to the useCallback dep array in useComposerState to satisfy react-hooks/exhaustive-deps.

Co-authored-by: orca-bot <bot@stably.ai>
This commit is contained in:
buf0-bot[bot] 2026-05-09 04:07:39 -07:00 committed by GitHub
parent 337ae272b8
commit e6e45dc22a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 78 additions and 4 deletions

View File

@ -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<typeof buildAgentStartupPlan> = null
@ -1589,6 +1590,7 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS
resolvedSetupDecision,
selectedRepo,
settings?.agentCmdOverrides,
settings?.terminalWindowsShell,
settings?.rightSidebarOpenByDefault,
setRightSidebarOpen,
setRightSidebarTab,

View File

@ -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 = {

View File

@ -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({

View File

@ -23,6 +23,14 @@ export type AgentStartupPlan = {
env?: Record<string, string>
}
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<Record<TuiAgent, string>>
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,