@agent-native/core 0.59.1 → 0.61.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 (201) hide show
  1. package/dist/a2a/index.d.ts +2 -0
  2. package/dist/a2a/index.d.ts.map +1 -1
  3. package/dist/a2a/index.js +1 -0
  4. package/dist/a2a/index.js.map +1 -1
  5. package/dist/a2a/invoke.d.ts +63 -0
  6. package/dist/a2a/invoke.d.ts.map +1 -0
  7. package/dist/a2a/invoke.js +157 -0
  8. package/dist/a2a/invoke.js.map +1 -0
  9. package/dist/agent/run-store.d.ts +15 -0
  10. package/dist/agent/run-store.d.ts.map +1 -1
  11. package/dist/agent/run-store.js +28 -0
  12. package/dist/agent/run-store.js.map +1 -1
  13. package/dist/chat-threads/store.d.ts +21 -0
  14. package/dist/chat-threads/store.d.ts.map +1 -1
  15. package/dist/chat-threads/store.js +128 -0
  16. package/dist/chat-threads/store.js.map +1 -1
  17. package/dist/cli/agent.d.ts +23 -0
  18. package/dist/cli/agent.d.ts.map +1 -0
  19. package/dist/cli/agent.js +300 -0
  20. package/dist/cli/agent.js.map +1 -0
  21. package/dist/cli/agents.d.ts +14 -0
  22. package/dist/cli/agents.d.ts.map +1 -0
  23. package/dist/cli/agents.js +95 -0
  24. package/dist/cli/agents.js.map +1 -0
  25. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  26. package/dist/cli/code-agent-executor.js +264 -2
  27. package/dist/cli/code-agent-executor.js.map +1 -1
  28. package/dist/cli/create.d.ts +3 -2
  29. package/dist/cli/create.d.ts.map +1 -1
  30. package/dist/cli/create.js +154 -83
  31. package/dist/cli/create.js.map +1 -1
  32. package/dist/cli/index.js +50 -2
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/invoke.d.ts +26 -0
  35. package/dist/cli/invoke.d.ts.map +1 -0
  36. package/dist/cli/invoke.js +227 -0
  37. package/dist/cli/invoke.js.map +1 -0
  38. package/dist/cli/templates-meta.d.ts +1 -1
  39. package/dist/cli/templates-meta.d.ts.map +1 -1
  40. package/dist/cli/templates-meta.js +9 -8
  41. package/dist/cli/templates-meta.js.map +1 -1
  42. package/dist/cli/workspacify.d.ts +1 -1
  43. package/dist/cli/workspacify.d.ts.map +1 -1
  44. package/dist/cli/workspacify.js +6 -6
  45. package/dist/cli/workspacify.js.map +1 -1
  46. package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
  47. package/dist/client/NewWorkspaceAppFlow.js +5 -4
  48. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  49. package/dist/client/blocks/library/diagram.d.ts.map +1 -1
  50. package/dist/client/blocks/library/diagram.js +23 -17
  51. package/dist/client/blocks/library/diagram.js.map +1 -1
  52. package/dist/client/blocks/types.d.ts +2 -0
  53. package/dist/client/blocks/types.d.ts.map +1 -1
  54. package/dist/client/blocks/types.js.map +1 -1
  55. package/dist/client/chat/index.d.ts +1 -1
  56. package/dist/client/chat/index.d.ts.map +1 -1
  57. package/dist/client/chat/index.js.map +1 -1
  58. package/dist/client/index.d.ts +1 -1
  59. package/dist/client/index.d.ts.map +1 -1
  60. package/dist/client/index.js.map +1 -1
  61. package/dist/client/settings/useBuilderStatus.d.ts +10 -0
  62. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  63. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  64. package/dist/client/use-chat-threads.d.ts +13 -0
  65. package/dist/client/use-chat-threads.d.ts.map +1 -1
  66. package/dist/client/use-chat-threads.js +41 -0
  67. package/dist/client/use-chat-threads.js.map +1 -1
  68. package/dist/integrations/plugin.d.ts.map +1 -1
  69. package/dist/integrations/plugin.js +2 -2
  70. package/dist/integrations/plugin.js.map +1 -1
  71. package/dist/onboarding/default-steps.d.ts.map +1 -1
  72. package/dist/onboarding/default-steps.js +102 -0
  73. package/dist/onboarding/default-steps.js.map +1 -1
  74. package/dist/provider-api/actions/github-repo-files.d.ts +84 -0
  75. package/dist/provider-api/actions/github-repo-files.d.ts.map +1 -0
  76. package/dist/provider-api/actions/github-repo-files.js +213 -0
  77. package/dist/provider-api/actions/github-repo-files.js.map +1 -0
  78. package/dist/provider-api/github-repo.d.ts +11 -0
  79. package/dist/provider-api/github-repo.d.ts.map +1 -0
  80. package/dist/provider-api/github-repo.js +553 -0
  81. package/dist/provider-api/github-repo.js.map +1 -0
  82. package/dist/provider-api/index.d.ts +184 -11
  83. package/dist/provider-api/index.d.ts.map +1 -1
  84. package/dist/provider-api/index.js +519 -0
  85. package/dist/provider-api/index.js.map +1 -1
  86. package/dist/scripts/docs/search.d.ts.map +1 -1
  87. package/dist/scripts/docs/search.js +38 -13
  88. package/dist/scripts/docs/search.js.map +1 -1
  89. package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
  90. package/dist/secrets/register-framework-secrets.js +11 -0
  91. package/dist/secrets/register-framework-secrets.js.map +1 -1
  92. package/dist/server/agent-chat-plugin.d.ts +32 -0
  93. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  94. package/dist/server/agent-chat-plugin.js +297 -2
  95. package/dist/server/agent-chat-plugin.js.map +1 -1
  96. package/dist/server/auth-marketing.d.ts.map +1 -1
  97. package/dist/server/auth-marketing.js +17 -7
  98. package/dist/server/auth-marketing.js.map +1 -1
  99. package/dist/server/auth.d.ts.map +1 -1
  100. package/dist/server/auth.js +6 -0
  101. package/dist/server/auth.js.map +1 -1
  102. package/dist/server/builder-browser.d.ts +11 -0
  103. package/dist/server/builder-browser.d.ts.map +1 -1
  104. package/dist/server/builder-browser.js.map +1 -1
  105. package/dist/server/builder-space.d.ts +47 -0
  106. package/dist/server/builder-space.d.ts.map +1 -0
  107. package/dist/server/builder-space.js +142 -0
  108. package/dist/server/builder-space.js.map +1 -0
  109. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  110. package/dist/server/core-routes-plugin.js +39 -98
  111. package/dist/server/core-routes-plugin.js.map +1 -1
  112. package/dist/server/credential-provider.d.ts.map +1 -1
  113. package/dist/server/credential-provider.js +29 -6
  114. package/dist/server/credential-provider.js.map +1 -1
  115. package/dist/styles/blocks.css +30 -8
  116. package/dist/styles/rich-markdown-editor.css +10 -4
  117. package/dist/templates/{starter-shell-sync.spec.ts → chat-shell-sync.spec.ts} +21 -21
  118. package/dist/templates/default/.agents/skills/actions/SKILL.md +5 -5
  119. package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +63 -0
  120. package/dist/templates/default/AGENTS.md +22 -1
  121. package/dist/templates/default/actions/hello.ts +1 -1
  122. package/dist/templates/default/actions/navigate.ts +1 -1
  123. package/dist/templates/default/actions/view-screen.ts +1 -1
  124. package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +63 -0
  125. package/dist/templates/headless/.env.example +4 -0
  126. package/dist/templates/headless/.prettierrc +5 -0
  127. package/dist/templates/headless/AGENTS.md +58 -0
  128. package/dist/templates/headless/DEVELOPING.md +22 -0
  129. package/dist/templates/headless/_gitignore +36 -0
  130. package/dist/templates/headless/actions/hello.ts +14 -0
  131. package/dist/templates/headless/actions/run.ts +3 -0
  132. package/dist/templates/headless/package.json +22 -0
  133. package/dist/templates/headless/tsconfig.json +7 -0
  134. package/dist/templates/ui-primitives-sync.spec.ts +2 -2
  135. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -5
  136. package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +63 -0
  137. package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +93 -0
  138. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +1 -1
  139. package/dist/templates/workspace-core/AGENTS.md +20 -3
  140. package/dist/templates/workspace-core/src/server/index.ts +1 -1
  141. package/dist/templates/workspace-root/AGENTS.md +25 -5
  142. package/dist/templates/workspace-root/README.md +7 -7
  143. package/dist/triggers/dispatcher.d.ts +2 -3
  144. package/dist/triggers/dispatcher.d.ts.map +1 -1
  145. package/dist/triggers/dispatcher.js +2 -3
  146. package/dist/triggers/dispatcher.js.map +1 -1
  147. package/dist/triggers/routes.d.ts +38 -0
  148. package/dist/triggers/routes.d.ts.map +1 -0
  149. package/dist/triggers/routes.js +202 -0
  150. package/dist/triggers/routes.js.map +1 -0
  151. package/docs/AGENTS.md +57 -0
  152. package/docs/SKILL.md +40 -0
  153. package/docs/content/a2a-protocol.md +1 -1
  154. package/docs/content/actions.md +48 -8
  155. package/docs/content/agent-surfaces.md +76 -14
  156. package/docs/content/cli-adapters.md +1 -1
  157. package/docs/content/cloneable-saas.md +5 -4
  158. package/docs/content/code-agents-ui.md +1 -1
  159. package/docs/content/components.md +1 -1
  160. package/docs/content/context-awareness.md +1 -1
  161. package/docs/content/creating-templates.md +9 -7
  162. package/docs/content/drop-in-agent.md +1 -1
  163. package/docs/content/faq.md +6 -4
  164. package/docs/content/getting-started.md +63 -73
  165. package/docs/content/key-concepts.md +24 -24
  166. package/docs/content/native-chat-ui.md +4 -4
  167. package/docs/content/pure-agent-apps.md +34 -10
  168. package/docs/content/security.md +1 -1
  169. package/docs/content/server.md +1 -1
  170. package/docs/content/template-chat.md +85 -0
  171. package/docs/content/template-dispatch.md +1 -1
  172. package/docs/content/tracking.md +1 -1
  173. package/docs/content/what-is-agent-native.md +7 -6
  174. package/package.json +10 -1
  175. package/src/templates/{starter-shell-sync.spec.ts → chat-shell-sync.spec.ts} +21 -21
  176. package/src/templates/default/.agents/skills/actions/SKILL.md +5 -5
  177. package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +63 -0
  178. package/src/templates/default/AGENTS.md +22 -1
  179. package/src/templates/default/actions/hello.ts +1 -1
  180. package/src/templates/default/actions/navigate.ts +1 -1
  181. package/src/templates/default/actions/view-screen.ts +1 -1
  182. package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +63 -0
  183. package/src/templates/headless/.env.example +4 -0
  184. package/src/templates/headless/.prettierrc +5 -0
  185. package/src/templates/headless/AGENTS.md +58 -0
  186. package/src/templates/headless/DEVELOPING.md +22 -0
  187. package/src/templates/headless/_gitignore +36 -0
  188. package/src/templates/headless/actions/hello.ts +14 -0
  189. package/src/templates/headless/actions/run.ts +3 -0
  190. package/src/templates/headless/package.json +22 -0
  191. package/src/templates/headless/tsconfig.json +7 -0
  192. package/src/templates/ui-primitives-sync.spec.ts +2 -2
  193. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -5
  194. package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +63 -0
  195. package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +93 -0
  196. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +1 -1
  197. package/src/templates/workspace-core/AGENTS.md +20 -3
  198. package/src/templates/workspace-core/src/server/index.ts +1 -1
  199. package/src/templates/workspace-root/AGENTS.md +25 -5
  200. package/src/templates/workspace-root/README.md +7 -7
  201. package/docs/content/template-starter.md +0 -78
@@ -33,12 +33,11 @@ export class ValidationError extends Error {
33
33
  }
34
34
  }
35
35
  /**
36
- * Move "starter" to the top of the list so it lines up with clack's default
37
- * highlight on the first option — otherwise users have to scroll to see that
38
- * Starter is pre-selected.
36
+ * Move the primitive-first and chat on-ramps to the top of the list so they
37
+ * line up with clack's default highlight.
39
38
  */
40
- function starterFirst(templates) {
41
- return moveTemplatesToFront(templates, ["starter"]);
39
+ function onRampFirst(templates) {
40
+ return moveTemplatesToFront(templates, ["headless", "chat"]);
42
41
  }
43
42
  function moveTemplatesToFront(templates, preferredNames) {
44
43
  const preferred = preferredNames
@@ -49,11 +48,11 @@ function moveTemplatesToFront(templates, preferredNames) {
49
48
  const preferredSet = new Set(preferred.map((t) => t.name));
50
49
  return [...preferred, ...templates.filter((t) => !preferredSet.has(t.name))];
51
50
  }
52
- /** Blank scaffold option appended to every picker. */
53
- const BLANK_OPTION = {
54
- name: "blank",
55
- label: "Blank",
56
- hint: "Empty starter build from scratch",
51
+ /** Primitive-first scaffold option appended to standalone pickers. */
52
+ const HEADLESS_OPTION = {
53
+ name: "headless",
54
+ label: "Headless",
55
+ hint: "Action-first app with one hello primitive and no UI shell",
57
56
  };
58
57
  /**
59
58
  * Main entry for `agent-native create [name]`.
@@ -83,6 +82,10 @@ export async function createApp(name, opts) {
83
82
  // Use `--template a,b` or pass no --template to opt into the workspace
84
83
  // flow with the multi-select picker.
85
84
  const parsed = parseTemplateList(opts?.template);
85
+ if (parsed.includes("headless") && parsed.length > 1) {
86
+ clack.cancel("The headless scaffold is standalone-only. Use `agent-native create my-app --headless`, or use the Chat template when adding a UI app to a workspace.");
87
+ process.exit(1);
88
+ }
86
89
  if (parsed.length === 1) {
87
90
  await createStandaloneApp(name, opts, clack);
88
91
  return;
@@ -102,8 +105,8 @@ async function createWorkspaceInteractive(name, opts, clack) {
102
105
  "container — it isn't an app itself. Inside it you pick one or more apps",
103
106
  "(below), and each app gets its own route, agent, and UI. Apps in the",
104
107
  "same workspace share auth, database, and the agent chat. Add more apps",
105
- "later with `npx @agent-native/core@latest add-app`. Starter is a minimal scaffold —",
106
- "useful as a blank app to build from scratch alongside the others.",
108
+ "later with `npx @agent-native/core@latest add-app`. Chat is the UI on-ramp",
109
+ "for a minimal chat-first app with the browser shell already wired.",
107
110
  "Dispatch is always included as the workspace control plane —",
108
111
  "it owns shared secrets, messaging, approvals, and cross-app routing.",
109
112
  ].join("\n"), "About workspaces");
@@ -114,8 +117,8 @@ async function createWorkspaceInteractive(name, opts, clack) {
114
117
  const optionalPicks = preselected.length > 0
115
118
  ? preselected.filter((t) => t !== "dispatch")
116
119
  : await promptTemplatePicker(preselected, clack, {
117
- defaultTemplates: ["starter"],
118
- preferredFirst: ["starter"],
120
+ defaultTemplates: ["chat"],
121
+ preferredFirst: ["chat"],
119
122
  excludeNames: ["dispatch"],
120
123
  });
121
124
  const templates = ["dispatch", ...optionalPicks];
@@ -126,37 +129,49 @@ async function createWorkspaceInteractive(name, opts, clack) {
126
129
  }
127
130
  const s = clack.spinner();
128
131
  s.start(`Scaffolding workspace "${name}"...`);
132
+ const appNames = new Set();
133
+ const scaffoldedApps = [];
129
134
  try {
130
135
  await scaffoldWorkspaceRoot(targetDir, name);
131
136
  const workspaceCoreName = `@${name}/shared`;
132
137
  for (let i = 0; i < templates.length; i++) {
133
- const t = templates[i];
138
+ const templateName = templates[i];
139
+ const appName = workspaceAppNameForTemplateSelection(templateName);
140
+ validateWorkspaceAppName(appName, clack, {
141
+ allowDispatch: appName === "dispatch" && templateName === "dispatch",
142
+ });
143
+ if (appNames.has(appName)) {
144
+ clack.cancel(`Workspace app "${appName}" is selected more than once. Choose unique app templates or app names.`);
145
+ process.exit(1);
146
+ }
147
+ appNames.add(appName);
148
+ scaffoldedApps.push(appName);
134
149
  // Distinguish download vs local copy in the spinner so a multi-second
135
150
  // GitHub fetch doesn't look like a frozen "Scaffolding..." message.
136
151
  // Mirrors the local-vs-remote decision inside scaffoldAppTemplate.
137
- const willDownload = t !== "blank" && t.startsWith("github:")
152
+ const willDownload = templateName !== "headless" && templateName.startsWith("github:")
138
153
  ? true
139
- : !findLocalTemplate(normalizeTemplateName(t));
154
+ : !findLocalTemplate(normalizeTemplateName(templateName));
140
155
  s.message(willDownload
141
- ? `Downloading ${titleCase(t)} template (${i + 1}/${templates.length})...`
142
- : `Scaffolding ${titleCase(t)} (${i + 1}/${templates.length})...`);
143
- const appDir = path.join(targetDir, "apps", t);
144
- await scaffoldAppTemplate(appDir, t);
145
- s.message(`Configuring ${titleCase(t)} (${i + 1}/${templates.length})...`);
146
- replacePlaceholders(appDir, t, appTitleForScaffold(t, t), name);
147
- rewriteTrackingAppId(appDir, t, t);
156
+ ? `Downloading ${titleCase(appName)} template (${i + 1}/${templates.length})...`
157
+ : `Scaffolding ${titleCase(appName)} (${i + 1}/${templates.length})...`);
158
+ const appDir = path.join(targetDir, "apps", appName);
159
+ await scaffoldAppTemplate(appDir, templateName);
160
+ s.message(`Configuring ${titleCase(appName)} (${i + 1}/${templates.length})...`);
161
+ replacePlaceholders(appDir, appName, appTitleForScaffold(appName), name);
162
+ rewriteTrackingAppId(appDir, appName, templateName);
148
163
  workspacifyApp({
149
164
  appDir,
150
- appName: t,
151
- templateName: t,
165
+ appName,
166
+ templateName,
152
167
  workspaceRoot: targetDir,
153
168
  workspaceCoreName,
154
169
  coreDependencyVersion: getCoreDependencyVersion(),
155
170
  dispatchDependencyVersion: getDispatchDependencyVersion(),
156
171
  });
157
- fixPackageJsonName(appDir, t);
158
- fixWebManifestName(appDir, t, t);
159
- rewriteNetlifyToml(appDir, t, "workspace");
172
+ fixPackageJsonName(appDir, appName, templateName);
173
+ fixWebManifestName(appDir, appName, templateName);
174
+ rewriteNetlifyToml(appDir, appName, "workspace");
160
175
  renameGitignore(appDir);
161
176
  // Each app owns its own .claude / .agents symlinks.
162
177
  setupAgentSymlinks(appDir);
@@ -180,8 +195,7 @@ async function createWorkspaceInteractive(name, opts, clack) {
180
195
  // makes the structure concrete.
181
196
  const treeLines = [
182
197
  ` ${name}/ ← your workspace`,
183
- ...templates.map((t, i) => ` ${i === templates.length - 1 ? "└─" : "├─"} apps/${t}/`.padEnd(30) +
184
- ` ← app`),
198
+ ...scaffoldedApps.map((appName, i) => ` ${i === scaffoldedApps.length - 1 ? "└─" : "├─"} apps/${appName}/`.padEnd(30) + ` ← app`),
185
199
  ];
186
200
  const dispatchNextStep = [
187
201
  `Once running, open Dispatch — you'll see "Workspace: ${titleCase(name)}"`,
@@ -215,6 +229,23 @@ async function createWorkspaceInteractive(name, opts, clack) {
215
229
  `Deploy the whole workspace: pnpm exec agent-native deploy`,
216
230
  ].join("\n"));
217
231
  }
232
+ function workspaceAppNameForTemplateSelection(templateName) {
233
+ const normalized = normalizeTemplateName(templateName);
234
+ if (!normalized.startsWith("github:"))
235
+ return templateName;
236
+ const repo = normalized.slice("github:".length).trim();
237
+ const repoName = repo.split("/").filter(Boolean).pop() ?? "app";
238
+ let appName = repoName
239
+ .toLowerCase()
240
+ .replace(/[^a-z0-9-]+/g, "-")
241
+ .replace(/-+/g, "-")
242
+ .replace(/^-+|-+$/g, "");
243
+ if (!appName)
244
+ appName = "app";
245
+ if (!/^[a-z]/.test(appName))
246
+ appName = `app-${appName}`;
247
+ return appName;
248
+ }
218
249
  /**
219
250
  * Detect whether pnpm is on PATH. End-user machines often have npm/yarn but
220
251
  * not pnpm; the workspace scaffold uses pnpm workspaces, so we surface a
@@ -321,6 +352,10 @@ export async function addAppToWorkspace(name, opts) {
321
352
  const installed = listInstalledApps(workspace.workspaceRoot);
322
353
  // Non-interactive path: name + single --template
323
354
  const preselected = parseTemplateList(opts?.template);
355
+ if (preselected.includes("headless")) {
356
+ clack.cancel("The headless scaffold is standalone-only. Use `agent-native create my-app --headless` outside a workspace, or use the Chat template when adding a UI app to a workspace.");
357
+ process.exit(1);
358
+ }
324
359
  if (name && preselected.length === 1) {
325
360
  const tpl = preselected[0];
326
361
  await scaffoldOneAppIntoWorkspace(workspace, name, tpl, clack);
@@ -331,7 +366,7 @@ export async function addAppToWorkspace(name, opts) {
331
366
  excludeNames: installed,
332
367
  message: "Which apps do you want to add?",
333
368
  defaultTemplates: hasDispatch ? undefined : ["dispatch"],
334
- preferredFirst: hasDispatch ? ["starter"] : ["dispatch", "starter"],
369
+ preferredFirst: hasDispatch ? ["chat"] : ["dispatch", "chat"],
335
370
  recommendedNames: hasDispatch ? [] : ["dispatch"],
336
371
  });
337
372
  if (templates.length === 0) {
@@ -359,7 +394,7 @@ async function scaffoldOneAppIntoWorkspace(workspace, appName, templateName, cla
359
394
  s.start(`Working... no action needed. Scaffolding apps/${appName} from ${templateName}.`);
360
395
  try {
361
396
  await scaffoldAppTemplate(appDir, templateName);
362
- replacePlaceholders(appDir, appName, appTitleForScaffold(appName, templateName), path.basename(workspace.workspaceRoot));
397
+ replacePlaceholders(appDir, appName, appTitleForScaffold(appName), path.basename(workspace.workspaceRoot));
363
398
  rewriteTrackingAppId(appDir, appName, templateName);
364
399
  workspacifyApp({
365
400
  appDir,
@@ -410,16 +445,18 @@ async function createStandaloneApp(name, opts, clack) {
410
445
  const picked = await clack.select({
411
446
  message: "Which template would you like to use?",
412
447
  options: [
413
- ...starterFirst(coreTemplates()).map((t) => ({
448
+ {
449
+ value: HEADLESS_OPTION.name,
450
+ label: HEADLESS_OPTION.label,
451
+ hint: HEADLESS_OPTION.hint,
452
+ },
453
+ ...onRampFirst(coreTemplates())
454
+ .filter((t) => t.name !== HEADLESS_OPTION.name)
455
+ .map((t) => ({
414
456
  value: t.name,
415
457
  label: t.label,
416
458
  hint: t.hint,
417
459
  })),
418
- {
419
- value: BLANK_OPTION.name,
420
- label: BLANK_OPTION.label,
421
- hint: BLANK_OPTION.hint,
422
- },
423
460
  ],
424
461
  });
425
462
  if (clack.isCancel(picked)) {
@@ -430,7 +467,9 @@ async function createStandaloneApp(name, opts, clack) {
430
467
  }
431
468
  template = normalizeTemplateName(template);
432
469
  const s = clack.spinner();
433
- s.start(`Downloading the ${template} template from GitHub…`);
470
+ s.start(template === "headless"
471
+ ? "Scaffolding the headless action app..."
472
+ : `Downloading the ${template} template from GitHub...`);
434
473
  try {
435
474
  await scaffoldAppTemplate(targetDir, template);
436
475
  s.message(`Setting up ${name}…`);
@@ -444,7 +483,21 @@ async function createStandaloneApp(name, opts, clack) {
444
483
  process.exit(1);
445
484
  }
446
485
  tryGitInit(targetDir);
447
- clack.outro(`Done! Next steps:\n\n cd ${name}\n pnpm install\n pnpm dev`);
486
+ if (template === "headless") {
487
+ clack.outro([
488
+ "Done! Next steps:",
489
+ "",
490
+ ` cd ${name}`,
491
+ " pnpm install",
492
+ " pnpm action hello --name Builder",
493
+ ` pnpm agent "Call hello for Builder"`,
494
+ "",
495
+ "Add a UI later by starting from the Chat template; `agent-native add` is reserved for integration blueprints.",
496
+ ].join("\n"));
497
+ }
498
+ else {
499
+ clack.outro(`Done! Next steps:\n\n cd ${name}\n pnpm install\n pnpm dev`);
500
+ }
448
501
  }
449
502
  /**
450
503
  * Remove a partially-scaffolded target directory after a scaffold failure so a
@@ -467,42 +520,48 @@ function cleanupOnFailure(targetDir) {
467
520
  * ───────────────────────────────────────────────────────────────────────── */
468
521
  /**
469
522
  * Scaffold a single app template into `targetDir`. Resolves:
470
- * - "blank" → bundled default template
523
+ * - "headless" / legacy "blank" → bundled action-first template
471
524
  * - "github:user/repo" → download the whole repo
472
525
  * - first-party template name → download that subdir from BuilderIO/agent-native
473
526
  */
474
527
  async function scaffoldAppTemplate(targetDir, template) {
475
528
  fs.mkdirSync(path.dirname(targetDir), { recursive: true });
476
- if (template === "blank") {
529
+ // Normalize legacy / renamed aliases.
530
+ let resolved = normalizeTemplateName(template);
531
+ if (resolved === "headless") {
477
532
  const packageRoot = path.resolve(__dirname, "../..");
478
- const defaultDir = path.join(packageRoot, "src/templates/default");
479
- if (!fs.existsSync(defaultDir)) {
480
- throw new Error(`Default template not found at ${defaultDir}. Is the package installed correctly?`);
533
+ const headlessDir = path.join(packageRoot, "src/templates/headless");
534
+ if (!fs.existsSync(headlessDir)) {
535
+ throw new Error(`Headless template not found at ${headlessDir}. Is the package installed correctly?`);
481
536
  }
482
- copyDir(defaultDir, targetDir);
537
+ copyDir(headlessDir, targetDir);
483
538
  return;
484
539
  }
485
- // Normalize legacy / renamed aliases.
486
- let resolved = normalizeTemplateName(template);
487
540
  if (resolved.startsWith("github:")) {
488
541
  const repo = resolved.slice("github:".length);
489
542
  await downloadGitHubRepo(repo, targetDir);
490
543
  return;
491
544
  }
492
- if (!allTemplateNames().includes(resolved)) {
545
+ if (!getTemplate(resolved)) {
493
546
  throw new Error(`Unknown template "${template}". Known: ${allTemplateNames().join(", ")} — or use github:user/repo for community templates.`);
494
547
  }
495
548
  // If running from the framework monorepo with a local templates/ dir, use
496
549
  // that. Otherwise download from GitHub. This keeps `agent-native create`
497
550
  // fast during framework development.
498
- const localTemplate = findLocalTemplate(resolved);
551
+ const sourceTemplate = templateSourceName(resolved);
552
+ const localTemplate = findLocalTemplate(sourceTemplate);
499
553
  if (localTemplate) {
500
554
  copyDir(localTemplate, targetDir);
501
555
  }
502
556
  else {
503
- await downloadGitHubSubdir(REPO, `${TEMPLATES_DIR}/${resolved}`, targetDir);
557
+ await downloadGitHubSubdir(REPO, `${TEMPLATES_DIR}/${sourceTemplate}`, targetDir);
504
558
  }
505
559
  }
560
+ function templateSourceName(name) {
561
+ if (name === "starter")
562
+ return "chat";
563
+ return name;
564
+ }
506
565
  /**
507
566
  * When developing the framework itself, prefer the sibling templates/<name>
508
567
  * directory. Returns undefined when running as a published package.
@@ -522,6 +581,8 @@ function findLocalTemplate(name) {
522
581
  return undefined;
523
582
  }
524
583
  function normalizeTemplateName(template) {
584
+ if (template === "blank")
585
+ return "headless";
525
586
  if (template === "video")
526
587
  return "videos";
527
588
  if (template === "image" || template === "images" || template === "asset") {
@@ -633,7 +694,7 @@ async function scaffoldRequiredPackages(templateNames, workspaceRoot) {
633
694
  * workspace:* deps, set up agent symlinks, etc.
634
695
  */
635
696
  function postProcessStandalone(name, targetDir, templateName) {
636
- const appTitle = appTitleForScaffold(name, templateName);
697
+ const appTitle = appTitleForScaffold(name);
637
698
  replacePlaceholders(targetDir, name, appTitle);
638
699
  rewriteTrackingAppId(targetDir, name, templateName);
639
700
  fixPackageJsonName(targetDir, name, templateName);
@@ -648,7 +709,7 @@ function postProcessStandalone(name, targetDir, templateName) {
648
709
  }
649
710
  renameGitignore(targetDir);
650
711
  // No monorepo-only files to drop for standalone scaffolds.
651
- // DEVELOPING.md is intentionally kept: it documents `pnpm dev`,
712
+ // DEVELOPING.md is intentionally kept: it documents local run commands,
652
713
  // DATABASE_URL defaults, and other local-run instructions that are equally
653
714
  // valid for standalone apps.
654
715
  // Resolve workspace:* and catalog: deps for standalone projects.
@@ -709,17 +770,20 @@ function postProcessStandalone(name, targetDir, templateName) {
709
770
  const existing = fs.existsSync(wsPath)
710
771
  ? fs.readFileSync(wsPath, "utf-8")
711
772
  : "";
712
- const updated = mergeWorkspaceYamlSections(existing, {
713
- overrides: {
714
- '"@assistant-ui/store"': '">=0.2.9 <0.2.14"',
715
- '"@assistant-ui/tap"': '"^0.5.14"',
716
- },
773
+ const sections = {
717
774
  allowBuilds: {
718
775
  "better-sqlite3": "true",
719
776
  esbuild: "true",
720
777
  "node-pty": "true",
721
778
  },
722
- });
779
+ };
780
+ if (templateName !== "headless") {
781
+ sections.overrides = {
782
+ '"@assistant-ui/store"': '">=0.2.9 <0.2.14"',
783
+ '"@assistant-ui/tap"': '"^0.5.14"',
784
+ };
785
+ }
786
+ const updated = mergeWorkspaceYamlSections(existing, sections);
723
787
  if (updated !== existing) {
724
788
  fs.writeFileSync(wsPath, updated);
725
789
  }
@@ -762,7 +826,7 @@ async function promptTemplatePicker(preselected, clack, opts) {
762
826
  const excluded = new Set(opts?.excludeNames ?? []);
763
827
  const orderedTemplates = opts?.preferredFirst
764
828
  ? moveTemplatesToFront(coreTemplates(), opts.preferredFirst)
765
- : starterFirst(coreTemplates());
829
+ : onRampFirst(coreTemplates());
766
830
  const recommendedNames = new Set(opts?.recommendedNames ?? []);
767
831
  const options = orderedTemplates
768
832
  .filter((t) => !excluded.has(t.name))
@@ -780,13 +844,13 @@ async function promptTemplatePicker(preselected, clack, opts) {
780
844
  if (options.length === 0)
781
845
  return [];
782
846
  // Default pre-selection: what the user passed via --template, falling
783
- // back to caller defaults, then to "starter" when available.
847
+ // back to caller defaults, then to "chat" when available.
784
848
  const defaults = preselected.length > 0
785
849
  ? preselected.filter((p) => options.some((o) => o.value === p))
786
850
  : opts?.defaultTemplates
787
851
  ? opts.defaultTemplates.filter((p) => options.some((o) => o.value === p))
788
- : options.some((o) => o.value === "starter")
789
- ? ["starter"]
852
+ : options.some((o) => o.value === "chat")
853
+ ? ["chat"]
790
854
  : [];
791
855
  const baseMessage = opts?.message ?? "Which apps would you like to include?";
792
856
  const result = await clack.multiselect({
@@ -849,7 +913,7 @@ export function detectWorkspace(startDir) {
849
913
  }
850
914
  export { parseWorkspaceScope };
851
915
  /** @internal — exported for E2E tests */
852
- 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, getGitHubTemplateRef as _getGitHubTemplateRef, getGitHubTemplateRefCandidates as _getGitHubTemplateRefCandidates, shouldSkipScaffoldEntry as _shouldSkipScaffoldEntry, tarExtractArgs as _tarExtractArgs, };
916
+ 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, getGitHubTemplateRef as _getGitHubTemplateRef, getGitHubTemplateRefCandidates as _getGitHubTemplateRefCandidates, workspaceAppNameForTemplateSelection as _workspaceAppNameForTemplateSelection, shouldSkipScaffoldEntry as _shouldSkipScaffoldEntry, tarExtractArgs as _tarExtractArgs, };
853
917
  /* ─────────────────────────────────────────────────────────────────────────
854
918
  * Download / copy helpers
855
919
  * ───────────────────────────────────────────────────────────────────────── */
@@ -998,16 +1062,17 @@ function titleCase(name) {
998
1062
  .map((w) => (w ? w[0].toUpperCase() + w.slice(1) : w))
999
1063
  .join(" ");
1000
1064
  }
1001
- function appTitleForScaffold(appName, templateName) {
1002
- if (appName === "starter" && (!templateName || templateName === "starter")) {
1003
- return "Blank app";
1004
- }
1065
+ function appTitleForScaffold(appName) {
1005
1066
  return titleCase(appName);
1006
1067
  }
1007
- function defaultPackageDescriptionForScaffold(appName, templateName) {
1008
- const appTitle = appTitleForScaffold(appName, templateName);
1009
- if (appTitle === "Blank app")
1010
- return "Blank agent-native app scaffold.";
1068
+ function isChatOnRampTemplate(templateName) {
1069
+ return templateName === "chat" || templateName === "starter";
1070
+ }
1071
+ function trackingTemplateName(templateName) {
1072
+ return templateName === "starter" ? "chat" : templateName;
1073
+ }
1074
+ function defaultPackageDescriptionForScaffold(appName) {
1075
+ const appTitle = appTitleForScaffold(appName);
1011
1076
  return `Workspace app for ${appTitle}.`;
1012
1077
  }
1013
1078
  function shouldReplaceScaffoldDescription(value) {
@@ -1022,22 +1087,24 @@ function fixPackageJsonName(appDir, name, templateName) {
1022
1087
  try {
1023
1088
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
1024
1089
  pkg.name = name;
1025
- // When the user picked a custom name (e.g. `add-app todo --template=starter`)
1090
+ const appTitle = appTitleForScaffold(name);
1091
+ // When the user picked a custom name (e.g. `add-app todo --template=chat`)
1026
1092
  // the template's displayName would otherwise leak into the workspace apps
1027
1093
  // grid as the new app's label. Overwrite it so the app shows up as "Todo"
1028
1094
  // instead of the source template's branding.
1029
1095
  if (templateName && name !== templateName) {
1030
- pkg.displayName = titleCase(name);
1096
+ pkg.displayName = appTitle;
1031
1097
  }
1032
- if (shouldReplaceScaffoldDescription(pkg.description)) {
1033
- pkg.description = defaultPackageDescriptionForScaffold(name, templateName);
1098
+ if (shouldReplaceScaffoldDescription(pkg.description) ||
1099
+ (isChatOnRampTemplate(templateName) && name !== templateName)) {
1100
+ pkg.description = defaultPackageDescriptionForScaffold(name);
1034
1101
  }
1035
1102
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
1036
1103
  }
1037
1104
  catch { }
1038
1105
  }
1039
1106
  function fixWebManifestName(appDir, name, templateName) {
1040
- if (templateName !== "starter" || name === templateName)
1107
+ if (!isChatOnRampTemplate(templateName) || name === templateName)
1041
1108
  return;
1042
1109
  const manifestPath = path.join(appDir, "public", "manifest.json");
1043
1110
  if (!fs.existsSync(manifestPath))
@@ -1048,8 +1115,8 @@ function fixWebManifestName(appDir, name, templateName) {
1048
1115
  manifest.name = appTitle;
1049
1116
  manifest.short_name = appTitle;
1050
1117
  if (typeof manifest.description !== "string" ||
1051
- /\b(blank app|starter)\b/i.test(manifest.description)) {
1052
- manifest.description = defaultPackageDescriptionForScaffold(name, templateName);
1118
+ /\b(blank app|starter|chat-first)\b/i.test(manifest.description)) {
1119
+ manifest.description = defaultPackageDescriptionForScaffold(name);
1053
1120
  }
1054
1121
  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
1055
1122
  }
@@ -1246,18 +1313,22 @@ function rewriteTrackingAppId(appDir, appName, templateName) {
1246
1313
  return;
1247
1314
  try {
1248
1315
  const content = fs.readFileSync(rootPath, "utf-8");
1316
+ const trackedTemplateName = trackingTemplateName(templateName);
1249
1317
  const sourceAppIds = ["agent-native-[^\"']+", "\\{\\{APP_NAME\\}\\}"];
1250
1318
  if (templateName && templateName !== appName) {
1251
1319
  sourceAppIds.push(escapeRegExp(templateName));
1252
1320
  }
1321
+ if (isChatOnRampTemplate(templateName)) {
1322
+ sourceAppIds.push("starter", "chat");
1323
+ }
1253
1324
  const pattern = new RegExp(`(^\\s*app:\\s*)(["'])(?:${sourceAppIds.join("|")})\\2(\\s*,?)`, "m");
1254
1325
  if (!pattern.test(content))
1255
1326
  return;
1256
1327
  let next = content.replace(pattern, (_match, prefix, quote, suffix) => `${prefix}${quote}${appName}${quote}${suffix}`);
1257
- if (templateName &&
1258
- templateName !== appName &&
1328
+ if (trackedTemplateName &&
1329
+ trackedTemplateName !== appName &&
1259
1330
  !hasTrackingTemplate(next)) {
1260
- next = next.replace(/(^\s*app:\s*["'][^"']+["'],?\s*$)/m, (line) => `${line}\n template: ${JSON.stringify(templateName)},`);
1331
+ next = next.replace(/(^\s*app:\s*["'][^"']+["'],?\s*$)/m, (line) => `${line}\n template: ${JSON.stringify(trackedTemplateName)},`);
1261
1332
  }
1262
1333
  if (next !== content) {
1263
1334
  fs.writeFileSync(rootPath, next);