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.
@@ -1,9 +1,9 @@
1
1
  import * as crypto from 'crypto';
2
2
  import { createHash } from 'crypto';
3
3
  import { AsyncLocalStorage } from 'async_hooks';
4
- import * as fs10 from 'fs';
4
+ import * as fs11 from 'fs';
5
5
  import * as os from 'os';
6
- import * as path11 from 'path';
6
+ import * as path12 from 'path';
7
7
  import ts from 'typescript';
8
8
  import { createRequire } from 'module';
9
9
  import { pathToFileURL } from 'url';
@@ -234,9 +234,9 @@ var JAVA_METHOD = /^\s+(?:public|private|protected)\s+(?:static\s+)?(?:final\s+)
234
234
  var jvmProfile = {
235
235
  id: "jvm",
236
236
  extensions: [".java", ".kt", ".kts", ".scala"],
237
- summarize(path12, content) {
237
+ summarize(path13, content) {
238
238
  const summary = emptySummary();
239
- const lower = path12.toLowerCase();
239
+ const lower = path13.toLowerCase();
240
240
  const isJava = lower.endsWith(".java");
241
241
  const isKotlin = lower.endsWith(".kt") || lower.endsWith(".kts");
242
242
  eachLine(content, (line) => {
@@ -689,16 +689,16 @@ function summarizeTypeScriptSource(relativePath, content) {
689
689
  // src/ast/profiles/sfc.ts
690
690
  var SCRIPT_BLOCK = /<script[^>]*>([\s\S]*?)<\/script>/gi;
691
691
  var ASTRO_FRONTMATTER = /^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?=\r?\n|$)/;
692
- function componentName(path12) {
693
- const base = path12.split("/").pop() ?? path12;
692
+ function componentName(path13) {
693
+ const base = path13.split("/").pop() ?? path13;
694
694
  return base.replace(/\.[^.]+$/, "");
695
695
  }
696
696
  var sfcProfile = {
697
697
  id: "sfc",
698
698
  extensions: [".vue", ".svelte", ".astro"],
699
- summarize(path12, content) {
699
+ summarize(path13, content) {
700
700
  let script = "";
701
- if (path12.toLowerCase().endsWith(".astro")) {
701
+ if (path13.toLowerCase().endsWith(".astro")) {
702
702
  const frontmatter = ASTRO_FRONTMATTER.exec(content);
703
703
  if (frontmatter) script += `${frontmatter[1]}
704
704
  `;
@@ -707,7 +707,7 @@ var sfcProfile = {
707
707
  script += `${match[1]}
708
708
  `;
709
709
  }
710
- const name = componentName(path12);
710
+ const name = componentName(path13);
711
711
  if (!script.trim()) {
712
712
  const summary2 = emptySummary();
713
713
  if (name) {
@@ -741,15 +741,15 @@ for (const profile of LANGUAGE_PROFILES) {
741
741
  BY_EXTENSION.set(extension.toLowerCase(), profile);
742
742
  }
743
743
  }
744
- function profileForPath(path12) {
745
- const lower = path12.toLowerCase();
744
+ function profileForPath(path13) {
745
+ const lower = path13.toLowerCase();
746
746
  const dot = lower.lastIndexOf(".");
747
747
  if (dot === -1) return null;
748
748
  return BY_EXTENSION.get(lower.slice(dot)) ?? null;
749
749
  }
750
- function summarizeWithProfile(path12, content) {
751
- const profile = profileForPath(path12);
752
- return profile ? profile.summarize(path12, content) : null;
750
+ function summarizeWithProfile(path13, content) {
751
+ const profile = profileForPath(path13);
752
+ return profile ? profile.summarize(path13, content) : null;
753
753
  }
754
754
 
755
755
  // src/ast/structured.ts
@@ -920,15 +920,15 @@ var SKIPPED_WALK_DIRS = /* @__PURE__ */ new Set([
920
920
  ]);
921
921
  function readSmallFile(fullPath) {
922
922
  try {
923
- const stats = fs10.statSync(fullPath);
923
+ const stats = fs11.statSync(fullPath);
924
924
  if (!stats.isFile() || stats.size > MANIFEST_MAX_BYTES) return null;
925
- return fs10.readFileSync(fullPath, "utf8");
925
+ return fs11.readFileSync(fullPath, "utf8");
926
926
  } catch {
927
927
  return null;
928
928
  }
929
929
  }
930
930
  function readManifest(cwd, name) {
931
- return readSmallFile(path11.join(cwd, name));
931
+ return readSmallFile(path12.join(cwd, name));
932
932
  }
933
933
  function dependenciesFromPackageJson(raw) {
934
934
  const names = /* @__PURE__ */ new Set();
@@ -988,7 +988,7 @@ function pnpmWorkspacePatterns(raw) {
988
988
  function childDirectories(cwd, parent) {
989
989
  let entries;
990
990
  try {
991
- entries = fs10.readdirSync(path11.join(cwd, parent), { withFileTypes: true });
991
+ entries = fs11.readdirSync(path12.join(cwd, parent), { withFileTypes: true });
992
992
  } catch {
993
993
  return [];
994
994
  }
@@ -1023,13 +1023,13 @@ function workspacePackageDirs(cwd, patterns) {
1023
1023
  function scanWorkspacePackages(cwd, patterns) {
1024
1024
  const files = /* @__PURE__ */ new Set();
1025
1025
  const dependencies = /* @__PURE__ */ new Set();
1026
- for (const relative6 of workspacePackageDirs(cwd, patterns)) {
1027
- const full = path11.join(cwd, relative6);
1026
+ for (const relative7 of workspacePackageDirs(cwd, patterns)) {
1027
+ const full = path12.join(cwd, relative7);
1028
1028
  try {
1029
- for (const entry of fs10.readdirSync(full, { withFileTypes: true })) files.add(entry.name);
1029
+ for (const entry of fs11.readdirSync(full, { withFileTypes: true })) files.add(entry.name);
1030
1030
  } catch {
1031
1031
  }
1032
- const raw = readSmallFile(path11.join(full, "package.json"));
1032
+ const raw = readSmallFile(path12.join(full, "package.json"));
1033
1033
  if (raw !== null) {
1034
1034
  for (const name of dependenciesFromPackageJson(raw)) dependencies.add(name);
1035
1035
  }
@@ -1046,7 +1046,7 @@ function countMarkdownFiles(cwd) {
1046
1046
  directoriesSeen += 1;
1047
1047
  let entries;
1048
1048
  try {
1049
- entries = fs10.readdirSync(current.dir, { withFileTypes: true });
1049
+ entries = fs11.readdirSync(current.dir, { withFileTypes: true });
1050
1050
  } catch {
1051
1051
  continue;
1052
1052
  }
@@ -1057,7 +1057,7 @@ function countMarkdownFiles(cwd) {
1057
1057
  }
1058
1058
  if (!entry.isDirectory() || current.depth >= 2) continue;
1059
1059
  if (entry.name.startsWith(".") || SKIPPED_WALK_DIRS.has(entry.name)) continue;
1060
- queue.push({ dir: path11.join(current.dir, entry.name), depth: current.depth + 1 });
1060
+ queue.push({ dir: path12.join(current.dir, entry.name), depth: current.depth + 1 });
1061
1061
  }
1062
1062
  }
1063
1063
  return count;
@@ -1066,7 +1066,7 @@ function buildPresetContext(cwd) {
1066
1066
  const files = /* @__PURE__ */ new Set();
1067
1067
  const dirs = /* @__PURE__ */ new Set();
1068
1068
  try {
1069
- for (const entry of fs10.readdirSync(cwd, { withFileTypes: true })) {
1069
+ for (const entry of fs11.readdirSync(cwd, { withFileTypes: true })) {
1070
1070
  if (entry.isDirectory()) dirs.add(entry.name);
1071
1071
  else if (entry.isFile()) files.add(entry.name);
1072
1072
  }
@@ -1083,7 +1083,7 @@ function buildPresetContext(cwd) {
1083
1083
  const isWorkspace = files.has("pnpm-workspace.yaml") || files.has("nx.json") || files.has("turbo.json") || files.has("lerna.json") || packageJson !== void 0 && packageJson.includes('"workspaces"') || declaredWorkspaces.length > 0;
1084
1084
  let workspace = { files: /* @__PURE__ */ new Set(), dependencies: /* @__PURE__ */ new Set() };
1085
1085
  if (isWorkspace) {
1086
- const pnpmRaw = files.has("pnpm-workspace.yaml") ? readSmallFile(path11.join(cwd, "pnpm-workspace.yaml")) : null;
1086
+ const pnpmRaw = files.has("pnpm-workspace.yaml") ? readSmallFile(path12.join(cwd, "pnpm-workspace.yaml")) : null;
1087
1087
  const declared = pnpmRaw === null ? [] : pnpmWorkspacePatterns(pnpmRaw);
1088
1088
  const patterns = declared.length > 0 ? declared : declaredWorkspaces.length > 0 ? declaredWorkspaces : DEFAULT_WORKSPACE_PATTERNS;
1089
1089
  workspace = scanWorkspacePackages(cwd, patterns);
@@ -1101,25 +1101,41 @@ function buildPresetContext(cwd) {
1101
1101
  markdownCount: files.has("package.json") ? 0 : countMarkdownFiles(cwd)
1102
1102
  };
1103
1103
  }
1104
- function hasFile(context, name) {
1105
- return context.files.has(name);
1104
+
1105
+ // src/presets/evidence.ts
1106
+ function fileEvidence(context, name) {
1107
+ return context.files.has(name) ? name : null;
1106
1108
  }
1107
- function hasFileStartingWith(context, prefix) {
1109
+ function fileStartingWithEvidence(context, prefix) {
1108
1110
  for (const file of context.files) {
1109
- if (file.startsWith(prefix)) return true;
1111
+ if (file.startsWith(prefix)) return file;
1110
1112
  }
1111
1113
  for (const file of context.workspaceFiles) {
1112
- if (file.startsWith(prefix)) return true;
1114
+ if (file.startsWith(prefix)) return file;
1113
1115
  }
1114
- return false;
1116
+ return null;
1117
+ }
1118
+ function dependencyEvidence(context, ...names) {
1119
+ for (const name of names) {
1120
+ if (context.dependencies.has(name)) return `depends on ${name}`;
1121
+ }
1122
+ return null;
1115
1123
  }
1116
- function manifestContains(context, manifest, needle) {
1124
+ function manifestEvidence(context, manifest, needle) {
1117
1125
  const content = context.manifests[manifest];
1118
- if (!content) return false;
1119
- return content.toLowerCase().includes(needle.toLowerCase());
1126
+ if (!content) return null;
1127
+ if (!content.toLowerCase().includes(needle.toLowerCase())) return null;
1128
+ return `${needle} in ${manifest}`;
1120
1129
  }
1121
- function hasDependency(context, ...names) {
1122
- return names.some((name) => context.dependencies.has(name));
1130
+ function suffixEvidence(context, suffix) {
1131
+ for (const file of context.files) {
1132
+ if (file.endsWith(suffix)) return file;
1133
+ }
1134
+ return null;
1135
+ }
1136
+ function markdownEvidence(context) {
1137
+ if (context.markdownCount < 3 || context.files.has("package.json")) return null;
1138
+ return `${context.markdownCount} markdown files, no package.json`;
1123
1139
  }
1124
1140
 
1125
1141
  // src/presets/helpers.ts
@@ -1327,7 +1343,7 @@ var JS_PRESETS = [
1327
1343
  {
1328
1344
  name: "nextjs",
1329
1345
  priority: 100,
1330
- detect: (context) => hasFileStartingWith(context, "next.config") || hasDependency(context, "next"),
1346
+ detect: (context) => fileStartingWithEvidence(context, "next.config") ?? dependencyEvidence(context, "next"),
1331
1347
  layers: NEXTJS_LAYERS,
1332
1348
  kinds: NEXTJS_KINDS,
1333
1349
  exclude: NEXT_LIKE_EXCLUDES,
@@ -1336,7 +1352,7 @@ var JS_PRESETS = [
1336
1352
  {
1337
1353
  name: "nuxt",
1338
1354
  priority: 98,
1339
- detect: (context) => hasFileStartingWith(context, "nuxt.config") || hasDependency(context, "nuxt", "nuxt3"),
1355
+ detect: (context) => fileStartingWithEvidence(context, "nuxt.config") ?? dependencyEvidence(context, "nuxt", "nuxt3"),
1340
1356
  layers: [
1341
1357
  layer("pages", "pages/", "src/pages/"),
1342
1358
  layer("components", "components/", "src/components/"),
@@ -1360,7 +1376,7 @@ var JS_PRESETS = [
1360
1376
  {
1361
1377
  name: "sveltekit",
1362
1378
  priority: 96,
1363
- detect: (context) => hasFileStartingWith(context, "svelte.config") || hasDependency(context, "@sveltejs/kit", "svelte"),
1379
+ detect: (context) => fileStartingWithEvidence(context, "svelte.config") ?? dependencyEvidence(context, "@sveltejs/kit", "svelte"),
1364
1380
  layers: [
1365
1381
  layer("routes", "src/routes/", "routes/"),
1366
1382
  layer("lib", "src/lib/", "lib/"),
@@ -1381,7 +1397,7 @@ var JS_PRESETS = [
1381
1397
  {
1382
1398
  name: "remix",
1383
1399
  priority: 94,
1384
- detect: (context) => hasDependency(context, "@remix-run/react", "@remix-run/node", "@remix-run/dev"),
1400
+ detect: (context) => dependencyEvidence(context, "@remix-run/react", "@remix-run/node", "@remix-run/dev"),
1385
1401
  layers: [
1386
1402
  layer("routes", "app/routes/", "routes/"),
1387
1403
  layer("components", "app/components/"),
@@ -1395,7 +1411,7 @@ var JS_PRESETS = [
1395
1411
  {
1396
1412
  name: "astro",
1397
1413
  priority: 92,
1398
- detect: (context) => hasFileStartingWith(context, "astro.config") || hasDependency(context, "astro"),
1414
+ detect: (context) => fileStartingWithEvidence(context, "astro.config") ?? dependencyEvidence(context, "astro"),
1399
1415
  layers: [
1400
1416
  layer("pages", "src/pages/"),
1401
1417
  layer("layouts", "src/layouts/"),
@@ -1410,7 +1426,7 @@ var JS_PRESETS = [
1410
1426
  {
1411
1427
  name: "angular",
1412
1428
  priority: 90,
1413
- detect: (context) => hasFile(context, "angular.json") || hasDependency(context, "@angular/core"),
1429
+ detect: (context) => fileEvidence(context, "angular.json") ?? dependencyEvidence(context, "@angular/core"),
1414
1430
  layers: [
1415
1431
  layer("app", "src/app/"),
1416
1432
  layer("features", "src/app/features/"),
@@ -1434,7 +1450,7 @@ var JS_PRESETS = [
1434
1450
  {
1435
1451
  name: "solid",
1436
1452
  priority: 88,
1437
- detect: (context) => hasDependency(context, "solid-js", "solid-start"),
1453
+ detect: (context) => dependencyEvidence(context, "solid-js", "solid-start"),
1438
1454
  layers: [
1439
1455
  layer("pages", "src/routes/", "src/pages/"),
1440
1456
  layer("components", "src/components/"),
@@ -1447,7 +1463,15 @@ var JS_PRESETS = [
1447
1463
  {
1448
1464
  name: "vue-vite",
1449
1465
  priority: 86,
1450
- detect: (context) => hasDependency(context, "vue") && (hasDependency(context, "vite") || hasFileStartingWith(context, "vite.config")),
1466
+ // `vue` is the decisive signal; `vite` (or its config file) only says the app is
1467
+ // built with Vite rather than shipped pre-compiled, so it is required but not what
1468
+ // gets reported.
1469
+ detect: (context) => {
1470
+ const framework = dependencyEvidence(context, "vue");
1471
+ if (framework === null) return null;
1472
+ const build = dependencyEvidence(context, "vite") ?? fileStartingWithEvidence(context, "vite.config");
1473
+ return build === null ? null : framework;
1474
+ },
1451
1475
  layers: [
1452
1476
  layer("pages", "src/pages/", "src/views/"),
1453
1477
  layer("components", "src/components/"),
@@ -1472,7 +1496,12 @@ var JS_PRESETS = [
1472
1496
  {
1473
1497
  name: "react-vite",
1474
1498
  priority: 84,
1475
- detect: (context) => hasDependency(context, "react") && (hasDependency(context, "vite") || hasFileStartingWith(context, "vite.config")),
1499
+ detect: (context) => {
1500
+ const framework = dependencyEvidence(context, "react");
1501
+ if (framework === null) return null;
1502
+ const build = dependencyEvidence(context, "vite") ?? fileStartingWithEvidence(context, "vite.config");
1503
+ return build === null ? null : framework;
1504
+ },
1476
1505
  layers: VITE_LAYERS,
1477
1506
  kinds: VITE_KINDS,
1478
1507
  exclude: [".vite", "dist"],
@@ -1481,7 +1510,7 @@ var JS_PRESETS = [
1481
1510
  {
1482
1511
  name: "nest",
1483
1512
  priority: 80,
1484
- detect: (context) => hasDependency(context, "@nestjs/core"),
1513
+ detect: (context) => dependencyEvidence(context, "@nestjs/core"),
1485
1514
  layers: [
1486
1515
  layer("modules", "src/modules/", "modules/"),
1487
1516
  layer("controllers", "src/controllers/", "controllers/"),
@@ -1507,7 +1536,7 @@ var JS_PRESETS = [
1507
1536
  {
1508
1537
  name: "node-express",
1509
1538
  priority: 78,
1510
- detect: (context) => hasDependency(context, "express", "fastify", "koa", "@hapi/hapi"),
1539
+ detect: (context) => dependencyEvidence(context, "express", "fastify", "koa", "@hapi/hapi"),
1511
1540
  layers: NODE_LAYERS,
1512
1541
  kinds: NODE_KINDS,
1513
1542
  exclude: ["dist"],
@@ -1518,7 +1547,7 @@ var JS_PRESETS = [
1518
1547
  // A bare package.json is often just tooling around a non-JS project, so it
1519
1548
  // ranks below every definitive language manifest.
1520
1549
  priority: 45,
1521
- detect: (context) => hasFile(context, "package.json"),
1550
+ detect: (context) => fileEvidence(context, "package.json"),
1522
1551
  layers: [
1523
1552
  layer("src", "src/"),
1524
1553
  layer("lib", "lib/"),
@@ -1532,13 +1561,12 @@ var JS_PRESETS = [
1532
1561
  ];
1533
1562
 
1534
1563
  // src/presets/lang-presets.ts
1535
- var anyFileEndingWith = (context, suffix) => Array.from(context.files).some((file) => file.endsWith(suffix));
1536
1564
  var DIR = (name) => `${name}/`;
1537
1565
  var LANGUAGE_PRESETS = [
1538
1566
  {
1539
1567
  name: "python-django",
1540
1568
  priority: 56,
1541
- detect: (context) => hasFile(context, "manage.py") || manifestContains(context, "pyproject.toml", "django") || manifestContains(context, "requirements.txt", "django"),
1569
+ detect: (context) => fileEvidence(context, "manage.py") ?? manifestEvidence(context, "pyproject.toml", "django") ?? manifestEvidence(context, "requirements.txt", "django"),
1542
1570
  layers: [
1543
1571
  layer("views", DIR("views"), DIR("app/views"), DIR("apps/views")),
1544
1572
  layer("models", DIR("models"), DIR("app/models"), DIR("apps/models")),
@@ -1564,7 +1592,7 @@ var LANGUAGE_PRESETS = [
1564
1592
  {
1565
1593
  name: "python-fastapi",
1566
1594
  priority: 54,
1567
- detect: (context) => manifestContains(context, "requirements.txt", "fastapi") || manifestContains(context, "requirements.txt", "flask") || manifestContains(context, "pyproject.toml", "fastapi") || manifestContains(context, "pyproject.toml", "flask"),
1595
+ detect: (context) => manifestEvidence(context, "requirements.txt", "fastapi") ?? manifestEvidence(context, "requirements.txt", "flask") ?? manifestEvidence(context, "pyproject.toml", "fastapi") ?? manifestEvidence(context, "pyproject.toml", "flask"),
1568
1596
  layers: [
1569
1597
  layer("routers", DIR("routers"), DIR("routes"), DIR("api")),
1570
1598
  layer("models", DIR("models"), DIR("db")),
@@ -1588,7 +1616,7 @@ var LANGUAGE_PRESETS = [
1588
1616
  name: "python-lib",
1589
1617
  // Below node-lib: a Node repo with a stray .py file is still a Node repo.
1590
1618
  priority: 30,
1591
- detect: (context) => hasFile(context, "pyproject.toml") || hasFile(context, "setup.py") || hasFile(context, "requirements.txt") || anyFileEndingWith(context, ".py"),
1619
+ detect: (context) => fileEvidence(context, "pyproject.toml") ?? fileEvidence(context, "setup.py") ?? fileEvidence(context, "requirements.txt") ?? suffixEvidence(context, ".py"),
1592
1620
  layers: [
1593
1621
  layer("package", DIR("src"), DIR("lib")),
1594
1622
  layer("tests", DIR("tests"), DIR("test")),
@@ -1601,7 +1629,7 @@ var LANGUAGE_PRESETS = [
1601
1629
  {
1602
1630
  name: "go",
1603
1631
  priority: 50,
1604
- detect: (context) => hasFile(context, "go.mod"),
1632
+ detect: (context) => fileEvidence(context, "go.mod"),
1605
1633
  layers: [
1606
1634
  layer("cmd", "cmd/"),
1607
1635
  layer("internal", "internal/"),
@@ -1625,7 +1653,7 @@ var LANGUAGE_PRESETS = [
1625
1653
  {
1626
1654
  name: "rust",
1627
1655
  priority: 48,
1628
- detect: (context) => hasFile(context, "Cargo.toml"),
1656
+ detect: (context) => fileEvidence(context, "Cargo.toml"),
1629
1657
  layers: [
1630
1658
  layer("src", "src/"),
1631
1659
  layer("crates", "crates/"),
@@ -1641,7 +1669,7 @@ var LANGUAGE_PRESETS = [
1641
1669
  {
1642
1670
  name: "java-spring",
1643
1671
  priority: 46,
1644
- detect: (context) => hasFile(context, "pom.xml") || hasFile(context, "build.gradle") || hasFile(context, "build.gradle.kts"),
1672
+ detect: (context) => fileEvidence(context, "pom.xml") ?? fileEvidence(context, "build.gradle") ?? fileEvidence(context, "build.gradle.kts"),
1645
1673
  layers: [
1646
1674
  layer("api", "src/main/java/"),
1647
1675
  layer("test", "src/test/java/"),
@@ -1664,7 +1692,7 @@ var LANGUAGE_PRESETS = [
1664
1692
  {
1665
1693
  name: "dotnet",
1666
1694
  priority: 44,
1667
- detect: (context) => anyFileEndingWith(context, ".csproj") || anyFileEndingWith(context, ".sln") || hasFile(context, "global.json"),
1695
+ detect: (context) => suffixEvidence(context, ".csproj") ?? suffixEvidence(context, ".sln") ?? fileEvidence(context, "global.json"),
1668
1696
  layers: [
1669
1697
  layer("web", "src/", "Web/"),
1670
1698
  layer("domain", "Domain/", "Core/"),
@@ -1687,7 +1715,7 @@ var LANGUAGE_PRESETS = [
1687
1715
  {
1688
1716
  name: "php-laravel",
1689
1717
  priority: 42,
1690
- detect: (context) => hasFile(context, "artisan") || manifestContains(context, "composer.json", "laravel") || hasFile(context, "composer.json"),
1718
+ detect: (context) => fileEvidence(context, "artisan") ?? manifestEvidence(context, "composer.json", "laravel") ?? fileEvidence(context, "composer.json"),
1691
1719
  layers: [
1692
1720
  layer("app", "app/"),
1693
1721
  layer("routes", "routes/"),
@@ -1713,7 +1741,7 @@ var LANGUAGE_PRESETS = [
1713
1741
  {
1714
1742
  name: "ruby-rails",
1715
1743
  priority: 40,
1716
- detect: (context) => hasFile(context, "Gemfile") || hasFile(context, "config.ru") || hasFile(context, "Rakefile"),
1744
+ detect: (context) => fileEvidence(context, "Gemfile") ?? fileEvidence(context, "config.ru") ?? fileEvidence(context, "Rakefile"),
1717
1745
  layers: [
1718
1746
  layer("app", "app/"),
1719
1747
  layer("controllers", "app/controllers/"),
@@ -1786,7 +1814,7 @@ var SHAPE_PRESETS = [
1786
1814
  {
1787
1815
  name: "monorepo",
1788
1816
  priority: 70,
1789
- detect: (context) => hasFile(context, "pnpm-workspace.yaml") || hasFile(context, "nx.json") || hasFile(context, "turbo.json") || hasFile(context, "lerna.json") || manifestContains(context, "package.json", '"workspaces"'),
1817
+ detect: (context) => fileEvidence(context, "pnpm-workspace.yaml") ?? fileEvidence(context, "nx.json") ?? fileEvidence(context, "turbo.json") ?? fileEvidence(context, "lerna.json") ?? manifestEvidence(context, "package.json", '"workspaces"'),
1790
1818
  layers: [
1791
1819
  layer("apps", "apps/"),
1792
1820
  layer("packages", "packages/"),
@@ -1800,7 +1828,7 @@ var SHAPE_PRESETS = [
1800
1828
  {
1801
1829
  name: "infra",
1802
1830
  priority: 24,
1803
- detect: (context) => Array.from(context.files).some((file) => file.endsWith(".tf")) || hasFile(context, "Chart.yaml") || hasFile(context, "kustomization.yaml") || hasFile(context, "docker-compose.yml") || hasFile(context, "docker-compose.yaml"),
1831
+ detect: (context) => suffixEvidence(context, ".tf") ?? fileEvidence(context, "Chart.yaml") ?? fileEvidence(context, "kustomization.yaml") ?? fileEvidence(context, "docker-compose.yml") ?? fileEvidence(context, "docker-compose.yaml"),
1804
1832
  layers: [
1805
1833
  layer("modules", "modules/"),
1806
1834
  layer("environments", "env/", "environments/"),
@@ -1822,7 +1850,7 @@ var SHAPE_PRESETS = [
1822
1850
  // up to two directories down (bounded in the context builder). Counting
1823
1851
  // only the root turned every real documentation repository — README.md plus
1824
1852
  // the pages under `docs/` — into `generic`.
1825
- detect: (context) => context.markdownCount >= 3 && !hasFile(context, "package.json"),
1853
+ detect: (context) => markdownEvidence(context),
1826
1854
  layers: [
1827
1855
  layer("content", "content/", "docs/"),
1828
1856
  layer("assets", "assets/", "static/", "public/"),
@@ -1835,7 +1863,10 @@ var SHAPE_PRESETS = [
1835
1863
  {
1836
1864
  name: "generic",
1837
1865
  priority: 0,
1838
- detect: () => true,
1866
+ // No detector: this is the fallback, and `detectPresetFromContext` reaches it only
1867
+ // when every other preset reported no evidence. `fallback: true` is what lets the
1868
+ // wizard tell "I recognised this" apart from "I gave up".
1869
+ fallback: true,
1839
1870
  layers: DEFAULT_LAYERS,
1840
1871
  kinds: DEFAULT_KINDS,
1841
1872
  exclude: [],
@@ -1867,20 +1898,27 @@ function detectPresetFromContext(context, forced) {
1867
1898
  if (forced !== void 0) {
1868
1899
  const preset = BY_NAME.get(forced);
1869
1900
  if (!preset) throw new Error(unknownPresetMessage(forced));
1870
- return toDetected(preset);
1901
+ return toDetected(preset, null);
1871
1902
  }
1872
1903
  for (const preset of PRESETS) {
1873
- if (preset.detect(context)) return toDetected(preset);
1904
+ const reason = preset.detect?.(context) ?? null;
1905
+ if (reason !== null) return toDetected(preset, reason);
1874
1906
  }
1875
- return toDetected(PRESETS[PRESETS.length - 1]);
1907
+ const fallback = PRESETS.find((preset) => preset.fallback === true);
1908
+ if (fallback === void 0) {
1909
+ throw new Error("No fallback preset is defined. Exactly one preset must set `fallback: true`.");
1910
+ }
1911
+ return toDetected(fallback, null, true);
1876
1912
  }
1877
- function toDetected(preset) {
1913
+ function toDetected(preset, reason, fallback = false) {
1878
1914
  return {
1879
1915
  presetName: preset.name,
1880
1916
  layers: preset.layers,
1881
1917
  kinds: preset.kinds,
1882
1918
  exclude: preset.exclude ? [...preset.exclude] : [],
1883
- recommendedBudget: preset.recommendedBudget ?? null
1919
+ recommendedBudget: preset.recommendedBudget ?? null,
1920
+ reason,
1921
+ fallback
1884
1922
  };
1885
1923
  }
1886
1924
  function detectPreset(cwd, forced) {
@@ -2320,14 +2358,14 @@ var PRESET_IGNORES = new Set(
2320
2358
  );
2321
2359
  function readLinkTarget(linkPath, fallback) {
2322
2360
  try {
2323
- return fs10.readlinkSync(linkPath);
2361
+ return fs11.readlinkSync(linkPath);
2324
2362
  } catch {
2325
2363
  return fallback;
2326
2364
  }
2327
2365
  }
2328
2366
  function isInside(root, candidate) {
2329
- const relative6 = path11.relative(root, candidate);
2330
- return relative6 === "" || !relative6.startsWith(`..${path11.sep}`) && relative6 !== ".." && !path11.isAbsolute(relative6);
2367
+ const relative7 = path12.relative(root, candidate);
2368
+ return relative7 === "" || !relative7.startsWith(`..${path12.sep}`) && relative7 !== ".." && !path12.isAbsolute(relative7);
2331
2369
  }
2332
2370
  function getGitInfo(cwd, fallbackBranch = "main") {
2333
2371
  try {
@@ -2532,10 +2570,10 @@ function trimUnescapedTrailingSpaces(line) {
2532
2570
  return line.slice(0, end);
2533
2571
  }
2534
2572
  function readGitignoreRules(dir) {
2535
- const gitignorePath2 = path11.join(dir, ".gitignore");
2573
+ const gitignorePath2 = path12.join(dir, ".gitignore");
2536
2574
  let raw;
2537
2575
  try {
2538
- raw = fs10.readFileSync(gitignorePath2, "utf8");
2576
+ raw = fs11.readFileSync(gitignorePath2, "utf8");
2539
2577
  } catch {
2540
2578
  return [];
2541
2579
  }
@@ -2662,7 +2700,7 @@ function isToolOwnedDirName(name) {
2662
2700
  }
2663
2701
  function hasSnapshotMarker(target) {
2664
2702
  try {
2665
- return fs10.statSync(target).isDirectory() && fs10.existsSync(path11.join(target, ".codebase-snapshot"));
2703
+ return fs11.statSync(target).isDirectory() && fs11.existsSync(path12.join(target, ".codebase-snapshot"));
2666
2704
  } catch {
2667
2705
  return false;
2668
2706
  }
@@ -2676,7 +2714,7 @@ function makeToolOwnedDecider(cwd, toolOwnedPaths) {
2676
2714
  const prefix = parts.slice(0, depth).join("/");
2677
2715
  let hasMarker = markers.get(prefix);
2678
2716
  if (hasMarker === void 0) {
2679
- hasMarker = fs10.existsSync(path11.join(cwd, prefix, ".codebase-snapshot"));
2717
+ hasMarker = fs11.existsSync(path12.join(cwd, prefix, ".codebase-snapshot"));
2680
2718
  markers.set(prefix, hasMarker);
2681
2719
  }
2682
2720
  if (hasMarker) return true;
@@ -2691,7 +2729,7 @@ function makeLinkProbe() {
2691
2729
  let known = cache.get(absolutePath);
2692
2730
  if (known === void 0) {
2693
2731
  try {
2694
- known = fs10.lstatSync(absolutePath).isSymbolicLink();
2732
+ known = fs11.lstatSync(absolutePath).isSymbolicLink();
2695
2733
  } catch {
2696
2734
  known = false;
2697
2735
  }
@@ -2704,7 +2742,7 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
2704
2742
  const isLink = makeLinkProbe();
2705
2743
  let rootReal = cwd;
2706
2744
  try {
2707
- rootReal = fs10.realpathSync(cwd);
2745
+ rootReal = fs11.realpathSync(cwd);
2708
2746
  } catch {
2709
2747
  }
2710
2748
  const reported = /* @__PURE__ */ new Set();
@@ -2714,7 +2752,7 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
2714
2752
  let linkedDir = null;
2715
2753
  for (let depth = 1; depth <= parts.length; depth += 1) {
2716
2754
  const prefix = parts.slice(0, depth).join("/");
2717
- if (isLink(path11.join(cwd, prefix))) {
2755
+ if (isLink(path12.join(cwd, prefix))) {
2718
2756
  linkedDir = prefix;
2719
2757
  break;
2720
2758
  }
@@ -2725,10 +2763,10 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
2725
2763
  }
2726
2764
  if (reported.has(linkedDir)) continue;
2727
2765
  reported.add(linkedDir);
2728
- const absolute = path11.join(cwd, linkedDir);
2766
+ const absolute = path12.join(cwd, linkedDir);
2729
2767
  let targetReal = absolute;
2730
2768
  try {
2731
- targetReal = fs10.realpathSync(absolute);
2769
+ targetReal = fs11.realpathSync(absolute);
2732
2770
  } catch {
2733
2771
  }
2734
2772
  const target = readLinkTarget(absolute, targetReal);
@@ -2805,7 +2843,7 @@ function collectFiles(cwd, options = {}) {
2805
2843
  "Discovery mode is `git`, but git could not list this directory's files. Run inside a working repository, or pass --discovery walk (or auto) to scan the tree directly."
2806
2844
  );
2807
2845
  }
2808
- if (fs10.existsSync(path11.join(cwd, ".git"))) {
2846
+ if (fs11.existsSync(path12.join(cwd, ".git"))) {
2809
2847
  throw new Error(
2810
2848
  "This directory is a git repository, but git could not list its files. A plain walk honours .gitignore files, but git also applies .git/info/exclude and your global excludes file, includes files that are tracked although a .gitignore now matches them, and treats a submodule as a single entry. Fix the repository (git status should work), run outside git, or pass --discovery walk to accept those differences."
2811
2849
  );
@@ -2864,7 +2902,7 @@ function collectFiles(cwd, options = {}) {
2864
2902
  }
2865
2903
  function rootRealFor(root) {
2866
2904
  try {
2867
- return fs10.realpathSync(root);
2905
+ return fs11.realpathSync(root);
2868
2906
  } catch {
2869
2907
  return root;
2870
2908
  }
@@ -2872,18 +2910,18 @@ function rootRealFor(root) {
2872
2910
  function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PURE__ */ new Set(), parentGitignoreLayers = []) {
2873
2911
  const results = { files: [], skipped: [] };
2874
2912
  const stack = [];
2875
- const relativeToBase = (target) => path11.relative(baseDir, target).replace(/\\/g, "/") || ".";
2913
+ const relativeToBase = (target) => path12.relative(baseDir, target).replace(/\\/g, "/") || ".";
2876
2914
  const enter = (current, layers) => {
2877
2915
  if (visited.has(current)) return false;
2878
2916
  visited.add(current);
2879
2917
  const localRules = readGitignoreRules(current);
2880
2918
  const gitignoreLayers = localRules.length === 0 ? layers : [
2881
2919
  ...layers,
2882
- { prefix: path11.relative(baseDir, current).replace(/\\/g, "/"), rules: localRules }
2920
+ { prefix: path12.relative(baseDir, current).replace(/\\/g, "/"), rules: localRules }
2883
2921
  ];
2884
2922
  let entries;
2885
2923
  try {
2886
- entries = fs10.readdirSync(current, { withFileTypes: true });
2924
+ entries = fs11.readdirSync(current, { withFileTypes: true });
2887
2925
  } catch {
2888
2926
  results.skipped.push({
2889
2927
  path: relativeToBase(current),
@@ -2905,8 +2943,8 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
2905
2943
  }
2906
2944
  const entry = frame.entries[frame.index];
2907
2945
  frame.index += 1;
2908
- const fullPath = path11.join(frame.dir, entry.name);
2909
- const relPath = path11.relative(baseDir, fullPath).replace(/\\/g, "/");
2946
+ const fullPath = path12.join(frame.dir, entry.name);
2947
+ const relPath = path12.relative(baseDir, fullPath).replace(/\\/g, "/");
2910
2948
  if (isToolOwnedDirName(entry.name) && reservedPaths.has(relPath)) continue;
2911
2949
  if (DEFAULT_IGNORED_DIRS.includes(entry.name)) {
2912
2950
  results.skipped.push({
@@ -2939,14 +2977,14 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
2939
2977
  if (entry.isSymbolicLink()) {
2940
2978
  let targetReal;
2941
2979
  try {
2942
- targetReal = fs10.realpathSync(fullPath);
2980
+ targetReal = fs11.realpathSync(fullPath);
2943
2981
  } catch {
2944
2982
  results.skipped.push({ path: relPath, reason: "read-error", detail: "broken symlink" });
2945
2983
  continue;
2946
2984
  }
2947
2985
  let stats;
2948
2986
  try {
2949
- stats = fs10.statSync(fullPath);
2987
+ stats = fs11.statSync(fullPath);
2950
2988
  } catch {
2951
2989
  results.skipped.push({ path: relPath, reason: "read-error", detail: "unreadable symlink" });
2952
2990
  continue;
@@ -2999,6 +3037,20 @@ var CONFIG_FILENAMES = [
2999
3037
  ".snapshotrc.json",
3000
3038
  ".snapshotrc"
3001
3039
  ];
3040
+ function findConfigFile(cwd) {
3041
+ for (const filename of CONFIG_FILENAMES) {
3042
+ const fullPath = path12.join(cwd, filename);
3043
+ if (fs11.existsSync(fullPath)) return fullPath;
3044
+ }
3045
+ const pkgJsonPath = path12.join(cwd, "package.json");
3046
+ if (!fs11.existsSync(pkgJsonPath)) return null;
3047
+ try {
3048
+ const parsed = JSON.parse(fs11.readFileSync(pkgJsonPath, "utf8"));
3049
+ return parsed.snapshot === void 0 ? null : pkgJsonPath;
3050
+ } catch {
3051
+ return null;
3052
+ }
3053
+ }
3002
3054
  var RESOLVED_CONFIG_BRAND = /* @__PURE__ */ Symbol.for(
3003
3055
  "codebase-snapshot/resolved-config"
3004
3056
  );
@@ -3244,11 +3296,11 @@ async function loadConfigFileDetailed(cwd) {
3244
3296
  const warnings = [];
3245
3297
  const errors = [];
3246
3298
  for (const filename of CONFIG_FILENAMES) {
3247
- const fullPath = path11.join(cwd, filename);
3248
- if (!fs10.existsSync(fullPath)) continue;
3299
+ const fullPath = path12.join(cwd, filename);
3300
+ if (!fs11.existsSync(fullPath)) continue;
3249
3301
  if (filename.endsWith(".json") || filename === ".snapshotrc") {
3250
3302
  try {
3251
- const config = parseJsonish(fs10.readFileSync(fullPath, "utf8"), fullPath);
3303
+ const config = parseJsonish(fs11.readFileSync(fullPath, "utf8"), fullPath);
3252
3304
  if (config === null || typeof config !== "object" || Array.isArray(config)) {
3253
3305
  throw new Error(
3254
3306
  `${fullPath} does not hold a configuration object (received ${describeValue2(config)}).`
@@ -3282,10 +3334,10 @@ async function loadConfigFileDetailed(cwd) {
3282
3334
  continue;
3283
3335
  }
3284
3336
  }
3285
- const pkgJsonPath = path11.join(cwd, "package.json");
3286
- if (fs10.existsSync(pkgJsonPath)) {
3337
+ const pkgJsonPath = path12.join(cwd, "package.json");
3338
+ if (fs11.existsSync(pkgJsonPath)) {
3287
3339
  try {
3288
- const parsed = parseJsonish(fs10.readFileSync(pkgJsonPath, "utf8"), pkgJsonPath);
3340
+ const parsed = parseJsonish(fs11.readFileSync(pkgJsonPath, "utf8"), pkgJsonPath);
3289
3341
  const snapshotKey = parsed.snapshot;
3290
3342
  if (snapshotKey && typeof snapshotKey === "object" && !Array.isArray(snapshotKey)) {
3291
3343
  return { config: snapshotKey, file: pkgJsonPath, warnings, errors };
@@ -3308,7 +3360,7 @@ async function loadConfigFile(cwd) {
3308
3360
  const loaded = await loadConfigFileDetailed(cwd);
3309
3361
  if (loaded.config && loaded.file && typeof loaded.config === "object") {
3310
3362
  Object.defineProperty(loaded.config, CONFIG_DIR_SYMBOL, {
3311
- value: path11.dirname(loaded.file),
3363
+ value: path12.dirname(loaded.file),
3312
3364
  enumerable: false,
3313
3365
  configurable: true
3314
3366
  });
@@ -3467,12 +3519,12 @@ function resolveKindRules(value) {
3467
3519
  function outputExcludePatterns(cwd, outDir, tmpDir) {
3468
3520
  const patterns = [];
3469
3521
  for (const dir of [outDir, tmpDir]) {
3470
- const absolute = path11.resolve(cwd, dir);
3471
- const relative6 = path11.relative(cwd, absolute);
3472
- if (!relative6 || relative6 === ".." || relative6.startsWith(`..${path11.sep}`) || path11.isAbsolute(relative6)) {
3522
+ const absolute = path12.resolve(cwd, dir);
3523
+ const relative7 = path12.relative(cwd, absolute);
3524
+ if (!relative7 || relative7 === ".." || relative7.startsWith(`..${path12.sep}`) || path12.isAbsolute(relative7)) {
3473
3525
  continue;
3474
3526
  }
3475
- patterns.push(`${relative6.replace(/\\/g, "/")}/**`);
3527
+ patterns.push(`${relative7.replace(/\\/g, "/")}/**`);
3476
3528
  }
3477
3529
  return patterns;
3478
3530
  }
@@ -3588,11 +3640,11 @@ function resolveCandidateCwd(userConfig, cliOptions, baseDir) {
3588
3640
  );
3589
3641
  }
3590
3642
  const candidate = cliOptions?.cwd || userConfig?.cwd || baseDir || process.cwd();
3591
- const resolved = path11.resolve(candidate);
3592
- if (!fs10.existsSync(resolved)) {
3643
+ const resolved = path12.resolve(candidate);
3644
+ if (!fs11.existsSync(resolved)) {
3593
3645
  throw new Error(`Working directory does not exist: ${resolved}`);
3594
3646
  }
3595
- if (!fs10.statSync(resolved).isDirectory()) {
3647
+ if (!fs11.statSync(resolved).isDirectory()) {
3596
3648
  throw new Error(`Working directory is not a directory: ${resolved}`);
3597
3649
  }
3598
3650
  return resolved;
@@ -4616,7 +4668,7 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
4616
4668
  ".so"
4617
4669
  ]);
4618
4670
  function isBinaryExtension(filePath) {
4619
- const ext = path11.extname(filePath).toLowerCase();
4671
+ const ext = path12.extname(filePath).toLowerCase();
4620
4672
  return BINARY_EXTENSIONS.has(ext);
4621
4673
  }
4622
4674
  function isBinaryBuffer(buffer) {
@@ -4786,13 +4838,13 @@ function isBinaryFile(absolutePath) {
4786
4838
  if (isBinaryExtension(absolutePath)) return true;
4787
4839
  let fd;
4788
4840
  try {
4789
- fd = fs10.openSync(absolutePath, "r");
4841
+ fd = fs11.openSync(absolutePath, "r");
4790
4842
  const buffer = Buffer.alloc(CHUNK);
4791
4843
  let sample;
4792
4844
  let position = 0;
4793
4845
  while (position < BINARY_SCAN_BYTES) {
4794
4846
  const wanted = Math.min(CHUNK, BINARY_SCAN_BYTES - position);
4795
- const bytesRead = fs10.readSync(fd, buffer, 0, wanted, position);
4847
+ const bytesRead = fs11.readSync(fd, buffer, 0, wanted, position);
4796
4848
  if (bytesRead === 0) return false;
4797
4849
  const chunk = buffer.subarray(0, bytesRead);
4798
4850
  if (sample === void 0) {
@@ -4810,7 +4862,7 @@ function isBinaryFile(absolutePath) {
4810
4862
  } finally {
4811
4863
  if (fd !== void 0) {
4812
4864
  try {
4813
- fs10.closeSync(fd);
4865
+ fs11.closeSync(fd);
4814
4866
  } catch {
4815
4867
  }
4816
4868
  }
@@ -4870,10 +4922,10 @@ var LANGUAGE_BY_BASENAME = {
4870
4922
  };
4871
4923
  function detectLanguage(filePath) {
4872
4924
  const normalized = filePath.replace(/\\/g, "/");
4873
- const base = path11.posix.basename(normalized);
4925
+ const base = path12.posix.basename(normalized);
4874
4926
  if (LANGUAGE_BY_BASENAME[base]) return LANGUAGE_BY_BASENAME[base];
4875
4927
  if (base === ".env" || base.startsWith(".env.")) return "dotenv";
4876
- const ext = path11.posix.extname(base).toLowerCase();
4928
+ const ext = path12.posix.extname(base).toLowerCase();
4877
4929
  return LANGUAGE_BY_EXTENSION[ext] ?? "text";
4878
4930
  }
4879
4931
  function maskedPath(value, secretRules) {
@@ -4881,33 +4933,33 @@ function maskedPath(value, secretRules) {
4881
4933
  }
4882
4934
  function confineToDirectory(directory, filename) {
4883
4935
  if (filename.includes("\0")) return null;
4884
- if (path11.isAbsolute(filename)) return null;
4885
- const base = path11.resolve(directory);
4886
- const resolved = path11.resolve(base, filename);
4936
+ if (path12.isAbsolute(filename)) return null;
4937
+ const base = path12.resolve(directory);
4938
+ const resolved = path12.resolve(base, filename);
4887
4939
  const caseInsensitive = process.platform === "win32" || process.platform === "darwin";
4888
- const prefix = caseInsensitive ? `${base}${path11.sep}`.toLowerCase() : `${base}${path11.sep}`;
4940
+ const prefix = caseInsensitive ? `${base}${path12.sep}`.toLowerCase() : `${base}${path12.sep}`;
4889
4941
  const candidate = caseInsensitive ? resolved.toLowerCase() : resolved;
4890
4942
  return candidate.startsWith(prefix) ? resolved : null;
4891
4943
  }
4892
4944
  function findStagingDirectory(config) {
4893
- const tmpDir = path11.resolve(config.cwd, config.tmpDir);
4945
+ const tmpDir = path12.resolve(config.cwd, config.tmpDir);
4894
4946
  let entries;
4895
4947
  try {
4896
- entries = fs10.readdirSync(tmpDir);
4948
+ entries = fs11.readdirSync(tmpDir);
4897
4949
  } catch {
4898
4950
  return null;
4899
4951
  }
4900
4952
  const runs = entries.filter((name) => name.startsWith("run-")).sort();
4901
4953
  const newest = runs[runs.length - 1];
4902
- return newest ? path11.join(tmpDir, newest) : null;
4954
+ return newest ? path12.join(tmpDir, newest) : null;
4903
4955
  }
4904
4956
  function describeMissingManifest(config, manifestPath) {
4905
- const outDir = path11.resolve(config.cwd, config.outDir);
4906
- const markerPath = path11.join(outDir, ".codebase-snapshot");
4957
+ const outDir = path12.resolve(config.cwd, config.outDir);
4958
+ const markerPath = path12.join(outDir, ".codebase-snapshot");
4907
4959
  let marker2 = null;
4908
4960
  try {
4909
- if (fs10.existsSync(markerPath)) {
4910
- const parsed = JSON.parse(fs10.readFileSync(markerPath, "utf8"));
4961
+ if (fs11.existsSync(markerPath)) {
4962
+ const parsed = JSON.parse(fs11.readFileSync(markerPath, "utf8"));
4911
4963
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
4912
4964
  marker2 = parsed;
4913
4965
  }
@@ -4943,14 +4995,14 @@ function sha256Hex(buffer) {
4943
4995
  }
4944
4996
  var VERIFY_SNIFF_MAX_BYTES = 8 * 1024 * 1024;
4945
4997
  function wouldNowBeIncluded(cwd, relativePath, reason) {
4946
- const absolutePath = path11.join(cwd, relativePath);
4998
+ const absolutePath = path12.join(cwd, relativePath);
4947
4999
  try {
4948
- const stats = fs10.statSync(absolutePath);
5000
+ const stats = fs11.statSync(absolutePath);
4949
5001
  if (!stats.isFile()) return false;
4950
5002
  if (isBinaryFile(absolutePath)) return false;
4951
5003
  if (reason !== "binary") return true;
4952
5004
  if (stats.size > VERIFY_SNIFF_MAX_BYTES) return false;
4953
- return !decodeText(fs10.readFileSync(absolutePath)).binary;
5005
+ return !decodeText(fs11.readFileSync(absolutePath)).binary;
4954
5006
  } catch {
4955
5007
  return false;
4956
5008
  }
@@ -5008,8 +5060,8 @@ function describeManifestShapeProblem(value) {
5008
5060
  function validateSnapshot(configInput) {
5009
5061
  assertUsableConfigInput(configInput);
5010
5062
  const config = isResolvedConfig(configInput) ? normalizeResolvedConfig(configInput) : resolveConfig(configInput);
5011
- const manifestPath = path11.resolve(config.cwd, config.outDir, "manifest.json");
5012
- if (!fs10.existsSync(manifestPath)) {
5063
+ const manifestPath = path12.resolve(config.cwd, config.outDir, "manifest.json");
5064
+ if (!fs11.existsSync(manifestPath)) {
5013
5065
  return {
5014
5066
  valid: false,
5015
5067
  manifestPresent: false,
@@ -5020,7 +5072,7 @@ function validateSnapshot(configInput) {
5020
5072
  }
5021
5073
  let manifest;
5022
5074
  try {
5023
- manifest = JSON.parse(fs10.readFileSync(manifestPath, "utf8"));
5075
+ manifest = JSON.parse(fs11.readFileSync(manifestPath, "utf8"));
5024
5076
  } catch (err) {
5025
5077
  return {
5026
5078
  valid: false,
@@ -5042,7 +5094,7 @@ function validateSnapshot(configInput) {
5042
5094
  }
5043
5095
  const drift = emptyDrift();
5044
5096
  const warnings = [];
5045
- const snapshotDir = path11.resolve(config.cwd, config.outDir);
5097
+ const snapshotDir = path12.resolve(config.cwd, config.outDir);
5046
5098
  const manifestFileMap = /* @__PURE__ */ new Map();
5047
5099
  for (const file of manifest.files) {
5048
5100
  if (file && typeof file.relativePath === "string") {
@@ -5076,7 +5128,7 @@ function validateSnapshot(configInput) {
5076
5128
  }
5077
5129
  let diskHash;
5078
5130
  try {
5079
- diskHash = sha256Hex(fs10.readFileSync(path11.join(config.cwd, diskFile)));
5131
+ diskHash = sha256Hex(fs11.readFileSync(path12.join(config.cwd, diskFile)));
5080
5132
  } catch (err) {
5081
5133
  if (err.code === "ENOENT") {
5082
5134
  drift.missingOnDisk.push(diskFile);
@@ -5100,7 +5152,7 @@ function validateSnapshot(configInput) {
5100
5152
  }
5101
5153
  let statFailure = null;
5102
5154
  try {
5103
- fs10.statSync(confined);
5155
+ fs11.statSync(confined);
5104
5156
  } catch (err) {
5105
5157
  statFailure = err;
5106
5158
  }
@@ -5120,7 +5172,7 @@ function validateSnapshot(configInput) {
5120
5172
  ...Object.values(config.budgets.byLayer)
5121
5173
  );
5122
5174
  const partNames = /* @__PURE__ */ new Set();
5123
- const partsDir = path11.join(snapshotDir, "parts");
5175
+ const partsDir = path12.join(snapshotDir, "parts");
5124
5176
  for (const part of manifest.parts) {
5125
5177
  if (!part || typeof part.filename !== "string") continue;
5126
5178
  partNames.add(part.filename);
@@ -5135,7 +5187,7 @@ function validateSnapshot(configInput) {
5135
5187
  }
5136
5188
  let buffer;
5137
5189
  try {
5138
- buffer = fs10.readFileSync(partPath);
5190
+ buffer = fs11.readFileSync(partPath);
5139
5191
  } catch {
5140
5192
  drift.partIssues.push({ part: part.filename, issue: "missing from snapshot/parts" });
5141
5193
  continue;
@@ -5349,9 +5401,9 @@ function validateSnapshot(configInput) {
5349
5401
  ["full-codebase.md", manifest.artifacts?.fullMarkdown]
5350
5402
  ]) {
5351
5403
  if (!artifact) continue;
5352
- const artifactPath = path11.join(snapshotDir, key);
5404
+ const artifactPath = path12.join(snapshotDir, key);
5353
5405
  try {
5354
- const buffer = fs10.readFileSync(artifactPath);
5406
+ const buffer = fs11.readFileSync(artifactPath);
5355
5407
  if (sha256Hex(buffer) !== artifact.sha256 || buffer.length !== artifact.byteSize) {
5356
5408
  drift.partIssues.push({ part: key, issue: "artifact does not match its recorded digest" });
5357
5409
  }
@@ -5421,7 +5473,7 @@ function validateSnapshot(configInput) {
5421
5473
 
5422
5474
  // src/version.ts
5423
5475
  var GENERATOR_NAME = "codebase-snapshot";
5424
- var GENERATOR_VERSION = "0.9.1";
5476
+ var GENERATOR_VERSION = "0.9.5";
5425
5477
  var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
5426
5478
  function isOurGeneratorName(name) {
5427
5479
  if (typeof name !== "string") return false;
@@ -5436,42 +5488,42 @@ function describeMatcher(matcher) {
5436
5488
  return String(matcher);
5437
5489
  }
5438
5490
  function normalizeTarget(cwd, target) {
5439
- const absolute = path11.resolve(cwd, target);
5440
- const relative6 = path11.relative(cwd, absolute);
5441
- if (relative6.startsWith("..") || path11.isAbsolute(relative6)) return target;
5442
- return relative6.split(path11.sep).join("/");
5491
+ const absolute = path12.resolve(cwd, target);
5492
+ const relative7 = path12.relative(cwd, absolute);
5493
+ if (relative7.startsWith("..") || path12.isAbsolute(relative7)) return target;
5494
+ return relative7.split(path12.sep).join("/");
5443
5495
  }
5444
5496
  function findRuleMatch(rules, relativePath) {
5445
5497
  return rules.find(
5446
5498
  (rule) => matchRule(relativePath, rule.match)
5447
5499
  );
5448
5500
  }
5449
- function findRecordedAncestor(skipped, relative6, maskedPath2) {
5501
+ function findRecordedAncestor(skipped, relative7, maskedPath2) {
5450
5502
  let nearest;
5451
5503
  for (const record of skipped) {
5452
5504
  const ancestorPath = record.path;
5453
5505
  if (ancestorPath.length === 0) continue;
5454
- const isAncestor = relative6.startsWith(`${ancestorPath}/`) || maskedPath2.startsWith(`${ancestorPath}/`);
5506
+ const isAncestor = relative7.startsWith(`${ancestorPath}/`) || maskedPath2.startsWith(`${ancestorPath}/`);
5455
5507
  if (!isAncestor) continue;
5456
5508
  if (!nearest || ancestorPath.length > nearest.path.length) nearest = record;
5457
5509
  }
5458
5510
  return nearest;
5459
5511
  }
5460
5512
  function readManifest2(config) {
5461
- const manifestPath = path11.resolve(config.cwd, config.outDir, "manifest.json");
5513
+ const manifestPath = path12.resolve(config.cwd, config.outDir, "manifest.json");
5462
5514
  try {
5463
- return JSON.parse(fs10.readFileSync(manifestPath, "utf8"));
5515
+ return JSON.parse(fs11.readFileSync(manifestPath, "utf8"));
5464
5516
  } catch {
5465
5517
  return null;
5466
5518
  }
5467
5519
  }
5468
5520
  function explainPath(config, target) {
5469
- const relative6 = normalizeTarget(config.cwd, target);
5470
- const absolute = path11.join(config.cwd, relative6);
5471
- const maskedPath2 = findSecretInPath(relative6, config.secretRules)?.masked ?? relative6;
5521
+ const relative7 = normalizeTarget(config.cwd, target);
5522
+ const absolute = path12.join(config.cwd, relative7);
5523
+ const maskedPath2 = findSecretInPath(relative7, config.secretRules)?.masked ?? relative7;
5472
5524
  const result = {
5473
5525
  path: maskedPath2,
5474
- exists: fs10.existsSync(absolute),
5526
+ exists: fs11.existsSync(absolute),
5475
5527
  included: false
5476
5528
  };
5477
5529
  const collection = collectFiles(config.cwd, {
@@ -5482,12 +5534,12 @@ function explainPath(config, target) {
5482
5534
  // Same exclusion `generate` applies, so `explain` never says "not-discovered" about a file
5483
5535
  // that is only missing because the walker dropped the tool's own output directory.
5484
5536
  toolOwnedPaths: [config.outDir, config.tmpDir].map(
5485
- (directory) => path11.relative(config.cwd, path11.resolve(config.cwd, directory)).replace(/\\/g, "/")
5486
- ).filter((relative7) => relative7.length > 0 && !relative7.startsWith(".."))
5537
+ (directory) => path12.relative(config.cwd, path12.resolve(config.cwd, directory)).replace(/\\/g, "/")
5538
+ ).filter((relative8) => relative8.length > 0 && !relative8.startsWith(".."))
5487
5539
  });
5488
- if (!collection.files.includes(relative6)) {
5540
+ if (!collection.files.includes(relative7)) {
5489
5541
  const record = collection.skipped.find(
5490
- (entry2) => entry2.path === relative6 || entry2.path === maskedPath2
5542
+ (entry2) => entry2.path === relative7 || entry2.path === maskedPath2
5491
5543
  );
5492
5544
  if (record) {
5493
5545
  result.reason = record.reason;
@@ -5496,7 +5548,7 @@ function explainPath(config, target) {
5496
5548
  result.reason = "missing";
5497
5549
  result.detail = "no such file or directory, relative to the configured cwd";
5498
5550
  } else {
5499
- const ancestor = findRecordedAncestor(collection.skipped, relative6, maskedPath2);
5551
+ const ancestor = findRecordedAncestor(collection.skipped, relative7, maskedPath2);
5500
5552
  if (ancestor) {
5501
5553
  result.reason = ancestor.reason;
5502
5554
  result.detail = ancestor.detail ? `${ancestor.detail} \u2014 inherited from the skipped directory \`${ancestor.path}\`` : `inside the skipped directory \`${ancestor.path}\``;
@@ -5514,15 +5566,15 @@ function explainPath(config, target) {
5514
5566
  result.detail = "binary content; generate skips it and records `binary` in manifest.skipped[]";
5515
5567
  return result;
5516
5568
  }
5517
- result.layer = classifyLayer(relative6, config.layers);
5518
- result.kind = classifyKind(relative6, config.kinds);
5519
- result.language = detectLanguage(relative6);
5520
- const layerRule = findRuleMatch(config.layers, relative6);
5569
+ result.layer = classifyLayer(relative7, config.layers);
5570
+ result.kind = classifyKind(relative7, config.kinds);
5571
+ result.language = detectLanguage(relative7);
5572
+ const layerRule = findRuleMatch(config.layers, relative7);
5521
5573
  if (layerRule) result.layerMatch = describeMatcher(layerRule.match);
5522
- const kindRule = findRuleMatch(config.kinds, relative6);
5574
+ const kindRule = findRuleMatch(config.kinds, relative7);
5523
5575
  if (kindRule) result.kindMatch = describeMatcher(kindRule.match);
5524
- if (fs10.existsSync(absolute) && fs10.statSync(absolute).isFile()) {
5525
- const buffer = fs10.readFileSync(absolute);
5576
+ if (fs11.existsSync(absolute) && fs11.statSync(absolute).isFile()) {
5577
+ const buffer = fs11.readFileSync(absolute);
5526
5578
  const decoded = decodeText(buffer);
5527
5579
  if (decoded.binary) {
5528
5580
  result.included = false;
@@ -5534,12 +5586,12 @@ function explainPath(config, target) {
5534
5586
  result.bytes = buffer.length;
5535
5587
  result.lines = countLines(text2);
5536
5588
  if (decoded.encoding !== "utf-8") result.encoding = decoded.encoding;
5537
- const { findings } = scanAndRedactSecrets(text2, config.secretRules, relative6);
5589
+ const { findings } = scanAndRedactSecrets(text2, config.secretRules, relative7);
5538
5590
  result.secretCount = findings.length;
5539
5591
  result.secrets = findings.slice(0, 20).map((finding) => ({ rules: finding.rules, line: finding.line }));
5540
5592
  }
5541
5593
  const manifest = readManifest2(config);
5542
- const entry = Array.isArray(manifest?.files) ? manifest.files.find((file) => file.relativePath === relative6) : void 0;
5594
+ const entry = Array.isArray(manifest?.files) ? manifest.files.find((file) => file.relativePath === relative7) : void 0;
5543
5595
  result.part = entry?.part ?? null;
5544
5596
  result.inclusionMode = entry?.inclusionMode;
5545
5597
  return result;
@@ -5770,14 +5822,14 @@ function compareManifests(before, after) {
5770
5822
  function loadManifest(target) {
5771
5823
  let isDirectory;
5772
5824
  try {
5773
- isDirectory = fs10.statSync(target).isDirectory();
5825
+ isDirectory = fs11.statSync(target).isDirectory();
5774
5826
  } catch {
5775
5827
  throw new Error(`No snapshot found at ${target}: the path does not exist.`);
5776
5828
  }
5777
- const manifestPath = isDirectory ? path11.join(target, "manifest.json") : target;
5829
+ const manifestPath = isDirectory ? path12.join(target, "manifest.json") : target;
5778
5830
  let raw;
5779
5831
  try {
5780
- raw = fs10.readFileSync(manifestPath, "utf8");
5832
+ raw = fs11.readFileSync(manifestPath, "utf8");
5781
5833
  } catch {
5782
5834
  throw new Error(`Could not read a manifest at ${manifestPath}.`);
5783
5835
  }
@@ -5834,15 +5886,15 @@ function renderDiffText(diff) {
5834
5886
  function appendGitignoreEntries(cwd, entries) {
5835
5887
  const wanted = [...new Set(entries)].filter((entry) => entry.trim().length > 0);
5836
5888
  if (wanted.length === 0) return { added: [], note: null };
5837
- const ignorePath = path11.join(cwd, ".gitignore");
5889
+ const ignorePath = path12.join(cwd, ".gitignore");
5838
5890
  const manual = `Add ${wanted.map((entry) => `"${entry}"`).join(" and ")} to your .gitignore yourself.`;
5839
5891
  let existing = "";
5840
5892
  try {
5841
- if (fs10.existsSync(ignorePath)) {
5842
- if (fs10.statSync(ignorePath).isDirectory()) {
5893
+ if (fs11.existsSync(ignorePath)) {
5894
+ if (fs11.statSync(ignorePath).isDirectory()) {
5843
5895
  return { added: [], note: `${ignorePath} is a directory, so it was not updated. ${manual}` };
5844
5896
  }
5845
- existing = fs10.readFileSync(ignorePath, "utf8");
5897
+ existing = fs11.readFileSync(ignorePath, "utf8");
5846
5898
  }
5847
5899
  } catch (err) {
5848
5900
  return {
@@ -5855,7 +5907,7 @@ function appendGitignoreEntries(cwd, entries) {
5855
5907
  if (added.length === 0) return { added: [], note: null };
5856
5908
  const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
5857
5909
  try {
5858
- fs10.writeFileSync(ignorePath, `${existing}${prefix}${added.join("\n")}
5910
+ fs11.writeFileSync(ignorePath, `${existing}${prefix}${added.join("\n")}
5859
5911
  `, "utf8");
5860
5912
  } catch (err) {
5861
5913
  return { added: [], note: `${err.message}. ${manual}` };
@@ -5863,7 +5915,7 @@ function appendGitignoreEntries(cwd, entries) {
5863
5915
  return { added, note: null };
5864
5916
  }
5865
5917
  function gitignorePath(cwd, filePath) {
5866
- return path11.relative(cwd, filePath).split(path11.sep).join("/");
5918
+ return path12.relative(cwd, filePath).split(path12.sep).join("/");
5867
5919
  }
5868
5920
 
5869
5921
  // src/wizard/config-template.ts
@@ -5997,6 +6049,12 @@ var PromptCancelled = class extends Error {
5997
6049
  this.name = "PromptCancelled";
5998
6050
  }
5999
6051
  };
6052
+ var PromptBack = class extends Error {
6053
+ constructor() {
6054
+ super("Prompt back");
6055
+ this.name = "PromptBack";
6056
+ }
6057
+ };
6000
6058
  function defaultIO() {
6001
6059
  return { input: process.stdin, output: process.stdout };
6002
6060
  }
@@ -6020,35 +6078,40 @@ function choiceLine(choice, active) {
6020
6078
  const hint = choice.hint ? ` ${choice.hint}` : "";
6021
6079
  return ` ${marker(active)} ${choice.label}${hint}`;
6022
6080
  }
6023
- function renderSelect(message, choices, index) {
6081
+ function listFooter(base, allowBack) {
6082
+ return allowBack ? `${base} \xB7 \u2190 back` : base;
6083
+ }
6084
+ function renderSelect(message, choices, index, allowBack = false) {
6024
6085
  const lines = [`\u25C6 ${message}`, ""];
6025
6086
  choices.forEach((choice, position) => lines.push(choiceLine(choice, position === index)));
6026
- lines.push("", " \u2191\u2193 move \xB7 enter select");
6087
+ lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 enter select", allowBack)}`);
6027
6088
  return lines.join("\n");
6028
6089
  }
6029
- function renderMultiSelect(message, choices, index, selected) {
6090
+ function renderMultiSelect(message, choices, index, selected, allowBack = false) {
6030
6091
  const lines = [`\u25C6 ${message}`, ""];
6031
6092
  choices.forEach((choice, position) => {
6032
6093
  const box = selected.has(position) ? "\u25FC" : "\u25FB";
6033
6094
  const hint = choice.hint ? ` ${choice.hint}` : "";
6034
6095
  lines.push(` ${marker(position === index)} ${box} ${choice.label}${hint}`);
6035
6096
  });
6036
- lines.push("", " \u2191\u2193 move \xB7 space toggle \xB7 enter confirm");
6097
+ lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 space toggle \xB7 enter confirm", allowBack)}`);
6037
6098
  return lines.join("\n");
6038
6099
  }
6039
- function renderConfirm(message, value) {
6100
+ function renderConfirm(message, value, allowBack = false) {
6101
+ const footer = allowBack ? "\u2190\u2192 or y/n \xB7 enter confirm \xB7 b back" : "\u2190\u2192 or y/n \xB7 enter confirm";
6040
6102
  return [
6041
6103
  `\u25C6 ${message}`,
6042
6104
  "",
6043
6105
  ` ${marker(value)} Yes`,
6044
6106
  ` ${marker(!value)} No`,
6045
6107
  "",
6046
- " \u2190\u2192 or y/n \xB7 enter confirm"
6108
+ ` ${footer}`
6047
6109
  ].join("\n");
6048
6110
  }
6049
- function renderText(message, value, error) {
6111
+ function renderText(message, value, error, allowBack = false) {
6050
6112
  const lines = [`\u25C6 ${message}`, "", ` ${value || "\u258F"}`];
6051
6113
  if (error) lines.push("", ` \u2716 ${error}`);
6114
+ if (allowBack) lines.push("", " enter confirm \xB7 \u2190 back");
6052
6115
  return lines.join("\n");
6053
6116
  }
6054
6117
  function keyReader(input) {
@@ -6097,14 +6160,18 @@ var Frame = class {
6097
6160
  function isCancel(key) {
6098
6161
  return key.ctrl === true && key.name === "c" || key.ctrl === true && key.name === "d";
6099
6162
  }
6163
+ function isListBack(key, allowBack) {
6164
+ return allowBack && (key.name === "left" || key.name === "b");
6165
+ }
6100
6166
  async function select(io, message, choices, options = {}) {
6101
6167
  if (choices.length === 0) throw new Error("select() requires at least one choice.");
6168
+ const allowBack = options.allowBack === true;
6102
6169
  const fallbackIndex = Math.max(
6103
6170
  0,
6104
6171
  choices.findIndex((choice) => choice.value === options.defaultValue)
6105
6172
  );
6106
6173
  if (!isInteractive(io)) {
6107
- io.output.write(`${renderSelect(message, choices, fallbackIndex)}
6174
+ io.output.write(`${renderSelect(message, choices, fallbackIndex, allowBack)}
6108
6175
  `);
6109
6176
  return choices[fallbackIndex].value;
6110
6177
  }
@@ -6113,9 +6180,10 @@ async function select(io, message, choices, options = {}) {
6113
6180
  let index = fallbackIndex;
6114
6181
  try {
6115
6182
  for (; ; ) {
6116
- frame.draw(renderSelect(message, choices, index));
6183
+ frame.draw(renderSelect(message, choices, index, allowBack));
6117
6184
  const key = await reader.next();
6118
6185
  if (isCancel(key)) throw new PromptCancelled();
6186
+ if (isListBack(key, allowBack)) throw new PromptBack();
6119
6187
  if (key.name === "up") index = moveSelection(index, -1, choices.length);
6120
6188
  else if (key.name === "down") index = moveSelection(index, 1, choices.length);
6121
6189
  else if (key.name === "return" || key.name === "enter") {
@@ -6124,18 +6192,22 @@ async function select(io, message, choices, options = {}) {
6124
6192
  } else if (key.name === "j") index = moveSelection(index, 1, choices.length);
6125
6193
  else if (key.name === "k") index = moveSelection(index, -1, choices.length);
6126
6194
  }
6195
+ } catch (error) {
6196
+ frame.erase();
6197
+ throw error;
6127
6198
  } finally {
6128
6199
  reader.close();
6129
6200
  }
6130
6201
  }
6131
6202
  async function multiSelect(io, message, choices, options = {}) {
6203
+ const allowBack = options.allowBack === true;
6132
6204
  const selected = /* @__PURE__ */ new Set();
6133
6205
  choices.forEach((choice, position) => {
6134
6206
  if (options.defaults?.includes(choice.value)) selected.add(position);
6135
6207
  });
6136
6208
  const result = () => choices.filter((_, position) => selected.has(position)).map((choice) => choice.value);
6137
6209
  if (!isInteractive(io)) {
6138
- io.output.write(`${renderMultiSelect(message, choices, 0, selected)}
6210
+ io.output.write(`${renderMultiSelect(message, choices, 0, selected, allowBack)}
6139
6211
  `);
6140
6212
  return result();
6141
6213
  }
@@ -6144,9 +6216,10 @@ async function multiSelect(io, message, choices, options = {}) {
6144
6216
  let index = 0;
6145
6217
  try {
6146
6218
  for (; ; ) {
6147
- frame.draw(renderMultiSelect(message, choices, index, selected));
6219
+ frame.draw(renderMultiSelect(message, choices, index, selected, allowBack));
6148
6220
  const key = await reader.next();
6149
6221
  if (isCancel(key)) throw new PromptCancelled();
6222
+ if (isListBack(key, allowBack)) throw new PromptBack();
6150
6223
  if (key.name === "up") index = moveSelection(index, -1, choices.length);
6151
6224
  else if (key.name === "down") index = moveSelection(index, 1, choices.length);
6152
6225
  else if (key.name === "space") {
@@ -6161,14 +6234,18 @@ async function multiSelect(io, message, choices, options = {}) {
6161
6234
  return result();
6162
6235
  }
6163
6236
  }
6237
+ } catch (error) {
6238
+ frame.erase();
6239
+ throw error;
6164
6240
  } finally {
6165
6241
  reader.close();
6166
6242
  }
6167
6243
  }
6168
6244
  async function confirm(io, message, options = {}) {
6245
+ const allowBack = options.allowBack === true;
6169
6246
  let value = options.defaultValue ?? true;
6170
6247
  if (!isInteractive(io)) {
6171
- io.output.write(`${renderConfirm(message, value)}
6248
+ io.output.write(`${renderConfirm(message, value, allowBack)}
6172
6249
  `);
6173
6250
  return value;
6174
6251
  }
@@ -6176,9 +6253,10 @@ async function confirm(io, message, options = {}) {
6176
6253
  const reader = keyReader(io.input);
6177
6254
  try {
6178
6255
  for (; ; ) {
6179
- frame.draw(renderConfirm(message, value));
6256
+ frame.draw(renderConfirm(message, value, allowBack));
6180
6257
  const key = await reader.next();
6181
6258
  if (isCancel(key)) throw new PromptCancelled();
6259
+ if (allowBack && key.name === "b") throw new PromptBack();
6182
6260
  if (key.name === "up" || key.name === "left") value = true;
6183
6261
  else if (key.name === "down" || key.name === "right") value = false;
6184
6262
  else if (key.name === "y") value = true;
@@ -6188,14 +6266,18 @@ async function confirm(io, message, options = {}) {
6188
6266
  return value;
6189
6267
  }
6190
6268
  }
6269
+ } catch (error) {
6270
+ frame.erase();
6271
+ throw error;
6191
6272
  } finally {
6192
6273
  reader.close();
6193
6274
  }
6194
6275
  }
6195
6276
  async function text(io, message, options = {}) {
6277
+ const allowBack = options.allowBack === true;
6196
6278
  const fallback = options.defaultValue ?? "";
6197
6279
  if (!isInteractive(io)) {
6198
- io.output.write(`${renderText(message, fallback)}
6280
+ io.output.write(`${renderText(message, fallback, void 0, allowBack)}
6199
6281
  `);
6200
6282
  return fallback;
6201
6283
  }
@@ -6205,9 +6287,10 @@ async function text(io, message, options = {}) {
6205
6287
  let error;
6206
6288
  try {
6207
6289
  for (; ; ) {
6208
- frame.draw(renderText(message, value, error));
6290
+ frame.draw(renderText(message, value, error, allowBack));
6209
6291
  const key = await reader.next();
6210
6292
  if (isCancel(key)) throw new PromptCancelled();
6293
+ if (allowBack && key.name === "left") throw new PromptBack();
6211
6294
  error = void 0;
6212
6295
  if (key.name === "return" || key.name === "enter") {
6213
6296
  const problem = options.validate ? options.validate(value) : null;
@@ -6228,6 +6311,9 @@ async function text(io, message, options = {}) {
6228
6311
  }
6229
6312
  if (_isPrintable(key)) value += _keyText(key);
6230
6313
  }
6314
+ } catch (error_) {
6315
+ frame.erase();
6316
+ throw error_;
6231
6317
  } finally {
6232
6318
  reader.close();
6233
6319
  }
@@ -6251,10 +6337,10 @@ var CONFIG_CANDIDATES = [
6251
6337
  ];
6252
6338
  function chooseConfigFileName(cwd) {
6253
6339
  if (!supportsTypeScriptImports()) return "snapshot.config.mjs";
6254
- const pkgPath = path11.join(cwd, "package.json");
6255
- if (!fs10.existsSync(pkgPath)) return "snapshot.config.mjs";
6340
+ const pkgPath = path12.join(cwd, "package.json");
6341
+ if (!fs11.existsSync(pkgPath)) return "snapshot.config.mjs";
6256
6342
  try {
6257
- const parsed = JSON.parse(fs10.readFileSync(pkgPath, "utf8"));
6343
+ const parsed = JSON.parse(fs11.readFileSync(pkgPath, "utf8"));
6258
6344
  return parsed.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
6259
6345
  } catch {
6260
6346
  return "snapshot.config.mjs";
@@ -6268,7 +6354,7 @@ var SIZE_CHOICES = [
6268
6354
  ];
6269
6355
  function packageJsonHasConfig(cwd) {
6270
6356
  try {
6271
- const parsed = JSON.parse(fs10.readFileSync(path11.join(cwd, "package.json"), "utf8"));
6357
+ const parsed = JSON.parse(fs11.readFileSync(path12.join(cwd, "package.json"), "utf8"));
6272
6358
  return typeof parsed.snapshot === "object" && parsed.snapshot !== null && !Array.isArray(parsed.snapshot);
6273
6359
  } catch {
6274
6360
  return false;
@@ -6289,7 +6375,7 @@ function artifactAnswers(options) {
6289
6375
  }
6290
6376
  function topLevelDirs(cwd) {
6291
6377
  try {
6292
- return fs10.readdirSync(cwd, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules").map((entry) => entry.name).sort();
6378
+ return fs11.readdirSync(cwd, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules").map((entry) => entry.name).sort();
6293
6379
  } catch {
6294
6380
  return [];
6295
6381
  }
@@ -6302,7 +6388,7 @@ function describeJsonValue(value) {
6302
6388
  }
6303
6389
  function writeFileOrExplain(target, content) {
6304
6390
  try {
6305
- fs10.writeFileSync(target, content, "utf8");
6391
+ fs11.writeFileSync(target, content, "utf8");
6306
6392
  } catch (err) {
6307
6393
  const code = err.code;
6308
6394
  const reason = code === "EACCES" || code === "EPERM" || code === "EROFS" ? `permission denied (${code})` : code === "EISDIR" ? "it is a directory" : `${err.message}`;
@@ -6313,7 +6399,7 @@ function canWriteModuleConfig(name, cwd) {
6313
6399
  if (name.endsWith(".mjs") || name.endsWith(".ts")) return true;
6314
6400
  if (!name.endsWith(".js")) return false;
6315
6401
  try {
6316
- const parsed = JSON.parse(fs10.readFileSync(path11.join(cwd, "package.json"), "utf8"));
6402
+ const parsed = JSON.parse(fs11.readFileSync(path12.join(cwd, "package.json"), "utf8"));
6317
6403
  return parsed.type === "module";
6318
6404
  } catch {
6319
6405
  return false;
@@ -6348,19 +6434,19 @@ function serializePackageJson(raw, value) {
6348
6434
  return `${text2}${raw.endsWith("\n") ? crlf ? "\r\n" : "\n" : ""}`;
6349
6435
  }
6350
6436
  function updatePackageJson(cwd, notes) {
6351
- const packagePath = path11.join(cwd, "package.json");
6437
+ const packagePath = path12.join(cwd, "package.json");
6352
6438
  const unchanged = {
6353
6439
  scripts: [],
6354
6440
  dependency: { name: GENERATOR_NAME, range: null, added: false }
6355
6441
  };
6356
- if (!fs10.existsSync(packagePath)) {
6442
+ if (!fs11.existsSync(packagePath)) {
6357
6443
  notes.push("No package.json found, so npm scripts and the devDependency were not added.");
6358
6444
  return unchanged;
6359
6445
  }
6360
6446
  let raw;
6361
6447
  let parsed;
6362
6448
  try {
6363
- raw = fs10.readFileSync(packagePath, "utf8");
6449
+ raw = fs11.readFileSync(packagePath, "utf8");
6364
6450
  parsed = JSON.parse(raw);
6365
6451
  } catch {
6366
6452
  notes.push("Could not parse package.json; npm scripts and the devDependency were not added.");
@@ -6423,16 +6509,18 @@ function updatePackageJson(cwd, notes) {
6423
6509
  return { scripts: added, dependency };
6424
6510
  }
6425
6511
  var GITIGNORE_ENTRIES = ["snapshot/", "snapshot.tmp/"];
6426
- function resolvePresetChoice(answers, presetName) {
6512
+ function resolvePresetChoice(detectedName, presetName) {
6427
6513
  return {
6428
6514
  value: presetName,
6429
6515
  label: presetName,
6430
- hint: presetName === answers.preset ? "detected" : void 0
6516
+ hint: presetName === detectedName ? "detected" : void 0
6431
6517
  };
6432
6518
  }
6433
6519
  async function runInit(options) {
6434
6520
  const io = options.io ?? defaultIO();
6435
6521
  const detected = detectPreset(options.cwd, options.preset);
6522
+ let reason = detected.reason;
6523
+ let fallback = detected.fallback;
6436
6524
  const notes = [];
6437
6525
  const answers = {
6438
6526
  preset: detected.presetName,
@@ -6454,73 +6542,37 @@ async function runInit(options) {
6454
6542
  if (interactive) {
6455
6543
  io.output.write("\n codebase-snapshot\n");
6456
6544
  io.output.write(" Turn this repository into an LLM-ready, redacted, byte-bounded snapshot.\n\n");
6457
- io.output.write(` Detected ${detected.presetName}
6545
+ if (fallback) {
6546
+ io.output.write(" \u26A0 Could not tell what kind of project this is.\n");
6547
+ io.output.write(" No package.json, go.mod, Cargo.toml, pyproject.toml, Gemfile,\n");
6548
+ io.output.write(" composer.json, pom.xml, *.csproj, next.config.*, vite.config.*, *.tf\n");
6549
+ io.output.write(" or workspace marker was found here.\n\n");
6550
+ } else {
6551
+ io.output.write(` Detected ${detected.presetName}
6458
6552
  `);
6459
- io.output.write(` Directory ${options.cwd}
6553
+ io.output.write(` Because ${reason}
6460
6554
 
6461
6555
  `);
6462
- const useDetected = await confirm(io, `Set up for ${detected.presetName}?`, {
6463
- defaultValue: true
6464
- });
6465
- if (!useDetected) {
6466
- answers.preset = await select(
6467
- io,
6468
- "What kind of project is this?",
6469
- listPresetNames().map((name) => resolvePresetChoice(answers, name))
6470
- );
6471
- const chosen = detectPreset(options.cwd, answers.preset);
6472
- answers.preset = chosen.presetName;
6473
- }
6474
- const scope = await select(
6475
- io,
6476
- "What should go into the snapshot?",
6477
- [
6478
- { value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
6479
- { value: "folders", label: "Only specific folders\u2026" },
6480
- { value: "no-tests", label: "Everything, but skip tests" },
6481
- { value: "custom", label: "Let me write glob patterns" }
6482
- ],
6483
- { defaultValue: "all" }
6484
- );
6485
- if (scope === "folders") {
6486
- const dirs = topLevelDirs(options.cwd).slice(0, 40);
6487
- if (dirs.length === 0) {
6488
- notes.push("No top-level directories found; the snapshot will include everything.");
6489
- } else {
6490
- const chosen = await multiSelect(
6491
- io,
6492
- "Select folders",
6493
- dirs.map((name) => ({ value: name, label: `${name}/` })),
6494
- { defaults: dirs }
6495
- );
6496
- answers.include = chosen.map((name) => `${name}/**`);
6497
- }
6498
- } else if (scope === "no-tests") {
6499
- answers.excludePatterns = [
6500
- ...answers.excludePatterns,
6501
- "**/*.test.*",
6502
- "**/*.spec.*",
6503
- "tests",
6504
- "test"
6505
- ];
6506
- } else if (scope === "custom") {
6507
- const raw = await text(io, "Glob patterns to include (comma separated)", {
6508
- defaultValue: ""
6509
- });
6510
- answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
6511
- }
6512
- const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
6513
- defaultValue: "120k"
6514
- });
6515
- if (sizeChoice === "custom") {
6516
- const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
6517
- defaultValue: String(answers.maxPartBytes),
6518
- validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null
6519
- });
6520
- answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
6521
- } else {
6522
- answers.maxPartBytes = sizeChoice;
6523
6556
  }
6557
+ io.output.write(` Directory ${options.cwd}
6558
+
6559
+ `);
6560
+ const paceChoices = fallback ? [
6561
+ { value: "choose", label: "Pick the closest preset", hint: "then generate with the defaults" },
6562
+ { value: "recommended", label: "Use the defaults", hint: "generic \xB7 everything the scanner finds" },
6563
+ { value: "walk", label: "Go through each setting", hint: "every question starts on its default" }
6564
+ ] : [
6565
+ {
6566
+ value: "recommended",
6567
+ label: "Use the recommended setup",
6568
+ hint: `${detected.presetName} \xB7 config \xB7 npm scripts \xB7 install \xB7 generate`
6569
+ },
6570
+ { value: "walk", label: "Go through each setting", hint: "every question starts on its default" },
6571
+ { value: "choose", label: "Pick a different preset", hint: "everything else stays at the defaults" }
6572
+ ];
6573
+ const baseInclude = [...answers.include];
6574
+ const baseExclude = [...answers.excludePatterns];
6575
+ let lastScope = "all";
6524
6576
  const artifactChoices = [
6525
6577
  { value: "readme", label: "README.md", hint: "orientation page" },
6526
6578
  { value: "manifest", label: "manifest.json", hint: "required by validate and report" },
@@ -6530,41 +6582,163 @@ async function runInit(options) {
6530
6582
  { value: "audit", label: "reports/audit.md", hint: "security and health findings" },
6531
6583
  { value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
6532
6584
  ];
6533
- const presetEnabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
6534
- const selectedArtifacts = await multiSelect(
6535
- io,
6536
- "Which artifacts should be generated?",
6537
- artifactChoices,
6538
- { defaults: presetEnabled }
6539
- );
6540
- for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
6541
- const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
6542
- answers.includeSensitiveFiles = await confirm(
6543
- io,
6544
- "Include credential files (.env, *.pem, .npmrc, .ssh/)?",
6545
- { defaultValue: false }
6546
- );
6547
- io.output.write("\n Review\n\n");
6548
- io.output.write(` project ${answers.preset}
6585
+ const scopeStep = async () => {
6586
+ for (; ; ) {
6587
+ const scope = await select(
6588
+ io,
6589
+ "What should go into the snapshot?",
6590
+ [
6591
+ { value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
6592
+ { value: "folders", label: "Only specific folders\u2026" },
6593
+ { value: "no-tests", label: "Everything, but skip tests" },
6594
+ { value: "custom", label: "Let me write glob patterns" }
6595
+ ],
6596
+ { defaultValue: lastScope, allowBack: true }
6597
+ );
6598
+ lastScope = scope;
6599
+ answers.include = [...baseInclude];
6600
+ answers.excludePatterns = [...baseExclude];
6601
+ try {
6602
+ if (scope === "folders") {
6603
+ const dirs = topLevelDirs(options.cwd).slice(0, 40);
6604
+ if (dirs.length === 0) {
6605
+ notes.push("No top-level directories found; the snapshot will include everything.");
6606
+ } else {
6607
+ const chosen = await multiSelect(
6608
+ io,
6609
+ "Select folders",
6610
+ dirs.map((name) => ({ value: name, label: `${name}/` })),
6611
+ { defaults: dirs, allowBack: true }
6612
+ );
6613
+ answers.include = chosen.map((name) => `${name}/**`);
6614
+ }
6615
+ } else if (scope === "no-tests") {
6616
+ answers.excludePatterns = [...baseExclude, "**/*.test.*", "**/*.spec.*", "tests", "test"];
6617
+ } else if (scope === "custom") {
6618
+ const raw = await text(io, "Glob patterns to include (comma separated)", {
6619
+ defaultValue: baseInclude.join(", "),
6620
+ allowBack: true
6621
+ });
6622
+ answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
6623
+ }
6624
+ return;
6625
+ } catch (error) {
6626
+ if (error instanceof PromptBack) continue;
6627
+ throw error;
6628
+ }
6629
+ }
6630
+ };
6631
+ const sizeStep = async () => {
6632
+ for (; ; ) {
6633
+ const currentBudget = String(answers.maxPartBytes);
6634
+ const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
6635
+ defaultValue: SIZE_CHOICES.some((choice) => choice.value === currentBudget) ? currentBudget : "custom",
6636
+ allowBack: true
6637
+ });
6638
+ if (sizeChoice !== "custom") {
6639
+ answers.maxPartBytes = sizeChoice;
6640
+ return;
6641
+ }
6642
+ try {
6643
+ const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
6644
+ defaultValue: currentBudget,
6645
+ validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null,
6646
+ allowBack: true
6647
+ });
6648
+ answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
6649
+ return;
6650
+ } catch (error) {
6651
+ if (error instanceof PromptBack) continue;
6652
+ throw error;
6653
+ }
6654
+ }
6655
+ };
6656
+ const artifactsStep = async () => {
6657
+ const enabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
6658
+ const selectedArtifacts = await multiSelect(
6659
+ io,
6660
+ "Which artifacts should be generated?",
6661
+ artifactChoices,
6662
+ { defaults: enabled, allowBack: true }
6663
+ );
6664
+ for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
6665
+ };
6666
+ const credentialsStep = async () => {
6667
+ answers.includeSensitiveFiles = await confirm(
6668
+ io,
6669
+ "Include credential files (.env, *.pem, *.npmrc, *.ssh/)?",
6670
+ { defaultValue: answers.includeSensitiveFiles, allowBack: true }
6671
+ );
6672
+ };
6673
+ const reviewStep = async () => {
6674
+ const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
6675
+ io.output.write("\n Review\n\n");
6676
+ io.output.write(` project ${answers.preset}
6549
6677
  `);
6550
- io.output.write(` include ${answers.include.length ? answers.include.join(", ") : "everything"}
6678
+ io.output.write(` include ${answers.include.length ? answers.include.join(", ") : "everything"}
6551
6679
  `);
6552
- io.output.write(` part size ${String(answers.maxPartBytes)}
6680
+ io.output.write(` part size ${String(answers.maxPartBytes)}
6553
6681
  `);
6554
- io.output.write(` artifacts ${enabledCount} of ${ARTIFACT_NAMES.length}
6682
+ io.output.write(` artifacts ${enabledCount} of ${ARTIFACT_NAMES.length}
6555
6683
  `);
6556
- io.output.write(` credentials ${answers.includeSensitiveFiles ? "included" : "skipped"}
6684
+ io.output.write(` credentials ${answers.includeSensitiveFiles ? "included" : "skipped"}
6557
6685
 
6558
6686
  `);
6559
- io.output.write(" Will write snapshot.config with your answers, plus every other setting\n");
6560
- io.output.write(" at its default \u2014 so nothing is hidden behind the docs.\n\n");
6561
- const proceed = await confirm(io, "Write this and continue?", { defaultValue: true });
6562
- if (!proceed) throw new PromptCancelled();
6687
+ io.output.write(" Will write snapshot.config with your answers, plus every other setting\n");
6688
+ io.output.write(" at its default \u2014 so nothing is hidden behind the docs.\n\n");
6689
+ const proceed = await confirm(io, "Write this and continue?", {
6690
+ defaultValue: true,
6691
+ allowBack: true
6692
+ });
6693
+ if (!proceed) throw new PromptBack();
6694
+ };
6695
+ const walk = async () => {
6696
+ const steps = [scopeStep, sizeStep, artifactsStep, credentialsStep, reviewStep];
6697
+ let index = 0;
6698
+ while (index < steps.length) {
6699
+ try {
6700
+ await steps[index]();
6701
+ index += 1;
6702
+ } catch (error) {
6703
+ if (!(error instanceof PromptBack)) throw error;
6704
+ if (index === 0) return false;
6705
+ index -= 1;
6706
+ io.output.write("\n");
6707
+ }
6708
+ }
6709
+ return true;
6710
+ };
6711
+ for (; ; ) {
6712
+ const pace = await select(io, "How should I set this up?", paceChoices, {
6713
+ defaultValue: fallback ? "choose" : "recommended"
6714
+ });
6715
+ if (pace === "choose") {
6716
+ try {
6717
+ answers.preset = await select(
6718
+ io,
6719
+ "What kind of project is this?",
6720
+ listPresetNames().map(
6721
+ (name) => resolvePresetChoice(fallback ? null : detected.presetName, name)
6722
+ ),
6723
+ { defaultValue: answers.preset, allowBack: true }
6724
+ );
6725
+ } catch (error) {
6726
+ if (error instanceof PromptBack) continue;
6727
+ throw error;
6728
+ }
6729
+ const chosen = detectPreset(options.cwd, answers.preset);
6730
+ answers.preset = chosen.presetName;
6731
+ reason = null;
6732
+ fallback = false;
6733
+ }
6734
+ if (pace === "walk" && !await walk()) continue;
6735
+ break;
6736
+ }
6563
6737
  }
6564
6738
  const configText = renderConfig(answers);
6565
6739
  const preferredName = chooseConfigFileName(options.cwd);
6566
6740
  const presentConfigs = CONFIG_CANDIDATES.filter(
6567
- (name) => fs10.existsSync(path11.join(options.cwd, name))
6741
+ (name) => fs11.existsSync(path12.join(options.cwd, name))
6568
6742
  );
6569
6743
  const present = presentConfigs[0] ?? null;
6570
6744
  if (presentConfigs.length > 1) {
@@ -6573,7 +6747,7 @@ async function runInit(options) {
6573
6747
  );
6574
6748
  }
6575
6749
  const configName = present !== null && options.force ? present : preferredName;
6576
- const configPath = path11.join(options.cwd, configName);
6750
+ const configPath = path12.join(options.cwd, configName);
6577
6751
  const reused = present !== null && options.force !== true && options.reuseConfig === true;
6578
6752
  if (reused) notes.push(`Kept the existing ${present} instead of writing a new one.`);
6579
6753
  if (options.dryRun) {
@@ -6583,6 +6757,8 @@ async function runInit(options) {
6583
6757
  written: false,
6584
6758
  reused,
6585
6759
  preset: answers.preset,
6760
+ reason,
6761
+ fallback,
6586
6762
  scripts: [],
6587
6763
  gitignoreEntries: [],
6588
6764
  dependency: { name: GENERATOR_NAME, range: `^${GENERATOR_VERSION}`, added: false },
@@ -6604,7 +6780,7 @@ async function runInit(options) {
6604
6780
  'package.json already defines a "snapshot" config. Remove that key, or re-run with --force to write a config file that takes precedence over it.'
6605
6781
  );
6606
6782
  }
6607
- if (fs10.existsSync(configPath) && !options.force && !reused) {
6783
+ if (fs11.existsSync(configPath) && !options.force && !reused) {
6608
6784
  throw new Error(`${configName} already exists. Re-run with --force to replace it.`);
6609
6785
  }
6610
6786
  if (!reused) writeFileOrExplain(configPath, configText);
@@ -6617,12 +6793,56 @@ async function runInit(options) {
6617
6793
  written: true,
6618
6794
  reused,
6619
6795
  preset: answers.preset,
6796
+ reason,
6797
+ fallback,
6620
6798
  scripts,
6621
6799
  gitignoreEntries: ignore.added,
6622
6800
  dependency,
6623
6801
  notes
6624
6802
  };
6625
6803
  }
6804
+ function describeTarget(cwd) {
6805
+ const relative7 = path12.relative(process.cwd(), cwd);
6806
+ if (relative7 === "") return "this directory";
6807
+ if (relative7.startsWith("..") || path12.isAbsolute(relative7)) return cwd;
6808
+ return relative7;
6809
+ }
6810
+ function firstRunMessage(reason, cwd) {
6811
+ const where = describeTarget(cwd);
6812
+ if (reason === "empty") {
6813
+ return `Nothing to snapshot: ${where} has no files. Run this inside the repository you want to snapshot, or pass --cwd.`;
6814
+ }
6815
+ return `No configuration found in ${where} and this session is not interactive, so nothing was generated.
6816
+ Choose one:
6817
+ ${GENERATOR_NAME} --autopilot detect the project, set it up and generate, no questions
6818
+ ${GENERATOR_NAME} init --yes write a config from the detected defaults, then generate
6819
+ ${GENERATOR_NAME} init write a config interactively`;
6820
+ }
6821
+ function planFirstRun(facts) {
6822
+ if (facts.emptyTarget) return { action: "refuse", exitCode: 1, reason: "empty" };
6823
+ if (facts.autopilot) return { action: "setup", interactive: false };
6824
+ if (facts.hasConfig || facts.hasPriorSnapshot) return { action: "generate" };
6825
+ if (facts.interactive) return { action: "setup", interactive: true };
6826
+ return { action: "refuse", exitCode: 2, reason: "undecided" };
6827
+ }
6828
+ var NOT_CONTENT = /* @__PURE__ */ new Set([".git", ".hg", ".svn", ".bzr", ".DS_Store", "Thumbs.db"]);
6829
+ function isEffectivelyEmpty(cwd) {
6830
+ try {
6831
+ return fs11.readdirSync(cwd).every((entry) => NOT_CONTENT.has(entry));
6832
+ } catch {
6833
+ return false;
6834
+ }
6835
+ }
6836
+ function hasPriorSnapshot(cwd, outDir) {
6837
+ let parsed;
6838
+ try {
6839
+ parsed = JSON.parse(fs11.readFileSync(path12.join(cwd, outDir, ".codebase-snapshot"), "utf8"));
6840
+ } catch {
6841
+ return false;
6842
+ }
6843
+ if (parsed === null || typeof parsed !== "object") return false;
6844
+ return isOurGeneratorName(parsed.generator);
6845
+ }
6626
6846
 
6627
6847
  // src/index.ts
6628
6848
  var SCHEMA_VERSION = "3.0.0";
@@ -6704,13 +6924,13 @@ function buildImportResolver(files) {
6704
6924
  const relativePath = file.relativePath;
6705
6925
  remember(byPath, relativePath, relativePath);
6706
6926
  remember(byPathFolded, relativePath.toLowerCase(), relativePath);
6707
- const extension = path11.posix.extname(relativePath);
6927
+ const extension = path12.posix.extname(relativePath);
6708
6928
  if (!SOURCE_EXTENSIONS.has(extension)) continue;
6709
6929
  const stem = relativePath.slice(0, -extension.length);
6710
6930
  remember(byStem, stem, relativePath);
6711
6931
  remember(byStemFolded, stem.toLowerCase(), relativePath);
6712
- if (path11.posix.basename(stem) === "index") {
6713
- const directory = path11.posix.dirname(stem);
6932
+ if (path12.posix.basename(stem) === "index") {
6933
+ const directory = path12.posix.dirname(stem);
6714
6934
  remember(byStem, directory, relativePath);
6715
6935
  remember(byStemFolded, directory.toLowerCase(), relativePath);
6716
6936
  }
@@ -6719,8 +6939,8 @@ function buildImportResolver(files) {
6719
6939
  }
6720
6940
  function resolveSpecifier(fromFile, specifier, resolver) {
6721
6941
  if (!RELATIVE_SPECIFIER.test(specifier)) return null;
6722
- const joined = path11.posix.normalize(
6723
- path11.posix.join(path11.posix.dirname(fromFile), specifier)
6942
+ const joined = path12.posix.normalize(
6943
+ path12.posix.join(path12.posix.dirname(fromFile), specifier)
6724
6944
  );
6725
6945
  const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
6726
6946
  const direct = exact(joined);
@@ -6813,11 +7033,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
6813
7033
  return errors;
6814
7034
  }
6815
7035
  function resolveSafeOutputDirs(config) {
6816
- const cwd = path11.resolve(config.cwd);
6817
- const outDir = path11.resolve(cwd, config.outDir);
6818
- const tmpDir = path11.resolve(cwd, config.tmpDir);
6819
- const root = path11.parse(cwd).root;
6820
- const contains = (parent, child) => child === parent || child.startsWith(`${parent}${path11.sep}`);
7036
+ const cwd = path12.resolve(config.cwd);
7037
+ const outDir = path12.resolve(cwd, config.outDir);
7038
+ const tmpDir = path12.resolve(cwd, config.tmpDir);
7039
+ const root = path12.parse(cwd).root;
7040
+ const contains = (parent, child) => child === parent || child.startsWith(`${parent}${path12.sep}`);
6821
7041
  const reject = (reason) => {
6822
7042
  throw new Error(
6823
7043
  `Refusing to generate: ${reason}. outDir=${outDir} tmpDir=${tmpDir} cwd=${cwd}`
@@ -6873,14 +7093,14 @@ function listEntries(dir) {
6873
7093
  const walk = (absolute, prefix) => {
6874
7094
  let children;
6875
7095
  try {
6876
- children = fs10.readdirSync(absolute, { withFileTypes: true });
7096
+ children = fs11.readdirSync(absolute, { withFileTypes: true });
6877
7097
  } catch {
6878
7098
  return;
6879
7099
  }
6880
7100
  for (const child of children) {
6881
- const relative6 = prefix === "" ? child.name : `${prefix}/${child.name}`;
6882
- entries.push(relative6);
6883
- if (child.isDirectory()) walk(path11.join(absolute, child.name), relative6);
7101
+ const relative7 = prefix === "" ? child.name : `${prefix}/${child.name}`;
7102
+ entries.push(relative7);
7103
+ if (child.isDirectory()) walk(path12.join(absolute, child.name), relative7);
6884
7104
  }
6885
7105
  };
6886
7106
  walk(dir, "");
@@ -6897,28 +7117,28 @@ function markerPayload(dir, state, id, createdAt) {
6897
7117
  pid: process.pid,
6898
7118
  entries: listEntries(dir)
6899
7119
  };
6900
- const manifestPath = path11.join(dir, "manifest.json");
6901
- if (fs10.existsSync(manifestPath)) {
6902
- marker2.manifestSha256 = sha256Hex2(fs10.readFileSync(manifestPath));
7120
+ const manifestPath = path12.join(dir, "manifest.json");
7121
+ if (fs11.existsSync(manifestPath)) {
7122
+ marker2.manifestSha256 = sha256Hex2(fs11.readFileSync(manifestPath));
6903
7123
  }
6904
7124
  return marker2;
6905
7125
  }
6906
7126
  function writeOwnershipMarker(dir, marker2) {
6907
- fs10.writeFileSync(
6908
- path11.join(dir, OWNERSHIP_MARKER),
7127
+ fs11.writeFileSync(
7128
+ path12.join(dir, OWNERSHIP_MARKER),
6909
7129
  `${JSON.stringify(marker2, null, 2)}
6910
7130
  `,
6911
7131
  "utf8"
6912
7132
  );
6913
7133
  }
6914
7134
  function readOwnershipMarker(dir) {
6915
- const markerPath = path11.join(dir, OWNERSHIP_MARKER);
6916
- if (!fs10.existsSync(markerPath)) {
7135
+ const markerPath = path12.join(dir, OWNERSHIP_MARKER);
7136
+ if (!fs11.existsSync(markerPath)) {
6917
7137
  return { marker: null, problem: "it holds no .codebase-snapshot marker" };
6918
7138
  }
6919
7139
  let raw;
6920
7140
  try {
6921
- raw = fs10.readFileSync(markerPath, "utf8");
7141
+ raw = fs11.readFileSync(markerPath, "utf8");
6922
7142
  } catch {
6923
7143
  return { marker: null, problem: "its .codebase-snapshot marker could not be read" };
6924
7144
  }
@@ -6986,11 +7206,11 @@ function assessSnapshotDirectory(dir) {
6986
7206
  marker: marker2
6987
7207
  };
6988
7208
  }
6989
- const manifestPath = path11.join(dir, "manifest.json");
6990
- if (fs10.existsSync(manifestPath)) {
7209
+ const manifestPath = path12.join(dir, "manifest.json");
7210
+ if (fs11.existsSync(manifestPath)) {
6991
7211
  let raw;
6992
7212
  try {
6993
- raw = fs10.readFileSync(manifestPath, "utf8");
7213
+ raw = fs11.readFileSync(manifestPath, "utf8");
6994
7214
  } catch {
6995
7215
  return {
6996
7216
  owned: false,
@@ -7072,13 +7292,13 @@ function containsNoFiles(dir) {
7072
7292
  const current = pending.pop();
7073
7293
  let entries;
7074
7294
  try {
7075
- entries = fs10.readdirSync(current, { withFileTypes: true });
7295
+ entries = fs11.readdirSync(current, { withFileTypes: true });
7076
7296
  } catch {
7077
7297
  return false;
7078
7298
  }
7079
7299
  for (const entry of entries) {
7080
7300
  if (entry.isDirectory()) {
7081
- pending.push(path11.join(current, entry.name));
7301
+ pending.push(path12.join(current, entry.name));
7082
7302
  } else if (!IGNORABLE_ENTRIES.has(entry.name)) {
7083
7303
  return false;
7084
7304
  }
@@ -7087,13 +7307,13 @@ function containsNoFiles(dir) {
7087
7307
  return true;
7088
7308
  }
7089
7309
  function assertReplaceable(target, label, secretRules) {
7090
- if (!fs10.existsSync(target)) return;
7091
- if (!fs10.statSync(target).isDirectory()) {
7310
+ if (!fs11.existsSync(target)) return;
7311
+ if (!fs11.statSync(target).isDirectory()) {
7092
7312
  throw new Error(
7093
7313
  `Refusing to write into ${label} ${target}: it exists but is not a directory.`
7094
7314
  );
7095
7315
  }
7096
- const entries = fs10.readdirSync(target);
7316
+ const entries = fs11.readdirSync(target);
7097
7317
  if (entries.length === 0) return;
7098
7318
  if (entries.every((name) => IGNORABLE_ENTRIES.has(name))) return;
7099
7319
  if (containsNoFiles(target)) return;
@@ -7113,18 +7333,18 @@ function assertReplaceable(target, label, secretRules) {
7113
7333
  );
7114
7334
  }
7115
7335
  function assertStagingRootUsable(tmpDir, secretRules) {
7116
- if (!fs10.existsSync(tmpDir)) return;
7117
- if (!fs10.statSync(tmpDir).isDirectory()) {
7336
+ if (!fs11.existsSync(tmpDir)) return;
7337
+ if (!fs11.statSync(tmpDir).isDirectory()) {
7118
7338
  throw new Error(`Refusing to write into tmpDir ${tmpDir}: it exists but is not a directory.`);
7119
7339
  }
7120
- const entries = fs10.readdirSync(tmpDir);
7340
+ const entries = fs11.readdirSync(tmpDir);
7121
7341
  if (entries.length === 0) return;
7122
7342
  const foreign = entries.filter((name) => {
7123
- const full = path11.join(tmpDir, name);
7343
+ const full = path12.join(tmpDir, name);
7124
7344
  if (!name.startsWith(RUN_DIR_PREFIX)) return true;
7125
7345
  let stats;
7126
7346
  try {
7127
- stats = fs10.statSync(full);
7347
+ stats = fs11.statSync(full);
7128
7348
  } catch {
7129
7349
  return true;
7130
7350
  }
@@ -7132,7 +7352,7 @@ function assertStagingRootUsable(tmpDir, secretRules) {
7132
7352
  const { marker: marker2 } = readOwnershipMarker(full);
7133
7353
  if (marker2) return false;
7134
7354
  try {
7135
- return fs10.readdirSync(full).length > 0;
7355
+ return fs11.readdirSync(full).length > 0;
7136
7356
  } catch {
7137
7357
  return true;
7138
7358
  }
@@ -7147,13 +7367,13 @@ function assertStagingRootUsable(tmpDir, secretRules) {
7147
7367
  function cleanStaleRunDirs(tmpDir) {
7148
7368
  let entries;
7149
7369
  try {
7150
- entries = fs10.readdirSync(tmpDir);
7370
+ entries = fs11.readdirSync(tmpDir);
7151
7371
  } catch {
7152
7372
  return;
7153
7373
  }
7154
7374
  for (const name of entries) {
7155
7375
  if (!name.startsWith(RUN_DIR_PREFIX)) continue;
7156
- const full = path11.join(tmpDir, name);
7376
+ const full = path12.join(tmpDir, name);
7157
7377
  const { marker: marker2 } = readOwnershipMarker(full);
7158
7378
  if (marker2) {
7159
7379
  const created = Date.parse(marker2.createdAt);
@@ -7161,33 +7381,33 @@ function cleanStaleRunDirs(tmpDir) {
7161
7381
  const abandoned = typeof marker2.pid === "number" && !isProcessAlive(marker2.pid);
7162
7382
  if (aged || abandoned) {
7163
7383
  try {
7164
- fs10.rmSync(full, { recursive: true, force: true });
7384
+ fs11.rmSync(full, { recursive: true, force: true });
7165
7385
  } catch {
7166
7386
  }
7167
7387
  }
7168
7388
  continue;
7169
7389
  }
7170
7390
  try {
7171
- if (fs10.readdirSync(full).length === 0) fs10.rmSync(full, { recursive: true, force: true });
7391
+ if (fs11.readdirSync(full).length === 0) fs11.rmSync(full, { recursive: true, force: true });
7172
7392
  } catch {
7173
7393
  }
7174
7394
  }
7175
7395
  }
7176
7396
  function removeIfEmpty(dir) {
7177
7397
  try {
7178
- if (fs10.readdirSync(dir).length === 0) fs10.rmSync(dir, { recursive: true, force: true });
7398
+ if (fs11.readdirSync(dir).length === 0) fs11.rmSync(dir, { recursive: true, force: true });
7179
7399
  } catch {
7180
7400
  }
7181
7401
  }
7182
7402
  function ensureParentDirectory(target, label) {
7183
- const parent = path11.dirname(target);
7184
- if (fs10.existsSync(parent) && !fs10.statSync(parent).isDirectory()) {
7403
+ const parent = path12.dirname(target);
7404
+ if (fs11.existsSync(parent) && !fs11.statSync(parent).isDirectory()) {
7185
7405
  throw new Error(
7186
7406
  `Refusing to create ${label} ${target}: ${parent} exists but is not a directory.`
7187
7407
  );
7188
7408
  }
7189
7409
  try {
7190
- fs10.mkdirSync(parent, { recursive: true });
7410
+ fs11.mkdirSync(parent, { recursive: true });
7191
7411
  } catch (err) {
7192
7412
  throw new Error(
7193
7413
  `Refusing to create ${label} ${target}: its parent ${parent} could not be created (${err.message}).`
@@ -7196,7 +7416,7 @@ function ensureParentDirectory(target, label) {
7196
7416
  }
7197
7417
  function acquireRunLock(stagingRoot) {
7198
7418
  const key = lockKeyFor(stagingRoot);
7199
- const lockPath = path11.join(os.tmpdir(), `codebase-snapshot-${key}.lock`);
7419
+ const lockPath = path12.join(os.tmpdir(), `codebase-snapshot-${key}.lock`);
7200
7420
  const payload = `${JSON.stringify({
7201
7421
  pid: process.pid,
7202
7422
  stagingRoot,
@@ -7205,22 +7425,22 @@ function acquireRunLock(stagingRoot) {
7205
7425
  `;
7206
7426
  for (let attempt = 0; attempt < 2; attempt += 1) {
7207
7427
  try {
7208
- const handle = fs10.openSync(lockPath, "wx");
7428
+ const handle = fs11.openSync(lockPath, "wx");
7209
7429
  try {
7210
- fs10.writeSync(handle, payload);
7430
+ fs11.writeSync(handle, payload);
7211
7431
  } finally {
7212
- fs10.closeSync(handle);
7432
+ fs11.closeSync(handle);
7213
7433
  }
7214
7434
  return () => {
7215
7435
  try {
7216
- fs10.rmSync(lockPath, { force: true });
7436
+ fs11.rmSync(lockPath, { force: true });
7217
7437
  } catch {
7218
7438
  }
7219
7439
  };
7220
7440
  } catch (err) {
7221
7441
  if (err.code !== "EEXIST") throw err;
7222
7442
  if (attempt === 0 && lockOwnerIsGone(lockPath)) {
7223
- fs10.rmSync(lockPath, { force: true });
7443
+ fs11.rmSync(lockPath, { force: true });
7224
7444
  continue;
7225
7445
  }
7226
7446
  throw new Error(
@@ -7233,7 +7453,7 @@ function acquireRunLock(stagingRoot) {
7233
7453
  function lockKeyFor(stagingRoot) {
7234
7454
  let key = stagingRoot;
7235
7455
  try {
7236
- key = fs10.realpathSync(stagingRoot);
7456
+ key = fs11.realpathSync(stagingRoot);
7237
7457
  } catch {
7238
7458
  }
7239
7459
  if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
@@ -7242,7 +7462,7 @@ function lockKeyFor(stagingRoot) {
7242
7462
  function lockOwnerIsGone(lockPath) {
7243
7463
  let raw;
7244
7464
  try {
7245
- raw = fs10.readFileSync(lockPath, "utf8");
7465
+ raw = fs11.readFileSync(lockPath, "utf8");
7246
7466
  } catch {
7247
7467
  return false;
7248
7468
  }
@@ -7262,12 +7482,12 @@ function lockOwnerIsGone(lockPath) {
7262
7482
  }
7263
7483
  function writeManifestAtomically(manifestPath, manifest) {
7264
7484
  const tempPath = `${manifestPath}.${process.pid}.tmp`;
7265
- fs10.writeFileSync(tempPath, JSON.stringify(manifest, null, 2), "utf8");
7485
+ fs11.writeFileSync(tempPath, JSON.stringify(manifest, null, 2), "utf8");
7266
7486
  try {
7267
- fs10.renameSync(tempPath, manifestPath);
7487
+ fs11.renameSync(tempPath, manifestPath);
7268
7488
  } catch (err) {
7269
7489
  try {
7270
- fs10.rmSync(tempPath, { force: true });
7490
+ fs11.rmSync(tempPath, { force: true });
7271
7491
  } catch {
7272
7492
  }
7273
7493
  throw err;
@@ -7290,31 +7510,31 @@ function swapIntoPlace(stagingDir, outDir) {
7290
7510
  }
7291
7511
  function swapOnce(stagingDir, outDir) {
7292
7512
  const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
7293
- const hadPrevious = fs10.existsSync(outDir);
7294
- if (hadPrevious) fs10.renameSync(outDir, backupDir);
7513
+ const hadPrevious = fs11.existsSync(outDir);
7514
+ if (hadPrevious) fs11.renameSync(outDir, backupDir);
7295
7515
  try {
7296
7516
  try {
7297
- fs10.renameSync(stagingDir, outDir);
7517
+ fs11.renameSync(stagingDir, outDir);
7298
7518
  } catch (err) {
7299
7519
  if (err.code !== "EXDEV") throw err;
7300
7520
  const partialDir = `${outDir}.partial-${process.pid}-${Date.now()}`;
7301
7521
  try {
7302
- fs10.cpSync(stagingDir, partialDir, { recursive: true });
7303
- fs10.renameSync(partialDir, outDir);
7304
- fs10.rmSync(stagingDir, { recursive: true, force: true });
7522
+ fs11.cpSync(stagingDir, partialDir, { recursive: true });
7523
+ fs11.renameSync(partialDir, outDir);
7524
+ fs11.rmSync(stagingDir, { recursive: true, force: true });
7305
7525
  } catch (copyErr) {
7306
7526
  try {
7307
- fs10.rmSync(partialDir, { recursive: true, force: true });
7527
+ fs11.rmSync(partialDir, { recursive: true, force: true });
7308
7528
  } catch {
7309
7529
  }
7310
7530
  throw copyErr;
7311
7531
  }
7312
7532
  }
7313
- if (hadPrevious) fs10.rmSync(backupDir, { recursive: true, force: true });
7533
+ if (hadPrevious) fs11.rmSync(backupDir, { recursive: true, force: true });
7314
7534
  } catch (err) {
7315
- if (hadPrevious && fs10.existsSync(backupDir)) {
7316
- if (fs10.existsSync(outDir)) fs10.rmSync(outDir, { recursive: true, force: true });
7317
- fs10.renameSync(backupDir, outDir);
7535
+ if (hadPrevious && fs11.existsSync(backupDir)) {
7536
+ if (fs11.existsSync(outDir)) fs11.rmSync(outDir, { recursive: true, force: true });
7537
+ fs11.renameSync(backupDir, outDir);
7318
7538
  }
7319
7539
  throw err;
7320
7540
  }
@@ -7409,8 +7629,8 @@ function generateSnapshot(configInput, options) {
7409
7629
  // first run cannot have marked them yet, so the name alone keeps a run from recording its own
7410
7630
  // output — but only at these exact paths, not at every directory that shares the name.
7411
7631
  toolOwnedPaths: [config.outDir, config.tmpDir].map(
7412
- (directory) => path11.relative(config.cwd, path11.resolve(config.cwd, directory)).replace(/\\/g, "/")
7413
- ).filter((relative6) => relative6.length > 0 && !relative6.startsWith(".."))
7632
+ (directory) => path12.relative(config.cwd, path12.resolve(config.cwd, directory)).replace(/\\/g, "/")
7633
+ ).filter((relative7) => relative7.length > 0 && !relative7.startsWith(".."))
7414
7634
  });
7415
7635
  const files = [];
7416
7636
  const monolithBodies = /* @__PURE__ */ new Map();
@@ -7430,12 +7650,12 @@ function generateSnapshot(configInput, options) {
7430
7650
  let totalSizeBytes = 0;
7431
7651
  let largeFileCount = 0;
7432
7652
  for (const relativePath of collection.files) {
7433
- const absolutePath = path11.join(config.cwd, relativePath);
7653
+ const absolutePath = path12.join(config.cwd, relativePath);
7434
7654
  let stats;
7435
7655
  try {
7436
- stats = fs10.statSync(absolutePath);
7656
+ stats = fs11.statSync(absolutePath);
7437
7657
  if (!stats.isFile()) {
7438
- const nestedRepository = stats.isDirectory() && fs10.existsSync(path11.join(absolutePath, ".git"));
7658
+ const nestedRepository = stats.isDirectory() && fs11.existsSync(path12.join(absolutePath, ".git"));
7439
7659
  skipped.push(
7440
7660
  nestedRepository ? {
7441
7661
  path: relativePath,
@@ -7455,7 +7675,7 @@ function generateSnapshot(configInput, options) {
7455
7675
  }
7456
7676
  let buffer;
7457
7677
  try {
7458
- buffer = fs10.readFileSync(absolutePath);
7678
+ buffer = fs11.readFileSync(absolutePath);
7459
7679
  } catch {
7460
7680
  skipped.push({ path: relativePath, reason: "read-error" });
7461
7681
  continue;
@@ -7785,12 +8005,12 @@ function generateSnapshot(configInput, options) {
7785
8005
  let stagingDir = null;
7786
8006
  try {
7787
8007
  ensureParentDirectory(outDir, "outDir");
7788
- fs10.mkdirSync(tmpDir, { recursive: true });
8008
+ fs11.mkdirSync(tmpDir, { recursive: true });
7789
8009
  cleanStaleRunDirs(tmpDir);
7790
8010
  const snapshotId = crypto.randomUUID();
7791
8011
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
7792
- stagingDir = path11.join(tmpDir, `${RUN_DIR_PREFIX}${process.pid}-${snapshotId.slice(0, 8)}`);
7793
- fs10.mkdirSync(stagingDir);
8012
+ stagingDir = path12.join(tmpDir, `${RUN_DIR_PREFIX}${process.pid}-${snapshotId.slice(0, 8)}`);
8013
+ fs11.mkdirSync(stagingDir);
7794
8014
  writeOwnershipMarker(stagingDir, {
7795
8015
  generator: GENERATOR_NAME,
7796
8016
  version: GENERATOR_VERSION,
@@ -7800,41 +8020,41 @@ function generateSnapshot(configInput, options) {
7800
8020
  id: snapshotId,
7801
8021
  pid: process.pid
7802
8022
  });
7803
- const partsDir = path11.join(stagingDir, "parts");
7804
- const reportsDir = path11.join(stagingDir, "reports");
7805
- if (artifacts.parts) fs10.mkdirSync(partsDir, { recursive: true });
8023
+ const partsDir = path12.join(stagingDir, "parts");
8024
+ const reportsDir = path12.join(stagingDir, "reports");
8025
+ if (artifacts.parts) fs11.mkdirSync(partsDir, { recursive: true });
7806
8026
  if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
7807
- fs10.mkdirSync(reportsDir, { recursive: true });
8027
+ fs11.mkdirSync(reportsDir, { recursive: true });
7808
8028
  }
7809
8029
  if (artifacts.parts) {
7810
8030
  for (const part of parts) {
7811
- fs10.writeFileSync(path11.join(partsDir, part.filename), part.markdown, "utf8");
8031
+ fs11.writeFileSync(path12.join(partsDir, part.filename), part.markdown, "utf8");
7812
8032
  }
7813
8033
  }
7814
8034
  if (projectTree !== null) {
7815
- fs10.writeFileSync(path11.join(reportsDir, "project-tree.md"), projectTree, "utf8");
8035
+ fs11.writeFileSync(path12.join(reportsDir, "project-tree.md"), projectTree, "utf8");
7816
8036
  }
7817
8037
  if (artifacts.navigation) {
7818
- fs10.writeFileSync(path11.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
8038
+ fs11.writeFileSync(path12.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
7819
8039
  }
7820
8040
  if (artifacts.audit) {
7821
- fs10.writeFileSync(
7822
- path11.join(reportsDir, "audit.md"),
8041
+ fs11.writeFileSync(
8042
+ path12.join(reportsDir, "audit.md"),
7823
8043
  generateAuditReport(manifest, secretFindings, config.secretRules),
7824
8044
  "utf8"
7825
8045
  );
7826
8046
  }
7827
8047
  if (artifacts.readme) {
7828
- fs10.writeFileSync(path11.join(stagingDir, "README.md"), generateReadme(manifest, artifacts), "utf8");
8048
+ fs11.writeFileSync(path12.join(stagingDir, "README.md"), generateReadme(manifest, artifacts), "utf8");
7829
8049
  }
7830
8050
  if (artifacts.manifest) {
7831
- fs10.writeFileSync(path11.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
8051
+ fs11.writeFileSync(path12.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
7832
8052
  }
7833
8053
  if (monolithJson !== null) {
7834
- fs10.writeFileSync(path11.join(stagingDir, "full-codebase.json"), monolithJson, "utf8");
8054
+ fs11.writeFileSync(path12.join(stagingDir, "full-codebase.json"), monolithJson, "utf8");
7835
8055
  }
7836
8056
  if (monolithMarkdown !== null) {
7837
- fs10.writeFileSync(path11.join(stagingDir, "full-codebase.md"), monolithMarkdown, "utf8");
8057
+ fs11.writeFileSync(path12.join(stagingDir, "full-codebase.md"), monolithMarkdown, "utf8");
7838
8058
  }
7839
8059
  writeOwnershipMarker(stagingDir, markerPayload(stagingDir, "complete", snapshotId, createdAt));
7840
8060
  swapIntoPlace(stagingDir, outDir);
@@ -7843,7 +8063,7 @@ function generateSnapshot(configInput, options) {
7843
8063
  const pendingHook = runAfterGenerate(config, manifest);
7844
8064
  const shipHookState = () => {
7845
8065
  if (artifacts.manifest && manifest.warnings.length !== warningsBeforeHook) {
7846
- writeManifestAtomically(path11.join(outDir, "manifest.json"), manifest);
8066
+ writeManifestAtomically(path12.join(outDir, "manifest.json"), manifest);
7847
8067
  }
7848
8068
  writeOwnershipMarker(outDir, markerPayload(outDir, "complete", snapshotId, createdAt));
7849
8069
  };
@@ -7866,7 +8086,7 @@ function generateSnapshot(configInput, options) {
7866
8086
  } catch (err) {
7867
8087
  if (stagingDir !== null) {
7868
8088
  try {
7869
- fs10.rmSync(stagingDir, { recursive: true, force: true });
8089
+ fs11.rmSync(stagingDir, { recursive: true, force: true });
7870
8090
  } catch {
7871
8091
  }
7872
8092
  }
@@ -7880,6 +8100,6 @@ async function generateSnapshotAsync(configInput, options) {
7880
8100
  return generateSnapshot(configInput, options);
7881
8101
  }
7882
8102
 
7883
- export { ARTIFACT_NAMES, DEFAULT_ANSWERS, DEFAULT_SECRET_PATTERNS, GENERATOR_NAME, GENERATOR_VERSION, LANGUAGE_PROFILES, MIN_MAX_PART_BYTES, PRESETS, PromptCancelled, SCHEMA_VERSION, anchorForPath, appendGitignoreEntries, assertReplaceable, assertUsableConfigInput, collectFiles, compareManifests, countInvalidUtf8Bytes, countLines, decodeText, defineConfig, describeManifestShapeProblem, detectLanguage, detectPreset, escapeControls, explainPath, fenceForContent, fenceForText, formatNumber, generateProjectTree, generateSnapshot, generateSnapshotAsync, getPreset, gitignorePath, globToRegExp, isBinaryBuffer, isBinaryExtension, isBinaryFile, isDiscoveryMode, isInteractive, isResolvedConfig, isSensitivePath, listPresetNames, loadConfigFile, loadConfigFileDetailed, loadManifest, markResolvedConfig, matchRule, matchesExclude, normalizeResolvedConfig, parseByteSize, partitionFiles, profileForPath, renderConfig, renderDiffText, renderExplainText, renumberParts, resolveConfig, resolveSafeOutputDirs, runInit, scanAndRedactSecrets, stripJsonComments, summarizeJson, summarizeMarkdown, summarizeTypeScriptSource, summarizeWithProfile, supportsTypeScriptImports, truncatePreview, truncateToBytes, unknownPresetMessage, validateSnapshot };
7884
- //# sourceMappingURL=chunk-HT7J3NWT.js.map
7885
- //# sourceMappingURL=chunk-HT7J3NWT.js.map
8103
+ export { ARTIFACT_NAMES, DEFAULT_ANSWERS, DEFAULT_SECRET_PATTERNS, GENERATOR_NAME, GENERATOR_VERSION, LANGUAGE_PROFILES, MIN_MAX_PART_BYTES, PRESETS, PromptCancelled, SCHEMA_VERSION, anchorForPath, appendGitignoreEntries, assertReplaceable, assertUsableConfigInput, collectFiles, compareManifests, countInvalidUtf8Bytes, countLines, decodeText, defaultIO, defineConfig, describeManifestShapeProblem, detectLanguage, detectPreset, escapeControls, explainPath, fenceForContent, fenceForText, findConfigFile, firstRunMessage, formatNumber, generateProjectTree, generateSnapshot, generateSnapshotAsync, getPreset, gitignorePath, globToRegExp, hasPriorSnapshot, isBinaryBuffer, isBinaryExtension, isBinaryFile, isDiscoveryMode, isEffectivelyEmpty, isInteractive, isResolvedConfig, isSensitivePath, listPresetNames, loadConfigFile, loadConfigFileDetailed, loadManifest, markResolvedConfig, matchRule, matchesExclude, normalizeResolvedConfig, parseByteSize, partitionFiles, planFirstRun, profileForPath, renderConfig, renderDiffText, renderExplainText, renumberParts, resolveConfig, resolveSafeOutputDirs, runInit, scanAndRedactSecrets, stripJsonComments, summarizeJson, summarizeMarkdown, summarizeTypeScriptSource, summarizeWithProfile, supportsTypeScriptImports, truncatePreview, truncateToBytes, unknownPresetMessage, validateSnapshot };
8104
+ //# sourceMappingURL=chunk-B336MMNC.js.map
8105
+ //# sourceMappingURL=chunk-B336MMNC.js.map