@agent-native/core 0.129.1 → 0.130.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 (596) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +78 -0
  3. package/corpus/core/docs/content/cloneable-saas.mdx +114 -0
  4. package/corpus/core/docs/content/creating-templates.mdx +28 -2
  5. package/corpus/core/docs/content/syncing-template-changes.mdx +7 -0
  6. package/corpus/core/docs/content/template-clips-developers.mdx +12 -5
  7. package/corpus/core/docs/content/template-dispatch-developers.mdx +1 -1
  8. package/corpus/core/docs/content/template-dispatch.mdx +1 -1
  9. package/corpus/core/package.json +1 -1
  10. package/corpus/core/src/action-ui.ts +1 -0
  11. package/corpus/core/src/action.ts +5 -1
  12. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +15 -5
  13. package/corpus/core/src/agent/harness/runner.ts +5 -0
  14. package/corpus/core/src/agent/production-agent.ts +33 -0
  15. package/corpus/core/src/agent/run-loop-with-resume.ts +5 -0
  16. package/corpus/core/src/agent/run-manager.ts +126 -1
  17. package/corpus/core/src/agent/thread-data-builder.ts +47 -2
  18. package/corpus/core/src/agent/types.ts +2 -0
  19. package/corpus/core/src/chat-threads/store.ts +4 -5
  20. package/corpus/core/src/cli/create.ts +1139 -65
  21. package/corpus/core/src/cli/design-connect.ts +3 -7
  22. package/corpus/core/src/cli/index.ts +5 -3
  23. package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +83 -47
  24. package/corpus/core/src/client/AssistantChat.tsx +27 -16
  25. package/corpus/core/src/client/agent-chat-adapter.ts +24 -0
  26. package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +17 -2
  27. package/corpus/core/src/client/chat/index.ts +1 -0
  28. package/corpus/core/src/client/chat/message-components.tsx +104 -1
  29. package/corpus/core/src/client/chat/run-recovery.tsx +15 -3
  30. package/corpus/core/src/client/chat/widgets/DataTableWidget.tsx +10 -29
  31. package/corpus/core/src/client/chat/widgets/WorkspaceFileWidget.tsx +71 -0
  32. package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +22 -0
  33. package/corpus/core/src/client/chat/widgets/workspace-file-result.ts +49 -0
  34. package/corpus/core/src/client/embed-auth.ts +2 -0
  35. package/corpus/core/src/client/org/InvitationBanner.tsx +14 -1
  36. package/corpus/core/src/client/org/OrgSwitcher.tsx +23 -0
  37. package/corpus/core/src/client/org/TeamPage.tsx +130 -0
  38. package/corpus/core/src/client/org/WorkspaceNotice.tsx +85 -0
  39. package/corpus/core/src/client/org/hooks.ts +14 -0
  40. package/corpus/core/src/client/org/index.ts +2 -0
  41. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +27 -40
  42. package/corpus/core/src/client/resources/ResourcesPanel.tsx +134 -51
  43. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +7 -0
  44. package/corpus/core/src/client/resources/use-resources.ts +6 -0
  45. package/corpus/core/src/client/use-action.ts +3 -1
  46. package/corpus/core/src/client/widgets/index.ts +1 -0
  47. package/corpus/core/src/coding-tools/run-code.ts +2 -1
  48. package/corpus/core/src/db/client.ts +28 -4
  49. package/corpus/core/src/deploy/build.ts +31 -3
  50. package/corpus/core/src/eject/remote-mcp-presets.ts +1 -1
  51. package/corpus/core/src/index.browser.ts +1 -0
  52. package/corpus/core/src/index.ts +1 -0
  53. package/corpus/core/src/integrations/google-docs-poller.ts +3 -0
  54. package/corpus/core/src/integrations/webhook-handler.ts +28 -11
  55. package/corpus/core/src/jobs/scheduler.ts +4 -0
  56. package/corpus/core/src/localization/default-messages.ts +3 -2
  57. package/corpus/core/src/mcp/build-server.ts +20 -2
  58. package/corpus/core/src/observability/traces.ts +48 -12
  59. package/corpus/core/src/org/handlers.ts +52 -1
  60. package/corpus/core/src/org/migrations.ts +4 -0
  61. package/corpus/core/src/org/plugin.ts +14 -0
  62. package/corpus/core/src/org/schema.ts +1 -0
  63. package/corpus/core/src/org/types.ts +8 -0
  64. package/corpus/core/src/org/workspace-url.ts +74 -0
  65. package/corpus/core/src/resources/handlers.ts +35 -5
  66. package/corpus/core/src/scripts/runner.ts +183 -5
  67. package/corpus/core/src/server/action-routes.ts +32 -2
  68. package/corpus/core/src/server/agent-chat-plugin.ts +29 -0
  69. package/corpus/core/src/server/agent-teams.ts +10 -1
  70. package/corpus/core/src/server/auth.ts +33 -7
  71. package/corpus/core/src/server/embed-route.ts +9 -0
  72. package/corpus/core/src/server/embed-session.ts +62 -3
  73. package/corpus/core/src/server/open-route.ts +20 -6
  74. package/corpus/core/src/server/prompts/framework-core-compact.ts +1 -1
  75. package/corpus/core/src/server/prompts/framework-core.ts +1 -0
  76. package/corpus/core/src/server/request-context.ts +36 -1
  77. package/corpus/core/src/shared/embed-auth.ts +1 -0
  78. package/corpus/core/src/shared/html-document.ts +115 -0
  79. package/corpus/core/src/shared/index.ts +1 -0
  80. package/corpus/core/src/sharing/access.ts +8 -1
  81. package/corpus/core/src/sharing/registry.ts +11 -2
  82. package/corpus/core/src/workspace-files/actions.ts +100 -0
  83. package/corpus/core/src/workspace-files/tool.ts +4 -0
  84. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +4 -0
  85. package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +9 -1
  86. package/corpus/templates/analytics/AGENTS.md +17 -24
  87. package/corpus/templates/analytics/actions/data-source-status.ts +11 -1
  88. package/corpus/templates/analytics/app/i18n-data.ts +34 -0
  89. package/corpus/templates/analytics/app/lib/data-source-status.ts +86 -0
  90. package/corpus/templates/analytics/app/lib/native-mcp-exclusions.ts +9 -0
  91. package/corpus/templates/analytics/app/pages/DataSources.tsx +184 -20
  92. package/corpus/templates/analytics/changelog/2026-07-28-native-analytics-data-sources-no-longer-show-duplicate-mc.md +6 -0
  93. package/corpus/templates/analytics/changelog/2026-07-29-exports-now-appear-in-chat-with-a-direct-download-instead-of.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-07-29-queued-chat-prompts-no-longer-resend-after-analytics-navigat.md +6 -0
  95. package/corpus/templates/analytics/changelog/2026-07-29-scheduled-dashboard-emails-load-every-panel-reliably-without.md +6 -0
  96. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +6 -3
  97. package/corpus/templates/analytics/server/lib/credential-keys.ts +1 -1
  98. package/corpus/templates/analytics/server/lib/hubspot.ts +1 -1
  99. package/corpus/templates/analytics/server/lib/provider-credentials.ts +55 -1
  100. package/corpus/templates/analytics/server/plugins/agent-chat.ts +123 -27
  101. package/corpus/templates/analytics/server/routes/[...page].get.ts +2 -7
  102. package/corpus/templates/analytics/vite.config.ts +7 -0
  103. package/corpus/templates/brain/actions/list-connection-providers.ts +100 -2
  104. package/corpus/templates/brain/app/lib/brain.ts +10 -0
  105. package/corpus/templates/brain/app/routes/sources.tsx +15 -4
  106. package/corpus/templates/brain/changelog/2026-07-29-brain-automatically-retries-failed-source-syncs-and-guides-y.md +6 -0
  107. package/corpus/templates/brain/jobs/process-ingest-queue.ts +13 -0
  108. package/corpus/templates/brain/server/jobs/sync-sources.ts +27 -22
  109. package/corpus/templates/brain/server/plugins/agent-chat.ts +2 -1
  110. package/corpus/templates/brain/server/routes/api/brain/queue/run.post.ts +7 -1
  111. package/corpus/templates/clips/AGENTS.md +1 -1
  112. package/corpus/templates/clips/actions/create-recording-agent-link.ts +11 -1
  113. package/corpus/templates/clips/actions/update-comment.ts +68 -0
  114. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +0 -1
  115. package/corpus/templates/clips/app/components/player/comments-panel.tsx +221 -65
  116. package/corpus/templates/clips/app/components/player/video-player.tsx +101 -27
  117. package/corpus/templates/clips/app/i18n/en-US.ts +2 -1
  118. package/corpus/templates/clips/app/routes/bug-report.done.tsx +48 -27
  119. package/corpus/templates/clips/app/routes/bug-report.tsx +43 -4
  120. package/corpus/templates/clips/app/routes/record.tsx +2 -0
  121. package/corpus/templates/clips/changelog/2026-07-29-clip-playback-now-recovers-instead-of-getting-stuck-when-a-b.md +6 -0
  122. package/corpus/templates/clips/changelog/2026-07-29-comments-can-now-be-edited-after-posting.md +6 -0
  123. package/corpus/templates/clips/changelog/2026-07-29-download-actions-now-use-a-shorter-clearer-label.md +6 -0
  124. package/corpus/templates/clips/changelog/2026-07-29-embedded-bug-reports-now-return-a-temporary-agent-readable.md +6 -0
  125. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  126. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +21 -1
  127. package/corpus/templates/clips/shared/bug-report.ts +99 -0
  128. package/corpus/templates/content/actions/_content-database-personal-view.ts +7 -1
  129. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +5 -2
  130. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +20 -8
  131. package/corpus/templates/content/changelog/2026-07-29-missing-databases-no-longer-trigger-repeated-personal-view-e.md +6 -0
  132. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +83 -47
  133. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +474 -55
  134. package/corpus/templates/design/.generated/bridge/embedded-wheel.generated.ts +3 -0
  135. package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +18 -1
  136. package/corpus/templates/design/.generated/bridge/source-location.generated.ts +348 -0
  137. package/corpus/templates/design/actions/add-localhost-screens.ts +2 -7
  138. package/corpus/templates/design/actions/apply-visual-edit.ts +7 -1
  139. package/corpus/templates/design/actions/connect-localhost.ts +5 -9
  140. package/corpus/templates/design/actions/create-component.ts +16 -13
  141. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +2 -7
  142. package/corpus/templates/design/actions/list-design-source-capabilities.ts +4 -7
  143. package/corpus/templates/design/actions/list-local-files.ts +26 -50
  144. package/corpus/templates/design/actions/list-localhost-connections.ts +2 -7
  145. package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +2 -2
  146. package/corpus/templates/design/actions/open-visual-edit.ts +234 -135
  147. package/corpus/templates/design/actions/read-local-file.ts +26 -50
  148. package/corpus/templates/design/actions/request-localhost-write-consent.ts +2 -7
  149. package/corpus/templates/design/actions/write-local-file.ts +31 -73
  150. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +307 -133
  151. package/corpus/templates/design/app/components/design/EditPanel.tsx +94 -22
  152. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +137 -60
  153. package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +75 -14
  154. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +755 -97
  155. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +8 -0
  156. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +24 -1
  157. package/corpus/templates/design/app/components/design/bridge/source-location.bridge.ts +530 -0
  158. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbenchLoader.tsx +278 -0
  159. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +32 -12
  160. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-setup.ts +14 -3
  161. package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +87 -24
  162. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +36 -0
  163. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +2 -1
  164. package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +48 -8
  165. package/corpus/templates/design/app/components/design/design-canvas/content-size-report.ts +3 -13
  166. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +4 -2
  167. package/corpus/templates/design/app/components/design/design-canvas/hit-test.ts +3 -13
  168. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +3 -1
  169. package/corpus/templates/design/app/components/design/edit-panel/inspect-code-source.ts +93 -0
  170. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +4 -2
  171. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +35 -0
  172. package/corpus/templates/design/app/components/design/multi-screen/iframe-targeting.ts +24 -0
  173. package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -0
  174. package/corpus/templates/design/app/components/design/types.ts +29 -0
  175. package/corpus/templates/design/app/components/layout/Layout.tsx +7 -4
  176. package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -0
  177. package/corpus/templates/design/app/i18n-data.ts +153 -46
  178. package/corpus/templates/design/app/i18n-responsive-interact.ts +12 -12
  179. package/corpus/templates/design/app/lib/design-editor-route.ts +23 -0
  180. package/corpus/templates/design/app/pages/VisualEdit.tsx +19 -6
  181. package/corpus/templates/design/app/pages/design-editor/action-error.ts +19 -0
  182. package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +29 -0
  183. package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +6 -0
  184. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +277 -15
  185. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +75 -9
  186. package/corpus/templates/design/app/pages/design-editor/layer-state-scope.ts +28 -0
  187. package/corpus/templates/design/app/pages/design-editor/live-screen-layer-drop.ts +41 -0
  188. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +496 -41
  189. package/corpus/templates/design/app/pages/design-editor/pending-live-edit-unload-guard.ts +27 -0
  190. package/corpus/templates/design/app/pages/design-editor/pending-structure-verification.ts +10 -0
  191. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +168 -9
  192. package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +41 -3
  193. package/corpus/templates/design/app/pages/design-editor/screen-command-utils.ts +5 -0
  194. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +42 -13
  195. package/corpus/templates/design/app/pages/design-editor/source-location.ts +180 -0
  196. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +17 -0
  197. package/corpus/templates/design/app/public-routes.ts +1 -0
  198. package/corpus/templates/design/app/routes/visual-edit.$id.tsx +15 -0
  199. package/corpus/templates/design/changelog/2026-06-29-screen-overview-is-easier-to-find-and-named-screens-open-dir.md +1 -1
  200. package/corpus/templates/design/changelog/2026-07-28-deleting-a-selected-element-now-removes-it-from-the-live-pre.md +6 -0
  201. package/corpus/templates/design/changelog/2026-07-28-deleting-an-element-on-a-connected-localhost-screen-now-queu.md +6 -0
  202. package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-shows-a-responsive-device-preview-with-pre.md +6 -0
  203. package/corpus/templates/design/changelog/2026-07-28-layer-hide-and-lock-now-apply-to-the-live-preview-instead-of.md +6 -0
  204. package/corpus/templates/design/changelog/2026-07-28-layers-panel-selections-on-a-connected-localhost-screen-can-.md +6 -0
  205. package/corpus/templates/design/changelog/2026-07-28-locked-layers-now-show-a-subtle-dashed-outline-in-the-live-p.md +6 -0
  206. package/corpus/templates/design/changelog/2026-07-28-the-apply-design-updates-button-now-counts-pending-live-text.md +6 -0
  207. package/corpus/templates/design/changelog/2026-07-28-the-design-canvas-no-longer-stalls-on-a-render-loop-while-a-.md +6 -0
  208. package/corpus/templates/design/changelog/2026-07-28-visual-edit-on-a-local-dev-server-no-longer-requires-signing.md +6 -0
  209. package/corpus/templates/design/changelog/2026-07-28-visual-edits-on-a-react-19-app-no-longer-report-a-source-lin.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-29-an-edit-that-cannot-be-written-to-source-now-names-the-reaso.md +6 -0
  211. package/corpus/templates/design/changelog/2026-07-29-applying-a-primitive-dropped-onto-a-connected-localhost-scre.md +6 -0
  212. package/corpus/templates/design/changelog/2026-07-29-changing-a-style-in-the-inspector-now-updates-a-connected-lo.md +6 -0
  213. package/corpus/templates/design/changelog/2026-07-29-choosing-edit-or-annotate-from-a-focused-screen-now-returns-.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-29-concurrent-source-edits-now-return-a-clear-conflict-response.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-29-creating-a-component-from-an-element-that-repeats-in-the-sou.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-29-deleting-an-element-you-just-dropped-onto-a-live-screen-no-l.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-29-dragging-a-layer-across-screens-no-longer-dies-when-the-poin.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-29-dragging-an-element-from-a-stored-screen-into-a-live-localho.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-29-drawing-a-box-on-the-canvas-and-dragging-it-into-a-connected.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-29-fixed-a-loading-screen-flicker-when-reopening-designs-with-a.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-29-interact-mode-has-edit-and-annotate-buttons-again-so-you-can.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-29-layer-groups-can-be-dropped-onto-live-localhost-screens.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-29-localhost-source-errors-now-say-what-actually-went-wrong-whi.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-29-opening-or-refreshing-a-live-localhost-screen-in-single-scre.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-29-redo-after-undoing-a-drop-onto-a-live-screen-puts-the-elemen.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-29-shapes-can-be-drawn-directly-onto-live-localhost-screens.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-29-shapes-drawn-on-the-canvas-board-now-appear-immediately-inst.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-29-style-edits-on-a-running-localhost-screen-now-preview-live-a.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-29-the-apply-design-updates-control-now-appears-as-a-clean-conn.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-29-visual-edits-on-a-mapped-component-instance-now-tell-the-age.md +6 -0
  231. package/corpus/templates/design/server/lib/design-data-access.ts +21 -7
  232. package/corpus/templates/design/server/lib/localhost-connection.ts +240 -0
  233. package/corpus/templates/design/server/routes/[...page].get.ts +2 -5
  234. package/corpus/templates/design/server/source-workspace.ts +2 -0
  235. package/corpus/templates/design/shared/code-layer.ts +43 -0
  236. package/corpus/templates/design/shared/html-content.ts +26 -0
  237. package/corpus/templates/design/shared/html-integrity.ts +21 -1
  238. package/corpus/templates/design/shared/local-jsx-visual-edit.ts +15 -0
  239. package/corpus/templates/design/shared/source-mode.ts +76 -0
  240. package/corpus/templates/dispatch/changelog/2026-07-29-workspace-members-without-admin-access-now-see-a-clear-permi.md +6 -0
  241. package/corpus/templates/dispatch/server/lib/usage-metrics.ts +2 -1
  242. package/corpus/templates/plan/app/lib/plan-annotation-runtime.ts +3 -4
  243. package/corpus/templates/plan/server/routes/[...page].get.ts +2 -5
  244. package/corpus/templates/slides/actions/patch-deck.ts +29 -17
  245. package/corpus/toolkit/CHANGELOG.md +6 -0
  246. package/corpus/toolkit/package.json +1 -1
  247. package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +3 -2
  248. package/dist/action-ui.d.ts +1 -0
  249. package/dist/action-ui.d.ts.map +1 -1
  250. package/dist/action-ui.js +1 -0
  251. package/dist/action-ui.js.map +1 -1
  252. package/dist/action.d.ts +5 -1
  253. package/dist/action.d.ts.map +1 -1
  254. package/dist/action.js.map +1 -1
  255. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  256. package/dist/agent/engine/ai-sdk-engine.js +13 -5
  257. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  258. package/dist/agent/harness/runner.d.ts.map +1 -1
  259. package/dist/agent/harness/runner.js +5 -0
  260. package/dist/agent/harness/runner.js.map +1 -1
  261. package/dist/agent/production-agent.d.ts +16 -0
  262. package/dist/agent/production-agent.d.ts.map +1 -1
  263. package/dist/agent/production-agent.js +18 -1
  264. package/dist/agent/production-agent.js.map +1 -1
  265. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  266. package/dist/agent/run-loop-with-resume.js +6 -0
  267. package/dist/agent/run-loop-with-resume.js.map +1 -1
  268. package/dist/agent/run-manager.d.ts +14 -0
  269. package/dist/agent/run-manager.d.ts.map +1 -1
  270. package/dist/agent/run-manager.js +91 -1
  271. package/dist/agent/run-manager.js.map +1 -1
  272. package/dist/agent/thread-data-builder.d.ts +3 -0
  273. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  274. package/dist/agent/thread-data-builder.js +35 -3
  275. package/dist/agent/thread-data-builder.js.map +1 -1
  276. package/dist/agent/types.d.ts +2 -0
  277. package/dist/agent/types.d.ts.map +1 -1
  278. package/dist/agent/types.js.map +1 -1
  279. package/dist/chat-threads/store.d.ts.map +1 -1
  280. package/dist/chat-threads/store.js +4 -6
  281. package/dist/chat-threads/store.js.map +1 -1
  282. package/dist/cli/create.d.ts +66 -6
  283. package/dist/cli/create.d.ts.map +1 -1
  284. package/dist/cli/create.js +765 -66
  285. package/dist/cli/create.js.map +1 -1
  286. package/dist/cli/design-connect.d.ts.map +1 -1
  287. package/dist/cli/design-connect.js +2 -7
  288. package/dist/cli/design-connect.js.map +1 -1
  289. package/dist/cli/index.js +5 -3
  290. package/dist/cli/index.js.map +1 -1
  291. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  292. package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
  293. package/dist/cli/skills-content/design-visual-edit-skill.js +83 -47
  294. package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
  295. package/dist/client/AssistantChat.d.ts.map +1 -1
  296. package/dist/client/AssistantChat.js +16 -9
  297. package/dist/client/AssistantChat.js.map +1 -1
  298. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  299. package/dist/client/agent-chat-adapter.js +21 -0
  300. package/dist/client/agent-chat-adapter.js.map +1 -1
  301. package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
  302. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
  303. package/dist/client/blocks/library/wireframe-kit.js +8 -2
  304. package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
  305. package/dist/client/chat/index.d.ts +1 -1
  306. package/dist/client/chat/index.d.ts.map +1 -1
  307. package/dist/client/chat/index.js +1 -1
  308. package/dist/client/chat/index.js.map +1 -1
  309. package/dist/client/chat/message-components.d.ts +17 -0
  310. package/dist/client/chat/message-components.d.ts.map +1 -1
  311. package/dist/client/chat/message-components.js +24 -2
  312. package/dist/client/chat/message-components.js.map +1 -1
  313. package/dist/client/chat/run-recovery.d.ts +6 -0
  314. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  315. package/dist/client/chat/run-recovery.js +13 -3
  316. package/dist/client/chat/run-recovery.js.map +1 -1
  317. package/dist/client/chat/widgets/DataTableWidget.d.ts.map +1 -1
  318. package/dist/client/chat/widgets/DataTableWidget.js +2 -3
  319. package/dist/client/chat/widgets/DataTableWidget.js.map +1 -1
  320. package/dist/client/chat/widgets/WorkspaceFileWidget.d.ts +5 -0
  321. package/dist/client/chat/widgets/WorkspaceFileWidget.d.ts.map +1 -0
  322. package/dist/client/chat/widgets/WorkspaceFileWidget.js +34 -0
  323. package/dist/client/chat/widgets/WorkspaceFileWidget.js.map +1 -0
  324. package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
  325. package/dist/client/chat/widgets/builtin-tool-renderers.js +13 -1
  326. package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
  327. package/dist/client/chat/widgets/workspace-file-result.d.ts +12 -0
  328. package/dist/client/chat/widgets/workspace-file-result.d.ts.map +1 -0
  329. package/dist/client/chat/widgets/workspace-file-result.js +34 -0
  330. package/dist/client/chat/widgets/workspace-file-result.js.map +1 -0
  331. package/dist/client/embed-auth.d.ts.map +1 -1
  332. package/dist/client/embed-auth.js +2 -1
  333. package/dist/client/embed-auth.js.map +1 -1
  334. package/dist/client/org/InvitationBanner.d.ts +5 -2
  335. package/dist/client/org/InvitationBanner.d.ts.map +1 -1
  336. package/dist/client/org/InvitationBanner.js +9 -2
  337. package/dist/client/org/InvitationBanner.js.map +1 -1
  338. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  339. package/dist/client/org/OrgSwitcher.js +3 -2
  340. package/dist/client/org/OrgSwitcher.js.map +1 -1
  341. package/dist/client/org/TeamPage.d.ts.map +1 -1
  342. package/dist/client/org/TeamPage.js +27 -3
  343. package/dist/client/org/TeamPage.js.map +1 -1
  344. package/dist/client/org/WorkspaceNotice.d.ts +18 -0
  345. package/dist/client/org/WorkspaceNotice.d.ts.map +1 -0
  346. package/dist/client/org/WorkspaceNotice.js +56 -0
  347. package/dist/client/org/WorkspaceNotice.js.map +1 -0
  348. package/dist/client/org/hooks.d.ts +1 -0
  349. package/dist/client/org/hooks.d.ts.map +1 -1
  350. package/dist/client/org/hooks.js +12 -0
  351. package/dist/client/org/hooks.js.map +1 -1
  352. package/dist/client/org/index.d.ts +2 -1
  353. package/dist/client/org/index.d.ts.map +1 -1
  354. package/dist/client/org/index.js +2 -1
  355. package/dist/client/org/index.js.map +1 -1
  356. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  357. package/dist/client/resources/McpIntegrationDialog.js +8 -12
  358. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  359. package/dist/client/resources/ResourcesPanel.d.ts +3 -0
  360. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  361. package/dist/client/resources/ResourcesPanel.js +49 -20
  362. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  363. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  364. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  365. package/dist/client/resources/mcp-integration-catalog.js +3 -0
  366. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  367. package/dist/client/resources/use-resources.d.ts +1 -0
  368. package/dist/client/resources/use-resources.d.ts.map +1 -1
  369. package/dist/client/resources/use-resources.js +3 -0
  370. package/dist/client/resources/use-resources.js.map +1 -1
  371. package/dist/client/use-action.d.ts +3 -1
  372. package/dist/client/use-action.d.ts.map +1 -1
  373. package/dist/client/use-action.js +3 -1
  374. package/dist/client/use-action.js.map +1 -1
  375. package/dist/client/widgets/index.d.ts +1 -1
  376. package/dist/client/widgets/index.d.ts.map +1 -1
  377. package/dist/client/widgets/index.js +1 -1
  378. package/dist/client/widgets/index.js.map +1 -1
  379. package/dist/coding-tools/run-code.js +2 -1
  380. package/dist/coding-tools/run-code.js.map +1 -1
  381. package/dist/collab/awareness.d.ts +2 -2
  382. package/dist/collab/awareness.d.ts.map +1 -1
  383. package/dist/collab/routes.d.ts +1 -1
  384. package/dist/db/client.d.ts +1 -0
  385. package/dist/db/client.d.ts.map +1 -1
  386. package/dist/db/client.js +24 -3
  387. package/dist/db/client.js.map +1 -1
  388. package/dist/deploy/build.d.ts.map +1 -1
  389. package/dist/deploy/build.js +25 -3
  390. package/dist/deploy/build.js.map +1 -1
  391. package/dist/eject/remote-mcp-presets.js +1 -1
  392. package/dist/eject/remote-mcp-presets.js.map +1 -1
  393. package/dist/index.browser.d.ts +1 -1
  394. package/dist/index.browser.d.ts.map +1 -1
  395. package/dist/index.browser.js +1 -1
  396. package/dist/index.browser.js.map +1 -1
  397. package/dist/index.d.ts +1 -1
  398. package/dist/index.d.ts.map +1 -1
  399. package/dist/index.js +1 -1
  400. package/dist/index.js.map +1 -1
  401. package/dist/integrations/google-docs-poller.d.ts.map +1 -1
  402. package/dist/integrations/google-docs-poller.js +4 -1
  403. package/dist/integrations/google-docs-poller.js.map +1 -1
  404. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  405. package/dist/integrations/webhook-handler.js +23 -11
  406. package/dist/integrations/webhook-handler.js.map +1 -1
  407. package/dist/jobs/scheduler.js +4 -0
  408. package/dist/jobs/scheduler.js.map +1 -1
  409. package/dist/localization/default-messages.d.ts +3 -2
  410. package/dist/localization/default-messages.d.ts.map +1 -1
  411. package/dist/localization/default-messages.js +3 -2
  412. package/dist/localization/default-messages.js.map +1 -1
  413. package/dist/mcp/build-server.d.ts.map +1 -1
  414. package/dist/mcp/build-server.js +10 -1
  415. package/dist/mcp/build-server.js.map +1 -1
  416. package/dist/notifications/routes.d.ts +1 -1
  417. package/dist/observability/traces.d.ts.map +1 -1
  418. package/dist/observability/traces.js +27 -7
  419. package/dist/observability/traces.js.map +1 -1
  420. package/dist/org/handlers.d.ts +15 -0
  421. package/dist/org/handlers.d.ts.map +1 -1
  422. package/dist/org/handlers.js +45 -1
  423. package/dist/org/handlers.js.map +1 -1
  424. package/dist/org/migrations.d.ts.map +1 -1
  425. package/dist/org/migrations.js +4 -0
  426. package/dist/org/migrations.js.map +1 -1
  427. package/dist/org/plugin.d.ts +1 -0
  428. package/dist/org/plugin.d.ts.map +1 -1
  429. package/dist/org/plugin.js +10 -1
  430. package/dist/org/plugin.js.map +1 -1
  431. package/dist/org/schema.d.ts +19 -0
  432. package/dist/org/schema.d.ts.map +1 -1
  433. package/dist/org/schema.js +1 -0
  434. package/dist/org/schema.js.map +1 -1
  435. package/dist/org/types.d.ts +8 -0
  436. package/dist/org/types.d.ts.map +1 -1
  437. package/dist/org/types.js.map +1 -1
  438. package/dist/org/workspace-url.d.ts +32 -0
  439. package/dist/org/workspace-url.d.ts.map +1 -0
  440. package/dist/org/workspace-url.js +63 -0
  441. package/dist/org/workspace-url.js.map +1 -0
  442. package/dist/progress/routes.d.ts +1 -1
  443. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  444. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  445. package/dist/resources/handlers.d.ts +2 -3
  446. package/dist/resources/handlers.d.ts.map +1 -1
  447. package/dist/resources/handlers.js +25 -5
  448. package/dist/resources/handlers.js.map +1 -1
  449. package/dist/scripts/runner.d.ts +17 -0
  450. package/dist/scripts/runner.d.ts.map +1 -1
  451. package/dist/scripts/runner.js +130 -5
  452. package/dist/scripts/runner.js.map +1 -1
  453. package/dist/secrets/routes.d.ts +9 -9
  454. package/dist/server/action-routes.d.ts.map +1 -1
  455. package/dist/server/action-routes.js +24 -2
  456. package/dist/server/action-routes.js.map +1 -1
  457. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  458. package/dist/server/agent-chat-plugin.js +26 -1
  459. package/dist/server/agent-chat-plugin.js.map +1 -1
  460. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  461. package/dist/server/agent-teams.d.ts.map +1 -1
  462. package/dist/server/agent-teams.js +10 -1
  463. package/dist/server/agent-teams.js.map +1 -1
  464. package/dist/server/auth.d.ts +22 -0
  465. package/dist/server/auth.d.ts.map +1 -1
  466. package/dist/server/auth.js +33 -7
  467. package/dist/server/auth.js.map +1 -1
  468. package/dist/server/embed-route.d.ts.map +1 -1
  469. package/dist/server/embed-route.js +6 -1
  470. package/dist/server/embed-route.js.map +1 -1
  471. package/dist/server/embed-session.d.ts +7 -0
  472. package/dist/server/embed-session.d.ts.map +1 -1
  473. package/dist/server/embed-session.js +46 -4
  474. package/dist/server/embed-session.js.map +1 -1
  475. package/dist/server/open-route.d.ts.map +1 -1
  476. package/dist/server/open-route.js +17 -7
  477. package/dist/server/open-route.js.map +1 -1
  478. package/dist/server/prompts/framework-core-compact.js +1 -1
  479. package/dist/server/prompts/framework-core-compact.js.map +1 -1
  480. package/dist/server/prompts/framework-core.d.ts.map +1 -1
  481. package/dist/server/prompts/framework-core.js +1 -0
  482. package/dist/server/prompts/framework-core.js.map +1 -1
  483. package/dist/server/request-context.d.ts +26 -0
  484. package/dist/server/request-context.d.ts.map +1 -1
  485. package/dist/server/request-context.js +18 -1
  486. package/dist/server/request-context.js.map +1 -1
  487. package/dist/shared/embed-auth.d.ts +1 -0
  488. package/dist/shared/embed-auth.d.ts.map +1 -1
  489. package/dist/shared/embed-auth.js +1 -0
  490. package/dist/shared/embed-auth.js.map +1 -1
  491. package/dist/shared/html-document.d.ts +12 -0
  492. package/dist/shared/html-document.d.ts.map +1 -0
  493. package/dist/shared/html-document.js +92 -0
  494. package/dist/shared/html-document.js.map +1 -0
  495. package/dist/shared/index.d.ts +1 -0
  496. package/dist/shared/index.d.ts.map +1 -1
  497. package/dist/shared/index.js +1 -0
  498. package/dist/shared/index.js.map +1 -1
  499. package/dist/sharing/access.d.ts +1 -0
  500. package/dist/sharing/access.d.ts.map +1 -1
  501. package/dist/sharing/access.js +8 -2
  502. package/dist/sharing/access.js.map +1 -1
  503. package/dist/sharing/registry.d.ts +3 -0
  504. package/dist/sharing/registry.d.ts.map +1 -1
  505. package/dist/sharing/registry.js.map +1 -1
  506. package/dist/workspace-files/actions.d.ts +28 -0
  507. package/dist/workspace-files/actions.d.ts.map +1 -0
  508. package/dist/workspace-files/actions.js +75 -0
  509. package/dist/workspace-files/actions.js.map +1 -0
  510. package/dist/workspace-files/tool.d.ts.map +1 -1
  511. package/dist/workspace-files/tool.js +4 -0
  512. package/dist/workspace-files/tool.js.map +1 -1
  513. package/docs/content/cloneable-saas.mdx +114 -0
  514. package/docs/content/creating-templates.mdx +28 -2
  515. package/docs/content/syncing-template-changes.mdx +7 -0
  516. package/docs/content/template-clips-developers.mdx +12 -5
  517. package/docs/content/template-dispatch-developers.mdx +1 -1
  518. package/docs/content/template-dispatch.mdx +1 -1
  519. package/package.json +2 -2
  520. package/src/action-ui.ts +1 -0
  521. package/src/action.ts +5 -1
  522. package/src/agent/engine/ai-sdk-engine.ts +15 -5
  523. package/src/agent/harness/runner.ts +5 -0
  524. package/src/agent/production-agent.ts +33 -0
  525. package/src/agent/run-loop-with-resume.ts +5 -0
  526. package/src/agent/run-manager.ts +126 -1
  527. package/src/agent/thread-data-builder.ts +47 -2
  528. package/src/agent/types.ts +2 -0
  529. package/src/chat-threads/store.ts +4 -5
  530. package/src/cli/create.ts +1139 -65
  531. package/src/cli/design-connect.ts +3 -7
  532. package/src/cli/index.ts +5 -3
  533. package/src/cli/skills-content/design-visual-edit-skill.ts +83 -47
  534. package/src/client/AssistantChat.tsx +27 -16
  535. package/src/client/agent-chat-adapter.ts +24 -0
  536. package/src/client/blocks/library/wireframe-kit.tsx +17 -2
  537. package/src/client/chat/index.ts +1 -0
  538. package/src/client/chat/message-components.tsx +104 -1
  539. package/src/client/chat/run-recovery.tsx +15 -3
  540. package/src/client/chat/widgets/DataTableWidget.tsx +10 -29
  541. package/src/client/chat/widgets/WorkspaceFileWidget.tsx +71 -0
  542. package/src/client/chat/widgets/builtin-tool-renderers.tsx +22 -0
  543. package/src/client/chat/widgets/workspace-file-result.ts +49 -0
  544. package/src/client/embed-auth.ts +2 -0
  545. package/src/client/org/InvitationBanner.tsx +14 -1
  546. package/src/client/org/OrgSwitcher.tsx +23 -0
  547. package/src/client/org/TeamPage.tsx +130 -0
  548. package/src/client/org/WorkspaceNotice.tsx +85 -0
  549. package/src/client/org/hooks.ts +14 -0
  550. package/src/client/org/index.ts +2 -0
  551. package/src/client/resources/McpIntegrationDialog.tsx +27 -40
  552. package/src/client/resources/ResourcesPanel.tsx +134 -51
  553. package/src/client/resources/mcp-integration-catalog.ts +7 -0
  554. package/src/client/resources/use-resources.ts +6 -0
  555. package/src/client/use-action.ts +3 -1
  556. package/src/client/widgets/index.ts +1 -0
  557. package/src/coding-tools/run-code.ts +2 -1
  558. package/src/db/client.ts +28 -4
  559. package/src/deploy/build.ts +31 -3
  560. package/src/eject/remote-mcp-presets.ts +1 -1
  561. package/src/index.browser.ts +1 -0
  562. package/src/index.ts +1 -0
  563. package/src/integrations/google-docs-poller.ts +3 -0
  564. package/src/integrations/webhook-handler.ts +28 -11
  565. package/src/jobs/scheduler.ts +4 -0
  566. package/src/localization/default-messages.ts +3 -2
  567. package/src/mcp/build-server.ts +20 -2
  568. package/src/observability/traces.ts +48 -12
  569. package/src/org/handlers.ts +52 -1
  570. package/src/org/migrations.ts +4 -0
  571. package/src/org/plugin.ts +14 -0
  572. package/src/org/schema.ts +1 -0
  573. package/src/org/types.ts +8 -0
  574. package/src/org/workspace-url.ts +74 -0
  575. package/src/resources/handlers.ts +35 -5
  576. package/src/scripts/runner.ts +183 -5
  577. package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +2 -1
  578. package/src/server/action-routes.ts +32 -2
  579. package/src/server/agent-chat-plugin.ts +29 -0
  580. package/src/server/agent-teams.ts +10 -1
  581. package/src/server/auth.ts +33 -7
  582. package/src/server/embed-route.ts +9 -0
  583. package/src/server/embed-session.ts +62 -3
  584. package/src/server/open-route.ts +20 -6
  585. package/src/server/prompts/framework-core-compact.ts +1 -1
  586. package/src/server/prompts/framework-core.ts +1 -0
  587. package/src/server/request-context.ts +36 -1
  588. package/src/shared/embed-auth.ts +1 -0
  589. package/src/shared/html-document.ts +115 -0
  590. package/src/shared/index.ts +1 -0
  591. package/src/sharing/access.ts +8 -1
  592. package/src/sharing/registry.ts +11 -2
  593. package/src/workspace-files/actions.ts +100 -0
  594. package/src/workspace-files/tool.ts +4 -0
  595. package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +0 -6
  596. package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +0 -6
@@ -73,6 +73,11 @@ const HEADLESS_OPTION = {
73
73
  label: "Headless",
74
74
  hint: "Action-first app with one hello primitive and no UI shell",
75
75
  };
76
+ const COMMUNITY_OPTION = {
77
+ name: "community",
78
+ label: "Community template",
79
+ hint: "Install a third-party Agent Native app from a public GitHub repository",
80
+ };
76
81
  /**
77
82
  * Main entry for `agent-native create [name]`.
78
83
  *
@@ -138,6 +143,11 @@ export async function createApp(name, opts) {
138
143
  await createStandaloneApp(name, { ...opts, template: shape }, clack);
139
144
  return;
140
145
  }
146
+ if (shape === "community") {
147
+ const template = await promptCommunityTemplate(clack);
148
+ await createStandaloneApp(name, { ...opts, template }, clack);
149
+ return;
150
+ }
141
151
  // shape === "template" → full app(s) in a workspace.
142
152
  await createWorkspaceInteractive(name, opts, clack);
143
153
  return;
@@ -150,6 +160,7 @@ export async function createApp(name, opts) {
150
160
  * choice made here implies the project structure, so we deliberately avoid a
151
161
  * separate "workspace or standalone?" question:
152
162
  * - "template" → full app(s) in a workspace (the multi-select picker)
163
+ * - "community" → a single standalone app from a public GitHub repository
153
164
  * - "chat" → a single standalone chat UI app
154
165
  * - "headless" → a single standalone action-first app with no UI shell
155
166
  * Chat and headless are standalone on purpose: a monorepo is unnecessary
@@ -176,8 +187,13 @@ function startShapePromptOptions() {
176
187
  },
177
188
  {
178
189
  value: "template",
179
- label: "Full template(s)",
180
- hint: "Clone complete apps (Mail, Calendar, Slides, ...) into a workspace",
190
+ label: "First-party template(s)",
191
+ hint: "Clone official apps (Mail, Calendar, Slides, ...) into a workspace",
192
+ },
193
+ {
194
+ value: COMMUNITY_OPTION.name,
195
+ label: COMMUNITY_OPTION.label,
196
+ hint: COMMUNITY_OPTION.hint,
181
197
  },
182
198
  {
183
199
  value: "headless",
@@ -187,6 +203,52 @@ function startShapePromptOptions() {
187
203
  ],
188
204
  };
189
205
  }
206
+ async function promptCommunityTemplate(clack) {
207
+ const selection = await clack.text({
208
+ message: "Which community template do you want to install?",
209
+ placeholder: "https://github.com/owner/repo[?app=id][#ref]",
210
+ validate(value) {
211
+ try {
212
+ parseCommunityPromptValue(String(value ?? ""));
213
+ }
214
+ catch (error) {
215
+ return error instanceof Error ? error.message : String(error);
216
+ }
217
+ },
218
+ });
219
+ if (clack.isCancel(selection)) {
220
+ clack.cancel("Cancelled.");
221
+ process.exit(0);
222
+ }
223
+ return parseCommunityPromptValue(String(selection)).canonical;
224
+ }
225
+ async function promptCommunityWorkspaceApp(apps, clack) {
226
+ const selection = await clack.select({
227
+ message: "Which app from this community workspace do you want to install?",
228
+ options: apps.map((app) => ({
229
+ value: app.name,
230
+ label: app.label,
231
+ hint: app.label === app.name ? undefined : app.name,
232
+ })),
233
+ });
234
+ if (clack.isCancel(selection)) {
235
+ clack.cancel("Cancelled.");
236
+ process.exit(0);
237
+ }
238
+ return String(selection);
239
+ }
240
+ function communityScaffoldOptions(clack, shape, destinationAppName, targetWorkspaceCoreName) {
241
+ return {
242
+ shape,
243
+ destinationAppName,
244
+ targetWorkspaceCoreName,
245
+ ...(process.stdin.isTTY && process.stdout.isTTY
246
+ ? {
247
+ selectCommunityWorkspaceApp: (apps) => promptCommunityWorkspaceApp(apps, clack),
248
+ }
249
+ : {}),
250
+ };
251
+ }
190
252
  /**
191
253
  * Validate a project name supplied on the command line. Mirrors the rule in
192
254
  * `promptNameIfMissing` so an invalid name is rejected before any interactive
@@ -233,6 +295,9 @@ async function createWorkspaceInteractive(name, opts, clack) {
233
295
  process.exit(1);
234
296
  }
235
297
  const s = clack.spinner();
298
+ for (const template of templates) {
299
+ showCommunityTemplateTrustNote(template, clack);
300
+ }
236
301
  s.start(`Scaffolding workspace "${name}"...`);
237
302
  const appNames = new Set();
238
303
  const scaffoldedApps = [];
@@ -254,17 +319,22 @@ async function createWorkspaceInteractive(name, opts, clack) {
254
319
  // Distinguish download vs local copy in the spinner so a multi-second
255
320
  // GitHub fetch doesn't look like a frozen "Scaffolding..." message.
256
321
  // Mirrors the local-vs-remote decision inside scaffoldAppTemplate.
257
- const willDownload = templateName !== "headless" && templateName.startsWith("github:")
258
- ? true
259
- : !findLocalTemplate(normalizeTemplateName(templateName));
322
+ const willDownload = templateName !== "headless" &&
323
+ (isCommunityTemplateSelection(templateName) ||
324
+ !findLocalTemplate(normalizeTemplateName(templateName)));
260
325
  s.message(willDownload
261
326
  ? `Downloading ${titleCase(appName)} template (${i + 1}/${templates.length})...`
262
327
  : `Scaffolding ${titleCase(appName)} (${i + 1}/${templates.length})...`);
263
328
  const appDir = path.join(targetDir, "apps", appName);
264
- await scaffoldAppTemplate(appDir, templateName);
329
+ const resolution = await scaffoldAppTemplate(appDir, templateName, communityScaffoldOptions(clack, "workspace", appName, workspaceCoreName));
265
330
  s.message(`Configuring ${titleCase(appName)} (${i + 1}/${templates.length})...`);
266
331
  replacePlaceholders(appDir, appName, appTitleForScaffold(appName), name);
267
- rewriteTrackingAppId(appDir, appName, templateName);
332
+ if (resolution.sourceIdentity) {
333
+ applyScaffoldIdentity(appDir, appName, templateName, resolution.sourceIdentity);
334
+ }
335
+ else {
336
+ rewriteTrackingAppId(appDir, appName, templateName);
337
+ }
268
338
  workspacifyApp({
269
339
  appDir,
270
340
  appName,
@@ -275,8 +345,11 @@ async function createWorkspaceInteractive(name, opts, clack) {
275
345
  dispatchDependencyVersion: getDispatchDependencyVersion(),
276
346
  toolkitDependencyVersion: getToolkitDependencyVersion(),
277
347
  });
278
- fixPackageJsonName(appDir, appName, templateName);
279
- fixWebManifestName(appDir, appName, templateName);
348
+ fixPackageJsonName(appDir, appName, templateName, {
349
+ ...resolution,
350
+ shape: "workspace",
351
+ });
352
+ fixWebManifestName(appDir, appName, templateName, resolution.sourceIdentity);
280
353
  rewriteNetlifyToml(appDir, appName, "workspace");
281
354
  renameGitignore(appDir);
282
355
  // Each app owns its own .claude / .agents symlinks.
@@ -337,10 +410,10 @@ async function createWorkspaceInteractive(name, opts, clack) {
337
410
  }
338
411
  function workspaceAppNameForTemplateSelection(templateName) {
339
412
  const normalized = normalizeTemplateName(templateName);
340
- if (!normalized.startsWith("github:"))
413
+ const community = parseCommunityTemplateSelection(normalized, false);
414
+ if (!community)
341
415
  return templateName;
342
- const repo = normalized.slice("github:".length).trim();
343
- const repoName = repo.split("/").filter(Boolean).pop() ?? "app";
416
+ const repoName = community.app ?? community.repo.split("/").pop() ?? "app";
344
417
  let appName = repoName
345
418
  .toLowerCase()
346
419
  .replace(/[^a-z0-9-]+/g, "-")
@@ -499,11 +572,17 @@ async function scaffoldOneAppIntoWorkspace(workspace, appName, templateName, cla
499
572
  process.exit(1);
500
573
  }
501
574
  const s = clack.spinner();
575
+ showCommunityTemplateTrustNote(templateName, clack);
502
576
  s.start(`Working... no action needed. Scaffolding apps/${appName} from ${templateName}.`);
503
577
  try {
504
- const resolution = await scaffoldAppTemplate(appDir, templateName);
578
+ const resolution = await scaffoldAppTemplate(appDir, templateName, communityScaffoldOptions(clack, "workspace", appName, workspace.workspaceCoreName));
505
579
  replacePlaceholders(appDir, appName, appTitleForScaffold(appName), path.basename(workspace.workspaceRoot));
506
- rewriteTrackingAppId(appDir, appName, templateName);
580
+ if (resolution.sourceIdentity) {
581
+ applyScaffoldIdentity(appDir, appName, templateName, resolution.sourceIdentity);
582
+ }
583
+ else {
584
+ rewriteTrackingAppId(appDir, appName, templateName);
585
+ }
507
586
  workspacifyApp({
508
587
  appDir,
509
588
  appName,
@@ -518,7 +597,7 @@ async function scaffoldOneAppIntoWorkspace(workspace, appName, templateName, cla
518
597
  ...resolution,
519
598
  shape: "workspace",
520
599
  });
521
- fixWebManifestName(appDir, appName, templateName);
600
+ fixWebManifestName(appDir, appName, templateName, resolution.sourceIdentity);
522
601
  rewriteNetlifyToml(appDir, appName, "workspace");
523
602
  renameGitignore(appDir);
524
603
  setupAgentSymlinks(appDir);
@@ -556,20 +635,7 @@ async function createStandaloneApp(name, opts, clack) {
556
635
  if (!template) {
557
636
  const picked = await clack.select({
558
637
  message: "Which template would you like to use?",
559
- options: [
560
- {
561
- value: HEADLESS_OPTION.name,
562
- label: HEADLESS_OPTION.label,
563
- hint: HEADLESS_OPTION.hint,
564
- },
565
- ...onRampFirst(coreTemplates())
566
- .filter((t) => t.name !== HEADLESS_OPTION.name)
567
- .map((t) => ({
568
- value: t.name,
569
- label: t.label,
570
- hint: t.hint,
571
- })),
572
- ],
638
+ options: standaloneTemplatePromptOptions(),
573
639
  });
574
640
  if (clack.isCancel(picked)) {
575
641
  clack.cancel("Cancelled.");
@@ -578,12 +644,17 @@ async function createStandaloneApp(name, opts, clack) {
578
644
  template = picked;
579
645
  }
580
646
  template = normalizeTemplateName(template);
647
+ if (template === COMMUNITY_OPTION.name) {
648
+ template = await promptCommunityTemplate(clack);
649
+ }
581
650
  const s = clack.spinner();
651
+ showCommunityTemplateTrustNote(template, clack);
582
652
  s.start(template === "headless"
583
653
  ? "Scaffolding the headless agent app..."
584
- : `Downloading the ${template} template from GitHub...`);
654
+ : (communityTemplateDownloadMessage(template) ??
655
+ `Downloading the ${template} template from GitHub...`));
585
656
  try {
586
- const resolution = await scaffoldAppTemplate(targetDir, template);
657
+ const resolution = await scaffoldAppTemplate(targetDir, template, communityScaffoldOptions(clack, "standalone", name));
587
658
  s.message(`Setting up ${name}…`);
588
659
  postProcessStandalone(name, targetDir, template, resolution);
589
660
  s.stop("App created!");
@@ -611,6 +682,27 @@ async function createStandaloneApp(name, opts, clack) {
611
682
  clack.outro(`Done! Next steps:\n\n cd ${name}\n pnpm install\n pnpm dev`);
612
683
  }
613
684
  }
685
+ function standaloneTemplatePromptOptions() {
686
+ return [
687
+ {
688
+ value: HEADLESS_OPTION.name,
689
+ label: HEADLESS_OPTION.label,
690
+ hint: HEADLESS_OPTION.hint,
691
+ },
692
+ ...onRampFirst(coreTemplates())
693
+ .filter((t) => t.name !== HEADLESS_OPTION.name)
694
+ .map((t) => ({
695
+ value: t.name,
696
+ label: t.label,
697
+ hint: t.hint,
698
+ })),
699
+ {
700
+ value: COMMUNITY_OPTION.name,
701
+ label: COMMUNITY_OPTION.label,
702
+ hint: COMMUNITY_OPTION.hint,
703
+ },
704
+ ];
705
+ }
614
706
  /**
615
707
  * Remove a partially-scaffolded target directory after a scaffold failure so a
616
708
  * retry doesn't hit the "Directory already exists" guard. Best-effort — the
@@ -630,11 +722,12 @@ function cleanupOnFailure(targetDir) {
630
722
  /**
631
723
  * Scaffold a single app template into `targetDir`. Resolves:
632
724
  * - "headless" / legacy "blank" → bundled action-first template
633
- * - "github:user/repo" → download the whole repo
725
+ * - "community:user/repo[#ref]" → download and validate the whole repo
726
+ * - legacy "github:user/repo[#ref]" and clean GitHub HTTPS URLs → community
634
727
  * - first-party template name → use a bundled copy or download its subdir
635
728
  * from BuilderIO/agent-native
636
729
  */
637
- async function scaffoldAppTemplate(targetDir, template) {
730
+ async function scaffoldAppTemplate(targetDir, template, options = {}) {
638
731
  fs.mkdirSync(path.dirname(targetDir), { recursive: true });
639
732
  // Normalize legacy / renamed aliases.
640
733
  let resolved = normalizeTemplateName(template);
@@ -650,13 +743,38 @@ async function scaffoldAppTemplate(targetDir, template) {
650
743
  templateRef: getGitHubTemplateRefCandidates()[0],
651
744
  };
652
745
  }
653
- if (resolved.startsWith("github:")) {
654
- const repo = resolved.slice("github:".length);
655
- await downloadGitHubRepo(repo, targetDir);
656
- return {};
746
+ const community = parseCommunityTemplateSelection(resolved, false);
747
+ if (community) {
748
+ const stagingDir = path.join(path.dirname(targetDir), `.agent-native-community-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
749
+ try {
750
+ const ref = await downloadGitHubRepo(community.repo, stagingDir, community.ref);
751
+ const source = await resolveCommunityTemplateSource(stagingDir, community, options);
752
+ copyDir(source.sourceDir, targetDir);
753
+ if (source.workspaceRoot) {
754
+ normalizeCommunityWorkspaceAppDependencies(targetDir, source.workspaceRoot, {
755
+ shape: options.shape,
756
+ destinationAppName: options.destinationAppName ?? path.basename(targetDir),
757
+ targetWorkspaceCoreName: options.targetWorkspaceCoreName,
758
+ sourceIdentity: source.sourceIdentity,
759
+ });
760
+ }
761
+ return {
762
+ templateSource: "github",
763
+ templateRef: ref,
764
+ sourceIdentity: source.sourceIdentity,
765
+ communityTemplate: {
766
+ source: `https://github.com/${community.repo}`,
767
+ ref,
768
+ ...(source.app ? { app: source.app } : {}),
769
+ },
770
+ };
771
+ }
772
+ finally {
773
+ fs.rmSync(stagingDir, { recursive: true, force: true });
774
+ }
657
775
  }
658
776
  if (!getTemplate(resolved)) {
659
- throw new Error(`Unknown template "${template}". Known: ${allTemplateNames().join(", ")} or use github:user/repo for community templates.`);
777
+ throw new Error(`Unknown template "${template}". Known first-party templates: ${allTemplateNames().join(", ")}. For a community template, use community:owner/repo.`);
660
778
  }
661
779
  // If running from the framework monorepo with a local templates/ dir, use
662
780
  // that. Otherwise download from GitHub. This keeps `agent-native create`
@@ -718,8 +836,149 @@ function normalizeTemplateName(template) {
718
836
  if (template === "image" || template === "images" || template === "asset") {
719
837
  return "assets";
720
838
  }
839
+ const community = parseCommunityTemplateSelection(template, false);
840
+ if (community)
841
+ return community.canonical;
721
842
  return template;
722
843
  }
844
+ function parseCommunityTemplateSelection(selection, required = true) {
845
+ const value = selection.trim();
846
+ let raw;
847
+ if (value.startsWith("community:")) {
848
+ raw = value.slice("community:".length).trim();
849
+ }
850
+ else if (value.startsWith("github:")) {
851
+ raw = value.slice("github:".length).trim();
852
+ }
853
+ else if (value.startsWith("https://github.com/")) {
854
+ raw = value;
855
+ }
856
+ else {
857
+ if (!required)
858
+ return undefined;
859
+ throw new ValidationError(`Invalid community template "${selection}". Use community:owner/repo, optionally with #branch, #tag, or #commit.`);
860
+ }
861
+ if (!raw) {
862
+ throw new ValidationError("A community template repository is required. Use owner/repo or a GitHub URL.");
863
+ }
864
+ let repo;
865
+ let app;
866
+ let ref;
867
+ if (raw.includes("://")) {
868
+ let url;
869
+ try {
870
+ url = new URL(raw);
871
+ }
872
+ catch {
873
+ throw new ValidationError(`Invalid GitHub repository URL "${raw}". Expected https://github.com/owner/repo.`);
874
+ }
875
+ const parts = url.pathname.split("/").filter(Boolean);
876
+ if (url.protocol !== "https:" ||
877
+ url.hostname !== "github.com" ||
878
+ url.port ||
879
+ url.username ||
880
+ url.password ||
881
+ parts.length !== 2) {
882
+ throw new ValidationError(`Invalid GitHub repository URL "${raw}". Expected https://github.com/owner/repo with an optional #ref.`);
883
+ }
884
+ repo = `${parts[0]}/${parts[1].replace(/\.git$/, "")}`;
885
+ app = parseCommunityAppSearchParams(url.searchParams, raw);
886
+ if (url.hash.length > 1) {
887
+ try {
888
+ ref = decodeURIComponent(url.hash.slice(1));
889
+ }
890
+ catch {
891
+ throw new ValidationError(`Invalid encoded Git ref in "${raw}".`);
892
+ }
893
+ }
894
+ }
895
+ else {
896
+ const hash = raw.indexOf("#");
897
+ const beforeHash = hash === -1 ? raw : raw.slice(0, hash);
898
+ const query = beforeHash.indexOf("?");
899
+ repo = (query === -1 ? beforeHash : beforeHash.slice(0, query)).replace(/\.git$/, "");
900
+ if (query !== -1) {
901
+ app = parseCommunityAppSearchParams(new URLSearchParams(beforeHash.slice(query + 1)), raw);
902
+ }
903
+ ref = hash === -1 ? undefined : raw.slice(hash + 1);
904
+ }
905
+ validateRepoName(repo);
906
+ if (app !== undefined)
907
+ validateCommunityAppSelector(app);
908
+ if (ref !== undefined)
909
+ validateGitRef(ref);
910
+ return {
911
+ repo,
912
+ ...(app ? { app } : {}),
913
+ ...(ref ? { ref } : {}),
914
+ canonical: `community:${repo}${app ? `?app=${app}` : ""}${ref ? `#${ref}` : ""}`,
915
+ };
916
+ }
917
+ function parseCommunityAppSearchParams(params, source) {
918
+ const entries = [...params.entries()];
919
+ if (entries.length === 0)
920
+ return undefined;
921
+ if (entries.length !== 1 || entries[0]?.[0] !== "app" || !entries[0][1]) {
922
+ throw new ValidationError(`Invalid community template selector in "${source}". Only ?app=<app-name> is supported.`);
923
+ }
924
+ return entries[0][1];
925
+ }
926
+ function validateCommunityAppSelector(app) {
927
+ if (app === "dispatch") {
928
+ throw new ValidationError("Dispatch cannot be installed from a community workspace.");
929
+ }
930
+ const error = getWorkspaceAppIdValidationError(app);
931
+ if (error)
932
+ throw new ValidationError(error);
933
+ }
934
+ function isCommunityTemplateSelection(selection) {
935
+ return parseCommunityTemplateSelection(selection, false) !== undefined;
936
+ }
937
+ function parseCommunityPromptValue(value) {
938
+ const trimmed = value.trim();
939
+ const selection = trimmed.startsWith("community:") ||
940
+ trimmed.startsWith("github:") ||
941
+ trimmed.startsWith("https://github.com/")
942
+ ? trimmed
943
+ : `community:${trimmed}`;
944
+ return parseCommunityTemplateSelection(selection);
945
+ }
946
+ function validateGitRef(ref) {
947
+ if (!ref ||
948
+ !/^[a-zA-Z0-9][a-zA-Z0-9._/+-]*$/.test(ref) ||
949
+ ref.includes("..") ||
950
+ ref.includes("//") ||
951
+ ref.includes("@{") ||
952
+ ref.endsWith("/") ||
953
+ ref.endsWith(".") ||
954
+ ref.split("/").some((part) => !part || part.endsWith(".lock"))) {
955
+ throw new ValidationError(`Invalid Git ref "${ref}". Use a branch, tag, or commit SHA without spaces or Git ref control characters.`);
956
+ }
957
+ }
958
+ function communityTemplateTrustMessage(selection) {
959
+ const community = parseCommunityTemplateSelection(selection, false);
960
+ if (!community)
961
+ return undefined;
962
+ return [
963
+ `${community.repo} is third-party code and is not reviewed or maintained by Agent Native.`,
964
+ "The CLI downloads source only; it does not install dependencies or run template scripts.",
965
+ "Review the generated files before running pnpm install.",
966
+ community.ref
967
+ ? `Requested ref: ${community.ref}`
968
+ : "No ref supplied; the CLI will try main, then master.",
969
+ ].join("\n");
970
+ }
971
+ function communityTemplateDownloadMessage(selection) {
972
+ const community = parseCommunityTemplateSelection(selection, false);
973
+ return community
974
+ ? `Downloading community template ${community.repo} from GitHub...`
975
+ : undefined;
976
+ }
977
+ function showCommunityTemplateTrustNote(selection, clack) {
978
+ const message = communityTemplateTrustMessage(selection);
979
+ if (message)
980
+ clack.note(message, "Community template — review before use");
981
+ }
723
982
  /**
724
983
  * Find a local packages/<name> directory (for framework development).
725
984
  * Returns undefined when running as a published npm package.
@@ -867,13 +1126,12 @@ async function scaffoldRequiredPackages(templateNames, workspaceRoot) {
867
1126
  function postProcessStandalone(name, targetDir, templateName, resolution) {
868
1127
  const appTitle = appTitleForScaffold(name);
869
1128
  replacePlaceholders(targetDir, name, appTitle);
870
- rewriteTrackingAppId(targetDir, name, templateName);
871
- rewriteAgentChatAppId(targetDir, name, templateName);
1129
+ applyScaffoldIdentity(targetDir, name, templateName, resolution?.sourceIdentity);
872
1130
  fixPackageJsonName(targetDir, name, templateName, {
873
1131
  ...resolution,
874
1132
  shape: "standalone",
875
1133
  });
876
- fixWebManifestName(targetDir, name, templateName);
1134
+ fixWebManifestName(targetDir, name, templateName, resolution?.sourceIdentity);
877
1135
  rewriteNetlifyToml(targetDir, name, "standalone");
878
1136
  for (const base of ["learnings"]) {
879
1137
  const defaultsFile = path.join(targetDir, `${base}.defaults.md`);
@@ -1157,12 +1415,14 @@ export function detectWorkspace(startDir) {
1157
1415
  }
1158
1416
  export { parseWorkspaceScope };
1159
1417
  /** @internal — exported for E2E tests */
1160
- export { scaffoldWorkspaceRoot as _scaffoldWorkspaceRoot, scaffoldAppTemplate as _scaffoldAppTemplate, scaffoldRequiredPackages as _scaffoldRequiredPackages, postProcessStandalone as _postProcessStandalone, loadCatalog as _loadCatalog, fixPackageJsonName as _fixPackageJsonName, renameGitignore as _renameGitignore, rewriteNetlifyToml as _rewriteNetlifyToml, getCoreDependencyVersion as _getCoreDependencyVersion, getDispatchDependencyVersion as _getDispatchDependencyVersion, getToolkitDependencyVersion as _getToolkitDependencyVersion, getCorePackageVersion as _getCorePackageVersion, getGitHubTemplateRef as _getGitHubTemplateRef, getGitHubTemplateRefCandidates as _getGitHubTemplateRefCandidates, githubTarballUrl as _githubTarballUrl, findLocalTemplateFrom as _findLocalTemplateFrom, workspaceAppNameForTemplateSelection as _workspaceAppNameForTemplateSelection, startShapePromptOptions as _startShapePromptOptions, shouldSkipScaffoldEntry as _shouldSkipScaffoldEntry, tarExtractArgs as _tarExtractArgs, downloadGitHubSubdir as _downloadGitHubSubdir, findLocalTemplate as _findLocalTemplate, templateSourceName as _templateSourceName, normalizeTemplateName as _normalizeTemplateName, appTitleForScaffold as _appTitleForScaffold, replacePlaceholders as _replacePlaceholders, rewriteTrackingAppId as _rewriteTrackingAppId, rewriteAgentChatAppId as _rewriteAgentChatAppId, fixWebManifestName as _fixWebManifestName, copyDir as _copyDir, localTemplateSourceKind as _localTemplateSourceKind, REPO as _REPO, TEMPLATES_DIR as _TEMPLATES_DIR, };
1418
+ export { scaffoldWorkspaceRoot as _scaffoldWorkspaceRoot, scaffoldAppTemplate as _scaffoldAppTemplate, scaffoldRequiredPackages as _scaffoldRequiredPackages, postProcessStandalone as _postProcessStandalone, loadCatalog as _loadCatalog, fixPackageJsonName as _fixPackageJsonName, renameGitignore as _renameGitignore, rewriteNetlifyToml as _rewriteNetlifyToml, getCoreDependencyVersion as _getCoreDependencyVersion, getDispatchDependencyVersion as _getDispatchDependencyVersion, getToolkitDependencyVersion as _getToolkitDependencyVersion, getCorePackageVersion as _getCorePackageVersion, getGitHubTemplateRef as _getGitHubTemplateRef, getGitHubTemplateRefCandidates as _getGitHubTemplateRefCandidates, githubTarballUrl as _githubTarballUrl, findLocalTemplateFrom as _findLocalTemplateFrom, workspaceAppNameForTemplateSelection as _workspaceAppNameForTemplateSelection, startShapePromptOptions as _startShapePromptOptions, standaloneTemplatePromptOptions as _standaloneTemplatePromptOptions, parseCommunityTemplateSelection as _parseCommunityTemplateSelection, communityTemplateTrustMessage as _communityTemplateTrustMessage, communityTemplateTarballUrl as _communityTemplateTarballUrl, assertCommunityTemplateRoot as _assertCommunityTemplateRoot, assertSafeCommunityArchiveListing as _assertSafeCommunityArchiveListing, validateCommunityArchive as _validateCommunityArchive, resolveCommunityTemplateSource as _resolveCommunityTemplateSource, discoverCommunityWorkspaceApps as _discoverCommunityWorkspaceApps, normalizeCommunityWorkspaceAppDependencies as _normalizeCommunityWorkspaceAppDependencies, shouldSkipScaffoldEntry as _shouldSkipScaffoldEntry, tarExtractArgs as _tarExtractArgs, downloadGitHubSubdir as _downloadGitHubSubdir, findLocalTemplate as _findLocalTemplate, templateSourceName as _templateSourceName, normalizeTemplateName as _normalizeTemplateName, appTitleForScaffold as _appTitleForScaffold, replacePlaceholders as _replacePlaceholders, rewriteTrackingAppId as _rewriteTrackingAppId, rewriteAgentChatAppId as _rewriteAgentChatAppId, applyScaffoldIdentity as _applyScaffoldIdentity, fixWebManifestName as _fixWebManifestName, copyDir as _copyDir, localTemplateSourceKind as _localTemplateSourceKind, REPO as _REPO, TEMPLATES_DIR as _TEMPLATES_DIR, };
1161
1419
  /* ─────────────────────────────────────────────────────────────────────────
1162
1420
  * Download / copy helpers
1163
1421
  * ───────────────────────────────────────────────────────────────────────── */
1164
1422
  function validateRepoName(repo) {
1165
- if (!/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repo)) {
1423
+ const parts = repo.split("/");
1424
+ if (!/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repo) ||
1425
+ parts.some((part) => part === "." || part === "..")) {
1166
1426
  throw new ValidationError(`Invalid repository name "${repo}". Expected format: user/repo`);
1167
1427
  }
1168
1428
  }
@@ -1173,7 +1433,18 @@ function tarExtractArgs(tarPath, destDir, options = {}) {
1173
1433
  pattern,
1174
1434
  ])
1175
1435
  : [];
1176
- return ["xzf", tarPath, "--strip-components=1", ...excludes, "-C", destDir];
1436
+ const safeOwnership = options.untrustedCommunityArchive
1437
+ ? ["--no-same-owner", "--no-same-permissions"]
1438
+ : [];
1439
+ return [
1440
+ "xzf",
1441
+ tarPath,
1442
+ "--strip-components=1",
1443
+ ...safeOwnership,
1444
+ ...excludes,
1445
+ "-C",
1446
+ destDir,
1447
+ ];
1177
1448
  }
1178
1449
  function execFileBuffer(command, args, options) {
1179
1450
  return new Promise((resolve, reject) => {
@@ -1208,6 +1479,9 @@ async function downloadAndExtract(url, destDir, options = {}) {
1208
1479
  const tarPath = path.join(destDir, ".download.tar.gz");
1209
1480
  fs.writeFileSync(tarPath, tarball);
1210
1481
  try {
1482
+ if (options.untrustedCommunityArchive) {
1483
+ validateCommunityArchive(tarPath);
1484
+ }
1211
1485
  execFileSync("tar", tarExtractArgs(tarPath, destDir, options), {
1212
1486
  stdio: "pipe",
1213
1487
  });
@@ -1216,6 +1490,40 @@ async function downloadAndExtract(url, destDir, options = {}) {
1216
1490
  fs.unlinkSync(tarPath);
1217
1491
  }
1218
1492
  }
1493
+ function validateCommunityArchive(tarPath) {
1494
+ const listing = execFileSync("tar", ["tvzf", tarPath], {
1495
+ encoding: "utf-8",
1496
+ stdio: ["ignore", "pipe", "pipe"],
1497
+ });
1498
+ assertSafeCommunityArchiveListing(listing);
1499
+ }
1500
+ function assertSafeCommunityArchiveListing(listing) {
1501
+ const unsafeEntry = listing.split(/\r?\n/).find((line) => {
1502
+ if (line.startsWith("h"))
1503
+ return true;
1504
+ if (!line.startsWith("l"))
1505
+ return false;
1506
+ return !isCanonicalAgentSymlinkListing(line);
1507
+ });
1508
+ if (unsafeEntry) {
1509
+ throw new ValidationError("Community template archives may only contain Agent Native's canonical internal symlinks (CLAUDE.md and .claude/skills). Remove other symbolic or hard links and try again.");
1510
+ }
1511
+ }
1512
+ function isCanonicalAgentSymlinkListing(line) {
1513
+ const match = line.match(/\s(\S+)\s+->\s+(\S+)\s*$/);
1514
+ if (!match)
1515
+ return false;
1516
+ const archivePath = match[1];
1517
+ const target = match[2];
1518
+ const parts = archivePath.split("/");
1519
+ if (parts.some((part) => !part || part === "." || part === ".." || part.includes("\\"))) {
1520
+ return false;
1521
+ }
1522
+ return ((parts.at(-1) === "CLAUDE.md" && target === "AGENTS.md") ||
1523
+ (parts.at(-2) === ".claude" &&
1524
+ parts.at(-1) === "skills" &&
1525
+ target === "../.agents/skills"));
1526
+ }
1219
1527
  /** Resolves to the ref that actually succeeded so callers can record it. */
1220
1528
  async function downloadGitHubSubdir(repo, subdir, targetDir, refOverride) {
1221
1529
  validateRepoName(repo);
@@ -1249,10 +1557,332 @@ async function downloadGitHubSubdir(repo, subdir, targetDir, refOverride) {
1249
1557
  }
1250
1558
  throw new Error(`Failed to download templates from ${repo}. Tried refs:\n${errors.join("\n")}`);
1251
1559
  }
1252
- async function downloadGitHubRepo(repo, targetDir) {
1560
+ async function downloadGitHubRepo(repo, targetDir, requestedRef) {
1253
1561
  validateRepoName(repo);
1254
- const tarUrl = githubTarballUrl(repo, "main", "branch");
1255
- await downloadAndExtract(tarUrl, targetDir);
1562
+ const refs = requestedRef ? [requestedRef] : ["main", "master"];
1563
+ const errors = [];
1564
+ for (const ref of refs) {
1565
+ try {
1566
+ await downloadAndExtract(communityTemplateTarballUrl(repo, ref), targetDir, { untrustedCommunityArchive: true });
1567
+ return ref;
1568
+ }
1569
+ catch (error) {
1570
+ errors.push(` ${ref}: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}`);
1571
+ cleanupOnFailure(targetDir);
1572
+ }
1573
+ }
1574
+ throw new Error(`Failed to download community template ${repo}. Tried refs:\n${errors.join("\n")}\nThe repository must be public, and the requested branch, tag, or commit must exist.`);
1575
+ }
1576
+ function communityTemplateTarballUrl(repo, ref) {
1577
+ return `https://codeload.github.com/${repo}/tar.gz/${encodeURIComponent(ref)}`;
1578
+ }
1579
+ function assertCommunityTemplateRoot(targetDir, repo) {
1580
+ const packagePath = path.join(targetDir, "package.json");
1581
+ if (!fs.existsSync(packagePath)) {
1582
+ throw new ValidationError(`Community template ${repo} is not an Agent Native app at the repository root: package.json was not found. Point to a repository whose root is the app.`);
1583
+ }
1584
+ let pkg;
1585
+ try {
1586
+ pkg = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
1587
+ }
1588
+ catch {
1589
+ throw new ValidationError(`Community template ${repo} has an unreadable package.json at the repository root.`);
1590
+ }
1591
+ const dependencyGroups = [
1592
+ pkg.dependencies,
1593
+ pkg.devDependencies,
1594
+ pkg.peerDependencies,
1595
+ ];
1596
+ const usesAgentNativeCore = dependencyGroups.some((group) => group !== null &&
1597
+ typeof group === "object" &&
1598
+ !Array.isArray(group) &&
1599
+ typeof group["@agent-native/core"] ===
1600
+ "string");
1601
+ if (!usesAgentNativeCore) {
1602
+ throw new ValidationError(`Community template ${repo} is not an Agent Native app at the repository root. Its package.json must directly depend on @agent-native/core in dependencies, devDependencies, or peerDependencies.`);
1603
+ }
1604
+ }
1605
+ async function resolveCommunityTemplateSource(stagingDir, selection, options = {}) {
1606
+ const rootPkg = readPackageJsonObject(path.join(stagingDir, "package.json"));
1607
+ const agentNative = rootPkg?.["agent-native"] &&
1608
+ typeof rootPkg["agent-native"] === "object" &&
1609
+ !Array.isArray(rootPkg["agent-native"])
1610
+ ? rootPkg["agent-native"]
1611
+ : undefined;
1612
+ const hasWorkspaceCoreMarker = agentNative && Object.hasOwn(agentNative, "workspaceCore");
1613
+ const workspaceCore = agentNative?.workspaceCore;
1614
+ if (hasWorkspaceCoreMarker &&
1615
+ (typeof workspaceCore !== "string" || !workspaceCore.trim())) {
1616
+ throw new ValidationError(`Community repository ${selection.repo} has invalid agent-native.workspaceCore metadata. Expected a non-empty workspace package name.`);
1617
+ }
1618
+ const isWorkspace = typeof workspaceCore === "string";
1619
+ if (!isWorkspace) {
1620
+ assertCommunityTemplateRoot(stagingDir, selection.repo);
1621
+ const sourceIdentity = readCommunitySourceIdentity(stagingDir, selection.repo);
1622
+ if (selection.app) {
1623
+ throw new ValidationError(`Community repository ${selection.repo} is a single app at its root. Remove ?app=${selection.app}; app selectors are only for workspace repositories.`);
1624
+ }
1625
+ return { sourceDir: stagingDir, sourceIdentity };
1626
+ }
1627
+ const workspaceApps = discoverCommunityWorkspaceApps(stagingDir);
1628
+ if (workspaceApps.length === 0) {
1629
+ throw new ValidationError(`Community workspace ${selection.repo} has no installable apps. Dispatch cannot be installed from a community workspace, and apps must directly depend on @agent-native/core.`);
1630
+ }
1631
+ let selectedApp = selection.app;
1632
+ if (!selectedApp) {
1633
+ if (!options.selectCommunityWorkspaceApp) {
1634
+ throw new ValidationError(`Community workspace ${selection.repo} requires an app selection, but no interactive terminal is available. Select one with ?app=<app-name>. Available apps: ${workspaceApps.map((app) => app.name).join(", ")}.`);
1635
+ }
1636
+ else {
1637
+ selectedApp = await options.selectCommunityWorkspaceApp(workspaceApps.map(({ name, label }) => ({ name, label })));
1638
+ validateCommunityAppSelector(selectedApp);
1639
+ }
1640
+ }
1641
+ const selected = workspaceApps.find((app) => app.name === selectedApp);
1642
+ if (!selected) {
1643
+ throw new ValidationError(`App "${selectedApp}" was not found in community workspace ${selection.repo}. Available apps: ${workspaceApps.map((app) => app.name).join(", ")}.`);
1644
+ }
1645
+ return {
1646
+ sourceDir: selected.dir,
1647
+ sourceIdentity: selected.sourceIdentity,
1648
+ app: selected.name,
1649
+ workspaceRoot: stagingDir,
1650
+ };
1651
+ }
1652
+ function discoverCommunityWorkspaceApps(workspaceRoot) {
1653
+ const appsDir = path.join(workspaceRoot, "apps");
1654
+ if (!fs.existsSync(appsDir))
1655
+ return [];
1656
+ return fs
1657
+ .readdirSync(appsDir, { withFileTypes: true })
1658
+ .filter((entry) => entry.isDirectory())
1659
+ .flatMap((entry) => {
1660
+ const appDir = path.join(appsDir, entry.name);
1661
+ const packagePath = path.join(appDir, "package.json");
1662
+ if (!fs.existsSync(packagePath))
1663
+ return [];
1664
+ const pkg = readPackageJsonObject(packagePath);
1665
+ if (!pkg) {
1666
+ throw new ValidationError(`Community workspace app "${entry.name}" has an unreadable package.json.`);
1667
+ }
1668
+ if (getWorkspaceAppIdValidationError(entry.name))
1669
+ return [];
1670
+ if (!packageDirectlyDependsOnAgentNativeCore(pkg))
1671
+ return [];
1672
+ const sourceIdentity = readCommunitySourceIdentity(appDir, `${path.basename(workspaceRoot)}/${entry.name}`);
1673
+ const packageName = sourceIdentity.appName.split("/").pop() ?? sourceIdentity.appName;
1674
+ if (packageName === "dispatch")
1675
+ return [];
1676
+ return [
1677
+ {
1678
+ name: entry.name,
1679
+ label: sourceIdentity.appTitle,
1680
+ dir: appDir,
1681
+ sourceIdentity,
1682
+ },
1683
+ ];
1684
+ })
1685
+ .sort((a, b) => a.name.localeCompare(b.name));
1686
+ }
1687
+ function readPackageJsonObject(packagePath) {
1688
+ try {
1689
+ const value = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
1690
+ return value && typeof value === "object" && !Array.isArray(value)
1691
+ ? value
1692
+ : undefined;
1693
+ }
1694
+ catch {
1695
+ return undefined;
1696
+ }
1697
+ }
1698
+ function packageDirectlyDependsOnAgentNativeCore(pkg) {
1699
+ return [pkg.dependencies, pkg.devDependencies, pkg.peerDependencies].some((group) => group !== null &&
1700
+ typeof group === "object" &&
1701
+ !Array.isArray(group) &&
1702
+ typeof group["@agent-native/core"] ===
1703
+ "string");
1704
+ }
1705
+ function normalizeCommunityWorkspaceAppDependencies(appDir, sourceWorkspaceRoot, options) {
1706
+ const appPackagePath = path.join(appDir, "package.json");
1707
+ const pkg = readPackageJsonObject(appPackagePath);
1708
+ if (!pkg) {
1709
+ throw new ValidationError("Selected community workspace app has an unreadable package.json.");
1710
+ }
1711
+ const rootPkg = readPackageJsonObject(path.join(sourceWorkspaceRoot, "package.json"));
1712
+ const agentNative = rootPkg?.["agent-native"] &&
1713
+ typeof rootPkg["agent-native"] === "object" &&
1714
+ !Array.isArray(rootPkg["agent-native"])
1715
+ ? rootPkg["agent-native"]
1716
+ : undefined;
1717
+ const sourceWorkspaceCore = typeof agentNative?.workspaceCore === "string"
1718
+ ? agentNative.workspaceCore
1719
+ : undefined;
1720
+ const catalog = loadCommunityWorkspaceCatalog(sourceWorkspaceRoot);
1721
+ const supportedWorkspacePackages = new Set([
1722
+ "@agent-native/core",
1723
+ "@agent-native/dispatch",
1724
+ "@agent-native/toolkit",
1725
+ ]);
1726
+ for (const depType of [
1727
+ "dependencies",
1728
+ "devDependencies",
1729
+ "peerDependencies",
1730
+ ]) {
1731
+ const deps = pkg[depType];
1732
+ if (!deps || typeof deps !== "object" || Array.isArray(deps))
1733
+ continue;
1734
+ for (const [key, value] of Object.entries(deps)) {
1735
+ if (key === sourceWorkspaceCore) {
1736
+ delete deps[key];
1737
+ continue;
1738
+ }
1739
+ if (value === "catalog:") {
1740
+ const resolved = catalog[key];
1741
+ if (!resolved) {
1742
+ throw new ValidationError(`Community workspace app dependency "${key}" uses catalog:, but no value was found in the source pnpm-workspace.yaml catalog.`);
1743
+ }
1744
+ deps[key] = resolved;
1745
+ continue;
1746
+ }
1747
+ if (typeof value === "string" && value.startsWith("catalog:")) {
1748
+ throw new ValidationError(`Community workspace app dependency "${key}" uses unsupported named catalog reference "${value}". Use a concrete version or the default catalog.`);
1749
+ }
1750
+ if (typeof value === "string" &&
1751
+ value.startsWith("workspace:") &&
1752
+ supportedWorkspacePackages.has(key)) {
1753
+ deps[key] =
1754
+ key === "@agent-native/core"
1755
+ ? getCoreDependencyVersion()
1756
+ : key === "@agent-native/dispatch"
1757
+ ? getDispatchDependencyVersion()
1758
+ : getToolkitDependencyVersion();
1759
+ continue;
1760
+ }
1761
+ if (typeof value === "string" && value.startsWith("workspace:")) {
1762
+ throw new ValidationError(`Community workspace app dependency "${key}" uses "${value}", but that source workspace package is not included when installing one app. Publish it with a concrete version or remove the dependency.`);
1763
+ }
1764
+ }
1765
+ }
1766
+ fs.writeFileSync(appPackagePath, JSON.stringify(pkg, null, 2) + "\n");
1767
+ if (sourceWorkspaceCore) {
1768
+ rewriteCanonicalCommunityWorkspacePlugins(appDir, sourceWorkspaceCore, options);
1769
+ }
1770
+ if (sourceWorkspaceCore &&
1771
+ findSourceImportSpecifier(appDir, sourceWorkspaceCore)) {
1772
+ throw new ValidationError(`Community workspace app imports its source shared package "${sourceWorkspaceCore}", which is not included when installing one app. Move that code into the app or publish it as a concrete dependency.`);
1773
+ }
1774
+ }
1775
+ function rewriteCanonicalCommunityWorkspacePlugins(appDir, sourceWorkspaceCore, options) {
1776
+ const authPath = path.join(appDir, "server", "plugins", "auth.ts");
1777
+ const agentChatPath = path.join(appDir, "server", "plugins", "agent-chat.ts");
1778
+ if (options.shape === "standalone") {
1779
+ if (fs.existsSync(authPath)) {
1780
+ const auth = fs.readFileSync(authPath, "utf-8");
1781
+ if (containsModuleSpecifier(auth, sourceWorkspaceCore) &&
1782
+ auth.includes("defaultAuthPlugin") &&
1783
+ auth.includes("workspaceServer")) {
1784
+ fs.writeFileSync(authPath, 'export { defaultAuthPlugin as default } from "@agent-native/core/server";\n');
1785
+ }
1786
+ }
1787
+ if (fs.existsSync(agentChatPath)) {
1788
+ const agentChat = fs.readFileSync(agentChatPath, "utf-8");
1789
+ if (containsModuleSpecifier(agentChat, sourceWorkspaceCore) &&
1790
+ agentChat.includes("createWorkspaceAgentChatPlugin") &&
1791
+ agentChat.includes("actionsRegistry")) {
1792
+ fs.writeFileSync(agentChatPath, [
1793
+ 'import { createAgentChatPlugin, loadActionsFromStaticRegistry } from "@agent-native/core/server";',
1794
+ 'import actionsRegistry from "../../.generated/actions-registry.js";',
1795
+ "",
1796
+ "export default createAgentChatPlugin({",
1797
+ ` appId: ${JSON.stringify(options.destinationAppName)},`,
1798
+ " actions: loadActionsFromStaticRegistry(actionsRegistry),",
1799
+ "});",
1800
+ "",
1801
+ ].join("\n"));
1802
+ }
1803
+ }
1804
+ return;
1805
+ }
1806
+ if (options.shape === "workspace" && options.targetWorkspaceCoreName) {
1807
+ for (const pluginPath of [authPath, agentChatPath]) {
1808
+ if (!fs.existsSync(pluginPath))
1809
+ continue;
1810
+ const content = fs.readFileSync(pluginPath, "utf-8");
1811
+ const next = replaceModuleSpecifierPrefix(content, sourceWorkspaceCore, options.targetWorkspaceCoreName);
1812
+ if (next !== content)
1813
+ fs.writeFileSync(pluginPath, next);
1814
+ }
1815
+ }
1816
+ }
1817
+ function containsModuleSpecifier(content, moduleName) {
1818
+ return new RegExp(`(["'])${escapeRegExp(moduleName)}(?:/[^"']*)?\\1`).test(content);
1819
+ }
1820
+ function replaceModuleSpecifierPrefix(content, sourceModule, targetModule) {
1821
+ return content.replace(new RegExp(`(["'])${escapeRegExp(sourceModule)}((?:/[^"']*)?)\\1`, "g"), (_match, quote, suffix) => `${quote}${targetModule}${suffix}${quote}`);
1822
+ }
1823
+ function findSourceImportSpecifier(dir, moduleName) {
1824
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
1825
+ if (entry.isSymbolicLink())
1826
+ continue;
1827
+ const filePath = path.join(dir, entry.name);
1828
+ if (entry.isDirectory()) {
1829
+ const nested = findSourceImportSpecifier(filePath, moduleName);
1830
+ if (nested)
1831
+ return nested;
1832
+ continue;
1833
+ }
1834
+ if (!/\.(?:[cm]?[jt]sx?)$/.test(entry.name))
1835
+ continue;
1836
+ try {
1837
+ const content = fs.readFileSync(filePath, "utf-8");
1838
+ const escaped = escapeRegExp(moduleName);
1839
+ if (new RegExp(`(?:from\\s*|import\\s*\\(\\s*|import\\s*|require\\s*\\(\\s*)(["'])${escaped}(?:/[^"']*)?\\1`).test(content)) {
1840
+ return filePath;
1841
+ }
1842
+ }
1843
+ catch {
1844
+ // Ignore unreadable source files; package validation reports main issues.
1845
+ }
1846
+ }
1847
+ return undefined;
1848
+ }
1849
+ function loadCommunityWorkspaceCatalog(workspaceRoot) {
1850
+ const workspacePath = path.join(workspaceRoot, "pnpm-workspace.yaml");
1851
+ if (!fs.existsSync(workspacePath))
1852
+ return {};
1853
+ const result = {};
1854
+ let inCatalog = false;
1855
+ for (const line of fs.readFileSync(workspacePath, "utf-8").split("\n")) {
1856
+ if (/^catalog:\s*$/.test(line)) {
1857
+ inCatalog = true;
1858
+ continue;
1859
+ }
1860
+ if (!inCatalog)
1861
+ continue;
1862
+ if (/^\S/.test(line))
1863
+ break;
1864
+ const match = line.match(/^\s+"?([^":]+)"?\s*:\s*"?([^"]+)"?\s*$/);
1865
+ if (match)
1866
+ result[match[1]] = match[2];
1867
+ }
1868
+ return result;
1869
+ }
1870
+ function readCommunitySourceIdentity(targetDir, repo) {
1871
+ const fallbackName = repo.split("/").pop() ?? "app";
1872
+ try {
1873
+ const pkg = JSON.parse(fs.readFileSync(path.join(targetDir, "package.json"), "utf-8"));
1874
+ const appName = typeof pkg.name === "string" && pkg.name.trim()
1875
+ ? pkg.name.trim()
1876
+ : fallbackName;
1877
+ const unscopedName = appName.split("/").pop() ?? appName;
1878
+ const appTitle = typeof pkg.displayName === "string" && pkg.displayName.trim()
1879
+ ? pkg.displayName.trim()
1880
+ : titleCase(unscopedName);
1881
+ return { appName, appTitle };
1882
+ }
1883
+ catch {
1884
+ return { appName: fallbackName, appTitle: titleCase(fallbackName) };
1885
+ }
1256
1886
  }
1257
1887
  function githubTarballUrl(repo, ref, kind) {
1258
1888
  return `https://codeload.github.com/${repo}/tar.gz/refs/${kind === "tag" ? "tags" : "heads"}/${encodeURIComponent(ref)}`;
@@ -1368,6 +1998,9 @@ function isChatOnRampTemplate(templateName) {
1368
1998
  return templateName === "chat" || templateName === "starter";
1369
1999
  }
1370
2000
  function trackingTemplateName(templateName) {
2001
+ if (templateName && isCommunityTemplateSelection(templateName)) {
2002
+ return undefined;
2003
+ }
1371
2004
  return templateName === "starter" ? "chat" : templateName;
1372
2005
  }
1373
2006
  function defaultPackageDescriptionForScaffold(appName) {
@@ -1398,26 +2031,36 @@ function fixPackageJsonName(appDir, name, templateName, provenance) {
1398
2031
  (isChatOnRampTemplate(templateName) && name !== templateName)) {
1399
2032
  pkg.description = defaultPackageDescriptionForScaffold(name);
1400
2033
  }
2034
+ else if (provenance?.sourceIdentity &&
2035
+ typeof pkg.description === "string") {
2036
+ pkg.description = replaceSourceIdentityText(pkg.description, provenance.sourceIdentity, name, appTitle);
2037
+ }
1401
2038
  const scaffoldGuidance = scaffoldGuidanceForTemplate(templateName);
1402
- if (scaffoldGuidance) {
2039
+ if (scaffoldGuidance || provenance?.communityTemplate) {
1403
2040
  const agentNative = pkg["agent-native"] &&
1404
2041
  typeof pkg["agent-native"] === "object" &&
1405
2042
  !Array.isArray(pkg["agent-native"])
1406
2043
  ? pkg["agent-native"]
1407
2044
  : {};
1408
- const coreVersion = getCorePackageVersion();
1409
- agentNative.scaffold = {
1410
- template: trackingTemplateName(templateName),
1411
- frameworkSkills: scaffoldGuidance,
1412
- ...(provenance?.templateRef
1413
- ? { templateRef: provenance.templateRef }
1414
- : {}),
1415
- ...(provenance?.templateSource
1416
- ? { templateSource: provenance.templateSource }
1417
- : {}),
1418
- ...(coreVersion ? { coreVersion } : {}),
1419
- ...(provenance?.shape ? { shape: provenance.shape } : {}),
1420
- };
2045
+ if (scaffoldGuidance) {
2046
+ const coreVersion = getCorePackageVersion();
2047
+ agentNative.scaffold = {
2048
+ template: trackingTemplateName(templateName),
2049
+ frameworkSkills: scaffoldGuidance,
2050
+ ...(provenance?.templateRef
2051
+ ? { templateRef: provenance.templateRef }
2052
+ : {}),
2053
+ ...(provenance?.templateSource
2054
+ ? { templateSource: provenance.templateSource }
2055
+ : {}),
2056
+ ...(coreVersion ? { coreVersion } : {}),
2057
+ ...(provenance?.shape ? { shape: provenance.shape } : {}),
2058
+ };
2059
+ }
2060
+ if (provenance?.communityTemplate) {
2061
+ delete agentNative.scaffold;
2062
+ agentNative.communityTemplate = provenance.communityTemplate;
2063
+ }
1421
2064
  pkg["agent-native"] = agentNative;
1422
2065
  }
1423
2066
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
@@ -1432,9 +2075,11 @@ function scaffoldGuidanceForTemplate(templateName) {
1432
2075
  return "headless";
1433
2076
  return getTemplate(normalized) ? "default" : undefined;
1434
2077
  }
1435
- function fixWebManifestName(appDir, name, templateName) {
1436
- if (!isChatOnRampTemplate(templateName) || name === templateName)
2078
+ function fixWebManifestName(appDir, name, templateName, sourceIdentity) {
2079
+ if ((!isChatOnRampTemplate(templateName) || name === templateName) &&
2080
+ !sourceIdentity) {
1437
2081
  return;
2082
+ }
1438
2083
  const manifestPath = path.join(appDir, "public", "manifest.json");
1439
2084
  if (!fs.existsSync(manifestPath))
1440
2085
  return;
@@ -1443,7 +2088,10 @@ function fixWebManifestName(appDir, name, templateName) {
1443
2088
  const appTitle = titleCase(name);
1444
2089
  manifest.name = appTitle;
1445
2090
  manifest.short_name = appTitle;
1446
- if (typeof manifest.description !== "string" ||
2091
+ if (sourceIdentity && typeof manifest.description === "string") {
2092
+ manifest.description = replaceSourceIdentityText(manifest.description, sourceIdentity, name, appTitle);
2093
+ }
2094
+ else if (typeof manifest.description !== "string" ||
1447
2095
  /\b(blank app|starter|chat-first)\b/i.test(manifest.description)) {
1448
2096
  manifest.description = defaultPackageDescriptionForScaffold(name);
1449
2097
  }
@@ -1451,6 +2099,21 @@ function fixWebManifestName(appDir, name, templateName) {
1451
2099
  }
1452
2100
  catch { }
1453
2101
  }
2102
+ function replaceSourceIdentityText(value, sourceIdentity, appName, appTitle) {
2103
+ const unscopedSourceName = sourceIdentity.appName.split("/").pop() ?? sourceIdentity.appName;
2104
+ const replacements = new Map([
2105
+ [sourceIdentity.appTitle, appTitle],
2106
+ [sourceIdentity.appName, appName],
2107
+ [unscopedSourceName, appName],
2108
+ ]);
2109
+ const sourceValues = [...replacements.keys()]
2110
+ .filter(Boolean)
2111
+ .sort((a, b) => b.length - a.length);
2112
+ if (sourceValues.length === 0)
2113
+ return value;
2114
+ const pattern = new RegExp(sourceValues.map(escapeRegExp).join("|"), "g");
2115
+ return value.replace(pattern, (match) => replacements.get(match) ?? match);
2116
+ }
1454
2117
  function getCoreDependencyVersion() {
1455
2118
  if (process.env.AGENT_NATIVE_CREATE_USE_LOCAL_CORE === "1") {
1456
2119
  const localCore = findLocalPackage("core");
@@ -1719,12 +2382,25 @@ function rewriteNetlifyToml(appDir, appName, mode) {
1719
2382
  }
1720
2383
  catch { }
1721
2384
  }
1722
- function rewriteAgentChatAppId(appDir, appName, templateName) {
2385
+ function applyScaffoldIdentity(appDir, appName, templateName, sourceIdentity) {
2386
+ rewriteTrackingAppId(appDir, appName, templateName, sourceIdentity);
2387
+ rewriteAgentChatAppId(appDir, appName, templateName, sourceIdentity);
2388
+ rewriteAppConfigIdentity(appDir, appName, sourceIdentity);
2389
+ }
2390
+ function rewriteAgentChatAppId(appDir, appName, templateName, sourceIdentity) {
1723
2391
  const pluginPath = path.join(appDir, "server", "plugins", "agent-chat.ts");
1724
2392
  if (!fs.existsSync(pluginPath))
1725
2393
  return;
1726
2394
  try {
1727
2395
  const content = fs.readFileSync(pluginPath, "utf-8");
2396
+ if (sourceIdentity) {
2397
+ const next = content
2398
+ .replace(/(createAgentChatPlugin\(\{[\s\S]*?\bappId:\s*)(["'])[^"']+\2/, (_match, prefix, quote) => `${prefix}${quote}${appName}${quote}`)
2399
+ .replace(/(const\s+options\s*=\s*\{[\s\S]*?\bappId:\s*)(["'])[^"']+\2/, (_match, prefix, quote) => `${prefix}${quote}${appName}${quote}`);
2400
+ if (next !== content)
2401
+ fs.writeFileSync(pluginPath, next);
2402
+ return;
2403
+ }
1728
2404
  const sourceAppIds = ["chat", "starter"];
1729
2405
  if (templateName && templateName !== appName) {
1730
2406
  sourceAppIds.push(templateName);
@@ -1739,13 +2415,19 @@ function rewriteAgentChatAppId(appDir, appName, templateName) {
1739
2415
  }
1740
2416
  catch { }
1741
2417
  }
1742
- function rewriteTrackingAppId(appDir, appName, templateName) {
2418
+ function rewriteTrackingAppId(appDir, appName, templateName, sourceIdentity) {
1743
2419
  const rootPath = path.join(appDir, "app", "root.tsx");
1744
2420
  if (!fs.existsSync(rootPath))
1745
2421
  return;
1746
2422
  try {
1747
2423
  const content = fs.readFileSync(rootPath, "utf-8");
1748
2424
  const trackedTemplateName = trackingTemplateName(templateName);
2425
+ if (sourceIdentity) {
2426
+ const next = content.replace(/(configureTracking\(\{[\s\S]*?\bapp:\s*)(["'])[^"']+\2/, (_match, prefix, quote) => `${prefix}${quote}${appName}${quote}`);
2427
+ if (next !== content)
2428
+ fs.writeFileSync(rootPath, next);
2429
+ return;
2430
+ }
1749
2431
  const sourceAppIds = ["agent-native-[^\"']+", "\\{\\{APP_NAME\\}\\}"];
1750
2432
  if (templateName && templateName !== appName) {
1751
2433
  sourceAppIds.push(escapeRegExp(templateName));
@@ -1768,6 +2450,23 @@ function rewriteTrackingAppId(appDir, appName, templateName) {
1768
2450
  }
1769
2451
  catch { }
1770
2452
  }
2453
+ function rewriteAppConfigIdentity(appDir, appName, sourceIdentity) {
2454
+ if (!sourceIdentity)
2455
+ return;
2456
+ const configPath = path.join(appDir, "app", "lib", "app-config.ts");
2457
+ if (!fs.existsSync(configPath))
2458
+ return;
2459
+ try {
2460
+ const content = fs.readFileSync(configPath, "utf-8");
2461
+ const appTitle = appTitleForScaffold(appName);
2462
+ const next = content
2463
+ .replace(/(const\s+rawAppName\s*=\s*)(["'])[^"']*\2/, (_match, prefix, quote) => `${prefix}${quote}${appName}${quote}`)
2464
+ .replace(/(const\s+rawAppTitle\s*=\s*)(["'])[^"']*\2/, (_match, prefix, quote) => `${prefix}${quote}${appTitle}${quote}`);
2465
+ if (next !== content)
2466
+ fs.writeFileSync(configPath, next);
2467
+ }
2468
+ catch { }
2469
+ }
1771
2470
  function escapeRegExp(value) {
1772
2471
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1773
2472
  }