@contentful/experience-design-system-cli 2.34.4-dev-build-f08fbb9.0 → 2.34.5-dev-build-a88e62a.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/README.md +26 -13
- package/bin/cli.js +13 -5
- package/dist/package.json +1 -1
- package/dist/src/credentials-store.d.ts +7 -0
- package/dist/src/import/orchestrator.d.ts +2 -0
- package/dist/src/import/strip-allowed-components.d.ts +18 -0
- package/dist/src/import/tui/WizardApp.d.ts +3 -1
- package/dist/src/import/tui/final-review-host.d.ts +3 -1
- package/dist/src/import/tui/scope-gate-host.d.ts +3 -1
- package/dist/src/import/tui/steps/AtomicGenerateReviewStep.d.ts +28 -0
- package/dist/src/import/tui/steps/AtomicScopeGateStep.d.ts +3 -0
- package/dist/src/index.js +1678 -826
- package/dist/src/lib/command-options.d.ts +1 -0
- package/dist/src/lib/composition-mode.d.ts +30 -0
- package/dist/src/runs/modify-launcher.d.ts +4 -0
- package/dist/src/runs/store.d.ts +5 -0
- package/dist/src/setup/composition-mode-prompt.d.ts +14 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -36,11 +36,20 @@ All intermediate data flows through a local SQLite session database (`~/.content
|
|
|
36
36
|
|
|
37
37
|
## Composite components & composition
|
|
38
38
|
|
|
39
|
-
A **composite component** is one that renders other components inside it — a `Card` that slots a `Button` and an `Icon`, a `Tabs` that slots `Tab` panels.
|
|
39
|
+
A **composite component** is one that renders other components inside it — a `Card` that slots a `Button` and an `Icon`, a `Tabs` that slots `Tab` panels. When you import composite components, the CLI can populate each slot's `$allowedComponents` so the parent→child relationships survive into Contentful.
|
|
40
|
+
|
|
41
|
+
### Atomic vs. composite
|
|
42
|
+
|
|
43
|
+
Imports are **atomic by default** — flat components, no embedded hierarchy. This is the right choice when you just want each component registered on its own. Opt into hierarchy resolution with `--composite` (or any composition flag, which implies it).
|
|
44
|
+
|
|
45
|
+
| Mode | Flag | Behavior |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| Atomic | `--atomic` (default) | Flat import; composition stripped before push |
|
|
48
|
+
| Composite | `--composite` | Resolve and import the parent→child hierarchy |
|
|
40
49
|
|
|
41
50
|
### How composition is resolved
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
Under `--composite`, relationships are resolved from the highest-confidence source available, in this precedence order:
|
|
44
53
|
|
|
45
54
|
1. **Typed slots (code)** — slots the source already declares, e.g. React `ReactElement<XProps>` / `children`, Svelte `Snippet<[XProps]>`, or an explicit `@allowedComponents` JSDoc tag. Fully deterministic; picked up automatically.
|
|
46
55
|
2. **Mapping map** — a hand-authored parent→children interchange map you feed with `--composition-map <path>`. Use `--generate-map <path>` to emit a skeleton from whatever was resolved, then hand-edit it and feed it back.
|
|
@@ -112,8 +121,8 @@ experiences import [flags]
|
|
|
112
121
|
```
|
|
113
122
|
welcome
|
|
114
123
|
↓
|
|
115
|
-
extracting — runs analyze extract (
|
|
116
|
-
|
|
124
|
+
extracting — runs analyze extract (atomic by default; resolves composition
|
|
125
|
+
under --composite, see below); spawns generate in parallel (prefetch)
|
|
117
126
|
↓
|
|
118
127
|
[auto-filter] — analyze select-agent runs automatically (skip with --no-auto-filter)
|
|
119
128
|
↓
|
|
@@ -172,10 +181,12 @@ Pass `--select-prompt-path <path>` and/or `--generate-prompt-path <path>` to swa
|
|
|
172
181
|
| `--model <name>` | agent default | Model name |
|
|
173
182
|
| `--tokens <path>` | — | DTCG `tokens.json` to push alongside generated components |
|
|
174
183
|
| `--auto-accept-scope` | off | Accept all extracted components without prompting (required for non-TTY without other headless flags) |
|
|
175
|
-
| `--
|
|
176
|
-
| `--
|
|
177
|
-
| `--composition-
|
|
178
|
-
| `--
|
|
184
|
+
| `--atomic` | **default** | Flat import, no embedded-component hierarchy (composition stripped on push) |
|
|
185
|
+
| `--composite` | — | Import the embedded-component hierarchy (any composition flag implies this) |
|
|
186
|
+
| `--composition-map <path>` | — | Consume a hand-authored parent→children interchange map (implies `--composite`) |
|
|
187
|
+
| `--generate-map <path>` | — | Also write a composition-map skeleton from the resolved composition (implies `--composite`) |
|
|
188
|
+
| `--composition-agent` | — | Opt into agentic resolution when deterministic sources find no groups (implies `--composite`) |
|
|
189
|
+
| `--composition-refresh` | — | Bypass the composition cache and re-resolve from scratch, forcing the agent to run (implies `--composite`) |
|
|
179
190
|
| `--composition-agent-mode <mode>` | `parser` | `parser` (agent writes a sandboxed parser) or `edges` (agent lists edges directly) |
|
|
180
191
|
| `--prompt <stage=value>` | — | Override a stage prompt (repeatable); value is a file path or literal text, e.g. `--prompt composition=./p.md` |
|
|
181
192
|
| `--auto-reject-cycles` | off (fail loud) | Auto-reject components in slot cycles and retry, instead of stopping with the cycle path |
|
|
@@ -274,10 +285,12 @@ experiences analyze extract --project <path> [--dir <src-dir>] [composition flag
|
|
|
274
285
|
| `--project <path>` | _(required)_ | Path to the project root |
|
|
275
286
|
| `--dir <path>` | `src` (falls back to project root) | Source directory relative to project root |
|
|
276
287
|
| `--resolve-unreachable <mode>` | `auto` | Retry pass for unresolved Svelte `Props` types: `auto`, `always`, or `never` |
|
|
277
|
-
| `--
|
|
278
|
-
| `--
|
|
279
|
-
| `--composition-
|
|
280
|
-
| `--
|
|
288
|
+
| `--atomic` | **default** | Skip composition resolution — flat components only |
|
|
289
|
+
| `--composite` | — | Resolve embedded-component composition (any composition flag implies this) |
|
|
290
|
+
| `--composition-map <path>` | — | Consume a hand-authored parent→children interchange map (implies `--composite`) |
|
|
291
|
+
| `--generate-map <path>` | — | Write a skeleton interchange map from the resolved composition (implies `--composite`) |
|
|
292
|
+
| `--composition-agent` | — | Opt into agentic resolution when deterministic sources find no groups (implies `--composite`) |
|
|
293
|
+
| `--composition-refresh` | — | Bypass the composition cache and re-resolve from scratch, forcing the agent to run (implies `--composite`) |
|
|
281
294
|
| `--composition-agent-mode <mode>` | `parser` | `parser` (agent writes a sandboxed parser — deterministic) or `edges` (agent lists edges directly) |
|
|
282
295
|
| `--agent <name>` | saved by setup | Coding agent for composition resolution: `claude`, `codex`, `opencode`, `cursor`, `copilot` |
|
|
283
296
|
| `--prompt <stage=value>` | — | Override a stage prompt (repeatable); value is a file path or literal text, e.g. `--prompt composition=./p.md` |
|
|
@@ -288,7 +301,7 @@ Writes extracted components to the session database and prints `session=<id>` to
|
|
|
288
301
|
|
|
289
302
|
The deterministic non-authorable filter drops infrastructure components with no authoring surface (Context providers, refs-only wrappers, etc.); each drop is reported as a warning so the operator can audit.
|
|
290
303
|
|
|
291
|
-
See [Composite components & composition](#composite-components--composition) for how the composition flags resolve parent→child relationships.
|
|
304
|
+
Extraction is **atomic by default** — flat components, no embedded hierarchy. See [Composite components & composition](#composite-components--composition) for how `--composite` and the composition flags resolve parent→child relationships.
|
|
292
305
|
|
|
293
306
|
---
|
|
294
307
|
|
package/bin/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ process.on("warning", (w) => {
|
|
|
11
11
|
// Skipped when src/ doesn't exist (e.g. npm-installed users).
|
|
12
12
|
import { existsSync, statSync, readdirSync } from "node:fs";
|
|
13
13
|
import { execFileSync } from "node:child_process";
|
|
14
|
+
import { createRequire } from "node:module";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
15
16
|
import { dirname, resolve, join } from "node:path";
|
|
16
17
|
|
|
@@ -42,12 +43,19 @@ if (
|
|
|
42
43
|
const distMtime = statSync(distEntry).mtimeMs;
|
|
43
44
|
if (newestMtime(srcDir) > distMtime) {
|
|
44
45
|
process.stderr.write("⚙ Source changed — rebuilding...\n");
|
|
45
|
-
|
|
46
|
+
// Run the compiler's own entry script with this Node rather than the
|
|
47
|
+
// node_modules/.bin/tsc shim: on Windows the extensionless shim is a POSIX
|
|
48
|
+
// sh script (the runnable one is tsc.CMD), so exec'ing it fails there.
|
|
49
|
+
const tsc = createRequire(import.meta.url).resolve("typescript/bin/tsc");
|
|
46
50
|
try {
|
|
47
|
-
execFileSync(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
execFileSync(
|
|
52
|
+
process.execPath,
|
|
53
|
+
[tsc, "-p", join(pkgRoot, "tsconfig.build.json")],
|
|
54
|
+
{
|
|
55
|
+
stdio: ["ignore", "ignore", "inherit"],
|
|
56
|
+
cwd: pkgRoot,
|
|
57
|
+
},
|
|
58
|
+
);
|
|
51
59
|
process.stderr.write("✓ Build complete\n");
|
|
52
60
|
} catch {
|
|
53
61
|
process.stderr.write("✗ Build failed — running with existing dist\n");
|
package/dist/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CompositionMode } from './lib/composition-mode.js';
|
|
1
2
|
export type ExperiencesCredentials = {
|
|
2
3
|
spaceId: string;
|
|
3
4
|
environmentId: string;
|
|
@@ -14,6 +15,12 @@ export type ExperiencesCredentials = {
|
|
|
14
15
|
debug?: boolean;
|
|
15
16
|
/** Persisted opt-out for anonymous CLI usage analytics, set via `experiences setup`. */
|
|
16
17
|
analyticsDisabled?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Feature (atomic mode): default composition mode. `atomic` (default) imports
|
|
20
|
+
* flat components with no embedded-component hierarchy; `composite` opts into
|
|
21
|
+
* the slot-graph machinery. Resolved `flag > env > this > default`.
|
|
22
|
+
*/
|
|
23
|
+
compositionMode?: CompositionMode;
|
|
17
24
|
};
|
|
18
25
|
/**
|
|
19
26
|
* Read persisted Contentful credentials.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CompositionMode } from '../lib/composition-mode.js';
|
|
1
2
|
export interface PipelineOptions {
|
|
2
3
|
project: string;
|
|
3
4
|
out: string;
|
|
@@ -27,6 +28,7 @@ export interface PipelineOptions {
|
|
|
27
28
|
/** When true, auto-reject cycle participants and retry push instead of surfacing an error. */
|
|
28
29
|
autoRejectCycles?: boolean;
|
|
29
30
|
allowDeletions?: boolean;
|
|
31
|
+
compositionMode?: CompositionMode;
|
|
30
32
|
compositionMap?: string;
|
|
31
33
|
compositionAgent?: boolean;
|
|
32
34
|
compositionAgentMode?: string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CDFComponentEntry } from '@contentful/experience-design-system-types';
|
|
2
|
+
/**
|
|
3
|
+
* Atomic-mode normalization (spec T8, option a): keep every slot's structure
|
|
4
|
+
* but drop its `$allowedComponents` composition constraint. Applied over the
|
|
5
|
+
* loaded component array regardless of source (`--session` or `--components`),
|
|
6
|
+
* so the atomic bypass cannot leak a live composition link into the pushed
|
|
7
|
+
* manifest. Starving `$allowedComponents` at this single point also makes
|
|
8
|
+
* slot-cycle detection structurally return zero (see T12).
|
|
9
|
+
*
|
|
10
|
+
* Returns a new array with cloned entries; inputs are not mutated.
|
|
11
|
+
*/
|
|
12
|
+
export declare function stripAllowedComponents(components: Array<{
|
|
13
|
+
key: string;
|
|
14
|
+
entry: CDFComponentEntry;
|
|
15
|
+
}>): Array<{
|
|
16
|
+
key: string;
|
|
17
|
+
entry: CDFComponentEntry;
|
|
18
|
+
}>;
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { type RunPickerSelection } from '../../runs/run-picker.js';
|
|
3
3
|
import type { RunRecord } from '../../runs/store.js';
|
|
4
4
|
import { type ConflictMode } from '../../runs/save-path-resolver.js';
|
|
5
|
+
import type { CompositionMode } from '../../lib/composition-mode.js';
|
|
5
6
|
export declare function buildSelectAgentArgs(opts: {
|
|
6
7
|
sessionId: string;
|
|
7
8
|
agent: string;
|
|
@@ -58,6 +59,7 @@ export type WizardAppProps = {
|
|
|
58
59
|
host?: string;
|
|
59
60
|
autoAcceptScope?: boolean;
|
|
60
61
|
autoRejectCycles?: boolean;
|
|
62
|
+
compositionMode?: CompositionMode;
|
|
61
63
|
compositionMap?: string;
|
|
62
64
|
compositionAgent?: boolean;
|
|
63
65
|
compositionAgentMode?: string;
|
|
@@ -84,4 +86,4 @@ export type WizardAppProps = {
|
|
|
84
86
|
onRunPicked?: (selection: RunPickerSelection) => void;
|
|
85
87
|
allowDeletions?: boolean;
|
|
86
88
|
};
|
|
87
|
-
export declare function WizardApp({ initialSpaceId, initialEnvironmentId, initialCmaToken, initialHost, initialAgent, initialModel, bedrock, initialProjectPath, host, autoAcceptScope, autoRejectCycles, compositionMap, compositionAgent, compositionAgentMode, compositionRefresh, generateMap, promptOverrides, noCache, autoFilter, livePreview, noPush, noSave, outDirOverride, onConflictMode, selectPromptPath, generatePromptPath, skipMapTokens, seedExtractSessionId, seedGenerateSessionId, seedTokenSessionId, seedTokensPath, initialStep, initialRawTokensPath, initialRuns, onRunPicked, allowDeletions, }?: WizardAppProps): React.ReactElement;
|
|
89
|
+
export declare function WizardApp({ initialSpaceId, initialEnvironmentId, initialCmaToken, initialHost, initialAgent, initialModel, bedrock, initialProjectPath, host, autoAcceptScope, autoRejectCycles, compositionMode, compositionMap, compositionAgent, compositionAgentMode, compositionRefresh, generateMap, promptOverrides, noCache, autoFilter, livePreview, noPush, noSave, outDirOverride, onConflictMode, selectPromptPath, generatePromptPath, skipMapTokens, seedExtractSessionId, seedGenerateSessionId, seedTokenSessionId, seedTokensPath, initialStep, initialRawTokensPath, initialRuns, onRunPicked, allowDeletions, }?: WizardAppProps): React.ReactElement;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { CompositionMode } from '../../lib/composition-mode.js';
|
|
2
3
|
import type { ReviewStepProps } from './review-step-props.js';
|
|
3
4
|
export type FinalReviewHostProps = Omit<ReviewStepProps, 'extractSessionId'> & {
|
|
4
5
|
extractSessionId: string | null;
|
|
5
6
|
generatedCount: number;
|
|
6
7
|
autoAccept: boolean;
|
|
8
|
+
compositionMode?: CompositionMode;
|
|
7
9
|
};
|
|
8
|
-
export declare function FinalReviewHost({ extractSessionId, tokenSessionId, generatedCount, autoAccept, onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, initialFinalizeError, allowDeletions, }: FinalReviewHostProps): React.ReactElement;
|
|
10
|
+
export declare function FinalReviewHost({ extractSessionId, tokenSessionId, generatedCount, autoAccept, compositionMode, onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, initialFinalizeError, allowDeletions, }: FinalReviewHostProps): React.ReactElement;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ScopeComponent } from './steps/ScopeGateStep.js';
|
|
3
|
+
import type { CompositionMode } from '../../lib/composition-mode.js';
|
|
3
4
|
export type { ScopeComponent };
|
|
4
5
|
type AutoFilterStatus = 'idle' | 'running' | 'complete' | 'cancelled' | 'failed';
|
|
5
6
|
export type ScopeGateHostProps = {
|
|
6
7
|
components: ReadonlyArray<ScopeComponent>;
|
|
7
8
|
autoAccept: boolean;
|
|
9
|
+
compositionMode?: CompositionMode;
|
|
8
10
|
onConfirm: (decisions: {
|
|
9
11
|
accepted: string[];
|
|
10
12
|
rejected: string[];
|
|
@@ -18,4 +20,4 @@ export type ScopeGateHostProps = {
|
|
|
18
20
|
aiFilterError?: string | null;
|
|
19
21
|
onCancelAutoFilter?: () => void;
|
|
20
22
|
};
|
|
21
|
-
export declare function ScopeGateHost({ components, autoAccept, onConfirm, onQuit, aiFilterStatus, aiFilterProgress, aiFilterError, onCancelAutoFilter, }: ScopeGateHostProps): React.ReactElement;
|
|
23
|
+
export declare function ScopeGateHost({ components, autoAccept, compositionMode, onConfirm, onQuit, aiFilterStatus, aiFilterProgress, aiFilterError, onCancelAutoFilter, }: ScopeGateHostProps): React.ReactElement;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type GenerateReviewStepProps = {
|
|
3
|
+
extractSessionId: string;
|
|
4
|
+
tokenSessionId?: string | null;
|
|
5
|
+
onFinalize: (accepted: number, rejected: number, unresolved: number) => void;
|
|
6
|
+
onQuit: () => void;
|
|
7
|
+
/**
|
|
8
|
+
* Feature 2 (live preview after every save). When `true` (default), the
|
|
9
|
+
* wizard re-runs `previewImport` after each successful FieldEditor Ctrl+S
|
|
10
|
+
* (debounced 500ms) and refreshes the sidebar's previewAnnotation badges.
|
|
11
|
+
* Operator opts out via `experiences import --no-live-preview`.
|
|
12
|
+
*/
|
|
13
|
+
livePreview?: boolean;
|
|
14
|
+
spaceId?: string;
|
|
15
|
+
environmentId?: string;
|
|
16
|
+
cmaToken?: string;
|
|
17
|
+
host?: string;
|
|
18
|
+
tokensPath?: string;
|
|
19
|
+
/**
|
|
20
|
+
* INTEG-4411 refined: initial value for the inline `finalizeError` banner.
|
|
21
|
+
* The wizard sets this when it routes back to `final-review` after the
|
|
22
|
+
* preview API returned an empty diff (pure no-op push). Cleared on the
|
|
23
|
+
* next `a` / `A` keystroke.
|
|
24
|
+
*/
|
|
25
|
+
initialFinalizeError?: string | null;
|
|
26
|
+
};
|
|
27
|
+
export declare function AtomicGenerateReviewStep({ extractSessionId, tokenSessionId, onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, initialFinalizeError, }: GenerateReviewStepProps): React.ReactElement;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ScopeGateStepProps } from './ScopeGateStep.js';
|
|
3
|
+
export declare function AtomicScopeGateStep({ components, onConfirm, onQuit, aiFilterStatus, aiFilterProgress, aiFilterError, onCancelAutoFilter, }: ScopeGateStepProps): React.ReactElement;
|