@kevinrabun/judges 3.61.0 → 3.63.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.
- package/CHANGELOG.md +14 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/batch-review.d.ts +5 -0
- package/dist/commands/batch-review.d.ts.map +1 -0
- package/dist/commands/batch-review.js +181 -0
- package/dist/commands/batch-review.js.map +1 -0
- package/dist/commands/custom-rule.d.ts +5 -0
- package/dist/commands/custom-rule.d.ts.map +1 -0
- package/dist/commands/custom-rule.js +211 -0
- package/dist/commands/custom-rule.js.map +1 -0
- package/dist/commands/diff-review.d.ts +5 -0
- package/dist/commands/diff-review.d.ts.map +1 -0
- package/dist/commands/diff-review.js +191 -0
- package/dist/commands/diff-review.js.map +1 -0
- package/dist/commands/finding-group.d.ts +16 -0
- package/dist/commands/finding-group.d.ts.map +1 -0
- package/dist/commands/finding-group.js +165 -0
- package/dist/commands/finding-group.js.map +1 -0
- package/dist/commands/focus-area.d.ts +6 -0
- package/dist/commands/focus-area.d.ts.map +1 -0
- package/dist/commands/focus-area.js +193 -0
- package/dist/commands/focus-area.js.map +1 -0
- package/dist/commands/ignore-list.d.ts +19 -0
- package/dist/commands/ignore-list.d.ts.map +1 -0
- package/dist/commands/ignore-list.js +166 -0
- package/dist/commands/ignore-list.js.map +1 -0
- package/dist/commands/incremental-review.d.ts +5 -0
- package/dist/commands/incremental-review.d.ts.map +1 -0
- package/dist/commands/incremental-review.js +240 -0
- package/dist/commands/incremental-review.js.map +1 -0
- package/dist/commands/review-cache.d.ts +23 -0
- package/dist/commands/review-cache.d.ts.map +1 -0
- package/dist/commands/review-cache.js +135 -0
- package/dist/commands/review-cache.js.map +1 -0
- package/dist/commands/review-compare.d.ts +5 -0
- package/dist/commands/review-compare.d.ts.map +1 -0
- package/dist/commands/review-compare.js +201 -0
- package/dist/commands/review-compare.js.map +1 -0
- package/dist/commands/review-explain.d.ts +6 -0
- package/dist/commands/review-explain.d.ts.map +1 -0
- package/dist/commands/review-explain.js +195 -0
- package/dist/commands/review-explain.js.map +1 -0
- package/dist/commands/review-gate.d.ts +5 -0
- package/dist/commands/review-gate.d.ts.map +1 -0
- package/dist/commands/review-gate.js +213 -0
- package/dist/commands/review-gate.js.map +1 -0
- package/dist/commands/review-log.d.ts +23 -0
- package/dist/commands/review-log.d.ts.map +1 -0
- package/dist/commands/review-log.js +165 -0
- package/dist/commands/review-log.js.map +1 -0
- package/dist/commands/review-summary.d.ts +5 -0
- package/dist/commands/review-summary.d.ts.map +1 -0
- package/dist/commands/review-summary.js +175 -0
- package/dist/commands/review-summary.js.map +1 -0
- package/dist/commands/rule-test.d.ts +5 -0
- package/dist/commands/rule-test.d.ts.map +1 -0
- package/dist/commands/rule-test.js +216 -0
- package/dist/commands/rule-test.js.map +1 -0
- package/dist/commands/severity-tune.d.ts +5 -0
- package/dist/commands/severity-tune.d.ts.map +1 -0
- package/dist/commands/severity-tune.js +209 -0
- package/dist/commands/severity-tune.js.map +1 -0
- package/dist/commands/team-config.d.ts +5 -0
- package/dist/commands/team-config.d.ts.map +1 -0
- package/dist/commands/team-config.js +235 -0
- package/dist/commands/team-config.js.map +1 -0
- package/dist/commands/trend-report.d.ts +5 -0
- package/dist/commands/trend-report.d.ts.map +1 -0
- package/dist/commands/trend-report.js +149 -0
- package/dist/commands/trend-report.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental-review — Only review files changed since last review.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
|
|
5
|
+
import { createHash } from "crypto";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import { join, dirname } from "path";
|
|
8
|
+
// ─── Hash helper ────────────────────────────────────────────────────────────
|
|
9
|
+
function hashContent(content) {
|
|
10
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
11
|
+
}
|
|
12
|
+
// ─── Git helpers ────────────────────────────────────────────────────────────
|
|
13
|
+
function getGitChangedFiles(since) {
|
|
14
|
+
try {
|
|
15
|
+
const args = since ? `diff --name-only ${since}` : "diff --name-only HEAD";
|
|
16
|
+
const output = execSync(`git ${args}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
17
|
+
if (!output)
|
|
18
|
+
return [];
|
|
19
|
+
return output.split("\n").filter((f) => f.length > 0);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getGitStagedFiles() {
|
|
26
|
+
try {
|
|
27
|
+
const output = execSync("git diff --name-only --cached", {
|
|
28
|
+
encoding: "utf-8",
|
|
29
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
30
|
+
}).trim();
|
|
31
|
+
if (!output)
|
|
32
|
+
return [];
|
|
33
|
+
return output.split("\n").filter((f) => f.length > 0);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function getCurrentCommit() {
|
|
40
|
+
try {
|
|
41
|
+
return execSync("git rev-parse HEAD", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return "unknown";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// ─── State management ───────────────────────────────────────────────────────
|
|
48
|
+
const STATE_DIR = join(".judges", "incremental");
|
|
49
|
+
const STATE_FILE = join(STATE_DIR, "state.json");
|
|
50
|
+
function loadState() {
|
|
51
|
+
if (!existsSync(STATE_FILE))
|
|
52
|
+
return null;
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(readFileSync(STATE_FILE, "utf-8"));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function saveState(state) {
|
|
61
|
+
mkdirSync(dirname(STATE_FILE), { recursive: true });
|
|
62
|
+
writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
|
|
63
|
+
}
|
|
64
|
+
// ─── Changed file detection ────────────────────────────────────────────────
|
|
65
|
+
function detectChangedFiles(state, allFiles) {
|
|
66
|
+
if (!state) {
|
|
67
|
+
return { changed: allFiles, unchanged: [], newFiles: allFiles };
|
|
68
|
+
}
|
|
69
|
+
const changed = [];
|
|
70
|
+
const unchanged = [];
|
|
71
|
+
const newFiles = [];
|
|
72
|
+
for (const file of allFiles) {
|
|
73
|
+
if (!existsSync(file))
|
|
74
|
+
continue;
|
|
75
|
+
const previousHash = state.fileHashes[file];
|
|
76
|
+
if (!previousHash) {
|
|
77
|
+
newFiles.push(file);
|
|
78
|
+
changed.push(file);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const content = readFileSync(file, "utf-8");
|
|
83
|
+
const currentHash = hashContent(content);
|
|
84
|
+
if (currentHash !== previousHash) {
|
|
85
|
+
changed.push(file);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
unchanged.push(file);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
changed.push(file);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { changed, unchanged, newFiles };
|
|
96
|
+
}
|
|
97
|
+
// ─── Update state with current hashes ───────────────────────────────────────
|
|
98
|
+
function buildStateFromFiles(files) {
|
|
99
|
+
const fileHashes = {};
|
|
100
|
+
for (const file of files) {
|
|
101
|
+
if (!existsSync(file))
|
|
102
|
+
continue;
|
|
103
|
+
try {
|
|
104
|
+
const content = readFileSync(file, "utf-8");
|
|
105
|
+
fileHashes[file] = hashContent(content);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// skip unreadable files
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
lastReviewTimestamp: new Date().toISOString(),
|
|
113
|
+
lastCommit: getCurrentCommit(),
|
|
114
|
+
fileHashes,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
118
|
+
export function runIncrementalReview(argv) {
|
|
119
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
120
|
+
console.log(`
|
|
121
|
+
judges incremental-review — Only review files changed since last review
|
|
122
|
+
|
|
123
|
+
Usage:
|
|
124
|
+
judges incremental-review Show changed files since last review
|
|
125
|
+
judges incremental-review --git Use git diff to detect changes
|
|
126
|
+
judges incremental-review --save Save current state as baseline
|
|
127
|
+
judges incremental-review --reset Reset incremental state
|
|
128
|
+
judges incremental-review --format json JSON output
|
|
129
|
+
|
|
130
|
+
Subcommands:
|
|
131
|
+
status Show what would be reviewed (default)
|
|
132
|
+
save Save current file state as baseline
|
|
133
|
+
reset Clear incremental state
|
|
134
|
+
|
|
135
|
+
Options:
|
|
136
|
+
--git Include git-tracked changed files
|
|
137
|
+
--staged Include only staged files
|
|
138
|
+
--since <commit> Git diff since specific commit
|
|
139
|
+
--files <glob> Only consider files matching pattern
|
|
140
|
+
--save Save state after showing changes
|
|
141
|
+
--reset Clear incremental state
|
|
142
|
+
--format json JSON output
|
|
143
|
+
--help, -h Show this help
|
|
144
|
+
|
|
145
|
+
Uses content hashing and git status to skip unchanged files.
|
|
146
|
+
Run after a review to save state, then on next run only changed
|
|
147
|
+
files are flagged for review.
|
|
148
|
+
`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
152
|
+
const useGit = argv.includes("--git");
|
|
153
|
+
const useStaged = argv.includes("--staged");
|
|
154
|
+
const sinceCommit = argv.find((_a, i) => argv[i - 1] === "--since");
|
|
155
|
+
const doSave = argv.includes("--save");
|
|
156
|
+
const doReset = argv.includes("--reset");
|
|
157
|
+
if (doReset) {
|
|
158
|
+
if (existsSync(STATE_FILE)) {
|
|
159
|
+
writeFileSync(STATE_FILE, "{}", "utf-8");
|
|
160
|
+
console.log("Incremental state reset.");
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
console.log("No incremental state found.");
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
// Collect files to consider
|
|
168
|
+
let targetFiles;
|
|
169
|
+
if (useStaged) {
|
|
170
|
+
targetFiles = getGitStagedFiles();
|
|
171
|
+
}
|
|
172
|
+
else if (useGit || sinceCommit) {
|
|
173
|
+
targetFiles = getGitChangedFiles(sinceCommit);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// No git flag — use state-based detection
|
|
177
|
+
// Collect all tracked files from git
|
|
178
|
+
try {
|
|
179
|
+
const output = execSync("git ls-files", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
180
|
+
targetFiles = output
|
|
181
|
+
.split("\n")
|
|
182
|
+
.filter((f) => f.length > 0 && /\.(ts|js|py|go|rs|java|cs|cpp|c|rb|php|tsx|jsx)$/.test(f));
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
console.error("Error: Cannot list files. Are you in a git repository?");
|
|
186
|
+
process.exitCode = 1;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const state = loadState();
|
|
191
|
+
const { changed, unchanged, newFiles } = detectChangedFiles(state, targetFiles);
|
|
192
|
+
if (doSave) {
|
|
193
|
+
const newState = buildStateFromFiles(targetFiles);
|
|
194
|
+
saveState(newState);
|
|
195
|
+
}
|
|
196
|
+
if (format === "json") {
|
|
197
|
+
console.log(JSON.stringify({
|
|
198
|
+
hasState: state !== null,
|
|
199
|
+
lastReview: state?.lastReviewTimestamp || null,
|
|
200
|
+
lastCommit: state?.lastCommit || null,
|
|
201
|
+
totalFiles: targetFiles.length,
|
|
202
|
+
changedFiles: changed.length,
|
|
203
|
+
unchangedFiles: unchanged.length,
|
|
204
|
+
newFiles: newFiles.length,
|
|
205
|
+
changed,
|
|
206
|
+
saved: doSave,
|
|
207
|
+
}, null, 2));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
console.log(`\n Incremental Review Status\n ─────────────────────────────`);
|
|
211
|
+
if (state) {
|
|
212
|
+
console.log(` Last review: ${state.lastReviewTimestamp}`);
|
|
213
|
+
console.log(` Last commit: ${state.lastCommit.slice(0, 8)}`);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
console.log(` No previous state — all files will be reviewed`);
|
|
217
|
+
}
|
|
218
|
+
console.log(`\n Total files: ${targetFiles.length}`);
|
|
219
|
+
console.log(` Changed: ${changed.length}`);
|
|
220
|
+
console.log(` Unchanged: ${unchanged.length}`);
|
|
221
|
+
console.log(` New: ${newFiles.length}`);
|
|
222
|
+
if (changed.length > 0) {
|
|
223
|
+
console.log(`\n Files to review:`);
|
|
224
|
+
for (const f of changed.slice(0, 30)) {
|
|
225
|
+
const marker = newFiles.includes(f) ? "🆕" : "📝";
|
|
226
|
+
console.log(` ${marker} ${f}`);
|
|
227
|
+
}
|
|
228
|
+
if (changed.length > 30) {
|
|
229
|
+
console.log(` ... and ${changed.length - 30} more`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
console.log(`\n ✅ No files have changed since last review.`);
|
|
234
|
+
}
|
|
235
|
+
if (doSave) {
|
|
236
|
+
console.log(`\n 💾 State saved.`);
|
|
237
|
+
}
|
|
238
|
+
console.log();
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=incremental-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"incremental-review.js","sourceRoot":"","sources":["../../src/commands/incremental-review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-cache — Cache review results to avoid re-analyzing unchanged files.
|
|
3
|
+
*/
|
|
4
|
+
interface CachedResult {
|
|
5
|
+
fileHash: string;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
findingCount: number;
|
|
8
|
+
findings: {
|
|
9
|
+
pattern: string;
|
|
10
|
+
severity: string;
|
|
11
|
+
line: number;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
declare function hashFile(content: string): string;
|
|
15
|
+
declare function getCached(filePath: string, content: string): CachedResult | null;
|
|
16
|
+
declare function setCached(filePath: string, content: string, findings: {
|
|
17
|
+
pattern: string;
|
|
18
|
+
severity: string;
|
|
19
|
+
line: number;
|
|
20
|
+
}[]): void;
|
|
21
|
+
export declare function runReviewCache(argv: string[]): void;
|
|
22
|
+
export { getCached, setCached, hashFile };
|
|
23
|
+
//# sourceMappingURL=review-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-cache.d.ts","sourceRoot":"","sources":["../../src/commands/review-cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjE;AAiBD,iBAAS,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzC;AAMD,iBAAS,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAezE;AAED,iBAAS,SAAS,CAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAC9D,IAAI,CAaN;AA2CD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAgDnD;AAGD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-cache — Cache review results to avoid re-analyzing unchanged files.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { createHash } from "crypto";
|
|
7
|
+
// ─── Cache operations ──────────────────────────────────────────────────────
|
|
8
|
+
function getCacheDir() {
|
|
9
|
+
return join(".", ".judges", "review-cache");
|
|
10
|
+
}
|
|
11
|
+
function hashFile(content) {
|
|
12
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
13
|
+
}
|
|
14
|
+
function cacheKey(filePath) {
|
|
15
|
+
return createHash("sha256").update(filePath).digest("hex").slice(0, 16);
|
|
16
|
+
}
|
|
17
|
+
function getCached(filePath, content) {
|
|
18
|
+
const dir = getCacheDir();
|
|
19
|
+
const key = cacheKey(filePath);
|
|
20
|
+
const cachePath = join(dir, `${key}.json`);
|
|
21
|
+
if (!existsSync(cachePath))
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
const cached = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
25
|
+
const currentHash = hashFile(content);
|
|
26
|
+
if (cached.fileHash === currentHash)
|
|
27
|
+
return cached;
|
|
28
|
+
return null; // File changed, cache invalid
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function setCached(filePath, content, findings) {
|
|
35
|
+
const dir = getCacheDir();
|
|
36
|
+
if (!existsSync(dir))
|
|
37
|
+
mkdirSync(dir, { recursive: true });
|
|
38
|
+
const key = cacheKey(filePath);
|
|
39
|
+
const result = {
|
|
40
|
+
fileHash: hashFile(content),
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
findingCount: findings.length,
|
|
43
|
+
findings,
|
|
44
|
+
};
|
|
45
|
+
writeFileSync(join(dir, `${key}.json`), JSON.stringify(result), "utf-8");
|
|
46
|
+
}
|
|
47
|
+
function getCacheStats() {
|
|
48
|
+
const dir = getCacheDir();
|
|
49
|
+
if (!existsSync(dir))
|
|
50
|
+
return { entries: 0, hits: 0, misses: 0, sizeBytes: 0, oldestEntry: "", newestEntry: "" };
|
|
51
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
52
|
+
let totalSize = 0;
|
|
53
|
+
let oldest = "";
|
|
54
|
+
let newest = "";
|
|
55
|
+
for (const f of files) {
|
|
56
|
+
try {
|
|
57
|
+
const content = readFileSync(join(dir, f), "utf-8");
|
|
58
|
+
totalSize += content.length;
|
|
59
|
+
const parsed = JSON.parse(content);
|
|
60
|
+
if (!oldest || parsed.timestamp < oldest)
|
|
61
|
+
oldest = parsed.timestamp;
|
|
62
|
+
if (!newest || parsed.timestamp > newest)
|
|
63
|
+
newest = parsed.timestamp;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// skip
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { entries: files.length, hits: 0, misses: 0, sizeBytes: totalSize, oldestEntry: oldest, newestEntry: newest };
|
|
70
|
+
}
|
|
71
|
+
function clearCache() {
|
|
72
|
+
const dir = getCacheDir();
|
|
73
|
+
if (!existsSync(dir))
|
|
74
|
+
return 0;
|
|
75
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
76
|
+
for (const f of files) {
|
|
77
|
+
try {
|
|
78
|
+
unlinkSync(join(dir, f));
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// skip
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return files.length;
|
|
85
|
+
}
|
|
86
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
87
|
+
export function runReviewCache(argv) {
|
|
88
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
89
|
+
console.log(`
|
|
90
|
+
judges review-cache — Manage review result cache
|
|
91
|
+
|
|
92
|
+
Usage:
|
|
93
|
+
judges review-cache stats Show cache statistics
|
|
94
|
+
judges review-cache clear Clear all cached results
|
|
95
|
+
judges review-cache --format json JSON output
|
|
96
|
+
|
|
97
|
+
Subcommands:
|
|
98
|
+
stats Show cache statistics
|
|
99
|
+
clear Clear all cached results
|
|
100
|
+
|
|
101
|
+
Options:
|
|
102
|
+
--format json JSON output
|
|
103
|
+
--help, -h Show this help
|
|
104
|
+
|
|
105
|
+
Review results are cached in .judges/review-cache/ based on file content
|
|
106
|
+
hashes. When a file hasn't changed, cached results are reused for faster
|
|
107
|
+
subsequent reviews.
|
|
108
|
+
`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
112
|
+
const subcommand = argv.find((a) => !a.startsWith("-") && a !== "review-cache") || "stats";
|
|
113
|
+
if (subcommand === "clear") {
|
|
114
|
+
const count = clearCache();
|
|
115
|
+
console.log(`Cleared ${count} cached review result(s).`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
// Stats
|
|
119
|
+
const stats = getCacheStats();
|
|
120
|
+
if (format === "json") {
|
|
121
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
console.log(`\n Review Cache\n ─────────────────────────────`);
|
|
125
|
+
console.log(` Entries: ${stats.entries}`);
|
|
126
|
+
console.log(` Size: ${Math.round(stats.sizeBytes / 1024)} KB`);
|
|
127
|
+
if (stats.oldestEntry)
|
|
128
|
+
console.log(` Oldest: ${stats.oldestEntry.slice(0, 10)}`);
|
|
129
|
+
if (stats.newestEntry)
|
|
130
|
+
console.log(` Newest: ${stats.newestEntry.slice(0, 10)}`);
|
|
131
|
+
console.log();
|
|
132
|
+
}
|
|
133
|
+
// Export helpers for use by other commands
|
|
134
|
+
export { getCached, setCached, hashFile };
|
|
135
|
+
//# sourceMappingURL=review-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-cache.js","sourceRoot":"","sources":["../../src/commands/review-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-compare.d.ts","sourceRoot":"","sources":["../../src/commands/review-compare.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0FH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAuJrD"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-compare — Compare two review runs to measure improvement.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
7
|
+
function getSnapshotsDir() {
|
|
8
|
+
return join(".", ".judges", "snapshots");
|
|
9
|
+
}
|
|
10
|
+
function loadSnapshot(id) {
|
|
11
|
+
const dir = getSnapshotsDir();
|
|
12
|
+
const filePath = join(dir, `${id}.json`);
|
|
13
|
+
if (!existsSync(filePath))
|
|
14
|
+
return null;
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function saveSnapshot(snapshot) {
|
|
23
|
+
const dir = getSnapshotsDir();
|
|
24
|
+
if (!existsSync(dir))
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
writeFileSync(join(dir, `${snapshot.id}.json`), JSON.stringify(snapshot, null, 2), "utf-8");
|
|
27
|
+
}
|
|
28
|
+
function listSnapshots() {
|
|
29
|
+
const dir = getSnapshotsDir();
|
|
30
|
+
if (!existsSync(dir))
|
|
31
|
+
return [];
|
|
32
|
+
try {
|
|
33
|
+
return readdirSync(dir)
|
|
34
|
+
.filter((f) => f.endsWith(".json"))
|
|
35
|
+
.map((f) => f.replace(".json", ""));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function compareSnapshots(before, after) {
|
|
42
|
+
// Build finding signatures for comparison
|
|
43
|
+
const beforeSigs = new Set(before.findings.map((f) => `${f.pattern}:${f.file}:${f.line}`));
|
|
44
|
+
const afterSigs = new Set(after.findings.map((f) => `${f.pattern}:${f.file}:${f.line}`));
|
|
45
|
+
const newFindings = after.findings
|
|
46
|
+
.filter((f) => !beforeSigs.has(`${f.pattern}:${f.file}:${f.line}`))
|
|
47
|
+
.map((f) => ({ pattern: f.pattern, severity: f.severity, file: f.file }));
|
|
48
|
+
const fixedFindings = before.findings
|
|
49
|
+
.filter((f) => !afterSigs.has(`${f.pattern}:${f.file}:${f.line}`))
|
|
50
|
+
.map((f) => ({ pattern: f.pattern, severity: f.severity, file: f.file }));
|
|
51
|
+
const persisting = after.counts.total - newFindings.length;
|
|
52
|
+
const improvement = before.counts.total - after.counts.total;
|
|
53
|
+
const improvementPercent = before.counts.total > 0 ? Math.round((improvement / before.counts.total) * 100) : 0;
|
|
54
|
+
return {
|
|
55
|
+
before: { id: before.id, label: before.label, total: before.counts.total },
|
|
56
|
+
after: { id: after.id, label: after.label, total: after.counts.total },
|
|
57
|
+
improvement,
|
|
58
|
+
improvementPercent,
|
|
59
|
+
newFindings,
|
|
60
|
+
fixedFindings,
|
|
61
|
+
persistingFindings: Math.max(0, persisting),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
65
|
+
export function runReviewCompare(argv) {
|
|
66
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
67
|
+
console.log(`
|
|
68
|
+
judges review-compare — Compare two review runs to measure improvement
|
|
69
|
+
|
|
70
|
+
Usage:
|
|
71
|
+
judges review-compare save --id run1 --label "Before refactor" --findings findings.json
|
|
72
|
+
judges review-compare diff --before run1 --after run2
|
|
73
|
+
judges review-compare list List saved snapshots
|
|
74
|
+
judges review-compare --format json JSON output
|
|
75
|
+
|
|
76
|
+
Subcommands:
|
|
77
|
+
save Save a review snapshot for later comparison
|
|
78
|
+
diff Compare two saved snapshots
|
|
79
|
+
list List all saved snapshots
|
|
80
|
+
|
|
81
|
+
Save Options:
|
|
82
|
+
--id <name> Snapshot identifier
|
|
83
|
+
--label <text> Human-readable label
|
|
84
|
+
--findings <path> JSON file with findings array
|
|
85
|
+
|
|
86
|
+
Diff Options:
|
|
87
|
+
--before <id> First snapshot ID
|
|
88
|
+
--after <id> Second snapshot ID
|
|
89
|
+
|
|
90
|
+
Snapshots are stored locally in .judges/snapshots/.
|
|
91
|
+
`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
95
|
+
const subcommand = argv.find((a) => !a.startsWith("-") && a !== "review-compare") || "list";
|
|
96
|
+
if (subcommand === "save") {
|
|
97
|
+
const id = argv.find((_a, i) => argv[i - 1] === "--id");
|
|
98
|
+
const label = argv.find((_a, i) => argv[i - 1] === "--label") || id || "unlabeled";
|
|
99
|
+
const findingsPath = argv.find((_a, i) => argv[i - 1] === "--findings");
|
|
100
|
+
if (!id) {
|
|
101
|
+
console.error("Error: --id is required for save.");
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
let findings = [];
|
|
106
|
+
if (findingsPath && existsSync(findingsPath)) {
|
|
107
|
+
try {
|
|
108
|
+
const raw = JSON.parse(readFileSync(findingsPath, "utf-8"));
|
|
109
|
+
findings = Array.isArray(raw) ? raw : Array.isArray(raw.findings) ? raw.findings : [];
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
console.error("Error: Cannot parse findings file.");
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const counts = { critical: 0, high: 0, medium: 0, low: 0, total: findings.length };
|
|
118
|
+
for (const f of findings) {
|
|
119
|
+
if (f.severity === "critical")
|
|
120
|
+
counts.critical++;
|
|
121
|
+
else if (f.severity === "high")
|
|
122
|
+
counts.high++;
|
|
123
|
+
else if (f.severity === "medium")
|
|
124
|
+
counts.medium++;
|
|
125
|
+
else
|
|
126
|
+
counts.low++;
|
|
127
|
+
}
|
|
128
|
+
const snapshot = { id, timestamp: new Date().toISOString(), label, findings, counts };
|
|
129
|
+
saveSnapshot(snapshot);
|
|
130
|
+
console.log(`Saved snapshot '${id}' with ${findings.length} findings.`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (subcommand === "list") {
|
|
134
|
+
const ids = listSnapshots();
|
|
135
|
+
if (ids.length === 0) {
|
|
136
|
+
console.log("No snapshots saved. Use 'judges review-compare save' to create one.");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
console.log(`\n Saved Snapshots:\n ─────────────────────────────`);
|
|
140
|
+
for (const id of ids) {
|
|
141
|
+
const snap = loadSnapshot(id);
|
|
142
|
+
if (snap) {
|
|
143
|
+
console.log(` ${snap.id} — ${snap.label} (${snap.counts.total} findings, ${snap.timestamp.slice(0, 10)})`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
console.log();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (subcommand === "diff") {
|
|
150
|
+
const beforeId = argv.find((_a, i) => argv[i - 1] === "--before");
|
|
151
|
+
const afterId = argv.find((_a, i) => argv[i - 1] === "--after");
|
|
152
|
+
if (!beforeId || !afterId) {
|
|
153
|
+
console.error("Error: Both --before and --after snapshot IDs are required.");
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const before = loadSnapshot(beforeId);
|
|
158
|
+
const after = loadSnapshot(afterId);
|
|
159
|
+
if (!before) {
|
|
160
|
+
console.error(`Error: Snapshot '${beforeId}' not found.`);
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (!after) {
|
|
165
|
+
console.error(`Error: Snapshot '${afterId}' not found.`);
|
|
166
|
+
process.exitCode = 1;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const result = compareSnapshots(before, after);
|
|
170
|
+
if (format === "json") {
|
|
171
|
+
console.log(JSON.stringify(result, null, 2));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const trendIcon = result.improvement > 0 ? "📉" : result.improvement < 0 ? "📈" : "➡️";
|
|
175
|
+
console.log(`\n Review Comparison\n ─────────────────────────────`);
|
|
176
|
+
console.log(` Before: ${result.before.label} (${result.before.total} findings)`);
|
|
177
|
+
console.log(` After: ${result.after.label} (${result.after.total} findings)`);
|
|
178
|
+
console.log(` ${trendIcon} Change: ${result.improvement > 0 ? "-" : "+"}${Math.abs(result.improvement)} findings (${result.improvementPercent}%)`);
|
|
179
|
+
console.log(` Fixed: ${result.fixedFindings.length} New: ${result.newFindings.length} Persisting: ${result.persistingFindings}`);
|
|
180
|
+
if (result.fixedFindings.length > 0) {
|
|
181
|
+
console.log("\n Fixed findings:");
|
|
182
|
+
for (const f of result.fixedFindings.slice(0, 10)) {
|
|
183
|
+
console.log(` ✅ [${f.severity}] ${f.pattern} — ${f.file}`);
|
|
184
|
+
}
|
|
185
|
+
if (result.fixedFindings.length > 10)
|
|
186
|
+
console.log(` ... +${result.fixedFindings.length - 10} more`);
|
|
187
|
+
}
|
|
188
|
+
if (result.newFindings.length > 0) {
|
|
189
|
+
console.log("\n New findings:");
|
|
190
|
+
for (const f of result.newFindings.slice(0, 10)) {
|
|
191
|
+
console.log(` ❌ [${f.severity}] ${f.pattern} — ${f.file}`);
|
|
192
|
+
}
|
|
193
|
+
if (result.newFindings.length > 10)
|
|
194
|
+
console.log(` ... +${result.newFindings.length - 10} more`);
|
|
195
|
+
}
|
|
196
|
+
console.log();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
console.log("Unknown subcommand. Use --help for usage.");
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=review-compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-compare.js","sourceRoot":"","sources":["../../src/commands/review-compare.ts"],"names":[],"mappings":"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"}
|