@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
@@ -0,0 +1,80 @@
1
+ import type { Logger, GlobalOptions } from './types';
2
+ import { exitWithError, type StructuredError, ErrorCode, createError, getExitCode } from './errors';
3
+
4
+ /**
5
+ * Enhanced logger wrapper that supports structured errors
6
+ */
7
+ export class CLILogger implements Logger {
8
+ constructor(
9
+ private logger: Logger,
10
+ private options: GlobalOptions
11
+ ) {}
12
+
13
+ /**
14
+ * Exit with a structured error (supports --error-format=json)
15
+ */
16
+ fatalWithError(error: StructuredError): never {
17
+ const errorFormat = this.options.errorFormat ?? 'text';
18
+ return exitWithError(error, this.logger, errorFormat);
19
+ }
20
+
21
+ // Delegate all other logger methods
22
+ trace(message: string, ...args: unknown[]): void {
23
+ this.logger.trace(message, ...args);
24
+ }
25
+
26
+ debug(message: string, ...args: unknown[]): void {
27
+ this.logger.debug(message, ...args);
28
+ }
29
+
30
+ info(message: string, ...args: unknown[]): void {
31
+ this.logger.info(message, ...args);
32
+ }
33
+
34
+ warn(message: string, ...args: unknown[]): void {
35
+ this.logger.warn(message, ...args);
36
+ }
37
+
38
+ error(message: string, ...args: unknown[]): void {
39
+ this.logger.error(message, ...args);
40
+ }
41
+
42
+ fatal(message: string, errorCode?: ErrorCode, ...args: unknown[]): never {
43
+ if (errorCode) {
44
+ // Use structured error with proper exit code
45
+ const formattedMessage = args.length > 0 ? this.formatMessage(message, ...args) : message;
46
+ const error = createError(errorCode, formattedMessage);
47
+ const exitCode = getExitCode(errorCode);
48
+
49
+ if (this.options.errorFormat === 'json') {
50
+ exitWithError(error, this.logger, 'json');
51
+ } else {
52
+ this.logger.error(formattedMessage);
53
+ process.exit(exitCode);
54
+ }
55
+ } else {
56
+ // Fallback to default behavior (exit code 1)
57
+ this.logger.fatal(message, ...args);
58
+ }
59
+ }
60
+
61
+ private formatMessage(message: string, ...args: unknown[]): string {
62
+ // Simple sprintf-style formatting
63
+ let formatted = message;
64
+ for (const arg of args) {
65
+ formatted = formatted.replace(/%[sd]/, String(arg));
66
+ }
67
+ return formatted;
68
+ }
69
+
70
+ child(opts: Record<string, unknown>): Logger {
71
+ return this.logger.child(opts);
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Wrap a logger with CLI error handling capabilities
77
+ */
78
+ export function wrapLogger(logger: Logger, options: GlobalOptions): CLILogger {
79
+ return new CLILogger(logger, options);
80
+ }
package/src/cli.ts CHANGED
@@ -9,6 +9,7 @@ import type {
9
9
  Optional,
10
10
  Logger,
11
11
  AuthData,
12
+ GlobalOptions,
12
13
  } from './types';
13
14
  import { showBanner } from './banner';
14
15
  import { requireAuth, optionalAuth, requireOrg, optionalOrg as selectOptionalOrg } from './auth';
@@ -18,6 +19,9 @@ import * as tui from './tui';
18
19
  import { parseArgsSchema, parseOptionsSchema, buildValidationInput } from './schema-parser';
19
20
  import { defaultProfileName, loadProjectConfig } from './config';
20
21
  import { APIClient, getAPIBaseURL, type APIClient as APIClientType } from './api';
22
+ import { ErrorCode, ExitCode, createError, exitWithError } from './errors';
23
+ import { getCommand } from './command-prefix';
24
+ import { isValidateMode, outputValidation, type ValidationResult } from './output';
21
25
 
22
26
  function createAPIClient(baseCtx: CommandContext, config: Config | null): APIClient {
23
27
  try {
@@ -41,6 +45,71 @@ function createAPIClient(baseCtx: CommandContext, config: Config | null): APICli
41
45
  }
42
46
  }
43
47
 
48
+ /**
49
+ * Execute handler or output validation result based on mode
50
+ */
51
+ async function executeOrValidate(
52
+ ctx: CommandContext,
53
+ commandName: string,
54
+ handler?: (ctx: CommandContext) => unknown | Promise<unknown>
55
+ ): Promise<void> {
56
+ if (isValidateMode(ctx.options)) {
57
+ // In validate mode, just output success (validation already passed via Zod)
58
+ const result: ValidationResult = {
59
+ valid: true,
60
+ command: commandName,
61
+ };
62
+ outputValidation(result, ctx.options);
63
+ } else if (handler) {
64
+ // Normal execution
65
+ await handler(ctx);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Handle validation error - output structured result in validate mode, otherwise log and exit
71
+ */
72
+ function handleValidationError(
73
+ error: unknown,
74
+ commandName: string,
75
+ baseCtx: { options: GlobalOptions; logger: Logger }
76
+ ): never {
77
+ if (error && typeof error === 'object' && 'issues' in error) {
78
+ const issues = (error as { issues: Array<{ path: string[]; message: string }> }).issues;
79
+
80
+ if (isValidateMode(baseCtx.options)) {
81
+ // In validate mode, output structured validation result
82
+ const result: ValidationResult = {
83
+ valid: false,
84
+ command: commandName,
85
+ errors: issues.map((issue) => ({
86
+ field: issue.path?.length ? issue.path.join('.') : 'unknown',
87
+ message: issue.message,
88
+ })),
89
+ };
90
+ outputValidation(result, baseCtx.options);
91
+ process.exit(ExitCode.VALIDATION_ERROR);
92
+ } else {
93
+ // Use centralized error handling
94
+ exitWithError(
95
+ {
96
+ code: ErrorCode.VALIDATION_FAILED,
97
+ message: 'Validation error',
98
+ details: {
99
+ issues: issues.map((issue) => ({
100
+ field: issue.path?.length ? issue.path.join('.') : 'unknown',
101
+ message: issue.message,
102
+ })),
103
+ },
104
+ },
105
+ baseCtx.logger,
106
+ baseCtx.options.errorFormat ?? 'text'
107
+ );
108
+ }
109
+ }
110
+ throw error;
111
+ }
112
+
44
113
  type Normalized = {
45
114
  requiresAuth: boolean;
46
115
  optionalAuth: false | string;
@@ -97,7 +166,12 @@ function normalizeReqs(def: CommandDefinition | SubcommandDefinition): Normalize
97
166
  };
98
167
  }
99
168
 
100
- function handleProjectConfigError(error: unknown, requiresProject: boolean, logger: Logger): never {
169
+ function handleProjectConfigError(
170
+ error: unknown,
171
+ requiresProject: boolean,
172
+ logger: Logger,
173
+ errorFormat?: 'json' | 'text'
174
+ ): never {
101
175
  if (
102
176
  requiresProject &&
103
177
  error &&
@@ -105,8 +179,14 @@ function handleProjectConfigError(error: unknown, requiresProject: boolean, logg
105
179
  'name' in error &&
106
180
  error.name === 'ProjectConfigNotFoundExpection'
107
181
  ) {
108
- logger.fatal(
109
- 'invalid project folder. use --dir to specify a different directory or change to a project folder'
182
+ exitWithError(
183
+ createError(ErrorCode.PROJECT_NOT_FOUND, 'Invalid project folder', undefined, [
184
+ 'Use --dir to specify a different directory',
185
+ 'Change to a directory containing agentuity.json',
186
+ `Run "${getCommand('project init')}" to create a new project`,
187
+ ]),
188
+ logger,
189
+ errorFormat ?? 'text'
110
190
  );
111
191
  }
112
192
  throw error;
@@ -117,11 +197,11 @@ export async function createCLI(version: string): Promise<Command> {
117
197
 
118
198
  program
119
199
  .name('agentuity')
120
- .description('Agentuity CLI')
121
200
  .version(version, '-V, --version', 'Display version')
122
- .helpOption('-h, --help', 'Display help')
201
+ .helpOption('-h, --help=[json]', 'Display help (with optional JSON output)')
123
202
  .allowUnknownOption(false)
124
- .allowExcessArguments(false);
203
+ .allowExcessArguments(false)
204
+ .showHelpAfterError(true);
125
205
 
126
206
  program
127
207
  .option('--config <path>', 'Config file path')
@@ -133,7 +213,15 @@ export async function createCLI(version: string): Promise<Command> {
133
213
  'Use a specific organization when performing operations',
134
214
  process.env.AGENTUITY_CLOUD_ORG_ID
135
215
  )
136
- .option('--color-scheme <scheme>', 'Color scheme: light or dark');
216
+ .option('--color-scheme <scheme>', 'Color scheme: light or dark')
217
+ .option('--color <mode>', 'Color output: auto, always, never', 'auto')
218
+ .option('--error-format <format>', 'Error output format: json or text', 'text')
219
+ .option('--json', 'Output in JSON format (machine-readable)', false)
220
+ .option('--quiet', 'Suppress non-essential output', false)
221
+ .option('--no-progress', 'Disable progress indicators', false)
222
+ .option('--explain', 'Show what the command would do without executing', false)
223
+ .option('--dry-run', 'Execute command without making changes', false)
224
+ .option('--validate', 'Validate arguments and options without executing', false);
137
225
 
138
226
  const skipVersionCheckOption = program.createOption(
139
227
  '--skip-version-check',
@@ -160,7 +248,7 @@ export async function createCLI(version: string): Promise<Command> {
160
248
  });
161
249
  }
162
250
  console.error();
163
- console.error(`Run 'agentuity --help' for usage information.`);
251
+ console.error(`Run '${getCommand('--help')}' for usage information.`);
164
252
  process.exit(1);
165
253
  });
166
254
 
@@ -173,7 +261,7 @@ export async function createCLI(version: string): Promise<Command> {
173
261
  const match = str.match(/got (\d+)/);
174
262
  if (match) {
175
263
  write(`error: unknown command or subcommand\n`);
176
- write(`\nRun 'agentuity --help' for available commands.\n`);
264
+ write(`\nRun '${getCommand('--help')}' for available commands.\n`);
177
265
  } else {
178
266
  write(str);
179
267
  }
@@ -219,7 +307,17 @@ async function resolveRegion(opts: ResolveRegionOptions): Promise<string | undef
219
307
  // No regions available
220
308
  if (regions.length === 0) {
221
309
  if (required) {
222
- logger.fatal('No cloud regions available');
310
+ const errorFormat = (options as Record<string, unknown>).errorFormat as
311
+ | 'json'
312
+ | 'text'
313
+ | undefined;
314
+ exitWithError(
315
+ createError(ErrorCode.NO_REGIONS_AVAILABLE, 'No cloud regions available', undefined, [
316
+ 'Contact support if you need access to cloud regions',
317
+ ]),
318
+ logger,
319
+ errorFormat ?? 'text'
320
+ );
223
321
  }
224
322
  return undefined;
225
323
  }
@@ -231,8 +329,19 @@ async function resolveRegion(opts: ResolveRegionOptions): Promise<string | undef
231
329
  if (region) {
232
330
  const found = regions.find((r) => r.region === region);
233
331
  if (!found) {
234
- logger.fatal(
235
- `Invalid region '${region}'. Use one of: ${regions.map((r) => r.region).join(', ')}`
332
+ const errorFormat = (options as Record<string, unknown>).errorFormat as
333
+ | 'json'
334
+ | 'text'
335
+ | undefined;
336
+ exitWithError(
337
+ createError(
338
+ ErrorCode.REGION_NOT_FOUND,
339
+ `Invalid region '${region}'`,
340
+ { region, availableRegions: regions.map((r) => r.region) },
341
+ [`Use one of: ${regions.map((r) => r.region).join(', ')}`]
342
+ ),
343
+ logger,
344
+ errorFormat ?? 'text'
236
345
  );
237
346
  }
238
347
  return region;
@@ -249,7 +358,20 @@ async function resolveRegion(opts: ResolveRegionOptions): Promise<string | undef
249
358
 
250
359
  // No flag provided - handle TTY vs non-TTY
251
360
  if (required && !process.stdin.isTTY) {
252
- logger.fatal('--region flag is required in non-interactive mode');
361
+ const errorFormat = (options as Record<string, unknown>).errorFormat as
362
+ | 'json'
363
+ | 'text'
364
+ | undefined;
365
+ exitWithError(
366
+ createError(
367
+ ErrorCode.REGION_REQUIRED,
368
+ '--region flag is required in non-interactive mode',
369
+ { availableRegions: regions.map((r) => r.region) },
370
+ [`Use --region with one of: ${regions.map((r) => r.region).join(', ')}`]
371
+ ),
372
+ logger,
373
+ errorFormat ?? 'text'
374
+ );
253
375
  }
254
376
 
255
377
  if (process.stdin.isTTY) {
@@ -274,6 +396,13 @@ async function registerSubcommand(
274
396
  cmd.aliases(subcommand.aliases);
275
397
  }
276
398
 
399
+ // Add examples to help text
400
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
401
+ const examples = (subcommand as any).examples as string[] | undefined;
402
+ if (examples && examples.length > 0) {
403
+ cmd.addHelpText('after', '\nExamples:\n' + examples.map((ex) => ` ${ex}`).join('\n'));
404
+ }
405
+
277
406
  // Check if this subcommand has its own subcommands (nested subcommands)
278
407
  const subDef = subcommand as unknown as { subcommands?: SubcommandDefinition[] };
279
408
  if (subDef.subcommands && subDef.subcommands.length > 0) {
@@ -323,19 +452,21 @@ async function registerSubcommand(
323
452
  for (const opt of parsed) {
324
453
  const flag = opt.name.replace(/([A-Z])/g, '-$1').toLowerCase();
325
454
  const desc = opt.description || '';
455
+ // Add short flag alias for verbose
456
+ const flagSpec = flag === 'verbose' ? `-v, --${flag}` : `--${flag}`;
326
457
  if (opt.type === 'boolean') {
327
458
  if (opt.hasDefault) {
328
459
  const defaultValue =
329
460
  typeof opt.defaultValue === 'function' ? opt.defaultValue() : opt.defaultValue;
330
461
  cmd.option(`--no-${flag}`, desc);
331
- cmd.option(`--${flag}`, desc, defaultValue);
462
+ cmd.option(flagSpec, desc, defaultValue);
332
463
  } else {
333
- cmd.option(`--${flag}`, desc);
464
+ cmd.option(flagSpec, desc);
334
465
  }
335
466
  } else if (opt.type === 'number') {
336
- cmd.option(`--${flag} <${opt.name}>`, desc, parseFloat);
467
+ cmd.option(`${flagSpec} <${opt.name}>`, desc, parseFloat);
337
468
  } else {
338
- cmd.option(`--${flag} <${opt.name}>`, desc);
469
+ cmd.option(`${flagSpec} <${opt.name}>`, desc);
339
470
  }
340
471
  }
341
472
  }
@@ -368,8 +499,14 @@ async function registerSubcommand(
368
499
  'name' in error &&
369
500
  error.name === 'ProjectConfigNotFoundExpection'
370
501
  ) {
371
- baseCtx.logger.fatal(
372
- 'invalid project folder. use --dir to specify a different directory or change to a project folder'
502
+ exitWithError(
503
+ createError(ErrorCode.PROJECT_NOT_FOUND, 'Invalid project folder', undefined, [
504
+ 'Use --dir to specify a different directory',
505
+ 'Change to a directory containing agentuity.json',
506
+ `Run "${getCommand('project init')}" to create a new project`,
507
+ ]),
508
+ baseCtx.logger,
509
+ baseCtx.options.errorFormat
373
510
  );
374
511
  }
375
512
  throw error;
@@ -448,22 +585,21 @@ async function registerSubcommand(
448
585
  ctx.region = region;
449
586
  }
450
587
  }
451
- if (subcommand.handler) {
452
- await subcommand.handler(ctx as CommandContext);
453
- }
588
+ await executeOrValidate(
589
+ ctx as CommandContext,
590
+ `${parent.name()} ${subcommand.name}`,
591
+ subcommand.handler
592
+ );
454
593
  } catch (error) {
455
594
  if (error && typeof error === 'object' && 'issues' in error) {
456
- baseCtx.logger.error('Validation error:');
457
- const issues = (error as { issues: Array<{ path: string[]; message: string }> })
458
- .issues;
459
- for (const issue of issues) {
460
- baseCtx.logger.error(
461
- ` ${issue.path?.length ? issue.path.join('.') + ': ' : ''}${issue.message}`
462
- );
463
- }
464
- process.exit(1);
595
+ handleValidationError(error, `${parent.name()} ${subcommand.name}`, baseCtx);
465
596
  }
466
- handleProjectConfigError(error, normalized.requiresProject, baseCtx.logger);
597
+ handleProjectConfigError(
598
+ error,
599
+ normalized.requiresProject,
600
+ baseCtx.logger,
601
+ baseCtx.options.errorFormat
602
+ );
467
603
  }
468
604
  } else {
469
605
  const ctx: Record<string, unknown> = {
@@ -596,22 +732,21 @@ async function registerSubcommand(
596
732
  ctx.region = region;
597
733
  }
598
734
  }
599
- if (subcommand.handler) {
600
- await subcommand.handler(ctx as CommandContext);
601
- }
735
+ await executeOrValidate(
736
+ ctx as CommandContext,
737
+ `${parent.name()} ${subcommand.name}`,
738
+ subcommand.handler
739
+ );
602
740
  } catch (error) {
603
741
  if (error && typeof error === 'object' && 'issues' in error) {
604
- baseCtx.logger.error('Validation error:');
605
- const issues = (error as { issues: Array<{ path: string[]; message: string }> })
606
- .issues;
607
- for (const issue of issues) {
608
- baseCtx.logger.error(
609
- ` ${issue.path?.length ? issue.path.join('.') + ': ' : ''}${issue.message}`
610
- );
611
- }
612
- process.exit(1);
742
+ handleValidationError(error, `${parent.name()} ${subcommand.name}`, baseCtx);
613
743
  }
614
- handleProjectConfigError(error, normalized.requiresProject, baseCtx.logger);
744
+ handleProjectConfigError(
745
+ error,
746
+ normalized.requiresProject,
747
+ baseCtx.logger,
748
+ baseCtx.options.errorFormat
749
+ );
615
750
  }
616
751
  } else {
617
752
  const ctx: Record<string, unknown> = {
@@ -694,22 +829,21 @@ async function registerSubcommand(
694
829
  ctx as CommandContext & { apiClient: APIClientType }
695
830
  );
696
831
  }
697
- if (subcommand.handler) {
698
- await subcommand.handler(ctx as CommandContext);
699
- }
832
+ await executeOrValidate(
833
+ ctx as CommandContext,
834
+ `${parent.name()} ${subcommand.name}`,
835
+ subcommand.handler
836
+ );
700
837
  } catch (error) {
701
838
  if (error && typeof error === 'object' && 'issues' in error) {
702
- baseCtx.logger.error('Validation error:');
703
- const issues = (error as { issues: Array<{ path: string[]; message: string }> })
704
- .issues;
705
- for (const issue of issues) {
706
- baseCtx.logger.error(
707
- ` ${issue.path?.length ? issue.path.join('.') + ': ' : ''}${issue.message}`
708
- );
709
- }
710
- process.exit(1);
839
+ handleValidationError(error, `${parent.name()} ${subcommand.name}`, baseCtx);
711
840
  }
712
- handleProjectConfigError(error, normalized.requiresProject, baseCtx.logger);
841
+ handleProjectConfigError(
842
+ error,
843
+ normalized.requiresProject,
844
+ baseCtx.logger,
845
+ baseCtx.options.errorFormat
846
+ );
713
847
  }
714
848
  } else {
715
849
  const ctx: Record<string, unknown> = {
@@ -0,0 +1,147 @@
1
+ import { z } from 'zod';
2
+ import { createCommand } from '../../types';
3
+ import * as tui from '../../tui';
4
+ import { APIResponseSchema } from '@agentuity/server';
5
+ import { Table } from 'console-table-printer';
6
+ import { abbreviate, abbreviateDescription } from '../../utils/format';
7
+
8
+ const AgentSchema = z.object({
9
+ id: z.string(),
10
+ name: z.string(),
11
+ description: z.string().nullable(),
12
+ identifier: z.string(),
13
+ version: z.string().nullable(),
14
+ deploymentId: z.string().nullable(),
15
+ devmode: z.boolean(),
16
+ metadata: z.record(z.string(), z.unknown()).nullable(),
17
+ createdAt: z.string(),
18
+ updatedAt: z.string(),
19
+ evals: z.array(
20
+ z.object({
21
+ id: z.string(),
22
+ name: z.string(),
23
+ description: z.string().nullable(),
24
+ identifier: z.string().nullable(),
25
+ deploymentId: z.string().nullable(),
26
+ version: z.string().nullable(),
27
+ devmode: z.boolean(),
28
+ createdAt: z.string(),
29
+ updatedAt: z.string(),
30
+ })
31
+ ),
32
+ });
33
+
34
+ const AgentsResponseSchema = APIResponseSchema(z.array(AgentSchema));
35
+
36
+ export const command = createCommand({
37
+ name: 'agents',
38
+ description: 'Show devmode agent results',
39
+ requires: { auth: true, apiClient: true, project: true },
40
+ schema: {
41
+ options: z.object({
42
+ deploymentId: z.string().optional().describe('Filter by deployment ID'),
43
+ format: z
44
+ .enum(['json', 'table'])
45
+ .optional()
46
+ .default('table')
47
+ .describe('Output format: json or table'),
48
+ verbose: z.boolean().optional().default(false).describe('Show full IDs and descriptions'),
49
+ }),
50
+ },
51
+ async handler(ctx) {
52
+ const { opts, apiClient, project } = ctx;
53
+ const projectId = project.projectId;
54
+ const format = opts?.format ?? 'table';
55
+ const verbose = opts?.verbose ?? false;
56
+
57
+ const deploymentId = opts?.deploymentId;
58
+ const queryParams = deploymentId ? `?deploymentId=${deploymentId}` : '';
59
+ const response = await tui.spinner('Fetching agents', async () => {
60
+ return apiClient.request(
61
+ 'GET',
62
+ `/cli/agent/${projectId}${queryParams}`,
63
+ AgentsResponseSchema
64
+ );
65
+ });
66
+
67
+ if (!response.success) {
68
+ tui.fatal(`Failed to fetch agents: ${response.message ?? 'Unknown error'}`);
69
+ }
70
+
71
+ const agents = response.data;
72
+
73
+ if (format === 'json') {
74
+ console.log(JSON.stringify(agents, null, 2));
75
+ return;
76
+ }
77
+
78
+ tui.info(`Agents (${agents.length})`);
79
+ if (agents.length === 0) {
80
+ tui.muted('No agents found');
81
+ } else {
82
+ const table = new Table({
83
+ columns: [
84
+ { name: 'Name', alignment: 'left' },
85
+ { name: 'ID', alignment: 'left' },
86
+ { name: 'Identifier', alignment: 'left' },
87
+ { name: 'Deployment', alignment: 'left' },
88
+ { name: 'Version', alignment: 'left' },
89
+ { name: 'Evals', alignment: 'center' },
90
+ { name: 'Created', alignment: 'left' },
91
+ ],
92
+ });
93
+
94
+ for (const agent of agents) {
95
+ table.addRow({
96
+ Name: agent.name,
97
+ ID: verbose ? agent.id : abbreviate(agent.id, 20),
98
+ Identifier: verbose ? agent.identifier : abbreviate(agent.identifier, 20),
99
+ Deployment: abbreviate(agent.deploymentId, 20),
100
+ Version: verbose
101
+ ? (agent.version ?? 'N/A')
102
+ : (abbreviate(agent.version, 20) ?? 'N/A'),
103
+ Evals: agent.evals.length,
104
+ Created: new Date(agent.createdAt).toLocaleString(),
105
+ });
106
+ }
107
+ table.printTable();
108
+
109
+ // Show evals for each agent
110
+ for (const agent of agents) {
111
+ if (agent.evals.length > 0) {
112
+ console.log(`\n Evals for ${agent.name}:`);
113
+ const evalTable = new Table({
114
+ columns: [
115
+ { name: 'Name', alignment: 'left' },
116
+ { name: 'ID', alignment: 'left' },
117
+ { name: 'Identifier', alignment: 'left' },
118
+ { name: 'Deployment', alignment: 'left' },
119
+ { name: 'Version', alignment: 'left' },
120
+ { name: 'Description', alignment: 'left' },
121
+ { name: 'Created', alignment: 'left' },
122
+ ],
123
+ });
124
+
125
+ for (const evalItem of agent.evals) {
126
+ evalTable.addRow({
127
+ Name: evalItem.name,
128
+ ID: verbose ? evalItem.id : abbreviate(evalItem.id, 20),
129
+ Identifier: verbose
130
+ ? (evalItem.identifier ?? 'N/A')
131
+ : (abbreviate(evalItem.identifier, 20) ?? 'N/A'),
132
+ Deployment: abbreviate(evalItem.deploymentId, 20),
133
+ Version: verbose
134
+ ? (evalItem.version ?? 'N/A')
135
+ : (abbreviate(evalItem.version, 20) ?? 'N/A'),
136
+ Description: verbose
137
+ ? (evalItem.description ?? 'N/A')
138
+ : abbreviateDescription(evalItem.description),
139
+ Created: new Date(evalItem.createdAt).toLocaleString(),
140
+ });
141
+ }
142
+ evalTable.printTable();
143
+ }
144
+ }
145
+ }
146
+ },
147
+ });
@@ -8,5 +8,6 @@ import { sshSubcommand } from './ssh';
8
8
  export const command = createCommand({
9
9
  name: 'auth',
10
10
  description: 'Authentication and authorization related commands',
11
+ tags: ['read-only', 'fast'],
11
12
  subcommands: [loginCommand, logoutCommand, signupCommand, whoamiCommand, sshSubcommand],
12
13
  });
@@ -4,12 +4,17 @@ import { getAppBaseURL } from '../../api';
4
4
  import { saveAuth } from '../../config';
5
5
  import { generateLoginOTP, pollForLoginCompletion } from './api';
6
6
  import * as tui from '../../tui';
7
+ import { getCommand } from '../../command-prefix';
8
+ import { ErrorCode } from '../../errors';
7
9
 
8
10
  export const loginCommand = createSubcommand({
9
11
  name: 'login',
10
12
  description: 'Login to the Agentuity Platform using a browser-based authentication flow',
13
+ tags: ['mutating', 'creates-resource', 'slow', 'api-intensive'],
11
14
  toplevel: true,
15
+ idempotent: false,
12
16
  requires: { apiClient: true },
17
+ examples: [getCommand('auth login'), getCommand('login')],
13
18
  async handler(ctx) {
14
19
  const { logger, config, apiClient } = ctx;
15
20
 
@@ -78,9 +83,9 @@ export const loginCommand = createSubcommand({
78
83
  error.issues.map((i) => tui.arrow(`${i.message} for ${i.path}`));
79
84
  process.exit(1);
80
85
  } else if (error instanceof Error) {
81
- logger.fatal(`Login failed: ${error.message}`);
86
+ logger.fatal(`Login failed: ${error.message}`, ErrorCode.AUTH_FAILED);
82
87
  } else {
83
- logger.fatal('Login failed');
88
+ logger.fatal('Login failed', ErrorCode.AUTH_FAILED);
84
89
  }
85
90
  }
86
91
  },
@@ -1,11 +1,15 @@
1
1
  import { createSubcommand } from '../../types';
2
2
  import { clearAuth } from '../../config';
3
3
  import * as tui from '../../tui';
4
+ import { getCommand } from '../../command-prefix';
4
5
 
5
6
  export const logoutCommand = createSubcommand({
6
7
  name: 'logout',
7
8
  description: 'Logout of the Agentuity Cloud Platform',
9
+ tags: ['mutating', 'deletes-resource', 'fast', 'requires-auth'],
8
10
  toplevel: true,
11
+ idempotent: false,
12
+ examples: [getCommand('auth logout'), getCommand('logout')],
9
13
 
10
14
  async handler() {
11
15
  await clearAuth();