@agent-native/core 0.153.10 → 0.154.1

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 (149) 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/actions/resolve-content-landing.ts +162 -32
  24. package/corpus/templates/content/app/i18n-data.ts +10 -10
  25. package/corpus/templates/content/scripts/check-native-deps.mjs +181 -15
  26. package/corpus/templates/content/shared/content-landing.ts +5 -0
  27. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +79 -7
  28. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +14 -2
  29. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +2 -2
  30. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +35 -11
  31. package/corpus/templates/design/actions/get-design-system.ts +103 -3
  32. package/corpus/templates/design/actions/import-design-tokens.ts +37 -8
  33. package/corpus/templates/design/actions/import-github.ts +32 -138
  34. package/corpus/templates/design/actions/index-design-system-with-builder.ts +49 -16
  35. package/corpus/templates/design/actions/present-design-variants.ts +93 -4
  36. package/corpus/templates/design/actions/run-design-audit.ts +229 -0
  37. package/corpus/templates/design/actions/sync-design-system-with-builder.ts +64 -0
  38. package/corpus/templates/design/app/components/design/code-workbench/commands.ts +2 -2
  39. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +17 -4
  40. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +5 -1
  41. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +5 -1
  42. package/corpus/templates/design/app/hooks/use-question-flow.ts +81 -5
  43. package/corpus/templates/design/app/i18n/en-US.ts +1 -1
  44. package/corpus/templates/design/app/i18n-data.ts +32 -1
  45. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +10 -10
  46. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +83 -11
  47. package/corpus/templates/design/app/pages/DesignSystems.tsx +14 -0
  48. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +22 -0
  49. package/corpus/templates/design/changelog/2026-08-13-a-screen-that-would-render-behind-an-invisible-alpine-overla.md +6 -0
  50. package/corpus/templates/design/changelog/2026-08-13-attach-a-ui-screenshot-and-the-design-is-reproduced-not-rein.md +6 -0
  51. package/corpus/templates/design/changelog/2026-08-13-linking-a-design-system-means-you-never-get-generic-directio.md +6 -0
  52. package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-reports-when-a-screen-ignores-its-linke.md +6 -0
  53. package/corpus/templates/design/changelog/2026-08-13-the-design-system-preview-now-shows-how-many-named-tokens-we.md +6 -0
  54. package/corpus/templates/design/changelog/2026-08-13-your-design-system-prompt-and-reference-screenshots-now-reac.md +6 -0
  55. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +18 -2
  56. package/corpus/templates/design/shared/design-review.ts +1 -0
  57. package/corpus/templates/design/shared/html-integrity.ts +171 -0
  58. package/corpus/templates/mail/app/lib/utils.ts +1 -1
  59. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +10 -6
  60. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +41 -3
  61. package/corpus/templates/slides/actions/patch-deck.ts +12 -3
  62. package/corpus/templates/slides/actions/sync-design-system-with-builder.ts +64 -0
  63. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
  64. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +193 -20
  65. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +73 -1
  66. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +64 -34
  67. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +34 -41
  68. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +119 -91
  69. package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +16 -0
  70. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +15 -0
  71. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +158 -31
  72. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +30 -14
  73. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +2 -2
  74. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +4 -1
  75. package/corpus/templates/slides/app/components/editor/rich-text-selection.ts +14 -0
  76. package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +24 -0
  77. package/corpus/templates/slides/app/context/DeckContext.tsx +19 -31
  78. package/corpus/templates/slides/app/global.css +24 -0
  79. package/corpus/templates/slides/app/hooks/use-navigation-state.ts +20 -17
  80. package/corpus/templates/slides/app/i18n/en-US.ts +19 -10
  81. package/corpus/templates/slides/app/lib/deck-filter.ts +47 -0
  82. package/corpus/templates/slides/app/lib/export-google-slides-client.ts +15 -4
  83. package/corpus/templates/slides/app/lib/export-pptx-client.ts +637 -1
  84. package/corpus/templates/slides/app/lib/image-download.ts +84 -0
  85. package/corpus/templates/slides/app/lib/utils.ts +1 -1
  86. package/corpus/templates/slides/app/pages/DeckEditor.tsx +9 -0
  87. package/corpus/templates/slides/app/pages/Index.tsx +25 -9
  88. package/corpus/templates/slides/changelog/2026-08-13-clicking-a-slide-thumbnail-now-focuses-it-so-the-slide-shortc.md +6 -0
  89. package/corpus/templates/slides/changelog/2026-08-13-duplicating-or-restoring-a-slide-now-keeps-its-transition-ani.md +6 -0
  90. package/corpus/templates/slides/changelog/2026-08-13-opening-presentation-mode-from-the-agent-now-starts-on-the-re.md +6 -0
  91. package/corpus/templates/slides/changelog/2026-08-13-slide-transitions-can-be-set-from-a-visible-control-in-the-sl.md +6 -0
  92. package/corpus/templates/slides/netlify.toml +1 -0
  93. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +14 -2
  94. package/dist/a2a/audience.d.ts +2 -0
  95. package/dist/a2a/audience.js +23 -0
  96. package/dist/a2a/client.d.ts +4 -0
  97. package/dist/a2a/client.js +32 -14
  98. package/dist/a2a/server.d.ts +9 -4
  99. package/dist/a2a/server.js +52 -25
  100. package/dist/a2a/types.d.ts +4 -4
  101. package/dist/agent/production-agent.js +1 -1
  102. package/dist/brand-kit/types.d.ts +10 -2
  103. package/dist/cli/code.js +1 -1
  104. package/dist/cli/index.js +30 -1
  105. package/dist/cli/native-dependencies.d.ts +24 -0
  106. package/dist/cli/native-dependencies.js +264 -0
  107. package/dist/cli/plan-local.js +1 -1
  108. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  109. package/dist/cli/skills-content/design-visual-edit-skill.js +2 -2
  110. package/dist/cli/visualize-repo.js +1 -1
  111. package/dist/client/FeedbackButton.js +12 -12
  112. package/dist/client/agent-chat-adapter.js +3 -4
  113. package/dist/client/assistant-ui-recovery.d.ts +1 -1
  114. package/dist/client/assistant-ui-recovery.js +4 -0
  115. package/dist/client/chat/message-components.d.ts +11 -3
  116. package/dist/client/chat/message-components.js +31 -5
  117. package/dist/client/chat/tool-call-display.d.ts +2 -1
  118. package/dist/client/chat/tool-call-display.js +33 -15
  119. package/dist/client/db-admin/SqlEditor.js +1 -1
  120. package/dist/client/dev-overlay/DevOverlay.js +2 -2
  121. package/dist/client/extensions/portable-extension.js +4 -0
  122. package/dist/client/guided-questions.d.ts +8 -0
  123. package/dist/client/guided-questions.js +12 -3
  124. package/dist/client/sharing/ShareButton.js +16 -24
  125. package/dist/client/sse-event-processor.js +35 -15
  126. package/dist/client/tool-display.d.ts +9 -0
  127. package/dist/client/tool-display.js +39 -0
  128. package/dist/client/vite-dev-recovery-script.js +1 -1
  129. package/dist/collab/awareness.d.ts +2 -2
  130. package/dist/collab/routes.d.ts +1 -1
  131. package/dist/collab/struct-routes.d.ts +1 -1
  132. package/dist/extensions/html-shell.js +5 -0
  133. package/dist/notifications/routes.d.ts +1 -1
  134. package/dist/observability/routes.d.ts +3 -3
  135. package/dist/provider-api/actions/custom-provider-registration.d.ts +2 -2
  136. package/dist/server/agent-capabilities.js +0 -0
  137. package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -13
  138. package/dist/server/agent-chat/action-filters-a2a.js +59 -23
  139. package/dist/server/agent-chat/prompt-resources.js +17 -16
  140. package/dist/server/agent-chat-plugin.d.ts +9 -0
  141. package/dist/server/agent-chat-plugin.js +29 -7
  142. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  143. package/dist/server/builder-design-systems.d.ts +33 -1
  144. package/dist/server/builder-design-systems.js +293 -11
  145. package/dist/server/design-token-utils.d.ts +11 -0
  146. package/dist/server/design-token-utils.js +68 -16
  147. package/dist/server/index.d.ts +1 -1
  148. package/dist/server/index.js +1 -1
  149. package/package.json +2 -2
@@ -1,11 +1,25 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readdirSync } from "node:fs";
2
+ import { execFileSync } from "node:child_process";
3
+ import {
4
+ closeSync,
5
+ existsSync,
6
+ openSync,
7
+ readFileSync,
8
+ readdirSync,
9
+ statSync,
10
+ unlinkSync,
11
+ writeSync,
12
+ } from "node:fs";
3
13
  import { createRequire } from "node:module";
4
14
  import { dirname, join } from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
15
+ import { fileURLToPath } from "node:url";
6
16
 
7
17
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
18
  const require = createRequire(import.meta.url);
19
+ const REBUILD_LOCK = ".agent-native-better-sqlite3-rebuild.lock";
20
+ const LOCK_INITIALIZATION_TIMEOUT_MS = 5_000;
21
+ const REBUILD_WAIT_MS = 250;
22
+ const REBUILD_TIMEOUT_MS = 10 * 60 * 1000;
9
23
 
10
24
  function resolveBetterSqlite3() {
11
25
  try {
@@ -29,29 +43,181 @@ function resolveBetterSqlite3() {
29
43
  }
30
44
  }
31
45
 
32
- try {
33
- const { default: Database } = await import(
34
- pathToFileURL(resolveBetterSqlite3()).href
46
+ function npmExecutable() {
47
+ const sibling = join(
48
+ dirname(process.execPath),
49
+ process.platform === "win32" ? "npm.cmd" : "npm",
35
50
  );
51
+ return existsSync(sibling) ? sibling : "npm";
52
+ }
53
+
54
+ function sleepSync(milliseconds) {
55
+ const signal = new Int32Array(new SharedArrayBuffer(4));
56
+ Atomics.wait(signal, 0, 0, milliseconds);
57
+ }
58
+
59
+ function processIsRunning(pid) {
60
+ try {
61
+ process.kill(pid, 0);
62
+ return true;
63
+ } catch (error) {
64
+ return error instanceof Error && "code" in error && error.code !== "ESRCH";
65
+ }
66
+ }
67
+
68
+ function acquireRebuildLock(packageDir) {
69
+ const lockPath = join(packageDir, REBUILD_LOCK);
70
+ const startedWaiting = Date.now();
71
+
72
+ while (true) {
73
+ try {
74
+ const lockFd = openSync(lockPath, "wx");
75
+ writeSync(
76
+ lockFd,
77
+ JSON.stringify({
78
+ pid: process.pid,
79
+ startedAt: new Date().toISOString(),
80
+ }),
81
+ );
82
+ return () => {
83
+ try {
84
+ closeSync(lockFd);
85
+ } finally {
86
+ try {
87
+ unlinkSync(lockPath);
88
+ } catch (error) {
89
+ if (
90
+ !(
91
+ error instanceof Error &&
92
+ "code" in error &&
93
+ error.code === "ENOENT"
94
+ )
95
+ ) {
96
+ throw error;
97
+ }
98
+ }
99
+ }
100
+ };
101
+ } catch (error) {
102
+ if (
103
+ !(error instanceof Error && "code" in error && error.code === "EEXIST")
104
+ ) {
105
+ throw error;
106
+ }
107
+
108
+ let ownerPid = null;
109
+ let lockAge = 0;
110
+ try {
111
+ const lock = JSON.parse(readFileSync(lockPath, "utf8"));
112
+ ownerPid = typeof lock.pid === "number" ? lock.pid : null;
113
+ } catch {
114
+ // The owner may be between creating and writing the lock metadata.
115
+ }
116
+ try {
117
+ lockAge = Date.now() - statSync(lockPath).mtimeMs;
118
+ } catch {
119
+ continue;
120
+ }
121
+
122
+ if (
123
+ (ownerPid !== null && !processIsRunning(ownerPid)) ||
124
+ (ownerPid === null && lockAge > LOCK_INITIALIZATION_TIMEOUT_MS)
125
+ ) {
126
+ try {
127
+ unlinkSync(lockPath);
128
+ continue;
129
+ } catch (unlinkError) {
130
+ if (
131
+ !(
132
+ unlinkError instanceof Error &&
133
+ "code" in unlinkError &&
134
+ unlinkError.code === "ENOENT"
135
+ )
136
+ ) {
137
+ throw unlinkError;
138
+ }
139
+ continue;
140
+ }
141
+ }
142
+
143
+ if (Date.now() - startedWaiting > REBUILD_TIMEOUT_MS) {
144
+ throw new Error(
145
+ "Timed out waiting for another process to rebuild better-sqlite3",
146
+ );
147
+ }
148
+ sleepSync(REBUILD_WAIT_MS);
149
+ }
150
+ }
151
+ }
152
+
153
+ function verifyBetterSqlite3(entry) {
154
+ const requireFromPackage = createRequire(entry);
155
+ const Database = requireFromPackage(entry);
36
156
  const db = new Database(":memory:");
37
157
  db.close();
158
+ }
159
+
160
+ let packageEntry;
161
+ try {
162
+ packageEntry = resolveBetterSqlite3();
38
163
  } catch (error) {
39
164
  const message = error instanceof Error ? error.message : String(error);
40
165
  console.error(`
41
166
  [content dev preflight] better-sqlite3 could not load its native binding.
42
167
 
43
- This usually means the local install was restored without rebuilding native
44
- modules. Try:
45
-
46
- pnpm rebuild better-sqlite3
47
-
48
- If that still fails, rebuild the package directly:
49
-
50
- cd ../../node_modules/.pnpm/better-sqlite3@*/node_modules/better-sqlite3
51
- npm run build-release
52
-
53
168
  Original error:
54
169
  ${message}
55
170
  `);
56
171
  process.exit(1);
57
172
  }
173
+
174
+ try {
175
+ verifyBetterSqlite3(packageEntry);
176
+ } catch (firstError) {
177
+ const packageDir = dirname(dirname(packageEntry));
178
+ const releaseLock = acquireRebuildLock(packageDir);
179
+ let repairError;
180
+ try {
181
+ let alreadyRepaired = false;
182
+ try {
183
+ verifyBetterSqlite3(packageEntry);
184
+ alreadyRepaired = true;
185
+ } catch {
186
+ // Another preflight may have repaired the package while we waited.
187
+ }
188
+ if (!alreadyRepaired) {
189
+ console.warn(
190
+ `[content dev preflight] Rebuilding ${packageDir} with Node ${process.version} (ABI ${process.versions.modules})...`,
191
+ );
192
+ execFileSync(
193
+ npmExecutable(),
194
+ ["run", "build-release", "--prefix", packageDir],
195
+ {
196
+ cwd: packageDir,
197
+ stdio: "inherit",
198
+ },
199
+ );
200
+ verifyBetterSqlite3(packageEntry);
201
+ }
202
+ } catch (error) {
203
+ repairError = error;
204
+ } finally {
205
+ releaseLock();
206
+ }
207
+
208
+ if (repairError) {
209
+ const original =
210
+ firstError instanceof Error ? firstError.message : String(firstError);
211
+ const repaired =
212
+ repairError instanceof Error ? repairError.message : String(repairError);
213
+ console.error(`
214
+ [content dev preflight] better-sqlite3 is still unusable after a direct rebuild.
215
+
216
+ Package: ${packageDir}
217
+ Node: ${process.version} (ABI ${process.versions.modules})
218
+ Original error: ${original}
219
+ Rebuild error: ${repaired}
220
+ `);
221
+ process.exit(1);
222
+ }
223
+ }
@@ -1,9 +1,14 @@
1
1
  export const CONTENT_LAST_LOCATION_STATE_KEY = "content-last-location-v1";
2
+ export const CONTENT_WELCOME_PAGE_STATE_KEY = "content-welcome-page-v1";
2
3
 
3
4
  export type ContentLastLocationState = {
4
5
  documentId: string;
5
6
  };
6
7
 
8
+ export type ContentWelcomePageState = {
9
+ generation: number;
10
+ documentId?: string;
11
+ };
7
12
  export type ContentLandingResolution =
8
13
  | "restored"
9
14
  | "welcome-created"
@@ -35,6 +35,53 @@ them:
35
35
 
36
36
  Jumping straight to code is how you get slop. Let the phases (below) do the work.
37
37
 
38
+ ## First decide the mode: reproduce or explore
39
+
40
+ The workflow above assumes an under-specified prompt. When the user has already
41
+ specified the design, that same workflow is the failure — exploration reads to
42
+ them as "the agent ignored everything I gave it".
43
+
44
+ Before Phase 1, check for **specification signals**:
45
+
46
+ - an attached screenshot, mockup, or photo of a UI,
47
+ - a linked design system / Brand Kit,
48
+ - a written outline naming concrete screens, sections, components, or copy,
49
+ - a named existing product/page to match, or pasted markup or tokens.
50
+
51
+ **Any one of these puts you in reproduce mode.** In reproduce mode:
52
+
53
+ - Do **not** call `present-design-variants`. Three divergent directions when the
54
+ user handed you one is not exploration, it is discarding their work. Produce
55
+ the one thing they specified.
56
+ - Do **not** call `show-design-questions` about anything the supplied material
57
+ already answers. Ask only about a genuine gap, and prefer stating an
58
+ assumption over asking.
59
+ - Every "Do" in the aesthetic quality bar below is **off** wherever it would
60
+ contradict the supplied material. Those rules break ties in the absence of a
61
+ spec; they never outvote one.
62
+ - Reproduce first, improve second. Match the structure, then raise quality
63
+ *within* it, and say what you changed and why.
64
+
65
+ Mixed input is normal: reproduce what was given, use the quality bar only for
66
+ the parts genuinely left open. When a supplied reference and the linked design
67
+ system disagree, follow the design system for tokens (color, type, spacing,
68
+ radius) and the reference for layout and composition, then say you did.
69
+
70
+ ### A reference screenshot is a specification, not a mood board
71
+
72
+ When the user attaches an image of a UI, read it as a layout spec and
73
+ reproduce it: the same regions in the same positions, the same navigation
74
+ pattern, the same information hierarchy and density, the same component
75
+ grammar (tabs vs. pills, cards vs. rows, sidebar vs. topbar), the same
76
+ approximate proportions. Name what you see before you build, so the user can
77
+ correct a misread early. Deviate only where the image is genuinely unreadable
78
+ or an explicit instruction overrides it — and say which, rather than
79
+ silently substituting your own composition.
80
+
81
+ An attached image is only guaranteed to be visible on the turn it arrived.
82
+ If a screenshot was described earlier in the thread but you cannot see one now,
83
+ say so and ask for a re-attach — never quietly design from the text alone.
84
+
38
85
  ## Authority and direction record
39
86
 
40
87
  Before generating, make a compact direction record: the job this surface must
@@ -69,13 +116,21 @@ change the design. Never smooth an evidence gap into confident copy.
69
116
 
70
117
  ## Aesthetic quality bar — beat distributional convergence
71
118
 
72
- The rules in this section are fallback quality heuristics. They do not
73
- override an active Agent-Native design system or an explicit brand constraint.
119
+ The rules in this section are fallback quality heuristics for prompts that
120
+ leave the look open. They do not override an active Agent-Native design system,
121
+ a supplied reference, or an explicit brand constraint. **A banned item stops
122
+ being banned the moment the user's own material specifies it** — if their Brand
123
+ Kit's body font is Inter, the design ships Inter; if their screenshot is a
124
+ centered hero with three icon cards, the design ships that. Substituting a
125
+ "better" choice for a specified one is the single most-reported failure of this
126
+ app. When you follow a specified value that appears on a "don't" list below,
127
+ just follow it — no apology, no note, no compensating flourish elsewhere.
74
128
 
75
129
  You sample toward the "on-distribution" center by default; refuse it. **Every
76
130
  "don't" here carries a "do"** — a banned default plus where to go instead —
77
131
  because banning Inter alone just makes you reach for Roboto next. Use a banned
78
- item only if the user explicitly asks.
132
+ item only if the user explicitly asks, or their design system or reference
133
+ already uses it.
79
134
 
80
135
  - **Fonts.** Don't: Inter, Roboto, Arial, Open Sans, system-ui. Do: a distinctive
81
136
  Google Font pairing matched to the chosen aesthetic (see the table) — editorial
@@ -97,7 +152,10 @@ item only if the user explicitly asks.
97
152
 
98
153
  **Second-order convergence is real.** Even your "creative" picks converge (Space
99
154
  Grotesk everywhere; teal accent + blinking dot + left accent bars). Vary
100
- deliberately across generations so two designs never share a fingerprint.
155
+ deliberately across generations so two designs never share a fingerprint —
156
+ but only across designs *you* had to invent. A token the user's own system
157
+ specifies is theirs, not your fingerprint: consistency with their brand is the
158
+ goal, and varying away from it to avoid repeating yourself is a bug.
101
159
 
102
160
  **Principles to quote back while building:** color creates hierarchy, not
103
161
  decoration · density over decoration · earn every animation · commit to one point
@@ -293,9 +351,15 @@ The WIDEST requested device is the base/primary frame; narrower devices become b
293
351
 
294
352
  ### Phase 2 — Generate side-by-side variations (2-5, three by default)
295
353
 
296
- For new designs, default to **three** variations (`present-design-variants`
297
- accepts 2-5; three is the sweet spot). Call `present-design-variants` for both
298
- first-party and external MCP-host flows. It saves each candidate as a normal
354
+ Skip this phase entirely in reproduce mode (see "First decide the mode" above) —
355
+ a screenshot, a written outline, or a linked design system means the direction
356
+ is already chosen, and offering three alternatives to it is the behavior users
357
+ report as "the agent ignored my design".
358
+
359
+ For new designs whose direction is genuinely open, default to **three**
360
+ variations (`present-design-variants` accepts 2-5; three is the sweet spot).
361
+ Call `present-design-variants` for both first-party and external MCP-host
362
+ flows. It saves each candidate as a normal
299
363
  overview-board screen, then renders an inline chat choice with one button per
300
364
  screen name.
301
365
 
@@ -389,6 +453,14 @@ structural issues, token drift), fix them directly with `edit-design`. **A
389
453
  design with audit errors is not ready** — don't report a design as done while
390
454
  `run-design-audit` still returns unresolved errors.
391
455
 
456
+ A `design-system-drift` finding means the screen uses none of the linked
457
+ system's fonts, colors, or token names. That is a generation bug, not a style
458
+ disagreement: the user linked that system precisely so the output would use it.
459
+ Call `get-design-system` for the linked id, apply the real values through
460
+ `edit-design`, and re-audit — never resolve it by explaining the deviation or
461
+ by unlinking the system. It is the one check that can catch a design that
462
+ looks fine and is still not the user's.
463
+
392
464
  After the audit is clean, call `take-design-screenshot` on each changed screen
393
465
  (default: 1280px desktop + 375px mobile). Fix everything its `diagnostics`
394
466
  report flags — real computed contrast ratios, horizontal/container overflow,
@@ -201,10 +201,22 @@ it reports an explicit static SSRF-safe fallback when no browser is available.
201
201
  ### Source: GitHub Repository
202
202
 
203
203
  ```bash
204
- pnpm action index-design-system-with-builder --githubRepoUrl "https://github.com/acme/ui"
204
+ pnpm action index-design-system-with-builder --githubSources '[{"repoUrl":"https://github.com/acme/ui","ref":"main","include":["src/styles","design.md"]}]'
205
205
  ```
206
206
 
207
- Starts Builder design-system indexing for the repository. Builder is the source of truth for the indexed brand kit, generated docs, and usage guidance. If Builder is not connected, stop and ask the user to connect Builder.
207
+ Starts one Builder design-system job with one or more GitHub sources. Each
208
+ source can pin a branch, tag, or commit and include repository-relative files or
209
+ folders. Unscoped public repositories stay native Builder sources so large
210
+ codebases are not truncated; private repos and scoped refs stay server-side and
211
+ use the saved `GITHUB_TOKEN` without exposing it to the browser or Builder.
212
+ Builder is the source of truth for the indexed brand kit, generated docs, and
213
+ usage guidance. If Builder is not connected, stop and ask the user to connect
214
+ Builder.
215
+
216
+ The legacy `githubRepoUrl` argument remains compatible for one unscoped repo.
217
+ For a saved GitHub-backed system, use `sync-design-system-with-builder --id
218
+ <localDesignSystemId>` to replay the stored repository/ref/scope after upstream
219
+ changes. Do not create a second local copy.
208
220
 
209
221
  ### Source: Local Code Files
210
222
 
@@ -51,8 +51,8 @@ Prefer the MCP App surface for a connected Design plugin, then the host's
51
51
  browser/preview tool as the universal fallback. Keep the canvas beside chat
52
52
  when the host supports rearrangeable panes.
53
53
 
54
- Inside Design, use **Show/Hide UI** from the `Cmd+K` menu or press Figma's
55
- `Shift+\` shortcut to toggle all editing chrome so only the canvas remains.
54
+ Inside Design, use **Show/Hide UI** from the `Cmd K` menu or press Figma's
55
+ `Shift \` shortcut to toggle all editing chrome so only the canvas remains.
56
56
  The same action is available from Design's empty-canvas context menu.
57
57
 
58
58
  ## Core Model
@@ -134,23 +134,46 @@ export const editorChromeBridgeScript: string = `"use strict";
134
134
  const fromSouth = input.handle === "sw" || input.handle === "s" || input.handle === "se";
135
135
  const resizesHorizontally = fromWest || fromEast;
136
136
  const resizesVertically = fromNorth || fromSouth;
137
- let width = start.width + (fromWest ? -input.delta.x : fromEast ? input.delta.x : 0);
138
- let height = start.height + (fromNorth ? -input.delta.y : fromSouth ? input.delta.y : 0);
137
+ const resizeFromCenter = Boolean(input.altKey);
138
+ let width = start.width + (fromWest ? -input.delta.x : fromEast ? input.delta.x : 0) * (resizeFromCenter ? 2 : 1);
139
+ let height = start.height + (fromNorth ? -input.delta.y : fromSouth ? input.delta.y : 0) * (resizeFromCenter ? 2 : 1);
139
140
  const minWidth = input.minWidth ?? DEFAULT_CANVAS_MIN_SIZE;
140
141
  const minHeight = input.minHeight ?? DEFAULT_CANVAS_MIN_SIZE;
141
- if (input.preserveAspectRatio && resizesHorizontally && resizesVertically && start.width > 0 && start.height > 0) {
142
- const horizontalScale = width / start.width;
143
- const verticalScale = height / start.height;
144
- const scale = Math.abs(horizontalScale - 1) >= Math.abs(verticalScale - 1) ? horizontalScale : verticalScale;
145
- const minScale = Math.max(minWidth / start.width, minHeight / start.height);
146
- width = start.width * Math.max(minScale, scale);
147
- height = start.height * Math.max(minScale, scale);
142
+ const preserveAspectRatio = Boolean(input.preserveAspectRatio) && start.width > 0 && start.height > 0;
143
+ const widthChange = Math.abs(width - start.width);
144
+ const heightChange = Math.abs(height - start.height);
145
+ const derivesHeight = preserveAspectRatio && resizesHorizontally && !resizesVertically;
146
+ const derivesWidth = preserveAspectRatio && resizesVertically && !resizesHorizontally;
147
+ if (derivesHeight) {
148
+ height = width / (start.width / start.height);
149
+ } else if (derivesWidth) {
150
+ width = height * (start.width / start.height);
151
+ } else if (preserveAspectRatio && resizesHorizontally && resizesVertically) {
152
+ if (widthChange >= heightChange) {
153
+ height = width / (start.width / start.height);
154
+ } else {
155
+ width = height * (start.width / start.height);
156
+ }
148
157
  }
158
+ const widthBelowMinimum = width < minWidth;
159
+ const heightBelowMinimum = height < minHeight;
149
160
  width = Math.max(minWidth, width);
150
161
  height = Math.max(minHeight, height);
162
+ if (preserveAspectRatio) {
163
+ if (widthBelowMinimum && !heightBelowMinimum) {
164
+ height = Math.max(minHeight, width / (start.width / start.height));
165
+ } else if (heightBelowMinimum && !widthBelowMinimum) {
166
+ width = Math.max(minWidth, height * (start.width / start.height));
167
+ } else if (widthBelowMinimum && heightBelowMinimum) {
168
+ width = Math.max(minWidth, minHeight * (start.width / start.height));
169
+ height = width / (start.width / start.height);
170
+ }
171
+ }
172
+ const centerDerivedHeight = derivesHeight && !resizeFromCenter;
173
+ const centerDerivedWidth = derivesWidth && !resizeFromCenter;
151
174
  return {
152
- x: fromWest ? start.x + start.width - width : start.x,
153
- y: fromNorth ? start.y + start.height - height : start.y,
175
+ x: resizeFromCenter || centerDerivedWidth ? start.x + (start.width - width) / 2 : fromWest ? start.x + start.width - width : start.x,
176
+ y: resizeFromCenter || centerDerivedHeight ? start.y + (start.height - height) / 2 : fromNorth ? start.y + start.height - height : start.y,
154
177
  width,
155
178
  height
156
179
  };
@@ -311,6 +334,7 @@ export const editorChromeBridgeScript: string = `"use strict";
311
334
  rect: core.resize(gestureStart.rect, {
312
335
  handle: gestureStart.handle,
313
336
  delta: convertedDelta,
337
+ altKey: pointer.altKey,
314
338
  preserveAspectRatio: Boolean(pointer.shiftKey)
315
339
  })
316
340
  };
@@ -13,6 +13,15 @@ const MAX_JSON_CONTEXT_CHARS = 2_500;
13
13
  const MAX_BUILDER_DOCS = 8;
14
14
  const MAX_BUILDER_DOC_CHARS = 1_200;
15
15
  const MAX_TOKEN_VALUES = 48;
16
+ // Per-section budgets for a locally-stored kit. These exist because one shared
17
+ // JSON dump starves whatever `JSON.stringify` happens to order last — which was
18
+ // `notes` and `customCSS`, the only carriers of component, shadow, and motion
19
+ // detail a rich import produces. Sectioning them means a 500-token kit loses
20
+ // tail tokens instead of losing its entire component vocabulary.
21
+ const MAX_NAMED_TOKENS = 220;
22
+ const MAX_CUSTOM_CSS_CHARS = 3_000;
23
+ const MAX_NOTES_CHARS = 3_000;
24
+ const MAX_CORE_TOKEN_JSON_CHARS = 2_000;
16
25
 
17
26
  interface BuilderGenerationContext {
18
27
  builderDesignSystemId: string;
@@ -50,6 +59,42 @@ function formatJson(value: unknown, maxChars = MAX_JSON_CONTEXT_CHARS): string {
50
59
  return truncate(JSON.stringify(value, null, 2), maxChars);
51
60
  }
52
61
 
62
+ /**
63
+ * The source system's own token names. A kit that renders as seven color roles
64
+ * reads as "a few colors" no matter how much was imported — these names are the
65
+ * difference between the user's design system and a palette that resembles it.
66
+ */
67
+ function formatNamedTokens(tokens: unknown): string[] {
68
+ if (!Array.isArray(tokens)) return [];
69
+ const usable = tokens.filter(
70
+ (token): token is Record<string, string> =>
71
+ Boolean(token) &&
72
+ typeof token === "object" &&
73
+ typeof (token as { name?: unknown }).name === "string" &&
74
+ typeof (token as { value?: unknown }).value === "string",
75
+ );
76
+ if (usable.length === 0) return [];
77
+ const shown = usable.slice(0, MAX_NAMED_TOKENS);
78
+ const lines = [
79
+ `Named tokens from the source system (${usable.length} total).`,
80
+ "These are the design team's own names. Use them verbatim as CSS custom " +
81
+ "properties instead of inventing generic ones:",
82
+ ...shown.map((token) => {
83
+ const cssVar = token.cssVar ? ` (${token.cssVar})` : "";
84
+ const group = token.group ? ` [${token.group}]` : "";
85
+ const type = token.type ? ` {${token.type}}` : "";
86
+ return `- ${token.name}${cssVar}: ${token.value}${type}${group}`;
87
+ }),
88
+ ];
89
+ if (shown.length < usable.length) {
90
+ lines.push(
91
+ `- [${usable.length - shown.length} further tokens stored but not listed here; ` +
92
+ "they exist in the kit — do not treat this list as the complete system]",
93
+ );
94
+ }
95
+ return lines;
96
+ }
97
+
53
98
  function formatTokenValues(tokenValues: Record<string, string>): string[] {
54
99
  const entries = Object.entries(tokenValues)
55
100
  .filter(([, value]) => typeof value === "string" && value.trim())
@@ -135,10 +180,65 @@ function buildDesignSystemAgentContext({
135
180
  );
136
181
  }
137
182
  }
138
- } else {
139
- const parsedData = parseJson(data);
183
+ }
184
+
185
+ // Builder hydration can succeed as a request and still carry nothing usable
186
+ // (a failed/incomplete index returns zero docs and zero token values). The
187
+ // stored local kit is then the only real content there is, so emit it rather
188
+ // than presenting placeholder proxy values as if they were the user's brand.
189
+ const builderUsable = Boolean(
190
+ builder &&
191
+ (builder.docCount > 0 || Object.keys(builder.tokenValues).length > 0),
192
+ );
193
+ if (builder && !builderUsable) {
194
+ lines.push(
195
+ "",
196
+ "- Builder returned no usable docs or token values for this system. " +
197
+ "Anything below comes from the locally stored kit; if that is also " +
198
+ "thin, say so and ask the user to finish indexing rather than " +
199
+ "filling the gap with a generic style.",
200
+ );
201
+ }
202
+
203
+ if (!builder || !builderUsable) {
204
+ const parsedData = parseJson(data) as Record<string, unknown> | null;
140
205
  if (parsedData) {
141
- lines.push("", "Local design-system tokens:", formatJson(parsedData));
206
+ const { tokens, customCSS, notes, ...coreTokens } = parsedData as {
207
+ tokens?: unknown;
208
+ customCSS?: unknown;
209
+ notes?: unknown;
210
+ } & Record<string, unknown>;
211
+
212
+ if (Object.keys(coreTokens).length > 0) {
213
+ lines.push(
214
+ "",
215
+ "Core design-system tokens:",
216
+ formatJson(coreTokens, MAX_CORE_TOKEN_JSON_CHARS),
217
+ );
218
+ }
219
+
220
+ const namedTokens = formatNamedTokens(tokens);
221
+ if (namedTokens.length > 0) {
222
+ lines.push("", ...namedTokens);
223
+ }
224
+
225
+ if (typeof customCSS === "string" && customCSS.trim()) {
226
+ lines.push(
227
+ "",
228
+ "Design-system CSS to place in the generated document's `:root` " +
229
+ "(use these declarations directly; do not re-derive them):",
230
+ truncate(customCSS.trim(), MAX_CUSTOM_CSS_CHARS),
231
+ );
232
+ }
233
+
234
+ if (typeof notes === "string" && notes.trim()) {
235
+ lines.push(
236
+ "",
237
+ "Design-system notes — component, elevation, motion, and usage " +
238
+ "detail captured at import. Follow these as rules, not trivia:",
239
+ truncate(notes.trim(), MAX_NOTES_CHARS),
240
+ );
241
+ }
142
242
  }
143
243
  }
144
244
 
@@ -1,4 +1,5 @@
1
1
  import { defineAction } from "@agent-native/core";
2
+ import type { BrandKitTokenType } from "@agent-native/core/brand-kit";
2
3
  import { buildDeepLink } from "@agent-native/core/server";
3
4
  import {
4
5
  CODE_MAX_FILES,
@@ -28,13 +29,10 @@ import {
28
29
  resolveTweaksToCssVars,
29
30
  } from "../shared/resolve-tweaks.js";
30
31
 
31
- type ImportedTokenType =
32
- | "color"
33
- | "typography"
34
- | "spacing"
35
- | "radius"
36
- | "shadow"
37
- | "other";
32
+ // Aliased, not re-declared: this was a hand-copy of BrandKitTokenType that
33
+ // drifted, so tokens classified here could name a category the stored kit had
34
+ // no word for.
35
+ type ImportedTokenType = BrandKitTokenType;
38
36
 
39
37
  interface ImportedDesignToken {
40
38
  name: string;
@@ -116,6 +114,23 @@ function designDeepLink(designId: string): string {
116
114
  });
117
115
  }
118
116
 
117
+ /** `120ms`, `0.2s`, `cubic-bezier(...)`, `ease-in-out`, `steps(4, end)`. */
118
+ function isMotionValue(value: string): boolean {
119
+ const v = value.trim().toLowerCase();
120
+ return (
121
+ /^-?\d*\.?\d+m?s$/.test(v) ||
122
+ /^(cubic-bezier|steps|linear)\s*\(/.test(v) ||
123
+ /^(linear|ease|ease-in|ease-out|ease-in-out|step-start|step-end)$/.test(v)
124
+ );
125
+ }
126
+
127
+ /** An offset/blur triple in px/rem/em with any color notation, or `none`. */
128
+ function isShadowValue(value: string): boolean {
129
+ const v = value.trim().toLowerCase();
130
+ if (v === "none") return true;
131
+ return /\d/.test(v) && /(px|rem|em)\b/.test(v);
132
+ }
133
+
119
134
  function classifyVar(name: string, value: string): ImportedTokenType {
120
135
  const n = name.toLowerCase();
121
136
  if (isColorValue(value)) return "color";
@@ -124,7 +139,10 @@ function classifyVar(name: string, value: string): ImportedTokenType {
124
139
  return "typography";
125
140
  }
126
141
  if (/spacing|gap|padding|margin|space/i.test(n)) return "spacing";
127
- if (/shadow|blur|drop/i.test(n)) return "shadow";
142
+ if (/shadow|blur|drop|elevation/i.test(n)) return "shadow";
143
+ if (/duration|easing|ease|transition|animation|motion|delay/i.test(n)) {
144
+ return "motion";
145
+ }
128
146
  if (
129
147
  /color|bg|background|text|border|accent|primary|secondary|surface|muted|foreground|fill|stroke/i.test(
130
148
  n,
@@ -272,6 +290,17 @@ function parseNamedLines(
272
290
  cssVar = tokenVar("spacing", label);
273
291
  } else if (/font|typeface|typography/.test(lower)) {
274
292
  cssVar = tokenVar("font", label);
293
+ } else if (/shadow|elevation/.test(lower) && isShadowValue(value)) {
294
+ cssVar = tokenVar("shadow", label);
295
+ } else if (
296
+ /duration|easing|ease|transition|animation|motion|delay/.test(lower) &&
297
+ isMotionValue(value)
298
+ ) {
299
+ // Motion is part of the brand. Dropping these here is why an imported
300
+ // system arrives with colors and type but nothing that moves. The
301
+ // value guard keeps prose out: this runs over free-text design.md
302
+ // lines, where "Release notes: v2" matches the label pattern.
303
+ cssVar = tokenVar("motion", label);
275
304
  }
276
305
 
277
306
  if (!cssVar) continue;