v0.0.1: agentu mvp
Add OpenAI-compatible providers, TUI, slash commands, and model switching. Add local file tools plus optional yolo shell execution. Document config, usage, and development workflow.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"agentu/internal/llm"
|
||||
)
|
||||
|
||||
type ShellRunTool struct {
|
||||
workingDir string
|
||||
}
|
||||
|
||||
func NewShellRunTool(workingDir string) *ShellRunTool {
|
||||
return &ShellRunTool{workingDir: workingDir}
|
||||
}
|
||||
|
||||
func (t *ShellRunTool) Definition() llm.Tool {
|
||||
return llm.Tool{
|
||||
Type: "function",
|
||||
Function: llm.ToolFunction{
|
||||
Name: "shell_run",
|
||||
Description: "Run a non-interactive shell command on the local machine. Use for builds, tests, local status inspection, and local automation. Avoid commands that require an interactive TTY, credentials, or open long-running sessions.",
|
||||
Parameters: JSONSchema(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {"type": "string", "description": "Command to execute."},
|
||||
"workdir": {"type": "string", "description": "Optional working directory. Relative paths are resolved from the configured agent working directory."}
|
||||
},
|
||||
"required": ["command"],
|
||||
"additionalProperties": false
|
||||
}`),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type shellRunArgs struct {
|
||||
Command string `json:"command"`
|
||||
Workdir string `json:"workdir"`
|
||||
}
|
||||
|
||||
func (t *ShellRunTool) Execute(ctx context.Context, raw json.RawMessage) (string, error) {
|
||||
var args shellRunArgs
|
||||
if err := json.Unmarshal(raw, &args); err != nil {
|
||||
return "", fmt.Errorf("parse arguments: %w", err)
|
||||
}
|
||||
if strings.TrimSpace(args.Command) == "" {
|
||||
return "", errors.New("command is required")
|
||||
}
|
||||
|
||||
workdir, err := resolvePath(t.workingDir, args.Workdir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
shell := os.Getenv("SHELL")
|
||||
if shell == "" {
|
||||
shell = "/bin/sh"
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, shell, "-lc", args.Command)
|
||||
cmd.Dir = workdir
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
text := FormatResult(string(output))
|
||||
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||
return text, fmt.Errorf("command timed out: %w", ctx.Err())
|
||||
}
|
||||
if err != nil {
|
||||
if text != "" {
|
||||
return text, fmt.Errorf("command failed: %w", err)
|
||||
}
|
||||
return "", fmt.Errorf("command failed: %w", err)
|
||||
}
|
||||
return text, nil
|
||||
}
|
||||
Reference in New Issue
Block a user