@stupify/cli 0.0.15 → 0.1.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/.review/CORPUS.md +73 -0
- package/.review/REVIEW-PROMPT.md +52 -0
- package/.review/RUBRIC.md +46 -0
- package/LICENSE +1 -1
- package/README.md +41 -39
- package/package.json +24 -25
- package/src/cli.ts +358 -0
- package/src/review-sweep.ts +492 -0
- package/dist/analysis.d.ts +0 -16
- package/dist/analysis.js +0 -165
- package/dist/cache.d.ts +0 -2
- package/dist/cache.js +0 -57
- package/dist/checks.d.ts +0 -4
- package/dist/checks.js +0 -228
- package/dist/command.d.ts +0 -2
- package/dist/command.js +0 -147
- package/dist/constants.d.ts +0 -4
- package/dist/constants.js +0 -53
- package/dist/counter-scout.d.ts +0 -21
- package/dist/counter-scout.js +0 -167
- package/dist/diff.d.ts +0 -1
- package/dist/diff.js +0 -10
- package/dist/doctor.d.ts +0 -4
- package/dist/doctor.js +0 -131
- package/dist/git.d.ts +0 -12
- package/dist/git.js +0 -298
- package/dist/hooks.d.ts +0 -3
- package/dist/hooks.js +0 -117
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/model.d.ts +0 -11
- package/dist/model.js +0 -296
- package/dist/prompts.d.ts +0 -8
- package/dist/prompts.js +0 -89
- package/dist/render.d.ts +0 -3
- package/dist/render.js +0 -151
- package/dist/repomix-provider.d.ts +0 -12
- package/dist/repomix-provider.js +0 -196
- package/dist/search-bench.d.ts +0 -1
- package/dist/search-bench.js +0 -677
- package/dist/search-profile.d.ts +0 -6
- package/dist/search-profile.js +0 -73
- package/dist/sem-provider.d.ts +0 -2
- package/dist/sem-provider.js +0 -252
- package/dist/stupify.d.ts +0 -38
- package/dist/stupify.js +0 -474
- package/dist/trace.d.ts +0 -31
- package/dist/trace.js +0 -86
- package/dist/types.d.ts +0 -328
- package/dist/types.js +0 -6
- package/dist/ui.d.ts +0 -34
- package/dist/ui.js +0 -143
- package/src/analysis.ts +0 -220
- package/src/cache.ts +0 -63
- package/src/checks.ts +0 -231
- package/src/command.ts +0 -173
- package/src/constants.ts +0 -56
- package/src/counter-scout.ts +0 -195
- package/src/diff.ts +0 -9
- package/src/doctor.ts +0 -140
- package/src/git.ts +0 -306
- package/src/hooks.ts +0 -134
- package/src/index.ts +0 -1
- package/src/model.ts +0 -367
- package/src/prompts.ts +0 -100
- package/src/render.ts +0 -154
- package/src/repomix-provider.ts +0 -219
- package/src/search-bench.ts +0 -783
- package/src/search-profile.ts +0 -89
- package/src/sem-provider.ts +0 -297
- package/src/stupify.ts +0 -571
- package/src/trace.ts +0 -126
- package/src/types.ts +0 -348
- package/src/ui.ts +0 -187
package/dist/cache.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { homedir, platform } from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
export function fingerprint(value) {
|
|
6
|
-
const text = typeof value === "string" ? value : stableStringify(value);
|
|
7
|
-
return createHash("sha256").update(text).digest("hex");
|
|
8
|
-
}
|
|
9
|
-
export async function cachedJson(namespace, key, compute) {
|
|
10
|
-
const filePath = cachePath(namespace, key);
|
|
11
|
-
try {
|
|
12
|
-
const value = JSON.parse(await readFile(filePath, "utf8"));
|
|
13
|
-
return value;
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
}
|
|
17
|
-
const value = await compute();
|
|
18
|
-
await writeCache(filePath, value).catch(() => undefined);
|
|
19
|
-
return value;
|
|
20
|
-
}
|
|
21
|
-
function cachePath(namespace, key) {
|
|
22
|
-
return path.join(cacheRoot(), "intermediate-v1", safeNamespace(namespace), `${key}.json`);
|
|
23
|
-
}
|
|
24
|
-
async function writeCache(filePath, value) {
|
|
25
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
26
|
-
const tempPath = `${filePath}.${process.pid}.tmp`;
|
|
27
|
-
try {
|
|
28
|
-
await writeFile(tempPath, JSON.stringify(value), "utf8");
|
|
29
|
-
await rename(tempPath, filePath);
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
await rm(tempPath, { force: true });
|
|
33
|
-
throw error;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function cacheRoot() {
|
|
37
|
-
if (process.env.STUPIFY_CACHE_DIR)
|
|
38
|
-
return process.env.STUPIFY_CACHE_DIR;
|
|
39
|
-
if (process.env.XDG_CACHE_HOME)
|
|
40
|
-
return path.join(process.env.XDG_CACHE_HOME, "stupify");
|
|
41
|
-
if (platform() === "darwin")
|
|
42
|
-
return path.join(homedir(), "Library", "Caches", "stupify");
|
|
43
|
-
if (platform() === "win32" && process.env.LOCALAPPDATA)
|
|
44
|
-
return path.join(process.env.LOCALAPPDATA, "stupify", "Cache");
|
|
45
|
-
return path.join(homedir(), ".cache", "stupify");
|
|
46
|
-
}
|
|
47
|
-
function safeNamespace(value) {
|
|
48
|
-
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
49
|
-
}
|
|
50
|
-
function stableStringify(value) {
|
|
51
|
-
if (value === null || typeof value !== "object")
|
|
52
|
-
return JSON.stringify(value);
|
|
53
|
-
if (Array.isArray(value))
|
|
54
|
-
return `[${value.map(stableStringify).join(",")}]`;
|
|
55
|
-
const record = value;
|
|
56
|
-
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
|
|
57
|
-
}
|
package/dist/checks.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { type StupifyCheck } from "./types.ts";
|
|
2
|
-
export declare const defaultChecks: readonly StupifyCheck[];
|
|
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
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import { checkId } from "./types.js";
|
|
2
|
-
export const defaultChecks = [
|
|
3
|
-
{
|
|
4
|
-
id: checkId("duplicated_schema"),
|
|
5
|
-
name: "Duplicated schema",
|
|
6
|
-
question: "Did the change duplicate an existing type, schema, payload, or DTO shape?",
|
|
7
|
-
why: "Duplicated shapes make it easier for AI-assisted changes to drift away from the real source of truth.",
|
|
8
|
-
lookFor: [
|
|
9
|
-
"local shape mirrors existing fields and maps them one-for-one",
|
|
10
|
-
"new response, payload, schema, or DTO adds no filtering, renaming, validation, or versioning",
|
|
11
|
-
],
|
|
12
|
-
ignoreWhen: [
|
|
13
|
-
"test fixture, mock, or intentional external contract",
|
|
14
|
-
"public API DTO filters, omits, protects, renames, or versions fields",
|
|
15
|
-
],
|
|
16
|
-
hookMode: "warn",
|
|
17
|
-
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.",
|
|
18
|
-
searchExamples: {
|
|
19
|
-
match: [
|
|
20
|
-
"LocalUserPayload repeats User fields and maps id/email/displayName one-for-one.",
|
|
21
|
-
],
|
|
22
|
-
nonMatch: [
|
|
23
|
-
"PublicWebhookDto omits privateNotes from InternalJob.",
|
|
24
|
-
"A client type describes an external dependency boundary.",
|
|
25
|
-
],
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: checkId("unnecessary_complexity"),
|
|
30
|
-
name: "Unnecessary complexity",
|
|
31
|
-
question: "Did the change add structure without buying clarity?",
|
|
32
|
-
why: "Extra indirection can hide simple decisions and make the code feel more designed than understood.",
|
|
33
|
-
lookFor: [
|
|
34
|
-
"helper, wrapper, service, layer, or extra file around simple logic without reuse",
|
|
35
|
-
],
|
|
36
|
-
ignoreWhen: [
|
|
37
|
-
"isolates dependency, removes duplication, or improves testability",
|
|
38
|
-
],
|
|
39
|
-
hookMode: "warn",
|
|
40
|
-
searchPrompt: `Find staged changes where a locally simple decision is made harder to understand by new indirection.
|
|
41
|
-
Only match when the staged diff clearly shows:
|
|
42
|
-
- a new named helper, wrapper, service, adapter, boundary, or abstraction
|
|
43
|
-
- and the surrounding change still appears locally simple
|
|
44
|
-
- and the new structure makes the decision harder to see
|
|
45
|
-
Do not match:
|
|
46
|
-
- plain conditionals, guard clauses, skip paths, or error handling
|
|
47
|
-
- normal feature structure
|
|
48
|
-
- exported utilities that are part of a real feature
|
|
49
|
-
- command plumbing
|
|
50
|
-
- prompt/instruction files
|
|
51
|
-
- domain configuration
|
|
52
|
-
- refactors that make ownership clearer
|
|
53
|
-
- changes where the payoff is unclear from the diff
|
|
54
|
-
Prefer no match over a weak match.`,
|
|
55
|
-
searchExamples: {
|
|
56
|
-
match: [
|
|
57
|
-
"A small inline operation becomes a helper/service/wrapper with one obvious caller.",
|
|
58
|
-
"A straightforward flow is split across files in a way that hides the decision.",
|
|
59
|
-
"A new abstraction appears before there is evidence it buys clarity, correctness, reuse, or isolation.",
|
|
60
|
-
],
|
|
61
|
-
nonMatch: [
|
|
62
|
-
"A real external dependency boundary is isolated.",
|
|
63
|
-
"A security/auth boundary becomes clearer.",
|
|
64
|
-
"A refactor removes larger complexity elsewhere.",
|
|
65
|
-
"Framework-required structure is added.",
|
|
66
|
-
],
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
id: checkId("fake_precision_windowing"),
|
|
71
|
-
name: "Fake precision windowing",
|
|
72
|
-
question: "Did the change add fake precision around model context?",
|
|
73
|
-
why: "Precise-looking bookkeeping can create confidence without improving the actual behavior.",
|
|
74
|
-
lookFor: [
|
|
75
|
-
"precise-looking counts, budgets, ratios, reports, or batching fields without useful behavior",
|
|
76
|
-
],
|
|
77
|
-
ignoreWhen: [
|
|
78
|
-
"simple fixed cap or chunking",
|
|
79
|
-
"external API requirement",
|
|
80
|
-
],
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
id: checkId("coauthored_slop"),
|
|
84
|
-
name: "Coauthored slop",
|
|
85
|
-
question: "Does author metadata contain co-author text?",
|
|
86
|
-
why: "Careless metadata is a cheap signal that the change may not have been reviewed with intent.",
|
|
87
|
-
lookFor: [
|
|
88
|
-
"author signal contains coauhtoried, coauthored, or co-authored text",
|
|
89
|
-
],
|
|
90
|
-
ignoreWhen: [
|
|
91
|
-
"normal Co-authored-by trailer in the commit body",
|
|
92
|
-
],
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
id: checkId("mega_file"),
|
|
96
|
-
name: "Mega file",
|
|
97
|
-
question: "Is a touched non-config file over 1000 LOC?",
|
|
98
|
-
why: "Large files make judgment harder by concentrating unrelated decisions in one place.",
|
|
99
|
-
lookFor: [
|
|
100
|
-
"touched non-config source file over 1000 LOC",
|
|
101
|
-
],
|
|
102
|
-
ignoreWhen: [
|
|
103
|
-
"config, lock, generated, fixture, or vendored file",
|
|
104
|
-
],
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
id: checkId("over_commenting"),
|
|
108
|
-
name: "Over commenting",
|
|
109
|
-
question: "Did the change add noisy comments?",
|
|
110
|
-
why: "Narrative comments can make routine code look deliberate without clarifying the underlying tradeoff.",
|
|
111
|
-
lookFor: [
|
|
112
|
-
"comments restate obvious code or narrate simple logic",
|
|
113
|
-
],
|
|
114
|
-
ignoreWhen: [
|
|
115
|
-
"comment explains intent, constraint, workaround, or public API behavior",
|
|
116
|
-
],
|
|
117
|
-
hookMode: "warn",
|
|
118
|
-
searchPrompt: "Find staged changes where comments appear to substitute for judgment rather than clarify it.",
|
|
119
|
-
searchExamples: {
|
|
120
|
-
match: [
|
|
121
|
-
"New comments narrate obvious code instead of explaining tradeoffs.",
|
|
122
|
-
"A simple change gains multiple generic comments that restate control flow.",
|
|
123
|
-
"Comments make the code look more deliberate without adding useful reasoning.",
|
|
124
|
-
],
|
|
125
|
-
nonMatch: [
|
|
126
|
-
"Comments explain a real domain constraint.",
|
|
127
|
-
"Comments document an external API quirk.",
|
|
128
|
-
"Comments clarify a surprising edge case.",
|
|
129
|
-
"Comments are sparse and specific.",
|
|
130
|
-
"Comments explain provider, finance, reconciliation, timezone, or ledger behavior.",
|
|
131
|
-
],
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
id: checkId("lint_bypass"),
|
|
136
|
-
name: "Lint bypass",
|
|
137
|
-
question: "Did the change bypass lint or type rules?",
|
|
138
|
-
why: "Unexplained suppressions remove useful feedback exactly where a change needs more scrutiny.",
|
|
139
|
-
lookFor: [
|
|
140
|
-
"adds suppressions, any, broad casts, or weakens lint/typecheck config",
|
|
141
|
-
],
|
|
142
|
-
ignoreWhen: [
|
|
143
|
-
"narrow suppression with a reason",
|
|
144
|
-
"type-level test",
|
|
145
|
-
"generated file convention",
|
|
146
|
-
],
|
|
147
|
-
hookMode: "warn",
|
|
148
|
-
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.",
|
|
149
|
-
searchExamples: {
|
|
150
|
-
match: [
|
|
151
|
-
"A bare // @ts-ignore hides property access on unknown input.",
|
|
152
|
-
],
|
|
153
|
-
nonMatch: [
|
|
154
|
-
"// @ts-expect-error explains a known external library typing gap.",
|
|
155
|
-
],
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
id: checkId("inconsistent_patterns"),
|
|
160
|
-
name: "Inconsistent patterns",
|
|
161
|
-
question: "Does the change clash with nearby patterns?",
|
|
162
|
-
why: "Pattern drift can signal that a change followed generic suggestions instead of local codebase judgment.",
|
|
163
|
-
lookFor: [
|
|
164
|
-
"same job uses different naming, errors, state, imports, or layout than nearby files",
|
|
165
|
-
],
|
|
166
|
-
ignoreWhen: [
|
|
167
|
-
"external API requires it",
|
|
168
|
-
"change follows a newer local convention",
|
|
169
|
-
],
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
id: checkId("reinvented_utils"),
|
|
173
|
-
name: "Reinvented utils",
|
|
174
|
-
question: "Did the change recreate an existing utility?",
|
|
175
|
-
why: "Generic helper reinvention can be a sign that the change optimized for plausible code over local reuse.",
|
|
176
|
-
lookFor: [
|
|
177
|
-
"new helper duplicates local utility or standard library behavior",
|
|
178
|
-
],
|
|
179
|
-
ignoreWhen: [
|
|
180
|
-
"existing utility has wrong contract",
|
|
181
|
-
"new helper is clearer as a tiny private expression",
|
|
182
|
-
"helper is domain-specific or used by multiple local call sites",
|
|
183
|
-
],
|
|
184
|
-
hookMode: "warn",
|
|
185
|
-
searchPrompt: "Find only tiny generic utility functions that recreate common helpers such as clamp, debounce, throttle, slugify, sort, pick, omit, uniq, or shuffle without domain-specific behavior. Do not match group/resolve/parse/format helpers, domain formatting, feature constants, or helpers with multiple obvious call sites.",
|
|
186
|
-
searchExamples: {
|
|
187
|
-
match: [
|
|
188
|
-
"clampValue returns min, max, or value.",
|
|
189
|
-
],
|
|
190
|
-
nonMatch: [
|
|
191
|
-
"formatCurrencyHelper is used by invoice and refund labels.",
|
|
192
|
-
"Subscription tier constants encode domain configuration.",
|
|
193
|
-
],
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
id: checkId("operator_style_mismatch"),
|
|
198
|
-
name: "Operator style mismatch",
|
|
199
|
-
question: "Does the change read unlike the surrounding code?",
|
|
200
|
-
why: "Style mismatch can reveal generic generated code that was not reconciled with nearby conventions.",
|
|
201
|
-
lookFor: [
|
|
202
|
-
"generic or template-like names, abstractions, comments, or control flow clash with local style",
|
|
203
|
-
],
|
|
204
|
-
ignoreWhen: [
|
|
205
|
-
"generated, vendored, framework-required, or newer established local style",
|
|
206
|
-
],
|
|
207
|
-
enabledByDefault: false,
|
|
208
|
-
},
|
|
209
|
-
];
|
|
210
|
-
export function enabledChecks(checkIds) {
|
|
211
|
-
if (!checkIds)
|
|
212
|
-
return defaultChecks.filter((check) => check.enabledByDefault !== false);
|
|
213
|
-
return checksById(checkIds);
|
|
214
|
-
}
|
|
215
|
-
export function searchChecks(checkIds) {
|
|
216
|
-
if (!checkIds)
|
|
217
|
-
return defaultChecks.filter((check) => check.hookMode === "warn");
|
|
218
|
-
return checksById(checkIds);
|
|
219
|
-
}
|
|
220
|
-
function checksById(checkIds) {
|
|
221
|
-
const checksById = new Map(defaultChecks.map((check) => [check.id, check]));
|
|
222
|
-
return checkIds.map((id) => {
|
|
223
|
-
const check = checksById.get(id);
|
|
224
|
-
if (!check)
|
|
225
|
-
throw new Error(`Unknown check: ${id}`);
|
|
226
|
-
return check;
|
|
227
|
-
});
|
|
228
|
-
}
|
package/dist/command.d.ts
DELETED
package/dist/command.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_MODEL_ID, MODEL_REGISTRY } from "./constants.js";
|
|
2
|
-
const DEFAULT_SINCE = "2 weeks ago";
|
|
3
|
-
const DEFAULT_MAX_CANDIDATES = 50;
|
|
4
|
-
const DEFAULT_MAX_SEARCH_INPUT_TOKENS = 12_000;
|
|
5
|
-
export function parseCommand(argv) {
|
|
6
|
-
if (argv.length === 1 && argv[0] && isHelp(argv[0]))
|
|
7
|
-
return { kind: "help" };
|
|
8
|
-
if (argv[0] === "bench") {
|
|
9
|
-
if (argv[1] !== "search" || !argv[2] || argv.length > 3) {
|
|
10
|
-
throw new Error("Usage: stupify bench search <config.json>");
|
|
11
|
-
}
|
|
12
|
-
return { kind: "bench-search", configPath: argv[2] };
|
|
13
|
-
}
|
|
14
|
-
if (argv[0] === "hook") {
|
|
15
|
-
const action = argv[1];
|
|
16
|
-
if (!action || !isHookAction(action) || argv.length > 2) {
|
|
17
|
-
throw new Error("Usage: stupify hook install|uninstall|status");
|
|
18
|
-
}
|
|
19
|
-
return { kind: "hook", action };
|
|
20
|
-
}
|
|
21
|
-
if (argv[0] === "doctor") {
|
|
22
|
-
if (argv.length > 1)
|
|
23
|
-
throw new Error("Usage: stupify doctor");
|
|
24
|
-
return { kind: "doctor" };
|
|
25
|
-
}
|
|
26
|
-
const initialState = {
|
|
27
|
-
inputMode: { kind: "since", since: DEFAULT_SINCE, source: "since" },
|
|
28
|
-
explicitInputMode: false,
|
|
29
|
-
checkIds: null,
|
|
30
|
-
json: false,
|
|
31
|
-
model: DEFAULT_MODEL_ID,
|
|
32
|
-
debugSem: false,
|
|
33
|
-
maxCandidates: DEFAULT_MAX_CANDIDATES,
|
|
34
|
-
maxSearchInputTokens: DEFAULT_MAX_SEARCH_INPUT_TOKENS,
|
|
35
|
-
searchProfilePath: null,
|
|
36
|
-
includeCounterReasonInPrompt: false,
|
|
37
|
-
};
|
|
38
|
-
const finalState = parseFrom(0, initialState);
|
|
39
|
-
return {
|
|
40
|
-
...finalState.inputMode,
|
|
41
|
-
mode: "search",
|
|
42
|
-
checkIds: finalState.checkIds,
|
|
43
|
-
json: finalState.json,
|
|
44
|
-
model: finalState.model,
|
|
45
|
-
debugSem: finalState.debugSem,
|
|
46
|
-
maxCandidates: finalState.maxCandidates,
|
|
47
|
-
maxSearchInputTokens: finalState.maxSearchInputTokens,
|
|
48
|
-
searchProfilePath: finalState.searchProfilePath,
|
|
49
|
-
includeCounterReasonInPrompt: finalState.includeCounterReasonInPrompt,
|
|
50
|
-
};
|
|
51
|
-
function parseFrom(index, state) {
|
|
52
|
-
if (index >= argv.length)
|
|
53
|
-
return state;
|
|
54
|
-
const arg = argv[index];
|
|
55
|
-
if (arg === "--mode") {
|
|
56
|
-
const value = argv[index + 1];
|
|
57
|
-
if (value !== "search")
|
|
58
|
-
throw new Error("--mode only supports search.");
|
|
59
|
-
return parseFrom(index + 2, state);
|
|
60
|
-
}
|
|
61
|
-
if (arg === "--staged")
|
|
62
|
-
return parseFrom(index + 1, setInputMode(state, { kind: "staged", source: "staged" }));
|
|
63
|
-
if (arg === "--stdin")
|
|
64
|
-
return parseFrom(index + 1, setInputMode(state, { kind: "stdin", source: "stdin" }));
|
|
65
|
-
if (arg === "--json")
|
|
66
|
-
return parseFrom(index + 1, { ...state, json: true });
|
|
67
|
-
if (arg === "--debug-sem")
|
|
68
|
-
return parseFrom(index + 1, { ...state, debugSem: true });
|
|
69
|
-
if (arg === "--include-counter-reason-in-prompt") {
|
|
70
|
-
return parseFrom(index + 1, { ...state, includeCounterReasonInPrompt: true });
|
|
71
|
-
}
|
|
72
|
-
if (arg === "--search-profile") {
|
|
73
|
-
const value = argv[index + 1];
|
|
74
|
-
if (!value || value.startsWith("-"))
|
|
75
|
-
throw new Error("--search-profile requires a JSON profile path.");
|
|
76
|
-
return parseFrom(index + 2, { ...state, searchProfilePath: value });
|
|
77
|
-
}
|
|
78
|
-
if (arg === "--since") {
|
|
79
|
-
const value = argv[index + 1];
|
|
80
|
-
if (!value || value.startsWith("-"))
|
|
81
|
-
throw new Error("--since requires a git date, such as \"2 weeks ago\".");
|
|
82
|
-
return parseFrom(index + 2, setInputMode(state, { kind: "since", since: value, source: "since" }));
|
|
83
|
-
}
|
|
84
|
-
if (arg === "--commit") {
|
|
85
|
-
const value = argv[index + 1];
|
|
86
|
-
if (!value || !isSafeCommitArg(value))
|
|
87
|
-
throw new Error("Invalid commit.");
|
|
88
|
-
return parseFrom(index + 2, setInputMode(state, { kind: "commit", commit: value, source: "commit" }));
|
|
89
|
-
}
|
|
90
|
-
if (arg === "--commits") {
|
|
91
|
-
const value = argv[index + 1];
|
|
92
|
-
const count = Number(value);
|
|
93
|
-
if (!Number.isInteger(count) || count < 1)
|
|
94
|
-
throw new Error("--commits requires a positive integer.");
|
|
95
|
-
return parseFrom(index + 2, setInputMode(state, { kind: "commits", count, source: "commits" }));
|
|
96
|
-
}
|
|
97
|
-
if (arg === "--checks") {
|
|
98
|
-
const value = argv[index + 1];
|
|
99
|
-
if (!value || value.startsWith("-"))
|
|
100
|
-
throw new Error("--checks requires a comma-separated list.");
|
|
101
|
-
const checkIds = value.split(",").map((id) => id.trim()).filter(Boolean);
|
|
102
|
-
if (checkIds.length === 0)
|
|
103
|
-
throw new Error("--checks requires at least one check id.");
|
|
104
|
-
return parseFrom(index + 2, { ...state, checkIds });
|
|
105
|
-
}
|
|
106
|
-
if (arg === "--model") {
|
|
107
|
-
const value = argv[index + 1];
|
|
108
|
-
if (!value || !isModelId(value))
|
|
109
|
-
throw new Error(`--model must be one of: ${Object.keys(MODEL_REGISTRY).join(", ")}`);
|
|
110
|
-
return parseFrom(index + 2, { ...state, model: value });
|
|
111
|
-
}
|
|
112
|
-
if (arg === "--max-candidates") {
|
|
113
|
-
const value = argv[index + 1];
|
|
114
|
-
const maxCandidates = Number(value);
|
|
115
|
-
if (!Number.isInteger(maxCandidates) || maxCandidates < 1) {
|
|
116
|
-
throw new Error("--max-candidates requires a positive integer.");
|
|
117
|
-
}
|
|
118
|
-
return parseFrom(index + 2, { ...state, maxCandidates });
|
|
119
|
-
}
|
|
120
|
-
if (arg === "--max-search-input-tokens") {
|
|
121
|
-
const value = argv[index + 1];
|
|
122
|
-
const maxSearchInputTokens = Number(value);
|
|
123
|
-
if (!Number.isInteger(maxSearchInputTokens) || maxSearchInputTokens < 1) {
|
|
124
|
-
throw new Error("--max-search-input-tokens requires a positive integer.");
|
|
125
|
-
}
|
|
126
|
-
return parseFrom(index + 2, { ...state, maxSearchInputTokens });
|
|
127
|
-
}
|
|
128
|
-
throw new Error(`Unknown option: ${arg}`);
|
|
129
|
-
}
|
|
130
|
-
function setInputMode(state, next) {
|
|
131
|
-
if (state.explicitInputMode)
|
|
132
|
-
throw new Error("Choose only one input mode: --since, --stdin, --commit, --commits, or --staged.");
|
|
133
|
-
return { ...state, inputMode: next, explicitInputMode: true };
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function isSafeCommitArg(value) {
|
|
137
|
-
return value.length > 0 && !value.startsWith("-") && /^[A-Za-z0-9._/@~^:+-]+$/.test(value);
|
|
138
|
-
}
|
|
139
|
-
function isHelp(value) {
|
|
140
|
-
return value === "--help" || value === "-h";
|
|
141
|
-
}
|
|
142
|
-
function isModelId(value) {
|
|
143
|
-
return value in MODEL_REGISTRY;
|
|
144
|
-
}
|
|
145
|
-
function isHookAction(value) {
|
|
146
|
-
return value === "install" || value === "uninstall" || value === "status";
|
|
147
|
-
}
|
package/dist/constants.d.ts
DELETED
package/dist/constants.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
export const VERSION = "0.0.15";
|
|
2
|
-
export const DEFAULT_MODEL_ID = "gemma-4-e2b";
|
|
3
|
-
export const MODEL_REGISTRY = {
|
|
4
|
-
"gemma-4-e2b": {
|
|
5
|
-
id: "gemma-4-e2b",
|
|
6
|
-
name: "Gemma 4 E2B Instruct Q4_K_M",
|
|
7
|
-
size: "about 3.1 GB",
|
|
8
|
-
file: "gemma-4-e2b-it-q4_k_m.gguf",
|
|
9
|
-
url: "https://huggingface.co/unsloth/gemma-4-E2B-it-GGUF/resolve/main/gemma-4-E2B-it-Q4_K_M.gguf?download=true",
|
|
10
|
-
},
|
|
11
|
-
"gemma-4-e4b": {
|
|
12
|
-
id: "gemma-4-e4b",
|
|
13
|
-
name: "Gemma 4 E4B Instruct Q4_K_M",
|
|
14
|
-
size: "about 5.0 GB",
|
|
15
|
-
file: "gemma-4-e4b-it-q4_k_m.gguf",
|
|
16
|
-
url: "https://huggingface.co/unsloth/gemma-4-E4B-it-GGUF/resolve/main/gemma-4-E4B-it-Q4_K_M.gguf?download=true",
|
|
17
|
-
},
|
|
18
|
-
"gemma-4-26b-a4b": {
|
|
19
|
-
id: "gemma-4-26b-a4b",
|
|
20
|
-
name: "Gemma 4 26B A4B Instruct UD-IQ2_XXS",
|
|
21
|
-
size: "about 9.9 GB",
|
|
22
|
-
file: "gemma-4-26b-a4b-it-ud-iq2_xxs.gguf",
|
|
23
|
-
url: "https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/resolve/main/gemma-4-26B-A4B-it-UD-IQ2_XXS.gguf?download=true",
|
|
24
|
-
},
|
|
25
|
-
"qwen3-4b-magicquant": {
|
|
26
|
-
id: "qwen3-4b-magicquant",
|
|
27
|
-
name: "Qwen3-4B-Instruct-2507 MagicQuant Q4_K_M",
|
|
28
|
-
size: "about 2.4 GB",
|
|
29
|
-
file: "qwen3-4b-instruct-2507-magicquant-q4_k_m.gguf",
|
|
30
|
-
url: "https://huggingface.co/magiccodingman/Qwen3-4B-Instruct-2507-Unsloth-MagicQuant-v2-GGUF/resolve/main/Model-MQ-Q4_K_M_1.gguf?download=true",
|
|
31
|
-
},
|
|
32
|
-
"qwen2.5-coder-1.5b": {
|
|
33
|
-
id: "qwen2.5-coder-1.5b",
|
|
34
|
-
name: "Qwen2.5-Coder-1.5B-Instruct Q4_K_M",
|
|
35
|
-
size: "about 1.1 GB",
|
|
36
|
-
file: "qwen2.5-coder-1.5b-instruct-q4_k_m.gguf",
|
|
37
|
-
url: "https://huggingface.co/Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF/resolve/main/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf?download=true",
|
|
38
|
-
},
|
|
39
|
-
"qwen2.5-coder-7b": {
|
|
40
|
-
id: "qwen2.5-coder-7b",
|
|
41
|
-
name: "Qwen2.5-Coder-7B-Instruct Q4_K_M",
|
|
42
|
-
size: "about 4.7 GB",
|
|
43
|
-
file: "qwen2.5-coder-7b-instruct-q4_k_m.gguf",
|
|
44
|
-
url: "https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct-GGUF/resolve/main/qwen2.5-coder-7b-instruct-q4_k_m.gguf?download=true",
|
|
45
|
-
},
|
|
46
|
-
"qwen2.5-coder-32b": {
|
|
47
|
-
id: "qwen2.5-coder-32b",
|
|
48
|
-
name: "Qwen2.5-Coder-32B-Instruct Q4_K_M",
|
|
49
|
-
size: "about 19 GB",
|
|
50
|
-
file: "qwen2.5-coder-32b-instruct-q4_k_m.gguf",
|
|
51
|
-
url: "https://huggingface.co/Qwen/Qwen2.5-Coder-32B-Instruct-GGUF/resolve/main/qwen2.5-coder-32b-instruct-q4_k_m.gguf?download=true",
|
|
52
|
-
},
|
|
53
|
-
};
|
package/dist/counter-scout.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { CheckId, SemCandidate, SemChangeSet, StupifyCheck } from "./types.ts";
|
|
2
|
-
type Signal = Readonly<{
|
|
3
|
-
checkId: CheckId;
|
|
4
|
-
entityId: string;
|
|
5
|
-
reasonCode: string;
|
|
6
|
-
}>;
|
|
7
|
-
type SignalBucket = Readonly<{
|
|
8
|
-
checkId: CheckId;
|
|
9
|
-
total: number;
|
|
10
|
-
examples: readonly Signal[];
|
|
11
|
-
}>;
|
|
12
|
-
export type CounterScoutPlan = Readonly<{
|
|
13
|
-
buckets: readonly SignalBucket[];
|
|
14
|
-
totalSignals: number;
|
|
15
|
-
maxTargets: number;
|
|
16
|
-
targets: readonly SemCandidate[];
|
|
17
|
-
}>;
|
|
18
|
-
export declare function counterScoutTargets(changeSet: SemChangeSet, checks: readonly StupifyCheck[], maxTargets: number): readonly SemCandidate[];
|
|
19
|
-
export declare function counterScoutPlan(changeSet: SemChangeSet, checks: readonly StupifyCheck[], maxTargets: number): CounterScoutPlan;
|
|
20
|
-
export declare function runSignalCounters(changeSet: SemChangeSet, checks: readonly StupifyCheck[]): readonly SignalBucket[];
|
|
21
|
-
export {};
|