@contentful/experience-design-system-cli 2.26.1-dev-build-64063df.0 → 2.26.1-dev-build-f1d1f30.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 +13 -9
- 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,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The CLI-owned composition interchange format (spec T1).
|
|
3
|
-
*
|
|
4
|
-
* Two views of the same data:
|
|
5
|
-
* - `InterchangeMap` — the human-authored / on-disk `{ version, groups }`
|
|
6
|
-
* shape (parent → children). This is what `--composition-map` consumes and
|
|
7
|
-
* `--generate-map` emits.
|
|
8
|
-
* - `CompositionEdge[]` — the flat internal runtime view, one edge per
|
|
9
|
-
* parent→child relationship, carrying optional `slot` (T7), `confidence`,
|
|
10
|
-
* and `provenance`. Easier to merge/dedupe across sources (T2).
|
|
11
|
-
*
|
|
12
|
-
* This module owns both shapes and the converters between them.
|
|
13
|
-
*/
|
|
14
|
-
export function validateInterchangeMap(input) {
|
|
15
|
-
const errors = [];
|
|
16
|
-
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
17
|
-
return { valid: false, errors: ['interchange map must be a JSON object'] };
|
|
18
|
-
}
|
|
19
|
-
const obj = input;
|
|
20
|
-
if (obj.version !== 1) {
|
|
21
|
-
errors.push(`unsupported interchange version: expected 1, got ${JSON.stringify(obj.version)}`);
|
|
22
|
-
}
|
|
23
|
-
if (typeof obj.groups !== 'object' || obj.groups === null || Array.isArray(obj.groups)) {
|
|
24
|
-
errors.push('interchange map must have a `groups` object');
|
|
25
|
-
return { valid: false, errors };
|
|
26
|
-
}
|
|
27
|
-
const groups = obj.groups;
|
|
28
|
-
for (const [parent, children] of Object.entries(groups)) {
|
|
29
|
-
if (!Array.isArray(children) || !children.every((c) => typeof c === 'string')) {
|
|
30
|
-
errors.push(`groups["${parent}"] must be an array of strings`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
if (errors.length > 0)
|
|
34
|
-
return { valid: false, errors };
|
|
35
|
-
return { valid: true, map: { version: 1, groups: groups } };
|
|
36
|
-
}
|
|
37
|
-
export function groupsToEdges(map, provenance) {
|
|
38
|
-
const seen = new Set();
|
|
39
|
-
const edges = [];
|
|
40
|
-
for (const [parent, children] of Object.entries(map.groups)) {
|
|
41
|
-
for (const child of children) {
|
|
42
|
-
const key = `${parent}::${child}`;
|
|
43
|
-
if (seen.has(key))
|
|
44
|
-
continue;
|
|
45
|
-
seen.add(key);
|
|
46
|
-
edges.push({ parent, child, provenance });
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return edges;
|
|
50
|
-
}
|
|
51
|
-
export function edgesToGroups(edges) {
|
|
52
|
-
const groups = {};
|
|
53
|
-
for (const { parent, child } of edges) {
|
|
54
|
-
(groups[parent] ??= new Set()).add(child);
|
|
55
|
-
}
|
|
56
|
-
const out = {};
|
|
57
|
-
for (const parent of Object.keys(groups).sort()) {
|
|
58
|
-
out[parent] = [...groups[parent]].sort();
|
|
59
|
-
}
|
|
60
|
-
return { version: 1, groups: out };
|
|
61
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { dirname } from 'node:path';
|
|
2
|
-
/**
|
|
3
|
-
* Prompt-injection safeguard for this signal: both parsers below are plain
|
|
4
|
-
* deterministic code, never an LLM prompt. `manifest.json`/`AGENTS.md`
|
|
5
|
-
* content is attacker-adjacent (Figma-authored / free-form prose respectively)
|
|
6
|
-
* but it only ever flows through JSON field access and a fixed regex, then is
|
|
7
|
-
* validated against the real `componentNames` allowlist before an edge is
|
|
8
|
-
* emitted — there is no path from this file's content to model instructions.
|
|
9
|
-
*/
|
|
10
|
-
/** Figma/manifest names are kebab-case (`blue-accordion-item`); React exports are PascalCase. */
|
|
11
|
-
export function kebabToPascal(name) {
|
|
12
|
-
return name
|
|
13
|
-
.split('-')
|
|
14
|
-
.filter(Boolean)
|
|
15
|
-
.map((seg) => seg.charAt(0).toUpperCase() + seg.slice(1))
|
|
16
|
-
.join('');
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Deterministic parse of a Figma-exported `manifest.json` — no LLM involved.
|
|
20
|
-
* Schema (confirmed against a real design-system manifest):
|
|
21
|
-
* { component: { name: "blue-accordion" },
|
|
22
|
-
* variantsMeta: { componentPropertyDefinitions: {
|
|
23
|
-
* "Slot#1533:33": { type: "SLOT", preferredValues: [{ name: "blue-accordion-item" }] }
|
|
24
|
-
* } } }
|
|
25
|
-
* The manifest's own `component.name` identifies its owning/parent component
|
|
26
|
-
* (more robust than inferring from directory proximity, since manifest.json
|
|
27
|
-
* typically lives one level below the component's source file, e.g. in a
|
|
28
|
-
* sibling `design/` directory).
|
|
29
|
-
*/
|
|
30
|
-
export function collectManifestEdges(files, componentNames) {
|
|
31
|
-
const edges = [];
|
|
32
|
-
const seen = new Set();
|
|
33
|
-
for (const file of files) {
|
|
34
|
-
if (!file.path.endsWith('manifest.json'))
|
|
35
|
-
continue;
|
|
36
|
-
let parsed;
|
|
37
|
-
try {
|
|
38
|
-
parsed = JSON.parse(file.content);
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (typeof parsed !== 'object' || parsed === null)
|
|
44
|
-
continue;
|
|
45
|
-
const root = parsed;
|
|
46
|
-
const component = root.component;
|
|
47
|
-
const rawParentName = typeof component === 'object' &&
|
|
48
|
-
component !== null &&
|
|
49
|
-
typeof component.name === 'string'
|
|
50
|
-
? component.name
|
|
51
|
-
: undefined;
|
|
52
|
-
if (!rawParentName)
|
|
53
|
-
continue;
|
|
54
|
-
const parent = kebabToPascal(rawParentName);
|
|
55
|
-
if (!componentNames.has(parent))
|
|
56
|
-
continue;
|
|
57
|
-
const variantsMeta = root.variantsMeta;
|
|
58
|
-
const propDefs = typeof variantsMeta === 'object' && variantsMeta !== null
|
|
59
|
-
? variantsMeta.componentPropertyDefinitions
|
|
60
|
-
: undefined;
|
|
61
|
-
if (typeof propDefs !== 'object' || propDefs === null)
|
|
62
|
-
continue;
|
|
63
|
-
for (const def of Object.values(propDefs)) {
|
|
64
|
-
if (typeof def !== 'object' || def === null)
|
|
65
|
-
continue;
|
|
66
|
-
const defObj = def;
|
|
67
|
-
if (defObj.type !== 'SLOT')
|
|
68
|
-
continue;
|
|
69
|
-
const preferredValues = Array.isArray(defObj.preferredValues) ? defObj.preferredValues : [];
|
|
70
|
-
for (const value of preferredValues) {
|
|
71
|
-
if (typeof value !== 'object' || value === null)
|
|
72
|
-
continue;
|
|
73
|
-
const rawChildName = value.name;
|
|
74
|
-
if (typeof rawChildName !== 'string')
|
|
75
|
-
continue;
|
|
76
|
-
const child = kebabToPascal(rawChildName);
|
|
77
|
-
if (!componentNames.has(child) || child === parent)
|
|
78
|
-
continue;
|
|
79
|
-
const key = `${parent}::${child}`;
|
|
80
|
-
if (seen.has(key))
|
|
81
|
-
continue;
|
|
82
|
-
seen.add(key);
|
|
83
|
-
edges.push({ parent, child, provenance: 'manifest' });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return edges;
|
|
88
|
-
}
|
|
89
|
-
const DOC_COMPOSITION_KEYWORDS = ['slot', 'child', 'children', 'compose', 'nested', 'contains', 'wraps'];
|
|
90
|
-
const BOLD_BACKTICK = /\*\*`([^`]+)`\*\*/g;
|
|
91
|
-
/**
|
|
92
|
-
* Deterministic parse of a component-level `AGENTS.md` — no LLM involved.
|
|
93
|
-
* Only a bold-backtick component mention (`` **`ComponentName`** ``) on a
|
|
94
|
-
* line that ALSO carries a composition keyword is accepted, e.g. "Direct
|
|
95
|
-
* **`AccordionItem`** children only." A bare cross-reference mention (no
|
|
96
|
-
* composition keyword on the same line, e.g. "see `other-component`") is
|
|
97
|
-
* rejected — it isn't asserting a parent-child relationship.
|
|
98
|
-
*
|
|
99
|
-
* The doc's owning component is resolved by directory colocation: the
|
|
100
|
-
* component whose `sourcePath` sits in the same directory as the doc file
|
|
101
|
-
* (the real-world convention — `<component>/AGENTS.md` next to
|
|
102
|
-
* `<component>/Component.tsx`).
|
|
103
|
-
*/
|
|
104
|
-
export function collectAgentsDocEdges(files, components, componentNames) {
|
|
105
|
-
const edges = [];
|
|
106
|
-
const seen = new Set();
|
|
107
|
-
for (const file of files) {
|
|
108
|
-
if (!file.path.endsWith('AGENTS.md'))
|
|
109
|
-
continue;
|
|
110
|
-
const docDir = dirname(file.path);
|
|
111
|
-
const parent = components.find((c) => c.sourcePath && dirname(c.sourcePath) === docDir)?.name;
|
|
112
|
-
if (!parent || !componentNames.has(parent))
|
|
113
|
-
continue;
|
|
114
|
-
for (const line of file.content.split('\n')) {
|
|
115
|
-
const lower = line.toLowerCase();
|
|
116
|
-
if (!DOC_COMPOSITION_KEYWORDS.some((kw) => lower.includes(kw)))
|
|
117
|
-
continue;
|
|
118
|
-
for (const match of line.matchAll(BOLD_BACKTICK)) {
|
|
119
|
-
const raw = match[1];
|
|
120
|
-
const candidate = componentNames.has(raw) ? raw : kebabToPascal(raw);
|
|
121
|
-
if (!componentNames.has(candidate) || candidate === parent)
|
|
122
|
-
continue;
|
|
123
|
-
const key = `${parent}::${candidate}`;
|
|
124
|
-
if (seen.has(key))
|
|
125
|
-
continue;
|
|
126
|
-
seen.add(key);
|
|
127
|
-
edges.push({ parent, child: candidate, provenance: 'doc' });
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return edges;
|
|
132
|
-
}
|
|
133
|
-
/** Both deterministic sources, combined for the `extraEdges` wiring in command.ts. */
|
|
134
|
-
export function collectManifestDocEdges(files, components, componentNames) {
|
|
135
|
-
return [...collectManifestEdges(files, componentNames), ...collectAgentsDocEdges(files, components, componentNames)];
|
|
136
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Provenance rank (spec T2). Lower number = higher trust = wins conflicts.
|
|
3
|
-
* 1 user > 2 typed-slot > 3 structural > 4 manifest > 5 doc >
|
|
4
|
-
* 6 adapter:* > 7 agent
|
|
5
|
-
*
|
|
6
|
-
* `structural` sits just below a declared slot contract: it's usage evidence
|
|
7
|
-
* (a runtime type-predicate function, a `.type === Component` identity check,
|
|
8
|
-
* direct JSX instantiation — see `structural-slot-evidence.ts`) rather than a
|
|
9
|
-
* typed generic or explicit marker, so a declared contract always overrides
|
|
10
|
-
* it on conflict, but it still outranks the LLM agent.
|
|
11
|
-
*
|
|
12
|
-
* `manifest` (Figma-generated `manifest.json` SLOT declarations) and `doc`
|
|
13
|
-
* (component `AGENTS.md` prose) are both deterministically parsed — no LLM
|
|
14
|
-
* ever reads their content for this signal — but they describe design intent
|
|
15
|
-
* rather than the shipped code, so they sit below code-derived evidence.
|
|
16
|
-
* `manifest` outranks `doc` since it's structured/systematic; free-form
|
|
17
|
-
* prose is the least verifiable of the code-adjacent signals.
|
|
18
|
-
*/
|
|
19
|
-
function rank(p) {
|
|
20
|
-
if (p === 'user')
|
|
21
|
-
return 1;
|
|
22
|
-
if (p === 'typed-slot')
|
|
23
|
-
return 2;
|
|
24
|
-
if (p === 'structural')
|
|
25
|
-
return 3;
|
|
26
|
-
if (p === 'manifest')
|
|
27
|
-
return 4;
|
|
28
|
-
if (p === 'doc')
|
|
29
|
-
return 5;
|
|
30
|
-
if (p.startsWith('adapter:'))
|
|
31
|
-
return 6;
|
|
32
|
-
return 7; // agent
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Union all edges, resolving conflicts by provenance rank. A conflict is two
|
|
36
|
-
* edges that share `parent::child` but disagree on existence-detail (here:
|
|
37
|
-
* slot placement). The higher-ranked edge wins; the loser is recorded (never
|
|
38
|
-
* silently dropped) so the review UI can surface it. Identical edges (same
|
|
39
|
-
* parent/child/slot) collapse to the highest-ranked copy with no conflict.
|
|
40
|
-
*/
|
|
41
|
-
export function mergeEdges(all) {
|
|
42
|
-
const byKey = new Map();
|
|
43
|
-
const conflicts = [];
|
|
44
|
-
for (const edge of all) {
|
|
45
|
-
const key = `${edge.parent}::${edge.child}`;
|
|
46
|
-
const existing = byKey.get(key);
|
|
47
|
-
if (!existing) {
|
|
48
|
-
byKey.set(key, edge);
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
const sameSlot = (existing.slot ?? '') === (edge.slot ?? '');
|
|
52
|
-
if (sameSlot) {
|
|
53
|
-
// Identical relationship — keep the higher-ranked provenance, no conflict.
|
|
54
|
-
if (rank(edge.provenance) < rank(existing.provenance))
|
|
55
|
-
byKey.set(key, edge);
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
// Slot placement disagreement → conflict. Higher rank wins.
|
|
59
|
-
const winnerEdge = rank(edge.provenance) < rank(existing.provenance) ? edge : existing;
|
|
60
|
-
const loserEdge = winnerEdge === edge ? existing : edge;
|
|
61
|
-
byKey.set(key, winnerEdge);
|
|
62
|
-
conflicts.push({
|
|
63
|
-
parent: edge.parent,
|
|
64
|
-
child: edge.child,
|
|
65
|
-
winner: winnerEdge.provenance,
|
|
66
|
-
loser: loserEdge.provenance,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
return { edges: [...byKey.values()], conflicts };
|
|
70
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
const VALID_MAP_TOOL_NAMES = new Set(['map_edge']);
|
|
2
|
-
export function parseMapEdges(raw, opts) {
|
|
3
|
-
const edges = [];
|
|
4
|
-
const warnings = [];
|
|
5
|
-
const { componentNames } = opts;
|
|
6
|
-
for (const line of raw.split('\n')) {
|
|
7
|
-
const trimmed = line.trim();
|
|
8
|
-
if (!trimmed.startsWith('{'))
|
|
9
|
-
continue;
|
|
10
|
-
let obj;
|
|
11
|
-
try {
|
|
12
|
-
obj = JSON.parse(trimmed);
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
warnings.push(`unparseable line: ${trimmed.slice(0, 120)}`);
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
if (typeof obj !== 'object' || obj === null || !('tool' in obj))
|
|
19
|
-
continue;
|
|
20
|
-
const rec = obj;
|
|
21
|
-
if (!VALID_MAP_TOOL_NAMES.has(rec.tool)) {
|
|
22
|
-
warnings.push(`unknown tool: ${String(rec.tool)}`);
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
if (typeof rec.parent !== 'string' || !rec.parent || typeof rec.child !== 'string' || !rec.child) {
|
|
26
|
-
warnings.push('map_edge missing parent/child — skipped');
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
const { parent, child } = rec;
|
|
30
|
-
// Verification (spec §1.3): both endpoints must name a known component.
|
|
31
|
-
if (!componentNames.has(parent)) {
|
|
32
|
-
warnings.push(`map_edge names unknown component (parent): ${parent} — skipped`);
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
if (!componentNames.has(child)) {
|
|
36
|
-
warnings.push(`map_edge names unknown component (child): ${child} — skipped`);
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
const edge = { parent, child, provenance: 'agent' };
|
|
40
|
-
if (typeof rec.slot === 'string' && rec.slot) {
|
|
41
|
-
edge.slot = rec.slot;
|
|
42
|
-
}
|
|
43
|
-
if (rec.confidence !== undefined) {
|
|
44
|
-
if (typeof rec.confidence === 'number' && rec.confidence >= 1 && rec.confidence <= 5) {
|
|
45
|
-
edge.confidence = rec.confidence;
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
// Lenient: keep the edge, drop the bad confidence, record a warning.
|
|
49
|
-
warnings.push(`map_edge (${parent}→${child}): invalid confidence ${String(rec.confidence)} — edge kept without confidence`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
edges.push(edge);
|
|
53
|
-
}
|
|
54
|
-
return { edges, warnings };
|
|
55
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { validateInterchangeMap } from './interchange-schema.js';
|
|
3
|
-
/** Read + validate a hand-authored interchange map (spec T1 / §1.2 third mechanism). */
|
|
4
|
-
export async function loadUserMap(path) {
|
|
5
|
-
let text;
|
|
6
|
-
try {
|
|
7
|
-
text = await readFile(path, 'utf8');
|
|
8
|
-
}
|
|
9
|
-
catch {
|
|
10
|
-
return { ok: false, error: `--composition-map: file not found: ${path}` };
|
|
11
|
-
}
|
|
12
|
-
let parsed;
|
|
13
|
-
try {
|
|
14
|
-
parsed = JSON.parse(text);
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return { ok: false, error: `--composition-map: not valid JSON: ${path}` };
|
|
18
|
-
}
|
|
19
|
-
const res = validateInterchangeMap(parsed);
|
|
20
|
-
if (!res.valid)
|
|
21
|
-
return { ok: false, error: `--composition-map: ${res.errors.join('; ')}` };
|
|
22
|
-
return { ok: true, map: res.map };
|
|
23
|
-
}
|
|
24
|
-
export function resolveCompositionSources(opts) {
|
|
25
|
-
return {
|
|
26
|
-
useAgent: !!opts.compositionAgent,
|
|
27
|
-
forceAgent: !!opts.compositionRefresh,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { groupsToEdges } from './interchange-schema.js';
|
|
2
|
-
import { mergeEdges } from './merge-edges.js';
|
|
3
|
-
import { parseMapEdges } from './parse-map-edges.js';
|
|
4
|
-
import { applyMapping } from './apply-mapping.js';
|
|
5
|
-
import { loadPrompt } from './agent-parser/load-prompt.js';
|
|
6
|
-
/**
|
|
7
|
-
* Orchestrate composition-map acquisition (spec T2) and enrichment (T7).
|
|
8
|
-
*
|
|
9
|
-
* Sources by rank: user map (1) > typed-slot / "code slots" (2) > structural
|
|
10
|
-
* usage evidence (3) > manifest (4) > doc (5) > adapter-resolved / extraEdges
|
|
11
|
-
* (6) > agent (7). Manifest/doc edges are computed deterministically outside
|
|
12
|
-
* this function (see manifest-doc-evidence.ts) and joined via `extraEdges`.
|
|
13
|
-
* ALL sources — including the code slots already on the incoming components —
|
|
14
|
-
* are fed into one ranked merge and unioned; non-conflicting edges from every
|
|
15
|
-
* source survive, and on a conflict (same parent+child, different slot) the
|
|
16
|
-
* higher-rank source wins and the loser is recorded. The agent runs only when
|
|
17
|
-
* `useAgent`/`forceAgent` is set AND there is residue a higher-rank source
|
|
18
|
-
* didn't cover (routing/cost optimization) — `forceAgent` bypasses that
|
|
19
|
-
* suppression but never changes rank.
|
|
20
|
-
*
|
|
21
|
-
* `runAgentFn` is injected (returns the agent's raw stdout) so this is
|
|
22
|
-
* testable without spawning a subprocess.
|
|
23
|
-
*/
|
|
24
|
-
export async function resolveMapping(input) {
|
|
25
|
-
const componentNames = new Set(input.components.map((c) => c.name));
|
|
26
|
-
const collected = [];
|
|
27
|
-
const agentWarnings = [];
|
|
28
|
-
// Rank 2 — typed-slot ("code slots") already resolved by the AST extractor.
|
|
29
|
-
// Feed them into the ranked merge so a conflicting lower-rank edge (agent
|
|
30
|
-
// placing the same child in a different slot) LOSES to code rather than being
|
|
31
|
-
// unioned in alongside it.
|
|
32
|
-
for (const c of input.components) {
|
|
33
|
-
for (const slot of c.slots) {
|
|
34
|
-
for (const child of slot.allowedComponents ?? []) {
|
|
35
|
-
collected.push({ parent: c.name, child, slot: slot.name, provenance: 'typed-slot' });
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
// Rank 3 — structural usage evidence (runtime type-predicate, `.type ===`
|
|
40
|
-
// identity check, or direct JSX nesting — see structural-slot-evidence.ts).
|
|
41
|
-
// Lower trust than a declared slot contract, but still code-derived, so it
|
|
42
|
-
// outranks the agent and suppresses a redundant agent run when it alone
|
|
43
|
-
// covers a parent.
|
|
44
|
-
for (const c of input.components) {
|
|
45
|
-
for (const slot of c.slots) {
|
|
46
|
-
for (const child of slot.structuralAllowedComponents ?? []) {
|
|
47
|
-
collected.push({ parent: c.name, child, slot: slot.name, provenance: 'structural' });
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// Rank 1 — user-provided map.
|
|
52
|
-
if (input.userMap) {
|
|
53
|
-
collected.push(...groupsToEdges(input.userMap, 'user'));
|
|
54
|
-
}
|
|
55
|
-
// Externally pre-resolved edges — manifest (4), doc (5), adapter-authored
|
|
56
|
-
// parser (6) — each edge carries its own provenance, so this loop is rank-
|
|
57
|
-
// agnostic; the merge below sorts it out.
|
|
58
|
-
if (input.extraEdges) {
|
|
59
|
-
collected.push(...input.extraEdges);
|
|
60
|
-
}
|
|
61
|
-
// Routing: which parents are already covered by a higher-rank source?
|
|
62
|
-
const coveredParents = new Set(collected.map((e) => e.parent));
|
|
63
|
-
const residueParents = input.components.map((c) => c.name).filter((n) => !coveredParents.has(n));
|
|
64
|
-
// Rank 7 — agent. Runs when enabled AND (forced OR there is residue).
|
|
65
|
-
const shouldRunAgent = (input.useAgent || input.forceAgent) && (input.forceAgent || residueParents.length > 0);
|
|
66
|
-
if (shouldRunAgent) {
|
|
67
|
-
const prompt = input.buildPrompt
|
|
68
|
-
? input.buildPrompt(input.files, [...componentNames])
|
|
69
|
-
: defaultPrompt(input.files, [...componentNames], input.promptOverride);
|
|
70
|
-
const raw = await input.runAgentFn({ prompt, files: input.files });
|
|
71
|
-
const parsed = parseMapEdges(raw, { componentNames });
|
|
72
|
-
collected.push(...parsed.edges);
|
|
73
|
-
agentWarnings.push(...parsed.warnings);
|
|
74
|
-
}
|
|
75
|
-
const merged = mergeEdges(collected);
|
|
76
|
-
// Apply the merged edges onto components whose allowedComponents are cleared,
|
|
77
|
-
// so the ranked merge is authoritative — a code edge that lost to a rank-1
|
|
78
|
-
// user override is actually gone, not left behind on the original slot.
|
|
79
|
-
// Slot structure is preserved; only the composition constraint is reset.
|
|
80
|
-
const base = input.components.map((c) => ({
|
|
81
|
-
...c,
|
|
82
|
-
slots: c.slots.map((s) => {
|
|
83
|
-
const { allowedComponents: _drop, ...rest } = s;
|
|
84
|
-
return rest;
|
|
85
|
-
}),
|
|
86
|
-
}));
|
|
87
|
-
const applied = applyMapping(base, merged.edges);
|
|
88
|
-
return {
|
|
89
|
-
components: applied.components,
|
|
90
|
-
edges: merged.edges,
|
|
91
|
-
conflicts: merged.conflicts,
|
|
92
|
-
warnings: [...agentWarnings, ...applied.warnings],
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function defaultPrompt(files, componentNames, promptOverride) {
|
|
96
|
-
const fileBlocks = files.map((f) => `--- ${f.path} ---\n${f.content}`).join('\n\n');
|
|
97
|
-
// The override replaces only the leading instruction; the output contract,
|
|
98
|
-
// name allowlist, and candidate files are always appended so the JSONL
|
|
99
|
-
// parser keeps working.
|
|
100
|
-
const instruction = promptOverride?.trim() ? promptOverride.trim() : loadPrompt('composition-edges.md').trim();
|
|
101
|
-
return [
|
|
102
|
-
instruction,
|
|
103
|
-
'',
|
|
104
|
-
'Emit one JSON object per line, each: {"tool":"map_edge","parent":"<Name>","child":"<Name>","slot"?:"<slot>","confidence"?:1-5,"reason":"<cite the file + declaration>"}.',
|
|
105
|
-
'Use ONLY these exact component names (an edge naming anything else is dropped):',
|
|
106
|
-
componentNames.join(', '),
|
|
107
|
-
'',
|
|
108
|
-
'Candidate files:',
|
|
109
|
-
fileBlocks,
|
|
110
|
-
].join('\n');
|
|
111
|
-
}
|