@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,31 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { dirname, join, resolve } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- /**
5
- * Resolve a bundled prompt file from the package-root `prompts/` directory.
6
- * Walks up from this module so it works from both `src/` and `dist/src/`,
7
- * mirroring `resolveSkillPath` for the skills/ directory.
8
- */
9
- export function resolvePromptPath(fileName) {
10
- const thisDir = dirname(fileURLToPath(import.meta.url));
11
- let dir = thisDir;
12
- for (;;) {
13
- const candidate = join(dir, 'prompts');
14
- if (existsSync(candidate))
15
- return join(candidate, fileName);
16
- const parent = resolve(dir, '..');
17
- if (parent === dir) {
18
- throw new Error(`prompt file missing from CLI installation (could not locate prompts/ directory from: ${thisDir})`);
19
- }
20
- dir = parent;
21
- }
22
- }
23
- export function loadPrompt(fileName) {
24
- const path = resolvePromptPath(fileName);
25
- try {
26
- return readFileSync(path, 'utf8');
27
- }
28
- catch {
29
- throw new Error(`prompt file missing from CLI installation — try reinstalling the CLI (looked for: ${path})`);
30
- }
31
- }
@@ -1,88 +0,0 @@
1
- import { buildAuthorPrompt } from './author-prompt.js';
2
- import { extractParserSource } from './extract-parser.js';
3
- import { runParserInSandbox } from './sandbox.js';
4
- import { loadPrompt } from './load-prompt.js';
5
- /**
6
- * Agent-authored parser path (spec: agent-authored-parser, Phase 2).
7
- *
8
- * Ask the agent to WRITE a pure `(ctx) => Edge[]` parser, run it in the
9
- * sandbox, and verify its edges against the component name set. One repair
10
- * round if the authored parser fails to run; otherwise signal fallback. The
11
- * agent call is injected so this is testable without spawning a subprocess.
12
- */
13
- export async function resolveViaAgentParser(input) {
14
- const warnings = [];
15
- const componentNamesArr = [...input.componentNames];
16
- const ctx = { files: input.runtimeFiles ?? input.files, componentNames: componentNamesArr };
17
- const verify = (edges) => {
18
- const out = [];
19
- for (const e of edges) {
20
- // A component cannot be its own composite parent. Self-edges are a common
21
- // parser bug (matching a component's own name in its own source) and, left
22
- // in, masquerade as one-node cycles downstream.
23
- if (e.parent === e.child) {
24
- warnings.push(`parser edge dropped: self-edge "${e.parent}→${e.child}"`);
25
- continue;
26
- }
27
- if (!input.componentNames.has(e.parent)) {
28
- warnings.push(`parser edge dropped: unknown parent "${e.parent}" (${e.parent}→${e.child})`);
29
- continue;
30
- }
31
- if (!input.componentNames.has(e.child)) {
32
- warnings.push(`parser edge dropped: unknown child "${e.child}" (${e.parent}→${e.child})`);
33
- continue;
34
- }
35
- out.push(e);
36
- }
37
- return out;
38
- };
39
- // Attempt: author → extract → sandbox. Returns null on a fallback-worthy miss.
40
- const attempt = async (prompt) => {
41
- input.onPhase?.('authoring');
42
- const raw = await input.runAgentFn({ prompt });
43
- const source = extractParserSource(raw);
44
- if (source === null)
45
- return { error: 'agent output had no parser-shaped code block (expected `export default function (ctx) {…}`)' };
46
- input.onPhase?.('parsing');
47
- const res = await runParserInSandbox(source, ctx, input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {});
48
- if (res.error)
49
- return { error: res.error };
50
- return { edges: res.edges, source };
51
- };
52
- const basePrompt = buildAuthorPrompt(input.files, componentNamesArr, input.instructionOverride);
53
- const first = await attempt(basePrompt);
54
- if ('edges' in first) {
55
- const verified = verify(first.edges);
56
- // Suspiciously-empty guard: a parser that ran cleanly but emitted no usable
57
- // edges, when the candidates DO contain composition markers, is almost
58
- // always broken (wrong parent attribution, wrong pattern). Give it one
59
- // repair round rather than silently returning nothing.
60
- if (verified.length > 0 || !input.retryOnEmpty) {
61
- return { edges: verified, warnings, usedFallback: false, parserSource: first.source };
62
- }
63
- warnings.push('parser produced 0 usable edges despite composition markers — retrying once');
64
- const emptyRepairPrompt = `${basePrompt}\n\n${loadPrompt('composition-parser-repair-empty.md').trim()}`;
65
- const retry = await attempt(emptyRepairPrompt);
66
- if ('edges' in retry) {
67
- const retried = verify(retry.edges);
68
- // Prefer the retry when it found something; otherwise keep the first
69
- // parser (it at least ran) rather than discarding a working artifact.
70
- if (retried.length > 0) {
71
- return { edges: retried, warnings, usedFallback: false, parserSource: retry.source };
72
- }
73
- warnings.push('repair still produced 0 edges — keeping the original parser');
74
- return { edges: verified, warnings, usedFallback: false, parserSource: first.source };
75
- }
76
- warnings.push(`repair attempt failed: ${retry.error} — keeping the original (empty) parser`);
77
- return { edges: verified, warnings, usedFallback: false, parserSource: first.source };
78
- }
79
- // One repair round — tell the agent what went wrong and try again.
80
- warnings.push(`parser attempt failed: ${first.error} — retrying once`);
81
- const repairPrompt = `${basePrompt}\n\n${loadPrompt('composition-parser-repair-error.md').trim().replace('{{error}}', first.error)}`;
82
- const second = await attempt(repairPrompt);
83
- if ('edges' in second) {
84
- return { edges: verify(second.edges), warnings, usedFallback: false, parserSource: second.source };
85
- }
86
- warnings.push(`parser attempt failed again: ${second.error} — falling back`);
87
- return { edges: [], warnings, usedFallback: true };
88
- }
@@ -1,187 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- const DEFAULT_TIMEOUT_MS = 5000;
3
- const HEAP_CAP_MB = 128;
4
- const MAX_INPUT_BYTES = 8 * 1024 * 1024; // refuse absurd ctx up front
5
- /**
6
- * Execute agent-authored parser source under three nested jails (spec:
7
- * dsi-agent-authored-parser-design, Phase 1 — the security core).
8
- *
9
- * OUTER — a throwaway child `node` process: `env: {}` (no secrets/tokens leak
10
- * even on a full escape), a heap cap (`--max-old-space-size`, OOM self-kills),
11
- * and a wall-clock `SIGKILL` the parent enforces for any hang (sync or async).
12
- *
13
- * PERMISSION — the child runs with `--permission` and NO `--allow-fs-*`, so
14
- * the Node runtime denies filesystem access, child_process spawning, worker
15
- * creation, and native addons (ERR_ACCESS_DENIED). The parser needs none —
16
- * its files arrive via stdin as ctx — so even a vm escape reaching
17
- * `require('node:fs')`/`child_process` is stopped at the runtime layer.
18
- *
19
- * INNER — a `node:vm` context created from a null-prototype object, an
20
- * ALLOW-LIST: it has only ECMAScript intrinsics (Object/Array/JSON/Math/RegExp
21
- * …). There is no `process`, `require`, `Buffer`, `fetch`, timers, or module
22
- * loader in scope, so the parser cannot reach them — and only STRINGS cross
23
- * the boundary (source + JSON-encoded ctx), so there is no host-object handle
24
- * for the classic `this.constructor.constructor('return process')()` escape to
25
- * climb. `vm.runInContext(..., { timeout })` kills sync infinite loops.
26
- *
27
- * The parser must be SYNCHRONOUS — an async/thenable return is rejected, which
28
- * closes the async-timeout gap (vm `timeout` covers sync execution only).
29
- *
30
- * Any failure — throw, timeout, OOM, denied access, bad/async return, compile
31
- * error — resolves to `{ edges: [], error }` and is NEVER thrown. Returned
32
- * edges are shape-validated here; the caller still re-verifies parent/child
33
- * against the component name set.
34
- */
35
- export function runParserInSandbox(source, ctx, opts = {}) {
36
- const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
37
- return new Promise((resolve) => {
38
- let input;
39
- try {
40
- input = JSON.stringify({ source, ctx, timeoutMs });
41
- }
42
- catch {
43
- resolve({ edges: [], error: 'ctx is not serializable' });
44
- return;
45
- }
46
- if (input.length > MAX_INPUT_BYTES) {
47
- resolve({ edges: [], error: 'parser input exceeds size limit' });
48
- return;
49
- }
50
- let settled = false;
51
- let stdout = '';
52
- let stderr = '';
53
- const child = spawn(process.execPath, [
54
- // Node Permission Model (Node ≥20/24): deny fs, child_process, worker,
55
- // and native addons at the RUNTIME level. We pass NO --allow-fs-* — the
56
- // parser needs no disk access (its files arrive via stdin as ctx), so
57
- // even if the vm allow-list were escaped and `require('node:fs')` were
58
- // reached, reads are denied (ERR_ACCESS_DENIED) and spawning is denied
59
- // by default. Third jail layer beneath process-isolation + vm.
60
- '--permission',
61
- `--max-old-space-size=${HEAP_CAP_MB}`,
62
- '--input-type=module',
63
- '-e',
64
- RUNNER,
65
- ],
66
- // No inherited env (secrets stay out of the jail), neutral cwd, no argv.
67
- { env: {}, cwd: process.cwd(), stdio: ['pipe', 'pipe', 'pipe'] });
68
- const done = (r) => {
69
- if (settled)
70
- return;
71
- settled = true;
72
- clearTimeout(timer);
73
- try {
74
- child.kill('SIGKILL');
75
- }
76
- catch {
77
- /* already gone */
78
- }
79
- resolve(r);
80
- };
81
- // Wall-clock backstop (covers async hangs the vm timeout can't). Grace over
82
- // the inner timeout so the vm-level error is preferred when it fires first.
83
- const timer = setTimeout(() => done({ edges: [], error: `parser timed out after ${timeoutMs}ms` }), timeoutMs + 1000);
84
- child.stdout.on('data', (c) => {
85
- stdout += String(c);
86
- });
87
- child.stderr.on('data', (c) => {
88
- stderr += String(c);
89
- });
90
- child.on('error', (e) => done({ edges: [], error: `sandbox spawn failed: ${e.message}` }));
91
- child.on('close', (code, signal) => {
92
- if (settled)
93
- return;
94
- if (signal === 'SIGKILL') {
95
- done({ edges: [], error: 'parser killed (timeout or memory limit)' });
96
- return;
97
- }
98
- let msg;
99
- try {
100
- msg = JSON.parse(stdout);
101
- }
102
- catch {
103
- done({
104
- edges: [],
105
- error: `sandbox produced no result (code ${code})${stderr ? `: ${stderr.slice(0, 200)}` : ''}`,
106
- });
107
- return;
108
- }
109
- if (msg.ok)
110
- done({ edges: sanitizeEdges(msg.edges) });
111
- else
112
- done({ edges: [], error: msg.error ?? 'parser failed' });
113
- });
114
- try {
115
- child.stdin.on('error', () => { });
116
- child.stdin.write(input);
117
- child.stdin.end();
118
- }
119
- catch {
120
- done({ edges: [], error: 'failed to send input to sandbox' });
121
- }
122
- });
123
- }
124
- /** Keep only well-formed edges: parent+child non-empty strings. */
125
- function sanitizeEdges(raw) {
126
- if (!Array.isArray(raw))
127
- return [];
128
- const out = [];
129
- for (const e of raw) {
130
- if (!e || typeof e !== 'object')
131
- continue;
132
- const rec = e;
133
- if (typeof rec.parent !== 'string' || rec.parent === '')
134
- continue;
135
- if (typeof rec.child !== 'string' || rec.child === '')
136
- continue;
137
- const edge = { parent: rec.parent, child: rec.child, provenance: 'adapter:agent-parser' };
138
- if (typeof rec.slot === 'string' && rec.slot !== '')
139
- edge.slot = rec.slot;
140
- if (typeof rec.confidence === 'number' && rec.confidence >= 1 && rec.confidence <= 5)
141
- edge.confidence = rec.confidence;
142
- out.push(edge);
143
- }
144
- return out;
145
- }
146
- /**
147
- * Child-process runner (trusted — OUR code). Reads {source, ctx, timeoutMs}
148
- * from stdin, runs the parser inside a vm allow-list context, writes a single
149
- * JSON result to stdout. Only strings enter the vm; only a JSON string leaves.
150
- */
151
- const RUNNER = `
152
- import vm from 'node:vm';
153
- let input = '';
154
- process.stdin.on('data', (c) => (input += c));
155
- process.stdin.on('end', () => {
156
- let out;
157
- try {
158
- const { source, ctx, timeoutMs } = JSON.parse(input);
159
- // Null-prototype global: no inherited host props. The context still gets
160
- // ECMAScript intrinsics (Object/Array/JSON/Function/Math/RegExp), but NOT
161
- // process/require/Buffer/timers/module.
162
- const context = vm.createContext(Object.create(null));
163
- // Cross the boundary as PRIMITIVES only — no host object/function handle
164
- // for a prototype-chain escape to grab.
165
- context.__source = String(source);
166
- context.__ctxJson = JSON.stringify(ctx);
167
- const bootstrap = [
168
- '(function () {',
169
- ' var ctx = JSON.parse(__ctxJson);',
170
- " var body = String(__source).replace(/export\\\\s+default\\\\s+/, 'return ');",
171
- ' var factory = Function(body);', // the vm context's own Function
172
- ' var parser = factory();',
173
- " if (typeof parser !== 'function') return JSON.stringify({ ok: false, error: 'parser source did not resolve to a function' });",
174
- ' var result = parser(ctx);',
175
- " if (result && typeof result.then === 'function') return JSON.stringify({ ok: false, error: 'parser must be synchronous' });",
176
- " if (!Array.isArray(result)) return JSON.stringify({ ok: false, error: 'parser did not return an array' });",
177
- ' return JSON.stringify({ ok: true, edges: result });',
178
- '})()',
179
- ].join('\\n');
180
- out = vm.runInContext(bootstrap, context, { timeout: timeoutMs });
181
- } catch (e) {
182
- out = JSON.stringify({ ok: false, error: String(e && e.message ? e.message : e) });
183
- }
184
- try { process.stdout.write(typeof out === 'string' ? out : JSON.stringify({ ok: false, error: 'no result' })); }
185
- catch { process.stdout.write(JSON.stringify({ ok: false, error: 'result not serializable' })); }
186
- });
187
- `;
@@ -1,67 +0,0 @@
1
- /**
2
- * Enrichment pass (spec T7): merge a resolved composition edge list into the
3
- * extracted components' `allowedComponents`. Runs AFTER extraction; it is the
4
- * mapping resolver's entire job — populating the one field every downstream
5
- * graph consumer reads.
6
- *
7
- * Slot targeting:
8
- * - edge.slot present + slot exists → write there.
9
- * - edge.slot present + slot missing → synthesize for high-trust
10
- * (typed-slot / adapter) edges; drop-and-warn for `agent` edges.
11
- * - edge.slot absent → default slot (isDefault), synthesized if none exists.
12
- *
13
- * Edges naming unknown parents/children are dropped-and-warned (§1.3). Inputs
14
- * are not mutated; a deep-enough clone of touched slots is returned.
15
- */
16
- export function applyMapping(components, edges) {
17
- const warnings = [];
18
- const names = new Set(components.map((c) => c.name));
19
- // Clone components (and their slots) so inputs stay untouched.
20
- const cloned = components.map((c) => ({
21
- ...c,
22
- slots: c.slots.map((s) => ({
23
- ...s,
24
- ...(s.allowedComponents ? { allowedComponents: [...s.allowedComponents] } : {}),
25
- })),
26
- }));
27
- const byName = new Map(cloned.map((c) => [c.name, c]));
28
- const isHighTrust = (p) => p === 'user' || p === 'typed-slot' || p.startsWith('adapter:');
29
- const addAllowed = (slot, child) => {
30
- const set = new Set(slot.allowedComponents ?? []);
31
- set.add(child);
32
- slot.allowedComponents = [...set];
33
- };
34
- for (const edge of edges) {
35
- if (!names.has(edge.parent)) {
36
- warnings.push(`dropped edge: unknown parent component "${edge.parent}" (${edge.parent}→${edge.child})`);
37
- continue;
38
- }
39
- if (!names.has(edge.child)) {
40
- warnings.push(`dropped edge: unknown child component "${edge.child}" (${edge.parent}→${edge.child})`);
41
- continue;
42
- }
43
- const parent = byName.get(edge.parent);
44
- if (edge.slot) {
45
- const named = parent.slots.find((s) => s.name === edge.slot);
46
- if (named) {
47
- addAllowed(named, edge.child);
48
- continue;
49
- }
50
- if (isHighTrust(edge.provenance)) {
51
- const synthesized = { name: edge.slot, isDefault: false, allowedComponents: [edge.child] };
52
- parent.slots.push(synthesized);
53
- continue;
54
- }
55
- warnings.push(`dropped edge: slot "${edge.slot}" not found on "${edge.parent}" (agent-provenance; ${edge.parent}→${edge.child})`);
56
- continue;
57
- }
58
- // Default slot.
59
- let def = parent.slots.find((s) => s.isDefault);
60
- if (!def) {
61
- def = { name: 'children', isDefault: true, allowedComponents: [] };
62
- parent.slots.push(def);
63
- }
64
- addAllowed(def, edge.child);
65
- }
66
- return { components: cloned, warnings };
67
- }
@@ -1,39 +0,0 @@
1
- /**
2
- * Prompt + reply parser for the directory completeness critic. Cheap: the
3
- * agent sees only directory NAMES (no file contents) and returns which look
4
- * like they might hold composition/mapping declarations.
5
- */
6
- import { loadPrompt } from './agent-parser/load-prompt.js';
7
- export function buildDirCriticPrompt(dirs) {
8
- return [loadPrompt('composition-dir-critic.md').trim(), '', 'Directories:', ...dirs.map((d) => `- ${d}`)].join('\n');
9
- }
10
- /**
11
- * Parse the agent's reply into the chosen directories, keeping only ones that
12
- * were offered (no injection). Lenient: extracts the first JSON array from
13
- * surrounding prose; malformed / absent → [].
14
- */
15
- export function parseDirCriticReply(reply, offered) {
16
- const start = reply.indexOf('[');
17
- const end = reply.indexOf(']', start);
18
- if (start === -1 || end === -1)
19
- return [];
20
- let arr;
21
- try {
22
- arr = JSON.parse(reply.slice(start, end + 1));
23
- }
24
- catch {
25
- return [];
26
- }
27
- if (!Array.isArray(arr))
28
- return [];
29
- const offeredSet = new Set(offered);
30
- const out = [];
31
- const seen = new Set();
32
- for (const v of arr) {
33
- if (typeof v === 'string' && offeredSet.has(v) && !seen.has(v)) {
34
- seen.add(v);
35
- out.push(v);
36
- }
37
- }
38
- return out;
39
- }
@@ -1,63 +0,0 @@
1
- /**
2
- * Completeness critic for the candidate-file filter (design: the heuristic can
3
- * miss a composition-layer directory whose name we didn't anticipate). We show
4
- * the agent the DIRECTORIES it did NOT pick — names only, no file contents, so
5
- * it's cheap — and let it flag ones that look promising from the path alone.
6
- * Flagged dirs' files are folded into the prompt sample.
7
- *
8
- * This only widens the AUTHORING-PROMPT sample; the parser already runs over
9
- * every file at runtime, so this is purely "help the agent see the convention",
10
- * never a correctness dependency.
11
- */
12
- function dirOf(path) {
13
- const i = path.lastIndexOf('/');
14
- return i === -1 ? '' : path.slice(0, i);
15
- }
16
- /** Directories present in `all` but with no file in `selected`, sorted+deduped. */
17
- export function uncoveredDirectories(all, selected) {
18
- const coveredDirs = new Set(selected.map((f) => dirOf(f.path)));
19
- const out = new Set();
20
- for (const f of all) {
21
- const d = dirOf(f.path);
22
- if (d !== '' && !coveredDirs.has(d))
23
- out.add(d);
24
- }
25
- return [...out].sort();
26
- }
27
- /** Union `selected` with every `all` file whose directory is in `chosenDirs`. */
28
- export function expandCandidatesByDirs(all, selected, chosenDirs) {
29
- const chosen = new Set(chosenDirs);
30
- const seen = new Set(selected.map((f) => f.path));
31
- const out = [...selected];
32
- for (const f of all) {
33
- if (chosen.has(dirOf(f.path)) && !seen.has(f.path)) {
34
- seen.add(f.path);
35
- out.push(f);
36
- }
37
- }
38
- return out;
39
- }
40
- /**
41
- * Run the completeness critic. `askDirs` receives the uncovered directory names
42
- * and returns the subset the agent judges composition-relevant. We only honor
43
- * dirs that were actually offered (no injection), and any error from the agent
44
- * falls back to the original selection — the critic can only ever ADD, never
45
- * break resolution.
46
- */
47
- export async function critiqueCandidates(all, selected, askDirs) {
48
- const uncovered = uncoveredDirectories(all, selected);
49
- if (uncovered.length === 0)
50
- return { files: selected, addedDirs: [] };
51
- let chosen;
52
- try {
53
- chosen = await askDirs(uncovered);
54
- }
55
- catch {
56
- return { files: selected, addedDirs: [] };
57
- }
58
- const offered = new Set(uncovered);
59
- const addedDirs = [...new Set(chosen)].filter((d) => offered.has(d)).sort();
60
- if (addedDirs.length === 0)
61
- return { files: selected, addedDirs: [] };
62
- return { files: expandCandidatesByDirs(all, selected, addedDirs), addedDirs };
63
- }
@@ -1,131 +0,0 @@
1
- export const CANDIDATE_NAME_PATTERNS = [/mapping/i, /meta/i, /registry/i, /schema/i, /composition/i];
2
- export const CANDIDATE_CONTENT_MARKERS = [
3
- 'requiredParent',
4
- 'withParentType',
5
- 'allowedTagNames',
6
- 'createContext',
7
- 'MappingContext',
8
- 'allowedComponents',
9
- ];
10
- export const CANDIDATE_TOKEN_BUDGET = 6000;
11
- /**
12
- * Token ceiling for the candidate set INLINED into a single agent prompt.
13
- * Sized to stay under a 200k-context model once the agent's own system prompt
14
- * + tool definitions (~105k observed) are added, so a large design system
15
- * (hundreds of components → many matched files) can't overflow the request and
16
- * fail resolution. Files beyond the budget are dropped (with a warning), not
17
- * silently truncated. Distinct from `CANDIDATE_TOKEN_BUDGET`, which sizes the
18
- * unused per-batch chunking.
19
- */
20
- export const PROMPT_CANDIDATE_TOKEN_BUDGET = 80_000;
21
- const CHARS_PER_TOKEN = 4;
22
- const DEFAULT_SLICE_WINDOW = 3;
23
- function matchReason(file) {
24
- // Test the name patterns against EVERY path segment (directory names + the
25
- // basename), not just the basename. A mapping-layer file that only DEFINES a
26
- // component — e.g. src/mapping/call_to_action.ts, no withParentType of its
27
- // own — must still be selected so the resolver can resolve OTHER files'
28
- // parent references to it. Matching only the basename silently dropped these.
29
- const segments = file.path.split('/').filter((s) => s !== '');
30
- for (const pattern of CANDIDATE_NAME_PATTERNS) {
31
- if (segments.some((seg) => pattern.test(seg))) {
32
- return `name:${pattern.source.toLowerCase()}`;
33
- }
34
- }
35
- for (const marker of CANDIDATE_CONTENT_MARKERS) {
36
- if (file.content.includes(marker))
37
- return `content:${marker}`;
38
- }
39
- return undefined;
40
- }
41
- export function selectCandidateFiles(files) {
42
- const selected = [];
43
- const seen = new Set();
44
- for (const file of files) {
45
- if (seen.has(file.path))
46
- continue;
47
- const reason = matchReason(file);
48
- if (reason === undefined)
49
- continue;
50
- seen.add(file.path);
51
- selected.push({ path: file.path, content: file.content, reason });
52
- }
53
- return selected;
54
- }
55
- /**
56
- * Cap a candidate set to what fits in a single agent prompt (see
57
- * `PROMPT_CANDIDATE_TOKEN_BUDGET`). Files are kept smallest-first so the budget
58
- * admits the most declarations; deterministic tie-break by path. Returns the
59
- * kept files plus the paths dropped so the caller can warn (silent truncation
60
- * would read as "resolved everything" when it didn't).
61
- */
62
- export function capCandidatesToPromptBudget(files, budget = PROMPT_CANDIDATE_TOKEN_BUDGET) {
63
- const ordered = [...files].sort((a, b) => a.content.length - b.content.length || a.path.localeCompare(b.path));
64
- const kept = [];
65
- const dropped = [];
66
- let spent = 0;
67
- for (const file of ordered) {
68
- const cost = tokenCost(file);
69
- if (spent + cost > budget) {
70
- dropped.push(file);
71
- continue;
72
- }
73
- kept.push(file);
74
- spent += cost;
75
- }
76
- return { kept, dropped };
77
- }
78
- export function sliceDeclarationRegions(content, markers = CANDIDATE_CONTENT_MARKERS, window = DEFAULT_SLICE_WINDOW) {
79
- const lines = content.split('\n');
80
- const hitLines = [];
81
- for (let i = 0; i < lines.length; i++) {
82
- if (markers.some((marker) => lines[i].includes(marker)))
83
- hitLines.push(i);
84
- }
85
- if (hitLines.length === 0)
86
- return [];
87
- const ranges = [];
88
- for (const hit of hitLines) {
89
- const start = Math.max(0, hit - window);
90
- const end = Math.min(lines.length - 1, hit + window);
91
- const last = ranges[ranges.length - 1];
92
- if (last && start <= last.end + 1) {
93
- last.end = Math.max(last.end, end);
94
- }
95
- else {
96
- ranges.push({ start, end });
97
- }
98
- }
99
- return ranges.map((r) => lines.slice(r.start, r.end + 1).join('\n'));
100
- }
101
- function tokenCost(file) {
102
- return Math.ceil(file.content.length / CHARS_PER_TOKEN);
103
- }
104
- export function batchCandidates(files, budget = CANDIDATE_TOKEN_BUDGET) {
105
- const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
106
- const batches = [];
107
- let current = [];
108
- let currentCost = 0;
109
- for (const file of sorted) {
110
- const cost = tokenCost(file);
111
- if (cost > budget) {
112
- if (current.length > 0) {
113
- batches.push(current);
114
- current = [];
115
- currentCost = 0;
116
- }
117
- batches.push([file]);
118
- continue;
119
- }
120
- if (current.length > 0 && currentCost + cost > budget) {
121
- batches.push(current);
122
- current = [];
123
- currentCost = 0;
124
- }
125
- current.push(file);
126
- currentCost += cost;
127
- }
128
- if (current.length > 0)
129
- batches.push(current);
130
- return batches;
131
- }
@@ -1,19 +0,0 @@
1
- import { hashContent } from '../../session/cache-keys.js';
2
- /**
3
- * Derive the `input_hash` for a composition-cache entry (stored in the pipeline
4
- * DB's `composition_cache` table). Content-addresses the candidate file set
5
- * (order-independent) plus the producing agent and the resolution kind, so a
6
- * re-run over unchanged files reuses the (token-costly) agent output.
7
- *
8
- * Content-hashing — not mtime — means a no-op `git checkout` that only bumps
9
- * mtime is still a hit. Prompt/skill changes are handled by the `cli_version`
10
- * column on the row (see `getCliCacheVersion`), so no separate resolver-version
11
- * constant is needed here.
12
- */
13
- export function buildCompositionInputHash(input) {
14
- const fileDigest = input.files
15
- .map((f) => `${f.path} ${hashContent(f.content)}`)
16
- .sort()
17
- .join('');
18
- return hashContent(`${input.kind}:agent:${input.agent}\n${fileDigest}`);
19
- }