From 2e2b2187b957e9af78797a67ec5c6874615fae02 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sun, 8 Feb 2026 21:35:45 -1000 Subject: Initial project: task model, executor, API server, CLI, storage, reporter Claudomator automation toolkit for Claude Code with: - Task model with YAML parsing, validation, state machine (49 tests, 0 races) - SQLite storage for tasks and executions - Executor pool with bounded concurrency, timeout, cancellation - REST API + WebSocket for mobile PWA integration - Webhook/multi-notifier system - CLI: init, run, serve, list, status commands - Console, JSON, HTML reporters with cost tracking Co-Authored-By: Claude Opus 4.6 --- internal/reporter/reporter.go | 117 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 internal/reporter/reporter.go (limited to 'internal/reporter/reporter.go') diff --git a/internal/reporter/reporter.go b/internal/reporter/reporter.go new file mode 100644 index 0000000..4ba66e0 --- /dev/null +++ b/internal/reporter/reporter.go @@ -0,0 +1,117 @@ +package reporter + +import ( + "encoding/json" + "fmt" + "io" + "text/tabwriter" + "time" + + "github.com/claudomator/claudomator/internal/storage" +) + +// Reporter generates reports from execution data. +type Reporter interface { + Generate(w io.Writer, executions []*storage.Execution) error +} + +// ConsoleReporter outputs a formatted table. +type ConsoleReporter struct{} + +func (r *ConsoleReporter) Generate(w io.Writer, executions []*storage.Execution) error { + if len(executions) == 0 { + fmt.Fprintln(w, "No executions found.") + return nil + } + + tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0) + fmt.Fprintln(tw, "ID\tTASK\tSTATUS\tEXIT\tCOST\tDURATION\tSTARTED") + + var totalCost float64 + var completed, failed int + + for _, e := range executions { + dur := e.EndTime.Sub(e.StartTime) + totalCost += e.CostUSD + if e.Status == "COMPLETED" { + completed++ + } else { + failed++ + } + + fmt.Fprintf(tw, "%.8s\t%.8s\t%s\t%d\t$%.4f\t%v\t%s\n", + e.ID, e.TaskID, e.Status, e.ExitCode, e.CostUSD, + dur.Round(time.Second), e.StartTime.Format("2006-01-02 15:04")) + } + tw.Flush() + + fmt.Fprintf(w, "\nSummary: %d completed, %d failed, total cost $%.4f\n", completed, failed, totalCost) + return nil +} + +// JSONReporter outputs JSON. +type JSONReporter struct { + Pretty bool +} + +func (r *JSONReporter) Generate(w io.Writer, executions []*storage.Execution) error { + enc := json.NewEncoder(w) + if r.Pretty { + enc.SetIndent("", " ") + } + return enc.Encode(executions) +} + +// HTMLReporter generates a standalone HTML report. +type HTMLReporter struct{} + +func (r *HTMLReporter) Generate(w io.Writer, executions []*storage.Execution) error { + var totalCost float64 + var completed, failed int + for _, e := range executions { + totalCost += e.CostUSD + if e.Status == "COMPLETED" { + completed++ + } else { + failed++ + } + } + + fmt.Fprint(w, ` + +Claudomator Report +
+

Claudomator Report

+
`) + + fmt.Fprintf(w, `
Completed
%d
`, completed) + fmt.Fprintf(w, `
Failed
%d
`, failed) + fmt.Fprintf(w, `
Total Cost
$%.4f
`, totalCost) + fmt.Fprintf(w, `
Total
%d
`, len(executions)) + + fmt.Fprint(w, `
`) + + for _, e := range executions { + dur := e.EndTime.Sub(e.StartTime).Round(time.Second) + fmt.Fprintf(w, ``, + e.ID, e.TaskID, e.Status, e.Status, e.ExitCode, e.CostUSD, dur, e.StartTime.Format("2006-01-02 15:04")) + } + + fmt.Fprint(w, `
IDTaskStatusExitCostDurationStarted
%.8s%.8s%s%d$%.4f%v%s
`) + return nil +} -- cgit v1.2.3