@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/src/types.ts
CHANGED
|
@@ -13,40 +13,39 @@ export function checkId(value: string): CheckId {
|
|
|
13
13
|
return value as CheckId;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export type
|
|
17
|
-
export type
|
|
18
|
-
export type
|
|
19
|
-
export type AuditPromptName = "strict" | "high_bar";
|
|
16
|
+
export type SearchMode = "warn" | "off";
|
|
17
|
+
export type HookAction = "install" | "uninstall" | "status";
|
|
18
|
+
export type SearchSource = "since" | "stdin" | "commit" | "commits" | "staged";
|
|
20
19
|
|
|
21
|
-
type
|
|
20
|
+
type SearchOptions = Readonly<{
|
|
22
21
|
checkIds: readonly string[] | null;
|
|
23
22
|
json: boolean;
|
|
24
23
|
model: ModelId;
|
|
25
|
-
engine: Engine;
|
|
26
|
-
scout: ScoutMode;
|
|
27
|
-
auditContext: AuditContextMode;
|
|
28
|
-
auditPrompt: AuditPromptName;
|
|
29
24
|
debugSem: boolean;
|
|
30
|
-
debugTargets: boolean;
|
|
31
25
|
maxCandidates: number;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
maxSearchInputTokens: number;
|
|
27
|
+
searchProfilePath: string | null;
|
|
28
|
+
includeCounterReasonInPrompt: boolean;
|
|
35
29
|
}>;
|
|
36
30
|
|
|
37
31
|
export type Command =
|
|
38
32
|
| Readonly<{ kind: "help" }>
|
|
39
|
-
| Readonly<{ kind: "
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
| (Readonly<{ kind: "
|
|
43
|
-
| (Readonly<{ kind: "
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
| Readonly<{ kind: "hook"; action: HookAction }>
|
|
34
|
+
| Readonly<{ kind: "doctor" }>
|
|
35
|
+
| Readonly<{ kind: "bench-search"; configPath: string }>
|
|
36
|
+
| (Readonly<{ kind: "since"; since: string; mode: "search"; source: "since" }> & SearchOptions)
|
|
37
|
+
| (Readonly<{ kind: "stdin"; mode: "search"; source: "stdin" }> & SearchOptions)
|
|
38
|
+
| (Readonly<{ kind: "commit"; commit: string; mode: "search"; source: "commit" }> & SearchOptions)
|
|
39
|
+
| (Readonly<{ kind: "commits"; count: number; mode: "search"; source: "commits" }> & SearchOptions)
|
|
40
|
+
| (Readonly<{ kind: "staged"; mode: "search"; source: "staged" }> & SearchOptions);
|
|
41
|
+
|
|
42
|
+
export type SearchCommand = Exclude<
|
|
43
|
+
Command,
|
|
46
44
|
| Readonly<{ kind: "help" }>
|
|
47
|
-
| Readonly<{ kind: "
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
| Readonly<{ kind: "hook"; action: HookAction }>
|
|
46
|
+
| Readonly<{ kind: "doctor" }>
|
|
47
|
+
| Readonly<{ kind: "bench-search"; configPath: string }>
|
|
48
|
+
>;
|
|
50
49
|
|
|
51
50
|
export type StupifyCheck = Readonly<{
|
|
52
51
|
id: CheckId;
|
|
@@ -55,48 +54,29 @@ export type StupifyCheck = Readonly<{
|
|
|
55
54
|
lookFor: readonly string[];
|
|
56
55
|
ignoreWhen: readonly string[];
|
|
57
56
|
enabledByDefault?: boolean;
|
|
57
|
+
hookMode?: SearchMode;
|
|
58
|
+
searchPrompt?: string;
|
|
59
|
+
searchExamples?: Readonly<{
|
|
60
|
+
match: readonly string[];
|
|
61
|
+
nonMatch: readonly string[];
|
|
62
|
+
}>;
|
|
58
63
|
examples?: Readonly<{
|
|
59
64
|
match?: readonly string[];
|
|
60
65
|
noMatch?: readonly string[];
|
|
61
66
|
}>;
|
|
62
67
|
}>;
|
|
63
68
|
|
|
64
|
-
export type FindingCandidate = Readonly<{
|
|
65
|
-
checkId: string;
|
|
66
|
-
why: string;
|
|
67
|
-
proof: string;
|
|
68
|
-
}>;
|
|
69
|
-
|
|
70
|
-
export type Finding = Readonly<{
|
|
71
|
-
sourceId: SourceId;
|
|
72
|
-
checkId: CheckId;
|
|
73
|
-
why: string;
|
|
74
|
-
proof: string;
|
|
75
|
-
}>;
|
|
76
|
-
|
|
77
|
-
export type FindingsResult = Readonly<{
|
|
78
|
-
findings: readonly Finding[];
|
|
79
|
-
summary?: string;
|
|
80
|
-
}>;
|
|
81
|
-
|
|
82
|
-
export type AuditReviewStats = Readonly<{
|
|
83
|
-
totalTargets: number;
|
|
84
|
-
finding: number;
|
|
85
|
-
clean: number;
|
|
86
|
-
uncertain: number;
|
|
87
|
-
invalid: number;
|
|
88
|
-
}>;
|
|
89
|
-
|
|
90
|
-
export type AuditReviewResult = FindingsResult & Readonly<{
|
|
91
|
-
stats: AuditReviewStats;
|
|
92
|
-
}>;
|
|
93
|
-
|
|
94
69
|
export type NetDiffStats = Readonly<{
|
|
95
70
|
filesChanged: number;
|
|
96
71
|
additions: number;
|
|
97
72
|
deletions: number;
|
|
98
73
|
}>;
|
|
99
74
|
|
|
75
|
+
export type StagedDiff = Readonly<{
|
|
76
|
+
text: string;
|
|
77
|
+
stats: NetDiffStats;
|
|
78
|
+
}>;
|
|
79
|
+
|
|
100
80
|
export type NetDiff = Readonly<{
|
|
101
81
|
id: SourceId;
|
|
102
82
|
label: string;
|
|
@@ -114,27 +94,6 @@ export type SourceRange = Readonly<{
|
|
|
114
94
|
stats: NetDiffStats;
|
|
115
95
|
}>;
|
|
116
96
|
|
|
117
|
-
export type DiffHunk = Readonly<{
|
|
118
|
-
pointer: string;
|
|
119
|
-
batchId: string;
|
|
120
|
-
fileId: string;
|
|
121
|
-
hunkId: string;
|
|
122
|
-
filePath: string;
|
|
123
|
-
lineCount: number;
|
|
124
|
-
text: string;
|
|
125
|
-
}>;
|
|
126
|
-
|
|
127
|
-
export type DiffBatch = Readonly<{
|
|
128
|
-
id: string;
|
|
129
|
-
hunks: readonly DiffHunk[];
|
|
130
|
-
text: string;
|
|
131
|
-
}>;
|
|
132
|
-
|
|
133
|
-
export type CandidateContext = Readonly<{
|
|
134
|
-
pointer: string;
|
|
135
|
-
text: string;
|
|
136
|
-
}>;
|
|
137
|
-
|
|
138
97
|
export type SemChange = Readonly<{
|
|
139
98
|
entityId: string;
|
|
140
99
|
entityName: string;
|
|
@@ -186,66 +145,176 @@ export type SemContext = Readonly<{
|
|
|
186
145
|
text: string;
|
|
187
146
|
}>;
|
|
188
147
|
|
|
189
|
-
export type DebugTarget = Readonly<{
|
|
190
|
-
targetId: string;
|
|
191
|
-
checkId: CheckId;
|
|
192
|
-
entityId: string;
|
|
193
|
-
entityKind?: string;
|
|
194
|
-
changeKind?: string;
|
|
195
|
-
scoutReason?: string;
|
|
196
|
-
sourceLabel?: string;
|
|
197
|
-
}>;
|
|
198
|
-
|
|
199
148
|
export type SemContextPack = Readonly<{
|
|
200
149
|
provider: "repomix";
|
|
201
150
|
filePaths: readonly string[];
|
|
202
151
|
totalCharacters: number;
|
|
203
152
|
totalTokens: number;
|
|
204
153
|
text: string;
|
|
154
|
+
config: RepomixSearchConfig;
|
|
205
155
|
}>;
|
|
206
156
|
|
|
207
|
-
export type
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
157
|
+
export type RepomixSearchConfig = Readonly<{
|
|
158
|
+
compress: boolean;
|
|
159
|
+
showLineNumbers: boolean;
|
|
160
|
+
removeEmptyLines: boolean;
|
|
161
|
+
maxFileSizeBytes: number;
|
|
162
|
+
maxTotalSizeBytes: number;
|
|
163
|
+
ignorePatterns: readonly string[];
|
|
164
|
+
}>;
|
|
165
|
+
|
|
166
|
+
export type SearchProfileRepomixConfig = Readonly<{
|
|
167
|
+
compress?: boolean;
|
|
168
|
+
showLineNumbers?: boolean;
|
|
169
|
+
removeEmptyLines?: boolean;
|
|
170
|
+
maxFileBytes?: number;
|
|
171
|
+
maxTotalBytes?: number;
|
|
172
|
+
ignorePatterns?: readonly string[];
|
|
173
|
+
}>;
|
|
174
|
+
|
|
175
|
+
export type SearchProfilePattern = Readonly<{
|
|
176
|
+
enabled?: boolean;
|
|
177
|
+
searchPrompt?: string;
|
|
178
|
+
matchExamples?: readonly string[];
|
|
179
|
+
nonMatchExamples?: readonly string[];
|
|
180
|
+
}>;
|
|
181
|
+
|
|
182
|
+
export type SearchProfile = Readonly<{
|
|
183
|
+
id: string;
|
|
184
|
+
context?: "repomix" | "sem";
|
|
185
|
+
maxCandidates?: number;
|
|
186
|
+
maxSearchInputTokens?: number;
|
|
187
|
+
includeCounterReasonInPrompt?: boolean;
|
|
188
|
+
repomix?: SearchProfileRepomixConfig;
|
|
189
|
+
patterns?: Readonly<Record<string, SearchProfilePattern>>;
|
|
190
|
+
}>;
|
|
191
|
+
|
|
192
|
+
export type SearchMatch = Readonly<{
|
|
193
|
+
targetId: string;
|
|
194
|
+
patternId: CheckId;
|
|
195
|
+
reason: string;
|
|
196
|
+
proof: string;
|
|
197
|
+
}>;
|
|
198
|
+
|
|
199
|
+
export type SearchRunJson = Readonly<{
|
|
200
|
+
schemaVersion: "search.v1";
|
|
201
|
+
mode: "search";
|
|
202
|
+
source: SearchSource;
|
|
203
|
+
model: Readonly<{ id: ModelId }>;
|
|
204
|
+
patterns: readonly CheckId[];
|
|
205
|
+
stats: Readonly<{
|
|
206
|
+
elapsedMs: number;
|
|
207
|
+
modelCalls: number;
|
|
208
|
+
inputTokens?: number;
|
|
209
|
+
inputTokenCap?: number;
|
|
210
|
+
skipped?: boolean;
|
|
211
|
+
skipReason?: "input_too_large" | "no_candidates";
|
|
212
|
+
filesChanged?: number;
|
|
213
|
+
entitiesScanned?: number;
|
|
214
|
+
candidates?: number;
|
|
215
|
+
repomixFiles?: number;
|
|
216
|
+
repomixTokens?: number;
|
|
217
|
+
repomixConfig?: RepomixSearchConfig;
|
|
218
|
+
searchTargets?: number;
|
|
219
|
+
profileId?: string;
|
|
220
|
+
targetsByPattern?: Readonly<Record<string, number>>;
|
|
221
|
+
targetsPreview?: readonly SearchTargetPreview[];
|
|
230
222
|
}>;
|
|
231
|
-
|
|
232
|
-
auditStats?: AuditReviewStats;
|
|
233
|
-
debugTargets?: readonly DebugTarget[];
|
|
234
|
-
traceEvents?: readonly TraceEvent[];
|
|
223
|
+
matches: readonly SearchMatch[];
|
|
235
224
|
}>;
|
|
236
225
|
|
|
237
|
-
export type
|
|
226
|
+
export type SearchTargetPreview = Readonly<{
|
|
227
|
+
targetId: string;
|
|
228
|
+
patternId: CheckId;
|
|
229
|
+
entityKind?: string;
|
|
230
|
+
sourceKind?: string;
|
|
231
|
+
}>;
|
|
232
|
+
|
|
233
|
+
export type SearchBenchConfig = Readonly<{
|
|
238
234
|
name: string;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
235
|
+
profiles: readonly string[];
|
|
236
|
+
fixtures: string;
|
|
237
|
+
realSmokeRuns?: readonly SearchBenchSmokeRun[];
|
|
238
|
+
realCommitReplay?: readonly SearchBenchCommitReplay[];
|
|
242
239
|
}>;
|
|
243
240
|
|
|
244
|
-
export type
|
|
245
|
-
|
|
246
|
-
|
|
241
|
+
export type SearchBenchSmokeRun = Readonly<{
|
|
242
|
+
id: string;
|
|
243
|
+
cwd?: string;
|
|
244
|
+
args: readonly string[];
|
|
247
245
|
}>;
|
|
248
246
|
|
|
247
|
+
export type SearchBenchCommitReplay = Readonly<{
|
|
248
|
+
id: string;
|
|
249
|
+
repoEnv?: string;
|
|
250
|
+
cwd?: string;
|
|
251
|
+
limit: number;
|
|
252
|
+
since?: string;
|
|
253
|
+
nonMerge?: boolean;
|
|
254
|
+
profiles: readonly string[];
|
|
255
|
+
}>;
|
|
256
|
+
|
|
257
|
+
export type SearchFixture = Readonly<{
|
|
258
|
+
id: string;
|
|
259
|
+
description: string;
|
|
260
|
+
stagedPatch: string;
|
|
261
|
+
checks: readonly string[];
|
|
262
|
+
expected: readonly SearchFixtureExpectation[];
|
|
263
|
+
}>;
|
|
264
|
+
|
|
265
|
+
export type SearchFixtureExpectation = Readonly<{
|
|
266
|
+
patternId: string;
|
|
267
|
+
shouldMatch: boolean;
|
|
268
|
+
}>;
|
|
269
|
+
|
|
270
|
+
export type SearchBenchRun = Readonly<{
|
|
271
|
+
profileId: string;
|
|
272
|
+
fixtureId?: string;
|
|
273
|
+
smokeId?: string;
|
|
274
|
+
elapsedMs: number;
|
|
275
|
+
modelCalls: number;
|
|
276
|
+
patterns: readonly CheckId[];
|
|
277
|
+
targets: number;
|
|
278
|
+
targetsByPattern: Readonly<Record<string, number>>;
|
|
279
|
+
inputTokens: number;
|
|
280
|
+
repomixPackedTokens?: number;
|
|
281
|
+
skipped: boolean;
|
|
282
|
+
skipReason?: string;
|
|
283
|
+
matches: readonly SearchMatch[];
|
|
284
|
+
expected?: readonly SearchFixtureExpectation[];
|
|
285
|
+
score?: number;
|
|
286
|
+
targetsPreview: readonly SearchTargetPreview[];
|
|
287
|
+
matchesUsingCounterReasonAsProof: number;
|
|
288
|
+
error?: string;
|
|
289
|
+
}>;
|
|
290
|
+
|
|
291
|
+
export type SearchBenchReplayRun = Readonly<{
|
|
292
|
+
profileId: string;
|
|
293
|
+
replayId: string;
|
|
294
|
+
commitId: string;
|
|
295
|
+
outcome: SearchReplayOutcome;
|
|
296
|
+
changedFiles: number;
|
|
297
|
+
addedLines: number;
|
|
298
|
+
deletedLines: number;
|
|
299
|
+
elapsedMs: number;
|
|
300
|
+
skipped: boolean;
|
|
301
|
+
skipReason?: string;
|
|
302
|
+
targets: number;
|
|
303
|
+
inputTokens: number;
|
|
304
|
+
repomixPackedTokens?: number;
|
|
305
|
+
modelCalls: number;
|
|
306
|
+
matches: readonly SearchMatch[];
|
|
307
|
+
matchesByPattern: Readonly<Record<string, number>>;
|
|
308
|
+
error?: string;
|
|
309
|
+
}>;
|
|
310
|
+
|
|
311
|
+
export type SearchReplayOutcome =
|
|
312
|
+
| "no_candidates"
|
|
313
|
+
| "ran_no_matches"
|
|
314
|
+
| "ran_with_matches"
|
|
315
|
+
| "skipped_input_too_large"
|
|
316
|
+
| "error";
|
|
317
|
+
|
|
249
318
|
export type ModelId =
|
|
250
319
|
| "gemma-4-e2b"
|
|
251
320
|
| "gemma-4-e4b"
|
|
@@ -258,7 +327,14 @@ export type ModelId =
|
|
|
258
327
|
export type ModelConfig = Readonly<{
|
|
259
328
|
id: ModelId;
|
|
260
329
|
name: string;
|
|
261
|
-
size: string;
|
|
262
330
|
file: string;
|
|
263
331
|
url: string;
|
|
332
|
+
size: string;
|
|
333
|
+
}>;
|
|
334
|
+
|
|
335
|
+
export type TraceEvent = Readonly<{
|
|
336
|
+
name: string;
|
|
337
|
+
ms: number;
|
|
338
|
+
count?: number;
|
|
339
|
+
detail?: string;
|
|
264
340
|
}>;
|
package/dist/batcher.d.ts
DELETED
package/dist/batcher.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
const DEFAULT_BATCH_LINES = 1_000;
|
|
2
|
-
export function batchDiff(diff, linesPerBatch = DEFAULT_BATCH_LINES) {
|
|
3
|
-
const hunks = parseHunks(diff).flatMap((item) => splitLargeHunk(item, linesPerBatch));
|
|
4
|
-
const initialState = {
|
|
5
|
-
batches: [],
|
|
6
|
-
current: [],
|
|
7
|
-
currentLines: 0,
|
|
8
|
-
batchNumber: 1,
|
|
9
|
-
};
|
|
10
|
-
const finalized = hunks.reduce((state, hunk) => {
|
|
11
|
-
const needsFlush = state.current.length > 0 &&
|
|
12
|
-
state.currentLines + hunk.lineCount > linesPerBatch;
|
|
13
|
-
const flushed = needsFlush ? flush(state) : state;
|
|
14
|
-
return {
|
|
15
|
-
...flushed,
|
|
16
|
-
current: [...flushed.current, toDiffHunk(hunk, flushed.batchNumber)],
|
|
17
|
-
currentLines: flushed.currentLines + hunk.lineCount,
|
|
18
|
-
};
|
|
19
|
-
}, initialState);
|
|
20
|
-
return finalized.current.length > 0
|
|
21
|
-
? [...finalized.batches, toBatch(finalized.batchNumber, finalized.current)]
|
|
22
|
-
: finalized.batches;
|
|
23
|
-
function flush(state) {
|
|
24
|
-
return {
|
|
25
|
-
batches: [...state.batches, toBatch(state.batchNumber, state.current)],
|
|
26
|
-
current: [],
|
|
27
|
-
currentLines: 0,
|
|
28
|
-
batchNumber: state.batchNumber + 1,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
export function allHunks(batches) {
|
|
33
|
-
return batches.flatMap((batch) => batch.hunks);
|
|
34
|
-
}
|
|
35
|
-
function parseHunks(diff) {
|
|
36
|
-
const lines = diff.split(/\r?\n/);
|
|
37
|
-
const initialState = {
|
|
38
|
-
hunks: [],
|
|
39
|
-
filePath: "unknown",
|
|
40
|
-
fileIndex: 0,
|
|
41
|
-
hunkIndex: 0,
|
|
42
|
-
fileHeader: [],
|
|
43
|
-
hunkLines: null,
|
|
44
|
-
};
|
|
45
|
-
const finalState = lines.reduce((state, line) => {
|
|
46
|
-
const fileMatch = /^diff --git a\/.+ b\/(.+)$/.exec(line);
|
|
47
|
-
if (fileMatch) {
|
|
48
|
-
const flushed = flush(state);
|
|
49
|
-
return {
|
|
50
|
-
...flushed,
|
|
51
|
-
fileIndex: flushed.fileIndex + 1,
|
|
52
|
-
hunkIndex: 0,
|
|
53
|
-
filePath: fileMatch[1],
|
|
54
|
-
fileHeader: [line],
|
|
55
|
-
hunkLines: null,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
if (line.startsWith("@@ ")) {
|
|
59
|
-
const flushed = flush(state);
|
|
60
|
-
return {
|
|
61
|
-
...flushed,
|
|
62
|
-
hunkIndex: flushed.hunkIndex + 1,
|
|
63
|
-
hunkLines: [...flushed.fileHeader, line],
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
if (state.hunkLines)
|
|
67
|
-
return { ...state, hunkLines: [...state.hunkLines, line] };
|
|
68
|
-
if (state.fileHeader.length > 0)
|
|
69
|
-
return { ...state, fileHeader: [...state.fileHeader, line] };
|
|
70
|
-
return state;
|
|
71
|
-
}, initialState);
|
|
72
|
-
return flush(finalState).hunks;
|
|
73
|
-
function flush(state) {
|
|
74
|
-
if (!state.hunkLines)
|
|
75
|
-
return state;
|
|
76
|
-
const fileId = `file-${pad(state.fileIndex)}`;
|
|
77
|
-
const hunkId = `hunk-${pad(state.hunkIndex)}`;
|
|
78
|
-
const text = state.hunkLines.join("\n").trimEnd();
|
|
79
|
-
const nextHunk = {
|
|
80
|
-
fileId,
|
|
81
|
-
hunkId,
|
|
82
|
-
filePath: state.filePath,
|
|
83
|
-
text,
|
|
84
|
-
lineCount: countLines(text),
|
|
85
|
-
};
|
|
86
|
-
return { ...state, hunks: [...state.hunks, nextHunk], hunkLines: null };
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function splitLargeHunk(hunk, linesPerBatch) {
|
|
90
|
-
if (hunk.lineCount <= linesPerBatch)
|
|
91
|
-
return [hunk];
|
|
92
|
-
const lines = hunk.text.split(/\r?\n/);
|
|
93
|
-
const chunkCount = Math.ceil(lines.length / linesPerBatch);
|
|
94
|
-
return Array.from({ length: chunkCount }, (_, chunkIndex) => {
|
|
95
|
-
const start = chunkIndex * linesPerBatch;
|
|
96
|
-
const text = lines.slice(start, start + linesPerBatch).join("\n");
|
|
97
|
-
return {
|
|
98
|
-
...hunk,
|
|
99
|
-
hunkId: `${hunk.hunkId}-part-${pad(chunkIndex + 1)}`,
|
|
100
|
-
text,
|
|
101
|
-
lineCount: countLines(text),
|
|
102
|
-
};
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
function toDiffHunk(hunk, batchNumber) {
|
|
106
|
-
const batchId = `batch-${pad(batchNumber)}`;
|
|
107
|
-
return {
|
|
108
|
-
...hunk,
|
|
109
|
-
batchId,
|
|
110
|
-
pointer: `${batchId}:${hunk.fileId}:${hunk.hunkId}`,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
function toBatch(batchNumber, hunks) {
|
|
114
|
-
const id = `batch-${pad(batchNumber)}`;
|
|
115
|
-
return {
|
|
116
|
-
id,
|
|
117
|
-
hunks,
|
|
118
|
-
text: hunks.map(formatHunkForSearch).join("\n\n"),
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
function formatHunkForSearch(hunk) {
|
|
122
|
-
return `POINTER ${hunk.pointer}
|
|
123
|
-
FILE ${hunk.fileId}
|
|
124
|
-
PATH ${hunk.filePath}
|
|
125
|
-
${searchView(hunk.text)}`;
|
|
126
|
-
}
|
|
127
|
-
function searchView(text) {
|
|
128
|
-
return text
|
|
129
|
-
.split(/\r?\n/)
|
|
130
|
-
.filter((line) => line.startsWith("diff --git ") ||
|
|
131
|
-
line.startsWith("--- ") ||
|
|
132
|
-
line.startsWith("+++ ") ||
|
|
133
|
-
line.startsWith("@@ ") ||
|
|
134
|
-
(line.startsWith("+") && !line.startsWith("+++")))
|
|
135
|
-
.join("\n");
|
|
136
|
-
}
|
|
137
|
-
function countLines(value) {
|
|
138
|
-
return value.length === 0 ? 0 : value.split(/\r?\n/).length;
|
|
139
|
-
}
|
|
140
|
-
function pad(value) {
|
|
141
|
-
return String(value).padStart(3, "0");
|
|
142
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { allHunks } from "./batcher.js";
|
|
2
|
-
const MAX_CONTEXT_LINES = 80;
|
|
3
|
-
export function candidateContexts(batches, candidatePointers) {
|
|
4
|
-
const hunks = allHunks(batches);
|
|
5
|
-
const byPointer = new Map(hunks.map((hunk) => [hunk.pointer, hunk]));
|
|
6
|
-
const uniquePointers = [...new Set(candidatePointers)]
|
|
7
|
-
.sort((left, right) => hunkPriority(byPointer.get(right)) - hunkPriority(byPointer.get(left)));
|
|
8
|
-
return uniquePointers.flatMap((pointer) => {
|
|
9
|
-
const hunk = byPointer.get(pointer);
|
|
10
|
-
if (!hunk)
|
|
11
|
-
return [];
|
|
12
|
-
return [{ pointer, text: formatHunk(hunk) }];
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
function formatHunk(hunk) {
|
|
16
|
-
return `PATH ${hunk.filePath}
|
|
17
|
-
${shorten(hunk.text)}`;
|
|
18
|
-
}
|
|
19
|
-
function shorten(text) {
|
|
20
|
-
const lines = text.split(/\r?\n/);
|
|
21
|
-
if (lines.length <= MAX_CONTEXT_LINES)
|
|
22
|
-
return text;
|
|
23
|
-
return `${lines.slice(0, MAX_CONTEXT_LINES).join("\n")}
|
|
24
|
-
[stupify: hunk shortened after ${MAX_CONTEXT_LINES} lines]`;
|
|
25
|
-
}
|
|
26
|
-
function hunkPriority(hunk) {
|
|
27
|
-
if (!hunk)
|
|
28
|
-
return 0;
|
|
29
|
-
const text = hunk.text;
|
|
30
|
-
let priority = 0;
|
|
31
|
-
if (/^\+export\s+type\s|\+export\s+interface\s|\+type\s|\+interface\s/m.test(text))
|
|
32
|
-
priority += 3;
|
|
33
|
-
if (/^\+export\s+function\s|\+function\s/m.test(text))
|
|
34
|
-
priority += 2;
|
|
35
|
-
if (/\.map\(|=>\s*\(\{|=>\s*\{/m.test(text))
|
|
36
|
-
priority += 2;
|
|
37
|
-
if (/payload|schema|dto|response|result/i.test(text))
|
|
38
|
-
priority += 1;
|
|
39
|
-
return priority;
|
|
40
|
-
}
|
package/dist/experiment.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function runExperiment(configPath: string): Promise<string>;
|