@stupify/cli 0.0.3 → 0.0.4
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 +183 -333
- 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 +213 -526
- 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/dist/model.d.ts
CHANGED
|
@@ -1,24 +1,10 @@
|
|
|
1
1
|
import type { ModelId } from "./types.ts";
|
|
2
|
-
export type ModelProfile = "scout"
|
|
2
|
+
export type ModelProfile = "scout";
|
|
3
3
|
export type LocalModel = Readonly<{
|
|
4
4
|
id: ModelId;
|
|
5
5
|
name: string;
|
|
6
6
|
baseUrl: string;
|
|
7
7
|
profile: ModelProfile;
|
|
8
8
|
}>;
|
|
9
|
-
export declare function loadLocalModels(modelId: ModelId): Promise<{
|
|
10
|
-
scoutModel: Readonly<{
|
|
11
|
-
id: ModelId;
|
|
12
|
-
name: string;
|
|
13
|
-
baseUrl: string;
|
|
14
|
-
profile: ModelProfile;
|
|
15
|
-
}>;
|
|
16
|
-
auditModel: Readonly<{
|
|
17
|
-
id: ModelId;
|
|
18
|
-
name: string;
|
|
19
|
-
baseUrl: string;
|
|
20
|
-
profile: ModelProfile;
|
|
21
|
-
}>;
|
|
22
|
-
}>;
|
|
23
9
|
export declare function firstRunModelBootstrap(modelId: ModelId): Promise<string>;
|
|
24
10
|
export declare function loadLocalModel(modelPath: string, modelId: ModelId, profile?: ModelProfile): Promise<LocalModel>;
|
package/dist/model.js
CHANGED
|
@@ -9,12 +9,6 @@ import { promisify } from "node:util";
|
|
|
9
9
|
import { MODEL_REGISTRY } from "./constants.js";
|
|
10
10
|
const execFileAsync = promisify(execFile);
|
|
11
11
|
const LLAMA_SERVER_HOST = "127.0.0.1";
|
|
12
|
-
export async function loadLocalModels(modelId) {
|
|
13
|
-
const modelPath = await firstRunModelBootstrap(modelId);
|
|
14
|
-
const scoutModel = await loadLocalModel(modelPath, modelId, "scout");
|
|
15
|
-
const auditModel = await loadLocalModel(modelPath, modelId, "audit");
|
|
16
|
-
return { scoutModel, auditModel };
|
|
17
|
-
}
|
|
18
12
|
export async function firstRunModelBootstrap(modelId) {
|
|
19
13
|
const selectedModel = MODEL_REGISTRY[modelId];
|
|
20
14
|
const modelDir = path.join(cacheDir(), "models");
|
|
@@ -62,16 +56,6 @@ export async function loadLocalModel(modelPath, modelId, profile = "scout") {
|
|
|
62
56
|
};
|
|
63
57
|
}
|
|
64
58
|
function modelRuntime(profile) {
|
|
65
|
-
if (profile === "audit") {
|
|
66
|
-
const baseUrl = process.env.STUPIFY_AUDIT_LLAMA_SERVER_URL ?? "http://127.0.0.1:8092";
|
|
67
|
-
return {
|
|
68
|
-
profile,
|
|
69
|
-
baseUrl,
|
|
70
|
-
port: new URL(baseUrl).port || "8092",
|
|
71
|
-
reasoning: "on",
|
|
72
|
-
reasoningBudget: 4_096,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
59
|
const baseUrl = process.env.STUPIFY_SCOUT_LLAMA_SERVER_URL ??
|
|
76
60
|
process.env.STUPIFY_LLAMA_SERVER_URL ??
|
|
77
61
|
"http://127.0.0.1:8091";
|
|
@@ -79,7 +63,15 @@ function modelRuntime(profile) {
|
|
|
79
63
|
profile,
|
|
80
64
|
baseUrl,
|
|
81
65
|
port: new URL(baseUrl).port || "8091",
|
|
66
|
+
contextSize: envInteger("STUPIFY_LLAMA_CONTEXT") ?? 65_536,
|
|
82
67
|
reasoning: "off",
|
|
68
|
+
gpuLayers: envInteger("STUPIFY_LLAMA_GPU_LAYERS") ?? 999,
|
|
69
|
+
batchSize: envInteger("STUPIFY_LLAMA_BATCH") ?? 2_048,
|
|
70
|
+
ubatchSize: envInteger("STUPIFY_LLAMA_UBATCH") ?? 512,
|
|
71
|
+
parallel: envInteger("STUPIFY_LLAMA_PARALLEL") ?? 2,
|
|
72
|
+
threads: envInteger("STUPIFY_LLAMA_THREADS"),
|
|
73
|
+
threadsBatch: envInteger("STUPIFY_LLAMA_THREADS_BATCH"),
|
|
74
|
+
flashAttention: envBoolean("STUPIFY_LLAMA_FLASH_ATTN"),
|
|
83
75
|
};
|
|
84
76
|
}
|
|
85
77
|
async function runningServerModel(baseUrl) {
|
|
@@ -127,11 +119,25 @@ async function startLlamaServer(modelPath, modelId, modelName, runtime) {
|
|
|
127
119
|
"--port",
|
|
128
120
|
runtime.port,
|
|
129
121
|
"-c",
|
|
130
|
-
|
|
122
|
+
String(runtime.contextSize),
|
|
131
123
|
"--reasoning",
|
|
132
124
|
runtime.reasoning,
|
|
133
125
|
"--no-warmup",
|
|
134
126
|
];
|
|
127
|
+
if (runtime.gpuLayers !== undefined)
|
|
128
|
+
args.push("-ngl", String(runtime.gpuLayers));
|
|
129
|
+
if (runtime.batchSize !== undefined)
|
|
130
|
+
args.push("-b", String(runtime.batchSize));
|
|
131
|
+
if (runtime.ubatchSize !== undefined)
|
|
132
|
+
args.push("-ub", String(runtime.ubatchSize));
|
|
133
|
+
if (runtime.parallel !== undefined)
|
|
134
|
+
args.push("-np", String(runtime.parallel));
|
|
135
|
+
if (runtime.threads !== undefined)
|
|
136
|
+
args.push("-t", String(runtime.threads));
|
|
137
|
+
if (runtime.threadsBatch !== undefined)
|
|
138
|
+
args.push("-tb", String(runtime.threadsBatch));
|
|
139
|
+
if (runtime.flashAttention !== undefined)
|
|
140
|
+
args.push("-fa", runtime.flashAttention ? "on" : "off");
|
|
135
141
|
if (runtime.reasoningBudget !== undefined) {
|
|
136
142
|
args.push("--reasoning-budget", String(runtime.reasoningBudget));
|
|
137
143
|
}
|
|
@@ -180,10 +186,20 @@ async function managedServerPid(runtime) {
|
|
|
180
186
|
}
|
|
181
187
|
}
|
|
182
188
|
function pidPath(runtime) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
189
|
+
return path.join(cacheDir(), "llama-server.pid");
|
|
190
|
+
}
|
|
191
|
+
function envInteger(name, fallback) {
|
|
192
|
+
const raw = process.env[name];
|
|
193
|
+
if (raw === undefined || raw === "")
|
|
194
|
+
return fallback;
|
|
195
|
+
const value = Number(raw);
|
|
196
|
+
return Number.isInteger(value) && value > 0 ? value : fallback;
|
|
197
|
+
}
|
|
198
|
+
function envBoolean(name) {
|
|
199
|
+
const raw = process.env[name];
|
|
200
|
+
if (raw === undefined || raw === "")
|
|
201
|
+
return undefined;
|
|
202
|
+
return /^(1|true|yes|on)$/i.test(raw);
|
|
187
203
|
}
|
|
188
204
|
async function waitForServer(baseUrl, modelId) {
|
|
189
205
|
const deadline = Date.now() + 120_000;
|
package/dist/prompts.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { SemChangeSet, SemContext, SemContextPack, StupifyCheck } from "./types.ts";
|
|
2
|
+
export declare function searchPrompt(input: Readonly<{
|
|
3
|
+
changeSet: SemChangeSet;
|
|
4
|
+
contexts: readonly SemContext[];
|
|
5
|
+
pack: SemContextPack;
|
|
6
|
+
patterns: readonly StupifyCheck[];
|
|
7
|
+
includeCounterReason: boolean;
|
|
8
|
+
}>): string;
|
package/dist/prompts.js
CHANGED
|
@@ -1,197 +1,87 @@
|
|
|
1
|
-
export function
|
|
2
|
-
return `
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export function auditPrompt(contexts, checks, sourceLabel) {
|
|
21
|
-
return `Audit candidate diff regions against enabled checks.
|
|
22
|
-
Return JSON only:
|
|
23
|
-
{
|
|
24
|
-
"findings": [{ "checkId": "check_id", "why": "one sentence", "proof": "exact POINTER" }],
|
|
25
|
-
"summary": "one short sentence"
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
Rules:
|
|
29
|
-
- Use only checks listed below.
|
|
30
|
-
- checkId must be a check ID, never a POINTER.
|
|
31
|
-
- proof must be one exact POINTER from candidate regions.
|
|
32
|
-
- why describes the suspicious structure, not an identifier.
|
|
33
|
-
- Do not describe an issue in summary unless it is also in findings.
|
|
34
|
-
- If no findings, return { "findings": [], "summary": "No clear judgment-offload signal found." }.
|
|
35
|
-
|
|
36
|
-
Allowed proof pointers:
|
|
37
|
-
${contexts.map((context) => `- ${context.pointer}`).join("\n")}
|
|
38
|
-
|
|
39
|
-
${formatFullChecks(checks)}
|
|
40
|
-
|
|
41
|
-
SOURCE:
|
|
42
|
-
${sourceLabel}
|
|
43
|
-
|
|
44
|
-
CANDIDATE REGIONS:
|
|
45
|
-
${contexts.map(formatContext).join("\n\n")}`;
|
|
46
|
-
}
|
|
47
|
-
export function semScoutPrompt(changeSet, checks, maxCandidates) {
|
|
48
|
-
return `Pick changed entity/check targets worth auditing.
|
|
49
|
-
Return JSON only:
|
|
50
|
-
{ "targets": [{ "entityId": "exact entityId", "checkId": "check_id", "reason": "short scout reason" }] }
|
|
51
|
-
|
|
52
|
-
Rules:
|
|
53
|
-
- Use entityId values exactly as shown.
|
|
54
|
-
- Each target has exactly one checkId.
|
|
55
|
-
- Return at most ${maxCandidates} targets.
|
|
56
|
-
- Return { "targets": [] } if clean.
|
|
57
|
-
- Pick definitions over usage sites.
|
|
58
|
-
- Prefer high recall, but do not attach unrelated checks.
|
|
59
|
-
|
|
60
|
-
${formatCompactChecks(checks)}
|
|
61
|
-
|
|
62
|
-
SOURCE:
|
|
63
|
-
${changeSet.label}
|
|
64
|
-
|
|
65
|
-
SEM CHANGE SUMMARY:
|
|
66
|
-
${JSON.stringify(changeSet.summary, null, 2)}
|
|
67
|
-
|
|
68
|
-
SEM ENTITY CHANGES:
|
|
69
|
-
${changeSet.changes.map(formatSemChange).join("\n\n")}`;
|
|
70
|
-
}
|
|
71
|
-
export function findingsAuditPrompt(contexts, pack, checks, sourceLabel, promptName) {
|
|
72
|
-
const task = promptName === "high_bar"
|
|
73
|
-
? `You are Stupify's audit model.
|
|
74
|
-
You are reviewing candidate/check targets for signs that AI-assisted coding may have replaced engineering judgment.
|
|
75
|
-
Only emit a finding if it is clearly useful to a developer.
|
|
76
|
-
A useful finding must:
|
|
77
|
-
- match the target's check exactly
|
|
78
|
-
- point to a concrete change pattern
|
|
79
|
-
- explain why the change may reflect judgment-offload
|
|
80
|
-
- avoid generic code-review commentary
|
|
81
|
-
If the target is normal engineering work, omit it.
|
|
82
|
-
If the target is merely plausible but not strong, omit it.
|
|
83
|
-
If the target does not exactly match its assigned check, omit it.`
|
|
84
|
-
: `You are Stupify's auditor.
|
|
85
|
-
Audit only the listed target/check pairs.
|
|
86
|
-
Emit only exceptions.`;
|
|
87
|
-
const highBarRules = promptName === "high_bar"
|
|
88
|
-
? `- Prefer clean over weak.
|
|
89
|
-
- Prefer no finding over generic finding.
|
|
90
|
-
- Do not emit style feedback unless the assigned check is truly about style.
|
|
91
|
-
- Do not turn functional refactors into style mismatch findings.`
|
|
92
|
-
: "";
|
|
93
|
-
return `${task}
|
|
1
|
+
export function searchPrompt(input) {
|
|
2
|
+
return `You are Stupify's local search model.
|
|
3
|
+
Stupify checks whether AI-assisted coding may be replacing developer judgment.
|
|
4
|
+
You will receive:
|
|
5
|
+
1. Semantic changed entities selected by a fast local counter.
|
|
6
|
+
2. Compressed local file context from Repomix.
|
|
7
|
+
3. A list of search targets. Each target has exactly one assigned pattern.
|
|
8
|
+
|
|
9
|
+
Your job:
|
|
10
|
+
Evaluate each target only against its assigned pattern.
|
|
11
|
+
False positives are expensive.
|
|
12
|
+
Only emit a match if the assigned pattern clearly applies to that exact target.
|
|
13
|
+
Do not perform general code review.
|
|
14
|
+
Do not suggest improvements.
|
|
15
|
+
Do not choose a pattern.
|
|
16
|
+
Do not apply other patterns.
|
|
17
|
+
Do not report issues for unlisted targets.
|
|
18
|
+
Do not emit clean results.
|
|
19
|
+
Omitted target = clean.
|
|
94
20
|
Return JSON only:
|
|
95
21
|
{
|
|
96
|
-
"
|
|
22
|
+
"matches": [
|
|
97
23
|
{
|
|
98
24
|
"targetId": "t001",
|
|
99
|
-
"
|
|
25
|
+
"reason": "one sentence",
|
|
100
26
|
"proof": "short pointer"
|
|
101
27
|
}
|
|
102
|
-
],
|
|
103
|
-
"uncertain": [
|
|
104
|
-
{
|
|
105
|
-
"targetId": "t002",
|
|
106
|
-
"why": "one sentence"
|
|
107
|
-
}
|
|
108
28
|
]
|
|
109
29
|
}
|
|
110
30
|
|
|
111
31
|
Rules:
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
- Emit uncertain only when the target may match, but evidence is insufficient.
|
|
116
|
-
- If a target is clean, emit nothing for it.
|
|
117
|
-
- Omitted target means clean.
|
|
118
|
-
- Do not output clean reviews.
|
|
119
|
-
- Do not explain clean targets.
|
|
120
|
-
- Do not write "no evidence" as a finding.
|
|
121
|
-
- Do not put negative statements in findings.
|
|
122
|
-
- Prefer omission over weak findings.
|
|
123
|
-
- Use only provided targetIds.
|
|
124
|
-
- Do not search for other checks.
|
|
32
|
+
- Use only targetIds from the input.
|
|
33
|
+
- Emit at most 5 matches.
|
|
34
|
+
- Prefer omission over a weak match.
|
|
125
35
|
- Do not quote source code.
|
|
126
|
-
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
36
|
+
- Do not write generic feedback.
|
|
37
|
+
- Do not emit "no evidence" or "does not apply."
|
|
38
|
+
- Proof must point to concrete changed product code that implements the pattern.
|
|
39
|
+
- Proof must not be a file header or start with "diff --git".
|
|
40
|
+
- Do not use pattern registry text, prompt text, docs, tests, or examples as proof.
|
|
41
|
+
- Do not treat pattern or prompt wording as the code being evaluated.
|
|
42
|
+
- Do not treat plain conditionals, guard clauses, skip paths, or error handling as indirection.
|
|
43
|
+
- For unnecessary_complexity, identify the exact new named abstraction in proof.
|
|
44
|
+
- If unnecessary_complexity proof would only be a file, hunk, or conditional block, omit it.
|
|
45
|
+
- If nothing clearly matches, return { "matches": [] }.
|
|
131
46
|
|
|
132
47
|
SOURCE:
|
|
133
|
-
${
|
|
48
|
+
${input.changeSet.label}
|
|
134
49
|
|
|
135
|
-
|
|
136
|
-
${contexts.map(
|
|
50
|
+
SEARCH TARGETS:
|
|
51
|
+
${input.contexts.map((context) => formatSearchTarget(context, patternForContext(context, input.patterns), input.includeCounterReason)).join("\n\n") || "(none)"}
|
|
137
52
|
|
|
138
|
-
|
|
139
|
-
${pack.text || "(none)"}`;
|
|
140
|
-
}
|
|
141
|
-
function formatCompactChecks(checks) {
|
|
142
|
-
return `Checks:
|
|
143
|
-
${checks.map((check) => `- ${check.id}: ${check.lookFor.join("; ")}`).join("\n")}`;
|
|
53
|
+
REPOMIX CONTEXT (${input.pack.filePaths.length} files, ${input.pack.totalTokens} tokens):
|
|
54
|
+
${input.pack.text || "(none)"}`;
|
|
144
55
|
}
|
|
145
|
-
function
|
|
146
|
-
return
|
|
147
|
-
}
|
|
148
|
-
function formatCheck(check) {
|
|
149
|
-
return `# ${check.name}
|
|
150
|
-
ID: ${check.id}
|
|
151
|
-
Q: ${check.question}
|
|
56
|
+
function formatSearchPattern(check) {
|
|
57
|
+
return `Pattern: ${check.id} (${check.name})
|
|
58
|
+
Question: ${check.searchPrompt ?? check.question}
|
|
152
59
|
Look for:
|
|
153
60
|
${check.lookFor.map((signal) => `- ${signal}`).join("\n")}
|
|
154
61
|
Ignore when:
|
|
155
62
|
${check.ignoreWhen.map((signal) => `- ${signal}`).join("\n")}
|
|
156
63
|
Match examples:
|
|
157
|
-
${(check.examples?.match ?? []).map((example) => `- ${example}`).join("\n")}
|
|
158
|
-
|
|
159
|
-
${(check.examples?.noMatch ?? []).map((example) => `- ${example}`).join("\n")}`;
|
|
160
|
-
}
|
|
161
|
-
function formatContext(context) {
|
|
162
|
-
return `POINTER ${context.pointer}
|
|
163
|
-
${context.text}`;
|
|
64
|
+
${(check.searchExamples?.match ?? check.examples?.match ?? []).map((example) => `- ${example}`).join("\n")}
|
|
65
|
+
Non-match examples:
|
|
66
|
+
${(check.searchExamples?.nonMatch ?? check.examples?.noMatch ?? []).map((example) => `- ${example}`).join("\n")}`;
|
|
164
67
|
}
|
|
165
|
-
function
|
|
166
|
-
return `ENTITY ${change.entityId}
|
|
167
|
-
TYPE ${change.entityType}
|
|
168
|
-
CHANGE ${change.changeType}
|
|
169
|
-
PATH ${change.filePath}`;
|
|
170
|
-
}
|
|
171
|
-
function formatSemContext(context) {
|
|
68
|
+
function formatSearchTarget(context, pattern, includeCounterReason) {
|
|
172
69
|
return `TARGET ${context.targetId}
|
|
70
|
+
ASSIGNED ${formatSearchPattern(pattern)}
|
|
71
|
+
SEM TARGET:
|
|
173
72
|
ENTITY ${context.entityId}
|
|
174
73
|
NAME ${context.entityName}
|
|
175
74
|
KIND ${context.entityKind}
|
|
176
75
|
CHANGE ${context.changeKind}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
CONTEXT:
|
|
180
|
-
${context.text}`;
|
|
181
|
-
}
|
|
182
|
-
function formatAuditTarget(context, checks) {
|
|
183
|
-
const check = checks.find((item) => item.id === context.checkId);
|
|
184
|
-
return `- targetId=${context.targetId} checkId=${context.checkId} entityId=${context.entityId}
|
|
185
|
-
scoutReason=${context.reason}
|
|
186
|
-
${check ? formatCheck(check) : ""}`;
|
|
76
|
+
FILE ${context.filePath ?? "(unknown)"}
|
|
77
|
+
${includeCounterReason ? `COUNTER_REASON ${context.reason}` : ""}`.trim();
|
|
187
78
|
}
|
|
188
|
-
function
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
[stupify: sem entity content shortened after ${limit} lines]`;
|
|
79
|
+
function patternForContext(context, patterns) {
|
|
80
|
+
return patterns.find((pattern) => pattern.id === context.checkId) ?? {
|
|
81
|
+
id: context.checkId,
|
|
82
|
+
name: context.checkId,
|
|
83
|
+
question: `Does this target match ${context.checkId}?`,
|
|
84
|
+
lookFor: [],
|
|
85
|
+
ignoreWhen: [],
|
|
86
|
+
};
|
|
197
87
|
}
|
package/dist/render.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function
|
|
1
|
+
import type { SearchCommand, SearchRunJson } from "./types.ts";
|
|
2
|
+
export declare function renderSearchRun(run: SearchRunJson, command: SearchCommand): string;
|
|
3
3
|
export declare function helpText(): string;
|
package/dist/render.js
CHANGED
|
@@ -1,40 +1,37 @@
|
|
|
1
1
|
import { VERSION } from "./constants.js";
|
|
2
|
-
export function
|
|
3
|
-
if (command.json)
|
|
4
|
-
return JSON.stringify(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
export function renderSearchRun(run, command) {
|
|
3
|
+
if (command.json)
|
|
4
|
+
return JSON.stringify(run, null, 2);
|
|
5
|
+
if (run.stats.skipped && run.stats.skipReason === "input_too_large") {
|
|
6
|
+
return `🧙 stupify 🪄
|
|
7
|
+
Search input is too large for precise local search.
|
|
8
|
+
Size:
|
|
9
|
+
~${run.stats.inputTokens ?? "unknown"} tokens
|
|
10
|
+
Limit:
|
|
11
|
+
${run.stats.inputTokenCap ?? "unknown"} tokens
|
|
12
|
+
Stupify skipped the search rather than review truncated context.
|
|
13
|
+
Nothing was blocked.
|
|
14
|
+
Try:
|
|
15
|
+
stupify ${sourceHint(command)} --max-search-input-tokens ${Math.max((run.stats.inputTokens ?? 12_000) + 1, (run.stats.inputTokenCap ?? 12_000) * 2)}`;
|
|
12
16
|
}
|
|
13
|
-
if (
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
${report.run.auditedCandidateCount} candidates inspected
|
|
19
|
-
${report.result.findings.length} findings
|
|
20
|
-
${renderAuditStats(report)}
|
|
21
|
-
${renderWarnings(report)}
|
|
22
|
-
Findings:
|
|
23
|
-
${renderFindings(report)}
|
|
24
|
-
Timing:
|
|
25
|
-
total_ms=${report.run.timingsMs.total} entity_diff_ms=${report.run.timingsMs.diff} model_ms=${report.run.timingsMs.modelLoad} scout_ms=${report.run.timingsMs.search} context_audit_ms=${report.run.timingsMs.audit}`;
|
|
17
|
+
if (run.stats.skipped && run.stats.skipReason === "no_candidates") {
|
|
18
|
+
return `🧙 stupify 🪄
|
|
19
|
+
Search complete.
|
|
20
|
+
Patterns: ${run.patterns.join(", ")}
|
|
21
|
+
No search targets found.`;
|
|
26
22
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
${
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
if (run.matches.length === 0) {
|
|
24
|
+
return `🧙 stupify 🪄
|
|
25
|
+
Search complete.
|
|
26
|
+
Patterns: ${run.patterns.join(", ")}
|
|
27
|
+
No judgment-offload signals found.`;
|
|
28
|
+
}
|
|
29
|
+
return `🧙 stupify 🪄
|
|
30
|
+
Possible judgment-offload detected:
|
|
31
|
+
${run.matches.map((match, index) => `${index + 1}. ${match.patternId}
|
|
32
|
+
${match.reason}
|
|
33
|
+
Proof: ${match.proof}`).join("\n")}
|
|
34
|
+
Search mode is warn-only.`;
|
|
38
35
|
}
|
|
39
36
|
export function helpText() {
|
|
40
37
|
return `Stupify ${VERSION}
|
|
@@ -44,58 +41,53 @@ Usage:
|
|
|
44
41
|
stupify --since "2 weeks ago"
|
|
45
42
|
stupify --commit <commit>
|
|
46
43
|
stupify --commits <count>
|
|
47
|
-
stupify
|
|
44
|
+
stupify --staged
|
|
45
|
+
stupify --mode search --staged
|
|
46
|
+
stupify hook install|uninstall|status
|
|
47
|
+
stupify doctor
|
|
48
|
+
stupify bench search experiments/search-bench.json
|
|
48
49
|
git diff HEAD~1..HEAD | stupify --stdin
|
|
49
50
|
|
|
50
51
|
Options:
|
|
51
|
-
--
|
|
52
|
-
--
|
|
53
|
-
--
|
|
54
|
-
--
|
|
55
|
-
--
|
|
56
|
-
--
|
|
57
|
-
--
|
|
58
|
-
|
|
59
|
-
--
|
|
60
|
-
|
|
61
|
-
--
|
|
62
|
-
--
|
|
63
|
-
--
|
|
64
|
-
|
|
65
|
-
--
|
|
66
|
-
|
|
67
|
-
--
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
--json Print JSON only.
|
|
52
|
+
--staged Search staged changes.
|
|
53
|
+
--mode <mode> search. Search is the only analysis mode.
|
|
54
|
+
--since <date> Search the net diff from the first commit before this git date to HEAD.
|
|
55
|
+
--commit <commit> Search one commit as a net diff.
|
|
56
|
+
--commits <count> Search the net diff across the last N non-merge commits.
|
|
57
|
+
--stdin Read a git diff from stdin.
|
|
58
|
+
--debug-sem Print sem commands and stderr.
|
|
59
|
+
--max-candidates <n> Max semantic search targets. Default: 10.
|
|
60
|
+
--max-search-input-tokens <n>
|
|
61
|
+
Max search input tokens before skipping. Default: 12000.
|
|
62
|
+
--checks <ids> Comma-separated pattern ids.
|
|
63
|
+
--model <id> gemma-4-e2b, gemma-4-e4b, gemma-4-26b-a4b, qwen3-4b-magicquant, qwen2.5-coder-1.5b, qwen2.5-coder-7b, or qwen2.5-coder-32b.
|
|
64
|
+
--search-profile <path>
|
|
65
|
+
Dev/bench-only search profile override.
|
|
66
|
+
--include-counter-reason-in-prompt
|
|
67
|
+
Debug/bench-only: include counter reason in the model prompt.
|
|
68
|
+
--json Print JSON only.
|
|
69
|
+
|
|
70
|
+
Diagnostics:
|
|
71
|
+
stupify doctor Check local setup, hook status, and privacy boundary.
|
|
72
72
|
|
|
73
73
|
Default:
|
|
74
74
|
stupify is equivalent to stupify --since "2 weeks ago".
|
|
75
75
|
|
|
76
|
+
Pipeline:
|
|
77
|
+
sem diff -> counter scout -> Repomix context -> local search model.
|
|
78
|
+
|
|
76
79
|
Not included:
|
|
77
|
-
|
|
78
|
-
`;
|
|
79
|
-
}
|
|
80
|
-
function renderFindings(report) {
|
|
81
|
-
if (report.result.findings.length === 0)
|
|
82
|
-
return " None.";
|
|
83
|
-
return report.result.findings
|
|
84
|
-
.map((finding) => `- ${finding.checkId}
|
|
85
|
-
${finding.why}
|
|
86
|
-
Proof: ${finding.proof}`)
|
|
87
|
-
.join("\n");
|
|
88
|
-
}
|
|
89
|
-
function renderWarnings(report) {
|
|
90
|
-
if (report.run.warnings.length === 0)
|
|
91
|
-
return "";
|
|
92
|
-
return `Warnings:
|
|
93
|
-
${report.run.warnings.map((warning) => ` ${warning}`).join("\n")}
|
|
80
|
+
Findings audit, validators, judges, baselines, sharing, hosted server calls, GitHub, dashboards, or repo-wide crawling.
|
|
94
81
|
`;
|
|
95
82
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
83
|
+
function sourceHint(command) {
|
|
84
|
+
if (command.kind === "staged")
|
|
85
|
+
return "--staged";
|
|
86
|
+
if (command.kind === "since")
|
|
87
|
+
return `--since "${command.since}"`;
|
|
88
|
+
if (command.kind === "commit")
|
|
89
|
+
return `--commit ${command.commit}`;
|
|
90
|
+
if (command.kind === "commits")
|
|
91
|
+
return `--commits ${command.count}`;
|
|
92
|
+
return "--stdin";
|
|
101
93
|
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import type { SemCandidate, SemChange, SemContext, SemContextPack } from "./types.ts";
|
|
1
|
+
import type { RepomixSearchConfig, SemCandidate, SemChange, SemContext, SemContextPack } from "./types.ts";
|
|
2
2
|
export declare function emptyContextPack(): SemContextPack;
|
|
3
|
-
export declare function repomixContextPack(cwd: string, contexts: readonly SemContext[], changes: readonly SemChange[]
|
|
3
|
+
export declare function repomixContextPack(cwd: string, contexts: readonly SemContext[], changes: readonly SemChange[], config?: Readonly<{
|
|
4
|
+
compress: boolean;
|
|
5
|
+
showLineNumbers: boolean;
|
|
6
|
+
removeEmptyLines: boolean;
|
|
7
|
+
maxFileSizeBytes: number;
|
|
8
|
+
maxTotalSizeBytes: number;
|
|
9
|
+
ignorePatterns: readonly string[];
|
|
10
|
+
}>): Promise<SemContextPack>;
|
|
4
11
|
export declare function entityContextsFromChanges(candidates: readonly SemCandidate[], changes: readonly SemChange[]): readonly SemContext[];
|
|
12
|
+
export declare function repomixSearchConfig(): RepomixSearchConfig;
|