codebase-snapshot 0.9.8 → 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 +536 -0
- package/README.md +3 -3
- package/dist/bin.js +5262 -3459
- package/dist/{chunk-C24OAT6T.js → chunk-3H45YMBG.js} +1867 -509
- package/dist/{chunk-RQKXZOD7.js → chunk-5OR3WVPW.js} +366 -84
- package/dist/cli.cjs +5264 -3481
- package/dist/cli.d.cts +282 -3
- package/dist/cli.d.ts +282 -3
- package/dist/cli.js +181 -39
- package/dist/generate-worker-D5Xgh27c.d.cts +57 -0
- package/dist/generate-worker-D5Xgh27c.d.ts +57 -0
- package/dist/index.cjs +1820 -506
- package/dist/index.d.cts +310 -5
- package/dist/index.d.ts +310 -5
- 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 +27 -4
- 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;
|
|
@@ -5500,6 +6476,23 @@ var PACKAGE_LATEST_URL = "https://registry.npmjs.org/codebase-snapshot/latest";
|
|
|
5500
6476
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
5501
6477
|
var FAILURE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
5502
6478
|
var REQUEST_TIMEOUT_MS = 1200;
|
|
6479
|
+
function installKind(scriptPath = process.argv[1] ?? "", cwd = process.cwd()) {
|
|
6480
|
+
const normalise = (value) => value.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
6481
|
+
const script = normalise(scriptPath);
|
|
6482
|
+
if (script === "") return "global";
|
|
6483
|
+
const scriptKey = script.toLowerCase();
|
|
6484
|
+
if (scriptKey.includes("/_npx/")) return "npx";
|
|
6485
|
+
const root = normalise(cwd).toLowerCase();
|
|
6486
|
+
if (scriptKey.includes("/node_modules/") && root !== "" && scriptKey.startsWith(`${root}/`)) {
|
|
6487
|
+
return "local";
|
|
6488
|
+
}
|
|
6489
|
+
return "global";
|
|
6490
|
+
}
|
|
6491
|
+
function updateCommand(kind) {
|
|
6492
|
+
if (kind === "npx") return "npx -y codebase-snapshot@latest";
|
|
6493
|
+
if (kind === "local") return "npm install codebase-snapshot@latest";
|
|
6494
|
+
return "npm install -g codebase-snapshot";
|
|
6495
|
+
}
|
|
5503
6496
|
function versionParts(value) {
|
|
5504
6497
|
const core = value.trim().replace(/^v/i, "").split("-")[0];
|
|
5505
6498
|
return core.split(".").map((part) => {
|
|
@@ -5520,17 +6513,17 @@ function isNewer(candidate, current) {
|
|
|
5520
6513
|
}
|
|
5521
6514
|
function cacheDirectory(env = process.env, platform = process.platform) {
|
|
5522
6515
|
if (platform === "win32") {
|
|
5523
|
-
const base2 = env.LOCALAPPDATA ??
|
|
5524
|
-
return
|
|
6516
|
+
const base2 = env.LOCALAPPDATA ?? path16__namespace.join(os__namespace.homedir(), "AppData", "Local");
|
|
6517
|
+
return path16__namespace.join(base2, "codebase-snapshot");
|
|
5525
6518
|
}
|
|
5526
6519
|
if (platform === "darwin") {
|
|
5527
|
-
return
|
|
6520
|
+
return path16__namespace.join(os__namespace.homedir(), "Library", "Caches", "codebase-snapshot");
|
|
5528
6521
|
}
|
|
5529
|
-
const base = env.XDG_CACHE_HOME ??
|
|
5530
|
-
return
|
|
6522
|
+
const base = env.XDG_CACHE_HOME ?? path16__namespace.join(os__namespace.homedir(), ".cache");
|
|
6523
|
+
return path16__namespace.join(base, "codebase-snapshot");
|
|
5531
6524
|
}
|
|
5532
6525
|
function cacheFilePath(env = process.env) {
|
|
5533
|
-
return
|
|
6526
|
+
return path16__namespace.join(cacheDirectory(env), "update-check.json");
|
|
5534
6527
|
}
|
|
5535
6528
|
function shouldCheck(argv, env) {
|
|
5536
6529
|
const disabled = ["1", "true", "yes"].includes(
|
|
@@ -5543,13 +6536,23 @@ function shouldCheck(argv, env) {
|
|
|
5543
6536
|
for (const skip of ["mcp", "describe", "skill", "--version", "-v", "--help", "-h"]) {
|
|
5544
6537
|
if (args.includes(skip)) return false;
|
|
5545
6538
|
}
|
|
5546
|
-
|
|
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
|
+
}
|
|
5547
6550
|
return true;
|
|
5548
6551
|
}
|
|
5549
6552
|
function readCache(file) {
|
|
5550
6553
|
let raw;
|
|
5551
6554
|
try {
|
|
5552
|
-
raw =
|
|
6555
|
+
raw = fs14__namespace.readFileSync(file, "utf8");
|
|
5553
6556
|
} catch {
|
|
5554
6557
|
return null;
|
|
5555
6558
|
}
|
|
@@ -5566,8 +6569,8 @@ function readCache(file) {
|
|
|
5566
6569
|
}
|
|
5567
6570
|
function writeCache(file, entry) {
|
|
5568
6571
|
try {
|
|
5569
|
-
|
|
5570
|
-
|
|
6572
|
+
fs14__namespace.mkdirSync(path16__namespace.dirname(file), { recursive: true });
|
|
6573
|
+
fs14__namespace.writeFileSync(file, `${JSON.stringify(entry)}
|
|
5571
6574
|
`, "utf8");
|
|
5572
6575
|
} catch {
|
|
5573
6576
|
}
|
|
@@ -5648,9 +6651,9 @@ async function checkForUpdate(options) {
|
|
|
5648
6651
|
if (!latest) return null;
|
|
5649
6652
|
return isNewer(latest, options.current) ? { current: options.current, latest } : null;
|
|
5650
6653
|
}
|
|
5651
|
-
function formatUpdateNotice(notice) {
|
|
6654
|
+
function formatUpdateNotice(notice, kind) {
|
|
5652
6655
|
return `\u2191 codebase-snapshot ${notice.latest} is available \u2014 you have ${notice.current}.
|
|
5653
|
-
|
|
6656
|
+
${updateCommand(kind)}`;
|
|
5654
6657
|
}
|
|
5655
6658
|
function describeMatcher(matcher) {
|
|
5656
6659
|
if (typeof matcher === "string") return matcher;
|
|
@@ -5661,42 +6664,42 @@ function describeMatcher(matcher) {
|
|
|
5661
6664
|
return String(matcher);
|
|
5662
6665
|
}
|
|
5663
6666
|
function normalizeTarget(cwd, target) {
|
|
5664
|
-
const absolute =
|
|
5665
|
-
const
|
|
5666
|
-
if (
|
|
5667
|
-
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("/");
|
|
5668
6671
|
}
|
|
5669
6672
|
function findRuleMatch(rules, relativePath) {
|
|
5670
6673
|
return rules.find(
|
|
5671
6674
|
(rule) => matchRule(relativePath, rule.match)
|
|
5672
6675
|
);
|
|
5673
6676
|
}
|
|
5674
|
-
function findRecordedAncestor(skipped,
|
|
6677
|
+
function findRecordedAncestor(skipped, relative8, maskedPath2) {
|
|
5675
6678
|
let nearest;
|
|
5676
6679
|
for (const record of skipped) {
|
|
5677
6680
|
const ancestorPath = record.path;
|
|
5678
6681
|
if (ancestorPath.length === 0) continue;
|
|
5679
|
-
const isAncestor =
|
|
6682
|
+
const isAncestor = relative8.startsWith(`${ancestorPath}/`) || maskedPath2.startsWith(`${ancestorPath}/`);
|
|
5680
6683
|
if (!isAncestor) continue;
|
|
5681
6684
|
if (!nearest || ancestorPath.length > nearest.path.length) nearest = record;
|
|
5682
6685
|
}
|
|
5683
6686
|
return nearest;
|
|
5684
6687
|
}
|
|
5685
6688
|
function readManifest2(config) {
|
|
5686
|
-
const manifestPath =
|
|
6689
|
+
const manifestPath = path16__namespace.resolve(config.cwd, config.outDir, "manifest.json");
|
|
5687
6690
|
try {
|
|
5688
|
-
return JSON.parse(
|
|
6691
|
+
return JSON.parse(fs14__namespace.readFileSync(manifestPath, "utf8"));
|
|
5689
6692
|
} catch {
|
|
5690
6693
|
return null;
|
|
5691
6694
|
}
|
|
5692
6695
|
}
|
|
5693
6696
|
function explainPath(config, target) {
|
|
5694
|
-
const
|
|
5695
|
-
const absolute =
|
|
5696
|
-
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;
|
|
5697
6700
|
const result = {
|
|
5698
6701
|
path: maskedPath2,
|
|
5699
|
-
exists:
|
|
6702
|
+
exists: fs14__namespace.existsSync(absolute),
|
|
5700
6703
|
included: false
|
|
5701
6704
|
};
|
|
5702
6705
|
const collection = collectFiles(config.cwd, {
|
|
@@ -5704,15 +6707,18 @@ function explainPath(config, target) {
|
|
|
5704
6707
|
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
5705
6708
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
5706
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,
|
|
5707
6713
|
// Same exclusion `generate` applies, so `explain` never says "not-discovered" about a file
|
|
5708
6714
|
// that is only missing because the walker dropped the tool's own output directory.
|
|
5709
6715
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
5710
|
-
(directory) =>
|
|
5711
|
-
).filter((
|
|
6716
|
+
(directory) => path16__namespace.relative(config.cwd, path16__namespace.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
6717
|
+
).filter((relative9) => relative9.length > 0 && !relative9.startsWith(".."))
|
|
5712
6718
|
});
|
|
5713
|
-
if (!collection.files.includes(
|
|
6719
|
+
if (!collection.files.includes(relative8)) {
|
|
5714
6720
|
const record = collection.skipped.find(
|
|
5715
|
-
(entry2) => entry2.path ===
|
|
6721
|
+
(entry2) => entry2.path === relative8 || entry2.path === maskedPath2
|
|
5716
6722
|
);
|
|
5717
6723
|
if (record) {
|
|
5718
6724
|
result.reason = record.reason;
|
|
@@ -5721,7 +6727,7 @@ function explainPath(config, target) {
|
|
|
5721
6727
|
result.reason = "missing";
|
|
5722
6728
|
result.detail = "no such file or directory, relative to the configured cwd";
|
|
5723
6729
|
} else {
|
|
5724
|
-
const ancestor = findRecordedAncestor(collection.skipped,
|
|
6730
|
+
const ancestor = findRecordedAncestor(collection.skipped, relative8, maskedPath2);
|
|
5725
6731
|
if (ancestor) {
|
|
5726
6732
|
result.reason = ancestor.reason;
|
|
5727
6733
|
result.detail = ancestor.detail ? `${ancestor.detail} \u2014 inherited from the skipped directory \`${ancestor.path}\`` : `inside the skipped directory \`${ancestor.path}\``;
|
|
@@ -5733,21 +6739,58 @@ function explainPath(config, target) {
|
|
|
5733
6739
|
return result;
|
|
5734
6740
|
}
|
|
5735
6741
|
result.included = true;
|
|
5736
|
-
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)) {
|
|
5737
6780
|
result.included = false;
|
|
5738
6781
|
result.reason = "binary";
|
|
5739
6782
|
result.detail = "binary content; generate skips it and records `binary` in manifest.skipped[]";
|
|
5740
6783
|
return result;
|
|
5741
6784
|
}
|
|
5742
|
-
result.layer = classifyLayer(
|
|
5743
|
-
result.kind = classifyKind(
|
|
5744
|
-
result.language = detectLanguage(
|
|
5745
|
-
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);
|
|
5746
6789
|
if (layerRule) result.layerMatch = describeMatcher(layerRule.match);
|
|
5747
|
-
const kindRule = findRuleMatch(config.kinds,
|
|
6790
|
+
const kindRule = findRuleMatch(config.kinds, relative8);
|
|
5748
6791
|
if (kindRule) result.kindMatch = describeMatcher(kindRule.match);
|
|
5749
|
-
if (
|
|
5750
|
-
const buffer =
|
|
6792
|
+
if (fs14__namespace.existsSync(absolute) && fs14__namespace.statSync(absolute).isFile()) {
|
|
6793
|
+
const buffer = fs14__namespace.readFileSync(absolute);
|
|
5751
6794
|
const decoded = decodeText(buffer);
|
|
5752
6795
|
if (decoded.binary) {
|
|
5753
6796
|
result.included = false;
|
|
@@ -5759,12 +6802,12 @@ function explainPath(config, target) {
|
|
|
5759
6802
|
result.bytes = buffer.length;
|
|
5760
6803
|
result.lines = countLines(text2);
|
|
5761
6804
|
if (decoded.encoding !== "utf-8") result.encoding = decoded.encoding;
|
|
5762
|
-
const { findings } = scanAndRedactSecrets(text2, config.secretRules,
|
|
6805
|
+
const { findings } = scanAndRedactSecrets(text2, config.secretRules, relative8);
|
|
5763
6806
|
result.secretCount = findings.length;
|
|
5764
6807
|
result.secrets = findings.slice(0, 20).map((finding) => ({ rules: finding.rules, line: finding.line }));
|
|
5765
6808
|
}
|
|
5766
6809
|
const manifest = readManifest2(config);
|
|
5767
|
-
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;
|
|
5768
6811
|
result.part = entry?.part ?? null;
|
|
5769
6812
|
result.inclusionMode = entry?.inclusionMode;
|
|
5770
6813
|
return result;
|
|
@@ -5995,14 +7038,14 @@ function compareManifests(before, after) {
|
|
|
5995
7038
|
function loadManifest(target) {
|
|
5996
7039
|
let isDirectory;
|
|
5997
7040
|
try {
|
|
5998
|
-
isDirectory =
|
|
7041
|
+
isDirectory = fs14__namespace.statSync(target).isDirectory();
|
|
5999
7042
|
} catch {
|
|
6000
7043
|
throw new Error(`No snapshot found at ${target}: the path does not exist.`);
|
|
6001
7044
|
}
|
|
6002
|
-
const manifestPath = isDirectory ?
|
|
7045
|
+
const manifestPath = isDirectory ? path16__namespace.join(target, "manifest.json") : target;
|
|
6003
7046
|
let raw;
|
|
6004
7047
|
try {
|
|
6005
|
-
raw =
|
|
7048
|
+
raw = fs14__namespace.readFileSync(manifestPath, "utf8");
|
|
6006
7049
|
} catch {
|
|
6007
7050
|
throw new Error(`Could not read a manifest at ${manifestPath}.`);
|
|
6008
7051
|
}
|
|
@@ -6059,15 +7102,15 @@ function renderDiffText(diff) {
|
|
|
6059
7102
|
function appendGitignoreEntries(cwd, entries) {
|
|
6060
7103
|
const wanted = [...new Set(entries)].filter((entry) => entry.trim().length > 0);
|
|
6061
7104
|
if (wanted.length === 0) return { added: [], note: null };
|
|
6062
|
-
const ignorePath =
|
|
7105
|
+
const ignorePath = path16__namespace.join(cwd, ".gitignore");
|
|
6063
7106
|
const manual = `Add ${wanted.map((entry) => `"${entry}"`).join(" and ")} to your .gitignore yourself.`;
|
|
6064
7107
|
let existing = "";
|
|
6065
7108
|
try {
|
|
6066
|
-
if (
|
|
6067
|
-
if (
|
|
7109
|
+
if (fs14__namespace.existsSync(ignorePath)) {
|
|
7110
|
+
if (fs14__namespace.statSync(ignorePath).isDirectory()) {
|
|
6068
7111
|
return { added: [], note: `${ignorePath} is a directory, so it was not updated. ${manual}` };
|
|
6069
7112
|
}
|
|
6070
|
-
existing =
|
|
7113
|
+
existing = fs14__namespace.readFileSync(ignorePath, "utf8");
|
|
6071
7114
|
}
|
|
6072
7115
|
} catch (err) {
|
|
6073
7116
|
return {
|
|
@@ -6080,7 +7123,7 @@ function appendGitignoreEntries(cwd, entries) {
|
|
|
6080
7123
|
if (added.length === 0) return { added: [], note: null };
|
|
6081
7124
|
const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
6082
7125
|
try {
|
|
6083
|
-
|
|
7126
|
+
fs14__namespace.writeFileSync(ignorePath, `${existing}${prefix}${added.join("\n")}
|
|
6084
7127
|
`, "utf8");
|
|
6085
7128
|
} catch (err) {
|
|
6086
7129
|
return { added: [], note: `${err.message}. ${manual}` };
|
|
@@ -6093,7 +7136,12 @@ var DEFAULT_ANSWERS = {
|
|
|
6093
7136
|
preset: "generic",
|
|
6094
7137
|
branch: "main",
|
|
6095
7138
|
maxPartBytes: "300k",
|
|
7139
|
+
maxPartTokens: null,
|
|
6096
7140
|
largeFileBytes: "200k",
|
|
7141
|
+
maxMonolithBytes: null,
|
|
7142
|
+
discovery: "auto",
|
|
7143
|
+
skeleton: false,
|
|
7144
|
+
sarif: false,
|
|
6097
7145
|
include: [],
|
|
6098
7146
|
excludePatterns: [],
|
|
6099
7147
|
artifacts: {
|
|
@@ -6146,6 +7194,9 @@ function renderConfig(answers) {
|
|
|
6146
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
|
|
6147
7195
|
// Hard ceiling per part. "300k", 307200 and "1.5mb" are all accepted.
|
|
6148
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)},
|
|
6149
7200
|
// Files above this size get a head/tail preview instead of the full body.
|
|
6150
7201
|
largeFileBytes: ${size(answers.largeFileBytes)},
|
|
6151
7202
|
previewHeadLines: 80,
|
|
@@ -6156,6 +7207,22 @@ function renderConfig(answers) {
|
|
|
6156
7207
|
byLayer: {},
|
|
6157
7208
|
},
|
|
6158
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
|
+
|
|
6159
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
|
|
6160
7227
|
// Allow-list. Empty means "everything that is not ignored".
|
|
6161
7228
|
// Example: ["src/**", "packages/*/src/**"]
|
|
@@ -6165,7 +7232,7 @@ function renderConfig(answers) {
|
|
|
6165
7232
|
// How candidate files are found: "auto" lists them through git when git can
|
|
6166
7233
|
// and walks the tree otherwise; "git" and "walk" force one path, and "walk" is
|
|
6167
7234
|
// the escape hatch when .git exists but git cannot list it.
|
|
6168
|
-
discovery:
|
|
7235
|
+
discovery: ${JSON.stringify(answers.discovery)},
|
|
6169
7236
|
|
|
6170
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
|
|
6171
7238
|
// "merge" = the preset's rules plus yours (yours win). "replace" = yours only.
|
|
@@ -6196,7 +7263,7 @@ function renderConfig(answers) {
|
|
|
6196
7263
|
monolith: ${artifacts.monolith}, // full-codebase.json + .md (roughly doubles output)
|
|
6197
7264
|
},
|
|
6198
7265
|
// Skip the monolith above this size. null = no limit.
|
|
6199
|
-
maxMonolithBytes: null,
|
|
7266
|
+
maxMonolithBytes: ${answers.maxMonolithBytes === null ? "null" : size(answers.maxMonolithBytes)},
|
|
6200
7267
|
// Top-level switch for the monolith, equivalent to artifacts.monolith.
|
|
6201
7268
|
// When both are present the artifact entry wins; this one exists so the
|
|
6202
7269
|
// generated file lists every setting the tool accepts.
|
|
@@ -6573,10 +7640,10 @@ var CONFIG_CANDIDATES = [
|
|
|
6573
7640
|
];
|
|
6574
7641
|
function chooseConfigFileName(cwd) {
|
|
6575
7642
|
if (!supportsTypeScriptImports()) return "snapshot.config.mjs";
|
|
6576
|
-
const pkgPath =
|
|
6577
|
-
if (!
|
|
7643
|
+
const pkgPath = path16__namespace.join(cwd, "package.json");
|
|
7644
|
+
if (!fs14__namespace.existsSync(pkgPath)) return "snapshot.config.mjs";
|
|
6578
7645
|
try {
|
|
6579
|
-
const parsed = JSON.parse(
|
|
7646
|
+
const parsed = JSON.parse(fs14__namespace.readFileSync(pkgPath, "utf8"));
|
|
6580
7647
|
return parsed.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
6581
7648
|
} catch {
|
|
6582
7649
|
return "snapshot.config.mjs";
|
|
@@ -6590,7 +7657,7 @@ var SIZE_CHOICES = [
|
|
|
6590
7657
|
];
|
|
6591
7658
|
function packageJsonHasConfig(cwd) {
|
|
6592
7659
|
try {
|
|
6593
|
-
const parsed = JSON.parse(
|
|
7660
|
+
const parsed = JSON.parse(fs14__namespace.readFileSync(path16__namespace.join(cwd, "package.json"), "utf8"));
|
|
6594
7661
|
return typeof parsed.snapshot === "object" && parsed.snapshot !== null && !Array.isArray(parsed.snapshot);
|
|
6595
7662
|
} catch {
|
|
6596
7663
|
return false;
|
|
@@ -6607,11 +7674,12 @@ function artifactAnswers(options) {
|
|
|
6607
7674
|
const keep = new Set(options.onlyArtifacts);
|
|
6608
7675
|
for (const name of ARTIFACT_NAMES) artifacts[name] = keep.has(name);
|
|
6609
7676
|
}
|
|
7677
|
+
if (options.includeMonolith === true) artifacts.monolith = true;
|
|
6610
7678
|
return artifacts;
|
|
6611
7679
|
}
|
|
6612
7680
|
function topLevelDirs(cwd) {
|
|
6613
7681
|
try {
|
|
6614
|
-
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();
|
|
6615
7683
|
} catch {
|
|
6616
7684
|
return [];
|
|
6617
7685
|
}
|
|
@@ -6624,7 +7692,7 @@ function describeJsonValue(value) {
|
|
|
6624
7692
|
}
|
|
6625
7693
|
function writeFileOrExplain(target, content) {
|
|
6626
7694
|
try {
|
|
6627
|
-
|
|
7695
|
+
fs14__namespace.writeFileSync(target, content, "utf8");
|
|
6628
7696
|
} catch (err) {
|
|
6629
7697
|
const code = err.code;
|
|
6630
7698
|
const reason = code === "EACCES" || code === "EPERM" || code === "EROFS" ? `permission denied (${code})` : code === "EISDIR" ? "it is a directory" : `${err.message}`;
|
|
@@ -6635,7 +7703,7 @@ function canWriteModuleConfig(name, cwd) {
|
|
|
6635
7703
|
if (name.endsWith(".mjs") || name.endsWith(".ts")) return true;
|
|
6636
7704
|
if (!name.endsWith(".js")) return false;
|
|
6637
7705
|
try {
|
|
6638
|
-
const parsed = JSON.parse(
|
|
7706
|
+
const parsed = JSON.parse(fs14__namespace.readFileSync(path16__namespace.join(cwd, "package.json"), "utf8"));
|
|
6639
7707
|
return parsed.type === "module";
|
|
6640
7708
|
} catch {
|
|
6641
7709
|
return false;
|
|
@@ -6670,19 +7738,19 @@ function serializePackageJson(raw, value) {
|
|
|
6670
7738
|
return `${text2}${raw.endsWith("\n") ? crlf ? "\r\n" : "\n" : ""}`;
|
|
6671
7739
|
}
|
|
6672
7740
|
function updatePackageJson(cwd, notes) {
|
|
6673
|
-
const packagePath =
|
|
7741
|
+
const packagePath = path16__namespace.join(cwd, "package.json");
|
|
6674
7742
|
const unchanged = {
|
|
6675
7743
|
scripts: [],
|
|
6676
7744
|
dependency: { name: GENERATOR_NAME, range: null, added: false }
|
|
6677
7745
|
};
|
|
6678
|
-
if (!
|
|
7746
|
+
if (!fs14__namespace.existsSync(packagePath)) {
|
|
6679
7747
|
notes.push("No package.json found, so npm scripts and the devDependency were not added.");
|
|
6680
7748
|
return unchanged;
|
|
6681
7749
|
}
|
|
6682
7750
|
let raw;
|
|
6683
7751
|
let parsed;
|
|
6684
7752
|
try {
|
|
6685
|
-
raw =
|
|
7753
|
+
raw = fs14__namespace.readFileSync(packagePath, "utf8");
|
|
6686
7754
|
parsed = JSON.parse(raw);
|
|
6687
7755
|
} catch {
|
|
6688
7756
|
notes.push("Could not parse package.json; npm scripts and the devDependency were not added.");
|
|
@@ -6707,17 +7775,26 @@ function updatePackageJson(cwd, notes) {
|
|
|
6707
7775
|
scriptsWereBroken = true;
|
|
6708
7776
|
}
|
|
6709
7777
|
const added = [];
|
|
7778
|
+
const isSelfRepository = parsed.name === GENERATOR_NAME;
|
|
7779
|
+
const scriptPrefix = isSelfRepository ? "node ./dist/bin.js" : `npx ${GENERATOR_NAME}`;
|
|
6710
7780
|
if (!scripts.snapshot) {
|
|
6711
|
-
scripts.snapshot =
|
|
7781
|
+
scripts.snapshot = `${scriptPrefix} generate`;
|
|
6712
7782
|
added.push("snapshot");
|
|
6713
7783
|
}
|
|
6714
7784
|
if (!scripts["snapshot:validate"]) {
|
|
6715
|
-
scripts["snapshot:validate"] =
|
|
7785
|
+
scripts["snapshot:validate"] = `${scriptPrefix} validate`;
|
|
6716
7786
|
added.push("snapshot:validate");
|
|
6717
7787
|
}
|
|
6718
7788
|
const declared = declaredRange(parsed);
|
|
6719
7789
|
let dependency;
|
|
6720
|
-
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) {
|
|
6721
7798
|
dependency = { name: GENERATOR_NAME, range: declared, added: false };
|
|
6722
7799
|
} else {
|
|
6723
7800
|
const range = `^${GENERATOR_VERSION}`;
|
|
@@ -6767,7 +7844,15 @@ async function runInit(options) {
|
|
|
6767
7844
|
// `validate` warn about a mismatch the user never caused.
|
|
6768
7845
|
branch: getGitInfo(options.cwd).branch,
|
|
6769
7846
|
maxPartBytes: options.maxPartBytes ?? detected.recommendedBudget ?? DEFAULT_ANSWERS.maxPartBytes,
|
|
6770
|
-
|
|
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,
|
|
6771
7856
|
include: options.include ?? [],
|
|
6772
7857
|
excludePatterns: options.exclude ?? [],
|
|
6773
7858
|
artifacts: artifactAnswers(options),
|
|
@@ -6974,7 +8059,7 @@ async function runInit(options) {
|
|
|
6974
8059
|
const configText = renderConfig(answers);
|
|
6975
8060
|
const preferredName = chooseConfigFileName(options.cwd);
|
|
6976
8061
|
const presentConfigs = CONFIG_CANDIDATES.filter(
|
|
6977
|
-
(name) =>
|
|
8062
|
+
(name) => fs14__namespace.existsSync(path16__namespace.join(options.cwd, name))
|
|
6978
8063
|
);
|
|
6979
8064
|
const present = presentConfigs[0] ?? null;
|
|
6980
8065
|
if (presentConfigs.length > 1) {
|
|
@@ -6983,7 +8068,7 @@ async function runInit(options) {
|
|
|
6983
8068
|
);
|
|
6984
8069
|
}
|
|
6985
8070
|
const configName = present !== null && options.force ? present : preferredName;
|
|
6986
|
-
const configPath =
|
|
8071
|
+
const configPath = path16__namespace.join(options.cwd, configName);
|
|
6987
8072
|
const reused = present !== null && options.force !== true && options.reuseConfig === true;
|
|
6988
8073
|
if (reused) notes.push(`Kept the existing ${present} instead of writing a new one.`);
|
|
6989
8074
|
if (options.dryRun) {
|
|
@@ -6998,7 +8083,8 @@ async function runInit(options) {
|
|
|
6998
8083
|
scripts: [],
|
|
6999
8084
|
gitignoreEntries: [],
|
|
7000
8085
|
dependency: { name: GENERATOR_NAME, range: `^${GENERATOR_VERSION}`, added: false },
|
|
7001
|
-
notes
|
|
8086
|
+
notes,
|
|
8087
|
+
outDir: answers.outDir
|
|
7002
8088
|
};
|
|
7003
8089
|
}
|
|
7004
8090
|
if (present !== null && present !== preferredName && !options.force && !reused) {
|
|
@@ -7016,7 +8102,7 @@ async function runInit(options) {
|
|
|
7016
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.'
|
|
7017
8103
|
);
|
|
7018
8104
|
}
|
|
7019
|
-
if (
|
|
8105
|
+
if (fs14__namespace.existsSync(configPath) && !options.force && !reused) {
|
|
7020
8106
|
throw new Error(`${configName} already exists. Re-run with --force to replace it.`);
|
|
7021
8107
|
}
|
|
7022
8108
|
if (!reused) writeFileOrExplain(configPath, configText);
|
|
@@ -7034,14 +8120,15 @@ async function runInit(options) {
|
|
|
7034
8120
|
scripts,
|
|
7035
8121
|
gitignoreEntries: ignore.added,
|
|
7036
8122
|
dependency,
|
|
7037
|
-
notes
|
|
8123
|
+
notes,
|
|
8124
|
+
outDir: answers.outDir
|
|
7038
8125
|
};
|
|
7039
8126
|
}
|
|
7040
8127
|
function describeTarget(cwd) {
|
|
7041
|
-
const
|
|
7042
|
-
if (
|
|
7043
|
-
if (
|
|
7044
|
-
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;
|
|
7045
8132
|
}
|
|
7046
8133
|
function firstRunMessage(reason, cwd) {
|
|
7047
8134
|
const where = describeTarget(cwd);
|
|
@@ -7064,7 +8151,7 @@ function planFirstRun(facts) {
|
|
|
7064
8151
|
var NOT_CONTENT = /* @__PURE__ */ new Set([".git", ".hg", ".svn", ".bzr", ".DS_Store", "Thumbs.db"]);
|
|
7065
8152
|
function isEffectivelyEmpty(cwd) {
|
|
7066
8153
|
try {
|
|
7067
|
-
return
|
|
8154
|
+
return fs14__namespace.readdirSync(cwd).every((entry) => NOT_CONTENT.has(entry));
|
|
7068
8155
|
} catch {
|
|
7069
8156
|
return false;
|
|
7070
8157
|
}
|
|
@@ -7072,19 +8159,132 @@ function isEffectivelyEmpty(cwd) {
|
|
|
7072
8159
|
function hasPriorSnapshot(cwd, outDir) {
|
|
7073
8160
|
let parsed;
|
|
7074
8161
|
try {
|
|
7075
|
-
parsed = JSON.parse(
|
|
8162
|
+
parsed = JSON.parse(fs14__namespace.readFileSync(path16__namespace.join(cwd, outDir, ".codebase-snapshot"), "utf8"));
|
|
7076
8163
|
} catch {
|
|
7077
8164
|
return false;
|
|
7078
8165
|
}
|
|
7079
8166
|
if (parsed === null || typeof parsed !== "object") return false;
|
|
7080
8167
|
return isOurGeneratorName(parsed.generator);
|
|
7081
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
|
+
}
|
|
7082
8282
|
|
|
7083
8283
|
// src/index.ts
|
|
7084
8284
|
var SCHEMA_VERSION = "3.0.0";
|
|
7085
8285
|
var SUMMARY_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
7086
8286
|
function sha256Hex2(input) {
|
|
7087
|
-
return
|
|
8287
|
+
return crypto2__namespace.createHash("sha256").update(input).digest("hex");
|
|
7088
8288
|
}
|
|
7089
8289
|
var TS_EXTENSIONS = [
|
|
7090
8290
|
".ts",
|
|
@@ -7123,71 +8323,15 @@ function hasStructuralSummary(relativePath) {
|
|
|
7123
8323
|
function bump(counts, key) {
|
|
7124
8324
|
counts[key] = (counts[key] ?? 0) + 1;
|
|
7125
8325
|
}
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
".js",
|
|
7133
|
-
".jsx",
|
|
7134
|
-
".mjs",
|
|
7135
|
-
".cjs",
|
|
7136
|
-
"/index.ts",
|
|
7137
|
-
"/index.tsx",
|
|
7138
|
-
"/index.js",
|
|
7139
|
-
"/index.mjs"
|
|
7140
|
-
];
|
|
7141
|
-
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
7142
|
-
".ts",
|
|
7143
|
-
".tsx",
|
|
7144
|
-
".mts",
|
|
7145
|
-
".cts",
|
|
7146
|
-
".js",
|
|
7147
|
-
".jsx",
|
|
7148
|
-
".mjs",
|
|
7149
|
-
".cjs"
|
|
7150
|
-
]);
|
|
7151
|
-
function buildImportResolver(files) {
|
|
7152
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
7153
|
-
const byPathFolded = /* @__PURE__ */ new Map();
|
|
7154
|
-
const byStem = /* @__PURE__ */ new Map();
|
|
7155
|
-
const byStemFolded = /* @__PURE__ */ new Map();
|
|
7156
|
-
const remember = (map, key, value) => {
|
|
7157
|
-
if (!map.has(key)) map.set(key, value);
|
|
7158
|
-
};
|
|
7159
|
-
for (const file of files) {
|
|
7160
|
-
const relativePath = file.relativePath;
|
|
7161
|
-
remember(byPath, relativePath, relativePath);
|
|
7162
|
-
remember(byPathFolded, relativePath.toLowerCase(), relativePath);
|
|
7163
|
-
const extension = path13__namespace.posix.extname(relativePath);
|
|
7164
|
-
if (!SOURCE_EXTENSIONS.has(extension)) continue;
|
|
7165
|
-
const stem = relativePath.slice(0, -extension.length);
|
|
7166
|
-
remember(byStem, stem, relativePath);
|
|
7167
|
-
remember(byStemFolded, stem.toLowerCase(), relativePath);
|
|
7168
|
-
if (path13__namespace.posix.basename(stem) === "index") {
|
|
7169
|
-
const directory = path13__namespace.posix.dirname(stem);
|
|
7170
|
-
remember(byStem, directory, relativePath);
|
|
7171
|
-
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;
|
|
7172
8332
|
}
|
|
7173
8333
|
}
|
|
7174
|
-
|
|
7175
|
-
}
|
|
7176
|
-
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
7177
|
-
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
7178
|
-
const joined = path13__namespace.posix.normalize(
|
|
7179
|
-
path13__namespace.posix.join(path13__namespace.posix.dirname(fromFile), specifier)
|
|
7180
|
-
);
|
|
7181
|
-
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
7182
|
-
const direct = exact(joined);
|
|
7183
|
-
if (direct) return direct;
|
|
7184
|
-
for (const suffix of IMPORT_CANDIDATE_SUFFIXES) {
|
|
7185
|
-
const hit = exact(`${joined}${suffix}`);
|
|
7186
|
-
if (hit) return hit;
|
|
7187
|
-
}
|
|
7188
|
-
const stem = joined.replace(/\.(?:js|mjs|cjs|jsx)$/, "");
|
|
7189
|
-
const byStem = (key) => resolver.byStem.get(key) ?? resolver.byStemFolded.get(key.toLowerCase()) ?? null;
|
|
7190
|
-
return byStem(joined) ?? byStem(stem);
|
|
8334
|
+
items.length = write;
|
|
7191
8335
|
}
|
|
7192
8336
|
function buildIndexes(files) {
|
|
7193
8337
|
const resolver = buildImportResolver(files);
|
|
@@ -7239,7 +8383,7 @@ function buildIndexes(files) {
|
|
|
7239
8383
|
}))
|
|
7240
8384
|
};
|
|
7241
8385
|
}
|
|
7242
|
-
function verifyInMemory(files, parts
|
|
8386
|
+
function verifyInMemory(files, parts) {
|
|
7243
8387
|
const errors = [];
|
|
7244
8388
|
const partNames = new Set(parts.map((part) => part.filename));
|
|
7245
8389
|
for (const file of files) {
|
|
@@ -7255,8 +8399,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
7255
8399
|
if (actualBytes !== part.metadata.byteSize) {
|
|
7256
8400
|
errors.push(`${part.filename} byteSize ${part.metadata.byteSize} != actual ${actualBytes}.`);
|
|
7257
8401
|
}
|
|
7258
|
-
|
|
7259
|
-
|
|
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
|
+
);
|
|
7260
8407
|
}
|
|
7261
8408
|
if (part.metadata.includedFiles.length !== part.metadata.fileCount) {
|
|
7262
8409
|
errors.push(`${part.filename} fileCount does not match its included files.`);
|
|
@@ -7269,11 +8416,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
7269
8416
|
return errors;
|
|
7270
8417
|
}
|
|
7271
8418
|
function resolveSafeOutputDirs(config) {
|
|
7272
|
-
const cwd =
|
|
7273
|
-
const outDir =
|
|
7274
|
-
const tmpDir =
|
|
7275
|
-
const root =
|
|
7276
|
-
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}`);
|
|
7277
8424
|
const reject = (reason) => {
|
|
7278
8425
|
throw new Error(
|
|
7279
8426
|
`Refusing to generate: ${reason}. outDir=${outDir} tmpDir=${tmpDir} cwd=${cwd}`
|
|
@@ -7329,14 +8476,14 @@ function listEntries(dir) {
|
|
|
7329
8476
|
const walk = (absolute, prefix) => {
|
|
7330
8477
|
let children;
|
|
7331
8478
|
try {
|
|
7332
|
-
children =
|
|
8479
|
+
children = fs14__namespace.readdirSync(absolute, { withFileTypes: true });
|
|
7333
8480
|
} catch {
|
|
7334
8481
|
return;
|
|
7335
8482
|
}
|
|
7336
8483
|
for (const child of children) {
|
|
7337
|
-
const
|
|
7338
|
-
entries.push(
|
|
7339
|
-
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);
|
|
7340
8487
|
}
|
|
7341
8488
|
};
|
|
7342
8489
|
walk(dir, "");
|
|
@@ -7353,28 +8500,33 @@ function markerPayload(dir, state, id, createdAt) {
|
|
|
7353
8500
|
pid: process.pid,
|
|
7354
8501
|
entries: listEntries(dir)
|
|
7355
8502
|
};
|
|
7356
|
-
const manifestPath =
|
|
7357
|
-
if (
|
|
7358
|
-
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));
|
|
7359
8506
|
}
|
|
7360
8507
|
return marker2;
|
|
7361
8508
|
}
|
|
7362
8509
|
function writeOwnershipMarker(dir, marker2) {
|
|
7363
|
-
|
|
7364
|
-
|
|
8510
|
+
fs14__namespace.writeFileSync(
|
|
8511
|
+
path16__namespace.join(dir, OWNERSHIP_MARKER),
|
|
7365
8512
|
`${JSON.stringify(marker2, null, 2)}
|
|
7366
8513
|
`,
|
|
7367
8514
|
"utf8"
|
|
7368
8515
|
);
|
|
7369
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
|
+
}
|
|
7370
8522
|
function readOwnershipMarker(dir) {
|
|
7371
|
-
const markerPath =
|
|
7372
|
-
if (!
|
|
8523
|
+
const markerPath = path16__namespace.join(dir, OWNERSHIP_MARKER);
|
|
8524
|
+
if (!fs14__namespace.existsSync(markerPath)) {
|
|
7373
8525
|
return { marker: null, problem: "it holds no .codebase-snapshot marker" };
|
|
7374
8526
|
}
|
|
7375
8527
|
let raw;
|
|
7376
8528
|
try {
|
|
7377
|
-
raw =
|
|
8529
|
+
raw = fs14__namespace.readFileSync(markerPath, "utf8");
|
|
7378
8530
|
} catch {
|
|
7379
8531
|
return { marker: null, problem: "its .codebase-snapshot marker could not be read" };
|
|
7380
8532
|
}
|
|
@@ -7442,11 +8594,11 @@ function assessSnapshotDirectory(dir) {
|
|
|
7442
8594
|
marker: marker2
|
|
7443
8595
|
};
|
|
7444
8596
|
}
|
|
7445
|
-
const manifestPath =
|
|
7446
|
-
if (
|
|
8597
|
+
const manifestPath = path16__namespace.join(dir, "manifest.json");
|
|
8598
|
+
if (fs14__namespace.existsSync(manifestPath)) {
|
|
7447
8599
|
let raw;
|
|
7448
8600
|
try {
|
|
7449
|
-
raw =
|
|
8601
|
+
raw = fs14__namespace.readFileSync(manifestPath, "utf8");
|
|
7450
8602
|
} catch {
|
|
7451
8603
|
return {
|
|
7452
8604
|
owned: false,
|
|
@@ -7528,13 +8680,13 @@ function containsNoFiles(dir) {
|
|
|
7528
8680
|
const current = pending.pop();
|
|
7529
8681
|
let entries;
|
|
7530
8682
|
try {
|
|
7531
|
-
entries =
|
|
8683
|
+
entries = fs14__namespace.readdirSync(current, { withFileTypes: true });
|
|
7532
8684
|
} catch {
|
|
7533
8685
|
return false;
|
|
7534
8686
|
}
|
|
7535
8687
|
for (const entry of entries) {
|
|
7536
8688
|
if (entry.isDirectory()) {
|
|
7537
|
-
pending.push(
|
|
8689
|
+
pending.push(path16__namespace.join(current, entry.name));
|
|
7538
8690
|
} else if (!IGNORABLE_ENTRIES.has(entry.name)) {
|
|
7539
8691
|
return false;
|
|
7540
8692
|
}
|
|
@@ -7543,13 +8695,13 @@ function containsNoFiles(dir) {
|
|
|
7543
8695
|
return true;
|
|
7544
8696
|
}
|
|
7545
8697
|
function assertReplaceable(target, label, secretRules) {
|
|
7546
|
-
if (!
|
|
7547
|
-
if (!
|
|
8698
|
+
if (!fs14__namespace.existsSync(target)) return;
|
|
8699
|
+
if (!fs14__namespace.statSync(target).isDirectory()) {
|
|
7548
8700
|
throw new Error(
|
|
7549
8701
|
`Refusing to write into ${label} ${target}: it exists but is not a directory.`
|
|
7550
8702
|
);
|
|
7551
8703
|
}
|
|
7552
|
-
const entries =
|
|
8704
|
+
const entries = fs14__namespace.readdirSync(target);
|
|
7553
8705
|
if (entries.length === 0) return;
|
|
7554
8706
|
if (entries.every((name) => IGNORABLE_ENTRIES.has(name))) return;
|
|
7555
8707
|
if (containsNoFiles(target)) return;
|
|
@@ -7569,18 +8721,18 @@ function assertReplaceable(target, label, secretRules) {
|
|
|
7569
8721
|
);
|
|
7570
8722
|
}
|
|
7571
8723
|
function assertStagingRootUsable(tmpDir, secretRules) {
|
|
7572
|
-
if (!
|
|
7573
|
-
if (!
|
|
8724
|
+
if (!fs14__namespace.existsSync(tmpDir)) return;
|
|
8725
|
+
if (!fs14__namespace.statSync(tmpDir).isDirectory()) {
|
|
7574
8726
|
throw new Error(`Refusing to write into tmpDir ${tmpDir}: it exists but is not a directory.`);
|
|
7575
8727
|
}
|
|
7576
|
-
const entries =
|
|
8728
|
+
const entries = fs14__namespace.readdirSync(tmpDir);
|
|
7577
8729
|
if (entries.length === 0) return;
|
|
7578
8730
|
const foreign = entries.filter((name) => {
|
|
7579
|
-
const full =
|
|
8731
|
+
const full = path16__namespace.join(tmpDir, name);
|
|
7580
8732
|
if (!name.startsWith(RUN_DIR_PREFIX)) return true;
|
|
7581
8733
|
let stats;
|
|
7582
8734
|
try {
|
|
7583
|
-
stats =
|
|
8735
|
+
stats = fs14__namespace.statSync(full);
|
|
7584
8736
|
} catch {
|
|
7585
8737
|
return true;
|
|
7586
8738
|
}
|
|
@@ -7588,7 +8740,7 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7588
8740
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7589
8741
|
if (marker2) return false;
|
|
7590
8742
|
try {
|
|
7591
|
-
return
|
|
8743
|
+
return fs14__namespace.readdirSync(full).length > 0;
|
|
7592
8744
|
} catch {
|
|
7593
8745
|
return true;
|
|
7594
8746
|
}
|
|
@@ -7603,13 +8755,13 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7603
8755
|
function cleanStaleRunDirs(tmpDir) {
|
|
7604
8756
|
let entries;
|
|
7605
8757
|
try {
|
|
7606
|
-
entries =
|
|
8758
|
+
entries = fs14__namespace.readdirSync(tmpDir);
|
|
7607
8759
|
} catch {
|
|
7608
8760
|
return;
|
|
7609
8761
|
}
|
|
7610
8762
|
for (const name of entries) {
|
|
7611
8763
|
if (!name.startsWith(RUN_DIR_PREFIX)) continue;
|
|
7612
|
-
const full =
|
|
8764
|
+
const full = path16__namespace.join(tmpDir, name);
|
|
7613
8765
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7614
8766
|
if (marker2) {
|
|
7615
8767
|
const created = Date.parse(marker2.createdAt);
|
|
@@ -7617,33 +8769,33 @@ function cleanStaleRunDirs(tmpDir) {
|
|
|
7617
8769
|
const abandoned = typeof marker2.pid === "number" && !isProcessAlive(marker2.pid);
|
|
7618
8770
|
if (aged || abandoned) {
|
|
7619
8771
|
try {
|
|
7620
|
-
|
|
8772
|
+
fs14__namespace.rmSync(full, { recursive: true, force: true });
|
|
7621
8773
|
} catch {
|
|
7622
8774
|
}
|
|
7623
8775
|
}
|
|
7624
8776
|
continue;
|
|
7625
8777
|
}
|
|
7626
8778
|
try {
|
|
7627
|
-
if (
|
|
8779
|
+
if (fs14__namespace.readdirSync(full).length === 0) fs14__namespace.rmSync(full, { recursive: true, force: true });
|
|
7628
8780
|
} catch {
|
|
7629
8781
|
}
|
|
7630
8782
|
}
|
|
7631
8783
|
}
|
|
7632
8784
|
function removeIfEmpty(dir) {
|
|
7633
8785
|
try {
|
|
7634
|
-
if (
|
|
8786
|
+
if (fs14__namespace.readdirSync(dir).length === 0) fs14__namespace.rmSync(dir, { recursive: true, force: true });
|
|
7635
8787
|
} catch {
|
|
7636
8788
|
}
|
|
7637
8789
|
}
|
|
7638
8790
|
function ensureParentDirectory(target, label) {
|
|
7639
|
-
const parent =
|
|
7640
|
-
if (
|
|
8791
|
+
const parent = path16__namespace.dirname(target);
|
|
8792
|
+
if (fs14__namespace.existsSync(parent) && !fs14__namespace.statSync(parent).isDirectory()) {
|
|
7641
8793
|
throw new Error(
|
|
7642
8794
|
`Refusing to create ${label} ${target}: ${parent} exists but is not a directory.`
|
|
7643
8795
|
);
|
|
7644
8796
|
}
|
|
7645
8797
|
try {
|
|
7646
|
-
|
|
8798
|
+
fs14__namespace.mkdirSync(parent, { recursive: true });
|
|
7647
8799
|
} catch (err) {
|
|
7648
8800
|
throw new Error(
|
|
7649
8801
|
`Refusing to create ${label} ${target}: its parent ${parent} could not be created (${err.message}).`
|
|
@@ -7651,8 +8803,7 @@ function ensureParentDirectory(target, label) {
|
|
|
7651
8803
|
}
|
|
7652
8804
|
}
|
|
7653
8805
|
function acquireRunLock(stagingRoot) {
|
|
7654
|
-
const
|
|
7655
|
-
const lockPath = path13__namespace.join(os__namespace.tmpdir(), `codebase-snapshot-${key}.lock`);
|
|
8806
|
+
const lockPath = lockPathFor(stagingRoot);
|
|
7656
8807
|
const payload = `${JSON.stringify({
|
|
7657
8808
|
pid: process.pid,
|
|
7658
8809
|
stagingRoot,
|
|
@@ -7661,22 +8812,22 @@ function acquireRunLock(stagingRoot) {
|
|
|
7661
8812
|
`;
|
|
7662
8813
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
7663
8814
|
try {
|
|
7664
|
-
const handle =
|
|
8815
|
+
const handle = fs14__namespace.openSync(lockPath, "wx");
|
|
7665
8816
|
try {
|
|
7666
|
-
|
|
8817
|
+
fs14__namespace.writeSync(handle, payload);
|
|
7667
8818
|
} finally {
|
|
7668
|
-
|
|
8819
|
+
fs14__namespace.closeSync(handle);
|
|
7669
8820
|
}
|
|
7670
8821
|
return () => {
|
|
7671
8822
|
try {
|
|
7672
|
-
|
|
8823
|
+
fs14__namespace.rmSync(lockPath, { force: true });
|
|
7673
8824
|
} catch {
|
|
7674
8825
|
}
|
|
7675
8826
|
};
|
|
7676
8827
|
} catch (err) {
|
|
7677
8828
|
if (err.code !== "EEXIST") throw err;
|
|
7678
8829
|
if (attempt === 0 && lockOwnerIsGone(lockPath)) {
|
|
7679
|
-
|
|
8830
|
+
fs14__namespace.rmSync(lockPath, { force: true });
|
|
7680
8831
|
continue;
|
|
7681
8832
|
}
|
|
7682
8833
|
throw new Error(
|
|
@@ -7689,16 +8840,19 @@ function acquireRunLock(stagingRoot) {
|
|
|
7689
8840
|
function lockKeyFor(stagingRoot) {
|
|
7690
8841
|
let key = stagingRoot;
|
|
7691
8842
|
try {
|
|
7692
|
-
key =
|
|
8843
|
+
key = fs14__namespace.realpathSync(stagingRoot);
|
|
7693
8844
|
} catch {
|
|
7694
8845
|
}
|
|
7695
8846
|
if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
|
|
7696
8847
|
return sha256Hex2(key).slice(0, 16);
|
|
7697
8848
|
}
|
|
8849
|
+
function lockPathFor(stagingRoot) {
|
|
8850
|
+
return path16__namespace.join(os__namespace.tmpdir(), `codebase-snapshot-${lockKeyFor(stagingRoot)}.lock`);
|
|
8851
|
+
}
|
|
7698
8852
|
function lockOwnerIsGone(lockPath) {
|
|
7699
8853
|
let raw;
|
|
7700
8854
|
try {
|
|
7701
|
-
raw =
|
|
8855
|
+
raw = fs14__namespace.readFileSync(lockPath, "utf8");
|
|
7702
8856
|
} catch {
|
|
7703
8857
|
return false;
|
|
7704
8858
|
}
|
|
@@ -7706,9 +8860,9 @@ function lockOwnerIsGone(lockPath) {
|
|
|
7706
8860
|
try {
|
|
7707
8861
|
pid = JSON.parse(raw).pid;
|
|
7708
8862
|
} catch {
|
|
7709
|
-
return
|
|
8863
|
+
return lockFileIsAncient(lockPath);
|
|
7710
8864
|
}
|
|
7711
|
-
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return
|
|
8865
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return lockFileIsAncient(lockPath);
|
|
7712
8866
|
try {
|
|
7713
8867
|
process.kill(pid, 0);
|
|
7714
8868
|
return false;
|
|
@@ -7716,14 +8870,21 @@ function lockOwnerIsGone(lockPath) {
|
|
|
7716
8870
|
return err.code === "ESRCH";
|
|
7717
8871
|
}
|
|
7718
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
|
+
}
|
|
7719
8880
|
function writeManifestAtomically(manifestPath, manifest) {
|
|
7720
8881
|
const tempPath = `${manifestPath}.${process.pid}.tmp`;
|
|
7721
|
-
|
|
8882
|
+
fs14__namespace.writeFileSync(tempPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
7722
8883
|
try {
|
|
7723
|
-
|
|
8884
|
+
fs14__namespace.renameSync(tempPath, manifestPath);
|
|
7724
8885
|
} catch (err) {
|
|
7725
8886
|
try {
|
|
7726
|
-
|
|
8887
|
+
fs14__namespace.rmSync(tempPath, { force: true });
|
|
7727
8888
|
} catch {
|
|
7728
8889
|
}
|
|
7729
8890
|
throw err;
|
|
@@ -7746,31 +8907,31 @@ function swapIntoPlace(stagingDir, outDir) {
|
|
|
7746
8907
|
}
|
|
7747
8908
|
function swapOnce(stagingDir, outDir) {
|
|
7748
8909
|
const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
|
|
7749
|
-
const hadPrevious =
|
|
7750
|
-
if (hadPrevious)
|
|
8910
|
+
const hadPrevious = fs14__namespace.existsSync(outDir);
|
|
8911
|
+
if (hadPrevious) fs14__namespace.renameSync(outDir, backupDir);
|
|
7751
8912
|
try {
|
|
7752
8913
|
try {
|
|
7753
|
-
|
|
8914
|
+
fs14__namespace.renameSync(stagingDir, outDir);
|
|
7754
8915
|
} catch (err) {
|
|
7755
8916
|
if (err.code !== "EXDEV") throw err;
|
|
7756
8917
|
const partialDir = `${outDir}.partial-${process.pid}-${Date.now()}`;
|
|
7757
8918
|
try {
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
8919
|
+
fs14__namespace.cpSync(stagingDir, partialDir, { recursive: true });
|
|
8920
|
+
fs14__namespace.renameSync(partialDir, outDir);
|
|
8921
|
+
fs14__namespace.rmSync(stagingDir, { recursive: true, force: true });
|
|
7761
8922
|
} catch (copyErr) {
|
|
7762
8923
|
try {
|
|
7763
|
-
|
|
8924
|
+
fs14__namespace.rmSync(partialDir, { recursive: true, force: true });
|
|
7764
8925
|
} catch {
|
|
7765
8926
|
}
|
|
7766
8927
|
throw copyErr;
|
|
7767
8928
|
}
|
|
7768
8929
|
}
|
|
7769
|
-
if (hadPrevious)
|
|
8930
|
+
if (hadPrevious) fs14__namespace.rmSync(backupDir, { recursive: true, force: true });
|
|
7770
8931
|
} catch (err) {
|
|
7771
|
-
if (hadPrevious &&
|
|
7772
|
-
if (
|
|
7773
|
-
|
|
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);
|
|
7774
8935
|
}
|
|
7775
8936
|
throw err;
|
|
7776
8937
|
}
|
|
@@ -7779,7 +8940,7 @@ function sleepSync(milliseconds) {
|
|
|
7779
8940
|
const buffer = new Int32Array(new SharedArrayBuffer(4));
|
|
7780
8941
|
Atomics.wait(buffer, 0, 0, milliseconds);
|
|
7781
8942
|
}
|
|
7782
|
-
function artifactWarnings(artifacts) {
|
|
8943
|
+
function artifactWarnings(artifacts, sarif) {
|
|
7783
8944
|
const messages = [];
|
|
7784
8945
|
if (!artifacts.manifest) {
|
|
7785
8946
|
messages.push("manifest.json is disabled: `validate` and `report` cannot inspect this snapshot.");
|
|
@@ -7789,7 +8950,7 @@ function artifactWarnings(artifacts) {
|
|
|
7789
8950
|
"parts are disabled but navigation.md is enabled: its links point at part files that were not written."
|
|
7790
8951
|
);
|
|
7791
8952
|
}
|
|
7792
|
-
if (ARTIFACT_NAMES.every((name) => artifacts[name] === false)) {
|
|
8953
|
+
if (!sarif && ARTIFACT_NAMES.every((name) => artifacts[name] === false)) {
|
|
7793
8954
|
messages.push(
|
|
7794
8955
|
"every artifact is disabled: only the .codebase-snapshot ownership marker will be written."
|
|
7795
8956
|
);
|
|
@@ -7861,13 +9022,25 @@ function generateSnapshot(configInput, options) {
|
|
|
7861
9022
|
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
7862
9023
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
7863
9024
|
secretRules: config.secretRules,
|
|
9025
|
+
discovery: config.discovery,
|
|
7864
9026
|
// The output and staging directories this run is about to write, relative to the root. A
|
|
7865
9027
|
// first run cannot have marked them yet, so the name alone keeps a run from recording its own
|
|
7866
9028
|
// output — but only at these exact paths, not at every directory that shares the name.
|
|
7867
9029
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
7868
|
-
(directory) =>
|
|
7869
|
-
).filter((
|
|
9030
|
+
(directory) => path16__namespace.relative(config.cwd, path16__namespace.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
9031
|
+
).filter((relative8) => relative8.length > 0 && !relative8.startsWith(".."))
|
|
7870
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;
|
|
7871
9044
|
const files = [];
|
|
7872
9045
|
const monolithBodies = /* @__PURE__ */ new Map();
|
|
7873
9046
|
const skipped = maskSkipRecords([...collection.skipped], config.secretRules);
|
|
@@ -7878,6 +9051,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7878
9051
|
return files.some((file) => file.isPreviewTruncated && !file.isLarge);
|
|
7879
9052
|
};
|
|
7880
9053
|
const encodingNotes = [];
|
|
9054
|
+
const fileWarnings = [];
|
|
7881
9055
|
const oversizeSummaryPaths = [];
|
|
7882
9056
|
const previewSummaryPaths = [];
|
|
7883
9057
|
const layerCounts = {};
|
|
@@ -7885,13 +9059,13 @@ function generateSnapshot(configInput, options) {
|
|
|
7885
9059
|
let totalLines = 0;
|
|
7886
9060
|
let totalSizeBytes = 0;
|
|
7887
9061
|
let largeFileCount = 0;
|
|
7888
|
-
for (const relativePath of
|
|
7889
|
-
const absolutePath =
|
|
9062
|
+
for (const relativePath of candidateFiles) {
|
|
9063
|
+
const absolutePath = path16__namespace.join(config.cwd, relativePath);
|
|
7890
9064
|
let stats;
|
|
7891
9065
|
try {
|
|
7892
|
-
stats =
|
|
9066
|
+
stats = fs14__namespace.statSync(absolutePath);
|
|
7893
9067
|
if (!stats.isFile()) {
|
|
7894
|
-
const nestedRepository = stats.isDirectory() &&
|
|
9068
|
+
const nestedRepository = stats.isDirectory() && fs14__namespace.existsSync(path16__namespace.join(absolutePath, ".git"));
|
|
7895
9069
|
skipped.push(
|
|
7896
9070
|
nestedRepository ? {
|
|
7897
9071
|
path: relativePath,
|
|
@@ -7905,13 +9079,17 @@ function generateSnapshot(configInput, options) {
|
|
|
7905
9079
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
7906
9080
|
continue;
|
|
7907
9081
|
}
|
|
7908
|
-
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)) {
|
|
7909
9087
|
skipped.push({ path: relativePath, reason: "binary", detail: `Size: ${stats.size} B` });
|
|
7910
9088
|
continue;
|
|
7911
9089
|
}
|
|
7912
9090
|
let buffer;
|
|
7913
9091
|
try {
|
|
7914
|
-
buffer =
|
|
9092
|
+
buffer = fs14__namespace.readFileSync(absolutePath);
|
|
7915
9093
|
} catch {
|
|
7916
9094
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
7917
9095
|
continue;
|
|
@@ -7966,7 +9144,16 @@ function generateSnapshot(configInput, options) {
|
|
|
7966
9144
|
let finalContent = redacted;
|
|
7967
9145
|
let isPreviewTruncated = false;
|
|
7968
9146
|
let includedLines = lineCount;
|
|
7969
|
-
|
|
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") {
|
|
7970
9157
|
const preview = truncatePreview(
|
|
7971
9158
|
redacted,
|
|
7972
9159
|
config.previewHeadLines,
|
|
@@ -7975,8 +9162,9 @@ function generateSnapshot(configInput, options) {
|
|
|
7975
9162
|
finalContent = preview.content;
|
|
7976
9163
|
isPreviewTruncated = preview.isTruncated;
|
|
7977
9164
|
includedLines = preview.includedLines;
|
|
9165
|
+
inclusionMode = isPreviewTruncated ? "preview" : "full";
|
|
7978
9166
|
}
|
|
7979
|
-
if (config.includeMonolith && isPreviewTruncated) {
|
|
9167
|
+
if (config.includeMonolith && (isPreviewTruncated || inclusionMode === "signatures")) {
|
|
7980
9168
|
monolithBodies.set(relativePath, redacted);
|
|
7981
9169
|
}
|
|
7982
9170
|
let summary;
|
|
@@ -7991,14 +9179,19 @@ function generateSnapshot(configInput, options) {
|
|
|
7991
9179
|
summary = outcome?.summary ? maskSummaryStrings(outcome.summary, config.secretRules) : void 0;
|
|
7992
9180
|
summaryQuality = outcome?.quality;
|
|
7993
9181
|
if (isSourceSummary(summary) && summary.parseErrors) {
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
9182
|
+
fileWarnings.push({
|
|
9183
|
+
path: relativePath,
|
|
9184
|
+
message: `Parse errors in ${maskPathText(relativePath, config.secretRules)} (${summary.parseErrors}); its summary is incomplete.`
|
|
9185
|
+
});
|
|
7997
9186
|
}
|
|
7998
9187
|
} catch (err) {
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
9188
|
+
fileWarnings.push({
|
|
9189
|
+
path: relativePath,
|
|
9190
|
+
message: `Summary skipped for ${maskPathText(relativePath, config.secretRules)}: ${err.message}`.replace(
|
|
9191
|
+
/\s+/g,
|
|
9192
|
+
" "
|
|
9193
|
+
)
|
|
9194
|
+
});
|
|
8002
9195
|
}
|
|
8003
9196
|
}
|
|
8004
9197
|
files.push({
|
|
@@ -8010,7 +9203,7 @@ function generateSnapshot(configInput, options) {
|
|
|
8010
9203
|
layer: layer2,
|
|
8011
9204
|
kind,
|
|
8012
9205
|
language,
|
|
8013
|
-
inclusionMode
|
|
9206
|
+
inclusionMode,
|
|
8014
9207
|
isLarge,
|
|
8015
9208
|
isPreviewTruncated,
|
|
8016
9209
|
originalLines: lineCount,
|
|
@@ -8022,6 +9215,58 @@ function generateSnapshot(configInput, options) {
|
|
|
8022
9215
|
content: finalContent
|
|
8023
9216
|
});
|
|
8024
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
|
+
}
|
|
8025
9270
|
const encodingBuckets = /* @__PURE__ */ new Map();
|
|
8026
9271
|
for (const note of encodingNotes) {
|
|
8027
9272
|
const bucket = encodingBuckets.get(note.encoding) ?? { paths: [], invalidBytes: 0 };
|
|
@@ -8072,13 +9317,8 @@ function generateSnapshot(configInput, options) {
|
|
|
8072
9317
|
);
|
|
8073
9318
|
}
|
|
8074
9319
|
const parts = partitionByBudget(files, config, fingerprint);
|
|
8075
|
-
const maxEffectiveBudget = Math.max(
|
|
8076
|
-
config.maxPartBytes,
|
|
8077
|
-
config.budgets.default,
|
|
8078
|
-
...Object.values(config.budgets.byLayer)
|
|
8079
|
-
);
|
|
8080
9320
|
const truncatedPreviewCount = files.filter((file) => file.isPreviewTruncated).length;
|
|
8081
|
-
const validationErrors = verifyInMemory(files, parts
|
|
9321
|
+
const validationErrors = verifyInMemory(files, parts);
|
|
8082
9322
|
const totalContentBytes = files.reduce((total, file) => total + file.includedBytes, 0);
|
|
8083
9323
|
const maskName = (value) => maskPathText(value, config.secretRules);
|
|
8084
9324
|
const manifest = {
|
|
@@ -8098,12 +9338,26 @@ function generateSnapshot(configInput, options) {
|
|
|
8098
9338
|
config: {
|
|
8099
9339
|
digest: configDigest(config),
|
|
8100
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 },
|
|
8101
9344
|
largeFileBytes: config.largeFileBytes,
|
|
8102
9345
|
previewHeadLines: config.previewHeadLines,
|
|
8103
9346
|
previewTailLines: config.previewTailLines,
|
|
8104
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,
|
|
8105
9352
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
8106
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 } : {},
|
|
8107
9361
|
artifacts: { ...config.artifacts },
|
|
8108
9362
|
include: [...config.include].map(maskName),
|
|
8109
9363
|
preset: config.preset,
|
|
@@ -8135,6 +9389,9 @@ function generateSnapshot(configInput, options) {
|
|
|
8135
9389
|
skippedFiles: skipped.length,
|
|
8136
9390
|
totalSourceBytes: totalSizeBytes,
|
|
8137
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),
|
|
8138
9395
|
partCount: parts.length,
|
|
8139
9396
|
warningCount: 0,
|
|
8140
9397
|
redactedLines: 0
|
|
@@ -8173,7 +9430,9 @@ function generateSnapshot(configInput, options) {
|
|
|
8173
9430
|
);
|
|
8174
9431
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
8175
9432
|
}
|
|
8176
|
-
for (const message of artifactWarnings(config.artifacts))
|
|
9433
|
+
for (const message of artifactWarnings(config.artifacts, config.sarif === true)) {
|
|
9434
|
+
manifest.warnings.push(message);
|
|
9435
|
+
}
|
|
8177
9436
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
8178
9437
|
let projectTree = null;
|
|
8179
9438
|
if (config.artifacts.projectTree) {
|
|
@@ -8186,6 +9445,13 @@ function generateSnapshot(configInput, options) {
|
|
|
8186
9445
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
8187
9446
|
}
|
|
8188
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
|
+
}
|
|
8189
9455
|
let monolithJson = null;
|
|
8190
9456
|
let monolithMarkdown = null;
|
|
8191
9457
|
if (config.includeMonolith) {
|
|
@@ -8236,17 +9502,17 @@ function generateSnapshot(configInput, options) {
|
|
|
8236
9502
|
const lockKeys = Array.from(/* @__PURE__ */ new Set([tmpDir, outDir])).sort();
|
|
8237
9503
|
const releases = lockKeys.map((key) => acquireRunLock(key));
|
|
8238
9504
|
const releaseLock = () => {
|
|
8239
|
-
for (const release of releases.reverse()) release();
|
|
9505
|
+
for (const release of [...releases].reverse()) release();
|
|
8240
9506
|
};
|
|
8241
9507
|
let stagingDir = null;
|
|
8242
9508
|
try {
|
|
8243
9509
|
ensureParentDirectory(outDir, "outDir");
|
|
8244
|
-
|
|
9510
|
+
fs14__namespace.mkdirSync(tmpDir, { recursive: true });
|
|
8245
9511
|
cleanStaleRunDirs(tmpDir);
|
|
8246
|
-
const snapshotId =
|
|
9512
|
+
const snapshotId = crypto2__namespace.randomUUID();
|
|
8247
9513
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8248
|
-
stagingDir =
|
|
8249
|
-
|
|
9514
|
+
stagingDir = path16__namespace.join(tmpDir, `${RUN_DIR_PREFIX}${process.pid}-${snapshotId.slice(0, 8)}`);
|
|
9515
|
+
fs14__namespace.mkdirSync(stagingDir);
|
|
8250
9516
|
writeOwnershipMarker(stagingDir, {
|
|
8251
9517
|
generator: GENERATOR_NAME,
|
|
8252
9518
|
version: GENERATOR_VERSION,
|
|
@@ -8256,41 +9522,44 @@ function generateSnapshot(configInput, options) {
|
|
|
8256
9522
|
id: snapshotId,
|
|
8257
9523
|
pid: process.pid
|
|
8258
9524
|
});
|
|
8259
|
-
const partsDir =
|
|
8260
|
-
const reportsDir =
|
|
8261
|
-
if (artifacts.parts)
|
|
8262
|
-
if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
|
|
8263
|
-
|
|
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 });
|
|
8264
9530
|
}
|
|
8265
9531
|
if (artifacts.parts) {
|
|
8266
9532
|
for (const part of parts) {
|
|
8267
|
-
|
|
9533
|
+
fs14__namespace.writeFileSync(path16__namespace.join(partsDir, part.filename), part.markdown, "utf8");
|
|
8268
9534
|
}
|
|
8269
9535
|
}
|
|
8270
9536
|
if (projectTree !== null) {
|
|
8271
|
-
|
|
9537
|
+
fs14__namespace.writeFileSync(path16__namespace.join(reportsDir, "project-tree.md"), projectTree, "utf8");
|
|
8272
9538
|
}
|
|
8273
9539
|
if (artifacts.navigation) {
|
|
8274
|
-
|
|
9540
|
+
fs14__namespace.writeFileSync(path16__namespace.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
|
|
8275
9541
|
}
|
|
8276
9542
|
if (artifacts.audit) {
|
|
8277
|
-
|
|
8278
|
-
|
|
9543
|
+
fs14__namespace.writeFileSync(
|
|
9544
|
+
path16__namespace.join(reportsDir, "audit.md"),
|
|
8279
9545
|
generateAuditReport(manifest, secretFindings, config.secretRules),
|
|
8280
9546
|
"utf8"
|
|
8281
9547
|
);
|
|
8282
9548
|
}
|
|
9549
|
+
if (sarifText !== null) {
|
|
9550
|
+
fs14__namespace.writeFileSync(path16__namespace.join(reportsDir, "audit.sarif"), sarifText, "utf8");
|
|
9551
|
+
}
|
|
8283
9552
|
if (artifacts.readme) {
|
|
8284
|
-
|
|
9553
|
+
fs14__namespace.writeFileSync(path16__namespace.join(stagingDir, "README.md"), generateReadme(manifest, artifacts), "utf8");
|
|
8285
9554
|
}
|
|
8286
9555
|
if (artifacts.manifest) {
|
|
8287
|
-
|
|
9556
|
+
fs14__namespace.writeFileSync(path16__namespace.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
8288
9557
|
}
|
|
8289
9558
|
if (monolithJson !== null) {
|
|
8290
|
-
|
|
9559
|
+
fs14__namespace.writeFileSync(path16__namespace.join(stagingDir, "full-codebase.json"), monolithJson, "utf8");
|
|
8291
9560
|
}
|
|
8292
9561
|
if (monolithMarkdown !== null) {
|
|
8293
|
-
|
|
9562
|
+
fs14__namespace.writeFileSync(path16__namespace.join(stagingDir, "full-codebase.md"), monolithMarkdown, "utf8");
|
|
8294
9563
|
}
|
|
8295
9564
|
writeOwnershipMarker(stagingDir, markerPayload(stagingDir, "complete", snapshotId, createdAt));
|
|
8296
9565
|
swapIntoPlace(stagingDir, outDir);
|
|
@@ -8298,31 +9567,41 @@ function generateSnapshot(configInput, options) {
|
|
|
8298
9567
|
const warningsBeforeHook = manifest.warnings.length;
|
|
8299
9568
|
const pendingHook = runAfterGenerate(config, manifest);
|
|
8300
9569
|
const shipHookState = () => {
|
|
9570
|
+
if (!markerBelongsTo(outDir, snapshotId)) return;
|
|
8301
9571
|
if (artifacts.manifest && manifest.warnings.length !== warningsBeforeHook) {
|
|
8302
|
-
writeManifestAtomically(
|
|
9572
|
+
writeManifestAtomically(path16__namespace.join(outDir, "manifest.json"), manifest);
|
|
8303
9573
|
}
|
|
8304
9574
|
writeOwnershipMarker(outDir, markerPayload(outDir, "complete", snapshotId, createdAt));
|
|
8305
9575
|
};
|
|
8306
9576
|
shipHookState();
|
|
8307
9577
|
if (pendingHook) {
|
|
8308
|
-
pendingHook.
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
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
|
+
}
|
|
8318
9596
|
}
|
|
8319
|
-
|
|
9597
|
+
);
|
|
9598
|
+
pendingHooks.set(outDir, pendingHook);
|
|
8320
9599
|
}
|
|
8321
9600
|
return manifest;
|
|
8322
9601
|
} catch (err) {
|
|
8323
9602
|
if (stagingDir !== null) {
|
|
8324
9603
|
try {
|
|
8325
|
-
|
|
9604
|
+
fs14__namespace.rmSync(stagingDir, { recursive: true, force: true });
|
|
8326
9605
|
} catch {
|
|
8327
9606
|
}
|
|
8328
9607
|
}
|
|
@@ -8332,19 +9611,31 @@ function generateSnapshot(configInput, options) {
|
|
|
8332
9611
|
releaseLock();
|
|
8333
9612
|
}
|
|
8334
9613
|
}
|
|
9614
|
+
var pendingHooks = /* @__PURE__ */ new Map();
|
|
8335
9615
|
async function generateSnapshotAsync(configInput, options) {
|
|
8336
|
-
|
|
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;
|
|
8337
9625
|
}
|
|
8338
9626
|
|
|
8339
9627
|
exports.ARTIFACT_NAMES = ARTIFACT_NAMES;
|
|
8340
9628
|
exports.CHECK_INTERVAL_MS = CHECK_INTERVAL_MS;
|
|
9629
|
+
exports.CONFIG_KEYS = CONFIG_KEYS;
|
|
8341
9630
|
exports.DEFAULT_ANSWERS = DEFAULT_ANSWERS;
|
|
8342
9631
|
exports.DEFAULT_SECRET_PATTERNS = DEFAULT_SECRET_PATTERNS;
|
|
8343
9632
|
exports.FAILURE_INTERVAL_MS = FAILURE_INTERVAL_MS;
|
|
8344
9633
|
exports.GENERATOR_NAME = GENERATOR_NAME;
|
|
8345
9634
|
exports.GENERATOR_VERSION = GENERATOR_VERSION;
|
|
9635
|
+
exports.HOOK_MARKER = HOOK_MARKER;
|
|
8346
9636
|
exports.LANGUAGE_PROFILES = LANGUAGE_PROFILES;
|
|
8347
9637
|
exports.MIN_MAX_PART_BYTES = MIN_MAX_PART_BYTES;
|
|
9638
|
+
exports.MIN_MAX_PART_TOKENS = MIN_MAX_PART_TOKENS;
|
|
8348
9639
|
exports.PACKAGE_LATEST_URL = PACKAGE_LATEST_URL;
|
|
8349
9640
|
exports.PRESETS = PRESETS;
|
|
8350
9641
|
exports.PromptBack = PromptBack;
|
|
@@ -8354,30 +9645,47 @@ exports.SCHEMA_VERSION = SCHEMA_VERSION;
|
|
|
8354
9645
|
exports.anchorForPath = anchorForPath;
|
|
8355
9646
|
exports.assertReplaceable = assertReplaceable;
|
|
8356
9647
|
exports.assertUsableConfigInput = assertUsableConfigInput;
|
|
9648
|
+
exports.buildImportResolver = buildImportResolver;
|
|
9649
|
+
exports.bytesForTokens = bytesForTokens;
|
|
8357
9650
|
exports.cacheDirectory = cacheDirectory;
|
|
8358
9651
|
exports.cacheFilePath = cacheFilePath;
|
|
8359
9652
|
exports.checkForUpdate = checkForUpdate;
|
|
9653
|
+
exports.classifyKind = classifyKind;
|
|
9654
|
+
exports.classifyLayer = classifyLayer;
|
|
8360
9655
|
exports.collectFiles = collectFiles;
|
|
8361
9656
|
exports.compareManifests = compareManifests;
|
|
9657
|
+
exports.configDigest = configDigest;
|
|
8362
9658
|
exports.countInvalidUtf8Bytes = countInvalidUtf8Bytes;
|
|
8363
9659
|
exports.countLines = countLines;
|
|
8364
9660
|
exports.decodeText = decodeText;
|
|
8365
9661
|
exports.defineConfig = defineConfig;
|
|
8366
9662
|
exports.detectLanguage = detectLanguage;
|
|
8367
9663
|
exports.detectPreset = detectPreset;
|
|
9664
|
+
exports.displayPath = displayPath;
|
|
8368
9665
|
exports.displayWidth = displayWidth;
|
|
9666
|
+
exports.enforceBudget = enforceBudget;
|
|
9667
|
+
exports.estimateTokens = estimateTokens;
|
|
8369
9668
|
exports.explainPath = explainPath;
|
|
8370
9669
|
exports.fenceForContent = fenceForContent;
|
|
8371
9670
|
exports.fenceForText = fenceForText;
|
|
8372
9671
|
exports.fetchLatestVersion = fetchLatestVersion;
|
|
9672
|
+
exports.findSecretInPath = findSecretInPath;
|
|
8373
9673
|
exports.firstRunMessage = firstRunMessage;
|
|
9674
|
+
exports.formatTokens = formatTokens;
|
|
8374
9675
|
exports.formatUpdateNotice = formatUpdateNotice;
|
|
8375
9676
|
exports.generateProjectTree = generateProjectTree;
|
|
9677
|
+
exports.generateSarifReport = generateSarifReport;
|
|
8376
9678
|
exports.generateSnapshot = generateSnapshot;
|
|
8377
9679
|
exports.generateSnapshotAsync = generateSnapshotAsync;
|
|
9680
|
+
exports.getGitInfo = getGitInfo;
|
|
8378
9681
|
exports.getPreset = getPreset;
|
|
8379
9682
|
exports.globToRegExp = globToRegExp;
|
|
8380
9683
|
exports.hasPriorSnapshot = hasPriorSnapshot;
|
|
9684
|
+
exports.importsChangedFile = importsChangedFile;
|
|
9685
|
+
exports.importsOfFile = importsOfFile;
|
|
9686
|
+
exports.importsOfText = importsOfText;
|
|
9687
|
+
exports.installKind = installKind;
|
|
9688
|
+
exports.installPreCommitHook = installPreCommitHook;
|
|
8381
9689
|
exports.isBinaryBuffer = isBinaryBuffer;
|
|
8382
9690
|
exports.isBinaryExtension = isBinaryExtension;
|
|
8383
9691
|
exports.isBinaryFile = isBinaryFile;
|
|
@@ -8386,10 +9694,13 @@ exports.isInteractive = isInteractive;
|
|
|
8386
9694
|
exports.isNewer = isNewer;
|
|
8387
9695
|
exports.isResolvedConfig = isResolvedConfig;
|
|
8388
9696
|
exports.isSensitivePath = isSensitivePath;
|
|
9697
|
+
exports.isSkeletonizable = isSkeletonizable;
|
|
9698
|
+
exports.isTsxFileName = isTsxFileName;
|
|
8389
9699
|
exports.listPresetNames = listPresetNames;
|
|
8390
9700
|
exports.loadConfigFile = loadConfigFile;
|
|
8391
9701
|
exports.loadConfigFileDetailed = loadConfigFileDetailed;
|
|
8392
9702
|
exports.loadManifest = loadManifest;
|
|
9703
|
+
exports.lockPathFor = lockPathFor;
|
|
8393
9704
|
exports.markResolvedConfig = markResolvedConfig;
|
|
8394
9705
|
exports.matchRule = matchRule;
|
|
8395
9706
|
exports.matchesExclude = matchesExclude;
|
|
@@ -8402,11 +9713,14 @@ exports.renderConfig = renderConfig;
|
|
|
8402
9713
|
exports.renderDiffText = renderDiffText;
|
|
8403
9714
|
exports.renderExplainText = renderExplainText;
|
|
8404
9715
|
exports.renumberParts = renumberParts;
|
|
9716
|
+
exports.resolveChangedScope = resolveChangedScope;
|
|
8405
9717
|
exports.resolveConfig = resolveConfig;
|
|
8406
9718
|
exports.resolveSafeOutputDirs = resolveSafeOutputDirs;
|
|
9719
|
+
exports.resolveSpecifier = resolveSpecifier;
|
|
8407
9720
|
exports.runInit = runInit;
|
|
8408
9721
|
exports.scanAndRedactSecrets = scanAndRedactSecrets;
|
|
8409
9722
|
exports.shouldCheck = shouldCheck;
|
|
9723
|
+
exports.skeletonizeTypeScript = skeletonizeTypeScript;
|
|
8410
9724
|
exports.stripJsonComments = stripJsonComments;
|
|
8411
9725
|
exports.summarizeJson = summarizeJson;
|
|
8412
9726
|
exports.summarizeMarkdown = summarizeMarkdown;
|
|
@@ -8415,7 +9729,7 @@ exports.summarizeWithProfile = summarizeWithProfile;
|
|
|
8415
9729
|
exports.supportsTypeScriptImports = supportsTypeScriptImports;
|
|
8416
9730
|
exports.truncatePreview = truncatePreview;
|
|
8417
9731
|
exports.truncateToBytes = truncateToBytes;
|
|
9732
|
+
exports.updateCommand = updateCommand;
|
|
8418
9733
|
exports.validateSnapshot = validateSnapshot;
|
|
9734
|
+
exports.workerEntry = workerEntry;
|
|
8419
9735
|
exports.wrapLines = wrapLines;
|
|
8420
|
-
//# sourceMappingURL=index.cjs.map
|
|
8421
|
-
//# sourceMappingURL=index.cjs.map
|