@contentful/experience-design-system-cli 2.26.1-dev-build-a852362.0 → 2.26.1-dev-build-f03f014.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/dist/package.json +14 -10
  2. package/dist/src/index.js +34937 -9
  3. package/dist/src/lib/cli-path.d.ts +4 -0
  4. package/dist/src/runs/export-helpers.d.ts +1 -12
  5. package/dist/src/runs/push-helpers.d.ts +1 -6
  6. package/package.json +12 -7
  7. package/skills/generate-components.md +427 -0
  8. package/skills/generate-tokens.md +194 -0
  9. package/skills/select-components.md +224 -0
  10. package/dist/src/analytics/apply.js +0 -30
  11. package/dist/src/analytics/client.js +0 -81
  12. package/dist/src/analytics/constants.js +0 -4
  13. package/dist/src/analytics/env.js +0 -14
  14. package/dist/src/analytics/exit.js +0 -24
  15. package/dist/src/analytics/index.js +0 -9
  16. package/dist/src/analytics/normalize.js +0 -18
  17. package/dist/src/analytics/os.js +0 -13
  18. package/dist/src/analytics/session.js +0 -15
  19. package/dist/src/analytics/tracker.js +0 -126
  20. package/dist/src/analytics/types.js +0 -1
  21. package/dist/src/analyze/build-analyze-view-rows.js +0 -63
  22. package/dist/src/analyze/command.js +0 -599
  23. package/dist/src/analyze/composite-closure.js +0 -0
  24. package/dist/src/analyze/composition/agent-parser/author-prompt.js +0 -22
  25. package/dist/src/analyze/composition/agent-parser/extract-parser.js +0 -44
  26. package/dist/src/analyze/composition/agent-parser/load-prompt.js +0 -31
  27. package/dist/src/analyze/composition/agent-parser/resolve-via-parser.js +0 -88
  28. package/dist/src/analyze/composition/agent-parser/sandbox.js +0 -187
  29. package/dist/src/analyze/composition/apply-mapping.js +0 -67
  30. package/dist/src/analyze/composition/candidate-critic-agent.js +0 -39
  31. package/dist/src/analyze/composition/candidate-critic.js +0 -63
  32. package/dist/src/analyze/composition/candidate-files.js +0 -131
  33. package/dist/src/analyze/composition/composition-cache-key.js +0 -19
  34. package/dist/src/analyze/composition/interchange-schema.js +0 -61
  35. package/dist/src/analyze/composition/manifest-doc-evidence.js +0 -136
  36. package/dist/src/analyze/composition/merge-edges.js +0 -70
  37. package/dist/src/analyze/composition/parse-map-edges.js +0 -55
  38. package/dist/src/analyze/composition/resolve-mapping-cli.js +0 -29
  39. package/dist/src/analyze/composition/resolve-mapping.js +0 -111
  40. package/dist/src/analyze/cycle-detection.js +0 -234
  41. package/dist/src/analyze/cycle-view.js +0 -28
  42. package/dist/src/analyze/fuzzy-search.js +0 -55
  43. package/dist/src/analyze/issue-inheritance.js +0 -108
  44. package/dist/src/analyze/lineage.js +0 -0
  45. package/dist/src/analyze/scope-gate-cascade.js +0 -170
  46. package/dist/src/analyze/search-neighborhood.js +0 -56
  47. package/dist/src/analyze/select/command.js +0 -484
  48. package/dist/src/analyze/select/index.js +0 -5
  49. package/dist/src/analyze/select/parser.js +0 -53
  50. package/dist/src/analyze/select/persistence.js +0 -80
  51. package/dist/src/analyze/select/preview-annotations.js +0 -62
  52. package/dist/src/analyze/select/stdout.js +0 -3
  53. package/dist/src/analyze/select/tui/App.js +0 -483
  54. package/dist/src/analyze/select/tui/components/ComponentDetail.js +0 -48
  55. package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +0 -125
  56. package/dist/src/analyze/select/tui/components/FieldEditor.js +0 -1131
  57. package/dist/src/analyze/select/tui/components/FinalizeDialog.js +0 -36
  58. package/dist/src/analyze/select/tui/components/GotoBanner.js +0 -30
  59. package/dist/src/analyze/select/tui/components/GroupedSidebar.js +0 -517
  60. package/dist/src/analyze/select/tui/components/HelpOverlay.js +0 -17
  61. package/dist/src/analyze/select/tui/components/JsonEditor.js +0 -131
  62. package/dist/src/analyze/select/tui/components/JsonPanel.js +0 -66
  63. package/dist/src/analyze/select/tui/components/LineagePanel.js +0 -22
  64. package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +0 -29
  65. package/dist/src/analyze/select/tui/components/QuitDialog.js +0 -14
  66. package/dist/src/analyze/select/tui/components/RationalePanel.js +0 -80
  67. package/dist/src/analyze/select/tui/components/Sidebar.js +0 -92
  68. package/dist/src/analyze/select/tui/components/SourcePanel.js +0 -52
  69. package/dist/src/analyze/select/tui/components/StatusBar.js +0 -7
  70. package/dist/src/analyze/select/tui/components/TopBar.js +0 -10
  71. package/dist/src/analyze/select/tui/components/removed-components-text.js +0 -11
  72. package/dist/src/analyze/select/tui/hooks/scroll-offset.js +0 -26
  73. package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +0 -66
  74. package/dist/src/analyze/select/tui/hooks/useKeymap.js +0 -67
  75. package/dist/src/analyze/select/tui/hooks/useSession.js +0 -52
  76. package/dist/src/analyze/select/tui/hooks/useUndo.js +0 -26
  77. package/dist/src/analyze/select/tui/theme.js +0 -28
  78. package/dist/src/analyze/select/types.js +0 -39
  79. package/dist/src/analyze/select-agent/command.js +0 -558
  80. package/dist/src/analyze/select-agent/context-builder.js +0 -197
  81. package/dist/src/analyze/select-agent/show-rationale.js +0 -92
  82. package/dist/src/analyze/selection-cascade.js +0 -19
  83. package/dist/src/analyze/slot-graph.js +0 -17
  84. package/dist/src/analyze/tui/AnalyzeView.js +0 -64
  85. package/dist/src/apply/api-client.js +0 -440
  86. package/dist/src/apply/command.js +0 -845
  87. package/dist/src/apply/manifest.js +0 -1
  88. package/dist/src/apply/preview-utils.js +0 -26
  89. package/dist/src/apply/tui/SelectView.js +0 -34
  90. package/dist/src/apply/tui/ServerApplyView.js +0 -49
  91. package/dist/src/apply/tui/ServerPreviewView.js +0 -23
  92. package/dist/src/credentials-store.js +0 -74
  93. package/dist/src/generate/command.js +0 -597
  94. package/dist/src/generate/edit/command.js +0 -127
  95. package/dist/src/generate/tui/GenerateView.js +0 -10
  96. package/dist/src/host-utils.js +0 -26
  97. package/dist/src/import/agent-model-resolve.js +0 -35
  98. package/dist/src/import/auto-filter-resolve.js +0 -16
  99. package/dist/src/import/command.js +0 -380
  100. package/dist/src/import/cycle-auto-reject.js +0 -14
  101. package/dist/src/import/orchestrator.js +0 -603
  102. package/dist/src/import/path-utils.js +0 -30
  103. package/dist/src/import/picker-dispatch.js +0 -44
  104. package/dist/src/import/print-prompt.js +0 -31
  105. package/dist/src/import/strip-allowed-components.js +0 -22
  106. package/dist/src/import/tui/CustomPromptBanner.js +0 -8
  107. package/dist/src/import/tui/WizardApp.js +0 -1670
  108. package/dist/src/import/tui/ai-flag.js +0 -6
  109. package/dist/src/import/tui/auto-filter-error.js +0 -29
  110. package/dist/src/import/tui/autocomplete.js +0 -32
  111. package/dist/src/import/tui/components/AutoFilterBanner.js +0 -19
  112. package/dist/src/import/tui/components/CounterStrip.js +0 -12
  113. package/dist/src/import/tui/components/LegendEntry.js +0 -6
  114. package/dist/src/import/tui/cycle-panel-scroll.js +0 -33
  115. package/dist/src/import/tui/final-review-host.js +0 -25
  116. package/dist/src/import/tui/group-collapse.js +0 -11
  117. package/dist/src/import/tui/history.js +0 -53
  118. package/dist/src/import/tui/hooks/useLineage.js +0 -50
  119. package/dist/src/import/tui/hooks/useOverlayPanel.js +0 -27
  120. package/dist/src/import/tui/lineage-layout.js +0 -82
  121. package/dist/src/import/tui/merge-ai-decisions.js +0 -22
  122. package/dist/src/import/tui/push-decision-gate-helpers.js +0 -8
  123. package/dist/src/import/tui/push-progress.js +0 -1
  124. package/dist/src/import/tui/run-print-files-helpers.js +0 -8
  125. package/dist/src/import/tui/run-teaser.js +0 -9
  126. package/dist/src/import/tui/runLivePreview.js +0 -82
  127. package/dist/src/import/tui/runScopeGate.js +0 -28
  128. package/dist/src/import/tui/scope-gate-columns.js +0 -94
  129. package/dist/src/import/tui/scope-gate-host.js +0 -28
  130. package/dist/src/import/tui/sidebar-width.js +0 -3
  131. package/dist/src/import/tui/spawn-generate.js +0 -53
  132. package/dist/src/import/tui/step-filters.js +0 -42
  133. package/dist/src/import/tui/steps/AtomicGenerateReviewStep.js +0 -726
  134. package/dist/src/import/tui/steps/AtomicScopeGateStep.js +0 -210
  135. package/dist/src/import/tui/steps/CredentialsStep.js +0 -108
  136. package/dist/src/import/tui/steps/DoneStep.js +0 -24
  137. package/dist/src/import/tui/steps/ErrorStep.js +0 -16
  138. package/dist/src/import/tui/steps/GateStep.js +0 -22
  139. package/dist/src/import/tui/steps/GenerateReviewStep.js +0 -1618
  140. package/dist/src/import/tui/steps/PathValidationStep.js +0 -152
  141. package/dist/src/import/tui/steps/PreviewStep.js +0 -37
  142. package/dist/src/import/tui/steps/PreviewValidationErrorStep.js +0 -23
  143. package/dist/src/import/tui/steps/PushDecisionGateStep.js +0 -73
  144. package/dist/src/import/tui/steps/PushingStep.js +0 -24
  145. package/dist/src/import/tui/steps/RunningStep.js +0 -21
  146. package/dist/src/import/tui/steps/ScopeGateStep.js +0 -907
  147. package/dist/src/import/tui/steps/TokenInputStep.js +0 -88
  148. package/dist/src/import/tui/steps/WelcomeStep.js +0 -34
  149. package/dist/src/import/tui/steps/WizardPreviewStep.js +0 -169
  150. package/dist/src/import/tui/steps/auto-reject-decision.js +0 -9
  151. package/dist/src/import/tui/steps/breaking-change-format.js +0 -24
  152. package/dist/src/import/tui/steps/enumerate-cycle-breaks.js +0 -0
  153. package/dist/src/import/tui/steps/preview-diff.js +0 -175
  154. package/dist/src/import/tui/useFinalizePreview.js +0 -0
  155. package/dist/src/import/tui/useLivePreview.js +0 -122
  156. package/dist/src/import/tui/wizard-422-helpers.js +0 -60
  157. package/dist/src/import/tui/wizard-generate-progress.js +0 -25
  158. package/dist/src/import/tui/wizard-save-flow.js +0 -20
  159. package/dist/src/import/tui/wizard-state-transitions.js +0 -32
  160. package/dist/src/lib/agent-model-options.js +0 -15
  161. package/dist/src/lib/command-options.js +0 -30
  162. package/dist/src/lib/composition-mode.js +0 -39
  163. package/dist/src/lib/contentful-urls.js +0 -27
  164. package/dist/src/lib/debug-logger.js +0 -229
  165. package/dist/src/lib/debug-preamble.js +0 -33
  166. package/dist/src/lib/error-parser.js +0 -269
  167. package/dist/src/lib/prompt-overrides.js +0 -69
  168. package/dist/src/lib/terminal-capabilities.js +0 -51
  169. package/dist/src/lib/user-agent.js +0 -29
  170. package/dist/src/output/format.js +0 -110
  171. package/dist/src/print/command.js +0 -221
  172. package/dist/src/print/validate/tui/ValidateView.js +0 -37
  173. package/dist/src/print/validate/validators/cdf-validator.js +0 -104
  174. package/dist/src/print/validate/validators/dtcg-validator.js +0 -110
  175. package/dist/src/print/validate/validators/format-errors.js +0 -18
  176. package/dist/src/program.js +0 -105
  177. package/dist/src/runs/export-helpers.js +0 -59
  178. package/dist/src/runs/fingerprint.js +0 -75
  179. package/dist/src/runs/ls-command.js +0 -137
  180. package/dist/src/runs/modify-launcher.js +0 -42
  181. package/dist/src/runs/path-prompt.js +0 -72
  182. package/dist/src/runs/push-creds-prompt.js +0 -39
  183. package/dist/src/runs/push-helpers.js +0 -72
  184. package/dist/src/runs/push-launcher.js +0 -77
  185. package/dist/src/runs/replay-helpers.js +0 -164
  186. package/dist/src/runs/resolve-run-target.js +0 -45
  187. package/dist/src/runs/run-picker-mount.js +0 -72
  188. package/dist/src/runs/run-picker.js +0 -129
  189. package/dist/src/runs/save-conflict.js +0 -56
  190. package/dist/src/runs/save-path-resolver.js +0 -75
  191. package/dist/src/runs/staleness.js +0 -140
  192. package/dist/src/runs/store.js +0 -136
  193. package/dist/src/session/cache-keys.js +0 -44
  194. package/dist/src/session/command.js +0 -261
  195. package/dist/src/session/db.js +0 -1538
  196. package/dist/src/session/migration.js +0 -117
  197. package/dist/src/session/session-id.js +0 -212
  198. package/dist/src/session/stats.js +0 -89
  199. package/dist/src/setup/analytics-prompt.js +0 -24
  200. package/dist/src/setup/auto-filter-prompt.js +0 -24
  201. package/dist/src/setup/command.js +0 -966
  202. package/dist/src/setup/composition-mode-prompt.js +0 -37
  203. package/dist/src/setup/debug-mode-prompt.js +0 -22
  204. package/dist/src/types.js +0 -1
@@ -1,164 +0,0 @@
1
- import { resolve } from 'node:path';
2
- import { resolveRunTarget } from './resolve-run-target.js';
3
- import { updateRun } from './store.js';
4
- import { pushRunSession } from './push-helpers.js';
5
- import { readExperiencesCredentials } from '../credentials-store.js';
6
- import { launchModifyWizard } from './modify-launcher.js';
7
- import { checkRunStaleness, formatStalenessDetail } from './staleness.js';
8
- function formatStalenessRefusal(run, detail) {
9
- return [
10
- `Refusing to replay run ${run.id} — source or saved files have drifted since the run was recorded.`,
11
- ...detail,
12
- '',
13
- 'Re-extract with `experiences import --project <path>` for a fresh run, or pass --force to bypass.',
14
- ].join('\n');
15
- }
16
- const MISSING_CREDS_ERROR = "--push-from-run requires credentials (space-id / environment-id / cma-token). Pass --cma-token or run 'experiences setup' first.";
17
- /**
18
- * Push a prior run's recorded pipeline.db session directly to Contentful
19
- * without re-running extraction or generation, and without writing any
20
- * components.json / tokens.json to disk.
21
- *
22
- * Exposed as `experiences import --push-from-run <id-or-path>`.
23
- *
24
- * Credentials resolution order:
25
- * 1. Flags on the command (`--space-id`, `--environment-id`, `--cma-token`,
26
- * `--host`).
27
- * 2. The run record's `pushedTo` field (space / env / host only — the
28
- * CMA token is never persisted).
29
- * 3. `~/.config/experiences/credentials.json` (the `experiences setup`
30
- * store) and matching env vars.
31
- * 4. If still incomplete and interactive: open the wizard's existing
32
- * credentials step. If non-TTY: error.
33
- */
34
- export async function replayRun(opts) {
35
- const run = await resolveRunTarget(opts.runIdOrPath);
36
- // Staleness gate: refuse before any push side effect when source files or
37
- // saved artifacts have drifted. Bypassed by --force.
38
- if (!opts.force) {
39
- const staleness = await checkRunStaleness(run);
40
- if (staleness.stale) {
41
- throw new Error(formatStalenessRefusal(run, formatStalenessDetail(staleness)));
42
- }
43
- }
44
- const sessionId = run.generateSessionId ?? run.extractSessionId;
45
- // Layered credentials resolution.
46
- const stored = await readExperiencesCredentials();
47
- let spaceId = opts.spaceId || run.pushedTo?.spaceId || stored.spaceId || '';
48
- let environmentId = opts.environmentId || run.pushedTo?.environmentId || stored.environmentId || '';
49
- let cmaToken = opts.cmaToken || stored.cmaToken || '';
50
- let host = opts.host || run.pushedTo?.host || stored.host || '';
51
- if (!spaceId || !environmentId || !cmaToken) {
52
- if (!opts.interactive) {
53
- throw new Error(MISSING_CREDS_ERROR);
54
- }
55
- const prompt = opts.promptForCredentials ??
56
- (async (need) => {
57
- const { promptForPushCredentials } = await import('./push-creds-prompt.js');
58
- return promptForPushCredentials({
59
- ...(need.spaceId ? { initialSpaceId: need.spaceId } : {}),
60
- ...(need.environmentId ? { initialEnvironmentId: need.environmentId } : {}),
61
- ...(need.cmaToken ? { initialCmaToken: need.cmaToken } : {}),
62
- ...(need.host ? { initialHost: need.host } : {}),
63
- summary: `Push run ${run.id} → enter credentials. Defaults reflect the run record and your saved setup.`,
64
- });
65
- });
66
- const collected = await prompt({
67
- spaceId,
68
- environmentId,
69
- cmaToken,
70
- host,
71
- });
72
- spaceId = collected.spaceId;
73
- environmentId = collected.environmentId;
74
- cmaToken = collected.cmaToken;
75
- host = collected.host;
76
- if (!spaceId || !environmentId || !cmaToken) {
77
- throw new Error(MISSING_CREDS_ERROR);
78
- }
79
- }
80
- const result = await pushRunSession({
81
- sessionId,
82
- spaceId,
83
- environmentId,
84
- cmaToken,
85
- ...(host ? { host } : {}),
86
- ...(opts.allowDeletions ? { allowDeletions: true } : {}),
87
- });
88
- if (!result.ok) {
89
- throw new Error(result.error);
90
- }
91
- // Push the tokens session too when the run has one. Tokens live in their
92
- // own pipeline.db session (separate from extract/generate), so a single
93
- // `apply push --session <id>` only covers components. Without this second
94
- // push, design tokens recorded by the run never reach Contentful on replay.
95
- if (run.tokenSessionId) {
96
- const tokenResult = await pushRunSession({
97
- sessionId: run.tokenSessionId,
98
- spaceId,
99
- environmentId,
100
- cmaToken,
101
- ...(host ? { host } : {}),
102
- ...(opts.allowDeletions ? { allowDeletions: true } : {}),
103
- });
104
- if (!tokenResult.ok) {
105
- throw new Error(tokenResult.error);
106
- }
107
- }
108
- await updateRun(run.id, {
109
- pushedTo: { spaceId, environmentId, host: host || '' },
110
- });
111
- const componentLine = `Pushed ${run.componentCount} component${run.componentCount === 1 ? '' : 's'} to ${spaceId}/${environmentId}`;
112
- const tokensLine = run.tokenCount > 0 ? ` (also: ${run.tokenCount} token${run.tokenCount === 1 ? '' : 's'})` : '';
113
- process.stdout.write(`${componentLine}${tokensLine}\n`);
114
- }
115
- /**
116
- * Re-open the wizard with a prior run's extract/generate session pre-populated
117
- * so the operator can tweak fields. Exposed as
118
- * `experiences import --modify <id-or-path>`. Default entry step is
119
- * `final-review`.
120
- */
121
- export async function modifyRun(opts) {
122
- if (opts.saveAsNew && opts.overwrite) {
123
- throw new Error('--save-as-new and --overwrite are mutually exclusive.');
124
- }
125
- const run = await resolveRunTarget(opts.runIdOrPath);
126
- if (!opts.force) {
127
- const staleness = await checkRunStaleness(run);
128
- if (staleness.stale) {
129
- throw new Error(formatStalenessRefusal(run, formatStalenessDetail(staleness)));
130
- }
131
- }
132
- const saveMode = opts.overwrite ? 'overwrite' : opts.saveAsNew ? 'new' : 'prompt';
133
- // Credentials pre-fill precedence: run.pushedTo field-by-field, then
134
- // credentials.json (which itself falls back to env vars). The modify entry
135
- // step goes straight to final-review and does not surface the credentials
136
- // step, so if `pushedTo` is null (last push failed or never happened) we
137
- // must consult the on-disk store here — otherwise the wizard mounts with
138
- // built-in defaults (environmentId='master', host='api.contentful.com')
139
- // and preview fires against the wrong endpoint. CMA token is now included
140
- // in the fallback because credentials-store.ts persists it (INTEG-4410);
141
- // if it's still empty after the merge, the wizard's final-review preview
142
- // will 401 and the operator sees a real gap — out of scope for this fix.
143
- const stored = await readExperiencesCredentials();
144
- const mergedSpaceId = run.pushedTo?.spaceId || stored.spaceId || '';
145
- const mergedEnvironmentId = run.pushedTo?.environmentId || stored.environmentId || '';
146
- const mergedHost = run.pushedTo?.host || stored.host || '';
147
- const mergedCmaToken = stored.cmaToken || '';
148
- await launchModifyWizard({
149
- extractSessionId: run.extractSessionId,
150
- generateSessionId: run.generateSessionId,
151
- tokenSessionId: run.tokenSessionId,
152
- projectPath: run.projectPath,
153
- savePath: run.savePath,
154
- entryStep: 'final-review',
155
- saveMode,
156
- ...(run.compositionMode ? { compositionMode: run.compositionMode } : {}),
157
- ...(opts.outDir ? { outDirOverride: resolve(opts.outDir) } : {}),
158
- ...(mergedSpaceId ? { initialSpaceId: mergedSpaceId } : {}),
159
- ...(mergedEnvironmentId ? { initialEnvironmentId: mergedEnvironmentId } : {}),
160
- ...(mergedHost ? { initialHost: mergedHost } : {}),
161
- ...(mergedCmaToken ? { initialCmaToken: mergedCmaToken } : {}),
162
- ...(opts.allowDeletions !== undefined ? { allowDeletions: opts.allowDeletions } : {}),
163
- });
164
- }
@@ -1,45 +0,0 @@
1
- import { homedir } from 'node:os';
2
- import { resolve as resolvePath } from 'node:path';
3
- import { findAllRunsBySavePath, getRun } from './store.js';
4
- /**
5
- * Decide whether a positional CLI arg refers to a run-id or a filesystem path,
6
- * then look up the corresponding RunRecord.
7
- *
8
- * Mirrors `git checkout` accepting either a sha or a ref: anything that *looks
9
- * like* a path is treated as one; otherwise we fall back to a run-id lookup.
10
- *
11
- * Path detection: arg starts with `/`, `./`, `../`, or `~/`, OR equals `.`,
12
- * OR equals `~`.
13
- */
14
- export async function resolveRunTarget(arg) {
15
- if (looksLikePath(arg)) {
16
- const absolute = resolvePath(expandHome(arg));
17
- const matches = await findAllRunsBySavePath(absolute);
18
- if (matches.length === 0) {
19
- throw new Error(`No run recorded for path ${absolute}. Run 'experiences runs' to list known runs.`);
20
- }
21
- if (matches.length === 1) {
22
- return matches[0];
23
- }
24
- // Multi-match: newest createdAt wins. ISO UTC timestamps compare
25
- // lexicographically (see store.ts: createdAt = new Date().toISOString()).
26
- const sorted = [...matches].sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1));
27
- const newest = sorted[0];
28
- const olderCount = matches.length - 1;
29
- process.stderr.write(`Multiple runs at ${absolute}; using newest ${newest.id} (createdAt ${newest.createdAt}). ${olderCount} older candidates ignored.\n`);
30
- return newest;
31
- }
32
- return getRun(arg);
33
- }
34
- function looksLikePath(arg) {
35
- if (arg === '.' || arg === '~')
36
- return true;
37
- return arg.startsWith('/') || arg.startsWith('./') || arg.startsWith('../') || arg.startsWith('~/');
38
- }
39
- function expandHome(arg) {
40
- if (arg === '~')
41
- return homedir();
42
- if (arg.startsWith('~/'))
43
- return resolvePath(homedir(), arg.slice(2));
44
- return arg;
45
- }
@@ -1,72 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { READABLE_VERSIONS, runsFilePath } from './store.js';
3
- function migrateRecord(rec) {
4
- return {
5
- ...rec,
6
- tokensPath: rec.tokensPath ?? null,
7
- tokenSessionId: rec.tokenSessionId ?? null,
8
- };
9
- }
10
- function hasBlockingFlag(flags) {
11
- if (flags.pushFromRun !== undefined)
12
- return true;
13
- if (flags.modify !== undefined)
14
- return true;
15
- if (flags.project !== undefined)
16
- return true;
17
- if (flags.autoAcceptScope)
18
- return true;
19
- if (flags.printPrompt)
20
- return true;
21
- if (flags.dryRun)
22
- return true;
23
- return false;
24
- }
25
- /**
26
- * Pure decision helper for whether the wizard should open with the run picker.
27
- *
28
- * Picker shows iff ALL hold:
29
- * 1. `~/.config/experiences/runs.json` exists
30
- * 2. The file has >=1 run entry
31
- * 3. None of `--push-from-run`, `--modify`, `--project`,
32
- * `--auto-accept-scope`, `--print-prompt`, or `--dry-run` were passed
33
- * 4. stdin is a TTY
34
- *
35
- * On any failure (including ENOENT or a parse error), the helper returns
36
- * `{ shouldShow: false, runs: [] }`. We deliberately swallow parse errors
37
- * here: the wizard's normal entry point is more important than surfacing a
38
- * malformed runs.json — the operator can still use `experiences runs ls` to
39
- * diagnose later.
40
- */
41
- export async function shouldShowRunPicker(input) {
42
- if (!input.isTTY)
43
- return { shouldShow: false, runs: [] };
44
- if (hasBlockingFlag(input.flags))
45
- return { shouldShow: false, runs: [] };
46
- const path = input.runsJsonPath ?? runsFilePath();
47
- let raw;
48
- try {
49
- raw = await readFile(path, 'utf8');
50
- }
51
- catch {
52
- return { shouldShow: false, runs: [] };
53
- }
54
- let parsed;
55
- try {
56
- parsed = JSON.parse(raw);
57
- }
58
- catch {
59
- return { shouldShow: false, runs: [] };
60
- }
61
- // Accept any version this CLI can read (see store.ts READABLE_VERSIONS).
62
- // Pre-PR-#78 v1 files are migrated to v2 in memory below; gating on strict
63
- // equality with RUNS_FILE_VERSION would silently hide every v1 run.
64
- if (!READABLE_VERSIONS.has(parsed.version)) {
65
- return { shouldShow: false, runs: [] };
66
- }
67
- const rawRuns = Array.isArray(parsed.runs) ? parsed.runs : [];
68
- if (rawRuns.length === 0)
69
- return { shouldShow: false, runs: [] };
70
- const runs = rawRuns.map(migrateRecord);
71
- return { shouldShow: true, runs };
72
- }
@@ -1,129 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Box, Text } from 'ink';
4
- import { useImmediateInput } from '../analyze/select/tui/hooks/useImmediateInput.js';
5
- const COLLAPSED_LIMIT = 10;
6
- const COLLAPSE_THRESHOLD = 12;
7
- function formatCreatedAt(iso) {
8
- const d = new Date(iso);
9
- if (Number.isNaN(d.getTime()))
10
- return iso;
11
- const yyyy = d.getFullYear();
12
- const mm = String(d.getMonth() + 1).padStart(2, '0');
13
- const dd = String(d.getDate()).padStart(2, '0');
14
- const hh = String(d.getHours()).padStart(2, '0');
15
- const mi = String(d.getMinutes()).padStart(2, '0');
16
- return `${yyyy}-${mm}-${dd} ${hh}:${mi}`;
17
- }
18
- function runLine(run) {
19
- const date = formatCreatedAt(run.createdAt);
20
- const pushedTag = run.pushedTo ? 'pushed' : 'not pushed';
21
- const count = `${run.componentCount} component${run.componentCount === 1 ? '' : 's'}`;
22
- return `${run.id} - ${date} - ${run.projectPath} (${count}, ${pushedTag})`;
23
- }
24
- const ACTION_OPTIONS = [
25
- { key: 'push', label: 'Push', description: "Push this run's recorded session to Contentful." },
26
- { key: 'modify', label: 'Modify', description: 'Re-open the wizard at final-review with this run pre-filled.' },
27
- { key: 'cancel', label: 'Cancel', description: 'Back to the run list.' },
28
- ];
29
- function ActionScreen({ runId, onChoose, onBack, }) {
30
- const [focusIdx, setFocusIdx] = useState(0);
31
- const fire = (opt) => {
32
- if (opt === 'cancel')
33
- return onBack();
34
- onChoose(opt);
35
- };
36
- useImmediateInput((rawInput, key) => {
37
- if (key.upArrow || rawInput === 'k') {
38
- setFocusIdx((i) => (i - 1 + ACTION_OPTIONS.length) % ACTION_OPTIONS.length);
39
- return;
40
- }
41
- if (key.downArrow || rawInput === 'j') {
42
- setFocusIdx((i) => (i + 1) % ACTION_OPTIONS.length);
43
- return;
44
- }
45
- if (key.return) {
46
- fire(ACTION_OPTIONS[focusIdx].key);
47
- return;
48
- }
49
- if (key.escape || rawInput === 'q') {
50
- onBack();
51
- return;
52
- }
53
- });
54
- return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, children: "Push or modify?" }), _jsxs(Text, { dimColor: true, children: ["Run ", runId] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: ACTION_OPTIONS.map((opt, i) => {
55
- const focused = i === focusIdx;
56
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: focused ? 'cyan' : undefined, children: focused ? '>' : ' ' }), _jsx(Text, { color: focused ? 'cyan' : undefined, children: opt.label }), _jsxs(Text, { dimColor: true, children: ["- ", opt.description] })] }, opt.key));
57
- }) }), _jsxs(Box, { gap: 3, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "[j/k] Navigate" }), _jsx(Text, { dimColor: true, children: "[Enter] Select" }), _jsx(Text, { dimColor: true, children: "[Esc] Back" })] })] }));
58
- }
59
- function buildRows(runs, expanded) {
60
- const showAll = expanded || runs.length < COLLAPSE_THRESHOLD;
61
- const visible = showAll ? runs : runs.slice(0, COLLAPSED_LIMIT);
62
- const rows = visible.map((run) => ({ kind: 'run', run }));
63
- if (!showAll)
64
- rows.push({ kind: 'show-all' });
65
- rows.push({ kind: 'new' });
66
- return rows;
67
- }
68
- export function RunPicker({ runs, staleRunIds, onSelect, onCancel }) {
69
- const [expanded, setExpanded] = useState(false);
70
- const [focusIdx, setFocusIdx] = useState(0);
71
- const [actionRunId, setActionRunId] = useState(null);
72
- const rows = buildRows(runs, expanded);
73
- const clampedFocus = Math.min(focusIdx, rows.length - 1);
74
- const selectRow = (row) => {
75
- if (row.kind === 'show-all') {
76
- setExpanded(true);
77
- setFocusIdx(COLLAPSED_LIMIT);
78
- return;
79
- }
80
- if (row.kind === 'new') {
81
- onSelect({ runId: null, action: 'new' });
82
- return;
83
- }
84
- setActionRunId(row.run.id);
85
- };
86
- useImmediateInput((rawInput, key) => {
87
- if (actionRunId !== null) {
88
- return;
89
- }
90
- if (key.upArrow || rawInput === 'k') {
91
- setFocusIdx((i) => (i - 1 + rows.length) % rows.length);
92
- return;
93
- }
94
- if (key.downArrow || rawInput === 'j') {
95
- setFocusIdx((i) => (i + 1) % rows.length);
96
- return;
97
- }
98
- if (rawInput === 'n') {
99
- onSelect({ runId: null, action: 'new' });
100
- return;
101
- }
102
- if (rawInput === 'q' || key.escape) {
103
- onCancel();
104
- return;
105
- }
106
- if (key.return) {
107
- const row = rows[clampedFocus];
108
- if (row)
109
- selectRow(row);
110
- return;
111
- }
112
- });
113
- if (actionRunId !== null) {
114
- return (_jsx(ActionScreen, { runId: actionRunId, onChoose: (action) => onSelect({ runId: actionRunId, action }), onBack: () => setActionRunId(null) }));
115
- }
116
- return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsxs(Text, { bold: true, children: ["Found ", runs.length, " prior run", runs.length === 1 ? '' : 's', ". Continue from one?"] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: rows.map((row, i) => {
117
- const focused = i === clampedFocus;
118
- const color = focused ? 'cyan' : undefined;
119
- const cursor = focused ? '>' : ' ';
120
- if (row.kind === 'run') {
121
- const isStale = staleRunIds?.has(row.run.id) ?? false;
122
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: color, children: cursor }), _jsx(Text, { color: color, children: runLine(row.run) }), isStale ? _jsx(Text, { dimColor: true, children: " (stale)" }) : null] }, `run-${row.run.id}`));
123
- }
124
- if (row.kind === 'show-all') {
125
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: color, children: cursor }), _jsxs(Text, { color: color, children: ["Show all (", runs.length, ")"] })] }, "show-all"));
126
- }
127
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: color, children: cursor }), _jsx(Text, { color: color, children: "[n] Start a new run" })] }, "new"));
128
- }) }), _jsxs(Box, { gap: 3, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "[j/k] Navigate" }), _jsx(Text, { dimColor: true, children: "[Enter] Select" }), _jsx(Text, { dimColor: true, children: "[n] New" }), _jsx(Text, { dimColor: true, children: "[q] Quit" })] })] }));
129
- }
@@ -1,56 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Box, Text } from 'ink';
4
- import { useImmediateInput } from '../analyze/select/tui/hooks/useImmediateInput.js';
5
- const OPTIONS = [
6
- { key: 'overwrite', label: 'overwrite', description: 'Replace existing files at this path.' },
7
- { key: 'new', label: 'new', description: 'Write to a timestamped subdirectory.' },
8
- { key: 'cancel', label: 'cancel', description: 'Go back and pick a different path.' },
9
- ];
10
- export function SaveConflictGate({ path, onOverwrite, onNew, onCancel }) {
11
- // Default cursor on "new" (index 1) — the safer non-destructive choice.
12
- const [focusIdx, setFocusIdx] = useState(1);
13
- const fire = (opt) => {
14
- if (opt === 'overwrite')
15
- onOverwrite();
16
- else if (opt === 'new')
17
- onNew();
18
- else
19
- onCancel();
20
- };
21
- useImmediateInput((rawInput, key) => {
22
- if (rawInput === 'o') {
23
- fire('overwrite');
24
- return;
25
- }
26
- if (rawInput === 'n') {
27
- fire('new');
28
- return;
29
- }
30
- if (rawInput === 'c') {
31
- fire('cancel');
32
- return;
33
- }
34
- if (key.upArrow) {
35
- setFocusIdx((i) => (i - 1 + OPTIONS.length) % OPTIONS.length);
36
- return;
37
- }
38
- if (key.downArrow) {
39
- setFocusIdx((i) => (i + 1) % OPTIONS.length);
40
- return;
41
- }
42
- if (key.return) {
43
- fire(OPTIONS[focusIdx].key);
44
- return;
45
- }
46
- if (key.escape) {
47
- onCancel();
48
- return;
49
- }
50
- });
51
- return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, color: "yellow", children: "A prior export exists at:" }), _jsx(Text, { children: path }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: OPTIONS.map((opt, i) => {
52
- const focused = i === focusIdx;
53
- const shortcut = opt.label[0];
54
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: focused ? 'cyan' : undefined, children: focused ? '❯' : ' ' }), _jsxs(Text, { color: focused ? 'cyan' : undefined, children: ["[", shortcut, "]", opt.label.slice(1)] }), _jsxs(Text, { dimColor: true, children: ["\u2014 ", opt.description] })] }, opt.key));
55
- }) }), _jsxs(Box, { gap: 3, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "[o/n/c] Shortcut" }), _jsx(Text, { dimColor: true, children: "[\u2191/\u2193 + Enter] Navigate" }), _jsx(Text, { dimColor: true, children: "[Esc] Cancel" })] })] }));
56
- }
@@ -1,75 +0,0 @@
1
- import { access } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
- export const CONFLICT_MODES = ['overwrite', 'skip', 'fail'];
4
- export function isConflictMode(value) {
5
- return CONFLICT_MODES.includes(value);
6
- }
7
- /**
8
- * The set of files the wizard / CLI may overwrite in the save directory.
9
- * Centralised so conflict detection and conflict reporting stay in lock-step.
10
- */
11
- const SAVE_FILES = ['components.json', 'tokens.json'];
12
- /**
13
- * Returns the subset of `SAVE_FILES` that already exist at `path`. Order is
14
- * stable so test assertions can reason about it.
15
- */
16
- async function listConflictingFiles(path) {
17
- const conflicts = [];
18
- for (const name of SAVE_FILES) {
19
- try {
20
- await access(join(path, name));
21
- conflicts.push(name);
22
- }
23
- catch {
24
- // ENOENT — file not present, keep checking the others.
25
- }
26
- }
27
- return conflicts;
28
- }
29
- /**
30
- * Returns true if `components.json` or `tokens.json` already exists at `path`.
31
- * Used by the wizard to decide whether to render the save-conflict gate before
32
- * writing.
33
- */
34
- export async function detectSaveConflict(path) {
35
- return (await listConflictingFiles(path)).length > 0;
36
- }
37
- function pad2(n) {
38
- return n < 10 ? `0${n}` : String(n);
39
- }
40
- /**
41
- * Build a timestamped subdirectory under `base` for the "new" branch of the
42
- * save-conflict gate. Format: `<base>/dsi-YYYYMMDD-HHMMSS` (local time).
43
- */
44
- export function buildTimestampedSubdir(base, now = new Date()) {
45
- const y = now.getFullYear();
46
- const m = pad2(now.getMonth() + 1);
47
- const d = pad2(now.getDate());
48
- const hh = pad2(now.getHours());
49
- const mm = pad2(now.getMinutes());
50
- const ss = pad2(now.getSeconds());
51
- return join(base, `dsi-${y}${m}${d}-${hh}${mm}${ss}`);
52
- }
53
- /**
54
- * Probe the requested save path and report whether the wizard needs to render
55
- * the conflict gate, or — when `onConflict` is supplied — resolve the conflict
56
- * headlessly using the chosen mode.
57
- */
58
- export async function resolveSavePath(path, options = {}) {
59
- const conflicts = await listConflictingFiles(path);
60
- const hasConflict = conflicts.length > 0;
61
- if (options.onConflict === undefined) {
62
- return hasConflict ? { kind: 'conflict', path } : { kind: 'no-conflict', path };
63
- }
64
- if (!hasConflict) {
65
- return { kind: 'write', path };
66
- }
67
- switch (options.onConflict) {
68
- case 'overwrite':
69
- return { kind: 'write', path };
70
- case 'skip':
71
- return { kind: 'write', path: buildTimestampedSubdir(path, options.now ?? new Date()) };
72
- case 'fail':
73
- return { kind: 'fail', conflict: { path, files: conflicts } };
74
- }
75
- }