@kevinrabun/judges 3.86.0 → 3.88.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 (78) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +126 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/finding-age-analysis.d.ts +5 -0
  6. package/dist/commands/finding-age-analysis.d.ts.map +1 -0
  7. package/dist/commands/finding-age-analysis.js +145 -0
  8. package/dist/commands/finding-age-analysis.js.map +1 -0
  9. package/dist/commands/finding-code-smell.d.ts +5 -0
  10. package/dist/commands/finding-code-smell.d.ts.map +1 -0
  11. package/dist/commands/finding-code-smell.js +114 -0
  12. package/dist/commands/finding-code-smell.js.map +1 -0
  13. package/dist/commands/finding-correlation.d.ts +5 -0
  14. package/dist/commands/finding-correlation.d.ts.map +1 -0
  15. package/dist/commands/finding-correlation.js +104 -0
  16. package/dist/commands/finding-correlation.js.map +1 -0
  17. package/dist/commands/finding-dependency-tree.d.ts +5 -0
  18. package/dist/commands/finding-dependency-tree.d.ts.map +1 -0
  19. package/dist/commands/finding-dependency-tree.js +117 -0
  20. package/dist/commands/finding-dependency-tree.js.map +1 -0
  21. package/dist/commands/finding-owner-assign.d.ts +5 -0
  22. package/dist/commands/finding-owner-assign.d.ts.map +1 -0
  23. package/dist/commands/finding-owner-assign.js +134 -0
  24. package/dist/commands/finding-owner-assign.js.map +1 -0
  25. package/dist/commands/finding-pattern-library.d.ts +5 -0
  26. package/dist/commands/finding-pattern-library.d.ts.map +1 -0
  27. package/dist/commands/finding-pattern-library.js +146 -0
  28. package/dist/commands/finding-pattern-library.js.map +1 -0
  29. package/dist/commands/finding-related-rules.d.ts +5 -0
  30. package/dist/commands/finding-related-rules.d.ts.map +1 -0
  31. package/dist/commands/finding-related-rules.js +152 -0
  32. package/dist/commands/finding-related-rules.js.map +1 -0
  33. package/dist/commands/finding-rule-explain.d.ts +5 -0
  34. package/dist/commands/finding-rule-explain.d.ts.map +1 -0
  35. package/dist/commands/finding-rule-explain.js +141 -0
  36. package/dist/commands/finding-rule-explain.js.map +1 -0
  37. package/dist/commands/finding-suppression-audit.d.ts +5 -0
  38. package/dist/commands/finding-suppression-audit.d.ts.map +1 -0
  39. package/dist/commands/finding-suppression-audit.js +138 -0
  40. package/dist/commands/finding-suppression-audit.js.map +1 -0
  41. package/dist/commands/review-ci-integration.d.ts +5 -0
  42. package/dist/commands/review-ci-integration.d.ts.map +1 -0
  43. package/dist/commands/review-ci-integration.js +126 -0
  44. package/dist/commands/review-ci-integration.js.map +1 -0
  45. package/dist/commands/review-comparative.d.ts +5 -0
  46. package/dist/commands/review-comparative.d.ts.map +1 -0
  47. package/dist/commands/review-comparative.js +150 -0
  48. package/dist/commands/review-comparative.js.map +1 -0
  49. package/dist/commands/review-custom-rule.d.ts +5 -0
  50. package/dist/commands/review-custom-rule.d.ts.map +1 -0
  51. package/dist/commands/review-custom-rule.js +170 -0
  52. package/dist/commands/review-custom-rule.js.map +1 -0
  53. package/dist/commands/review-lock-file.d.ts +5 -0
  54. package/dist/commands/review-lock-file.d.ts.map +1 -0
  55. package/dist/commands/review-lock-file.js +154 -0
  56. package/dist/commands/review-lock-file.js.map +1 -0
  57. package/dist/commands/review-notification.d.ts +5 -0
  58. package/dist/commands/review-notification.d.ts.map +1 -0
  59. package/dist/commands/review-notification.js +127 -0
  60. package/dist/commands/review-notification.js.map +1 -0
  61. package/dist/commands/review-plugin-list.d.ts +5 -0
  62. package/dist/commands/review-plugin-list.d.ts.map +1 -0
  63. package/dist/commands/review-plugin-list.js +100 -0
  64. package/dist/commands/review-plugin-list.js.map +1 -0
  65. package/dist/commands/review-status-badge.d.ts +5 -0
  66. package/dist/commands/review-status-badge.d.ts.map +1 -0
  67. package/dist/commands/review-status-badge.js +121 -0
  68. package/dist/commands/review-status-badge.js.map +1 -0
  69. package/dist/commands/review-template-export.d.ts +5 -0
  70. package/dist/commands/review-template-export.d.ts.map +1 -0
  71. package/dist/commands/review-template-export.js +147 -0
  72. package/dist/commands/review-template-export.js.map +1 -0
  73. package/dist/commands/review-token-budget.d.ts +5 -0
  74. package/dist/commands/review-token-budget.d.ts.map +1 -0
  75. package/dist/commands/review-token-budget.js +118 -0
  76. package/dist/commands/review-token-budget.js.map +1 -0
  77. package/package.json +1 -1
  78. package/server.json +2 -2
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Finding-suppression-audit — Audit suppressed/ignored findings for review.
3
+ */
4
+ export declare function runFindingSuppressionAudit(argv: string[]): void;
5
+ //# sourceMappingURL=finding-suppression-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-suppression-audit.d.ts","sourceRoot":"","sources":["../../src/commands/finding-suppression-audit.ts"],"names":[],"mappings":"AAAA;;GAEG;AAkGH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA2E/D"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Finding-suppression-audit — Audit suppressed/ignored findings for review.
3
+ */
4
+ import { readFileSync, existsSync } from "fs";
5
+ // ─── Analysis ───────────────────────────────────────────────────────────────
6
+ function auditSuppressions(verdict, sourceFile, disabledRules, minSeverity) {
7
+ const records = [];
8
+ const disabled = new Set(disabledRules || []);
9
+ // check source for inline suppressions
10
+ let sourceLines = [];
11
+ if (sourceFile && existsSync(sourceFile)) {
12
+ sourceLines = readFileSync(sourceFile, "utf-8").split("\n");
13
+ }
14
+ // inline comment suppressions
15
+ const suppressionPatterns = [
16
+ /eslint-disable/,
17
+ /noqa/,
18
+ /noinspection/,
19
+ /@SuppressWarnings/,
20
+ /NOSONAR/,
21
+ /judges-ignore/,
22
+ /# type:\s*ignore/,
23
+ ];
24
+ for (let i = 0; i < sourceLines.length; i++) {
25
+ const line = sourceLines[i];
26
+ for (const pat of suppressionPatterns) {
27
+ if (pat.test(line)) {
28
+ records.push({
29
+ ruleId: `LINE-${i + 1}`,
30
+ title: `Inline suppression at line ${i + 1}`,
31
+ severity: "medium",
32
+ suppressionType: "inline-comment",
33
+ reason: line.trim().slice(0, 80),
34
+ risk: "medium",
35
+ });
36
+ break;
37
+ }
38
+ }
39
+ }
40
+ // config-disabled rules
41
+ for (const ruleId of disabled) {
42
+ const finding = verdict.findings.find((f) => f.ruleId === ruleId);
43
+ records.push({
44
+ ruleId,
45
+ title: finding ? finding.title : `Disabled rule: ${ruleId}`,
46
+ severity: finding ? (finding.severity || "medium").toLowerCase() : "unknown",
47
+ suppressionType: "config-disabled",
48
+ reason: "Rule disabled in configuration",
49
+ risk: "high",
50
+ });
51
+ }
52
+ // severity-filtered findings
53
+ if (minSeverity !== undefined) {
54
+ const sevOrder = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
55
+ const threshold = sevOrder[minSeverity] || 2;
56
+ for (const f of verdict.findings) {
57
+ const fSev = (f.severity || "medium").toLowerCase();
58
+ if ((sevOrder[fSev] || 2) > threshold) {
59
+ records.push({
60
+ ruleId: f.ruleId,
61
+ title: f.title,
62
+ severity: fSev,
63
+ suppressionType: "severity-filter",
64
+ reason: `Below minimum severity: ${minSeverity}`,
65
+ risk: "low",
66
+ });
67
+ }
68
+ }
69
+ }
70
+ return records;
71
+ }
72
+ // ─── CLI ────────────────────────────────────────────────────────────────────
73
+ export function runFindingSuppressionAudit(argv) {
74
+ const fileIdx = argv.indexOf("--file");
75
+ const sourceIdx = argv.indexOf("--source");
76
+ const disabledIdx = argv.indexOf("--disabled");
77
+ const formatIdx = argv.indexOf("--format");
78
+ const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : undefined;
79
+ const sourceFile = sourceIdx >= 0 ? argv[sourceIdx + 1] : undefined;
80
+ const disabledStr = disabledIdx >= 0 ? argv[disabledIdx + 1] : undefined;
81
+ const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
82
+ if (argv.includes("--help") || argv.includes("-h")) {
83
+ console.log(`
84
+ judges finding-suppression-audit — Audit suppressed findings
85
+
86
+ Usage:
87
+ judges finding-suppression-audit --file <verdict.json> [--source <src.ts>]
88
+ [--disabled <RULE1,RULE2>] [--format table|json]
89
+
90
+ Options:
91
+ --file <path> Path to verdict JSON file (required)
92
+ --source <path> Source file to check for inline suppressions
93
+ --disabled <rules> Comma-separated disabled rule IDs
94
+ --format <fmt> Output format: table (default), json
95
+ --help, -h Show this help
96
+ `);
97
+ return;
98
+ }
99
+ if (!filePath) {
100
+ console.error("Error: --file required");
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+ if (!existsSync(filePath)) {
105
+ console.error(`Error: not found: ${filePath}`);
106
+ process.exitCode = 1;
107
+ return;
108
+ }
109
+ let verdict;
110
+ try {
111
+ verdict = JSON.parse(readFileSync(filePath, "utf-8"));
112
+ }
113
+ catch {
114
+ console.error("Error: invalid JSON");
115
+ process.exitCode = 1;
116
+ return;
117
+ }
118
+ const disabled = disabledStr ? disabledStr.split(",").map((s) => s.trim()) : [];
119
+ const records = auditSuppressions(verdict, sourceFile, disabled);
120
+ if (format === "json") {
121
+ console.log(JSON.stringify(records, null, 2));
122
+ return;
123
+ }
124
+ const highRisk = records.filter((r) => r.risk === "high").length;
125
+ const medRisk = records.filter((r) => r.risk === "medium").length;
126
+ console.log(`\nSuppression Audit (${records.length} suppressions)`);
127
+ console.log("═".repeat(75));
128
+ console.log(` High risk: ${highRisk} | Medium risk: ${medRisk} | Low risk: ${records.length - highRisk - medRisk}`);
129
+ console.log("─".repeat(75));
130
+ console.log(`${"Risk".padEnd(8)} ${"Type".padEnd(18)} ${"Rule".padEnd(18)} ${"Severity".padEnd(10)} Reason`);
131
+ console.log("─".repeat(75));
132
+ for (const r of records) {
133
+ const reason = r.reason.length > 25 ? r.reason.slice(0, 25) + "…" : r.reason;
134
+ console.log(`${r.risk.padEnd(8)} ${r.suppressionType.padEnd(18)} ${r.ruleId.padEnd(18)} ${r.severity.padEnd(10)} ${reason}`);
135
+ }
136
+ console.log("═".repeat(75));
137
+ }
138
+ //# sourceMappingURL=finding-suppression-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-suppression-audit.js","sourceRoot":"","sources":["../../src/commands/finding-suppression-audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Review-ci-integration — Generate CI pipeline configuration for Judges.
3
+ */
4
+ export declare function runReviewCiIntegration(argv: string[]): void;
5
+ //# sourceMappingURL=review-ci-integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-ci-integration.d.ts","sourceRoot":"","sources":["../../src/commands/review-ci-integration.ts"],"names":[],"mappings":"AAAA;;GAEG;AA8EH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAwD3D"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Review-ci-integration — Generate CI pipeline configuration for Judges.
3
+ */
4
+ import { existsSync, writeFileSync } from "fs";
5
+ // ─── Templates ──────────────────────────────────────────────────────────────
6
+ function githubActionsTemplate() {
7
+ return `name: Judges Review
8
+ on:
9
+ pull_request:
10
+ types: [opened, synchronize]
11
+
12
+ permissions:
13
+ contents: read
14
+ pull-requests: write
15
+
16
+ jobs:
17
+ review:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: actions/setup-node@v4
22
+ with:
23
+ node-version: '20'
24
+ - run: npm install -g @kevinrabun/judges
25
+ - run: judges eval --file \${{ github.event.pull_request.head.sha }} --format sarif --output judges-report.sarif
26
+ - uses: github/codeql-action/upload-sarif@v3
27
+ if: always()
28
+ with:
29
+ sarif_file: judges-report.sarif
30
+ `;
31
+ }
32
+ function azurePipelinesTemplate() {
33
+ return `trigger:
34
+ branches:
35
+ include:
36
+ - main
37
+ pr:
38
+ branches:
39
+ include:
40
+ - main
41
+
42
+ pool:
43
+ vmImage: 'ubuntu-latest'
44
+
45
+ steps:
46
+ - task: NodeTool@0
47
+ inputs:
48
+ versionSpec: '20.x'
49
+ - script: npm install -g @kevinrabun/judges
50
+ displayName: 'Install Judges'
51
+ - script: judges eval --format sarif --output judges-report.sarif
52
+ displayName: 'Run Judges Review'
53
+ - task: PublishBuildArtifacts@1
54
+ inputs:
55
+ pathtoPublish: judges-report.sarif
56
+ artifactName: judges-report
57
+ `;
58
+ }
59
+ function gitlabCiTemplate() {
60
+ return `judges-review:
61
+ image: node:20
62
+ stage: test
63
+ script:
64
+ - npm install -g @kevinrabun/judges
65
+ - judges eval --format sarif --output judges-report.sarif
66
+ artifacts:
67
+ reports:
68
+ sast: judges-report.sarif
69
+ paths:
70
+ - judges-report.sarif
71
+ `;
72
+ }
73
+ // ─── CLI ────────────────────────────────────────────────────────────────────
74
+ export function runReviewCiIntegration(argv) {
75
+ const platformIdx = argv.indexOf("--platform");
76
+ const outputIdx = argv.indexOf("--output");
77
+ const platform = platformIdx >= 0 ? argv[platformIdx + 1] : "github";
78
+ const outputPath = outputIdx >= 0 ? argv[outputIdx + 1] : undefined;
79
+ if (argv.includes("--help") || argv.includes("-h")) {
80
+ console.log(`
81
+ judges review-ci-integration — Generate CI pipeline configuration
82
+
83
+ Usage:
84
+ judges review-ci-integration [--platform github|azure|gitlab]
85
+ [--output <file>]
86
+
87
+ Options:
88
+ --platform <type> CI platform: github (default), azure, gitlab
89
+ --output <path> Write config to file
90
+ --help, -h Show this help
91
+ `);
92
+ return;
93
+ }
94
+ let template;
95
+ let defaultFile;
96
+ switch (platform) {
97
+ case "azure":
98
+ template = azurePipelinesTemplate();
99
+ defaultFile = "azure-pipelines.yml";
100
+ break;
101
+ case "gitlab":
102
+ template = gitlabCiTemplate();
103
+ defaultFile = ".gitlab-ci.yml";
104
+ break;
105
+ default:
106
+ template = githubActionsTemplate();
107
+ defaultFile = ".github/workflows/judges-review.yml";
108
+ break;
109
+ }
110
+ if (outputPath) {
111
+ if (existsSync(outputPath)) {
112
+ console.error(`Error: file already exists: ${outputPath}`);
113
+ console.error("Remove it first or choose a different path");
114
+ process.exitCode = 1;
115
+ return;
116
+ }
117
+ writeFileSync(outputPath, template);
118
+ console.log(`CI config written to ${outputPath}`);
119
+ return;
120
+ }
121
+ console.log(`\n# Judges CI Configuration (${platform})`);
122
+ console.log(`# Suggested file: ${defaultFile}`);
123
+ console.log("─".repeat(60));
124
+ console.log(template);
125
+ }
126
+ //# sourceMappingURL=review-ci-integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-ci-integration.js","sourceRoot":"","sources":["../../src/commands/review-ci-integration.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAE/C,+EAA+E;AAE/E,SAAS,qBAAqB;IAC5B,OAAO;;;;;;;;;;;;;;;;;;;;;;;CAuBR,CAAC;AACF,CAAC;AAED,SAAS,sBAAsB;IAC7B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAwBR,CAAC;AACF,CAAC;AAED,SAAS,gBAAgB;IACvB,OAAO;;;;;;;;;;;CAWR,CAAC;AACF,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,sBAAsB,CAAC,IAAc;IACnD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACrE,MAAM,UAAU,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpE,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;CAWf,CAAC,CAAC;QACC,OAAO;IACT,CAAC;IAED,IAAI,QAAgB,CAAC;IACrB,IAAI,WAAmB,CAAC;IAExB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,OAAO;YACV,QAAQ,GAAG,sBAAsB,EAAE,CAAC;YACpC,WAAW,GAAG,qBAAqB,CAAC;YACpC,MAAM;QACR,KAAK,QAAQ;YACX,QAAQ,GAAG,gBAAgB,EAAE,CAAC;YAC9B,WAAW,GAAG,gBAAgB,CAAC;YAC/B,MAAM;QACR;YACE,QAAQ,GAAG,qBAAqB,EAAE,CAAC;YACnC,WAAW,GAAG,qCAAqC,CAAC;YACpD,MAAM;IACV,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,+BAA+B,UAAU,EAAE,CAAC,CAAC;YAC3D,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAC5D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,gCAAgC,QAAQ,GAAG,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,qBAAqB,WAAW,EAAE,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Review-comparative — Compare two verdict reports side by side.
3
+ */
4
+ export declare function runReviewComparative(argv: string[]): void;
5
+ //# sourceMappingURL=review-comparative.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-comparative.d.ts","sourceRoot":"","sources":["../../src/commands/review-comparative.ts"],"names":[],"mappings":"AAAA;;GAEG;AA6FH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8FzD"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Review-comparative — Compare two verdict reports side by side.
3
+ */
4
+ import { readFileSync, existsSync } from "fs";
5
+ // ─── Analysis ───────────────────────────────────────────────────────────────
6
+ function compareVerdicts(before, after) {
7
+ const metrics = [];
8
+ // overall verdict
9
+ metrics.push({
10
+ metric: "Overall Verdict",
11
+ before: before.overallVerdict,
12
+ after: after.overallVerdict,
13
+ change: before.overallVerdict === after.overallVerdict ? "unchanged" : "changed",
14
+ });
15
+ // score
16
+ const scoreDiff = after.overallScore - before.overallScore;
17
+ metrics.push({
18
+ metric: "Score",
19
+ before: before.overallScore,
20
+ after: after.overallScore,
21
+ change: scoreDiff > 0 ? `+${scoreDiff}` : String(scoreDiff),
22
+ });
23
+ // finding counts
24
+ metrics.push({
25
+ metric: "Total Findings",
26
+ before: before.findings.length,
27
+ after: after.findings.length,
28
+ change: String(after.findings.length - before.findings.length),
29
+ });
30
+ metrics.push({
31
+ metric: "Critical",
32
+ before: before.criticalCount,
33
+ after: after.criticalCount,
34
+ change: String(after.criticalCount - before.criticalCount),
35
+ });
36
+ metrics.push({
37
+ metric: "High",
38
+ before: before.highCount,
39
+ after: after.highCount,
40
+ change: String(after.highCount - before.highCount),
41
+ });
42
+ // finding diff
43
+ const beforeRules = new Set(before.findings.map((f) => f.ruleId));
44
+ const afterRules = new Set(after.findings.map((f) => f.ruleId));
45
+ const findings = [];
46
+ for (const f of after.findings) {
47
+ if (!beforeRules.has(f.ruleId)) {
48
+ findings.push({ ruleId: f.ruleId, title: f.title, status: "added" });
49
+ }
50
+ }
51
+ for (const f of before.findings) {
52
+ if (!afterRules.has(f.ruleId)) {
53
+ findings.push({ ruleId: f.ruleId, title: f.title, status: "removed" });
54
+ }
55
+ }
56
+ for (const f of after.findings) {
57
+ if (beforeRules.has(f.ruleId)) {
58
+ findings.push({ ruleId: f.ruleId, title: f.title, status: "unchanged" });
59
+ }
60
+ }
61
+ return { metrics, findings };
62
+ }
63
+ // ─── CLI ────────────────────────────────────────────────────────────────────
64
+ export function runReviewComparative(argv) {
65
+ const beforeIdx = argv.indexOf("--before");
66
+ const afterIdx = argv.indexOf("--after");
67
+ const formatIdx = argv.indexOf("--format");
68
+ const beforePath = beforeIdx >= 0 ? argv[beforeIdx + 1] : undefined;
69
+ const afterPath = afterIdx >= 0 ? argv[afterIdx + 1] : undefined;
70
+ const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
71
+ if (argv.includes("--help") || argv.includes("-h")) {
72
+ console.log(`
73
+ judges review-comparative — Compare two verdict reports
74
+
75
+ Usage:
76
+ judges review-comparative --before <old.json> --after <new.json>
77
+ [--format table|json]
78
+
79
+ Options:
80
+ --before <path> Path to baseline verdict JSON (required)
81
+ --after <path> Path to new verdict JSON (required)
82
+ --format <fmt> Output format: table (default), json
83
+ --help, -h Show this help
84
+ `);
85
+ return;
86
+ }
87
+ if (!beforePath || !afterPath) {
88
+ console.error("Error: --before and --after required");
89
+ process.exitCode = 1;
90
+ return;
91
+ }
92
+ if (!existsSync(beforePath)) {
93
+ console.error(`Error: not found: ${beforePath}`);
94
+ process.exitCode = 1;
95
+ return;
96
+ }
97
+ if (!existsSync(afterPath)) {
98
+ console.error(`Error: not found: ${afterPath}`);
99
+ process.exitCode = 1;
100
+ return;
101
+ }
102
+ let before;
103
+ let after;
104
+ try {
105
+ before = JSON.parse(readFileSync(beforePath, "utf-8"));
106
+ }
107
+ catch {
108
+ console.error("Error: invalid JSON in before file");
109
+ process.exitCode = 1;
110
+ return;
111
+ }
112
+ try {
113
+ after = JSON.parse(readFileSync(afterPath, "utf-8"));
114
+ }
115
+ catch {
116
+ console.error("Error: invalid JSON in after file");
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+ const result = compareVerdicts(before, after);
121
+ if (format === "json") {
122
+ console.log(JSON.stringify(result, null, 2));
123
+ return;
124
+ }
125
+ console.log(`\nComparative Review`);
126
+ console.log("═".repeat(65));
127
+ console.log(`${"Metric".padEnd(20)} ${"Before".padEnd(15)} ${"After".padEnd(15)} Change`);
128
+ console.log("─".repeat(65));
129
+ for (const m of result.metrics) {
130
+ console.log(`${m.metric.padEnd(20)} ${String(m.before).padEnd(15)} ${String(m.after).padEnd(15)} ${m.change}`);
131
+ }
132
+ const added = result.findings.filter((f) => f.status === "added");
133
+ const removed = result.findings.filter((f) => f.status === "removed");
134
+ if (added.length > 0) {
135
+ console.log(`\n New findings (+${added.length}):`);
136
+ for (const f of added) {
137
+ const title = f.title.length > 40 ? f.title.slice(0, 40) + "…" : f.title;
138
+ console.log(` + ${f.ruleId.padEnd(18)} ${title}`);
139
+ }
140
+ }
141
+ if (removed.length > 0) {
142
+ console.log(`\n Resolved findings (-${removed.length}):`);
143
+ for (const f of removed) {
144
+ const title = f.title.length > 40 ? f.title.slice(0, 40) + "…" : f.title;
145
+ console.log(` - ${f.ruleId.padEnd(18)} ${title}`);
146
+ }
147
+ }
148
+ console.log("═".repeat(65));
149
+ }
150
+ //# sourceMappingURL=review-comparative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-comparative.js","sourceRoot":"","sources":["../../src/commands/review-comparative.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Review-custom-rule — Create and manage custom rules for review.
3
+ */
4
+ export declare function runReviewCustomRule(argv: string[]): void;
5
+ //# sourceMappingURL=review-custom-rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-custom-rule.d.ts","sourceRoot":"","sources":["../../src/commands/review-custom-rule.ts"],"names":[],"mappings":"AAAA;;GAEG;AAiEH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAuIxD"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Review-custom-rule — Create and manage custom rules for review.
3
+ */
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
+ import { dirname } from "path";
6
+ // ─── Helpers ────────────────────────────────────────────────────────────────
7
+ function loadConfig(configPath) {
8
+ if (!existsSync(configPath)) {
9
+ return { version: 1, rules: [] };
10
+ }
11
+ try {
12
+ return JSON.parse(readFileSync(configPath, "utf-8"));
13
+ }
14
+ catch {
15
+ return { version: 1, rules: [] };
16
+ }
17
+ }
18
+ function saveConfig(configPath, config) {
19
+ const dir = dirname(configPath);
20
+ if (!existsSync(dir)) {
21
+ mkdirSync(dir, { recursive: true });
22
+ }
23
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
24
+ }
25
+ function testRule(rule, sourceFile) {
26
+ if (!existsSync(sourceFile))
27
+ return [];
28
+ const lines = readFileSync(sourceFile, "utf-8").split("\n");
29
+ const matches = [];
30
+ try {
31
+ const regex = new RegExp(rule.pattern, "gi");
32
+ for (let i = 0; i < lines.length; i++) {
33
+ const m = lines[i].match(regex);
34
+ if (m !== null) {
35
+ matches.push({ line: i + 1, match: m[0] });
36
+ }
37
+ }
38
+ }
39
+ catch {
40
+ // invalid regex
41
+ }
42
+ return matches;
43
+ }
44
+ // ─── CLI ────────────────────────────────────────────────────────────────────
45
+ export function runReviewCustomRule(argv) {
46
+ const actionIdx = argv.indexOf("--action");
47
+ const configIdx = argv.indexOf("--config");
48
+ const idIdx = argv.indexOf("--id");
49
+ const titleIdx = argv.indexOf("--title");
50
+ const severityIdx = argv.indexOf("--severity");
51
+ const patternIdx = argv.indexOf("--pattern");
52
+ const sourceIdx = argv.indexOf("--source");
53
+ const formatIdx = argv.indexOf("--format");
54
+ const action = actionIdx >= 0 ? argv[actionIdx + 1] : "list";
55
+ const configPath = configIdx >= 0 ? argv[configIdx + 1] : ".judges-custom-rules.json";
56
+ const ruleId = idIdx >= 0 ? argv[idIdx + 1] : undefined;
57
+ const title = titleIdx >= 0 ? argv[titleIdx + 1] : undefined;
58
+ const severity = severityIdx >= 0 ? argv[severityIdx + 1] : "medium";
59
+ const pattern = patternIdx >= 0 ? argv[patternIdx + 1] : undefined;
60
+ const sourceFile = sourceIdx >= 0 ? argv[sourceIdx + 1] : undefined;
61
+ const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
62
+ if (argv.includes("--help") || argv.includes("-h")) {
63
+ console.log(`
64
+ judges review-custom-rule — Manage custom review rules
65
+
66
+ Usage:
67
+ judges review-custom-rule --action <action> [options]
68
+
69
+ Actions:
70
+ list List custom rules (default)
71
+ add Add a new custom rule
72
+ remove Remove a custom rule
73
+ test Test a rule against a source file
74
+
75
+ Options:
76
+ --action <act> Action: list, add, remove, test
77
+ --config <path> Config file (default: .judges-custom-rules.json)
78
+ --id <id> Rule ID (for add/remove/test)
79
+ --title <title> Rule title (for add)
80
+ --severity <sev> Severity: critical, high, medium (default), low, info
81
+ --pattern <regex> Regex pattern (for add)
82
+ --source <path> Source file (for test)
83
+ --format <fmt> Output format: table (default), json
84
+ --help, -h Show this help
85
+ `);
86
+ return;
87
+ }
88
+ const config = loadConfig(configPath);
89
+ if (action === "add") {
90
+ if (!ruleId || !pattern) {
91
+ console.error("Error: --id and --pattern required for add");
92
+ process.exitCode = 1;
93
+ return;
94
+ }
95
+ if (config.rules.some((r) => r.id === ruleId)) {
96
+ console.error(`Error: rule ${ruleId} already exists`);
97
+ process.exitCode = 1;
98
+ return;
99
+ }
100
+ config.rules.push({
101
+ id: ruleId,
102
+ title: title || ruleId,
103
+ severity: severity,
104
+ pattern,
105
+ description: `Custom rule: ${ruleId}`,
106
+ recommendation: "Review matched code for compliance",
107
+ enabled: true,
108
+ });
109
+ saveConfig(configPath, config);
110
+ console.log(`Added rule: ${ruleId}`);
111
+ return;
112
+ }
113
+ if (action === "remove") {
114
+ if (!ruleId) {
115
+ console.error("Error: --id required for remove");
116
+ process.exitCode = 1;
117
+ return;
118
+ }
119
+ const idx = config.rules.findIndex((r) => r.id === ruleId);
120
+ if (idx < 0) {
121
+ console.error(`Error: rule ${ruleId} not found`);
122
+ process.exitCode = 1;
123
+ return;
124
+ }
125
+ config.rules.splice(idx, 1);
126
+ saveConfig(configPath, config);
127
+ console.log(`Removed rule: ${ruleId}`);
128
+ return;
129
+ }
130
+ if (action === "test") {
131
+ if (!ruleId || !sourceFile) {
132
+ console.error("Error: --id and --source required for test");
133
+ process.exitCode = 1;
134
+ return;
135
+ }
136
+ const rule = config.rules.find((r) => r.id === ruleId);
137
+ if (rule === undefined) {
138
+ console.error(`Error: rule ${ruleId} not found`);
139
+ process.exitCode = 1;
140
+ return;
141
+ }
142
+ const matches = testRule(rule, sourceFile);
143
+ if (format === "json") {
144
+ console.log(JSON.stringify({ rule: rule.id, matches }, null, 2));
145
+ return;
146
+ }
147
+ console.log(`\nTest Results: ${rule.id} (${matches.length} matches)`);
148
+ console.log("─".repeat(50));
149
+ for (const m of matches) {
150
+ console.log(` Line ${String(m.line).padEnd(6)} ${m.match}`);
151
+ }
152
+ return;
153
+ }
154
+ // default: list
155
+ if (format === "json") {
156
+ console.log(JSON.stringify(config, null, 2));
157
+ return;
158
+ }
159
+ console.log(`\nCustom Rules (${config.rules.length})`);
160
+ console.log("═".repeat(70));
161
+ console.log(`${"ID".padEnd(18)} ${"Severity".padEnd(10)} ${"Enabled".padEnd(10)} ${"Pattern".padEnd(25)} Title`);
162
+ console.log("─".repeat(70));
163
+ for (const r of config.rules) {
164
+ const pat = r.pattern.length > 23 ? r.pattern.slice(0, 23) + "…" : r.pattern;
165
+ const title2 = r.title.length > 15 ? r.title.slice(0, 15) + "…" : r.title;
166
+ console.log(`${r.id.padEnd(18)} ${r.severity.padEnd(10)} ${String(r.enabled).padEnd(10)} ${pat.padEnd(25)} ${title2}`);
167
+ }
168
+ console.log("═".repeat(70));
169
+ }
170
+ //# sourceMappingURL=review-custom-rule.js.map