renderReport prints the terminal analytics report.
(w io.Writer, s stats.Stats)
| 109 | |
| 110 | // renderReport prints the terminal analytics report. |
| 111 | func renderReport(w io.Writer, s stats.Stats) error { |
| 112 | fmt.Fprintf(w, "flow stats — %s", s.Window) |
| 113 | if s.Project != "" { |
| 114 | fmt.Fprintf(w, " · project %s", s.Project) |
| 115 | } |
| 116 | fmt.Fprintln(w) |
| 117 | fmt.Fprintln(w) |
| 118 | |
| 119 | fmt.Fprintln(w, " Your AI remembered, so you didn't.") |
| 120 | fmt.Fprintf(w, " flow recalled your context %d times — you never re-explained it.\n", s.LookupsTotal) |
| 121 | fmt.Fprintf(w, " resume %d · reference %d · cross-task %d · kb %d\n", |
| 122 | s.LookupsByKind[stats.LookupResume], s.LookupsByKind[stats.LookupReference], |
| 123 | s.LookupsByKind[stats.LookupCrossTask], s.LookupsByKind[stats.LookupKB]) |
| 124 | fmt.Fprintln(w) |
| 125 | |
| 126 | fmt.Fprintln(w, " Memory") |
| 127 | fmt.Fprintf(w, " Context re-established : ~%s tokens you never re-typed (est.)\n", humanInt(s.Savings.ContextTokens)) |
| 128 | fmt.Fprintf(w, " Instant resumes : %d× — flow dropped you straight back into work, in context not from scratch\n", s.LookupsByKind[stats.LookupResume]) |
| 129 | fmt.Fprintln(w) |
| 130 | |
| 131 | fmt.Fprintln(w, " Shipped") |
| 132 | fmt.Fprintf(w, " Tasks done : %d\n", s.TasksDone) |
| 133 | fmt.Fprintf(w, " Tokens processed : %s\n", humanInt(s.Tokens.Total())) |
| 134 | fmt.Fprintf(w, " KB facts : %d\n", s.KBFacts) |
| 135 | |
| 136 | if s.AutoRuns+s.OwnerTicks+s.PlaybookRuns > 0 { |
| 137 | fmt.Fprintln(w) |
| 138 | fmt.Fprintln(w, " Automation (power-user)") |
| 139 | fmt.Fprintf(w, " Auto runs : %d\n", s.AutoRuns) |
| 140 | fmt.Fprintf(w, " Owner ticks : %d\n", s.OwnerTicks) |
| 141 | fmt.Fprintf(w, " Playbook runs : %d\n", s.PlaybookRuns) |
| 142 | fmt.Fprintf(w, " Unattended work : ~%.1f hrs · ≈$%s (est.)\n", s.Savings.AutomationHours, humanInt(int64(s.Savings.AutomationHours*s.DollarPerHour))) |
| 143 | } |
| 144 | fmt.Fprintln(w) |
| 145 | |
| 146 | fmt.Fprintf(w, " Addressed by name, not a UUID : %d\n", s.Savings.AddressableCount) |
| 147 | |
| 148 | if len(s.Weekly) > 0 { |
| 149 | vals := make([]int, len(s.Weekly)) |
| 150 | for i, wp := range s.Weekly { |
| 151 | vals[i] = wp.Lookups |
| 152 | } |
| 153 | fmt.Fprintf(w, " Weekly recalls : %s\n", sparkline(vals)) |
| 154 | } |
| 155 | fmt.Fprintln(w) |
| 156 | fmt.Fprintf(w, " counts exact · time/tokens are est. · tune ~/.flow/stats.json\n") |
| 157 | return nil |
| 158 | } |
| 159 | |
| 160 | // humanInt formats n with thousands separators (e.g. 1041234 → "1,041,234"). |
| 161 | // Pure Go, no third-party deps. |