@contentful/experience-design-system-cli 2.39.1-dev-build-6e58be6f.0 → 2.39.1-dev-build-c1faff77.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/index.js CHANGED
@@ -19407,6 +19407,325 @@ var init_PageContainer = __esm({
19407
19407
  }
19408
19408
  });
19409
19409
 
19410
+ // packages/experience-design-system-cli/src/tui/settings/debug-mode/debug-mode-store.ts
19411
+ function toRetentionDays(value) {
19412
+ return RETENTION_CHOICES.find((choice) => choice === value) ?? DEFAULT_RETENTION_DAYS;
19413
+ }
19414
+ async function readDebugModeSetting() {
19415
+ const { debugMode } = await readSettings2();
19416
+ const saved = debugMode ?? {};
19417
+ return {
19418
+ enabled: typeof saved.enabled === "boolean" ? saved.enabled : true,
19419
+ retentionDays: toRetentionDays(saved.retentionDays)
19420
+ };
19421
+ }
19422
+ async function writeDebugModeSetting(change) {
19423
+ await updateSettings2({ debugMode: { ...await readDebugModeSetting(), ...change } });
19424
+ }
19425
+ var RETENTION_CHOICES, DEFAULT_RETENTION_DAYS;
19426
+ var init_debug_mode_store = __esm({
19427
+ "packages/experience-design-system-cli/src/tui/settings/debug-mode/debug-mode-store.ts"() {
19428
+ "use strict";
19429
+ init_config();
19430
+ RETENTION_CHOICES = [7, 14, 30];
19431
+ DEFAULT_RETENTION_DAYS = 30;
19432
+ }
19433
+ });
19434
+
19435
+ // packages/experience-design-system-cli/src/tui/debug-prune.ts
19436
+ import { readdir as readdir2, rm, rmdir, stat as stat4 } from "node:fs/promises";
19437
+ import { dirname as dirname15, join as join18 } from "node:path";
19438
+ async function logFilesIn(dir) {
19439
+ const entries = await readdir2(dir, { withFileTypes: true });
19440
+ const nested = await Promise.all(
19441
+ entries.map(async (entry) => {
19442
+ const path5 = join18(dir, entry.name);
19443
+ if (entry.isDirectory()) return logFilesIn(path5);
19444
+ return entry.isFile() && entry.name.endsWith(".md") ? [path5] : [];
19445
+ })
19446
+ );
19447
+ return nested.flat();
19448
+ }
19449
+ async function removeEmptyFolders(from, stop) {
19450
+ let dir = from;
19451
+ let removed = 0;
19452
+ for (; ; ) {
19453
+ try {
19454
+ await rmdir(dir);
19455
+ } catch {
19456
+ return removed;
19457
+ }
19458
+ removed += 1;
19459
+ if (dir === stop) return removed;
19460
+ dir = dirname15(dir);
19461
+ }
19462
+ }
19463
+ async function pruneExpiredLogs(base, retentionDays, now = Date.now()) {
19464
+ const cutoff = now - retentionDays * DAY_MS;
19465
+ const contentfulDir = join18(base, ".contentful");
19466
+ const result = { logs: 0, folders: 0 };
19467
+ try {
19468
+ const sessions = (await readdir2(contentfulDir)).filter((name) => SESSION_FOLDER.test(name));
19469
+ for (const session of sessions) {
19470
+ const files = await logFilesIn(join18(contentfulDir, session, "debug")).catch(() => []);
19471
+ for (const file of files) {
19472
+ try {
19473
+ if ((await stat4(file)).mtimeMs >= cutoff) continue;
19474
+ await rm(file);
19475
+ result.logs += 1;
19476
+ result.folders += await removeEmptyFolders(dirname15(file), join18(contentfulDir, session));
19477
+ } catch {
19478
+ }
19479
+ }
19480
+ }
19481
+ } catch {
19482
+ }
19483
+ return result;
19484
+ }
19485
+ var DAY_MS, SESSION_FOLDER;
19486
+ var init_debug_prune = __esm({
19487
+ "packages/experience-design-system-cli/src/tui/debug-prune.ts"() {
19488
+ "use strict";
19489
+ DAY_MS = 24 * 60 * 60 * 1e3;
19490
+ SESSION_FOLDER = /^\d{2}-\d{2}-\d{4}-session-\d+$/;
19491
+ }
19492
+ });
19493
+
19494
+ // packages/experience-design-system-cli/src/tui/debug-store.ts
19495
+ var debug_store_exports = {};
19496
+ __export(debug_store_exports, {
19497
+ currentDebugSessionDir: () => currentDebugSessionDir,
19498
+ currentSessionDir: () => currentSessionDir,
19499
+ finishDebugRun: () => finishDebugRun,
19500
+ startDebugRun: () => startDebugRun,
19501
+ startDebugSessionIn: () => startDebugSessionIn,
19502
+ startImportRun: () => startImportRun
19503
+ });
19504
+ import { mkdir as mkdir7, writeFile as writeFile7 } from "node:fs/promises";
19505
+ import { existsSync as existsSync9, readdirSync as readdirSync3 } from "node:fs";
19506
+ import { join as join19, resolve as resolve20 } from "node:path";
19507
+ function generateRunId() {
19508
+ const now = /* @__PURE__ */ new Date();
19509
+ const pad = (n) => String(n).padStart(2, "0");
19510
+ return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
19511
+ }
19512
+ function datePrefix(now = /* @__PURE__ */ new Date()) {
19513
+ const pad = (n) => String(n).padStart(2, "0");
19514
+ return `${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${now.getFullYear()}`;
19515
+ }
19516
+ function slugify(value) {
19517
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
19518
+ }
19519
+ async function readBaseDir() {
19520
+ try {
19521
+ const dir = (await readSettings2())["contentfulBaseDir"];
19522
+ return typeof dir === "string" && dir !== "" ? resolve20(dir) : void 0;
19523
+ } catch {
19524
+ return void 0;
19525
+ }
19526
+ }
19527
+ function nextSessionId(contentfulDir) {
19528
+ const today = datePrefix();
19529
+ const existing = existsSync9(contentfulDir) ? readdirSync3(contentfulDir) : [];
19530
+ const seqPattern = new RegExp(`^${today}-session-(\\d+)$`);
19531
+ const maxSeq = existing.reduce((max, entry) => {
19532
+ const match2 = seqPattern.exec(entry);
19533
+ return match2 ? Math.max(max, Number(match2[1])) : max;
19534
+ }, 0);
19535
+ return `${today}-session-${maxSeq + 1}`;
19536
+ }
19537
+ function startSession(base, { replace }) {
19538
+ const contentfulDir = join19(base, ".contentful");
19539
+ if (replace || !sessionDir) sessionDir = join19(contentfulDir, nextSessionId(contentfulDir));
19540
+ return sessionDir;
19541
+ }
19542
+ function currentDebugSessionDir() {
19543
+ return sessionDir && join19(sessionDir, "debug");
19544
+ }
19545
+ function currentSessionDir() {
19546
+ return sessionDir;
19547
+ }
19548
+ async function startDebugSessionIn(base, { replace = false } = {}) {
19549
+ try {
19550
+ if (!await isDebugModeEnabled()) return;
19551
+ await mkdir7(join19(startSession(resolve20(base), { replace }), "debug"), { recursive: true });
19552
+ await writeWaitingLogs();
19553
+ } catch {
19554
+ }
19555
+ }
19556
+ function nextImportRunDir(session) {
19557
+ const existing = existsSync9(session) ? readdirSync3(session) : [];
19558
+ const maxRun = existing.reduce((max, entry) => {
19559
+ const match2 = /^dsi-import-(\d+)$/.exec(entry);
19560
+ return match2 ? Math.max(max, Number(match2[1])) : max;
19561
+ }, 0);
19562
+ return join19(session, `dsi-import-${maxRun + 1}`);
19563
+ }
19564
+ async function startImportRun(projectPath) {
19565
+ await startDebugSessionIn(projectPath);
19566
+ if (!sessionDir) return join19(resolve20(projectPath), ".contentful");
19567
+ const dir = nextImportRunDir(sessionDir);
19568
+ await mkdir7(dir, { recursive: true });
19569
+ return dir;
19570
+ }
19571
+ async function isDebugModeEnabled() {
19572
+ try {
19573
+ return (await readDebugModeSetting()).enabled;
19574
+ } catch {
19575
+ return true;
19576
+ }
19577
+ }
19578
+ function redact(value) {
19579
+ const clone = { ...value };
19580
+ if ("cma_token" in clone) {
19581
+ const raw = clone.cma_token;
19582
+ clone.cma_token = typeof raw === "string" && raw.length > 0 ? `[present, ${raw.length} chars]` : "[not set]";
19583
+ }
19584
+ return clone;
19585
+ }
19586
+ function startDebugRun({
19587
+ flow,
19588
+ step,
19589
+ menuOption,
19590
+ inputs
19591
+ }) {
19592
+ const runId = generateRunId();
19593
+ const startedAt = Date.now();
19594
+ const redactedInputs = redact(inputs);
19595
+ isDebugModeEnabled().then((enabled) => {
19596
+ if (!enabled) return;
19597
+ pendingRun = { flow, step, runId, menuOption, inputs: redactedInputs, startedAt };
19598
+ });
19599
+ }
19600
+ async function finishDebugRun({
19601
+ outputs,
19602
+ status,
19603
+ exitMethod
19604
+ }) {
19605
+ const run = pendingRun;
19606
+ if (!run) return;
19607
+ pendingRun = void 0;
19608
+ const durationMs = Date.now() - run.startedAt;
19609
+ const log = {
19610
+ flow: run.flow,
19611
+ menuOption: run.menuOption,
19612
+ content: renderMarkdown(run, { outputs: redact(outputs), status, exitMethod, durationMs })
19613
+ };
19614
+ const root = currentDebugSessionDir();
19615
+ if (!root) {
19616
+ if (waiting.length < WAITING_LIMIT) waiting.push(log);
19617
+ return;
19618
+ }
19619
+ await writeLog(root, log);
19620
+ }
19621
+ async function writeLog(root, log) {
19622
+ const dir = join19(root, ...log.flow.split("/"));
19623
+ await mkdir7(dir, { recursive: true });
19624
+ const filename = nextAttemptFilename(dir, log.menuOption);
19625
+ await writeFile7(join19(dir, filename), log.content, { mode: 384 });
19626
+ }
19627
+ async function writeWaitingLogs() {
19628
+ const root = currentDebugSessionDir();
19629
+ if (!root) return;
19630
+ for (const log of waiting.splice(0)) {
19631
+ try {
19632
+ await writeLog(root, log);
19633
+ } catch {
19634
+ }
19635
+ }
19636
+ }
19637
+ function nextAttemptFilename(dir, menuOption) {
19638
+ const today = datePrefix();
19639
+ const optionSlug = slugify(menuOption);
19640
+ const existing = existsSync9(dir) ? readdirSync3(dir) : [];
19641
+ const attemptPattern = new RegExp(`^${today}-${optionSlug}-attempt-(\\d+)\\.md$`);
19642
+ const maxAttempt = existing.reduce((max, entry) => {
19643
+ const match2 = attemptPattern.exec(entry);
19644
+ return match2 ? Math.max(max, Number(match2[1])) : max;
19645
+ }, 0);
19646
+ return `${today}-${optionSlug}-attempt-${maxAttempt + 1}.md`;
19647
+ }
19648
+ function renderMarkdown(run, finish) {
19649
+ const stepMatch = /^(\d+)-(.+)$/.exec(run.step);
19650
+ const stepLabel = stepMatch ? `${stepMatch[1]} - ${stepMatch[2]}` : run.step;
19651
+ const flowSlug = run.flow.split("/").join("-");
19652
+ return `# Debug Log
19653
+
19654
+ - Run ID: ${run.runId}
19655
+ - Flow: ${flowSlug}
19656
+ - Step: ${stepLabel}
19657
+ - Menu option: ${run.menuOption}
19658
+ - Date: ${(/* @__PURE__ */ new Date()).toISOString()}
19659
+ - CLI version: ${readPackageVersion()}
19660
+ - Platform: ${process.platform} / Node ${process.version}
19661
+ - Duration on screen: ${finish.durationMs}ms
19662
+ - Status: ${finish.status}
19663
+ - Exit method: ${finish.exitMethod}
19664
+
19665
+ ## Inputs
19666
+ \`\`\`json
19667
+ ${JSON.stringify(run.inputs, null, 2)}
19668
+ \`\`\`
19669
+
19670
+ ## Outputs
19671
+ \`\`\`json
19672
+ ${JSON.stringify(finish.outputs, null, 2)}
19673
+ \`\`\`
19674
+ `;
19675
+ }
19676
+ function handleCrash() {
19677
+ return finishDebugRun({ outputs: {}, status: "error", exitMethod: "crashed" });
19678
+ }
19679
+ var pendingRun, sessionDir, WAITING_LIMIT, waiting;
19680
+ var init_debug_store = __esm({
19681
+ "packages/experience-design-system-cli/src/tui/debug-store.ts"() {
19682
+ "use strict";
19683
+ init_config();
19684
+ init_version();
19685
+ init_debug_mode_store();
19686
+ init_debug_prune();
19687
+ void Promise.all([isDebugModeEnabled(), readBaseDir()]).then(async ([enabled, base]) => {
19688
+ if (!base) return;
19689
+ const retention = await readDebugModeSetting().catch(() => void 0);
19690
+ const pruned = retention ? await pruneExpiredLogs(base, retention.retentionDays).catch(() => void 0) : void 0;
19691
+ if (!enabled) return;
19692
+ const debugDir = join19(startSession(base, { replace: false }), "debug");
19693
+ await mkdir7(debugDir, { recursive: true });
19694
+ await writeWaitingLogs();
19695
+ if (retention && pruned && pruned.logs + pruned.folders > 0) {
19696
+ await writeFile7(
19697
+ join19(debugDir, `${datePrefix()}-cleanup-${generateRunId()}.md`),
19698
+ `# Debug Log Cleanup
19699
+
19700
+ - Date: ${(/* @__PURE__ */ new Date()).toISOString()}
19701
+ - Retention: ${retention.retentionDays} days
19702
+ - Logs deleted: ${pruned.logs}
19703
+ - Empty folders removed: ${pruned.folders}
19704
+ `,
19705
+ { mode: 384 }
19706
+ );
19707
+ }
19708
+ });
19709
+ WAITING_LIMIT = 50;
19710
+ waiting = [];
19711
+ process.once("uncaughtException", (err) => {
19712
+ void handleCrash().finally(() => {
19713
+ throw err;
19714
+ });
19715
+ });
19716
+ process.once("unhandledRejection", (reason) => {
19717
+ void handleCrash().finally(() => {
19718
+ throw reason;
19719
+ });
19720
+ });
19721
+ process.once("SIGINT", () => {
19722
+ void handleCrash().finally(() => {
19723
+ process.exit(130);
19724
+ });
19725
+ });
19726
+ }
19727
+ });
19728
+
19410
19729
  // packages/experience-design-system-cli/src/tui/import/steps/00-preflight/logic.ts
19411
19730
  function overviewFor(target) {
19412
19731
  return target === "tokens" ? TOKENS_ONLY_OVERVIEW : COMPONENTS_OVERVIEW;
@@ -19916,12 +20235,37 @@ var init_shared2 = __esm({
19916
20235
  });
19917
20236
 
19918
20237
  // packages/experience-design-system-cli/src/tui/import/steps/00-preflight/screen.tsx
19919
- import { useState as useState4 } from "react";
20238
+ import { useEffect as useEffect4, useRef, useState as useState4 } from "react";
19920
20239
  import { Box as Box9, Text as Text9 } from "ink";
19921
20240
  import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
19922
20241
  function PreflightScreen({ onChoose, onQuit }) {
19923
20242
  const [focus, setFocus] = useState4(0);
19924
- usePreflightControls({ focus, onFocus: setFocus, onChoose, onQuit });
20243
+ const viewed = useRef([optionAt(0)]).current;
20244
+ useEffect4(() => {
20245
+ startDebugRun({
20246
+ flow: "import/preflight",
20247
+ step: "00-preflight",
20248
+ menuOption: "Preflight",
20249
+ inputs: { options: IMPORT_TARGET_OPTIONS.map((option) => option.target), initial_focus: optionAt(0) }
20250
+ });
20251
+ }, []);
20252
+ const moveTo = (next) => {
20253
+ setFocus(next);
20254
+ if (viewed[viewed.length - 1] !== optionAt(next)) viewed.push(optionAt(next));
20255
+ };
20256
+ const choose = (target) => {
20257
+ void finishDebugRun({
20258
+ outputs: { chosen: target, viewed, steps_that_will_run: overviewFor(target).map((step) => step.description) },
20259
+ status: "success",
20260
+ exitMethod: "saved"
20261
+ });
20262
+ onChoose(target);
20263
+ };
20264
+ const quit = () => {
20265
+ void finishDebugRun({ outputs: { chosen: null, viewed }, status: "success", exitMethod: "discarded" });
20266
+ onQuit();
20267
+ };
20268
+ usePreflightControls({ focus, onFocus: moveTo, onChoose: choose, onQuit: quit });
19925
20269
  return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
19926
20270
  /* @__PURE__ */ jsx9(StepHeader, { step: "setup" }),
19927
20271
  /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, paddingX: 2, paddingBottom: 1, children: [
@@ -19933,25 +20277,28 @@ function PreflightScreen({ onChoose, onQuit }) {
19933
20277
  /* @__PURE__ */ jsx9(Text9, { bold: focused, color: focused ? PALETTE.accent : void 0, children: option.label })
19934
20278
  ] }, option.target);
19935
20279
  }) }),
19936
- /* @__PURE__ */ jsx9(OverviewBox, { steps: overviewFor(IMPORT_TARGET_OPTIONS[focus].target) }),
20280
+ /* @__PURE__ */ jsx9(OverviewBox, { steps: overviewFor(optionAt(focus)) }),
19937
20281
  /* @__PURE__ */ jsx9(ControlHints, { hints: PREFLIGHT_CONTROLS })
19938
20282
  ] })
19939
20283
  ] });
19940
20284
  }
20285
+ var optionAt;
19941
20286
  var init_screen = __esm({
19942
20287
  "packages/experience-design-system-cli/src/tui/import/steps/00-preflight/screen.tsx"() {
19943
20288
  "use strict";
19944
20289
  init_theme();
20290
+ init_debug_store();
19945
20291
  init_controls();
19946
20292
  init_logic();
19947
20293
  init_shared2();
19948
20294
  init_ControlHints();
19949
20295
  init_StepHeader();
20296
+ optionAt = (index) => IMPORT_TARGET_OPTIONS[index].target;
19950
20297
  }
19951
20298
  });
19952
20299
 
19953
20300
  // packages/experience-design-system-cli/src/tui/shared/components/ClearableTextInput.tsx
19954
- import { useRef } from "react";
20301
+ import { useRef as useRef2 } from "react";
19955
20302
  import { useInput as useInput6 } from "ink";
19956
20303
  import TextInput from "ink-text-input";
19957
20304
  import { jsx as jsx10 } from "react/jsx-runtime";
@@ -19959,7 +20306,7 @@ function isClearKey(input, key) {
19959
20306
  return key.ctrl && input === "d";
19960
20307
  }
19961
20308
  function ClearableTextInput({ onChange, ...props }) {
19962
- const justCleared = useRef(false);
20309
+ const justCleared = useRef2(false);
19963
20310
  useInput6((input, key) => {
19964
20311
  if (!isClearKey(input, key)) return;
19965
20312
  justCleared.current = true;
@@ -20092,7 +20439,7 @@ var init_controls5 = __esm({
20092
20439
 
20093
20440
  // packages/experience-design-system-cli/src/tui/import/resolve-user-path.ts
20094
20441
  import { homedir as homedir3 } from "node:os";
20095
- import { resolve as resolve20 } from "node:path";
20442
+ import { resolve as resolve21 } from "node:path";
20096
20443
  function stripQuotes(value) {
20097
20444
  const quoted = value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'");
20098
20445
  return value.length >= 2 && quoted ? value.slice(1, -1) : value;
@@ -20101,7 +20448,7 @@ function expandHome(value) {
20101
20448
  return value === "~" || value.startsWith("~/") || value.startsWith("~\\") ? homedir3() + value.slice(1) : value;
20102
20449
  }
20103
20450
  function resolveUserPath(raw) {
20104
- return resolve20(expandHome(stripQuotes(raw.trim())));
20451
+ return resolve21(expandHome(stripQuotes(raw.trim())));
20105
20452
  }
20106
20453
  var init_resolve_user_path = __esm({
20107
20454
  "packages/experience-design-system-cli/src/tui/import/resolve-user-path.ts"() {
@@ -20333,7 +20680,7 @@ var init_scan_files = __esm({
20333
20680
  });
20334
20681
 
20335
20682
  // packages/experience-design-system-cli/src/tui/import/steps/03-path-validation/screen.tsx
20336
- import { useEffect as useEffect4, useMemo, useState as useState7 } from "react";
20683
+ import { useEffect as useEffect5, useMemo, useState as useState7 } from "react";
20337
20684
  import { Box as Box12, Text as Text12 } from "ink";
20338
20685
  import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
20339
20686
  function Row({ row }) {
@@ -20389,7 +20736,7 @@ function PathValidationScreen({
20389
20736
  }) {
20390
20737
  const resolvedPath = useMemo(() => resolveUserPath(projectPath), [projectPath]);
20391
20738
  const [scan, setScan] = useState7();
20392
- useEffect4(() => {
20739
+ useEffect5(() => {
20393
20740
  setScan(scanFiles(resolvedPath));
20394
20741
  }, [resolvedPath]);
20395
20742
  const phase = phaseOf(scan);
@@ -20686,7 +21033,7 @@ var init_validate4 = __esm({
20686
21033
  });
20687
21034
 
20688
21035
  // packages/experience-design-system-cli/src/tui/import/steps/04-credentials/screen.tsx
20689
- import { useRef as useRef2, useState as useState9 } from "react";
21036
+ import { useRef as useRef3, useState as useState9 } from "react";
20690
21037
  import { Box as Box14, Text as Text14, useInput as useInput11 } from "ink";
20691
21038
  import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
20692
21039
  function CredentialsScreen({
@@ -20696,7 +21043,7 @@ function CredentialsScreen({
20696
21043
  validate: validate3 = validateCredentials,
20697
21044
  tokensOnly = false
20698
21045
  }) {
20699
- const initialValues = useRef2(
21046
+ const initialValues = useRef3(
20700
21047
  normalize({
20701
21048
  spaceId: initial.spaceId ?? "",
20702
21049
  environmentId: initial.environmentId || "master",
@@ -21144,16 +21491,16 @@ var init_steps = __esm({
21144
21491
  });
21145
21492
 
21146
21493
  // packages/experience-design-system-cli/src/tui/import/steps/06-extraction/find-skill-path.ts
21147
- import { existsSync as existsSync9 } from "node:fs";
21148
- import { dirname as dirname15, join as join18, resolve as resolve21 } from "node:path";
21494
+ import { existsSync as existsSync10 } from "node:fs";
21495
+ import { dirname as dirname16, join as join20, resolve as resolve22 } from "node:path";
21149
21496
  import { fileURLToPath as fileURLToPath4 } from "node:url";
21150
21497
  function findSkillPath(fileName) {
21151
- const thisDir = dirname15(fileURLToPath4(import.meta.url));
21498
+ const thisDir = dirname16(fileURLToPath4(import.meta.url));
21152
21499
  let dir = thisDir;
21153
21500
  for (; ; ) {
21154
- const candidate = join18(dir, "skills", fileName);
21155
- if (existsSync9(candidate)) return candidate;
21156
- const parent = resolve21(dir, "..");
21501
+ const candidate = join20(dir, "skills", fileName);
21502
+ if (existsSync10(candidate)) return candidate;
21503
+ const parent = resolve22(dir, "..");
21157
21504
  if (parent === dir) {
21158
21505
  throw new Error(`skill file not found (looked for skills/${fileName} from ${thisDir})`);
21159
21506
  }
@@ -21167,9 +21514,9 @@ var init_find_skill_path = __esm({
21167
21514
  });
21168
21515
 
21169
21516
  // packages/experience-design-system-cli/src/tui/import/steps/06-extraction/selection.ts
21170
- import { resolve as resolve22 } from "node:path";
21517
+ import { resolve as resolve23 } from "node:path";
21171
21518
  async function hashSelectPrompt(options) {
21172
- const promptHash = options.promptText ? hashContent(options.promptText) : options.promptPath ? await hashFile(resolve22(options.promptPath)) : await hashFile(SELECT_SKILL_PATH);
21519
+ const promptHash = options.promptText ? hashContent(options.promptText) : options.promptPath ? await hashFile(resolve23(options.promptPath)) : await hashFile(SELECT_SKILL_PATH);
21173
21520
  return hashContent(`${promptHash}|${options.agent}|${options.model ?? ""}`);
21174
21521
  }
21175
21522
  async function runSelection(options) {
@@ -21263,10 +21610,10 @@ var init_selection2 = __esm({
21263
21610
  });
21264
21611
 
21265
21612
  // packages/experience-design-system-cli/src/tui/import/steps/06-extraction/run.ts
21266
- import { mkdir as mkdir7 } from "node:fs/promises";
21267
- import { join as join19, resolve as resolve23 } from "node:path";
21613
+ import { mkdir as mkdir8 } from "node:fs/promises";
21614
+ import { join as join21, resolve as resolve24 } from "node:path";
21268
21615
  async function runExtraction(input, onProgress, deps = realDeps) {
21269
- const outDir = input.outDir ?? join19(resolve23(input.projectPath), ".contentful");
21616
+ const outDir = input.outDir ?? join21(resolve24(input.projectPath), ".contentful");
21270
21617
  let progress = initialProgress({
21271
21618
  hasCredentials: input.credentials !== null,
21272
21619
  hasRawTokens: input.rawTokensPath !== ""
@@ -21370,7 +21717,7 @@ async function runExtraction(input, onProgress, deps = realDeps) {
21370
21717
  }
21371
21718
  async function fetchEntities(credentials, outDir) {
21372
21719
  try {
21373
- await mkdir7(outDir, { recursive: true });
21720
+ await mkdir8(outDir, { recursive: true });
21374
21721
  const result = await fetchAndPersistExistingContentfulEntities({
21375
21722
  spaceId: credentials.spaceId,
21376
21723
  environmentId: credentials.environmentId,
@@ -21390,7 +21737,7 @@ async function resolveSelectionPrompt(overrides) {
21390
21737
  if (select?.kind === "text") return { promptText: select.value };
21391
21738
  if (select?.kind === "path") {
21392
21739
  await resolvePromptOverride(select);
21393
- return { promptPath: resolve23(select.value) };
21740
+ return { promptPath: resolve24(select.value) };
21394
21741
  }
21395
21742
  return {};
21396
21743
  }
@@ -21416,11 +21763,11 @@ var init_run = __esm({
21416
21763
  });
21417
21764
 
21418
21765
  // packages/experience-design-system-cli/src/tui/import/steps/06-extraction/use-extraction.ts
21419
- import { useEffect as useEffect5, useRef as useRef3, useState as useState11 } from "react";
21766
+ import { useEffect as useEffect6, useRef as useRef4, useState as useState11 } from "react";
21420
21767
  function useExtraction(input) {
21421
21768
  const [state2, setState] = useState11();
21422
- const current = useRef3();
21423
- useEffect5(() => {
21769
+ const current = useRef4();
21770
+ useEffect6(() => {
21424
21771
  current.current = input;
21425
21772
  if (!input) {
21426
21773
  setState(void 0);
@@ -21491,11 +21838,11 @@ var init_run_tokens_only = __esm({
21491
21838
  });
21492
21839
 
21493
21840
  // packages/experience-design-system-cli/src/tui/import/steps/06-extraction/use-tokens-only.ts
21494
- import { useEffect as useEffect6, useRef as useRef4, useState as useState12 } from "react";
21841
+ import { useEffect as useEffect7, useRef as useRef5, useState as useState12 } from "react";
21495
21842
  function useTokensOnly(input) {
21496
21843
  const [state2, setState] = useState12();
21497
- const current = useRef4();
21498
- useEffect6(() => {
21844
+ const current = useRef5();
21845
+ useEffect7(() => {
21499
21846
  current.current = input;
21500
21847
  if (!input) {
21501
21848
  setState(void 0);
@@ -21792,6 +22139,7 @@ function GotoBanner({
21792
22139
  borderStyle: "single",
21793
22140
  borderColor: PALETTE.accent,
21794
22141
  paddingX: 1,
22142
+ paddingBottom: 1,
21795
22143
  marginTop: width === void 0 ? 1 : 0,
21796
22144
  width,
21797
22145
  flexShrink: 0,
@@ -21803,7 +22151,7 @@ function GotoBanner({
21803
22151
  return renderRow(row, i, i === highlightIndex);
21804
22152
  }),
21805
22153
  moreBelow > 0 && /* @__PURE__ */ jsx24(Text21, { color: PALETTE.muted, children: ` \u25BC ${moreBelow} more` }),
21806
- footerHint !== void 0 && /* @__PURE__ */ jsx24(Text21, { color: PALETTE.muted, children: footerHint })
22154
+ footerHint !== void 0 && /* @__PURE__ */ jsx24(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx24(Text21, { color: PALETTE.muted, children: footerHint }) })
21807
22155
  ]
21808
22156
  }
21809
22157
  );
@@ -22318,11 +22666,11 @@ function buildVisibleRows(props) {
22318
22666
  if (!expanded) continue;
22319
22667
  const subtree = computeCycleMemberSubtree(key);
22320
22668
  const isLastAtDepthSubtree = (idx) => {
22321
- const own = subtree[idx].depth;
22669
+ const own2 = subtree[idx].depth;
22322
22670
  for (let j = idx + 1; j < subtree.length; j++) {
22323
22671
  const d = subtree[j].depth;
22324
- if (d < own) return true;
22325
- if (d === own) return false;
22672
+ if (d < own2) return true;
22673
+ if (d === own2) return false;
22326
22674
  }
22327
22675
  return true;
22328
22676
  };
@@ -22442,11 +22790,11 @@ function buildVisibleRows(props) {
22442
22790
  injectedChildren.push({ name: c2.name, depth: c2.depth, isCycleChild: false });
22443
22791
  }
22444
22792
  const isLastAtDepth = (idx) => {
22445
- const own = injectedChildren[idx].depth;
22793
+ const own2 = injectedChildren[idx].depth;
22446
22794
  for (let j = idx + 1; j < injectedChildren.length; j++) {
22447
22795
  const d = injectedChildren[j].depth;
22448
- if (d < own) return true;
22449
- if (d === own) return false;
22796
+ if (d < own2) return true;
22797
+ if (d === own2) return false;
22450
22798
  }
22451
22799
  return true;
22452
22800
  };
@@ -22691,7 +23039,7 @@ var init_GroupedSidebar = __esm({
22691
23039
  });
22692
23040
 
22693
23041
  // packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/sidebar/hooks/useImmediateInput.ts
22694
- import { useLayoutEffect, useRef as useRef5 } from "react";
23042
+ import { useLayoutEffect, useRef as useRef6 } from "react";
22695
23043
  import { useStdin } from "ink";
22696
23044
  function parseInput(data) {
22697
23045
  const isShiftTab = data === "\x1B[Z";
@@ -22753,7 +23101,7 @@ function registerInputHandler(stdin, setRawMode, handler) {
22753
23101
  }
22754
23102
  function useImmediateInput(handler, enabled = true) {
22755
23103
  const { stdin, setRawMode } = useStdin();
22756
- const handlerRef = useRef5(handler);
23104
+ const handlerRef = useRef6(handler);
22757
23105
  handlerRef.current = handler;
22758
23106
  useLayoutEffect(() => {
22759
23107
  if (!enabled) return;
@@ -22771,7 +23119,7 @@ var init_useImmediateInput = __esm({
22771
23119
 
22772
23120
  // packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/sidebar/hooks/use-terminal-size.ts
22773
23121
  import { useStdout as useStdout2 } from "ink";
22774
- import { useEffect as useEffect7, useState as useState13 } from "react";
23122
+ import { useEffect as useEffect8, useState as useState13 } from "react";
22775
23123
  function readTerminalSize(stdout) {
22776
23124
  return {
22777
23125
  columns: stdout?.columns ?? 80,
@@ -22781,7 +23129,7 @@ function readTerminalSize(stdout) {
22781
23129
  function useTerminalSize() {
22782
23130
  const { stdout } = useStdout2();
22783
23131
  const [size, setSize] = useState13(() => readTerminalSize(stdout));
22784
- useEffect7(() => {
23132
+ useEffect8(() => {
22785
23133
  const updateSize = () => setSize(readTerminalSize(stdout));
22786
23134
  stdout?.on("resize", updateSize);
22787
23135
  updateSize();
@@ -23783,7 +24131,7 @@ var init_state = __esm({
23783
24131
  });
23784
24132
 
23785
24133
  // packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/use-scope-gate.ts
23786
- import { useEffect as useEffect8, useMemo as useMemo3, useRef as useRef6, useState as useState16 } from "react";
24134
+ import { useEffect as useEffect9, useMemo as useMemo3, useRef as useRef7, useState as useState16 } from "react";
23787
24135
  function useScopeGate({ components, tokenCount, tokensWidth, onConfirm, onQuit }) {
23788
24136
  const { columns: totalWidth, rows: terminalRows } = useTerminalSize();
23789
24137
  const columnPlan = useMemo3(
@@ -23829,8 +24177,8 @@ function useScopeGate({ components, tokenCount, tokensWidth, onConfirm, onQuit }
23829
24177
  for (const p of cycleParticipants) seed.add(p);
23830
24178
  return seed;
23831
24179
  });
23832
- const seededGroupsRef = useRef6(closures.size > 0 || cycleParticipants.size > 0);
23833
- useEffect8(() => {
24180
+ const seededGroupsRef = useRef7(closures.size > 0 || cycleParticipants.size > 0);
24181
+ useEffect9(() => {
23834
24182
  if (seededGroupsRef.current) return;
23835
24183
  if (closures.size === 0 && cycleParticipants.size === 0) return;
23836
24184
  seededGroupsRef.current = true;
@@ -23921,7 +24269,7 @@ function useScopeGate({ components, tokenCount, tokensWidth, onConfirm, onQuit }
23921
24269
  entryCount: 0
23922
24270
  });
23923
24271
  const panelLayout = useMemo3(() => computePanelLayout(visibleCount), [visibleCount]);
23924
- useEffect8(() => {
24272
+ useEffect9(() => {
23925
24273
  setNav((prev) => {
23926
24274
  const maxIdx = Math.max(0, visibleRows.length - 1);
23927
24275
  const nextCursor = Math.min(prev.cursor, maxIdx);
@@ -24494,13 +24842,13 @@ var init_load2 = __esm({
24494
24842
  });
24495
24843
 
24496
24844
  // packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/use-scope-gate-flow.ts
24497
- import { useEffect as useEffect9, useMemo as useMemo5, useRef as useRef7, useState as useState17 } from "react";
24845
+ import { useEffect as useEffect10, useMemo as useMemo5, useRef as useRef8, useState as useState17 } from "react";
24498
24846
  function useScopeGateFlow(input, deps = realDeps3) {
24499
24847
  const sessionId = input?.sessionId;
24500
24848
  const loaded = useMemo5(() => sessionId ? deps.load(sessionId) : void 0, [sessionId]);
24501
- const cacheCheck = useRef7(void 0);
24849
+ const cacheCheck = useRef8(void 0);
24502
24850
  const [confirmed, setConfirmed] = useState17();
24503
- useEffect9(() => {
24851
+ useEffect10(() => {
24504
24852
  if (!input || !loaded?.ok || cacheCheck.current) return;
24505
24853
  cacheCheck.current = deps.checkCache(input.cache);
24506
24854
  }, [input, loaded]);
@@ -24535,7 +24883,7 @@ function formatAcceptanceSummary(counts) {
24535
24883
  function describeGeneration(input) {
24536
24884
  const agent = capitalize(input.agent);
24537
24885
  return `${formatAcceptanceSummary({ accepted: input.acceptedCount, autoRejected: 0 })}
24538
- ${agent} is reading each selected component and writing a Contentful component definition for it${input.hasTokens ? " Using your design tokens for prop resolution." : ""}`;
24886
+ ${agent} is reading each selected component and writing a Contentful component definition for it.${input.hasTokens ? "\nUsing your design tokens for prop resolution." : ""}`;
24539
24887
  }
24540
24888
  function progressRows2(progress, agent) {
24541
24889
  const name = capitalize(agent);
@@ -24638,7 +24986,7 @@ var init_save_decisions = __esm({
24638
24986
  });
24639
24987
 
24640
24988
  // packages/experience-design-system-cli/src/tui/import/steps/08-generation/run.ts
24641
- import { resolve as resolve24 } from "node:path";
24989
+ import { resolve as resolve25 } from "node:path";
24642
24990
  async function runGeneration(input, onProgress, deps = realDeps4) {
24643
24991
  let progress = { components: null, mapTokens: "idle", mapTokensEligible: null };
24644
24992
  const update = (change) => {
@@ -24724,7 +25072,7 @@ async function buildMapTokensOptions(input) {
24724
25072
  if (mapOverride?.kind === "text") prompt.skillContentOverride = mapOverride.value;
24725
25073
  if (mapOverride?.kind === "path") {
24726
25074
  await resolvePromptOverride(mapOverride);
24727
- prompt.skillPathOverride = resolve24(mapOverride.value);
25075
+ prompt.skillPathOverride = resolve25(mapOverride.value);
24728
25076
  }
24729
25077
  if (input.existingEntitiesPath) {
24730
25078
  const entities = await readExistingContentfulEntitiesFromSession({ path: input.existingEntitiesPath });
@@ -24771,11 +25119,11 @@ var init_run2 = __esm({
24771
25119
  });
24772
25120
 
24773
25121
  // packages/experience-design-system-cli/src/tui/import/steps/08-generation/use-generation.ts
24774
- import { useEffect as useEffect10, useRef as useRef8, useState as useState18 } from "react";
25122
+ import { useEffect as useEffect11, useRef as useRef9, useState as useState18 } from "react";
24775
25123
  function useGeneration(input) {
24776
25124
  const [state2, setState] = useState18();
24777
- const current = useRef8();
24778
- useEffect10(() => {
25125
+ const current = useRef9();
25126
+ useEffect11(() => {
24779
25127
  current.current = input;
24780
25128
  if (!input) {
24781
25129
  setState(void 0);
@@ -24802,7 +25150,7 @@ var init_use_generation = __esm({
24802
25150
  });
24803
25151
 
24804
25152
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/build-flow-start.ts
24805
- import { join as join20 } from "node:path";
25153
+ import { join as join22 } from "node:path";
24806
25154
  function buildFlowStart(input) {
24807
25155
  const { extraction, handoff, saved, generatePromptPath, tokensOnly } = input;
24808
25156
  const credentials = extraction.credentials ?? {
@@ -24815,7 +25163,7 @@ function buildFlowStart(input) {
24815
25163
  handoff,
24816
25164
  agent: extraction.agent,
24817
25165
  projectPath: extraction.projectPath,
24818
- outDir: extraction.projectPath ? join20(extraction.projectPath, ".contentful") : "",
25166
+ outDir: extraction.projectPath ? join22(extraction.projectPath, ".contentful") : "",
24819
25167
  rawTokensPath: extraction.rawTokensPath,
24820
25168
  credentials: {
24821
25169
  spaceId: credentials.spaceId,
@@ -25203,12 +25551,141 @@ var init_dtcg2 = __esm({
25203
25551
  }
25204
25552
  });
25205
25553
 
25554
+ // packages/experience-design-system-types/dist/src/property-contract/json-value.js
25555
+ var nodeTypes;
25556
+ var init_json_value = __esm({
25557
+ "packages/experience-design-system-types/dist/src/property-contract/json-value.js"() {
25558
+ "use strict";
25559
+ nodeTypes = globalThis.process?.getBuiltinModule?.("node:util")?.types;
25560
+ }
25561
+ });
25562
+
25563
+ // packages/experience-design-system-types/dist/src/property-contract/date-time-value.js
25564
+ import * as z5 from "zod/mini";
25565
+ var dateTimeValueSchema;
25566
+ var init_date_time_value = __esm({
25567
+ "packages/experience-design-system-types/dist/src/property-contract/date-time-value.js"() {
25568
+ "use strict";
25569
+ dateTimeValueSchema = z5.iso.datetime({ offset: true });
25570
+ }
25571
+ });
25572
+
25573
+ // packages/experience-design-system-types/dist/src/property-contract/discriminated-union.js
25574
+ var init_discriminated_union = __esm({
25575
+ "packages/experience-design-system-types/dist/src/property-contract/discriminated-union.js"() {
25576
+ "use strict";
25577
+ init_json_value();
25578
+ }
25579
+ });
25580
+
25581
+ // packages/experience-design-system-types/dist/src/property-contract/target-definition.js
25582
+ var TARGET_PROPERTY_TYPES, SAME_SPACE_RESOURCE_SOURCE, targetDefinitionJsonSchema;
25583
+ var init_target_definition = __esm({
25584
+ "packages/experience-design-system-types/dist/src/property-contract/target-definition.js"() {
25585
+ "use strict";
25586
+ init_json_value();
25587
+ init_date_time_value();
25588
+ init_discriminated_union();
25589
+ TARGET_PROPERTY_TYPES = [
25590
+ "String",
25591
+ "Number",
25592
+ "Boolean",
25593
+ "Array",
25594
+ "Record",
25595
+ "Integer",
25596
+ "DateTime",
25597
+ "RichText",
25598
+ "JSON",
25599
+ "ResourceLink",
25600
+ "Literal",
25601
+ "DiscriminatedUnion"
25602
+ ];
25603
+ SAME_SPACE_RESOURCE_SOURCE = "crn:contentful:::content:spaces/$self/environments/$self";
25604
+ targetDefinitionJsonSchema = {
25605
+ $schema: "http://json-schema.org/draft-07/schema#",
25606
+ $ref: "#/definitions/target",
25607
+ definitions: {
25608
+ target: {
25609
+ type: "object",
25610
+ required: ["type"],
25611
+ properties: {
25612
+ type: { enum: [...TARGET_PROPERTY_TYPES] },
25613
+ id: { type: "string" },
25614
+ name: { type: "string" },
25615
+ description: { type: "string" },
25616
+ required: { type: "boolean" },
25617
+ values: { type: "array", minItems: 1 },
25618
+ defaultValue: {},
25619
+ items: { $ref: "#/definitions/target" },
25620
+ fields: { type: "object", minProperties: 1, additionalProperties: { $ref: "#/definitions/target" } },
25621
+ allowedResources: {
25622
+ type: "array",
25623
+ minItems: 1,
25624
+ items: {
25625
+ type: "object",
25626
+ required: ["type", "source"],
25627
+ properties: {
25628
+ type: { enum: ["Contentful:Entry", "Contentful:Asset"] },
25629
+ source: { const: SAME_SPACE_RESOURCE_SOURCE },
25630
+ allowedTypes: { type: "array", items: { type: "string" } }
25631
+ },
25632
+ allOf: [
25633
+ {
25634
+ if: { properties: { type: { const: "Contentful:Entry" } } },
25635
+ then: { required: ["allowedTypes"] },
25636
+ else: { not: { required: ["allowedTypes"] } }
25637
+ }
25638
+ ],
25639
+ additionalProperties: false
25640
+ }
25641
+ },
25642
+ value: {},
25643
+ valueType: { $ref: "#/definitions/target" },
25644
+ discriminator: { type: "string", minLength: 1 },
25645
+ members: {
25646
+ type: "array",
25647
+ minItems: 2,
25648
+ uniqueItems: true,
25649
+ items: {
25650
+ allOf: [
25651
+ { $ref: "#/definitions/target" },
25652
+ { type: "object", properties: { type: { const: "Record" } }, required: ["type", "fields"] }
25653
+ ]
25654
+ }
25655
+ }
25656
+ },
25657
+ allOf: [
25658
+ {
25659
+ if: { properties: { type: { const: "DiscriminatedUnion" } } },
25660
+ then: { required: ["discriminator", "members"] },
25661
+ else: { not: { anyOf: [{ required: ["discriminator"] }, { required: ["members"] }] } }
25662
+ }
25663
+ ],
25664
+ additionalProperties: false
25665
+ }
25666
+ }
25667
+ };
25668
+ }
25669
+ });
25670
+
25671
+ // packages/experience-design-system-types/dist/src/property-contract/index.js
25672
+ var init_property_contract = __esm({
25673
+ "packages/experience-design-system-types/dist/src/property-contract/index.js"() {
25674
+ "use strict";
25675
+ init_json_value();
25676
+ init_date_time_value();
25677
+ init_discriminated_union();
25678
+ init_target_definition();
25679
+ }
25680
+ });
25681
+
25206
25682
  // packages/experience-design-system-types/dist/src/index.js
25207
25683
  var init_src4 = __esm({
25208
25684
  "packages/experience-design-system-types/dist/src/index.js"() {
25209
25685
  "use strict";
25210
25686
  init_cdf2();
25211
25687
  init_dtcg2();
25688
+ init_property_contract();
25212
25689
  }
25213
25690
  });
25214
25691
 
@@ -25348,14 +25825,14 @@ function computeRenderStatuses(closure, directIssues) {
25348
25825
  for (const node of closure.nodes) {
25349
25826
  nodesByName.set(node.name, { name: node.name, path: node.path });
25350
25827
  }
25351
- const own = /* @__PURE__ */ new Map();
25828
+ const own2 = /* @__PURE__ */ new Map();
25352
25829
  for (const node of closure.nodes) {
25353
25830
  const s = directIssues.get(node.name);
25354
- if (s && s !== "ok") own.set(node.name, s);
25831
+ if (s && s !== "ok") own2.set(node.name, s);
25355
25832
  }
25356
25833
  const inheritedSources = /* @__PURE__ */ new Map();
25357
25834
  for (const node of closure.nodes) {
25358
- const ownStatus = own.get(node.name);
25835
+ const ownStatus = own2.get(node.name);
25359
25836
  if (!ownStatus) continue;
25360
25837
  for (let i = 0; i < node.path.length - 1; i++) {
25361
25838
  const ancestor = node.path[i];
@@ -25363,7 +25840,7 @@ function computeRenderStatuses(closure, directIssues) {
25363
25840
  inheritedSources.get(ancestor).set(node.name, ownStatus);
25364
25841
  }
25365
25842
  }
25366
- for (const [name, status] of own.entries()) {
25843
+ for (const [name, status] of own2.entries()) {
25367
25844
  out.set(name, {
25368
25845
  status,
25369
25846
  isOwn: true,
@@ -25633,7 +26110,7 @@ var init_scroll_offset = __esm({
25633
26110
  });
25634
26111
 
25635
26112
  // packages/experience-design-system-cli/src/tui/import/steps/09-review/components/FieldEditor.tsx
25636
- import React13, { useEffect as useEffect11, useLayoutEffect as useLayoutEffect2, useState as useState19 } from "react";
26113
+ import React13, { useEffect as useEffect12, useLayoutEffect as useLayoutEffect2, useState as useState19 } from "react";
25637
26114
  import figures6 from "figures";
25638
26115
  import { Box as Box26, measureElement, Text as Text27 } from "ink";
25639
26116
  import { Fragment as Fragment6, jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
@@ -26400,6 +26877,7 @@ function FieldEditor({
26400
26877
  const [rationaleOpen, setRationaleOpen] = useState19(false);
26401
26878
  const [rationaleScrollOffset, setRationaleScrollOffset] = useState19(0);
26402
26879
  const [showHelp, setShowHelp] = useState19(false);
26880
+ const [pendingDelete, setPendingDelete] = useState19(null);
26403
26881
  const props = editorState.props;
26404
26882
  const slots = editorState.slots;
26405
26883
  const contentPropIndexes = React13.useMemo(
@@ -26491,6 +26969,46 @@ function FieldEditor({
26491
26969
  setEditorState(next);
26492
26970
  onChange(serializeState(next, value));
26493
26971
  };
26972
+ const deleteRow = (row) => {
26973
+ if (row.kind === "prop") {
26974
+ const nextProps = removeAt(editorState.props, row.idx);
26975
+ commit({ ...editorState, props: nextProps });
26976
+ setPropIdx(Math.max(0, Math.min(row.idx, nextProps.length - 1)));
26977
+ const rowsAfter2 = selectableRows.filter((r) => !(r.kind === "prop" && r.idx === row.idx));
26978
+ const neighbour2 = rowsAfter2[Math.max(0, selectableRows.findIndex((r) => r.kind === row.kind && r.idx === row.idx) - 1)];
26979
+ if (!neighbour2) {
26980
+ setInSlots(false);
26981
+ setInComponentDesc(true);
26982
+ setFocusLevel("componentDescription");
26983
+ } else if (neighbour2.kind === "prop") {
26984
+ setInSlots(false);
26985
+ setPropIdx(neighbour2.idx > row.idx ? neighbour2.idx - 1 : neighbour2.idx);
26986
+ setFocusLevel("prop");
26987
+ } else {
26988
+ setInSlots(true);
26989
+ setSlotIdx(neighbour2.idx);
26990
+ setFocusLevel("slot");
26991
+ }
26992
+ return;
26993
+ }
26994
+ const nextSlots = removeAt(editorState.slots, row.idx);
26995
+ commit({ ...editorState, slots: nextSlots });
26996
+ const rowsAfter = selectableRows.filter((r) => !(r.kind === "slot" && r.idx === row.idx));
26997
+ const neighbour = rowsAfter[Math.max(0, selectableRows.findIndex((r) => r.kind === row.kind && r.idx === row.idx) - 1)];
26998
+ if (!neighbour) {
26999
+ setInSlots(false);
27000
+ setInComponentDesc(true);
27001
+ setFocusLevel("componentDescription");
27002
+ } else if (neighbour.kind === "slot") {
27003
+ setInSlots(true);
27004
+ setSlotIdx(neighbour.idx > row.idx ? neighbour.idx - 1 : neighbour.idx);
27005
+ setFocusLevel("slot");
27006
+ } else {
27007
+ setInSlots(false);
27008
+ setPropIdx(neighbour.idx);
27009
+ setFocusLevel("prop");
27010
+ }
27011
+ };
26494
27012
  const canonicalize = React13.useCallback((json) => {
26495
27013
  try {
26496
27014
  return JSON.stringify(JSON.parse(json));
@@ -26527,6 +27045,11 @@ function FieldEditor({
26527
27045
  onSave();
26528
27046
  return;
26529
27047
  }
27048
+ if (pendingDelete) {
27049
+ if (input === "y") deleteRow(pendingDelete);
27050
+ setPendingDelete(null);
27051
+ return;
27052
+ }
26530
27053
  const inDescriptionTextEntryForHelp = focusLevel === "field" && editingField && activeField === "description";
26531
27054
  const inStringDefaultTextEntryForHelp = focusLevel === "field" && editingField && activeField === "default" && currentProp != null && (currentProp.type === "string" || currentProp.type === "token");
26532
27055
  const inValueListTextEntry = editingValue != null;
@@ -26726,6 +27249,11 @@ function FieldEditor({
26726
27249
  }
26727
27250
  return;
26728
27251
  }
27252
+ if (input === "d" && !key.ctrl && !key.meta) {
27253
+ if (focusLevel === "prop" && currentProp) setPendingDelete({ kind: "prop", idx: propIdx });
27254
+ if (focusLevel === "slot" && currentSlot) setPendingDelete({ kind: "slot", idx: slotIdx });
27255
+ return;
27256
+ }
26729
27257
  if (focusLevel === "slot" && key.return && currentSlot) {
26730
27258
  setFocusLevel("field");
26731
27259
  setActiveField(SLOT_FIELDS[0] ?? null);
@@ -27032,6 +27560,10 @@ function FieldEditor({
27032
27560
  }
27033
27561
  const hasEmptyProperties = props.length === 0 && slots.length === 0;
27034
27562
  const modeLabel = (() => {
27563
+ if (pendingDelete) {
27564
+ const name = pendingDelete.kind === "prop" ? props[pendingDelete.idx]?.name : slots[pendingDelete.idx]?.name;
27565
+ return `Delete ${pendingDelete.kind} "${name}"? [y] delete \xB7 any other key cancels`;
27566
+ }
27035
27567
  if (editingValue) {
27036
27568
  return editingValue.mode === "add" ? "Enter to add \xB7 [Esc] to cancel" : "Enter to save edit \xB7 [Esc] to cancel";
27037
27569
  }
@@ -27053,7 +27585,7 @@ function FieldEditor({
27053
27585
  if (rationaleOpen) {
27054
27586
  return "[\u2191\u2193] scroll [i/Esc] close rationale panel";
27055
27587
  }
27056
- return `[\u2191\u2193] navigate rows [Enter] edit fields [s] source [${propRationaleKey}] prop rationale [${componentRationaleKey}] component rationale [h] help [Esc] exit panel`;
27588
+ return `[\u2191\u2193] navigate [Enter] edit fields [d] delete property [h] help [s] source [Esc] exit panel`;
27057
27589
  })();
27058
27590
  const rows = [];
27059
27591
  rows.push({ kind: "component-description" });
@@ -27105,7 +27637,7 @@ function FieldEditor({
27105
27637
  }
27106
27638
  return Math.max(start + 1, end2);
27107
27639
  };
27108
- useEffect11(() => {
27640
+ useEffect12(() => {
27109
27641
  if (focusLevel === "field" || selectedRowIdx < 0) return;
27110
27642
  setListScrollStart((previous) => {
27111
27643
  if (initialFocusTarget && !initialFocusScrollApplied.current) {
@@ -27319,6 +27851,7 @@ function FieldEditor({
27319
27851
  /* @__PURE__ */ jsx32(Text27, { bold: true, children: "Row navigation" }),
27320
27852
  /* @__PURE__ */ jsx32(Text27, { children: " \u2191/\u2193 move between rows" }),
27321
27853
  /* @__PURE__ */ jsx32(Text27, { children: " Enter edit fields on the current row" }),
27854
+ /* @__PURE__ */ jsx32(Text27, { children: " d delete the current prop or slot (asks to confirm)" }),
27322
27855
  /* @__PURE__ */ jsx32(Text27, { children: " Esc exit the panel" }),
27323
27856
  /* @__PURE__ */ jsx32(Text27, { children: " " }),
27324
27857
  /* @__PURE__ */ jsx32(Text27, { bold: true, children: "Field editing" }),
@@ -27342,7 +27875,7 @@ function FieldEditor({
27342
27875
  /* @__PURE__ */ jsx32(Text27, { color: PALETTE.muted, children: "press ? or Esc to close" })
27343
27876
  ] }),
27344
27877
  validationError && /* @__PURE__ */ jsx32(Text27, { color: PALETTE.error, children: "\u2717 " + validationError }),
27345
- /* @__PURE__ */ jsx32(Box26, { paddingTop: 1, children: /* @__PURE__ */ jsx32(Text27, { color: PALETTE.muted, children: modeLabel }) })
27878
+ /* @__PURE__ */ jsx32(Box26, { paddingTop: 1, children: /* @__PURE__ */ jsx32(Text27, { color: pendingDelete ? PALETTE.text : PALETTE.muted, children: modeLabel }) })
27346
27879
  ]
27347
27880
  }
27348
27881
  );
@@ -27735,8 +28268,8 @@ function TokenReviewPanel({
27735
28268
  borderStyle: "single",
27736
28269
  borderColor: active ? PALETTE.text : void 0,
27737
28270
  children: [
27738
- /* @__PURE__ */ jsx39(Text32, { bold: true, color: active ? void 0 : PALETTE.muted, wrap: "truncate", children: `TOKEN REVIEW \u2014 ${componentName} \xB7 ${current.propName} (edit allowed)` }),
27739
- /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, wrap: "truncate", children: `select which tokens are allowed (${rangeLabel})` }),
28271
+ /* @__PURE__ */ jsx39(Text32, { bold: true, color: active ? void 0 : PALETTE.muted, wrap: "truncate", children: `Review mapped tokens \u2014 ${componentName} \xB7 ${current.propName}${current.kinds.length > 0 ? ` (${current.kinds.join(", ")})` : ""}` }),
28272
+ /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, wrap: "truncate", children: `Tick the tokens this property may use (${rangeLabel}).` }),
27740
28273
  /* @__PURE__ */ jsx39(Text32, { children: " " }),
27741
28274
  current.paths.length === 0 && /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, children: "(no compatible tokens found for this token kind)" }),
27742
28275
  visiblePaths.map((path5, i) => {
@@ -27769,18 +28302,33 @@ function TokenReviewPanel({
27769
28302
  borderStyle: "single",
27770
28303
  borderColor: active ? PALETTE.text : void 0,
27771
28304
  children: [
27772
- /* @__PURE__ */ jsx39(Text32, { bold: true, color: active ? void 0 : PALETTE.muted, children: `TOKEN REVIEW \u2014 ${componentName}` }),
28305
+ /* @__PURE__ */ jsx39(Text32, { bold: true, color: active ? void 0 : PALETTE.muted, children: `Review mapped tokens \u2014 ${componentName}` }),
28306
+ /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, wrap: "truncate", children: `${componentName} has ${suggestions.length} design ${suggestions.length === 1 ? "prop" : "props"} of type token. Select which tokens are allowed for each property.` }),
28307
+ /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, wrap: "truncate", children: "Suggested tokens come from the agent. Allowed tokens are what editors can pick in Contentful." }),
28308
+ /* @__PURE__ */ jsx39(Text32, { children: " " }),
27773
28309
  suggestions.length === 0 && /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, children: "(no token suggestions for this component)" }),
27774
28310
  suggestions.map((s, i) => {
27775
28311
  const focused = i === selectedRow;
27776
28312
  return /* @__PURE__ */ jsxs30(Box31, { flexDirection: "column", children: [
27777
- /* @__PURE__ */ jsx39(Box31, { children: /* @__PURE__ */ jsx39(Text32, { color: focused ? PALETTE.accent : active ? void 0 : PALETTE.muted, bold: focused, children: `${focused ? figures7.pointer : " "} ${s.propName}` }) }),
27778
- /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, children: ` suggested: ${s.suggested.join(", ")}` }),
27779
- /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, children: ` allowed: ${s.allowed.join(", ")}` })
28313
+ /* @__PURE__ */ jsx39(Box31, { children: /* @__PURE__ */ jsx39(Text32, { color: focused ? PALETTE.accent : active ? void 0 : PALETTE.muted, bold: focused, children: `${focused ? figures7.pointer : " "} ${s.propName}${s.kinds.length > 0 ? ` (${s.kinds.join(", ")})` : ""}` }) }),
28314
+ /* @__PURE__ */ jsx39(
28315
+ Text32,
28316
+ {
28317
+ color: PALETTE.muted,
28318
+ children: ` suggested: ${s.suggested.length > 0 ? s.suggested.join(", ") : "none"}`
28319
+ }
28320
+ ),
28321
+ /* @__PURE__ */ jsx39(
28322
+ Text32,
28323
+ {
28324
+ color: PALETTE.muted,
28325
+ children: ` allowed: ${s.allowed.length > 0 ? s.allowed.join(", ") : "none (any token)"}`
28326
+ }
28327
+ ),
28328
+ /* @__PURE__ */ jsx39(Text32, { children: " " })
27780
28329
  ] }, s.propName);
27781
28330
  }),
27782
- /* @__PURE__ */ jsx39(Text32, { children: " " }),
27783
- /* @__PURE__ */ jsx39(Text32, { children: "[\u2191/\u2193] move [Enter] edit allowed [Esc] close" })
28331
+ /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, children: "[\u2191/\u2193] move [Enter] edit allowed tokens [Esc] close" })
27784
28332
  ]
27785
28333
  }
27786
28334
  );
@@ -28054,15 +28602,9 @@ function ReviewFinalizeError({
28054
28602
  function ReviewPanelFooter({
28055
28603
  reviewEditor,
28056
28604
  sidebarFocused,
28057
- sidebarFooter,
28058
- livePreview,
28059
- livePreviewSpinner
28605
+ sidebarFooter
28060
28606
  }) {
28061
- return /* @__PURE__ */ jsxs32(Fragment10, { children: [
28062
- reviewEditor.panelOpen === "token-review" ? " [\u2191/\u2193] move [Enter] edit allowed [Esc] close" : sidebarFocused ? sidebarFooter : reviewEditor.showJson ? " [\u2191\u2193] scroll [Ctrl+u/d] half-page [gg/G] top/bottom [Tab] focus list" : " [Tab] focus list (edit fields)" + (reviewEditor.currentTokenSuggestions().length > 0 ? " [t] token review" : ""),
28063
- livePreview.status === "running" && /* @__PURE__ */ jsx42(Text34, { children: ` ${livePreviewSpinner} live preview` }),
28064
- livePreview.disabled && /* @__PURE__ */ jsx42(Text34, { children: " \xB7 live preview disabled" })
28065
- ] });
28607
+ return /* @__PURE__ */ jsx42(Fragment10, { children: reviewEditor.panelOpen === "token-review" ? "" : sidebarFocused ? sidebarFooter : reviewEditor.showJson ? " [\u2191\u2193] scroll [Ctrl+u/d] half-page [gg/G] top/bottom [Tab] focus list" : " [Tab] focus list [Enter] edit fields" + (reviewEditor.currentTokenSuggestions().length > 0 ? " [t] review mapped tokens" : "") });
28066
28608
  }
28067
28609
  function ReviewComponentTitle({
28068
28610
  selectedKey,
@@ -28096,9 +28638,7 @@ function ReviewComponentPanel({
28096
28638
  sidebarFocused,
28097
28639
  fieldEditor,
28098
28640
  saveError,
28099
- sidebarFooter,
28100
- livePreview,
28101
- livePreviewSpinner
28641
+ sidebarFooter
28102
28642
  }) {
28103
28643
  const propCount = Object.keys(selectedEntry.$properties).length;
28104
28644
  const slotCount = selectedEntry.$slots ? Object.keys(selectedEntry.$slots).length : 0;
@@ -28135,9 +28675,7 @@ function ReviewComponentPanel({
28135
28675
  {
28136
28676
  reviewEditor,
28137
28677
  sidebarFocused,
28138
- sidebarFooter,
28139
- livePreview,
28140
- livePreviewSpinner
28678
+ sidebarFooter
28141
28679
  }
28142
28680
  ) }) })
28143
28681
  ] }) });
@@ -28494,7 +29032,7 @@ var init_FinalizeDialog = __esm({
28494
29032
  });
28495
29033
 
28496
29034
  // packages/experience-design-system-cli/src/tui/import/steps/09-review/use-finalize-preview.ts
28497
- import { useEffect as useEffect12, useRef as useRef9, useState as useState20 } from "react";
29035
+ import { useEffect as useEffect13, useRef as useRef10, useState as useState20 } from "react";
28498
29036
  function useReviewFinalizePreview({
28499
29037
  components,
28500
29038
  ...options
@@ -28509,9 +29047,9 @@ function useFinalizePreview(opts) {
28509
29047
  const [status, setStatus] = useState20("idle");
28510
29048
  const [removed, setRemoved] = useState20([]);
28511
29049
  const [scrollOffset, setScrollOffset] = useState20(0);
28512
- const generationRef = useRef9(0);
29050
+ const generationRef = useRef10(0);
28513
29051
  const acceptedKey = [...acceptedKeys].sort().join("\0");
28514
- useEffect12(() => {
29052
+ useEffect13(() => {
28515
29053
  if (!open) {
28516
29054
  setStatus("idle");
28517
29055
  setRemoved([]);
@@ -28563,7 +29101,7 @@ var init_use_finalize_preview = __esm({
28563
29101
  });
28564
29102
 
28565
29103
  // packages/experience-design-system-cli/src/tui/import/steps/09-review/use-review-session.ts
28566
- import { useCallback as useCallback2, useEffect as useEffect13, useRef as useRef10, useState as useState21 } from "react";
29104
+ import { useCallback as useCallback2, useEffect as useEffect14, useRef as useRef11, useState as useState21 } from "react";
28567
29105
  function useReviewSession({
28568
29106
  loadSession,
28569
29107
  tokensPath,
@@ -28582,7 +29120,7 @@ function useReviewSession({
28582
29120
  }));
28583
29121
  return { result, catalog };
28584
29122
  }, [loadSession, tokensPath]);
28585
- useEffect13(() => {
29123
+ useEffect14(() => {
28586
29124
  let disposed = false;
28587
29125
  void (async () => {
28588
29126
  try {
@@ -28635,7 +29173,7 @@ function useReviewSession({
28635
29173
  function useReviewMetadata({ components, selectedIdx, extractSessionId }) {
28636
29174
  const [reviewMetadata, setReviewMetadata] = useState21(null);
28637
29175
  const [componentRationale, setComponentRationale] = useState21(null);
28638
- useEffect13(() => {
29176
+ useEffect14(() => {
28639
29177
  const current = components[selectedIdx];
28640
29178
  if (!current) {
28641
29179
  setReviewMetadata(null);
@@ -28665,9 +29203,9 @@ function useReviewMetadata({ components, selectedIdx, extractSessionId }) {
28665
29203
  return { reviewMetadata, componentRationale };
28666
29204
  }
28667
29205
  function useReviewHistory({ loading, components, createSnapshot, applySnapshot }) {
28668
- const historyRef = useRef10(null);
28669
- const historySeededRef = useRef10(false);
28670
- useEffect13(() => {
29206
+ const historyRef = useRef11(null);
29207
+ const historySeededRef = useRef11(false);
29208
+ useEffect14(() => {
28671
29209
  if (loading || historySeededRef.current) return;
28672
29210
  historySeededRef.current = true;
28673
29211
  historyRef.current = createHistoryStack(createSnapshot(components));
@@ -28739,7 +29277,7 @@ function collectTokenSuggestions(entry, availableTokens = []) {
28739
29277
  const compatiblePaths = new Set(compatible);
28740
29278
  const allowed = availableTokens.length > 0 ? persistedAllowed.filter((path5) => compatiblePaths.has(path5)) : persistedAllowed;
28741
29279
  const paths = availableTokens.length > 0 ? compatible : allowed;
28742
- return { propName, paths, suggested: [...allowed], allowed };
29280
+ return { propName, kinds, paths, suggested: [...allowed], allowed };
28743
29281
  });
28744
29282
  }
28745
29283
  var init_token_suggestions = __esm({
@@ -28749,7 +29287,7 @@ var init_token_suggestions = __esm({
28749
29287
  });
28750
29288
 
28751
29289
  // packages/experience-design-system-cli/src/tui/import/steps/09-review/use-review-editor.ts
28752
- import { useEffect as useEffect14, useRef as useRef11, useState as useState22 } from "react";
29290
+ import { useEffect as useEffect15, useRef as useRef12, useState as useState22 } from "react";
28753
29291
  function parseReviewEntry(draftValue) {
28754
29292
  const parsed = JSON.parse(draftValue);
28755
29293
  const keys = Object.keys(parsed);
@@ -28781,9 +29319,9 @@ function useReviewEditor({
28781
29319
  const [tokenReviewEditing, setTokenReviewEditing] = useState22(false);
28782
29320
  const [tokenReviewEditCursor, setTokenReviewEditCursor] = useState22(0);
28783
29321
  const [tokenReviewEditSelection, setTokenReviewEditSelection] = useState22(/* @__PURE__ */ new Set());
28784
- const tokenReviewSuggestedRef = useRef11(/* @__PURE__ */ new Map());
28785
- const pendingGRef = useRef11(false);
28786
- useEffect14(() => {
29322
+ const tokenReviewSuggestedRef = useRef12(/* @__PURE__ */ new Map());
29323
+ const pendingGRef = useRef12(false);
29324
+ useEffect15(() => {
28787
29325
  setTokenReviewRow(0);
28788
29326
  setTokenReviewEditing(false);
28789
29327
  setTokenReviewEditCursor(0);
@@ -29265,24 +29803,24 @@ var init_preview_annotations = __esm({
29265
29803
  });
29266
29804
 
29267
29805
  // packages/experience-design-system-cli/src/tui/import/steps/09-review/use-live-preview.ts
29268
- import { useCallback as useCallback3, useEffect as useEffect15, useRef as useRef12, useState as useState24 } from "react";
29806
+ import { useCallback as useCallback3, useEffect as useEffect16, useRef as useRef13, useState as useState24 } from "react";
29269
29807
  function useLivePreview(opts) {
29270
29808
  const debounceMs = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
29271
29809
  const [status, setStatus] = useState24("idle");
29272
29810
  const [disabled, setDisabled] = useState24(false);
29273
- const timerRef = useRef12(null);
29274
- const generationRef = useRef12(0);
29275
- const latestRef = useRef12(0);
29276
- const inflightRef = useRef12(0);
29277
- const optsRef = useRef12(opts);
29278
- useEffect15(() => {
29811
+ const timerRef = useRef13(null);
29812
+ const generationRef = useRef13(0);
29813
+ const latestRef = useRef13(0);
29814
+ const inflightRef = useRef13(0);
29815
+ const optsRef = useRef13(opts);
29816
+ useEffect16(() => {
29279
29817
  optsRef.current = opts;
29280
29818
  });
29281
- const disabledRef = useRef12(disabled);
29282
- useEffect15(() => {
29819
+ const disabledRef = useRef13(disabled);
29820
+ useEffect16(() => {
29283
29821
  disabledRef.current = disabled;
29284
29822
  }, [disabled]);
29285
- useEffect15(() => {
29823
+ useEffect16(() => {
29286
29824
  return () => {
29287
29825
  if (timerRef.current) {
29288
29826
  clearTimeout(timerRef.current);
@@ -29363,7 +29901,7 @@ var init_use_live_preview = __esm({
29363
29901
  });
29364
29902
 
29365
29903
  // packages/experience-design-system-cli/src/tui/import/steps/09-review/use-review-preview.ts
29366
- import { useCallback as useCallback4, useEffect as useEffect16, useState as useState25 } from "react";
29904
+ import { useCallback as useCallback4, useEffect as useEffect17, useState as useState25 } from "react";
29367
29905
  function useReviewPreview({
29368
29906
  components,
29369
29907
  loading,
@@ -29405,13 +29943,13 @@ function useReviewPreview({
29405
29943
  deleteAllComponents
29406
29944
  });
29407
29945
  const [spinnerTick, setSpinnerTick] = useState25(0);
29408
- useEffect16(() => {
29946
+ useEffect17(() => {
29409
29947
  if (livePreviewHook.status !== "running") return;
29410
29948
  const id = setInterval(() => setSpinnerTick((tick) => tick + 1), 80);
29411
29949
  return () => clearInterval(id);
29412
29950
  }, [livePreviewHook.status]);
29413
29951
  const livePreviewSpinner = SPINNER_FRAMES[spinnerTick % SPINNER_FRAMES.length];
29414
- useEffect16(() => {
29952
+ useEffect17(() => {
29415
29953
  if (loading) return;
29416
29954
  if (!livePreview) return;
29417
29955
  if (components.length === 0) return;
@@ -29468,7 +30006,7 @@ var init_LivePreviewSummary = __esm({
29468
30006
  });
29469
30007
 
29470
30008
  // packages/experience-design-system-cli/src/tui/import/steps/09-review/generate-review-step.tsx
29471
- import { useCallback as useCallback5, useEffect as useEffect17, useMemo as useMemo6, useRef as useRef13, useState as useState26 } from "react";
30009
+ import { useCallback as useCallback5, useEffect as useEffect18, useMemo as useMemo6, useRef as useRef14, useState as useState26 } from "react";
29472
30010
  import figures8 from "figures";
29473
30011
  import { Box as Box38, Text as Text40 } from "ink";
29474
30012
  import { Fragment as Fragment14, jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
@@ -29682,7 +30220,7 @@ function GenerateReviewStepView({
29682
30220
  setFinalizeError
29683
30221
  } = reviewSurface;
29684
30222
  const [removedBannerCollapsed, setRemovedBannerCollapsed] = useState26(false);
29685
- const removedBannerDefaultedRef = useRef13(false);
30223
+ const removedBannerDefaultedRef = useRef14(false);
29686
30224
  const [cyclePanelScroll, setCyclePanelScroll] = useState26(0);
29687
30225
  const [cyclesCursor, setCyclesCursor] = useState26(0);
29688
30226
  const cyclePanel = useOverlayPanel({
@@ -29702,7 +30240,7 @@ function GenerateReviewStepView({
29702
30240
  const [breakCursor, setBreakCursor] = useState26(0);
29703
30241
  const [breakConfirm, setBreakConfirm] = useState26(false);
29704
30242
  const [expandedGroups, setExpandedGroups] = useState26(/* @__PURE__ */ new Set());
29705
- const seededGroupsRef = useRef13(false);
30243
+ const seededGroupsRef = useRef14(false);
29706
30244
  const {
29707
30245
  searchOpen,
29708
30246
  setSearchOpen,
@@ -29726,7 +30264,7 @@ function GenerateReviewStepView({
29726
30264
  const [pendingEditorFocus, setPendingEditorFocus] = useState26(null);
29727
30265
  const [autoRejected, setAutoRejected] = useState26([]);
29728
30266
  const [undoSnapshot, setUndoSnapshot] = useState26(null);
29729
- const autoRejectFiredRef = useRef13(false);
30267
+ const autoRejectFiredRef = useRef14(false);
29730
30268
  const [showReloadDialog, setShowReloadDialog] = useState26(false);
29731
30269
  const acceptedCountForPreview = components.filter((c2) => c2.status === "accepted").length;
29732
30270
  const { previewAnnotations, removedComponents, livePreviewHook, livePreviewSpinner } = useReviewPreview({
@@ -29789,7 +30327,7 @@ function GenerateReviewStepView({
29789
30327
  [components]
29790
30328
  );
29791
30329
  const closures = useMemo6(() => computeAllClosures(componentGraph), [componentGraph]);
29792
- useEffect17(() => {
30330
+ useEffect18(() => {
29793
30331
  if (seededGroupsRef.current) return;
29794
30332
  if (closures.size === 0 && slotCycles.length === 0) return;
29795
30333
  seededGroupsRef.current = true;
@@ -29816,7 +30354,7 @@ function GenerateReviewStepView({
29816
30354
  suggestedBreak: cycle.edges.length > 0 ? suggestCycleBreakEdge2(cycle, cycleView.pushBlocking) : null
29817
30355
  }));
29818
30356
  }, [slotCycles, cycleView]);
29819
- useEffect17(() => {
30357
+ useEffect18(() => {
29820
30358
  const decision = computeAutoRejectDecision({
29821
30359
  loading,
29822
30360
  autoRejectFired: autoRejectFiredRef.current,
@@ -29859,8 +30397,8 @@ function GenerateReviewStepView({
29859
30397
  }),
29860
30398
  applySnapshot: applyHistorySnapshot
29861
30399
  });
29862
- const autoRejectPushedRef = useRef13(false);
29863
- useEffect17(() => {
30400
+ const autoRejectPushedRef = useRef14(false);
30401
+ useEffect18(() => {
29864
30402
  if (loading) return;
29865
30403
  if (!historySeededRef.current) return;
29866
30404
  if (!autoRejectFiredRef.current) return;
@@ -29944,7 +30482,7 @@ function GenerateReviewStepView({
29944
30482
  const panelHeight = leftPanelHeight + 2;
29945
30483
  const panelContentHeight = Math.max(1, panelHeight - 3);
29946
30484
  const cycleViewportHeight = Math.max(1, panelHeight - 4);
29947
- useEffect17(() => {
30485
+ useEffect18(() => {
29948
30486
  if (selectableRowPositions.length === 0) return;
29949
30487
  const cursorInRange = selectableRowPositions.includes(cursorRowIdx);
29950
30488
  const maxScroll = Math.max(0, visibleRowsMemo.length - visibleCount);
@@ -30690,9 +31228,7 @@ function GenerateReviewStepView({
30690
31228
  initialFocusTarget: pendingEditorFocus && pendingEditorFocus.componentName === selected.key ? pendingEditorFocus.target : { kind: "description" }
30691
31229
  }
30692
31230
  ),
30693
- saveError: reviewEditor.saveError,
30694
- livePreview: livePreviewHook,
30695
- livePreviewSpinner
31231
+ saveError: reviewEditor.saveError
30696
31232
  }
30697
31233
  ) : /* @__PURE__ */ jsx48(ReviewNoSelection, {})
30698
31234
  ] }),
@@ -30724,7 +31260,7 @@ function GenerateReviewStepView({
30724
31260
  hidden: dialogOpen
30725
31261
  }
30726
31262
  ),
30727
- !dialogOpen && sidebarFocused && /* @__PURE__ */ jsx48(CompactControlBar, { hasGroupRoots, searchActive: searchOpen || searchQuery.length > 0 })
31263
+ !dialogOpen && sidebarFocused && reviewEditor.panelOpen !== "token-review" && /* @__PURE__ */ jsx48(CompactControlBar, { hasGroupRoots, searchActive: searchOpen || searchQuery.length > 0 })
30728
31264
  ] });
30729
31265
  }
30730
31266
  var HELP_SECTIONS2;
@@ -30806,7 +31342,7 @@ var init_generate_review_step = __esm({
30806
31342
  { keys: "P", label: "Component rationale" },
30807
31343
  { keys: "s", label: "Source code snippet" },
30808
31344
  { keys: "J", label: "Toggle JSON" },
30809
- { keys: "t", label: "Token review" }
31345
+ { keys: "t", label: "Review mapped tokens" }
30810
31346
  ]
30811
31347
  },
30812
31348
  {
@@ -31005,7 +31541,7 @@ var init_DoneStep = __esm({
31005
31541
  });
31006
31542
 
31007
31543
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/path-utils.ts
31008
- import { resolve as resolve25 } from "node:path";
31544
+ import { resolve as resolve26 } from "node:path";
31009
31545
  import { homedir as homedir4 } from "node:os";
31010
31546
  function expandTilde(input) {
31011
31547
  if (input === "~" || input.startsWith("~/") || input.startsWith("~\\")) {
@@ -31019,7 +31555,7 @@ function normalizePath(input) {
31019
31555
  p = p.slice(1, -1);
31020
31556
  }
31021
31557
  p = expandTilde(p);
31022
- return resolve25(p);
31558
+ return resolve26(p);
31023
31559
  }
31024
31560
  var init_path_utils = __esm({
31025
31561
  "packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/path-utils.ts"() {
@@ -31028,10 +31564,10 @@ var init_path_utils = __esm({
31028
31564
  });
31029
31565
 
31030
31566
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/use-blinking-cursor.ts
31031
- import { useEffect as useEffect18, useState as useState27 } from "react";
31567
+ import { useEffect as useEffect19, useState as useState27 } from "react";
31032
31568
  function useBlinkingCursor() {
31033
31569
  const [visible, setVisible] = useState27(true);
31034
- useEffect18(() => {
31570
+ useEffect19(() => {
31035
31571
  const interval = setInterval(() => setVisible((current) => !current), 500);
31036
31572
  return () => clearInterval(interval);
31037
31573
  }, []);
@@ -31046,29 +31582,29 @@ var init_use_blinking_cursor = __esm({
31046
31582
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/screens/PathPrompt.tsx
31047
31583
  import { useState as useState28 } from "react";
31048
31584
  import { Box as Box42, Text as Text44 } from "ink";
31049
- import { readdirSync as readdirSync3 } from "node:fs";
31050
- import { dirname as dirname16, basename as basename6, join as join21 } from "node:path";
31585
+ import { readdirSync as readdirSync4 } from "node:fs";
31586
+ import { dirname as dirname17, basename as basename6, join as join23 } from "node:path";
31051
31587
  import { jsx as jsx52, jsxs as jsxs42 } from "react/jsx-runtime";
31052
31588
  function autocomplete(partial) {
31053
31589
  if (!partial) return null;
31054
31590
  const normalized = normalizePath(partial);
31055
- const dir = dirname16(normalized);
31591
+ const dir = dirname17(normalized);
31056
31592
  const base = basename6(normalized);
31057
31593
  let entries;
31058
31594
  try {
31059
- entries = readdirSync3(dir);
31595
+ entries = readdirSync4(dir);
31060
31596
  } catch {
31061
31597
  return null;
31062
31598
  }
31063
31599
  const matches = entries.filter((e) => e.startsWith(base));
31064
31600
  if (matches.length === 0) return null;
31065
- if (matches.length === 1) return join21(dir, matches[0]);
31601
+ if (matches.length === 1) return join23(dir, matches[0]);
31066
31602
  let prefix = matches[0];
31067
31603
  for (const m of matches) {
31068
31604
  while (!m.startsWith(prefix)) prefix = prefix.slice(0, -1);
31069
31605
  if (!prefix) break;
31070
31606
  }
31071
- return prefix.length > base.length ? join21(dir, prefix) : null;
31607
+ return prefix.length > base.length ? join23(dir, prefix) : null;
31072
31608
  }
31073
31609
  function PathPrompt({
31074
31610
  defaultPath,
@@ -31168,12 +31704,12 @@ var init_produced = __esm({
31168
31704
  });
31169
31705
 
31170
31706
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/use-timed-spinner.tsx
31171
- import { useEffect as useEffect19, useState as useState29 } from "react";
31707
+ import { useEffect as useEffect20, useState as useState29 } from "react";
31172
31708
  import { Spinner as Spinner2 } from "@inkjs/ui";
31173
31709
  import { jsx as jsx53 } from "react/jsx-runtime";
31174
31710
  function useTimedSpinner() {
31175
31711
  const [elapsedSeconds, setElapsedSeconds] = useState29(0);
31176
- useEffect19(() => {
31712
+ useEffect20(() => {
31177
31713
  const timer = setInterval(() => setElapsedSeconds((current) => current + 1), 1e3);
31178
31714
  return () => {
31179
31715
  clearInterval(timer);
@@ -31385,12 +31921,20 @@ function diffProperty(name, old, newDef) {
31385
31921
  diffs.push({ key: `${name}-new`, color: PALETTE.success, text: `+ ${name}: ${formatPropDef(newDef)}` });
31386
31922
  return diffs;
31387
31923
  }
31924
+ function formatDefault(value) {
31925
+ const sys = value?.sys;
31926
+ if (sys?.linkType === "Contentful:DesignToken" && typeof sys.urn === "string") {
31927
+ const name = sys.urn.split("/designTokens/")[1];
31928
+ if (name) return name;
31929
+ }
31930
+ return JSON.stringify(value);
31931
+ }
31388
31932
  function formatOldProp(prop) {
31389
31933
  const parts = [];
31390
31934
  if (prop.type) parts.push(prop.type);
31391
31935
  if (prop.category) parts.push(prop.category);
31392
31936
  parts.push(prop.required ? "required" : "optional");
31393
- if (prop.default !== void 0) parts.push(`default=${JSON.stringify(prop.default)}`);
31937
+ if (prop.default !== void 0) parts.push(`default=${formatDefault(prop.default)}`);
31394
31938
  return parts.join(", ");
31395
31939
  }
31396
31940
  function formatPropDef(def) {
@@ -31399,7 +31943,7 @@ function formatPropDef(def) {
31399
31943
  if (def["$category"]) parts.push(def["$category"]);
31400
31944
  if (def["$required"] === true) parts.push("required");
31401
31945
  else parts.push("optional");
31402
- if (def["$default"] !== void 0) parts.push(`default=${JSON.stringify(def["$default"])}`);
31946
+ if (def["$default"] !== void 0) parts.push(`default=${formatDefault(def["$default"])}`);
31403
31947
  return parts.join(", ");
31404
31948
  }
31405
31949
  var init_preview_diff = __esm({
@@ -31434,7 +31978,7 @@ var init_preview_confirmation_input = __esm({
31434
31978
  });
31435
31979
 
31436
31980
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/screens/WizardPreviewStep.tsx
31437
- import { useEffect as useEffect20, useState as useState30, useMemo as useMemo7 } from "react";
31981
+ import { useEffect as useEffect21, useState as useState30, useMemo as useMemo7 } from "react";
31438
31982
  import { Box as Box44, Text as Text46 } from "ink";
31439
31983
  import { Fragment as Fragment16, jsx as jsx55, jsxs as jsxs44 } from "react/jsx-runtime";
31440
31984
  function buildPreviewSummaryLines(preview) {
@@ -31644,7 +32188,7 @@ function WizardPreviewStep({
31644
32188
  const activeLines = diffExpanded ? diffLines : summaryLines;
31645
32189
  const maxScroll = Math.max(0, activeLines.length - viewportHeight);
31646
32190
  const boundedScrollOffset = Math.min(scrollOffset, maxScroll);
31647
- useEffect20(() => {
32191
+ useEffect21(() => {
31648
32192
  setScrollOffset((previous) => Math.min(previous, maxScroll));
31649
32193
  }, [maxScroll]);
31650
32194
  useImmediateInput((input, key) => {
@@ -32280,8 +32824,8 @@ var init_run_print_files_helpers = __esm({
32280
32824
  });
32281
32825
 
32282
32826
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/save.ts
32283
- import { writeFile as writeFile7 } from "node:fs/promises";
32284
- import { join as join22 } from "node:path";
32827
+ import { writeFile as writeFile8 } from "node:fs/promises";
32828
+ import { join as join24 } from "node:path";
32285
32829
  function buildSavedCDF(components, tokens, opts = {}) {
32286
32830
  const cdf = buildCDF2(components, toCdfTokens(tokens), opts);
32287
32831
  if (!cdf) throw new Error("nothing to save \u2014 no components or tokens resolved");
@@ -32293,7 +32837,7 @@ function buildSavedCDF(components, tokens, opts = {}) {
32293
32837
  }
32294
32838
  async function runPrintFiles(input, deps, onPatch) {
32295
32839
  onPatch({ step: "printing" });
32296
- const componentsPath = join22(input.outDir, "components.json");
32840
+ const componentsPath = join24(input.outDir, "components.json");
32297
32841
  try {
32298
32842
  const db = deps.openDb();
32299
32843
  let components = [];
@@ -32377,7 +32921,7 @@ var init_save = __esm({
32377
32921
  realSaveDeps = () => ({
32378
32922
  openDb: openPipelineDb,
32379
32923
  readTokens: (path5) => readTokensFromPath2({ flag: "tokens", path: path5 }),
32380
- writeFile: (path5, contents) => writeFile7(path5, contents),
32924
+ writeFile: (path5, contents) => writeFile8(path5, contents),
32381
32925
  appendRun,
32382
32926
  buildSourceFingerprint,
32383
32927
  sleep: (ms) => new Promise((resolve29) => setTimeout(resolve29, ms)),
@@ -32400,9 +32944,9 @@ var init_types9 = __esm({
32400
32944
 
32401
32945
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/controller.ts
32402
32946
  import { appendFileSync, writeFileSync } from "node:fs";
32403
- import { mkdir as mkdir8 } from "node:fs/promises";
32947
+ import { mkdir as mkdir9 } from "node:fs/promises";
32404
32948
  import { tmpdir } from "node:os";
32405
- import { join as join23, resolve as resolve26 } from "node:path";
32949
+ import { join as join25, resolve as resolve27 } from "node:path";
32406
32950
  function appendWizardLog(entry) {
32407
32951
  appendFileSync(WIZARD_LOG, JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry }) + "\n");
32408
32952
  }
@@ -32425,7 +32969,7 @@ function realFlowDeps() {
32425
32969
  }
32426
32970
  },
32427
32971
  mkdir: async (path5) => {
32428
- await mkdir8(path5, { recursive: true });
32972
+ await mkdir9(path5, { recursive: true });
32429
32973
  },
32430
32974
  detectSaveConflict,
32431
32975
  buildTimestampedSubdir,
@@ -32655,367 +33199,71 @@ function createFlowController(start, deps) {
32655
33199
  },
32656
33200
  async submitPath(submitted) {
32657
33201
  await deps.mkdir(submitted);
32658
- if (resolve26(submitted) !== resolve26(state2.outDir) && await deps.detectSaveConflict(submitted)) {
33202
+ if (resolve27(submitted) !== resolve27(state2.outDir) && await deps.detectSaveConflict(submitted)) {
32659
33203
  const subdir = deps.buildTimestampedSubdir(submitted);
32660
33204
  await deps.mkdir(subdir);
32661
33205
  await proceedToWrite(subdir);
32662
33206
  return;
32663
33207
  }
32664
- await proceedToWrite(submitted);
32665
- },
32666
- confirmPreview: (acknowledge) => void push(acknowledge),
32667
- acknowledgeBreakingChanges: () => void push(true),
32668
- editDefinitions: () => update({ step: "final-review" }),
32669
- async skipValidationErrors() {
32670
- if (state2.extractSessionId) {
32671
- const names = namesToSkip(state2.previewValidationErrors);
32672
- if (names.length > 0) await deps.rejectComponentsByName(state2.extractSessionId, names);
32673
- }
32674
- await preview();
32675
- }
32676
- };
32677
- }
32678
- var WIZARD_LOG;
32679
- var init_controller = __esm({
32680
- "packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/controller.ts"() {
32681
- "use strict";
32682
- init_src3();
32683
- init_slot_graph();
32684
- init_cycle_detection();
32685
- init_wizard_state_transitions();
32686
- init_preview2();
32687
- init_push();
32688
- init_save();
32689
- init_types9();
32690
- WIZARD_LOG = join23(tmpdir(), "experiences-import-wizard.log");
32691
- }
32692
- });
32693
-
32694
- // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/use-flow.ts
32695
- import { useEffect as useEffect21, useRef as useRef14, useSyncExternalStore } from "react";
32696
- function useFlow(start, options = {}) {
32697
- const controller = useRef14();
32698
- if (start && !controller.current) {
32699
- try {
32700
- startWizardLog();
32701
- } catch {
32702
- }
32703
- const flowDeps = (options.deps ?? realFlowDeps)();
32704
- controller.current = createFlowController(start, {
32705
- ...flowDeps,
32706
- ...options.beforeStepChange ? { beforeStepChange: options.beforeStepChange } : {}
32707
- });
32708
- }
32709
- const current = controller.current;
32710
- const state2 = useSyncExternalStore(
32711
- (listener) => current?.subscribe(listener) ?? (() => void 0),
32712
- () => current?.getState()
32713
- );
32714
- useEffect21(() => {
32715
- if (!start) controller.current = void 0;
32716
- }, [start]);
32717
- return current && state2 ? { controller: current, state: state2 } : void 0;
32718
- }
32719
- var init_use_flow = __esm({
32720
- "packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/use-flow.ts"() {
32721
- "use strict";
32722
- init_controller();
32723
- }
32724
- });
32725
-
32726
- // packages/experience-design-system-cli/src/tui/settings/debug-mode/debug-mode-store.ts
32727
- function toRetentionDays(value) {
32728
- return RETENTION_CHOICES.find((choice) => choice === value) ?? DEFAULT_RETENTION_DAYS;
32729
- }
32730
- async function readDebugModeSetting() {
32731
- const { debugMode } = await readSettings2();
32732
- const saved = debugMode ?? {};
32733
- return {
32734
- enabled: typeof saved.enabled === "boolean" ? saved.enabled : true,
32735
- retentionDays: toRetentionDays(saved.retentionDays)
32736
- };
32737
- }
32738
- async function writeDebugModeSetting(change) {
32739
- await updateSettings2({ debugMode: { ...await readDebugModeSetting(), ...change } });
32740
- }
32741
- var RETENTION_CHOICES, DEFAULT_RETENTION_DAYS;
32742
- var init_debug_mode_store = __esm({
32743
- "packages/experience-design-system-cli/src/tui/settings/debug-mode/debug-mode-store.ts"() {
32744
- "use strict";
32745
- init_config();
32746
- RETENTION_CHOICES = [7, 14, 30];
32747
- DEFAULT_RETENTION_DAYS = 30;
32748
- }
32749
- });
32750
-
32751
- // packages/experience-design-system-cli/src/tui/debug-prune.ts
32752
- import { readdir as readdir2, rm, rmdir, stat as stat4 } from "node:fs/promises";
32753
- import { dirname as dirname17, join as join24 } from "node:path";
32754
- async function logFilesIn(dir) {
32755
- const entries = await readdir2(dir, { withFileTypes: true });
32756
- const nested = await Promise.all(
32757
- entries.map(async (entry) => {
32758
- const path5 = join24(dir, entry.name);
32759
- if (entry.isDirectory()) return logFilesIn(path5);
32760
- return entry.isFile() && entry.name.endsWith(".md") ? [path5] : [];
32761
- })
32762
- );
32763
- return nested.flat();
32764
- }
32765
- async function removeEmptyFolders(from, stop) {
32766
- let dir = from;
32767
- let removed = 0;
32768
- for (; ; ) {
32769
- try {
32770
- await rmdir(dir);
32771
- } catch {
32772
- return removed;
32773
- }
32774
- removed += 1;
32775
- if (dir === stop) return removed;
32776
- dir = dirname17(dir);
32777
- }
32778
- }
32779
- async function pruneExpiredLogs(base, retentionDays, now = Date.now()) {
32780
- const cutoff = now - retentionDays * DAY_MS;
32781
- const contentfulDir = join24(base, ".contentful");
32782
- const result = { logs: 0, folders: 0 };
32783
- try {
32784
- const sessions = (await readdir2(contentfulDir)).filter((name) => SESSION_FOLDER.test(name));
32785
- for (const session of sessions) {
32786
- const files = await logFilesIn(join24(contentfulDir, session, "debug")).catch(() => []);
32787
- for (const file of files) {
32788
- try {
32789
- if ((await stat4(file)).mtimeMs >= cutoff) continue;
32790
- await rm(file);
32791
- result.logs += 1;
32792
- result.folders += await removeEmptyFolders(dirname17(file), join24(contentfulDir, session));
32793
- } catch {
32794
- }
32795
- }
33208
+ await proceedToWrite(submitted);
33209
+ },
33210
+ confirmPreview: (acknowledge) => void push(acknowledge),
33211
+ acknowledgeBreakingChanges: () => void push(true),
33212
+ editDefinitions: () => update({ step: "final-review" }),
33213
+ async skipValidationErrors() {
33214
+ if (state2.extractSessionId) {
33215
+ const names = namesToSkip(state2.previewValidationErrors);
33216
+ if (names.length > 0) await deps.rejectComponentsByName(state2.extractSessionId, names);
33217
+ }
33218
+ await preview();
32796
33219
  }
32797
- } catch {
32798
- }
32799
- return result;
33220
+ };
32800
33221
  }
32801
- var DAY_MS, SESSION_FOLDER;
32802
- var init_debug_prune = __esm({
32803
- "packages/experience-design-system-cli/src/tui/debug-prune.ts"() {
33222
+ var WIZARD_LOG;
33223
+ var init_controller = __esm({
33224
+ "packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/controller.ts"() {
32804
33225
  "use strict";
32805
- DAY_MS = 24 * 60 * 60 * 1e3;
32806
- SESSION_FOLDER = /^\d{2}-\d{2}-\d{4}-session-\d+$/;
33226
+ init_src3();
33227
+ init_slot_graph();
33228
+ init_cycle_detection();
33229
+ init_wizard_state_transitions();
33230
+ init_preview2();
33231
+ init_push();
33232
+ init_save();
33233
+ init_types9();
33234
+ WIZARD_LOG = join25(tmpdir(), "experiences-import-wizard.log");
32807
33235
  }
32808
33236
  });
32809
33237
 
32810
- // packages/experience-design-system-cli/src/tui/debug-store.ts
32811
- var debug_store_exports = {};
32812
- __export(debug_store_exports, {
32813
- currentDebugSessionDir: () => currentDebugSessionDir,
32814
- currentSessionDir: () => currentSessionDir,
32815
- finishDebugRun: () => finishDebugRun,
32816
- startDebugRun: () => startDebugRun,
32817
- startDebugSessionIn: () => startDebugSessionIn,
32818
- startImportRun: () => startImportRun
32819
- });
32820
- import { mkdir as mkdir9, writeFile as writeFile8 } from "node:fs/promises";
32821
- import { existsSync as existsSync10, readdirSync as readdirSync4 } from "node:fs";
32822
- import { join as join25, resolve as resolve27 } from "node:path";
32823
- function generateRunId() {
32824
- const now = /* @__PURE__ */ new Date();
32825
- const pad = (n) => String(n).padStart(2, "0");
32826
- return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
32827
- }
32828
- function datePrefix(now = /* @__PURE__ */ new Date()) {
32829
- const pad = (n) => String(n).padStart(2, "0");
32830
- return `${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${now.getFullYear()}`;
32831
- }
32832
- function slugify(value) {
32833
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
32834
- }
32835
- async function readBaseDir() {
32836
- try {
32837
- const dir = (await readSettings2())["contentfulBaseDir"];
32838
- return typeof dir === "string" && dir !== "" ? resolve27(dir) : void 0;
32839
- } catch {
32840
- return void 0;
32841
- }
32842
- }
32843
- function nextSessionId(contentfulDir) {
32844
- const today = datePrefix();
32845
- const existing = existsSync10(contentfulDir) ? readdirSync4(contentfulDir) : [];
32846
- const seqPattern = new RegExp(`^${today}-session-(\\d+)$`);
32847
- const maxSeq = existing.reduce((max, entry) => {
32848
- const match2 = seqPattern.exec(entry);
32849
- return match2 ? Math.max(max, Number(match2[1])) : max;
32850
- }, 0);
32851
- return `${today}-session-${maxSeq + 1}`;
32852
- }
32853
- function startSession(base, { replace }) {
32854
- const contentfulDir = join25(base, ".contentful");
32855
- if (replace || !sessionDir) sessionDir = join25(contentfulDir, nextSessionId(contentfulDir));
32856
- return sessionDir;
32857
- }
32858
- function currentDebugSessionDir() {
32859
- return sessionDir && join25(sessionDir, "debug");
32860
- }
32861
- function currentSessionDir() {
32862
- return sessionDir;
32863
- }
32864
- async function startDebugSessionIn(base, { replace = false } = {}) {
32865
- try {
32866
- if (!await isDebugModeEnabled()) return;
32867
- await mkdir9(join25(startSession(resolve27(base), { replace }), "debug"), { recursive: true });
32868
- } catch {
32869
- }
32870
- }
32871
- function nextImportRunDir(session) {
32872
- const existing = existsSync10(session) ? readdirSync4(session) : [];
32873
- const maxRun = existing.reduce((max, entry) => {
32874
- const match2 = /^dsi-import-(\d+)$/.exec(entry);
32875
- return match2 ? Math.max(max, Number(match2[1])) : max;
32876
- }, 0);
32877
- return join25(session, `dsi-import-${maxRun + 1}`);
32878
- }
32879
- async function startImportRun(projectPath) {
32880
- await startDebugSessionIn(projectPath);
32881
- if (!sessionDir) return join25(resolve27(projectPath), ".contentful");
32882
- const dir = nextImportRunDir(sessionDir);
32883
- await mkdir9(dir, { recursive: true });
32884
- return dir;
32885
- }
32886
- async function isDebugModeEnabled() {
32887
- try {
32888
- return (await readDebugModeSetting()).enabled;
32889
- } catch {
32890
- return true;
32891
- }
32892
- }
32893
- function redact(value) {
32894
- const clone = { ...value };
32895
- if ("cma_token" in clone) {
32896
- const raw = clone.cma_token;
32897
- clone.cma_token = typeof raw === "string" && raw.length > 0 ? `[present, ${raw.length} chars]` : "[not set]";
33238
+ // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/use-flow.ts
33239
+ import { useEffect as useEffect22, useRef as useRef15, useSyncExternalStore } from "react";
33240
+ function useFlow(start, options = {}) {
33241
+ const controller = useRef15();
33242
+ if (start && !controller.current) {
33243
+ try {
33244
+ startWizardLog();
33245
+ } catch {
33246
+ }
33247
+ const flowDeps = (options.deps ?? realFlowDeps)();
33248
+ controller.current = createFlowController(start, {
33249
+ ...flowDeps,
33250
+ ...options.beforeStepChange ? { beforeStepChange: options.beforeStepChange } : {}
33251
+ });
32898
33252
  }
32899
- return clone;
32900
- }
32901
- function startDebugRun({
32902
- flow,
32903
- step,
32904
- menuOption,
32905
- inputs
32906
- }) {
32907
- const runId = generateRunId();
32908
- const startedAt = Date.now();
32909
- const redactedInputs = redact(inputs);
32910
- isDebugModeEnabled().then((enabled) => {
32911
- if (!enabled) return;
32912
- pendingRun = { flow, step, runId, menuOption, inputs: redactedInputs, startedAt };
32913
- });
32914
- }
32915
- async function finishDebugRun({
32916
- outputs,
32917
- status,
32918
- exitMethod
32919
- }) {
32920
- const run = pendingRun;
32921
- if (!run) return;
32922
- pendingRun = void 0;
32923
- const durationMs = Date.now() - run.startedAt;
32924
- const content = renderMarkdown(run, { outputs: redact(outputs), status, exitMethod, durationMs });
32925
- const root = currentDebugSessionDir();
32926
- if (!root) return;
32927
- const flowSegments = run.flow.split("/");
32928
- const dir = join25(root, ...flowSegments);
32929
- await mkdir9(dir, { recursive: true });
32930
- const filename = nextAttemptFilename(dir, run.menuOption);
32931
- await writeFile8(join25(dir, filename), content, { mode: 384 });
32932
- }
32933
- function nextAttemptFilename(dir, menuOption) {
32934
- const today = datePrefix();
32935
- const optionSlug = slugify(menuOption);
32936
- const existing = existsSync10(dir) ? readdirSync4(dir) : [];
32937
- const attemptPattern = new RegExp(`^${today}-${optionSlug}-attempt-(\\d+)\\.md$`);
32938
- const maxAttempt = existing.reduce((max, entry) => {
32939
- const match2 = attemptPattern.exec(entry);
32940
- return match2 ? Math.max(max, Number(match2[1])) : max;
32941
- }, 0);
32942
- return `${today}-${optionSlug}-attempt-${maxAttempt + 1}.md`;
32943
- }
32944
- function renderMarkdown(run, finish) {
32945
- const stepMatch = /^(\d+)-(.+)$/.exec(run.step);
32946
- const stepLabel = stepMatch ? `${stepMatch[1]} - ${stepMatch[2]}` : run.step;
32947
- const flowSlug = run.flow.split("/").join("-");
32948
- return `# Debug Log
32949
-
32950
- - Run ID: ${run.runId}
32951
- - Flow: ${flowSlug}
32952
- - Step: ${stepLabel}
32953
- - Menu option: ${run.menuOption}
32954
- - Date: ${(/* @__PURE__ */ new Date()).toISOString()}
32955
- - CLI version: ${readPackageVersion()}
32956
- - Platform: ${process.platform} / Node ${process.version}
32957
- - Duration on screen: ${finish.durationMs}ms
32958
- - Status: ${finish.status}
32959
- - Exit method: ${finish.exitMethod}
32960
-
32961
- ## Inputs
32962
- \`\`\`json
32963
- ${JSON.stringify(run.inputs, null, 2)}
32964
- \`\`\`
32965
-
32966
- ## Outputs
32967
- \`\`\`json
32968
- ${JSON.stringify(finish.outputs, null, 2)}
32969
- \`\`\`
32970
- `;
32971
- }
32972
- function handleCrash() {
32973
- return finishDebugRun({ outputs: {}, status: "error", exitMethod: "crashed" });
33253
+ const current = controller.current;
33254
+ const state2 = useSyncExternalStore(
33255
+ (listener) => current?.subscribe(listener) ?? (() => void 0),
33256
+ () => current?.getState()
33257
+ );
33258
+ useEffect22(() => {
33259
+ if (!start) controller.current = void 0;
33260
+ }, [start]);
33261
+ return current && state2 ? { controller: current, state: state2 } : void 0;
32974
33262
  }
32975
- var pendingRun, sessionDir;
32976
- var init_debug_store = __esm({
32977
- "packages/experience-design-system-cli/src/tui/debug-store.ts"() {
33263
+ var init_use_flow = __esm({
33264
+ "packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/use-flow.ts"() {
32978
33265
  "use strict";
32979
- init_config();
32980
- init_version();
32981
- init_debug_mode_store();
32982
- init_debug_prune();
32983
- void Promise.all([isDebugModeEnabled(), readBaseDir()]).then(async ([enabled, base]) => {
32984
- if (!base) return;
32985
- const retention = await readDebugModeSetting().catch(() => void 0);
32986
- const pruned = retention ? await pruneExpiredLogs(base, retention.retentionDays).catch(() => void 0) : void 0;
32987
- if (!enabled) return;
32988
- const debugDir = join25(startSession(base, { replace: false }), "debug");
32989
- await mkdir9(debugDir, { recursive: true });
32990
- if (retention && pruned && pruned.logs + pruned.folders > 0) {
32991
- await writeFile8(
32992
- join25(debugDir, `${datePrefix()}-cleanup-${generateRunId()}.md`),
32993
- `# Debug Log Cleanup
32994
-
32995
- - Date: ${(/* @__PURE__ */ new Date()).toISOString()}
32996
- - Retention: ${retention.retentionDays} days
32997
- - Logs deleted: ${pruned.logs}
32998
- - Empty folders removed: ${pruned.folders}
32999
- `,
33000
- { mode: 384 }
33001
- );
33002
- }
33003
- });
33004
- process.once("uncaughtException", (err) => {
33005
- void handleCrash().finally(() => {
33006
- throw err;
33007
- });
33008
- });
33009
- process.once("unhandledRejection", (reason) => {
33010
- void handleCrash().finally(() => {
33011
- throw reason;
33012
- });
33013
- });
33014
- process.once("SIGINT", () => {
33015
- void handleCrash().finally(() => {
33016
- process.exit(130);
33017
- });
33018
- });
33266
+ init_controller();
33019
33267
  }
33020
33268
  });
33021
33269
 
@@ -33086,7 +33334,7 @@ var init_help_info = __esm({
33086
33334
  });
33087
33335
 
33088
33336
  // packages/experience-design-system-cli/src/tui/help/PageContainer.tsx
33089
- import { useEffect as useEffect22, useState as useState31 } from "react";
33337
+ import { useEffect as useEffect23, useState as useState31 } from "react";
33090
33338
  import { Box as Box46, Text as Text47, useInput as useInput13 } from "ink";
33091
33339
  import { Fragment as Fragment17, jsx as jsx57, jsxs as jsxs46 } from "react/jsx-runtime";
33092
33340
  function Section({ title, rows }) {
@@ -33100,7 +33348,7 @@ function Section({ title, rows }) {
33100
33348
  }
33101
33349
  function HelpScreen({ onDone }) {
33102
33350
  const [info, setInfo] = useState31();
33103
- useEffect22(() => {
33351
+ useEffect23(() => {
33104
33352
  startDebugRun({ flow: "help", step: "01-help", menuOption: "Help", inputs: {} });
33105
33353
  void readHelpInfo().then(setInfo);
33106
33354
  }, []);
@@ -33521,7 +33769,7 @@ var init_AgentStatusBadge = __esm({
33521
33769
  });
33522
33770
 
33523
33771
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentDetail.tsx
33524
- import { useEffect as useEffect23, useState as useState33 } from "react";
33772
+ import { useEffect as useEffect24, useState as useState33 } from "react";
33525
33773
  import { Box as Box48, Text as Text50 } from "ink";
33526
33774
  import { jsx as jsx60, jsxs as jsxs48 } from "react/jsx-runtime";
33527
33775
  function StatusHeading() {
@@ -33542,7 +33790,7 @@ function AgentDetail({
33542
33790
  const [testing, setTesting] = useState33(false);
33543
33791
  const [notice, setNotice] = useState33(null);
33544
33792
  const [checks, setChecks] = useState33(void 0);
33545
- useEffect23(() => {
33793
+ useEffect24(() => {
33546
33794
  let active = true;
33547
33795
  setChecks(void 0);
33548
33796
  const timer = setTimeout(() => {
@@ -33634,7 +33882,7 @@ var init_AgentDetail = __esm({
33634
33882
  });
33635
33883
 
33636
33884
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentsScreen.tsx
33637
- import { useEffect as useEffect24, useRef as useRef15, useState as useState34 } from "react";
33885
+ import { useEffect as useEffect25, useRef as useRef16, useState as useState34 } from "react";
33638
33886
  import { Box as Box49, Text as Text51, useInput as useInput15 } from "ink";
33639
33887
  import { Fragment as Fragment18, jsx as jsx61, jsxs as jsxs49 } from "react/jsx-runtime";
33640
33888
  function AgentsScreen({ onDone }) {
@@ -33645,7 +33893,7 @@ function AgentsScreen({ onDone }) {
33645
33893
  const [saving, setSaving] = useState34(false);
33646
33894
  const [loadError, setLoadError] = useState34(null);
33647
33895
  const [checks, setChecks] = useState34({});
33648
- const visit = useRef15({
33896
+ const visit = useRef16({
33649
33897
  trail: createTrail(),
33650
33898
  before: void 0,
33651
33899
  environment: void 0,
@@ -33658,7 +33906,7 @@ function AgentsScreen({ onDone }) {
33658
33906
  );
33659
33907
  }
33660
33908
  };
33661
- useEffect24(() => {
33909
+ useEffect25(() => {
33662
33910
  readAgentConfig().then((loaded) => {
33663
33911
  setConfig(loaded);
33664
33912
  inspectAll(loaded);
@@ -33884,7 +34132,7 @@ var init_project_store = __esm({
33884
34132
  });
33885
34133
 
33886
34134
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/ProjectScreen.tsx
33887
- import { useEffect as useEffect25, useRef as useRef16, useState as useState35 } from "react";
34135
+ import { useEffect as useEffect26, useRef as useRef17, useState as useState35 } from "react";
33888
34136
  import { Box as Box50, Text as Text52, useInput as useInput16 } from "ink";
33889
34137
  import { Fragment as Fragment19, jsx as jsx62, jsxs as jsxs50 } from "react/jsx-runtime";
33890
34138
  function debugFields(config) {
@@ -33903,14 +34151,14 @@ function ProjectScreen({ onDone }) {
33903
34151
  const [saving, setSaving] = useState35(false);
33904
34152
  const [notice, setNotice] = useState35(null);
33905
34153
  const [loadError, setLoadError] = useState35(null);
33906
- const visit = useRef16({
34154
+ const visit = useRef17({
33907
34155
  trail: createTrail(),
33908
34156
  before: void 0,
33909
34157
  persisted: void 0,
33910
34158
  error: ""
33911
34159
  }).current;
33912
34160
  const scrub = (err) => scrubError(err, [config?.cmaToken ?? ""]);
33913
- useEffect25(() => {
34161
+ useEffect26(() => {
33914
34162
  readCredentials().then((cfg) => {
33915
34163
  setConfig(cfg);
33916
34164
  visit.before = snapshotOf(cfg);
@@ -34097,13 +34345,13 @@ var init_ConfigurationScreen = __esm({
34097
34345
  });
34098
34346
 
34099
34347
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/screen.tsx
34100
- import { useEffect as useEffect26, useState as useState37 } from "react";
34348
+ import { useEffect as useEffect27, useState as useState37 } from "react";
34101
34349
  import { Box as Box52, Text as Text54, useInput as useInput18 } from "ink";
34102
34350
  import { Fragment as Fragment20, jsx as jsx64, jsxs as jsxs52 } from "react/jsx-runtime";
34103
34351
  function OptInAnalyticsScreen({ onDone }) {
34104
34352
  const [loading, setLoading] = useState37(true);
34105
34353
  const [enabled, setEnabled] = useState37(true);
34106
- useEffect26(() => {
34354
+ useEffect27(() => {
34107
34355
  readAnalyticsSetting().then((setting) => {
34108
34356
  setEnabled(setting.enabled);
34109
34357
  setLoading(false);
@@ -34166,7 +34414,7 @@ var init_screen12 = __esm({
34166
34414
  });
34167
34415
 
34168
34416
  // packages/experience-design-system-cli/src/tui/settings/debug-mode/screen.tsx
34169
- import { useEffect as useEffect27, useRef as useRef17, useState as useState38 } from "react";
34417
+ import { useEffect as useEffect28, useRef as useRef18, useState as useState38 } from "react";
34170
34418
  import { Box as Box53, Text as Text55, useInput as useInput19 } from "ink";
34171
34419
  import { Fragment as Fragment21, jsx as jsx65, jsxs as jsxs53 } from "react/jsx-runtime";
34172
34420
  function DebugModeScreen({ onDone }) {
@@ -34176,8 +34424,8 @@ function DebugModeScreen({ onDone }) {
34176
34424
  RETENTION_CHOICES[RETENTION_CHOICES.length - 1]
34177
34425
  );
34178
34426
  const [notice, setNotice] = useState38(null);
34179
- const visit = useRef17({ trail: createTrail(), before: void 0, error: "" }).current;
34180
- useEffect27(() => {
34427
+ const visit = useRef18({ trail: createTrail(), before: void 0, error: "" }).current;
34428
+ useEffect28(() => {
34181
34429
  readDebugModeSetting().then((loaded) => {
34182
34430
  setSetting(loaded);
34183
34431
  setPending(loaded.retentionDays);
@@ -34318,7 +34566,7 @@ var app_exports = {};
34318
34566
  __export(app_exports, {
34319
34567
  runApp: () => runApp
34320
34568
  });
34321
- import { useEffect as useEffect28, useState as useState39 } from "react";
34569
+ import { useEffect as useEffect29, useState as useState39 } from "react";
34322
34570
  import { Fragment as Fragment22, jsx as jsx66 } from "react/jsx-runtime";
34323
34571
  function App({ importOptions, inkClear }) {
34324
34572
  const [screen, setScreen] = useState39("start");
@@ -34352,7 +34600,7 @@ function App({ importOptions, inkClear }) {
34352
34600
  const [flowStart, setFlowStart] = useState39();
34353
34601
  const flow = useFlow(flowStart, { beforeStepChange: inkClear });
34354
34602
  const [importDefaults, setImportDefaults] = useState39();
34355
- useEffect28(() => {
34603
+ useEffect29(() => {
34356
34604
  if (screen !== "import") {
34357
34605
  setImportDefaults(void 0);
34358
34606
  return;
@@ -34425,7 +34673,7 @@ function App({ importOptions, inkClear }) {
34425
34673
  }
34426
34674
  });
34427
34675
  };
34428
- useEffect28(() => {
34676
+ useEffect29(() => {
34429
34677
  if (!tokensOnlyRun || tokensOnly?.status !== "done" || !tokensOnly.result.ok) return;
34430
34678
  const { credentialsSkipped, input, tokens } = tokensOnlyRun;
34431
34679
  const { tokensPath, tokenSessionId, tokenCount } = tokensOnly.result;
@@ -34447,12 +34695,12 @@ function App({ importOptions, inkClear }) {
34447
34695
  }
34448
34696
  });
34449
34697
  }, [tokensOnly, tokensOnlyRun]);
34450
- useEffect28(() => {
34698
+ useEffect29(() => {
34451
34699
  if (!extractionRun || extraction?.status !== "done" || !extraction.result.ok) return;
34452
34700
  setExtractionRun(void 0);
34453
34701
  setScopeRun({ ...extractionRun, extraction: extraction.result.extraction });
34454
34702
  }, [extraction, extractionRun]);
34455
- useEffect28(() => {
34703
+ useEffect29(() => {
34456
34704
  if (!scopeRun || scopeFlow?.status !== "confirmed" || generationInput) return;
34457
34705
  const { extraction: produced, input, generatePromptPath } = scopeRun;
34458
34706
  setGenerationInput({
@@ -34470,7 +34718,7 @@ function App({ importOptions, inkClear }) {
34470
34718
  ...generatePromptPath ? { generatePromptPath } : {}
34471
34719
  });
34472
34720
  }, [scopeFlow, scopeRun, generationInput]);
34473
- useEffect28(() => {
34721
+ useEffect29(() => {
34474
34722
  if (!scopeRun || generation?.status !== "done" || !generation.result.ok) return;
34475
34723
  const { credentialsSkipped, extraction: produced, input, generatePromptPath } = scopeRun;
34476
34724
  inkClear();