@agentuity/cli 0.0.61 → 0.0.62

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 (73) hide show
  1. package/dist/banner.js +1 -1
  2. package/dist/banner.js.map +1 -1
  3. package/dist/cmd/ai/capabilities/show.js +1 -1
  4. package/dist/cmd/ai/capabilities/show.js.map +1 -1
  5. package/dist/cmd/ai/schema/show.js +1 -1
  6. package/dist/cmd/ai/schema/show.js.map +1 -1
  7. package/dist/cmd/build/ast.d.ts.map +1 -1
  8. package/dist/cmd/build/ast.js +36 -11
  9. package/dist/cmd/build/ast.js.map +1 -1
  10. package/dist/cmd/cloud/index.d.ts.map +1 -1
  11. package/dist/cmd/cloud/index.js +2 -0
  12. package/dist/cmd/cloud/index.js.map +1 -1
  13. package/dist/cmd/cloud/secret/set.js +2 -2
  14. package/dist/cmd/cloud/secret/set.js.map +1 -1
  15. package/dist/cmd/cloud/session/get.d.ts.map +1 -1
  16. package/dist/cmd/cloud/session/get.js +10 -2
  17. package/dist/cmd/cloud/session/get.js.map +1 -1
  18. package/dist/cmd/cloud/storage/create.d.ts.map +1 -1
  19. package/dist/cmd/cloud/storage/create.js +12 -3
  20. package/dist/cmd/cloud/storage/create.js.map +1 -1
  21. package/dist/cmd/cloud/storage/delete.d.ts.map +1 -1
  22. package/dist/cmd/cloud/storage/delete.js +13 -4
  23. package/dist/cmd/cloud/storage/delete.js.map +1 -1
  24. package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
  25. package/dist/cmd/cloud/stream/list.js +8 -5
  26. package/dist/cmd/cloud/stream/list.js.map +1 -1
  27. package/dist/cmd/cloud/thread/delete.d.ts +2 -0
  28. package/dist/cmd/cloud/thread/delete.d.ts.map +1 -0
  29. package/dist/cmd/cloud/thread/delete.js +41 -0
  30. package/dist/cmd/cloud/thread/delete.js.map +1 -0
  31. package/dist/cmd/cloud/thread/get.d.ts +2 -0
  32. package/dist/cmd/cloud/thread/get.d.ts.map +1 -0
  33. package/dist/cmd/cloud/thread/get.js +80 -0
  34. package/dist/cmd/cloud/thread/get.js.map +1 -0
  35. package/dist/cmd/cloud/thread/index.d.ts +2 -0
  36. package/dist/cmd/cloud/thread/index.d.ts.map +1 -0
  37. package/dist/cmd/cloud/thread/index.js +17 -0
  38. package/dist/cmd/cloud/thread/index.js.map +1 -0
  39. package/dist/cmd/cloud/thread/list.d.ts +2 -0
  40. package/dist/cmd/cloud/thread/list.d.ts.map +1 -0
  41. package/dist/cmd/cloud/thread/list.js +112 -0
  42. package/dist/cmd/cloud/thread/list.js.map +1 -0
  43. package/dist/cmd/profile/use.d.ts.map +1 -1
  44. package/dist/cmd/profile/use.js +12 -3
  45. package/dist/cmd/profile/use.js.map +1 -1
  46. package/dist/cmd/project/download.d.ts.map +1 -1
  47. package/dist/cmd/project/download.js +15 -6
  48. package/dist/cmd/project/download.js.map +1 -1
  49. package/dist/cmd/project/list.d.ts.map +1 -1
  50. package/dist/cmd/project/list.js +6 -3
  51. package/dist/cmd/project/list.js.map +1 -1
  52. package/dist/cmd/version/index.d.ts.map +1 -1
  53. package/dist/cmd/version/index.js +5 -2
  54. package/dist/cmd/version/index.js.map +1 -1
  55. package/package.json +4 -3
  56. package/src/banner.ts +1 -1
  57. package/src/cmd/ai/capabilities/show.ts +1 -1
  58. package/src/cmd/ai/schema/show.ts +1 -1
  59. package/src/cmd/build/ast.ts +35 -11
  60. package/src/cmd/cloud/index.ts +2 -0
  61. package/src/cmd/cloud/secret/set.ts +2 -2
  62. package/src/cmd/cloud/session/get.ts +16 -2
  63. package/src/cmd/cloud/storage/create.ts +12 -3
  64. package/src/cmd/cloud/storage/delete.ts +13 -4
  65. package/src/cmd/cloud/stream/list.ts +8 -5
  66. package/src/cmd/cloud/thread/delete.ts +44 -0
  67. package/src/cmd/cloud/thread/get.ts +88 -0
  68. package/src/cmd/cloud/thread/index.ts +17 -0
  69. package/src/cmd/cloud/thread/list.ts +125 -0
  70. package/src/cmd/profile/use.ts +12 -3
  71. package/src/cmd/project/download.ts +15 -6
  72. package/src/cmd/project/list.ts +6 -3
  73. package/src/cmd/version/index.ts +5 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentuity/cli",
3
- "version": "0.0.61",
3
+ "version": "0.0.62",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Agentuity employees and contributors",
6
6
  "type": "module",
@@ -35,8 +35,9 @@
35
35
  "prepublishOnly": "bun run clean && bun run build"
36
36
  },
37
37
  "dependencies": {
38
- "@agentuity/core": "0.0.61",
39
- "@agentuity/server": "0.0.61",
38
+ "@agentuity/core": "0.0.62",
39
+ "@agentuity/server": "0.0.62",
40
+ "@datasert/cronjs-parser": "^1.4.0",
40
41
  "@terascope/fetch-github-release": "^2.2.1",
41
42
  "acorn-loose": "^8.5.2",
42
43
  "adm-zip": "^0.5.16",
package/src/banner.ts CHANGED
@@ -46,7 +46,7 @@ export function generateBanner(version?: string, compact?: true): string {
46
46
  CYAN + '╰────────────────────────────────────────────────────╯' + RESET,
47
47
  ].filter(Boolean) as string[];
48
48
 
49
- return lines.map((line) => console.log(line)).join('');
49
+ return lines.join('\n');
50
50
  }
51
51
 
52
52
  export function showBanner(version?: string, compact?: true): void {
@@ -53,7 +53,7 @@ export const showSubcommand = createSubcommand({
53
53
  description: 'Display CLI capabilities',
54
54
  tags: ['read-only', 'fast'],
55
55
  examples: [
56
- { command: getCommand('capabilities show'), description: 'Show details' },
56
+ { command: getCommand('capabilities show'), description: 'Show CLI AI capabilities' },
57
57
  {
58
58
  command: getCommand('--json capabilities show'),
59
59
  description: 'Show output in JSON format',
@@ -9,7 +9,7 @@ export const showSubcommand = createSubcommand({
9
9
  idempotent: true,
10
10
  examples: [
11
11
  { command: getCommand('schema show'), description: 'Show details' },
12
- { command: getCommand('--help=json'), description: 'Show help information' },
12
+ { command: getCommand('--json schema show'), description: 'Show output in JSON format' },
13
13
  ],
14
14
  async handler(ctx: CommandContext) {
15
15
  const { logger } = ctx;
@@ -1,5 +1,6 @@
1
1
  import * as acornLoose from 'acorn-loose';
2
2
  import { basename, dirname, relative } from 'node:path';
3
+ import { parse as parseCronExpression } from '@datasert/cronjs-parser';
3
4
  import { generate } from 'astring';
4
5
  import type { BuildMetadata } from '../../types';
5
6
  import { createLogger } from '@agentuity/server';
@@ -192,7 +193,7 @@ function augmentAgentMetadataNode(
192
193
  ): [string, Map<string, string>] {
193
194
  const metadata = parseObjectExpressionToMap(propvalue);
194
195
  if (!metadata.has('name')) {
195
- const location = ast.loc?.start ? ` on line ${ast.loc.start}` : '';
196
+ const location = ast.loc?.start?.line ? ` on line ${ast.loc.start.line}` : '';
196
197
  throw new MetadataError({
197
198
  filename,
198
199
  line: ast.loc?.start?.line,
@@ -201,7 +202,7 @@ function augmentAgentMetadataNode(
201
202
  }
202
203
  const name = metadata.get('name')!;
203
204
  if (metadata.has('identifier') && identifier !== metadata.get('identifier')) {
204
- const location = ast.loc?.start ? ` on line ${ast.loc.start}` : '';
205
+ const location = ast.loc?.start?.line ? ` on line ${ast.loc.start.line}` : '';
205
206
  throw new MetadataError({
206
207
  filename,
207
208
  line: ast.loc?.start?.line,
@@ -414,7 +415,11 @@ export function parseEvalMetadata(
414
415
  | 'error';
415
416
  const logger = createLogger(logLevel);
416
417
  logger.trace(`Parsing evals from ${filename}`);
417
- const ast = acornLoose.parse(contents, { ecmaVersion: 'latest', sourceType: 'module' });
418
+ const ast = acornLoose.parse(contents, {
419
+ locations: true,
420
+ ecmaVersion: 'latest',
421
+ sourceType: 'module',
422
+ });
418
423
  const rel = relative(rootDir, filename);
419
424
  const version = hash(contents);
420
425
  const evals: Array<{
@@ -609,7 +614,11 @@ export async function parseAgentMetadata(
609
614
  projectId: string,
610
615
  deploymentId: string
611
616
  ): Promise<[string, Map<string, string>]> {
612
- const ast = acornLoose.parse(contents, { ecmaVersion: 'latest', sourceType: 'module' });
617
+ const ast = acornLoose.parse(contents, {
618
+ locations: true,
619
+ ecmaVersion: 'latest',
620
+ sourceType: 'module',
621
+ });
613
622
  let exportName: string | undefined;
614
623
  const rel = relative(rootDir, filename);
615
624
  const name = basename(dirname(filename));
@@ -784,7 +793,11 @@ export async function parseRoute(
784
793
  ): Promise<BuildMetadata['routes']> {
785
794
  const contents = await Bun.file(filename).text();
786
795
  const version = hash(contents);
787
- const ast = acornLoose.parse(contents, { ecmaVersion: 'latest', sourceType: 'module' });
796
+ const ast = acornLoose.parse(contents, {
797
+ locations: true,
798
+ ecmaVersion: 'latest',
799
+ sourceType: 'module',
800
+ });
788
801
  let exportName: string | undefined;
789
802
  let variableName: string | undefined;
790
803
  for (const body of ast.body) {
@@ -878,8 +891,8 @@ export async function parseRoute(
878
891
  } else {
879
892
  throw new InvalidRouterConfigError({
880
893
  filename,
881
- line: body.start,
882
- message: `unsupported HTTP method ${method} in ${filename} at line ${body.start}`,
894
+ line: body.loc?.start?.line,
895
+ message: `unsupported HTTP method ${method} in ${filename} at line ${body.loc?.start?.line}`,
883
896
  });
884
897
  }
885
898
  break;
@@ -933,8 +946,19 @@ export async function parseRoute(
933
946
  method = 'post';
934
947
  const theaction = action as ASTLiteral;
935
948
  if (theaction.type === 'Literal') {
936
- const number = theaction.value;
937
- suffix = hash(number);
949
+ const expression = theaction.value;
950
+ try {
951
+ parseCronExpression(expression, { hasSeconds: false });
952
+ } catch (ex) {
953
+ throw new InvalidRouterConfigError({
954
+ filename,
955
+ cause: ex,
956
+ line: body.loc?.start?.line,
957
+ message: `invalid cron expression "${expression}" in ${filename} at line ${body.loc?.start?.line}`,
958
+ });
959
+ }
960
+ suffix = hash(expression);
961
+ config = { expression };
938
962
  break;
939
963
  }
940
964
  break;
@@ -942,8 +966,8 @@ export async function parseRoute(
942
966
  default: {
943
967
  throw new InvalidRouterConfigError({
944
968
  filename,
945
- line: body.start,
946
- message: `unsupported router method ${method} in ${filename} at line ${body.start}`,
969
+ line: body.loc?.start?.line,
970
+ message: `unsupported router method ${method} in ${filename} at line ${body.loc?.start?.line}`,
947
971
  });
948
972
  }
949
973
  }
@@ -3,6 +3,7 @@ import { deploySubcommand } from './deploy';
3
3
  import { dbCommand } from './db';
4
4
  import { storageCommand } from './storage';
5
5
  import { sessionCommand } from './session';
6
+ import { threadCommand } from './thread';
6
7
  import { sshSubcommand } from './ssh';
7
8
  import { scpSubcommand } from './scp';
8
9
  import { deploymentCommand } from './deployment';
@@ -37,6 +38,7 @@ export const command = createCommand({
37
38
  dbCommand,
38
39
  storageCommand,
39
40
  sessionCommand,
41
+ threadCommand,
40
42
  sshSubcommand,
41
43
  scpSubcommand,
42
44
  deploymentCommand,
@@ -19,11 +19,11 @@ export const setSubcommand = createSubcommand({
19
19
  examples: [
20
20
  {
21
21
  command: getCommand('secret set DATABASE_URL "postgres://user:pass@host:5432/db"'),
22
- description: 'Run "postgres://user:pass@host:5432/db" command',
22
+ description: 'Set the DATABASE_URL environment secret',
23
23
  },
24
24
  {
25
25
  command: getCommand('secret set STRIPE_SECRET_KEY "sk_live_..."'),
26
- description: 'Run "sk_live_..." command',
26
+ description: 'Set the STRIPE_SECRET_KEY environment secret',
27
27
  },
28
28
  ],
29
29
  requires: { auth: true, project: true, apiClient: true },
@@ -1,7 +1,13 @@
1
1
  import { z } from 'zod';
2
2
  import { createSubcommand } from '../../../types';
3
3
  import * as tui from '../../../tui';
4
- import { sessionGet, type SpanNode, type EvalRun, type AgentInfo } from '@agentuity/server';
4
+ import {
5
+ sessionGet,
6
+ type SpanNode,
7
+ type EvalRun,
8
+ type AgentInfo,
9
+ APIError,
10
+ } from '@agentuity/server';
5
11
  import { getCommand } from '../../../command-prefix';
6
12
  import { ErrorCode } from '../../../errors';
7
13
  import { getCatalystAPIClient } from '../../../config';
@@ -104,7 +110,12 @@ export const getSubcommand = createSubcommand({
104
110
  name: 'get',
105
111
  description: 'Get details about a specific session',
106
112
  tags: ['read-only', 'fast', 'requires-auth'],
107
- examples: [{ command: getCommand('cloud session get'), description: 'sess_abc123xyz' }],
113
+ examples: [
114
+ {
115
+ command: getCommand('cloud session get sess_abc123xyz'),
116
+ description: 'Get a session by ID',
117
+ },
118
+ ],
108
119
  requires: { auth: true },
109
120
  idempotent: true,
110
121
  schema: {
@@ -233,6 +244,9 @@ export const getSubcommand = createSubcommand({
233
244
 
234
245
  return result;
235
246
  } catch (ex) {
247
+ if (ex instanceof APIError && ex.status === 404) {
248
+ tui.fatal(`Session ${args.session_id} not found`, ErrorCode.RESOURCE_NOT_FOUND);
249
+ }
236
250
  tui.fatal(
237
251
  `Failed to get session: ${ex instanceof Error ? ex.message : String(ex)}`,
238
252
  ErrorCode.API_ERROR
@@ -14,9 +14,18 @@ export const createSubcommand = defineSubcommand({
14
14
  idempotent: false,
15
15
  requires: { auth: true, org: true, region: true },
16
16
  examples: [
17
- { command: getCommand('cloud storage create'), description: 'Create new item' },
18
- { command: getCommand('cloud storage new'), description: 'Run new command' },
19
- { command: getCommand('--dry-run cloud storage create'), description: 'Create new item' },
17
+ {
18
+ command: getCommand('cloud storage create'),
19
+ description: 'Create a new cloud storage bucket',
20
+ },
21
+ {
22
+ command: getCommand('cloud storage new'),
23
+ description: 'Alias for "cloud storage create" (shorthand "new")',
24
+ },
25
+ {
26
+ command: getCommand('--dry-run cloud storage create'),
27
+ description: 'Dry-run: display what would be created without making changes',
28
+ },
20
29
  ],
21
30
  schema: {
22
31
  response: z.object({
@@ -17,12 +17,21 @@ export const deleteSubcommand = createSubcommand({
17
17
  idempotent: false,
18
18
  requires: { auth: true, org: true, region: true },
19
19
  examples: [
20
- { command: getCommand('cloud storage delete my-bucket'), description: 'Delete item' },
21
- { command: getCommand('cloud storage rm my-bucket file.txt'), description: 'Delete item' },
22
- { command: getCommand('cloud storage delete'), description: 'Delete item' },
20
+ {
21
+ command: getCommand('cloud storage delete my-bucket'),
22
+ description: 'Delete a storage bucket',
23
+ },
24
+ {
25
+ command: getCommand('cloud storage rm my-bucket file.txt'),
26
+ description: 'Delete a file from a bucket',
27
+ },
28
+ {
29
+ command: getCommand('cloud storage delete'),
30
+ description: 'Interactive selection to delete a bucket',
31
+ },
23
32
  {
24
33
  command: getCommand('--dry-run cloud storage delete my-bucket'),
25
- description: 'Delete item',
34
+ description: 'Dry-run: show what would be deleted without making changes',
26
35
  },
27
36
  ],
28
37
  schema: {
@@ -32,14 +32,17 @@ export const listSubcommand = createCommand({
32
32
  requires: { auth: true, project: true },
33
33
  idempotent: true,
34
34
  examples: [
35
- { command: getCommand('stream list'), description: 'List all streams' },
36
- { command: getCommand('stream ls --size 50'), description: 'List 50 most recent streams' },
37
- { command: getCommand('stream list --name agent-logs'), description: 'Filter by name' },
35
+ { command: getCommand('cloud stream list'), description: 'List all streams' },
38
36
  {
39
- command: getCommand('stream list --metadata type=export'),
37
+ command: getCommand('cloud stream ls --size 50'),
38
+ description: 'List 50 most recent streams',
39
+ },
40
+ { command: getCommand('cloud stream list --name agent-logs'), description: 'Filter by name' },
41
+ {
42
+ command: getCommand('cloud stream list --metadata type=export'),
40
43
  description: 'Filter by metadata',
41
44
  },
42
- { command: getCommand('stream ls --json'), description: 'Output as JSON' },
45
+ { command: getCommand('cloud stream ls --json'), description: 'Output as JSON' },
43
46
  ],
44
47
  schema: {
45
48
  options: z.object({
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { threadDelete, APIError } from '@agentuity/server';
5
+ import { getCommand } from '../../../command-prefix';
6
+ import { ErrorCode } from '../../../errors';
7
+ import { getCatalystAPIClient } from '../../../config';
8
+
9
+ export const deleteSubcommand = createSubcommand({
10
+ name: 'delete',
11
+ description: 'Delete a thread',
12
+ tags: ['destructive', 'requires-auth'],
13
+ examples: [
14
+ {
15
+ command: getCommand('cloud thread delete thrd_abc123xyz'),
16
+ description: 'Delete a thread by ID',
17
+ },
18
+ ],
19
+ aliases: ['rm'],
20
+ requires: { auth: true },
21
+ schema: {
22
+ args: z.object({
23
+ thread_id: z.string().describe('Thread ID'),
24
+ }),
25
+ },
26
+ async handler(ctx) {
27
+ const { config, logger, auth, args } = ctx;
28
+ const catalystClient = getCatalystAPIClient(config, logger, auth);
29
+
30
+ try {
31
+ await threadDelete(catalystClient, { id: args.thread_id });
32
+ tui.success(`Thread ${args.thread_id} deleted successfully`);
33
+ } catch (ex) {
34
+ if (ex instanceof APIError && ex.status === 404) {
35
+ tui.fatal(`Thread ${args.thread_id} not found`, ErrorCode.RESOURCE_NOT_FOUND);
36
+ return;
37
+ }
38
+ tui.fatal(
39
+ `Failed to delete thread: ${ex instanceof Error ? ex.message : String(ex)}`,
40
+ ErrorCode.API_ERROR
41
+ );
42
+ }
43
+ },
44
+ });
@@ -0,0 +1,88 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { threadGet, APIError } from '@agentuity/server';
5
+ import { getCommand } from '../../../command-prefix';
6
+ import { ErrorCode } from '../../../errors';
7
+ import { getCatalystAPIClient } from '../../../config';
8
+
9
+ const ThreadGetResponseSchema = z.object({
10
+ id: z.string().describe('Thread ID'),
11
+ created_at: z.string().describe('Creation timestamp'),
12
+ updated_at: z.string().describe('Update timestamp'),
13
+ deleted: z.boolean().describe('Deleted status'),
14
+ deleted_at: z.string().nullable().describe('Deletion timestamp'),
15
+ deleted_by: z.string().nullable().describe('Deleted by'),
16
+ org_id: z.string().describe('Organization ID'),
17
+ project_id: z.string().describe('Project ID'),
18
+ user_data: z.string().nullable().optional().describe('User data as JSON'),
19
+ });
20
+
21
+ export const getSubcommand = createSubcommand({
22
+ name: 'get',
23
+ description: 'Get details about a specific thread',
24
+ tags: ['read-only', 'fast', 'requires-auth'],
25
+ examples: [
26
+ {
27
+ command: getCommand('cloud thread get thrd_abc123xyz'),
28
+ description: 'Get a thread by ID',
29
+ },
30
+ ],
31
+ requires: { auth: true },
32
+ idempotent: true,
33
+ schema: {
34
+ args: z.object({
35
+ thread_id: z.string().describe('Thread ID'),
36
+ }),
37
+ response: ThreadGetResponseSchema,
38
+ },
39
+ async handler(ctx) {
40
+ const { config, logger, auth, args, options } = ctx;
41
+ const catalystClient = getCatalystAPIClient(config, logger, auth);
42
+
43
+ try {
44
+ const thread = await threadGet(catalystClient, { id: args.thread_id });
45
+
46
+ const result = {
47
+ id: thread.id,
48
+ created_at: thread.created_at,
49
+ updated_at: thread.updated_at,
50
+ deleted: thread.deleted,
51
+ deleted_at: thread.deleted_at,
52
+ deleted_by: thread.deleted_by,
53
+ org_id: thread.org_id,
54
+ project_id: thread.project_id,
55
+ user_data: thread.user_data,
56
+ };
57
+
58
+ if (options.json) {
59
+ return result;
60
+ }
61
+
62
+ console.log(tui.bold('ID: ') + thread.id);
63
+ console.log(tui.bold('Project: ') + thread.project_id);
64
+ console.log(tui.bold('Created: ') + new Date(thread.created_at).toLocaleString());
65
+ console.log(tui.bold('Updated: ') + new Date(thread.updated_at).toLocaleString());
66
+ console.log(tui.bold('Deleted: ') + (thread.deleted ? 'Yes' : 'No'));
67
+ if (thread.deleted_at) {
68
+ console.log(tui.bold('Deleted At: ') + new Date(thread.deleted_at).toLocaleString());
69
+ }
70
+ if (thread.deleted_by) {
71
+ console.log(tui.bold('Deleted By: ') + thread.deleted_by);
72
+ }
73
+ if (thread.user_data) {
74
+ console.log(tui.bold('User Data: ') + thread.user_data);
75
+ }
76
+
77
+ return result;
78
+ } catch (ex) {
79
+ if (ex instanceof APIError && ex.status === 404) {
80
+ tui.fatal(`Thread ${args.thread_id} not found`, ErrorCode.RESOURCE_NOT_FOUND);
81
+ }
82
+ tui.fatal(
83
+ `Failed to get thread: ${ex instanceof Error ? ex.message : String(ex)}`,
84
+ ErrorCode.API_ERROR
85
+ );
86
+ }
87
+ },
88
+ });
@@ -0,0 +1,17 @@
1
+ import { createCommand } from '../../../types';
2
+ import { getSubcommand } from './get';
3
+ import { listSubcommand } from './list';
4
+ import { deleteSubcommand } from './delete';
5
+ import { getCommand } from '../../../command-prefix';
6
+
7
+ export const threadCommand = createCommand({
8
+ name: 'thread',
9
+ description: 'Manage threads',
10
+ tags: ['requires-auth'],
11
+ examples: [
12
+ { command: getCommand('cloud thread list'), description: 'List all threads' },
13
+ { command: getCommand('cloud thread get <id>'), description: 'Get thread details' },
14
+ { command: getCommand('cloud thread delete <id>'), description: 'Delete a thread' },
15
+ ],
16
+ subcommands: [getSubcommand, listSubcommand, deleteSubcommand],
17
+ });
@@ -0,0 +1,125 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { threadList, type Thread } from '@agentuity/server';
5
+ import { getCommand } from '../../../command-prefix';
6
+ import { ErrorCode } from '../../../errors';
7
+ import { getCatalystAPIClient } from '../../../config';
8
+
9
+ const ThreadListResponseSchema = z.array(
10
+ z.object({
11
+ id: z.string().describe('Thread ID'),
12
+ created_at: z.string().describe('Creation timestamp'),
13
+ updated_at: z.string().describe('Update timestamp'),
14
+ project_id: z.string().describe('Project ID'),
15
+ user_data: z.string().nullable().optional().describe('User data as JSON'),
16
+ })
17
+ );
18
+
19
+ export const listSubcommand = createSubcommand({
20
+ name: 'list',
21
+ description: 'List recent threads',
22
+ tags: ['read-only', 'fast', 'requires-auth'],
23
+ examples: [
24
+ { command: getCommand('cloud thread list'), description: 'List 10 most recent threads' },
25
+ {
26
+ command: getCommand('cloud thread list --count=25'),
27
+ description: 'List 25 most recent threads',
28
+ },
29
+ {
30
+ command: getCommand('cloud thread list --project-id=proj_*'),
31
+ description: 'Filter by project',
32
+ },
33
+ {
34
+ command: getCommand('cloud thread list --org-id=org_*'),
35
+ description: 'Filter by organization',
36
+ },
37
+ ],
38
+ aliases: ['ls'],
39
+ requires: { auth: true },
40
+ optional: { project: true },
41
+ idempotent: true,
42
+ pagination: {
43
+ supported: true,
44
+ defaultLimit: 10,
45
+ maxLimit: 100,
46
+ parameters: {
47
+ limit: 'count',
48
+ },
49
+ },
50
+ schema: {
51
+ options: z.object({
52
+ count: z.coerce
53
+ .number()
54
+ .int()
55
+ .min(1)
56
+ .max(100)
57
+ .default(10)
58
+ .describe('Number of threads to list (1–100)'),
59
+ orgId: z.string().optional().describe('Filter by organization ID'),
60
+ projectId: z.string().optional().describe('Filter by project ID'),
61
+ }),
62
+ response: ThreadListResponseSchema,
63
+ },
64
+ async handler(ctx) {
65
+ const { config, logger, auth, project, opts, options } = ctx;
66
+ const catalystClient = getCatalystAPIClient(config, logger, auth);
67
+
68
+ const projectId = opts.projectId || project?.projectId;
69
+ const orgId = opts.orgId;
70
+
71
+ try {
72
+ const threads = await threadList(catalystClient, {
73
+ count: opts.count,
74
+ orgId,
75
+ projectId,
76
+ });
77
+
78
+ const result = threads.map((t: Thread) => ({
79
+ id: t.id,
80
+ created_at: t.created_at,
81
+ updated_at: t.updated_at,
82
+ project_id: t.project_id,
83
+ user_data: t.user_data,
84
+ }));
85
+
86
+ if (options.json) {
87
+ return result;
88
+ }
89
+
90
+ if (threads.length === 0) {
91
+ tui.info('No threads found.');
92
+ return [];
93
+ }
94
+
95
+ const tableData = threads.map((t: Thread) => {
96
+ return {
97
+ ID: t.id,
98
+ Project: t.project_id,
99
+ Created: new Date(t.created_at).toLocaleString(),
100
+ Updated: new Date(t.updated_at).toLocaleString(),
101
+ 'User Data': t.user_data
102
+ ? t.user_data.length > 50
103
+ ? t.user_data.substring(0, 47) + '...'
104
+ : t.user_data
105
+ : '-',
106
+ };
107
+ });
108
+
109
+ tui.table(tableData, [
110
+ { name: 'ID', alignment: 'left' },
111
+ { name: 'Project', alignment: 'left' },
112
+ { name: 'Created', alignment: 'left' },
113
+ { name: 'Updated', alignment: 'left' },
114
+ { name: 'User Data', alignment: 'left' },
115
+ ]);
116
+
117
+ return result;
118
+ } catch (ex) {
119
+ tui.fatal(
120
+ `Failed to list threads: ${ex instanceof Error ? ex.message : String(ex)}`,
121
+ ErrorCode.API_ERROR
122
+ );
123
+ }
124
+ },
125
+ });
@@ -11,9 +11,18 @@ export const useCommand = createSubcommand({
11
11
  aliases: ['switch'],
12
12
  idempotent: true,
13
13
  examples: [
14
- { command: getCommand('profile use production'), description: 'Run production command' },
15
- { command: getCommand('profile switch staging'), description: 'Run staging command' },
16
- { command: getCommand('profile use'), description: 'Run use command' },
14
+ {
15
+ command: getCommand('profile use production'),
16
+ description: 'Switch to the "production" profile',
17
+ },
18
+ {
19
+ command: getCommand('profile switch staging'),
20
+ description: 'Switch to the "staging" profile',
21
+ },
22
+ {
23
+ command: getCommand('profile use'),
24
+ description: 'Show interactive profile selection menu',
25
+ },
17
26
  ],
18
27
  schema: {
19
28
  args: z.object({
@@ -282,14 +282,23 @@ export async function setupProject(options: SetupOptions): Promise<void> {
282
282
  }
283
283
 
284
284
  // generate and write AGENTS.md for each of the main folders
285
- const agentsAPIFile = join(dest, 'src', 'agents', 'AGENTS.md');
286
- await Bun.write(agentsAPIFile, generateAgentPrompt());
285
+ const agentsDir = join(dest, 'src', 'agents');
286
+ if (existsSync(agentsDir)) {
287
+ const agentsAPIFile = join(agentsDir, 'AGENTS.md');
288
+ await Bun.write(agentsAPIFile, generateAgentPrompt());
289
+ }
287
290
 
288
- const agentsAPIsFile = join(dest, 'src', 'apis', 'AGENTS.md');
289
- await Bun.write(agentsAPIsFile, generateAPIPrompt());
291
+ const apisDir = join(dest, 'src', 'apis');
292
+ if (existsSync(apisDir)) {
293
+ const agentsAPIsFile = join(apisDir, 'AGENTS.md');
294
+ await Bun.write(agentsAPIsFile, generateAPIPrompt());
295
+ }
290
296
 
291
- const agentsWebFile = join(dest, 'src', 'web', 'AGENTS.md');
292
- await Bun.write(agentsWebFile, generateWebPrompt());
297
+ const webDir = join(dest, 'src', 'web');
298
+ if (existsSync(webDir)) {
299
+ const agentsWebFile = join(webDir, 'AGENTS.md');
300
+ await Bun.write(agentsWebFile, generateWebPrompt());
301
+ }
293
302
  }
294
303
 
295
304
  async function replaceInFiles(dir: string, projectName: string, dirName: string): Promise<void> {
@@ -24,9 +24,12 @@ export const listSubcommand = createSubcommand({
24
24
  response: ProjectListResponseSchema,
25
25
  },
26
26
  examples: [
27
- { command: getCommand('project list'), description: 'List items' },
28
- { command: getCommand('--json project list'), description: 'Show output in JSON format' },
29
- { command: getCommand('project ls'), description: 'List items' },
27
+ { command: getCommand('project list'), description: 'List projects (human-readable)' },
28
+ { command: getCommand('--json project list'), description: 'List projects in JSON format' },
29
+ {
30
+ command: getCommand('project ls'),
31
+ description: 'Alias for "project list" — list projects (human-readable)',
32
+ },
30
33
  ],
31
34
 
32
35
  async handler(ctx) {
@@ -12,8 +12,11 @@ export const command = createCommand({
12
12
  description: 'Display version information',
13
13
  tags: ['read-only', 'fast'],
14
14
  examples: [
15
- { command: getCommand('version'), description: 'Run version command' },
16
- { command: getCommand('--version'), description: 'Show version information' },
15
+ { command: getCommand('version'), description: 'Show the CLI semantic version' },
16
+ {
17
+ command: getCommand('--version'),
18
+ description: 'Display the current installed CLI version and build metadata',
19
+ },
17
20
  ],
18
21
  schema: {
19
22
  response: VersionResponseSchema,