feat(pwsh): render pwsh calls as bash-shaped terminal cards in the Web UI

A completed pwsh foreground call now presents as the bash tool's terminal
card with the parsed exit-status pill instead of a generic console fence,
and the collapsed row classifies as the shell family (Pwsh title). The
marker-to-exit-status parse moves from dsh-tool-bash's private render
module into the @deepseek-ai/dsh-bash seam so both shell tools share one
inverse of the marker contract (the bash-env precedent). The Web UI needs
no per-tool code: the terminal-card bridge maps any card:'terminal' view.

Coverage: dsh-bash owns the parse edge cases; the tool-pwsh presenter
suite mirrors tool-bash's; the client row-model suite pins the Pwsh shell
row; the new keyless pwsh-terminal web lane seeds an authored session,
presents it through the real tool on replay, and pins the card golden.

TUI is out of scope: the TUI package was removed (ed30088adb), so the Web
surface is the only UI the gap affected; the roadmap proposal's stage 2
is updated accordingly.
This commit is contained in:
Huanqi Cao
2026-08-05 02:05:06 +08:00
parent 81c63bbf4d
commit 61bd42f8f9
29 changed files with 433 additions and 65 deletions

View File

@@ -31,6 +31,9 @@ export const VARIANT_TITLES: Record<ToolRowVariant, string> = {
/** Known tool name -> variant. */
const TOOL_VARIANTS: Record<string, ToolRowVariant> = {
bash: 'bash',
// The PowerShell twin is a shell tool: the bash row family (icon, colors)
// with its own title from TOOL_TITLES, not the generic `others` row.
pwsh: 'bash',
read: 'read',
web_fetch: 'read',
web_search: 'search',
@@ -49,6 +52,7 @@ const TOOL_TITLES: Record<string, string> = {
cordis_inspect: 'Inspect',
cordis_mount: 'Mount temporary Plugin',
cordis_unmount: 'Unmount temporary Plugin',
pwsh: 'Pwsh',
}
/**

View File

@@ -59,6 +59,7 @@ const result = (over?: Partial<ToolResultNode>): ToolResultNode => ({
describe('tool-call-model', () => {
it('classifies known tools and falls back to others', () => {
expect(classifyTool('bash')).toBe('bash')
expect(classifyTool('pwsh')).toBe('bash')
expect(classifyTool('read')).toBe('read')
expect(classifyTool('web_fetch')).toBe('read')
expect(classifyTool('web_search')).toBe('search')
@@ -71,6 +72,12 @@ describe('tool-call-model', () => {
expect(classifyTool('todo_write')).toBe('others')
})
it('gives the pwsh shell row the bash family treatment with its own title', () => {
const m = toolRowModel('pwsh', running())
expect(m.variant).toBe('bash')
expect(m.title).toBe('Pwsh')
})
it('derives state across running/ok/error/interrupted', () => {
expect(toolRowModel('bash', running()).state).toBe('running')
expect(toolRowModel('bash', result()).state).toBe('ok')