@stupify/cli 0.0.3 → 0.0.5
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/README.md +26 -31
- package/dist/analysis.d.ts +11 -9
- package/dist/analysis.js +30 -173
- package/dist/checks.d.ts +1 -0
- package/dist/checks.js +89 -2
- package/dist/command.js +55 -91
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/counter-scout.js +70 -8
- package/dist/doctor.d.ts +4 -0
- package/dist/doctor.js +131 -0
- package/dist/git.d.ts +4 -1
- package/dist/git.js +34 -0
- package/dist/hooks.d.ts +3 -0
- package/dist/hooks.js +117 -0
- package/dist/model.d.ts +1 -15
- package/dist/model.js +37 -21
- package/dist/prompts.d.ts +8 -5
- package/dist/prompts.js +58 -168
- package/dist/render.d.ts +2 -2
- package/dist/render.js +70 -78
- package/dist/repomix-provider.d.ts +10 -2
- package/dist/repomix-provider.js +62 -11
- package/dist/search-bench.d.ts +1 -0
- package/dist/search-bench.js +675 -0
- package/dist/search-profile.d.ts +6 -0
- package/dist/search-profile.js +73 -0
- package/dist/sem-provider.d.ts +2 -2
- package/dist/sem-provider.js +33 -7
- package/dist/stupify.d.ts +2 -0
- package/dist/stupify.js +185 -334
- package/dist/types.d.ts +193 -109
- package/package.json +1 -1
- package/src/analysis.ts +48 -268
- package/src/checks.ts +91 -2
- package/src/command.ts +62 -107
- package/src/constants.ts +1 -1
- package/src/counter-scout.ts +63 -7
- package/src/doctor.ts +140 -0
- package/src/git.ts +35 -1
- package/src/hooks.ts +134 -0
- package/src/model.ts +39 -26
- package/src/prompts.ts +66 -202
- package/src/render.ts +68 -79
- package/src/repomix-provider.ts +66 -10
- package/src/search-bench.ts +783 -0
- package/src/search-profile.ts +89 -0
- package/src/sem-provider.ts +36 -9
- package/src/stupify.ts +215 -527
- package/src/types.ts +195 -119
- package/dist/batcher.d.ts +0 -3
- package/dist/batcher.js +0 -142
- package/dist/candidate-context.d.ts +0 -2
- package/dist/candidate-context.js +0 -40
- package/dist/experiment.d.ts +0 -1
- package/dist/experiment.js +0 -225
- package/src/batcher.ts +0 -198
- package/src/candidate-context.ts +0 -43
- package/src/experiment.ts +0 -317
package/README.md
CHANGED
|
@@ -2,59 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
Local-only diagnostic CLI for checking whether AI is making you dumber.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
line-sized diff batches. The sem engine uses entity-level changes, scouts
|
|
7
|
-
candidate entity IDs, optionally packs selected candidate files with Repomix,
|
|
8
|
-
and prints findings.
|
|
5
|
+
Stupify has one analysis path:
|
|
9
6
|
|
|
10
|
-
```
|
|
11
|
-
|
|
7
|
+
```text
|
|
8
|
+
sem diff -> counter scout -> Repomix context -> local search model
|
|
12
9
|
```
|
|
13
10
|
|
|
11
|
+
It emits search `matches`, not audit findings.
|
|
12
|
+
|
|
14
13
|
```sh
|
|
15
|
-
npx @stupify/cli --
|
|
14
|
+
npx @stupify/cli --staged
|
|
15
|
+
npx @stupify/cli --since "2 weeks ago"
|
|
16
|
+
npx @stupify/cli --commit HEAD
|
|
17
|
+
npx @stupify/cli --commits 20
|
|
18
|
+
git diff HEAD~1..HEAD | npx @stupify/cli --stdin
|
|
16
19
|
```
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
prompt exceeds `--max-audit-input-tokens`. Use `--audit-concurrency` to tune
|
|
20
|
-
parallel local audit calls. The default sem scout uses fast deterministic
|
|
21
|
-
signal counters; use `--scout llm` to compare against the older local model
|
|
22
|
-
scout. Use `--audit-context none|repomix` and `--audit-prompt strict|high_bar`
|
|
23
|
-
to run audit ablations without changing code.
|
|
21
|
+
Install the warn-only pre-commit hook:
|
|
24
22
|
|
|
25
23
|
```sh
|
|
26
|
-
stupify
|
|
27
|
-
stupify experiment experiments/bevyl-per-check.json
|
|
24
|
+
stupify hook install
|
|
28
25
|
```
|
|
29
26
|
|
|
30
|
-
The
|
|
31
|
-
manual-label markdown files under `experiments/results/`.
|
|
27
|
+
The hook runs `stupify --staged` and exits 0.
|
|
32
28
|
|
|
33
|
-
|
|
34
|
-
Commit mode analyzes `<commit>^..commit` as a net diff.
|
|
35
|
-
The default registry currently runs nine concise checks for duplicated schemas,
|
|
36
|
-
unnecessary complexity, fake precision, noisy metadata, mega-files,
|
|
37
|
-
over-commenting, lint bypasses, inconsistent patterns, and reinvented
|
|
38
|
-
utilities. `operator_style_mismatch` remains available with `--checks`, but is
|
|
39
|
-
not enabled by default in the sem audit path.
|
|
29
|
+
Check local setup:
|
|
40
30
|
|
|
41
31
|
```sh
|
|
42
|
-
|
|
32
|
+
stupify doctor
|
|
43
33
|
```
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
Default search enables the checks that currently pass the local hook-safety
|
|
36
|
+
bench: `duplicated_schema`, `unnecessary_complexity`, `over_commenting`,
|
|
37
|
+
`lint_bypass`, and `reinvented_utils`. Other registry patterns can be opted in
|
|
38
|
+
with `--checks`.
|
|
47
39
|
|
|
48
40
|
```sh
|
|
49
|
-
|
|
41
|
+
stupify --staged --checks over_commenting
|
|
50
42
|
```
|
|
51
43
|
|
|
44
|
+
Large search inputs are skipped rather than truncated:
|
|
45
|
+
|
|
52
46
|
```sh
|
|
53
|
-
stupify --
|
|
47
|
+
stupify --staged --max-search-input-tokens 24000
|
|
54
48
|
```
|
|
55
49
|
|
|
56
50
|
The package is prepared for the public `@stupify` npm scope. Publishing should
|
|
57
51
|
run the TypeScript build first so the executable points at `dist/stupify.js`.
|
|
58
52
|
|
|
59
|
-
This iteration intentionally does not
|
|
60
|
-
LLM APIs,
|
|
53
|
+
This iteration intentionally does not run findings audit, validators, judges,
|
|
54
|
+
baselines, hosted LLM APIs, GitHub integration, dashboards, or repo-wide
|
|
55
|
+
crawling.
|
package/dist/analysis.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { LocalModel } from "./model.ts";
|
|
2
|
-
import type {
|
|
3
|
-
export declare function
|
|
4
|
-
export
|
|
5
|
-
export declare function scoutSemChanges(model: LocalModel, changeSet: SemChangeSet, checks: readonly StupifyCheck[], maxCandidates: number): Promise<readonly SemCandidate[]>;
|
|
6
|
-
export declare function runFindingsAudit(model: LocalModel, changeSet: SemChangeSet, contexts: readonly SemContext[], pack: SemContextPack, checks: readonly StupifyCheck[], request?: Readonly<{
|
|
7
|
-
prompt: string;
|
|
8
|
-
schema: unknown;
|
|
9
|
-
}>): Promise<AuditReviewResult>;
|
|
10
|
-
export declare function findingsAuditRequest(changeSet: SemChangeSet, contexts: readonly SemContext[], pack: SemContextPack, checks: readonly StupifyCheck[], promptName?: AuditPromptName): Readonly<{
|
|
2
|
+
import type { SearchMatch, SemChangeSet, SemContext, SemContextPack, StupifyCheck } from "./types.ts";
|
|
3
|
+
export declare function runSearch(model: LocalModel, request: SearchRequest): Promise<readonly SearchMatch[]>;
|
|
4
|
+
export type SearchRequest = Readonly<{
|
|
11
5
|
prompt: string;
|
|
12
6
|
schema: unknown;
|
|
7
|
+
contexts: readonly SemContext[];
|
|
13
8
|
}>;
|
|
9
|
+
export declare function searchRequest(input: Readonly<{
|
|
10
|
+
changeSet: SemChangeSet;
|
|
11
|
+
contexts: readonly SemContext[];
|
|
12
|
+
pack: SemContextPack;
|
|
13
|
+
patterns: readonly StupifyCheck[];
|
|
14
|
+
includeCounterReasonInPrompt?: boolean;
|
|
15
|
+
}>): SearchRequest;
|
|
14
16
|
export declare function countPromptTokens(model: LocalModel, prompt: string): Promise<number>;
|
package/dist/analysis.js
CHANGED
|
@@ -1,34 +1,17 @@
|
|
|
1
|
-
import { auditPrompt, findingsAuditPrompt, scoutPrompt, semScoutPrompt } from "./prompts.js";
|
|
2
1
|
import { cachedJson, fingerprint } from "./cache.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return uncheckedCandidates(raw);
|
|
6
|
-
}
|
|
7
|
-
export async function auditCandidates(model, diff, contexts, checks) {
|
|
8
|
-
if (contexts.length === 0)
|
|
9
|
-
return { findings: [], summary: "No candidate regions found." };
|
|
10
|
-
const raw = await runJsonPrompt(model, auditPrompt(contexts, checks, diff.label), auditSchema(contexts), 0);
|
|
11
|
-
return uncheckedRawAuditResult(raw, diff.id);
|
|
12
|
-
}
|
|
13
|
-
export async function scoutSemChanges(model, changeSet, checks, maxCandidates) {
|
|
14
|
-
const raw = await runJsonPrompt(model, semScoutPrompt(changeSet, checks, maxCandidates), semScoutSchema(changeSet, checks, maxCandidates), 0);
|
|
15
|
-
return uncheckedSemCandidates(raw, changeSet.id);
|
|
16
|
-
}
|
|
17
|
-
export async function runFindingsAudit(model, changeSet, contexts, pack, checks, request = findingsAuditRequest(changeSet, contexts, pack, checks)) {
|
|
18
|
-
if (contexts.length === 0) {
|
|
19
|
-
return {
|
|
20
|
-
findings: [],
|
|
21
|
-
summary: "No candidate entities found.",
|
|
22
|
-
stats: { totalTargets: 0, finding: 0, clean: 0, uncertain: 0, invalid: 0 },
|
|
23
|
-
};
|
|
24
|
-
}
|
|
2
|
+
import { searchPrompt } from "./prompts.js";
|
|
3
|
+
export async function runSearch(model, request) {
|
|
25
4
|
const raw = await runJsonPrompt(model, request.prompt, request.schema, 0);
|
|
26
|
-
return
|
|
5
|
+
return uncheckedSearchMatches(raw, request.contexts);
|
|
27
6
|
}
|
|
28
|
-
export function
|
|
7
|
+
export function searchRequest(input) {
|
|
29
8
|
return {
|
|
30
|
-
prompt:
|
|
31
|
-
|
|
9
|
+
prompt: searchPrompt({
|
|
10
|
+
...input,
|
|
11
|
+
includeCounterReason: input.includeCounterReasonInPrompt ?? false,
|
|
12
|
+
}),
|
|
13
|
+
schema: searchSchema(input.contexts),
|
|
14
|
+
contexts: input.contexts,
|
|
32
15
|
};
|
|
33
16
|
}
|
|
34
17
|
export async function countPromptTokens(model, prompt) {
|
|
@@ -53,171 +36,45 @@ export async function countPromptTokens(model, prompt) {
|
|
|
53
36
|
});
|
|
54
37
|
return cached.count;
|
|
55
38
|
}
|
|
56
|
-
function
|
|
57
|
-
const targetIds = contexts.map((context) => context.targetId);
|
|
58
|
-
const findingItem = {
|
|
59
|
-
type: "object",
|
|
60
|
-
properties: {
|
|
61
|
-
targetId: { type: "string", enum: targetIds },
|
|
62
|
-
why: { type: "string" },
|
|
63
|
-
proof: { type: "string" },
|
|
64
|
-
},
|
|
65
|
-
required: ["targetId", "why", "proof"],
|
|
66
|
-
additionalProperties: false,
|
|
67
|
-
};
|
|
68
|
-
const uncertainItem = {
|
|
69
|
-
type: "object",
|
|
70
|
-
properties: {
|
|
71
|
-
targetId: { type: "string", enum: targetIds },
|
|
72
|
-
why: { type: "string" },
|
|
73
|
-
},
|
|
74
|
-
required: ["targetId", "why"],
|
|
75
|
-
additionalProperties: false,
|
|
76
|
-
};
|
|
77
|
-
return {
|
|
78
|
-
type: "object",
|
|
79
|
-
properties: {
|
|
80
|
-
findings: {
|
|
81
|
-
type: "array",
|
|
82
|
-
items: findingItem,
|
|
83
|
-
},
|
|
84
|
-
uncertain: {
|
|
85
|
-
type: "array",
|
|
86
|
-
items: uncertainItem,
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
additionalProperties: false,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
function auditSchema(contexts) {
|
|
93
|
-
return auditSchemaFromProofs(contexts.map((context) => context.pointer));
|
|
94
|
-
}
|
|
95
|
-
function auditSchemaFromProofs(proofs) {
|
|
96
|
-
return {
|
|
97
|
-
type: "object",
|
|
98
|
-
properties: {
|
|
99
|
-
findings: {
|
|
100
|
-
type: "array",
|
|
101
|
-
items: {
|
|
102
|
-
type: "object",
|
|
103
|
-
properties: {
|
|
104
|
-
checkId: { type: "string" },
|
|
105
|
-
why: { type: "string" },
|
|
106
|
-
proof: { type: "string", enum: proofs },
|
|
107
|
-
},
|
|
108
|
-
required: ["checkId", "why", "proof"],
|
|
109
|
-
additionalProperties: false,
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
summary: { type: "string" },
|
|
113
|
-
},
|
|
114
|
-
required: ["findings", "summary"],
|
|
115
|
-
additionalProperties: false,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
function semScoutSchema(changeSet, checks, maxCandidates) {
|
|
39
|
+
function searchSchema(contexts) {
|
|
119
40
|
return {
|
|
120
41
|
type: "object",
|
|
121
42
|
properties: {
|
|
122
|
-
|
|
43
|
+
matches: {
|
|
123
44
|
type: "array",
|
|
124
|
-
maxItems:
|
|
45
|
+
maxItems: 5,
|
|
125
46
|
items: {
|
|
126
47
|
type: "object",
|
|
127
48
|
properties: {
|
|
128
|
-
|
|
129
|
-
checkId: { type: "string", enum: checks.map((check) => check.id) },
|
|
49
|
+
targetId: { type: "string", enum: contexts.map((context) => context.targetId) },
|
|
130
50
|
reason: { type: "string" },
|
|
51
|
+
proof: { type: "string" },
|
|
131
52
|
},
|
|
132
|
-
required: ["
|
|
53
|
+
required: ["targetId", "reason", "proof"],
|
|
133
54
|
additionalProperties: false,
|
|
134
55
|
},
|
|
135
56
|
},
|
|
136
57
|
},
|
|
137
|
-
required: ["
|
|
58
|
+
required: ["matches"],
|
|
138
59
|
additionalProperties: false,
|
|
139
60
|
};
|
|
140
61
|
}
|
|
141
|
-
function
|
|
142
|
-
return {
|
|
143
|
-
type: "object",
|
|
144
|
-
properties: {
|
|
145
|
-
candidates: {
|
|
146
|
-
type: "array",
|
|
147
|
-
maxItems: 3,
|
|
148
|
-
items: { type: "string", enum: batch.hunks.map((hunk) => hunk.pointer) },
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
required: ["candidates"],
|
|
152
|
-
additionalProperties: false,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
function uncheckedCandidates(value) {
|
|
156
|
-
return [...(value.candidates ?? [])];
|
|
157
|
-
}
|
|
158
|
-
function uncheckedRawAuditResult(value, sourceId) {
|
|
159
|
-
const output = value;
|
|
160
|
-
const findings = (output.findings ?? []).map((finding) => ({
|
|
161
|
-
sourceId,
|
|
162
|
-
checkId: (finding.checkId ?? ""),
|
|
163
|
-
why: finding.why ?? "",
|
|
164
|
-
proof: finding.proof ?? "",
|
|
165
|
-
}));
|
|
166
|
-
return { findings, summary: output.summary ?? defaultSummary(findings.length) };
|
|
167
|
-
}
|
|
168
|
-
function uncheckedSemCandidates(value, sourceId) {
|
|
62
|
+
function uncheckedSearchMatches(value, contexts) {
|
|
169
63
|
const output = value;
|
|
170
|
-
const
|
|
171
|
-
return
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
sourceId,
|
|
183
|
-
targetId: candidate.targetId ?? "",
|
|
184
|
-
entityId: candidate.entityId ?? "",
|
|
185
|
-
checkId: checkId,
|
|
186
|
-
reason: candidate.reason ?? "",
|
|
187
|
-
}));
|
|
64
|
+
const contextsByTargetId = new Map(contexts.map((context) => [context.targetId, context]));
|
|
65
|
+
return (output.matches ?? []).flatMap((match) => {
|
|
66
|
+
const targetId = match.targetId ?? "";
|
|
67
|
+
const context = contextsByTargetId.get(targetId);
|
|
68
|
+
if (!context)
|
|
69
|
+
return [];
|
|
70
|
+
return [{
|
|
71
|
+
targetId,
|
|
72
|
+
patternId: context.checkId,
|
|
73
|
+
reason: match.reason ?? "",
|
|
74
|
+
proof: match.proof ?? "",
|
|
75
|
+
}];
|
|
188
76
|
});
|
|
189
77
|
}
|
|
190
|
-
function uncheckedFindingsAuditResult(value, sourceId, contexts) {
|
|
191
|
-
const output = value;
|
|
192
|
-
const targetsById = new Map(contexts.map((context) => [context.targetId, context]));
|
|
193
|
-
const findings = (output.findings ?? []).map((finding) => {
|
|
194
|
-
const target = targetsById.get(finding.targetId ?? "");
|
|
195
|
-
return {
|
|
196
|
-
sourceId,
|
|
197
|
-
checkId: (target?.checkId ?? ""),
|
|
198
|
-
why: finding.why ?? "",
|
|
199
|
-
proof: finding.proof ?? "",
|
|
200
|
-
};
|
|
201
|
-
});
|
|
202
|
-
const uncertain = output.uncertain?.length ?? 0;
|
|
203
|
-
const totalTargets = contexts.length;
|
|
204
|
-
return {
|
|
205
|
-
findings,
|
|
206
|
-
summary: defaultSummary(findings.length),
|
|
207
|
-
stats: {
|
|
208
|
-
totalTargets,
|
|
209
|
-
finding: findings.length,
|
|
210
|
-
clean: Math.max(0, totalTargets - findings.length - uncertain),
|
|
211
|
-
uncertain,
|
|
212
|
-
invalid: 0,
|
|
213
|
-
},
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
function defaultSummary(findingCount) {
|
|
217
|
-
return findingCount === 0
|
|
218
|
-
? "No clear judgment-offload signal found."
|
|
219
|
-
: `${findingCount} finding review${findingCount === 1 ? "" : "s"} accepted.`;
|
|
220
|
-
}
|
|
221
78
|
async function runJsonPrompt(model, prompt, schema, temperature) {
|
|
222
79
|
return cachedJson("model-json", fingerprint({
|
|
223
80
|
version: 1,
|
package/dist/checks.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { type StupifyCheck } from "./types.ts";
|
|
2
2
|
export declare const defaultChecks: readonly StupifyCheck[];
|
|
3
3
|
export declare function enabledChecks(checkIds: readonly string[] | null): readonly StupifyCheck[];
|
|
4
|
+
export declare function searchChecks(checkIds: readonly string[] | null): readonly StupifyCheck[];
|
package/dist/checks.js
CHANGED
|
@@ -10,7 +10,19 @@ export const defaultChecks = [
|
|
|
10
10
|
],
|
|
11
11
|
ignoreWhen: [
|
|
12
12
|
"test fixture, mock, or intentional external contract",
|
|
13
|
-
|
|
13
|
+
"public API DTO filters, omits, protects, renames, or versions fields",
|
|
14
|
+
],
|
|
15
|
+
hookMode: "warn",
|
|
16
|
+
searchPrompt: "Find only local/private payload or schema shapes that clearly copy another local shape one-for-one without creating a boundary. Do not match ordinary Input/Output/Request/Response types by name alone, public DTOs, external contracts, client types, or types that omit/protect private fields.",
|
|
17
|
+
searchExamples: {
|
|
18
|
+
match: [
|
|
19
|
+
"LocalUserPayload repeats User fields and maps id/email/displayName one-for-one.",
|
|
20
|
+
],
|
|
21
|
+
nonMatch: [
|
|
22
|
+
"PublicWebhookDto omits privateNotes from InternalJob.",
|
|
23
|
+
"A client type describes an external dependency boundary.",
|
|
24
|
+
],
|
|
25
|
+
},
|
|
14
26
|
},
|
|
15
27
|
{
|
|
16
28
|
id: checkId("unnecessary_complexity"),
|
|
@@ -22,6 +34,35 @@ export const defaultChecks = [
|
|
|
22
34
|
ignoreWhen: [
|
|
23
35
|
"isolates dependency, removes duplication, or improves testability",
|
|
24
36
|
],
|
|
37
|
+
hookMode: "warn",
|
|
38
|
+
searchPrompt: `Find staged changes where a locally simple decision is made harder to understand by new indirection.
|
|
39
|
+
Only match when the staged diff clearly shows:
|
|
40
|
+
- a new named helper, wrapper, service, adapter, boundary, or abstraction
|
|
41
|
+
- and the surrounding change still appears locally simple
|
|
42
|
+
- and the new structure makes the decision harder to see
|
|
43
|
+
Do not match:
|
|
44
|
+
- plain conditionals, guard clauses, skip paths, or error handling
|
|
45
|
+
- normal feature structure
|
|
46
|
+
- exported utilities that are part of a real feature
|
|
47
|
+
- command plumbing
|
|
48
|
+
- prompt/instruction files
|
|
49
|
+
- domain configuration
|
|
50
|
+
- refactors that make ownership clearer
|
|
51
|
+
- changes where the payoff is unclear from the diff
|
|
52
|
+
Prefer no match over a weak match.`,
|
|
53
|
+
searchExamples: {
|
|
54
|
+
match: [
|
|
55
|
+
"A small inline operation becomes a helper/service/wrapper with one obvious caller.",
|
|
56
|
+
"A straightforward flow is split across files in a way that hides the decision.",
|
|
57
|
+
"A new abstraction appears before there is evidence it buys clarity, correctness, reuse, or isolation.",
|
|
58
|
+
],
|
|
59
|
+
nonMatch: [
|
|
60
|
+
"A real external dependency boundary is isolated.",
|
|
61
|
+
"A security/auth boundary becomes clearer.",
|
|
62
|
+
"A refactor removes larger complexity elsewhere.",
|
|
63
|
+
"Framework-required structure is added.",
|
|
64
|
+
],
|
|
65
|
+
},
|
|
25
66
|
},
|
|
26
67
|
{
|
|
27
68
|
id: checkId("fake_precision_windowing"),
|
|
@@ -67,6 +108,22 @@ export const defaultChecks = [
|
|
|
67
108
|
ignoreWhen: [
|
|
68
109
|
"comment explains intent, constraint, workaround, or public API behavior",
|
|
69
110
|
],
|
|
111
|
+
hookMode: "warn",
|
|
112
|
+
searchPrompt: "Find staged changes where comments appear to substitute for judgment rather than clarify it.",
|
|
113
|
+
searchExamples: {
|
|
114
|
+
match: [
|
|
115
|
+
"New comments narrate obvious code instead of explaining tradeoffs.",
|
|
116
|
+
"A simple change gains multiple generic comments that restate control flow.",
|
|
117
|
+
"Comments make the code look more deliberate without adding useful reasoning.",
|
|
118
|
+
],
|
|
119
|
+
nonMatch: [
|
|
120
|
+
"Comments explain a real domain constraint.",
|
|
121
|
+
"Comments document an external API quirk.",
|
|
122
|
+
"Comments clarify a surprising edge case.",
|
|
123
|
+
"Comments are sparse and specific.",
|
|
124
|
+
"Comments explain provider, finance, reconciliation, timezone, or ledger behavior.",
|
|
125
|
+
],
|
|
126
|
+
},
|
|
70
127
|
},
|
|
71
128
|
{
|
|
72
129
|
id: checkId("lint_bypass"),
|
|
@@ -80,6 +137,16 @@ export const defaultChecks = [
|
|
|
80
137
|
"type-level test",
|
|
81
138
|
"generated file convention",
|
|
82
139
|
],
|
|
140
|
+
hookMode: "warn",
|
|
141
|
+
searchPrompt: "Find only broad lint/type bypasses that hide useful feedback. Match bare @ts-ignore, bare @ts-expect-error, broad casts, any, or eslint/biome suppressions without a concrete inline reason. Do not match targeted suppressions that include a reason for a known framework, test, mock, or external-library limitation.",
|
|
142
|
+
searchExamples: {
|
|
143
|
+
match: [
|
|
144
|
+
"A bare // @ts-ignore hides property access on unknown input.",
|
|
145
|
+
],
|
|
146
|
+
nonMatch: [
|
|
147
|
+
"// @ts-expect-error explains a known external library typing gap.",
|
|
148
|
+
],
|
|
149
|
+
},
|
|
83
150
|
},
|
|
84
151
|
{
|
|
85
152
|
id: checkId("inconsistent_patterns"),
|
|
@@ -103,7 +170,19 @@ export const defaultChecks = [
|
|
|
103
170
|
ignoreWhen: [
|
|
104
171
|
"existing utility has wrong contract",
|
|
105
172
|
"new helper is clearer as a tiny private expression",
|
|
106
|
-
|
|
173
|
+
"helper is domain-specific or used by multiple local call sites",
|
|
174
|
+
],
|
|
175
|
+
hookMode: "warn",
|
|
176
|
+
searchPrompt: "Find only tiny generic utility functions that recreate common helpers such as clamp, debounce, throttle, slugify, group, sort, pick, omit, uniq, or shuffle without domain-specific behavior. Do not match resolve/parse/format helpers, domain formatting, feature constants, or helpers with multiple obvious call sites.",
|
|
177
|
+
searchExamples: {
|
|
178
|
+
match: [
|
|
179
|
+
"clampValue returns min, max, or value.",
|
|
180
|
+
],
|
|
181
|
+
nonMatch: [
|
|
182
|
+
"formatCurrencyHelper is used by invoice and refund labels.",
|
|
183
|
+
"Subscription tier constants encode domain configuration.",
|
|
184
|
+
],
|
|
185
|
+
},
|
|
107
186
|
},
|
|
108
187
|
{
|
|
109
188
|
id: checkId("operator_style_mismatch"),
|
|
@@ -121,6 +200,14 @@ export const defaultChecks = [
|
|
|
121
200
|
export function enabledChecks(checkIds) {
|
|
122
201
|
if (!checkIds)
|
|
123
202
|
return defaultChecks.filter((check) => check.enabledByDefault !== false);
|
|
203
|
+
return checksById(checkIds);
|
|
204
|
+
}
|
|
205
|
+
export function searchChecks(checkIds) {
|
|
206
|
+
if (!checkIds)
|
|
207
|
+
return defaultChecks.filter((check) => check.hookMode === "warn");
|
|
208
|
+
return checksById(checkIds);
|
|
209
|
+
}
|
|
210
|
+
function checksById(checkIds) {
|
|
124
211
|
const checksById = new Map(defaultChecks.map((check) => [check.id, check]));
|
|
125
212
|
return checkIds.map((id) => {
|
|
126
213
|
const check = checksById.get(id);
|