@agentuity/cli 0.0.68 → 0.0.70

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 (182) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/cli.ts +20 -8
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +16 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cmd/ai/capabilities/show.d.ts.map +1 -1
  8. package/dist/cmd/ai/capabilities/show.js +0 -13
  9. package/dist/cmd/ai/capabilities/show.js.map +1 -1
  10. package/dist/cmd/ai/prompt/agent.d.ts.map +1 -1
  11. package/dist/cmd/ai/prompt/agent.js +23 -24
  12. package/dist/cmd/ai/prompt/agent.js.map +1 -1
  13. package/dist/cmd/ai/prompt/api.d.ts.map +1 -1
  14. package/dist/cmd/ai/prompt/api.js +12 -9
  15. package/dist/cmd/ai/prompt/api.js.map +1 -1
  16. package/dist/cmd/build/ast.d.ts +1 -2
  17. package/dist/cmd/build/ast.d.ts.map +1 -1
  18. package/dist/cmd/build/ast.js +261 -260
  19. package/dist/cmd/build/ast.js.map +1 -1
  20. package/dist/cmd/build/bundler.d.ts +2 -1
  21. package/dist/cmd/build/bundler.d.ts.map +1 -1
  22. package/dist/cmd/build/bundler.js +11 -2
  23. package/dist/cmd/build/bundler.js.map +1 -1
  24. package/dist/cmd/build/index.js +1 -1
  25. package/dist/cmd/build/index.js.map +1 -1
  26. package/dist/cmd/build/plugin.d.ts.map +1 -1
  27. package/dist/cmd/build/plugin.js +152 -416
  28. package/dist/cmd/build/plugin.js.map +1 -1
  29. package/dist/cmd/build/route-registry.d.ts +36 -0
  30. package/dist/cmd/build/route-registry.d.ts.map +1 -0
  31. package/dist/cmd/build/route-registry.js +151 -0
  32. package/dist/cmd/build/route-registry.js.map +1 -0
  33. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  34. package/dist/cmd/cloud/deploy.js +1 -0
  35. package/dist/cmd/cloud/deploy.js.map +1 -1
  36. package/dist/cmd/cloud/index.d.ts.map +1 -1
  37. package/dist/cmd/cloud/index.js +0 -2
  38. package/dist/cmd/cloud/index.js.map +1 -1
  39. package/dist/cmd/dev/index.js +3 -3
  40. package/dist/cmd/dev/index.js.map +1 -1
  41. package/dist/cmd/dev/sync.d.ts.map +1 -1
  42. package/dist/cmd/dev/sync.js +0 -15
  43. package/dist/cmd/dev/sync.js.map +1 -1
  44. package/dist/cmd/dev/templates.d.ts.map +1 -1
  45. package/dist/cmd/dev/templates.js +14 -31
  46. package/dist/cmd/dev/templates.js.map +1 -1
  47. package/dist/cmd/profile/create.d.ts.map +1 -1
  48. package/dist/cmd/profile/create.js +0 -1
  49. package/dist/cmd/profile/create.js.map +1 -1
  50. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  51. package/dist/cmd/project/template-flow.js +64 -53
  52. package/dist/cmd/project/template-flow.js.map +1 -1
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +0 -3
  55. package/dist/config.js.map +1 -1
  56. package/dist/keychain.d.ts.map +1 -1
  57. package/dist/keychain.js +8 -4
  58. package/dist/keychain.js.map +1 -1
  59. package/dist/tui/box.d.ts +19 -0
  60. package/dist/tui/box.d.ts.map +1 -0
  61. package/dist/tui/box.js +160 -0
  62. package/dist/tui/box.js.map +1 -0
  63. package/dist/tui/colors.d.ts +20 -0
  64. package/dist/tui/colors.d.ts.map +1 -0
  65. package/dist/tui/colors.js +52 -0
  66. package/dist/tui/colors.js.map +1 -0
  67. package/dist/tui/group.d.ts +25 -0
  68. package/dist/tui/group.d.ts.map +1 -0
  69. package/dist/tui/group.js +32 -0
  70. package/dist/tui/group.js.map +1 -0
  71. package/dist/tui/prompt.d.ts +65 -0
  72. package/dist/tui/prompt.d.ts.map +1 -0
  73. package/dist/tui/prompt.js +377 -0
  74. package/dist/tui/prompt.js.map +1 -0
  75. package/dist/tui/symbols.d.ts +32 -0
  76. package/dist/tui/symbols.d.ts.map +1 -0
  77. package/dist/tui/symbols.js +52 -0
  78. package/dist/tui/symbols.js.map +1 -0
  79. package/dist/tui.d.ts +6 -0
  80. package/dist/tui.d.ts.map +1 -1
  81. package/dist/tui.js +6 -0
  82. package/dist/tui.js.map +1 -1
  83. package/dist/types.d.ts +0 -24
  84. package/dist/types.d.ts.map +1 -1
  85. package/dist/types.js +0 -1
  86. package/dist/types.js.map +1 -1
  87. package/package.json +3 -3
  88. package/src/cli.ts +19 -0
  89. package/src/cmd/ai/capabilities/show.ts +0 -13
  90. package/src/cmd/ai/prompt/agent.ts +23 -24
  91. package/src/cmd/ai/prompt/api.ts +12 -9
  92. package/src/cmd/build/ast.ts +364 -334
  93. package/src/cmd/build/bundler.ts +16 -1
  94. package/src/cmd/build/index.ts +1 -1
  95. package/src/cmd/build/plugin.ts +171 -495
  96. package/src/cmd/build/route-registry.ts +198 -0
  97. package/src/cmd/cloud/deploy.ts +1 -0
  98. package/src/cmd/cloud/index.ts +0 -2
  99. package/src/cmd/dev/index.ts +3 -3
  100. package/src/cmd/dev/sync.ts +0 -28
  101. package/src/cmd/dev/templates.ts +14 -31
  102. package/src/cmd/profile/create.ts +0 -1
  103. package/src/cmd/project/template-flow.ts +77 -57
  104. package/src/config.ts +1 -5
  105. package/src/keychain.ts +9 -11
  106. package/src/tui/box.ts +202 -0
  107. package/src/tui/colors.ts +55 -0
  108. package/src/tui/group.ts +56 -0
  109. package/src/tui/prompt.ts +506 -0
  110. package/src/tui/symbols.ts +64 -0
  111. package/src/tui.ts +14 -0
  112. package/src/types.ts +0 -1
  113. package/dist/cmd/build/ast.test.d.ts +0 -2
  114. package/dist/cmd/build/ast.test.d.ts.map +0 -1
  115. package/dist/cmd/build/ast.test.js +0 -339
  116. package/dist/cmd/build/ast.test.js.map +0 -1
  117. package/dist/cmd/build/fix-duplicate-exports.test.d.ts +0 -2
  118. package/dist/cmd/build/fix-duplicate-exports.test.d.ts.map +0 -1
  119. package/dist/cmd/build/fix-duplicate-exports.test.js +0 -300
  120. package/dist/cmd/build/fix-duplicate-exports.test.js.map +0 -1
  121. package/dist/cmd/cloud/objectstore/delete-bucket.d.ts +0 -3
  122. package/dist/cmd/cloud/objectstore/delete-bucket.d.ts.map +0 -1
  123. package/dist/cmd/cloud/objectstore/delete-bucket.js +0 -72
  124. package/dist/cmd/cloud/objectstore/delete-bucket.js.map +0 -1
  125. package/dist/cmd/cloud/objectstore/delete.d.ts +0 -3
  126. package/dist/cmd/cloud/objectstore/delete.d.ts.map +0 -1
  127. package/dist/cmd/cloud/objectstore/delete.js +0 -65
  128. package/dist/cmd/cloud/objectstore/delete.js.map +0 -1
  129. package/dist/cmd/cloud/objectstore/get.d.ts +0 -3
  130. package/dist/cmd/cloud/objectstore/get.d.ts.map +0 -1
  131. package/dist/cmd/cloud/objectstore/get.js +0 -75
  132. package/dist/cmd/cloud/objectstore/get.js.map +0 -1
  133. package/dist/cmd/cloud/objectstore/index.d.ts +0 -3
  134. package/dist/cmd/cloud/objectstore/index.d.ts.map +0 -1
  135. package/dist/cmd/cloud/objectstore/index.js +0 -36
  136. package/dist/cmd/cloud/objectstore/index.js.map +0 -1
  137. package/dist/cmd/cloud/objectstore/list-buckets.d.ts +0 -3
  138. package/dist/cmd/cloud/objectstore/list-buckets.d.ts.map +0 -1
  139. package/dist/cmd/cloud/objectstore/list-buckets.js +0 -46
  140. package/dist/cmd/cloud/objectstore/list-buckets.js.map +0 -1
  141. package/dist/cmd/cloud/objectstore/list-keys.d.ts +0 -3
  142. package/dist/cmd/cloud/objectstore/list-keys.d.ts.map +0 -1
  143. package/dist/cmd/cloud/objectstore/list-keys.js +0 -58
  144. package/dist/cmd/cloud/objectstore/list-keys.js.map +0 -1
  145. package/dist/cmd/cloud/objectstore/put.d.ts +0 -3
  146. package/dist/cmd/cloud/objectstore/put.d.ts.map +0 -1
  147. package/dist/cmd/cloud/objectstore/put.js +0 -66
  148. package/dist/cmd/cloud/objectstore/put.js.map +0 -1
  149. package/dist/cmd/cloud/objectstore/repl.d.ts +0 -3
  150. package/dist/cmd/cloud/objectstore/repl.d.ts.map +0 -1
  151. package/dist/cmd/cloud/objectstore/repl.js +0 -224
  152. package/dist/cmd/cloud/objectstore/repl.js.map +0 -1
  153. package/dist/cmd/cloud/objectstore/url.d.ts +0 -3
  154. package/dist/cmd/cloud/objectstore/url.d.ts.map +0 -1
  155. package/dist/cmd/cloud/objectstore/url.js +0 -64
  156. package/dist/cmd/cloud/objectstore/url.js.map +0 -1
  157. package/dist/cmd/cloud/objectstore/util.d.ts +0 -11
  158. package/dist/cmd/cloud/objectstore/util.d.ts.map +0 -1
  159. package/dist/cmd/cloud/objectstore/util.js +0 -18
  160. package/dist/cmd/cloud/objectstore/util.js.map +0 -1
  161. package/dist/crypto/box.test.d.ts +0 -2
  162. package/dist/crypto/box.test.d.ts.map +0 -1
  163. package/dist/crypto/box.test.js +0 -317
  164. package/dist/crypto/box.test.js.map +0 -1
  165. package/dist/env-util.test.d.ts +0 -2
  166. package/dist/env-util.test.d.ts.map +0 -1
  167. package/dist/env-util.test.js +0 -146
  168. package/dist/env-util.test.js.map +0 -1
  169. package/src/cmd/build/ast.test.ts +0 -418
  170. package/src/cmd/build/fix-duplicate-exports.test.ts +0 -387
  171. package/src/cmd/cloud/objectstore/delete-bucket.ts +0 -77
  172. package/src/cmd/cloud/objectstore/delete.ts +0 -67
  173. package/src/cmd/cloud/objectstore/get.ts +0 -77
  174. package/src/cmd/cloud/objectstore/index.ts +0 -36
  175. package/src/cmd/cloud/objectstore/list-buckets.ts +0 -51
  176. package/src/cmd/cloud/objectstore/list-keys.ts +0 -63
  177. package/src/cmd/cloud/objectstore/put.ts +0 -74
  178. package/src/cmd/cloud/objectstore/repl.ts +0 -239
  179. package/src/cmd/cloud/objectstore/url.ts +0 -67
  180. package/src/cmd/cloud/objectstore/util.ts +0 -29
  181. package/src/crypto/box.test.ts +0 -431
  182. package/src/env-util.test.ts +0 -194
@@ -1,22 +1,21 @@
1
1
  import type { BunPlugin } from 'bun';
2
- import { dirname, basename, join, resolve } from 'node:path';
2
+ import { dirname, join, resolve } from 'node:path';
3
3
  import { existsSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';
4
4
  import type { BuildMetadata } from '@agentuity/server';
5
5
  import {
6
6
  parseAgentMetadata,
7
- parseRoute,
8
7
  parseEvalMetadata,
8
+ parseRoute,
9
9
  analyzeWorkbench,
10
- checkRouteConflicts,
11
10
  generateLifecycleTypes,
12
11
  findCreateAppEndPosition,
13
12
  } from './ast';
14
13
  import { StructuredError, type WorkbenchConfig } from '@agentuity/core';
15
14
  import { applyPatch, generatePatches } from './patch';
16
- import { detectSubagent } from '../../utils/detectSubagent';
17
15
  import { createLogger } from '@agentuity/server';
18
16
  import type { LogLevel } from '../../types';
19
17
  import { toCamelCase, toPascalCase } from '../../utils/string';
18
+ import { generateRouteRegistry, type RouteInfo } from './route-registry';
20
19
 
21
20
  /**
22
21
  * Setup lifecycle types by analyzing app.ts for setup() function
@@ -77,53 +76,21 @@ async function setupWorkbench(srcDir: string): Promise<WorkbenchConfig | null> {
77
76
 
78
77
  const workbenchConfig = analysis.config;
79
78
 
80
- // Check for route conflicts if workbench is being used
81
- if (workbenchConfig?.route) {
82
- const hasConflict = checkRouteConflicts(appContent, workbenchConfig.route);
83
- if (hasConflict) {
84
- const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
85
- logger.error(`🚨 Route conflict detected!\n`);
86
- logger.error(
87
- ` Workbench route '${workbenchConfig.route}' conflicts with existing application route`
88
- );
89
- logger.error(` Please use a different route or remove the conflicting route.\n`);
90
- }
91
- }
92
-
93
79
  return workbenchConfig;
94
80
  }
95
81
 
96
82
  const AgentIdentifierCollisionError = StructuredError('AgentIdentifierCollisionError');
97
- const SubAgentNameCollisionError = StructuredError('SubAgentNameCollisionError');
98
83
 
99
84
  function generateAgentRegistry(srcDir: string, agentInfo: Array<Record<string, string>>) {
100
- // Separate parent agents and subagents
101
- const parentAgents = agentInfo.filter((a) => !a.parent);
102
- const subagents = agentInfo.filter((a) => a.parent);
103
-
104
- // Group subagents by parent
105
- const subagentsByParent = new Map<string, Array<Record<string, string>>>();
106
- for (const subagent of subagents) {
107
- const parentName = subagent.parent!;
108
- if (!subagentsByParent.has(parentName)) {
109
- subagentsByParent.set(parentName, []);
110
- }
111
- subagentsByParent.get(parentName)!.push(subagent);
112
- }
113
-
114
85
  // Detect naming collisions in generated identifiers
115
- // Naming strategy: parent + child names are combined as `${parent}_${name}` then converted to camelCase
116
- // Example: parent="user", child="profile" → "user_profile" → "userProfile"
117
- // Potential collision: parent="user_profile", child="info" and parent="user", child="profile_info" both → "userProfileInfo"
118
86
  const generatedNames = new Set<string>();
119
87
  const collisions: string[] = [];
120
88
 
121
89
  for (const agent of agentInfo) {
122
- const fullName = agent.parent ? `${agent.parent}_${agent.name}` : agent.name;
123
- const camelName = toCamelCase(fullName);
90
+ const camelName = toCamelCase(agent.name);
124
91
 
125
92
  if (generatedNames.has(camelName)) {
126
- collisions.push(`Identifier collision detected: "${camelName}" (from "${fullName}")`);
93
+ collisions.push(`Identifier collision detected: "${camelName}" (from "${agent.name}")`);
127
94
  }
128
95
  generatedNames.add(camelName);
129
96
  }
@@ -138,158 +105,45 @@ function generateAgentRegistry(srcDir: string, agentInfo: Array<Record<string, s
138
105
  }
139
106
 
140
107
  // Generate imports for all agents
108
+ // Registry is now in .agentuity/, so imports need to go up one level and into src/
141
109
  const imports = agentInfo
142
- .map(({ name, path, parent }) => {
143
- const fullName = parent ? `${parent}.${name}` : name;
144
- const camelName = toCamelCase(fullName.replace('.', '_'));
145
- const relativePath = path.replace(/^\.\/agent\//, './');
110
+ .map(({ name, path }) => {
111
+ const camelName = toCamelCase(name);
112
+ const relativePath = path.replace(/^\.\/agent\//, '../src/agent/');
146
113
  return `import ${camelName}Agent from '${relativePath}';`;
147
114
  })
148
115
  .join('\n');
149
116
 
150
- // Evals are now imported in plugin.ts when agents are registered
151
- // No need to import them in registry.generated.ts
152
- const evalsImportsStr = '';
153
-
154
- // Validate that child property names don't collide with parent agent properties
155
- const reservedAgentProperties = ['metadata', 'run', 'inputSchema', 'outputSchema', 'stream'];
156
- for (const parentAgent of parentAgents) {
157
- const children = subagentsByParent.get(parentAgent.name) || [];
158
- for (const child of children) {
159
- const childPropertyName = toCamelCase(child.name);
160
-
161
- // Check for collision with reserved agent properties
162
- if (reservedAgentProperties.includes(childPropertyName)) {
163
- throw new SubAgentNameCollisionError({
164
- message:
165
- `Subagent property name collision detected: "${childPropertyName}" in parent "${parentAgent.name}"\n` +
166
- `The child name "${child.name}" conflicts with a reserved agent property (${reservedAgentProperties.join(', ')}).\n` +
167
- `Please rename the subagent to avoid this collision.`,
168
- });
169
- }
170
- }
171
- }
172
-
173
- // Generate nested registry structure
174
- const registryLines: string[] = [];
175
- for (const parentAgent of parentAgents) {
176
- const parentCamelName = toCamelCase(parentAgent.name);
177
- const children = subagentsByParent.get(parentAgent.name) || [];
178
-
179
- if (children.length === 0) {
180
- // No subagents, simple assignment
181
- registryLines.push(` ${parentCamelName}: ${parentCamelName}Agent,`);
182
- } else {
183
- // Has subagents, create nested structure using object spread (no mutation)
184
- registryLines.push(` ${parentCamelName}: {`);
185
- registryLines.push(` ...${parentCamelName}Agent,`);
186
- for (const child of children) {
187
- const childCamelName = toCamelCase(`${parentAgent.name}_${child.name}`);
188
- registryLines.push(` ${toCamelCase(child.name)}: ${childCamelName}Agent,`);
189
- }
190
- registryLines.push(` },`);
191
- }
192
- }
193
- const registry = registryLines.join('\n');
117
+ // Generate flat registry structure (no subagents)
118
+ const registry = agentInfo
119
+ .map(({ name }) => {
120
+ const camelName = toCamelCase(name);
121
+ return ` ${camelName}: ${camelName}Agent,`;
122
+ })
123
+ .join('\n');
194
124
 
195
125
  // Generate type exports for all agents
196
126
  const typeExports = agentInfo
197
- .map(({ name, parent }) => {
198
- const fullName = parent ? `${parent}_${name}` : name;
199
- const camelName = toCamelCase(fullName);
200
- const pascalName = toPascalCase(fullName);
127
+ .map(({ name }) => {
128
+ const camelName = toCamelCase(name);
129
+ const pascalName = toPascalCase(name);
201
130
  return `export type ${pascalName}AgentRunner = AgentRunner<typeof ${camelName}Agent['inputSchema'], typeof ${camelName}Agent['outputSchema'], typeof ${camelName}Agent['stream'] extends true ? true : false>;`;
202
131
  })
203
132
  .join('\n');
204
133
 
205
- // Generate nested agent type definitions for Hono Context augmentation
206
- const honoAgentTypeLines: string[] = [];
207
- for (const parentAgent of parentAgents) {
208
- const parentCamelName = toCamelCase(parentAgent.name);
209
- const children = subagentsByParent.get(parentAgent.name) || [];
210
-
211
- if (children.length === 0) {
212
- // No subagents
213
- honoAgentTypeLines.push(
214
- ` ${parentCamelName}: AgentRunner<LocalAgentRegistry['${parentCamelName}']['inputSchema'], LocalAgentRegistry['${parentCamelName}']['outputSchema'], LocalAgentRegistry['${parentCamelName}']['stream'] extends true ? true : false>;`
215
- );
216
- } else {
217
- // Has subagents - create intersection type
218
- honoAgentTypeLines.push(
219
- ` ${parentCamelName}: AgentRunner<LocalAgentRegistry['${parentCamelName}']['inputSchema'], LocalAgentRegistry['${parentCamelName}']['outputSchema'], LocalAgentRegistry['${parentCamelName}']['stream'] extends true ? true : false> & {`
220
- );
221
- for (const child of children) {
222
- const childCamelName = toCamelCase(child.name);
223
- const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
224
- honoAgentTypeLines.push(
225
- ` ${childCamelName}: AgentRunner<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema'], typeof ${fullChildName}Agent['stream'] extends true ? true : false>;`
226
- );
227
- }
228
- honoAgentTypeLines.push(` };`);
229
- }
230
- }
231
- const honoAgentTypes = honoAgentTypeLines.join('\n');
232
-
233
- // Generate agent type definitions for AgentRegistry interface augmentation
234
- const runtimeAgentTypeLines: string[] = [];
235
- for (const parentAgent of parentAgents) {
236
- const parentCamelName = toCamelCase(parentAgent.name);
237
- const children = subagentsByParent.get(parentAgent.name) || [];
238
-
239
- if (children.length === 0) {
240
- // No subagents - use typeof the imported agent
241
- runtimeAgentTypeLines.push(
242
- ` ${parentCamelName}: AgentRunner<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema'], typeof ${parentCamelName}Agent['stream'] extends true ? true : false>;`
243
- );
244
- } else {
245
- // Has subagents - create intersection type using typeof
246
- runtimeAgentTypeLines.push(
247
- ` ${parentCamelName}: AgentRunner<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema'], typeof ${parentCamelName}Agent['stream'] extends true ? true : false> & {`
248
- );
249
- for (const child of children) {
250
- const childCamelName = toCamelCase(child.name);
251
- const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
252
- runtimeAgentTypeLines.push(
253
- ` ${childCamelName}: AgentRunner<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema'], typeof ${fullChildName}Agent['stream'] extends true ? true : false>;`
254
- );
255
- }
256
- runtimeAgentTypeLines.push(` };`);
257
- }
258
- }
259
- const runtimeAgentTypes = runtimeAgentTypeLines.join('\n');
260
-
261
- // Generate React client types with nested structure
262
- const clientAgentTypeLines: string[] = [];
263
- for (const parentAgent of parentAgents) {
264
- const parentCamelName = toCamelCase(parentAgent.name);
265
- const children = subagentsByParent.get(parentAgent.name) || [];
266
-
267
- if (children.length === 0) {
268
- // No subagents
269
- clientAgentTypeLines.push(
270
- ` '${parentAgent.name}': Agent<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema']>;`
271
- );
272
- } else {
273
- // Has subagents - create nested type with subagent access via dot notation
274
- clientAgentTypeLines.push(
275
- ` '${parentAgent.name}': Agent<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema']>;`
276
- );
277
- for (const child of children) {
278
- const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
279
- clientAgentTypeLines.push(
280
- ` '${parentAgent.name}.${child.name}': Agent<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema']>;`
281
- );
282
- }
283
- }
284
- }
285
- const reactAgentTypes = clientAgentTypeLines.join('\n');
134
+ // Generate flat agent type definitions for AgentRegistry interface augmentation
135
+ const runtimeAgentTypes = agentInfo
136
+ .map(({ name }) => {
137
+ const camelName = toCamelCase(name);
138
+ return ` ${camelName}: AgentRunner<typeof ${camelName}Agent['inputSchema'], typeof ${camelName}Agent['outputSchema'], typeof ${camelName}Agent['stream'] extends true ? true : false>;`;
139
+ })
140
+ .join('\n');
286
141
 
287
142
  const generatedContent = `/// <reference types="hono" />
288
143
  // Auto-generated by Agentuity - do not edit manually
289
- ${imports}${evalsImportsStr}
144
+ ${imports}
290
145
  import type { AgentRunner, Logger } from '@agentuity/runtime';
291
- import type { KeyValueStorage, ObjectStorage, StreamStorage, VectorStorage } from '@agentuity/core';
292
- import type { Agent } from '@agentuity/react';
146
+ import type { KeyValueStorage, StreamStorage, VectorStorage } from '@agentuity/core';
293
147
 
294
148
  /**
295
149
  * Registry of all agents in this application.
@@ -315,38 +169,20 @@ ${runtimeAgentTypes}
315
169
  }
316
170
  }
317
171
 
318
- // Augment Hono Context to provide strongly-typed agents and runtime services
319
- // Note: Properties are added to Context via middleware in @agentuity/runtime
320
- declare module "hono" {
321
- interface Context {
322
- agentName: LocalAgentName;
323
- agent: {
324
- ${honoAgentTypes}
325
- };
326
- waitUntil: (promise: Promise<void> | (() => void | Promise<void>)) => void;
327
- logger: Logger;
328
- kv: KeyValueStorage;
329
- objectstore: ObjectStorage;
330
- stream: StreamStorage;
331
- vector: VectorStorage;
332
- }
333
- }
334
-
335
- // Augment @agentuity/react types with strongly-typed agents from this project
336
- declare module '@agentuity/react' {
337
- interface AgentRegistry {
338
- ${reactAgentTypes}
339
- }
340
- }
172
+ // NOTE: Hono Context properties are accessed via c.var (e.g., c.var.logger, c.var.kv)
173
+ // The Variables interface in @agentuity/runtime defines all available context properties
341
174
  `;
342
175
 
176
+ const projectRoot = join(srcDir, '..');
177
+ const agentuityDir = join(projectRoot, '.agentuity');
178
+ const registryPath = join(agentuityDir, 'registry.generated.ts');
179
+
343
180
  const agentsDir = join(srcDir, 'agent');
344
- const registryPath = join(agentsDir, 'registry.generated.ts');
345
181
  const legacyTypesPath = join(agentsDir, 'types.generated.d.ts');
346
182
 
347
- // Ensure agent directory exists
348
- if (!existsSync(agentsDir)) {
349
- mkdirSync(agentsDir, { recursive: true });
183
+ // Ensure .agentuity directory exists
184
+ if (!existsSync(agentuityDir)) {
185
+ mkdirSync(agentuityDir, { recursive: true });
350
186
  }
351
187
 
352
188
  writeFileSync(registryPath, generatedContent, 'utf-8');
@@ -364,11 +200,9 @@ export function getBuildMetadata(): Partial<BuildMetadata> {
364
200
  }
365
201
 
366
202
  const AgentNameDuplicateError = StructuredError('AgentNameDuplicateError');
367
- const MetadataMissingError = StructuredError('MetadataMissingError');
368
203
  const MetadataPropertyMissingError = StructuredError('MetadataPropertyMissingError')<{
369
204
  name: string;
370
205
  }>();
371
- const SubAgentMissingError = StructuredError('SubAgentMissingError');
372
206
 
373
207
  const AgentuityBundler: BunPlugin = {
374
208
  name: 'Agentuity Bundler',
@@ -389,69 +223,51 @@ const AgentuityBundler: BunPlugin = {
389
223
  (build.config.define?.['process.env.NODE_ENV']
390
224
  ? JSON.parse(build.config.define['process.env.NODE_ENV'])
391
225
  : 'production') === 'development';
392
- const routes: Set<string> = new Set();
393
226
  const agentInfo: Array<Record<string, string>> = [];
394
227
  const agentMetadata: Map<string, Map<string, string>> = new Map<
395
228
  string,
396
229
  Map<string, string>
397
230
  >();
398
231
  const transpiler = new Bun.Transpiler({ loader: 'ts', target: 'bun' });
399
- let routeDefinitions: BuildMetadata['routes'] = [];
400
-
401
- build.onResolve({ filter: /\/route\.ts$/, namespace: 'file' }, async (args) => {
402
- if (args.path.startsWith(srcDir)) {
403
- const importPath = args.path
404
- .replace(rootDir, '')
405
- .replace('.ts', '')
406
- .replace('/src/', './');
407
- routes.add(importPath);
408
- }
409
- return args;
410
- });
411
-
412
- build.onLoad({ filter: /\/route\.ts$/, namespace: 'file' }, async (args) => {
413
- if (args.path.startsWith(srcDir)) {
414
- const importPath = args.path
415
- .replace(rootDir, '')
416
- .replace('.ts', '')
417
- .replace('/src/', './');
418
- routes.add(importPath);
419
- }
420
- // return undefined to let Bun handle loading normally
421
- return;
422
- });
423
232
 
424
- build.onLoad({ filter: /\/agent\.ts$/, namespace: 'file' }, async (args) => {
233
+ // Scan ALL .ts files in src/agent directory for agents
234
+ build.onLoad({ filter: /\/agent\/.*\.ts$/, namespace: 'file' }, async (args) => {
425
235
  let newsource = await Bun.file(args.path).text();
426
236
  if (args.path.startsWith(srcDir)) {
427
237
  const contents = transpiler.transformSync(newsource);
428
- const [ns, md] = await parseAgentMetadata(
238
+ const result = await parseAgentMetadata(
429
239
  rootDir,
430
240
  args.path,
431
241
  contents,
432
242
  projectId,
433
243
  deploymentId
434
244
  );
435
- newsource = ns;
436
245
 
437
- // Detect if this is a subagent by checking path structure
438
- // Note: Path structure assumption - 4 segments: agent/parent/child/agent.ts
439
- const { isSubagent, parentName } = detectSubagent(args.path, srcDir);
440
- if (isSubagent && parentName) {
441
- md.set('parent', parentName);
246
+ // Skip files that don't have a createAgent export
247
+ if (result === undefined) {
248
+ return {
249
+ contents: newsource,
250
+ loader: 'ts',
251
+ };
442
252
  }
443
253
 
444
- const newAgentName = md.get('name');
445
- for (const [, kv] of agentMetadata) {
446
- const found = kv.get('name');
447
- if (newAgentName === found) {
448
- throw new AgentNameDuplicateError({
449
- message: `The agent in ${kv.get('filename')} and the agent in ${md.get('filename')} have the same name (${found}). Agent Names must be unique within a project.`,
450
- });
254
+ const [ns, md] = result;
255
+ newsource = ns;
256
+
257
+ // Only process files that actually export an agent
258
+ if (md.has('name')) {
259
+ const newAgentName = md.get('name');
260
+ for (const [, kv] of agentMetadata) {
261
+ const found = kv.get('name');
262
+ if (newAgentName === found) {
263
+ throw new AgentNameDuplicateError({
264
+ message: `The agent in ${kv.get('filename')} and the agent in ${md.get('filename')} have the same name (${found}). Agent Names must be unique within a project.`,
265
+ });
266
+ }
451
267
  }
452
- }
453
268
 
454
- agentMetadata.set(md.get('identifier')!, md);
269
+ agentMetadata.set(md.get('name')!, md);
270
+ }
455
271
  }
456
272
  return {
457
273
  contents: newsource,
@@ -509,123 +325,6 @@ const AgentuityBundler: BunPlugin = {
509
325
  await args.defer();
510
326
 
511
327
  const inserts: string[] = [];
512
- const routeMapping: Record<string, string> = {};
513
-
514
- for (const route of routes) {
515
- const name = basename(dirname(route));
516
- const agent = route.replace(/\/route$/, '/agent');
517
- const hasAgent = existsSync(join(srcDir, agent + '.ts'));
518
-
519
- // Detect if this is a subagent route using shared utility
520
- const { isSubagent, parentName } = detectSubagent(route);
521
-
522
- const agentPath = route
523
- .replace(/\/route$/, '/*')
524
- .replace('./', '/');
525
- const routePath = route
526
- .replace(/\/route$/, '')
527
- .replace('/web/', '/api/')
528
- .replace('/web', '/api')
529
- .replace('./', '/');
530
-
531
- const definitions = await parseRoute(
532
- rootDir,
533
- join(srcDir, `${route}.ts`),
534
- projectId,
535
- deploymentId
536
- );
537
-
538
- let agentDetail: Record<string, string> = {};
539
-
540
- if (hasAgent) {
541
- const md = agentMetadata.get(name);
542
- if (!md) {
543
- throw new MetadataMissingError({
544
- message: `Couldn't find agent metadata for ${route}`,
545
- });
546
- }
547
- agentDetail = {
548
- name,
549
- id: md.get('id')!,
550
- path: `.${agent}`,
551
- filename: md.get('filename')!,
552
- identifier: md.get('identifier')!,
553
- description: md.get('description') ?? '',
554
- agentId: md.get('agentId')!,
555
- };
556
- if (isSubagent && parentName) {
557
- agentDetail.parent = parentName;
558
- }
559
- agentInfo.push(agentDetail);
560
- for (const def of definitions) {
561
- def.agentIds = [agentDetail.agentId, agentDetail.id];
562
- }
563
- }
564
-
565
- // do this after handling the agent association (if any)
566
- routeDefinitions = [...routeDefinitions, ...definitions];
567
-
568
- let buffer = `await (async() => {
569
- const { createAgentMiddleware, getRouter, registerAgent } = await import('@agentuity/runtime');
570
- const router = getRouter()!;
571
- const route = require('./src/${route}').default;`;
572
- if (hasAgent) {
573
- const agentRegistrationName =
574
- isSubagent && parentName ? `${parentName}.${name}` : name;
575
- // Build evals path from agent path (e.g., 'agent/eval/agent' -> 'agent/eval/eval.ts')
576
- const agentDirPath = agent.replace(/\/agent$/, '');
577
- const evalsPath = join(srcDir, agentDirPath, 'eval.ts');
578
- const evalsImport = existsSync(evalsPath)
579
- ? `\n require('./src/${agentDirPath}/eval');`
580
- : '';
581
- buffer += `
582
- const agent = require('./src/${agent}').default;
583
- router.all("${agentPath}", createAgentMiddleware('${agentRegistrationName}'));
584
- registerAgent("${agentRegistrationName}", agent);${evalsImport}`;
585
- }
586
- buffer += `
587
- router.route("${routePath}", route);
588
- })();`;
589
- inserts.push(buffer);
590
-
591
- for (const def of definitions) {
592
- routeMapping[`${def.method} ${def.path}`] = def.id;
593
- }
594
- }
595
-
596
- // Register standalone agents (agents without routes)
597
- const routeAgentNames = new Set(
598
- agentInfo
599
- .filter((a) => {
600
- // Check if this agent was added via a route (has a corresponding route file)
601
- const agentPath = a.path.replace(/^\./, '').replace(/\/agent$/, '/route');
602
- return routes.has(agentPath.replace(/^\/src\//, './'));
603
- })
604
- .map((a) => a.name)
605
- );
606
-
607
- for (const agentDetail of agentInfo) {
608
- if (!routeAgentNames.has(agentDetail.name)) {
609
- // This is a standalone agent - register it without a route
610
- const agentPath = agentDetail.path;
611
- const agentDirPath = agentPath.replace(/\/agent$/, '');
612
- const evalsPath = join(srcDir, agentDirPath.replace(/^\./, ''), 'eval.ts');
613
- const evalsImport = existsSync(evalsPath)
614
- ? `\n require('./src/${agentDirPath.replace(/^\.\//, '')}/eval');`
615
- : '';
616
- const isSubagent = !!agentDetail.parent;
617
- const agentRegistrationName = isSubagent
618
- ? `${agentDetail.parent}.${agentDetail.name}`
619
- : agentDetail.name;
620
-
621
- const buffer = `await (async() => {
622
- const { registerAgent } = await import('@agentuity/runtime');
623
- const agent = require('./src${agentPath}').default;
624
- registerAgent("${agentRegistrationName}", agent);${evalsImport}
625
- })();`;
626
- inserts.push(buffer);
627
- }
628
- }
629
328
 
630
329
  const indexFile = join(srcDir, 'web', 'index.html');
631
330
 
@@ -679,39 +378,48 @@ import { readFileSync, existsSync } from 'node:fs';
679
378
  })();`);
680
379
  }
681
380
 
682
- // Add standalone agents (agents without routes) to agentInfo
683
- // These agents can still be called by other agents or routes via ctx.agent
684
- const registeredIdentifiers = new Set(agentInfo.map((a) => a.identifier));
685
- for (const [identifier, md] of agentMetadata) {
686
- if (!registeredIdentifiers.has(identifier)) {
687
- // md.get('filename') can be either absolute or relative to rootDir
688
- const filename = md.get('filename')!;
689
- const absolutePath = filename.startsWith('/')
690
- ? filename
691
- : join(rootDir, filename);
692
-
693
- // Convert to path relative to srcDir like route-based agents
694
- // e.g., /path/to/src/agent/lifecycle/agent.ts -> ./agent/lifecycle/agent
695
- const agentPath = absolutePath.replace(srcDir, '.').replace('.ts', '');
696
-
697
- // Extract folder name as agent name (same as route-based logic)
698
- const folderName = basename(dirname(absolutePath));
699
-
700
- const { isSubagent, parentName } = detectSubagent(absolutePath, srcDir);
701
-
702
- const agentDetail: Record<string, string> = {
703
- name: folderName,
704
- id: md.get('id')!,
705
- path: agentPath,
706
- filename: absolutePath,
707
- identifier: md.get('identifier')!,
708
- description: md.get('description') ?? '',
709
- agentId: md.get('agentId')!,
710
- };
711
- if (isSubagent && parentName) {
712
- agentDetail.parent = parentName;
381
+ // Build agentInfo from all discovered agents and track directories
382
+ const agentDirs = new Set<string>();
383
+ for (const [_identifier, md] of agentMetadata) {
384
+ // md.get('filename') can be either absolute or relative to rootDir
385
+ const filename = md.get('filename')!;
386
+ const absolutePath = filename.startsWith('/') ? filename : join(rootDir, filename);
387
+
388
+ // Track which directories have agents
389
+ const dir = dirname(absolutePath);
390
+ agentDirs.add(dir);
391
+
392
+ // Convert to path relative to srcDir
393
+ // e.g., /path/to/src/agent/my-agent.ts -> ./agent/my-agent
394
+ const agentPath = absolutePath.replace(srcDir, '.').replace('.ts', '');
395
+
396
+ const agentDetail: Record<string, string> = {
397
+ name: md.get('name')!,
398
+ id: md.get('id')!,
399
+ path: agentPath,
400
+ filename: absolutePath,
401
+ description: md.get('description') ?? '',
402
+ agentId: md.get('agentId')!,
403
+ };
404
+ agentInfo.push(agentDetail);
405
+ }
406
+
407
+ // Validate that all directories in src/agent have at least one agent
408
+ const agentBaseDir = join(srcDir, 'agent');
409
+ if (existsSync(agentBaseDir)) {
410
+ const { readdirSync, statSync } = await import('node:fs');
411
+ const subdirs = readdirSync(agentBaseDir).filter((name) => {
412
+ const fullPath = join(agentBaseDir, name);
413
+ return statSync(fullPath).isDirectory();
414
+ });
415
+
416
+ for (const subdir of subdirs) {
417
+ const fullPath = join(agentBaseDir, subdir);
418
+ if (!agentDirs.has(fullPath)) {
419
+ throw new Error(
420
+ `Directory ${subdir} in src/agent must contain at least one agent (a file with a createAgent export)`
421
+ );
713
422
  }
714
- agentInfo.push(agentDetail);
715
423
  }
716
424
  }
717
425
 
@@ -725,6 +433,72 @@ import { readFileSync, existsSync } from 'node:fs';
725
433
  // Generate lifecycle types if setup() is present in app.ts
726
434
  await setupLifecycleTypes(rootDir, outDir, srcDir, logger);
727
435
 
436
+ // Parse routes from src/api directory
437
+ const apiRoutesMetadata: BuildMetadata['routes'] = [];
438
+ const routeInfoList: RouteInfo[] = [];
439
+ const apiDir = join(srcDir, 'api');
440
+ if (existsSync(apiDir)) {
441
+ const { readdirSync, statSync } = await import('node:fs');
442
+ const apiFiles = readdirSync(apiDir)
443
+ .filter((name) => name.endsWith('.ts') && !name.endsWith('.generated.ts'))
444
+ .map((name) => join(apiDir, name));
445
+
446
+ for (const apiFile of apiFiles) {
447
+ if (statSync(apiFile).isFile()) {
448
+ try {
449
+ const routes = await parseRoute(rootDir, apiFile, projectId, deploymentId);
450
+ apiRoutesMetadata.push(...routes);
451
+
452
+ // Collect route info for RouteRegistry generation
453
+ for (const route of routes) {
454
+ routeInfoList.push({
455
+ method: route.method.toUpperCase(),
456
+ path: route.path,
457
+ filename: route.filename,
458
+ hasValidator: route.config?.hasValidator === true,
459
+ routeType: route.type || 'api',
460
+ agentVariable: route.config?.agentVariable as string | undefined,
461
+ agentImportPath: route.config?.agentImportPath as string | undefined,
462
+ inputSchemaVariable: route.config?.inputSchemaVariable as
463
+ | string
464
+ | undefined,
465
+ outputSchemaVariable: route.config?.outputSchemaVariable as
466
+ | string
467
+ | undefined,
468
+ });
469
+ }
470
+ } catch (error) {
471
+ // Skip files that don't have createRouter (they might be utilities)
472
+ if (
473
+ error instanceof Error &&
474
+ error.message.includes('could not find an proper createRouter')
475
+ ) {
476
+ logger.trace(`Skipping ${apiFile}: no createRouter found`);
477
+ } else {
478
+ throw error;
479
+ }
480
+ }
481
+ }
482
+ }
483
+ }
484
+
485
+ // Generate RouteRegistry for type-safe route access
486
+ if (routeInfoList.length > 0) {
487
+ logger.trace(`Generating RouteRegistry with ${routeInfoList.length} routes`);
488
+ generateRouteRegistry(srcDir, routeInfoList);
489
+ }
490
+
491
+ // Auto-mount src/api/index.ts if it exists
492
+ const apiIndexPath = join(srcDir, 'api', 'index.ts');
493
+ if (existsSync(apiIndexPath)) {
494
+ inserts.push(`await (async() => {
495
+ const { getRouter } = await import('@agentuity/runtime');
496
+ const router = getRouter()!;
497
+ const api = require('./src/api/index').default;
498
+ router.route('/api', api);
499
+ })();`);
500
+ }
501
+
728
502
  // Only create the workbench routes if workbench is actually configured
729
503
  if (workbenchConfig) {
730
504
  inserts.push(`await (async() => {
@@ -774,14 +548,11 @@ await (async() => {
774
548
 
775
549
  // generate the build metadata
776
550
  metadata = {
777
- routes: routeDefinitions,
551
+ routes: apiRoutesMetadata,
778
552
  agents: [],
779
553
  };
780
554
 
781
- // Group agents by parent/child relationship
782
- const parentAgentMetadata = new Map<string, Map<string, string>>();
783
- const subagentsByParent = new Map<string, Array<Map<string, string>>>();
784
-
555
+ // Validate required metadata properties and build agent metadata
785
556
  for (const [, v] of agentMetadata) {
786
557
  if (!v.has('filename')) {
787
558
  throw new MetadataPropertyMissingError({
@@ -795,12 +566,6 @@ await (async() => {
795
566
  message: 'agent metadata is missing expected id property',
796
567
  });
797
568
  }
798
- if (!v.has('identifier')) {
799
- throw new MetadataPropertyMissingError({
800
- name: 'identifier',
801
- message: 'agent metadata is missing expected identifier property',
802
- });
803
- }
804
569
  if (!v.has('version')) {
805
570
  throw new MetadataPropertyMissingError({
806
571
  name: 'version',
@@ -819,45 +584,13 @@ await (async() => {
819
584
  message: 'agent metadata is missing expected agentId property',
820
585
  });
821
586
  }
822
-
823
- const parentName = v.get('parent');
824
- if (parentName) {
825
- // This is a subagent
826
- if (!subagentsByParent.has(parentName)) {
827
- subagentsByParent.set(parentName, []);
828
- }
829
- subagentsByParent.get(parentName)!.push(v);
830
- } else {
831
- // This is a parent or standalone agent
832
- parentAgentMetadata.set(v.get('identifier')!, v);
833
- }
834
- }
835
-
836
- // Validate that all subagents reference existing parent agents
837
- for (const [parentName, subagents] of subagentsByParent) {
838
- const parentExists = Array.from(parentAgentMetadata.values()).some(
839
- (meta) => meta.get('name') === parentName || meta.get('identifier') === parentName
840
- );
841
- if (!parentExists) {
842
- const subagentPaths = subagents.map((s) => s.get('filename')).join(', ');
843
- throw new SubAgentMissingError({
844
- message:
845
- `Subagent(s) [${subagentPaths}] reference parent "${parentName}" which does not exist. ` +
846
- `Ensure the parent agent is defined.`,
847
- });
848
- }
849
- }
850
-
851
- // Build metadata with nested subagents
852
- for (const [_identifier, v] of parentAgentMetadata) {
853
587
  const agentData: BuildMetadata['agents'][number] = {
854
588
  filename: v.get('filename')!,
855
589
  id: v.get('id')!,
856
- identifier: v.get('identifier')!,
857
590
  agentId: v.get('agentId')!,
858
591
  version: v.get('version')!,
859
592
  name: v.get('name')!,
860
- description: v.get('description') ?? '<no description provided>',
593
+ description: v.get('description') ?? '',
861
594
  projectId,
862
595
  };
863
596
 
@@ -891,66 +624,9 @@ await (async() => {
891
624
  logger.trace(`[plugin] No evals found for agent ${agentData.name}`);
892
625
  }
893
626
 
894
- // Add subagents if any (check both name and identifier)
895
- const subagents =
896
- subagentsByParent.get(agentData.name) ||
897
- subagentsByParent.get(agentData.identifier);
898
- if (subagents && subagents.length > 0) {
899
- agentData.subagents = subagents.map((sub) => {
900
- const subagentData: BuildMetadata['agents'][number] = {
901
- filename: sub.get('filename')!,
902
- id: sub.get('id')!,
903
- identifier: sub.get('identifier')!,
904
- agentId: sub.get('agentId')!,
905
- version: sub.get('version')!,
906
- name: sub.get('name')!,
907
- description: sub.get('description') ?? '<no description provided>',
908
- projectId,
909
- };
910
-
911
- // Add evals for subagents if any
912
- const subEvalsStr = sub.get('evals');
913
- if (subEvalsStr) {
914
- logger.trace(
915
- `[plugin] Found evals string for subagent ${subagentData.name}, parsing...`
916
- );
917
- try {
918
- const parsedSubEvals = JSON.parse(subEvalsStr) as Array<
919
- Omit<
920
- NonNullable<BuildMetadata['agents'][number]['evals']>[number],
921
- 'agentIdentifier' | 'projectId'
922
- >
923
- >;
924
- subagentData.evals = parsedSubEvals.map((evalItem) => ({
925
- ...evalItem,
926
- agentIdentifier: subagentData.agentId,
927
- projectId,
928
- }));
929
- logger.trace(
930
- `[plugin] Successfully parsed ${subagentData.evals?.length ?? 0} eval(s) for subagent ${subagentData.name}`
931
- );
932
- } catch (e) {
933
- logger.trace(
934
- `[plugin] Failed to parse evals for subagent ${subagentData.name}: ${e}`
935
- );
936
- console.warn(
937
- `Failed to parse evals for subagent ${subagentData.name}: ${e}`
938
- );
939
- }
940
- } else {
941
- logger.trace(`[plugin] No evals found for subagent ${subagentData.name}`);
942
- }
943
-
944
- return subagentData;
945
- });
946
- }
947
-
948
627
  metadata.agents!.push(agentData);
949
628
  }
950
629
 
951
- const routeMappingJSFile = Bun.file(join(outDir, '.routemapping.json'));
952
- await routeMappingJSFile.write(JSON.stringify(routeMapping));
953
-
954
630
  return {
955
631
  contents,
956
632
  loader: 'ts',