diff --git a/claudetool/bash.go b/claudetool/bash.go index 8c9e673be01e467581beaea2d310671ba946762e..1b5ebc0b148fc78f9e6f22fcadd7cecadbbba42e 100644 --- a/claudetool/bash.go +++ b/claudetool/bash.go @@ -141,6 +141,11 @@ type bashInput struct { Background bool `json:"background,omitempty"` } +// BashDisplayData is the display data sent to the UI for bash tool results. +type BashDisplayData struct { + WorkingDir string `json:"workingDir"` +} + type BackgroundResult struct { PID int OutFile string @@ -203,13 +208,15 @@ func (b *BashTool) Run(ctx context.Context, m json.RawMessage) llm.ToolOut { timeout := req.timeout(b.Timeouts) + display := BashDisplayData{WorkingDir: wd} + // If Background is set to true, use executeBackgroundBash if req.Background { result, err := b.executeBackgroundBash(ctx, req, timeout) if err != nil { return llm.ErrorToolOut(err) } - return llm.ToolOut{LLMContent: llm.TextContent(result.XMLish())} + return llm.ToolOut{LLMContent: llm.TextContent(result.XMLish()), Display: display} } // For foreground commands, use executeBash @@ -217,7 +224,7 @@ func (b *BashTool) Run(ctx context.Context, m json.RawMessage) llm.ToolOut { if execErr != nil { return llm.ErrorToolOut(execErr) } - return llm.ToolOut{LLMContent: llm.TextContent(out)} + return llm.ToolOut{LLMContent: llm.TextContent(out), Display: display} } const maxBashOutputLength = 131072 diff --git a/claudetool/bash_test.go b/claudetool/bash_test.go index db91ee39abb3f082ffc42f38c50d85c8c338ec0d..d1c0dea6bdfe7d415ad85e304324653d562fd3ae 100644 --- a/claudetool/bash_test.go +++ b/claudetool/bash_test.go @@ -87,6 +87,15 @@ func TestBashTool(t *testing.T) { if len(result) == 0 || result[0].Text != expected { t.Errorf("Expected %q, got %q", expected, result[0].Text) } + + // Verify Display data contains working directory + display, ok := toolOut.Display.(BashDisplayData) + if !ok { + t.Fatalf("Expected Display to be BashDisplayData, got %T", toolOut.Display) + } + if display.WorkingDir != "/" { + t.Errorf("Expected WorkingDir to be '/', got %q", display.WorkingDir) + } }) // Test with arguments diff --git a/ui/src/components/BashTool.tsx b/ui/src/components/BashTool.tsx index 8d1b9e22acde6a09d5e065a73ea952887e2eff43..f94ebfa1bb056593203555f31470c7632a9f9df6 100644 --- a/ui/src/components/BashTool.tsx +++ b/ui/src/components/BashTool.tsx @@ -1,6 +1,11 @@ import React, { useState } from "react"; import { LLMContent } from "../types"; +// Display data from the bash tool backend +interface BashDisplayData { + workingDir: string; +} + interface BashToolProps { // For tool_use (pending state) toolInput?: unknown; @@ -10,11 +15,28 @@ interface BashToolProps { toolResult?: LLMContent[]; hasError?: boolean; executionTime?: string; + display?: unknown; } -function BashTool({ toolInput, isRunning, toolResult, hasError, executionTime }: BashToolProps) { +function BashTool({ + toolInput, + isRunning, + toolResult, + hasError, + executionTime, + display, +}: BashToolProps) { const [isExpanded, setIsExpanded] = useState(false); + // Extract working directory from display data + const displayData: BashDisplayData | null = + display && + typeof display === "object" && + "workingDir" in display && + typeof display.workingDir === "string" + ? (display as BashDisplayData) + : null; + // Extract command from toolInput const command = typeof toolInput === "object" && @@ -51,6 +73,11 @@ function BashTool({ toolInput, isRunning, toolResult, hasError, executionTime }:
🛠️ {displayCommand} + {displayData?.workingDir && ( + + in {displayData.workingDir} + + )} {isComplete && isCancelled && ✗ cancelled} {isComplete && hasError && !isCancelled && } {isComplete && !hasError && } @@ -84,6 +111,12 @@ function BashTool({ toolInput, isRunning, toolResult, hasError, executionTime }: {isExpanded && (
+ {displayData?.workingDir && ( +
+
Working Directory:
+
{displayData.workingDir}
+
+ )}
Command:
{command}
diff --git a/ui/src/styles.css b/ui/src/styles.css index de4cd7ece8c22620dd1d548fabf5bd307f94b27c..e3fd300f8eee5f84450bdeb6f8d7abe5786a53f7 100644 --- a/ui/src/styles.css +++ b/ui/src/styles.css @@ -1004,6 +1004,19 @@ button { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + flex-shrink: 1; + min-width: 0; +} + +.bash-tool-cwd { + font-family: var(--font-mono); + font-size: 0.75rem; + color: var(--text-tertiary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-shrink: 0; + max-width: 30%; } .bash-tool-running {