fix(core): align shell tool description with configured shell#4170
Conversation
wenshao
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅ — glm-5.1 via Qwen Code /review
wenshao
left a comment
There was a problem hiding this comment.
[Suggestion] Missing test coverage: the getDescription tests cover Git Bash detection via MSYSTEM=MINGW64 but do not cover the TERM-based Git Bash path (TERM containing msys or cygwin, without MSYSTEM set). getShellConfiguration() handles this case but the shell description layer has no corresponding test, leaving a regression gap for shell-aware description generation.
| function getShellToolDescription(): string { | ||
| const shellConfiguration = getShellConfiguration(); | ||
| const executionWrapper = getShellExecutionWrapper(shellConfiguration); | ||
| const isWindows = os.platform() === 'win32'; |
There was a problem hiding this comment.
[Suggestion] processGroupNote still uses os.platform() === 'win32' (platform-based) instead of being shell-aware like the rest of the description. When Windows runs Git Bash (shell='bash'), the description says "The active shell is Bash" with Bash quoting guidance, but the process group note (kill -- -PGID) is suppressed because isWindows=true. The test explicitly asserts this behavior (.not.toContain('Command process group can be terminated')), but it creates an inconsistency: the model gets Bash instructions for quoting/sequencing but misses the process group management note.
If Git Bash on Windows genuinely does not support kill -- -PGID, consider adding a comment explaining why the platform check is intentional here while the rest of the function is shell-aware. If it does support it, change the check to shellConfiguration.shell === 'bash'.
| const isWindows = os.platform() === 'win32'; | |
| // Git Bash on Windows does not support POSIX process group signals, | |
| // so we gate process group notes on platform, not shell type. | |
| const processGroupNote = isWindows | |
| ? '' | |
| : '\n - Command is executed as a subprocess that leads its own process group. Command process group can be terminated as `kill -- -PGID` or signaled as `kill -s SIGNAL -- -PGID`.'; |
— DeepSeek/deepseek-v4-pro via Qwen Code /review
wenshao
left a comment
There was a problem hiding this comment.
Code Review Summary
Overall: This is a well-structured PR that cleanly replaces hardcoded cmd.exe descriptions with dynamic shell detection. The helper function decomposition (getExecutableBasename, getShellDisplayName, getShellExecutionWrapper, etc.) is clean and the test coverage is thorough (182/182 passing). Build and lint pass.
Deterministic analysis: tsc = 0, eslint = 0 findings.
Findings
| # | File | Finding | Confidence | Level |
|---|---|---|---|---|
| 1 | shell.ts |
Redundant path.basename() wrapper in getExecutableBasename |
High | Suggestion |
| 2 | shell.test.ts |
3 new test cases missing toMatchSnapshot() (existing tests have them) |
High | Suggestion |
Note: The processGroupNote using os.platform() (already commented inline) is intentionally correct — process groups are an OS capability, not a shell feature. Test assertions confirm this.
Reviewed 3 changed files (+248/-47). 9 review agents + verification + reverse audit.
Review by Qwen Code — model: mimo-v2.5-pro
|
|
||
| function getExecutableBasename(executable: string): string { | ||
| return path.basename(path.win32.basename(executable)); | ||
| } |
There was a problem hiding this comment.
[Suggestion] path.win32.basename() already handles both \ and / separators, so the outer path.basename() is redundant — path.win32.basename alone returns the correct basename on all platforms.
// Current:
return path.basename(path.win32.basename(executable));
// Simplified:
return path.win32.basename(executable);This is a minor clarity improvement; the double-call is functionally harmless but may confuse future readers into thinking it handles an edge case that doesn't exist.
Confidence: High
| }); | ||
|
|
||
| it('should describe PowerShell when ComSpec points to powershell.exe', async () => { | ||
| vi.mocked(os.platform).mockReturnValue('win32'); |
There was a problem hiding this comment.
[Suggestion] The two existing getDescription tests ('should return the windows description' and 'should return the non-windows description') both include toMatchSnapshot() assertions, but the three new tests (should describe PowerShell…, should describe pwsh…, should describe bash when Windows is running in Git Bash) only use toContain()/not.toContain() without snapshot assertions.
Adding expect(shellTool.description).toMatchSnapshot() to each would:
- Keep the test pattern consistent
- Catch unexpected wording regressions that
toContainmight miss - Create a reviewable snapshot diff showing the exact description for each shell
Confidence: High
|
Local tmux verification transcript, for the record. 1. PR-listed
|
| Check | Result |
|---|---|
mergeable |
MERGEABLE |
mergeStateStatus |
CLEAN |
reviewDecision |
APPROVED |
| Lint / CodeQL / Test mac · ubuntu · windows | all SUCCESS |
| Unresolved Critical comments | 0 |
| Unresolved Suggestion comments | 3 (non-blocking — processGroupNote is still platform-based instead of shell-aware, path.basename(path.win32.basename(...)) is a redundant double-call, and the 3 new tests don't carry snapshot assertions; worth a follow-up but not a merge blocker) |
Design notes
The new helpers split the previously hardcoded cmd.exe / bash -c description into:
getShellDisplayName—ShellType→ user-facing executable name (cmd.exe / powershell.exe / pwsh.exe / bash) with an exhaustiveneverswitchgetShellQuotingGuidance(shell)— three distinct rule sets (bash ANSI-C / heredoc, PowerShell here-string / single-quote doubling, cmd.exe^escaping)getShellCommandSequencingGuidance({executable, shell})— four branches, importantly distinguishing pwsh.exe (PS 7+, supports&&) from powershell.exe (Windows PowerShell 5.x, requires$LASTEXITCODEflow control)
That pwsh-vs-powershell distinction is the one detail a more naive refactor would have missed and the part the model would actually trip over.
Environment
- Linux x86_64, Node v20.19.2 (project engines require ≥22; only the project's own
doctorChecksNode-version assertion reflects this in CI's matrix) - PR head
05018f7ddagainstmainmerge-base1c529e4f0 - Worktree at
/tmp/pr4170withnode_modulesandcore/distsymlinked from a parent install
Summary
Validation
cd packages/core npx vitest run src/tools/shell.test.ts npm run typecheck npm run build git diff --checkcd packages/core npx vitest run src/tools/shell.test.tsScope / Risk
Testing Matrix
Testing matrix notes:
Linked Issues / Bugs
None.