@contentful/experience-design-system-cli 2.14.2 → 2.15.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 +64 -2
- package/dist/package.json +3 -2
- package/dist/src/analyze/command.d.ts +11 -0
- package/dist/src/analyze/command.js +298 -3
- package/dist/src/analyze/composite-closure.d.ts +25 -0
- package/dist/src/analyze/composite-closure.js +0 -0
- package/dist/src/analyze/composition/agent-parser/author-prompt.d.ts +4 -0
- package/dist/src/analyze/composition/agent-parser/author-prompt.js +22 -0
- package/dist/src/analyze/composition/agent-parser/extract-parser.d.ts +8 -0
- package/dist/src/analyze/composition/agent-parser/extract-parser.js +44 -0
- package/dist/src/analyze/composition/agent-parser/load-prompt.d.ts +7 -0
- package/dist/src/analyze/composition/agent-parser/load-prompt.js +31 -0
- package/dist/src/analyze/composition/agent-parser/resolve-via-parser.d.ts +46 -0
- package/dist/src/analyze/composition/agent-parser/resolve-via-parser.js +88 -0
- package/dist/src/analyze/composition/agent-parser/sandbox.d.ts +45 -0
- package/dist/src/analyze/composition/agent-parser/sandbox.js +187 -0
- package/dist/src/analyze/composition/apply-mapping.d.ts +22 -0
- package/dist/src/analyze/composition/apply-mapping.js +67 -0
- package/dist/src/analyze/composition/candidate-critic-agent.d.ts +7 -0
- package/dist/src/analyze/composition/candidate-critic-agent.js +39 -0
- package/dist/src/analyze/composition/candidate-critic.d.ts +32 -0
- package/dist/src/analyze/composition/candidate-critic.js +63 -0
- package/dist/src/analyze/composition/candidate-files.d.ts +34 -0
- package/dist/src/analyze/composition/candidate-files.js +131 -0
- package/dist/src/analyze/composition/composition-cache-key.d.ts +19 -0
- package/dist/src/analyze/composition/composition-cache-key.js +19 -0
- package/dist/src/analyze/composition/interchange-schema.d.ts +37 -0
- package/dist/src/analyze/composition/interchange-schema.js +61 -0
- package/dist/src/analyze/composition/merge-edges.d.ts +19 -0
- package/dist/src/analyze/composition/merge-edges.js +50 -0
- package/dist/src/analyze/composition/parse-map-edges.d.ts +16 -0
- package/dist/src/analyze/composition/parse-map-edges.js +55 -0
- package/dist/src/analyze/composition/resolve-mapping-cli.d.ts +23 -0
- package/dist/src/analyze/composition/resolve-mapping-cli.js +29 -0
- package/dist/src/analyze/composition/resolve-mapping.d.ts +59 -0
- package/dist/src/analyze/composition/resolve-mapping.js +94 -0
- package/dist/src/analyze/cycle-detection.d.ts +27 -0
- package/dist/src/analyze/cycle-detection.js +234 -0
- package/dist/src/analyze/cycle-view.d.ts +7 -0
- package/dist/src/analyze/cycle-view.js +28 -0
- package/dist/src/analyze/fuzzy-search.d.ts +3 -0
- package/dist/src/analyze/fuzzy-search.js +55 -0
- package/dist/src/analyze/issue-inheritance.d.ts +12 -0
- package/dist/src/analyze/issue-inheritance.js +108 -0
- package/dist/src/analyze/lineage.d.ts +29 -0
- package/dist/src/analyze/lineage.js +0 -0
- package/dist/src/analyze/scope-gate-cascade.d.ts +11 -0
- package/dist/src/analyze/scope-gate-cascade.js +170 -0
- package/dist/src/analyze/search-neighborhood.d.ts +3 -0
- package/dist/src/analyze/search-neighborhood.js +56 -0
- package/dist/src/analyze/select/tui/App.js +5 -16
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.d.ts +0 -4
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +3 -15
- package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +29 -38
- package/dist/src/analyze/select/tui/components/FieldEditor.js +309 -185
- package/dist/src/analyze/select/tui/components/FinalizeDialog.d.ts +13 -1
- package/dist/src/analyze/select/tui/components/FinalizeDialog.js +23 -2
- package/dist/src/analyze/select/tui/components/GotoBanner.d.ts +31 -0
- package/dist/src/analyze/select/tui/components/GotoBanner.js +30 -0
- package/dist/src/analyze/select/tui/components/GroupedSidebar.d.ts +91 -0
- package/dist/src/analyze/select/tui/components/GroupedSidebar.js +497 -0
- package/dist/src/analyze/select/tui/components/HelpOverlay.d.ts +13 -1
- package/dist/src/analyze/select/tui/components/HelpOverlay.js +9 -3
- package/dist/src/analyze/select/tui/components/JsonEditor.js +3 -3
- package/dist/src/analyze/select/tui/components/LineagePanel.d.ts +14 -0
- package/dist/src/analyze/select/tui/components/LineagePanel.js +22 -0
- package/dist/src/analyze/select/tui/components/RationalePanel.d.ts +0 -7
- package/dist/src/analyze/select/tui/components/RationalePanel.js +3 -15
- package/dist/src/analyze/select/tui/components/Sidebar.d.ts +0 -6
- package/dist/src/analyze/select/tui/components/Sidebar.js +11 -27
- package/dist/src/analyze/select/tui/components/StatusBar.js +2 -1
- package/dist/src/analyze/select/tui/components/removed-components-text.d.ts +7 -0
- package/dist/src/analyze/select/tui/components/removed-components-text.js +11 -0
- package/dist/src/analyze/select/tui/hooks/useImmediateInput.d.ts +1 -5
- package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +8 -10
- package/dist/src/analyze/select/tui/theme.d.ts +15 -0
- package/dist/src/analyze/select/tui/theme.js +28 -0
- package/dist/src/analyze/selection-cascade.d.ts +3 -0
- package/dist/src/analyze/selection-cascade.js +19 -0
- package/dist/src/analyze/slot-graph.d.ts +11 -0
- package/dist/src/analyze/slot-graph.js +17 -0
- package/dist/src/apply/api-client.d.ts +2 -1
- package/dist/src/apply/api-client.js +40 -6
- package/dist/src/apply/command.d.ts +17 -1
- package/dist/src/apply/command.js +69 -11
- package/dist/src/apply/error-parser.d.ts +16 -0
- package/dist/src/apply/error-parser.js +117 -0
- package/dist/src/apply/tui/ServerPreviewView.js +3 -1
- package/dist/src/credentials-store.d.ts +6 -0
- package/dist/src/credentials-store.js +5 -1
- package/dist/src/generate/agent-runner.d.ts +6 -0
- package/dist/src/generate/agent-runner.js +24 -8
- package/dist/src/import/command.js +69 -31
- package/dist/src/import/cycle-auto-reject.d.ts +4 -0
- package/dist/src/import/cycle-auto-reject.js +14 -0
- package/dist/src/import/orchestrator.d.ts +20 -7
- package/dist/src/import/orchestrator.js +139 -35
- package/dist/src/import/strip-allowed-components.d.ts +18 -0
- package/dist/src/import/strip-allowed-components.js +22 -0
- package/dist/src/import/tui/CustomPromptBanner.d.ts +0 -5
- package/dist/src/import/tui/CustomPromptBanner.js +2 -1
- package/dist/src/import/tui/WizardApp.d.ts +10 -74
- package/dist/src/import/tui/WizardApp.js +234 -246
- package/dist/src/import/tui/ai-flag.d.ts +5 -0
- package/dist/src/import/tui/ai-flag.js +6 -0
- package/dist/src/import/tui/autocomplete.d.ts +5 -0
- package/dist/src/import/tui/autocomplete.js +32 -0
- package/dist/src/import/tui/components/AutoFilterBanner.d.ts +10 -0
- package/dist/src/import/tui/components/AutoFilterBanner.js +19 -0
- package/dist/src/import/tui/components/CounterStrip.d.ts +12 -0
- package/dist/src/import/tui/components/CounterStrip.js +12 -0
- package/dist/src/import/tui/components/LegendEntry.d.ts +2 -0
- package/dist/src/import/tui/components/LegendEntry.js +6 -0
- package/dist/src/import/tui/cycle-panel-scroll.d.ts +8 -0
- package/dist/src/import/tui/cycle-panel-scroll.js +33 -0
- package/dist/src/import/tui/final-review-host.d.ts +3 -7
- package/dist/src/import/tui/final-review-host.js +9 -4
- package/dist/src/import/tui/group-collapse.d.ts +2 -0
- package/dist/src/import/tui/group-collapse.js +11 -0
- package/dist/src/import/tui/history.d.ts +22 -0
- package/dist/src/import/tui/history.js +53 -0
- package/dist/src/import/tui/hooks/useLineage.d.ts +32 -0
- package/dist/src/import/tui/hooks/useLineage.js +50 -0
- package/dist/src/import/tui/hooks/useOverlayPanel.d.ts +13 -0
- package/dist/src/import/tui/hooks/useOverlayPanel.js +27 -0
- package/dist/src/import/tui/lineage-layout.d.ts +75 -0
- package/dist/src/import/tui/lineage-layout.js +82 -0
- package/dist/src/import/tui/runLivePreview.d.ts +11 -0
- package/dist/src/import/tui/runLivePreview.js +7 -1
- package/dist/src/import/tui/scope-gate-columns.d.ts +31 -0
- package/dist/src/import/tui/scope-gate-columns.js +94 -0
- package/dist/src/import/tui/scope-gate-host.d.ts +3 -1
- package/dist/src/import/tui/scope-gate-host.js +11 -3
- package/dist/src/import/tui/sidebar-width.d.ts +1 -0
- package/dist/src/import/tui/sidebar-width.js +3 -0
- package/dist/src/import/tui/step-filters.d.ts +19 -0
- package/dist/src/import/tui/step-filters.js +42 -0
- package/dist/src/import/tui/steps/AtomicGenerateReviewStep.d.ts +42 -0
- package/dist/src/import/tui/steps/AtomicGenerateReviewStep.js +726 -0
- package/dist/src/import/tui/steps/AtomicScopeGateStep.d.ts +3 -0
- package/dist/src/import/tui/steps/AtomicScopeGateStep.js +211 -0
- package/dist/src/import/tui/steps/CredentialsStep.js +5 -18
- package/dist/src/import/tui/steps/DoneStep.d.ts +0 -2
- package/dist/src/import/tui/steps/DoneStep.js +3 -2
- package/dist/src/import/tui/steps/ErrorStep.js +2 -1
- package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +19 -25
- package/dist/src/import/tui/steps/GenerateReviewStep.js +1258 -229
- package/dist/src/import/tui/steps/PathValidationStep.js +3 -2
- package/dist/src/import/tui/steps/PreviewStep.js +2 -1
- package/dist/src/import/tui/steps/PushDecisionGateStep.d.ts +0 -7
- package/dist/src/import/tui/steps/PushDecisionGateStep.js +3 -6
- package/dist/src/import/tui/steps/PushingStep.js +2 -1
- package/dist/src/import/tui/steps/RunningStep.d.ts +4 -1
- package/dist/src/import/tui/steps/RunningStep.js +3 -2
- package/dist/src/import/tui/steps/ScopeGateStep.d.ts +24 -0
- package/dist/src/import/tui/steps/ScopeGateStep.js +850 -193
- package/dist/src/import/tui/steps/TokenInputStep.js +2 -1
- package/dist/src/import/tui/steps/WelcomeStep.js +2 -1
- package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +6 -0
- package/dist/src/import/tui/steps/WizardPreviewStep.js +95 -56
- package/dist/src/import/tui/steps/auto-reject-decision.d.ts +7 -0
- package/dist/src/import/tui/steps/auto-reject-decision.js +9 -0
- package/dist/src/import/tui/steps/breaking-change-format.d.ts +2 -0
- package/dist/src/import/tui/steps/breaking-change-format.js +24 -0
- package/dist/src/import/tui/steps/enumerate-cycle-breaks.d.ts +14 -0
- package/dist/src/import/tui/steps/enumerate-cycle-breaks.js +0 -0
- package/dist/src/import/tui/steps/preview-diff.js +48 -5
- package/dist/src/import/tui/useFinalizePreview.d.ts +34 -0
- package/dist/src/import/tui/useFinalizePreview.js +0 -0
- package/dist/src/import/tui/useLivePreview.d.ts +3 -0
- package/dist/src/import/tui/useLivePreview.js +1 -0
- package/dist/src/import/tui/wizard-state-transitions.d.ts +5 -59
- package/dist/src/import/tui/wizard-state-transitions.js +5 -60
- package/dist/src/lib/composition-mode.d.ts +28 -0
- package/dist/src/lib/composition-mode.js +37 -0
- package/dist/src/lib/prompt-overrides.d.ts +38 -0
- package/dist/src/lib/prompt-overrides.js +69 -0
- package/dist/src/print/command.js +19 -1
- package/dist/src/runs/modify-launcher.d.ts +3 -0
- package/dist/src/runs/modify-launcher.js +2 -0
- package/dist/src/runs/replay-helpers.js +1 -0
- package/dist/src/runs/run-picker.js +2 -18
- package/dist/src/runs/store.d.ts +4 -0
- package/dist/src/session/db.d.ts +23 -40
- package/dist/src/session/db.js +110 -138
- package/dist/src/setup/composition-mode-prompt.d.ts +14 -0
- package/dist/src/setup/composition-mode-prompt.js +37 -0
- package/package.json +5 -4
- package/prompts/composition-dir-critic.md +7 -0
- package/prompts/composition-edges.md +8 -0
- package/prompts/composition-parser-repair-empty.md +1 -0
- package/prompts/composition-parser-repair-error.md +2 -0
- package/prompts/composition-parser.md +42 -0
|
@@ -1,45 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure state-machine helpers for the wizard. Extracted from WizardApp.tsx so the
|
|
3
|
-
* transitions can be unit-tested without rendering ink.
|
|
4
|
-
*
|
|
5
|
-
* Bug fix (2026-06): credentials moved BEFORE generate so:
|
|
6
|
-
* 1. Bad creds (401/403) are caught upfront — before the operator pays the cost
|
|
7
|
-
* of extract + LLM classification (potentially many minutes).
|
|
8
|
-
* 2. Live preview in final-review actually fires, because the validated creds
|
|
9
|
-
* are present in WizardApp state when GenerateReviewStep mounts.
|
|
10
|
-
*
|
|
11
|
-
* New order:
|
|
12
|
-
* welcome → token-input → path-validation → extracting → scope-gate
|
|
13
|
-
* → credentials (validates inline) → generating
|
|
14
|
-
* → generating → final-review → push-decision-gate → previewing
|
|
15
|
-
* → preview-gate → pushing → done
|
|
16
|
-
*
|
|
17
|
-
* `--no-push` short-circuits the credentials/preview/push branch — the operator
|
|
18
|
-
* just wants extract + classify + final-review without ever touching Contentful.
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
|
-
* Transition target after the scope-gate confirms.
|
|
22
|
-
*
|
|
23
|
-
* - acceptedCount > 0:
|
|
24
|
-
* - !noPush → `credentials` (gather + validate creds upfront, before LLM cost)
|
|
25
|
-
* - noPush → `generating` (skip creds entirely; live preview no-ops gracefully)
|
|
26
|
-
* - acceptedCount === 0 (everything rejected):
|
|
27
|
-
* - !noPush → `credentials` (still need creds to push tokens / removals)
|
|
28
|
-
* - noPush → `print-gate` (nothing to do; let the operator save files and exit)
|
|
29
|
-
*/
|
|
30
1
|
export function nextStepAfterScopeGate(opts) {
|
|
31
2
|
if (opts.acceptedCount > 0) {
|
|
32
3
|
return opts.noPush ? 'generating' : 'credentials';
|
|
33
4
|
}
|
|
34
5
|
return opts.noPush ? 'print-gate' : 'credentials';
|
|
35
6
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Transition target after credentials are validated (or skipped via the
|
|
38
|
-
* inline validation pings the API). Drives the post-credentials branch:
|
|
39
|
-
* - acceptedCount > 0 → `generating` (run the generator now that creds are good)
|
|
40
|
-
* - acceptedCount === 0 → `push-decision-gate` (no components to generate;
|
|
41
|
-
* skip straight to push-decision and preview)
|
|
42
|
-
*/
|
|
43
7
|
export function nextStepAfterCredentialsValidated(opts) {
|
|
44
8
|
return opts.acceptedCount > 0 ? 'generating' : 'push-decision-gate';
|
|
45
9
|
}
|
|
@@ -47,38 +11,19 @@ export function shouldSkipFinalReviewAfterCredentials(state) {
|
|
|
47
11
|
return state.finalReviewPassed && state.generateSessionId != null;
|
|
48
12
|
}
|
|
49
13
|
export function resolveNoCacheForGenerate(opts) {
|
|
50
|
-
if (opts.isFreshSession)
|
|
51
|
-
return true;
|
|
52
14
|
return opts.cliNoCache;
|
|
53
15
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Skip-credentials spec — Task 2. When the operator pressed `s` on the
|
|
56
|
-
* credentials screen, the wizard advanced without validating creds. That
|
|
57
|
-
* makes `previewImport` impossible (no working token) and undesirable (the
|
|
58
|
-
* whole point is to inspect locally without touching the server). Callers
|
|
59
|
-
* check this before issuing the API call and route to
|
|
60
|
-
* `buildSkippedPreviewTransition()` instead.
|
|
61
|
-
*/
|
|
62
16
|
export function shouldBypassPreview(state) {
|
|
63
17
|
return state.credentialsSkipped === true;
|
|
64
18
|
}
|
|
65
|
-
/**
|
|
66
|
-
* State patch applied when `shouldBypassPreview` is true: skip the preview
|
|
67
|
-
* step entirely, surface no server preview to downstream consumers, and
|
|
68
|
-
* hand control to the push-decision-gate (which will render with push
|
|
69
|
-
* disabled — see Task 3).
|
|
70
|
-
*/
|
|
71
19
|
export function buildSkippedPreviewTransition() {
|
|
72
20
|
return { step: 'push-decision-gate', serverPreview: null };
|
|
73
21
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* refuses to issue the API call — instead it routes back to the print-files
|
|
80
|
-
* local-save path via `buildSkippedPushTransition`.
|
|
81
|
-
*/
|
|
22
|
+
export function resolveCycleGateAction(opts) {
|
|
23
|
+
if (!opts.hasCycles)
|
|
24
|
+
return 'proceed';
|
|
25
|
+
return opts.autoRejectCycles ? 'auto-reject' : 'block';
|
|
26
|
+
}
|
|
82
27
|
export function shouldRefusePush(state) {
|
|
83
28
|
return state.credentialsSkipped === true;
|
|
84
29
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type CompositionMode = 'composite' | 'atomic';
|
|
2
|
+
/**
|
|
3
|
+
* Options that, when present, IMPLY composite mode — passing any of them is a
|
|
4
|
+
* clear opt-in, so requiring an explicit `--composite` alongside is redundant.
|
|
5
|
+
*/
|
|
6
|
+
export type CompositionSourceOptions = {
|
|
7
|
+
compositionMap?: string;
|
|
8
|
+
compositionAgent?: boolean;
|
|
9
|
+
compositionRefresh?: boolean;
|
|
10
|
+
generateMap?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the effective composition mode with precedence
|
|
14
|
+
* `explicit flag > implied source > env > persisted config > default`.
|
|
15
|
+
* Default is `atomic` (composition OFF); `--composite` opts into the hierarchy
|
|
16
|
+
* machinery.
|
|
17
|
+
*
|
|
18
|
+
* `--atomic` is accepted for symmetry and wins when passed — even alongside a
|
|
19
|
+
* composition source, since it's an explicit "no" that overrides the implicit
|
|
20
|
+
* opt-in. If both `--composite` and `--atomic` are present, `--composite`
|
|
21
|
+
* wins. Passing a composition source (`--composition-map/-agent`,
|
|
22
|
+
* `--composition-refresh`, or `--generate-map`) implies composite without a
|
|
23
|
+
* separate `--composite`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveCompositionMode(opts: {
|
|
26
|
+
composite?: boolean;
|
|
27
|
+
atomic?: boolean;
|
|
28
|
+
} & CompositionSourceOptions, configMode?: CompositionMode): CompositionMode;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
function hasCompositionSource(opts) {
|
|
2
|
+
return !!opts.compositionMap || !!opts.compositionAgent || !!opts.compositionRefresh || !!opts.generateMap;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the effective composition mode with precedence
|
|
6
|
+
* `explicit flag > implied source > env > persisted config > default`.
|
|
7
|
+
* Default is `atomic` (composition OFF); `--composite` opts into the hierarchy
|
|
8
|
+
* machinery.
|
|
9
|
+
*
|
|
10
|
+
* `--atomic` is accepted for symmetry and wins when passed — even alongside a
|
|
11
|
+
* composition source, since it's an explicit "no" that overrides the implicit
|
|
12
|
+
* opt-in. If both `--composite` and `--atomic` are present, `--composite`
|
|
13
|
+
* wins. Passing a composition source (`--composition-map/-agent`,
|
|
14
|
+
* `--composition-refresh`, or `--generate-map`) implies composite without a
|
|
15
|
+
* separate `--composite`.
|
|
16
|
+
*/
|
|
17
|
+
export function resolveCompositionMode(opts, configMode) {
|
|
18
|
+
if (opts.composite)
|
|
19
|
+
return 'composite';
|
|
20
|
+
if (opts.atomic)
|
|
21
|
+
return 'atomic';
|
|
22
|
+
// A composition source is an explicit opt-in for this invocation — it beats
|
|
23
|
+
// env/config, but not an explicit --atomic (handled above).
|
|
24
|
+
if (hasCompositionSource(opts))
|
|
25
|
+
return 'composite';
|
|
26
|
+
const env = process.env['EXPERIENCES_COMPOSITION_MODE'];
|
|
27
|
+
if (env !== undefined && env !== '') {
|
|
28
|
+
const v = env.toLowerCase();
|
|
29
|
+
if (v === 'composite')
|
|
30
|
+
return 'composite';
|
|
31
|
+
if (v === 'atomic')
|
|
32
|
+
return 'atomic';
|
|
33
|
+
}
|
|
34
|
+
if (configMode === 'composite' || configMode === 'atomic')
|
|
35
|
+
return configMode;
|
|
36
|
+
return 'atomic';
|
|
37
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic `--prompt <stage>=<value>` override parser.
|
|
3
|
+
*
|
|
4
|
+
* One repeatable flag overrides the prompt for any named pipeline stage
|
|
5
|
+
* (e.g. `composition`, `select`, `generate`). The value is classified by
|
|
6
|
+
* string SHAPE only — no filesystem access here: a value that looks like a
|
|
7
|
+
* path is resolved+read by the consumer; otherwise it is treated as the
|
|
8
|
+
* literal prompt text. This keeps the parser pure and testable.
|
|
9
|
+
*/
|
|
10
|
+
export type PromptOverride = {
|
|
11
|
+
kind: 'path';
|
|
12
|
+
value: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'text';
|
|
15
|
+
value: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Heuristic: does this value look like a file path (vs. literal prompt text)?
|
|
19
|
+
* Path if it contains a path separator, starts with `~`, or ends with a known
|
|
20
|
+
* prompt-file extension. Deliberately does NOT touch the filesystem.
|
|
21
|
+
*/
|
|
22
|
+
export declare function looksLikePath(value: string): boolean;
|
|
23
|
+
export type ParsePromptOverridesResult = {
|
|
24
|
+
overrides: Map<string, PromptOverride>;
|
|
25
|
+
errors: string[];
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Parse repeated `stage=value` flag inputs into a stage→override map. Splits on
|
|
29
|
+
* the FIRST `=` so values may contain `=`. Last write wins for a repeated
|
|
30
|
+
* stage. Malformed entries are collected in `errors` (caller decides fatality).
|
|
31
|
+
*/
|
|
32
|
+
export declare function parsePromptOverrides(inputs: string[]): ParsePromptOverridesResult;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve an override to its prompt text: read the file for a `path` override
|
|
35
|
+
* (resolved against cwd), or return the literal `text` value. Throws with a
|
|
36
|
+
* clear message if a path override can't be read.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolvePromptOverride(override: PromptOverride): Promise<string>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic `--prompt <stage>=<value>` override parser.
|
|
3
|
+
*
|
|
4
|
+
* One repeatable flag overrides the prompt for any named pipeline stage
|
|
5
|
+
* (e.g. `composition`, `select`, `generate`). The value is classified by
|
|
6
|
+
* string SHAPE only — no filesystem access here: a value that looks like a
|
|
7
|
+
* path is resolved+read by the consumer; otherwise it is treated as the
|
|
8
|
+
* literal prompt text. This keeps the parser pure and testable.
|
|
9
|
+
*/
|
|
10
|
+
import { readFile } from 'node:fs/promises';
|
|
11
|
+
import { resolve } from 'node:path';
|
|
12
|
+
const PROMPT_FILE_EXTENSIONS = ['.md', '.txt', '.prompt'];
|
|
13
|
+
/**
|
|
14
|
+
* Heuristic: does this value look like a file path (vs. literal prompt text)?
|
|
15
|
+
* Path if it contains a path separator, starts with `~`, or ends with a known
|
|
16
|
+
* prompt-file extension. Deliberately does NOT touch the filesystem.
|
|
17
|
+
*/
|
|
18
|
+
export function looksLikePath(value) {
|
|
19
|
+
if (value.includes('/') || value.includes('\\'))
|
|
20
|
+
return true;
|
|
21
|
+
if (value.startsWith('~'))
|
|
22
|
+
return true;
|
|
23
|
+
const lower = value.toLowerCase();
|
|
24
|
+
return PROMPT_FILE_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parse repeated `stage=value` flag inputs into a stage→override map. Splits on
|
|
28
|
+
* the FIRST `=` so values may contain `=`. Last write wins for a repeated
|
|
29
|
+
* stage. Malformed entries are collected in `errors` (caller decides fatality).
|
|
30
|
+
*/
|
|
31
|
+
export function parsePromptOverrides(inputs) {
|
|
32
|
+
const overrides = new Map();
|
|
33
|
+
const errors = [];
|
|
34
|
+
for (const raw of inputs) {
|
|
35
|
+
const eq = raw.indexOf('=');
|
|
36
|
+
if (eq === -1) {
|
|
37
|
+
errors.push(`--prompt "${raw}" must be in the form stage=value`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const stage = raw.slice(0, eq).trim();
|
|
41
|
+
const value = raw.slice(eq + 1);
|
|
42
|
+
if (stage === '') {
|
|
43
|
+
errors.push(`--prompt "${raw}" has an empty stage (expected stage=value)`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (value === '') {
|
|
47
|
+
errors.push(`--prompt "${raw}" has an empty value`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
overrides.set(stage, looksLikePath(value) ? { kind: 'path', value } : { kind: 'text', value });
|
|
51
|
+
}
|
|
52
|
+
return { overrides, errors };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve an override to its prompt text: read the file for a `path` override
|
|
56
|
+
* (resolved against cwd), or return the literal `text` value. Throws with a
|
|
57
|
+
* clear message if a path override can't be read.
|
|
58
|
+
*/
|
|
59
|
+
export async function resolvePromptOverride(override) {
|
|
60
|
+
if (override.kind === 'text')
|
|
61
|
+
return override.value;
|
|
62
|
+
const abs = resolve(override.value);
|
|
63
|
+
try {
|
|
64
|
+
return await readFile(abs, 'utf8');
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
throw new Error(`--prompt: could not read prompt file: ${abs}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -80,6 +80,7 @@ export function registerPrintCommand(program) {
|
|
|
80
80
|
.description('Write generated components to a CDF JSON file')
|
|
81
81
|
.option('--session <id>', 'Session ID to print from (default: most recent generate components session)')
|
|
82
82
|
.option('--out <path>', 'Output file path', 'components.json')
|
|
83
|
+
.option('--allow-empty', 'Write an empty-but-present components manifest when no components are accepted (a subsequent push then removes ALL components from the target space). Without this, an empty accepted set is an error.')
|
|
83
84
|
.action(async (opts) => {
|
|
84
85
|
const outPath = resolve(opts.out);
|
|
85
86
|
await assertOutIsNotDirectory(outPath);
|
|
@@ -87,18 +88,35 @@ export function registerPrintCommand(program) {
|
|
|
87
88
|
const db = openPipelineDb();
|
|
88
89
|
let components;
|
|
89
90
|
let generateStepStatus = null;
|
|
91
|
+
let rejectedCount = 0;
|
|
90
92
|
try {
|
|
91
93
|
components = loadCDFComponents(db, sessionId);
|
|
92
94
|
const stepRow = db
|
|
93
95
|
.prepare(`SELECT status FROM steps WHERE session_id = ? AND command = 'generate components' ORDER BY id DESC LIMIT 1`)
|
|
94
96
|
.get(sessionId);
|
|
95
97
|
generateStepStatus = stepRow?.status ?? null;
|
|
98
|
+
const rejectedRow = db
|
|
99
|
+
.prepare(`SELECT COUNT(*) AS n FROM raw_components WHERE session_id = ? AND status = 'generate-rejected'`)
|
|
100
|
+
.get(sessionId);
|
|
101
|
+
rejectedCount = rejectedRow?.n ?? 0;
|
|
96
102
|
}
|
|
97
103
|
finally {
|
|
98
104
|
db.close();
|
|
99
105
|
}
|
|
100
106
|
if (components.length === 0) {
|
|
101
|
-
|
|
107
|
+
if (rejectedCount > 0) {
|
|
108
|
+
// Components WERE generated, but final review left none accepted — every
|
|
109
|
+
// component was rejected or left unresolved. This is a legitimate
|
|
110
|
+
// "clear the space" intent, but it's destructive, so require --allow-empty.
|
|
111
|
+
if (!opts.allowEmpty) {
|
|
112
|
+
die(`Error: all ${rejectedCount} generated component${rejectedCount === 1 ? ' was' : 's were'} rejected or left unresolved at final review in session '${sessionId}', so there is nothing to save. Accept at least one component (press [a] on a row, or [A] to accept all), or pass --allow-empty to write an empty manifest that will DELETE all components from the target space on push.`);
|
|
113
|
+
}
|
|
114
|
+
// Fall through: write an empty-but-present components manifest so a
|
|
115
|
+
// subsequent push removes every component from the target space.
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
die(`Error: no generated components in session '${sessionId}'. Run generate components first.`);
|
|
119
|
+
}
|
|
102
120
|
}
|
|
103
121
|
if (generateStepStatus === 'failed') {
|
|
104
122
|
process.stderr.write(`Warning: session '${sessionId}' generate step failed — output may be incomplete (${components.length} components found)\n`);
|
|
@@ -13,6 +13,9 @@ export type ModifyLauncherInput = {
|
|
|
13
13
|
savePath: string;
|
|
14
14
|
entryStep: 'scope-gate' | 'final-review';
|
|
15
15
|
saveMode: 'overwrite' | 'new' | 'prompt';
|
|
16
|
+
/** Composition mode from the run record, so the modify wizard resumes in the
|
|
17
|
+
* same mode. Omitted → the wizard's default (`atomic`). */
|
|
18
|
+
compositionMode?: 'composite' | 'atomic';
|
|
16
19
|
outDirOverride?: string;
|
|
17
20
|
/** Pre-fill space id (from the run record's pushedTo). */
|
|
18
21
|
initialSpaceId?: string;
|
|
@@ -21,6 +21,8 @@ export async function launchModifyWizard(input) {
|
|
|
21
21
|
props.seedGenerateSessionId = input.generateSessionId;
|
|
22
22
|
if (input.tokenSessionId)
|
|
23
23
|
props.seedTokenSessionId = input.tokenSessionId;
|
|
24
|
+
if (input.compositionMode)
|
|
25
|
+
props.compositionMode = input.compositionMode;
|
|
24
26
|
if (input.saveMode === 'overwrite')
|
|
25
27
|
props.outDirOverride = input.savePath;
|
|
26
28
|
if (input.outDirOverride)
|
|
@@ -151,6 +151,7 @@ export async function modifyRun(opts) {
|
|
|
151
151
|
savePath: run.savePath,
|
|
152
152
|
entryStep: 'final-review',
|
|
153
153
|
saveMode,
|
|
154
|
+
...(run.compositionMode ? { compositionMode: run.compositionMode } : {}),
|
|
154
155
|
...(opts.outDir ? { outDirOverride: resolve(opts.outDir) } : {}),
|
|
155
156
|
...(mergedSpaceId ? { initialSpaceId: mergedSpaceId } : {}),
|
|
156
157
|
...(mergedEnvironmentId ? { initialEnvironmentId: mergedEnvironmentId } : {}),
|
|
@@ -2,18 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { useImmediateInput } from '../analyze/select/tui/hooks/useImmediateInput.js';
|
|
5
|
-
const COLLAPSED_LIMIT =
|
|
6
|
-
|
|
7
|
-
* The picker collapses to top 3 + Show all only when there are 5+ total.
|
|
8
|
-
* Four or fewer runs render in full.
|
|
9
|
-
*/
|
|
10
|
-
const COLLAPSE_THRESHOLD = 5;
|
|
11
|
-
/**
|
|
12
|
-
* Format the createdAt timestamp as `YYYY-MM-DD HH:MM` in the operator's
|
|
13
|
-
* local timezone. We intentionally use local time (not UTC) because the
|
|
14
|
-
* picker is for human recognition — operators expect to see times in the
|
|
15
|
-
* same zone they were when the run happened.
|
|
16
|
-
*/
|
|
5
|
+
const COLLAPSED_LIMIT = 10;
|
|
6
|
+
const COLLAPSE_THRESHOLD = 12;
|
|
17
7
|
function formatCreatedAt(iso) {
|
|
18
8
|
const d = new Date(iso);
|
|
19
9
|
if (Number.isNaN(d.getTime()))
|
|
@@ -78,15 +68,12 @@ function buildRows(runs, expanded) {
|
|
|
78
68
|
export function RunPicker({ runs, staleRunIds, onSelect, onCancel }) {
|
|
79
69
|
const [expanded, setExpanded] = useState(false);
|
|
80
70
|
const [focusIdx, setFocusIdx] = useState(0);
|
|
81
|
-
// When non-null, render the Push/Modify/Cancel sub-screen for this run.
|
|
82
71
|
const [actionRunId, setActionRunId] = useState(null);
|
|
83
72
|
const rows = buildRows(runs, expanded);
|
|
84
|
-
// Clamp focus if the row count shrinks (e.g. after expansion changes layout).
|
|
85
73
|
const clampedFocus = Math.min(focusIdx, rows.length - 1);
|
|
86
74
|
const selectRow = (row) => {
|
|
87
75
|
if (row.kind === 'show-all') {
|
|
88
76
|
setExpanded(true);
|
|
89
|
-
// Keep cursor on the first newly-revealed run for continuity.
|
|
90
77
|
setFocusIdx(COLLAPSED_LIMIT);
|
|
91
78
|
return;
|
|
92
79
|
}
|
|
@@ -98,9 +85,6 @@ export function RunPicker({ runs, staleRunIds, onSelect, onCancel }) {
|
|
|
98
85
|
};
|
|
99
86
|
useImmediateInput((rawInput, key) => {
|
|
100
87
|
if (actionRunId !== null) {
|
|
101
|
-
// Sub-screen owns input while it's active. (ActionScreen also installs
|
|
102
|
-
// its own useImmediateInput; only one is mounted at a time so there's
|
|
103
|
-
// no double-handle.)
|
|
104
88
|
return;
|
|
105
89
|
}
|
|
106
90
|
if (key.upArrow || rawInput === 'k') {
|
package/dist/src/runs/store.d.ts
CHANGED
|
@@ -55,6 +55,10 @@ export type RunRecord = {
|
|
|
55
55
|
* writers always populate. Added in runs.json v3. Optional for the same
|
|
56
56
|
* reason as `sourceFingerprint`. */
|
|
57
57
|
savedFingerprint?: SavedFingerprint | null;
|
|
58
|
+
/** Composition mode the run was produced in, so modify/replay resumes in the
|
|
59
|
+
* same mode (`composite` vs `atomic`). Absent on records written before this
|
|
60
|
+
* field existed; callers treat a missing value as `atomic` (the default). */
|
|
61
|
+
compositionMode?: 'composite' | 'atomic';
|
|
58
62
|
notes?: string;
|
|
59
63
|
};
|
|
60
64
|
export type RunsFile = {
|
package/dist/src/session/db.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { RawComponentDefinition } from '../types.js';
|
|
|
3
3
|
import type { CDFComponentEntry, DTCGTokenEntry, DTCGTokenGroup } from '@contentful/experience-design-system-types';
|
|
4
4
|
import type { ToolCall, TokenToolCall } from '../generate/agent-runner.js';
|
|
5
5
|
import type { ComponentTypeSummary } from '@contentful/experience-design-system-types';
|
|
6
|
+
import type { SlotCycle, SlotEdge } from '../analyze/cycle-detection.js';
|
|
6
7
|
export type StepStatus = 'pending' | 'complete' | 'failed' | 'interrupted';
|
|
7
8
|
export type CommandName = 'analyze extract' | 'analyze select' | 'generate components' | 'generate tokens' | 'generate edit' | 'apply preview' | 'apply select' | 'apply push' | 'print components' | 'print tokens' | 'import';
|
|
8
9
|
export interface SessionRow {
|
|
@@ -31,12 +32,6 @@ export interface ApplyToolCallsResult {
|
|
|
31
32
|
slots: number;
|
|
32
33
|
warnings: string[];
|
|
33
34
|
}
|
|
34
|
-
/**
|
|
35
|
-
* Feature 1: load per-component metadata for surfacing in the review UI —
|
|
36
|
-
* source path, full source text, and per-prop rationale + source-location.
|
|
37
|
-
* Returns null when no row matches. Decoupled from loadCDFComponents so
|
|
38
|
-
* the CDF projection stays unaffected.
|
|
39
|
-
*/
|
|
40
35
|
export interface ComponentReviewMetadata {
|
|
41
36
|
sourcePath: string | null;
|
|
42
37
|
componentSource: string | null;
|
|
@@ -65,13 +60,6 @@ export interface ComponentRationale {
|
|
|
65
60
|
rationale: string | null;
|
|
66
61
|
}>;
|
|
67
62
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Component-rationale loader: data contract for the `I` ComponentRationalePanel.
|
|
70
|
-
* Returns the component's description + the three component-level rationale
|
|
71
|
-
* strings (why-description / why-props / why-slots), plus per-prop and per-slot
|
|
72
|
-
* rationale rows for the operator-facing panel. Returns null when no component
|
|
73
|
-
* matches.
|
|
74
|
-
*/
|
|
75
63
|
export declare function loadComponentRationale(db: DatabaseSync, sessionId: string, componentName: string): ComponentRationale | null;
|
|
76
64
|
export declare function applyToolCalls(db: DatabaseSync, sessionId: string, componentId: string, componentName: string, calls: ToolCall[], incomingWarnings: string[]): ApplyToolCallsResult;
|
|
77
65
|
export interface MatchHints {
|
|
@@ -95,12 +83,6 @@ export type RawComponentWithId = RawComponentDefinition & {
|
|
|
95
83
|
component_id: string;
|
|
96
84
|
};
|
|
97
85
|
export declare function loadRawComponents(db: DatabaseSync, sessionId: string, allowedNames?: Set<string>): RawComponentWithId[];
|
|
98
|
-
/**
|
|
99
|
-
* Rename empty-named slots in the DB to heuristic names before generation.
|
|
100
|
-
* The LLM classify_slot tool call is matched back to the DB row by name, so a
|
|
101
|
-
* row with name="" can never be updated by the LLM — renaming it here makes it
|
|
102
|
-
* classifiable. Returns one warning string per renamed slot.
|
|
103
|
-
*/
|
|
104
86
|
export declare function renameEmptySlots(db: DatabaseSync, sessionId: string, componentId: string, componentName: string, slotCount: number): {
|
|
105
87
|
renames: Array<{
|
|
106
88
|
oldName: string;
|
|
@@ -121,6 +103,10 @@ export type ScopeComponentRow = {
|
|
|
121
103
|
componentId: string;
|
|
122
104
|
aiDecision: 'accepted' | 'rejected' | null;
|
|
123
105
|
aiReason: string | null;
|
|
106
|
+
slots: Array<{
|
|
107
|
+
name: string;
|
|
108
|
+
allowedComponents: string[];
|
|
109
|
+
}>;
|
|
124
110
|
};
|
|
125
111
|
export declare function loadScopeComponents(db: DatabaseSync, sessionId: string): ScopeComponentRow[];
|
|
126
112
|
export declare function applyScopeDecisions(db: DatabaseSync, sessionId: string, decisions: {
|
|
@@ -141,22 +127,10 @@ export declare function loadDTCGTokens(db: DatabaseSync, sessionId: string): {
|
|
|
141
127
|
export declare function findLatestSessionForCommand(db: DatabaseSync, command: CommandName): string | null;
|
|
142
128
|
export declare function seedCDFFromPriorSession(db: DatabaseSync, targetSessionId: string): number;
|
|
143
129
|
export declare function seedCDFFromPreviewResponse(db: DatabaseSync, sessionId: string, removedItems: ComponentTypeSummary[]): number;
|
|
144
|
-
/**
|
|
145
|
-
* Seeds server-side metadata for changed components:
|
|
146
|
-
* 1. Fills in defaults the server has but we don't (prevents accidental removal)
|
|
147
|
-
* 2. Seeds cdf_type/cdf_category for props that exist on server but lack CDF locally
|
|
148
|
-
* (e.g. props with non-standard source types the generation step couldn't classify)
|
|
149
|
-
*/
|
|
150
130
|
export declare function seedDefaultsFromChangedItems(db: DatabaseSync, sessionId: string, changedItems: Array<{
|
|
151
131
|
current: ComponentTypeSummary;
|
|
152
132
|
proposed: object;
|
|
153
133
|
}>): number;
|
|
154
|
-
/**
|
|
155
|
-
* Ensures all props on generated components have a cdf_type.
|
|
156
|
-
* Uses the pre-classification `category` column when available.
|
|
157
|
-
* Falls back to cdf_type: 'string', cdf_category: 'content' for props
|
|
158
|
-
* with no pre-classification (safe default — content is the most common category).
|
|
159
|
-
*/
|
|
160
134
|
export declare function backfillUnclassifiedProps(db: DatabaseSync, sessionId: string): number;
|
|
161
135
|
export interface CacheEntry {
|
|
162
136
|
inputHash: string;
|
|
@@ -164,7 +138,6 @@ export interface CacheEntry {
|
|
|
164
138
|
entityId: string;
|
|
165
139
|
sourceSessionId: string;
|
|
166
140
|
humanEdited: boolean;
|
|
167
|
-
/** sha256 of the prompt content. '' for pre-upgrade rows that predate the fine-grained cache. */
|
|
168
141
|
promptHash: string;
|
|
169
142
|
createdAt: string;
|
|
170
143
|
updatedAt: string;
|
|
@@ -176,17 +149,17 @@ export declare function lookupCacheByEntity(db: DatabaseSync, entityType: 'compo
|
|
|
176
149
|
export declare function storeCache(db: DatabaseSync, inputHash: string, entityType: 'component' | 'token_set', entityId: string, sourceSessionId: string, humanEdited: boolean, promptHash?: string): void;
|
|
177
150
|
export declare function storeScannedFiles(db: DatabaseSync, sessionId: string, filePaths: string[]): void;
|
|
178
151
|
export declare function loadScannedFiles(db: DatabaseSync, sessionId: string): string[];
|
|
152
|
+
export declare function storeSlotCycles(db: DatabaseSync, sessionId: string, cycles: Array<SlotCycle & {
|
|
153
|
+
suggestedBreak?: SlotEdge | null;
|
|
154
|
+
}>): void;
|
|
155
|
+
export interface StoredSlotCycle extends SlotCycle {
|
|
156
|
+
suggestedBreak: SlotEdge | null;
|
|
157
|
+
}
|
|
158
|
+
export declare function loadSlotCycles(db: DatabaseSync, sessionId: string): StoredSlotCycle[];
|
|
159
|
+
export declare function clearSlotCycles(db: DatabaseSync, sessionId: string): void;
|
|
179
160
|
export declare function markCacheHumanEdited(db: DatabaseSync, entityType: 'component' | 'token_set', entityId: string): void;
|
|
180
161
|
export declare function copyComponentFromCache(db: DatabaseSync, sourceSessionId: string, targetSessionId: string, componentId: string): void;
|
|
181
162
|
export declare function copyTokensFromCache(db: DatabaseSync, sourceSessionId: string, targetSessionId: string): void;
|
|
182
|
-
/**
|
|
183
|
-
* Returns the CLI cache version used to namespace cache rows. To avoid busting
|
|
184
|
-
* the cache on every npm patch bump, this hashes the content of the bundled
|
|
185
|
-
* skill prompts. When the prompts change, cache invalidates; when only library
|
|
186
|
-
* code or test fixtures change, the cache stays warm.
|
|
187
|
-
*
|
|
188
|
-
* Memoized per-process. Falls back to package version on any I/O error.
|
|
189
|
-
*/
|
|
190
163
|
export declare function getCliCacheVersion(): Promise<string>;
|
|
191
164
|
export interface ExtractCacheEntry {
|
|
192
165
|
filePath: string;
|
|
@@ -198,6 +171,16 @@ export interface ExtractCacheEntry {
|
|
|
198
171
|
}
|
|
199
172
|
export declare function storeExtractCache(db: DatabaseSync, filePath: string, fileHash: string, cliVersion: string, components: RawComponentDefinition[]): void;
|
|
200
173
|
export declare function lookupExtractCache(db: DatabaseSync, fileHash: string, cliVersion: string): ExtractCacheEntry | null;
|
|
174
|
+
/**
|
|
175
|
+
* Content-addressed cache for the composition resolver's raw agent output
|
|
176
|
+
* (authored parser source or emitted-edge JSONL). Keyed by a hash of the
|
|
177
|
+
* candidate files + producer identity (`composition-cache-key.ts`), scoped by
|
|
178
|
+
* `cli_version` so a prompt/skill change invalidates stale entries. Replaces
|
|
179
|
+
* the former on-disk `~/.contentful/.../composition-cache` store so all CLI
|
|
180
|
+
* caches live in the one pipeline DB.
|
|
181
|
+
*/
|
|
182
|
+
export declare function storeCompositionCache(db: DatabaseSync, inputHash: string, cliVersion: string, agentOutput: string): void;
|
|
183
|
+
export declare function lookupCompositionCache(db: DatabaseSync, inputHash: string, cliVersion: string): string | null;
|
|
201
184
|
export type SelectDecision = 'accepted' | 'rejected';
|
|
202
185
|
export interface SelectCacheEntry {
|
|
203
186
|
componentHash: string;
|