codebase-snapshot 0.9.1 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +181 -0
- package/README.md +141 -127
- package/dist/bin.js +2543 -506
- package/dist/chunk-EWZYK3VX.js +2577 -0
- package/dist/{chunk-HT7J3NWT.js → chunk-FCWD4LY7.js} +493 -362
- package/dist/cli.cjs +2450 -435
- package/dist/cli.d.cts +11 -1
- package/dist/cli.d.ts +11 -1
- package/dist/cli.js +68 -750
- package/dist/index.cjs +481 -360
- package/dist/index.d.cts +111 -5
- package/dist/index.d.ts +111 -5
- package/dist/index.js +1 -1
- package/dist/mcp.cjs +9398 -0
- package/dist/mcp.d.cts +399 -0
- package/dist/mcp.d.ts +399 -0
- package/dist/mcp.js +4 -0
- package/docs/agent.md +36 -0
- package/docs/cli.md +85 -14
- package/docs/codebase-token-budget.md +98 -0
- package/docs/configuration.md +12 -2
- package/docs/how-to-feed-a-repo-to-claude.md +4 -2
- package/docs/install.md +39 -11
- package/docs/mcp.md +149 -0
- package/docs/stale-context-is-your-bug.md +118 -0
- package/docs/troubleshooting.md +13 -0
- package/docs/vs-repomix.md +4 -2
- package/package.json +29 -14
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as crypto from 'crypto';
|
|
2
2
|
import { createHash } from 'crypto';
|
|
3
3
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
|
-
import * as
|
|
4
|
+
import * as fs11 from 'fs';
|
|
5
5
|
import * as os from 'os';
|
|
6
|
-
import * as
|
|
6
|
+
import * as path12 from 'path';
|
|
7
7
|
import ts from 'typescript';
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
9
|
import { pathToFileURL } from 'url';
|
|
@@ -234,9 +234,9 @@ var JAVA_METHOD = /^\s+(?:public|private|protected)\s+(?:static\s+)?(?:final\s+)
|
|
|
234
234
|
var jvmProfile = {
|
|
235
235
|
id: "jvm",
|
|
236
236
|
extensions: [".java", ".kt", ".kts", ".scala"],
|
|
237
|
-
summarize(
|
|
237
|
+
summarize(path13, content) {
|
|
238
238
|
const summary = emptySummary();
|
|
239
|
-
const lower =
|
|
239
|
+
const lower = path13.toLowerCase();
|
|
240
240
|
const isJava = lower.endsWith(".java");
|
|
241
241
|
const isKotlin = lower.endsWith(".kt") || lower.endsWith(".kts");
|
|
242
242
|
eachLine(content, (line) => {
|
|
@@ -689,16 +689,16 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
689
689
|
// src/ast/profiles/sfc.ts
|
|
690
690
|
var SCRIPT_BLOCK = /<script[^>]*>([\s\S]*?)<\/script>/gi;
|
|
691
691
|
var ASTRO_FRONTMATTER = /^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?=\r?\n|$)/;
|
|
692
|
-
function componentName(
|
|
693
|
-
const base =
|
|
692
|
+
function componentName(path13) {
|
|
693
|
+
const base = path13.split("/").pop() ?? path13;
|
|
694
694
|
return base.replace(/\.[^.]+$/, "");
|
|
695
695
|
}
|
|
696
696
|
var sfcProfile = {
|
|
697
697
|
id: "sfc",
|
|
698
698
|
extensions: [".vue", ".svelte", ".astro"],
|
|
699
|
-
summarize(
|
|
699
|
+
summarize(path13, content) {
|
|
700
700
|
let script = "";
|
|
701
|
-
if (
|
|
701
|
+
if (path13.toLowerCase().endsWith(".astro")) {
|
|
702
702
|
const frontmatter = ASTRO_FRONTMATTER.exec(content);
|
|
703
703
|
if (frontmatter) script += `${frontmatter[1]}
|
|
704
704
|
`;
|
|
@@ -707,7 +707,7 @@ var sfcProfile = {
|
|
|
707
707
|
script += `${match[1]}
|
|
708
708
|
`;
|
|
709
709
|
}
|
|
710
|
-
const name = componentName(
|
|
710
|
+
const name = componentName(path13);
|
|
711
711
|
if (!script.trim()) {
|
|
712
712
|
const summary2 = emptySummary();
|
|
713
713
|
if (name) {
|
|
@@ -741,15 +741,15 @@ for (const profile of LANGUAGE_PROFILES) {
|
|
|
741
741
|
BY_EXTENSION.set(extension.toLowerCase(), profile);
|
|
742
742
|
}
|
|
743
743
|
}
|
|
744
|
-
function profileForPath(
|
|
745
|
-
const lower =
|
|
744
|
+
function profileForPath(path13) {
|
|
745
|
+
const lower = path13.toLowerCase();
|
|
746
746
|
const dot = lower.lastIndexOf(".");
|
|
747
747
|
if (dot === -1) return null;
|
|
748
748
|
return BY_EXTENSION.get(lower.slice(dot)) ?? null;
|
|
749
749
|
}
|
|
750
|
-
function summarizeWithProfile(
|
|
751
|
-
const profile = profileForPath(
|
|
752
|
-
return profile ? profile.summarize(
|
|
750
|
+
function summarizeWithProfile(path13, content) {
|
|
751
|
+
const profile = profileForPath(path13);
|
|
752
|
+
return profile ? profile.summarize(path13, content) : null;
|
|
753
753
|
}
|
|
754
754
|
|
|
755
755
|
// src/ast/structured.ts
|
|
@@ -920,15 +920,15 @@ var SKIPPED_WALK_DIRS = /* @__PURE__ */ new Set([
|
|
|
920
920
|
]);
|
|
921
921
|
function readSmallFile(fullPath) {
|
|
922
922
|
try {
|
|
923
|
-
const stats =
|
|
923
|
+
const stats = fs11.statSync(fullPath);
|
|
924
924
|
if (!stats.isFile() || stats.size > MANIFEST_MAX_BYTES) return null;
|
|
925
|
-
return
|
|
925
|
+
return fs11.readFileSync(fullPath, "utf8");
|
|
926
926
|
} catch {
|
|
927
927
|
return null;
|
|
928
928
|
}
|
|
929
929
|
}
|
|
930
930
|
function readManifest(cwd, name) {
|
|
931
|
-
return readSmallFile(
|
|
931
|
+
return readSmallFile(path12.join(cwd, name));
|
|
932
932
|
}
|
|
933
933
|
function dependenciesFromPackageJson(raw) {
|
|
934
934
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -988,7 +988,7 @@ function pnpmWorkspacePatterns(raw) {
|
|
|
988
988
|
function childDirectories(cwd, parent) {
|
|
989
989
|
let entries;
|
|
990
990
|
try {
|
|
991
|
-
entries =
|
|
991
|
+
entries = fs11.readdirSync(path12.join(cwd, parent), { withFileTypes: true });
|
|
992
992
|
} catch {
|
|
993
993
|
return [];
|
|
994
994
|
}
|
|
@@ -1023,13 +1023,13 @@ function workspacePackageDirs(cwd, patterns) {
|
|
|
1023
1023
|
function scanWorkspacePackages(cwd, patterns) {
|
|
1024
1024
|
const files = /* @__PURE__ */ new Set();
|
|
1025
1025
|
const dependencies = /* @__PURE__ */ new Set();
|
|
1026
|
-
for (const
|
|
1027
|
-
const full =
|
|
1026
|
+
for (const relative7 of workspacePackageDirs(cwd, patterns)) {
|
|
1027
|
+
const full = path12.join(cwd, relative7);
|
|
1028
1028
|
try {
|
|
1029
|
-
for (const entry of
|
|
1029
|
+
for (const entry of fs11.readdirSync(full, { withFileTypes: true })) files.add(entry.name);
|
|
1030
1030
|
} catch {
|
|
1031
1031
|
}
|
|
1032
|
-
const raw = readSmallFile(
|
|
1032
|
+
const raw = readSmallFile(path12.join(full, "package.json"));
|
|
1033
1033
|
if (raw !== null) {
|
|
1034
1034
|
for (const name of dependenciesFromPackageJson(raw)) dependencies.add(name);
|
|
1035
1035
|
}
|
|
@@ -1046,7 +1046,7 @@ function countMarkdownFiles(cwd) {
|
|
|
1046
1046
|
directoriesSeen += 1;
|
|
1047
1047
|
let entries;
|
|
1048
1048
|
try {
|
|
1049
|
-
entries =
|
|
1049
|
+
entries = fs11.readdirSync(current.dir, { withFileTypes: true });
|
|
1050
1050
|
} catch {
|
|
1051
1051
|
continue;
|
|
1052
1052
|
}
|
|
@@ -1057,7 +1057,7 @@ function countMarkdownFiles(cwd) {
|
|
|
1057
1057
|
}
|
|
1058
1058
|
if (!entry.isDirectory() || current.depth >= 2) continue;
|
|
1059
1059
|
if (entry.name.startsWith(".") || SKIPPED_WALK_DIRS.has(entry.name)) continue;
|
|
1060
|
-
queue.push({ dir:
|
|
1060
|
+
queue.push({ dir: path12.join(current.dir, entry.name), depth: current.depth + 1 });
|
|
1061
1061
|
}
|
|
1062
1062
|
}
|
|
1063
1063
|
return count;
|
|
@@ -1066,7 +1066,7 @@ function buildPresetContext(cwd) {
|
|
|
1066
1066
|
const files = /* @__PURE__ */ new Set();
|
|
1067
1067
|
const dirs = /* @__PURE__ */ new Set();
|
|
1068
1068
|
try {
|
|
1069
|
-
for (const entry of
|
|
1069
|
+
for (const entry of fs11.readdirSync(cwd, { withFileTypes: true })) {
|
|
1070
1070
|
if (entry.isDirectory()) dirs.add(entry.name);
|
|
1071
1071
|
else if (entry.isFile()) files.add(entry.name);
|
|
1072
1072
|
}
|
|
@@ -1083,7 +1083,7 @@ function buildPresetContext(cwd) {
|
|
|
1083
1083
|
const isWorkspace = files.has("pnpm-workspace.yaml") || files.has("nx.json") || files.has("turbo.json") || files.has("lerna.json") || packageJson !== void 0 && packageJson.includes('"workspaces"') || declaredWorkspaces.length > 0;
|
|
1084
1084
|
let workspace = { files: /* @__PURE__ */ new Set(), dependencies: /* @__PURE__ */ new Set() };
|
|
1085
1085
|
if (isWorkspace) {
|
|
1086
|
-
const pnpmRaw = files.has("pnpm-workspace.yaml") ? readSmallFile(
|
|
1086
|
+
const pnpmRaw = files.has("pnpm-workspace.yaml") ? readSmallFile(path12.join(cwd, "pnpm-workspace.yaml")) : null;
|
|
1087
1087
|
const declared = pnpmRaw === null ? [] : pnpmWorkspacePatterns(pnpmRaw);
|
|
1088
1088
|
const patterns = declared.length > 0 ? declared : declaredWorkspaces.length > 0 ? declaredWorkspaces : DEFAULT_WORKSPACE_PATTERNS;
|
|
1089
1089
|
workspace = scanWorkspacePackages(cwd, patterns);
|
|
@@ -1101,25 +1101,41 @@ function buildPresetContext(cwd) {
|
|
|
1101
1101
|
markdownCount: files.has("package.json") ? 0 : countMarkdownFiles(cwd)
|
|
1102
1102
|
};
|
|
1103
1103
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1104
|
+
|
|
1105
|
+
// src/presets/evidence.ts
|
|
1106
|
+
function fileEvidence(context, name) {
|
|
1107
|
+
return context.files.has(name) ? name : null;
|
|
1106
1108
|
}
|
|
1107
|
-
function
|
|
1109
|
+
function fileStartingWithEvidence(context, prefix) {
|
|
1108
1110
|
for (const file of context.files) {
|
|
1109
|
-
if (file.startsWith(prefix)) return
|
|
1111
|
+
if (file.startsWith(prefix)) return file;
|
|
1110
1112
|
}
|
|
1111
1113
|
for (const file of context.workspaceFiles) {
|
|
1112
|
-
if (file.startsWith(prefix)) return
|
|
1114
|
+
if (file.startsWith(prefix)) return file;
|
|
1113
1115
|
}
|
|
1114
|
-
return
|
|
1116
|
+
return null;
|
|
1115
1117
|
}
|
|
1116
|
-
function
|
|
1118
|
+
function dependencyEvidence(context, ...names) {
|
|
1119
|
+
for (const name of names) {
|
|
1120
|
+
if (context.dependencies.has(name)) return `depends on ${name}`;
|
|
1121
|
+
}
|
|
1122
|
+
return null;
|
|
1123
|
+
}
|
|
1124
|
+
function manifestEvidence(context, manifest, needle) {
|
|
1117
1125
|
const content = context.manifests[manifest];
|
|
1118
|
-
if (!content) return
|
|
1119
|
-
|
|
1126
|
+
if (!content) return null;
|
|
1127
|
+
if (!content.toLowerCase().includes(needle.toLowerCase())) return null;
|
|
1128
|
+
return `${needle} in ${manifest}`;
|
|
1120
1129
|
}
|
|
1121
|
-
function
|
|
1122
|
-
|
|
1130
|
+
function suffixEvidence(context, suffix) {
|
|
1131
|
+
for (const file of context.files) {
|
|
1132
|
+
if (file.endsWith(suffix)) return file;
|
|
1133
|
+
}
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
function markdownEvidence(context) {
|
|
1137
|
+
if (context.markdownCount < 3 || context.files.has("package.json")) return null;
|
|
1138
|
+
return `${context.markdownCount} markdown files, no package.json`;
|
|
1123
1139
|
}
|
|
1124
1140
|
|
|
1125
1141
|
// src/presets/helpers.ts
|
|
@@ -1327,7 +1343,7 @@ var JS_PRESETS = [
|
|
|
1327
1343
|
{
|
|
1328
1344
|
name: "nextjs",
|
|
1329
1345
|
priority: 100,
|
|
1330
|
-
detect: (context) =>
|
|
1346
|
+
detect: (context) => fileStartingWithEvidence(context, "next.config") ?? dependencyEvidence(context, "next"),
|
|
1331
1347
|
layers: NEXTJS_LAYERS,
|
|
1332
1348
|
kinds: NEXTJS_KINDS,
|
|
1333
1349
|
exclude: NEXT_LIKE_EXCLUDES,
|
|
@@ -1336,7 +1352,7 @@ var JS_PRESETS = [
|
|
|
1336
1352
|
{
|
|
1337
1353
|
name: "nuxt",
|
|
1338
1354
|
priority: 98,
|
|
1339
|
-
detect: (context) =>
|
|
1355
|
+
detect: (context) => fileStartingWithEvidence(context, "nuxt.config") ?? dependencyEvidence(context, "nuxt", "nuxt3"),
|
|
1340
1356
|
layers: [
|
|
1341
1357
|
layer("pages", "pages/", "src/pages/"),
|
|
1342
1358
|
layer("components", "components/", "src/components/"),
|
|
@@ -1360,7 +1376,7 @@ var JS_PRESETS = [
|
|
|
1360
1376
|
{
|
|
1361
1377
|
name: "sveltekit",
|
|
1362
1378
|
priority: 96,
|
|
1363
|
-
detect: (context) =>
|
|
1379
|
+
detect: (context) => fileStartingWithEvidence(context, "svelte.config") ?? dependencyEvidence(context, "@sveltejs/kit", "svelte"),
|
|
1364
1380
|
layers: [
|
|
1365
1381
|
layer("routes", "src/routes/", "routes/"),
|
|
1366
1382
|
layer("lib", "src/lib/", "lib/"),
|
|
@@ -1381,7 +1397,7 @@ var JS_PRESETS = [
|
|
|
1381
1397
|
{
|
|
1382
1398
|
name: "remix",
|
|
1383
1399
|
priority: 94,
|
|
1384
|
-
detect: (context) =>
|
|
1400
|
+
detect: (context) => dependencyEvidence(context, "@remix-run/react", "@remix-run/node", "@remix-run/dev"),
|
|
1385
1401
|
layers: [
|
|
1386
1402
|
layer("routes", "app/routes/", "routes/"),
|
|
1387
1403
|
layer("components", "app/components/"),
|
|
@@ -1395,7 +1411,7 @@ var JS_PRESETS = [
|
|
|
1395
1411
|
{
|
|
1396
1412
|
name: "astro",
|
|
1397
1413
|
priority: 92,
|
|
1398
|
-
detect: (context) =>
|
|
1414
|
+
detect: (context) => fileStartingWithEvidence(context, "astro.config") ?? dependencyEvidence(context, "astro"),
|
|
1399
1415
|
layers: [
|
|
1400
1416
|
layer("pages", "src/pages/"),
|
|
1401
1417
|
layer("layouts", "src/layouts/"),
|
|
@@ -1410,7 +1426,7 @@ var JS_PRESETS = [
|
|
|
1410
1426
|
{
|
|
1411
1427
|
name: "angular",
|
|
1412
1428
|
priority: 90,
|
|
1413
|
-
detect: (context) =>
|
|
1429
|
+
detect: (context) => fileEvidence(context, "angular.json") ?? dependencyEvidence(context, "@angular/core"),
|
|
1414
1430
|
layers: [
|
|
1415
1431
|
layer("app", "src/app/"),
|
|
1416
1432
|
layer("features", "src/app/features/"),
|
|
@@ -1434,7 +1450,7 @@ var JS_PRESETS = [
|
|
|
1434
1450
|
{
|
|
1435
1451
|
name: "solid",
|
|
1436
1452
|
priority: 88,
|
|
1437
|
-
detect: (context) =>
|
|
1453
|
+
detect: (context) => dependencyEvidence(context, "solid-js", "solid-start"),
|
|
1438
1454
|
layers: [
|
|
1439
1455
|
layer("pages", "src/routes/", "src/pages/"),
|
|
1440
1456
|
layer("components", "src/components/"),
|
|
@@ -1447,7 +1463,15 @@ var JS_PRESETS = [
|
|
|
1447
1463
|
{
|
|
1448
1464
|
name: "vue-vite",
|
|
1449
1465
|
priority: 86,
|
|
1450
|
-
|
|
1466
|
+
// `vue` is the decisive signal; `vite` (or its config file) only says the app is
|
|
1467
|
+
// built with Vite rather than shipped pre-compiled, so it is required but not what
|
|
1468
|
+
// gets reported.
|
|
1469
|
+
detect: (context) => {
|
|
1470
|
+
const framework = dependencyEvidence(context, "vue");
|
|
1471
|
+
if (framework === null) return null;
|
|
1472
|
+
const build = dependencyEvidence(context, "vite") ?? fileStartingWithEvidence(context, "vite.config");
|
|
1473
|
+
return build === null ? null : framework;
|
|
1474
|
+
},
|
|
1451
1475
|
layers: [
|
|
1452
1476
|
layer("pages", "src/pages/", "src/views/"),
|
|
1453
1477
|
layer("components", "src/components/"),
|
|
@@ -1472,7 +1496,12 @@ var JS_PRESETS = [
|
|
|
1472
1496
|
{
|
|
1473
1497
|
name: "react-vite",
|
|
1474
1498
|
priority: 84,
|
|
1475
|
-
detect: (context) =>
|
|
1499
|
+
detect: (context) => {
|
|
1500
|
+
const framework = dependencyEvidence(context, "react");
|
|
1501
|
+
if (framework === null) return null;
|
|
1502
|
+
const build = dependencyEvidence(context, "vite") ?? fileStartingWithEvidence(context, "vite.config");
|
|
1503
|
+
return build === null ? null : framework;
|
|
1504
|
+
},
|
|
1476
1505
|
layers: VITE_LAYERS,
|
|
1477
1506
|
kinds: VITE_KINDS,
|
|
1478
1507
|
exclude: [".vite", "dist"],
|
|
@@ -1481,7 +1510,7 @@ var JS_PRESETS = [
|
|
|
1481
1510
|
{
|
|
1482
1511
|
name: "nest",
|
|
1483
1512
|
priority: 80,
|
|
1484
|
-
detect: (context) =>
|
|
1513
|
+
detect: (context) => dependencyEvidence(context, "@nestjs/core"),
|
|
1485
1514
|
layers: [
|
|
1486
1515
|
layer("modules", "src/modules/", "modules/"),
|
|
1487
1516
|
layer("controllers", "src/controllers/", "controllers/"),
|
|
@@ -1507,7 +1536,7 @@ var JS_PRESETS = [
|
|
|
1507
1536
|
{
|
|
1508
1537
|
name: "node-express",
|
|
1509
1538
|
priority: 78,
|
|
1510
|
-
detect: (context) =>
|
|
1539
|
+
detect: (context) => dependencyEvidence(context, "express", "fastify", "koa", "@hapi/hapi"),
|
|
1511
1540
|
layers: NODE_LAYERS,
|
|
1512
1541
|
kinds: NODE_KINDS,
|
|
1513
1542
|
exclude: ["dist"],
|
|
@@ -1518,7 +1547,7 @@ var JS_PRESETS = [
|
|
|
1518
1547
|
// A bare package.json is often just tooling around a non-JS project, so it
|
|
1519
1548
|
// ranks below every definitive language manifest.
|
|
1520
1549
|
priority: 45,
|
|
1521
|
-
detect: (context) =>
|
|
1550
|
+
detect: (context) => fileEvidence(context, "package.json"),
|
|
1522
1551
|
layers: [
|
|
1523
1552
|
layer("src", "src/"),
|
|
1524
1553
|
layer("lib", "lib/"),
|
|
@@ -1532,13 +1561,12 @@ var JS_PRESETS = [
|
|
|
1532
1561
|
];
|
|
1533
1562
|
|
|
1534
1563
|
// src/presets/lang-presets.ts
|
|
1535
|
-
var anyFileEndingWith = (context, suffix) => Array.from(context.files).some((file) => file.endsWith(suffix));
|
|
1536
1564
|
var DIR = (name) => `${name}/`;
|
|
1537
1565
|
var LANGUAGE_PRESETS = [
|
|
1538
1566
|
{
|
|
1539
1567
|
name: "python-django",
|
|
1540
1568
|
priority: 56,
|
|
1541
|
-
detect: (context) =>
|
|
1569
|
+
detect: (context) => fileEvidence(context, "manage.py") ?? manifestEvidence(context, "pyproject.toml", "django") ?? manifestEvidence(context, "requirements.txt", "django"),
|
|
1542
1570
|
layers: [
|
|
1543
1571
|
layer("views", DIR("views"), DIR("app/views"), DIR("apps/views")),
|
|
1544
1572
|
layer("models", DIR("models"), DIR("app/models"), DIR("apps/models")),
|
|
@@ -1564,7 +1592,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1564
1592
|
{
|
|
1565
1593
|
name: "python-fastapi",
|
|
1566
1594
|
priority: 54,
|
|
1567
|
-
detect: (context) =>
|
|
1595
|
+
detect: (context) => manifestEvidence(context, "requirements.txt", "fastapi") ?? manifestEvidence(context, "requirements.txt", "flask") ?? manifestEvidence(context, "pyproject.toml", "fastapi") ?? manifestEvidence(context, "pyproject.toml", "flask"),
|
|
1568
1596
|
layers: [
|
|
1569
1597
|
layer("routers", DIR("routers"), DIR("routes"), DIR("api")),
|
|
1570
1598
|
layer("models", DIR("models"), DIR("db")),
|
|
@@ -1588,7 +1616,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1588
1616
|
name: "python-lib",
|
|
1589
1617
|
// Below node-lib: a Node repo with a stray .py file is still a Node repo.
|
|
1590
1618
|
priority: 30,
|
|
1591
|
-
detect: (context) =>
|
|
1619
|
+
detect: (context) => fileEvidence(context, "pyproject.toml") ?? fileEvidence(context, "setup.py") ?? fileEvidence(context, "requirements.txt") ?? suffixEvidence(context, ".py"),
|
|
1592
1620
|
layers: [
|
|
1593
1621
|
layer("package", DIR("src"), DIR("lib")),
|
|
1594
1622
|
layer("tests", DIR("tests"), DIR("test")),
|
|
@@ -1601,7 +1629,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1601
1629
|
{
|
|
1602
1630
|
name: "go",
|
|
1603
1631
|
priority: 50,
|
|
1604
|
-
detect: (context) =>
|
|
1632
|
+
detect: (context) => fileEvidence(context, "go.mod"),
|
|
1605
1633
|
layers: [
|
|
1606
1634
|
layer("cmd", "cmd/"),
|
|
1607
1635
|
layer("internal", "internal/"),
|
|
@@ -1625,7 +1653,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1625
1653
|
{
|
|
1626
1654
|
name: "rust",
|
|
1627
1655
|
priority: 48,
|
|
1628
|
-
detect: (context) =>
|
|
1656
|
+
detect: (context) => fileEvidence(context, "Cargo.toml"),
|
|
1629
1657
|
layers: [
|
|
1630
1658
|
layer("src", "src/"),
|
|
1631
1659
|
layer("crates", "crates/"),
|
|
@@ -1641,7 +1669,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1641
1669
|
{
|
|
1642
1670
|
name: "java-spring",
|
|
1643
1671
|
priority: 46,
|
|
1644
|
-
detect: (context) =>
|
|
1672
|
+
detect: (context) => fileEvidence(context, "pom.xml") ?? fileEvidence(context, "build.gradle") ?? fileEvidence(context, "build.gradle.kts"),
|
|
1645
1673
|
layers: [
|
|
1646
1674
|
layer("api", "src/main/java/"),
|
|
1647
1675
|
layer("test", "src/test/java/"),
|
|
@@ -1664,7 +1692,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1664
1692
|
{
|
|
1665
1693
|
name: "dotnet",
|
|
1666
1694
|
priority: 44,
|
|
1667
|
-
detect: (context) =>
|
|
1695
|
+
detect: (context) => suffixEvidence(context, ".csproj") ?? suffixEvidence(context, ".sln") ?? fileEvidence(context, "global.json"),
|
|
1668
1696
|
layers: [
|
|
1669
1697
|
layer("web", "src/", "Web/"),
|
|
1670
1698
|
layer("domain", "Domain/", "Core/"),
|
|
@@ -1687,7 +1715,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1687
1715
|
{
|
|
1688
1716
|
name: "php-laravel",
|
|
1689
1717
|
priority: 42,
|
|
1690
|
-
detect: (context) =>
|
|
1718
|
+
detect: (context) => fileEvidence(context, "artisan") ?? manifestEvidence(context, "composer.json", "laravel") ?? fileEvidence(context, "composer.json"),
|
|
1691
1719
|
layers: [
|
|
1692
1720
|
layer("app", "app/"),
|
|
1693
1721
|
layer("routes", "routes/"),
|
|
@@ -1713,7 +1741,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1713
1741
|
{
|
|
1714
1742
|
name: "ruby-rails",
|
|
1715
1743
|
priority: 40,
|
|
1716
|
-
detect: (context) =>
|
|
1744
|
+
detect: (context) => fileEvidence(context, "Gemfile") ?? fileEvidence(context, "config.ru") ?? fileEvidence(context, "Rakefile"),
|
|
1717
1745
|
layers: [
|
|
1718
1746
|
layer("app", "app/"),
|
|
1719
1747
|
layer("controllers", "app/controllers/"),
|
|
@@ -1786,7 +1814,7 @@ var SHAPE_PRESETS = [
|
|
|
1786
1814
|
{
|
|
1787
1815
|
name: "monorepo",
|
|
1788
1816
|
priority: 70,
|
|
1789
|
-
detect: (context) =>
|
|
1817
|
+
detect: (context) => fileEvidence(context, "pnpm-workspace.yaml") ?? fileEvidence(context, "nx.json") ?? fileEvidence(context, "turbo.json") ?? fileEvidence(context, "lerna.json") ?? manifestEvidence(context, "package.json", '"workspaces"'),
|
|
1790
1818
|
layers: [
|
|
1791
1819
|
layer("apps", "apps/"),
|
|
1792
1820
|
layer("packages", "packages/"),
|
|
@@ -1800,7 +1828,7 @@ var SHAPE_PRESETS = [
|
|
|
1800
1828
|
{
|
|
1801
1829
|
name: "infra",
|
|
1802
1830
|
priority: 24,
|
|
1803
|
-
detect: (context) =>
|
|
1831
|
+
detect: (context) => suffixEvidence(context, ".tf") ?? fileEvidence(context, "Chart.yaml") ?? fileEvidence(context, "kustomization.yaml") ?? fileEvidence(context, "docker-compose.yml") ?? fileEvidence(context, "docker-compose.yaml"),
|
|
1804
1832
|
layers: [
|
|
1805
1833
|
layer("modules", "modules/"),
|
|
1806
1834
|
layer("environments", "env/", "environments/"),
|
|
@@ -1822,7 +1850,7 @@ var SHAPE_PRESETS = [
|
|
|
1822
1850
|
// up to two directories down (bounded in the context builder). Counting
|
|
1823
1851
|
// only the root turned every real documentation repository — README.md plus
|
|
1824
1852
|
// the pages under `docs/` — into `generic`.
|
|
1825
|
-
detect: (context) =>
|
|
1853
|
+
detect: (context) => markdownEvidence(context),
|
|
1826
1854
|
layers: [
|
|
1827
1855
|
layer("content", "content/", "docs/"),
|
|
1828
1856
|
layer("assets", "assets/", "static/", "public/"),
|
|
@@ -1835,7 +1863,10 @@ var SHAPE_PRESETS = [
|
|
|
1835
1863
|
{
|
|
1836
1864
|
name: "generic",
|
|
1837
1865
|
priority: 0,
|
|
1838
|
-
|
|
1866
|
+
// No detector: this is the fallback, and `detectPresetFromContext` reaches it only
|
|
1867
|
+
// when every other preset reported no evidence. `fallback: true` is what lets the
|
|
1868
|
+
// wizard tell "I recognised this" apart from "I gave up".
|
|
1869
|
+
fallback: true,
|
|
1839
1870
|
layers: DEFAULT_LAYERS,
|
|
1840
1871
|
kinds: DEFAULT_KINDS,
|
|
1841
1872
|
exclude: [],
|
|
@@ -1867,20 +1898,27 @@ function detectPresetFromContext(context, forced) {
|
|
|
1867
1898
|
if (forced !== void 0) {
|
|
1868
1899
|
const preset = BY_NAME.get(forced);
|
|
1869
1900
|
if (!preset) throw new Error(unknownPresetMessage(forced));
|
|
1870
|
-
return toDetected(preset);
|
|
1901
|
+
return toDetected(preset, null);
|
|
1871
1902
|
}
|
|
1872
1903
|
for (const preset of PRESETS) {
|
|
1873
|
-
|
|
1904
|
+
const reason = preset.detect?.(context) ?? null;
|
|
1905
|
+
if (reason !== null) return toDetected(preset, reason);
|
|
1874
1906
|
}
|
|
1875
|
-
|
|
1907
|
+
const fallback = PRESETS.find((preset) => preset.fallback === true);
|
|
1908
|
+
if (fallback === void 0) {
|
|
1909
|
+
throw new Error("No fallback preset is defined. Exactly one preset must set `fallback: true`.");
|
|
1910
|
+
}
|
|
1911
|
+
return toDetected(fallback, null, true);
|
|
1876
1912
|
}
|
|
1877
|
-
function toDetected(preset) {
|
|
1913
|
+
function toDetected(preset, reason, fallback = false) {
|
|
1878
1914
|
return {
|
|
1879
1915
|
presetName: preset.name,
|
|
1880
1916
|
layers: preset.layers,
|
|
1881
1917
|
kinds: preset.kinds,
|
|
1882
1918
|
exclude: preset.exclude ? [...preset.exclude] : [],
|
|
1883
|
-
recommendedBudget: preset.recommendedBudget ?? null
|
|
1919
|
+
recommendedBudget: preset.recommendedBudget ?? null,
|
|
1920
|
+
reason,
|
|
1921
|
+
fallback
|
|
1884
1922
|
};
|
|
1885
1923
|
}
|
|
1886
1924
|
function detectPreset(cwd, forced) {
|
|
@@ -2320,14 +2358,14 @@ var PRESET_IGNORES = new Set(
|
|
|
2320
2358
|
);
|
|
2321
2359
|
function readLinkTarget(linkPath, fallback) {
|
|
2322
2360
|
try {
|
|
2323
|
-
return
|
|
2361
|
+
return fs11.readlinkSync(linkPath);
|
|
2324
2362
|
} catch {
|
|
2325
2363
|
return fallback;
|
|
2326
2364
|
}
|
|
2327
2365
|
}
|
|
2328
2366
|
function isInside(root, candidate) {
|
|
2329
|
-
const
|
|
2330
|
-
return
|
|
2367
|
+
const relative7 = path12.relative(root, candidate);
|
|
2368
|
+
return relative7 === "" || !relative7.startsWith(`..${path12.sep}`) && relative7 !== ".." && !path12.isAbsolute(relative7);
|
|
2331
2369
|
}
|
|
2332
2370
|
function getGitInfo(cwd, fallbackBranch = "main") {
|
|
2333
2371
|
try {
|
|
@@ -2532,10 +2570,10 @@ function trimUnescapedTrailingSpaces(line) {
|
|
|
2532
2570
|
return line.slice(0, end);
|
|
2533
2571
|
}
|
|
2534
2572
|
function readGitignoreRules(dir) {
|
|
2535
|
-
const gitignorePath2 =
|
|
2573
|
+
const gitignorePath2 = path12.join(dir, ".gitignore");
|
|
2536
2574
|
let raw;
|
|
2537
2575
|
try {
|
|
2538
|
-
raw =
|
|
2576
|
+
raw = fs11.readFileSync(gitignorePath2, "utf8");
|
|
2539
2577
|
} catch {
|
|
2540
2578
|
return [];
|
|
2541
2579
|
}
|
|
@@ -2662,7 +2700,7 @@ function isToolOwnedDirName(name) {
|
|
|
2662
2700
|
}
|
|
2663
2701
|
function hasSnapshotMarker(target) {
|
|
2664
2702
|
try {
|
|
2665
|
-
return
|
|
2703
|
+
return fs11.statSync(target).isDirectory() && fs11.existsSync(path12.join(target, ".codebase-snapshot"));
|
|
2666
2704
|
} catch {
|
|
2667
2705
|
return false;
|
|
2668
2706
|
}
|
|
@@ -2676,7 +2714,7 @@ function makeToolOwnedDecider(cwd, toolOwnedPaths) {
|
|
|
2676
2714
|
const prefix = parts.slice(0, depth).join("/");
|
|
2677
2715
|
let hasMarker = markers.get(prefix);
|
|
2678
2716
|
if (hasMarker === void 0) {
|
|
2679
|
-
hasMarker =
|
|
2717
|
+
hasMarker = fs11.existsSync(path12.join(cwd, prefix, ".codebase-snapshot"));
|
|
2680
2718
|
markers.set(prefix, hasMarker);
|
|
2681
2719
|
}
|
|
2682
2720
|
if (hasMarker) return true;
|
|
@@ -2691,7 +2729,7 @@ function makeLinkProbe() {
|
|
|
2691
2729
|
let known = cache.get(absolutePath);
|
|
2692
2730
|
if (known === void 0) {
|
|
2693
2731
|
try {
|
|
2694
|
-
known =
|
|
2732
|
+
known = fs11.lstatSync(absolutePath).isSymbolicLink();
|
|
2695
2733
|
} catch {
|
|
2696
2734
|
known = false;
|
|
2697
2735
|
}
|
|
@@ -2704,7 +2742,7 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2704
2742
|
const isLink = makeLinkProbe();
|
|
2705
2743
|
let rootReal = cwd;
|
|
2706
2744
|
try {
|
|
2707
|
-
rootReal =
|
|
2745
|
+
rootReal = fs11.realpathSync(cwd);
|
|
2708
2746
|
} catch {
|
|
2709
2747
|
}
|
|
2710
2748
|
const reported = /* @__PURE__ */ new Set();
|
|
@@ -2714,7 +2752,7 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2714
2752
|
let linkedDir = null;
|
|
2715
2753
|
for (let depth = 1; depth <= parts.length; depth += 1) {
|
|
2716
2754
|
const prefix = parts.slice(0, depth).join("/");
|
|
2717
|
-
if (isLink(
|
|
2755
|
+
if (isLink(path12.join(cwd, prefix))) {
|
|
2718
2756
|
linkedDir = prefix;
|
|
2719
2757
|
break;
|
|
2720
2758
|
}
|
|
@@ -2725,10 +2763,10 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2725
2763
|
}
|
|
2726
2764
|
if (reported.has(linkedDir)) continue;
|
|
2727
2765
|
reported.add(linkedDir);
|
|
2728
|
-
const absolute =
|
|
2766
|
+
const absolute = path12.join(cwd, linkedDir);
|
|
2729
2767
|
let targetReal = absolute;
|
|
2730
2768
|
try {
|
|
2731
|
-
targetReal =
|
|
2769
|
+
targetReal = fs11.realpathSync(absolute);
|
|
2732
2770
|
} catch {
|
|
2733
2771
|
}
|
|
2734
2772
|
const target = readLinkTarget(absolute, targetReal);
|
|
@@ -2805,7 +2843,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2805
2843
|
"Discovery mode is `git`, but git could not list this directory's files. Run inside a working repository, or pass --discovery walk (or auto) to scan the tree directly."
|
|
2806
2844
|
);
|
|
2807
2845
|
}
|
|
2808
|
-
if (
|
|
2846
|
+
if (fs11.existsSync(path12.join(cwd, ".git"))) {
|
|
2809
2847
|
throw new Error(
|
|
2810
2848
|
"This directory is a git repository, but git could not list its files. A plain walk honours .gitignore files, but git also applies .git/info/exclude and your global excludes file, includes files that are tracked although a .gitignore now matches them, and treats a submodule as a single entry. Fix the repository (git status should work), run outside git, or pass --discovery walk to accept those differences."
|
|
2811
2849
|
);
|
|
@@ -2864,7 +2902,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2864
2902
|
}
|
|
2865
2903
|
function rootRealFor(root) {
|
|
2866
2904
|
try {
|
|
2867
|
-
return
|
|
2905
|
+
return fs11.realpathSync(root);
|
|
2868
2906
|
} catch {
|
|
2869
2907
|
return root;
|
|
2870
2908
|
}
|
|
@@ -2872,18 +2910,18 @@ function rootRealFor(root) {
|
|
|
2872
2910
|
function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PURE__ */ new Set(), parentGitignoreLayers = []) {
|
|
2873
2911
|
const results = { files: [], skipped: [] };
|
|
2874
2912
|
const stack = [];
|
|
2875
|
-
const relativeToBase = (target) =>
|
|
2913
|
+
const relativeToBase = (target) => path12.relative(baseDir, target).replace(/\\/g, "/") || ".";
|
|
2876
2914
|
const enter = (current, layers) => {
|
|
2877
2915
|
if (visited.has(current)) return false;
|
|
2878
2916
|
visited.add(current);
|
|
2879
2917
|
const localRules = readGitignoreRules(current);
|
|
2880
2918
|
const gitignoreLayers = localRules.length === 0 ? layers : [
|
|
2881
2919
|
...layers,
|
|
2882
|
-
{ prefix:
|
|
2920
|
+
{ prefix: path12.relative(baseDir, current).replace(/\\/g, "/"), rules: localRules }
|
|
2883
2921
|
];
|
|
2884
2922
|
let entries;
|
|
2885
2923
|
try {
|
|
2886
|
-
entries =
|
|
2924
|
+
entries = fs11.readdirSync(current, { withFileTypes: true });
|
|
2887
2925
|
} catch {
|
|
2888
2926
|
results.skipped.push({
|
|
2889
2927
|
path: relativeToBase(current),
|
|
@@ -2905,8 +2943,8 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
2905
2943
|
}
|
|
2906
2944
|
const entry = frame.entries[frame.index];
|
|
2907
2945
|
frame.index += 1;
|
|
2908
|
-
const fullPath =
|
|
2909
|
-
const relPath =
|
|
2946
|
+
const fullPath = path12.join(frame.dir, entry.name);
|
|
2947
|
+
const relPath = path12.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
2910
2948
|
if (isToolOwnedDirName(entry.name) && reservedPaths.has(relPath)) continue;
|
|
2911
2949
|
if (DEFAULT_IGNORED_DIRS.includes(entry.name)) {
|
|
2912
2950
|
results.skipped.push({
|
|
@@ -2939,14 +2977,14 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
2939
2977
|
if (entry.isSymbolicLink()) {
|
|
2940
2978
|
let targetReal;
|
|
2941
2979
|
try {
|
|
2942
|
-
targetReal =
|
|
2980
|
+
targetReal = fs11.realpathSync(fullPath);
|
|
2943
2981
|
} catch {
|
|
2944
2982
|
results.skipped.push({ path: relPath, reason: "read-error", detail: "broken symlink" });
|
|
2945
2983
|
continue;
|
|
2946
2984
|
}
|
|
2947
2985
|
let stats;
|
|
2948
2986
|
try {
|
|
2949
|
-
stats =
|
|
2987
|
+
stats = fs11.statSync(fullPath);
|
|
2950
2988
|
} catch {
|
|
2951
2989
|
results.skipped.push({ path: relPath, reason: "read-error", detail: "unreadable symlink" });
|
|
2952
2990
|
continue;
|
|
@@ -2999,6 +3037,20 @@ var CONFIG_FILENAMES = [
|
|
|
2999
3037
|
".snapshotrc.json",
|
|
3000
3038
|
".snapshotrc"
|
|
3001
3039
|
];
|
|
3040
|
+
function findConfigFile(cwd) {
|
|
3041
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
3042
|
+
const fullPath = path12.join(cwd, filename);
|
|
3043
|
+
if (fs11.existsSync(fullPath)) return fullPath;
|
|
3044
|
+
}
|
|
3045
|
+
const pkgJsonPath = path12.join(cwd, "package.json");
|
|
3046
|
+
if (!fs11.existsSync(pkgJsonPath)) return null;
|
|
3047
|
+
try {
|
|
3048
|
+
const parsed = JSON.parse(fs11.readFileSync(pkgJsonPath, "utf8"));
|
|
3049
|
+
return parsed.snapshot === void 0 ? null : pkgJsonPath;
|
|
3050
|
+
} catch {
|
|
3051
|
+
return null;
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3002
3054
|
var RESOLVED_CONFIG_BRAND = /* @__PURE__ */ Symbol.for(
|
|
3003
3055
|
"codebase-snapshot/resolved-config"
|
|
3004
3056
|
);
|
|
@@ -3244,11 +3296,11 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3244
3296
|
const warnings = [];
|
|
3245
3297
|
const errors = [];
|
|
3246
3298
|
for (const filename of CONFIG_FILENAMES) {
|
|
3247
|
-
const fullPath =
|
|
3248
|
-
if (!
|
|
3299
|
+
const fullPath = path12.join(cwd, filename);
|
|
3300
|
+
if (!fs11.existsSync(fullPath)) continue;
|
|
3249
3301
|
if (filename.endsWith(".json") || filename === ".snapshotrc") {
|
|
3250
3302
|
try {
|
|
3251
|
-
const config = parseJsonish(
|
|
3303
|
+
const config = parseJsonish(fs11.readFileSync(fullPath, "utf8"), fullPath);
|
|
3252
3304
|
if (config === null || typeof config !== "object" || Array.isArray(config)) {
|
|
3253
3305
|
throw new Error(
|
|
3254
3306
|
`${fullPath} does not hold a configuration object (received ${describeValue2(config)}).`
|
|
@@ -3282,10 +3334,10 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3282
3334
|
continue;
|
|
3283
3335
|
}
|
|
3284
3336
|
}
|
|
3285
|
-
const pkgJsonPath =
|
|
3286
|
-
if (
|
|
3337
|
+
const pkgJsonPath = path12.join(cwd, "package.json");
|
|
3338
|
+
if (fs11.existsSync(pkgJsonPath)) {
|
|
3287
3339
|
try {
|
|
3288
|
-
const parsed = parseJsonish(
|
|
3340
|
+
const parsed = parseJsonish(fs11.readFileSync(pkgJsonPath, "utf8"), pkgJsonPath);
|
|
3289
3341
|
const snapshotKey = parsed.snapshot;
|
|
3290
3342
|
if (snapshotKey && typeof snapshotKey === "object" && !Array.isArray(snapshotKey)) {
|
|
3291
3343
|
return { config: snapshotKey, file: pkgJsonPath, warnings, errors };
|
|
@@ -3308,7 +3360,7 @@ async function loadConfigFile(cwd) {
|
|
|
3308
3360
|
const loaded = await loadConfigFileDetailed(cwd);
|
|
3309
3361
|
if (loaded.config && loaded.file && typeof loaded.config === "object") {
|
|
3310
3362
|
Object.defineProperty(loaded.config, CONFIG_DIR_SYMBOL, {
|
|
3311
|
-
value:
|
|
3363
|
+
value: path12.dirname(loaded.file),
|
|
3312
3364
|
enumerable: false,
|
|
3313
3365
|
configurable: true
|
|
3314
3366
|
});
|
|
@@ -3467,12 +3519,12 @@ function resolveKindRules(value) {
|
|
|
3467
3519
|
function outputExcludePatterns(cwd, outDir, tmpDir) {
|
|
3468
3520
|
const patterns = [];
|
|
3469
3521
|
for (const dir of [outDir, tmpDir]) {
|
|
3470
|
-
const absolute =
|
|
3471
|
-
const
|
|
3472
|
-
if (!
|
|
3522
|
+
const absolute = path12.resolve(cwd, dir);
|
|
3523
|
+
const relative7 = path12.relative(cwd, absolute);
|
|
3524
|
+
if (!relative7 || relative7 === ".." || relative7.startsWith(`..${path12.sep}`) || path12.isAbsolute(relative7)) {
|
|
3473
3525
|
continue;
|
|
3474
3526
|
}
|
|
3475
|
-
patterns.push(`${
|
|
3527
|
+
patterns.push(`${relative7.replace(/\\/g, "/")}/**`);
|
|
3476
3528
|
}
|
|
3477
3529
|
return patterns;
|
|
3478
3530
|
}
|
|
@@ -3588,11 +3640,11 @@ function resolveCandidateCwd(userConfig, cliOptions, baseDir) {
|
|
|
3588
3640
|
);
|
|
3589
3641
|
}
|
|
3590
3642
|
const candidate = cliOptions?.cwd || userConfig?.cwd || baseDir || process.cwd();
|
|
3591
|
-
const resolved =
|
|
3592
|
-
if (!
|
|
3643
|
+
const resolved = path12.resolve(candidate);
|
|
3644
|
+
if (!fs11.existsSync(resolved)) {
|
|
3593
3645
|
throw new Error(`Working directory does not exist: ${resolved}`);
|
|
3594
3646
|
}
|
|
3595
|
-
if (!
|
|
3647
|
+
if (!fs11.statSync(resolved).isDirectory()) {
|
|
3596
3648
|
throw new Error(`Working directory is not a directory: ${resolved}`);
|
|
3597
3649
|
}
|
|
3598
3650
|
return resolved;
|
|
@@ -4616,7 +4668,7 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
4616
4668
|
".so"
|
|
4617
4669
|
]);
|
|
4618
4670
|
function isBinaryExtension(filePath) {
|
|
4619
|
-
const ext =
|
|
4671
|
+
const ext = path12.extname(filePath).toLowerCase();
|
|
4620
4672
|
return BINARY_EXTENSIONS.has(ext);
|
|
4621
4673
|
}
|
|
4622
4674
|
function isBinaryBuffer(buffer) {
|
|
@@ -4786,13 +4838,13 @@ function isBinaryFile(absolutePath) {
|
|
|
4786
4838
|
if (isBinaryExtension(absolutePath)) return true;
|
|
4787
4839
|
let fd;
|
|
4788
4840
|
try {
|
|
4789
|
-
fd =
|
|
4841
|
+
fd = fs11.openSync(absolutePath, "r");
|
|
4790
4842
|
const buffer = Buffer.alloc(CHUNK);
|
|
4791
4843
|
let sample;
|
|
4792
4844
|
let position = 0;
|
|
4793
4845
|
while (position < BINARY_SCAN_BYTES) {
|
|
4794
4846
|
const wanted = Math.min(CHUNK, BINARY_SCAN_BYTES - position);
|
|
4795
|
-
const bytesRead =
|
|
4847
|
+
const bytesRead = fs11.readSync(fd, buffer, 0, wanted, position);
|
|
4796
4848
|
if (bytesRead === 0) return false;
|
|
4797
4849
|
const chunk = buffer.subarray(0, bytesRead);
|
|
4798
4850
|
if (sample === void 0) {
|
|
@@ -4810,7 +4862,7 @@ function isBinaryFile(absolutePath) {
|
|
|
4810
4862
|
} finally {
|
|
4811
4863
|
if (fd !== void 0) {
|
|
4812
4864
|
try {
|
|
4813
|
-
|
|
4865
|
+
fs11.closeSync(fd);
|
|
4814
4866
|
} catch {
|
|
4815
4867
|
}
|
|
4816
4868
|
}
|
|
@@ -4870,10 +4922,10 @@ var LANGUAGE_BY_BASENAME = {
|
|
|
4870
4922
|
};
|
|
4871
4923
|
function detectLanguage(filePath) {
|
|
4872
4924
|
const normalized = filePath.replace(/\\/g, "/");
|
|
4873
|
-
const base =
|
|
4925
|
+
const base = path12.posix.basename(normalized);
|
|
4874
4926
|
if (LANGUAGE_BY_BASENAME[base]) return LANGUAGE_BY_BASENAME[base];
|
|
4875
4927
|
if (base === ".env" || base.startsWith(".env.")) return "dotenv";
|
|
4876
|
-
const ext =
|
|
4928
|
+
const ext = path12.posix.extname(base).toLowerCase();
|
|
4877
4929
|
return LANGUAGE_BY_EXTENSION[ext] ?? "text";
|
|
4878
4930
|
}
|
|
4879
4931
|
function maskedPath(value, secretRules) {
|
|
@@ -4881,33 +4933,33 @@ function maskedPath(value, secretRules) {
|
|
|
4881
4933
|
}
|
|
4882
4934
|
function confineToDirectory(directory, filename) {
|
|
4883
4935
|
if (filename.includes("\0")) return null;
|
|
4884
|
-
if (
|
|
4885
|
-
const base =
|
|
4886
|
-
const resolved =
|
|
4936
|
+
if (path12.isAbsolute(filename)) return null;
|
|
4937
|
+
const base = path12.resolve(directory);
|
|
4938
|
+
const resolved = path12.resolve(base, filename);
|
|
4887
4939
|
const caseInsensitive = process.platform === "win32" || process.platform === "darwin";
|
|
4888
|
-
const prefix = caseInsensitive ? `${base}${
|
|
4940
|
+
const prefix = caseInsensitive ? `${base}${path12.sep}`.toLowerCase() : `${base}${path12.sep}`;
|
|
4889
4941
|
const candidate = caseInsensitive ? resolved.toLowerCase() : resolved;
|
|
4890
4942
|
return candidate.startsWith(prefix) ? resolved : null;
|
|
4891
4943
|
}
|
|
4892
4944
|
function findStagingDirectory(config) {
|
|
4893
|
-
const tmpDir =
|
|
4945
|
+
const tmpDir = path12.resolve(config.cwd, config.tmpDir);
|
|
4894
4946
|
let entries;
|
|
4895
4947
|
try {
|
|
4896
|
-
entries =
|
|
4948
|
+
entries = fs11.readdirSync(tmpDir);
|
|
4897
4949
|
} catch {
|
|
4898
4950
|
return null;
|
|
4899
4951
|
}
|
|
4900
4952
|
const runs = entries.filter((name) => name.startsWith("run-")).sort();
|
|
4901
4953
|
const newest = runs[runs.length - 1];
|
|
4902
|
-
return newest ?
|
|
4954
|
+
return newest ? path12.join(tmpDir, newest) : null;
|
|
4903
4955
|
}
|
|
4904
4956
|
function describeMissingManifest(config, manifestPath) {
|
|
4905
|
-
const outDir =
|
|
4906
|
-
const markerPath =
|
|
4957
|
+
const outDir = path12.resolve(config.cwd, config.outDir);
|
|
4958
|
+
const markerPath = path12.join(outDir, ".codebase-snapshot");
|
|
4907
4959
|
let marker2 = null;
|
|
4908
4960
|
try {
|
|
4909
|
-
if (
|
|
4910
|
-
const parsed = JSON.parse(
|
|
4961
|
+
if (fs11.existsSync(markerPath)) {
|
|
4962
|
+
const parsed = JSON.parse(fs11.readFileSync(markerPath, "utf8"));
|
|
4911
4963
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
4912
4964
|
marker2 = parsed;
|
|
4913
4965
|
}
|
|
@@ -4943,14 +4995,14 @@ function sha256Hex(buffer) {
|
|
|
4943
4995
|
}
|
|
4944
4996
|
var VERIFY_SNIFF_MAX_BYTES = 8 * 1024 * 1024;
|
|
4945
4997
|
function wouldNowBeIncluded(cwd, relativePath, reason) {
|
|
4946
|
-
const absolutePath =
|
|
4998
|
+
const absolutePath = path12.join(cwd, relativePath);
|
|
4947
4999
|
try {
|
|
4948
|
-
const stats =
|
|
5000
|
+
const stats = fs11.statSync(absolutePath);
|
|
4949
5001
|
if (!stats.isFile()) return false;
|
|
4950
5002
|
if (isBinaryFile(absolutePath)) return false;
|
|
4951
5003
|
if (reason !== "binary") return true;
|
|
4952
5004
|
if (stats.size > VERIFY_SNIFF_MAX_BYTES) return false;
|
|
4953
|
-
return !decodeText(
|
|
5005
|
+
return !decodeText(fs11.readFileSync(absolutePath)).binary;
|
|
4954
5006
|
} catch {
|
|
4955
5007
|
return false;
|
|
4956
5008
|
}
|
|
@@ -5008,8 +5060,8 @@ function describeManifestShapeProblem(value) {
|
|
|
5008
5060
|
function validateSnapshot(configInput) {
|
|
5009
5061
|
assertUsableConfigInput(configInput);
|
|
5010
5062
|
const config = isResolvedConfig(configInput) ? normalizeResolvedConfig(configInput) : resolveConfig(configInput);
|
|
5011
|
-
const manifestPath =
|
|
5012
|
-
if (!
|
|
5063
|
+
const manifestPath = path12.resolve(config.cwd, config.outDir, "manifest.json");
|
|
5064
|
+
if (!fs11.existsSync(manifestPath)) {
|
|
5013
5065
|
return {
|
|
5014
5066
|
valid: false,
|
|
5015
5067
|
manifestPresent: false,
|
|
@@ -5020,7 +5072,7 @@ function validateSnapshot(configInput) {
|
|
|
5020
5072
|
}
|
|
5021
5073
|
let manifest;
|
|
5022
5074
|
try {
|
|
5023
|
-
manifest = JSON.parse(
|
|
5075
|
+
manifest = JSON.parse(fs11.readFileSync(manifestPath, "utf8"));
|
|
5024
5076
|
} catch (err) {
|
|
5025
5077
|
return {
|
|
5026
5078
|
valid: false,
|
|
@@ -5042,7 +5094,7 @@ function validateSnapshot(configInput) {
|
|
|
5042
5094
|
}
|
|
5043
5095
|
const drift = emptyDrift();
|
|
5044
5096
|
const warnings = [];
|
|
5045
|
-
const snapshotDir =
|
|
5097
|
+
const snapshotDir = path12.resolve(config.cwd, config.outDir);
|
|
5046
5098
|
const manifestFileMap = /* @__PURE__ */ new Map();
|
|
5047
5099
|
for (const file of manifest.files) {
|
|
5048
5100
|
if (file && typeof file.relativePath === "string") {
|
|
@@ -5076,7 +5128,7 @@ function validateSnapshot(configInput) {
|
|
|
5076
5128
|
}
|
|
5077
5129
|
let diskHash;
|
|
5078
5130
|
try {
|
|
5079
|
-
diskHash = sha256Hex(
|
|
5131
|
+
diskHash = sha256Hex(fs11.readFileSync(path12.join(config.cwd, diskFile)));
|
|
5080
5132
|
} catch (err) {
|
|
5081
5133
|
if (err.code === "ENOENT") {
|
|
5082
5134
|
drift.missingOnDisk.push(diskFile);
|
|
@@ -5100,7 +5152,7 @@ function validateSnapshot(configInput) {
|
|
|
5100
5152
|
}
|
|
5101
5153
|
let statFailure = null;
|
|
5102
5154
|
try {
|
|
5103
|
-
|
|
5155
|
+
fs11.statSync(confined);
|
|
5104
5156
|
} catch (err) {
|
|
5105
5157
|
statFailure = err;
|
|
5106
5158
|
}
|
|
@@ -5120,7 +5172,7 @@ function validateSnapshot(configInput) {
|
|
|
5120
5172
|
...Object.values(config.budgets.byLayer)
|
|
5121
5173
|
);
|
|
5122
5174
|
const partNames = /* @__PURE__ */ new Set();
|
|
5123
|
-
const partsDir =
|
|
5175
|
+
const partsDir = path12.join(snapshotDir, "parts");
|
|
5124
5176
|
for (const part of manifest.parts) {
|
|
5125
5177
|
if (!part || typeof part.filename !== "string") continue;
|
|
5126
5178
|
partNames.add(part.filename);
|
|
@@ -5135,7 +5187,7 @@ function validateSnapshot(configInput) {
|
|
|
5135
5187
|
}
|
|
5136
5188
|
let buffer;
|
|
5137
5189
|
try {
|
|
5138
|
-
buffer =
|
|
5190
|
+
buffer = fs11.readFileSync(partPath);
|
|
5139
5191
|
} catch {
|
|
5140
5192
|
drift.partIssues.push({ part: part.filename, issue: "missing from snapshot/parts" });
|
|
5141
5193
|
continue;
|
|
@@ -5349,9 +5401,9 @@ function validateSnapshot(configInput) {
|
|
|
5349
5401
|
["full-codebase.md", manifest.artifacts?.fullMarkdown]
|
|
5350
5402
|
]) {
|
|
5351
5403
|
if (!artifact) continue;
|
|
5352
|
-
const artifactPath =
|
|
5404
|
+
const artifactPath = path12.join(snapshotDir, key);
|
|
5353
5405
|
try {
|
|
5354
|
-
const buffer =
|
|
5406
|
+
const buffer = fs11.readFileSync(artifactPath);
|
|
5355
5407
|
if (sha256Hex(buffer) !== artifact.sha256 || buffer.length !== artifact.byteSize) {
|
|
5356
5408
|
drift.partIssues.push({ part: key, issue: "artifact does not match its recorded digest" });
|
|
5357
5409
|
}
|
|
@@ -5421,7 +5473,7 @@ function validateSnapshot(configInput) {
|
|
|
5421
5473
|
|
|
5422
5474
|
// src/version.ts
|
|
5423
5475
|
var GENERATOR_NAME = "codebase-snapshot";
|
|
5424
|
-
var GENERATOR_VERSION = "0.9.
|
|
5476
|
+
var GENERATOR_VERSION = "0.9.4";
|
|
5425
5477
|
var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
|
|
5426
5478
|
function isOurGeneratorName(name) {
|
|
5427
5479
|
if (typeof name !== "string") return false;
|
|
@@ -5436,42 +5488,42 @@ function describeMatcher(matcher) {
|
|
|
5436
5488
|
return String(matcher);
|
|
5437
5489
|
}
|
|
5438
5490
|
function normalizeTarget(cwd, target) {
|
|
5439
|
-
const absolute =
|
|
5440
|
-
const
|
|
5441
|
-
if (
|
|
5442
|
-
return
|
|
5491
|
+
const absolute = path12.resolve(cwd, target);
|
|
5492
|
+
const relative7 = path12.relative(cwd, absolute);
|
|
5493
|
+
if (relative7.startsWith("..") || path12.isAbsolute(relative7)) return target;
|
|
5494
|
+
return relative7.split(path12.sep).join("/");
|
|
5443
5495
|
}
|
|
5444
5496
|
function findRuleMatch(rules, relativePath) {
|
|
5445
5497
|
return rules.find(
|
|
5446
5498
|
(rule) => matchRule(relativePath, rule.match)
|
|
5447
5499
|
);
|
|
5448
5500
|
}
|
|
5449
|
-
function findRecordedAncestor(skipped,
|
|
5501
|
+
function findRecordedAncestor(skipped, relative7, maskedPath2) {
|
|
5450
5502
|
let nearest;
|
|
5451
5503
|
for (const record of skipped) {
|
|
5452
5504
|
const ancestorPath = record.path;
|
|
5453
5505
|
if (ancestorPath.length === 0) continue;
|
|
5454
|
-
const isAncestor =
|
|
5506
|
+
const isAncestor = relative7.startsWith(`${ancestorPath}/`) || maskedPath2.startsWith(`${ancestorPath}/`);
|
|
5455
5507
|
if (!isAncestor) continue;
|
|
5456
5508
|
if (!nearest || ancestorPath.length > nearest.path.length) nearest = record;
|
|
5457
5509
|
}
|
|
5458
5510
|
return nearest;
|
|
5459
5511
|
}
|
|
5460
5512
|
function readManifest2(config) {
|
|
5461
|
-
const manifestPath =
|
|
5513
|
+
const manifestPath = path12.resolve(config.cwd, config.outDir, "manifest.json");
|
|
5462
5514
|
try {
|
|
5463
|
-
return JSON.parse(
|
|
5515
|
+
return JSON.parse(fs11.readFileSync(manifestPath, "utf8"));
|
|
5464
5516
|
} catch {
|
|
5465
5517
|
return null;
|
|
5466
5518
|
}
|
|
5467
5519
|
}
|
|
5468
5520
|
function explainPath(config, target) {
|
|
5469
|
-
const
|
|
5470
|
-
const absolute =
|
|
5471
|
-
const maskedPath2 = findSecretInPath(
|
|
5521
|
+
const relative7 = normalizeTarget(config.cwd, target);
|
|
5522
|
+
const absolute = path12.join(config.cwd, relative7);
|
|
5523
|
+
const maskedPath2 = findSecretInPath(relative7, config.secretRules)?.masked ?? relative7;
|
|
5472
5524
|
const result = {
|
|
5473
5525
|
path: maskedPath2,
|
|
5474
|
-
exists:
|
|
5526
|
+
exists: fs11.existsSync(absolute),
|
|
5475
5527
|
included: false
|
|
5476
5528
|
};
|
|
5477
5529
|
const collection = collectFiles(config.cwd, {
|
|
@@ -5482,12 +5534,12 @@ function explainPath(config, target) {
|
|
|
5482
5534
|
// Same exclusion `generate` applies, so `explain` never says "not-discovered" about a file
|
|
5483
5535
|
// that is only missing because the walker dropped the tool's own output directory.
|
|
5484
5536
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
5485
|
-
(directory) =>
|
|
5486
|
-
).filter((
|
|
5537
|
+
(directory) => path12.relative(config.cwd, path12.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
5538
|
+
).filter((relative8) => relative8.length > 0 && !relative8.startsWith(".."))
|
|
5487
5539
|
});
|
|
5488
|
-
if (!collection.files.includes(
|
|
5540
|
+
if (!collection.files.includes(relative7)) {
|
|
5489
5541
|
const record = collection.skipped.find(
|
|
5490
|
-
(entry2) => entry2.path ===
|
|
5542
|
+
(entry2) => entry2.path === relative7 || entry2.path === maskedPath2
|
|
5491
5543
|
);
|
|
5492
5544
|
if (record) {
|
|
5493
5545
|
result.reason = record.reason;
|
|
@@ -5496,7 +5548,7 @@ function explainPath(config, target) {
|
|
|
5496
5548
|
result.reason = "missing";
|
|
5497
5549
|
result.detail = "no such file or directory, relative to the configured cwd";
|
|
5498
5550
|
} else {
|
|
5499
|
-
const ancestor = findRecordedAncestor(collection.skipped,
|
|
5551
|
+
const ancestor = findRecordedAncestor(collection.skipped, relative7, maskedPath2);
|
|
5500
5552
|
if (ancestor) {
|
|
5501
5553
|
result.reason = ancestor.reason;
|
|
5502
5554
|
result.detail = ancestor.detail ? `${ancestor.detail} \u2014 inherited from the skipped directory \`${ancestor.path}\`` : `inside the skipped directory \`${ancestor.path}\``;
|
|
@@ -5514,15 +5566,15 @@ function explainPath(config, target) {
|
|
|
5514
5566
|
result.detail = "binary content; generate skips it and records `binary` in manifest.skipped[]";
|
|
5515
5567
|
return result;
|
|
5516
5568
|
}
|
|
5517
|
-
result.layer = classifyLayer(
|
|
5518
|
-
result.kind = classifyKind(
|
|
5519
|
-
result.language = detectLanguage(
|
|
5520
|
-
const layerRule = findRuleMatch(config.layers,
|
|
5569
|
+
result.layer = classifyLayer(relative7, config.layers);
|
|
5570
|
+
result.kind = classifyKind(relative7, config.kinds);
|
|
5571
|
+
result.language = detectLanguage(relative7);
|
|
5572
|
+
const layerRule = findRuleMatch(config.layers, relative7);
|
|
5521
5573
|
if (layerRule) result.layerMatch = describeMatcher(layerRule.match);
|
|
5522
|
-
const kindRule = findRuleMatch(config.kinds,
|
|
5574
|
+
const kindRule = findRuleMatch(config.kinds, relative7);
|
|
5523
5575
|
if (kindRule) result.kindMatch = describeMatcher(kindRule.match);
|
|
5524
|
-
if (
|
|
5525
|
-
const buffer =
|
|
5576
|
+
if (fs11.existsSync(absolute) && fs11.statSync(absolute).isFile()) {
|
|
5577
|
+
const buffer = fs11.readFileSync(absolute);
|
|
5526
5578
|
const decoded = decodeText(buffer);
|
|
5527
5579
|
if (decoded.binary) {
|
|
5528
5580
|
result.included = false;
|
|
@@ -5534,12 +5586,12 @@ function explainPath(config, target) {
|
|
|
5534
5586
|
result.bytes = buffer.length;
|
|
5535
5587
|
result.lines = countLines(text2);
|
|
5536
5588
|
if (decoded.encoding !== "utf-8") result.encoding = decoded.encoding;
|
|
5537
|
-
const { findings } = scanAndRedactSecrets(text2, config.secretRules,
|
|
5589
|
+
const { findings } = scanAndRedactSecrets(text2, config.secretRules, relative7);
|
|
5538
5590
|
result.secretCount = findings.length;
|
|
5539
5591
|
result.secrets = findings.slice(0, 20).map((finding) => ({ rules: finding.rules, line: finding.line }));
|
|
5540
5592
|
}
|
|
5541
5593
|
const manifest = readManifest2(config);
|
|
5542
|
-
const entry = Array.isArray(manifest?.files) ? manifest.files.find((file) => file.relativePath ===
|
|
5594
|
+
const entry = Array.isArray(manifest?.files) ? manifest.files.find((file) => file.relativePath === relative7) : void 0;
|
|
5543
5595
|
result.part = entry?.part ?? null;
|
|
5544
5596
|
result.inclusionMode = entry?.inclusionMode;
|
|
5545
5597
|
return result;
|
|
@@ -5770,14 +5822,14 @@ function compareManifests(before, after) {
|
|
|
5770
5822
|
function loadManifest(target) {
|
|
5771
5823
|
let isDirectory;
|
|
5772
5824
|
try {
|
|
5773
|
-
isDirectory =
|
|
5825
|
+
isDirectory = fs11.statSync(target).isDirectory();
|
|
5774
5826
|
} catch {
|
|
5775
5827
|
throw new Error(`No snapshot found at ${target}: the path does not exist.`);
|
|
5776
5828
|
}
|
|
5777
|
-
const manifestPath = isDirectory ?
|
|
5829
|
+
const manifestPath = isDirectory ? path12.join(target, "manifest.json") : target;
|
|
5778
5830
|
let raw;
|
|
5779
5831
|
try {
|
|
5780
|
-
raw =
|
|
5832
|
+
raw = fs11.readFileSync(manifestPath, "utf8");
|
|
5781
5833
|
} catch {
|
|
5782
5834
|
throw new Error(`Could not read a manifest at ${manifestPath}.`);
|
|
5783
5835
|
}
|
|
@@ -5834,15 +5886,15 @@ function renderDiffText(diff) {
|
|
|
5834
5886
|
function appendGitignoreEntries(cwd, entries) {
|
|
5835
5887
|
const wanted = [...new Set(entries)].filter((entry) => entry.trim().length > 0);
|
|
5836
5888
|
if (wanted.length === 0) return { added: [], note: null };
|
|
5837
|
-
const ignorePath =
|
|
5889
|
+
const ignorePath = path12.join(cwd, ".gitignore");
|
|
5838
5890
|
const manual = `Add ${wanted.map((entry) => `"${entry}"`).join(" and ")} to your .gitignore yourself.`;
|
|
5839
5891
|
let existing = "";
|
|
5840
5892
|
try {
|
|
5841
|
-
if (
|
|
5842
|
-
if (
|
|
5893
|
+
if (fs11.existsSync(ignorePath)) {
|
|
5894
|
+
if (fs11.statSync(ignorePath).isDirectory()) {
|
|
5843
5895
|
return { added: [], note: `${ignorePath} is a directory, so it was not updated. ${manual}` };
|
|
5844
5896
|
}
|
|
5845
|
-
existing =
|
|
5897
|
+
existing = fs11.readFileSync(ignorePath, "utf8");
|
|
5846
5898
|
}
|
|
5847
5899
|
} catch (err) {
|
|
5848
5900
|
return {
|
|
@@ -5855,7 +5907,7 @@ function appendGitignoreEntries(cwd, entries) {
|
|
|
5855
5907
|
if (added.length === 0) return { added: [], note: null };
|
|
5856
5908
|
const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
5857
5909
|
try {
|
|
5858
|
-
|
|
5910
|
+
fs11.writeFileSync(ignorePath, `${existing}${prefix}${added.join("\n")}
|
|
5859
5911
|
`, "utf8");
|
|
5860
5912
|
} catch (err) {
|
|
5861
5913
|
return { added: [], note: `${err.message}. ${manual}` };
|
|
@@ -5863,7 +5915,7 @@ function appendGitignoreEntries(cwd, entries) {
|
|
|
5863
5915
|
return { added, note: null };
|
|
5864
5916
|
}
|
|
5865
5917
|
function gitignorePath(cwd, filePath) {
|
|
5866
|
-
return
|
|
5918
|
+
return path12.relative(cwd, filePath).split(path12.sep).join("/");
|
|
5867
5919
|
}
|
|
5868
5920
|
|
|
5869
5921
|
// src/wizard/config-template.ts
|
|
@@ -6251,10 +6303,10 @@ var CONFIG_CANDIDATES = [
|
|
|
6251
6303
|
];
|
|
6252
6304
|
function chooseConfigFileName(cwd) {
|
|
6253
6305
|
if (!supportsTypeScriptImports()) return "snapshot.config.mjs";
|
|
6254
|
-
const pkgPath =
|
|
6255
|
-
if (!
|
|
6306
|
+
const pkgPath = path12.join(cwd, "package.json");
|
|
6307
|
+
if (!fs11.existsSync(pkgPath)) return "snapshot.config.mjs";
|
|
6256
6308
|
try {
|
|
6257
|
-
const parsed = JSON.parse(
|
|
6309
|
+
const parsed = JSON.parse(fs11.readFileSync(pkgPath, "utf8"));
|
|
6258
6310
|
return parsed.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
6259
6311
|
} catch {
|
|
6260
6312
|
return "snapshot.config.mjs";
|
|
@@ -6268,7 +6320,7 @@ var SIZE_CHOICES = [
|
|
|
6268
6320
|
];
|
|
6269
6321
|
function packageJsonHasConfig(cwd) {
|
|
6270
6322
|
try {
|
|
6271
|
-
const parsed = JSON.parse(
|
|
6323
|
+
const parsed = JSON.parse(fs11.readFileSync(path12.join(cwd, "package.json"), "utf8"));
|
|
6272
6324
|
return typeof parsed.snapshot === "object" && parsed.snapshot !== null && !Array.isArray(parsed.snapshot);
|
|
6273
6325
|
} catch {
|
|
6274
6326
|
return false;
|
|
@@ -6289,7 +6341,7 @@ function artifactAnswers(options) {
|
|
|
6289
6341
|
}
|
|
6290
6342
|
function topLevelDirs(cwd) {
|
|
6291
6343
|
try {
|
|
6292
|
-
return
|
|
6344
|
+
return fs11.readdirSync(cwd, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules").map((entry) => entry.name).sort();
|
|
6293
6345
|
} catch {
|
|
6294
6346
|
return [];
|
|
6295
6347
|
}
|
|
@@ -6302,7 +6354,7 @@ function describeJsonValue(value) {
|
|
|
6302
6354
|
}
|
|
6303
6355
|
function writeFileOrExplain(target, content) {
|
|
6304
6356
|
try {
|
|
6305
|
-
|
|
6357
|
+
fs11.writeFileSync(target, content, "utf8");
|
|
6306
6358
|
} catch (err) {
|
|
6307
6359
|
const code = err.code;
|
|
6308
6360
|
const reason = code === "EACCES" || code === "EPERM" || code === "EROFS" ? `permission denied (${code})` : code === "EISDIR" ? "it is a directory" : `${err.message}`;
|
|
@@ -6313,7 +6365,7 @@ function canWriteModuleConfig(name, cwd) {
|
|
|
6313
6365
|
if (name.endsWith(".mjs") || name.endsWith(".ts")) return true;
|
|
6314
6366
|
if (!name.endsWith(".js")) return false;
|
|
6315
6367
|
try {
|
|
6316
|
-
const parsed = JSON.parse(
|
|
6368
|
+
const parsed = JSON.parse(fs11.readFileSync(path12.join(cwd, "package.json"), "utf8"));
|
|
6317
6369
|
return parsed.type === "module";
|
|
6318
6370
|
} catch {
|
|
6319
6371
|
return false;
|
|
@@ -6348,19 +6400,19 @@ function serializePackageJson(raw, value) {
|
|
|
6348
6400
|
return `${text2}${raw.endsWith("\n") ? crlf ? "\r\n" : "\n" : ""}`;
|
|
6349
6401
|
}
|
|
6350
6402
|
function updatePackageJson(cwd, notes) {
|
|
6351
|
-
const packagePath =
|
|
6403
|
+
const packagePath = path12.join(cwd, "package.json");
|
|
6352
6404
|
const unchanged = {
|
|
6353
6405
|
scripts: [],
|
|
6354
6406
|
dependency: { name: GENERATOR_NAME, range: null, added: false }
|
|
6355
6407
|
};
|
|
6356
|
-
if (!
|
|
6408
|
+
if (!fs11.existsSync(packagePath)) {
|
|
6357
6409
|
notes.push("No package.json found, so npm scripts and the devDependency were not added.");
|
|
6358
6410
|
return unchanged;
|
|
6359
6411
|
}
|
|
6360
6412
|
let raw;
|
|
6361
6413
|
let parsed;
|
|
6362
6414
|
try {
|
|
6363
|
-
raw =
|
|
6415
|
+
raw = fs11.readFileSync(packagePath, "utf8");
|
|
6364
6416
|
parsed = JSON.parse(raw);
|
|
6365
6417
|
} catch {
|
|
6366
6418
|
notes.push("Could not parse package.json; npm scripts and the devDependency were not added.");
|
|
@@ -6423,16 +6475,18 @@ function updatePackageJson(cwd, notes) {
|
|
|
6423
6475
|
return { scripts: added, dependency };
|
|
6424
6476
|
}
|
|
6425
6477
|
var GITIGNORE_ENTRIES = ["snapshot/", "snapshot.tmp/"];
|
|
6426
|
-
function resolvePresetChoice(
|
|
6478
|
+
function resolvePresetChoice(detectedName, presetName) {
|
|
6427
6479
|
return {
|
|
6428
6480
|
value: presetName,
|
|
6429
6481
|
label: presetName,
|
|
6430
|
-
hint: presetName ===
|
|
6482
|
+
hint: presetName === detectedName ? "detected" : void 0
|
|
6431
6483
|
};
|
|
6432
6484
|
}
|
|
6433
6485
|
async function runInit(options) {
|
|
6434
6486
|
const io = options.io ?? defaultIO();
|
|
6435
6487
|
const detected = detectPreset(options.cwd, options.preset);
|
|
6488
|
+
let reason = detected.reason;
|
|
6489
|
+
let fallback = detected.fallback;
|
|
6436
6490
|
const notes = [];
|
|
6437
6491
|
const answers = {
|
|
6438
6492
|
preset: detected.presetName,
|
|
@@ -6454,117 +6508,148 @@ async function runInit(options) {
|
|
|
6454
6508
|
if (interactive) {
|
|
6455
6509
|
io.output.write("\n codebase-snapshot\n");
|
|
6456
6510
|
io.output.write(" Turn this repository into an LLM-ready, redacted, byte-bounded snapshot.\n\n");
|
|
6457
|
-
|
|
6511
|
+
if (fallback) {
|
|
6512
|
+
io.output.write(" \u26A0 Could not tell what kind of project this is.\n");
|
|
6513
|
+
io.output.write(" No package.json, go.mod, Cargo.toml, pyproject.toml, Gemfile,\n");
|
|
6514
|
+
io.output.write(" composer.json, pom.xml, *.csproj, next.config.*, vite.config.*, *.tf\n");
|
|
6515
|
+
io.output.write(" or workspace marker was found here.\n\n");
|
|
6516
|
+
} else {
|
|
6517
|
+
io.output.write(` Detected ${detected.presetName}
|
|
6518
|
+
`);
|
|
6519
|
+
io.output.write(` Because ${reason}
|
|
6520
|
+
|
|
6458
6521
|
`);
|
|
6522
|
+
}
|
|
6459
6523
|
io.output.write(` Directory ${options.cwd}
|
|
6460
6524
|
|
|
6461
6525
|
`);
|
|
6462
|
-
const
|
|
6463
|
-
|
|
6526
|
+
const paceChoices = fallback ? [
|
|
6527
|
+
{ value: "choose", label: "Pick the closest preset", hint: "then generate with the defaults" },
|
|
6528
|
+
{ value: "recommended", label: "Use the defaults", hint: "generic \xB7 everything the scanner finds" },
|
|
6529
|
+
{ value: "walk", label: "Go through each setting", hint: "every question starts on its default" }
|
|
6530
|
+
] : [
|
|
6531
|
+
{
|
|
6532
|
+
value: "recommended",
|
|
6533
|
+
label: "Use the recommended setup",
|
|
6534
|
+
hint: `${detected.presetName} \xB7 config \xB7 npm scripts \xB7 install \xB7 generate`
|
|
6535
|
+
},
|
|
6536
|
+
{ value: "walk", label: "Go through each setting", hint: "every question starts on its default" },
|
|
6537
|
+
{ value: "choose", label: "Pick a different preset", hint: "everything else stays at the defaults" }
|
|
6538
|
+
];
|
|
6539
|
+
const pace = await select(io, "How should I set this up?", paceChoices, {
|
|
6540
|
+
defaultValue: fallback ? "choose" : "recommended"
|
|
6464
6541
|
});
|
|
6465
|
-
if (
|
|
6542
|
+
if (pace === "choose") {
|
|
6466
6543
|
answers.preset = await select(
|
|
6467
6544
|
io,
|
|
6468
6545
|
"What kind of project is this?",
|
|
6469
|
-
listPresetNames().map(
|
|
6546
|
+
listPresetNames().map(
|
|
6547
|
+
(name) => resolvePresetChoice(fallback ? null : detected.presetName, name)
|
|
6548
|
+
),
|
|
6549
|
+
{ defaultValue: answers.preset }
|
|
6470
6550
|
);
|
|
6471
6551
|
const chosen = detectPreset(options.cwd, answers.preset);
|
|
6472
6552
|
answers.preset = chosen.presetName;
|
|
6553
|
+
reason = null;
|
|
6554
|
+
fallback = false;
|
|
6473
6555
|
}
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6556
|
+
if (pace === "walk") {
|
|
6557
|
+
const scope = await select(
|
|
6558
|
+
io,
|
|
6559
|
+
"What should go into the snapshot?",
|
|
6560
|
+
[
|
|
6561
|
+
{ value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
|
|
6562
|
+
{ value: "folders", label: "Only specific folders\u2026" },
|
|
6563
|
+
{ value: "no-tests", label: "Everything, but skip tests" },
|
|
6564
|
+
{ value: "custom", label: "Let me write glob patterns" }
|
|
6565
|
+
],
|
|
6566
|
+
{ defaultValue: "all" }
|
|
6567
|
+
);
|
|
6568
|
+
if (scope === "folders") {
|
|
6569
|
+
const dirs = topLevelDirs(options.cwd).slice(0, 40);
|
|
6570
|
+
if (dirs.length === 0) {
|
|
6571
|
+
notes.push("No top-level directories found; the snapshot will include everything.");
|
|
6572
|
+
} else {
|
|
6573
|
+
const chosen = await multiSelect(
|
|
6574
|
+
io,
|
|
6575
|
+
"Select folders",
|
|
6576
|
+
dirs.map((name) => ({ value: name, label: `${name}/` })),
|
|
6577
|
+
{ defaults: dirs }
|
|
6578
|
+
);
|
|
6579
|
+
answers.include = chosen.map((name) => `${name}/**`);
|
|
6580
|
+
}
|
|
6581
|
+
} else if (scope === "no-tests") {
|
|
6582
|
+
answers.excludePatterns = [
|
|
6583
|
+
...answers.excludePatterns,
|
|
6584
|
+
"**/*.test.*",
|
|
6585
|
+
"**/*.spec.*",
|
|
6586
|
+
"tests",
|
|
6587
|
+
"test"
|
|
6588
|
+
];
|
|
6589
|
+
} else if (scope === "custom") {
|
|
6590
|
+
const raw = await text(io, "Glob patterns to include (comma separated)", {
|
|
6591
|
+
defaultValue: ""
|
|
6592
|
+
});
|
|
6593
|
+
answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
6594
|
+
}
|
|
6595
|
+
const currentBudget = String(answers.maxPartBytes);
|
|
6596
|
+
const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
|
|
6597
|
+
defaultValue: SIZE_CHOICES.some((choice) => choice.value === currentBudget) ? currentBudget : "120k"
|
|
6598
|
+
});
|
|
6599
|
+
if (sizeChoice === "custom") {
|
|
6600
|
+
const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
|
|
6601
|
+
defaultValue: String(answers.maxPartBytes),
|
|
6602
|
+
validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null
|
|
6603
|
+
});
|
|
6604
|
+
answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
|
|
6489
6605
|
} else {
|
|
6490
|
-
|
|
6491
|
-
io,
|
|
6492
|
-
"Select folders",
|
|
6493
|
-
dirs.map((name) => ({ value: name, label: `${name}/` })),
|
|
6494
|
-
{ defaults: dirs }
|
|
6495
|
-
);
|
|
6496
|
-
answers.include = chosen.map((name) => `${name}/**`);
|
|
6606
|
+
answers.maxPartBytes = sizeChoice;
|
|
6497
6607
|
}
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
"
|
|
6502
|
-
"
|
|
6503
|
-
"
|
|
6504
|
-
"
|
|
6608
|
+
const artifactChoices = [
|
|
6609
|
+
{ value: "readme", label: "README.md", hint: "orientation page" },
|
|
6610
|
+
{ value: "manifest", label: "manifest.json", hint: "required by validate and report" },
|
|
6611
|
+
{ value: "parts", label: "parts/", hint: "the code payload" },
|
|
6612
|
+
{ value: "navigation", label: "reports/navigation.md", hint: "file \u2192 part index" },
|
|
6613
|
+
{ value: "projectTree", label: "reports/project-tree.md", hint: "ASCII tree" },
|
|
6614
|
+
{ value: "audit", label: "reports/audit.md", hint: "security and health findings" },
|
|
6615
|
+
{ value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
|
|
6505
6616
|
];
|
|
6506
|
-
|
|
6507
|
-
const
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
answers.maxPartBytes = sizeChoice;
|
|
6523
|
-
}
|
|
6524
|
-
const artifactChoices = [
|
|
6525
|
-
{ value: "readme", label: "README.md", hint: "orientation page" },
|
|
6526
|
-
{ value: "manifest", label: "manifest.json", hint: "required by validate and report" },
|
|
6527
|
-
{ value: "parts", label: "parts/", hint: "the code payload" },
|
|
6528
|
-
{ value: "navigation", label: "reports/navigation.md", hint: "file \u2192 part index" },
|
|
6529
|
-
{ value: "projectTree", label: "reports/project-tree.md", hint: "ASCII tree" },
|
|
6530
|
-
{ value: "audit", label: "reports/audit.md", hint: "security and health findings" },
|
|
6531
|
-
{ value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
|
|
6532
|
-
];
|
|
6533
|
-
const presetEnabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
|
|
6534
|
-
const selectedArtifacts = await multiSelect(
|
|
6535
|
-
io,
|
|
6536
|
-
"Which artifacts should be generated?",
|
|
6537
|
-
artifactChoices,
|
|
6538
|
-
{ defaults: presetEnabled }
|
|
6539
|
-
);
|
|
6540
|
-
for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
|
|
6541
|
-
const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
|
|
6542
|
-
answers.includeSensitiveFiles = await confirm(
|
|
6543
|
-
io,
|
|
6544
|
-
"Include credential files (.env, *.pem, .npmrc, .ssh/)?",
|
|
6545
|
-
{ defaultValue: false }
|
|
6546
|
-
);
|
|
6547
|
-
io.output.write("\n Review\n\n");
|
|
6548
|
-
io.output.write(` project ${answers.preset}
|
|
6617
|
+
const presetEnabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
|
|
6618
|
+
const selectedArtifacts = await multiSelect(
|
|
6619
|
+
io,
|
|
6620
|
+
"Which artifacts should be generated?",
|
|
6621
|
+
artifactChoices,
|
|
6622
|
+
{ defaults: presetEnabled }
|
|
6623
|
+
);
|
|
6624
|
+
for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
|
|
6625
|
+
const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
|
|
6626
|
+
answers.includeSensitiveFiles = await confirm(
|
|
6627
|
+
io,
|
|
6628
|
+
"Include credential files (.env, *.pem, .npmrc, .ssh/)?",
|
|
6629
|
+
{ defaultValue: false }
|
|
6630
|
+
);
|
|
6631
|
+
io.output.write("\n Review\n\n");
|
|
6632
|
+
io.output.write(` project ${answers.preset}
|
|
6549
6633
|
`);
|
|
6550
|
-
|
|
6634
|
+
io.output.write(` include ${answers.include.length ? answers.include.join(", ") : "everything"}
|
|
6551
6635
|
`);
|
|
6552
|
-
|
|
6636
|
+
io.output.write(` part size ${String(answers.maxPartBytes)}
|
|
6553
6637
|
`);
|
|
6554
|
-
|
|
6638
|
+
io.output.write(` artifacts ${enabledCount} of ${ARTIFACT_NAMES.length}
|
|
6555
6639
|
`);
|
|
6556
|
-
|
|
6640
|
+
io.output.write(` credentials ${answers.includeSensitiveFiles ? "included" : "skipped"}
|
|
6557
6641
|
|
|
6558
6642
|
`);
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6643
|
+
io.output.write(" Will write snapshot.config with your answers, plus every other setting\n");
|
|
6644
|
+
io.output.write(" at its default \u2014 so nothing is hidden behind the docs.\n\n");
|
|
6645
|
+
const proceed = await confirm(io, "Write this and continue?", { defaultValue: true });
|
|
6646
|
+
if (!proceed) throw new PromptCancelled();
|
|
6647
|
+
}
|
|
6563
6648
|
}
|
|
6564
6649
|
const configText = renderConfig(answers);
|
|
6565
6650
|
const preferredName = chooseConfigFileName(options.cwd);
|
|
6566
6651
|
const presentConfigs = CONFIG_CANDIDATES.filter(
|
|
6567
|
-
(name) =>
|
|
6652
|
+
(name) => fs11.existsSync(path12.join(options.cwd, name))
|
|
6568
6653
|
);
|
|
6569
6654
|
const present = presentConfigs[0] ?? null;
|
|
6570
6655
|
if (presentConfigs.length > 1) {
|
|
@@ -6573,7 +6658,7 @@ async function runInit(options) {
|
|
|
6573
6658
|
);
|
|
6574
6659
|
}
|
|
6575
6660
|
const configName = present !== null && options.force ? present : preferredName;
|
|
6576
|
-
const configPath =
|
|
6661
|
+
const configPath = path12.join(options.cwd, configName);
|
|
6577
6662
|
const reused = present !== null && options.force !== true && options.reuseConfig === true;
|
|
6578
6663
|
if (reused) notes.push(`Kept the existing ${present} instead of writing a new one.`);
|
|
6579
6664
|
if (options.dryRun) {
|
|
@@ -6583,6 +6668,8 @@ async function runInit(options) {
|
|
|
6583
6668
|
written: false,
|
|
6584
6669
|
reused,
|
|
6585
6670
|
preset: answers.preset,
|
|
6671
|
+
reason,
|
|
6672
|
+
fallback,
|
|
6586
6673
|
scripts: [],
|
|
6587
6674
|
gitignoreEntries: [],
|
|
6588
6675
|
dependency: { name: GENERATOR_NAME, range: `^${GENERATOR_VERSION}`, added: false },
|
|
@@ -6604,7 +6691,7 @@ async function runInit(options) {
|
|
|
6604
6691
|
'package.json already defines a "snapshot" config. Remove that key, or re-run with --force to write a config file that takes precedence over it.'
|
|
6605
6692
|
);
|
|
6606
6693
|
}
|
|
6607
|
-
if (
|
|
6694
|
+
if (fs11.existsSync(configPath) && !options.force && !reused) {
|
|
6608
6695
|
throw new Error(`${configName} already exists. Re-run with --force to replace it.`);
|
|
6609
6696
|
}
|
|
6610
6697
|
if (!reused) writeFileOrExplain(configPath, configText);
|
|
@@ -6617,12 +6704,56 @@ async function runInit(options) {
|
|
|
6617
6704
|
written: true,
|
|
6618
6705
|
reused,
|
|
6619
6706
|
preset: answers.preset,
|
|
6707
|
+
reason,
|
|
6708
|
+
fallback,
|
|
6620
6709
|
scripts,
|
|
6621
6710
|
gitignoreEntries: ignore.added,
|
|
6622
6711
|
dependency,
|
|
6623
6712
|
notes
|
|
6624
6713
|
};
|
|
6625
6714
|
}
|
|
6715
|
+
function describeTarget(cwd) {
|
|
6716
|
+
const relative7 = path12.relative(process.cwd(), cwd);
|
|
6717
|
+
if (relative7 === "") return "this directory";
|
|
6718
|
+
if (relative7.startsWith("..") || path12.isAbsolute(relative7)) return cwd;
|
|
6719
|
+
return relative7;
|
|
6720
|
+
}
|
|
6721
|
+
function firstRunMessage(reason, cwd) {
|
|
6722
|
+
const where = describeTarget(cwd);
|
|
6723
|
+
if (reason === "empty") {
|
|
6724
|
+
return `Nothing to snapshot: ${where} has no files. Run this inside the repository you want to snapshot, or pass --cwd.`;
|
|
6725
|
+
}
|
|
6726
|
+
return `No configuration found in ${where} and this session is not interactive, so nothing was generated.
|
|
6727
|
+
Choose one:
|
|
6728
|
+
${GENERATOR_NAME} --autopilot detect the project, set it up and generate, no questions
|
|
6729
|
+
${GENERATOR_NAME} init --yes write a config from the detected defaults, then generate
|
|
6730
|
+
${GENERATOR_NAME} init write a config interactively`;
|
|
6731
|
+
}
|
|
6732
|
+
function planFirstRun(facts) {
|
|
6733
|
+
if (facts.emptyTarget) return { action: "refuse", exitCode: 1, reason: "empty" };
|
|
6734
|
+
if (facts.autopilot) return { action: "setup", interactive: false };
|
|
6735
|
+
if (facts.hasConfig || facts.hasPriorSnapshot) return { action: "generate" };
|
|
6736
|
+
if (facts.interactive) return { action: "setup", interactive: true };
|
|
6737
|
+
return { action: "refuse", exitCode: 2, reason: "undecided" };
|
|
6738
|
+
}
|
|
6739
|
+
var NOT_CONTENT = /* @__PURE__ */ new Set([".git", ".hg", ".svn", ".bzr", ".DS_Store", "Thumbs.db"]);
|
|
6740
|
+
function isEffectivelyEmpty(cwd) {
|
|
6741
|
+
try {
|
|
6742
|
+
return fs11.readdirSync(cwd).every((entry) => NOT_CONTENT.has(entry));
|
|
6743
|
+
} catch {
|
|
6744
|
+
return false;
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6747
|
+
function hasPriorSnapshot(cwd, outDir) {
|
|
6748
|
+
let parsed;
|
|
6749
|
+
try {
|
|
6750
|
+
parsed = JSON.parse(fs11.readFileSync(path12.join(cwd, outDir, ".codebase-snapshot"), "utf8"));
|
|
6751
|
+
} catch {
|
|
6752
|
+
return false;
|
|
6753
|
+
}
|
|
6754
|
+
if (parsed === null || typeof parsed !== "object") return false;
|
|
6755
|
+
return isOurGeneratorName(parsed.generator);
|
|
6756
|
+
}
|
|
6626
6757
|
|
|
6627
6758
|
// src/index.ts
|
|
6628
6759
|
var SCHEMA_VERSION = "3.0.0";
|
|
@@ -6704,13 +6835,13 @@ function buildImportResolver(files) {
|
|
|
6704
6835
|
const relativePath = file.relativePath;
|
|
6705
6836
|
remember(byPath, relativePath, relativePath);
|
|
6706
6837
|
remember(byPathFolded, relativePath.toLowerCase(), relativePath);
|
|
6707
|
-
const extension =
|
|
6838
|
+
const extension = path12.posix.extname(relativePath);
|
|
6708
6839
|
if (!SOURCE_EXTENSIONS.has(extension)) continue;
|
|
6709
6840
|
const stem = relativePath.slice(0, -extension.length);
|
|
6710
6841
|
remember(byStem, stem, relativePath);
|
|
6711
6842
|
remember(byStemFolded, stem.toLowerCase(), relativePath);
|
|
6712
|
-
if (
|
|
6713
|
-
const directory =
|
|
6843
|
+
if (path12.posix.basename(stem) === "index") {
|
|
6844
|
+
const directory = path12.posix.dirname(stem);
|
|
6714
6845
|
remember(byStem, directory, relativePath);
|
|
6715
6846
|
remember(byStemFolded, directory.toLowerCase(), relativePath);
|
|
6716
6847
|
}
|
|
@@ -6719,8 +6850,8 @@ function buildImportResolver(files) {
|
|
|
6719
6850
|
}
|
|
6720
6851
|
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
6721
6852
|
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
6722
|
-
const joined =
|
|
6723
|
-
|
|
6853
|
+
const joined = path12.posix.normalize(
|
|
6854
|
+
path12.posix.join(path12.posix.dirname(fromFile), specifier)
|
|
6724
6855
|
);
|
|
6725
6856
|
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
6726
6857
|
const direct = exact(joined);
|
|
@@ -6813,11 +6944,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
6813
6944
|
return errors;
|
|
6814
6945
|
}
|
|
6815
6946
|
function resolveSafeOutputDirs(config) {
|
|
6816
|
-
const cwd =
|
|
6817
|
-
const outDir =
|
|
6818
|
-
const tmpDir =
|
|
6819
|
-
const root =
|
|
6820
|
-
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${
|
|
6947
|
+
const cwd = path12.resolve(config.cwd);
|
|
6948
|
+
const outDir = path12.resolve(cwd, config.outDir);
|
|
6949
|
+
const tmpDir = path12.resolve(cwd, config.tmpDir);
|
|
6950
|
+
const root = path12.parse(cwd).root;
|
|
6951
|
+
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${path12.sep}`);
|
|
6821
6952
|
const reject = (reason) => {
|
|
6822
6953
|
throw new Error(
|
|
6823
6954
|
`Refusing to generate: ${reason}. outDir=${outDir} tmpDir=${tmpDir} cwd=${cwd}`
|
|
@@ -6873,14 +7004,14 @@ function listEntries(dir) {
|
|
|
6873
7004
|
const walk = (absolute, prefix) => {
|
|
6874
7005
|
let children;
|
|
6875
7006
|
try {
|
|
6876
|
-
children =
|
|
7007
|
+
children = fs11.readdirSync(absolute, { withFileTypes: true });
|
|
6877
7008
|
} catch {
|
|
6878
7009
|
return;
|
|
6879
7010
|
}
|
|
6880
7011
|
for (const child of children) {
|
|
6881
|
-
const
|
|
6882
|
-
entries.push(
|
|
6883
|
-
if (child.isDirectory()) walk(
|
|
7012
|
+
const relative7 = prefix === "" ? child.name : `${prefix}/${child.name}`;
|
|
7013
|
+
entries.push(relative7);
|
|
7014
|
+
if (child.isDirectory()) walk(path12.join(absolute, child.name), relative7);
|
|
6884
7015
|
}
|
|
6885
7016
|
};
|
|
6886
7017
|
walk(dir, "");
|
|
@@ -6897,28 +7028,28 @@ function markerPayload(dir, state, id, createdAt) {
|
|
|
6897
7028
|
pid: process.pid,
|
|
6898
7029
|
entries: listEntries(dir)
|
|
6899
7030
|
};
|
|
6900
|
-
const manifestPath =
|
|
6901
|
-
if (
|
|
6902
|
-
marker2.manifestSha256 = sha256Hex2(
|
|
7031
|
+
const manifestPath = path12.join(dir, "manifest.json");
|
|
7032
|
+
if (fs11.existsSync(manifestPath)) {
|
|
7033
|
+
marker2.manifestSha256 = sha256Hex2(fs11.readFileSync(manifestPath));
|
|
6903
7034
|
}
|
|
6904
7035
|
return marker2;
|
|
6905
7036
|
}
|
|
6906
7037
|
function writeOwnershipMarker(dir, marker2) {
|
|
6907
|
-
|
|
6908
|
-
|
|
7038
|
+
fs11.writeFileSync(
|
|
7039
|
+
path12.join(dir, OWNERSHIP_MARKER),
|
|
6909
7040
|
`${JSON.stringify(marker2, null, 2)}
|
|
6910
7041
|
`,
|
|
6911
7042
|
"utf8"
|
|
6912
7043
|
);
|
|
6913
7044
|
}
|
|
6914
7045
|
function readOwnershipMarker(dir) {
|
|
6915
|
-
const markerPath =
|
|
6916
|
-
if (!
|
|
7046
|
+
const markerPath = path12.join(dir, OWNERSHIP_MARKER);
|
|
7047
|
+
if (!fs11.existsSync(markerPath)) {
|
|
6917
7048
|
return { marker: null, problem: "it holds no .codebase-snapshot marker" };
|
|
6918
7049
|
}
|
|
6919
7050
|
let raw;
|
|
6920
7051
|
try {
|
|
6921
|
-
raw =
|
|
7052
|
+
raw = fs11.readFileSync(markerPath, "utf8");
|
|
6922
7053
|
} catch {
|
|
6923
7054
|
return { marker: null, problem: "its .codebase-snapshot marker could not be read" };
|
|
6924
7055
|
}
|
|
@@ -6986,11 +7117,11 @@ function assessSnapshotDirectory(dir) {
|
|
|
6986
7117
|
marker: marker2
|
|
6987
7118
|
};
|
|
6988
7119
|
}
|
|
6989
|
-
const manifestPath =
|
|
6990
|
-
if (
|
|
7120
|
+
const manifestPath = path12.join(dir, "manifest.json");
|
|
7121
|
+
if (fs11.existsSync(manifestPath)) {
|
|
6991
7122
|
let raw;
|
|
6992
7123
|
try {
|
|
6993
|
-
raw =
|
|
7124
|
+
raw = fs11.readFileSync(manifestPath, "utf8");
|
|
6994
7125
|
} catch {
|
|
6995
7126
|
return {
|
|
6996
7127
|
owned: false,
|
|
@@ -7072,13 +7203,13 @@ function containsNoFiles(dir) {
|
|
|
7072
7203
|
const current = pending.pop();
|
|
7073
7204
|
let entries;
|
|
7074
7205
|
try {
|
|
7075
|
-
entries =
|
|
7206
|
+
entries = fs11.readdirSync(current, { withFileTypes: true });
|
|
7076
7207
|
} catch {
|
|
7077
7208
|
return false;
|
|
7078
7209
|
}
|
|
7079
7210
|
for (const entry of entries) {
|
|
7080
7211
|
if (entry.isDirectory()) {
|
|
7081
|
-
pending.push(
|
|
7212
|
+
pending.push(path12.join(current, entry.name));
|
|
7082
7213
|
} else if (!IGNORABLE_ENTRIES.has(entry.name)) {
|
|
7083
7214
|
return false;
|
|
7084
7215
|
}
|
|
@@ -7087,13 +7218,13 @@ function containsNoFiles(dir) {
|
|
|
7087
7218
|
return true;
|
|
7088
7219
|
}
|
|
7089
7220
|
function assertReplaceable(target, label, secretRules) {
|
|
7090
|
-
if (!
|
|
7091
|
-
if (!
|
|
7221
|
+
if (!fs11.existsSync(target)) return;
|
|
7222
|
+
if (!fs11.statSync(target).isDirectory()) {
|
|
7092
7223
|
throw new Error(
|
|
7093
7224
|
`Refusing to write into ${label} ${target}: it exists but is not a directory.`
|
|
7094
7225
|
);
|
|
7095
7226
|
}
|
|
7096
|
-
const entries =
|
|
7227
|
+
const entries = fs11.readdirSync(target);
|
|
7097
7228
|
if (entries.length === 0) return;
|
|
7098
7229
|
if (entries.every((name) => IGNORABLE_ENTRIES.has(name))) return;
|
|
7099
7230
|
if (containsNoFiles(target)) return;
|
|
@@ -7113,18 +7244,18 @@ function assertReplaceable(target, label, secretRules) {
|
|
|
7113
7244
|
);
|
|
7114
7245
|
}
|
|
7115
7246
|
function assertStagingRootUsable(tmpDir, secretRules) {
|
|
7116
|
-
if (!
|
|
7117
|
-
if (!
|
|
7247
|
+
if (!fs11.existsSync(tmpDir)) return;
|
|
7248
|
+
if (!fs11.statSync(tmpDir).isDirectory()) {
|
|
7118
7249
|
throw new Error(`Refusing to write into tmpDir ${tmpDir}: it exists but is not a directory.`);
|
|
7119
7250
|
}
|
|
7120
|
-
const entries =
|
|
7251
|
+
const entries = fs11.readdirSync(tmpDir);
|
|
7121
7252
|
if (entries.length === 0) return;
|
|
7122
7253
|
const foreign = entries.filter((name) => {
|
|
7123
|
-
const full =
|
|
7254
|
+
const full = path12.join(tmpDir, name);
|
|
7124
7255
|
if (!name.startsWith(RUN_DIR_PREFIX)) return true;
|
|
7125
7256
|
let stats;
|
|
7126
7257
|
try {
|
|
7127
|
-
stats =
|
|
7258
|
+
stats = fs11.statSync(full);
|
|
7128
7259
|
} catch {
|
|
7129
7260
|
return true;
|
|
7130
7261
|
}
|
|
@@ -7132,7 +7263,7 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7132
7263
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7133
7264
|
if (marker2) return false;
|
|
7134
7265
|
try {
|
|
7135
|
-
return
|
|
7266
|
+
return fs11.readdirSync(full).length > 0;
|
|
7136
7267
|
} catch {
|
|
7137
7268
|
return true;
|
|
7138
7269
|
}
|
|
@@ -7147,13 +7278,13 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7147
7278
|
function cleanStaleRunDirs(tmpDir) {
|
|
7148
7279
|
let entries;
|
|
7149
7280
|
try {
|
|
7150
|
-
entries =
|
|
7281
|
+
entries = fs11.readdirSync(tmpDir);
|
|
7151
7282
|
} catch {
|
|
7152
7283
|
return;
|
|
7153
7284
|
}
|
|
7154
7285
|
for (const name of entries) {
|
|
7155
7286
|
if (!name.startsWith(RUN_DIR_PREFIX)) continue;
|
|
7156
|
-
const full =
|
|
7287
|
+
const full = path12.join(tmpDir, name);
|
|
7157
7288
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7158
7289
|
if (marker2) {
|
|
7159
7290
|
const created = Date.parse(marker2.createdAt);
|
|
@@ -7161,33 +7292,33 @@ function cleanStaleRunDirs(tmpDir) {
|
|
|
7161
7292
|
const abandoned = typeof marker2.pid === "number" && !isProcessAlive(marker2.pid);
|
|
7162
7293
|
if (aged || abandoned) {
|
|
7163
7294
|
try {
|
|
7164
|
-
|
|
7295
|
+
fs11.rmSync(full, { recursive: true, force: true });
|
|
7165
7296
|
} catch {
|
|
7166
7297
|
}
|
|
7167
7298
|
}
|
|
7168
7299
|
continue;
|
|
7169
7300
|
}
|
|
7170
7301
|
try {
|
|
7171
|
-
if (
|
|
7302
|
+
if (fs11.readdirSync(full).length === 0) fs11.rmSync(full, { recursive: true, force: true });
|
|
7172
7303
|
} catch {
|
|
7173
7304
|
}
|
|
7174
7305
|
}
|
|
7175
7306
|
}
|
|
7176
7307
|
function removeIfEmpty(dir) {
|
|
7177
7308
|
try {
|
|
7178
|
-
if (
|
|
7309
|
+
if (fs11.readdirSync(dir).length === 0) fs11.rmSync(dir, { recursive: true, force: true });
|
|
7179
7310
|
} catch {
|
|
7180
7311
|
}
|
|
7181
7312
|
}
|
|
7182
7313
|
function ensureParentDirectory(target, label) {
|
|
7183
|
-
const parent =
|
|
7184
|
-
if (
|
|
7314
|
+
const parent = path12.dirname(target);
|
|
7315
|
+
if (fs11.existsSync(parent) && !fs11.statSync(parent).isDirectory()) {
|
|
7185
7316
|
throw new Error(
|
|
7186
7317
|
`Refusing to create ${label} ${target}: ${parent} exists but is not a directory.`
|
|
7187
7318
|
);
|
|
7188
7319
|
}
|
|
7189
7320
|
try {
|
|
7190
|
-
|
|
7321
|
+
fs11.mkdirSync(parent, { recursive: true });
|
|
7191
7322
|
} catch (err) {
|
|
7192
7323
|
throw new Error(
|
|
7193
7324
|
`Refusing to create ${label} ${target}: its parent ${parent} could not be created (${err.message}).`
|
|
@@ -7196,7 +7327,7 @@ function ensureParentDirectory(target, label) {
|
|
|
7196
7327
|
}
|
|
7197
7328
|
function acquireRunLock(stagingRoot) {
|
|
7198
7329
|
const key = lockKeyFor(stagingRoot);
|
|
7199
|
-
const lockPath =
|
|
7330
|
+
const lockPath = path12.join(os.tmpdir(), `codebase-snapshot-${key}.lock`);
|
|
7200
7331
|
const payload = `${JSON.stringify({
|
|
7201
7332
|
pid: process.pid,
|
|
7202
7333
|
stagingRoot,
|
|
@@ -7205,22 +7336,22 @@ function acquireRunLock(stagingRoot) {
|
|
|
7205
7336
|
`;
|
|
7206
7337
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
7207
7338
|
try {
|
|
7208
|
-
const handle =
|
|
7339
|
+
const handle = fs11.openSync(lockPath, "wx");
|
|
7209
7340
|
try {
|
|
7210
|
-
|
|
7341
|
+
fs11.writeSync(handle, payload);
|
|
7211
7342
|
} finally {
|
|
7212
|
-
|
|
7343
|
+
fs11.closeSync(handle);
|
|
7213
7344
|
}
|
|
7214
7345
|
return () => {
|
|
7215
7346
|
try {
|
|
7216
|
-
|
|
7347
|
+
fs11.rmSync(lockPath, { force: true });
|
|
7217
7348
|
} catch {
|
|
7218
7349
|
}
|
|
7219
7350
|
};
|
|
7220
7351
|
} catch (err) {
|
|
7221
7352
|
if (err.code !== "EEXIST") throw err;
|
|
7222
7353
|
if (attempt === 0 && lockOwnerIsGone(lockPath)) {
|
|
7223
|
-
|
|
7354
|
+
fs11.rmSync(lockPath, { force: true });
|
|
7224
7355
|
continue;
|
|
7225
7356
|
}
|
|
7226
7357
|
throw new Error(
|
|
@@ -7233,7 +7364,7 @@ function acquireRunLock(stagingRoot) {
|
|
|
7233
7364
|
function lockKeyFor(stagingRoot) {
|
|
7234
7365
|
let key = stagingRoot;
|
|
7235
7366
|
try {
|
|
7236
|
-
key =
|
|
7367
|
+
key = fs11.realpathSync(stagingRoot);
|
|
7237
7368
|
} catch {
|
|
7238
7369
|
}
|
|
7239
7370
|
if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
|
|
@@ -7242,7 +7373,7 @@ function lockKeyFor(stagingRoot) {
|
|
|
7242
7373
|
function lockOwnerIsGone(lockPath) {
|
|
7243
7374
|
let raw;
|
|
7244
7375
|
try {
|
|
7245
|
-
raw =
|
|
7376
|
+
raw = fs11.readFileSync(lockPath, "utf8");
|
|
7246
7377
|
} catch {
|
|
7247
7378
|
return false;
|
|
7248
7379
|
}
|
|
@@ -7262,12 +7393,12 @@ function lockOwnerIsGone(lockPath) {
|
|
|
7262
7393
|
}
|
|
7263
7394
|
function writeManifestAtomically(manifestPath, manifest) {
|
|
7264
7395
|
const tempPath = `${manifestPath}.${process.pid}.tmp`;
|
|
7265
|
-
|
|
7396
|
+
fs11.writeFileSync(tempPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
7266
7397
|
try {
|
|
7267
|
-
|
|
7398
|
+
fs11.renameSync(tempPath, manifestPath);
|
|
7268
7399
|
} catch (err) {
|
|
7269
7400
|
try {
|
|
7270
|
-
|
|
7401
|
+
fs11.rmSync(tempPath, { force: true });
|
|
7271
7402
|
} catch {
|
|
7272
7403
|
}
|
|
7273
7404
|
throw err;
|
|
@@ -7290,31 +7421,31 @@ function swapIntoPlace(stagingDir, outDir) {
|
|
|
7290
7421
|
}
|
|
7291
7422
|
function swapOnce(stagingDir, outDir) {
|
|
7292
7423
|
const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
|
|
7293
|
-
const hadPrevious =
|
|
7294
|
-
if (hadPrevious)
|
|
7424
|
+
const hadPrevious = fs11.existsSync(outDir);
|
|
7425
|
+
if (hadPrevious) fs11.renameSync(outDir, backupDir);
|
|
7295
7426
|
try {
|
|
7296
7427
|
try {
|
|
7297
|
-
|
|
7428
|
+
fs11.renameSync(stagingDir, outDir);
|
|
7298
7429
|
} catch (err) {
|
|
7299
7430
|
if (err.code !== "EXDEV") throw err;
|
|
7300
7431
|
const partialDir = `${outDir}.partial-${process.pid}-${Date.now()}`;
|
|
7301
7432
|
try {
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7433
|
+
fs11.cpSync(stagingDir, partialDir, { recursive: true });
|
|
7434
|
+
fs11.renameSync(partialDir, outDir);
|
|
7435
|
+
fs11.rmSync(stagingDir, { recursive: true, force: true });
|
|
7305
7436
|
} catch (copyErr) {
|
|
7306
7437
|
try {
|
|
7307
|
-
|
|
7438
|
+
fs11.rmSync(partialDir, { recursive: true, force: true });
|
|
7308
7439
|
} catch {
|
|
7309
7440
|
}
|
|
7310
7441
|
throw copyErr;
|
|
7311
7442
|
}
|
|
7312
7443
|
}
|
|
7313
|
-
if (hadPrevious)
|
|
7444
|
+
if (hadPrevious) fs11.rmSync(backupDir, { recursive: true, force: true });
|
|
7314
7445
|
} catch (err) {
|
|
7315
|
-
if (hadPrevious &&
|
|
7316
|
-
if (
|
|
7317
|
-
|
|
7446
|
+
if (hadPrevious && fs11.existsSync(backupDir)) {
|
|
7447
|
+
if (fs11.existsSync(outDir)) fs11.rmSync(outDir, { recursive: true, force: true });
|
|
7448
|
+
fs11.renameSync(backupDir, outDir);
|
|
7318
7449
|
}
|
|
7319
7450
|
throw err;
|
|
7320
7451
|
}
|
|
@@ -7409,8 +7540,8 @@ function generateSnapshot(configInput, options) {
|
|
|
7409
7540
|
// first run cannot have marked them yet, so the name alone keeps a run from recording its own
|
|
7410
7541
|
// output — but only at these exact paths, not at every directory that shares the name.
|
|
7411
7542
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
7412
|
-
(directory) =>
|
|
7413
|
-
).filter((
|
|
7543
|
+
(directory) => path12.relative(config.cwd, path12.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
7544
|
+
).filter((relative7) => relative7.length > 0 && !relative7.startsWith(".."))
|
|
7414
7545
|
});
|
|
7415
7546
|
const files = [];
|
|
7416
7547
|
const monolithBodies = /* @__PURE__ */ new Map();
|
|
@@ -7430,12 +7561,12 @@ function generateSnapshot(configInput, options) {
|
|
|
7430
7561
|
let totalSizeBytes = 0;
|
|
7431
7562
|
let largeFileCount = 0;
|
|
7432
7563
|
for (const relativePath of collection.files) {
|
|
7433
|
-
const absolutePath =
|
|
7564
|
+
const absolutePath = path12.join(config.cwd, relativePath);
|
|
7434
7565
|
let stats;
|
|
7435
7566
|
try {
|
|
7436
|
-
stats =
|
|
7567
|
+
stats = fs11.statSync(absolutePath);
|
|
7437
7568
|
if (!stats.isFile()) {
|
|
7438
|
-
const nestedRepository = stats.isDirectory() &&
|
|
7569
|
+
const nestedRepository = stats.isDirectory() && fs11.existsSync(path12.join(absolutePath, ".git"));
|
|
7439
7570
|
skipped.push(
|
|
7440
7571
|
nestedRepository ? {
|
|
7441
7572
|
path: relativePath,
|
|
@@ -7455,7 +7586,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7455
7586
|
}
|
|
7456
7587
|
let buffer;
|
|
7457
7588
|
try {
|
|
7458
|
-
buffer =
|
|
7589
|
+
buffer = fs11.readFileSync(absolutePath);
|
|
7459
7590
|
} catch {
|
|
7460
7591
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
7461
7592
|
continue;
|
|
@@ -7785,12 +7916,12 @@ function generateSnapshot(configInput, options) {
|
|
|
7785
7916
|
let stagingDir = null;
|
|
7786
7917
|
try {
|
|
7787
7918
|
ensureParentDirectory(outDir, "outDir");
|
|
7788
|
-
|
|
7919
|
+
fs11.mkdirSync(tmpDir, { recursive: true });
|
|
7789
7920
|
cleanStaleRunDirs(tmpDir);
|
|
7790
7921
|
const snapshotId = crypto.randomUUID();
|
|
7791
7922
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7792
|
-
stagingDir =
|
|
7793
|
-
|
|
7923
|
+
stagingDir = path12.join(tmpDir, `${RUN_DIR_PREFIX}${process.pid}-${snapshotId.slice(0, 8)}`);
|
|
7924
|
+
fs11.mkdirSync(stagingDir);
|
|
7794
7925
|
writeOwnershipMarker(stagingDir, {
|
|
7795
7926
|
generator: GENERATOR_NAME,
|
|
7796
7927
|
version: GENERATOR_VERSION,
|
|
@@ -7800,41 +7931,41 @@ function generateSnapshot(configInput, options) {
|
|
|
7800
7931
|
id: snapshotId,
|
|
7801
7932
|
pid: process.pid
|
|
7802
7933
|
});
|
|
7803
|
-
const partsDir =
|
|
7804
|
-
const reportsDir =
|
|
7805
|
-
if (artifacts.parts)
|
|
7934
|
+
const partsDir = path12.join(stagingDir, "parts");
|
|
7935
|
+
const reportsDir = path12.join(stagingDir, "reports");
|
|
7936
|
+
if (artifacts.parts) fs11.mkdirSync(partsDir, { recursive: true });
|
|
7806
7937
|
if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
|
|
7807
|
-
|
|
7938
|
+
fs11.mkdirSync(reportsDir, { recursive: true });
|
|
7808
7939
|
}
|
|
7809
7940
|
if (artifacts.parts) {
|
|
7810
7941
|
for (const part of parts) {
|
|
7811
|
-
|
|
7942
|
+
fs11.writeFileSync(path12.join(partsDir, part.filename), part.markdown, "utf8");
|
|
7812
7943
|
}
|
|
7813
7944
|
}
|
|
7814
7945
|
if (projectTree !== null) {
|
|
7815
|
-
|
|
7946
|
+
fs11.writeFileSync(path12.join(reportsDir, "project-tree.md"), projectTree, "utf8");
|
|
7816
7947
|
}
|
|
7817
7948
|
if (artifacts.navigation) {
|
|
7818
|
-
|
|
7949
|
+
fs11.writeFileSync(path12.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
|
|
7819
7950
|
}
|
|
7820
7951
|
if (artifacts.audit) {
|
|
7821
|
-
|
|
7822
|
-
|
|
7952
|
+
fs11.writeFileSync(
|
|
7953
|
+
path12.join(reportsDir, "audit.md"),
|
|
7823
7954
|
generateAuditReport(manifest, secretFindings, config.secretRules),
|
|
7824
7955
|
"utf8"
|
|
7825
7956
|
);
|
|
7826
7957
|
}
|
|
7827
7958
|
if (artifacts.readme) {
|
|
7828
|
-
|
|
7959
|
+
fs11.writeFileSync(path12.join(stagingDir, "README.md"), generateReadme(manifest, artifacts), "utf8");
|
|
7829
7960
|
}
|
|
7830
7961
|
if (artifacts.manifest) {
|
|
7831
|
-
|
|
7962
|
+
fs11.writeFileSync(path12.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
7832
7963
|
}
|
|
7833
7964
|
if (monolithJson !== null) {
|
|
7834
|
-
|
|
7965
|
+
fs11.writeFileSync(path12.join(stagingDir, "full-codebase.json"), monolithJson, "utf8");
|
|
7835
7966
|
}
|
|
7836
7967
|
if (monolithMarkdown !== null) {
|
|
7837
|
-
|
|
7968
|
+
fs11.writeFileSync(path12.join(stagingDir, "full-codebase.md"), monolithMarkdown, "utf8");
|
|
7838
7969
|
}
|
|
7839
7970
|
writeOwnershipMarker(stagingDir, markerPayload(stagingDir, "complete", snapshotId, createdAt));
|
|
7840
7971
|
swapIntoPlace(stagingDir, outDir);
|
|
@@ -7843,7 +7974,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7843
7974
|
const pendingHook = runAfterGenerate(config, manifest);
|
|
7844
7975
|
const shipHookState = () => {
|
|
7845
7976
|
if (artifacts.manifest && manifest.warnings.length !== warningsBeforeHook) {
|
|
7846
|
-
writeManifestAtomically(
|
|
7977
|
+
writeManifestAtomically(path12.join(outDir, "manifest.json"), manifest);
|
|
7847
7978
|
}
|
|
7848
7979
|
writeOwnershipMarker(outDir, markerPayload(outDir, "complete", snapshotId, createdAt));
|
|
7849
7980
|
};
|
|
@@ -7866,7 +7997,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7866
7997
|
} catch (err) {
|
|
7867
7998
|
if (stagingDir !== null) {
|
|
7868
7999
|
try {
|
|
7869
|
-
|
|
8000
|
+
fs11.rmSync(stagingDir, { recursive: true, force: true });
|
|
7870
8001
|
} catch {
|
|
7871
8002
|
}
|
|
7872
8003
|
}
|
|
@@ -7880,6 +8011,6 @@ async function generateSnapshotAsync(configInput, options) {
|
|
|
7880
8011
|
return generateSnapshot(configInput, options);
|
|
7881
8012
|
}
|
|
7882
8013
|
|
|
7883
|
-
export { ARTIFACT_NAMES, DEFAULT_ANSWERS, DEFAULT_SECRET_PATTERNS, GENERATOR_NAME, GENERATOR_VERSION, LANGUAGE_PROFILES, MIN_MAX_PART_BYTES, PRESETS, PromptCancelled, SCHEMA_VERSION, anchorForPath, appendGitignoreEntries, assertReplaceable, assertUsableConfigInput, collectFiles, compareManifests, countInvalidUtf8Bytes, countLines, decodeText, defineConfig, describeManifestShapeProblem, detectLanguage, detectPreset, escapeControls, explainPath, fenceForContent, fenceForText, formatNumber, generateProjectTree, generateSnapshot, generateSnapshotAsync, getPreset, gitignorePath, globToRegExp, isBinaryBuffer, isBinaryExtension, isBinaryFile, isDiscoveryMode, isInteractive, isResolvedConfig, isSensitivePath, listPresetNames, loadConfigFile, loadConfigFileDetailed, loadManifest, markResolvedConfig, matchRule, matchesExclude, normalizeResolvedConfig, parseByteSize, partitionFiles, profileForPath, renderConfig, renderDiffText, renderExplainText, renumberParts, resolveConfig, resolveSafeOutputDirs, runInit, scanAndRedactSecrets, stripJsonComments, summarizeJson, summarizeMarkdown, summarizeTypeScriptSource, summarizeWithProfile, supportsTypeScriptImports, truncatePreview, truncateToBytes, unknownPresetMessage, validateSnapshot };
|
|
7884
|
-
//# sourceMappingURL=chunk-
|
|
7885
|
-
//# sourceMappingURL=chunk-
|
|
8014
|
+
export { ARTIFACT_NAMES, DEFAULT_ANSWERS, DEFAULT_SECRET_PATTERNS, GENERATOR_NAME, GENERATOR_VERSION, LANGUAGE_PROFILES, MIN_MAX_PART_BYTES, PRESETS, PromptCancelled, SCHEMA_VERSION, anchorForPath, appendGitignoreEntries, assertReplaceable, assertUsableConfigInput, collectFiles, compareManifests, countInvalidUtf8Bytes, countLines, decodeText, defaultIO, defineConfig, describeManifestShapeProblem, detectLanguage, detectPreset, escapeControls, explainPath, fenceForContent, fenceForText, findConfigFile, firstRunMessage, formatNumber, generateProjectTree, generateSnapshot, generateSnapshotAsync, getPreset, gitignorePath, globToRegExp, hasPriorSnapshot, isBinaryBuffer, isBinaryExtension, isBinaryFile, isDiscoveryMode, isEffectivelyEmpty, isInteractive, isResolvedConfig, isSensitivePath, listPresetNames, loadConfigFile, loadConfigFileDetailed, loadManifest, markResolvedConfig, matchRule, matchesExclude, normalizeResolvedConfig, parseByteSize, partitionFiles, planFirstRun, profileForPath, renderConfig, renderDiffText, renderExplainText, renumberParts, resolveConfig, resolveSafeOutputDirs, runInit, scanAndRedactSecrets, stripJsonComments, summarizeJson, summarizeMarkdown, summarizeTypeScriptSource, summarizeWithProfile, supportsTypeScriptImports, truncatePreview, truncateToBytes, unknownPresetMessage, validateSnapshot };
|
|
8015
|
+
//# sourceMappingURL=chunk-FCWD4LY7.js.map
|
|
8016
|
+
//# sourceMappingURL=chunk-FCWD4LY7.js.map
|