codebase-snapshot 0.9.12 → 0.9.14
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 +379 -0
- package/README.md +1 -1
- package/dist/bin.js +1313 -633
- package/dist/{chunk-3H45YMBG.js → chunk-DUZK5VL2.js} +1413 -767
- package/dist/{chunk-5OR3WVPW.js → chunk-J4W74N4Y.js} +144 -44
- package/dist/cli.cjs +1305 -628
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +43 -54
- package/dist/{generate-worker-D5Xgh27c.d.cts → generate-worker-D_mUjgXw.d.cts} +21 -5
- package/dist/{generate-worker-D5Xgh27c.d.ts → generate-worker-D_mUjgXw.d.ts} +21 -5
- package/dist/index.cjs +1389 -760
- package/dist/index.d.cts +58 -35
- package/dist/index.d.ts +58 -35
- package/dist/index.js +1 -1
- package/dist/mcp.cjs +973 -405
- package/dist/mcp.d.cts +67 -67
- package/dist/mcp.d.ts +67 -67
- package/dist/mcp.js +2 -2
- package/dist/{types-CTewen3J.d.cts → types-DIX6zWSt.d.cts} +15 -0
- package/dist/{types-CTewen3J.d.ts → types-DIX6zWSt.d.ts} +15 -0
- package/docs/cli.md +8 -6
- package/docs/configuration.md +44 -10
- package/docs/mcp.md +16 -3
- package/docs/output.md +13 -0
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -11,6 +11,7 @@ var ts = require('typescript');
|
|
|
11
11
|
var readline = require('readline');
|
|
12
12
|
var async_hooks = require('async_hooks');
|
|
13
13
|
var os = require('os');
|
|
14
|
+
var http = require('http');
|
|
14
15
|
var https = require('https');
|
|
15
16
|
var worker_threads = require('worker_threads');
|
|
16
17
|
|
|
@@ -42,6 +43,7 @@ var crypto3__namespace = /*#__PURE__*/_interopNamespace(crypto3);
|
|
|
42
43
|
var ts__default = /*#__PURE__*/_interopDefault(ts);
|
|
43
44
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
44
45
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
46
|
+
var http__namespace = /*#__PURE__*/_interopNamespace(http);
|
|
45
47
|
var https__namespace = /*#__PURE__*/_interopNamespace(https);
|
|
46
48
|
|
|
47
49
|
// src/cli.ts
|
|
@@ -1281,9 +1283,18 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
1281
1283
|
// The unquoted twin for YAML, docker-compose and `.env`: those formats never
|
|
1282
1284
|
// quote their values, so the rules above cannot see them. `valueGroup` keeps
|
|
1283
1285
|
// `POSTGRES_PASSWORD:` in the output.
|
|
1286
|
+
//
|
|
1287
|
+
// This is the one unquoted rule without `NOT_AFTER_QUOTE`, and the lookbehind is a
|
|
1288
|
+
// string-literal guard rather than a precision rule: a credential assignment *inside* a
|
|
1289
|
+
// literal — `const dsn = "password=hunter2;host=db"`, `curl -d "password=hunter2"` — was
|
|
1290
|
+
// rejected before it could be considered, and a DSN is a credential in a string. What made the
|
|
1291
|
+
// lookbehind redundant is the shape of `UNQUOTED_LITERAL`: a value has to start with a word
|
|
1292
|
+
// character, so `["awsSecret:", "apiKey:"]` — the list of key names the lookbehind was added
|
|
1293
|
+
// for — cannot match here anyway. `redact.ts` still refuses a match whose separator is inside
|
|
1294
|
+
// the literal, which is the case that list actually is.
|
|
1284
1295
|
rule: "credential-assignment-unquoted",
|
|
1285
1296
|
pattern: new RegExp(
|
|
1286
|
-
`${
|
|
1297
|
+
`${UNQUOTED_STRONG_NAME}\\s{0,8}${ASSIGN}\\s{0,8}${NOT_A_LITERAL_VALUE}${UNQUOTED_LITERAL}`,
|
|
1287
1298
|
"i"
|
|
1288
1299
|
),
|
|
1289
1300
|
valueGroup: 1
|
|
@@ -1318,6 +1329,27 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
1318
1329
|
pattern: /(?<!["'])(?:api[_-]?key|secret[_-]?key|auth[_-]?token|client[_-]?secret|access[_-]?token|refresh[_-]?token|private[_-]?key|password|passwd|pwd)["']?\s*(?<![=!<>])[:=](?!=)\s*["']([^"'\s]{8,256})["']/i,
|
|
1319
1330
|
valueGroup: 1
|
|
1320
1331
|
},
|
|
1332
|
+
{
|
|
1333
|
+
// `${VAR:-fallback}` — a default value in a shell script, a `.env`, docker-compose or a
|
|
1334
|
+
// GitHub Actions workflow.
|
|
1335
|
+
//
|
|
1336
|
+
// This shape defeated every rule above it, and each refusal was individually reasonable:
|
|
1337
|
+
// the quoted rules need a quote after the separator, and the unquoted ones need the value to
|
|
1338
|
+
// begin with a word character — a fallback begins with `$`. So
|
|
1339
|
+
// `POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-MyProdPassword123!}` was emitted whole, `.env` files
|
|
1340
|
+
// and compose files included, which is where that idiom is the norm.
|
|
1341
|
+
//
|
|
1342
|
+
// The **fallback** is what gets masked, not the whole expansion: the variable name is the
|
|
1343
|
+
// readable context, exactly as `POSTGRES_PASSWORD:` is kept by the rule above. A single
|
|
1344
|
+
// interpolation with no operator (`${DB_PASSWORD}`) still does not match — there is nothing
|
|
1345
|
+
// between the braces to mask, and `isTemplateInterpolation` keeps such a value readable.
|
|
1346
|
+
rule: "interpolation-fallback-secret",
|
|
1347
|
+
pattern: new RegExp(
|
|
1348
|
+
`(?:["']${STRONG_CREDENTIAL_NAME}["']|${NOT_AFTER_QUOTE}${STRONG_CREDENTIAL_NAME})\\s{0,8}${ASSIGN}\\s{0,8}["'\`]?\\$\\{[A-Za-z_][A-Za-z0-9_]*:?[-=?+]([^}"'\`\\r\\n]{2,512})\\}["'\`]?`,
|
|
1349
|
+
"i"
|
|
1350
|
+
),
|
|
1351
|
+
valueGroup: 1
|
|
1352
|
+
},
|
|
1321
1353
|
{
|
|
1322
1354
|
// Covers `.env` / shell assignment syntax, which never quotes its values and
|
|
1323
1355
|
// was therefore invisible to the rule above. Anchored to the line start and
|
|
@@ -1345,8 +1377,8 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
1345
1377
|
valueGroup: 1
|
|
1346
1378
|
}
|
|
1347
1379
|
];
|
|
1348
|
-
var PEM_BLOCK_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?-----END (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/g;
|
|
1349
|
-
var PEM_UNTERMINATED_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:\r
|
|
1380
|
+
var PEM_BLOCK_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:(?!-----BEGIN)[\s\S])*?-----END (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/g;
|
|
1381
|
+
var PEM_UNTERMINATED_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:\r\n|\n|\r)(?:(?:[ \t]*(?:Proc-Type|DEK-Info|Comment|Version):[^\r\n]*|[ \t]*)(?:\r\n|\n|\r))*(?:[ \t]*[A-Za-z0-9+/=]{8,}[ \t]*(?:\r\n|\n|\r|$))+/g;
|
|
1350
1382
|
|
|
1351
1383
|
// src/security/redact.ts
|
|
1352
1384
|
var REDACTION_MARKER = "[REDACTED: potential secret-like value omitted from snapshot]";
|
|
@@ -1440,6 +1472,20 @@ function toGlobal(rule) {
|
|
|
1440
1472
|
return new RegExp(rule.pattern.source, [...flags].join(""));
|
|
1441
1473
|
}
|
|
1442
1474
|
var LINE_BREAK = /(\r\n|\n|\r)/;
|
|
1475
|
+
var LINE_TERMINATOR = /\r\n|\n|\r/g;
|
|
1476
|
+
var COMPILED_RULES = /* @__PURE__ */ new WeakMap();
|
|
1477
|
+
function compiledRuleFor(rule) {
|
|
1478
|
+
let compiled = COMPILED_RULES.get(rule);
|
|
1479
|
+
if (compiled === void 0) {
|
|
1480
|
+
compiled = {
|
|
1481
|
+
rule: rule.rule,
|
|
1482
|
+
pattern: toGlobal(rule),
|
|
1483
|
+
...rule.valueGroup === void 0 ? {} : { valueGroup: rule.valueGroup }
|
|
1484
|
+
};
|
|
1485
|
+
COMPILED_RULES.set(rule, compiled);
|
|
1486
|
+
}
|
|
1487
|
+
return compiled;
|
|
1488
|
+
}
|
|
1443
1489
|
function scanAndRedactSecrets(content, rules, filePath) {
|
|
1444
1490
|
const findings = [];
|
|
1445
1491
|
let redacted = content;
|
|
@@ -1451,18 +1497,15 @@ function scanAndRedactSecrets(content, rules, filePath) {
|
|
|
1451
1497
|
blockPattern.lastIndex += 1;
|
|
1452
1498
|
continue;
|
|
1453
1499
|
}
|
|
1454
|
-
const
|
|
1455
|
-
const
|
|
1500
|
+
const before = redacted.slice(0, match.index);
|
|
1501
|
+
const line = (before.match(LINE_TERMINATOR) ?? []).length + 1;
|
|
1502
|
+
const newlines = (match[0].match(LINE_TERMINATOR) ?? []).length;
|
|
1456
1503
|
findings.push({ rules: ["private-key-block"], line, path: filePath });
|
|
1457
|
-
redacted =
|
|
1504
|
+
redacted = before + PEM_REDACTION_MARKER + "\n".repeat(newlines) + redacted.slice(match.index + match[0].length);
|
|
1458
1505
|
blockPattern.lastIndex = match.index + PEM_REDACTION_MARKER.length + newlines;
|
|
1459
1506
|
}
|
|
1460
1507
|
}
|
|
1461
|
-
const compiled = rules.map(
|
|
1462
|
-
rule: rule.rule,
|
|
1463
|
-
pattern: toGlobal(rule),
|
|
1464
|
-
...rule.valueGroup === void 0 ? {} : { valueGroup: rule.valueGroup }
|
|
1465
|
-
}));
|
|
1508
|
+
const compiled = rules.map(compiledRuleFor);
|
|
1466
1509
|
const segments = redacted.split(LINE_BREAK);
|
|
1467
1510
|
let lineNumber = 1;
|
|
1468
1511
|
for (let index = 0; index < segments.length; index += 2) {
|
|
@@ -1480,8 +1523,10 @@ function scanAndRedactSecrets(content, rules, filePath) {
|
|
|
1480
1523
|
}
|
|
1481
1524
|
const group = valueGroup === void 0 ? void 0 : match.indices?.[valueGroup];
|
|
1482
1525
|
if (group && isSelfNamingConstant(line, group[0], group[1])) continue;
|
|
1483
|
-
if (valueGroup !== void 0 && startsInsideStringLiteral(line, match.index))
|
|
1484
|
-
|
|
1526
|
+
if (valueGroup !== void 0 && startsInsideStringLiteral(line, match.index, group?.[0])) {
|
|
1527
|
+
continue;
|
|
1528
|
+
}
|
|
1529
|
+
if (group && isUnquotedValue(line, group[0]) && isDeclaredBinding(line, match.index, filePath)) {
|
|
1485
1530
|
continue;
|
|
1486
1531
|
}
|
|
1487
1532
|
if (group && isTemplateInterpolation(line, group[0], group[1])) continue;
|
|
@@ -1514,14 +1559,20 @@ function expandOverToken(line, start, end) {
|
|
|
1514
1559
|
while (right < line.length && TOKEN_CHARS_RIGHT.test(line[right])) right += 1;
|
|
1515
1560
|
return { start: left, end: right };
|
|
1516
1561
|
}
|
|
1517
|
-
function startsInsideStringLiteral(line, matchIndex) {
|
|
1562
|
+
function startsInsideStringLiteral(line, matchIndex, valueStart) {
|
|
1518
1563
|
let runStart = matchIndex;
|
|
1519
1564
|
while (runStart > 0 && /[A-Za-z0-9_-]/.test(line[runStart - 1])) runStart -= 1;
|
|
1520
1565
|
const before = runStart > 0 ? line[runStart - 1] : "";
|
|
1521
|
-
|
|
1566
|
+
if (before !== '"' && before !== "'" && before !== "`") return false;
|
|
1567
|
+
if (valueStart === void 0) return true;
|
|
1568
|
+
return /["'`]/.test(line.slice(matchIndex, valueStart));
|
|
1522
1569
|
}
|
|
1523
1570
|
function isTemplateInterpolation(line, start, end) {
|
|
1524
|
-
|
|
1571
|
+
if (line[start] !== "$" || line[start + 1] !== "{" || line[end - 1] !== "}") {
|
|
1572
|
+
return false;
|
|
1573
|
+
}
|
|
1574
|
+
const inner = line.slice(start + 2, end - 1).trim();
|
|
1575
|
+
return /^[A-Za-z_$][A-Za-z0-9_$.()[\]]*$/.test(inner);
|
|
1525
1576
|
}
|
|
1526
1577
|
function isSelfNamingConstant(line, start, end) {
|
|
1527
1578
|
const value = line.slice(start, end);
|
|
@@ -1546,9 +1597,19 @@ function isUnquotedValue(line, start) {
|
|
|
1546
1597
|
const before = start > 0 ? line[start - 1] : "";
|
|
1547
1598
|
return before !== '"' && before !== "'" && before !== "`";
|
|
1548
1599
|
}
|
|
1549
|
-
var DECLARATION_KEYWORDS = /\b(?:const|let|var|readonly|public|private|protected|static|
|
|
1600
|
+
var DECLARATION_KEYWORDS = /\b(?:const|let|var|readonly|public|private|protected|static|using)\s+$/;
|
|
1601
|
+
var ASSIGNMENT_FILE = /\.(?:sh|bash|zsh|ksh|fish|env|ya?ml|toml|ini|conf|cfg|properties|tf|tfvars|service)$/i;
|
|
1602
|
+
var ASSIGNMENT_BASENAME = /^(?:\.env(?:\.[^/]*)?|\.envrc|\.profile|\.bashrc|\.zshrc|\.bash_profile)$/i;
|
|
1603
|
+
function isAssignmentFile(filePath) {
|
|
1604
|
+
if (filePath === void 0) return false;
|
|
1605
|
+
const normalised = filePath.replace(/\\/g, "/");
|
|
1606
|
+
if (ASSIGNMENT_FILE.test(normalised)) return true;
|
|
1607
|
+
const base = normalised.slice(normalised.lastIndexOf("/") + 1);
|
|
1608
|
+
return ASSIGNMENT_BASENAME.test(base);
|
|
1609
|
+
}
|
|
1550
1610
|
var DECLARATION_WINDOW = 40;
|
|
1551
|
-
function isDeclaredBinding(line, matchIndex) {
|
|
1611
|
+
function isDeclaredBinding(line, matchIndex, filePath) {
|
|
1612
|
+
if (isAssignmentFile(filePath)) return false;
|
|
1552
1613
|
let runStart = matchIndex;
|
|
1553
1614
|
while (runStart > 0 && /[A-Za-z0-9_$]/.test(line[runStart - 1])) runStart -= 1;
|
|
1554
1615
|
const from = Math.max(0, runStart - DECLARATION_WINDOW);
|
|
@@ -1590,6 +1651,15 @@ function readLinkTarget(linkPath, fallback) {
|
|
|
1590
1651
|
return fallback;
|
|
1591
1652
|
}
|
|
1592
1653
|
}
|
|
1654
|
+
function brokenLinkRecord(linkPath, relativePath, rootReal) {
|
|
1655
|
+
const rawTarget = readLinkTarget(linkPath, linkPath);
|
|
1656
|
+
const resolvedFallback = path16__namespace.resolve(path16__namespace.dirname(linkPath), rawTarget);
|
|
1657
|
+
return isInside(rootReal, resolvedFallback) ? { path: relativePath, reason: "read-error", detail: "broken symlink" } : {
|
|
1658
|
+
path: relativePath,
|
|
1659
|
+
reason: "outside-root",
|
|
1660
|
+
detail: `link target resolves outside the scanned root (target: ${rawTarget}); not followed`
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1593
1663
|
function isInside(root, candidate) {
|
|
1594
1664
|
const relative12 = path16__namespace.relative(root, candidate);
|
|
1595
1665
|
return relative12 === "" || !relative12.startsWith(`..${path16__namespace.sep}`) && relative12 !== ".." && !path16__namespace.isAbsolute(relative12);
|
|
@@ -1679,6 +1749,13 @@ function isSensitivePath(relativePath) {
|
|
|
1679
1749
|
return matchesSensitiveList(posixPath, base);
|
|
1680
1750
|
}
|
|
1681
1751
|
var GLOBAL_PATTERNS = /* @__PURE__ */ new WeakMap();
|
|
1752
|
+
var GLOB_REGEX_CACHE = /* @__PURE__ */ new Map();
|
|
1753
|
+
var GITIGNORE_REGEX_CACHE = /* @__PURE__ */ new Map();
|
|
1754
|
+
var REGEX_CACHE_LIMIT = 4096;
|
|
1755
|
+
function rememberCompiled(cache, key, value) {
|
|
1756
|
+
if (cache.size >= REGEX_CACHE_LIMIT) cache.clear();
|
|
1757
|
+
cache.set(key, value);
|
|
1758
|
+
}
|
|
1682
1759
|
function globalPattern(pattern) {
|
|
1683
1760
|
let compiled = GLOBAL_PATTERNS.get(pattern);
|
|
1684
1761
|
if (compiled === void 0) {
|
|
@@ -1689,8 +1766,8 @@ function globalPattern(pattern) {
|
|
|
1689
1766
|
}
|
|
1690
1767
|
var DEFAULT_PATH_RULES = [...DEFAULT_SECRET_PATTERNS];
|
|
1691
1768
|
function pathScanRules(extraRules) {
|
|
1692
|
-
if (extraRules === void 0
|
|
1693
|
-
return
|
|
1769
|
+
if (extraRules === void 0) return DEFAULT_PATH_RULES;
|
|
1770
|
+
return extraRules;
|
|
1694
1771
|
}
|
|
1695
1772
|
function findSecretInPath(relativePath, extraRules) {
|
|
1696
1773
|
const posixPath = relativePath.replace(/\\/g, "/");
|
|
@@ -1739,11 +1816,22 @@ function maskSkipPaths(records, extraRules) {
|
|
|
1739
1816
|
});
|
|
1740
1817
|
}
|
|
1741
1818
|
function globToRegExp(glob) {
|
|
1819
|
+
const cached = GLOB_REGEX_CACHE.get(glob);
|
|
1820
|
+
if (cached !== void 0) return cached;
|
|
1742
1821
|
const normalized = glob.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
1743
1822
|
const anchored = normalized.startsWith("/");
|
|
1744
1823
|
const body = anchored ? normalized.slice(1) : normalized;
|
|
1745
1824
|
const prefix = anchored || body.includes("/") ? "^" : "(?:^|.*/)";
|
|
1746
|
-
|
|
1825
|
+
let compiled;
|
|
1826
|
+
try {
|
|
1827
|
+
compiled = new RegExp(`${prefix}${globBodyToRegExp(body, true)}(?:/.*)?$`);
|
|
1828
|
+
} catch (error) {
|
|
1829
|
+
throw new Error(
|
|
1830
|
+
`"${glob}" is not a valid glob: ${error.message}. A character class must be ascending, as in "[a-z]".`
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
rememberCompiled(GLOB_REGEX_CACHE, glob, compiled);
|
|
1834
|
+
return compiled;
|
|
1747
1835
|
}
|
|
1748
1836
|
function globBodyToRegExp(body, allowBraces) {
|
|
1749
1837
|
let out = "";
|
|
@@ -1855,6 +1943,14 @@ function escapeRegExpChar(character) {
|
|
|
1855
1943
|
return /[\\^$.*+?()[\]{}|/]/.test(character) ? `\\${character}` : character;
|
|
1856
1944
|
}
|
|
1857
1945
|
function matchGitignorePattern(target, pattern) {
|
|
1946
|
+
let compiled = GITIGNORE_REGEX_CACHE.get(pattern);
|
|
1947
|
+
if (compiled === void 0) {
|
|
1948
|
+
compiled = compileGitignorePattern(pattern);
|
|
1949
|
+
rememberCompiled(GITIGNORE_REGEX_CACHE, pattern, compiled);
|
|
1950
|
+
}
|
|
1951
|
+
return compiled.test(target);
|
|
1952
|
+
}
|
|
1953
|
+
function compileGitignorePattern(pattern) {
|
|
1858
1954
|
const anchored = pattern.startsWith("/");
|
|
1859
1955
|
const body = anchored ? pattern.slice(1) : pattern;
|
|
1860
1956
|
let source = "";
|
|
@@ -1903,7 +1999,11 @@ function matchGitignorePattern(target, pattern) {
|
|
|
1903
1999
|
source += escapeRegExpChar(character);
|
|
1904
2000
|
}
|
|
1905
2001
|
const prefix = anchored || body.includes("/") ? "^" : "(?:^|.*/)";
|
|
1906
|
-
|
|
2002
|
+
try {
|
|
2003
|
+
return new RegExp(`${prefix}${source}(?:/.*)?$`);
|
|
2004
|
+
} catch {
|
|
2005
|
+
return /$^/;
|
|
2006
|
+
}
|
|
1907
2007
|
}
|
|
1908
2008
|
function matchesGitignoreRule(rule, scoped, isDirectory) {
|
|
1909
2009
|
if (!rule.dirOnly) return matchGitignorePattern(scoped, rule.pattern);
|
|
@@ -1933,14 +2033,31 @@ function gitignoreVerdict(layers, relativePath, isDirectory) {
|
|
|
1933
2033
|
}
|
|
1934
2034
|
return verdict;
|
|
1935
2035
|
}
|
|
1936
|
-
|
|
2036
|
+
var DEFAULT_TOOL_DIR_NAMES = ["snapshot"];
|
|
2037
|
+
function toolDirNamesFor(toolOwnedPaths) {
|
|
2038
|
+
const names = new Set(DEFAULT_TOOL_DIR_NAMES);
|
|
2039
|
+
for (const entry of toolOwnedPaths) {
|
|
2040
|
+
const base = entry.replace(/\\/g, "/").replace(/\/+$/, "").split("/").pop();
|
|
2041
|
+
if (base !== void 0 && base.length > 0) names.add(base);
|
|
2042
|
+
}
|
|
2043
|
+
return [...names];
|
|
2044
|
+
}
|
|
2045
|
+
function isToolLeftoverName(name, dirNames) {
|
|
2046
|
+
for (const dirName of dirNames) {
|
|
2047
|
+
if (name.startsWith(`${dirName}.backup-`) || name.startsWith(`${dirName}.partial-`)) {
|
|
2048
|
+
return true;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
return false;
|
|
2052
|
+
}
|
|
2053
|
+
function defaultIgnoreReason(relativePath, toolDirNames = DEFAULT_TOOL_DIR_NAMES) {
|
|
1937
2054
|
const posixPath = relativePath.replace(/\\/g, "/");
|
|
1938
2055
|
const parts = posixPath.split("/");
|
|
1939
2056
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
1940
2057
|
if (DEFAULT_IGNORED_DIRS.includes(parts[i])) {
|
|
1941
2058
|
return `built-in ignore: directory "${parts[i]}"`;
|
|
1942
2059
|
}
|
|
1943
|
-
if (parts[i]
|
|
2060
|
+
if (isToolLeftoverName(parts[i], toolDirNames)) {
|
|
1944
2061
|
return `built-in ignore: leftover snapshot backup "${parts[i]}"`;
|
|
1945
2062
|
}
|
|
1946
2063
|
}
|
|
@@ -1948,6 +2065,9 @@ function defaultIgnoreReason(relativePath) {
|
|
|
1948
2065
|
if (DEFAULT_IGNORED_FILES.includes(base)) {
|
|
1949
2066
|
return `built-in ignore: filename "${base}"`;
|
|
1950
2067
|
}
|
|
2068
|
+
if (isToolLeftoverName(base, toolDirNames)) {
|
|
2069
|
+
return `built-in ignore: leftover snapshot backup "${base}"`;
|
|
2070
|
+
}
|
|
1951
2071
|
return null;
|
|
1952
2072
|
}
|
|
1953
2073
|
function listIgnoredPaths(cwd) {
|
|
@@ -2016,41 +2136,72 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2016
2136
|
}
|
|
2017
2137
|
const reported = /* @__PURE__ */ new Set();
|
|
2018
2138
|
const kept = [];
|
|
2139
|
+
const report = (linkedPath, record) => {
|
|
2140
|
+
if (reported.has(linkedPath)) return;
|
|
2141
|
+
reported.add(linkedPath);
|
|
2142
|
+
skipped.push(record);
|
|
2143
|
+
};
|
|
2019
2144
|
for (const candidate of candidates) {
|
|
2020
2145
|
const parts = candidate.split("/");
|
|
2021
|
-
let
|
|
2146
|
+
let linkedPath = null;
|
|
2022
2147
|
for (let depth = 1; depth <= parts.length; depth += 1) {
|
|
2023
2148
|
const prefix = parts.slice(0, depth).join("/");
|
|
2024
2149
|
if (isLink(path16__namespace.join(cwd, prefix))) {
|
|
2025
|
-
|
|
2150
|
+
linkedPath = prefix;
|
|
2026
2151
|
break;
|
|
2027
2152
|
}
|
|
2028
2153
|
}
|
|
2029
|
-
if (
|
|
2154
|
+
if (linkedPath === null) {
|
|
2030
2155
|
kept.push(candidate);
|
|
2031
2156
|
continue;
|
|
2032
2157
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
let targetReal = absolute;
|
|
2158
|
+
const absolute = path16__namespace.join(cwd, linkedPath);
|
|
2159
|
+
const rawTarget = readLinkTarget(absolute, absolute);
|
|
2160
|
+
let targetReal = null;
|
|
2037
2161
|
try {
|
|
2038
2162
|
targetReal = fs14__namespace.realpathSync(absolute);
|
|
2039
2163
|
} catch {
|
|
2164
|
+
targetReal = null;
|
|
2040
2165
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
} : {
|
|
2049
|
-
path: linkedDir,
|
|
2166
|
+
if (targetReal === null) {
|
|
2167
|
+
report(linkedPath, brokenLinkRecord(absolute, linkedPath, rootReal));
|
|
2168
|
+
continue;
|
|
2169
|
+
}
|
|
2170
|
+
if (!isInside(rootReal, targetReal)) {
|
|
2171
|
+
report(linkedPath, {
|
|
2172
|
+
path: linkedPath,
|
|
2050
2173
|
reason: "outside-root",
|
|
2051
|
-
detail: `link target resolves outside the scanned root (target: ${
|
|
2052
|
-
}
|
|
2053
|
-
|
|
2174
|
+
detail: `link target resolves outside the scanned root (target: ${rawTarget}); not followed`
|
|
2175
|
+
});
|
|
2176
|
+
continue;
|
|
2177
|
+
}
|
|
2178
|
+
let stats = null;
|
|
2179
|
+
try {
|
|
2180
|
+
stats = fs14__namespace.statSync(absolute);
|
|
2181
|
+
} catch {
|
|
2182
|
+
stats = null;
|
|
2183
|
+
}
|
|
2184
|
+
if (stats === null) {
|
|
2185
|
+
report(linkedPath, { path: linkedPath, reason: "read-error", detail: "unreadable symlink" });
|
|
2186
|
+
continue;
|
|
2187
|
+
}
|
|
2188
|
+
if (linkedPath !== candidate || stats.isDirectory()) {
|
|
2189
|
+
report(linkedPath, {
|
|
2190
|
+
path: linkedPath,
|
|
2191
|
+
reason: "symlink",
|
|
2192
|
+
detail: `directory link not followed (target: ${rawTarget})`
|
|
2193
|
+
});
|
|
2194
|
+
continue;
|
|
2195
|
+
}
|
|
2196
|
+
if (!stats.isFile()) {
|
|
2197
|
+
report(linkedPath, {
|
|
2198
|
+
path: linkedPath,
|
|
2199
|
+
reason: "symlink",
|
|
2200
|
+
detail: `link to neither a file nor a directory (target: ${rawTarget})`
|
|
2201
|
+
});
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
kept.push(candidate);
|
|
2054
2205
|
}
|
|
2055
2206
|
return kept;
|
|
2056
2207
|
}
|
|
@@ -2097,6 +2248,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2097
2248
|
(options.toolOwnedPaths ?? []).map((entry) => entry.replace(/\\/g, "/"))
|
|
2098
2249
|
);
|
|
2099
2250
|
const isToolOwned = makeToolOwnedDecider(cwd, [...toolOwnedPaths]);
|
|
2251
|
+
const toolDirNames = toolDirNamesFor(toolOwnedPaths);
|
|
2100
2252
|
let candidates;
|
|
2101
2253
|
const skipped = [];
|
|
2102
2254
|
const discovery = options.discovery ?? "auto";
|
|
@@ -2125,7 +2277,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2125
2277
|
}
|
|
2126
2278
|
const files = candidates.filter((file) => {
|
|
2127
2279
|
if (isToolOwned(file)) return false;
|
|
2128
|
-
const ignored = defaultIgnoreReason(file);
|
|
2280
|
+
const ignored = defaultIgnoreReason(file, toolDirNames);
|
|
2129
2281
|
if (ignored) {
|
|
2130
2282
|
skipped.push({ path: file, reason: "default-ignore", detail: ignored });
|
|
2131
2283
|
return false;
|
|
@@ -2180,6 +2332,7 @@ function rootRealFor(root) {
|
|
|
2180
2332
|
function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PURE__ */ new Set(), parentGitignoreLayers = []) {
|
|
2181
2333
|
const results = { files: [], skipped: [] };
|
|
2182
2334
|
const stack = [];
|
|
2335
|
+
const toolDirNames = toolDirNamesFor(reservedPaths);
|
|
2183
2336
|
const relativeToBase = (target) => path16__namespace.relative(baseDir, target).replace(/\\/g, "/") || ".";
|
|
2184
2337
|
const enter = (current, layers) => {
|
|
2185
2338
|
if (visited.has(current)) return false;
|
|
@@ -2224,11 +2377,11 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
2224
2377
|
});
|
|
2225
2378
|
continue;
|
|
2226
2379
|
}
|
|
2227
|
-
if (DEFAULT_IGNORED_FILES.includes(entry.name) || entry.name
|
|
2380
|
+
if (DEFAULT_IGNORED_FILES.includes(entry.name) || isToolLeftoverName(entry.name, toolDirNames)) {
|
|
2228
2381
|
results.skipped.push({
|
|
2229
2382
|
path: relPath,
|
|
2230
2383
|
reason: "default-ignore",
|
|
2231
|
-
detail: "built-in ignore: filename"
|
|
2384
|
+
detail: isToolLeftoverName(entry.name, toolDirNames) ? `built-in ignore: leftover snapshot backup "${entry.name}" (not descended)` : "built-in ignore: filename"
|
|
2232
2385
|
});
|
|
2233
2386
|
continue;
|
|
2234
2387
|
}
|
|
@@ -2249,7 +2402,7 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
2249
2402
|
try {
|
|
2250
2403
|
targetReal = fs14__namespace.realpathSync(fullPath);
|
|
2251
2404
|
} catch {
|
|
2252
|
-
results.skipped.push(
|
|
2405
|
+
results.skipped.push(brokenLinkRecord(fullPath, relPath, rootReal));
|
|
2253
2406
|
continue;
|
|
2254
2407
|
}
|
|
2255
2408
|
let stats;
|
|
@@ -2337,8 +2490,9 @@ function findConfigFile(cwd) {
|
|
|
2337
2490
|
const pkgJsonPath = path16__namespace.join(cwd, "package.json");
|
|
2338
2491
|
if (!fs14__namespace.existsSync(pkgJsonPath)) return null;
|
|
2339
2492
|
try {
|
|
2340
|
-
const parsed = JSON.parse(fs14__namespace.readFileSync(pkgJsonPath, "utf8"));
|
|
2341
|
-
|
|
2493
|
+
const parsed = JSON.parse(stripJsonComments(fs14__namespace.readFileSync(pkgJsonPath, "utf8")));
|
|
2494
|
+
const snapshot = parsed?.snapshot;
|
|
2495
|
+
return typeof snapshot === "object" && snapshot !== null && !Array.isArray(snapshot) ? pkgJsonPath : null;
|
|
2342
2496
|
} catch {
|
|
2343
2497
|
return null;
|
|
2344
2498
|
}
|
|
@@ -2388,15 +2542,19 @@ function normalizeBudgets(budgets, maxPartBytes) {
|
|
|
2388
2542
|
}
|
|
2389
2543
|
function normalizeResolvedConfig(config) {
|
|
2390
2544
|
const discovery = isDiscoveryMode(config.discovery) ? config.discovery : "auto";
|
|
2545
|
+
const monolithWanted = config.includeMonolith === true || config.artifacts?.monolith === true;
|
|
2546
|
+
const artifacts = resolveArtifacts(
|
|
2547
|
+
config.artifacts,
|
|
2548
|
+
void 0,
|
|
2549
|
+
monolithWanted,
|
|
2550
|
+
monolithWanted
|
|
2551
|
+
);
|
|
2391
2552
|
return markResolvedConfig({
|
|
2392
2553
|
...config,
|
|
2393
2554
|
discovery,
|
|
2555
|
+
includeMonolith: artifacts.monolith,
|
|
2394
2556
|
secretRules: compileSecretRules(config.secretRules),
|
|
2395
|
-
artifacts
|
|
2396
|
-
config.artifacts,
|
|
2397
|
-
void 0,
|
|
2398
|
-
config.includeMonolith === true
|
|
2399
|
-
),
|
|
2557
|
+
artifacts,
|
|
2400
2558
|
include: resolveInclude(config.include),
|
|
2401
2559
|
preset: typeof config.preset === "string" ? config.preset : "generic",
|
|
2402
2560
|
presetExclude: [
|
|
@@ -2605,6 +2763,16 @@ async function importConfigModule(fullPath) {
|
|
|
2605
2763
|
url$1.searchParams.set("codebaseSnapshotLoad", String(moduleConfigLoadToken));
|
|
2606
2764
|
return await import(url$1.href);
|
|
2607
2765
|
}
|
|
2766
|
+
function attachConfigDir(config, filePath) {
|
|
2767
|
+
if (typeof config === "object" && config !== null) {
|
|
2768
|
+
Object.defineProperty(config, CONFIG_DIR_SYMBOL, {
|
|
2769
|
+
value: path16__namespace.dirname(filePath),
|
|
2770
|
+
enumerable: false,
|
|
2771
|
+
configurable: true
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
return config;
|
|
2775
|
+
}
|
|
2608
2776
|
async function loadConfigFileDetailed(cwd) {
|
|
2609
2777
|
const warnings = [];
|
|
2610
2778
|
const errors = [];
|
|
@@ -2620,7 +2788,7 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
2620
2788
|
);
|
|
2621
2789
|
}
|
|
2622
2790
|
assertKnownConfigKeys(config, fullPath);
|
|
2623
|
-
return { config, file: fullPath, warnings, errors };
|
|
2791
|
+
return { config: attachConfigDir(config, fullPath), file: fullPath, warnings, errors };
|
|
2624
2792
|
} catch (err) {
|
|
2625
2793
|
const raw = err.message;
|
|
2626
2794
|
const message = raw.includes(fullPath) ? raw : `${fullPath}: ${raw}`;
|
|
@@ -2639,7 +2807,7 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
2639
2807
|
const imported = await importConfigModule(fullPath);
|
|
2640
2808
|
const hasDefault = Object.prototype.hasOwnProperty.call(imported, "default");
|
|
2641
2809
|
const config = readConfigExport(hasDefault ? imported.default : imported, fullPath);
|
|
2642
|
-
return { config, file: fullPath, warnings, errors };
|
|
2810
|
+
return { config: attachConfigDir(config, fullPath), file: fullPath, warnings, errors };
|
|
2643
2811
|
} catch (err) {
|
|
2644
2812
|
const message = `Could not load ${fullPath}: ${err.message}`;
|
|
2645
2813
|
warnings.push(message);
|
|
@@ -2657,7 +2825,12 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
2657
2825
|
snapshotKey,
|
|
2658
2826
|
`${pkgJsonPath} (the "snapshot" key)`
|
|
2659
2827
|
);
|
|
2660
|
-
return {
|
|
2828
|
+
return {
|
|
2829
|
+
config: attachConfigDir(snapshotKey, pkgJsonPath),
|
|
2830
|
+
file: pkgJsonPath,
|
|
2831
|
+
warnings,
|
|
2832
|
+
errors
|
|
2833
|
+
};
|
|
2661
2834
|
}
|
|
2662
2835
|
if (snapshotKey !== void 0) {
|
|
2663
2836
|
const kind = Array.isArray(snapshotKey) ? "array" : typeof snapshotKey;
|
|
@@ -2696,7 +2869,7 @@ var DEFAULT_ARTIFACTS = {
|
|
|
2696
2869
|
function isArtifactName(value) {
|
|
2697
2870
|
return ARTIFACT_NAMES.includes(value);
|
|
2698
2871
|
}
|
|
2699
|
-
function resolveArtifacts(requested, skipped, includeMonolith, monolithOverride) {
|
|
2872
|
+
function resolveArtifacts(requested, skipped, includeMonolith, monolithOverride, only) {
|
|
2700
2873
|
const artifacts = {
|
|
2701
2874
|
...DEFAULT_ARTIFACTS,
|
|
2702
2875
|
// `includeMonolith` predates this selection and stays the switch for it.
|
|
@@ -2721,6 +2894,21 @@ function resolveArtifacts(requested, skipped, includeMonolith, monolithOverride)
|
|
|
2721
2894
|
if (monolithOverride !== void 0) {
|
|
2722
2895
|
artifacts.monolith = monolithOverride;
|
|
2723
2896
|
}
|
|
2897
|
+
if (only !== void 0 && only.length > 0) {
|
|
2898
|
+
const keep = /* @__PURE__ */ new Set();
|
|
2899
|
+
for (const name of only) {
|
|
2900
|
+
if (!isArtifactName(name)) {
|
|
2901
|
+
throw new Error(
|
|
2902
|
+
`Unknown artifact "${name}". Expected one of: ${ARTIFACT_NAMES.join(", ")}.`
|
|
2903
|
+
);
|
|
2904
|
+
}
|
|
2905
|
+
keep.add(name);
|
|
2906
|
+
}
|
|
2907
|
+
for (const name of ARTIFACT_NAMES) {
|
|
2908
|
+
artifacts[name] = keep.has(name);
|
|
2909
|
+
}
|
|
2910
|
+
return artifacts;
|
|
2911
|
+
}
|
|
2724
2912
|
for (const name of skipped ?? []) {
|
|
2725
2913
|
if (!isArtifactName(name)) {
|
|
2726
2914
|
throw new Error(
|
|
@@ -3021,7 +3209,8 @@ function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
|
3021
3209
|
userConfig?.artifacts,
|
|
3022
3210
|
cliOptions?.skipArtifacts,
|
|
3023
3211
|
includeMonolith,
|
|
3024
|
-
cliMonolith
|
|
3212
|
+
cliMonolith,
|
|
3213
|
+
cliOptions?.onlyArtifacts
|
|
3025
3214
|
);
|
|
3026
3215
|
const explicitMaxPartBytes = cliOptions?.maxPartBytes ?? userConfig?.maxPartBytes;
|
|
3027
3216
|
const maxPartBytes = boundedBytes(
|
|
@@ -3195,7 +3384,18 @@ function configDigest(config) {
|
|
|
3195
3384
|
excludePatterns: config.excludePatterns,
|
|
3196
3385
|
preset: config.preset,
|
|
3197
3386
|
layersMode: config.layersMode,
|
|
3198
|
-
budgets
|
|
3387
|
+
// `budgets.byLayer` is a plain object, and `JSON.stringify` preserves insertion order — so
|
|
3388
|
+
// `{ docs, api }` and `{ api, docs }` hashed differently despite being the same settings, and
|
|
3389
|
+
// `validate` reported configuration drift for a snapshot nobody had changed. The keys are
|
|
3390
|
+
// canonicalised here; nothing else about the object is order-dependent.
|
|
3391
|
+
budgets: {
|
|
3392
|
+
default: config.budgets.default,
|
|
3393
|
+
byLayer: Object.fromEntries(
|
|
3394
|
+
Object.entries(config.budgets.byLayer).sort(
|
|
3395
|
+
([left], [right]) => left < right ? -1 : left > right ? 1 : 0
|
|
3396
|
+
)
|
|
3397
|
+
)
|
|
3398
|
+
},
|
|
3199
3399
|
// The matcher is what makes a layer a layer. Hashing only the names meant that
|
|
3200
3400
|
// moving `{ name: "api", match: "src/api/**" }` to `"src/v2/**"` produced the same
|
|
3201
3401
|
// digest, so `validate` reported "valid and up to date" for a snapshot whose files
|
|
@@ -3297,6 +3497,10 @@ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
3297
3497
|
".mjs",
|
|
3298
3498
|
".cjs"
|
|
3299
3499
|
]);
|
|
3500
|
+
var PYTHON_EXTENSIONS = /* @__PURE__ */ new Set([".py", ".pyi"]);
|
|
3501
|
+
var RUBY_EXTENSIONS = /* @__PURE__ */ new Set([".rb", ".rake", ".gemspec"]);
|
|
3502
|
+
var C_EXTENSIONS = /* @__PURE__ */ new Set([".c", ".h", ".cc", ".cpp", ".cxx", ".hpp", ".hxx", ".m", ".mm"]);
|
|
3503
|
+
var PYTHON_SUFFIXES = [".py", ".pyi", "/__init__.py", "/__init__.pyi"];
|
|
3300
3504
|
function buildImportResolver(files) {
|
|
3301
3505
|
const byPath = /* @__PURE__ */ new Map();
|
|
3302
3506
|
const byPathFolded = /* @__PURE__ */ new Map();
|
|
@@ -3323,11 +3527,45 @@ function buildImportResolver(files) {
|
|
|
3323
3527
|
return { byPath, byPathFolded, byStem, byStemFolded };
|
|
3324
3528
|
}
|
|
3325
3529
|
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
3530
|
+
const extension = path16__namespace.posix.extname(fromFile).toLowerCase();
|
|
3531
|
+
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
3532
|
+
const firstOf = (candidates) => {
|
|
3533
|
+
for (const candidate of candidates) {
|
|
3534
|
+
const hit = exact(candidate);
|
|
3535
|
+
if (hit) return hit;
|
|
3536
|
+
}
|
|
3537
|
+
return null;
|
|
3538
|
+
};
|
|
3539
|
+
if (PYTHON_EXTENSIONS.has(extension) && specifier.startsWith(".")) {
|
|
3540
|
+
const dots = /^\.+/.exec(specifier)?.[0].length ?? 0;
|
|
3541
|
+
const rest = specifier.slice(dots).replace(/\./g, "/");
|
|
3542
|
+
let base = path16__namespace.posix.dirname(fromFile);
|
|
3543
|
+
for (let up = 1; up < dots; up += 1) base = path16__namespace.posix.dirname(base);
|
|
3544
|
+
const joined2 = rest ? path16__namespace.posix.normalize(path16__namespace.posix.join(base, rest)) : base;
|
|
3545
|
+
return firstOf([joined2, ...PYTHON_SUFFIXES.map((suffix) => `${joined2}${suffix}`)]);
|
|
3546
|
+
}
|
|
3547
|
+
if (RUBY_EXTENSIONS.has(extension) && specifier.startsWith("./")) {
|
|
3548
|
+
const joined2 = path16__namespace.posix.normalize(
|
|
3549
|
+
path16__namespace.posix.join(path16__namespace.posix.dirname(fromFile), specifier)
|
|
3550
|
+
);
|
|
3551
|
+
return firstOf([`${joined2}.rb`, joined2]);
|
|
3552
|
+
}
|
|
3553
|
+
if (C_EXTENSIONS.has(extension) && specifier.startsWith("./")) {
|
|
3554
|
+
const joined2 = path16__namespace.posix.normalize(
|
|
3555
|
+
path16__namespace.posix.join(path16__namespace.posix.dirname(fromFile), specifier)
|
|
3556
|
+
);
|
|
3557
|
+
return exact(joined2);
|
|
3558
|
+
}
|
|
3559
|
+
if (extension === ".dart" && !specifier.includes(":")) {
|
|
3560
|
+
const joined2 = path16__namespace.posix.normalize(
|
|
3561
|
+
path16__namespace.posix.join(path16__namespace.posix.dirname(fromFile), specifier)
|
|
3562
|
+
);
|
|
3563
|
+
return firstOf([joined2, `${joined2}.dart`]);
|
|
3564
|
+
}
|
|
3326
3565
|
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
3327
3566
|
const joined = path16__namespace.posix.normalize(
|
|
3328
3567
|
path16__namespace.posix.join(path16__namespace.posix.dirname(fromFile), specifier)
|
|
3329
3568
|
);
|
|
3330
|
-
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
3331
3569
|
const direct = exact(joined);
|
|
3332
3570
|
if (direct) return direct;
|
|
3333
3571
|
for (const suffix of IMPORT_CANDIDATE_SUFFIXES) {
|
|
@@ -3453,13 +3691,23 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
3453
3691
|
if (ts__default.default.isImportDeclaration(node) && ts__default.default.isStringLiteral(node.moduleSpecifier)) {
|
|
3454
3692
|
summary.imports.push(node.moduleSpecifier.text);
|
|
3455
3693
|
}
|
|
3694
|
+
if (ts__default.default.isImportEqualsDeclaration(node) && ts__default.default.isExternalModuleReference(node.moduleReference) && node.moduleReference.expression !== void 0 && ts__default.default.isStringLiteral(node.moduleReference.expression)) {
|
|
3695
|
+
summary.imports.push(node.moduleReference.expression.text);
|
|
3696
|
+
}
|
|
3697
|
+
if (ts__default.default.isCallExpression(node) && node.arguments.length >= 1) {
|
|
3698
|
+
const firstArgument = node.arguments[0];
|
|
3699
|
+
if (firstArgument !== void 0 && ts__default.default.isStringLiteral(firstArgument)) {
|
|
3700
|
+
const isDynamicImport = node.expression.kind === ts__default.default.SyntaxKind.ImportKeyword;
|
|
3701
|
+
const isRequire = ts__default.default.isIdentifier(node.expression) && node.expression.text === "require";
|
|
3702
|
+
if (isDynamicImport || isRequire) summary.imports.push(firstArgument.text);
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3456
3705
|
if (ts__default.default.isExportDeclaration(node)) {
|
|
3457
3706
|
if (node.moduleSpecifier && ts__default.default.isStringLiteral(node.moduleSpecifier)) {
|
|
3458
3707
|
summary.imports.push(node.moduleSpecifier.text);
|
|
3708
|
+
summary.reExports.push(node.moduleSpecifier.text);
|
|
3459
3709
|
if (node.exportClause && ts__default.default.isNamespaceExport(node.exportClause)) {
|
|
3460
3710
|
summary.exports.push(node.exportClause.name.text);
|
|
3461
|
-
} else if (!node.exportClause) {
|
|
3462
|
-
summary.reExports.push(node.moduleSpecifier.text);
|
|
3463
3711
|
}
|
|
3464
3712
|
}
|
|
3465
3713
|
if (node.exportClause && ts__default.default.isNamedExports(node.exportClause)) {
|
|
@@ -3604,7 +3852,7 @@ function isCapitalizedName(name) {
|
|
|
3604
3852
|
}
|
|
3605
3853
|
|
|
3606
3854
|
// src/ast/profiles/cfamily.ts
|
|
3607
|
-
var INCLUDE = /^#\s*include\s*[<"]([^>"]+)[>"]/;
|
|
3855
|
+
var INCLUDE = /^#\s*include\s*([<"])([^>"]+)[>"]/;
|
|
3608
3856
|
var DEFINE = /^#\s*define\s+([A-Za-z_]\w*)/;
|
|
3609
3857
|
var USING = /^using\s+(?:namespace\s+)?([\w:]+)\s*;/;
|
|
3610
3858
|
var TYPE_DECL = /^(?:typedef\s+)?(?:struct|union|class|enum)\s+([A-Za-z_]\w*)/;
|
|
@@ -3623,7 +3871,7 @@ var cfamilyProfile = {
|
|
|
3623
3871
|
}
|
|
3624
3872
|
const included = INCLUDE.exec(trimmed);
|
|
3625
3873
|
if (included) {
|
|
3626
|
-
summary.imports.push(included[1]);
|
|
3874
|
+
summary.imports.push(included[1] === '"' ? `./${included[2]}` : included[2]);
|
|
3627
3875
|
return;
|
|
3628
3876
|
}
|
|
3629
3877
|
const used = USING.exec(trimmed);
|
|
@@ -4027,7 +4275,15 @@ var pythonProfile = {
|
|
|
4027
4275
|
}
|
|
4028
4276
|
const from = FROM_IMPORT.exec(trimmed);
|
|
4029
4277
|
if (from) {
|
|
4030
|
-
|
|
4278
|
+
const module = from[1];
|
|
4279
|
+
if (/^\.+$/.test(module)) {
|
|
4280
|
+
for (const name of from[2].split(",")) {
|
|
4281
|
+
const imported2 = name.replace(/[()]/g, "").trim().split(/\s+as\s+/)[0].trim();
|
|
4282
|
+
if (/^[A-Za-z_]\w*$/.test(imported2)) summary.imports.push(`${module}${imported2}`);
|
|
4283
|
+
}
|
|
4284
|
+
} else {
|
|
4285
|
+
summary.imports.push(module);
|
|
4286
|
+
}
|
|
4031
4287
|
routePending = false;
|
|
4032
4288
|
return;
|
|
4033
4289
|
}
|
|
@@ -4076,7 +4332,8 @@ var pythonProfile = {
|
|
|
4076
4332
|
};
|
|
4077
4333
|
|
|
4078
4334
|
// src/ast/profiles/ruby.ts
|
|
4079
|
-
var
|
|
4335
|
+
var REQUIRE_RELATIVE = /^require_relative\s+["']([^"']+)["']/;
|
|
4336
|
+
var REQUIRE = /^require\s+["']([^"']+)["']/;
|
|
4080
4337
|
var CLASS_OR_MODULE = /^(?:class|module)\s+([A-Z][\w:]*)/;
|
|
4081
4338
|
var METHOD2 = /^def\s+(?:self\.)?([A-Za-z_]\w*[?!=]?)/;
|
|
4082
4339
|
var rubyProfile = {
|
|
@@ -4087,6 +4344,11 @@ var rubyProfile = {
|
|
|
4087
4344
|
eachLine(content, (line) => {
|
|
4088
4345
|
const trimmed = line.trim();
|
|
4089
4346
|
if (!trimmed || trimmed.startsWith("#")) return;
|
|
4347
|
+
const relative12 = REQUIRE_RELATIVE.exec(trimmed);
|
|
4348
|
+
if (relative12) {
|
|
4349
|
+
summary.imports.push(`./${relative12[1]}`);
|
|
4350
|
+
return;
|
|
4351
|
+
}
|
|
4090
4352
|
const required = REQUIRE.exec(trimmed);
|
|
4091
4353
|
if (required) {
|
|
4092
4354
|
summary.imports.push(required[1]);
|
|
@@ -4177,7 +4439,8 @@ var rustProfile = {
|
|
|
4177
4439
|
};
|
|
4178
4440
|
|
|
4179
4441
|
// src/ast/profiles/sfc.ts
|
|
4180
|
-
var SCRIPT_BLOCK = /<script[^>]
|
|
4442
|
+
var SCRIPT_BLOCK = /<script([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
4443
|
+
var JSX_LANG = /\blang\s*=\s*["']?[tj]sx\b/i;
|
|
4181
4444
|
var ASTRO_FRONTMATTER = /^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?=\r?\n|$)/;
|
|
4182
4445
|
function componentName(path22) {
|
|
4183
4446
|
const base = path22.split("/").pop() ?? path22;
|
|
@@ -4188,13 +4451,15 @@ var sfcProfile = {
|
|
|
4188
4451
|
extensions: [".vue", ".svelte", ".astro"],
|
|
4189
4452
|
summarize(path22, content) {
|
|
4190
4453
|
let script = "";
|
|
4454
|
+
let usesJsx = path22.toLowerCase().endsWith(".astro");
|
|
4191
4455
|
if (path22.toLowerCase().endsWith(".astro")) {
|
|
4192
4456
|
const frontmatter = ASTRO_FRONTMATTER.exec(content);
|
|
4193
4457
|
if (frontmatter) script += `${frontmatter[1]}
|
|
4194
4458
|
`;
|
|
4195
4459
|
}
|
|
4196
4460
|
for (const match of content.matchAll(SCRIPT_BLOCK)) {
|
|
4197
|
-
|
|
4461
|
+
if (JSX_LANG.test(match[1] ?? "")) usesJsx = true;
|
|
4462
|
+
script += `${match[2]}
|
|
4198
4463
|
`;
|
|
4199
4464
|
}
|
|
4200
4465
|
const name = componentName(path22);
|
|
@@ -4206,7 +4471,7 @@ var sfcProfile = {
|
|
|
4206
4471
|
}
|
|
4207
4472
|
return { summary: finalizeSummary(summary2), quality: "heuristic" };
|
|
4208
4473
|
}
|
|
4209
|
-
const summary = summarizeTypeScriptSource(`${name}
|
|
4474
|
+
const summary = summarizeTypeScriptSource(`${name}.${usesJsx ? "tsx" : "ts"}`, script);
|
|
4210
4475
|
summary.reactComponents.push(name);
|
|
4211
4476
|
summary.exports.push(name);
|
|
4212
4477
|
return { summary: finalizeSummary(summary), quality: "ast" };
|
|
@@ -4640,18 +4905,22 @@ function isBinaryFile(absolutePath) {
|
|
|
4640
4905
|
}
|
|
4641
4906
|
|
|
4642
4907
|
// src/ast/imports.ts
|
|
4908
|
+
var SUMMARY_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
4643
4909
|
var TS_SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
4644
4910
|
var isTsSource = (relativePath) => {
|
|
4645
4911
|
const lower = relativePath.toLowerCase();
|
|
4646
4912
|
return TS_SOURCE_EXTENSIONS.some((extension) => lower.endsWith(extension));
|
|
4647
4913
|
};
|
|
4648
4914
|
function importsOfText(relativePath, text2) {
|
|
4915
|
+
if (Buffer.byteLength(text2, "utf8") > SUMMARY_MAX_SOURCE_BYTES) return [];
|
|
4649
4916
|
if (isTsSource(relativePath)) return summarizeTypeScriptSource(relativePath, text2).imports;
|
|
4650
4917
|
return summarizeWithProfile(relativePath, text2)?.summary.imports ?? [];
|
|
4651
4918
|
}
|
|
4652
4919
|
function importsOfFile(relativePath, absolutePath) {
|
|
4653
4920
|
let buffer;
|
|
4654
4921
|
try {
|
|
4922
|
+
const stats = fs14__namespace.statSync(absolutePath);
|
|
4923
|
+
if (!stats.isFile() || stats.size > SUMMARY_MAX_SOURCE_BYTES) return [];
|
|
4655
4924
|
buffer = fs14__namespace.readFileSync(absolutePath);
|
|
4656
4925
|
} catch {
|
|
4657
4926
|
return [];
|
|
@@ -4782,9 +5051,13 @@ function describeManifestShapeProblem(value) {
|
|
|
4782
5051
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
4783
5052
|
return 'a "parts" entry is not an object';
|
|
4784
5053
|
}
|
|
4785
|
-
|
|
5054
|
+
const filename = entry.filename;
|
|
5055
|
+
if (typeof filename !== "string" || filename.trim() === "") {
|
|
4786
5056
|
return 'a "parts" entry has no "filename"';
|
|
4787
5057
|
}
|
|
5058
|
+
if (filename.includes("/") || filename.includes("\\") || filename.includes("\0") || filename === "." || filename === ".." || path16__namespace.isAbsolute(filename)) {
|
|
5059
|
+
return 'a "parts" entry "filename" must be a bare filename inside parts/';
|
|
5060
|
+
}
|
|
4788
5061
|
}
|
|
4789
5062
|
if (!candidate.layers || typeof candidate.layers !== "object" || Array.isArray(candidate.layers)) {
|
|
4790
5063
|
return 'missing "layers" object';
|
|
@@ -4895,7 +5168,7 @@ function validateSnapshot(configInput) {
|
|
|
4895
5168
|
});
|
|
4896
5169
|
for (const diskFile of collection.files) {
|
|
4897
5170
|
const snapshotHash = manifestFileMap.get(diskFile);
|
|
4898
|
-
if (
|
|
5171
|
+
if (snapshotHash === void 0) {
|
|
4899
5172
|
const skipReason = skippedReasonOnDisk(diskFile);
|
|
4900
5173
|
if (skipReason === "unchanged") {
|
|
4901
5174
|
if (unchangedScope !== null) {
|
|
@@ -4909,6 +5182,11 @@ function validateSnapshot(configInput) {
|
|
|
4909
5182
|
drift.missingInSnapshot.push(diskFile);
|
|
4910
5183
|
continue;
|
|
4911
5184
|
}
|
|
5185
|
+
if (unchangedScope !== null && !unchangedScope.has(diskFile) && !becameDependent(diskFile)) {
|
|
5186
|
+
drift.configMismatches.push(
|
|
5187
|
+
`scope: ${maskedPath(diskFile, config.secretRules)} is in the snapshot but is no longer in the changed-files scope`
|
|
5188
|
+
);
|
|
5189
|
+
}
|
|
4912
5190
|
let diskHash;
|
|
4913
5191
|
try {
|
|
4914
5192
|
diskHash = sha256Hex(fs14__namespace.readFileSync(path16__namespace.join(config.cwd, diskFile)));
|
|
@@ -5049,6 +5327,41 @@ function validateSnapshot(configInput) {
|
|
|
5049
5327
|
`includeSensitiveFiles: manifest ${recorded.includeSensitiveFiles} vs current ${config.includeSensitiveFiles}`
|
|
5050
5328
|
);
|
|
5051
5329
|
}
|
|
5330
|
+
if ((recorded.maxPartTokens ?? void 0) !== (config.maxPartTokens ?? void 0)) {
|
|
5331
|
+
drift.configMismatches.push(
|
|
5332
|
+
`maxPartTokens: manifest ${String(recorded.maxPartTokens ?? "unset")} vs current ${String(config.maxPartTokens ?? "unset")}`
|
|
5333
|
+
);
|
|
5334
|
+
}
|
|
5335
|
+
if ((recorded.maxMonolithBytes ?? null) !== (config.maxMonolithBytes ?? null)) {
|
|
5336
|
+
drift.configMismatches.push(
|
|
5337
|
+
`maxMonolithBytes: manifest ${String(recorded.maxMonolithBytes ?? null)} vs current ${String(config.maxMonolithBytes ?? null)}`
|
|
5338
|
+
);
|
|
5339
|
+
}
|
|
5340
|
+
if (Boolean(recorded.skeleton) !== Boolean(config.skeleton)) {
|
|
5341
|
+
drift.configMismatches.push(
|
|
5342
|
+
`skeleton: manifest ${Boolean(recorded.skeleton)} vs current ${Boolean(config.skeleton)}`
|
|
5343
|
+
);
|
|
5344
|
+
}
|
|
5345
|
+
if (Boolean(recorded.sarif) !== Boolean(config.sarif)) {
|
|
5346
|
+
drift.configMismatches.push(
|
|
5347
|
+
`sarif: manifest ${Boolean(recorded.sarif)} vs current ${Boolean(config.sarif)}`
|
|
5348
|
+
);
|
|
5349
|
+
}
|
|
5350
|
+
if ((recorded.changedSince ?? void 0) !== (config.changedSince ?? void 0)) {
|
|
5351
|
+
drift.configMismatches.push(
|
|
5352
|
+
`changedSince: manifest ${String(recorded.changedSince ?? "unset")} vs current ${String(config.changedSince ?? "unset")}`
|
|
5353
|
+
);
|
|
5354
|
+
}
|
|
5355
|
+
if (Boolean(recorded.staged) !== Boolean(config.staged)) {
|
|
5356
|
+
drift.configMismatches.push(
|
|
5357
|
+
`staged: manifest ${Boolean(recorded.staged)} vs current ${Boolean(config.staged)}`
|
|
5358
|
+
);
|
|
5359
|
+
}
|
|
5360
|
+
if (Boolean(recorded.withDependents) !== Boolean(config.withDependents)) {
|
|
5361
|
+
drift.configMismatches.push(
|
|
5362
|
+
`withDependents: manifest ${Boolean(recorded.withDependents)} vs current ${Boolean(config.withDependents)}`
|
|
5363
|
+
);
|
|
5364
|
+
}
|
|
5052
5365
|
const effectiveDiscovery = config.discovery ?? "auto";
|
|
5053
5366
|
const recordedDiscovery = recorded.discovery;
|
|
5054
5367
|
if (recordedDiscovery !== void 0 && recordedDiscovery !== effectiveDiscovery) {
|
|
@@ -5523,9 +5836,21 @@ function renderDiffText(diff) {
|
|
|
5523
5836
|
}
|
|
5524
5837
|
|
|
5525
5838
|
// src/partitioner/preview.ts
|
|
5839
|
+
var LINE_ENDINGS = /\r\n|\r/g;
|
|
5840
|
+
var normaliseLineEndings = (content) => content.replace(LINE_ENDINGS, "\n");
|
|
5526
5841
|
function countLines(content) {
|
|
5527
5842
|
if (content.length === 0) return 0;
|
|
5528
|
-
|
|
5843
|
+
let breaks = 0;
|
|
5844
|
+
for (let index = 0; index < content.length; index += 1) {
|
|
5845
|
+
const code = content.charCodeAt(index);
|
|
5846
|
+
if (code === 10) {
|
|
5847
|
+
breaks += 1;
|
|
5848
|
+
continue;
|
|
5849
|
+
}
|
|
5850
|
+
if (code === 13 && content.charCodeAt(index + 1) !== 10) breaks += 1;
|
|
5851
|
+
}
|
|
5852
|
+
const last = content.charCodeAt(content.length - 1);
|
|
5853
|
+
return last === 10 || last === 13 ? breaks : breaks + 1;
|
|
5529
5854
|
}
|
|
5530
5855
|
function truncateToBytes(content, maxBytes) {
|
|
5531
5856
|
if (maxBytes <= 0) return { content: "", isTruncated: content.length > 0 };
|
|
@@ -5536,17 +5861,18 @@ function truncateToBytes(content, maxBytes) {
|
|
|
5536
5861
|
return { content: slice, isTruncated: true };
|
|
5537
5862
|
}
|
|
5538
5863
|
function truncatePreview(content, headLines, tailLines) {
|
|
5539
|
-
const
|
|
5540
|
-
const
|
|
5864
|
+
const normalised = normaliseLineEndings(content);
|
|
5865
|
+
const lines = normalised.split("\n");
|
|
5866
|
+
const total = countLines(normalised);
|
|
5541
5867
|
if (total <= headLines + tailLines) {
|
|
5542
5868
|
return {
|
|
5543
|
-
content,
|
|
5869
|
+
content: normalised,
|
|
5544
5870
|
isTruncated: false,
|
|
5545
5871
|
originalLines: total,
|
|
5546
5872
|
includedLines: total
|
|
5547
5873
|
};
|
|
5548
5874
|
}
|
|
5549
|
-
const window =
|
|
5875
|
+
const window = normalised.endsWith("\n") ? lines.slice(0, -1) : lines;
|
|
5550
5876
|
const head = window.slice(0, headLines);
|
|
5551
5877
|
const tail = tailLines > 0 ? window.slice(window.length - tailLines) : [];
|
|
5552
5878
|
const omitted = total - head.length - tail.length;
|
|
@@ -5666,10 +5992,13 @@ function describeMatcher(matcher) {
|
|
|
5666
5992
|
return String(matcher);
|
|
5667
5993
|
}
|
|
5668
5994
|
function normalizeTarget(cwd, target) {
|
|
5669
|
-
const
|
|
5670
|
-
const
|
|
5671
|
-
|
|
5672
|
-
|
|
5995
|
+
const resolvedCwd = path16__namespace.resolve(cwd);
|
|
5996
|
+
const absolute = path16__namespace.resolve(resolvedCwd, target);
|
|
5997
|
+
const relative12 = path16__namespace.relative(resolvedCwd, absolute);
|
|
5998
|
+
if (relative12 === "" || relative12 === ".." || relative12.startsWith(`..${path16__namespace.sep}`) || path16__namespace.isAbsolute(relative12)) {
|
|
5999
|
+
return { relative: target.replace(/\\/g, "/"), outsideRoot: true };
|
|
6000
|
+
}
|
|
6001
|
+
return { relative: relative12.split(path16__namespace.sep).join("/"), outsideRoot: false };
|
|
5673
6002
|
}
|
|
5674
6003
|
function findRuleMatch(rules, relativePath) {
|
|
5675
6004
|
return rules.find(
|
|
@@ -5696,9 +6025,19 @@ function readManifest2(config) {
|
|
|
5696
6025
|
}
|
|
5697
6026
|
}
|
|
5698
6027
|
function explainPath(config, target) {
|
|
5699
|
-
const
|
|
5700
|
-
const
|
|
6028
|
+
const normalized = normalizeTarget(config.cwd, target);
|
|
6029
|
+
const relative12 = normalized.relative;
|
|
5701
6030
|
const maskedPath2 = findSecretInPath(relative12, config.secretRules)?.masked ?? relative12;
|
|
6031
|
+
if (normalized.outsideRoot) {
|
|
6032
|
+
return {
|
|
6033
|
+
path: maskedPath2,
|
|
6034
|
+
exists: false,
|
|
6035
|
+
included: false,
|
|
6036
|
+
reason: "outside-root",
|
|
6037
|
+
detail: "path resolves outside the configured repository root and was not inspected"
|
|
6038
|
+
};
|
|
6039
|
+
}
|
|
6040
|
+
const absolute = path16__namespace.join(config.cwd, relative12);
|
|
5702
6041
|
const result = {
|
|
5703
6042
|
path: maskedPath2,
|
|
5704
6043
|
exists: fs14__namespace.existsSync(absolute),
|
|
@@ -5848,6 +6187,178 @@ function renderExplainText(result) {
|
|
|
5848
6187
|
);
|
|
5849
6188
|
return lines.join("\n");
|
|
5850
6189
|
}
|
|
6190
|
+
|
|
6191
|
+
// src/ast/structured.ts
|
|
6192
|
+
function stripJsonComments2(content) {
|
|
6193
|
+
const text2 = content.replace(/^\uFEFF/, "");
|
|
6194
|
+
const withoutComments = stripComments(text2);
|
|
6195
|
+
return stripTrailingCommas2(withoutComments);
|
|
6196
|
+
}
|
|
6197
|
+
function stripComments(text2) {
|
|
6198
|
+
let out = "";
|
|
6199
|
+
let inString = false;
|
|
6200
|
+
let escaped = false;
|
|
6201
|
+
for (let index = 0; index < text2.length; index += 1) {
|
|
6202
|
+
const char = text2[index];
|
|
6203
|
+
if (inString) {
|
|
6204
|
+
out += char;
|
|
6205
|
+
if (escaped) escaped = false;
|
|
6206
|
+
else if (char === "\\") escaped = true;
|
|
6207
|
+
else if (char === '"') inString = false;
|
|
6208
|
+
continue;
|
|
6209
|
+
}
|
|
6210
|
+
if (char === '"') {
|
|
6211
|
+
inString = true;
|
|
6212
|
+
out += char;
|
|
6213
|
+
continue;
|
|
6214
|
+
}
|
|
6215
|
+
if (char === "/" && text2[index + 1] === "/") {
|
|
6216
|
+
while (index < text2.length && text2[index] !== "\n" && text2[index] !== "\r") index += 1;
|
|
6217
|
+
index -= 1;
|
|
6218
|
+
continue;
|
|
6219
|
+
}
|
|
6220
|
+
if (char === "/" && text2[index + 1] === "*") {
|
|
6221
|
+
index += 2;
|
|
6222
|
+
while (index < text2.length && !(text2[index] === "*" && text2[index + 1] === "/")) {
|
|
6223
|
+
if (text2[index] === "\n" || text2[index] === "\r") out += text2[index];
|
|
6224
|
+
index += 1;
|
|
6225
|
+
}
|
|
6226
|
+
index += 1;
|
|
6227
|
+
continue;
|
|
6228
|
+
}
|
|
6229
|
+
out += char;
|
|
6230
|
+
}
|
|
6231
|
+
return out;
|
|
6232
|
+
}
|
|
6233
|
+
function stripTrailingCommas2(text2) {
|
|
6234
|
+
let out = "";
|
|
6235
|
+
let inString = false;
|
|
6236
|
+
let escaped = false;
|
|
6237
|
+
for (let index = 0; index < text2.length; index += 1) {
|
|
6238
|
+
const char = text2[index];
|
|
6239
|
+
if (inString) {
|
|
6240
|
+
out += char;
|
|
6241
|
+
if (escaped) escaped = false;
|
|
6242
|
+
else if (char === "\\") escaped = true;
|
|
6243
|
+
else if (char === '"') inString = false;
|
|
6244
|
+
continue;
|
|
6245
|
+
}
|
|
6246
|
+
if (char === '"') {
|
|
6247
|
+
inString = true;
|
|
6248
|
+
out += char;
|
|
6249
|
+
continue;
|
|
6250
|
+
}
|
|
6251
|
+
if (char === ",") {
|
|
6252
|
+
let next = index + 1;
|
|
6253
|
+
while (next < text2.length && /\s/.test(text2[next])) next += 1;
|
|
6254
|
+
if (text2[next] === "}" || text2[next] === "]") continue;
|
|
6255
|
+
}
|
|
6256
|
+
out += char;
|
|
6257
|
+
}
|
|
6258
|
+
return out;
|
|
6259
|
+
}
|
|
6260
|
+
function summarizeJson(content) {
|
|
6261
|
+
try {
|
|
6262
|
+
const parsed = JSON.parse(stripJsonComments2(content));
|
|
6263
|
+
if (Array.isArray(parsed)) {
|
|
6264
|
+
const sample = parsed[0];
|
|
6265
|
+
const sampleKeys = sample && typeof sample === "object" && !Array.isArray(sample) ? Object.keys(sample).slice(0, 10).sort() : [];
|
|
6266
|
+
return {
|
|
6267
|
+
shape: "array",
|
|
6268
|
+
itemCount: parsed.length,
|
|
6269
|
+
keys: [],
|
|
6270
|
+
sampleKeys
|
|
6271
|
+
};
|
|
6272
|
+
}
|
|
6273
|
+
if (parsed && typeof parsed === "object") {
|
|
6274
|
+
const keys = Object.keys(parsed).sort();
|
|
6275
|
+
return {
|
|
6276
|
+
shape: "object",
|
|
6277
|
+
itemCount: keys.length,
|
|
6278
|
+
keys: keys.slice(0, 30),
|
|
6279
|
+
sampleKeys: []
|
|
6280
|
+
};
|
|
6281
|
+
}
|
|
6282
|
+
return {
|
|
6283
|
+
shape: typeof parsed,
|
|
6284
|
+
itemCount: null,
|
|
6285
|
+
keys: [],
|
|
6286
|
+
sampleKeys: []
|
|
6287
|
+
};
|
|
6288
|
+
} catch {
|
|
6289
|
+
return {
|
|
6290
|
+
shape: "invalid-json",
|
|
6291
|
+
itemCount: null,
|
|
6292
|
+
keys: [],
|
|
6293
|
+
sampleKeys: []
|
|
6294
|
+
};
|
|
6295
|
+
}
|
|
6296
|
+
}
|
|
6297
|
+
function frontMatterEnd(lines) {
|
|
6298
|
+
if (lines.length < 3 || lines[0].trim() !== "---") return 0;
|
|
6299
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
6300
|
+
const line = lines[index];
|
|
6301
|
+
const candidate = line.trim();
|
|
6302
|
+
if (candidate === "---" || candidate === "...") {
|
|
6303
|
+
return index > 1 ? index : 0;
|
|
6304
|
+
}
|
|
6305
|
+
if (candidate === "") continue;
|
|
6306
|
+
if (/^#(\s|$)/.test(candidate)) continue;
|
|
6307
|
+
if (/^(`{3,}|~{3,})/.test(candidate)) return 0;
|
|
6308
|
+
if (/^#{1,6}\s+\S/.test(candidate)) return 0;
|
|
6309
|
+
const isYamlMapping = /^[A-Za-z0-9_.-]+\s*:(\s|$)/.test(candidate);
|
|
6310
|
+
const isListItem = /^-(?:(\s|$))/.test(candidate);
|
|
6311
|
+
const isIndented = /^[ \t]/.test(line);
|
|
6312
|
+
if (!isYamlMapping && !isListItem && !isIndented) return 0;
|
|
6313
|
+
}
|
|
6314
|
+
return 0;
|
|
6315
|
+
}
|
|
6316
|
+
function summarizeMarkdown(content) {
|
|
6317
|
+
const headings = [];
|
|
6318
|
+
const frontmatterKeys = [];
|
|
6319
|
+
const lines = content.replace(/^\uFEFF/, "").split(/\r\n|\n|\r/);
|
|
6320
|
+
let inFrontmatter = false;
|
|
6321
|
+
let fence = null;
|
|
6322
|
+
const frontMatterEndLine = frontMatterEnd(lines);
|
|
6323
|
+
for (let index = 0; index < lines.length; index++) {
|
|
6324
|
+
const line = lines[index];
|
|
6325
|
+
if (index === 0 && frontMatterEndLine > 0) {
|
|
6326
|
+
inFrontmatter = true;
|
|
6327
|
+
continue;
|
|
6328
|
+
}
|
|
6329
|
+
if (inFrontmatter) {
|
|
6330
|
+
const trimmedFrontmatter = line.trim();
|
|
6331
|
+
if (trimmedFrontmatter === "---" || trimmedFrontmatter === "...") {
|
|
6332
|
+
inFrontmatter = false;
|
|
6333
|
+
continue;
|
|
6334
|
+
}
|
|
6335
|
+
const match = /^([A-Za-z0-9_.-]+)\s*:/.exec(line);
|
|
6336
|
+
if (match) frontmatterKeys.push(match[1]);
|
|
6337
|
+
continue;
|
|
6338
|
+
}
|
|
6339
|
+
const trimmed = line.trim();
|
|
6340
|
+
const fenceMatch = /^(`{3,}|~{3,})(.*)$/.exec(trimmed);
|
|
6341
|
+
if (fenceMatch) {
|
|
6342
|
+
const run = fenceMatch[1];
|
|
6343
|
+
const rest = fenceMatch[2].trim();
|
|
6344
|
+
if (fence === null) {
|
|
6345
|
+
fence = { marker: run[0], length: run.length };
|
|
6346
|
+
} else if (run[0] === fence.marker && run.length >= fence.length && rest === "") {
|
|
6347
|
+
fence = null;
|
|
6348
|
+
}
|
|
6349
|
+
continue;
|
|
6350
|
+
}
|
|
6351
|
+
if (fence !== null) continue;
|
|
6352
|
+
const headingMatch = /^(#{1,6})\s+(.+)$/.exec(trimmed);
|
|
6353
|
+
if (headingMatch) {
|
|
6354
|
+
headings.push(`${headingMatch[1]} ${headingMatch[2].trim()}`);
|
|
6355
|
+
}
|
|
6356
|
+
}
|
|
6357
|
+
return {
|
|
6358
|
+
headings: headings.slice(0, 40),
|
|
6359
|
+
frontmatterKeys: Array.from(new Set(frontmatterKeys)).sort()
|
|
6360
|
+
};
|
|
6361
|
+
}
|
|
5851
6362
|
function appendGitignoreEntries(cwd, entries) {
|
|
5852
6363
|
const wanted = [...new Set(entries)].filter((entry) => entry.trim().length > 0);
|
|
5853
6364
|
if (wanted.length === 0) return { added: [], note: null };
|
|
@@ -5885,7 +6396,7 @@ function gitignorePath(cwd, filePath) {
|
|
|
5885
6396
|
|
|
5886
6397
|
// src/version.ts
|
|
5887
6398
|
var GENERATOR_NAME = "codebase-snapshot";
|
|
5888
|
-
var GENERATOR_VERSION = "0.9.
|
|
6399
|
+
var GENERATOR_VERSION = "0.9.14";
|
|
5889
6400
|
var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
|
|
5890
6401
|
function isOurGeneratorName(name) {
|
|
5891
6402
|
if (typeof name !== "string") return false;
|
|
@@ -6088,6 +6599,24 @@ function displayWidth(line) {
|
|
|
6088
6599
|
return width;
|
|
6089
6600
|
}
|
|
6090
6601
|
var GUTTER_PATTERN = /^[ \t]*(?:[❯◆◻◼▏✖][ \t]+)*/;
|
|
6602
|
+
function sliceByDisplayWidth(text2, maxCells) {
|
|
6603
|
+
let cells = 0;
|
|
6604
|
+
let lastSpace = -1;
|
|
6605
|
+
let index = 0;
|
|
6606
|
+
for (const character of text2) {
|
|
6607
|
+
const width = isWideCodePoint(character.codePointAt(0) ?? 0) ? 2 : 1;
|
|
6608
|
+
if (cells + width > maxCells) break;
|
|
6609
|
+
if (character === " ") lastSpace = index;
|
|
6610
|
+
cells += width;
|
|
6611
|
+
index += character.length;
|
|
6612
|
+
}
|
|
6613
|
+
if (index >= text2.length) return { head: text2, tail: "" };
|
|
6614
|
+
if (lastSpace > 0) {
|
|
6615
|
+
return { head: text2.slice(0, lastSpace), tail: text2.slice(lastSpace + 1).trimStart() };
|
|
6616
|
+
}
|
|
6617
|
+
const safeIndex = index > 0 ? index : Array.from(text2)[0]?.length ?? 1;
|
|
6618
|
+
return { head: text2.slice(0, safeIndex), tail: text2.slice(safeIndex).trimStart() };
|
|
6619
|
+
}
|
|
6091
6620
|
function wrapOneLine(line, limit) {
|
|
6092
6621
|
if (displayWidth(line) <= limit) return [line];
|
|
6093
6622
|
const gutter = GUTTER_PATTERN.exec(line)?.[0] ?? "";
|
|
@@ -6102,12 +6631,11 @@ function wrapOneLine(line, limit) {
|
|
|
6102
6631
|
rows.push(prefix + rest);
|
|
6103
6632
|
break;
|
|
6104
6633
|
}
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
rest = rest.slice(cut).trimStart();
|
|
6634
|
+
const { head, tail } = sliceByDisplayWidth(rest, room);
|
|
6635
|
+
rows.push(
|
|
6636
|
+
`${prefix}${head.length > 0 ? head : rest.slice(0, 1)}`
|
|
6637
|
+
);
|
|
6638
|
+
rest = tail;
|
|
6111
6639
|
prefix = continuation;
|
|
6112
6640
|
}
|
|
6113
6641
|
return rows;
|
|
@@ -6178,7 +6706,7 @@ function keyReader(input) {
|
|
|
6178
6706
|
next() {
|
|
6179
6707
|
const queued = queue.shift();
|
|
6180
6708
|
if (queued) return Promise.resolve(queued);
|
|
6181
|
-
return new Promise((
|
|
6709
|
+
return new Promise((resolve10) => waiters.push(resolve10));
|
|
6182
6710
|
},
|
|
6183
6711
|
close() {
|
|
6184
6712
|
input.off("keypress", onKey);
|
|
@@ -6395,17 +6923,25 @@ var CONFIG_CANDIDATES = [
|
|
|
6395
6923
|
".snapshotrc.json",
|
|
6396
6924
|
".snapshotrc"
|
|
6397
6925
|
];
|
|
6398
|
-
function
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6926
|
+
function parsePackageJson(raw) {
|
|
6927
|
+
const parsed = JSON.parse(stripJsonComments2(raw));
|
|
6928
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
6929
|
+
throw new Error("package.json is not a JSON object");
|
|
6930
|
+
}
|
|
6931
|
+
return parsed;
|
|
6932
|
+
}
|
|
6933
|
+
function readPackageJson(cwd) {
|
|
6402
6934
|
try {
|
|
6403
|
-
|
|
6404
|
-
return parsed.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
6935
|
+
return parsePackageJson(fs14__namespace.readFileSync(path16__namespace.join(cwd, "package.json"), "utf8"));
|
|
6405
6936
|
} catch {
|
|
6406
|
-
return
|
|
6937
|
+
return null;
|
|
6407
6938
|
}
|
|
6408
6939
|
}
|
|
6940
|
+
function chooseConfigFileName(cwd) {
|
|
6941
|
+
if (!supportsTypeScriptImports()) return "snapshot.config.mjs";
|
|
6942
|
+
const parsed = readPackageJson(cwd);
|
|
6943
|
+
return parsed?.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
6944
|
+
}
|
|
6409
6945
|
var SIZE_CHOICES = [
|
|
6410
6946
|
{ value: "60k", label: "Small", hint: "60 KB \xB7 ~15k tokens \xB7 tight context windows" },
|
|
6411
6947
|
{ value: "120k", label: "Medium", hint: "120 KB \xB7 ~30k tokens \xB7 recommended" },
|
|
@@ -6413,12 +6949,9 @@ var SIZE_CHOICES = [
|
|
|
6413
6949
|
{ value: "custom", label: "Custom\u2026", hint: "type a size like 150k or 1.5mb" }
|
|
6414
6950
|
];
|
|
6415
6951
|
function packageJsonHasConfig(cwd) {
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
} catch {
|
|
6420
|
-
return false;
|
|
6421
|
-
}
|
|
6952
|
+
const parsed = readPackageJson(cwd);
|
|
6953
|
+
if (parsed === null) return false;
|
|
6954
|
+
return typeof parsed.snapshot === "object" && parsed.snapshot !== null && !Array.isArray(parsed.snapshot);
|
|
6422
6955
|
}
|
|
6423
6956
|
function artifactAnswers(options) {
|
|
6424
6957
|
const artifacts = { ...DEFAULT_ANSWERS.artifacts };
|
|
@@ -6459,12 +6992,7 @@ function writeFileOrExplain(target, content) {
|
|
|
6459
6992
|
function canWriteModuleConfig(name, cwd) {
|
|
6460
6993
|
if (name.endsWith(".mjs") || name.endsWith(".ts")) return true;
|
|
6461
6994
|
if (!name.endsWith(".js")) return false;
|
|
6462
|
-
|
|
6463
|
-
const parsed = JSON.parse(fs14__namespace.readFileSync(path16__namespace.join(cwd, "package.json"), "utf8"));
|
|
6464
|
-
return parsed.type === "module";
|
|
6465
|
-
} catch {
|
|
6466
|
-
return false;
|
|
6467
|
-
}
|
|
6995
|
+
return readPackageJson(cwd)?.type === "module";
|
|
6468
6996
|
}
|
|
6469
6997
|
function configFormatLabel(name) {
|
|
6470
6998
|
if (name.endsWith(".js")) return 'CommonJS `.js` (package.json has no "type": "module")';
|
|
@@ -6492,7 +7020,8 @@ function serializePackageJson(raw, value) {
|
|
|
6492
7020
|
const crlf = raw.includes("\r\n");
|
|
6493
7021
|
const body = JSON.stringify(value, null, spacing);
|
|
6494
7022
|
const text2 = crlf ? body.replace(/\n/g, "\r\n") : body;
|
|
6495
|
-
|
|
7023
|
+
const bom = raw.startsWith("\uFEFF") ? "\uFEFF" : "";
|
|
7024
|
+
return `${bom}${text2}${raw.endsWith("\n") ? crlf ? "\r\n" : "\n" : ""}`;
|
|
6496
7025
|
}
|
|
6497
7026
|
function updatePackageJson(cwd, notes) {
|
|
6498
7027
|
const packagePath = path16__namespace.join(cwd, "package.json");
|
|
@@ -6508,7 +7037,7 @@ function updatePackageJson(cwd, notes) {
|
|
|
6508
7037
|
let parsed;
|
|
6509
7038
|
try {
|
|
6510
7039
|
raw = fs14__namespace.readFileSync(packagePath, "utf8");
|
|
6511
|
-
parsed =
|
|
7040
|
+
parsed = parsePackageJson(raw);
|
|
6512
7041
|
} catch {
|
|
6513
7042
|
notes.push("Could not parse package.json; npm scripts and the devDependency were not added.");
|
|
6514
7043
|
return unchanged;
|
|
@@ -6920,177 +7449,102 @@ function hasPriorSnapshot(cwd, outDir) {
|
|
|
6920
7449
|
} catch {
|
|
6921
7450
|
return false;
|
|
6922
7451
|
}
|
|
6923
|
-
if (parsed === null || typeof parsed !== "object") return false;
|
|
6924
|
-
return isOurGeneratorName(parsed.generator);
|
|
6925
|
-
}
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
if (inString) {
|
|
6952
|
-
out += char;
|
|
6953
|
-
if (char === "\\") {
|
|
6954
|
-
if (next !== void 0) {
|
|
6955
|
-
out += next;
|
|
6956
|
-
i += 1;
|
|
6957
|
-
}
|
|
6958
|
-
} else if (char === '"') {
|
|
6959
|
-
inString = false;
|
|
6960
|
-
}
|
|
6961
|
-
continue;
|
|
6962
|
-
}
|
|
6963
|
-
if (char === '"') {
|
|
6964
|
-
inString = true;
|
|
6965
|
-
out += char;
|
|
6966
|
-
continue;
|
|
6967
|
-
}
|
|
6968
|
-
if (char === "/" && next === "/") {
|
|
6969
|
-
inLineComment = true;
|
|
6970
|
-
i += 1;
|
|
6971
|
-
continue;
|
|
6972
|
-
}
|
|
6973
|
-
if (char === "/" && next === "*") {
|
|
6974
|
-
inBlockComment = true;
|
|
6975
|
-
i += 1;
|
|
6976
|
-
continue;
|
|
6977
|
-
}
|
|
6978
|
-
out += char;
|
|
6979
|
-
}
|
|
6980
|
-
return out;
|
|
6981
|
-
}
|
|
6982
|
-
function summarizeJson(content) {
|
|
6983
|
-
try {
|
|
6984
|
-
const parsed = JSON.parse(stripJsonComments2(content));
|
|
6985
|
-
if (Array.isArray(parsed)) {
|
|
6986
|
-
const sample = parsed[0];
|
|
6987
|
-
const sampleKeys = sample && typeof sample === "object" && !Array.isArray(sample) ? Object.keys(sample).slice(0, 10).sort() : [];
|
|
6988
|
-
return {
|
|
6989
|
-
shape: "array",
|
|
6990
|
-
itemCount: parsed.length,
|
|
6991
|
-
keys: [],
|
|
6992
|
-
sampleKeys
|
|
6993
|
-
};
|
|
6994
|
-
}
|
|
6995
|
-
if (parsed && typeof parsed === "object") {
|
|
6996
|
-
const keys = Object.keys(parsed).sort();
|
|
6997
|
-
return {
|
|
6998
|
-
shape: "object",
|
|
6999
|
-
itemCount: keys.length,
|
|
7000
|
-
keys: keys.slice(0, 30),
|
|
7001
|
-
sampleKeys: []
|
|
7002
|
-
};
|
|
7003
|
-
}
|
|
7004
|
-
return {
|
|
7005
|
-
shape: typeof parsed,
|
|
7006
|
-
itemCount: null,
|
|
7007
|
-
keys: [],
|
|
7008
|
-
sampleKeys: []
|
|
7009
|
-
};
|
|
7010
|
-
} catch {
|
|
7011
|
-
return {
|
|
7012
|
-
shape: "invalid-json",
|
|
7013
|
-
itemCount: null,
|
|
7014
|
-
keys: [],
|
|
7015
|
-
sampleKeys: []
|
|
7016
|
-
};
|
|
7017
|
-
}
|
|
7018
|
-
}
|
|
7019
|
-
function summarizeMarkdown(content) {
|
|
7020
|
-
const headings = [];
|
|
7021
|
-
const frontmatterKeys = [];
|
|
7022
|
-
const lines = content.replace(/^\uFEFF/, "").split("\n");
|
|
7023
|
-
let inFrontmatter = false;
|
|
7024
|
-
let fence = null;
|
|
7025
|
-
let closesFrontmatter = false;
|
|
7026
|
-
for (let index = 1; index < lines.length; index++) {
|
|
7027
|
-
if (lines[index].trim() === "---") {
|
|
7028
|
-
closesFrontmatter = true;
|
|
7029
|
-
break;
|
|
7030
|
-
}
|
|
7031
|
-
}
|
|
7032
|
-
for (let index = 0; index < lines.length; index++) {
|
|
7033
|
-
const line = lines[index];
|
|
7034
|
-
if (index === 0 && line.trim() === "---" && closesFrontmatter) {
|
|
7035
|
-
inFrontmatter = true;
|
|
7036
|
-
continue;
|
|
7037
|
-
}
|
|
7038
|
-
if (inFrontmatter) {
|
|
7039
|
-
if (line.trim() === "---") {
|
|
7040
|
-
inFrontmatter = false;
|
|
7041
|
-
continue;
|
|
7452
|
+
if (parsed === null || typeof parsed !== "object") return false;
|
|
7453
|
+
return isOurGeneratorName(parsed.generator);
|
|
7454
|
+
}
|
|
7455
|
+
var hookStdoutScope = new async_hooks.AsyncLocalStorage();
|
|
7456
|
+
var captureScope = new async_hooks.AsyncLocalStorage();
|
|
7457
|
+
var installs = 0;
|
|
7458
|
+
var underlying = null;
|
|
7459
|
+
var router = null;
|
|
7460
|
+
var asText = (chunk) => typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
7461
|
+
var callbackIn = (rest) => {
|
|
7462
|
+
for (const argument of rest) {
|
|
7463
|
+
if (typeof argument === "function") return argument;
|
|
7464
|
+
}
|
|
7465
|
+
return void 0;
|
|
7466
|
+
};
|
|
7467
|
+
function installStdoutRouter() {
|
|
7468
|
+
if (installs === 0) {
|
|
7469
|
+
underlying = process.stdout.write;
|
|
7470
|
+
const realStdout = underlying.bind(process.stdout);
|
|
7471
|
+
const realStderr = process.stderr.write.bind(process.stderr);
|
|
7472
|
+
router = ((...args) => {
|
|
7473
|
+
if (hookStdoutScope.getStore() === true) return realStderr(...args);
|
|
7474
|
+
const capture = captureScope.getStore();
|
|
7475
|
+
if (capture !== void 0) {
|
|
7476
|
+
capture.write(asText(args[0]));
|
|
7477
|
+
const callback = callbackIn(args.slice(1));
|
|
7478
|
+
if (callback) callback(null);
|
|
7479
|
+
return true;
|
|
7042
7480
|
}
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7481
|
+
return realStdout(...args);
|
|
7482
|
+
});
|
|
7483
|
+
process.stdout.write = router;
|
|
7484
|
+
}
|
|
7485
|
+
installs += 1;
|
|
7486
|
+
let released = false;
|
|
7487
|
+
return () => {
|
|
7488
|
+
if (released) return;
|
|
7489
|
+
released = true;
|
|
7490
|
+
installs -= 1;
|
|
7491
|
+
if (installs === 0) {
|
|
7492
|
+
if (process.stdout.write === router && underlying !== null) {
|
|
7493
|
+
process.stdout.write = underlying;
|
|
7056
7494
|
}
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
if (fence !== null) continue;
|
|
7060
|
-
const headingMatch = /^(#{1,6})\s+(.+)$/.exec(trimmed);
|
|
7061
|
-
if (headingMatch) {
|
|
7062
|
-
headings.push(`${headingMatch[1]} ${headingMatch[2].trim()}`);
|
|
7495
|
+
underlying = null;
|
|
7496
|
+
router = null;
|
|
7063
7497
|
}
|
|
7064
|
-
}
|
|
7065
|
-
return {
|
|
7066
|
-
headings: headings.slice(0, 40),
|
|
7067
|
-
frontmatterKeys: Array.from(new Set(frontmatterKeys)).sort()
|
|
7068
7498
|
};
|
|
7069
7499
|
}
|
|
7500
|
+
async function runWithStdoutCapture(fn) {
|
|
7501
|
+
let text2 = "";
|
|
7502
|
+
const release = installStdoutRouter();
|
|
7503
|
+
try {
|
|
7504
|
+
const value = await captureScope.run(
|
|
7505
|
+
{ write: (chunk) => void (text2 += chunk) },
|
|
7506
|
+
fn
|
|
7507
|
+
);
|
|
7508
|
+
return { value, text: text2 };
|
|
7509
|
+
} finally {
|
|
7510
|
+
release();
|
|
7511
|
+
}
|
|
7512
|
+
}
|
|
7070
7513
|
function hasCollapsibleBody(node) {
|
|
7071
7514
|
if (!("body" in node)) return false;
|
|
7072
7515
|
const body = node.body;
|
|
7073
7516
|
return body !== void 0 && ts__default.default.isBlock(body);
|
|
7074
7517
|
}
|
|
7075
|
-
function
|
|
7076
|
-
|
|
7518
|
+
function scriptKindFor(fileName) {
|
|
7519
|
+
const lower = fileName.toLowerCase();
|
|
7520
|
+
if (lower.endsWith(".tsx")) return ts__default.default.ScriptKind.TSX;
|
|
7521
|
+
if (lower.endsWith(".jsx")) return ts__default.default.ScriptKind.JSX;
|
|
7522
|
+
if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs")) {
|
|
7523
|
+
return ts__default.default.ScriptKind.JS;
|
|
7524
|
+
}
|
|
7525
|
+
return ts__default.default.ScriptKind.TS;
|
|
7077
7526
|
}
|
|
7078
7527
|
function skeletonizeTypeScript(source, fileName = "file.ts") {
|
|
7079
|
-
const scriptKind = isTsxFileName(fileName) ? ts__default.default.ScriptKind.TSX : ts__default.default.ScriptKind.TS;
|
|
7080
7528
|
const sourceFile = ts__default.default.createSourceFile(
|
|
7081
7529
|
fileName,
|
|
7082
7530
|
source,
|
|
7083
7531
|
ts__default.default.ScriptTarget.Latest,
|
|
7084
|
-
|
|
7085
|
-
|
|
7532
|
+
// `setParentNodes: false`. Nothing here reads `.parent` — the one `getStart` call is given the
|
|
7533
|
+
// source file explicitly, which is the reason that argument exists. Asking for parent pointers
|
|
7534
|
+
// makes the parser run a second full pass over every node in the file to set them, on top of
|
|
7535
|
+
// the summary parse this same file already pays for.
|
|
7536
|
+
false,
|
|
7537
|
+
scriptKindFor(fileName)
|
|
7086
7538
|
);
|
|
7087
7539
|
const spans = [];
|
|
7088
7540
|
const visit = (node) => {
|
|
7089
7541
|
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)) {
|
|
7090
7542
|
const body = node.body;
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7543
|
+
if (body.statements.length > 0) {
|
|
7544
|
+
const start = body.getStart(sourceFile);
|
|
7545
|
+
const end = body.getEnd();
|
|
7546
|
+
if (end > start) spans.push({ start, end });
|
|
7547
|
+
}
|
|
7094
7548
|
}
|
|
7095
7549
|
ts__default.default.forEachChild(node, visit);
|
|
7096
7550
|
};
|
|
@@ -7102,16 +7556,21 @@ function skeletonizeTypeScript(source, fileName = "file.ts") {
|
|
|
7102
7556
|
if (last && span.start < last.end) continue;
|
|
7103
7557
|
outermost.push(span);
|
|
7104
7558
|
}
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7559
|
+
const chunks = [];
|
|
7560
|
+
let cursor = 0;
|
|
7561
|
+
for (const span of outermost) {
|
|
7562
|
+
chunks.push(source.slice(cursor, span.start), "{ /* ... */ }");
|
|
7563
|
+
cursor = span.end;
|
|
7109
7564
|
}
|
|
7110
|
-
|
|
7565
|
+
chunks.push(source.slice(cursor));
|
|
7566
|
+
return { text: chunks.join(""), collapsed: outermost.length };
|
|
7111
7567
|
}
|
|
7112
7568
|
function isSkeletonizable(filePath) {
|
|
7113
7569
|
const lower = filePath.toLowerCase();
|
|
7114
|
-
|
|
7570
|
+
if (lower.endsWith(".d.ts") || lower.endsWith(".d.mts") || lower.endsWith(".d.cts")) {
|
|
7571
|
+
return false;
|
|
7572
|
+
}
|
|
7573
|
+
return lower.endsWith(".ts") || lower.endsWith(".tsx") || lower.endsWith(".mts") || lower.endsWith(".cts") || lower.endsWith(".js") || lower.endsWith(".jsx") || lower.endsWith(".mjs") || lower.endsWith(".cjs");
|
|
7115
7574
|
}
|
|
7116
7575
|
|
|
7117
7576
|
// src/generator/audit.ts
|
|
@@ -7379,23 +7838,55 @@ function truncateWithBudgetMarker(content, maxBytes, budget, originalLines) {
|
|
|
7379
7838
|
};
|
|
7380
7839
|
}
|
|
7381
7840
|
function partitionFiles(files, maxPartBytes, contentFingerprint) {
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
const
|
|
7386
|
-
|
|
7841
|
+
return partitionFilesForBuckets([{ files, budgetBytes: maxPartBytes }], contentFingerprint);
|
|
7842
|
+
}
|
|
7843
|
+
function partitionFilesForBuckets(buckets, contentFingerprint) {
|
|
7844
|
+
const groups = [];
|
|
7845
|
+
for (const bucket of buckets) {
|
|
7846
|
+
if (bucket.files.length === 0) continue;
|
|
7847
|
+
const budget = Math.max(
|
|
7848
|
+
MIN_SECTION_BUDGET_BYTES + HEADER_RESERVE_BYTES,
|
|
7849
|
+
bucket.budgetBytes
|
|
7850
|
+
);
|
|
7851
|
+
const sectionBudget = budget - HEADER_RESERVE_BYTES;
|
|
7852
|
+
const prepared = bucket.files.map((file) => prepareSection(file, sectionBudget));
|
|
7853
|
+
for (const sections of greedyGroups(prepared, budget)) groups.push({ sections, budget });
|
|
7854
|
+
}
|
|
7855
|
+
if (groups.length === 0) return [];
|
|
7856
|
+
const measurements = /* @__PURE__ */ new Map();
|
|
7857
|
+
const widthOf = (total) => Math.max(3, String(total).length);
|
|
7858
|
+
const measure = (group, index, total) => {
|
|
7859
|
+
const cached = measurements.get(group);
|
|
7860
|
+
const width = widthOf(total);
|
|
7861
|
+
if (cached !== void 0 && cached.index === index && cached.width === width) {
|
|
7862
|
+
return cached.size;
|
|
7863
|
+
}
|
|
7864
|
+
const { content } = assembleGroup(group, index, total, contentFingerprint);
|
|
7865
|
+
const size2 = Buffer.byteLength(content, "utf8");
|
|
7866
|
+
measurements.set(group, { index, width, size: size2 });
|
|
7867
|
+
return size2;
|
|
7868
|
+
};
|
|
7387
7869
|
for (let pass = 0; pass < MAX_SPLIT_PASSES; pass++) {
|
|
7388
|
-
const
|
|
7389
|
-
|
|
7390
|
-
|
|
7870
|
+
const total = groups.length;
|
|
7871
|
+
let oversized = -1;
|
|
7872
|
+
for (let index = 0; index < total; index += 1) {
|
|
7873
|
+
const group2 = groups[index];
|
|
7874
|
+
if (measure(group2, index, total) > group2.budget) {
|
|
7875
|
+
oversized = index;
|
|
7876
|
+
break;
|
|
7877
|
+
}
|
|
7878
|
+
}
|
|
7879
|
+
if (oversized === -1) return renderGroups(groups, contentFingerprint);
|
|
7391
7880
|
const group = groups[oversized];
|
|
7392
|
-
if (group.length > 1) {
|
|
7393
|
-
const moved = group.pop();
|
|
7394
|
-
groups.splice(oversized + 1, 0, [moved]);
|
|
7881
|
+
if (group.sections.length > 1) {
|
|
7882
|
+
const moved = group.sections.pop();
|
|
7883
|
+
groups.splice(oversized + 1, 0, { sections: [moved], budget: group.budget });
|
|
7884
|
+
measurements.delete(group);
|
|
7395
7885
|
continue;
|
|
7396
7886
|
}
|
|
7397
|
-
const [section] = group;
|
|
7398
|
-
|
|
7887
|
+
const [section] = group.sections;
|
|
7888
|
+
if (section === void 0) return renderGroups(groups, contentFingerprint);
|
|
7889
|
+
const overflow = measure(group, oversized, total) - group.budget;
|
|
7399
7890
|
const currentContent = section.file.content ?? "";
|
|
7400
7891
|
const shrinkTo = Math.max(
|
|
7401
7892
|
0,
|
|
@@ -7404,26 +7895,33 @@ function partitionFiles(files, maxPartBytes, contentFingerprint) {
|
|
|
7404
7895
|
const shrunk = truncateWithBudgetMarker(
|
|
7405
7896
|
currentContent,
|
|
7406
7897
|
shrinkTo,
|
|
7407
|
-
budget,
|
|
7898
|
+
group.budget,
|
|
7408
7899
|
section.file.originalLines
|
|
7409
7900
|
);
|
|
7410
7901
|
if (shrunk.isTruncated) {
|
|
7411
7902
|
applyTruncatedContent(section.file, shrunk.content);
|
|
7412
7903
|
section.text = renderFileSection(section.file);
|
|
7413
7904
|
section.byteSize = Buffer.byteLength(section.text, "utf8");
|
|
7905
|
+
measurements.delete(group);
|
|
7414
7906
|
continue;
|
|
7415
7907
|
}
|
|
7908
|
+
const rendered = renderGroups(groups, contentFingerprint);
|
|
7416
7909
|
const marker2 = TRUNCATION_MARKER;
|
|
7417
|
-
assertBudgetHoldsMarker(budget);
|
|
7910
|
+
assertBudgetHoldsMarker(group.budget);
|
|
7418
7911
|
const target = Math.max(
|
|
7419
7912
|
0,
|
|
7420
|
-
budget - Buffer.byteLength(marker2, "utf8") - TOKEN_LINE_SLACK
|
|
7913
|
+
group.budget - Buffer.byteLength(marker2, "utf8") - TOKEN_LINE_SLACK
|
|
7421
7914
|
);
|
|
7422
7915
|
const clipped = truncateToBytes(rendered[oversized].markdown, target);
|
|
7423
|
-
rendered[oversized] = rehashPart(
|
|
7916
|
+
rendered[oversized] = rehashPart(
|
|
7917
|
+
rendered[oversized],
|
|
7918
|
+
clipped.content + marker2
|
|
7919
|
+
);
|
|
7424
7920
|
return rendered;
|
|
7425
7921
|
}
|
|
7426
|
-
return
|
|
7922
|
+
return renderGroups(groups, contentFingerprint).flatMap(
|
|
7923
|
+
(part) => enforceBudget([part], part.metadata.budgetBytes)
|
|
7924
|
+
);
|
|
7427
7925
|
}
|
|
7428
7926
|
var TRUNCATION_MARKER = "\n... [part truncated: cannot fit budget] ...\n";
|
|
7429
7927
|
function assertBudgetHoldsMarker(budget) {
|
|
@@ -7446,69 +7944,7 @@ function enforceBudget(parts, budget) {
|
|
|
7446
7944
|
return rehashPart(part, clipped.content + TRUNCATION_MARKER);
|
|
7447
7945
|
});
|
|
7448
7946
|
}
|
|
7449
|
-
|
|
7450
|
-
const total = parts.length;
|
|
7451
|
-
if (total === 0) return [];
|
|
7452
|
-
const zeroPad = (index) => String(index).padStart(Math.max(3, String(total).length), "0");
|
|
7453
|
-
const nameFor = (position) => `part-${zeroPad(position)}.md`;
|
|
7454
|
-
const marker2 = "## Contents\n";
|
|
7455
|
-
const renumbered = parts.map((part, index) => {
|
|
7456
|
-
const markerIndex = part.markdown.indexOf(marker2);
|
|
7457
|
-
const body = markerIndex === -1 ? "" : part.markdown.slice(markerIndex + marker2.length);
|
|
7458
|
-
const included = part.metadata.includedFiles;
|
|
7459
|
-
if (markerIndex === -1) {
|
|
7460
|
-
throw new Error(`${part.filename} has no "## Contents" section to re-number.`);
|
|
7461
|
-
}
|
|
7462
|
-
const layerCounts = {};
|
|
7463
|
-
const kindCounts = {};
|
|
7464
|
-
for (const file of included) {
|
|
7465
|
-
layerCounts[file.layer] = (layerCounts[file.layer] || 0) + 1;
|
|
7466
|
-
kindCounts[file.kind] = (kindCounts[file.kind] || 0) + 1;
|
|
7467
|
-
}
|
|
7468
|
-
const pathLines = included.map((file) => displayPath(file.relativePath));
|
|
7469
|
-
const listFence = fenceForText(pathLines.join("\n"), 3);
|
|
7470
|
-
const header = [
|
|
7471
|
-
`# Codebase Snapshot Part ${zeroPad(index + 1)} of ${zeroPad(total)}`,
|
|
7472
|
-
"",
|
|
7473
|
-
`- Previous: ${index === 0 ? "none" : nameFor(index)}`,
|
|
7474
|
-
`- Next: ${index === total - 1 ? "none" : nameFor(index + 2)}`,
|
|
7475
|
-
`- Files: ${included.length}`,
|
|
7476
|
-
`- Content Fingerprint: ${contentFingerprint || "none"}`,
|
|
7477
|
-
`- Layers: ${topEntries(layerCounts, 6) || "none"}`,
|
|
7478
|
-
`- Kinds: ${topEntries(kindCounts, 8) || "none"}`,
|
|
7479
|
-
"",
|
|
7480
|
-
UNTRUSTED_CONTENT_NOTICE,
|
|
7481
|
-
"",
|
|
7482
|
-
"## Files",
|
|
7483
|
-
"",
|
|
7484
|
-
`${listFence}text`,
|
|
7485
|
-
...pathLines,
|
|
7486
|
-
listFence,
|
|
7487
|
-
"",
|
|
7488
|
-
"## Contents",
|
|
7489
|
-
""
|
|
7490
|
-
].join("\n");
|
|
7491
|
-
const assembled = (header + body).replace(/\r\n/g, "\n");
|
|
7492
|
-
const estimatedTokens = estimateTokens(assembled);
|
|
7493
|
-
const markdown = insertTokenLine(assembled, estimatedTokens);
|
|
7494
|
-
const metadata = {
|
|
7495
|
-
...part.metadata,
|
|
7496
|
-
index: index + 1,
|
|
7497
|
-
filename: nameFor(index + 1),
|
|
7498
|
-
byteSize: Buffer.byteLength(markdown, "utf8"),
|
|
7499
|
-
estimatedTokens,
|
|
7500
|
-
sha256: crypto3__namespace.createHash("sha256").update(markdown).digest("hex"),
|
|
7501
|
-
lines: countLines(markdown),
|
|
7502
|
-
fileCount: included.length,
|
|
7503
|
-
layersCovered: Object.keys(layerCounts).sort()
|
|
7504
|
-
};
|
|
7505
|
-
return { filename: metadata.filename, markdown, metadata };
|
|
7506
|
-
});
|
|
7507
|
-
return renumbered.map(
|
|
7508
|
-
(part) => enforceBudget([part], part.metadata.budgetBytes)[0] ?? part
|
|
7509
|
-
);
|
|
7510
|
-
}
|
|
7511
|
-
var SECTION_TRUNCATION_NOTICE = "\n... [section truncated: metadata alone exceeds the part budget] ...\n";
|
|
7947
|
+
var SECTION_TRUNCATION_NOTICE = "... [section truncated: the fixed metadata and a code fence did not fit the part budget] ...";
|
|
7512
7948
|
var METADATA_VALUE_MAX = 200;
|
|
7513
7949
|
var DISPLAY_PATH_MAX = 300;
|
|
7514
7950
|
function capValue(value, max) {
|
|
@@ -7550,13 +7986,57 @@ function prepareSection(file, sectionBudget) {
|
|
|
7550
7986
|
shrinkBody();
|
|
7551
7987
|
}
|
|
7552
7988
|
if (byteSize > sectionBudget) {
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7989
|
+
applyTruncatedContent(file, SECTION_TRUNCATION_NOTICE);
|
|
7990
|
+
file.summary = void 0;
|
|
7991
|
+
file.summaryOmitted = true;
|
|
7992
|
+
text2 = renderFileSection(file);
|
|
7556
7993
|
byteSize = Buffer.byteLength(text2, "utf8");
|
|
7994
|
+
if (byteSize > sectionBudget) {
|
|
7995
|
+
text2 = truncateRenderedSection(text2, sectionBudget, fenceForContent(file.content ?? ""));
|
|
7996
|
+
byteSize = Buffer.byteLength(text2, "utf8");
|
|
7997
|
+
}
|
|
7557
7998
|
}
|
|
7558
7999
|
return { file, text: text2, byteSize };
|
|
7559
8000
|
}
|
|
8001
|
+
function truncateRenderedSection(text2, sectionBudget, fence) {
|
|
8002
|
+
const closing = `
|
|
8003
|
+
${SECTION_TRUNCATION_NOTICE}
|
|
8004
|
+
${fence}
|
|
8005
|
+
`;
|
|
8006
|
+
const bodyRoom = sectionBudget - Buffer.byteLength(closing, "utf8");
|
|
8007
|
+
if (bodyRoom > 0) {
|
|
8008
|
+
const cut2 = truncateToBytes(text2, bodyRoom).content;
|
|
8009
|
+
const lastBreak2 = cut2.lastIndexOf("\n");
|
|
8010
|
+
const kept = lastBreak2 >= 0 ? cut2.slice(0, lastBreak2 + 1) : "";
|
|
8011
|
+
if (fenceIsOpen(kept, fence)) return `${kept}${closing}`;
|
|
8012
|
+
}
|
|
8013
|
+
const probe = text2.slice(0, 512);
|
|
8014
|
+
const emptyFence = fenceForText(probe.slice(0, Math.max(0, probe.lastIndexOf("\n") + 1)), 4);
|
|
8015
|
+
const block = `
|
|
8016
|
+
${emptyFence}
|
|
8017
|
+
${SECTION_TRUNCATION_NOTICE}
|
|
8018
|
+
${emptyFence}
|
|
8019
|
+
`;
|
|
8020
|
+
const prefixRoom = sectionBudget - Buffer.byteLength(block, "utf8");
|
|
8021
|
+
const cut = prefixRoom > 0 ? truncateToBytes(text2, prefixRoom).content : "";
|
|
8022
|
+
const lastBreak = cut.lastIndexOf("\n");
|
|
8023
|
+
const prefix = lastBreak >= 0 ? cut.slice(0, lastBreak + 1) : "";
|
|
8024
|
+
return `${dropTrailingOpenFence(prefix, fence)}${block}`;
|
|
8025
|
+
}
|
|
8026
|
+
function dropTrailingOpenFence(text2, fence) {
|
|
8027
|
+
if (!fenceIsOpen(text2, fence)) return text2;
|
|
8028
|
+
const lines = text2.split("\n");
|
|
8029
|
+
const opening = lines.findIndex((line) => line.startsWith(fence));
|
|
8030
|
+
if (opening < 0) return text2;
|
|
8031
|
+
return lines.slice(0, opening).join("\n") + (opening > 0 ? "\n" : "");
|
|
8032
|
+
}
|
|
8033
|
+
function fenceIsOpen(text2, fence) {
|
|
8034
|
+
let open = false;
|
|
8035
|
+
for (const line of text2.split("\n")) {
|
|
8036
|
+
if (line.startsWith(fence)) open = !open;
|
|
8037
|
+
}
|
|
8038
|
+
return open;
|
|
8039
|
+
}
|
|
7560
8040
|
function applyTruncatedContent(file, content) {
|
|
7561
8041
|
file.content = content;
|
|
7562
8042
|
file.isPreviewTruncated = true;
|
|
@@ -7604,52 +8084,64 @@ function greedyGroups(sections, budget) {
|
|
|
7604
8084
|
if (current.length > 0) groups.push(current);
|
|
7605
8085
|
return groups;
|
|
7606
8086
|
}
|
|
7607
|
-
function
|
|
8087
|
+
function assembleGroup(group, index, total, contentFingerprint) {
|
|
8088
|
+
const width = Math.max(3, String(total).length);
|
|
8089
|
+
const zeroPad = (value) => String(value).padStart(width, "0");
|
|
8090
|
+
const filename = `part-${zeroPad(index + 1)}.md`;
|
|
8091
|
+
const prev = index === 0 ? "none" : `part-${zeroPad(index)}.md`;
|
|
8092
|
+
const next = index === total - 1 ? "none" : `part-${zeroPad(index + 2)}.md`;
|
|
8093
|
+
for (const section of group.sections) {
|
|
8094
|
+
section.file.part = filename;
|
|
8095
|
+
}
|
|
8096
|
+
const layerCounts = {};
|
|
8097
|
+
const kindCounts = {};
|
|
8098
|
+
for (const section of group.sections) {
|
|
8099
|
+
layerCounts[section.file.layer] = (layerCounts[section.file.layer] || 0) + 1;
|
|
8100
|
+
kindCounts[section.file.kind] = (kindCounts[section.file.kind] || 0) + 1;
|
|
8101
|
+
}
|
|
8102
|
+
const pathLines = group.sections.map((s) => displayPath(s.file.relativePath));
|
|
8103
|
+
const listFence = fenceForText(pathLines.join("\n"), 3);
|
|
8104
|
+
const header = [
|
|
8105
|
+
`# Codebase Snapshot Part ${zeroPad(index + 1)} of ${zeroPad(total)}`,
|
|
8106
|
+
"",
|
|
8107
|
+
`- Previous: ${prev}`,
|
|
8108
|
+
`- Next: ${next}`,
|
|
8109
|
+
`- Files: ${group.sections.length}`,
|
|
8110
|
+
`- Content Fingerprint: ${contentFingerprint || "none"}`,
|
|
8111
|
+
`- Layers: ${topEntries(layerCounts, 6) || "none"}`,
|
|
8112
|
+
`- Kinds: ${topEntries(kindCounts, 8) || "none"}`,
|
|
8113
|
+
"",
|
|
8114
|
+
UNTRUSTED_CONTENT_NOTICE,
|
|
8115
|
+
"",
|
|
8116
|
+
"## Files",
|
|
8117
|
+
"",
|
|
8118
|
+
`${listFence}text`,
|
|
8119
|
+
...pathLines,
|
|
8120
|
+
listFence,
|
|
8121
|
+
"",
|
|
8122
|
+
"## Contents",
|
|
8123
|
+
""
|
|
8124
|
+
].join("\n");
|
|
8125
|
+
const assembled = (header + group.sections.map((s) => s.text).join("")).replace(
|
|
8126
|
+
/\r\n|\r/g,
|
|
8127
|
+
"\n"
|
|
8128
|
+
);
|
|
8129
|
+
const estimatedTokens = estimateTokens(assembled);
|
|
8130
|
+
return { filename, content: insertTokenLine(assembled, estimatedTokens), estimatedTokens };
|
|
8131
|
+
}
|
|
8132
|
+
function renderGroups(groups, contentFingerprint) {
|
|
7608
8133
|
const total = groups.length;
|
|
7609
|
-
const zeroPad = (index) => String(index).padStart(Math.max(3, String(total).length), "0");
|
|
7610
8134
|
return groups.map((group, index) => {
|
|
7611
|
-
const filename =
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
const layersCovered = Array.from(new Set(group.map((s) => s.file.layer))).sort();
|
|
7618
|
-
const layerCounts = {};
|
|
7619
|
-
const kindCounts = {};
|
|
7620
|
-
for (const section of group) {
|
|
7621
|
-
layerCounts[section.file.layer] = (layerCounts[section.file.layer] || 0) + 1;
|
|
7622
|
-
kindCounts[section.file.kind] = (kindCounts[section.file.kind] || 0) + 1;
|
|
7623
|
-
}
|
|
7624
|
-
const pathLines = group.map((s) => displayPath(s.file.relativePath));
|
|
7625
|
-
const listFence = fenceForText(pathLines.join("\n"), 3);
|
|
7626
|
-
const header = [
|
|
7627
|
-
`# Codebase Snapshot Part ${zeroPad(index + 1)} of ${zeroPad(total)}`,
|
|
7628
|
-
"",
|
|
7629
|
-
`- Previous: ${prev}`,
|
|
7630
|
-
`- Next: ${next}`,
|
|
7631
|
-
`- Files: ${group.length}`,
|
|
7632
|
-
`- Content Fingerprint: ${contentFingerprint || "none"}`,
|
|
7633
|
-
`- Layers: ${topEntries(layerCounts, 6) || "none"}`,
|
|
7634
|
-
`- Kinds: ${topEntries(kindCounts, 8) || "none"}`,
|
|
7635
|
-
"",
|
|
7636
|
-
UNTRUSTED_CONTENT_NOTICE,
|
|
7637
|
-
"",
|
|
7638
|
-
"## Files",
|
|
7639
|
-
"",
|
|
7640
|
-
`${listFence}text`,
|
|
7641
|
-
...pathLines,
|
|
7642
|
-
listFence,
|
|
7643
|
-
"",
|
|
7644
|
-
"## Contents",
|
|
7645
|
-
""
|
|
7646
|
-
].join("\n");
|
|
7647
|
-
const assembled = (header + group.map((s) => s.text).join("")).replace(/\r\n/g, "\n");
|
|
7648
|
-
const estimatedTokens = estimateTokens(assembled);
|
|
7649
|
-
const content = insertTokenLine(assembled, estimatedTokens);
|
|
8135
|
+
const { filename, content, estimatedTokens } = assembleGroup(
|
|
8136
|
+
group,
|
|
8137
|
+
index,
|
|
8138
|
+
total,
|
|
8139
|
+
contentFingerprint
|
|
8140
|
+
);
|
|
7650
8141
|
const byteSize = Buffer.byteLength(content, "utf8");
|
|
7651
8142
|
const sha256 = crypto3__namespace.createHash("sha256").update(content).digest("hex");
|
|
7652
|
-
const
|
|
8143
|
+
const layersCovered = Array.from(new Set(group.sections.map((s) => s.file.layer))).sort();
|
|
8144
|
+
const includedFiles = group.sections.map(({ file }) => ({
|
|
7653
8145
|
relativePath: file.relativePath,
|
|
7654
8146
|
sourceBytes: file.sizeBytes,
|
|
7655
8147
|
includedBytes: file.includedBytes,
|
|
@@ -7662,10 +8154,10 @@ function renderGroups(groups, budget, contentFingerprint) {
|
|
|
7662
8154
|
filename,
|
|
7663
8155
|
byteSize,
|
|
7664
8156
|
estimatedTokens,
|
|
7665
|
-
budgetBytes: budget,
|
|
8157
|
+
budgetBytes: group.budget,
|
|
7666
8158
|
sha256,
|
|
7667
8159
|
lines: countLines(content),
|
|
7668
|
-
fileCount: group.length,
|
|
8160
|
+
fileCount: group.sections.length,
|
|
7669
8161
|
layersCovered,
|
|
7670
8162
|
includedFiles
|
|
7671
8163
|
};
|
|
@@ -7673,7 +8165,7 @@ function renderGroups(groups, budget, contentFingerprint) {
|
|
|
7673
8165
|
});
|
|
7674
8166
|
}
|
|
7675
8167
|
function emittedBody(content) {
|
|
7676
|
-
const normalised = content.replace(/\r\n/g, "\n");
|
|
8168
|
+
const normalised = content.replace(/\r\n|\r/g, "\n");
|
|
7677
8169
|
return normalised.endsWith("\n") ? normalised.slice(0, -1) : normalised;
|
|
7678
8170
|
}
|
|
7679
8171
|
function compactMetadata(file) {
|
|
@@ -7741,7 +8233,7 @@ function generateMonolithJson(manifest, filesWithContent) {
|
|
|
7741
8233
|
}
|
|
7742
8234
|
function generateMonolithMarkdown(manifest, filesWithContent) {
|
|
7743
8235
|
const sections = filesWithContent.map((file) => {
|
|
7744
|
-
const content = (file.content ?? "").replace(/\r\n/g, "\n");
|
|
8236
|
+
const content = (file.content ?? "").replace(/\r\n|\r/g, "\n");
|
|
7745
8237
|
const fence = fenceForContent(content);
|
|
7746
8238
|
return [
|
|
7747
8239
|
`## File: ${escapeControls(file.relativePath)}`,
|
|
@@ -8001,22 +8493,28 @@ function updateCommand(kind) {
|
|
|
8001
8493
|
if (kind === "local") return "npm install codebase-snapshot@latest";
|
|
8002
8494
|
return "npm install -g codebase-snapshot";
|
|
8003
8495
|
}
|
|
8004
|
-
function
|
|
8005
|
-
const
|
|
8006
|
-
|
|
8496
|
+
function parseSemver(value) {
|
|
8497
|
+
const cleaned = value.trim().replace(/^v/i, "").split("+")[0] ?? "";
|
|
8498
|
+
const dash = cleaned.indexOf("-");
|
|
8499
|
+
const corePart = dash === -1 ? cleaned : cleaned.slice(0, dash);
|
|
8500
|
+
const prerelease = dash === -1 ? null : cleaned.slice(dash + 1);
|
|
8501
|
+
const core = corePart.split(".").map((part) => {
|
|
8007
8502
|
const parsed = Number(part);
|
|
8008
8503
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
8009
8504
|
});
|
|
8505
|
+
return { core, prerelease };
|
|
8010
8506
|
}
|
|
8011
8507
|
function isNewer(candidate, current) {
|
|
8012
|
-
const left =
|
|
8013
|
-
const right =
|
|
8014
|
-
|
|
8508
|
+
const left = parseSemver(candidate);
|
|
8509
|
+
const right = parseSemver(current);
|
|
8510
|
+
if (left.prerelease !== null && right.prerelease === null) return false;
|
|
8511
|
+
const length = Math.max(left.core.length, right.core.length);
|
|
8015
8512
|
for (let index = 0; index < length; index += 1) {
|
|
8016
|
-
const a = left[index] ?? 0;
|
|
8017
|
-
const b = right[index] ?? 0;
|
|
8513
|
+
const a = left.core[index] ?? 0;
|
|
8514
|
+
const b = right.core[index] ?? 0;
|
|
8018
8515
|
if (a !== b) return a > b;
|
|
8019
8516
|
}
|
|
8517
|
+
if (left.prerelease === null && right.prerelease !== null) return true;
|
|
8020
8518
|
return false;
|
|
8021
8519
|
}
|
|
8022
8520
|
function cacheDirectory(env = process.env, platform = process.platform) {
|
|
@@ -8084,14 +8582,22 @@ function writeCache(file, entry) {
|
|
|
8084
8582
|
}
|
|
8085
8583
|
}
|
|
8086
8584
|
function fetchLatestVersion(url = PACKAGE_LATEST_URL, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
8087
|
-
return new Promise((
|
|
8585
|
+
return new Promise((resolve10) => {
|
|
8088
8586
|
let settled = false;
|
|
8587
|
+
let request = null;
|
|
8089
8588
|
const finish = (value) => {
|
|
8090
8589
|
if (settled) return;
|
|
8091
8590
|
settled = true;
|
|
8092
|
-
|
|
8591
|
+
clearTimeout(deadline);
|
|
8592
|
+
resolve10(value);
|
|
8093
8593
|
};
|
|
8094
|
-
const
|
|
8594
|
+
const deadline = setTimeout(() => {
|
|
8595
|
+
request?.destroy();
|
|
8596
|
+
finish(null);
|
|
8597
|
+
}, timeoutMs);
|
|
8598
|
+
deadline.unref();
|
|
8599
|
+
const transport = url.startsWith("http:") ? http__namespace : https__namespace;
|
|
8600
|
+
request = transport.get(
|
|
8095
8601
|
url,
|
|
8096
8602
|
{
|
|
8097
8603
|
headers: {
|
|
@@ -8110,7 +8616,7 @@ function fetchLatestVersion(url = PACKAGE_LATEST_URL, timeoutMs = REQUEST_TIMEOU
|
|
|
8110
8616
|
response.on("data", (chunk) => {
|
|
8111
8617
|
body += chunk;
|
|
8112
8618
|
if (body.length > 64 * 1024) {
|
|
8113
|
-
request
|
|
8619
|
+
request?.destroy();
|
|
8114
8620
|
finish(null);
|
|
8115
8621
|
}
|
|
8116
8622
|
});
|
|
@@ -8125,10 +8631,6 @@ function fetchLatestVersion(url = PACKAGE_LATEST_URL, timeoutMs = REQUEST_TIMEOU
|
|
|
8125
8631
|
}
|
|
8126
8632
|
);
|
|
8127
8633
|
request.on("error", () => finish(null));
|
|
8128
|
-
request.setTimeout(timeoutMs, () => {
|
|
8129
|
-
request.destroy();
|
|
8130
|
-
finish(null);
|
|
8131
|
-
});
|
|
8132
8634
|
});
|
|
8133
8635
|
}
|
|
8134
8636
|
async function checkForUpdate(options) {
|
|
@@ -8287,7 +8789,10 @@ const { parentPort, workerData } = require("node:worker_threads");
|
|
|
8287
8789
|
parentPort.postMessage({ ok: false, message: loaded.errors.join("; ") });
|
|
8288
8790
|
return;
|
|
8289
8791
|
}
|
|
8290
|
-
|
|
8792
|
+
// \`hooks: {}\` is a value, not an absence: the config is applied with the hook list emptied,
|
|
8793
|
+
// so the run is the documented MCP run rather than the CLI run with one setting removed.
|
|
8794
|
+
const base = loaded.config ? { ...loaded.config, hooks: {} } : null;
|
|
8795
|
+
const config = mod.resolveConfig(base, workerData.cliOptions, workerData.cwd);
|
|
8291
8796
|
const manifest = mod.generateSnapshot(config);
|
|
8292
8797
|
parentPort.postMessage({
|
|
8293
8798
|
ok: true,
|
|
@@ -8310,14 +8815,15 @@ const { parentPort, workerData } = require("node:worker_threads");
|
|
|
8310
8815
|
})();
|
|
8311
8816
|
`;
|
|
8312
8817
|
function generateInWorker(cwd, cliOptions) {
|
|
8313
|
-
return new Promise((
|
|
8818
|
+
return new Promise((resolve10) => {
|
|
8314
8819
|
let settled = false;
|
|
8820
|
+
let worker = null;
|
|
8315
8821
|
const finish = (result) => {
|
|
8316
8822
|
if (settled) return;
|
|
8317
8823
|
settled = true;
|
|
8318
|
-
|
|
8824
|
+
if (worker !== null) void worker.terminate();
|
|
8825
|
+
resolve10(result);
|
|
8319
8826
|
};
|
|
8320
|
-
let worker;
|
|
8321
8827
|
const isCjs = typeof __dirname !== "undefined";
|
|
8322
8828
|
const directory = isCjs ? __dirname : path16__namespace.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))));
|
|
8323
8829
|
try {
|
|
@@ -8350,7 +8856,100 @@ function generateInWorker(cwd, cliOptions) {
|
|
|
8350
8856
|
|
|
8351
8857
|
// src/index.ts
|
|
8352
8858
|
var SCHEMA_VERSION = "3.0.0";
|
|
8353
|
-
var
|
|
8859
|
+
var SUMMARY_MAX_SOURCE_BYTES2 = SUMMARY_MAX_SOURCE_BYTES;
|
|
8860
|
+
var FILE_DERIVATIONS = /* @__PURE__ */ new Map();
|
|
8861
|
+
var FILE_DERIVATION_LIMIT_BYTES = 64 * 1024 * 1024;
|
|
8862
|
+
var fileDerivationBytes = 0;
|
|
8863
|
+
function takeDerivation(path22, hash, settings) {
|
|
8864
|
+
const cached = FILE_DERIVATIONS.get(path22);
|
|
8865
|
+
if (cached === void 0 || cached.hash !== hash || cached.settings !== settings) return void 0;
|
|
8866
|
+
return cached;
|
|
8867
|
+
}
|
|
8868
|
+
function rememberDerivation(path22, hash, settings, derivation) {
|
|
8869
|
+
const bodyBytes = derivation.finalContent === derivation.redacted ? derivation.redacted.length : derivation.redacted.length + derivation.finalContent.length;
|
|
8870
|
+
const bytes = bodyBytes * 2 + 512;
|
|
8871
|
+
if (bytes > FILE_DERIVATION_LIMIT_BYTES / 4) return;
|
|
8872
|
+
while (fileDerivationBytes + bytes > FILE_DERIVATION_LIMIT_BYTES) {
|
|
8873
|
+
const oldest = FILE_DERIVATIONS.keys().next();
|
|
8874
|
+
if (oldest.done === true) break;
|
|
8875
|
+
const evicted = FILE_DERIVATIONS.get(oldest.value);
|
|
8876
|
+
FILE_DERIVATIONS.delete(oldest.value);
|
|
8877
|
+
if (evicted !== void 0) fileDerivationBytes -= evicted.bytes;
|
|
8878
|
+
}
|
|
8879
|
+
FILE_DERIVATIONS.set(path22, { ...derivation, hash, settings, bytes });
|
|
8880
|
+
fileDerivationBytes += bytes;
|
|
8881
|
+
}
|
|
8882
|
+
function derivationSettings(config) {
|
|
8883
|
+
const rules = config.secretRules.map(
|
|
8884
|
+
(rule) => `${rule.rule}\0${rule.pattern.source}\0${rule.pattern.flags}\0${rule.valueGroup ?? ""}`
|
|
8885
|
+
).join("");
|
|
8886
|
+
return `${GENERATOR_VERSION}${configDigest(config)}${sha256Hex2(rules)}`;
|
|
8887
|
+
}
|
|
8888
|
+
function deriveFile(relativePath, rawContent, sizeBytes, config) {
|
|
8889
|
+
const lineCount = countLines(rawContent);
|
|
8890
|
+
const { redacted, findings } = scanAndRedactSecrets(rawContent, config.secretRules, relativePath);
|
|
8891
|
+
const isLarge = sizeBytes > config.largeFileBytes;
|
|
8892
|
+
let finalContent = redacted;
|
|
8893
|
+
let isPreviewTruncated = false;
|
|
8894
|
+
let includedLines = lineCount;
|
|
8895
|
+
let inclusionMode = "full";
|
|
8896
|
+
if (config.skeleton === true && isSkeletonizable(relativePath)) {
|
|
8897
|
+
const skeleton = skeletonizeTypeScript(redacted, relativePath);
|
|
8898
|
+
if (skeleton.collapsed > 0) {
|
|
8899
|
+
finalContent = skeleton.text;
|
|
8900
|
+
inclusionMode = "signatures";
|
|
8901
|
+
includedLines = countLines(skeleton.text);
|
|
8902
|
+
}
|
|
8903
|
+
}
|
|
8904
|
+
if (isLarge && inclusionMode !== "signatures") {
|
|
8905
|
+
const preview = truncatePreview(redacted, config.previewHeadLines, config.previewTailLines);
|
|
8906
|
+
finalContent = preview.content;
|
|
8907
|
+
isPreviewTruncated = preview.isTruncated;
|
|
8908
|
+
includedLines = preview.includedLines;
|
|
8909
|
+
inclusionMode = isPreviewTruncated ? "preview" : "full";
|
|
8910
|
+
}
|
|
8911
|
+
let summary;
|
|
8912
|
+
let summaryQuality;
|
|
8913
|
+
let summarySkip = null;
|
|
8914
|
+
let summaryWarning = null;
|
|
8915
|
+
let dependentImports = null;
|
|
8916
|
+
if (Buffer.byteLength(rawContent, "utf8") > SUMMARY_MAX_SOURCE_BYTES2) {
|
|
8917
|
+
summarySkip = "oversize";
|
|
8918
|
+
dependentImports = [];
|
|
8919
|
+
} else if (isPreviewTruncated && hasStructuralSummary(relativePath)) {
|
|
8920
|
+
summarySkip = "preview";
|
|
8921
|
+
dependentImports = importsOfText(relativePath, rawContent);
|
|
8922
|
+
} else {
|
|
8923
|
+
try {
|
|
8924
|
+
const outcome = summarizeFile(relativePath, rawContent);
|
|
8925
|
+
summary = outcome?.summary ? maskSummaryStrings(outcome.summary, config.secretRules) : void 0;
|
|
8926
|
+
summaryQuality = outcome?.quality;
|
|
8927
|
+
if (isSourceSummary(summary) && summary.parseErrors) {
|
|
8928
|
+
summaryWarning = `Parse errors in ${maskPathText(relativePath, config.secretRules)} (${summary.parseErrors}); its summary is incomplete.`;
|
|
8929
|
+
}
|
|
8930
|
+
} catch (err) {
|
|
8931
|
+
summaryWarning = `Summary skipped for ${maskPathText(relativePath, config.secretRules)}: ${err.message}`.replace(
|
|
8932
|
+
/\s+/g,
|
|
8933
|
+
" "
|
|
8934
|
+
);
|
|
8935
|
+
}
|
|
8936
|
+
}
|
|
8937
|
+
return {
|
|
8938
|
+
redacted,
|
|
8939
|
+
findings,
|
|
8940
|
+
lineCount,
|
|
8941
|
+
finalContent,
|
|
8942
|
+
inclusionMode,
|
|
8943
|
+
isPreviewTruncated,
|
|
8944
|
+
includedLines,
|
|
8945
|
+
isLarge,
|
|
8946
|
+
summary,
|
|
8947
|
+
summaryQuality,
|
|
8948
|
+
summarySkip,
|
|
8949
|
+
summaryWarning,
|
|
8950
|
+
dependentImports
|
|
8951
|
+
};
|
|
8952
|
+
}
|
|
8354
8953
|
function sha256Hex2(input) {
|
|
8355
8954
|
return crypto3__namespace.createHash("sha256").update(input).digest("hex");
|
|
8356
8955
|
}
|
|
@@ -8894,10 +9493,7 @@ function acquireRunLock(stagingRoot) {
|
|
|
8894
9493
|
};
|
|
8895
9494
|
} catch (err) {
|
|
8896
9495
|
if (err.code !== "EEXIST") throw err;
|
|
8897
|
-
if (attempt === 0 &&
|
|
8898
|
-
fs14__namespace.rmSync(lockPath, { force: true });
|
|
8899
|
-
continue;
|
|
8900
|
-
}
|
|
9496
|
+
if (attempt === 0 && reclaimStaleLock(lockPath)) continue;
|
|
8901
9497
|
throw new Error(
|
|
8902
9498
|
`Another generate run is already using the staging directory ${stagingRoot}. Wait for it to finish, or delete ${lockPath} if no other run is active.`
|
|
8903
9499
|
);
|
|
@@ -8906,14 +9502,25 @@ function acquireRunLock(stagingRoot) {
|
|
|
8906
9502
|
throw new Error(`Could not acquire the generate lock for ${stagingRoot}.`);
|
|
8907
9503
|
}
|
|
8908
9504
|
function lockKeyFor(stagingRoot) {
|
|
8909
|
-
let key = stagingRoot;
|
|
8910
|
-
try {
|
|
8911
|
-
key = fs14__namespace.realpathSync(stagingRoot);
|
|
8912
|
-
} catch {
|
|
8913
|
-
}
|
|
9505
|
+
let key = canonicalLockTarget(stagingRoot);
|
|
8914
9506
|
if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
|
|
8915
9507
|
return sha256Hex2(key).slice(0, 16);
|
|
8916
9508
|
}
|
|
9509
|
+
function canonicalLockTarget(targetPath2) {
|
|
9510
|
+
const resolved = path16__namespace.resolve(targetPath2);
|
|
9511
|
+
let existing = resolved;
|
|
9512
|
+
const tail = [];
|
|
9513
|
+
for (; ; ) {
|
|
9514
|
+
try {
|
|
9515
|
+
return path16__namespace.join(fs14__namespace.realpathSync(existing), ...tail);
|
|
9516
|
+
} catch {
|
|
9517
|
+
const parent = path16__namespace.dirname(existing);
|
|
9518
|
+
if (parent === existing) return resolved;
|
|
9519
|
+
tail.unshift(path16__namespace.basename(existing));
|
|
9520
|
+
existing = parent;
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
}
|
|
8917
9524
|
function lockPathFor(stagingRoot) {
|
|
8918
9525
|
return path16__namespace.join(os__namespace.tmpdir(), `codebase-snapshot-${lockKeyFor(stagingRoot)}.lock`);
|
|
8919
9526
|
}
|
|
@@ -8938,6 +9545,24 @@ function lockOwnerIsGone(lockPath) {
|
|
|
8938
9545
|
return err.code === "ESRCH";
|
|
8939
9546
|
}
|
|
8940
9547
|
}
|
|
9548
|
+
function reclaimStaleLock(lockPath) {
|
|
9549
|
+
let before;
|
|
9550
|
+
try {
|
|
9551
|
+
before = fs14__namespace.statSync(lockPath);
|
|
9552
|
+
} catch {
|
|
9553
|
+
return true;
|
|
9554
|
+
}
|
|
9555
|
+
if (!lockOwnerIsGone(lockPath)) return false;
|
|
9556
|
+
try {
|
|
9557
|
+
const current = fs14__namespace.statSync(lockPath);
|
|
9558
|
+
const sameFile = current.ino === before.ino && current.birthtimeMs === before.birthtimeMs && current.mtimeMs === before.mtimeMs;
|
|
9559
|
+
if (!sameFile) return false;
|
|
9560
|
+
fs14__namespace.rmSync(lockPath, { force: true });
|
|
9561
|
+
return true;
|
|
9562
|
+
} catch {
|
|
9563
|
+
return false;
|
|
9564
|
+
}
|
|
9565
|
+
}
|
|
8941
9566
|
function lockFileIsAncient(lockPath) {
|
|
8942
9567
|
try {
|
|
8943
9568
|
return Date.now() - fs14__namespace.statSync(lockPath).mtimeMs > STALE_RUN_MS;
|
|
@@ -8995,7 +9620,6 @@ function swapOnce(stagingDir, outDir) {
|
|
|
8995
9620
|
throw copyErr;
|
|
8996
9621
|
}
|
|
8997
9622
|
}
|
|
8998
|
-
if (hadPrevious) fs14__namespace.rmSync(backupDir, { recursive: true, force: true });
|
|
8999
9623
|
} catch (err) {
|
|
9000
9624
|
if (hadPrevious && fs14__namespace.existsSync(backupDir)) {
|
|
9001
9625
|
if (fs14__namespace.existsSync(outDir)) fs14__namespace.rmSync(outDir, { recursive: true, force: true });
|
|
@@ -9003,6 +9627,16 @@ function swapOnce(stagingDir, outDir) {
|
|
|
9003
9627
|
}
|
|
9004
9628
|
throw err;
|
|
9005
9629
|
}
|
|
9630
|
+
if (hadPrevious) {
|
|
9631
|
+
try {
|
|
9632
|
+
fs14__namespace.rmSync(backupDir, { recursive: true, force: true });
|
|
9633
|
+
} catch (err) {
|
|
9634
|
+
process.stderr.write(
|
|
9635
|
+
`\u26A0 The previous snapshot could not be removed from ${backupDir} (${err.message}). The new snapshot is in place; delete that directory by hand when convenient.
|
|
9636
|
+
`
|
|
9637
|
+
);
|
|
9638
|
+
}
|
|
9639
|
+
}
|
|
9006
9640
|
}
|
|
9007
9641
|
function sleepSync(milliseconds) {
|
|
9008
9642
|
const buffer = new Int32Array(new SharedArrayBuffer(4));
|
|
@@ -9025,22 +9659,18 @@ function artifactWarnings(artifacts, sarif) {
|
|
|
9025
9659
|
}
|
|
9026
9660
|
return messages;
|
|
9027
9661
|
}
|
|
9662
|
+
function installHookStdoutRedirect() {
|
|
9663
|
+
return installStdoutRouter();
|
|
9664
|
+
}
|
|
9028
9665
|
function runAfterGenerate(config, manifest) {
|
|
9029
9666
|
const hook = config.hooks.afterGenerate;
|
|
9030
9667
|
if (!hook) return null;
|
|
9031
|
-
const
|
|
9032
|
-
const stderrWrite = process.stderr.write.bind(process.stderr);
|
|
9033
|
-
const hookScope = new async_hooks.AsyncLocalStorage();
|
|
9034
|
-
const patchedWrite = ((...args) => hookScope.getStore() === true ? stderrWrite(...args) : stdoutWrite(...args));
|
|
9035
|
-
const restoreStdout = () => {
|
|
9036
|
-
if (process.stdout.write === patchedWrite) process.stdout.write = stdoutWrite;
|
|
9037
|
-
};
|
|
9668
|
+
const releaseRedirect = installHookStdoutRedirect();
|
|
9038
9669
|
let returned;
|
|
9039
9670
|
try {
|
|
9040
|
-
|
|
9041
|
-
returned = hookScope.run(true, () => hook(manifest));
|
|
9671
|
+
returned = hookStdoutScope.run(true, () => hook(manifest));
|
|
9042
9672
|
} catch (err) {
|
|
9043
|
-
|
|
9673
|
+
releaseRedirect();
|
|
9044
9674
|
const message = maskPathText(err.message, config.secretRules);
|
|
9045
9675
|
manifest.warnings.push(`hooks.afterGenerate failed: ${message}`);
|
|
9046
9676
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
@@ -9048,10 +9678,10 @@ function runAfterGenerate(config, manifest) {
|
|
|
9048
9678
|
}
|
|
9049
9679
|
const thenable = returned;
|
|
9050
9680
|
if (!thenable || typeof thenable.then !== "function") {
|
|
9051
|
-
|
|
9681
|
+
releaseRedirect();
|
|
9052
9682
|
return null;
|
|
9053
9683
|
}
|
|
9054
|
-
return Promise.resolve(returned).finally(
|
|
9684
|
+
return Promise.resolve(returned).finally(releaseRedirect);
|
|
9055
9685
|
}
|
|
9056
9686
|
function partitionByBudget(files, config, fingerprint) {
|
|
9057
9687
|
const budgetFor = (file) => config.budgets.byLayer[file.layer] ?? config.budgets.default;
|
|
@@ -9062,23 +9692,14 @@ function partitionByBudget(files, config, fingerprint) {
|
|
|
9062
9692
|
if (bucket) bucket.push(file);
|
|
9063
9693
|
else buckets.set(budget, [file]);
|
|
9064
9694
|
}
|
|
9065
|
-
if (buckets.size
|
|
9066
|
-
|
|
9067
|
-
return partitionFiles(files,
|
|
9695
|
+
if (buckets.size === 0) return [];
|
|
9696
|
+
if (buckets.size === 1) {
|
|
9697
|
+
return partitionFiles(files, Array.from(buckets.keys())[0], fingerprint);
|
|
9068
9698
|
}
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
9073
|
-
const merged = renumberParts(parts, fingerprint);
|
|
9074
|
-
const byPath = new Map(files.map((file) => [file.relativePath, file]));
|
|
9075
|
-
for (const part of merged) {
|
|
9076
|
-
for (const included of part.metadata.includedFiles) {
|
|
9077
|
-
const file = byPath.get(included.relativePath);
|
|
9078
|
-
if (file) file.part = part.filename;
|
|
9079
|
-
}
|
|
9080
|
-
}
|
|
9081
|
-
return merged;
|
|
9699
|
+
return partitionFilesForBuckets(
|
|
9700
|
+
Array.from(buckets.entries()).sort(([left], [right]) => left - right).map(([budgetBytes, bucketFiles]) => ({ files: bucketFiles, budgetBytes })),
|
|
9701
|
+
fingerprint
|
|
9702
|
+
);
|
|
9082
9703
|
}
|
|
9083
9704
|
function generateSnapshot(configInput, options) {
|
|
9084
9705
|
assertUsableConfigInput(configInput);
|
|
@@ -9110,6 +9731,7 @@ function generateSnapshot(configInput, options) {
|
|
|
9110
9731
|
return false;
|
|
9111
9732
|
}) : collection.files;
|
|
9112
9733
|
const files = [];
|
|
9734
|
+
const derivationKey = derivationSettings(config);
|
|
9113
9735
|
const monolithBodies = /* @__PURE__ */ new Map();
|
|
9114
9736
|
const skipped = maskSkipRecords([...collection.skipped], config.secretRules);
|
|
9115
9737
|
const secretFindings = [];
|
|
@@ -9122,6 +9744,7 @@ function generateSnapshot(configInput, options) {
|
|
|
9122
9744
|
const fileWarnings = [];
|
|
9123
9745
|
const oversizeSummaryPaths = [];
|
|
9124
9746
|
const previewSummaryPaths = [];
|
|
9747
|
+
const skippedSummaryImports = /* @__PURE__ */ new Map();
|
|
9125
9748
|
const layerCounts = {};
|
|
9126
9749
|
const kindCounts = {};
|
|
9127
9750
|
let totalLines = 0;
|
|
@@ -9186,8 +9809,6 @@ function generateSnapshot(configInput, options) {
|
|
|
9186
9809
|
}
|
|
9187
9810
|
const hashSha256 = sha256Hex2(buffer);
|
|
9188
9811
|
const rawContent = decoded.text;
|
|
9189
|
-
const lineCount = countLines(rawContent);
|
|
9190
|
-
totalLines += lineCount;
|
|
9191
9812
|
totalSizeBytes += stats.size;
|
|
9192
9813
|
if (decoded.encoding !== "utf-8") {
|
|
9193
9814
|
encodingNotes.push({
|
|
@@ -9201,66 +9822,25 @@ function generateSnapshot(configInput, options) {
|
|
|
9201
9822
|
const language = detectLanguage(relativePath);
|
|
9202
9823
|
bump(layerCounts, layer2);
|
|
9203
9824
|
bump(kindCounts, kind);
|
|
9204
|
-
const
|
|
9205
|
-
rawContent,
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
);
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
}
|
|
9224
|
-
if (isLarge && inclusionMode !== "signatures") {
|
|
9225
|
-
const preview = truncatePreview(
|
|
9226
|
-
redacted,
|
|
9227
|
-
config.previewHeadLines,
|
|
9228
|
-
config.previewTailLines
|
|
9229
|
-
);
|
|
9230
|
-
finalContent = preview.content;
|
|
9231
|
-
isPreviewTruncated = preview.isTruncated;
|
|
9232
|
-
includedLines = preview.includedLines;
|
|
9233
|
-
inclusionMode = isPreviewTruncated ? "preview" : "full";
|
|
9234
|
-
}
|
|
9235
|
-
if (config.includeMonolith && (isPreviewTruncated || inclusionMode === "signatures")) {
|
|
9236
|
-
monolithBodies.set(relativePath, redacted);
|
|
9237
|
-
}
|
|
9238
|
-
let summary;
|
|
9239
|
-
let summaryQuality;
|
|
9240
|
-
if (Buffer.byteLength(rawContent, "utf8") > SUMMARY_MAX_SOURCE_BYTES) {
|
|
9241
|
-
oversizeSummaryPaths.push(relativePath);
|
|
9242
|
-
} else if (isPreviewTruncated && hasStructuralSummary(relativePath)) {
|
|
9243
|
-
previewSummaryPaths.push(relativePath);
|
|
9244
|
-
} else {
|
|
9245
|
-
try {
|
|
9246
|
-
const outcome = summarizeFile(relativePath, rawContent);
|
|
9247
|
-
summary = outcome?.summary ? maskSummaryStrings(outcome.summary, config.secretRules) : void 0;
|
|
9248
|
-
summaryQuality = outcome?.quality;
|
|
9249
|
-
if (isSourceSummary(summary) && summary.parseErrors) {
|
|
9250
|
-
fileWarnings.push({
|
|
9251
|
-
path: relativePath,
|
|
9252
|
-
message: `Parse errors in ${maskPathText(relativePath, config.secretRules)} (${summary.parseErrors}); its summary is incomplete.`
|
|
9253
|
-
});
|
|
9254
|
-
}
|
|
9255
|
-
} catch (err) {
|
|
9256
|
-
fileWarnings.push({
|
|
9257
|
-
path: relativePath,
|
|
9258
|
-
message: `Summary skipped for ${maskPathText(relativePath, config.secretRules)}: ${err.message}`.replace(
|
|
9259
|
-
/\s+/g,
|
|
9260
|
-
" "
|
|
9261
|
-
)
|
|
9262
|
-
});
|
|
9263
|
-
}
|
|
9825
|
+
const derivation = takeDerivation(absolutePath, hashSha256, derivationKey) ?? (() => {
|
|
9826
|
+
const fresh = deriveFile(relativePath, rawContent, stats.size, config);
|
|
9827
|
+
rememberDerivation(absolutePath, hashSha256, derivationKey, fresh);
|
|
9828
|
+
return fresh;
|
|
9829
|
+
})();
|
|
9830
|
+
const { lineCount } = derivation;
|
|
9831
|
+
totalLines += lineCount;
|
|
9832
|
+
secretFindings.push(...derivation.findings);
|
|
9833
|
+
if (derivation.isLarge) largeFileCount += 1;
|
|
9834
|
+
if (derivation.summarySkip === "oversize") oversizeSummaryPaths.push(relativePath);
|
|
9835
|
+
else if (derivation.summarySkip === "preview") previewSummaryPaths.push(relativePath);
|
|
9836
|
+
if (derivation.dependentImports !== null) {
|
|
9837
|
+
skippedSummaryImports.set(relativePath, derivation.dependentImports);
|
|
9838
|
+
}
|
|
9839
|
+
if (derivation.summaryWarning !== null) {
|
|
9840
|
+
fileWarnings.push({ path: relativePath, message: derivation.summaryWarning });
|
|
9841
|
+
}
|
|
9842
|
+
if (config.includeMonolith && (derivation.isPreviewTruncated || derivation.inclusionMode === "signatures")) {
|
|
9843
|
+
monolithBodies.set(relativePath, derivation.redacted);
|
|
9264
9844
|
}
|
|
9265
9845
|
files.push({
|
|
9266
9846
|
relativePath,
|
|
@@ -9271,24 +9851,25 @@ function generateSnapshot(configInput, options) {
|
|
|
9271
9851
|
layer: layer2,
|
|
9272
9852
|
kind,
|
|
9273
9853
|
language,
|
|
9274
|
-
inclusionMode,
|
|
9275
|
-
isLarge,
|
|
9276
|
-
isPreviewTruncated,
|
|
9854
|
+
inclusionMode: derivation.inclusionMode,
|
|
9855
|
+
isLarge: derivation.isLarge,
|
|
9856
|
+
isPreviewTruncated: derivation.isPreviewTruncated,
|
|
9277
9857
|
originalLines: lineCount,
|
|
9278
|
-
includedLines,
|
|
9279
|
-
includedBytes: Buffer.byteLength(finalContent, "utf8"),
|
|
9280
|
-
summary,
|
|
9281
|
-
summaryQuality,
|
|
9858
|
+
includedLines: derivation.includedLines,
|
|
9859
|
+
includedBytes: Buffer.byteLength(derivation.finalContent, "utf8"),
|
|
9860
|
+
summary: derivation.summary,
|
|
9861
|
+
summaryQuality: derivation.summaryQuality,
|
|
9282
9862
|
...decoded.encoding === "utf-8" ? {} : { encoding: decoded.encoding },
|
|
9283
|
-
content: finalContent
|
|
9863
|
+
content: derivation.finalContent
|
|
9284
9864
|
});
|
|
9285
9865
|
}
|
|
9286
9866
|
if (changedScope !== null && changedPaths !== null && config.withDependents === true) {
|
|
9287
9867
|
const keep = new Set(changedPaths);
|
|
9288
9868
|
const resolver = buildImportResolver(files);
|
|
9289
9869
|
for (const file of files) {
|
|
9290
|
-
|
|
9291
|
-
if (
|
|
9870
|
+
const fileImports = isSourceSummary(file.summary) ? file.summary.imports : skippedSummaryImports.get(file.relativePath) ?? [];
|
|
9871
|
+
if (fileImports.length === 0) continue;
|
|
9872
|
+
if (importsChangedFile(file.relativePath, fileImports, changedPaths, resolver)) {
|
|
9292
9873
|
keep.add(file.relativePath);
|
|
9293
9874
|
}
|
|
9294
9875
|
}
|
|
@@ -9319,6 +9900,9 @@ function generateSnapshot(configInput, options) {
|
|
|
9319
9900
|
for (const path22 of [...monolithBodies.keys()]) {
|
|
9320
9901
|
if (!keep.has(path22)) monolithBodies.delete(path22);
|
|
9321
9902
|
}
|
|
9903
|
+
for (const path22 of [...skippedSummaryImports.keys()]) {
|
|
9904
|
+
if (!keep.has(path22)) skippedSummaryImports.delete(path22);
|
|
9905
|
+
}
|
|
9322
9906
|
}
|
|
9323
9907
|
if (changedScope !== null) {
|
|
9324
9908
|
for (const relativePath of unchangedPaths) {
|
|
@@ -9354,7 +9938,7 @@ function generateSnapshot(configInput, options) {
|
|
|
9354
9938
|
const shown = oversizeSummaryPaths.slice(0, ENCODING_PATHS_SHOWN).map((entry) => maskPathText(entry, config.secretRules));
|
|
9355
9939
|
const hidden = oversizeSummaryPaths.length - ENCODING_PATHS_SHOWN;
|
|
9356
9940
|
warnings.push(
|
|
9357
|
-
`${oversizeSummaryPaths.length} file(s) exceed ${Math.round(
|
|
9941
|
+
`${oversizeSummaryPaths.length} file(s) exceed ${Math.round(SUMMARY_MAX_SOURCE_BYTES2 / (1024 * 1024))} MB and were not parsed for a structural summary: ${shown.join(", ")}${hidden > 0 ? ` and ${hidden} more` : ""}. Their bodies are emitted as head/tail previews.`
|
|
9358
9942
|
);
|
|
9359
9943
|
}
|
|
9360
9944
|
for (const [encoding, bucket] of encodingBuckets) {
|
|
@@ -9378,11 +9962,13 @@ function generateSnapshot(configInput, options) {
|
|
|
9378
9962
|
).join("\n")
|
|
9379
9963
|
);
|
|
9380
9964
|
const fullContents = /* @__PURE__ */ new Map();
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9965
|
+
if (config.includeMonolith) {
|
|
9966
|
+
for (const file of files) {
|
|
9967
|
+
fullContents.set(
|
|
9968
|
+
file.relativePath,
|
|
9969
|
+
monolithBodies.get(file.relativePath) ?? file.content ?? ""
|
|
9970
|
+
);
|
|
9971
|
+
}
|
|
9386
9972
|
}
|
|
9387
9973
|
const parts = partitionByBudget(files, config, fingerprint);
|
|
9388
9974
|
const truncatedPreviewCount = files.filter((file) => file.isPreviewTruncated).length;
|
|
@@ -9643,7 +10229,7 @@ function generateSnapshot(configInput, options) {
|
|
|
9643
10229
|
};
|
|
9644
10230
|
shipHookState();
|
|
9645
10231
|
if (pendingHook) {
|
|
9646
|
-
pendingHook.then(
|
|
10232
|
+
const tracked = pendingHook.then(
|
|
9647
10233
|
() => {
|
|
9648
10234
|
try {
|
|
9649
10235
|
shipHookState();
|
|
@@ -9662,8 +10248,10 @@ function generateSnapshot(configInput, options) {
|
|
|
9662
10248
|
} catch {
|
|
9663
10249
|
}
|
|
9664
10250
|
}
|
|
9665
|
-
)
|
|
9666
|
-
|
|
10251
|
+
).finally(() => {
|
|
10252
|
+
if (pendingHooks.get(outDir) === tracked) pendingHooks.delete(outDir);
|
|
10253
|
+
});
|
|
10254
|
+
pendingHooks.set(outDir, tracked);
|
|
9667
10255
|
}
|
|
9668
10256
|
return manifest;
|
|
9669
10257
|
} catch (err) {
|
|
@@ -9837,12 +10425,35 @@ async function openWorkspace(root, overrides = {}) {
|
|
|
9837
10425
|
}
|
|
9838
10426
|
cliOptions.maxPartBytes = parsed;
|
|
9839
10427
|
}
|
|
10428
|
+
if (overrides.maxPartTokens !== void 0) {
|
|
10429
|
+
const parsed = parseByteSize(overrides.maxPartTokens);
|
|
10430
|
+
if (parsed === void 0) {
|
|
10431
|
+
return {
|
|
10432
|
+
ok: false,
|
|
10433
|
+
message: '"maxPartTokens" must be a token count, for example 32000 or "32k".'
|
|
10434
|
+
};
|
|
10435
|
+
}
|
|
10436
|
+
cliOptions.maxPartTokens = parsed;
|
|
10437
|
+
}
|
|
9840
10438
|
if (overrides.outDir !== void 0) {
|
|
9841
10439
|
if (typeof overrides.outDir !== "string" || overrides.outDir.trim().length === 0) {
|
|
9842
10440
|
return { ok: false, message: '"outDir" must be a non-empty string.' };
|
|
9843
10441
|
}
|
|
9844
10442
|
cliOptions.outDir = overrides.outDir.trim();
|
|
9845
10443
|
}
|
|
10444
|
+
if (overrides.changedSince !== void 0) {
|
|
10445
|
+
if (typeof overrides.changedSince !== "string" || overrides.changedSince.trim().length === 0) {
|
|
10446
|
+
return {
|
|
10447
|
+
ok: false,
|
|
10448
|
+
message: '"changedSince" must be a git ref such as "main" or "HEAD~3".'
|
|
10449
|
+
};
|
|
10450
|
+
}
|
|
10451
|
+
cliOptions.changedSince = overrides.changedSince.trim();
|
|
10452
|
+
}
|
|
10453
|
+
if (overrides.skeleton === true) cliOptions.skeleton = true;
|
|
10454
|
+
if (overrides.sarif === true) cliOptions.sarif = true;
|
|
10455
|
+
if (overrides.staged === true) cliOptions.staged = true;
|
|
10456
|
+
if (overrides.withDependents === true) cliOptions.withDependents = true;
|
|
9846
10457
|
try {
|
|
9847
10458
|
const config = resolveConfig(loaded.config, cliOptions, root);
|
|
9848
10459
|
return { ok: true, workspace: { root, config, configFile: loaded.file, cliOptions } };
|
|
@@ -9854,6 +10465,16 @@ var manifestCache = /* @__PURE__ */ new Map();
|
|
|
9854
10465
|
function manifestPathFor(workspace) {
|
|
9855
10466
|
return path16__namespace.resolve(workspace.root, workspace.config.outDir, "manifest.json");
|
|
9856
10467
|
}
|
|
10468
|
+
function partPathFor(workspace, filename) {
|
|
10469
|
+
if (filename.length === 0 || filename.includes("\0")) return null;
|
|
10470
|
+
const partsDir = path16__namespace.resolve(workspace.root, workspace.config.outDir, "parts");
|
|
10471
|
+
const resolved = path16__namespace.resolve(partsDir, filename);
|
|
10472
|
+
const relative12 = path16__namespace.relative(partsDir, resolved);
|
|
10473
|
+
if (relative12.length === 0 || relative12.includes(path16__namespace.sep) || relative12.includes("/") || relative12.includes("\\") || path16__namespace.isAbsolute(relative12)) {
|
|
10474
|
+
return null;
|
|
10475
|
+
}
|
|
10476
|
+
return resolved;
|
|
10477
|
+
}
|
|
9857
10478
|
function readManifest3(workspace) {
|
|
9858
10479
|
const file = manifestPathFor(workspace);
|
|
9859
10480
|
const shown = path16__namespace.relative(workspace.root, file).split(path16__namespace.sep).join("/") || file;
|
|
@@ -9924,6 +10545,44 @@ var OUT_DIR_PROPERTY = {
|
|
|
9924
10545
|
description: "Snapshot directory, relative to the root. Defaults to the project configuration's outDir."
|
|
9925
10546
|
};
|
|
9926
10547
|
var outDirOverride = (args) => ({ outDir: args.outDir });
|
|
10548
|
+
var SCOPE_PROPERTIES = {
|
|
10549
|
+
maxPartTokens: {
|
|
10550
|
+
type: "string",
|
|
10551
|
+
description: 'Token ceiling per part, for example 32000 or "32k". Narrower than maxPartBytes; it converts at four bytes per token.'
|
|
10552
|
+
},
|
|
10553
|
+
skeleton: {
|
|
10554
|
+
type: "boolean",
|
|
10555
|
+
description: "Replace every function body with `{ /* ... */ }`, keeping signatures, imports and exports. Applies to TypeScript, JavaScript and JSX."
|
|
10556
|
+
},
|
|
10557
|
+
sarif: {
|
|
10558
|
+
type: "boolean",
|
|
10559
|
+
description: "Also write reports/audit.sarif beside reports/audit.md, for CI code scanning."
|
|
10560
|
+
},
|
|
10561
|
+
changedSince: {
|
|
10562
|
+
type: "string",
|
|
10563
|
+
description: 'Only files changed since this git ref, for example "main" or "HEAD~3".'
|
|
10564
|
+
},
|
|
10565
|
+
staged: {
|
|
10566
|
+
type: "boolean",
|
|
10567
|
+
description: "Only files staged in the git index."
|
|
10568
|
+
},
|
|
10569
|
+
withDependents: {
|
|
10570
|
+
type: "boolean",
|
|
10571
|
+
description: "With staged or changedSince, also include the files that import the changed ones (one hop)."
|
|
10572
|
+
}
|
|
10573
|
+
};
|
|
10574
|
+
function workspaceOverrides(args) {
|
|
10575
|
+
return {
|
|
10576
|
+
outDir: args.outDir,
|
|
10577
|
+
maxPartBytes: args.maxPartBytes,
|
|
10578
|
+
maxPartTokens: args.maxPartTokens,
|
|
10579
|
+
skeleton: args.skeleton,
|
|
10580
|
+
sarif: args.sarif,
|
|
10581
|
+
changedSince: args.changedSince,
|
|
10582
|
+
staged: args.staged,
|
|
10583
|
+
withDependents: args.withDependents
|
|
10584
|
+
};
|
|
10585
|
+
}
|
|
9927
10586
|
function failure(message) {
|
|
9928
10587
|
return { text: message, isError: true };
|
|
9929
10588
|
}
|
|
@@ -9954,12 +10613,13 @@ function summarizeDrift(drift) {
|
|
|
9954
10613
|
}
|
|
9955
10614
|
function toRelativePath(root, value) {
|
|
9956
10615
|
const cleaned = value.trim();
|
|
9957
|
-
if (cleaned.length === 0) return null;
|
|
9958
|
-
|
|
9959
|
-
|
|
10616
|
+
if (cleaned.length === 0 || cleaned.includes("\0")) return null;
|
|
10617
|
+
const resolvedRoot = path16__namespace.resolve(root);
|
|
10618
|
+
const absolute = path16__namespace.resolve(resolvedRoot, cleaned);
|
|
10619
|
+
const relative12 = path16__namespace.relative(resolvedRoot, absolute);
|
|
10620
|
+
if (relative12 === "" || relative12 === ".." || relative12.startsWith(`..${path16__namespace.sep}`) || path16__namespace.isAbsolute(relative12)) {
|
|
10621
|
+
return null;
|
|
9960
10622
|
}
|
|
9961
|
-
const relative12 = path16__namespace.relative(root, cleaned);
|
|
9962
|
-
if (relative12.startsWith("..") || path16__namespace.isAbsolute(relative12)) return null;
|
|
9963
10623
|
return relative12.split(path16__namespace.sep).join("/");
|
|
9964
10624
|
}
|
|
9965
10625
|
function symbolsOf(entry) {
|
|
@@ -10095,7 +10755,8 @@ var planTool = {
|
|
|
10095
10755
|
maxPartBytes: {
|
|
10096
10756
|
type: "string",
|
|
10097
10757
|
description: 'Byte budget per part, for example 307200 or "300k". Defaults to the project configuration.'
|
|
10098
|
-
}
|
|
10758
|
+
},
|
|
10759
|
+
...SCOPE_PROPERTIES
|
|
10099
10760
|
},
|
|
10100
10761
|
additionalProperties: false
|
|
10101
10762
|
},
|
|
@@ -10112,7 +10773,7 @@ var planTool = {
|
|
|
10112
10773
|
},
|
|
10113
10774
|
annotations: READ_ONLY,
|
|
10114
10775
|
async handler(args, context) {
|
|
10115
|
-
const prepared = await prepare(args, context,
|
|
10776
|
+
const prepared = await prepare(args, context, workspaceOverrides(args));
|
|
10116
10777
|
if (!prepared.ok) return prepared.outcome;
|
|
10117
10778
|
const workspace = prepared.workspace;
|
|
10118
10779
|
let manifest;
|
|
@@ -10170,7 +10831,8 @@ var generateTool = {
|
|
|
10170
10831
|
maxPartBytes: {
|
|
10171
10832
|
type: "string",
|
|
10172
10833
|
description: 'Byte budget per part, for example 307200 or "300k". Defaults to the project configuration.'
|
|
10173
|
-
}
|
|
10834
|
+
},
|
|
10835
|
+
...SCOPE_PROPERTIES
|
|
10174
10836
|
},
|
|
10175
10837
|
additionalProperties: false
|
|
10176
10838
|
},
|
|
@@ -10186,10 +10848,7 @@ var generateTool = {
|
|
|
10186
10848
|
},
|
|
10187
10849
|
annotations: WRITES,
|
|
10188
10850
|
async handler(args, context) {
|
|
10189
|
-
const prepared = await prepare(args, context,
|
|
10190
|
-
maxPartBytes: args.maxPartBytes,
|
|
10191
|
-
outDir: args.outDir
|
|
10192
|
-
});
|
|
10851
|
+
const prepared = await prepare(args, context, workspaceOverrides(args));
|
|
10193
10852
|
if (!prepared.ok) return prepared.outcome;
|
|
10194
10853
|
const workspace = prepared.workspace;
|
|
10195
10854
|
const result = await generateInWorker(workspace.root, workspace.cliOptions);
|
|
@@ -10228,7 +10887,7 @@ var validateTool = {
|
|
|
10228
10887
|
description: "Re-read the repository and report whether the snapshot on disk still matches it. Use it before trusting context that came from an earlier session, or after changing the config.",
|
|
10229
10888
|
inputSchema: {
|
|
10230
10889
|
type: "object",
|
|
10231
|
-
properties: { root: ROOT_PROPERTY, outDir: OUT_DIR_PROPERTY },
|
|
10890
|
+
properties: { root: ROOT_PROPERTY, outDir: OUT_DIR_PROPERTY, ...SCOPE_PROPERTIES },
|
|
10232
10891
|
additionalProperties: false
|
|
10233
10892
|
},
|
|
10234
10893
|
outputSchema: {
|
|
@@ -10240,7 +10899,7 @@ var validateTool = {
|
|
|
10240
10899
|
},
|
|
10241
10900
|
annotations: READ_ONLY,
|
|
10242
10901
|
async handler(args, context) {
|
|
10243
|
-
const prepared = await prepare(args, context,
|
|
10902
|
+
const prepared = await prepare(args, context, workspaceOverrides(args));
|
|
10244
10903
|
if (!prepared.ok) return prepared.outcome;
|
|
10245
10904
|
const workspace = prepared.workspace;
|
|
10246
10905
|
let result;
|
|
@@ -10585,7 +11244,12 @@ var readTool = {
|
|
|
10585
11244
|
`${relativePath} is not in the snapshot. Call snapshot_locate to find the path, or snapshot_explain to learn why it was excluded.`
|
|
10586
11245
|
);
|
|
10587
11246
|
}
|
|
10588
|
-
const partPath =
|
|
11247
|
+
const partPath = partPathFor(workspace, entry.part);
|
|
11248
|
+
if (partPath === null) {
|
|
11249
|
+
return failure(
|
|
11250
|
+
`${entry.part} is not a part of this snapshot: its name leaves ${workspace.config.outDir}/parts. Run snapshot_generate to rebuild it.`
|
|
11251
|
+
);
|
|
11252
|
+
}
|
|
10589
11253
|
let partText;
|
|
10590
11254
|
try {
|
|
10591
11255
|
partText = fs14__namespace.readFileSync(partPath, "utf8");
|
|
@@ -10665,7 +11329,8 @@ var explainTool = {
|
|
|
10665
11329
|
description: "Path relative to the repository root. Directories are explained as a whole."
|
|
10666
11330
|
},
|
|
10667
11331
|
root: ROOT_PROPERTY,
|
|
10668
|
-
outDir: OUT_DIR_PROPERTY
|
|
11332
|
+
outDir: OUT_DIR_PROPERTY,
|
|
11333
|
+
...SCOPE_PROPERTIES
|
|
10669
11334
|
},
|
|
10670
11335
|
required: ["path"],
|
|
10671
11336
|
additionalProperties: false
|
|
@@ -10680,7 +11345,7 @@ var explainTool = {
|
|
|
10680
11345
|
},
|
|
10681
11346
|
annotations: READ_ONLY,
|
|
10682
11347
|
async handler(args, context) {
|
|
10683
|
-
const prepared = await prepare(args, context,
|
|
11348
|
+
const prepared = await prepare(args, context, workspaceOverrides(args));
|
|
10684
11349
|
if (!prepared.ok) return prepared.outcome;
|
|
10685
11350
|
const workspace = prepared.workspace;
|
|
10686
11351
|
if (typeof args.path !== "string") return failure('"path" must be a string.');
|
|
@@ -11518,7 +12183,7 @@ function buildAgentContract() {
|
|
|
11518
12183
|
{ key: "preset", type: "string", default: "detected", summary: "Force a preset. Unknown names are an error." },
|
|
11519
12184
|
{ key: "maxPartBytes", type: "number | string", default: 307200, summary: "Per-part byte budget." },
|
|
11520
12185
|
{ key: "maxPartTokens", type: "number | string | null", default: null, summary: "Per-part token ceiling; narrows maxPartBytes. Minimum 1024." },
|
|
11521
|
-
{ key: "skeleton", type: "boolean", default: false, summary: "Collapse function bodies to `{ /* ... */ }` in
|
|
12186
|
+
{ key: "skeleton", type: "boolean", default: false, summary: "Collapse function bodies to `{ /* ... */ }` in TS, JS, TSX and JSX files." },
|
|
11522
12187
|
{ key: "sarif", type: "boolean", default: false, summary: "Also write reports/audit.sarif." },
|
|
11523
12188
|
{ key: "changedSince", type: "string | null", default: null, summary: "Snapshot only files that differ from this git ref." },
|
|
11524
12189
|
{ key: "staged", type: "boolean", default: false, summary: "Snapshot only files staged in the index." },
|
|
@@ -12452,9 +13117,9 @@ function buildResourceProvider() {
|
|
|
12452
13117
|
const index = Number(partMatch[1]);
|
|
12453
13118
|
const part = read.manifest.parts.find((candidate) => candidate.index === index);
|
|
12454
13119
|
if (!part) return null;
|
|
12455
|
-
const
|
|
12456
|
-
|
|
12457
|
-
);
|
|
13120
|
+
const partPath = partPathFor(workspace, part.filename);
|
|
13121
|
+
if (partPath === null) return null;
|
|
13122
|
+
const text2 = readFileIfPresent(partPath);
|
|
12458
13123
|
if (text2 === null) return null;
|
|
12459
13124
|
return { uri, mimeType: "text/markdown", text: capUtf8(text2, MAX_RESOURCE_BYTES).text };
|
|
12460
13125
|
}
|
|
@@ -12486,11 +13151,11 @@ var MAX_LINE_BYTES = 32 * 1024 * 1024;
|
|
|
12486
13151
|
function createWriter(output) {
|
|
12487
13152
|
return {
|
|
12488
13153
|
write(value) {
|
|
12489
|
-
return new Promise((
|
|
13154
|
+
return new Promise((resolve10, reject) => {
|
|
12490
13155
|
output.write(`${JSON.stringify(value)}
|
|
12491
13156
|
`, (error) => {
|
|
12492
13157
|
if (error) reject(error);
|
|
12493
|
-
else
|
|
13158
|
+
else resolve10();
|
|
12494
13159
|
});
|
|
12495
13160
|
});
|
|
12496
13161
|
}
|
|
@@ -12498,6 +13163,7 @@ function createWriter(output) {
|
|
|
12498
13163
|
}
|
|
12499
13164
|
function createLineReader(input, options) {
|
|
12500
13165
|
let buffer = "";
|
|
13166
|
+
let bufferBytes = 0;
|
|
12501
13167
|
let discarding = false;
|
|
12502
13168
|
let discardedBytes = 0;
|
|
12503
13169
|
const deliver = (line) => {
|
|
@@ -12510,6 +13176,13 @@ function createLineReader(input, options) {
|
|
|
12510
13176
|
if (trimmed.trim().length === 0) return;
|
|
12511
13177
|
options.onLine(trimmed);
|
|
12512
13178
|
};
|
|
13179
|
+
const discardBuffered = () => {
|
|
13180
|
+
discarding = true;
|
|
13181
|
+
discardedBytes += bufferBytes;
|
|
13182
|
+
buffer = "";
|
|
13183
|
+
bufferBytes = 0;
|
|
13184
|
+
options.onOverflow?.(discardedBytes);
|
|
13185
|
+
};
|
|
12513
13186
|
const drain = () => {
|
|
12514
13187
|
let index = buffer.indexOf("\n");
|
|
12515
13188
|
while (index >= 0) {
|
|
@@ -12517,42 +13190,57 @@ function createLineReader(input, options) {
|
|
|
12517
13190
|
buffer = buffer.slice(index + 1);
|
|
12518
13191
|
if (discarding) {
|
|
12519
13192
|
deliver(line);
|
|
12520
|
-
} else if (line.length > MAX_LINE_BYTES) {
|
|
12521
|
-
options.onOverflow?.(line.length);
|
|
12522
13193
|
} else {
|
|
12523
|
-
|
|
13194
|
+
const lineBytes = Buffer.byteLength(line, "utf8");
|
|
13195
|
+
if (lineBytes > MAX_LINE_BYTES) options.onOverflow?.(lineBytes);
|
|
13196
|
+
else deliver(line);
|
|
12524
13197
|
}
|
|
12525
13198
|
index = buffer.indexOf("\n");
|
|
12526
13199
|
}
|
|
13200
|
+
bufferBytes = Buffer.byteLength(buffer, "utf8");
|
|
13201
|
+
if (!discarding && bufferBytes > MAX_LINE_BYTES) discardBuffered();
|
|
12527
13202
|
};
|
|
12528
13203
|
const onData = (chunk) => {
|
|
13204
|
+
const chunkBytes = Buffer.byteLength(chunk, "utf8");
|
|
12529
13205
|
if (discarding) {
|
|
12530
13206
|
const index = chunk.indexOf("\n");
|
|
12531
13207
|
if (index < 0) {
|
|
12532
|
-
discardedBytes +=
|
|
13208
|
+
discardedBytes += chunkBytes;
|
|
12533
13209
|
return;
|
|
12534
13210
|
}
|
|
12535
13211
|
buffer = chunk.slice(index + 1);
|
|
13212
|
+
bufferBytes = Buffer.byteLength(buffer, "utf8");
|
|
12536
13213
|
deliver("");
|
|
12537
13214
|
drain();
|
|
12538
13215
|
return;
|
|
12539
13216
|
}
|
|
12540
|
-
|
|
12541
|
-
drain();
|
|
12542
|
-
if (buffer.length > MAX_LINE_BYTES) {
|
|
13217
|
+
if (chunk.indexOf("\n") < 0 && bufferBytes + chunkBytes > MAX_LINE_BYTES) {
|
|
12543
13218
|
discarding = true;
|
|
12544
|
-
discardedBytes =
|
|
13219
|
+
discardedBytes = bufferBytes + chunkBytes;
|
|
12545
13220
|
buffer = "";
|
|
13221
|
+
bufferBytes = 0;
|
|
12546
13222
|
options.onOverflow?.(discardedBytes);
|
|
12547
13223
|
return;
|
|
12548
13224
|
}
|
|
13225
|
+
buffer += chunk;
|
|
13226
|
+
drain();
|
|
12549
13227
|
};
|
|
12550
13228
|
const onEnd = () => {
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
12554
|
-
|
|
13229
|
+
const rest = buffer;
|
|
13230
|
+
const restBytes = bufferBytes || Buffer.byteLength(rest, "utf8");
|
|
13231
|
+
buffer = "";
|
|
13232
|
+
bufferBytes = 0;
|
|
13233
|
+
if (rest.length === 0) return;
|
|
13234
|
+
if (discarding) {
|
|
13235
|
+
discarding = false;
|
|
13236
|
+
discardedBytes = 0;
|
|
13237
|
+
return;
|
|
12555
13238
|
}
|
|
13239
|
+
if (restBytes > MAX_LINE_BYTES) {
|
|
13240
|
+
options.onOverflow?.(restBytes);
|
|
13241
|
+
return;
|
|
13242
|
+
}
|
|
13243
|
+
deliver(rest);
|
|
12556
13244
|
};
|
|
12557
13245
|
input.setEncoding("utf8");
|
|
12558
13246
|
input.on("data", onData);
|
|
@@ -12603,8 +13291,8 @@ async function runStdioServer(options) {
|
|
|
12603
13291
|
let inFlight = 0;
|
|
12604
13292
|
let inputEnded = false;
|
|
12605
13293
|
let resolveDone = () => void 0;
|
|
12606
|
-
const finished = new Promise((
|
|
12607
|
-
resolveDone =
|
|
13294
|
+
const finished = new Promise((resolve10) => {
|
|
13295
|
+
resolveDone = resolve10;
|
|
12608
13296
|
});
|
|
12609
13297
|
const finishIfIdle = () => {
|
|
12610
13298
|
if (inputEnded && inFlight === 0) resolveDone(0);
|
|
@@ -13128,7 +13816,9 @@ function nextStepHint(cwd, scriptsAddedNow) {
|
|
|
13128
13816
|
let hasScript = scriptsAddedNow;
|
|
13129
13817
|
if (!hasScript) {
|
|
13130
13818
|
try {
|
|
13131
|
-
const parsed = JSON.parse(
|
|
13819
|
+
const parsed = JSON.parse(
|
|
13820
|
+
stripJsonComments2(fs14__namespace.readFileSync(path16__namespace.join(cwd, "package.json"), "utf8"))
|
|
13821
|
+
);
|
|
13132
13822
|
hasScript = typeof parsed.scripts?.snapshot === "string";
|
|
13133
13823
|
} catch {
|
|
13134
13824
|
hasScript = false;
|
|
@@ -13230,18 +13920,8 @@ function installDependencies(cwd) {
|
|
|
13230
13920
|
return { manager: manager.name, command, ok: true, detail: seconds };
|
|
13231
13921
|
}
|
|
13232
13922
|
async function captureStdout(fn) {
|
|
13233
|
-
const
|
|
13234
|
-
|
|
13235
|
-
process.stdout.write = ((chunk) => {
|
|
13236
|
-
text2 += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
13237
|
-
return true;
|
|
13238
|
-
});
|
|
13239
|
-
try {
|
|
13240
|
-
const code = await fn();
|
|
13241
|
-
return { code, text: text2 };
|
|
13242
|
-
} finally {
|
|
13243
|
-
process.stdout.write = original;
|
|
13244
|
-
}
|
|
13923
|
+
const { value, text: text2 } = await runWithStdoutCapture(fn);
|
|
13924
|
+
return { code: value, text: text2 };
|
|
13245
13925
|
}
|
|
13246
13926
|
async function runSetupCommand(invocation, options = {}) {
|
|
13247
13927
|
try {
|
|
@@ -13356,31 +14036,33 @@ async function runSetupCommand(invocation, options = {}) {
|
|
|
13356
14036
|
if (invocation.watch) generateArgs.push("--watch");
|
|
13357
14037
|
if (invocation.format === "json") {
|
|
13358
14038
|
const generated = await captureStdout(() => main(generateArgs));
|
|
13359
|
-
let
|
|
14039
|
+
let parsed = null;
|
|
13360
14040
|
try {
|
|
13361
|
-
|
|
14041
|
+
parsed = JSON.parse(generated.text);
|
|
13362
14042
|
} catch {
|
|
13363
|
-
|
|
14043
|
+
parsed = null;
|
|
14044
|
+
}
|
|
14045
|
+
const setup = {
|
|
14046
|
+
configPath: init.configPath,
|
|
14047
|
+
configReused: init.reused,
|
|
14048
|
+
preset: init.preset,
|
|
14049
|
+
scripts: init.scripts,
|
|
14050
|
+
dependency: init.dependency,
|
|
14051
|
+
gitignoreEntries: init.gitignoreEntries,
|
|
14052
|
+
install,
|
|
14053
|
+
notes: init.notes,
|
|
14054
|
+
hook
|
|
14055
|
+
};
|
|
14056
|
+
if (generated.code !== 0) {
|
|
14057
|
+
const inner = parsed && typeof parsed.error === "object" && parsed.error !== null ? parsed.error : { message: "Snapshot generation failed during setup.", exitCode: generated.code };
|
|
14058
|
+
process.stdout.write(
|
|
14059
|
+
`${JSON.stringify({ error: inner, setup, manifest: null }, null, 2)}
|
|
14060
|
+
`
|
|
14061
|
+
);
|
|
14062
|
+
return generated.code;
|
|
13364
14063
|
}
|
|
13365
14064
|
process.stdout.write(
|
|
13366
|
-
`${JSON.stringify(
|
|
13367
|
-
{
|
|
13368
|
-
setup: {
|
|
13369
|
-
configPath: init.configPath,
|
|
13370
|
-
configReused: init.reused,
|
|
13371
|
-
preset: init.preset,
|
|
13372
|
-
scripts: init.scripts,
|
|
13373
|
-
dependency: init.dependency,
|
|
13374
|
-
gitignoreEntries: init.gitignoreEntries,
|
|
13375
|
-
install,
|
|
13376
|
-
notes: init.notes,
|
|
13377
|
-
hook
|
|
13378
|
-
},
|
|
13379
|
-
manifest
|
|
13380
|
-
},
|
|
13381
|
-
null,
|
|
13382
|
-
2
|
|
13383
|
-
)}
|
|
14065
|
+
`${JSON.stringify({ setup, manifest: parsed }, null, 2)}
|
|
13384
14066
|
`
|
|
13385
14067
|
);
|
|
13386
14068
|
return generated.code;
|
|
@@ -13573,6 +14255,10 @@ ${USAGE}
|
|
|
13573
14255
|
largeFileBytes: invocation.largeFileBytes,
|
|
13574
14256
|
maxMonolithBytes: invocation.maxMonolithBytes,
|
|
13575
14257
|
skipArtifacts: invocation.skipArtifacts,
|
|
14258
|
+
// `--only-artifacts` is forwarded as itself, not only as the derived suppression list:
|
|
14259
|
+
// suppression cannot switch an artifact *on*, so a config that had turned one of the
|
|
14260
|
+
// requested artifacts off left the run with nothing to write.
|
|
14261
|
+
onlyArtifacts: invocation.onlyArtifacts,
|
|
13576
14262
|
discovery: invocation.discovery,
|
|
13577
14263
|
changedSince: invocation.changedSince,
|
|
13578
14264
|
staged: invocation.staged,
|
|
@@ -13584,19 +14270,6 @@ ${USAGE}
|
|
|
13584
14270
|
);
|
|
13585
14271
|
if (invocation.command === "validate") {
|
|
13586
14272
|
const result = validateSnapshot(resolved);
|
|
13587
|
-
if (result.valid) {
|
|
13588
|
-
const manifestPath = path16__namespace.resolve(resolved.cwd, resolved.outDir, "manifest.json");
|
|
13589
|
-
let manifest;
|
|
13590
|
-
try {
|
|
13591
|
-
manifest = JSON.parse(fs14__namespace.readFileSync(manifestPath, "utf8"));
|
|
13592
|
-
} catch {
|
|
13593
|
-
manifest = void 0;
|
|
13594
|
-
}
|
|
13595
|
-
const shapeError = manifest === void 0 ? null : describeManifestShapeProblem(manifest);
|
|
13596
|
-
if (shapeError) {
|
|
13597
|
-
return fail(invocation.format, `${manifestPath} is not a snapshot manifest: ${shapeError}`);
|
|
13598
|
-
}
|
|
13599
|
-
}
|
|
13600
14273
|
emit(invocation.format, result, () => renderValidateText(result));
|
|
13601
14274
|
return result.valid ? 0 : 1;
|
|
13602
14275
|
}
|
|
@@ -13791,10 +14464,13 @@ function isGeneratedWatchPath(candidate, paths) {
|
|
|
13791
14464
|
const outDir = comparablePath(path16__namespace.resolve(paths.cwd, paths.outDir));
|
|
13792
14465
|
const tmpDir = comparablePath(path16__namespace.resolve(paths.cwd, paths.tmpDir));
|
|
13793
14466
|
if (isSameOrInside(outDir, absolute) || isSameOrInside(tmpDir, absolute)) return true;
|
|
13794
|
-
const
|
|
13795
|
-
|
|
13796
|
-
|
|
13797
|
-
|
|
14467
|
+
const offsets = [path16__namespace.basename(outDir), path16__namespace.basename(tmpDir)].filter((name) => name.length > 0);
|
|
14468
|
+
const leftover = absolute.split(/[\\/]+/).some(
|
|
14469
|
+
(segment) => offsets.some(
|
|
14470
|
+
(name) => segment.startsWith(`${name}.backup-`) || segment.startsWith(`${name}.partial-`)
|
|
14471
|
+
)
|
|
14472
|
+
);
|
|
14473
|
+
if (leftover) return true;
|
|
13798
14474
|
return absolute.split(/[\\/]+/).some((segment) => VCS_SEGMENTS.has(segment));
|
|
13799
14475
|
}
|
|
13800
14476
|
function decideWatchEvent(state, event, paths) {
|
|
@@ -13869,6 +14545,7 @@ async function runWatchLoop(invocation, resolved, options) {
|
|
|
13869
14545
|
largeFileBytes: invocation.largeFileBytes,
|
|
13870
14546
|
maxMonolithBytes: invocation.maxMonolithBytes,
|
|
13871
14547
|
skipArtifacts: invocation.skipArtifacts,
|
|
14548
|
+
onlyArtifacts: invocation.onlyArtifacts,
|
|
13872
14549
|
discovery: invocation.discovery,
|
|
13873
14550
|
changedSince: invocation.changedSince,
|
|
13874
14551
|
staged: invocation.staged,
|
|
@@ -13925,12 +14602,12 @@ async function runWatchLoop(invocation, resolved, options) {
|
|
|
13925
14602
|
info(`\u26A0 Watch error: ${err.message}`);
|
|
13926
14603
|
info(" The watcher may have missed changes. Restart with Ctrl+C if they stop arriving.");
|
|
13927
14604
|
});
|
|
13928
|
-
await new Promise((
|
|
14605
|
+
await new Promise((resolve10) => {
|
|
13929
14606
|
const stop = () => {
|
|
13930
14607
|
stopped = true;
|
|
13931
14608
|
if (timer) clearTimeout(timer);
|
|
13932
14609
|
watcher.close();
|
|
13933
|
-
|
|
14610
|
+
resolve10();
|
|
13934
14611
|
};
|
|
13935
14612
|
process.once("SIGINT", stop);
|
|
13936
14613
|
process.once("SIGTERM", stop);
|