package file import ( "context" "fmt" "os" "path/filepath" "github.com/enmanuel/agents/internal/config" "github.com/enmanuel/agents/tools" ) // maxWriteSize is the maximum content size for write_file (1 MB). const maxWriteSize = 1 * 1024 * 1024 // NewWriteFile creates a write_file tool that writes content to a local file. // Deny-by-default: if AllowedPaths is empty, all writes are rejected. // Rejects if ReadOnly is true. Creates parent directories if needed. func NewWriteFile(cfg config.FileOpsCfg) tools.Tool { return tools.Tool{ Def: tools.Def{ Name: "write_file", Description: "Write content to a local file. Creates the file if it does not exist. Creates parent directories if needed. Overwrites existing content.", Parameters: []tools.Param{ {Name: "path", Type: "string", Description: "Absolute path to the file to write", Required: true}, {Name: "content", Type: "string", Description: "Content to write to the file", Required: true}, }, }, Exec: func(ctx context.Context, args map[string]any) tools.Result { path := tools.GetString(args, "path") if path == "" { return tools.Result{Err: fmt.Errorf("write_file: path is required")} } content := tools.GetString(args, "content") if content == "" { return tools.Result{Err: fmt.Errorf("write_file: content is required")} } if len(content) > maxWriteSize { return tools.Result{Err: fmt.Errorf("write_file: content exceeds maximum size of 1 MB")} } absPath, err := filepath.Abs(path) if err != nil { return tools.Result{Err: fmt.Errorf("write_file: %w", err)} } if err := validateWritePath(absPath, cfg.AllowedPaths, cfg.ReadOnly); err != nil { return tools.Result{Err: err} } // Create parent directories if they don't exist. dir := filepath.Dir(absPath) if err := os.MkdirAll(dir, 0755); err != nil { return tools.Result{Err: fmt.Errorf("write_file: cannot create directories: %w", err)} } data := []byte(content) if err := os.WriteFile(absPath, data, 0644); err != nil { return tools.Result{Err: fmt.Errorf("write_file: %w", err)} } return tools.Result{Output: fmt.Sprintf("wrote %d bytes to %s", len(data), absPath)} }, } }