@contentful/experience-design-system-cli 2.16.2-dev-build-ec5957c.0 → 2.16.2-dev-build-a6d3bd1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.16.2-dev-build-ec5957c.0",
3
+ "version": "2.16.2-dev-build-a6d3bd1.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,6 +19,7 @@ import { buildCompositionInputHash } from './composition/composition-cache-key.j
19
19
  import { runParserInSandbox } from './composition/agent-parser/sandbox.js';
20
20
  import { resolveViaAgentParser } from './composition/agent-parser/resolve-via-parser.js';
21
21
  import { parsePromptOverrides, resolvePromptOverride } from '../lib/prompt-overrides.js';
22
+ import { DEFAULT_AGENT_NAME, isAgentName } from '../lib/agent-names.js';
22
23
  import { runAgent } from '../generate/agent-runner.js';
23
24
  import { readExperiencesCredentials } from '../credentials-store.js';
24
25
  import { buildAnalyzeViewRows, partitionGlobalWarnings } from './build-analyze-view-rows.js';
@@ -132,13 +133,12 @@ export function componentsToInterchangeMap(components) {
132
133
  }
133
134
  /** Resolve which coding-agent runs mapping resolution: `--agent` flag > env > default. */
134
135
  function resolveCompositionAgentName(flagValue) {
135
- const valid = ['claude', 'codex', 'opencode', 'cursor'];
136
- if (flagValue && valid.includes(flagValue))
136
+ if (flagValue && isAgentName(flagValue))
137
137
  return flagValue;
138
138
  const env = process.env['EDS_COMPOSITION_AGENT'];
139
- if (env && valid.includes(env))
139
+ if (env && isAgentName(env))
140
140
  return env;
141
- return 'claude';
141
+ return DEFAULT_AGENT_NAME;
142
142
  }
143
143
  /**
144
144
  * Read the source files of the extracted components plus nearby mapping/meta
@@ -5,7 +5,7 @@ import { appendReviewEvent, getRefineArtifactsRoot, ensureRefineSession, getRefi
5
5
  import { loadReviewInput } from '../select/parser.js';
6
6
  import { buildPrompt } from '../../generate/prompt-builder.js';
7
7
  import { formatCustomPromptBanner } from '../../generate/command.js';
8
- import { parseSelectToolCallLines, runAgent } from '../../generate/agent-runner.js';
8
+ import { AGENT_NAMES, isAgentName, parseSelectToolCallLines, runAgent } from '../../generate/agent-runner.js';
9
9
  import { access } from 'node:fs/promises';
10
10
  import { readExperiencesCredentials } from '../../credentials-store.js';
11
11
  import { OutputFormatter, c } from '../../output/format.js';
@@ -13,7 +13,6 @@ import { buildRepoContextIndex, buildSelectionContext } from './context-builder.
13
13
  import { runShowRationale } from './show-rationale.js';
14
14
  import { isAbsolute, resolve } from 'node:path';
15
15
  import { validateExtractedComponents, shouldExcludeDueToValidation, formatExclusionWarning, } from '@contentful/experience-design-system-extraction';
16
- const VALID_AGENTS = new Set(['claude', 'codex', 'opencode', 'cursor']);
17
16
  const DEFAULT_TIMEOUT_MS = Number(process.env.EDS_AGENT_TIMEOUT_MS ?? 3 * 60 * 1000);
18
17
  export const DEFAULT_CONCURRENCY = 10;
19
18
  export const DEFAULT_BATCH_SIZE = 5;
@@ -336,8 +335,8 @@ export function registerAnalyzeSelectAgentCommand(program) {
336
335
  const savedCreds = await readExperiencesCredentials();
337
336
  const agentName = opts.agent ?? savedCreds.agent;
338
337
  const model = opts.model ?? savedCreds.agentModel;
339
- if (!agentName || !VALID_AGENTS.has(agentName)) {
340
- process.stderr.write(`Error: no agent configured. Pass --agent <name> or run experiences setup. Accepted values: claude, codex, opencode, cursor\n`);
338
+ if (!agentName || !isAgentName(agentName)) {
339
+ process.stderr.write(`Error: no agent configured. Pass --agent <name> or run experiences setup. Accepted values: ${AGENT_NAMES.join(', ')}\n`);
341
340
  process.exit(1);
342
341
  return;
343
342
  }
@@ -12,6 +12,7 @@ import { ServerPreviewApp, ServerPreviewConfirm, ServerApplyProgress, ServerAppl
12
12
  import { SelectView, makeSelectKey } from './tui/SelectView.js';
13
13
  import { buildPostPushUrl } from '../lib/contentful-urls.js';
14
14
  import { resolveCompositionMode } from '../lib/composition-mode.js';
15
+ import { addArtifactInputOptions, addCompositionOptions, addContentfulTargetOptions, addSelectionOptions, } from '../lib/command-options.js';
15
16
  import { stripAllowedComponents } from '../import/strip-allowed-components.js';
16
17
  import { readExperiencesCredentials } from '../credentials-store.js';
17
18
  import { getInteractiveTerminalSupport, requireInteractiveTerminal } from '../lib/terminal-capabilities.js';
@@ -411,26 +412,15 @@ function SelectApp({ entities, spaceId, environmentId, onApply }) {
411
412
  importing,
412
413
  });
413
414
  }
414
- function collect(val, prev) {
415
- return [...prev, val];
416
- }
417
415
  export function registerApplyCommand(program) {
418
416
  const applyCmd = program
419
417
  .command('apply')
420
418
  .description('Preview, select, or push design system entities to Contentful ExO');
421
- applyCmd
422
- .command('preview')
423
- .description('Show a read-only diff of what apply push would do')
424
- .option('--components <path>', 'Path to components.json (CDF)')
425
- .option('--tokens <path>', 'Path to tokens.json (DTCG)')
426
- .option('--session <id>', 'Pipeline session ID to load generated components from')
427
- .requiredOption('--space-id <id>', 'Contentful space ID')
428
- .requiredOption('--environment-id <id>', 'Contentful environment ID')
429
- .option('--cma-token <token>', 'CMA personal access token (or set CONTENTFUL_MANAGEMENT_TOKEN)')
430
- .option('--host <url>', 'Override API base URL')
431
- .option('--composite', 'Import embedded-component hierarchy (opt in; default is atomic)')
432
- .option('--atomic', 'Import flat components with no embedded-component hierarchy (default)')
433
- .action(async (opts) => {
419
+ const previewCmd = applyCmd.command('preview').description('Show a read-only diff of what apply push would do');
420
+ addArtifactInputOptions(previewCmd);
421
+ addContentfulTargetOptions(previewCmd);
422
+ addCompositionOptions(previewCmd);
423
+ previewCmd.action(async (opts) => {
434
424
  let inputs;
435
425
  try {
436
426
  inputs = await resolveSharedInputs(opts);
@@ -475,18 +465,11 @@ export function registerApplyCommand(program) {
475
465
  process.exit(0);
476
466
  }
477
467
  });
478
- applyCmd
479
- .command('push')
480
- .description('Write component types and design tokens to Contentful ExO')
481
- .option('--components <path>', 'Path to components.json (CDF)')
482
- .option('--tokens <path>', 'Path to tokens.json (DTCG)')
483
- .option('--session <id>', 'Pipeline session ID to load generated components from')
484
- .requiredOption('--space-id <id>', 'Contentful space ID')
485
- .requiredOption('--environment-id <id>', 'Contentful environment ID')
486
- .option('--cma-token <token>', 'CMA personal access token (or set CONTENTFUL_MANAGEMENT_TOKEN)')
487
- .option('--host <url>', 'Override API base URL')
488
- .option('--composite', 'Import embedded-component hierarchy (opt in; default is atomic)')
489
- .option('--atomic', 'Import flat components with no embedded-component hierarchy (default)')
468
+ const pushCmd = applyCmd.command('push').description('Write component types and design tokens to Contentful ExO');
469
+ addArtifactInputOptions(pushCmd);
470
+ addContentfulTargetOptions(pushCmd);
471
+ addCompositionOptions(pushCmd);
472
+ pushCmd
490
473
  .option('--yes', 'Skip interactive confirmation')
491
474
  .option('--verbose', 'Show all entity progress including skipped/unchanged')
492
475
  .option('--force', 'Skip confirmation for breaking changes (for CI)')
@@ -652,23 +635,12 @@ export function registerApplyCommand(program) {
652
635
  void instance.waitUntilExit().then(() => resolvePromise());
653
636
  });
654
637
  });
655
- applyCmd
656
- .command('select')
657
- .description('Select a subset of entities and push to Contentful ExO')
658
- .option('--components <path>', 'Path to components.json (CDF)')
659
- .option('--tokens <path>', 'Path to tokens.json (DTCG)')
660
- .option('--session <id>', 'Pipeline session ID to load generated components from')
661
- .requiredOption('--space-id <id>', 'Contentful space ID')
662
- .requiredOption('--environment-id <id>', 'Contentful environment ID')
663
- .option('--cma-token <token>', 'CMA personal access token (or set CONTENTFUL_MANAGEMENT_TOKEN)')
664
- .option('--host <url>', 'Override API base URL')
665
- .option('--composite', 'Import embedded-component hierarchy (opt in; default is atomic)')
666
- .option('--atomic', 'Import flat components with no embedded-component hierarchy (default)')
667
- .option('--select-all', 'Select all entities without launching TUI')
668
- .option('--select <pattern>', 'Select entities by ID pattern (repeatable)', collect, [])
669
- .option('--deselect <pattern>', 'Deselect entities by ID pattern (repeatable)', collect, [])
670
- .option('--force', 'Skip confirmation for breaking changes')
671
- .action(async (opts) => {
638
+ const selectCmd = applyCmd.command('select').description('Select a subset of entities and push to Contentful ExO');
639
+ addArtifactInputOptions(selectCmd);
640
+ addContentfulTargetOptions(selectCmd);
641
+ addCompositionOptions(selectCmd);
642
+ addSelectionOptions(selectCmd);
643
+ selectCmd.option('--force', 'Skip confirmation for breaking changes').action(async (opts) => {
672
644
  const nonInteractive = opts.selectAll || (opts.select ?? []).length > 0 || (opts.deselect ?? []).length > 0;
673
645
  if (!nonInteractive) {
674
646
  requireInteractiveTerminal({
@@ -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;
@@ -17,7 +18,7 @@ export type ExperiencesCredentials = {
17
18
  * flat components with no embedded-component hierarchy; `composite` opts into
18
19
  * the slot-graph machinery. Resolved `flag > env > this > default`.
19
20
  */
20
- compositionMode?: 'composite' | 'atomic';
21
+ compositionMode?: CompositionMode;
21
22
  };
22
23
  /**
23
24
  * Read persisted Contentful credentials.
@@ -2,6 +2,7 @@ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { toConfiguredHost } from './host-utils.js';
5
+ import { isCompositionMode } from './lib/composition-mode.js';
5
6
  const CREDENTIALS_DIR = join(homedir(), '.config', 'experiences');
6
7
  const CREDENTIALS_PATH = join(CREDENTIALS_DIR, 'credentials.json');
7
8
  /**
@@ -35,7 +36,7 @@ export async function readExperiencesCredentials() {
35
36
  ...(parsed.generatePromptPath ? { generatePromptPath: parsed.generatePromptPath } : {}),
36
37
  ...(typeof parsed.autoFilter === 'boolean' ? { autoFilter: parsed.autoFilter } : {}),
37
38
  ...(typeof parsed.debug === 'boolean' ? { debug: parsed.debug } : {}),
38
- ...(parsed.compositionMode === 'composite' || parsed.compositionMode === 'atomic'
39
+ ...(typeof parsed.compositionMode === 'string' && isCompositionMode(parsed.compositionMode)
39
40
  ? { compositionMode: parsed.compositionMode }
40
41
  : {}),
41
42
  };
@@ -63,7 +64,7 @@ export async function writeExperiencesCredentials(creds) {
63
64
  ...(generatePromptPath ? { generatePromptPath } : {}),
64
65
  ...(typeof autoFilter === 'boolean' ? { autoFilter } : {}),
65
66
  ...(typeof debug === 'boolean' ? { debug } : {}),
66
- ...(compositionMode === 'composite' || compositionMode === 'atomic' ? { compositionMode } : {}),
67
+ ...(compositionMode && isCompositionMode(compositionMode) ? { compositionMode } : {}),
67
68
  }, null, 2) + '\n', { mode: 0o600 });
68
69
  }
69
70
  export function experiencesCredentialsPath() {
@@ -1,4 +1,5 @@
1
- export type AgentName = 'claude' | 'codex' | 'opencode' | 'cursor';
1
+ import type { AgentName } from '../lib/agent-names.js';
2
+ export { AGENT_NAMES, isAgentName, type AgentName } from '../lib/agent-names.js';
2
3
  export interface AgentRunResult {
3
4
  exitCode: number;
4
5
  stdout: string;
@@ -1,5 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { getDebugLogger } from '../lib/debug-logger.js';
3
+ export { AGENT_NAMES, isAgentName } from '../lib/agent-names.js';
3
4
  const VALID_SELECT_TOOL_NAMES = new Set(['select_component', 'reject_component']);
4
5
  export function parseSelectToolCallLines(stdout) {
5
6
  const calls = [];
@@ -4,7 +4,7 @@ import { access, readFile, readdir, stat } from 'node:fs/promises';
4
4
  import { join, resolve } from 'node:path';
5
5
  import { execFile } from 'node:child_process';
6
6
  import { promisify } from 'node:util';
7
- import { describeAgentFailure, parseToolCallLines, parseTokenToolCallLines, resolveBinary, runAgent, } from './agent-runner.js';
7
+ import { AGENT_NAMES, describeAgentFailure, isAgentName, parseToolCallLines, parseTokenToolCallLines, resolveBinary, runAgent, } from './agent-runner.js';
8
8
  import { OutputFormatter, c } from '../output/format.js';
9
9
  import { formatGenerateProgressLine } from './progress.js';
10
10
  import { buildPrompt, resolveSkillPath } from './prompt-builder.js';
@@ -16,7 +16,6 @@ import { getRefineArtifactsRoot, getRefineSessionPaths } from '../analyze/select
16
16
  import { readExperiencesCredentials } from '../credentials-store.js';
17
17
  import { addAgentModelOptions } from '../lib/agent-model-options.js';
18
18
  const execFileAsync = promisify(execFile);
19
- const VALID_AGENTS = new Set(['claude', 'codex', 'opencode', 'cursor']);
20
19
  const DEFAULT_TIMEOUT_MS = Number(process.env.EDS_AGENT_TIMEOUT_MS ?? 3 * 60 * 1000);
21
20
  const DEFAULT_COMPONENT_CONCURRENCY = 10;
22
21
  const RETRY_BACKOFF_MS = Number(process.env.EDS_RETRY_BACKOFF_MS ?? 5_000);
@@ -313,8 +312,8 @@ async function runGenerateSkill(skill, opts, verbose = false) {
313
312
  const savedCreds = await readExperiencesCredentials();
314
313
  const agentName = opts.agent ?? savedCreds.agent;
315
314
  const model = opts.model ?? savedCreds.agentModel;
316
- if (!agentName || !VALID_AGENTS.has(agentName)) {
317
- die(`Error: no agent configured. Pass --agent <name> or run experiences setup. Accepted values: claude, codex, opencode, cursor`);
315
+ if (!agentName || !isAgentName(agentName)) {
316
+ die(`Error: no agent configured. Pass --agent <name> or run experiences setup. Accepted values: ${AGENT_NAMES.join(', ')}`);
318
317
  }
319
318
  const agent = agentName;
320
319
  // Feature 8: resolve custom-prompt path for `components` (flag wins over
@@ -4,6 +4,8 @@ import { resolveAutoFilter } from './auto-filter-resolve.js';
4
4
  import { resolveAgent, resolveModel } from './agent-model-resolve.js';
5
5
  import { addAgentModelOptions } from '../lib/agent-model-options.js';
6
6
  import { resolveCompositionMode } from '../lib/composition-mode.js';
7
+ import { addCompositionOptions } from '../lib/command-options.js';
8
+ import { isConflictMode } from '../runs/save-path-resolver.js';
7
9
  import { readExperiencesCredentials } from '../credentials-store.js';
8
10
  import { DEFAULT_CONFIGURED_HOST, toConfiguredHost } from '../host-utils.js';
9
11
  import { replayRun, modifyRun } from '../runs/replay-helpers.js';
@@ -42,9 +44,9 @@ export function registerImportCommand(program) {
42
44
  .option('--host <url>', 'Override API base URL (passed to apply push)')
43
45
  .option('--dry-run', "(deprecated, will change semantics in a future release) Print generate components prompt without invoking the agent. Use --print-prompt for the same behaviour explicitly, or '--dry-run --no-push' for the upcoming manifest-preview semantics.")
44
46
  .option('--print-prompt', 'Print the generate components prompt without invoking the agent. Replaces the legacy --dry-run prompt-print behaviour on this command.')
45
- .option('--auto-accept-scope', 'Accept all extracted components without prompting (for scripted/non-TTY callers)')
46
- .option('--composite', 'Import embedded-component hierarchy (opt in; default is atomic)')
47
- .option('--atomic', 'Import flat components with no embedded-component hierarchy (default)')
47
+ .option('--auto-accept-scope', 'Accept all extracted components without prompting (for scripted/non-TTY callers)');
48
+ addCompositionOptions(cmd);
49
+ cmd
48
50
  .option('--composition-map <path>', 'Consume a hand-authored parent→children interchange map (implies --composite)')
49
51
  .option('--composition-agent', 'Opt into agentic mapping resolution when deterministic sources find no groups (implies --composite)')
50
52
  .option('--composition-refresh', 'Bypass the composition cache and re-resolve from scratch, forcing the agent to run (implies --composite)')
@@ -59,7 +61,7 @@ export function registerImportCommand(program) {
59
61
  .option('--no-save', 'Push without writing components.json / tokens.json to disk (default: save AND push)')
60
62
  .option('--out-dir <path>', 'Save components.json / tokens.json to this directory; bypasses the inline save-path prompt')
61
63
  .option('--on-conflict <mode>', "How to handle existing components.json / tokens.json at the save path: 'overwrite' replaces files, 'skip' writes to a timestamped subdirectory, 'fail' exits non-zero. Skips the wizard's interactive conflict gate when set.", (value) => {
62
- if (value !== 'overwrite' && value !== 'skip' && value !== 'fail') {
64
+ if (!isConflictMode(value)) {
63
65
  process.stderr.write(`Error: invalid --on-conflict value '${value}'. Use one of: overwrite, skip, fail.\n`);
64
66
  process.exit(1);
65
67
  }
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { type RunPickerSelection } from '../../runs/run-picker.js';
3
3
  import type { RunRecord } from '../../runs/store.js';
4
- import { type OnConflictMode } from '../../runs/save-path-resolver.js';
4
+ import { type ConflictMode } from '../../runs/save-path-resolver.js';
5
5
  import type { CompositionMode } from '../../lib/composition-mode.js';
6
6
  export declare function buildSelectAgentArgs(opts: {
7
7
  sessionId: string;
@@ -55,7 +55,7 @@ export type WizardAppProps = {
55
55
  noPush?: boolean;
56
56
  noSave?: boolean;
57
57
  outDirOverride?: string;
58
- onConflictMode?: OnConflictMode;
58
+ onConflictMode?: ConflictMode;
59
59
  selectPromptPath?: string;
60
60
  generatePromptPath?: string;
61
61
  seedExtractSessionId?: string;
@@ -1,5 +1,5 @@
1
1
  import { type RunRecord } from '../../runs/store.js';
2
- import { type OnConflictMode } from '../../runs/save-path-resolver.js';
2
+ import { type ConflictMode } from '../../runs/save-path-resolver.js';
3
3
  /**
4
4
  * Wizard save-flow orchestration helpers (Task 4 / spec part 1+2).
5
5
  *
@@ -17,7 +17,7 @@ export type PlanSaveFlowInput = {
17
17
  * path interactively. When provided alongside an override, the conflict
18
18
  * gate is bypassed and the mode is applied via `resolveSavePath`.
19
19
  */
20
- onConflict?: OnConflictMode | undefined;
20
+ onConflict?: ConflictMode | undefined;
21
21
  };
22
22
  export type PlanSaveFlowOutput = {
23
23
  kind: 'write';
@@ -1,5 +1,5 @@
1
1
  import type { Command } from 'commander';
2
- export declare const AGENT_DESCRIPTION = "Agent to use: claude, codex, opencode, cursor (defaults to value saved by experiences setup)";
2
+ export declare const AGENT_DESCRIPTION: string;
3
3
  export declare const MODEL_DESCRIPTION = "Model to use (defaults to a lightweight per-agent model; override with EDS_AGENT_MODEL_<AGENT>)";
4
4
  export interface AgentModelOptionsConfig {
5
5
  agentDescription?: string;
@@ -1,4 +1,5 @@
1
- export const AGENT_DESCRIPTION = 'Agent to use: claude, codex, opencode, cursor (defaults to value saved by experiences setup)';
1
+ import { AGENT_NAMES } from './agent-names.js';
2
+ export const AGENT_DESCRIPTION = `Agent to use: ${AGENT_NAMES.join(', ')} (defaults to value saved by experiences setup)`;
2
3
  export const MODEL_DESCRIPTION = 'Model to use (defaults to a lightweight per-agent model; override with EDS_AGENT_MODEL_<AGENT>)';
3
4
  /**
4
5
  * Register --agent and optionally --model flags to a Commander command, with customizable descriptions.
@@ -0,0 +1,4 @@
1
+ export declare const AGENT_NAMES: readonly ["claude", "codex", "opencode", "cursor"];
2
+ export type AgentName = (typeof AGENT_NAMES)[number];
3
+ export declare const DEFAULT_AGENT_NAME: AgentName;
4
+ export declare function isAgentName(value: string): value is AgentName;
@@ -0,0 +1,5 @@
1
+ export const AGENT_NAMES = ['claude', 'codex', 'opencode', 'cursor'];
2
+ export const DEFAULT_AGENT_NAME = 'claude';
3
+ export function isAgentName(value) {
4
+ return AGENT_NAMES.includes(value);
5
+ }
@@ -0,0 +1,5 @@
1
+ import type { Command } from 'commander';
2
+ export declare function addArtifactInputOptions(cmd: Command): Command;
3
+ export declare function addContentfulTargetOptions(cmd: Command): Command;
4
+ export declare function addCompositionOptions(cmd: Command): Command;
5
+ export declare function addSelectionOptions(cmd: Command): Command;
@@ -0,0 +1,27 @@
1
+ export function addArtifactInputOptions(cmd) {
2
+ return cmd
3
+ .option('--components <path>', 'Path to components.json (CDF)')
4
+ .option('--tokens <path>', 'Path to tokens.json (DTCG)')
5
+ .option('--session <id>', 'Pipeline session ID to load generated components from');
6
+ }
7
+ export function addContentfulTargetOptions(cmd) {
8
+ return cmd
9
+ .requiredOption('--space-id <id>', 'Contentful space ID')
10
+ .requiredOption('--environment-id <id>', 'Contentful environment ID')
11
+ .option('--cma-token <token>', 'CMA personal access token (or set CONTENTFUL_MANAGEMENT_TOKEN)')
12
+ .option('--host <url>', 'Override API base URL');
13
+ }
14
+ export function addCompositionOptions(cmd) {
15
+ return cmd
16
+ .option('--composite', 'Import embedded-component hierarchy (opt in; default is atomic)')
17
+ .option('--atomic', 'Import flat components with no embedded-component hierarchy (default)');
18
+ }
19
+ function collectOptionValue(value, previous) {
20
+ return [...previous, value];
21
+ }
22
+ export function addSelectionOptions(cmd) {
23
+ return cmd
24
+ .option('--select-all', 'Select all entities without launching TUI')
25
+ .option('--select <pattern>', 'Select entities by ID pattern (repeatable)', collectOptionValue, [])
26
+ .option('--deselect <pattern>', 'Deselect entities by ID pattern (repeatable)', collectOptionValue, []);
27
+ }
@@ -1,4 +1,6 @@
1
- export type CompositionMode = 'composite' | 'atomic';
1
+ export declare const COMPOSITION_MODES: readonly ["composite", "atomic"];
2
+ export type CompositionMode = (typeof COMPOSITION_MODES)[number];
3
+ export declare function isCompositionMode(value: string): value is CompositionMode;
2
4
  /**
3
5
  * Options that, when present, IMPLY composite mode — passing any of them is a
4
6
  * clear opt-in, so requiring an explicit `--composite` alongside is redundant.
@@ -1,3 +1,7 @@
1
+ export const COMPOSITION_MODES = ['composite', 'atomic'];
2
+ export function isCompositionMode(value) {
3
+ return COMPOSITION_MODES.includes(value);
4
+ }
1
5
  function hasCompositionSource(opts) {
2
6
  return !!opts.compositionMap || !!opts.compositionAgent || !!opts.compositionRefresh || !!opts.generateMap;
3
7
  }
@@ -26,12 +30,10 @@ export function resolveCompositionMode(opts, configMode) {
26
30
  const env = process.env['EXPERIENCES_COMPOSITION_MODE'];
27
31
  if (env !== undefined && env !== '') {
28
32
  const v = env.toLowerCase();
29
- if (v === 'composite')
30
- return 'composite';
31
- if (v === 'atomic')
32
- return 'atomic';
33
+ if (isCompositionMode(v))
34
+ return v;
33
35
  }
34
- if (configMode === 'composite' || configMode === 'atomic')
36
+ if (configMode && isCompositionMode(configMode))
35
37
  return configMode;
36
38
  return 'atomic';
37
39
  }
@@ -2,6 +2,7 @@
2
2
  * Thin indirection so `runModifyCommand` can be unit-tested without spinning
3
3
  * up the real Ink wizard. The production implementation lives here.
4
4
  */
5
+ import type { CompositionMode } from '../lib/composition-mode.js';
5
6
  export type ModifyLauncherInput = {
6
7
  extractSessionId: string;
7
8
  generateSessionId: string | null;
@@ -15,7 +16,7 @@ export type ModifyLauncherInput = {
15
16
  saveMode: 'overwrite' | 'new' | 'prompt';
16
17
  /** Composition mode from the run record, so the modify wizard resumes in the
17
18
  * same mode. Omitted → the wizard's default (`atomic`). */
18
- compositionMode?: 'composite' | 'atomic';
19
+ compositionMode?: CompositionMode;
19
20
  outDirOverride?: string;
20
21
  /** Pre-fill space id (from the run record's pushedTo). */
21
22
  initialSpaceId?: string;
@@ -1,3 +1,6 @@
1
+ export declare const CONFLICT_MODES: readonly ["overwrite", "skip", "fail"];
2
+ export type ConflictMode = (typeof CONFLICT_MODES)[number];
3
+ export declare function isConflictMode(value: string): value is ConflictMode;
1
4
  /**
2
5
  * Returns true if `components.json` or `tokens.json` already exists at `path`.
3
6
  * Used by the wizard to decide whether to render the save-conflict gate before
@@ -16,10 +19,9 @@ export declare function buildTimestampedSubdir(base: string, now?: Date): string
16
19
  * - `skip` — write to a timestamped subdir under the requested path.
17
20
  * - `fail` — refuse to write; surface the conflicting filenames.
18
21
  */
19
- export type OnConflictMode = 'overwrite' | 'skip' | 'fail';
20
22
  export type ResolveSavePathOptions = {
21
23
  /** When provided, applies the chosen mode automatically (no interactive gate). */
22
- onConflict?: OnConflictMode;
24
+ onConflict?: ConflictMode;
23
25
  /** Injected for deterministic tests; falls back to `new Date()`. */
24
26
  now?: Date;
25
27
  };
@@ -1,5 +1,9 @@
1
1
  import { access } from 'node:fs/promises';
2
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
+ }
3
7
  /**
4
8
  * The set of files the wizard / CLI may overwrite in the save directory.
5
9
  * Centralised so conflict detection and conflict reporting stay in lock-step.
@@ -1,3 +1,4 @@
1
+ import type { CompositionMode } from '../lib/composition-mode.js';
1
2
  export declare const RUNS_FILE_VERSION: 3;
2
3
  export declare const RUNS_FILE_CAP = 200;
3
4
  /** Versions this CLI can read. v1 / v2 files are auto-migrated in memory;
@@ -58,7 +59,7 @@ export type RunRecord = {
58
59
  /** Composition mode the run was produced in, so modify/replay resumes in the
59
60
  * same mode (`composite` vs `atomic`). Absent on records written before this
60
61
  * field existed; callers treat a missing value as `atomic` (the default). */
61
- compositionMode?: 'composite' | 'atomic';
62
+ compositionMode?: CompositionMode;
62
63
  notes?: string;
63
64
  };
64
65
  export type RunsFile = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.16.2-dev-build-ec5957c.0",
3
+ "version": "2.16.2-dev-build-a6d3bd1.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,8 +34,8 @@
34
34
  "react": "^18.3.1",
35
35
  "react-devtools-core": "^4.19.1",
36
36
  "react-dom": "^18.3.1",
37
- "@contentful/experience-design-system-extraction": "2.16.2-dev-build-ec5957c.0",
38
- "@contentful/experience-design-system-types": "2.16.2-dev-build-ec5957c.0"
37
+ "@contentful/experience-design-system-extraction": "2.16.2-dev-build-a6d3bd1.0",
38
+ "@contentful/experience-design-system-types": "2.16.2-dev-build-a6d3bd1.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@tsconfig/node24": "^24.0.3",