@contentful/experience-design-system-cli 2.2.1

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 (165) hide show
  1. package/README.md +532 -0
  2. package/bin/cli.js +58 -0
  3. package/dist/package.json +56 -0
  4. package/dist/src/analyze/command.d.ts +3 -0
  5. package/dist/src/analyze/command.js +175 -0
  6. package/dist/src/analyze/extract/astro.d.ts +5 -0
  7. package/dist/src/analyze/extract/astro.js +280 -0
  8. package/dist/src/analyze/extract/pipeline.d.ts +6 -0
  9. package/dist/src/analyze/extract/pipeline.js +298 -0
  10. package/dist/src/analyze/extract/react.d.ts +2 -0
  11. package/dist/src/analyze/extract/react.js +1949 -0
  12. package/dist/src/analyze/extract/slot-detection.d.ts +35 -0
  13. package/dist/src/analyze/extract/slot-detection.js +101 -0
  14. package/dist/src/analyze/extract/stencil.d.ts +2 -0
  15. package/dist/src/analyze/extract/stencil.js +293 -0
  16. package/dist/src/analyze/extract/tsx-shared.d.ts +8 -0
  17. package/dist/src/analyze/extract/tsx-shared.js +263 -0
  18. package/dist/src/analyze/extract/vue-tsx.d.ts +2 -0
  19. package/dist/src/analyze/extract/vue-tsx.js +498 -0
  20. package/dist/src/analyze/extract/vue.d.ts +5 -0
  21. package/dist/src/analyze/extract/vue.js +647 -0
  22. package/dist/src/analyze/extract/web-components.d.ts +2 -0
  23. package/dist/src/analyze/extract/web-components.js +866 -0
  24. package/dist/src/analyze/pre-classify.d.ts +17 -0
  25. package/dist/src/analyze/pre-classify.js +144 -0
  26. package/dist/src/analyze/select/command.d.ts +2 -0
  27. package/dist/src/analyze/select/command.js +256 -0
  28. package/dist/src/analyze/select/index.d.ts +6 -0
  29. package/dist/src/analyze/select/index.js +5 -0
  30. package/dist/src/analyze/select/parser.d.ts +6 -0
  31. package/dist/src/analyze/select/parser.js +53 -0
  32. package/dist/src/analyze/select/persistence.d.ts +9 -0
  33. package/dist/src/analyze/select/persistence.js +42 -0
  34. package/dist/src/analyze/select/stdout.d.ts +7 -0
  35. package/dist/src/analyze/select/stdout.js +3 -0
  36. package/dist/src/analyze/select/tui/App.d.ts +8 -0
  37. package/dist/src/analyze/select/tui/App.js +491 -0
  38. package/dist/src/analyze/select/tui/components/ComponentDetail.d.ts +20 -0
  39. package/dist/src/analyze/select/tui/components/ComponentDetail.js +43 -0
  40. package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +11 -0
  41. package/dist/src/analyze/select/tui/components/FieldEditor.js +531 -0
  42. package/dist/src/analyze/select/tui/components/FinalizeDialog.d.ts +10 -0
  43. package/dist/src/analyze/select/tui/components/FinalizeDialog.js +15 -0
  44. package/dist/src/analyze/select/tui/components/HelpOverlay.d.ts +7 -0
  45. package/dist/src/analyze/select/tui/components/HelpOverlay.js +11 -0
  46. package/dist/src/analyze/select/tui/components/JsonEditor.d.ts +11 -0
  47. package/dist/src/analyze/select/tui/components/JsonEditor.js +154 -0
  48. package/dist/src/analyze/select/tui/components/JsonPanel.d.ts +11 -0
  49. package/dist/src/analyze/select/tui/components/JsonPanel.js +62 -0
  50. package/dist/src/analyze/select/tui/components/PreviewSummaryBar.d.ts +8 -0
  51. package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +29 -0
  52. package/dist/src/analyze/select/tui/components/QuitDialog.d.ts +8 -0
  53. package/dist/src/analyze/select/tui/components/QuitDialog.js +14 -0
  54. package/dist/src/analyze/select/tui/components/Sidebar.d.ts +15 -0
  55. package/dist/src/analyze/select/tui/components/Sidebar.js +48 -0
  56. package/dist/src/analyze/select/tui/components/SourcePanel.d.ts +11 -0
  57. package/dist/src/analyze/select/tui/components/SourcePanel.js +52 -0
  58. package/dist/src/analyze/select/tui/components/StatusBar.d.ts +11 -0
  59. package/dist/src/analyze/select/tui/components/StatusBar.js +6 -0
  60. package/dist/src/analyze/select/tui/components/TopBar.d.ts +10 -0
  61. package/dist/src/analyze/select/tui/components/TopBar.js +5 -0
  62. package/dist/src/analyze/select/tui/hooks/useImmediateInput.d.ts +24 -0
  63. package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +68 -0
  64. package/dist/src/analyze/select/tui/hooks/useKeymap.d.ts +24 -0
  65. package/dist/src/analyze/select/tui/hooks/useKeymap.js +67 -0
  66. package/dist/src/analyze/select/tui/hooks/useSession.d.ts +19 -0
  67. package/dist/src/analyze/select/tui/hooks/useSession.js +52 -0
  68. package/dist/src/analyze/select/tui/hooks/useUndo.d.ts +8 -0
  69. package/dist/src/analyze/select/tui/hooks/useUndo.js +26 -0
  70. package/dist/src/analyze/select/types.d.ts +46 -0
  71. package/dist/src/analyze/select/types.js +20 -0
  72. package/dist/src/analyze/select-agent/command.d.ts +2 -0
  73. package/dist/src/analyze/select-agent/command.js +208 -0
  74. package/dist/src/analyze/tui/AnalyzeView.d.ts +24 -0
  75. package/dist/src/analyze/tui/AnalyzeView.js +38 -0
  76. package/dist/src/apply/api-client.d.ts +35 -0
  77. package/dist/src/apply/api-client.js +143 -0
  78. package/dist/src/apply/command.d.ts +6 -0
  79. package/dist/src/apply/command.js +787 -0
  80. package/dist/src/apply/manifest.d.ts +1 -0
  81. package/dist/src/apply/manifest.js +1 -0
  82. package/dist/src/apply/tui/SelectView.d.ts +18 -0
  83. package/dist/src/apply/tui/SelectView.js +34 -0
  84. package/dist/src/apply/tui/ServerApplyView.d.ts +32 -0
  85. package/dist/src/apply/tui/ServerApplyView.js +42 -0
  86. package/dist/src/apply/tui/ServerPreviewView.d.ts +9 -0
  87. package/dist/src/apply/tui/ServerPreviewView.js +21 -0
  88. package/dist/src/credentials-store.d.ts +8 -0
  89. package/dist/src/credentials-store.js +30 -0
  90. package/dist/src/generate/agent-runner.d.ts +86 -0
  91. package/dist/src/generate/agent-runner.js +314 -0
  92. package/dist/src/generate/command.d.ts +2 -0
  93. package/dist/src/generate/command.js +545 -0
  94. package/dist/src/generate/edit/command.d.ts +2 -0
  95. package/dist/src/generate/edit/command.js +126 -0
  96. package/dist/src/generate/prompt-builder.d.ts +18 -0
  97. package/dist/src/generate/prompt-builder.js +202 -0
  98. package/dist/src/generate/tui/GenerateView.d.ts +12 -0
  99. package/dist/src/generate/tui/GenerateView.js +10 -0
  100. package/dist/src/import/command.d.ts +2 -0
  101. package/dist/src/import/command.js +96 -0
  102. package/dist/src/import/orchestrator.d.ts +37 -0
  103. package/dist/src/import/orchestrator.js +374 -0
  104. package/dist/src/import/path-utils.d.ts +15 -0
  105. package/dist/src/import/path-utils.js +30 -0
  106. package/dist/src/import/tui/WizardApp.d.ts +10 -0
  107. package/dist/src/import/tui/WizardApp.js +906 -0
  108. package/dist/src/import/tui/steps/CredentialsStep.d.ts +15 -0
  109. package/dist/src/import/tui/steps/CredentialsStep.js +79 -0
  110. package/dist/src/import/tui/steps/DoneStep.d.ts +20 -0
  111. package/dist/src/import/tui/steps/DoneStep.js +17 -0
  112. package/dist/src/import/tui/steps/ErrorStep.d.ts +8 -0
  113. package/dist/src/import/tui/steps/ErrorStep.js +11 -0
  114. package/dist/src/import/tui/steps/GateStep.d.ts +14 -0
  115. package/dist/src/import/tui/steps/GateStep.js +20 -0
  116. package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +8 -0
  117. package/dist/src/import/tui/steps/GenerateReviewStep.js +208 -0
  118. package/dist/src/import/tui/steps/PathValidationStep.d.ts +10 -0
  119. package/dist/src/import/tui/steps/PathValidationStep.js +151 -0
  120. package/dist/src/import/tui/steps/PreviewStep.d.ts +21 -0
  121. package/dist/src/import/tui/steps/PreviewStep.js +36 -0
  122. package/dist/src/import/tui/steps/RunningStep.d.ts +10 -0
  123. package/dist/src/import/tui/steps/RunningStep.js +20 -0
  124. package/dist/src/import/tui/steps/TokenInputStep.d.ts +8 -0
  125. package/dist/src/import/tui/steps/TokenInputStep.js +70 -0
  126. package/dist/src/import/tui/steps/WelcomeStep.d.ts +7 -0
  127. package/dist/src/import/tui/steps/WelcomeStep.js +33 -0
  128. package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +15 -0
  129. package/dist/src/import/tui/steps/WizardPreviewStep.js +121 -0
  130. package/dist/src/import/tui/steps/preview-diff.d.ts +10 -0
  131. package/dist/src/import/tui/steps/preview-diff.js +132 -0
  132. package/dist/src/index.d.ts +1 -0
  133. package/dist/src/index.js +2 -0
  134. package/dist/src/output/format.d.ts +23 -0
  135. package/dist/src/output/format.js +110 -0
  136. package/dist/src/print/command.d.ts +2 -0
  137. package/dist/src/print/command.js +199 -0
  138. package/dist/src/print/validate/tui/ValidateView.d.ts +15 -0
  139. package/dist/src/print/validate/tui/ValidateView.js +37 -0
  140. package/dist/src/print/validate/validators/cdf-validator.d.ts +2 -0
  141. package/dist/src/print/validate/validators/cdf-validator.js +104 -0
  142. package/dist/src/print/validate/validators/dtcg-validator.d.ts +2 -0
  143. package/dist/src/print/validate/validators/dtcg-validator.js +110 -0
  144. package/dist/src/print/validate/validators/format-errors.d.ts +12 -0
  145. package/dist/src/print/validate/validators/format-errors.js +18 -0
  146. package/dist/src/program.d.ts +2 -0
  147. package/dist/src/program.js +25 -0
  148. package/dist/src/session/command.d.ts +2 -0
  149. package/dist/src/session/command.js +261 -0
  150. package/dist/src/session/db.d.ts +111 -0
  151. package/dist/src/session/db.js +1114 -0
  152. package/dist/src/session/migration.d.ts +4 -0
  153. package/dist/src/session/migration.js +117 -0
  154. package/dist/src/session/session-id.d.ts +1 -0
  155. package/dist/src/session/session-id.js +212 -0
  156. package/dist/src/session/stats.d.ts +27 -0
  157. package/dist/src/session/stats.js +89 -0
  158. package/dist/src/setup/command.d.ts +2 -0
  159. package/dist/src/setup/command.js +765 -0
  160. package/dist/src/types.d.ts +48 -0
  161. package/dist/src/types.js +1 -0
  162. package/package.json +55 -0
  163. package/skills/generate-components.md +361 -0
  164. package/skills/generate-tokens.md +194 -0
  165. package/skills/select-components.md +180 -0
package/README.md ADDED
@@ -0,0 +1,532 @@
1
+ # @contentful/experience-design-system-cli
2
+
3
+ CLI for extracting, reviewing, generating, validating, and pushing Contentful Experience Design System component definitions.
4
+
5
+ ## Pipeline Overview
6
+
7
+ The commands form a pipeline. Run them in order, or use `import` to orchestrate the whole thing at once:
8
+
9
+ ```
10
+ analyze extract → analyze select-agent → generate components → apply push
11
+ ```
12
+
13
+ `analyze select-agent` uses an AI agent to decide which extracted components belong in Contentful Experience Orchestration. You can substitute it with `analyze select` for manual/pattern-based selection.
14
+
15
+ All intermediate data flows through a local SQLite session database (`~/.contentful/experience-design-system-cli/pipeline.db`). No JSON files are written between steps — each command reads its inputs from the session and writes its outputs back to it. Use `print` to export session data to JSON files on demand (e.g. for inspection or manual validation).
16
+
17
+ ---
18
+
19
+ ## Prerequisites
20
+
21
+ ### Coding agent
22
+
23
+ `generate components` (and `generate tokens`) requires a coding agent CLI in your `$PATH`. Choose one:
24
+
25
+ | Agent | Install | Auth |
26
+ |---|---|---|
27
+ | **Claude Code** (`claude`) | `npm install -g @anthropic-ai/claude-code` | `claude login` (browser OAuth) **or** set `ANTHROPIC_API_KEY` |
28
+ | **OpenAI Codex** (`codex`) | `npm install -g @openai/codex` | Set `OPENAI_API_KEY` |
29
+ | **OpenCode** (`opencode`) | `npm install -g opencode-ai` | Configure via `opencode auth` (supports multiple providers) |
30
+ | **Cursor** (`cursor`) | Install [Cursor](https://cursor.com) | Sign in to Cursor; exposes `cursor-agent` binary |
31
+
32
+ The CLI invokes the agent non-interactively in a subprocess. If the binary is not found in `$PATH`, the command exits 1 and prints manual fallback instructions.
33
+
34
+ ### Contentful credentials
35
+
36
+ `apply preview`, `apply select`, `apply push`, and `import` require access to a Contentful space. Set these environment variables (or pass the equivalent flags):
37
+
38
+ ```bash
39
+ export CONTENTFUL_MANAGEMENT_TOKEN=<your-cma-token> # required
40
+ export CONTENTFUL_SPACE_ID=<your-space-id> # required
41
+ export CONTENTFUL_ENVIRONMENT_ID=master # required
42
+ ```
43
+
44
+ **Option A — Contentful CLI:**
45
+
46
+ ```bash
47
+ npm install -g contentful-cli
48
+ contentful login # opens browser OAuth flow; token is stored in ~/.contentfulrc.json
49
+ ```
50
+
51
+ After logging in, retrieve the token:
52
+
53
+ ```bash
54
+ # Prints the stored token
55
+ contentful login
56
+
57
+ # Or read it directly
58
+ cat ~/.contentfulrc.json
59
+ ```
60
+
61
+ **Option B — Contentful web app:**
62
+
63
+ Settings → API keys → Content management tokens → Generate personal token.
64
+
65
+ Once you have the token, export it:
66
+
67
+ ```bash
68
+ export CONTENTFUL_MANAGEMENT_TOKEN=<your-cma-token>
69
+ ```
70
+
71
+ Alternatively, pass `--cma-token`, `--space-id`, and `--environment-id` directly on each command.
72
+
73
+ ---
74
+
75
+ ## Commands
76
+
77
+ ### `analyze extract`
78
+
79
+ Extract component definitions from a project source tree.
80
+
81
+ ```bash
82
+ experience-design-system-cli analyze extract --project <path> [--dir <src-dir>]
83
+ ```
84
+
85
+ | Option | Default | Description |
86
+ |---|---|---|
87
+ | `--project <path>` | _(required)_ | Path to the project root |
88
+ | `--dir <path>` | `src` (falls back to project root) | Source directory relative to project root |
89
+
90
+ Scans `.tsx`, `.ts`, `.jsx`, `.js`, `.vue`, and `.astro` files. Ignores `node_modules`, `dist`, `build`, `.next`, `.nuxt`, `coverage`, `storybook-static`, and `out` directories. Also ignores `*.stories.*`, `*.story.*`, `*.spec.*`, and `*.test.*` files.
91
+
92
+ Writes extracted components to the session database and prints `session=<id>` to stdout. In an interactive terminal, a scrollable TUI displays the extraction summary (including a warning for any components with 0 props and 0 slots); press `q` or `Enter` to exit.
93
+
94
+ ---
95
+
96
+ ### `analyze select`
97
+
98
+ Interactively select components for generation and optionally patch their definitions. Alias: `analyze edit`.
99
+
100
+ ```bash
101
+ experience-design-system-cli analyze select [--session <id>] [--project-root <path>]
102
+ ```
103
+
104
+ | Option | Default | Description |
105
+ |---|---|---|
106
+ | `--session <id>` | most recent completed `analyze extract` | Session ID from `analyze extract` |
107
+ | `--project-root <path>` | `cwd` | Project root for resolving component source files |
108
+ | `--select-all` | — | Select all components without launching the TUI |
109
+ | `--select <pattern>` | — | Select components whose name contains pattern (repeatable) |
110
+ | `--deselect <pattern>` | — | Deselect components whose name contains pattern (repeatable) |
111
+ | `--accept-all` | — | Alias for `--select-all` |
112
+ | `--reject <pattern>` | — | Alias for `--deselect <pattern>` (repeatable) |
113
+ | `--patch <path>` | — | Path to a JSON patch file for structured overrides |
114
+
115
+ Without `--select-all`, `--select`, `--deselect`, or `--patch`, launches a full-screen TUI (requires 60+ columns). With any non-interactive flag, exits immediately after applying decisions.
116
+
117
+ #### Keyboard Reference
118
+
119
+ | Key | Action |
120
+ |---|---|
121
+ | `↑` / `k` | Navigate up |
122
+ | `↓` / `j` | Navigate down |
123
+ | `Tab` | Toggle focus between sidebar and main panel |
124
+ | `a` | Accept selected component |
125
+ | `r` | Reject selected component |
126
+ | `e` | Enter edit mode for selected component |
127
+ | `s` | Toggle source code panel (requires 120+ cols) |
128
+ | `A` | Approve all unreviewed components |
129
+ | `F` | Open finalize dialog |
130
+ | `q` | Quit (prompts if unsaved edits) |
131
+ | `?` | Toggle help overlay |
132
+
133
+ **In edit mode:**
134
+
135
+ | Key | Action |
136
+ |---|---|
137
+ | Arrow keys | Move cursor |
138
+ | `Ctrl+S` | Save edits (validates JSON) |
139
+ | `Ctrl+Z` | Undo |
140
+ | `Esc` | Discard changes |
141
+
142
+ #### Patch file format
143
+
144
+ `--patch` accepts a JSON array of operations. Each operation targets a component by name:
145
+
146
+ ```json
147
+ [
148
+ { "component": "Button", "status": "accepted" },
149
+ { "component": "Input", "status": "rejected" },
150
+ { "component": "Card", "set": { "props[name=variant].type": "string" } }
151
+ ]
152
+ ```
153
+
154
+ `set` paths support dot notation and array item matching with `[name=value]` predicates.
155
+
156
+ #### Session resume
157
+
158
+ The review session is persisted in `~/.contentful/experience-design-system-cli/`. If the TUI is interrupted, re-running with the same `--session` resumes where you left off.
159
+
160
+ ---
161
+
162
+ ### `analyze select-agent`
163
+
164
+ Use an AI agent to decide which extracted components belong in Contentful Experience Orchestration as Component Types. Accepts any component that renders visible UI (atoms, molecules, and organisms are all valid) and rejects non-visual infrastructure: React hooks, context providers, A/B testing or variant-routing wrappers, analytics trackers, and security utilities. Runs one agent invocation per component at configurable concurrency, mirroring how `generate components` works.
165
+
166
+ ```bash
167
+ experience-design-system-cli analyze select-agent --agent claude [--session <id>]
168
+ ```
169
+
170
+ | Option | Default | Description |
171
+ |---|---|---|
172
+ | `--agent <name>` | _(required)_ | Agent to use: `claude`, `codex`, `opencode`, or `cursor` |
173
+ | `--session <id>` | most recent completed `analyze extract` | Session ID from `analyze extract` |
174
+ | `--project-root <path>` | `cwd` | Project root for resolving component source files |
175
+ | `--model <name>` | agent default | Model to use (defaults to a small/fast model per agent) |
176
+ | `--verbose` | — | Show full agent output including reasoning text |
177
+ | `--dry-run` | — | Print the prompt for the first component without invoking the agent |
178
+
179
+ Results are written to the same session state file used by `analyze select`, so `generate components` will pick up the decisions automatically. If you want to review or override the agent's selections, run `analyze select --session <id>` after `select-agent` completes.
180
+
181
+ ---
182
+
183
+ ### `generate components`
184
+
185
+ Invoke a coding agent to generate CDF component definitions from raw analysis output. Results are stored in the session database and passed directly to `apply` commands via `--session`. Use `print components` to export them to a JSON file on demand.
186
+
187
+ ```bash
188
+ experience-design-system-cli generate components --agent claude [--session <id>]
189
+ ```
190
+
191
+ | Option | Default | Description |
192
+ |---|---|---|
193
+ | `--agent <name>` | _(required)_ | Agent to use: `claude`, `codex`, `opencode`, or `cursor` |
194
+ | `--session <id>` | most recent completed `analyze extract` | Session ID from `analyze extract` |
195
+ | `--tokens <path>` | — | Path to `tokens.json` for token-linked prop resolution (optional) |
196
+ | `--token-map <path>` | — | Path to `token-name-map.json` sidecar (optional) |
197
+ | `--model <name>` | agent default | Model to use (defaults to a small/fast model per agent) |
198
+ | `--dry-run` | — | Print the prompt without invoking the agent |
199
+
200
+ Raw components are loaded from the session database and embedded directly in the prompt — no intermediate file is read. On success, generated CDF components are written back to the session database and `session=<id>` is printed to stdout.
201
+
202
+ **Autonomous mode** (default): the agent runs non-interactively, prints its result between `<<<EDS_OUTPUT_START>>>` / `<<<EDS_OUTPUT_END>>>` sentinel markers, and the CLI stores the validated CDF in the session database.
203
+
204
+ If the agent binary is not found in `$PATH`, the command exits 1 and prints manual fallback instructions including the skill file path.
205
+
206
+ ---
207
+
208
+ ### `generate components edit`
209
+
210
+ Review and correct the output of `generate components` before pushing.
211
+
212
+ ```bash
213
+ experience-design-system-cli generate components edit [--session <id>]
214
+ ```
215
+
216
+ | Option | Default | Description |
217
+ |---|---|---|
218
+ | `--session <id>` | most recent active session | Session ID to operate on |
219
+ | `--accept-all` | — | Accept all definitions without launching the TUI |
220
+ | `--reject <pattern>` | — | Reject definitions whose name contains pattern (repeatable) |
221
+ | `--patch <path>` | — | Path to a JSON patch file for structured overrides |
222
+
223
+ ---
224
+
225
+ ---
226
+
227
+ ### `generate tokens`
228
+
229
+ Invoke a coding agent to generate DTCG design tokens from raw token data. Results are stored in the session database and passed directly to `apply` commands via `--session`. Use `print tokens` to export them to a JSON file on demand.
230
+
231
+ ```bash
232
+ experience-design-system-cli generate tokens --agent claude [--raw-tokens <path>]
233
+ ```
234
+
235
+ | Option | Default | Description |
236
+ |---|---|---|
237
+ | `--agent <name>` | _(required)_ | Agent to use: `claude`, `codex`, `opencode`, or `cursor` |
238
+ | `--raw-tokens <path>` | — | Path to raw token input file |
239
+ | `--model <name>` | agent default | Model to use (defaults to a small/fast model per agent) |
240
+ | `--dry-run` | — | Print the prompt without invoking the agent |
241
+
242
+ ---
243
+
244
+ ### `generate tokens edit`
245
+
246
+ Review and correct the output of `generate tokens` before pushing.
247
+
248
+ ```bash
249
+ experience-design-system-cli generate tokens edit [--session <id>]
250
+ ```
251
+
252
+ Accepts the same flags as `generate components edit`.
253
+
254
+ ---
255
+
256
+ ### `print components`
257
+
258
+ Write generated CDF component definitions from the session database to a JSON file.
259
+
260
+ ```bash
261
+ experience-design-system-cli print components [--session <id>] [--out <path>]
262
+ ```
263
+
264
+ | Option | Default | Description |
265
+ |---|---|---|
266
+ | `--session <id>` | most recent completed `generate components` session | Session ID to read from |
267
+ | `--out <path>` | `components.json` | Output file path |
268
+
269
+ Exits 1 if no generated components exist for the session.
270
+
271
+ ---
272
+
273
+ ### `print tokens`
274
+
275
+ Write generated DTCG design tokens from the session database to a JSON file.
276
+
277
+ ```bash
278
+ experience-design-system-cli print tokens [--session <id>] [--out <path>]
279
+ ```
280
+
281
+ | Option | Default | Description |
282
+ |---|---|---|
283
+ | `--session <id>` | most recent completed `generate tokens` session | Session ID to read from |
284
+ | `--out <path>` | `tokens.json` | Output file path |
285
+
286
+ Reconstructs the full DTCG nested tree (groups + leaf tokens) from the normalized session storage. Exits 1 if no generated tokens exist for the session.
287
+
288
+ ---
289
+
290
+ ### `print validate`
291
+
292
+ Validate CDF component definitions and/or DTCG token files against their schemas.
293
+
294
+ ```bash
295
+ experience-design-system-cli print validate [--components <path>] [--tokens <path>]
296
+ ```
297
+
298
+ | Option | Description |
299
+ |---|---|
300
+ | `--components <path>` | Path to a CDF component JSON file |
301
+ | `--tokens <path>` | Path to a DTCG token JSON file |
302
+
303
+ At least one flag is required. In an interactive terminal, a scrollable TUI displays validation results. Exit code: `0` if all files are valid, `1` if any errors are found.
304
+
305
+ ---
306
+
307
+ ### `apply preview`
308
+
309
+ Show a read-only diff of what `apply push` would do.
310
+
311
+ ```bash
312
+ experience-design-system-cli apply preview \
313
+ --space-id $CONTENTFUL_SPACE_ID \
314
+ --environment-id master \
315
+ --session <id>
316
+ ```
317
+
318
+ At least one of `--session`, `--components`, or `--tokens` is required. `--session` and `--components` are mutually exclusive.
319
+
320
+ | Option | Default | Description |
321
+ |---|---|---|
322
+ | `--session <id>` | — | Session ID from `generate components` (reads components from session DB) |
323
+ | `--components <path>` | — | Path to a CDF `components.json` file (alternative to `--session`) |
324
+ | `--tokens <path>` | — | Path to a DTCG `tokens.json` file |
325
+ | `--space-id <id>` | _(required)_ | Contentful space ID |
326
+ | `--environment-id <id>` | _(required)_ | Contentful environment ID |
327
+ | `--cma-token <token>` | `CONTENTFUL_MANAGEMENT_TOKEN` env | CMA personal access token or app token |
328
+ | `--viewports <path>` | single catch-all viewport | JSON file with a viewport array applied to every imported component type |
329
+ | `--host <url>` | `https://api.contentful.com` | Override API base URL |
330
+ | `--include-unchanged` | — | Include unchanged entities in non-interactive JSON output |
331
+
332
+ In interactive mode, renders a two-level TUI: a summary view listing entities by status (new / changed / unchanged / conflict), with `Enter` to expand into a property-level diff view. In non-interactive mode, writes a structured JSON diff to stdout. Exit code: `0` if the diff is clean, `1` if there are kind conflicts that would block the push.
333
+
334
+ ---
335
+
336
+ ### `apply select`
337
+
338
+ Choose a subset of entities to push. Opens a checkbox TUI after computing the diff, or use non-interactive flags.
339
+
340
+ ```bash
341
+ experience-design-system-cli apply select \
342
+ --space-id $CONTENTFUL_SPACE_ID \
343
+ --environment-id master \
344
+ --session <id>
345
+ ```
346
+
347
+ Accepts the same flags as `apply preview` (except `--include-unchanged`), plus:
348
+
349
+ | Option | Default | Description |
350
+ |---|---|---|
351
+ | `--select-all` | — | Select all entities without launching TUI |
352
+ | `--select <pattern>` | — | Select entities by ID pattern (repeatable) |
353
+ | `--deselect <pattern>` | — | Deselect entities by ID pattern (repeatable) |
354
+
355
+ **Default TUI selection:** entities with status `new` or `changed` are pre-selected; `unchanged` and `kindConflict` start unchecked.
356
+
357
+ **Keyboard map (TUI):**
358
+
359
+ | Key | Action |
360
+ |---|---|
361
+ | `↑` / `↓` | Move cursor |
362
+ | `Space` | Toggle entity |
363
+ | `A` | Select all |
364
+ | `N` | Deselect all |
365
+ | `I` | Push selected entities |
366
+ | `Q` | Quit without pushing |
367
+
368
+ ---
369
+
370
+ ### `apply push`
371
+
372
+ Write component types and design tokens to Contentful ExO.
373
+
374
+ ```bash
375
+ experience-design-system-cli apply push \
376
+ --space-id $CONTENTFUL_SPACE_ID \
377
+ --environment-id master \
378
+ --session <id>
379
+ ```
380
+
381
+ Accepts the same flags as `apply preview` (except `--include-unchanged`), plus:
382
+
383
+ | Option | Default | Description |
384
+ |---|---|---|
385
+ | `--yes` | — | Skip interactive confirmation (required in non-TTY mode) |
386
+
387
+ Design tokens are written first (component types may reference token kinds). Each entity write is recorded in the session database atomically — if the push is interrupted, re-running with the same flags resumes from where it left off, skipping already-succeeded entities.
388
+
389
+ #### Viewport configuration
390
+
391
+ By default every imported component type gets a single catch-all viewport:
392
+
393
+ ```json
394
+ [{ "id": "all", "query": "*", "displayName": "All Sizes", "previewSize": "100%" }]
395
+ ```
396
+
397
+ To apply a custom set, pass `--viewports <path>` with a JSON array of objects containing `id`, `query`, `displayName`, and `previewSize`.
398
+
399
+ ---
400
+
401
+ ### `import`
402
+
403
+ Run the full pipeline in one command: analyze → select-agent → generate → push.
404
+
405
+ ```bash
406
+ experience-design-system-cli import \
407
+ --space-id $CONTENTFUL_SPACE_ID \
408
+ --environment-id master \
409
+ --cma-token $CONTENTFUL_MANAGEMENT_TOKEN \
410
+ --project <path> \
411
+ --agent claude
412
+ ```
413
+
414
+ Contentful credentials (`--space-id`, `--environment-id`, `--cma-token`) are only required when the apply step runs. Pass `--skip-apply` to run the pipeline without pushing to Contentful.
415
+
416
+ The select step uses `analyze select-agent` by default, letting the agent decide which components belong in Experience Orchestration. Pass `--select-all`, `--select`, or `--deselect` to override with pattern-based selection instead. To review and select components interactively, run the steps manually rather than using `import`.
417
+
418
+ | Option | Default | Description |
419
+ |---|---|---|
420
+ | `--space-id <id>` | _(required unless `--skip-apply`)_ | Contentful space ID |
421
+ | `--environment-id <id>` | _(required unless `--skip-apply`)_ | Contentful environment ID |
422
+ | `--cma-token <token>` | `CONTENTFUL_MANAGEMENT_TOKEN` env | CMA personal access token or app token |
423
+ | `--project <path>` | `.` | Path to the project root to analyze |
424
+ | `--out <path>` | `<project>/.contentful` | Directory where `components.json` is written when `--print` is set |
425
+ | `--agent <name>` | `claude` | Agent to use for `analyze select-agent` and `generate components` |
426
+ | `--model <name>` | agent default | Model to use for agent steps |
427
+ | `--select-all` | — | Skip agentic select; accept all extracted components |
428
+ | `--select <pattern>` | — | Skip agentic select; accept components matching pattern (repeatable) |
429
+ | `--deselect <pattern>` | — | Skip agentic select; deselect components matching pattern (repeatable) |
430
+ | `--skip-analyze` | — | Skip analyze; use most recent `analyze extract` session |
431
+ | `--skip-generate` | — | Skip generate; use most recent `generate components` session |
432
+ | `--print` | — | Write `components.json` to `--out` after generation |
433
+ | `--skip-apply` | — | Skip pushing to Contentful (stops after generate) |
434
+ | `--no-cache` | — | Re-run all steps even if output already exists |
435
+ | `--yes` | — | Skip interactive confirmation in `apply push` |
436
+ | `--viewports <path>` | — | JSON file with viewport array (passed to `apply push`) |
437
+ | `--host <url>` | — | Override API base URL (passed to `apply push`) |
438
+ | `--dry-run` | — | Print the generate prompt without invoking the agent |
439
+
440
+ ---
441
+
442
+ ### `session list`
443
+
444
+ List all pipeline sessions.
445
+
446
+ ```bash
447
+ experience-design-system-cli session list [--status <status>] [--limit <n>] [--json]
448
+ ```
449
+
450
+ | Option | Default | Description |
451
+ |---|---|---|
452
+ | `--status <status>` | — | Filter by `in-progress`, `complete`, `failed`, or `interrupted` |
453
+ | `--all` | — | Include interrupted sessions (hidden by default) |
454
+ | `--limit <n>` | `20` | Max rows to return |
455
+ | `--json` | — | Force JSON output |
456
+
457
+ ---
458
+
459
+ ### `session show <id>`
460
+
461
+ Show all steps for a session.
462
+
463
+ ```bash
464
+ experience-design-system-cli session show <id> [--json]
465
+ ```
466
+
467
+ ---
468
+
469
+ ### `session stats`
470
+
471
+ Show aggregate storage and record counts for the pipeline database.
472
+
473
+ ```bash
474
+ experience-design-system-cli session stats [--json]
475
+ ```
476
+
477
+ ---
478
+
479
+ ### `session prune`
480
+
481
+ Delete sessions matching criteria.
482
+
483
+ ```bash
484
+ experience-design-system-cli session prune --older-than 30d [--dry-run] [--yes]
485
+ ```
486
+
487
+ | Option | Description |
488
+ |---|---|
489
+ | `--id <id>` | Delete a specific session by ID |
490
+ | `--older-than <duration>` | Delete sessions older than this age (e.g. `30d`, `2w`, `1y`) |
491
+ | `--status <status>` | Delete sessions by last step status: `complete`, `failed`, `interrupted` |
492
+ | `--yes` | Skip confirmation prompt |
493
+ | `--dry-run` | Print what would be deleted without deleting |
494
+
495
+ At least one of `--id`, `--older-than`, or `--status` is required.
496
+
497
+ ---
498
+
499
+ ## Session Database
500
+
501
+ All pipeline state is stored in `~/.contentful/experience-design-system-cli/pipeline.db` (SQLite). The path can be overridden with the `EDS_PIPELINE_DB_PATH` environment variable.
502
+
503
+ Sessions are created by `analyze extract` and shared across all downstream commands. Use `session list` to see active sessions and `session prune` to clean up old ones.
504
+
505
+ ---
506
+
507
+ ## Terminal Compatibility
508
+
509
+ - Minimum 60 columns required for `analyze edit` interactive mode
510
+ - 80+ columns recommended for full sidebar + detail view
511
+ - 120+ columns required to show the source code panel
512
+ - `NO_COLOR=1` suppresses all ANSI color output
513
+ - Windows: supported via Ink v4; known limitations with older ConEmu and cmd.exe
514
+
515
+ ---
516
+
517
+ ## Development
518
+
519
+ ```bash
520
+ # Install dependencies from repo root
521
+ pnpm install
522
+
523
+ # Build
524
+ pnpm -F @contentful/experience-design-system-cli build
525
+
526
+ # Run tests
527
+ pnpm -F @contentful/experience-design-system-cli test
528
+
529
+ # Typecheck
530
+ pnpm -F @contentful/experience-design-system-cli typecheck
531
+ ```
532
+
package/bin/cli.js ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ // Suppress the SQLite ExperimentalWarning before any modules load.
3
+ // Must use dynamic import so this handler registers before sqlite is imported.
4
+ process.removeAllListeners("warning");
5
+ process.on("warning", (w) => {
6
+ if (w.name === "ExperimentalWarning" && w.message.includes("SQLite")) return;
7
+ process.stderr.write(`${w.name}: ${w.message}\n`);
8
+ });
9
+
10
+ // Auto-rebuild when running from source (dev mode only).
11
+ // Skipped when src/ doesn't exist (e.g. npm-installed users).
12
+ import { existsSync, statSync, readdirSync } from "node:fs";
13
+ import { execFileSync } from "node:child_process";
14
+ import { fileURLToPath } from "node:url";
15
+ import { dirname, resolve, join } from "node:path";
16
+
17
+ function newestMtime(dir, depth = 0) {
18
+ if (depth > 10) return 0;
19
+ let max = 0;
20
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
21
+ if (entry.isSymbolicLink()) continue;
22
+ const full = join(dir, entry.name);
23
+ if (entry.isDirectory()) {
24
+ max = Math.max(max, newestMtime(full, depth + 1));
25
+ } else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
26
+ max = Math.max(max, statSync(full).mtimeMs);
27
+ }
28
+ }
29
+ return max;
30
+ }
31
+
32
+ const __dirname = dirname(fileURLToPath(import.meta.url));
33
+ const pkgRoot = resolve(__dirname, "..");
34
+ const srcDir = join(pkgRoot, "src");
35
+ const distEntry = join(pkgRoot, "dist", "src", "index.js");
36
+
37
+ if (
38
+ existsSync(srcDir) &&
39
+ existsSync(distEntry) &&
40
+ process.env.NODE_ENV !== "test"
41
+ ) {
42
+ const distMtime = statSync(distEntry).mtimeMs;
43
+ if (newestMtime(srcDir) > distMtime) {
44
+ process.stderr.write("⚙ Source changed — rebuilding...\n");
45
+ const tsc = join(pkgRoot, "node_modules", ".bin", "tsc");
46
+ try {
47
+ execFileSync(tsc, ["-p", join(pkgRoot, "tsconfig.build.json")], {
48
+ stdio: ["ignore", "ignore", "inherit"],
49
+ cwd: pkgRoot,
50
+ });
51
+ process.stderr.write("✓ Build complete\n");
52
+ } catch {
53
+ process.stderr.write("✗ Build failed — running with existing dist\n");
54
+ }
55
+ }
56
+ }
57
+
58
+ await import("../dist/src/index.js");
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@contentful/experience-design-system-cli",
3
+ "version": "2.2.1",
4
+ "description": "Contentful Experiences design system import CLI",
5
+ "type": "module",
6
+ "bin": {
7
+ "experience-design-system-cli": "./bin/cli.js",
8
+ "exo": "./bin/cli.js"
9
+ },
10
+ "main": "./dist/src/index.js",
11
+ "types": "./dist/src/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/src/index.d.ts",
15
+ "import": "./dist/src/index.js",
16
+ "node": "./dist/src/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "bin/",
21
+ "dist/",
22
+ "skills/"
23
+ ],
24
+ "scripts": {
25
+ "build": "nx build experience-design-system-cli",
26
+ "typecheck": "nx typecheck experience-design-system-cli",
27
+ "clean": "nx clean experience-design-system-cli",
28
+ "test": "nx test experience-design-system-cli",
29
+ "test:watch": "nx test:watch experience-design-system-cli",
30
+ "lint": "nx lint experience-design-system-cli",
31
+ "lint:fix": "nx lint:fix experience-design-system-cli"
32
+ },
33
+ "dependencies": {
34
+ "@contentful/experience-design-system-types": "workspace:*",
35
+ "@vue/compiler-sfc": "^3.5.31",
36
+ "commander": "^13.1.0",
37
+ "ink": "^4.4.1",
38
+ "react": "^18.3.1",
39
+ "react-devtools-core": "^4.19.1",
40
+ "react-dom": "^18.3.1",
41
+ "ts-morph": "^27.0.2",
42
+ "typescript": "^5.9.3"
43
+ },
44
+ "devDependencies": {
45
+ "@tsconfig/node24": "^24.0.3",
46
+ "@types/node": "^24.0.3",
47
+ "@types/react": "^18.3.24",
48
+ "eslint": "^9.39.2",
49
+ "eslint-config-prettier": "^10.1.8",
50
+ "eslint-plugin-prettier": "^5.5.4",
51
+ "ink-testing-library": "^4.0.0",
52
+ "typescript-eslint": "^8.52.0",
53
+ "vitest": "^4.0.16"
54
+ },
55
+ "module": "./src/index.js"
56
+ }
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function collectSourceFiles(directory: string, onProgress?: (scannedCount: number) => void): Promise<string[]>;
3
+ export declare function registerAnalyzeCommand(program: Command): void;