@agentuity/cli 0.0.55 → 0.0.57

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 (79) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +41 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/cmd/ai/schema/generate.d.ts +3 -0
  5. package/dist/cmd/ai/schema/generate.d.ts.map +1 -0
  6. package/dist/cmd/ai/schema/generate.js +50 -0
  7. package/dist/cmd/ai/schema/generate.js.map +1 -0
  8. package/dist/cmd/ai/schema/index.d.ts.map +1 -1
  9. package/dist/cmd/ai/schema/index.js +2 -1
  10. package/dist/cmd/ai/schema/index.js.map +1 -1
  11. package/dist/cmd/build/ast.d.ts +4 -10
  12. package/dist/cmd/build/ast.d.ts.map +1 -1
  13. package/dist/cmd/build/ast.js +110 -91
  14. package/dist/cmd/build/ast.js.map +1 -1
  15. package/dist/cmd/build/ast.test.js +135 -370
  16. package/dist/cmd/build/ast.test.js.map +1 -1
  17. package/dist/cmd/build/bundler.d.ts +25 -2
  18. package/dist/cmd/build/bundler.d.ts.map +1 -1
  19. package/dist/cmd/build/bundler.js +138 -43
  20. package/dist/cmd/build/bundler.js.map +1 -1
  21. package/dist/cmd/build/plugin.d.ts.map +1 -1
  22. package/dist/cmd/build/plugin.js +16 -8
  23. package/dist/cmd/build/plugin.js.map +1 -1
  24. package/dist/cmd/build/workbench-templates.d.ts +4 -0
  25. package/dist/cmd/build/workbench-templates.d.ts.map +1 -0
  26. package/dist/cmd/build/workbench-templates.js +49 -0
  27. package/dist/cmd/build/workbench-templates.js.map +1 -0
  28. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  29. package/dist/cmd/cloud/deploy.js +11 -3
  30. package/dist/cmd/cloud/deploy.js.map +1 -1
  31. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  32. package/dist/cmd/cloud/deployment/show.js +73 -20
  33. package/dist/cmd/cloud/deployment/show.js.map +1 -1
  34. package/dist/cmd/cloud/session/get.d.ts.map +1 -1
  35. package/dist/cmd/cloud/session/get.js +77 -17
  36. package/dist/cmd/cloud/session/get.js.map +1 -1
  37. package/dist/cmd/index.d.ts.map +1 -1
  38. package/dist/cmd/index.js +2 -0
  39. package/dist/cmd/index.js.map +1 -1
  40. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  41. package/dist/cmd/project/template-flow.js +1 -0
  42. package/dist/cmd/project/template-flow.js.map +1 -1
  43. package/dist/config.d.ts +27 -3
  44. package/dist/config.d.ts.map +1 -1
  45. package/dist/config.js +31 -3
  46. package/dist/config.js.map +1 -1
  47. package/dist/schema-generator.d.ts +1 -1
  48. package/dist/schema-generator.d.ts.map +1 -1
  49. package/dist/schema-parser.d.ts +2 -1
  50. package/dist/schema-parser.d.ts.map +1 -1
  51. package/dist/schema-parser.js +18 -2
  52. package/dist/schema-parser.js.map +1 -1
  53. package/dist/steps.d.ts +2 -1
  54. package/dist/steps.d.ts.map +1 -1
  55. package/dist/steps.js +26 -3
  56. package/dist/steps.js.map +1 -1
  57. package/dist/types.d.ts +39 -2
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/types.js +4 -75
  60. package/dist/types.js.map +1 -1
  61. package/package.json +3 -3
  62. package/src/cli.ts +49 -2
  63. package/src/cmd/ai/schema/generate.ts +64 -0
  64. package/src/cmd/ai/schema/index.ts +2 -1
  65. package/src/cmd/build/ast.test.ts +157 -549
  66. package/src/cmd/build/ast.ts +130 -116
  67. package/src/cmd/build/bundler.ts +157 -42
  68. package/src/cmd/build/plugin.ts +18 -9
  69. package/src/cmd/build/workbench-templates.ts +52 -0
  70. package/src/cmd/cloud/deploy.ts +11 -3
  71. package/src/cmd/cloud/deployment/show.ts +60 -17
  72. package/src/cmd/cloud/session/get.ts +91 -19
  73. package/src/cmd/index.ts +2 -0
  74. package/src/cmd/project/template-flow.ts +1 -0
  75. package/src/config.ts +44 -5
  76. package/src/schema-generator.ts +1 -1
  77. package/src/schema-parser.ts +19 -4
  78. package/src/steps.ts +27 -4
  79. package/src/types.ts +5 -84
@@ -8,8 +8,8 @@ import {
8
8
  parseEvalMetadata,
9
9
  analyzeWorkbench,
10
10
  checkRouteConflicts,
11
- type WorkbenchConfig,
12
11
  } from './ast';
12
+ import type { WorkbenchConfig } from '@agentuity/core';
13
13
  import { applyPatch, generatePatches } from './patch';
14
14
  import { detectSubagent } from '../../utils/detectSubagent';
15
15
  import { createLogger } from '@agentuity/server';
@@ -35,18 +35,18 @@ async function setupWorkbench(srcDir: string): Promise<WorkbenchConfig | null> {
35
35
  const srcAppFile = join(srcDir, 'app.ts');
36
36
 
37
37
  let appFile = '';
38
- if (existsSync(rootAppFile)) {
38
+ if (await Bun.file(rootAppFile).exists()) {
39
39
  appFile = rootAppFile;
40
- } else if (existsSync(srcAppFile)) {
40
+ } else if (await Bun.file(srcAppFile).exists()) {
41
41
  appFile = srcAppFile;
42
42
  }
43
43
 
44
- if (!appFile || !existsSync(appFile)) {
44
+ if (!appFile || !(await Bun.file(appFile).exists())) {
45
45
  return null;
46
46
  }
47
47
 
48
48
  const appContent = await Bun.file(appFile).text();
49
- const analysis = await analyzeWorkbench(appContent);
49
+ const analysis = analyzeWorkbench(appContent);
50
50
 
51
51
  if (!analysis.hasWorkbench) {
52
52
  return null;
@@ -56,7 +56,7 @@ async function setupWorkbench(srcDir: string): Promise<WorkbenchConfig | null> {
56
56
 
57
57
  // Check for route conflicts if workbench is being used
58
58
  if (workbenchConfig?.route) {
59
- const hasConflict = await checkRouteConflicts(appContent, workbenchConfig.route);
59
+ const hasConflict = checkRouteConflicts(appContent, workbenchConfig.route);
60
60
  if (hasConflict) {
61
61
  const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
62
62
  logger.error(`🚨 Route conflict detected!\n`);
@@ -493,7 +493,6 @@ const AgentuityBundler: BunPlugin = {
493
493
  projectId,
494
494
  deploymentId
495
495
  );
496
- routeDefinitions = [...routeDefinitions, ...definitions];
497
496
 
498
497
  let agentDetail: Record<string, string> = {};
499
498
 
@@ -504,6 +503,7 @@ const AgentuityBundler: BunPlugin = {
504
503
  }
505
504
  agentDetail = {
506
505
  name,
506
+ id: md.get('id')!,
507
507
  path: `.${agent}`,
508
508
  filename: md.get('filename')!,
509
509
  identifier: md.get('identifier')!,
@@ -514,8 +514,14 @@ const AgentuityBundler: BunPlugin = {
514
514
  agentDetail.parent = parentName;
515
515
  }
516
516
  agentInfo.push(agentDetail);
517
+ for (const def of definitions) {
518
+ def.agentIds = [agentDetail.agentId, agentDetail.id];
519
+ }
517
520
  }
518
521
 
522
+ // do this after handling the agent association (if any)
523
+ routeDefinitions = [...routeDefinitions, ...definitions];
524
+
519
525
  let buffer = `await (async() => {
520
526
  const { createAgentMiddleware, getRouter, registerAgent } = await import('@agentuity/runtime');
521
527
  const router = getRouter()!;
@@ -567,8 +573,8 @@ const AgentuityBundler: BunPlugin = {
567
573
  // Use the workbench config determined at build time
568
574
  const route = ${JSON.stringify(workbenchConfig?.route || '/workbench')};
569
575
 
570
- // If using custom route, update HTML to point to absolute /workbench/ paths
571
- if (route !== '/workbench') {
576
+ // If using custom route, update HTML to point to absolute /workbench/ paths
577
+ if (route !== '/workbench') {
572
578
  workbenchIndex = workbenchIndex.replace(new RegExp('src="\\\\.\\\\/workbench\\\\/', 'g'), 'src="/workbench/');
573
579
  }
574
580
 
@@ -657,6 +663,9 @@ if (route !== '/workbench') {
657
663
  if (!v.has('name')) {
658
664
  throw new Error('agent metadata is missing expected name property');
659
665
  }
666
+ if (!v.has('agentId')) {
667
+ throw new Error('agent metadata is missing expected agentId property');
668
+ }
660
669
 
661
670
  const parentName = v.get('parent');
662
671
  if (parentName) {
@@ -0,0 +1,52 @@
1
+ import type { WorkbenchConfig } from '@agentuity/core';
2
+
3
+ export function generateWorkbenchMainTsx(config: WorkbenchConfig): string {
4
+ const configString = JSON.stringify(config);
5
+
6
+ return `// Generated workbench entry point
7
+ import React from 'react';
8
+ import { createRoot } from 'react-dom/client';
9
+ import { AgentuityProvider } from '@agentuity/react';
10
+ import { createWorkbench, Workbench } from '@agentuity/workbench';
11
+ import '@agentuity/workbench/styles';
12
+
13
+ // Root element
14
+ const rootElement = document.getElementById('root');
15
+ if (!rootElement) {
16
+ throw new Error('Root element not found');
17
+ }
18
+
19
+ // Create workbench instance with config from bundler
20
+ const workbenchConfig = ${configString};
21
+ const workbench = createWorkbench(workbenchConfig);
22
+
23
+ function App() {
24
+ return (
25
+ <AgentuityProvider baseUrl={window.location.origin}>
26
+ <div className="min-h-screen bg-background text-foreground">
27
+ <Workbench workbench={workbench} />
28
+ </div>
29
+ </AgentuityProvider>
30
+ );
31
+ }
32
+
33
+ // Render the app
34
+ const root = createRoot(rootElement);
35
+ root.render(<App />);
36
+ `;
37
+ }
38
+
39
+ export function generateWorkbenchIndexHtml(): string {
40
+ return `<!DOCTYPE html>
41
+ <html lang="en">
42
+ <head>
43
+ <meta charset="UTF-8">
44
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
45
+ <title>Agentuity Workbench</title>
46
+ </head>
47
+ <body>
48
+ <div id="root"></div>
49
+ <script type="module" src="./main.tsx"></script>
50
+ </body>
51
+ </html>`;
52
+ }
@@ -1,3 +1,4 @@
1
+ import { z } from 'zod';
1
2
  import { join, resolve } from 'node:path';
2
3
  import { createPublicKey } from 'node:crypto';
3
4
  import { createReadStream, createWriteStream } from 'node:fs';
@@ -26,9 +27,9 @@ import {
26
27
  } from '../../env-util';
27
28
  import { zipDir } from '../../utils/zip';
28
29
  import { encryptFIPSKEMDEMStream } from '../../crypto/box';
29
- import { checkCustomDomainForDNS } from './domain';
30
+ import { DeployOptionsSchema } from '../build/bundler';
30
31
  import { getCommand } from '../../command-prefix';
31
- import { z } from 'zod';
32
+ import { checkCustomDomainForDNS } from './domain';
32
33
 
33
34
  const DeployResponseSchema = z.object({
34
35
  success: z.boolean().describe('Whether deployment succeeded'),
@@ -58,17 +59,19 @@ export const deploySubcommand = createSubcommand({
58
59
  examples: [
59
60
  `${getCommand('cloud deploy')} # Deploy current project`,
60
61
  `${getCommand('cloud deploy')} --log-level=debug # Deploy with verbose output`,
62
+ `${getCommand('cloud deploy')} --tag a --tag b # Deploy with specific tags`,
61
63
  ],
62
64
  toplevel: true,
63
65
  idempotent: false,
64
66
  requires: { auth: true, project: true, apiClient: true },
65
67
  prerequisites: ['auth login'],
66
68
  schema: {
69
+ options: DeployOptionsSchema,
67
70
  response: DeployResponseSchema,
68
71
  },
69
72
 
70
73
  async handler(ctx) {
71
- const { project, apiClient, projectDir, config, options } = ctx;
74
+ const { project, apiClient, projectDir, config, options, opts } = ctx;
72
75
 
73
76
  let deployment: Deployment | undefined;
74
77
  let build: BuildMetadata | undefined;
@@ -171,6 +174,11 @@ export const deploySubcommand = createSubcommand({
171
174
  orgId: deployment.orgId,
172
175
  projectId: project.projectId,
173
176
  project,
177
+ commitUrl: opts?.commitUrl,
178
+ logsUrl: opts?.logsUrl,
179
+ provider: opts?.provider,
180
+ trigger: opts?.trigger,
181
+ tag: opts?.tag,
174
182
  });
175
183
  build = await loadBuildMetadata(join(projectDir, '.agentuity'));
176
184
  instructions = await projectDeploymentUpdate(
@@ -15,7 +15,39 @@ const DeploymentShowResponseSchema = z.object({
15
15
  tags: z.array(z.string()).describe('Deployment tags'),
16
16
  customDomains: z.array(z.string()).optional().describe('Custom domains'),
17
17
  cloudRegion: z.string().optional().describe('Cloud region'),
18
- metadata: z.any().optional().describe('Deployment metadata'),
18
+ metadata: z
19
+ .object({
20
+ git: z
21
+ .object({
22
+ repo: z.string().optional(),
23
+ commit: z.string().optional(),
24
+ message: z.string().optional(),
25
+ branch: z.string().optional(),
26
+ url: z.string().optional(),
27
+ trigger: z.string().optional(),
28
+ provider: z.string().optional(),
29
+ event: z.string().optional(),
30
+ buildUrl: z.string().optional(),
31
+ pull_request: z
32
+ .object({
33
+ number: z.number(),
34
+ url: z.string().optional(),
35
+ commentId: z.string().optional(),
36
+ })
37
+ .optional(),
38
+ })
39
+ .optional(),
40
+ build: z
41
+ .object({
42
+ agentuity: z.string().optional(),
43
+ bun: z.string().optional(),
44
+ platform: z.string().optional(),
45
+ arch: z.string().optional(),
46
+ })
47
+ .optional(),
48
+ })
49
+ .optional()
50
+ .describe('Deployment metadata'),
19
51
  });
20
52
 
21
53
  export const showSubcommand = createSubcommand({
@@ -49,8 +81,6 @@ export const showSubcommand = createSubcommand({
49
81
 
50
82
  // Skip TUI output in JSON mode
51
83
  if (!options.json) {
52
- tui.banner(`Deployment ${deployment.id}`, `State: ${deployment.state || 'unknown'}`);
53
-
54
84
  console.log(tui.bold('ID: ') + deployment.id);
55
85
  console.log(tui.bold('Project: ') + projectId);
56
86
  console.log(tui.bold('State: ') + (deployment.state || 'unknown'));
@@ -74,22 +104,35 @@ export const showSubcommand = createSubcommand({
74
104
  console.log(tui.bold('Region: ') + deployment.cloudRegion);
75
105
  }
76
106
 
77
- // Metadata
78
- const origin = deployment.metadata?.origin;
79
- if (origin?.commit) {
107
+ // Git metadata
108
+ const git = deployment.metadata?.git;
109
+ if (git) {
80
110
  tui.newline();
81
- tui.info('Origin Information');
82
- if (origin.trigger) console.log(` Trigger: ${origin.trigger}`);
83
- if (origin.provider) console.log(` Provider: ${origin.provider}`);
84
- if (origin.event) console.log(` Event: ${origin.event}`);
85
- if (origin.branch) console.log(` Branch: ${origin.branch}`);
86
-
87
- if (origin.commit) {
88
- console.log(` Commit: ${origin.commit.hash}`);
89
- if (origin.commit.message) console.log(` Message: ${origin.commit.message}`);
90
- if (origin.commit.author?.name)
91
- console.log(` Author: ${origin.commit.author.name}`);
111
+ tui.info('Git Information');
112
+ if (git.repo) console.log(` Repo: ${git.repo}`);
113
+ if (git.branch) console.log(` Branch: ${git.branch}`);
114
+ if (git.commit) console.log(` Commit: ${git.commit}`);
115
+ if (git.message) console.log(` Message: ${git.message}`);
116
+ if (git.url) console.log(` URL: ${git.url}`);
117
+ if (git.trigger) console.log(` Trigger: ${git.trigger}`);
118
+ if (git.provider) console.log(` Provider: ${git.provider}`);
119
+ if (git.event) console.log(` Event: ${git.event}`);
120
+ if (git.pull_request) {
121
+ console.log(` PR: #${git.pull_request.number}`);
122
+ if (git.pull_request.url) console.log(` PR URL: ${git.pull_request.url}`);
92
123
  }
124
+ if (git.buildUrl) console.log(` Build: ${git.buildUrl}`);
125
+ }
126
+
127
+ // Build metadata
128
+ const build = deployment.metadata?.build;
129
+ if (build) {
130
+ tui.newline();
131
+ tui.info('Build Information');
132
+ if (build.agentuity) console.log(` Agentuity: ${build.agentuity}`);
133
+ if (build.bun) console.log(` Bun: ${build.bun}`);
134
+ if (build.platform) console.log(` Platform: ${build.platform}`);
135
+ if (build.arch) console.log(` Arch: ${build.arch}`);
93
136
  }
94
137
  }
95
138
 
@@ -1,11 +1,29 @@
1
1
  import { z } from 'zod';
2
2
  import { createSubcommand } from '../../../types';
3
3
  import * as tui from '../../../tui';
4
- import { sessionGet } from '@agentuity/server';
4
+ import { sessionGet, type SpanNode } from '@agentuity/server';
5
5
  import { getCommand } from '../../../command-prefix';
6
6
  import { ErrorCode } from '../../../errors';
7
7
  import { getCatalystAPIClient } from '../../../config';
8
8
 
9
+ const SpanNodeSchema: z.ZodType<SpanNode> = z.lazy(() =>
10
+ z.object({
11
+ id: z.string().describe('Span ID'),
12
+ duration: z.number().describe('Duration in milliseconds'),
13
+ operation: z.string().describe('Operation name'),
14
+ attributes: z.record(z.string(), z.unknown()).describe('Span attributes'),
15
+ children: z.array(SpanNodeSchema).optional().describe('Child spans'),
16
+ })
17
+ );
18
+
19
+ const RouteInfoSchema = z
20
+ .object({
21
+ id: z.string().describe('Route ID'),
22
+ method: z.string().describe('HTTP method'),
23
+ path: z.string().describe('Route path'),
24
+ })
25
+ .nullable();
26
+
9
27
  const SessionGetResponseSchema = z.object({
10
28
  id: z.string().describe('Session ID'),
11
29
  created_at: z.string().describe('Creation timestamp'),
@@ -26,8 +44,15 @@ const SessionGetResponseSchema = z.object({
26
44
  url: z.string().describe('Request URL'),
27
45
  route_id: z.string().describe('Route ID'),
28
46
  thread_id: z.string().describe('Thread ID'),
29
- agentNames: z.array(z.string()).describe('Agent names'),
30
- evalRuns: z
47
+ agents: z
48
+ .array(
49
+ z.object({
50
+ name: z.string(),
51
+ identifier: z.string(),
52
+ })
53
+ )
54
+ .describe('Agents'),
55
+ eval_runs: z
31
56
  .array(
32
57
  z.object({
33
58
  id: z.string(),
@@ -40,8 +65,41 @@ const SessionGetResponseSchema = z.object({
40
65
  })
41
66
  )
42
67
  .describe('Eval runs'),
68
+ timeline: SpanNodeSchema.nullable().optional().describe('Session timeline'),
69
+ route: RouteInfoSchema.optional().describe('Route information'),
43
70
  });
44
71
 
72
+ function formatDuration(ms: number): string {
73
+ if (ms < 1) {
74
+ return `${(ms * 1000).toFixed(0)}µs`;
75
+ }
76
+ if (ms < 1000) {
77
+ return `${ms.toFixed(1)}ms`;
78
+ }
79
+ return `${(ms / 1000).toFixed(2)}s`;
80
+ }
81
+
82
+ function printTimeline(node: SpanNode, prefix: string, isLast = true): void {
83
+ const connector = isLast ? '└── ' : '├── ';
84
+ const duration = tui.muted(`(${formatDuration(node.duration)})`);
85
+ let extra = '';
86
+ if (node.operation.startsWith('agentuity.')) {
87
+ if ('name' in node.attributes && 'key' in node.attributes) {
88
+ extra = tui.colorSuccess(`${node.attributes.name} ${node.attributes.key}`) + ' ';
89
+ }
90
+ }
91
+ if (node.operation.startsWith('HTTP ') && 'http.url' in node.attributes) {
92
+ extra = `${tui.colorSuccess(node.attributes['http.url'] as string)} `;
93
+ }
94
+ console.log(`${prefix}${connector}${node.operation} ${extra}${duration}`);
95
+
96
+ const childPrefix = prefix + (isLast ? ' ' : '│ ');
97
+ const children = node.children ?? [];
98
+ children.forEach((child, index) => {
99
+ printTimeline(child, childPrefix, index === children.length - 1);
100
+ });
101
+ }
102
+
45
103
  export const getSubcommand = createSubcommand({
46
104
  name: 'get',
47
105
  description: 'Get details about a specific session',
@@ -83,8 +141,8 @@ export const getSubcommand = createSubcommand({
83
141
  url: session.url,
84
142
  route_id: session.route_id,
85
143
  thread_id: session.thread_id,
86
- agentNames: enriched.agentNames,
87
- evalRuns: enriched.evalRuns.map((run) => ({
144
+ agents: enriched.agents,
145
+ eval_runs: enriched.evalRuns.map((run) => ({
88
146
  id: run.id,
89
147
  eval_id: run.eval_id,
90
148
  created_at: run.created_at,
@@ -93,6 +151,8 @@ export const getSubcommand = createSubcommand({
93
151
  error: run.error,
94
152
  result: run.result,
95
153
  })),
154
+ timeline: enriched.timeline,
155
+ route: enriched.route,
96
156
  };
97
157
 
98
158
  if (options.json) {
@@ -100,17 +160,14 @@ export const getSubcommand = createSubcommand({
100
160
  return result;
101
161
  }
102
162
 
103
- tui.banner(`Session ${session.id}`, `Status: ${session.success ? 'Success' : 'Failed'}`);
104
-
105
163
  console.log(tui.bold('ID: ') + session.id);
106
164
  console.log(tui.bold('Project: ') + session.project_id);
107
165
  console.log(tui.bold('Deployment: ') + (session.deployment_id || '-'));
108
- console.log(tui.bold('Created: ') + new Date(session.created_at).toLocaleString());
109
166
  console.log(tui.bold('Start: ') + new Date(session.start_time).toLocaleString());
110
167
  if (session.end_time) {
111
168
  console.log(tui.bold('End: ') + new Date(session.end_time).toLocaleString());
112
169
  }
113
- if (session.duration) {
170
+ if (session.duration && session.end_time) {
114
171
  console.log(
115
172
  tui.bold('Duration: ') + `${(session.duration / 1_000_000).toFixed(0)}ms`
116
173
  );
@@ -118,23 +175,32 @@ export const getSubcommand = createSubcommand({
118
175
  console.log(tui.bold('Method: ') + session.method);
119
176
  console.log(tui.bold('URL: ') + tui.link(session.url, session.url));
120
177
  console.log(tui.bold('Trigger: ') + session.trigger);
121
- console.log(tui.bold('Environment: ') + session.env);
178
+ if (session.env !== 'production') {
179
+ console.log(tui.bold('Environment: ') + session.env);
180
+ }
122
181
  console.log(tui.bold('Dev Mode: ') + (session.devmode ? 'Yes' : 'No'));
123
- console.log(tui.bold('Success: ') + (session.success ? '✓' : '✗'));
182
+ console.log(
183
+ tui.bold('Success: ') +
184
+ (session.success ? tui.colorSuccess('✓') : tui.colorError('✗'))
185
+ );
124
186
  console.log(tui.bold('Pending: ') + (session.pending ? 'Yes' : 'No'));
125
187
  if (session.error) {
126
188
  console.log(tui.bold('Error: ') + tui.error(session.error));
127
189
  }
128
- if (enriched.agentNames.length > 0) {
129
- const agentDisplay = enriched.agentNames
130
- .map((name, idx) => {
131
- const agentId = session.agent_ids[idx];
132
- return `${name} ${tui.muted(`(${agentId})`)}`;
133
- })
190
+ if (enriched.agents.length > 0) {
191
+ const agentDisplay = enriched.agents
192
+ .map((agent) => `${agent.name} ${tui.muted(`(${agent.identifier})`)}`)
134
193
  .join(', ');
135
194
  console.log(tui.bold('Agents: ') + agentDisplay);
136
195
  }
137
- console.log(tui.bold('Route ID: ') + session.route_id);
196
+ if (enriched.route) {
197
+ console.log(
198
+ tui.bold('Route: ') +
199
+ `${enriched.route.method.toUpperCase()} ${enriched.route.path} ${tui.muted(`(${enriched.route.id})`)}`
200
+ );
201
+ } else {
202
+ console.log(tui.bold('Route ID: ') + session.route_id);
203
+ }
138
204
  console.log(tui.bold('Thread ID: ') + session.thread_id);
139
205
 
140
206
  if (enriched.evalRuns.length > 0) {
@@ -143,7 +209,7 @@ export const getSubcommand = createSubcommand({
143
209
  const evalTableData = enriched.evalRuns.map((run) => ({
144
210
  ID: run.id,
145
211
  'Eval ID': run.eval_id,
146
- Success: run.success ? '✓' : '✗',
212
+ Success: run.success ? tui.colorSuccess('✓') : tui.colorError('✗'),
147
213
  Pending: run.pending ? '⏳' : '✓',
148
214
  Error: run.error || 'No',
149
215
  Created: new Date(run.created_at).toLocaleString(),
@@ -159,6 +225,12 @@ export const getSubcommand = createSubcommand({
159
225
  ]);
160
226
  }
161
227
 
228
+ if (result.timeline) {
229
+ console.log('');
230
+ console.log(tui.bold('Timeline:'));
231
+ printTimeline(result.timeline, '');
232
+ }
233
+
162
234
  return result;
163
235
  } catch (ex) {
164
236
  tui.fatal(
package/src/cmd/index.ts CHANGED
@@ -38,6 +38,8 @@ export async function discoverCommands(): Promise<CommandDefinition[]> {
38
38
  prerequisites: (subcommand as any).prerequisites,
39
39
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
40
  tags: (subcommand as any).tags,
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ examples: (subcommand as any).examples,
41
43
  };
42
44
  commands.push(alias as CommandDefinition);
43
45
  }
@@ -325,6 +325,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
325
325
  deployment: {
326
326
  resources: resourceConfig,
327
327
  },
328
+ region: region ?? 'usc',
328
329
  });
329
330
  },
330
331
  });
package/src/config.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { existsSync } from 'node:fs';
2
+ import { existsSync, mkdirSync } from 'node:fs';
3
3
  import type { Logger } from '@agentuity/core';
4
4
  import {
5
5
  BuildMetadataSchema,
@@ -486,6 +486,7 @@ class ProjectConfigNotFoundExpection extends Error {
486
486
  type ProjectConfig = z.infer<typeof ProjectSchema>;
487
487
 
488
488
  function generateJSON5WithComments(
489
+ jsonSchema: string,
489
490
  schema: z.ZodObject<z.ZodRawShape>,
490
491
  data: Record<string, unknown>
491
492
  ): string {
@@ -493,6 +494,8 @@ function generateJSON5WithComments(
493
494
  const shape = schema.shape;
494
495
  const keys = Object.keys(shape);
495
496
 
497
+ lines.push(` "$schema": "${jsonSchema}",`);
498
+
496
499
  for (let i = 0; i < keys.length; i++) {
497
500
  const key = keys[i];
498
501
  const fieldSchema = shape[key] as z.ZodTypeAny;
@@ -551,23 +554,59 @@ export async function loadProjectConfig(
551
554
  return result.data;
552
555
  }
553
556
 
554
- type InitialProjectConfig = ProjectConfig & {
555
- sdkKey: string;
556
- };
557
+ export const InitialProjectConfigSchema = z.intersection(
558
+ ProjectSchema,
559
+ z.object({
560
+ sdkKey: z.string().describe('the project specific SDK key'),
561
+ $schema: z.string().optional(),
562
+ })
563
+ );
564
+
565
+ type InitialProjectConfig = z.infer<typeof InitialProjectConfigSchema>;
557
566
 
558
567
  export async function createProjectConfig(dir: string, config: InitialProjectConfig) {
559
568
  const { sdkKey, ...sanitizedConfig } = config;
560
569
 
570
+ // generate the project config
561
571
  const configPath = join(dir, 'agentuity.json');
562
- const json5Content = generateJSON5WithComments(ProjectSchema, sanitizedConfig);
572
+ const json5Content = generateJSON5WithComments(
573
+ 'https://agentuity.dev/schema/cli/v1/agentuity.json',
574
+ ProjectSchema,
575
+ sanitizedConfig
576
+ );
563
577
  await Bun.write(configPath, json5Content + '\n');
564
578
 
579
+ // generate the .env file with initial secret
565
580
  const envPath = join(dir, '.env');
566
581
  const comment =
567
582
  '# AGENTUITY_SDK_KEY is a sensitive value and should not be committed to version control.';
568
583
  const content = `${comment}\nAGENTUITY_SDK_KEY=${sdkKey}\n`;
569
584
  await Bun.write(envPath, content);
570
585
  await chmod(envPath, 0o600);
586
+
587
+ // generate the vscode settings
588
+ const vscodeDir = join(dir, '.vscode');
589
+ mkdirSync(vscodeDir);
590
+
591
+ const settings = {
592
+ 'files.associations': {
593
+ 'agentuity.json': 'jsonc',
594
+ },
595
+ 'search.exclude': {
596
+ '**/.git/**': true,
597
+ '**/node_modules/**': true,
598
+ '**/bun.lock': true,
599
+ '**/.agentuity/**': true,
600
+ },
601
+ 'json.schemas': [
602
+ {
603
+ fileMatch: ['agentuity.json'],
604
+ url: 'https://agentuity.dev/schema/cli/v1/agentuity.json',
605
+ },
606
+ ],
607
+ };
608
+
609
+ await Bun.write(join(vscodeDir, 'settings.json'), JSON.stringify(settings, null, 2));
571
610
  }
572
611
 
573
612
  export async function loadBuildMetadata(dir: string): Promise<BuildMetadata> {
@@ -13,7 +13,7 @@ export interface SchemaArgument {
13
13
 
14
14
  export interface SchemaOption {
15
15
  name: string;
16
- type: 'string' | 'number' | 'boolean';
16
+ type: 'string' | 'number' | 'boolean' | 'array';
17
17
  required: boolean;
18
18
  default?: unknown;
19
19
  description?: string;
@@ -13,9 +13,10 @@ export interface ParsedArgs {
13
13
  export interface ParsedOption {
14
14
  name: string;
15
15
  description?: string;
16
- type: 'string' | 'number' | 'boolean';
16
+ type: 'string' | 'number' | 'boolean' | 'array';
17
17
  hasDefault?: boolean;
18
18
  defaultValue?: unknown;
19
+ enumValues?: string[];
19
20
  }
20
21
 
21
22
  interface ZodTypeDef {
@@ -167,11 +168,21 @@ export function parseOptionsSchema(schema: ZodType): ParsedOption[] {
167
168
  ? (defaultInfo.defaultValue as () => unknown)()
168
169
  : defaultInfo.defaultValue;
169
170
 
170
- let type: 'string' | 'number' | 'boolean' = 'string';
171
+ let type: 'string' | 'number' | 'boolean' | 'array' = 'string';
172
+ let enumValues: string[] | undefined;
171
173
  if (typeId === 'ZodNumber' || typeId === 'number') {
172
174
  type = 'number';
173
175
  } else if (typeId === 'ZodBoolean' || typeId === 'boolean') {
174
176
  type = 'boolean';
177
+ } else if (typeId === 'ZodArray' || typeId === 'array') {
178
+ type = 'array';
179
+ } else if (typeId === 'ZodEnum' || typeId === 'enum') {
180
+ // Extract enum values from Zod 4's def.entries
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ const def = (unwrapped as any)?._def;
183
+ if (def?.entries && typeof def.entries === 'object') {
184
+ enumValues = Object.values(def.entries as Record<string, string>);
185
+ }
175
186
  }
176
187
 
177
188
  options.push({
@@ -180,6 +191,7 @@ export function parseOptionsSchema(schema: ZodType): ParsedOption[] {
180
191
  description,
181
192
  hasDefault: defaultInfo.hasDefault,
182
193
  defaultValue,
194
+ enumValues,
183
195
  });
184
196
  }
185
197
 
@@ -203,8 +215,11 @@ export function buildValidationInput(
203
215
  if (schemas.options) {
204
216
  const parsed = parseOptionsSchema(schemas.options);
205
217
  for (const opt of parsed) {
206
- // Always include the option value (even if undefined) so zod can apply defaults
207
- result.options[opt.name] = rawOptions[opt.name];
218
+ // Only include the option if it has a value - omitting undefined allows Zod to apply defaults
219
+ const value = rawOptions[opt.name];
220
+ if (value !== undefined) {
221
+ result.options[opt.name] = value;
222
+ }
208
223
  }
209
224
  }
210
225