@agentuity/cli 0.0.101 → 0.0.103

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 (210) hide show
  1. package/AGENTS.md +19 -188
  2. package/bin/cli.ts +21 -14
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +41 -12
  5. package/dist/cli.js.map +1 -1
  6. package/dist/cmd/ai/index.d.ts.map +1 -1
  7. package/dist/cmd/ai/index.js +6 -1
  8. package/dist/cmd/ai/index.js.map +1 -1
  9. package/dist/cmd/ai/prompt/agent.d.ts +7 -0
  10. package/dist/cmd/ai/prompt/agent.d.ts.map +1 -1
  11. package/dist/cmd/ai/prompt/agent.js +12 -323
  12. package/dist/cmd/ai/prompt/agent.js.map +1 -1
  13. package/dist/cmd/ai/prompt/api.d.ts +7 -0
  14. package/dist/cmd/ai/prompt/api.d.ts.map +1 -1
  15. package/dist/cmd/ai/prompt/api.js +12 -260
  16. package/dist/cmd/ai/prompt/api.js.map +1 -1
  17. package/dist/cmd/ai/prompt/version.d.ts +35 -0
  18. package/dist/cmd/ai/prompt/version.d.ts.map +1 -0
  19. package/dist/cmd/ai/prompt/version.js +55 -0
  20. package/dist/cmd/ai/prompt/version.js.map +1 -0
  21. package/dist/cmd/ai/prompt/web.d.ts +7 -0
  22. package/dist/cmd/ai/prompt/web.d.ts.map +1 -1
  23. package/dist/cmd/ai/prompt/web.js +12 -283
  24. package/dist/cmd/ai/prompt/web.js.map +1 -1
  25. package/dist/cmd/ai/skills/generate.d.ts +3 -0
  26. package/dist/cmd/ai/skills/generate.d.ts.map +1 -0
  27. package/dist/cmd/ai/skills/generate.js +65 -0
  28. package/dist/cmd/ai/skills/generate.js.map +1 -0
  29. package/dist/cmd/ai/skills/generator.d.ts +4 -0
  30. package/dist/cmd/ai/skills/generator.d.ts.map +1 -0
  31. package/dist/cmd/ai/skills/generator.js +402 -0
  32. package/dist/cmd/ai/skills/generator.js.map +1 -0
  33. package/dist/cmd/ai/skills/index.d.ts +4 -0
  34. package/dist/cmd/ai/skills/index.d.ts.map +1 -0
  35. package/dist/cmd/ai/skills/index.js +21 -0
  36. package/dist/cmd/ai/skills/index.js.map +1 -0
  37. package/dist/cmd/auth/signup.d.ts.map +1 -1
  38. package/dist/cmd/auth/signup.js +1 -0
  39. package/dist/cmd/auth/signup.js.map +1 -1
  40. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  41. package/dist/cmd/build/entry-generator.js +40 -5
  42. package/dist/cmd/build/entry-generator.js.map +1 -1
  43. package/dist/cmd/build/vite/bun-dev-server.d.ts +7 -1
  44. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  45. package/dist/cmd/build/vite/bun-dev-server.js +30 -26
  46. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  47. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  48. package/dist/cmd/build/vite/metadata-generator.js +58 -7
  49. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  50. package/dist/cmd/build/vite/prompt-generator.d.ts +23 -0
  51. package/dist/cmd/build/vite/prompt-generator.d.ts.map +1 -0
  52. package/dist/cmd/build/vite/prompt-generator.js +123 -0
  53. package/dist/cmd/build/vite/prompt-generator.js.map +1 -0
  54. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  55. package/dist/cmd/build/vite/registry-generator.js +28 -11
  56. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  57. package/dist/cmd/build/vite/server-bundler.d.ts +4 -0
  58. package/dist/cmd/build/vite/server-bundler.d.ts.map +1 -1
  59. package/dist/cmd/build/vite/server-bundler.js +45 -16
  60. package/dist/cmd/build/vite/server-bundler.js.map +1 -1
  61. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  62. package/dist/cmd/build/vite/vite-asset-server-config.js +4 -0
  63. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  64. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  65. package/dist/cmd/build/vite/vite-builder.js +99 -87
  66. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  67. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  68. package/dist/cmd/cloud/deploy.js +80 -27
  69. package/dist/cmd/cloud/deploy.js.map +1 -1
  70. package/dist/cmd/cloud/keyvalue/create-namespace.d.ts.map +1 -1
  71. package/dist/cmd/cloud/keyvalue/create-namespace.js +3 -1
  72. package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
  73. package/dist/cmd/cloud/keyvalue/delete-namespace.d.ts.map +1 -1
  74. package/dist/cmd/cloud/keyvalue/delete-namespace.js +3 -1
  75. package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
  76. package/dist/cmd/cloud/keyvalue/delete.d.ts.map +1 -1
  77. package/dist/cmd/cloud/keyvalue/delete.js +3 -1
  78. package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
  79. package/dist/cmd/cloud/keyvalue/set.d.ts.map +1 -1
  80. package/dist/cmd/cloud/keyvalue/set.js +4 -2
  81. package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
  82. package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
  83. package/dist/cmd/cloud/stream/get.js +2 -13
  84. package/dist/cmd/cloud/stream/get.js.map +1 -1
  85. package/dist/cmd/cloud/vector/delete-namespace.d.ts +3 -0
  86. package/dist/cmd/cloud/vector/delete-namespace.d.ts.map +1 -0
  87. package/dist/cmd/cloud/vector/delete-namespace.js +77 -0
  88. package/dist/cmd/cloud/vector/delete-namespace.js.map +1 -0
  89. package/dist/cmd/cloud/vector/index.d.ts.map +1 -1
  90. package/dist/cmd/cloud/vector/index.js +21 -4
  91. package/dist/cmd/cloud/vector/index.js.map +1 -1
  92. package/dist/cmd/cloud/vector/list-namespaces.d.ts +3 -0
  93. package/dist/cmd/cloud/vector/list-namespaces.d.ts.map +1 -0
  94. package/dist/cmd/cloud/vector/list-namespaces.js +42 -0
  95. package/dist/cmd/cloud/vector/list-namespaces.js.map +1 -0
  96. package/dist/cmd/cloud/vector/stats.d.ts +3 -0
  97. package/dist/cmd/cloud/vector/stats.d.ts.map +1 -0
  98. package/dist/cmd/cloud/vector/stats.js +142 -0
  99. package/dist/cmd/cloud/vector/stats.js.map +1 -0
  100. package/dist/cmd/cloud/vector/upsert.d.ts +3 -0
  101. package/dist/cmd/cloud/vector/upsert.d.ts.map +1 -0
  102. package/dist/cmd/cloud/vector/upsert.js +192 -0
  103. package/dist/cmd/cloud/vector/upsert.js.map +1 -0
  104. package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
  105. package/dist/cmd/dev/file-watcher.js +90 -31
  106. package/dist/cmd/dev/file-watcher.js.map +1 -1
  107. package/dist/cmd/dev/index.d.ts.map +1 -1
  108. package/dist/cmd/dev/index.js +244 -64
  109. package/dist/cmd/dev/index.js.map +1 -1
  110. package/dist/cmd/dev/skills.d.ts +10 -0
  111. package/dist/cmd/dev/skills.d.ts.map +1 -0
  112. package/dist/cmd/dev/skills.js +57 -0
  113. package/dist/cmd/dev/skills.js.map +1 -0
  114. package/dist/cmd/dev/sync.js +7 -7
  115. package/dist/cmd/dev/sync.js.map +1 -1
  116. package/dist/cmd/index.d.ts.map +1 -1
  117. package/dist/cmd/index.js +1 -0
  118. package/dist/cmd/index.js.map +1 -1
  119. package/dist/cmd/project/create.d.ts.map +1 -1
  120. package/dist/cmd/project/create.js +3 -0
  121. package/dist/cmd/project/create.js.map +1 -1
  122. package/dist/cmd/project/template-flow.d.ts +1 -0
  123. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  124. package/dist/cmd/project/template-flow.js +30 -5
  125. package/dist/cmd/project/template-flow.js.map +1 -1
  126. package/dist/cmd/setup/index.d.ts.map +1 -1
  127. package/dist/cmd/setup/index.js +1 -0
  128. package/dist/cmd/setup/index.js.map +1 -1
  129. package/dist/cmd/upgrade/index.d.ts +15 -0
  130. package/dist/cmd/upgrade/index.d.ts.map +1 -1
  131. package/dist/cmd/upgrade/index.js +59 -4
  132. package/dist/cmd/upgrade/index.js.map +1 -1
  133. package/dist/domain.d.ts +45 -0
  134. package/dist/domain.d.ts.map +1 -0
  135. package/dist/domain.js +200 -0
  136. package/dist/domain.js.map +1 -0
  137. package/dist/schema-generator.d.ts +2 -0
  138. package/dist/schema-generator.d.ts.map +1 -1
  139. package/dist/schema-generator.js +18 -0
  140. package/dist/schema-generator.js.map +1 -1
  141. package/dist/steps.d.ts +1 -1
  142. package/dist/steps.d.ts.map +1 -1
  143. package/dist/steps.js +16 -5
  144. package/dist/steps.js.map +1 -1
  145. package/dist/tui/prompt.d.ts +1 -2
  146. package/dist/tui/prompt.d.ts.map +1 -1
  147. package/dist/tui/prompt.js +8 -4
  148. package/dist/tui/prompt.js.map +1 -1
  149. package/dist/tui.d.ts +16 -0
  150. package/dist/tui.d.ts.map +1 -1
  151. package/dist/tui.js +23 -2
  152. package/dist/tui.js.map +1 -1
  153. package/dist/types.d.ts +9 -2
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +3 -3
  156. package/dist/types.js.map +1 -1
  157. package/package.json +4 -4
  158. package/src/cli.ts +47 -12
  159. package/src/cmd/ai/index.ts +6 -1
  160. package/src/cmd/ai/prompt/agent.md +306 -0
  161. package/src/cmd/ai/prompt/agent.ts +12 -322
  162. package/src/cmd/ai/prompt/api.md +360 -0
  163. package/src/cmd/ai/prompt/api.ts +13 -260
  164. package/src/cmd/ai/prompt/version.ts +61 -0
  165. package/src/cmd/ai/prompt/web.md +509 -0
  166. package/src/cmd/ai/prompt/web.ts +12 -282
  167. package/src/cmd/ai/skills/generate.ts +75 -0
  168. package/src/cmd/ai/skills/generator.ts +519 -0
  169. package/src/cmd/ai/skills/index.ts +23 -0
  170. package/src/cmd/auth/signup.ts +1 -0
  171. package/src/cmd/build/entry-generator.ts +43 -7
  172. package/src/cmd/build/vite/bun-dev-server.ts +31 -28
  173. package/src/cmd/build/vite/metadata-generator.ts +73 -7
  174. package/src/cmd/build/vite/prompt-generator.ts +169 -0
  175. package/src/cmd/build/vite/registry-generator.ts +33 -10
  176. package/src/cmd/build/vite/server-bundler.ts +53 -22
  177. package/src/cmd/build/vite/vite-asset-server-config.ts +5 -0
  178. package/src/cmd/build/vite/vite-builder.ts +107 -87
  179. package/src/cmd/cloud/deploy.ts +103 -31
  180. package/src/cmd/cloud/keyvalue/create-namespace.ts +3 -1
  181. package/src/cmd/cloud/keyvalue/delete-namespace.ts +3 -1
  182. package/src/cmd/cloud/keyvalue/delete.ts +3 -1
  183. package/src/cmd/cloud/keyvalue/set.ts +4 -2
  184. package/src/cmd/cloud/stream/get.ts +2 -9
  185. package/src/cmd/cloud/vector/delete-namespace.ts +89 -0
  186. package/src/cmd/cloud/vector/index.ts +21 -4
  187. package/src/cmd/cloud/vector/list-namespaces.ts +46 -0
  188. package/src/cmd/cloud/vector/stats.ts +160 -0
  189. package/src/cmd/cloud/vector/upsert.ts +216 -0
  190. package/src/cmd/dev/file-watcher.ts +101 -32
  191. package/src/cmd/dev/index.ts +343 -115
  192. package/src/cmd/dev/skills.ts +82 -0
  193. package/src/cmd/dev/sync.ts +7 -7
  194. package/src/cmd/index.ts +1 -0
  195. package/src/cmd/project/create.ts +3 -0
  196. package/src/cmd/project/template-flow.ts +37 -5
  197. package/src/cmd/setup/index.ts +1 -0
  198. package/src/cmd/upgrade/index.ts +68 -4
  199. package/src/domain.ts +273 -0
  200. package/src/schema-generator.ts +23 -0
  201. package/src/steps.ts +16 -5
  202. package/src/tui/prompt.ts +11 -5
  203. package/src/tui.ts +21 -2
  204. package/src/types/md.d.ts +8 -0
  205. package/src/types.ts +12 -3
  206. package/dist/cmd/cloud/domain.d.ts +0 -17
  207. package/dist/cmd/cloud/domain.d.ts.map +0 -1
  208. package/dist/cmd/cloud/domain.js +0 -79
  209. package/dist/cmd/cloud/domain.js.map +0 -1
  210. package/src/cmd/cloud/domain.ts +0 -100
@@ -23,45 +23,48 @@ export interface BunDevServerResult {
23
23
 
24
24
  /**
25
25
  * Start Bun dev server (Vite asset server must already be running)
26
- * Generates entry file with proxy routes pointing to Vite
26
+ *
27
+ * IMPORTANT: This function assumes that the dev bundle has already been built:
28
+ * - Entry file generated at src/generated/app.ts (with workbench config if enabled)
29
+ * - Bundled to .agentuity/app.js with LLM patches applied
30
+ *
31
+ * The bundle is loaded here to ensure AI Gateway routing patches are active.
32
+ * Vite port is read from process.env.VITE_PORT at runtime.
27
33
  */
28
34
  export async function startBunDevServer(options: BunDevServerOptions): Promise<BunDevServerResult> {
29
- const { rootDir, port = 3500, projectId = '', deploymentId = '', logger, vitePort } = options;
35
+ const { rootDir, port = 3500, logger, vitePort } = options;
30
36
 
31
37
  logger.debug('Starting Bun dev server (Vite already running on port %d)...', vitePort);
32
38
 
33
- // Generate workbench source files if enabled (dev mode)
34
- const { loadAgentuityConfig, getWorkbenchConfig } = await import('./config-loader');
35
- const config = await loadAgentuityConfig(rootDir, logger);
36
- const workbenchConfig = getWorkbenchConfig(config, true); // dev mode
39
+ // Load the bundled app - this will start Bun.serve() internally
40
+ // IMPORTANT: We must import the bundled .agentuity/app.js (NOT src/generated/app.ts)
41
+ // because the bundled version has LLM provider patches applied that enable AI Gateway routing.
42
+ // Importing the source file directly would bypass these patches.
43
+ logger.debug('📦 Loading bundled app (Bun server will start)...');
44
+ const appPath = `${rootDir}/.agentuity/app.js`;
37
45
 
38
- if (workbenchConfig.enabled) {
39
- logger.debug('Workbench enabled (dev mode), generating source files...');
40
- const { generateWorkbenchFiles } = await import('./workbench-generator');
41
- await generateWorkbenchFiles(rootDir, projectId, workbenchConfig, logger);
46
+ // Verify bundle exists before attempting to load
47
+ const appFile = Bun.file(appPath);
48
+ if (!(await appFile.exists())) {
49
+ throw new Error(
50
+ `Dev bundle not found at ${appPath}. The bundle must be generated before starting the dev server.`
51
+ );
42
52
  }
43
53
 
44
- // Step 2: Generate entry file with Vite port for asset proxying
45
- logger.debug('📝 Generating entry file with asset proxy configuration...');
46
- const { generateEntryFile } = await import('../entry-generator');
47
- await generateEntryFile({
48
- rootDir,
49
- projectId: projectId || '',
50
- deploymentId: deploymentId || '',
51
- logger,
52
- mode: 'dev',
53
- workbench: workbenchConfig.enabled ? workbenchConfig : undefined,
54
- vitePort, // Pass Vite port for proxy routes
55
- });
56
-
57
- // Step 3: Load the generated app - this will start Bun.serve() internally
58
- logger.debug('📦 Loading generated app (Bun server will start)...');
59
- const appPath = `${rootDir}/src/generated/app.ts`;
60
-
61
54
  // Set PORT env var so the generated app uses the correct port
62
55
  process.env.PORT = String(port);
63
56
 
64
- await import(appPath);
57
+ // Import the generated app with cache-busting query parameter.
58
+ // Bun's module cache is keyed by the full specifier including query string,
59
+ // so adding a unique timestamp forces a fresh import on each reload.
60
+ const cacheBuster = `?t=${Date.now()}`;
61
+ try {
62
+ await import(appPath + cacheBuster);
63
+ } catch (err) {
64
+ const errorMessage = err instanceof Error ? err.message : String(err);
65
+ logger.error('Failed to import generated app from %s: %s', appPath, errorMessage);
66
+ throw new Error(`Failed to load generated app: ${errorMessage}`);
67
+ }
65
68
 
66
69
  // Wait for server to actually start listening
67
70
  // The generated app sets (globalThis as any).__AGENTUITY_SERVER__ when server starts
@@ -25,6 +25,7 @@ interface AssetInfo {
25
25
  kind: string;
26
26
  contentType: string;
27
27
  size: number;
28
+ contentEncoding?: string;
28
29
  }
29
30
 
30
31
  function getContentType(filename: string): string {
@@ -65,6 +66,59 @@ function getContentType(filename: string): string {
65
66
  }
66
67
  }
67
68
 
69
+ /**
70
+ * Determine if an asset should be compressed with gzip.
71
+ * The result is included in asset metadata so the API can generate
72
+ * presigned URLs with matching Content-Encoding.
73
+ */
74
+ function shouldCompressAsset(asset: {
75
+ filename: string;
76
+ contentType: string;
77
+ kind: string;
78
+ }): boolean {
79
+ const ct = asset.contentType.toLowerCase();
80
+ const filename = asset.filename.toLowerCase();
81
+
82
+ if (ct.startsWith('image/') && ct !== 'image/svg+xml') {
83
+ return false;
84
+ }
85
+ if (ct.startsWith('video/') || ct.startsWith('audio/')) {
86
+ return false;
87
+ }
88
+ if (ct === 'font/woff' || ct === 'font/woff2') {
89
+ return false;
90
+ }
91
+ if (/\.(zip|gz|tgz|tar|bz2|br)$/.test(filename)) {
92
+ return false;
93
+ }
94
+
95
+ if (
96
+ ct.startsWith('text/') ||
97
+ ct === 'application/javascript' ||
98
+ ct === 'application/json' ||
99
+ ct === 'application/xml' ||
100
+ ct === 'application/xhtml+xml' ||
101
+ ct === 'image/svg+xml'
102
+ ) {
103
+ return true;
104
+ }
105
+
106
+ if (ct === 'font/ttf' || ct === 'application/vnd.ms-fontobject') {
107
+ return true;
108
+ }
109
+
110
+ if (
111
+ asset.kind === 'entry-point' ||
112
+ asset.kind === 'script' ||
113
+ asset.kind === 'stylesheet' ||
114
+ asset.kind === 'sourcemap'
115
+ ) {
116
+ return true;
117
+ }
118
+
119
+ return false;
120
+ }
121
+
68
122
  function getAssetKind(filename: string, isEntry: boolean = false): string {
69
123
  const ext = filename.split('.').pop()?.toLowerCase();
70
124
 
@@ -220,12 +274,18 @@ export async function generateMetadata(options: MetadataGeneratorOptions): Promi
220
274
  }
221
275
 
222
276
  seenAssets.add(assetPath);
223
- assets.push({
277
+ const kind = getAssetKind(relativePath, isEntry);
278
+ const contentType = getContentType(relativePath);
279
+ const assetInfo: AssetInfo = {
224
280
  filename: assetPath,
225
- kind: getAssetKind(relativePath, isEntry),
226
- contentType: getContentType(relativePath),
281
+ kind,
282
+ contentType,
227
283
  size: stats.size,
228
- });
284
+ };
285
+ if (shouldCompressAsset(assetInfo)) {
286
+ assetInfo.contentEncoding = 'gzip';
287
+ }
288
+ assets.push(assetInfo);
229
289
  }
230
290
  };
231
291
 
@@ -328,12 +388,17 @@ export async function generateMetadata(options: MetadataGeneratorOptions): Promi
328
388
  const assetPath = `public/${relativePath}`;
329
389
  if (!seenAssets.has(assetPath)) {
330
390
  seenAssets.add(assetPath);
331
- assets.push({
391
+ const contentType = getContentType(entry.name);
392
+ const assetInfo: AssetInfo = {
332
393
  filename: assetPath,
333
394
  kind: 'static',
334
- contentType: getContentType(entry.name),
395
+ contentType,
335
396
  size: stats.size,
336
- });
397
+ };
398
+ if (shouldCompressAsset(assetInfo)) {
399
+ assetInfo.contentEncoding = 'gzip';
400
+ }
401
+ assets.push(assetInfo);
337
402
  }
338
403
  }
339
404
  }
@@ -668,6 +733,7 @@ function generateAgentsMd(metadata: BuildMetadata): string {
668
733
  lines.push('- Home directory: `/home/agentuity`');
669
734
  lines.push('- Working directory: `/home/agentuity/app` (application code deployed here)');
670
735
  lines.push('- Logs directory: `/home/agentuity/logs`');
736
+ lines.push('- Temp directory: `/home/agentuity/tmp`');
671
737
  lines.push('');
672
738
  lines.push('**Pre-installed Tools:**');
673
739
  lines.push('- **Runtimes:** Node.js 24, Bun 1.x');
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Prompt Generator
3
+ *
4
+ * Generates AGENTS.md prompt files in .agents/agentuity/sdk/[type]/ directories.
5
+ * Also creates reference files in src/[type]/AGENTS.md (write-once, only if missing).
6
+ *
7
+ * Uses hash tracking to detect template changes in .agents/ folder.
8
+ * Reference files in src/ are never overwritten once created.
9
+ *
10
+ * Only runs in dev mode.
11
+ */
12
+
13
+ import { join, dirname } from 'node:path';
14
+ import { mkdir } from 'node:fs/promises';
15
+ import type { Logger } from '../../../types';
16
+ import { needsUpdate } from '../../ai/prompt/version';
17
+ import {
18
+ generateLLMPrompt as generateAgentPrompt,
19
+ getPromptContent as getAgentContent,
20
+ } from '../../ai/prompt/agent';
21
+ import {
22
+ generateLLMPrompt as generateApiPrompt,
23
+ getPromptContent as getApiContent,
24
+ } from '../../ai/prompt/api';
25
+ import {
26
+ generateLLMPrompt as generateWebPrompt,
27
+ getPromptContent as getWebContent,
28
+ } from '../../ai/prompt/web';
29
+
30
+ interface PromptConfig {
31
+ name: string;
32
+ srcFolder: string;
33
+ generate: () => string;
34
+ getContent: () => string;
35
+ }
36
+
37
+ const PROMPTS: PromptConfig[] = [
38
+ {
39
+ name: 'agent',
40
+ srcFolder: 'agent',
41
+ generate: generateAgentPrompt,
42
+ getContent: getAgentContent,
43
+ },
44
+ {
45
+ name: 'api',
46
+ srcFolder: 'api',
47
+ generate: generateApiPrompt,
48
+ getContent: getApiContent,
49
+ },
50
+ {
51
+ name: 'web',
52
+ srcFolder: 'web',
53
+ generate: generateWebPrompt,
54
+ getContent: getWebContent,
55
+ },
56
+ ];
57
+
58
+ /**
59
+ * Generate the reference file content that points to .agents/
60
+ */
61
+ function generateReferenceContent(name: string): string {
62
+ return `See [.agents/agentuity/sdk/${name}/AGENTS.md](../../.agents/agentuity/sdk/${name}/AGENTS.md) for Agentuity ${name} development guidelines.
63
+ `;
64
+ }
65
+
66
+ /**
67
+ * Generate or update prompt files.
68
+ *
69
+ * - .agents/agentuity/sdk/[type]/AGENTS.md: Full content, updated when hash differs
70
+ * - src/[type]/AGENTS.md: Reference file, only created if missing
71
+ *
72
+ * @param srcDir - The src/ directory path
73
+ * @param logger - Logger for output
74
+ */
75
+ export async function generatePromptFiles(srcDir: string, logger: Logger): Promise<void> {
76
+ const projectRoot = dirname(srcDir);
77
+
78
+ for (const prompt of PROMPTS) {
79
+ await generatePromptFile(projectRoot, srcDir, prompt, logger);
80
+ }
81
+ }
82
+
83
+ async function generatePromptFile(
84
+ projectRoot: string,
85
+ srcDir: string,
86
+ config: PromptConfig,
87
+ logger: Logger
88
+ ): Promise<void> {
89
+ const srcFolderPath = join(srcDir, config.srcFolder);
90
+
91
+ // Check if the src folder exists (e.g., src/agent/)
92
+ try {
93
+ const stat = await Bun.$`test -d ${srcFolderPath}`.nothrow();
94
+ if (stat.exitCode !== 0) {
95
+ logger.trace(`Skipping ${config.name} prompt - src/${config.srcFolder}/ does not exist`);
96
+ return;
97
+ }
98
+ } catch {
99
+ logger.trace(`Skipping ${config.name} prompt - src/${config.srcFolder}/ does not exist`);
100
+ return;
101
+ }
102
+
103
+ // Generate files
104
+ await generateAgentsFile(projectRoot, config, logger);
105
+ await generateReferenceFile(srcFolderPath, config, logger);
106
+ }
107
+
108
+ /**
109
+ * Generate/update the .agents/agentuity/sdk/[type]/AGENTS.md file.
110
+ * Overwrites if hash differs from source template.
111
+ */
112
+ async function generateAgentsFile(
113
+ projectRoot: string,
114
+ config: PromptConfig,
115
+ logger: Logger
116
+ ): Promise<void> {
117
+ const agentsDir = join(projectRoot, '.agents', 'agentuity', 'sdk', config.name);
118
+ const filePath = join(agentsDir, 'AGENTS.md');
119
+
120
+ const file = Bun.file(filePath);
121
+ const fileExists = await file.exists();
122
+
123
+ if (!fileExists) {
124
+ // File doesn't exist - create it
125
+ await mkdir(agentsDir, { recursive: true });
126
+ const content = config.generate();
127
+ await Bun.write(filePath, content);
128
+ logger.debug(`Generated .agents/agentuity/sdk/${config.name}/AGENTS.md`);
129
+ return;
130
+ }
131
+
132
+ // File exists - check if it needs to be updated
133
+ const existingContent = await file.text();
134
+ const sourceContent = config.getContent();
135
+
136
+ if (needsUpdate(existingContent, sourceContent)) {
137
+ const content = config.generate();
138
+ await Bun.write(filePath, content);
139
+ logger.debug(`Updated .agents/agentuity/sdk/${config.name}/AGENTS.md`);
140
+ return;
141
+ }
142
+
143
+ logger.trace(`Skipping ${config.name} prompt - already up to date`);
144
+ }
145
+
146
+ /**
147
+ * Generate the src/[type]/AGENTS.md reference file.
148
+ * Only creates if missing (write-once).
149
+ */
150
+ async function generateReferenceFile(
151
+ srcFolderPath: string,
152
+ config: PromptConfig,
153
+ logger: Logger
154
+ ): Promise<void> {
155
+ const filePath = join(srcFolderPath, 'AGENTS.md');
156
+
157
+ const file = Bun.file(filePath);
158
+ const fileExists = await file.exists();
159
+
160
+ if (fileExists) {
161
+ logger.trace(`Skipping src/${config.srcFolder}/AGENTS.md - already exists`);
162
+ return;
163
+ }
164
+
165
+ // Create the reference file
166
+ const content = generateReferenceContent(config.name);
167
+ await Bun.write(filePath, content);
168
+ logger.debug(`Generated src/${config.srcFolder}/AGENTS.md (reference)`);
169
+ }
@@ -13,6 +13,22 @@ import type { RouteInfo } from './route-discovery';
13
13
 
14
14
  const AgentIdentifierCollisionError = StructuredError('AgentIdentifierCollisionError');
15
15
 
16
+ /**
17
+ * Regex to strip route parameter characters that produce invalid TypeScript property names:
18
+ * - Leading : (path parameters, e.g., :id)
19
+ * - Leading * (wildcard routes, e.g., *path)
20
+ * - Trailing ?, +, * (optional/one-or-more/wildcard modifiers, e.g., :userId?)
21
+ */
22
+ const ROUTE_PARAM_CHARS = /^[:*]|[?+*]$/g;
23
+
24
+ /**
25
+ * Sanitize a route path segment for use as a TypeScript property name.
26
+ * Strips route parameter characters and converts to camelCase.
27
+ */
28
+ function sanitizePathSegment(segment: string): string {
29
+ return toCamelCase(segment.replace(ROUTE_PARAM_CHARS, ''));
30
+ }
31
+
16
32
  /**
17
33
  * Generate src/generated/registry.ts with agent registry and types
18
34
  */
@@ -244,9 +260,9 @@ function generateRPCRegistryType(
244
260
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
261
  let current: any = tree;
246
262
 
247
- // Add path segments (all parts) - convert to camelCase for safe property access
263
+ // Add path segments - sanitize for valid TypeScript property names
248
264
  for (let i = 0; i < pathParts.length; i++) {
249
- const part = toCamelCase(pathParts[i]);
265
+ const part = sanitizePathSegment(pathParts[i]);
250
266
  if (!current[part]) {
251
267
  current[part] = {};
252
268
  }
@@ -382,11 +398,11 @@ function generateRPCRuntimeMetadata(
382
398
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
383
399
  let current: any = tree;
384
400
 
385
- // Convert path segments to camelCase for safe property access
401
+ // Sanitize path segments for valid property names (must match type generation)
386
402
  for (const part of pathParts) {
387
- const camelPart = toCamelCase(part);
388
- if (!current[camelPart]) current[camelPart] = {};
389
- current = current[camelPart];
403
+ const sanitized = sanitizePathSegment(part);
404
+ if (!current[sanitized]) current[sanitized] = {};
405
+ current = current[sanitized];
390
406
  }
391
407
 
392
408
  // Use terminal method name based on route type
@@ -478,7 +494,7 @@ export function generateRouteRegistry(
478
494
  // Collect agent and schema imports from routes with validators or exported schemas
479
495
  allRoutes.forEach((route) => {
480
496
  const hasSchemaVars = !!route.inputSchemaVariable || !!route.outputSchemaVariable;
481
- if (!route.hasValidator && !hasSchemaVars) return;
497
+ if (!route.hasValidator && !hasSchemaVars && !route.agentVariable) return;
482
498
 
483
499
  // Collect agent imports (when using agent.validator())
484
500
  if (
@@ -592,7 +608,7 @@ export function generateRouteRegistry(
592
608
 
593
609
  // Add InferInput/InferOutput imports if we have any routes with schemas
594
610
  const hasSchemas = allRoutes.some(
595
- (r) => r.hasValidator || r.inputSchemaVariable || r.outputSchemaVariable
611
+ (r) => r.hasValidator || r.inputSchemaVariable || r.outputSchemaVariable || r.agentVariable
596
612
  );
597
613
  const typeImports = hasSchemas
598
614
  ? `import type { InferInput, InferOutput } from '@agentuity/core';\n`
@@ -600,7 +616,9 @@ export function generateRouteRegistry(
600
616
 
601
617
  // Generate individual route schema types
602
618
  const routeSchemaTypes = allRoutes
603
- .filter((r) => r.hasValidator || r.inputSchemaVariable || r.outputSchemaVariable)
619
+ .filter(
620
+ (r) => r.hasValidator || r.inputSchemaVariable || r.outputSchemaVariable || r.agentVariable
621
+ )
604
622
  .map((route) => {
605
623
  const routeKey = route.method ? `${route.method.toUpperCase()} ${route.path}` : route.path;
606
624
  const safeName = routeKey
@@ -707,7 +725,12 @@ export function generateRouteRegistry(
707
725
  .replace(/_+/g, '_');
708
726
  const pascalName = toPascalCase(safeName);
709
727
 
710
- if (!route.hasValidator && !route.inputSchemaVariable && !route.outputSchemaVariable) {
728
+ if (
729
+ !route.hasValidator &&
730
+ !route.inputSchemaVariable &&
731
+ !route.outputSchemaVariable &&
732
+ !route.agentVariable
733
+ ) {
711
734
  const streamValue = route.stream === true ? 'true' : 'false';
712
735
  return `\t'${routeKey}': {
713
736
  \t\tinputSchema: never;
@@ -9,6 +9,37 @@ import type { Logger } from '../../../types';
9
9
  import type { BunPlugin } from 'bun';
10
10
  import { generatePatches, applyPatch } from '../patch';
11
11
 
12
+ /**
13
+ * Format a Bun build log (BuildMessage or ResolveMessage) into a readable string
14
+ */
15
+ export function formatBuildLog(log: BuildMessage | ResolveMessage): string {
16
+ const parts: string[] = [];
17
+
18
+ // For ResolveMessage, format with specifier info
19
+ if (log.name === 'ResolveMessage') {
20
+ const resolveLog = log as ResolveMessage;
21
+ if (resolveLog.specifier) {
22
+ parts.push(`Could not resolve "${resolveLog.specifier}"`);
23
+ // Use referrer if available, otherwise fall back to position.file
24
+ const referrer = resolveLog.referrer || resolveLog.position?.file;
25
+ if (referrer) {
26
+ parts.push(` imported from: ${referrer}`);
27
+ }
28
+ } else if (resolveLog.message) {
29
+ parts.push(resolveLog.message);
30
+ }
31
+ } else if (log.message) {
32
+ parts.push(log.message);
33
+ }
34
+
35
+ // Add position info if available (only if we haven't already shown referrer from position)
36
+ if (log.position && log.name !== 'ResolveMessage') {
37
+ parts.push(` at ${log.position.file}:${log.position.line}:${log.position.column}`);
38
+ }
39
+
40
+ return parts.join('\n');
41
+ }
42
+
12
43
  export interface ServerBundleOptions {
13
44
  rootDir: string;
14
45
  dev: boolean;
@@ -282,19 +313,29 @@ export async function installExternalsAndBuild(options: ServerBundleOptions): Pr
282
313
  if (originalNodeEnv !== undefined) {
283
314
  process.env.NODE_ENV = originalNodeEnv;
284
315
  }
285
- logger.error('Bun.build threw an exception');
286
316
 
287
317
  // Handle AggregateError with build/resolve messages
288
318
  if (error instanceof AggregateError && error.errors) {
289
- for (const err of error.errors) {
290
- const parts = [err.message || err.text || 'Unknown error'];
291
- if (err.position) {
292
- parts.push(` at ${err.position.file}:${err.position.line}:${err.position.column}`);
293
- }
294
- logger.error(parts.join('\n'));
295
- }
296
- } else {
297
- logger.error(` ${error instanceof Error ? error.message : String(error)}`);
319
+ const formattedErrors = error.errors
320
+ .map((err) => {
321
+ // Try to use formatBuildLog if it looks like a BuildMessage/ResolveMessage
322
+ if (err && typeof err === 'object' && 'name' in err) {
323
+ const formatted = formatBuildLog(err as BuildMessage | ResolveMessage);
324
+ if (formatted) return formatted;
325
+ }
326
+ // Fallback for other error types
327
+ const parts = [err.message || err.text || 'Unknown error'];
328
+ if (err.position) {
329
+ parts.push(
330
+ ` at ${err.position.file}:${err.position.line}:${err.position.column}`
331
+ );
332
+ }
333
+ return parts.join('\n');
334
+ })
335
+ .filter(Boolean)
336
+ .join('\n');
337
+
338
+ throw new Error(formattedErrors || 'Build failed');
298
339
  }
299
340
 
300
341
  throw error;
@@ -306,19 +347,9 @@ export async function installExternalsAndBuild(options: ServerBundleOptions): Pr
306
347
  }
307
348
 
308
349
  if (!result.success) {
309
- logger.error('Bun.build failed for server');
310
- logger.error(
311
- `Build result: success=${result.success}, outputs=${result.outputs.length}, logs=${result.logs.length}`
312
- );
313
-
314
350
  const errorMessages = result.logs
315
- .map((log) => {
316
- const parts = [log.message];
317
- if (log.position) {
318
- parts.push(` at ${log.position.file}:${log.position.line}:${log.position.column}`);
319
- }
320
- return parts.join('\n');
321
- })
351
+ .map((log) => formatBuildLog(log))
352
+ .filter(Boolean)
322
353
  .join('\n');
323
354
 
324
355
  throw new Error(errorMessages || 'Build failed with no error messages');
@@ -69,6 +69,11 @@ export async function generateAssetServerConfig(
69
69
  dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],
70
70
  },
71
71
 
72
+ // Pre-bundle @agentuity/workbench to avoid React preamble issues with pre-built JSX
73
+ optimizeDeps: {
74
+ include: ['@agentuity/workbench', '@agentuity/core', '@agentuity/react'],
75
+ },
76
+
72
77
  // Only allow frontend env vars (server uses process.env)
73
78
  envPrefix: ['VITE_', 'AGENTUITY_PUBLIC_', 'PUBLIC_'],
74
79