Skip to content

feature: unified-shell-resolution (2/4) - #1125

Open
myk1yt wants to merge 20 commits into
Zoo-Code-Org:mainfrom
myk1yt:pr/b05-shell-resolution-v2
Open

feature: unified-shell-resolution (2/4)#1125
myk1yt wants to merge 20 commits into
Zoo-Code-Org:mainfrom
myk1yt:pr/b05-shell-resolution-v2

Conversation

@myk1yt

@myk1yt myk1yt commented Aug 4, 2026

Copy link
Copy Markdown

Stack Position

Description

https://youtube.com/shorts/-cm4pnaoXD0

Full Feature Description

  • Feature Branch: feature/unified-shell-resolution
  • Feature Name: Unified Shell Resolution
  • Purpose: Resolves the problem where shell selection, profile interpretation, argument assembly, and terminal reuse differ across command execution paths. Unifies the priority among user-configured shell, VS Code default profile, OS default, and safe fallback into a single typed resolution pipeline. This ensures that the same user settings produce a predictable execution environment across Windows Command Prompt, PowerShell, WSL, and macOS/Linux POSIX shells, reducing cases where the entire task fails in unclear ways due to misconfiguration.
  • Full Change Description: B04 defines the shared shell settings types and the UI using local cached state before saving. B05 resolves settings and platform information into an executable, shell family, source, and argument array, preserving argument boundaries instead of string concatenation. B06 manages command queue, terminal lifecycle, registry, reuse, trace, cancellation, and disposal. B07 connects the resolver and lifecycle to the task, command tool, extension API, and webview message paths.
  • Impact Scope: Affects the shared contracts terminal.ts, global-settings.ts, vscode-extension-host.ts, the settings UI TerminalSettings.tsx and SettingsView.tsx, the backend terminal layer src/integrations/terminal, and the task/tool/API wiring Task.ts, ExecuteCommandTool.ts, api.ts.
  • Errors and Edge Cases: If an explicit user override is invalid, returns a typed rejectable error. If an automatic candidate is invalid, proceeds to the next candidate. Timeout, user cancellation, non-zero exit, and terminal disposal are kept as distinct outcomes. Shell path and command arguments are never combined into a single unescaped string. Inputs in SettingsView.tsx bind to cachedState, not live extension state.
  • Testing Method: Run B04's contract and settings component tests, B05's Windows/POSIX/WSL resolution and invocation tests, B06's queue/reuse/cancellation/disposal tests, B07's task/tool/message tests and terminal-profile.test.ts. Manually run the same command in default, PowerShell, Command Prompt, and where available WSL/POSIX profiles, comparing the selected executable, output, exit code, cancellation, and cleanup.

Why Split Into 17 PRs

Instead of submitting this feature as a single unified PR, it was split into individual PRs because as code size grows, safely reviewing a PR becomes very difficult. The feature was broken into mutually exclusive individual PRs so that each can be reviewed independently.

What This PR Specifically Changes

Adds CLI/user/legacy/VS Code/OS/fallback priority resolver, platform shell classification, typed result/error, executable and safe argument array. Does not include scheduler, registry, or task wiring.

Included Files

  • src/integrations/terminal/shell/ShellResolver.ts
  • src/integrations/terminal/shell/ShellInvocationAdapter.ts
  • src/integrations/terminal/shell/TerminalProfileResolver.ts
  • src/utils/shell.ts
  • resolver/invocation/profile direct tests

Exclusion Scope

  • src/integrations/terminal/CommandScheduler.ts
  • src/integrations/terminal/TerminalLifecycle.ts
  • src/integrations/terminal/TerminalRegistry.ts
  • src/integrations/terminal/CommandTrace.ts
  • task/provider/extension wiring
  • All items in the common removal rules

Summary by CodeRabbit

  • New Features
    • Added terminal shell selection in Settings for automatic detection, profiles, and custom executable paths.
    • Displays effective shell details, availability, fallback behavior, and validation errors.
    • Added shell-aware command execution with improved queuing, recovery, terminal reuse, and status reporting.
    • Prompts and command guidance now reflect the selected shell and environment.
  • Bug Fixes
    • Improved handling of unavailable shell integration and stale terminal activity.
    • Updated terminal profile behavior and login-shell configuration.
  • Documentation
    • Added localized settings text across supported languages.

Zoo (VP) added 19 commits August 2, 2026 07:43
Merge feature/unified-shell-resolution into pr/b04-shell-contracts-v2.
Combines B04's command_output ask delay with B05's shell resolution
system (ShellResolver, ShellInvocationAdapter, TerminalProfileResolver,
CommandEnvironmentService, CommandScheduler).

Conflict resolution in ExecuteCommandTool.ts:
- Kept B05 ShellFallbackMismatchError + enhanced getTerminalProviderForExecution
- Kept B04 COMMAND_OUTPUT_ASK_DELAY_MS + command_output ask delay logic
- Merged onShellExecutionStarted signature (process param from B04 + traceBuilder from B05)
- Combined commandStartedAt fallback with ExecaTerminal shell invocation plan

Conflict resolution in executeCommandTool.spec.ts:
- Kept both B04 command_output ask policy tests and B05 cwd parameter validation tests

Note: no-explicit-any lint errors are pre-existing in feature/unified-shell-resolution
…s for new test files, update counts for modified files
…onmentService - fixes e2e terminal-profile test where no VS Code terminal was created because provider was hardcoded to execa
- reserveTerminal: guard integration-ready self-transition when reusing a
  terminal already in integration-ready state (fixes IllegalTransitionError
  in e2e shell-race tests; the "404 No fixture matched" OpenRouter errors
  were a downstream symptom).
- classifyShellFamily: use separator-agnostic basename instead of
  path.basename so Windows paths classify correctly on POSIX hosts
  (fixes ubuntu getProfileShell("win32") returning undefined for Git Bash).
- ExecaTerminal.runCommand: transition from creating/idle to fallback-ready
  so setActiveStream's -> running transition is legal for directly
  constructed terminals (fixes ubuntu ExecaTerminal onLine not firing).
- TerminalRegistry: replace two as-any casts with proper types
  (removes no-explicit-any lint errors without touching suppressions).
…ode-sync cachedState reset

- Terminal.ts: When resolvedEnv is present, also check Terminal.getProfileShell()
  for shellArgs and pass them to vscode.window.createTerminal(). This fixes the
  e2e-mock terminal-profile test where creationOptions.shellArgs was missing
  --noprofile/--norc from the configured Bash profile.

- SettingsView.tsx: Re-apply mode-based cachedState sync from ac0ed1b that
  was reverted by a68ac23 (B05 merge). The useEffect now resets cachedState
  when either currentApiConfigName OR mode changes, fixing platform-unit-test
  failures on both ubuntu and windows.
… os-name in shell-env prompt spec

- Terminal.ts waitForShellIntegration: skip integration-ready/integration-pending
  transitions when already in integration-ready/fallback-ready. Reused VS Code
  terminals promoted by the registry fire the readiness path while already in
  integration-ready, causing IllegalTransitionError (integration-ready → integration-ready)
  and 6 e2e-mock failures (long-running-silent-command, terminal-reuse-shell-race,
  zero-chunk-shell-race).
- shell-environment-prompt.spec.ts: mock os-name to avoid spawning PowerShell per
  test. Under coverage instrumentation on windows-latest this exceeded the 20s test
  timeout (8 getSystemInfoSection failures). Matches all sibling prompt specs.
…d env resolution

Task.resolveCommandEnvironment() only read terminalProfile from persisted
provider state, ignoring programmatic overrides set via api.setTerminalProfile().
This caused the ShellResolver to resolve the default shell instead of the
profile override, leading to e2e test timeout in terminal-profile.test.ts.

Fix: fall back to Terminal.getTerminalProfile() when state.terminalProfile
is undefined, and invalidate the CommandEnvironmentService cache in
api.setTerminalProfile() so the next task re-resolves with the new profile.
…rminalProfile

The mock sidebarProvider in unit tests may not have getCommandEnvironmentService.
Use ?.() optional call syntax to tolerate missing method.
… tests

The profile-override test flaked in CI (run 30752014262): the custom
--noprofile/--norc bash terminal did not emit the OSC 633;A shell-integration
marker within the default 5s window on a loaded runner, aborting with
SI_ACTIVATION_TIMEOUT and hitting the 90s waitUntilCompleted budget.

Set terminalShellIntegrationTimeout to 30s in both Terminal Profile task
configurations so shell integration has time to activate.
…al-profile e2e

Root cause of persistent Terminal Profile e2e flake (runs 30752014262,
30760530287): the previous fix set terminalShellIntegrationTimeout via the
per-task startNewTask configuration, but that settings key is only applied
through the webview config-applier (ClineProvider). The extension-host API
setConfiguration path (contextProxy.setValues) never reaches
Terminal.setShellIntegrationTimeout, so the activation window stayed at the
default 5s and the --noprofile/--norc bash profile terminal aborted with
SI_ACTIVATION_TIMEOUT on loaded CI runners (terminal create -> abort exactly
5.000s).

- Add API.setShellIntegrationTimeout(timeoutMs) that updates the Terminal
  static immediately, and declare it on the RooCodeAPI interface.
- terminal-profile.test.ts now calls setShellIntegrationTimeout(30_000) in
  suiteSetup (restored to 5_000 in suiteTeardown) and drops the ineffective
  per-task config keys.
The --noprofile/--norc bash profile depends on VS Code injecting shell
integration via the shell startup path. On loaded CI runners that injection
intermittently exceeds even a 30s activation window (run 30761508190: terminal
created 18:40:49.05, abort 18:41:19.05 = exactly 30s, SI never fired). Each
mocha retry runs the test against a freshly created terminal, which typically
lets SI activate. Matches the retries:3 pattern already used by apply-diff.
…ARCH-TERMINAL-002)

Remove --norc from the terminal-profile E2E test so VS Code can inject
shell integration through the Bash startup path. --norc disables .bashrc
reading, which makes shell integration physically impossible.

- Change profile args from --noprofile --norc to --noprofile
- Remove Mocha retries (the failure was deterministic, not flaky)
- Remove 30s shell-integration timeout override (test-only API)
- Remove setShellIntegrationTimeout from RooCodeAPI and extension facade

Split the single contradictory assertion into two contracts:
1. Compatible profile: proves profile selection + shell integration works
2. Incompatible profile (--norc): will prove typed Execa fallback (B07)

Refs: ARCH-TERMINAL-002
--noprofile also blocks VS Code's bash shell integration injection
(just like --norc). Use --login instead, which is safe for shell
integration while still proving custom profile args pass-through.
The shell dropdown's onShellSelectionChange only updated the pending
selection state; the Save button stayed disabled unless the unrelated
onTerminalProfilePickerOpened hook happened to fire. Wrap the handler so
a shell selection change explicitly calls setChangeDetected(true),
enabling Save on shell-only changes. Behavior is otherwise identical.
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds configurable terminal shell selection and deterministic shell resolution. It connects resolved shell environments to prompts and command execution, and adds lifecycle, scheduling, tracing, recovery, provider fallback, webview, and settings UI behavior.

Changes

Terminal shell contracts and resolution

Layer / File(s) Summary
Shell schemas and resolution services
packages/types/src/global-settings.ts, packages/types/src/terminal.ts, packages/types/src/vscode-extension-host.ts, src/integrations/terminal/shell/*, src/utils/shell.ts
Defines typed shell selections, resolution results, invocation plans, shell profiles, shell-family classification, fallback behavior, and terminal message payloads.
Resolution and contract tests
packages/types/src/__tests__/terminal-shell-settings.spec.ts, src/integrations/terminal/__tests__/ShellResolver.spec.ts, src/integrations/terminal/__tests__/ShellInvocationAdapter.spec.ts, src/utils/__tests__/shell.spec.ts
Validates selection schemas, resolution precedence, platform behavior, invocation arguments, fallback handling, and allowlisted shell paths.

Prompt and task integration

Layer / File(s) Summary
Resolved environment propagation
src/core/task/Task.ts, src/core/task/build-tools.ts, src/core/prompts/system.ts, src/core/prompts/sections/*, src/core/prompts/tools/native-tools/*
Resolves and caches command environments per request. System prompts and execute_command descriptions use shell, provider, operator, interactivity, and fallback metadata.
Prompt behavior tests
src/core/prompts/__tests__/shell-environment-prompt.spec.ts
Validates shell-specific prompt sections, command descriptions, fallback text, and consistency across prompt components.

Terminal lifecycle and execution

Layer / File(s) Summary
Lifecycle, scheduling, and tracing
src/integrations/terminal/TerminalLifecycle.ts, src/integrations/terminal/CommandScheduler.ts, src/integrations/terminal/CommandTrace.ts, src/integrations/terminal/types.ts, src/integrations/terminal/BaseTerminal.ts
Adds lifecycle states, ownership checks, reuse validation, FIFO command scheduling, terminal-creation permits, structured errors, and privacy-safe execution traces.
Terminal providers and registry
src/integrations/terminal/Terminal.ts, src/integrations/terminal/ExecaTerminal.ts, src/integrations/terminal/TerminalProcess.ts, src/integrations/terminal/ExecaTerminalProcess.ts, src/integrations/terminal/TerminalRegistry.ts
Uses resolved shell plans and execution IDs. Adds shell-integration health handling, stale-terminal recovery, provider switching, same-family fallback, and lifecycle-based completion.
Command execution orchestration
src/core/tools/ExecuteCommandTool.ts, src/extension.ts, src/extension/api.ts
Queues commands, validates parameters, records traces, retries safe pre-submit failures, switches providers when possible, and initializes or invalidates terminal services.
Execution tests
src/core/tools/__tests__/*, src/integrations/terminal/__tests__/*
Covers scheduling, lifecycle transitions, invocation plans, terminal reuse, shell-integration failures, provider switching, watchdog recovery, and execution tracing.

Settings UI and webview

Layer / File(s) Summary
Webview shell management
src/core/webview/ClineProvider.ts, src/core/webview/webviewMessageHandler.ts, src/core/webview/generateSystemPrompt.ts, packages/types/src/vscode-extension-host.ts
Exposes sanitized shell options, validates and persists selections, refreshes effective-shell state, invalidates cached environments, and supports custom executable selection.
Settings components
webview-ui/src/components/settings/SettingsView.tsx, webview-ui/src/components/settings/TerminalSettings.tsx
Adds pending shell-selection state, save and discard handling, inline shell selection, effective-shell details, validation errors, and native executable picking.
UI tests and localization
webview-ui/src/components/settings/__tests__/*, webview-ui/src/i18n/locales/*/settings.json
Tests shell selection messaging and rendering. Adds inline-shell translation keys across supported locales.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

  • Zoo-Code-Org/Zoo-Code#1120 — Contains the shell-selection contracts, settings UI, localization, and tests extended by this PR.
  • Zoo-Code-Org/Zoo-Code#1136 — Overlaps across shell resolution, terminal lifecycle, prompts, webview integration, and command execution.
  • Zoo-Code-Org/Zoo-Code#834 — Modifies the same terminal execution and lifecycle paths for shell-aware execution and recovery.

Suggested labels: enhancement

Suggested reviewers: taltas, edelauna

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: introducing a unified shell-resolution feature.
Docstring Coverage ✅ Passed Docstring coverage is 80.95% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/vscode-e2e/src/suite/tools/terminal-profile.test.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

packages/types/src/__tests__/terminal-shell-settings.spec.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

packages/types/src/global-settings.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

  • 54 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 4, 2026
@myk1yt
myk1yt force-pushed the pr/b05-shell-resolution-v2 branch 3 times, most recently from fe6c9a7 to 2d862ca Compare August 4, 2026 20:40

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (13)
packages/types/src/__tests__/terminal-shell-settings.spec.ts-60-62 (1)

60-62: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Rename the test to match the assertion.

The test name states rejection, but the assertion is .not.toThrow(). The comment confirms that z.string() accepts an empty string. Rename the test so the intent matches the behavior.

♻️ Proposed rename
-		it("should reject profile with empty profileName", () => {
+		it("should accept profile with empty profileName (host validates)", () => {
 			expect(() => terminalShellSelectionSchema.parse({ kind: "profile", profileName: "" })).not.toThrow() // z.string() accepts empty; validation is extension-host responsibility
 		})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/types/src/__tests__/terminal-shell-settings.spec.ts` around lines 60
- 62, Rename the test case around terminalShellSelectionSchema.parse to state
that a profile with an empty profileName is accepted, matching the existing
not.toThrow assertion and explanatory comment.
src/core/webview/__tests__/terminal-shell-messages.spec.ts-227-235 (1)

227-235: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep Auto option metadata consistent with resolution.

Line 232 and Line 306 set the Auto option family to powershell, but mockEnv.primaryPlan.family is posix. Line 340 accepts the incorrect value. Derive the option family from the resolved environment.

Proposed fix
-						family: "powershell",
+						family: env.primaryPlan.family,
...
-						family: "powershell",
+						family: env.primaryPlan.family,
...
-				family: "powershell",
+				family: "posix",

Also applies to: 302-310, 337-343

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/__tests__/terminal-shell-messages.spec.ts` around lines 227
- 235, Update the Auto terminal option setup in the affected test cases around
the options arrays and assertions to derive family from the resolved
environment, using mockEnv.primaryPlan.family instead of hard-coding
"powershell". Ensure the assertions validate the resolved family consistently
with the option metadata.
webview-ui/src/i18n/locales/pt-BR/settings.json-850-864 (1)

850-864: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the terminal.inlineShell values.

These non-English locale files contain English-only values. This creates a mixed-language settings UI.

  • webview-ui/src/i18n/locales/pt-BR/settings.json#L850-L864: Translate all terminal.inlineShell values into Brazilian Portuguese.
  • webview-ui/src/i18n/locales/ru/settings.json#L850-L864: Translate all terminal.inlineShell values into Russian.
  • webview-ui/src/i18n/locales/tr/settings.json#L850-L864: Translate all terminal.inlineShell values into Turkish.
  • webview-ui/src/i18n/locales/vi/settings.json#L850-L864: Translate all terminal.inlineShell values into Vietnamese.
  • webview-ui/src/i18n/locales/zh-CN/settings.json#L850-L864: Translate all terminal.inlineShell values into Simplified Chinese.
  • webview-ui/src/i18n/locales/zh-TW/settings.json#L877-L891: Translate all terminal.inlineShell values into Traditional Chinese.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/i18n/locales/pt-BR/settings.json` around lines 850 - 864,
Translate every value in the terminal.inlineShell object, including nested
effectiveShell and error entries, into the target locale language without
changing keys or structure: Brazilian Portuguese in
webview-ui/src/i18n/locales/pt-BR/settings.json lines 850-864; Russian in
webview-ui/src/i18n/locales/ru/settings.json lines 850-864; Turkish in
webview-ui/src/i18n/locales/tr/settings.json lines 850-864; Vietnamese in
webview-ui/src/i18n/locales/vi/settings.json lines 850-864; Simplified Chinese
in webview-ui/src/i18n/locales/zh-CN/settings.json lines 850-864; and
Traditional Chinese in webview-ui/src/i18n/locales/zh-TW/settings.json lines
877-891.
webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx-164-175 (1)

164-175: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Require the profile option before testing selection.

The conditional permits this test to pass when profile:PowerShell is not rendered. Use getByTestId("option-profile:PowerShell") and always execute the click and callback assertions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx`
around lines 164 - 175, Update the profile selection test to use
getByTestId("option-profile:PowerShell") instead of queryByTestId, removing the
conditional guard so the click and
onShellSelectionChange/onTerminalProfilePickerOpened assertions always execute
and fail when the option is missing.
webview-ui/src/components/settings/TerminalSettings.tsx-317-327 (1)

317-327: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Render effectiveShell.label.

The effective-shell panel shows only the family and source. It does not show the supplied executable label. Users cannot distinguish shells in the same family, such as pwsh.exe and powershell.exe.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/components/settings/TerminalSettings.tsx` around lines 317 -
327, Update the effective-shell panel in TerminalSettings to also render the
supplied executable label using the existing effectiveShell.label translation
and shellOptions.effectiveShell.label value, alongside the family and source
fields.
webview-ui/src/components/settings/TerminalSettings.tsx-334-341 (1)

334-341: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Show an availability error for option discovery failures.

shellError comes from TerminalShellOptionsPayload.error, which reports shell-option discovery failure. The UI always renders error.invalid, so an unavailable extension-host service is reported as an invalid user selection.

Render error.unavailable for this payload path. Add an assertion for the displayed translation key.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/components/settings/TerminalSettings.tsx` around lines 334 -
341, Update the shellError rendering in TerminalSettings so this
option-discovery failure displays the
settings:terminal.inlineShell.error.unavailable translation instead of
error.invalid. Add or update the component assertion for
terminal-inline-shell-error to verify the unavailable translation key is shown.
webview-ui/src/i18n/locales/ca/settings.json-849-865 (1)

849-865: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the new inlineShell values.

The Catalan, German, Spanish, and French locale files contain English UI text. The terminal settings view changes language inside one section.

  • webview-ui/src/i18n/locales/ca/settings.json#L849-L865: add Catalan translations.
  • webview-ui/src/i18n/locales/de/settings.json#L849-L865: add German translations.
  • webview-ui/src/i18n/locales/es/settings.json#L849-L865: add Spanish translations.
  • webview-ui/src/i18n/locales/fr/settings.json#L849-L865: add French translations.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/i18n/locales/ca/settings.json` around lines 849 - 865,
Translate every English value in the inlineShell section into the appropriate
locale language: Catalan in webview-ui/src/i18n/locales/ca/settings.json lines
849-865, German in webview-ui/src/i18n/locales/de/settings.json lines 849-865,
Spanish in webview-ui/src/i18n/locales/es/settings.json lines 849-865, and
French in webview-ui/src/i18n/locales/fr/settings.json lines 849-865. Preserve
the existing keys and JSON structure while translating labels, descriptions,
options, placeholders, and error messages.
src/integrations/terminal/ExecaTerminalProcess.ts-23-27 (1)

23-27: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard the terminal dereference in the completed handler.

The terminal getter throws when the WeakRef target is collected. This handler runs from this.emit("completed", ...) at the end of run, which is outside the try block. A throw there rejects the run promise, and ExecaTerminal.runCommand does not observe that rejection. Read the reference defensively in the handler.

🛡️ Proposed fix
 		this.once("completed", () => {
 			// Lifecycle: transition to idle on completion.
 			// (architect report Section 1.4: ExecaTerminalProcess completion → idle)
-			this.terminal.lifecycle.resetToIdle()
+			this.terminalRef.deref()?.lifecycle.resetToIdle()
 		})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/ExecaTerminalProcess.ts` around lines 23 - 27,
Update the completed handler in ExecaTerminalProcess to read the terminal
reference defensively before calling lifecycle.resetToIdle, avoiding the
throwing terminal getter when the WeakRef target has been collected. Only reset
the lifecycle when a terminal instance is available, while preserving the
existing completion behavior otherwise.
src/integrations/terminal/TerminalRegistry.ts-377-392 (1)

377-392: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove the broken terminal from the registry.

When a healthy idle VS Code terminal has lost shell integration, this branch marks it broken, disposes the VS Code terminal, and then only continues the loop. The wrapper stays in this.terminals. getAllTerminals removes entries only when isClosed() is true, and exitStatus is not set synchronously after dispose(). The disposed wrapper is therefore re-evaluated on every later search and its ZDOTDIR map entry stays alive until the close event arrives. Remove it directly.

♻️ Proposed fix
 				terminal.lifecycle.markBroken()
 				if (terminal instanceof Terminal) {
 					terminal.terminal.dispose()
-					ShellIntegrationManager.zshCleanupTmpDir(terminal.id)
 				}
+				this.removeTerminal(terminal.id)
 				continue

removeTerminal already calls ShellIntegrationManager.zshCleanupTmpDir.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/TerminalRegistry.ts` around lines 377 - 392, Update
the broken-terminal branch in the registry scan to remove the affected terminal
wrapper directly after marking it broken, instead of only disposing it and
continuing. Reuse the existing removeTerminal method for this cleanup, and avoid
separately calling ShellIntegrationManager.zshCleanupTmpDir because
removeTerminal already handles it.
src/integrations/terminal/TerminalRegistry.ts-850-858 (1)

850-858: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

priorTerminalState always reports disposed.

The trace reads source.lifecycle.state after Line 850 transitions the source to disposed. The field therefore never carries the state that preceded the switch, which removes the diagnostic value of the trace. Capture the state before the failed transition and pass the captured value.

♻️ Proposed fix
+		const priorTerminalState = source.lifecycle.state
+
 		// 1. Transition source to failed.
 		source.lifecycle.transition("failed", executionId)
-			priorTerminalState: source.lifecycle.state,
+			priorTerminalState,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/TerminalRegistry.ts` around lines 850 - 858,
Capture the source terminal lifecycle state before the transition to "disposed"
in the terminal switch flow, then pass that captured value as priorTerminalState
in emitCommandTrace. Keep the existing transition and trace emission behavior
unchanged while ensuring the field reflects the state preceding disposal.
src/integrations/terminal/CommandTrace.ts-170-174 (1)

170-174: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

markShellIntegrationActivatedAt overwrites shellIntegrationInitiallyAvailable.

shellIntegrationInitiallyAvailable records whether shell integration was already available when the terminal was acquired. markShellIntegrationActivatedAt sets it to true, which reports a late activation as an initial availability. ExecuteCommandTool sets the flag explicitly at terminal acquisition (markShellIntegrationInitiallyAvailable), and a later activation event then overwrites that value. This makes cold-start measurements unreliable.

Record activation only in the timestamp field.

🔧 Proposed fix
 	markShellIntegrationActivatedAt(ts: number): this {
 		this.trace.shellIntegrationActivatedAt = ts
-		this.trace.shellIntegrationInitiallyAvailable = true
 		return this
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/CommandTrace.ts` around lines 170 - 174, Update
markShellIntegrationActivatedAt in CommandTrace so it only records the
activation timestamp in shellIntegrationActivatedAt. Remove the assignment that
changes shellIntegrationInitiallyAvailable, preserving the value established by
markShellIntegrationInitiallyAvailable during terminal acquisition.
src/core/tools/__tests__/executeCommandTool.spec.ts-159-178 (1)

159-178: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

These tests no longer exercise unescapeHtmlEntities.

Each input now contains the literal character instead of the HTML entity, and the expected value equals the input. The assertions pass for any implementation, including an identity function. The test titles still describe entity decoding.

Restore entity inputs so the tests verify the decoding of <, >, and &.

💚 Proposed fix
-		it("should unescape < to < character", () => {
-			const input = "echo <test>"
+		it("should unescape &lt; to < character", () => {
+			const input = "echo &lt;test&gt;"
 			const expected = "echo <test>"
 			expect(unescapeHtmlEntities(input)).toBe(expected)
 		})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tools/__tests__/executeCommandTool.spec.ts` around lines 159 - 178,
Update the tests around unescapeHtmlEntities so each input contains the
corresponding encoded entity (&lt;, &gt;, and &amp;) while expected values
retain the decoded characters. Adjust the mixed-entity case similarly,
preserving the existing test coverage and titles.
src/integrations/terminal/__tests__/TerminalProcessExec.pwsh.spec.ts-100-101 (1)

100-101: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Set resolvedShellFamily in the PowerShell exec setup.

TerminalProcess.run now derives shellKind.isPowerShell from this.terminal.resolvedShellFamily, and that test’s reconstructed Terminal defaults to "posix" because it passes no profile/shell context. Add the PowerShell marker before terminalProcess.run(), or assert the mocked command with the wrapper used by this path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/TerminalProcessExec.pwsh.spec.ts` around
lines 100 - 101, Update the PowerShell test setup around mockTerminalInfo and
TerminalProcess.run so the reconstructed Terminal has resolvedShellFamily
configured as PowerShell before execution, or mock/assert the command through
the wrapper used by this path. Preserve the existing lifecycle state and command
expectations while ensuring the test exercises the PowerShell branch.
🧹 Nitpick comments (20)
packages/types/src/global-settings.ts (1)

111-115: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider requiring non-empty strings in the schema.

profileName and path accept empty strings. The extension host currently rejects an empty path in ShellResolver.tryResolveExplicitPath, so this is not exploitable today. A schema-level min(1) makes the contract self-enforcing for every future consumer.

♻️ Proposed schema tightening
 export const terminalShellSelectionSchema = z.discriminatedUnion("kind", [
 	z.object({ kind: z.literal("auto") }),
-	z.object({ kind: z.literal("profile"), profileName: z.string() }),
-	z.object({ kind: z.literal("path"), path: z.string() }),
+	z.object({ kind: z.literal("profile"), profileName: z.string().min(1) }),
+	z.object({ kind: z.literal("path"), path: z.string().min(1) }),
 ])

Note: the existing test at packages/types/src/__tests__/terminal-shell-settings.spec.ts line 61 asserts that an empty profileName parses. Update that test if you apply this change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/types/src/global-settings.ts` around lines 111 - 115, Update
terminalShellSelectionSchema so the profileName and path fields require
non-empty strings using the schema’s minimum-length validation. Adjust the
terminal-shell settings test to expect empty profileName values to be rejected
while preserving valid selection behavior.
src/utils/shell.ts (2)

9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Export SHELL_ALLOWLIST as a read-only type.

Set<string> is exported mutably, so any importer can call SHELL_ALLOWLIST.add(...) and widen the trust boundary that isShellPathAllowed enforces. This is hardening, not an exploitable path, because an attacker who can run code in the extension host already has that authority. Annotate the export as ReadonlySet<string> so accidental mutation fails at compile time.

🛡️ Proposed change
-export const SHELL_ALLOWLIST = new Set<string>([
+const SHELL_ALLOWLIST_ENTRIES = new Set<string>([

Then add after the literal:

export const SHELL_ALLOWLIST: ReadonlySet<string> = SHELL_ALLOWLIST_ENTRIES
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/shell.ts` at line 9, Update the SHELL_ALLOWLIST export in
src/utils/shell.ts to use the ReadonlySet<string> type, preserving its existing
entries and behavior while preventing consumers from mutating it through methods
such as add.

460-475: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log ShellResolver fallback failures and update the documented shell-resolution chain.

resolveExecutable({}) intentionally omits steps 2–5, but the getShell() docstring still lists the full eight-step chain as if it applies. Update that section, add resolveExecutable() settings where callers need user-selected shells, and log any TerminalProfileResolver.forRuntime() / ShellResolver.forRuntime() failures instead of falling through silently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/shell.ts` around lines 460 - 475, Update getShell() documentation
to describe only the resolution steps performed by resolveExecutable({}), and
document the full chain separately only where applicable. Pass
resolveExecutable() settings from callers that require user-selected shells, and
replace the silent catch around TerminalProfileResolver.forRuntime() and
ShellResolver.forRuntime() with logging of the failure before retaining legacy
fallback behavior.

Source: Coding guidelines

src/integrations/terminal/types.ts (1)

155-169: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

fromDetails recomputes defaults that the constructor already applies.

Lines 158-159 duplicate the outcome and retryDisposition defaults from lines 137-139. Pass the optional values through and let the constructor apply the defaults, so the two default sets cannot diverge.

♻️ Proposed simplification
 	static fromDetails(details: ShellIntegrationErrorDetails, options?: { causeName?: string }): ShellIntegrationError {
 		const code = details.code ?? "SI_ACTIVATION_TIMEOUT"
-		const commandSubmitted = details.commandSubmitted
-		const defaultOutcome: TerminalErrorOutcome = commandSubmitted ? "unknown" : "not-started"
-		const defaultRetry: TerminalErrorRetryDisposition = commandSubmitted ? "never" : "same-terminal-once"
-
-		return new ShellIntegrationError(details.message, commandSubmitted, code, {
-			phase: details.phase ?? "prepare",
-			provider: details.provider ?? "vscode",
+
+		return new ShellIntegrationError(details.message, details.commandSubmitted, code, {
+			phase: details.phase,
+			provider: details.provider,
 			terminalId: details.terminalId,
-			outcome: details.outcome ?? defaultOutcome,
-			retryDisposition: details.retryDisposition ?? defaultRetry,
+			outcome: details.outcome,
+			retryDisposition: details.retryDisposition,
 			causeName: options?.causeName ?? details.causeName,
 		})
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/types.ts` around lines 155 - 169, Update
ShellIntegrationError.fromDetails to stop computing defaultOutcome and
defaultRetry; pass details.outcome and details.retryDisposition through
unchanged and let the ShellIntegrationError constructor apply its existing
defaults, while preserving the remaining field mappings.
src/integrations/terminal/shell/TerminalProfileResolver.ts (1)

381-415: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Deduplicate the name-based PowerShell and WSL detection.

resolveWellKnownProfileName (lines 389-412) and resolveProfileEntry (lines 459-489) contain the same win32 name matching and the same hardcoded executable selection. resolveSourceProfile (lines 514-546) repeats the executable selection a third time. The only difference is that the resolveProfileEntry branches also attach env: this.sanitizeEnv(entry.env).

Extract one helper that takes the profile name and the optional entry env, and call it from all three sites. This keeps the three paths from drifting when the PowerShell path list changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/shell/TerminalProfileResolver.ts` around lines 381
- 415, The PowerShell and WSL name-resolution logic is duplicated across
resolveWellKnownProfileName, resolveProfileEntry, and resolveSourceProfile.
Extract a shared helper accepting the profile name and optional entry
environment, centralize win32 matching and executable selection there, preserve
sanitizeEnv(entry.env) for profile entries, and update all three methods to use
the helper.
packages/types/src/vscode-extension-host.ts (1)

435-446: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Export the shell-family union once and reuse it.

TerminalShellOption.family repeats the literal union that ShellFamily declares in src/integrations/terminal/shell/types.ts. src/core/webview/ClineProvider.ts (lines 3093-3180) already needs a cast: env.primaryPlan.family as "powershell" | "cmd" | "posix" | "fish" | "wsl". If a family is added later, the two lists drift and the cast hides the mismatch.

Declare the union in packages/types and let ShellFamily alias it, so the extension-side type imports from @roo-code/types and the cast is no longer required.

♻️ Proposed direction
+/** Shell family controlling invocation semantics and command chaining. */
+export type TerminalShellFamily = "powershell" | "cmd" | "posix" | "fish" | "wsl"
+
 export interface TerminalShellOption {
 	id: string
 	label: string
-	/** Shell family controlling invocation semantics and command chaining. */
-	family: "powershell" | "cmd" | "posix" | "fish" | "wsl"
+	family: TerminalShellFamily
 	source: string
 	available: boolean
 }

Then in src/integrations/terminal/shell/types.ts:

import type { TerminalShellFamily } from "`@roo-code/types`"

export type ShellFamily = TerminalShellFamily
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/types/src/vscode-extension-host.ts` around lines 435 - 446, Declare
and export a shared TerminalShellFamily union in the packages/types definitions,
then update TerminalShellOption.family to use it. Change
integrations/terminal/shell/types.ts so ShellFamily aliases the imported
TerminalShellFamily, and update the ClineProvider primaryPlan.family usage to
remove the redundant literal-union cast while preserving type safety.
webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx (1)

35-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace broad test-double types with precise types.

The new tests use null as any, any message state, and untyped component mocks. These types hide prop-contract regressions in the shell-selection flow.

  • webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx#L35-L43: type captured TerminalSettings props without as any.
  • webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx#L269-L291: define a narrow extension-state fixture type.
  • webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx#L19-L71: type message spies and UI mock props with precise test-double interfaces.

After the change, run pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 <relative-file> for each edited file and confirm suppression counts do not increase. As per coding guidelines, “Avoid as any; use typed APIs, bracket notation for private members where appropriate, or precise test doubles and unknown with type guards.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx`
around lines 35 - 43, Replace broad any-based test doubles with precise
interfaces in SettingsView.shell-selection.spec.tsx lines 35-43 by typing
captured TerminalSettings props, in lines 269-291 by defining a narrow
extension-state fixture type, and in TerminalSettings.shell.spec.tsx lines 19-71
by typing message spies and mocked UI component props. Preserve existing test
behavior while removing null as any and untyped mock props; run the specified
eslint command for each edited file and ensure suppression counts do not
increase.

Source: Coding guidelines

src/integrations/terminal/__tests__/TerminalRegistry.spec.ts (2)

587-609: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the shared resolvedEnv fixture.

Both provider-switch tests build an identical fallbackPlan and ResolvedCommandEnvironment. Extract one factory in the describe block and override only the fields a test needs. This keeps the two tests in sync when ResolvedCommandEnvironment gains required fields.

Also applies to: 642-664

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/TerminalRegistry.spec.ts` around lines
587 - 609, The provider-switch tests duplicate the fallbackPlan and
ResolvedCommandEnvironment fixtures. Add a shared factory within the describe
block, such as around the existing test setup, that returns the common resolved
environment and accepts overrides for test-specific fields; update both
provider-switch tests to use it while preserving their individual overrides.

157-161: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The releaseOwner call after resetToIdle is a silent no-op.

TerminalLifecycle.resetToIdle already clears _ownerExecutionId. first.lifecycle.ownerExecutionId! therefore evaluates to undefined, and releaseOwner(undefined) passes its own guard only because undefined !== undefined is false. The non-null assertion hides that. The setup reads as if it releases a real owner, and it would start throwing if releaseOwner later rejected undefined. Release ownership before the reset, or drop the call. The same pattern repeats at Lines 172-173, 187-188, 202-203, 215-216, 264-265, and 282-283.

♻️ Proposed fix
-			first.lifecycle.resetToIdle()
-			first.lifecycle.releaseOwner(first.lifecycle.ownerExecutionId!)
-			first.lifecycle.markHealthy()
+			first.lifecycle.releaseOwner(first.lifecycle.ownerExecutionId!)
+			first.lifecycle.resetToIdle()
+			first.lifecycle.markHealthy()

Consider a shared makeReusable(terminal) helper so all six sites stay consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/TerminalRegistry.spec.ts` around lines
157 - 161, Update the repeated terminal setup sequences around
TerminalRegistry.getOrCreateTerminal so ownership is released before
lifecycle.resetToIdle, or remove the redundant releaseOwner call when
resetToIdle is sufficient. Apply the same correction at all listed sites, and
consider extracting a shared makeReusable helper to keep the setup consistent
without passing the cleared ownerExecutionId.
src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts (1)

139-161: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Restore mutated process.env values and cover null plan env.

These tests assign process.env.EXISTING_VAR, process.env.LANG, and process.env.LC_ALL and never restore them. The values persist for every later test in this worker, so the suite is order dependent. Use vi.stubEnv with vi.unstubAllEnvs in a teardown hook, or save and restore the previous values. Add one case for a plan.env entry set to null, because ShellInvocationPlan documents null as "unset variable".

♻️ Proposed fix
 		it("should preserve existing environment variables when plan is provided", async () => {
-			process.env.EXISTING_VAR = "existing"
+			vitest.stubEnv("EXISTING_VAR", "existing")
 			terminalProcess = new ExecaTerminalProcess(mockTerminal)
 		it("should override existing LANG and LC_ALL values when plan is provided", async () => {
-			process.env.LANG = "C"
-			process.env.LC_ALL = "POSIX"
+			vitest.stubEnv("LANG", "C")
+			vitest.stubEnv("LC_ALL", "POSIX")
 			terminalProcess = new ExecaTerminalProcess(mockTerminal)

Add the teardown hook in the enclosing describe:

afterEach(() => {
	vitest.unstubAllEnvs()
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts` around
lines 139 - 161, Update the tests around ExecaTerminalProcess to stub
environment variables instead of mutating process.env directly, and add the
enclosing describe teardown to call vitest.unstubAllEnvs after each test. Add a
case covering a plan.env entry with a null value and assert that the
corresponding variable is unset in the Execa options.
src/integrations/terminal/__tests__/TerminalLifecycle.spec.ts (1)

414-455: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tests for resetToIdle and forceState.

The suite covers resetForReuse but not resetToIdle or forceState. Both are production cleanup paths: BaseTerminal.shellExecutionComplete, the legacy busy/running setters, ExecaTerminalProcess completion, and TerminalRegistry.recoverStaleTerminal all depend on them. Add cases for the no-op behavior on failed, disposed, and idle, for clearing ownership and submission state from a pre-idle state, and for forceState with and without an owner.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/TerminalLifecycle.spec.ts` around lines
414 - 455, Extend the TerminalLifecycle test suite with coverage for resetToIdle
and forceState: verify resetToIdle is a no-op for failed, disposed, and idle
states, and clears ownership and command-submission state when returning a
pre-idle lifecycle to idle. Add forceState cases that validate state changes
both without an owner and with an owner, including the expected ownership
behavior.
src/integrations/terminal/__tests__/ShellInvocationAdapter.spec.ts (1)

1-6: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import the Vitest globals explicitly.

This file uses describe, it, and expect without importing them. The sibling suite src/integrations/terminal/__tests__/TerminalLifecycle.spec.ts imports them from vitest. The file compiles only while globals stays enabled in the Vitest config. Add the explicit import for consistency.

♻️ Proposed fix
+import { describe, it, expect } from "vitest"
+
 import { ShellInvocationAdapter } from "../shell/ShellInvocationAdapter"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/ShellInvocationAdapter.spec.ts` around
lines 1 - 6, Update the imports in ShellInvocationAdapter.spec.ts to explicitly
import describe, it, and expect from vitest, matching the sibling
TerminalLifecycle.spec.ts suite; leave the test behavior unchanged.
src/core/tools/__tests__/executeCommandTool.spec.ts (1)

713-801: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the duplicated cwd parameter validation describe block.

Lines 621-711 already define a describe("cwd parameter validation") block with the same four invalid-cwd cases and the same four tests. Lines 713-801 repeat it exactly. The duplicate adds no coverage and doubles the runtime of this section. Delete the second block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tools/__tests__/executeCommandTool.spec.ts` around lines 713 - 801,
Remove the later duplicated describe("cwd parameter validation") block in the
test file, including its repeated invalid-cwd cases and
acceptance/terminal-acquisition tests. Preserve the earlier cwd validation block
and all unique test coverage.
src/core/tools/__tests__/terminal-provider-fallback.spec.ts (1)

116-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

These tests assert only on the local makeEnv helper.

The same-family fallback and cross-family rejection blocks call no production code. They verify the values that makeEnv hardcodes at Lines 35-46. The suite name suggests that the resolver produces same-family fallbacks and that mismatches are rejected, but neither behavior is exercised.

Assert against the real resolver output, or move these cases to the resolver test suite.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tools/__tests__/terminal-provider-fallback.spec.ts` around lines 116
- 137, The fallback tests only validate values hardcoded by makeEnv instead of
exercising production behavior. Update the same-family fallback and cross-family
rejection cases to invoke the real fallback resolver and assert its returned
plans and mismatch handling, or relocate these cases to the resolver test suite;
retain the expected PowerShell same-family result and cmd/PowerShell mismatch
rejection.
src/integrations/terminal/__tests__/CommandScheduler.spec.ts (1)

410-444: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Restore real timers in a hook, not at the end of each test.

Each test calls vi.useFakeTimers() and vi.useRealTimers() inline. If an assertion fails, vi.useRealTimers() never runs. Fake timers then leak into the following tests in this file and cause unrelated failures. Move the switch into beforeEach/afterEach for this describe block, or call vi.useRealTimers() in afterEach.

♻️ Proposed change
 	afterEach(() => {
 		scheduler.dispose()
+		vi.useRealTimers()
 	})

Also applies to: 446-470, 549-568

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/CommandScheduler.spec.ts` around lines
410 - 444, Move fake-timer setup and restoration for the affected
CommandScheduler tests into the surrounding describe block’s
beforeEach/afterEach hooks, removing each test’s inline vi.useFakeTimers and
vi.useRealTimers calls. Ensure afterEach always restores real timers even when
assertions fail, including the tests around the cooldown cases and the
additional referenced ranges.
src/integrations/terminal/Terminal.ts (1)

545-553: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused resolvedExecutable parameter.

resolveShellFamily never reads resolvedExecutable. The documented priority list also does not use it. Drop the parameter and the argument at Line 109 so the signature matches the behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/Terminal.ts` around lines 545 - 553, Remove the
unused resolvedExecutable parameter from the resolveShellFamily method and
remove the corresponding argument at its call site. Preserve the existing
shell-family resolution behavior and remaining parameter order.
src/integrations/terminal/__tests__/ShellResolver.spec.ts (2)

286-294: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the resolution outcome unconditionally.

The only assertion sits inside if (result.ok). If resolve returns a failure, this test passes without checking anything. Assert result.ok first, then assert the source.

♻️ Proposed change
 			const result = resolver.resolve({
 				terminalProfile: "malicious-workspace-profile",
 			})
 
-			// Should fall through — not resolve the workspace profile
-			if (result.ok) {
-				expect(result.shell.source).not.toBe("zooProfile")
-			}
+			// Should fall through — not resolve the workspace profile.
+			expect(result.ok).toBe(true)
+			if (result.ok) {
+				expect(result.shell.source).not.toBe("zooProfile")
+			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/ShellResolver.spec.ts` around lines 286 -
294, Update the test around resolver.resolve for "malicious-workspace-profile"
to assert result.ok unconditionally before accessing result.shell.source, then
assert that the source is not "zooProfile"; remove the conditional guard so
failures cannot pass silently.

39-59: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the untyped test doubles with typed ones, or explain the assertions.

entry: any, source: any, as unknown as TerminalProfileResolver (Line 58), and settings as any (Line 182) drop type checking in the test. Use ShellResolutionSource for source, ShellResolverSettings for settings, and a Partial<TerminalProfileResolver> typed double. If a double assertion stays necessary, add a comment that states the reason.

As per coding guidelines: "Avoid as any; use typed APIs, bracket notation for private members where appropriate, or precise test doubles and unknown with type guards. Use double assertions only as a last resort and explain them with a comment."

Also applies to: 182-182

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/ShellResolver.spec.ts` around lines 39 -
59, Replace the untyped test doubles in createProfileResolverMock with
ShellResolutionSource for source and a typed entry shape, and construct the mock
as Partial<TerminalProfileResolver> before satisfying the resolver type; if a
double assertion remains, add a comment explaining its necessity. Update the
settings as any usage near the referenced test to use ShellResolverSettings
directly, avoiding any and preserving type checking.

Source: Coding guidelines

src/integrations/terminal/__tests__/TerminalProfile.spec.ts (1)

592-592: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Anchor both alternatives in the regex.

/pwsh\.exe|powershell\.exe$/i anchors only the second alternative. The first alternative matches pwsh.exe anywhere in the path. Group the alternation so both ends are anchored.

♻️ Proposed change
-			expect(result?.shellPath).toMatch(/pwsh\.exe|powershell\.exe$/i)
+			expect(result?.shellPath).toMatch(/(?:pwsh|powershell)\.exe$/i)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/TerminalProfile.spec.ts` at line 592,
Update the shellPath assertion in the TerminalProfile test to group the pwsh.exe
and powershell.exe alternatives under a single end anchor, ensuring the match
ends with either executable name rather than allowing pwsh.exe anywhere in the
path.
src/integrations/terminal/TerminalProcess.ts (1)

74-82: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Log the swallowed transition error.

The catch block discards the error from lifecycle.transition("failed"). If the transition table rejects the current state, the failure becomes invisible during diagnosis. Log the error at warn level, and pass the executionId when it is available so the lifecycle records the owner.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/TerminalProcess.ts` around lines 74 - 82, Update
the catch block around lifecycle.transition("failed") in the TerminalProcess
failure handling to capture the transition error and log it at warn level.
Include the available executionId in the lifecycle warning context, while
preserving the existing behavior of ignoring the transition failure and setting
lastError to SI_NEVER_AVAILABLE.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c96df25-09f2-43a7-bdcd-ed083f15f16b

📥 Commits

Reviewing files that changed from the base of the PR and between 7918f6b and 681e848.

⛔ Files ignored due to path filters (7)
  • src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/add-custom-instructions/no-mcp-servers.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap is excluded by !**/*.snap
📒 Files selected for processing (74)
  • apps/vscode-e2e/src/suite/tools/terminal-profile.test.ts
  • packages/types/src/__tests__/terminal-shell-settings.spec.ts
  • packages/types/src/global-settings.ts
  • packages/types/src/terminal.ts
  • packages/types/src/vscode-extension-host.ts
  • src/core/prompts/__tests__/shell-environment-prompt.spec.ts
  • src/core/prompts/sections/rules.ts
  • src/core/prompts/sections/system-info.ts
  • src/core/prompts/system.ts
  • src/core/prompts/tools/native-tools/execute_command.ts
  • src/core/prompts/tools/native-tools/index.ts
  • src/core/task/Task.ts
  • src/core/task/build-tools.ts
  • src/core/tools/ExecuteCommandTool.ts
  • src/core/tools/__tests__/executeCommand.spec.ts
  • src/core/tools/__tests__/executeCommandTool.spec.ts
  • src/core/tools/__tests__/terminal-provider-fallback.spec.ts
  • src/core/webview/ClineProvider.ts
  • src/core/webview/__tests__/terminal-shell-messages.spec.ts
  • src/core/webview/generateSystemPrompt.ts
  • src/core/webview/webviewMessageHandler.ts
  • src/eslint-suppressions.json
  • src/extension.ts
  • src/extension/api.ts
  • src/integrations/terminal/BaseTerminal.ts
  • src/integrations/terminal/CommandScheduler.ts
  • src/integrations/terminal/CommandTrace.ts
  • src/integrations/terminal/ExecaTerminal.ts
  • src/integrations/terminal/ExecaTerminalProcess.ts
  • src/integrations/terminal/Terminal.ts
  • src/integrations/terminal/TerminalLifecycle.ts
  • src/integrations/terminal/TerminalProcess.ts
  • src/integrations/terminal/TerminalRegistry.ts
  • src/integrations/terminal/__tests__/CommandScheduler.spec.ts
  • src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
  • src/integrations/terminal/__tests__/ShellInvocationAdapter.spec.ts
  • src/integrations/terminal/__tests__/ShellResolver.spec.ts
  • src/integrations/terminal/__tests__/TerminalLifecycle.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcess.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.bash.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.cmd.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.pwsh.spec.ts
  • src/integrations/terminal/__tests__/TerminalProfile.spec.ts
  • src/integrations/terminal/__tests__/TerminalRegistry.spec.ts
  • src/integrations/terminal/shell/CommandEnvironmentService.ts
  • src/integrations/terminal/shell/ShellInvocationAdapter.ts
  • src/integrations/terminal/shell/ShellResolver.ts
  • src/integrations/terminal/shell/TerminalProfileResolver.ts
  • src/integrations/terminal/shell/types.ts
  • src/integrations/terminal/types.ts
  • src/utils/__tests__/shell.spec.ts
  • src/utils/shell.ts
  • webview-ui/src/components/settings/SettingsView.tsx
  • webview-ui/src/components/settings/TerminalSettings.tsx
  • webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx
  • webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx
  • webview-ui/src/i18n/locales/ca/settings.json
  • webview-ui/src/i18n/locales/de/settings.json
  • webview-ui/src/i18n/locales/en/settings.json
  • webview-ui/src/i18n/locales/es/settings.json
  • webview-ui/src/i18n/locales/fr/settings.json
  • webview-ui/src/i18n/locales/hi/settings.json
  • webview-ui/src/i18n/locales/id/settings.json
  • webview-ui/src/i18n/locales/it/settings.json
  • webview-ui/src/i18n/locales/ja/settings.json
  • webview-ui/src/i18n/locales/ko/settings.json
  • webview-ui/src/i18n/locales/nl/settings.json
  • webview-ui/src/i18n/locales/pl/settings.json
  • webview-ui/src/i18n/locales/pt-BR/settings.json
  • webview-ui/src/i18n/locales/ru/settings.json
  • webview-ui/src/i18n/locales/tr/settings.json
  • webview-ui/src/i18n/locales/vi/settings.json
  • webview-ui/src/i18n/locales/zh-CN/settings.json
  • webview-ui/src/i18n/locales/zh-TW/settings.json

Comment on lines +274 to +276
const execTool = tools.find((t) => (t as any).function?.name === "execute_command")
expect(execTool).toBeDefined()
const desc = (execTool as any).function.description

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 core/prompts/__tests__/shell-environment-prompt.spec.ts

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 1595


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the target test range plus nearby context.
sed -n '230,310p' src/core/prompts/__tests__/shell-environment-prompt.spec.ts | cat -n

printf '\n--- TypeScript references to execTool/function/name/description ---\n'
rg -n "execTool|function\\?\\.|function\\.|execute_command|ChatCompletionFunctionTool|ChatCompletionTool" src/core/prompts/__tests__/shell-environment-prompt.spec.ts

printf '\n--- OpenAI type/package availability ---\n'
node - <<'JS'
try {
  const { OpenAI } = require('openai');
  console.log('openai module available, version', require('openai/package.json').version);
} catch (e) {
  console.log('openai module unavailable:', e.message);
}
try {
  const content = require('fs').readFileSync('node_modules/openai/index.d.ts', 'utf8');
  const matched = content.match(/interface\s+ChatCompletionFunctionTool[\s\S]{0,800}/);
  console.log('openai type definitions loaded; ChatCompletionFunctionTool present:', !!matched);
  if (matched) console.log(matched[0].substring(0, 800));
} catch (e) {
  console.log('openai index.d.ts unavailable:', e.message);
}
JS

printf '\n--- tsconfig type settings ---\n'
sed -n '1,220p' src/tsconfig.json 2>/dev/null || true

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 6287


Remove as any from the native-tool tests.

execTool is a ChatCompletionTool union, so lines 274 and 282 suppress type checking. Narrow function tools with a type predicate, guard execTool, then access execTool.function.description directly for lines 276 and 284.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/prompts/__tests__/shell-environment-prompt.spec.ts` around lines 274
- 276, Update the native-tool tests around the execTool lookups to remove both
as any casts: narrow the ChatCompletionTool union with a type predicate for
function tools, guard that execTool was found, then access
execTool.function.description directly while preserving the existing assertions.

Source: Coding guidelines

Comment thread src/core/task/Task.ts
Comment on lines +54 to 69
export class ShellFallbackMismatchError extends Error {
readonly code = "SHELL_FALLBACK_MISMATCH" as const
readonly primaryFamily: string
readonly fallbackFamily: string | undefined

constructor(primaryFamily: string, fallbackFamily: string | undefined) {
super(
`SHELL_FALLBACK_MISMATCH: Primary shell family "${primaryFamily}" has no compatible fallback` +
(fallbackFamily ? ` (fallback family: "${fallbackFamily}")` : " (no fallback plan available)") +
". Command was not executed.",
)
this.name = "ShellFallbackMismatchError"
this.primaryFamily = primaryFamily
this.fallbackFamily = fallbackFamily
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The fallback path never checks shell-family compatibility.

ShellFallbackMismatchError states that a command must not be retried under a different shell family. This file exports the class, and terminal-provider-fallback.spec.ts shows an environment where primaryPlan.family is cmd and fallbackPlan.family is powershell. The fallback branch at Line 318 sets useFallbackPlan: !!resolvedEnv without comparing the two families, and no code path throws ShellFallbackMismatchError. When the families differ, the command text produced for the primary shell syntax runs under a different shell. This can change command semantics.

Compare resolvedEnv.primaryPlan.family with resolvedEnv.fallbackPlan?.family before the retry, and report ShellFallbackMismatchError instead of replaying the command.

🐛 Proposed guard
 					if (error.retryDisposition === "fallback-safe" && !error.commandSubmitted) {
 						const terminalId = typeof error.terminalId === "number" ? error.terminalId : undefined
+
+						if (resolvedEnv && resolvedEnv.fallbackPlan?.family !== resolvedEnv.primaryPlan.family) {
+							const mismatch = new ShellFallbackMismatchError(
+								resolvedEnv.primaryPlan.family,
+								resolvedEnv.fallbackPlan?.family,
+							)
+							pushToolResult(formatResponse.toolError(mismatch.message))
+							return
+						}

Also applies to: 297-324

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tools/ExecuteCommandTool.ts` around lines 54 - 69, Update the
fallback retry branch around the logic that sets useFallbackPlan to compare
resolvedEnv.primaryPlan.family with resolvedEnv.fallbackPlan?.family before
replaying the command. When the families differ, stop the retry and report
ShellFallbackMismatchError using the primary and fallback family values; only
set useFallbackPlan when the shell families are compatible.

Comment thread src/core/tools/ExecuteCommandTool.ts
const closeIdleTerminalsMock = vi.fn()
vi.mock("../../../integrations/terminal/TerminalRegistry", () => ({
TerminalRegistry: {
closeIdleTerminals: (...args: any[]) => closeIdleTerminalsMock(...args),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file stats =="
wc -l src/core/webview/__tests__/terminal-shell-messages.spec.ts src/eslint-suppressions.json 2>/dev/null || true

echo "== target file outline =="
ast-grep outline src/core/webview/__tests__/terminal-shell-messages.spec.ts --view expanded 2>/dev/null || true

echo "== relevant any/unknown/message occurrences =="
rg -n "\bany\b|as\s+unknown|as\s+WebviewMessage|interface .*Message|type .*Message|sendMessage|closeIdleTerminals|createProvider|provider" src/core/webview/__tests__/terminal-shell-messages.spec.ts || true

echo "== eslint suppression snapshot =="
if [ -f src/eslint-suppressions.json ]; then
  python3 - <<'PY'
import json
p='src/eslint-suppressions.json'
data=json.load(open(p, encoding='utf-8'))
print(json.dumps(data, indent=2)[:4000])
PY
fi

echo "== diff against original branch if available =="
git diff -- src/core/webview/__tests__/terminal-shell-messages.spec.ts | sed -n '1,220p' || true

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 5739


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file stats =="
wc -l src/core/webview/__tests__/terminal-shell-messages.spec.ts src/eslint-suppressions.json 2>/dev/null || true

echo "== target file outline =="
ast-grep outline src/core/webview/__tests__/terminal-shell-messages.spec.ts --view expanded 2>/dev/null || true

echo "== relevant any/unknown/message occurrences =="
rg -n "\bany\b|as\s+unknown|as\s+WebviewMessage|interface .*Message|type .*Message|sendMessage|closeIdleTerminals|createProvider|provider" src/core/webview/__tests__/terminal-shell-messages.spec.ts || true

echo "== eslint suppression snapshot =="
if [ -f src/eslint-suppressions.json ]; then
  python3 - <<'PY'
import json
p='src/eslint-suppressions.json'
data=json.load(open(p, encoding='utf-8'))
print(json.dumps(data, indent=2, sort_keys=True)[:5000])
PY
fi

echo "== target lines with context =="
sed -n '50,160p;200,275p;320,345p;360,410p;418,455p' src/core/webview/__tests__/terminal-shell-messages.spec.ts

echo "== lint script availability =="
if [ -f package.json ]; then
  jq '.scripts' package.json
fi

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 17725


Remove explicit any from the terminal-shell messaging tests.

Use typed mocks/fixtures for the provider and terminal registry/profile resolver callbacks, and type message objects directly instead of as any. Define malformed payloads as unknown with a type guard instead.

Also applies to: 78-99, 142, 210-263, 329-331, 370-385, 400-414, 422-425, 446-463.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/__tests__/terminal-shell-messages.spec.ts` at line 69,
Replace explicit any usage throughout terminal-shell-messages.spec.ts with typed
mocks and fixtures, including callbacks such as closeIdleTerminals and the
provider, terminal registry, and profile resolver interactions. Type message
objects directly, and represent malformed payloads as unknown validated through
a type guard rather than using as any, covering the referenced test sections
while preserving their behavior.

Source: Coding guidelines

Comment on lines +284 to 332
public waitForShellIntegration(timeoutMs: number, executionId?: string, abortSignal?: AbortSignal): Promise<void> {
if (this.terminal.shellIntegration) {
// A reused terminal may already be in `integration-ready` (promoted by the
// registry during reservation) while shellIntegration is still defined.
// `integration-ready → integration-ready` is not a legal self-transition,
// so only promote when not already ready.
if (this.lifecycle.state !== "integration-ready") {
this.lifecycle.transition("integration-ready", executionId)
}
this.lifecycle.markHealthy()
return Promise.resolve()
}

// Only move to `integration-pending` from a state where that transition is
// legal. From `integration-ready`/`fallback-ready` the forward table does
// not allow `→ integration-pending`; in that case leave the state as-is and
// rely on the readiness event (or timeout) to drive the next transition.
if (this.lifecycle.state !== "integration-ready" && this.lifecycle.state !== "fallback-ready") {
this.lifecycle.transition("integration-pending", executionId)
}
this.shellIntegrationAbortController = new AbortController()
const abortController = this.shellIntegrationAbortController

if (abortSignal) {
abortSignal.addEventListener("abort", () => abortController.abort(), { once: true })
}

return new Promise<void>((resolve, reject) => {
const onAbort = () => {
clearTimeout(timer)
ref.disposable?.dispose()
const err = new Error("Shell integration wait cancelled")
err.name = "AbortError"
reject(err)
}

if (abortController.signal.aborted) {
onAbort()
return
}

abortController.signal.addEventListener("abort", onAbort, { once: true })

const ref = { disposable: null as vscode.Disposable | null }
const timer = setTimeout(() => {
ref.disposable?.dispose()
abortController.signal.removeEventListener("abort", onAbort)
reject(new Error(`Shell integration did not activate within ${timeoutMs / 1000}s`))
}, timeoutMs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Handle an already-aborted abortSignal, and move onAbort after timer/ref.

Two defects exist in waitForShellIntegration:

  1. Line 308 adds an abort listener to abortSignal. If the caller passes a signal that is already aborted, the listener never fires. The internal abortController then stays unaborted, so the wait runs until timeoutMs and rejects with a timeout error instead of an AbortError. The caller in runCommand (Line 249) then emits SI_ACTIVATION_TIMEOUT for a cancelled wait.
  2. onAbort reads timer and ref, which are const bindings declared after the if (abortController.signal.aborted) { onAbort(); return } check on Line 320. If that branch ever runs, onAbort throws a ReferenceError from the temporal dead zone instead of rejecting with AbortError.

Also clear shellIntegrationAbortController when the wait settles. Otherwise cancelShellIntegrationWait() aborts a controller that belongs to a wait that already finished.

🐛 Proposed fix
 		this.shellIntegrationAbortController = new AbortController()
 		const abortController = this.shellIntegrationAbortController
 
 		if (abortSignal) {
-			abortSignal.addEventListener("abort", () => abortController.abort(), { once: true })
+			if (abortSignal.aborted) {
+				abortController.abort()
+			} else {
+				abortSignal.addEventListener("abort", () => abortController.abort(), { once: true })
+			}
 		}
 
 		return new Promise<void>((resolve, reject) => {
+			const ref = { disposable: null as vscode.Disposable | null }
+			let timer: NodeJS.Timeout | undefined
+
 			const onAbort = () => {
 				clearTimeout(timer)
 				ref.disposable?.dispose()
+				this.shellIntegrationAbortController = undefined
 				const err = new Error("Shell integration wait cancelled")
 				err.name = "AbortError"
 				reject(err)
 			}
 
 			if (abortController.signal.aborted) {
 				onAbort()
 				return
 			}
 
 			abortController.signal.addEventListener("abort", onAbort, { once: true })
 
-			const ref = { disposable: null as vscode.Disposable | null }
-			const timer = setTimeout(() => {
+			timer = setTimeout(() => {
 				ref.disposable?.dispose()
 				abortController.signal.removeEventListener("abort", onAbort)
+				this.shellIntegrationAbortController = undefined
 				reject(new Error(`Shell integration did not activate within ${timeoutMs / 1000}s`))
 			}, timeoutMs)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/Terminal.ts` around lines 284 - 332, Update
waitForShellIntegration so an already-aborted abortSignal immediately aborts the
internal controller and rejects with AbortError. In the Promise setup, declare
ref and timer before onAbort so its cleanup references are initialized before
any immediate abort path. Clear shellIntegrationAbortController whenever the
wait settles, including abort, timeout, readiness, and other completion paths,
so cancelShellIntegrationWait cannot affect a completed wait.

Comment on lines +511 to +520
resetToIdle(): void {
if (this._state === "disposed" || this._state === "failed" || this._state === "idle") {
return
}
this._state = "idle"
this._stateChangedAt = this._now()
this._ownerExecutionId = undefined
this._commandSubmittedAt = undefined
this._recoveryAttempts = 0
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Show every resetToIdle and legacy busy=false call site with context.
rg -nP --type=ts -C 6 '\bresetToIdle\s*\(|\bbusy\s*=\s*false' src

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 25906


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== TerminalLifecycle relevant methods =="
sed -n '460,525p' src/integrations/terminal/TerminalLifecycle.ts

echo
echo "== TerminalRegistry end-event block =="
sed -n '178,215p' src/integrations/terminal/TerminalRegistry.ts

echo
echo "== TerminalRegistry reusable terminal path around 740-770 =="
sed -n '738,768p' src/integrations/terminal/TerminalRegistry.ts

echo
echo "== BaseTerminal busy setter and shellExecutionComplete =="
sed -n '40,165p' src/integrations/terminal/BaseTerminal.ts

echo
echo "== TerminalRegistry process reservation paths =="
rg -n --type=ts -C 4 'activeShellExecution|process\.executionId|executionId|busy = true|busy = false' src/integrations/terminal/TerminalRegistry.ts src/integrations/terminal/Terminal.ts src/integrations/terminal/BaseTerminal.ts

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 33893


Guard resetToIdle against ownership changes.

resetToIdle always clears _ownerExecutionId, _commandSubmittedAt, and _recoveryAttempts from non-disposed/non-failed/non-idle states. Reused terminals are acquired before the endpoint is ready, so a stale end event path such as terminal.busy = false can drop the currently reserved execution’s ownership after a later command. Add an optional executionId parameter and return when the caller is not the current owner.

🛡️ Proposed guard
-	resetToIdle(): void {
+	resetToIdle(executionId?: string): void {
+		if (
+			executionId !== undefined &&
+			this._ownerExecutionId !== undefined &&
+			this._ownerExecutionId !== executionId
+		) {
+			return
+		}
 		if (this._state === "disposed" || this._state === "failed" || this._state === "idle") {
 			return
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resetToIdle(): void {
if (this._state === "disposed" || this._state === "failed" || this._state === "idle") {
return
}
this._state = "idle"
this._stateChangedAt = this._now()
this._ownerExecutionId = undefined
this._commandSubmittedAt = undefined
this._recoveryAttempts = 0
}
resetToIdle(executionId?: string): void {
if (
executionId !== undefined &&
this._ownerExecutionId !== undefined &&
this._ownerExecutionId !== executionId
) {
return
}
if (this._state === "disposed" || this._state === "failed" || this._state === "idle") {
return
}
this._state = "idle"
this._stateChangedAt = this._now()
this._ownerExecutionId = undefined
this._commandSubmittedAt = undefined
this._recoveryAttempts = 0
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/TerminalLifecycle.ts` around lines 511 - 520,
Update TerminalLifecycle.resetToIdle to accept an optional executionId and,
before changing state or clearing ownership fields, return when a provided
caller ID does not match the current _ownerExecutionId. Preserve existing
behavior for callers without an ID and for the current owner, including
resetting state, timestamps, and recovery counters.

Comment on lines +594 to +676
private static runWatchdog(): void {
const now = Date.now()
const shellIntegrationTimeout = Terminal.getShellIntegrationTimeout()

// Iterate over the raw terminals array so the watchdog can see closed
// terminals and recover them before getAllTerminals() filters them out.
for (const terminal of [...this.terminals]) {
const lifecycle = terminal.lifecycle
const ownerExecutionId = lifecycle.ownerExecutionId
if (ownerExecutionId === undefined) {
continue
}

const state = lifecycle.state
const process = terminal.process
const terminalClosed = terminal.isClosed()

// Evidence 1: terminal closed while owned.
if (terminalClosed) {
console.info(
`[TerminalRegistry/watchdog] Terminal ${terminal.id} closed while owned by ${ownerExecutionId}; recovering`,
)
this.recoverStaleTerminal(terminal.id, ownerExecutionId, "TERMINAL_DISPOSED")
continue
}

// Evidence 2: attached process belongs to a different execution.
if (
process &&
"executionId" in process &&
process.executionId !== undefined &&
process.executionId !== ownerExecutionId
) {
console.info(
`[TerminalRegistry/watchdog] Terminal ${terminal.id} process belongs to ${process.executionId} but owner is ${ownerExecutionId}; recovering`,
)
this.recoverStaleTerminal(terminal.id, ownerExecutionId, "TERMINAL_BUSY_STALE")
continue
}

// Evidence 3: pre-submission states exceeded their deadline.
const elapsed = now - lifecycle.stateChangedAt
const preSubmissionDeadline = shellIntegrationTimeout + 1_000

if (state === "creating" || state === "process-started" || state === "integration-pending") {
if (elapsed > preSubmissionDeadline) {
console.info(
`[TerminalRegistry/watchdog] Terminal ${terminal.id} pre-submission state ${state} exceeded deadline (${elapsed}ms); recovering`,
)
this.recoverStaleTerminal(terminal.id, ownerExecutionId, "TERMINAL_BUSY_STALE")
}
continue
}

if (state === "integration-ready" || state === "fallback-ready") {
if (elapsed > READY_RESERVATION_DEADLINE_MS) {
console.info(
`[TerminalRegistry/watchdog] Terminal ${terminal.id} ready reservation exceeded ${READY_RESERVATION_DEADLINE_MS}ms; recovering`,
)
this.recoverStaleTerminal(terminal.id, ownerExecutionId, "TERMINAL_BUSY_STALE")
}
continue
}

// Evidence 4: owned but no process in a state that requires one.
if (state === "running" && !process) {
console.info(
`[TerminalRegistry/watchdog] Terminal ${terminal.id} is running but has no process; recovering`,
)
this.recoverStaleTerminal(terminal.id, ownerExecutionId, "TERMINAL_BUSY_STALE")
continue
}

// Running with a matching process is intentionally NOT reset by time.
if (state === "running") {
if (elapsed > 10_000) {
console.info(
`[TerminalRegistry/watchdog] Terminal ${terminal.id} has been running for ${elapsed}ms with a matching process; diagnostic only`,
)
}
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

The watchdog never reaps an owned terminal in idle or failed.

runWatchdog handles creating, process-started, integration-pending, integration-ready, fallback-ready, and running. It has no branch for idle or failed while ownerExecutionId is set. Two reachable paths leave a terminal in exactly that shape:

  • TerminalLifecycle.resetToIdle returns early for failed, so it does not clear ownership. In the Execa branch of recoverStaleTerminal (Line 763), a failed terminal keeps its owner.
  • The same Execa branch performs no reset at all when a process is attached or when the process has no executionId, so the owner remains after recovery.

An owned terminal in these states fails canReuse forever and the watchdog ignores it, so it leaks for the lifetime of the extension host. Add an evidence branch for an owned terminal in idle or failed, and release ownership in the Execa recovery path.

Also applies to: 761-767

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/TerminalRegistry.ts` around lines 594 - 676, Update
runWatchdog to detect owned terminals whose lifecycle state is idle or failed
and recover them through recoverStaleTerminal, preserving the existing recovery
reason and logging pattern. In the Execa recovery path of recoverStaleTerminal,
ensure ownership is cleared/reset even when the terminal is failed, has an
attached process, or its process lacks an executionId, so recovery cannot leave
the terminal permanently unreusable.

Comment on lines +789 to +816
if (commandSubmitted) {
return {
terminal: this.getTerminalById(terminalId)!,
provider: fromProvider,
}
}
if (!resolvedEnv.fallbackPlan) {
throw new Error("TERMINAL/PROVIDER_SWITCH/003: fallback plan is required")
}

const source = this.getTerminalById(terminalId)
if (!source) {
throw new Error(`TERMINAL/PROVIDER_SWITCH/004: source terminal ${terminalId} not found`)
}
if (source.provider !== "vscode") {
throw new Error("TERMINAL/PROVIDER_SWITCH/005: source terminal is not a VS Code terminal")
}
if (source.lifecycle.ownerExecutionId !== executionId) {
throw new Error(
`TERMINAL/PROVIDER_SWITCH/006: owner mismatch (expected ${executionId}, got ${source.lifecycle.ownerExecutionId})`,
)
}

// 1. Transition source to failed.
source.lifecycle.transition("failed", executionId)

// 2. Cancel shell-integration wait.
;(source as Terminal).cancelShellIntegrationWait()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Validate the source terminal before the non-null assertion and the cast.

Two unchecked assumptions exist in this precondition block:

  • Line 791 uses this.getTerminalById(terminalId)!. getTerminalById returns undefined when the terminal is closed or absent, so result.terminal can be undefined while the return type claims RooTerminal. The caller then dereferences undefined.
  • Line 816 casts with (source as Terminal) and calls cancelShellIntegrationWait(). Every other step in this method uses instanceof Terminal. A RooTerminal that reports provider === "vscode" but is not a Terminal instance throws a TypeError here.
🛡️ Proposed fix
 		if (commandSubmitted) {
-			return {
-				terminal: this.getTerminalById(terminalId)!,
-				provider: fromProvider,
-			}
+			const current = this.getTerminalById(terminalId)
+			if (!current) {
+				throw new Error(`TERMINAL/PROVIDER_SWITCH/007: source terminal ${terminalId} not found`)
+			}
+			return { terminal: current, provider: fromProvider }
 		}
 		// 2. Cancel shell-integration wait.
-		;(source as Terminal).cancelShellIntegrationWait()
+		if (source instanceof Terminal) {
+			source.cancelShellIntegrationWait()
+		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (commandSubmitted) {
return {
terminal: this.getTerminalById(terminalId)!,
provider: fromProvider,
}
}
if (!resolvedEnv.fallbackPlan) {
throw new Error("TERMINAL/PROVIDER_SWITCH/003: fallback plan is required")
}
const source = this.getTerminalById(terminalId)
if (!source) {
throw new Error(`TERMINAL/PROVIDER_SWITCH/004: source terminal ${terminalId} not found`)
}
if (source.provider !== "vscode") {
throw new Error("TERMINAL/PROVIDER_SWITCH/005: source terminal is not a VS Code terminal")
}
if (source.lifecycle.ownerExecutionId !== executionId) {
throw new Error(
`TERMINAL/PROVIDER_SWITCH/006: owner mismatch (expected ${executionId}, got ${source.lifecycle.ownerExecutionId})`,
)
}
// 1. Transition source to failed.
source.lifecycle.transition("failed", executionId)
// 2. Cancel shell-integration wait.
;(source as Terminal).cancelShellIntegrationWait()
if (commandSubmitted) {
const current = this.getTerminalById(terminalId)
if (!current) {
throw new Error(`TERMINAL/PROVIDER_SWITCH/007: source terminal ${terminalId} not found`)
}
return { terminal: current, provider: fromProvider }
}
if (!resolvedEnv.fallbackPlan) {
throw new Error("TERMINAL/PROVIDER_SWITCH/003: fallback plan is required")
}
const source = this.getTerminalById(terminalId)
if (!source) {
throw new Error(`TERMINAL/PROVIDER_SWITCH/004: source terminal ${terminalId} not found`)
}
if (source.provider !== "vscode") {
throw new Error("TERMINAL/PROVIDER_SWITCH/005: source terminal is not a VS Code terminal")
}
if (source.lifecycle.ownerExecutionId !== executionId) {
throw new Error(
`TERMINAL/PROVIDER_SWITCH/006: owner mismatch (expected ${executionId}, got ${source.lifecycle.ownerExecutionId})`,
)
}
// 1. Transition source to failed.
source.lifecycle.transition("failed", executionId)
// 2. Cancel shell-integration wait.
if (source instanceof Terminal) {
source.cancelShellIntegrationWait()
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/TerminalRegistry.ts` around lines 789 - 816,
Validate the terminal returned by getTerminalById before returning it from the
commandSubmitted path, throwing the same missing-source error instead of using
the non-null assertion. In the fallback path, require source to be an actual
Terminal instance before calling cancelShellIntegrationWait, and replace the
unchecked (source as Terminal) cast with the validated instance.

Comment thread webview-ui/src/components/settings/SettingsView.tsx
…ice, ShellResolver edge cases, and webview inline shell selector

- Add CommandTrace.spec.ts (100% line coverage for builder + collector)
- Add CommandEnvironmentService.spec.ts (98% line coverage; previously 0%)
- Extend ShellResolver.spec.ts with bare-name normalization, invalid-path, and Unix env-probe fallback cases (76% -> 84%)
- Extend TerminalSettings.shell.spec.tsx with path/cmd selection, custom path button, and dropdown value mapping (63% -> 80%)
- Add session coverage report
@myk1yt
myk1yt force-pushed the pr/b05-shell-resolution-v2 branch from 681e848 to 12775cb Compare August 5, 2026 08:00
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Nitpick comments (1)
src/integrations/terminal/shell/TerminalProfileResolver.ts (1)

381-415: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate the duplicated Windows name-based resolution.

resolveWellKnownProfileName repeats the logic in resolveProfileEntry lines 459-489. Both branches map a profile name that contains powershell or wsl to the same executables and the same trustEvidence. The only difference is the sanitized env field.

Extract one private helper that takes the profile name, the source, and an optional env, then call it from both sites. This prevents the two copies from diverging when the known Windows paths change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/shell/TerminalProfileResolver.ts` around lines 381
- 415, Extract a private helper for the shared Windows name-based resolution
used by resolveWellKnownProfileName and resolveProfileEntry, accepting
profileName, source, and optional env. Move the powershell/wsl matching and
ResolvedShell construction into that helper, preserving the existing executable,
family, displayName, and trustedProfile behavior while applying the optional
sanitized env. Replace both duplicated branches with calls to the helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md`:
- Line 39: The report must not present the 5.71 MiB webview bundle size or
current build output as verified evidence. In
docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md at lines
39-39 and 352-352, mark the value unverified or attach reproducible measurement
evidence; at lines 576-584, verify the output path exists and VSIX packaging
consumes it; at lines 605-609, remove the confirmation claim unless evidence is
recorded. In docs/feedbacks/fromarchitect/260801_missing_webview_build_path.md
at lines 11-21, retain the unmeasured status unless a reproducible build
measurement is added.

In `@docs/260801_0001_session_fork-pr-rebase-ci/222900_architect-report.md`:
- Line 160: Replace the movable backup branch command with an immutable
recovery-reference procedure, using annotated tags or a protected backup-ref
namespace for the pre-rewrite tip. Record each referenced object ID and
explicitly prohibit updates to the backup references before any branch
rewriting, including the corresponding command at the other occurrence.

In `@docs/260801_0001_session_fork-pr-rebase-ci/224000_code-report.md`:
- Around line 50-55: Replace the Next Step Recommendations’ plain rebase
workflow with rebuilding each B branch from its declared base using the
feature-commit manifest, excluding copied prerequisite and CI commits. After
rebuilding, run git range-diff and changed-file checks, then verify the branch
builds and tests pass; retain the backup-tag rollback guidance.

In `@docs/260801_0001_session_fork-pr-rebase-ci/224700_code-report.md`:
- Line 5: Keep the release status provisional until current-head remote gates
are verified: in
docs/260801_0001_session_fork-pr-rebase-ci/224700_code-report.md lines 5-5,
describe the results as local CI-equivalent checks passing; in lines 56-73,
defer overall success until GitHub Actions passes for the current head SHA; in
docs/260805_0001_session_ci-all-green/164900_code-report.md lines 28-36,
distinguish local coverage from Codecov acceptance; and in lines 44-48, record
the current-head Codecov result before marking the objective complete.

In `@docs/260801_0001_session_fork-pr-rebase-ci/232300_code-report.md`:
- Line 56: Add the bash language identifier to the fenced shell command block in
docs/260801_0001_session_fork-pr-rebase-ci/232300_code-report.md at lines 56-56,
and add the text language identifier to the dependency graph fence in
docs/260801_0001_session_fork-pr-rebase-ci/requirement-checklist.md at lines
36-36.

In `@docs/260801_0001_session_fork-pr-rebase-ci/requirement-checklist.md`:
- Around line 9-10: Update the REQ-001 and REQ-002 checklist entries in
requirement-checklist.md to checked, matching the completed sync and force-push
evidence recorded in rebase-evidence.md.

In `@docs/260805_0001_session_ci-all-green/163300_debug-coverage-b05.md`:
- Line 101: Correct the backend coverage calculation in the documented coverage
summary: update the stated additional-line gap from 3,523 to 3,534, while
preserving the surrounding totals, target, and planning context.

In `@docs/feedbacks/fromarchitect/260801_crow_recall_register_validation.md`:
- Line 1: Update each repeated top-level heading in the report, including every
occurrence of “Environment Feedback Report,” to use a unique title that
distinguishes its corresponding report while preserving the existing report
content.

In `@src/integrations/terminal/shell/TerminalProfileResolver.ts`:
- Around line 189-202: Add an explicit fallback branch to deriveDisplayName so
every ShellFamily value produces a string, preserving the function’s declared
return type when classifyShellFamily yields an unexpected or newly added family.
Use the project’s established exhaustiveness-guard pattern if available, and
ensure the fallback does not return undefined.
- Around line 390-399: Update the PowerShell resolution logic around the
executable selection and return object to verify both POWERSHELL_7_PATH and
POWERSHELL_LEGACY_PATH exist before returning a profile. Return undefined when
neither path exists, and apply the same guard to the equivalent resolution
patterns around the logic referenced at lines 464 and 515 so missing executables
fall through to the next source.

In
`@webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx`:
- Around line 263-266: Fix the vacuous auto-option assertion in the
TerminalSettings test by asserting the rendered option-auto entry count,
ensuring exactly one auto entry is present. Keep the existing PowerShell and cmd
assertions unchanged.
- Around line 171-182: Update the profile selection test around profileButton to
retrieve option-profile:PowerShell with getByTestId instead of conditionally
using queryByTestId. Remove the truthy guard so the click and both callback
assertions always execute, causing the test to fail when the option is not
rendered.

In `@webview-ui/src/i18n/locales/de/settings.json`:
- Around line 849-865: Translate every string in the terminal.inlineShell block:
update the German keys in webview-ui/src/i18n/locales/de/settings.json lines
849-865, the Korean keys in webview-ui/src/i18n/locales/ko/settings.json lines
849-865, and the Vietnamese keys in webview-ui/src/i18n/locales/vi/settings.json
lines 849-865, covering label, description, auto, customPath,
customPathPlaceholder, all effectiveShell fields, and both error messages while
preserving the existing JSON structure and keys.

---

Nitpick comments:
In `@src/integrations/terminal/shell/TerminalProfileResolver.ts`:
- Around line 381-415: Extract a private helper for the shared Windows
name-based resolution used by resolveWellKnownProfileName and
resolveProfileEntry, accepting profileName, source, and optional env. Move the
powershell/wsl matching and ResolvedShell construction into that helper,
preserving the existing executable, family, displayName, and trustedProfile
behavior while applying the optional sanitized env. Replace both duplicated
branches with calls to the helper.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e543546-8137-4aba-ae4b-7a9598d73d37

📥 Commits

Reviewing files that changed from the base of the PR and between 64d6e37 and 12775cb.

⛔ Files ignored due to path filters (7)
  • src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/add-custom-instructions/no-mcp-servers.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap is excluded by !**/*.snap
  • src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap is excluded by !**/*.snap
📒 Files selected for processing (90)
  • apps/vscode-e2e/src/suite/tools/terminal-profile.test.ts
  • docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md
  • docs/260801_0001_session_fork-pr-rebase-ci/222900_architect-report.md
  • docs/260801_0001_session_fork-pr-rebase-ci/224000_code-report.md
  • docs/260801_0001_session_fork-pr-rebase-ci/224700_code-report.md
  • docs/260801_0001_session_fork-pr-rebase-ci/230415_code-report.md
  • docs/260801_0001_session_fork-pr-rebase-ci/232300_code-report.md
  • docs/260801_0001_session_fork-pr-rebase-ci/234030_code-report.md
  • docs/260801_0001_session_fork-pr-rebase-ci/decisions.md
  • docs/260801_0001_session_fork-pr-rebase-ci/rebase-evidence.md
  • docs/260801_0001_session_fork-pr-rebase-ci/requirement-checklist.md
  • docs/260805_0001_session_ci-all-green/163300_debug-coverage-b05.md
  • docs/260805_0001_session_ci-all-green/164900_code-report.md
  • docs/feedbacks/fromarchitect/260801_crow_recall_register_validation.md
  • docs/feedbacks/fromarchitect/260801_missing_webview_build_path.md
  • packages/types/src/__tests__/terminal-shell-settings.spec.ts
  • packages/types/src/global-settings.ts
  • packages/types/src/terminal.ts
  • packages/types/src/vscode-extension-host.ts
  • src/core/prompts/__tests__/shell-environment-prompt.spec.ts
  • src/core/prompts/sections/rules.ts
  • src/core/prompts/sections/system-info.ts
  • src/core/prompts/system.ts
  • src/core/prompts/tools/native-tools/execute_command.ts
  • src/core/prompts/tools/native-tools/index.ts
  • src/core/task/Task.ts
  • src/core/task/build-tools.ts
  • src/core/tools/ExecuteCommandTool.ts
  • src/core/tools/__tests__/executeCommand.spec.ts
  • src/core/tools/__tests__/executeCommandTool.spec.ts
  • src/core/tools/__tests__/terminal-provider-fallback.spec.ts
  • src/core/webview/ClineProvider.ts
  • src/core/webview/__tests__/terminal-shell-messages.spec.ts
  • src/core/webview/generateSystemPrompt.ts
  • src/core/webview/webviewMessageHandler.ts
  • src/eslint-suppressions.json
  • src/extension.ts
  • src/extension/api.ts
  • src/integrations/terminal/BaseTerminal.ts
  • src/integrations/terminal/CommandScheduler.ts
  • src/integrations/terminal/CommandTrace.ts
  • src/integrations/terminal/ExecaTerminal.ts
  • src/integrations/terminal/ExecaTerminalProcess.ts
  • src/integrations/terminal/Terminal.ts
  • src/integrations/terminal/TerminalLifecycle.ts
  • src/integrations/terminal/TerminalProcess.ts
  • src/integrations/terminal/TerminalRegistry.ts
  • src/integrations/terminal/__tests__/CommandEnvironmentService.spec.ts
  • src/integrations/terminal/__tests__/CommandScheduler.spec.ts
  • src/integrations/terminal/__tests__/CommandTrace.spec.ts
  • src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
  • src/integrations/terminal/__tests__/ShellInvocationAdapter.spec.ts
  • src/integrations/terminal/__tests__/ShellResolver.spec.ts
  • src/integrations/terminal/__tests__/TerminalLifecycle.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcess.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.bash.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.cmd.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.pwsh.spec.ts
  • src/integrations/terminal/__tests__/TerminalProfile.spec.ts
  • src/integrations/terminal/__tests__/TerminalRegistry.spec.ts
  • src/integrations/terminal/shell/CommandEnvironmentService.ts
  • src/integrations/terminal/shell/ShellInvocationAdapter.ts
  • src/integrations/terminal/shell/ShellResolver.ts
  • src/integrations/terminal/shell/TerminalProfileResolver.ts
  • src/integrations/terminal/shell/types.ts
  • src/integrations/terminal/types.ts
  • src/utils/__tests__/shell.spec.ts
  • src/utils/shell.ts
  • webview-ui/src/components/settings/SettingsView.tsx
  • webview-ui/src/components/settings/TerminalSettings.tsx
  • webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx
  • webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx
  • webview-ui/src/i18n/locales/ca/settings.json
  • webview-ui/src/i18n/locales/de/settings.json
  • webview-ui/src/i18n/locales/en/settings.json
  • webview-ui/src/i18n/locales/es/settings.json
  • webview-ui/src/i18n/locales/fr/settings.json
  • webview-ui/src/i18n/locales/hi/settings.json
  • webview-ui/src/i18n/locales/id/settings.json
  • webview-ui/src/i18n/locales/it/settings.json
  • webview-ui/src/i18n/locales/ja/settings.json
  • webview-ui/src/i18n/locales/ko/settings.json
  • webview-ui/src/i18n/locales/nl/settings.json
  • webview-ui/src/i18n/locales/pl/settings.json
  • webview-ui/src/i18n/locales/pt-BR/settings.json
  • webview-ui/src/i18n/locales/ru/settings.json
  • webview-ui/src/i18n/locales/tr/settings.json
  • webview-ui/src/i18n/locales/vi/settings.json
  • webview-ui/src/i18n/locales/zh-CN/settings.json
  • webview-ui/src/i18n/locales/zh-TW/settings.json
🚧 Files skipped from review as they are similar to previous changes (67)
  • webview-ui/src/i18n/locales/tr/settings.json
  • webview-ui/src/i18n/locales/zh-TW/settings.json
  • webview-ui/src/i18n/locales/zh-CN/settings.json
  • src/integrations/terminal/tests/TerminalProcessExec.bash.spec.ts
  • src/integrations/terminal/tests/TerminalProfile.spec.ts
  • webview-ui/src/i18n/locales/pl/settings.json
  • webview-ui/src/i18n/locales/ru/settings.json
  • src/integrations/terminal/tests/TerminalProcessExec.pwsh.spec.ts
  • src/core/task/build-tools.ts
  • src/core/webview/generateSystemPrompt.ts
  • src/core/prompts/system.ts
  • src/integrations/terminal/tests/TerminalProcessExec.cmd.spec.ts
  • src/integrations/terminal/tests/TerminalLifecycle.spec.ts
  • src/integrations/terminal/tests/ShellInvocationAdapter.spec.ts
  • src/core/prompts/tests/shell-environment-prompt.spec.ts
  • webview-ui/src/i18n/locales/pt-BR/settings.json
  • webview-ui/src/i18n/locales/fr/settings.json
  • packages/types/src/terminal.ts
  • src/core/tools/tests/terminal-provider-fallback.spec.ts
  • src/core/tools/tests/executeCommand.spec.ts
  • src/integrations/terminal/shell/types.ts
  • packages/types/src/global-settings.ts
  • src/core/webview/webviewMessageHandler.ts
  • src/extension.ts
  • webview-ui/src/i18n/locales/nl/settings.json
  • webview-ui/src/i18n/locales/es/settings.json
  • src/core/prompts/tools/native-tools/index.ts
  • webview-ui/src/i18n/locales/id/settings.json
  • packages/types/src/tests/terminal-shell-settings.spec.ts
  • src/integrations/terminal/ExecaTerminalProcess.ts
  • webview-ui/src/i18n/locales/en/settings.json
  • src/core/webview/tests/terminal-shell-messages.spec.ts
  • webview-ui/src/i18n/locales/ja/settings.json
  • webview-ui/src/components/settings/tests/SettingsView.shell-selection.spec.tsx
  • src/integrations/terminal/CommandScheduler.ts
  • src/integrations/terminal/tests/ExecaTerminalProcess.spec.ts
  • webview-ui/src/i18n/locales/hi/settings.json
  • webview-ui/src/i18n/locales/ca/settings.json
  • src/core/prompts/sections/system-info.ts
  • src/extension/api.ts
  • src/core/prompts/sections/rules.ts
  • src/integrations/terminal/shell/CommandEnvironmentService.ts
  • src/integrations/terminal/CommandTrace.ts
  • src/utils/tests/shell.spec.ts
  • src/integrations/terminal/tests/TerminalRegistry.spec.ts
  • webview-ui/src/i18n/locales/it/settings.json
  • src/integrations/terminal/TerminalProcess.ts
  • apps/vscode-e2e/src/suite/tools/terminal-profile.test.ts
  • src/integrations/terminal/tests/TerminalProcess.spec.ts
  • src/eslint-suppressions.json
  • src/core/prompts/tools/native-tools/execute_command.ts
  • webview-ui/src/components/settings/SettingsView.tsx
  • src/integrations/terminal/shell/ShellInvocationAdapter.ts
  • src/integrations/terminal/types.ts
  • webview-ui/src/components/settings/TerminalSettings.tsx
  • packages/types/src/vscode-extension-host.ts
  • src/core/webview/ClineProvider.ts
  • src/utils/shell.ts
  • src/integrations/terminal/Terminal.ts
  • src/integrations/terminal/BaseTerminal.ts
  • src/core/tools/tests/executeCommandTool.spec.ts
  • src/integrations/terminal/TerminalLifecycle.ts
  • src/integrations/terminal/shell/ShellResolver.ts
  • src/integrations/terminal/TerminalRegistry.ts
  • src/core/task/Task.ts
  • src/integrations/terminal/tests/CommandScheduler.spec.ts
  • src/core/tools/ExecuteCommandTool.ts

| Today coverage | 0.644 ms |
| NDJSON size | 7.25 MiB |
| NDJSON idempotency rebuild, warm median | about 115 ms |
| Main webview JavaScript bundle | 5.71 MiB |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not treat the webview bundle size as verified evidence.

The architecture report records 5.71 MiB and says the build output was confirmed. The environment feedback says webview-ui/build/assets was absent and the bundle size was unmeasured. Mark this value as historical, or rebuild and record the exact output path and packaged artifact before using it in the Dashboard decision.

  • docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md#L39-L39: mark the bundle size as unverified or attach the measurement.
  • docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md#L352-L352: do not use the 5.71 MiB value as confirmed evidence.
  • docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md#L576-L584: verify that the build output exists and that VSIX packaging consumes it.
  • docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md#L605-L609: remove the claim that the current build output was confirmed unless evidence is recorded.
  • docs/feedbacks/fromarchitect/260801_missing_webview_build_path.md#L11-L21: keep the unmeasured status unless a reproducible build measurement is added.
📍 Affects 2 files
  • docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md#L39-L39 (this comment)
  • docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md#L352-L352
  • docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md#L576-L584
  • docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md#L605-L609
  • docs/feedbacks/fromarchitect/260801_missing_webview_build_path.md#L11-L21
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md` at
line 39, The report must not present the 5.71 MiB webview bundle size or current
build output as verified evidence. In
docs/260731_0001_session_dashboard-blank-fix/164200_architect-report.md at lines
39-39 and 352-352, mark the value unverified or attach reproducible measurement
evidence; at lines 576-584, verify the output path exists and VSIX packaging
consumes it; at lines 605-609, remove the confirmation claim unless evidence is
recorded. In docs/feedbacks/fromarchitect/260801_missing_webview_build_path.md
at lines 11-21, retain the unmeasured status unless a reproducible build
measurement is added.

git rev-parse upstream/main
git rev-parse myk1yt/main
git rev-list --left-right --count upstream/main...myk1yt/main
git branch backup/main-before-sync-260801 myk1yt/main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use immutable recovery references for backups.

git branch backup/... creates a movable branch. The plan requires an immutable recovery reference for every pre-rewrite tip. Use annotated tags or a protected backup-ref procedure, record the object IDs, and prohibit updates to the backup namespace before rewriting branches.

Also applies to: 187-187

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260801_0001_session_fork-pr-rebase-ci/222900_architect-report.md` at
line 160, Replace the movable backup branch command with an immutable
recovery-reference procedure, using annotated tags or a protected backup-ref
namespace for the pre-rewrite tip. Record each referenced object ID and
explicitly prohibit updates to the backup references before any branch
rewriting, including the corresponding command at the other occurrence.

Comment on lines +50 to +55
## Next Step Recommendations

- Proceed with Sub-task 2: rebase each B branch onto the new main (`992585ff8`)
- Use `git rebase main <branch-name>` for each branch, resolving conflicts as needed
- After each successful rebase, verify the branch still builds and tests pass
- Backup tags remain available for rollback if any rebase fails

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not use a plain rebase for these branches.

The plan states that current branches contain copied prerequisite and CI commits. git rebase main <branch-name> will retain those commits and can make each PR include unrelated history. Rebuild each branch from its declared base with the feature-commit manifest, then run git range-diff and changed-file checks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260801_0001_session_fork-pr-rebase-ci/224000_code-report.md` around
lines 50 - 55, Replace the Next Step Recommendations’ plain rebase workflow with
rebuilding each B branch from its declared base using the feature-commit
manifest, excluding copied prerequisite and CI commits. After rebuilding, run
git range-diff and changed-file checks, then verify the branch builds and tests
pass; retain the backup-tag rollback guidance.


## Task Summary

Rebuilt the B04 (shell contracts) branch against the updated fork main (`992585ff8`), cherry-picking only the 3 B04 feature commits while excluding 4 CI-config fix commits. All 4 CI checks and both focused test suites pass.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep release status provisional until current-head gates pass.

These reports call the work successful before all remote evidence is available. The B04 report says remote CI is pending. The coverage report says Codecov may still differ and requires another CI run.

  • docs/260801_0001_session_fork-pr-rebase-ci/224700_code-report.md#L5-L5: state that local CI-equivalent checks passed.
  • docs/260801_0001_session_fork-pr-rebase-ci/224700_code-report.md#L56-L73: keep overall success pending until GitHub Actions passes for the current head SHA.
  • docs/260805_0001_session_ci-all-green/164900_code-report.md#L28-L36: distinguish local coverage from Codecov acceptance.
  • docs/260805_0001_session_ci-all-green/164900_code-report.md#L44-L48: record the current-head Codecov result before declaring the objective complete.
📍 Affects 2 files
  • docs/260801_0001_session_fork-pr-rebase-ci/224700_code-report.md#L5-L5 (this comment)
  • docs/260801_0001_session_fork-pr-rebase-ci/224700_code-report.md#L56-L73
  • docs/260805_0001_session_ci-all-green/164900_code-report.md#L28-L36
  • docs/260805_0001_session_ci-all-green/164900_code-report.md#L44-L48
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260801_0001_session_fork-pr-rebase-ci/224700_code-report.md` at line 5,
Keep the release status provisional until current-head remote gates are
verified: in docs/260801_0001_session_fork-pr-rebase-ci/224700_code-report.md
lines 5-5, describe the results as local CI-equivalent checks passing; in lines
56-73, defer overall success until GitHub Actions passes for the current head
SHA; in docs/260805_0001_session_ci-all-green/164900_code-report.md lines 28-36,
distinguish local coverage from Codecov acceptance; and in lines 44-48, record
the current-head Codecov result before marking the objective complete.


### 5. Test Execution

```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language identifiers to both fenced Markdown blocks.

Both fences omit the language identifier required by Markdown linting.

  • docs/260801_0001_session_fork-pr-rebase-ci/232300_code-report.md#L56-L56: use bash for the shell command block.
  • docs/260801_0001_session_fork-pr-rebase-ci/requirement-checklist.md#L36-L36: use text for the dependency graph block.
🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 56-56: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

📍 Affects 2 files
  • docs/260801_0001_session_fork-pr-rebase-ci/232300_code-report.md#L56-L56 (this comment)
  • docs/260801_0001_session_fork-pr-rebase-ci/requirement-checklist.md#L36-L36
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260801_0001_session_fork-pr-rebase-ci/232300_code-report.md` at line 56,
Add the bash language identifier to the fenced shell command block in
docs/260801_0001_session_fork-pr-rebase-ci/232300_code-report.md at lines 56-56,
and add the text language identifier to the dependency graph fence in
docs/260801_0001_session_fork-pr-rebase-ci/requirement-checklist.md at lines
36-36.

Source: Linters/SAST tools

Comment on lines +189 to +202
function deriveDisplayName(family: ShellFamily, executable: string, profileName?: string): string {
switch (family) {
case "powershell":
return /pwsh/i.test(executable) ? "PowerShell 7" : "Windows PowerShell 5.1"
case "cmd":
return "Command Prompt"
case "wsl":
return profileName ? `WSL: ${profileName}` : "WSL"
case "fish":
return "Fish"
case "posix":
return path.basename(executable)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add an exhaustiveness guard to deriveDisplayName.

The switch has no default branch. The function is typed to return string, but it returns undefined when family is not one of the five handled values. A new ShellFamily member, or an unvalidated value from classifyShellFamily, then produces displayName: undefined inside a ResolvedShell.

Add an explicit fallback so the return type stays honest.

🛡️ Proposed fallback branch
 		case "posix":
 			return path.basename(executable)
+		default:
+			return profileName ?? path.basename(executable)
 	}
 }

Run this script to confirm the ShellFamily union members:

#!/bin/bash
# Confirm the ShellFamily union and classifyShellFamily return values.
fd -t f 'types.ts' src/integrations/terminal/shell --exec cat -n
rg -n -C4 'ShellFamily' src/utils/shell.ts
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/shell/TerminalProfileResolver.ts` around lines 189
- 202, Add an explicit fallback branch to deriveDisplayName so every ShellFamily
value produces a string, preserving the function’s declared return type when
classifyShellFamily yields an unexpected or newly added family. Use the
project’s established exhaustiveness-guard pattern if available, and ensure the
fallback does not return undefined.

Comment on lines +390 to +399
const executable = this.fs.existsSync(POWERSHELL_7_PATH) ? POWERSHELL_7_PATH : POWERSHELL_LEGACY_PATH

return {
executable,
family: "powershell",
displayName: deriveDisplayName("powershell", executable, profileName),
source,
profileName,
trustEvidence: "trustedProfile",
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Verify the legacy PowerShell path before you return it.

If POWERSHELL_7_PATH does not exist, the code returns POWERSHELL_LEGACY_PATH without an existence check. On a Windows host without Windows PowerShell 5.1, the resolver reports a resolved shell with trustEvidence: "trustedProfile" for an executable that is absent. The failure then surfaces later as a terminal spawn error instead of a clean fallback to the next resolution source. Lines 464 and 515 use the same pattern.

Return undefined when neither executable exists.

🐛 Proposed check
-		if (nameLower.includes("powershell")) {
-			const executable = this.fs.existsSync(POWERSHELL_7_PATH) ? POWERSHELL_7_PATH : POWERSHELL_LEGACY_PATH
+		if (nameLower.includes("powershell")) {
+			const executable = this.fs.existsSync(POWERSHELL_7_PATH)
+				? POWERSHELL_7_PATH
+				: this.fs.existsSync(POWERSHELL_LEGACY_PATH)
+					? POWERSHELL_LEGACY_PATH
+					: undefined
+			if (!executable) {
+				return undefined
+			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const executable = this.fs.existsSync(POWERSHELL_7_PATH) ? POWERSHELL_7_PATH : POWERSHELL_LEGACY_PATH
return {
executable,
family: "powershell",
displayName: deriveDisplayName("powershell", executable, profileName),
source,
profileName,
trustEvidence: "trustedProfile",
}
const executable = this.fs.existsSync(POWERSHELL_7_PATH)
? POWERSHELL_7_PATH
: this.fs.existsSync(POWERSHELL_LEGACY_PATH)
? POWERSHELL_LEGACY_PATH
: undefined
if (!executable) {
return undefined
}
return {
executable,
family: "powershell",
displayName: deriveDisplayName("powershell", executable, profileName),
source,
profileName,
trustEvidence: "trustedProfile",
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/shell/TerminalProfileResolver.ts` around lines 390
- 399, Update the PowerShell resolution logic around the executable selection
and return object to verify both POWERSHELL_7_PATH and POWERSHELL_LEGACY_PATH
exist before returning a profile. Return undefined when neither path exists, and
apply the same guard to the equivalent resolution patterns around the logic
referenced at lines 464 and 515 so missing executables fall through to the next
source.

Comment on lines +171 to +182
const profileButton = screen.queryByTestId("option-profile:PowerShell")
if (profileButton) {
act(() => {
fireEvent.click(profileButton)
})

expect(onShellSelectionChange).toHaveBeenCalledWith({
kind: "profile",
profileName: "PowerShell",
})
expect(onTerminalProfilePickerOpened).toHaveBeenCalled()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Make the profile selection assertions unconditional.

The assertions run only when profileButton is truthy. If the component stops rendering the profile:PowerShell option, this test passes with zero assertions and hides the regression. Query the element with getByTestId so a missing option fails the test.

💚 Proposed fix
-		const profileButton = screen.queryByTestId("option-profile:PowerShell")
-		if (profileButton) {
-			act(() => {
-				fireEvent.click(profileButton)
-			})
-
-			expect(onShellSelectionChange).toHaveBeenCalledWith({
-				kind: "profile",
-				profileName: "PowerShell",
-			})
-			expect(onTerminalProfilePickerOpened).toHaveBeenCalled()
-		}
+		const profileButton = screen.getByTestId("option-profile:PowerShell")
+		act(() => {
+			fireEvent.click(profileButton)
+		})
+
+		expect(onShellSelectionChange).toHaveBeenCalledWith({
+			kind: "profile",
+			profileName: "PowerShell",
+		})
+		expect(onTerminalProfilePickerOpened).toHaveBeenCalled()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const profileButton = screen.queryByTestId("option-profile:PowerShell")
if (profileButton) {
act(() => {
fireEvent.click(profileButton)
})
expect(onShellSelectionChange).toHaveBeenCalledWith({
kind: "profile",
profileName: "PowerShell",
})
expect(onTerminalProfilePickerOpened).toHaveBeenCalled()
}
const profileButton = screen.getByTestId("option-profile:PowerShell")
act(() => {
fireEvent.click(profileButton)
})
expect(onShellSelectionChange).toHaveBeenCalledWith({
kind: "profile",
profileName: "PowerShell",
})
expect(onTerminalProfilePickerOpened).toHaveBeenCalled()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx`
around lines 171 - 182, Update the profile selection test around profileButton
to retrieve option-profile:PowerShell with getByTestId instead of conditionally
using queryByTestId. Remove the truthy guard so the click and both callback
assertions always execute, causing the test to fail when the option is not
rendered.

Comment on lines +263 to +266
// The "auto" option must not be re-rendered as a selectable item.
expect(screen.queryByTestId("option-auto")).toBeDefined()
expect(screen.getByTestId("option-profile:PowerShell")).toBeDefined()
expect(screen.getByTestId("option-cmd")).toBeDefined()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the vacuous assertion on the auto option.

The comment states that auto must not be re-rendered as a selectable item, but expect(screen.queryByTestId("option-auto")).toBeDefined() passes in both cases. queryByTestId returns null when the element is absent, and expect(null).toBeDefined() succeeds. The assertion tests nothing.

If the intent is a single auto entry, assert the count instead.

💚 Proposed fix
-		// The "auto" option must not be re-rendered as a selectable item.
-		expect(screen.queryByTestId("option-auto")).toBeDefined()
+		// The "auto" option must appear exactly once, not duplicated from the payload.
+		expect(screen.queryAllByTestId("option-auto")).toHaveLength(1)
 		expect(screen.getByTestId("option-profile:PowerShell")).toBeDefined()
 		expect(screen.getByTestId("option-cmd")).toBeDefined()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// The "auto" option must not be re-rendered as a selectable item.
expect(screen.queryByTestId("option-auto")).toBeDefined()
expect(screen.getByTestId("option-profile:PowerShell")).toBeDefined()
expect(screen.getByTestId("option-cmd")).toBeDefined()
// The "auto" option must appear exactly once, not duplicated from the payload.
expect(screen.queryAllByTestId("option-auto")).toHaveLength(1)
expect(screen.getByTestId("option-profile:PowerShell")).toBeDefined()
expect(screen.getByTestId("option-cmd")).toBeDefined()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx`
around lines 263 - 266, Fix the vacuous auto-option assertion in the
TerminalSettings test by asserting the rendered option-auto entry count,
ensuring exactly one auto entry is present. Keep the existing PowerShell and cmd
assertions unchanged.

Comment on lines +849 to +865
"inlineShell": {
"label": "Inline Terminal Shell",
"description": "Select the shell used for inline terminal command execution. Auto follows your trusted VS Code terminal profile. Custom paths are validated by the extension host.",
"auto": "Auto (follows trusted terminal profile)",
"customPath": "Choose custom executable",
"customPathPlaceholder": "Select a shell executable...",
"effectiveShell": {
"label": "Effective shell",
"family": "Family",
"source": "Source",
"fallback": "Fallback behavior",
"fallbackDescription": "If shell integration fails, commands retry using the same shell family."
},
"error": {
"invalid": "The selected shell is not supported. Choose a trusted profile or a valid shell executable.",
"unavailable": "Shell options are currently unavailable. The extension host may still be initializing."
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Untranslated terminal.inlineShell block in three locale files. The new English block was copied into each non-English locale without translation, so users of these locales see mixed-language text in the inline shell selector.

  • webview-ui/src/i18n/locales/de/settings.json#L849-L865: translate label, description, auto, customPath, customPathPlaceholder, the effectiveShell fields, and both error messages into German.
  • webview-ui/src/i18n/locales/ko/settings.json#L849-L865: translate the same keys into Korean.
  • webview-ui/src/i18n/locales/vi/settings.json#L849-L865: translate the same keys into Vietnamese.
📍 Affects 3 files
  • webview-ui/src/i18n/locales/de/settings.json#L849-L865 (this comment)
  • webview-ui/src/i18n/locales/ko/settings.json#L849-L865
  • webview-ui/src/i18n/locales/vi/settings.json#L849-L865
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/i18n/locales/de/settings.json` around lines 849 - 865,
Translate every string in the terminal.inlineShell block: update the German keys
in webview-ui/src/i18n/locales/de/settings.json lines 849-865, the Korean keys
in webview-ui/src/i18n/locales/ko/settings.json lines 849-865, and the
Vietnamese keys in webview-ui/src/i18n/locales/vi/settings.json lines 849-865,
covering label, description, auto, customPath, customPathPlaceholder, all
effectiveShell fields, and both error messages while preserving the existing
JSON structure and keys.

@github-actions github-actions Bot removed the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant