@contentful/experience-design-system-cli 2.37.1-dev-build-2582b468.0 → 2.37.1-dev-build-414ce827.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.
package/dist/app.js CHANGED
@@ -28565,68 +28565,44 @@ function SettingsScreen({
28565
28565
 
28566
28566
  // packages/experience-design-system-cli/src/tui/settings/configuration/ConfigurationScreen.tsx
28567
28567
  import { useState as useState33 } from "react";
28568
- import { Box as Box48, Text as Text51, useInput as useInput13 } from "ink";
28568
+ import { Box as Box48, Text as Text51, useInput as useInput14 } from "ink";
28569
28569
 
28570
28570
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentsScreen.tsx
28571
- import { useEffect as useEffect23, useState as useState31 } from "react";
28571
+ import { useEffect as useEffect23, useRef as useRef13, useState as useState31 } from "react";
28572
28572
  import { Box as Box46, Text as Text49, useInput as useInput12 } from "ink";
28573
28573
  init_debug_store();
28574
28574
 
28575
- // packages/experience-design-system-cli/src/tui/settings/shared/v1-store.ts
28575
+ // packages/experience-design-system-cli/src/tui/settings/shared/debug-trail.ts
28576
28576
  init_config();
28577
- var readV1Store = readSettings2;
28578
- var writeV1Store = writeSettings2;
28579
-
28580
- // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-store.ts
28581
- var AGENT_LABELS = {
28582
- claude: "Claude Code",
28583
- codex: "OpenAI Codex",
28584
- opencode: "OpenCode",
28585
- cursor: "Cursor",
28586
- copilot: "GitHub Copilot"
28587
- };
28588
- var asString = (value) => typeof value === "string" ? value : "";
28589
- function emptyAgents() {
28590
- return Object.fromEntries(AGENT_NAMES.map((name) => [name, { model: "", binary: "" }]));
28577
+ import { existsSync as existsSync10 } from "node:fs";
28578
+ function diffSnapshots(before, after) {
28579
+ const names = [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])];
28580
+ const changed_fields = names.filter((name) => before[name] !== after[name]);
28581
+ return { changed: changed_fields.length > 0, changed_fields };
28582
+ }
28583
+ var DEFAULT_TRAIL_LIMIT = 30;
28584
+ function createTrail(limit = DEFAULT_TRAIL_LIMIT) {
28585
+ const actions = [];
28586
+ return {
28587
+ record(action) {
28588
+ actions.push(action);
28589
+ if (actions.length > limit) actions.shift();
28590
+ },
28591
+ entries: () => [...actions]
28592
+ };
28591
28593
  }
28592
- async function readAgentConfig() {
28593
- const store = await readV1Store();
28594
- const stored = asString(store["agent"]);
28595
- const defaultChosen = isAgentName(stored);
28596
- const defaultAgent = defaultChosen ? stored : DEFAULT_AGENT_NAME;
28597
- const agents = emptyAgents();
28598
- const saved = store["agents"];
28599
- if (saved && typeof saved === "object") {
28600
- for (const name of AGENT_NAMES) {
28601
- const entry = saved[name];
28602
- if (entry && typeof entry === "object") {
28603
- const fields2 = entry;
28604
- agents[name] = { model: asString(fields2["model"]), binary: asString(fields2["binary"]) };
28605
- }
28606
- }
28594
+ var MIN_SECRET_LENGTH = 6;
28595
+ function scrubError(error, secrets) {
28596
+ let message = error instanceof Error ? error.message : String(error);
28597
+ for (const secret of secrets) {
28598
+ if (secret.length < MIN_SECRET_LENGTH) continue;
28599
+ for (const form of /* @__PURE__ */ new Set([secret, encodeURIComponent(secret)])) message = message.split(form).join("[redacted]");
28607
28600
  }
28608
- const legacyModel = asString(store["agentModel"]);
28609
- if (legacyModel && !agents[defaultAgent].model)
28610
- agents[defaultAgent] = { ...agents[defaultAgent], model: legacyModel };
28611
- return { defaultAgent, defaultChosen, agents };
28601
+ return message;
28612
28602
  }
28613
- async function writeAgentConfig(config) {
28614
- const existing = await readV1Store();
28615
- const merged = { ...existing };
28616
- const agents = {};
28617
- for (const name of AGENT_NAMES) {
28618
- const model = config.agents[name].model.trim();
28619
- const binary = config.agents[name].binary.trim();
28620
- if (model || binary) agents[name] = { ...model ? { model } : {}, ...binary ? { binary } : {} };
28621
- }
28622
- const defaultModel = config.defaultChosen ? agents[config.defaultAgent]?.model : void 0;
28623
- if (config.defaultChosen) merged["agent"] = config.defaultAgent;
28624
- else delete merged["agent"];
28625
- if (defaultModel) merged["agentModel"] = defaultModel;
28626
- else delete merged["agentModel"];
28627
- if (Object.keys(agents).length > 0) merged["agents"] = agents;
28628
- else delete merged["agents"];
28629
- await writeV1Store(merged);
28603
+ function describeSettingsFile() {
28604
+ const path5 = configFilePath2();
28605
+ return { path: path5, eds_home_set: Boolean(process.env["EDS_HOME"]), exists: existsSync10(path5) };
28630
28606
  }
28631
28607
 
28632
28608
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-status.ts
@@ -28832,6 +28808,94 @@ async function withBinaryEnv(name, binary, run) {
28832
28808
  }
28833
28809
  }
28834
28810
 
28811
+ // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-debug.ts
28812
+ function agentSnapshot(config) {
28813
+ const snapshot = { default_agent: config.defaultAgent };
28814
+ for (const name of AGENT_NAMES) {
28815
+ snapshot[`${name}.model`] = config.agents[name].model;
28816
+ snapshot[`${name}.binary`] = config.agents[name].binary;
28817
+ }
28818
+ return snapshot;
28819
+ }
28820
+ function environmentFrom(checks, savedBinary, envValue) {
28821
+ const path5 = checks.find((check) => check.step === "path");
28822
+ const version = checks.find((check) => check.step === "version");
28823
+ const overridden = envValue !== void 0 && envValue !== "" && envValue !== savedBinary.trim();
28824
+ return {
28825
+ found: path5?.ok === true,
28826
+ resolved_path: path5?.ok ? path5.detail : "",
28827
+ version: version?.ok ? version.detail : "",
28828
+ env_override: overridden ? envValue : ""
28829
+ };
28830
+ }
28831
+ async function describeAgents(config) {
28832
+ const entries = await Promise.all(
28833
+ AGENT_NAMES.map(async (name) => {
28834
+ const saved = config.agents[name].binary;
28835
+ const checks = await inspectAgent(name, saved);
28836
+ return [name, environmentFrom(checks, saved, process.env[`EDS_AGENT_BINARY_${name.toUpperCase()}`])];
28837
+ })
28838
+ );
28839
+ return Object.fromEntries(entries);
28840
+ }
28841
+
28842
+ // packages/experience-design-system-cli/src/tui/settings/shared/v1-store.ts
28843
+ init_config();
28844
+ var readV1Store = readSettings2;
28845
+ var writeV1Store = writeSettings2;
28846
+
28847
+ // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-store.ts
28848
+ var AGENT_LABELS = {
28849
+ claude: "Claude Code",
28850
+ codex: "OpenAI Codex",
28851
+ opencode: "OpenCode",
28852
+ cursor: "Cursor",
28853
+ copilot: "GitHub Copilot"
28854
+ };
28855
+ var asString = (value) => typeof value === "string" ? value : "";
28856
+ function emptyAgents() {
28857
+ return Object.fromEntries(AGENT_NAMES.map((name) => [name, { model: "", binary: "" }]));
28858
+ }
28859
+ async function readAgentConfig() {
28860
+ const store = await readV1Store();
28861
+ const stored = asString(store["agent"]);
28862
+ const defaultChosen = isAgentName(stored);
28863
+ const defaultAgent = defaultChosen ? stored : DEFAULT_AGENT_NAME;
28864
+ const agents = emptyAgents();
28865
+ const saved = store["agents"];
28866
+ if (saved && typeof saved === "object") {
28867
+ for (const name of AGENT_NAMES) {
28868
+ const entry = saved[name];
28869
+ if (entry && typeof entry === "object") {
28870
+ const fields2 = entry;
28871
+ agents[name] = { model: asString(fields2["model"]), binary: asString(fields2["binary"]) };
28872
+ }
28873
+ }
28874
+ }
28875
+ const legacyModel = asString(store["agentModel"]);
28876
+ if (legacyModel && !agents[defaultAgent].model)
28877
+ agents[defaultAgent] = { ...agents[defaultAgent], model: legacyModel };
28878
+ return { defaultAgent, defaultChosen, agents };
28879
+ }
28880
+ async function writeAgentConfig(config) {
28881
+ const existing = await readV1Store();
28882
+ const merged = { ...existing };
28883
+ const agents = {};
28884
+ for (const name of AGENT_NAMES) {
28885
+ const model = config.agents[name].model.trim();
28886
+ const binary = config.agents[name].binary.trim();
28887
+ if (model || binary) agents[name] = { ...model ? { model } : {}, ...binary ? { binary } : {} };
28888
+ }
28889
+ const defaultModel = config.defaultChosen ? agents[config.defaultAgent]?.model : void 0;
28890
+ if (config.defaultChosen) merged["agent"] = config.defaultAgent;
28891
+ else delete merged["agent"];
28892
+ if (defaultModel) merged["agentModel"] = defaultModel;
28893
+ else delete merged["agentModel"];
28894
+ if (Object.keys(agents).length > 0) merged["agents"] = agents;
28895
+ else delete merged["agents"];
28896
+ await writeV1Store(merged);
28897
+ }
28898
+
28835
28899
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentDetail.tsx
28836
28900
  import { useEffect as useEffect22, useState as useState30 } from "react";
28837
28901
  import { Box as Box45, Text as Text48 } from "ink";
@@ -29033,7 +29097,8 @@ function AgentDetail({
29033
29097
  name,
29034
29098
  config,
29035
29099
  setConfig,
29036
- onBack
29100
+ onBack,
29101
+ record: record3
29037
29102
  }) {
29038
29103
  const settings = config.agents[name];
29039
29104
  const [saving, setSaving] = useState30(false);
@@ -29056,10 +29121,12 @@ function AgentDetail({
29056
29121
  setNotice(null);
29057
29122
  writeAgentConfig(config).then(() => {
29058
29123
  applyBinaryOverrides(config);
29124
+ record3(`saved ${name}`);
29059
29125
  setNotice({ kind: "success", text: `Saved: ${AGENT_LABELS[name]} settings are written to config.json` });
29060
- }).catch(
29061
- (err) => setNotice({ kind: "error", text: `Not saved: ${err instanceof Error ? err.message : String(err)}` })
29062
- ).finally(() => setSaving(false));
29126
+ }).catch((err) => {
29127
+ record3(`saving ${name} failed`, err);
29128
+ setNotice({ kind: "error", text: `Not saved: ${err instanceof Error ? err.message : String(err)}` });
29129
+ }).finally(() => setSaving(false));
29063
29130
  };
29064
29131
  const runTest = () => {
29065
29132
  setTesting(true);
@@ -29067,12 +29134,15 @@ function AgentDetail({
29067
29134
  testAgent(name, settings.binary, settings.model).then((result) => {
29068
29135
  setChecks(result);
29069
29136
  const outcome = testOutcome(result);
29137
+ if (!outcome || !outcome.ok)
29138
+ record3(`test of ${name} failed: ${outcome?.text ?? "the agent could not be started"}`);
29070
29139
  setNotice(
29071
29140
  outcome ? { kind: outcome.ok ? "success" : "error", text: outcome.text } : { kind: "error", text: "Test not run: the agent could not be started (see Status)" }
29072
29141
  );
29073
- }).catch(
29074
- (err) => setNotice({ kind: "error", text: `Test could not run: ${err instanceof Error ? err.message : String(err)}` })
29075
- ).finally(() => setTesting(false));
29142
+ }).catch((err) => {
29143
+ record3(`test of ${name} could not run`, err);
29144
+ setNotice({ kind: "error", text: `Test could not run: ${err instanceof Error ? err.message : String(err)}` });
29145
+ }).finally(() => setTesting(false));
29076
29146
  };
29077
29147
  const action = {
29078
29148
  label: "Run test",
@@ -29093,6 +29163,7 @@ function AgentDetail({
29093
29163
  onChange: (key, value) => {
29094
29164
  setConfig({ ...config, agents: { ...config.agents, [name]: { ...settings, [key]: value } } });
29095
29165
  setNotice(null);
29166
+ record3(`edited ${name}.${key}`);
29096
29167
  },
29097
29168
  onSave: save,
29098
29169
  onQuit: onBack,
@@ -29119,6 +29190,12 @@ function AgentsScreen({ onDone }) {
29119
29190
  const [saving, setSaving] = useState31(false);
29120
29191
  const [loadError, setLoadError] = useState31(null);
29121
29192
  const [checks, setChecks] = useState31({});
29193
+ const visit = useRef13({
29194
+ trail: createTrail(),
29195
+ before: void 0,
29196
+ environment: void 0,
29197
+ error: ""
29198
+ }).current;
29122
29199
  const inspectAll = (loaded) => {
29123
29200
  for (const name of AGENT_NAMES) {
29124
29201
  void inspectAgent(name, loaded.agents[name].binary).then(
@@ -29130,14 +29207,38 @@ function AgentsScreen({ onDone }) {
29130
29207
  readAgentConfig().then((loaded) => {
29131
29208
  setConfig(loaded);
29132
29209
  inspectAll(loaded);
29210
+ visit.before = agentSnapshot(loaded);
29133
29211
  startDebugRun({
29134
29212
  flow: "settings/agents",
29135
29213
  step: "01-agents",
29136
29214
  menuOption: "Configuration > Agents",
29137
- inputs: { default_agent: loaded.defaultAgent }
29215
+ inputs: { ...visit.before, default_chosen: loaded.defaultChosen ?? false }
29138
29216
  });
29139
- }).catch((err) => setLoadError(err instanceof Error ? err.message : String(err)));
29217
+ void describeAgents(loaded).then((found) => {
29218
+ visit.environment = found;
29219
+ });
29220
+ }).catch((err) => {
29221
+ visit.error = scrubError(err, []);
29222
+ startDebugRun({ flow: "settings/agents", step: "01-agents", menuOption: "Configuration > Agents", inputs: {} });
29223
+ setLoadError(visit.error);
29224
+ });
29140
29225
  }, []);
29226
+ const endVisit = (current) => {
29227
+ const after = current ? agentSnapshot(current) : void 0;
29228
+ const change = visit.before && after ? diffSnapshots(visit.before, after) : { changed: false, changed_fields: [] };
29229
+ void finishDebugRun({
29230
+ outputs: {
29231
+ ...after ?? {},
29232
+ ...change,
29233
+ actions: visit.trail.entries(),
29234
+ // Missing only when the user left before the probes finished.
29235
+ ...visit.environment ? { agents: visit.environment } : {},
29236
+ ...visit.error ? { error: visit.error } : {}
29237
+ },
29238
+ status: visit.error ? "error" : "success",
29239
+ exitMethod: change.changed ? "saved" : "discarded"
29240
+ });
29241
+ };
29141
29242
  const makeDefault = (name) => {
29142
29243
  if (!config || saving) return;
29143
29244
  if (name === config.defaultAgent) {
@@ -29149,15 +29250,16 @@ function AgentsScreen({ onDone }) {
29149
29250
  writeAgentConfig({ ...config, defaultAgent: name, defaultChosen: true }).then(() => {
29150
29251
  setConfig((current) => current ? { ...current, defaultAgent: name, defaultChosen: true } : current);
29151
29252
  setNotice({ kind: "success", text: `Default agent is now ${AGENT_LABELS[name]}` });
29152
- }).catch(
29153
- (err) => setNotice({
29154
- kind: "error",
29155
- text: `Default agent not changed: ${err instanceof Error ? err.message : String(err)}`
29156
- })
29157
- ).finally(() => setSaving(false));
29253
+ visit.trail.record(`made ${name} the default`);
29254
+ }).catch((err) => {
29255
+ visit.error = scrubError(err, []);
29256
+ visit.trail.record(`making ${name} the default failed`);
29257
+ setNotice({ kind: "error", text: `Default agent not changed: ${visit.error}` });
29258
+ }).finally(() => setSaving(false));
29158
29259
  };
29159
29260
  useInput12((input, key) => {
29160
29261
  if (loadError && (key.escape || input === "q")) {
29262
+ endVisit(null);
29161
29263
  onDone();
29162
29264
  return;
29163
29265
  }
@@ -29182,7 +29284,7 @@ function AgentsScreen({ onDone }) {
29182
29284
  return;
29183
29285
  }
29184
29286
  if (key.escape || input === "q") {
29185
- void finishDebugRun({ outputs: { default_agent: config.defaultAgent }, status: "success", exitMethod: "saved" });
29287
+ endVisit(config);
29186
29288
  onDone();
29187
29289
  }
29188
29290
  });
@@ -29207,6 +29309,10 @@ function AgentsScreen({ onDone }) {
29207
29309
  name: open,
29208
29310
  config,
29209
29311
  setConfig,
29312
+ record: (action, error) => {
29313
+ visit.trail.record(action);
29314
+ if (error !== void 0) visit.error = scrubError(error, []);
29315
+ },
29210
29316
  onBack: () => {
29211
29317
  setOpen(null);
29212
29318
  void readAgentConfig().then((saved) => {
@@ -29273,8 +29379,8 @@ function AgentsScreen({ onDone }) {
29273
29379
  }
29274
29380
 
29275
29381
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/ProjectScreen.tsx
29276
- import { useEffect as useEffect24, useState as useState32 } from "react";
29277
- import { Box as Box47, Text as Text50 } from "ink";
29382
+ import { useEffect as useEffect24, useRef as useRef14, useState as useState32 } from "react";
29383
+ import { Box as Box47, Text as Text50, useInput as useInput13 } from "ink";
29278
29384
  init_debug_store();
29279
29385
 
29280
29386
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/project-store.ts
@@ -29303,7 +29409,7 @@ async function writeCredentials(config) {
29303
29409
  }
29304
29410
 
29305
29411
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/ProjectScreen.tsx
29306
- import { jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
29412
+ import { Fragment as Fragment19, jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
29307
29413
  var FIELDS2 = [
29308
29414
  { key: "spaceId", label: "Space ID" },
29309
29415
  { key: "environmentId", label: "Environment ID" },
@@ -29324,45 +29430,94 @@ function debugFields(config) {
29324
29430
  contentful_base_dir: config?.contentfulBaseDir ?? ""
29325
29431
  };
29326
29432
  }
29433
+ var snapshotOf = (config) => Object.fromEntries(FIELDS2.map(({ key }) => [key, config[key] || ""]));
29327
29434
  var valuesOf = (config) => Object.fromEntries(FIELDS2.map(({ key }) => [key, config[key] || ""]));
29328
29435
  function ProjectScreen({ onDone }) {
29329
29436
  const [config, setConfig] = useState32(null);
29330
29437
  const [saving, setSaving] = useState32(false);
29331
29438
  const [notice, setNotice] = useState32(null);
29332
- const [lastSaved, setLastSaved] = useState32(null);
29439
+ const [loadError, setLoadError] = useState32(null);
29440
+ const visit = useRef14({
29441
+ trail: createTrail(),
29442
+ before: void 0,
29443
+ persisted: void 0,
29444
+ error: ""
29445
+ }).current;
29446
+ const scrub = (err) => scrubError(err, [config?.cmaToken ?? ""]);
29333
29447
  useEffect24(() => {
29334
- void readCredentials().then((cfg) => {
29448
+ readCredentials().then((cfg) => {
29335
29449
  setConfig(cfg);
29450
+ visit.before = snapshotOf(cfg);
29451
+ visit.persisted = visit.before;
29452
+ startDebugRun({
29453
+ flow: "settings/contentful-configuration",
29454
+ step: "01-configuration",
29455
+ menuOption: "Configuration > Project",
29456
+ inputs: { ...debugFields(cfg), settings_file: describeSettingsFile() }
29457
+ });
29458
+ }).catch((err) => {
29459
+ visit.error = scrubError(err, []);
29336
29460
  startDebugRun({
29337
29461
  flow: "settings/contentful-configuration",
29338
29462
  step: "01-configuration",
29339
29463
  menuOption: "Configuration > Project",
29340
- inputs: debugFields(cfg)
29464
+ inputs: { settings_file: describeSettingsFile() }
29341
29465
  });
29466
+ setLoadError(visit.error);
29342
29467
  });
29343
29468
  }, []);
29469
+ const endVisit = (current) => {
29470
+ const onScreen = current ? snapshotOf(current) : void 0;
29471
+ const change = visit.before && visit.persisted ? diffSnapshots(visit.before, visit.persisted) : { changed: false, changed_fields: [] };
29472
+ const unsaved = visit.persisted && onScreen ? diffSnapshots(visit.persisted, onScreen) : { changed: false, changed_fields: [] };
29473
+ void finishDebugRun({
29474
+ outputs: {
29475
+ ...debugFields(current),
29476
+ ...change,
29477
+ unsaved_edits: unsaved.changed_fields,
29478
+ actions: visit.trail.entries(),
29479
+ ...visit.error ? { error: visit.error } : {}
29480
+ },
29481
+ status: visit.error ? "error" : "success",
29482
+ exitMethod: change.changed ? "saved" : "discarded"
29483
+ });
29484
+ };
29485
+ useInput13((input, key) => {
29486
+ if (!config && loadError && (key.escape || input === "q")) {
29487
+ endVisit(null);
29488
+ onDone();
29489
+ }
29490
+ });
29344
29491
  if (!config) {
29345
29492
  return /* @__PURE__ */ jsxs48(Box47, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
29346
29493
  /* @__PURE__ */ jsx59(Text50, { bold: true, children: "Project" }),
29347
29494
  /* @__PURE__ */ jsx59(Text50, { children: " " }),
29348
- /* @__PURE__ */ jsx59(Text50, { color: PALETTE.accent, children: "Loading..." })
29495
+ loadError ? /* @__PURE__ */ jsxs48(Fragment19, { children: [
29496
+ /* @__PURE__ */ jsxs48(Text50, { color: PALETTE.error, children: [
29497
+ "\u2717 Could not read the settings: ",
29498
+ loadError
29499
+ ] }),
29500
+ /* @__PURE__ */ jsx59(Text50, { children: " " }),
29501
+ /* @__PURE__ */ jsx59(ControlHints, { hints: [{ keys: "Esc/q", label: "back" }] })
29502
+ ] }) : /* @__PURE__ */ jsx59(Text50, { color: PALETTE.accent, children: "Loading..." })
29349
29503
  ] });
29350
29504
  }
29351
29505
  const save = () => {
29352
29506
  setSaving(true);
29353
29507
  setNotice(null);
29354
29508
  writeCredentials(config).then(() => {
29355
- setLastSaved(config);
29509
+ visit.persisted = snapshotOf(config);
29510
+ visit.trail.record("saved");
29356
29511
  if (config.contentfulBaseDir) void startDebugSessionIn(config.contentfulBaseDir, { replace: true });
29357
29512
  setNotice({ kind: "success", text: "Project settings saved" });
29358
29513
  }).catch((err) => {
29514
+ visit.error = scrub(err);
29515
+ visit.trail.record("save failed");
29359
29516
  setNotice({ kind: "error", text: err instanceof Error ? err.message : "Failed to save" });
29360
29517
  }).finally(() => setSaving(false));
29361
29518
  };
29362
29519
  const quit = () => {
29363
- void finishDebugRun(
29364
- lastSaved ? { outputs: debugFields(lastSaved), status: "success", exitMethod: "saved" } : { outputs: {}, status: "success", exitMethod: "discarded" }
29365
- );
29520
+ endVisit(config);
29366
29521
  onDone();
29367
29522
  };
29368
29523
  return /* @__PURE__ */ jsx59(
@@ -29375,6 +29530,7 @@ function ProjectScreen({ onDone }) {
29375
29530
  onChange: (key, value) => {
29376
29531
  setConfig({ ...config, [key]: value });
29377
29532
  setNotice(null);
29533
+ visit.trail.record(`edited ${key}`);
29378
29534
  },
29379
29535
  onSave: save,
29380
29536
  onQuit: quit,
@@ -29398,7 +29554,7 @@ function ConfigurationScreen({ onDone }) {
29398
29554
  const [focus, setFocus] = useState33(0);
29399
29555
  const [open, setOpen] = useState33(null);
29400
29556
  const close = () => setOpen(null);
29401
- useInput13((input, key) => {
29557
+ useInput14((input, key) => {
29402
29558
  if (open) return;
29403
29559
  if (key.upArrow) {
29404
29560
  setFocus((i) => (i - 1 + SECTIONS.length) % SECTIONS.length);
@@ -29440,7 +29596,7 @@ function ConfigurationScreen({ onDone }) {
29440
29596
 
29441
29597
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/screen.tsx
29442
29598
  import { useEffect as useEffect25, useState as useState34 } from "react";
29443
- import { Box as Box49, Text as Text52, useInput as useInput14 } from "ink";
29599
+ import { Box as Box49, Text as Text52, useInput as useInput15 } from "ink";
29444
29600
 
29445
29601
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/analytics-store.ts
29446
29602
  async function readAnalyticsSetting() {
@@ -29459,7 +29615,7 @@ async function writeAnalyticsSetting(setting) {
29459
29615
 
29460
29616
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/screen.tsx
29461
29617
  init_debug_store();
29462
- import { Fragment as Fragment19, jsx as jsx61, jsxs as jsxs50 } from "react/jsx-runtime";
29618
+ import { Fragment as Fragment20, jsx as jsx61, jsxs as jsxs50 } from "react/jsx-runtime";
29463
29619
  function OptInAnalyticsScreen({ onDone }) {
29464
29620
  const [loading, setLoading] = useState34(true);
29465
29621
  const [enabled, setEnabled] = useState34(true);
@@ -29475,7 +29631,7 @@ function OptInAnalyticsScreen({ onDone }) {
29475
29631
  });
29476
29632
  });
29477
29633
  }, []);
29478
- useInput14((input, key) => {
29634
+ useInput15((input, key) => {
29479
29635
  if (loading) return;
29480
29636
  if (key.return || input === " ") {
29481
29637
  setEnabled((current) => {
@@ -29493,7 +29649,7 @@ function OptInAnalyticsScreen({ onDone }) {
29493
29649
  return /* @__PURE__ */ jsxs50(Box49, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
29494
29650
  /* @__PURE__ */ jsx61(Text52, { bold: true, children: "Settings \u203A Opt-in Analytics" }),
29495
29651
  /* @__PURE__ */ jsx61(Text52, { children: " " }),
29496
- loading ? /* @__PURE__ */ jsx61(Text52, { color: PALETTE.muted, children: "Loading\u2026" }) : /* @__PURE__ */ jsxs50(Fragment19, { children: [
29652
+ loading ? /* @__PURE__ */ jsx61(Text52, { color: PALETTE.muted, children: "Loading\u2026" }) : /* @__PURE__ */ jsxs50(Fragment20, { children: [
29497
29653
  /* @__PURE__ */ jsxs50(Text52, { children: [
29498
29654
  "Share anonymous usage data:",
29499
29655
  " ",
@@ -29518,7 +29674,7 @@ function OptInAnalyticsScreen({ onDone }) {
29518
29674
 
29519
29675
  // packages/experience-design-system-cli/src/tui/settings/debug-mode/screen.tsx
29520
29676
  import { useEffect as useEffect26, useState as useState35 } from "react";
29521
- import { Box as Box50, Text as Text53, useInput as useInput15 } from "ink";
29677
+ import { Box as Box50, Text as Text53, useInput as useInput16 } from "ink";
29522
29678
  init_debug_mode_store();
29523
29679
  init_debug_store();
29524
29680
  import { jsx as jsx62, jsxs as jsxs51 } from "react/jsx-runtime";
@@ -29537,7 +29693,7 @@ function DebugModeScreen({ onDone }) {
29537
29693
  });
29538
29694
  });
29539
29695
  }, []);
29540
- useInput15((input, key) => {
29696
+ useInput16((input, key) => {
29541
29697
  if (loading) return;
29542
29698
  if (key.return || input === " ") {
29543
29699
  setEnabled((current) => {
@@ -29574,7 +29730,7 @@ function DebugModeScreen({ onDone }) {
29574
29730
  }
29575
29731
 
29576
29732
  // packages/experience-design-system-cli/app.tsx
29577
- import { Fragment as Fragment20, jsx as jsx63 } from "react/jsx-runtime";
29733
+ import { Fragment as Fragment21, jsx as jsx63 } from "react/jsx-runtime";
29578
29734
  function App({ importOptions, inkClear }) {
29579
29735
  const [screen, setScreen] = useState36("start");
29580
29736
  const [importExitCode, setImportExitCode] = useState36();
@@ -29743,7 +29899,7 @@ function App({ importOptions, inkClear }) {
29743
29899
  }
29744
29900
  );
29745
29901
  }
29746
- if (importDefaults === void 0) return /* @__PURE__ */ jsx63(Fragment20, {});
29902
+ if (importDefaults === void 0) return /* @__PURE__ */ jsx63(Fragment21, {});
29747
29903
  if (projectPath === void 0) {
29748
29904
  return /* @__PURE__ */ jsx63(WelcomeScreen, { onContinue: setProjectPath, onQuit: leaveImport, initialPath: importDefaults.componentDir });
29749
29905
  }
@@ -29782,7 +29938,7 @@ function App({ importOptions, inkClear }) {
29782
29938
  }
29783
29939
  );
29784
29940
  }
29785
- return /* @__PURE__ */ jsx63(Fragment20, {});
29941
+ return /* @__PURE__ */ jsx63(Fragment21, {});
29786
29942
  }
29787
29943
  if (scopeRun && scopeFlow) {
29788
29944
  if (scopeFlow.status === "failed") {
@@ -29819,7 +29975,7 @@ function App({ importOptions, inkClear }) {
29819
29975
  }
29820
29976
  );
29821
29977
  }
29822
- return /* @__PURE__ */ jsx63(Fragment20, {});
29978
+ return /* @__PURE__ */ jsx63(Fragment21, {});
29823
29979
  }
29824
29980
  if (pendingLaunch?.cached) {
29825
29981
  return /* @__PURE__ */ jsx63(
package/dist/index.js CHANGED
@@ -33084,6 +33084,45 @@ var init_PageContainer3 = __esm({
33084
33084
  }
33085
33085
  });
33086
33086
 
33087
+ // packages/experience-design-system-cli/src/tui/settings/shared/debug-trail.ts
33088
+ import { existsSync as existsSync11 } from "node:fs";
33089
+ function diffSnapshots(before, after) {
33090
+ const names = [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])];
33091
+ const changed_fields = names.filter((name) => before[name] !== after[name]);
33092
+ return { changed: changed_fields.length > 0, changed_fields };
33093
+ }
33094
+ function createTrail(limit = DEFAULT_TRAIL_LIMIT) {
33095
+ const actions = [];
33096
+ return {
33097
+ record(action) {
33098
+ actions.push(action);
33099
+ if (actions.length > limit) actions.shift();
33100
+ },
33101
+ entries: () => [...actions]
33102
+ };
33103
+ }
33104
+ function scrubError(error, secrets) {
33105
+ let message = error instanceof Error ? error.message : String(error);
33106
+ for (const secret of secrets) {
33107
+ if (secret.length < MIN_SECRET_LENGTH) continue;
33108
+ for (const form of /* @__PURE__ */ new Set([secret, encodeURIComponent(secret)])) message = message.split(form).join("[redacted]");
33109
+ }
33110
+ return message;
33111
+ }
33112
+ function describeSettingsFile() {
33113
+ const path5 = configFilePath2();
33114
+ return { path: path5, eds_home_set: Boolean(process.env["EDS_HOME"]), exists: existsSync11(path5) };
33115
+ }
33116
+ var DEFAULT_TRAIL_LIMIT, MIN_SECRET_LENGTH;
33117
+ var init_debug_trail = __esm({
33118
+ "packages/experience-design-system-cli/src/tui/settings/shared/debug-trail.ts"() {
33119
+ "use strict";
33120
+ init_config();
33121
+ DEFAULT_TRAIL_LIMIT = 30;
33122
+ MIN_SECRET_LENGTH = 6;
33123
+ }
33124
+ });
33125
+
33087
33126
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-status.ts
33088
33127
  import { execFile as execFile2 } from "node:child_process";
33089
33128
  import { access as access6, constants } from "node:fs/promises";
@@ -33294,6 +33333,44 @@ var init_agent_status = __esm({
33294
33333
  }
33295
33334
  });
33296
33335
 
33336
+ // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-debug.ts
33337
+ function agentSnapshot(config) {
33338
+ const snapshot = { default_agent: config.defaultAgent };
33339
+ for (const name of AGENT_NAMES) {
33340
+ snapshot[`${name}.model`] = config.agents[name].model;
33341
+ snapshot[`${name}.binary`] = config.agents[name].binary;
33342
+ }
33343
+ return snapshot;
33344
+ }
33345
+ function environmentFrom(checks, savedBinary, envValue) {
33346
+ const path5 = checks.find((check) => check.step === "path");
33347
+ const version = checks.find((check) => check.step === "version");
33348
+ const overridden = envValue !== void 0 && envValue !== "" && envValue !== savedBinary.trim();
33349
+ return {
33350
+ found: path5?.ok === true,
33351
+ resolved_path: path5?.ok ? path5.detail : "",
33352
+ version: version?.ok ? version.detail : "",
33353
+ env_override: overridden ? envValue : ""
33354
+ };
33355
+ }
33356
+ async function describeAgents(config) {
33357
+ const entries = await Promise.all(
33358
+ AGENT_NAMES.map(async (name) => {
33359
+ const saved = config.agents[name].binary;
33360
+ const checks = await inspectAgent(name, saved);
33361
+ return [name, environmentFrom(checks, saved, process.env[`EDS_AGENT_BINARY_${name.toUpperCase()}`])];
33362
+ })
33363
+ );
33364
+ return Object.fromEntries(entries);
33365
+ }
33366
+ var init_agent_debug = __esm({
33367
+ "packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-debug.ts"() {
33368
+ "use strict";
33369
+ init_src4();
33370
+ init_agent_status();
33371
+ }
33372
+ });
33373
+
33297
33374
  // packages/experience-design-system-cli/src/tui/settings/shared/FieldForm.tsx
33298
33375
  import { useState as useState29 } from "react";
33299
33376
  import { Box as Box44, Text as Text46, useInput as useInput11 } from "ink";
@@ -33485,7 +33562,8 @@ function AgentDetail({
33485
33562
  name,
33486
33563
  config,
33487
33564
  setConfig,
33488
- onBack
33565
+ onBack,
33566
+ record: record3
33489
33567
  }) {
33490
33568
  const settings = config.agents[name];
33491
33569
  const [saving, setSaving] = useState30(false);
@@ -33508,10 +33586,12 @@ function AgentDetail({
33508
33586
  setNotice(null);
33509
33587
  writeAgentConfig(config).then(() => {
33510
33588
  applyBinaryOverrides(config);
33589
+ record3(`saved ${name}`);
33511
33590
  setNotice({ kind: "success", text: `Saved: ${AGENT_LABELS[name]} settings are written to config.json` });
33512
- }).catch(
33513
- (err) => setNotice({ kind: "error", text: `Not saved: ${err instanceof Error ? err.message : String(err)}` })
33514
- ).finally(() => setSaving(false));
33591
+ }).catch((err) => {
33592
+ record3(`saving ${name} failed`, err);
33593
+ setNotice({ kind: "error", text: `Not saved: ${err instanceof Error ? err.message : String(err)}` });
33594
+ }).finally(() => setSaving(false));
33515
33595
  };
33516
33596
  const runTest = () => {
33517
33597
  setTesting(true);
@@ -33519,12 +33599,15 @@ function AgentDetail({
33519
33599
  testAgent(name, settings.binary, settings.model).then((result) => {
33520
33600
  setChecks(result);
33521
33601
  const outcome = testOutcome(result);
33602
+ if (!outcome || !outcome.ok)
33603
+ record3(`test of ${name} failed: ${outcome?.text ?? "the agent could not be started"}`);
33522
33604
  setNotice(
33523
33605
  outcome ? { kind: outcome.ok ? "success" : "error", text: outcome.text } : { kind: "error", text: "Test not run: the agent could not be started (see Status)" }
33524
33606
  );
33525
- }).catch(
33526
- (err) => setNotice({ kind: "error", text: `Test could not run: ${err instanceof Error ? err.message : String(err)}` })
33527
- ).finally(() => setTesting(false));
33607
+ }).catch((err) => {
33608
+ record3(`test of ${name} could not run`, err);
33609
+ setNotice({ kind: "error", text: `Test could not run: ${err instanceof Error ? err.message : String(err)}` });
33610
+ }).finally(() => setTesting(false));
33528
33611
  };
33529
33612
  const action = {
33530
33613
  label: "Run test",
@@ -33545,6 +33628,7 @@ function AgentDetail({
33545
33628
  onChange: (key, value) => {
33546
33629
  setConfig({ ...config, agents: { ...config.agents, [name]: { ...settings, [key]: value } } });
33547
33630
  setNotice(null);
33631
+ record3(`edited ${name}.${key}`);
33548
33632
  },
33549
33633
  onSave: save,
33550
33634
  onQuit: onBack,
@@ -33578,7 +33662,7 @@ var init_AgentDetail = __esm({
33578
33662
  });
33579
33663
 
33580
33664
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentsScreen.tsx
33581
- import { useEffect as useEffect23, useState as useState31 } from "react";
33665
+ import { useEffect as useEffect23, useRef as useRef13, useState as useState31 } from "react";
33582
33666
  import { Box as Box46, Text as Text49, useInput as useInput12 } from "ink";
33583
33667
  import { Fragment as Fragment18, jsx as jsx58, jsxs as jsxs47 } from "react/jsx-runtime";
33584
33668
  function AgentsScreen({ onDone }) {
@@ -33589,6 +33673,12 @@ function AgentsScreen({ onDone }) {
33589
33673
  const [saving, setSaving] = useState31(false);
33590
33674
  const [loadError, setLoadError] = useState31(null);
33591
33675
  const [checks, setChecks] = useState31({});
33676
+ const visit = useRef13({
33677
+ trail: createTrail(),
33678
+ before: void 0,
33679
+ environment: void 0,
33680
+ error: ""
33681
+ }).current;
33592
33682
  const inspectAll = (loaded) => {
33593
33683
  for (const name of AGENT_NAMES) {
33594
33684
  void inspectAgent(name, loaded.agents[name].binary).then(
@@ -33600,14 +33690,38 @@ function AgentsScreen({ onDone }) {
33600
33690
  readAgentConfig().then((loaded) => {
33601
33691
  setConfig(loaded);
33602
33692
  inspectAll(loaded);
33693
+ visit.before = agentSnapshot(loaded);
33603
33694
  startDebugRun({
33604
33695
  flow: "settings/agents",
33605
33696
  step: "01-agents",
33606
33697
  menuOption: "Configuration > Agents",
33607
- inputs: { default_agent: loaded.defaultAgent }
33698
+ inputs: { ...visit.before, default_chosen: loaded.defaultChosen ?? false }
33699
+ });
33700
+ void describeAgents(loaded).then((found) => {
33701
+ visit.environment = found;
33608
33702
  });
33609
- }).catch((err) => setLoadError(err instanceof Error ? err.message : String(err)));
33703
+ }).catch((err) => {
33704
+ visit.error = scrubError(err, []);
33705
+ startDebugRun({ flow: "settings/agents", step: "01-agents", menuOption: "Configuration > Agents", inputs: {} });
33706
+ setLoadError(visit.error);
33707
+ });
33610
33708
  }, []);
33709
+ const endVisit = (current) => {
33710
+ const after = current ? agentSnapshot(current) : void 0;
33711
+ const change = visit.before && after ? diffSnapshots(visit.before, after) : { changed: false, changed_fields: [] };
33712
+ void finishDebugRun({
33713
+ outputs: {
33714
+ ...after ?? {},
33715
+ ...change,
33716
+ actions: visit.trail.entries(),
33717
+ // Missing only when the user left before the probes finished.
33718
+ ...visit.environment ? { agents: visit.environment } : {},
33719
+ ...visit.error ? { error: visit.error } : {}
33720
+ },
33721
+ status: visit.error ? "error" : "success",
33722
+ exitMethod: change.changed ? "saved" : "discarded"
33723
+ });
33724
+ };
33611
33725
  const makeDefault = (name) => {
33612
33726
  if (!config || saving) return;
33613
33727
  if (name === config.defaultAgent) {
@@ -33619,15 +33733,16 @@ function AgentsScreen({ onDone }) {
33619
33733
  writeAgentConfig({ ...config, defaultAgent: name, defaultChosen: true }).then(() => {
33620
33734
  setConfig((current) => current ? { ...current, defaultAgent: name, defaultChosen: true } : current);
33621
33735
  setNotice({ kind: "success", text: `Default agent is now ${AGENT_LABELS[name]}` });
33622
- }).catch(
33623
- (err) => setNotice({
33624
- kind: "error",
33625
- text: `Default agent not changed: ${err instanceof Error ? err.message : String(err)}`
33626
- })
33627
- ).finally(() => setSaving(false));
33736
+ visit.trail.record(`made ${name} the default`);
33737
+ }).catch((err) => {
33738
+ visit.error = scrubError(err, []);
33739
+ visit.trail.record(`making ${name} the default failed`);
33740
+ setNotice({ kind: "error", text: `Default agent not changed: ${visit.error}` });
33741
+ }).finally(() => setSaving(false));
33628
33742
  };
33629
33743
  useInput12((input, key) => {
33630
33744
  if (loadError && (key.escape || input === "q")) {
33745
+ endVisit(null);
33631
33746
  onDone();
33632
33747
  return;
33633
33748
  }
@@ -33652,7 +33767,7 @@ function AgentsScreen({ onDone }) {
33652
33767
  return;
33653
33768
  }
33654
33769
  if (key.escape || input === "q") {
33655
- void finishDebugRun({ outputs: { default_agent: config.defaultAgent }, status: "success", exitMethod: "saved" });
33770
+ endVisit(config);
33656
33771
  onDone();
33657
33772
  }
33658
33773
  });
@@ -33677,6 +33792,10 @@ function AgentsScreen({ onDone }) {
33677
33792
  name: open,
33678
33793
  config,
33679
33794
  setConfig,
33795
+ record: (action, error) => {
33796
+ visit.trail.record(action);
33797
+ if (error !== void 0) visit.error = scrubError(error, []);
33798
+ },
33680
33799
  onBack: () => {
33681
33800
  setOpen(null);
33682
33801
  void readAgentConfig().then((saved) => {
@@ -33749,6 +33868,8 @@ var init_AgentsScreen = __esm({
33749
33868
  init_theme();
33750
33869
  init_control_hints();
33751
33870
  init_debug_store();
33871
+ init_debug_trail();
33872
+ init_agent_debug();
33752
33873
  init_agent_store();
33753
33874
  init_agent_status();
33754
33875
  init_AgentDetail();
@@ -33790,9 +33911,9 @@ var init_project_store = __esm({
33790
33911
  });
33791
33912
 
33792
33913
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/ProjectScreen.tsx
33793
- import { useEffect as useEffect24, useState as useState32 } from "react";
33794
- import { Box as Box47, Text as Text50 } from "ink";
33795
- import { jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
33914
+ import { useEffect as useEffect24, useRef as useRef14, useState as useState32 } from "react";
33915
+ import { Box as Box47, Text as Text50, useInput as useInput13 } from "ink";
33916
+ import { Fragment as Fragment19, jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
33796
33917
  function debugFields(config) {
33797
33918
  return {
33798
33919
  space_id: config?.spaceId ?? "",
@@ -33808,40 +33929,88 @@ function ProjectScreen({ onDone }) {
33808
33929
  const [config, setConfig] = useState32(null);
33809
33930
  const [saving, setSaving] = useState32(false);
33810
33931
  const [notice, setNotice] = useState32(null);
33811
- const [lastSaved, setLastSaved] = useState32(null);
33932
+ const [loadError, setLoadError] = useState32(null);
33933
+ const visit = useRef14({
33934
+ trail: createTrail(),
33935
+ before: void 0,
33936
+ persisted: void 0,
33937
+ error: ""
33938
+ }).current;
33939
+ const scrub = (err) => scrubError(err, [config?.cmaToken ?? ""]);
33812
33940
  useEffect24(() => {
33813
- void readCredentials().then((cfg) => {
33941
+ readCredentials().then((cfg) => {
33814
33942
  setConfig(cfg);
33943
+ visit.before = snapshotOf(cfg);
33944
+ visit.persisted = visit.before;
33815
33945
  startDebugRun({
33816
33946
  flow: "settings/contentful-configuration",
33817
33947
  step: "01-configuration",
33818
33948
  menuOption: "Configuration > Project",
33819
- inputs: debugFields(cfg)
33949
+ inputs: { ...debugFields(cfg), settings_file: describeSettingsFile() }
33820
33950
  });
33951
+ }).catch((err) => {
33952
+ visit.error = scrubError(err, []);
33953
+ startDebugRun({
33954
+ flow: "settings/contentful-configuration",
33955
+ step: "01-configuration",
33956
+ menuOption: "Configuration > Project",
33957
+ inputs: { settings_file: describeSettingsFile() }
33958
+ });
33959
+ setLoadError(visit.error);
33821
33960
  });
33822
33961
  }, []);
33962
+ const endVisit = (current) => {
33963
+ const onScreen = current ? snapshotOf(current) : void 0;
33964
+ const change = visit.before && visit.persisted ? diffSnapshots(visit.before, visit.persisted) : { changed: false, changed_fields: [] };
33965
+ const unsaved = visit.persisted && onScreen ? diffSnapshots(visit.persisted, onScreen) : { changed: false, changed_fields: [] };
33966
+ void finishDebugRun({
33967
+ outputs: {
33968
+ ...debugFields(current),
33969
+ ...change,
33970
+ unsaved_edits: unsaved.changed_fields,
33971
+ actions: visit.trail.entries(),
33972
+ ...visit.error ? { error: visit.error } : {}
33973
+ },
33974
+ status: visit.error ? "error" : "success",
33975
+ exitMethod: change.changed ? "saved" : "discarded"
33976
+ });
33977
+ };
33978
+ useInput13((input, key) => {
33979
+ if (!config && loadError && (key.escape || input === "q")) {
33980
+ endVisit(null);
33981
+ onDone();
33982
+ }
33983
+ });
33823
33984
  if (!config) {
33824
33985
  return /* @__PURE__ */ jsxs48(Box47, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
33825
33986
  /* @__PURE__ */ jsx59(Text50, { bold: true, children: "Project" }),
33826
33987
  /* @__PURE__ */ jsx59(Text50, { children: " " }),
33827
- /* @__PURE__ */ jsx59(Text50, { color: PALETTE.accent, children: "Loading..." })
33988
+ loadError ? /* @__PURE__ */ jsxs48(Fragment19, { children: [
33989
+ /* @__PURE__ */ jsxs48(Text50, { color: PALETTE.error, children: [
33990
+ "\u2717 Could not read the settings: ",
33991
+ loadError
33992
+ ] }),
33993
+ /* @__PURE__ */ jsx59(Text50, { children: " " }),
33994
+ /* @__PURE__ */ jsx59(ControlHints, { hints: [{ keys: "Esc/q", label: "back" }] })
33995
+ ] }) : /* @__PURE__ */ jsx59(Text50, { color: PALETTE.accent, children: "Loading..." })
33828
33996
  ] });
33829
33997
  }
33830
33998
  const save = () => {
33831
33999
  setSaving(true);
33832
34000
  setNotice(null);
33833
34001
  writeCredentials(config).then(() => {
33834
- setLastSaved(config);
34002
+ visit.persisted = snapshotOf(config);
34003
+ visit.trail.record("saved");
33835
34004
  if (config.contentfulBaseDir) void startDebugSessionIn(config.contentfulBaseDir, { replace: true });
33836
34005
  setNotice({ kind: "success", text: "Project settings saved" });
33837
34006
  }).catch((err) => {
34007
+ visit.error = scrub(err);
34008
+ visit.trail.record("save failed");
33838
34009
  setNotice({ kind: "error", text: err instanceof Error ? err.message : "Failed to save" });
33839
34010
  }).finally(() => setSaving(false));
33840
34011
  };
33841
34012
  const quit = () => {
33842
- void finishDebugRun(
33843
- lastSaved ? { outputs: debugFields(lastSaved), status: "success", exitMethod: "saved" } : { outputs: {}, status: "success", exitMethod: "discarded" }
33844
- );
34013
+ endVisit(config);
33845
34014
  onDone();
33846
34015
  };
33847
34016
  return /* @__PURE__ */ jsx59(
@@ -33854,6 +34023,7 @@ function ProjectScreen({ onDone }) {
33854
34023
  onChange: (key, value) => {
33855
34024
  setConfig({ ...config, [key]: value });
33856
34025
  setNotice(null);
34026
+ visit.trail.record(`edited ${key}`);
33857
34027
  },
33858
34028
  onSave: save,
33859
34029
  onQuit: quit,
@@ -33862,12 +34032,14 @@ function ProjectScreen({ onDone }) {
33862
34032
  }
33863
34033
  );
33864
34034
  }
33865
- var FIELDS2, valuesOf;
34035
+ var FIELDS2, snapshotOf, valuesOf;
33866
34036
  var init_ProjectScreen = __esm({
33867
34037
  "packages/experience-design-system-cli/src/tui/settings/configuration/project/ProjectScreen.tsx"() {
33868
34038
  "use strict";
33869
34039
  init_theme();
34040
+ init_control_hints();
33870
34041
  init_debug_store();
34042
+ init_debug_trail();
33871
34043
  init_FieldForm();
33872
34044
  init_project_store();
33873
34045
  FIELDS2 = [
@@ -33879,19 +34051,20 @@ var init_ProjectScreen = __esm({
33879
34051
  { key: "defaultTokenFile", label: "Default token file" },
33880
34052
  { key: "contentfulBaseDir", label: "Root Project Directory", hint: "(where .contentful goes)" }
33881
34053
  ];
34054
+ snapshotOf = (config) => Object.fromEntries(FIELDS2.map(({ key }) => [key, config[key] || ""]));
33882
34055
  valuesOf = (config) => Object.fromEntries(FIELDS2.map(({ key }) => [key, config[key] || ""]));
33883
34056
  }
33884
34057
  });
33885
34058
 
33886
34059
  // packages/experience-design-system-cli/src/tui/settings/configuration/ConfigurationScreen.tsx
33887
34060
  import { useState as useState33 } from "react";
33888
- import { Box as Box48, Text as Text51, useInput as useInput13 } from "ink";
34061
+ import { Box as Box48, Text as Text51, useInput as useInput14 } from "ink";
33889
34062
  import { jsx as jsx60, jsxs as jsxs49 } from "react/jsx-runtime";
33890
34063
  function ConfigurationScreen({ onDone }) {
33891
34064
  const [focus, setFocus] = useState33(0);
33892
34065
  const [open, setOpen] = useState33(null);
33893
34066
  const close = () => setOpen(null);
33894
- useInput13((input, key) => {
34067
+ useInput14((input, key) => {
33895
34068
  if (open) return;
33896
34069
  if (key.upArrow) {
33897
34070
  setFocus((i) => (i - 1 + SECTIONS.length) % SECTIONS.length);
@@ -33951,8 +34124,8 @@ var init_ConfigurationScreen = __esm({
33951
34124
 
33952
34125
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/screen.tsx
33953
34126
  import { useEffect as useEffect25, useState as useState34 } from "react";
33954
- import { Box as Box49, Text as Text52, useInput as useInput14 } from "ink";
33955
- import { Fragment as Fragment19, jsx as jsx61, jsxs as jsxs50 } from "react/jsx-runtime";
34127
+ import { Box as Box49, Text as Text52, useInput as useInput15 } from "ink";
34128
+ import { Fragment as Fragment20, jsx as jsx61, jsxs as jsxs50 } from "react/jsx-runtime";
33956
34129
  function OptInAnalyticsScreen({ onDone }) {
33957
34130
  const [loading, setLoading] = useState34(true);
33958
34131
  const [enabled, setEnabled] = useState34(true);
@@ -33968,7 +34141,7 @@ function OptInAnalyticsScreen({ onDone }) {
33968
34141
  });
33969
34142
  });
33970
34143
  }, []);
33971
- useInput14((input, key) => {
34144
+ useInput15((input, key) => {
33972
34145
  if (loading) return;
33973
34146
  if (key.return || input === " ") {
33974
34147
  setEnabled((current) => {
@@ -33986,7 +34159,7 @@ function OptInAnalyticsScreen({ onDone }) {
33986
34159
  return /* @__PURE__ */ jsxs50(Box49, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
33987
34160
  /* @__PURE__ */ jsx61(Text52, { bold: true, children: "Settings \u203A Opt-in Analytics" }),
33988
34161
  /* @__PURE__ */ jsx61(Text52, { children: " " }),
33989
- loading ? /* @__PURE__ */ jsx61(Text52, { color: PALETTE.muted, children: "Loading\u2026" }) : /* @__PURE__ */ jsxs50(Fragment19, { children: [
34162
+ loading ? /* @__PURE__ */ jsx61(Text52, { color: PALETTE.muted, children: "Loading\u2026" }) : /* @__PURE__ */ jsxs50(Fragment20, { children: [
33990
34163
  /* @__PURE__ */ jsxs50(Text52, { children: [
33991
34164
  "Share anonymous usage data:",
33992
34165
  " ",
@@ -34020,7 +34193,7 @@ var init_screen11 = __esm({
34020
34193
 
34021
34194
  // packages/experience-design-system-cli/src/tui/settings/debug-mode/screen.tsx
34022
34195
  import { useEffect as useEffect26, useState as useState35 } from "react";
34023
- import { Box as Box50, Text as Text53, useInput as useInput15 } from "ink";
34196
+ import { Box as Box50, Text as Text53, useInput as useInput16 } from "ink";
34024
34197
  import { jsx as jsx62, jsxs as jsxs51 } from "react/jsx-runtime";
34025
34198
  function DebugModeScreen({ onDone }) {
34026
34199
  const [loading, setLoading] = useState35(true);
@@ -34037,7 +34210,7 @@ function DebugModeScreen({ onDone }) {
34037
34210
  });
34038
34211
  });
34039
34212
  }, []);
34040
- useInput15((input, key) => {
34213
+ useInput16((input, key) => {
34041
34214
  if (loading) return;
34042
34215
  if (key.return || input === " ") {
34043
34216
  setEnabled((current) => {
@@ -34089,7 +34262,7 @@ __export(app_exports, {
34089
34262
  });
34090
34263
  import { join as join27 } from "node:path";
34091
34264
  import { useEffect as useEffect27, useState as useState36 } from "react";
34092
- import { Fragment as Fragment20, jsx as jsx63 } from "react/jsx-runtime";
34265
+ import { Fragment as Fragment21, jsx as jsx63 } from "react/jsx-runtime";
34093
34266
  function App({ importOptions, inkClear }) {
34094
34267
  const [screen, setScreen] = useState36("start");
34095
34268
  const [importExitCode, setImportExitCode] = useState36();
@@ -34258,7 +34431,7 @@ function App({ importOptions, inkClear }) {
34258
34431
  }
34259
34432
  );
34260
34433
  }
34261
- if (importDefaults === void 0) return /* @__PURE__ */ jsx63(Fragment20, {});
34434
+ if (importDefaults === void 0) return /* @__PURE__ */ jsx63(Fragment21, {});
34262
34435
  if (projectPath === void 0) {
34263
34436
  return /* @__PURE__ */ jsx63(WelcomeScreen, { onContinue: setProjectPath, onQuit: leaveImport, initialPath: importDefaults.componentDir });
34264
34437
  }
@@ -34297,7 +34470,7 @@ function App({ importOptions, inkClear }) {
34297
34470
  }
34298
34471
  );
34299
34472
  }
34300
- return /* @__PURE__ */ jsx63(Fragment20, {});
34473
+ return /* @__PURE__ */ jsx63(Fragment21, {});
34301
34474
  }
34302
34475
  if (scopeRun && scopeFlow) {
34303
34476
  if (scopeFlow.status === "failed") {
@@ -34334,7 +34507,7 @@ function App({ importOptions, inkClear }) {
34334
34507
  }
34335
34508
  );
34336
34509
  }
34337
- return /* @__PURE__ */ jsx63(Fragment20, {});
34510
+ return /* @__PURE__ */ jsx63(Fragment21, {});
34338
34511
  }
34339
34512
  if (pendingLaunch?.cached) {
34340
34513
  return /* @__PURE__ */ jsx63(
@@ -34514,7 +34687,7 @@ function createProgram() {
34514
34687
  // packages/experience-design-system-cli/src/config/migrate.ts
34515
34688
  init_config();
34516
34689
  init_package_root();
34517
- import { existsSync as existsSync11 } from "node:fs";
34690
+ import { existsSync as existsSync12 } from "node:fs";
34518
34691
  import { mkdir as mkdir10, readFile as readFile19, rename as rename3, rmdir, unlink } from "node:fs/promises";
34519
34692
  import { dirname as dirname16, join as join29 } from "node:path";
34520
34693
  var PACKAGE_NAME3 = "@contentful/experience-design-system-cli";
@@ -34540,8 +34713,8 @@ async function migrateOldConfig() {
34540
34713
  if (debugMode) migrated["debugMode"] = debugMode;
34541
34714
  Object.assign(migrated, await readJson(oldCredentials));
34542
34715
  if (Object.keys(migrated).length > 0) await updateSettings2({ ...migrated, ...await readSettings2() });
34543
- if (existsSync11(oldRuns)) {
34544
- if (!existsSync11(runsFilePath2())) {
34716
+ if (existsSync12(oldRuns)) {
34717
+ if (!existsSync12(runsFilePath2())) {
34545
34718
  await mkdir10(dirname16(runsFilePath2()), { recursive: true });
34546
34719
  await rename3(oldRuns, runsFilePath2());
34547
34720
  } else {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.37.1-dev-build-2582b468.0",
3
+ "version": "2.37.1-dev-build-414ce827.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -5,9 +5,11 @@ import { type AgentConfig } from './agent-store.js';
5
5
  * One agent's screen: what was found for it, its two settings, and the test. Saving writes the whole agent config, so the
6
6
  * default agent and the other agents are kept. The test uses the values on screen, saved or not.
7
7
  */
8
- export declare function AgentDetail({ name, config, setConfig, onBack, }: {
8
+ export declare function AgentDetail({ name, config, setConfig, onBack, record, }: {
9
9
  name: AgentName;
10
10
  config: AgentConfig;
11
11
  setConfig: (config: AgentConfig) => void;
12
12
  onBack: () => void;
13
+ /** Adds a line to the debug log of this visit. The Agents list owns the log, since it is the screen that is open. */
14
+ record: (action: string, error?: unknown) => void;
13
15
  }): React.ReactElement;
@@ -0,0 +1,25 @@
1
+ import { type AgentName } from '@contentful/experience-design-system-backend-pipeline';
2
+ import type { Snapshot } from '../../shared/debug-trail.js';
3
+ import type { AgentConfig } from './agent-store.js';
4
+ /**
5
+ * The agent settings as flat text, for `diffSnapshots`: `default_agent`, then `<agent>.model` and `<agent>.binary` for
6
+ * every agent. Taken on entry and on exit, so the log shows what a visit changed.
7
+ */
8
+ export declare function agentSnapshot(config: AgentConfig): Snapshot;
9
+ export type AgentEnvironment = {
10
+ /** Whether the command was found, and the path it resolved to. */
11
+ found: boolean;
12
+ resolved_path: string;
13
+ version: string;
14
+ /**
15
+ * The `EDS_AGENT_BINARY_<AGENT>` variable when it points somewhere other than the saved path. Settings copies a saved
16
+ * path into that variable at startup, so a value equal to the saved path is not an override and is left out.
17
+ */
18
+ env_override: string;
19
+ };
20
+ /**
21
+ * What is true about each agent on this machine, so "my agent does not work" can be read from the log: found or not,
22
+ * where, its version, and whether a shell variable is overriding the saved path. Uses the quick checks only, so it never
23
+ * spends a prompt.
24
+ */
25
+ export declare function describeAgents(config: AgentConfig): Promise<Record<AgentName, AgentEnvironment>>;
@@ -0,0 +1,35 @@
1
+ /** One screen's values, as flat text, taken when the screen opens and again when it closes. */
2
+ export type Snapshot = Record<string, string>;
3
+ export type Change = {
4
+ changed: boolean;
5
+ /** Field names that differ between the two snapshots, in the order the fields appear. Never the values. */
6
+ changed_fields: string[];
7
+ };
8
+ /**
9
+ * Compares the snapshot taken on entry with the one taken on exit. A field that exists in only one of them counts as
10
+ * changed. Only names are returned, so the result can be logged without repeating a secret.
11
+ */
12
+ export declare function diffSnapshots(before: Snapshot, after: Snapshot): Change;
13
+ export type Trail = {
14
+ /** Adds one action, such as `edited spaceId`, `saved`, or `made Codex the default`. Field names, never values. */
15
+ record(action: string): void;
16
+ /** The recorded actions in order, oldest first. Once the limit is passed the oldest are dropped. */
17
+ entries(): string[];
18
+ };
19
+ /** Keeps the last `limit` actions of a visit, so a long visit cannot make the log grow without bound. */
20
+ export declare function createTrail(limit?: number): Trail;
21
+ /**
22
+ * The message of a failed load or save, safe to log: any of `secrets` that appears in it is replaced. The CMA token is
23
+ * only redacted by key elsewhere, so an error that quotes it would otherwise get through. A secret is matched as it is
24
+ * and as it looks URL-encoded, which is how it appears in a request error.
25
+ */
26
+ export declare function scrubError(error: unknown, secrets: readonly string[]): string;
27
+ /**
28
+ * Where Project's settings live and what could be moving them: the settings file path, whether `EDS_HOME` relocated it,
29
+ * and whether the file exists. Facts for the log, with no values read from the file.
30
+ */
31
+ export declare function describeSettingsFile(): {
32
+ path: string;
33
+ eds_home_set: boolean;
34
+ exists: boolean;
35
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.37.1-dev-build-2582b468.0",
3
+ "version": "2.37.1-dev-build-414ce827.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,8 +45,8 @@
45
45
  "ts-morph": "^27.0.2",
46
46
  "typescript": "^5.9.3",
47
47
  "zod": "^4.6.5",
48
- "@contentful/experience-design-system-backend-pipeline": "2.37.1-dev-build-2582b468.0",
49
- "@contentful/experience-design-system-types": "2.37.1-dev-build-2582b468.0"
48
+ "@contentful/experience-design-system-types": "2.37.1-dev-build-414ce827.0",
49
+ "@contentful/experience-design-system-backend-pipeline": "2.37.1-dev-build-414ce827.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@tsconfig/node24": "^24.0.4",