@kevinrabun/judges 3.62.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +56 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/finding-group.d.ts +16 -0
  6. package/dist/commands/finding-group.d.ts.map +1 -0
  7. package/dist/commands/finding-group.js +165 -0
  8. package/dist/commands/finding-group.js.map +1 -0
  9. package/dist/commands/ignore-list.d.ts +19 -0
  10. package/dist/commands/ignore-list.d.ts.map +1 -0
  11. package/dist/commands/ignore-list.js +166 -0
  12. package/dist/commands/ignore-list.js.map +1 -0
  13. package/dist/commands/incremental-review.d.ts +5 -0
  14. package/dist/commands/incremental-review.d.ts.map +1 -0
  15. package/dist/commands/incremental-review.js +240 -0
  16. package/dist/commands/incremental-review.js.map +1 -0
  17. package/dist/commands/review-cache.d.ts +23 -0
  18. package/dist/commands/review-cache.d.ts.map +1 -0
  19. package/dist/commands/review-cache.js +135 -0
  20. package/dist/commands/review-cache.js.map +1 -0
  21. package/dist/commands/review-log.d.ts +23 -0
  22. package/dist/commands/review-log.d.ts.map +1 -0
  23. package/dist/commands/review-log.js +165 -0
  24. package/dist/commands/review-log.js.map +1 -0
  25. package/dist/commands/review-summary.d.ts +5 -0
  26. package/dist/commands/review-summary.d.ts.map +1 -0
  27. package/dist/commands/review-summary.js +175 -0
  28. package/dist/commands/review-summary.js.map +1 -0
  29. package/dist/commands/rule-test.d.ts +5 -0
  30. package/dist/commands/rule-test.d.ts.map +1 -0
  31. package/dist/commands/rule-test.js +216 -0
  32. package/dist/commands/rule-test.js.map +1 -0
  33. package/dist/commands/team-config.d.ts +5 -0
  34. package/dist/commands/team-config.d.ts.map +1 -0
  35. package/dist/commands/team-config.js +235 -0
  36. package/dist/commands/team-config.js.map +1 -0
  37. package/package.json +1 -1
  38. package/server.json +2 -2
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Finding-group — Group related findings into actionable clusters.
3
+ */
4
+ import { readFileSync } from "fs";
5
+ // ─── Grouping strategies ────────────────────────────────────────────────────
6
+ function groupByRule(findings) {
7
+ const groups = new Map();
8
+ for (const f of findings) {
9
+ const key = f.ruleId || "unknown";
10
+ const existing = groups.get(key) || [];
11
+ existing.push(f);
12
+ groups.set(key, existing);
13
+ }
14
+ return groups;
15
+ }
16
+ function groupBySeverity(findings) {
17
+ const groups = new Map();
18
+ for (const f of findings) {
19
+ const key = f.severity || "unknown";
20
+ const existing = groups.get(key) || [];
21
+ existing.push(f);
22
+ groups.set(key, existing);
23
+ }
24
+ return groups;
25
+ }
26
+ function groupByCategory(findings) {
27
+ const groups = new Map();
28
+ for (const f of findings) {
29
+ const ruleId = f.ruleId || "";
30
+ let category = "other";
31
+ if (/sql|inject|xss|csrf|traversal|auth|secret|crypt/i.test(ruleId))
32
+ category = "security";
33
+ else if (/perf|cache|memory|leak|optim/i.test(ruleId))
34
+ category = "performance";
35
+ else if (/error|exception|throw|catch/i.test(ruleId))
36
+ category = "error-handling";
37
+ else if (/doc|comment|jsdoc|readme/i.test(ruleId))
38
+ category = "documentation";
39
+ else if (/test|assert|mock|coverage/i.test(ruleId))
40
+ category = "testing";
41
+ else if (/lint|format|naming|style/i.test(ruleId))
42
+ category = "code-style";
43
+ const existing = groups.get(category) || [];
44
+ existing.push(f);
45
+ groups.set(category, existing);
46
+ }
47
+ return groups;
48
+ }
49
+ // ─── Build clusters ─────────────────────────────────────────────────────────
50
+ function buildClusters(groups, strategy) {
51
+ const clusters = [];
52
+ let idx = 0;
53
+ for (const [key, findings] of groups) {
54
+ idx++;
55
+ const severities = {};
56
+ for (const f of findings) {
57
+ const s = f.severity || "unknown";
58
+ severities[s] = (severities[s] || 0) + 1;
59
+ }
60
+ const topFinding = findings.sort((a, b) => {
61
+ const order = { critical: 0, high: 1, medium: 2, low: 3 };
62
+ return (order[a.severity] ?? 4) - (order[b.severity] ?? 4);
63
+ })[0];
64
+ clusters.push({
65
+ id: `${strategy}-${idx}`,
66
+ label: key,
67
+ pattern: strategy,
68
+ count: findings.length,
69
+ severities,
70
+ findings,
71
+ files: [],
72
+ recommendation: topFinding?.recommendation || "Review grouped findings",
73
+ });
74
+ }
75
+ return clusters.sort((a, b) => b.count - a.count);
76
+ }
77
+ // ─── CLI ────────────────────────────────────────────────────────────────────
78
+ export function runFindingGroup(argv) {
79
+ if (argv.includes("--help") || argv.includes("-h")) {
80
+ console.log(`
81
+ judges finding-group — Group related findings into actionable clusters
82
+
83
+ Usage:
84
+ judges finding-group --input findings.json
85
+ judges finding-group --input findings.json --strategy severity
86
+ judges finding-group --input findings.json --format json
87
+
88
+ Options:
89
+ --input <file> JSON file with findings array (required)
90
+ --strategy <type> Grouping strategy: rule, severity, category (default: rule)
91
+ --format json JSON output
92
+ --min-count <n> Minimum findings to form a cluster (default: 1)
93
+ --help, -h Show this help
94
+
95
+ Strategies:
96
+ rule Group by rule ID (most specific)
97
+ severity Group by severity level
98
+ category Group by inferred category (security, performance, etc.)
99
+
100
+ Groups related findings to help you fix issues systematically rather
101
+ than one by one.
102
+ `);
103
+ return;
104
+ }
105
+ const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
106
+ const strategy = argv.find((_a, i) => argv[i - 1] === "--strategy") || "rule";
107
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
108
+ const minCountStr = argv.find((_a, i) => argv[i - 1] === "--min-count");
109
+ const minCount = minCountStr ? parseInt(minCountStr, 10) : 1;
110
+ if (!inputPath) {
111
+ console.error("Error: --input is required. Provide a JSON file with findings.");
112
+ process.exitCode = 1;
113
+ return;
114
+ }
115
+ let findings;
116
+ try {
117
+ const raw = readFileSync(inputPath, "utf-8");
118
+ const parsed = JSON.parse(raw);
119
+ findings = Array.isArray(parsed) ? parsed : parsed.findings || [];
120
+ }
121
+ catch {
122
+ console.error(`Error: Cannot read or parse ${inputPath}`);
123
+ process.exitCode = 1;
124
+ return;
125
+ }
126
+ if (findings.length === 0) {
127
+ console.log("No findings to group.");
128
+ return;
129
+ }
130
+ let groups;
131
+ switch (strategy) {
132
+ case "severity":
133
+ groups = groupBySeverity(findings);
134
+ break;
135
+ case "category":
136
+ groups = groupByCategory(findings);
137
+ break;
138
+ default:
139
+ groups = groupByRule(findings);
140
+ }
141
+ let clusters = buildClusters(groups, strategy);
142
+ clusters = clusters.filter((c) => c.count >= minCount);
143
+ if (format === "json") {
144
+ console.log(JSON.stringify({
145
+ strategy,
146
+ totalFindings: findings.length,
147
+ clusterCount: clusters.length,
148
+ clusters: clusters.map((c) => ({ ...c, findings: undefined })),
149
+ }, null, 2));
150
+ return;
151
+ }
152
+ console.log(`\n Finding Groups (strategy: ${strategy})\n ─────────────────────────────`);
153
+ console.log(` Total findings: ${findings.length}`);
154
+ console.log(` Clusters: ${clusters.length}\n`);
155
+ for (const cluster of clusters) {
156
+ const severitySummary = Object.entries(cluster.severities)
157
+ .map(([s, n]) => `${n} ${s}`)
158
+ .join(", ");
159
+ console.log(` [${cluster.id}] ${cluster.label} — ${cluster.count} finding(s)`);
160
+ console.log(` Severities: ${severitySummary}`);
161
+ console.log(` Recommendation: ${cluster.recommendation}`);
162
+ console.log();
163
+ }
164
+ }
165
+ //# sourceMappingURL=finding-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-group.js","sourceRoot":"","sources":["../../src/commands/finding-group.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Ignore-list — Configurable ignore patterns for files, directories, and rules.
3
+ */
4
+ interface IgnoreConfig {
5
+ version: string;
6
+ filePatterns: string[];
7
+ directoryPatterns: string[];
8
+ ruleIgnores: string[];
9
+ inlineSuppressions: boolean;
10
+ }
11
+ declare function shouldIgnoreFile(filePath: string, baseDir: string, config: IgnoreConfig): boolean;
12
+ declare function shouldIgnoreRule(ruleId: string, config: IgnoreConfig): boolean;
13
+ declare function filterFiles(files: string[], baseDir: string, config: IgnoreConfig): {
14
+ effective: string[];
15
+ ignored: string[];
16
+ };
17
+ export declare function runIgnoreList(argv: string[]): void;
18
+ export { shouldIgnoreFile, shouldIgnoreRule, filterFiles, IgnoreConfig };
19
+ //# sourceMappingURL=ignore-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ignore-list.d.ts","sourceRoot":"","sources":["../../src/commands/ignore-list.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2BH,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAgCD,iBAAS,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAc1F;AAED,iBAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAEvE;AAED,iBAAS,WAAW,CAClB,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,YAAY,GACnB;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAa5C;AAID,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8FlD;AAGD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Ignore-list — Configurable ignore patterns for files, directories, and rules.
3
+ */
4
+ import { readFileSync, writeFileSync, existsSync } from "fs";
5
+ import { join, relative } from "path";
6
+ // ─── Simple glob matcher ────────────────────────────────────────────────────
7
+ function globToRegex(pattern) {
8
+ const regex = pattern
9
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&") // Escape special regex chars (but not * and ?)
10
+ .replace(/\*\*/g, "{{GLOBSTAR}}") // Temp placeholder for **
11
+ .replace(/\*/g, "[^/]*") // * matches anything except /
12
+ .replace(/\?/g, "[^/]") // ? matches single char except /
13
+ .replace(/\{\{GLOBSTAR\}\}/g, ".*"); // ** matches everything including /
14
+ return new RegExp(`^${regex}$`);
15
+ }
16
+ function matchGlob(filePath, pattern) {
17
+ try {
18
+ return globToRegex(pattern).test(filePath);
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ // ─── Default config ────────────────────────────────────────────────────────
25
+ function defaultIgnoreConfig() {
26
+ return {
27
+ version: "1.0.0",
28
+ filePatterns: [
29
+ "**/*.test.ts",
30
+ "**/*.test.js",
31
+ "**/*.spec.ts",
32
+ "**/*.spec.js",
33
+ "**/*.d.ts",
34
+ "**/fixtures/**",
35
+ "**/testdata/**",
36
+ ],
37
+ directoryPatterns: ["node_modules", "dist", "build", "coverage", ".git", ".next", "__pycache__", "vendor"],
38
+ ruleIgnores: [],
39
+ inlineSuppressions: true,
40
+ };
41
+ }
42
+ // ─── Matching engine ───────────────────────────────────────────────────────
43
+ function shouldIgnoreFile(filePath, baseDir, config) {
44
+ const rel = relative(baseDir, filePath).replace(/\\/g, "/");
45
+ for (const pattern of config.filePatterns) {
46
+ if (matchGlob(rel, pattern))
47
+ return true;
48
+ }
49
+ // Check directory patterns
50
+ const parts = rel.split("/");
51
+ for (const dir of config.directoryPatterns) {
52
+ if (parts.includes(dir))
53
+ return true;
54
+ }
55
+ return false;
56
+ }
57
+ function shouldIgnoreRule(ruleId, config) {
58
+ return config.ruleIgnores.includes(ruleId);
59
+ }
60
+ function filterFiles(files, baseDir, config) {
61
+ const effective = [];
62
+ const ignored = [];
63
+ for (const f of files) {
64
+ if (shouldIgnoreFile(f, baseDir, config)) {
65
+ ignored.push(f);
66
+ }
67
+ else {
68
+ effective.push(f);
69
+ }
70
+ }
71
+ return { effective, ignored };
72
+ }
73
+ // ─── CLI ────────────────────────────────────────────────────────────────────
74
+ export function runIgnoreList(argv) {
75
+ if (argv.includes("--help") || argv.includes("-h")) {
76
+ console.log(`
77
+ judges ignore-list — Manage review ignore patterns
78
+
79
+ Usage:
80
+ judges ignore-list init Create .judgesignore.json template
81
+ judges ignore-list show Show current ignore config
82
+ judges ignore-list test <path> Test if a path would be ignored
83
+ judges ignore-list --format json JSON output
84
+
85
+ Subcommands:
86
+ init Create a .judgesignore.json template
87
+ show Display current ignore configuration
88
+ test <path> Test whether a specific path is ignored
89
+
90
+ Options:
91
+ --format json JSON output
92
+ --help, -h Show this help
93
+
94
+ Ignore patterns are stored in .judgesignore.json and control which files,
95
+ directories, and rules are excluded from review.
96
+ `);
97
+ return;
98
+ }
99
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
100
+ const configPath = join(".", ".judgesignore.json");
101
+ const subcommand = argv.find((a) => !a.startsWith("-") && a !== "ignore-list") || "show";
102
+ if (subcommand === "init") {
103
+ if (existsSync(configPath)) {
104
+ console.error("Error: .judgesignore.json already exists.");
105
+ process.exitCode = 1;
106
+ return;
107
+ }
108
+ writeFileSync(configPath, JSON.stringify(defaultIgnoreConfig(), null, 2), "utf-8");
109
+ console.log("Created .judgesignore.json with default patterns.");
110
+ return;
111
+ }
112
+ // Load config
113
+ let config;
114
+ if (existsSync(configPath)) {
115
+ try {
116
+ config = JSON.parse(readFileSync(configPath, "utf-8"));
117
+ }
118
+ catch {
119
+ console.error("Error: .judgesignore.json is not valid JSON.");
120
+ process.exitCode = 1;
121
+ return;
122
+ }
123
+ }
124
+ else {
125
+ config = defaultIgnoreConfig();
126
+ }
127
+ if (subcommand === "test") {
128
+ const testPath = argv.find((a) => !a.startsWith("-") && a !== "ignore-list" && a !== "test");
129
+ if (!testPath) {
130
+ console.error("Error: Provide a path to test.");
131
+ process.exitCode = 1;
132
+ return;
133
+ }
134
+ const ignored = shouldIgnoreFile(testPath, ".", config);
135
+ if (format === "json") {
136
+ console.log(JSON.stringify({ path: testPath, ignored }));
137
+ }
138
+ else {
139
+ console.log(` ${testPath}: ${ignored ? "❌ IGNORED" : "✅ INCLUDED"}`);
140
+ }
141
+ return;
142
+ }
143
+ // Show
144
+ if (format === "json") {
145
+ console.log(JSON.stringify(config, null, 2));
146
+ return;
147
+ }
148
+ console.log(`\n Ignore Configuration\n ─────────────────────────────`);
149
+ console.log(` Source: ${existsSync(configPath) ? configPath : "defaults (no .judgesignore.json)"}`);
150
+ console.log(`\n File Patterns (${config.filePatterns.length}):`);
151
+ for (const p of config.filePatterns)
152
+ console.log(` ⬜ ${p}`);
153
+ console.log(`\n Directory Patterns (${config.directoryPatterns.length}):`);
154
+ for (const p of config.directoryPatterns)
155
+ console.log(` ⬜ ${p}`);
156
+ if (config.ruleIgnores.length > 0) {
157
+ console.log(`\n Ignored Rules (${config.ruleIgnores.length}):`);
158
+ for (const r of config.ruleIgnores)
159
+ console.log(` ⬜ ${r}`);
160
+ }
161
+ console.log(`\n Inline suppressions: ${config.inlineSuppressions ? "enabled" : "disabled"}`);
162
+ console.log();
163
+ }
164
+ // Export helpers for use by other commands
165
+ export { shouldIgnoreFile, shouldIgnoreRule, filterFiles };
166
+ //# sourceMappingURL=ignore-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ignore-list.js","sourceRoot":"","sources":["../../src/commands/ignore-list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Incremental-review — Only review files changed since last review.
3
+ */
4
+ export declare function runIncrementalReview(argv: string[]): void;
5
+ //# sourceMappingURL=incremental-review.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental-review.d.ts","sourceRoot":"","sources":["../../src/commands/incremental-review.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2IH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAsIzD"}
@@ -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