@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.
Files changed (204) hide show
  1. package/dist/package.json +13 -9
  2. package/dist/src/index.js +34937 -9
  3. package/dist/src/lib/cli-path.d.ts +4 -0
  4. package/dist/src/runs/export-helpers.d.ts +1 -12
  5. package/dist/src/runs/push-helpers.d.ts +1 -6
  6. package/package.json +12 -7
  7. package/skills/generate-components.md +427 -0
  8. package/skills/generate-tokens.md +194 -0
  9. package/skills/select-components.md +224 -0
  10. package/dist/src/analytics/apply.js +0 -30
  11. package/dist/src/analytics/client.js +0 -81
  12. package/dist/src/analytics/constants.js +0 -4
  13. package/dist/src/analytics/env.js +0 -14
  14. package/dist/src/analytics/exit.js +0 -24
  15. package/dist/src/analytics/index.js +0 -9
  16. package/dist/src/analytics/normalize.js +0 -18
  17. package/dist/src/analytics/os.js +0 -13
  18. package/dist/src/analytics/session.js +0 -15
  19. package/dist/src/analytics/tracker.js +0 -126
  20. package/dist/src/analytics/types.js +0 -1
  21. package/dist/src/analyze/build-analyze-view-rows.js +0 -63
  22. package/dist/src/analyze/command.js +0 -599
  23. package/dist/src/analyze/composite-closure.js +0 -0
  24. package/dist/src/analyze/composition/agent-parser/author-prompt.js +0 -22
  25. package/dist/src/analyze/composition/agent-parser/extract-parser.js +0 -44
  26. package/dist/src/analyze/composition/agent-parser/load-prompt.js +0 -31
  27. package/dist/src/analyze/composition/agent-parser/resolve-via-parser.js +0 -88
  28. package/dist/src/analyze/composition/agent-parser/sandbox.js +0 -187
  29. package/dist/src/analyze/composition/apply-mapping.js +0 -67
  30. package/dist/src/analyze/composition/candidate-critic-agent.js +0 -39
  31. package/dist/src/analyze/composition/candidate-critic.js +0 -63
  32. package/dist/src/analyze/composition/candidate-files.js +0 -131
  33. package/dist/src/analyze/composition/composition-cache-key.js +0 -19
  34. package/dist/src/analyze/composition/interchange-schema.js +0 -61
  35. package/dist/src/analyze/composition/manifest-doc-evidence.js +0 -136
  36. package/dist/src/analyze/composition/merge-edges.js +0 -70
  37. package/dist/src/analyze/composition/parse-map-edges.js +0 -55
  38. package/dist/src/analyze/composition/resolve-mapping-cli.js +0 -29
  39. package/dist/src/analyze/composition/resolve-mapping.js +0 -111
  40. package/dist/src/analyze/cycle-detection.js +0 -234
  41. package/dist/src/analyze/cycle-view.js +0 -28
  42. package/dist/src/analyze/fuzzy-search.js +0 -55
  43. package/dist/src/analyze/issue-inheritance.js +0 -108
  44. package/dist/src/analyze/lineage.js +0 -0
  45. package/dist/src/analyze/scope-gate-cascade.js +0 -170
  46. package/dist/src/analyze/search-neighborhood.js +0 -56
  47. package/dist/src/analyze/select/command.js +0 -484
  48. package/dist/src/analyze/select/index.js +0 -5
  49. package/dist/src/analyze/select/parser.js +0 -53
  50. package/dist/src/analyze/select/persistence.js +0 -80
  51. package/dist/src/analyze/select/preview-annotations.js +0 -62
  52. package/dist/src/analyze/select/stdout.js +0 -3
  53. package/dist/src/analyze/select/tui/App.js +0 -483
  54. package/dist/src/analyze/select/tui/components/ComponentDetail.js +0 -48
  55. package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +0 -125
  56. package/dist/src/analyze/select/tui/components/FieldEditor.js +0 -1131
  57. package/dist/src/analyze/select/tui/components/FinalizeDialog.js +0 -36
  58. package/dist/src/analyze/select/tui/components/GotoBanner.js +0 -30
  59. package/dist/src/analyze/select/tui/components/GroupedSidebar.js +0 -517
  60. package/dist/src/analyze/select/tui/components/HelpOverlay.js +0 -17
  61. package/dist/src/analyze/select/tui/components/JsonEditor.js +0 -131
  62. package/dist/src/analyze/select/tui/components/JsonPanel.js +0 -66
  63. package/dist/src/analyze/select/tui/components/LineagePanel.js +0 -22
  64. package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +0 -29
  65. package/dist/src/analyze/select/tui/components/QuitDialog.js +0 -14
  66. package/dist/src/analyze/select/tui/components/RationalePanel.js +0 -80
  67. package/dist/src/analyze/select/tui/components/Sidebar.js +0 -92
  68. package/dist/src/analyze/select/tui/components/SourcePanel.js +0 -52
  69. package/dist/src/analyze/select/tui/components/StatusBar.js +0 -7
  70. package/dist/src/analyze/select/tui/components/TopBar.js +0 -10
  71. package/dist/src/analyze/select/tui/components/removed-components-text.js +0 -11
  72. package/dist/src/analyze/select/tui/hooks/scroll-offset.js +0 -26
  73. package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +0 -66
  74. package/dist/src/analyze/select/tui/hooks/useKeymap.js +0 -67
  75. package/dist/src/analyze/select/tui/hooks/useSession.js +0 -52
  76. package/dist/src/analyze/select/tui/hooks/useUndo.js +0 -26
  77. package/dist/src/analyze/select/tui/theme.js +0 -28
  78. package/dist/src/analyze/select/types.js +0 -39
  79. package/dist/src/analyze/select-agent/command.js +0 -558
  80. package/dist/src/analyze/select-agent/context-builder.js +0 -197
  81. package/dist/src/analyze/select-agent/show-rationale.js +0 -92
  82. package/dist/src/analyze/selection-cascade.js +0 -19
  83. package/dist/src/analyze/slot-graph.js +0 -17
  84. package/dist/src/analyze/tui/AnalyzeView.js +0 -64
  85. package/dist/src/apply/api-client.js +0 -440
  86. package/dist/src/apply/command.js +0 -845
  87. package/dist/src/apply/manifest.js +0 -1
  88. package/dist/src/apply/preview-utils.js +0 -26
  89. package/dist/src/apply/tui/SelectView.js +0 -34
  90. package/dist/src/apply/tui/ServerApplyView.js +0 -49
  91. package/dist/src/apply/tui/ServerPreviewView.js +0 -23
  92. package/dist/src/credentials-store.js +0 -74
  93. package/dist/src/generate/command.js +0 -597
  94. package/dist/src/generate/edit/command.js +0 -127
  95. package/dist/src/generate/tui/GenerateView.js +0 -10
  96. package/dist/src/host-utils.js +0 -26
  97. package/dist/src/import/agent-model-resolve.js +0 -35
  98. package/dist/src/import/auto-filter-resolve.js +0 -16
  99. package/dist/src/import/command.js +0 -380
  100. package/dist/src/import/cycle-auto-reject.js +0 -14
  101. package/dist/src/import/orchestrator.js +0 -603
  102. package/dist/src/import/path-utils.js +0 -30
  103. package/dist/src/import/picker-dispatch.js +0 -44
  104. package/dist/src/import/print-prompt.js +0 -31
  105. package/dist/src/import/strip-allowed-components.js +0 -22
  106. package/dist/src/import/tui/CustomPromptBanner.js +0 -8
  107. package/dist/src/import/tui/WizardApp.js +0 -1670
  108. package/dist/src/import/tui/ai-flag.js +0 -6
  109. package/dist/src/import/tui/auto-filter-error.js +0 -29
  110. package/dist/src/import/tui/autocomplete.js +0 -32
  111. package/dist/src/import/tui/components/AutoFilterBanner.js +0 -19
  112. package/dist/src/import/tui/components/CounterStrip.js +0 -12
  113. package/dist/src/import/tui/components/LegendEntry.js +0 -6
  114. package/dist/src/import/tui/cycle-panel-scroll.js +0 -33
  115. package/dist/src/import/tui/final-review-host.js +0 -25
  116. package/dist/src/import/tui/group-collapse.js +0 -11
  117. package/dist/src/import/tui/history.js +0 -53
  118. package/dist/src/import/tui/hooks/useLineage.js +0 -50
  119. package/dist/src/import/tui/hooks/useOverlayPanel.js +0 -27
  120. package/dist/src/import/tui/lineage-layout.js +0 -82
  121. package/dist/src/import/tui/merge-ai-decisions.js +0 -22
  122. package/dist/src/import/tui/push-decision-gate-helpers.js +0 -8
  123. package/dist/src/import/tui/push-progress.js +0 -1
  124. package/dist/src/import/tui/run-print-files-helpers.js +0 -8
  125. package/dist/src/import/tui/run-teaser.js +0 -9
  126. package/dist/src/import/tui/runLivePreview.js +0 -82
  127. package/dist/src/import/tui/runScopeGate.js +0 -28
  128. package/dist/src/import/tui/scope-gate-columns.js +0 -94
  129. package/dist/src/import/tui/scope-gate-host.js +0 -28
  130. package/dist/src/import/tui/sidebar-width.js +0 -3
  131. package/dist/src/import/tui/spawn-generate.js +0 -53
  132. package/dist/src/import/tui/step-filters.js +0 -42
  133. package/dist/src/import/tui/steps/AtomicGenerateReviewStep.js +0 -726
  134. package/dist/src/import/tui/steps/AtomicScopeGateStep.js +0 -210
  135. package/dist/src/import/tui/steps/CredentialsStep.js +0 -108
  136. package/dist/src/import/tui/steps/DoneStep.js +0 -24
  137. package/dist/src/import/tui/steps/ErrorStep.js +0 -16
  138. package/dist/src/import/tui/steps/GateStep.js +0 -22
  139. package/dist/src/import/tui/steps/GenerateReviewStep.js +0 -1618
  140. package/dist/src/import/tui/steps/PathValidationStep.js +0 -152
  141. package/dist/src/import/tui/steps/PreviewStep.js +0 -37
  142. package/dist/src/import/tui/steps/PreviewValidationErrorStep.js +0 -23
  143. package/dist/src/import/tui/steps/PushDecisionGateStep.js +0 -73
  144. package/dist/src/import/tui/steps/PushingStep.js +0 -24
  145. package/dist/src/import/tui/steps/RunningStep.js +0 -21
  146. package/dist/src/import/tui/steps/ScopeGateStep.js +0 -907
  147. package/dist/src/import/tui/steps/TokenInputStep.js +0 -88
  148. package/dist/src/import/tui/steps/WelcomeStep.js +0 -34
  149. package/dist/src/import/tui/steps/WizardPreviewStep.js +0 -169
  150. package/dist/src/import/tui/steps/auto-reject-decision.js +0 -9
  151. package/dist/src/import/tui/steps/breaking-change-format.js +0 -24
  152. package/dist/src/import/tui/steps/enumerate-cycle-breaks.js +0 -0
  153. package/dist/src/import/tui/steps/preview-diff.js +0 -175
  154. package/dist/src/import/tui/useFinalizePreview.js +0 -0
  155. package/dist/src/import/tui/useLivePreview.js +0 -122
  156. package/dist/src/import/tui/wizard-422-helpers.js +0 -60
  157. package/dist/src/import/tui/wizard-generate-progress.js +0 -25
  158. package/dist/src/import/tui/wizard-save-flow.js +0 -20
  159. package/dist/src/import/tui/wizard-state-transitions.js +0 -32
  160. package/dist/src/lib/agent-model-options.js +0 -15
  161. package/dist/src/lib/command-options.js +0 -30
  162. package/dist/src/lib/composition-mode.js +0 -39
  163. package/dist/src/lib/contentful-urls.js +0 -27
  164. package/dist/src/lib/debug-logger.js +0 -229
  165. package/dist/src/lib/debug-preamble.js +0 -33
  166. package/dist/src/lib/error-parser.js +0 -269
  167. package/dist/src/lib/prompt-overrides.js +0 -69
  168. package/dist/src/lib/terminal-capabilities.js +0 -51
  169. package/dist/src/lib/user-agent.js +0 -29
  170. package/dist/src/output/format.js +0 -110
  171. package/dist/src/print/command.js +0 -221
  172. package/dist/src/print/validate/tui/ValidateView.js +0 -37
  173. package/dist/src/print/validate/validators/cdf-validator.js +0 -104
  174. package/dist/src/print/validate/validators/dtcg-validator.js +0 -110
  175. package/dist/src/print/validate/validators/format-errors.js +0 -18
  176. package/dist/src/program.js +0 -105
  177. package/dist/src/runs/export-helpers.js +0 -59
  178. package/dist/src/runs/fingerprint.js +0 -75
  179. package/dist/src/runs/ls-command.js +0 -137
  180. package/dist/src/runs/modify-launcher.js +0 -42
  181. package/dist/src/runs/path-prompt.js +0 -72
  182. package/dist/src/runs/push-creds-prompt.js +0 -39
  183. package/dist/src/runs/push-helpers.js +0 -72
  184. package/dist/src/runs/push-launcher.js +0 -77
  185. package/dist/src/runs/replay-helpers.js +0 -164
  186. package/dist/src/runs/resolve-run-target.js +0 -45
  187. package/dist/src/runs/run-picker-mount.js +0 -72
  188. package/dist/src/runs/run-picker.js +0 -129
  189. package/dist/src/runs/save-conflict.js +0 -56
  190. package/dist/src/runs/save-path-resolver.js +0 -75
  191. package/dist/src/runs/staleness.js +0 -140
  192. package/dist/src/runs/store.js +0 -136
  193. package/dist/src/session/cache-keys.js +0 -44
  194. package/dist/src/session/command.js +0 -261
  195. package/dist/src/session/db.js +0 -1538
  196. package/dist/src/session/migration.js +0 -117
  197. package/dist/src/session/session-id.js +0 -212
  198. package/dist/src/session/stats.js +0 -89
  199. package/dist/src/setup/analytics-prompt.js +0 -24
  200. package/dist/src/setup/auto-filter-prompt.js +0 -24
  201. package/dist/src/setup/command.js +0 -966
  202. package/dist/src/setup/composition-mode-prompt.js +0 -37
  203. package/dist/src/setup/debug-mode-prompt.js +0 -22
  204. 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
- }