@contentful/experience-design-system-cli 2.37.1-dev-build-4ce44f69.0 → 2.37.1-dev-build-f57f8147.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
@@ -837,7 +837,7 @@ async function runAgent(options) {
837
837
  promptLen: prompt.length,
838
838
  promptHead: prompt.slice(0, 500)
839
839
  });
840
- return new Promise((resolve26) => {
840
+ return new Promise((resolve27) => {
841
841
  const bedrockEnv = bedrock ? BEDROCK_ENV_BY_AGENT[agent] : void 0;
842
842
  const child = spawn(binary, args, {
843
843
  stdio: ["pipe", "pipe", "pipe"],
@@ -886,7 +886,7 @@ async function runAgent(options) {
886
886
  stderrLen: stderr.length,
887
887
  stderrTail: stderr.slice(-1e3)
888
888
  });
889
- resolve26(result);
889
+ resolve27(result);
890
890
  });
891
891
  });
892
892
  }
@@ -908,7 +908,7 @@ async function checkAgentAuth(agent) {
908
908
  return "not-found";
909
909
  if (agent !== "claude")
910
910
  return "ok";
911
- return new Promise((resolve26) => {
911
+ return new Promise((resolve27) => {
912
912
  const child = spawn2(binary, ["auth", "status", "--json"], { stdio: ["ignore", "pipe", "pipe"] });
913
913
  let stdout = "";
914
914
  let done = false;
@@ -917,7 +917,7 @@ async function checkAgentAuth(agent) {
917
917
  return;
918
918
  done = true;
919
919
  child.kill("SIGTERM");
920
- resolve26("unauthenticated");
920
+ resolve27("unauthenticated");
921
921
  }, 5e3);
922
922
  child.stdout?.on("data", (chunk) => {
923
923
  stdout += chunk.toString();
@@ -928,14 +928,14 @@ async function checkAgentAuth(agent) {
928
928
  done = true;
929
929
  clearTimeout(timer);
930
930
  if (code !== 0) {
931
- resolve26("unauthenticated");
931
+ resolve27("unauthenticated");
932
932
  return;
933
933
  }
934
934
  try {
935
935
  const status = JSON.parse(stdout);
936
- resolve26(status.loggedIn ? "ok" : "unauthenticated");
936
+ resolve27(status.loggedIn ? "ok" : "unauthenticated");
937
937
  } catch {
938
- resolve26("unauthenticated");
938
+ resolve27("unauthenticated");
939
939
  }
940
940
  });
941
941
  });
@@ -945,9 +945,9 @@ async function binaryIsOnPath(binary) {
945
945
  const fs = await import("node:fs/promises");
946
946
  return fs.access(binary).then(() => true, () => false);
947
947
  }
948
- return new Promise((resolve26) => {
948
+ return new Promise((resolve27) => {
949
949
  const child = spawn2(process.platform === "win32" ? "where" : "which", [binary], { stdio: "ignore" });
950
- child.on("close", (code) => resolve26(code === 0));
950
+ child.on("close", (code) => resolve27(code === 0));
951
951
  });
952
952
  }
953
953
  var init_check_agent_auth = __esm({
@@ -2141,11 +2141,12 @@ __export(debug_store_exports, {
2141
2141
  currentSessionDir: () => currentSessionDir,
2142
2142
  finishDebugRun: () => finishDebugRun,
2143
2143
  startDebugRun: () => startDebugRun,
2144
- startDebugSessionIn: () => startDebugSessionIn
2144
+ startDebugSessionIn: () => startDebugSessionIn,
2145
+ startImportRun: () => startImportRun
2145
2146
  });
2146
- import { mkdir as mkdir8, writeFile as writeFile7 } from "node:fs/promises";
2147
- import { existsSync as existsSync9, readdirSync as readdirSync3 } from "node:fs";
2148
- import { join as join20, resolve as resolve23 } from "node:path";
2147
+ import { mkdir as mkdir9, writeFile as writeFile8 } from "node:fs/promises";
2148
+ import { existsSync as existsSync9, readdirSync as readdirSync4 } from "node:fs";
2149
+ import { join as join24, resolve as resolve26 } from "node:path";
2149
2150
  function generateRunId() {
2150
2151
  const now = /* @__PURE__ */ new Date();
2151
2152
  const pad = (n) => String(n).padStart(2, "0");
@@ -2161,14 +2162,14 @@ function slugify(value) {
2161
2162
  async function readBaseDir() {
2162
2163
  try {
2163
2164
  const dir = (await readSettings2())["contentfulBaseDir"];
2164
- return typeof dir === "string" && dir !== "" ? resolve23(dir) : void 0;
2165
+ return typeof dir === "string" && dir !== "" ? resolve26(dir) : void 0;
2165
2166
  } catch {
2166
2167
  return void 0;
2167
2168
  }
2168
2169
  }
2169
2170
  function nextSessionId(contentfulDir) {
2170
2171
  const today = datePrefix();
2171
- const existing = existsSync9(contentfulDir) ? readdirSync3(contentfulDir) : [];
2172
+ const existing = existsSync9(contentfulDir) ? readdirSync4(contentfulDir) : [];
2172
2173
  const seqPattern = new RegExp(`^${today}-session-(\\d+)$`);
2173
2174
  const maxSeq = existing.reduce((max, entry) => {
2174
2175
  const match2 = seqPattern.exec(entry);
@@ -2177,12 +2178,12 @@ function nextSessionId(contentfulDir) {
2177
2178
  return `${today}-session-${maxSeq + 1}`;
2178
2179
  }
2179
2180
  function startSession(base, { replace }) {
2180
- const contentfulDir = join20(base, ".contentful");
2181
- if (replace || !sessionDir) sessionDir = join20(contentfulDir, nextSessionId(contentfulDir));
2181
+ const contentfulDir = join24(base, ".contentful");
2182
+ if (replace || !sessionDir) sessionDir = join24(contentfulDir, nextSessionId(contentfulDir));
2182
2183
  return sessionDir;
2183
2184
  }
2184
2185
  function currentDebugSessionDir() {
2185
- return sessionDir && join20(sessionDir, "debug");
2186
+ return sessionDir && join24(sessionDir, "debug");
2186
2187
  }
2187
2188
  function currentSessionDir() {
2188
2189
  return sessionDir;
@@ -2190,10 +2191,25 @@ function currentSessionDir() {
2190
2191
  async function startDebugSessionIn(base, { replace = false } = {}) {
2191
2192
  try {
2192
2193
  if (!await isDebugModeEnabled()) return;
2193
- await mkdir8(join20(startSession(resolve23(base), { replace }), "debug"), { recursive: true });
2194
+ await mkdir9(join24(startSession(resolve26(base), { replace }), "debug"), { recursive: true });
2194
2195
  } catch {
2195
2196
  }
2196
2197
  }
2198
+ function nextImportRunDir(session) {
2199
+ const existing = existsSync9(session) ? readdirSync4(session) : [];
2200
+ const maxRun = existing.reduce((max, entry) => {
2201
+ const match2 = /^dsi-import-(\d+)$/.exec(entry);
2202
+ return match2 ? Math.max(max, Number(match2[1])) : max;
2203
+ }, 0);
2204
+ return join24(session, `dsi-import-${maxRun + 1}`);
2205
+ }
2206
+ async function startImportRun(projectPath) {
2207
+ await startDebugSessionIn(projectPath);
2208
+ if (!sessionDir) return join24(resolve26(projectPath), ".contentful");
2209
+ const dir = nextImportRunDir(sessionDir);
2210
+ await mkdir9(dir, { recursive: true });
2211
+ return dir;
2212
+ }
2197
2213
  async function isDebugModeEnabled() {
2198
2214
  try {
2199
2215
  return (await readDebugModeSetting()).enabled;
@@ -2236,15 +2252,15 @@ async function finishDebugRun({
2236
2252
  const root = currentDebugSessionDir();
2237
2253
  if (!root) return;
2238
2254
  const flowSegments = run.flow.split("/");
2239
- const dir = join20(root, ...flowSegments);
2240
- await mkdir8(dir, { recursive: true });
2255
+ const dir = join24(root, ...flowSegments);
2256
+ await mkdir9(dir, { recursive: true });
2241
2257
  const filename = nextAttemptFilename(dir, run.menuOption);
2242
- await writeFile7(join20(dir, filename), content, { mode: 384 });
2258
+ await writeFile8(join24(dir, filename), content, { mode: 384 });
2243
2259
  }
2244
2260
  function nextAttemptFilename(dir, menuOption) {
2245
2261
  const today = datePrefix();
2246
2262
  const optionSlug = slugify(menuOption);
2247
- const existing = existsSync9(dir) ? readdirSync3(dir) : [];
2263
+ const existing = existsSync9(dir) ? readdirSync4(dir) : [];
2248
2264
  const attemptPattern = new RegExp(`^${today}-${optionSlug}-attempt-(\\d+)\\.md$`);
2249
2265
  const maxAttempt = existing.reduce((max, entry) => {
2250
2266
  const match2 = attemptPattern.exec(entry);
@@ -2292,7 +2308,7 @@ var init_debug_store = __esm({
2292
2308
  init_debug_mode_store();
2293
2309
  void Promise.all([isDebugModeEnabled(), readBaseDir()]).then(([enabled, base]) => {
2294
2310
  if (!enabled || !base) return void 0;
2295
- return mkdir8(join20(startSession(base, { replace: false }), "debug"), { recursive: true });
2311
+ return mkdir9(join24(startSession(base, { replace: false }), "debug"), { recursive: true });
2296
2312
  });
2297
2313
  process.once("uncaughtException", (err) => {
2298
2314
  void handleCrash().finally(() => {
@@ -13754,7 +13770,7 @@ function errorMessage(error) {
13754
13770
  return error instanceof Error && error.message ? error.message : String(error);
13755
13771
  }
13756
13772
  function defaultSleep(delayMs) {
13757
- return new Promise((resolve26) => setTimeout(resolve26, delayMs));
13773
+ return new Promise((resolve27) => setTimeout(resolve27, delayMs));
13758
13774
  }
13759
13775
 
13760
13776
  // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/sanitize-preview.js
@@ -13995,7 +14011,7 @@ var ApiClient = class {
13995
14011
  const progress = maxAttempts > 1 ? attempt / (maxAttempts - 1) : 0;
13996
14012
  const baseDelay = intervalMs + (maxIntervalMs - intervalMs) * progress;
13997
14013
  const jitter = Math.random() * baseDelay * 0.15;
13998
- await new Promise((resolve26) => setTimeout(resolve26, baseDelay + jitter));
14014
+ await new Promise((resolve27) => setTimeout(resolve27, baseDelay + jitter));
13999
14015
  }
14000
14016
  }
14001
14017
  throw new Error(`Operation ${operationId} timed out after ${maxAttempts} attempts`);
@@ -16108,7 +16124,7 @@ async function retryDatabaseWrite(operation, attempts = 8) {
16108
16124
  const message = error instanceof Error ? error.message : String(error);
16109
16125
  if (!/database is locked|database is busy/i.test(message) || attempt >= attempts)
16110
16126
  throw error;
16111
- await new Promise((resolve26) => setTimeout(resolve26, Math.min(250 * attempt, 2e3)));
16127
+ await new Promise((resolve27) => setTimeout(resolve27, Math.min(250 * attempt, 2e3)));
16112
16128
  }
16113
16129
  }
16114
16130
  }
@@ -17280,11 +17296,34 @@ function RunningStep({
17280
17296
  }
17281
17297
 
17282
17298
  // packages/experience-design-system-cli/src/tui/import/steps/05-token-reuse/logic.ts
17283
- import { access as access5, stat as stat4 } from "node:fs/promises";
17299
+ import { access as access5, readdir as readdir2, stat as stat4 } from "node:fs/promises";
17284
17300
  import { join as join16 } from "node:path";
17285
- async function inspectTokens({ rawTokensPath, outDir, cache }) {
17301
+ async function findLatestTokens(contentfulDir) {
17302
+ const candidates = [join16(contentfulDir, "tokens.json")];
17303
+ try {
17304
+ for (const session of await readdir2(contentfulDir, { withFileTypes: true })) {
17305
+ if (!session.isDirectory()) continue;
17306
+ for (const run of await readdir2(join16(contentfulDir, session.name), { withFileTypes: true }).catch(() => [])) {
17307
+ if (run.isDirectory() && /^dsi-import-\d+$/.test(run.name)) {
17308
+ candidates.push(join16(contentfulDir, session.name, run.name, "tokens.json"));
17309
+ }
17310
+ }
17311
+ }
17312
+ } catch {
17313
+ }
17314
+ const found = [];
17315
+ for (const path5 of candidates) {
17316
+ try {
17317
+ found.push({ path: path5, mtimeMs: (await stat4(path5)).mtimeMs });
17318
+ } catch {
17319
+ }
17320
+ }
17321
+ return found.sort((a, b) => b.mtimeMs - a.mtimeMs)[0]?.path ?? null;
17322
+ }
17323
+ async function inspectTokens({ rawTokensPath, contentfulDir, cache }) {
17286
17324
  if (!cache) return { kind: "none" };
17287
- const tokensPath = join16(outDir, "tokens.json");
17325
+ const tokensPath = await findLatestTokens(contentfulDir);
17326
+ if (!tokensPath) return { kind: "none" };
17288
17327
  try {
17289
17328
  await access5(tokensPath);
17290
17329
  } catch {
@@ -17563,7 +17602,7 @@ var realDeps = {
17563
17602
  var fail = (step, message) => ({ ok: false, step, message });
17564
17603
  var errorMessage2 = (error) => error instanceof Error ? error.message : String(error);
17565
17604
  async function runExtraction(input, onProgress, deps = realDeps) {
17566
- const outDir = join19(resolve22(input.projectPath), ".contentful");
17605
+ const outDir = input.outDir ?? join19(resolve22(input.projectPath), ".contentful");
17567
17606
  let progress = initialProgress({
17568
17607
  hasCredentials: input.credentials !== null,
17569
17608
  hasRawTokens: input.rawTokensPath !== "",
@@ -17712,7 +17751,6 @@ function useExtraction(input) {
17712
17751
  reuseTokens: input.tokenStrategy === "reuse"
17713
17752
  })
17714
17753
  });
17715
- void Promise.resolve().then(() => (init_debug_store(), debug_store_exports)).then((store) => store.startDebugSessionIn(input.projectPath));
17716
17754
  void runExtraction(input, (progress) => {
17717
17755
  if (current.current === input) setState({ status: "running", progress });
17718
17756
  }).then((result) => {
@@ -20599,7 +20637,7 @@ function GenerationScreen({
20599
20637
  import { useEffect as useEffect9, useRef as useRef6, useState as useState14 } from "react";
20600
20638
 
20601
20639
  // packages/experience-design-system-cli/src/tui/import/steps/08-generation/run.ts
20602
- import { resolve as resolve24 } from "node:path";
20640
+ import { resolve as resolve23 } from "node:path";
20603
20641
 
20604
20642
  // packages/experience-design-system-cli/src/tui/import/steps/08-generation/map-tokens.ts
20605
20643
  async function prepareMapTokens(input, generatedSessionId) {
@@ -20734,7 +20772,7 @@ async function buildMapTokensOptions(input) {
20734
20772
  if (mapOverride?.kind === "text") prompt.skillContentOverride = mapOverride.value;
20735
20773
  if (mapOverride?.kind === "path") {
20736
20774
  await resolvePromptOverride(mapOverride);
20737
- prompt.skillPathOverride = resolve24(mapOverride.value);
20775
+ prompt.skillPathOverride = resolve23(mapOverride.value);
20738
20776
  }
20739
20777
  if (input.existingEntitiesPath) {
20740
20778
  const entities = await readExistingContentfulEntitiesFromSession({ path: input.existingEntitiesPath });
@@ -20782,7 +20820,7 @@ function useGeneration(input) {
20782
20820
  }
20783
20821
 
20784
20822
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/build-flow-start.ts
20785
- import { join as join21 } from "node:path";
20823
+ import { join as join20 } from "node:path";
20786
20824
  function buildFlowStart(input) {
20787
20825
  const { extraction, handoff, saved, generatePromptPath } = input;
20788
20826
  const credentials = extraction.credentials ?? {
@@ -20795,7 +20833,7 @@ function buildFlowStart(input) {
20795
20833
  handoff,
20796
20834
  agent: extraction.agent,
20797
20835
  projectPath: extraction.projectPath,
20798
- outDir: extraction.projectPath ? join21(extraction.projectPath, ".contentful") : "",
20836
+ outDir: extraction.projectPath ? join20(extraction.projectPath, ".contentful") : "",
20799
20837
  rawTokensPath: extraction.rawTokensPath,
20800
20838
  credentials: {
20801
20839
  spaceId: credentials.spaceId,
@@ -26538,11 +26576,11 @@ function DoneStep({
26538
26576
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/screens/PathPrompt.tsx
26539
26577
  import { useState as useState24 } from "react";
26540
26578
  import { Box as Box37, Text as Text40 } from "ink";
26541
- import { readdirSync as readdirSync4 } from "node:fs";
26542
- import { dirname as dirname15, basename as basename6, join as join22 } from "node:path";
26579
+ import { readdirSync as readdirSync3 } from "node:fs";
26580
+ import { dirname as dirname15, basename as basename6, join as join21 } from "node:path";
26543
26581
 
26544
26582
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/path-utils.ts
26545
- import { resolve as resolve25 } from "node:path";
26583
+ import { resolve as resolve24 } from "node:path";
26546
26584
  import { homedir as homedir4 } from "node:os";
26547
26585
  function expandTilde(input) {
26548
26586
  if (input === "~" || input.startsWith("~/") || input.startsWith("~\\")) {
@@ -26556,7 +26594,7 @@ function normalizePath(input) {
26556
26594
  p = p.slice(1, -1);
26557
26595
  }
26558
26596
  p = expandTilde(p);
26559
- return resolve25(p);
26597
+ return resolve24(p);
26560
26598
  }
26561
26599
 
26562
26600
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/use-blinking-cursor.ts
@@ -26579,19 +26617,19 @@ function autocomplete(partial) {
26579
26617
  const base = basename6(normalized);
26580
26618
  let entries;
26581
26619
  try {
26582
- entries = readdirSync4(dir);
26620
+ entries = readdirSync3(dir);
26583
26621
  } catch {
26584
26622
  return null;
26585
26623
  }
26586
26624
  const matches = entries.filter((e) => e.startsWith(base));
26587
26625
  if (matches.length === 0) return null;
26588
- if (matches.length === 1) return join22(dir, matches[0]);
26626
+ if (matches.length === 1) return join21(dir, matches[0]);
26589
26627
  let prefix = matches[0];
26590
26628
  for (const m of matches) {
26591
26629
  while (!m.startsWith(prefix)) prefix = prefix.slice(0, -1);
26592
26630
  if (!prefix) break;
26593
26631
  }
26594
- return prefix.length > base.length ? join22(dir, prefix) : null;
26632
+ return prefix.length > base.length ? join21(dir, prefix) : null;
26595
26633
  }
26596
26634
  function PathPrompt({
26597
26635
  defaultPath,
@@ -27350,9 +27388,9 @@ import { useEffect as useEffect21, useRef as useRef13, useSyncExternalStore } fr
27350
27388
 
27351
27389
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/controller.ts
27352
27390
  import { appendFileSync, writeFileSync } from "node:fs";
27353
- import { mkdir as mkdir9 } from "node:fs/promises";
27391
+ import { mkdir as mkdir8 } from "node:fs/promises";
27354
27392
  import { tmpdir } from "node:os";
27355
- import { join as join24 } from "node:path";
27393
+ import { join as join23, resolve as resolve25 } from "node:path";
27356
27394
 
27357
27395
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/wizard-state-transitions.ts
27358
27396
  function shouldBypassPreview(state) {
@@ -27696,8 +27734,8 @@ function pushFailure(e, deps) {
27696
27734
  }
27697
27735
 
27698
27736
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/save.ts
27699
- import { writeFile as writeFile8 } from "node:fs/promises";
27700
- import { join as join23 } from "node:path";
27737
+ import { writeFile as writeFile7 } from "node:fs/promises";
27738
+ import { join as join22 } from "node:path";
27701
27739
 
27702
27740
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/run-print-files-helpers.ts
27703
27741
  function nextStateAfterPrint(opts) {
@@ -27720,15 +27758,15 @@ function buildSavedCDF(components, tokens, opts = {}) {
27720
27758
  var realSaveDeps = () => ({
27721
27759
  openDb: openPipelineDb,
27722
27760
  readTokens: (path5) => readTokensFromPath2({ flag: "tokens", path: path5 }),
27723
- writeFile: (path5, contents) => writeFile8(path5, contents),
27761
+ writeFile: (path5, contents) => writeFile7(path5, contents),
27724
27762
  appendRun,
27725
27763
  buildSourceFingerprint,
27726
- sleep: (ms) => new Promise((resolve26) => setTimeout(resolve26, ms)),
27764
+ sleep: (ms) => new Promise((resolve27) => setTimeout(resolve27, ms)),
27727
27765
  warn: (message) => process.stderr.write(message)
27728
27766
  });
27729
27767
  async function runPrintFiles(input, deps, onPatch) {
27730
27768
  onPatch({ step: "printing" });
27731
- const componentsPath = join23(input.outDir, "components.json");
27769
+ const componentsPath = join22(input.outDir, "components.json");
27732
27770
  try {
27733
27771
  const db = deps.openDb();
27734
27772
  let components = [];
@@ -27809,7 +27847,7 @@ var EMPTY_PUSH_RESULT = {
27809
27847
  };
27810
27848
 
27811
27849
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/controller.ts
27812
- var WIZARD_LOG = join24(tmpdir(), "experiences-import-wizard.log");
27850
+ var WIZARD_LOG = join23(tmpdir(), "experiences-import-wizard.log");
27813
27851
  function appendWizardLog(entry) {
27814
27852
  appendFileSync(WIZARD_LOG, JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry }) + "\n");
27815
27853
  }
@@ -27817,9 +27855,6 @@ function startWizardLog() {
27817
27855
  writeFileSync(WIZARD_LOG, `--- experiences import session ${(/* @__PURE__ */ new Date()).toISOString()} ---
27818
27856
  `);
27819
27857
  }
27820
- async function realSessionDir() {
27821
- return (await Promise.resolve().then(() => (init_debug_store(), debug_store_exports))).currentSessionDir();
27822
- }
27823
27858
  function realFlowDeps() {
27824
27859
  const log = (entry) => appendWizardLog(entry);
27825
27860
  return {
@@ -27835,14 +27870,13 @@ function realFlowDeps() {
27835
27870
  }
27836
27871
  },
27837
27872
  mkdir: async (path5) => {
27838
- await mkdir9(path5, { recursive: true });
27873
+ await mkdir8(path5, { recursive: true });
27839
27874
  },
27840
27875
  detectSaveConflict,
27841
27876
  buildTimestampedSubdir,
27842
27877
  rejectComponentsByName,
27843
27878
  log,
27844
- stderr: (text) => process.stderr.write(text),
27845
- sessionDir: realSessionDir
27879
+ stderr: (text) => process.stderr.write(text)
27846
27880
  };
27847
27881
  }
27848
27882
  function initialFlowState(start) {
@@ -27983,9 +28017,6 @@ function createFlowController(start, deps) {
27983
28017
  const startSave = (opts, patch = {}) => {
27984
28018
  pendingSave = opts;
27985
28019
  update({ ...patch, step: "path-prompt" });
27986
- void deps.sessionDir?.().then((session) => {
27987
- if (session && state.step === "path-prompt") update({ outDir: join24(session, "dsi-import") });
27988
- });
27989
28020
  };
27990
28021
  const proceedToWrite = async (path5) => {
27991
28022
  update({ outDir: path5 });
@@ -28058,7 +28089,7 @@ function createFlowController(start, deps) {
28058
28089
  },
28059
28090
  async submitPath(submitted) {
28060
28091
  await deps.mkdir(submitted);
28061
- if (await deps.detectSaveConflict(submitted)) {
28092
+ if (resolve25(submitted) !== resolve25(state.outDir) && await deps.detectSaveConflict(submitted)) {
28062
28093
  const subdir = deps.buildTimestampedSubdir(submitted);
28063
28094
  await deps.mkdir(subdir);
28064
28095
  await proceedToWrite(subdir);
@@ -28707,7 +28738,7 @@ function App({ importOptions, inkClear }) {
28707
28738
  const launch = { projectPath: projectPath2, tokens, filePaths, ...importOptions };
28708
28739
  const state = await inspectTokens({
28709
28740
  rawTokensPath: tokens,
28710
- outDir: join25(resolveUserPath(projectPath2), ".contentful"),
28741
+ contentfulDir: join25(resolveUserPath(projectPath2), ".contentful"),
28711
28742
  cache: importOptions.cache
28712
28743
  });
28713
28744
  if (state.kind === "cached") {
@@ -28728,6 +28759,7 @@ function App({ importOptions, inkClear }) {
28728
28759
  const settings = await readSettings2();
28729
28760
  const { agent, agentModel } = resolveAgentChoice(launch.agent, settings);
28730
28761
  const generatePromptPath = typeof settings.generatePromptPath === "string" ? settings.generatePromptPath : void 0;
28762
+ const outDir = await (await Promise.resolve().then(() => (init_debug_store(), debug_store_exports))).startImportRun(resolveUserPath(launch.projectPath ?? ""));
28731
28763
  setExtractionRun({
28732
28764
  ...generatePromptPath ? { generatePromptPath } : {},
28733
28765
  // the later steps get the same agent, so they use what the stage used
@@ -28736,6 +28768,7 @@ function App({ importOptions, inkClear }) {
28736
28768
  tokenStrategy,
28737
28769
  input: {
28738
28770
  projectPath: resolveUserPath(launch.projectPath ?? ""),
28771
+ outDir,
28739
28772
  filePaths: launch.filePaths ?? [],
28740
28773
  rawTokensPath: launch.tokens ?? "",
28741
28774
  tokenStrategy,
package/dist/index.js CHANGED
@@ -2121,7 +2121,7 @@ async function runAgent(options) {
2121
2121
  promptLen: prompt.length,
2122
2122
  promptHead: prompt.slice(0, 500)
2123
2123
  });
2124
- return new Promise((resolve26) => {
2124
+ return new Promise((resolve27) => {
2125
2125
  const bedrockEnv = bedrock ? BEDROCK_ENV_BY_AGENT[agent] : void 0;
2126
2126
  const child = spawn3(binary, args, {
2127
2127
  stdio: ["pipe", "pipe", "pipe"],
@@ -2170,7 +2170,7 @@ async function runAgent(options) {
2170
2170
  stderrLen: stderr.length,
2171
2171
  stderrTail: stderr.slice(-1e3)
2172
2172
  });
2173
- resolve26(result);
2173
+ resolve27(result);
2174
2174
  });
2175
2175
  });
2176
2176
  }
@@ -2192,7 +2192,7 @@ async function checkAgentAuth(agent) {
2192
2192
  return "not-found";
2193
2193
  if (agent !== "claude")
2194
2194
  return "ok";
2195
- return new Promise((resolve26) => {
2195
+ return new Promise((resolve27) => {
2196
2196
  const child = spawn4(binary, ["auth", "status", "--json"], { stdio: ["ignore", "pipe", "pipe"] });
2197
2197
  let stdout = "";
2198
2198
  let done = false;
@@ -2201,7 +2201,7 @@ async function checkAgentAuth(agent) {
2201
2201
  return;
2202
2202
  done = true;
2203
2203
  child.kill("SIGTERM");
2204
- resolve26("unauthenticated");
2204
+ resolve27("unauthenticated");
2205
2205
  }, 5e3);
2206
2206
  child.stdout?.on("data", (chunk) => {
2207
2207
  stdout += chunk.toString();
@@ -2212,14 +2212,14 @@ async function checkAgentAuth(agent) {
2212
2212
  done = true;
2213
2213
  clearTimeout(timer);
2214
2214
  if (code !== 0) {
2215
- resolve26("unauthenticated");
2215
+ resolve27("unauthenticated");
2216
2216
  return;
2217
2217
  }
2218
2218
  try {
2219
2219
  const status = JSON.parse(stdout);
2220
- resolve26(status.loggedIn ? "ok" : "unauthenticated");
2220
+ resolve27(status.loggedIn ? "ok" : "unauthenticated");
2221
2221
  } catch {
2222
- resolve26("unauthenticated");
2222
+ resolve27("unauthenticated");
2223
2223
  }
2224
2224
  });
2225
2225
  });
@@ -2229,9 +2229,9 @@ async function binaryIsOnPath(binary) {
2229
2229
  const fs = await import("node:fs/promises");
2230
2230
  return fs.access(binary).then(() => true, () => false);
2231
2231
  }
2232
- return new Promise((resolve26) => {
2232
+ return new Promise((resolve27) => {
2233
2233
  const child = spawn4(process.platform === "win32" ? "where" : "which", [binary], { stdio: "ignore" });
2234
- child.on("close", (code) => resolve26(code === 0));
2234
+ child.on("close", (code) => resolve27(code === 0));
2235
2235
  });
2236
2236
  }
2237
2237
  var init_check_agent_auth = __esm({
@@ -14901,7 +14901,7 @@ function errorMessage(error) {
14901
14901
  return error instanceof Error && error.message ? error.message : String(error);
14902
14902
  }
14903
14903
  function defaultSleep(delayMs) {
14904
- return new Promise((resolve26) => setTimeout(resolve26, delayMs));
14904
+ return new Promise((resolve27) => setTimeout(resolve27, delayMs));
14905
14905
  }
14906
14906
  var MAX_RETRY_AFTER_MS;
14907
14907
  var init_http_utils = __esm({
@@ -15177,7 +15177,7 @@ var init_api_client = __esm({
15177
15177
  const progress = maxAttempts > 1 ? attempt / (maxAttempts - 1) : 0;
15178
15178
  const baseDelay = intervalMs + (maxIntervalMs - intervalMs) * progress;
15179
15179
  const jitter = Math.random() * baseDelay * 0.15;
15180
- await new Promise((resolve26) => setTimeout(resolve26, baseDelay + jitter));
15180
+ await new Promise((resolve27) => setTimeout(resolve27, baseDelay + jitter));
15181
15181
  }
15182
15182
  }
15183
15183
  throw new Error(`Operation ${operationId} timed out after ${maxAttempts} attempts`);
@@ -18734,7 +18734,7 @@ async function retryDatabaseWrite(operation, attempts = 8) {
18734
18734
  const message = error instanceof Error ? error.message : String(error);
18735
18735
  if (!/database is locked|database is busy/i.test(message) || attempt >= attempts)
18736
18736
  throw error;
18737
- await new Promise((resolve26) => setTimeout(resolve26, Math.min(250 * attempt, 2e3)));
18737
+ await new Promise((resolve27) => setTimeout(resolve27, Math.min(250 * attempt, 2e3)));
18738
18738
  }
18739
18739
  }
18740
18740
  }
@@ -20301,11 +20301,34 @@ var init_shared2 = __esm({
20301
20301
  });
20302
20302
 
20303
20303
  // packages/experience-design-system-cli/src/tui/import/steps/05-token-reuse/logic.ts
20304
- import { access as access5, stat as stat4 } from "node:fs/promises";
20304
+ import { access as access5, readdir as readdir2, stat as stat4 } from "node:fs/promises";
20305
20305
  import { join as join18 } from "node:path";
20306
- async function inspectTokens({ rawTokensPath, outDir, cache }) {
20306
+ async function findLatestTokens(contentfulDir) {
20307
+ const candidates = [join18(contentfulDir, "tokens.json")];
20308
+ try {
20309
+ for (const session of await readdir2(contentfulDir, { withFileTypes: true })) {
20310
+ if (!session.isDirectory()) continue;
20311
+ for (const run of await readdir2(join18(contentfulDir, session.name), { withFileTypes: true }).catch(() => [])) {
20312
+ if (run.isDirectory() && /^dsi-import-\d+$/.test(run.name)) {
20313
+ candidates.push(join18(contentfulDir, session.name, run.name, "tokens.json"));
20314
+ }
20315
+ }
20316
+ }
20317
+ } catch {
20318
+ }
20319
+ const found = [];
20320
+ for (const path5 of candidates) {
20321
+ try {
20322
+ found.push({ path: path5, mtimeMs: (await stat4(path5)).mtimeMs });
20323
+ } catch {
20324
+ }
20325
+ }
20326
+ return found.sort((a, b) => b.mtimeMs - a.mtimeMs)[0]?.path ?? null;
20327
+ }
20328
+ async function inspectTokens({ rawTokensPath, contentfulDir, cache }) {
20307
20329
  if (!cache) return { kind: "none" };
20308
- const tokensPath = join18(outDir, "tokens.json");
20330
+ const tokensPath = await findLatestTokens(contentfulDir);
20331
+ if (!tokensPath) return { kind: "none" };
20309
20332
  try {
20310
20333
  await access5(tokensPath);
20311
20334
  } catch {
@@ -20653,7 +20676,7 @@ var init_selection2 = __esm({
20653
20676
  import { mkdir as mkdir7 } from "node:fs/promises";
20654
20677
  import { join as join21, resolve as resolve22 } from "node:path";
20655
20678
  async function runExtraction(input, onProgress, deps = realDeps) {
20656
- const outDir = join21(resolve22(input.projectPath), ".contentful");
20679
+ const outDir = input.outDir ?? join21(resolve22(input.projectPath), ".contentful");
20657
20680
  let progress = initialProgress({
20658
20681
  hasCredentials: input.credentials !== null,
20659
20682
  hasRawTokens: input.rawTokensPath !== "",
@@ -20804,203 +20827,6 @@ var init_run = __esm({
20804
20827
  }
20805
20828
  });
20806
20829
 
20807
- // packages/experience-design-system-cli/src/tui/settings/debug-mode/debug-mode-store.ts
20808
- async function readDebugModeSetting() {
20809
- const { debugMode } = await readSettings2();
20810
- return { ...DEFAULT_DEBUG_MODE_SETTING, ...debugMode };
20811
- }
20812
- async function writeDebugModeSetting(setting) {
20813
- await updateSettings2({ debugMode: setting });
20814
- }
20815
- var DEFAULT_DEBUG_MODE_SETTING;
20816
- var init_debug_mode_store = __esm({
20817
- "packages/experience-design-system-cli/src/tui/settings/debug-mode/debug-mode-store.ts"() {
20818
- "use strict";
20819
- init_config();
20820
- DEFAULT_DEBUG_MODE_SETTING = {
20821
- enabled: true
20822
- };
20823
- }
20824
- });
20825
-
20826
- // packages/experience-design-system-cli/src/tui/debug-store.ts
20827
- var debug_store_exports = {};
20828
- __export(debug_store_exports, {
20829
- currentDebugSessionDir: () => currentDebugSessionDir,
20830
- currentSessionDir: () => currentSessionDir,
20831
- finishDebugRun: () => finishDebugRun,
20832
- startDebugRun: () => startDebugRun,
20833
- startDebugSessionIn: () => startDebugSessionIn
20834
- });
20835
- import { mkdir as mkdir8, writeFile as writeFile7 } from "node:fs/promises";
20836
- import { existsSync as existsSync11, readdirSync as readdirSync3 } from "node:fs";
20837
- import { join as join22, resolve as resolve23 } from "node:path";
20838
- function generateRunId() {
20839
- const now = /* @__PURE__ */ new Date();
20840
- const pad = (n) => String(n).padStart(2, "0");
20841
- return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
20842
- }
20843
- function datePrefix(now = /* @__PURE__ */ new Date()) {
20844
- const pad = (n) => String(n).padStart(2, "0");
20845
- return `${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${now.getFullYear()}`;
20846
- }
20847
- function slugify(value) {
20848
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
20849
- }
20850
- async function readBaseDir() {
20851
- try {
20852
- const dir = (await readSettings2())["contentfulBaseDir"];
20853
- return typeof dir === "string" && dir !== "" ? resolve23(dir) : void 0;
20854
- } catch {
20855
- return void 0;
20856
- }
20857
- }
20858
- function nextSessionId(contentfulDir) {
20859
- const today = datePrefix();
20860
- const existing = existsSync11(contentfulDir) ? readdirSync3(contentfulDir) : [];
20861
- const seqPattern = new RegExp(`^${today}-session-(\\d+)$`);
20862
- const maxSeq = existing.reduce((max, entry) => {
20863
- const match2 = seqPattern.exec(entry);
20864
- return match2 ? Math.max(max, Number(match2[1])) : max;
20865
- }, 0);
20866
- return `${today}-session-${maxSeq + 1}`;
20867
- }
20868
- function startSession(base, { replace }) {
20869
- const contentfulDir = join22(base, ".contentful");
20870
- if (replace || !sessionDir) sessionDir = join22(contentfulDir, nextSessionId(contentfulDir));
20871
- return sessionDir;
20872
- }
20873
- function currentDebugSessionDir() {
20874
- return sessionDir && join22(sessionDir, "debug");
20875
- }
20876
- function currentSessionDir() {
20877
- return sessionDir;
20878
- }
20879
- async function startDebugSessionIn(base, { replace = false } = {}) {
20880
- try {
20881
- if (!await isDebugModeEnabled()) return;
20882
- await mkdir8(join22(startSession(resolve23(base), { replace }), "debug"), { recursive: true });
20883
- } catch {
20884
- }
20885
- }
20886
- async function isDebugModeEnabled() {
20887
- try {
20888
- return (await readDebugModeSetting()).enabled;
20889
- } catch {
20890
- return true;
20891
- }
20892
- }
20893
- function redact(value) {
20894
- const clone = { ...value };
20895
- if ("cma_token" in clone) {
20896
- const raw = clone.cma_token;
20897
- clone.cma_token = typeof raw === "string" && raw.length > 0 ? `[present, ${raw.length} chars]` : "[not set]";
20898
- }
20899
- return clone;
20900
- }
20901
- function startDebugRun({
20902
- flow,
20903
- step,
20904
- menuOption,
20905
- inputs
20906
- }) {
20907
- const runId = generateRunId();
20908
- const startedAt = Date.now();
20909
- const redactedInputs = redact(inputs);
20910
- isDebugModeEnabled().then((enabled) => {
20911
- if (!enabled) return;
20912
- pendingRun = { flow, step, runId, menuOption, inputs: redactedInputs, startedAt };
20913
- });
20914
- }
20915
- async function finishDebugRun({
20916
- outputs,
20917
- status,
20918
- exitMethod
20919
- }) {
20920
- const run = pendingRun;
20921
- if (!run) return;
20922
- pendingRun = void 0;
20923
- const durationMs = Date.now() - run.startedAt;
20924
- const content = renderMarkdown(run, { outputs: redact(outputs), status, exitMethod, durationMs });
20925
- const root = currentDebugSessionDir();
20926
- if (!root) return;
20927
- const flowSegments = run.flow.split("/");
20928
- const dir = join22(root, ...flowSegments);
20929
- await mkdir8(dir, { recursive: true });
20930
- const filename = nextAttemptFilename(dir, run.menuOption);
20931
- await writeFile7(join22(dir, filename), content, { mode: 384 });
20932
- }
20933
- function nextAttemptFilename(dir, menuOption) {
20934
- const today = datePrefix();
20935
- const optionSlug = slugify(menuOption);
20936
- const existing = existsSync11(dir) ? readdirSync3(dir) : [];
20937
- const attemptPattern = new RegExp(`^${today}-${optionSlug}-attempt-(\\d+)\\.md$`);
20938
- const maxAttempt = existing.reduce((max, entry) => {
20939
- const match2 = attemptPattern.exec(entry);
20940
- return match2 ? Math.max(max, Number(match2[1])) : max;
20941
- }, 0);
20942
- return `${today}-${optionSlug}-attempt-${maxAttempt + 1}.md`;
20943
- }
20944
- function renderMarkdown(run, finish) {
20945
- const stepMatch = /^(\d+)-(.+)$/.exec(run.step);
20946
- const stepLabel2 = stepMatch ? `${stepMatch[1]} - ${stepMatch[2]}` : run.step;
20947
- const flowSlug = run.flow.split("/").join("-");
20948
- return `# Debug Log
20949
-
20950
- - Run ID: ${run.runId}
20951
- - Flow: ${flowSlug}
20952
- - Step: ${stepLabel2}
20953
- - Menu option: ${run.menuOption}
20954
- - Date: ${(/* @__PURE__ */ new Date()).toISOString()}
20955
- - CLI version: ${readPackageVersion()}
20956
- - Platform: ${process.platform} / Node ${process.version}
20957
- - Duration on screen: ${finish.durationMs}ms
20958
- - Status: ${finish.status}
20959
- - Exit method: ${finish.exitMethod}
20960
-
20961
- ## Inputs
20962
- \`\`\`json
20963
- ${JSON.stringify(run.inputs, null, 2)}
20964
- \`\`\`
20965
-
20966
- ## Outputs
20967
- \`\`\`json
20968
- ${JSON.stringify(finish.outputs, null, 2)}
20969
- \`\`\`
20970
- `;
20971
- }
20972
- function handleCrash() {
20973
- return finishDebugRun({ outputs: {}, status: "error", exitMethod: "crashed" });
20974
- }
20975
- var pendingRun, sessionDir;
20976
- var init_debug_store = __esm({
20977
- "packages/experience-design-system-cli/src/tui/debug-store.ts"() {
20978
- "use strict";
20979
- init_config();
20980
- init_version();
20981
- init_debug_mode_store();
20982
- void Promise.all([isDebugModeEnabled(), readBaseDir()]).then(([enabled, base]) => {
20983
- if (!enabled || !base) return void 0;
20984
- return mkdir8(join22(startSession(base, { replace: false }), "debug"), { recursive: true });
20985
- });
20986
- process.once("uncaughtException", (err) => {
20987
- void handleCrash().finally(() => {
20988
- throw err;
20989
- });
20990
- });
20991
- process.once("unhandledRejection", (reason) => {
20992
- void handleCrash().finally(() => {
20993
- throw reason;
20994
- });
20995
- });
20996
- process.once("SIGINT", () => {
20997
- void handleCrash().finally(() => {
20998
- process.exit(130);
20999
- });
21000
- });
21001
- }
21002
- });
21003
-
21004
20830
  // packages/experience-design-system-cli/src/tui/import/steps/06-extraction/use-extraction.ts
21005
20831
  import { useEffect as useEffect5, useRef as useRef2, useState as useState8 } from "react";
21006
20832
  function useExtraction(input) {
@@ -21020,7 +20846,6 @@ function useExtraction(input) {
21020
20846
  reuseTokens: input.tokenStrategy === "reuse"
21021
20847
  })
21022
20848
  });
21023
- void Promise.resolve().then(() => (init_debug_store(), debug_store_exports)).then((store) => store.startDebugSessionIn(input.projectPath));
21024
20849
  void runExtraction(input, (progress) => {
21025
20850
  if (current.current === input) setState({ status: "running", progress });
21026
20851
  }).then((result) => {
@@ -24242,7 +24067,7 @@ var init_save_decisions = __esm({
24242
24067
  });
24243
24068
 
24244
24069
  // packages/experience-design-system-cli/src/tui/import/steps/08-generation/run.ts
24245
- import { resolve as resolve24 } from "node:path";
24070
+ import { resolve as resolve23 } from "node:path";
24246
24071
  async function runGeneration(input, onProgress, deps = realDeps3) {
24247
24072
  let progress = { components: null, mapTokens: "idle", mapTokensEligible: null };
24248
24073
  const update = (change) => {
@@ -24328,7 +24153,7 @@ async function buildMapTokensOptions(input) {
24328
24153
  if (mapOverride?.kind === "text") prompt.skillContentOverride = mapOverride.value;
24329
24154
  if (mapOverride?.kind === "path") {
24330
24155
  await resolvePromptOverride(mapOverride);
24331
- prompt.skillPathOverride = resolve24(mapOverride.value);
24156
+ prompt.skillPathOverride = resolve23(mapOverride.value);
24332
24157
  }
24333
24158
  if (input.existingEntitiesPath) {
24334
24159
  const entities = await readExistingContentfulEntitiesFromSession({ path: input.existingEntitiesPath });
@@ -24406,7 +24231,7 @@ var init_use_generation = __esm({
24406
24231
  });
24407
24232
 
24408
24233
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/build-flow-start.ts
24409
- import { join as join23 } from "node:path";
24234
+ import { join as join22 } from "node:path";
24410
24235
  function buildFlowStart(input) {
24411
24236
  const { extraction, handoff, saved, generatePromptPath } = input;
24412
24237
  const credentials = extraction.credentials ?? {
@@ -24419,7 +24244,7 @@ function buildFlowStart(input) {
24419
24244
  handoff,
24420
24245
  agent: extraction.agent,
24421
24246
  projectPath: extraction.projectPath,
24422
- outDir: extraction.projectPath ? join23(extraction.projectPath, ".contentful") : "",
24247
+ outDir: extraction.projectPath ? join22(extraction.projectPath, ".contentful") : "",
24423
24248
  rawTokensPath: extraction.rawTokensPath,
24424
24249
  credentials: {
24425
24250
  spaceId: credentials.spaceId,
@@ -30615,7 +30440,7 @@ var init_DoneStep = __esm({
30615
30440
  });
30616
30441
 
30617
30442
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/path-utils.ts
30618
- import { resolve as resolve25 } from "node:path";
30443
+ import { resolve as resolve24 } from "node:path";
30619
30444
  import { homedir as homedir4 } from "node:os";
30620
30445
  function expandTilde(input) {
30621
30446
  if (input === "~" || input.startsWith("~/") || input.startsWith("~\\")) {
@@ -30629,7 +30454,7 @@ function normalizePath(input) {
30629
30454
  p = p.slice(1, -1);
30630
30455
  }
30631
30456
  p = expandTilde(p);
30632
- return resolve25(p);
30457
+ return resolve24(p);
30633
30458
  }
30634
30459
  var init_path_utils = __esm({
30635
30460
  "packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/path-utils.ts"() {
@@ -30656,8 +30481,8 @@ var init_use_blinking_cursor = __esm({
30656
30481
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/screens/PathPrompt.tsx
30657
30482
  import { useState as useState24 } from "react";
30658
30483
  import { Box as Box37, Text as Text40 } from "ink";
30659
- import { readdirSync as readdirSync4 } from "node:fs";
30660
- import { dirname as dirname15, basename as basename6, join as join24 } from "node:path";
30484
+ import { readdirSync as readdirSync3 } from "node:fs";
30485
+ import { dirname as dirname15, basename as basename6, join as join23 } from "node:path";
30661
30486
  import { jsx as jsx47, jsxs as jsxs38 } from "react/jsx-runtime";
30662
30487
  function autocomplete(partial) {
30663
30488
  if (!partial) return null;
@@ -30666,19 +30491,19 @@ function autocomplete(partial) {
30666
30491
  const base = basename6(normalized);
30667
30492
  let entries;
30668
30493
  try {
30669
- entries = readdirSync4(dir);
30494
+ entries = readdirSync3(dir);
30670
30495
  } catch {
30671
30496
  return null;
30672
30497
  }
30673
30498
  const matches = entries.filter((e) => e.startsWith(base));
30674
30499
  if (matches.length === 0) return null;
30675
- if (matches.length === 1) return join24(dir, matches[0]);
30500
+ if (matches.length === 1) return join23(dir, matches[0]);
30676
30501
  let prefix = matches[0];
30677
30502
  for (const m of matches) {
30678
30503
  while (!m.startsWith(prefix)) prefix = prefix.slice(0, -1);
30679
30504
  if (!prefix) break;
30680
30505
  }
30681
- return prefix.length > base.length ? join24(dir, prefix) : null;
30506
+ return prefix.length > base.length ? join23(dir, prefix) : null;
30682
30507
  }
30683
30508
  function PathPrompt({
30684
30509
  defaultPath,
@@ -31881,8 +31706,8 @@ var init_run_print_files_helpers = __esm({
31881
31706
  });
31882
31707
 
31883
31708
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/save.ts
31884
- import { writeFile as writeFile8 } from "node:fs/promises";
31885
- import { join as join25 } from "node:path";
31709
+ import { writeFile as writeFile7 } from "node:fs/promises";
31710
+ import { join as join24 } from "node:path";
31886
31711
  function buildSavedCDF(components, tokens, opts = {}) {
31887
31712
  const cdf = buildCDF2(components, toCdfTokens(tokens), opts);
31888
31713
  if (!cdf) throw new Error("nothing to save \u2014 no components or tokens resolved");
@@ -31894,7 +31719,7 @@ function buildSavedCDF(components, tokens, opts = {}) {
31894
31719
  }
31895
31720
  async function runPrintFiles(input, deps, onPatch) {
31896
31721
  onPatch({ step: "printing" });
31897
- const componentsPath = join25(input.outDir, "components.json");
31722
+ const componentsPath = join24(input.outDir, "components.json");
31898
31723
  try {
31899
31724
  const db = deps.openDb();
31900
31725
  let components = [];
@@ -31978,10 +31803,10 @@ var init_save = __esm({
31978
31803
  realSaveDeps = () => ({
31979
31804
  openDb: openPipelineDb,
31980
31805
  readTokens: (path5) => readTokensFromPath2({ flag: "tokens", path: path5 }),
31981
- writeFile: (path5, contents) => writeFile8(path5, contents),
31806
+ writeFile: (path5, contents) => writeFile7(path5, contents),
31982
31807
  appendRun,
31983
31808
  buildSourceFingerprint,
31984
- sleep: (ms) => new Promise((resolve26) => setTimeout(resolve26, ms)),
31809
+ sleep: (ms) => new Promise((resolve27) => setTimeout(resolve27, ms)),
31985
31810
  warn: (message) => process.stderr.write(message)
31986
31811
  });
31987
31812
  }
@@ -32001,9 +31826,9 @@ var init_types9 = __esm({
32001
31826
 
32002
31827
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/controller.ts
32003
31828
  import { appendFileSync, writeFileSync } from "node:fs";
32004
- import { mkdir as mkdir9 } from "node:fs/promises";
31829
+ import { mkdir as mkdir8 } from "node:fs/promises";
32005
31830
  import { tmpdir } from "node:os";
32006
- import { join as join26 } from "node:path";
31831
+ import { join as join25, resolve as resolve25 } from "node:path";
32007
31832
  function appendWizardLog(entry) {
32008
31833
  appendFileSync(WIZARD_LOG, JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry }) + "\n");
32009
31834
  }
@@ -32011,9 +31836,6 @@ function startWizardLog() {
32011
31836
  writeFileSync(WIZARD_LOG, `--- experiences import session ${(/* @__PURE__ */ new Date()).toISOString()} ---
32012
31837
  `);
32013
31838
  }
32014
- async function realSessionDir() {
32015
- return (await Promise.resolve().then(() => (init_debug_store(), debug_store_exports))).currentSessionDir();
32016
- }
32017
31839
  function realFlowDeps() {
32018
31840
  const log = (entry) => appendWizardLog(entry);
32019
31841
  return {
@@ -32029,14 +31851,13 @@ function realFlowDeps() {
32029
31851
  }
32030
31852
  },
32031
31853
  mkdir: async (path5) => {
32032
- await mkdir9(path5, { recursive: true });
31854
+ await mkdir8(path5, { recursive: true });
32033
31855
  },
32034
31856
  detectSaveConflict,
32035
31857
  buildTimestampedSubdir,
32036
31858
  rejectComponentsByName,
32037
31859
  log,
32038
- stderr: (text) => process.stderr.write(text),
32039
- sessionDir: realSessionDir
31860
+ stderr: (text) => process.stderr.write(text)
32040
31861
  };
32041
31862
  }
32042
31863
  function initialFlowState(start) {
@@ -32177,9 +31998,6 @@ function createFlowController(start, deps) {
32177
31998
  const startSave = (opts, patch = {}) => {
32178
31999
  pendingSave = opts;
32179
32000
  update({ ...patch, step: "path-prompt" });
32180
- void deps.sessionDir?.().then((session) => {
32181
- if (session && state.step === "path-prompt") update({ outDir: join26(session, "dsi-import") });
32182
- });
32183
32001
  };
32184
32002
  const proceedToWrite = async (path5) => {
32185
32003
  update({ outDir: path5 });
@@ -32252,7 +32070,7 @@ function createFlowController(start, deps) {
32252
32070
  },
32253
32071
  async submitPath(submitted) {
32254
32072
  await deps.mkdir(submitted);
32255
- if (await deps.detectSaveConflict(submitted)) {
32073
+ if (resolve25(submitted) !== resolve25(state.outDir) && await deps.detectSaveConflict(submitted)) {
32256
32074
  const subdir = deps.buildTimestampedSubdir(submitted);
32257
32075
  await deps.mkdir(subdir);
32258
32076
  await proceedToWrite(subdir);
@@ -32284,7 +32102,7 @@ var init_controller = __esm({
32284
32102
  init_push();
32285
32103
  init_save();
32286
32104
  init_types9();
32287
- WIZARD_LOG = join26(tmpdir(), "experiences-import-wizard.log");
32105
+ WIZARD_LOG = join25(tmpdir(), "experiences-import-wizard.log");
32288
32106
  }
32289
32107
  });
32290
32108
 
@@ -32316,6 +32134,219 @@ var init_use_flow = __esm({
32316
32134
  }
32317
32135
  });
32318
32136
 
32137
+ // packages/experience-design-system-cli/src/tui/settings/debug-mode/debug-mode-store.ts
32138
+ async function readDebugModeSetting() {
32139
+ const { debugMode } = await readSettings2();
32140
+ return { ...DEFAULT_DEBUG_MODE_SETTING, ...debugMode };
32141
+ }
32142
+ async function writeDebugModeSetting(setting) {
32143
+ await updateSettings2({ debugMode: setting });
32144
+ }
32145
+ var DEFAULT_DEBUG_MODE_SETTING;
32146
+ var init_debug_mode_store = __esm({
32147
+ "packages/experience-design-system-cli/src/tui/settings/debug-mode/debug-mode-store.ts"() {
32148
+ "use strict";
32149
+ init_config();
32150
+ DEFAULT_DEBUG_MODE_SETTING = {
32151
+ enabled: true
32152
+ };
32153
+ }
32154
+ });
32155
+
32156
+ // packages/experience-design-system-cli/src/tui/debug-store.ts
32157
+ var debug_store_exports = {};
32158
+ __export(debug_store_exports, {
32159
+ currentDebugSessionDir: () => currentDebugSessionDir,
32160
+ currentSessionDir: () => currentSessionDir,
32161
+ finishDebugRun: () => finishDebugRun,
32162
+ startDebugRun: () => startDebugRun,
32163
+ startDebugSessionIn: () => startDebugSessionIn,
32164
+ startImportRun: () => startImportRun
32165
+ });
32166
+ import { mkdir as mkdir9, writeFile as writeFile8 } from "node:fs/promises";
32167
+ import { existsSync as existsSync11, readdirSync as readdirSync4 } from "node:fs";
32168
+ import { join as join26, resolve as resolve26 } from "node:path";
32169
+ function generateRunId() {
32170
+ const now = /* @__PURE__ */ new Date();
32171
+ const pad = (n) => String(n).padStart(2, "0");
32172
+ return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
32173
+ }
32174
+ function datePrefix(now = /* @__PURE__ */ new Date()) {
32175
+ const pad = (n) => String(n).padStart(2, "0");
32176
+ return `${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${now.getFullYear()}`;
32177
+ }
32178
+ function slugify(value) {
32179
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
32180
+ }
32181
+ async function readBaseDir() {
32182
+ try {
32183
+ const dir = (await readSettings2())["contentfulBaseDir"];
32184
+ return typeof dir === "string" && dir !== "" ? resolve26(dir) : void 0;
32185
+ } catch {
32186
+ return void 0;
32187
+ }
32188
+ }
32189
+ function nextSessionId(contentfulDir) {
32190
+ const today = datePrefix();
32191
+ const existing = existsSync11(contentfulDir) ? readdirSync4(contentfulDir) : [];
32192
+ const seqPattern = new RegExp(`^${today}-session-(\\d+)$`);
32193
+ const maxSeq = existing.reduce((max, entry) => {
32194
+ const match2 = seqPattern.exec(entry);
32195
+ return match2 ? Math.max(max, Number(match2[1])) : max;
32196
+ }, 0);
32197
+ return `${today}-session-${maxSeq + 1}`;
32198
+ }
32199
+ function startSession(base, { replace }) {
32200
+ const contentfulDir = join26(base, ".contentful");
32201
+ if (replace || !sessionDir) sessionDir = join26(contentfulDir, nextSessionId(contentfulDir));
32202
+ return sessionDir;
32203
+ }
32204
+ function currentDebugSessionDir() {
32205
+ return sessionDir && join26(sessionDir, "debug");
32206
+ }
32207
+ function currentSessionDir() {
32208
+ return sessionDir;
32209
+ }
32210
+ async function startDebugSessionIn(base, { replace = false } = {}) {
32211
+ try {
32212
+ if (!await isDebugModeEnabled()) return;
32213
+ await mkdir9(join26(startSession(resolve26(base), { replace }), "debug"), { recursive: true });
32214
+ } catch {
32215
+ }
32216
+ }
32217
+ function nextImportRunDir(session) {
32218
+ const existing = existsSync11(session) ? readdirSync4(session) : [];
32219
+ const maxRun = existing.reduce((max, entry) => {
32220
+ const match2 = /^dsi-import-(\d+)$/.exec(entry);
32221
+ return match2 ? Math.max(max, Number(match2[1])) : max;
32222
+ }, 0);
32223
+ return join26(session, `dsi-import-${maxRun + 1}`);
32224
+ }
32225
+ async function startImportRun(projectPath) {
32226
+ await startDebugSessionIn(projectPath);
32227
+ if (!sessionDir) return join26(resolve26(projectPath), ".contentful");
32228
+ const dir = nextImportRunDir(sessionDir);
32229
+ await mkdir9(dir, { recursive: true });
32230
+ return dir;
32231
+ }
32232
+ async function isDebugModeEnabled() {
32233
+ try {
32234
+ return (await readDebugModeSetting()).enabled;
32235
+ } catch {
32236
+ return true;
32237
+ }
32238
+ }
32239
+ function redact(value) {
32240
+ const clone = { ...value };
32241
+ if ("cma_token" in clone) {
32242
+ const raw = clone.cma_token;
32243
+ clone.cma_token = typeof raw === "string" && raw.length > 0 ? `[present, ${raw.length} chars]` : "[not set]";
32244
+ }
32245
+ return clone;
32246
+ }
32247
+ function startDebugRun({
32248
+ flow,
32249
+ step,
32250
+ menuOption,
32251
+ inputs
32252
+ }) {
32253
+ const runId = generateRunId();
32254
+ const startedAt = Date.now();
32255
+ const redactedInputs = redact(inputs);
32256
+ isDebugModeEnabled().then((enabled) => {
32257
+ if (!enabled) return;
32258
+ pendingRun = { flow, step, runId, menuOption, inputs: redactedInputs, startedAt };
32259
+ });
32260
+ }
32261
+ async function finishDebugRun({
32262
+ outputs,
32263
+ status,
32264
+ exitMethod
32265
+ }) {
32266
+ const run = pendingRun;
32267
+ if (!run) return;
32268
+ pendingRun = void 0;
32269
+ const durationMs = Date.now() - run.startedAt;
32270
+ const content = renderMarkdown(run, { outputs: redact(outputs), status, exitMethod, durationMs });
32271
+ const root = currentDebugSessionDir();
32272
+ if (!root) return;
32273
+ const flowSegments = run.flow.split("/");
32274
+ const dir = join26(root, ...flowSegments);
32275
+ await mkdir9(dir, { recursive: true });
32276
+ const filename = nextAttemptFilename(dir, run.menuOption);
32277
+ await writeFile8(join26(dir, filename), content, { mode: 384 });
32278
+ }
32279
+ function nextAttemptFilename(dir, menuOption) {
32280
+ const today = datePrefix();
32281
+ const optionSlug = slugify(menuOption);
32282
+ const existing = existsSync11(dir) ? readdirSync4(dir) : [];
32283
+ const attemptPattern = new RegExp(`^${today}-${optionSlug}-attempt-(\\d+)\\.md$`);
32284
+ const maxAttempt = existing.reduce((max, entry) => {
32285
+ const match2 = attemptPattern.exec(entry);
32286
+ return match2 ? Math.max(max, Number(match2[1])) : max;
32287
+ }, 0);
32288
+ return `${today}-${optionSlug}-attempt-${maxAttempt + 1}.md`;
32289
+ }
32290
+ function renderMarkdown(run, finish) {
32291
+ const stepMatch = /^(\d+)-(.+)$/.exec(run.step);
32292
+ const stepLabel2 = stepMatch ? `${stepMatch[1]} - ${stepMatch[2]}` : run.step;
32293
+ const flowSlug = run.flow.split("/").join("-");
32294
+ return `# Debug Log
32295
+
32296
+ - Run ID: ${run.runId}
32297
+ - Flow: ${flowSlug}
32298
+ - Step: ${stepLabel2}
32299
+ - Menu option: ${run.menuOption}
32300
+ - Date: ${(/* @__PURE__ */ new Date()).toISOString()}
32301
+ - CLI version: ${readPackageVersion()}
32302
+ - Platform: ${process.platform} / Node ${process.version}
32303
+ - Duration on screen: ${finish.durationMs}ms
32304
+ - Status: ${finish.status}
32305
+ - Exit method: ${finish.exitMethod}
32306
+
32307
+ ## Inputs
32308
+ \`\`\`json
32309
+ ${JSON.stringify(run.inputs, null, 2)}
32310
+ \`\`\`
32311
+
32312
+ ## Outputs
32313
+ \`\`\`json
32314
+ ${JSON.stringify(finish.outputs, null, 2)}
32315
+ \`\`\`
32316
+ `;
32317
+ }
32318
+ function handleCrash() {
32319
+ return finishDebugRun({ outputs: {}, status: "error", exitMethod: "crashed" });
32320
+ }
32321
+ var pendingRun, sessionDir;
32322
+ var init_debug_store = __esm({
32323
+ "packages/experience-design-system-cli/src/tui/debug-store.ts"() {
32324
+ "use strict";
32325
+ init_config();
32326
+ init_version();
32327
+ init_debug_mode_store();
32328
+ void Promise.all([isDebugModeEnabled(), readBaseDir()]).then(([enabled, base]) => {
32329
+ if (!enabled || !base) return void 0;
32330
+ return mkdir9(join26(startSession(base, { replace: false }), "debug"), { recursive: true });
32331
+ });
32332
+ process.once("uncaughtException", (err) => {
32333
+ void handleCrash().finally(() => {
32334
+ throw err;
32335
+ });
32336
+ });
32337
+ process.once("unhandledRejection", (reason) => {
32338
+ void handleCrash().finally(() => {
32339
+ throw reason;
32340
+ });
32341
+ });
32342
+ process.once("SIGINT", () => {
32343
+ void handleCrash().finally(() => {
32344
+ process.exit(130);
32345
+ });
32346
+ });
32347
+ }
32348
+ });
32349
+
32319
32350
  // packages/experience-design-system-cli/src/tui/help/help-info.ts
32320
32351
  async function readHelpInfo() {
32321
32352
  const debugMode = await readDebugModeSetting();
@@ -32991,7 +33022,7 @@ function App({ importOptions, inkClear }) {
32991
33022
  const launch = { projectPath: projectPath2, tokens, filePaths, ...importOptions };
32992
33023
  const state = await inspectTokens({
32993
33024
  rawTokensPath: tokens,
32994
- outDir: join27(resolveUserPath(projectPath2), ".contentful"),
33025
+ contentfulDir: join27(resolveUserPath(projectPath2), ".contentful"),
32995
33026
  cache: importOptions.cache
32996
33027
  });
32997
33028
  if (state.kind === "cached") {
@@ -33012,6 +33043,7 @@ function App({ importOptions, inkClear }) {
33012
33043
  const settings = await readSettings2();
33013
33044
  const { agent, agentModel } = resolveAgentChoice(launch.agent, settings);
33014
33045
  const generatePromptPath = typeof settings.generatePromptPath === "string" ? settings.generatePromptPath : void 0;
33046
+ const outDir = await (await Promise.resolve().then(() => (init_debug_store(), debug_store_exports))).startImportRun(resolveUserPath(launch.projectPath ?? ""));
33015
33047
  setExtractionRun({
33016
33048
  ...generatePromptPath ? { generatePromptPath } : {},
33017
33049
  // the later steps get the same agent, so they use what the stage used
@@ -33020,6 +33052,7 @@ function App({ importOptions, inkClear }) {
33020
33052
  tokenStrategy,
33021
33053
  input: {
33022
33054
  projectPath: resolveUserPath(launch.projectPath ?? ""),
33055
+ outDir,
33023
33056
  filePaths: launch.filePaths ?? [],
33024
33057
  rawTokensPath: launch.tokens ?? "",
33025
33058
  tokenStrategy,
@@ -33367,14 +33400,14 @@ function runDevWatch(args) {
33367
33400
  process.stderr.write("--watch only works from a checkout of the repo (scripts/dev.mjs is not in the package).\n");
33368
33401
  return Promise.resolve(1);
33369
33402
  }
33370
- return new Promise((resolve26) => {
33403
+ return new Promise((resolve27) => {
33371
33404
  const child = spawn2(process.execPath, [script, ...args], { stdio: "inherit" });
33372
33405
  process.on("SIGINT", () => void 0);
33373
- child.on("exit", (code) => resolve26(code ?? 0));
33406
+ child.on("exit", (code) => resolve27(code ?? 0));
33374
33407
  child.on("error", (err) => {
33375
33408
  process.stderr.write(`${err.message}
33376
33409
  `);
33377
- resolve26(1);
33410
+ resolve27(1);
33378
33411
  });
33379
33412
  });
33380
33413
  }
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-4ce44f69.0",
3
+ "version": "2.37.1-dev-build-f57f8147.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -11,6 +11,13 @@ export declare function currentSessionDir(): string | undefined;
11
11
  export declare function startDebugSessionIn(base: string, { replace }?: {
12
12
  replace?: boolean | undefined;
13
13
  }): Promise<void>;
14
+ /**
15
+ * Where this import run writes everything it produces (tokens.json, .existing-entities.json, components.json): a new
16
+ * `dsi-import-N` folder in this session's folder, next to the debug logs. The session is started here, and waited for,
17
+ * so the answer does not depend on how quickly the boot-time start finished. Without a session (Debug Mode off, or a
18
+ * folder that cannot be created) the files stay in `<project>/.contentful`, as before.
19
+ */
20
+ export declare function startImportRun(projectPath: string): Promise<string>;
14
21
  export declare function startDebugRun({ flow, step, menuOption, inputs, }: {
15
22
  flow: string;
16
23
  step: string;
@@ -8,10 +8,11 @@ export type TokensState = {
8
8
  };
9
9
  interface InspectInput {
10
10
  rawTokensPath: string;
11
- outDir: string;
11
+ /** The `.contentful` folder: the sessions' `dsi-import` folders sit inside it, and an older `tokens.json` may sit in it. */
12
+ contentfulDir: string;
12
13
  cache: boolean;
13
14
  }
14
- export declare function inspectTokens({ rawTokensPath, outDir, cache }: InspectInput): Promise<TokensState>;
15
+ export declare function inspectTokens({ rawTokensPath, contentfulDir, cache }: InspectInput): Promise<TokensState>;
15
16
  export declare function gateText({ tokensPath, sourceChanged }: {
16
17
  tokensPath: string;
17
18
  sourceChanged: boolean;
@@ -2,6 +2,8 @@ import type { ExtractionHandoff, ImportHandoff } from '@contentful/experience-de
2
2
  export type TokenStrategy = ImportHandoff['tokens']['strategy'];
3
3
  export interface ExtractionInput {
4
4
  projectPath: string;
5
+ /** Where the stage writes tokens.json and the other files. Defaults to `<projectPath>/.contentful`. */
6
+ outDir?: string;
5
7
  /** Absolute source files from the path-validation step (collectFiles output). */
6
8
  filePaths: string[];
7
9
  /** Raw tokens file the user pointed at (always set: v2 asks for one). */
@@ -38,8 +38,6 @@ export interface FlowDeps {
38
38
  /** Writes one line of the wizard's step log. */
39
39
  log: (entry: Record<string, unknown>) => void;
40
40
  stderr: (text: string) => void;
41
- /** This terminal session's folder, when there is one; the saved files default to a `dsi-import` folder inside it. */
42
- sessionDir?: () => Promise<string | undefined>;
43
41
  }
44
42
  export declare function startWizardLog(): void;
45
43
  export declare function realFlowDeps(): FlowDeps;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.37.1-dev-build-4ce44f69.0",
3
+ "version": "2.37.1-dev-build-f57f8147.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
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-4ce44f69.0",
3
+ "version": "2.37.1-dev-build-f57f8147.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-4ce44f69.0",
49
- "@contentful/experience-design-system-types": "2.37.1-dev-build-4ce44f69.0"
48
+ "@contentful/experience-design-system-backend-pipeline": "2.37.1-dev-build-f57f8147.0",
49
+ "@contentful/experience-design-system-types": "2.37.1-dev-build-f57f8147.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@tsconfig/node24": "^24.0.4",