1package tools
2
3import (
4 "context"
5 _ "embed"
6 "fmt"
7 "strings"
8
9 "charm.land/fantasy"
10 "github.com/charmbracelet/crush/internal/shell"
11)
12
13const (
14 BashOutputToolName = "bash_output"
15)
16
17//go:embed bash_output.md
18var bashOutputDescription []byte
19
20type BashOutputParams struct {
21 ShellID string `json:"shell_id" description:"The ID of the background shell to retrieve output from"`
22}
23
24type BashOutputResponseMetadata struct {
25 ShellID string `json:"shell_id"`
26 Done bool `json:"done"`
27 WorkingDirectory string `json:"working_directory"`
28}
29
30func NewBashOutputTool() fantasy.AgentTool {
31 return fantasy.NewAgentTool(
32 BashOutputToolName,
33 string(bashOutputDescription),
34 func(ctx context.Context, params BashOutputParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) {
35 if params.ShellID == "" {
36 return fantasy.NewTextErrorResponse("missing shell_id"), nil
37 }
38
39 bgManager := shell.GetBackgroundShellManager()
40 bgShell, ok := bgManager.Get(params.ShellID)
41 if !ok {
42 return fantasy.NewTextErrorResponse(fmt.Sprintf("background shell not found: %s", params.ShellID)), nil
43 }
44
45 stdout, stderr, done, err := bgShell.GetOutput()
46
47 var outputParts []string
48 if stdout != "" {
49 outputParts = append(outputParts, stdout)
50 }
51 if stderr != "" {
52 outputParts = append(outputParts, stderr)
53 }
54
55 status := "running"
56 if done {
57 status = "completed"
58 if err != nil {
59 exitCode := shell.ExitCode(err)
60 if exitCode != 0 {
61 outputParts = append(outputParts, fmt.Sprintf("Exit code %d", exitCode))
62 }
63 }
64 }
65
66 output := strings.Join(outputParts, "\n")
67
68 metadata := BashOutputResponseMetadata{
69 ShellID: params.ShellID,
70 Done: done,
71 WorkingDirectory: bgShell.GetWorkingDir(),
72 }
73
74 if output == "" {
75 output = BashNoOutput
76 }
77
78 result := fmt.Sprintf("Shell ID: %s\nStatus: %s\n\nOutput:\n%s", params.ShellID, status, output)
79 return fantasy.WithResponseMetadata(fantasy.NewTextResponse(result), metadata), nil
80 })
81}