@contentful/experience-design-system-cli 2.18.1-dev-build-180092e.0 → 2.18.1-dev-build-0f85b83.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 +2 -1
- package/dist/package.json +2 -2
- package/dist/src/analyze/command.js +3 -3
- package/dist/src/analyze/composition/parse-map-edges.d.ts +1 -1
- package/dist/src/analyze/select/tui/App.js +4 -1
- package/dist/src/analyze/select-agent/command.js +5 -6
- package/dist/src/apply/api-client.d.ts +5 -2
- package/dist/src/apply/api-client.js +7 -4
- package/dist/src/apply/command.js +27 -10
- package/dist/src/apply/preview-utils.d.ts +5 -1
- package/dist/src/apply/preview-utils.js +5 -1
- package/dist/src/apply/tui/ServerApplyView.d.ts +6 -3
- package/dist/src/apply/tui/ServerApplyView.js +15 -6
- package/dist/src/apply/tui/ServerPreviewView.d.ts +2 -1
- package/dist/src/apply/tui/ServerPreviewView.js +3 -3
- package/dist/src/generate/agent-runner.d.ts +125 -0
- package/dist/src/generate/agent-runner.js +427 -0
- package/dist/src/generate/command.d.ts +6 -0
- package/dist/src/generate/command.js +17 -7
- package/dist/src/generate/progress.d.ts +9 -0
- package/dist/src/generate/progress.js +11 -0
- package/dist/src/generate/prompt-builder.d.ts +26 -0
- package/dist/src/generate/prompt-builder.js +213 -0
- package/dist/src/import/command.js +6 -1
- package/dist/src/import/orchestrator.d.ts +1 -0
- package/dist/src/import/orchestrator.js +2 -0
- package/dist/src/import/tui/WizardApp.d.ts +2 -1
- package/dist/src/import/tui/WizardApp.js +9 -9
- package/dist/src/import/tui/final-review-host.d.ts +2 -1
- package/dist/src/import/tui/final-review-host.js +2 -2
- package/dist/src/import/tui/runLivePreview.d.ts +3 -0
- package/dist/src/import/tui/runLivePreview.js +4 -1
- package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +2 -1
- package/dist/src/import/tui/steps/GenerateReviewStep.js +3 -3
- package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +6 -2
- package/dist/src/import/tui/steps/WizardPreviewStep.js +12 -3
- package/dist/src/import/tui/useFinalizePreview.d.ts +2 -0
- package/dist/src/import/tui/useFinalizePreview.js +0 -0
- package/dist/src/import/tui/useLivePreview.d.ts +2 -0
- package/dist/src/import/tui/useLivePreview.js +1 -0
- package/dist/src/lib/agent-model-options.js +1 -1
- package/dist/src/lib/agent-names.d.ts +4 -0
- package/dist/src/lib/agent-names.js +5 -0
- package/dist/src/lib/command-options.d.ts +1 -0
- package/dist/src/lib/command-options.js +3 -0
- package/dist/src/runs/modify-launcher.d.ts +2 -0
- package/dist/src/runs/modify-launcher.js +2 -0
- package/dist/src/runs/push-helpers.d.ts +1 -0
- package/dist/src/runs/push-helpers.js +3 -0
- package/dist/src/runs/replay-helpers.d.ts +4 -0
- package/dist/src/runs/replay-helpers.js +3 -0
- package/dist/src/session/cache-keys.d.ts +1 -1
- package/dist/src/session/cache-keys.js +1 -1
- package/dist/src/session/db.d.ts +1 -1
- package/dist/src/session/db.js +1 -1
- package/package.json +5 -5
- package/skills/generate-components.md +427 -0
- package/skills/generate-tokens.md +194 -0
- package/skills/select-components.md +224 -0
package/README.md
CHANGED
|
@@ -214,6 +214,7 @@ Pass `--select-prompt-path <path>` and/or `--generate-prompt-path <path>` to swa
|
|
|
214
214
|
| `--host <url>` | `https://api.contentful.com` | Override API base URL |
|
|
215
215
|
| `--on-conflict <mode>` | _(prompt via `<SaveConflictGate>`)_ | Headless conflict resolution when a file already exists at the save path: `overwrite`, `skip`, or `fail`. Bypasses the wizard's interactive save-conflict gate. Mutex with `--no-save`. |
|
|
216
216
|
| `--print-prompt` | — | Print the generate prompt to stdout and exit. Replaces the prompt-print semantics of `--dry-run`. |
|
|
217
|
+
| `--allow-deletions` | off (non-destructive) | Allow the push to delete remote ComponentTypes/DesignTokens missing from the manifest. Default skips them instead of deleting. Without this flag, preview responses suppress the removed-entity list and return a count instead; interactive confirm screens show an opt-out toggle (never opt-in) only when the flag is passed. Forwarded to headless subprocess pushes and `--push-from-run`. |
|
|
217
218
|
| `--dry-run` | _(deprecated)_ | Deprecated alias for `--print-prompt`. Emits a stderr deprecation notice; prompt-print semantics will be removed in a future release. |
|
|
218
219
|
|
|
219
220
|
### Run-picker at wizard start
|
|
@@ -418,7 +419,7 @@ experiences apply select --space-id <id> --environment-id <env> --session <id>
|
|
|
418
419
|
experiences apply push --space-id <id> --environment-id <env> --session <id> [--yes]
|
|
419
420
|
```
|
|
420
421
|
|
|
421
|
-
Shared flags: `--components`, `--tokens`, `--session`, `--space-id`, `--environment-id`, `--cma-token`, `--host`, `--viewports`. `apply preview` adds `--include-unchanged`. `apply select` adds `--select-all`, `--select`, `--deselect`, `--force`. `apply push` adds `--yes`, `--verbose`, `--force`, `--dry-run`.
|
|
422
|
+
Shared flags: `--components`, `--tokens`, `--session`, `--space-id`, `--environment-id`, `--cma-token`, `--host`, `--viewports`. `apply preview` adds `--include-unchanged`. `apply select` adds `--select-all`, `--select`, `--deselect`, `--force`, `--allow-deletions`. `apply push` adds `--yes`, `--verbose`, `--force`, `--dry-run`, `--allow-deletions`. By default, remote ComponentTypes and DesignTokens missing from the pushed manifest are skipped, not deleted; pass `--allow-deletions` to restore the prior delete behavior.
|
|
422
423
|
|
|
423
424
|
Design tokens are written first (component types may reference token kinds). Each entity write is recorded in the session database atomically — interrupted pushes resume from where they left off.
|
|
424
425
|
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.18.1-dev-build-
|
|
3
|
+
"version": "2.18.1-dev-build-0f85b83.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"bin/",
|
|
27
27
|
"dist/",
|
|
28
|
+
"skills/",
|
|
28
29
|
"prompts/"
|
|
29
30
|
],
|
|
30
31
|
"scripts": {
|
|
@@ -39,7 +40,6 @@
|
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"@contentful/experience-design-system-client": "workspace:*",
|
|
41
42
|
"@contentful/experience-design-system-extraction": "workspace:*",
|
|
42
|
-
"@contentful/experience-design-system-generation": "workspace:*",
|
|
43
43
|
"@contentful/experience-design-system-types": "workspace:*",
|
|
44
44
|
"commander": "^13.1.0",
|
|
45
45
|
"ink": "^4.4.1",
|
|
@@ -19,11 +19,11 @@ 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
|
|
22
|
+
import { DEFAULT_AGENT_NAME, isAgentName } from '../lib/agent-names.js';
|
|
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';
|
|
25
26
|
import { getInteractiveTerminalSupport } from '../lib/terminal-capabilities.js';
|
|
26
|
-
import { getDebugLogger } from '../lib/debug-logger.js';
|
|
27
27
|
const SCANNED_FILE_EXTENSIONS = new Set(['.astro', '.js', '.jsx', '.svelte', '.ts', '.tsx', '.vue']);
|
|
28
28
|
const IGNORED_DIRECTORY_NAMES = new Set([
|
|
29
29
|
'.git',
|
|
@@ -351,9 +351,9 @@ export function registerAnalyzeCommand(program) {
|
|
|
351
351
|
const res = await runAgent({
|
|
352
352
|
agent: resolverAgent,
|
|
353
353
|
prompt,
|
|
354
|
+
interactive: false,
|
|
354
355
|
timeoutMs: 120_000,
|
|
355
356
|
promptViaStdin: true,
|
|
356
|
-
onDebugEvent: (name, payload) => getDebugLogger().event('agent', name, payload),
|
|
357
357
|
});
|
|
358
358
|
lastAgentExitCode = res.exitCode;
|
|
359
359
|
if (res.exitCode !== 0 && res.stderr.trim()) {
|
|
@@ -2,7 +2,7 @@ import type { CompositionEdge } from './interchange-schema.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Lenient JSONL parser for the composition-mapping agent's tool-call output
|
|
4
4
|
* (spec T4). Mirrors `parseSelectToolCallLines` / `parseToolCallLines` in
|
|
5
|
-
*
|
|
5
|
+
* `src/generate/agent-runner.ts`: one JSON object per line, each with a `tool`
|
|
6
6
|
* field from a fixed allowlist. A line that fails JSON.parse or validation is
|
|
7
7
|
* DROPPED into `warnings[]` (with a reason) and parsing CONTINUES — no retry,
|
|
8
8
|
* no throw. Non-`{` prose and blank lines are skipped silently.
|
|
@@ -80,7 +80,10 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
80
80
|
if (!manifest.componentsManifest)
|
|
81
81
|
manifest.componentsManifest = {};
|
|
82
82
|
const client = new ImportApiClient({ cmaToken, spaceId, environmentId });
|
|
83
|
-
|
|
83
|
+
// This preview is display-only (component-picker annotations); it never
|
|
84
|
+
// triggers an apply, so always request the full diff to keep the
|
|
85
|
+
// existing "removed" annotation working.
|
|
86
|
+
const preview = await client.previewImport(manifest, true);
|
|
84
87
|
const annotations = {};
|
|
85
88
|
for (const item of preview.components.new) {
|
|
86
89
|
const name = item.name ?? '';
|
|
@@ -3,21 +3,19 @@ import { openPipelineDb, loadRawComponents, loadScannedFiles, createStep, update
|
|
|
3
3
|
import { hashPromptForSkill } from '../../session/cache-keys.js';
|
|
4
4
|
import { appendReviewEvent, getRefineArtifactsRoot, ensureRefineSession, getRefineSessionPaths, saveReviewState, } from '../select/persistence.js';
|
|
5
5
|
import { loadReviewInput } from '../select/parser.js';
|
|
6
|
-
import {
|
|
6
|
+
import { buildPrompt } from '../../generate/prompt-builder.js';
|
|
7
|
+
import { formatCustomPromptBanner } from '../../generate/command.js';
|
|
8
|
+
import { AGENT_NAMES, isAgentName, parseSelectToolCallLines, runAgent } from '../../generate/agent-runner.js';
|
|
7
9
|
import { access } from 'node:fs/promises';
|
|
8
10
|
import { readExperiencesCredentials } from '../../credentials-store.js';
|
|
9
11
|
import { OutputFormatter, c } from '../../output/format.js';
|
|
10
12
|
import { buildRepoContextIndex, buildSelectionContext } from './context-builder.js';
|
|
11
13
|
import { runShowRationale } from './show-rationale.js';
|
|
12
14
|
import { isAbsolute, resolve } from 'node:path';
|
|
13
|
-
import { getDebugLogger } from '../../lib/debug-logger.js';
|
|
14
15
|
import { validateExtractedComponents, shouldExcludeDueToValidation, formatExclusionWarning, } from '@contentful/experience-design-system-extraction';
|
|
15
16
|
const DEFAULT_TIMEOUT_MS = Number(process.env.EDS_AGENT_TIMEOUT_MS ?? 3 * 60 * 1000);
|
|
16
17
|
export const DEFAULT_CONCURRENCY = 10;
|
|
17
18
|
export const DEFAULT_BATCH_SIZE = 5;
|
|
18
|
-
const invoker = createLocalCliAgentInvoker({
|
|
19
|
-
onDebugEvent: (name, payload) => getDebugLogger().event('agent', name, payload),
|
|
20
|
-
});
|
|
21
19
|
function resolveBatchSize() {
|
|
22
20
|
const raw = process.env.EDS_SELECT_BATCH_SIZE;
|
|
23
21
|
if (!raw)
|
|
@@ -111,10 +109,11 @@ async function selectBatch(agent, model, batch, total, verbose, skillPathOverrid
|
|
|
111
109
|
const formatter = new OutputFormatter(verbose, (s) => {
|
|
112
110
|
outputBuf += s;
|
|
113
111
|
});
|
|
114
|
-
const result = await
|
|
112
|
+
const result = await runAgent({
|
|
115
113
|
agent,
|
|
116
114
|
model,
|
|
117
115
|
prompt,
|
|
116
|
+
interactive: false,
|
|
118
117
|
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
119
118
|
onOutput: (chunk) => formatter.push(chunk),
|
|
120
119
|
});
|
|
@@ -50,8 +50,11 @@ export declare class ImportApiClient {
|
|
|
50
50
|
private headers;
|
|
51
51
|
private requestWithRetry;
|
|
52
52
|
validateToken(): Promise<void>;
|
|
53
|
-
previewImport(manifest: ManifestPayload): Promise<ServerPreviewResponse>;
|
|
54
|
-
applyImport(manifest: ManifestPayload,
|
|
53
|
+
previewImport(manifest: ManifestPayload, allowDeletions?: boolean): Promise<ServerPreviewResponse>;
|
|
54
|
+
applyImport(manifest: ManifestPayload, options: {
|
|
55
|
+
acknowledgeBreakingChanges: boolean;
|
|
56
|
+
allowDeletions?: boolean;
|
|
57
|
+
}): Promise<ApplyOperationResponse>;
|
|
55
58
|
pollOperation(operationId: string, opts?: {
|
|
56
59
|
intervalMs?: number;
|
|
57
60
|
maxIntervalMs?: number;
|
|
@@ -268,19 +268,20 @@ export class ImportApiClient {
|
|
|
268
268
|
throw new ApiError(`unexpected error validating token: ${res.status}`, res.status, await res.text());
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
|
-
async previewImport(manifest) {
|
|
271
|
+
async previewImport(manifest, allowDeletions = false) {
|
|
272
272
|
const debug = getDebugLogger();
|
|
273
273
|
const startedAt = Date.now();
|
|
274
274
|
debug.event('apply', 'preview.request', {
|
|
275
275
|
url: `${this.base()}/design_systems/imports/preview`,
|
|
276
276
|
componentCount: manifest.components?.length ?? 0,
|
|
277
277
|
tokenCount: manifest.designTokens?.length ?? 0,
|
|
278
|
+
allowDeletions,
|
|
278
279
|
});
|
|
279
280
|
const result = await this.requestWithRetry('preview', PREVIEW_ERROR_PREFIX, () => designSystemImportSourcelessPreview({
|
|
280
281
|
baseUrl: this.host,
|
|
281
282
|
headers: this.headers(),
|
|
282
283
|
path: { spaceId: this.spaceId, environmentId: this.environmentId },
|
|
283
|
-
body: manifest,
|
|
284
|
+
body: { ...manifest, allowDeletions },
|
|
284
285
|
parseAs: 'json',
|
|
285
286
|
}));
|
|
286
287
|
if (!result.response.ok) {
|
|
@@ -296,12 +297,14 @@ export class ImportApiClient {
|
|
|
296
297
|
debug.event('apply', 'preview.ok', { status: result.response.status, durationMs: Date.now() - startedAt });
|
|
297
298
|
return sanitizePreviewResponse(parsed);
|
|
298
299
|
}
|
|
299
|
-
async applyImport(manifest,
|
|
300
|
+
async applyImport(manifest, options) {
|
|
301
|
+
const { acknowledgeBreakingChanges, allowDeletions = false } = options;
|
|
300
302
|
const debug = getDebugLogger();
|
|
301
303
|
const startedAt = Date.now();
|
|
302
304
|
debug.event('apply', 'apply.request', {
|
|
303
305
|
url: `${this.base()}/design_systems/imports/apply`,
|
|
304
306
|
acknowledgeBreakingChanges,
|
|
307
|
+
allowDeletions,
|
|
305
308
|
});
|
|
306
309
|
let result;
|
|
307
310
|
try {
|
|
@@ -309,7 +312,7 @@ export class ImportApiClient {
|
|
|
309
312
|
baseUrl: this.host,
|
|
310
313
|
headers: this.headers(),
|
|
311
314
|
path: { spaceId: this.spaceId, environmentId: this.environmentId },
|
|
312
|
-
body: { ...manifest, acknowledgeBreakingChanges },
|
|
315
|
+
body: { ...manifest, acknowledgeBreakingChanges, allowDeletions },
|
|
313
316
|
parseAs: 'json',
|
|
314
317
|
});
|
|
315
318
|
}
|
|
@@ -12,7 +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
|
+
import { addAllowDeletionsOption, addArtifactInputOptions, addCompositionOptions, addContentfulTargetOptions, addSelectionOptions, } from '../lib/command-options.js';
|
|
16
16
|
import { stripAllowedComponents } from '../import/strip-allowed-components.js';
|
|
17
17
|
import { readExperiencesCredentials } from '../credentials-store.js';
|
|
18
18
|
import { getInteractiveTerminalSupport, requireInteractiveTerminal } from '../lib/terminal-capabilities.js';
|
|
@@ -457,6 +457,7 @@ export function registerApplyCommand(program) {
|
|
|
457
457
|
preview,
|
|
458
458
|
spaceId,
|
|
459
459
|
environmentId,
|
|
460
|
+
allowDeletions: false,
|
|
460
461
|
}));
|
|
461
462
|
await waitUntilExit();
|
|
462
463
|
}
|
|
@@ -469,6 +470,7 @@ export function registerApplyCommand(program) {
|
|
|
469
470
|
addArtifactInputOptions(pushCmd);
|
|
470
471
|
addContentfulTargetOptions(pushCmd);
|
|
471
472
|
addCompositionOptions(pushCmd);
|
|
473
|
+
addAllowDeletionsOption(pushCmd);
|
|
472
474
|
pushCmd
|
|
473
475
|
.option('--yes', 'Skip interactive confirmation')
|
|
474
476
|
.option('--verbose', 'Show all entity progress including skipped/unchanged')
|
|
@@ -502,7 +504,7 @@ export function registerApplyCommand(program) {
|
|
|
502
504
|
const manifest = buildManifest(components, tokens);
|
|
503
505
|
let preview;
|
|
504
506
|
try {
|
|
505
|
-
preview = await client.previewImport(manifest);
|
|
507
|
+
preview = await client.previewImport(manifest, opts.allowDeletions === true);
|
|
506
508
|
}
|
|
507
509
|
catch (e) {
|
|
508
510
|
if (e instanceof ApiError)
|
|
@@ -517,6 +519,7 @@ export function registerApplyCommand(program) {
|
|
|
517
519
|
preview,
|
|
518
520
|
spaceId,
|
|
519
521
|
environmentId,
|
|
522
|
+
allowDeletions: opts.allowDeletions === true,
|
|
520
523
|
}));
|
|
521
524
|
await waitUntilExit();
|
|
522
525
|
}
|
|
@@ -547,7 +550,10 @@ export function registerApplyCommand(program) {
|
|
|
547
550
|
}
|
|
548
551
|
let operation;
|
|
549
552
|
try {
|
|
550
|
-
operation = await client.applyImport(manifest,
|
|
553
|
+
operation = await client.applyImport(manifest, {
|
|
554
|
+
acknowledgeBreakingChanges: breakingWithImpact || opts.force === true,
|
|
555
|
+
allowDeletions: opts.allowDeletions === true,
|
|
556
|
+
});
|
|
551
557
|
}
|
|
552
558
|
catch (e) {
|
|
553
559
|
if (e instanceof ApiError)
|
|
@@ -569,7 +575,7 @@ export function registerApplyCommand(program) {
|
|
|
569
575
|
return;
|
|
570
576
|
}
|
|
571
577
|
await new Promise((resolvePromise) => {
|
|
572
|
-
const runApply = async (acknowledge) => {
|
|
578
|
+
const runApply = async (acknowledge, applyDeletions) => {
|
|
573
579
|
instance.rerender(createElement(ServerApplyProgress, {
|
|
574
580
|
spaceId,
|
|
575
581
|
environmentId,
|
|
@@ -577,7 +583,10 @@ export function registerApplyCommand(program) {
|
|
|
577
583
|
}));
|
|
578
584
|
let operation;
|
|
579
585
|
try {
|
|
580
|
-
operation = await client.applyImport(manifest,
|
|
586
|
+
operation = await client.applyImport(manifest, {
|
|
587
|
+
acknowledgeBreakingChanges: acknowledge,
|
|
588
|
+
allowDeletions: applyDeletions,
|
|
589
|
+
});
|
|
581
590
|
}
|
|
582
591
|
catch (e) {
|
|
583
592
|
if (e instanceof ApiError) {
|
|
@@ -625,8 +634,9 @@ export function registerApplyCommand(program) {
|
|
|
625
634
|
spaceId,
|
|
626
635
|
environmentId,
|
|
627
636
|
breakingWithImpact,
|
|
628
|
-
|
|
629
|
-
|
|
637
|
+
allowDeletions: opts.allowDeletions === true,
|
|
638
|
+
onConfirm: (acknowledge, applyDeletions) => {
|
|
639
|
+
void runApply(acknowledge, applyDeletions);
|
|
630
640
|
},
|
|
631
641
|
onCancel: () => {
|
|
632
642
|
process.exit(0);
|
|
@@ -640,6 +650,7 @@ export function registerApplyCommand(program) {
|
|
|
640
650
|
addContentfulTargetOptions(selectCmd);
|
|
641
651
|
addCompositionOptions(selectCmd);
|
|
642
652
|
addSelectionOptions(selectCmd);
|
|
653
|
+
addAllowDeletionsOption(selectCmd);
|
|
643
654
|
selectCmd.option('--force', 'Skip confirmation for breaking changes').action(async (opts) => {
|
|
644
655
|
const nonInteractive = opts.selectAll || (opts.select ?? []).length > 0 || (opts.deselect ?? []).length > 0;
|
|
645
656
|
if (!nonInteractive) {
|
|
@@ -669,7 +680,7 @@ export function registerApplyCommand(program) {
|
|
|
669
680
|
const fullManifest = buildManifest(components, tokens);
|
|
670
681
|
let preview;
|
|
671
682
|
try {
|
|
672
|
-
preview = await client.previewImport(fullManifest);
|
|
683
|
+
preview = await client.previewImport(fullManifest, opts.allowDeletions === true);
|
|
673
684
|
}
|
|
674
685
|
catch (e) {
|
|
675
686
|
if (e instanceof ApiError)
|
|
@@ -707,7 +718,10 @@ export function registerApplyCommand(program) {
|
|
|
707
718
|
}
|
|
708
719
|
let operation;
|
|
709
720
|
try {
|
|
710
|
-
operation = await client.applyImport(filteredManifest,
|
|
721
|
+
operation = await client.applyImport(filteredManifest, {
|
|
722
|
+
acknowledgeBreakingChanges: hasBreaking || opts.force === true,
|
|
723
|
+
allowDeletions: opts.allowDeletions === true,
|
|
724
|
+
});
|
|
711
725
|
}
|
|
712
726
|
catch (e) {
|
|
713
727
|
if (e instanceof ApiError)
|
|
@@ -749,7 +763,10 @@ export function registerApplyCommand(program) {
|
|
|
749
763
|
}));
|
|
750
764
|
let operation;
|
|
751
765
|
try {
|
|
752
|
-
operation = await client.applyImport(filteredManifest,
|
|
766
|
+
operation = await client.applyImport(filteredManifest, {
|
|
767
|
+
acknowledgeBreakingChanges: hasBreaking,
|
|
768
|
+
allowDeletions: opts.allowDeletions === true,
|
|
769
|
+
});
|
|
753
770
|
}
|
|
754
771
|
catch (e) {
|
|
755
772
|
if (e instanceof ApiError) {
|
|
@@ -7,6 +7,10 @@ import type { ServerPreviewResponse } from '@contentful/experience-design-system
|
|
|
7
7
|
* would be a pure no-op (INTEG-4411 refined guard).
|
|
8
8
|
*
|
|
9
9
|
* A push that produces ANY entry in ANY bucket — including a rejection that
|
|
10
|
-
* removes a server-side component — is NOT empty.
|
|
10
|
+
* removes a server-side component — is NOT empty. Note the server hides
|
|
11
|
+
* removals entirely (empty removed[]) unless the preview was fetched with
|
|
12
|
+
* allowDeletions: true, so "empty" here also correctly describes a preview
|
|
13
|
+
* with pending-but-hidden deletions: pushing without --allow-deletions is a
|
|
14
|
+
* genuine no-op in that case too, since the apply gate hides them the same way.
|
|
11
15
|
*/
|
|
12
16
|
export declare function isEmptyPreview(preview: ServerPreviewResponse): boolean;
|
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
* would be a pure no-op (INTEG-4411 refined guard).
|
|
7
7
|
*
|
|
8
8
|
* A push that produces ANY entry in ANY bucket — including a rejection that
|
|
9
|
-
* removes a server-side component — is NOT empty.
|
|
9
|
+
* removes a server-side component — is NOT empty. Note the server hides
|
|
10
|
+
* removals entirely (empty removed[]) unless the preview was fetched with
|
|
11
|
+
* allowDeletions: true, so "empty" here also correctly describes a preview
|
|
12
|
+
* with pending-but-hidden deletions: pushing without --allow-deletions is a
|
|
13
|
+
* genuine no-op in that case too, since the apply gate hides them the same way.
|
|
10
14
|
*/
|
|
11
15
|
export function isEmptyPreview(preview) {
|
|
12
16
|
const { components, tokens, taxonomies } = preview;
|
|
@@ -5,16 +5,19 @@ interface ServerPreviewConfirmProps {
|
|
|
5
5
|
spaceId: string;
|
|
6
6
|
environmentId: string;
|
|
7
7
|
breakingWithImpact: boolean;
|
|
8
|
-
|
|
8
|
+
/** The value the preview was actually fetched with. */
|
|
9
|
+
allowDeletions: boolean;
|
|
10
|
+
onConfirm: (acknowledge: boolean, allowDeletions: boolean) => void;
|
|
9
11
|
onCancel: () => void;
|
|
10
12
|
}
|
|
11
|
-
export declare function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, onConfirm, onCancel, }: ServerPreviewConfirmProps): React.ReactElement;
|
|
13
|
+
export declare function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, allowDeletions: fetchedAllowDeletions, onConfirm, onCancel, }: ServerPreviewConfirmProps): React.ReactElement;
|
|
12
14
|
interface ServerPreviewAppProps {
|
|
13
15
|
preview: ServerPreviewResponse;
|
|
14
16
|
spaceId: string;
|
|
15
17
|
environmentId: string;
|
|
18
|
+
allowDeletions: boolean;
|
|
16
19
|
}
|
|
17
|
-
export declare function ServerPreviewApp({ preview, spaceId, environmentId }: ServerPreviewAppProps): React.ReactElement;
|
|
20
|
+
export declare function ServerPreviewApp({ preview, spaceId, environmentId, allowDeletions, }: ServerPreviewAppProps): React.ReactElement;
|
|
18
21
|
interface ServerApplyProgressProps {
|
|
19
22
|
spaceId: string;
|
|
20
23
|
environmentId: string;
|
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { Box, Text, useInput } from 'ink';
|
|
3
4
|
import { ServerPreviewView } from './ServerPreviewView.js';
|
|
4
5
|
import { buildPostPushUrl } from '../../lib/contentful-urls.js';
|
|
5
6
|
import { formatEdsiError } from '../../lib/error-parser.js';
|
|
6
|
-
export function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, onConfirm, onCancel, }) {
|
|
7
|
+
export function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, allowDeletions: fetchedAllowDeletions, onConfirm, onCancel, }) {
|
|
8
|
+
const [allowDeletions, setAllowDeletions] = useState(fetchedAllowDeletions);
|
|
9
|
+
const removedCount = preview.components.removed.length + preview.tokens.removed.length;
|
|
7
10
|
useInput((input, key) => {
|
|
8
|
-
if (key.return)
|
|
9
|
-
onConfirm(breakingWithImpact);
|
|
11
|
+
if (key.return) {
|
|
12
|
+
onConfirm(breakingWithImpact, allowDeletions);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if ((input === 'x' || input === 'X') && fetchedAllowDeletions && removedCount > 0) {
|
|
16
|
+
setAllowDeletions((prev) => !prev);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
10
19
|
if (key.escape || input === 'q')
|
|
11
20
|
onCancel();
|
|
12
21
|
});
|
|
13
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId }), _jsxs(Box, { paddingX: 2, flexDirection: "column", children: [breakingWithImpact && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 Breaking changes will affect downstream entities. Press Enter to acknowledge and apply."] })), _jsxs(Text, { children: [' ', "Press ", _jsx(Text, { bold: true, children: "Enter" }), " to apply, ", _jsx(Text, { bold: true, children: "Esc" }), " to cancel"] })] })] }));
|
|
22
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId, allowDeletions: allowDeletions }), _jsxs(Box, { paddingX: 2, flexDirection: "column", children: [breakingWithImpact && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 Breaking changes will affect downstream entities. Press Enter to acknowledge and apply."] })), allowDeletions && removedCount > 0 && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 ", removedCount, " missing ", removedCount === 1 ? 'entity' : 'entities', " will be permanently deleted. Press Enter to confirm."] })), fetchedAllowDeletions && removedCount > 0 && (_jsxs(Text, { dimColor: true, children: [' ', "[x] ", allowDeletions ? '[✓]' : '[ ]', " Also delete ", removedCount, " ", removedCount === 1 ? 'entity' : 'entities'] })), _jsxs(Text, { children: [' ', "Press ", _jsx(Text, { bold: true, children: "Enter" }), " to apply, ", _jsx(Text, { bold: true, children: "Esc" }), " to cancel"] })] })] }));
|
|
14
23
|
}
|
|
15
|
-
export function ServerPreviewApp({ preview, spaceId, environmentId }) {
|
|
24
|
+
export function ServerPreviewApp({ preview, spaceId, environmentId, allowDeletions, }) {
|
|
16
25
|
useInput((input, key) => {
|
|
17
26
|
if (key.escape || input === 'q')
|
|
18
27
|
process.exit(0);
|
|
19
28
|
});
|
|
20
|
-
return _jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId });
|
|
29
|
+
return (_jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId, allowDeletions: allowDeletions }));
|
|
21
30
|
}
|
|
22
31
|
export function ServerApplyProgress({ spaceId, environmentId, status, operationId, error, }) {
|
|
23
32
|
useInput((_input, key) => {
|
|
@@ -4,6 +4,7 @@ interface ServerPreviewViewProps {
|
|
|
4
4
|
preview: ServerPreviewResponse;
|
|
5
5
|
spaceId: string;
|
|
6
6
|
environmentId: string;
|
|
7
|
+
allowDeletions: boolean;
|
|
7
8
|
}
|
|
8
|
-
export declare function ServerPreviewView({ preview, spaceId, environmentId }: ServerPreviewViewProps): React.ReactElement;
|
|
9
|
+
export declare function ServerPreviewView({ preview, spaceId, environmentId, allowDeletions, }: ServerPreviewViewProps): React.ReactElement;
|
|
9
10
|
export {};
|
|
@@ -13,11 +13,11 @@ function DraftWarning({ hasDraft }) {
|
|
|
13
13
|
return null;
|
|
14
14
|
return _jsx(Text, { color: "yellow", children: " \u26A1 has pending draft changes" });
|
|
15
15
|
}
|
|
16
|
-
export function ServerPreviewView({ preview, spaceId, environmentId }) {
|
|
16
|
+
export function ServerPreviewView({ preview, spaceId, environmentId, allowDeletions, }) {
|
|
17
17
|
const { components, tokens } = preview;
|
|
18
18
|
const totalComponents = components.new.length + components.changed.length + components.unchanged.length + components.removed.length;
|
|
19
19
|
const totalTokens = tokens.new.length + tokens.changed.length + tokens.unchanged.length + tokens.removed.length;
|
|
20
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Text, { bold: true, children: ["Preview \u2014 ", environmentId, " @ ", spaceId] }), _jsx(Text, { children: " " }), totalComponents > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [" Component Types (", totalComponents, " total)"] }), _jsxs(Text, { color: "green", children: [" \u2746 ", components.new.length, " to create"] }), _jsxs(Text, { color: "yellow", children: [" ~ ", components.changed.length, " to update"] }), _jsxs(Text, { color:
|
|
20
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Text, { bold: true, children: ["Preview \u2014 ", environmentId, " @ ", spaceId] }), _jsx(Text, { children: " " }), totalComponents > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [" Component Types (", totalComponents, " total)"] }), _jsxs(Text, { color: "green", children: [" \u2746 ", components.new.length, " to create"] }), _jsxs(Text, { color: "yellow", children: [" ~ ", components.changed.length, " to update"] }), _jsxs(Text, { color: allowDeletions ? 'red' : 'yellow', children: [' ', allowDeletions ? '✗' : '⊘', " ", components.removed.length, " to ", allowDeletions ? 'delete' : 'skip'] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 ", components.unchanged.length, " unchanged"] }), components.changed.map((item, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: item.hasPendingDraftChanges })] }), _jsx(BreakingBadge, { item: item })] }, i))), _jsx(Text, { children: " " })] })), totalTokens > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [" Design Tokens (", totalTokens, " total)"] }), _jsxs(Text, { color: "green", children: [" \u2746 ", tokens.new.length, " to create"] }), _jsxs(Text, { color: "yellow", children: [" ~ ", tokens.changed.length, " to update"] }), _jsxs(Text, { color: allowDeletions ? 'red' : 'yellow', children: [' ', allowDeletions ? '✗' : '⊘', " ", tokens.removed.length, " to ", allowDeletions ? 'delete' : 'skip'] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 ", tokens.unchanged.length, " unchanged"] }), tokens.changed
|
|
21
21
|
.filter((t) => t.hasPendingDraftChanges)
|
|
22
|
-
.map((item, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: true })] }, i))), _jsx(Text, { children: " " })] })), _jsx(Text, { dimColor: true, children: " Press Q to exit." })] }));
|
|
22
|
+
.map((item, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: true })] }, i))), _jsx(Text, { children: " " })] })), !allowDeletions && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: " \u2298 Deletions are hidden by default \u2014 rerun with --allow-deletions to see them." }) })), _jsx(Text, { dimColor: true, children: " Press Q to exit." })] }));
|
|
23
23
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { AgentName } from '../lib/agent-names.js';
|
|
2
|
+
export { AGENT_NAMES, isAgentName, type AgentName } from '../lib/agent-names.js';
|
|
3
|
+
export interface AgentRunResult {
|
|
4
|
+
exitCode: number;
|
|
5
|
+
stdout: string;
|
|
6
|
+
stderr: string;
|
|
7
|
+
timedOut: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ClassifyPropCall {
|
|
10
|
+
tool: 'classify_prop';
|
|
11
|
+
prop: string;
|
|
12
|
+
cdf_type: string;
|
|
13
|
+
cdf_category: 'content' | 'design' | 'state';
|
|
14
|
+
values?: string[];
|
|
15
|
+
token_kind?: string;
|
|
16
|
+
required?: boolean;
|
|
17
|
+
description?: string;
|
|
18
|
+
default?: string | boolean;
|
|
19
|
+
/** Internal LLM rationale; not customer-facing. Persisted to raw_props.rationale. */
|
|
20
|
+
reason?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ExcludePropCall {
|
|
23
|
+
tool: 'exclude_prop';
|
|
24
|
+
prop: string;
|
|
25
|
+
reason: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ClassifyComponentCall {
|
|
28
|
+
tool: 'classify_component';
|
|
29
|
+
description?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Component-level rationale strings. Surfaced by the `I` ComponentRationalePanel.
|
|
32
|
+
* Each field is optional; missing fields leave existing DB values untouched
|
|
33
|
+
* (sparse update semantics in applyToolCalls).
|
|
34
|
+
*/
|
|
35
|
+
rationale?: {
|
|
36
|
+
description?: string;
|
|
37
|
+
props?: string;
|
|
38
|
+
slots?: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export interface ClassifySlotCall {
|
|
42
|
+
tool: 'classify_slot';
|
|
43
|
+
slot: string;
|
|
44
|
+
required?: boolean;
|
|
45
|
+
allowed_components?: string[];
|
|
46
|
+
description?: string;
|
|
47
|
+
/** Per-slot rationale; persisted to raw_slots.rationale. */
|
|
48
|
+
rationale?: string;
|
|
49
|
+
}
|
|
50
|
+
export type ToolCall = ClassifyPropCall | ExcludePropCall | ClassifyComponentCall | ClassifySlotCall;
|
|
51
|
+
export interface SelectComponentCall {
|
|
52
|
+
tool: 'select_component';
|
|
53
|
+
name: string;
|
|
54
|
+
reason?: string;
|
|
55
|
+
confidence?: number;
|
|
56
|
+
}
|
|
57
|
+
export interface RejectComponentCall {
|
|
58
|
+
tool: 'reject_component';
|
|
59
|
+
name: string;
|
|
60
|
+
reason?: string;
|
|
61
|
+
confidence?: number;
|
|
62
|
+
}
|
|
63
|
+
export type SelectToolCall = SelectComponentCall | RejectComponentCall;
|
|
64
|
+
export interface ParsedSelectToolCalls {
|
|
65
|
+
calls: SelectToolCall[];
|
|
66
|
+
warnings: string[];
|
|
67
|
+
}
|
|
68
|
+
export declare function parseSelectToolCallLines(stdout: string): ParsedSelectToolCalls;
|
|
69
|
+
export interface SetTokenCall {
|
|
70
|
+
tool: 'set_token';
|
|
71
|
+
path: string;
|
|
72
|
+
type: string;
|
|
73
|
+
value: unknown;
|
|
74
|
+
description?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface SetGroupCall {
|
|
77
|
+
tool: 'set_group';
|
|
78
|
+
path: string;
|
|
79
|
+
description?: string;
|
|
80
|
+
}
|
|
81
|
+
export type TokenToolCall = SetTokenCall | SetGroupCall;
|
|
82
|
+
export interface ParsedTokenToolCalls {
|
|
83
|
+
calls: TokenToolCall[];
|
|
84
|
+
warnings: string[];
|
|
85
|
+
}
|
|
86
|
+
export interface ParsedToolCalls {
|
|
87
|
+
calls: ToolCall[];
|
|
88
|
+
warnings: string[];
|
|
89
|
+
}
|
|
90
|
+
export declare function parseToolCallLines(stdout: string): ParsedToolCalls;
|
|
91
|
+
export declare function parseTokenToolCallLines(stdout: string): ParsedTokenToolCalls;
|
|
92
|
+
export declare function resolveBinary(agent: AgentName): string;
|
|
93
|
+
/**
|
|
94
|
+
* Resolve the model for an agent. Explicit flag/creds value wins, then a
|
|
95
|
+
* per-agent `EDS_AGENT_MODEL_<AGENT>` env override (mirrors the
|
|
96
|
+
* `EDS_AGENT_BINARY_<AGENT>` pattern), otherwise the lightweight default for
|
|
97
|
+
* that agent.
|
|
98
|
+
*/
|
|
99
|
+
export declare function resolveAgentModel(agent: AgentName, explicit?: string): string;
|
|
100
|
+
export declare function buildArgs(agent: AgentName, prompt: string, model?: string, promptViaStdin?: boolean): string[];
|
|
101
|
+
export declare function runAgent(options: {
|
|
102
|
+
agent: AgentName;
|
|
103
|
+
prompt: string;
|
|
104
|
+
interactive: boolean;
|
|
105
|
+
timeoutMs: number;
|
|
106
|
+
model?: string;
|
|
107
|
+
onOutput?: (chunk: string) => void;
|
|
108
|
+
/**
|
|
109
|
+
* Deliver the prompt on stdin instead of as an argv positional. Required for
|
|
110
|
+
* large prompts (e.g. the composition resolver inlining candidate files),
|
|
111
|
+
* which overflow ARG_MAX when passed as an argument. Non-interactive only.
|
|
112
|
+
*/
|
|
113
|
+
promptViaStdin?: boolean;
|
|
114
|
+
}): Promise<AgentRunResult>;
|
|
115
|
+
export type AgentAuthStatus = 'ok' | 'unauthenticated' | 'not-found';
|
|
116
|
+
export declare function checkAgentAuth(agent: AgentName): Promise<AgentAuthStatus>;
|
|
117
|
+
/**
|
|
118
|
+
* Build a diagnostic string from a failed agent run, surfacing the agent's own
|
|
119
|
+
* stderr (or stdout, when stderr is empty) so callers never emit a context-free
|
|
120
|
+
* "agent failed". Callers only invoke this on a failed run: either a non-zero
|
|
121
|
+
* exit, or a zero exit that produced no tool calls. A non-zero exit is reported
|
|
122
|
+
* as such; a zero exit is therefore the "produced no tool calls" case.
|
|
123
|
+
*/
|
|
124
|
+
export declare function describeAgentFailure(result: AgentRunResult, maxDetail?: number): string;
|
|
125
|
+
export declare function extractSentinelOutput(stdout: string): string | null | 'multiple';
|