@agentuity/cli 0.1.15 → 0.1.16

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 (114) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +15 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/cmd/ai/opencode/install.js +1 -1
  5. package/dist/cmd/ai/opencode/install.js.map +1 -1
  6. package/dist/cmd/cloud/env/delete.d.ts.map +1 -1
  7. package/dist/cmd/cloud/env/delete.js +87 -34
  8. package/dist/cmd/cloud/env/delete.js.map +1 -1
  9. package/dist/cmd/cloud/env/get.d.ts.map +1 -1
  10. package/dist/cmd/cloud/env/get.js +50 -16
  11. package/dist/cmd/cloud/env/get.js.map +1 -1
  12. package/dist/cmd/cloud/env/import.d.ts.map +1 -1
  13. package/dist/cmd/cloud/env/import.js +76 -32
  14. package/dist/cmd/cloud/env/import.js.map +1 -1
  15. package/dist/cmd/cloud/env/index.d.ts.map +1 -1
  16. package/dist/cmd/cloud/env/index.js +6 -2
  17. package/dist/cmd/cloud/env/index.js.map +1 -1
  18. package/dist/cmd/cloud/env/list.d.ts.map +1 -1
  19. package/dist/cmd/cloud/env/list.js +94 -23
  20. package/dist/cmd/cloud/env/list.js.map +1 -1
  21. package/dist/cmd/cloud/env/org-util.d.ts +16 -0
  22. package/dist/cmd/cloud/env/org-util.d.ts.map +1 -0
  23. package/dist/cmd/cloud/env/org-util.js +28 -0
  24. package/dist/cmd/cloud/env/org-util.js.map +1 -0
  25. package/dist/cmd/cloud/env/pull.d.ts.map +1 -1
  26. package/dist/cmd/cloud/env/pull.js +61 -29
  27. package/dist/cmd/cloud/env/pull.js.map +1 -1
  28. package/dist/cmd/cloud/env/push.d.ts.map +1 -1
  29. package/dist/cmd/cloud/env/push.js +69 -23
  30. package/dist/cmd/cloud/env/push.js.map +1 -1
  31. package/dist/cmd/cloud/env/set.d.ts.map +1 -1
  32. package/dist/cmd/cloud/env/set.js +69 -26
  33. package/dist/cmd/cloud/env/set.js.map +1 -1
  34. package/dist/cmd/cloud/keyvalue/create-namespace.js +1 -1
  35. package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
  36. package/dist/cmd/cloud/keyvalue/delete-namespace.js +2 -2
  37. package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
  38. package/dist/cmd/cloud/keyvalue/delete.js +1 -1
  39. package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
  40. package/dist/cmd/cloud/keyvalue/get.js +1 -1
  41. package/dist/cmd/cloud/keyvalue/get.js.map +1 -1
  42. package/dist/cmd/cloud/keyvalue/index.js +1 -1
  43. package/dist/cmd/cloud/keyvalue/index.js.map +1 -1
  44. package/dist/cmd/cloud/keyvalue/keys.js +1 -1
  45. package/dist/cmd/cloud/keyvalue/keys.js.map +1 -1
  46. package/dist/cmd/cloud/keyvalue/list-namespaces.js +1 -1
  47. package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -1
  48. package/dist/cmd/cloud/keyvalue/repl.d.ts.map +1 -1
  49. package/dist/cmd/cloud/keyvalue/repl.js +8 -5
  50. package/dist/cmd/cloud/keyvalue/repl.js.map +1 -1
  51. package/dist/cmd/cloud/keyvalue/search.js +1 -1
  52. package/dist/cmd/cloud/keyvalue/search.js.map +1 -1
  53. package/dist/cmd/cloud/keyvalue/set.js +1 -1
  54. package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
  55. package/dist/cmd/cloud/keyvalue/stats.js +1 -1
  56. package/dist/cmd/cloud/keyvalue/stats.js.map +1 -1
  57. package/dist/cmd/cloud/keyvalue/util.d.ts +4 -4
  58. package/dist/cmd/cloud/keyvalue/util.d.ts.map +1 -1
  59. package/dist/cmd/cloud/keyvalue/util.js +4 -9
  60. package/dist/cmd/cloud/keyvalue/util.js.map +1 -1
  61. package/dist/cmd/project/create.d.ts.map +1 -1
  62. package/dist/cmd/project/create.js +12 -0
  63. package/dist/cmd/project/create.js.map +1 -1
  64. package/dist/cmd/project/template-flow.d.ts +3 -0
  65. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  66. package/dist/cmd/project/template-flow.js +157 -68
  67. package/dist/cmd/project/template-flow.js.map +1 -1
  68. package/dist/cmd/setup/index.d.ts.map +1 -1
  69. package/dist/cmd/setup/index.js +2 -1
  70. package/dist/cmd/setup/index.js.map +1 -1
  71. package/dist/onboarding/agentPrompt.d.ts +8 -0
  72. package/dist/onboarding/agentPrompt.d.ts.map +1 -0
  73. package/dist/onboarding/agentPrompt.js +263 -0
  74. package/dist/onboarding/agentPrompt.js.map +1 -0
  75. package/dist/schema-generator.d.ts +1 -1
  76. package/dist/schema-generator.d.ts.map +1 -1
  77. package/dist/schema-parser.d.ts +1 -1
  78. package/dist/schema-parser.d.ts.map +1 -1
  79. package/dist/schema-parser.js +36 -1
  80. package/dist/schema-parser.js.map +1 -1
  81. package/dist/tui.d.ts.map +1 -1
  82. package/dist/tui.js +17 -7
  83. package/dist/tui.js.map +1 -1
  84. package/package.json +6 -6
  85. package/src/cli.ts +14 -1
  86. package/src/cmd/ai/opencode/install.ts +1 -1
  87. package/src/cmd/cloud/env/delete.ts +100 -41
  88. package/src/cmd/cloud/env/get.ts +53 -16
  89. package/src/cmd/cloud/env/import.ts +86 -37
  90. package/src/cmd/cloud/env/index.ts +6 -2
  91. package/src/cmd/cloud/env/list.ts +102 -27
  92. package/src/cmd/cloud/env/org-util.ts +37 -0
  93. package/src/cmd/cloud/env/pull.ts +67 -31
  94. package/src/cmd/cloud/env/push.ts +81 -28
  95. package/src/cmd/cloud/env/set.ts +82 -33
  96. package/src/cmd/cloud/keyvalue/create-namespace.ts +1 -1
  97. package/src/cmd/cloud/keyvalue/delete-namespace.ts +2 -2
  98. package/src/cmd/cloud/keyvalue/delete.ts +1 -1
  99. package/src/cmd/cloud/keyvalue/get.ts +1 -1
  100. package/src/cmd/cloud/keyvalue/index.ts +1 -1
  101. package/src/cmd/cloud/keyvalue/keys.ts +1 -1
  102. package/src/cmd/cloud/keyvalue/list-namespaces.ts +1 -1
  103. package/src/cmd/cloud/keyvalue/repl.ts +8 -5
  104. package/src/cmd/cloud/keyvalue/search.ts +1 -1
  105. package/src/cmd/cloud/keyvalue/set.ts +1 -1
  106. package/src/cmd/cloud/keyvalue/stats.ts +1 -1
  107. package/src/cmd/cloud/keyvalue/util.ts +8 -17
  108. package/src/cmd/project/create.ts +12 -0
  109. package/src/cmd/project/template-flow.ts +183 -69
  110. package/src/cmd/setup/index.ts +2 -1
  111. package/src/onboarding/agentPrompt.ts +263 -0
  112. package/src/schema-generator.ts +1 -1
  113. package/src/schema-parser.ts +42 -3
  114. package/src/tui.ts +19 -9
@@ -55,6 +55,9 @@ interface CreateFlowOptions {
55
55
  orgId?: string;
56
56
  region?: string;
57
57
  apiClient?: APIClient;
58
+ database?: string;
59
+ storage?: string;
60
+ enableAuth?: boolean;
58
61
  }
59
62
 
60
63
  export interface CreateFlowResult {
@@ -85,8 +88,14 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
85
88
  region,
86
89
  apiClient,
87
90
  domains,
91
+ database: databaseOption,
92
+ storage: storageOption,
93
+ enableAuth: enableAuthOption,
88
94
  } = options;
89
95
 
96
+ const isHeadless = !process.stdin.isTTY || !process.stdout.isTTY;
97
+ const isInteractive = !skipPrompts && !isHeadless;
98
+
90
99
  // Fetch available templates
91
100
  if (templateDir) {
92
101
  tui.info(`📋 Loading templates from local directory: ${templateDir}...\n`);
@@ -120,11 +129,11 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
120
129
  // Create prompt flow
121
130
  const prompt = createPrompt();
122
131
 
123
- if (!skipPrompts) {
132
+ if (isInteractive) {
124
133
  prompt.intro('Create Agentuity Project');
125
134
  }
126
135
 
127
- if (!projectName && !skipPrompts) {
136
+ if (!projectName && isInteractive) {
128
137
  projectName = await prompt.text({
129
138
  message: 'What is the name of your project?',
130
139
  hint: 'The name must be unique for your organization',
@@ -164,8 +173,8 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
164
173
  const destEmpty = destIsDir ? readdirSync(dest).length === 0 : !destExists;
165
174
 
166
175
  if (destExists && !destEmpty && dirName !== '.') {
167
- // In TTY mode, ask if they want to overwrite
168
- if (process.stdin.isTTY && !skipPrompts) {
176
+ // In interactive mode, ask if they want to overwrite
177
+ if (isInteractive) {
169
178
  tui.warning(`Directory ${dest} already exists and is not empty.`, true);
170
179
  console.log(tui.tuiColors.secondary('│'));
171
180
  const overwrite = await prompt.confirm({
@@ -210,7 +219,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
210
219
  return undefined as never;
211
220
  }
212
221
  selectedTemplate = found;
213
- } else if (skipPrompts || templates.length === 1) {
222
+ } else if (!isInteractive || templates.length === 1) {
214
223
  selectedTemplate = templates[0];
215
224
  } else {
216
225
  let maxLength = 15;
@@ -275,7 +284,13 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
275
284
  }
276
285
 
277
286
  // Add separator bar if we're going to show resource prompts
278
- if (!skipPrompts && auth && apiClient && catalystClient && orgId && region) {
287
+ const canProvision = auth && apiClient && catalystClient && orgId && region;
288
+ // Only count as resource flags if actually requesting provisioning (not explicit skip)
289
+ const hasResourceFlags =
290
+ (databaseOption !== undefined && databaseOption.toLowerCase() !== 'skip') ||
291
+ (storageOption !== undefined && storageOption.toLowerCase() !== 'skip');
292
+
293
+ if (isInteractive && canProvision) {
279
294
  const { symbols, tuiColors } = tui;
280
295
  console.log(tuiColors.secondary(symbols.bar));
281
296
  }
@@ -283,43 +298,127 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
283
298
  let _domains = domains;
284
299
  const resourceEnvVars: EnvVars = {};
285
300
 
286
- if (auth && apiClient && catalystClient && orgId && region && !skipPrompts) {
287
- // Fetch resources for selected org and region using Catalyst API
288
- const resources = await tui.spinner({
289
- message: 'Fetching resources',
290
- clearOnSuccess: true,
291
- callback: async () => {
292
- return listResources(catalystClient, orgId, region);
293
- },
294
- });
301
+ // Validate that resource flags require authentication and registration
302
+ if (hasResourceFlags && !canProvision) {
303
+ logger.fatal(
304
+ 'Cannot provision database/storage without being authenticated and registering the project.\n' +
305
+ 'Remove --no-register or omit --database/--storage flags.',
306
+ ErrorCode.VALIDATION_FAILED
307
+ );
308
+ }
295
309
 
296
- logger.debug(`Resources for org ${orgId} in region ${region}:`, resources);
310
+ // Validate that --enable-auth requires authentication and registration
311
+ if (enableAuthOption && !canProvision) {
312
+ logger.fatal(
313
+ 'Cannot enable Agentuity Auth without being authenticated and registering the project.\n' +
314
+ 'Remove --no-register or omit --enable-auth flag.',
315
+ ErrorCode.VALIDATION_FAILED
316
+ );
317
+ }
297
318
 
298
- const db_action = await prompt.select({
299
- message: 'Create SQL Database?',
300
- options: [
301
- { value: 'Skip', label: 'Skip or Setup later' },
302
- { value: 'Create New', label: 'Create a new database' },
303
- ...resources.db.map((db) => ({
304
- value: db.name,
305
- label: `Use database: ${tui.tuiColors.primary(db.name)}`,
306
- })),
307
- ],
308
- });
319
+ if (canProvision) {
320
+ // Fetch resources for selected org and region using Catalyst API (needed for both interactive and CLI flags)
321
+ let resources: Awaited<ReturnType<typeof listResources>> | undefined;
309
322
 
310
- const s3_action = await prompt.select({
311
- message: 'Create Storage Bucket?',
312
- options: [
313
- { value: 'Skip', label: 'Skip or Setup later' },
314
- { value: 'Create New', label: 'Create a new bucket' },
315
- ...resources.s3.map((bucket) => ({
316
- value: bucket.bucket_name,
317
- label: `Use bucket: ${tui.tuiColors.primary(bucket.bucket_name)}`,
318
- })),
319
- ],
320
- });
323
+ const needResources =
324
+ isInteractive ||
325
+ (databaseOption && databaseOption !== 'skip' && databaseOption !== 'new') ||
326
+ (storageOption && storageOption !== 'skip' && storageOption !== 'new');
327
+
328
+ if (needResources) {
329
+ resources = await tui.spinner({
330
+ message: 'Fetching resources',
331
+ clearOnSuccess: true,
332
+ callback: async () => {
333
+ return listResources(catalystClient!, orgId!, region!);
334
+ },
335
+ });
336
+ // Log sanitized summary (avoid exposing DATABASE_URL, tokens, secrets)
337
+ logger.debug(
338
+ `Resources for org ${orgId} in region ${region}: ${resources.db.length} databases, ${resources.s3.length} storage buckets`
339
+ );
340
+ logger.debug(
341
+ `Database names: ${resources.db.map((d) => d.name).join(', ') || '(none)'}`
342
+ );
343
+ logger.debug(
344
+ `Storage buckets: ${resources.s3.map((b) => b.bucket_name).join(', ') || '(none)'}`
345
+ );
346
+ }
347
+
348
+ // Determine database action: CLI flag > interactive prompt > skip (headless)
349
+ let db_action: string;
350
+ if (databaseOption !== undefined) {
351
+ // CLI flag provided - normalize to expected values
352
+ if (databaseOption.toLowerCase() === 'new') {
353
+ db_action = 'Create New';
354
+ } else if (databaseOption.toLowerCase() === 'skip') {
355
+ db_action = 'Skip';
356
+ } else {
357
+ // Existing database name - validate it exists
358
+ const existingDb = resources?.db.find((d) => d.name === databaseOption);
359
+ if (!existingDb) {
360
+ logger.fatal(
361
+ `Database '${databaseOption}' not found. Use 'new' to create a new database or 'skip' to skip.`,
362
+ ErrorCode.RESOURCE_NOT_FOUND
363
+ );
364
+ }
365
+ db_action = databaseOption;
366
+ }
367
+ } else if (isInteractive) {
368
+ db_action = await prompt.select({
369
+ message: 'Create SQL Database?',
370
+ options: [
371
+ { value: 'Skip', label: 'Skip or Setup later' },
372
+ { value: 'Create New', label: 'Create a new database' },
373
+ ...resources!.db.map((db) => ({
374
+ value: db.name,
375
+ label: `Use database: ${tui.tuiColors.primary(db.name)}`,
376
+ })),
377
+ ],
378
+ });
379
+ } else {
380
+ // Headless without flag - skip
381
+ db_action = 'Skip';
382
+ }
321
383
 
322
- if (!domains?.length) {
384
+ // Determine storage action: CLI flag > interactive prompt > skip (headless)
385
+ let s3_action: string;
386
+ if (storageOption !== undefined) {
387
+ // CLI flag provided - normalize to expected values
388
+ if (storageOption.toLowerCase() === 'new') {
389
+ s3_action = 'Create New';
390
+ } else if (storageOption.toLowerCase() === 'skip') {
391
+ s3_action = 'Skip';
392
+ } else {
393
+ // Existing bucket name - validate it exists
394
+ const existingBucket = resources?.s3.find((b) => b.bucket_name === storageOption);
395
+ if (!existingBucket) {
396
+ logger.fatal(
397
+ `Storage bucket '${storageOption}' not found. Use 'new' to create a new bucket or 'skip' to skip.`,
398
+ ErrorCode.RESOURCE_NOT_FOUND
399
+ );
400
+ }
401
+ s3_action = storageOption;
402
+ }
403
+ } else if (isInteractive) {
404
+ s3_action = await prompt.select({
405
+ message: 'Create Storage Bucket?',
406
+ options: [
407
+ { value: 'Skip', label: 'Skip or Setup later' },
408
+ { value: 'Create New', label: 'Create a new bucket' },
409
+ ...resources!.s3.map((bucket) => ({
410
+ value: bucket.bucket_name,
411
+ label: `Use bucket: ${tui.tuiColors.primary(bucket.bucket_name)}`,
412
+ })),
413
+ ],
414
+ });
415
+ } else {
416
+ // Headless without flag - skip
417
+ s3_action = 'Skip';
418
+ }
419
+
420
+ // Custom DNS: only prompt in interactive mode if not already provided
421
+ if (!domains?.length && isInteractive) {
323
422
  const customDns = await prompt.text({
324
423
  message: 'Setup custom DNS?',
325
424
  hint: 'Enter a domain name or press Enter to skip',
@@ -335,14 +434,14 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
335
434
  }
336
435
  }
337
436
 
338
- const choices = { db_action, s3_action };
339
- switch (choices.s3_action) {
437
+ // Process storage action
438
+ switch (s3_action) {
340
439
  case 'Create New': {
341
440
  const created = await tui.spinner({
342
441
  message: 'Provisioning New Bucket',
343
442
  clearOnSuccess: true,
344
443
  callback: async () => {
345
- return createResources(catalystClient, orgId, region!, [{ type: 's3' }]);
444
+ return createResources(catalystClient!, orgId!, region!, [{ type: 's3' }]);
346
445
  },
347
446
  });
348
447
  // Collect env vars from newly created resource
@@ -356,39 +455,49 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
356
455
  }
357
456
  default: {
358
457
  // User selected an existing bucket - get env vars from the resources list
359
- const selectedBucket = resources.s3.find((b) => b.bucket_name === choices.s3_action);
458
+ const selectedBucket = resources?.s3.find((b) => b.bucket_name === s3_action);
360
459
  if (selectedBucket?.env) {
361
460
  Object.assign(resourceEnvVars, selectedBucket.env);
362
461
  }
363
462
  break;
364
463
  }
365
464
  }
366
- switch (choices.db_action) {
465
+
466
+ // Process database action
467
+ switch (db_action) {
367
468
  case 'Create New': {
368
- const dbNameInput = await prompt.text({
369
- message: 'Database name',
370
- hint: 'Optional - lowercase letters, digits, underscores only',
371
- validate: (value: string) => {
372
- const trimmed = value.trim();
373
- if (trimmed === '') return true;
374
- const result = validateDatabaseName(trimmed);
375
- return result.valid ? true : result.error!;
376
- },
377
- });
378
- const dbName = dbNameInput.trim() || undefined;
379
- const dbDescription = await prompt.text({
380
- message: 'Database description',
381
- hint: 'Optional - press Enter to skip',
382
- });
469
+ let dbName: string | undefined;
470
+ let dbDescription: string | undefined;
471
+
472
+ // Only prompt for name/description in interactive mode
473
+ if (isInteractive) {
474
+ const dbNameInput = await prompt.text({
475
+ message: 'Database name',
476
+ hint: 'Optional - lowercase letters, digits, underscores only',
477
+ validate: (value: string) => {
478
+ const trimmed = value.trim();
479
+ if (trimmed === '') return true;
480
+ const result = validateDatabaseName(trimmed);
481
+ return result.valid ? true : result.error!;
482
+ },
483
+ });
484
+ dbName = dbNameInput.trim() || undefined;
485
+ dbDescription =
486
+ (await prompt.text({
487
+ message: 'Database description',
488
+ hint: 'Optional - press Enter to skip',
489
+ })) || undefined;
490
+ }
491
+
383
492
  const created = await tui.spinner({
384
493
  message: 'Provisioning New SQL Database',
385
494
  clearOnSuccess: true,
386
495
  callback: async () => {
387
- return createResources(catalystClient, orgId, region!, [
496
+ return createResources(catalystClient!, orgId!, region!, [
388
497
  {
389
498
  type: 'db',
390
499
  name: dbName,
391
- description: dbDescription || undefined,
500
+ description: dbDescription,
392
501
  },
393
502
  ]);
394
503
  },
@@ -404,7 +513,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
404
513
  }
405
514
  default: {
406
515
  // User selected an existing database - get env vars from the resources list
407
- const selectedDb = resources.db.find((d) => d.name === choices.db_action);
516
+ const selectedDb = resources?.db.find((d) => d.name === db_action);
408
517
  if (selectedDb?.env) {
409
518
  Object.assign(resourceEnvVars, selectedDb.env);
410
519
  }
@@ -413,15 +522,19 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
413
522
  }
414
523
  }
415
524
 
416
- // Auth setup - either from template or user choice
525
+ // Auth setup - either from template, CLI flag, or user choice
417
526
  const templateHasAuth = selectedTemplate.id === 'agentuity-auth';
418
527
 
419
528
  let authEnabled = templateHasAuth; // Auth templates have auth enabled by default
420
529
  let authDatabaseName: string | undefined;
421
530
  let authDatabaseUrl: string | undefined;
422
531
 
423
- // For non-auth templates, ask if they want to enable auth
424
- if (auth && catalystClient && orgId && region && !skipPrompts && !templateHasAuth) {
532
+ // Handle auth enablement: CLI flag > interactive prompt > disabled (headless)
533
+ if (enableAuthOption !== undefined) {
534
+ // CLI flag provided
535
+ authEnabled = enableAuthOption;
536
+ } else if (canProvision && isInteractive && !templateHasAuth) {
537
+ // For non-auth templates in interactive mode, ask if they want to enable auth
425
538
  const enableAuth = await prompt.select({
426
539
  message: 'Enable Agentuity Authentication?',
427
540
  options: [
@@ -434,9 +547,10 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
434
547
  authEnabled = true;
435
548
  }
436
549
  }
550
+ // In headless mode without --enable-auth flag, authEnabled stays false (unless template has auth)
437
551
 
438
552
  // Set up database and secret for any auth-enabled project
439
- if (authEnabled && auth && catalystClient && orgId && region && !skipPrompts) {
553
+ if (authEnabled && canProvision) {
440
554
  // If a database was already selected/created above, use it for auth
441
555
  if (resourceEnvVars.DATABASE_URL) {
442
556
  authDatabaseUrl = resourceEnvVars.DATABASE_URL;
@@ -457,7 +571,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
457
571
  message: 'Provisioning database for auth',
458
572
  clearOnSuccess: true,
459
573
  callback: async () => {
460
- return createResources(catalystClient, orgId, region!, [{ type: 'db' }]);
574
+ return createResources(catalystClient!, orgId!, region!, [{ type: 'db' }]);
461
575
  },
462
576
  });
463
577
  authDatabaseName = created[0].name;
@@ -607,7 +721,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
607
721
  await initGitRepo(dest);
608
722
 
609
723
  // Show completion message
610
- if (!skipPrompts) {
724
+ if (isInteractive) {
611
725
  if (setupResult.success) {
612
726
  tui.success('✨ Project created successfully!\n');
613
727
  } else {
@@ -641,7 +755,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
641
755
 
642
756
  playSound();
643
757
 
644
- if (process.stdin.isTTY && !skipPrompts && _domains?.length && projectId) {
758
+ if (isInteractive && _domains?.length && projectId) {
645
759
  tui.newline();
646
760
  const ok = await tui.confirm('Would you like to configure DNS now?', true);
647
761
  if (ok) {
@@ -4,6 +4,7 @@ import { hasLoggedInBefore } from '../../auth';
4
4
  import { showBanner } from '../../banner';
5
5
  import * as tui from '../../tui';
6
6
  import { getCommand } from '../../command-prefix';
7
+ import { getAgentPromptMarkdown } from '../../onboarding/agentPrompt';
7
8
 
8
9
  const validateToken = /[\d]{7,}\.[\w-_.]{22}/;
9
10
 
@@ -53,7 +54,7 @@ export const command = createCommand({
53
54
  },
54
55
  });
55
56
  if (ok) {
56
- /* TODO */
57
+ process.stdout.write(getAgentPromptMarkdown());
57
58
  return;
58
59
  }
59
60
  }
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Agent Onboarding Prompt
3
+ *
4
+ * This generates the markdown prompt that coding agents use to guide developers
5
+ * through Agentuity setup after the CLI has been installed.
6
+ */
7
+
8
+ export function getAgentPromptMarkdown(): string {
9
+ return `
10
+ **Your goal:** Guide the user from zero to a deployed Agentuity project, then help them understand what they have and what to do next.
11
+
12
+ ---
13
+
14
+ ## What is Agentuity?
15
+
16
+ Agentuity is the full-stack platform for AI agents. Briefly explain to the user:
17
+
18
+ - **Full-stack agents**: Build agents, APIs, and front-end integrations with end-to-end type safety
19
+ - **Bun/TypeScript SDKs**: First-class developer experience with the fastest JavaScript runtime
20
+ - **AI Gateway**: Unified access to LLMs (OpenAI-compatible SDK) with single billing—no separate API keys needed
21
+ - **Observability**: Logs, tracing, and evals built-in so you can debug and improve agents
22
+ - **Production services**: Sandboxes, storage (S3-compatible), Postgres, KV, and enterprise features available as you grow
23
+
24
+ We're going to be setting up the default template quickly.
25
+
26
+ The default template includes AI Gateway integration, thread state persistence, structured logging, Tailwind CSS, and a local Workbench at \`/workbench\` for testing agents.
27
+
28
+ ---
29
+
30
+ ## How to Guide the User
31
+
32
+ **Your job is to make onboarding as smooth as possible with minimal user interaction.**
33
+
34
+ 1. **Start by briefly explaining** what Agentuity is and what you'll help them accomplish
35
+ 2. **Run commands automatically** when you have terminal access—don't ask permission for each step
36
+ 3. **Only pause for human action** when absolutely required
37
+ 4. **At each step**: Briefly explain what it does and why, then execute
38
+ 5. **Celebrate success**: When deployment completes, make sure the user knows they earned free credits and show them what to explore next
39
+
40
+ Assume the user is technical but new to Agentuity. Be warm, efficient, and action-oriented. The goal is a successful deployment with as few interruptions as possible.
41
+
42
+ ---
43
+
44
+ ## Onboarding Steps
45
+
46
+ ### Step 1: You're Already Logged In!
47
+
48
+ **The install command automatically authenticated you.** No manual login needed.
49
+
50
+ Verify with:
51
+ \`\`\`bash
52
+ agentuity auth whoami
53
+ \`\`\`
54
+
55
+ This outputs your user info including your \`org_id\`. You'll need this for the create command.
56
+
57
+ ---
58
+
59
+ ### Step 2: Create a New Project
60
+
61
+ **Why:** This scaffolds a full-stack agent app with AI Gateway, persistence, logging, and a testing UI.
62
+
63
+ **First, get the user's organization ID:**
64
+ \`\`\`bash
65
+ agentuity auth whoami
66
+ \`\`\`
67
+
68
+ This outputs the user's info including their \`org_id\`. You'll need this for the create command.
69
+
70
+ **Then create the project (non-interactive):**
71
+ \`\`\`bash
72
+ agentuity create --name "my-first-agent" --org-id <ORG_ID> --region usc --confirm
73
+ \`\`\`
74
+
75
+ **Required flags for non-interactive mode:**
76
+ - \`--name\`: Project name (use \`my-first-agent\` as default)
77
+ - \`--org-id\`: The organization ID from \`agentuity auth whoami\` (required in non-interactive mode)
78
+ - \`--region\`: Cloud region (use \`usc\` for US Central, or \`use\` for US East)
79
+ - \`--confirm\`: Skips interactive prompts
80
+
81
+ **Example with real org ID:**
82
+ \`\`\`bash
83
+ agentuity create --name "my-first-agent" --org-id ..... --region usc --confirm
84
+ \`\`\`
85
+
86
+ If the user wants a different location, add \`--dir\`:
87
+ \`\`\`bash
88
+ agentuity create --name "my-first-agent" --org-id <ORG_ID> --region usc --dir ~/projects/my-first-agent --confirm
89
+ \`\`\`
90
+
91
+ After creation, \`cd\` into the project directory.
92
+
93
+ **Docs:** https://agentuity.dev/Get-Started/quickstart
94
+
95
+ ---
96
+
97
+ ### Step 3: Run Locally
98
+
99
+ **Why:** Start the development server to test the project locally before deploying.
100
+
101
+ **Command:**
102
+ \`\`\`bash
103
+ agentuity dev
104
+ \`\`\`
105
+
106
+ **⚠️ This is a long-running process!** The dev server runs continuously until killed. Before or after starting it, **immediately tell the user what to do** — don't just wait silently.
107
+
108
+ **What happens:**
109
+ - **Local server** starts at \`http://localhost:3500\` — this is where your APIs and agents are served
110
+ - **Workbench** available at \`http://localhost:3500/workbench\` — a built-in UI for testing and interacting with your agents directly, without needing a frontend
111
+ - **Public tunnel URL** is automatically created — the CLI will display a public URL that tunnels to your local server, useful for testing webhooks or sharing with others
112
+ - **Hot reload** enabled — code changes reflect immediately
113
+
114
+ **Immediately after starting the dev server, tell the user:**
115
+
116
+ > "The dev server is now running! Here's what to try:
117
+ >
118
+ > 1. Open **http://localhost:3500** to see the app
119
+ > 2. Open **http://localhost:3500/workbench** to interact with your agents (this is a key feature!)
120
+ > 3. The public tunnel URL shown in the terminal can be shared with others
121
+ >
122
+ > Take a few minutes to explore. When you're ready to deploy to production, let me know!"
123
+
124
+ **Wait for the user to confirm** they're ready to deploy before proceeding to Step 4.
125
+
126
+ **Keyboard shortcuts** while dev server runs: \`h\` for help, \`r\` to restart, \`q\` to quit.
127
+
128
+ **Docs:** https://agentuity.dev/Reference/CLI/development
129
+
130
+ ---
131
+
132
+ ### Step 4: Deploy to Production
133
+
134
+ **Why:** Make the project live with observability, logs, and analytics.
135
+
136
+ **Important:** Deploying their first project earns the user **free credits** to continue experimenting with the platform. Make sure they know this is a reward for completing onboarding!
137
+
138
+ **First, stop the dev server:**
139
+ Before deploying, you must stop the dev server that's still running from Step 3. Send \`Ctrl+C\` or \`q\` to the terminal running \`agentuity dev\` to gracefully shut it down. Don't leave it running or you'll have an orphaned process on port 3500.
140
+
141
+ **Then deploy:**
142
+ \`\`\`bash
143
+ agentuity deploy
144
+ \`\`\`
145
+
146
+ **What happens:**
147
+ - Project builds and deploys to Agentuity's global edge network
148
+ - The CLI will output the production URL when deployment succeeds
149
+ - Observability (logs, tracing, analytics) is now live
150
+ - **Free credits are awarded** for their first deployment
151
+
152
+ **⚠️ CRITICAL: Do not hallucinate URLs!**
153
+ - **Only show the user URLs that the CLI actually outputs**
154
+ - Do not make up or guess production URLs (e.g., don't invent \`https://project-name.agentuity.app\`)
155
+ - If the deploy command fails or doesn't show a URL, tell the user to check the dashboard instead
156
+ - If there's an error, report the actual error message from the CLI
157
+
158
+ **After successful deployment:**
159
+ 1. Show the user the **exact production URL from the CLI output**
160
+ 2. Direct them to the Agentuity dashboard at https://app.agentuity.com to:
161
+ - View their deployed project
162
+ - Explore logs and traces
163
+ - See their credit balance
164
+ - Monitor analytics
165
+
166
+ **If deployment fails or shows errors:**
167
+ - Report the actual error message
168
+ - Don't claim success if the CLI didn't confirm it
169
+ - Suggest checking the dashboard for deployment status
170
+
171
+ ---
172
+
173
+ ## What the Template Provides
174
+
175
+ After deployment, explain what they now have:
176
+
177
+ | Feature | What It Does |
178
+ |---------|--------------|
179
+ | **AI Gateway** | OpenAI-compatible SDK routed through Agentuity—unified billing, no separate API keys |
180
+ | **Thread State** | Conversations and agent state persist automatically for multi-turn flows |
181
+ | **Structured Logging** | Logs and traces captured for debugging and analytics |
182
+ | **Workbench** | Local UI at \`/workbench\` for testing agents without a frontend |
183
+ | **Tailwind CSS** | Pre-configured styling for the frontend |
184
+ | **Type Safety** | End-to-end types from agent schemas to API routes to frontend |
185
+
186
+ ---
187
+
188
+ ## Next Steps
189
+
190
+ After successful deployment, guide the user through these recommendations:
191
+
192
+ ### 1. Celebrate the Free Credits!
193
+ - Remind the user: their first deployment earned them **free credits** to keep experimenting
194
+ - They can view their credit balance in the Agentuity dashboard
195
+ - These credits let them continue building and testing without upfront cost
196
+
197
+ ### 2. Join the Community
198
+ - **Discord**: https://discord.gg/agentuity
199
+ - Get help, see examples, share what you're building
200
+
201
+ ### 3. Explore the Dashboard
202
+ - **Observability**: View logs, traces, and evals for your deployment
203
+ - **Deployments**: See version history with rollback options
204
+ - **Analytics**: Monitor usage and performance
205
+
206
+ ### 4. Connect GitHub
207
+ - Enable GitHub integration in project settings for:
208
+ - Automatic deployments from branches
209
+ - CI/CD workflows
210
+ - Team collaboration
211
+
212
+ ### 5. Explore Additional Services
213
+ As your project grows, Agentuity provides:
214
+ - **Postgres**: Relational database
215
+ - **KV**: Fast key-value storage
216
+ - **Storage**: S3-compatible file storage
217
+ - **Sandboxes**: Isolated execution environments
218
+
219
+ ### 6. Customize Your Agents
220
+ The SDK makes it easy to:
221
+ - Add new agents in \`src/agent/\` (just add a folder with \`agent.ts\`)
222
+ - Create API routes in \`src/api/\`
223
+ - Build type-safe frontend integrations
224
+
225
+ **Docs:** https://agentuity.dev/Get-Started/project-structure
226
+
227
+ ---
228
+
229
+ ## Handling Common Issues
230
+
231
+ - **Bun not installed**: The CLI will prompt to install it, or run: \`curl -fsSL https://bun.sh/install | bash\`
232
+ - **Login fails/times out**: Rerun \`agentuity login\` and check browser
233
+ - **Port 3500 in use**: Kill the existing process or use \`agentuity dev --port 8080\`
234
+ - **Deploy fails**: Check build errors in CLI output, ensure you're in the project directory
235
+
236
+ ---
237
+
238
+ ## Success Criteria
239
+
240
+ **Onboarding is successful when the user has:**
241
+
242
+ 1. ✅ **CLI installed** and authenticated
243
+ 2. ✅ **Local dev environment running** — they've seen \`localhost:3500\` and tried the Workbench
244
+ 3. ✅ **First deployment complete** — they have a live production URL
245
+ 4. ✅ **Free credits earned** — they know they've been rewarded and can keep building
246
+ 5. ✅ **Clear next steps** — they know about Discord, the dashboard, and how to extend their project
247
+
248
+ **Make them feel accomplished!** They just went from zero to a deployed AI agent platform in minutes.
249
+
250
+ ---
251
+
252
+ ## Style Guidelines
253
+
254
+ - Keep explanations short and practical
255
+ - Explain what & why at each step
256
+ - Give exact commands
257
+ - Handle errors calmly with concrete suggestions
258
+ - Be explicit when human action is required
259
+ - Use non-interactive CLI flags (\`--confirm\`, \`--name\`, \`--org-id\`, \`--region usc\`, \`--dir\`) for smooth automation
260
+ - **Never hallucinate or make up information** — only report URLs, IDs, and status from actual CLI output
261
+ - If something fails or you're unsure, say so honestly and direct the user to the dashboard
262
+ `;
263
+ }
@@ -13,7 +13,7 @@ export interface SchemaArgument {
13
13
 
14
14
  export interface SchemaOption {
15
15
  name: string;
16
- type: 'string' | 'number' | 'boolean' | 'array';
16
+ type: 'string' | 'number' | 'boolean' | 'array' | 'optionalString';
17
17
  required: boolean;
18
18
  default?: unknown;
19
19
  description?: string;