package tools import ( "context" "os" "os/exec" "path/filepath" "strings" "time" ) // Format-on-write for the mutating file tools. When enabled, a successful // edit_file/write_file runs the language's standard formatter on the file it // just wrote, so the model's output always lands in project-canonical style // and never fails a CI format check it cannot see. Off by default (set // ZERO_FORMAT_ON_WRITE=1): auto-reformatting changes bytes the model did not // write, which strict workflows may not want. // // Ordering matters: formatting runs BEFORE the FileTracker re-baseline, and // the caller records the POST-format content. Formatting after the baseline // would make the very next edit look like an external modification and trip // the conflict guard. // formatOnWriteTimeout bounds one formatter run; a wedged formatter must never // hang a tool call. On timeout the unformatted write stands. const formatOnWriteTimeout = 10 * time.Second // formatterCommands maps a file extension to the formatter argv; the file path // is appended as the final argument. Only in-place, config-respecting, // community-standard formatters — a missing binary silently skips formatting. var formatterCommands = map[string][]string{ ".go": {"gofmt", "-w"}, ".rs": {"rustfmt"}, ".py": {"ruff", "format", "--quiet"}, ".ts": {"prettier", "--log-level", "silent", "--write"}, ".tsx": {"prettier", "--log-level", "silent", "--write"}, ".js": {"prettier", "--log-level", "silent", "--write"}, ".jsx": {"prettier", "--log-level", "silent", "--write"}, ".json": {"prettier", "--log-level", "silent", "--write"}, ".css": {"prettier", "--log-level", "silent", "--write"}, ".scss": {"prettier", "--log-level", "silent", "--write"}, ".html": {"prettier", "--log-level", "silent", "--write"}, ".md": {"prettier", "--log-level", "silent", "--write"}, ".yaml": {"prettier", "--log-level", "silent", "--write"}, ".yml": {"prettier", "--log-level", "silent", "--write"}, ".zig": {"zig", "fmt"}, ".dart": {"dart", "format"}, ".tf": {"terraform", "fmt"}, ".gleam": {"gleam", "format"}, ".sh": {"shfmt", "-w"}, ".bash": {"shfmt", "-w"}, ".c": {"clang-format", "-i"}, ".h": {"clang-format", "-i"}, ".cpp": {"clang-format", "-i"}, ".hpp": {"clang-format", "-i"}, ".cc": {"clang-format", "-i"}, ".kt": {"ktlint", "-F"}, ".swift": {"swiftformat"}, ".lua": {"stylua"}, } // formatOnWriteEnabled reports whether the opt-in env toggle is set. func formatOnWriteEnabled() bool { value := strings.TrimSpace(os.Getenv("ZERO_FORMAT_ON_WRITE")) return value != "" && value != "0" && !strings.EqualFold(value, "false") } // maybeFormatWrittenFile runs the configured formatter for absolutePath (when // enabled and on PATH) and returns the file's content afterwards. Best-effort // throughout: any failure — no formatter, formatter error, timeout, unreadable // result — returns writtenContent so the caller's state matches the last write // it performed itself. func maybeFormatWrittenFile(ctx context.Context, absolutePath string, writtenContent string) string { if !formatOnWriteEnabled() { return writtenContent } command, ok := formatterCommands[strings.ToLower(filepath.Ext(absolutePath))] if !ok { return writtenContent } if _, err := exec.LookPath(command[0]); err != nil { return writtenContent } formatCtx, cancel := context.WithTimeout(ctx, formatOnWriteTimeout) defer cancel() arguments := append(append([]string(nil), command[1:]...), absolutePath) formatter := exec.CommandContext(formatCtx, command[0], arguments...) formatter.Dir = filepath.Dir(absolutePath) if err := formatter.Run(); err != nil { return writtenContent } formatted, err := os.ReadFile(absolutePath) if err != nil { return writtenContent } return string(formatted) }