@contentful/experience-design-system-cli 2.32.1-dev-build-403b769.0 → 2.32.1-dev-build-6b922ec.0

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.
Files changed (35) hide show
  1. package/dist/package.json +2 -1
  2. package/dist/src/credentials-store.d.ts +0 -23
  3. package/dist/src/doctor/checks.d.ts +5 -0
  4. package/dist/src/doctor/command.d.ts +7 -0
  5. package/dist/src/doctor/report.d.ts +12 -0
  6. package/dist/src/index.js +1341 -848
  7. package/dist/src/lib/agent-definitions.d.ts +7 -0
  8. package/dist/src/setup/command.d.ts +4 -13
  9. package/dist/src/setup/lib/checks.d.ts +69 -0
  10. package/dist/src/setup/lib/layout.d.ts +34 -0
  11. package/dist/src/setup/lib/prompt-input.d.ts +17 -0
  12. package/dist/src/setup/lib/shell.d.ts +15 -0
  13. package/dist/src/setup/lib/types.d.ts +55 -0
  14. package/dist/src/setup/lib/yes-no-prompt.d.ts +8 -0
  15. package/dist/src/setup/steps/coding-agent.d.ts +2 -0
  16. package/dist/src/setup/steps/contentful.d.ts +2 -0
  17. package/dist/src/setup/steps/preferences/analytics.d.ts +5 -0
  18. package/dist/src/setup/steps/preferences/auto-filter.d.ts +4 -0
  19. package/dist/src/setup/steps/preferences/concurrency.d.ts +3 -0
  20. package/dist/src/setup/steps/preferences/custom-prompts.d.ts +8 -0
  21. package/dist/src/setup/steps/preferences/debug.d.ts +4 -0
  22. package/dist/src/setup/steps/preferences/index.d.ts +41 -0
  23. package/dist/src/setup/steps/preferences/no-color.d.ts +3 -0
  24. package/dist/src/setup/steps/prerequisites/build.d.ts +2 -0
  25. package/dist/src/setup/steps/prerequisites/index.d.ts +8 -0
  26. package/dist/src/setup/steps/prerequisites/node.d.ts +4 -0
  27. package/dist/src/setup/steps/prerequisites/pnpm.d.ts +2 -0
  28. package/dist/src/setup/tui/SetupScreen.d.ts +30 -0
  29. package/dist/src/setup/tui/SetupStepper.d.ts +7 -0
  30. package/dist/src/setup/tui/usePromptInput.d.ts +19 -0
  31. package/package.json +7 -6
  32. package/dist/src/setup/analytics-prompt.d.ts +0 -13
  33. package/dist/src/setup/auto-filter-prompt.d.ts +0 -13
  34. package/dist/src/setup/composition-mode-prompt.d.ts +0 -14
  35. package/dist/src/setup/debug-mode-prompt.d.ts +0 -11
package/dist/src/index.js CHANGED
@@ -297,10 +297,10 @@ function codexBedrockConfigArgs() {
297
297
  const region = process.env.AWS_REGION?.trim() || process.env.AWS_DEFAULT_REGION?.trim() || DEFAULT_CODEX_BEDROCK_REGION;
298
298
  return ["-c", "model_provider=amazon-bedrock", "-c", `model_providers.amazon-bedrock.region=${region}`];
299
299
  }
300
- function buildArgs(agent, prompt2, model, promptViaStdin = false, bedrock = false) {
300
+ function buildArgs(agent, prompt, model, promptViaStdin = false, bedrock = false) {
301
301
  const resolvedModel = resolveAgentModel(agent, model, bedrock);
302
302
  const modelArg = resolvedModel ? ["--model", resolvedModel] : [];
303
- const promptArg = promptViaStdin ? [] : [prompt2];
303
+ const promptArg = promptViaStdin ? [] : [prompt];
304
304
  switch (agent) {
305
305
  case "claude":
306
306
  return ["--print", ...modelArg, ...promptArg];
@@ -320,11 +320,11 @@ function buildArgs(agent, prompt2, model, promptViaStdin = false, bedrock = fals
320
320
  }
321
321
  }
322
322
  async function runAgent(options) {
323
- const { agent, prompt: prompt2, timeoutMs, model, onOutput, promptViaStdin, onDebugEvent } = options;
323
+ const { agent, prompt, timeoutMs, model, onOutput, promptViaStdin, onDebugEvent } = options;
324
324
  const bedrock = options.bedrock ?? process.env.EDS_BEDROCK === "1";
325
325
  const binary = resolveBinary(agent);
326
- const useStdin2 = !!promptViaStdin;
327
- const args = buildArgs(agent, prompt2, model, useStdin2, bedrock);
326
+ const useStdin3 = !!promptViaStdin;
327
+ const args = buildArgs(agent, prompt, model, useStdin3, bedrock);
328
328
  const startedAt = Date.now();
329
329
  onDebugEvent?.("run.start", {
330
330
  agent,
@@ -332,8 +332,8 @@ async function runAgent(options) {
332
332
  model,
333
333
  bedrock: !!bedrock,
334
334
  timeoutMs,
335
- promptLen: prompt2.length,
336
- promptHead: prompt2.slice(0, 500)
335
+ promptLen: prompt.length,
336
+ promptHead: prompt.slice(0, 500)
337
337
  });
338
338
  return new Promise((resolve27) => {
339
339
  const bedrockEnv = bedrock ? BEDROCK_ENV_BY_AGENT[agent] : void 0;
@@ -341,10 +341,10 @@ async function runAgent(options) {
341
341
  stdio: ["pipe", "pipe", "pipe"],
342
342
  ...bedrockEnv ? { env: { ...process.env, ...bedrockEnv } } : {}
343
343
  });
344
- if (useStdin2 && child.stdin) {
344
+ if (useStdin3 && child.stdin) {
345
345
  child.stdin.on("error", () => {
346
346
  });
347
- child.stdin.write(prompt2, () => {
347
+ child.stdin.write(prompt, () => {
348
348
  child.stdin?.end();
349
349
  });
350
350
  } else {
@@ -13370,10 +13370,10 @@ function parseJsonBody(body) {
13370
13370
  const p = asRecord(parsed);
13371
13371
  if (!p) return null;
13372
13372
  const details = asRecord(p.details) ?? {};
13373
- const pick2 = (k) => p[k] !== void 0 ? p[k] : details[k];
13374
- const codeRaw = pick2("code") ?? asRecord(p.sys)?.id;
13375
- const messageRaw = pick2("message");
13376
- const cycleRaw = pick2("cycle");
13373
+ const pick = (k) => p[k] !== void 0 ? p[k] : details[k];
13374
+ const codeRaw = pick("code") ?? asRecord(p.sys)?.id;
13375
+ const messageRaw = pick("message");
13376
+ const cycleRaw = pick("cycle");
13377
13377
  const out = {};
13378
13378
  if (typeof codeRaw === "string") out.code = codeRaw;
13379
13379
  if (typeof messageRaw === "string") out.message = messageRaw;
@@ -17401,7 +17401,7 @@ function WelcomeStep({ onContinue, onQuit }) {
17401
17401
  });
17402
17402
  const displayValue2 = inputValue + (cursorVisible ? "\u2588" : " ");
17403
17403
  return /* @__PURE__ */ jsxs24(Box24, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [
17404
- /* @__PURE__ */ jsx24(Text24, { bold: true, color: PALETTE.info, children: "\u{1F44B} Hey! Let's import your design system into Contentful." }),
17404
+ /* @__PURE__ */ jsx24(Text24, { bold: true, color: PALETTE.success, children: "\u{1F44B} Hey! Let's import your design system into Contentful." }),
17405
17405
  /* @__PURE__ */ jsx24(Text24, { dimColor: true, children: "I'll walk you through 5 steps to get your components into Contentful ExO." }),
17406
17406
  /* @__PURE__ */ jsxs24(Box24, { flexDirection: "column", marginTop: 1, gap: 0, children: [
17407
17407
  /* @__PURE__ */ jsx24(Text24, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
@@ -29780,13 +29780,13 @@ __export(push_creds_prompt_exports, {
29780
29780
  });
29781
29781
  import React29 from "react";
29782
29782
  async function promptForPushCredentials(opts = {}) {
29783
- const { render: render7, Box: Box50, Text: Text52 } = await import("ink");
29784
- const { useState: useState29 } = await import("react");
29783
+ const { render: render7, Box: Box52, Text: Text54 } = await import("ink");
29784
+ const { useState: useState30 } = await import("react");
29785
29785
  const { CredentialsStep: CredentialsStep2 } = await Promise.resolve().then(() => (init_CredentialsStep(), CredentialsStep_exports));
29786
29786
  return new Promise((resolve27, reject) => {
29787
29787
  let app = null;
29788
29788
  function App2() {
29789
- const [done, setDone] = useState29(false);
29789
+ const [done, setDone] = useState30(false);
29790
29790
  const handle = (spaceId, environmentId, cmaToken, host) => {
29791
29791
  if (done) return;
29792
29792
  setDone(true);
@@ -29796,7 +29796,7 @@ async function promptForPushCredentials(opts = {}) {
29796
29796
  });
29797
29797
  };
29798
29798
  if (done) {
29799
- return React29.createElement(Box50, null, React29.createElement(Text52, null, ""));
29799
+ return React29.createElement(Box52, null, React29.createElement(Text54, null, ""));
29800
29800
  }
29801
29801
  return React29.createElement(CredentialsStep2, {
29802
29802
  summary: opts.summary ?? "Enter Contentful credentials to push this run. Press Enter on each field to advance.",
@@ -29821,11 +29821,1116 @@ var init_push_creds_prompt = __esm({
29821
29821
  }
29822
29822
  });
29823
29823
 
29824
+ // packages/experience-design-system-cli/src/lib/agent-definitions.ts
29825
+ var AGENT_DEFS;
29826
+ var init_agent_definitions = __esm({
29827
+ "packages/experience-design-system-cli/src/lib/agent-definitions.ts"() {
29828
+ "use strict";
29829
+ AGENT_DEFS = [
29830
+ { name: "Claude Code", binary: "claude", installHint: "npm install -g @anthropic-ai/claude-code && claude login" },
29831
+ { name: "OpenAI Codex", binary: "codex", installHint: "npm install -g @openai/codex (requires OPENAI_API_KEY)" },
29832
+ { name: "OpenCode", binary: "opencode", installHint: "npm install -g opencode-ai && opencode auth" },
29833
+ { name: "GitHub Copilot", binary: "copilot", installHint: "npm install -g @github/copilot && copilot" }
29834
+ ];
29835
+ }
29836
+ });
29837
+
29838
+ // packages/experience-design-system-cli/src/setup/lib/shell.ts
29839
+ import { execFile as execFile5, spawn as spawn5 } from "node:child_process";
29840
+ import { appendFile as appendFile2, readFile as readFile27, access as access10 } from "node:fs/promises";
29841
+ import { homedir as homedir9 } from "node:os";
29842
+ import { join as join26 } from "node:path";
29843
+ import { promisify as promisify2 } from "node:util";
29844
+ async function binaryExists(name) {
29845
+ try {
29846
+ await execFileAsync2("which", [name]);
29847
+ return true;
29848
+ } catch {
29849
+ return false;
29850
+ }
29851
+ }
29852
+ async function pathExists5(path) {
29853
+ return access10(path).then(() => true).catch(() => false);
29854
+ }
29855
+ function runSpawn(cmd, args, opts = {}) {
29856
+ return new Promise((resolve27) => {
29857
+ const child = spawn5(cmd, args, {
29858
+ cwd: opts.cwd,
29859
+ env: opts.env ?? process.env,
29860
+ stdio: ["ignore", "pipe", "pipe"]
29861
+ });
29862
+ let settled = false;
29863
+ let stdout = "";
29864
+ let stderr = "";
29865
+ const settle = (result) => {
29866
+ if (settled) return;
29867
+ settled = true;
29868
+ resolve27(result);
29869
+ };
29870
+ child.on("error", (err) => settle({ exitCode: 1, stdout: "", stderr: err.message }));
29871
+ child.stdout.on("data", (d) => {
29872
+ stdout += String(d);
29873
+ });
29874
+ child.stderr.on("data", (d) => {
29875
+ stderr += String(d);
29876
+ });
29877
+ child.on("exit", (code) => settle({ exitCode: code ?? 1, stdout, stderr }));
29878
+ });
29879
+ }
29880
+ async function detectShellProfile() {
29881
+ const shell = process.env["SHELL"] ?? "";
29882
+ const home = homedir9();
29883
+ if (shell.includes("zsh")) return join26(home, ".zshrc");
29884
+ if (shell.includes("bash")) {
29885
+ const bashProfile = join26(home, ".bash_profile");
29886
+ return await pathExists5(bashProfile) ? bashProfile : join26(home, ".bashrc");
29887
+ }
29888
+ if (shell.includes("fish")) return join26(home, ".config", "fish", "config.fish");
29889
+ return join26(home, ".profile");
29890
+ }
29891
+ async function profileContains(profilePath, value) {
29892
+ try {
29893
+ const content = await readFile27(profilePath, "utf8");
29894
+ return content.includes(value);
29895
+ } catch {
29896
+ return false;
29897
+ }
29898
+ }
29899
+ async function appendToProfile(profilePath, lines) {
29900
+ await appendFile2(profilePath, `
29901
+ ${lines}
29902
+ `, "utf8");
29903
+ }
29904
+ var execFileAsync2, REQUIRED_NODE_MAJOR;
29905
+ var init_shell = __esm({
29906
+ "packages/experience-design-system-cli/src/setup/lib/shell.ts"() {
29907
+ "use strict";
29908
+ execFileAsync2 = promisify2(execFile5);
29909
+ REQUIRED_NODE_MAJOR = 24;
29910
+ }
29911
+ });
29912
+
29913
+ // packages/experience-design-system-cli/src/setup/lib/checks.ts
29914
+ import { join as join27 } from "node:path";
29915
+ function checkNodeVersion(nodeVersion = process.versions.node) {
29916
+ const major = Number.parseInt(nodeVersion.split(".")[0], 10);
29917
+ return { passed: major >= REQUIRED_NODE_MAJOR, version: nodeVersion, major, required: REQUIRED_NODE_MAJOR };
29918
+ }
29919
+ async function detectNodeVersionManagers(homeDir, deps = { binaryExists, pathExists: pathExists5 }) {
29920
+ const nvmScript = join27(homeDir, ".nvm", "nvm.sh");
29921
+ return {
29922
+ nvm: await deps.binaryExists("nvm") || await deps.pathExists(nvmScript),
29923
+ fnm: await deps.binaryExists("fnm"),
29924
+ nvmScript
29925
+ };
29926
+ }
29927
+ async function detectPnpm(deps = { binaryExists, run: runSpawn }) {
29928
+ if (!await deps.binaryExists("pnpm")) return { status: "missing" };
29929
+ const version = await deps.run("pnpm", ["--version"]);
29930
+ if (version.exitCode !== 0) return { status: "broken" };
29931
+ return { status: "ok", version: version.stdout.trim() };
29932
+ }
29933
+ async function checkPnpm(pkgRoot, deps = { binaryExists, run: runSpawn }) {
29934
+ const detected = await detectPnpm(deps);
29935
+ if (detected.status !== "ok") return detected;
29936
+ const ping = await deps.run("pnpm", ["exec", "node", "--version"], { cwd: pkgRoot });
29937
+ if (ping.exitCode !== 0) return { status: "unusable-in-repo", version: detected.version };
29938
+ return detected;
29939
+ }
29940
+ async function installDependencies(pkgRoot, deps = { pathExists: pathExists5, run: runSpawn }) {
29941
+ const hadNodeModules = await deps.pathExists(join27(pkgRoot, "node_modules"));
29942
+ const repoRoot = join27(pkgRoot, "..", "..");
29943
+ const result = await deps.run("pnpm", ["install", "--frozen-lockfile"], { cwd: repoRoot });
29944
+ return { passed: result.exitCode === 0, hadNodeModules, result };
29945
+ }
29946
+ async function buildCli(repoRoot, deps = { run: runSpawn }) {
29947
+ const result = await deps.run("pnpm", ["--filter", "@contentful/experience-design-system-cli", "run", "build"], {
29948
+ cwd: repoRoot
29949
+ });
29950
+ return { passed: result.exitCode === 0, result };
29951
+ }
29952
+ var init_checks = __esm({
29953
+ "packages/experience-design-system-cli/src/setup/lib/checks.ts"() {
29954
+ "use strict";
29955
+ init_shell();
29956
+ }
29957
+ });
29958
+
29959
+ // packages/experience-design-system-cli/src/setup/lib/layout.ts
29960
+ function shouldUseWideStepper(columns) {
29961
+ return columns !== void 0 && columns >= WIDE_STEPPER_MIN_COLUMNS;
29962
+ }
29963
+ function setupStepperSeparator(columns) {
29964
+ return shouldUseWideStepper(columns) ? WIDE_STEPPER_SEPARATOR : COMPACT_STEPPER_SEPARATOR;
29965
+ }
29966
+ function setupStepperEntries(activeStep, columns) {
29967
+ const isWide = shouldUseWideStepper(columns);
29968
+ return SETUP_SCREENS.map((screen, index) => {
29969
+ const step = index + 1;
29970
+ const state = step < activeStep ? "complete" : step === activeStep ? "active" : "pending";
29971
+ const compactLabel = state === "complete" ? "\u2713" : state === "active" ? `[${step}]` : `${step}`;
29972
+ const wideLabel = state === "complete" ? `\u2713 ${screen.title}` : state === "active" ? `[${step} ${screen.title}]` : `${step} ${screen.title}`;
29973
+ return { step, title: screen.title, state, label: isWide ? wideLabel : compactLabel };
29974
+ });
29975
+ }
29976
+ function shouldAlignVersionRight(version, columns) {
29977
+ const contentWidth = SETUP_TITLE.length + HEADER_INLINE_GAP + `v${version}`.length;
29978
+ return columns !== void 0 && columns >= contentWidth;
29979
+ }
29980
+ function countRequiredFailures(results) {
29981
+ return results.filter((result) => result.required && result.status === "failed").length;
29982
+ }
29983
+ function formatSetupCompletionMessage(results) {
29984
+ const requiredFailed = countRequiredFailures(results);
29985
+ if (requiredFailed === 0) return "\u2713 Setup complete. You can now run: experiences import";
29986
+ return `\u26A0 ${requiredFailed} required step${requiredFailed === 1 ? "" : "s"} incomplete.`;
29987
+ }
29988
+ var SETUP_SCREENS, SETUP_TITLE, WIDE_STEPPER_SEPARATOR, COMPACT_STEPPER_SEPARATOR, HEADER_INLINE_GAP, WIDE_STEPPER_MIN_COLUMNS;
29989
+ var init_layout = __esm({
29990
+ "packages/experience-design-system-cli/src/setup/lib/layout.ts"() {
29991
+ "use strict";
29992
+ SETUP_SCREENS = [
29993
+ { title: "Prerequisites", kind: "required" },
29994
+ { title: "Coding agent", kind: "required" },
29995
+ { title: "Contentful", kind: "optional" },
29996
+ { title: "Preferences", kind: "optional" }
29997
+ ];
29998
+ SETUP_TITLE = "experiences setup";
29999
+ WIDE_STEPPER_SEPARATOR = " \xB7 ";
30000
+ COMPACT_STEPPER_SEPARATOR = " ";
30001
+ HEADER_INLINE_GAP = 2;
30002
+ WIDE_STEPPER_MIN_COLUMNS = SETUP_SCREENS.map((screen, index) => {
30003
+ const label = `${index + 1} ${screen.title}`;
30004
+ return index === 0 ? `[${label}]` : label;
30005
+ }).join(WIDE_STEPPER_SEPARATOR).length;
30006
+ }
30007
+ });
30008
+
30009
+ // packages/experience-design-system-cli/src/setup/lib/prompt-input.ts
30010
+ function splitPromptInput(input) {
30011
+ const cleaned = stripBracketedPasteMarkers(input);
30012
+ const newlineIndex = cleaned.search(/[\r\n]/);
30013
+ if (newlineIndex === -1) return { text: stripControlCharacters(cleaned), submitted: false };
30014
+ return { text: stripControlCharacters(cleaned.slice(0, newlineIndex)), submitted: true };
30015
+ }
30016
+ function stripBracketedPasteMarkers(value) {
30017
+ return value.replace(/\x1b\[20[01]~/g, "");
30018
+ }
30019
+ function stripControlCharacters(value) {
30020
+ return value.replace(/[\x00-\x1f\x7f]/g, "");
30021
+ }
30022
+ var init_prompt_input = __esm({
30023
+ "packages/experience-design-system-cli/src/setup/lib/prompt-input.ts"() {
30024
+ "use strict";
30025
+ }
30026
+ });
30027
+
30028
+ // packages/experience-design-system-cli/src/setup/tui/usePromptInput.ts
30029
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef10 } from "react";
30030
+ import { useStdin as useStdin2 } from "ink";
30031
+ function parseKey(data) {
30032
+ const code = data.length === 1 ? data.charCodeAt(0) : -1;
30033
+ const isReturn = data === "\r" || data === "\n";
30034
+ const isTab = data === " " || data === "\x1B[Z";
30035
+ const isDelete = data === "\x1B[3~";
30036
+ const isPaste = data.startsWith(BRACKETED_PASTE_START);
30037
+ const upArrow = data === "\x1B[A";
30038
+ const downArrow = data === "\x1B[B";
30039
+ const isEscapeSequence = isPaste || isTab || isDelete || upArrow || downArrow;
30040
+ return {
30041
+ return: isReturn,
30042
+ escape: data === "\x1B",
30043
+ tab: isTab,
30044
+ backspace: data === "\x7F" || data === "\b",
30045
+ delete: isDelete,
30046
+ upArrow,
30047
+ downArrow,
30048
+ ctrl: !isReturn && !isTab && code >= 1 && code <= 26,
30049
+ meta: !isEscapeSequence && data.length > 1 && data.startsWith("\x1B")
30050
+ };
30051
+ }
30052
+ function usePromptInput(handler) {
30053
+ const { stdin, setRawMode } = useStdin2();
30054
+ const handlerRef = useRef10(handler);
30055
+ handlerRef.current = handler;
30056
+ useLayoutEffect2(() => {
30057
+ setRawMode(true);
30058
+ const handleData = (data) => {
30059
+ const chunk = Buffer.isBuffer(data) ? data.toString("utf8") : data;
30060
+ handlerRef.current(chunk, parseKey(chunk));
30061
+ };
30062
+ stdin.on("data", handleData);
30063
+ return () => {
30064
+ stdin.off("data", handleData);
30065
+ setRawMode(false);
30066
+ };
30067
+ }, [stdin, setRawMode]);
30068
+ }
30069
+ var BRACKETED_PASTE_START;
30070
+ var init_usePromptInput = __esm({
30071
+ "packages/experience-design-system-cli/src/setup/tui/usePromptInput.ts"() {
30072
+ "use strict";
30073
+ BRACKETED_PASTE_START = "\x1B[200~";
30074
+ }
30075
+ });
30076
+
30077
+ // packages/experience-design-system-cli/src/setup/tui/SetupStepper.tsx
30078
+ import React30 from "react";
30079
+ import { Box as Box50, Text as Text52 } from "ink";
30080
+ import { jsx as jsx53, jsxs as jsxs52 } from "react/jsx-runtime";
30081
+ function stepColor(state) {
30082
+ if (state === "complete") return PALETTE.success;
30083
+ if (state === "active") return PALETTE.info;
30084
+ return void 0;
30085
+ }
30086
+ function SetupStepper({ activeStep, columns }) {
30087
+ const entries = setupStepperEntries(activeStep, columns);
30088
+ const separator = setupStepperSeparator(columns);
30089
+ return /* @__PURE__ */ jsx53(Box50, { children: entries.map((entry, index) => /* @__PURE__ */ jsxs52(React30.Fragment, { children: [
30090
+ index > 0 && /* @__PURE__ */ jsx53(Text52, { dimColor: true, children: separator }),
30091
+ /* @__PURE__ */ jsx53(Text52, { bold: entry.state === "active", dimColor: entry.state === "pending", color: stepColor(entry.state), children: entry.label })
30092
+ ] }, entry.step)) });
30093
+ }
30094
+ var init_SetupStepper = __esm({
30095
+ "packages/experience-design-system-cli/src/setup/tui/SetupStepper.tsx"() {
30096
+ "use strict";
30097
+ init_theme();
30098
+ init_layout();
30099
+ }
30100
+ });
30101
+
30102
+ // packages/experience-design-system-cli/src/setup/lib/types.ts
30103
+ function emit(dependencies, kind, message) {
30104
+ dependencies.write({ kind, message });
30105
+ }
30106
+ var init_types4 = __esm({
30107
+ "packages/experience-design-system-cli/src/setup/lib/types.ts"() {
30108
+ "use strict";
30109
+ }
30110
+ });
30111
+
30112
+ // packages/experience-design-system-cli/src/setup/steps/coding-agent.ts
30113
+ async function promptCodexModel(dependencies) {
30114
+ if (dependencies.env["OPENAI_API_KEY"]) return void 0;
30115
+ emit(dependencies, "warning", "No OPENAI_API_KEY \u2014 using ChatGPT account authentication.");
30116
+ emit(dependencies, "info", "Tip: run codex then type /model to browse all available models.");
30117
+ const model = (await dependencies.ask("Model name (optional - press Enter for Codex default): ")).trim();
30118
+ return model || void 0;
30119
+ }
30120
+ async function runAgentSetup(dependencies) {
30121
+ emit(dependencies, "info", "Experiences import uses a coding agent to generate component definitions.");
30122
+ const found = (await Promise.all(
30123
+ AGENT_DEFS.map(async (agent) => await dependencies.binaryExists(agent.binary) ? agent : void 0)
30124
+ )).filter((agent) => agent !== void 0);
30125
+ if (found.length === 1) {
30126
+ const selected2 = found[0];
30127
+ emit(dependencies, "success", `${selected2.name} (${selected2.binary}) found`);
30128
+ const agentModel2 = selected2.binary === "codex" ? await promptCodexModel(dependencies) : void 0;
30129
+ return { agent: selected2.binary, agentModel: agentModel2, passed: true };
30130
+ }
30131
+ if (found.length > 1) {
30132
+ const index2 = await dependencies.choose(
30133
+ "Multiple coding agents found. Choose one to use as the default:",
30134
+ found.map((agent) => ({ label: agent.name, description: agent.binary }))
30135
+ );
30136
+ if (index2 === void 0) return { agent: void 0, agentModel: void 0, passed: false };
30137
+ const selected2 = found[index2] ?? found[0];
30138
+ const agentModel2 = selected2.binary === "codex" ? await promptCodexModel(dependencies) : void 0;
30139
+ return { agent: selected2.binary, agentModel: agentModel2, passed: true };
30140
+ }
30141
+ emit(dependencies, "warning", "No coding agent found on PATH");
30142
+ const installable = AGENT_DEFS.slice(0, 3);
30143
+ const index = await dependencies.choose(
30144
+ "Choose one to install:",
30145
+ installable.map((agent) => ({ label: agent.name, description: agent.installHint }))
30146
+ );
30147
+ const selected = index === void 0 ? void 0 : installable[index];
30148
+ if (!selected) return { agent: void 0, agentModel: void 0, passed: false };
30149
+ const packageName = selected.binary === "claude" ? "@anthropic-ai/claude-code" : selected.binary === "codex" ? "@openai/codex" : "opencode-ai";
30150
+ const installed = await dependencies.run("npm", ["install", "-g", packageName]);
30151
+ if (installed.exitCode !== 0 || !await dependencies.binaryExists(selected.binary)) {
30152
+ emit(dependencies, "failure", "Install failed");
30153
+ return { agent: void 0, agentModel: void 0, passed: false };
30154
+ }
30155
+ emit(dependencies, "success", `${selected.name} installed`);
30156
+ const agentModel = selected.binary === "codex" ? await promptCodexModel(dependencies) : void 0;
30157
+ return { agent: selected.binary, agentModel, passed: true };
30158
+ }
30159
+ var init_coding_agent = __esm({
30160
+ "packages/experience-design-system-cli/src/setup/steps/coding-agent.ts"() {
30161
+ "use strict";
30162
+ init_agent_definitions();
30163
+ init_types4();
30164
+ }
30165
+ });
30166
+
30167
+ // packages/experience-design-system-cli/src/setup/steps/contentful.ts
30168
+ function emitCurrentValue(dependencies, label, value) {
30169
+ const paddedLabel = label.padEnd(CURRENT_VALUE_LABEL_WIDTH);
30170
+ if (value) emit(dependencies, "value", `${paddedLabel}${value}`);
30171
+ else emit(dependencies, "warning", `${paddedLabel}(not set)`);
30172
+ }
30173
+ async function runCredentialsSetup(dependencies) {
30174
+ emit(
30175
+ dependencies,
30176
+ "help",
30177
+ `Saved to ${dependencies.credentialsPath()} \u2014 loaded automatically by experiences import.`
30178
+ );
30179
+ emit(dependencies, "info", "");
30180
+ const envShadowing = [
30181
+ dependencies.env["CONTENTFUL_SPACE_ID"] ? "CONTENTFUL_SPACE_ID" : void 0,
30182
+ dependencies.env["CONTENTFUL_ENVIRONMENT_ID"] ? "CONTENTFUL_ENVIRONMENT_ID" : void 0,
30183
+ dependencies.env["CONTENTFUL_MANAGEMENT_TOKEN"] ? "CONTENTFUL_MANAGEMENT_TOKEN" : void 0,
30184
+ dependencies.env["EDS_HOST"] ? "EDS_HOST" : void 0
30185
+ ].filter((value) => value !== void 0);
30186
+ if (envShadowing.length > 0) {
30187
+ emit(
30188
+ dependencies,
30189
+ "warning",
30190
+ `Env vars set: ${envShadowing.join(", ")}. Values saved here take precedence; env vars only apply where disk is empty.`
30191
+ );
30192
+ }
30193
+ const stored = await dependencies.readCredentials();
30194
+ const currentHost = stored.host ?? DEFAULT_CONFIGURED_HOST;
30195
+ const hasAny = Boolean(stored.spaceId || stored.environmentId || stored.cmaToken);
30196
+ const allSet = Boolean(stored.spaceId && stored.environmentId && stored.cmaToken);
30197
+ if (hasAny) {
30198
+ emit(dependencies, "info", "Current values:");
30199
+ emitCurrentValue(dependencies, "Space ID", stored.spaceId);
30200
+ emitCurrentValue(dependencies, "Environment ID", stored.environmentId);
30201
+ emitCurrentValue(
30202
+ dependencies,
30203
+ "CMA Token",
30204
+ stored.cmaToken && `${"\u2022".repeat(Math.min(stored.cmaToken.length, 8))}...`
30205
+ );
30206
+ emitCurrentValue(dependencies, "API Host", currentHost);
30207
+ }
30208
+ if (!await dependencies.confirm(hasAny ? "Update credentials?" : "Configure Contentful credentials?", !allSet)) {
30209
+ if (allSet) emit(dependencies, "success", "Credentials already configured \u2014 no changes made");
30210
+ else emit(dependencies, "warning", "Skipped. experiences import will prompt for credentials interactively.");
30211
+ return { passed: true };
30212
+ }
30213
+ const spaceId = await dependencies.ask(`Space ID${stored.spaceId ? ` [${stored.spaceId}]` : ""}: `) || stored.spaceId;
30214
+ const environmentId = await dependencies.ask(`Environment ID [${stored.environmentId || "master"}]: `) || stored.environmentId || "master";
30215
+ const cmaToken = await dependencies.askSecret(
30216
+ `CMA token${stored.cmaToken ? " [press Enter to keep existing]" : " (paste here)"}: `
30217
+ ) || stored.cmaToken;
30218
+ if (!spaceId || !cmaToken) {
30219
+ emit(dependencies, "warning", "Space ID and CMA token are required. Skipped.");
30220
+ return { passed: false };
30221
+ }
30222
+ const hostInput = await dependencies.ask(`API host [${currentHost}]: `);
30223
+ const host = toConfiguredHost(hostInput) ?? stored.host;
30224
+ await dependencies.writeCredentials({ ...stored, spaceId, environmentId, cmaToken, ...host ? { host } : {} });
30225
+ emit(dependencies, "success", `Credentials saved to ${dependencies.credentialsPath()}`);
30226
+ emit(dependencies, "success", `API host set to ${host ?? DEFAULT_CONFIGURED_HOST}`);
30227
+ emit(dependencies, "info", "Run experiences import \u2014 credentials will be pre-filled automatically.");
30228
+ return { passed: true };
30229
+ }
30230
+ var CURRENT_VALUE_LABEL_WIDTH;
30231
+ var init_contentful = __esm({
30232
+ "packages/experience-design-system-cli/src/setup/steps/contentful.ts"() {
30233
+ "use strict";
30234
+ init_host_utils();
30235
+ init_types4();
30236
+ CURRENT_VALUE_LABEL_WIDTH = "Environment ID".length + 2;
30237
+ }
30238
+ });
30239
+
30240
+ // packages/experience-design-system-cli/src/setup/lib/yes-no-prompt.ts
30241
+ async function askYesNo(ask, question, defaultValue) {
30242
+ const hint = defaultValue ? "[Y/n]" : "[y/N]";
30243
+ const answer = (await ask(`${question} ${hint} `)).trim().toLowerCase();
30244
+ if (answer.startsWith("y")) return true;
30245
+ if (answer.startsWith("n")) return false;
30246
+ return defaultValue;
30247
+ }
30248
+ var init_yes_no_prompt = __esm({
30249
+ "packages/experience-design-system-cli/src/setup/lib/yes-no-prompt.ts"() {
30250
+ "use strict";
30251
+ }
30252
+ });
30253
+
30254
+ // packages/experience-design-system-cli/src/setup/steps/preferences/analytics.ts
30255
+ async function promptAnalyticsPreference(ask, current) {
30256
+ return askYesNo(ask, "Disable anonymous usage analytics?", current ?? false);
30257
+ }
30258
+ async function configureAnalytics(dependencies) {
30259
+ emit(dependencies, "page", ANALYTICS_HELP);
30260
+ const stored = await dependencies.readCredentials();
30261
+ const analyticsDisabled = await promptAnalyticsPreference(dependencies.ask, stored.analyticsDisabled);
30262
+ if (analyticsDisabled !== (stored.analyticsDisabled ?? false))
30263
+ await dependencies.writeCredentials({ ...stored, analyticsDisabled });
30264
+ }
30265
+ var ANALYTICS_HELP;
30266
+ var init_analytics2 = __esm({
30267
+ "packages/experience-design-system-cli/src/setup/steps/preferences/analytics.ts"() {
30268
+ "use strict";
30269
+ init_yes_no_prompt();
30270
+ init_types4();
30271
+ ANALYTICS_HELP = "Shares anonymous usage data about which CLI commands run.";
30272
+ }
30273
+ });
30274
+
30275
+ // packages/experience-design-system-cli/src/setup/steps/preferences/auto-filter.ts
30276
+ async function promptAutoFilterPreference(ask, current) {
30277
+ return askYesNo(ask, "Enable AI auto-filter by default?", current ?? true);
30278
+ }
30279
+ async function configureAutoFilter(dependencies) {
30280
+ emit(dependencies, "page", AUTO_FILTER_HELP);
30281
+ const stored = await dependencies.readCredentials();
30282
+ const autoFilter = await promptAutoFilterPreference(dependencies.ask, stored.autoFilter);
30283
+ if (autoFilter !== (stored.autoFilter ?? true)) await dependencies.writeCredentials({ ...stored, autoFilter });
30284
+ }
30285
+ var AUTO_FILTER_HELP;
30286
+ var init_auto_filter = __esm({
30287
+ "packages/experience-design-system-cli/src/setup/steps/preferences/auto-filter.ts"() {
30288
+ "use strict";
30289
+ init_yes_no_prompt();
30290
+ init_types4();
30291
+ AUTO_FILTER_HELP = "Filters out components irrelevant to experience orchestration during extraction.";
30292
+ }
30293
+ });
30294
+
30295
+ // packages/experience-design-system-cli/src/setup/steps/preferences/concurrency.ts
30296
+ async function configureConcurrency(dependencies, profilePath) {
30297
+ emit(dependencies, "page", CONCURRENCY_HELP);
30298
+ if (await dependencies.profileContains(profilePath, PROFILE_VARIABLE)) return;
30299
+ if (!await dependencies.confirm("Speed up component analysis on this machine?", false)) return;
30300
+ await dependencies.appendToProfile(profilePath, `# experiences performance
30301
+ export ${PROFILE_VARIABLE}=8`);
30302
+ }
30303
+ var CONCURRENCY_HELP, PROFILE_VARIABLE;
30304
+ var init_concurrency = __esm({
30305
+ "packages/experience-design-system-cli/src/setup/steps/preferences/concurrency.ts"() {
30306
+ "use strict";
30307
+ init_types4();
30308
+ CONCURRENCY_HELP = "Analyzes more components at once, which is faster on machines with spare cores.";
30309
+ PROFILE_VARIABLE = "EDS_EXTRACT_CONCURRENCY";
30310
+ }
30311
+ });
30312
+
30313
+ // packages/experience-design-system-cli/src/setup/steps/preferences/custom-prompts.ts
30314
+ async function promptCustomSkillPathAction(kind, current, ask) {
30315
+ const label = kind === "select" ? "select (analyze select-agent)" : "generate (generate components)";
30316
+ const answer = await ask(
30317
+ `Custom ${label} prompt path${current ? ` [${current}]` : " [none]"} (empty=keep, "-"=clear): `
30318
+ );
30319
+ const trimmed = answer.trim();
30320
+ return trimmed === "" ? void 0 : trimmed === "-" ? null : trimmed;
30321
+ }
30322
+ async function configureCustomPrompts(dependencies) {
30323
+ emit(dependencies, "page", CUSTOM_PROMPTS_HELP);
30324
+ if (!await dependencies.confirm("Use your own prompt files instead of the built-in ones?", false)) return;
30325
+ const stored = await dependencies.readCredentials();
30326
+ const selectPromptPath = await promptCustomSkillPathAction("select", stored.selectPromptPath, dependencies.ask);
30327
+ const generatePromptPath = await promptCustomSkillPathAction("generate", stored.generatePromptPath, dependencies.ask);
30328
+ const updated = { ...stored };
30329
+ if (selectPromptPath === null) delete updated.selectPromptPath;
30330
+ else if (selectPromptPath !== void 0) updated.selectPromptPath = selectPromptPath;
30331
+ if (generatePromptPath === null) delete updated.generatePromptPath;
30332
+ else if (generatePromptPath !== void 0) updated.generatePromptPath = generatePromptPath;
30333
+ await dependencies.writeCredentials(updated);
30334
+ }
30335
+ var CUSTOM_PROMPTS_HELP;
30336
+ var init_custom_prompts = __esm({
30337
+ "packages/experience-design-system-cli/src/setup/steps/preferences/custom-prompts.ts"() {
30338
+ "use strict";
30339
+ init_types4();
30340
+ CUSTOM_PROMPTS_HELP = "Replaces the built-in instructions the coding agent follows when it selects and generates components.";
30341
+ }
30342
+ });
30343
+
30344
+ // packages/experience-design-system-cli/src/setup/steps/preferences/debug.ts
30345
+ async function promptDebugModePreference(ask, current) {
30346
+ return askYesNo(ask, "Enable debug logging by default?", current ?? false);
30347
+ }
30348
+ async function configureDebug(dependencies) {
30349
+ emit(dependencies, "page", DEBUG_HELP);
30350
+ const stored = await dependencies.readCredentials();
30351
+ const debug = await promptDebugModePreference(dependencies.ask, stored.debug);
30352
+ if (debug !== (stored.debug ?? false)) await dependencies.writeCredentials({ ...stored, debug });
30353
+ }
30354
+ var DEBUG_HELP;
30355
+ var init_debug = __esm({
30356
+ "packages/experience-design-system-cli/src/setup/steps/preferences/debug.ts"() {
30357
+ "use strict";
30358
+ init_yes_no_prompt();
30359
+ init_types4();
30360
+ DEBUG_HELP = "Writes a verbose trace of every command decision, for troubleshooting.";
30361
+ }
30362
+ });
30363
+
30364
+ // packages/experience-design-system-cli/src/setup/steps/preferences/no-color.ts
30365
+ async function configureNoColor(dependencies, profilePath) {
30366
+ emit(dependencies, "page", NO_COLOR_HELP);
30367
+ if (!await dependencies.confirm("Turn off colored output?", false)) return;
30368
+ if (await dependencies.profileContains(profilePath, PROFILE_VARIABLE2)) return;
30369
+ await dependencies.appendToProfile(profilePath, `export ${PROFILE_VARIABLE2}=1`);
30370
+ }
30371
+ var NO_COLOR_HELP, PROFILE_VARIABLE2;
30372
+ var init_no_color = __esm({
30373
+ "packages/experience-design-system-cli/src/setup/steps/preferences/no-color.ts"() {
30374
+ "use strict";
30375
+ init_types4();
30376
+ NO_COLOR_HELP = "Prints plain text with no color, which suits CI logs and basic terminals.";
30377
+ PROFILE_VARIABLE2 = "NO_COLOR";
30378
+ }
30379
+ });
30380
+
30381
+ // packages/experience-design-system-cli/src/setup/steps/preferences/index.ts
30382
+ async function runPreferenceSetupAction(dependencies, profilePath) {
30383
+ for (const option of PREFERENCE_OPTIONS) {
30384
+ await option.configure(dependencies, profilePath);
30385
+ }
30386
+ return { selected: PREFERENCE_OPTIONS.map((option) => option.key) };
30387
+ }
30388
+ var PREFERENCE_OPTIONS;
30389
+ var init_preferences = __esm({
30390
+ "packages/experience-design-system-cli/src/setup/steps/preferences/index.ts"() {
30391
+ "use strict";
30392
+ init_analytics2();
30393
+ init_auto_filter();
30394
+ init_concurrency();
30395
+ init_custom_prompts();
30396
+ init_debug();
30397
+ init_no_color();
30398
+ PREFERENCE_OPTIONS = [
30399
+ { key: "autoFilter", label: "AI auto-filter", configure: configureAutoFilter },
30400
+ { key: "concurrency", label: "Performance concurrency", configure: configureConcurrency },
30401
+ { key: "customPrompts", label: "Custom prompts", configure: configureCustomPrompts },
30402
+ { key: "debug", label: "Debug logging", configure: configureDebug },
30403
+ { key: "analytics", label: "Usage analytics", configure: configureAnalytics },
30404
+ { key: "noColor", label: "Disable terminal colors", configure: configureNoColor }
30405
+ ];
30406
+ }
30407
+ });
30408
+
30409
+ // packages/experience-design-system-cli/src/setup/steps/prerequisites/build.ts
30410
+ async function runBuildSetup(dependencies, repoRoot) {
30411
+ emit(dependencies, "info", "Running pnpm install...");
30412
+ const install = await dependencies.run("pnpm", ["install", "--frozen-lockfile"], { cwd: repoRoot });
30413
+ if (install.exitCode !== 0) {
30414
+ emit(dependencies, "failure", "pnpm install failed");
30415
+ return { passed: false };
30416
+ }
30417
+ emit(dependencies, "success", "Dependencies installed");
30418
+ emit(dependencies, "info", "Building CLI...");
30419
+ const build = await dependencies.run(
30420
+ "pnpm",
30421
+ ["--filter", "@contentful/experience-design-system-cli", "run", "build"],
30422
+ { cwd: repoRoot }
30423
+ );
30424
+ if (build.exitCode !== 0) {
30425
+ emit(dependencies, "failure", "Build failed");
30426
+ return { passed: false };
30427
+ }
30428
+ emit(dependencies, "success", "CLI built successfully");
30429
+ return { passed: true };
30430
+ }
30431
+ var init_build = __esm({
30432
+ "packages/experience-design-system-cli/src/setup/steps/prerequisites/build.ts"() {
30433
+ "use strict";
30434
+ init_types4();
30435
+ }
30436
+ });
30437
+
30438
+ // packages/experience-design-system-cli/src/setup/steps/prerequisites/node.ts
30439
+ async function runNodeSetup(dependencies) {
30440
+ const check = checkNodeVersion(dependencies.nodeVersion);
30441
+ if (check.passed) {
30442
+ emit(dependencies, "success", `Node.js v${check.version} \u2014 already good`);
30443
+ return { passed: true };
30444
+ }
30445
+ emit(dependencies, "failure", `Node.js v${check.version} \u2014 need v${check.required}+`);
30446
+ const { nvm: hasNvm, fnm: hasFnm, nvmScript } = await detectNodeVersionManagers(dependencies.homeDir, dependencies);
30447
+ if (hasNvm) {
30448
+ emit(
30449
+ dependencies,
30450
+ "info",
30451
+ `nvm detected. Will run: nvm install ${REQUIRED_NODE_MAJOR} && nvm use ${REQUIRED_NODE_MAJOR}`
30452
+ );
30453
+ if (!await dependencies.confirm(`Install and switch to Node ${REQUIRED_NODE_MAJOR} via nvm?`)) {
30454
+ emit(
30455
+ dependencies,
30456
+ "warning",
30457
+ `Skipped. Re-run experiences setup after switching to Node ${REQUIRED_NODE_MAJOR}.`
30458
+ );
30459
+ return { passed: false };
30460
+ }
30461
+ const result2 = await dependencies.run("bash", [
30462
+ "-c",
30463
+ `source "${nvmScript}" && nvm install ${REQUIRED_NODE_MAJOR} && nvm alias default ${REQUIRED_NODE_MAJOR}`
30464
+ ]);
30465
+ if (result2.exitCode !== 0) {
30466
+ emit(dependencies, "failure", "nvm install failed");
30467
+ emit(dependencies, "info", `Run manually: nvm install ${REQUIRED_NODE_MAJOR} && nvm use ${REQUIRED_NODE_MAJOR}`);
30468
+ return { passed: false };
30469
+ }
30470
+ emit(
30471
+ dependencies,
30472
+ "success",
30473
+ `Node ${REQUIRED_NODE_MAJOR} installed via nvm. Re-run experiences setup in a fresh shell to pick it up.`
30474
+ );
30475
+ return { passed: false, restartRequired: true };
30476
+ }
30477
+ if (hasFnm) {
30478
+ emit(
30479
+ dependencies,
30480
+ "info",
30481
+ `fnm detected. Will run: fnm install ${REQUIRED_NODE_MAJOR} && fnm use ${REQUIRED_NODE_MAJOR}`
30482
+ );
30483
+ if (!await dependencies.confirm(`Install and switch to Node ${REQUIRED_NODE_MAJOR} via fnm?`)) {
30484
+ emit(
30485
+ dependencies,
30486
+ "warning",
30487
+ `Skipped. Re-run experiences setup after switching to Node ${REQUIRED_NODE_MAJOR}.`
30488
+ );
30489
+ return { passed: false };
30490
+ }
30491
+ const install = await dependencies.run("fnm", ["install", String(REQUIRED_NODE_MAJOR)]);
30492
+ if (install.exitCode !== 0) {
30493
+ emit(dependencies, "failure", "fnm install failed");
30494
+ return { passed: false };
30495
+ }
30496
+ const useResult = await dependencies.run("fnm", ["use", String(REQUIRED_NODE_MAJOR)]);
30497
+ if (useResult.exitCode !== 0) {
30498
+ emit(dependencies, "warning", `fnm use ${REQUIRED_NODE_MAJOR} failed \u2014 node installed but not activated`);
30499
+ emit(dependencies, "info", `Run manually: fnm use ${REQUIRED_NODE_MAJOR} && fnm default ${REQUIRED_NODE_MAJOR}`);
30500
+ } else {
30501
+ const defaultResult = await dependencies.run("fnm", ["default", String(REQUIRED_NODE_MAJOR)]);
30502
+ if (defaultResult.exitCode !== 0) {
30503
+ emit(
30504
+ dependencies,
30505
+ "warning",
30506
+ `fnm default ${REQUIRED_NODE_MAJOR} failed \u2014 version won't persist across new shells`
30507
+ );
30508
+ emit(dependencies, "info", `Run manually: fnm default ${REQUIRED_NODE_MAJOR}`);
30509
+ }
30510
+ }
30511
+ emit(
30512
+ dependencies,
30513
+ "success",
30514
+ `Node ${REQUIRED_NODE_MAJOR} installed via fnm. Re-run experiences setup in a fresh shell.`
30515
+ );
30516
+ return { passed: false, restartRequired: true };
30517
+ }
30518
+ emit(dependencies, "info", "No Node version manager detected (nvm or fnm).");
30519
+ if (!await dependencies.confirm("Install nvm now? (recommended)")) {
30520
+ emit(dependencies, "info", `Install Node ${REQUIRED_NODE_MAJOR} manually from https://nodejs.org`);
30521
+ return { passed: false };
30522
+ }
30523
+ const result = await dependencies.run("bash", [
30524
+ "-c",
30525
+ "curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash"
30526
+ ]);
30527
+ if (result.exitCode !== 0) {
30528
+ emit(dependencies, "failure", "nvm install failed");
30529
+ return { passed: false };
30530
+ }
30531
+ emit(dependencies, "success", "nvm installed. Open a new shell, then re-run experiences setup.");
30532
+ return { passed: false, restartRequired: true };
30533
+ }
30534
+ var init_node = __esm({
30535
+ "packages/experience-design-system-cli/src/setup/steps/prerequisites/node.ts"() {
30536
+ "use strict";
30537
+ init_checks();
30538
+ init_shell();
30539
+ init_types4();
30540
+ }
30541
+ });
30542
+
30543
+ // packages/experience-design-system-cli/src/setup/steps/prerequisites/pnpm.ts
30544
+ async function runPnpmSetup(dependencies) {
30545
+ const detected = await detectPnpm(dependencies);
30546
+ if (detected.status === "ok") {
30547
+ emit(dependencies, "success", `pnpm v${detected.version} \u2014 already installed`);
30548
+ return { passed: true };
30549
+ }
30550
+ emit(dependencies, "failure", "pnpm not found");
30551
+ if (await dependencies.binaryExists("corepack")) {
30552
+ emit(dependencies, "info", "Will run: corepack enable && corepack prepare pnpm@latest --activate");
30553
+ if (await dependencies.confirm("Install pnpm via corepack?")) {
30554
+ const enabled = await dependencies.run("corepack", ["enable"]);
30555
+ const installed = enabled.exitCode === 0 ? await dependencies.run("corepack", ["prepare", "pnpm@latest", "--activate"]) : enabled;
30556
+ if (installed.exitCode === 0) {
30557
+ emit(dependencies, "success", "pnpm installed via corepack");
30558
+ return { passed: true };
30559
+ }
30560
+ emit(dependencies, "failure", "corepack install failed");
30561
+ return { passed: false };
30562
+ }
30563
+ }
30564
+ emit(dependencies, "info", "Will run: npm install -g pnpm");
30565
+ if (!await dependencies.confirm("Install pnpm via npm?")) {
30566
+ emit(dependencies, "warning", "Skipped. Install pnpm manually: npm install -g pnpm");
30567
+ return { passed: false };
30568
+ }
30569
+ const result = await dependencies.run("npm", ["install", "-g", "pnpm"]);
30570
+ if (result.exitCode !== 0) {
30571
+ emit(dependencies, "failure", "npm install -g pnpm failed");
30572
+ return { passed: false };
30573
+ }
30574
+ emit(dependencies, "success", "pnpm installed");
30575
+ return { passed: true };
30576
+ }
30577
+ var init_pnpm = __esm({
30578
+ "packages/experience-design-system-cli/src/setup/steps/prerequisites/pnpm.ts"() {
30579
+ "use strict";
30580
+ init_checks();
30581
+ init_types4();
30582
+ }
30583
+ });
30584
+
30585
+ // packages/experience-design-system-cli/src/setup/steps/prerequisites/index.ts
30586
+ async function runPrerequisitesSetup(dependencies, repoRoot, options = {}) {
30587
+ const node = await runNodeSetup(dependencies);
30588
+ if (!node.passed) return { node };
30589
+ const pnpm = await runPnpmSetup(dependencies);
30590
+ if (!pnpm.passed || options.skipBuild) return { node, pnpm };
30591
+ const build = await runBuildSetup(dependencies, repoRoot);
30592
+ return { node, pnpm, build };
30593
+ }
30594
+ var init_prerequisites = __esm({
30595
+ "packages/experience-design-system-cli/src/setup/steps/prerequisites/index.ts"() {
30596
+ "use strict";
30597
+ init_build();
30598
+ init_node();
30599
+ init_pnpm();
30600
+ }
30601
+ });
30602
+
30603
+ // packages/experience-design-system-cli/src/setup/tui/SetupScreen.tsx
30604
+ var SetupScreen_exports = {};
30605
+ __export(SetupScreen_exports, {
30606
+ SetupScreen: () => SetupScreen
30607
+ });
30608
+ import { useEffect as useEffect16, useRef as useRef11, useState as useState29 } from "react";
30609
+ import { Box as Box51, Text as Text53, useStdout as useStdout9 } from "ink";
30610
+ import { Select } from "@inkjs/ui";
30611
+ import { Fragment as Fragment16, jsx as jsx54, jsxs as jsxs53 } from "react/jsx-runtime";
30612
+ function SetupScreen({
30613
+ version,
30614
+ repoRoot,
30615
+ profilePath,
30616
+ dependencies,
30617
+ skip = {},
30618
+ columns: columnsOverride,
30619
+ offerDoctor = false,
30620
+ onComplete
30621
+ }) {
30622
+ const { stdout } = useStdout9();
30623
+ const columns = columnsOverride ?? stdout?.columns;
30624
+ const [activeStep, setActiveStep] = useState29(1);
30625
+ const [events, setEvents] = useState29([]);
30626
+ const [prompt, setPrompt] = useState29(null);
30627
+ const [pageHelp, setPageHelp] = useState29(null);
30628
+ const [inputValue, setInputValue] = useState29("");
30629
+ const inputValueRef = useRef11("");
30630
+ const updateInput = (next) => {
30631
+ inputValueRef.current = next;
30632
+ setInputValue(next);
30633
+ };
30634
+ const [outcome, setOutcome] = useState29(null);
30635
+ const [notice, setNotice] = useState29(null);
30636
+ const startedRef = useRef11(false);
30637
+ useEffect16(() => {
30638
+ if (startedRef.current) return;
30639
+ startedRef.current = true;
30640
+ const request2 = (build) => new Promise((resolve27) => {
30641
+ updateInput("");
30642
+ setPrompt(build(resolve27));
30643
+ });
30644
+ const uiDependencies = {
30645
+ ...dependencies,
30646
+ ask: (question) => request2((resolve27) => ({ kind: "text", question, resolve: resolve27 })),
30647
+ askSecret: (question) => request2((resolve27) => ({ kind: "secret", question, resolve: resolve27 })),
30648
+ confirm: (question, defaultYes = true) => request2((resolve27) => ({ kind: "confirm", question, defaultYes, resolve: resolve27 })),
30649
+ choose: (question, options) => request2((resolve27) => ({ kind: "select", question, options, resolve: resolve27 })),
30650
+ write: (event) => {
30651
+ if (event.kind === "page") {
30652
+ setEvents([]);
30653
+ setPageHelp(event.message);
30654
+ return;
30655
+ }
30656
+ setEvents((current) => [...current, event]);
30657
+ }
30658
+ };
30659
+ const enterStep = (step) => {
30660
+ setActiveStep(step);
30661
+ setEvents([]);
30662
+ setPageHelp(null);
30663
+ };
30664
+ void (async () => {
30665
+ const results = [];
30666
+ const prerequisites = await runPrerequisitesSetup(uiDependencies, repoRoot, {
30667
+ ...skip.skipBuild !== void 0 ? { skipBuild: skip.skipBuild } : {}
30668
+ });
30669
+ results.push({
30670
+ name: "Node.js 24+",
30671
+ status: prerequisites.node.passed ? "completed" : "failed",
30672
+ required: true
30673
+ });
30674
+ if (!prerequisites.node.passed) {
30675
+ setNotice("Node.js setup requires a shell restart. Re-run experiences setup afterwards.");
30676
+ const restartOutcome = {
30677
+ results,
30678
+ exitCode: 0,
30679
+ restartRequired: prerequisites.node.restartRequired ?? false,
30680
+ runDoctor: false
30681
+ };
30682
+ setOutcome(restartOutcome);
30683
+ onComplete(restartOutcome);
30684
+ return;
30685
+ }
30686
+ results.push({ name: "pnpm", status: prerequisites.pnpm?.passed ? "completed" : "failed", required: true });
30687
+ if (prerequisites.build) {
30688
+ results.push({
30689
+ name: "Install & build",
30690
+ status: prerequisites.build.passed ? "completed" : "failed",
30691
+ required: true
30692
+ });
30693
+ } else if (skip.skipBuild) {
30694
+ results.push({ name: "Install & build", status: "skipped", required: false });
30695
+ }
30696
+ enterStep(2);
30697
+ if (skip.skipAgent) {
30698
+ results.push({ name: "Coding agent", status: "skipped", required: false });
30699
+ } else {
30700
+ const { agent, agentModel, passed } = await runAgentSetup(uiDependencies);
30701
+ if (agent) {
30702
+ const stored = await dependencies.readCredentials();
30703
+ const { agentModel: _staleModel, ...storedWithoutModel } = stored;
30704
+ await dependencies.writeCredentials({
30705
+ ...storedWithoutModel,
30706
+ agent,
30707
+ ...agentModel ? { agentModel } : {}
30708
+ });
30709
+ }
30710
+ results.push({ name: "Coding agent", status: passed ? "completed" : "failed", required: true });
30711
+ }
30712
+ enterStep(3);
30713
+ if (skip.skipCredentials) {
30714
+ results.push({ name: "Contentful credentials", status: "skipped", required: false });
30715
+ } else {
30716
+ const credentials = await runCredentialsSetup(uiDependencies);
30717
+ results.push({
30718
+ name: "Contentful credentials",
30719
+ status: credentials.passed ? "completed" : "failed",
30720
+ required: false
30721
+ });
30722
+ }
30723
+ enterStep(4);
30724
+ if (skip.skipOptional) {
30725
+ results.push({ name: "Preferences", status: "skipped", required: false });
30726
+ } else {
30727
+ const { selected } = await runPreferenceSetupAction(uiDependencies, profilePath);
30728
+ results.push({
30729
+ name: "Preferences",
30730
+ status: selected.length > 0 ? "completed" : "skipped",
30731
+ required: false
30732
+ });
30733
+ }
30734
+ const exitCode = countRequiredFailures(results) === 0 ? 0 : 1;
30735
+ setPrompt(null);
30736
+ setEvents([]);
30737
+ setOutcome({ results, exitCode, restartRequired: false, runDoctor: false });
30738
+ const runDoctor2 = offerDoctor ? await request2((resolve27) => ({
30739
+ kind: "confirm",
30740
+ question: "Run experiences doctor now to verify your environment?",
30741
+ defaultYes: exitCode === 0,
30742
+ resolve: resolve27
30743
+ })) : false;
30744
+ setPrompt(null);
30745
+ const finalOutcome = { results, exitCode, restartRequired: false, runDoctor: runDoctor2 };
30746
+ setOutcome(finalOutcome);
30747
+ onComplete(finalOutcome);
30748
+ })();
30749
+ }, []);
30750
+ usePromptInput((chunk, key) => {
30751
+ const active = prompt;
30752
+ if (!active) return;
30753
+ if (active.kind === "select") return;
30754
+ const submit = (value) => {
30755
+ setPrompt(null);
30756
+ updateInput("");
30757
+ if (active.kind === "confirm") {
30758
+ const answer = value.trim().toLowerCase();
30759
+ active.resolve(answer === "" ? active.defaultYes : answer.startsWith("y"));
30760
+ } else {
30761
+ active.resolve(value.trim());
30762
+ }
30763
+ };
30764
+ if (key.return) {
30765
+ submit(inputValueRef.current);
30766
+ return;
30767
+ }
30768
+ if (key.backspace || key.delete) {
30769
+ updateInput(inputValueRef.current.slice(0, -1));
30770
+ return;
30771
+ }
30772
+ if (key.ctrl || key.meta || key.escape || key.tab) return;
30773
+ const { text, submitted } = splitPromptInput(chunk);
30774
+ if (!text && !submitted) return;
30775
+ const next = inputValueRef.current + text;
30776
+ if (submitted) submit(next);
30777
+ else updateInput(next);
30778
+ });
30779
+ const versionLabel = `v${version}`;
30780
+ const alignRight = shouldAlignVersionRight(version, columns);
30781
+ return /* @__PURE__ */ jsxs53(Box51, { flexDirection: "column", paddingX: 1, children: [
30782
+ /* @__PURE__ */ jsxs53(Box51, { children: [
30783
+ /* @__PURE__ */ jsx54(Text53, { bold: true, children: SETUP_TITLE }),
30784
+ alignRight ? /* @__PURE__ */ jsx54(Box51, { flexGrow: 1 }) : /* @__PURE__ */ jsx54(Text53, { children: " " }),
30785
+ /* @__PURE__ */ jsx54(Text53, { dimColor: true, children: versionLabel })
30786
+ ] }),
30787
+ /* @__PURE__ */ jsx54(Box51, { marginTop: 1, children: /* @__PURE__ */ jsx54(SetupStepper, { activeStep, columns }) }),
30788
+ outcome ? /* @__PURE__ */ jsx54(SetupSummary, { outcome, notice }) : /* @__PURE__ */ jsxs53(Fragment16, { children: [
30789
+ /* @__PURE__ */ jsx54(SetupEventLog, { events }),
30790
+ !prompt && /* @__PURE__ */ jsx54(Box51, { marginTop: 1, children: /* @__PURE__ */ jsxs53(Text53, { color: PALETTE.info, children: [
30791
+ "\u27F3 ",
30792
+ STEP_ACTIVITY[activeStep - 1],
30793
+ "\u2026"
30794
+ ] }) })
30795
+ ] }),
30796
+ prompt && (prompt.kind === "select" ? /* @__PURE__ */ jsx54(
30797
+ SetupChoiceList,
30798
+ {
30799
+ prompt,
30800
+ onResolve: (index) => {
30801
+ setPrompt(null);
30802
+ prompt.resolve(index);
30803
+ }
30804
+ }
30805
+ ) : /* @__PURE__ */ jsx54(SetupPrompt, { prompt, value: inputValue })),
30806
+ pageHelp && !outcome && /* @__PURE__ */ jsx54(Box51, { marginTop: 1, children: /* @__PURE__ */ jsx54(Text53, { dimColor: true, children: pageHelp }) })
30807
+ ] });
30808
+ }
30809
+ function SetupEventLog({ events }) {
30810
+ if (events.length === 0) return null;
30811
+ return /* @__PURE__ */ jsx54(Box51, { flexDirection: "column", marginTop: 1, children: events.map((event, index) => /* @__PURE__ */ jsx54(SetupEventLine, { event }, `${index}-${event.message}`)) });
30812
+ }
30813
+ function SetupEventLine({ event }) {
30814
+ if (event.message === "") return /* @__PURE__ */ jsx54(Text53, { children: " " });
30815
+ if (event.kind === "success") return /* @__PURE__ */ jsxs53(Text53, { color: PALETTE.success, children: [
30816
+ "\u2713 ",
30817
+ event.message
30818
+ ] });
30819
+ if (event.kind === "failure") return /* @__PURE__ */ jsxs53(Text53, { color: PALETTE.error, children: [
30820
+ "\u2717 ",
30821
+ event.message
30822
+ ] });
30823
+ if (event.kind === "warning") return /* @__PURE__ */ jsxs53(Text53, { color: PALETTE.warning, children: [
30824
+ "\u26A0 ",
30825
+ event.message
30826
+ ] });
30827
+ if (event.kind === "help") return /* @__PURE__ */ jsx54(Text53, { dimColor: true, children: event.message });
30828
+ if (event.kind === "value") return /* @__PURE__ */ jsxs53(Text53, { children: [
30829
+ " ",
30830
+ event.message
30831
+ ] });
30832
+ return /* @__PURE__ */ jsx54(Text53, { children: event.message });
30833
+ }
30834
+ function SetupChoiceList({
30835
+ prompt,
30836
+ onResolve
30837
+ }) {
30838
+ const options = [
30839
+ ...prompt.options.map((option, index) => ({
30840
+ label: option.description ? `${option.label} ${option.description}` : option.label,
30841
+ value: String(index)
30842
+ })),
30843
+ { label: "Skip", value: SKIP_VALUE }
30844
+ ];
30845
+ return /* @__PURE__ */ jsxs53(Box51, { flexDirection: "column", marginTop: 1, children: [
30846
+ /* @__PURE__ */ jsx54(Text53, { children: prompt.question }),
30847
+ /* @__PURE__ */ jsx54(Box51, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx54(
30848
+ Select,
30849
+ {
30850
+ options,
30851
+ visibleOptionCount: options.length,
30852
+ onChange: (value) => onResolve(value === SKIP_VALUE ? void 0 : Number(value))
30853
+ }
30854
+ ) }),
30855
+ /* @__PURE__ */ jsx54(Box51, { marginTop: 1, children: /* @__PURE__ */ jsx54(Text53, { dimColor: true, children: "\u2191\u2193 to move \xB7 Enter to select" }) })
30856
+ ] });
30857
+ }
30858
+ function SetupPrompt({ prompt, value }) {
30859
+ if (prompt.kind === "select") return /* @__PURE__ */ jsx54(Text53, { children: prompt.question });
30860
+ const display = prompt.kind === "secret" ? "\u2022".repeat(value.length) : value;
30861
+ const hint = prompt.kind === "confirm" ? prompt.defaultYes ? " [Y/n]" : " [y/N]" : "";
30862
+ return /* @__PURE__ */ jsxs53(Box51, { marginTop: 1, children: [
30863
+ /* @__PURE__ */ jsx54(Text53, { color: PALETTE.info, children: "\u203A " }),
30864
+ /* @__PURE__ */ jsxs53(Text53, { children: [
30865
+ prompt.question,
30866
+ hint
30867
+ ] }),
30868
+ /* @__PURE__ */ jsxs53(Text53, { children: [
30869
+ " ",
30870
+ display
30871
+ ] }),
30872
+ /* @__PURE__ */ jsx54(Text53, { color: PALETTE.info, children: "\u2588" })
30873
+ ] });
30874
+ }
30875
+ function SetupSummary({ outcome, notice }) {
30876
+ const requiredFailed = countRequiredFailures(outcome.results);
30877
+ return /* @__PURE__ */ jsxs53(Box51, { flexDirection: "column", marginTop: 1, children: [
30878
+ /* @__PURE__ */ jsx54(Text53, { bold: true, children: "Summary" }),
30879
+ outcome.results.map((result) => {
30880
+ if (result.status === "completed") {
30881
+ return /* @__PURE__ */ jsxs53(Text53, { color: PALETTE.success, children: [
30882
+ "\u2713 ",
30883
+ result.name
30884
+ ] }, result.name);
30885
+ }
30886
+ if (result.status === "skipped") {
30887
+ return /* @__PURE__ */ jsxs53(Text53, { dimColor: true, children: [
30888
+ "\u2013 ",
30889
+ result.name,
30890
+ " \u2014 skipped"
30891
+ ] }, result.name);
30892
+ }
30893
+ return /* @__PURE__ */ jsxs53(Text53, { color: result.required ? PALETTE.error : PALETTE.warning, children: [
30894
+ result.required ? "\u2717" : "\u26A0",
30895
+ " ",
30896
+ result.name,
30897
+ " \u2014 ",
30898
+ result.required ? "required" : "optional"
30899
+ ] }, result.name);
30900
+ }),
30901
+ notice && /* @__PURE__ */ jsx54(Box51, { marginTop: 1, children: /* @__PURE__ */ jsx54(Text53, { color: PALETTE.warning, children: notice }) }),
30902
+ /* @__PURE__ */ jsx54(Box51, { marginTop: 1, children: /* @__PURE__ */ jsx54(Text53, { bold: true, color: requiredFailed === 0 ? PALETTE.success : PALETTE.warning, children: formatSetupCompletionMessage(outcome.results) }) }),
30903
+ requiredFailed > 0 && /* @__PURE__ */ jsx54(Text53, { dimColor: true, children: "Complete the steps above, then re-run experiences setup." })
30904
+ ] });
30905
+ }
30906
+ var STEP_ACTIVITY, SKIP_VALUE;
30907
+ var init_SetupScreen = __esm({
30908
+ "packages/experience-design-system-cli/src/setup/tui/SetupScreen.tsx"() {
30909
+ "use strict";
30910
+ init_theme();
30911
+ init_layout();
30912
+ init_prompt_input();
30913
+ init_usePromptInput();
30914
+ init_SetupStepper();
30915
+ init_coding_agent();
30916
+ init_contentful();
30917
+ init_preferences();
30918
+ init_prerequisites();
30919
+ STEP_ACTIVITY = [
30920
+ "Checking prerequisites",
30921
+ "Configuring your coding agent",
30922
+ "Configuring Contentful credentials",
30923
+ "Configuring preferences"
30924
+ ];
30925
+ SKIP_VALUE = "\0skip";
30926
+ }
30927
+ });
30928
+
29824
30929
  // packages/experience-design-system-cli/src/program.ts
29825
30930
  import { spawn as spawn6 } from "node:child_process";
29826
30931
  import { fileURLToPath as fileURLToPath5 } from "node:url";
29827
- import { dirname as dirname14, join as join27, resolve as resolve26 } from "node:path";
29828
- import { readFileSync as readFileSync10 } from "node:fs";
30932
+ import { dirname as dirname14, join as join30, resolve as resolve26 } from "node:path";
30933
+ import { readFileSync as readFileSync11 } from "node:fs";
29829
30934
  import { Command } from "commander";
29830
30935
 
29831
30936
  // packages/experience-design-system-cli/src/analyze/command.ts
@@ -29905,7 +31010,7 @@ function AnalyzeView({ result, onExit }) {
29905
31010
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Session: " + result.sessionId }),
29906
31011
  /* @__PURE__ */ jsx2(Text2, { children: " " }),
29907
31012
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2500".repeat(70) }),
29908
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "Components" + (showScrollUp || showScrollDown ? " (" + (scrollOffset + 1) + "\u2013" + Math.min(scrollOffset + visibleCount, result.components.length) + " of " + result.components.length + ")" : "") }),
31013
+ /* @__PURE__ */ jsx2(Text2, { bold: true, children: "Components" + (showScrollUp || showScrollDown ? " (" + (scrollOffset + 1) + "\u2013" + Math.min(scrollOffset + visibleCount, result.components.length) + " of " + result.components.length + ")" : "") }),
29909
31014
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2500".repeat(70) }),
29910
31015
  /* @__PURE__ */ jsx2(Text2, { children: " " }),
29911
31016
  showScrollUp && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " \u25B2 scroll up" }),
@@ -30402,7 +31507,7 @@ function resolveProjectRoot(sessionId2, projectRootFlag) {
30402
31507
  }
30403
31508
  }
30404
31509
  async function selectBatch(agent, model, batch, total, verbose, skillPathOverride) {
30405
- const prompt2 = await buildPrompt({
31510
+ const prompt = await buildPrompt({
30406
31511
  skill: "select",
30407
31512
  mode: "autonomous",
30408
31513
  rawComponentsInline: JSON.stringify(
@@ -30420,7 +31525,7 @@ async function selectBatch(agent, model, batch, total, verbose, skillPathOverrid
30420
31525
  const result = await invoker.invoke({
30421
31526
  agent,
30422
31527
  model,
30423
- prompt: prompt2,
31528
+ prompt,
30424
31529
  timeoutMs: DEFAULT_TIMEOUT_MS,
30425
31530
  onOutput: (chunk) => formatter.push(chunk)
30426
31531
  });
@@ -30724,14 +31829,14 @@ function registerAnalyzeSelectAgentCommand(program) {
30724
31829
  return;
30725
31830
  }
30726
31831
  const first = selectionCandidates[0];
30727
- const prompt2 = await buildPrompt({
31832
+ const prompt = await buildPrompt({
30728
31833
  skill: "select",
30729
31834
  mode: "autonomous",
30730
31835
  rawComponentsInline: JSON.stringify([buildComponentData(first)], null, 2),
30731
31836
  outDir: process.cwd(),
30732
31837
  skillPathOverride: selectPromptPath ? resolve12(selectPromptPath) : void 0
30733
31838
  });
30734
- process.stdout.write(prompt2 + "\n");
31839
+ process.stdout.write(prompt + "\n");
30735
31840
  await exitWithAnalytics(0);
30736
31841
  return;
30737
31842
  }
@@ -31090,8 +32195,8 @@ async function resolveMapping(input) {
31090
32195
  const residueParents = input.components.map((c2) => c2.name).filter((n) => !coveredParents.has(n));
31091
32196
  const shouldRunAgent = (input.useAgent || input.forceAgent) && (input.forceAgent || residueParents.length > 0);
31092
32197
  if (shouldRunAgent) {
31093
- const prompt2 = input.buildPrompt ? input.buildPrompt(input.files, [...componentNames]) : defaultPrompt(input.files, [...componentNames], input.promptOverride);
31094
- const raw = await input.runAgentFn({ prompt: prompt2, files: input.files });
32198
+ const prompt = input.buildPrompt ? input.buildPrompt(input.files, [...componentNames]) : defaultPrompt(input.files, [...componentNames], input.promptOverride);
32199
+ const raw = await input.runAgentFn({ prompt, files: input.files });
31095
32200
  const parsed = parseMapEdges(raw, { componentNames });
31096
32201
  collected.push(...parsed.edges);
31097
32202
  agentWarnings.push(...parsed.warnings);
@@ -31571,9 +32676,9 @@ async function resolveViaAgentParser(input) {
31571
32676
  }
31572
32677
  return out;
31573
32678
  };
31574
- const attempt = async (prompt2) => {
32679
+ const attempt = async (prompt) => {
31575
32680
  input.onPhase?.("authoring");
31576
- const raw = await input.runAgentFn({ prompt: prompt2 });
32681
+ const raw = await input.runAgentFn({ prompt });
31577
32682
  const source = extractParserSource(raw);
31578
32683
  if (source === null)
31579
32684
  return { error: "agent output had no parser-shaped code block (expected `export default function (ctx) {\u2026}`)" };
@@ -32032,10 +33137,10 @@ function registerAnalyzeCommand(program) {
32032
33137
  const componentNameSet = new Set(validatedComponents.map((c2) => c2.name));
32033
33138
  const cacheVersion = await getCliCacheVersion();
32034
33139
  let lastAgentExitCode = 0;
32035
- const spawnAgent = async (prompt2) => {
33140
+ const spawnAgent = async (prompt) => {
32036
33141
  const res = await runAgent({
32037
33142
  agent: resolverAgent,
32038
- prompt: prompt2,
33143
+ prompt,
32039
33144
  timeoutMs: 12e4,
32040
33145
  promptViaStdin: true,
32041
33146
  onDebugEvent: (name, payload) => getDebugLogger().event("agent", name, payload)
@@ -32087,7 +33192,7 @@ function registerAnalyzeCommand(program) {
32087
33192
  files: promptFiles,
32088
33193
  runtimeFiles,
32089
33194
  componentNames: componentNameSet,
32090
- runAgentFn: ({ prompt: prompt2 }) => spawnAgent(prompt2),
33195
+ runAgentFn: ({ prompt }) => spawnAgent(prompt),
32091
33196
  ...compositionPrompt ? { instructionOverride: compositionPrompt } : {},
32092
33197
  onPhase: (phase) => emitCompositionProgress(phase),
32093
33198
  // The candidate filter selected these files because they carry
@@ -32131,7 +33236,7 @@ function registerAnalyzeCommand(program) {
32131
33236
  forceAgent: sources.forceAgent && useEdgeEmission,
32132
33237
  files: promptFiles,
32133
33238
  ...compositionPrompt ? { promptOverride: compositionPrompt } : {},
32134
- runAgentFn: async ({ prompt: prompt2 }) => {
33239
+ runAgentFn: async ({ prompt }) => {
32135
33240
  if (!opts.compositionRefresh) {
32136
33241
  const cached = lookupCompositionCache(db, agentCacheKey, cacheVersion);
32137
33242
  if (cached !== null) {
@@ -32140,7 +33245,7 @@ function registerAnalyzeCommand(program) {
32140
33245
  }
32141
33246
  }
32142
33247
  emitCompositionProgress(`agent:${resolverAgent}`);
32143
- const stdout = await spawnAgent(prompt2);
33248
+ const stdout = await spawnAgent(prompt);
32144
33249
  if (lastAgentExitCode === 0) {
32145
33250
  storeCompositionCache(db, agentCacheKey, cacheVersion, stdout);
32146
33251
  }
@@ -32581,7 +33686,7 @@ async function runOneComponent(agent, model, db, sessionId2, component, tokensIn
32581
33686
  component.props.map((p) => p.name),
32582
33687
  component.props.map((p) => p.type)
32583
33688
  );
32584
- const prompt2 = await buildPrompt({
33689
+ const prompt = await buildPrompt({
32585
33690
  skill: "components",
32586
33691
  mode: "autonomous",
32587
33692
  rawComponentsInline,
@@ -32603,7 +33708,7 @@ async function runOneComponent(agent, model, db, sessionId2, component, tokensIn
32603
33708
  const result = await invoker2.invoke({
32604
33709
  agent,
32605
33710
  model,
32606
- prompt: prompt2,
33711
+ prompt,
32607
33712
  timeoutMs: DEFAULT_TIMEOUT_MS3,
32608
33713
  onOutput: (chunk) => formatter.push(chunk)
32609
33714
  });
@@ -32826,7 +33931,7 @@ async function runGenerateSkill(skill, opts, verbose = false) {
32826
33931
  sampleComponent.props.map((p) => p.name),
32827
33932
  sampleComponent.props.map((p) => p.type)
32828
33933
  ) : void 0;
32829
- const prompt2 = await buildPrompt({
33934
+ const prompt = await buildPrompt({
32830
33935
  skill,
32831
33936
  mode: "autonomous",
32832
33937
  rawComponentsInline: sampleInline ?? rawTokensInline,
@@ -32838,7 +33943,7 @@ async function runGenerateSkill(skill, opts, verbose = false) {
32838
33943
  componentSourceRefs: sampleSourceRef ? [sampleSourceRef] : void 0,
32839
33944
  skillPathOverride: generatePromptPath
32840
33945
  });
32841
- process.stdout.write(prompt2 + "\n");
33946
+ process.stdout.write(prompt + "\n");
32842
33947
  await exitWithAnalytics(0);
32843
33948
  }
32844
33949
  const binary = resolveBinary(agent);
@@ -32958,7 +34063,7 @@ session=${sessionId2 ?? ""}
32958
34063
  return;
32959
34064
  }
32960
34065
  }
32961
- const prompt2 = await buildPrompt({
34066
+ const prompt = await buildPrompt({
32962
34067
  skill,
32963
34068
  mode: "autonomous",
32964
34069
  rawTokensInline,
@@ -32970,7 +34075,7 @@ session=${sessionId2 ?? ""}
32970
34075
  const result = await invoker2.invoke({
32971
34076
  agent,
32972
34077
  model,
32973
- prompt: prompt2,
34078
+ prompt,
32974
34079
  timeoutMs: DEFAULT_TIMEOUT_MS3 * 5
32975
34080
  });
32976
34081
  if (result.timedOut) {
@@ -34322,7 +35427,7 @@ ${defaultDiagnostics.map((diagnostic) => ` ${diagnostic}`).join("\n")}
34322
35427
  const tokenTree = rebuildDTCGTree(groups, tokens);
34323
35428
  const componentSourceRefs = await loadComponentSourceRefs(db, sessionId2);
34324
35429
  if (opts.printPrompt) {
34325
- const prompt3 = await buildPrompt({
35430
+ const prompt2 = await buildPrompt({
34326
35431
  skill: "map-tokens",
34327
35432
  mode: "autonomous",
34328
35433
  generatedCdf,
@@ -34330,7 +35435,7 @@ ${defaultDiagnostics.map((diagnostic) => ` ${diagnostic}`).join("\n")}
34330
35435
  componentSourceRefs,
34331
35436
  outDir: process.cwd()
34332
35437
  });
34333
- process.stdout.write(prompt3 + "\n");
35438
+ process.stdout.write(prompt2 + "\n");
34334
35439
  await exitWithAnalytics(0);
34335
35440
  return;
34336
35441
  }
@@ -34367,7 +35472,7 @@ Install it, choose another agent with --agent, or use --print-prompt to run the
34367
35472
  );
34368
35473
  }
34369
35474
  const stepId = createStep(db, sessionId2, "map tokens", { agent, model: model ?? "" });
34370
- const prompt2 = await buildPrompt({
35475
+ const prompt = await buildPrompt({
34371
35476
  skill: "map-tokens",
34372
35477
  mode: "autonomous",
34373
35478
  generatedCdf,
@@ -34376,7 +35481,7 @@ Install it, choose another agent with --agent, or use --print-prompt to run the
34376
35481
  outDir: process.cwd()
34377
35482
  });
34378
35483
  const invoker3 = createLocalCliAgentInvoker();
34379
- const result = await invoker3.invoke({ agent, model, prompt: prompt2, timeoutMs: DEFAULT_TIMEOUT_MS4 });
35484
+ const result = await invoker3.invoke({ agent, model, prompt, timeoutMs: DEFAULT_TIMEOUT_MS4 });
34380
35485
  if (result.timedOut || result.exitCode !== 0) {
34381
35486
  const error = result.timedOut ? `timed out after ${DEFAULT_TIMEOUT_MS4 / 6e4} minutes` : describeAgentFailure(result);
34382
35487
  updateStep(db, stepId, "failed", {}, error);
@@ -35285,7 +36390,7 @@ async function replayRun(opts) {
35285
36390
  if (!opts.interactive) {
35286
36391
  throw new Error(MISSING_CREDS_ERROR);
35287
36392
  }
35288
- const prompt2 = opts.promptForCredentials ?? (async (need) => {
36393
+ const prompt = opts.promptForCredentials ?? (async (need) => {
35289
36394
  const { promptForPushCredentials: promptForPushCredentials2 } = await Promise.resolve().then(() => (init_push_creds_prompt(), push_creds_prompt_exports));
35290
36395
  return promptForPushCredentials2({
35291
36396
  ...need.spaceId ? { initialSpaceId: need.spaceId } : {},
@@ -35295,7 +36400,7 @@ async function replayRun(opts) {
35295
36400
  summary: `Push run ${run.id} \u2192 enter credentials. Defaults reflect the run record and your saved setup.`
35296
36401
  });
35297
36402
  });
35298
- const collected = await prompt2({
36403
+ const collected = await prompt({
35299
36404
  spaceId,
35300
36405
  environmentId,
35301
36406
  cmaToken,
@@ -35945,54 +37050,18 @@ function registerImportCommand(program) {
35945
37050
  );
35946
37051
  }
35947
37052
 
35948
- // packages/experience-design-system-cli/src/setup/command.ts
35949
- init_credentials_store();
35950
- import { execFile as execFile5, spawn as spawn5 } from "node:child_process";
35951
- import { appendFile as appendFile2, readFile as readFile27, access as access10 } from "node:fs/promises";
35952
- import { join as join26 } from "node:path";
35953
- import { homedir as homedir9 } from "node:os";
35954
- import { fileURLToPath as fileURLToPath4 } from "node:url";
35955
- import { createInterface } from "node:readline";
35956
- import { promisify as promisify2 } from "node:util";
35957
-
35958
- // packages/experience-design-system-cli/src/setup/auto-filter-prompt.ts
35959
- async function promptAutoFilterPreference(ask, current) {
35960
- const defaultValue = current ?? true;
35961
- const hint = defaultValue ? "[Y/n]" : "[y/N]";
35962
- const answer = (await ask(` Enable AI auto-filter by default? ${hint} `)).trim().toLowerCase();
35963
- if (answer === "") return defaultValue;
35964
- if (answer.startsWith("y")) return true;
35965
- if (answer.startsWith("n")) return false;
35966
- return defaultValue;
35967
- }
35968
-
35969
- // packages/experience-design-system-cli/src/setup/debug-mode-prompt.ts
35970
- async function promptDebugModePreference(ask, current) {
35971
- const defaultValue = current ?? false;
35972
- const hint = defaultValue ? "[Y/n]" : "[y/N]";
35973
- const answer = (await ask(` Enable debug logging by default? ${hint} `)).trim().toLowerCase();
35974
- if (answer === "") return defaultValue;
35975
- if (answer.startsWith("y")) return true;
35976
- if (answer.startsWith("n")) return false;
35977
- return defaultValue;
35978
- }
37053
+ // packages/experience-design-system-cli/src/doctor/command.ts
37054
+ init_cli_path();
35979
37055
 
35980
- // packages/experience-design-system-cli/src/setup/analytics-prompt.ts
35981
- async function promptAnalyticsPreference(ask, current) {
35982
- const defaultValue = current ?? false;
35983
- const hint = defaultValue ? "[Y/n]" : "[y/N]";
35984
- const answer = (await ask(` Disable anonymous usage analytics? ${hint} `)).trim().toLowerCase();
35985
- if (answer === "") return defaultValue;
35986
- if (answer.startsWith("y")) return true;
35987
- if (answer.startsWith("n")) return false;
35988
- return defaultValue;
35989
- }
37056
+ // packages/experience-design-system-cli/src/doctor/checks.ts
37057
+ init_credentials_store();
37058
+ init_agent_definitions();
37059
+ init_shell();
37060
+ init_checks();
37061
+ import { homedir as homedir10 } from "node:os";
37062
+ import { join as join28 } from "node:path";
35990
37063
 
35991
- // packages/experience-design-system-cli/src/setup/command.ts
35992
- init_host_utils();
35993
- init_cli_path();
35994
- var execFileAsync2 = promisify2(execFile5);
35995
- var REQUIRED_NODE_MAJOR = 24;
37064
+ // packages/experience-design-system-cli/src/doctor/report.ts
35996
37065
  function ok(msg) {
35997
37066
  process.stdout.write(` \x1B[32m\u2713\x1B[0m ${msg}
35998
37067
  `);
@@ -36009,618 +37078,56 @@ function info(msg) {
36009
37078
  process.stdout.write(` ${msg}
36010
37079
  `);
36011
37080
  }
36012
- function section(title, tag) {
36013
- const tagStr = tag ? tag === "[required]" ? ` \x1B[31m[required]\x1B[0m` : ` \x1B[2m[optional]\x1B[0m` : "";
37081
+ function section(title) {
36014
37082
  process.stdout.write(`
36015
- \x1B[1m${title}\x1B[0m${tagStr}
36016
- `);
36017
- }
36018
- function dim(msg) {
36019
- process.stdout.write(`\x1B[2m${msg}\x1B[0m
36020
- `);
36021
- }
36022
- function isInteractivePromptSession() {
36023
- return !!(process.stdin.isTTY && process.stdout.isTTY);
36024
- }
36025
- function prompt(question) {
36026
- if (!isInteractivePromptSession()) {
36027
- return Promise.resolve("");
36028
- }
36029
- const rl = createInterface({ input: process.stdin, output: process.stdout });
36030
- return new Promise((resolve27) => {
36031
- rl.question(question, (answer) => {
36032
- rl.close();
36033
- resolve27(answer.trim());
36034
- });
36035
- });
36036
- }
36037
- function promptSecret(question) {
36038
- if (!isInteractivePromptSession()) {
36039
- return Promise.resolve("");
36040
- }
36041
- return new Promise((resolve27) => {
36042
- const rl = createInterface({
36043
- input: process.stdin,
36044
- output: process.stdout,
36045
- terminal: process.stdin.isTTY
36046
- });
36047
- let value = "";
36048
- process.stdout.write(question);
36049
- let origWrite = null;
36050
- if (process.stdin.isTTY) {
36051
- origWrite = rl.output.write.bind(
36052
- rl.output
36053
- );
36054
- rl.output.write = (s) => {
36055
- if (s === "\r\n" || s === "\n" || s === "\r") origWrite(s);
36056
- };
36057
- }
36058
- rl.on("line", (line) => {
36059
- value = line;
36060
- rl.close();
36061
- });
36062
- rl.once("close", () => {
36063
- if (origWrite) {
36064
- rl.output.write = origWrite;
36065
- }
36066
- if (!process.stdin.isTTY) process.stdout.write("\n");
36067
- process.stdin.resume();
36068
- resolve27(value);
36069
- });
36070
- });
36071
- }
36072
- async function confirm(question, defaultYes = true) {
36073
- if (!isInteractivePromptSession()) {
36074
- return false;
36075
- }
36076
- const hint = defaultYes ? "[Y/n]" : "[y/N]";
36077
- const answer = await prompt(` ${question} ${hint} `);
36078
- if (!answer) return defaultYes;
36079
- return answer.toLowerCase().startsWith("y");
36080
- }
36081
- async function binaryExists(name) {
36082
- try {
36083
- await execFileAsync2("which", [name]);
36084
- return true;
36085
- } catch {
36086
- return false;
36087
- }
36088
- }
36089
- function runSpawn(cmd, args, opts = {}) {
36090
- return new Promise((resolve27) => {
36091
- const child = spawn5(cmd, args, {
36092
- cwd: opts.cwd,
36093
- env: opts.env ?? process.env,
36094
- stdio: ["ignore", "pipe", "pipe"]
36095
- });
36096
- let settled = false;
36097
- let stdout = "";
36098
- let stderr = "";
36099
- child.on("error", (err) => {
36100
- if (!settled) {
36101
- settled = true;
36102
- resolve27({ exitCode: 1, stdout: "", stderr: err.message });
36103
- }
36104
- });
36105
- child.stdout.on("data", (d) => {
36106
- stdout += String(d);
36107
- });
36108
- child.stderr.on("data", (d) => {
36109
- stderr += String(d);
36110
- });
36111
- child.on("exit", (code) => {
36112
- if (!settled) {
36113
- settled = true;
36114
- resolve27({ exitCode: code ?? 1, stdout, stderr });
36115
- }
36116
- });
36117
- });
36118
- }
36119
- async function detectShellProfile() {
36120
- const shell = process.env["SHELL"] ?? "";
36121
- const home = homedir9();
36122
- if (shell.includes("zsh")) {
36123
- return join26(home, ".zshrc");
36124
- }
36125
- if (shell.includes("bash")) {
36126
- const bashProfile = join26(home, ".bash_profile");
36127
- const exists = await access10(bashProfile).then(() => true).catch(() => false);
36128
- return exists ? bashProfile : join26(home, ".bashrc");
36129
- }
36130
- if (shell.includes("fish")) {
36131
- return join26(home, ".config", "fish", "config.fish");
36132
- }
36133
- return join26(home, ".profile");
36134
- }
36135
- async function profileContains(profilePath, str) {
36136
- try {
36137
- const content = await readFile27(profilePath, "utf8");
36138
- return content.includes(str);
36139
- } catch {
36140
- return false;
36141
- }
36142
- }
36143
- async function appendToProfile(profilePath, lines) {
36144
- await appendFile2(profilePath, `
36145
- ${lines}
36146
- `, "utf8");
36147
- }
36148
- async function setupNode() {
36149
- section("Step 1: Node.js", "[required]");
36150
- const current = process.versions.node;
36151
- const major = parseInt(current.split(".")[0], 10);
36152
- if (major >= REQUIRED_NODE_MAJOR) {
36153
- ok(`Node.js v${current} \u2014 already good`);
36154
- return true;
36155
- }
36156
- fail(`Node.js v${current} \u2014 need v${REQUIRED_NODE_MAJOR}+`);
36157
- info("");
36158
- const hasNvm = await binaryExists("nvm") || await access10(join26(homedir9(), ".nvm", "nvm.sh")).then(() => true).catch(() => false);
36159
- const hasFnm = await binaryExists("fnm");
36160
- if (hasNvm) {
36161
- info(`nvm detected. Will run: nvm install ${REQUIRED_NODE_MAJOR} && nvm use ${REQUIRED_NODE_MAJOR}`);
36162
- const go = await confirm(`Install and switch to Node ${REQUIRED_NODE_MAJOR} via nvm?`);
36163
- if (!go) {
36164
- warn(`Skipped. Re-run experiences setup after switching to Node ${REQUIRED_NODE_MAJOR}.`);
36165
- return false;
36166
- }
36167
- const nvmScript = join26(homedir9(), ".nvm", "nvm.sh");
36168
- const result = await runSpawn("bash", [
36169
- "-c",
36170
- `source "${nvmScript}" && nvm install ${REQUIRED_NODE_MAJOR} && nvm alias default ${REQUIRED_NODE_MAJOR}`
36171
- ]);
36172
- if (result.exitCode !== 0) {
36173
- fail("nvm install failed");
36174
- info(result.stderr.trim().split("\n").slice(0, 5).join("\n"));
36175
- info(`Run manually: nvm install ${REQUIRED_NODE_MAJOR} && nvm use ${REQUIRED_NODE_MAJOR}`);
36176
- return false;
36177
- }
36178
- ok(`Node ${REQUIRED_NODE_MAJOR} installed via nvm. Re-run experiences setup in a fresh shell to pick it up.`);
36179
- return false;
36180
- }
36181
- if (hasFnm) {
36182
- info(`fnm detected. Will run: fnm install ${REQUIRED_NODE_MAJOR} && fnm use ${REQUIRED_NODE_MAJOR}`);
36183
- const go = await confirm(`Install and switch to Node ${REQUIRED_NODE_MAJOR} via fnm?`);
36184
- if (!go) {
36185
- warn(`Skipped. Re-run experiences setup after switching to Node ${REQUIRED_NODE_MAJOR}.`);
36186
- return false;
36187
- }
36188
- const result = await runSpawn("fnm", ["install", String(REQUIRED_NODE_MAJOR)]);
36189
- if (result.exitCode !== 0) {
36190
- fail("fnm install failed");
36191
- info(`Run manually: fnm install ${REQUIRED_NODE_MAJOR} && fnm use ${REQUIRED_NODE_MAJOR}`);
36192
- return false;
36193
- }
36194
- const useResult = await runSpawn("fnm", ["use", String(REQUIRED_NODE_MAJOR)]);
36195
- if (useResult.exitCode !== 0) {
36196
- warn(`fnm use ${REQUIRED_NODE_MAJOR} failed \u2014 node installed but not activated`);
36197
- info(`Run manually: fnm use ${REQUIRED_NODE_MAJOR} && fnm default ${REQUIRED_NODE_MAJOR}`);
36198
- } else {
36199
- const defaultResult = await runSpawn("fnm", ["default", String(REQUIRED_NODE_MAJOR)]);
36200
- if (defaultResult.exitCode !== 0) {
36201
- warn(`fnm default ${REQUIRED_NODE_MAJOR} failed \u2014 version won't persist across new shells`);
36202
- info(`Run manually: fnm default ${REQUIRED_NODE_MAJOR}`);
36203
- }
36204
- }
36205
- ok(`Node ${REQUIRED_NODE_MAJOR} installed via fnm. Re-run experiences setup in a fresh shell.`);
36206
- return false;
36207
- }
36208
- info("No Node version manager detected (nvm or fnm).");
36209
- const installNvm = await confirm("Install nvm now? (recommended)");
36210
- if (installNvm) {
36211
- info("Running nvm install script...");
36212
- const result = await runSpawn("bash", [
36213
- "-c",
36214
- "curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash"
36215
- ]);
36216
- if (result.exitCode !== 0) {
36217
- fail("nvm install failed");
36218
- info("Install manually: https://github.com/nvm-sh/nvm#installing-and-updating");
36219
- return false;
36220
- }
36221
- ok("nvm installed. Open a new shell, then re-run experiences setup.");
36222
- return false;
36223
- }
36224
- info(`Install Node ${REQUIRED_NODE_MAJOR} manually from https://nodejs.org`);
36225
- return false;
36226
- }
36227
- async function setupPnpm() {
36228
- section("Step 2: pnpm", "[required]");
36229
- if (await binaryExists("pnpm")) {
36230
- const v = await runSpawn("pnpm", ["--version"]);
36231
- ok(`pnpm v${v.stdout.trim()} \u2014 already installed`);
36232
- return true;
36233
- }
36234
- fail("pnpm not found");
36235
- info("");
36236
- const hasCorecpack = await binaryExists("corepack");
36237
- if (hasCorecpack) {
36238
- info("Will run: corepack enable && corepack prepare pnpm@latest --activate");
36239
- const go2 = await confirm("Install pnpm via corepack?");
36240
- if (go2) {
36241
- const r1 = await runSpawn("corepack", ["enable"]);
36242
- const r2 = r1.exitCode === 0 ? await runSpawn("corepack", ["prepare", "pnpm@latest", "--activate"]) : r1;
36243
- if (r2.exitCode !== 0) {
36244
- fail("corepack install failed");
36245
- info("Try: npm install -g pnpm");
36246
- return false;
36247
- }
36248
- ok("pnpm installed via corepack");
36249
- return true;
36250
- }
36251
- }
36252
- info("Will run: npm install -g pnpm");
36253
- const go = await confirm("Install pnpm via npm?");
36254
- if (!go) {
36255
- warn("Skipped. Install pnpm manually: npm install -g pnpm");
36256
- return false;
36257
- }
36258
- const result = await runSpawn("npm", ["install", "-g", "pnpm"]);
36259
- if (result.exitCode !== 0) {
36260
- fail("npm install -g pnpm failed");
36261
- info(result.stderr.trim().split("\n").slice(0, 5).join("\n"));
36262
- return false;
36263
- }
36264
- ok("pnpm installed");
36265
- return true;
36266
- }
36267
- async function setupBuild(repoRoot) {
36268
- section("Step 3: Install dependencies & build", "[required]");
36269
- info("Running pnpm install...");
36270
- const installResult = await runSpawn("pnpm", ["install", "--frozen-lockfile"], { cwd: repoRoot });
36271
- if (installResult.exitCode !== 0) {
36272
- fail("pnpm install failed");
36273
- const errLines = installResult.stderr.trim().split("\n").slice(0, 8);
36274
- for (const line of errLines) info(line);
36275
- info("");
36276
- info("Try: pnpm install (without --frozen-lockfile) to update the lockfile");
36277
- return false;
36278
- }
36279
- ok("Dependencies installed");
36280
- info("Building CLI...");
36281
- const buildResult = await runSpawn("pnpm", ["--filter", "@contentful/experience-design-system-cli", "run", "build"], {
36282
- cwd: repoRoot
36283
- });
36284
- if (buildResult.exitCode !== 0) {
36285
- fail("Build failed");
36286
- const errLines = buildResult.stderr.trim().split("\n").slice(0, 10);
36287
- for (const line of errLines) info(line);
36288
- return false;
36289
- }
36290
- ok("CLI built successfully");
36291
- return true;
36292
- }
36293
- var AGENT_DEFS = [
36294
- { name: "Claude Code", binary: "claude", installHint: "npm install -g @anthropic-ai/claude-code && claude login" },
36295
- { name: "OpenAI Codex", binary: "codex", installHint: "npm install -g @openai/codex (requires OPENAI_API_KEY)" },
36296
- { name: "OpenCode", binary: "opencode", installHint: "npm install -g opencode-ai && opencode auth" },
36297
- { name: "GitHub Copilot", binary: "copilot", installHint: "npm install -g @github/copilot && copilot" }
36298
- ];
36299
- function pick(items, defaultIdx = 0) {
36300
- items.forEach((item, i) => {
36301
- const num = `\x1B[1m[${i + 1}]\x1B[0m`;
36302
- const desc = item.description ? ` \x1B[2m${item.description}\x1B[0m` : "";
36303
- const def = i === defaultIdx ? ` \x1B[2m(default)\x1B[0m` : "";
36304
- process.stdout.write(` ${num} ${item.label}${desc}${def}
36305
- `);
36306
- });
36307
- process.stdout.write(` \x1B[2m[s] Skip\x1B[0m
37083
+ \x1B[1m${title}\x1B[0m
36308
37084
  `);
36309
37085
  }
36310
- async function promptCodexModel(ask = prompt) {
36311
- if (process.env["OPENAI_API_KEY"]) return void 0;
36312
- info("");
36313
- process.stdout.write(` \x1B[33m\u26A0\x1B[0m No OPENAI_API_KEY \u2014 using ChatGPT account authentication.
36314
- `);
36315
- info(" Tip: run \x1B[1mcodex\x1B[0m then type \x1B[1m/model\x1B[0m to browse the models available to your account.");
36316
- const model = await ask(" Model name (optional - press Enter for Codex default): ");
36317
- const trimmed = model.trim();
36318
- return trimmed || void 0;
36319
- }
36320
- async function setupAgent() {
36321
- section("Step 4: Coding agent (claude, codex, opencode, or copilot)", "[required]");
36322
- info("experiences import uses a coding agent to generate component definitions.");
36323
- info("");
36324
- const found = (await Promise.all(AGENT_DEFS.map(async (a) => await binaryExists(a.binary) ? a : null))).filter(
36325
- (a) => a !== null
36326
- );
36327
- if (found.length === 1) {
36328
- ok(`${found[0].name} (${found[0].binary}) found`);
36329
- const agentModel = found[0].binary === "codex" ? await promptCodexModel() : void 0;
36330
- return { agent: found[0].binary, agentModel };
36331
- }
36332
- if (found.length > 1) {
36333
- info("Multiple coding agents found. Choose one to use as the default:");
36334
- info("");
36335
- pick(found.map((a) => ({ label: `${a.name}`, description: a.binary })));
36336
- info("");
36337
- const choice2 = await prompt(" \x1B[2m\u203A\x1B[0m Your choice [1]: ");
36338
- if (choice2.toLowerCase() === "s") {
36339
- warn("Skipped. Install a coding agent before running experiences import.");
36340
- return { agent: void 0, agentModel: void 0 };
36341
- }
36342
- const parsed = choice2 === "" ? 1 : parseInt(choice2, 10);
36343
- const idx = Number.isNaN(parsed) || parsed < 1 || parsed > found.length ? 0 : parsed - 1;
36344
- const selected = found[idx];
36345
- const agentModel = selected.binary === "codex" ? await promptCodexModel() : void 0;
36346
- return { agent: selected.binary, agentModel };
36347
- }
36348
- warn("No coding agent found on PATH");
36349
- info("");
36350
- info("Choose one to install:");
36351
- info("");
36352
- pick([
36353
- { label: "Claude Code", description: "npm install -g @anthropic-ai/claude-code" },
36354
- { label: "OpenAI Codex", description: "npm install -g @openai/codex" },
36355
- { label: "OpenCode", description: "npm install -g opencode-ai" }
36356
- ]);
36357
- info("");
36358
- const choice = await prompt(" Your choice: ");
36359
- if (choice === "1" || choice === "") {
36360
- const r = await runSpawn("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
36361
- if (r.exitCode !== 0) {
36362
- fail("Install failed");
36363
- info(r.stderr.trim().split("\n").slice(0, 5).join("\n"));
36364
- return { agent: void 0, agentModel: void 0 };
36365
- }
36366
- if (!await binaryExists("claude")) {
36367
- fail("claude binary not found on PATH after install \u2014 check your npm global bin directory");
36368
- return { agent: void 0, agentModel: void 0 };
36369
- }
36370
- ok("Claude Code installed");
36371
- info("");
36372
- info("Next: run `claude login` to authenticate (browser OAuth).");
36373
- info("Or set ANTHROPIC_API_KEY in your shell profile.");
36374
- return { agent: "claude", agentModel: void 0 };
36375
- }
36376
- if (choice === "2") {
36377
- const r = await runSpawn("npm", ["install", "-g", "@openai/codex"]);
36378
- if (r.exitCode !== 0) {
36379
- fail("Install failed");
36380
- return { agent: void 0, agentModel: void 0 };
36381
- }
36382
- if (!await binaryExists("codex")) {
36383
- fail("codex binary not found on PATH after install \u2014 check your npm global bin directory");
36384
- return { agent: void 0, agentModel: void 0 };
36385
- }
36386
- ok("OpenAI Codex installed");
36387
- const agentModel = await promptCodexModel();
36388
- return { agent: "codex", agentModel };
36389
- }
36390
- if (choice === "3") {
36391
- const r = await runSpawn("npm", ["install", "-g", "opencode-ai"]);
36392
- if (r.exitCode !== 0) {
36393
- fail("Install failed");
36394
- return { agent: void 0, agentModel: void 0 };
36395
- }
36396
- if (!await binaryExists("opencode")) {
36397
- fail("opencode binary not found on PATH after install \u2014 check your npm global bin directory");
36398
- return { agent: void 0, agentModel: void 0 };
36399
- }
36400
- ok("OpenCode installed");
36401
- info("Run `opencode auth` to configure your provider.");
36402
- return { agent: "opencode", agentModel: void 0 };
36403
- }
36404
- warn("Skipped. Install a coding agent before running experiences import.");
36405
- return { agent: void 0, agentModel: void 0 };
37086
+ function infoStderr(stderr, limit) {
37087
+ for (const line of stderr.trim().split("\n").slice(0, limit)) info(line);
36406
37088
  }
36407
- async function setupContentfulCredentials() {
36408
- section("Step 5: Contentful credentials", "[optional]");
36409
- info(`Saved to ${experiencesCredentialsPath()} \u2014 loaded automatically by experiences import.`);
36410
- info("");
36411
- const envShadowing = [
36412
- process.env["CONTENTFUL_SPACE_ID"] ? "CONTENTFUL_SPACE_ID" : null,
36413
- process.env["CONTENTFUL_ENVIRONMENT_ID"] ? "CONTENTFUL_ENVIRONMENT_ID" : null,
36414
- process.env["CONTENTFUL_MANAGEMENT_TOKEN"] ? "CONTENTFUL_MANAGEMENT_TOKEN" : null,
36415
- process.env["EDS_HOST"] ? "EDS_HOST" : null
36416
- ].filter((v) => !!v);
36417
- if (envShadowing.length > 0) {
36418
- warn(
36419
- `Env vars set: ${envShadowing.join(", ")}. Values saved here take precedence; env vars only apply where disk is empty.`
36420
- );
36421
- info("");
36422
- }
36423
- const stored = await readExperiencesCredentials();
36424
- const currentSpace = stored.spaceId;
36425
- const currentEnv = stored.environmentId;
36426
- const currentToken = stored.cmaToken;
36427
- const storedHost = stored.host;
36428
- const currentHost = storedHost ?? DEFAULT_CONFIGURED_HOST;
36429
- const hasAny = !!(currentSpace || currentEnv || currentToken);
36430
- if (hasAny) {
36431
- info("Current values:");
36432
- if (currentSpace) {
36433
- ok(`Space ID ${currentSpace}`);
36434
- } else {
36435
- warn("Space ID (not set)");
36436
- }
36437
- if (currentEnv) {
36438
- ok(`Environment ID ${currentEnv}`);
36439
- } else {
36440
- warn("Environment ID (not set)");
36441
- }
36442
- if (currentToken) {
36443
- ok(`CMA Token ${"\u2022".repeat(Math.min(currentToken.length, 8))}...`);
36444
- } else {
36445
- warn("CMA Token (not set)");
36446
- }
36447
- ok(`API Host ${currentHost}`);
36448
- info("");
36449
- }
36450
- const allSet = !!(currentSpace && currentEnv && currentToken);
36451
- const doUpdate = await confirm(hasAny ? "Update credentials?" : "Configure Contentful credentials?", !allSet);
36452
- if (!doUpdate) {
36453
- if (allSet) {
36454
- ok("Credentials already configured \u2014 no changes made");
36455
- } else {
36456
- warn("Skipped. experiences import will prompt for credentials interactively.");
36457
- }
37089
+
37090
+ // packages/experience-design-system-cli/src/doctor/checks.ts
37091
+ async function reportNode() {
37092
+ section("Checking Node.js version");
37093
+ const check = checkNodeVersion();
37094
+ if (check.passed) {
37095
+ ok(`Node.js v${check.version}`);
36458
37096
  return true;
36459
37097
  }
37098
+ fail(`Node.js v${check.version} \u2014 need v${check.required}+`);
36460
37099
  info("");
36461
- info("Get your CMA token: Contentful web app \u2192 Settings \u2192 API keys \u2192 Content management tokens");
36462
- info("");
36463
- const spaceIdInput = await prompt(` Space ID${currentSpace ? ` [${currentSpace}]` : ""}: `);
36464
- const spaceId = spaceIdInput || currentSpace;
36465
- const envIdInput = await prompt(` Environment ID [${currentEnv || "master"}]: `);
36466
- const environmentId = envIdInput || currentEnv || "master";
36467
- const tokenInput = await promptSecret(
36468
- ` CMA token${currentToken ? " [press Enter to keep existing]" : " (paste here)"}: `
36469
- );
36470
- const cmaToken = tokenInput || currentToken;
36471
- if (!cmaToken || !spaceId) {
36472
- warn("Space ID and CMA token are required. Skipped.");
36473
- return false;
36474
- }
36475
- const hostInput = await prompt(` API host [${currentHost}]: `);
36476
- const host = toConfiguredHost(hostInput) ?? storedHost;
36477
- const existing = await readExperiencesCredentials();
36478
- await writeExperiencesCredentials({ ...existing, spaceId, environmentId, cmaToken, ...host ? { host } : {} });
36479
- ok(`Credentials saved to ${experiencesCredentialsPath()}`);
36480
- ok(`API host set to ${host ?? DEFAULT_CONFIGURED_HOST}`);
36481
- info("Run experiences import \u2014 credentials will be pre-filled automatically.");
36482
- return true;
36483
- }
36484
- async function promptCustomSkillPath(kind, current, ask = prompt) {
36485
- const flagName = kind === "select" ? "--select-prompt-path" : "--generate-prompt-path";
36486
- void flagName;
36487
- const label = kind === "select" ? "select (analyze select-agent)" : "generate (generate components)";
36488
- const currentLabel = current ? ` [${current}]` : " [none]";
36489
- const answer = await ask(` Custom ${label} prompt path${currentLabel} (empty=keep, "-"=clear): `);
36490
- const trimmed = answer.trim();
36491
- if (trimmed === "") return void 0;
36492
- if (trimmed === "-") return null;
36493
- return trimmed;
36494
- }
36495
- async function setupQoL(profilePath) {
36496
- section("Step 6: Optional extras", "[optional]");
36497
- info("These are not required for experiences import but improve the experience.");
36498
- info("");
36499
- const existingCreds = await readExperiencesCredentials();
36500
- info("AI auto-filter \u2014 runs an agent pass before the manual scope-gate to prefilter components.");
36501
- info("Operators who prefer to review every component can default this OFF and override per run with --auto-filter.");
36502
- const autoFilter = await promptAutoFilterPreference((q) => prompt(q), existingCreds.autoFilter);
36503
- if (autoFilter !== (existingCreds.autoFilter ?? true)) {
36504
- await writeExperiencesCredentials({ ...existingCreds, autoFilter });
36505
- ok(`AI auto-filter default set to ${autoFilter ? "ON" : "OFF"}`);
36506
- } else {
36507
- dim(" unchanged");
36508
- }
36509
- info("");
36510
- const hasConcurrency = await profileContains(profilePath, "EDS_EXTRACT_CONCURRENCY");
36511
- if (!hasConcurrency) {
36512
- info("EDS_EXTRACT_CONCURRENCY \u2014 controls how many components are analyzed in parallel.");
36513
- info("Default is 4. Set higher (e.g. 8) on fast machines to speed up large codebases.");
36514
- const setConcurrency = await confirm("Add EDS_EXTRACT_CONCURRENCY=8 to your profile?", false);
36515
- if (setConcurrency) {
36516
- await appendToProfile(profilePath, "# experiences performance\nexport EDS_EXTRACT_CONCURRENCY=8");
36517
- ok(`EDS_EXTRACT_CONCURRENCY=8 written to ${profilePath}`);
36518
- } else {
36519
- dim(" skipped");
36520
- }
36521
- } else {
36522
- ok("EDS_EXTRACT_CONCURRENCY \u2014 already set");
36523
- }
36524
- info("");
36525
- info("Custom skill prompt paths \u2014 point select-agent and/or generate components at your own .md prompts.");
36526
- info("When set, the bundled invariants (utility-wrapper rejection, description rules) do NOT apply.");
36527
- const offerCustomPrompts = await confirm("Configure custom skill prompt paths?", false);
36528
- if (offerCustomPrompts) {
36529
- const stored = await readExperiencesCredentials();
36530
- const selectAnswer = await promptCustomSkillPath("select", stored.selectPromptPath);
36531
- const generateAnswer = await promptCustomSkillPath("generate", stored.generatePromptPath);
36532
- const updated = { ...stored };
36533
- if (selectAnswer === null) delete updated.selectPromptPath;
36534
- else if (selectAnswer !== void 0) updated.selectPromptPath = selectAnswer;
36535
- if (generateAnswer === null) delete updated.generatePromptPath;
36536
- else if (generateAnswer !== void 0) updated.generatePromptPath = generateAnswer;
36537
- await writeExperiencesCredentials(updated);
36538
- ok(`Custom prompt paths saved to ${experiencesCredentialsPath()}`);
36539
- } else {
36540
- dim(" skipped");
36541
- }
36542
- info("");
36543
- info("Debug logging \u2014 writes a JSONL trace of every command decision (agent calls, tool calls,");
36544
- info("apply actions, filter decisions, etc.) to ~/.contentful/experience-design-system-cli/debug/.");
36545
- info("Useful for developers debugging the CLI; OFF by default because traces are verbose.");
36546
- const debugCreds = await readExperiencesCredentials();
36547
- const debugChoice = await promptDebugModePreference((q) => prompt(q), debugCreds.debug);
36548
- if (debugChoice !== (debugCreds.debug ?? false)) {
36549
- await writeExperiencesCredentials({ ...debugCreds, debug: debugChoice });
36550
- ok(`Debug logging default set to ${debugChoice ? "ON" : "OFF"}`);
36551
- } else {
36552
- dim(" unchanged");
36553
- }
36554
- info("");
36555
- info("");
36556
- info("Anonymous usage analytics \u2014 helps us see which commands are used and where imports");
36557
- info("succeed or fail. Never includes source code, file paths, credentials, or authored content.");
36558
- info("Disabling here persists the opt-out; it will not silently re-enable later. See README > Usage data.");
36559
- const analyticsCreds = await readExperiencesCredentials();
36560
- const analyticsDisabled = await promptAnalyticsPreference((q) => prompt(q), analyticsCreds.analyticsDisabled);
36561
- if (analyticsDisabled !== (analyticsCreds.analyticsDisabled ?? false)) {
36562
- await writeExperiencesCredentials({ ...analyticsCreds, analyticsDisabled });
36563
- ok(`Analytics ${analyticsDisabled ? "disabled" : "enabled"}`);
36564
- } else {
36565
- dim(" unchanged");
36566
- }
36567
- info("");
36568
- info("");
36569
- info("NO_COLOR \u2014 set to 1 to disable ANSI color output (useful in CI or plain terminals).");
36570
- const setNoColor = await confirm("Add NO_COLOR=1 (disable colors) to your profile?", false);
36571
- if (setNoColor) {
36572
- const hasNoColor = await profileContains(profilePath, "NO_COLOR");
36573
- if (!hasNoColor) {
36574
- await appendToProfile(profilePath, "export NO_COLOR=1");
36575
- ok(`NO_COLOR=1 written to ${profilePath}`);
36576
- } else {
36577
- warn("NO_COLOR already present in profile \u2014 skipping");
36578
- }
37100
+ info("How to fix:");
37101
+ const managers = await detectNodeVersionManagers(homedir10());
37102
+ if (managers.nvm) {
37103
+ info(` nvm install ${check.required}`);
37104
+ info(` nvm use ${check.required}`);
37105
+ info(` nvm alias default ${check.required} # make it permanent`);
37106
+ } else if (managers.fnm) {
37107
+ info(` fnm install ${check.required}`);
37108
+ info(` fnm use ${check.required}`);
36579
37109
  } else {
36580
- dim(" skipped");
36581
- }
36582
- }
36583
- async function checkNode() {
36584
- section("Checking Node.js version");
36585
- const current = process.versions.node;
36586
- const major = parseInt(current.split(".")[0], 10);
36587
- if (major < REQUIRED_NODE_MAJOR) {
36588
- fail(`Node.js v${current} \u2014 need v${REQUIRED_NODE_MAJOR}+`);
36589
- info("");
36590
- info("How to fix:");
36591
- if (await binaryExists("nvm")) {
36592
- info(` nvm install ${REQUIRED_NODE_MAJOR}`);
36593
- info(` nvm use ${REQUIRED_NODE_MAJOR}`);
36594
- info(` nvm alias default ${REQUIRED_NODE_MAJOR} # make it permanent`);
36595
- } else if (await binaryExists("fnm")) {
36596
- info(` fnm install ${REQUIRED_NODE_MAJOR}`);
36597
- info(` fnm use ${REQUIRED_NODE_MAJOR}`);
36598
- } else {
36599
- info(` Download Node v${REQUIRED_NODE_MAJOR} from https://nodejs.org`);
36600
- }
36601
- return false;
37110
+ info(` Download Node v${check.required} from https://nodejs.org`);
36602
37111
  }
36603
- ok(`Node.js v${current}`);
36604
- return true;
37112
+ return false;
36605
37113
  }
36606
- async function checkPnpm(pkgRoot) {
37114
+ async function reportPnpm(pkgRoot) {
36607
37115
  section("Checking pnpm");
36608
- if (!await binaryExists("pnpm")) {
37116
+ const check = await checkPnpm(pkgRoot);
37117
+ if (check.status === "missing") {
36609
37118
  fail("pnpm not found");
36610
37119
  info("How to fix:");
36611
37120
  info(" npm install -g pnpm");
36612
37121
  info(" # or: corepack enable pnpm");
36613
37122
  return false;
36614
37123
  }
36615
- const versionResult = await runSpawn("pnpm", ["--version"]);
36616
- if (versionResult.exitCode !== 0) {
37124
+ if (check.status === "broken") {
36617
37125
  fail("pnpm found but not working");
36618
37126
  info("Try reinstalling: npm install -g pnpm --force");
36619
37127
  return false;
36620
37128
  }
36621
- ok(`pnpm v${versionResult.stdout.trim()}`);
36622
- const pingResult = await runSpawn("pnpm", ["exec", "node", "--version"], { cwd: pkgRoot });
36623
- if (pingResult.exitCode !== 0) {
37129
+ ok(`pnpm v${check.version}`);
37130
+ if (check.status === "unusable-in-repo") {
36624
37131
  fail("pnpm cannot execute in repo root");
36625
37132
  info("The pnpm global store may be mismatched with your current Node version.");
36626
37133
  info("How to fix:");
@@ -36629,21 +37136,18 @@ async function checkPnpm(pkgRoot) {
36629
37136
  }
36630
37137
  return true;
36631
37138
  }
36632
- async function checkDependencies(pkgRoot) {
37139
+ async function reportDependencies(pkgRoot) {
36633
37140
  section("Checking dependencies (pnpm install)");
36634
- const nodeModulesExists = await access10(join26(pkgRoot, "node_modules")).then(() => true).catch(() => false);
36635
- if (!nodeModulesExists) {
37141
+ if (!await pathExists5(join28(pkgRoot, "node_modules"))) {
36636
37142
  info("node_modules not found \u2014 running pnpm install...");
36637
37143
  } else {
36638
37144
  info("Running pnpm install to ensure dependencies are up to date...");
36639
37145
  }
36640
- const repoRoot = join26(pkgRoot, "..", "..");
36641
- const result = await runSpawn("pnpm", ["install", "--frozen-lockfile"], { cwd: repoRoot });
36642
- if (result.exitCode !== 0) {
37146
+ const check = await installDependencies(pkgRoot);
37147
+ if (!check.passed) {
36643
37148
  fail("pnpm install failed");
36644
37149
  info("");
36645
- const errLines = result.stderr.trim().split("\n").slice(0, 10);
36646
- for (const line of errLines) info(line);
37150
+ infoStderr(check.result.stderr, 10);
36647
37151
  info("");
36648
37152
  info("How to fix:");
36649
37153
  info(" \u2022 Try: pnpm install (without --frozen-lockfile) to update the lockfile");
@@ -36653,18 +37157,14 @@ async function checkDependencies(pkgRoot) {
36653
37157
  ok("Dependencies installed");
36654
37158
  return true;
36655
37159
  }
36656
- async function checkBuild(pkgRoot) {
37160
+ async function reportBuild(pkgRoot) {
36657
37161
  section("Building CLI");
36658
37162
  info("Running pnpm build...");
36659
- const repoRoot = join26(pkgRoot, "..", "..");
36660
- const result = await runSpawn("pnpm", ["--filter", "@contentful/experience-design-system-cli", "run", "build"], {
36661
- cwd: repoRoot
36662
- });
36663
- if (result.exitCode !== 0) {
37163
+ const check = await buildCli(join28(pkgRoot, "..", ".."));
37164
+ if (!check.passed) {
36664
37165
  fail("Build failed");
36665
37166
  info("");
36666
- const errLines = result.stderr.trim().split("\n").slice(0, 15);
36667
- for (const line of errLines) info(line);
37167
+ infoStderr(check.result.stderr, 15);
36668
37168
  info("");
36669
37169
  info("How to fix:");
36670
37170
  info(" \u2022 Check for TypeScript errors: pnpm typecheck");
@@ -36674,25 +37174,22 @@ async function checkBuild(pkgRoot) {
36674
37174
  ok("Build succeeded");
36675
37175
  return true;
36676
37176
  }
36677
- async function checkAgent() {
37177
+ async function reportAgent() {
36678
37178
  section("Checking coding agent");
36679
- const agents = AGENT_DEFS;
36680
37179
  const creds = await readExperiencesCredentials();
36681
37180
  const savedAgent = creds.agent;
36682
37181
  const savedModel = creds.agentModel;
36683
37182
  if (savedAgent) {
36684
- const found = await binaryExists(savedAgent);
36685
- if (found) {
37183
+ if (await binaryExists(savedAgent)) {
36686
37184
  const modelStr = savedModel ? ` \u2014 model: ${savedModel}` : "";
36687
37185
  ok(`${savedAgent}${modelStr} (saved preference)`);
36688
37186
  return true;
36689
- } else {
36690
- warn(`Saved agent '${savedAgent}' not found on PATH`);
36691
- info(`Re-run experiences setup to reconfigure.`);
36692
- return false;
36693
37187
  }
37188
+ warn(`Saved agent '${savedAgent}' not found on PATH`);
37189
+ info(`Re-run experiences setup to reconfigure.`);
37190
+ return false;
36694
37191
  }
36695
- for (const agent of agents) {
37192
+ for (const agent of AGENT_DEFS) {
36696
37193
  if (await binaryExists(agent.binary)) {
36697
37194
  ok(`${agent.name} (${agent.binary}) found`);
36698
37195
  info("Tip: run experiences setup to save a default agent and model.");
@@ -36707,152 +37204,147 @@ async function checkAgent() {
36707
37204
  info(" \u2022 OpenCode: npm install -g opencode-ai");
36708
37205
  return false;
36709
37206
  }
36710
- function registerSetupCommand(program) {
36711
- program.command("doctor").description("Check prerequisites so experiences import runs without errors").option("--skip-build", "Skip the pnpm install + build step (useful if already built)").option("--skip-agent", "Skip the coding agent check").action(async (opts) => {
36712
- process.stderr.write("\x1B[1mexperiences doctor\x1B[0m \u2014 checking your environment\n");
36713
- const pkgRoot = findPkgRoot();
36714
- const results = [];
36715
- const nodeOk = await checkNode();
36716
- results.push({ name: "Node.js version", ok: nodeOk, required: true });
36717
- if (nodeOk) {
36718
- const pnpmOk = await checkPnpm(pkgRoot);
36719
- results.push({ name: "pnpm", ok: pnpmOk, required: true });
36720
- if (!opts.skipBuild) {
36721
- if (pnpmOk) {
36722
- const depsOk = await checkDependencies(pkgRoot);
36723
- results.push({ name: "dependencies", ok: depsOk, required: true });
36724
- if (depsOk) {
36725
- const buildOk = await checkBuild(pkgRoot);
36726
- results.push({ name: "build", ok: buildOk, required: true });
36727
- }
37207
+
37208
+ // packages/experience-design-system-cli/src/doctor/command.ts
37209
+ async function runDoctor(opts) {
37210
+ process.stderr.write("\x1B[1mexperiences doctor\x1B[0m \u2014 checking your environment\n");
37211
+ const pkgRoot = findPkgRoot();
37212
+ const results = [];
37213
+ const nodeOk = await reportNode();
37214
+ results.push({ name: "Node.js version", ok: nodeOk, required: true });
37215
+ if (nodeOk) {
37216
+ const pnpmOk = await reportPnpm(pkgRoot);
37217
+ results.push({ name: "pnpm", ok: pnpmOk, required: true });
37218
+ if (!opts.skipBuild) {
37219
+ if (pnpmOk) {
37220
+ const depsOk = await reportDependencies(pkgRoot);
37221
+ results.push({ name: "dependencies", ok: depsOk, required: true });
37222
+ if (depsOk) {
37223
+ const buildOk = await reportBuild(pkgRoot);
37224
+ results.push({ name: "build", ok: buildOk, required: true });
36728
37225
  }
36729
- } else {
36730
- info("\nSkipping install + build (--skip-build)");
36731
37226
  }
36732
- }
36733
- if (!opts.skipAgent) {
36734
- const agentOk = await checkAgent();
36735
- results.push({ name: "coding agent", ok: agentOk, required: false });
36736
- }
36737
- section("Summary");
36738
- const failed = results.filter((r) => !r.ok);
36739
- const requiredFailed = failed.filter((r) => r.required);
36740
- for (const r of results) {
36741
- if (r.ok) {
36742
- ok(r.name);
36743
- } else if (r.required) {
36744
- fail(`${r.name} \u2014 required`);
36745
- } else {
36746
- warn(`${r.name} \u2014 optional`);
36747
- }
36748
- }
36749
- if (requiredFailed.length === 0 && failed.length === 0) {
36750
- process.stderr.write(
36751
- "\n\x1B[32m\x1B[1m\u2713 All checks passed. You are ready to run: experiences import\x1B[0m\n\n"
36752
- );
36753
- process.exit(0);
36754
- } else if (requiredFailed.length === 0) {
36755
- process.stderr.write("\n\x1B[33m\x1B[1m\u26A0 Required checks passed, but optional checks failed.\x1B[0m\n");
36756
- process.stderr.write(
36757
- " You can run \x1B[1mexperiences import\x1B[0m but the generate steps may fail without a coding agent.\n\n"
36758
- );
36759
- process.exit(0);
36760
37227
  } else {
36761
- process.stderr.write(
36762
- `
37228
+ info("\nSkipping install + build (--skip-build)");
37229
+ }
37230
+ }
37231
+ if (!opts.skipAgent) {
37232
+ const agentOk = await reportAgent();
37233
+ results.push({ name: "coding agent", ok: agentOk, required: false });
37234
+ }
37235
+ section("Summary");
37236
+ const failed = results.filter((r) => !r.ok);
37237
+ const requiredFailed = failed.filter((r) => r.required);
37238
+ for (const r of results) {
37239
+ if (r.ok) ok(r.name);
37240
+ else if (r.required) fail(`${r.name} \u2014 required`);
37241
+ else warn(`${r.name} \u2014 optional`);
37242
+ }
37243
+ if (requiredFailed.length === 0 && failed.length === 0) {
37244
+ process.stderr.write("\n\x1B[32m\x1B[1m\u2713 All checks passed. You are ready to run: experiences import\x1B[0m\n\n");
37245
+ process.exit(0);
37246
+ } else if (requiredFailed.length === 0) {
37247
+ process.stderr.write("\n\x1B[33m\x1B[1m\u26A0 Required checks passed, but optional checks failed.\x1B[0m\n");
37248
+ process.stderr.write(
37249
+ " You can run \x1B[1mexperiences import\x1B[0m but the generate steps may fail without a coding agent.\n\n"
37250
+ );
37251
+ process.exit(0);
37252
+ } else {
37253
+ process.stderr.write(
37254
+ `
36763
37255
  \x1B[31m\x1B[1m\u2717 ${requiredFailed.length} required check${requiredFailed.length === 1 ? "" : "s"} failed.\x1B[0m
36764
37256
  `
36765
- );
36766
- process.stderr.write(" Fix the issues above, then re-run \x1B[1mexperiences doctor\x1B[0m.\n\n");
36767
- process.exit(1);
36768
- }
36769
- });
36770
- program.command("setup").description("Interactive setup wizard: installs prerequisites and configures credentials for experiences import").option("--skip-build", "Skip the pnpm install + build step").option("--skip-agent", "Skip the coding agent check").option("--skip-credentials", "Skip the Contentful credentials step").option("--skip-optional", "Skip optional quality-of-life extras").action(
36771
- async (opts) => {
36772
- process.stdout.write("\n\x1B[1mexperiences setup\x1B[0m \u2014 interactive setup wizard\n");
36773
- process.stdout.write("Sets up everything you need to run \x1B[1mexperiences import\x1B[0m.\n");
36774
- const pkgRoot = findPkgRoot();
36775
- const repoRoot = join26(pkgRoot, "..", "..");
36776
- const profilePath = await detectShellProfile();
36777
- const results = [];
36778
- const nodeOk = await setupNode();
36779
- results.push({ name: "Node.js 24+", passed: nodeOk, required: true });
36780
- if (!nodeOk) {
36781
- process.stdout.write(
36782
- "\n\x1B[33mNode.js setup requires a shell restart. Re-run experiences setup afterwards.\x1B[0m\n\n"
36783
- );
36784
- process.exit(0);
36785
- }
36786
- const pnpmOk = await setupPnpm();
36787
- results.push({ name: "pnpm", passed: pnpmOk, required: true });
36788
- if (!opts.skipBuild && pnpmOk) {
36789
- const buildOk = await setupBuild(repoRoot);
36790
- results.push({ name: "install & build", passed: buildOk, required: true });
36791
- } else if (opts.skipBuild) {
36792
- info("\nSkipping install + build (--skip-build)");
36793
- }
36794
- if (!opts.skipAgent) {
36795
- const { agent, agentModel } = await setupAgent();
36796
- if (agent) {
36797
- const stored = await readExperiencesCredentials();
36798
- const { agentModel: _staleModel, ...storedWithoutModel } = stored;
36799
- await writeExperiencesCredentials({ ...storedWithoutModel, agent, ...agentModel ? { agentModel } : {} });
36800
- }
36801
- results.push({ name: "coding agent", passed: !!agent, required: true });
36802
- } else {
36803
- info("\nSkipping agent check (--skip-agent)");
36804
- results.push({ name: "coding agent", passed: true, required: false });
36805
- }
36806
- if (!opts.skipCredentials) {
36807
- const credsOk = await setupContentfulCredentials();
36808
- results.push({ name: "Contentful credentials", passed: credsOk, required: false });
36809
- } else {
36810
- info("\nSkipping credentials (--skip-credentials)");
36811
- }
36812
- if (!opts.skipOptional) {
36813
- await setupQoL(profilePath);
36814
- }
36815
- section("Summary");
36816
- const requiredFailed = results.filter((r) => r.required && !r.passed);
36817
- const optionalFailed = results.filter((r) => !r.required && !r.passed);
36818
- for (const r of results) {
36819
- if (r.passed) {
36820
- ok(`${r.name}`);
36821
- } else if (r.required) {
36822
- fail(`${r.name} \u2014 required`);
36823
- } else {
36824
- warn(`${r.name} \u2014 optional`);
36825
- }
36826
- }
36827
- process.stdout.write("\n");
36828
- if (requiredFailed.length === 0) {
36829
- process.stdout.write("\x1B[32m\x1B[1m\u2713 Setup complete. You can now run: experiences import\x1B[0m\n");
36830
- if (optionalFailed.length > 0) {
36831
- process.stdout.write(" (Some optional steps were skipped \u2014 that's fine.)\n");
36832
- }
36833
- } else {
36834
- process.stdout.write(
36835
- `\x1B[33m\x1B[1m\u26A0 ${requiredFailed.length} required step${requiredFailed.length === 1 ? "" : "s"} incomplete.\x1B[0m
36836
- `
36837
- );
36838
- process.stdout.write(" Complete the steps above, then re-run \x1B[1mexperiences setup\x1B[0m.\n");
36839
- }
36840
- process.stdout.write("\n");
36841
- const runDoctor = process.stdout.isTTY && await confirm("Run experiences doctor now to verify your environment?", requiredFailed.length === 0);
36842
- if (runDoctor) {
36843
- process.stdout.write("\n");
36844
- const cliBin = process.argv[1] ?? fileURLToPath4(import.meta.url);
36845
- const doctorResult = await runSpawn(process.execPath, [cliBin, "doctor"], {
36846
- env: process.env
36847
- });
36848
- process.stdout.write(doctorResult.stdout);
36849
- process.stderr.write(doctorResult.stderr);
36850
- process.exit(doctorResult.exitCode);
37257
+ );
37258
+ process.stderr.write(" Fix the issues above, then re-run \x1B[1mexperiences doctor\x1B[0m.\n\n");
37259
+ process.exit(1);
37260
+ }
37261
+ }
37262
+ function registerDoctorCommand(program) {
37263
+ program.command("doctor").description("Check prerequisites so experiences import runs without errors").option("--skip-build", "Skip the pnpm install + build step (useful if already built)").option("--skip-agent", "Skip the coding agent check").action((opts) => runDoctor(opts));
37264
+ }
37265
+
37266
+ // packages/experience-design-system-cli/src/setup/command.ts
37267
+ init_credentials_store();
37268
+ init_cli_path();
37269
+ init_terminal_capabilities();
37270
+ init_shell();
37271
+ import { readFileSync as readFileSync10 } from "node:fs";
37272
+ import { join as join29 } from "node:path";
37273
+ import { homedir as homedir11 } from "node:os";
37274
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
37275
+ var SETUP_REQUIRES_TTY_MESSAGE = "Error: experiences setup requires an interactive terminal.";
37276
+ function getCliVersion() {
37277
+ const pkg4 = JSON.parse(readFileSync10(join29(findPkgRoot(), "package.json"), "utf8"));
37278
+ return pkg4.version;
37279
+ }
37280
+ function createSetupScreenDependencies() {
37281
+ return {
37282
+ nodeVersion: process.versions.node,
37283
+ homeDir: homedir11(),
37284
+ env: process.env,
37285
+ binaryExists,
37286
+ run: runSpawn,
37287
+ pathExists: pathExists5,
37288
+ profileContains,
37289
+ appendToProfile,
37290
+ readCredentials: readExperiencesCredentials,
37291
+ writeCredentials: writeExperiencesCredentials,
37292
+ credentialsPath: experiencesCredentialsPath
37293
+ };
37294
+ }
37295
+ async function runSetup(opts) {
37296
+ if (!getInteractiveTerminalSupport().supported) {
37297
+ process.stderr.write(`${SETUP_REQUIRES_TTY_MESSAGE}
37298
+ `);
37299
+ process.exit(1);
37300
+ return;
37301
+ }
37302
+ const { render: render7 } = await import("ink");
37303
+ const { createElement: createElement7 } = await import("react");
37304
+ const { SetupScreen: SetupScreen2 } = await Promise.resolve().then(() => (init_SetupScreen(), SetupScreen_exports));
37305
+ const repoRoot = join29(findPkgRoot(), "..", "..");
37306
+ const profilePath = await detectShellProfile();
37307
+ const completion = { outcome: null };
37308
+ let unmountInk = null;
37309
+ const { waitUntilExit, unmount } = render7(
37310
+ createElement7(SetupScreen2, {
37311
+ version: getCliVersion(),
37312
+ repoRoot,
37313
+ profilePath,
37314
+ dependencies: createSetupScreenDependencies(),
37315
+ skip: {
37316
+ ...opts.skipBuild !== void 0 ? { skipBuild: opts.skipBuild } : {},
37317
+ ...opts.skipAgent !== void 0 ? { skipAgent: opts.skipAgent } : {},
37318
+ ...opts.skipCredentials !== void 0 ? { skipCredentials: opts.skipCredentials } : {},
37319
+ ...opts.skipOptional !== void 0 ? { skipOptional: opts.skipOptional } : {}
37320
+ },
37321
+ offerDoctor: true,
37322
+ onComplete: (result) => {
37323
+ completion.outcome = result;
37324
+ unmountInk?.();
36851
37325
  }
36852
- process.stdout.write("\nRun \x1B[1mexperiences doctor\x1B[0m at any time to re-check your environment.\n\n");
36853
- process.exit(requiredFailed.length === 0 ? 0 : 1);
36854
- }
37326
+ })
36855
37327
  );
37328
+ unmountInk = unmount;
37329
+ await waitUntilExit();
37330
+ const finished = completion.outcome;
37331
+ if (!finished) {
37332
+ process.exit(1);
37333
+ return;
37334
+ }
37335
+ if (finished.runDoctor) {
37336
+ const cliBin = process.argv[1] ?? fileURLToPath4(import.meta.url);
37337
+ const doctorResult = await runSpawn(process.execPath, [cliBin, "doctor"], { env: process.env });
37338
+ process.stdout.write(doctorResult.stdout);
37339
+ process.stderr.write(doctorResult.stderr);
37340
+ process.exit(doctorResult.exitCode);
37341
+ return;
37342
+ }
37343
+ process.stdout.write("\nRun \x1B[1mexperiences doctor\x1B[0m at any time to re-check your environment.\n\n");
37344
+ process.exit(finished.exitCode);
37345
+ }
37346
+ function registerSetupCommand(program) {
37347
+ program.command("setup").description("Interactive setup wizard: installs prerequisites and configures credentials for experiences import").option("--skip-build", "Skip the pnpm install + build step").option("--skip-agent", "Skip the coding agent check").option("--skip-credentials", "Skip the Contentful credentials step").option("--skip-optional", "Skip optional quality-of-life extras").action((opts) => runSetup(opts));
36856
37348
  }
36857
37349
 
36858
37350
  // packages/experience-design-system-cli/src/runs/ls-command.ts
@@ -37012,7 +37504,7 @@ async function beginCommand(command, opts) {
37012
37504
  init_analytics();
37013
37505
  init_credentials_store();
37014
37506
  init_cli_path();
37015
- var pkg3 = JSON.parse(readFileSync10(join27(findPkgRoot(), "package.json"), "utf8"));
37507
+ var pkg3 = JSON.parse(readFileSync11(join30(findPkgRoot(), "package.json"), "utf8"));
37016
37508
  async function runBuild(opts) {
37017
37509
  return new Promise((resolvePromise) => {
37018
37510
  const child = opts.spawnFn();
@@ -37068,6 +37560,7 @@ function createProgram() {
37068
37560
  registerSessionCommand(program);
37069
37561
  registerImportCommand(program);
37070
37562
  registerImportV2Command(program);
37563
+ registerDoctorCommand(program);
37071
37564
  registerSetupCommand(program);
37072
37565
  registerRunsCommand(program);
37073
37566
  registerBuildCommand(program);