@contentful/experience-design-system-cli 2.26.1-dev-build-a852362.0 → 2.26.1-dev-build-f03f014.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 +14 -10
- package/dist/src/index.js +34937 -9
- package/dist/src/lib/cli-path.d.ts +4 -0
- package/dist/src/runs/export-helpers.d.ts +1 -12
- package/dist/src/runs/push-helpers.d.ts +1 -6
- package/package.json +12 -7
- package/skills/generate-components.md +427 -0
- package/skills/generate-tokens.md +194 -0
- package/skills/select-components.md +224 -0
- package/dist/src/analytics/apply.js +0 -30
- package/dist/src/analytics/client.js +0 -81
- package/dist/src/analytics/constants.js +0 -4
- package/dist/src/analytics/env.js +0 -14
- package/dist/src/analytics/exit.js +0 -24
- package/dist/src/analytics/index.js +0 -9
- package/dist/src/analytics/normalize.js +0 -18
- package/dist/src/analytics/os.js +0 -13
- package/dist/src/analytics/session.js +0 -15
- package/dist/src/analytics/tracker.js +0 -126
- package/dist/src/analytics/types.js +0 -1
- package/dist/src/analyze/build-analyze-view-rows.js +0 -63
- package/dist/src/analyze/command.js +0 -599
- package/dist/src/analyze/composite-closure.js +0 -0
- package/dist/src/analyze/composition/agent-parser/author-prompt.js +0 -22
- package/dist/src/analyze/composition/agent-parser/extract-parser.js +0 -44
- package/dist/src/analyze/composition/agent-parser/load-prompt.js +0 -31
- package/dist/src/analyze/composition/agent-parser/resolve-via-parser.js +0 -88
- package/dist/src/analyze/composition/agent-parser/sandbox.js +0 -187
- package/dist/src/analyze/composition/apply-mapping.js +0 -67
- package/dist/src/analyze/composition/candidate-critic-agent.js +0 -39
- package/dist/src/analyze/composition/candidate-critic.js +0 -63
- package/dist/src/analyze/composition/candidate-files.js +0 -131
- package/dist/src/analyze/composition/composition-cache-key.js +0 -19
- package/dist/src/analyze/composition/interchange-schema.js +0 -61
- package/dist/src/analyze/composition/manifest-doc-evidence.js +0 -136
- package/dist/src/analyze/composition/merge-edges.js +0 -70
- package/dist/src/analyze/composition/parse-map-edges.js +0 -55
- package/dist/src/analyze/composition/resolve-mapping-cli.js +0 -29
- package/dist/src/analyze/composition/resolve-mapping.js +0 -111
- package/dist/src/analyze/cycle-detection.js +0 -234
- package/dist/src/analyze/cycle-view.js +0 -28
- package/dist/src/analyze/fuzzy-search.js +0 -55
- package/dist/src/analyze/issue-inheritance.js +0 -108
- package/dist/src/analyze/lineage.js +0 -0
- package/dist/src/analyze/scope-gate-cascade.js +0 -170
- package/dist/src/analyze/search-neighborhood.js +0 -56
- package/dist/src/analyze/select/command.js +0 -484
- package/dist/src/analyze/select/index.js +0 -5
- package/dist/src/analyze/select/parser.js +0 -53
- package/dist/src/analyze/select/persistence.js +0 -80
- package/dist/src/analyze/select/preview-annotations.js +0 -62
- package/dist/src/analyze/select/stdout.js +0 -3
- package/dist/src/analyze/select/tui/App.js +0 -483
- package/dist/src/analyze/select/tui/components/ComponentDetail.js +0 -48
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +0 -125
- package/dist/src/analyze/select/tui/components/FieldEditor.js +0 -1131
- package/dist/src/analyze/select/tui/components/FinalizeDialog.js +0 -36
- package/dist/src/analyze/select/tui/components/GotoBanner.js +0 -30
- package/dist/src/analyze/select/tui/components/GroupedSidebar.js +0 -517
- package/dist/src/analyze/select/tui/components/HelpOverlay.js +0 -17
- package/dist/src/analyze/select/tui/components/JsonEditor.js +0 -131
- package/dist/src/analyze/select/tui/components/JsonPanel.js +0 -66
- package/dist/src/analyze/select/tui/components/LineagePanel.js +0 -22
- package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +0 -29
- package/dist/src/analyze/select/tui/components/QuitDialog.js +0 -14
- package/dist/src/analyze/select/tui/components/RationalePanel.js +0 -80
- package/dist/src/analyze/select/tui/components/Sidebar.js +0 -92
- package/dist/src/analyze/select/tui/components/SourcePanel.js +0 -52
- package/dist/src/analyze/select/tui/components/StatusBar.js +0 -7
- package/dist/src/analyze/select/tui/components/TopBar.js +0 -10
- package/dist/src/analyze/select/tui/components/removed-components-text.js +0 -11
- package/dist/src/analyze/select/tui/hooks/scroll-offset.js +0 -26
- package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +0 -66
- package/dist/src/analyze/select/tui/hooks/useKeymap.js +0 -67
- package/dist/src/analyze/select/tui/hooks/useSession.js +0 -52
- package/dist/src/analyze/select/tui/hooks/useUndo.js +0 -26
- package/dist/src/analyze/select/tui/theme.js +0 -28
- package/dist/src/analyze/select/types.js +0 -39
- package/dist/src/analyze/select-agent/command.js +0 -558
- package/dist/src/analyze/select-agent/context-builder.js +0 -197
- package/dist/src/analyze/select-agent/show-rationale.js +0 -92
- package/dist/src/analyze/selection-cascade.js +0 -19
- package/dist/src/analyze/slot-graph.js +0 -17
- package/dist/src/analyze/tui/AnalyzeView.js +0 -64
- package/dist/src/apply/api-client.js +0 -440
- package/dist/src/apply/command.js +0 -845
- package/dist/src/apply/manifest.js +0 -1
- package/dist/src/apply/preview-utils.js +0 -26
- package/dist/src/apply/tui/SelectView.js +0 -34
- package/dist/src/apply/tui/ServerApplyView.js +0 -49
- package/dist/src/apply/tui/ServerPreviewView.js +0 -23
- package/dist/src/credentials-store.js +0 -74
- package/dist/src/generate/command.js +0 -597
- package/dist/src/generate/edit/command.js +0 -127
- package/dist/src/generate/tui/GenerateView.js +0 -10
- package/dist/src/host-utils.js +0 -26
- package/dist/src/import/agent-model-resolve.js +0 -35
- package/dist/src/import/auto-filter-resolve.js +0 -16
- package/dist/src/import/command.js +0 -380
- package/dist/src/import/cycle-auto-reject.js +0 -14
- package/dist/src/import/orchestrator.js +0 -603
- package/dist/src/import/path-utils.js +0 -30
- package/dist/src/import/picker-dispatch.js +0 -44
- package/dist/src/import/print-prompt.js +0 -31
- package/dist/src/import/strip-allowed-components.js +0 -22
- package/dist/src/import/tui/CustomPromptBanner.js +0 -8
- package/dist/src/import/tui/WizardApp.js +0 -1670
- package/dist/src/import/tui/ai-flag.js +0 -6
- package/dist/src/import/tui/auto-filter-error.js +0 -29
- package/dist/src/import/tui/autocomplete.js +0 -32
- package/dist/src/import/tui/components/AutoFilterBanner.js +0 -19
- package/dist/src/import/tui/components/CounterStrip.js +0 -12
- package/dist/src/import/tui/components/LegendEntry.js +0 -6
- package/dist/src/import/tui/cycle-panel-scroll.js +0 -33
- package/dist/src/import/tui/final-review-host.js +0 -25
- package/dist/src/import/tui/group-collapse.js +0 -11
- package/dist/src/import/tui/history.js +0 -53
- package/dist/src/import/tui/hooks/useLineage.js +0 -50
- package/dist/src/import/tui/hooks/useOverlayPanel.js +0 -27
- package/dist/src/import/tui/lineage-layout.js +0 -82
- package/dist/src/import/tui/merge-ai-decisions.js +0 -22
- package/dist/src/import/tui/push-decision-gate-helpers.js +0 -8
- package/dist/src/import/tui/push-progress.js +0 -1
- package/dist/src/import/tui/run-print-files-helpers.js +0 -8
- package/dist/src/import/tui/run-teaser.js +0 -9
- package/dist/src/import/tui/runLivePreview.js +0 -82
- package/dist/src/import/tui/runScopeGate.js +0 -28
- package/dist/src/import/tui/scope-gate-columns.js +0 -94
- package/dist/src/import/tui/scope-gate-host.js +0 -28
- package/dist/src/import/tui/sidebar-width.js +0 -3
- package/dist/src/import/tui/spawn-generate.js +0 -53
- package/dist/src/import/tui/step-filters.js +0 -42
- package/dist/src/import/tui/steps/AtomicGenerateReviewStep.js +0 -726
- package/dist/src/import/tui/steps/AtomicScopeGateStep.js +0 -210
- package/dist/src/import/tui/steps/CredentialsStep.js +0 -108
- package/dist/src/import/tui/steps/DoneStep.js +0 -24
- package/dist/src/import/tui/steps/ErrorStep.js +0 -16
- package/dist/src/import/tui/steps/GateStep.js +0 -22
- package/dist/src/import/tui/steps/GenerateReviewStep.js +0 -1618
- package/dist/src/import/tui/steps/PathValidationStep.js +0 -152
- package/dist/src/import/tui/steps/PreviewStep.js +0 -37
- package/dist/src/import/tui/steps/PreviewValidationErrorStep.js +0 -23
- package/dist/src/import/tui/steps/PushDecisionGateStep.js +0 -73
- package/dist/src/import/tui/steps/PushingStep.js +0 -24
- package/dist/src/import/tui/steps/RunningStep.js +0 -21
- package/dist/src/import/tui/steps/ScopeGateStep.js +0 -907
- package/dist/src/import/tui/steps/TokenInputStep.js +0 -88
- package/dist/src/import/tui/steps/WelcomeStep.js +0 -34
- package/dist/src/import/tui/steps/WizardPreviewStep.js +0 -169
- package/dist/src/import/tui/steps/auto-reject-decision.js +0 -9
- package/dist/src/import/tui/steps/breaking-change-format.js +0 -24
- package/dist/src/import/tui/steps/enumerate-cycle-breaks.js +0 -0
- package/dist/src/import/tui/steps/preview-diff.js +0 -175
- package/dist/src/import/tui/useFinalizePreview.js +0 -0
- package/dist/src/import/tui/useLivePreview.js +0 -122
- package/dist/src/import/tui/wizard-422-helpers.js +0 -60
- package/dist/src/import/tui/wizard-generate-progress.js +0 -25
- package/dist/src/import/tui/wizard-save-flow.js +0 -20
- package/dist/src/import/tui/wizard-state-transitions.js +0 -32
- package/dist/src/lib/agent-model-options.js +0 -15
- package/dist/src/lib/command-options.js +0 -30
- package/dist/src/lib/composition-mode.js +0 -39
- package/dist/src/lib/contentful-urls.js +0 -27
- package/dist/src/lib/debug-logger.js +0 -229
- package/dist/src/lib/debug-preamble.js +0 -33
- package/dist/src/lib/error-parser.js +0 -269
- package/dist/src/lib/prompt-overrides.js +0 -69
- package/dist/src/lib/terminal-capabilities.js +0 -51
- package/dist/src/lib/user-agent.js +0 -29
- package/dist/src/output/format.js +0 -110
- package/dist/src/print/command.js +0 -221
- package/dist/src/print/validate/tui/ValidateView.js +0 -37
- package/dist/src/print/validate/validators/cdf-validator.js +0 -104
- package/dist/src/print/validate/validators/dtcg-validator.js +0 -110
- package/dist/src/print/validate/validators/format-errors.js +0 -18
- package/dist/src/program.js +0 -105
- package/dist/src/runs/export-helpers.js +0 -59
- package/dist/src/runs/fingerprint.js +0 -75
- package/dist/src/runs/ls-command.js +0 -137
- package/dist/src/runs/modify-launcher.js +0 -42
- package/dist/src/runs/path-prompt.js +0 -72
- package/dist/src/runs/push-creds-prompt.js +0 -39
- package/dist/src/runs/push-helpers.js +0 -72
- package/dist/src/runs/push-launcher.js +0 -77
- package/dist/src/runs/replay-helpers.js +0 -164
- package/dist/src/runs/resolve-run-target.js +0 -45
- package/dist/src/runs/run-picker-mount.js +0 -72
- package/dist/src/runs/run-picker.js +0 -129
- package/dist/src/runs/save-conflict.js +0 -56
- package/dist/src/runs/save-path-resolver.js +0 -75
- package/dist/src/runs/staleness.js +0 -140
- package/dist/src/runs/store.js +0 -136
- package/dist/src/session/cache-keys.js +0 -44
- package/dist/src/session/command.js +0 -261
- package/dist/src/session/db.js +0 -1538
- package/dist/src/session/migration.js +0 -117
- package/dist/src/session/session-id.js +0 -212
- package/dist/src/session/stats.js +0 -89
- package/dist/src/setup/analytics-prompt.js +0 -24
- package/dist/src/setup/auto-filter-prompt.js +0 -24
- package/dist/src/setup/command.js +0 -966
- package/dist/src/setup/composition-mode-prompt.js +0 -37
- package/dist/src/setup/debug-mode-prompt.js +0 -22
- package/dist/src/types.js +0 -1
|
@@ -1,597 +0,0 @@
|
|
|
1
|
-
import { createElement } from 'react';
|
|
2
|
-
import { render } from 'ink';
|
|
3
|
-
import { access, readFile, readdir, stat } from 'node:fs/promises';
|
|
4
|
-
import { join, resolve } from 'node:path';
|
|
5
|
-
import { execFile } from 'node:child_process';
|
|
6
|
-
import { promisify } from 'node:util';
|
|
7
|
-
import { AGENT_NAMES, createLocalCliAgentInvoker, describeAgentFailure, formatCustomPromptBanner, formatGenerateProgressLine, isAgentName, parseToolCallLines, parseTokenToolCallLines, resolveBinary, resolveSkillPath, buildPrompt, } from '@contentful/experience-design-system-generation';
|
|
8
|
-
import { OutputFormatter, c } from '../output/format.js';
|
|
9
|
-
import { getDebugLogger } from '../lib/debug-logger.js';
|
|
10
|
-
import { GenerateView } from './tui/GenerateView.js';
|
|
11
|
-
import { registerGenerateEditCommand } from './edit/command.js';
|
|
12
|
-
import { openPipelineDb, loadRawComponents, applyToolCalls, applyTokenToolCalls, computeComponentInputHash, computeTokenInputHash, lookupCache, lookupCacheByEntity, storeCache, copyComponentFromCache, copyTokensFromCache, renameEmptySlots, } from '../session/db.js';
|
|
13
|
-
import { hashPromptForSkill } from '../session/cache-keys.js';
|
|
14
|
-
import { getRefineArtifactsRoot, getRefineSessionPaths } from '../analyze/select/persistence.js';
|
|
15
|
-
import { readExperiencesCredentials } from '../credentials-store.js';
|
|
16
|
-
import { addAgentModelOptions } from '../lib/agent-model-options.js';
|
|
17
|
-
import { bindAnalyticsSessionId, exitWithAnalytics } from '../analytics/index.js';
|
|
18
|
-
const execFileAsync = promisify(execFile);
|
|
19
|
-
const DEFAULT_TIMEOUT_MS = Number(process.env.EDS_AGENT_TIMEOUT_MS ?? 3 * 60 * 1000);
|
|
20
|
-
const DEFAULT_COMPONENT_CONCURRENCY = 10;
|
|
21
|
-
const RETRY_BACKOFF_MS = Number(process.env.EDS_RETRY_BACKOFF_MS ?? 5_000);
|
|
22
|
-
const invoker = createLocalCliAgentInvoker({
|
|
23
|
-
onDebugEvent: (name, payload) => getDebugLogger().event('agent', name, payload),
|
|
24
|
-
});
|
|
25
|
-
function die(message) {
|
|
26
|
-
process.stderr.write(`${message}\n`);
|
|
27
|
-
void exitWithAnalytics(1);
|
|
28
|
-
throw new Error('exit');
|
|
29
|
-
}
|
|
30
|
-
async function pathExists(p) {
|
|
31
|
-
return access(p)
|
|
32
|
-
.then(() => true)
|
|
33
|
-
.catch(() => false);
|
|
34
|
-
}
|
|
35
|
-
async function assertFileExists(flag, p) {
|
|
36
|
-
if (!(await pathExists(p)))
|
|
37
|
-
die(`Error: file not found: ${p} (from ${flag})`);
|
|
38
|
-
}
|
|
39
|
-
async function readFileInline(path) {
|
|
40
|
-
if (!path)
|
|
41
|
-
return undefined;
|
|
42
|
-
const resolved = resolve(path);
|
|
43
|
-
let s;
|
|
44
|
-
try {
|
|
45
|
-
s = await stat(resolved);
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
if (!s.isDirectory())
|
|
51
|
-
return readFile(resolved, 'utf8');
|
|
52
|
-
// Directory: collect and concatenate all JSON files
|
|
53
|
-
const files = [];
|
|
54
|
-
async function walk(dir) {
|
|
55
|
-
let entries;
|
|
56
|
-
try {
|
|
57
|
-
entries = await readdir(dir);
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
for (const entry of entries.sort()) {
|
|
63
|
-
const full = join(dir, entry);
|
|
64
|
-
let es;
|
|
65
|
-
try {
|
|
66
|
-
es = await stat(full);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (es.isDirectory()) {
|
|
72
|
-
await walk(full);
|
|
73
|
-
}
|
|
74
|
-
else if (entry.endsWith('.json')) {
|
|
75
|
-
files.push(full);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
await walk(resolved);
|
|
80
|
-
if (files.length === 0)
|
|
81
|
-
return undefined;
|
|
82
|
-
const parts = await Promise.all(files.map((f) => readFile(f, 'utf8').catch(() => '')));
|
|
83
|
-
return parts.filter(Boolean).join('\n\n');
|
|
84
|
-
}
|
|
85
|
-
async function assertBinaryInPath(binary) {
|
|
86
|
-
try {
|
|
87
|
-
await execFileAsync('which', [binary]);
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
function printFallbackInstructions(options) {
|
|
95
|
-
const binary = resolveBinary(options.agent);
|
|
96
|
-
const skillPath = resolveSkillPath(options.skill);
|
|
97
|
-
const lines = [
|
|
98
|
-
`Error: agent '${options.agent}' not found in $PATH (looked for binary: ${binary}).`,
|
|
99
|
-
`Install it or use one of: claude, codex, opencode, cursor`,
|
|
100
|
-
``,
|
|
101
|
-
`To run the generation step manually:`,
|
|
102
|
-
``,
|
|
103
|
-
` 1. Open your coding agent`,
|
|
104
|
-
` 2. Run this skill (all input data will be embedded inline):`,
|
|
105
|
-
` ${skillPath}`,
|
|
106
|
-
``,
|
|
107
|
-
` Use --dry-run to print the full prompt including all inline data.`,
|
|
108
|
-
];
|
|
109
|
-
lines.push(``, ` When done, the agent output must be stored in the session database.`);
|
|
110
|
-
lines.push(` Re-run the generate command with the agent available, or use --dry-run to inspect the prompt.`);
|
|
111
|
-
process.stderr.write(lines.join('\n') + '\n');
|
|
112
|
-
}
|
|
113
|
-
async function runOneComponent(agent, model, db, sessionId, component, tokensInline, tokenMapInline, index, total, verbose, noCache, skillPathOverride, promptHash) {
|
|
114
|
-
const pos = c.dim(`[${index + 1}/${total}]`);
|
|
115
|
-
if (!noCache) {
|
|
116
|
-
const inputHash = computeComponentInputHash(component);
|
|
117
|
-
const cached = lookupCache(db, inputHash, 'component', component.component_id, promptHash);
|
|
118
|
-
if (cached) {
|
|
119
|
-
copyComponentFromCache(db, cached.sourceSessionId, sessionId, component.component_id);
|
|
120
|
-
process.stderr.write(` ${pos} ${c.bold(component.name)} ${c.green('cached')}\n`);
|
|
121
|
-
return {
|
|
122
|
-
componentName: component.name,
|
|
123
|
-
classified: 0,
|
|
124
|
-
excluded: 0,
|
|
125
|
-
slots: 0,
|
|
126
|
-
warnings: [],
|
|
127
|
-
failed: false,
|
|
128
|
-
cached: true,
|
|
129
|
-
renamedSlotsCount: 0,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
// Check for pinned (human-edited) entry with a different hash
|
|
133
|
-
const pinned = lookupCacheByEntity(db, 'component', component.component_id);
|
|
134
|
-
if (pinned?.humanEdited) {
|
|
135
|
-
copyComponentFromCache(db, pinned.sourceSessionId, sessionId, component.component_id);
|
|
136
|
-
process.stderr.write(` ${pos} ${c.bold(component.name)} ${c.cyan('pinned (human-edited)')}\n`);
|
|
137
|
-
return {
|
|
138
|
-
componentName: component.name,
|
|
139
|
-
classified: 0,
|
|
140
|
-
excluded: 0,
|
|
141
|
-
slots: 0,
|
|
142
|
-
warnings: [`${component.name}: source changed but human edits preserved`],
|
|
143
|
-
failed: false,
|
|
144
|
-
cached: true,
|
|
145
|
-
renamedSlotsCount: 0,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
// Rename empty-named slots in the DB and patch the in-memory component so the
|
|
150
|
-
// prompt sees the heuristic names. applyToolCalls matches by name — a row with
|
|
151
|
-
// name="" would never be reachable by a classify_slot call otherwise.
|
|
152
|
-
const { renames, warnings: renameWarnings } = renameEmptySlots(db, sessionId, component.component_id, component.name, component.slots.length);
|
|
153
|
-
let effectiveSlots = component.slots;
|
|
154
|
-
if (renames.length > 0) {
|
|
155
|
-
const renameMap = new Map(renames.map((r) => [r.oldName, r.newName]));
|
|
156
|
-
effectiveSlots = component.slots.map((s) => (renameMap.has(s.name) ? { ...s, name: renameMap.get(s.name) } : s));
|
|
157
|
-
for (const w of renameWarnings)
|
|
158
|
-
process.stderr.write(` ${c.yellow('⚠')} ${w}\n`);
|
|
159
|
-
}
|
|
160
|
-
const rawComponentsInline = JSON.stringify([
|
|
161
|
-
{
|
|
162
|
-
name: component.name,
|
|
163
|
-
source: component.source,
|
|
164
|
-
framework: component.framework,
|
|
165
|
-
props: component.props,
|
|
166
|
-
slots: effectiveSlots,
|
|
167
|
-
},
|
|
168
|
-
], null, 2);
|
|
169
|
-
const prompt = await buildPrompt({
|
|
170
|
-
skill: 'components',
|
|
171
|
-
mode: 'autonomous',
|
|
172
|
-
rawComponentsInline,
|
|
173
|
-
tokensInline,
|
|
174
|
-
tokenMapInline,
|
|
175
|
-
outDir: process.cwd(),
|
|
176
|
-
componentName: component.name,
|
|
177
|
-
skillPathOverride,
|
|
178
|
-
});
|
|
179
|
-
const maxAttempts = 2;
|
|
180
|
-
let lastError = '';
|
|
181
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
182
|
-
if (attempt > 1)
|
|
183
|
-
await new Promise((res) => setTimeout(res, RETRY_BACKOFF_MS));
|
|
184
|
-
let outputBuf = '';
|
|
185
|
-
const formatter = new OutputFormatter(verbose, (s) => {
|
|
186
|
-
outputBuf += s;
|
|
187
|
-
});
|
|
188
|
-
const result = await invoker.invoke({
|
|
189
|
-
agent,
|
|
190
|
-
model,
|
|
191
|
-
prompt,
|
|
192
|
-
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
193
|
-
onOutput: (chunk) => formatter.push(chunk),
|
|
194
|
-
});
|
|
195
|
-
formatter.flush();
|
|
196
|
-
// Write header + all tool-call output as one block so concurrent workers don't interleave.
|
|
197
|
-
const retryNote = attempt > 1 ? ` ${c.yellow(`retrying (${attempt}/${maxAttempts})`)}` : '';
|
|
198
|
-
process.stderr.write(` ${pos} ${c.bold(component.name)}${retryNote}\n${outputBuf}`);
|
|
199
|
-
if (result.timedOut) {
|
|
200
|
-
// Don't retry timeouts — same timeout will hit again
|
|
201
|
-
return {
|
|
202
|
-
componentName: component.name,
|
|
203
|
-
classified: 0,
|
|
204
|
-
excluded: 0,
|
|
205
|
-
slots: 0,
|
|
206
|
-
warnings: [],
|
|
207
|
-
failed: true,
|
|
208
|
-
error: `timed out after ${DEFAULT_TIMEOUT_MS / 60000} minutes`,
|
|
209
|
-
renamedSlotsCount: renames.length,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
if (result.exitCode !== 0) {
|
|
213
|
-
lastError = describeAgentFailure(result);
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
const { calls, warnings } = parseToolCallLines(result.stdout);
|
|
217
|
-
if (calls.length === 0) {
|
|
218
|
-
lastError = describeAgentFailure(result);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
const applied = applyToolCalls(db, sessionId, component.component_id, component.name, calls, warnings);
|
|
222
|
-
if (!noCache) {
|
|
223
|
-
const inputHash = computeComponentInputHash(component);
|
|
224
|
-
storeCache(db, inputHash, 'component', component.component_id, sessionId, false, promptHash);
|
|
225
|
-
}
|
|
226
|
-
return {
|
|
227
|
-
componentName: component.name,
|
|
228
|
-
classified: applied.classified,
|
|
229
|
-
excluded: applied.excluded,
|
|
230
|
-
slots: applied.slots,
|
|
231
|
-
warnings: applied.warnings,
|
|
232
|
-
failed: false,
|
|
233
|
-
renamedSlotsCount: renames.length,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
return {
|
|
237
|
-
componentName: component.name,
|
|
238
|
-
classified: 0,
|
|
239
|
-
excluded: 0,
|
|
240
|
-
slots: 0,
|
|
241
|
-
warnings: [],
|
|
242
|
-
failed: true,
|
|
243
|
-
error: lastError,
|
|
244
|
-
renamedSlotsCount: renames.length,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
async function runAllComponents(agent, model, db, sessionId, components, tokensInline, tokenMapInline, verbose, noCache, skillPathOverride, promptHash) {
|
|
248
|
-
const concurrency = Number(process.env.EDS_GENERATE_CONCURRENCY ?? DEFAULT_COMPONENT_CONCURRENCY);
|
|
249
|
-
process.stderr.write(`Categorizing ${c.bold(String(components.length))} component${components.length === 1 ? '' : 's'}` +
|
|
250
|
-
c.dim(` (concurrency: ${concurrency})`) +
|
|
251
|
-
'\n');
|
|
252
|
-
const results = new Array(components.length);
|
|
253
|
-
let next = 0;
|
|
254
|
-
let completed = 0;
|
|
255
|
-
async function worker() {
|
|
256
|
-
while (next < components.length) {
|
|
257
|
-
const i = next++;
|
|
258
|
-
results[i] = await runOneComponent(agent, model, db, sessionId, components[i], tokensInline, tokenMapInline, i, components.length, verbose, noCache, skillPathOverride, promptHash);
|
|
259
|
-
completed += 1;
|
|
260
|
-
process.stderr.write(`${formatGenerateProgressLine(completed, components.length, results[i].componentName)}\n`);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
await Promise.all(Array.from({ length: Math.min(concurrency, components.length) }, worker));
|
|
264
|
-
return results;
|
|
265
|
-
}
|
|
266
|
-
function resolveSessionId(sessionFlag) {
|
|
267
|
-
if (sessionFlag)
|
|
268
|
-
return sessionFlag;
|
|
269
|
-
const db = openPipelineDb();
|
|
270
|
-
try {
|
|
271
|
-
const row = db
|
|
272
|
-
.prepare(`SELECT s.id FROM sessions s
|
|
273
|
-
JOIN steps st ON st.session_id = s.id
|
|
274
|
-
WHERE st.command = 'analyze extract'
|
|
275
|
-
AND st.status = 'complete'
|
|
276
|
-
ORDER BY st.started_at DESC
|
|
277
|
-
LIMIT 1`)
|
|
278
|
-
.get();
|
|
279
|
-
if (!row) {
|
|
280
|
-
process.stderr.write('Error: no completed analyze extract session found. Run analyze extract first, or pass --session <id>.\n');
|
|
281
|
-
void exitWithAnalytics(1);
|
|
282
|
-
throw new Error('exit');
|
|
283
|
-
}
|
|
284
|
-
return row.id;
|
|
285
|
-
}
|
|
286
|
-
finally {
|
|
287
|
-
db.close();
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
async function loadAcceptedNames(sessionId) {
|
|
291
|
-
try {
|
|
292
|
-
const artifactsRoot = getRefineArtifactsRoot();
|
|
293
|
-
const paths = await getRefineSessionPaths(sessionId, artifactsRoot);
|
|
294
|
-
const raw = await readFile(paths.statePath, 'utf8');
|
|
295
|
-
const snapshot = JSON.parse(raw);
|
|
296
|
-
const accepted = snapshot.components.filter((c) => c.status === 'accepted').map((c) => c.name);
|
|
297
|
-
if (accepted.length === 0)
|
|
298
|
-
return null;
|
|
299
|
-
return new Set(accepted);
|
|
300
|
-
}
|
|
301
|
-
catch {
|
|
302
|
-
return null;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
async function runGenerateSkill(skill, opts, verbose = false) {
|
|
306
|
-
const savedCreds = await readExperiencesCredentials();
|
|
307
|
-
const agentName = opts.agent ?? savedCreds.agent;
|
|
308
|
-
const model = opts.model ?? savedCreds.agentModel;
|
|
309
|
-
if (!agentName || !isAgentName(agentName)) {
|
|
310
|
-
die(`Error: no agent configured. Pass --agent <name> or run experiences setup. Accepted values: ${AGENT_NAMES.join(', ')}`);
|
|
311
|
-
}
|
|
312
|
-
const agent = agentName;
|
|
313
|
-
// Feature 8: resolve custom-prompt path for `components` (flag wins over
|
|
314
|
-
// saved credentials), validate, and emit the warning banner once at action
|
|
315
|
-
// entry.
|
|
316
|
-
const generatePromptPath = skill === 'components' ? (opts.generatePromptPath ?? savedCreds.generatePromptPath) : undefined;
|
|
317
|
-
if (generatePromptPath) {
|
|
318
|
-
if (!(await pathExists(resolve(generatePromptPath)))) {
|
|
319
|
-
die(`Error: custom prompt path not found: ${resolve(generatePromptPath)}`);
|
|
320
|
-
}
|
|
321
|
-
if (!generatePromptPath.toLowerCase().endsWith('.md')) {
|
|
322
|
-
process.stderr.write(`WARNING: custom prompt path does not end in .md (${generatePromptPath}) — proceeding anyway.\n`);
|
|
323
|
-
}
|
|
324
|
-
process.stderr.write(formatCustomPromptBanner('components', resolve(generatePromptPath)));
|
|
325
|
-
}
|
|
326
|
-
if (skill === 'tokens' && !opts.rawTokens) {
|
|
327
|
-
die('Error: --raw-tokens is required when using generate tokens');
|
|
328
|
-
}
|
|
329
|
-
if (opts.rawTokens)
|
|
330
|
-
await assertFileExists('--raw-tokens', opts.rawTokens);
|
|
331
|
-
if (opts.tokens)
|
|
332
|
-
await assertFileExists('--tokens', opts.tokens);
|
|
333
|
-
if (opts.tokenMap)
|
|
334
|
-
await assertFileExists('--token-map', opts.tokenMap);
|
|
335
|
-
// Read all token files inline so the agent never needs to read files itself
|
|
336
|
-
const [rawTokensInline, tokensInline, tokenMapInline] = await Promise.all([
|
|
337
|
-
readFileInline(opts.rawTokens),
|
|
338
|
-
readFileInline(opts.tokens),
|
|
339
|
-
readFileInline(opts.tokenMap),
|
|
340
|
-
]);
|
|
341
|
-
// Load raw components from DB for the components skill
|
|
342
|
-
let sessionId;
|
|
343
|
-
let allComponents;
|
|
344
|
-
if (skill === 'components') {
|
|
345
|
-
sessionId = resolveSessionId(opts.session);
|
|
346
|
-
await bindAnalyticsSessionId(sessionId);
|
|
347
|
-
const acceptedNames = await loadAcceptedNames(sessionId);
|
|
348
|
-
const db = openPipelineDb();
|
|
349
|
-
try {
|
|
350
|
-
allComponents = loadRawComponents(db, sessionId, acceptedNames ?? undefined);
|
|
351
|
-
}
|
|
352
|
-
finally {
|
|
353
|
-
db.close();
|
|
354
|
-
}
|
|
355
|
-
if (allComponents.length === 0) {
|
|
356
|
-
die(`Error: session '${sessionId}' has no raw components. Run analyze extract first.`);
|
|
357
|
-
}
|
|
358
|
-
if (acceptedNames) {
|
|
359
|
-
process.stderr.write(`Scope: ${allComponents.length} accepted component(s) from analyze select\n`);
|
|
360
|
-
}
|
|
361
|
-
// Warn about duplicate component names — later occurrences will overwrite earlier ones
|
|
362
|
-
const nameCounts = new Map();
|
|
363
|
-
for (const c of allComponents) {
|
|
364
|
-
const sources = nameCounts.get(c.name) ?? [];
|
|
365
|
-
sources.push(c.source);
|
|
366
|
-
nameCounts.set(c.name, sources);
|
|
367
|
-
}
|
|
368
|
-
const dupes = [...nameCounts.entries()].filter(([, srcs]) => srcs.length > 1);
|
|
369
|
-
if (dupes.length > 0) {
|
|
370
|
-
process.stderr.write(`Warning: ${dupes.length} duplicate component name(s) detected — only the last occurrence will be generated:\n`);
|
|
371
|
-
for (const [name, sources] of dupes) {
|
|
372
|
-
process.stderr.write(` ${name}:\n`);
|
|
373
|
-
for (const src of sources)
|
|
374
|
-
process.stderr.write(` ${src}\n`);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
if (opts.dryRun) {
|
|
379
|
-
const sampleComponent = allComponents?.[0];
|
|
380
|
-
const sampleInline = sampleComponent
|
|
381
|
-
? JSON.stringify([
|
|
382
|
-
{
|
|
383
|
-
name: sampleComponent.name,
|
|
384
|
-
source: sampleComponent.source,
|
|
385
|
-
framework: sampleComponent.framework,
|
|
386
|
-
props: sampleComponent.props,
|
|
387
|
-
slots: sampleComponent.slots,
|
|
388
|
-
},
|
|
389
|
-
], null, 2)
|
|
390
|
-
: undefined;
|
|
391
|
-
const prompt = await buildPrompt({
|
|
392
|
-
skill,
|
|
393
|
-
mode: 'autonomous',
|
|
394
|
-
rawComponentsInline: sampleInline ?? rawTokensInline,
|
|
395
|
-
rawTokensInline: skill === 'tokens' ? rawTokensInline : undefined,
|
|
396
|
-
rawTokensFilename: opts.rawTokens ? resolve(opts.rawTokens).split('/').pop() : undefined,
|
|
397
|
-
tokensInline,
|
|
398
|
-
tokenMapInline,
|
|
399
|
-
outDir: process.cwd(),
|
|
400
|
-
skillPathOverride: generatePromptPath,
|
|
401
|
-
});
|
|
402
|
-
process.stdout.write(prompt + '\n');
|
|
403
|
-
await exitWithAnalytics(0);
|
|
404
|
-
}
|
|
405
|
-
const binary = resolveBinary(agent);
|
|
406
|
-
if (!(await assertBinaryInPath(binary))) {
|
|
407
|
-
printFallbackInstructions({
|
|
408
|
-
agent,
|
|
409
|
-
skill,
|
|
410
|
-
sessionId: sessionId ?? '',
|
|
411
|
-
});
|
|
412
|
-
await exitWithAnalytics(1);
|
|
413
|
-
}
|
|
414
|
-
if (skill === 'components' && allComponents && sessionId) {
|
|
415
|
-
const db = openPipelineDb();
|
|
416
|
-
let componentResults;
|
|
417
|
-
try {
|
|
418
|
-
const promptHash = await hashPromptForSkill('components', agent, model, generatePromptPath);
|
|
419
|
-
componentResults = await runAllComponents(agent, model, db, sessionId, allComponents, tokensInline, tokenMapInline, verbose, opts.cache === false || process.env.EDS_NO_CACHE === '1', generatePromptPath, promptHash);
|
|
420
|
-
}
|
|
421
|
-
finally {
|
|
422
|
-
db.close();
|
|
423
|
-
}
|
|
424
|
-
const failed = componentResults.filter((r) => r.failed);
|
|
425
|
-
const cachedResults = componentResults.filter((r) => r.cached);
|
|
426
|
-
const generated = componentResults.filter((r) => !r.failed && !r.cached);
|
|
427
|
-
const allWarnings = componentResults.flatMap((r) => r.warnings.map((w) => ` ${r.componentName}: ${w}`));
|
|
428
|
-
if (allWarnings.length > 0) {
|
|
429
|
-
process.stderr.write(c.yellow('Warnings:') + '\n' + allWarnings.join('\n') + '\n');
|
|
430
|
-
}
|
|
431
|
-
if (failed.length > 0) {
|
|
432
|
-
process.stderr.write(c.red(`Failed (${failed.length}/${componentResults.length}):`) + '\n');
|
|
433
|
-
for (const f of failed) {
|
|
434
|
-
process.stderr.write(` ${c.red('✗')} ${f.componentName} ${c.dim(f.error ?? 'unknown error')}\n`);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
const totalClassified = generated.reduce((s, r) => s + r.classified, 0);
|
|
438
|
-
const totalExcluded = generated.reduce((s, r) => s + r.excluded, 0);
|
|
439
|
-
const totalRenamedSlots = componentResults.reduce((s, r) => s + r.renamedSlotsCount, 0);
|
|
440
|
-
const allOk = failed.length === 0;
|
|
441
|
-
const cachedNote = cachedResults.length > 0 ? c.dim(` (${cachedResults.length} cached)`) : '';
|
|
442
|
-
process.stderr.write((allOk ? c.green('✓') : c.yellow('⚠')) +
|
|
443
|
-
` ${generated.length + cachedResults.length}/${componentResults.length} components` +
|
|
444
|
-
cachedNote +
|
|
445
|
-
c.dim(` ${totalClassified} classified, ${totalExcluded} excluded`) +
|
|
446
|
-
'\n');
|
|
447
|
-
// Machine-parseable summary on stdout for the wizard / orchestrator.
|
|
448
|
-
process.stdout.write(`renamed-slots: ${totalRenamedSlots}\n`);
|
|
449
|
-
if (generated.length === 0 && cachedResults.length === 0) {
|
|
450
|
-
die(`Error: all ${componentResults.length} component(s) failed to generate — see the per-component errors above.`);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
else if (skill === 'tokens') {
|
|
454
|
-
const noCache = opts.cache === false || process.env.EDS_NO_CACHE === '1';
|
|
455
|
-
const tokenInputContent = rawTokensInline ?? '';
|
|
456
|
-
const tokenInputHash = computeTokenInputHash(tokenInputContent);
|
|
457
|
-
const db = openPipelineDb();
|
|
458
|
-
try {
|
|
459
|
-
let resolvedSessionId = opts.session;
|
|
460
|
-
if (!resolvedSessionId) {
|
|
461
|
-
const s = db
|
|
462
|
-
.prepare(`SELECT s.id FROM sessions s
|
|
463
|
-
JOIN steps st ON st.session_id = s.id
|
|
464
|
-
WHERE st.command = 'analyze extract' AND st.status = 'complete'
|
|
465
|
-
ORDER BY st.started_at DESC LIMIT 1`)
|
|
466
|
-
.get();
|
|
467
|
-
if (s) {
|
|
468
|
-
resolvedSessionId = s.id;
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
const { generateSessionId } = await import('../session/session-id.js');
|
|
472
|
-
const newId = generateSessionId();
|
|
473
|
-
const now = new Date().toISOString();
|
|
474
|
-
db.prepare('INSERT INTO sessions (id, name, created_at, updated_at) VALUES (?, NULL, ?, ?)').run(newId, now, now);
|
|
475
|
-
resolvedSessionId = newId;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
sessionId = resolvedSessionId;
|
|
479
|
-
await bindAnalyticsSessionId(resolvedSessionId);
|
|
480
|
-
const tokenPromptHash = await hashPromptForSkill('tokens', agent, model);
|
|
481
|
-
// Check cache before invoking agent
|
|
482
|
-
if (!noCache) {
|
|
483
|
-
const tokenCached = lookupCache(db, tokenInputHash, 'token_set', '__tokens__', tokenPromptHash);
|
|
484
|
-
if (tokenCached) {
|
|
485
|
-
copyTokensFromCache(db, tokenCached.sourceSessionId, resolvedSessionId);
|
|
486
|
-
sessionId = resolvedSessionId;
|
|
487
|
-
process.stderr.write(`Done: tokens reused from cache ${c.dim(`(source: ${tokenCached.sourceSessionId.slice(0, 12)})`)}\n`);
|
|
488
|
-
db.close();
|
|
489
|
-
// Skip agent invocation — jump to view
|
|
490
|
-
const viewResult = { skill, agent, sessionId: sessionId ?? '' };
|
|
491
|
-
if (process.stdout.isTTY) {
|
|
492
|
-
const { waitUntilExit } = render(createElement(GenerateView, { result: viewResult, onExit: () => void exitWithAnalytics(0) }));
|
|
493
|
-
await waitUntilExit();
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
process.stdout.write(`generate complete\nskill: ${skill}\nagent: ${agent}\nsession=${sessionId ?? ''}\n`);
|
|
497
|
-
await exitWithAnalytics(0);
|
|
498
|
-
}
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
// Cache miss — invoke agent
|
|
503
|
-
const prompt = await buildPrompt({
|
|
504
|
-
skill,
|
|
505
|
-
mode: 'autonomous',
|
|
506
|
-
rawTokensInline,
|
|
507
|
-
rawTokensFilename: opts.rawTokens ? resolve(opts.rawTokens).split('/').pop() : undefined,
|
|
508
|
-
tokensInline,
|
|
509
|
-
tokenMapInline,
|
|
510
|
-
outDir: process.cwd(),
|
|
511
|
-
});
|
|
512
|
-
const result = await invoker.invoke({
|
|
513
|
-
agent,
|
|
514
|
-
model,
|
|
515
|
-
prompt,
|
|
516
|
-
timeoutMs: DEFAULT_TIMEOUT_MS * 5,
|
|
517
|
-
});
|
|
518
|
-
if (result.timedOut) {
|
|
519
|
-
die(`Error: agent did not complete within ${(DEFAULT_TIMEOUT_MS * 5) / 60000} minutes`);
|
|
520
|
-
}
|
|
521
|
-
if (result.exitCode !== 0) {
|
|
522
|
-
if (result.stderr)
|
|
523
|
-
process.stderr.write(result.stderr);
|
|
524
|
-
die(`Error: agent exited with code ${result.exitCode}`);
|
|
525
|
-
}
|
|
526
|
-
const { calls: tokenCalls, warnings: tokenWarnings } = parseTokenToolCallLines(result.stdout);
|
|
527
|
-
const tokenCount = tokenCalls.filter((tc) => tc.tool === 'set_token').length;
|
|
528
|
-
if (tokenCount === 0) {
|
|
529
|
-
process.stderr.write(`Error: agent produced no set_token calls.\n` +
|
|
530
|
-
`Run with --dry-run to inspect the prompt.\n\n` +
|
|
531
|
-
`Agent output:\n${result.stdout}\n`);
|
|
532
|
-
await exitWithAnalytics(1);
|
|
533
|
-
}
|
|
534
|
-
if (tokenWarnings.length > 0) {
|
|
535
|
-
process.stderr.write(`Warnings:\n${tokenWarnings.map((w) => ` ${w}`).join('\n')}\n`);
|
|
536
|
-
}
|
|
537
|
-
applyTokenToolCalls(db, resolvedSessionId, tokenCalls, []);
|
|
538
|
-
if (!noCache) {
|
|
539
|
-
storeCache(db, tokenInputHash, 'token_set', '__tokens__', resolvedSessionId, false, tokenPromptHash);
|
|
540
|
-
}
|
|
541
|
-
sessionId = resolvedSessionId;
|
|
542
|
-
const groupCount = tokenCalls.filter((tc) => tc.tool === 'set_group').length;
|
|
543
|
-
process.stderr.write(`Done: ${tokenCount} tokens, ${groupCount} groups stored\n`);
|
|
544
|
-
}
|
|
545
|
-
finally {
|
|
546
|
-
db.close();
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
const viewResult = {
|
|
550
|
-
skill,
|
|
551
|
-
agent,
|
|
552
|
-
sessionId: sessionId ?? '',
|
|
553
|
-
};
|
|
554
|
-
if (process.stdout.isTTY) {
|
|
555
|
-
const { waitUntilExit } = render(createElement(GenerateView, {
|
|
556
|
-
result: viewResult,
|
|
557
|
-
onExit: () => void exitWithAnalytics(0),
|
|
558
|
-
}));
|
|
559
|
-
await waitUntilExit();
|
|
560
|
-
}
|
|
561
|
-
else {
|
|
562
|
-
process.stdout.write(`generate complete\nskill: ${skill}\nagent: ${agent}\nsession=${sessionId ?? ''}\n`);
|
|
563
|
-
await exitWithAnalytics(0);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
function addAgentFlags(cmd) {
|
|
567
|
-
return addAgentModelOptions(cmd)
|
|
568
|
-
.option('--verbose', 'Show full agent output including reasoning text')
|
|
569
|
-
.option('--dry-run', 'Print the prompt without invoking the agent')
|
|
570
|
-
.option('--no-cache', 'Bypass ALL fine-grained caches (extract, select, generate) and force AI re-run. ' +
|
|
571
|
-
'Cache keys now factor in prompt content — changing the prompt file via --generate-prompt-path or ' +
|
|
572
|
-
'--select-prompt-path will already bust the corresponding stage. Use --no-cache to force a full re-run.');
|
|
573
|
-
}
|
|
574
|
-
export function registerGenerateCommand(program) {
|
|
575
|
-
const generate = program.command('generate').description('Generate CDF/DTCG artifacts or correct generation output');
|
|
576
|
-
// generate components subcommand
|
|
577
|
-
const componentsCmd = generate
|
|
578
|
-
.command('components')
|
|
579
|
-
.description('Invoke a coding agent to produce components.json from raw analysis output')
|
|
580
|
-
.option('--session <id>', 'Session ID from analyze extract (defaults to most recent)')
|
|
581
|
-
.option('--tokens <path>', 'Path to tokens.json for token-linked prop resolution')
|
|
582
|
-
.option('--token-map <path>', 'Path to token-name-map.json sidecar')
|
|
583
|
-
.option('--generate-prompt-path <path>', 'Path to a custom .md skill prompt for components generation (bypasses bundled prompt invariants)');
|
|
584
|
-
addAgentFlags(componentsCmd).action(async (opts) => {
|
|
585
|
-
await runGenerateSkill('components', opts, opts.verbose ?? false);
|
|
586
|
-
});
|
|
587
|
-
registerGenerateEditCommand(componentsCmd, 'components');
|
|
588
|
-
// generate tokens subcommand
|
|
589
|
-
const tokensCmd = generate
|
|
590
|
-
.command('tokens')
|
|
591
|
-
.description('Invoke a coding agent to produce tokens.json from raw token data')
|
|
592
|
-
.option('--raw-tokens <path>', 'Path to raw token input file');
|
|
593
|
-
addAgentFlags(tokensCmd).action(async (opts) => {
|
|
594
|
-
await runGenerateSkill('tokens', opts, opts.verbose ?? false);
|
|
595
|
-
});
|
|
596
|
-
registerGenerateEditCommand(tokensCmd, 'tokens');
|
|
597
|
-
}
|