agentwaste-core 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/tools.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { toReportJson } from "./report.js";
2
2
 
3
- export const TOOL_IDS = ["suckytraces", "keybarf", "tokengoblin", "tooltantrum"];
3
+ export const TOOL_IDS = ["suckytraces", "tooltantrum"];
4
4
 
5
5
  const TOOL_CONFIGS = {
6
6
  suckytraces: {
@@ -8,133 +8,39 @@ const TOOL_CONFIGS = {
8
8
  packageName: "suckytraces",
9
9
  binName: "suckytraces",
10
10
  metricKey: "trace_coverage",
11
- emoji: "🕵️",
12
- npc: "Trace Goblin",
13
- tagline: "sniffs out agent runs that cannot be replayed, explained, or defended in standup.",
14
- good: "traceable little angel",
15
- bad: "all vibes, no receipts",
16
11
  commandHint: "npx suckytraces",
12
+ tagline: "Trace coverage for local coding-agent sessions.",
13
+ good: "trace trail intact",
14
+ bad: "trace blackout",
17
15
  score(metric) {
18
- if (metric.session_log_files === 0) return 20;
16
+ if (metric.session_log_files === 0) return 0;
19
17
  return clamp(100 - metric.session_file_coverage_percent);
20
18
  },
21
- findings(metric) {
22
- return [
23
- finding("coverage", `${metric.session_file_coverage_percent}% replayable`, metric.level),
24
- finding("traced files", `${formatNumber(metric.traced_session_log_files)} / ${formatNumber(metric.session_log_files)} session logs`),
25
- finding("trace crumbs", `${formatNumber(metric.structured_trace_signals)} structured signals`),
26
- ];
27
- },
28
- roast(metric) {
29
- if (metric.session_log_files === 0) return "No session logs found. The goblin is unemployed but suspicious.";
30
- if (metric.session_file_coverage_percent >= 80) return "Your traces have shoes, a map, and a tiny flashlight. Respect.";
31
- if (metric.session_file_coverage_percent >= 30) return "Some traces exist, but the murder board still has yarn gaps.";
32
- return "Debugging these runs is basically reading tea leaves in a server room.";
19
+ headline(metric) {
20
+ if (metric.session_log_files === 0) return "NO FOOTAGE FOUND";
21
+ if (metric.session_file_coverage_percent < 30) return "THE PAPER TRAIL IS ON FIRE";
22
+ if (metric.session_file_coverage_percent < 80) return "THE ALIBI HAS HOLES";
23
+ return "THE TRACE TRAIL IS CLEAN";
33
24
  },
34
- next(metric) {
35
- if (metric.session_log_files === 0) return "run an agent once, then re-run this goblin";
36
- if (metric.session_file_coverage_percent >= 80) return "keep run_id / trace_id attached to every meaningful tool call";
37
- return "add run_id, trace_id, span_id, retry metadata, and final artifacts to agent/tool logs";
38
- },
39
- evidence(report) {
40
- return report.evidence.trace_files_sample;
41
- },
42
- explain: [
43
- "coverage = session log files with structured trace evidence / scanned session log files * 100",
44
- "trace evidence includes trace_id, span_id, run_id, OpenTelemetry, or otel",
45
- "goal: when an agent breaks prod, you can replay the plot instead of interviewing a ghost",
46
- ],
47
- },
48
- keybarf: {
49
- id: "keybarf",
50
- packageName: "keybarf",
51
- binName: "keybarf",
52
- metricKey: "credential_exposure",
53
- emoji: "🤮",
54
- npc: "Secret Raccoon",
55
- tagline: "rummages through local agent logs for API keys that should not be cosplay props.",
56
- good: "vault goblin satisfied",
57
- bad: "credential confetti cannon",
58
- commandHint: "npx keybarf",
59
- score(metric) {
60
- return clamp(metric.score ?? 0);
25
+ summary(metric) {
26
+ const missing = Math.max(0, metric.session_log_files - metric.traced_session_log_files);
27
+ if (metric.session_log_files === 0) return "No session logs were found, so there is nothing to replay yet.";
28
+ if (missing === 0) return "Every scanned log has trace evidence. The replay trail is intact.";
29
+ return `Only ${formatPercent(metric.session_file_coverage_percent)} of session logs include trace/run/span IDs. ${formatCount(missing, "log")} would be hard to replay after an incident.`;
61
30
  },
62
31
  findings(metric) {
63
- const kinds = Object.entries(metric.by_kind ?? {})
64
- .filter(([, count]) => count > 0)
65
- .sort((a, b) => b[1] - a[1])
66
- .slice(0, 4)
67
- .map(([kind, count]) => `${kind}:${count}`)
68
- .join(", ");
32
+ const missing = Math.max(0, metric.session_log_files - metric.traced_session_log_files);
69
33
  return [
70
- finding("secret fp", `${formatNumber(metric.unique_secret_fingerprints)} unique redacted fingerprints`, metric.level),
71
- finding("model-facing", `${formatNumber(metric.model_facing_secret_fingerprints)} fingerprints reached prompt/tool context`, metric.model_facing_secret_fingerprints > 0 ? "CRITICAL" : "LOW"),
72
- finding("kinds", kinds || "none spotted"),
73
- finding("risk score", `${formatNumber(metric.score)} / 100`, metric.level),
34
+ finding("coverage", `${formatPercent(metric.session_file_coverage_percent)} traced`, metric.level),
35
+ finding("with ids", `${formatNumber(metric.traced_session_log_files)} / ${formatNumber(metric.session_log_files)} logs`, metric.level),
36
+ finding("missing", `${formatNumber(missing)} logs`, missing > 0 ? metric.level : "LOW"),
37
+ finding("signals", `${formatNumber(metric.structured_trace_signals)} markers`),
74
38
  ];
75
39
  },
76
- roast(metric) {
77
- if (metric.model_facing_secret_fingerprints > 0) return "A model saw secret-shaped material. The raccoon is screaming into a paper bag.";
78
- if (metric.unique_secret_fingerprints > 0) return "Secrets are sitting in logs/config like snacks on a sidewalk.";
79
- return "No obvious key barf. The raccoon found crumbs, not crimes.";
80
- },
81
- next(metric) {
82
- if (metric.model_facing_secret_fingerprints > 0) return "rotate exposed keys, purge logs, and vault credentials before the next agent run";
83
- if (metric.unique_secret_fingerprints > 0) return "move keys into a vault/env boundary and stop printing them into logs";
84
- return "keep secrets out of prompts, tool args, pasted stack traces, and debug logs";
85
- },
86
- evidence(report) {
87
- return report.evidence.secret_files_sample;
88
- },
89
40
  explain: [
90
- "raw keys = unique redacted fingerprints matched by known API-key patterns",
91
- "model-facing keys = those fingerprints inside user/assistant/tool-call records",
92
- "level = critical for model-facing material, high for raw secret material in logs/config",
93
- ],
94
- },
95
- tokengoblin: {
96
- id: "tokengoblin",
97
- packageName: "tokengoblin",
98
- binName: "tokengoblin",
99
- metricKey: "context_efficiency",
100
- emoji: "🧌",
101
- npc: "Token Goblin",
102
- tagline: "counts prompt calories, cache snacks, and context bloat burps.",
103
- good: "lean prompt machine",
104
- bad: "context buffet disaster",
105
- commandHint: "npx tokengoblin",
106
- score(metric) {
107
- const levelBase = scoreFromLevel(metric.level);
108
- const excessBoost = Math.min(35, metric.excess_fresh_input_percent || 0);
109
- const cachePenalty = metric.cache_hit_percent < 20 && metric.fresh_input_tokens > 0 ? 10 : 0;
110
- return clamp(levelBase + excessBoost + cachePenalty);
111
- },
112
- findings(metric, report) {
113
- return [
114
- finding("fresh/tool", `${formatNumber(metric.fresh_input_tokens_per_tool_call)} tokens`, metric.level),
115
- finding("cache hit", `${metric.cache_hit_percent}%`, metric.cache_hit_percent >= 50 ? "LOW" : "MEDIUM"),
116
- finding("excess", `${formatNumber(metric.excess_fresh_input_tokens)} fresh tokens over review line`),
117
- finding("30d burn", `${formatNumber(report.monthly_impact_estimate.active_tokens_30d)} active tokens`),
118
- ];
119
- },
120
- roast(metric) {
121
- if (metric.level === "NO_DATA") return "No token footprints yet. Either pristine, empty, or wearing tiny socks.";
122
- if (metric.fresh_input_tokens_per_tool_call >= metric.review_threshold_fresh_input_tokens_per_tool_call) return "Every tool call is dragging a suitcase full of prompt soup.";
123
- if (metric.cache_hit_percent < 20 && metric.fresh_input_tokens > 0) return "The cache is basically a decorative bowl.";
124
- return "Token goblin nibbled, but did not find a buffet catastrophe.";
125
- },
126
- next(metric) {
127
- if (metric.level === "NO_DATA") return "run agents with token usage enabled, then ask the goblin again";
128
- if (metric.fresh_input_tokens_per_tool_call >= metric.review_threshold_fresh_input_tokens_per_tool_call) return "trim repeated context, summarize durable state, and cache stable prompt chunks";
129
- return "keep fresh input per tool low and move repeat loops into scripts/jobs";
130
- },
131
- evidence() {
132
- return [];
133
- },
134
- explain: [
135
- "fresh input = model input minus cached reads where providers expose caching",
136
- "fresh/tool = sum(fresh input tokens) / tool calls",
137
- "excess = fresh input above a conservative 4,000-token-per-tool review line",
41
+ "coverage = session log files with structured trace evidence / scanned session log files * 100",
42
+ "trace evidence includes trace_id, span_id, run_id, OpenTelemetry, or otel",
43
+ "goal: make local agent runs replayable without reading the whole transcript",
138
44
  ],
139
45
  },
140
46
  tooltantrum: {
@@ -142,37 +48,39 @@ const TOOL_CONFIGS = {
142
48
  packageName: "tooltantrum",
143
49
  binName: "tooltantrum",
144
50
  metricKey: "tool_reliability",
145
- emoji: "🛠️",
146
- npc: "Retry Possum",
147
- tagline: "tool reliability: failure rate, retry recovery, and unresolved calls.",
148
- good: "healthy tool reliability",
149
- bad: "unresolved tool failures",
150
51
  commandHint: "npx tooltantrum",
52
+ tagline: "Tool-call reliability for local coding-agent sessions.",
53
+ good: "boringly stable",
54
+ bad: "tool meltdown",
151
55
  score(metric) {
56
+ if (metric.tool_calls === 0) return 0;
152
57
  return clamp(scoreFromLevel(metric.level) + Math.min(40, metric.failure_rate_percent));
153
58
  },
59
+ headline(metric) {
60
+ if (metric.tool_calls === 0) return "NO TOOLS ENTERED THE ARENA";
61
+ if (metric.unresolved_failures > 0) return "TOOLS ARE THROWING HANDS";
62
+ if (metric.failure_rate_percent >= 15) return "THE TOOLS ARE BLEEDING";
63
+ if (metric.failed_tool_calls > 0) return "A FEW TOOLS TRIPPED";
64
+ return "THE TOOLS BEHAVED";
65
+ },
66
+ summary(metric) {
67
+ if (metric.tool_calls === 0) return "No tool-call records were found, so there is nothing to score yet.";
68
+ if (metric.unresolved_failures > 0) {
69
+ return `${formatPercent(metric.failure_rate_percent)} of tool calls failed. Only ${formatPercent(metric.retry_recovery_percent)} of failures showed retry recovery, leaving ${formatCount(metric.unresolved_failures, "failure")} unresolved.`;
70
+ }
71
+ if (metric.failed_tool_calls > 0) {
72
+ return `${formatCount(metric.failed_tool_calls, "call")} failed, but the scanned retry signals recovered them.`;
73
+ }
74
+ return "No failed tool calls were detected in the scanned window.";
75
+ },
154
76
  findings(metric) {
155
77
  return [
156
- finding("failure rate", `${metric.failure_rate_percent}%`, metric.level),
157
- finding("failed", `${formatNumber(metric.failed_tool_calls)} / ${formatNumber(metric.tool_calls)} tool calls`),
158
- finding("unresolved", `${formatNumber(metric.unresolved_failures)} failures never recovered`, metric.unresolved_failures > 0 ? metric.level : "LOW"),
159
- finding("retry rescue", `${metric.retry_recovery_percent}% recovered after retry`),
78
+ finding("failure", formatPercent(metric.failure_rate_percent), metric.level),
79
+ finding("failed", `${formatNumber(metric.failed_tool_calls)} / ${formatNumber(metric.tool_calls)} calls`, metric.level),
80
+ finding("unresolved", `${formatNumber(metric.unresolved_failures)} unresolved`, metric.unresolved_failures > 0 ? metric.level : "LOW"),
81
+ finding("rescued", `${formatPercent(metric.retry_recovery_percent)} after retry`),
160
82
  ];
161
83
  },
162
- roast(metric) {
163
- if (metric.tool_calls === 0) return "No tool calls found, so there is nothing to score yet.";
164
- if (metric.failure_rate_percent >= 30) return "Very high tool failure rate. Agents are losing too many actions before finishing.";
165
- if (metric.unresolved_failures > 0) return "Most failed tool calls did not show a later recovery signal.";
166
- return "Tool calls look stable; retry behavior is boring in the good way.";
167
- },
168
- next(metric) {
169
- if (metric.tool_calls === 0) return "run an agent with tool usage, then scan again";
170
- if (metric.unresolved_failures > 0) return "classify retryable errors, make actions idempotent, cap retries, and save failure artifacts";
171
- return "keep retries boring: classify failures, cap attempts, and log recovery";
172
- },
173
- evidence(report) {
174
- return report.evidence.diagnostics;
175
- },
176
84
  explain: [
177
85
  "failure rate = failed_tool_calls / tool_calls * 100",
178
86
  "retry recovery = retry-after-failure signals / failed_tool_calls * 100",
@@ -196,29 +104,35 @@ export function buildToolReport(stats, options) {
196
104
  const color = colorizer(options.color);
197
105
  const full = toReportJson(stats, options);
198
106
  const metric = full.metrics[config.metricKey];
199
- const chaosScore = config.score(metric, full);
200
- const verdict = verdictFor(config, metric, chaosScore);
107
+ const score = config.score(metric, full);
108
+ const verdict = verdictFor(config, metric, score);
109
+ const headline = config.headline(metric, full);
110
+ const summary = config.summary(metric, full);
201
111
  const findings = config.findings(metric, full);
202
- const evidence = config.evidence(full).slice(0, 5);
112
+ const sources = sourceEntries(config.id, full);
113
+ const share = renderShareCard(config, full, metric, score, headline, sources);
114
+
203
115
  const json = {
204
116
  version: options.version,
205
117
  package: config.packageName,
206
118
  command: config.commandHint,
207
- npc: config.npc,
208
- emoji: config.emoji,
209
119
  tagline: config.tagline,
210
120
  scanned: full.scanned,
211
121
  metric_key: config.metricKey,
212
122
  metric,
213
- chaos_score: chaosScore,
123
+ score,
214
124
  verdict,
125
+ headline,
126
+ summary,
215
127
  findings: findings.map(({ label, value }) => ({ label, value })),
216
- roast: config.roast(metric, full),
217
- evidence_sample: evidence,
128
+ share,
129
+ source_breakdown: sources.map((entry) => entry.json),
218
130
  };
131
+
219
132
  return {
220
133
  json,
221
- text: renderToolText(config, full, metric, findings, evidence, verdict, chaosScore, color),
134
+ share,
135
+ text: renderToolText(config, full, metric, verdict, score, headline, summary, color),
222
136
  };
223
137
  }
224
138
 
@@ -228,446 +142,172 @@ export function explainTool(toolId, options = {}) {
228
142
  version: options.version ?? "0.1.0",
229
143
  package: config.packageName,
230
144
  command: config.commandHint,
231
- npc: config.npc,
232
145
  metric_key: config.metricKey,
233
146
  formulas: config.explain,
234
147
  };
235
148
  if (options.json) return JSON.stringify(payload, null, 2);
236
149
  return [
237
150
  `${config.packageName} explain v${payload.version}`,
238
- `${config.emoji} ${config.npc}: ${config.tagline}`,
151
+ config.tagline,
239
152
  "",
240
153
  ...config.explain.map((line) => `- ${line}`),
241
154
  ].join("\n");
242
155
  }
243
156
 
244
- function renderToolText(config, full, metric, findings, evidence, verdict, chaosScore, color) {
157
+ function renderToolText(config, full, metric, verdict, score, headline, summary, color) {
158
+ const sources = sourceEntries(config.id, full).filter((entry) => entry.showInText);
159
+ const sourceLabelWidth = Math.max(11, ...sources.map((entry) => entry.label.toLowerCase().length));
245
160
  const rows = [
246
- reportText(`${config.packageName} postmortem`, color.title(`${config.packageName} postmortem`)),
247
- reportText("──────────────────────────────", color.rule("──────────────────────────────")),
248
- reportText(""),
249
- reportSection("finding", color),
250
- ...paragraphRows(toolFinding(config.id, metric), color, { level: metric.level ?? verdict.level }),
251
- reportText(""),
252
- reportSection("impact", color),
253
- ...paragraphRows(toolImpact(config.id, full, metric), color),
254
- reportText(""),
255
- reportSection("agent leaderboard", color),
256
- ...toolAgentLeaderboard(config.id, full, color),
257
- reportText(""),
258
- reportSection("numbers", color),
259
- ...toolNumbers(config.id, full, metric, color),
161
+ ...heroRows(config, full, metric, score, headline, verdict, color),
162
+ blankRow(),
163
+ sectionRow("🚨 WORST OFFENDERS", color),
164
+ ...(sources.length > 0 ? sources.map((entry, index) => sourceRow(entry, color, index === 0, sourceLabelWidth)) : [detailRow("none", "no active sources found", color)]),
165
+ blankRow(),
166
+ sectionRow("STATS", color),
167
+ ...statRows(config.id, metric, verdict, score, color),
168
+ blankRow(),
169
+ sectionRow("SUMMARY", color),
170
+ ...paragraphRows(summary, color, { level: verdict.level }),
260
171
  ];
261
172
 
262
- return rows.map((row) => row.painted).join("\n");
263
- }
264
-
265
- function toolFinding(toolId, metric) {
266
- switch (toolId) {
267
- case "suckytraces":
268
- if (metric.session_log_files === 0) return "No session log files were found.";
269
- return `Only ${formatPercent(metric.session_file_coverage_percent)} of scanned session logs include trace, run, or span IDs.`;
270
- case "keybarf":
271
- if (metric.model_facing_secret_fingerprints > 0) return "Secret fingerprints appeared in model-facing records.";
272
- if (metric.unique_secret_fingerprints > 0) return "Secret fingerprints were found in local logs or config.";
273
- return "No known secret fingerprints were found.";
274
- case "tokengoblin":
275
- if (metric.level === "NO_DATA") return "No token usage records were found.";
276
- return `Fresh input averages ${formatNumber(metric.fresh_input_tokens_per_tool_call)} tokens per tool call.`;
277
- case "tooltantrum":
278
- if (metric.tool_calls === 0) return "No tool-call records were found.";
279
- return `${formatPercent(metric.failure_rate_percent)} of tool calls failed; ${formatPercent(metric.retry_recovery_percent)} of failures showed retry recovery.`;
280
- default:
281
- return "No finding available.";
282
- }
173
+ return `\n${rows.map((row) => (row.raw ? ` ${row.painted}` : "")).join("\n")}`;
283
174
  }
284
175
 
285
- function toolImpact(toolId, full, metric) {
286
- switch (toolId) {
287
- case "suckytraces": {
288
- const missing = Math.max(0, metric.session_log_files - metric.traced_session_log_files);
289
- if (metric.session_log_files === 0) return "There are no runs to assess yet.";
290
- return `${formatNumber(missing)} session logs may be hard to replay or debug after the fact.`;
291
- }
292
- case "keybarf":
293
- if (metric.model_facing_secret_fingerprints > 0) return "Credentials may need rotation because secret-like data reached model-facing context.";
294
- if (metric.unique_secret_fingerprints > 0) return "Secret material is present where local tools or logs can retain it.";
295
- return "No credential exposure was detected by the current patterns.";
296
- case "tokengoblin":
297
- if (metric.excess_fresh_input_tokens > 0) return `${formatNumber(metric.excess_fresh_input_tokens)} fresh input tokens exceeded the review threshold in the scanned window.`;
298
- return "Fresh input stayed under the review threshold for the scanned window.";
299
- case "tooltantrum":
300
- if (metric.unresolved_failures > 0) return `${formatNumber(metric.unresolved_failures)} failed tool calls did not show a later recovery signal.`;
301
- if (metric.failed_tool_calls > 0) return "Failures occurred, but scanned retry signals recovered them.";
302
- return "No failed tool calls were detected in the scanned window.";
303
- default:
304
- return "No impact available.";
305
- }
176
+ function heroRows(config, full, metric, score, headline, verdict, color) {
177
+ const hero = `${heroEmoji(config.id, score)} ${headline}`;
178
+ return [
179
+ textRow(hero, color.level(hero, verdict.level)),
180
+ textRow(heroStatLine(config.id, metric, score), color.value(heroStatLine(config.id, metric, score))),
181
+ textRow(
182
+ `scanned ${formatNumber(full.scanned.sessions)} sessions · ${formatNumber(full.scanned.files)} files · ${scanWindow(full.scanned.days)}`,
183
+ color.muted(`scanned ${formatNumber(full.scanned.sessions)} sessions · ${formatNumber(full.scanned.files)} files · ${scanWindow(full.scanned.days)}`),
184
+ ),
185
+ ruleRow(color),
186
+ ];
306
187
  }
307
188
 
308
- function toolNumbers(toolId, full, metric, color) {
309
- switch (toolId) {
310
- case "suckytraces":
311
- return [
312
- metricLine("session logs scanned", formatNumber(metric.session_log_files), color),
313
- metricLine("logs with trace/run ids", `${formatNumber(metric.traced_session_log_files)} / ${formatNumber(metric.session_log_files)}`, color, { level: metric.level }),
314
- metricLine("trace coverage", formatPercent(metric.session_file_coverage_percent), color, { level: metric.level }),
315
- metricLine("trace/run/span markers", formatNumber(metric.structured_trace_signals), color),
316
- ];
317
- case "keybarf":
318
- return [
319
- metricLine("unique fingerprints", formatNumber(metric.unique_secret_fingerprints), color, { level: metric.level }),
320
- metricLine("model-facing", formatNumber(metric.model_facing_secret_fingerprints), color, { level: metric.model_facing_secret_fingerprints > 0 ? "CRITICAL" : "LOW" }),
321
- metricLine("detected kinds", compactKinds(metric.by_kind), color),
322
- ];
323
- case "tokengoblin":
324
- return [
325
- metricLine("fresh input/tool", `${formatNumber(metric.fresh_input_tokens_per_tool_call)} tokens`, color, { level: metric.level }),
326
- metricLine("review threshold", `${formatNumber(metric.review_threshold_fresh_input_tokens_per_tool_call)} tokens/tool`, color),
327
- metricLine("cache hit rate", formatPercent(metric.cache_hit_percent), color, { level: metric.cache_hit_percent >= 50 ? "LOW" : "MEDIUM" }),
328
- metricLine("excess fresh input", `${formatNumber(metric.excess_fresh_input_tokens)} tokens`, color, { level: metric.excess_fresh_input_tokens > 0 ? metric.level : "LOW" }),
329
- metricLine("30d active tokens", formatNumber(full.monthly_impact_estimate.active_tokens_30d), color),
330
- ];
331
- case "tooltantrum": {
332
- const recovered = Math.max(0, metric.failed_tool_calls - metric.unresolved_failures);
333
- return [
334
- metricLine("tool calls", formatNumber(metric.tool_calls), color),
335
- metricLine("failed calls", formatNumber(metric.failed_tool_calls), color, { level: metric.failed_tool_calls > 0 ? metric.level : "LOW" }),
336
- metricLine("failure rate", formatPercent(metric.failure_rate_percent), color, { level: metric.level }),
337
- metricLine("recovered after retry", formatNumber(recovered), color, { level: recovered > 0 ? "LOW" : undefined }),
338
- metricLine("unresolved failures", formatNumber(metric.unresolved_failures), color, { level: metric.unresolved_failures > 0 ? metric.level : "LOW" }),
339
- metricLine("retry recovery rate", formatPercent(metric.retry_recovery_percent), color, { level: metric.retry_recovery_percent >= 80 ? "LOW" : "HIGH" }),
340
- ];
341
- }
342
- default:
343
- return [];
189
+ function statRows(toolId, metric, verdict, score, color) {
190
+ if (toolId === "suckytraces") {
191
+ const missing = Math.max(0, metric.session_log_files - metric.traced_session_log_files);
192
+ return [
193
+ detailRow("severity", `${score}/100 · ${verdict.label}`, color, { level: verdict.level }),
194
+ detailRow("coverage", formatPercent(metric.session_file_coverage_percent), color, { level: metric.level }),
195
+ detailRow("with ids", `${formatNumber(metric.traced_session_log_files)} / ${formatNumber(metric.session_log_files)}`, color, { level: metric.level }),
196
+ detailRow("missing", formatNumber(missing), color, { level: missing > 0 ? metric.level : "LOW" }),
197
+ detailRow("signals", formatNumber(metric.structured_trace_signals), color),
198
+ ];
344
199
  }
345
- }
346
200
 
347
- function toolNextStep(toolId, metric) {
348
- switch (toolId) {
349
- case "suckytraces":
350
- return "Write a run_id, trace_id, or span_id into each tool execution log.";
351
- case "keybarf":
352
- if (metric.model_facing_secret_fingerprints > 0) return "Rotate exposed credentials and move secrets behind environment or secret-manager boundaries.";
353
- if (metric.unique_secret_fingerprints > 0) return "Move secrets out of logs/config and keep only redacted values in agent context.";
354
- return "Keep secrets out of prompts, tool arguments, stack traces, and debug logs.";
355
- case "tokengoblin":
356
- if (metric.excess_fresh_input_tokens > 0) return "Trim repeated prompt context and cache stable input before tool-heavy runs.";
357
- return "Keep fresh input per tool call below the review threshold.";
358
- case "tooltantrum":
359
- if (metric.unresolved_failures > 0) return "Classify retryable errors, make tool actions idempotent, and persist final failure details.";
360
- return "Keep retry behavior explicit: classify errors, cap attempts, and log recovery.";
361
- default:
362
- return "Review the finding above.";
363
- }
201
+ const recovered = Math.max(0, metric.failed_tool_calls - metric.unresolved_failures);
202
+ return [
203
+ detailRow("severity", `${score}/100 · ${verdict.label}`, color, { level: verdict.level }),
204
+ detailRow("calls", formatNumber(metric.tool_calls), color),
205
+ detailRow("failed", `${formatNumber(metric.failed_tool_calls)} (${formatPercent(metric.failure_rate_percent)})`, color, { level: metric.level }),
206
+ detailRow("rescued", `${formatNumber(recovered)} (${formatPercent(metric.retry_recovery_percent)})`, color),
207
+ detailRow("unresolved", formatNumber(metric.unresolved_failures), color, { level: metric.unresolved_failures > 0 ? metric.level : "LOW" }),
208
+ ];
364
209
  }
365
210
 
366
- function toolAgentLeaderboard(toolId, full, color) {
367
- const ranked = rankAgents(toolId, full).slice(0, 5);
368
- if (ranked.length === 0) return paragraphRows("No agent had enough data for a leaderboard.", color);
369
- const best = ranked[0];
370
- return ranked.map((entry) => {
371
- const winner = sameRank(entry, best);
372
- return leaderboardLine(winner ? `🏆 ${entry.label}` : ` ${entry.label}`, entry.value, color, winner);
373
- });
211
+ function sourceEntries(toolId, full) {
212
+ return activeSources(full)
213
+ .map((source) => sourceEntry(toolId, source))
214
+ .sort((a, b) => b.sort - a.sort || a.label.localeCompare(b.label));
374
215
  }
375
216
 
376
- function rankAgents(toolId, full) {
377
- const sources = Object.values(full.scanned.sources ?? {})
378
- .filter((source) => source.label !== "Agent config" && source.unavailable !== true);
217
+ function sourceEntry(toolId, source) {
218
+ const files = source.files ?? 0;
219
+ const sessions = source.sessions ?? 0;
379
220
 
380
221
  if (toolId === "suckytraces") {
381
- return sources
382
- .filter((source) => eligibleAgent(toolId, source))
383
- .map((source) => {
384
- const percent = source.files > 0 ? Math.round(((source.traced_session_log_files ?? 0) / source.files) * 100) : 0;
385
- return {
386
- label: source.label,
387
- sort: -percent,
388
- value: `${percent}% coverage (${formatNumber(source.traced_session_log_files ?? 0)} / ${formatNumber(source.files)})`,
389
- };
390
- })
391
- .sort(compareAgentRank);
392
- }
393
-
394
- if (toolId === "keybarf") {
395
- return sources
396
- .filter((source) => eligibleAgent(toolId, source))
397
- .map((source) => {
398
- const modelFacing = source.model_facing_secret_fingerprints ?? 0;
399
- const total = Math.max(source.secret_fingerprints ?? 0, modelFacing);
400
- return {
401
- label: source.label,
402
- sort: modelFacing,
403
- tiebreaker: total,
404
- value: `${formatNumber(modelFacing)} model-facing · ${formatNumber(total)} total`,
405
- };
406
- })
407
- .sort(compareAgentRank);
408
- }
409
-
410
- if (toolId === "tokengoblin") {
411
- return sources
412
- .filter((source) => eligibleAgent(toolId, source))
413
- .map((source) => {
414
- const freshPerTool = Math.round((source.fresh_input_tokens ?? 0) / source.tool_calls);
415
- return {
416
- label: source.label,
417
- sort: freshPerTool,
418
- value: `${formatNumber(freshPerTool)} fresh input/tool`,
419
- };
420
- })
421
- .sort(compareAgentRank);
422
- }
423
-
424
- if (toolId === "tooltantrum") {
425
- return sources
426
- .filter((source) => eligibleAgent(toolId, source))
427
- .map((source) => {
428
- const failureRate = round(((source.failed_tool_calls ?? 0) / source.tool_calls) * 100, 1);
429
- return {
430
- label: source.label,
431
- sort: failureRate,
432
- tiebreaker: source.failed_tool_calls ?? 0,
433
- value: `${failureRate}% failure (${formatNumber(source.failed_tool_calls ?? 0)} / ${formatNumber(source.tool_calls)})`,
434
- };
435
- })
436
- .sort(compareAgentRank);
222
+ const traced = source.traced_session_log_files ?? 0;
223
+ const missing = Math.max(0, files - traced);
224
+ const coverage = files > 0 ? round((traced / files) * 100, 1) : 0;
225
+ return {
226
+ label: source.label,
227
+ sort: ((100 - coverage) * 1_000_000) + files,
228
+ showInText: files > 0,
229
+ detail: `${formatPercent(coverage)} traced · ${formatNumber(missing)} missing · ${formatNumber(files)} files`,
230
+ json: { source: source.label, files, sessions, trace_coverage_percent: coverage, traced_session_log_files: traced, missing_trace_files: missing },
231
+ };
437
232
  }
438
233
 
439
- return [];
234
+ const calls = source.tool_calls ?? 0;
235
+ const failed = source.failed_tool_calls ?? 0;
236
+ const failureRate = calls > 0 ? round((failed / calls) * 100, 1) : 0;
237
+ return {
238
+ label: source.label,
239
+ sort: (failureRate * 1_000_000) + failed,
240
+ showInText: calls > 0,
241
+ detail: `${formatPercent(failureRate)} fail · ${formatNumber(failed)}/${formatNumber(calls)} · ${formatNumber(sessions)} sessions`,
242
+ json: { source: source.label, files, sessions, tool_calls: calls, failed_tool_calls: failed, failure_rate_percent: failureRate },
243
+ };
440
244
  }
441
245
 
442
- function eligibleAgent(toolId, source) {
443
- switch (toolId) {
444
- case "suckytraces":
445
- return (source.files ?? 0) >= 20;
446
- case "keybarf":
447
- return (source.files ?? 0) >= 20 || (source.sessions ?? 0) >= 20;
448
- case "tokengoblin":
449
- return (source.tool_calls ?? 0) >= 50 && (source.fresh_input_tokens ?? 0) > 0;
450
- case "tooltantrum":
451
- return (source.tool_calls ?? 0) >= 50;
452
- default:
453
- return false;
454
- }
246
+ function activeSources(full) {
247
+ return Object.values(full.scanned.sources ?? {}).filter((source) => {
248
+ if (source.label === "Agent config" || source.unavailable === true) return false;
249
+ return (source.files ?? 0) > 0 ||
250
+ (source.sessions ?? 0) > 0 ||
251
+ (source.tool_calls ?? 0) > 0 ||
252
+ (source.trace_signals ?? 0) > 0;
253
+ });
455
254
  }
456
255
 
457
- function compareAgentRank(a, b) {
458
- if (a.sort !== b.sort) return a.sort - b.sort;
459
- return (a.tiebreaker ?? 0) - (b.tiebreaker ?? 0);
460
- }
256
+ function renderShareCard(config, full, metric, score, headline, sources) {
257
+ const top = sources.find((entry) => entry.showInText);
258
+ const lines = [
259
+ `${heroEmoji(config.id, score)} ${headline}`,
260
+ `Severity: ${score}/100`,
261
+ `Scanned: ${formatNumber(full.scanned.sessions)} sessions · ${formatNumber(full.scanned.files)} files · ${scanWindow(full.scanned.days)}`,
262
+ ];
461
263
 
462
- function sameRank(a, b) {
463
- return a.sort === b.sort && (a.tiebreaker ?? 0) === (b.tiebreaker ?? 0);
464
- }
264
+ if (config.id === "suckytraces") {
265
+ const missing = Math.max(0, metric.session_log_files - metric.traced_session_log_files);
266
+ lines.push(`Stats: ${formatPercent(metric.session_file_coverage_percent)} traced · ${formatNumber(metric.traced_session_log_files)}/${formatNumber(metric.session_log_files)} logs with IDs · ${formatNumber(missing)} missing`);
267
+ } else {
268
+ lines.push(`Stats: ${formatPercent(metric.failure_rate_percent)} failed · ${formatNumber(metric.failed_tool_calls)}/${formatNumber(metric.tool_calls)} calls · ${formatNumber(metric.unresolved_failures)} unresolved · ${formatPercent(metric.retry_recovery_percent)} rescued`);
269
+ }
465
270
 
466
- function leaderboardLine(label, value, color, winner) {
467
- const safeLabel = shortText(label, 26).padEnd(26);
468
- const paintLabel = winner ? color.solution(safeLabel) : color.label(safeLabel);
469
- const paintValue = winner ? color.solution(value) : color.value(value);
470
- return { raw: ` ${safeLabel} ${value}`, painted: ` ${paintLabel} ${paintValue}` };
271
+ if (top) lines.push(`Worst: ${top.label} ${top.detail}`);
272
+ return lines.join("\n");
471
273
  }
472
274
 
473
- function sectionTitle(toolId) {
474
- switch (toolId) {
475
- case "suckytraces":
476
- return "trace coverage";
477
- case "keybarf":
478
- return "credential exposure";
479
- case "tokengoblin":
480
- return "context usage";
481
- case "tooltantrum":
482
- return "tool reliability";
483
- default:
484
- return "metrics";
485
- }
275
+ function heroTitle(toolId) {
276
+ return toolId === "suckytraces" ? "SUCKY TRACES" : "TOOL TANTRUM";
486
277
  }
487
278
 
488
- function compactStatus(toolId, metric) {
489
- switch (toolId) {
490
- case "suckytraces":
491
- if (metric.session_log_files === 0) return "no session log files were found";
492
- if (metric.session_file_coverage_percent >= 80) return "most session logs contain trace evidence";
493
- if (metric.session_file_coverage_percent >= 30) return "some session logs contain trace evidence";
494
- return "fewer than 30% of session logs contain trace evidence";
495
- case "keybarf":
496
- if (metric.model_facing_secret_fingerprints > 0) return "secret fingerprints reached model-facing records";
497
- if (metric.unique_secret_fingerprints > 0) return "secret fingerprints were found in logs or config";
498
- return "no secret fingerprints matched known patterns";
499
- case "tokengoblin":
500
- if (metric.level === "NO_DATA") return "no token usage records were found";
501
- if (metric.fresh_input_tokens_per_tool_call >= metric.review_threshold_fresh_input_tokens_per_tool_call) return "fresh input per tool call exceeds the review threshold";
502
- if (metric.cache_hit_percent < 20 && metric.fresh_input_tokens > 0) return "cache hit rate is low for observed input";
503
- return "fresh input per tool call is within the review threshold";
504
- case "tooltantrum":
505
- return toolReliabilityStatus(metric);
506
- default:
507
- return "ok";
508
- }
279
+ function heroEmoji(toolId, score) {
280
+ if (score >= 75) return toolId === "suckytraces" ? "🕳️" : "🔥";
281
+ if (score >= 45) return "⚠️";
282
+ return "";
509
283
  }
510
284
 
511
- function toolReliabilityStatus(metric) {
512
- if (metric.tool_calls === 0) return "no tool-call records were found";
513
- if (metric.unresolved_failures > 0 && metric.retry_recovery_percent < 20) return "failed calls rarely show retry recovery";
514
- if (metric.unresolved_failures > 0) return "some failed calls did not show recovery";
515
- if (metric.failure_rate_percent >= 15) return "tool-call failure rate is high";
516
- if (metric.failure_rate_percent >= 5) return "tool-call failure rate is moderate";
517
- return "tool-call failure rate is low";
285
+ function heroStatLine(toolId, metric, score) {
286
+ if (toolId === "suckytraces") {
287
+ const missing = Math.max(0, metric.session_log_files - metric.traced_session_log_files);
288
+ return `${score}/100 SEVERITY · ${formatPercent(metric.session_file_coverage_percent)} TRACED · ${formatNumber(missing)} MISSING`;
289
+ }
290
+ return `${score}/100 SEVERITY · ${formatPercent(metric.failure_rate_percent)} FAILED · ${formatNumber(metric.unresolved_failures)} UNRESOLVED`;
518
291
  }
519
292
 
520
293
  function verdictFor(config, metric, score) {
521
- const level = metric.level ?? scoreLevel(score);
522
- if (score >= 75 || level === "CRITICAL" || level === "MISSING") return { level, label: config.bad, mood: "bad" };
523
- if (score >= 45 || level === "HIGH") return { level, label: "spicy enough to investigate", mood: "spicy" };
524
- if (score >= 20 || level === "MEDIUM" || level === "PARTIAL") return { level, label: "a little haunted", mood: "haunted" };
525
- return { level, label: config.good, mood: "good" };
294
+ const level = scoreLevel(score, metric.level);
295
+ if (score >= 75 || level === "CRITICAL" || level === "MISSING") return { level, label: config.bad };
296
+ if (score >= 45 || level === "HIGH") return { level, label: "serious" };
297
+ if (score >= 20 || level === "MEDIUM" || level === "PARTIAL") return { level, label: "watch" };
298
+ return { level, label: config.good };
526
299
  }
527
300
 
528
- function scoreLevel(score) {
301
+ function scoreLevel(score, fallback) {
302
+ const normalized = String(fallback ?? "").toUpperCase();
303
+ if (normalized === "CRITICAL" || normalized === "MISSING") return normalized;
529
304
  if (score >= 75) return "CRITICAL";
530
305
  if (score >= 45) return "HIGH";
531
306
  if (score >= 20) return "MEDIUM";
307
+ if (normalized === "NO_DATA" || normalized === "CONFIG_ONLY") return normalized;
532
308
  return "LOW";
533
309
  }
534
310
 
535
- function finding(label, value, level) {
536
- return { label, value, level };
537
- }
538
-
539
- function pairRow(label, value, color, options = {}) {
540
- const safeLabel = String(label).slice(0, 12);
541
- const raw = `${safeLabel.padEnd(12)} ${value}`;
542
- const paintValue = options.solution ? color.solution(value) : options.level ? color.level(value, options.level) : color.value(value);
543
- return { raw, painted: `${color.label(safeLabel.padEnd(12))} ${paintValue}` };
544
- }
545
-
546
- function reportLine(label, value, color, options = {}) {
547
- const labelWidth = options.labelWidth ?? 14;
548
- const indent = " ".repeat(options.indent ?? 2);
549
- const safeLabel = String(label).toLowerCase().slice(0, labelWidth);
550
- const raw = `${indent}${safeLabel.padEnd(labelWidth)} ${value}`;
551
- const paintValue = options.solution ? color.solution(value) : options.level ? color.level(value, options.level) : color.value(value);
552
- return { raw, painted: `${indent}${color.label(safeLabel.padEnd(labelWidth))} ${paintValue}` };
553
- }
554
-
555
- function metricLine(label, value, color, options = {}) {
556
- return reportLine(label, value, color, { ...options, indent: 4, labelWidth: 28 });
557
- }
558
-
559
- function reportSection(label, color) {
560
- return reportText(String(label).toLowerCase(), color.title(String(label).toLowerCase()));
561
- }
562
-
563
- function paragraphRows(value, color, options = {}) {
564
- const indent = " ";
565
- return wrapText(String(value), 76).map((line) => {
566
- const paintValue = options.solution ? color.solution(line) : options.level ? color.level(line, options.level) : color.value(line);
567
- return { raw: `${indent}${line}`, painted: `${indent}${paintValue}` };
568
- });
569
- }
570
-
571
- function reportText(raw, painted = raw) {
572
- return { raw, painted: raw ? ` ${painted}` : "" };
573
- }
574
-
575
- function wrappedPairRows(label, value, color, options = {}) {
576
- const labelWidth = 12;
577
- const width = options.width ?? 74;
578
- const textWidth = Math.max(20, width - labelWidth - 1);
579
- const lines = wrapText(String(value), textWidth);
580
- return lines.map((line, index) => {
581
- const visibleLabel = index === 0 ? String(label).slice(0, labelWidth) : "";
582
- const paintValue = options.solution ? color.solution(line) : options.level ? color.level(line, options.level) : color.value(line);
583
- return {
584
- raw: `${visibleLabel.padEnd(labelWidth)} ${line}`,
585
- painted: `${color.label(visibleLabel.padEnd(labelWidth))} ${paintValue}`,
586
- };
587
- });
588
- }
589
-
590
- function ruleRow() {
591
- return { rule: true, raw: "", painted: "" };
592
- }
593
-
594
- function textRow(raw, painted = raw) {
595
- return { raw, painted };
596
- }
597
-
598
- function blankRow() {
599
- return { raw: "", painted: "" };
600
- }
601
-
602
- function box(title, rows, color) {
603
- const width = Math.max(48, title.length + 4, ...rows.map((row) => row.raw.length));
604
- const topFill = "─".repeat(Math.max(1, width - title.length - 1));
605
- const bottomFill = "─".repeat(width + 2);
606
- return [
607
- `${color.border("╭─")} ${color.title(title)} ${color.border(`${topFill}╮`)}`,
608
- ...rows.map((row) => boxRow(row, width, color)),
609
- `${color.border("╰")}${color.border(bottomFill)}${color.border("╯")}`,
610
- ].join("\n");
611
- }
612
-
613
- function boxRow(row, width, color) {
614
- const padding = " ".repeat(Math.max(0, width - row.raw.length));
615
- return `${color.border("│")} ${row.painted}${padding} ${color.border("│")}`;
616
- }
617
-
618
- function fixedBox(title, rows, color, width) {
619
- const topFill = "─".repeat(Math.max(1, width - title.length - 1));
620
- const bottomFill = "─".repeat(width + 2);
621
- return [
622
- `${color.border("╭─")} ${color.title(title)} ${color.border(`${topFill}╮`)}`,
623
- ...rows.map((row) => fixedBoxRow(row, width, color)),
624
- `${color.border("╰")}${color.border(bottomFill)}${color.border("╯")}`,
625
- ].join("\n");
626
- }
627
-
628
- function fixedBoxRow(row, width, color) {
629
- if (row.rule) return `${color.border("├")}${color.border("─".repeat(width + 2))}${color.border("┤")}`;
630
- const raw = row.raw.length > width ? `${row.raw.slice(0, Math.max(0, width - 1))}…` : row.raw;
631
- const painted = row.raw.length > width ? raw : row.painted;
632
- const padding = " ".repeat(Math.max(0, width - raw.length));
633
- return `${color.border("│")} ${painted}${padding} ${color.border("│")}`;
634
- }
635
-
636
- function largestFileLine(files) {
637
- if (!Array.isArray(files) || files.length === 0) return "none";
638
- const file = files[0];
639
- if (typeof file === "string") return compactPath(file);
640
- return `${compactPath(file.path ?? file.file ?? "unknown")} (${formatNumber(file.lines ?? 0)} lines)`;
641
- }
642
-
643
- function compactPath(value) {
644
- const text = String(value ?? "unknown");
645
- if (text.length <= 30) return text;
646
- const tail = text.split(/[\\/]/).slice(-2).join("/");
647
- return `…/${tail}`;
648
- }
649
-
650
- function compactKinds(kinds) {
651
- const value = Object.entries(kinds ?? {})
652
- .filter(([, count]) => count > 0)
653
- .sort((a, b) => b[1] - a[1])
654
- .slice(0, 4)
655
- .map(([kind, count]) => `${kind}:${count}`)
656
- .join(", ");
657
- return value || "none";
658
- }
659
-
660
- function rootSummary(roots) {
661
- if (!Array.isArray(roots) || roots.length === 0) return "0";
662
- return shortText(`${roots.length} (${roots.slice(0, 3).join(", ")}${roots.length > 3 ? ", …" : ""})`, 44);
663
- }
664
-
665
- function shortText(value, max) {
666
- const text = String(value ?? "");
667
- if (text.length <= max) return text;
668
- return `${text.slice(0, Math.max(0, max - 1))}…`;
669
- }
670
-
671
311
  function scoreFromLevel(level) {
672
312
  switch (String(level).toUpperCase()) {
673
313
  case "CRITICAL":
@@ -678,44 +318,57 @@ function scoreFromLevel(level) {
678
318
  case "MEDIUM":
679
319
  case "PARTIAL":
680
320
  return 34;
681
- case "CONFIG_ONLY":
682
- case "NO_DATA":
683
- return 12;
684
321
  default:
685
322
  return 5;
686
323
  }
687
324
  }
688
325
 
689
- function clamp(value) {
690
- return Math.max(0, Math.min(100, Math.round(Number(value) || 0)));
326
+ function finding(label, value, level) {
327
+ return { label, value, level };
691
328
  }
692
329
 
693
- function round(value, digits) {
694
- const factor = 10 ** digits;
695
- return Math.round(value * factor) / factor;
330
+ function textRow(raw, painted = raw) {
331
+ return { raw, painted };
696
332
  }
697
333
 
698
- function scanWindow(days) {
699
- return days === "all" ? "all sessions" : `last ${days}d`;
334
+ function blankRow() {
335
+ return { raw: "", painted: "" };
700
336
  }
701
337
 
702
- function formatNumber(value) {
703
- return Number(value || 0).toLocaleString("en-US");
338
+ function ruleRow(color) {
339
+ const rule = "━".repeat(58);
340
+ return textRow(rule, color.rule(rule));
704
341
  }
705
342
 
706
- function formatPercent(value) {
707
- const number = Number(value || 0);
708
- return `${number.toFixed(Number.isInteger(number) ? 0 : 1)}%`;
343
+ function sectionRow(title, color) {
344
+ return textRow(title, color.title(title));
345
+ }
346
+
347
+ function detailRow(label, value, color, options = {}) {
348
+ const safeLabel = String(label).toLowerCase().padEnd(11);
349
+ const raw = ` ${safeLabel} ${value}`;
350
+ const paintValue = options.level ? color.level(value, options.level) : color.value(value);
351
+ return textRow(raw, ` ${color.label(safeLabel)} ${paintValue}`);
709
352
  }
710
353
 
711
- function progressBar(value, max, width) {
712
- const ratio = Math.max(0, Math.min(1, Number(value || 0) / Math.max(1, max)));
713
- const filled = Math.max(value > 0 ? 1 : 0, Math.round(ratio * width));
714
- return `${"█".repeat(Math.min(width, filled))}${"░".repeat(Math.max(0, width - filled))}`;
354
+ function sourceRow(entry, color, highlight = false, labelWidth = 11) {
355
+ const prefix = highlight ? "🚨 " : " ";
356
+ const label = `${prefix}${entry.label.toLowerCase()}`.padEnd(labelWidth + prefix.length);
357
+ const raw = ` ${label} ${entry.detail}`;
358
+ if (!highlight) return textRow(raw, ` ${color.label(label)} ${color.value(entry.detail)}`);
359
+ return textRow(raw, ` ${color.level(label, "CRITICAL")} ${color.level(entry.detail, "CRITICAL")}`);
715
360
  }
716
361
 
717
- function scoreSparkline(score) {
718
- return progressBar(score, 100, 12);
362
+ function mutedRow(raw, color) {
363
+ return textRow(raw, color.muted(raw));
364
+ }
365
+
366
+ function paragraphRows(value, color, options = {}) {
367
+ return wrapText(String(value), 58).map((line) => {
368
+ const raw = ` ${line}`;
369
+ const paintedValue = options.level ? color.level(line, options.level) : color.value(line);
370
+ return textRow(raw, ` ${paintedValue}`);
371
+ });
719
372
  }
720
373
 
721
374
  function wrapText(value, width) {
@@ -723,14 +376,6 @@ function wrapText(value, width) {
723
376
  const lines = [];
724
377
  let line = "";
725
378
  for (const word of words) {
726
- if (word.length > width) {
727
- if (line) {
728
- lines.push(line);
729
- line = "";
730
- }
731
- lines.push(word.slice(0, width));
732
- continue;
733
- }
734
379
  if (!line) {
735
380
  line = word;
736
381
  continue;
@@ -746,21 +391,40 @@ function wrapText(value, width) {
746
391
  return lines.length > 0 ? lines : [""];
747
392
  }
748
393
 
749
- function formatSeconds(ms) {
750
- const seconds = Math.max(0.1, Number(ms || 0) / 1000);
751
- return `${seconds.toFixed(seconds >= 10 ? 0 : 1)}s`;
394
+ function scanWindow(days) {
395
+ return days === "all" ? "all sessions" : `last ${days}d`;
396
+ }
397
+
398
+ function clamp(value) {
399
+ return Math.max(0, Math.min(100, Math.round(Number(value) || 0)));
400
+ }
401
+
402
+ function round(value, digits) {
403
+ const factor = 10 ** digits;
404
+ return Math.round(value * factor) / factor;
405
+ }
406
+
407
+ function formatNumber(value) {
408
+ return Number(value || 0).toLocaleString("en-US");
409
+ }
410
+
411
+ function formatCount(value, singular, plural = `${singular}s`) {
412
+ const number = Number(value || 0);
413
+ return `${formatNumber(number)} ${number === 1 ? singular : plural}`;
414
+ }
415
+
416
+ function formatPercent(value) {
417
+ const number = Number(value || 0);
418
+ return `${number.toFixed(Number.isInteger(number) ? 0 : 1)}%`;
752
419
  }
753
420
 
754
421
  function colorizer(enabled) {
755
422
  if (!enabled) return plainColors();
756
423
  const paint = (code, value) => `\x1b[${code}m${value}\x1b[0m`;
757
424
  return {
758
- accent: (value) => paint("38;2;147;197;253", value),
759
- border: (value) => paint("38;2;100;116;139", value),
760
425
  label: (value) => paint("38;2;186;230;253", value),
761
426
  muted: (value) => paint("38;2;148;163;184", value),
762
- rule: (value) => paint("38;2;71;85;105", value),
763
- solution: (value) => paint("38;2;125;211;252", value),
427
+ rule: (value) => paint("38;2;51;65;85", value),
764
428
  title: (value) => paint("38;2;147;197;253;1", value),
765
429
  value: (value) => paint("38;2;226;232;240", value),
766
430
  level: (value, level) => paint(levelColor(level), value),
@@ -769,12 +433,9 @@ function colorizer(enabled) {
769
433
 
770
434
  function plainColors() {
771
435
  return {
772
- accent: (value) => value,
773
- border: (value) => value,
774
436
  label: (value) => value,
775
437
  muted: (value) => value,
776
438
  rule: (value) => value,
777
- solution: (value) => value,
778
439
  title: (value) => value,
779
440
  value: (value) => value,
780
441
  level: (value) => value,