codebase-snapshot 0.9.1 → 0.9.5
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 +219 -0
- package/README.md +141 -127
- package/dist/bin.js +2617 -492
- package/dist/chunk-4AHHSNGG.js +2577 -0
- package/dist/{chunk-HT7J3NWT.js → chunk-B336MMNC.js} +599 -379
- package/dist/cli.cjs +2703 -600
- package/dist/cli.d.cts +11 -1
- package/dist/cli.d.ts +11 -1
- package/dist/cli.js +71 -754
- package/dist/index.cjs +587 -377
- 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 +95 -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
package/dist/index.cjs
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var crypto = require('crypto');
|
|
4
4
|
var async_hooks = require('async_hooks');
|
|
5
|
-
var
|
|
5
|
+
var fs11 = require('fs');
|
|
6
6
|
var os = require('os');
|
|
7
|
-
var
|
|
7
|
+
var path12 = require('path');
|
|
8
8
|
var ts = require('typescript');
|
|
9
9
|
var module$1 = require('module');
|
|
10
10
|
var url = require('url');
|
|
@@ -32,9 +32,9 @@ function _interopNamespace(e) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
35
|
-
var
|
|
35
|
+
var fs11__namespace = /*#__PURE__*/_interopNamespace(fs11);
|
|
36
36
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
37
|
-
var
|
|
37
|
+
var path12__namespace = /*#__PURE__*/_interopNamespace(path12);
|
|
38
38
|
var ts__default = /*#__PURE__*/_interopDefault(ts);
|
|
39
39
|
var childProcess__namespace = /*#__PURE__*/_interopNamespace(childProcess);
|
|
40
40
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
@@ -263,9 +263,9 @@ var JAVA_METHOD = /^\s+(?:public|private|protected)\s+(?:static\s+)?(?:final\s+)
|
|
|
263
263
|
var jvmProfile = {
|
|
264
264
|
id: "jvm",
|
|
265
265
|
extensions: [".java", ".kt", ".kts", ".scala"],
|
|
266
|
-
summarize(
|
|
266
|
+
summarize(path13, content) {
|
|
267
267
|
const summary = emptySummary();
|
|
268
|
-
const lower =
|
|
268
|
+
const lower = path13.toLowerCase();
|
|
269
269
|
const isJava = lower.endsWith(".java");
|
|
270
270
|
const isKotlin = lower.endsWith(".kt") || lower.endsWith(".kts");
|
|
271
271
|
eachLine(content, (line) => {
|
|
@@ -718,16 +718,16 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
718
718
|
// src/ast/profiles/sfc.ts
|
|
719
719
|
var SCRIPT_BLOCK = /<script[^>]*>([\s\S]*?)<\/script>/gi;
|
|
720
720
|
var ASTRO_FRONTMATTER = /^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?=\r?\n|$)/;
|
|
721
|
-
function componentName(
|
|
722
|
-
const base =
|
|
721
|
+
function componentName(path13) {
|
|
722
|
+
const base = path13.split("/").pop() ?? path13;
|
|
723
723
|
return base.replace(/\.[^.]+$/, "");
|
|
724
724
|
}
|
|
725
725
|
var sfcProfile = {
|
|
726
726
|
id: "sfc",
|
|
727
727
|
extensions: [".vue", ".svelte", ".astro"],
|
|
728
|
-
summarize(
|
|
728
|
+
summarize(path13, content) {
|
|
729
729
|
let script = "";
|
|
730
|
-
if (
|
|
730
|
+
if (path13.toLowerCase().endsWith(".astro")) {
|
|
731
731
|
const frontmatter = ASTRO_FRONTMATTER.exec(content);
|
|
732
732
|
if (frontmatter) script += `${frontmatter[1]}
|
|
733
733
|
`;
|
|
@@ -736,7 +736,7 @@ var sfcProfile = {
|
|
|
736
736
|
script += `${match[1]}
|
|
737
737
|
`;
|
|
738
738
|
}
|
|
739
|
-
const name = componentName(
|
|
739
|
+
const name = componentName(path13);
|
|
740
740
|
if (!script.trim()) {
|
|
741
741
|
const summary2 = emptySummary();
|
|
742
742
|
if (name) {
|
|
@@ -770,15 +770,15 @@ for (const profile of LANGUAGE_PROFILES) {
|
|
|
770
770
|
BY_EXTENSION.set(extension.toLowerCase(), profile);
|
|
771
771
|
}
|
|
772
772
|
}
|
|
773
|
-
function profileForPath(
|
|
774
|
-
const lower =
|
|
773
|
+
function profileForPath(path13) {
|
|
774
|
+
const lower = path13.toLowerCase();
|
|
775
775
|
const dot = lower.lastIndexOf(".");
|
|
776
776
|
if (dot === -1) return null;
|
|
777
777
|
return BY_EXTENSION.get(lower.slice(dot)) ?? null;
|
|
778
778
|
}
|
|
779
|
-
function summarizeWithProfile(
|
|
780
|
-
const profile = profileForPath(
|
|
781
|
-
return profile ? profile.summarize(
|
|
779
|
+
function summarizeWithProfile(path13, content) {
|
|
780
|
+
const profile = profileForPath(path13);
|
|
781
|
+
return profile ? profile.summarize(path13, content) : null;
|
|
782
782
|
}
|
|
783
783
|
|
|
784
784
|
// src/ast/structured.ts
|
|
@@ -949,15 +949,15 @@ var SKIPPED_WALK_DIRS = /* @__PURE__ */ new Set([
|
|
|
949
949
|
]);
|
|
950
950
|
function readSmallFile(fullPath) {
|
|
951
951
|
try {
|
|
952
|
-
const stats =
|
|
952
|
+
const stats = fs11__namespace.statSync(fullPath);
|
|
953
953
|
if (!stats.isFile() || stats.size > MANIFEST_MAX_BYTES) return null;
|
|
954
|
-
return
|
|
954
|
+
return fs11__namespace.readFileSync(fullPath, "utf8");
|
|
955
955
|
} catch {
|
|
956
956
|
return null;
|
|
957
957
|
}
|
|
958
958
|
}
|
|
959
959
|
function readManifest(cwd, name) {
|
|
960
|
-
return readSmallFile(
|
|
960
|
+
return readSmallFile(path12__namespace.join(cwd, name));
|
|
961
961
|
}
|
|
962
962
|
function dependenciesFromPackageJson(raw) {
|
|
963
963
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -1017,7 +1017,7 @@ function pnpmWorkspacePatterns(raw) {
|
|
|
1017
1017
|
function childDirectories(cwd, parent) {
|
|
1018
1018
|
let entries;
|
|
1019
1019
|
try {
|
|
1020
|
-
entries =
|
|
1020
|
+
entries = fs11__namespace.readdirSync(path12__namespace.join(cwd, parent), { withFileTypes: true });
|
|
1021
1021
|
} catch {
|
|
1022
1022
|
return [];
|
|
1023
1023
|
}
|
|
@@ -1052,13 +1052,13 @@ function workspacePackageDirs(cwd, patterns) {
|
|
|
1052
1052
|
function scanWorkspacePackages(cwd, patterns) {
|
|
1053
1053
|
const files = /* @__PURE__ */ new Set();
|
|
1054
1054
|
const dependencies = /* @__PURE__ */ new Set();
|
|
1055
|
-
for (const
|
|
1056
|
-
const full =
|
|
1055
|
+
for (const relative7 of workspacePackageDirs(cwd, patterns)) {
|
|
1056
|
+
const full = path12__namespace.join(cwd, relative7);
|
|
1057
1057
|
try {
|
|
1058
|
-
for (const entry of
|
|
1058
|
+
for (const entry of fs11__namespace.readdirSync(full, { withFileTypes: true })) files.add(entry.name);
|
|
1059
1059
|
} catch {
|
|
1060
1060
|
}
|
|
1061
|
-
const raw = readSmallFile(
|
|
1061
|
+
const raw = readSmallFile(path12__namespace.join(full, "package.json"));
|
|
1062
1062
|
if (raw !== null) {
|
|
1063
1063
|
for (const name of dependenciesFromPackageJson(raw)) dependencies.add(name);
|
|
1064
1064
|
}
|
|
@@ -1075,7 +1075,7 @@ function countMarkdownFiles(cwd) {
|
|
|
1075
1075
|
directoriesSeen += 1;
|
|
1076
1076
|
let entries;
|
|
1077
1077
|
try {
|
|
1078
|
-
entries =
|
|
1078
|
+
entries = fs11__namespace.readdirSync(current.dir, { withFileTypes: true });
|
|
1079
1079
|
} catch {
|
|
1080
1080
|
continue;
|
|
1081
1081
|
}
|
|
@@ -1086,7 +1086,7 @@ function countMarkdownFiles(cwd) {
|
|
|
1086
1086
|
}
|
|
1087
1087
|
if (!entry.isDirectory() || current.depth >= 2) continue;
|
|
1088
1088
|
if (entry.name.startsWith(".") || SKIPPED_WALK_DIRS.has(entry.name)) continue;
|
|
1089
|
-
queue.push({ dir:
|
|
1089
|
+
queue.push({ dir: path12__namespace.join(current.dir, entry.name), depth: current.depth + 1 });
|
|
1090
1090
|
}
|
|
1091
1091
|
}
|
|
1092
1092
|
return count;
|
|
@@ -1095,7 +1095,7 @@ function buildPresetContext(cwd) {
|
|
|
1095
1095
|
const files = /* @__PURE__ */ new Set();
|
|
1096
1096
|
const dirs = /* @__PURE__ */ new Set();
|
|
1097
1097
|
try {
|
|
1098
|
-
for (const entry of
|
|
1098
|
+
for (const entry of fs11__namespace.readdirSync(cwd, { withFileTypes: true })) {
|
|
1099
1099
|
if (entry.isDirectory()) dirs.add(entry.name);
|
|
1100
1100
|
else if (entry.isFile()) files.add(entry.name);
|
|
1101
1101
|
}
|
|
@@ -1112,7 +1112,7 @@ function buildPresetContext(cwd) {
|
|
|
1112
1112
|
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;
|
|
1113
1113
|
let workspace = { files: /* @__PURE__ */ new Set(), dependencies: /* @__PURE__ */ new Set() };
|
|
1114
1114
|
if (isWorkspace) {
|
|
1115
|
-
const pnpmRaw = files.has("pnpm-workspace.yaml") ? readSmallFile(
|
|
1115
|
+
const pnpmRaw = files.has("pnpm-workspace.yaml") ? readSmallFile(path12__namespace.join(cwd, "pnpm-workspace.yaml")) : null;
|
|
1116
1116
|
const declared = pnpmRaw === null ? [] : pnpmWorkspacePatterns(pnpmRaw);
|
|
1117
1117
|
const patterns = declared.length > 0 ? declared : declaredWorkspaces.length > 0 ? declaredWorkspaces : DEFAULT_WORKSPACE_PATTERNS;
|
|
1118
1118
|
workspace = scanWorkspacePackages(cwd, patterns);
|
|
@@ -1130,25 +1130,41 @@ function buildPresetContext(cwd) {
|
|
|
1130
1130
|
markdownCount: files.has("package.json") ? 0 : countMarkdownFiles(cwd)
|
|
1131
1131
|
};
|
|
1132
1132
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1133
|
+
|
|
1134
|
+
// src/presets/evidence.ts
|
|
1135
|
+
function fileEvidence(context, name) {
|
|
1136
|
+
return context.files.has(name) ? name : null;
|
|
1135
1137
|
}
|
|
1136
|
-
function
|
|
1138
|
+
function fileStartingWithEvidence(context, prefix) {
|
|
1137
1139
|
for (const file of context.files) {
|
|
1138
|
-
if (file.startsWith(prefix)) return
|
|
1140
|
+
if (file.startsWith(prefix)) return file;
|
|
1139
1141
|
}
|
|
1140
1142
|
for (const file of context.workspaceFiles) {
|
|
1141
|
-
if (file.startsWith(prefix)) return
|
|
1143
|
+
if (file.startsWith(prefix)) return file;
|
|
1142
1144
|
}
|
|
1143
|
-
return
|
|
1145
|
+
return null;
|
|
1146
|
+
}
|
|
1147
|
+
function dependencyEvidence(context, ...names) {
|
|
1148
|
+
for (const name of names) {
|
|
1149
|
+
if (context.dependencies.has(name)) return `depends on ${name}`;
|
|
1150
|
+
}
|
|
1151
|
+
return null;
|
|
1144
1152
|
}
|
|
1145
|
-
function
|
|
1153
|
+
function manifestEvidence(context, manifest, needle) {
|
|
1146
1154
|
const content = context.manifests[manifest];
|
|
1147
|
-
if (!content) return
|
|
1148
|
-
|
|
1155
|
+
if (!content) return null;
|
|
1156
|
+
if (!content.toLowerCase().includes(needle.toLowerCase())) return null;
|
|
1157
|
+
return `${needle} in ${manifest}`;
|
|
1158
|
+
}
|
|
1159
|
+
function suffixEvidence(context, suffix) {
|
|
1160
|
+
for (const file of context.files) {
|
|
1161
|
+
if (file.endsWith(suffix)) return file;
|
|
1162
|
+
}
|
|
1163
|
+
return null;
|
|
1149
1164
|
}
|
|
1150
|
-
function
|
|
1151
|
-
|
|
1165
|
+
function markdownEvidence(context) {
|
|
1166
|
+
if (context.markdownCount < 3 || context.files.has("package.json")) return null;
|
|
1167
|
+
return `${context.markdownCount} markdown files, no package.json`;
|
|
1152
1168
|
}
|
|
1153
1169
|
|
|
1154
1170
|
// src/presets/helpers.ts
|
|
@@ -1356,7 +1372,7 @@ var JS_PRESETS = [
|
|
|
1356
1372
|
{
|
|
1357
1373
|
name: "nextjs",
|
|
1358
1374
|
priority: 100,
|
|
1359
|
-
detect: (context) =>
|
|
1375
|
+
detect: (context) => fileStartingWithEvidence(context, "next.config") ?? dependencyEvidence(context, "next"),
|
|
1360
1376
|
layers: NEXTJS_LAYERS,
|
|
1361
1377
|
kinds: NEXTJS_KINDS,
|
|
1362
1378
|
exclude: NEXT_LIKE_EXCLUDES,
|
|
@@ -1365,7 +1381,7 @@ var JS_PRESETS = [
|
|
|
1365
1381
|
{
|
|
1366
1382
|
name: "nuxt",
|
|
1367
1383
|
priority: 98,
|
|
1368
|
-
detect: (context) =>
|
|
1384
|
+
detect: (context) => fileStartingWithEvidence(context, "nuxt.config") ?? dependencyEvidence(context, "nuxt", "nuxt3"),
|
|
1369
1385
|
layers: [
|
|
1370
1386
|
layer("pages", "pages/", "src/pages/"),
|
|
1371
1387
|
layer("components", "components/", "src/components/"),
|
|
@@ -1389,7 +1405,7 @@ var JS_PRESETS = [
|
|
|
1389
1405
|
{
|
|
1390
1406
|
name: "sveltekit",
|
|
1391
1407
|
priority: 96,
|
|
1392
|
-
detect: (context) =>
|
|
1408
|
+
detect: (context) => fileStartingWithEvidence(context, "svelte.config") ?? dependencyEvidence(context, "@sveltejs/kit", "svelte"),
|
|
1393
1409
|
layers: [
|
|
1394
1410
|
layer("routes", "src/routes/", "routes/"),
|
|
1395
1411
|
layer("lib", "src/lib/", "lib/"),
|
|
@@ -1410,7 +1426,7 @@ var JS_PRESETS = [
|
|
|
1410
1426
|
{
|
|
1411
1427
|
name: "remix",
|
|
1412
1428
|
priority: 94,
|
|
1413
|
-
detect: (context) =>
|
|
1429
|
+
detect: (context) => dependencyEvidence(context, "@remix-run/react", "@remix-run/node", "@remix-run/dev"),
|
|
1414
1430
|
layers: [
|
|
1415
1431
|
layer("routes", "app/routes/", "routes/"),
|
|
1416
1432
|
layer("components", "app/components/"),
|
|
@@ -1424,7 +1440,7 @@ var JS_PRESETS = [
|
|
|
1424
1440
|
{
|
|
1425
1441
|
name: "astro",
|
|
1426
1442
|
priority: 92,
|
|
1427
|
-
detect: (context) =>
|
|
1443
|
+
detect: (context) => fileStartingWithEvidence(context, "astro.config") ?? dependencyEvidence(context, "astro"),
|
|
1428
1444
|
layers: [
|
|
1429
1445
|
layer("pages", "src/pages/"),
|
|
1430
1446
|
layer("layouts", "src/layouts/"),
|
|
@@ -1439,7 +1455,7 @@ var JS_PRESETS = [
|
|
|
1439
1455
|
{
|
|
1440
1456
|
name: "angular",
|
|
1441
1457
|
priority: 90,
|
|
1442
|
-
detect: (context) =>
|
|
1458
|
+
detect: (context) => fileEvidence(context, "angular.json") ?? dependencyEvidence(context, "@angular/core"),
|
|
1443
1459
|
layers: [
|
|
1444
1460
|
layer("app", "src/app/"),
|
|
1445
1461
|
layer("features", "src/app/features/"),
|
|
@@ -1463,7 +1479,7 @@ var JS_PRESETS = [
|
|
|
1463
1479
|
{
|
|
1464
1480
|
name: "solid",
|
|
1465
1481
|
priority: 88,
|
|
1466
|
-
detect: (context) =>
|
|
1482
|
+
detect: (context) => dependencyEvidence(context, "solid-js", "solid-start"),
|
|
1467
1483
|
layers: [
|
|
1468
1484
|
layer("pages", "src/routes/", "src/pages/"),
|
|
1469
1485
|
layer("components", "src/components/"),
|
|
@@ -1476,7 +1492,15 @@ var JS_PRESETS = [
|
|
|
1476
1492
|
{
|
|
1477
1493
|
name: "vue-vite",
|
|
1478
1494
|
priority: 86,
|
|
1479
|
-
|
|
1495
|
+
// `vue` is the decisive signal; `vite` (or its config file) only says the app is
|
|
1496
|
+
// built with Vite rather than shipped pre-compiled, so it is required but not what
|
|
1497
|
+
// gets reported.
|
|
1498
|
+
detect: (context) => {
|
|
1499
|
+
const framework = dependencyEvidence(context, "vue");
|
|
1500
|
+
if (framework === null) return null;
|
|
1501
|
+
const build = dependencyEvidence(context, "vite") ?? fileStartingWithEvidence(context, "vite.config");
|
|
1502
|
+
return build === null ? null : framework;
|
|
1503
|
+
},
|
|
1480
1504
|
layers: [
|
|
1481
1505
|
layer("pages", "src/pages/", "src/views/"),
|
|
1482
1506
|
layer("components", "src/components/"),
|
|
@@ -1501,7 +1525,12 @@ var JS_PRESETS = [
|
|
|
1501
1525
|
{
|
|
1502
1526
|
name: "react-vite",
|
|
1503
1527
|
priority: 84,
|
|
1504
|
-
detect: (context) =>
|
|
1528
|
+
detect: (context) => {
|
|
1529
|
+
const framework = dependencyEvidence(context, "react");
|
|
1530
|
+
if (framework === null) return null;
|
|
1531
|
+
const build = dependencyEvidence(context, "vite") ?? fileStartingWithEvidence(context, "vite.config");
|
|
1532
|
+
return build === null ? null : framework;
|
|
1533
|
+
},
|
|
1505
1534
|
layers: VITE_LAYERS,
|
|
1506
1535
|
kinds: VITE_KINDS,
|
|
1507
1536
|
exclude: [".vite", "dist"],
|
|
@@ -1510,7 +1539,7 @@ var JS_PRESETS = [
|
|
|
1510
1539
|
{
|
|
1511
1540
|
name: "nest",
|
|
1512
1541
|
priority: 80,
|
|
1513
|
-
detect: (context) =>
|
|
1542
|
+
detect: (context) => dependencyEvidence(context, "@nestjs/core"),
|
|
1514
1543
|
layers: [
|
|
1515
1544
|
layer("modules", "src/modules/", "modules/"),
|
|
1516
1545
|
layer("controllers", "src/controllers/", "controllers/"),
|
|
@@ -1536,7 +1565,7 @@ var JS_PRESETS = [
|
|
|
1536
1565
|
{
|
|
1537
1566
|
name: "node-express",
|
|
1538
1567
|
priority: 78,
|
|
1539
|
-
detect: (context) =>
|
|
1568
|
+
detect: (context) => dependencyEvidence(context, "express", "fastify", "koa", "@hapi/hapi"),
|
|
1540
1569
|
layers: NODE_LAYERS,
|
|
1541
1570
|
kinds: NODE_KINDS,
|
|
1542
1571
|
exclude: ["dist"],
|
|
@@ -1547,7 +1576,7 @@ var JS_PRESETS = [
|
|
|
1547
1576
|
// A bare package.json is often just tooling around a non-JS project, so it
|
|
1548
1577
|
// ranks below every definitive language manifest.
|
|
1549
1578
|
priority: 45,
|
|
1550
|
-
detect: (context) =>
|
|
1579
|
+
detect: (context) => fileEvidence(context, "package.json"),
|
|
1551
1580
|
layers: [
|
|
1552
1581
|
layer("src", "src/"),
|
|
1553
1582
|
layer("lib", "lib/"),
|
|
@@ -1561,13 +1590,12 @@ var JS_PRESETS = [
|
|
|
1561
1590
|
];
|
|
1562
1591
|
|
|
1563
1592
|
// src/presets/lang-presets.ts
|
|
1564
|
-
var anyFileEndingWith = (context, suffix) => Array.from(context.files).some((file) => file.endsWith(suffix));
|
|
1565
1593
|
var DIR = (name) => `${name}/`;
|
|
1566
1594
|
var LANGUAGE_PRESETS = [
|
|
1567
1595
|
{
|
|
1568
1596
|
name: "python-django",
|
|
1569
1597
|
priority: 56,
|
|
1570
|
-
detect: (context) =>
|
|
1598
|
+
detect: (context) => fileEvidence(context, "manage.py") ?? manifestEvidence(context, "pyproject.toml", "django") ?? manifestEvidence(context, "requirements.txt", "django"),
|
|
1571
1599
|
layers: [
|
|
1572
1600
|
layer("views", DIR("views"), DIR("app/views"), DIR("apps/views")),
|
|
1573
1601
|
layer("models", DIR("models"), DIR("app/models"), DIR("apps/models")),
|
|
@@ -1593,7 +1621,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1593
1621
|
{
|
|
1594
1622
|
name: "python-fastapi",
|
|
1595
1623
|
priority: 54,
|
|
1596
|
-
detect: (context) =>
|
|
1624
|
+
detect: (context) => manifestEvidence(context, "requirements.txt", "fastapi") ?? manifestEvidence(context, "requirements.txt", "flask") ?? manifestEvidence(context, "pyproject.toml", "fastapi") ?? manifestEvidence(context, "pyproject.toml", "flask"),
|
|
1597
1625
|
layers: [
|
|
1598
1626
|
layer("routers", DIR("routers"), DIR("routes"), DIR("api")),
|
|
1599
1627
|
layer("models", DIR("models"), DIR("db")),
|
|
@@ -1617,7 +1645,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1617
1645
|
name: "python-lib",
|
|
1618
1646
|
// Below node-lib: a Node repo with a stray .py file is still a Node repo.
|
|
1619
1647
|
priority: 30,
|
|
1620
|
-
detect: (context) =>
|
|
1648
|
+
detect: (context) => fileEvidence(context, "pyproject.toml") ?? fileEvidence(context, "setup.py") ?? fileEvidence(context, "requirements.txt") ?? suffixEvidence(context, ".py"),
|
|
1621
1649
|
layers: [
|
|
1622
1650
|
layer("package", DIR("src"), DIR("lib")),
|
|
1623
1651
|
layer("tests", DIR("tests"), DIR("test")),
|
|
@@ -1630,7 +1658,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1630
1658
|
{
|
|
1631
1659
|
name: "go",
|
|
1632
1660
|
priority: 50,
|
|
1633
|
-
detect: (context) =>
|
|
1661
|
+
detect: (context) => fileEvidence(context, "go.mod"),
|
|
1634
1662
|
layers: [
|
|
1635
1663
|
layer("cmd", "cmd/"),
|
|
1636
1664
|
layer("internal", "internal/"),
|
|
@@ -1654,7 +1682,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1654
1682
|
{
|
|
1655
1683
|
name: "rust",
|
|
1656
1684
|
priority: 48,
|
|
1657
|
-
detect: (context) =>
|
|
1685
|
+
detect: (context) => fileEvidence(context, "Cargo.toml"),
|
|
1658
1686
|
layers: [
|
|
1659
1687
|
layer("src", "src/"),
|
|
1660
1688
|
layer("crates", "crates/"),
|
|
@@ -1670,7 +1698,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1670
1698
|
{
|
|
1671
1699
|
name: "java-spring",
|
|
1672
1700
|
priority: 46,
|
|
1673
|
-
detect: (context) =>
|
|
1701
|
+
detect: (context) => fileEvidence(context, "pom.xml") ?? fileEvidence(context, "build.gradle") ?? fileEvidence(context, "build.gradle.kts"),
|
|
1674
1702
|
layers: [
|
|
1675
1703
|
layer("api", "src/main/java/"),
|
|
1676
1704
|
layer("test", "src/test/java/"),
|
|
@@ -1693,7 +1721,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1693
1721
|
{
|
|
1694
1722
|
name: "dotnet",
|
|
1695
1723
|
priority: 44,
|
|
1696
|
-
detect: (context) =>
|
|
1724
|
+
detect: (context) => suffixEvidence(context, ".csproj") ?? suffixEvidence(context, ".sln") ?? fileEvidence(context, "global.json"),
|
|
1697
1725
|
layers: [
|
|
1698
1726
|
layer("web", "src/", "Web/"),
|
|
1699
1727
|
layer("domain", "Domain/", "Core/"),
|
|
@@ -1716,7 +1744,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1716
1744
|
{
|
|
1717
1745
|
name: "php-laravel",
|
|
1718
1746
|
priority: 42,
|
|
1719
|
-
detect: (context) =>
|
|
1747
|
+
detect: (context) => fileEvidence(context, "artisan") ?? manifestEvidence(context, "composer.json", "laravel") ?? fileEvidence(context, "composer.json"),
|
|
1720
1748
|
layers: [
|
|
1721
1749
|
layer("app", "app/"),
|
|
1722
1750
|
layer("routes", "routes/"),
|
|
@@ -1742,7 +1770,7 @@ var LANGUAGE_PRESETS = [
|
|
|
1742
1770
|
{
|
|
1743
1771
|
name: "ruby-rails",
|
|
1744
1772
|
priority: 40,
|
|
1745
|
-
detect: (context) =>
|
|
1773
|
+
detect: (context) => fileEvidence(context, "Gemfile") ?? fileEvidence(context, "config.ru") ?? fileEvidence(context, "Rakefile"),
|
|
1746
1774
|
layers: [
|
|
1747
1775
|
layer("app", "app/"),
|
|
1748
1776
|
layer("controllers", "app/controllers/"),
|
|
@@ -1815,7 +1843,7 @@ var SHAPE_PRESETS = [
|
|
|
1815
1843
|
{
|
|
1816
1844
|
name: "monorepo",
|
|
1817
1845
|
priority: 70,
|
|
1818
|
-
detect: (context) =>
|
|
1846
|
+
detect: (context) => fileEvidence(context, "pnpm-workspace.yaml") ?? fileEvidence(context, "nx.json") ?? fileEvidence(context, "turbo.json") ?? fileEvidence(context, "lerna.json") ?? manifestEvidence(context, "package.json", '"workspaces"'),
|
|
1819
1847
|
layers: [
|
|
1820
1848
|
layer("apps", "apps/"),
|
|
1821
1849
|
layer("packages", "packages/"),
|
|
@@ -1829,7 +1857,7 @@ var SHAPE_PRESETS = [
|
|
|
1829
1857
|
{
|
|
1830
1858
|
name: "infra",
|
|
1831
1859
|
priority: 24,
|
|
1832
|
-
detect: (context) =>
|
|
1860
|
+
detect: (context) => suffixEvidence(context, ".tf") ?? fileEvidence(context, "Chart.yaml") ?? fileEvidence(context, "kustomization.yaml") ?? fileEvidence(context, "docker-compose.yml") ?? fileEvidence(context, "docker-compose.yaml"),
|
|
1833
1861
|
layers: [
|
|
1834
1862
|
layer("modules", "modules/"),
|
|
1835
1863
|
layer("environments", "env/", "environments/"),
|
|
@@ -1851,7 +1879,7 @@ var SHAPE_PRESETS = [
|
|
|
1851
1879
|
// up to two directories down (bounded in the context builder). Counting
|
|
1852
1880
|
// only the root turned every real documentation repository — README.md plus
|
|
1853
1881
|
// the pages under `docs/` — into `generic`.
|
|
1854
|
-
detect: (context) =>
|
|
1882
|
+
detect: (context) => markdownEvidence(context),
|
|
1855
1883
|
layers: [
|
|
1856
1884
|
layer("content", "content/", "docs/"),
|
|
1857
1885
|
layer("assets", "assets/", "static/", "public/"),
|
|
@@ -1864,7 +1892,10 @@ var SHAPE_PRESETS = [
|
|
|
1864
1892
|
{
|
|
1865
1893
|
name: "generic",
|
|
1866
1894
|
priority: 0,
|
|
1867
|
-
|
|
1895
|
+
// No detector: this is the fallback, and `detectPresetFromContext` reaches it only
|
|
1896
|
+
// when every other preset reported no evidence. `fallback: true` is what lets the
|
|
1897
|
+
// wizard tell "I recognised this" apart from "I gave up".
|
|
1898
|
+
fallback: true,
|
|
1868
1899
|
layers: DEFAULT_LAYERS,
|
|
1869
1900
|
kinds: DEFAULT_KINDS,
|
|
1870
1901
|
exclude: [],
|
|
@@ -1896,20 +1927,27 @@ function detectPresetFromContext(context, forced) {
|
|
|
1896
1927
|
if (forced !== void 0) {
|
|
1897
1928
|
const preset = BY_NAME.get(forced);
|
|
1898
1929
|
if (!preset) throw new Error(unknownPresetMessage(forced));
|
|
1899
|
-
return toDetected(preset);
|
|
1930
|
+
return toDetected(preset, null);
|
|
1900
1931
|
}
|
|
1901
1932
|
for (const preset of PRESETS) {
|
|
1902
|
-
|
|
1933
|
+
const reason = preset.detect?.(context) ?? null;
|
|
1934
|
+
if (reason !== null) return toDetected(preset, reason);
|
|
1935
|
+
}
|
|
1936
|
+
const fallback = PRESETS.find((preset) => preset.fallback === true);
|
|
1937
|
+
if (fallback === void 0) {
|
|
1938
|
+
throw new Error("No fallback preset is defined. Exactly one preset must set `fallback: true`.");
|
|
1903
1939
|
}
|
|
1904
|
-
return toDetected(
|
|
1940
|
+
return toDetected(fallback, null, true);
|
|
1905
1941
|
}
|
|
1906
|
-
function toDetected(preset) {
|
|
1942
|
+
function toDetected(preset, reason, fallback = false) {
|
|
1907
1943
|
return {
|
|
1908
1944
|
presetName: preset.name,
|
|
1909
1945
|
layers: preset.layers,
|
|
1910
1946
|
kinds: preset.kinds,
|
|
1911
1947
|
exclude: preset.exclude ? [...preset.exclude] : [],
|
|
1912
|
-
recommendedBudget: preset.recommendedBudget ?? null
|
|
1948
|
+
recommendedBudget: preset.recommendedBudget ?? null,
|
|
1949
|
+
reason,
|
|
1950
|
+
fallback
|
|
1913
1951
|
};
|
|
1914
1952
|
}
|
|
1915
1953
|
function detectPreset(cwd, forced) {
|
|
@@ -2349,14 +2387,14 @@ var PRESET_IGNORES = new Set(
|
|
|
2349
2387
|
);
|
|
2350
2388
|
function readLinkTarget(linkPath, fallback) {
|
|
2351
2389
|
try {
|
|
2352
|
-
return
|
|
2390
|
+
return fs11__namespace.readlinkSync(linkPath);
|
|
2353
2391
|
} catch {
|
|
2354
2392
|
return fallback;
|
|
2355
2393
|
}
|
|
2356
2394
|
}
|
|
2357
2395
|
function isInside(root, candidate) {
|
|
2358
|
-
const
|
|
2359
|
-
return
|
|
2396
|
+
const relative7 = path12__namespace.relative(root, candidate);
|
|
2397
|
+
return relative7 === "" || !relative7.startsWith(`..${path12__namespace.sep}`) && relative7 !== ".." && !path12__namespace.isAbsolute(relative7);
|
|
2360
2398
|
}
|
|
2361
2399
|
function getGitInfo(cwd, fallbackBranch = "main") {
|
|
2362
2400
|
try {
|
|
@@ -2561,10 +2599,10 @@ function trimUnescapedTrailingSpaces(line) {
|
|
|
2561
2599
|
return line.slice(0, end);
|
|
2562
2600
|
}
|
|
2563
2601
|
function readGitignoreRules(dir) {
|
|
2564
|
-
const gitignorePath =
|
|
2602
|
+
const gitignorePath = path12__namespace.join(dir, ".gitignore");
|
|
2565
2603
|
let raw;
|
|
2566
2604
|
try {
|
|
2567
|
-
raw =
|
|
2605
|
+
raw = fs11__namespace.readFileSync(gitignorePath, "utf8");
|
|
2568
2606
|
} catch {
|
|
2569
2607
|
return [];
|
|
2570
2608
|
}
|
|
@@ -2691,7 +2729,7 @@ function isToolOwnedDirName(name) {
|
|
|
2691
2729
|
}
|
|
2692
2730
|
function hasSnapshotMarker(target) {
|
|
2693
2731
|
try {
|
|
2694
|
-
return
|
|
2732
|
+
return fs11__namespace.statSync(target).isDirectory() && fs11__namespace.existsSync(path12__namespace.join(target, ".codebase-snapshot"));
|
|
2695
2733
|
} catch {
|
|
2696
2734
|
return false;
|
|
2697
2735
|
}
|
|
@@ -2705,7 +2743,7 @@ function makeToolOwnedDecider(cwd, toolOwnedPaths) {
|
|
|
2705
2743
|
const prefix = parts.slice(0, depth).join("/");
|
|
2706
2744
|
let hasMarker = markers.get(prefix);
|
|
2707
2745
|
if (hasMarker === void 0) {
|
|
2708
|
-
hasMarker =
|
|
2746
|
+
hasMarker = fs11__namespace.existsSync(path12__namespace.join(cwd, prefix, ".codebase-snapshot"));
|
|
2709
2747
|
markers.set(prefix, hasMarker);
|
|
2710
2748
|
}
|
|
2711
2749
|
if (hasMarker) return true;
|
|
@@ -2720,7 +2758,7 @@ function makeLinkProbe() {
|
|
|
2720
2758
|
let known = cache.get(absolutePath);
|
|
2721
2759
|
if (known === void 0) {
|
|
2722
2760
|
try {
|
|
2723
|
-
known =
|
|
2761
|
+
known = fs11__namespace.lstatSync(absolutePath).isSymbolicLink();
|
|
2724
2762
|
} catch {
|
|
2725
2763
|
known = false;
|
|
2726
2764
|
}
|
|
@@ -2733,7 +2771,7 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2733
2771
|
const isLink = makeLinkProbe();
|
|
2734
2772
|
let rootReal = cwd;
|
|
2735
2773
|
try {
|
|
2736
|
-
rootReal =
|
|
2774
|
+
rootReal = fs11__namespace.realpathSync(cwd);
|
|
2737
2775
|
} catch {
|
|
2738
2776
|
}
|
|
2739
2777
|
const reported = /* @__PURE__ */ new Set();
|
|
@@ -2743,7 +2781,7 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2743
2781
|
let linkedDir = null;
|
|
2744
2782
|
for (let depth = 1; depth <= parts.length; depth += 1) {
|
|
2745
2783
|
const prefix = parts.slice(0, depth).join("/");
|
|
2746
|
-
if (isLink(
|
|
2784
|
+
if (isLink(path12__namespace.join(cwd, prefix))) {
|
|
2747
2785
|
linkedDir = prefix;
|
|
2748
2786
|
break;
|
|
2749
2787
|
}
|
|
@@ -2754,10 +2792,10 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
2754
2792
|
}
|
|
2755
2793
|
if (reported.has(linkedDir)) continue;
|
|
2756
2794
|
reported.add(linkedDir);
|
|
2757
|
-
const absolute =
|
|
2795
|
+
const absolute = path12__namespace.join(cwd, linkedDir);
|
|
2758
2796
|
let targetReal = absolute;
|
|
2759
2797
|
try {
|
|
2760
|
-
targetReal =
|
|
2798
|
+
targetReal = fs11__namespace.realpathSync(absolute);
|
|
2761
2799
|
} catch {
|
|
2762
2800
|
}
|
|
2763
2801
|
const target = readLinkTarget(absolute, targetReal);
|
|
@@ -2834,7 +2872,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2834
2872
|
"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."
|
|
2835
2873
|
);
|
|
2836
2874
|
}
|
|
2837
|
-
if (
|
|
2875
|
+
if (fs11__namespace.existsSync(path12__namespace.join(cwd, ".git"))) {
|
|
2838
2876
|
throw new Error(
|
|
2839
2877
|
"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."
|
|
2840
2878
|
);
|
|
@@ -2893,7 +2931,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2893
2931
|
}
|
|
2894
2932
|
function rootRealFor(root) {
|
|
2895
2933
|
try {
|
|
2896
|
-
return
|
|
2934
|
+
return fs11__namespace.realpathSync(root);
|
|
2897
2935
|
} catch {
|
|
2898
2936
|
return root;
|
|
2899
2937
|
}
|
|
@@ -2901,18 +2939,18 @@ function rootRealFor(root) {
|
|
|
2901
2939
|
function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PURE__ */ new Set(), parentGitignoreLayers = []) {
|
|
2902
2940
|
const results = { files: [], skipped: [] };
|
|
2903
2941
|
const stack = [];
|
|
2904
|
-
const relativeToBase = (target) =>
|
|
2942
|
+
const relativeToBase = (target) => path12__namespace.relative(baseDir, target).replace(/\\/g, "/") || ".";
|
|
2905
2943
|
const enter = (current, layers) => {
|
|
2906
2944
|
if (visited.has(current)) return false;
|
|
2907
2945
|
visited.add(current);
|
|
2908
2946
|
const localRules = readGitignoreRules(current);
|
|
2909
2947
|
const gitignoreLayers = localRules.length === 0 ? layers : [
|
|
2910
2948
|
...layers,
|
|
2911
|
-
{ prefix:
|
|
2949
|
+
{ prefix: path12__namespace.relative(baseDir, current).replace(/\\/g, "/"), rules: localRules }
|
|
2912
2950
|
];
|
|
2913
2951
|
let entries;
|
|
2914
2952
|
try {
|
|
2915
|
-
entries =
|
|
2953
|
+
entries = fs11__namespace.readdirSync(current, { withFileTypes: true });
|
|
2916
2954
|
} catch {
|
|
2917
2955
|
results.skipped.push({
|
|
2918
2956
|
path: relativeToBase(current),
|
|
@@ -2934,8 +2972,8 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
2934
2972
|
}
|
|
2935
2973
|
const entry = frame.entries[frame.index];
|
|
2936
2974
|
frame.index += 1;
|
|
2937
|
-
const fullPath =
|
|
2938
|
-
const relPath =
|
|
2975
|
+
const fullPath = path12__namespace.join(frame.dir, entry.name);
|
|
2976
|
+
const relPath = path12__namespace.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
2939
2977
|
if (isToolOwnedDirName(entry.name) && reservedPaths.has(relPath)) continue;
|
|
2940
2978
|
if (DEFAULT_IGNORED_DIRS.includes(entry.name)) {
|
|
2941
2979
|
results.skipped.push({
|
|
@@ -2968,14 +3006,14 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
2968
3006
|
if (entry.isSymbolicLink()) {
|
|
2969
3007
|
let targetReal;
|
|
2970
3008
|
try {
|
|
2971
|
-
targetReal =
|
|
3009
|
+
targetReal = fs11__namespace.realpathSync(fullPath);
|
|
2972
3010
|
} catch {
|
|
2973
3011
|
results.skipped.push({ path: relPath, reason: "read-error", detail: "broken symlink" });
|
|
2974
3012
|
continue;
|
|
2975
3013
|
}
|
|
2976
3014
|
let stats;
|
|
2977
3015
|
try {
|
|
2978
|
-
stats =
|
|
3016
|
+
stats = fs11__namespace.statSync(fullPath);
|
|
2979
3017
|
} catch {
|
|
2980
3018
|
results.skipped.push({ path: relPath, reason: "read-error", detail: "unreadable symlink" });
|
|
2981
3019
|
continue;
|
|
@@ -3273,11 +3311,11 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3273
3311
|
const warnings = [];
|
|
3274
3312
|
const errors = [];
|
|
3275
3313
|
for (const filename of CONFIG_FILENAMES) {
|
|
3276
|
-
const fullPath =
|
|
3277
|
-
if (!
|
|
3314
|
+
const fullPath = path12__namespace.join(cwd, filename);
|
|
3315
|
+
if (!fs11__namespace.existsSync(fullPath)) continue;
|
|
3278
3316
|
if (filename.endsWith(".json") || filename === ".snapshotrc") {
|
|
3279
3317
|
try {
|
|
3280
|
-
const config = parseJsonish(
|
|
3318
|
+
const config = parseJsonish(fs11__namespace.readFileSync(fullPath, "utf8"), fullPath);
|
|
3281
3319
|
if (config === null || typeof config !== "object" || Array.isArray(config)) {
|
|
3282
3320
|
throw new Error(
|
|
3283
3321
|
`${fullPath} does not hold a configuration object (received ${describeValue2(config)}).`
|
|
@@ -3311,10 +3349,10 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3311
3349
|
continue;
|
|
3312
3350
|
}
|
|
3313
3351
|
}
|
|
3314
|
-
const pkgJsonPath =
|
|
3315
|
-
if (
|
|
3352
|
+
const pkgJsonPath = path12__namespace.join(cwd, "package.json");
|
|
3353
|
+
if (fs11__namespace.existsSync(pkgJsonPath)) {
|
|
3316
3354
|
try {
|
|
3317
|
-
const parsed = parseJsonish(
|
|
3355
|
+
const parsed = parseJsonish(fs11__namespace.readFileSync(pkgJsonPath, "utf8"), pkgJsonPath);
|
|
3318
3356
|
const snapshotKey = parsed.snapshot;
|
|
3319
3357
|
if (snapshotKey && typeof snapshotKey === "object" && !Array.isArray(snapshotKey)) {
|
|
3320
3358
|
return { config: snapshotKey, file: pkgJsonPath, warnings, errors };
|
|
@@ -3337,7 +3375,7 @@ async function loadConfigFile(cwd) {
|
|
|
3337
3375
|
const loaded = await loadConfigFileDetailed(cwd);
|
|
3338
3376
|
if (loaded.config && loaded.file && typeof loaded.config === "object") {
|
|
3339
3377
|
Object.defineProperty(loaded.config, CONFIG_DIR_SYMBOL, {
|
|
3340
|
-
value:
|
|
3378
|
+
value: path12__namespace.dirname(loaded.file),
|
|
3341
3379
|
enumerable: false,
|
|
3342
3380
|
configurable: true
|
|
3343
3381
|
});
|
|
@@ -3496,12 +3534,12 @@ function resolveKindRules(value) {
|
|
|
3496
3534
|
function outputExcludePatterns(cwd, outDir, tmpDir) {
|
|
3497
3535
|
const patterns = [];
|
|
3498
3536
|
for (const dir of [outDir, tmpDir]) {
|
|
3499
|
-
const absolute =
|
|
3500
|
-
const
|
|
3501
|
-
if (!
|
|
3537
|
+
const absolute = path12__namespace.resolve(cwd, dir);
|
|
3538
|
+
const relative7 = path12__namespace.relative(cwd, absolute);
|
|
3539
|
+
if (!relative7 || relative7 === ".." || relative7.startsWith(`..${path12__namespace.sep}`) || path12__namespace.isAbsolute(relative7)) {
|
|
3502
3540
|
continue;
|
|
3503
3541
|
}
|
|
3504
|
-
patterns.push(`${
|
|
3542
|
+
patterns.push(`${relative7.replace(/\\/g, "/")}/**`);
|
|
3505
3543
|
}
|
|
3506
3544
|
return patterns;
|
|
3507
3545
|
}
|
|
@@ -3617,11 +3655,11 @@ function resolveCandidateCwd(userConfig, cliOptions, baseDir) {
|
|
|
3617
3655
|
);
|
|
3618
3656
|
}
|
|
3619
3657
|
const candidate = cliOptions?.cwd || userConfig?.cwd || baseDir || process.cwd();
|
|
3620
|
-
const resolved =
|
|
3621
|
-
if (!
|
|
3658
|
+
const resolved = path12__namespace.resolve(candidate);
|
|
3659
|
+
if (!fs11__namespace.existsSync(resolved)) {
|
|
3622
3660
|
throw new Error(`Working directory does not exist: ${resolved}`);
|
|
3623
3661
|
}
|
|
3624
|
-
if (!
|
|
3662
|
+
if (!fs11__namespace.statSync(resolved).isDirectory()) {
|
|
3625
3663
|
throw new Error(`Working directory is not a directory: ${resolved}`);
|
|
3626
3664
|
}
|
|
3627
3665
|
return resolved;
|
|
@@ -4645,7 +4683,7 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
4645
4683
|
".so"
|
|
4646
4684
|
]);
|
|
4647
4685
|
function isBinaryExtension(filePath) {
|
|
4648
|
-
const ext =
|
|
4686
|
+
const ext = path12__namespace.extname(filePath).toLowerCase();
|
|
4649
4687
|
return BINARY_EXTENSIONS.has(ext);
|
|
4650
4688
|
}
|
|
4651
4689
|
function isBinaryBuffer(buffer) {
|
|
@@ -4815,13 +4853,13 @@ function isBinaryFile(absolutePath) {
|
|
|
4815
4853
|
if (isBinaryExtension(absolutePath)) return true;
|
|
4816
4854
|
let fd;
|
|
4817
4855
|
try {
|
|
4818
|
-
fd =
|
|
4856
|
+
fd = fs11__namespace.openSync(absolutePath, "r");
|
|
4819
4857
|
const buffer = Buffer.alloc(CHUNK);
|
|
4820
4858
|
let sample;
|
|
4821
4859
|
let position = 0;
|
|
4822
4860
|
while (position < BINARY_SCAN_BYTES) {
|
|
4823
4861
|
const wanted = Math.min(CHUNK, BINARY_SCAN_BYTES - position);
|
|
4824
|
-
const bytesRead =
|
|
4862
|
+
const bytesRead = fs11__namespace.readSync(fd, buffer, 0, wanted, position);
|
|
4825
4863
|
if (bytesRead === 0) return false;
|
|
4826
4864
|
const chunk = buffer.subarray(0, bytesRead);
|
|
4827
4865
|
if (sample === void 0) {
|
|
@@ -4839,7 +4877,7 @@ function isBinaryFile(absolutePath) {
|
|
|
4839
4877
|
} finally {
|
|
4840
4878
|
if (fd !== void 0) {
|
|
4841
4879
|
try {
|
|
4842
|
-
|
|
4880
|
+
fs11__namespace.closeSync(fd);
|
|
4843
4881
|
} catch {
|
|
4844
4882
|
}
|
|
4845
4883
|
}
|
|
@@ -4899,10 +4937,10 @@ var LANGUAGE_BY_BASENAME = {
|
|
|
4899
4937
|
};
|
|
4900
4938
|
function detectLanguage(filePath) {
|
|
4901
4939
|
const normalized = filePath.replace(/\\/g, "/");
|
|
4902
|
-
const base =
|
|
4940
|
+
const base = path12__namespace.posix.basename(normalized);
|
|
4903
4941
|
if (LANGUAGE_BY_BASENAME[base]) return LANGUAGE_BY_BASENAME[base];
|
|
4904
4942
|
if (base === ".env" || base.startsWith(".env.")) return "dotenv";
|
|
4905
|
-
const ext =
|
|
4943
|
+
const ext = path12__namespace.posix.extname(base).toLowerCase();
|
|
4906
4944
|
return LANGUAGE_BY_EXTENSION[ext] ?? "text";
|
|
4907
4945
|
}
|
|
4908
4946
|
function maskedPath(value, secretRules) {
|
|
@@ -4910,33 +4948,33 @@ function maskedPath(value, secretRules) {
|
|
|
4910
4948
|
}
|
|
4911
4949
|
function confineToDirectory(directory, filename) {
|
|
4912
4950
|
if (filename.includes("\0")) return null;
|
|
4913
|
-
if (
|
|
4914
|
-
const base =
|
|
4915
|
-
const resolved =
|
|
4951
|
+
if (path12__namespace.isAbsolute(filename)) return null;
|
|
4952
|
+
const base = path12__namespace.resolve(directory);
|
|
4953
|
+
const resolved = path12__namespace.resolve(base, filename);
|
|
4916
4954
|
const caseInsensitive = process.platform === "win32" || process.platform === "darwin";
|
|
4917
|
-
const prefix = caseInsensitive ? `${base}${
|
|
4955
|
+
const prefix = caseInsensitive ? `${base}${path12__namespace.sep}`.toLowerCase() : `${base}${path12__namespace.sep}`;
|
|
4918
4956
|
const candidate = caseInsensitive ? resolved.toLowerCase() : resolved;
|
|
4919
4957
|
return candidate.startsWith(prefix) ? resolved : null;
|
|
4920
4958
|
}
|
|
4921
4959
|
function findStagingDirectory(config) {
|
|
4922
|
-
const tmpDir =
|
|
4960
|
+
const tmpDir = path12__namespace.resolve(config.cwd, config.tmpDir);
|
|
4923
4961
|
let entries;
|
|
4924
4962
|
try {
|
|
4925
|
-
entries =
|
|
4963
|
+
entries = fs11__namespace.readdirSync(tmpDir);
|
|
4926
4964
|
} catch {
|
|
4927
4965
|
return null;
|
|
4928
4966
|
}
|
|
4929
4967
|
const runs = entries.filter((name) => name.startsWith("run-")).sort();
|
|
4930
4968
|
const newest = runs[runs.length - 1];
|
|
4931
|
-
return newest ?
|
|
4969
|
+
return newest ? path12__namespace.join(tmpDir, newest) : null;
|
|
4932
4970
|
}
|
|
4933
4971
|
function describeMissingManifest(config, manifestPath) {
|
|
4934
|
-
const outDir =
|
|
4935
|
-
const markerPath =
|
|
4972
|
+
const outDir = path12__namespace.resolve(config.cwd, config.outDir);
|
|
4973
|
+
const markerPath = path12__namespace.join(outDir, ".codebase-snapshot");
|
|
4936
4974
|
let marker2 = null;
|
|
4937
4975
|
try {
|
|
4938
|
-
if (
|
|
4939
|
-
const parsed = JSON.parse(
|
|
4976
|
+
if (fs11__namespace.existsSync(markerPath)) {
|
|
4977
|
+
const parsed = JSON.parse(fs11__namespace.readFileSync(markerPath, "utf8"));
|
|
4940
4978
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
4941
4979
|
marker2 = parsed;
|
|
4942
4980
|
}
|
|
@@ -4972,14 +5010,14 @@ function sha256Hex(buffer) {
|
|
|
4972
5010
|
}
|
|
4973
5011
|
var VERIFY_SNIFF_MAX_BYTES = 8 * 1024 * 1024;
|
|
4974
5012
|
function wouldNowBeIncluded(cwd, relativePath, reason) {
|
|
4975
|
-
const absolutePath =
|
|
5013
|
+
const absolutePath = path12__namespace.join(cwd, relativePath);
|
|
4976
5014
|
try {
|
|
4977
|
-
const stats =
|
|
5015
|
+
const stats = fs11__namespace.statSync(absolutePath);
|
|
4978
5016
|
if (!stats.isFile()) return false;
|
|
4979
5017
|
if (isBinaryFile(absolutePath)) return false;
|
|
4980
5018
|
if (reason !== "binary") return true;
|
|
4981
5019
|
if (stats.size > VERIFY_SNIFF_MAX_BYTES) return false;
|
|
4982
|
-
return !decodeText(
|
|
5020
|
+
return !decodeText(fs11__namespace.readFileSync(absolutePath)).binary;
|
|
4983
5021
|
} catch {
|
|
4984
5022
|
return false;
|
|
4985
5023
|
}
|
|
@@ -5037,8 +5075,8 @@ function describeManifestShapeProblem(value) {
|
|
|
5037
5075
|
function validateSnapshot(configInput) {
|
|
5038
5076
|
assertUsableConfigInput(configInput);
|
|
5039
5077
|
const config = isResolvedConfig(configInput) ? normalizeResolvedConfig(configInput) : resolveConfig(configInput);
|
|
5040
|
-
const manifestPath =
|
|
5041
|
-
if (!
|
|
5078
|
+
const manifestPath = path12__namespace.resolve(config.cwd, config.outDir, "manifest.json");
|
|
5079
|
+
if (!fs11__namespace.existsSync(manifestPath)) {
|
|
5042
5080
|
return {
|
|
5043
5081
|
valid: false,
|
|
5044
5082
|
manifestPresent: false,
|
|
@@ -5049,7 +5087,7 @@ function validateSnapshot(configInput) {
|
|
|
5049
5087
|
}
|
|
5050
5088
|
let manifest;
|
|
5051
5089
|
try {
|
|
5052
|
-
manifest = JSON.parse(
|
|
5090
|
+
manifest = JSON.parse(fs11__namespace.readFileSync(manifestPath, "utf8"));
|
|
5053
5091
|
} catch (err) {
|
|
5054
5092
|
return {
|
|
5055
5093
|
valid: false,
|
|
@@ -5071,7 +5109,7 @@ function validateSnapshot(configInput) {
|
|
|
5071
5109
|
}
|
|
5072
5110
|
const drift = emptyDrift();
|
|
5073
5111
|
const warnings = [];
|
|
5074
|
-
const snapshotDir =
|
|
5112
|
+
const snapshotDir = path12__namespace.resolve(config.cwd, config.outDir);
|
|
5075
5113
|
const manifestFileMap = /* @__PURE__ */ new Map();
|
|
5076
5114
|
for (const file of manifest.files) {
|
|
5077
5115
|
if (file && typeof file.relativePath === "string") {
|
|
@@ -5105,7 +5143,7 @@ function validateSnapshot(configInput) {
|
|
|
5105
5143
|
}
|
|
5106
5144
|
let diskHash;
|
|
5107
5145
|
try {
|
|
5108
|
-
diskHash = sha256Hex(
|
|
5146
|
+
diskHash = sha256Hex(fs11__namespace.readFileSync(path12__namespace.join(config.cwd, diskFile)));
|
|
5109
5147
|
} catch (err) {
|
|
5110
5148
|
if (err.code === "ENOENT") {
|
|
5111
5149
|
drift.missingOnDisk.push(diskFile);
|
|
@@ -5129,7 +5167,7 @@ function validateSnapshot(configInput) {
|
|
|
5129
5167
|
}
|
|
5130
5168
|
let statFailure = null;
|
|
5131
5169
|
try {
|
|
5132
|
-
|
|
5170
|
+
fs11__namespace.statSync(confined);
|
|
5133
5171
|
} catch (err) {
|
|
5134
5172
|
statFailure = err;
|
|
5135
5173
|
}
|
|
@@ -5149,7 +5187,7 @@ function validateSnapshot(configInput) {
|
|
|
5149
5187
|
...Object.values(config.budgets.byLayer)
|
|
5150
5188
|
);
|
|
5151
5189
|
const partNames = /* @__PURE__ */ new Set();
|
|
5152
|
-
const partsDir =
|
|
5190
|
+
const partsDir = path12__namespace.join(snapshotDir, "parts");
|
|
5153
5191
|
for (const part of manifest.parts) {
|
|
5154
5192
|
if (!part || typeof part.filename !== "string") continue;
|
|
5155
5193
|
partNames.add(part.filename);
|
|
@@ -5164,7 +5202,7 @@ function validateSnapshot(configInput) {
|
|
|
5164
5202
|
}
|
|
5165
5203
|
let buffer;
|
|
5166
5204
|
try {
|
|
5167
|
-
buffer =
|
|
5205
|
+
buffer = fs11__namespace.readFileSync(partPath);
|
|
5168
5206
|
} catch {
|
|
5169
5207
|
drift.partIssues.push({ part: part.filename, issue: "missing from snapshot/parts" });
|
|
5170
5208
|
continue;
|
|
@@ -5378,9 +5416,9 @@ function validateSnapshot(configInput) {
|
|
|
5378
5416
|
["full-codebase.md", manifest.artifacts?.fullMarkdown]
|
|
5379
5417
|
]) {
|
|
5380
5418
|
if (!artifact) continue;
|
|
5381
|
-
const artifactPath =
|
|
5419
|
+
const artifactPath = path12__namespace.join(snapshotDir, key);
|
|
5382
5420
|
try {
|
|
5383
|
-
const buffer =
|
|
5421
|
+
const buffer = fs11__namespace.readFileSync(artifactPath);
|
|
5384
5422
|
if (sha256Hex(buffer) !== artifact.sha256 || buffer.length !== artifact.byteSize) {
|
|
5385
5423
|
drift.partIssues.push({ part: key, issue: "artifact does not match its recorded digest" });
|
|
5386
5424
|
}
|
|
@@ -5450,7 +5488,7 @@ function validateSnapshot(configInput) {
|
|
|
5450
5488
|
|
|
5451
5489
|
// src/version.ts
|
|
5452
5490
|
var GENERATOR_NAME = "codebase-snapshot";
|
|
5453
|
-
var GENERATOR_VERSION = "0.9.
|
|
5491
|
+
var GENERATOR_VERSION = "0.9.5";
|
|
5454
5492
|
var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
|
|
5455
5493
|
function isOurGeneratorName(name) {
|
|
5456
5494
|
if (typeof name !== "string") return false;
|
|
@@ -5465,42 +5503,42 @@ function describeMatcher(matcher) {
|
|
|
5465
5503
|
return String(matcher);
|
|
5466
5504
|
}
|
|
5467
5505
|
function normalizeTarget(cwd, target) {
|
|
5468
|
-
const absolute =
|
|
5469
|
-
const
|
|
5470
|
-
if (
|
|
5471
|
-
return
|
|
5506
|
+
const absolute = path12__namespace.resolve(cwd, target);
|
|
5507
|
+
const relative7 = path12__namespace.relative(cwd, absolute);
|
|
5508
|
+
if (relative7.startsWith("..") || path12__namespace.isAbsolute(relative7)) return target;
|
|
5509
|
+
return relative7.split(path12__namespace.sep).join("/");
|
|
5472
5510
|
}
|
|
5473
5511
|
function findRuleMatch(rules, relativePath) {
|
|
5474
5512
|
return rules.find(
|
|
5475
5513
|
(rule) => matchRule(relativePath, rule.match)
|
|
5476
5514
|
);
|
|
5477
5515
|
}
|
|
5478
|
-
function findRecordedAncestor(skipped,
|
|
5516
|
+
function findRecordedAncestor(skipped, relative7, maskedPath2) {
|
|
5479
5517
|
let nearest;
|
|
5480
5518
|
for (const record of skipped) {
|
|
5481
5519
|
const ancestorPath = record.path;
|
|
5482
5520
|
if (ancestorPath.length === 0) continue;
|
|
5483
|
-
const isAncestor =
|
|
5521
|
+
const isAncestor = relative7.startsWith(`${ancestorPath}/`) || maskedPath2.startsWith(`${ancestorPath}/`);
|
|
5484
5522
|
if (!isAncestor) continue;
|
|
5485
5523
|
if (!nearest || ancestorPath.length > nearest.path.length) nearest = record;
|
|
5486
5524
|
}
|
|
5487
5525
|
return nearest;
|
|
5488
5526
|
}
|
|
5489
5527
|
function readManifest2(config) {
|
|
5490
|
-
const manifestPath =
|
|
5528
|
+
const manifestPath = path12__namespace.resolve(config.cwd, config.outDir, "manifest.json");
|
|
5491
5529
|
try {
|
|
5492
|
-
return JSON.parse(
|
|
5530
|
+
return JSON.parse(fs11__namespace.readFileSync(manifestPath, "utf8"));
|
|
5493
5531
|
} catch {
|
|
5494
5532
|
return null;
|
|
5495
5533
|
}
|
|
5496
5534
|
}
|
|
5497
5535
|
function explainPath(config, target) {
|
|
5498
|
-
const
|
|
5499
|
-
const absolute =
|
|
5500
|
-
const maskedPath2 = findSecretInPath(
|
|
5536
|
+
const relative7 = normalizeTarget(config.cwd, target);
|
|
5537
|
+
const absolute = path12__namespace.join(config.cwd, relative7);
|
|
5538
|
+
const maskedPath2 = findSecretInPath(relative7, config.secretRules)?.masked ?? relative7;
|
|
5501
5539
|
const result = {
|
|
5502
5540
|
path: maskedPath2,
|
|
5503
|
-
exists:
|
|
5541
|
+
exists: fs11__namespace.existsSync(absolute),
|
|
5504
5542
|
included: false
|
|
5505
5543
|
};
|
|
5506
5544
|
const collection = collectFiles(config.cwd, {
|
|
@@ -5511,12 +5549,12 @@ function explainPath(config, target) {
|
|
|
5511
5549
|
// Same exclusion `generate` applies, so `explain` never says "not-discovered" about a file
|
|
5512
5550
|
// that is only missing because the walker dropped the tool's own output directory.
|
|
5513
5551
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
5514
|
-
(directory) =>
|
|
5515
|
-
).filter((
|
|
5552
|
+
(directory) => path12__namespace.relative(config.cwd, path12__namespace.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
5553
|
+
).filter((relative8) => relative8.length > 0 && !relative8.startsWith(".."))
|
|
5516
5554
|
});
|
|
5517
|
-
if (!collection.files.includes(
|
|
5555
|
+
if (!collection.files.includes(relative7)) {
|
|
5518
5556
|
const record = collection.skipped.find(
|
|
5519
|
-
(entry2) => entry2.path ===
|
|
5557
|
+
(entry2) => entry2.path === relative7 || entry2.path === maskedPath2
|
|
5520
5558
|
);
|
|
5521
5559
|
if (record) {
|
|
5522
5560
|
result.reason = record.reason;
|
|
@@ -5525,7 +5563,7 @@ function explainPath(config, target) {
|
|
|
5525
5563
|
result.reason = "missing";
|
|
5526
5564
|
result.detail = "no such file or directory, relative to the configured cwd";
|
|
5527
5565
|
} else {
|
|
5528
|
-
const ancestor = findRecordedAncestor(collection.skipped,
|
|
5566
|
+
const ancestor = findRecordedAncestor(collection.skipped, relative7, maskedPath2);
|
|
5529
5567
|
if (ancestor) {
|
|
5530
5568
|
result.reason = ancestor.reason;
|
|
5531
5569
|
result.detail = ancestor.detail ? `${ancestor.detail} \u2014 inherited from the skipped directory \`${ancestor.path}\`` : `inside the skipped directory \`${ancestor.path}\``;
|
|
@@ -5543,15 +5581,15 @@ function explainPath(config, target) {
|
|
|
5543
5581
|
result.detail = "binary content; generate skips it and records `binary` in manifest.skipped[]";
|
|
5544
5582
|
return result;
|
|
5545
5583
|
}
|
|
5546
|
-
result.layer = classifyLayer(
|
|
5547
|
-
result.kind = classifyKind(
|
|
5548
|
-
result.language = detectLanguage(
|
|
5549
|
-
const layerRule = findRuleMatch(config.layers,
|
|
5584
|
+
result.layer = classifyLayer(relative7, config.layers);
|
|
5585
|
+
result.kind = classifyKind(relative7, config.kinds);
|
|
5586
|
+
result.language = detectLanguage(relative7);
|
|
5587
|
+
const layerRule = findRuleMatch(config.layers, relative7);
|
|
5550
5588
|
if (layerRule) result.layerMatch = describeMatcher(layerRule.match);
|
|
5551
|
-
const kindRule = findRuleMatch(config.kinds,
|
|
5589
|
+
const kindRule = findRuleMatch(config.kinds, relative7);
|
|
5552
5590
|
if (kindRule) result.kindMatch = describeMatcher(kindRule.match);
|
|
5553
|
-
if (
|
|
5554
|
-
const buffer =
|
|
5591
|
+
if (fs11__namespace.existsSync(absolute) && fs11__namespace.statSync(absolute).isFile()) {
|
|
5592
|
+
const buffer = fs11__namespace.readFileSync(absolute);
|
|
5555
5593
|
const decoded = decodeText(buffer);
|
|
5556
5594
|
if (decoded.binary) {
|
|
5557
5595
|
result.included = false;
|
|
@@ -5563,12 +5601,12 @@ function explainPath(config, target) {
|
|
|
5563
5601
|
result.bytes = buffer.length;
|
|
5564
5602
|
result.lines = countLines(text2);
|
|
5565
5603
|
if (decoded.encoding !== "utf-8") result.encoding = decoded.encoding;
|
|
5566
|
-
const { findings } = scanAndRedactSecrets(text2, config.secretRules,
|
|
5604
|
+
const { findings } = scanAndRedactSecrets(text2, config.secretRules, relative7);
|
|
5567
5605
|
result.secretCount = findings.length;
|
|
5568
5606
|
result.secrets = findings.slice(0, 20).map((finding) => ({ rules: finding.rules, line: finding.line }));
|
|
5569
5607
|
}
|
|
5570
5608
|
const manifest = readManifest2(config);
|
|
5571
|
-
const entry = Array.isArray(manifest?.files) ? manifest.files.find((file) => file.relativePath ===
|
|
5609
|
+
const entry = Array.isArray(manifest?.files) ? manifest.files.find((file) => file.relativePath === relative7) : void 0;
|
|
5572
5610
|
result.part = entry?.part ?? null;
|
|
5573
5611
|
result.inclusionMode = entry?.inclusionMode;
|
|
5574
5612
|
return result;
|
|
@@ -5799,14 +5837,14 @@ function compareManifests(before, after) {
|
|
|
5799
5837
|
function loadManifest(target) {
|
|
5800
5838
|
let isDirectory;
|
|
5801
5839
|
try {
|
|
5802
|
-
isDirectory =
|
|
5840
|
+
isDirectory = fs11__namespace.statSync(target).isDirectory();
|
|
5803
5841
|
} catch {
|
|
5804
5842
|
throw new Error(`No snapshot found at ${target}: the path does not exist.`);
|
|
5805
5843
|
}
|
|
5806
|
-
const manifestPath = isDirectory ?
|
|
5844
|
+
const manifestPath = isDirectory ? path12__namespace.join(target, "manifest.json") : target;
|
|
5807
5845
|
let raw;
|
|
5808
5846
|
try {
|
|
5809
|
-
raw =
|
|
5847
|
+
raw = fs11__namespace.readFileSync(manifestPath, "utf8");
|
|
5810
5848
|
} catch {
|
|
5811
5849
|
throw new Error(`Could not read a manifest at ${manifestPath}.`);
|
|
5812
5850
|
}
|
|
@@ -5863,15 +5901,15 @@ function renderDiffText(diff) {
|
|
|
5863
5901
|
function appendGitignoreEntries(cwd, entries) {
|
|
5864
5902
|
const wanted = [...new Set(entries)].filter((entry) => entry.trim().length > 0);
|
|
5865
5903
|
if (wanted.length === 0) return { added: [], note: null };
|
|
5866
|
-
const ignorePath =
|
|
5904
|
+
const ignorePath = path12__namespace.join(cwd, ".gitignore");
|
|
5867
5905
|
const manual = `Add ${wanted.map((entry) => `"${entry}"`).join(" and ")} to your .gitignore yourself.`;
|
|
5868
5906
|
let existing = "";
|
|
5869
5907
|
try {
|
|
5870
|
-
if (
|
|
5871
|
-
if (
|
|
5908
|
+
if (fs11__namespace.existsSync(ignorePath)) {
|
|
5909
|
+
if (fs11__namespace.statSync(ignorePath).isDirectory()) {
|
|
5872
5910
|
return { added: [], note: `${ignorePath} is a directory, so it was not updated. ${manual}` };
|
|
5873
5911
|
}
|
|
5874
|
-
existing =
|
|
5912
|
+
existing = fs11__namespace.readFileSync(ignorePath, "utf8");
|
|
5875
5913
|
}
|
|
5876
5914
|
} catch (err) {
|
|
5877
5915
|
return {
|
|
@@ -5884,7 +5922,7 @@ function appendGitignoreEntries(cwd, entries) {
|
|
|
5884
5922
|
if (added.length === 0) return { added: [], note: null };
|
|
5885
5923
|
const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
5886
5924
|
try {
|
|
5887
|
-
|
|
5925
|
+
fs11__namespace.writeFileSync(ignorePath, `${existing}${prefix}${added.join("\n")}
|
|
5888
5926
|
`, "utf8");
|
|
5889
5927
|
} catch (err) {
|
|
5890
5928
|
return { added: [], note: `${err.message}. ${manual}` };
|
|
@@ -6023,6 +6061,12 @@ var PromptCancelled = class extends Error {
|
|
|
6023
6061
|
this.name = "PromptCancelled";
|
|
6024
6062
|
}
|
|
6025
6063
|
};
|
|
6064
|
+
var PromptBack = class extends Error {
|
|
6065
|
+
constructor() {
|
|
6066
|
+
super("Prompt back");
|
|
6067
|
+
this.name = "PromptBack";
|
|
6068
|
+
}
|
|
6069
|
+
};
|
|
6026
6070
|
function defaultIO() {
|
|
6027
6071
|
return { input: process.stdin, output: process.stdout };
|
|
6028
6072
|
}
|
|
@@ -6046,35 +6090,40 @@ function choiceLine(choice, active) {
|
|
|
6046
6090
|
const hint = choice.hint ? ` ${choice.hint}` : "";
|
|
6047
6091
|
return ` ${marker(active)} ${choice.label}${hint}`;
|
|
6048
6092
|
}
|
|
6049
|
-
function
|
|
6093
|
+
function listFooter(base, allowBack) {
|
|
6094
|
+
return allowBack ? `${base} \xB7 \u2190 back` : base;
|
|
6095
|
+
}
|
|
6096
|
+
function renderSelect(message, choices, index, allowBack = false) {
|
|
6050
6097
|
const lines = [`\u25C6 ${message}`, ""];
|
|
6051
6098
|
choices.forEach((choice, position) => lines.push(choiceLine(choice, position === index)));
|
|
6052
|
-
lines.push("", "
|
|
6099
|
+
lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 enter select", allowBack)}`);
|
|
6053
6100
|
return lines.join("\n");
|
|
6054
6101
|
}
|
|
6055
|
-
function renderMultiSelect(message, choices, index, selected) {
|
|
6102
|
+
function renderMultiSelect(message, choices, index, selected, allowBack = false) {
|
|
6056
6103
|
const lines = [`\u25C6 ${message}`, ""];
|
|
6057
6104
|
choices.forEach((choice, position) => {
|
|
6058
6105
|
const box = selected.has(position) ? "\u25FC" : "\u25FB";
|
|
6059
6106
|
const hint = choice.hint ? ` ${choice.hint}` : "";
|
|
6060
6107
|
lines.push(` ${marker(position === index)} ${box} ${choice.label}${hint}`);
|
|
6061
6108
|
});
|
|
6062
|
-
lines.push("", "
|
|
6109
|
+
lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 space toggle \xB7 enter confirm", allowBack)}`);
|
|
6063
6110
|
return lines.join("\n");
|
|
6064
6111
|
}
|
|
6065
|
-
function renderConfirm(message, value) {
|
|
6112
|
+
function renderConfirm(message, value, allowBack = false) {
|
|
6113
|
+
const footer = allowBack ? "\u2190\u2192 or y/n \xB7 enter confirm \xB7 b back" : "\u2190\u2192 or y/n \xB7 enter confirm";
|
|
6066
6114
|
return [
|
|
6067
6115
|
`\u25C6 ${message}`,
|
|
6068
6116
|
"",
|
|
6069
6117
|
` ${marker(value)} Yes`,
|
|
6070
6118
|
` ${marker(!value)} No`,
|
|
6071
6119
|
"",
|
|
6072
|
-
|
|
6120
|
+
` ${footer}`
|
|
6073
6121
|
].join("\n");
|
|
6074
6122
|
}
|
|
6075
|
-
function renderText(message, value, error) {
|
|
6123
|
+
function renderText(message, value, error, allowBack = false) {
|
|
6076
6124
|
const lines = [`\u25C6 ${message}`, "", ` ${value || "\u258F"}`];
|
|
6077
6125
|
if (error) lines.push("", ` \u2716 ${error}`);
|
|
6126
|
+
if (allowBack) lines.push("", " enter confirm \xB7 \u2190 back");
|
|
6078
6127
|
return lines.join("\n");
|
|
6079
6128
|
}
|
|
6080
6129
|
function keyReader(input) {
|
|
@@ -6123,14 +6172,18 @@ var Frame = class {
|
|
|
6123
6172
|
function isCancel(key) {
|
|
6124
6173
|
return key.ctrl === true && key.name === "c" || key.ctrl === true && key.name === "d";
|
|
6125
6174
|
}
|
|
6175
|
+
function isListBack(key, allowBack) {
|
|
6176
|
+
return allowBack && (key.name === "left" || key.name === "b");
|
|
6177
|
+
}
|
|
6126
6178
|
async function select(io, message, choices, options = {}) {
|
|
6127
6179
|
if (choices.length === 0) throw new Error("select() requires at least one choice.");
|
|
6180
|
+
const allowBack = options.allowBack === true;
|
|
6128
6181
|
const fallbackIndex = Math.max(
|
|
6129
6182
|
0,
|
|
6130
6183
|
choices.findIndex((choice) => choice.value === options.defaultValue)
|
|
6131
6184
|
);
|
|
6132
6185
|
if (!isInteractive(io)) {
|
|
6133
|
-
io.output.write(`${renderSelect(message, choices, fallbackIndex)}
|
|
6186
|
+
io.output.write(`${renderSelect(message, choices, fallbackIndex, allowBack)}
|
|
6134
6187
|
`);
|
|
6135
6188
|
return choices[fallbackIndex].value;
|
|
6136
6189
|
}
|
|
@@ -6139,9 +6192,10 @@ async function select(io, message, choices, options = {}) {
|
|
|
6139
6192
|
let index = fallbackIndex;
|
|
6140
6193
|
try {
|
|
6141
6194
|
for (; ; ) {
|
|
6142
|
-
frame.draw(renderSelect(message, choices, index));
|
|
6195
|
+
frame.draw(renderSelect(message, choices, index, allowBack));
|
|
6143
6196
|
const key = await reader.next();
|
|
6144
6197
|
if (isCancel(key)) throw new PromptCancelled();
|
|
6198
|
+
if (isListBack(key, allowBack)) throw new PromptBack();
|
|
6145
6199
|
if (key.name === "up") index = moveSelection(index, -1, choices.length);
|
|
6146
6200
|
else if (key.name === "down") index = moveSelection(index, 1, choices.length);
|
|
6147
6201
|
else if (key.name === "return" || key.name === "enter") {
|
|
@@ -6150,18 +6204,22 @@ async function select(io, message, choices, options = {}) {
|
|
|
6150
6204
|
} else if (key.name === "j") index = moveSelection(index, 1, choices.length);
|
|
6151
6205
|
else if (key.name === "k") index = moveSelection(index, -1, choices.length);
|
|
6152
6206
|
}
|
|
6207
|
+
} catch (error) {
|
|
6208
|
+
frame.erase();
|
|
6209
|
+
throw error;
|
|
6153
6210
|
} finally {
|
|
6154
6211
|
reader.close();
|
|
6155
6212
|
}
|
|
6156
6213
|
}
|
|
6157
6214
|
async function multiSelect(io, message, choices, options = {}) {
|
|
6215
|
+
const allowBack = options.allowBack === true;
|
|
6158
6216
|
const selected = /* @__PURE__ */ new Set();
|
|
6159
6217
|
choices.forEach((choice, position) => {
|
|
6160
6218
|
if (options.defaults?.includes(choice.value)) selected.add(position);
|
|
6161
6219
|
});
|
|
6162
6220
|
const result = () => choices.filter((_, position) => selected.has(position)).map((choice) => choice.value);
|
|
6163
6221
|
if (!isInteractive(io)) {
|
|
6164
|
-
io.output.write(`${renderMultiSelect(message, choices, 0, selected)}
|
|
6222
|
+
io.output.write(`${renderMultiSelect(message, choices, 0, selected, allowBack)}
|
|
6165
6223
|
`);
|
|
6166
6224
|
return result();
|
|
6167
6225
|
}
|
|
@@ -6170,9 +6228,10 @@ async function multiSelect(io, message, choices, options = {}) {
|
|
|
6170
6228
|
let index = 0;
|
|
6171
6229
|
try {
|
|
6172
6230
|
for (; ; ) {
|
|
6173
|
-
frame.draw(renderMultiSelect(message, choices, index, selected));
|
|
6231
|
+
frame.draw(renderMultiSelect(message, choices, index, selected, allowBack));
|
|
6174
6232
|
const key = await reader.next();
|
|
6175
6233
|
if (isCancel(key)) throw new PromptCancelled();
|
|
6234
|
+
if (isListBack(key, allowBack)) throw new PromptBack();
|
|
6176
6235
|
if (key.name === "up") index = moveSelection(index, -1, choices.length);
|
|
6177
6236
|
else if (key.name === "down") index = moveSelection(index, 1, choices.length);
|
|
6178
6237
|
else if (key.name === "space") {
|
|
@@ -6187,14 +6246,18 @@ async function multiSelect(io, message, choices, options = {}) {
|
|
|
6187
6246
|
return result();
|
|
6188
6247
|
}
|
|
6189
6248
|
}
|
|
6249
|
+
} catch (error) {
|
|
6250
|
+
frame.erase();
|
|
6251
|
+
throw error;
|
|
6190
6252
|
} finally {
|
|
6191
6253
|
reader.close();
|
|
6192
6254
|
}
|
|
6193
6255
|
}
|
|
6194
6256
|
async function confirm(io, message, options = {}) {
|
|
6257
|
+
const allowBack = options.allowBack === true;
|
|
6195
6258
|
let value = options.defaultValue ?? true;
|
|
6196
6259
|
if (!isInteractive(io)) {
|
|
6197
|
-
io.output.write(`${renderConfirm(message, value)}
|
|
6260
|
+
io.output.write(`${renderConfirm(message, value, allowBack)}
|
|
6198
6261
|
`);
|
|
6199
6262
|
return value;
|
|
6200
6263
|
}
|
|
@@ -6202,9 +6265,10 @@ async function confirm(io, message, options = {}) {
|
|
|
6202
6265
|
const reader = keyReader(io.input);
|
|
6203
6266
|
try {
|
|
6204
6267
|
for (; ; ) {
|
|
6205
|
-
frame.draw(renderConfirm(message, value));
|
|
6268
|
+
frame.draw(renderConfirm(message, value, allowBack));
|
|
6206
6269
|
const key = await reader.next();
|
|
6207
6270
|
if (isCancel(key)) throw new PromptCancelled();
|
|
6271
|
+
if (allowBack && key.name === "b") throw new PromptBack();
|
|
6208
6272
|
if (key.name === "up" || key.name === "left") value = true;
|
|
6209
6273
|
else if (key.name === "down" || key.name === "right") value = false;
|
|
6210
6274
|
else if (key.name === "y") value = true;
|
|
@@ -6214,14 +6278,18 @@ async function confirm(io, message, options = {}) {
|
|
|
6214
6278
|
return value;
|
|
6215
6279
|
}
|
|
6216
6280
|
}
|
|
6281
|
+
} catch (error) {
|
|
6282
|
+
frame.erase();
|
|
6283
|
+
throw error;
|
|
6217
6284
|
} finally {
|
|
6218
6285
|
reader.close();
|
|
6219
6286
|
}
|
|
6220
6287
|
}
|
|
6221
6288
|
async function text(io, message, options = {}) {
|
|
6289
|
+
const allowBack = options.allowBack === true;
|
|
6222
6290
|
const fallback = options.defaultValue ?? "";
|
|
6223
6291
|
if (!isInteractive(io)) {
|
|
6224
|
-
io.output.write(`${renderText(message, fallback)}
|
|
6292
|
+
io.output.write(`${renderText(message, fallback, void 0, allowBack)}
|
|
6225
6293
|
`);
|
|
6226
6294
|
return fallback;
|
|
6227
6295
|
}
|
|
@@ -6231,9 +6299,10 @@ async function text(io, message, options = {}) {
|
|
|
6231
6299
|
let error;
|
|
6232
6300
|
try {
|
|
6233
6301
|
for (; ; ) {
|
|
6234
|
-
frame.draw(renderText(message, value, error));
|
|
6302
|
+
frame.draw(renderText(message, value, error, allowBack));
|
|
6235
6303
|
const key = await reader.next();
|
|
6236
6304
|
if (isCancel(key)) throw new PromptCancelled();
|
|
6305
|
+
if (allowBack && key.name === "left") throw new PromptBack();
|
|
6237
6306
|
error = void 0;
|
|
6238
6307
|
if (key.name === "return" || key.name === "enter") {
|
|
6239
6308
|
const problem = options.validate ? options.validate(value) : null;
|
|
@@ -6254,6 +6323,9 @@ async function text(io, message, options = {}) {
|
|
|
6254
6323
|
}
|
|
6255
6324
|
if (_isPrintable(key)) value += _keyText(key);
|
|
6256
6325
|
}
|
|
6326
|
+
} catch (error_) {
|
|
6327
|
+
frame.erase();
|
|
6328
|
+
throw error_;
|
|
6257
6329
|
} finally {
|
|
6258
6330
|
reader.close();
|
|
6259
6331
|
}
|
|
@@ -6277,10 +6349,10 @@ var CONFIG_CANDIDATES = [
|
|
|
6277
6349
|
];
|
|
6278
6350
|
function chooseConfigFileName(cwd) {
|
|
6279
6351
|
if (!supportsTypeScriptImports()) return "snapshot.config.mjs";
|
|
6280
|
-
const pkgPath =
|
|
6281
|
-
if (!
|
|
6352
|
+
const pkgPath = path12__namespace.join(cwd, "package.json");
|
|
6353
|
+
if (!fs11__namespace.existsSync(pkgPath)) return "snapshot.config.mjs";
|
|
6282
6354
|
try {
|
|
6283
|
-
const parsed = JSON.parse(
|
|
6355
|
+
const parsed = JSON.parse(fs11__namespace.readFileSync(pkgPath, "utf8"));
|
|
6284
6356
|
return parsed.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
6285
6357
|
} catch {
|
|
6286
6358
|
return "snapshot.config.mjs";
|
|
@@ -6294,7 +6366,7 @@ var SIZE_CHOICES = [
|
|
|
6294
6366
|
];
|
|
6295
6367
|
function packageJsonHasConfig(cwd) {
|
|
6296
6368
|
try {
|
|
6297
|
-
const parsed = JSON.parse(
|
|
6369
|
+
const parsed = JSON.parse(fs11__namespace.readFileSync(path12__namespace.join(cwd, "package.json"), "utf8"));
|
|
6298
6370
|
return typeof parsed.snapshot === "object" && parsed.snapshot !== null && !Array.isArray(parsed.snapshot);
|
|
6299
6371
|
} catch {
|
|
6300
6372
|
return false;
|
|
@@ -6315,7 +6387,7 @@ function artifactAnswers(options) {
|
|
|
6315
6387
|
}
|
|
6316
6388
|
function topLevelDirs(cwd) {
|
|
6317
6389
|
try {
|
|
6318
|
-
return
|
|
6390
|
+
return fs11__namespace.readdirSync(cwd, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules").map((entry) => entry.name).sort();
|
|
6319
6391
|
} catch {
|
|
6320
6392
|
return [];
|
|
6321
6393
|
}
|
|
@@ -6328,7 +6400,7 @@ function describeJsonValue(value) {
|
|
|
6328
6400
|
}
|
|
6329
6401
|
function writeFileOrExplain(target, content) {
|
|
6330
6402
|
try {
|
|
6331
|
-
|
|
6403
|
+
fs11__namespace.writeFileSync(target, content, "utf8");
|
|
6332
6404
|
} catch (err) {
|
|
6333
6405
|
const code = err.code;
|
|
6334
6406
|
const reason = code === "EACCES" || code === "EPERM" || code === "EROFS" ? `permission denied (${code})` : code === "EISDIR" ? "it is a directory" : `${err.message}`;
|
|
@@ -6339,7 +6411,7 @@ function canWriteModuleConfig(name, cwd) {
|
|
|
6339
6411
|
if (name.endsWith(".mjs") || name.endsWith(".ts")) return true;
|
|
6340
6412
|
if (!name.endsWith(".js")) return false;
|
|
6341
6413
|
try {
|
|
6342
|
-
const parsed = JSON.parse(
|
|
6414
|
+
const parsed = JSON.parse(fs11__namespace.readFileSync(path12__namespace.join(cwd, "package.json"), "utf8"));
|
|
6343
6415
|
return parsed.type === "module";
|
|
6344
6416
|
} catch {
|
|
6345
6417
|
return false;
|
|
@@ -6374,19 +6446,19 @@ function serializePackageJson(raw, value) {
|
|
|
6374
6446
|
return `${text2}${raw.endsWith("\n") ? crlf ? "\r\n" : "\n" : ""}`;
|
|
6375
6447
|
}
|
|
6376
6448
|
function updatePackageJson(cwd, notes) {
|
|
6377
|
-
const packagePath =
|
|
6449
|
+
const packagePath = path12__namespace.join(cwd, "package.json");
|
|
6378
6450
|
const unchanged = {
|
|
6379
6451
|
scripts: [],
|
|
6380
6452
|
dependency: { name: GENERATOR_NAME, range: null, added: false }
|
|
6381
6453
|
};
|
|
6382
|
-
if (!
|
|
6454
|
+
if (!fs11__namespace.existsSync(packagePath)) {
|
|
6383
6455
|
notes.push("No package.json found, so npm scripts and the devDependency were not added.");
|
|
6384
6456
|
return unchanged;
|
|
6385
6457
|
}
|
|
6386
6458
|
let raw;
|
|
6387
6459
|
let parsed;
|
|
6388
6460
|
try {
|
|
6389
|
-
raw =
|
|
6461
|
+
raw = fs11__namespace.readFileSync(packagePath, "utf8");
|
|
6390
6462
|
parsed = JSON.parse(raw);
|
|
6391
6463
|
} catch {
|
|
6392
6464
|
notes.push("Could not parse package.json; npm scripts and the devDependency were not added.");
|
|
@@ -6449,16 +6521,18 @@ function updatePackageJson(cwd, notes) {
|
|
|
6449
6521
|
return { scripts: added, dependency };
|
|
6450
6522
|
}
|
|
6451
6523
|
var GITIGNORE_ENTRIES = ["snapshot/", "snapshot.tmp/"];
|
|
6452
|
-
function resolvePresetChoice(
|
|
6524
|
+
function resolvePresetChoice(detectedName, presetName) {
|
|
6453
6525
|
return {
|
|
6454
6526
|
value: presetName,
|
|
6455
6527
|
label: presetName,
|
|
6456
|
-
hint: presetName ===
|
|
6528
|
+
hint: presetName === detectedName ? "detected" : void 0
|
|
6457
6529
|
};
|
|
6458
6530
|
}
|
|
6459
6531
|
async function runInit(options) {
|
|
6460
6532
|
const io = options.io ?? defaultIO();
|
|
6461
6533
|
const detected = detectPreset(options.cwd, options.preset);
|
|
6534
|
+
let reason = detected.reason;
|
|
6535
|
+
let fallback = detected.fallback;
|
|
6462
6536
|
const notes = [];
|
|
6463
6537
|
const answers = {
|
|
6464
6538
|
preset: detected.presetName,
|
|
@@ -6480,73 +6554,37 @@ async function runInit(options) {
|
|
|
6480
6554
|
if (interactive) {
|
|
6481
6555
|
io.output.write("\n codebase-snapshot\n");
|
|
6482
6556
|
io.output.write(" Turn this repository into an LLM-ready, redacted, byte-bounded snapshot.\n\n");
|
|
6483
|
-
|
|
6557
|
+
if (fallback) {
|
|
6558
|
+
io.output.write(" \u26A0 Could not tell what kind of project this is.\n");
|
|
6559
|
+
io.output.write(" No package.json, go.mod, Cargo.toml, pyproject.toml, Gemfile,\n");
|
|
6560
|
+
io.output.write(" composer.json, pom.xml, *.csproj, next.config.*, vite.config.*, *.tf\n");
|
|
6561
|
+
io.output.write(" or workspace marker was found here.\n\n");
|
|
6562
|
+
} else {
|
|
6563
|
+
io.output.write(` Detected ${detected.presetName}
|
|
6484
6564
|
`);
|
|
6485
|
-
|
|
6565
|
+
io.output.write(` Because ${reason}
|
|
6486
6566
|
|
|
6487
6567
|
`);
|
|
6488
|
-
const useDetected = await confirm(io, `Set up for ${detected.presetName}?`, {
|
|
6489
|
-
defaultValue: true
|
|
6490
|
-
});
|
|
6491
|
-
if (!useDetected) {
|
|
6492
|
-
answers.preset = await select(
|
|
6493
|
-
io,
|
|
6494
|
-
"What kind of project is this?",
|
|
6495
|
-
listPresetNames().map((name) => resolvePresetChoice(answers, name))
|
|
6496
|
-
);
|
|
6497
|
-
const chosen = detectPreset(options.cwd, answers.preset);
|
|
6498
|
-
answers.preset = chosen.presetName;
|
|
6499
|
-
}
|
|
6500
|
-
const scope = await select(
|
|
6501
|
-
io,
|
|
6502
|
-
"What should go into the snapshot?",
|
|
6503
|
-
[
|
|
6504
|
-
{ value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
|
|
6505
|
-
{ value: "folders", label: "Only specific folders\u2026" },
|
|
6506
|
-
{ value: "no-tests", label: "Everything, but skip tests" },
|
|
6507
|
-
{ value: "custom", label: "Let me write glob patterns" }
|
|
6508
|
-
],
|
|
6509
|
-
{ defaultValue: "all" }
|
|
6510
|
-
);
|
|
6511
|
-
if (scope === "folders") {
|
|
6512
|
-
const dirs = topLevelDirs(options.cwd).slice(0, 40);
|
|
6513
|
-
if (dirs.length === 0) {
|
|
6514
|
-
notes.push("No top-level directories found; the snapshot will include everything.");
|
|
6515
|
-
} else {
|
|
6516
|
-
const chosen = await multiSelect(
|
|
6517
|
-
io,
|
|
6518
|
-
"Select folders",
|
|
6519
|
-
dirs.map((name) => ({ value: name, label: `${name}/` })),
|
|
6520
|
-
{ defaults: dirs }
|
|
6521
|
-
);
|
|
6522
|
-
answers.include = chosen.map((name) => `${name}/**`);
|
|
6523
|
-
}
|
|
6524
|
-
} else if (scope === "no-tests") {
|
|
6525
|
-
answers.excludePatterns = [
|
|
6526
|
-
...answers.excludePatterns,
|
|
6527
|
-
"**/*.test.*",
|
|
6528
|
-
"**/*.spec.*",
|
|
6529
|
-
"tests",
|
|
6530
|
-
"test"
|
|
6531
|
-
];
|
|
6532
|
-
} else if (scope === "custom") {
|
|
6533
|
-
const raw = await text(io, "Glob patterns to include (comma separated)", {
|
|
6534
|
-
defaultValue: ""
|
|
6535
|
-
});
|
|
6536
|
-
answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
6537
|
-
}
|
|
6538
|
-
const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
|
|
6539
|
-
defaultValue: "120k"
|
|
6540
|
-
});
|
|
6541
|
-
if (sizeChoice === "custom") {
|
|
6542
|
-
const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
|
|
6543
|
-
defaultValue: String(answers.maxPartBytes),
|
|
6544
|
-
validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null
|
|
6545
|
-
});
|
|
6546
|
-
answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
|
|
6547
|
-
} else {
|
|
6548
|
-
answers.maxPartBytes = sizeChoice;
|
|
6549
6568
|
}
|
|
6569
|
+
io.output.write(` Directory ${options.cwd}
|
|
6570
|
+
|
|
6571
|
+
`);
|
|
6572
|
+
const paceChoices = fallback ? [
|
|
6573
|
+
{ value: "choose", label: "Pick the closest preset", hint: "then generate with the defaults" },
|
|
6574
|
+
{ value: "recommended", label: "Use the defaults", hint: "generic \xB7 everything the scanner finds" },
|
|
6575
|
+
{ value: "walk", label: "Go through each setting", hint: "every question starts on its default" }
|
|
6576
|
+
] : [
|
|
6577
|
+
{
|
|
6578
|
+
value: "recommended",
|
|
6579
|
+
label: "Use the recommended setup",
|
|
6580
|
+
hint: `${detected.presetName} \xB7 config \xB7 npm scripts \xB7 install \xB7 generate`
|
|
6581
|
+
},
|
|
6582
|
+
{ value: "walk", label: "Go through each setting", hint: "every question starts on its default" },
|
|
6583
|
+
{ value: "choose", label: "Pick a different preset", hint: "everything else stays at the defaults" }
|
|
6584
|
+
];
|
|
6585
|
+
const baseInclude = [...answers.include];
|
|
6586
|
+
const baseExclude = [...answers.excludePatterns];
|
|
6587
|
+
let lastScope = "all";
|
|
6550
6588
|
const artifactChoices = [
|
|
6551
6589
|
{ value: "readme", label: "README.md", hint: "orientation page" },
|
|
6552
6590
|
{ value: "manifest", label: "manifest.json", hint: "required by validate and report" },
|
|
@@ -6556,41 +6594,163 @@ async function runInit(options) {
|
|
|
6556
6594
|
{ value: "audit", label: "reports/audit.md", hint: "security and health findings" },
|
|
6557
6595
|
{ value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
|
|
6558
6596
|
];
|
|
6559
|
-
const
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6597
|
+
const scopeStep = async () => {
|
|
6598
|
+
for (; ; ) {
|
|
6599
|
+
const scope = await select(
|
|
6600
|
+
io,
|
|
6601
|
+
"What should go into the snapshot?",
|
|
6602
|
+
[
|
|
6603
|
+
{ value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
|
|
6604
|
+
{ value: "folders", label: "Only specific folders\u2026" },
|
|
6605
|
+
{ value: "no-tests", label: "Everything, but skip tests" },
|
|
6606
|
+
{ value: "custom", label: "Let me write glob patterns" }
|
|
6607
|
+
],
|
|
6608
|
+
{ defaultValue: lastScope, allowBack: true }
|
|
6609
|
+
);
|
|
6610
|
+
lastScope = scope;
|
|
6611
|
+
answers.include = [...baseInclude];
|
|
6612
|
+
answers.excludePatterns = [...baseExclude];
|
|
6613
|
+
try {
|
|
6614
|
+
if (scope === "folders") {
|
|
6615
|
+
const dirs = topLevelDirs(options.cwd).slice(0, 40);
|
|
6616
|
+
if (dirs.length === 0) {
|
|
6617
|
+
notes.push("No top-level directories found; the snapshot will include everything.");
|
|
6618
|
+
} else {
|
|
6619
|
+
const chosen = await multiSelect(
|
|
6620
|
+
io,
|
|
6621
|
+
"Select folders",
|
|
6622
|
+
dirs.map((name) => ({ value: name, label: `${name}/` })),
|
|
6623
|
+
{ defaults: dirs, allowBack: true }
|
|
6624
|
+
);
|
|
6625
|
+
answers.include = chosen.map((name) => `${name}/**`);
|
|
6626
|
+
}
|
|
6627
|
+
} else if (scope === "no-tests") {
|
|
6628
|
+
answers.excludePatterns = [...baseExclude, "**/*.test.*", "**/*.spec.*", "tests", "test"];
|
|
6629
|
+
} else if (scope === "custom") {
|
|
6630
|
+
const raw = await text(io, "Glob patterns to include (comma separated)", {
|
|
6631
|
+
defaultValue: baseInclude.join(", "),
|
|
6632
|
+
allowBack: true
|
|
6633
|
+
});
|
|
6634
|
+
answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
6635
|
+
}
|
|
6636
|
+
return;
|
|
6637
|
+
} catch (error) {
|
|
6638
|
+
if (error instanceof PromptBack) continue;
|
|
6639
|
+
throw error;
|
|
6640
|
+
}
|
|
6641
|
+
}
|
|
6642
|
+
};
|
|
6643
|
+
const sizeStep = async () => {
|
|
6644
|
+
for (; ; ) {
|
|
6645
|
+
const currentBudget = String(answers.maxPartBytes);
|
|
6646
|
+
const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
|
|
6647
|
+
defaultValue: SIZE_CHOICES.some((choice) => choice.value === currentBudget) ? currentBudget : "custom",
|
|
6648
|
+
allowBack: true
|
|
6649
|
+
});
|
|
6650
|
+
if (sizeChoice !== "custom") {
|
|
6651
|
+
answers.maxPartBytes = sizeChoice;
|
|
6652
|
+
return;
|
|
6653
|
+
}
|
|
6654
|
+
try {
|
|
6655
|
+
const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
|
|
6656
|
+
defaultValue: currentBudget,
|
|
6657
|
+
validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null,
|
|
6658
|
+
allowBack: true
|
|
6659
|
+
});
|
|
6660
|
+
answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
|
|
6661
|
+
return;
|
|
6662
|
+
} catch (error) {
|
|
6663
|
+
if (error instanceof PromptBack) continue;
|
|
6664
|
+
throw error;
|
|
6665
|
+
}
|
|
6666
|
+
}
|
|
6667
|
+
};
|
|
6668
|
+
const artifactsStep = async () => {
|
|
6669
|
+
const enabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
|
|
6670
|
+
const selectedArtifacts = await multiSelect(
|
|
6671
|
+
io,
|
|
6672
|
+
"Which artifacts should be generated?",
|
|
6673
|
+
artifactChoices,
|
|
6674
|
+
{ defaults: enabled, allowBack: true }
|
|
6675
|
+
);
|
|
6676
|
+
for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
|
|
6677
|
+
};
|
|
6678
|
+
const credentialsStep = async () => {
|
|
6679
|
+
answers.includeSensitiveFiles = await confirm(
|
|
6680
|
+
io,
|
|
6681
|
+
"Include credential files (.env, *.pem, *.npmrc, *.ssh/)?",
|
|
6682
|
+
{ defaultValue: answers.includeSensitiveFiles, allowBack: true }
|
|
6683
|
+
);
|
|
6684
|
+
};
|
|
6685
|
+
const reviewStep = async () => {
|
|
6686
|
+
const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
|
|
6687
|
+
io.output.write("\n Review\n\n");
|
|
6688
|
+
io.output.write(` project ${answers.preset}
|
|
6575
6689
|
`);
|
|
6576
|
-
|
|
6690
|
+
io.output.write(` include ${answers.include.length ? answers.include.join(", ") : "everything"}
|
|
6577
6691
|
`);
|
|
6578
|
-
|
|
6692
|
+
io.output.write(` part size ${String(answers.maxPartBytes)}
|
|
6579
6693
|
`);
|
|
6580
|
-
|
|
6694
|
+
io.output.write(` artifacts ${enabledCount} of ${ARTIFACT_NAMES.length}
|
|
6581
6695
|
`);
|
|
6582
|
-
|
|
6696
|
+
io.output.write(` credentials ${answers.includeSensitiveFiles ? "included" : "skipped"}
|
|
6583
6697
|
|
|
6584
6698
|
`);
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6699
|
+
io.output.write(" Will write snapshot.config with your answers, plus every other setting\n");
|
|
6700
|
+
io.output.write(" at its default \u2014 so nothing is hidden behind the docs.\n\n");
|
|
6701
|
+
const proceed = await confirm(io, "Write this and continue?", {
|
|
6702
|
+
defaultValue: true,
|
|
6703
|
+
allowBack: true
|
|
6704
|
+
});
|
|
6705
|
+
if (!proceed) throw new PromptBack();
|
|
6706
|
+
};
|
|
6707
|
+
const walk = async () => {
|
|
6708
|
+
const steps = [scopeStep, sizeStep, artifactsStep, credentialsStep, reviewStep];
|
|
6709
|
+
let index = 0;
|
|
6710
|
+
while (index < steps.length) {
|
|
6711
|
+
try {
|
|
6712
|
+
await steps[index]();
|
|
6713
|
+
index += 1;
|
|
6714
|
+
} catch (error) {
|
|
6715
|
+
if (!(error instanceof PromptBack)) throw error;
|
|
6716
|
+
if (index === 0) return false;
|
|
6717
|
+
index -= 1;
|
|
6718
|
+
io.output.write("\n");
|
|
6719
|
+
}
|
|
6720
|
+
}
|
|
6721
|
+
return true;
|
|
6722
|
+
};
|
|
6723
|
+
for (; ; ) {
|
|
6724
|
+
const pace = await select(io, "How should I set this up?", paceChoices, {
|
|
6725
|
+
defaultValue: fallback ? "choose" : "recommended"
|
|
6726
|
+
});
|
|
6727
|
+
if (pace === "choose") {
|
|
6728
|
+
try {
|
|
6729
|
+
answers.preset = await select(
|
|
6730
|
+
io,
|
|
6731
|
+
"What kind of project is this?",
|
|
6732
|
+
listPresetNames().map(
|
|
6733
|
+
(name) => resolvePresetChoice(fallback ? null : detected.presetName, name)
|
|
6734
|
+
),
|
|
6735
|
+
{ defaultValue: answers.preset, allowBack: true }
|
|
6736
|
+
);
|
|
6737
|
+
} catch (error) {
|
|
6738
|
+
if (error instanceof PromptBack) continue;
|
|
6739
|
+
throw error;
|
|
6740
|
+
}
|
|
6741
|
+
const chosen = detectPreset(options.cwd, answers.preset);
|
|
6742
|
+
answers.preset = chosen.presetName;
|
|
6743
|
+
reason = null;
|
|
6744
|
+
fallback = false;
|
|
6745
|
+
}
|
|
6746
|
+
if (pace === "walk" && !await walk()) continue;
|
|
6747
|
+
break;
|
|
6748
|
+
}
|
|
6589
6749
|
}
|
|
6590
6750
|
const configText = renderConfig(answers);
|
|
6591
6751
|
const preferredName = chooseConfigFileName(options.cwd);
|
|
6592
6752
|
const presentConfigs = CONFIG_CANDIDATES.filter(
|
|
6593
|
-
(name) =>
|
|
6753
|
+
(name) => fs11__namespace.existsSync(path12__namespace.join(options.cwd, name))
|
|
6594
6754
|
);
|
|
6595
6755
|
const present = presentConfigs[0] ?? null;
|
|
6596
6756
|
if (presentConfigs.length > 1) {
|
|
@@ -6599,7 +6759,7 @@ async function runInit(options) {
|
|
|
6599
6759
|
);
|
|
6600
6760
|
}
|
|
6601
6761
|
const configName = present !== null && options.force ? present : preferredName;
|
|
6602
|
-
const configPath =
|
|
6762
|
+
const configPath = path12__namespace.join(options.cwd, configName);
|
|
6603
6763
|
const reused = present !== null && options.force !== true && options.reuseConfig === true;
|
|
6604
6764
|
if (reused) notes.push(`Kept the existing ${present} instead of writing a new one.`);
|
|
6605
6765
|
if (options.dryRun) {
|
|
@@ -6609,6 +6769,8 @@ async function runInit(options) {
|
|
|
6609
6769
|
written: false,
|
|
6610
6770
|
reused,
|
|
6611
6771
|
preset: answers.preset,
|
|
6772
|
+
reason,
|
|
6773
|
+
fallback,
|
|
6612
6774
|
scripts: [],
|
|
6613
6775
|
gitignoreEntries: [],
|
|
6614
6776
|
dependency: { name: GENERATOR_NAME, range: `^${GENERATOR_VERSION}`, added: false },
|
|
@@ -6630,7 +6792,7 @@ async function runInit(options) {
|
|
|
6630
6792
|
'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.'
|
|
6631
6793
|
);
|
|
6632
6794
|
}
|
|
6633
|
-
if (
|
|
6795
|
+
if (fs11__namespace.existsSync(configPath) && !options.force && !reused) {
|
|
6634
6796
|
throw new Error(`${configName} already exists. Re-run with --force to replace it.`);
|
|
6635
6797
|
}
|
|
6636
6798
|
if (!reused) writeFileOrExplain(configPath, configText);
|
|
@@ -6643,12 +6805,56 @@ async function runInit(options) {
|
|
|
6643
6805
|
written: true,
|
|
6644
6806
|
reused,
|
|
6645
6807
|
preset: answers.preset,
|
|
6808
|
+
reason,
|
|
6809
|
+
fallback,
|
|
6646
6810
|
scripts,
|
|
6647
6811
|
gitignoreEntries: ignore.added,
|
|
6648
6812
|
dependency,
|
|
6649
6813
|
notes
|
|
6650
6814
|
};
|
|
6651
6815
|
}
|
|
6816
|
+
function describeTarget(cwd) {
|
|
6817
|
+
const relative7 = path12__namespace.relative(process.cwd(), cwd);
|
|
6818
|
+
if (relative7 === "") return "this directory";
|
|
6819
|
+
if (relative7.startsWith("..") || path12__namespace.isAbsolute(relative7)) return cwd;
|
|
6820
|
+
return relative7;
|
|
6821
|
+
}
|
|
6822
|
+
function firstRunMessage(reason, cwd) {
|
|
6823
|
+
const where = describeTarget(cwd);
|
|
6824
|
+
if (reason === "empty") {
|
|
6825
|
+
return `Nothing to snapshot: ${where} has no files. Run this inside the repository you want to snapshot, or pass --cwd.`;
|
|
6826
|
+
}
|
|
6827
|
+
return `No configuration found in ${where} and this session is not interactive, so nothing was generated.
|
|
6828
|
+
Choose one:
|
|
6829
|
+
${GENERATOR_NAME} --autopilot detect the project, set it up and generate, no questions
|
|
6830
|
+
${GENERATOR_NAME} init --yes write a config from the detected defaults, then generate
|
|
6831
|
+
${GENERATOR_NAME} init write a config interactively`;
|
|
6832
|
+
}
|
|
6833
|
+
function planFirstRun(facts) {
|
|
6834
|
+
if (facts.emptyTarget) return { action: "refuse", exitCode: 1, reason: "empty" };
|
|
6835
|
+
if (facts.autopilot) return { action: "setup", interactive: false };
|
|
6836
|
+
if (facts.hasConfig || facts.hasPriorSnapshot) return { action: "generate" };
|
|
6837
|
+
if (facts.interactive) return { action: "setup", interactive: true };
|
|
6838
|
+
return { action: "refuse", exitCode: 2, reason: "undecided" };
|
|
6839
|
+
}
|
|
6840
|
+
var NOT_CONTENT = /* @__PURE__ */ new Set([".git", ".hg", ".svn", ".bzr", ".DS_Store", "Thumbs.db"]);
|
|
6841
|
+
function isEffectivelyEmpty(cwd) {
|
|
6842
|
+
try {
|
|
6843
|
+
return fs11__namespace.readdirSync(cwd).every((entry) => NOT_CONTENT.has(entry));
|
|
6844
|
+
} catch {
|
|
6845
|
+
return false;
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
function hasPriorSnapshot(cwd, outDir) {
|
|
6849
|
+
let parsed;
|
|
6850
|
+
try {
|
|
6851
|
+
parsed = JSON.parse(fs11__namespace.readFileSync(path12__namespace.join(cwd, outDir, ".codebase-snapshot"), "utf8"));
|
|
6852
|
+
} catch {
|
|
6853
|
+
return false;
|
|
6854
|
+
}
|
|
6855
|
+
if (parsed === null || typeof parsed !== "object") return false;
|
|
6856
|
+
return isOurGeneratorName(parsed.generator);
|
|
6857
|
+
}
|
|
6652
6858
|
|
|
6653
6859
|
// src/index.ts
|
|
6654
6860
|
var SCHEMA_VERSION = "3.0.0";
|
|
@@ -6730,13 +6936,13 @@ function buildImportResolver(files) {
|
|
|
6730
6936
|
const relativePath = file.relativePath;
|
|
6731
6937
|
remember(byPath, relativePath, relativePath);
|
|
6732
6938
|
remember(byPathFolded, relativePath.toLowerCase(), relativePath);
|
|
6733
|
-
const extension =
|
|
6939
|
+
const extension = path12__namespace.posix.extname(relativePath);
|
|
6734
6940
|
if (!SOURCE_EXTENSIONS.has(extension)) continue;
|
|
6735
6941
|
const stem = relativePath.slice(0, -extension.length);
|
|
6736
6942
|
remember(byStem, stem, relativePath);
|
|
6737
6943
|
remember(byStemFolded, stem.toLowerCase(), relativePath);
|
|
6738
|
-
if (
|
|
6739
|
-
const directory =
|
|
6944
|
+
if (path12__namespace.posix.basename(stem) === "index") {
|
|
6945
|
+
const directory = path12__namespace.posix.dirname(stem);
|
|
6740
6946
|
remember(byStem, directory, relativePath);
|
|
6741
6947
|
remember(byStemFolded, directory.toLowerCase(), relativePath);
|
|
6742
6948
|
}
|
|
@@ -6745,8 +6951,8 @@ function buildImportResolver(files) {
|
|
|
6745
6951
|
}
|
|
6746
6952
|
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
6747
6953
|
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
6748
|
-
const joined =
|
|
6749
|
-
|
|
6954
|
+
const joined = path12__namespace.posix.normalize(
|
|
6955
|
+
path12__namespace.posix.join(path12__namespace.posix.dirname(fromFile), specifier)
|
|
6750
6956
|
);
|
|
6751
6957
|
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
6752
6958
|
const direct = exact(joined);
|
|
@@ -6839,11 +7045,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
6839
7045
|
return errors;
|
|
6840
7046
|
}
|
|
6841
7047
|
function resolveSafeOutputDirs(config) {
|
|
6842
|
-
const cwd =
|
|
6843
|
-
const outDir =
|
|
6844
|
-
const tmpDir =
|
|
6845
|
-
const root =
|
|
6846
|
-
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${
|
|
7048
|
+
const cwd = path12__namespace.resolve(config.cwd);
|
|
7049
|
+
const outDir = path12__namespace.resolve(cwd, config.outDir);
|
|
7050
|
+
const tmpDir = path12__namespace.resolve(cwd, config.tmpDir);
|
|
7051
|
+
const root = path12__namespace.parse(cwd).root;
|
|
7052
|
+
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${path12__namespace.sep}`);
|
|
6847
7053
|
const reject = (reason) => {
|
|
6848
7054
|
throw new Error(
|
|
6849
7055
|
`Refusing to generate: ${reason}. outDir=${outDir} tmpDir=${tmpDir} cwd=${cwd}`
|
|
@@ -6899,14 +7105,14 @@ function listEntries(dir) {
|
|
|
6899
7105
|
const walk = (absolute, prefix) => {
|
|
6900
7106
|
let children;
|
|
6901
7107
|
try {
|
|
6902
|
-
children =
|
|
7108
|
+
children = fs11__namespace.readdirSync(absolute, { withFileTypes: true });
|
|
6903
7109
|
} catch {
|
|
6904
7110
|
return;
|
|
6905
7111
|
}
|
|
6906
7112
|
for (const child of children) {
|
|
6907
|
-
const
|
|
6908
|
-
entries.push(
|
|
6909
|
-
if (child.isDirectory()) walk(
|
|
7113
|
+
const relative7 = prefix === "" ? child.name : `${prefix}/${child.name}`;
|
|
7114
|
+
entries.push(relative7);
|
|
7115
|
+
if (child.isDirectory()) walk(path12__namespace.join(absolute, child.name), relative7);
|
|
6910
7116
|
}
|
|
6911
7117
|
};
|
|
6912
7118
|
walk(dir, "");
|
|
@@ -6923,28 +7129,28 @@ function markerPayload(dir, state, id, createdAt) {
|
|
|
6923
7129
|
pid: process.pid,
|
|
6924
7130
|
entries: listEntries(dir)
|
|
6925
7131
|
};
|
|
6926
|
-
const manifestPath =
|
|
6927
|
-
if (
|
|
6928
|
-
marker2.manifestSha256 = sha256Hex2(
|
|
7132
|
+
const manifestPath = path12__namespace.join(dir, "manifest.json");
|
|
7133
|
+
if (fs11__namespace.existsSync(manifestPath)) {
|
|
7134
|
+
marker2.manifestSha256 = sha256Hex2(fs11__namespace.readFileSync(manifestPath));
|
|
6929
7135
|
}
|
|
6930
7136
|
return marker2;
|
|
6931
7137
|
}
|
|
6932
7138
|
function writeOwnershipMarker(dir, marker2) {
|
|
6933
|
-
|
|
6934
|
-
|
|
7139
|
+
fs11__namespace.writeFileSync(
|
|
7140
|
+
path12__namespace.join(dir, OWNERSHIP_MARKER),
|
|
6935
7141
|
`${JSON.stringify(marker2, null, 2)}
|
|
6936
7142
|
`,
|
|
6937
7143
|
"utf8"
|
|
6938
7144
|
);
|
|
6939
7145
|
}
|
|
6940
7146
|
function readOwnershipMarker(dir) {
|
|
6941
|
-
const markerPath =
|
|
6942
|
-
if (!
|
|
7147
|
+
const markerPath = path12__namespace.join(dir, OWNERSHIP_MARKER);
|
|
7148
|
+
if (!fs11__namespace.existsSync(markerPath)) {
|
|
6943
7149
|
return { marker: null, problem: "it holds no .codebase-snapshot marker" };
|
|
6944
7150
|
}
|
|
6945
7151
|
let raw;
|
|
6946
7152
|
try {
|
|
6947
|
-
raw =
|
|
7153
|
+
raw = fs11__namespace.readFileSync(markerPath, "utf8");
|
|
6948
7154
|
} catch {
|
|
6949
7155
|
return { marker: null, problem: "its .codebase-snapshot marker could not be read" };
|
|
6950
7156
|
}
|
|
@@ -7012,11 +7218,11 @@ function assessSnapshotDirectory(dir) {
|
|
|
7012
7218
|
marker: marker2
|
|
7013
7219
|
};
|
|
7014
7220
|
}
|
|
7015
|
-
const manifestPath =
|
|
7016
|
-
if (
|
|
7221
|
+
const manifestPath = path12__namespace.join(dir, "manifest.json");
|
|
7222
|
+
if (fs11__namespace.existsSync(manifestPath)) {
|
|
7017
7223
|
let raw;
|
|
7018
7224
|
try {
|
|
7019
|
-
raw =
|
|
7225
|
+
raw = fs11__namespace.readFileSync(manifestPath, "utf8");
|
|
7020
7226
|
} catch {
|
|
7021
7227
|
return {
|
|
7022
7228
|
owned: false,
|
|
@@ -7098,13 +7304,13 @@ function containsNoFiles(dir) {
|
|
|
7098
7304
|
const current = pending.pop();
|
|
7099
7305
|
let entries;
|
|
7100
7306
|
try {
|
|
7101
|
-
entries =
|
|
7307
|
+
entries = fs11__namespace.readdirSync(current, { withFileTypes: true });
|
|
7102
7308
|
} catch {
|
|
7103
7309
|
return false;
|
|
7104
7310
|
}
|
|
7105
7311
|
for (const entry of entries) {
|
|
7106
7312
|
if (entry.isDirectory()) {
|
|
7107
|
-
pending.push(
|
|
7313
|
+
pending.push(path12__namespace.join(current, entry.name));
|
|
7108
7314
|
} else if (!IGNORABLE_ENTRIES.has(entry.name)) {
|
|
7109
7315
|
return false;
|
|
7110
7316
|
}
|
|
@@ -7113,13 +7319,13 @@ function containsNoFiles(dir) {
|
|
|
7113
7319
|
return true;
|
|
7114
7320
|
}
|
|
7115
7321
|
function assertReplaceable(target, label, secretRules) {
|
|
7116
|
-
if (!
|
|
7117
|
-
if (!
|
|
7322
|
+
if (!fs11__namespace.existsSync(target)) return;
|
|
7323
|
+
if (!fs11__namespace.statSync(target).isDirectory()) {
|
|
7118
7324
|
throw new Error(
|
|
7119
7325
|
`Refusing to write into ${label} ${target}: it exists but is not a directory.`
|
|
7120
7326
|
);
|
|
7121
7327
|
}
|
|
7122
|
-
const entries =
|
|
7328
|
+
const entries = fs11__namespace.readdirSync(target);
|
|
7123
7329
|
if (entries.length === 0) return;
|
|
7124
7330
|
if (entries.every((name) => IGNORABLE_ENTRIES.has(name))) return;
|
|
7125
7331
|
if (containsNoFiles(target)) return;
|
|
@@ -7139,18 +7345,18 @@ function assertReplaceable(target, label, secretRules) {
|
|
|
7139
7345
|
);
|
|
7140
7346
|
}
|
|
7141
7347
|
function assertStagingRootUsable(tmpDir, secretRules) {
|
|
7142
|
-
if (!
|
|
7143
|
-
if (!
|
|
7348
|
+
if (!fs11__namespace.existsSync(tmpDir)) return;
|
|
7349
|
+
if (!fs11__namespace.statSync(tmpDir).isDirectory()) {
|
|
7144
7350
|
throw new Error(`Refusing to write into tmpDir ${tmpDir}: it exists but is not a directory.`);
|
|
7145
7351
|
}
|
|
7146
|
-
const entries =
|
|
7352
|
+
const entries = fs11__namespace.readdirSync(tmpDir);
|
|
7147
7353
|
if (entries.length === 0) return;
|
|
7148
7354
|
const foreign = entries.filter((name) => {
|
|
7149
|
-
const full =
|
|
7355
|
+
const full = path12__namespace.join(tmpDir, name);
|
|
7150
7356
|
if (!name.startsWith(RUN_DIR_PREFIX)) return true;
|
|
7151
7357
|
let stats;
|
|
7152
7358
|
try {
|
|
7153
|
-
stats =
|
|
7359
|
+
stats = fs11__namespace.statSync(full);
|
|
7154
7360
|
} catch {
|
|
7155
7361
|
return true;
|
|
7156
7362
|
}
|
|
@@ -7158,7 +7364,7 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7158
7364
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7159
7365
|
if (marker2) return false;
|
|
7160
7366
|
try {
|
|
7161
|
-
return
|
|
7367
|
+
return fs11__namespace.readdirSync(full).length > 0;
|
|
7162
7368
|
} catch {
|
|
7163
7369
|
return true;
|
|
7164
7370
|
}
|
|
@@ -7173,13 +7379,13 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7173
7379
|
function cleanStaleRunDirs(tmpDir) {
|
|
7174
7380
|
let entries;
|
|
7175
7381
|
try {
|
|
7176
|
-
entries =
|
|
7382
|
+
entries = fs11__namespace.readdirSync(tmpDir);
|
|
7177
7383
|
} catch {
|
|
7178
7384
|
return;
|
|
7179
7385
|
}
|
|
7180
7386
|
for (const name of entries) {
|
|
7181
7387
|
if (!name.startsWith(RUN_DIR_PREFIX)) continue;
|
|
7182
|
-
const full =
|
|
7388
|
+
const full = path12__namespace.join(tmpDir, name);
|
|
7183
7389
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7184
7390
|
if (marker2) {
|
|
7185
7391
|
const created = Date.parse(marker2.createdAt);
|
|
@@ -7187,33 +7393,33 @@ function cleanStaleRunDirs(tmpDir) {
|
|
|
7187
7393
|
const abandoned = typeof marker2.pid === "number" && !isProcessAlive(marker2.pid);
|
|
7188
7394
|
if (aged || abandoned) {
|
|
7189
7395
|
try {
|
|
7190
|
-
|
|
7396
|
+
fs11__namespace.rmSync(full, { recursive: true, force: true });
|
|
7191
7397
|
} catch {
|
|
7192
7398
|
}
|
|
7193
7399
|
}
|
|
7194
7400
|
continue;
|
|
7195
7401
|
}
|
|
7196
7402
|
try {
|
|
7197
|
-
if (
|
|
7403
|
+
if (fs11__namespace.readdirSync(full).length === 0) fs11__namespace.rmSync(full, { recursive: true, force: true });
|
|
7198
7404
|
} catch {
|
|
7199
7405
|
}
|
|
7200
7406
|
}
|
|
7201
7407
|
}
|
|
7202
7408
|
function removeIfEmpty(dir) {
|
|
7203
7409
|
try {
|
|
7204
|
-
if (
|
|
7410
|
+
if (fs11__namespace.readdirSync(dir).length === 0) fs11__namespace.rmSync(dir, { recursive: true, force: true });
|
|
7205
7411
|
} catch {
|
|
7206
7412
|
}
|
|
7207
7413
|
}
|
|
7208
7414
|
function ensureParentDirectory(target, label) {
|
|
7209
|
-
const parent =
|
|
7210
|
-
if (
|
|
7415
|
+
const parent = path12__namespace.dirname(target);
|
|
7416
|
+
if (fs11__namespace.existsSync(parent) && !fs11__namespace.statSync(parent).isDirectory()) {
|
|
7211
7417
|
throw new Error(
|
|
7212
7418
|
`Refusing to create ${label} ${target}: ${parent} exists but is not a directory.`
|
|
7213
7419
|
);
|
|
7214
7420
|
}
|
|
7215
7421
|
try {
|
|
7216
|
-
|
|
7422
|
+
fs11__namespace.mkdirSync(parent, { recursive: true });
|
|
7217
7423
|
} catch (err) {
|
|
7218
7424
|
throw new Error(
|
|
7219
7425
|
`Refusing to create ${label} ${target}: its parent ${parent} could not be created (${err.message}).`
|
|
@@ -7222,7 +7428,7 @@ function ensureParentDirectory(target, label) {
|
|
|
7222
7428
|
}
|
|
7223
7429
|
function acquireRunLock(stagingRoot) {
|
|
7224
7430
|
const key = lockKeyFor(stagingRoot);
|
|
7225
|
-
const lockPath =
|
|
7431
|
+
const lockPath = path12__namespace.join(os__namespace.tmpdir(), `codebase-snapshot-${key}.lock`);
|
|
7226
7432
|
const payload = `${JSON.stringify({
|
|
7227
7433
|
pid: process.pid,
|
|
7228
7434
|
stagingRoot,
|
|
@@ -7231,22 +7437,22 @@ function acquireRunLock(stagingRoot) {
|
|
|
7231
7437
|
`;
|
|
7232
7438
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
7233
7439
|
try {
|
|
7234
|
-
const handle =
|
|
7440
|
+
const handle = fs11__namespace.openSync(lockPath, "wx");
|
|
7235
7441
|
try {
|
|
7236
|
-
|
|
7442
|
+
fs11__namespace.writeSync(handle, payload);
|
|
7237
7443
|
} finally {
|
|
7238
|
-
|
|
7444
|
+
fs11__namespace.closeSync(handle);
|
|
7239
7445
|
}
|
|
7240
7446
|
return () => {
|
|
7241
7447
|
try {
|
|
7242
|
-
|
|
7448
|
+
fs11__namespace.rmSync(lockPath, { force: true });
|
|
7243
7449
|
} catch {
|
|
7244
7450
|
}
|
|
7245
7451
|
};
|
|
7246
7452
|
} catch (err) {
|
|
7247
7453
|
if (err.code !== "EEXIST") throw err;
|
|
7248
7454
|
if (attempt === 0 && lockOwnerIsGone(lockPath)) {
|
|
7249
|
-
|
|
7455
|
+
fs11__namespace.rmSync(lockPath, { force: true });
|
|
7250
7456
|
continue;
|
|
7251
7457
|
}
|
|
7252
7458
|
throw new Error(
|
|
@@ -7259,7 +7465,7 @@ function acquireRunLock(stagingRoot) {
|
|
|
7259
7465
|
function lockKeyFor(stagingRoot) {
|
|
7260
7466
|
let key = stagingRoot;
|
|
7261
7467
|
try {
|
|
7262
|
-
key =
|
|
7468
|
+
key = fs11__namespace.realpathSync(stagingRoot);
|
|
7263
7469
|
} catch {
|
|
7264
7470
|
}
|
|
7265
7471
|
if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
|
|
@@ -7268,7 +7474,7 @@ function lockKeyFor(stagingRoot) {
|
|
|
7268
7474
|
function lockOwnerIsGone(lockPath) {
|
|
7269
7475
|
let raw;
|
|
7270
7476
|
try {
|
|
7271
|
-
raw =
|
|
7477
|
+
raw = fs11__namespace.readFileSync(lockPath, "utf8");
|
|
7272
7478
|
} catch {
|
|
7273
7479
|
return false;
|
|
7274
7480
|
}
|
|
@@ -7288,12 +7494,12 @@ function lockOwnerIsGone(lockPath) {
|
|
|
7288
7494
|
}
|
|
7289
7495
|
function writeManifestAtomically(manifestPath, manifest) {
|
|
7290
7496
|
const tempPath = `${manifestPath}.${process.pid}.tmp`;
|
|
7291
|
-
|
|
7497
|
+
fs11__namespace.writeFileSync(tempPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
7292
7498
|
try {
|
|
7293
|
-
|
|
7499
|
+
fs11__namespace.renameSync(tempPath, manifestPath);
|
|
7294
7500
|
} catch (err) {
|
|
7295
7501
|
try {
|
|
7296
|
-
|
|
7502
|
+
fs11__namespace.rmSync(tempPath, { force: true });
|
|
7297
7503
|
} catch {
|
|
7298
7504
|
}
|
|
7299
7505
|
throw err;
|
|
@@ -7316,31 +7522,31 @@ function swapIntoPlace(stagingDir, outDir) {
|
|
|
7316
7522
|
}
|
|
7317
7523
|
function swapOnce(stagingDir, outDir) {
|
|
7318
7524
|
const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
|
|
7319
|
-
const hadPrevious =
|
|
7320
|
-
if (hadPrevious)
|
|
7525
|
+
const hadPrevious = fs11__namespace.existsSync(outDir);
|
|
7526
|
+
if (hadPrevious) fs11__namespace.renameSync(outDir, backupDir);
|
|
7321
7527
|
try {
|
|
7322
7528
|
try {
|
|
7323
|
-
|
|
7529
|
+
fs11__namespace.renameSync(stagingDir, outDir);
|
|
7324
7530
|
} catch (err) {
|
|
7325
7531
|
if (err.code !== "EXDEV") throw err;
|
|
7326
7532
|
const partialDir = `${outDir}.partial-${process.pid}-${Date.now()}`;
|
|
7327
7533
|
try {
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7534
|
+
fs11__namespace.cpSync(stagingDir, partialDir, { recursive: true });
|
|
7535
|
+
fs11__namespace.renameSync(partialDir, outDir);
|
|
7536
|
+
fs11__namespace.rmSync(stagingDir, { recursive: true, force: true });
|
|
7331
7537
|
} catch (copyErr) {
|
|
7332
7538
|
try {
|
|
7333
|
-
|
|
7539
|
+
fs11__namespace.rmSync(partialDir, { recursive: true, force: true });
|
|
7334
7540
|
} catch {
|
|
7335
7541
|
}
|
|
7336
7542
|
throw copyErr;
|
|
7337
7543
|
}
|
|
7338
7544
|
}
|
|
7339
|
-
if (hadPrevious)
|
|
7545
|
+
if (hadPrevious) fs11__namespace.rmSync(backupDir, { recursive: true, force: true });
|
|
7340
7546
|
} catch (err) {
|
|
7341
|
-
if (hadPrevious &&
|
|
7342
|
-
if (
|
|
7343
|
-
|
|
7547
|
+
if (hadPrevious && fs11__namespace.existsSync(backupDir)) {
|
|
7548
|
+
if (fs11__namespace.existsSync(outDir)) fs11__namespace.rmSync(outDir, { recursive: true, force: true });
|
|
7549
|
+
fs11__namespace.renameSync(backupDir, outDir);
|
|
7344
7550
|
}
|
|
7345
7551
|
throw err;
|
|
7346
7552
|
}
|
|
@@ -7435,8 +7641,8 @@ function generateSnapshot(configInput, options) {
|
|
|
7435
7641
|
// first run cannot have marked them yet, so the name alone keeps a run from recording its own
|
|
7436
7642
|
// output — but only at these exact paths, not at every directory that shares the name.
|
|
7437
7643
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
7438
|
-
(directory) =>
|
|
7439
|
-
).filter((
|
|
7644
|
+
(directory) => path12__namespace.relative(config.cwd, path12__namespace.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
7645
|
+
).filter((relative7) => relative7.length > 0 && !relative7.startsWith(".."))
|
|
7440
7646
|
});
|
|
7441
7647
|
const files = [];
|
|
7442
7648
|
const monolithBodies = /* @__PURE__ */ new Map();
|
|
@@ -7456,12 +7662,12 @@ function generateSnapshot(configInput, options) {
|
|
|
7456
7662
|
let totalSizeBytes = 0;
|
|
7457
7663
|
let largeFileCount = 0;
|
|
7458
7664
|
for (const relativePath of collection.files) {
|
|
7459
|
-
const absolutePath =
|
|
7665
|
+
const absolutePath = path12__namespace.join(config.cwd, relativePath);
|
|
7460
7666
|
let stats;
|
|
7461
7667
|
try {
|
|
7462
|
-
stats =
|
|
7668
|
+
stats = fs11__namespace.statSync(absolutePath);
|
|
7463
7669
|
if (!stats.isFile()) {
|
|
7464
|
-
const nestedRepository = stats.isDirectory() &&
|
|
7670
|
+
const nestedRepository = stats.isDirectory() && fs11__namespace.existsSync(path12__namespace.join(absolutePath, ".git"));
|
|
7465
7671
|
skipped.push(
|
|
7466
7672
|
nestedRepository ? {
|
|
7467
7673
|
path: relativePath,
|
|
@@ -7481,7 +7687,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7481
7687
|
}
|
|
7482
7688
|
let buffer;
|
|
7483
7689
|
try {
|
|
7484
|
-
buffer =
|
|
7690
|
+
buffer = fs11__namespace.readFileSync(absolutePath);
|
|
7485
7691
|
} catch {
|
|
7486
7692
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
7487
7693
|
continue;
|
|
@@ -7811,12 +8017,12 @@ function generateSnapshot(configInput, options) {
|
|
|
7811
8017
|
let stagingDir = null;
|
|
7812
8018
|
try {
|
|
7813
8019
|
ensureParentDirectory(outDir, "outDir");
|
|
7814
|
-
|
|
8020
|
+
fs11__namespace.mkdirSync(tmpDir, { recursive: true });
|
|
7815
8021
|
cleanStaleRunDirs(tmpDir);
|
|
7816
8022
|
const snapshotId = crypto__namespace.randomUUID();
|
|
7817
8023
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7818
|
-
stagingDir =
|
|
7819
|
-
|
|
8024
|
+
stagingDir = path12__namespace.join(tmpDir, `${RUN_DIR_PREFIX}${process.pid}-${snapshotId.slice(0, 8)}`);
|
|
8025
|
+
fs11__namespace.mkdirSync(stagingDir);
|
|
7820
8026
|
writeOwnershipMarker(stagingDir, {
|
|
7821
8027
|
generator: GENERATOR_NAME,
|
|
7822
8028
|
version: GENERATOR_VERSION,
|
|
@@ -7826,41 +8032,41 @@ function generateSnapshot(configInput, options) {
|
|
|
7826
8032
|
id: snapshotId,
|
|
7827
8033
|
pid: process.pid
|
|
7828
8034
|
});
|
|
7829
|
-
const partsDir =
|
|
7830
|
-
const reportsDir =
|
|
7831
|
-
if (artifacts.parts)
|
|
8035
|
+
const partsDir = path12__namespace.join(stagingDir, "parts");
|
|
8036
|
+
const reportsDir = path12__namespace.join(stagingDir, "reports");
|
|
8037
|
+
if (artifacts.parts) fs11__namespace.mkdirSync(partsDir, { recursive: true });
|
|
7832
8038
|
if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
|
|
7833
|
-
|
|
8039
|
+
fs11__namespace.mkdirSync(reportsDir, { recursive: true });
|
|
7834
8040
|
}
|
|
7835
8041
|
if (artifacts.parts) {
|
|
7836
8042
|
for (const part of parts) {
|
|
7837
|
-
|
|
8043
|
+
fs11__namespace.writeFileSync(path12__namespace.join(partsDir, part.filename), part.markdown, "utf8");
|
|
7838
8044
|
}
|
|
7839
8045
|
}
|
|
7840
8046
|
if (projectTree !== null) {
|
|
7841
|
-
|
|
8047
|
+
fs11__namespace.writeFileSync(path12__namespace.join(reportsDir, "project-tree.md"), projectTree, "utf8");
|
|
7842
8048
|
}
|
|
7843
8049
|
if (artifacts.navigation) {
|
|
7844
|
-
|
|
8050
|
+
fs11__namespace.writeFileSync(path12__namespace.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
|
|
7845
8051
|
}
|
|
7846
8052
|
if (artifacts.audit) {
|
|
7847
|
-
|
|
7848
|
-
|
|
8053
|
+
fs11__namespace.writeFileSync(
|
|
8054
|
+
path12__namespace.join(reportsDir, "audit.md"),
|
|
7849
8055
|
generateAuditReport(manifest, secretFindings, config.secretRules),
|
|
7850
8056
|
"utf8"
|
|
7851
8057
|
);
|
|
7852
8058
|
}
|
|
7853
8059
|
if (artifacts.readme) {
|
|
7854
|
-
|
|
8060
|
+
fs11__namespace.writeFileSync(path12__namespace.join(stagingDir, "README.md"), generateReadme(manifest, artifacts), "utf8");
|
|
7855
8061
|
}
|
|
7856
8062
|
if (artifacts.manifest) {
|
|
7857
|
-
|
|
8063
|
+
fs11__namespace.writeFileSync(path12__namespace.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
7858
8064
|
}
|
|
7859
8065
|
if (monolithJson !== null) {
|
|
7860
|
-
|
|
8066
|
+
fs11__namespace.writeFileSync(path12__namespace.join(stagingDir, "full-codebase.json"), monolithJson, "utf8");
|
|
7861
8067
|
}
|
|
7862
8068
|
if (monolithMarkdown !== null) {
|
|
7863
|
-
|
|
8069
|
+
fs11__namespace.writeFileSync(path12__namespace.join(stagingDir, "full-codebase.md"), monolithMarkdown, "utf8");
|
|
7864
8070
|
}
|
|
7865
8071
|
writeOwnershipMarker(stagingDir, markerPayload(stagingDir, "complete", snapshotId, createdAt));
|
|
7866
8072
|
swapIntoPlace(stagingDir, outDir);
|
|
@@ -7869,7 +8075,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7869
8075
|
const pendingHook = runAfterGenerate(config, manifest);
|
|
7870
8076
|
const shipHookState = () => {
|
|
7871
8077
|
if (artifacts.manifest && manifest.warnings.length !== warningsBeforeHook) {
|
|
7872
|
-
writeManifestAtomically(
|
|
8078
|
+
writeManifestAtomically(path12__namespace.join(outDir, "manifest.json"), manifest);
|
|
7873
8079
|
}
|
|
7874
8080
|
writeOwnershipMarker(outDir, markerPayload(outDir, "complete", snapshotId, createdAt));
|
|
7875
8081
|
};
|
|
@@ -7892,7 +8098,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7892
8098
|
} catch (err) {
|
|
7893
8099
|
if (stagingDir !== null) {
|
|
7894
8100
|
try {
|
|
7895
|
-
|
|
8101
|
+
fs11__namespace.rmSync(stagingDir, { recursive: true, force: true });
|
|
7896
8102
|
} catch {
|
|
7897
8103
|
}
|
|
7898
8104
|
}
|
|
@@ -7930,14 +8136,17 @@ exports.detectPreset = detectPreset;
|
|
|
7930
8136
|
exports.explainPath = explainPath;
|
|
7931
8137
|
exports.fenceForContent = fenceForContent;
|
|
7932
8138
|
exports.fenceForText = fenceForText;
|
|
8139
|
+
exports.firstRunMessage = firstRunMessage;
|
|
7933
8140
|
exports.generateProjectTree = generateProjectTree;
|
|
7934
8141
|
exports.generateSnapshot = generateSnapshot;
|
|
7935
8142
|
exports.generateSnapshotAsync = generateSnapshotAsync;
|
|
7936
8143
|
exports.getPreset = getPreset;
|
|
7937
8144
|
exports.globToRegExp = globToRegExp;
|
|
8145
|
+
exports.hasPriorSnapshot = hasPriorSnapshot;
|
|
7938
8146
|
exports.isBinaryBuffer = isBinaryBuffer;
|
|
7939
8147
|
exports.isBinaryExtension = isBinaryExtension;
|
|
7940
8148
|
exports.isBinaryFile = isBinaryFile;
|
|
8149
|
+
exports.isEffectivelyEmpty = isEffectivelyEmpty;
|
|
7941
8150
|
exports.isInteractive = isInteractive;
|
|
7942
8151
|
exports.isResolvedConfig = isResolvedConfig;
|
|
7943
8152
|
exports.isSensitivePath = isSensitivePath;
|
|
@@ -7951,6 +8160,7 @@ exports.matchesExclude = matchesExclude;
|
|
|
7951
8160
|
exports.normalizeResolvedConfig = normalizeResolvedConfig;
|
|
7952
8161
|
exports.parseByteSize = parseByteSize;
|
|
7953
8162
|
exports.partitionFiles = partitionFiles;
|
|
8163
|
+
exports.planFirstRun = planFirstRun;
|
|
7954
8164
|
exports.profileForPath = profileForPath;
|
|
7955
8165
|
exports.renderConfig = renderConfig;
|
|
7956
8166
|
exports.renderDiffText = renderDiffText;
|