package main import ( "encoding/json" "fmt" "os" "path/filepath" "sort" "strings" ) type Tool struct { Type string `json:"type"` Function ToolFunction `json:"function"` } type ToolFunction struct { Name string `json:"name"` Description string `json:"description"` Parameters map[string]any `json:"parameters"` } func toolDefinitions() []Tool { return []Tool{ { Type: "function", Function: ToolFunction{ Name: "read_file", Description: "Read a UTF-8 text file relative to the project root. Returns the file contents.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "path": map[string]any{ "type": "string", "description": "Relative path from the project root.", }, }, "required": []string{"path"}, }, }, }, { Type: "function", Function: ToolFunction{ Name: "create_file", Description: "Create a new file with the given content. Fails if the file already exists. Parent directories are created as needed.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "path": map[string]any{"type": "string", "description": "Relative path from the project root."}, "content": map[string]any{"type": "string", "description": "Full file contents to write."}, }, "required": []string{"path", "content"}, }, }, }, { Type: "function", Function: ToolFunction{ Name: "edit_file", Description: "Overwrite an existing file with new content. Fails if the file does not exist.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "path": map[string]any{"type": "string", "description": "Relative path from the project root."}, "content": map[string]any{"type": "string", "description": "Full replacement contents."}, }, "required": []string{"path", "content"}, }, }, }, { Type: "function", Function: ToolFunction{ Name: "delete_file", Description: "Delete a file. Fails if the path is a directory or does not exist.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "path": map[string]any{"type": "string", "description": "Relative path from the project root."}, }, "required": []string{"path"}, }, }, }, { Type: "function", Function: ToolFunction{ Name: "list_directory", Description: "List the entries of a directory, relative to the project root. Use \".\" for the root.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "path": map[string]any{"type": "string", "description": "Relative directory path. Use \".\" for the project root."}, }, "required": []string{"path"}, }, }, }, } } // runTool executes a tool call and returns a string payload for the model. // Errors are returned as strings too so the model can react, not fatal. func runTool(root, name, argsJSON string) string { var a struct { Path string `json:"path"` Content string `json:"content"` } if argsJSON != "" { if err := json.Unmarshal([]byte(argsJSON), &a); err != nil { return fmt.Sprintf("error: invalid arguments: %v", err) } } switch name { case "read_file": return doRead(root, a.Path) case "create_file": return doCreate(root, a.Path, a.Content) case "edit_file": return doEdit(root, a.Path, a.Content) case "delete_file": return doDelete(root, a.Path) case "list_directory": return doList(root, a.Path) default: return fmt.Sprintf("error: unknown tool %q", name) } } func doRead(root, p string) string { abs, err := resolveSafe(root, p) if err != nil { return "error: " + err.Error() } b, err := os.ReadFile(abs) if err != nil { return "error: " + err.Error() } return string(b) } func doCreate(root, p, content string) string { abs, err := resolveSafe(root, p) if err != nil { return "error: " + err.Error() } if _, err := os.Stat(abs); err == nil { return fmt.Sprintf("error: file already exists: %s", p) } if err := os.MkdirAll(filepath.Dir(abs), 0o755); err != nil { return "error: " + err.Error() } if err := os.WriteFile(abs, []byte(content), 0o644); err != nil { return "error: " + err.Error() } return fmt.Sprintf("created %s (%d bytes)", p, len(content)) } func doEdit(root, p, content string) string { abs, err := resolveSafe(root, p) if err != nil { return "error: " + err.Error() } info, err := os.Stat(abs) if err != nil { return "error: " + err.Error() } if info.IsDir() { return fmt.Sprintf("error: path is a directory: %s", p) } if err := os.WriteFile(abs, []byte(content), 0o644); err != nil { return "error: " + err.Error() } return fmt.Sprintf("updated %s (%d bytes)", p, len(content)) } func doDelete(root, p string) string { abs, err := resolveSafe(root, p) if err != nil { return "error: " + err.Error() } if abs == root { return "error: refusing to delete project root" } info, err := os.Stat(abs) if err != nil { return "error: " + err.Error() } if info.IsDir() { return fmt.Sprintf("error: path is a directory: %s", p) } if err := os.Remove(abs); err != nil { return "error: " + err.Error() } return fmt.Sprintf("deleted %s", p) } func doList(root, p string) string { if p == "" { p = "." } abs, err := resolveSafe(root, p) if err != nil { return "error: " + err.Error() } entries, err := os.ReadDir(abs) if err != nil { return "error: " + err.Error() } lines := make([]string, 0, len(entries)) for _, e := range entries { name := e.Name() if e.IsDir() { name += "/" } lines = append(lines, name) } sort.Strings(lines) if len(lines) == 0 { return "(empty)" } return strings.Join(lines, "\n") }