The write_file tool

Reading is easy. Writing is where coding agents start to feel real. The model can now emit a path and a content string, and your Go function creates or overwrites the file. We will keep the spec as narrow as possible on purpose.

agent/tools.go
go
{
	Spec: ToolSpec{
		Type: "function",
		Function: FunctionSpec{
			Name:        "write_file",
			Description: "Create or overwrite a file with the given text content.",
			Parameters: map[string]any{
				"type": "object",
				"properties": map[string]any{
					"path":    map[string]any{"type": "string", "description": "Relative path."},
					"content": map[string]any{"type": "string", "description": "Full file content."},
				},
				"required": []string{"path", "content"},
			},
		},
	},
	Run: runWriteFile,
},

Narrow surface area. Two fields, both required. No append mode, no binary, no partial patches. A smaller spec is a safer spec.

agent/tools.go
go
func runWriteFile(args string) (string, error) {
	var in struct {
		Path    string `json:"path"`
		Content string `json:"content"`
	}
	if err := json.Unmarshal([]byte(args), &in); err != nil {
		return "", fmt.Errorf("write_file: invalid args: %w", err)
	}
	if in.Path == "" {
		return "", fmt.Errorf("write_file: path is required")
	}
	clean := filepath.Clean(in.Path)
	if err := os.MkdirAll(filepath.Dir(clean), 0o755); err != nil {
		return "", fmt.Errorf("write_file mkdir: %w", err)
	}
	if err := os.WriteFile(clean, []byte(in.Content), 0o644); err != nil {
		return "", fmt.Errorf("write_file: %w", err)
	}
	return fmt.Sprintf("wrote %d bytes to %s", len(in.Content), clean), nil
}

os.MkdirAll creates missing parent directories so the model can create new folders. Permissions stay sane at 0755 for dirs and 0644 for files.

Quiz: Quiz

Loading practice…