@kevinrabun/judges 3.64.0 → 3.66.0

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +112 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/code-health.d.ts +5 -0
  6. package/dist/commands/code-health.d.ts.map +1 -0
  7. package/dist/commands/code-health.js +196 -0
  8. package/dist/commands/code-health.js.map +1 -0
  9. package/dist/commands/finding-suppress.d.ts +5 -0
  10. package/dist/commands/finding-suppress.d.ts.map +1 -0
  11. package/dist/commands/finding-suppress.js +165 -0
  12. package/dist/commands/finding-suppress.js.map +1 -0
  13. package/dist/commands/finding-timeline.d.ts +5 -0
  14. package/dist/commands/finding-timeline.d.ts.map +1 -0
  15. package/dist/commands/finding-timeline.js +144 -0
  16. package/dist/commands/finding-timeline.js.map +1 -0
  17. package/dist/commands/fix-verify.d.ts +5 -0
  18. package/dist/commands/fix-verify.d.ts.map +1 -0
  19. package/dist/commands/fix-verify.js +124 -0
  20. package/dist/commands/fix-verify.js.map +1 -0
  21. package/dist/commands/judge-config.d.ts +5 -0
  22. package/dist/commands/judge-config.d.ts.map +1 -0
  23. package/dist/commands/judge-config.js +152 -0
  24. package/dist/commands/judge-config.js.map +1 -0
  25. package/dist/commands/review-annotate.d.ts +5 -0
  26. package/dist/commands/review-annotate.d.ts.map +1 -0
  27. package/dist/commands/review-annotate.js +123 -0
  28. package/dist/commands/review-annotate.js.map +1 -0
  29. package/dist/commands/review-checkpoint.d.ts +5 -0
  30. package/dist/commands/review-checkpoint.d.ts.map +1 -0
  31. package/dist/commands/review-checkpoint.js +164 -0
  32. package/dist/commands/review-checkpoint.js.map +1 -0
  33. package/dist/commands/review-comment.d.ts +5 -0
  34. package/dist/commands/review-comment.d.ts.map +1 -0
  35. package/dist/commands/review-comment.js +166 -0
  36. package/dist/commands/review-comment.js.map +1 -0
  37. package/dist/commands/review-export.d.ts +5 -0
  38. package/dist/commands/review-export.d.ts.map +1 -0
  39. package/dist/commands/review-export.js +180 -0
  40. package/dist/commands/review-export.js.map +1 -0
  41. package/dist/commands/review-filter.d.ts +5 -0
  42. package/dist/commands/review-filter.d.ts.map +1 -0
  43. package/dist/commands/review-filter.js +166 -0
  44. package/dist/commands/review-filter.js.map +1 -0
  45. package/dist/commands/review-merge.d.ts +5 -0
  46. package/dist/commands/review-merge.d.ts.map +1 -0
  47. package/dist/commands/review-merge.js +146 -0
  48. package/dist/commands/review-merge.js.map +1 -0
  49. package/dist/commands/review-schedule.d.ts +5 -0
  50. package/dist/commands/review-schedule.d.ts.map +1 -0
  51. package/dist/commands/review-schedule.js +170 -0
  52. package/dist/commands/review-schedule.js.map +1 -0
  53. package/dist/commands/review-scope.d.ts +5 -0
  54. package/dist/commands/review-scope.d.ts.map +1 -0
  55. package/dist/commands/review-scope.js +198 -0
  56. package/dist/commands/review-scope.js.map +1 -0
  57. package/dist/commands/review-webhook.d.ts +5 -0
  58. package/dist/commands/review-webhook.d.ts.map +1 -0
  59. package/dist/commands/review-webhook.js +141 -0
  60. package/dist/commands/review-webhook.js.map +1 -0
  61. package/dist/commands/rule-catalog.d.ts +5 -0
  62. package/dist/commands/rule-catalog.d.ts.map +1 -0
  63. package/dist/commands/rule-catalog.js +129 -0
  64. package/dist/commands/rule-catalog.js.map +1 -0
  65. package/dist/commands/setup-wizard.d.ts +5 -0
  66. package/dist/commands/setup-wizard.d.ts.map +1 -0
  67. package/dist/commands/setup-wizard.js +175 -0
  68. package/dist/commands/setup-wizard.js.map +1 -0
  69. package/package.json +1 -1
  70. package/server.json +2 -2
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Code-health — Overall codebase health score across multiple dimensions.
3
+ */
4
+ import { readFileSync, existsSync } from "fs";
5
+ // ─── Severity weights ───────────────────────────────────────────────────────
6
+ const SEVERITY_PENALTY = {
7
+ critical: 25,
8
+ high: 15,
9
+ medium: 8,
10
+ low: 3,
11
+ info: 1,
12
+ };
13
+ function computeGrade(score) {
14
+ if (score >= 90)
15
+ return "A";
16
+ if (score >= 80)
17
+ return "B";
18
+ if (score >= 70)
19
+ return "C";
20
+ if (score >= 60)
21
+ return "D";
22
+ return "F";
23
+ }
24
+ // ─── Dimension Analysis ────────────────────────────────────────────────────
25
+ function categorizeFinding(f) {
26
+ const rule = (f.ruleId || "").toLowerCase();
27
+ const title = (f.title || "").toLowerCase();
28
+ if (rule.includes("sql") ||
29
+ rule.includes("xss") ||
30
+ rule.includes("inject") ||
31
+ rule.includes("auth") ||
32
+ rule.includes("crypto") ||
33
+ rule.includes("secret") ||
34
+ title.includes("vulnerab") ||
35
+ title.includes("security") ||
36
+ title.includes("injection")) {
37
+ return "security";
38
+ }
39
+ if (rule.includes("error") ||
40
+ rule.includes("null") ||
41
+ rule.includes("type") ||
42
+ rule.includes("bound") ||
43
+ rule.includes("overflow") ||
44
+ title.includes("bug") ||
45
+ title.includes("error") ||
46
+ title.includes("crash")) {
47
+ return "reliability";
48
+ }
49
+ if (rule.includes("complex") ||
50
+ rule.includes("dupl") ||
51
+ rule.includes("smell") ||
52
+ rule.includes("long") ||
53
+ rule.includes("dead") ||
54
+ title.includes("maintain") ||
55
+ title.includes("complex") ||
56
+ title.includes("duplicate")) {
57
+ return "maintainability";
58
+ }
59
+ if (rule.includes("perf") ||
60
+ rule.includes("optim") ||
61
+ rule.includes("leak") ||
62
+ title.includes("perform") ||
63
+ title.includes("slow") ||
64
+ title.includes("memory")) {
65
+ return "performance";
66
+ }
67
+ return "quality";
68
+ }
69
+ function computeDimension(name, findings, weight) {
70
+ let penalty = 0;
71
+ for (const f of findings) {
72
+ const sev = (f.severity || "low").toLowerCase();
73
+ penalty += SEVERITY_PENALTY[sev] || 3;
74
+ }
75
+ const score = Math.max(0, Math.min(100, 100 - penalty));
76
+ const details = findings.length === 0 ? `No ${name} issues found` : `${findings.length} ${name} finding(s) detected`;
77
+ return { name, score, weight, findingsCount: findings.length, details };
78
+ }
79
+ // ─── CLI ────────────────────────────────────────────────────────────────────
80
+ export function runCodeHealth(argv) {
81
+ if (argv.includes("--help") || argv.includes("-h")) {
82
+ console.log(`
83
+ judges code-health — Overall codebase health score
84
+
85
+ Usage:
86
+ judges code-health --file verdict.json Compute health from verdict
87
+ judges code-health --format json JSON output
88
+ judges code-health --weights "security=40,reliability=25,maintainability=20,performance=10,quality=5"
89
+
90
+ Options:
91
+ --file <path> Verdict JSON file
92
+ --weights <spec> Custom dimension weights (must total 100)
93
+ --format json JSON output
94
+ --help, -h Show this help
95
+
96
+ Dimensions:
97
+ security Injection, auth, crypto, secrets
98
+ reliability Null checks, type errors, bounds
99
+ maintainability Complexity, duplication, code smells
100
+ performance Memory leaks, optimization issues
101
+ quality General code quality findings
102
+
103
+ Computes a weighted health score (0-100) with a letter grade (A-F).
104
+ `);
105
+ return;
106
+ }
107
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
108
+ const file = argv.find((_a, i) => argv[i - 1] === "--file");
109
+ // Parse custom weights
110
+ const defaultWeights = {
111
+ security: 35,
112
+ reliability: 25,
113
+ maintainability: 20,
114
+ performance: 10,
115
+ quality: 10,
116
+ };
117
+ const weightsArg = argv.find((_a, i) => argv[i - 1] === "--weights");
118
+ if (weightsArg) {
119
+ for (const part of weightsArg.split(",")) {
120
+ const [key, val] = part.split("=");
121
+ if (key && val)
122
+ defaultWeights[key.trim()] = parseInt(val, 10);
123
+ }
124
+ }
125
+ let findings = [];
126
+ let baseScore = 0;
127
+ if (file) {
128
+ if (!existsSync(file)) {
129
+ console.error(`Error: File not found: ${file}`);
130
+ process.exitCode = 1;
131
+ return;
132
+ }
133
+ try {
134
+ const verdict = JSON.parse(readFileSync(file, "utf-8"));
135
+ findings = verdict.findings || [];
136
+ baseScore = verdict.overallScore || 0;
137
+ }
138
+ catch {
139
+ console.error(`Error: Could not parse ${file}`);
140
+ process.exitCode = 1;
141
+ return;
142
+ }
143
+ }
144
+ // Group findings by dimension
145
+ const groups = new Map();
146
+ for (const key of Object.keys(defaultWeights)) {
147
+ groups.set(key, []);
148
+ }
149
+ for (const f of findings) {
150
+ const cat = categorizeFinding(f);
151
+ const arr = groups.get(cat);
152
+ if (arr)
153
+ arr.push(f);
154
+ else {
155
+ const qArr = groups.get("quality");
156
+ if (qArr)
157
+ qArr.push(f);
158
+ }
159
+ }
160
+ // Compute dimensions
161
+ const dimensions = [];
162
+ for (const [name, weight] of Object.entries(defaultWeights)) {
163
+ const dimFindings = groups.get(name) || [];
164
+ dimensions.push(computeDimension(name, dimFindings, weight));
165
+ }
166
+ // Weighted average
167
+ const totalWeight = dimensions.reduce((sum, d) => sum + d.weight, 0);
168
+ const weightedScore = totalWeight > 0 ? Math.round(dimensions.reduce((sum, d) => sum + d.score * d.weight, 0) / totalWeight) : baseScore;
169
+ const report = {
170
+ timestamp: new Date().toISOString(),
171
+ overallScore: weightedScore,
172
+ grade: computeGrade(weightedScore),
173
+ dimensions,
174
+ totalFindings: findings.length,
175
+ summary: `Health score: ${weightedScore}/100 (Grade ${computeGrade(weightedScore)}). ${findings.length} total findings across ${dimensions.filter((d) => d.findingsCount > 0).length} dimensions.`,
176
+ };
177
+ if (format === "json") {
178
+ console.log(JSON.stringify(report, null, 2));
179
+ return;
180
+ }
181
+ console.log(`\n Codebase Health Report\n ═════════════════════════════`);
182
+ console.log(` Overall Score: ${report.overallScore}/100`);
183
+ console.log(` Grade: ${report.grade}`);
184
+ console.log(` Total Findings: ${report.totalFindings}`);
185
+ console.log();
186
+ console.log(" Dimensions");
187
+ console.log(" ─────────────────────────────");
188
+ for (const d of dimensions) {
189
+ const bar = "█".repeat(Math.floor(d.score / 5)) + "░".repeat(20 - Math.floor(d.score / 5));
190
+ const grade = computeGrade(d.score);
191
+ console.log(` ${d.name.padEnd(18)} ${bar} ${d.score}/100 (${grade}) — ${d.findingsCount} findings [weight: ${d.weight}%]`);
192
+ }
193
+ console.log(`\n Summary: ${report.summary}`);
194
+ console.log();
195
+ }
196
+ //# sourceMappingURL=code-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-health.js","sourceRoot":"","sources":["../../src/commands/code-health.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Finding-suppress — Suppress specific findings with inline or config suppression.
3
+ */
4
+ export declare function runFindingSuppress(argv: string[]): void;
5
+ //# sourceMappingURL=finding-suppress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-suppress.d.ts","sourceRoot":"","sources":["../../src/commands/finding-suppress.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgDH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA2JvD"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Finding-suppress — Suppress specific findings with inline or config suppression.
3
+ */
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
+ import { join, dirname } from "path";
6
+ // ─── Storage ────────────────────────────────────────────────────────────────
7
+ const SUPPRESS_FILE = join(".judges", "suppressions.json");
8
+ function loadSuppressions() {
9
+ if (!existsSync(SUPPRESS_FILE))
10
+ return { version: "1.0.0", suppressions: [] };
11
+ try {
12
+ return JSON.parse(readFileSync(SUPPRESS_FILE, "utf-8"));
13
+ }
14
+ catch {
15
+ return { version: "1.0.0", suppressions: [] };
16
+ }
17
+ }
18
+ function saveSuppressions(config) {
19
+ mkdirSync(dirname(SUPPRESS_FILE), { recursive: true });
20
+ writeFileSync(SUPPRESS_FILE, JSON.stringify(config, null, 2), "utf-8");
21
+ }
22
+ // ─── Active check ───────────────────────────────────────────────────────────
23
+ function isActive(entry) {
24
+ if (!entry.expiresAt)
25
+ return true;
26
+ return new Date(entry.expiresAt) > new Date();
27
+ }
28
+ // ─── CLI ────────────────────────────────────────────────────────────────────
29
+ export function runFindingSuppress(argv) {
30
+ if (argv.includes("--help") || argv.includes("-h")) {
31
+ console.log(`
32
+ judges finding-suppress — Suppress specific findings
33
+
34
+ Usage:
35
+ judges finding-suppress list List suppressions
36
+ judges finding-suppress add --rule sql-injection --reason "False positive in test"
37
+ judges finding-suppress remove --rule sql-injection
38
+ judges finding-suppress check --rule sql-injection
39
+ judges finding-suppress --format json
40
+
41
+ Subcommands:
42
+ list List all active suppressions
43
+ add Add a suppression
44
+ remove Remove a suppression
45
+ check Check if a rule is suppressed
46
+
47
+ Options:
48
+ --rule <id> Rule ID to suppress
49
+ --reason <text> Reason for suppression (required for add)
50
+ --author <name> Who suppressed (default: "unknown")
51
+ --expires <date> Expiration date (ISO 8601, optional)
52
+ --scope <scope> Scope: global, file, line (default: global)
53
+ --format json JSON output
54
+ --help, -h Show this help
55
+
56
+ Suppressions are stored in .judges/suppressions.json. Expired
57
+ suppressions are automatically ignored. Use inline comments
58
+ (// judges-suppress: <rule-id>) for line-level suppression.
59
+ `);
60
+ return;
61
+ }
62
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
63
+ const subcommand = argv.find((a) => ["list", "add", "remove", "check"].includes(a)) || "list";
64
+ const config = loadSuppressions();
65
+ if (subcommand === "add") {
66
+ const ruleId = argv.find((_a, i) => argv[i - 1] === "--rule");
67
+ const reason = argv.find((_a, i) => argv[i - 1] === "--reason");
68
+ const author = argv.find((_a, i) => argv[i - 1] === "--author") || "unknown";
69
+ const expires = argv.find((_a, i) => argv[i - 1] === "--expires");
70
+ const scope = argv.find((_a, i) => argv[i - 1] === "--scope") || "global";
71
+ if (!ruleId) {
72
+ console.error("Error: --rule is required.");
73
+ process.exitCode = 1;
74
+ return;
75
+ }
76
+ if (!reason) {
77
+ console.error("Error: --reason is required. Explain why the finding is suppressed.");
78
+ process.exitCode = 1;
79
+ return;
80
+ }
81
+ const existing = config.suppressions.find((s) => s.ruleId === ruleId);
82
+ if (existing) {
83
+ console.error(`Error: Rule '${ruleId}' is already suppressed.`);
84
+ process.exitCode = 1;
85
+ return;
86
+ }
87
+ const entry = {
88
+ ruleId,
89
+ reason,
90
+ author,
91
+ timestamp: new Date().toISOString(),
92
+ scope,
93
+ };
94
+ if (expires)
95
+ entry.expiresAt = expires;
96
+ config.suppressions.push(entry);
97
+ saveSuppressions(config);
98
+ console.log(`Suppressed '${ruleId}' — reason: ${reason}${expires ? ` (expires: ${expires})` : ""}`);
99
+ return;
100
+ }
101
+ if (subcommand === "remove") {
102
+ const ruleId = argv.find((_a, i) => argv[i - 1] === "--rule");
103
+ if (!ruleId) {
104
+ console.error("Error: --rule is required.");
105
+ process.exitCode = 1;
106
+ return;
107
+ }
108
+ const idx = config.suppressions.findIndex((s) => s.ruleId === ruleId);
109
+ if (idx < 0) {
110
+ console.error(`Error: No suppression found for '${ruleId}'.`);
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+ config.suppressions.splice(idx, 1);
115
+ saveSuppressions(config);
116
+ console.log(`Removed suppression for '${ruleId}'.`);
117
+ return;
118
+ }
119
+ if (subcommand === "check") {
120
+ const ruleId = argv.find((_a, i) => argv[i - 1] === "--rule");
121
+ if (!ruleId) {
122
+ console.error("Error: --rule is required.");
123
+ process.exitCode = 1;
124
+ return;
125
+ }
126
+ const entry = config.suppressions.find((s) => s.ruleId === ruleId);
127
+ if (!entry) {
128
+ console.log(`Rule '${ruleId}' is NOT suppressed.`);
129
+ return;
130
+ }
131
+ const active = isActive(entry);
132
+ if (format === "json") {
133
+ console.log(JSON.stringify({ ruleId, suppressed: active, entry }, null, 2));
134
+ return;
135
+ }
136
+ console.log(`Rule '${ruleId}' is ${active ? "SUPPRESSED" : "EXPIRED"}`);
137
+ console.log(` Reason: ${entry.reason}`);
138
+ console.log(` Author: ${entry.author}`);
139
+ console.log(` Since: ${entry.timestamp}`);
140
+ if (entry.expiresAt)
141
+ console.log(` Expires: ${entry.expiresAt}`);
142
+ return;
143
+ }
144
+ // List
145
+ const active = config.suppressions.filter(isActive);
146
+ const expired = config.suppressions.filter((s) => !isActive(s));
147
+ if (format === "json") {
148
+ console.log(JSON.stringify({ active: active.length, expired: expired.length, suppressions: config.suppressions }, null, 2));
149
+ return;
150
+ }
151
+ console.log(`\n Finding Suppressions\n ─────────────────────────────`);
152
+ console.log(` Active: ${active.length} | Expired: ${expired.length}\n`);
153
+ if (active.length === 0 && expired.length === 0) {
154
+ console.log(" No suppressions configured.");
155
+ }
156
+ for (const s of active) {
157
+ console.log(` 🔇 ${s.ruleId} — ${s.reason}`);
158
+ console.log(` Author: ${s.author} | Scope: ${s.scope}${s.expiresAt ? ` | Expires: ${s.expiresAt}` : ""}`);
159
+ }
160
+ for (const s of expired) {
161
+ console.log(` ⏰ ${s.ruleId} (expired) — ${s.reason}`);
162
+ }
163
+ console.log();
164
+ }
165
+ //# sourceMappingURL=finding-suppress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-suppress.js","sourceRoot":"","sources":["../../src/commands/finding-suppress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Finding-timeline — Track finding trends across commits over time.
3
+ */
4
+ export declare function runFindingTimeline(argv: string[]): void;
5
+ //# sourceMappingURL=finding-timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-timeline.d.ts","sourceRoot":"","sources":["../../src/commands/finding-timeline.ts"],"names":[],"mappings":"AAAA;;GAEG;AAqDH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+HvD"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Finding-timeline — Track finding trends across commits over time.
3
+ */
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
+ import { execSync } from "child_process";
6
+ import { join, dirname } from "path";
7
+ // ─── Storage ────────────────────────────────────────────────────────────────
8
+ const TIMELINE_FILE = join(".judges", "finding-timeline.json");
9
+ function loadTimeline() {
10
+ if (!existsSync(TIMELINE_FILE))
11
+ return { version: "1.0.0", entries: [] };
12
+ try {
13
+ return JSON.parse(readFileSync(TIMELINE_FILE, "utf-8"));
14
+ }
15
+ catch {
16
+ return { version: "1.0.0", entries: [] };
17
+ }
18
+ }
19
+ function saveTimeline(store) {
20
+ mkdirSync(dirname(TIMELINE_FILE), { recursive: true });
21
+ writeFileSync(TIMELINE_FILE, JSON.stringify(store, null, 2), "utf-8");
22
+ }
23
+ function countBySeverity(findings) {
24
+ const counts = {};
25
+ for (const f of findings) {
26
+ const sev = f.severity || "unknown";
27
+ counts[sev] = (counts[sev] || 0) + 1;
28
+ }
29
+ return counts;
30
+ }
31
+ // ─── CLI ────────────────────────────────────────────────────────────────────
32
+ export function runFindingTimeline(argv) {
33
+ if (argv.includes("--help") || argv.includes("-h")) {
34
+ console.log(`
35
+ judges finding-timeline — Track finding trends across commits
36
+
37
+ Usage:
38
+ judges finding-timeline record --file verdict.json --label "v1.0"
39
+ judges finding-timeline show
40
+ judges finding-timeline show --last 10
41
+ judges finding-timeline clear
42
+
43
+ Subcommands:
44
+ record Record a data point from a verdict
45
+ show Show timeline with trend visualization
46
+ clear Clear all timeline data
47
+
48
+ Options:
49
+ --file <path> Verdict JSON (for record)
50
+ --label <text> Label for this data point
51
+ --commit <hash> Git commit hash (auto-detected if omitted)
52
+ --last <n> Show only last N entries
53
+ --format json JSON output
54
+ --help, -h Show this help
55
+
56
+ Tracks findings over time to show improvement trends.
57
+ Data is stored locally in .judges/finding-timeline.json.
58
+ `);
59
+ return;
60
+ }
61
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
62
+ const subcommand = argv.find((a) => ["record", "show", "clear"].includes(a)) || "show";
63
+ const store = loadTimeline();
64
+ if (subcommand === "record") {
65
+ const file = argv.find((_a, i) => argv[i - 1] === "--file");
66
+ if (!file || !existsSync(file)) {
67
+ console.error("Error: --file with a valid verdict JSON is required.");
68
+ process.exitCode = 1;
69
+ return;
70
+ }
71
+ let verdict;
72
+ try {
73
+ verdict = JSON.parse(readFileSync(file, "utf-8"));
74
+ }
75
+ catch {
76
+ console.error(`Error: Could not parse ${file}`);
77
+ process.exitCode = 1;
78
+ return;
79
+ }
80
+ const label = argv.find((_a, i) => argv[i - 1] === "--label") || `entry-${store.entries.length + 1}`;
81
+ let commit = argv.find((_a, i) => argv[i - 1] === "--commit") || "";
82
+ if (!commit) {
83
+ try {
84
+ commit = execSync("git rev-parse --short HEAD", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
85
+ }
86
+ catch {
87
+ commit = "unknown";
88
+ }
89
+ }
90
+ const findings = verdict.findings || [];
91
+ const entry = {
92
+ id: `tl-${Date.now().toString(36)}`,
93
+ timestamp: new Date().toISOString(),
94
+ commit,
95
+ label,
96
+ totalFindings: findings.length,
97
+ bySeverity: countBySeverity(findings),
98
+ score: verdict.overallScore || 0,
99
+ };
100
+ store.entries.push(entry);
101
+ saveTimeline(store);
102
+ console.log(`Recorded timeline entry '${label}' — ${findings.length} findings, score ${entry.score}`);
103
+ return;
104
+ }
105
+ if (subcommand === "clear") {
106
+ saveTimeline({ version: "1.0.0", entries: [] });
107
+ console.log("Timeline cleared.");
108
+ return;
109
+ }
110
+ // Show
111
+ const lastN = parseInt(argv.find((_a, i) => argv[i - 1] === "--last") || "0", 10);
112
+ const entries = lastN > 0 ? store.entries.slice(-lastN) : store.entries;
113
+ if (format === "json") {
114
+ console.log(JSON.stringify({ total: store.entries.length, shown: entries.length, entries }, null, 2));
115
+ return;
116
+ }
117
+ console.log(`\n Finding Timeline (${entries.length} entries)\n ═════════════════════════════`);
118
+ if (entries.length === 0) {
119
+ console.log(" No data. Record with: judges finding-timeline record --file verdict.json");
120
+ console.log();
121
+ return;
122
+ }
123
+ // ASCII chart
124
+ const maxFindings = Math.max(...entries.map((e) => e.totalFindings), 1);
125
+ const barWidth = 30;
126
+ for (const entry of entries) {
127
+ const barLen = Math.round((entry.totalFindings / maxFindings) * barWidth);
128
+ const bar = "█".repeat(barLen) + "░".repeat(barWidth - barLen);
129
+ const date = entry.timestamp.slice(0, 10);
130
+ console.log(` ${date} ${entry.label.padEnd(15)} ${bar} ${entry.totalFindings} findings (score: ${entry.score})`);
131
+ }
132
+ // Show trend
133
+ if (entries.length >= 2) {
134
+ const first = entries[0];
135
+ const last = entries[entries.length - 1];
136
+ const delta = last.totalFindings - first.totalFindings;
137
+ const scoreDelta = last.score - first.score;
138
+ console.log();
139
+ console.log(` Trend: findings ${delta >= 0 ? "+" : ""}${delta}, score ${scoreDelta >= 0 ? "+" : ""}${scoreDelta}`);
140
+ console.log(` ${delta <= 0 && scoreDelta >= 0 ? "📈 Improving" : delta > 0 ? "📉 Declining" : "➡️ Stable"}`);
141
+ }
142
+ console.log();
143
+ }
144
+ //# sourceMappingURL=finding-timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-timeline.js","sourceRoot":"","sources":["../../src/commands/finding-timeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Fix-verify — Re-run review on fixed code to confirm findings are resolved.
3
+ */
4
+ export declare function runFixVerify(argv: string[]): void;
5
+ //# sourceMappingURL=fix-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-verify.d.ts","sourceRoot":"","sources":["../../src/commands/fix-verify.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwDH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoGjD"}