Skip to content

fix(terminal): honor configured terminal profile in background exec mode#11283

Open
sanidhyasin wants to merge 3 commits into
cline:mainfrom
sanidhyasin:fix/background-exec-terminal-profile
Open

fix(terminal): honor configured terminal profile in background exec mode#11283
sanidhyasin wants to merge 3 commits into
cline:mainfrom
sanidhyasin:fix/background-exec-terminal-profile

Conversation

@sanidhyasin

Copy link
Copy Markdown

Related Issue

Issue: #11017

Description

When the terminal Execution Mode is set to Background Exec, Cline ignored the Default Terminal Profile setting and always spawned the system default shell (e.g. fish), even when the user explicitly selected another profile (e.g. bash).

The root cause is in StandaloneTerminalManager (used for background execution, and in CLI/JetBrains environments). Unlike VscodeTerminalManager, it never resolved the configured defaultTerminalProfile into a shell path:

  • getOrCreateTerminal() created terminals without a shellPath, so StandaloneTerminalProcess.run() fell back to process.env.SHELL || "/bin/bash".
  • setDefaultTerminalProfile() passed the raw profile id (e.g. "bash") into handleTerminalProfileChange(), which expects a shell path.

This change mirrors the existing, working behavior of VscodeTerminalManager:

  • Resolve the profile to a shell path with getShellForProfile() and pass it as shellPath when creating terminals.
  • Match/reuse terminals by shellPath, so switching profiles spawns the correct shell instead of reusing a terminal running the old shell.
  • Resolve the profile id to a shell path before calling handleTerminalProfileChange().

getShellForProfile() maps "default" to undefined (preserving the existing default-shell behavior) and any other profile id to its configured shell path. Importing @utils/shell is safe in standalone/CLI builds because vscode is stubbed at runtime there.

Test Procedure

  • Added unit tests in StandaloneTerminalManager.profile.test.ts covering:
    • a configured profile (bash) resolves to its shell path on created terminals (both TerminalInfo.shellPath and the underlying terminal's _shellPath);
    • the default profile leaves shellPath undefined (unchanged behavior);
    • switching profiles does not reuse a terminal whose shell no longer matches.
  • Manual repro from the issue: set Default Terminal Profile to bash, Terminal Execution Mode to Background Exec, on a system whose default shell is fish. Before this change Cline ran commands under fish; after it runs under bash.

I scoped the change to mirror VscodeTerminalManager so behavior stays consistent across hosts. The default profile path is unchanged, so existing setups are unaffected.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)

Pre-flight Checklist

  • Changes are limited to a single feature, bugfix or chore
  • Tests added/updated where it makes sense
  • A changeset is included (.changeset/honest-otters-shine.md)

@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes StandaloneTerminalManager (used in Background Exec mode) ignoring the user-configured "Default Terminal Profile" and always spawning the system default shell. The fix mirrors VscodeTerminalManager: profile id → shell path resolution is applied both when creating terminals and when matching/reusing them.

  • getOrCreateTerminal(): computes expectedShellPath once per call and filters both the CWD-match and the fallback-reuse candidates against it, then passes it to registry.createTerminal() so TerminalInfo.shellPath is set correctly.
  • setDefaultTerminalProfile(): resolves the incoming profile id to a shell path before forwarding to handleTerminalProfileChange(), so idle terminals running the old shell are properly closed on profile switch.
  • Tests: three new cases cover bash/zsh (posix) or powershell-legacy/cmd (Windows) — correct shell on create, undefined for the default profile, and no terminal reuse across profile switches.

Confidence Score: 5/5

Safe to merge — the change is narrowly scoped to StandaloneTerminalManager and correctly mirrors the already-working VscodeTerminalManager behavior.

The logic is straightforward: profile id resolution is added in exactly two places (terminal creation and profile-change cleanup), the default-profile path (shellPath = undefined) is preserved unchanged, and the new tests validate all three behavioural cases across both posix and Windows platforms. No regressions were found in terminal reuse, background-command tracking, or the handleTerminalProfileChange cleanup path.

No files require special attention.

Important Files Changed

Filename Overview
apps/vscode/src/integrations/terminal/standalone/StandaloneTerminalManager.ts Core fix: resolves the configured terminal profile to a shell path in getOrCreateTerminal() and setDefaultTerminalProfile(), mirroring VscodeTerminalManager; terminal matching and creation now both honour the expectedShellPath.
apps/vscode/src/integrations/terminal/tests/StandaloneTerminalManager.profile.test.ts New unit tests covering profile-to-shell-path resolution, default-profile preservation, and cross-profile terminal isolation; platform-aware profile selection handled correctly.
.changeset/honest-otters-shine.md Patch-level changeset entry accurately describing the terminal profile fix.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["getOrCreateTerminal(cwd)"] --> B["Resolve expectedShellPath\n(profile !== 'default'\n  ? getShellForProfile(profile)\n  : undefined)"]
    B --> C{"Find terminal\n!busy &&\nshellPath === expected &&\n_cwd === cwd?"}
    C -- match --> D["Return existing terminal"]
    C -- no match --> E{"terminalReuseEnabled?"}
    E -- yes --> F{"Find terminal\n!busy &&\nshellPath === expected?"}
    F -- match --> G["cd to cwd\nReturn reused terminal"]
    F -- no match --> H["Create new terminal\nwith shellPath = expected"]
    E -- no --> H
    H --> I["registry.createTerminal\n({ cwd, name, shellPath })"]
    I --> J["TerminalInfo.shellPath set\n→ StandaloneTerminalProcess.run\n  uses shellPath"]

    K["setDefaultTerminalProfile(profile)"] --> L["previousProfile !== profile?"]
    L -- yes --> M["newShellPath =\nprofile !== 'default'\n  ? getShellForProfile(profile)\n  : undefined"]
    M --> N["handleTerminalProfileChange(newShellPath)\nclose idle terminals where\nshellPath !== newShellPath"]
    L -- no --> O["return {closedCount:0, busyTerminals:[]}"]
Loading

Reviews (3): Last reviewed commit: "test(terminal): use built-in Windows Pow..." | Re-trigger Greptile

@sanidhyasin

Copy link
Copy Markdown
Author

Pushed a follow-up (a0eaca0):

  • Review feedback: dropped the redundant (terminal as any)._shellPath assertion — terminalInfo.shellPath already carries the same value (the registry sets both from options.shellPath), so the private-field check added no coverage and was brittle to a rename. Thanks @greptile-apps.
  • CI (windows-latest) fix: the reuse test assumed bash/zsh resolve to distinct shells, but getAvailableTerminalProfiles() only exposes those on macOS/Linux — on Windows both fell back to the same default shell, so the terminal was correctly reused and the assertion failed. The tests now pick a platform-appropriate profile pair (powershell-7/cmd on Windows, bash/zsh elsewhere) and guard the distinctness assertion.

@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Both changes look good. The test file is clean and the fixes are well-reasoned:

  1. Dropped _shellPath assertion — correct call. terminalInfo.shellPath is the stable public surface; the private field was just noise.

  2. Windows CI fix — the platform-appropriate profile pair plus the distinctness guard (if (getShellForProfile(PROFILE_A) === getShellForProfile(PROFILE_B)) { return }) handles the edge case cleanly without skipping the test entirely on any platform.

One minor note: powershell-7 assumes PowerShell 7 is installed on the Windows CI runner. If it's not available, getShellForProfile("powershell-7") would likely return undefined, making it the same as cmd's fallback, which would trigger the guard and skip the assertion silently. That's safe behavior — just worth verifying the CI image has PS7. If it doesn't, ["powershell", "cmd"] (the built-in Windows PowerShell) might be a more reliable choice.

Otherwise this is solid — nice work addressing both points.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

@sanidhyasin

Copy link
Copy Markdown
Author

Good catch on the PS7 assumption — switched the Windows pair to powershell-legacy (built-in Windows PowerShell 5.x, present on every Windows install) in f1da853.

For the record: getAvailableTerminalProfiles() pushes the profile entries with hardcoded constant paths and does no fs.existsSync check, so getShellForProfile("powershell-7") would actually have returned the constant PS7 path (not undefined) even if PS7 weren't installed — meaning the guard wouldn't have silently skipped. But powershell-legacy is the more robust/clear choice regardless.

In background execution mode the StandaloneTerminalManager always spawned
the system default shell, ignoring the user's "Default Terminal Profile"
setting. Unlike VscodeTerminalManager, it never resolved the configured
profile to a shell path when creating terminals.

Resolve the profile to a shell path via getShellForProfile() and pass it
when creating terminals, and match/reuse terminals by shell path so that
switching profiles spawns the correct shell. Also resolve the profile id
before handleTerminalProfileChange(), which expects a shell path.

Fixes cline#11017
…assertion

The reuse test assumed bash/zsh resolve to distinct shells, which fails on
Windows where getAvailableTerminalProfiles() exposes powershell/cmd instead
(both bash and zsh fell back to the same default shell, so the terminal was
reused). Pick a platform-appropriate pair of profiles and guard the
distinctness assertion in case a platform ever maps them to the same shell.

Also drop the redundant private _shellPath assertion per review feedback:
terminalInfo.shellPath already carries the same value the registry assigns.
powershell-legacy (Windows PowerShell 5.x) is present on every Windows
install, whereas powershell-7 may be absent on a CI runner. Both resolve to
distinct constant paths vs cmd, so this only hardens the test's assumptions.
@sanidhyasin sanidhyasin force-pushed the fix/background-exec-terminal-profile branch from f1da853 to 90f57ea Compare June 4, 2026 20:51
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