codebase-snapshot 0.9.4 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -5488,7 +5488,7 @@ function validateSnapshot(configInput) {
5488
5488
 
5489
5489
  // src/version.ts
5490
5490
  var GENERATOR_NAME = "codebase-snapshot";
5491
- var GENERATOR_VERSION = "0.9.4";
5491
+ var GENERATOR_VERSION = "0.9.5";
5492
5492
  var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
5493
5493
  function isOurGeneratorName(name) {
5494
5494
  if (typeof name !== "string") return false;
@@ -6061,6 +6061,12 @@ var PromptCancelled = class extends Error {
6061
6061
  this.name = "PromptCancelled";
6062
6062
  }
6063
6063
  };
6064
+ var PromptBack = class extends Error {
6065
+ constructor() {
6066
+ super("Prompt back");
6067
+ this.name = "PromptBack";
6068
+ }
6069
+ };
6064
6070
  function defaultIO() {
6065
6071
  return { input: process.stdin, output: process.stdout };
6066
6072
  }
@@ -6084,35 +6090,40 @@ function choiceLine(choice, active) {
6084
6090
  const hint = choice.hint ? ` ${choice.hint}` : "";
6085
6091
  return ` ${marker(active)} ${choice.label}${hint}`;
6086
6092
  }
6087
- function renderSelect(message, choices, index) {
6093
+ function listFooter(base, allowBack) {
6094
+ return allowBack ? `${base} \xB7 \u2190 back` : base;
6095
+ }
6096
+ function renderSelect(message, choices, index, allowBack = false) {
6088
6097
  const lines = [`\u25C6 ${message}`, ""];
6089
6098
  choices.forEach((choice, position) => lines.push(choiceLine(choice, position === index)));
6090
- lines.push("", " \u2191\u2193 move \xB7 enter select");
6099
+ lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 enter select", allowBack)}`);
6091
6100
  return lines.join("\n");
6092
6101
  }
6093
- function renderMultiSelect(message, choices, index, selected) {
6102
+ function renderMultiSelect(message, choices, index, selected, allowBack = false) {
6094
6103
  const lines = [`\u25C6 ${message}`, ""];
6095
6104
  choices.forEach((choice, position) => {
6096
6105
  const box = selected.has(position) ? "\u25FC" : "\u25FB";
6097
6106
  const hint = choice.hint ? ` ${choice.hint}` : "";
6098
6107
  lines.push(` ${marker(position === index)} ${box} ${choice.label}${hint}`);
6099
6108
  });
6100
- lines.push("", " \u2191\u2193 move \xB7 space toggle \xB7 enter confirm");
6109
+ lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 space toggle \xB7 enter confirm", allowBack)}`);
6101
6110
  return lines.join("\n");
6102
6111
  }
6103
- function renderConfirm(message, value) {
6112
+ function renderConfirm(message, value, allowBack = false) {
6113
+ const footer = allowBack ? "\u2190\u2192 or y/n \xB7 enter confirm \xB7 b back" : "\u2190\u2192 or y/n \xB7 enter confirm";
6104
6114
  return [
6105
6115
  `\u25C6 ${message}`,
6106
6116
  "",
6107
6117
  ` ${marker(value)} Yes`,
6108
6118
  ` ${marker(!value)} No`,
6109
6119
  "",
6110
- " \u2190\u2192 or y/n \xB7 enter confirm"
6120
+ ` ${footer}`
6111
6121
  ].join("\n");
6112
6122
  }
6113
- function renderText(message, value, error) {
6123
+ function renderText(message, value, error, allowBack = false) {
6114
6124
  const lines = [`\u25C6 ${message}`, "", ` ${value || "\u258F"}`];
6115
6125
  if (error) lines.push("", ` \u2716 ${error}`);
6126
+ if (allowBack) lines.push("", " enter confirm \xB7 \u2190 back");
6116
6127
  return lines.join("\n");
6117
6128
  }
6118
6129
  function keyReader(input) {
@@ -6161,14 +6172,18 @@ var Frame = class {
6161
6172
  function isCancel(key) {
6162
6173
  return key.ctrl === true && key.name === "c" || key.ctrl === true && key.name === "d";
6163
6174
  }
6175
+ function isListBack(key, allowBack) {
6176
+ return allowBack && (key.name === "left" || key.name === "b");
6177
+ }
6164
6178
  async function select(io, message, choices, options = {}) {
6165
6179
  if (choices.length === 0) throw new Error("select() requires at least one choice.");
6180
+ const allowBack = options.allowBack === true;
6166
6181
  const fallbackIndex = Math.max(
6167
6182
  0,
6168
6183
  choices.findIndex((choice) => choice.value === options.defaultValue)
6169
6184
  );
6170
6185
  if (!isInteractive(io)) {
6171
- io.output.write(`${renderSelect(message, choices, fallbackIndex)}
6186
+ io.output.write(`${renderSelect(message, choices, fallbackIndex, allowBack)}
6172
6187
  `);
6173
6188
  return choices[fallbackIndex].value;
6174
6189
  }
@@ -6177,9 +6192,10 @@ async function select(io, message, choices, options = {}) {
6177
6192
  let index = fallbackIndex;
6178
6193
  try {
6179
6194
  for (; ; ) {
6180
- frame.draw(renderSelect(message, choices, index));
6195
+ frame.draw(renderSelect(message, choices, index, allowBack));
6181
6196
  const key = await reader.next();
6182
6197
  if (isCancel(key)) throw new PromptCancelled();
6198
+ if (isListBack(key, allowBack)) throw new PromptBack();
6183
6199
  if (key.name === "up") index = moveSelection(index, -1, choices.length);
6184
6200
  else if (key.name === "down") index = moveSelection(index, 1, choices.length);
6185
6201
  else if (key.name === "return" || key.name === "enter") {
@@ -6188,18 +6204,22 @@ async function select(io, message, choices, options = {}) {
6188
6204
  } else if (key.name === "j") index = moveSelection(index, 1, choices.length);
6189
6205
  else if (key.name === "k") index = moveSelection(index, -1, choices.length);
6190
6206
  }
6207
+ } catch (error) {
6208
+ frame.erase();
6209
+ throw error;
6191
6210
  } finally {
6192
6211
  reader.close();
6193
6212
  }
6194
6213
  }
6195
6214
  async function multiSelect(io, message, choices, options = {}) {
6215
+ const allowBack = options.allowBack === true;
6196
6216
  const selected = /* @__PURE__ */ new Set();
6197
6217
  choices.forEach((choice, position) => {
6198
6218
  if (options.defaults?.includes(choice.value)) selected.add(position);
6199
6219
  });
6200
6220
  const result = () => choices.filter((_, position) => selected.has(position)).map((choice) => choice.value);
6201
6221
  if (!isInteractive(io)) {
6202
- io.output.write(`${renderMultiSelect(message, choices, 0, selected)}
6222
+ io.output.write(`${renderMultiSelect(message, choices, 0, selected, allowBack)}
6203
6223
  `);
6204
6224
  return result();
6205
6225
  }
@@ -6208,9 +6228,10 @@ async function multiSelect(io, message, choices, options = {}) {
6208
6228
  let index = 0;
6209
6229
  try {
6210
6230
  for (; ; ) {
6211
- frame.draw(renderMultiSelect(message, choices, index, selected));
6231
+ frame.draw(renderMultiSelect(message, choices, index, selected, allowBack));
6212
6232
  const key = await reader.next();
6213
6233
  if (isCancel(key)) throw new PromptCancelled();
6234
+ if (isListBack(key, allowBack)) throw new PromptBack();
6214
6235
  if (key.name === "up") index = moveSelection(index, -1, choices.length);
6215
6236
  else if (key.name === "down") index = moveSelection(index, 1, choices.length);
6216
6237
  else if (key.name === "space") {
@@ -6225,14 +6246,18 @@ async function multiSelect(io, message, choices, options = {}) {
6225
6246
  return result();
6226
6247
  }
6227
6248
  }
6249
+ } catch (error) {
6250
+ frame.erase();
6251
+ throw error;
6228
6252
  } finally {
6229
6253
  reader.close();
6230
6254
  }
6231
6255
  }
6232
6256
  async function confirm(io, message, options = {}) {
6257
+ const allowBack = options.allowBack === true;
6233
6258
  let value = options.defaultValue ?? true;
6234
6259
  if (!isInteractive(io)) {
6235
- io.output.write(`${renderConfirm(message, value)}
6260
+ io.output.write(`${renderConfirm(message, value, allowBack)}
6236
6261
  `);
6237
6262
  return value;
6238
6263
  }
@@ -6240,9 +6265,10 @@ async function confirm(io, message, options = {}) {
6240
6265
  const reader = keyReader(io.input);
6241
6266
  try {
6242
6267
  for (; ; ) {
6243
- frame.draw(renderConfirm(message, value));
6268
+ frame.draw(renderConfirm(message, value, allowBack));
6244
6269
  const key = await reader.next();
6245
6270
  if (isCancel(key)) throw new PromptCancelled();
6271
+ if (allowBack && key.name === "b") throw new PromptBack();
6246
6272
  if (key.name === "up" || key.name === "left") value = true;
6247
6273
  else if (key.name === "down" || key.name === "right") value = false;
6248
6274
  else if (key.name === "y") value = true;
@@ -6252,14 +6278,18 @@ async function confirm(io, message, options = {}) {
6252
6278
  return value;
6253
6279
  }
6254
6280
  }
6281
+ } catch (error) {
6282
+ frame.erase();
6283
+ throw error;
6255
6284
  } finally {
6256
6285
  reader.close();
6257
6286
  }
6258
6287
  }
6259
6288
  async function text(io, message, options = {}) {
6289
+ const allowBack = options.allowBack === true;
6260
6290
  const fallback = options.defaultValue ?? "";
6261
6291
  if (!isInteractive(io)) {
6262
- io.output.write(`${renderText(message, fallback)}
6292
+ io.output.write(`${renderText(message, fallback, void 0, allowBack)}
6263
6293
  `);
6264
6294
  return fallback;
6265
6295
  }
@@ -6269,9 +6299,10 @@ async function text(io, message, options = {}) {
6269
6299
  let error;
6270
6300
  try {
6271
6301
  for (; ; ) {
6272
- frame.draw(renderText(message, value, error));
6302
+ frame.draw(renderText(message, value, error, allowBack));
6273
6303
  const key = await reader.next();
6274
6304
  if (isCancel(key)) throw new PromptCancelled();
6305
+ if (allowBack && key.name === "left") throw new PromptBack();
6275
6306
  error = void 0;
6276
6307
  if (key.name === "return" || key.name === "enter") {
6277
6308
  const problem = options.validate ? options.validate(value) : null;
@@ -6292,6 +6323,9 @@ async function text(io, message, options = {}) {
6292
6323
  }
6293
6324
  if (_isPrintable(key)) value += _keyText(key);
6294
6325
  }
6326
+ } catch (error_) {
6327
+ frame.erase();
6328
+ throw error_;
6295
6329
  } finally {
6296
6330
  reader.close();
6297
6331
  }
@@ -6548,98 +6582,108 @@ async function runInit(options) {
6548
6582
  { value: "walk", label: "Go through each setting", hint: "every question starts on its default" },
6549
6583
  { value: "choose", label: "Pick a different preset", hint: "everything else stays at the defaults" }
6550
6584
  ];
6551
- const pace = await select(io, "How should I set this up?", paceChoices, {
6552
- defaultValue: fallback ? "choose" : "recommended"
6553
- });
6554
- if (pace === "choose") {
6555
- answers.preset = await select(
6556
- io,
6557
- "What kind of project is this?",
6558
- listPresetNames().map(
6559
- (name) => resolvePresetChoice(fallback ? null : detected.presetName, name)
6560
- ),
6561
- { defaultValue: answers.preset }
6562
- );
6563
- const chosen = detectPreset(options.cwd, answers.preset);
6564
- answers.preset = chosen.presetName;
6565
- reason = null;
6566
- fallback = false;
6567
- }
6568
- if (pace === "walk") {
6569
- const scope = await select(
6570
- io,
6571
- "What should go into the snapshot?",
6572
- [
6573
- { value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
6574
- { value: "folders", label: "Only specific folders\u2026" },
6575
- { value: "no-tests", label: "Everything, but skip tests" },
6576
- { value: "custom", label: "Let me write glob patterns" }
6577
- ],
6578
- { defaultValue: "all" }
6579
- );
6580
- if (scope === "folders") {
6581
- const dirs = topLevelDirs(options.cwd).slice(0, 40);
6582
- if (dirs.length === 0) {
6583
- notes.push("No top-level directories found; the snapshot will include everything.");
6584
- } else {
6585
- const chosen = await multiSelect(
6586
- io,
6587
- "Select folders",
6588
- dirs.map((name) => ({ value: name, label: `${name}/` })),
6589
- { defaults: dirs }
6590
- );
6591
- answers.include = chosen.map((name) => `${name}/**`);
6585
+ const baseInclude = [...answers.include];
6586
+ const baseExclude = [...answers.excludePatterns];
6587
+ let lastScope = "all";
6588
+ const artifactChoices = [
6589
+ { value: "readme", label: "README.md", hint: "orientation page" },
6590
+ { value: "manifest", label: "manifest.json", hint: "required by validate and report" },
6591
+ { value: "parts", label: "parts/", hint: "the code payload" },
6592
+ { value: "navigation", label: "reports/navigation.md", hint: "file \u2192 part index" },
6593
+ { value: "projectTree", label: "reports/project-tree.md", hint: "ASCII tree" },
6594
+ { value: "audit", label: "reports/audit.md", hint: "security and health findings" },
6595
+ { value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
6596
+ ];
6597
+ const scopeStep = async () => {
6598
+ for (; ; ) {
6599
+ const scope = await select(
6600
+ io,
6601
+ "What should go into the snapshot?",
6602
+ [
6603
+ { value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
6604
+ { value: "folders", label: "Only specific folders\u2026" },
6605
+ { value: "no-tests", label: "Everything, but skip tests" },
6606
+ { value: "custom", label: "Let me write glob patterns" }
6607
+ ],
6608
+ { defaultValue: lastScope, allowBack: true }
6609
+ );
6610
+ lastScope = scope;
6611
+ answers.include = [...baseInclude];
6612
+ answers.excludePatterns = [...baseExclude];
6613
+ try {
6614
+ if (scope === "folders") {
6615
+ const dirs = topLevelDirs(options.cwd).slice(0, 40);
6616
+ if (dirs.length === 0) {
6617
+ notes.push("No top-level directories found; the snapshot will include everything.");
6618
+ } else {
6619
+ const chosen = await multiSelect(
6620
+ io,
6621
+ "Select folders",
6622
+ dirs.map((name) => ({ value: name, label: `${name}/` })),
6623
+ { defaults: dirs, allowBack: true }
6624
+ );
6625
+ answers.include = chosen.map((name) => `${name}/**`);
6626
+ }
6627
+ } else if (scope === "no-tests") {
6628
+ answers.excludePatterns = [...baseExclude, "**/*.test.*", "**/*.spec.*", "tests", "test"];
6629
+ } else if (scope === "custom") {
6630
+ const raw = await text(io, "Glob patterns to include (comma separated)", {
6631
+ defaultValue: baseInclude.join(", "),
6632
+ allowBack: true
6633
+ });
6634
+ answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
6635
+ }
6636
+ return;
6637
+ } catch (error) {
6638
+ if (error instanceof PromptBack) continue;
6639
+ throw error;
6592
6640
  }
6593
- } else if (scope === "no-tests") {
6594
- answers.excludePatterns = [
6595
- ...answers.excludePatterns,
6596
- "**/*.test.*",
6597
- "**/*.spec.*",
6598
- "tests",
6599
- "test"
6600
- ];
6601
- } else if (scope === "custom") {
6602
- const raw = await text(io, "Glob patterns to include (comma separated)", {
6603
- defaultValue: ""
6604
- });
6605
- answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
6606
6641
  }
6607
- const currentBudget = String(answers.maxPartBytes);
6608
- const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
6609
- defaultValue: SIZE_CHOICES.some((choice) => choice.value === currentBudget) ? currentBudget : "120k"
6610
- });
6611
- if (sizeChoice === "custom") {
6612
- const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
6613
- defaultValue: String(answers.maxPartBytes),
6614
- validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null
6642
+ };
6643
+ const sizeStep = async () => {
6644
+ for (; ; ) {
6645
+ const currentBudget = String(answers.maxPartBytes);
6646
+ const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
6647
+ defaultValue: SIZE_CHOICES.some((choice) => choice.value === currentBudget) ? currentBudget : "custom",
6648
+ allowBack: true
6615
6649
  });
6616
- answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
6617
- } else {
6618
- answers.maxPartBytes = sizeChoice;
6650
+ if (sizeChoice !== "custom") {
6651
+ answers.maxPartBytes = sizeChoice;
6652
+ return;
6653
+ }
6654
+ try {
6655
+ const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
6656
+ defaultValue: currentBudget,
6657
+ validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null,
6658
+ allowBack: true
6659
+ });
6660
+ answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
6661
+ return;
6662
+ } catch (error) {
6663
+ if (error instanceof PromptBack) continue;
6664
+ throw error;
6665
+ }
6619
6666
  }
6620
- const artifactChoices = [
6621
- { value: "readme", label: "README.md", hint: "orientation page" },
6622
- { value: "manifest", label: "manifest.json", hint: "required by validate and report" },
6623
- { value: "parts", label: "parts/", hint: "the code payload" },
6624
- { value: "navigation", label: "reports/navigation.md", hint: "file \u2192 part index" },
6625
- { value: "projectTree", label: "reports/project-tree.md", hint: "ASCII tree" },
6626
- { value: "audit", label: "reports/audit.md", hint: "security and health findings" },
6627
- { value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
6628
- ];
6629
- const presetEnabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
6667
+ };
6668
+ const artifactsStep = async () => {
6669
+ const enabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
6630
6670
  const selectedArtifacts = await multiSelect(
6631
6671
  io,
6632
6672
  "Which artifacts should be generated?",
6633
6673
  artifactChoices,
6634
- { defaults: presetEnabled }
6674
+ { defaults: enabled, allowBack: true }
6635
6675
  );
6636
6676
  for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
6637
- const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
6677
+ };
6678
+ const credentialsStep = async () => {
6638
6679
  answers.includeSensitiveFiles = await confirm(
6639
6680
  io,
6640
- "Include credential files (.env, *.pem, .npmrc, .ssh/)?",
6641
- { defaultValue: false }
6681
+ "Include credential files (.env, *.pem, *.npmrc, *.ssh/)?",
6682
+ { defaultValue: answers.includeSensitiveFiles, allowBack: true }
6642
6683
  );
6684
+ };
6685
+ const reviewStep = async () => {
6686
+ const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
6643
6687
  io.output.write("\n Review\n\n");
6644
6688
  io.output.write(` project ${answers.preset}
6645
6689
  `);
@@ -6654,8 +6698,53 @@ async function runInit(options) {
6654
6698
  `);
6655
6699
  io.output.write(" Will write snapshot.config with your answers, plus every other setting\n");
6656
6700
  io.output.write(" at its default \u2014 so nothing is hidden behind the docs.\n\n");
6657
- const proceed = await confirm(io, "Write this and continue?", { defaultValue: true });
6658
- if (!proceed) throw new PromptCancelled();
6701
+ const proceed = await confirm(io, "Write this and continue?", {
6702
+ defaultValue: true,
6703
+ allowBack: true
6704
+ });
6705
+ if (!proceed) throw new PromptBack();
6706
+ };
6707
+ const walk = async () => {
6708
+ const steps = [scopeStep, sizeStep, artifactsStep, credentialsStep, reviewStep];
6709
+ let index = 0;
6710
+ while (index < steps.length) {
6711
+ try {
6712
+ await steps[index]();
6713
+ index += 1;
6714
+ } catch (error) {
6715
+ if (!(error instanceof PromptBack)) throw error;
6716
+ if (index === 0) return false;
6717
+ index -= 1;
6718
+ io.output.write("\n");
6719
+ }
6720
+ }
6721
+ return true;
6722
+ };
6723
+ for (; ; ) {
6724
+ const pace = await select(io, "How should I set this up?", paceChoices, {
6725
+ defaultValue: fallback ? "choose" : "recommended"
6726
+ });
6727
+ if (pace === "choose") {
6728
+ try {
6729
+ answers.preset = await select(
6730
+ io,
6731
+ "What kind of project is this?",
6732
+ listPresetNames().map(
6733
+ (name) => resolvePresetChoice(fallback ? null : detected.presetName, name)
6734
+ ),
6735
+ { defaultValue: answers.preset, allowBack: true }
6736
+ );
6737
+ } catch (error) {
6738
+ if (error instanceof PromptBack) continue;
6739
+ throw error;
6740
+ }
6741
+ const chosen = detectPreset(options.cwd, answers.preset);
6742
+ answers.preset = chosen.presetName;
6743
+ reason = null;
6744
+ fallback = false;
6745
+ }
6746
+ if (pace === "walk" && !await walk()) continue;
6747
+ break;
6659
6748
  }
6660
6749
  }
6661
6750
  const configText = renderConfig(answers);
package/dist/index.d.cts CHANGED
@@ -422,7 +422,7 @@ declare function validateSnapshot(configInput: ResolvedSnapshotConfig | Snapshot
422
422
  * `package.json` carries the same version; `npm run verify` compares the two.
423
423
  */
424
424
  declare const GENERATOR_NAME = "codebase-snapshot";
425
- declare const GENERATOR_VERSION = "0.9.4";
425
+ declare const GENERATOR_VERSION = "0.9.5";
426
426
 
427
427
  declare const DEFAULT_SECRET_PATTERNS: SecretRule[];
428
428
 
package/dist/index.d.ts CHANGED
@@ -422,7 +422,7 @@ declare function validateSnapshot(configInput: ResolvedSnapshotConfig | Snapshot
422
422
  * `package.json` carries the same version; `npm run verify` compares the two.
423
423
  */
424
424
  declare const GENERATOR_NAME = "codebase-snapshot";
425
- declare const GENERATOR_VERSION = "0.9.4";
425
+ declare const GENERATOR_VERSION = "0.9.5";
426
426
 
427
427
  declare const DEFAULT_SECRET_PATTERNS: SecretRule[];
428
428
 
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { ARTIFACT_NAMES, DEFAULT_ANSWERS, DEFAULT_SECRET_PATTERNS, GENERATOR_NAME, GENERATOR_VERSION, LANGUAGE_PROFILES, MIN_MAX_PART_BYTES, PRESETS, PromptCancelled, SCHEMA_VERSION, anchorForPath, assertReplaceable, assertUsableConfigInput, collectFiles, compareManifests, countInvalidUtf8Bytes, countLines, decodeText, defineConfig, detectLanguage, detectPreset, explainPath, fenceForContent, fenceForText, firstRunMessage, generateProjectTree, generateSnapshot, generateSnapshotAsync, getPreset, globToRegExp, hasPriorSnapshot, isBinaryBuffer, isBinaryExtension, isBinaryFile, 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, validateSnapshot } from './chunk-FCWD4LY7.js';
1
+ export { ARTIFACT_NAMES, DEFAULT_ANSWERS, DEFAULT_SECRET_PATTERNS, GENERATOR_NAME, GENERATOR_VERSION, LANGUAGE_PROFILES, MIN_MAX_PART_BYTES, PRESETS, PromptCancelled, SCHEMA_VERSION, anchorForPath, assertReplaceable, assertUsableConfigInput, collectFiles, compareManifests, countInvalidUtf8Bytes, countLines, decodeText, defineConfig, detectLanguage, detectPreset, explainPath, fenceForContent, fenceForText, firstRunMessage, generateProjectTree, generateSnapshot, generateSnapshotAsync, getPreset, globToRegExp, hasPriorSnapshot, isBinaryBuffer, isBinaryExtension, isBinaryFile, 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, validateSnapshot } from './chunk-B336MMNC.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/dist/mcp.cjs CHANGED
@@ -6115,7 +6115,7 @@ function validateSnapshot(configInput) {
6115
6115
 
6116
6116
  // src/version.ts
6117
6117
  var GENERATOR_NAME = "codebase-snapshot";
6118
- var GENERATOR_VERSION = "0.9.4";
6118
+ var GENERATOR_VERSION = "0.9.5";
6119
6119
  var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
6120
6120
  function isOurGeneratorName(name) {
6121
6121
  if (typeof name !== "string") return false;
package/dist/mcp.js CHANGED
@@ -1,4 +1,4 @@
1
- export { ERROR_CODES, LEGACY_PROTOCOL_VERSIONS, MAX_LINE_BYTES, METHOD, MODERN_PROTOCOL_VERSIONS, SERVER_INSTRUCTIONS, SUPPORTED_PROTOCOL_VERSIONS, buildPrompts, buildResourceProvider, buildTools, classifyRequest, createLineReader, createMcpServer, createServer, createSession, createWriter, inspectModernMeta, negotiateLegacyVersion, runStdioServer, validateArguments } from './chunk-EWZYK3VX.js';
2
- import './chunk-FCWD4LY7.js';
1
+ export { ERROR_CODES, LEGACY_PROTOCOL_VERSIONS, MAX_LINE_BYTES, METHOD, MODERN_PROTOCOL_VERSIONS, SERVER_INSTRUCTIONS, SUPPORTED_PROTOCOL_VERSIONS, buildPrompts, buildResourceProvider, buildTools, classifyRequest, createLineReader, createMcpServer, createServer, createSession, createWriter, inspectModernMeta, negotiateLegacyVersion, runStdioServer, validateArguments } from './chunk-4AHHSNGG.js';
2
+ import './chunk-B336MMNC.js';
3
3
  //# sourceMappingURL=mcp.js.map
4
4
  //# sourceMappingURL=mcp.js.map
package/docs/cli.md CHANGED
@@ -174,7 +174,7 @@ The whole first run in one command, with defaults and no prompts. This is the sa
174
174
  🚀 Setup for: /path/to/repo
175
175
  ✅ Created snapshot.config.ts (preset: nextjs)
176
176
  Added npm scripts: snapshot, snapshot:validate
177
- Added devDependency: codebase-snapshot ^0.9.4
177
+ Added devDependency: codebase-snapshot ^0.9.5
178
178
  Added to .gitignore: snapshot/, snapshot.tmp/
179
179
  ⏳ Installing dependencies…
180
180
  ✅ Installed dependencies (npm install, 2.1s)
@@ -211,7 +211,7 @@ It also has three side effects on files you own, and it says so when it does the
211
211
  ```
212
212
  ✅ Created snapshot.config.ts (preset: node-lib)
213
213
  Added npm scripts: snapshot, snapshot:validate
214
- Added devDependency: codebase-snapshot ^0.9.4
214
+ Added devDependency: codebase-snapshot ^0.9.5
215
215
  Added to .gitignore: snapshot/, snapshot.tmp/
216
216
  ```
217
217
 
@@ -225,6 +225,16 @@ It also has three side effects on files you own, and it says so when it does the
225
225
  - `snapshot/` and `snapshot.tmp/` are appended to `.gitignore` if they are not already listed;
226
226
  the file is created if missing, and entries already present are not duplicated.
227
227
 
228
+ ### Going back
229
+
230
+ At a terminal, `init` asks five questions and every one of them can be revisited: **`←`** goes back
231
+ on a list or a text field, **`b`** on a list or a confirmation. `text` takes `←` alone because `b` is
232
+ a character you may be typing, and `confirm` takes `b` alone because `←`/`→` already mean yes/no.
233
+
234
+ Back steps to the previous question with your earlier answer still selected; back from the first one
235
+ returns to *"How should I set this up?"*. Answering **No** at the review steps back rather than
236
+ abandoning the run — Ctrl+C is how you abandon.
237
+
228
238
  Nothing else in the repository is touched: no `AGENTS.md`, no `.claude/`, no `.cursor/`. In a
229
239
  repository with a `package.json` and no config, `git status` shows the config as the only new
230
240
  file.
package/docs/mcp.md CHANGED
@@ -10,7 +10,7 @@ walk — that the client sees.
10
10
  ## Start it
11
11
 
12
12
  ```bash
13
- npx -y codebase-snapshot@0.9.4 mcp
13
+ npx -y codebase-snapshot@0.9.5 mcp
14
14
  ```
15
15
 
16
16
  The server serves the directory it was started in. `--cwd <path>` points it elsewhere, and that
@@ -25,7 +25,7 @@ Claude Desktop and Cursor:
25
25
  "mcpServers": {
26
26
  "codebase-snapshot": {
27
27
  "command": "npx",
28
- "args": ["-y", "codebase-snapshot@0.9.4", "mcp", "--cwd", "/absolute/path/to/repo"]
28
+ "args": ["-y", "codebase-snapshot@0.9.5", "mcp", "--cwd", "/absolute/path/to/repo"]
29
29
  }
30
30
  }
31
31
  }
@@ -34,7 +34,7 @@ Claude Desktop and Cursor:
34
34
  Claude Code registers the same thing from a shell:
35
35
 
36
36
  ```bash
37
- claude mcp add codebase-snapshot -- npx -y codebase-snapshot@0.9.4 mcp --cwd /absolute/path/to/repo
37
+ claude mcp add codebase-snapshot -- npx -y codebase-snapshot@0.9.5 mcp --cwd /absolute/path/to/repo
38
38
  ```
39
39
 
40
40
  On Windows, wrap the command as `cmd /c npx` with the same arguments — `npx` is a `.cmd` shim
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebase-snapshot",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "description": "Pack a codebase for an LLM into budgeted Markdown parts, with a manifest of every file and skip, and validate to prove the snapshot is current.",
5
5
  "keywords": [
6
6
  "codebase",