@kevinrabun/judges 3.66.0 → 3.67.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 +8 -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/config-lint.d.ts +5 -0
  6. package/dist/commands/config-lint.d.ts.map +1 -0
  7. package/dist/commands/config-lint.js +188 -0
  8. package/dist/commands/config-lint.js.map +1 -0
  9. package/dist/commands/finding-age.d.ts +5 -0
  10. package/dist/commands/finding-age.d.ts.map +1 -0
  11. package/dist/commands/finding-age.js +146 -0
  12. package/dist/commands/finding-age.js.map +1 -0
  13. package/dist/commands/finding-rank.d.ts +5 -0
  14. package/dist/commands/finding-rank.d.ts.map +1 -0
  15. package/dist/commands/finding-rank.js +139 -0
  16. package/dist/commands/finding-rank.js.map +1 -0
  17. package/dist/commands/review-dashboard.d.ts +5 -0
  18. package/dist/commands/review-dashboard.d.ts.map +1 -0
  19. package/dist/commands/review-dashboard.js +141 -0
  20. package/dist/commands/review-dashboard.js.map +1 -0
  21. package/dist/commands/review-diff-summary.d.ts +5 -0
  22. package/dist/commands/review-diff-summary.d.ts.map +1 -0
  23. package/dist/commands/review-diff-summary.js +155 -0
  24. package/dist/commands/review-diff-summary.js.map +1 -0
  25. package/dist/commands/review-notify.d.ts +5 -0
  26. package/dist/commands/review-notify.d.ts.map +1 -0
  27. package/dist/commands/review-notify.js +144 -0
  28. package/dist/commands/review-notify.js.map +1 -0
  29. package/dist/commands/review-offline.d.ts +5 -0
  30. package/dist/commands/review-offline.d.ts.map +1 -0
  31. package/dist/commands/review-offline.js +126 -0
  32. package/dist/commands/review-offline.js.map +1 -0
  33. package/dist/commands/review-quota.d.ts +5 -0
  34. package/dist/commands/review-quota.d.ts.map +1 -0
  35. package/dist/commands/review-quota.js +127 -0
  36. package/dist/commands/review-quota.js.map +1 -0
  37. package/package.json +1 -1
  38. package/server.json +2 -2
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Config-lint — Lint and validate .judgesrc configuration files.
3
+ */
4
+ import { readFileSync, existsSync } from "fs";
5
+ // ─── Validation ─────────────────────────────────────────────────────────────
6
+ const VALID_PRESETS = [
7
+ "strict",
8
+ "lenient",
9
+ "security-only",
10
+ "startup",
11
+ "compliance",
12
+ "performance",
13
+ "react",
14
+ "express",
15
+ "fastapi",
16
+ "django",
17
+ "spring-boot",
18
+ "rails",
19
+ "nextjs",
20
+ "terraform",
21
+ "kubernetes",
22
+ ];
23
+ const VALID_SEVERITIES = ["critical", "high", "medium", "low", "info"];
24
+ function lintConfig(config, issues) {
25
+ // Check preset
26
+ if (config["preset"] !== undefined) {
27
+ const preset = String(config["preset"]);
28
+ const presets = preset.split(",").map((p) => p.trim());
29
+ for (const p of presets) {
30
+ if (!VALID_PRESETS.includes(p)) {
31
+ issues.push({
32
+ level: "warning",
33
+ field: "preset",
34
+ message: `Unknown preset '${p}'. Valid: ${VALID_PRESETS.join(", ")}`,
35
+ });
36
+ }
37
+ }
38
+ }
39
+ // Check minSeverity
40
+ if (config["minSeverity"] !== undefined) {
41
+ const sev = String(config["minSeverity"]).toLowerCase();
42
+ if (!VALID_SEVERITIES.includes(sev)) {
43
+ issues.push({
44
+ level: "error",
45
+ field: "minSeverity",
46
+ message: `Invalid severity '${sev}'. Valid: ${VALID_SEVERITIES.join(", ")}`,
47
+ });
48
+ }
49
+ }
50
+ // Check disabledJudges
51
+ if (config["disabledJudges"] !== undefined) {
52
+ if (!Array.isArray(config["disabledJudges"])) {
53
+ issues.push({ level: "error", field: "disabledJudges", message: "Must be an array of strings" });
54
+ }
55
+ }
56
+ // Check disabledRules
57
+ if (config["disabledRules"] !== undefined) {
58
+ if (!Array.isArray(config["disabledRules"])) {
59
+ issues.push({ level: "error", field: "disabledRules", message: "Must be an array of strings" });
60
+ }
61
+ }
62
+ // Check ruleOverrides
63
+ if (config["ruleOverrides"] !== undefined) {
64
+ if (typeof config["ruleOverrides"] !== "object" || config["ruleOverrides"] === null) {
65
+ issues.push({ level: "error", field: "ruleOverrides", message: "Must be an object" });
66
+ }
67
+ }
68
+ // Warn about unknown fields
69
+ const knownFields = new Set([
70
+ "preset",
71
+ "minSeverity",
72
+ "disabledJudges",
73
+ "disabledRules",
74
+ "ruleOverrides",
75
+ "format",
76
+ "failOnFindings",
77
+ "minScore",
78
+ "exclude",
79
+ "include",
80
+ "maxFiles",
81
+ "language",
82
+ "baseline",
83
+ ]);
84
+ for (const key of Object.keys(config)) {
85
+ if (!knownFields.has(key)) {
86
+ issues.push({ level: "info", field: key, message: `Unknown field '${key}' — may be ignored` });
87
+ }
88
+ }
89
+ // Check format
90
+ if (config["format"] !== undefined) {
91
+ const validFormats = ["text", "json", "sarif", "markdown", "html", "pdf", "junit", "codeclimate", "github-actions"];
92
+ if (!validFormats.includes(String(config["format"]))) {
93
+ issues.push({
94
+ level: "warning",
95
+ field: "format",
96
+ message: `Unknown format '${config["format"]}'. Valid: ${validFormats.join(", ")}`,
97
+ });
98
+ }
99
+ }
100
+ // Check minScore
101
+ if (config["minScore"] !== undefined) {
102
+ const score = Number(config["minScore"]);
103
+ if (isNaN(score) || score < 0 || score > 100) {
104
+ issues.push({ level: "error", field: "minScore", message: "Must be a number between 0 and 100" });
105
+ }
106
+ }
107
+ }
108
+ // ─── CLI ────────────────────────────────────────────────────────────────────
109
+ export function runConfigLint(argv) {
110
+ if (argv.includes("--help") || argv.includes("-h")) {
111
+ console.log(`
112
+ judges config-lint — Lint and validate .judgesrc configuration
113
+
114
+ Usage:
115
+ judges config-lint Lint .judgesrc in current dir
116
+ judges config-lint --file custom.judgesrc Lint specific file
117
+ judges config-lint --strict Treat warnings as errors
118
+ judges config-lint --format json JSON output
119
+
120
+ Options:
121
+ --file <path> Configuration file to lint (default: .judgesrc)
122
+ --strict Treat warnings as errors
123
+ --format json JSON output
124
+ --help, -h Show this help
125
+
126
+ Validates configuration for correctness: preset names,
127
+ severity levels, field types, and unknown properties.
128
+ `);
129
+ return;
130
+ }
131
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
132
+ const configFile = argv.find((_a, i) => argv[i - 1] === "--file") || ".judgesrc";
133
+ const strict = argv.includes("--strict");
134
+ if (!existsSync(configFile)) {
135
+ console.error(`Error: Configuration file not found: ${configFile}`);
136
+ process.exitCode = 1;
137
+ return;
138
+ }
139
+ let config;
140
+ try {
141
+ config = JSON.parse(readFileSync(configFile, "utf-8"));
142
+ }
143
+ catch (e) {
144
+ console.error(`Error: Invalid JSON in ${configFile}: ${e instanceof Error ? e.message : "parse error"}`);
145
+ process.exitCode = 1;
146
+ return;
147
+ }
148
+ const issues = [];
149
+ lintConfig(config, issues);
150
+ const errors = issues.filter((i) => i.level === "error");
151
+ const warnings = issues.filter((i) => i.level === "warning");
152
+ const infos = issues.filter((i) => i.level === "info");
153
+ const hasErrors = errors.length > 0 || (strict && warnings.length > 0);
154
+ if (format === "json") {
155
+ console.log(JSON.stringify({
156
+ file: configFile,
157
+ valid: !hasErrors,
158
+ errors: errors.length,
159
+ warnings: warnings.length,
160
+ infos: infos.length,
161
+ issues,
162
+ }, null, 2));
163
+ if (hasErrors)
164
+ process.exitCode = 1;
165
+ return;
166
+ }
167
+ console.log(`\n Config Lint: ${configFile}\n ─────────────────────────────`);
168
+ if (issues.length === 0) {
169
+ console.log(" ✅ Configuration is valid. No issues found.");
170
+ console.log();
171
+ return;
172
+ }
173
+ for (const issue of issues) {
174
+ const icon = issue.level === "error" ? "❌" : issue.level === "warning" ? "⚠️" : "ℹ️";
175
+ console.log(` ${icon} [${issue.level.toUpperCase()}] ${issue.field}: ${issue.message}`);
176
+ }
177
+ console.log();
178
+ console.log(` Errors: ${errors.length}, Warnings: ${warnings.length}, Info: ${infos.length}`);
179
+ if (hasErrors) {
180
+ console.log(" ❌ Configuration has errors.");
181
+ process.exitCode = 1;
182
+ }
183
+ else {
184
+ console.log(" ✅ Configuration is valid (with warnings).");
185
+ }
186
+ console.log();
187
+ }
188
+ //# sourceMappingURL=config-lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-lint.js","sourceRoot":"","sources":["../../src/commands/config-lint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Finding-age — Track how long findings have been unresolved.
3
+ */
4
+ export declare function runFindingAge(argv: string[]): void;
5
+ //# sourceMappingURL=finding-age.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-age.d.ts","sourceRoot":"","sources":["../../src/commands/finding-age.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsDH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqIlD"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Finding-age — Track how long findings have been unresolved.
3
+ */
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
+ import { join, dirname } from "path";
6
+ // ─── Storage ────────────────────────────────────────────────────────────────
7
+ const AGE_FILE = join(".judges", "finding-age.json");
8
+ function loadAgeStore() {
9
+ if (!existsSync(AGE_FILE))
10
+ return { version: "1.0.0", records: [] };
11
+ try {
12
+ return JSON.parse(readFileSync(AGE_FILE, "utf-8"));
13
+ }
14
+ catch {
15
+ return { version: "1.0.0", records: [] };
16
+ }
17
+ }
18
+ function saveAgeStore(store) {
19
+ mkdirSync(dirname(AGE_FILE), { recursive: true });
20
+ writeFileSync(AGE_FILE, JSON.stringify(store, null, 2), "utf-8");
21
+ }
22
+ function findingFingerprint(f) {
23
+ return [f.ruleId || "", f.title || "", String(f.severity || "")].join("|").toLowerCase();
24
+ }
25
+ function daysBetween(a, b) {
26
+ const msPerDay = 86400000;
27
+ return Math.floor((new Date(b).getTime() - new Date(a).getTime()) / msPerDay);
28
+ }
29
+ // ─── CLI ────────────────────────────────────────────────────────────────────
30
+ export function runFindingAge(argv) {
31
+ if (argv.includes("--help") || argv.includes("-h")) {
32
+ console.log(`
33
+ judges finding-age — Track how long findings have been unresolved
34
+
35
+ Usage:
36
+ judges finding-age update --file verdict.json Update age records
37
+ judges finding-age show Show finding ages
38
+ judges finding-age show --stale 30 Show findings older than 30 days
39
+ judges finding-age clear Clear all records
40
+
41
+ Subcommands:
42
+ update Update records from a verdict file
43
+ show Show age report
44
+ clear Clear all tracking data
45
+
46
+ Options:
47
+ --file <path> Verdict JSON (for update)
48
+ --stale <days> Show only findings older than N days
49
+ --format json JSON output
50
+ --help, -h Show this help
51
+
52
+ Tracks first-seen dates and age of findings across reviews.
53
+ Data stored locally in .judges/finding-age.json.
54
+ `);
55
+ return;
56
+ }
57
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
58
+ const subcommand = argv.find((a) => ["update", "show", "clear"].includes(a)) || "show";
59
+ const store = loadAgeStore();
60
+ if (subcommand === "update") {
61
+ const file = argv.find((_a, i) => argv[i - 1] === "--file");
62
+ if (!file || !existsSync(file)) {
63
+ console.error("Error: --file with valid verdict JSON is required.");
64
+ process.exitCode = 1;
65
+ return;
66
+ }
67
+ let verdict;
68
+ try {
69
+ verdict = JSON.parse(readFileSync(file, "utf-8"));
70
+ }
71
+ catch {
72
+ console.error(`Error: Could not parse ${file}`);
73
+ process.exitCode = 1;
74
+ return;
75
+ }
76
+ const now = new Date().toISOString();
77
+ const currentFingerprints = new Set();
78
+ for (const f of verdict.findings || []) {
79
+ const fp = findingFingerprint(f);
80
+ currentFingerprints.add(fp);
81
+ const existing = store.records.find((r) => r.fingerprint === fp);
82
+ if (existing) {
83
+ existing.lastSeen = now;
84
+ existing.ageInDays = daysBetween(existing.firstSeen, now);
85
+ existing.resolved = false;
86
+ existing.resolvedAt = "";
87
+ }
88
+ else {
89
+ store.records.push({
90
+ fingerprint: fp,
91
+ ruleId: f.ruleId || "",
92
+ title: f.title || "",
93
+ severity: f.severity || "unknown",
94
+ firstSeen: now,
95
+ lastSeen: now,
96
+ resolved: false,
97
+ resolvedAt: "",
98
+ ageInDays: 0,
99
+ });
100
+ }
101
+ }
102
+ // Mark resolved findings
103
+ for (const r of store.records) {
104
+ if (!currentFingerprints.has(r.fingerprint) && !r.resolved) {
105
+ r.resolved = true;
106
+ r.resolvedAt = now;
107
+ }
108
+ }
109
+ saveAgeStore(store);
110
+ console.log(`Updated ${store.records.length} finding age records.`);
111
+ return;
112
+ }
113
+ if (subcommand === "clear") {
114
+ saveAgeStore({ version: "1.0.0", records: [] });
115
+ console.log("Finding age records cleared.");
116
+ return;
117
+ }
118
+ // Show
119
+ const staleStr = argv.find((_a, i) => argv[i - 1] === "--stale");
120
+ const staleDays = staleStr ? parseInt(staleStr, 10) : 0;
121
+ let records = store.records.filter((r) => !r.resolved);
122
+ if (staleDays > 0) {
123
+ records = records.filter((r) => r.ageInDays >= staleDays);
124
+ }
125
+ records.sort((a, b) => b.ageInDays - a.ageInDays);
126
+ if (format === "json") {
127
+ console.log(JSON.stringify({ total: store.records.length, unresolved: records.length, staleDays, records }, null, 2));
128
+ return;
129
+ }
130
+ console.log(`\n Finding Age Report\n ═════════════════════════════`);
131
+ console.log(` Total tracked: ${store.records.length}`);
132
+ console.log(` Unresolved: ${records.length}`);
133
+ console.log(` Resolved: ${store.records.filter((r) => r.resolved).length}`);
134
+ if (staleDays > 0)
135
+ console.log(` Showing: older than ${staleDays} days`);
136
+ console.log();
137
+ if (records.length === 0) {
138
+ console.log(" No unresolved findings matching criteria.");
139
+ }
140
+ for (const r of records) {
141
+ const icon = r.ageInDays > 30 ? "🔴" : r.ageInDays > 7 ? "🟡" : "🟢";
142
+ console.log(` ${icon} ${r.ageInDays}d — [${r.severity.toUpperCase()}] ${r.title || r.ruleId} (since ${r.firstSeen.slice(0, 10)})`);
143
+ }
144
+ console.log();
145
+ }
146
+ //# sourceMappingURL=finding-age.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-age.js","sourceRoot":"","sources":["../../src/commands/finding-age.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Finding-rank — Rank findings by business impact and fix effort.
3
+ */
4
+ export declare function runFindingRank(argv: string[]): void;
5
+ //# sourceMappingURL=finding-rank.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-rank.d.ts","sourceRoot":"","sources":["../../src/commands/finding-rank.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2CH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+HnD"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Finding-rank — Rank findings by business impact and fix effort.
3
+ */
4
+ import { readFileSync, existsSync } from "fs";
5
+ // ─── Scoring ────────────────────────────────────────────────────────────────
6
+ const SEVERITY_IMPACT = {
7
+ critical: 100,
8
+ high: 75,
9
+ medium: 50,
10
+ low: 25,
11
+ info: 10,
12
+ };
13
+ function estimateEffort(f) {
14
+ // Estimated fix effort (1-10 scale): lower = easier to fix
15
+ if (f.patch)
16
+ return 2; // Has auto-fix
17
+ if (f.recommendation)
18
+ return 4; // Has guidance
19
+ const sev = (f.severity || "medium").toLowerCase();
20
+ if (sev === "low" || sev === "info")
21
+ return 3;
22
+ if (sev === "critical")
23
+ return 8;
24
+ return 5;
25
+ }
26
+ function computePriority(f) {
27
+ const impact = SEVERITY_IMPACT[(f.severity || "medium").toLowerCase()] || 50;
28
+ const conf = (f.confidence ?? 0.5) * 100;
29
+ const effort = estimateEffort(f);
30
+ // Priority = high impact + high confidence + low effort = higher score
31
+ return Math.round(impact * 0.5 + conf * 0.3 + (10 - effort) * 2);
32
+ }
33
+ // ─── CLI ────────────────────────────────────────────────────────────────────
34
+ export function runFindingRank(argv) {
35
+ if (argv.includes("--help") || argv.includes("-h")) {
36
+ console.log(`
37
+ judges finding-rank — Rank findings by business impact and fix effort
38
+
39
+ Usage:
40
+ judges finding-rank --file verdict.json Rank all findings
41
+ judges finding-rank --file verdict.json --top 10 Show top 10
42
+ judges finding-rank --file verdict.json --quick-wins Show easy high-impact fixes
43
+
44
+ Options:
45
+ --file <path> Verdict JSON to rank
46
+ --top <n> Show only top N findings
47
+ --quick-wins Show findings with high impact and low effort
48
+ --format json JSON output
49
+ --help, -h Show this help
50
+
51
+ Rankings prioritize by: severity (50%), confidence (30%),
52
+ and fix ease (20%). Quick-wins filter for high-impact, low-effort items.
53
+ `);
54
+ return;
55
+ }
56
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
57
+ const file = argv.find((_a, i) => argv[i - 1] === "--file");
58
+ const topN = parseInt(argv.find((_a, i) => argv[i - 1] === "--top") || "0", 10);
59
+ const quickWins = argv.includes("--quick-wins");
60
+ if (!file) {
61
+ console.error("Error: --file is required.");
62
+ process.exitCode = 1;
63
+ return;
64
+ }
65
+ if (!existsSync(file)) {
66
+ console.error(`Error: File not found: ${file}`);
67
+ process.exitCode = 1;
68
+ return;
69
+ }
70
+ let verdict;
71
+ try {
72
+ verdict = JSON.parse(readFileSync(file, "utf-8"));
73
+ }
74
+ catch {
75
+ console.error(`Error: Could not parse ${file}`);
76
+ process.exitCode = 1;
77
+ return;
78
+ }
79
+ const findings = verdict.findings || [];
80
+ let ranked = findings.map((f) => ({
81
+ rank: 0,
82
+ priority: computePriority(f),
83
+ impact: SEVERITY_IMPACT[(f.severity || "medium").toLowerCase()] || 50,
84
+ effort: estimateEffort(f),
85
+ finding: f,
86
+ }));
87
+ // Sort by priority descending
88
+ ranked.sort((a, b) => b.priority - a.priority);
89
+ // Assign ranks
90
+ ranked.forEach((r, i) => {
91
+ r.rank = i + 1;
92
+ });
93
+ // Filter quick wins: high impact (>=50), low effort (<=4)
94
+ if (quickWins) {
95
+ ranked = ranked.filter((r) => r.impact >= 50 && r.effort <= 4);
96
+ }
97
+ // Limit
98
+ if (topN > 0) {
99
+ ranked = ranked.slice(0, topN);
100
+ }
101
+ if (format === "json") {
102
+ console.log(JSON.stringify({
103
+ total: findings.length,
104
+ shown: ranked.length,
105
+ rankings: ranked.map((r) => ({
106
+ rank: r.rank,
107
+ priority: r.priority,
108
+ impact: r.impact,
109
+ effort: r.effort,
110
+ ruleId: r.finding.ruleId,
111
+ title: r.finding.title,
112
+ severity: r.finding.severity,
113
+ })),
114
+ }, null, 2));
115
+ return;
116
+ }
117
+ console.log(`\n Finding Rankings${quickWins ? " (Quick Wins)" : ""}\n ═════════════════════════════`);
118
+ console.log(` Total findings: ${findings.length}`);
119
+ console.log(` Showing: ${ranked.length}`);
120
+ console.log();
121
+ if (ranked.length === 0) {
122
+ console.log(" No findings match criteria.");
123
+ console.log();
124
+ return;
125
+ }
126
+ console.log(" Rank Priority Impact Effort Finding");
127
+ console.log(" ──── ──────── ────── ────── ───────");
128
+ for (const r of ranked) {
129
+ const sev = (r.finding.severity || "").toUpperCase().slice(0, 4).padEnd(4);
130
+ const title = (r.finding.title || r.finding.ruleId || "").slice(0, 40);
131
+ const hasPatch = r.finding.patch ? " 🔧" : "";
132
+ console.log(` #${String(r.rank).padEnd(4)} ${String(r.priority).padStart(4)} ${String(r.impact).padStart(4)} ${String(r.effort).padStart(4)} [${sev}] ${title}${hasPatch}`);
133
+ }
134
+ if (quickWins && ranked.length > 0) {
135
+ console.log(`\n 💡 ${ranked.length} quick win(s) — high impact, low effort`);
136
+ }
137
+ console.log();
138
+ }
139
+ //# sourceMappingURL=finding-rank.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-rank.js","sourceRoot":"","sources":["../../src/commands/finding-rank.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Review-dashboard — Terminal-based dashboard summary of review health.
3
+ */
4
+ export declare function runReviewDashboard(argv: string[]): void;
5
+ //# sourceMappingURL=review-dashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-dashboard.d.ts","sourceRoot":"","sources":["../../src/commands/review-dashboard.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqJvD"}