@contentful/experience-design-system-cli 2.12.0 → 2.12.1-dev-build-bae15c7.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 +214 -357
- package/dist/package.json +1 -1
- package/dist/src/analyze/extract/astro.js +9 -0
- package/dist/src/analyze/extract/react.js +23 -0
- package/dist/src/analyze/extract/stencil.js +3 -0
- package/dist/src/analyze/extract/vue.js +6 -0
- package/dist/src/analyze/extract/web-components.js +10 -0
- package/dist/src/analyze/pre-classify.d.ts +1 -1
- package/dist/src/analyze/pre-classify.js +3 -3
- package/dist/src/analyze/select/persistence.d.ts +13 -0
- package/dist/src/analyze/select/persistence.js +38 -0
- package/dist/src/analyze/select/preview-annotations.d.ts +26 -0
- package/dist/src/analyze/select/preview-annotations.js +62 -0
- package/dist/src/analyze/select/tui/App.js +5 -2
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.d.ts +30 -0
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +137 -0
- package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +58 -1
- package/dist/src/analyze/select/tui/components/FieldEditor.js +637 -161
- package/dist/src/analyze/select/tui/components/FinalizeDialog.js +1 -1
- package/dist/src/analyze/select/tui/components/JsonPanel.js +5 -1
- package/dist/src/analyze/select/tui/components/RationalePanel.d.ts +34 -0
- package/dist/src/analyze/select/tui/components/RationalePanel.js +92 -0
- package/dist/src/analyze/select/tui/components/Sidebar.d.ts +13 -1
- package/dist/src/analyze/select/tui/components/Sidebar.js +27 -3
- package/dist/src/analyze/select/tui/hooks/scroll-offset.d.ts +29 -0
- package/dist/src/analyze/select/tui/hooks/scroll-offset.js +26 -0
- package/dist/src/analyze/select-agent/command.d.ts +2 -0
- package/dist/src/analyze/select-agent/command.js +260 -45
- package/dist/src/analyze/select-agent/show-rationale.d.ts +35 -0
- package/dist/src/analyze/select-agent/show-rationale.js +92 -0
- package/dist/src/apply/command.js +7 -3
- package/dist/src/apply/tui/ServerApplyView.d.ts +3 -1
- package/dist/src/apply/tui/ServerApplyView.js +3 -2
- package/dist/src/credentials-store.d.ts +5 -0
- package/dist/src/credentials-store.js +7 -1
- package/dist/src/generate/agent-runner.d.ts +14 -0
- package/dist/src/generate/agent-runner.js +16 -0
- package/dist/src/generate/command.d.ts +6 -0
- package/dist/src/generate/command.js +45 -10
- 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 +8 -0
- package/dist/src/generate/prompt-builder.js +22 -11
- package/dist/src/import/agent-model-resolve.d.ts +23 -0
- package/dist/src/import/agent-model-resolve.js +35 -0
- package/dist/src/import/auto-filter-resolve.d.ts +12 -0
- package/dist/src/import/auto-filter-resolve.js +16 -0
- package/dist/src/import/command.js +248 -8
- package/dist/src/import/orchestrator.d.ts +2 -0
- package/dist/src/import/orchestrator.js +11 -3
- package/dist/src/import/print-prompt.d.ts +35 -0
- package/dist/src/import/print-prompt.js +31 -0
- package/dist/src/import/tui/CustomPromptBanner.d.ts +11 -0
- package/dist/src/import/tui/CustomPromptBanner.js +7 -0
- package/dist/src/import/tui/WizardApp.d.ts +112 -8
- package/dist/src/import/tui/WizardApp.js +820 -333
- package/dist/src/import/tui/auto-filter-error.d.ts +2 -0
- package/dist/src/import/tui/auto-filter-error.js +29 -0
- package/dist/src/import/tui/final-review-host.d.ts +15 -0
- package/dist/src/import/tui/final-review-host.js +20 -0
- package/dist/src/import/tui/merge-ai-decisions.d.ts +5 -0
- package/dist/src/import/tui/merge-ai-decisions.js +22 -0
- package/dist/src/import/tui/push-decision-gate-helpers.d.ts +8 -0
- package/dist/src/import/tui/push-decision-gate-helpers.js +8 -0
- package/dist/src/import/tui/push-progress.d.ts +23 -0
- package/dist/src/import/tui/push-progress.js +14 -0
- package/dist/src/import/tui/run-print-files-helpers.d.ts +26 -0
- package/dist/src/import/tui/run-print-files-helpers.js +8 -0
- package/dist/src/import/tui/run-teaser.d.ts +5 -0
- package/dist/src/import/tui/run-teaser.js +9 -0
- package/dist/src/import/tui/runLivePreview.d.ts +35 -0
- package/dist/src/import/tui/runLivePreview.js +73 -0
- package/dist/src/import/tui/runScopeGate.d.ts +22 -0
- package/dist/src/import/tui/runScopeGate.js +28 -0
- package/dist/src/import/tui/scope-gate-host.d.ts +21 -0
- package/dist/src/import/tui/scope-gate-host.js +20 -0
- package/dist/src/import/tui/spawn-generate.d.ts +33 -0
- package/dist/src/import/tui/spawn-generate.js +53 -0
- package/dist/src/import/tui/steps/CredentialsStep.d.ts +25 -1
- package/dist/src/import/tui/steps/CredentialsStep.js +31 -3
- package/dist/src/import/tui/steps/DoneStep.d.ts +6 -1
- package/dist/src/import/tui/steps/DoneStep.js +10 -4
- package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +29 -2
- package/dist/src/import/tui/steps/GenerateReviewStep.js +412 -48
- package/dist/src/import/tui/steps/PushDecisionGateStep.d.ts +19 -0
- package/dist/src/import/tui/steps/PushDecisionGateStep.js +76 -0
- package/dist/src/import/tui/steps/PushingStep.d.ts +10 -0
- package/dist/src/import/tui/steps/PushingStep.js +40 -0
- package/dist/src/import/tui/steps/ScopeGateStep.d.ts +23 -0
- package/dist/src/import/tui/steps/ScopeGateStep.js +250 -0
- package/dist/src/import/tui/steps/TokenInputStep.js +20 -3
- package/dist/src/import/tui/useLivePreview.d.ts +35 -0
- package/dist/src/import/tui/useLivePreview.js +120 -0
- package/dist/src/import/tui/wizard-generate-progress.d.ts +21 -0
- package/dist/src/import/tui/wizard-generate-progress.js +25 -0
- package/dist/src/import/tui/wizard-save-flow.d.ts +37 -0
- package/dist/src/import/tui/wizard-save-flow.js +20 -0
- package/dist/src/import/tui/wizard-state-transitions.d.ts +80 -0
- package/dist/src/import/tui/wizard-state-transitions.js +79 -0
- package/dist/src/lib/contentful-urls.d.ts +34 -0
- package/dist/src/lib/contentful-urls.js +41 -0
- package/dist/src/program.js +2 -0
- package/dist/src/runs/export-helpers.d.ts +30 -0
- package/dist/src/runs/export-helpers.js +59 -0
- package/dist/src/runs/fingerprint.d.ts +47 -0
- package/dist/src/runs/fingerprint.js +75 -0
- package/dist/src/runs/ls-command.d.ts +12 -0
- package/dist/src/runs/ls-command.js +137 -0
- package/dist/src/runs/modify-launcher.d.ts +24 -0
- package/dist/src/runs/modify-launcher.js +36 -0
- package/dist/src/runs/path-prompt.d.ts +8 -0
- package/dist/src/runs/path-prompt.js +72 -0
- package/dist/src/runs/push-creds-prompt.d.ts +25 -0
- package/dist/src/runs/push-creds-prompt.js +39 -0
- package/dist/src/runs/push-helpers.d.ts +29 -0
- package/dist/src/runs/push-helpers.js +69 -0
- package/dist/src/runs/replay-helpers.d.ts +67 -0
- package/dist/src/runs/replay-helpers.js +148 -0
- package/dist/src/runs/resolve-run-target.d.ts +12 -0
- package/dist/src/runs/resolve-run-target.js +45 -0
- package/dist/src/runs/run-picker-mount.d.ts +46 -0
- package/dist/src/runs/run-picker-mount.js +72 -0
- package/dist/src/runs/run-picker.d.ts +17 -0
- package/dist/src/runs/run-picker.js +145 -0
- package/dist/src/runs/save-conflict.d.ts +8 -0
- package/dist/src/runs/save-conflict.js +56 -0
- package/dist/src/runs/save-path-resolver.d.ts +55 -0
- package/dist/src/runs/save-path-resolver.js +71 -0
- package/dist/src/runs/staleness.d.ts +38 -0
- package/dist/src/runs/staleness.js +140 -0
- package/dist/src/runs/store.d.ts +78 -0
- package/dist/src/runs/store.js +136 -0
- package/dist/src/session/cache-keys.d.ts +15 -0
- package/dist/src/session/cache-keys.js +38 -0
- package/dist/src/session/db.d.ts +89 -3
- package/dist/src/session/db.js +447 -104
- package/dist/src/setup/auto-filter-prompt.d.ts +13 -0
- package/dist/src/setup/auto-filter-prompt.js +24 -0
- package/dist/src/setup/command.d.ts +7 -0
- package/dist/src/setup/command.js +58 -2
- package/dist/src/types.d.ts +6 -0
- package/package.json +2 -2
- package/skills/generate-components.md +54 -6
- package/skills/select-components.md +13 -4
|
@@ -16,6 +16,9 @@ export async function readExperiencesCredentials() {
|
|
|
16
16
|
...(host ? { host } : {}),
|
|
17
17
|
...(parsed.agent ? { agent: parsed.agent } : {}),
|
|
18
18
|
...(parsed.agentModel ? { agentModel: parsed.agentModel } : {}),
|
|
19
|
+
...(parsed.selectPromptPath ? { selectPromptPath: parsed.selectPromptPath } : {}),
|
|
20
|
+
...(parsed.generatePromptPath ? { generatePromptPath: parsed.generatePromptPath } : {}),
|
|
21
|
+
...(typeof parsed.autoFilter === 'boolean' ? { autoFilter: parsed.autoFilter } : {}),
|
|
19
22
|
};
|
|
20
23
|
}
|
|
21
24
|
catch {
|
|
@@ -29,7 +32,7 @@ export async function readExperiencesCredentials() {
|
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
34
|
export async function writeExperiencesCredentials(creds) {
|
|
32
|
-
const { host: _host, agent, agentModel, ...rest } = creds;
|
|
35
|
+
const { host: _host, agent, agentModel, selectPromptPath, generatePromptPath, autoFilter, ...rest } = creds;
|
|
33
36
|
const host = toConfiguredHost(creds.host);
|
|
34
37
|
await mkdir(CREDENTIALS_DIR, { recursive: true });
|
|
35
38
|
await writeFile(CREDENTIALS_PATH, JSON.stringify({
|
|
@@ -37,6 +40,9 @@ export async function writeExperiencesCredentials(creds) {
|
|
|
37
40
|
...(host ? { host } : {}),
|
|
38
41
|
...(agent ? { agent } : {}),
|
|
39
42
|
...(agentModel ? { agentModel } : {}),
|
|
43
|
+
...(selectPromptPath ? { selectPromptPath } : {}),
|
|
44
|
+
...(generatePromptPath ? { generatePromptPath } : {}),
|
|
45
|
+
...(typeof autoFilter === 'boolean' ? { autoFilter } : {}),
|
|
40
46
|
}, null, 2) + '\n', { mode: 0o600 });
|
|
41
47
|
}
|
|
42
48
|
export function experiencesCredentialsPath() {
|
|
@@ -15,6 +15,8 @@ export interface ClassifyPropCall {
|
|
|
15
15
|
required?: boolean;
|
|
16
16
|
description?: string;
|
|
17
17
|
default?: string | boolean;
|
|
18
|
+
/** Internal LLM rationale; not customer-facing. Persisted to raw_props.rationale. */
|
|
19
|
+
reason?: string;
|
|
18
20
|
}
|
|
19
21
|
export interface ExcludePropCall {
|
|
20
22
|
tool: 'exclude_prop';
|
|
@@ -24,6 +26,16 @@ export interface ExcludePropCall {
|
|
|
24
26
|
export interface ClassifyComponentCall {
|
|
25
27
|
tool: 'classify_component';
|
|
26
28
|
description?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Component-level rationale strings. Surfaced by the `I` ComponentRationalePanel.
|
|
31
|
+
* Each field is optional; missing fields leave existing DB values untouched
|
|
32
|
+
* (sparse update semantics in applyToolCalls).
|
|
33
|
+
*/
|
|
34
|
+
rationale?: {
|
|
35
|
+
description?: string;
|
|
36
|
+
props?: string;
|
|
37
|
+
slots?: string;
|
|
38
|
+
};
|
|
27
39
|
}
|
|
28
40
|
export interface ClassifySlotCall {
|
|
29
41
|
tool: 'classify_slot';
|
|
@@ -31,6 +43,8 @@ export interface ClassifySlotCall {
|
|
|
31
43
|
required?: boolean;
|
|
32
44
|
allowed_components?: string[];
|
|
33
45
|
description?: string;
|
|
46
|
+
/** Per-slot rationale; persisted to raw_slots.rationale. */
|
|
47
|
+
rationale?: string;
|
|
34
48
|
}
|
|
35
49
|
export type ToolCall = ClassifyPropCall | ExcludePropCall | ClassifyComponentCall | ClassifySlotCall;
|
|
36
50
|
export interface SelectComponentCall {
|
|
@@ -99,6 +99,8 @@ export function parseToolCallLines(stdout) {
|
|
|
99
99
|
call.description = rec.description;
|
|
100
100
|
if (typeof rec.default === 'string' || typeof rec.default === 'boolean')
|
|
101
101
|
call.default = rec.default;
|
|
102
|
+
if (typeof rec.reason === 'string')
|
|
103
|
+
call.reason = rec.reason;
|
|
102
104
|
calls.push(call);
|
|
103
105
|
}
|
|
104
106
|
else if (tool === 'exclude_prop') {
|
|
@@ -116,6 +118,18 @@ export function parseToolCallLines(stdout) {
|
|
|
116
118
|
const call = { tool: 'classify_component' };
|
|
117
119
|
if (typeof rec.description === 'string')
|
|
118
120
|
call.description = rec.description;
|
|
121
|
+
if (typeof rec.rationale === 'object' && rec.rationale !== null) {
|
|
122
|
+
const r = rec.rationale;
|
|
123
|
+
const rationale = {};
|
|
124
|
+
if (typeof r.description === 'string')
|
|
125
|
+
rationale.description = r.description;
|
|
126
|
+
if (typeof r.props === 'string')
|
|
127
|
+
rationale.props = r.props;
|
|
128
|
+
if (typeof r.slots === 'string')
|
|
129
|
+
rationale.slots = r.slots;
|
|
130
|
+
if (Object.keys(rationale).length > 0)
|
|
131
|
+
call.rationale = rationale;
|
|
132
|
+
}
|
|
119
133
|
calls.push(call);
|
|
120
134
|
}
|
|
121
135
|
else if (tool === 'classify_slot') {
|
|
@@ -131,6 +145,8 @@ export function parseToolCallLines(stdout) {
|
|
|
131
145
|
}
|
|
132
146
|
if (typeof rec.description === 'string')
|
|
133
147
|
call.description = rec.description;
|
|
148
|
+
if (typeof rec.rationale === 'string')
|
|
149
|
+
call.rationale = rec.rationale;
|
|
134
150
|
calls.push(call);
|
|
135
151
|
}
|
|
136
152
|
}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Feature 8: render the warning banner shown when a custom skill prompt is
|
|
4
|
+
* active. Always cites the bundled invariants that the override bypasses so
|
|
5
|
+
* the operator cannot miss it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatCustomPromptBanner(skill: 'components' | 'select', path: string): string;
|
|
2
8
|
export declare function registerGenerateCommand(program: Command): void;
|
|
@@ -6,10 +6,12 @@ import { execFile } from 'node:child_process';
|
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { parseToolCallLines, parseTokenToolCallLines, resolveBinary, runAgent, } from './agent-runner.js';
|
|
8
8
|
import { OutputFormatter, c } from '../output/format.js';
|
|
9
|
+
import { formatGenerateProgressLine } from './progress.js';
|
|
9
10
|
import { buildPrompt, resolveSkillPath } from './prompt-builder.js';
|
|
10
11
|
import { GenerateView } from './tui/GenerateView.js';
|
|
11
12
|
import { registerGenerateEditCommand } from './edit/command.js';
|
|
12
13
|
import { openPipelineDb, loadRawComponents, applyToolCalls, applyTokenToolCalls, computeComponentInputHash, computeTokenInputHash, lookupCache, lookupCacheByEntity, storeCache, copyComponentFromCache, copyTokensFromCache, renameEmptySlots, } from '../session/db.js';
|
|
14
|
+
import { hashPromptForSkill } from '../session/cache-keys.js';
|
|
13
15
|
import { getRefineArtifactsRoot, getRefineSessionPaths } from '../analyze/select/persistence.js';
|
|
14
16
|
import { readExperiencesCredentials } from '../credentials-store.js';
|
|
15
17
|
const execFileAsync = promisify(execFile);
|
|
@@ -17,6 +19,16 @@ const VALID_AGENTS = new Set(['claude', 'codex', 'opencode', 'cursor']);
|
|
|
17
19
|
const DEFAULT_TIMEOUT_MS = Number(process.env.EDS_AGENT_TIMEOUT_MS ?? 3 * 60 * 1000);
|
|
18
20
|
const DEFAULT_COMPONENT_CONCURRENCY = 10;
|
|
19
21
|
const RETRY_BACKOFF_MS = Number(process.env.EDS_RETRY_BACKOFF_MS ?? 5_000);
|
|
22
|
+
/**
|
|
23
|
+
* Feature 8: render the warning banner shown when a custom skill prompt is
|
|
24
|
+
* active. Always cites the bundled invariants that the override bypasses so
|
|
25
|
+
* the operator cannot miss it.
|
|
26
|
+
*/
|
|
27
|
+
export function formatCustomPromptBanner(skill, path) {
|
|
28
|
+
return (`WARNING: Custom prompt active for ${skill}: ${path}\n` +
|
|
29
|
+
` Bundled invariants (utility-wrapper rejection, description content rules) do NOT apply.\n` +
|
|
30
|
+
` You are responsible for the prompt's correctness.\n`);
|
|
31
|
+
}
|
|
20
32
|
function die(message) {
|
|
21
33
|
process.stderr.write(`${message}\n`);
|
|
22
34
|
process.exit(1);
|
|
@@ -104,11 +116,11 @@ function printFallbackInstructions(options) {
|
|
|
104
116
|
lines.push(` Re-run the generate command with the agent available, or use --dry-run to inspect the prompt.`);
|
|
105
117
|
process.stderr.write(lines.join('\n') + '\n');
|
|
106
118
|
}
|
|
107
|
-
async function runOneComponent(agent, model, db, sessionId, component, tokensInline, tokenMapInline, index, total, verbose, noCache) {
|
|
119
|
+
async function runOneComponent(agent, model, db, sessionId, component, tokensInline, tokenMapInline, index, total, verbose, noCache, skillPathOverride, promptHash) {
|
|
108
120
|
const pos = c.dim(`[${index + 1}/${total}]`);
|
|
109
121
|
if (!noCache) {
|
|
110
122
|
const inputHash = computeComponentInputHash(component);
|
|
111
|
-
const cached = lookupCache(db, inputHash, 'component', component.component_id);
|
|
123
|
+
const cached = lookupCache(db, inputHash, 'component', component.component_id, promptHash);
|
|
112
124
|
if (cached) {
|
|
113
125
|
copyComponentFromCache(db, cached.sourceSessionId, sessionId, component.component_id);
|
|
114
126
|
process.stderr.write(` ${pos} ${c.bold(component.name)} ${c.green('cached')}\n`);
|
|
@@ -168,6 +180,7 @@ async function runOneComponent(agent, model, db, sessionId, component, tokensInl
|
|
|
168
180
|
tokenMapInline,
|
|
169
181
|
outDir: process.cwd(),
|
|
170
182
|
componentName: component.name,
|
|
183
|
+
skillPathOverride,
|
|
171
184
|
});
|
|
172
185
|
const maxAttempts = 2;
|
|
173
186
|
let lastError = '';
|
|
@@ -215,7 +228,7 @@ async function runOneComponent(agent, model, db, sessionId, component, tokensInl
|
|
|
215
228
|
const applied = applyToolCalls(db, sessionId, component.component_id, component.name, calls, warnings);
|
|
216
229
|
if (!noCache) {
|
|
217
230
|
const inputHash = computeComponentInputHash(component);
|
|
218
|
-
storeCache(db, inputHash, 'component', component.component_id, sessionId, false);
|
|
231
|
+
storeCache(db, inputHash, 'component', component.component_id, sessionId, false, promptHash);
|
|
219
232
|
}
|
|
220
233
|
return {
|
|
221
234
|
componentName: component.name,
|
|
@@ -238,17 +251,20 @@ async function runOneComponent(agent, model, db, sessionId, component, tokensInl
|
|
|
238
251
|
renamedSlotsCount: renames.length,
|
|
239
252
|
};
|
|
240
253
|
}
|
|
241
|
-
async function runAllComponents(agent, model, db, sessionId, components, tokensInline, tokenMapInline, verbose, noCache) {
|
|
254
|
+
async function runAllComponents(agent, model, db, sessionId, components, tokensInline, tokenMapInline, verbose, noCache, skillPathOverride, promptHash) {
|
|
242
255
|
const concurrency = Number(process.env.EDS_GENERATE_CONCURRENCY ?? DEFAULT_COMPONENT_CONCURRENCY);
|
|
243
256
|
process.stderr.write(`Categorizing ${c.bold(String(components.length))} component${components.length === 1 ? '' : 's'}` +
|
|
244
257
|
c.dim(` (concurrency: ${concurrency})`) +
|
|
245
258
|
'\n');
|
|
246
259
|
const results = new Array(components.length);
|
|
247
260
|
let next = 0;
|
|
261
|
+
let completed = 0;
|
|
248
262
|
async function worker() {
|
|
249
263
|
while (next < components.length) {
|
|
250
264
|
const i = next++;
|
|
251
|
-
results[i] = await runOneComponent(agent, model, db, sessionId, components[i], tokensInline, tokenMapInline, i, components.length, verbose, noCache);
|
|
265
|
+
results[i] = await runOneComponent(agent, model, db, sessionId, components[i], tokensInline, tokenMapInline, i, components.length, verbose, noCache, skillPathOverride, promptHash);
|
|
266
|
+
completed += 1;
|
|
267
|
+
process.stderr.write(`${formatGenerateProgressLine(completed, components.length, results[i].componentName)}\n`);
|
|
252
268
|
}
|
|
253
269
|
}
|
|
254
270
|
await Promise.all(Array.from({ length: Math.min(concurrency, components.length) }, worker));
|
|
@@ -300,6 +316,19 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
300
316
|
die(`Error: no agent configured. Pass --agent <name> or run experiences setup. Accepted values: claude, codex, opencode, cursor`);
|
|
301
317
|
}
|
|
302
318
|
const agent = agentName;
|
|
319
|
+
// Feature 8: resolve custom-prompt path for `components` (flag wins over
|
|
320
|
+
// saved credentials), validate, and emit the warning banner once at action
|
|
321
|
+
// entry.
|
|
322
|
+
const generatePromptPath = skill === 'components' ? (opts.generatePromptPath ?? savedCreds.generatePromptPath) : undefined;
|
|
323
|
+
if (generatePromptPath) {
|
|
324
|
+
if (!(await pathExists(resolve(generatePromptPath)))) {
|
|
325
|
+
die(`Error: custom prompt path not found: ${resolve(generatePromptPath)}`);
|
|
326
|
+
}
|
|
327
|
+
if (!generatePromptPath.toLowerCase().endsWith('.md')) {
|
|
328
|
+
process.stderr.write(`WARNING: custom prompt path does not end in .md (${generatePromptPath}) — proceeding anyway.\n`);
|
|
329
|
+
}
|
|
330
|
+
process.stderr.write(formatCustomPromptBanner('components', resolve(generatePromptPath)));
|
|
331
|
+
}
|
|
303
332
|
if (skill === 'tokens' && !opts.rawTokens) {
|
|
304
333
|
die('Error: --raw-tokens is required when using generate tokens');
|
|
305
334
|
}
|
|
@@ -373,6 +402,7 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
373
402
|
tokensInline,
|
|
374
403
|
tokenMapInline,
|
|
375
404
|
outDir: process.cwd(),
|
|
405
|
+
skillPathOverride: generatePromptPath,
|
|
376
406
|
});
|
|
377
407
|
process.stdout.write(prompt + '\n');
|
|
378
408
|
process.exit(0);
|
|
@@ -390,7 +420,8 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
390
420
|
const db = openPipelineDb();
|
|
391
421
|
let componentResults;
|
|
392
422
|
try {
|
|
393
|
-
|
|
423
|
+
const promptHash = await hashPromptForSkill('components', generatePromptPath);
|
|
424
|
+
componentResults = await runAllComponents(agent, model, db, sessionId, allComponents, tokensInline, tokenMapInline, verbose, opts.cache === false || process.env.EDS_NO_CACHE === '1', generatePromptPath, promptHash);
|
|
394
425
|
}
|
|
395
426
|
finally {
|
|
396
427
|
db.close();
|
|
@@ -449,9 +480,10 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
449
480
|
resolvedSessionId = newId;
|
|
450
481
|
}
|
|
451
482
|
}
|
|
483
|
+
const tokenPromptHash = await hashPromptForSkill('tokens');
|
|
452
484
|
// Check cache before invoking agent
|
|
453
485
|
if (!noCache) {
|
|
454
|
-
const tokenCached = lookupCache(db, tokenInputHash, 'token_set', '__tokens__');
|
|
486
|
+
const tokenCached = lookupCache(db, tokenInputHash, 'token_set', '__tokens__', tokenPromptHash);
|
|
455
487
|
if (tokenCached) {
|
|
456
488
|
copyTokensFromCache(db, tokenCached.sourceSessionId, resolvedSessionId);
|
|
457
489
|
sessionId = resolvedSessionId;
|
|
@@ -508,7 +540,7 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
508
540
|
}
|
|
509
541
|
applyTokenToolCalls(db, resolvedSessionId, tokenCalls, []);
|
|
510
542
|
if (!noCache) {
|
|
511
|
-
storeCache(db, tokenInputHash, 'token_set', '__tokens__', resolvedSessionId, false);
|
|
543
|
+
storeCache(db, tokenInputHash, 'token_set', '__tokens__', resolvedSessionId, false, tokenPromptHash);
|
|
512
544
|
}
|
|
513
545
|
sessionId = resolvedSessionId;
|
|
514
546
|
const groupCount = tokenCalls.filter((tc) => tc.tool === 'set_group').length;
|
|
@@ -541,7 +573,9 @@ function addAgentFlags(cmd) {
|
|
|
541
573
|
.option('--model <name>', 'Model to use (defaults to a small/fast model per agent)')
|
|
542
574
|
.option('--verbose', 'Show full agent output including reasoning text')
|
|
543
575
|
.option('--dry-run', 'Print the prompt without invoking the agent')
|
|
544
|
-
.option('--no-cache', 'Bypass
|
|
576
|
+
.option('--no-cache', 'Bypass ALL fine-grained caches (extract, select, generate) and force AI re-run. ' +
|
|
577
|
+
'Cache keys now factor in prompt content — changing the prompt file via --generate-prompt-path or ' +
|
|
578
|
+
'--select-prompt-path will already bust the corresponding stage. Use --no-cache to force a full re-run.');
|
|
545
579
|
}
|
|
546
580
|
export function registerGenerateCommand(program) {
|
|
547
581
|
const generate = program.command('generate').description('Generate CDF/DTCG artifacts or correct generation output');
|
|
@@ -551,7 +585,8 @@ export function registerGenerateCommand(program) {
|
|
|
551
585
|
.description('Invoke a coding agent to produce components.json from raw analysis output')
|
|
552
586
|
.option('--session <id>', 'Session ID from analyze extract (defaults to most recent)')
|
|
553
587
|
.option('--tokens <path>', 'Path to tokens.json for token-linked prop resolution')
|
|
554
|
-
.option('--token-map <path>', 'Path to token-name-map.json sidecar')
|
|
588
|
+
.option('--token-map <path>', 'Path to token-name-map.json sidecar')
|
|
589
|
+
.option('--generate-prompt-path <path>', 'Path to a custom .md skill prompt for components generation (bypasses bundled prompt invariants)');
|
|
555
590
|
addAgentFlags(componentsCmd).action(async (opts) => {
|
|
556
591
|
await runGenerateSkill('components', opts, opts.verbose ?? false);
|
|
557
592
|
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a structured generation-progress line for the wizard parser.
|
|
3
|
+
*
|
|
4
|
+
* Emitted at terminal completion of each component (success, cache hit,
|
|
5
|
+
* pinned hit, or final failure). `done` represents the count of completed
|
|
6
|
+
* components, which is monotonically non-decreasing — unlike the legacy
|
|
7
|
+
* `[index+1/total]` line, which reports input position.
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatGenerateProgressLine(done: number, total: number, name: string): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a structured generation-progress line for the wizard parser.
|
|
3
|
+
*
|
|
4
|
+
* Emitted at terminal completion of each component (success, cache hit,
|
|
5
|
+
* pinned hit, or final failure). `done` represents the count of completed
|
|
6
|
+
* components, which is monotonically non-decreasing — unlike the legacy
|
|
7
|
+
* `[index+1/total]` line, which reports input position.
|
|
8
|
+
*/
|
|
9
|
+
export function formatGenerateProgressLine(done, total, name) {
|
|
10
|
+
return `progress=generate:${done}/${total}:${name}`;
|
|
11
|
+
}
|
|
@@ -13,6 +13,14 @@ export interface PromptOptions {
|
|
|
13
13
|
outDir: string;
|
|
14
14
|
/** For components skill only: the single component's name (used in error messages). */
|
|
15
15
|
componentName?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Feature 8: custom prompt path override. When set, this absolute or relative
|
|
18
|
+
* `.md` path is read in place of the bundled skill file. The bundled-prompt
|
|
19
|
+
* invariants (utility-wrapper rejection, description content rules, etc.) do
|
|
20
|
+
* NOT apply under an override — callers are responsible for showing the
|
|
21
|
+
* appropriate warning banner.
|
|
22
|
+
*/
|
|
23
|
+
skillPathOverride?: string;
|
|
16
24
|
}
|
|
17
25
|
export declare function buildPrompt(options: PromptOptions): Promise<string>;
|
|
18
26
|
export declare function resolveSkillPath(skill: Skill): string;
|
|
@@ -13,7 +13,7 @@ const OUTPUT_FILES = {
|
|
|
13
13
|
select: 'select.json',
|
|
14
14
|
};
|
|
15
15
|
export async function buildPrompt(options) {
|
|
16
|
-
const skillContent = await readSkillFile(options.skill);
|
|
16
|
+
const skillContent = await readSkillFile(options.skill, options.skillPathOverride);
|
|
17
17
|
const preamble = buildPreamble(options);
|
|
18
18
|
return `${preamble}\n\nSkill instructions follow:\n---\n${skillContent}`;
|
|
19
19
|
}
|
|
@@ -34,7 +34,16 @@ export function resolveSkillPath(skill) {
|
|
|
34
34
|
dir = parent;
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
async function readSkillFile(skill) {
|
|
37
|
+
async function readSkillFile(skill, override) {
|
|
38
|
+
if (override) {
|
|
39
|
+
const skillPath = resolve(override);
|
|
40
|
+
try {
|
|
41
|
+
return await readFile(skillPath, 'utf8');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw new Error(`custom prompt file not found (skill: ${skill}, path: ${skillPath})`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
38
47
|
const skillPath = resolveSkillPath(skill);
|
|
39
48
|
try {
|
|
40
49
|
return await readFile(skillPath, 'utf8');
|
|
@@ -123,13 +132,13 @@ Do NOT write any files or emit any JSON blobs. Instead, emit one JSON object per
|
|
|
123
132
|
The four tool calls you may emit are:
|
|
124
133
|
|
|
125
134
|
\`\`\`
|
|
126
|
-
{"tool":"classify_component","description":"<optional component-level description>"}
|
|
135
|
+
{"tool":"classify_component","description":"<optional component-level description>","rationale":{"description":"<why this component is classified the way it is>","props":"<why these props were chosen>","slots":"<why these slots were chosen>"}}
|
|
127
136
|
|
|
128
|
-
{"tool":"classify_prop","prop":"<propName>","cdf_type":"<type>","cdf_category":"<category>","required":<bool>,"description":"<reason>","values":["a","b"],"token_kind":"color","default":"<value>"}
|
|
137
|
+
{"tool":"classify_prop","prop":"<propName>","cdf_type":"<type>","cdf_category":"<category>","required":<bool>,"description":"<short customer-facing description>","reason":"<full internal rationale; not customer-facing>","values":["a","b"],"token_kind":"color","default":"<value>"}
|
|
129
138
|
|
|
130
139
|
{"tool":"exclude_prop","prop":"<propName>","reason":"<why excluded>"}
|
|
131
140
|
|
|
132
|
-
{"tool":"classify_slot","slot":"<slotName>","required":<bool>,"allowed_components":["ComponentName"],"description":"<reason>"}
|
|
141
|
+
{"tool":"classify_slot","slot":"<slotName>","required":<bool>,"allowed_components":["ComponentName"],"description":"<reason>","rationale":"<why this slot was kept in the catalog>"}
|
|
133
142
|
\`\`\`
|
|
134
143
|
|
|
135
144
|
Rules:
|
|
@@ -143,12 +152,14 @@ Rules:
|
|
|
143
152
|
- href and URL props → cdf_type "string", cdf_category "content". Do NOT use cdf_type "link" — it is not valid.
|
|
144
153
|
- Framework internals (ref, event handlers, test IDs) → exclude_prop.
|
|
145
154
|
- CSS design props (className, style, styles, positional/geometric props: top, bottom, left, right, rotation, offset, etc.) → classify_prop, cdf_type: "string", cdf_category: "design".
|
|
155
|
+
- On classify_component, "rationale" fields are operator-facing (read-only) but may surface in customer-facing exports. The "rationale.description" field is subject to the description content rules in the skill prompt (no internal initiative names). "rationale.props" and "rationale.slots" describe your reasoning about scope; "classify_slot.rationale" explains why each slot was kept.
|
|
156
|
+
- On classify_prop, "reason" is REQUIRED and is the LLM's internal rationale — shown to the developer reviewing the import, never to end-users. "description" is the customer-facing copy and is subject to the description content rules in the skill prompt. Keep them distinct: "description" is short and customer-facing; "reason" explains your reasoning in detail.
|
|
146
157
|
- You may emit prose lines (not starting with {) anywhere — they are ignored by the parser and serve as your reasoning log.`;
|
|
147
158
|
}
|
|
148
159
|
function buildSelectAutonomousPreamble(inputBlock) {
|
|
149
160
|
return `You are running as part of the experience-design-system-cli import pipeline in AUTONOMOUS mode. The developer is not present to answer questions.
|
|
150
161
|
|
|
151
|
-
Your task: review the
|
|
162
|
+
Your task: review the components provided below and decide whether each belongs in Contentful Experience Orchestration as a Component Type. The input is a JSON array — you may receive 1–N components in a single message. Emit one tool call per input component, named after the component. Apply all judgment calls yourself — do not pause to ask for confirmation. Include a brief "reason" to document your reasoning for each decision.
|
|
152
163
|
|
|
153
164
|
Key rule: accept any component that renders visible UI — atoms, molecules, and organisms are all valid Component Types in Contentful Experience Orchestration. Reject only components that produce zero visual output: React hooks, pure context providers, A/B testing or variant-routing wrappers, analytics trackers, and security utilities. Do NOT reject a component because it has few props, is low-level, or has some A/B testing or personalization-related props mixed in — those props are handled in the generate step.
|
|
154
165
|
|
|
@@ -156,9 +167,9 @@ All input data is provided inline below — do not read any additional files.${i
|
|
|
156
167
|
|
|
157
168
|
## Output protocol
|
|
158
169
|
|
|
159
|
-
Do NOT write any files or emit any JSON blobs. Instead, emit
|
|
170
|
+
Do NOT write any files or emit any JSON blobs. Instead, emit JSON tool calls one per line to stdout. The CLI reads your stdout line by line.
|
|
160
171
|
|
|
161
|
-
The two tool calls — emit exactly one:
|
|
172
|
+
The two tool calls — emit exactly one per input component:
|
|
162
173
|
|
|
163
174
|
\`\`\`
|
|
164
175
|
{"tool":"select_component","name":"<ComponentName>","reason":"<brief reason>"}
|
|
@@ -167,9 +178,9 @@ The two tool calls — emit exactly one:
|
|
|
167
178
|
\`\`\`
|
|
168
179
|
|
|
169
180
|
Rules:
|
|
170
|
-
- Emit exactly one JSON object
|
|
171
|
-
- The name must match
|
|
172
|
-
- You may emit prose lines (not starting with {) to reason before
|
|
181
|
+
- Emit exactly one JSON object per line. No multi-line JSON. No markdown fences.
|
|
182
|
+
- Emit exactly one tool call per input component. The "name" field must match a component name from the input array exactly. Tool calls may appear in any order.
|
|
183
|
+
- You may emit prose lines (not starting with {) to reason before each tool call — they are ignored by the parser.`;
|
|
173
184
|
}
|
|
174
185
|
function buildTokensAutonomousPreamble(inputBlock) {
|
|
175
186
|
return `You are running as part of the experience-design-system-cli generate pipeline in AUTONOMOUS mode. The developer is not present to answer questions.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution chain for `--agent` and `--model` overrides on `experiences import`.
|
|
3
|
+
*
|
|
4
|
+
* The wizard path previously hard-defaulted `--agent` to `'claude'` at the
|
|
5
|
+
* commander layer, which (a) shadowed the stored `credentials.json` value when
|
|
6
|
+
* the user passed no flag, and (b) made it impossible to distinguish "user
|
|
7
|
+
* explicitly passed --agent claude" from "user passed nothing". `--model` was
|
|
8
|
+
* registered but never threaded into the wizard subprocesses at all.
|
|
9
|
+
*
|
|
10
|
+
* These helpers produce the resolved values to feed into `WizardApp` /
|
|
11
|
+
* `runPipeline`:
|
|
12
|
+
*
|
|
13
|
+
* 1. CLI flag wins when provided.
|
|
14
|
+
* 2. Otherwise, the value persisted in `credentials.json` (written by
|
|
15
|
+
* `experiences setup`) is used.
|
|
16
|
+
* 3. Otherwise, the built-in default (currently `'claude'`) is used for
|
|
17
|
+
* the agent. Model has no default — agents pick a small/fast model
|
|
18
|
+
* themselves when `--model` is omitted.
|
|
19
|
+
*/
|
|
20
|
+
/** Built-in fallback agent when neither a flag nor a stored preference is set. */
|
|
21
|
+
export declare const DEFAULT_AGENT = "claude";
|
|
22
|
+
export declare function resolveAgent(flagValue: string | undefined, storedValue: string | undefined): string;
|
|
23
|
+
export declare function resolveModel(flagValue: string | undefined, storedValue: string | undefined): string | undefined;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution chain for `--agent` and `--model` overrides on `experiences import`.
|
|
3
|
+
*
|
|
4
|
+
* The wizard path previously hard-defaulted `--agent` to `'claude'` at the
|
|
5
|
+
* commander layer, which (a) shadowed the stored `credentials.json` value when
|
|
6
|
+
* the user passed no flag, and (b) made it impossible to distinguish "user
|
|
7
|
+
* explicitly passed --agent claude" from "user passed nothing". `--model` was
|
|
8
|
+
* registered but never threaded into the wizard subprocesses at all.
|
|
9
|
+
*
|
|
10
|
+
* These helpers produce the resolved values to feed into `WizardApp` /
|
|
11
|
+
* `runPipeline`:
|
|
12
|
+
*
|
|
13
|
+
* 1. CLI flag wins when provided.
|
|
14
|
+
* 2. Otherwise, the value persisted in `credentials.json` (written by
|
|
15
|
+
* `experiences setup`) is used.
|
|
16
|
+
* 3. Otherwise, the built-in default (currently `'claude'`) is used for
|
|
17
|
+
* the agent. Model has no default — agents pick a small/fast model
|
|
18
|
+
* themselves when `--model` is omitted.
|
|
19
|
+
*/
|
|
20
|
+
/** Built-in fallback agent when neither a flag nor a stored preference is set. */
|
|
21
|
+
export const DEFAULT_AGENT = 'claude';
|
|
22
|
+
export function resolveAgent(flagValue, storedValue) {
|
|
23
|
+
if (flagValue && flagValue.length > 0)
|
|
24
|
+
return flagValue;
|
|
25
|
+
if (storedValue && storedValue.length > 0)
|
|
26
|
+
return storedValue;
|
|
27
|
+
return DEFAULT_AGENT;
|
|
28
|
+
}
|
|
29
|
+
export function resolveModel(flagValue, storedValue) {
|
|
30
|
+
if (flagValue && flagValue.length > 0)
|
|
31
|
+
return flagValue;
|
|
32
|
+
if (storedValue && storedValue.length > 0)
|
|
33
|
+
return storedValue;
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the effective auto-filter setting from a CLI flag plus the persisted
|
|
3
|
+
* config preference.
|
|
4
|
+
*
|
|
5
|
+
* Precedence (highest first):
|
|
6
|
+
* 1. CLI flag (`--auto-filter` or `--no-auto-filter`) — wins when set
|
|
7
|
+
* 2. Config (`credentials.json` `autoFilter` field) — used when flag absent
|
|
8
|
+
* 3. Default ON — when neither is set
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveAutoFilter(opts: {
|
|
11
|
+
autoFilter?: boolean;
|
|
12
|
+
}, configAutoFilter?: boolean): boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the effective auto-filter setting from a CLI flag plus the persisted
|
|
3
|
+
* config preference.
|
|
4
|
+
*
|
|
5
|
+
* Precedence (highest first):
|
|
6
|
+
* 1. CLI flag (`--auto-filter` or `--no-auto-filter`) — wins when set
|
|
7
|
+
* 2. Config (`credentials.json` `autoFilter` field) — used when flag absent
|
|
8
|
+
* 3. Default ON — when neither is set
|
|
9
|
+
*/
|
|
10
|
+
export function resolveAutoFilter(opts, configAutoFilter) {
|
|
11
|
+
if (opts.autoFilter !== undefined)
|
|
12
|
+
return opts.autoFilter;
|
|
13
|
+
if (configAutoFilter !== undefined)
|
|
14
|
+
return configAutoFilter;
|
|
15
|
+
return true;
|
|
16
|
+
}
|