@agentuity/cli 0.0.48 → 0.0.50

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 (282) hide show
  1. package/bin/cli.ts +26 -5
  2. package/dist/banner.d.ts +1 -1
  3. package/dist/banner.d.ts.map +1 -1
  4. package/dist/cli-logger.d.ts +27 -0
  5. package/dist/cli-logger.d.ts.map +1 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cmd/agents/index.d.ts +2 -0
  8. package/dist/cmd/agents/index.d.ts.map +1 -0
  9. package/dist/cmd/auth/index.d.ts.map +1 -1
  10. package/dist/cmd/auth/login.d.ts.map +1 -1
  11. package/dist/cmd/auth/logout.d.ts.map +1 -1
  12. package/dist/cmd/auth/signup.d.ts.map +1 -1
  13. package/dist/cmd/auth/ssh/add.d.ts.map +1 -1
  14. package/dist/cmd/auth/ssh/delete.d.ts.map +1 -1
  15. package/dist/cmd/auth/ssh/index.d.ts +1 -2
  16. package/dist/cmd/auth/ssh/index.d.ts.map +1 -1
  17. package/dist/cmd/auth/ssh/list.d.ts.map +1 -1
  18. package/dist/cmd/auth/whoami.d.ts.map +1 -1
  19. package/dist/cmd/bundle/ast.d.ts +3 -1
  20. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  21. package/dist/cmd/bundle/index.d.ts.map +1 -1
  22. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  23. package/dist/cmd/capabilities/index.d.ts +4 -0
  24. package/dist/cmd/capabilities/index.d.ts.map +1 -0
  25. package/dist/cmd/capabilities/show.d.ts +20 -0
  26. package/dist/cmd/capabilities/show.d.ts.map +1 -0
  27. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  28. package/dist/cmd/cloud/deployment/index.d.ts.map +1 -1
  29. package/dist/cmd/cloud/deployment/list.d.ts.map +1 -1
  30. package/dist/cmd/cloud/deployment/remove.d.ts.map +1 -1
  31. package/dist/cmd/cloud/deployment/rollback.d.ts.map +1 -1
  32. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  33. package/dist/cmd/cloud/deployment/undeploy.d.ts.map +1 -1
  34. package/dist/cmd/cloud/deployment/utils.d.ts +4 -2
  35. package/dist/cmd/cloud/deployment/utils.d.ts.map +1 -1
  36. package/dist/cmd/cloud/domain.d.ts.map +1 -1
  37. package/dist/cmd/cloud/index.d.ts.map +1 -1
  38. package/dist/cmd/cloud/resource/add.d.ts.map +1 -1
  39. package/dist/cmd/cloud/resource/delete.d.ts.map +1 -1
  40. package/dist/cmd/cloud/resource/index.d.ts +1 -2
  41. package/dist/cmd/cloud/resource/index.d.ts.map +1 -1
  42. package/dist/cmd/cloud/resource/list.d.ts.map +1 -1
  43. package/dist/cmd/cloud/scp/download.d.ts.map +1 -1
  44. package/dist/cmd/cloud/scp/index.d.ts +1 -2
  45. package/dist/cmd/cloud/scp/index.d.ts.map +1 -1
  46. package/dist/cmd/cloud/scp/upload.d.ts.map +1 -1
  47. package/dist/cmd/cloud/session/get.d.ts +2 -0
  48. package/dist/cmd/cloud/session/get.d.ts.map +1 -0
  49. package/dist/cmd/cloud/session/index.d.ts +2 -0
  50. package/dist/cmd/cloud/session/index.d.ts.map +1 -0
  51. package/dist/cmd/cloud/session/list.d.ts +2 -0
  52. package/dist/cmd/cloud/session/list.d.ts.map +1 -0
  53. package/dist/cmd/cloud/session/logs.d.ts +2 -0
  54. package/dist/cmd/cloud/session/logs.d.ts.map +1 -0
  55. package/dist/cmd/cloud/ssh.d.ts.map +1 -1
  56. package/dist/cmd/dev/agents.d.ts +2 -0
  57. package/dist/cmd/dev/agents.d.ts.map +1 -0
  58. package/dist/cmd/dev/index.d.ts.map +1 -1
  59. package/dist/cmd/dev/sync.d.ts +12 -0
  60. package/dist/cmd/dev/sync.d.ts.map +1 -0
  61. package/dist/cmd/env/delete.d.ts.map +1 -1
  62. package/dist/cmd/env/get.d.ts.map +1 -1
  63. package/dist/cmd/env/import.d.ts.map +1 -1
  64. package/dist/cmd/env/index.d.ts.map +1 -1
  65. package/dist/cmd/env/list.d.ts.map +1 -1
  66. package/dist/cmd/env/pull.d.ts.map +1 -1
  67. package/dist/cmd/env/push.d.ts.map +1 -1
  68. package/dist/cmd/env/set.d.ts.map +1 -1
  69. package/dist/cmd/index.d.ts.map +1 -1
  70. package/dist/cmd/kv/create-namespace.d.ts +3 -0
  71. package/dist/cmd/kv/create-namespace.d.ts.map +1 -0
  72. package/dist/cmd/kv/delete-namespace.d.ts +3 -0
  73. package/dist/cmd/kv/delete-namespace.d.ts.map +1 -0
  74. package/dist/cmd/kv/delete.d.ts +3 -0
  75. package/dist/cmd/kv/delete.d.ts.map +1 -0
  76. package/dist/cmd/kv/get.d.ts +3 -0
  77. package/dist/cmd/kv/get.d.ts.map +1 -0
  78. package/dist/cmd/kv/index.d.ts +2 -0
  79. package/dist/cmd/kv/index.d.ts.map +1 -0
  80. package/dist/cmd/kv/keys.d.ts +3 -0
  81. package/dist/cmd/kv/keys.d.ts.map +1 -0
  82. package/dist/cmd/kv/list-namespaces.d.ts +3 -0
  83. package/dist/cmd/kv/list-namespaces.d.ts.map +1 -0
  84. package/dist/cmd/kv/repl.d.ts +3 -0
  85. package/dist/cmd/kv/repl.d.ts.map +1 -0
  86. package/dist/cmd/kv/search.d.ts +3 -0
  87. package/dist/cmd/kv/search.d.ts.map +1 -0
  88. package/dist/cmd/kv/set.d.ts +3 -0
  89. package/dist/cmd/kv/set.d.ts.map +1 -0
  90. package/dist/cmd/kv/stats.d.ts +3 -0
  91. package/dist/cmd/kv/stats.d.ts.map +1 -0
  92. package/dist/cmd/kv/util.d.ts +8 -0
  93. package/dist/cmd/kv/util.d.ts.map +1 -0
  94. package/dist/cmd/objectstore/delete-bucket.d.ts +3 -0
  95. package/dist/cmd/objectstore/delete-bucket.d.ts.map +1 -0
  96. package/dist/cmd/objectstore/delete.d.ts +3 -0
  97. package/dist/cmd/objectstore/delete.d.ts.map +1 -0
  98. package/dist/cmd/objectstore/get.d.ts +3 -0
  99. package/dist/cmd/objectstore/get.d.ts.map +1 -0
  100. package/dist/cmd/objectstore/index.d.ts +2 -0
  101. package/dist/cmd/objectstore/index.d.ts.map +1 -0
  102. package/dist/cmd/objectstore/list-buckets.d.ts +3 -0
  103. package/dist/cmd/objectstore/list-buckets.d.ts.map +1 -0
  104. package/dist/cmd/objectstore/list-keys.d.ts +3 -0
  105. package/dist/cmd/objectstore/list-keys.d.ts.map +1 -0
  106. package/dist/cmd/objectstore/put.d.ts +3 -0
  107. package/dist/cmd/objectstore/put.d.ts.map +1 -0
  108. package/dist/cmd/objectstore/repl.d.ts +3 -0
  109. package/dist/cmd/objectstore/repl.d.ts.map +1 -0
  110. package/dist/cmd/objectstore/url.d.ts +3 -0
  111. package/dist/cmd/objectstore/url.d.ts.map +1 -0
  112. package/dist/cmd/objectstore/util.d.ts +8 -0
  113. package/dist/cmd/objectstore/util.d.ts.map +1 -0
  114. package/dist/cmd/profile/create.d.ts.map +1 -1
  115. package/dist/cmd/profile/delete.d.ts.map +1 -1
  116. package/dist/cmd/profile/index.d.ts.map +1 -1
  117. package/dist/cmd/profile/list.d.ts +1 -2
  118. package/dist/cmd/profile/list.d.ts.map +1 -1
  119. package/dist/cmd/profile/show.d.ts.map +1 -1
  120. package/dist/cmd/profile/use.d.ts.map +1 -1
  121. package/dist/cmd/project/create.d.ts.map +1 -1
  122. package/dist/cmd/project/delete.d.ts.map +1 -1
  123. package/dist/cmd/project/index.d.ts.map +1 -1
  124. package/dist/cmd/project/list.d.ts.map +1 -1
  125. package/dist/cmd/project/show.d.ts.map +1 -1
  126. package/dist/cmd/project/template-flow.d.ts +1 -1
  127. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  128. package/dist/cmd/prompt/index.d.ts +4 -0
  129. package/dist/cmd/prompt/index.d.ts.map +1 -0
  130. package/dist/cmd/prompt/llm.d.ts +3 -0
  131. package/dist/cmd/prompt/llm.d.ts.map +1 -0
  132. package/dist/cmd/repl/index.d.ts +3 -0
  133. package/dist/cmd/repl/index.d.ts.map +1 -0
  134. package/dist/cmd/schema/index.d.ts +4 -0
  135. package/dist/cmd/schema/index.d.ts.map +1 -0
  136. package/dist/cmd/schema/show.d.ts +3 -0
  137. package/dist/cmd/schema/show.d.ts.map +1 -0
  138. package/dist/cmd/secret/delete.d.ts.map +1 -1
  139. package/dist/cmd/secret/get.d.ts.map +1 -1
  140. package/dist/cmd/secret/import.d.ts.map +1 -1
  141. package/dist/cmd/secret/index.d.ts.map +1 -1
  142. package/dist/cmd/secret/list.d.ts.map +1 -1
  143. package/dist/cmd/secret/pull.d.ts.map +1 -1
  144. package/dist/cmd/secret/push.d.ts.map +1 -1
  145. package/dist/cmd/secret/set.d.ts.map +1 -1
  146. package/dist/cmd/version/index.d.ts.map +1 -1
  147. package/dist/config.d.ts +1 -1
  148. package/dist/config.d.ts.map +1 -1
  149. package/dist/errors.d.ts +83 -0
  150. package/dist/errors.d.ts.map +1 -0
  151. package/dist/explain.d.ts +47 -0
  152. package/dist/explain.d.ts.map +1 -0
  153. package/dist/index.d.ts +6 -0
  154. package/dist/index.d.ts.map +1 -1
  155. package/dist/json.d.ts +3 -0
  156. package/dist/json.d.ts.map +1 -0
  157. package/dist/output.d.ts +136 -0
  158. package/dist/output.d.ts.map +1 -0
  159. package/dist/repl.d.ts +120 -0
  160. package/dist/repl.d.ts.map +1 -0
  161. package/dist/schema-generator.d.ts +67 -0
  162. package/dist/schema-generator.d.ts.map +1 -0
  163. package/dist/tui.d.ts +35 -1
  164. package/dist/tui.d.ts.map +1 -1
  165. package/dist/types.d.ts +77 -6
  166. package/dist/types.d.ts.map +1 -1
  167. package/dist/utils/format.d.ts +9 -0
  168. package/dist/utils/format.d.ts.map +1 -0
  169. package/package.json +12 -4
  170. package/src/banner.ts +7 -7
  171. package/src/cli-logger.ts +80 -0
  172. package/src/cli.ts +192 -58
  173. package/src/cmd/agents/index.ts +147 -0
  174. package/src/cmd/auth/index.ts +1 -0
  175. package/src/cmd/auth/login.ts +7 -2
  176. package/src/cmd/auth/logout.ts +4 -0
  177. package/src/cmd/auth/signup.ts +7 -2
  178. package/src/cmd/auth/ssh/add.ts +20 -3
  179. package/src/cmd/auth/ssh/delete.ts +57 -4
  180. package/src/cmd/auth/ssh/index.ts +4 -3
  181. package/src/cmd/auth/ssh/list.ts +38 -27
  182. package/src/cmd/auth/whoami.ts +32 -21
  183. package/src/cmd/bundle/ast.test.ts +2 -2
  184. package/src/cmd/bundle/ast.ts +112 -22
  185. package/src/cmd/bundle/index.ts +20 -0
  186. package/src/cmd/bundle/plugin.ts +60 -14
  187. package/src/cmd/capabilities/index.ts +12 -0
  188. package/src/cmd/capabilities/show.ts +256 -0
  189. package/src/cmd/cloud/deploy.ts +54 -0
  190. package/src/cmd/cloud/deployment/index.ts +1 -0
  191. package/src/cmd/cloud/deployment/list.ts +66 -25
  192. package/src/cmd/cloud/deployment/remove.ts +26 -2
  193. package/src/cmd/cloud/deployment/rollback.ts +35 -4
  194. package/src/cmd/cloud/deployment/show.ts +37 -2
  195. package/src/cmd/cloud/deployment/undeploy.ts +12 -1
  196. package/src/cmd/cloud/deployment/utils.ts +5 -2
  197. package/src/cmd/cloud/domain.ts +3 -2
  198. package/src/cmd/cloud/index.ts +10 -1
  199. package/src/cmd/cloud/resource/add.ts +19 -0
  200. package/src/cmd/cloud/resource/delete.ts +24 -3
  201. package/src/cmd/cloud/resource/index.ts +4 -3
  202. package/src/cmd/cloud/resource/list.ts +36 -10
  203. package/src/cmd/cloud/scp/download.ts +27 -1
  204. package/src/cmd/cloud/scp/index.ts +4 -3
  205. package/src/cmd/cloud/scp/upload.ts +27 -1
  206. package/src/cmd/cloud/session/get.ts +164 -0
  207. package/src/cmd/cloud/session/index.ts +11 -0
  208. package/src/cmd/cloud/session/list.ts +145 -0
  209. package/src/cmd/cloud/session/logs.ts +68 -0
  210. package/src/cmd/cloud/ssh.ts +12 -0
  211. package/src/cmd/dev/agents.ts +122 -0
  212. package/src/cmd/dev/index.ts +106 -8
  213. package/src/cmd/dev/sync.ts +414 -0
  214. package/src/cmd/dev/templates.ts +1 -1
  215. package/src/cmd/env/delete.ts +17 -0
  216. package/src/cmd/env/get.ts +17 -1
  217. package/src/cmd/env/import.ts +47 -3
  218. package/src/cmd/env/index.ts +1 -0
  219. package/src/cmd/env/list.ts +13 -1
  220. package/src/cmd/env/pull.ts +20 -0
  221. package/src/cmd/env/push.ts +33 -1
  222. package/src/cmd/env/set.ts +25 -1
  223. package/src/cmd/index.ts +9 -2
  224. package/src/cmd/kv/create-namespace.ts +45 -0
  225. package/src/cmd/kv/delete-namespace.ts +73 -0
  226. package/src/cmd/kv/delete.ts +51 -0
  227. package/src/cmd/kv/get.ts +65 -0
  228. package/src/cmd/kv/index.ts +31 -0
  229. package/src/cmd/kv/keys.ts +57 -0
  230. package/src/cmd/kv/list-namespaces.ts +43 -0
  231. package/src/cmd/kv/repl.ts +284 -0
  232. package/src/cmd/kv/search.ts +80 -0
  233. package/src/cmd/kv/set.ts +63 -0
  234. package/src/cmd/kv/stats.ts +96 -0
  235. package/src/cmd/kv/util.ts +32 -0
  236. package/src/cmd/objectstore/delete-bucket.ts +72 -0
  237. package/src/cmd/objectstore/delete.ts +59 -0
  238. package/src/cmd/objectstore/get.ts +64 -0
  239. package/src/cmd/objectstore/index.ts +27 -0
  240. package/src/cmd/objectstore/list-buckets.ts +45 -0
  241. package/src/cmd/objectstore/list-keys.ts +60 -0
  242. package/src/cmd/objectstore/put.ts +62 -0
  243. package/src/cmd/objectstore/repl.ts +235 -0
  244. package/src/cmd/objectstore/url.ts +59 -0
  245. package/src/cmd/objectstore/util.ts +28 -0
  246. package/src/cmd/profile/create.ts +28 -2
  247. package/src/cmd/profile/delete.ts +17 -2
  248. package/src/cmd/profile/index.ts +1 -0
  249. package/src/cmd/profile/list.ts +7 -3
  250. package/src/cmd/profile/show.ts +20 -5
  251. package/src/cmd/profile/use.ts +8 -0
  252. package/src/cmd/project/create.ts +31 -0
  253. package/src/cmd/project/delete.ts +24 -2
  254. package/src/cmd/project/index.ts +1 -0
  255. package/src/cmd/project/list.ts +24 -10
  256. package/src/cmd/project/show.ts +28 -9
  257. package/src/cmd/project/template-flow.ts +10 -6
  258. package/src/cmd/prompt/index.ts +12 -0
  259. package/src/cmd/prompt/llm.ts +368 -0
  260. package/src/cmd/repl/index.ts +477 -0
  261. package/src/cmd/schema/index.ts +12 -0
  262. package/src/cmd/schema/show.ts +27 -0
  263. package/src/cmd/secret/delete.ts +17 -0
  264. package/src/cmd/secret/get.ts +20 -1
  265. package/src/cmd/secret/import.ts +45 -2
  266. package/src/cmd/secret/index.ts +1 -0
  267. package/src/cmd/secret/list.ts +10 -1
  268. package/src/cmd/secret/pull.ts +20 -0
  269. package/src/cmd/secret/push.ts +33 -1
  270. package/src/cmd/secret/set.ts +20 -0
  271. package/src/cmd/version/index.ts +15 -2
  272. package/src/config.ts +17 -4
  273. package/src/errors.ts +222 -0
  274. package/src/explain.ts +126 -0
  275. package/src/index.ts +51 -0
  276. package/src/json.ts +28 -0
  277. package/src/output.ts +307 -0
  278. package/src/repl.ts +1507 -0
  279. package/src/schema-generator.ts +389 -0
  280. package/src/tui.ts +178 -13
  281. package/src/types.ts +75 -22
  282. package/src/utils/format.ts +17 -0
@@ -1,15 +1,15 @@
1
1
  /** biome-ignore-all lint/style/useTemplate: its easier */
2
2
  import { z } from 'zod';
3
- import type { BuildMetadata } from '@agentuity/server';
4
3
  import { resolve, join } from 'node:path';
5
4
  import { bundle } from '../bundle/bundler';
6
5
  import { getBuildMetadata } from '../bundle/plugin';
7
6
  import { existsSync, type FSWatcher, watch, statSync, readdirSync } from 'node:fs';
8
7
  import {
9
8
  getDefaultConfigDir,
10
- loadDevelopmentProjectSDKKey,
9
+ loadProjectSDKKey,
11
10
  saveProjectDir,
12
11
  saveConfig,
12
+ loadBuildMetadata,
13
13
  } from '../../config';
14
14
  import { type Config, createCommand } from '../../types';
15
15
  import * as tui from '../../tui';
@@ -17,10 +17,22 @@ import { createAgentTemplates, createAPITemplates } from './templates';
17
17
  import { generateEndpoint, type DevmodeResponse } from './api';
18
18
  import { APIClient, getAPIBaseURL } from '../../api';
19
19
  import { download } from './download';
20
+ import { createDevmodeSyncService } from './sync';
21
+ import { getDevmodeDeploymentId } from '../bundle/ast';
22
+ import { BuildMetadata } from '@agentuity/server';
23
+ import { getCommand } from '../../command-prefix';
20
24
 
21
25
  export const command = createCommand({
22
26
  name: 'dev',
23
27
  description: 'Build and run the development server',
28
+ tags: ['mutating', 'slow', 'requires-project'],
29
+ idempotent: true,
30
+ examples: [
31
+ getCommand('dev'),
32
+ getCommand('dev --port 8080'),
33
+ getCommand('dev --local'),
34
+ getCommand('dev --no-public'),
35
+ ],
24
36
  schema: {
25
37
  options: z.object({
26
38
  local: z.boolean().optional().describe('Turn on local services (instead of cloud)'),
@@ -43,6 +55,11 @@ export const command = createCommand({
43
55
  const { opts, logger, options, project, projectDir, auth } = ctx;
44
56
  let { config } = ctx;
45
57
 
58
+ // Allow sync with mock service even without devmode endpoint
59
+ const useMockService = process.env.DEVMODE_SYNC_SERVICE_MOCK === 'true';
60
+ const apiClient = new APIClient(getAPIBaseURL(config), logger, config);
61
+ const syncService = createDevmodeSyncService({ logger, apiClient, mock: useMockService });
62
+
46
63
  const rootDir = projectDir;
47
64
  const appTs = join(rootDir, 'app.ts');
48
65
  const srcDir = join(rootDir, 'src');
@@ -69,12 +86,11 @@ export const command = createCommand({
69
86
  let gravityBin: string | undefined;
70
87
 
71
88
  if (auth && project && opts.public) {
72
- // Only create apiClient if auth is available
73
- const apiClient = new APIClient(getAPIBaseURL(config), logger, config);
89
+ // Generate devmode endpoint only when using --public
74
90
  const endpoint = await tui.spinner({
75
91
  message: 'Connecting to Gravity',
76
92
  callback: () => {
77
- return generateEndpoint(apiClient, project.projectId, config?.devmode?.hostname);
93
+ return generateEndpoint(apiClient!, project.projectId, config?.devmode?.hostname);
78
94
  },
79
95
  clearOnSuccess: true,
80
96
  });
@@ -87,6 +103,13 @@ export const command = createCommand({
87
103
  devmode = endpoint;
88
104
  }
89
105
 
106
+ logger.debug(
107
+ 'Getting devmode deployment id for projectId: %s, endpointId: %s',
108
+ project?.projectId,
109
+ devmode?.id
110
+ );
111
+ const deploymentId = getDevmodeDeploymentId(project?.projectId ?? '', devmode?.id ?? '');
112
+
90
113
  if (devmode) {
91
114
  const configDir = getDefaultConfigDir();
92
115
  const gravityDir = join(configDir, 'gravity');
@@ -179,6 +202,21 @@ export const command = createCommand({
179
202
  const agentuityDir = resolve(rootDir, '.agentuity');
180
203
  const appPath = resolve(agentuityDir, 'app.js');
181
204
 
205
+ // Load existing metadata file to use as previousMetadata for sync
206
+ // This prevents reinserting agents/evals that haven't changed
207
+ let previousMetadata: BuildMetadata | undefined;
208
+ try {
209
+ previousMetadata = await loadBuildMetadata(agentuityDir);
210
+ logger.debug(
211
+ 'Loaded previous metadata with %d agent(s)',
212
+ previousMetadata.agents?.length ?? 0
213
+ );
214
+ } catch (_error) {
215
+ // File doesn't exist yet (first run), that's okay
216
+ logger.debug('No previous metadata file found, will treat all agents/evals as new');
217
+ previousMetadata = undefined;
218
+ }
219
+
182
220
  // Watch directories instead of files to survive atomic replacements (sed -i, cp)
183
221
  const watches = [rootDir];
184
222
  const watchers: FSWatcher[] = [];
@@ -200,7 +238,7 @@ export const command = createCommand({
200
238
  let gravityClient: Bun.Subprocess | undefined;
201
239
 
202
240
  if (gravityBin && devmode && project) {
203
- const sdkKey = await loadDevelopmentProjectSDKKey(rootDir);
241
+ const sdkKey = await loadProjectSDKKey(rootDir);
204
242
  if (!sdkKey) {
205
243
  tui.warning(`Couldn't find the AGENTUITY_SDK_KEY in ${rootDir} .env file`);
206
244
  } else {
@@ -390,6 +428,8 @@ export const command = createCommand({
390
428
  await bundle({
391
429
  rootDir,
392
430
  dev: true,
431
+ projectId: project?.projectId,
432
+ deploymentId,
393
433
  });
394
434
  building = false;
395
435
  buildCompletedAt = Date.now();
@@ -419,6 +459,64 @@ export const command = createCommand({
419
459
  metadata = getBuildMetadata();
420
460
  logger.trace('Build metadata retrieved');
421
461
 
462
+ // Sync agents and evals to API if in devmode with auth
463
+ if (auth && project && apiClient) {
464
+ try {
465
+ logger.debug('Loading build metadata for sync...');
466
+ const currentMetadata = await loadBuildMetadata(agentuityDir);
467
+ logger.debug(
468
+ 'Found %d agent(s) and %d route(s) in metadata',
469
+ currentMetadata.agents?.length ?? 0,
470
+ currentMetadata.routes?.length ?? 0
471
+ );
472
+ if (currentMetadata.agents) {
473
+ for (const agent of currentMetadata.agents) {
474
+ logger.debug(
475
+ 'Agent: id=%s, name=%s, version=%s, evals=%d',
476
+ agent.id,
477
+ agent.name,
478
+ agent.version,
479
+ agent.evals?.length ?? 0
480
+ );
481
+ if (agent.evals) {
482
+ for (const evalItem of agent.evals) {
483
+ logger.debug(
484
+ ' Eval: id=%s, name=%s, version=%s',
485
+ evalItem.id,
486
+ evalItem.name,
487
+ evalItem.version
488
+ );
489
+ }
490
+ }
491
+ }
492
+ }
493
+ logger.debug('Syncing agents and evals...');
494
+
495
+ await syncService.sync(
496
+ currentMetadata,
497
+ previousMetadata,
498
+ project.projectId,
499
+ deploymentId
500
+ );
501
+ previousMetadata = currentMetadata;
502
+ logger.debug('Sync completed successfully');
503
+ } catch (error) {
504
+ logger.error('Failed to sync agents/evals: %s', error);
505
+ if (error instanceof Error) {
506
+ logger.error('Error stack: %s', error.stack);
507
+ }
508
+ // Don't fail the build, just log the error
509
+ }
510
+ } else {
511
+ logger.trace(
512
+ 'Skipping sync - auth=%s, project=%s, devmode=%s, apiClient=%s',
513
+ !!auth,
514
+ !!project,
515
+ !!devmode,
516
+ !!apiClient
517
+ );
518
+ }
519
+
422
520
  logger.trace('Starting dev server: %s', appPath);
423
521
  // Use shell to run in a process group for proper cleanup
424
522
  // The 'exec' ensures the shell is replaced by the actual process
@@ -554,12 +652,12 @@ export const command = createCommand({
554
652
 
555
653
  const showRoutes = () => {
556
654
  tui.info('API Route Detail');
557
- console.table(metadata?.routes, ['method', 'path', 'filename']);
655
+ tui.table(metadata?.routes ?? [], ['method', 'path', 'filename']);
558
656
  };
559
657
 
560
658
  const showAgents = () => {
561
659
  tui.info('Agent Detail');
562
- console.table(metadata?.agents, ['name', 'filename', 'description']);
660
+ tui.table(metadata?.agents ?? [], ['name', 'filename', 'description']);
563
661
  };
564
662
 
565
663
  process.stdin.on('data', (data) => {
@@ -0,0 +1,414 @@
1
+ import { z } from 'zod';
2
+ import type { Logger, BuildMetadata } from '../../types';
3
+ import type { APIClient } from '../../api';
4
+
5
+ interface AgentSyncPayload {
6
+ id: string;
7
+ name: string;
8
+ identifier: string;
9
+ agentId: string;
10
+ description?: string;
11
+ version: string;
12
+ filename: string;
13
+ projectId: string;
14
+ subagents?: Array<{
15
+ id: string;
16
+ name: string;
17
+ identifier: string;
18
+ agentId: string;
19
+ description?: string;
20
+ version: string;
21
+ filename: string;
22
+ projectId: string;
23
+ }>;
24
+ }
25
+
26
+ interface EvalSyncPayload {
27
+ id: string;
28
+ name: string;
29
+ identifier: string;
30
+ evalId: string;
31
+ description?: string;
32
+ version: string;
33
+ filename: string;
34
+ projectId: string;
35
+ agentIdentifier: string;
36
+ }
37
+
38
+ interface IDevmodeSyncService {
39
+ sync(
40
+ currentMetadata: BuildMetadata,
41
+ previousMetadata: BuildMetadata | undefined,
42
+ projectId: string,
43
+ deploymentId: string
44
+ ): Promise<void>;
45
+ }
46
+
47
+ // Shared diff logic for agents
48
+ function getAgentsToSync(
49
+ currentAgents: BuildMetadata['agents'],
50
+ previousAgentIds: Set<string>,
51
+ projectId: string,
52
+ logger: Logger
53
+ ): { create: AgentSyncPayload[]; delete: string[] } {
54
+ const agentsToCreate: AgentSyncPayload[] = [];
55
+ const currentAgentIds = new Set<string>();
56
+
57
+ for (const agent of currentAgents || []) {
58
+ currentAgentIds.add(agent.id);
59
+ // If ID is not in previous, add to create
60
+ if (!previousAgentIds.has(agent.id)) {
61
+ logger.debug(
62
+ '[CLI AGENT SYNC] Preparing to create: id="%s", name="%s"',
63
+ agent.id,
64
+ agent.name
65
+ );
66
+
67
+ // Add projectId to subagents if they exist
68
+ const subagents = agent.subagents?.map((subagent) => {
69
+ return {
70
+ id: subagent.id,
71
+ name: subagent.name,
72
+ identifier: subagent.identifier,
73
+ agentId: subagent.agentId,
74
+ description: subagent.description,
75
+ version: subagent.version,
76
+ filename: subagent.filename,
77
+ projectId,
78
+ };
79
+ });
80
+
81
+ agentsToCreate.push({
82
+ id: agent.id,
83
+ name: agent.name,
84
+ identifier: agent.identifier,
85
+ agentId: agent.agentId,
86
+ description: agent.description,
87
+ version: agent.version,
88
+ filename: agent.filename,
89
+ projectId,
90
+ ...(subagents && { subagents }),
91
+ });
92
+ }
93
+ }
94
+
95
+ // If ID is in previous but not in current, add to delete
96
+ const agentsToDelete: string[] = [];
97
+ for (const previousId of previousAgentIds) {
98
+ if (!currentAgentIds.has(previousId)) {
99
+ logger.debug('[CLI AGENT SYNC] Preparing to delete: id="%s"', previousId);
100
+ agentsToDelete.push(previousId);
101
+ }
102
+ }
103
+
104
+ return { create: agentsToCreate, delete: agentsToDelete };
105
+ }
106
+
107
+ // Shared diff logic for evals
108
+ function getEvalsToSync(
109
+ currentMetadata: BuildMetadata,
110
+ previousEvalIds: Set<string>,
111
+ projectId: string,
112
+ logger: Logger
113
+ ): { create: EvalSyncPayload[]; delete: string[] } {
114
+ const evalsToCreate: EvalSyncPayload[] = [];
115
+ const currentEvalIds = new Set<string>();
116
+
117
+ for (const agent of currentMetadata.agents || []) {
118
+ if (agent.evals) {
119
+ for (const evalItem of agent.evals) {
120
+ currentEvalIds.add(evalItem.id);
121
+ // If ID is not in previous, add to create
122
+ if (!previousEvalIds.has(evalItem.id)) {
123
+ logger.debug(
124
+ '[CLI EVAL SYNC] Preparing to create: id="%s", name="%s"',
125
+ evalItem.id,
126
+ evalItem.name
127
+ );
128
+
129
+ evalsToCreate.push({
130
+ ...evalItem,
131
+ evalId: evalItem.evalId,
132
+ projectId,
133
+ agentIdentifier: agent.agentId,
134
+ });
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ // If ID is in previous but not in current, add to delete
141
+ const evalsToDelete: string[] = [];
142
+ for (const previousId of previousEvalIds) {
143
+ if (!currentEvalIds.has(previousId)) {
144
+ logger.debug('[CLI EVAL SYNC] Preparing to delete: id="%s"', previousId);
145
+ evalsToDelete.push(previousId);
146
+ }
147
+ }
148
+
149
+ return { create: evalsToCreate, delete: evalsToDelete };
150
+ }
151
+
152
+ class DevmodeSyncService implements IDevmodeSyncService {
153
+ constructor(
154
+ private logger: Logger,
155
+ private apiClient: APIClient
156
+ ) {}
157
+
158
+ async sync(
159
+ currentMetadata: BuildMetadata,
160
+ previousMetadata: BuildMetadata | undefined,
161
+ projectId: string,
162
+ deploymentId: string
163
+ ): Promise<void> {
164
+ // Build previous agent IDs set
165
+ const previousAgentIds = new Set<string>();
166
+ if (previousMetadata) {
167
+ this.logger.debug(
168
+ 'Previous metadata found with %d agent(s)',
169
+ previousMetadata.agents?.length ?? 0
170
+ );
171
+ for (const agent of previousMetadata.agents || []) {
172
+ previousAgentIds.add(agent.id);
173
+ }
174
+ } else {
175
+ this.logger.debug('No previous metadata, all agents will be treated as new');
176
+ }
177
+
178
+ // Build previous eval IDs set
179
+ const previousEvalIds = new Set<string>();
180
+ if (previousMetadata) {
181
+ let prevEvalCount = 0;
182
+ for (const agent of previousMetadata.agents || []) {
183
+ if (agent.evals) {
184
+ for (const evalItem of agent.evals) {
185
+ previousEvalIds.add(evalItem.id);
186
+ prevEvalCount++;
187
+ }
188
+ }
189
+ }
190
+ this.logger.debug('Previous metadata found with %d eval(s)', prevEvalCount);
191
+ } else {
192
+ this.logger.debug('No previous metadata, all evals will be treated as new');
193
+ }
194
+
195
+ const currentAgents = currentMetadata.agents || [];
196
+ this.logger.debug('Processing %d current agent(s)', currentAgents.length);
197
+
198
+ let currentEvalCount = 0;
199
+ for (const agent of currentMetadata.agents || []) {
200
+ if (agent.evals) {
201
+ currentEvalCount += agent.evals.length;
202
+ }
203
+ }
204
+ this.logger.debug('Processing %d current eval(s)', currentEvalCount);
205
+
206
+ // Get agents and evals to sync using shared diff logic
207
+ const { create: agentsToCreate, delete: agentsToDelete } = getAgentsToSync(
208
+ currentAgents,
209
+ previousAgentIds,
210
+ projectId,
211
+ this.logger
212
+ );
213
+ const { create: evalsToCreate, delete: evalsToDelete } = getEvalsToSync(
214
+ currentMetadata,
215
+ previousEvalIds,
216
+ projectId,
217
+ this.logger
218
+ );
219
+
220
+ if (agentsToCreate.length > 0 || agentsToDelete.length > 0) {
221
+ this.logger.debug(
222
+ 'Bulk syncing %d agent(s) to create, %d agent(s) to delete',
223
+ agentsToCreate.length,
224
+ agentsToDelete.length
225
+ );
226
+ }
227
+ if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
228
+ this.logger.debug(
229
+ 'Bulk syncing %d eval(s) to create, %d eval(s) to delete',
230
+ evalsToCreate.length,
231
+ evalsToDelete.length
232
+ );
233
+ }
234
+
235
+ // Sync both in parallel
236
+ try {
237
+ await Promise.all([
238
+ this.syncAgents(agentsToCreate, agentsToDelete, deploymentId),
239
+ this.syncEvals(evalsToCreate, evalsToDelete, deploymentId),
240
+ ]);
241
+
242
+ if (agentsToCreate.length > 0 || agentsToDelete.length > 0) {
243
+ this.logger.debug(
244
+ 'Successfully bulk synced %d agent(s) to create, %d agent(s) to delete',
245
+ agentsToCreate.length,
246
+ agentsToDelete.length
247
+ );
248
+ }
249
+ if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
250
+ this.logger.debug(
251
+ 'Successfully bulk synced %d eval(s) to create, %d eval(s) to delete',
252
+ evalsToCreate.length,
253
+ evalsToDelete.length
254
+ );
255
+ }
256
+ } catch (error) {
257
+ this.logger.error('Failed to bulk sync agents/evals: %s', error);
258
+ if (error instanceof Error) {
259
+ this.logger.error('Error details: %s', error.message);
260
+ }
261
+ throw error;
262
+ }
263
+ }
264
+
265
+ private async syncAgents(
266
+ agents: AgentSyncPayload[],
267
+ agentsToDelete: string[],
268
+ deploymentId: string
269
+ ): Promise<void> {
270
+ if (agents.length === 0 && agentsToDelete.length === 0) {
271
+ return;
272
+ }
273
+
274
+ const payload = {
275
+ create: agents,
276
+ delete: agentsToDelete,
277
+ deploymentId,
278
+ };
279
+ this.logger.trace(
280
+ '[CLI AGENT SYNC] Sending payload to POST /cli/devmode/agent: %s',
281
+ JSON.stringify(payload, null, 2)
282
+ );
283
+
284
+ await this.apiClient.request(
285
+ 'POST',
286
+ '/cli/devmode/agent',
287
+ z.object({ success: z.boolean() }),
288
+ payload
289
+ );
290
+ }
291
+
292
+ private async syncEvals(
293
+ evals: EvalSyncPayload[],
294
+ evalsToDelete: string[],
295
+ deploymentId: string
296
+ ): Promise<void> {
297
+ if (evals.length === 0 && evalsToDelete.length === 0) {
298
+ return;
299
+ }
300
+
301
+ const payload = {
302
+ deploymentId,
303
+ create: evals,
304
+ delete: evalsToDelete,
305
+ };
306
+ this.logger.trace(
307
+ '[CLI EVAL SYNC] Sending payload to POST /cli/devmode/eval: %s',
308
+ JSON.stringify(payload, null, 2)
309
+ );
310
+
311
+ await this.apiClient.request(
312
+ 'POST',
313
+ '/cli/devmode/eval',
314
+ z.object({ success: z.boolean() }),
315
+ payload
316
+ );
317
+ }
318
+ }
319
+
320
+ class MockDevmodeSyncService implements IDevmodeSyncService {
321
+ constructor(private logger: Logger) {}
322
+
323
+ async sync(
324
+ currentMetadata: BuildMetadata,
325
+ previousMetadata: BuildMetadata | undefined,
326
+ projectId: string,
327
+ deploymentId: string
328
+ ): Promise<void> {
329
+ // Build previous agent IDs set
330
+ this.logger.debug('Mock syncing agents and evals for deploymentId: %s', deploymentId);
331
+ const previousAgentIds = new Set<string>();
332
+ if (previousMetadata) {
333
+ for (const agent of previousMetadata.agents || []) {
334
+ previousAgentIds.add(agent.id);
335
+ }
336
+ }
337
+
338
+ // Build previous eval IDs set
339
+ const previousEvalIds = new Set<string>();
340
+ if (previousMetadata) {
341
+ for (const agent of previousMetadata.agents || []) {
342
+ if (agent.evals) {
343
+ for (const evalItem of agent.evals) {
344
+ previousEvalIds.add(evalItem.id);
345
+ }
346
+ }
347
+ }
348
+ }
349
+
350
+ // Get agents and evals to sync using shared diff logic
351
+ const { create: agentsToCreate, delete: agentsToDelete } = getAgentsToSync(
352
+ currentMetadata.agents,
353
+ previousAgentIds,
354
+ projectId,
355
+ this.logger
356
+ );
357
+ const { create: evalsToCreate, delete: evalsToDelete } = getEvalsToSync(
358
+ currentMetadata,
359
+ previousEvalIds,
360
+ projectId,
361
+ this.logger
362
+ );
363
+
364
+ // Log the requests that would be made
365
+ if (agentsToCreate.length > 0 || agentsToDelete.length > 0) {
366
+ this.logger.info(
367
+ '[MOCK] Would make request: POST /cli/devmode/agent with %d agent(s) to create, %d agent(s) to delete',
368
+ agentsToCreate.length,
369
+ agentsToDelete.length
370
+ );
371
+ this.logger.info(
372
+ '[MOCK] Request payload: %s',
373
+ JSON.stringify({ create: agentsToCreate, delete: agentsToDelete }, null, 2)
374
+ );
375
+ }
376
+
377
+ if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
378
+ this.logger.info(
379
+ '[MOCK] Would make request: POST /cli/devmode/eval with %d eval(s) to create, %d eval(s) to delete',
380
+ evalsToCreate.length,
381
+ evalsToDelete.length
382
+ );
383
+ this.logger.info(
384
+ '[MOCK] Request payload: %s',
385
+ JSON.stringify({ create: evalsToCreate, delete: evalsToDelete }, null, 2)
386
+ );
387
+ }
388
+
389
+ if (
390
+ agentsToCreate.length === 0 &&
391
+ agentsToDelete.length === 0 &&
392
+ evalsToCreate.length === 0 &&
393
+ evalsToDelete.length === 0
394
+ ) {
395
+ this.logger.info('[MOCK] No requests would be made (no changes detected)');
396
+ }
397
+ }
398
+ }
399
+
400
+ export function createDevmodeSyncService({
401
+ logger,
402
+ apiClient,
403
+ mock = false,
404
+ }: {
405
+ logger: Logger;
406
+ apiClient: APIClient;
407
+ mock?: boolean;
408
+ }): IDevmodeSyncService {
409
+ if (mock) {
410
+ return new MockDevmodeSyncService(logger);
411
+ }
412
+
413
+ return new DevmodeSyncService(logger, apiClient);
414
+ }
@@ -8,7 +8,7 @@ import { z } from 'zod';
8
8
  const agent = createAgent({
9
9
  metadata: {
10
10
  name: '${name}',
11
- description: 'Add my agent description here',
11
+ description: 'Add your agent description here',
12
12
  },
13
13
  schema: {
14
14
  input: z.string(),
@@ -8,16 +8,27 @@ import {
8
8
  writeEnvFile,
9
9
  filterAgentuitySdkKeys,
10
10
  } from '../../env-util';
11
+ import { getCommand } from '../../command-prefix';
12
+
13
+ const EnvDeleteResponseSchema = z.object({
14
+ success: z.boolean().describe('Whether the operation succeeded'),
15
+ key: z.string().describe('Environment variable key that was deleted'),
16
+ path: z.string().describe('Local file path where env var was removed'),
17
+ });
11
18
 
12
19
  export const deleteSubcommand = createSubcommand({
13
20
  name: 'delete',
14
21
  aliases: ['del', 'remove', 'rm'],
15
22
  description: 'Delete an environment variable',
23
+ tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth', 'requires-project'],
24
+ idempotent: true,
25
+ examples: [getCommand('env delete OLD_FEATURE_FLAG'), getCommand('env rm PORT')],
16
26
  requires: { auth: true, project: true, apiClient: true },
17
27
  schema: {
18
28
  args: z.object({
19
29
  key: z.string().describe('the environment variable key to delete'),
20
30
  }),
31
+ response: EnvDeleteResponseSchema,
21
32
  },
22
33
 
23
34
  async handler(ctx) {
@@ -43,5 +54,11 @@ export const deleteSubcommand = createSubcommand({
43
54
  tui.success(
44
55
  `Environment variable '${args.key}' deleted successfully (cloud + ${envFilePath})`
45
56
  );
57
+
58
+ return {
59
+ success: true,
60
+ key: args.key,
61
+ path: envFilePath,
62
+ };
46
63
  },
47
64
  });
@@ -3,10 +3,19 @@ import { createSubcommand } from '../../types';
3
3
  import * as tui from '../../tui';
4
4
  import { projectGet } from '@agentuity/server';
5
5
  import { maskSecret } from '../../env-util';
6
+ import { getCommand } from '../../command-prefix';
7
+ import { ErrorCode } from '../../errors';
8
+
9
+ const EnvGetResponseSchema = z.object({
10
+ key: z.string().describe('Environment variable key name'),
11
+ value: z.string().describe('Environment variable value'),
12
+ });
6
13
 
7
14
  export const getSubcommand = createSubcommand({
8
15
  name: 'get',
9
16
  description: 'Get an environment variable value',
17
+ tags: ['read-only', 'fast', 'requires-auth', 'requires-project'],
18
+ examples: [getCommand('env get NODE_ENV'), getCommand('env get LOG_LEVEL')],
10
19
  requires: { auth: true, project: true, apiClient: true },
11
20
  schema: {
12
21
  args: z.object({
@@ -18,7 +27,9 @@ export const getSubcommand = createSubcommand({
18
27
  .default(false)
19
28
  .describe('mask the value in output (default: true in TTY, false otherwise)'),
20
29
  }),
30
+ response: EnvGetResponseSchema,
21
31
  },
32
+ idempotent: true,
22
33
 
23
34
  async handler(ctx) {
24
35
  const { args, opts, apiClient, project } = ctx;
@@ -32,7 +43,7 @@ export const getSubcommand = createSubcommand({
32
43
  const value = projectData.env?.[args.key];
33
44
 
34
45
  if (value === undefined) {
35
- tui.fatal(`Environment variable '${args.key}' not found`);
46
+ tui.fatal(`Environment variable '${args.key}' not found`, ErrorCode.RESOURCE_NOT_FOUND);
36
47
  }
37
48
 
38
49
  // Display the value, masked if requested
@@ -49,5 +60,10 @@ export const getSubcommand = createSubcommand({
49
60
  console.log(`${args.key}=${value}`);
50
61
  }
51
62
  }
63
+
64
+ return {
65
+ key: args.key,
66
+ value,
67
+ };
52
68
  },
53
69
  });