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/CHANGELOG.md CHANGED
@@ -21,6 +21,44 @@ Two versions exist on the registry that are not releases, and both are deprecate
21
21
  Deleting them requires account-level rights that the release pipeline does not have, so they are
22
22
  deprecated with an explanatory message instead. `latest` has pointed at 0.9.0 since it existed.
23
23
 
24
+ ## 0.9.5
25
+
26
+ ### Every question in the wizard is reversible
27
+
28
+ The first run asked five questions and accepted one answer each. Reaching the part-size question and
29
+ changing your mind meant killing the process and starting again — and at the review screen,
30
+ answering **No** to "Write this and continue?" threw the same cancellation Ctrl+C does, so "that is
31
+ not what I meant" ended the run instead of correcting it.
32
+
33
+ - **`←` goes back** on any list or text field, `b` on any list or confirmation. Each footer names
34
+ the keys that question actually accepts: `text` takes `←` alone (`b` there is a character you are
35
+ typing) and `confirm` takes `b` alone (`←`/`→` already mean yes/no).
36
+ - **Back is a step, not a restart.** The five questions are now steps — scope → part size →
37
+ artifacts → credentials → review. Going back re-asks the previous one with your earlier answer
38
+ still selected, and back from the first step returns to *"How should I set this up?"* rather than
39
+ out of the wizard.
40
+ - **`No` at the review steps back.** It used to abort. Ctrl+C is still how you say stop.
41
+ - **Answers are rebuilt, not mutated.** Re-entering the scope step clears the globs it added, so
42
+ choosing "skip tests", going back, and choosing "everything" no longer leaves `**/*.test.*` in
43
+ the written config.
44
+
45
+ Four new tests drive real keypress events through a fake TTY, alongside the four that already
46
+ covered the forward path.
47
+
48
+ ### A clean first run
49
+
50
+ Setup printed a Node `DEP0190` deprecation warning after installing dependencies: `spawnSync` was
51
+ given an args array *and* `shell: true`, which Node deprecated because the arguments are
52
+ concatenated rather than escaped. The Windows path now hands the shell one pre-formed command
53
+ string — both halves are fixed literals from `detectPackageManager` — and every other platform
54
+ spawns with no shell at all.
55
+
56
+ ### Still untested here
57
+
58
+ The MCP server has not been run against a real client. Its protocol is exercised against the built
59
+ binary, but a client's own configuration, timeouts and approval prompts are theirs. That is the next
60
+ thing to check, not something this release proves.
61
+
24
62
  ## 0.9.4
25
63
 
26
64
  ### The snapshot is now something an agent can ask for
package/README.md CHANGED
@@ -197,7 +197,7 @@ Everything an agent needs is available as data, and the tool can install its own
197
197
  ```bash
198
198
  npx codebase-snapshot describe --format json # contract: workflow, options, exit codes
199
199
  npx codebase-snapshot skill --install # into .claude/skills + .cursor/rules
200
- npx -y codebase-snapshot@0.9.4 mcp # or serve it over MCP
200
+ npx -y codebase-snapshot@0.9.5 mcp # or serve it over MCP
201
201
  ```
202
202
 
203
203
  An agent told only "use `npx codebase-snapshot`" can read the contract, follow its ordered
package/dist/bin.js CHANGED
@@ -4346,7 +4346,7 @@ function gitignorePath(cwd, filePath) {
4346
4346
 
4347
4347
  // src/version.ts
4348
4348
  var GENERATOR_NAME = "codebase-snapshot";
4349
- var GENERATOR_VERSION = "0.9.4";
4349
+ var GENERATOR_VERSION = "0.9.5";
4350
4350
  var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
4351
4351
  function isOurGeneratorName(name) {
4352
4352
  if (typeof name !== "string") return false;
@@ -4487,6 +4487,12 @@ var PromptCancelled = class extends Error {
4487
4487
  this.name = "PromptCancelled";
4488
4488
  }
4489
4489
  };
4490
+ var PromptBack = class extends Error {
4491
+ constructor() {
4492
+ super("Prompt back");
4493
+ this.name = "PromptBack";
4494
+ }
4495
+ };
4490
4496
  function defaultIO() {
4491
4497
  return { input: process.stdin, output: process.stdout };
4492
4498
  }
@@ -4510,35 +4516,40 @@ function choiceLine(choice, active) {
4510
4516
  const hint = choice.hint ? ` ${choice.hint}` : "";
4511
4517
  return ` ${marker(active)} ${choice.label}${hint}`;
4512
4518
  }
4513
- function renderSelect(message, choices, index) {
4519
+ function listFooter(base, allowBack) {
4520
+ return allowBack ? `${base} \xB7 \u2190 back` : base;
4521
+ }
4522
+ function renderSelect(message, choices, index, allowBack = false) {
4514
4523
  const lines = [`\u25C6 ${message}`, ""];
4515
4524
  choices.forEach((choice, position) => lines.push(choiceLine(choice, position === index)));
4516
- lines.push("", " \u2191\u2193 move \xB7 enter select");
4525
+ lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 enter select", allowBack)}`);
4517
4526
  return lines.join("\n");
4518
4527
  }
4519
- function renderMultiSelect(message, choices, index, selected) {
4528
+ function renderMultiSelect(message, choices, index, selected, allowBack = false) {
4520
4529
  const lines = [`\u25C6 ${message}`, ""];
4521
4530
  choices.forEach((choice, position) => {
4522
4531
  const box = selected.has(position) ? "\u25FC" : "\u25FB";
4523
4532
  const hint = choice.hint ? ` ${choice.hint}` : "";
4524
4533
  lines.push(` ${marker(position === index)} ${box} ${choice.label}${hint}`);
4525
4534
  });
4526
- lines.push("", " \u2191\u2193 move \xB7 space toggle \xB7 enter confirm");
4535
+ lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 space toggle \xB7 enter confirm", allowBack)}`);
4527
4536
  return lines.join("\n");
4528
4537
  }
4529
- function renderConfirm(message, value) {
4538
+ function renderConfirm(message, value, allowBack = false) {
4539
+ const footer = allowBack ? "\u2190\u2192 or y/n \xB7 enter confirm \xB7 b back" : "\u2190\u2192 or y/n \xB7 enter confirm";
4530
4540
  return [
4531
4541
  `\u25C6 ${message}`,
4532
4542
  "",
4533
4543
  ` ${marker(value)} Yes`,
4534
4544
  ` ${marker(!value)} No`,
4535
4545
  "",
4536
- " \u2190\u2192 or y/n \xB7 enter confirm"
4546
+ ` ${footer}`
4537
4547
  ].join("\n");
4538
4548
  }
4539
- function renderText(message, value, error) {
4549
+ function renderText(message, value, error, allowBack = false) {
4540
4550
  const lines = [`\u25C6 ${message}`, "", ` ${value || "\u258F"}`];
4541
4551
  if (error) lines.push("", ` \u2716 ${error}`);
4552
+ if (allowBack) lines.push("", " enter confirm \xB7 \u2190 back");
4542
4553
  return lines.join("\n");
4543
4554
  }
4544
4555
  function keyReader(input) {
@@ -4587,14 +4598,18 @@ var Frame = class {
4587
4598
  function isCancel(key) {
4588
4599
  return key.ctrl === true && key.name === "c" || key.ctrl === true && key.name === "d";
4589
4600
  }
4601
+ function isListBack(key, allowBack) {
4602
+ return allowBack && (key.name === "left" || key.name === "b");
4603
+ }
4590
4604
  async function select(io, message, choices, options = {}) {
4591
4605
  if (choices.length === 0) throw new Error("select() requires at least one choice.");
4606
+ const allowBack = options.allowBack === true;
4592
4607
  const fallbackIndex = Math.max(
4593
4608
  0,
4594
4609
  choices.findIndex((choice) => choice.value === options.defaultValue)
4595
4610
  );
4596
4611
  if (!isInteractive(io)) {
4597
- io.output.write(`${renderSelect(message, choices, fallbackIndex)}
4612
+ io.output.write(`${renderSelect(message, choices, fallbackIndex, allowBack)}
4598
4613
  `);
4599
4614
  return choices[fallbackIndex].value;
4600
4615
  }
@@ -4603,9 +4618,10 @@ async function select(io, message, choices, options = {}) {
4603
4618
  let index = fallbackIndex;
4604
4619
  try {
4605
4620
  for (; ; ) {
4606
- frame.draw(renderSelect(message, choices, index));
4621
+ frame.draw(renderSelect(message, choices, index, allowBack));
4607
4622
  const key = await reader.next();
4608
4623
  if (isCancel(key)) throw new PromptCancelled();
4624
+ if (isListBack(key, allowBack)) throw new PromptBack();
4609
4625
  if (key.name === "up") index = moveSelection(index, -1, choices.length);
4610
4626
  else if (key.name === "down") index = moveSelection(index, 1, choices.length);
4611
4627
  else if (key.name === "return" || key.name === "enter") {
@@ -4614,18 +4630,22 @@ async function select(io, message, choices, options = {}) {
4614
4630
  } else if (key.name === "j") index = moveSelection(index, 1, choices.length);
4615
4631
  else if (key.name === "k") index = moveSelection(index, -1, choices.length);
4616
4632
  }
4633
+ } catch (error) {
4634
+ frame.erase();
4635
+ throw error;
4617
4636
  } finally {
4618
4637
  reader.close();
4619
4638
  }
4620
4639
  }
4621
4640
  async function multiSelect(io, message, choices, options = {}) {
4641
+ const allowBack = options.allowBack === true;
4622
4642
  const selected = /* @__PURE__ */ new Set();
4623
4643
  choices.forEach((choice, position) => {
4624
4644
  if (options.defaults?.includes(choice.value)) selected.add(position);
4625
4645
  });
4626
4646
  const result = () => choices.filter((_, position) => selected.has(position)).map((choice) => choice.value);
4627
4647
  if (!isInteractive(io)) {
4628
- io.output.write(`${renderMultiSelect(message, choices, 0, selected)}
4648
+ io.output.write(`${renderMultiSelect(message, choices, 0, selected, allowBack)}
4629
4649
  `);
4630
4650
  return result();
4631
4651
  }
@@ -4634,9 +4654,10 @@ async function multiSelect(io, message, choices, options = {}) {
4634
4654
  let index = 0;
4635
4655
  try {
4636
4656
  for (; ; ) {
4637
- frame.draw(renderMultiSelect(message, choices, index, selected));
4657
+ frame.draw(renderMultiSelect(message, choices, index, selected, allowBack));
4638
4658
  const key = await reader.next();
4639
4659
  if (isCancel(key)) throw new PromptCancelled();
4660
+ if (isListBack(key, allowBack)) throw new PromptBack();
4640
4661
  if (key.name === "up") index = moveSelection(index, -1, choices.length);
4641
4662
  else if (key.name === "down") index = moveSelection(index, 1, choices.length);
4642
4663
  else if (key.name === "space") {
@@ -4651,14 +4672,18 @@ async function multiSelect(io, message, choices, options = {}) {
4651
4672
  return result();
4652
4673
  }
4653
4674
  }
4675
+ } catch (error) {
4676
+ frame.erase();
4677
+ throw error;
4654
4678
  } finally {
4655
4679
  reader.close();
4656
4680
  }
4657
4681
  }
4658
4682
  async function confirm(io, message, options = {}) {
4683
+ const allowBack = options.allowBack === true;
4659
4684
  let value = options.defaultValue ?? true;
4660
4685
  if (!isInteractive(io)) {
4661
- io.output.write(`${renderConfirm(message, value)}
4686
+ io.output.write(`${renderConfirm(message, value, allowBack)}
4662
4687
  `);
4663
4688
  return value;
4664
4689
  }
@@ -4666,9 +4691,10 @@ async function confirm(io, message, options = {}) {
4666
4691
  const reader = keyReader(io.input);
4667
4692
  try {
4668
4693
  for (; ; ) {
4669
- frame.draw(renderConfirm(message, value));
4694
+ frame.draw(renderConfirm(message, value, allowBack));
4670
4695
  const key = await reader.next();
4671
4696
  if (isCancel(key)) throw new PromptCancelled();
4697
+ if (allowBack && key.name === "b") throw new PromptBack();
4672
4698
  if (key.name === "up" || key.name === "left") value = true;
4673
4699
  else if (key.name === "down" || key.name === "right") value = false;
4674
4700
  else if (key.name === "y") value = true;
@@ -4678,14 +4704,18 @@ async function confirm(io, message, options = {}) {
4678
4704
  return value;
4679
4705
  }
4680
4706
  }
4707
+ } catch (error) {
4708
+ frame.erase();
4709
+ throw error;
4681
4710
  } finally {
4682
4711
  reader.close();
4683
4712
  }
4684
4713
  }
4685
4714
  async function text(io, message, options = {}) {
4715
+ const allowBack = options.allowBack === true;
4686
4716
  const fallback = options.defaultValue ?? "";
4687
4717
  if (!isInteractive(io)) {
4688
- io.output.write(`${renderText(message, fallback)}
4718
+ io.output.write(`${renderText(message, fallback, void 0, allowBack)}
4689
4719
  `);
4690
4720
  return fallback;
4691
4721
  }
@@ -4695,9 +4725,10 @@ async function text(io, message, options = {}) {
4695
4725
  let error;
4696
4726
  try {
4697
4727
  for (; ; ) {
4698
- frame.draw(renderText(message, value, error));
4728
+ frame.draw(renderText(message, value, error, allowBack));
4699
4729
  const key = await reader.next();
4700
4730
  if (isCancel(key)) throw new PromptCancelled();
4731
+ if (allowBack && key.name === "left") throw new PromptBack();
4701
4732
  error = void 0;
4702
4733
  if (key.name === "return" || key.name === "enter") {
4703
4734
  const problem = options.validate ? options.validate(value) : null;
@@ -4718,6 +4749,9 @@ async function text(io, message, options = {}) {
4718
4749
  }
4719
4750
  if (_isPrintable(key)) value += _keyText(key);
4720
4751
  }
4752
+ } catch (error_) {
4753
+ frame.erase();
4754
+ throw error_;
4721
4755
  } finally {
4722
4756
  reader.close();
4723
4757
  }
@@ -4974,98 +5008,108 @@ async function runInit(options) {
4974
5008
  { value: "walk", label: "Go through each setting", hint: "every question starts on its default" },
4975
5009
  { value: "choose", label: "Pick a different preset", hint: "everything else stays at the defaults" }
4976
5010
  ];
4977
- const pace = await select(io, "How should I set this up?", paceChoices, {
4978
- defaultValue: fallback ? "choose" : "recommended"
4979
- });
4980
- if (pace === "choose") {
4981
- answers.preset = await select(
4982
- io,
4983
- "What kind of project is this?",
4984
- listPresetNames().map(
4985
- (name) => resolvePresetChoice(fallback ? null : detected.presetName, name)
4986
- ),
4987
- { defaultValue: answers.preset }
4988
- );
4989
- const chosen = detectPreset(options.cwd, answers.preset);
4990
- answers.preset = chosen.presetName;
4991
- reason = null;
4992
- fallback = false;
4993
- }
4994
- if (pace === "walk") {
4995
- const scope = await select(
4996
- io,
4997
- "What should go into the snapshot?",
4998
- [
4999
- { value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
5000
- { value: "folders", label: "Only specific folders\u2026" },
5001
- { value: "no-tests", label: "Everything, but skip tests" },
5002
- { value: "custom", label: "Let me write glob patterns" }
5003
- ],
5004
- { defaultValue: "all" }
5005
- );
5006
- if (scope === "folders") {
5007
- const dirs = topLevelDirs(options.cwd).slice(0, 40);
5008
- if (dirs.length === 0) {
5009
- notes.push("No top-level directories found; the snapshot will include everything.");
5010
- } else {
5011
- const chosen = await multiSelect(
5012
- io,
5013
- "Select folders",
5014
- dirs.map((name) => ({ value: name, label: `${name}/` })),
5015
- { defaults: dirs }
5016
- );
5017
- answers.include = chosen.map((name) => `${name}/**`);
5011
+ const baseInclude = [...answers.include];
5012
+ const baseExclude = [...answers.excludePatterns];
5013
+ let lastScope = "all";
5014
+ const artifactChoices = [
5015
+ { value: "readme", label: "README.md", hint: "orientation page" },
5016
+ { value: "manifest", label: "manifest.json", hint: "required by validate and report" },
5017
+ { value: "parts", label: "parts/", hint: "the code payload" },
5018
+ { value: "navigation", label: "reports/navigation.md", hint: "file \u2192 part index" },
5019
+ { value: "projectTree", label: "reports/project-tree.md", hint: "ASCII tree" },
5020
+ { value: "audit", label: "reports/audit.md", hint: "security and health findings" },
5021
+ { value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
5022
+ ];
5023
+ const scopeStep = async () => {
5024
+ for (; ; ) {
5025
+ const scope = await select(
5026
+ io,
5027
+ "What should go into the snapshot?",
5028
+ [
5029
+ { value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
5030
+ { value: "folders", label: "Only specific folders\u2026" },
5031
+ { value: "no-tests", label: "Everything, but skip tests" },
5032
+ { value: "custom", label: "Let me write glob patterns" }
5033
+ ],
5034
+ { defaultValue: lastScope, allowBack: true }
5035
+ );
5036
+ lastScope = scope;
5037
+ answers.include = [...baseInclude];
5038
+ answers.excludePatterns = [...baseExclude];
5039
+ try {
5040
+ if (scope === "folders") {
5041
+ const dirs = topLevelDirs(options.cwd).slice(0, 40);
5042
+ if (dirs.length === 0) {
5043
+ notes.push("No top-level directories found; the snapshot will include everything.");
5044
+ } else {
5045
+ const chosen = await multiSelect(
5046
+ io,
5047
+ "Select folders",
5048
+ dirs.map((name) => ({ value: name, label: `${name}/` })),
5049
+ { defaults: dirs, allowBack: true }
5050
+ );
5051
+ answers.include = chosen.map((name) => `${name}/**`);
5052
+ }
5053
+ } else if (scope === "no-tests") {
5054
+ answers.excludePatterns = [...baseExclude, "**/*.test.*", "**/*.spec.*", "tests", "test"];
5055
+ } else if (scope === "custom") {
5056
+ const raw = await text(io, "Glob patterns to include (comma separated)", {
5057
+ defaultValue: baseInclude.join(", "),
5058
+ allowBack: true
5059
+ });
5060
+ answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
5061
+ }
5062
+ return;
5063
+ } catch (error) {
5064
+ if (error instanceof PromptBack) continue;
5065
+ throw error;
5018
5066
  }
5019
- } else if (scope === "no-tests") {
5020
- answers.excludePatterns = [
5021
- ...answers.excludePatterns,
5022
- "**/*.test.*",
5023
- "**/*.spec.*",
5024
- "tests",
5025
- "test"
5026
- ];
5027
- } else if (scope === "custom") {
5028
- const raw = await text(io, "Glob patterns to include (comma separated)", {
5029
- defaultValue: ""
5030
- });
5031
- answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
5032
5067
  }
5033
- const currentBudget = String(answers.maxPartBytes);
5034
- const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
5035
- defaultValue: SIZE_CHOICES.some((choice) => choice.value === currentBudget) ? currentBudget : "120k"
5036
- });
5037
- if (sizeChoice === "custom") {
5038
- const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
5039
- defaultValue: String(answers.maxPartBytes),
5040
- validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null
5068
+ };
5069
+ const sizeStep = async () => {
5070
+ for (; ; ) {
5071
+ const currentBudget = String(answers.maxPartBytes);
5072
+ const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
5073
+ defaultValue: SIZE_CHOICES.some((choice) => choice.value === currentBudget) ? currentBudget : "custom",
5074
+ allowBack: true
5041
5075
  });
5042
- answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
5043
- } else {
5044
- answers.maxPartBytes = sizeChoice;
5045
- }
5046
- const artifactChoices = [
5047
- { value: "readme", label: "README.md", hint: "orientation page" },
5048
- { value: "manifest", label: "manifest.json", hint: "required by validate and report" },
5049
- { value: "parts", label: "parts/", hint: "the code payload" },
5050
- { value: "navigation", label: "reports/navigation.md", hint: "file \u2192 part index" },
5051
- { value: "projectTree", label: "reports/project-tree.md", hint: "ASCII tree" },
5052
- { value: "audit", label: "reports/audit.md", hint: "security and health findings" },
5053
- { value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
5054
- ];
5055
- const presetEnabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
5076
+ if (sizeChoice !== "custom") {
5077
+ answers.maxPartBytes = sizeChoice;
5078
+ return;
5079
+ }
5080
+ try {
5081
+ const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
5082
+ defaultValue: currentBudget,
5083
+ validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null,
5084
+ allowBack: true
5085
+ });
5086
+ answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
5087
+ return;
5088
+ } catch (error) {
5089
+ if (error instanceof PromptBack) continue;
5090
+ throw error;
5091
+ }
5092
+ }
5093
+ };
5094
+ const artifactsStep = async () => {
5095
+ const enabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
5056
5096
  const selectedArtifacts = await multiSelect(
5057
5097
  io,
5058
5098
  "Which artifacts should be generated?",
5059
5099
  artifactChoices,
5060
- { defaults: presetEnabled }
5100
+ { defaults: enabled, allowBack: true }
5061
5101
  );
5062
5102
  for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
5063
- const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
5103
+ };
5104
+ const credentialsStep = async () => {
5064
5105
  answers.includeSensitiveFiles = await confirm(
5065
5106
  io,
5066
- "Include credential files (.env, *.pem, .npmrc, .ssh/)?",
5067
- { defaultValue: false }
5107
+ "Include credential files (.env, *.pem, *.npmrc, *.ssh/)?",
5108
+ { defaultValue: answers.includeSensitiveFiles, allowBack: true }
5068
5109
  );
5110
+ };
5111
+ const reviewStep = async () => {
5112
+ const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
5069
5113
  io.output.write("\n Review\n\n");
5070
5114
  io.output.write(` project ${answers.preset}
5071
5115
  `);
@@ -5080,8 +5124,53 @@ async function runInit(options) {
5080
5124
  `);
5081
5125
  io.output.write(" Will write snapshot.config with your answers, plus every other setting\n");
5082
5126
  io.output.write(" at its default \u2014 so nothing is hidden behind the docs.\n\n");
5083
- const proceed = await confirm(io, "Write this and continue?", { defaultValue: true });
5084
- if (!proceed) throw new PromptCancelled();
5127
+ const proceed = await confirm(io, "Write this and continue?", {
5128
+ defaultValue: true,
5129
+ allowBack: true
5130
+ });
5131
+ if (!proceed) throw new PromptBack();
5132
+ };
5133
+ const walk = async () => {
5134
+ const steps = [scopeStep, sizeStep, artifactsStep, credentialsStep, reviewStep];
5135
+ let index = 0;
5136
+ while (index < steps.length) {
5137
+ try {
5138
+ await steps[index]();
5139
+ index += 1;
5140
+ } catch (error) {
5141
+ if (!(error instanceof PromptBack)) throw error;
5142
+ if (index === 0) return false;
5143
+ index -= 1;
5144
+ io.output.write("\n");
5145
+ }
5146
+ }
5147
+ return true;
5148
+ };
5149
+ for (; ; ) {
5150
+ const pace = await select(io, "How should I set this up?", paceChoices, {
5151
+ defaultValue: fallback ? "choose" : "recommended"
5152
+ });
5153
+ if (pace === "choose") {
5154
+ try {
5155
+ answers.preset = await select(
5156
+ io,
5157
+ "What kind of project is this?",
5158
+ listPresetNames().map(
5159
+ (name) => resolvePresetChoice(fallback ? null : detected.presetName, name)
5160
+ ),
5161
+ { defaultValue: answers.preset, allowBack: true }
5162
+ );
5163
+ } catch (error) {
5164
+ if (error instanceof PromptBack) continue;
5165
+ throw error;
5166
+ }
5167
+ const chosen = detectPreset(options.cwd, answers.preset);
5168
+ answers.preset = chosen.presetName;
5169
+ reason = null;
5170
+ fallback = false;
5171
+ }
5172
+ if (pace === "walk" && !await walk()) continue;
5173
+ break;
5085
5174
  }
5086
5175
  }
5087
5176
  const configText = renderConfig(answers);
@@ -11206,14 +11295,13 @@ function installDependencies(cwd) {
11206
11295
  const manager = detectPackageManager(cwd);
11207
11296
  const command = `${manager.command} ${manager.args.join(" ")}`;
11208
11297
  const started = performance.now();
11209
- const result = spawnSync(manager.command, manager.args, {
11298
+ const spawnOptions = {
11210
11299
  cwd,
11211
11300
  encoding: "utf8",
11212
- // npm, pnpm and yarn ship as `.cmd` shims on Windows, which spawnSync cannot exec directly.
11213
- shell: process.platform === "win32",
11214
11301
  timeout: INSTALL_TIMEOUT_MS,
11215
11302
  env: cleanInstallEnv()
11216
- });
11303
+ };
11304
+ const result = process.platform === "win32" ? spawnSync(command, { ...spawnOptions, shell: true }) : spawnSync(manager.command, manager.args, spawnOptions);
11217
11305
  const seconds = `${((performance.now() - started) / 1e3).toFixed(1)}s`;
11218
11306
  if (result.error) {
11219
11307
  return { manager: manager.name, command, ok: false, detail: `${result.error.message} (${seconds})` };
@@ -1,4 +1,4 @@
1
- import { GENERATOR_VERSION, MIN_MAX_PART_BYTES, SCHEMA_VERSION, GENERATOR_NAME, ARTIFACT_NAMES, PRESETS, generateSnapshot, validateSnapshot, explainPath, renderExplainText, describeManifestShapeProblem, loadConfigFileDetailed, parseByteSize, resolveConfig } from './chunk-FCWD4LY7.js';
1
+ import { GENERATOR_VERSION, MIN_MAX_PART_BYTES, SCHEMA_VERSION, GENERATOR_NAME, ARTIFACT_NAMES, PRESETS, generateSnapshot, validateSnapshot, explainPath, renderExplainText, describeManifestShapeProblem, loadConfigFileDetailed, parseByteSize, resolveConfig } from './chunk-B336MMNC.js';
2
2
  import * as fs from 'fs';
3
3
  import * as path2 from 'path';
4
4
 
@@ -2573,5 +2573,5 @@ async function runStdioServer(options) {
2573
2573
  }
2574
2574
 
2575
2575
  export { COMMAND_OPTION_SCOPE, ERROR_CODES, LEGACY_PROTOCOL_VERSIONS, MAX_LINE_BYTES, METHOD, MODERN_PROTOCOL_VERSIONS, SERVER_INSTRUCTIONS, SKILL_NAME, SUPPORTED_PROTOCOL_VERSIONS, buildAgentContract, buildPrompts, buildResourceProvider, buildTools, classifyRequest, commandsForOption, createLineReader, createMcpServer, createServer, createSession, createWriter, inspectModernMeta, negotiateLegacyVersion, renderContractText, renderSkill, runStdioServer, validateArguments };
2576
- //# sourceMappingURL=chunk-EWZYK3VX.js.map
2577
- //# sourceMappingURL=chunk-EWZYK3VX.js.map
2576
+ //# sourceMappingURL=chunk-4AHHSNGG.js.map
2577
+ //# sourceMappingURL=chunk-4AHHSNGG.js.map