@agent-native/core 0.153.10 → 0.154.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 (128) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/AGENTS.md +20 -30
  3. package/corpus/templates/analytics/actions/search-dashboard-references.ts +58 -0
  4. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
  5. package/corpus/templates/analytics/app/lib/utils.ts +1 -1
  6. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewSqlPopover.tsx +1 -1
  7. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +2 -0
  8. package/corpus/templates/analytics/server/lib/dashboards-store.ts +366 -3
  9. package/corpus/templates/analytics/server/lib/real-data-actions.ts +3 -1
  10. package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -2
  11. package/corpus/templates/calendar/app/components/calendar/EventDialog.tsx +1 -1
  12. package/corpus/templates/calendar/app/lib/utils.ts +1 -1
  13. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +32 -5
  14. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +15 -15
  15. package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
  16. package/corpus/templates/clips/app/lib/utils.ts +5 -6
  17. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +43 -8
  18. package/corpus/templates/clips/desktop/src/app.tsx +26 -12
  19. package/corpus/templates/clips/desktop/src/lib/recorder.ts +23 -9
  20. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +261 -11
  21. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +21 -3
  22. package/corpus/templates/clips/shared/mic-audio-cleanup.ts +350 -0
  23. package/corpus/templates/content/app/i18n-data.ts +10 -10
  24. package/corpus/templates/content/scripts/check-native-deps.mjs +181 -15
  25. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +79 -7
  26. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +14 -2
  27. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +2 -2
  28. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +35 -11
  29. package/corpus/templates/design/actions/get-design-system.ts +103 -3
  30. package/corpus/templates/design/actions/import-design-tokens.ts +37 -8
  31. package/corpus/templates/design/actions/import-github.ts +32 -138
  32. package/corpus/templates/design/actions/index-design-system-with-builder.ts +49 -16
  33. package/corpus/templates/design/actions/present-design-variants.ts +93 -4
  34. package/corpus/templates/design/actions/run-design-audit.ts +229 -0
  35. package/corpus/templates/design/actions/sync-design-system-with-builder.ts +64 -0
  36. package/corpus/templates/design/app/components/design/code-workbench/commands.ts +2 -2
  37. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +17 -4
  38. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +5 -1
  39. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +5 -1
  40. package/corpus/templates/design/app/hooks/use-question-flow.ts +81 -5
  41. package/corpus/templates/design/app/i18n/en-US.ts +1 -1
  42. package/corpus/templates/design/app/i18n-data.ts +32 -1
  43. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +10 -10
  44. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +83 -11
  45. package/corpus/templates/design/app/pages/DesignSystems.tsx +14 -0
  46. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +22 -0
  47. package/corpus/templates/design/changelog/2026-08-13-a-screen-that-would-render-behind-an-invisible-alpine-overla.md +6 -0
  48. package/corpus/templates/design/changelog/2026-08-13-attach-a-ui-screenshot-and-the-design-is-reproduced-not-rein.md +6 -0
  49. package/corpus/templates/design/changelog/2026-08-13-linking-a-design-system-means-you-never-get-generic-directio.md +6 -0
  50. package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-reports-when-a-screen-ignores-its-linke.md +6 -0
  51. package/corpus/templates/design/changelog/2026-08-13-the-design-system-preview-now-shows-how-many-named-tokens-we.md +6 -0
  52. package/corpus/templates/design/changelog/2026-08-13-your-design-system-prompt-and-reference-screenshots-now-reac.md +6 -0
  53. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +18 -2
  54. package/corpus/templates/design/shared/design-review.ts +1 -0
  55. package/corpus/templates/design/shared/html-integrity.ts +171 -0
  56. package/corpus/templates/mail/app/lib/utils.ts +1 -1
  57. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +10 -6
  58. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +41 -3
  59. package/corpus/templates/slides/actions/sync-design-system-with-builder.ts +64 -0
  60. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
  61. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +193 -20
  62. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +58 -33
  63. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +28 -41
  64. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +119 -91
  65. package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +16 -0
  66. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +158 -31
  67. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +30 -14
  68. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +2 -2
  69. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +4 -1
  70. package/corpus/templates/slides/app/components/editor/rich-text-selection.ts +14 -0
  71. package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +24 -0
  72. package/corpus/templates/slides/app/global.css +24 -0
  73. package/corpus/templates/slides/app/hooks/use-navigation-state.ts +5 -1
  74. package/corpus/templates/slides/app/i18n/en-US.ts +19 -10
  75. package/corpus/templates/slides/app/lib/deck-filter.ts +47 -0
  76. package/corpus/templates/slides/app/lib/export-google-slides-client.ts +15 -4
  77. package/corpus/templates/slides/app/lib/export-pptx-client.ts +637 -1
  78. package/corpus/templates/slides/app/lib/image-download.ts +84 -0
  79. package/corpus/templates/slides/app/lib/utils.ts +1 -1
  80. package/corpus/templates/slides/app/pages/Index.tsx +25 -9
  81. package/corpus/templates/slides/netlify.toml +1 -0
  82. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +14 -2
  83. package/dist/agent/production-agent.js +1 -1
  84. package/dist/brand-kit/types.d.ts +10 -2
  85. package/dist/cli/code.js +1 -1
  86. package/dist/cli/index.js +30 -1
  87. package/dist/cli/native-dependencies.d.ts +24 -0
  88. package/dist/cli/native-dependencies.js +264 -0
  89. package/dist/cli/plan-local.js +1 -1
  90. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  91. package/dist/cli/skills-content/design-visual-edit-skill.js +2 -2
  92. package/dist/cli/visualize-repo.js +1 -1
  93. package/dist/client/FeedbackButton.js +12 -12
  94. package/dist/client/agent-chat-adapter.js +3 -4
  95. package/dist/client/assistant-ui-recovery.d.ts +1 -1
  96. package/dist/client/assistant-ui-recovery.js +4 -0
  97. package/dist/client/chat/message-components.d.ts +11 -3
  98. package/dist/client/chat/message-components.js +31 -5
  99. package/dist/client/chat/tool-call-display.d.ts +2 -1
  100. package/dist/client/chat/tool-call-display.js +33 -15
  101. package/dist/client/db-admin/SqlEditor.js +1 -1
  102. package/dist/client/dev-overlay/DevOverlay.js +2 -2
  103. package/dist/client/extensions/portable-extension.js +4 -0
  104. package/dist/client/guided-questions.d.ts +8 -0
  105. package/dist/client/guided-questions.js +12 -3
  106. package/dist/client/sharing/ShareButton.js +16 -24
  107. package/dist/client/sse-event-processor.js +35 -15
  108. package/dist/client/tool-display.d.ts +9 -0
  109. package/dist/client/tool-display.js +39 -0
  110. package/dist/client/vite-dev-recovery-script.js +1 -1
  111. package/dist/collab/awareness.d.ts +2 -2
  112. package/dist/collab/routes.d.ts +1 -1
  113. package/dist/collab/struct-routes.d.ts +1 -1
  114. package/dist/extensions/html-shell.js +5 -0
  115. package/dist/notifications/routes.d.ts +1 -1
  116. package/dist/observability/routes.d.ts +3 -3
  117. package/dist/resources/handlers.d.ts +1 -1
  118. package/dist/secrets/routes.d.ts +3 -3
  119. package/dist/server/agent-chat/prompt-resources.js +17 -16
  120. package/dist/server/agent-chat-plugin.d.ts +9 -0
  121. package/dist/server/agent-chat-plugin.js +29 -7
  122. package/dist/server/builder-design-systems.d.ts +33 -1
  123. package/dist/server/builder-design-systems.js +293 -11
  124. package/dist/server/design-token-utils.d.ts +11 -0
  125. package/dist/server/design-token-utils.js +68 -16
  126. package/dist/server/index.d.ts +1 -1
  127. package/dist/server/index.js +1 -1
  128. package/package.json +3 -3
@@ -121,10 +121,12 @@ export async function loadDesignSystemGenerationContext(
121
121
  export function designIntakeQuestionDirectives(
122
122
  designId: string,
123
123
  designSystemId?: string | null,
124
+ referenceImageCount = 0,
124
125
  ): string[] {
125
126
  return [
126
127
  `This is a new UI-started design for design id "${designId}". The design shell already exists - DO NOT call create-design.`,
127
128
  ...designSystemGenerationDirectives(designSystemId),
129
+ ...referenceImageDirectives(referenceImageCount),
128
130
  "First, call `show-design-questions` with 4-6 tailored questions and then stop. Do NOT call generate-design or present-design-variants until the user submits or skips the questions.",
129
131
  "Make the questions feel like Claude Design intake: form factor, aesthetic direction, important features/content, special interactions/polish, and whether to explore variations. Omit or rephrase anything the user's prompt already answered.",
130
132
  "Use concise option chips with `allowOther: true`; include a practical `Decide for me` option where useful. Use `multiSelect: true` for feature/interactions questions.",
@@ -159,13 +161,33 @@ export function designVariantGenerationDirectives(
159
161
  ];
160
162
  }
161
163
 
164
+ /**
165
+ * An attached UI screenshot is the specification. Reproducing it is the whole
166
+ * job, so these directives ship in the same prompt as the image — the skill
167
+ * body is not in the system prompt and loses every conflict against directives
168
+ * that are.
169
+ */
170
+ export function referenceImageDirectives(
171
+ referenceImageCount: number,
172
+ ): string[] {
173
+ if (referenceImageCount < 1) return [];
174
+ return [
175
+ `The user attached ${referenceImageCount} reference image(s) to this message. Treat any image showing a UI as a layout specification to reproduce, not as loose inspiration.`,
176
+ "Match its regions and their positions, navigation pattern, information hierarchy, density, component grammar (tabs vs pills, cards vs rows, sidebar vs topbar), and approximate proportions. Briefly name what you see before building so a misread can be corrected early.",
177
+ "Do NOT substitute your own composition, palette, or font for something the image or the linked design system already specifies — including choices a generic quality heuristic would discourage. Deviate only where the image is genuinely unreadable, and say so when you do.",
178
+ "Do NOT call `show-design-questions` or `present-design-variants` about anything the image already answers; the direction is chosen. Generate the one design it specifies.",
179
+ ];
180
+ }
181
+
162
182
  export function designGenerationDirectives(
163
183
  designId: string,
164
184
  designSystemId?: string | null,
185
+ referenceImageCount = 0,
165
186
  ): string[] {
166
187
  return [
167
188
  `Use the \`generate-design --designId="${designId}"\` action with exactly one complete, renderable \`index.html\` file first. The design already exists - DO NOT call create-design.`,
168
189
  ...designSystemGenerationDirectives(designSystemId),
190
+ ...referenceImageDirectives(referenceImageCount),
169
191
  'If the user asked to explore variations, call `present-design-variants` with 2-5 concise directions. Prefer label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens. Wait for their chat pick, delete each unchosen variant screen at most once, call `get-design-snapshot` exactly once with `fileId` for the kept screen, then call `edit-design` exactly once on that same `fileId` in a bounded pass. Use `mode: "replace-file"` when expanding the representative placeholder into a complete but compact product UI in the chosen direction. Prioritize the primary workflow and render secondary details as visible controls, states, or affordances if the feature list is too large for one reliable edit. Do not repeat delete/snapshot cycles. Do not call `generate-design` after a variant pick. Stop after the first successful `edit-design` save. Otherwise generate one polished first direction.',
170
192
  'Responsive behavior is mandatory for every web design: use a mobile-first layout, include a viewport meta tag, stack or collapse desktop columns at narrow widths, and never rely on a fixed-width desktop shell. Default to a desktop primary artboard. For a Desktop or Both/responsive intake answer, pass `primaryViewport: "desktop"` and `canvasFrames` with width 1440 and height 1024; pass `primaryViewport: "mobile"` only when the user explicitly chooses a mobile-primary artboard.',
171
193
  "Keep the first pass bounded enough to finish quickly: one self-contained Alpine.js + Tailwind CDN HTML document, polished but concise. Add 3-6 tweaks only when they naturally fit the design.",
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-13
4
+ ---
5
+
6
+ A screen whose Alpine overlay would cover it on load — `x-cloak` without its hiding rule, or a broken Alpine script URL — is now caught when it is saved instead of shipping as a blank or covered design.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-13
4
+ ---
5
+
6
+ Attach a UI screenshot and the design is reproduced from it instead of being reinterpreted as three alternative directions.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-13
4
+ ---
5
+
6
+ Linking a design system means you never get generic direction cards: exploring variants without real HTML is now refused for that design rather than rendering placeholder screens that ignore your tokens.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-08-13
4
+ ---
5
+
6
+ The design audit now reports when a screen uses none of its linked design system's fonts, colors, or token names.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-13
4
+ ---
5
+
6
+ The design system preview now shows how many named tokens were captured, so a large imported system no longer looks like a handful of colors.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-13
4
+ ---
5
+
6
+ Your design system, original prompt, and reference screenshots now reach the turn that actually generates the design, instead of being spent on the questions step.
@@ -2,6 +2,8 @@ import {
2
2
  createBuilderDesignSystemProxyFields,
3
3
  localBuilderDesignSystemId,
4
4
  type BuilderDesignSystemIndexResult,
5
+ type BuilderDesignSystemGitHubSource,
6
+ type BuilderDesignSystemSourceKind,
5
7
  } from "@agent-native/core/server";
6
8
  import { and, eq, isNull } from "drizzle-orm";
7
9
  import { nanoid } from "nanoid";
@@ -14,12 +16,18 @@ export async function upsertBuilderProxyDesignSystem({
14
16
  orgId,
15
17
  projectName,
16
18
  description,
19
+ sourceKind,
20
+ githubSources,
21
+ localDesignSystemId: requestedLocalDesignSystemId,
17
22
  }: {
18
23
  result: BuilderDesignSystemIndexResult;
19
24
  ownerEmail: string;
20
25
  orgId?: string | null;
21
26
  projectName?: string;
22
27
  description?: string;
28
+ sourceKind?: BuilderDesignSystemSourceKind;
29
+ githubSources?: BuilderDesignSystemGitHubSource[];
30
+ localDesignSystemId?: string;
23
31
  }) {
24
32
  const db = getDb();
25
33
  const now = new Date().toISOString();
@@ -31,6 +39,9 @@ export async function upsertBuilderProxyDesignSystem({
31
39
  projectName,
32
40
  description,
33
41
  surface: "design",
42
+ sourceKind,
43
+ githubSources,
44
+ syncedAt: githubSources?.length ? now : undefined,
34
45
  });
35
46
  const [existing] = await db
36
47
  .select({
@@ -39,7 +50,12 @@ export async function upsertBuilderProxyDesignSystem({
39
50
  orgId: schema.designSystems.orgId,
40
51
  })
41
52
  .from(schema.designSystems)
42
- .where(eq(schema.designSystems.id, baseLocalDesignSystemId))
53
+ .where(
54
+ eq(
55
+ schema.designSystems.id,
56
+ requestedLocalDesignSystemId ?? baseLocalDesignSystemId,
57
+ ),
58
+ )
43
59
  .limit(1);
44
60
  const existingBelongsToScope =
45
61
  existing?.ownerEmail === ownerEmail &&
@@ -47,7 +63,7 @@ export async function upsertBuilderProxyDesignSystem({
47
63
  const localDesignSystemId =
48
64
  existing && !existingBelongsToScope
49
65
  ? `${baseLocalDesignSystemId}-${nanoid(8)}`
50
- : baseLocalDesignSystemId;
66
+ : (requestedLocalDesignSystemId ?? baseLocalDesignSystemId);
51
67
  if (existingBelongsToScope) {
52
68
  await db
53
69
  .update(schema.designSystems)
@@ -24,6 +24,7 @@ export const A11Y_FINDING_CATEGORIES = [
24
24
  "reduced-motion",
25
25
  "role",
26
26
  "token-drift",
27
+ "design-system-drift",
27
28
  "other",
28
29
  ] as const;
29
30
 
@@ -32,6 +32,8 @@ export type DesignHtmlIntegrityIssue =
32
32
  | "close-tag-orphaned"
33
33
  | "content-truncated"
34
34
  | "runtime-missing"
35
+ | "runtime-alpine-missing"
36
+ | "runtime-cloak-missing"
35
37
  | "url-backed-screen-replaced";
36
38
 
37
39
  /**
@@ -86,6 +88,10 @@ const DOCUMENT_SHAPE_MESSAGES: Partial<
86
88
  "an editor-managed <style>/<script> block appears more than once; there must be exactly one of each",
87
89
  "url-backed-screen-replaced":
88
90
  "this screen's content is its live route URL, and the write would replace it with document markup — that permanently unbinds the screen from the running app. Edit the app's own source instead",
91
+ "runtime-cloak-missing":
92
+ "this document uses x-cloak without the CSS rule that keeps Alpine-controlled content hidden before Alpine starts",
93
+ "runtime-alpine-missing":
94
+ "this document uses x-cloak but does not load Alpine.js, so the hidden state can never be removed",
89
95
  };
90
96
 
91
97
  export function describeDesignHtmlIntegrityIssue(
@@ -141,6 +147,19 @@ export function describeDesignHtmlIntegrityIssue(
141
147
  `<script src="…@tailwindcss/browser@4"> or a <style type="text/tailwindcss">). ` +
142
148
  `Utility classes will not apply and the design renders unstyled.`
143
149
  );
150
+ case "runtime-alpine-missing":
151
+ return (
152
+ `the document uses x-cloak on <${detail.tag ?? "element"}> at ${at}, ` +
153
+ `but no script source contains Alpine.js. The element will remain ` +
154
+ `hidden forever; load Alpine.js before relying on x-cloak.`
155
+ );
156
+ case "runtime-cloak-missing":
157
+ return (
158
+ `the document uses x-cloak on <${detail.tag ?? "element"}> at ${at}, ` +
159
+ `but no style block defines [x-cloak] { display: none !important; }. ` +
160
+ `Alpine-controlled overlays can flash or cover the entire screen before ` +
161
+ `Alpine starts. Add that exact hiding rule to the document.`
162
+ );
144
163
  default:
145
164
  return `the document structure is invalid (${detail.issue}) at ${at}.`;
146
165
  }
@@ -1077,6 +1096,85 @@ function collectManagedMarkerIssue(
1077
1096
  return null;
1078
1097
  }
1079
1098
 
1099
+ /**
1100
+ * The selector may sit anywhere in a comma-separated list, and any of the three
1101
+ * hidings below is a correct pre-Alpine hide. Matching only
1102
+ * `[x-cloak] { display: none }` rejects documents that render perfectly.
1103
+ */
1104
+ const X_CLOAK_RULE =
1105
+ /\[x-cloak\][^{}]*\{[^}]*(?:display\s*:\s*none|visibility\s*:\s*hidden|@apply[^;}]*\bhidden\b)/i;
1106
+ const ALPINE_RUNTIME = /\balpinejs\b/i;
1107
+ const INLINE_PRE_HIDE = /(?:display\s*:\s*none|visibility\s*:\s*hidden)/i;
1108
+
1109
+ /**
1110
+ * `x-cloak` is an Alpine convention, not a runtime feature: it only works
1111
+ * when the authored document also supplies the CSS rule that hides the node
1112
+ * before Alpine initializes. A missing rule is especially dangerous for
1113
+ * fixed overlays, which can make a correct screen look completely replaced.
1114
+ */
1115
+ function collectInteractiveRuntimeIssues(
1116
+ parsed: ParsedDocument,
1117
+ locate: Locator,
1118
+ ): DesignHtmlIntegrityIssueDetail[] {
1119
+ const cloaked = parsed.elements.find((element) =>
1120
+ element.attrs.some(
1121
+ (attribute) => attribute.name.toLowerCase() === "x-cloak",
1122
+ ),
1123
+ );
1124
+ if (!cloaked) return [];
1125
+
1126
+ const hasAlpineRuntime = parsed.elements.some(
1127
+ (element) =>
1128
+ element.tagName === "script" &&
1129
+ ALPINE_RUNTIME.test(attributeOf(element, "src") ?? ""),
1130
+ );
1131
+
1132
+ // A linked stylesheet is content this parser cannot read, so its rules are
1133
+ // unknown — not missing. Reporting "no rule defined" here would be the
1134
+ // coercion this whole module exists to prevent.
1135
+ const hasUnreadableStylesheet = parsed.elements.some(
1136
+ (element) =>
1137
+ element.tagName === "link" &&
1138
+ /\bstylesheet\b/i.test(attributeOf(element, "rel") ?? "") &&
1139
+ (attributeOf(element, "href") ?? "").trim().length > 0,
1140
+ );
1141
+
1142
+ const hasCloakRule =
1143
+ hasUnreadableStylesheet ||
1144
+ INLINE_PRE_HIDE.test(attributeOf(cloaked, "style") ?? "") ||
1145
+ parsed.elements.some((element) => {
1146
+ if (element.tagName !== "style") return false;
1147
+ const css = childrenOf(element)
1148
+ .filter((node) => node.nodeName === "#text")
1149
+ .map((node) => (node as DefaultTreeAdapterTypes.TextNode).value)
1150
+ .join("\n");
1151
+ return X_CLOAK_RULE.test(css);
1152
+ });
1153
+
1154
+ const attribute = cloaked.attrs.find(
1155
+ (entry) => entry.name.toLowerCase() === "x-cloak",
1156
+ );
1157
+ const offset =
1158
+ locationOf(cloaked)?.attrs?.[attribute?.name ?? ""]?.startOffset ??
1159
+ locationOf(cloaked)?.startOffset ??
1160
+ 0;
1161
+ const detail = {
1162
+ line: locate(offset).line,
1163
+ column: locate(offset).column,
1164
+ excerpt: locate(offset).excerpt,
1165
+ tag: cloaked.tagName,
1166
+ attribute: "x-cloak",
1167
+ };
1168
+ return [
1169
+ ...(hasAlpineRuntime
1170
+ ? []
1171
+ : [{ issue: "runtime-alpine-missing" as const, ...detail }]),
1172
+ ...(hasCloakRule
1173
+ ? []
1174
+ : [{ issue: "runtime-cloak-missing" as const, ...detail }]),
1175
+ ];
1176
+ }
1177
+
1080
1178
  /**
1081
1179
  * Reported, never enforced: legitimate fragments and token-only screens carry no
1082
1180
  * runtime of their own, so blocking here would reject valid work.
@@ -1142,10 +1240,45 @@ export function inspectDesignHtmlDocumentIntegrity(
1142
1240
  const marker = collectManagedMarkerIssue(parsed);
1143
1241
  if (marker) return { valid: false, issue: marker };
1144
1242
 
1243
+ const interactiveRuntime = collectInteractiveRuntimeIssues(parsed, locate);
1244
+ if (interactiveRuntime.length > 0) {
1245
+ return {
1246
+ valid: false,
1247
+ issue: interactiveRuntime[0]!.issue,
1248
+ detail: interactiveRuntime,
1249
+ };
1250
+ }
1251
+
1145
1252
  const advisory = collectAdvisoryIssues(parsed, locate);
1146
1253
  return advisory.length > 0 ? { valid: true, advisory } : { valid: true };
1147
1254
  }
1148
1255
 
1256
+ const RUNTIME_ISSUES: ReadonlySet<DesignHtmlIntegrityIssue> = new Set([
1257
+ "runtime-alpine-missing",
1258
+ "runtime-cloak-missing",
1259
+ ]);
1260
+
1261
+ /**
1262
+ * The runtime issues are the one class an edit may carry forward. A screen
1263
+ * saved before these checks existed still renders, and the person dragging on
1264
+ * the canvas did not author its markup and cannot add a CSS rule — refusing
1265
+ * their unrelated edit strands the screen. Block what the edit introduces;
1266
+ * the create path still refuses to author the defect in the first place.
1267
+ */
1268
+ function introducedRuntimeIssues(
1269
+ next: DesignHtmlIntegrityIssueDetail[],
1270
+ previousContent: string,
1271
+ ): DesignHtmlIntegrityIssueDetail[] {
1272
+ if (next.length === 0 || !previousContent.trim()) return next;
1273
+ const inherited = new Set(
1274
+ collectInteractiveRuntimeIssues(
1275
+ parseDocument(previousContent),
1276
+ createLocator(previousContent),
1277
+ ).map((entry) => entry.issue),
1278
+ );
1279
+ return next.filter((entry) => !inherited.has(entry.issue));
1280
+ }
1281
+
1149
1282
  /**
1150
1283
  * Fail closed only for document edits. Standalone Alpine fragments remain
1151
1284
  * supported. Existing malformed documents can still be repaired: a candidate
@@ -1186,6 +1319,19 @@ export function assertDesignHtmlEditIntegrity(args: {
1186
1319
  detail: structural,
1187
1320
  });
1188
1321
  }
1322
+ const interactiveRuntime = introducedRuntimeIssues(
1323
+ collectInteractiveRuntimeIssues(
1324
+ parseDocument(args.nextContent),
1325
+ createLocator(args.nextContent),
1326
+ ),
1327
+ args.previousContent,
1328
+ );
1329
+ if (interactiveRuntime.length > 0) {
1330
+ throw new DesignHtmlIntegrityError(interactiveRuntime[0]!.issue, {
1331
+ filename: args.filename,
1332
+ detail: interactiveRuntime,
1333
+ });
1334
+ }
1189
1335
  return;
1190
1336
  }
1191
1337
  if (previousIsDocument && !nextIsDocument) {
@@ -1195,6 +1341,17 @@ export function assertDesignHtmlEditIntegrity(args: {
1195
1341
  }
1196
1342
  const result = inspectDesignHtmlDocumentIntegrity(args.nextContent);
1197
1343
  if (!result.valid) {
1344
+ if (result.issue && RUNTIME_ISSUES.has(result.issue)) {
1345
+ const introduced = introducedRuntimeIssues(
1346
+ result.detail ?? [],
1347
+ args.previousContent,
1348
+ );
1349
+ if (introduced.length === 0) return;
1350
+ throw new DesignHtmlIntegrityError(introduced[0]!.issue, {
1351
+ filename: args.filename,
1352
+ detail: introduced,
1353
+ });
1354
+ }
1198
1355
  throw new DesignHtmlIntegrityError(result.issue ?? "document-root", {
1199
1356
  filename: args.filename,
1200
1357
  detail: result.detail,
@@ -1227,6 +1384,20 @@ export function assertDesignHtmlWellFormed(args: {
1227
1384
  detail: structural,
1228
1385
  });
1229
1386
  }
1387
+ // Fragments are checked too, unlike the document-shape rules above: a screen
1388
+ // is rendered as its own `srcdoc`, so nothing injects Alpine or a `[x-cloak]`
1389
+ // rule around it. A sketch that omits them is broken exactly as a full
1390
+ // document would be.
1391
+ const interactiveRuntime = collectInteractiveRuntimeIssues(
1392
+ parseDocument(args.content),
1393
+ createLocator(args.content),
1394
+ );
1395
+ if (interactiveRuntime.length > 0) {
1396
+ throw new DesignHtmlIntegrityError(interactiveRuntime[0]!.issue, {
1397
+ filename: args.filename,
1398
+ detail: interactiveRuntime,
1399
+ });
1400
+ }
1230
1401
  }
1231
1402
 
1232
1403
  export function assertDesignHtmlCreateIntegrity(args: {
@@ -194,5 +194,5 @@ export function formatShortcut(key: string): string {
194
194
  if (lower === "space") return "Space";
195
195
  return token.length === 1 ? token.toUpperCase() : token;
196
196
  })
197
- .join("+");
197
+ .join(" ");
198
198
  }
@@ -72,12 +72,16 @@ Builder indexing flow.
72
72
 
73
73
  ### Source: connected code, GitHub, or `design.md`
74
74
 
75
- For any other reusable source — connected code, a GitHub repo, local
76
- code/design files, or an optional `design.md` — use Builder-backed DSI
77
- indexing through `index-design-system-with-builder`. Pass readable `design.md`
78
- content as `designMd`, and use the returned local design system id in the rest
79
- of the Slides flow. Call `get-design-system` before generation so Builder docs
80
- and tokens are hydrated when available.
75
+ For any other reusable source - connected code, a GitHub repo, local
76
+ code/design files, or an optional `design.md` - use Builder-backed DSI
77
+ indexing through `index-design-system-with-builder`. Pass GitHub sources as one
78
+ `githubSources` array; each source may pin a branch/tag/commit and include
79
+ repository-relative files or folders. Pass readable `design.md` content as
80
+ `designMd`, and use the returned local design system id in the rest of the
81
+ Slides flow. Call `get-design-system` before generation so Builder docs and
82
+ tokens are hydrated when available. For a saved GitHub-backed system, call
83
+ `sync-design-system-with-builder --id <localDesignSystemId>` to replay its
84
+ persisted source scope after upstream changes.
81
85
 
82
86
  Never create a duplicate local design system from raw Figma or code sources.
83
87
  Builder owns the indexed brand kit; a second local copy drifts from it and
@@ -32,6 +32,30 @@ const codeFileSchema = z.object({
32
32
  ),
33
33
  });
34
34
 
35
+ const githubSourceSchema = z.object({
36
+ repoUrl: z.string().trim().min(1).describe("GitHub repository URL"),
37
+ ref: z
38
+ .string()
39
+ .trim()
40
+ .min(1)
41
+ .optional()
42
+ .describe("Optional branch, tag, or commit"),
43
+ include: z
44
+ .array(z.string().trim().min(1))
45
+ .optional()
46
+ .describe("Optional repository-relative files or folders to include"),
47
+ exclude: z
48
+ .array(z.string().trim().min(1))
49
+ .optional()
50
+ .describe("Optional repository-relative files or folders to exclude"),
51
+ instructions: z
52
+ .string()
53
+ .trim()
54
+ .min(1)
55
+ .optional()
56
+ .describe("Optional indexing guidance for this repository"),
57
+ });
58
+
35
59
  export default defineAction({
36
60
  description:
37
61
  "Start Builder DSI design-system indexing from connected code, a GitHub repository, code/design files, and optional design.md guidance. " +
@@ -49,7 +73,15 @@ export default defineAction({
49
73
  githubRepoUrl: z
50
74
  .string()
51
75
  .optional()
52
- .describe("GitHub repository URL to index with Builder"),
76
+ .describe("Legacy single GitHub repository URL to index with Builder"),
77
+ githubSources: z
78
+ .array(githubSourceSchema)
79
+ .min(1)
80
+ .max(20)
81
+ .optional()
82
+ .describe(
83
+ "GitHub repositories to index in one design system. Each source may specify a branch/tag/commit and repository-relative files or folders.",
84
+ ),
53
85
  connectedProjectId: z
54
86
  .string()
55
87
  .optional()
@@ -69,6 +101,7 @@ export default defineAction({
69
101
  projectName,
70
102
  description,
71
103
  githubRepoUrl,
104
+ githubSources,
72
105
  connectedProjectId,
73
106
  codeFiles,
74
107
  designMd,
@@ -81,6 +114,7 @@ export default defineAction({
81
114
  projectName,
82
115
  description,
83
116
  githubRepoUrl,
117
+ githubRepos: githubSources,
84
118
  connectedProjectId,
85
119
  files,
86
120
  });
@@ -93,10 +127,13 @@ export default defineAction({
93
127
  orgId: getRequestOrgId(),
94
128
  projectName,
95
129
  description,
130
+ githubSources:
131
+ githubSources ?? (githubRepoUrl ? [{ repoUrl: githubRepoUrl }] : []),
96
132
  sourceKind:
97
- githubRepoUrl && (codeFiles?.length || designMd)
133
+ (githubSources?.length || githubRepoUrl) &&
134
+ (codeFiles?.length || designMd)
98
135
  ? "mixed"
99
- : githubRepoUrl
136
+ : githubSources?.length || githubRepoUrl
100
137
  ? "github"
101
138
  : codeFiles?.length || designMd
102
139
  ? "code"
@@ -107,6 +144,7 @@ export default defineAction({
107
144
  ...result,
108
145
  ...proxy,
109
146
  uploadedFileCount: files.length,
147
+ githubSourceCount: githubSources?.length ?? (githubRepoUrl ? 1 : 0),
110
148
  };
111
149
  },
112
150
  });
@@ -0,0 +1,64 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import {
3
+ parseBuilderDesignSystemProxyReference,
4
+ startBuilderDesignSystemIndex,
5
+ } from "@agent-native/core/server";
6
+ import { getRequestUserEmail } from "@agent-native/core/server/request-context";
7
+ import { assertAccess, resolveAccess } from "@agent-native/core/sharing";
8
+ import { z } from "zod";
9
+
10
+ import "../server/db/index.js";
11
+ import { upsertBuilderProxyDesignSystem } from "../server/lib/builder-design-system-proxy.js";
12
+
13
+ export default defineAction({
14
+ description:
15
+ "Re-index a Builder-backed GitHub design system using its persisted repository, ref, and file/folder scope. Requires editor access.",
16
+ schema: z.object({
17
+ id: z.string().min(1).describe("Local design system id"),
18
+ }),
19
+ run: async ({ id }) => {
20
+ await assertAccess("design-system", id, "editor");
21
+ const access = await resolveAccess("design-system", id);
22
+ if (!access) throw new Error("Design system not found");
23
+
24
+ const reference = parseBuilderDesignSystemProxyReference(
25
+ access.resource.data,
26
+ );
27
+ if (!reference?.githubSources?.length) {
28
+ throw new Error(
29
+ "This design system has no persisted GitHub source scope to sync.",
30
+ );
31
+ }
32
+ if (reference.sourceKind === "mixed") {
33
+ throw new Error(
34
+ "This design system combines GitHub with non-replayable sources. Re-import all sources before syncing it.",
35
+ );
36
+ }
37
+
38
+ const result = await startBuilderDesignSystemIndex({
39
+ projectName: access.resource.title,
40
+ description: access.resource.description ?? undefined,
41
+ githubRepos: reference.githubSources,
42
+ });
43
+ const ownerEmail = getRequestUserEmail();
44
+ if (!ownerEmail) throw new Error("no authenticated user");
45
+
46
+ const proxy = await upsertBuilderProxyDesignSystem({
47
+ result,
48
+ ownerEmail: access.resource.ownerEmail,
49
+ orgId: access.resource.orgId,
50
+ projectName: access.resource.title,
51
+ description: access.resource.description ?? undefined,
52
+ sourceKind: reference.sourceKind,
53
+ githubSources: reference.githubSources,
54
+ localDesignSystemId: id,
55
+ });
56
+
57
+ return {
58
+ ...result,
59
+ ...proxy,
60
+ synced: true,
61
+ githubSourceCount: reference.githubSources.length,
62
+ };
63
+ },
64
+ });
@@ -189,7 +189,7 @@ export default function DeckCard({
189
189
  </DropdownMenuTrigger>
190
190
  <DropdownMenuContent
191
191
  align="end"
192
- className="w-40"
192
+ className="w-56"
193
193
  onCloseAutoFocus={(e) => {
194
194
  if (pendingRenameRef.current) {
195
195
  e.preventDefault();