codebase-snapshot 0.9.9 → 0.9.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +494 -0
- package/README.md +3 -3
- package/dist/bin.js +5227 -3461
- package/dist/{chunk-KUVFTJHK.js → chunk-3H45YMBG.js} +1848 -507
- package/dist/{chunk-HQBEQ67K.js → chunk-5OR3WVPW.js} +366 -84
- package/dist/cli.cjs +5245 -3499
- package/dist/cli.d.cts +282 -3
- package/dist/cli.d.ts +282 -3
- package/dist/cli.js +160 -38
- package/dist/generate-worker-D5Xgh27c.d.cts +57 -0
- package/dist/generate-worker-D5Xgh27c.d.ts +57 -0
- package/dist/index.cjs +1799 -504
- package/dist/index.d.cts +293 -4
- package/dist/index.d.ts +293 -4
- package/dist/index.js +1 -3
- package/dist/mcp.cjs +5670 -4877
- package/dist/mcp.d.cts +70 -2
- package/dist/mcp.d.ts +70 -2
- package/dist/mcp.js +2 -4
- package/dist/{types-Ca4PmkiQ.d.cts → types-CTewen3J.d.cts} +115 -2
- package/dist/{types-Ca4PmkiQ.d.ts → types-CTewen3J.d.ts} +115 -2
- package/docs/agent.md +1 -1
- package/docs/cli.md +21 -3
- package/docs/configuration.md +5 -0
- package/docs/how-to-feed-a-repo-to-claude.md +61 -0
- package/docs/mcp.md +23 -10
- package/docs/output.md +36 -4
- package/docs/stale-context-is-your-bug.md +13 -0
- package/docs/vs-repomix.md +1 -1
- package/package.json +5 -3
package/dist/index.cjs
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var crypto2 = require('crypto');
|
|
4
4
|
var async_hooks = require('async_hooks');
|
|
5
|
-
var
|
|
5
|
+
var fs14 = require('fs');
|
|
6
6
|
var os = require('os');
|
|
7
|
-
var
|
|
7
|
+
var path16 = require('path');
|
|
8
8
|
var ts = require('typescript');
|
|
9
9
|
var module$1 = require('module');
|
|
10
10
|
var url = require('url');
|
|
11
11
|
var childProcess = require('child_process');
|
|
12
12
|
var https = require('https');
|
|
13
13
|
var readline = require('readline');
|
|
14
|
+
require('worker_threads');
|
|
14
15
|
|
|
15
16
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
17
|
|
|
@@ -32,10 +33,10 @@ function _interopNamespace(e) {
|
|
|
32
33
|
return Object.freeze(n);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
var
|
|
36
|
-
var
|
|
36
|
+
var crypto2__namespace = /*#__PURE__*/_interopNamespace(crypto2);
|
|
37
|
+
var fs14__namespace = /*#__PURE__*/_interopNamespace(fs14);
|
|
37
38
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
38
|
-
var
|
|
39
|
+
var path16__namespace = /*#__PURE__*/_interopNamespace(path16);
|
|
39
40
|
var ts__default = /*#__PURE__*/_interopDefault(ts);
|
|
40
41
|
var childProcess__namespace = /*#__PURE__*/_interopNamespace(childProcess);
|
|
41
42
|
var https__namespace = /*#__PURE__*/_interopNamespace(https);
|
|
@@ -155,6 +156,59 @@ var cfamilyProfile = {
|
|
|
155
156
|
}
|
|
156
157
|
};
|
|
157
158
|
|
|
159
|
+
// src/ast/profiles/dart.ts
|
|
160
|
+
var IMPORT = /^import\s+['"]([^'"]+)['"]/;
|
|
161
|
+
var PART = /^part(?:\s+of)?\s+['"]?([\w./]+)['"]?/;
|
|
162
|
+
var TYPE = /^(?:abstract\s+|base\s+|final\s+|sealed\s+|interface\s+)*(class|mixin|enum|extension|typedef)\s+([A-Za-z_]\w*)/;
|
|
163
|
+
var FUNC = /^(?:Future<[^>]*>|Stream<[^>]*>|void|bool|int|double|num|String|dynamic|var|[A-Z][\w<>, ?]*)\s+([A-Za-z_]\w*)\s*[(<]/;
|
|
164
|
+
var VARIABLE = /^(?:late\s+)?(?:final|const|var)\s+(?:[\w<>?,. ]+?\s+)?([A-Za-z_]\w*)\s*=/;
|
|
165
|
+
var dartProfile = {
|
|
166
|
+
id: "dart",
|
|
167
|
+
extensions: [".dart"],
|
|
168
|
+
summarize(_path, content) {
|
|
169
|
+
const summary = emptySummary();
|
|
170
|
+
eachLine(content, (line) => {
|
|
171
|
+
const trimmed = line.trim();
|
|
172
|
+
if (!trimmed || trimmed.startsWith("//")) return;
|
|
173
|
+
const imported = IMPORT.exec(trimmed);
|
|
174
|
+
if (imported) {
|
|
175
|
+
summary.imports.push(imported[1]);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (/^part\b/.test(trimmed)) {
|
|
179
|
+
const part = PART.exec(trimmed);
|
|
180
|
+
summary.directives.push(part ? `part ${part[1]}` : "part");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!isTopLevel(line)) return;
|
|
184
|
+
const type = TYPE.exec(trimmed);
|
|
185
|
+
if (type) {
|
|
186
|
+
const [, keyword, name] = type;
|
|
187
|
+
if (keyword === "mixin" || keyword === "extension") summary.types.push(name);
|
|
188
|
+
else if (keyword === "enum") summary.enums.push(name);
|
|
189
|
+
else if (keyword === "typedef") summary.types.push(name);
|
|
190
|
+
else summary.classes.push(name);
|
|
191
|
+
if (isPublicName(name)) summary.exports.push(name);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const func = FUNC.exec(trimmed);
|
|
195
|
+
if (func) {
|
|
196
|
+
const name = func[1];
|
|
197
|
+
summary.functions.push(name);
|
|
198
|
+
if (isPublicName(name)) summary.exports.push(name);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const variable = VARIABLE.exec(trimmed);
|
|
202
|
+
if (variable) {
|
|
203
|
+
const name = variable[1];
|
|
204
|
+
summary.variables.push(name);
|
|
205
|
+
if (isPublicName(name)) summary.exports.push(name);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
158
212
|
// src/ast/profiles/dotnet.ts
|
|
159
213
|
var USING2 = /^using\s+([\w.]+)\s*;/;
|
|
160
214
|
var TYPE_DECL2 = /^\s*(?:public\s+|internal\s+|private\s+|protected\s+|sealed\s+|abstract\s+|static\s+|partial\s+|readonly\s+)*(class|interface|struct|record|enum)\s+([A-Za-z_]\w*)/;
|
|
@@ -195,8 +249,8 @@ var dotnetProfile = {
|
|
|
195
249
|
};
|
|
196
250
|
|
|
197
251
|
// src/ast/profiles/go.ts
|
|
198
|
-
var
|
|
199
|
-
var
|
|
252
|
+
var FUNC2 = /^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/;
|
|
253
|
+
var TYPE2 = /^type\s+([A-Za-z_]\w*)\s+(.*)$/;
|
|
200
254
|
var VAR_CONST = /^(?:var|const)\s+([A-Za-z_]\w*)/;
|
|
201
255
|
var IMPORT_SINGLE = /^import\s+(?:[\w.]+\s+)?"([^"]+)"/;
|
|
202
256
|
var IMPORT_OPEN = /^import\s*\(/;
|
|
@@ -229,14 +283,14 @@ var goProfile = {
|
|
|
229
283
|
return;
|
|
230
284
|
}
|
|
231
285
|
if (!isTopLevel(line)) return;
|
|
232
|
-
const method =
|
|
286
|
+
const method = FUNC2.exec(line);
|
|
233
287
|
if (method) {
|
|
234
288
|
const name = method[1];
|
|
235
289
|
summary.functions.push(name);
|
|
236
290
|
if (isCapitalizedName(name)) summary.exports.push(name);
|
|
237
291
|
return;
|
|
238
292
|
}
|
|
239
|
-
const declared =
|
|
293
|
+
const declared = TYPE2.exec(line);
|
|
240
294
|
if (declared) {
|
|
241
295
|
const name = declared[1];
|
|
242
296
|
const body = declared[2].trim();
|
|
@@ -256,8 +310,54 @@ var goProfile = {
|
|
|
256
310
|
}
|
|
257
311
|
};
|
|
258
312
|
|
|
313
|
+
// src/ast/profiles/graphql.ts
|
|
314
|
+
var DEFINITION = /^(?:extend\s+)?(type|interface|input|enum|union|scalar|schema)\s+([A-Za-z_]\w*)/;
|
|
315
|
+
var OPERATION = /^(?:query|mutation|subscription)\s+([A-Za-z_]\w*)/;
|
|
316
|
+
var DIRECTIVE = /^directive\s+@([A-Za-z_]\w*)/;
|
|
317
|
+
var IMPORT2 = /^#import\s+"([^"]+)"/;
|
|
318
|
+
var graphqlProfile = {
|
|
319
|
+
id: "graphql",
|
|
320
|
+
extensions: [".graphql", ".gql"],
|
|
321
|
+
summarize(_path, content) {
|
|
322
|
+
const summary = emptySummary();
|
|
323
|
+
eachLine(content, (line) => {
|
|
324
|
+
const trimmed = line.trim();
|
|
325
|
+
if (!trimmed || trimmed.startsWith("#") && !IMPORT2.test(trimmed)) return;
|
|
326
|
+
const imported = IMPORT2.exec(trimmed);
|
|
327
|
+
if (imported) {
|
|
328
|
+
summary.imports.push(imported[1]);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const directive = DIRECTIVE.exec(trimmed);
|
|
332
|
+
if (directive) {
|
|
333
|
+
summary.functions.push(`@${directive[1]}`);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const definition = DEFINITION.exec(trimmed);
|
|
337
|
+
if (definition) {
|
|
338
|
+
const [, keyword, name] = definition;
|
|
339
|
+
if (keyword === "interface") summary.interfaces.push(name);
|
|
340
|
+
else if (keyword === "enum") summary.enums.push(name);
|
|
341
|
+
else if (keyword === "input" || keyword === "scalar" || keyword === "union") {
|
|
342
|
+
summary.types.push(name);
|
|
343
|
+
} else summary.classes.push(name);
|
|
344
|
+
summary.exports.push(name);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (!isTopLevel(line)) return;
|
|
348
|
+
const operation = OPERATION.exec(trimmed);
|
|
349
|
+
if (operation) {
|
|
350
|
+
const name = operation[1];
|
|
351
|
+
summary.functions.push(name);
|
|
352
|
+
summary.routeHandlers.push(name);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
259
359
|
// src/ast/profiles/jvm.ts
|
|
260
|
-
var
|
|
360
|
+
var IMPORT3 = /^import\s+(?:static\s+)?([\w.]+?)(?:\.\*)?;?\s*$/;
|
|
261
361
|
var TYPE_DECL3 = /^\s*(?:public\s+|private\s+|protected\s+|internal\s+)?(?:final\s+|abstract\s+|open\s+|sealed\s+|data\s+|static\s+|partial\s+)*(class|interface|enum|record|object|trait)\s+(?:class\s+)?([A-Za-z_]\w*)/;
|
|
262
362
|
var KOTLIN_FUN = /^\s*(?:public\s+|private\s+|protected\s+|internal\s+|override\s+|suspend\s+|inline\s+|open\s+|operator\s+)*fun\s+(?:<[^>]*>\s*)?(?:[\w.]+\.)?([A-Za-z_]\w*)/;
|
|
263
363
|
var SCALA_DEF = /^\s*def\s+([A-Za-z_]\w*)/;
|
|
@@ -265,9 +365,9 @@ var JAVA_METHOD = /^\s+(?:public|private|protected)\s+(?:static\s+)?(?:final\s+)
|
|
|
265
365
|
var jvmProfile = {
|
|
266
366
|
id: "jvm",
|
|
267
367
|
extensions: [".java", ".kt", ".kts", ".scala"],
|
|
268
|
-
summarize(
|
|
368
|
+
summarize(path17, content) {
|
|
269
369
|
const summary = emptySummary();
|
|
270
|
-
const lower =
|
|
370
|
+
const lower = path17.toLowerCase();
|
|
271
371
|
const isJava = lower.endsWith(".java");
|
|
272
372
|
const isKotlin = lower.endsWith(".kt") || lower.endsWith(".kts");
|
|
273
373
|
eachLine(content, (line) => {
|
|
@@ -275,7 +375,7 @@ var jvmProfile = {
|
|
|
275
375
|
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) {
|
|
276
376
|
return;
|
|
277
377
|
}
|
|
278
|
-
const imported =
|
|
378
|
+
const imported = IMPORT3.exec(trimmed);
|
|
279
379
|
if (imported) {
|
|
280
380
|
summary.imports.push(imported[1]);
|
|
281
381
|
return;
|
|
@@ -356,8 +456,50 @@ var phpProfile = {
|
|
|
356
456
|
}
|
|
357
457
|
};
|
|
358
458
|
|
|
459
|
+
// src/ast/profiles/protobuf.ts
|
|
460
|
+
var DECLARATION = /^(?:message|enum|service|extend)\s+([A-Za-z_]\w*)/;
|
|
461
|
+
var RPC = /^\s*rpc\s+([A-Za-z_]\w*)/;
|
|
462
|
+
var IMPORT4 = /^\s*import\s+(?:public\s+|weak\s+)?"([^"]+)"/;
|
|
463
|
+
var PACKAGE = /^\s*package\s+([\w.]+)\s*;/;
|
|
464
|
+
var protobufProfile = {
|
|
465
|
+
id: "protobuf",
|
|
466
|
+
extensions: [".proto"],
|
|
467
|
+
summarize(_path, content) {
|
|
468
|
+
const summary = emptySummary();
|
|
469
|
+
eachLine(content, (line) => {
|
|
470
|
+
const trimmed = line.trim();
|
|
471
|
+
if (!trimmed || trimmed.startsWith("//")) return;
|
|
472
|
+
const imported = IMPORT4.exec(line);
|
|
473
|
+
if (imported) {
|
|
474
|
+
summary.imports.push(imported[1]);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const pkg = PACKAGE.exec(line);
|
|
478
|
+
if (pkg) {
|
|
479
|
+
summary.directives.push(`package ${pkg[1]}`);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const rpc = RPC.exec(line);
|
|
483
|
+
if (rpc) {
|
|
484
|
+
const name2 = rpc[1];
|
|
485
|
+
summary.functions.push(name2);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const declaration = DECLARATION.exec(trimmed);
|
|
489
|
+
if (!declaration) return;
|
|
490
|
+
const name = declaration[1];
|
|
491
|
+
const keyword = /^\w+/.exec(trimmed)?.[0] ?? "";
|
|
492
|
+
if (keyword === "service") summary.interfaces.push(name);
|
|
493
|
+
else if (keyword === "enum") summary.enums.push(name);
|
|
494
|
+
else summary.classes.push(name);
|
|
495
|
+
summary.exports.push(name);
|
|
496
|
+
});
|
|
497
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
|
|
359
501
|
// src/ast/profiles/python.ts
|
|
360
|
-
var
|
|
502
|
+
var IMPORT5 = /^import\s+(.+)$/;
|
|
361
503
|
var FROM_IMPORT = /^from\s+([\w.]+)\s+import\s+(.+)$/;
|
|
362
504
|
var CLASS = /^class\s+([A-Za-z_]\w*)/;
|
|
363
505
|
var DEF = /^(?:async\s+)?def\s+([A-Za-z_]\w*)/;
|
|
@@ -383,7 +525,7 @@ var pythonProfile = {
|
|
|
383
525
|
routePending = false;
|
|
384
526
|
return;
|
|
385
527
|
}
|
|
386
|
-
const imported =
|
|
528
|
+
const imported = IMPORT5.exec(trimmed);
|
|
387
529
|
if (imported) {
|
|
388
530
|
for (const part of imported[1].split(",")) {
|
|
389
531
|
const name = part.trim().split(/\s+as\s+/)[0].trim();
|
|
@@ -467,7 +609,7 @@ var FN = /^(pub(?:\([^)]*\))?\s+)?(?:const\s+)?(?:async\s+)?(?:unsafe\s+)?fn\s+(
|
|
|
467
609
|
var STRUCT = /^(pub(?:\([^)]*\))?\s+)?struct\s+([A-Za-z_]\w*)/;
|
|
468
610
|
var TRAIT = /^(pub(?:\([^)]*\))?\s+)?(?:unsafe\s+)?trait\s+([A-Za-z_]\w*)/;
|
|
469
611
|
var ENUM = /^(pub(?:\([^)]*\))?\s+)?enum\s+([A-Za-z_]\w*)/;
|
|
470
|
-
var
|
|
612
|
+
var TYPE3 = /^(pub(?:\([^)]*\))?\s+)?type\s+([A-Za-z_]\w*)/;
|
|
471
613
|
var MOD = /^(pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_]\w*)/;
|
|
472
614
|
var IMPL = /^impl(?:<[^>]*>)?\s+(?:[\w:<>]+\s+for\s+)?([A-Za-z_]\w*)/;
|
|
473
615
|
var rustProfile = {
|
|
@@ -509,7 +651,7 @@ var rustProfile = {
|
|
|
509
651
|
if (isPublic) summary.exports.push(enumeration[2]);
|
|
510
652
|
return;
|
|
511
653
|
}
|
|
512
|
-
const alias =
|
|
654
|
+
const alias = TYPE3.exec(line);
|
|
513
655
|
if (alias) {
|
|
514
656
|
summary.types.push(alias[2]);
|
|
515
657
|
if (isPublic) summary.exports.push(alias[2]);
|
|
@@ -536,6 +678,16 @@ var ROUTE_HANDLER_NAMES = /* @__PURE__ */ new Set([
|
|
|
536
678
|
"HEAD",
|
|
537
679
|
"OPTIONS"
|
|
538
680
|
]);
|
|
681
|
+
function bindingNames(name, into) {
|
|
682
|
+
if (ts__default.default.isIdentifier(name)) {
|
|
683
|
+
into.push(name.text);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
for (const element of name.elements) {
|
|
687
|
+
if (ts__default.default.isOmittedExpression(element)) continue;
|
|
688
|
+
bindingNames(element.name, into);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
539
691
|
function summarizeTypeScriptSource(relativePath, content) {
|
|
540
692
|
const summary = {
|
|
541
693
|
directives: [],
|
|
@@ -636,12 +788,15 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
636
788
|
}
|
|
637
789
|
if (node.exportClause && ts__default.default.isNamedExports(node.exportClause)) {
|
|
638
790
|
for (const element of node.exportClause.elements) {
|
|
639
|
-
|
|
791
|
+
const exported = element.name.text;
|
|
792
|
+
summary.exports.push(exported);
|
|
793
|
+
if (ROUTE_HANDLER_NAMES.has(exported)) summary.routeHandlers.push(exported);
|
|
640
794
|
}
|
|
641
795
|
}
|
|
642
796
|
}
|
|
643
797
|
if (ts__default.default.isExportAssignment(node)) {
|
|
644
798
|
summary.exports.push("default");
|
|
799
|
+
if (rendersJsx(node.expression)) summary.reactComponents.push("default");
|
|
645
800
|
}
|
|
646
801
|
if (ts__default.default.isFunctionDeclaration(node)) {
|
|
647
802
|
const exported = isExported(node);
|
|
@@ -679,16 +834,18 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
679
834
|
if (ts__default.default.isVariableStatement(node)) {
|
|
680
835
|
const exported = isExported(node);
|
|
681
836
|
for (const declaration of node.declarationList.declarations) {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
837
|
+
const bound = [];
|
|
838
|
+
bindingNames(declaration.name, bound);
|
|
839
|
+
for (const name of bound) {
|
|
840
|
+
summary.variables.push(name);
|
|
841
|
+
if (exported) {
|
|
842
|
+
summary.exports.push(name);
|
|
843
|
+
if (ROUTE_HANDLER_NAMES.has(name)) summary.routeHandlers.push(name);
|
|
844
|
+
}
|
|
845
|
+
if (/^use[A-Z0-9]/.test(name)) summary.hooks.push(name);
|
|
846
|
+
if (/^[A-Z]/.test(name) && declaration.initializer !== void 0 && rendersJsx(declaration.initializer)) {
|
|
847
|
+
summary.reactComponents.push(name);
|
|
848
|
+
}
|
|
692
849
|
}
|
|
693
850
|
}
|
|
694
851
|
}
|
|
@@ -720,16 +877,16 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
720
877
|
// src/ast/profiles/sfc.ts
|
|
721
878
|
var SCRIPT_BLOCK = /<script[^>]*>([\s\S]*?)<\/script>/gi;
|
|
722
879
|
var ASTRO_FRONTMATTER = /^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?=\r?\n|$)/;
|
|
723
|
-
function componentName(
|
|
724
|
-
const base =
|
|
880
|
+
function componentName(path17) {
|
|
881
|
+
const base = path17.split("/").pop() ?? path17;
|
|
725
882
|
return base.replace(/\.[^.]+$/, "");
|
|
726
883
|
}
|
|
727
884
|
var sfcProfile = {
|
|
728
885
|
id: "sfc",
|
|
729
886
|
extensions: [".vue", ".svelte", ".astro"],
|
|
730
|
-
summarize(
|
|
887
|
+
summarize(path17, content) {
|
|
731
888
|
let script = "";
|
|
732
|
-
if (
|
|
889
|
+
if (path17.toLowerCase().endsWith(".astro")) {
|
|
733
890
|
const frontmatter = ASTRO_FRONTMATTER.exec(content);
|
|
734
891
|
if (frontmatter) script += `${frontmatter[1]}
|
|
735
892
|
`;
|
|
@@ -738,7 +895,7 @@ var sfcProfile = {
|
|
|
738
895
|
script += `${match[1]}
|
|
739
896
|
`;
|
|
740
897
|
}
|
|
741
|
-
const name = componentName(
|
|
898
|
+
const name = componentName(path17);
|
|
742
899
|
if (!script.trim()) {
|
|
743
900
|
const summary2 = emptySummary();
|
|
744
901
|
if (name) {
|
|
@@ -750,7 +907,158 @@ var sfcProfile = {
|
|
|
750
907
|
const summary = summarizeTypeScriptSource(`${name}.ts`, script);
|
|
751
908
|
summary.reactComponents.push(name);
|
|
752
909
|
summary.exports.push(name);
|
|
753
|
-
return { summary, quality: "ast" };
|
|
910
|
+
return { summary: finalizeSummary(summary), quality: "ast" };
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
// src/ast/profiles/sql.ts
|
|
915
|
+
var CREATE = /^\s*CREATE\s+(?:OR\s+REPLACE\s+)?(?:TEMP(?:ORARY)?\s+)?(TABLE|VIEW|MATERIALIZED\s+VIEW|FUNCTION|PROCEDURE|INDEX|TYPE|SCHEMA|TRIGGER|SEQUENCE)\s+(?:IF\s+NOT\s+EXISTS\s+)?([A-Za-z_"][\w."]*)/i;
|
|
916
|
+
var ALTER = /^\s*ALTER\s+(TABLE|VIEW)\s+(?:IF\s+EXISTS\s+)?([A-Za-z_"][\w."]*)/i;
|
|
917
|
+
function bareName(identifier) {
|
|
918
|
+
const last = identifier.split(".").pop() ?? identifier;
|
|
919
|
+
return last.replace(/["`[\]]/g, "");
|
|
920
|
+
}
|
|
921
|
+
var sqlProfile = {
|
|
922
|
+
id: "sql",
|
|
923
|
+
extensions: [".sql"],
|
|
924
|
+
summarize(_path, content) {
|
|
925
|
+
const summary = emptySummary();
|
|
926
|
+
const seen = /* @__PURE__ */ new Set();
|
|
927
|
+
const declare = (kind, identifier) => {
|
|
928
|
+
const name = bareName(identifier);
|
|
929
|
+
if (!name || seen.has(`${kind}:${name}`)) return;
|
|
930
|
+
seen.add(`${kind}:${name}`);
|
|
931
|
+
const normalized = kind.toUpperCase().replace(/\s+/g, " ");
|
|
932
|
+
if (normalized === "TABLE" || normalized.endsWith("VIEW")) summary.classes.push(name);
|
|
933
|
+
else if (normalized === "TYPE" || normalized === "SCHEMA" || normalized === "SEQUENCE") {
|
|
934
|
+
summary.types.push(name);
|
|
935
|
+
} else summary.functions.push(name);
|
|
936
|
+
summary.exports.push(name);
|
|
937
|
+
};
|
|
938
|
+
eachLine(content, (line) => {
|
|
939
|
+
const trimmed = line.trim();
|
|
940
|
+
if (!trimmed || trimmed.startsWith("--")) return;
|
|
941
|
+
const created = CREATE.exec(trimmed);
|
|
942
|
+
if (created) {
|
|
943
|
+
declare(String(created[1]), String(created[2]));
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
const altered = ALTER.exec(trimmed);
|
|
947
|
+
if (altered) declare(String(altered[1]), String(altered[2]));
|
|
948
|
+
});
|
|
949
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
|
|
953
|
+
// src/ast/profiles/swift.ts
|
|
954
|
+
var MODIFIERS = "(?:@\\w+(?:\\([^)]*\\))?\\s+|(?:public|open|internal|fileprivate|private|final|static|mutating|nonmutating|override|required|convenience|lazy|weak|unowned)\\s+)*";
|
|
955
|
+
var TYPE4 = new RegExp(`^${MODIFIERS}(class|struct|enum|protocol|actor|extension)\\s+([A-Za-z_]\\w*)`);
|
|
956
|
+
var FUNC3 = new RegExp(`^${MODIFIERS}func\\s+([A-Za-z_]\\w*)`);
|
|
957
|
+
var PROPERTY = new RegExp(`^${MODIFIERS}(?:let|var)\\s+([A-Za-z_]\\w*)`);
|
|
958
|
+
var IMPORT6 = /^import\s+([A-Za-z_][\w.]*)/;
|
|
959
|
+
var swiftProfile = {
|
|
960
|
+
id: "swift",
|
|
961
|
+
extensions: [".swift"],
|
|
962
|
+
summarize(_path, content) {
|
|
963
|
+
const summary = emptySummary();
|
|
964
|
+
eachLine(content, (line) => {
|
|
965
|
+
const trimmed = line.trim();
|
|
966
|
+
if (!trimmed || trimmed.startsWith("//")) return;
|
|
967
|
+
const imported = IMPORT6.exec(trimmed);
|
|
968
|
+
if (imported) {
|
|
969
|
+
summary.imports.push(imported[1]);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
if (!isTopLevel(line)) return;
|
|
973
|
+
const exported = /\b(?:public|open)\b/.test(trimmed);
|
|
974
|
+
const type = TYPE4.exec(trimmed);
|
|
975
|
+
if (type) {
|
|
976
|
+
const [, keyword, name] = type;
|
|
977
|
+
if (keyword === "protocol") summary.interfaces.push(name);
|
|
978
|
+
else if (keyword === "enum") summary.enums.push(name);
|
|
979
|
+
else if (keyword === "extension") summary.types.push(name);
|
|
980
|
+
else summary.classes.push(name);
|
|
981
|
+
if (exported) summary.exports.push(name);
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
const func = FUNC3.exec(trimmed);
|
|
985
|
+
if (func) {
|
|
986
|
+
const name = func[1];
|
|
987
|
+
summary.functions.push(name);
|
|
988
|
+
if (exported) summary.exports.push(name);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const property = PROPERTY.exec(trimmed);
|
|
992
|
+
if (property) {
|
|
993
|
+
const name = property[1];
|
|
994
|
+
summary.variables.push(name);
|
|
995
|
+
if (exported && isPublicName(name)) summary.exports.push(name);
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
// src/ast/profiles/terraform.ts
|
|
1003
|
+
var BLOCK = /^\s*(resource|data|module|provider|variable|output|locals|terraform)\b\s*("([^"]+)")?\s*("([^"]+)")?/;
|
|
1004
|
+
var TERRAFORM_VERSION = /^\s*required_version\s*=\s*"([^"]+)"/;
|
|
1005
|
+
var terraformProfile = {
|
|
1006
|
+
id: "terraform",
|
|
1007
|
+
extensions: [".tf", ".tfvars"],
|
|
1008
|
+
summarize(_path, content) {
|
|
1009
|
+
const summary = emptySummary();
|
|
1010
|
+
eachLine(content, (line) => {
|
|
1011
|
+
const trimmed = line.trim();
|
|
1012
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//")) return;
|
|
1013
|
+
const version = TERRAFORM_VERSION.exec(line);
|
|
1014
|
+
if (version) {
|
|
1015
|
+
summary.directives.push(`required_version ${version[1]}`);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
if (/^[A-Za-z_][\w-]*\s*=/.test(trimmed)) {
|
|
1019
|
+
summary.variables.push(/^([A-Za-z_][\w-]*)/.exec(trimmed)?.[1] ?? trimmed);
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const block = BLOCK.exec(line);
|
|
1023
|
+
if (!block) return;
|
|
1024
|
+
const keyword = block[1];
|
|
1025
|
+
const first = block[3];
|
|
1026
|
+
const second = block[5];
|
|
1027
|
+
switch (keyword) {
|
|
1028
|
+
case "resource":
|
|
1029
|
+
case "data": {
|
|
1030
|
+
if (!first || !second) return;
|
|
1031
|
+
const name = `${first}.${second}`;
|
|
1032
|
+
summary.classes.push(name);
|
|
1033
|
+
summary.exports.push(name);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
case "module": {
|
|
1037
|
+
if (!first) return;
|
|
1038
|
+
summary.imports.push(first);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
case "variable": {
|
|
1042
|
+
if (!first) return;
|
|
1043
|
+
summary.variables.push(first);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
case "output": {
|
|
1047
|
+
if (!first) return;
|
|
1048
|
+
summary.exports.push(first);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
case "provider": {
|
|
1052
|
+
if (!first) return;
|
|
1053
|
+
summary.imports.push(`provider:${first}`);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
default: {
|
|
1057
|
+
summary.directives.push(keyword);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
754
1062
|
}
|
|
755
1063
|
};
|
|
756
1064
|
|
|
@@ -764,7 +1072,13 @@ var LANGUAGE_PROFILES = [
|
|
|
764
1072
|
phpProfile,
|
|
765
1073
|
rubyProfile,
|
|
766
1074
|
cfamilyProfile,
|
|
767
|
-
sfcProfile
|
|
1075
|
+
sfcProfile,
|
|
1076
|
+
swiftProfile,
|
|
1077
|
+
dartProfile,
|
|
1078
|
+
sqlProfile,
|
|
1079
|
+
graphqlProfile,
|
|
1080
|
+
protobufProfile,
|
|
1081
|
+
terraformProfile
|
|
768
1082
|
];
|
|
769
1083
|
var BY_EXTENSION = /* @__PURE__ */ new Map();
|
|
770
1084
|
for (const profile of LANGUAGE_PROFILES) {
|
|
@@ -772,15 +1086,15 @@ for (const profile of LANGUAGE_PROFILES) {
|
|
|
772
1086
|
BY_EXTENSION.set(extension.toLowerCase(), profile);
|
|
773
1087
|
}
|
|
774
1088
|
}
|
|
775
|
-
function profileForPath(
|
|
776
|
-
const lower =
|
|
1089
|
+
function profileForPath(path17) {
|
|
1090
|
+
const lower = path17.toLowerCase();
|
|
777
1091
|
const dot = lower.lastIndexOf(".");
|
|
778
1092
|
if (dot === -1) return null;
|
|
779
1093
|
return BY_EXTENSION.get(lower.slice(dot)) ?? null;
|
|
780
1094
|
}
|
|
781
|
-
function summarizeWithProfile(
|
|
782
|
-
const profile = profileForPath(
|
|
783
|
-
return profile ? profile.summarize(
|
|
1095
|
+
function summarizeWithProfile(path17, content) {
|
|
1096
|
+
const profile = profileForPath(path17);
|
|
1097
|
+
return profile ? profile.summarize(path17, content) : null;
|
|
784
1098
|
}
|
|
785
1099
|
|
|
786
1100
|
// src/ast/structured.ts
|
|
@@ -880,9 +1194,17 @@ function summarizeMarkdown(content) {
|
|
|
880
1194
|
const frontmatterKeys = [];
|
|
881
1195
|
const lines = content.replace(/^\uFEFF/, "").split("\n");
|
|
882
1196
|
let inFrontmatter = false;
|
|
1197
|
+
let fence = null;
|
|
1198
|
+
let closesFrontmatter = false;
|
|
1199
|
+
for (let index = 1; index < lines.length; index++) {
|
|
1200
|
+
if (lines[index].trim() === "---") {
|
|
1201
|
+
closesFrontmatter = true;
|
|
1202
|
+
break;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
883
1205
|
for (let index = 0; index < lines.length; index++) {
|
|
884
1206
|
const line = lines[index];
|
|
885
|
-
if (index === 0 && line.trim() === "---") {
|
|
1207
|
+
if (index === 0 && line.trim() === "---" && closesFrontmatter) {
|
|
886
1208
|
inFrontmatter = true;
|
|
887
1209
|
continue;
|
|
888
1210
|
}
|
|
@@ -895,7 +1217,20 @@ function summarizeMarkdown(content) {
|
|
|
895
1217
|
if (match) frontmatterKeys.push(match[1]);
|
|
896
1218
|
continue;
|
|
897
1219
|
}
|
|
898
|
-
const
|
|
1220
|
+
const trimmed = line.trim();
|
|
1221
|
+
const fenceMatch = /^(`{3,}|~{3,})(.*)$/.exec(trimmed);
|
|
1222
|
+
if (fenceMatch) {
|
|
1223
|
+
const run = fenceMatch[1];
|
|
1224
|
+
const rest = fenceMatch[2].trim();
|
|
1225
|
+
if (fence === null) {
|
|
1226
|
+
fence = { marker: run[0], length: run.length };
|
|
1227
|
+
} else if (run[0] === fence.marker && run.length >= fence.length && rest === "") {
|
|
1228
|
+
fence = null;
|
|
1229
|
+
}
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
if (fence !== null) continue;
|
|
1233
|
+
const headingMatch = /^(#{1,6})\s+(.+)$/.exec(trimmed);
|
|
899
1234
|
if (headingMatch) {
|
|
900
1235
|
headings.push(`${headingMatch[1]} ${headingMatch[2].trim()}`);
|
|
901
1236
|
}
|
|
@@ -905,6 +1240,52 @@ function summarizeMarkdown(content) {
|
|
|
905
1240
|
frontmatterKeys: Array.from(new Set(frontmatterKeys)).sort()
|
|
906
1241
|
};
|
|
907
1242
|
}
|
|
1243
|
+
function hasCollapsibleBody(node) {
|
|
1244
|
+
if (!("body" in node)) return false;
|
|
1245
|
+
const body = node.body;
|
|
1246
|
+
return body !== void 0 && ts__default.default.isBlock(body);
|
|
1247
|
+
}
|
|
1248
|
+
function isTsxFileName(fileName) {
|
|
1249
|
+
return fileName.toLowerCase().endsWith(".tsx");
|
|
1250
|
+
}
|
|
1251
|
+
function skeletonizeTypeScript(source, fileName = "file.ts") {
|
|
1252
|
+
const scriptKind = isTsxFileName(fileName) ? ts__default.default.ScriptKind.TSX : ts__default.default.ScriptKind.TS;
|
|
1253
|
+
const sourceFile = ts__default.default.createSourceFile(
|
|
1254
|
+
fileName,
|
|
1255
|
+
source,
|
|
1256
|
+
ts__default.default.ScriptTarget.Latest,
|
|
1257
|
+
true,
|
|
1258
|
+
scriptKind
|
|
1259
|
+
);
|
|
1260
|
+
const spans = [];
|
|
1261
|
+
const visit = (node) => {
|
|
1262
|
+
if ((ts__default.default.isFunctionDeclaration(node) || ts__default.default.isMethodDeclaration(node) || ts__default.default.isConstructorDeclaration(node) || ts__default.default.isGetAccessorDeclaration(node) || ts__default.default.isSetAccessorDeclaration(node) || ts__default.default.isFunctionExpression(node) || ts__default.default.isArrowFunction(node)) && hasCollapsibleBody(node)) {
|
|
1263
|
+
const body = node.body;
|
|
1264
|
+
const start = body.getStart(sourceFile);
|
|
1265
|
+
const end = body.getEnd();
|
|
1266
|
+
if (end > start) spans.push({ start, end });
|
|
1267
|
+
}
|
|
1268
|
+
ts__default.default.forEachChild(node, visit);
|
|
1269
|
+
};
|
|
1270
|
+
visit(sourceFile);
|
|
1271
|
+
if (spans.length === 0) return { text: source, collapsed: 0 };
|
|
1272
|
+
const outermost = [];
|
|
1273
|
+
for (const span of spans.sort((a, b) => a.start - b.start || b.end - a.end)) {
|
|
1274
|
+
const last = outermost[outermost.length - 1];
|
|
1275
|
+
if (last && span.start < last.end) continue;
|
|
1276
|
+
outermost.push(span);
|
|
1277
|
+
}
|
|
1278
|
+
let text2 = source;
|
|
1279
|
+
for (let index = outermost.length - 1; index >= 0; index -= 1) {
|
|
1280
|
+
const span = outermost[index];
|
|
1281
|
+
text2 = `${text2.slice(0, span.start)}{ /* ... */ }${text2.slice(span.end)}`;
|
|
1282
|
+
}
|
|
1283
|
+
return { text: text2, collapsed: outermost.length };
|
|
1284
|
+
}
|
|
1285
|
+
function isSkeletonizable(filePath) {
|
|
1286
|
+
const lower = filePath.toLowerCase();
|
|
1287
|
+
return lower.endsWith(".ts") || lower.endsWith(".tsx") || lower.endsWith(".mts") || lower.endsWith(".cts");
|
|
1288
|
+
}
|
|
908
1289
|
var MANIFEST_FILES = [
|
|
909
1290
|
"package.json",
|
|
910
1291
|
"pyproject.toml",
|
|
@@ -951,15 +1332,15 @@ var SKIPPED_WALK_DIRS = /* @__PURE__ */ new Set([
|
|
|
951
1332
|
]);
|
|
952
1333
|
function readSmallFile(fullPath) {
|
|
953
1334
|
try {
|
|
954
|
-
const stats =
|
|
1335
|
+
const stats = fs14__namespace.statSync(fullPath);
|
|
955
1336
|
if (!stats.isFile() || stats.size > MANIFEST_MAX_BYTES) return null;
|
|
956
|
-
return
|
|
1337
|
+
return fs14__namespace.readFileSync(fullPath, "utf8");
|
|
957
1338
|
} catch {
|
|
958
1339
|
return null;
|
|
959
1340
|
}
|
|
960
1341
|
}
|
|
961
1342
|
function readManifest(cwd, name) {
|
|
962
|
-
return readSmallFile(
|
|
1343
|
+
return readSmallFile(path16__namespace.join(cwd, name));
|
|
963
1344
|
}
|
|
964
1345
|
function dependenciesFromPackageJson(raw) {
|
|
965
1346
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -1019,7 +1400,7 @@ function pnpmWorkspacePatterns(raw) {
|
|
|
1019
1400
|
function childDirectories(cwd, parent) {
|
|
1020
1401
|
let entries;
|
|
1021
1402
|
try {
|
|
1022
|
-
entries =
|
|
1403
|
+
entries = fs14__namespace.readdirSync(path16__namespace.join(cwd, parent), { withFileTypes: true });
|
|
1023
1404
|
} catch {
|
|
1024
1405
|
return [];
|
|
1025
1406
|
}
|
|
@@ -1054,13 +1435,13 @@ function workspacePackageDirs(cwd, patterns) {
|
|
|
1054
1435
|
function scanWorkspacePackages(cwd, patterns) {
|
|
1055
1436
|
const files = /* @__PURE__ */ new Set();
|
|
1056
1437
|
const dependencies = /* @__PURE__ */ new Set();
|
|
1057
|
-
for (const
|
|
1058
|
-
const full =
|
|
1438
|
+
for (const relative8 of workspacePackageDirs(cwd, patterns)) {
|
|
1439
|
+
const full = path16__namespace.join(cwd, relative8);
|
|
1059
1440
|
try {
|
|
1060
|
-
for (const entry of
|
|
1441
|
+
for (const entry of fs14__namespace.readdirSync(full, { withFileTypes: true })) files.add(entry.name);
|
|
1061
1442
|
} catch {
|
|
1062
1443
|
}
|
|
1063
|
-
const raw = readSmallFile(
|
|
1444
|
+
const raw = readSmallFile(path16__namespace.join(full, "package.json"));
|
|
1064
1445
|
if (raw !== null) {
|
|
1065
1446
|
for (const name of dependenciesFromPackageJson(raw)) dependencies.add(name);
|
|
1066
1447
|
}
|
|
@@ -1077,7 +1458,7 @@ function countMarkdownFiles(cwd) {
|
|
|
1077
1458
|
directoriesSeen += 1;
|
|
1078
1459
|
let entries;
|
|
1079
1460
|
try {
|
|
1080
|
-
entries =
|
|
1461
|
+
entries = fs14__namespace.readdirSync(current.dir, { withFileTypes: true });
|
|
1081
1462
|
} catch {
|
|
1082
1463
|
continue;
|
|
1083
1464
|
}
|
|
@@ -1088,7 +1469,7 @@ function countMarkdownFiles(cwd) {
|
|
|
1088
1469
|
}
|
|
1089
1470
|
if (!entry.isDirectory() || current.depth >= 2) continue;
|
|
1090
1471
|
if (entry.name.startsWith(".") || SKIPPED_WALK_DIRS.has(entry.name)) continue;
|
|
1091
|
-
queue.push({ dir:
|
|
1472
|
+
queue.push({ dir: path16__namespace.join(current.dir, entry.name), depth: current.depth + 1 });
|
|
1092
1473
|
}
|
|
1093
1474
|
}
|
|
1094
1475
|
return count;
|
|
@@ -1097,7 +1478,7 @@ function buildPresetContext(cwd) {
|
|
|
1097
1478
|
const files = /* @__PURE__ */ new Set();
|
|
1098
1479
|
const dirs = /* @__PURE__ */ new Set();
|
|
1099
1480
|
try {
|
|
1100
|
-
for (const entry of
|
|
1481
|
+
for (const entry of fs14__namespace.readdirSync(cwd, { withFileTypes: true })) {
|
|
1101
1482
|
if (entry.isDirectory()) dirs.add(entry.name);
|
|
1102
1483
|
else if (entry.isFile()) files.add(entry.name);
|
|
1103
1484
|
}
|
|
@@ -1114,7 +1495,7 @@ function buildPresetContext(cwd) {
|
|
|
1114
1495
|
const isWorkspace = files.has("pnpm-workspace.yaml") || files.has("nx.json") || files.has("turbo.json") || files.has("lerna.json") || packageJson !== void 0 && packageJson.includes('"workspaces"') || declaredWorkspaces.length > 0;
|
|
1115
1496
|
let workspace = { files: /* @__PURE__ */ new Set(), dependencies: /* @__PURE__ */ new Set() };
|
|
1116
1497
|
if (isWorkspace) {
|
|
1117
|
-
const pnpmRaw = files.has("pnpm-workspace.yaml") ? readSmallFile(
|
|
1498
|
+
const pnpmRaw = files.has("pnpm-workspace.yaml") ? readSmallFile(path16__namespace.join(cwd, "pnpm-workspace.yaml")) : null;
|
|
1118
1499
|
const declared = pnpmRaw === null ? [] : pnpmWorkspacePatterns(pnpmRaw);
|
|
1119
1500
|
const patterns = declared.length > 0 ? declared : declaredWorkspaces.length > 0 ? declaredWorkspaces : DEFAULT_WORKSPACE_PATTERNS;
|
|
1120
1501
|
workspace = scanWorkspacePackages(cwd, patterns);
|
|
@@ -1959,10 +2340,15 @@ function detectPreset(cwd, forced) {
|
|
|
1959
2340
|
// src/security/patterns.ts
|
|
1960
2341
|
var STRONG_CREDENTIAL_NAME = "(?:password|passwd|passphrase|pwd|api[_-]?key|api[_-]?secret|secret[_-]?access[_-]?key|secret[_-]?key|client[_-]?secret[_-]?key|client[_-]?secret|private[_-]?key|aws[_-]?secret[_-]?access[_-]?key|access[_-]?token|refresh[_-]?token|auth[_-]?token|session[_-]?token|secret|token)";
|
|
1961
2342
|
var WEAK_CREDENTIAL_NAME = "(?:pass|id[_-]?token|credentials?)";
|
|
1962
|
-
var NOT_A_LITERAL_VALUE = "(?!(?:string|number|boolean|bigint|symbol|unknown|any|never|void|null|undefined|true|false|process\\.env|Buffer|Record|object)\\b)";
|
|
1963
|
-
var
|
|
1964
|
-
var
|
|
1965
|
-
var
|
|
2343
|
+
var NOT_A_LITERAL_VALUE = "(?!(?:string|number|boolean|bigint|symbol|unknown|any|never|void|null|undefined|true|false|await|async|new|this|typeof|delete|yield|function|return|process\\.env|Buffer|Record|object)\\b)";
|
|
2344
|
+
var UNQUOTED_MIN_LENGTH = 2;
|
|
2345
|
+
var PROSE_CONTINUATION = String.raw`(?!\s+[a-z])`;
|
|
2346
|
+
var UNQUOTED_LITERAL = String.raw`(?![0-9]+(?![A-Za-z0-9_+=/%:.~-]))([A-Za-z0-9_][A-Za-z0-9_+=/%:~-]{${UNQUOTED_MIN_LENGTH - 1},511})(?![A-Za-z0-9_+=/%:.~-]|[({[])` + PROSE_CONTINUATION;
|
|
2347
|
+
var UNQUOTED_AMBIGUOUS_LITERAL = String.raw`(?![0-9]+(?![A-Za-z0-9_+=/%:.~-]))([A-Za-z0-9_][A-Za-z0-9_+=/%:~-]{7,511})(?![A-Za-z0-9_+=/%:.~-]|[({[])` + PROSE_CONTINUATION;
|
|
2348
|
+
var ASSIGN = String.raw`(?<![=!<>])[:=](?!=)`;
|
|
2349
|
+
var NOT_AFTER_QUOTE = String.raw`(?<!["'])`;
|
|
2350
|
+
var NOT_A_NAMESPACED_IDENTIFIER = String.raw`(?![a-z][a-z0-9]*(?:\.[a-z][a-z0-9-]*)+/)`;
|
|
2351
|
+
var QUOTED_VALUE = String.raw`((?:[^"'\\\n]|\\\\["'](?=[A-Za-z0-9_])|\\.){1,512})`;
|
|
1966
2352
|
var UNQUOTED_STRONG_NAME = `(?<!id[-_])${STRONG_CREDENTIAL_NAME}`;
|
|
1967
2353
|
var DEFAULT_SECRET_PATTERNS = [
|
|
1968
2354
|
{
|
|
@@ -2005,7 +2391,7 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2005
2391
|
// literal words matched. The value is masked alone, so the key survives.
|
|
2006
2392
|
rule: "aws-secret-access-key",
|
|
2007
2393
|
pattern: new RegExp(
|
|
2008
|
-
|
|
2394
|
+
`(?:["']${STRONG_CREDENTIAL_NAME}["']|${NOT_AFTER_QUOTE}${STRONG_CREDENTIAL_NAME})\\s{0,8}${ASSIGN}\\s{0,8}["']?([A-Za-z0-9/+=]{40})["']?`,
|
|
2009
2395
|
"i"
|
|
2010
2396
|
),
|
|
2011
2397
|
valueGroup: 1
|
|
@@ -2117,7 +2503,7 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2117
2503
|
// the key name.
|
|
2118
2504
|
rule: "credential-assignment",
|
|
2119
2505
|
pattern: new RegExp(
|
|
2120
|
-
`(?:["']${STRONG_CREDENTIAL_NAME}["']|${STRONG_CREDENTIAL_NAME})\\s{0,8}
|
|
2506
|
+
`(?:["']${STRONG_CREDENTIAL_NAME}["']|${NOT_AFTER_QUOTE}${STRONG_CREDENTIAL_NAME})\\s{0,8}${ASSIGN}\\s{0,8}${NOT_A_NAMESPACED_IDENTIFIER}(["'\`])${QUOTED_VALUE}\\1`,
|
|
2121
2507
|
"i"
|
|
2122
2508
|
),
|
|
2123
2509
|
valueGroup: 2
|
|
@@ -2127,7 +2513,7 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2127
2513
|
// are accepted only against a quoted value long enough to be a credential.
|
|
2128
2514
|
rule: "credential-assignment-weak",
|
|
2129
2515
|
pattern: new RegExp(
|
|
2130
|
-
`(?:["']${WEAK_CREDENTIAL_NAME}["']|${WEAK_CREDENTIAL_NAME})\\s{0,8}
|
|
2516
|
+
`(?:["']${WEAK_CREDENTIAL_NAME}["']|${NOT_AFTER_QUOTE}${WEAK_CREDENTIAL_NAME})\\s{0,8}${ASSIGN}\\s{0,8}(["'\`])${QUOTED_VALUE}\\1`,
|
|
2131
2517
|
"i"
|
|
2132
2518
|
),
|
|
2133
2519
|
valueGroup: 2
|
|
@@ -2138,7 +2524,7 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2138
2524
|
// `POSTGRES_PASSWORD:` in the output.
|
|
2139
2525
|
rule: "credential-assignment-unquoted",
|
|
2140
2526
|
pattern: new RegExp(
|
|
2141
|
-
`${UNQUOTED_STRONG_NAME}\\s{0,8}
|
|
2527
|
+
`${NOT_AFTER_QUOTE}${UNQUOTED_STRONG_NAME}\\s{0,8}${ASSIGN}\\s{0,8}${NOT_A_LITERAL_VALUE}${UNQUOTED_LITERAL}`,
|
|
2142
2528
|
"i"
|
|
2143
2529
|
),
|
|
2144
2530
|
valueGroup: 1
|
|
@@ -2149,7 +2535,7 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2149
2535
|
// `pass: 3` / `credentials: none` readable.
|
|
2150
2536
|
rule: "credential-assignment-unquoted-weak",
|
|
2151
2537
|
pattern: new RegExp(
|
|
2152
|
-
`${WEAK_CREDENTIAL_NAME}\\s{0,8}
|
|
2538
|
+
`${NOT_AFTER_QUOTE}${WEAK_CREDENTIAL_NAME}\\s{0,8}${ASSIGN}\\s{0,8}${NOT_A_LITERAL_VALUE}${UNQUOTED_AMBIGUOUS_LITERAL}`,
|
|
2153
2539
|
"i"
|
|
2154
2540
|
),
|
|
2155
2541
|
valueGroup: 1
|
|
@@ -2170,7 +2556,7 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2170
2556
|
// above because it also matches a *quoted key* followed by an unquoted
|
|
2171
2557
|
// value, e.g. `"client_secret": abc123`.
|
|
2172
2558
|
rule: "generic-secret-assignment",
|
|
2173
|
-
pattern: /(?:api[_-]?key|secret[_-]?key|auth[_-]?token|client[_-]?secret|access[_-]?token|refresh[_-]?token|private[_-]?key|password|passwd|pwd)["']?\s*[:=]\s*["']([^"'\s]{8,256})["']/i,
|
|
2559
|
+
pattern: /(?<!["'])(?:api[_-]?key|secret[_-]?key|auth[_-]?token|client[_-]?secret|access[_-]?token|refresh[_-]?token|private[_-]?key|password|passwd|pwd)["']?\s*(?<![=!<>])[:=](?!=)\s*["']([^"'\s]{8,256})["']/i,
|
|
2174
2560
|
valueGroup: 1
|
|
2175
2561
|
},
|
|
2176
2562
|
{
|
|
@@ -2186,8 +2572,17 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2186
2572
|
// Anchoring the value to end-of-line (`[^\s#]{6,512}$`) meant ANY trailing
|
|
2187
2573
|
// comment or space disabled the rule, so `TOKEN=secret # rotate` and
|
|
2188
2574
|
// `TOKEN=secret ` were emitted in clear; a CRLF file disabled it wholesale.
|
|
2575
|
+
//
|
|
2576
|
+
// The name is now required to be uppercase. With `/i`, a reassignment inside a
|
|
2577
|
+
// function — ` key = fs.realpathSync(stagingRoot)` — matched `key`…`KEY` and
|
|
2578
|
+
// was emitted as `key = [REDACTED](stagingRoot)`, which corrupted this
|
|
2579
|
+
// repository's own `src/index.ts`. Shell and `.env` names are uppercase by
|
|
2580
|
+
// convention and a lowercase `key = …` at an indent is a variable, so the case
|
|
2581
|
+
// of the name is the discriminator that separates the two. The value must end
|
|
2582
|
+
// at end-of-line, whitespace or a comment: that is what stops a call expression
|
|
2583
|
+
// (`NAME=f(x)`) from being read as an assigned literal.
|
|
2189
2584
|
rule: "env-secret-assignment",
|
|
2190
|
-
pattern: /^\s{0,64}(?:export\s{1,8})?[A-
|
|
2585
|
+
pattern: /^\s{0,64}(?:export\s{1,8})?(?=[A-Z][A-Z0-9_]{0,191}\s{0,8}=)[A-Z0-9_]{0,96}(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PASS|PWD|CREDENTIAL)[A-Z0-9_]{0,96}\s{0,8}=(?!=)\s{0,8}(?!["'`$<{[\]\s])([^\s#;"'`()]{1,512})(?=$|[\s#;])/,
|
|
2191
2586
|
valueGroup: 1
|
|
2192
2587
|
}
|
|
2193
2588
|
];
|
|
@@ -2325,6 +2720,12 @@ function scanAndRedactSecrets(content, rules, filePath) {
|
|
|
2325
2720
|
continue;
|
|
2326
2721
|
}
|
|
2327
2722
|
const group = valueGroup === void 0 ? void 0 : match.indices?.[valueGroup];
|
|
2723
|
+
if (group && isSelfNamingConstant(line, group[0], group[1])) continue;
|
|
2724
|
+
if (valueGroup !== void 0 && startsInsideStringLiteral(line, match.index)) continue;
|
|
2725
|
+
if (group && isUnquotedValue(line, group[0]) && isDeclaredBinding(line, match.index)) {
|
|
2726
|
+
continue;
|
|
2727
|
+
}
|
|
2728
|
+
if (group && isTemplateInterpolation(line, group[0], group[1])) continue;
|
|
2328
2729
|
spans.push(
|
|
2329
2730
|
group ? { start: group[0], end: group[1], rule } : { start: match.index, end: match.index + match[0].length, rule }
|
|
2330
2731
|
);
|
|
@@ -2354,6 +2755,46 @@ function expandOverToken(line, start, end) {
|
|
|
2354
2755
|
while (right < line.length && TOKEN_CHARS_RIGHT.test(line[right])) right += 1;
|
|
2355
2756
|
return { start: left, end: right };
|
|
2356
2757
|
}
|
|
2758
|
+
function startsInsideStringLiteral(line, matchIndex) {
|
|
2759
|
+
let runStart = matchIndex;
|
|
2760
|
+
while (runStart > 0 && /[A-Za-z0-9_-]/.test(line[runStart - 1])) runStart -= 1;
|
|
2761
|
+
const before = runStart > 0 ? line[runStart - 1] : "";
|
|
2762
|
+
return before === '"' || before === "'" || before === "`";
|
|
2763
|
+
}
|
|
2764
|
+
function isTemplateInterpolation(line, start, end) {
|
|
2765
|
+
return line[start] === "$" && line[start + 1] === "{" && line[end - 1] === "}";
|
|
2766
|
+
}
|
|
2767
|
+
function isSelfNamingConstant(line, start, end) {
|
|
2768
|
+
const value = line.slice(start, end);
|
|
2769
|
+
if (!/[a-z][A-Z]/.test(value)) return false;
|
|
2770
|
+
let index = start;
|
|
2771
|
+
if (index > 0) {
|
|
2772
|
+
const openingQuote = line[index - 1];
|
|
2773
|
+
if (openingQuote === '"' || openingQuote === "'" || openingQuote === "`") index -= 1;
|
|
2774
|
+
}
|
|
2775
|
+
while (index > 0 && /\s/.test(line[index - 1])) index -= 1;
|
|
2776
|
+
if (index === 0) return false;
|
|
2777
|
+
if (line[index - 1] !== ":" && line[index - 1] !== "=") return false;
|
|
2778
|
+
index -= 1;
|
|
2779
|
+
while (index > 0 && /\s/.test(line[index - 1])) index -= 1;
|
|
2780
|
+
if (index > 0) {
|
|
2781
|
+
const closingQuote = line[index - 1];
|
|
2782
|
+
if (closingQuote === '"' || closingQuote === "'" || closingQuote === "`") index -= 1;
|
|
2783
|
+
}
|
|
2784
|
+
return line.endsWith(value, index);
|
|
2785
|
+
}
|
|
2786
|
+
function isUnquotedValue(line, start) {
|
|
2787
|
+
const before = start > 0 ? line[start - 1] : "";
|
|
2788
|
+
return before !== '"' && before !== "'" && before !== "`";
|
|
2789
|
+
}
|
|
2790
|
+
var DECLARATION_KEYWORDS = /\b(?:const|let|var|readonly|public|private|protected|static|declare|export|using)\s+$/;
|
|
2791
|
+
var DECLARATION_WINDOW = 40;
|
|
2792
|
+
function isDeclaredBinding(line, matchIndex) {
|
|
2793
|
+
let runStart = matchIndex;
|
|
2794
|
+
while (runStart > 0 && /[A-Za-z0-9_$]/.test(line[runStart - 1])) runStart -= 1;
|
|
2795
|
+
const from = Math.max(0, runStart - DECLARATION_WINDOW);
|
|
2796
|
+
return DECLARATION_KEYWORDS.test(line.slice(from, runStart));
|
|
2797
|
+
}
|
|
2357
2798
|
function applySpans(line, spans) {
|
|
2358
2799
|
const ordered = [...spans].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
2359
2800
|
const merged = [];
|
|
@@ -2380,45 +2821,58 @@ function applySpans(line, spans) {
|
|
|
2380
2821
|
function isDiscoveryMode(value) {
|
|
2381
2822
|
return value === "auto" || value === "git" || value === "walk";
|
|
2382
2823
|
}
|
|
2383
|
-
var activeDiscovery = "auto";
|
|
2384
|
-
function setActiveDiscovery(mode) {
|
|
2385
|
-
activeDiscovery = mode;
|
|
2386
|
-
}
|
|
2387
2824
|
var PRESET_IGNORES = new Set(
|
|
2388
2825
|
PRESETS.flatMap((preset) => preset.exclude ?? [])
|
|
2389
2826
|
);
|
|
2390
2827
|
function readLinkTarget(linkPath, fallback) {
|
|
2391
2828
|
try {
|
|
2392
|
-
return
|
|
2829
|
+
return fs14__namespace.readlinkSync(linkPath);
|
|
2393
2830
|
} catch {
|
|
2394
2831
|
return fallback;
|
|
2395
2832
|
}
|
|
2396
2833
|
}
|
|
2397
2834
|
function isInside(root, candidate) {
|
|
2398
|
-
const
|
|
2399
|
-
return
|
|
2835
|
+
const relative8 = path16__namespace.relative(root, candidate);
|
|
2836
|
+
return relative8 === "" || !relative8.startsWith(`..${path16__namespace.sep}`) && relative8 !== ".." && !path16__namespace.isAbsolute(relative8);
|
|
2400
2837
|
}
|
|
2838
|
+
var GIT_OUTPUT_LIMIT = 68157440;
|
|
2401
2839
|
function getGitInfo(cwd, fallbackBranch = "main") {
|
|
2402
2840
|
try {
|
|
2403
|
-
const
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2841
|
+
const output = childProcess__namespace.execFileSync(
|
|
2842
|
+
"git",
|
|
2843
|
+
["status", "--porcelain=v2", "--branch", "-z"],
|
|
2844
|
+
{
|
|
2845
|
+
cwd,
|
|
2846
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2847
|
+
encoding: "utf8",
|
|
2848
|
+
maxBuffer: GIT_OUTPUT_LIMIT
|
|
2849
|
+
}
|
|
2850
|
+
);
|
|
2851
|
+
let branch = fallbackBranch;
|
|
2852
|
+
let commit = "unknown";
|
|
2853
|
+
let isDirty = false;
|
|
2854
|
+
const records = output.split("\0").flatMap((record) => record.split("\n"));
|
|
2855
|
+
for (const record of records) {
|
|
2856
|
+
if (record.length === 0) continue;
|
|
2857
|
+
if (!record.startsWith("# ")) {
|
|
2858
|
+
isDirty = true;
|
|
2859
|
+
break;
|
|
2860
|
+
}
|
|
2861
|
+
const oid = record.match(/^# branch\.oid (.+)$/);
|
|
2862
|
+
if (oid) {
|
|
2863
|
+
commit = oid[1] === "(initial)" ? "unknown" : oid[1];
|
|
2864
|
+
continue;
|
|
2865
|
+
}
|
|
2866
|
+
const head = record.match(/^# branch\.head (.+)$/);
|
|
2867
|
+
if (head) {
|
|
2868
|
+
branch = head[1] === "(detached)" ? fallbackBranch : head[1];
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
return { branch, commit, isDirty };
|
|
2872
|
+
} catch (err) {
|
|
2873
|
+
const code = err.code;
|
|
2874
|
+
const isDirty = code === "ENOBUFS";
|
|
2875
|
+
return { branch: fallbackBranch, commit: "unknown", isDirty };
|
|
2422
2876
|
}
|
|
2423
2877
|
}
|
|
2424
2878
|
var DEFAULT_IGNORED_DIRS = [
|
|
@@ -2465,16 +2919,27 @@ function isSensitivePath(relativePath) {
|
|
|
2465
2919
|
}
|
|
2466
2920
|
return matchesSensitiveList(posixPath, base);
|
|
2467
2921
|
}
|
|
2922
|
+
var GLOBAL_PATTERNS = /* @__PURE__ */ new WeakMap();
|
|
2923
|
+
function globalPattern(pattern) {
|
|
2924
|
+
let compiled = GLOBAL_PATTERNS.get(pattern);
|
|
2925
|
+
if (compiled === void 0) {
|
|
2926
|
+
compiled = new RegExp(pattern.source, `${pattern.flags.replace(/[gy]/g, "")}g`);
|
|
2927
|
+
GLOBAL_PATTERNS.set(pattern, compiled);
|
|
2928
|
+
}
|
|
2929
|
+
return compiled;
|
|
2930
|
+
}
|
|
2931
|
+
var DEFAULT_PATH_RULES = [...DEFAULT_SECRET_PATTERNS];
|
|
2468
2932
|
function pathScanRules(extraRules) {
|
|
2469
|
-
|
|
2470
|
-
return [...DEFAULT_SECRET_PATTERNS, ...
|
|
2933
|
+
if (extraRules === void 0 || extraRules.length === 0) return DEFAULT_PATH_RULES;
|
|
2934
|
+
return [...DEFAULT_SECRET_PATTERNS, ...extraRules];
|
|
2471
2935
|
}
|
|
2472
2936
|
function findSecretInPath(relativePath, extraRules) {
|
|
2473
2937
|
const posixPath = relativePath.replace(/\\/g, "/");
|
|
2474
2938
|
const spans = [];
|
|
2475
2939
|
const rules = [];
|
|
2476
2940
|
for (const { rule, pattern } of pathScanRules(extraRules)) {
|
|
2477
|
-
const scan =
|
|
2941
|
+
const scan = globalPattern(pattern);
|
|
2942
|
+
scan.lastIndex = 0;
|
|
2478
2943
|
let match;
|
|
2479
2944
|
while ((match = scan.exec(posixPath)) !== null) {
|
|
2480
2945
|
if (match[0].length === 0) {
|
|
@@ -2601,10 +3066,10 @@ function trimUnescapedTrailingSpaces(line) {
|
|
|
2601
3066
|
return line.slice(0, end);
|
|
2602
3067
|
}
|
|
2603
3068
|
function readGitignoreRules(dir) {
|
|
2604
|
-
const gitignorePath =
|
|
3069
|
+
const gitignorePath = path16__namespace.join(dir, ".gitignore");
|
|
2605
3070
|
let raw;
|
|
2606
3071
|
try {
|
|
2607
|
-
raw =
|
|
3072
|
+
raw = fs14__namespace.readFileSync(gitignorePath, "utf8");
|
|
2608
3073
|
} catch {
|
|
2609
3074
|
return [];
|
|
2610
3075
|
}
|
|
@@ -2630,7 +3095,7 @@ function readGitignoreRules(dir) {
|
|
|
2630
3095
|
function escapeRegExpChar(character) {
|
|
2631
3096
|
return /[\\^$.*+?()[\]{}|/]/.test(character) ? `\\${character}` : character;
|
|
2632
3097
|
}
|
|
2633
|
-
function
|
|
3098
|
+
function matchGitignorePattern(target, pattern) {
|
|
2634
3099
|
const anchored = pattern.startsWith("/");
|
|
2635
3100
|
const body = anchored ? pattern.slice(1) : pattern;
|
|
2636
3101
|
let source = "";
|
|
@@ -2664,15 +3129,33 @@ function matchEscapedGitignorePattern(target, pattern) {
|
|
|
2664
3129
|
source += "[^/]";
|
|
2665
3130
|
continue;
|
|
2666
3131
|
}
|
|
3132
|
+
if (character === "[") {
|
|
3133
|
+
const close = body.indexOf("]", i + 1);
|
|
3134
|
+
if (close !== -1 && close > i + 1) {
|
|
3135
|
+
const inner = body.slice(i + 1, close);
|
|
3136
|
+
const negated = inner.startsWith("!") || inner.startsWith("^");
|
|
3137
|
+
const chars = negated ? inner.slice(1) : inner;
|
|
3138
|
+
const safe = chars.replace(/[\\^\]]/g, (value) => `\\${value}`);
|
|
3139
|
+
source += negated ? `[^/${safe}]` : `[${safe}]`;
|
|
3140
|
+
i = close;
|
|
3141
|
+
continue;
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
2667
3144
|
source += escapeRegExpChar(character);
|
|
2668
3145
|
}
|
|
2669
3146
|
const prefix = anchored || body.includes("/") ? "^" : "(?:^|.*/)";
|
|
2670
3147
|
return new RegExp(`${prefix}${source}(?:/.*)?$`).test(target);
|
|
2671
3148
|
}
|
|
2672
|
-
function
|
|
2673
|
-
|
|
3149
|
+
function matchesGitignoreRule(rule, scoped, isDirectory) {
|
|
3150
|
+
if (!rule.dirOnly) return matchGitignorePattern(scoped, rule.pattern);
|
|
3151
|
+
const segments = scoped.split("/");
|
|
3152
|
+
const limit = isDirectory ? segments.length : segments.length - 1;
|
|
3153
|
+
for (let depth = 1; depth <= limit; depth += 1) {
|
|
3154
|
+
if (matchGitignorePattern(segments.slice(0, depth).join("/"), rule.pattern)) return true;
|
|
3155
|
+
}
|
|
3156
|
+
return false;
|
|
2674
3157
|
}
|
|
2675
|
-
function gitignoreVerdict(layers, relativePath) {
|
|
3158
|
+
function gitignoreVerdict(layers, relativePath, isDirectory) {
|
|
2676
3159
|
let verdict = null;
|
|
2677
3160
|
for (const layer2 of layers) {
|
|
2678
3161
|
let scoped;
|
|
@@ -2685,8 +3168,7 @@ function gitignoreVerdict(layers, relativePath) {
|
|
|
2685
3168
|
}
|
|
2686
3169
|
let layerVerdict = null;
|
|
2687
3170
|
for (const rule of layer2.rules) {
|
|
2688
|
-
|
|
2689
|
-
if (matchesGitignorePattern(target, rule.pattern)) layerVerdict = !rule.negated;
|
|
3171
|
+
if (matchesGitignoreRule(rule, scoped, isDirectory)) layerVerdict = !rule.negated;
|
|
2690
3172
|
}
|
|
2691
3173
|
if (layerVerdict !== null) verdict = layerVerdict;
|
|
2692
3174
|
}
|
|
@@ -2711,13 +3193,14 @@ function defaultIgnoreReason(relativePath) {
|
|
|
2711
3193
|
}
|
|
2712
3194
|
function listIgnoredPaths(cwd) {
|
|
2713
3195
|
try {
|
|
2714
|
-
const stdout = childProcess__namespace.
|
|
2715
|
-
"git
|
|
3196
|
+
const stdout = childProcess__namespace.execFileSync(
|
|
3197
|
+
"git",
|
|
3198
|
+
["ls-files", "-z", "--others", "--ignored", "--exclude-standard", "--directory"],
|
|
2716
3199
|
{
|
|
2717
3200
|
cwd,
|
|
2718
3201
|
stdio: ["ignore", "pipe", "ignore"],
|
|
2719
3202
|
encoding: "utf8",
|
|
2720
|
-
maxBuffer:
|
|
3203
|
+
maxBuffer: GIT_OUTPUT_LIMIT
|
|
2721
3204
|
}
|
|
2722
3205
|
);
|
|
2723
3206
|
return stdout.split("\0").filter((entry) => entry.length > 0).map((entry) => entry.replace(/\\/g, "/"));
|
|
@@ -2725,13 +3208,9 @@ function listIgnoredPaths(cwd) {
|
|
|
2725
3208
|
return [];
|
|
2726
3209
|
}
|
|
2727
3210
|
}
|
|
2728
|
-
var TOOL_OWNED_DIR_NAMES = /* @__PURE__ */ new Set(["snapshot", "snapshot.tmp"]);
|
|
2729
|
-
function isToolOwnedDirName(name) {
|
|
2730
|
-
return TOOL_OWNED_DIR_NAMES.has(name) || name.startsWith("snapshot.backup-");
|
|
2731
|
-
}
|
|
2732
3211
|
function hasSnapshotMarker(target) {
|
|
2733
3212
|
try {
|
|
2734
|
-
return
|
|
3213
|
+
return fs14__namespace.statSync(target).isDirectory() && fs14__namespace.existsSync(path16__namespace.join(target, ".codebase-snapshot"));
|
|
2735
3214
|
} catch {
|
|
2736
3215
|
return false;
|
|
2737
3216
|
}
|
|
@@ -2745,7 +3224,7 @@ function makeToolOwnedDecider(cwd, toolOwnedPaths) {
|
|
|
2745
3224
|
const prefix = parts.slice(0, depth).join("/");
|
|
2746
3225
|
let hasMarker = markers.get(prefix);
|
|
2747
3226
|
if (hasMarker === void 0) {
|
|
2748
|
-
hasMarker =
|
|
3227
|
+
hasMarker = fs14__namespace.existsSync(path16__namespace.join(cwd, prefix, ".codebase-snapshot"));
|
|
2749
3228
|
markers.set(prefix, hasMarker);
|
|
2750
3229
|
}
|
|
2751
3230
|
if (hasMarker) return true;
|
|
@@ -2760,7 +3239,7 @@ function makeLinkProbe() {
|
|
|
2760
3239
|
let known = cache.get(absolutePath);
|
|
2761
3240
|
if (known === void 0) {
|
|
2762
3241
|
try {
|
|
2763
|
-
known =
|
|
3242
|
+
known = fs14__namespace.lstatSync(absolutePath).isSymbolicLink();
|
|
2764
3243
|
} catch {
|
|
2765
3244
|
known = false;
|
|
2766
3245
|
}
|
|
@@ -2773,7 +3252,7 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2773
3252
|
const isLink = makeLinkProbe();
|
|
2774
3253
|
let rootReal = cwd;
|
|
2775
3254
|
try {
|
|
2776
|
-
rootReal =
|
|
3255
|
+
rootReal = fs14__namespace.realpathSync(cwd);
|
|
2777
3256
|
} catch {
|
|
2778
3257
|
}
|
|
2779
3258
|
const reported = /* @__PURE__ */ new Set();
|
|
@@ -2783,7 +3262,7 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2783
3262
|
let linkedDir = null;
|
|
2784
3263
|
for (let depth = 1; depth <= parts.length; depth += 1) {
|
|
2785
3264
|
const prefix = parts.slice(0, depth).join("/");
|
|
2786
|
-
if (isLink(
|
|
3265
|
+
if (isLink(path16__namespace.join(cwd, prefix))) {
|
|
2787
3266
|
linkedDir = prefix;
|
|
2788
3267
|
break;
|
|
2789
3268
|
}
|
|
@@ -2794,10 +3273,10 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2794
3273
|
}
|
|
2795
3274
|
if (reported.has(linkedDir)) continue;
|
|
2796
3275
|
reported.add(linkedDir);
|
|
2797
|
-
const absolute =
|
|
3276
|
+
const absolute = path16__namespace.join(cwd, linkedDir);
|
|
2798
3277
|
let targetReal = absolute;
|
|
2799
3278
|
try {
|
|
2800
|
-
targetReal =
|
|
3279
|
+
targetReal = fs14__namespace.realpathSync(absolute);
|
|
2801
3280
|
} catch {
|
|
2802
3281
|
}
|
|
2803
3282
|
const target = readLinkTarget(absolute, targetReal);
|
|
@@ -2817,13 +3296,14 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2817
3296
|
return kept;
|
|
2818
3297
|
}
|
|
2819
3298
|
function listGitCandidates(cwd, skipped, isToolOwned) {
|
|
2820
|
-
const stdout = childProcess__namespace.
|
|
2821
|
-
"git
|
|
3299
|
+
const stdout = childProcess__namespace.execFileSync(
|
|
3300
|
+
"git",
|
|
3301
|
+
["ls-files", "-z", "--cached", "--others", "--exclude-standard"],
|
|
2822
3302
|
{
|
|
2823
3303
|
cwd,
|
|
2824
3304
|
stdio: ["ignore", "pipe", "ignore"],
|
|
2825
3305
|
encoding: "utf8",
|
|
2826
|
-
maxBuffer:
|
|
3306
|
+
maxBuffer: GIT_OUTPUT_LIMIT
|
|
2827
3307
|
}
|
|
2828
3308
|
);
|
|
2829
3309
|
let candidates = stdout.split("\0").filter((entry) => entry.length > 0).map((entry) => entry.replace(/\\/g, "/"));
|
|
@@ -2860,7 +3340,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2860
3340
|
const isToolOwned = makeToolOwnedDecider(cwd, [...toolOwnedPaths]);
|
|
2861
3341
|
let candidates;
|
|
2862
3342
|
const skipped = [];
|
|
2863
|
-
const discovery = options.discovery ??
|
|
3343
|
+
const discovery = options.discovery ?? "auto";
|
|
2864
3344
|
if (discovery === "walk") {
|
|
2865
3345
|
const walked = walkDirectory(cwd, cwd, rootRealFor(cwd), /* @__PURE__ */ new Set(), toolOwnedPaths);
|
|
2866
3346
|
candidates = walked.files;
|
|
@@ -2874,7 +3354,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2874
3354
|
"Discovery mode is `git`, but git could not list this directory's files. Run inside a working repository, or pass --discovery walk (or auto) to scan the tree directly."
|
|
2875
3355
|
);
|
|
2876
3356
|
}
|
|
2877
|
-
if (
|
|
3357
|
+
if (fs14__namespace.existsSync(path16__namespace.join(cwd, ".git"))) {
|
|
2878
3358
|
throw new Error(
|
|
2879
3359
|
"This directory is a git repository, but git could not list its files. A plain walk honours .gitignore files, but git also applies .git/info/exclude and your global excludes file, includes files that are tracked although a .gitignore now matches them, and treats a submodule as a single entry. Fix the repository (git status should work), run outside git, or pass --discovery walk to accept those differences."
|
|
2880
3360
|
);
|
|
@@ -2933,7 +3413,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2933
3413
|
}
|
|
2934
3414
|
function rootRealFor(root) {
|
|
2935
3415
|
try {
|
|
2936
|
-
return
|
|
3416
|
+
return fs14__namespace.realpathSync(root);
|
|
2937
3417
|
} catch {
|
|
2938
3418
|
return root;
|
|
2939
3419
|
}
|
|
@@ -2941,18 +3421,18 @@ function rootRealFor(root) {
|
|
|
2941
3421
|
function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PURE__ */ new Set(), parentGitignoreLayers = []) {
|
|
2942
3422
|
const results = { files: [], skipped: [] };
|
|
2943
3423
|
const stack = [];
|
|
2944
|
-
const relativeToBase = (target) =>
|
|
3424
|
+
const relativeToBase = (target) => path16__namespace.relative(baseDir, target).replace(/\\/g, "/") || ".";
|
|
2945
3425
|
const enter = (current, layers) => {
|
|
2946
3426
|
if (visited.has(current)) return false;
|
|
2947
3427
|
visited.add(current);
|
|
2948
3428
|
const localRules = readGitignoreRules(current);
|
|
2949
3429
|
const gitignoreLayers = localRules.length === 0 ? layers : [
|
|
2950
3430
|
...layers,
|
|
2951
|
-
{ prefix:
|
|
3431
|
+
{ prefix: path16__namespace.relative(baseDir, current).replace(/\\/g, "/"), rules: localRules }
|
|
2952
3432
|
];
|
|
2953
3433
|
let entries;
|
|
2954
3434
|
try {
|
|
2955
|
-
entries =
|
|
3435
|
+
entries = fs14__namespace.readdirSync(current, { withFileTypes: true });
|
|
2956
3436
|
} catch {
|
|
2957
3437
|
results.skipped.push({
|
|
2958
3438
|
path: relativeToBase(current),
|
|
@@ -2974,10 +3454,10 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
2974
3454
|
}
|
|
2975
3455
|
const entry = frame.entries[frame.index];
|
|
2976
3456
|
frame.index += 1;
|
|
2977
|
-
const fullPath =
|
|
2978
|
-
const relPath =
|
|
2979
|
-
if (
|
|
2980
|
-
if (DEFAULT_IGNORED_DIRS.includes(entry.name)) {
|
|
3457
|
+
const fullPath = path16__namespace.join(frame.dir, entry.name);
|
|
3458
|
+
const relPath = path16__namespace.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
3459
|
+
if (reservedPaths.has(relPath)) continue;
|
|
3460
|
+
if (entry.isDirectory() && DEFAULT_IGNORED_DIRS.includes(entry.name)) {
|
|
2981
3461
|
results.skipped.push({
|
|
2982
3462
|
path: relPath,
|
|
2983
3463
|
reason: "default-ignore",
|
|
@@ -2993,7 +3473,7 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
2993
3473
|
});
|
|
2994
3474
|
continue;
|
|
2995
3475
|
}
|
|
2996
|
-
if (gitignoreVerdict(frame.gitignoreLayers, relPath) === true) {
|
|
3476
|
+
if (gitignoreVerdict(frame.gitignoreLayers, relPath, entry.isDirectory()) === true) {
|
|
2997
3477
|
if (!hasSnapshotMarker(fullPath)) {
|
|
2998
3478
|
results.skipped.push({
|
|
2999
3479
|
path: relPath,
|
|
@@ -3008,14 +3488,14 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
3008
3488
|
if (entry.isSymbolicLink()) {
|
|
3009
3489
|
let targetReal;
|
|
3010
3490
|
try {
|
|
3011
|
-
targetReal =
|
|
3491
|
+
targetReal = fs14__namespace.realpathSync(fullPath);
|
|
3012
3492
|
} catch {
|
|
3013
3493
|
results.skipped.push({ path: relPath, reason: "read-error", detail: "broken symlink" });
|
|
3014
3494
|
continue;
|
|
3015
3495
|
}
|
|
3016
3496
|
let stats;
|
|
3017
3497
|
try {
|
|
3018
|
-
stats =
|
|
3498
|
+
stats = fs14__namespace.statSync(fullPath);
|
|
3019
3499
|
} catch {
|
|
3020
3500
|
results.skipped.push({ path: relPath, reason: "read-error", detail: "unreadable symlink" });
|
|
3021
3501
|
continue;
|
|
@@ -3057,6 +3537,28 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
3057
3537
|
return results;
|
|
3058
3538
|
}
|
|
3059
3539
|
|
|
3540
|
+
// src/tokenize.ts
|
|
3541
|
+
var WORD_RUN = /[A-Za-z0-9_]+/g;
|
|
3542
|
+
var SINGLE_TOKEN = /[^\sA-Za-z0-9_]/g;
|
|
3543
|
+
var LINE_BREAK_RUN = /\n+/g;
|
|
3544
|
+
var CHARS_PER_WORD_TOKEN = 6;
|
|
3545
|
+
function estimateTokens(text2) {
|
|
3546
|
+
if (text2.length === 0) return 0;
|
|
3547
|
+
let total = 0;
|
|
3548
|
+
for (const match of text2.match(WORD_RUN) ?? []) {
|
|
3549
|
+
total += Math.max(1, Math.round(match.length / CHARS_PER_WORD_TOKEN));
|
|
3550
|
+
}
|
|
3551
|
+
total += text2.match(SINGLE_TOKEN)?.length ?? 0;
|
|
3552
|
+
total += text2.match(LINE_BREAK_RUN)?.length ?? 0;
|
|
3553
|
+
return total;
|
|
3554
|
+
}
|
|
3555
|
+
function bytesForTokens(tokens) {
|
|
3556
|
+
return Math.max(0, Math.floor(tokens * 4));
|
|
3557
|
+
}
|
|
3558
|
+
function formatTokens(count) {
|
|
3559
|
+
return count.toLocaleString("en-US");
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3060
3562
|
// src/config.ts
|
|
3061
3563
|
var CONFIG_DIR_SYMBOL = /* @__PURE__ */ Symbol.for("codebase-snapshot.configDir");
|
|
3062
3564
|
var CONFIG_FILENAMES = [
|
|
@@ -3096,9 +3598,23 @@ function isResolvedConfig(value) {
|
|
|
3096
3598
|
);
|
|
3097
3599
|
return hasResolvedNumbers && hasResolvedStrings && hasResolvedFlags && hasResolvedRules && hasCompiledRules;
|
|
3098
3600
|
}
|
|
3601
|
+
function normalizeBudgets(budgets, maxPartBytes) {
|
|
3602
|
+
const clearer = typeof maxPartBytes === "number" && Number.isFinite(maxPartBytes) && maxPartBytes > 0 ? maxPartBytes : void 0;
|
|
3603
|
+
if (budgets === void 0 || typeof budgets !== "object" || budgets === null) {
|
|
3604
|
+
return { default: clearer ?? 0, byLayer: {} };
|
|
3605
|
+
}
|
|
3606
|
+
const fallbackDefault = clearer ?? budgets.default;
|
|
3607
|
+
const byLayer = {};
|
|
3608
|
+
const layers = budgets.byLayer;
|
|
3609
|
+
if (layers !== void 0 && typeof layers === "object" && layers !== null) {
|
|
3610
|
+
for (const [layer2, value] of Object.entries(layers)) {
|
|
3611
|
+
byLayer[layer2] = Math.min(value, fallbackDefault);
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
return { default: fallbackDefault, byLayer };
|
|
3615
|
+
}
|
|
3099
3616
|
function normalizeResolvedConfig(config) {
|
|
3100
3617
|
const discovery = isDiscoveryMode(config.discovery) ? config.discovery : "auto";
|
|
3101
|
-
setActiveDiscovery(discovery);
|
|
3102
3618
|
return markResolvedConfig({
|
|
3103
3619
|
...config,
|
|
3104
3620
|
discovery,
|
|
@@ -3117,7 +3633,7 @@ function normalizeResolvedConfig(config) {
|
|
|
3117
3633
|
])
|
|
3118
3634
|
],
|
|
3119
3635
|
layersMode: config.layersMode === "replace" ? "replace" : "merge",
|
|
3120
|
-
budgets: config.budgets
|
|
3636
|
+
budgets: normalizeBudgets(config.budgets, config.maxPartBytes),
|
|
3121
3637
|
hooks: resolveHooks(config.hooks),
|
|
3122
3638
|
excludePatterns: Array.isArray(config.excludePatterns) ? config.excludePatterns : [],
|
|
3123
3639
|
layers: Array.isArray(config.layers) ? config.layers : [],
|
|
@@ -3239,6 +3755,7 @@ var CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
3239
3755
|
"artifacts",
|
|
3240
3756
|
"branch",
|
|
3241
3757
|
"budgets",
|
|
3758
|
+
"changedSince",
|
|
3242
3759
|
"cwd",
|
|
3243
3760
|
"discovery",
|
|
3244
3761
|
"exclude",
|
|
@@ -3253,12 +3770,18 @@ var CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
3253
3770
|
"layersMode",
|
|
3254
3771
|
"maxMonolithBytes",
|
|
3255
3772
|
"maxPartBytes",
|
|
3773
|
+
"maxPartTokens",
|
|
3256
3774
|
"outDir",
|
|
3257
3775
|
"preset",
|
|
3258
3776
|
"previewHeadLines",
|
|
3777
|
+
"staged",
|
|
3259
3778
|
"previewTailLines",
|
|
3779
|
+
"secretRules",
|
|
3260
3780
|
"secrets",
|
|
3261
|
-
"
|
|
3781
|
+
"sarif",
|
|
3782
|
+
"skeleton",
|
|
3783
|
+
"tmpDir",
|
|
3784
|
+
"withDependents"
|
|
3262
3785
|
]);
|
|
3263
3786
|
function assertKnownConfigKeys(value, file) {
|
|
3264
3787
|
const unknownKeys = Object.keys(value).filter((key) => !CONFIG_KEYS.has(key));
|
|
@@ -3313,11 +3836,11 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3313
3836
|
const warnings = [];
|
|
3314
3837
|
const errors = [];
|
|
3315
3838
|
for (const filename of CONFIG_FILENAMES) {
|
|
3316
|
-
const fullPath =
|
|
3317
|
-
if (!
|
|
3839
|
+
const fullPath = path16__namespace.join(cwd, filename);
|
|
3840
|
+
if (!fs14__namespace.existsSync(fullPath)) continue;
|
|
3318
3841
|
if (filename.endsWith(".json") || filename === ".snapshotrc") {
|
|
3319
3842
|
try {
|
|
3320
|
-
const config = parseJsonish(
|
|
3843
|
+
const config = parseJsonish(fs14__namespace.readFileSync(fullPath, "utf8"), fullPath);
|
|
3321
3844
|
if (config === null || typeof config !== "object" || Array.isArray(config)) {
|
|
3322
3845
|
throw new Error(
|
|
3323
3846
|
`${fullPath} does not hold a configuration object (received ${describeValue2(config)}).`
|
|
@@ -3351,12 +3874,16 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3351
3874
|
continue;
|
|
3352
3875
|
}
|
|
3353
3876
|
}
|
|
3354
|
-
const pkgJsonPath =
|
|
3355
|
-
if (
|
|
3877
|
+
const pkgJsonPath = path16__namespace.join(cwd, "package.json");
|
|
3878
|
+
if (fs14__namespace.existsSync(pkgJsonPath)) {
|
|
3356
3879
|
try {
|
|
3357
|
-
const parsed = parseJsonish(
|
|
3880
|
+
const parsed = parseJsonish(fs14__namespace.readFileSync(pkgJsonPath, "utf8"), pkgJsonPath);
|
|
3358
3881
|
const snapshotKey = parsed.snapshot;
|
|
3359
3882
|
if (snapshotKey && typeof snapshotKey === "object" && !Array.isArray(snapshotKey)) {
|
|
3883
|
+
assertKnownConfigKeys(
|
|
3884
|
+
snapshotKey,
|
|
3885
|
+
`${pkgJsonPath} (the "snapshot" key)`
|
|
3886
|
+
);
|
|
3360
3887
|
return { config: snapshotKey, file: pkgJsonPath, warnings, errors };
|
|
3361
3888
|
}
|
|
3362
3889
|
if (snapshotKey !== void 0) {
|
|
@@ -3377,7 +3904,7 @@ async function loadConfigFile(cwd) {
|
|
|
3377
3904
|
const loaded = await loadConfigFileDetailed(cwd);
|
|
3378
3905
|
if (loaded.config && loaded.file && typeof loaded.config === "object") {
|
|
3379
3906
|
Object.defineProperty(loaded.config, CONFIG_DIR_SYMBOL, {
|
|
3380
|
-
value:
|
|
3907
|
+
value: path16__namespace.dirname(loaded.file),
|
|
3381
3908
|
enumerable: false,
|
|
3382
3909
|
configurable: true
|
|
3383
3910
|
});
|
|
@@ -3388,6 +3915,7 @@ async function loadConfigFile(cwd) {
|
|
|
3388
3915
|
return loaded.config;
|
|
3389
3916
|
}
|
|
3390
3917
|
var MIN_MAX_PART_BYTES = 4096;
|
|
3918
|
+
var MIN_MAX_PART_TOKENS = MIN_MAX_PART_BYTES / 4;
|
|
3391
3919
|
var ARTIFACT_NAMES = [
|
|
3392
3920
|
"readme",
|
|
3393
3921
|
"manifest",
|
|
@@ -3536,12 +4064,12 @@ function resolveKindRules(value) {
|
|
|
3536
4064
|
function outputExcludePatterns(cwd, outDir, tmpDir) {
|
|
3537
4065
|
const patterns = [];
|
|
3538
4066
|
for (const dir of [outDir, tmpDir]) {
|
|
3539
|
-
const absolute =
|
|
3540
|
-
const
|
|
3541
|
-
if (!
|
|
4067
|
+
const absolute = path16__namespace.resolve(cwd, dir);
|
|
4068
|
+
const relative8 = path16__namespace.relative(cwd, absolute);
|
|
4069
|
+
if (!relative8 || relative8 === ".." || relative8.startsWith(`..${path16__namespace.sep}`) || path16__namespace.isAbsolute(relative8)) {
|
|
3542
4070
|
continue;
|
|
3543
4071
|
}
|
|
3544
|
-
patterns.push(`${
|
|
4072
|
+
patterns.push(`${relative8.replace(/\\/g, "/")}/**`);
|
|
3545
4073
|
}
|
|
3546
4074
|
return patterns;
|
|
3547
4075
|
}
|
|
@@ -3588,6 +4116,20 @@ function boundedBytes(value, fallback, minimum, label) {
|
|
|
3588
4116
|
}
|
|
3589
4117
|
return parsed;
|
|
3590
4118
|
}
|
|
4119
|
+
function boundedTokens(value, label) {
|
|
4120
|
+
const parsed = parseByteSize(value);
|
|
4121
|
+
if (parsed === void 0 || !Number.isInteger(parsed)) {
|
|
4122
|
+
throw new Error(
|
|
4123
|
+
`${label} must be a whole token count such as 30000 or "30k" (received ${describeBytes(value)}).`
|
|
4124
|
+
);
|
|
4125
|
+
}
|
|
4126
|
+
if (parsed < MIN_MAX_PART_TOKENS) {
|
|
4127
|
+
throw new Error(
|
|
4128
|
+
`${label} must be at least ${MIN_MAX_PART_TOKENS} tokens (${MIN_MAX_PART_BYTES} bytes is the smallest part budget); received ${describeBytes(value)}.`
|
|
4129
|
+
);
|
|
4130
|
+
}
|
|
4131
|
+
return parsed;
|
|
4132
|
+
}
|
|
3591
4133
|
function optionalBytes(value, label) {
|
|
3592
4134
|
if (value === void 0 || value === null) return null;
|
|
3593
4135
|
const parsed = parseByteSize(value);
|
|
@@ -3657,11 +4199,11 @@ function resolveCandidateCwd(userConfig, cliOptions, baseDir) {
|
|
|
3657
4199
|
);
|
|
3658
4200
|
}
|
|
3659
4201
|
const candidate = cliOptions?.cwd || userConfig?.cwd || baseDir || process.cwd();
|
|
3660
|
-
const resolved =
|
|
3661
|
-
if (!
|
|
4202
|
+
const resolved = path16__namespace.resolve(candidate);
|
|
4203
|
+
if (!fs14__namespace.existsSync(resolved)) {
|
|
3662
4204
|
throw new Error(`Working directory does not exist: ${resolved}`);
|
|
3663
4205
|
}
|
|
3664
|
-
if (!
|
|
4206
|
+
if (!fs14__namespace.statSync(resolved).isDirectory()) {
|
|
3665
4207
|
throw new Error(`Working directory is not a directory: ${resolved}`);
|
|
3666
4208
|
}
|
|
3667
4209
|
return resolved;
|
|
@@ -3676,7 +4218,6 @@ function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
|
3676
4218
|
);
|
|
3677
4219
|
}
|
|
3678
4220
|
const discovery = requestedDiscovery ?? "auto";
|
|
3679
|
-
setActiveDiscovery(discovery);
|
|
3680
4221
|
const forcedPreset = resolvePresetName(userConfig?.preset);
|
|
3681
4222
|
if (forcedPreset !== void 0 && !getPreset(forcedPreset)) {
|
|
3682
4223
|
throw new Error(unknownPresetMessage(forcedPreset));
|
|
@@ -3730,13 +4271,53 @@ function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
|
3730
4271
|
MIN_MAX_PART_BYTES,
|
|
3731
4272
|
"maxPartBytes"
|
|
3732
4273
|
);
|
|
4274
|
+
const rawMaxPartTokens = cliOptions?.maxPartTokens ?? userConfig?.maxPartTokens;
|
|
4275
|
+
const requestedMaxPartTokens = rawMaxPartTokens === null ? void 0 : rawMaxPartTokens;
|
|
4276
|
+
const maxPartTokens = requestedMaxPartTokens === void 0 ? void 0 : boundedTokens(requestedMaxPartTokens, "maxPartTokens");
|
|
4277
|
+
const effectiveMaxPartBytes = maxPartTokens === void 0 ? maxPartBytes : Math.min(maxPartBytes, bytesForTokens(maxPartTokens));
|
|
3733
4278
|
const budgets = resolveBudgets(
|
|
3734
4279
|
userConfig?.budgets,
|
|
3735
|
-
|
|
3736
|
-
explicitMaxPartBytes !== void 0
|
|
4280
|
+
effectiveMaxPartBytes,
|
|
4281
|
+
explicitMaxPartBytes !== void 0 || maxPartTokens !== void 0
|
|
3737
4282
|
);
|
|
4283
|
+
const resolvedMaxPartBytes = explicitMaxPartBytes !== void 0 || maxPartTokens !== void 0 ? effectiveMaxPartBytes : budgets.default;
|
|
3738
4284
|
const configuredOutDir = strictString(userConfig?.outDir, "snapshot", "`outDir`");
|
|
3739
4285
|
const outDir = cliOptions?.outDir || configuredOutDir;
|
|
4286
|
+
const rawChangedSinceSetting = cliOptions?.changedSince ?? userConfig?.changedSince;
|
|
4287
|
+
const rawChangedSince = rawChangedSinceSetting === null ? void 0 : rawChangedSinceSetting;
|
|
4288
|
+
if (rawChangedSince !== void 0 && (typeof rawChangedSince !== "string" || rawChangedSince.trim().length === 0)) {
|
|
4289
|
+
throw new Error(
|
|
4290
|
+
`\`changedSince\` must be a git ref such as "main" or "HEAD~3" (received ${describeValue2(rawChangedSince)}).`
|
|
4291
|
+
);
|
|
4292
|
+
}
|
|
4293
|
+
const changedSince = rawChangedSince === void 0 ? void 0 : String(rawChangedSince).trim();
|
|
4294
|
+
const rawStaged = cliOptions?.staged ?? userConfig?.staged;
|
|
4295
|
+
if (rawStaged !== void 0 && typeof rawStaged !== "boolean") {
|
|
4296
|
+
throw new Error(`\`staged\` must be a boolean (received ${describeValue2(rawStaged)}).`);
|
|
4297
|
+
}
|
|
4298
|
+
const staged = rawStaged === true;
|
|
4299
|
+
const rawWithDependents = cliOptions?.withDependents ?? userConfig?.withDependents;
|
|
4300
|
+
if (rawWithDependents !== void 0 && typeof rawWithDependents !== "boolean") {
|
|
4301
|
+
throw new Error(
|
|
4302
|
+
`\`withDependents\` must be a boolean (received ${describeValue2(rawWithDependents)}).`
|
|
4303
|
+
);
|
|
4304
|
+
}
|
|
4305
|
+
const withDependents = rawWithDependents === true;
|
|
4306
|
+
const rawSkeleton = cliOptions?.skeleton ?? userConfig?.skeleton;
|
|
4307
|
+
if (rawSkeleton !== void 0 && typeof rawSkeleton !== "boolean") {
|
|
4308
|
+
throw new Error(`\`skeleton\` must be a boolean (received ${describeValue2(rawSkeleton)}).`);
|
|
4309
|
+
}
|
|
4310
|
+
const skeleton = rawSkeleton === true;
|
|
4311
|
+
const rawSarif = cliOptions?.sarif ?? userConfig?.sarif;
|
|
4312
|
+
if (rawSarif !== void 0 && typeof rawSarif !== "boolean") {
|
|
4313
|
+
throw new Error(`\`sarif\` must be a boolean (received ${describeValue2(rawSarif)}).`);
|
|
4314
|
+
}
|
|
4315
|
+
const sarif = rawSarif === true;
|
|
4316
|
+
if (withDependents && changedSince === void 0 && !staged) {
|
|
4317
|
+
throw new Error(
|
|
4318
|
+
"`withDependents` widens a changed-files scope, so it needs `changedSince` or `staged` (with neither, every file is already included)."
|
|
4319
|
+
);
|
|
4320
|
+
}
|
|
3740
4321
|
const tmpDir = strictString(userConfig?.tmpDir, "snapshot.tmp", "`tmpDir`");
|
|
3741
4322
|
const branch = strictString(userConfig?.branch, "main", "`branch`");
|
|
3742
4323
|
const configuredSensitiveFiles = strictBoolean(
|
|
@@ -3757,7 +4338,8 @@ function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
|
3757
4338
|
branch,
|
|
3758
4339
|
outDir,
|
|
3759
4340
|
tmpDir,
|
|
3760
|
-
maxPartBytes,
|
|
4341
|
+
maxPartBytes: resolvedMaxPartBytes,
|
|
4342
|
+
...maxPartTokens === void 0 ? {} : { maxPartTokens },
|
|
3761
4343
|
largeFileBytes: boundedBytes(cliOptions?.largeFileBytes ?? userConfig?.largeFileBytes, 200 * 1024, 1, "largeFileBytes"),
|
|
3762
4344
|
previewHeadLines: strictPositiveInteger(userConfig?.previewHeadLines, 80, "`previewHeadLines`"),
|
|
3763
4345
|
previewTailLines: strictPositiveInteger(userConfig?.previewTailLines, 40, "`previewTailLines`"),
|
|
@@ -3767,6 +4349,11 @@ function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
|
3767
4349
|
"maxMonolithBytes"
|
|
3768
4350
|
),
|
|
3769
4351
|
includeSensitiveFiles,
|
|
4352
|
+
...changedSince === void 0 ? {} : { changedSince },
|
|
4353
|
+
...staged ? { staged } : {},
|
|
4354
|
+
...withDependents ? { withDependents } : {},
|
|
4355
|
+
...skeleton ? { skeleton } : {},
|
|
4356
|
+
...sarif ? { sarif } : {},
|
|
3770
4357
|
include: resolveInclude(userConfig?.include),
|
|
3771
4358
|
preset: preset.presetName,
|
|
3772
4359
|
presetExclude,
|
|
@@ -3800,14 +4387,11 @@ function matchRule(filePath, matcher) {
|
|
|
3800
4387
|
if (pattern.endsWith("/**")) {
|
|
3801
4388
|
const prefix = pattern.slice(0, -3).replace(/\/+$/, "");
|
|
3802
4389
|
if (!prefix) return true;
|
|
3803
|
-
if (!/[*?]/.test(prefix)) {
|
|
4390
|
+
if (!/[*?{[]/.test(prefix)) {
|
|
3804
4391
|
return filePath === prefix || filePath.startsWith(`${prefix}/`);
|
|
3805
4392
|
}
|
|
3806
4393
|
}
|
|
3807
|
-
|
|
3808
|
-
return globToRegExp(pattern).test(filePath);
|
|
3809
|
-
}
|
|
3810
|
-
return filePath.includes(pattern);
|
|
4394
|
+
return matchesExclude(filePath, pattern);
|
|
3811
4395
|
}
|
|
3812
4396
|
return false;
|
|
3813
4397
|
}
|
|
@@ -3826,6 +4410,21 @@ function classifyKind(filePath, kinds) {
|
|
|
3826
4410
|
function configDigest(config) {
|
|
3827
4411
|
const material = {
|
|
3828
4412
|
maxPartBytes: config.maxPartBytes,
|
|
4413
|
+
// The user's own unit. `maxPartBytes` above already reflects the converted ceiling, but
|
|
4414
|
+
// recording the token number means changing 32k to 64k moves the digest even in the
|
|
4415
|
+
// corner where both round to the same byte budget.
|
|
4416
|
+
maxPartTokens: config.maxPartTokens ?? null,
|
|
4417
|
+
// A changed-files scope decides which files are in the snapshot at all, so a snapshot
|
|
4418
|
+
// taken over a different scope is a different snapshot.
|
|
4419
|
+
changedSince: config.changedSince ?? null,
|
|
4420
|
+
staged: config.staged === true,
|
|
4421
|
+
withDependents: config.withDependents === true,
|
|
4422
|
+
// Signature compression changes the emitted bytes of every TypeScript file it touches.
|
|
4423
|
+
skeleton: config.skeleton === true,
|
|
4424
|
+
// `sarif` is not one of `artifacts`: it writes `reports/audit.sarif` beside the audit, and
|
|
4425
|
+
// leaving it out of the digest meant a snapshot taken without it and one taken with it
|
|
4426
|
+
// compared equal even though their directories differ.
|
|
4427
|
+
sarif: config.sarif === true,
|
|
3829
4428
|
largeFileBytes: config.largeFileBytes,
|
|
3830
4429
|
previewHeadLines: config.previewHeadLines,
|
|
3831
4430
|
previewTailLines: config.previewTailLines,
|
|
@@ -3838,11 +4437,21 @@ function configDigest(config) {
|
|
|
3838
4437
|
preset: config.preset,
|
|
3839
4438
|
layersMode: config.layersMode,
|
|
3840
4439
|
budgets: config.budgets,
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
4440
|
+
// The matcher is what makes a layer a layer. Hashing only the names meant that
|
|
4441
|
+
// moving `{ name: "api", match: "src/api/**" }` to `"src/v2/**"` produced the same
|
|
4442
|
+
// digest, so `validate` reported "valid and up to date" for a snapshot whose files
|
|
4443
|
+
// had all been reclassified — and per-layer budgets applied to different files.
|
|
4444
|
+
layers: config.layers.map((layer2) => ({ name: layer2.name, match: String(layer2.match) })),
|
|
4445
|
+
kinds: config.kinds.map((kind) => ({ kind: kind.kind, match: String(kind.match) })),
|
|
4446
|
+
// How files are enumerated decides what is in the snapshot at all.
|
|
4447
|
+
discovery: config.discovery ?? "auto",
|
|
4448
|
+
// `flags` and `valueGroup` change *what is masked*, and a rule's source alone does
|
|
4449
|
+
// not imply either: the same source with and without `i` redacts different lines.
|
|
4450
|
+
secretRules: config.secretRules.map(
|
|
4451
|
+
(rule) => `${rule.rule}:${rule.pattern.source}/${rule.pattern.flags}:${rule.valueGroup ?? ""}`
|
|
4452
|
+
).sort()
|
|
3844
4453
|
};
|
|
3845
|
-
return
|
|
4454
|
+
return crypto2.createHash("sha256").update(JSON.stringify(material)).digest("hex");
|
|
3846
4455
|
}
|
|
3847
4456
|
|
|
3848
4457
|
// src/define-config.ts
|
|
@@ -4020,6 +4629,95 @@ function generateAuditReport(manifest, secretFindings, secretRules) {
|
|
|
4020
4629
|
}
|
|
4021
4630
|
return lines.join("\n");
|
|
4022
4631
|
}
|
|
4632
|
+
var SARIF_VERSION = "2.1.0";
|
|
4633
|
+
var SARIF_SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json";
|
|
4634
|
+
var BUILTIN_RULE_IDS = new Map(
|
|
4635
|
+
[
|
|
4636
|
+
...DEFAULT_SECRET_PATTERNS.map((rule) => rule.rule),
|
|
4637
|
+
// Produced by the whole-block pass in `redact.ts`, not by a pattern in the catalogue.
|
|
4638
|
+
"private-key-block"
|
|
4639
|
+
].map((name, index) => [name, `CS${String(index + 1).padStart(3, "0")}`])
|
|
4640
|
+
);
|
|
4641
|
+
function ruleIdFor(name) {
|
|
4642
|
+
const builtin = BUILTIN_RULE_IDS.get(name);
|
|
4643
|
+
if (builtin !== void 0) return builtin;
|
|
4644
|
+
const digest = crypto2__namespace.createHash("sha256").update(name).digest("hex").slice(0, 8);
|
|
4645
|
+
return `CS${digest.toUpperCase()}`;
|
|
4646
|
+
}
|
|
4647
|
+
function generateSarifReport(manifest, findings, rules, toolVersion) {
|
|
4648
|
+
const ruleNames = Array.from(
|
|
4649
|
+
/* @__PURE__ */ new Set([...rules.map((rule) => rule.rule), ...findings.flatMap((finding) => finding.rules)])
|
|
4650
|
+
).sort();
|
|
4651
|
+
const ruleIndex = /* @__PURE__ */ new Map();
|
|
4652
|
+
const sarifRules = ruleNames.map((name, index) => {
|
|
4653
|
+
ruleIndex.set(name, index);
|
|
4654
|
+
const id = ruleIdFor(name);
|
|
4655
|
+
return {
|
|
4656
|
+
id,
|
|
4657
|
+
name,
|
|
4658
|
+
shortDescription: { text: `Credential-shaped value masked by the "${name}" rule` },
|
|
4659
|
+
defaultConfiguration: { level: "note" },
|
|
4660
|
+
help: {
|
|
4661
|
+
text: "A value in this line matched a secret pattern and was replaced with a redaction marker in the snapshot. If this is a real credential, rotate it; if it is a fixture, the placeholder can be made unmistakable."
|
|
4662
|
+
}
|
|
4663
|
+
};
|
|
4664
|
+
});
|
|
4665
|
+
const results = findings.filter((finding) => typeof finding.path === "string" && finding.path.length > 0).map((finding) => {
|
|
4666
|
+
const primary = finding.rules[0] ?? "unknown";
|
|
4667
|
+
const line = Math.max(1, finding.line);
|
|
4668
|
+
const findingPath = String(finding.path).replace(/\\/g, "/");
|
|
4669
|
+
return {
|
|
4670
|
+
ruleId: ruleIdFor(primary),
|
|
4671
|
+
ruleIndex: ruleIndex.get(primary) ?? 0,
|
|
4672
|
+
level: "note",
|
|
4673
|
+
message: {
|
|
4674
|
+
text: `Credential-shaped value masked (rule${finding.rules.length === 1 ? "" : "s"}: ${finding.rules.join(", ")})`
|
|
4675
|
+
},
|
|
4676
|
+
locations: [
|
|
4677
|
+
{
|
|
4678
|
+
physicalLocation: {
|
|
4679
|
+
artifactLocation: { uri: findingPath, uriBaseId: "%SRCROOT%" },
|
|
4680
|
+
region: { startLine: line }
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
],
|
|
4684
|
+
partialFingerprints: {
|
|
4685
|
+
// Stable across commits for the same finding, which is what keeps an alert open
|
|
4686
|
+
// instead of closing and reopening it on every run.
|
|
4687
|
+
codebaseSnapshotFinding: `${findingPath}#L${line}#${finding.rules.join("+")}`
|
|
4688
|
+
}
|
|
4689
|
+
};
|
|
4690
|
+
}).sort(
|
|
4691
|
+
(a, b) => a.locations[0].physicalLocation.artifactLocation.uri.localeCompare(
|
|
4692
|
+
b.locations[0].physicalLocation.artifactLocation.uri
|
|
4693
|
+
) || a.locations[0].physicalLocation.region.startLine - b.locations[0].physicalLocation.region.startLine
|
|
4694
|
+
);
|
|
4695
|
+
const log = {
|
|
4696
|
+
$schema: SARIF_SCHEMA,
|
|
4697
|
+
version: SARIF_VERSION,
|
|
4698
|
+
runs: [
|
|
4699
|
+
{
|
|
4700
|
+
tool: {
|
|
4701
|
+
driver: {
|
|
4702
|
+
name: "codebase-snapshot",
|
|
4703
|
+
version: toolVersion,
|
|
4704
|
+
informationUri: "https://github.com/ankurhalder/codebase-snapshot",
|
|
4705
|
+
rules: sarifRules
|
|
4706
|
+
}
|
|
4707
|
+
},
|
|
4708
|
+
automationDetails: {
|
|
4709
|
+
// The snapshot's own fingerprint: two uploads of the same snapshot are the same run,
|
|
4710
|
+
// so Code Scanning can tell an unchanged upload from a new one.
|
|
4711
|
+
id: `codebase-snapshot/${manifest.fingerprint}`
|
|
4712
|
+
},
|
|
4713
|
+
originalUriBaseIds: { "%SRCROOT%": { uri: "file:///" } },
|
|
4714
|
+
results
|
|
4715
|
+
}
|
|
4716
|
+
]
|
|
4717
|
+
};
|
|
4718
|
+
return `${JSON.stringify(log, null, 2)}
|
|
4719
|
+
`;
|
|
4720
|
+
}
|
|
4023
4721
|
|
|
4024
4722
|
// src/partitioner/preview.ts
|
|
4025
4723
|
function countLines(content) {
|
|
@@ -4069,14 +4767,29 @@ var HEADER_RESERVE_BYTES = 2048 + Buffer.byteLength(UNTRUSTED_CONTENT_NOTICE, "u
|
|
|
4069
4767
|
var MIN_SECTION_BUDGET_BYTES = 512;
|
|
4070
4768
|
var MAX_SPLIT_PASSES = 64;
|
|
4071
4769
|
var BUDGET_MARKER_RESERVE = 96;
|
|
4770
|
+
function insertTokenLine(markdown, tokens) {
|
|
4771
|
+
return markdown.replace(
|
|
4772
|
+
/^(- Files: .*)$/m,
|
|
4773
|
+
`$1
|
|
4774
|
+
- Estimated Tokens: ~${formatTokens(tokens)}`
|
|
4775
|
+
);
|
|
4776
|
+
}
|
|
4777
|
+
function replaceTokenLine(markdown, tokens) {
|
|
4778
|
+
const line = `- Estimated Tokens: ~${formatTokens(tokens)}`;
|
|
4779
|
+
if (/^- Estimated Tokens: .*$/m.test(markdown)) {
|
|
4780
|
+
return markdown.replace(/^- Estimated Tokens: .*$/m, line);
|
|
4781
|
+
}
|
|
4782
|
+
return insertTokenLine(markdown, tokens);
|
|
4783
|
+
}
|
|
4072
4784
|
var BUDGET_MARKER_SUFFIX = /\n\.\.\. \[\d+ lines omitted: part budget \d+ B\] \.\.\.$/;
|
|
4073
|
-
|
|
4785
|
+
var TOKEN_LINE = /^- Estimated Tokens: .*$/m;
|
|
4786
|
+
var TOKEN_LINE_SLACK = 32;
|
|
4787
|
+
function truncateWithBudgetMarker(content, maxBytes, budget, originalLines) {
|
|
4074
4788
|
const base = content.replace(BUDGET_MARKER_SUFFIX, "");
|
|
4075
|
-
const baseLines = countLines(base);
|
|
4076
4789
|
const truncated = truncateToBytes(base, Math.max(0, maxBytes));
|
|
4077
4790
|
if (!truncated.isTruncated) return { content: base, isTruncated: false };
|
|
4078
4791
|
const keptLines = countLines(truncated.content);
|
|
4079
|
-
const omitted = Math.max(1,
|
|
4792
|
+
const omitted = Math.max(1, Math.max(countLines(base), originalLines) - keptLines);
|
|
4080
4793
|
return {
|
|
4081
4794
|
content: `${truncated.content}
|
|
4082
4795
|
... [${omitted} lines omitted: part budget ${budget} B] ...`,
|
|
@@ -4090,7 +4803,7 @@ function partitionFiles(files, maxPartBytes, contentFingerprint) {
|
|
|
4090
4803
|
const prepared = files.map((file) => prepareSection(file, sectionBudget));
|
|
4091
4804
|
let groups = greedyGroups(prepared, budget);
|
|
4092
4805
|
for (let pass = 0; pass < MAX_SPLIT_PASSES; pass++) {
|
|
4093
|
-
const rendered = renderGroups(groups, contentFingerprint);
|
|
4806
|
+
const rendered = renderGroups(groups, budget, contentFingerprint);
|
|
4094
4807
|
const oversized = rendered.findIndex((part) => part.metadata.byteSize > budget);
|
|
4095
4808
|
if (oversized === -1) return rendered;
|
|
4096
4809
|
const group = groups[oversized];
|
|
@@ -4106,28 +4819,49 @@ function partitionFiles(files, maxPartBytes, contentFingerprint) {
|
|
|
4106
4819
|
0,
|
|
4107
4820
|
Buffer.byteLength(currentContent, "utf8") - overflow - BUDGET_MARKER_RESERVE
|
|
4108
4821
|
);
|
|
4109
|
-
const shrunk = truncateWithBudgetMarker(
|
|
4822
|
+
const shrunk = truncateWithBudgetMarker(
|
|
4823
|
+
currentContent,
|
|
4824
|
+
shrinkTo,
|
|
4825
|
+
budget,
|
|
4826
|
+
section.file.originalLines
|
|
4827
|
+
);
|
|
4110
4828
|
if (shrunk.isTruncated) {
|
|
4111
4829
|
applyTruncatedContent(section.file, shrunk.content);
|
|
4112
4830
|
section.text = renderFileSection(section.file);
|
|
4113
4831
|
section.byteSize = Buffer.byteLength(section.text, "utf8");
|
|
4114
4832
|
continue;
|
|
4115
4833
|
}
|
|
4116
|
-
const marker2 =
|
|
4117
|
-
|
|
4834
|
+
const marker2 = TRUNCATION_MARKER;
|
|
4835
|
+
assertBudgetHoldsMarker(budget);
|
|
4836
|
+
const target = Math.max(
|
|
4837
|
+
0,
|
|
4838
|
+
budget - Buffer.byteLength(marker2, "utf8") - TOKEN_LINE_SLACK
|
|
4839
|
+
);
|
|
4118
4840
|
const clipped = truncateToBytes(rendered[oversized].markdown, target);
|
|
4119
4841
|
rendered[oversized] = rehashPart(rendered[oversized], clipped.content + marker2);
|
|
4120
4842
|
return rendered;
|
|
4121
4843
|
}
|
|
4122
|
-
return enforceBudget(renderGroups(groups, contentFingerprint), budget);
|
|
4844
|
+
return enforceBudget(renderGroups(groups, budget, contentFingerprint), budget);
|
|
4123
4845
|
}
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
const
|
|
4127
|
-
|
|
4128
|
-
|
|
4846
|
+
var TRUNCATION_MARKER = "\n... [part truncated: cannot fit budget] ...\n";
|
|
4847
|
+
function assertBudgetHoldsMarker(budget) {
|
|
4848
|
+
const minimum = Buffer.byteLength(TRUNCATION_MARKER, "utf8");
|
|
4849
|
+
if (budget < minimum) {
|
|
4850
|
+
throw new Error(
|
|
4851
|
+
`A part budget of ${budget} B cannot hold the truncation notice (${minimum} B), so a clipped part could not fit and still say it was clipped. Use a budget of at least ${minimum} B.`
|
|
4852
|
+
);
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
function enforceBudget(parts, budget) {
|
|
4856
|
+
const target = Math.max(
|
|
4857
|
+
0,
|
|
4858
|
+
budget - Buffer.byteLength(TRUNCATION_MARKER, "utf8") - TOKEN_LINE_SLACK
|
|
4859
|
+
);
|
|
4860
|
+
return parts.map((part) => {
|
|
4861
|
+
if (part.metadata.byteSize <= budget) return part;
|
|
4862
|
+
assertBudgetHoldsMarker(budget);
|
|
4129
4863
|
const clipped = truncateToBytes(part.markdown, target);
|
|
4130
|
-
return rehashPart(part, clipped.content +
|
|
4864
|
+
return rehashPart(part, clipped.content + TRUNCATION_MARKER);
|
|
4131
4865
|
});
|
|
4132
4866
|
}
|
|
4133
4867
|
function renumberParts(parts, contentFingerprint) {
|
|
@@ -4136,7 +4870,7 @@ function renumberParts(parts, contentFingerprint) {
|
|
|
4136
4870
|
const zeroPad = (index) => String(index).padStart(Math.max(3, String(total).length), "0");
|
|
4137
4871
|
const nameFor = (position) => `part-${zeroPad(position)}.md`;
|
|
4138
4872
|
const marker2 = "## Contents\n";
|
|
4139
|
-
|
|
4873
|
+
const renumbered = parts.map((part, index) => {
|
|
4140
4874
|
const markerIndex = part.markdown.indexOf(marker2);
|
|
4141
4875
|
const body = markerIndex === -1 ? "" : part.markdown.slice(markerIndex + marker2.length);
|
|
4142
4876
|
const included = part.metadata.includedFiles;
|
|
@@ -4172,19 +4906,25 @@ function renumberParts(parts, contentFingerprint) {
|
|
|
4172
4906
|
"## Contents",
|
|
4173
4907
|
""
|
|
4174
4908
|
].join("\n");
|
|
4175
|
-
const
|
|
4909
|
+
const assembled = (header + body).replace(/\r\n/g, "\n");
|
|
4910
|
+
const estimatedTokens = estimateTokens(assembled);
|
|
4911
|
+
const markdown = insertTokenLine(assembled, estimatedTokens);
|
|
4176
4912
|
const metadata = {
|
|
4177
4913
|
...part.metadata,
|
|
4178
4914
|
index: index + 1,
|
|
4179
4915
|
filename: nameFor(index + 1),
|
|
4180
4916
|
byteSize: Buffer.byteLength(markdown, "utf8"),
|
|
4181
|
-
|
|
4917
|
+
estimatedTokens,
|
|
4918
|
+
sha256: crypto2__namespace.createHash("sha256").update(markdown).digest("hex"),
|
|
4182
4919
|
lines: countLines(markdown),
|
|
4183
4920
|
fileCount: included.length,
|
|
4184
4921
|
layersCovered: Object.keys(layerCounts).sort()
|
|
4185
4922
|
};
|
|
4186
4923
|
return { filename: metadata.filename, markdown, metadata };
|
|
4187
4924
|
});
|
|
4925
|
+
return renumbered.map(
|
|
4926
|
+
(part) => enforceBudget([part], part.metadata.budgetBytes)[0] ?? part
|
|
4927
|
+
);
|
|
4188
4928
|
}
|
|
4189
4929
|
var SECTION_TRUNCATION_NOTICE = "\n... [section truncated: metadata alone exceeds the part budget] ...\n";
|
|
4190
4930
|
var METADATA_VALUE_MAX = 200;
|
|
@@ -4208,7 +4948,12 @@ function prepareSection(file, sectionBudget) {
|
|
|
4208
4948
|
const overhead = byteSize - Buffer.byteLength(content, "utf8");
|
|
4209
4949
|
const allowed = sectionBudget - overhead - BUDGET_MARKER_RESERVE;
|
|
4210
4950
|
if (allowed <= 0) return;
|
|
4211
|
-
const truncated = truncateWithBudgetMarker(
|
|
4951
|
+
const truncated = truncateWithBudgetMarker(
|
|
4952
|
+
content,
|
|
4953
|
+
allowed,
|
|
4954
|
+
sectionBudget,
|
|
4955
|
+
file.originalLines
|
|
4956
|
+
);
|
|
4212
4957
|
if (!truncated.isTruncated) return;
|
|
4213
4958
|
applyTruncatedContent(file, truncated.content);
|
|
4214
4959
|
text2 = renderFileSection(file);
|
|
@@ -4238,14 +4983,17 @@ function applyTruncatedContent(file, content) {
|
|
|
4238
4983
|
file.includedLines = countLines(content);
|
|
4239
4984
|
}
|
|
4240
4985
|
function rehashPart(part, markdown) {
|
|
4986
|
+
const estimatedTokens = estimateTokens(markdown.replace(TOKEN_LINE, ""));
|
|
4987
|
+
const redistributed = replaceTokenLine(markdown, estimatedTokens);
|
|
4241
4988
|
return {
|
|
4242
4989
|
filename: part.filename,
|
|
4243
|
-
markdown,
|
|
4990
|
+
markdown: redistributed,
|
|
4244
4991
|
metadata: {
|
|
4245
4992
|
...part.metadata,
|
|
4246
|
-
byteSize: Buffer.byteLength(
|
|
4247
|
-
sha256:
|
|
4248
|
-
lines: countLines(
|
|
4993
|
+
byteSize: Buffer.byteLength(redistributed, "utf8"),
|
|
4994
|
+
sha256: crypto2__namespace.createHash("sha256").update(redistributed).digest("hex"),
|
|
4995
|
+
lines: countLines(redistributed),
|
|
4996
|
+
estimatedTokens
|
|
4249
4997
|
}
|
|
4250
4998
|
};
|
|
4251
4999
|
}
|
|
@@ -4274,7 +5022,7 @@ function greedyGroups(sections, budget) {
|
|
|
4274
5022
|
if (current.length > 0) groups.push(current);
|
|
4275
5023
|
return groups;
|
|
4276
5024
|
}
|
|
4277
|
-
function renderGroups(groups, contentFingerprint) {
|
|
5025
|
+
function renderGroups(groups, budget, contentFingerprint) {
|
|
4278
5026
|
const total = groups.length;
|
|
4279
5027
|
const zeroPad = (index) => String(index).padStart(Math.max(3, String(total).length), "0");
|
|
4280
5028
|
return groups.map((group, index) => {
|
|
@@ -4314,9 +5062,11 @@ function renderGroups(groups, contentFingerprint) {
|
|
|
4314
5062
|
"## Contents",
|
|
4315
5063
|
""
|
|
4316
5064
|
].join("\n");
|
|
4317
|
-
const
|
|
5065
|
+
const assembled = (header + group.map((s) => s.text).join("")).replace(/\r\n/g, "\n");
|
|
5066
|
+
const estimatedTokens = estimateTokens(assembled);
|
|
5067
|
+
const content = insertTokenLine(assembled, estimatedTokens);
|
|
4318
5068
|
const byteSize = Buffer.byteLength(content, "utf8");
|
|
4319
|
-
const sha256 =
|
|
5069
|
+
const sha256 = crypto2__namespace.createHash("sha256").update(content).digest("hex");
|
|
4320
5070
|
const includedFiles = group.map(({ file }) => ({
|
|
4321
5071
|
relativePath: file.relativePath,
|
|
4322
5072
|
sourceBytes: file.sizeBytes,
|
|
@@ -4329,6 +5079,8 @@ function renderGroups(groups, contentFingerprint) {
|
|
|
4329
5079
|
index: index + 1,
|
|
4330
5080
|
filename,
|
|
4331
5081
|
byteSize,
|
|
5082
|
+
estimatedTokens,
|
|
5083
|
+
budgetBytes: budget,
|
|
4332
5084
|
sha256,
|
|
4333
5085
|
lines: countLines(content),
|
|
4334
5086
|
fileCount: group.length,
|
|
@@ -4342,6 +5094,16 @@ function emittedBody(content) {
|
|
|
4342
5094
|
const normalised = content.replace(/\r\n/g, "\n");
|
|
4343
5095
|
return normalised.endsWith("\n") ? normalised.slice(0, -1) : normalised;
|
|
4344
5096
|
}
|
|
5097
|
+
function compactMetadata(file) {
|
|
5098
|
+
const count = (value) => `${value} line${value === 1 ? "" : "s"}`;
|
|
5099
|
+
const lines = file.includedLines === file.originalLines ? count(file.originalLines) : `${count(file.includedLines)} of ${count(file.originalLines)}`;
|
|
5100
|
+
return [
|
|
5101
|
+
`- Meta: ${capMetadata(file.layer)}/${capMetadata(file.kind)}`,
|
|
5102
|
+
capMetadata(file.language),
|
|
5103
|
+
file.inclusionMode,
|
|
5104
|
+
`${lines} (${file.sizeBytes} B)`
|
|
5105
|
+
].join(" | ");
|
|
5106
|
+
}
|
|
4345
5107
|
function renderFileSection(file) {
|
|
4346
5108
|
const content = file.content ?? "";
|
|
4347
5109
|
const body = emittedBody(content);
|
|
@@ -4353,16 +5115,9 @@ function renderFileSection(file) {
|
|
|
4353
5115
|
`<a id="${anchor}"></a>`,
|
|
4354
5116
|
`### FILE: ${displayPath(file.relativePath)}`,
|
|
4355
5117
|
"",
|
|
4356
|
-
|
|
4357
|
-
`- Kind: ${capMetadata(file.kind)}`,
|
|
4358
|
-
`- Language: ${capMetadata(file.language)}`,
|
|
4359
|
-
`- Inclusion: ${file.inclusionMode}`,
|
|
4360
|
-
`- Source Bytes: ${file.sizeBytes}`,
|
|
4361
|
-
`- Source Lines: ${file.originalLines}`,
|
|
4362
|
-
`- Included Lines: ${file.includedLines}`,
|
|
4363
|
-
`- SHA-256: ${file.hashSha256}`,
|
|
5118
|
+
compactMetadata(file),
|
|
4364
5119
|
...file.summaryQuality ? [`- Summary Quality: ${file.summaryQuality}`] : [],
|
|
4365
|
-
...renderSummary(file.summary),
|
|
5120
|
+
...renderSummary(file.summary, file.inclusionMode),
|
|
4366
5121
|
...file.summaryOmitted ? ["- Summary: (omitted: exceeds the part budget)"] : [],
|
|
4367
5122
|
"",
|
|
4368
5123
|
file.inclusionMode === "preview" ? "#### Bounded Preview" : "#### Content",
|
|
@@ -4382,14 +5137,15 @@ function fenceForText(content, minimum) {
|
|
|
4382
5137
|
const maxRun = matches?.reduce((max, run) => Math.max(max, run.length), 0) ?? 0;
|
|
4383
5138
|
return "`".repeat(Math.max(minimum, maxRun + 1));
|
|
4384
5139
|
}
|
|
4385
|
-
function renderSummary(summary) {
|
|
5140
|
+
function renderSummary(summary, inclusionMode) {
|
|
4386
5141
|
if (!summary) return [];
|
|
4387
5142
|
if (typeof summary === "string") return [`- Summary: ${summary}`];
|
|
5143
|
+
if (inclusionMode === "full") return [];
|
|
4388
5144
|
return ["- Summary:", "```json", JSON.stringify(summary, null, 2), "```"];
|
|
4389
5145
|
}
|
|
4390
5146
|
function anchorForPath(filePath) {
|
|
4391
5147
|
const slug = filePath.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
4392
|
-
const digest =
|
|
5148
|
+
const digest = crypto2__namespace.createHash("sha256").update(filePath).digest("hex").slice(0, 8);
|
|
4393
5149
|
return `${slug || "file"}-${digest}`;
|
|
4394
5150
|
}
|
|
4395
5151
|
|
|
@@ -4685,7 +5441,7 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
4685
5441
|
".so"
|
|
4686
5442
|
]);
|
|
4687
5443
|
function isBinaryExtension(filePath) {
|
|
4688
|
-
const ext =
|
|
5444
|
+
const ext = path16__namespace.extname(filePath).toLowerCase();
|
|
4689
5445
|
return BINARY_EXTENSIONS.has(ext);
|
|
4690
5446
|
}
|
|
4691
5447
|
function isBinaryBuffer(buffer) {
|
|
@@ -4855,13 +5611,13 @@ function isBinaryFile(absolutePath) {
|
|
|
4855
5611
|
if (isBinaryExtension(absolutePath)) return true;
|
|
4856
5612
|
let fd;
|
|
4857
5613
|
try {
|
|
4858
|
-
fd =
|
|
5614
|
+
fd = fs14__namespace.openSync(absolutePath, "r");
|
|
4859
5615
|
const buffer = Buffer.alloc(CHUNK);
|
|
4860
5616
|
let sample;
|
|
4861
5617
|
let position = 0;
|
|
4862
5618
|
while (position < BINARY_SCAN_BYTES) {
|
|
4863
5619
|
const wanted = Math.min(CHUNK, BINARY_SCAN_BYTES - position);
|
|
4864
|
-
const bytesRead =
|
|
5620
|
+
const bytesRead = fs14__namespace.readSync(fd, buffer, 0, wanted, position);
|
|
4865
5621
|
if (bytesRead === 0) return false;
|
|
4866
5622
|
const chunk = buffer.subarray(0, bytesRead);
|
|
4867
5623
|
if (sample === void 0) {
|
|
@@ -4879,12 +5635,145 @@ function isBinaryFile(absolutePath) {
|
|
|
4879
5635
|
} finally {
|
|
4880
5636
|
if (fd !== void 0) {
|
|
4881
5637
|
try {
|
|
4882
|
-
|
|
5638
|
+
fs14__namespace.closeSync(fd);
|
|
4883
5639
|
} catch {
|
|
4884
5640
|
}
|
|
4885
5641
|
}
|
|
4886
5642
|
}
|
|
4887
5643
|
}
|
|
5644
|
+
function gitPaths(cwd, args) {
|
|
5645
|
+
let output;
|
|
5646
|
+
try {
|
|
5647
|
+
output = childProcess.execFileSync("git", args, {
|
|
5648
|
+
cwd,
|
|
5649
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
5650
|
+
encoding: "utf8",
|
|
5651
|
+
maxBuffer: 64 * 1024 * 1024
|
|
5652
|
+
});
|
|
5653
|
+
} catch (err) {
|
|
5654
|
+
const stderr = err.stderr ?? "";
|
|
5655
|
+
const reason = stderr.trim() || err.message;
|
|
5656
|
+
throw new Error(
|
|
5657
|
+
`Could not list changed files (git ${args.join(" ")}): ${reason}. A changed-files snapshot needs a git working tree and a ref that exists; run \`codebase-snapshot generate\` without --changed-since to scan everything.`
|
|
5658
|
+
);
|
|
5659
|
+
}
|
|
5660
|
+
return output.split("\0").map((entry) => entry.trim().replace(/\\/g, "/")).filter((entry) => entry.length > 0);
|
|
5661
|
+
}
|
|
5662
|
+
function assertUsableRef(ref) {
|
|
5663
|
+
if (ref.startsWith("-")) {
|
|
5664
|
+
throw new Error(
|
|
5665
|
+
`--changed-since must be a git ref, not an option (received ${JSON.stringify(ref)}). Name a branch, tag, or commit such as \`main\` or \`HEAD~1\`.`
|
|
5666
|
+
);
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
function resolveChangedScope(cwd, request) {
|
|
5670
|
+
const paths = /* @__PURE__ */ new Set();
|
|
5671
|
+
const descriptions = [];
|
|
5672
|
+
if (request.changedSince !== void 0) {
|
|
5673
|
+
assertUsableRef(request.changedSince);
|
|
5674
|
+
for (const path17 of gitPaths(cwd, [
|
|
5675
|
+
"diff",
|
|
5676
|
+
"--name-only",
|
|
5677
|
+
"-z",
|
|
5678
|
+
"--relative",
|
|
5679
|
+
request.changedSince,
|
|
5680
|
+
"--"
|
|
5681
|
+
])) {
|
|
5682
|
+
paths.add(path17);
|
|
5683
|
+
}
|
|
5684
|
+
for (const path17 of gitPaths(cwd, ["ls-files", "--others", "--exclude-standard", "-z", "--"])) {
|
|
5685
|
+
paths.add(path17);
|
|
5686
|
+
}
|
|
5687
|
+
descriptions.push(`changed since ${request.changedSince}`);
|
|
5688
|
+
}
|
|
5689
|
+
if (request.staged === true) {
|
|
5690
|
+
for (const path17 of gitPaths(cwd, [
|
|
5691
|
+
"diff",
|
|
5692
|
+
"--cached",
|
|
5693
|
+
"--name-only",
|
|
5694
|
+
"-z",
|
|
5695
|
+
"--relative",
|
|
5696
|
+
"--"
|
|
5697
|
+
])) {
|
|
5698
|
+
paths.add(path17);
|
|
5699
|
+
}
|
|
5700
|
+
descriptions.push("staged");
|
|
5701
|
+
}
|
|
5702
|
+
return { paths, detail: descriptions.join(" and ") };
|
|
5703
|
+
}
|
|
5704
|
+
var RELATIVE_SPECIFIER = /^\.{1,2}\//;
|
|
5705
|
+
var IMPORT_CANDIDATE_SUFFIXES = [
|
|
5706
|
+
".ts",
|
|
5707
|
+
".tsx",
|
|
5708
|
+
".mts",
|
|
5709
|
+
".cts",
|
|
5710
|
+
".js",
|
|
5711
|
+
".jsx",
|
|
5712
|
+
".mjs",
|
|
5713
|
+
".cjs",
|
|
5714
|
+
"/index.ts",
|
|
5715
|
+
"/index.tsx",
|
|
5716
|
+
"/index.js",
|
|
5717
|
+
"/index.mjs"
|
|
5718
|
+
];
|
|
5719
|
+
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5720
|
+
".ts",
|
|
5721
|
+
".tsx",
|
|
5722
|
+
".mts",
|
|
5723
|
+
".cts",
|
|
5724
|
+
".js",
|
|
5725
|
+
".jsx",
|
|
5726
|
+
".mjs",
|
|
5727
|
+
".cjs"
|
|
5728
|
+
]);
|
|
5729
|
+
function buildImportResolver(files) {
|
|
5730
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
5731
|
+
const byPathFolded = /* @__PURE__ */ new Map();
|
|
5732
|
+
const byStem = /* @__PURE__ */ new Map();
|
|
5733
|
+
const byStemFolded = /* @__PURE__ */ new Map();
|
|
5734
|
+
const remember = (map, key, value) => {
|
|
5735
|
+
if (!map.has(key)) map.set(key, value);
|
|
5736
|
+
};
|
|
5737
|
+
for (const file of files) {
|
|
5738
|
+
const relativePath = file.relativePath;
|
|
5739
|
+
remember(byPath, relativePath, relativePath);
|
|
5740
|
+
remember(byPathFolded, relativePath.toLowerCase(), relativePath);
|
|
5741
|
+
const extension = path16__namespace.posix.extname(relativePath);
|
|
5742
|
+
if (!SOURCE_EXTENSIONS.has(extension)) continue;
|
|
5743
|
+
const stem = relativePath.slice(0, -extension.length);
|
|
5744
|
+
remember(byStem, stem, relativePath);
|
|
5745
|
+
remember(byStemFolded, stem.toLowerCase(), relativePath);
|
|
5746
|
+
if (path16__namespace.posix.basename(stem) === "index") {
|
|
5747
|
+
const directory = path16__namespace.posix.dirname(stem);
|
|
5748
|
+
remember(byStem, directory, relativePath);
|
|
5749
|
+
remember(byStemFolded, directory.toLowerCase(), relativePath);
|
|
5750
|
+
}
|
|
5751
|
+
}
|
|
5752
|
+
return { byPath, byPathFolded, byStem, byStemFolded };
|
|
5753
|
+
}
|
|
5754
|
+
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
5755
|
+
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
5756
|
+
const joined = path16__namespace.posix.normalize(
|
|
5757
|
+
path16__namespace.posix.join(path16__namespace.posix.dirname(fromFile), specifier)
|
|
5758
|
+
);
|
|
5759
|
+
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
5760
|
+
const direct = exact(joined);
|
|
5761
|
+
if (direct) return direct;
|
|
5762
|
+
for (const suffix of IMPORT_CANDIDATE_SUFFIXES) {
|
|
5763
|
+
const hit = exact(`${joined}${suffix}`);
|
|
5764
|
+
if (hit) return hit;
|
|
5765
|
+
}
|
|
5766
|
+
const stem = joined.replace(/\.(?:js|mjs|cjs|jsx)$/, "");
|
|
5767
|
+
const byStem = (key) => resolver.byStem.get(key) ?? resolver.byStemFolded.get(key.toLowerCase()) ?? null;
|
|
5768
|
+
return byStem(joined) ?? byStem(stem);
|
|
5769
|
+
}
|
|
5770
|
+
function importsChangedFile(importerPath, imports, changedPaths, resolver) {
|
|
5771
|
+
for (const specifier of imports) {
|
|
5772
|
+
const resolved = resolveSpecifier(importerPath, specifier, resolver);
|
|
5773
|
+
if (resolved !== null && changedPaths.has(resolved)) return true;
|
|
5774
|
+
}
|
|
5775
|
+
return false;
|
|
5776
|
+
}
|
|
4888
5777
|
var LANGUAGE_BY_EXTENSION = {
|
|
4889
5778
|
".ts": "typescript",
|
|
4890
5779
|
".mts": "typescript",
|
|
@@ -4918,6 +5807,8 @@ var LANGUAGE_BY_EXTENSION = {
|
|
|
4918
5807
|
".java": "java",
|
|
4919
5808
|
".rb": "ruby",
|
|
4920
5809
|
".php": "php",
|
|
5810
|
+
".swift": "swift",
|
|
5811
|
+
".dart": "dart",
|
|
4921
5812
|
".toml": "toml",
|
|
4922
5813
|
".ini": "ini",
|
|
4923
5814
|
".env": "dotenv",
|
|
@@ -4926,7 +5817,8 @@ var LANGUAGE_BY_EXTENSION = {
|
|
|
4926
5817
|
".graphql": "graphql",
|
|
4927
5818
|
".gql": "graphql",
|
|
4928
5819
|
".proto": "protobuf",
|
|
4929
|
-
".tf": "hcl"
|
|
5820
|
+
".tf": "hcl",
|
|
5821
|
+
".tfvars": "hcl"
|
|
4930
5822
|
};
|
|
4931
5823
|
var LANGUAGE_BY_BASENAME = {
|
|
4932
5824
|
Dockerfile: "dockerfile",
|
|
@@ -4939,44 +5831,66 @@ var LANGUAGE_BY_BASENAME = {
|
|
|
4939
5831
|
};
|
|
4940
5832
|
function detectLanguage(filePath) {
|
|
4941
5833
|
const normalized = filePath.replace(/\\/g, "/");
|
|
4942
|
-
const base =
|
|
5834
|
+
const base = path16__namespace.posix.basename(normalized);
|
|
4943
5835
|
if (LANGUAGE_BY_BASENAME[base]) return LANGUAGE_BY_BASENAME[base];
|
|
4944
5836
|
if (base === ".env" || base.startsWith(".env.")) return "dotenv";
|
|
4945
|
-
const ext =
|
|
5837
|
+
const ext = path16__namespace.posix.extname(base).toLowerCase();
|
|
4946
5838
|
return LANGUAGE_BY_EXTENSION[ext] ?? "text";
|
|
4947
5839
|
}
|
|
5840
|
+
var TS_SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
5841
|
+
var isTsSource = (relativePath) => {
|
|
5842
|
+
const lower = relativePath.toLowerCase();
|
|
5843
|
+
return TS_SOURCE_EXTENSIONS.some((extension) => lower.endsWith(extension));
|
|
5844
|
+
};
|
|
5845
|
+
function importsOfText(relativePath, text2) {
|
|
5846
|
+
if (isTsSource(relativePath)) return summarizeTypeScriptSource(relativePath, text2).imports;
|
|
5847
|
+
return summarizeWithProfile(relativePath, text2)?.summary.imports ?? [];
|
|
5848
|
+
}
|
|
5849
|
+
function importsOfFile(relativePath, absolutePath) {
|
|
5850
|
+
let buffer;
|
|
5851
|
+
try {
|
|
5852
|
+
buffer = fs14__namespace.readFileSync(absolutePath);
|
|
5853
|
+
} catch {
|
|
5854
|
+
return [];
|
|
5855
|
+
}
|
|
5856
|
+
const decoded = decodeText(buffer);
|
|
5857
|
+
if (decoded.binary) return [];
|
|
5858
|
+
return importsOfText(relativePath, decoded.text);
|
|
5859
|
+
}
|
|
5860
|
+
|
|
5861
|
+
// src/validator/validate.ts
|
|
4948
5862
|
function maskedPath(value, secretRules) {
|
|
4949
5863
|
return findSecretInPath(value, secretRules)?.masked ?? value;
|
|
4950
5864
|
}
|
|
4951
5865
|
function confineToDirectory(directory, filename) {
|
|
4952
5866
|
if (filename.includes("\0")) return null;
|
|
4953
|
-
if (
|
|
4954
|
-
const base =
|
|
4955
|
-
const resolved =
|
|
5867
|
+
if (path16__namespace.isAbsolute(filename)) return null;
|
|
5868
|
+
const base = path16__namespace.resolve(directory);
|
|
5869
|
+
const resolved = path16__namespace.resolve(base, filename);
|
|
4956
5870
|
const caseInsensitive = process.platform === "win32" || process.platform === "darwin";
|
|
4957
|
-
const prefix = caseInsensitive ? `${base}${
|
|
5871
|
+
const prefix = caseInsensitive ? `${base}${path16__namespace.sep}`.toLowerCase() : `${base}${path16__namespace.sep}`;
|
|
4958
5872
|
const candidate = caseInsensitive ? resolved.toLowerCase() : resolved;
|
|
4959
5873
|
return candidate.startsWith(prefix) ? resolved : null;
|
|
4960
5874
|
}
|
|
4961
5875
|
function findStagingDirectory(config) {
|
|
4962
|
-
const tmpDir =
|
|
5876
|
+
const tmpDir = path16__namespace.resolve(config.cwd, config.tmpDir);
|
|
4963
5877
|
let entries;
|
|
4964
5878
|
try {
|
|
4965
|
-
entries =
|
|
5879
|
+
entries = fs14__namespace.readdirSync(tmpDir);
|
|
4966
5880
|
} catch {
|
|
4967
5881
|
return null;
|
|
4968
5882
|
}
|
|
4969
5883
|
const runs = entries.filter((name) => name.startsWith("run-")).sort();
|
|
4970
5884
|
const newest = runs[runs.length - 1];
|
|
4971
|
-
return newest ?
|
|
5885
|
+
return newest ? path16__namespace.join(tmpDir, newest) : null;
|
|
4972
5886
|
}
|
|
4973
5887
|
function describeMissingManifest(config, manifestPath) {
|
|
4974
|
-
const outDir =
|
|
4975
|
-
const markerPath =
|
|
5888
|
+
const outDir = path16__namespace.resolve(config.cwd, config.outDir);
|
|
5889
|
+
const markerPath = path16__namespace.join(outDir, ".codebase-snapshot");
|
|
4976
5890
|
let marker2 = null;
|
|
4977
5891
|
try {
|
|
4978
|
-
if (
|
|
4979
|
-
const parsed = JSON.parse(
|
|
5892
|
+
if (fs14__namespace.existsSync(markerPath)) {
|
|
5893
|
+
const parsed = JSON.parse(fs14__namespace.readFileSync(markerPath, "utf8"));
|
|
4980
5894
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
4981
5895
|
marker2 = parsed;
|
|
4982
5896
|
}
|
|
@@ -5008,18 +5922,18 @@ function emptyDrift() {
|
|
|
5008
5922
|
};
|
|
5009
5923
|
}
|
|
5010
5924
|
function sha256Hex(buffer) {
|
|
5011
|
-
return
|
|
5925
|
+
return crypto2__namespace.createHash("sha256").update(buffer).digest("hex");
|
|
5012
5926
|
}
|
|
5013
5927
|
var VERIFY_SNIFF_MAX_BYTES = 8 * 1024 * 1024;
|
|
5014
|
-
function wouldNowBeIncluded(cwd, relativePath
|
|
5015
|
-
const absolutePath =
|
|
5928
|
+
function wouldNowBeIncluded(cwd, relativePath) {
|
|
5929
|
+
const absolutePath = path16__namespace.join(cwd, relativePath);
|
|
5016
5930
|
try {
|
|
5017
|
-
const stats =
|
|
5931
|
+
const stats = fs14__namespace.statSync(absolutePath);
|
|
5018
5932
|
if (!stats.isFile()) return false;
|
|
5019
|
-
if (
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
return !
|
|
5933
|
+
if (stats.size <= VERIFY_SNIFF_MAX_BYTES) {
|
|
5934
|
+
return !decodeText(fs14__namespace.readFileSync(absolutePath)).binary;
|
|
5935
|
+
}
|
|
5936
|
+
return !isBinaryFile(absolutePath);
|
|
5023
5937
|
} catch {
|
|
5024
5938
|
return false;
|
|
5025
5939
|
}
|
|
@@ -5077,8 +5991,8 @@ function describeManifestShapeProblem(value) {
|
|
|
5077
5991
|
function validateSnapshot(configInput) {
|
|
5078
5992
|
assertUsableConfigInput(configInput);
|
|
5079
5993
|
const config = isResolvedConfig(configInput) ? normalizeResolvedConfig(configInput) : resolveConfig(configInput);
|
|
5080
|
-
const manifestPath =
|
|
5081
|
-
if (!
|
|
5994
|
+
const manifestPath = path16__namespace.resolve(config.cwd, config.outDir, "manifest.json");
|
|
5995
|
+
if (!fs14__namespace.existsSync(manifestPath)) {
|
|
5082
5996
|
return {
|
|
5083
5997
|
valid: false,
|
|
5084
5998
|
manifestPresent: false,
|
|
@@ -5089,7 +6003,7 @@ function validateSnapshot(configInput) {
|
|
|
5089
6003
|
}
|
|
5090
6004
|
let manifest;
|
|
5091
6005
|
try {
|
|
5092
|
-
manifest = JSON.parse(
|
|
6006
|
+
manifest = JSON.parse(fs14__namespace.readFileSync(manifestPath, "utf8"));
|
|
5093
6007
|
} catch (err) {
|
|
5094
6008
|
return {
|
|
5095
6009
|
valid: false,
|
|
@@ -5111,7 +6025,7 @@ function validateSnapshot(configInput) {
|
|
|
5111
6025
|
}
|
|
5112
6026
|
const drift = emptyDrift();
|
|
5113
6027
|
const warnings = [];
|
|
5114
|
-
const snapshotDir =
|
|
6028
|
+
const snapshotDir = path16__namespace.resolve(config.cwd, config.outDir);
|
|
5115
6029
|
const manifestFileMap = /* @__PURE__ */ new Map();
|
|
5116
6030
|
for (const file of manifest.files) {
|
|
5117
6031
|
if (file && typeof file.relativePath === "string") {
|
|
@@ -5129,23 +6043,72 @@ function validateSnapshot(configInput) {
|
|
|
5129
6043
|
if (direct !== void 0) return direct;
|
|
5130
6044
|
return skippedReasons.get(maskedPath(diskFile, config.secretRules)) ?? null;
|
|
5131
6045
|
};
|
|
6046
|
+
let unchangedScope = null;
|
|
6047
|
+
const recordedChangedSince = manifest.config?.changedSince;
|
|
6048
|
+
const recordedStaged = manifest.config?.staged === true;
|
|
6049
|
+
const recordedWithDependents = manifest.config?.withDependents === true;
|
|
6050
|
+
if (recordedChangedSince !== void 0 || recordedStaged) {
|
|
6051
|
+
try {
|
|
6052
|
+
unchangedScope = new Set(
|
|
6053
|
+
resolveChangedScope(config.cwd, {
|
|
6054
|
+
...recordedChangedSince === void 0 ? {} : { changedSince: recordedChangedSince },
|
|
6055
|
+
...recordedStaged ? { staged: true } : {}
|
|
6056
|
+
}).paths
|
|
6057
|
+
);
|
|
6058
|
+
} catch {
|
|
6059
|
+
warnings.push(
|
|
6060
|
+
"The snapshot was generated with a changed-files scope, but that scope could not be re-resolved; files it recorded as `unchanged` were trusted rather than re-checked."
|
|
6061
|
+
);
|
|
6062
|
+
}
|
|
6063
|
+
}
|
|
6064
|
+
let dependentResolver = null;
|
|
6065
|
+
function becameDependent(diskFile) {
|
|
6066
|
+
if (!recordedWithDependents || unchangedScope === null) return false;
|
|
6067
|
+
dependentResolver ??= buildImportResolver(
|
|
6068
|
+
collection.files.map((file) => ({ relativePath: file }))
|
|
6069
|
+
);
|
|
6070
|
+
return importsChangedFile(
|
|
6071
|
+
diskFile,
|
|
6072
|
+
importsOfFile(diskFile, path16__namespace.join(config.cwd, diskFile)),
|
|
6073
|
+
unchangedScope,
|
|
6074
|
+
dependentResolver
|
|
6075
|
+
);
|
|
6076
|
+
}
|
|
5132
6077
|
const collection = collectFiles(config.cwd, {
|
|
5133
6078
|
includePatterns: config.include,
|
|
5134
6079
|
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
5135
6080
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
5136
|
-
secretRules: config.secretRules
|
|
6081
|
+
secretRules: config.secretRules,
|
|
6082
|
+
// Passed explicitly, like `generate` does. Relying on a process-wide "current
|
|
6083
|
+
// discovery mode" meant `validate` could enumerate files one way while the snapshot
|
|
6084
|
+
// had been generated another way, and then blame the difference on drift.
|
|
6085
|
+
discovery: config.discovery,
|
|
6086
|
+
// The same exclusion `generate` applies: without it, `validate` recounted the tool's
|
|
6087
|
+
// own output directory as a set of files missing from the snapshot, so a correct
|
|
6088
|
+
// snapshot was reported as drifting.
|
|
6089
|
+
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
6090
|
+
(directory) => path16__namespace.relative(config.cwd, path16__namespace.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
6091
|
+
).filter((relative8) => relative8.length > 0 && !relative8.startsWith(".."))
|
|
5137
6092
|
});
|
|
5138
6093
|
for (const diskFile of collection.files) {
|
|
5139
6094
|
const snapshotHash = manifestFileMap.get(diskFile);
|
|
5140
6095
|
if (!snapshotHash) {
|
|
5141
6096
|
const skipReason = skippedReasonOnDisk(diskFile);
|
|
5142
|
-
if (skipReason
|
|
6097
|
+
if (skipReason === "unchanged") {
|
|
6098
|
+
if (unchangedScope !== null) {
|
|
6099
|
+
if (unchangedScope.has(diskFile) || becameDependent(diskFile)) {
|
|
6100
|
+
drift.missingInSnapshot.push(diskFile);
|
|
6101
|
+
}
|
|
6102
|
+
}
|
|
6103
|
+
continue;
|
|
6104
|
+
}
|
|
6105
|
+
if (skipReason !== null && !wouldNowBeIncluded(config.cwd, diskFile)) continue;
|
|
5143
6106
|
drift.missingInSnapshot.push(diskFile);
|
|
5144
6107
|
continue;
|
|
5145
6108
|
}
|
|
5146
6109
|
let diskHash;
|
|
5147
6110
|
try {
|
|
5148
|
-
diskHash = sha256Hex(
|
|
6111
|
+
diskHash = sha256Hex(fs14__namespace.readFileSync(path16__namespace.join(config.cwd, diskFile)));
|
|
5149
6112
|
} catch (err) {
|
|
5150
6113
|
if (err.code === "ENOENT") {
|
|
5151
6114
|
drift.missingOnDisk.push(diskFile);
|
|
@@ -5169,7 +6132,7 @@ function validateSnapshot(configInput) {
|
|
|
5169
6132
|
}
|
|
5170
6133
|
let statFailure = null;
|
|
5171
6134
|
try {
|
|
5172
|
-
|
|
6135
|
+
fs14__namespace.statSync(confined);
|
|
5173
6136
|
} catch (err) {
|
|
5174
6137
|
statFailure = err;
|
|
5175
6138
|
}
|
|
@@ -5189,7 +6152,7 @@ function validateSnapshot(configInput) {
|
|
|
5189
6152
|
...Object.values(config.budgets.byLayer)
|
|
5190
6153
|
);
|
|
5191
6154
|
const partNames = /* @__PURE__ */ new Set();
|
|
5192
|
-
const partsDir =
|
|
6155
|
+
const partsDir = path16__namespace.join(snapshotDir, "parts");
|
|
5193
6156
|
for (const part of manifest.parts) {
|
|
5194
6157
|
if (!part || typeof part.filename !== "string") continue;
|
|
5195
6158
|
partNames.add(part.filename);
|
|
@@ -5204,7 +6167,7 @@ function validateSnapshot(configInput) {
|
|
|
5204
6167
|
}
|
|
5205
6168
|
let buffer;
|
|
5206
6169
|
try {
|
|
5207
|
-
buffer =
|
|
6170
|
+
buffer = fs14__namespace.readFileSync(partPath);
|
|
5208
6171
|
} catch {
|
|
5209
6172
|
drift.partIssues.push({ part: part.filename, issue: "missing from snapshot/parts" });
|
|
5210
6173
|
continue;
|
|
@@ -5218,10 +6181,11 @@ function validateSnapshot(configInput) {
|
|
|
5218
6181
|
if (sha256Hex(buffer) !== part.sha256) {
|
|
5219
6182
|
drift.partIssues.push({ part: part.filename, issue: "sha256 does not match the manifest" });
|
|
5220
6183
|
}
|
|
5221
|
-
|
|
6184
|
+
const partBudget = typeof part.budgetBytes === "number" ? part.budgetBytes : maxEffectiveBudget;
|
|
6185
|
+
if (buffer.length > partBudget) {
|
|
5222
6186
|
drift.partIssues.push({
|
|
5223
6187
|
part: part.filename,
|
|
5224
|
-
issue: `${buffer.length} B exceeds the effective part budget ${
|
|
6188
|
+
issue: typeof part.budgetBytes === "number" ? `${buffer.length} B exceeds its part budget ${partBudget} B` : `${buffer.length} B exceeds the effective part budget ${partBudget} B`
|
|
5225
6189
|
});
|
|
5226
6190
|
}
|
|
5227
6191
|
}
|
|
@@ -5418,9 +6382,9 @@ function validateSnapshot(configInput) {
|
|
|
5418
6382
|
["full-codebase.md", manifest.artifacts?.fullMarkdown]
|
|
5419
6383
|
]) {
|
|
5420
6384
|
if (!artifact) continue;
|
|
5421
|
-
const artifactPath =
|
|
6385
|
+
const artifactPath = path16__namespace.join(snapshotDir, key);
|
|
5422
6386
|
try {
|
|
5423
|
-
const buffer =
|
|
6387
|
+
const buffer = fs14__namespace.readFileSync(artifactPath);
|
|
5424
6388
|
if (sha256Hex(buffer) !== artifact.sha256 || buffer.length !== artifact.byteSize) {
|
|
5425
6389
|
drift.partIssues.push({ part: key, issue: "artifact does not match its recorded digest" });
|
|
5426
6390
|
}
|
|
@@ -5428,6 +6392,18 @@ function validateSnapshot(configInput) {
|
|
|
5428
6392
|
drift.partIssues.push({ part: key, issue: "declared artifact is missing" });
|
|
5429
6393
|
}
|
|
5430
6394
|
}
|
|
6395
|
+
const sarifArtifact = manifest.artifacts?.sarif;
|
|
6396
|
+
if (sarifArtifact) {
|
|
6397
|
+
const sarifKey = "reports/audit.sarif";
|
|
6398
|
+
try {
|
|
6399
|
+
const buffer = fs14__namespace.readFileSync(path16__namespace.join(snapshotDir, "reports", "audit.sarif"));
|
|
6400
|
+
if (sha256Hex(buffer) !== sarifArtifact.sha256 || buffer.length !== sarifArtifact.byteSize) {
|
|
6401
|
+
drift.partIssues.push({ part: sarifKey, issue: "artifact does not match its recorded digest" });
|
|
6402
|
+
}
|
|
6403
|
+
} catch {
|
|
6404
|
+
drift.partIssues.push({ part: sarifKey, issue: "declared artifact is missing" });
|
|
6405
|
+
}
|
|
6406
|
+
}
|
|
5431
6407
|
if (manifest.git && manifest.git.branch && manifest.git.branch !== config.branch) {
|
|
5432
6408
|
warnings.push(
|
|
5433
6409
|
`Snapshot was generated on branch "${manifest.git.branch}" but the configured branch is "${config.branch}".`
|
|
@@ -5490,7 +6466,7 @@ function validateSnapshot(configInput) {
|
|
|
5490
6466
|
|
|
5491
6467
|
// src/version.ts
|
|
5492
6468
|
var GENERATOR_NAME = "codebase-snapshot";
|
|
5493
|
-
var GENERATOR_VERSION = "0.9.
|
|
6469
|
+
var GENERATOR_VERSION = "0.9.12";
|
|
5494
6470
|
var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
|
|
5495
6471
|
function isOurGeneratorName(name) {
|
|
5496
6472
|
if (typeof name !== "string") return false;
|
|
@@ -5537,17 +6513,17 @@ function isNewer(candidate, current) {
|
|
|
5537
6513
|
}
|
|
5538
6514
|
function cacheDirectory(env = process.env, platform = process.platform) {
|
|
5539
6515
|
if (platform === "win32") {
|
|
5540
|
-
const base2 = env.LOCALAPPDATA ??
|
|
5541
|
-
return
|
|
6516
|
+
const base2 = env.LOCALAPPDATA ?? path16__namespace.join(os__namespace.homedir(), "AppData", "Local");
|
|
6517
|
+
return path16__namespace.join(base2, "codebase-snapshot");
|
|
5542
6518
|
}
|
|
5543
6519
|
if (platform === "darwin") {
|
|
5544
|
-
return
|
|
6520
|
+
return path16__namespace.join(os__namespace.homedir(), "Library", "Caches", "codebase-snapshot");
|
|
5545
6521
|
}
|
|
5546
|
-
const base = env.XDG_CACHE_HOME ??
|
|
5547
|
-
return
|
|
6522
|
+
const base = env.XDG_CACHE_HOME ?? path16__namespace.join(os__namespace.homedir(), ".cache");
|
|
6523
|
+
return path16__namespace.join(base, "codebase-snapshot");
|
|
5548
6524
|
}
|
|
5549
6525
|
function cacheFilePath(env = process.env) {
|
|
5550
|
-
return
|
|
6526
|
+
return path16__namespace.join(cacheDirectory(env), "update-check.json");
|
|
5551
6527
|
}
|
|
5552
6528
|
function shouldCheck(argv, env) {
|
|
5553
6529
|
const disabled = ["1", "true", "yes"].includes(
|
|
@@ -5560,13 +6536,23 @@ function shouldCheck(argv, env) {
|
|
|
5560
6536
|
for (const skip of ["mcp", "describe", "skill", "--version", "-v", "--help", "-h"]) {
|
|
5561
6537
|
if (args.includes(skip)) return false;
|
|
5562
6538
|
}
|
|
5563
|
-
|
|
6539
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6540
|
+
const argument = args[index];
|
|
6541
|
+
if (argument === "--format") {
|
|
6542
|
+
if (args[index + 1] === "json") return false;
|
|
6543
|
+
break;
|
|
6544
|
+
}
|
|
6545
|
+
if (argument.startsWith("--format=")) {
|
|
6546
|
+
if (argument.slice("--format=".length) === "json") return false;
|
|
6547
|
+
break;
|
|
6548
|
+
}
|
|
6549
|
+
}
|
|
5564
6550
|
return true;
|
|
5565
6551
|
}
|
|
5566
6552
|
function readCache(file) {
|
|
5567
6553
|
let raw;
|
|
5568
6554
|
try {
|
|
5569
|
-
raw =
|
|
6555
|
+
raw = fs14__namespace.readFileSync(file, "utf8");
|
|
5570
6556
|
} catch {
|
|
5571
6557
|
return null;
|
|
5572
6558
|
}
|
|
@@ -5583,8 +6569,8 @@ function readCache(file) {
|
|
|
5583
6569
|
}
|
|
5584
6570
|
function writeCache(file, entry) {
|
|
5585
6571
|
try {
|
|
5586
|
-
|
|
5587
|
-
|
|
6572
|
+
fs14__namespace.mkdirSync(path16__namespace.dirname(file), { recursive: true });
|
|
6573
|
+
fs14__namespace.writeFileSync(file, `${JSON.stringify(entry)}
|
|
5588
6574
|
`, "utf8");
|
|
5589
6575
|
} catch {
|
|
5590
6576
|
}
|
|
@@ -5678,42 +6664,42 @@ function describeMatcher(matcher) {
|
|
|
5678
6664
|
return String(matcher);
|
|
5679
6665
|
}
|
|
5680
6666
|
function normalizeTarget(cwd, target) {
|
|
5681
|
-
const absolute =
|
|
5682
|
-
const
|
|
5683
|
-
if (
|
|
5684
|
-
return
|
|
6667
|
+
const absolute = path16__namespace.resolve(cwd, target);
|
|
6668
|
+
const relative8 = path16__namespace.relative(cwd, absolute);
|
|
6669
|
+
if (relative8.startsWith("..") || path16__namespace.isAbsolute(relative8)) return target;
|
|
6670
|
+
return relative8.split(path16__namespace.sep).join("/");
|
|
5685
6671
|
}
|
|
5686
6672
|
function findRuleMatch(rules, relativePath) {
|
|
5687
6673
|
return rules.find(
|
|
5688
6674
|
(rule) => matchRule(relativePath, rule.match)
|
|
5689
6675
|
);
|
|
5690
6676
|
}
|
|
5691
|
-
function findRecordedAncestor(skipped,
|
|
6677
|
+
function findRecordedAncestor(skipped, relative8, maskedPath2) {
|
|
5692
6678
|
let nearest;
|
|
5693
6679
|
for (const record of skipped) {
|
|
5694
6680
|
const ancestorPath = record.path;
|
|
5695
6681
|
if (ancestorPath.length === 0) continue;
|
|
5696
|
-
const isAncestor =
|
|
6682
|
+
const isAncestor = relative8.startsWith(`${ancestorPath}/`) || maskedPath2.startsWith(`${ancestorPath}/`);
|
|
5697
6683
|
if (!isAncestor) continue;
|
|
5698
6684
|
if (!nearest || ancestorPath.length > nearest.path.length) nearest = record;
|
|
5699
6685
|
}
|
|
5700
6686
|
return nearest;
|
|
5701
6687
|
}
|
|
5702
6688
|
function readManifest2(config) {
|
|
5703
|
-
const manifestPath =
|
|
6689
|
+
const manifestPath = path16__namespace.resolve(config.cwd, config.outDir, "manifest.json");
|
|
5704
6690
|
try {
|
|
5705
|
-
return JSON.parse(
|
|
6691
|
+
return JSON.parse(fs14__namespace.readFileSync(manifestPath, "utf8"));
|
|
5706
6692
|
} catch {
|
|
5707
6693
|
return null;
|
|
5708
6694
|
}
|
|
5709
6695
|
}
|
|
5710
6696
|
function explainPath(config, target) {
|
|
5711
|
-
const
|
|
5712
|
-
const absolute =
|
|
5713
|
-
const maskedPath2 = findSecretInPath(
|
|
6697
|
+
const relative8 = normalizeTarget(config.cwd, target);
|
|
6698
|
+
const absolute = path16__namespace.join(config.cwd, relative8);
|
|
6699
|
+
const maskedPath2 = findSecretInPath(relative8, config.secretRules)?.masked ?? relative8;
|
|
5714
6700
|
const result = {
|
|
5715
6701
|
path: maskedPath2,
|
|
5716
|
-
exists:
|
|
6702
|
+
exists: fs14__namespace.existsSync(absolute),
|
|
5717
6703
|
included: false
|
|
5718
6704
|
};
|
|
5719
6705
|
const collection = collectFiles(config.cwd, {
|
|
@@ -5721,15 +6707,18 @@ function explainPath(config, target) {
|
|
|
5721
6707
|
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
5722
6708
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
5723
6709
|
secretRules: config.secretRules,
|
|
6710
|
+
// `explain` must enumerate files exactly as `generate` would, or it explains a
|
|
6711
|
+
// decision the generator never made.
|
|
6712
|
+
discovery: config.discovery,
|
|
5724
6713
|
// Same exclusion `generate` applies, so `explain` never says "not-discovered" about a file
|
|
5725
6714
|
// that is only missing because the walker dropped the tool's own output directory.
|
|
5726
6715
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
5727
|
-
(directory) =>
|
|
5728
|
-
).filter((
|
|
6716
|
+
(directory) => path16__namespace.relative(config.cwd, path16__namespace.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
6717
|
+
).filter((relative9) => relative9.length > 0 && !relative9.startsWith(".."))
|
|
5729
6718
|
});
|
|
5730
|
-
if (!collection.files.includes(
|
|
6719
|
+
if (!collection.files.includes(relative8)) {
|
|
5731
6720
|
const record = collection.skipped.find(
|
|
5732
|
-
(entry2) => entry2.path ===
|
|
6721
|
+
(entry2) => entry2.path === relative8 || entry2.path === maskedPath2
|
|
5733
6722
|
);
|
|
5734
6723
|
if (record) {
|
|
5735
6724
|
result.reason = record.reason;
|
|
@@ -5738,7 +6727,7 @@ function explainPath(config, target) {
|
|
|
5738
6727
|
result.reason = "missing";
|
|
5739
6728
|
result.detail = "no such file or directory, relative to the configured cwd";
|
|
5740
6729
|
} else {
|
|
5741
|
-
const ancestor = findRecordedAncestor(collection.skipped,
|
|
6730
|
+
const ancestor = findRecordedAncestor(collection.skipped, relative8, maskedPath2);
|
|
5742
6731
|
if (ancestor) {
|
|
5743
6732
|
result.reason = ancestor.reason;
|
|
5744
6733
|
result.detail = ancestor.detail ? `${ancestor.detail} \u2014 inherited from the skipped directory \`${ancestor.path}\`` : `inside the skipped directory \`${ancestor.path}\``;
|
|
@@ -5750,21 +6739,58 @@ function explainPath(config, target) {
|
|
|
5750
6739
|
return result;
|
|
5751
6740
|
}
|
|
5752
6741
|
result.included = true;
|
|
5753
|
-
if (
|
|
6742
|
+
if (config.changedSince !== void 0 || config.staged === true) {
|
|
6743
|
+
let inScope;
|
|
6744
|
+
let scopeDetail = "";
|
|
6745
|
+
try {
|
|
6746
|
+
const scope = resolveChangedScope(config.cwd, {
|
|
6747
|
+
...config.changedSince === void 0 ? {} : { changedSince: config.changedSince },
|
|
6748
|
+
...config.staged === true ? { staged: true } : {}
|
|
6749
|
+
});
|
|
6750
|
+
inScope = scope.paths.has(relative8);
|
|
6751
|
+
if (!inScope && config.withDependents === true) {
|
|
6752
|
+
inScope = importsChangedFile(
|
|
6753
|
+
relative8,
|
|
6754
|
+
importsOfFile(relative8, absolute),
|
|
6755
|
+
scope.paths,
|
|
6756
|
+
buildImportResolver(collection.files.map((file) => ({ relativePath: file })))
|
|
6757
|
+
);
|
|
6758
|
+
}
|
|
6759
|
+
scopeDetail = scope.detail;
|
|
6760
|
+
} catch (err) {
|
|
6761
|
+
result.included = false;
|
|
6762
|
+
result.reason = "unknown";
|
|
6763
|
+
result.detail = `the changed-files scope could not be resolved: ${err.message}`;
|
|
6764
|
+
return result;
|
|
6765
|
+
}
|
|
6766
|
+
if (!inScope) {
|
|
6767
|
+
result.included = false;
|
|
6768
|
+
result.reason = "unchanged";
|
|
6769
|
+
result.detail = `not ${scopeDetail}; the snapshot was generated with a changed-files scope, which keeps only the files that moved (plus dependents, with --with-dependents)`;
|
|
6770
|
+
return result;
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
6773
|
+
let sizeOnDisk = null;
|
|
6774
|
+
try {
|
|
6775
|
+
sizeOnDisk = fs14__namespace.statSync(absolute).size;
|
|
6776
|
+
} catch {
|
|
6777
|
+
sizeOnDisk = null;
|
|
6778
|
+
}
|
|
6779
|
+
if (sizeOnDisk !== null && sizeOnDisk > BINARY_SCAN_BYTES && isBinaryFile(absolute)) {
|
|
5754
6780
|
result.included = false;
|
|
5755
6781
|
result.reason = "binary";
|
|
5756
6782
|
result.detail = "binary content; generate skips it and records `binary` in manifest.skipped[]";
|
|
5757
6783
|
return result;
|
|
5758
6784
|
}
|
|
5759
|
-
result.layer = classifyLayer(
|
|
5760
|
-
result.kind = classifyKind(
|
|
5761
|
-
result.language = detectLanguage(
|
|
5762
|
-
const layerRule = findRuleMatch(config.layers,
|
|
6785
|
+
result.layer = classifyLayer(relative8, config.layers);
|
|
6786
|
+
result.kind = classifyKind(relative8, config.kinds);
|
|
6787
|
+
result.language = detectLanguage(relative8);
|
|
6788
|
+
const layerRule = findRuleMatch(config.layers, relative8);
|
|
5763
6789
|
if (layerRule) result.layerMatch = describeMatcher(layerRule.match);
|
|
5764
|
-
const kindRule = findRuleMatch(config.kinds,
|
|
6790
|
+
const kindRule = findRuleMatch(config.kinds, relative8);
|
|
5765
6791
|
if (kindRule) result.kindMatch = describeMatcher(kindRule.match);
|
|
5766
|
-
if (
|
|
5767
|
-
const buffer =
|
|
6792
|
+
if (fs14__namespace.existsSync(absolute) && fs14__namespace.statSync(absolute).isFile()) {
|
|
6793
|
+
const buffer = fs14__namespace.readFileSync(absolute);
|
|
5768
6794
|
const decoded = decodeText(buffer);
|
|
5769
6795
|
if (decoded.binary) {
|
|
5770
6796
|
result.included = false;
|
|
@@ -5776,12 +6802,12 @@ function explainPath(config, target) {
|
|
|
5776
6802
|
result.bytes = buffer.length;
|
|
5777
6803
|
result.lines = countLines(text2);
|
|
5778
6804
|
if (decoded.encoding !== "utf-8") result.encoding = decoded.encoding;
|
|
5779
|
-
const { findings } = scanAndRedactSecrets(text2, config.secretRules,
|
|
6805
|
+
const { findings } = scanAndRedactSecrets(text2, config.secretRules, relative8);
|
|
5780
6806
|
result.secretCount = findings.length;
|
|
5781
6807
|
result.secrets = findings.slice(0, 20).map((finding) => ({ rules: finding.rules, line: finding.line }));
|
|
5782
6808
|
}
|
|
5783
6809
|
const manifest = readManifest2(config);
|
|
5784
|
-
const entry = Array.isArray(manifest?.files) ? manifest.files.find((file) => file.relativePath ===
|
|
6810
|
+
const entry = Array.isArray(manifest?.files) ? manifest.files.find((file) => file.relativePath === relative8) : void 0;
|
|
5785
6811
|
result.part = entry?.part ?? null;
|
|
5786
6812
|
result.inclusionMode = entry?.inclusionMode;
|
|
5787
6813
|
return result;
|
|
@@ -6012,14 +7038,14 @@ function compareManifests(before, after) {
|
|
|
6012
7038
|
function loadManifest(target) {
|
|
6013
7039
|
let isDirectory;
|
|
6014
7040
|
try {
|
|
6015
|
-
isDirectory =
|
|
7041
|
+
isDirectory = fs14__namespace.statSync(target).isDirectory();
|
|
6016
7042
|
} catch {
|
|
6017
7043
|
throw new Error(`No snapshot found at ${target}: the path does not exist.`);
|
|
6018
7044
|
}
|
|
6019
|
-
const manifestPath = isDirectory ?
|
|
7045
|
+
const manifestPath = isDirectory ? path16__namespace.join(target, "manifest.json") : target;
|
|
6020
7046
|
let raw;
|
|
6021
7047
|
try {
|
|
6022
|
-
raw =
|
|
7048
|
+
raw = fs14__namespace.readFileSync(manifestPath, "utf8");
|
|
6023
7049
|
} catch {
|
|
6024
7050
|
throw new Error(`Could not read a manifest at ${manifestPath}.`);
|
|
6025
7051
|
}
|
|
@@ -6076,15 +7102,15 @@ function renderDiffText(diff) {
|
|
|
6076
7102
|
function appendGitignoreEntries(cwd, entries) {
|
|
6077
7103
|
const wanted = [...new Set(entries)].filter((entry) => entry.trim().length > 0);
|
|
6078
7104
|
if (wanted.length === 0) return { added: [], note: null };
|
|
6079
|
-
const ignorePath =
|
|
7105
|
+
const ignorePath = path16__namespace.join(cwd, ".gitignore");
|
|
6080
7106
|
const manual = `Add ${wanted.map((entry) => `"${entry}"`).join(" and ")} to your .gitignore yourself.`;
|
|
6081
7107
|
let existing = "";
|
|
6082
7108
|
try {
|
|
6083
|
-
if (
|
|
6084
|
-
if (
|
|
7109
|
+
if (fs14__namespace.existsSync(ignorePath)) {
|
|
7110
|
+
if (fs14__namespace.statSync(ignorePath).isDirectory()) {
|
|
6085
7111
|
return { added: [], note: `${ignorePath} is a directory, so it was not updated. ${manual}` };
|
|
6086
7112
|
}
|
|
6087
|
-
existing =
|
|
7113
|
+
existing = fs14__namespace.readFileSync(ignorePath, "utf8");
|
|
6088
7114
|
}
|
|
6089
7115
|
} catch (err) {
|
|
6090
7116
|
return {
|
|
@@ -6097,7 +7123,7 @@ function appendGitignoreEntries(cwd, entries) {
|
|
|
6097
7123
|
if (added.length === 0) return { added: [], note: null };
|
|
6098
7124
|
const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
6099
7125
|
try {
|
|
6100
|
-
|
|
7126
|
+
fs14__namespace.writeFileSync(ignorePath, `${existing}${prefix}${added.join("\n")}
|
|
6101
7127
|
`, "utf8");
|
|
6102
7128
|
} catch (err) {
|
|
6103
7129
|
return { added: [], note: `${err.message}. ${manual}` };
|
|
@@ -6110,7 +7136,12 @@ var DEFAULT_ANSWERS = {
|
|
|
6110
7136
|
preset: "generic",
|
|
6111
7137
|
branch: "main",
|
|
6112
7138
|
maxPartBytes: "300k",
|
|
7139
|
+
maxPartTokens: null,
|
|
6113
7140
|
largeFileBytes: "200k",
|
|
7141
|
+
maxMonolithBytes: null,
|
|
7142
|
+
discovery: "auto",
|
|
7143
|
+
skeleton: false,
|
|
7144
|
+
sarif: false,
|
|
6114
7145
|
include: [],
|
|
6115
7146
|
excludePatterns: [],
|
|
6116
7147
|
artifacts: {
|
|
@@ -6163,6 +7194,9 @@ function renderConfig(answers) {
|
|
|
6163
7194
|
// \u2500\u2500 Size \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6164
7195
|
// Hard ceiling per part. "300k", 307200 and "1.5mb" are all accepted.
|
|
6165
7196
|
maxPartBytes: ${size(answers.maxPartBytes)},
|
|
7197
|
+
// The same ceiling expressed in tokens; it narrows maxPartBytes rather than
|
|
7198
|
+
// replacing it. null = not set. Minimum 1024.
|
|
7199
|
+
maxPartTokens: ${answers.maxPartTokens === null ? "null" : String(answers.maxPartTokens)},
|
|
6166
7200
|
// Files above this size get a head/tail preview instead of the full body.
|
|
6167
7201
|
largeFileBytes: ${size(answers.largeFileBytes)},
|
|
6168
7202
|
previewHeadLines: 80,
|
|
@@ -6173,6 +7207,22 @@ function renderConfig(answers) {
|
|
|
6173
7207
|
byLayer: {},
|
|
6174
7208
|
},
|
|
6175
7209
|
|
|
7210
|
+
// \u2500\u2500 Scope and compression \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7211
|
+
// Replace function and method bodies with \`{ /* ... */ }\` in TypeScript files.
|
|
7212
|
+
// Signatures and comments stay, so the structure of the file survives.
|
|
7213
|
+
skeleton: ${answers.skeleton},
|
|
7214
|
+
// Also write reports/audit.sarif (SARIF 2.1.0) for a CI security gate. The
|
|
7215
|
+
// matched value is never included \u2014 only the path, the line and the rule.
|
|
7216
|
+
sarif: ${answers.sarif},
|
|
7217
|
+
// Snapshot only the files that differ from a git ref (for example "main"),
|
|
7218
|
+
// plus untracked ones. null and false both mean "snapshot everything".
|
|
7219
|
+
changedSince: null,
|
|
7220
|
+
// Snapshot only what is staged in the index: the pre-commit counterpart.
|
|
7221
|
+
staged: false,
|
|
7222
|
+
// With changedSince or staged, also include the files that import a changed
|
|
7223
|
+
// one, one hop out. Meaningless without a scope.
|
|
7224
|
+
withDependents: false,
|
|
7225
|
+
|
|
6176
7226
|
// \u2500\u2500 What goes in \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6177
7227
|
// Allow-list. Empty means "everything that is not ignored".
|
|
6178
7228
|
// Example: ["src/**", "packages/*/src/**"]
|
|
@@ -6182,7 +7232,7 @@ function renderConfig(answers) {
|
|
|
6182
7232
|
// How candidate files are found: "auto" lists them through git when git can
|
|
6183
7233
|
// and walks the tree otherwise; "git" and "walk" force one path, and "walk" is
|
|
6184
7234
|
// the escape hatch when .git exists but git cannot list it.
|
|
6185
|
-
discovery:
|
|
7235
|
+
discovery: ${JSON.stringify(answers.discovery)},
|
|
6186
7236
|
|
|
6187
7237
|
// \u2500\u2500 Classification \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6188
7238
|
// "merge" = the preset's rules plus yours (yours win). "replace" = yours only.
|
|
@@ -6213,7 +7263,7 @@ function renderConfig(answers) {
|
|
|
6213
7263
|
monolith: ${artifacts.monolith}, // full-codebase.json + .md (roughly doubles output)
|
|
6214
7264
|
},
|
|
6215
7265
|
// Skip the monolith above this size. null = no limit.
|
|
6216
|
-
maxMonolithBytes: null,
|
|
7266
|
+
maxMonolithBytes: ${answers.maxMonolithBytes === null ? "null" : size(answers.maxMonolithBytes)},
|
|
6217
7267
|
// Top-level switch for the monolith, equivalent to artifacts.monolith.
|
|
6218
7268
|
// When both are present the artifact entry wins; this one exists so the
|
|
6219
7269
|
// generated file lists every setting the tool accepts.
|
|
@@ -6590,10 +7640,10 @@ var CONFIG_CANDIDATES = [
|
|
|
6590
7640
|
];
|
|
6591
7641
|
function chooseConfigFileName(cwd) {
|
|
6592
7642
|
if (!supportsTypeScriptImports()) return "snapshot.config.mjs";
|
|
6593
|
-
const pkgPath =
|
|
6594
|
-
if (!
|
|
7643
|
+
const pkgPath = path16__namespace.join(cwd, "package.json");
|
|
7644
|
+
if (!fs14__namespace.existsSync(pkgPath)) return "snapshot.config.mjs";
|
|
6595
7645
|
try {
|
|
6596
|
-
const parsed = JSON.parse(
|
|
7646
|
+
const parsed = JSON.parse(fs14__namespace.readFileSync(pkgPath, "utf8"));
|
|
6597
7647
|
return parsed.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
6598
7648
|
} catch {
|
|
6599
7649
|
return "snapshot.config.mjs";
|
|
@@ -6607,7 +7657,7 @@ var SIZE_CHOICES = [
|
|
|
6607
7657
|
];
|
|
6608
7658
|
function packageJsonHasConfig(cwd) {
|
|
6609
7659
|
try {
|
|
6610
|
-
const parsed = JSON.parse(
|
|
7660
|
+
const parsed = JSON.parse(fs14__namespace.readFileSync(path16__namespace.join(cwd, "package.json"), "utf8"));
|
|
6611
7661
|
return typeof parsed.snapshot === "object" && parsed.snapshot !== null && !Array.isArray(parsed.snapshot);
|
|
6612
7662
|
} catch {
|
|
6613
7663
|
return false;
|
|
@@ -6624,11 +7674,12 @@ function artifactAnswers(options) {
|
|
|
6624
7674
|
const keep = new Set(options.onlyArtifacts);
|
|
6625
7675
|
for (const name of ARTIFACT_NAMES) artifacts[name] = keep.has(name);
|
|
6626
7676
|
}
|
|
7677
|
+
if (options.includeMonolith === true) artifacts.monolith = true;
|
|
6627
7678
|
return artifacts;
|
|
6628
7679
|
}
|
|
6629
7680
|
function topLevelDirs(cwd) {
|
|
6630
7681
|
try {
|
|
6631
|
-
return
|
|
7682
|
+
return fs14__namespace.readdirSync(cwd, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules").map((entry) => entry.name).sort();
|
|
6632
7683
|
} catch {
|
|
6633
7684
|
return [];
|
|
6634
7685
|
}
|
|
@@ -6641,7 +7692,7 @@ function describeJsonValue(value) {
|
|
|
6641
7692
|
}
|
|
6642
7693
|
function writeFileOrExplain(target, content) {
|
|
6643
7694
|
try {
|
|
6644
|
-
|
|
7695
|
+
fs14__namespace.writeFileSync(target, content, "utf8");
|
|
6645
7696
|
} catch (err) {
|
|
6646
7697
|
const code = err.code;
|
|
6647
7698
|
const reason = code === "EACCES" || code === "EPERM" || code === "EROFS" ? `permission denied (${code})` : code === "EISDIR" ? "it is a directory" : `${err.message}`;
|
|
@@ -6652,7 +7703,7 @@ function canWriteModuleConfig(name, cwd) {
|
|
|
6652
7703
|
if (name.endsWith(".mjs") || name.endsWith(".ts")) return true;
|
|
6653
7704
|
if (!name.endsWith(".js")) return false;
|
|
6654
7705
|
try {
|
|
6655
|
-
const parsed = JSON.parse(
|
|
7706
|
+
const parsed = JSON.parse(fs14__namespace.readFileSync(path16__namespace.join(cwd, "package.json"), "utf8"));
|
|
6656
7707
|
return parsed.type === "module";
|
|
6657
7708
|
} catch {
|
|
6658
7709
|
return false;
|
|
@@ -6687,19 +7738,19 @@ function serializePackageJson(raw, value) {
|
|
|
6687
7738
|
return `${text2}${raw.endsWith("\n") ? crlf ? "\r\n" : "\n" : ""}`;
|
|
6688
7739
|
}
|
|
6689
7740
|
function updatePackageJson(cwd, notes) {
|
|
6690
|
-
const packagePath =
|
|
7741
|
+
const packagePath = path16__namespace.join(cwd, "package.json");
|
|
6691
7742
|
const unchanged = {
|
|
6692
7743
|
scripts: [],
|
|
6693
7744
|
dependency: { name: GENERATOR_NAME, range: null, added: false }
|
|
6694
7745
|
};
|
|
6695
|
-
if (!
|
|
7746
|
+
if (!fs14__namespace.existsSync(packagePath)) {
|
|
6696
7747
|
notes.push("No package.json found, so npm scripts and the devDependency were not added.");
|
|
6697
7748
|
return unchanged;
|
|
6698
7749
|
}
|
|
6699
7750
|
let raw;
|
|
6700
7751
|
let parsed;
|
|
6701
7752
|
try {
|
|
6702
|
-
raw =
|
|
7753
|
+
raw = fs14__namespace.readFileSync(packagePath, "utf8");
|
|
6703
7754
|
parsed = JSON.parse(raw);
|
|
6704
7755
|
} catch {
|
|
6705
7756
|
notes.push("Could not parse package.json; npm scripts and the devDependency were not added.");
|
|
@@ -6724,17 +7775,26 @@ function updatePackageJson(cwd, notes) {
|
|
|
6724
7775
|
scriptsWereBroken = true;
|
|
6725
7776
|
}
|
|
6726
7777
|
const added = [];
|
|
7778
|
+
const isSelfRepository = parsed.name === GENERATOR_NAME;
|
|
7779
|
+
const scriptPrefix = isSelfRepository ? "node ./dist/bin.js" : `npx ${GENERATOR_NAME}`;
|
|
6727
7780
|
if (!scripts.snapshot) {
|
|
6728
|
-
scripts.snapshot =
|
|
7781
|
+
scripts.snapshot = `${scriptPrefix} generate`;
|
|
6729
7782
|
added.push("snapshot");
|
|
6730
7783
|
}
|
|
6731
7784
|
if (!scripts["snapshot:validate"]) {
|
|
6732
|
-
scripts["snapshot:validate"] =
|
|
7785
|
+
scripts["snapshot:validate"] = `${scriptPrefix} validate`;
|
|
6733
7786
|
added.push("snapshot:validate");
|
|
6734
7787
|
}
|
|
6735
7788
|
const declared = declaredRange(parsed);
|
|
6736
7789
|
let dependency;
|
|
6737
|
-
if (
|
|
7790
|
+
if (isSelfRepository) {
|
|
7791
|
+
if (declared !== null) {
|
|
7792
|
+
notes.push(
|
|
7793
|
+
`package.json declares ${GENERATOR_NAME} as a dependency of itself. It was left as it is, but a package cannot depend on itself: remove the entry and run the local build instead.`
|
|
7794
|
+
);
|
|
7795
|
+
}
|
|
7796
|
+
dependency = { name: GENERATOR_NAME, range: declared, added: false };
|
|
7797
|
+
} else if (declared !== null) {
|
|
6738
7798
|
dependency = { name: GENERATOR_NAME, range: declared, added: false };
|
|
6739
7799
|
} else {
|
|
6740
7800
|
const range = `^${GENERATOR_VERSION}`;
|
|
@@ -6784,7 +7844,15 @@ async function runInit(options) {
|
|
|
6784
7844
|
// `validate` warn about a mismatch the user never caused.
|
|
6785
7845
|
branch: getGitInfo(options.cwd).branch,
|
|
6786
7846
|
maxPartBytes: options.maxPartBytes ?? detected.recommendedBudget ?? DEFAULT_ANSWERS.maxPartBytes,
|
|
6787
|
-
|
|
7847
|
+
// Every setting the caller named is written down, not just forwarded. A config that omits
|
|
7848
|
+
// what the run used is drift the next `validate` reports, and `setup` is the one command
|
|
7849
|
+
// that is supposed to leave a project in a consistent state.
|
|
7850
|
+
maxPartTokens: options.maxPartTokens ?? DEFAULT_ANSWERS.maxPartTokens,
|
|
7851
|
+
largeFileBytes: options.largeFileBytes ?? DEFAULT_ANSWERS.largeFileBytes,
|
|
7852
|
+
maxMonolithBytes: options.maxMonolithBytes ?? DEFAULT_ANSWERS.maxMonolithBytes,
|
|
7853
|
+
discovery: options.discovery ?? DEFAULT_ANSWERS.discovery,
|
|
7854
|
+
skeleton: options.skeleton ?? DEFAULT_ANSWERS.skeleton,
|
|
7855
|
+
sarif: options.sarif ?? DEFAULT_ANSWERS.sarif,
|
|
6788
7856
|
include: options.include ?? [],
|
|
6789
7857
|
excludePatterns: options.exclude ?? [],
|
|
6790
7858
|
artifacts: artifactAnswers(options),
|
|
@@ -6991,7 +8059,7 @@ async function runInit(options) {
|
|
|
6991
8059
|
const configText = renderConfig(answers);
|
|
6992
8060
|
const preferredName = chooseConfigFileName(options.cwd);
|
|
6993
8061
|
const presentConfigs = CONFIG_CANDIDATES.filter(
|
|
6994
|
-
(name) =>
|
|
8062
|
+
(name) => fs14__namespace.existsSync(path16__namespace.join(options.cwd, name))
|
|
6995
8063
|
);
|
|
6996
8064
|
const present = presentConfigs[0] ?? null;
|
|
6997
8065
|
if (presentConfigs.length > 1) {
|
|
@@ -7000,7 +8068,7 @@ async function runInit(options) {
|
|
|
7000
8068
|
);
|
|
7001
8069
|
}
|
|
7002
8070
|
const configName = present !== null && options.force ? present : preferredName;
|
|
7003
|
-
const configPath =
|
|
8071
|
+
const configPath = path16__namespace.join(options.cwd, configName);
|
|
7004
8072
|
const reused = present !== null && options.force !== true && options.reuseConfig === true;
|
|
7005
8073
|
if (reused) notes.push(`Kept the existing ${present} instead of writing a new one.`);
|
|
7006
8074
|
if (options.dryRun) {
|
|
@@ -7015,7 +8083,8 @@ async function runInit(options) {
|
|
|
7015
8083
|
scripts: [],
|
|
7016
8084
|
gitignoreEntries: [],
|
|
7017
8085
|
dependency: { name: GENERATOR_NAME, range: `^${GENERATOR_VERSION}`, added: false },
|
|
7018
|
-
notes
|
|
8086
|
+
notes,
|
|
8087
|
+
outDir: answers.outDir
|
|
7019
8088
|
};
|
|
7020
8089
|
}
|
|
7021
8090
|
if (present !== null && present !== preferredName && !options.force && !reused) {
|
|
@@ -7033,7 +8102,7 @@ async function runInit(options) {
|
|
|
7033
8102
|
'package.json already defines a "snapshot" config. Remove that key, or re-run with --force to write a config file that takes precedence over it.'
|
|
7034
8103
|
);
|
|
7035
8104
|
}
|
|
7036
|
-
if (
|
|
8105
|
+
if (fs14__namespace.existsSync(configPath) && !options.force && !reused) {
|
|
7037
8106
|
throw new Error(`${configName} already exists. Re-run with --force to replace it.`);
|
|
7038
8107
|
}
|
|
7039
8108
|
if (!reused) writeFileOrExplain(configPath, configText);
|
|
@@ -7051,14 +8120,15 @@ async function runInit(options) {
|
|
|
7051
8120
|
scripts,
|
|
7052
8121
|
gitignoreEntries: ignore.added,
|
|
7053
8122
|
dependency,
|
|
7054
|
-
notes
|
|
8123
|
+
notes,
|
|
8124
|
+
outDir: answers.outDir
|
|
7055
8125
|
};
|
|
7056
8126
|
}
|
|
7057
8127
|
function describeTarget(cwd) {
|
|
7058
|
-
const
|
|
7059
|
-
if (
|
|
7060
|
-
if (
|
|
7061
|
-
return
|
|
8128
|
+
const relative8 = path16__namespace.relative(process.cwd(), cwd);
|
|
8129
|
+
if (relative8 === "") return "this directory";
|
|
8130
|
+
if (relative8.startsWith("..") || path16__namespace.isAbsolute(relative8)) return cwd;
|
|
8131
|
+
return relative8;
|
|
7062
8132
|
}
|
|
7063
8133
|
function firstRunMessage(reason, cwd) {
|
|
7064
8134
|
const where = describeTarget(cwd);
|
|
@@ -7081,7 +8151,7 @@ function planFirstRun(facts) {
|
|
|
7081
8151
|
var NOT_CONTENT = /* @__PURE__ */ new Set([".git", ".hg", ".svn", ".bzr", ".DS_Store", "Thumbs.db"]);
|
|
7082
8152
|
function isEffectivelyEmpty(cwd) {
|
|
7083
8153
|
try {
|
|
7084
|
-
return
|
|
8154
|
+
return fs14__namespace.readdirSync(cwd).every((entry) => NOT_CONTENT.has(entry));
|
|
7085
8155
|
} catch {
|
|
7086
8156
|
return false;
|
|
7087
8157
|
}
|
|
@@ -7089,19 +8159,132 @@ function isEffectivelyEmpty(cwd) {
|
|
|
7089
8159
|
function hasPriorSnapshot(cwd, outDir) {
|
|
7090
8160
|
let parsed;
|
|
7091
8161
|
try {
|
|
7092
|
-
parsed = JSON.parse(
|
|
8162
|
+
parsed = JSON.parse(fs14__namespace.readFileSync(path16__namespace.join(cwd, outDir, ".codebase-snapshot"), "utf8"));
|
|
7093
8163
|
} catch {
|
|
7094
8164
|
return false;
|
|
7095
8165
|
}
|
|
7096
8166
|
if (parsed === null || typeof parsed !== "object") return false;
|
|
7097
8167
|
return isOurGeneratorName(parsed.generator);
|
|
7098
8168
|
}
|
|
8169
|
+
var HOOK_MARKER = `# ${GENERATOR_NAME}: pre-commit snapshot check`;
|
|
8170
|
+
function shellQuote(value) {
|
|
8171
|
+
return value.replace(/[\\"`$]/g, (character) => `\\${character}`);
|
|
8172
|
+
}
|
|
8173
|
+
function hookScript(outDir) {
|
|
8174
|
+
const dir = shellQuote(outDir.replace(/\\/g, "/"));
|
|
8175
|
+
return `#!/bin/sh
|
|
8176
|
+
${HOOK_MARKER}
|
|
8177
|
+
#
|
|
8178
|
+
# Refuse the commit when the snapshot is out of date with the tree.
|
|
8179
|
+
#
|
|
8180
|
+
# Remove this file to stop the check, or edit it \u2014 a hook this tool did not write is never
|
|
8181
|
+
# overwritten, and neither is one it did: run \`${GENERATOR_NAME} init --install-hook\` again
|
|
8182
|
+
# to restore the body below.
|
|
8183
|
+
set -e
|
|
8184
|
+
|
|
8185
|
+
SNAPSHOT_DIR="${dir}"
|
|
8186
|
+
|
|
8187
|
+
if [ ! -f "$SNAPSHOT_DIR/manifest.json" ]; then
|
|
8188
|
+
echo "${GENERATOR_NAME}: no snapshot in $SNAPSHOT_DIR, skipping the staleness check." >&2
|
|
8189
|
+
echo " Run \\"npx ${GENERATOR_NAME} generate\\" to create one." >&2
|
|
8190
|
+
exit 0
|
|
8191
|
+
fi
|
|
8192
|
+
|
|
8193
|
+
if ! npx --no-install ${GENERATOR_NAME} validate >/dev/null 2>&1; then
|
|
8194
|
+
echo "${GENERATOR_NAME}: the snapshot is out of date." >&2
|
|
8195
|
+
echo " Run \\"npx ${GENERATOR_NAME} generate\\", stage the change, and commit again." >&2
|
|
8196
|
+
echo " (npx --no-install is used on purpose: this hook must not install anything)" >&2
|
|
8197
|
+
exit 1
|
|
8198
|
+
fi
|
|
8199
|
+
`;
|
|
8200
|
+
}
|
|
8201
|
+
function gitDirectory(cwd) {
|
|
8202
|
+
const dotGit = path16__namespace.join(cwd, ".git");
|
|
8203
|
+
let stats;
|
|
8204
|
+
try {
|
|
8205
|
+
stats = fs14__namespace.statSync(dotGit);
|
|
8206
|
+
} catch {
|
|
8207
|
+
return null;
|
|
8208
|
+
}
|
|
8209
|
+
if (stats.isDirectory()) return dotGit;
|
|
8210
|
+
if (!stats.isFile()) return null;
|
|
8211
|
+
try {
|
|
8212
|
+
const contents = fs14__namespace.readFileSync(dotGit, "utf8");
|
|
8213
|
+
const match = /^gitdir:\s*(.+)$/m.exec(contents);
|
|
8214
|
+
if (!match) return null;
|
|
8215
|
+
const target = match[1];
|
|
8216
|
+
const resolved = path16__namespace.isAbsolute(target.trim()) ? target.trim() : path16__namespace.join(cwd, target.trim());
|
|
8217
|
+
return fs14__namespace.statSync(resolved).isDirectory() ? resolved : null;
|
|
8218
|
+
} catch {
|
|
8219
|
+
return null;
|
|
8220
|
+
}
|
|
8221
|
+
}
|
|
8222
|
+
function installPreCommitHook(cwd, outDir = "snapshot") {
|
|
8223
|
+
const gitDir = gitDirectory(cwd);
|
|
8224
|
+
if (gitDir === null) {
|
|
8225
|
+
return {
|
|
8226
|
+
path: null,
|
|
8227
|
+
written: false,
|
|
8228
|
+
notes: [
|
|
8229
|
+
"Not a git working tree (no .git directory or gitdir link), so no pre-commit hook was installed."
|
|
8230
|
+
]
|
|
8231
|
+
};
|
|
8232
|
+
}
|
|
8233
|
+
const hooksDir = path16__namespace.join(gitDir, "hooks");
|
|
8234
|
+
const hookPath = path16__namespace.join(hooksDir, "pre-commit");
|
|
8235
|
+
try {
|
|
8236
|
+
fs14__namespace.mkdirSync(hooksDir, { recursive: true });
|
|
8237
|
+
} catch (err) {
|
|
8238
|
+
return {
|
|
8239
|
+
path: hookPath,
|
|
8240
|
+
written: false,
|
|
8241
|
+
notes: [`Could not create ${hooksDir}: ${err.message}`]
|
|
8242
|
+
};
|
|
8243
|
+
}
|
|
8244
|
+
let existing = null;
|
|
8245
|
+
try {
|
|
8246
|
+
existing = fs14__namespace.readFileSync(hookPath, "utf8");
|
|
8247
|
+
} catch {
|
|
8248
|
+
existing = null;
|
|
8249
|
+
}
|
|
8250
|
+
if (existing !== null && !existing.includes(HOOK_MARKER)) {
|
|
8251
|
+
return {
|
|
8252
|
+
path: hookPath,
|
|
8253
|
+
written: false,
|
|
8254
|
+
notes: [
|
|
8255
|
+
`${hookPath} already exists and was not written by ${GENERATOR_NAME}; it was left untouched.`,
|
|
8256
|
+
`Add \`npx ${GENERATOR_NAME} validate\` to it to get the same check.`
|
|
8257
|
+
]
|
|
8258
|
+
};
|
|
8259
|
+
}
|
|
8260
|
+
try {
|
|
8261
|
+
fs14__namespace.writeFileSync(hookPath, hookScript(outDir), "utf8");
|
|
8262
|
+
fs14__namespace.chmodSync(hookPath, 493);
|
|
8263
|
+
} catch (err) {
|
|
8264
|
+
return {
|
|
8265
|
+
path: hookPath,
|
|
8266
|
+
written: false,
|
|
8267
|
+
notes: [`Could not write ${hookPath}: ${err.message}`]
|
|
8268
|
+
};
|
|
8269
|
+
}
|
|
8270
|
+
return {
|
|
8271
|
+
path: hookPath,
|
|
8272
|
+
written: true,
|
|
8273
|
+
notes: [
|
|
8274
|
+
existing === null ? `Installed ${hookPath}: commits are refused while the snapshot is out of date.` : `Updated ${hookPath} to the current ${GENERATOR_NAME} check.`
|
|
8275
|
+
]
|
|
8276
|
+
};
|
|
8277
|
+
}
|
|
8278
|
+
function workerEntry(directory, isCjs) {
|
|
8279
|
+
const entry = path16__namespace.join(directory, isCjs ? "index.cjs" : "index.js");
|
|
8280
|
+
return { path: entry, url: url.pathToFileURL(entry).href };
|
|
8281
|
+
}
|
|
7099
8282
|
|
|
7100
8283
|
// src/index.ts
|
|
7101
8284
|
var SCHEMA_VERSION = "3.0.0";
|
|
7102
8285
|
var SUMMARY_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
7103
8286
|
function sha256Hex2(input) {
|
|
7104
|
-
return
|
|
8287
|
+
return crypto2__namespace.createHash("sha256").update(input).digest("hex");
|
|
7105
8288
|
}
|
|
7106
8289
|
var TS_EXTENSIONS = [
|
|
7107
8290
|
".ts",
|
|
@@ -7140,71 +8323,15 @@ function hasStructuralSummary(relativePath) {
|
|
|
7140
8323
|
function bump(counts, key) {
|
|
7141
8324
|
counts[key] = (counts[key] ?? 0) + 1;
|
|
7142
8325
|
}
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
".js",
|
|
7150
|
-
".jsx",
|
|
7151
|
-
".mjs",
|
|
7152
|
-
".cjs",
|
|
7153
|
-
"/index.ts",
|
|
7154
|
-
"/index.tsx",
|
|
7155
|
-
"/index.js",
|
|
7156
|
-
"/index.mjs"
|
|
7157
|
-
];
|
|
7158
|
-
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
7159
|
-
".ts",
|
|
7160
|
-
".tsx",
|
|
7161
|
-
".mts",
|
|
7162
|
-
".cts",
|
|
7163
|
-
".js",
|
|
7164
|
-
".jsx",
|
|
7165
|
-
".mjs",
|
|
7166
|
-
".cjs"
|
|
7167
|
-
]);
|
|
7168
|
-
function buildImportResolver(files) {
|
|
7169
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
7170
|
-
const byPathFolded = /* @__PURE__ */ new Map();
|
|
7171
|
-
const byStem = /* @__PURE__ */ new Map();
|
|
7172
|
-
const byStemFolded = /* @__PURE__ */ new Map();
|
|
7173
|
-
const remember = (map, key, value) => {
|
|
7174
|
-
if (!map.has(key)) map.set(key, value);
|
|
7175
|
-
};
|
|
7176
|
-
for (const file of files) {
|
|
7177
|
-
const relativePath = file.relativePath;
|
|
7178
|
-
remember(byPath, relativePath, relativePath);
|
|
7179
|
-
remember(byPathFolded, relativePath.toLowerCase(), relativePath);
|
|
7180
|
-
const extension = path13__namespace.posix.extname(relativePath);
|
|
7181
|
-
if (!SOURCE_EXTENSIONS.has(extension)) continue;
|
|
7182
|
-
const stem = relativePath.slice(0, -extension.length);
|
|
7183
|
-
remember(byStem, stem, relativePath);
|
|
7184
|
-
remember(byStemFolded, stem.toLowerCase(), relativePath);
|
|
7185
|
-
if (path13__namespace.posix.basename(stem) === "index") {
|
|
7186
|
-
const directory = path13__namespace.posix.dirname(stem);
|
|
7187
|
-
remember(byStem, directory, relativePath);
|
|
7188
|
-
remember(byStemFolded, directory.toLowerCase(), relativePath);
|
|
8326
|
+
function retain(items, keep) {
|
|
8327
|
+
let write = 0;
|
|
8328
|
+
for (const item of items) {
|
|
8329
|
+
if (keep(item)) {
|
|
8330
|
+
items[write] = item;
|
|
8331
|
+
write += 1;
|
|
7189
8332
|
}
|
|
7190
8333
|
}
|
|
7191
|
-
|
|
7192
|
-
}
|
|
7193
|
-
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
7194
|
-
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
7195
|
-
const joined = path13__namespace.posix.normalize(
|
|
7196
|
-
path13__namespace.posix.join(path13__namespace.posix.dirname(fromFile), specifier)
|
|
7197
|
-
);
|
|
7198
|
-
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
7199
|
-
const direct = exact(joined);
|
|
7200
|
-
if (direct) return direct;
|
|
7201
|
-
for (const suffix of IMPORT_CANDIDATE_SUFFIXES) {
|
|
7202
|
-
const hit = exact(`${joined}${suffix}`);
|
|
7203
|
-
if (hit) return hit;
|
|
7204
|
-
}
|
|
7205
|
-
const stem = joined.replace(/\.(?:js|mjs|cjs|jsx)$/, "");
|
|
7206
|
-
const byStem = (key) => resolver.byStem.get(key) ?? resolver.byStemFolded.get(key.toLowerCase()) ?? null;
|
|
7207
|
-
return byStem(joined) ?? byStem(stem);
|
|
8334
|
+
items.length = write;
|
|
7208
8335
|
}
|
|
7209
8336
|
function buildIndexes(files) {
|
|
7210
8337
|
const resolver = buildImportResolver(files);
|
|
@@ -7256,7 +8383,7 @@ function buildIndexes(files) {
|
|
|
7256
8383
|
}))
|
|
7257
8384
|
};
|
|
7258
8385
|
}
|
|
7259
|
-
function verifyInMemory(files, parts
|
|
8386
|
+
function verifyInMemory(files, parts) {
|
|
7260
8387
|
const errors = [];
|
|
7261
8388
|
const partNames = new Set(parts.map((part) => part.filename));
|
|
7262
8389
|
for (const file of files) {
|
|
@@ -7272,8 +8399,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
7272
8399
|
if (actualBytes !== part.metadata.byteSize) {
|
|
7273
8400
|
errors.push(`${part.filename} byteSize ${part.metadata.byteSize} != actual ${actualBytes}.`);
|
|
7274
8401
|
}
|
|
7275
|
-
|
|
7276
|
-
|
|
8402
|
+
const partBudget = part.metadata.budgetBytes;
|
|
8403
|
+
if (actualBytes > partBudget) {
|
|
8404
|
+
errors.push(
|
|
8405
|
+
`${part.filename} (${actualBytes} B) exceeds its part budget of ${partBudget} B.`
|
|
8406
|
+
);
|
|
7277
8407
|
}
|
|
7278
8408
|
if (part.metadata.includedFiles.length !== part.metadata.fileCount) {
|
|
7279
8409
|
errors.push(`${part.filename} fileCount does not match its included files.`);
|
|
@@ -7286,11 +8416,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
7286
8416
|
return errors;
|
|
7287
8417
|
}
|
|
7288
8418
|
function resolveSafeOutputDirs(config) {
|
|
7289
|
-
const cwd =
|
|
7290
|
-
const outDir =
|
|
7291
|
-
const tmpDir =
|
|
7292
|
-
const root =
|
|
7293
|
-
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${
|
|
8419
|
+
const cwd = path16__namespace.resolve(config.cwd);
|
|
8420
|
+
const outDir = path16__namespace.resolve(cwd, config.outDir);
|
|
8421
|
+
const tmpDir = path16__namespace.resolve(cwd, config.tmpDir);
|
|
8422
|
+
const root = path16__namespace.parse(cwd).root;
|
|
8423
|
+
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${path16__namespace.sep}`);
|
|
7294
8424
|
const reject = (reason) => {
|
|
7295
8425
|
throw new Error(
|
|
7296
8426
|
`Refusing to generate: ${reason}. outDir=${outDir} tmpDir=${tmpDir} cwd=${cwd}`
|
|
@@ -7346,14 +8476,14 @@ function listEntries(dir) {
|
|
|
7346
8476
|
const walk = (absolute, prefix) => {
|
|
7347
8477
|
let children;
|
|
7348
8478
|
try {
|
|
7349
|
-
children =
|
|
8479
|
+
children = fs14__namespace.readdirSync(absolute, { withFileTypes: true });
|
|
7350
8480
|
} catch {
|
|
7351
8481
|
return;
|
|
7352
8482
|
}
|
|
7353
8483
|
for (const child of children) {
|
|
7354
|
-
const
|
|
7355
|
-
entries.push(
|
|
7356
|
-
if (child.isDirectory()) walk(
|
|
8484
|
+
const relative8 = prefix === "" ? child.name : `${prefix}/${child.name}`;
|
|
8485
|
+
entries.push(relative8);
|
|
8486
|
+
if (child.isDirectory()) walk(path16__namespace.join(absolute, child.name), relative8);
|
|
7357
8487
|
}
|
|
7358
8488
|
};
|
|
7359
8489
|
walk(dir, "");
|
|
@@ -7370,28 +8500,33 @@ function markerPayload(dir, state, id, createdAt) {
|
|
|
7370
8500
|
pid: process.pid,
|
|
7371
8501
|
entries: listEntries(dir)
|
|
7372
8502
|
};
|
|
7373
|
-
const manifestPath =
|
|
7374
|
-
if (
|
|
7375
|
-
marker2.manifestSha256 = sha256Hex2(
|
|
8503
|
+
const manifestPath = path16__namespace.join(dir, "manifest.json");
|
|
8504
|
+
if (fs14__namespace.existsSync(manifestPath)) {
|
|
8505
|
+
marker2.manifestSha256 = sha256Hex2(fs14__namespace.readFileSync(manifestPath));
|
|
7376
8506
|
}
|
|
7377
8507
|
return marker2;
|
|
7378
8508
|
}
|
|
7379
8509
|
function writeOwnershipMarker(dir, marker2) {
|
|
7380
|
-
|
|
7381
|
-
|
|
8510
|
+
fs14__namespace.writeFileSync(
|
|
8511
|
+
path16__namespace.join(dir, OWNERSHIP_MARKER),
|
|
7382
8512
|
`${JSON.stringify(marker2, null, 2)}
|
|
7383
8513
|
`,
|
|
7384
8514
|
"utf8"
|
|
7385
8515
|
);
|
|
7386
8516
|
}
|
|
8517
|
+
function markerBelongsTo(dir, id) {
|
|
8518
|
+
const evidence = readOwnershipMarker(dir);
|
|
8519
|
+
if (evidence.marker === null) return true;
|
|
8520
|
+
return evidence.marker.id === id;
|
|
8521
|
+
}
|
|
7387
8522
|
function readOwnershipMarker(dir) {
|
|
7388
|
-
const markerPath =
|
|
7389
|
-
if (!
|
|
8523
|
+
const markerPath = path16__namespace.join(dir, OWNERSHIP_MARKER);
|
|
8524
|
+
if (!fs14__namespace.existsSync(markerPath)) {
|
|
7390
8525
|
return { marker: null, problem: "it holds no .codebase-snapshot marker" };
|
|
7391
8526
|
}
|
|
7392
8527
|
let raw;
|
|
7393
8528
|
try {
|
|
7394
|
-
raw =
|
|
8529
|
+
raw = fs14__namespace.readFileSync(markerPath, "utf8");
|
|
7395
8530
|
} catch {
|
|
7396
8531
|
return { marker: null, problem: "its .codebase-snapshot marker could not be read" };
|
|
7397
8532
|
}
|
|
@@ -7459,11 +8594,11 @@ function assessSnapshotDirectory(dir) {
|
|
|
7459
8594
|
marker: marker2
|
|
7460
8595
|
};
|
|
7461
8596
|
}
|
|
7462
|
-
const manifestPath =
|
|
7463
|
-
if (
|
|
8597
|
+
const manifestPath = path16__namespace.join(dir, "manifest.json");
|
|
8598
|
+
if (fs14__namespace.existsSync(manifestPath)) {
|
|
7464
8599
|
let raw;
|
|
7465
8600
|
try {
|
|
7466
|
-
raw =
|
|
8601
|
+
raw = fs14__namespace.readFileSync(manifestPath, "utf8");
|
|
7467
8602
|
} catch {
|
|
7468
8603
|
return {
|
|
7469
8604
|
owned: false,
|
|
@@ -7545,13 +8680,13 @@ function containsNoFiles(dir) {
|
|
|
7545
8680
|
const current = pending.pop();
|
|
7546
8681
|
let entries;
|
|
7547
8682
|
try {
|
|
7548
|
-
entries =
|
|
8683
|
+
entries = fs14__namespace.readdirSync(current, { withFileTypes: true });
|
|
7549
8684
|
} catch {
|
|
7550
8685
|
return false;
|
|
7551
8686
|
}
|
|
7552
8687
|
for (const entry of entries) {
|
|
7553
8688
|
if (entry.isDirectory()) {
|
|
7554
|
-
pending.push(
|
|
8689
|
+
pending.push(path16__namespace.join(current, entry.name));
|
|
7555
8690
|
} else if (!IGNORABLE_ENTRIES.has(entry.name)) {
|
|
7556
8691
|
return false;
|
|
7557
8692
|
}
|
|
@@ -7560,13 +8695,13 @@ function containsNoFiles(dir) {
|
|
|
7560
8695
|
return true;
|
|
7561
8696
|
}
|
|
7562
8697
|
function assertReplaceable(target, label, secretRules) {
|
|
7563
|
-
if (!
|
|
7564
|
-
if (!
|
|
8698
|
+
if (!fs14__namespace.existsSync(target)) return;
|
|
8699
|
+
if (!fs14__namespace.statSync(target).isDirectory()) {
|
|
7565
8700
|
throw new Error(
|
|
7566
8701
|
`Refusing to write into ${label} ${target}: it exists but is not a directory.`
|
|
7567
8702
|
);
|
|
7568
8703
|
}
|
|
7569
|
-
const entries =
|
|
8704
|
+
const entries = fs14__namespace.readdirSync(target);
|
|
7570
8705
|
if (entries.length === 0) return;
|
|
7571
8706
|
if (entries.every((name) => IGNORABLE_ENTRIES.has(name))) return;
|
|
7572
8707
|
if (containsNoFiles(target)) return;
|
|
@@ -7586,18 +8721,18 @@ function assertReplaceable(target, label, secretRules) {
|
|
|
7586
8721
|
);
|
|
7587
8722
|
}
|
|
7588
8723
|
function assertStagingRootUsable(tmpDir, secretRules) {
|
|
7589
|
-
if (!
|
|
7590
|
-
if (!
|
|
8724
|
+
if (!fs14__namespace.existsSync(tmpDir)) return;
|
|
8725
|
+
if (!fs14__namespace.statSync(tmpDir).isDirectory()) {
|
|
7591
8726
|
throw new Error(`Refusing to write into tmpDir ${tmpDir}: it exists but is not a directory.`);
|
|
7592
8727
|
}
|
|
7593
|
-
const entries =
|
|
8728
|
+
const entries = fs14__namespace.readdirSync(tmpDir);
|
|
7594
8729
|
if (entries.length === 0) return;
|
|
7595
8730
|
const foreign = entries.filter((name) => {
|
|
7596
|
-
const full =
|
|
8731
|
+
const full = path16__namespace.join(tmpDir, name);
|
|
7597
8732
|
if (!name.startsWith(RUN_DIR_PREFIX)) return true;
|
|
7598
8733
|
let stats;
|
|
7599
8734
|
try {
|
|
7600
|
-
stats =
|
|
8735
|
+
stats = fs14__namespace.statSync(full);
|
|
7601
8736
|
} catch {
|
|
7602
8737
|
return true;
|
|
7603
8738
|
}
|
|
@@ -7605,7 +8740,7 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7605
8740
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7606
8741
|
if (marker2) return false;
|
|
7607
8742
|
try {
|
|
7608
|
-
return
|
|
8743
|
+
return fs14__namespace.readdirSync(full).length > 0;
|
|
7609
8744
|
} catch {
|
|
7610
8745
|
return true;
|
|
7611
8746
|
}
|
|
@@ -7620,13 +8755,13 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7620
8755
|
function cleanStaleRunDirs(tmpDir) {
|
|
7621
8756
|
let entries;
|
|
7622
8757
|
try {
|
|
7623
|
-
entries =
|
|
8758
|
+
entries = fs14__namespace.readdirSync(tmpDir);
|
|
7624
8759
|
} catch {
|
|
7625
8760
|
return;
|
|
7626
8761
|
}
|
|
7627
8762
|
for (const name of entries) {
|
|
7628
8763
|
if (!name.startsWith(RUN_DIR_PREFIX)) continue;
|
|
7629
|
-
const full =
|
|
8764
|
+
const full = path16__namespace.join(tmpDir, name);
|
|
7630
8765
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7631
8766
|
if (marker2) {
|
|
7632
8767
|
const created = Date.parse(marker2.createdAt);
|
|
@@ -7634,33 +8769,33 @@ function cleanStaleRunDirs(tmpDir) {
|
|
|
7634
8769
|
const abandoned = typeof marker2.pid === "number" && !isProcessAlive(marker2.pid);
|
|
7635
8770
|
if (aged || abandoned) {
|
|
7636
8771
|
try {
|
|
7637
|
-
|
|
8772
|
+
fs14__namespace.rmSync(full, { recursive: true, force: true });
|
|
7638
8773
|
} catch {
|
|
7639
8774
|
}
|
|
7640
8775
|
}
|
|
7641
8776
|
continue;
|
|
7642
8777
|
}
|
|
7643
8778
|
try {
|
|
7644
|
-
if (
|
|
8779
|
+
if (fs14__namespace.readdirSync(full).length === 0) fs14__namespace.rmSync(full, { recursive: true, force: true });
|
|
7645
8780
|
} catch {
|
|
7646
8781
|
}
|
|
7647
8782
|
}
|
|
7648
8783
|
}
|
|
7649
8784
|
function removeIfEmpty(dir) {
|
|
7650
8785
|
try {
|
|
7651
|
-
if (
|
|
8786
|
+
if (fs14__namespace.readdirSync(dir).length === 0) fs14__namespace.rmSync(dir, { recursive: true, force: true });
|
|
7652
8787
|
} catch {
|
|
7653
8788
|
}
|
|
7654
8789
|
}
|
|
7655
8790
|
function ensureParentDirectory(target, label) {
|
|
7656
|
-
const parent =
|
|
7657
|
-
if (
|
|
8791
|
+
const parent = path16__namespace.dirname(target);
|
|
8792
|
+
if (fs14__namespace.existsSync(parent) && !fs14__namespace.statSync(parent).isDirectory()) {
|
|
7658
8793
|
throw new Error(
|
|
7659
8794
|
`Refusing to create ${label} ${target}: ${parent} exists but is not a directory.`
|
|
7660
8795
|
);
|
|
7661
8796
|
}
|
|
7662
8797
|
try {
|
|
7663
|
-
|
|
8798
|
+
fs14__namespace.mkdirSync(parent, { recursive: true });
|
|
7664
8799
|
} catch (err) {
|
|
7665
8800
|
throw new Error(
|
|
7666
8801
|
`Refusing to create ${label} ${target}: its parent ${parent} could not be created (${err.message}).`
|
|
@@ -7668,8 +8803,7 @@ function ensureParentDirectory(target, label) {
|
|
|
7668
8803
|
}
|
|
7669
8804
|
}
|
|
7670
8805
|
function acquireRunLock(stagingRoot) {
|
|
7671
|
-
const
|
|
7672
|
-
const lockPath = path13__namespace.join(os__namespace.tmpdir(), `codebase-snapshot-${key}.lock`);
|
|
8806
|
+
const lockPath = lockPathFor(stagingRoot);
|
|
7673
8807
|
const payload = `${JSON.stringify({
|
|
7674
8808
|
pid: process.pid,
|
|
7675
8809
|
stagingRoot,
|
|
@@ -7678,22 +8812,22 @@ function acquireRunLock(stagingRoot) {
|
|
|
7678
8812
|
`;
|
|
7679
8813
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
7680
8814
|
try {
|
|
7681
|
-
const handle =
|
|
8815
|
+
const handle = fs14__namespace.openSync(lockPath, "wx");
|
|
7682
8816
|
try {
|
|
7683
|
-
|
|
8817
|
+
fs14__namespace.writeSync(handle, payload);
|
|
7684
8818
|
} finally {
|
|
7685
|
-
|
|
8819
|
+
fs14__namespace.closeSync(handle);
|
|
7686
8820
|
}
|
|
7687
8821
|
return () => {
|
|
7688
8822
|
try {
|
|
7689
|
-
|
|
8823
|
+
fs14__namespace.rmSync(lockPath, { force: true });
|
|
7690
8824
|
} catch {
|
|
7691
8825
|
}
|
|
7692
8826
|
};
|
|
7693
8827
|
} catch (err) {
|
|
7694
8828
|
if (err.code !== "EEXIST") throw err;
|
|
7695
8829
|
if (attempt === 0 && lockOwnerIsGone(lockPath)) {
|
|
7696
|
-
|
|
8830
|
+
fs14__namespace.rmSync(lockPath, { force: true });
|
|
7697
8831
|
continue;
|
|
7698
8832
|
}
|
|
7699
8833
|
throw new Error(
|
|
@@ -7706,16 +8840,19 @@ function acquireRunLock(stagingRoot) {
|
|
|
7706
8840
|
function lockKeyFor(stagingRoot) {
|
|
7707
8841
|
let key = stagingRoot;
|
|
7708
8842
|
try {
|
|
7709
|
-
key =
|
|
8843
|
+
key = fs14__namespace.realpathSync(stagingRoot);
|
|
7710
8844
|
} catch {
|
|
7711
8845
|
}
|
|
7712
8846
|
if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
|
|
7713
8847
|
return sha256Hex2(key).slice(0, 16);
|
|
7714
8848
|
}
|
|
8849
|
+
function lockPathFor(stagingRoot) {
|
|
8850
|
+
return path16__namespace.join(os__namespace.tmpdir(), `codebase-snapshot-${lockKeyFor(stagingRoot)}.lock`);
|
|
8851
|
+
}
|
|
7715
8852
|
function lockOwnerIsGone(lockPath) {
|
|
7716
8853
|
let raw;
|
|
7717
8854
|
try {
|
|
7718
|
-
raw =
|
|
8855
|
+
raw = fs14__namespace.readFileSync(lockPath, "utf8");
|
|
7719
8856
|
} catch {
|
|
7720
8857
|
return false;
|
|
7721
8858
|
}
|
|
@@ -7723,9 +8860,9 @@ function lockOwnerIsGone(lockPath) {
|
|
|
7723
8860
|
try {
|
|
7724
8861
|
pid = JSON.parse(raw).pid;
|
|
7725
8862
|
} catch {
|
|
7726
|
-
return
|
|
8863
|
+
return lockFileIsAncient(lockPath);
|
|
7727
8864
|
}
|
|
7728
|
-
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return
|
|
8865
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return lockFileIsAncient(lockPath);
|
|
7729
8866
|
try {
|
|
7730
8867
|
process.kill(pid, 0);
|
|
7731
8868
|
return false;
|
|
@@ -7733,14 +8870,21 @@ function lockOwnerIsGone(lockPath) {
|
|
|
7733
8870
|
return err.code === "ESRCH";
|
|
7734
8871
|
}
|
|
7735
8872
|
}
|
|
8873
|
+
function lockFileIsAncient(lockPath) {
|
|
8874
|
+
try {
|
|
8875
|
+
return Date.now() - fs14__namespace.statSync(lockPath).mtimeMs > STALE_RUN_MS;
|
|
8876
|
+
} catch {
|
|
8877
|
+
return false;
|
|
8878
|
+
}
|
|
8879
|
+
}
|
|
7736
8880
|
function writeManifestAtomically(manifestPath, manifest) {
|
|
7737
8881
|
const tempPath = `${manifestPath}.${process.pid}.tmp`;
|
|
7738
|
-
|
|
8882
|
+
fs14__namespace.writeFileSync(tempPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
7739
8883
|
try {
|
|
7740
|
-
|
|
8884
|
+
fs14__namespace.renameSync(tempPath, manifestPath);
|
|
7741
8885
|
} catch (err) {
|
|
7742
8886
|
try {
|
|
7743
|
-
|
|
8887
|
+
fs14__namespace.rmSync(tempPath, { force: true });
|
|
7744
8888
|
} catch {
|
|
7745
8889
|
}
|
|
7746
8890
|
throw err;
|
|
@@ -7763,31 +8907,31 @@ function swapIntoPlace(stagingDir, outDir) {
|
|
|
7763
8907
|
}
|
|
7764
8908
|
function swapOnce(stagingDir, outDir) {
|
|
7765
8909
|
const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
|
|
7766
|
-
const hadPrevious =
|
|
7767
|
-
if (hadPrevious)
|
|
8910
|
+
const hadPrevious = fs14__namespace.existsSync(outDir);
|
|
8911
|
+
if (hadPrevious) fs14__namespace.renameSync(outDir, backupDir);
|
|
7768
8912
|
try {
|
|
7769
8913
|
try {
|
|
7770
|
-
|
|
8914
|
+
fs14__namespace.renameSync(stagingDir, outDir);
|
|
7771
8915
|
} catch (err) {
|
|
7772
8916
|
if (err.code !== "EXDEV") throw err;
|
|
7773
8917
|
const partialDir = `${outDir}.partial-${process.pid}-${Date.now()}`;
|
|
7774
8918
|
try {
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
8919
|
+
fs14__namespace.cpSync(stagingDir, partialDir, { recursive: true });
|
|
8920
|
+
fs14__namespace.renameSync(partialDir, outDir);
|
|
8921
|
+
fs14__namespace.rmSync(stagingDir, { recursive: true, force: true });
|
|
7778
8922
|
} catch (copyErr) {
|
|
7779
8923
|
try {
|
|
7780
|
-
|
|
8924
|
+
fs14__namespace.rmSync(partialDir, { recursive: true, force: true });
|
|
7781
8925
|
} catch {
|
|
7782
8926
|
}
|
|
7783
8927
|
throw copyErr;
|
|
7784
8928
|
}
|
|
7785
8929
|
}
|
|
7786
|
-
if (hadPrevious)
|
|
8930
|
+
if (hadPrevious) fs14__namespace.rmSync(backupDir, { recursive: true, force: true });
|
|
7787
8931
|
} catch (err) {
|
|
7788
|
-
if (hadPrevious &&
|
|
7789
|
-
if (
|
|
7790
|
-
|
|
8932
|
+
if (hadPrevious && fs14__namespace.existsSync(backupDir)) {
|
|
8933
|
+
if (fs14__namespace.existsSync(outDir)) fs14__namespace.rmSync(outDir, { recursive: true, force: true });
|
|
8934
|
+
fs14__namespace.renameSync(backupDir, outDir);
|
|
7791
8935
|
}
|
|
7792
8936
|
throw err;
|
|
7793
8937
|
}
|
|
@@ -7796,7 +8940,7 @@ function sleepSync(milliseconds) {
|
|
|
7796
8940
|
const buffer = new Int32Array(new SharedArrayBuffer(4));
|
|
7797
8941
|
Atomics.wait(buffer, 0, 0, milliseconds);
|
|
7798
8942
|
}
|
|
7799
|
-
function artifactWarnings(artifacts) {
|
|
8943
|
+
function artifactWarnings(artifacts, sarif) {
|
|
7800
8944
|
const messages = [];
|
|
7801
8945
|
if (!artifacts.manifest) {
|
|
7802
8946
|
messages.push("manifest.json is disabled: `validate` and `report` cannot inspect this snapshot.");
|
|
@@ -7806,7 +8950,7 @@ function artifactWarnings(artifacts) {
|
|
|
7806
8950
|
"parts are disabled but navigation.md is enabled: its links point at part files that were not written."
|
|
7807
8951
|
);
|
|
7808
8952
|
}
|
|
7809
|
-
if (ARTIFACT_NAMES.every((name) => artifacts[name] === false)) {
|
|
8953
|
+
if (!sarif && ARTIFACT_NAMES.every((name) => artifacts[name] === false)) {
|
|
7810
8954
|
messages.push(
|
|
7811
8955
|
"every artifact is disabled: only the .codebase-snapshot ownership marker will be written."
|
|
7812
8956
|
);
|
|
@@ -7878,13 +9022,25 @@ function generateSnapshot(configInput, options) {
|
|
|
7878
9022
|
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
7879
9023
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
7880
9024
|
secretRules: config.secretRules,
|
|
9025
|
+
discovery: config.discovery,
|
|
7881
9026
|
// The output and staging directories this run is about to write, relative to the root. A
|
|
7882
9027
|
// first run cannot have marked them yet, so the name alone keeps a run from recording its own
|
|
7883
9028
|
// output — but only at these exact paths, not at every directory that shares the name.
|
|
7884
9029
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
7885
|
-
(directory) =>
|
|
7886
|
-
).filter((
|
|
9030
|
+
(directory) => path16__namespace.relative(config.cwd, path16__namespace.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
9031
|
+
).filter((relative8) => relative8.length > 0 && !relative8.startsWith(".."))
|
|
7887
9032
|
});
|
|
9033
|
+
const changedScope = config.changedSince !== void 0 || config.staged === true ? resolveChangedScope(config.cwd, {
|
|
9034
|
+
...config.changedSince === void 0 ? {} : { changedSince: config.changedSince },
|
|
9035
|
+
...config.staged === true ? { staged: true } : {}
|
|
9036
|
+
}) : null;
|
|
9037
|
+
const changedPaths = changedScope === null ? null : new Set(changedScope.paths);
|
|
9038
|
+
const unchangedPaths = [];
|
|
9039
|
+
const candidateFiles = changedPaths !== null && config.withDependents !== true ? collection.files.filter((relativePath) => {
|
|
9040
|
+
if (changedPaths.has(relativePath)) return true;
|
|
9041
|
+
unchangedPaths.push(relativePath);
|
|
9042
|
+
return false;
|
|
9043
|
+
}) : collection.files;
|
|
7888
9044
|
const files = [];
|
|
7889
9045
|
const monolithBodies = /* @__PURE__ */ new Map();
|
|
7890
9046
|
const skipped = maskSkipRecords([...collection.skipped], config.secretRules);
|
|
@@ -7895,6 +9051,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7895
9051
|
return files.some((file) => file.isPreviewTruncated && !file.isLarge);
|
|
7896
9052
|
};
|
|
7897
9053
|
const encodingNotes = [];
|
|
9054
|
+
const fileWarnings = [];
|
|
7898
9055
|
const oversizeSummaryPaths = [];
|
|
7899
9056
|
const previewSummaryPaths = [];
|
|
7900
9057
|
const layerCounts = {};
|
|
@@ -7902,13 +9059,13 @@ function generateSnapshot(configInput, options) {
|
|
|
7902
9059
|
let totalLines = 0;
|
|
7903
9060
|
let totalSizeBytes = 0;
|
|
7904
9061
|
let largeFileCount = 0;
|
|
7905
|
-
for (const relativePath of
|
|
7906
|
-
const absolutePath =
|
|
9062
|
+
for (const relativePath of candidateFiles) {
|
|
9063
|
+
const absolutePath = path16__namespace.join(config.cwd, relativePath);
|
|
7907
9064
|
let stats;
|
|
7908
9065
|
try {
|
|
7909
|
-
stats =
|
|
9066
|
+
stats = fs14__namespace.statSync(absolutePath);
|
|
7910
9067
|
if (!stats.isFile()) {
|
|
7911
|
-
const nestedRepository = stats.isDirectory() &&
|
|
9068
|
+
const nestedRepository = stats.isDirectory() && fs14__namespace.existsSync(path16__namespace.join(absolutePath, ".git"));
|
|
7912
9069
|
skipped.push(
|
|
7913
9070
|
nestedRepository ? {
|
|
7914
9071
|
path: relativePath,
|
|
@@ -7922,13 +9079,17 @@ function generateSnapshot(configInput, options) {
|
|
|
7922
9079
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
7923
9080
|
continue;
|
|
7924
9081
|
}
|
|
7925
|
-
if (
|
|
9082
|
+
if (isBinaryExtension(absolutePath)) {
|
|
9083
|
+
skipped.push({ path: relativePath, reason: "binary", detail: `Size: ${stats.size} B` });
|
|
9084
|
+
continue;
|
|
9085
|
+
}
|
|
9086
|
+
if (stats.size > BINARY_SCAN_BYTES && isBinaryFile(absolutePath)) {
|
|
7926
9087
|
skipped.push({ path: relativePath, reason: "binary", detail: `Size: ${stats.size} B` });
|
|
7927
9088
|
continue;
|
|
7928
9089
|
}
|
|
7929
9090
|
let buffer;
|
|
7930
9091
|
try {
|
|
7931
|
-
buffer =
|
|
9092
|
+
buffer = fs14__namespace.readFileSync(absolutePath);
|
|
7932
9093
|
} catch {
|
|
7933
9094
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
7934
9095
|
continue;
|
|
@@ -7983,7 +9144,16 @@ function generateSnapshot(configInput, options) {
|
|
|
7983
9144
|
let finalContent = redacted;
|
|
7984
9145
|
let isPreviewTruncated = false;
|
|
7985
9146
|
let includedLines = lineCount;
|
|
7986
|
-
|
|
9147
|
+
let inclusionMode = "full";
|
|
9148
|
+
if (config.skeleton === true && isSkeletonizable(relativePath)) {
|
|
9149
|
+
const skeleton = skeletonizeTypeScript(redacted, relativePath);
|
|
9150
|
+
if (skeleton.collapsed > 0) {
|
|
9151
|
+
finalContent = skeleton.text;
|
|
9152
|
+
inclusionMode = "signatures";
|
|
9153
|
+
includedLines = countLines(skeleton.text);
|
|
9154
|
+
}
|
|
9155
|
+
}
|
|
9156
|
+
if (isLarge && inclusionMode !== "signatures") {
|
|
7987
9157
|
const preview = truncatePreview(
|
|
7988
9158
|
redacted,
|
|
7989
9159
|
config.previewHeadLines,
|
|
@@ -7992,8 +9162,9 @@ function generateSnapshot(configInput, options) {
|
|
|
7992
9162
|
finalContent = preview.content;
|
|
7993
9163
|
isPreviewTruncated = preview.isTruncated;
|
|
7994
9164
|
includedLines = preview.includedLines;
|
|
9165
|
+
inclusionMode = isPreviewTruncated ? "preview" : "full";
|
|
7995
9166
|
}
|
|
7996
|
-
if (config.includeMonolith && isPreviewTruncated) {
|
|
9167
|
+
if (config.includeMonolith && (isPreviewTruncated || inclusionMode === "signatures")) {
|
|
7997
9168
|
monolithBodies.set(relativePath, redacted);
|
|
7998
9169
|
}
|
|
7999
9170
|
let summary;
|
|
@@ -8008,14 +9179,19 @@ function generateSnapshot(configInput, options) {
|
|
|
8008
9179
|
summary = outcome?.summary ? maskSummaryStrings(outcome.summary, config.secretRules) : void 0;
|
|
8009
9180
|
summaryQuality = outcome?.quality;
|
|
8010
9181
|
if (isSourceSummary(summary) && summary.parseErrors) {
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
9182
|
+
fileWarnings.push({
|
|
9183
|
+
path: relativePath,
|
|
9184
|
+
message: `Parse errors in ${maskPathText(relativePath, config.secretRules)} (${summary.parseErrors}); its summary is incomplete.`
|
|
9185
|
+
});
|
|
8014
9186
|
}
|
|
8015
9187
|
} catch (err) {
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
9188
|
+
fileWarnings.push({
|
|
9189
|
+
path: relativePath,
|
|
9190
|
+
message: `Summary skipped for ${maskPathText(relativePath, config.secretRules)}: ${err.message}`.replace(
|
|
9191
|
+
/\s+/g,
|
|
9192
|
+
" "
|
|
9193
|
+
)
|
|
9194
|
+
});
|
|
8019
9195
|
}
|
|
8020
9196
|
}
|
|
8021
9197
|
files.push({
|
|
@@ -8027,7 +9203,7 @@ function generateSnapshot(configInput, options) {
|
|
|
8027
9203
|
layer: layer2,
|
|
8028
9204
|
kind,
|
|
8029
9205
|
language,
|
|
8030
|
-
inclusionMode
|
|
9206
|
+
inclusionMode,
|
|
8031
9207
|
isLarge,
|
|
8032
9208
|
isPreviewTruncated,
|
|
8033
9209
|
originalLines: lineCount,
|
|
@@ -8039,6 +9215,58 @@ function generateSnapshot(configInput, options) {
|
|
|
8039
9215
|
content: finalContent
|
|
8040
9216
|
});
|
|
8041
9217
|
}
|
|
9218
|
+
if (changedScope !== null && changedPaths !== null && config.withDependents === true) {
|
|
9219
|
+
const keep = new Set(changedPaths);
|
|
9220
|
+
const resolver = buildImportResolver(files);
|
|
9221
|
+
for (const file of files) {
|
|
9222
|
+
if (!isSourceSummary(file.summary)) continue;
|
|
9223
|
+
if (importsChangedFile(file.relativePath, file.summary.imports, changedPaths, resolver)) {
|
|
9224
|
+
keep.add(file.relativePath);
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9227
|
+
for (const file of files) {
|
|
9228
|
+
if (!keep.has(file.relativePath)) unchangedPaths.push(file.relativePath);
|
|
9229
|
+
}
|
|
9230
|
+
const kept = files.filter((file) => keep.has(file.relativePath));
|
|
9231
|
+
files.length = 0;
|
|
9232
|
+
files.push(...kept);
|
|
9233
|
+
totalLines = files.reduce((total, file) => total + file.lines, 0);
|
|
9234
|
+
totalSizeBytes = files.reduce((total, file) => total + file.sizeBytes, 0);
|
|
9235
|
+
largeFileCount = 0;
|
|
9236
|
+
for (const key of Object.keys(layerCounts)) delete layerCounts[key];
|
|
9237
|
+
for (const key of Object.keys(kindCounts)) delete kindCounts[key];
|
|
9238
|
+
for (const file of files) {
|
|
9239
|
+
bump(layerCounts, file.layer);
|
|
9240
|
+
bump(kindCounts, file.kind);
|
|
9241
|
+
if (file.isLarge) largeFileCount += 1;
|
|
9242
|
+
}
|
|
9243
|
+
retain(
|
|
9244
|
+
secretFindings,
|
|
9245
|
+
(finding) => finding.path === void 0 || keep.has(finding.path)
|
|
9246
|
+
);
|
|
9247
|
+
retain(encodingNotes, (note) => keep.has(note.path));
|
|
9248
|
+
retain(oversizeSummaryPaths, (path17) => keep.has(path17));
|
|
9249
|
+
retain(previewSummaryPaths, (path17) => keep.has(path17));
|
|
9250
|
+
retain(fileWarnings, (warning) => keep.has(warning.path));
|
|
9251
|
+
for (const path17 of [...monolithBodies.keys()]) {
|
|
9252
|
+
if (!keep.has(path17)) monolithBodies.delete(path17);
|
|
9253
|
+
}
|
|
9254
|
+
}
|
|
9255
|
+
if (changedScope !== null) {
|
|
9256
|
+
for (const relativePath of unchangedPaths) {
|
|
9257
|
+
skipped.push({
|
|
9258
|
+
path: relativePath,
|
|
9259
|
+
reason: "unchanged",
|
|
9260
|
+
detail: `not ${changedScope.detail}`
|
|
9261
|
+
});
|
|
9262
|
+
}
|
|
9263
|
+
}
|
|
9264
|
+
for (const warning of fileWarnings) warnings.push(warning.message);
|
|
9265
|
+
if (changedScope !== null && files.length === 0) {
|
|
9266
|
+
warnings.push(
|
|
9267
|
+
`No file is ${changedScope.detail}, so the snapshot is empty. Check the ref, or run without --changed-since to scan the whole repository.`
|
|
9268
|
+
);
|
|
9269
|
+
}
|
|
8042
9270
|
const encodingBuckets = /* @__PURE__ */ new Map();
|
|
8043
9271
|
for (const note of encodingNotes) {
|
|
8044
9272
|
const bucket = encodingBuckets.get(note.encoding) ?? { paths: [], invalidBytes: 0 };
|
|
@@ -8089,13 +9317,8 @@ function generateSnapshot(configInput, options) {
|
|
|
8089
9317
|
);
|
|
8090
9318
|
}
|
|
8091
9319
|
const parts = partitionByBudget(files, config, fingerprint);
|
|
8092
|
-
const maxEffectiveBudget = Math.max(
|
|
8093
|
-
config.maxPartBytes,
|
|
8094
|
-
config.budgets.default,
|
|
8095
|
-
...Object.values(config.budgets.byLayer)
|
|
8096
|
-
);
|
|
8097
9320
|
const truncatedPreviewCount = files.filter((file) => file.isPreviewTruncated).length;
|
|
8098
|
-
const validationErrors = verifyInMemory(files, parts
|
|
9321
|
+
const validationErrors = verifyInMemory(files, parts);
|
|
8099
9322
|
const totalContentBytes = files.reduce((total, file) => total + file.includedBytes, 0);
|
|
8100
9323
|
const maskName = (value) => maskPathText(value, config.secretRules);
|
|
8101
9324
|
const manifest = {
|
|
@@ -8115,12 +9338,26 @@ function generateSnapshot(configInput, options) {
|
|
|
8115
9338
|
config: {
|
|
8116
9339
|
digest: configDigest(config),
|
|
8117
9340
|
maxPartBytes: config.maxPartBytes,
|
|
9341
|
+
// The ceiling the user named, in their own unit. `maxPartBytes` above is what the
|
|
9342
|
+
// partitioner enforced; both are recorded so the manifest can explain the gap.
|
|
9343
|
+
...config.maxPartTokens === void 0 ? {} : { maxPartTokens: config.maxPartTokens },
|
|
8118
9344
|
largeFileBytes: config.largeFileBytes,
|
|
8119
9345
|
previewHeadLines: config.previewHeadLines,
|
|
8120
9346
|
previewTailLines: config.previewTailLines,
|
|
8121
9347
|
includeMonolith: config.includeMonolith,
|
|
9348
|
+
// A ceiling that decides whether `full-codebase.*` is written at all, so it belongs
|
|
9349
|
+
// in the record of what the run enforced. Omitting it left `--max-monolith-bytes`
|
|
9350
|
+
// invisible in the manifest that is supposed to describe the run.
|
|
9351
|
+
maxMonolithBytes: config.maxMonolithBytes ?? null,
|
|
8122
9352
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
8123
9353
|
discovery: config.discovery ?? "auto",
|
|
9354
|
+
// A changed-files scope decides which files are in the snapshot, so the manifest that
|
|
9355
|
+
// describes the run has to name it.
|
|
9356
|
+
...config.changedSince === void 0 ? {} : { changedSince: config.changedSince },
|
|
9357
|
+
...config.staged === true ? { staged: true } : {},
|
|
9358
|
+
...config.withDependents === true ? { withDependents: true } : {},
|
|
9359
|
+
...config.skeleton === true ? { skeleton: true } : {},
|
|
9360
|
+
...config.sarif === true ? { sarif: true } : {},
|
|
8124
9361
|
artifacts: { ...config.artifacts },
|
|
8125
9362
|
include: [...config.include].map(maskName),
|
|
8126
9363
|
preset: config.preset,
|
|
@@ -8152,6 +9389,9 @@ function generateSnapshot(configInput, options) {
|
|
|
8152
9389
|
skippedFiles: skipped.length,
|
|
8153
9390
|
totalSourceBytes: totalSizeBytes,
|
|
8154
9391
|
totalContentBytes,
|
|
9392
|
+
// The parts are what a model is handed, so the estimate is over the parts rather than
|
|
9393
|
+
// over the sources: previews and the byte budget both shrink the number.
|
|
9394
|
+
estimatedTokens: parts.reduce((total, part) => total + part.metadata.estimatedTokens, 0),
|
|
8155
9395
|
partCount: parts.length,
|
|
8156
9396
|
warningCount: 0,
|
|
8157
9397
|
redactedLines: 0
|
|
@@ -8190,7 +9430,9 @@ function generateSnapshot(configInput, options) {
|
|
|
8190
9430
|
);
|
|
8191
9431
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
8192
9432
|
}
|
|
8193
|
-
for (const message of artifactWarnings(config.artifacts))
|
|
9433
|
+
for (const message of artifactWarnings(config.artifacts, config.sarif === true)) {
|
|
9434
|
+
manifest.warnings.push(message);
|
|
9435
|
+
}
|
|
8194
9436
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
8195
9437
|
let projectTree = null;
|
|
8196
9438
|
if (config.artifacts.projectTree) {
|
|
@@ -8203,6 +9445,13 @@ function generateSnapshot(configInput, options) {
|
|
|
8203
9445
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
8204
9446
|
}
|
|
8205
9447
|
}
|
|
9448
|
+
const sarifText = config.sarif === true ? generateSarifReport(manifest, secretFindings, config.secretRules, GENERATOR_VERSION) : null;
|
|
9449
|
+
if (sarifText !== null) {
|
|
9450
|
+
manifest.artifacts.sarif = {
|
|
9451
|
+
byteSize: Buffer.byteLength(sarifText, "utf8"),
|
|
9452
|
+
sha256: sha256Hex2(sarifText)
|
|
9453
|
+
};
|
|
9454
|
+
}
|
|
8206
9455
|
let monolithJson = null;
|
|
8207
9456
|
let monolithMarkdown = null;
|
|
8208
9457
|
if (config.includeMonolith) {
|
|
@@ -8253,17 +9502,17 @@ function generateSnapshot(configInput, options) {
|
|
|
8253
9502
|
const lockKeys = Array.from(/* @__PURE__ */ new Set([tmpDir, outDir])).sort();
|
|
8254
9503
|
const releases = lockKeys.map((key) => acquireRunLock(key));
|
|
8255
9504
|
const releaseLock = () => {
|
|
8256
|
-
for (const release of releases.reverse()) release();
|
|
9505
|
+
for (const release of [...releases].reverse()) release();
|
|
8257
9506
|
};
|
|
8258
9507
|
let stagingDir = null;
|
|
8259
9508
|
try {
|
|
8260
9509
|
ensureParentDirectory(outDir, "outDir");
|
|
8261
|
-
|
|
9510
|
+
fs14__namespace.mkdirSync(tmpDir, { recursive: true });
|
|
8262
9511
|
cleanStaleRunDirs(tmpDir);
|
|
8263
|
-
const snapshotId =
|
|
9512
|
+
const snapshotId = crypto2__namespace.randomUUID();
|
|
8264
9513
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8265
|
-
stagingDir =
|
|
8266
|
-
|
|
9514
|
+
stagingDir = path16__namespace.join(tmpDir, `${RUN_DIR_PREFIX}${process.pid}-${snapshotId.slice(0, 8)}`);
|
|
9515
|
+
fs14__namespace.mkdirSync(stagingDir);
|
|
8267
9516
|
writeOwnershipMarker(stagingDir, {
|
|
8268
9517
|
generator: GENERATOR_NAME,
|
|
8269
9518
|
version: GENERATOR_VERSION,
|
|
@@ -8273,41 +9522,44 @@ function generateSnapshot(configInput, options) {
|
|
|
8273
9522
|
id: snapshotId,
|
|
8274
9523
|
pid: process.pid
|
|
8275
9524
|
});
|
|
8276
|
-
const partsDir =
|
|
8277
|
-
const reportsDir =
|
|
8278
|
-
if (artifacts.parts)
|
|
8279
|
-
if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
|
|
8280
|
-
|
|
9525
|
+
const partsDir = path16__namespace.join(stagingDir, "parts");
|
|
9526
|
+
const reportsDir = path16__namespace.join(stagingDir, "reports");
|
|
9527
|
+
if (artifacts.parts) fs14__namespace.mkdirSync(partsDir, { recursive: true });
|
|
9528
|
+
if (artifacts.navigation || artifacts.projectTree || artifacts.audit || config.sarif === true) {
|
|
9529
|
+
fs14__namespace.mkdirSync(reportsDir, { recursive: true });
|
|
8281
9530
|
}
|
|
8282
9531
|
if (artifacts.parts) {
|
|
8283
9532
|
for (const part of parts) {
|
|
8284
|
-
|
|
9533
|
+
fs14__namespace.writeFileSync(path16__namespace.join(partsDir, part.filename), part.markdown, "utf8");
|
|
8285
9534
|
}
|
|
8286
9535
|
}
|
|
8287
9536
|
if (projectTree !== null) {
|
|
8288
|
-
|
|
9537
|
+
fs14__namespace.writeFileSync(path16__namespace.join(reportsDir, "project-tree.md"), projectTree, "utf8");
|
|
8289
9538
|
}
|
|
8290
9539
|
if (artifacts.navigation) {
|
|
8291
|
-
|
|
9540
|
+
fs14__namespace.writeFileSync(path16__namespace.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
|
|
8292
9541
|
}
|
|
8293
9542
|
if (artifacts.audit) {
|
|
8294
|
-
|
|
8295
|
-
|
|
9543
|
+
fs14__namespace.writeFileSync(
|
|
9544
|
+
path16__namespace.join(reportsDir, "audit.md"),
|
|
8296
9545
|
generateAuditReport(manifest, secretFindings, config.secretRules),
|
|
8297
9546
|
"utf8"
|
|
8298
9547
|
);
|
|
8299
9548
|
}
|
|
9549
|
+
if (sarifText !== null) {
|
|
9550
|
+
fs14__namespace.writeFileSync(path16__namespace.join(reportsDir, "audit.sarif"), sarifText, "utf8");
|
|
9551
|
+
}
|
|
8300
9552
|
if (artifacts.readme) {
|
|
8301
|
-
|
|
9553
|
+
fs14__namespace.writeFileSync(path16__namespace.join(stagingDir, "README.md"), generateReadme(manifest, artifacts), "utf8");
|
|
8302
9554
|
}
|
|
8303
9555
|
if (artifacts.manifest) {
|
|
8304
|
-
|
|
9556
|
+
fs14__namespace.writeFileSync(path16__namespace.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
8305
9557
|
}
|
|
8306
9558
|
if (monolithJson !== null) {
|
|
8307
|
-
|
|
9559
|
+
fs14__namespace.writeFileSync(path16__namespace.join(stagingDir, "full-codebase.json"), monolithJson, "utf8");
|
|
8308
9560
|
}
|
|
8309
9561
|
if (monolithMarkdown !== null) {
|
|
8310
|
-
|
|
9562
|
+
fs14__namespace.writeFileSync(path16__namespace.join(stagingDir, "full-codebase.md"), monolithMarkdown, "utf8");
|
|
8311
9563
|
}
|
|
8312
9564
|
writeOwnershipMarker(stagingDir, markerPayload(stagingDir, "complete", snapshotId, createdAt));
|
|
8313
9565
|
swapIntoPlace(stagingDir, outDir);
|
|
@@ -8315,31 +9567,41 @@ function generateSnapshot(configInput, options) {
|
|
|
8315
9567
|
const warningsBeforeHook = manifest.warnings.length;
|
|
8316
9568
|
const pendingHook = runAfterGenerate(config, manifest);
|
|
8317
9569
|
const shipHookState = () => {
|
|
9570
|
+
if (!markerBelongsTo(outDir, snapshotId)) return;
|
|
8318
9571
|
if (artifacts.manifest && manifest.warnings.length !== warningsBeforeHook) {
|
|
8319
|
-
writeManifestAtomically(
|
|
9572
|
+
writeManifestAtomically(path16__namespace.join(outDir, "manifest.json"), manifest);
|
|
8320
9573
|
}
|
|
8321
9574
|
writeOwnershipMarker(outDir, markerPayload(outDir, "complete", snapshotId, createdAt));
|
|
8322
9575
|
};
|
|
8323
9576
|
shipHookState();
|
|
8324
9577
|
if (pendingHook) {
|
|
8325
|
-
pendingHook.
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
9578
|
+
pendingHook.then(
|
|
9579
|
+
() => {
|
|
9580
|
+
try {
|
|
9581
|
+
shipHookState();
|
|
9582
|
+
} catch {
|
|
9583
|
+
}
|
|
9584
|
+
},
|
|
9585
|
+
(err) => {
|
|
9586
|
+
const message = maskPathText(
|
|
9587
|
+
err instanceof Error ? err.message : String(err),
|
|
9588
|
+
config.secretRules
|
|
9589
|
+
);
|
|
9590
|
+
manifest.warnings.push(`hooks.afterGenerate failed: ${message}`);
|
|
9591
|
+
manifest.totals.warningCount = manifest.warnings.length;
|
|
9592
|
+
try {
|
|
9593
|
+
shipHookState();
|
|
9594
|
+
} catch {
|
|
9595
|
+
}
|
|
8335
9596
|
}
|
|
8336
|
-
|
|
9597
|
+
);
|
|
9598
|
+
pendingHooks.set(outDir, pendingHook);
|
|
8337
9599
|
}
|
|
8338
9600
|
return manifest;
|
|
8339
9601
|
} catch (err) {
|
|
8340
9602
|
if (stagingDir !== null) {
|
|
8341
9603
|
try {
|
|
8342
|
-
|
|
9604
|
+
fs14__namespace.rmSync(stagingDir, { recursive: true, force: true });
|
|
8343
9605
|
} catch {
|
|
8344
9606
|
}
|
|
8345
9607
|
}
|
|
@@ -8349,19 +9611,31 @@ function generateSnapshot(configInput, options) {
|
|
|
8349
9611
|
releaseLock();
|
|
8350
9612
|
}
|
|
8351
9613
|
}
|
|
9614
|
+
var pendingHooks = /* @__PURE__ */ new Map();
|
|
8352
9615
|
async function generateSnapshotAsync(configInput, options) {
|
|
8353
|
-
|
|
9616
|
+
const manifest = generateSnapshot(configInput, options);
|
|
9617
|
+
const config = isResolvedConfig(configInput) ? normalizeResolvedConfig(configInput) : resolveConfig(configInput ?? null);
|
|
9618
|
+
const { outDir } = resolveSafeOutputDirs(config);
|
|
9619
|
+
const pending = pendingHooks.get(outDir);
|
|
9620
|
+
if (pending !== void 0) {
|
|
9621
|
+
pendingHooks.delete(outDir);
|
|
9622
|
+
await pending.catch(() => void 0);
|
|
9623
|
+
}
|
|
9624
|
+
return manifest;
|
|
8354
9625
|
}
|
|
8355
9626
|
|
|
8356
9627
|
exports.ARTIFACT_NAMES = ARTIFACT_NAMES;
|
|
8357
9628
|
exports.CHECK_INTERVAL_MS = CHECK_INTERVAL_MS;
|
|
9629
|
+
exports.CONFIG_KEYS = CONFIG_KEYS;
|
|
8358
9630
|
exports.DEFAULT_ANSWERS = DEFAULT_ANSWERS;
|
|
8359
9631
|
exports.DEFAULT_SECRET_PATTERNS = DEFAULT_SECRET_PATTERNS;
|
|
8360
9632
|
exports.FAILURE_INTERVAL_MS = FAILURE_INTERVAL_MS;
|
|
8361
9633
|
exports.GENERATOR_NAME = GENERATOR_NAME;
|
|
8362
9634
|
exports.GENERATOR_VERSION = GENERATOR_VERSION;
|
|
9635
|
+
exports.HOOK_MARKER = HOOK_MARKER;
|
|
8363
9636
|
exports.LANGUAGE_PROFILES = LANGUAGE_PROFILES;
|
|
8364
9637
|
exports.MIN_MAX_PART_BYTES = MIN_MAX_PART_BYTES;
|
|
9638
|
+
exports.MIN_MAX_PART_TOKENS = MIN_MAX_PART_TOKENS;
|
|
8365
9639
|
exports.PACKAGE_LATEST_URL = PACKAGE_LATEST_URL;
|
|
8366
9640
|
exports.PRESETS = PRESETS;
|
|
8367
9641
|
exports.PromptBack = PromptBack;
|
|
@@ -8371,31 +9645,47 @@ exports.SCHEMA_VERSION = SCHEMA_VERSION;
|
|
|
8371
9645
|
exports.anchorForPath = anchorForPath;
|
|
8372
9646
|
exports.assertReplaceable = assertReplaceable;
|
|
8373
9647
|
exports.assertUsableConfigInput = assertUsableConfigInput;
|
|
9648
|
+
exports.buildImportResolver = buildImportResolver;
|
|
9649
|
+
exports.bytesForTokens = bytesForTokens;
|
|
8374
9650
|
exports.cacheDirectory = cacheDirectory;
|
|
8375
9651
|
exports.cacheFilePath = cacheFilePath;
|
|
8376
9652
|
exports.checkForUpdate = checkForUpdate;
|
|
9653
|
+
exports.classifyKind = classifyKind;
|
|
9654
|
+
exports.classifyLayer = classifyLayer;
|
|
8377
9655
|
exports.collectFiles = collectFiles;
|
|
8378
9656
|
exports.compareManifests = compareManifests;
|
|
9657
|
+
exports.configDigest = configDigest;
|
|
8379
9658
|
exports.countInvalidUtf8Bytes = countInvalidUtf8Bytes;
|
|
8380
9659
|
exports.countLines = countLines;
|
|
8381
9660
|
exports.decodeText = decodeText;
|
|
8382
9661
|
exports.defineConfig = defineConfig;
|
|
8383
9662
|
exports.detectLanguage = detectLanguage;
|
|
8384
9663
|
exports.detectPreset = detectPreset;
|
|
9664
|
+
exports.displayPath = displayPath;
|
|
8385
9665
|
exports.displayWidth = displayWidth;
|
|
9666
|
+
exports.enforceBudget = enforceBudget;
|
|
9667
|
+
exports.estimateTokens = estimateTokens;
|
|
8386
9668
|
exports.explainPath = explainPath;
|
|
8387
9669
|
exports.fenceForContent = fenceForContent;
|
|
8388
9670
|
exports.fenceForText = fenceForText;
|
|
8389
9671
|
exports.fetchLatestVersion = fetchLatestVersion;
|
|
9672
|
+
exports.findSecretInPath = findSecretInPath;
|
|
8390
9673
|
exports.firstRunMessage = firstRunMessage;
|
|
9674
|
+
exports.formatTokens = formatTokens;
|
|
8391
9675
|
exports.formatUpdateNotice = formatUpdateNotice;
|
|
8392
9676
|
exports.generateProjectTree = generateProjectTree;
|
|
9677
|
+
exports.generateSarifReport = generateSarifReport;
|
|
8393
9678
|
exports.generateSnapshot = generateSnapshot;
|
|
8394
9679
|
exports.generateSnapshotAsync = generateSnapshotAsync;
|
|
9680
|
+
exports.getGitInfo = getGitInfo;
|
|
8395
9681
|
exports.getPreset = getPreset;
|
|
8396
9682
|
exports.globToRegExp = globToRegExp;
|
|
8397
9683
|
exports.hasPriorSnapshot = hasPriorSnapshot;
|
|
9684
|
+
exports.importsChangedFile = importsChangedFile;
|
|
9685
|
+
exports.importsOfFile = importsOfFile;
|
|
9686
|
+
exports.importsOfText = importsOfText;
|
|
8398
9687
|
exports.installKind = installKind;
|
|
9688
|
+
exports.installPreCommitHook = installPreCommitHook;
|
|
8399
9689
|
exports.isBinaryBuffer = isBinaryBuffer;
|
|
8400
9690
|
exports.isBinaryExtension = isBinaryExtension;
|
|
8401
9691
|
exports.isBinaryFile = isBinaryFile;
|
|
@@ -8404,10 +9694,13 @@ exports.isInteractive = isInteractive;
|
|
|
8404
9694
|
exports.isNewer = isNewer;
|
|
8405
9695
|
exports.isResolvedConfig = isResolvedConfig;
|
|
8406
9696
|
exports.isSensitivePath = isSensitivePath;
|
|
9697
|
+
exports.isSkeletonizable = isSkeletonizable;
|
|
9698
|
+
exports.isTsxFileName = isTsxFileName;
|
|
8407
9699
|
exports.listPresetNames = listPresetNames;
|
|
8408
9700
|
exports.loadConfigFile = loadConfigFile;
|
|
8409
9701
|
exports.loadConfigFileDetailed = loadConfigFileDetailed;
|
|
8410
9702
|
exports.loadManifest = loadManifest;
|
|
9703
|
+
exports.lockPathFor = lockPathFor;
|
|
8411
9704
|
exports.markResolvedConfig = markResolvedConfig;
|
|
8412
9705
|
exports.matchRule = matchRule;
|
|
8413
9706
|
exports.matchesExclude = matchesExclude;
|
|
@@ -8420,11 +9713,14 @@ exports.renderConfig = renderConfig;
|
|
|
8420
9713
|
exports.renderDiffText = renderDiffText;
|
|
8421
9714
|
exports.renderExplainText = renderExplainText;
|
|
8422
9715
|
exports.renumberParts = renumberParts;
|
|
9716
|
+
exports.resolveChangedScope = resolveChangedScope;
|
|
8423
9717
|
exports.resolveConfig = resolveConfig;
|
|
8424
9718
|
exports.resolveSafeOutputDirs = resolveSafeOutputDirs;
|
|
9719
|
+
exports.resolveSpecifier = resolveSpecifier;
|
|
8425
9720
|
exports.runInit = runInit;
|
|
8426
9721
|
exports.scanAndRedactSecrets = scanAndRedactSecrets;
|
|
8427
9722
|
exports.shouldCheck = shouldCheck;
|
|
9723
|
+
exports.skeletonizeTypeScript = skeletonizeTypeScript;
|
|
8428
9724
|
exports.stripJsonComments = stripJsonComments;
|
|
8429
9725
|
exports.summarizeJson = summarizeJson;
|
|
8430
9726
|
exports.summarizeMarkdown = summarizeMarkdown;
|
|
@@ -8435,6 +9731,5 @@ exports.truncatePreview = truncatePreview;
|
|
|
8435
9731
|
exports.truncateToBytes = truncateToBytes;
|
|
8436
9732
|
exports.updateCommand = updateCommand;
|
|
8437
9733
|
exports.validateSnapshot = validateSnapshot;
|
|
9734
|
+
exports.workerEntry = workerEntry;
|
|
8438
9735
|
exports.wrapLines = wrapLines;
|
|
8439
|
-
//# sourceMappingURL=index.cjs.map
|
|
8440
|
-
//# sourceMappingURL=index.cjs.map
|