@contentful/experience-design-system-cli 2.39.1 → 2.40.1-dev-build-08f065f9.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;
@@ -20019,15 +20366,49 @@ var init_controls4 = __esm({
20019
20366
  });
20020
20367
 
20021
20368
  // packages/experience-design-system-cli/src/tui/import/steps/01-welcome/screen.tsx
20022
- import { useState as useState5 } from "react";
20369
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState5 } from "react";
20023
20370
  import { Box as Box10, Text as Text10 } from "ink";
20024
20371
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
20025
- function WelcomeScreen({ onContinue, onQuit, initialPath = "" }) {
20372
+ function WelcomeScreen({ mode, onContinue, onQuit, initialPath = "" }) {
20026
20373
  const [projectPath, setProjectPath] = useState5(initialPath);
20027
- useWelcomeControls(onQuit);
20374
+ const visit = useRef3({ emptySubmits: 0 }).current;
20375
+ useEffect5(() => {
20376
+ startDebugRun({
20377
+ flow: "import/welcome",
20378
+ step: "01-welcome",
20379
+ menuOption: "Welcome",
20380
+ inputs: { import_mode: mode, initial_path: initialPath }
20381
+ });
20382
+ }, []);
20383
+ const quit = () => {
20384
+ void finishDebugRun({
20385
+ outputs: {
20386
+ project_path: null,
20387
+ typed_something: toProjectPath(projectPath) !== null,
20388
+ empty_submits: visit.emptySubmits
20389
+ },
20390
+ status: "success",
20391
+ exitMethod: "discarded"
20392
+ });
20393
+ onQuit();
20394
+ };
20395
+ useWelcomeControls(quit);
20028
20396
  const submit = (value) => {
20029
20397
  const path5 = toProjectPath(value);
20030
- if (path5) onContinue(path5);
20398
+ if (!path5) {
20399
+ visit.emptySubmits += 1;
20400
+ return;
20401
+ }
20402
+ void finishDebugRun({
20403
+ outputs: {
20404
+ project_path: path5,
20405
+ changed_from_initial: path5 !== initialPath.trim(),
20406
+ empty_submits: visit.emptySubmits
20407
+ },
20408
+ status: "success",
20409
+ exitMethod: "saved"
20410
+ });
20411
+ onContinue(path5);
20031
20412
  };
20032
20413
  return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
20033
20414
  /* @__PURE__ */ jsx11(StepHeader, { step: "setup" }),
@@ -20048,6 +20429,7 @@ var init_screen2 = __esm({
20048
20429
  "packages/experience-design-system-cli/src/tui/import/steps/01-welcome/screen.tsx"() {
20049
20430
  "use strict";
20050
20431
  init_theme();
20432
+ init_debug_store();
20051
20433
  init_ClearableTextInput();
20052
20434
  init_controls4();
20053
20435
  init_logic5();
@@ -20092,7 +20474,7 @@ var init_controls5 = __esm({
20092
20474
 
20093
20475
  // packages/experience-design-system-cli/src/tui/import/resolve-user-path.ts
20094
20476
  import { homedir as homedir3 } from "node:os";
20095
- import { resolve as resolve20 } from "node:path";
20477
+ import { resolve as resolve21 } from "node:path";
20096
20478
  function stripQuotes(value) {
20097
20479
  const quoted = value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'");
20098
20480
  return value.length >= 2 && quoted ? value.slice(1, -1) : value;
@@ -20101,7 +20483,7 @@ function expandHome(value) {
20101
20483
  return value === "~" || value.startsWith("~/") || value.startsWith("~\\") ? homedir3() + value.slice(1) : value;
20102
20484
  }
20103
20485
  function resolveUserPath(raw) {
20104
- return resolve20(expandHome(stripQuotes(raw.trim())));
20486
+ return resolve21(expandHome(stripQuotes(raw.trim())));
20105
20487
  }
20106
20488
  var init_resolve_user_path = __esm({
20107
20489
  "packages/experience-design-system-cli/src/tui/import/resolve-user-path.ts"() {
@@ -20333,7 +20715,7 @@ var init_scan_files = __esm({
20333
20715
  });
20334
20716
 
20335
20717
  // 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";
20718
+ import { useEffect as useEffect6, useMemo, useState as useState7 } from "react";
20337
20719
  import { Box as Box12, Text as Text12 } from "ink";
20338
20720
  import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
20339
20721
  function Row({ row }) {
@@ -20389,7 +20771,7 @@ function PathValidationScreen({
20389
20771
  }) {
20390
20772
  const resolvedPath = useMemo(() => resolveUserPath(projectPath), [projectPath]);
20391
20773
  const [scan, setScan] = useState7();
20392
- useEffect4(() => {
20774
+ useEffect6(() => {
20393
20775
  setScan(scanFiles(resolvedPath));
20394
20776
  }, [resolvedPath]);
20395
20777
  const phase = phaseOf(scan);
@@ -20686,7 +21068,7 @@ var init_validate4 = __esm({
20686
21068
  });
20687
21069
 
20688
21070
  // packages/experience-design-system-cli/src/tui/import/steps/04-credentials/screen.tsx
20689
- import { useRef as useRef2, useState as useState9 } from "react";
21071
+ import { useRef as useRef4, useState as useState9 } from "react";
20690
21072
  import { Box as Box14, Text as Text14, useInput as useInput11 } from "ink";
20691
21073
  import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
20692
21074
  function CredentialsScreen({
@@ -20696,7 +21078,7 @@ function CredentialsScreen({
20696
21078
  validate: validate3 = validateCredentials,
20697
21079
  tokensOnly = false
20698
21080
  }) {
20699
- const initialValues = useRef2(
21081
+ const initialValues = useRef4(
20700
21082
  normalize({
20701
21083
  spaceId: initial.spaceId ?? "",
20702
21084
  environmentId: initial.environmentId || "master",
@@ -21144,16 +21526,16 @@ var init_steps = __esm({
21144
21526
  });
21145
21527
 
21146
21528
  // 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";
21529
+ import { existsSync as existsSync10 } from "node:fs";
21530
+ import { dirname as dirname16, join as join20, resolve as resolve22 } from "node:path";
21149
21531
  import { fileURLToPath as fileURLToPath4 } from "node:url";
21150
21532
  function findSkillPath(fileName) {
21151
- const thisDir = dirname15(fileURLToPath4(import.meta.url));
21533
+ const thisDir = dirname16(fileURLToPath4(import.meta.url));
21152
21534
  let dir = thisDir;
21153
21535
  for (; ; ) {
21154
- const candidate = join18(dir, "skills", fileName);
21155
- if (existsSync9(candidate)) return candidate;
21156
- const parent = resolve21(dir, "..");
21536
+ const candidate = join20(dir, "skills", fileName);
21537
+ if (existsSync10(candidate)) return candidate;
21538
+ const parent = resolve22(dir, "..");
21157
21539
  if (parent === dir) {
21158
21540
  throw new Error(`skill file not found (looked for skills/${fileName} from ${thisDir})`);
21159
21541
  }
@@ -21167,9 +21549,9 @@ var init_find_skill_path = __esm({
21167
21549
  });
21168
21550
 
21169
21551
  // packages/experience-design-system-cli/src/tui/import/steps/06-extraction/selection.ts
21170
- import { resolve as resolve22 } from "node:path";
21552
+ import { resolve as resolve23 } from "node:path";
21171
21553
  async function hashSelectPrompt(options) {
21172
- const promptHash = options.promptText ? hashContent(options.promptText) : options.promptPath ? await hashFile(resolve22(options.promptPath)) : await hashFile(SELECT_SKILL_PATH);
21554
+ const promptHash = options.promptText ? hashContent(options.promptText) : options.promptPath ? await hashFile(resolve23(options.promptPath)) : await hashFile(SELECT_SKILL_PATH);
21173
21555
  return hashContent(`${promptHash}|${options.agent}|${options.model ?? ""}`);
21174
21556
  }
21175
21557
  async function runSelection(options) {
@@ -21263,10 +21645,10 @@ var init_selection2 = __esm({
21263
21645
  });
21264
21646
 
21265
21647
  // 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";
21648
+ import { mkdir as mkdir8 } from "node:fs/promises";
21649
+ import { join as join21, resolve as resolve24 } from "node:path";
21268
21650
  async function runExtraction(input, onProgress, deps = realDeps) {
21269
- const outDir = input.outDir ?? join19(resolve23(input.projectPath), ".contentful");
21651
+ const outDir = input.outDir ?? join21(resolve24(input.projectPath), ".contentful");
21270
21652
  let progress = initialProgress({
21271
21653
  hasCredentials: input.credentials !== null,
21272
21654
  hasRawTokens: input.rawTokensPath !== ""
@@ -21370,7 +21752,7 @@ async function runExtraction(input, onProgress, deps = realDeps) {
21370
21752
  }
21371
21753
  async function fetchEntities(credentials, outDir) {
21372
21754
  try {
21373
- await mkdir7(outDir, { recursive: true });
21755
+ await mkdir8(outDir, { recursive: true });
21374
21756
  const result = await fetchAndPersistExistingContentfulEntities({
21375
21757
  spaceId: credentials.spaceId,
21376
21758
  environmentId: credentials.environmentId,
@@ -21390,7 +21772,7 @@ async function resolveSelectionPrompt(overrides) {
21390
21772
  if (select?.kind === "text") return { promptText: select.value };
21391
21773
  if (select?.kind === "path") {
21392
21774
  await resolvePromptOverride(select);
21393
- return { promptPath: resolve23(select.value) };
21775
+ return { promptPath: resolve24(select.value) };
21394
21776
  }
21395
21777
  return {};
21396
21778
  }
@@ -21416,11 +21798,11 @@ var init_run = __esm({
21416
21798
  });
21417
21799
 
21418
21800
  // 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";
21801
+ import { useEffect as useEffect7, useRef as useRef5, useState as useState11 } from "react";
21420
21802
  function useExtraction(input) {
21421
21803
  const [state2, setState] = useState11();
21422
- const current = useRef3();
21423
- useEffect5(() => {
21804
+ const current = useRef5();
21805
+ useEffect7(() => {
21424
21806
  current.current = input;
21425
21807
  if (!input) {
21426
21808
  setState(void 0);
@@ -21491,11 +21873,11 @@ var init_run_tokens_only = __esm({
21491
21873
  });
21492
21874
 
21493
21875
  // 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";
21876
+ import { useEffect as useEffect8, useRef as useRef6, useState as useState12 } from "react";
21495
21877
  function useTokensOnly(input) {
21496
21878
  const [state2, setState] = useState12();
21497
- const current = useRef4();
21498
- useEffect6(() => {
21879
+ const current = useRef6();
21880
+ useEffect8(() => {
21499
21881
  current.current = input;
21500
21882
  if (!input) {
21501
21883
  setState(void 0);
@@ -21792,6 +22174,7 @@ function GotoBanner({
21792
22174
  borderStyle: "single",
21793
22175
  borderColor: PALETTE.accent,
21794
22176
  paddingX: 1,
22177
+ paddingBottom: 1,
21795
22178
  marginTop: width === void 0 ? 1 : 0,
21796
22179
  width,
21797
22180
  flexShrink: 0,
@@ -21803,7 +22186,7 @@ function GotoBanner({
21803
22186
  return renderRow(row, i, i === highlightIndex);
21804
22187
  }),
21805
22188
  moreBelow > 0 && /* @__PURE__ */ jsx24(Text21, { color: PALETTE.muted, children: ` \u25BC ${moreBelow} more` }),
21806
- footerHint !== void 0 && /* @__PURE__ */ jsx24(Text21, { color: PALETTE.muted, children: footerHint })
22189
+ footerHint !== void 0 && /* @__PURE__ */ jsx24(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx24(Text21, { color: PALETTE.muted, children: footerHint }) })
21807
22190
  ]
21808
22191
  }
21809
22192
  );
@@ -22318,11 +22701,11 @@ function buildVisibleRows(props) {
22318
22701
  if (!expanded) continue;
22319
22702
  const subtree = computeCycleMemberSubtree(key);
22320
22703
  const isLastAtDepthSubtree = (idx) => {
22321
- const own = subtree[idx].depth;
22704
+ const own2 = subtree[idx].depth;
22322
22705
  for (let j = idx + 1; j < subtree.length; j++) {
22323
22706
  const d = subtree[j].depth;
22324
- if (d < own) return true;
22325
- if (d === own) return false;
22707
+ if (d < own2) return true;
22708
+ if (d === own2) return false;
22326
22709
  }
22327
22710
  return true;
22328
22711
  };
@@ -22442,11 +22825,11 @@ function buildVisibleRows(props) {
22442
22825
  injectedChildren.push({ name: c2.name, depth: c2.depth, isCycleChild: false });
22443
22826
  }
22444
22827
  const isLastAtDepth = (idx) => {
22445
- const own = injectedChildren[idx].depth;
22828
+ const own2 = injectedChildren[idx].depth;
22446
22829
  for (let j = idx + 1; j < injectedChildren.length; j++) {
22447
22830
  const d = injectedChildren[j].depth;
22448
- if (d < own) return true;
22449
- if (d === own) return false;
22831
+ if (d < own2) return true;
22832
+ if (d === own2) return false;
22450
22833
  }
22451
22834
  return true;
22452
22835
  };
@@ -22691,7 +23074,7 @@ var init_GroupedSidebar = __esm({
22691
23074
  });
22692
23075
 
22693
23076
  // packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/sidebar/hooks/useImmediateInput.ts
22694
- import { useLayoutEffect, useRef as useRef5 } from "react";
23077
+ import { useLayoutEffect, useRef as useRef7 } from "react";
22695
23078
  import { useStdin } from "ink";
22696
23079
  function parseInput(data) {
22697
23080
  const isShiftTab = data === "\x1B[Z";
@@ -22753,7 +23136,7 @@ function registerInputHandler(stdin, setRawMode, handler) {
22753
23136
  }
22754
23137
  function useImmediateInput(handler, enabled = true) {
22755
23138
  const { stdin, setRawMode } = useStdin();
22756
- const handlerRef = useRef5(handler);
23139
+ const handlerRef = useRef7(handler);
22757
23140
  handlerRef.current = handler;
22758
23141
  useLayoutEffect(() => {
22759
23142
  if (!enabled) return;
@@ -22771,7 +23154,7 @@ var init_useImmediateInput = __esm({
22771
23154
 
22772
23155
  // packages/experience-design-system-cli/src/tui/import/steps/07-scope-gate/sidebar/hooks/use-terminal-size.ts
22773
23156
  import { useStdout as useStdout2 } from "ink";
22774
- import { useEffect as useEffect7, useState as useState13 } from "react";
23157
+ import { useEffect as useEffect9, useState as useState13 } from "react";
22775
23158
  function readTerminalSize(stdout) {
22776
23159
  return {
22777
23160
  columns: stdout?.columns ?? 80,
@@ -22781,7 +23164,7 @@ function readTerminalSize(stdout) {
22781
23164
  function useTerminalSize() {
22782
23165
  const { stdout } = useStdout2();
22783
23166
  const [size, setSize] = useState13(() => readTerminalSize(stdout));
22784
- useEffect7(() => {
23167
+ useEffect9(() => {
22785
23168
  const updateSize = () => setSize(readTerminalSize(stdout));
22786
23169
  stdout?.on("resize", updateSize);
22787
23170
  updateSize();
@@ -23783,7 +24166,7 @@ var init_state = __esm({
23783
24166
  });
23784
24167
 
23785
24168
  // 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";
24169
+ import { useEffect as useEffect10, useMemo as useMemo3, useRef as useRef8, useState as useState16 } from "react";
23787
24170
  function useScopeGate({ components, tokenCount, tokensWidth, onConfirm, onQuit }) {
23788
24171
  const { columns: totalWidth, rows: terminalRows } = useTerminalSize();
23789
24172
  const columnPlan = useMemo3(
@@ -23829,8 +24212,8 @@ function useScopeGate({ components, tokenCount, tokensWidth, onConfirm, onQuit }
23829
24212
  for (const p of cycleParticipants) seed.add(p);
23830
24213
  return seed;
23831
24214
  });
23832
- const seededGroupsRef = useRef6(closures.size > 0 || cycleParticipants.size > 0);
23833
- useEffect8(() => {
24215
+ const seededGroupsRef = useRef8(closures.size > 0 || cycleParticipants.size > 0);
24216
+ useEffect10(() => {
23834
24217
  if (seededGroupsRef.current) return;
23835
24218
  if (closures.size === 0 && cycleParticipants.size === 0) return;
23836
24219
  seededGroupsRef.current = true;
@@ -23921,7 +24304,7 @@ function useScopeGate({ components, tokenCount, tokensWidth, onConfirm, onQuit }
23921
24304
  entryCount: 0
23922
24305
  });
23923
24306
  const panelLayout = useMemo3(() => computePanelLayout(visibleCount), [visibleCount]);
23924
- useEffect8(() => {
24307
+ useEffect10(() => {
23925
24308
  setNav((prev) => {
23926
24309
  const maxIdx = Math.max(0, visibleRows.length - 1);
23927
24310
  const nextCursor = Math.min(prev.cursor, maxIdx);
@@ -24494,13 +24877,13 @@ var init_load2 = __esm({
24494
24877
  });
24495
24878
 
24496
24879
  // 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";
24880
+ import { useEffect as useEffect11, useMemo as useMemo5, useRef as useRef9, useState as useState17 } from "react";
24498
24881
  function useScopeGateFlow(input, deps = realDeps3) {
24499
24882
  const sessionId = input?.sessionId;
24500
24883
  const loaded = useMemo5(() => sessionId ? deps.load(sessionId) : void 0, [sessionId]);
24501
- const cacheCheck = useRef7(void 0);
24884
+ const cacheCheck = useRef9(void 0);
24502
24885
  const [confirmed, setConfirmed] = useState17();
24503
- useEffect9(() => {
24886
+ useEffect11(() => {
24504
24887
  if (!input || !loaded?.ok || cacheCheck.current) return;
24505
24888
  cacheCheck.current = deps.checkCache(input.cache);
24506
24889
  }, [input, loaded]);
@@ -24535,7 +24918,7 @@ function formatAcceptanceSummary(counts) {
24535
24918
  function describeGeneration(input) {
24536
24919
  const agent = capitalize(input.agent);
24537
24920
  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." : ""}`;
24921
+ ${agent} is reading each selected component and writing a Contentful component definition for it.${input.hasTokens ? "\nUsing your design tokens for prop resolution." : ""}`;
24539
24922
  }
24540
24923
  function progressRows2(progress, agent) {
24541
24924
  const name = capitalize(agent);
@@ -24638,7 +25021,7 @@ var init_save_decisions = __esm({
24638
25021
  });
24639
25022
 
24640
25023
  // packages/experience-design-system-cli/src/tui/import/steps/08-generation/run.ts
24641
- import { resolve as resolve24 } from "node:path";
25024
+ import { resolve as resolve25 } from "node:path";
24642
25025
  async function runGeneration(input, onProgress, deps = realDeps4) {
24643
25026
  let progress = { components: null, mapTokens: "idle", mapTokensEligible: null };
24644
25027
  const update = (change) => {
@@ -24724,7 +25107,7 @@ async function buildMapTokensOptions(input) {
24724
25107
  if (mapOverride?.kind === "text") prompt.skillContentOverride = mapOverride.value;
24725
25108
  if (mapOverride?.kind === "path") {
24726
25109
  await resolvePromptOverride(mapOverride);
24727
- prompt.skillPathOverride = resolve24(mapOverride.value);
25110
+ prompt.skillPathOverride = resolve25(mapOverride.value);
24728
25111
  }
24729
25112
  if (input.existingEntitiesPath) {
24730
25113
  const entities = await readExistingContentfulEntitiesFromSession({ path: input.existingEntitiesPath });
@@ -24771,11 +25154,11 @@ var init_run2 = __esm({
24771
25154
  });
24772
25155
 
24773
25156
  // 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";
25157
+ import { useEffect as useEffect12, useRef as useRef10, useState as useState18 } from "react";
24775
25158
  function useGeneration(input) {
24776
25159
  const [state2, setState] = useState18();
24777
- const current = useRef8();
24778
- useEffect10(() => {
25160
+ const current = useRef10();
25161
+ useEffect12(() => {
24779
25162
  current.current = input;
24780
25163
  if (!input) {
24781
25164
  setState(void 0);
@@ -24802,7 +25185,7 @@ var init_use_generation = __esm({
24802
25185
  });
24803
25186
 
24804
25187
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/build-flow-start.ts
24805
- import { join as join20 } from "node:path";
25188
+ import { join as join22 } from "node:path";
24806
25189
  function buildFlowStart(input) {
24807
25190
  const { extraction, handoff, saved, generatePromptPath, tokensOnly } = input;
24808
25191
  const credentials = extraction.credentials ?? {
@@ -24815,7 +25198,7 @@ function buildFlowStart(input) {
24815
25198
  handoff,
24816
25199
  agent: extraction.agent,
24817
25200
  projectPath: extraction.projectPath,
24818
- outDir: extraction.projectPath ? join20(extraction.projectPath, ".contentful") : "",
25201
+ outDir: extraction.projectPath ? join22(extraction.projectPath, ".contentful") : "",
24819
25202
  rawTokensPath: extraction.rawTokensPath,
24820
25203
  credentials: {
24821
25204
  spaceId: credentials.spaceId,
@@ -25203,12 +25586,141 @@ var init_dtcg2 = __esm({
25203
25586
  }
25204
25587
  });
25205
25588
 
25589
+ // packages/experience-design-system-types/dist/src/property-contract/json-value.js
25590
+ var nodeTypes;
25591
+ var init_json_value = __esm({
25592
+ "packages/experience-design-system-types/dist/src/property-contract/json-value.js"() {
25593
+ "use strict";
25594
+ nodeTypes = globalThis.process?.getBuiltinModule?.("node:util")?.types;
25595
+ }
25596
+ });
25597
+
25598
+ // packages/experience-design-system-types/dist/src/property-contract/date-time-value.js
25599
+ import * as z5 from "zod/mini";
25600
+ var dateTimeValueSchema;
25601
+ var init_date_time_value = __esm({
25602
+ "packages/experience-design-system-types/dist/src/property-contract/date-time-value.js"() {
25603
+ "use strict";
25604
+ dateTimeValueSchema = z5.iso.datetime({ offset: true });
25605
+ }
25606
+ });
25607
+
25608
+ // packages/experience-design-system-types/dist/src/property-contract/discriminated-union.js
25609
+ var init_discriminated_union = __esm({
25610
+ "packages/experience-design-system-types/dist/src/property-contract/discriminated-union.js"() {
25611
+ "use strict";
25612
+ init_json_value();
25613
+ }
25614
+ });
25615
+
25616
+ // packages/experience-design-system-types/dist/src/property-contract/target-definition.js
25617
+ var TARGET_PROPERTY_TYPES, SAME_SPACE_RESOURCE_SOURCE, targetDefinitionJsonSchema;
25618
+ var init_target_definition = __esm({
25619
+ "packages/experience-design-system-types/dist/src/property-contract/target-definition.js"() {
25620
+ "use strict";
25621
+ init_json_value();
25622
+ init_date_time_value();
25623
+ init_discriminated_union();
25624
+ TARGET_PROPERTY_TYPES = [
25625
+ "String",
25626
+ "Number",
25627
+ "Boolean",
25628
+ "Array",
25629
+ "Record",
25630
+ "Integer",
25631
+ "DateTime",
25632
+ "RichText",
25633
+ "JSON",
25634
+ "ResourceLink",
25635
+ "Literal",
25636
+ "DiscriminatedUnion"
25637
+ ];
25638
+ SAME_SPACE_RESOURCE_SOURCE = "crn:contentful:::content:spaces/$self/environments/$self";
25639
+ targetDefinitionJsonSchema = {
25640
+ $schema: "http://json-schema.org/draft-07/schema#",
25641
+ $ref: "#/definitions/target",
25642
+ definitions: {
25643
+ target: {
25644
+ type: "object",
25645
+ required: ["type"],
25646
+ properties: {
25647
+ type: { enum: [...TARGET_PROPERTY_TYPES] },
25648
+ id: { type: "string" },
25649
+ name: { type: "string" },
25650
+ description: { type: "string" },
25651
+ required: { type: "boolean" },
25652
+ values: { type: "array", minItems: 1 },
25653
+ defaultValue: {},
25654
+ items: { $ref: "#/definitions/target" },
25655
+ fields: { type: "object", minProperties: 1, additionalProperties: { $ref: "#/definitions/target" } },
25656
+ allowedResources: {
25657
+ type: "array",
25658
+ minItems: 1,
25659
+ items: {
25660
+ type: "object",
25661
+ required: ["type", "source"],
25662
+ properties: {
25663
+ type: { enum: ["Contentful:Entry", "Contentful:Asset"] },
25664
+ source: { const: SAME_SPACE_RESOURCE_SOURCE },
25665
+ allowedTypes: { type: "array", items: { type: "string" } }
25666
+ },
25667
+ allOf: [
25668
+ {
25669
+ if: { properties: { type: { const: "Contentful:Entry" } } },
25670
+ then: { required: ["allowedTypes"] },
25671
+ else: { not: { required: ["allowedTypes"] } }
25672
+ }
25673
+ ],
25674
+ additionalProperties: false
25675
+ }
25676
+ },
25677
+ value: {},
25678
+ valueType: { $ref: "#/definitions/target" },
25679
+ discriminator: { type: "string", minLength: 1 },
25680
+ members: {
25681
+ type: "array",
25682
+ minItems: 2,
25683
+ uniqueItems: true,
25684
+ items: {
25685
+ allOf: [
25686
+ { $ref: "#/definitions/target" },
25687
+ { type: "object", properties: { type: { const: "Record" } }, required: ["type", "fields"] }
25688
+ ]
25689
+ }
25690
+ }
25691
+ },
25692
+ allOf: [
25693
+ {
25694
+ if: { properties: { type: { const: "DiscriminatedUnion" } } },
25695
+ then: { required: ["discriminator", "members"] },
25696
+ else: { not: { anyOf: [{ required: ["discriminator"] }, { required: ["members"] }] } }
25697
+ }
25698
+ ],
25699
+ additionalProperties: false
25700
+ }
25701
+ }
25702
+ };
25703
+ }
25704
+ });
25705
+
25706
+ // packages/experience-design-system-types/dist/src/property-contract/index.js
25707
+ var init_property_contract = __esm({
25708
+ "packages/experience-design-system-types/dist/src/property-contract/index.js"() {
25709
+ "use strict";
25710
+ init_json_value();
25711
+ init_date_time_value();
25712
+ init_discriminated_union();
25713
+ init_target_definition();
25714
+ }
25715
+ });
25716
+
25206
25717
  // packages/experience-design-system-types/dist/src/index.js
25207
25718
  var init_src4 = __esm({
25208
25719
  "packages/experience-design-system-types/dist/src/index.js"() {
25209
25720
  "use strict";
25210
25721
  init_cdf2();
25211
25722
  init_dtcg2();
25723
+ init_property_contract();
25212
25724
  }
25213
25725
  });
25214
25726
 
@@ -25348,14 +25860,14 @@ function computeRenderStatuses(closure, directIssues) {
25348
25860
  for (const node of closure.nodes) {
25349
25861
  nodesByName.set(node.name, { name: node.name, path: node.path });
25350
25862
  }
25351
- const own = /* @__PURE__ */ new Map();
25863
+ const own2 = /* @__PURE__ */ new Map();
25352
25864
  for (const node of closure.nodes) {
25353
25865
  const s = directIssues.get(node.name);
25354
- if (s && s !== "ok") own.set(node.name, s);
25866
+ if (s && s !== "ok") own2.set(node.name, s);
25355
25867
  }
25356
25868
  const inheritedSources = /* @__PURE__ */ new Map();
25357
25869
  for (const node of closure.nodes) {
25358
- const ownStatus = own.get(node.name);
25870
+ const ownStatus = own2.get(node.name);
25359
25871
  if (!ownStatus) continue;
25360
25872
  for (let i = 0; i < node.path.length - 1; i++) {
25361
25873
  const ancestor = node.path[i];
@@ -25363,7 +25875,7 @@ function computeRenderStatuses(closure, directIssues) {
25363
25875
  inheritedSources.get(ancestor).set(node.name, ownStatus);
25364
25876
  }
25365
25877
  }
25366
- for (const [name, status] of own.entries()) {
25878
+ for (const [name, status] of own2.entries()) {
25367
25879
  out.set(name, {
25368
25880
  status,
25369
25881
  isOwn: true,
@@ -25633,7 +26145,7 @@ var init_scroll_offset = __esm({
25633
26145
  });
25634
26146
 
25635
26147
  // 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";
26148
+ import React13, { useEffect as useEffect13, useLayoutEffect as useLayoutEffect2, useState as useState19 } from "react";
25637
26149
  import figures6 from "figures";
25638
26150
  import { Box as Box26, measureElement, Text as Text27 } from "ink";
25639
26151
  import { Fragment as Fragment6, jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
@@ -26400,6 +26912,7 @@ function FieldEditor({
26400
26912
  const [rationaleOpen, setRationaleOpen] = useState19(false);
26401
26913
  const [rationaleScrollOffset, setRationaleScrollOffset] = useState19(0);
26402
26914
  const [showHelp, setShowHelp] = useState19(false);
26915
+ const [pendingDelete, setPendingDelete] = useState19(null);
26403
26916
  const props = editorState.props;
26404
26917
  const slots = editorState.slots;
26405
26918
  const contentPropIndexes = React13.useMemo(
@@ -26491,6 +27004,46 @@ function FieldEditor({
26491
27004
  setEditorState(next);
26492
27005
  onChange(serializeState(next, value));
26493
27006
  };
27007
+ const deleteRow = (row) => {
27008
+ if (row.kind === "prop") {
27009
+ const nextProps = removeAt(editorState.props, row.idx);
27010
+ commit({ ...editorState, props: nextProps });
27011
+ setPropIdx(Math.max(0, Math.min(row.idx, nextProps.length - 1)));
27012
+ const rowsAfter2 = selectableRows.filter((r) => !(r.kind === "prop" && r.idx === row.idx));
27013
+ const neighbour2 = rowsAfter2[Math.max(0, selectableRows.findIndex((r) => r.kind === row.kind && r.idx === row.idx) - 1)];
27014
+ if (!neighbour2) {
27015
+ setInSlots(false);
27016
+ setInComponentDesc(true);
27017
+ setFocusLevel("componentDescription");
27018
+ } else if (neighbour2.kind === "prop") {
27019
+ setInSlots(false);
27020
+ setPropIdx(neighbour2.idx > row.idx ? neighbour2.idx - 1 : neighbour2.idx);
27021
+ setFocusLevel("prop");
27022
+ } else {
27023
+ setInSlots(true);
27024
+ setSlotIdx(neighbour2.idx);
27025
+ setFocusLevel("slot");
27026
+ }
27027
+ return;
27028
+ }
27029
+ const nextSlots = removeAt(editorState.slots, row.idx);
27030
+ commit({ ...editorState, slots: nextSlots });
27031
+ const rowsAfter = selectableRows.filter((r) => !(r.kind === "slot" && r.idx === row.idx));
27032
+ const neighbour = rowsAfter[Math.max(0, selectableRows.findIndex((r) => r.kind === row.kind && r.idx === row.idx) - 1)];
27033
+ if (!neighbour) {
27034
+ setInSlots(false);
27035
+ setInComponentDesc(true);
27036
+ setFocusLevel("componentDescription");
27037
+ } else if (neighbour.kind === "slot") {
27038
+ setInSlots(true);
27039
+ setSlotIdx(neighbour.idx > row.idx ? neighbour.idx - 1 : neighbour.idx);
27040
+ setFocusLevel("slot");
27041
+ } else {
27042
+ setInSlots(false);
27043
+ setPropIdx(neighbour.idx);
27044
+ setFocusLevel("prop");
27045
+ }
27046
+ };
26494
27047
  const canonicalize = React13.useCallback((json) => {
26495
27048
  try {
26496
27049
  return JSON.stringify(JSON.parse(json));
@@ -26527,6 +27080,11 @@ function FieldEditor({
26527
27080
  onSave();
26528
27081
  return;
26529
27082
  }
27083
+ if (pendingDelete) {
27084
+ if (input === "y") deleteRow(pendingDelete);
27085
+ setPendingDelete(null);
27086
+ return;
27087
+ }
26530
27088
  const inDescriptionTextEntryForHelp = focusLevel === "field" && editingField && activeField === "description";
26531
27089
  const inStringDefaultTextEntryForHelp = focusLevel === "field" && editingField && activeField === "default" && currentProp != null && (currentProp.type === "string" || currentProp.type === "token");
26532
27090
  const inValueListTextEntry = editingValue != null;
@@ -26726,6 +27284,11 @@ function FieldEditor({
26726
27284
  }
26727
27285
  return;
26728
27286
  }
27287
+ if (input === "d" && !key.ctrl && !key.meta) {
27288
+ if (focusLevel === "prop" && currentProp) setPendingDelete({ kind: "prop", idx: propIdx });
27289
+ if (focusLevel === "slot" && currentSlot) setPendingDelete({ kind: "slot", idx: slotIdx });
27290
+ return;
27291
+ }
26729
27292
  if (focusLevel === "slot" && key.return && currentSlot) {
26730
27293
  setFocusLevel("field");
26731
27294
  setActiveField(SLOT_FIELDS[0] ?? null);
@@ -27032,6 +27595,10 @@ function FieldEditor({
27032
27595
  }
27033
27596
  const hasEmptyProperties = props.length === 0 && slots.length === 0;
27034
27597
  const modeLabel = (() => {
27598
+ if (pendingDelete) {
27599
+ const name = pendingDelete.kind === "prop" ? props[pendingDelete.idx]?.name : slots[pendingDelete.idx]?.name;
27600
+ return `Delete ${pendingDelete.kind} "${name}"? [y] delete \xB7 any other key cancels`;
27601
+ }
27035
27602
  if (editingValue) {
27036
27603
  return editingValue.mode === "add" ? "Enter to add \xB7 [Esc] to cancel" : "Enter to save edit \xB7 [Esc] to cancel";
27037
27604
  }
@@ -27053,7 +27620,7 @@ function FieldEditor({
27053
27620
  if (rationaleOpen) {
27054
27621
  return "[\u2191\u2193] scroll [i/Esc] close rationale panel";
27055
27622
  }
27056
- return `[\u2191\u2193] navigate rows [Enter] edit fields [s] source [${propRationaleKey}] prop rationale [${componentRationaleKey}] component rationale [h] help [Esc] exit panel`;
27623
+ return `[\u2191\u2193] navigate [Enter] edit fields [d] delete property [h] help [s] source [Esc] exit panel`;
27057
27624
  })();
27058
27625
  const rows = [];
27059
27626
  rows.push({ kind: "component-description" });
@@ -27105,7 +27672,7 @@ function FieldEditor({
27105
27672
  }
27106
27673
  return Math.max(start + 1, end2);
27107
27674
  };
27108
- useEffect11(() => {
27675
+ useEffect13(() => {
27109
27676
  if (focusLevel === "field" || selectedRowIdx < 0) return;
27110
27677
  setListScrollStart((previous) => {
27111
27678
  if (initialFocusTarget && !initialFocusScrollApplied.current) {
@@ -27319,6 +27886,7 @@ function FieldEditor({
27319
27886
  /* @__PURE__ */ jsx32(Text27, { bold: true, children: "Row navigation" }),
27320
27887
  /* @__PURE__ */ jsx32(Text27, { children: " \u2191/\u2193 move between rows" }),
27321
27888
  /* @__PURE__ */ jsx32(Text27, { children: " Enter edit fields on the current row" }),
27889
+ /* @__PURE__ */ jsx32(Text27, { children: " d delete the current prop or slot (asks to confirm)" }),
27322
27890
  /* @__PURE__ */ jsx32(Text27, { children: " Esc exit the panel" }),
27323
27891
  /* @__PURE__ */ jsx32(Text27, { children: " " }),
27324
27892
  /* @__PURE__ */ jsx32(Text27, { bold: true, children: "Field editing" }),
@@ -27342,7 +27910,7 @@ function FieldEditor({
27342
27910
  /* @__PURE__ */ jsx32(Text27, { color: PALETTE.muted, children: "press ? or Esc to close" })
27343
27911
  ] }),
27344
27912
  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 }) })
27913
+ /* @__PURE__ */ jsx32(Box26, { paddingTop: 1, children: /* @__PURE__ */ jsx32(Text27, { color: pendingDelete ? PALETTE.text : PALETTE.muted, children: modeLabel }) })
27346
27914
  ]
27347
27915
  }
27348
27916
  );
@@ -27735,8 +28303,8 @@ function TokenReviewPanel({
27735
28303
  borderStyle: "single",
27736
28304
  borderColor: active ? PALETTE.text : void 0,
27737
28305
  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})` }),
28306
+ /* @__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(", ")})` : ""}` }),
28307
+ /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, wrap: "truncate", children: `Tick the tokens this property may use (${rangeLabel}).` }),
27740
28308
  /* @__PURE__ */ jsx39(Text32, { children: " " }),
27741
28309
  current.paths.length === 0 && /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, children: "(no compatible tokens found for this token kind)" }),
27742
28310
  visiblePaths.map((path5, i) => {
@@ -27769,18 +28337,33 @@ function TokenReviewPanel({
27769
28337
  borderStyle: "single",
27770
28338
  borderColor: active ? PALETTE.text : void 0,
27771
28339
  children: [
27772
- /* @__PURE__ */ jsx39(Text32, { bold: true, color: active ? void 0 : PALETTE.muted, children: `TOKEN REVIEW \u2014 ${componentName}` }),
28340
+ /* @__PURE__ */ jsx39(Text32, { bold: true, color: active ? void 0 : PALETTE.muted, children: `Review mapped tokens \u2014 ${componentName}` }),
28341
+ /* @__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.` }),
28342
+ /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, wrap: "truncate", children: "Suggested tokens come from the agent. Allowed tokens are what editors can pick in Contentful." }),
28343
+ /* @__PURE__ */ jsx39(Text32, { children: " " }),
27773
28344
  suggestions.length === 0 && /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, children: "(no token suggestions for this component)" }),
27774
28345
  suggestions.map((s, i) => {
27775
28346
  const focused = i === selectedRow;
27776
28347
  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(", ")}` })
28348
+ /* @__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(", ")})` : ""}` }) }),
28349
+ /* @__PURE__ */ jsx39(
28350
+ Text32,
28351
+ {
28352
+ color: PALETTE.muted,
28353
+ children: ` suggested: ${s.suggested.length > 0 ? s.suggested.join(", ") : "none"}`
28354
+ }
28355
+ ),
28356
+ /* @__PURE__ */ jsx39(
28357
+ Text32,
28358
+ {
28359
+ color: PALETTE.muted,
28360
+ children: ` allowed: ${s.allowed.length > 0 ? s.allowed.join(", ") : "none (any token)"}`
28361
+ }
28362
+ ),
28363
+ /* @__PURE__ */ jsx39(Text32, { children: " " })
27780
28364
  ] }, s.propName);
27781
28365
  }),
27782
- /* @__PURE__ */ jsx39(Text32, { children: " " }),
27783
- /* @__PURE__ */ jsx39(Text32, { children: "[\u2191/\u2193] move [Enter] edit allowed [Esc] close" })
28366
+ /* @__PURE__ */ jsx39(Text32, { color: PALETTE.muted, children: "[\u2191/\u2193] move [Enter] edit allowed tokens [Esc] close" })
27784
28367
  ]
27785
28368
  }
27786
28369
  );
@@ -28054,15 +28637,9 @@ function ReviewFinalizeError({
28054
28637
  function ReviewPanelFooter({
28055
28638
  reviewEditor,
28056
28639
  sidebarFocused,
28057
- sidebarFooter,
28058
- livePreview,
28059
- livePreviewSpinner
28640
+ sidebarFooter
28060
28641
  }) {
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
- ] });
28642
+ 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
28643
  }
28067
28644
  function ReviewComponentTitle({
28068
28645
  selectedKey,
@@ -28096,9 +28673,7 @@ function ReviewComponentPanel({
28096
28673
  sidebarFocused,
28097
28674
  fieldEditor,
28098
28675
  saveError,
28099
- sidebarFooter,
28100
- livePreview,
28101
- livePreviewSpinner
28676
+ sidebarFooter
28102
28677
  }) {
28103
28678
  const propCount = Object.keys(selectedEntry.$properties).length;
28104
28679
  const slotCount = selectedEntry.$slots ? Object.keys(selectedEntry.$slots).length : 0;
@@ -28135,9 +28710,7 @@ function ReviewComponentPanel({
28135
28710
  {
28136
28711
  reviewEditor,
28137
28712
  sidebarFocused,
28138
- sidebarFooter,
28139
- livePreview,
28140
- livePreviewSpinner
28713
+ sidebarFooter
28141
28714
  }
28142
28715
  ) }) })
28143
28716
  ] }) });
@@ -28494,7 +29067,7 @@ var init_FinalizeDialog = __esm({
28494
29067
  });
28495
29068
 
28496
29069
  // 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";
29070
+ import { useEffect as useEffect14, useRef as useRef11, useState as useState20 } from "react";
28498
29071
  function useReviewFinalizePreview({
28499
29072
  components,
28500
29073
  ...options
@@ -28509,9 +29082,9 @@ function useFinalizePreview(opts) {
28509
29082
  const [status, setStatus] = useState20("idle");
28510
29083
  const [removed, setRemoved] = useState20([]);
28511
29084
  const [scrollOffset, setScrollOffset] = useState20(0);
28512
- const generationRef = useRef9(0);
29085
+ const generationRef = useRef11(0);
28513
29086
  const acceptedKey = [...acceptedKeys].sort().join("\0");
28514
- useEffect12(() => {
29087
+ useEffect14(() => {
28515
29088
  if (!open) {
28516
29089
  setStatus("idle");
28517
29090
  setRemoved([]);
@@ -28563,7 +29136,7 @@ var init_use_finalize_preview = __esm({
28563
29136
  });
28564
29137
 
28565
29138
  // 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";
29139
+ import { useCallback as useCallback2, useEffect as useEffect15, useRef as useRef12, useState as useState21 } from "react";
28567
29140
  function useReviewSession({
28568
29141
  loadSession,
28569
29142
  tokensPath,
@@ -28582,7 +29155,7 @@ function useReviewSession({
28582
29155
  }));
28583
29156
  return { result, catalog };
28584
29157
  }, [loadSession, tokensPath]);
28585
- useEffect13(() => {
29158
+ useEffect15(() => {
28586
29159
  let disposed = false;
28587
29160
  void (async () => {
28588
29161
  try {
@@ -28635,7 +29208,7 @@ function useReviewSession({
28635
29208
  function useReviewMetadata({ components, selectedIdx, extractSessionId }) {
28636
29209
  const [reviewMetadata, setReviewMetadata] = useState21(null);
28637
29210
  const [componentRationale, setComponentRationale] = useState21(null);
28638
- useEffect13(() => {
29211
+ useEffect15(() => {
28639
29212
  const current = components[selectedIdx];
28640
29213
  if (!current) {
28641
29214
  setReviewMetadata(null);
@@ -28665,9 +29238,9 @@ function useReviewMetadata({ components, selectedIdx, extractSessionId }) {
28665
29238
  return { reviewMetadata, componentRationale };
28666
29239
  }
28667
29240
  function useReviewHistory({ loading, components, createSnapshot, applySnapshot }) {
28668
- const historyRef = useRef10(null);
28669
- const historySeededRef = useRef10(false);
28670
- useEffect13(() => {
29241
+ const historyRef = useRef12(null);
29242
+ const historySeededRef = useRef12(false);
29243
+ useEffect15(() => {
28671
29244
  if (loading || historySeededRef.current) return;
28672
29245
  historySeededRef.current = true;
28673
29246
  historyRef.current = createHistoryStack(createSnapshot(components));
@@ -28739,7 +29312,7 @@ function collectTokenSuggestions(entry, availableTokens = []) {
28739
29312
  const compatiblePaths = new Set(compatible);
28740
29313
  const allowed = availableTokens.length > 0 ? persistedAllowed.filter((path5) => compatiblePaths.has(path5)) : persistedAllowed;
28741
29314
  const paths = availableTokens.length > 0 ? compatible : allowed;
28742
- return { propName, paths, suggested: [...allowed], allowed };
29315
+ return { propName, kinds, paths, suggested: [...allowed], allowed };
28743
29316
  });
28744
29317
  }
28745
29318
  var init_token_suggestions = __esm({
@@ -28749,7 +29322,7 @@ var init_token_suggestions = __esm({
28749
29322
  });
28750
29323
 
28751
29324
  // 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";
29325
+ import { useEffect as useEffect16, useRef as useRef13, useState as useState22 } from "react";
28753
29326
  function parseReviewEntry(draftValue) {
28754
29327
  const parsed = JSON.parse(draftValue);
28755
29328
  const keys = Object.keys(parsed);
@@ -28781,9 +29354,9 @@ function useReviewEditor({
28781
29354
  const [tokenReviewEditing, setTokenReviewEditing] = useState22(false);
28782
29355
  const [tokenReviewEditCursor, setTokenReviewEditCursor] = useState22(0);
28783
29356
  const [tokenReviewEditSelection, setTokenReviewEditSelection] = useState22(/* @__PURE__ */ new Set());
28784
- const tokenReviewSuggestedRef = useRef11(/* @__PURE__ */ new Map());
28785
- const pendingGRef = useRef11(false);
28786
- useEffect14(() => {
29357
+ const tokenReviewSuggestedRef = useRef13(/* @__PURE__ */ new Map());
29358
+ const pendingGRef = useRef13(false);
29359
+ useEffect16(() => {
28787
29360
  setTokenReviewRow(0);
28788
29361
  setTokenReviewEditing(false);
28789
29362
  setTokenReviewEditCursor(0);
@@ -29265,24 +29838,24 @@ var init_preview_annotations = __esm({
29265
29838
  });
29266
29839
 
29267
29840
  // 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";
29841
+ import { useCallback as useCallback3, useEffect as useEffect17, useRef as useRef14, useState as useState24 } from "react";
29269
29842
  function useLivePreview(opts) {
29270
29843
  const debounceMs = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
29271
29844
  const [status, setStatus] = useState24("idle");
29272
29845
  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(() => {
29846
+ const timerRef = useRef14(null);
29847
+ const generationRef = useRef14(0);
29848
+ const latestRef = useRef14(0);
29849
+ const inflightRef = useRef14(0);
29850
+ const optsRef = useRef14(opts);
29851
+ useEffect17(() => {
29279
29852
  optsRef.current = opts;
29280
29853
  });
29281
- const disabledRef = useRef12(disabled);
29282
- useEffect15(() => {
29854
+ const disabledRef = useRef14(disabled);
29855
+ useEffect17(() => {
29283
29856
  disabledRef.current = disabled;
29284
29857
  }, [disabled]);
29285
- useEffect15(() => {
29858
+ useEffect17(() => {
29286
29859
  return () => {
29287
29860
  if (timerRef.current) {
29288
29861
  clearTimeout(timerRef.current);
@@ -29363,7 +29936,7 @@ var init_use_live_preview = __esm({
29363
29936
  });
29364
29937
 
29365
29938
  // 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";
29939
+ import { useCallback as useCallback4, useEffect as useEffect18, useState as useState25 } from "react";
29367
29940
  function useReviewPreview({
29368
29941
  components,
29369
29942
  loading,
@@ -29405,13 +29978,13 @@ function useReviewPreview({
29405
29978
  deleteAllComponents
29406
29979
  });
29407
29980
  const [spinnerTick, setSpinnerTick] = useState25(0);
29408
- useEffect16(() => {
29981
+ useEffect18(() => {
29409
29982
  if (livePreviewHook.status !== "running") return;
29410
29983
  const id = setInterval(() => setSpinnerTick((tick) => tick + 1), 80);
29411
29984
  return () => clearInterval(id);
29412
29985
  }, [livePreviewHook.status]);
29413
29986
  const livePreviewSpinner = SPINNER_FRAMES[spinnerTick % SPINNER_FRAMES.length];
29414
- useEffect16(() => {
29987
+ useEffect18(() => {
29415
29988
  if (loading) return;
29416
29989
  if (!livePreview) return;
29417
29990
  if (components.length === 0) return;
@@ -29468,7 +30041,7 @@ var init_LivePreviewSummary = __esm({
29468
30041
  });
29469
30042
 
29470
30043
  // 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";
30044
+ import { useCallback as useCallback5, useEffect as useEffect19, useMemo as useMemo6, useRef as useRef15, useState as useState26 } from "react";
29472
30045
  import figures8 from "figures";
29473
30046
  import { Box as Box38, Text as Text40 } from "ink";
29474
30047
  import { Fragment as Fragment14, jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
@@ -29682,7 +30255,7 @@ function GenerateReviewStepView({
29682
30255
  setFinalizeError
29683
30256
  } = reviewSurface;
29684
30257
  const [removedBannerCollapsed, setRemovedBannerCollapsed] = useState26(false);
29685
- const removedBannerDefaultedRef = useRef13(false);
30258
+ const removedBannerDefaultedRef = useRef15(false);
29686
30259
  const [cyclePanelScroll, setCyclePanelScroll] = useState26(0);
29687
30260
  const [cyclesCursor, setCyclesCursor] = useState26(0);
29688
30261
  const cyclePanel = useOverlayPanel({
@@ -29702,7 +30275,7 @@ function GenerateReviewStepView({
29702
30275
  const [breakCursor, setBreakCursor] = useState26(0);
29703
30276
  const [breakConfirm, setBreakConfirm] = useState26(false);
29704
30277
  const [expandedGroups, setExpandedGroups] = useState26(/* @__PURE__ */ new Set());
29705
- const seededGroupsRef = useRef13(false);
30278
+ const seededGroupsRef = useRef15(false);
29706
30279
  const {
29707
30280
  searchOpen,
29708
30281
  setSearchOpen,
@@ -29726,7 +30299,7 @@ function GenerateReviewStepView({
29726
30299
  const [pendingEditorFocus, setPendingEditorFocus] = useState26(null);
29727
30300
  const [autoRejected, setAutoRejected] = useState26([]);
29728
30301
  const [undoSnapshot, setUndoSnapshot] = useState26(null);
29729
- const autoRejectFiredRef = useRef13(false);
30302
+ const autoRejectFiredRef = useRef15(false);
29730
30303
  const [showReloadDialog, setShowReloadDialog] = useState26(false);
29731
30304
  const acceptedCountForPreview = components.filter((c2) => c2.status === "accepted").length;
29732
30305
  const { previewAnnotations, removedComponents, livePreviewHook, livePreviewSpinner } = useReviewPreview({
@@ -29789,7 +30362,7 @@ function GenerateReviewStepView({
29789
30362
  [components]
29790
30363
  );
29791
30364
  const closures = useMemo6(() => computeAllClosures(componentGraph), [componentGraph]);
29792
- useEffect17(() => {
30365
+ useEffect19(() => {
29793
30366
  if (seededGroupsRef.current) return;
29794
30367
  if (closures.size === 0 && slotCycles.length === 0) return;
29795
30368
  seededGroupsRef.current = true;
@@ -29816,7 +30389,7 @@ function GenerateReviewStepView({
29816
30389
  suggestedBreak: cycle.edges.length > 0 ? suggestCycleBreakEdge2(cycle, cycleView.pushBlocking) : null
29817
30390
  }));
29818
30391
  }, [slotCycles, cycleView]);
29819
- useEffect17(() => {
30392
+ useEffect19(() => {
29820
30393
  const decision = computeAutoRejectDecision({
29821
30394
  loading,
29822
30395
  autoRejectFired: autoRejectFiredRef.current,
@@ -29859,8 +30432,8 @@ function GenerateReviewStepView({
29859
30432
  }),
29860
30433
  applySnapshot: applyHistorySnapshot
29861
30434
  });
29862
- const autoRejectPushedRef = useRef13(false);
29863
- useEffect17(() => {
30435
+ const autoRejectPushedRef = useRef15(false);
30436
+ useEffect19(() => {
29864
30437
  if (loading) return;
29865
30438
  if (!historySeededRef.current) return;
29866
30439
  if (!autoRejectFiredRef.current) return;
@@ -29944,7 +30517,7 @@ function GenerateReviewStepView({
29944
30517
  const panelHeight = leftPanelHeight + 2;
29945
30518
  const panelContentHeight = Math.max(1, panelHeight - 3);
29946
30519
  const cycleViewportHeight = Math.max(1, panelHeight - 4);
29947
- useEffect17(() => {
30520
+ useEffect19(() => {
29948
30521
  if (selectableRowPositions.length === 0) return;
29949
30522
  const cursorInRange = selectableRowPositions.includes(cursorRowIdx);
29950
30523
  const maxScroll = Math.max(0, visibleRowsMemo.length - visibleCount);
@@ -30690,9 +31263,7 @@ function GenerateReviewStepView({
30690
31263
  initialFocusTarget: pendingEditorFocus && pendingEditorFocus.componentName === selected.key ? pendingEditorFocus.target : { kind: "description" }
30691
31264
  }
30692
31265
  ),
30693
- saveError: reviewEditor.saveError,
30694
- livePreview: livePreviewHook,
30695
- livePreviewSpinner
31266
+ saveError: reviewEditor.saveError
30696
31267
  }
30697
31268
  ) : /* @__PURE__ */ jsx48(ReviewNoSelection, {})
30698
31269
  ] }),
@@ -30724,7 +31295,7 @@ function GenerateReviewStepView({
30724
31295
  hidden: dialogOpen
30725
31296
  }
30726
31297
  ),
30727
- !dialogOpen && sidebarFocused && /* @__PURE__ */ jsx48(CompactControlBar, { hasGroupRoots, searchActive: searchOpen || searchQuery.length > 0 })
31298
+ !dialogOpen && sidebarFocused && reviewEditor.panelOpen !== "token-review" && /* @__PURE__ */ jsx48(CompactControlBar, { hasGroupRoots, searchActive: searchOpen || searchQuery.length > 0 })
30728
31299
  ] });
30729
31300
  }
30730
31301
  var HELP_SECTIONS2;
@@ -30806,7 +31377,7 @@ var init_generate_review_step = __esm({
30806
31377
  { keys: "P", label: "Component rationale" },
30807
31378
  { keys: "s", label: "Source code snippet" },
30808
31379
  { keys: "J", label: "Toggle JSON" },
30809
- { keys: "t", label: "Token review" }
31380
+ { keys: "t", label: "Review mapped tokens" }
30810
31381
  ]
30811
31382
  },
30812
31383
  {
@@ -31005,7 +31576,7 @@ var init_DoneStep = __esm({
31005
31576
  });
31006
31577
 
31007
31578
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/path-utils.ts
31008
- import { resolve as resolve25 } from "node:path";
31579
+ import { resolve as resolve26 } from "node:path";
31009
31580
  import { homedir as homedir4 } from "node:os";
31010
31581
  function expandTilde(input) {
31011
31582
  if (input === "~" || input.startsWith("~/") || input.startsWith("~\\")) {
@@ -31019,7 +31590,7 @@ function normalizePath(input) {
31019
31590
  p = p.slice(1, -1);
31020
31591
  }
31021
31592
  p = expandTilde(p);
31022
- return resolve25(p);
31593
+ return resolve26(p);
31023
31594
  }
31024
31595
  var init_path_utils = __esm({
31025
31596
  "packages/experience-design-system-cli/src/tui/import/steps/10-flow/support/path-utils.ts"() {
@@ -31028,10 +31599,10 @@ var init_path_utils = __esm({
31028
31599
  });
31029
31600
 
31030
31601
  // 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";
31602
+ import { useEffect as useEffect20, useState as useState27 } from "react";
31032
31603
  function useBlinkingCursor() {
31033
31604
  const [visible, setVisible] = useState27(true);
31034
- useEffect18(() => {
31605
+ useEffect20(() => {
31035
31606
  const interval = setInterval(() => setVisible((current) => !current), 500);
31036
31607
  return () => clearInterval(interval);
31037
31608
  }, []);
@@ -31046,29 +31617,29 @@ var init_use_blinking_cursor = __esm({
31046
31617
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/screens/PathPrompt.tsx
31047
31618
  import { useState as useState28 } from "react";
31048
31619
  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";
31620
+ import { readdirSync as readdirSync4 } from "node:fs";
31621
+ import { dirname as dirname17, basename as basename6, join as join23 } from "node:path";
31051
31622
  import { jsx as jsx52, jsxs as jsxs42 } from "react/jsx-runtime";
31052
31623
  function autocomplete(partial) {
31053
31624
  if (!partial) return null;
31054
31625
  const normalized = normalizePath(partial);
31055
- const dir = dirname16(normalized);
31626
+ const dir = dirname17(normalized);
31056
31627
  const base = basename6(normalized);
31057
31628
  let entries;
31058
31629
  try {
31059
- entries = readdirSync3(dir);
31630
+ entries = readdirSync4(dir);
31060
31631
  } catch {
31061
31632
  return null;
31062
31633
  }
31063
31634
  const matches = entries.filter((e) => e.startsWith(base));
31064
31635
  if (matches.length === 0) return null;
31065
- if (matches.length === 1) return join21(dir, matches[0]);
31636
+ if (matches.length === 1) return join23(dir, matches[0]);
31066
31637
  let prefix = matches[0];
31067
31638
  for (const m of matches) {
31068
31639
  while (!m.startsWith(prefix)) prefix = prefix.slice(0, -1);
31069
31640
  if (!prefix) break;
31070
31641
  }
31071
- return prefix.length > base.length ? join21(dir, prefix) : null;
31642
+ return prefix.length > base.length ? join23(dir, prefix) : null;
31072
31643
  }
31073
31644
  function PathPrompt({
31074
31645
  defaultPath,
@@ -31168,12 +31739,12 @@ var init_produced = __esm({
31168
31739
  });
31169
31740
 
31170
31741
  // 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";
31742
+ import { useEffect as useEffect21, useState as useState29 } from "react";
31172
31743
  import { Spinner as Spinner2 } from "@inkjs/ui";
31173
31744
  import { jsx as jsx53 } from "react/jsx-runtime";
31174
31745
  function useTimedSpinner() {
31175
31746
  const [elapsedSeconds, setElapsedSeconds] = useState29(0);
31176
- useEffect19(() => {
31747
+ useEffect21(() => {
31177
31748
  const timer = setInterval(() => setElapsedSeconds((current) => current + 1), 1e3);
31178
31749
  return () => {
31179
31750
  clearInterval(timer);
@@ -31385,12 +31956,20 @@ function diffProperty(name, old, newDef) {
31385
31956
  diffs.push({ key: `${name}-new`, color: PALETTE.success, text: `+ ${name}: ${formatPropDef(newDef)}` });
31386
31957
  return diffs;
31387
31958
  }
31959
+ function formatDefault(value) {
31960
+ const sys = value?.sys;
31961
+ if (sys?.linkType === "Contentful:DesignToken" && typeof sys.urn === "string") {
31962
+ const name = sys.urn.split("/designTokens/")[1];
31963
+ if (name) return name;
31964
+ }
31965
+ return JSON.stringify(value);
31966
+ }
31388
31967
  function formatOldProp(prop) {
31389
31968
  const parts = [];
31390
31969
  if (prop.type) parts.push(prop.type);
31391
31970
  if (prop.category) parts.push(prop.category);
31392
31971
  parts.push(prop.required ? "required" : "optional");
31393
- if (prop.default !== void 0) parts.push(`default=${JSON.stringify(prop.default)}`);
31972
+ if (prop.default !== void 0) parts.push(`default=${formatDefault(prop.default)}`);
31394
31973
  return parts.join(", ");
31395
31974
  }
31396
31975
  function formatPropDef(def) {
@@ -31399,7 +31978,7 @@ function formatPropDef(def) {
31399
31978
  if (def["$category"]) parts.push(def["$category"]);
31400
31979
  if (def["$required"] === true) parts.push("required");
31401
31980
  else parts.push("optional");
31402
- if (def["$default"] !== void 0) parts.push(`default=${JSON.stringify(def["$default"])}`);
31981
+ if (def["$default"] !== void 0) parts.push(`default=${formatDefault(def["$default"])}`);
31403
31982
  return parts.join(", ");
31404
31983
  }
31405
31984
  var init_preview_diff = __esm({
@@ -31434,7 +32013,7 @@ var init_preview_confirmation_input = __esm({
31434
32013
  });
31435
32014
 
31436
32015
  // 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";
32016
+ import { useEffect as useEffect22, useState as useState30, useMemo as useMemo7 } from "react";
31438
32017
  import { Box as Box44, Text as Text46 } from "ink";
31439
32018
  import { Fragment as Fragment16, jsx as jsx55, jsxs as jsxs44 } from "react/jsx-runtime";
31440
32019
  function buildPreviewSummaryLines(preview) {
@@ -31644,7 +32223,7 @@ function WizardPreviewStep({
31644
32223
  const activeLines = diffExpanded ? diffLines : summaryLines;
31645
32224
  const maxScroll = Math.max(0, activeLines.length - viewportHeight);
31646
32225
  const boundedScrollOffset = Math.min(scrollOffset, maxScroll);
31647
- useEffect20(() => {
32226
+ useEffect22(() => {
31648
32227
  setScrollOffset((previous) => Math.min(previous, maxScroll));
31649
32228
  }, [maxScroll]);
31650
32229
  useImmediateInput((input, key) => {
@@ -32280,8 +32859,8 @@ var init_run_print_files_helpers = __esm({
32280
32859
  });
32281
32860
 
32282
32861
  // 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";
32862
+ import { writeFile as writeFile8 } from "node:fs/promises";
32863
+ import { join as join24 } from "node:path";
32285
32864
  function buildSavedCDF(components, tokens, opts = {}) {
32286
32865
  const cdf = buildCDF2(components, toCdfTokens(tokens), opts);
32287
32866
  if (!cdf) throw new Error("nothing to save \u2014 no components or tokens resolved");
@@ -32293,7 +32872,7 @@ function buildSavedCDF(components, tokens, opts = {}) {
32293
32872
  }
32294
32873
  async function runPrintFiles(input, deps, onPatch) {
32295
32874
  onPatch({ step: "printing" });
32296
- const componentsPath = join22(input.outDir, "components.json");
32875
+ const componentsPath = join24(input.outDir, "components.json");
32297
32876
  try {
32298
32877
  const db = deps.openDb();
32299
32878
  let components = [];
@@ -32377,7 +32956,7 @@ var init_save = __esm({
32377
32956
  realSaveDeps = () => ({
32378
32957
  openDb: openPipelineDb,
32379
32958
  readTokens: (path5) => readTokensFromPath2({ flag: "tokens", path: path5 }),
32380
- writeFile: (path5, contents) => writeFile7(path5, contents),
32959
+ writeFile: (path5, contents) => writeFile8(path5, contents),
32381
32960
  appendRun,
32382
32961
  buildSourceFingerprint,
32383
32962
  sleep: (ms) => new Promise((resolve29) => setTimeout(resolve29, ms)),
@@ -32400,9 +32979,9 @@ var init_types9 = __esm({
32400
32979
 
32401
32980
  // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/controller.ts
32402
32981
  import { appendFileSync, writeFileSync } from "node:fs";
32403
- import { mkdir as mkdir8 } from "node:fs/promises";
32982
+ import { mkdir as mkdir9 } from "node:fs/promises";
32404
32983
  import { tmpdir } from "node:os";
32405
- import { join as join23, resolve as resolve26 } from "node:path";
32984
+ import { join as join25, resolve as resolve27 } from "node:path";
32406
32985
  function appendWizardLog(entry) {
32407
32986
  appendFileSync(WIZARD_LOG, JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry }) + "\n");
32408
32987
  }
@@ -32425,7 +33004,7 @@ function realFlowDeps() {
32425
33004
  }
32426
33005
  },
32427
33006
  mkdir: async (path5) => {
32428
- await mkdir8(path5, { recursive: true });
33007
+ await mkdir9(path5, { recursive: true });
32429
33008
  },
32430
33009
  detectSaveConflict,
32431
33010
  buildTimestampedSubdir,
@@ -32655,367 +33234,71 @@ function createFlowController(start, deps) {
32655
33234
  },
32656
33235
  async submitPath(submitted) {
32657
33236
  await deps.mkdir(submitted);
32658
- if (resolve26(submitted) !== resolve26(state2.outDir) && await deps.detectSaveConflict(submitted)) {
33237
+ if (resolve27(submitted) !== resolve27(state2.outDir) && await deps.detectSaveConflict(submitted)) {
32659
33238
  const subdir = deps.buildTimestampedSubdir(submitted);
32660
- await deps.mkdir(subdir);
32661
- await proceedToWrite(subdir);
32662
- return;
32663
- }
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
- }
33239
+ await deps.mkdir(subdir);
33240
+ await proceedToWrite(subdir);
33241
+ return;
33242
+ }
33243
+ await proceedToWrite(submitted);
33244
+ },
33245
+ confirmPreview: (acknowledge) => void push(acknowledge),
33246
+ acknowledgeBreakingChanges: () => void push(true),
33247
+ editDefinitions: () => update({ step: "final-review" }),
33248
+ async skipValidationErrors() {
33249
+ if (state2.extractSessionId) {
33250
+ const names = namesToSkip(state2.previewValidationErrors);
33251
+ if (names.length > 0) await deps.rejectComponentsByName(state2.extractSessionId, names);
32795
33252
  }
33253
+ await preview();
32796
33254
  }
32797
- } catch {
32798
- }
32799
- return result;
33255
+ };
32800
33256
  }
32801
- var DAY_MS, SESSION_FOLDER;
32802
- var init_debug_prune = __esm({
32803
- "packages/experience-design-system-cli/src/tui/debug-prune.ts"() {
33257
+ var WIZARD_LOG;
33258
+ var init_controller = __esm({
33259
+ "packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/controller.ts"() {
32804
33260
  "use strict";
32805
- DAY_MS = 24 * 60 * 60 * 1e3;
32806
- SESSION_FOLDER = /^\d{2}-\d{2}-\d{4}-session-\d+$/;
33261
+ init_src3();
33262
+ init_slot_graph();
33263
+ init_cycle_detection();
33264
+ init_wizard_state_transitions();
33265
+ init_preview2();
33266
+ init_push();
33267
+ init_save();
33268
+ init_types9();
33269
+ WIZARD_LOG = join25(tmpdir(), "experiences-import-wizard.log");
32807
33270
  }
32808
33271
  });
32809
33272
 
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]";
33273
+ // packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/use-flow.ts
33274
+ import { useEffect as useEffect23, useRef as useRef16, useSyncExternalStore } from "react";
33275
+ function useFlow(start, options = {}) {
33276
+ const controller = useRef16();
33277
+ if (start && !controller.current) {
33278
+ try {
33279
+ startWizardLog();
33280
+ } catch {
33281
+ }
33282
+ const flowDeps = (options.deps ?? realFlowDeps)();
33283
+ controller.current = createFlowController(start, {
33284
+ ...flowDeps,
33285
+ ...options.beforeStepChange ? { beforeStepChange: options.beforeStepChange } : {}
33286
+ });
32898
33287
  }
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" });
33288
+ const current = controller.current;
33289
+ const state2 = useSyncExternalStore(
33290
+ (listener) => current?.subscribe(listener) ?? (() => void 0),
33291
+ () => current?.getState()
33292
+ );
33293
+ useEffect23(() => {
33294
+ if (!start) controller.current = void 0;
33295
+ }, [start]);
33296
+ return current && state2 ? { controller: current, state: state2 } : void 0;
32974
33297
  }
32975
- var pendingRun, sessionDir;
32976
- var init_debug_store = __esm({
32977
- "packages/experience-design-system-cli/src/tui/debug-store.ts"() {
33298
+ var init_use_flow = __esm({
33299
+ "packages/experience-design-system-cli/src/tui/import/steps/10-flow/flow/use-flow.ts"() {
32978
33300
  "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
- });
33301
+ init_controller();
33019
33302
  }
33020
33303
  });
33021
33304
 
@@ -33086,7 +33369,7 @@ var init_help_info = __esm({
33086
33369
  });
33087
33370
 
33088
33371
  // packages/experience-design-system-cli/src/tui/help/PageContainer.tsx
33089
- import { useEffect as useEffect22, useState as useState31 } from "react";
33372
+ import { useEffect as useEffect24, useState as useState31 } from "react";
33090
33373
  import { Box as Box46, Text as Text47, useInput as useInput13 } from "ink";
33091
33374
  import { Fragment as Fragment17, jsx as jsx57, jsxs as jsxs46 } from "react/jsx-runtime";
33092
33375
  function Section({ title, rows }) {
@@ -33100,7 +33383,7 @@ function Section({ title, rows }) {
33100
33383
  }
33101
33384
  function HelpScreen({ onDone }) {
33102
33385
  const [info, setInfo] = useState31();
33103
- useEffect22(() => {
33386
+ useEffect24(() => {
33104
33387
  startDebugRun({ flow: "help", step: "01-help", menuOption: "Help", inputs: {} });
33105
33388
  void readHelpInfo().then(setInfo);
33106
33389
  }, []);
@@ -33521,7 +33804,7 @@ var init_AgentStatusBadge = __esm({
33521
33804
  });
33522
33805
 
33523
33806
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentDetail.tsx
33524
- import { useEffect as useEffect23, useState as useState33 } from "react";
33807
+ import { useEffect as useEffect25, useState as useState33 } from "react";
33525
33808
  import { Box as Box48, Text as Text50 } from "ink";
33526
33809
  import { jsx as jsx60, jsxs as jsxs48 } from "react/jsx-runtime";
33527
33810
  function StatusHeading() {
@@ -33542,7 +33825,7 @@ function AgentDetail({
33542
33825
  const [testing, setTesting] = useState33(false);
33543
33826
  const [notice, setNotice] = useState33(null);
33544
33827
  const [checks, setChecks] = useState33(void 0);
33545
- useEffect23(() => {
33828
+ useEffect25(() => {
33546
33829
  let active = true;
33547
33830
  setChecks(void 0);
33548
33831
  const timer = setTimeout(() => {
@@ -33634,7 +33917,7 @@ var init_AgentDetail = __esm({
33634
33917
  });
33635
33918
 
33636
33919
  // 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";
33920
+ import { useEffect as useEffect26, useRef as useRef17, useState as useState34 } from "react";
33638
33921
  import { Box as Box49, Text as Text51, useInput as useInput15 } from "ink";
33639
33922
  import { Fragment as Fragment18, jsx as jsx61, jsxs as jsxs49 } from "react/jsx-runtime";
33640
33923
  function AgentsScreen({ onDone }) {
@@ -33645,7 +33928,7 @@ function AgentsScreen({ onDone }) {
33645
33928
  const [saving, setSaving] = useState34(false);
33646
33929
  const [loadError, setLoadError] = useState34(null);
33647
33930
  const [checks, setChecks] = useState34({});
33648
- const visit = useRef15({
33931
+ const visit = useRef17({
33649
33932
  trail: createTrail(),
33650
33933
  before: void 0,
33651
33934
  environment: void 0,
@@ -33658,7 +33941,7 @@ function AgentsScreen({ onDone }) {
33658
33941
  );
33659
33942
  }
33660
33943
  };
33661
- useEffect24(() => {
33944
+ useEffect26(() => {
33662
33945
  readAgentConfig().then((loaded) => {
33663
33946
  setConfig(loaded);
33664
33947
  inspectAll(loaded);
@@ -33884,7 +34167,7 @@ var init_project_store = __esm({
33884
34167
  });
33885
34168
 
33886
34169
  // 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";
34170
+ import { useEffect as useEffect27, useRef as useRef18, useState as useState35 } from "react";
33888
34171
  import { Box as Box50, Text as Text52, useInput as useInput16 } from "ink";
33889
34172
  import { Fragment as Fragment19, jsx as jsx62, jsxs as jsxs50 } from "react/jsx-runtime";
33890
34173
  function debugFields(config) {
@@ -33903,14 +34186,14 @@ function ProjectScreen({ onDone }) {
33903
34186
  const [saving, setSaving] = useState35(false);
33904
34187
  const [notice, setNotice] = useState35(null);
33905
34188
  const [loadError, setLoadError] = useState35(null);
33906
- const visit = useRef16({
34189
+ const visit = useRef18({
33907
34190
  trail: createTrail(),
33908
34191
  before: void 0,
33909
34192
  persisted: void 0,
33910
34193
  error: ""
33911
34194
  }).current;
33912
34195
  const scrub = (err) => scrubError(err, [config?.cmaToken ?? ""]);
33913
- useEffect25(() => {
34196
+ useEffect27(() => {
33914
34197
  readCredentials().then((cfg) => {
33915
34198
  setConfig(cfg);
33916
34199
  visit.before = snapshotOf(cfg);
@@ -34097,13 +34380,13 @@ var init_ConfigurationScreen = __esm({
34097
34380
  });
34098
34381
 
34099
34382
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/screen.tsx
34100
- import { useEffect as useEffect26, useState as useState37 } from "react";
34383
+ import { useEffect as useEffect28, useState as useState37 } from "react";
34101
34384
  import { Box as Box52, Text as Text54, useInput as useInput18 } from "ink";
34102
34385
  import { Fragment as Fragment20, jsx as jsx64, jsxs as jsxs52 } from "react/jsx-runtime";
34103
34386
  function OptInAnalyticsScreen({ onDone }) {
34104
34387
  const [loading, setLoading] = useState37(true);
34105
34388
  const [enabled, setEnabled] = useState37(true);
34106
- useEffect26(() => {
34389
+ useEffect28(() => {
34107
34390
  readAnalyticsSetting().then((setting) => {
34108
34391
  setEnabled(setting.enabled);
34109
34392
  setLoading(false);
@@ -34166,7 +34449,7 @@ var init_screen12 = __esm({
34166
34449
  });
34167
34450
 
34168
34451
  // 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";
34452
+ import { useEffect as useEffect29, useRef as useRef19, useState as useState38 } from "react";
34170
34453
  import { Box as Box53, Text as Text55, useInput as useInput19 } from "ink";
34171
34454
  import { Fragment as Fragment21, jsx as jsx65, jsxs as jsxs53 } from "react/jsx-runtime";
34172
34455
  function DebugModeScreen({ onDone }) {
@@ -34176,8 +34459,8 @@ function DebugModeScreen({ onDone }) {
34176
34459
  RETENTION_CHOICES[RETENTION_CHOICES.length - 1]
34177
34460
  );
34178
34461
  const [notice, setNotice] = useState38(null);
34179
- const visit = useRef17({ trail: createTrail(), before: void 0, error: "" }).current;
34180
- useEffect27(() => {
34462
+ const visit = useRef19({ trail: createTrail(), before: void 0, error: "" }).current;
34463
+ useEffect29(() => {
34181
34464
  readDebugModeSetting().then((loaded) => {
34182
34465
  setSetting(loaded);
34183
34466
  setPending(loaded.retentionDays);
@@ -34318,7 +34601,7 @@ var app_exports = {};
34318
34601
  __export(app_exports, {
34319
34602
  runApp: () => runApp
34320
34603
  });
34321
- import { useEffect as useEffect28, useState as useState39 } from "react";
34604
+ import { useEffect as useEffect30, useState as useState39 } from "react";
34322
34605
  import { Fragment as Fragment22, jsx as jsx66 } from "react/jsx-runtime";
34323
34606
  function App({ importOptions, inkClear }) {
34324
34607
  const [screen, setScreen] = useState39("start");
@@ -34352,7 +34635,7 @@ function App({ importOptions, inkClear }) {
34352
34635
  const [flowStart, setFlowStart] = useState39();
34353
34636
  const flow = useFlow(flowStart, { beforeStepChange: inkClear });
34354
34637
  const [importDefaults, setImportDefaults] = useState39();
34355
- useEffect28(() => {
34638
+ useEffect30(() => {
34356
34639
  if (screen !== "import") {
34357
34640
  setImportDefaults(void 0);
34358
34641
  return;
@@ -34425,7 +34708,7 @@ function App({ importOptions, inkClear }) {
34425
34708
  }
34426
34709
  });
34427
34710
  };
34428
- useEffect28(() => {
34711
+ useEffect30(() => {
34429
34712
  if (!tokensOnlyRun || tokensOnly?.status !== "done" || !tokensOnly.result.ok) return;
34430
34713
  const { credentialsSkipped, input, tokens } = tokensOnlyRun;
34431
34714
  const { tokensPath, tokenSessionId, tokenCount } = tokensOnly.result;
@@ -34447,12 +34730,12 @@ function App({ importOptions, inkClear }) {
34447
34730
  }
34448
34731
  });
34449
34732
  }, [tokensOnly, tokensOnlyRun]);
34450
- useEffect28(() => {
34733
+ useEffect30(() => {
34451
34734
  if (!extractionRun || extraction?.status !== "done" || !extraction.result.ok) return;
34452
34735
  setExtractionRun(void 0);
34453
34736
  setScopeRun({ ...extractionRun, extraction: extraction.result.extraction });
34454
34737
  }, [extraction, extractionRun]);
34455
- useEffect28(() => {
34738
+ useEffect30(() => {
34456
34739
  if (!scopeRun || scopeFlow?.status !== "confirmed" || generationInput) return;
34457
34740
  const { extraction: produced, input, generatePromptPath } = scopeRun;
34458
34741
  setGenerationInput({
@@ -34470,7 +34753,7 @@ function App({ importOptions, inkClear }) {
34470
34753
  ...generatePromptPath ? { generatePromptPath } : {}
34471
34754
  });
34472
34755
  }, [scopeFlow, scopeRun, generationInput]);
34473
- useEffect28(() => {
34756
+ useEffect30(() => {
34474
34757
  if (!scopeRun || generation?.status !== "done" || !generation.result.ok) return;
34475
34758
  const { credentialsSkipped, extraction: produced, input, generatePromptPath } = scopeRun;
34476
34759
  inkClear();
@@ -34553,6 +34836,7 @@ function App({ importOptions, inkClear }) {
34553
34836
  return /* @__PURE__ */ jsx66(
34554
34837
  WelcomeScreen,
34555
34838
  {
34839
+ mode: target,
34556
34840
  onContinue: (projectPath) => answer({ projectPath }),
34557
34841
  onQuit: () => goBackFrom("project"),
34558
34842
  initialPath: importDefaults.componentDir