mdx-artifacts 0.1.2 → 0.2.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 (141) hide show
  1. package/README.md +211 -59
  2. package/README.zh-CN.md +299 -42
  3. package/agents/AGENTS.snippet.md +21 -10
  4. package/artifact-docs/examples/commentable-feedback.mdx +76 -70
  5. package/artifact-docs/examples/decision-matrix.mdx +122 -50
  6. package/artifact-docs/examples/layout-composition.mdx +106 -128
  7. package/artifact-docs/examples/streamlit-style-mixed.mdx +100 -85
  8. package/dist/lib/cli/{build.js → commands/build.js} +2 -2
  9. package/dist/lib/cli/{components.js → commands/components.js} +19 -3
  10. package/dist/lib/cli/{dev.js → commands/dev.js} +2 -2
  11. package/dist/lib/cli/commands/interactions.d.ts +2 -0
  12. package/dist/lib/cli/commands/interactions.js +280 -0
  13. package/dist/lib/cli/commands/review.d.ts +1 -0
  14. package/dist/lib/cli/commands/review.js +171 -0
  15. package/dist/lib/cli/commands/scaffold.d.ts +16 -0
  16. package/dist/lib/cli/commands/scaffold.js +440 -0
  17. package/dist/lib/cli/commands/validate.d.ts +18 -0
  18. package/dist/lib/cli/commands/validate.js +311 -0
  19. package/dist/lib/cli/config/config.d.ts +2 -0
  20. package/dist/lib/cli/{config.js → config/config.js} +3 -2
  21. package/dist/lib/cli/{types.d.ts → config/types.d.ts} +2 -1
  22. package/dist/lib/cli/{vite-artifact.d.ts → dev-server/vite-artifact.d.ts} +3 -3
  23. package/dist/lib/cli/{vite-artifact.js → dev-server/vite-artifact.js} +170 -10
  24. package/dist/lib/cli/diagnostics/diagnostics.d.ts +11 -0
  25. package/dist/lib/cli/diagnostics/diagnostics.js +1 -0
  26. package/dist/lib/cli/index.js +39 -18
  27. package/dist/lib/cli/mdx/sortable-list.d.ts +14 -0
  28. package/dist/lib/cli/mdx/sortable-list.js +520 -0
  29. package/dist/lib/cli/resources/resource-policy.d.ts +15 -0
  30. package/dist/lib/cli/resources/resource-policy.js +46 -0
  31. package/dist/lib/cli/resources/safe-path.d.ts +13 -0
  32. package/dist/lib/cli/resources/safe-path.js +55 -0
  33. package/dist/lib/cli/services/interaction-service.d.ts +40 -0
  34. package/dist/lib/cli/services/interaction-service.js +226 -0
  35. package/dist/lib/cli/services/review.d.ts +43 -0
  36. package/dist/lib/cli/{review.js → services/review.js} +34 -172
  37. package/dist/lib/react/{components → composites/comparison-set}/ComparisonSet.d.ts +1 -1
  38. package/dist/lib/react/{components → composites/comparison-set}/ComparisonSet.js +3 -3
  39. package/dist/lib/react/composites/comparison-set/index.d.ts +2 -0
  40. package/dist/lib/react/composites/comparison-set/index.js +1 -0
  41. package/dist/lib/react/composites/content-set/ContentItem.d.ts +37 -0
  42. package/dist/lib/react/composites/content-set/ContentItem.js +49 -0
  43. package/dist/lib/react/composites/content-set/index.d.ts +2 -0
  44. package/dist/lib/react/composites/content-set/index.js +1 -0
  45. package/dist/lib/react/{components → composites/export-panel}/ExportPanel.js +2 -2
  46. package/dist/lib/react/composites/export-panel/index.d.ts +2 -0
  47. package/dist/lib/react/composites/export-panel/index.js +1 -0
  48. package/dist/lib/react/{components → composites/section}/Section.js +1 -1
  49. package/dist/lib/react/composites/section/index.d.ts +2 -0
  50. package/dist/lib/react/composites/section/index.js +1 -0
  51. package/dist/lib/react/index.d.ts +36 -31
  52. package/dist/lib/react/index.js +18 -15
  53. package/dist/lib/react/interactions/artifact-state/index.d.ts +1 -0
  54. package/dist/lib/react/interactions/artifact-state/index.js +1 -0
  55. package/dist/lib/react/{components → interactions/comments}/Comments.d.ts +2 -2
  56. package/dist/lib/react/{components → interactions/comments}/Comments.js +3 -3
  57. package/dist/lib/react/interactions/comments/index.d.ts +1 -0
  58. package/dist/lib/react/interactions/comments/index.js +1 -0
  59. package/dist/lib/react/interactions/sortable-list/SortableList.d.ts +29 -0
  60. package/dist/lib/react/interactions/sortable-list/SortableList.js +282 -0
  61. package/dist/lib/react/interactions/sortable-list/index.d.ts +1 -0
  62. package/dist/lib/react/interactions/sortable-list/index.js +1 -0
  63. package/dist/lib/react/layout/layout-primitives/index.d.ts +2 -0
  64. package/dist/lib/react/layout/layout-primitives/index.js +1 -0
  65. package/dist/lib/react/legacy/LegacyContentComponents.d.ts +65 -0
  66. package/dist/lib/react/legacy/LegacyContentComponents.js +26 -0
  67. package/dist/lib/react/mdx-components.d.ts +5 -0
  68. package/dist/lib/react/mdx-components.js +38 -0
  69. package/dist/lib/react/{components → primitives/annotated-code}/AnnotatedCode.d.ts +1 -1
  70. package/dist/lib/react/{components → primitives/annotated-code}/AnnotatedCode.js +5 -5
  71. package/dist/lib/react/primitives/annotated-code/index.d.ts +2 -0
  72. package/dist/lib/react/primitives/annotated-code/index.js +1 -0
  73. package/dist/lib/react/primitives/callout/Callout.d.ts +11 -0
  74. package/dist/lib/react/{components → primitives/callout}/Callout.js +9 -6
  75. package/dist/lib/react/primitives/callout/index.d.ts +2 -0
  76. package/dist/lib/react/primitives/callout/index.js +1 -0
  77. package/dist/lib/react/primitives/code-block/CodeBlock.d.ts +20 -0
  78. package/dist/lib/react/primitives/code-block/CodeBlock.js +32 -0
  79. package/dist/lib/react/primitives/code-block/index.d.ts +2 -0
  80. package/dist/lib/react/primitives/code-block/index.js +1 -0
  81. package/dist/lib/react/primitives/code-surface/CodeSurface.d.ts +11 -0
  82. package/dist/lib/react/primitives/code-surface/CodeSurface.js +34 -0
  83. package/dist/lib/react/primitives/code-surface/index.d.ts +2 -0
  84. package/dist/lib/react/primitives/code-surface/index.js +1 -0
  85. package/dist/lib/react/primitives/diff-block/DiffBlock.js +25 -0
  86. package/dist/lib/react/primitives/diff-block/index.d.ts +2 -0
  87. package/dist/lib/react/primitives/diff-block/index.js +1 -0
  88. package/dist/lib/react/{components → primitives/inline-text}/InlineText.d.ts +4 -2
  89. package/dist/lib/react/primitives/inline-text/InlineText.js +28 -0
  90. package/dist/lib/react/primitives/inline-text/index.d.ts +2 -0
  91. package/dist/lib/react/primitives/inline-text/index.js +1 -0
  92. package/dist/lib/react/primitives/markdown-body/MarkdownBody.d.ts +9 -0
  93. package/dist/lib/react/primitives/markdown-body/MarkdownBody.js +49 -0
  94. package/dist/lib/react/primitives/markdown-body/index.d.ts +2 -0
  95. package/dist/lib/react/primitives/markdown-body/index.js +1 -0
  96. package/dist/lib/react/primitives/severity-badge/index.d.ts +2 -0
  97. package/dist/lib/react/primitives/severity-badge/index.js +1 -0
  98. package/dist/lib/react/registry.d.ts +10 -0
  99. package/dist/lib/react/registry.js +505 -210
  100. package/dist/lib/react/styles.css +490 -38
  101. package/docs/cli-structure.md +141 -0
  102. package/docs/component-protocol.md +199 -33
  103. package/docs/component-taxonomy.md +40 -4
  104. package/docs/design.md +42 -21
  105. package/docs/design.zh-CN.md +41 -21
  106. package/docs/naming.md +17 -7
  107. package/docs/releasing.md +132 -0
  108. package/docs/testing.md +35 -10
  109. package/package.json +9 -7
  110. package/dist/lib/cli/config.d.ts +0 -2
  111. package/dist/lib/cli/review.d.ts +0 -33
  112. package/dist/lib/cli/scaffold.d.ts +0 -1
  113. package/dist/lib/cli/scaffold.js +0 -56
  114. package/dist/lib/cli/validate.d.ts +0 -6
  115. package/dist/lib/cli/validate.js +0 -79
  116. package/dist/lib/react/components/Callout.d.ts +0 -9
  117. package/dist/lib/react/components/CodeBlock.d.ts +0 -10
  118. package/dist/lib/react/components/CodeBlock.js +0 -28
  119. package/dist/lib/react/components/DecisionMatrix.d.ts +0 -16
  120. package/dist/lib/react/components/DecisionMatrix.js +0 -27
  121. package/dist/lib/react/components/DiffBlock.js +0 -24
  122. package/dist/lib/react/components/InlineText.js +0 -18
  123. package/dist/lib/react/components/MarkdownBody.d.ts +0 -7
  124. package/dist/lib/react/components/MarkdownBody.js +0 -36
  125. package/dist/lib/react/components/OptionGrid.d.ts +0 -13
  126. package/dist/lib/react/components/OptionGrid.js +0 -21
  127. /package/dist/lib/cli/{build.d.ts → commands/build.d.ts} +0 -0
  128. /package/dist/lib/cli/{components.d.ts → commands/components.d.ts} +0 -0
  129. /package/dist/lib/cli/{dev.d.ts → commands/dev.d.ts} +0 -0
  130. /package/dist/lib/cli/{types.js → config/types.js} +0 -0
  131. /package/dist/lib/cli/{artifact-state.d.ts → state/artifact-state.d.ts} +0 -0
  132. /package/dist/lib/cli/{artifact-state.js → state/artifact-state.js} +0 -0
  133. /package/dist/lib/react/{components → composites/export-panel}/ExportPanel.d.ts +0 -0
  134. /package/dist/lib/react/{components → composites/section}/Section.d.ts +0 -0
  135. /package/dist/lib/react/{components → interactions/artifact-state}/ArtifactState.d.ts +0 -0
  136. /package/dist/lib/react/{components → interactions/artifact-state}/ArtifactState.js +0 -0
  137. /package/dist/lib/react/{components → layout/layout-primitives}/Layout.d.ts +0 -0
  138. /package/dist/lib/react/{components → layout/layout-primitives}/Layout.js +0 -0
  139. /package/dist/lib/react/{components → primitives/diff-block}/DiffBlock.d.ts +0 -0
  140. /package/dist/lib/react/{components → primitives/severity-badge}/SeverityBadge.d.ts +0 -0
  141. /package/dist/lib/react/{components → primitives/severity-badge}/SeverityBadge.js +0 -0
@@ -2,11 +2,10 @@ import {
2
2
  Callout,
3
3
  CodeBlock,
4
4
  ComparisonSet,
5
- DecisionMatrix,
5
+ ContentSet,
6
6
  ExportPanel,
7
7
  Frame,
8
8
  MarkdownBody,
9
- OptionGrid,
10
9
  } from "mdx-artifacts/react";
11
10
 
12
11
  # Streamlit-Style Linear Artifact
@@ -28,56 +27,60 @@ Streamlit works well because authors can write a small amount of text, drop in a
28
27
  id="callout.linear-check"
29
28
  tone="info"
30
29
  title="What this example is checking"
31
- body="The artifact should read as one continuous document, not as disconnected component demos."
32
- />
30
+ >
31
+ The artifact should read as one continuous document, not as disconnected component demos.
32
+ </Callout>
33
33
 
34
34
  ## Decision point
35
35
 
36
36
  The first question is whether the core authoring experience should stay close to native MDX or move toward more explicit text components.
37
37
 
38
- <DecisionMatrix
39
- id="decision.native-mdx"
40
- question="Should top-level prose use native MDX by default?"
41
- options={[
42
- {
43
- id: "native-mdx",
44
- name: "Native MDX prose",
45
- summary:
46
- "Use regular headings, paragraphs, lists, links, and blockquotes between workflow components.",
47
- pros: [
48
- "Shortest authoring path",
49
- "Agent-friendly",
50
- "No extra text API to learn",
51
- ],
52
- cons: [
53
- "Needs strong default prose styling",
54
- "Harder to constrain every edge case",
55
- ],
56
- confidence: "high",
57
- verdict: "Recommended baseline",
58
- },
59
- {
60
- id: "text-components",
61
- name: "Text components everywhere",
62
- summary:
63
- "Require components such as InlineText and MarkdownBody for most visible copy.",
64
- pros: ["More controlled rendering", "Clear metadata boundary"],
65
- cons: ["Verbose for simple documents", "Less natural than MDX"],
66
- confidence: "medium",
67
- verdict: "Use inside reusable components",
68
- },
69
- {
70
- id: "raw-html",
71
- name: "Raw HTML escape hatch",
72
- summary:
73
- "Let authors write arbitrary HTML when layout or text rendering gets specific.",
74
- pros: ["Maximum flexibility"],
75
- cons: ["Style drift", "Weak validation", "Harder for agents to reuse"],
76
- confidence: "low",
77
- verdict: "Avoid unless necessary",
78
- },
79
- ]}
80
- />
38
+ <ContentSet
39
+ id="set.native-mdx"
40
+ title="Should top-level prose use native MDX by default?"
41
+ layout="grid"
42
+ columns={3}
43
+ >
44
+ <ContentSet.Item
45
+ id="native-mdx"
46
+ title="Native MDX prose"
47
+ badge="Recommended"
48
+ tone="positive"
49
+ emphasis="primary"
50
+ summary="Use regular headings, paragraphs, lists, links, and blockquotes between workflow components."
51
+ >
52
+ - Shortest authoring path
53
+ - Agent-friendly
54
+ - No extra text API to learn
55
+ - Needs strong default prose styling
56
+ - Harder to constrain every edge case
57
+ </ContentSet.Item>
58
+ <ContentSet.Item
59
+ id="text-components"
60
+ title="Text components everywhere"
61
+ badge="Component-local"
62
+ tone="info"
63
+ summary="Require components such as `InlineText` and `MarkdownBody` for most visible copy."
64
+ >
65
+ - More controlled rendering
66
+ - Clear metadata boundary
67
+ - Verbose for simple documents
68
+ - Less natural than MDX
69
+ </ContentSet.Item>
70
+ <ContentSet.Item
71
+ id="raw-html"
72
+ title="Raw HTML escape hatch"
73
+ badge="Avoid"
74
+ tone="danger"
75
+ emphasis="subtle"
76
+ summary="Let authors write arbitrary HTML when layout or text rendering gets specific."
77
+ >
78
+ - Maximum flexibility
79
+ - Style drift
80
+ - Weak validation
81
+ - Harder for agents to reuse
82
+ </ContentSet.Item>
83
+ </ContentSet>
81
84
 
82
85
  After the decision block, the document should continue naturally. This paragraph is intentionally plain MDX so the spacing between prose and the component above is easy to judge.
83
86
 
@@ -87,40 +90,50 @@ After the decision block, the document should continue naturally. This paragraph
87
90
 
88
91
  The next block lists the current component roles. It should feel like a continuation of the same document, not a separate app screen.
89
92
 
90
- <OptionGrid
91
- id="option.linear-building-blocks"
93
+ <ContentSet
94
+ id="set.linear-building-blocks"
92
95
  title="Linear authoring building blocks"
93
- options={[
94
- {
95
- id: "native-mdx",
96
- name: "Native MDX",
97
- intent: "Own top-level narrative flow.",
98
- tradeoffs: [
99
- "Best for headings and explanatory paragraphs",
100
- "Needs polished document-level CSS",
101
- ],
102
- },
103
- {
104
- id: "markdown-body",
105
- name: "MarkdownBody",
106
- intent: "Own controlled body copy inside a component slot.",
107
- tradeoffs: [
108
- "Good for reusable components",
109
- "Less convenient for top-level prose",
110
- ],
111
- },
112
- {
113
- id: "workflow-components",
114
- name: "Workflow components",
115
- intent:
116
- "Own structured decisions, comparisons, code notes, and export surfaces.",
117
- tradeoffs: [
118
- "Agent-queryable through the registry",
119
- "Should stay higher-level than raw cards",
120
- ],
121
- },
122
- ]}
123
- />
96
+ layout="grid"
97
+ columns={3}
98
+ >
99
+ <ContentSet.Item
100
+ id="native-mdx"
101
+ title="Native MDX"
102
+ badge="Document"
103
+ tone="info"
104
+ emphasis="primary"
105
+ summary="Own top-level narrative flow."
106
+ >
107
+ Use this for the document's main reading order.
108
+
109
+ - Best for headings and explanatory paragraphs
110
+ - Needs polished document-level CSS
111
+ </ContentSet.Item>
112
+ <ContentSet.Item
113
+ id="markdown-body"
114
+ title="MarkdownBody"
115
+ badge="Controlled"
116
+ tone="neutral"
117
+ summary="Own controlled body copy inside a component slot."
118
+ >
119
+ Use this when reusable components need safe Markdown rendering.
120
+
121
+ - Good for reusable components
122
+ - Less convenient for top-level prose
123
+ </ContentSet.Item>
124
+ <ContentSet.Item
125
+ id="workflow-components"
126
+ title="Workflow components"
127
+ badge="Reusable"
128
+ tone="accent"
129
+ summary="Own structured decisions, comparisons, code notes, and export surfaces."
130
+ >
131
+ Use this for reusable artifact meaning rather than layout-only cards.
132
+
133
+ - Agent-queryable through the registry
134
+ - Should stay higher-level than raw cards
135
+ </ContentSet.Item>
136
+ </ContentSet>
124
137
 
125
138
  ## Mixed content
126
139
 
@@ -131,8 +144,9 @@ Here is a small comparison set with component-local Markdown inside each item. T
131
144
  <Frame surface="subtle">
132
145
  <MarkdownBody
133
146
  variant="compact"
134
- body="Use this for the document's main reading order: headings, paragraphs, lists, and short notes between components."
135
- />
147
+ >
148
+ Use this for the document's main reading order: headings, paragraphs, lists, and short notes between components.
149
+ </MarkdownBody>
136
150
  </Frame>
137
151
  </ComparisonSet.Item>
138
152
 
@@ -140,8 +154,9 @@ Here is a small comparison set with component-local Markdown inside each item. T
140
154
  <Frame surface="subtle">
141
155
  <MarkdownBody
142
156
  variant="compact"
143
- body="Use this when a component needs a controlled body field with safe Markdown rendering."
144
- />
157
+ >
158
+ Use this when a component needs a controlled body slot with safe Markdown rendering.
159
+ </MarkdownBody>
145
160
  </Frame>
146
161
  </ComparisonSet.Item>
147
162
 
@@ -154,7 +169,7 @@ Here is a small comparison set with component-local Markdown inside each item. T
154
169
 
155
170
  Plain MDX prose.
156
171
 
157
- <DecisionMatrix ... />
172
+ <ContentSet ... />
158
173
 
159
174
  More prose.`}
160
175
  />
@@ -1,7 +1,7 @@
1
1
  import { mkdir, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { loadConfig } from "./config.js";
4
- import { buildArtifact, createArtifactProject } from "./vite-artifact.js";
3
+ import { loadConfig } from "../config/config.js";
4
+ import { buildArtifact, createArtifactProject } from "../dev-server/vite-artifact.js";
5
5
  export async function buildCommand(projectRoot, input) {
6
6
  const config = await loadConfig(projectRoot);
7
7
  const mdxPath = path.resolve(projectRoot, input);
@@ -1,4 +1,4 @@
1
- import { componentRegistry, findComponentMeta } from "../react/registry.js";
1
+ import { componentRegistry, findComponentMeta } from "../../react/registry.js";
2
2
  export function componentsCommand(input, options = {}) {
3
3
  if (input) {
4
4
  const component = findComponentMeta(input);
@@ -20,15 +20,31 @@ export function componentsCommand(input, options = {}) {
20
20
  for (const component of componentRegistry) {
21
21
  console.log(`- ${component.name}: ${component.description}`);
22
22
  }
23
- console.log("\nUse `artifact-kit components <ComponentName>` to inspect props and examples.");
24
- console.log("Use `artifact-kit components --json` for machine-readable metadata.");
23
+ console.log("\nAuthoring rule: prefer MDX children for human-readable content; use props for stable ids, variants, layout controls, export values, and structured data.");
24
+ console.log("\nUse `mdx-artifacts components <ComponentName>` to inspect props, authoring guidance, and examples.");
25
+ console.log("Use `mdx-artifacts components --json` for machine-readable metadata.");
25
26
  }
26
27
  function printComponent(component) {
27
28
  console.log(`${component.name}\n`);
28
29
  console.log(component.description);
30
+ if (component.deprecated) {
31
+ console.log(`\nDeprecated: use ${component.deprecated.replacement} for new artifacts.`);
32
+ for (const item of component.deprecated.guidance) {
33
+ console.log(`- ${item}`);
34
+ }
35
+ console.log("\nReplacement example:");
36
+ console.log(component.example);
37
+ return;
38
+ }
29
39
  if (component.category || component.stability) {
30
40
  console.log(`\nMetadata: ${[component.category ? `category=${component.category}` : undefined, component.stability ? `stability=${component.stability}` : undefined].filter(Boolean).join(", ")}`);
31
41
  }
42
+ if (component.authoring) {
43
+ console.log(`\nAuthoring: ${component.authoring.kind}`);
44
+ for (const item of component.authoring.guidance) {
45
+ console.log(`- ${item}`);
46
+ }
47
+ }
32
48
  console.log("\nUse when:");
33
49
  for (const item of component.useWhen) {
34
50
  console.log(`- ${item}`);
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
- import { loadConfig } from "./config.js";
3
- import { createArtifactProject, startDevServer } from "./vite-artifact.js";
2
+ import { loadConfig } from "../config/config.js";
3
+ import { createArtifactProject, startDevServer } from "../dev-server/vite-artifact.js";
4
4
  export async function devCommand(projectRoot, input) {
5
5
  const config = await loadConfig(projectRoot);
6
6
  const mdxPath = path.resolve(projectRoot, input);
@@ -0,0 +1,2 @@
1
+ export type { InteractionsInspectResult } from "../services/interaction-service";
2
+ export declare function interactionsCommand(projectRoot: string, args: string[]): Promise<void>;
@@ -0,0 +1,280 @@
1
+ import { addInteractionItemService, inspectInteractionService, promoteInteractionService, removeInteractionItemService, resetInteractionService, setInteractionOrderService, updateInteractionItemService } from "../services/interaction-service.js";
2
+ export async function interactionsCommand(projectRoot, args) {
3
+ const [subcommand] = args;
4
+ if (subcommand === "inspect") {
5
+ const options = parseInspectArgs(args.slice(1));
6
+ const result = await inspectInteractionService(projectRoot, options.input, options.id);
7
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatInspectResult(result));
8
+ return;
9
+ }
10
+ if (subcommand === "set-order") {
11
+ const options = parseSetOrderArgs(args.slice(1));
12
+ const result = await setInteractionOrderService(projectRoot, options.input, options.id, options.orderedIds);
13
+ console.log(formatMutationResult(result));
14
+ return;
15
+ }
16
+ if (subcommand === "reset") {
17
+ const options = parseResetArgs(args.slice(1));
18
+ const result = await resetInteractionService(projectRoot, options.input, options.id);
19
+ console.log(formatMutationResult(result));
20
+ return;
21
+ }
22
+ if (subcommand === "promote") {
23
+ const options = parsePromoteArgs(args.slice(1));
24
+ const result = await promoteInteractionService(projectRoot, options.input, options.id);
25
+ console.log(formatMutationResult(result));
26
+ return;
27
+ }
28
+ if (subcommand === "add-item") {
29
+ const options = parseAddItemArgs(args.slice(1));
30
+ const result = await addInteractionItemService(projectRoot, options.input, options.id, options.item, {
31
+ afterId: options.afterId
32
+ });
33
+ console.log(formatMutationResult(result));
34
+ return;
35
+ }
36
+ if (subcommand === "remove-item") {
37
+ const options = parseRemoveItemArgs(args.slice(1));
38
+ const result = await removeInteractionItemService(projectRoot, options.input, options.id, options.itemId);
39
+ console.log(formatMutationResult(result));
40
+ return;
41
+ }
42
+ if (subcommand === "update-item") {
43
+ const options = parseUpdateItemArgs(args.slice(1));
44
+ const result = await updateInteractionItemService(projectRoot, options.input, options.id, options.itemId, options.patch);
45
+ console.log(formatMutationResult(result));
46
+ return;
47
+ }
48
+ throw new Error("interactions requires a subcommand. Use inspect, set-order, reset, promote, add-item, remove-item, or update-item.");
49
+ }
50
+ function parseInspectArgs(args) {
51
+ const positional = args.filter((arg) => arg !== "--json");
52
+ const json = args.includes("--json");
53
+ const [input, id, ...rest] = positional;
54
+ if (!input) {
55
+ throw new Error("interactions inspect requires a .mdx file path.");
56
+ }
57
+ if (!id) {
58
+ throw new Error("interactions inspect requires a component id.");
59
+ }
60
+ if (rest.length > 0) {
61
+ throw new Error(`Unexpected interactions inspect argument: ${rest[0]}`);
62
+ }
63
+ return { input, id, json };
64
+ }
65
+ function parseSetOrderArgs(args) {
66
+ const [input, id, ...rest] = args;
67
+ if (!input) {
68
+ throw new Error("interactions set-order requires a .mdx file path.");
69
+ }
70
+ if (!id) {
71
+ throw new Error("interactions set-order requires a component id.");
72
+ }
73
+ const orderedIds = parseOrderedIds(rest);
74
+ return { input, id, orderedIds };
75
+ }
76
+ function parseResetArgs(args) {
77
+ const [input, id, ...rest] = args;
78
+ if (!input) {
79
+ throw new Error("interactions reset requires a .mdx file path.");
80
+ }
81
+ if (!id) {
82
+ throw new Error("interactions reset requires a component id.");
83
+ }
84
+ if (rest.length > 0) {
85
+ throw new Error(`Unexpected interactions reset argument: ${rest[0]}`);
86
+ }
87
+ return { input, id };
88
+ }
89
+ function parsePromoteArgs(args) {
90
+ const [input, id, ...rest] = args;
91
+ if (!input) {
92
+ throw new Error("interactions promote requires a .mdx file path.");
93
+ }
94
+ if (!id) {
95
+ throw new Error("interactions promote requires a component id.");
96
+ }
97
+ if (rest.length > 0) {
98
+ throw new Error(`Unexpected interactions promote argument: ${rest[0]}`);
99
+ }
100
+ return { input, id };
101
+ }
102
+ function parseAddItemArgs(args) {
103
+ const [input, id, ...rest] = args;
104
+ if (!input) {
105
+ throw new Error("interactions add-item requires a .mdx file path.");
106
+ }
107
+ if (!id) {
108
+ throw new Error("interactions add-item requires a component id.");
109
+ }
110
+ const options = parseOptionMap(rest, ["item-id", "title", "summary", "badge", "tags", "disabled", "after"], []);
111
+ const itemId = options.values.get("item-id");
112
+ const title = options.values.get("title");
113
+ if (!itemId) {
114
+ throw new Error("interactions add-item requires --item-id <id>.");
115
+ }
116
+ if (!title) {
117
+ throw new Error("interactions add-item requires --title <title>.");
118
+ }
119
+ return {
120
+ input,
121
+ id,
122
+ item: createItemFromOptions(itemId, title, options.values),
123
+ ...(options.values.get("after") ? { afterId: options.values.get("after") } : {})
124
+ };
125
+ }
126
+ function parseRemoveItemArgs(args) {
127
+ const [input, id, ...rest] = args;
128
+ if (!input) {
129
+ throw new Error("interactions remove-item requires a .mdx file path.");
130
+ }
131
+ if (!id) {
132
+ throw new Error("interactions remove-item requires a component id.");
133
+ }
134
+ const options = parseOptionMap(rest, ["item-id"], []);
135
+ const itemId = options.values.get("item-id");
136
+ if (!itemId) {
137
+ throw new Error("interactions remove-item requires --item-id <id>.");
138
+ }
139
+ return { input, id, itemId };
140
+ }
141
+ function parseUpdateItemArgs(args) {
142
+ const [input, id, ...rest] = args;
143
+ if (!input) {
144
+ throw new Error("interactions update-item requires a .mdx file path.");
145
+ }
146
+ if (!id) {
147
+ throw new Error("interactions update-item requires a component id.");
148
+ }
149
+ const options = parseOptionMap(rest, ["item-id", "title", "summary", "badge", "tags", "disabled"], ["clear-summary", "clear-badge", "clear-tags"]);
150
+ const itemId = options.values.get("item-id");
151
+ if (!itemId) {
152
+ throw new Error("interactions update-item requires --item-id <id>.");
153
+ }
154
+ const patch = createPatchFromOptions(options.values, options.flags);
155
+ if (Object.keys(patch).length === 0) {
156
+ throw new Error("interactions update-item requires at least one field update.");
157
+ }
158
+ return { input, id, itemId, patch };
159
+ }
160
+ function parseOrderedIds(args) {
161
+ const flagIndex = args.indexOf("--ordered-ids");
162
+ if (flagIndex < 0) {
163
+ throw new Error("interactions set-order requires --ordered-ids <id> [...id].");
164
+ }
165
+ if (flagIndex > 0) {
166
+ throw new Error(`Unexpected interactions set-order argument: ${args[0]}`);
167
+ }
168
+ const orderedIds = args
169
+ .slice(flagIndex + 1)
170
+ .flatMap((value) => value.split(","))
171
+ .map((value) => value.trim())
172
+ .filter(Boolean);
173
+ if (orderedIds.length === 0) {
174
+ throw new Error("interactions set-order requires at least one ordered id.");
175
+ }
176
+ return orderedIds;
177
+ }
178
+ function parseOptionMap(args, valueKeys, flagKeys) {
179
+ const values = new Map();
180
+ const flags = new Set();
181
+ for (let index = 0; index < args.length; index += 1) {
182
+ const name = args[index];
183
+ if (!name?.startsWith("--")) {
184
+ throw new Error(`Unexpected interactions argument: ${name}`);
185
+ }
186
+ const key = name.slice(2);
187
+ if (flagKeys.includes(key)) {
188
+ flags.add(key);
189
+ continue;
190
+ }
191
+ if (!valueKeys.includes(key)) {
192
+ throw new Error(`Unknown interactions option: --${key}.`);
193
+ }
194
+ const value = args[index + 1];
195
+ if (value === undefined || value.startsWith("--")) {
196
+ throw new Error(`Missing value for --${key}.`);
197
+ }
198
+ values.set(key, value);
199
+ index += 1;
200
+ }
201
+ return { flags, values };
202
+ }
203
+ function createItemFromOptions(itemId, title, options) {
204
+ return {
205
+ id: itemId,
206
+ title,
207
+ ...createPatchFromOptions(options, new Set())
208
+ };
209
+ }
210
+ function createPatchFromOptions(options, flags) {
211
+ return {
212
+ ...(options.has("title") ? { title: options.get("title") ?? "" } : {}),
213
+ ...(flags.has("clear-summary")
214
+ ? { summary: undefined }
215
+ : options.has("summary")
216
+ ? { summary: options.get("summary") ?? "" }
217
+ : {}),
218
+ ...(flags.has("clear-badge")
219
+ ? { badge: undefined }
220
+ : options.has("badge")
221
+ ? { badge: options.get("badge") ?? "" }
222
+ : {}),
223
+ ...(flags.has("clear-tags") ? { tags: undefined } : options.has("tags") ? { tags: parseTags(options.get("tags") ?? "") } : {}),
224
+ ...(options.has("disabled") ? { disabled: parseBooleanOption(options.get("disabled") ?? "", "disabled") } : {})
225
+ };
226
+ }
227
+ function parseTags(value) {
228
+ return value
229
+ .split(",")
230
+ .map((tag) => tag.trim())
231
+ .filter(Boolean);
232
+ }
233
+ function parseBooleanOption(value, name) {
234
+ if (value === "true")
235
+ return true;
236
+ if (value === "false")
237
+ return false;
238
+ throw new Error(`--${name} must be true or false.`);
239
+ }
240
+ function formatInspectResult(result) {
241
+ return [
242
+ "interactions inspect ok",
243
+ `source: ${result.source.path}`,
244
+ `state: ${result.state.path}${result.state.exists ? "" : " (not found)"}`,
245
+ `component: ${result.component.id}`,
246
+ `order source: ${result.order.source}`,
247
+ `items: ${result.component.items.length}`,
248
+ "order:",
249
+ ...result.order.orderedIds.map((itemId, index) => {
250
+ const item = result.component.items.find((candidate) => candidate.id === itemId);
251
+ return `${index + 1}. ${itemId}${item ? ` - ${item.title}` : ""}`;
252
+ }),
253
+ ...(result.order.appendedIds.length > 0 ? [`appended: ${result.order.appendedIds.join(", ")}`] : []),
254
+ ...(result.warnings.length > 0 ? ["warnings:", ...result.warnings.map((warning) => `- ${warning}`)] : [])
255
+ ].join("\n");
256
+ }
257
+ function formatMutationResult(result) {
258
+ if (result.action === "promote" && result.status === "noop") {
259
+ return [
260
+ "interactions promote noop",
261
+ `source: ${result.sourcePath}`,
262
+ `component: ${result.component}`,
263
+ `reason: ${result.reason ?? "no runtime overlay"}`
264
+ ].join("\n");
265
+ }
266
+ const lines = [
267
+ `interactions ${result.action} ok`,
268
+ ...(result.action === "reset" || result.action === "set-order" ? [] : [`source: ${result.sourcePath}`]),
269
+ `state: ${result.statePath}`,
270
+ `component: ${result.component}`
271
+ ];
272
+ if (result.itemId) {
273
+ lines.push(`item: ${result.itemId}`);
274
+ }
275
+ if (result.orderedIds) {
276
+ lines.push(`items: ${result.orderedIds.length}`);
277
+ lines.push(`orderedIds: ${result.orderedIds.join(", ")}`);
278
+ }
279
+ return lines.join("\n");
280
+ }
@@ -0,0 +1 @@
1
+ export declare function reviewCommand(projectRoot: string, args: string[]): Promise<void>;