Skip to content

feature: unified-shell-resolution (1/4) - #1120

Open
myk1yt wants to merge 7 commits into
Zoo-Code-Org:mainfrom
myk1yt:pr/b04-shell-contracts-v2
Open

feature: unified-shell-resolution (1/4)#1120
myk1yt wants to merge 7 commits into
Zoo-Code-Org:mainfrom
myk1yt:pr/b04-shell-contracts-v2

Conversation

@myk1yt

@myk1yt myk1yt commented Aug 4, 2026

Copy link
Copy Markdown

Stack Position

  • Feature Branch: feature/unified-shell-resolution
  • Stage: 1/4
  • Depends on: None

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 typed shell selection/profile/default contracts, backward-compatible serialization, settings UI and locale. The UI uses SettingsView.tsx's cachedState and does not change command execution.

Included Files

  • packages/types/src/terminal.ts
  • packages/types/src/global-settings.ts
  • packages/types/src/vscode-extension-host.ts
  • webview-ui/src/components/settings/TerminalSettings.tsx
  • webview-ui/src/components/settings/SettingsView.tsx
  • Related locale and direct tests

Exclusion Scope

  • Command execution changes
  • Resolver, lifecycle, scheduler, extension wiring
  • Settings implementation that directly binds to live extension state
  • All items in the common removal rules

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added inline terminal shell selection with automatic mode, saved profiles, explicit paths, and custom executable browsing.
    • Displays the effective shell and provides feedback for unavailable or invalid selections.
    • Added queued and recovering terminal execution statuses with additional error details.
    • Settings changes can be saved or discarded while remaining synchronized.
  • Localization
    • Added localized terminal shell settings text across supported languages.
  • Tests
    • Added coverage for shell selection, persistence, validation, fallback behavior, and settings interactions.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0796464f-6346-49f2-b46a-70159a8bb4b9

📥 Commits

Reviewing files that changed from the base of the PR and between d6a4150 and fe104b2.

📒 Files selected for processing (1)
  • progress.txt
💤 Files with no reviewable changes (1)
  • progress.txt

📝 Walkthrough

Walkthrough

The PR adds typed terminal shell selection for automatic, profile, and explicit path modes. It adds extension-host messages, inline settings UI, buffered save behavior, validation tests, terminal status fields, and localization strings.

Changes

Terminal shell selection

Layer / File(s) Summary
Shell selection and messaging contracts
packages/types/src/global-settings.ts, packages/types/src/terminal.ts, packages/types/src/vscode-extension-host.ts
Settings and messages support automatic, profile, and path selections. Legacy execaShellPath remains available. Terminal statuses add queued, recovering, and error metadata.
Inline shell selector and feedback
webview-ui/src/components/settings/TerminalSettings.tsx, webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx, webview-ui/src/i18n/locales/*/settings.json
TerminalSettings requests shell options, renders supported selections, requests custom paths, and displays effective-shell details and errors. Tests and localization strings cover the UI.
Settings buffering and save flow
webview-ui/src/components/settings/SettingsView.tsx, webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx
SettingsView buffers shell changes, marks settings dirty, sends selections on save, and clears them on discard or state synchronization.
Contract validation coverage
packages/types/src/__tests__/terminal-shell-settings.spec.ts, codecov.yml
Tests validate schemas, global settings, message payloads, and discriminated unions. Patch coverage checks are informational.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SettingsView
  participant TerminalSettings
  participant ExtensionHost
  TerminalSettings->>ExtensionHost: requestTerminalShellOptions
  ExtensionHost-->>TerminalSettings: terminalShellOptions payload
  TerminalSettings->>SettingsView: onShellSelectionChange(selection)
  SettingsView->>ExtensionHost: setTerminalShellSelection on save
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: edelauna

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the feature, scope, exclusions, and testing approach, but it omits the required issue link and pre-submission checklist. Add the required template sections, provide an approved issue number after “Closes: #”, complete the checklist, and document UI snapshot, documentation, and reviewer-contact decisions.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the unified shell resolution feature and its staged position.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

❌ Patch coverage is 97.95918% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ew-ui/src/components/settings/TerminalSettings.tsx 97.61% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@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: 9

🤖 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
`@webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx`:
- Around line 33-43: Replace the any-typed capturedTerminalProps and mocked
TerminalSettings props with the existing TerminalSettingsProps shape, preserving
a nullable current value. Import or reference the webview-ui package’s
TerminalSettingsProps type using the correct package path, and type the mock
callback contract through onShellSelectionChange instead of suppressing it with
as any.
- Around line 93-110: Update the partial vi.mock factories for the Tab module
and ToggleSwitch module to be async, await vi.importActual (or use
importOriginal), and spread the resolved exports before applying the Tab* and
ToggleSwitch overrides. Preserve the existing mock implementations while
ensuring all unmodified exports remain available.

In
`@webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx`:
- Around line 164-175: Update the profile-selection test to use getByTestId for
the PowerShell option instead of queryByTestId, remove the conditional guard,
and execute the click plus onShellSelectionChange and
onTerminalProfilePickerOpened assertions unconditionally.

In `@webview-ui/src/components/settings/SettingsView.tsx`:
- Line 198: Update SettingsView.tsx lines 198-198 to hydrate
terminalShellSelection into cachedState without marking the form dirty; update
lines 463-477 to track the submitted selection and clear or reconcile the
pending value after host acknowledgement; update lines 917-926 to render the
reconciled cached selection. Add separate tests in
webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx
lines 317-345 covering persisted-selection hydration with Save disabled and
host-state acknowledgement after Save.

In `@webview-ui/src/components/settings/TerminalSettings.tsx`:
- Around line 301-305: In TerminalSettings, add a typed response handler for the
requestCustomShellPath result that converts the selected path into a
TerminalShellSelection and passes it to onShellSelectionChange. Ensure the
existing SettingsView buffering flow persists the selection through
setTerminalShellSelection only when Save is used.
- Around line 317-327: Update the effective shell display in TerminalSettings to
render shellOptions.effectiveShell.label alongside the existing family and
source values, using the corresponding effectiveShell.label translation key. Add
an assertion in TerminalSettings.shell.spec.tsx covering the resolved executable
label.
- Around line 335-340: Update the shellError rendering in TerminalSettings to
use the unavailable-shell translation for TerminalShellOptionsPayload discovery
failures instead of the selection-specific invalid message. Preserve invalid
messaging only when the error represents an unsupported selected shell, and
update the relevant shell test to assert the unavailable message.

In `@webview-ui/src/i18n/locales/de/settings.json`:
- Line 851: Update the German settings strings around the description and the
related entries at the referenced symbols to use the feminine grammatical forms:
“die Shell”, “Aktive Shell”, and “Die ausgewählte Shell”. Preserve the existing
meanings and surrounding localization text.

In `@webview-ui/src/i18n/locales/pt-BR/settings.json`:
- Line 860: The fallbackDescription field in the pt-BR settings translation uses
"os comandos são reutilizados" (commands are reused) but should instead use "os
comandos são executados novamente" (commands are executed again) to accurately
describe the fallback behavior. Update the fallbackDescription string to replace
the phrase about reusing with the correct phrase about executing commands again.
🪄 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: bd97182f-570b-4d50-a157-1e3fd8b44b8f

📥 Commits

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

📒 Files selected for processing (26)
  • 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
  • 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 +33 to +43
// Capture the props SettingsView passes to TerminalSettings so tests can
// drive onShellSelectionChange directly.
const capturedTerminalProps = vi.hoisted(() => ({ current: null as any }))

vi.mock("../TerminalSettings", () => ({
DEFAULT_PROFILE_VALUE: "__zoo_code_follow_vscode_sentinel__",
TerminalSettings: (props: any) => {
capturedTerminalProps.current = props
return <div data-testid="terminal-settings-stub" />
},
}))

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 \
  webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 922


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'repo files matching target:\n'
git ls-files | grep -F 'webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx' || true

printf '\npackage location:\n'
fd -a 'package.json$' . | sed 's#^\./##' | head -20

printf '\ntry lint from repo root with relative pattern:\n'
pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 \
  webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx || true

printf '\ntry lint with file directly:\n'
pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 \
  webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx || true

printf '\nsearch test file contents:\n'
sed -n '1,130p' webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx

printf '\nsearch related source exports/types:\n'
rg -n "type TerminalShellSelection|interface TerminalShellSelection|enum TerminalShellSelection|onShellSelectionChange|TerminalSettings" webview-ui/src/components/settings -S

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 14000


Replace the untyped terminal-settings test double.

The settings subdirectory is not inside package src, so ESLint needs the webview-ui package path. The file also uses as any on lines 36 and 39, which hides the SettingsViewTerminalSettings callback contract. Use the existing TerminalSettingsProps/onShellSelectionChange shape with a nullable current.

🤖 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 33 - 43, Replace the any-typed capturedTerminalProps and mocked
TerminalSettings props with the existing TerminalSettingsProps shape, preserving
a nullable current value. Import or reference the webview-ui package’s
TerminalSettingsProps type using the correct package path, and type the mock
callback contract through onShellSelectionChange instead of suppressing it with
as any.

Source: Coding guidelines

Comment on lines +93 to +110
vi.mock("../../../components/common/Tab", () => ({
...vi.importActual("../../../components/common/Tab"),
Tab: ({ children }: any) => <div data-testid="tab-container">{children}</div>,
TabHeader: ({ children }: any) => <div data-testid="tab-header">{children}</div>,
TabContent: ({ children, "data-testid": dataTestId }: any) => (
<div data-testid={dataTestId || "tab-content"}>{children}</div>
),
TabList: ({ children, value, "data-testid": dataTestId }: any) => (
<div data-testid={dataTestId} data-value={value}>
{children}
</div>
),
TabTrigger: ({ children, value, "data-testid": dataTestId, onClick, isSelected }: any) => (
<button data-testid={dataTestId} data-value={value} data-selected={isSelected} onClick={onClick}>
{children}
</button>
),
}))

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 4 'vi\.mock\(|vi\.importActual\(' \
  webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx

pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 \
  webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 2972


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files | rg 'SettingsView\.shell-selection\.spec\.tsx$|package\.json$|vite.config|vitest|eslint'

echo
echo "Relevant mock locations:"
rg -n -C 5 'vi\.mock\(|vi\.importActual\(' webview-ui/src || true

echo
echo "ESLint config/package hints:"
rg -n '"eslint"|"vitest"|"import/no-unresolved"|"no-floating-promises"|no-floating-promises|plugin:`@typescript-eslint/recommended`|recommended' package.json webview-ui package.json webview-ui/package.json 2>/dev/null || true

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 50378


🌐 Web query:

Vitest vi.mock vi.importActual async factory importOriginal documentation

💡 Result:

In Vitest, vi.mock is used to substitute modules with mocks [1][2]. The factory function provided to vi.mock allows for dynamic module creation and accepts an importOriginal helper as its first argument [1][3][2][4]. vi.mock factory and importOriginal When you use vi.mock(path, factory), the factory is a function that can receive an importOriginal parameter [1][2]. This parameter is a helper function you must call (and await) to retrieve the original, unmocked module [3][5][4]. This is particularly useful when you want to partially mock a module—by spreading the original module's exports and overriding specific ones [6][7][2]. Example: vi.mock('./example.js', async (importOriginal) => { const originalModule = await importOriginal // Get the original module return {...originalModule, // Keep original functionality methodToMock: vi.fn, // Override specific export } }) vi.importActual vi.importActual(path) is a standalone function that imports the original module, bypassing any mocks [1][7][2]. While you can use it inside a vi.mock factory, the importOriginal helper provided directly by the factory is the preferred approach, especially when using dynamic imports (e.g., vi.mock(import('./path'))) because it can automatically inherit types and handle paths more effectively [2][5]. Key Differences and Notes - Hoisting: Both vi.mock calls are hoisted to the top of the file and executed before other imports [1][6][8][2]. - Dynamic Imports: Using dynamic imports (vi.mock(import('./path'))) in the vi.mock call is recommended for better IDE support and type safety [2][5]. - Asynchronicity: The importOriginal helper and the vi.importActual function are asynchronous and must be awaited [3][2][4]. The documentation emphasizes using vi.spyOn if you only need to spy on specific methods without replacing the entire module, as it is often a simpler alternative [3][6][5].

Citations:


🌐 Web query:

Vitest importOriginal helper types return Promise or actual module

💡 Result:

In Vitest, the importOriginal helper function provided within the vi.mock factory is indeed an asynchronous function that returns a Promise [1][2]. When using vi.mock, you must await the result of importOriginal to access the original module's exports [1][2]. Example Usage: vi.mock(import('./your-module.js'), async (importOriginal) => { // importOriginal returns a Promise, so you must await it const originalModule = await importOriginal; return {...originalModule, // Add your mocks here someExport: vi.fn, }; }); By using dynamic imports (e.g., import('./path')) in your vi.mock call, Vitest can automatically infer the types for importOriginal based on the actual module being mocked, providing full TypeScript support and IDE autocompletion for the returned module object [3][4][5].

Citations:


Await the actual module in partial vi.mock() factories.

Make both factory functions async, await vi.importActual() or use importOriginal, then spread the resolved exports before overriding Tab* and ToggleSwitch. This keeps unmodified exports from being replaced and prevents the factory promise from being ignored.

🤖 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 93 - 110, Update the partial vi.mock factories for the Tab module
and ToggleSwitch module to be async, await vi.importActual (or use
importOriginal), and spread the resolved exports before applying the Tab* and
ToggleSwitch overrides. Preserve the existing mock implementations while
ensuring all unmodified exports remain available.

Source: Coding guidelines

Comment on lines +164 to +175
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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the profile-selection assertion unconditional.

Line 164 uses queryByTestId, and the if block skips every assertion when the profile option is absent. A regression that removes profile options will pass this test. Use getByTestId and run the click assertions unconditionally.

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 164 - 175, Update the profile-selection test to use getByTestId for
the PowerShell option instead of queryByTestId, remove the conditional guard,
and execute the click plus onShellSelectionChange and
onTerminalProfilePickerOpened assertions unconditionally.

terminalZshP10k,
terminalZdotdir,
terminalProfile,
terminalShellSelection,

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

Hydrate and reconcile terminal shell selection state.

For an initial state message that changes only terminalShellSelection, cachedState retains its pre-hydration value. At Line 228, the cache refresh exits unless the API configuration or mode changes. Line 917 can then send undefined to TerminalSettings instead of the persisted selection. After Save, the still-pending value remains preferred and can be posted again on a later save.

  • webview-ui/src/components/settings/SettingsView.tsx#L198-L198: synchronize the persisted selection into cachedState during hydration without setting the dirty flag.
  • webview-ui/src/components/settings/SettingsView.tsx#L463-L477: track the submitted selection and clear or reconcile the pending value after the extension host acknowledges it.
  • webview-ui/src/components/settings/SettingsView.tsx#L917-L926: render the reconciled cached value after acknowledgement.
  • webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx#L317-L345: add separate tests for persisted-selection hydration with Save disabled and host-state acknowledgement after Save.

As per coding guidelines, “For SettingsView, keep inputs in local cachedState until save, and test automatic initialization separately from genuine user edits.”

📍 Affects 2 files
  • webview-ui/src/components/settings/SettingsView.tsx#L198-L198 (this comment)
  • webview-ui/src/components/settings/SettingsView.tsx#L463-L477
  • webview-ui/src/components/settings/SettingsView.tsx#L917-L926
  • webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx#L317-L345
🤖 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/SettingsView.tsx` at line 198, Update
SettingsView.tsx lines 198-198 to hydrate terminalShellSelection into
cachedState without marking the form dirty; update lines 463-477 to track the
submitted selection and clear or reconcile the pending value after host
acknowledgement; update lines 917-926 to render the reconciled cached selection.
Add separate tests in
webview-ui/src/components/settings/__tests__/SettingsView.shell-selection.spec.tsx
lines 317-345 covering persisted-selection hydration with Save disabled and
host-state acknowledgement after Save.

Source: Coding guidelines

Comment on lines +301 to +305
onClick={() => {
// Ask the extension host to open a native file picker
// for selecting a shell executable path.
vscode.postMessage({ type: "requestCustomShellPath" })
}}

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline webview-ui/src/components/settings/TerminalSettings.tsx --items all
ast-grep outline webview-ui/src/components/settings/SettingsView.tsx --items all

rg -n -C 8 --glob '*.ts' --glob '*.tsx' \
  'requestCustomShellPath|customShellPath|terminalShellSelection|onShellSelectionChange|setTerminalShellSelection' .

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== TerminalSettings relevant handler =="
sed -n '76,131p' webview-ui/src/components/settings/TerminalSettings.tsx | cat -n

echo "== TerminalSettings picker options/paths =="
sed -n '239,310p' webview-ui/src/components/settings/TerminalSettings.tsx | cat -n

echo "== SettingsView TerminalSettings props =="
sed -n '909,926p' webview-ui/src/components/settings/SettingsView.tsx | cat -n

echo "== exact requestCustomShellPath occurrences =="
rg -n -C 3 'requestCustomShellPath|customShell' .

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 8670


Wire the custom shell picker result into TerminalSettings buffering.

SettingsView buffers shell changes through TerminalSettings.onShellSelectionChange, but TerminalSettings has no response handler for requestCustomShellPath. Add a typed picker-result message handler that converts the chosen path to TerminalShellSelection, calls onShellSelectionChange, and lets SettingsView persist it via setTerminalShellSelection on Save.

🤖 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 301 -
305, In TerminalSettings, add a typed response handler for the
requestCustomShellPath result that converts the selected path into a
TerminalShellSelection and passes it to onShellSelectionChange. Ensure the
existing SettingsView buffering flow persists the selection through
setTerminalShellSelection only when Save is used.

Comment on lines +317 to +327
<div className="font-medium">
{t("settings:terminal.inlineShell.effectiveShell.label")}
</div>
<div>
{t("settings:terminal.inlineShell.effectiveShell.family")}:{" "}
{shellOptions.effectiveShell.family}
</div>
<div>
{t("settings:terminal.inlineShell.effectiveShell.source")}:{" "}
{shellOptions.effectiveShell.source}
</div>

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

Show the effective shell executable.

TerminalShellOptionsPayload.effectiveShell.label contains the display label for the resolved executable. These lines show only the heading, family, and source. Add the label and assert it in webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx Lines 178-198.

Proposed fix
 <div className="font-medium">
   {t("settings:terminal.inlineShell.effectiveShell.label")}
 </div>
+<div>{shellOptions.effectiveShell.label}</div>
 <div>
📝 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
<div className="font-medium">
{t("settings:terminal.inlineShell.effectiveShell.label")}
</div>
<div>
{t("settings:terminal.inlineShell.effectiveShell.family")}:{" "}
{shellOptions.effectiveShell.family}
</div>
<div>
{t("settings:terminal.inlineShell.effectiveShell.source")}:{" "}
{shellOptions.effectiveShell.source}
</div>
<div className="font-medium">
{t("settings:terminal.inlineShell.effectiveShell.label")}
</div>
<div>{shellOptions.effectiveShell.label}</div>
<div>
{t("settings:terminal.inlineShell.effectiveShell.family")}:{" "}
{shellOptions.effectiveShell.family}
</div>
<div>
{t("settings:terminal.inlineShell.effectiveShell.source")}:{" "}
{shellOptions.effectiveShell.source}
</div>
🤖 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 display in TerminalSettings to render
shellOptions.effectiveShell.label alongside the existing family and source
values, using the corresponding effectiveShell.label translation key. Add an
assertion in TerminalSettings.shell.spec.tsx covering the resolved executable
label.

Comment on lines +335 to +340
{shellError && (
<div
className="mt-2 text-vscode-errorForeground text-sm"
data-testid="terminal-inline-shell-error">
{t("settings:terminal.inlineShell.error.invalid")}
</div>

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

Use the unavailable-shell message for discovery failures.

TerminalShellOptionsPayload.error represents shell-option discovery failure. This block always renders error.invalid. A discovery error such as Service unavailable incorrectly tells the user that the selected shell is unsupported. Render error.unavailable, or add a discriminated error type before using selection-specific text. Update the test at webview-ui/src/components/settings/__tests__/TerminalSettings.shell.spec.tsx Lines 200-215 to assert the message.

Proposed fix
- {t("settings:terminal.inlineShell.error.invalid")}
+ {t("settings:terminal.inlineShell.error.unavailable")}
📝 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
{shellError && (
<div
className="mt-2 text-vscode-errorForeground text-sm"
data-testid="terminal-inline-shell-error">
{t("settings:terminal.inlineShell.error.invalid")}
</div>
{shellError && (
<div
className="mt-2 text-vscode-errorForeground text-sm"
data-testid="terminal-inline-shell-error">
{t("settings:terminal.inlineShell.error.unavailable")}
</div>
🤖 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 335 -
340, Update the shellError rendering in TerminalSettings to use the
unavailable-shell translation for TerminalShellOptionsPayload discovery failures
instead of the selection-specific invalid message. Preserve invalid messaging
only when the error represents an unsupported selected shell, and update the
relevant shell test to assert the unavailable message.

},
"inlineShell": {
"label": "Inline-Terminal-Shell",
"description": "Wähle das Shell für die Inline-Terminal-Befehlsausführung. Auto folgt deinem vertrauenswürdigen VS Code Terminal-Profil. Benutzerdefinierte Pfade werden vom Extension-Host validiert.",

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

Correct the German grammatical gender for “Shell”.

“Shell” is feminine in standard German. These strings use neuter forms. Use die Shell, Aktive Shell, and Die ausgewählte Shell.

Proposed fix
- "description": "Wähle das Shell für die Inline-Terminal-Befehlsausführung. Auto folgt deinem vertrauenswürdigen VS Code Terminal-Profil. Benutzerdefinierte Pfade werden vom Extension-Host validiert.",
+ "description": "Wähle die Shell für die Inline-Terminal-Befehlsausführung. Auto folgt deinem vertrauenswürdigen VS Code Terminal-Profil. Benutzerdefinierte Pfade werden vom Extension-Host validiert.",

- "label": "Aktives Shell",
+ "label": "Aktive Shell",

- "invalid": "Das ausgewählte Shell wird nicht unterstützt. Wähle ein vertrauenswürdiges Profil oder ein gültiges Shell-Programm.",
+ "invalid": "Die ausgewählte Shell wird nicht unterstützt. Wähle ein vertrauenswürdiges Profil oder ein gültiges Shell-Programm.",

Also applies to: 856-856, 863-863

🤖 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` at line 851, Update the German
settings strings around the description and the related entries at the
referenced symbols to use the feminine grammatical forms: “die Shell”, “Aktive
Shell”, and “Die ausgewählte Shell”. Preserve the existing meanings and
surrounding localization text.

"family": "Família",
"source": "Fonte",
"fallback": "Comportamento de fallback",
"fallbackDescription": "Se a integração do shell falhar, os comandos são reutilizados com a mesma família de shell."

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

Correct the fallback action description.

“Os comandos são reutilizados” says that commands are reused. The fallback executes the commands again. Replace it with “os comandos são executados novamente”.

🤖 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` at line 860, The
fallbackDescription field in the pt-BR settings translation uses "os comandos
são reutilizados" (commands are reused) but should instead use "os comandos são
executados novamente" (commands are executed again) to accurately describe the
fallback behavior. Update the fallbackDescription string to replace the phrase
about reusing with the correct phrase about executing commands again.

@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 4, 2026
Patch coverage checks were blocking 10+ PRs with 80%/70% thresholds.
Changed to informational: true so patch coverage is reported but not
a required status check.

@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: 1

🤖 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 `@codecov.yml`:
- Around line 1-57: Convert the entire codecov.yml file to LF line endings,
preserving all existing coverage, flag, component, and comment configuration
unchanged.
🪄 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: 79f735b6-6b50-439d-8956-6a77ca2a733a

📥 Commits

Reviewing files that changed from the base of the PR and between d59cdba and f1a9832.

📒 Files selected for processing (22)
  • codecov.yml
  • packages/types/src/global-settings.ts
  • packages/types/src/vscode-extension-host.ts
  • webview-ui/src/components/settings/SettingsView.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 (21)
  • webview-ui/src/i18n/locales/en/settings.json
  • webview-ui/src/i18n/locales/id/settings.json
  • webview-ui/src/i18n/locales/ca/settings.json
  • webview-ui/src/i18n/locales/zh-CN/settings.json
  • webview-ui/src/i18n/locales/pl/settings.json
  • webview-ui/src/i18n/locales/nl/settings.json
  • webview-ui/src/i18n/locales/vi/settings.json
  • webview-ui/src/i18n/locales/zh-TW/settings.json
  • webview-ui/src/i18n/locales/de/settings.json
  • webview-ui/src/i18n/locales/fr/settings.json
  • webview-ui/src/i18n/locales/it/settings.json
  • webview-ui/src/i18n/locales/ru/settings.json
  • webview-ui/src/i18n/locales/pt-BR/settings.json
  • packages/types/src/vscode-extension-host.ts
  • packages/types/src/global-settings.ts
  • webview-ui/src/i18n/locales/tr/settings.json
  • webview-ui/src/i18n/locales/hi/settings.json
  • webview-ui/src/components/settings/SettingsView.tsx
  • webview-ui/src/i18n/locales/es/settings.json
  • webview-ui/src/i18n/locales/ja/settings.json
  • webview-ui/src/i18n/locales/ko/settings.json

Comment thread codecov.yml Outdated
@myk1yt
myk1yt force-pushed the pr/b04-shell-contracts-v2 branch from f1ca0e8 to 952963b Compare August 7, 2026 04:11
@github-actions github-actions Bot added awaiting-review PR changes are ready and waiting for maintainer re-review and removed awaiting-review PR changes are ready and waiting for maintainer re-review labels Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-review PR changes are ready and waiting for maintainer re-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant