@kevinrabun/judges 3.58.0 → 3.60.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 +24 -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/adoption-track.d.ts +5 -0
- package/dist/commands/adoption-track.d.ts.map +1 -0
- package/dist/commands/adoption-track.js +247 -0
- package/dist/commands/adoption-track.js.map +1 -0
- package/dist/commands/api-misuse.d.ts +5 -0
- package/dist/commands/api-misuse.d.ts.map +1 -0
- package/dist/commands/api-misuse.js +261 -0
- package/dist/commands/api-misuse.js.map +1 -0
- package/dist/commands/completion-audit.d.ts +5 -0
- package/dist/commands/completion-audit.d.ts.map +1 -0
- package/dist/commands/completion-audit.js +297 -0
- package/dist/commands/completion-audit.js.map +1 -0
- package/dist/commands/context-blind.d.ts +5 -0
- package/dist/commands/context-blind.d.ts.map +1 -0
- package/dist/commands/context-blind.js +273 -0
- package/dist/commands/context-blind.js.map +1 -0
- package/dist/commands/cross-file-consistency.d.ts +5 -0
- package/dist/commands/cross-file-consistency.d.ts.map +1 -0
- package/dist/commands/cross-file-consistency.js +255 -0
- package/dist/commands/cross-file-consistency.js.map +1 -0
- package/dist/commands/example-leak.d.ts +5 -0
- package/dist/commands/example-leak.d.ts.map +1 -0
- package/dist/commands/example-leak.js +233 -0
- package/dist/commands/example-leak.js.map +1 -0
- package/dist/commands/finding-budget.d.ts +5 -0
- package/dist/commands/finding-budget.d.ts.map +1 -0
- package/dist/commands/finding-budget.js +233 -0
- package/dist/commands/finding-budget.js.map +1 -0
- package/dist/commands/hallucination-detect.d.ts +5 -0
- package/dist/commands/hallucination-detect.d.ts.map +1 -0
- package/dist/commands/hallucination-detect.js +351 -0
- package/dist/commands/hallucination-detect.js.map +1 -0
- package/dist/commands/logic-lint.d.ts +5 -0
- package/dist/commands/logic-lint.d.ts.map +1 -0
- package/dist/commands/logic-lint.js +256 -0
- package/dist/commands/logic-lint.js.map +1 -0
- package/dist/commands/over-abstraction.d.ts +5 -0
- package/dist/commands/over-abstraction.d.ts.map +1 -0
- package/dist/commands/over-abstraction.js +308 -0
- package/dist/commands/over-abstraction.js.map +1 -0
- package/dist/commands/phantom-import.d.ts +5 -0
- package/dist/commands/phantom-import.d.ts.map +1 -0
- package/dist/commands/phantom-import.js +261 -0
- package/dist/commands/phantom-import.js.map +1 -0
- package/dist/commands/review-digest.d.ts +5 -0
- package/dist/commands/review-digest.d.ts.map +1 -0
- package/dist/commands/review-digest.js +266 -0
- package/dist/commands/review-digest.js.map +1 -0
- package/dist/commands/review-focus.d.ts +5 -0
- package/dist/commands/review-focus.d.ts.map +1 -0
- package/dist/commands/review-focus.js +197 -0
- package/dist/commands/review-focus.js.map +1 -0
- package/dist/commands/security-theater.d.ts +5 -0
- package/dist/commands/security-theater.d.ts.map +1 -0
- package/dist/commands/security-theater.js +279 -0
- package/dist/commands/security-theater.js.map +1 -0
- package/dist/commands/spec-conform.d.ts +5 -0
- package/dist/commands/spec-conform.d.ts.map +1 -0
- package/dist/commands/spec-conform.js +305 -0
- package/dist/commands/spec-conform.js.map +1 -0
- package/dist/commands/stale-pattern.d.ts +5 -0
- package/dist/commands/stale-pattern.d.ts.map +1 -0
- package/dist/commands/stale-pattern.js +294 -0
- package/dist/commands/stale-pattern.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logic lint — detect common logic errors that AI code generators produce.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
5
|
+
import { join, extname } from "path";
|
|
6
|
+
// ─── File Collection ────────────────────────────────────────────────────────
|
|
7
|
+
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs", ".cs"]);
|
|
8
|
+
function collectFiles(dir, max = 300) {
|
|
9
|
+
const files = [];
|
|
10
|
+
function walk(d) {
|
|
11
|
+
if (files.length >= max)
|
|
12
|
+
return;
|
|
13
|
+
let entries;
|
|
14
|
+
try {
|
|
15
|
+
entries = readdirSync(d);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const e of entries) {
|
|
21
|
+
if (files.length >= max)
|
|
22
|
+
return;
|
|
23
|
+
if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
|
|
24
|
+
continue;
|
|
25
|
+
const full = join(d, e);
|
|
26
|
+
try {
|
|
27
|
+
if (statSync(full).isDirectory())
|
|
28
|
+
walk(full);
|
|
29
|
+
else if (CODE_EXTS.has(extname(full)))
|
|
30
|
+
files.push(full);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* skip */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
walk(dir);
|
|
38
|
+
return files;
|
|
39
|
+
}
|
|
40
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
41
|
+
function analyzeFile(filepath) {
|
|
42
|
+
const issues = [];
|
|
43
|
+
let content;
|
|
44
|
+
try {
|
|
45
|
+
content = readFileSync(filepath, "utf-8");
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return issues;
|
|
49
|
+
}
|
|
50
|
+
const lines = content.split("\n");
|
|
51
|
+
for (let i = 0; i < lines.length; i++) {
|
|
52
|
+
const line = lines[i];
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
// Tautological comparison: x === x, x !== x
|
|
55
|
+
if (/(\w+)\s*===\s*\1(?!\w)/.test(trimmed) || /(\w+)\s*!==\s*\1(?!\w)/.test(trimmed)) {
|
|
56
|
+
const match = trimmed.match(/(\w+)\s*[!=]==\s*\1(?!\w)/);
|
|
57
|
+
if (match && match[1] !== "NaN") {
|
|
58
|
+
issues.push({
|
|
59
|
+
file: filepath,
|
|
60
|
+
line: i + 1,
|
|
61
|
+
issue: "Tautological comparison",
|
|
62
|
+
severity: "high",
|
|
63
|
+
detail: `\`${match[1]}\` compared to itself — always true (===) or always false (!==)`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Assignment in condition (single = in if/while)
|
|
68
|
+
if (/(?:if|while)\s*\(\s*[^=!<>]*[^=!<>]=[^=]/.test(trimmed) &&
|
|
69
|
+
!/==|!=|<=|>=/.test(trimmed.replace(/=[^=]/, "XX"))) {
|
|
70
|
+
if (!/===|!==/.test(trimmed)) {
|
|
71
|
+
issues.push({
|
|
72
|
+
file: filepath,
|
|
73
|
+
line: i + 1,
|
|
74
|
+
issue: "Assignment in condition",
|
|
75
|
+
severity: "high",
|
|
76
|
+
detail: "Single `=` in if/while condition — likely meant `===` or `==`",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Unreachable code after return/throw/break/continue
|
|
81
|
+
if (/^\s*(?:return|throw|break|continue)\b/.test(trimmed) && !trimmed.endsWith("{")) {
|
|
82
|
+
const nextLine = (lines[i + 1] || "").trim();
|
|
83
|
+
if (nextLine &&
|
|
84
|
+
nextLine !== "}" &&
|
|
85
|
+
nextLine !== "}" &&
|
|
86
|
+
!/^\s*(?:case|default|\/\/|\/\*|\*|else|catch|finally)/.test(nextLine)) {
|
|
87
|
+
issues.push({
|
|
88
|
+
file: filepath,
|
|
89
|
+
line: i + 2,
|
|
90
|
+
issue: "Unreachable code after flow control",
|
|
91
|
+
severity: "medium",
|
|
92
|
+
detail: "Code after return/throw/break/continue is never executed",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Off-by-one: <= array.length in loop (should be <)
|
|
97
|
+
if (/for\s*\([^;]*;\s*\w+\s*<=\s*\w+\.length\s*;/.test(trimmed)) {
|
|
98
|
+
issues.push({
|
|
99
|
+
file: filepath,
|
|
100
|
+
line: i + 1,
|
|
101
|
+
issue: "Off-by-one in loop bound",
|
|
102
|
+
severity: "high",
|
|
103
|
+
detail: "`<= .length` iterates one past the end — use `< .length`",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// Constant condition in if/while
|
|
107
|
+
if (/(?:if|while)\s*\(\s*(?:true|false|1|0|null|undefined)\s*\)/.test(trimmed)) {
|
|
108
|
+
if (!/while\s*\(\s*true\s*\)/.test(trimmed)) {
|
|
109
|
+
issues.push({
|
|
110
|
+
file: filepath,
|
|
111
|
+
line: i + 1,
|
|
112
|
+
issue: "Constant condition",
|
|
113
|
+
severity: "medium",
|
|
114
|
+
detail: "Condition is always true or always false — branch is never taken or always taken",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Inverted null check: if (x) { x = ... } vs if (!x) { x = ... }
|
|
119
|
+
if (/if\s*\(\s*(\w+)\s*\)\s*\{/.test(trimmed)) {
|
|
120
|
+
const varName = trimmed.match(/if\s*\(\s*(\w+)\s*\)/)?.[1];
|
|
121
|
+
const nextLines = lines.slice(i + 1, Math.min(i + 3, lines.length)).join("\n");
|
|
122
|
+
if (varName && new RegExp(`${varName}\\s*=\\s*(?:null|undefined|"")`).test(nextLines)) {
|
|
123
|
+
issues.push({
|
|
124
|
+
file: filepath,
|
|
125
|
+
line: i + 1,
|
|
126
|
+
issue: "Likely inverted null check",
|
|
127
|
+
severity: "medium",
|
|
128
|
+
detail: `Checking \`${varName}\` is truthy then setting it to null — did you mean \`!${varName}\`?`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Mismatched operator precedence: a && b || c (missing parentheses)
|
|
133
|
+
if (/\w+\s*&&\s*\w+\s*\|\|\s*\w+/.test(trimmed) && !trimmed.includes("(")) {
|
|
134
|
+
issues.push({
|
|
135
|
+
file: filepath,
|
|
136
|
+
line: i + 1,
|
|
137
|
+
issue: "Ambiguous operator precedence",
|
|
138
|
+
severity: "low",
|
|
139
|
+
detail: "`&&` and `||` mixed without parentheses — intention is unclear",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Empty catch block
|
|
143
|
+
if (/catch\s*\([^)]*\)\s*\{\s*\}/.test(trimmed)) {
|
|
144
|
+
issues.push({
|
|
145
|
+
file: filepath,
|
|
146
|
+
line: i + 1,
|
|
147
|
+
issue: "Empty catch block",
|
|
148
|
+
severity: "medium",
|
|
149
|
+
detail: "Errors silently swallowed — at minimum log the error",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// typeof compared to wrong string
|
|
153
|
+
if (/typeof\s+\w+\s*===?\s*['"]/.test(trimmed)) {
|
|
154
|
+
const typeVal = trimmed.match(/typeof\s+\w+\s*===?\s*['"]([\w]+)['"]/)?.[1];
|
|
155
|
+
const validTypes = new Set([
|
|
156
|
+
"string",
|
|
157
|
+
"number",
|
|
158
|
+
"boolean",
|
|
159
|
+
"object",
|
|
160
|
+
"function",
|
|
161
|
+
"undefined",
|
|
162
|
+
"symbol",
|
|
163
|
+
"bigint",
|
|
164
|
+
]);
|
|
165
|
+
if (typeVal && !validTypes.has(typeVal)) {
|
|
166
|
+
issues.push({
|
|
167
|
+
file: filepath,
|
|
168
|
+
line: i + 1,
|
|
169
|
+
issue: "Invalid typeof comparison",
|
|
170
|
+
severity: "high",
|
|
171
|
+
detail: `typeof never returns "${typeVal}" — valid values: ${[...validTypes].join(", ")}`,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Doubled negation logic: !!x === false or !(!x)
|
|
176
|
+
if (/!!\w+\s*===?\s*false/.test(trimmed) || /!\s*\(\s*!\s*\w+\s*\)/.test(trimmed)) {
|
|
177
|
+
issues.push({
|
|
178
|
+
file: filepath,
|
|
179
|
+
line: i + 1,
|
|
180
|
+
issue: "Redundant double negation",
|
|
181
|
+
severity: "low",
|
|
182
|
+
detail: "Double negation with false comparison — simplify the expression",
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
// Floating point equality
|
|
186
|
+
if (/(?:===?|!==?)\s*(?:\d+\.\d+|Math\.\w+)/.test(trimmed) && !/\.length|\.size|\.count|\.index/.test(trimmed)) {
|
|
187
|
+
if (/\d+\.\d+/.test(trimmed)) {
|
|
188
|
+
issues.push({
|
|
189
|
+
file: filepath,
|
|
190
|
+
line: i + 1,
|
|
191
|
+
issue: "Floating-point equality",
|
|
192
|
+
severity: "medium",
|
|
193
|
+
detail: "Exact equality with floating-point values is unreliable — use tolerance comparison (Math.abs(a-b) < epsilon)",
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return issues;
|
|
199
|
+
}
|
|
200
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
201
|
+
export function runLogicLint(argv) {
|
|
202
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
203
|
+
console.log(`
|
|
204
|
+
judges logic-lint — Detect common logic errors in AI-generated code
|
|
205
|
+
|
|
206
|
+
Usage:
|
|
207
|
+
judges logic-lint [dir]
|
|
208
|
+
judges logic-lint src/ --format json
|
|
209
|
+
|
|
210
|
+
Options:
|
|
211
|
+
[dir] Directory to scan (default: .)
|
|
212
|
+
--format json JSON output
|
|
213
|
+
--help, -h Show this help
|
|
214
|
+
|
|
215
|
+
Checks: tautological comparisons, assignment in conditions, off-by-one loops,
|
|
216
|
+
unreachable code, constant conditions, inverted null checks, ambiguous precedence,
|
|
217
|
+
empty catch blocks, invalid typeof, floating-point equality.
|
|
218
|
+
`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
222
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
223
|
+
const files = collectFiles(dir);
|
|
224
|
+
const allIssues = [];
|
|
225
|
+
for (const f of files)
|
|
226
|
+
allIssues.push(...analyzeFile(f));
|
|
227
|
+
const highCount = allIssues.filter((i) => i.severity === "high").length;
|
|
228
|
+
const medCount = allIssues.filter((i) => i.severity === "medium").length;
|
|
229
|
+
const score = Math.max(0, 100 - highCount * 12 - medCount * 5);
|
|
230
|
+
if (format === "json") {
|
|
231
|
+
console.log(JSON.stringify({
|
|
232
|
+
issues: allIssues,
|
|
233
|
+
score,
|
|
234
|
+
summary: { high: highCount, medium: medCount, total: allIssues.length },
|
|
235
|
+
timestamp: new Date().toISOString(),
|
|
236
|
+
}, null, 2));
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
const badge = score >= 80 ? "✅ CLEAN" : score >= 50 ? "⚠️ SUSPECT" : "❌ BUGGY";
|
|
240
|
+
console.log(`\n Logic Lint: ${badge} (${score}/100)\n ─────────────────────────────`);
|
|
241
|
+
if (allIssues.length === 0) {
|
|
242
|
+
console.log(" No logic issues detected.\n");
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
for (const issue of allIssues.slice(0, 25)) {
|
|
246
|
+
const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
|
|
247
|
+
console.log(` ${icon} ${issue.issue}`);
|
|
248
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
249
|
+
console.log(` ${issue.detail}`);
|
|
250
|
+
}
|
|
251
|
+
if (allIssues.length > 25)
|
|
252
|
+
console.log(` ... and ${allIssues.length - 25} more`);
|
|
253
|
+
console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=logic-lint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logic-lint.js","sourceRoot":"","sources":["../../src/commands/logic-lint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"over-abstraction.d.ts","sourceRoot":"","sources":["../../src/commands/over-abstraction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAyQH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA0EvD"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Over-abstraction — detect unnecessary abstractions and premature generalization from AI.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
5
|
+
import { join, extname } from "path";
|
|
6
|
+
// ─── File Collection ────────────────────────────────────────────────────────
|
|
7
|
+
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
8
|
+
function collectFiles(dir, max = 300) {
|
|
9
|
+
const files = [];
|
|
10
|
+
function walk(d) {
|
|
11
|
+
if (files.length >= max)
|
|
12
|
+
return;
|
|
13
|
+
let entries;
|
|
14
|
+
try {
|
|
15
|
+
entries = readdirSync(d);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const e of entries) {
|
|
21
|
+
if (files.length >= max)
|
|
22
|
+
return;
|
|
23
|
+
if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
|
|
24
|
+
continue;
|
|
25
|
+
const full = join(d, e);
|
|
26
|
+
try {
|
|
27
|
+
if (statSync(full).isDirectory())
|
|
28
|
+
walk(full);
|
|
29
|
+
else if (CODE_EXTS.has(extname(full)))
|
|
30
|
+
files.push(full);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* skip */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
walk(dir);
|
|
38
|
+
return files;
|
|
39
|
+
}
|
|
40
|
+
// ─── Cross-file data ────────────────────────────────────────────────────────
|
|
41
|
+
function buildUsageIndex(files) {
|
|
42
|
+
const usageCount = new Map();
|
|
43
|
+
const allContent = new Map();
|
|
44
|
+
for (const f of files) {
|
|
45
|
+
try {
|
|
46
|
+
allContent.set(f, readFileSync(f, "utf-8"));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* skip */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Count how many files reference each exported name
|
|
53
|
+
for (const [_file, content] of allContent) {
|
|
54
|
+
const exportNames = [
|
|
55
|
+
...content.matchAll(/export\s+(?:default\s+)?(?:function|class|const|let|var|type|interface|enum|abstract\s+class)\s+(\w+)/g),
|
|
56
|
+
].map((m) => m[1]);
|
|
57
|
+
for (const name of exportNames) {
|
|
58
|
+
let count = 0;
|
|
59
|
+
for (const [otherFile, otherContent] of allContent) {
|
|
60
|
+
if (otherFile === _file)
|
|
61
|
+
continue;
|
|
62
|
+
if (new RegExp(`\\b${name}\\b`).test(otherContent))
|
|
63
|
+
count++;
|
|
64
|
+
}
|
|
65
|
+
usageCount.set(name, count);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return usageCount;
|
|
69
|
+
}
|
|
70
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
71
|
+
function analyzeFile(filepath, usageCount, allContents) {
|
|
72
|
+
const issues = [];
|
|
73
|
+
let content;
|
|
74
|
+
try {
|
|
75
|
+
content = readFileSync(filepath, "utf-8");
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return issues;
|
|
79
|
+
}
|
|
80
|
+
const lines = content.split("\n");
|
|
81
|
+
for (let i = 0; i < lines.length; i++) {
|
|
82
|
+
const line = lines[i];
|
|
83
|
+
// Abstract class with single subclass
|
|
84
|
+
if (/(?:export\s+)?abstract\s+class\s+(\w+)/.test(line)) {
|
|
85
|
+
const className = line.match(/abstract\s+class\s+(\w+)/)?.[1];
|
|
86
|
+
if (className) {
|
|
87
|
+
let subclassCount = 0;
|
|
88
|
+
for (const [_f, c] of allContents) {
|
|
89
|
+
const extends_ = (c.match(new RegExp(`extends\\s+${className}\\b`, "g")) || []).length;
|
|
90
|
+
subclassCount += extends_;
|
|
91
|
+
}
|
|
92
|
+
if (subclassCount <= 1) {
|
|
93
|
+
issues.push({
|
|
94
|
+
file: filepath,
|
|
95
|
+
line: i + 1,
|
|
96
|
+
issue: "Abstract class with single implementation",
|
|
97
|
+
severity: "medium",
|
|
98
|
+
detail: `\`${className}\` has ${subclassCount} subclass(es) — abstract base is premature; inline into the concrete class`,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Interface with single implementation
|
|
104
|
+
if (/(?:export\s+)?interface\s+(\w+)\s*\{/.test(line)) {
|
|
105
|
+
const ifaceName = line.match(/interface\s+(\w+)/)?.[1];
|
|
106
|
+
if (ifaceName &&
|
|
107
|
+
!ifaceName.endsWith("Props") &&
|
|
108
|
+
!ifaceName.endsWith("Options") &&
|
|
109
|
+
!ifaceName.endsWith("Config")) {
|
|
110
|
+
let implCount = 0;
|
|
111
|
+
for (const [_f, c] of allContents) {
|
|
112
|
+
if (new RegExp(`implements\\s+${ifaceName}\\b`).test(c))
|
|
113
|
+
implCount++;
|
|
114
|
+
if (new RegExp(`:\\s*${ifaceName}\\b`).test(c))
|
|
115
|
+
implCount++;
|
|
116
|
+
}
|
|
117
|
+
const uses = usageCount.get(ifaceName) || 0;
|
|
118
|
+
if (implCount <= 1 && uses <= 1 && !/export/.test(line)) {
|
|
119
|
+
issues.push({
|
|
120
|
+
file: filepath,
|
|
121
|
+
line: i + 1,
|
|
122
|
+
issue: "Interface used only once",
|
|
123
|
+
severity: "low",
|
|
124
|
+
detail: `\`${ifaceName}\` has single implementation and minimal usage — may be premature abstraction`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Generic type parameter used in one instantiation
|
|
130
|
+
if (/(?:function|class)\s+\w+\s*<(\w+)/.test(line)) {
|
|
131
|
+
const genericParam = line.match(/<(\w+)(?:\s+extends)?/)?.[1];
|
|
132
|
+
if (genericParam && genericParam !== "T") {
|
|
133
|
+
// This is a heuristic — single-letter generics are normal
|
|
134
|
+
const body = lines.slice(i, Math.min(i + 30, lines.length)).join("\n");
|
|
135
|
+
const usages = (body.match(new RegExp(`\\b${genericParam}\\b`, "g")) || []).length;
|
|
136
|
+
if (usages <= 2) {
|
|
137
|
+
issues.push({
|
|
138
|
+
file: filepath,
|
|
139
|
+
line: i + 1,
|
|
140
|
+
issue: "Generic type parameter barely used",
|
|
141
|
+
severity: "low",
|
|
142
|
+
detail: `Generic \`<${genericParam}>\` used only ${usages} times in body — concrete type may suffice`,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Factory function that creates only one type
|
|
148
|
+
if (/(?:function|const)\s+create(\w+)\s*(?:=\s*\(|\()/.test(line)) {
|
|
149
|
+
const factoryName = line.match(/(?:function|const)\s+(create\w+)/)?.[1];
|
|
150
|
+
if (factoryName) {
|
|
151
|
+
const block = lines.slice(i, Math.min(i + 20, lines.length)).join("\n");
|
|
152
|
+
const newCount = (block.match(/new\s+\w+/g) || []).length;
|
|
153
|
+
const returnCount = (block.match(/return\s+(?:new\s+)?\w+/g) || []).length;
|
|
154
|
+
if (newCount === 1 && returnCount <= 1) {
|
|
155
|
+
issues.push({
|
|
156
|
+
file: filepath,
|
|
157
|
+
line: i + 1,
|
|
158
|
+
issue: "Factory that creates single type",
|
|
159
|
+
severity: "low",
|
|
160
|
+
detail: `\`${factoryName}\` creates one concrete type — factory pattern adds unnecessary indirection`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Wrapper function that only delegates
|
|
166
|
+
if (/(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/.test(line)) {
|
|
167
|
+
const funcName = line.match(/function\s+(\w+)/)?.[1];
|
|
168
|
+
const block = lines
|
|
169
|
+
.slice(i + 1, Math.min(i + 5, lines.length))
|
|
170
|
+
.join("\n")
|
|
171
|
+
.trim();
|
|
172
|
+
// Function body is just "return otherFunc(args)" or similar
|
|
173
|
+
if (/^(?:\{?\s*)?return\s+\w+\s*\([^)]*\)\s*;?\s*\}?\s*$/.test(block)) {
|
|
174
|
+
issues.push({
|
|
175
|
+
file: filepath,
|
|
176
|
+
line: i + 1,
|
|
177
|
+
issue: "Wrapper function with no added logic",
|
|
178
|
+
severity: "medium",
|
|
179
|
+
detail: `\`${funcName}\` only delegates to another function — remove the wrapper and call directly`,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Config object for single value
|
|
184
|
+
if (/(?:interface|type)\s+(\w*Config\w*|\w*Options\w*)\s*[={]/.test(line)) {
|
|
185
|
+
const configName = line.match(/(?:interface|type)\s+(\w+)/)?.[1];
|
|
186
|
+
if (configName) {
|
|
187
|
+
let depth = 0;
|
|
188
|
+
let end = i;
|
|
189
|
+
let started = false;
|
|
190
|
+
for (let j = i; j < Math.min(i + 20, lines.length); j++) {
|
|
191
|
+
for (const ch of lines[j]) {
|
|
192
|
+
if (ch === "{") {
|
|
193
|
+
depth++;
|
|
194
|
+
started = true;
|
|
195
|
+
}
|
|
196
|
+
if (ch === "}")
|
|
197
|
+
depth--;
|
|
198
|
+
}
|
|
199
|
+
if (started && depth <= 0) {
|
|
200
|
+
end = j;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const body = lines.slice(i + 1, end).join("\n");
|
|
205
|
+
const fieldCount = (body.match(/\w+\s*[?:]?\s*:/g) || []).length;
|
|
206
|
+
if (fieldCount === 1) {
|
|
207
|
+
issues.push({
|
|
208
|
+
file: filepath,
|
|
209
|
+
line: i + 1,
|
|
210
|
+
issue: "Config type with single field",
|
|
211
|
+
severity: "low",
|
|
212
|
+
detail: `\`${configName}\` has only 1 field — pass the value directly instead of wrapping in config object`,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Strategy pattern with single strategy
|
|
218
|
+
if (/(?:Strategy|Handler|Provider|Adapter)\s*[{<\]]/.test(line) && /interface|type/.test(line)) {
|
|
219
|
+
const patternName = line.match(/(\w+(?:Strategy|Handler|Provider|Adapter))/)?.[1];
|
|
220
|
+
if (patternName) {
|
|
221
|
+
let implCount = 0;
|
|
222
|
+
for (const [_f, c] of allContents) {
|
|
223
|
+
if (new RegExp(`implements\\s+${patternName}\\b`).test(c))
|
|
224
|
+
implCount++;
|
|
225
|
+
if (new RegExp(`class\\s+\\w+${patternName.replace(/^I/, "")}\\b`).test(c))
|
|
226
|
+
implCount++;
|
|
227
|
+
}
|
|
228
|
+
if (implCount <= 1) {
|
|
229
|
+
issues.push({
|
|
230
|
+
file: filepath,
|
|
231
|
+
line: i + 1,
|
|
232
|
+
issue: "Pattern interface with single implementation",
|
|
233
|
+
severity: "medium",
|
|
234
|
+
detail: `\`${patternName}\` has ${implCount} implementation(s) — strategy/adapter pattern is premature`,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return issues;
|
|
241
|
+
}
|
|
242
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
243
|
+
export function runOverAbstraction(argv) {
|
|
244
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
245
|
+
console.log(`
|
|
246
|
+
judges over-abstraction — Detect unnecessary abstractions from AI-generated code
|
|
247
|
+
|
|
248
|
+
Usage:
|
|
249
|
+
judges over-abstraction [dir]
|
|
250
|
+
judges over-abstraction src/ --format json
|
|
251
|
+
|
|
252
|
+
Options:
|
|
253
|
+
[dir] Directory to scan (default: .)
|
|
254
|
+
--format json JSON output
|
|
255
|
+
--help, -h Show this help
|
|
256
|
+
|
|
257
|
+
Checks: abstract classes with single subclass, single-implementation interfaces,
|
|
258
|
+
barely-used generics, single-type factories, delegation-only wrappers,
|
|
259
|
+
single-field config types, single-impl strategy/adapter patterns.
|
|
260
|
+
`);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
264
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
265
|
+
const files = collectFiles(dir);
|
|
266
|
+
const usageCount = buildUsageIndex(files);
|
|
267
|
+
const allContents = new Map();
|
|
268
|
+
for (const f of files) {
|
|
269
|
+
try {
|
|
270
|
+
allContents.set(f, readFileSync(f, "utf-8"));
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
/* skip */
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const allIssues = [];
|
|
277
|
+
for (const f of files)
|
|
278
|
+
allIssues.push(...analyzeFile(f, usageCount, allContents));
|
|
279
|
+
const highCount = allIssues.filter((i) => i.severity === "high").length;
|
|
280
|
+
const medCount = allIssues.filter((i) => i.severity === "medium").length;
|
|
281
|
+
const score = Math.max(0, 100 - highCount * 10 - medCount * 5 - allIssues.filter((i) => i.severity === "low").length * 2);
|
|
282
|
+
if (format === "json") {
|
|
283
|
+
console.log(JSON.stringify({
|
|
284
|
+
issues: allIssues,
|
|
285
|
+
score,
|
|
286
|
+
summary: { high: highCount, medium: medCount, total: allIssues.length },
|
|
287
|
+
timestamp: new Date().toISOString(),
|
|
288
|
+
}, null, 2));
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
const badge = score >= 80 ? "✅ LEAN" : score >= 50 ? "⚠️ OVER-BUILT" : "❌ OVER-ENGINEERED";
|
|
292
|
+
console.log(`\n Over-Abstraction: ${badge} (${score}/100)\n ─────────────────────────────`);
|
|
293
|
+
if (allIssues.length === 0) {
|
|
294
|
+
console.log(" No over-abstraction detected.\n");
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
for (const issue of allIssues.slice(0, 25)) {
|
|
298
|
+
const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
|
|
299
|
+
console.log(` ${icon} ${issue.issue}`);
|
|
300
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
301
|
+
console.log(` ${issue.detail}`);
|
|
302
|
+
}
|
|
303
|
+
if (allIssues.length > 25)
|
|
304
|
+
console.log(` ... and ${allIssues.length - 25} more`);
|
|
305
|
+
console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=over-abstraction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"over-abstraction.js","sourceRoot":"","sources":["../../src/commands/over-abstraction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phantom-import.d.ts","sourceRoot":"","sources":["../../src/commands/phantom-import.ts"],"names":[],"mappings":"AAAA;;GAEG;AA8NH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8DrD"}
|