diff --git a/internal/llm/agent/agent.go b/internal/llm/agent/agent.go
index 907a2348f838aa2f2ba6792db9b768eb656904a8..39c762991019f339348efab8cd9b769077e316f5 100644
--- a/internal/llm/agent/agent.go
+++ b/internal/llm/agent/agent.go
@@ -22,6 +22,7 @@ import (
"github.com/charmbracelet/crush/internal/permission"
"github.com/charmbracelet/crush/internal/pubsub"
"github.com/charmbracelet/crush/internal/session"
+ "github.com/charmbracelet/crush/internal/shell"
)
// Common errors
@@ -762,6 +763,8 @@ func (a *agent) Summarize(ctx context.Context, sessionID string) error {
a.Publish(pubsub.CreatedEvent, event)
return
}
+ shell := shell.GetPersistentShell(config.Get().WorkingDir())
+ summary += "\n\n**Current working directory of the persistent shell**\n\n" + shell.GetWorkingDir()
event = AgentEvent{
Type: AgentEventTypeSummarize,
Progress: "Creating new session...",
diff --git a/internal/llm/tools/bash.go b/internal/llm/tools/bash.go
index 10051a24bce881b9bdc4a8990364d30dec92bc85..99ab86068a5effa1e631037f3340ba814055d709 100644
--- a/internal/llm/tools/bash.go
+++ b/internal/llm/tools/bash.go
@@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
- "log/slog"
"strings"
"time"
@@ -23,8 +22,10 @@ type BashPermissionsParams struct {
}
type BashResponseMetadata struct {
- StartTime int64 `json:"start_time"`
- EndTime int64 `json:"end_time"`
+ StartTime int64 `json:"start_time"`
+ EndTime int64 `json:"end_time"`
+ Output string `json:"output"`
+ WorkingDirectory string `json:"working_directory"`
}
type bashTool struct {
permissions permission.Service
@@ -146,6 +147,7 @@ Before executing the command, please follow these steps:
5. Return Result:
- Provide the processed output of the command.
- If any errors occurred during execution, include those in the output.
+ - The result will also have metadata like the cwd (current working directory) at the end, included with tags.
Usage notes:
- The command argument is required.
@@ -389,9 +391,12 @@ func (b *bashTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error)
ctx, cancel = context.WithTimeout(ctx, time.Duration(params.Timeout)*time.Millisecond)
defer cancel()
}
- stdout, stderr, err := shell.
- GetPersistentShell(b.workingDir).
- Exec(ctx, params.Command)
+
+ persistentShell := shell.GetPersistentShell(b.workingDir)
+ stdout, stderr, err := persistentShell.Exec(ctx, params.Command)
+
+ // Get the current working directory after command execution
+ currentWorkingDir := persistentShell.GetWorkingDir()
interrupted := shell.IsInterrupt(err)
exitCode := shell.ExitCode(err)
if exitCode == 0 && !interrupted && err != nil {
@@ -401,15 +406,6 @@ func (b *bashTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error)
stdout = truncateOutput(stdout)
stderr = truncateOutput(stderr)
- slog.Info("Bash command executed",
- "command", params.Command,
- "stdout", stdout,
- "stderr", stderr,
- "exit_code", exitCode,
- "interrupted", interrupted,
- "err", err,
- )
-
errorMessage := stderr
if errorMessage == "" && err != nil {
errorMessage = err.Error()
@@ -438,9 +434,12 @@ func (b *bashTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error)
}
metadata := BashResponseMetadata{
- StartTime: startTime.UnixMilli(),
- EndTime: time.Now().UnixMilli(),
+ StartTime: startTime.UnixMilli(),
+ EndTime: time.Now().UnixMilli(),
+ Output: stdout,
+ WorkingDirectory: currentWorkingDir,
}
+ stdout += fmt.Sprintf("\n\n%s", currentWorkingDir)
if stdout == "" {
return WithResponseMetadata(NewTextResponse(BashNoOutput), metadata), nil
}
diff --git a/internal/tui/components/chat/messages/renderer.go b/internal/tui/components/chat/messages/renderer.go
index 4483b4ed2a9d2bf0b87a6eeae4565049edfa303e..ad9cd475704a84f2c400b611f592d25b5a78eddc 100644
--- a/internal/tui/components/chat/messages/renderer.go
+++ b/internal/tui/components/chat/messages/renderer.go
@@ -212,10 +212,19 @@ func (br bashRenderer) Render(v *toolCallCmp) string {
args := newParamBuilder().addMain(cmd).build()
return br.renderWithParams(v, "Bash", args, func() string {
- if v.result.Content == tools.BashNoOutput {
+ var meta tools.BashResponseMetadata
+ if err := br.unmarshalParams(v.result.Metadata, &meta); err != nil {
+ return renderPlainContent(v, v.result.Content)
+ }
+ // for backwards compatibility with older tool calls.
+ if meta.Output == "" && v.result.Content != tools.BashNoOutput {
+ meta.Output = v.result.Content
+ }
+
+ if meta.Output == "" {
return ""
}
- return renderPlainContent(v, v.result.Content)
+ return renderPlainContent(v, meta.Output)
})
}