@agentuity/cli 0.0.57 → 0.0.59

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 (149) hide show
  1. package/dist/cmd/build/bundler.d.ts +1 -1
  2. package/dist/cmd/build/bundler.js +8 -9
  3. package/dist/cmd/build/bundler.js.map +1 -1
  4. package/dist/cmd/cloud/agent/get.d.ts +2 -0
  5. package/dist/cmd/cloud/agent/get.d.ts.map +1 -0
  6. package/dist/cmd/cloud/agent/get.js +97 -0
  7. package/dist/cmd/cloud/agent/get.js.map +1 -0
  8. package/dist/cmd/cloud/agent/index.d.ts +2 -0
  9. package/dist/cmd/cloud/agent/index.d.ts.map +1 -0
  10. package/dist/cmd/cloud/agent/index.js +11 -0
  11. package/dist/cmd/cloud/agent/index.js.map +1 -0
  12. package/dist/cmd/cloud/agent/list.d.ts +2 -0
  13. package/dist/cmd/cloud/agent/list.d.ts.map +1 -0
  14. package/dist/cmd/cloud/agent/list.js +101 -0
  15. package/dist/cmd/cloud/agent/list.js.map +1 -0
  16. package/dist/cmd/cloud/agent/schema.d.ts +4 -0
  17. package/dist/cmd/cloud/agent/schema.d.ts.map +1 -0
  18. package/dist/cmd/cloud/agent/schema.js +22 -0
  19. package/dist/cmd/cloud/agent/schema.js.map +1 -0
  20. package/dist/cmd/cloud/apikey/create.d.ts +2 -0
  21. package/dist/cmd/cloud/apikey/create.d.ts.map +1 -0
  22. package/dist/cmd/cloud/apikey/create.js +100 -0
  23. package/dist/cmd/cloud/apikey/create.js.map +1 -0
  24. package/dist/cmd/cloud/apikey/delete.d.ts +2 -0
  25. package/dist/cmd/cloud/apikey/delete.d.ts.map +1 -0
  26. package/dist/cmd/cloud/apikey/delete.js +55 -0
  27. package/dist/cmd/cloud/apikey/delete.js.map +1 -0
  28. package/dist/cmd/cloud/apikey/get.d.ts +2 -0
  29. package/dist/cmd/cloud/apikey/get.d.ts.map +1 -0
  30. package/dist/cmd/cloud/apikey/get.js +57 -0
  31. package/dist/cmd/cloud/apikey/get.js.map +1 -0
  32. package/dist/cmd/cloud/apikey/index.d.ts.map +1 -0
  33. package/dist/cmd/cloud/apikey/index.js +13 -0
  34. package/dist/cmd/cloud/apikey/index.js.map +1 -0
  35. package/dist/cmd/cloud/apikey/list.d.ts +2 -0
  36. package/dist/cmd/cloud/apikey/list.d.ts.map +1 -0
  37. package/dist/cmd/cloud/apikey/list.js +53 -0
  38. package/dist/cmd/cloud/apikey/list.js.map +1 -0
  39. package/dist/cmd/cloud/deployment/index.d.ts.map +1 -1
  40. package/dist/cmd/cloud/deployment/index.js +2 -0
  41. package/dist/cmd/cloud/deployment/index.js.map +1 -1
  42. package/dist/cmd/cloud/deployment/logs.d.ts +2 -0
  43. package/dist/cmd/cloud/deployment/logs.d.ts.map +1 -0
  44. package/dist/cmd/cloud/deployment/logs.js +70 -0
  45. package/dist/cmd/cloud/deployment/logs.js.map +1 -0
  46. package/dist/cmd/cloud/index.d.ts.map +1 -1
  47. package/dist/cmd/cloud/index.js +8 -2
  48. package/dist/cmd/cloud/index.js.map +1 -1
  49. package/dist/cmd/cloud/session/get.js.map +1 -1
  50. package/dist/cmd/cloud/session/logs.d.ts.map +1 -1
  51. package/dist/cmd/cloud/session/logs.js +12 -9
  52. package/dist/cmd/cloud/session/logs.js.map +1 -1
  53. package/dist/cmd/cloud/stream/delete.d.ts +3 -0
  54. package/dist/cmd/cloud/stream/delete.d.ts.map +1 -0
  55. package/dist/cmd/cloud/stream/delete.js +42 -0
  56. package/dist/cmd/cloud/stream/delete.js.map +1 -0
  57. package/dist/cmd/cloud/stream/get.d.ts +3 -0
  58. package/dist/cmd/cloud/stream/get.d.ts.map +1 -0
  59. package/dist/cmd/cloud/stream/get.js +110 -0
  60. package/dist/cmd/cloud/stream/get.js.map +1 -0
  61. package/dist/cmd/cloud/stream/index.d.ts +3 -0
  62. package/dist/cmd/cloud/stream/index.d.ts.map +1 -0
  63. package/dist/cmd/cloud/stream/index.js +13 -0
  64. package/dist/cmd/cloud/stream/index.js.map +1 -0
  65. package/dist/cmd/cloud/stream/list.d.ts +3 -0
  66. package/dist/cmd/cloud/stream/list.d.ts.map +1 -0
  67. package/dist/cmd/cloud/stream/list.js +131 -0
  68. package/dist/cmd/cloud/stream/list.js.map +1 -0
  69. package/dist/cmd/cloud/stream/util.d.ts +8 -0
  70. package/dist/cmd/cloud/stream/util.d.ts.map +1 -0
  71. package/dist/cmd/cloud/stream/util.js +19 -0
  72. package/dist/cmd/cloud/stream/util.js.map +1 -0
  73. package/dist/cmd/cloud/vector/delete.d.ts +3 -0
  74. package/dist/cmd/cloud/vector/delete.d.ts.map +1 -0
  75. package/dist/cmd/cloud/vector/delete.js +90 -0
  76. package/dist/cmd/cloud/vector/delete.js.map +1 -0
  77. package/dist/cmd/cloud/vector/get.d.ts +3 -0
  78. package/dist/cmd/cloud/vector/get.d.ts.map +1 -0
  79. package/dist/cmd/cloud/vector/get.js +80 -0
  80. package/dist/cmd/cloud/vector/get.js.map +1 -0
  81. package/dist/cmd/cloud/vector/index.d.ts +3 -0
  82. package/dist/cmd/cloud/vector/index.d.ts.map +1 -0
  83. package/dist/cmd/cloud/vector/index.js +14 -0
  84. package/dist/cmd/cloud/vector/index.js.map +1 -0
  85. package/dist/cmd/cloud/vector/search.d.ts +3 -0
  86. package/dist/cmd/cloud/vector/search.d.ts.map +1 -0
  87. package/dist/cmd/cloud/vector/search.js +136 -0
  88. package/dist/cmd/cloud/vector/search.js.map +1 -0
  89. package/dist/cmd/cloud/vector/util.d.ts +8 -0
  90. package/dist/cmd/cloud/vector/util.d.ts.map +1 -0
  91. package/dist/cmd/cloud/vector/util.js +18 -0
  92. package/dist/cmd/cloud/vector/util.js.map +1 -0
  93. package/dist/cmd/dev/agents.d.ts.map +1 -1
  94. package/dist/cmd/dev/agents.js +3 -2
  95. package/dist/cmd/dev/agents.js.map +1 -1
  96. package/dist/cmd/dev/index.d.ts.map +1 -1
  97. package/dist/cmd/dev/index.js +103 -55
  98. package/dist/cmd/dev/index.js.map +1 -1
  99. package/dist/cmd/index.d.ts.map +1 -1
  100. package/dist/cmd/index.js +38 -42
  101. package/dist/cmd/index.js.map +1 -1
  102. package/dist/schema-parser.d.ts.map +1 -1
  103. package/dist/schema-parser.js +3 -1
  104. package/dist/schema-parser.js.map +1 -1
  105. package/dist/tui.d.ts +10 -1
  106. package/dist/tui.d.ts.map +1 -1
  107. package/dist/tui.js +67 -41
  108. package/dist/tui.js.map +1 -1
  109. package/dist/utils/date.d.ts +14 -0
  110. package/dist/utils/date.d.ts.map +1 -0
  111. package/dist/utils/date.js +51 -0
  112. package/dist/utils/date.js.map +1 -0
  113. package/package.json +6 -6
  114. package/src/cmd/build/bundler.ts +8 -8
  115. package/src/cmd/cloud/agent/get.ts +105 -0
  116. package/src/cmd/cloud/agent/index.ts +11 -0
  117. package/src/cmd/cloud/agent/list.ts +107 -0
  118. package/src/cmd/cloud/agent/schema.ts +25 -0
  119. package/src/cmd/cloud/apikey/create.ts +114 -0
  120. package/src/cmd/cloud/apikey/delete.ts +66 -0
  121. package/src/cmd/cloud/apikey/get.ts +62 -0
  122. package/src/cmd/cloud/apikey/index.ts +13 -0
  123. package/src/cmd/cloud/apikey/list.ts +58 -0
  124. package/src/cmd/cloud/deployment/index.ts +2 -0
  125. package/src/cmd/cloud/deployment/logs.ts +75 -0
  126. package/src/cmd/cloud/index.ts +8 -2
  127. package/src/cmd/cloud/session/get.ts +5 -5
  128. package/src/cmd/cloud/session/logs.ts +12 -11
  129. package/src/cmd/cloud/stream/delete.ts +49 -0
  130. package/src/cmd/cloud/stream/get.ts +118 -0
  131. package/src/cmd/cloud/stream/index.ts +14 -0
  132. package/src/cmd/cloud/stream/list.ts +146 -0
  133. package/src/cmd/cloud/stream/util.ts +32 -0
  134. package/src/cmd/cloud/vector/delete.ts +105 -0
  135. package/src/cmd/cloud/vector/get.ts +94 -0
  136. package/src/cmd/cloud/vector/index.ts +15 -0
  137. package/src/cmd/cloud/vector/search.ts +156 -0
  138. package/src/cmd/cloud/vector/util.ts +28 -0
  139. package/src/cmd/dev/agents.ts +4 -3
  140. package/src/cmd/dev/index.ts +114 -59
  141. package/src/cmd/index.ts +38 -42
  142. package/src/schema-parser.ts +3 -1
  143. package/src/tui.ts +80 -48
  144. package/src/utils/date.ts +57 -0
  145. package/dist/cmd/cloud/agents/index.d.ts.map +0 -1
  146. package/dist/cmd/cloud/agents/index.js +0 -133
  147. package/dist/cmd/cloud/agents/index.js.map +0 -1
  148. package/src/cmd/cloud/agents/index.ts +0 -148
  149. /package/dist/cmd/cloud/{agents → apikey}/index.d.ts +0 -0
@@ -0,0 +1,156 @@
1
+ import { z } from 'zod';
2
+ import { createCommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { createStorageAdapter } from './util';
5
+ import { getCommand } from '../../../command-prefix';
6
+
7
+ const VectorSearchResultSchema = z.object({
8
+ id: z.string().describe('Vector ID'),
9
+ key: z.string().describe('Vector key'),
10
+ similarity: z.number().describe('Similarity score (0-1)'),
11
+ metadata: z.record(z.string(), z.unknown()).optional().describe('Vector metadata'),
12
+ });
13
+
14
+ const VectorSearchResponseSchema = z.object({
15
+ namespace: z.string().describe('Namespace name'),
16
+ query: z.string().describe('Search query used'),
17
+ results: z.array(VectorSearchResultSchema).describe('Search results'),
18
+ count: z.number().describe('Number of results found'),
19
+ });
20
+
21
+ export const searchSubcommand = createCommand({
22
+ name: 'search',
23
+ aliases: ['list', 'ls'],
24
+ description: 'Search for vectors using semantic similarity',
25
+ tags: ['read-only', 'slow', 'requires-auth'],
26
+ requires: { auth: true, project: true },
27
+ idempotent: true,
28
+ examples: [
29
+ `${getCommand('vector search products "comfortable office chair"')} - Search for similar products`,
30
+ `${getCommand('vector list knowledge-base "machine learning"')} - Search knowledge base`,
31
+ `${getCommand('vector search docs "API documentation" --limit 5')} - Limit results`,
32
+ `${getCommand('vector search products "ergonomic" --similarity 0.8')} - Set minimum similarity`,
33
+ `${getCommand('vector ls embeddings "neural networks" --metadata category=ai')} - Filter by metadata`,
34
+ ],
35
+ schema: {
36
+ args: z.object({
37
+ namespace: z.string().min(1).describe('the vector storage namespace'),
38
+ query: z.string().min(1).describe('the search query text'),
39
+ }),
40
+ options: z.object({
41
+ limit: z.number().optional().describe('maximum number of results to return (default: 10)'),
42
+ similarity: z
43
+ .number()
44
+ .min(0)
45
+ .max(1)
46
+ .optional()
47
+ .describe('minimum similarity threshold (0.0-1.0)'),
48
+ metadata: z
49
+ .string()
50
+ .optional()
51
+ .describe('filter by metadata (format: key=value or key1=value1,key2=value2)'),
52
+ }),
53
+ response: VectorSearchResponseSchema,
54
+ },
55
+
56
+ async handler(ctx) {
57
+ const { args, opts, options } = ctx;
58
+ const storage = await createStorageAdapter(ctx);
59
+ const started = Date.now();
60
+
61
+ // Parse metadata filter if provided
62
+ let metadataFilter: Record<string, unknown> | undefined;
63
+ if (opts.metadata) {
64
+ const validPairs: Record<string, unknown> = {};
65
+ const malformed: string[] = [];
66
+ const pairs = opts.metadata.split(',');
67
+
68
+ for (const pair of pairs) {
69
+ const trimmedPair = pair.trim();
70
+ if (!trimmedPair) continue;
71
+
72
+ const firstEqualIdx = trimmedPair.indexOf('=');
73
+ if (firstEqualIdx === -1) {
74
+ malformed.push(trimmedPair);
75
+ continue;
76
+ }
77
+
78
+ const key = trimmedPair.substring(0, firstEqualIdx).trim();
79
+ const value = trimmedPair.substring(firstEqualIdx + 1).trim();
80
+
81
+ if (!key || !value) {
82
+ malformed.push(trimmedPair);
83
+ continue;
84
+ }
85
+
86
+ // Try to parse as JSON for complex values, otherwise use as string
87
+ try {
88
+ validPairs[key] = JSON.parse(value);
89
+ } catch {
90
+ validPairs[key] = value;
91
+ }
92
+ }
93
+
94
+ if (malformed.length > 0) {
95
+ ctx.logger.warn(`Skipping malformed metadata pairs: ${malformed.join(', ')}`);
96
+ }
97
+
98
+ if (Object.keys(validPairs).length > 0) {
99
+ metadataFilter = validPairs;
100
+ }
101
+ }
102
+
103
+ const results = await storage.search(args.namespace, {
104
+ query: args.query,
105
+ limit: opts.limit,
106
+ similarity: opts.similarity,
107
+ metadata: metadataFilter,
108
+ });
109
+
110
+ const durationMs = Date.now() - started;
111
+
112
+ if (!options.json) {
113
+ if (results.length === 0) {
114
+ tui.info(
115
+ `No vectors found matching "${tui.bold(args.query)}" in ${tui.bold(args.namespace)}`
116
+ );
117
+ } else {
118
+ tui.info(
119
+ `Found ${results.length} result(s) in ${tui.bold(args.namespace)} (${durationMs}ms):`
120
+ );
121
+
122
+ const tableData = results.map((result) => {
123
+ const metadataStr = result.metadata ? JSON.stringify(result.metadata) : '-';
124
+ return {
125
+ Key: result.key,
126
+ ID: result.id,
127
+ Similarity: `${(result.similarity * 100).toFixed(1)}%`,
128
+ Metadata:
129
+ metadataStr.length > 50 ? metadataStr.substring(0, 47) + '...' : metadataStr,
130
+ };
131
+ });
132
+
133
+ tui.table(tableData, [
134
+ { name: 'Key', alignment: 'left' },
135
+ { name: 'ID', alignment: 'left' },
136
+ { name: 'Similarity', alignment: 'right' },
137
+ { name: 'Metadata', alignment: 'left' },
138
+ ]);
139
+ }
140
+ }
141
+
142
+ return {
143
+ namespace: args.namespace,
144
+ query: args.query,
145
+ results: results.map((r) => ({
146
+ id: r.id,
147
+ key: r.key,
148
+ similarity: r.similarity,
149
+ metadata: r.metadata,
150
+ })),
151
+ count: results.length,
152
+ };
153
+ },
154
+ });
155
+
156
+ export default searchSubcommand;
@@ -0,0 +1,28 @@
1
+ import { Logger, VectorStorageService } from '@agentuity/core';
2
+ import { createServerFetchAdapter } from '@agentuity/server';
3
+ import { loadProjectSDKKey } from '../../../config';
4
+ import type { Config } from '../../../types';
5
+ import * as tui from '../../../tui';
6
+
7
+ export async function createStorageAdapter(ctx: {
8
+ logger: Logger;
9
+ projectDir: string;
10
+ config: Config | null;
11
+ }) {
12
+ const sdkKey = await loadProjectSDKKey(ctx.projectDir);
13
+ if (!sdkKey) {
14
+ tui.fatal(`Couldn't find the AGENTUITY_SDK_KEY in ${ctx.projectDir} .env file`);
15
+ }
16
+
17
+ const adapter = createServerFetchAdapter(
18
+ {
19
+ headers: {
20
+ Authorization: `Bearer ${sdkKey}`,
21
+ },
22
+ },
23
+ ctx.logger
24
+ );
25
+
26
+ const baseUrl = ctx.config?.overrides?.catalyst_url ?? 'https://catalyst.agentuity.cloud';
27
+ return new VectorStorageService(baseUrl, adapter);
28
+ }
@@ -8,7 +8,7 @@ const AgentSchema = z.object({
8
8
  id: z.string(),
9
9
  name: z.string(),
10
10
  description: z.string().nullable(),
11
- identifier: z.string(),
11
+ identifier: z.string().nullable(), // nullable for legacy records
12
12
  version: z.string().nullable(),
13
13
  deploymentId: z.string().nullable(),
14
14
  devmode: z.boolean(),
@@ -75,7 +75,8 @@ export const agentsSubcommand = createSubcommand({
75
75
  tui.fatal(`Failed to fetch agents: ${response.message ?? 'Unknown error'}`);
76
76
  }
77
77
 
78
- const agents = response.data;
78
+ // Filter out legacy agents without identifiers
79
+ const agents = response.data.filter((agent) => agent.identifier !== null);
79
80
 
80
81
  if (format === 'json' && !options.json) {
81
82
  console.log(JSON.stringify(agents, null, 2));
@@ -91,7 +92,7 @@ export const agentsSubcommand = createSubcommand({
91
92
  agents.map((agent) => ({
92
93
  name: agent.name,
93
94
  id: verbose ? agent.id : abbreviate(agent.id, 20),
94
- identifier: verbose ? agent.identifier : abbreviate(agent.identifier, 20),
95
+ identifier: verbose ? agent.identifier! : abbreviate(agent.identifier!, 20),
95
96
  deployment: abbreviate(agent.deploymentId, 20),
96
97
  version: verbose
97
98
  ? (agent.version ?? 'N/A')
@@ -110,7 +110,7 @@ export const command = createCommand({
110
110
  );
111
111
  const deploymentId = getDevmodeDeploymentId(project?.projectId ?? '', devmode?.id ?? '');
112
112
 
113
- if (devmode) {
113
+ if (devmode && opts.public) {
114
114
  const configDir = getDefaultConfigDir();
115
115
  const gravityDir = join(configDir, 'gravity');
116
116
  let mustCheck = true;
@@ -238,48 +238,52 @@ export const command = createCommand({
238
238
  let gravityClient: Bun.Subprocess | undefined;
239
239
  let initialStartupComplete = false;
240
240
 
241
- if (gravityBin && devmode && project) {
242
- const sdkKey = await loadProjectSDKKey(rootDir);
243
- if (!sdkKey) {
244
- tui.warning(`Couldn't find the AGENTUITY_SDK_KEY in ${rootDir} .env file`);
245
- } else {
246
- const gravityBinExists = await Bun.file(gravityBin).exists();
247
- if (!gravityBinExists) {
248
- logger.error(
249
- `Gravity binary not found at ${gravityBin}, skipping gravity client startup`
250
- );
251
- } else {
252
- try {
253
- gravityClient = Bun.spawn(
254
- [
255
- gravityBin,
256
- '--endpoint-id',
257
- devmode.id,
258
- '--port',
259
- env.PORT,
260
- '--url',
261
- config?.overrides?.gravity_url ?? 'grpc://devmode.agentuity.com',
262
- '--log-level',
263
- process.env.AGENTUITY_GRAVITY_LOG_LEVEL ?? 'error',
264
- ],
265
- {
266
- cwd: rootDir,
267
- stdout: 'inherit',
268
- stderr: 'inherit',
269
- stdin: 'ignore',
270
- env: { ...env, AGENTUITY_SDK_KEY: sdkKey },
271
- }
272
- );
273
- gravityClient.exited.then(() => {
274
- logger.debug('gravity client exited');
275
- });
276
- } catch (err) {
277
- logger.error(
278
- 'Failed to spawn gravity client: %s',
279
- err instanceof Error ? err.message : String(err)
280
- );
241
+ const sdkKey = await loadProjectSDKKey(rootDir);
242
+ if (!sdkKey) {
243
+ tui.warning(`Couldn't find the AGENTUITY_SDK_KEY in ${rootDir} .env file`);
244
+ }
245
+ const gravityBinExists = gravityBin ? await Bun.file(gravityBin).exists() : true;
246
+ if (!gravityBinExists) {
247
+ logger.error(`Gravity binary not found at ${gravityBin}, skipping gravity client startup`);
248
+ }
249
+
250
+ async function restartGravityClient() {
251
+ if (gravityClient) {
252
+ gravityClient.kill('SIGINT');
253
+ gravityClient.kill();
254
+ }
255
+ if (!devmode || !opts.public) {
256
+ return;
257
+ }
258
+ try {
259
+ gravityClient = Bun.spawn(
260
+ [
261
+ gravityBin!,
262
+ '--endpoint-id',
263
+ devmode.id,
264
+ '--port',
265
+ env.PORT!,
266
+ '--url',
267
+ config?.overrides?.gravity_url ?? 'grpc://devmode.agentuity.com',
268
+ '--log-level',
269
+ process.env.AGENTUITY_GRAVITY_LOG_LEVEL ?? 'error',
270
+ ],
271
+ {
272
+ cwd: rootDir,
273
+ stdout: 'inherit',
274
+ stderr: 'inherit',
275
+ stdin: 'ignore',
276
+ env: { ...env, AGENTUITY_SDK_KEY: sdkKey },
281
277
  }
282
- }
278
+ );
279
+ gravityClient.exited.then(() => {
280
+ logger.debug('gravity client exited');
281
+ });
282
+ } catch (err) {
283
+ logger.error(
284
+ 'Failed to spawn gravity client: %s',
285
+ err instanceof Error ? err.message : String(err)
286
+ );
283
287
  }
284
288
  }
285
289
 
@@ -307,14 +311,42 @@ export const command = createCommand({
307
311
  }
308
312
  }
309
313
 
314
+ let lastErrorLineCount = 0;
315
+ let showedRestartMessage = false;
316
+
317
+ function clearRestartMessage() {
318
+ if (showedRestartMessage) {
319
+ process.stdout.write('\x1b[1A\x1b[2K');
320
+ showedRestartMessage = false;
321
+ }
322
+ }
323
+
310
324
  function failure(msg: string) {
311
325
  failed = true;
312
326
  failures++;
327
+ // Exit immediately on initial startup failure
328
+ if (!initialStartupComplete) {
329
+ tui.fatal(msg);
330
+ }
331
+ // During hot reload, show error but don't exit unless too many failures
313
332
  if (failures >= 5) {
314
333
  tui.error(msg);
315
334
  tui.fatal('too many failures, exiting');
316
335
  } else {
317
- setImmediate(() => tui.error(msg));
336
+ // Ensure we're on a new line before printing error
337
+ tui.error(msg);
338
+ // Track lines: 1 for "✗ Building..." + 1 for error message
339
+ lastErrorLineCount = 2;
340
+ }
341
+ }
342
+
343
+ function clearLastError() {
344
+ if (lastErrorLineCount > 0) {
345
+ // Move cursor up and clear each line
346
+ for (let i = 0; i < lastErrorLineCount; i++) {
347
+ process.stdout.write('\x1b[1A\x1b[2K');
348
+ }
349
+ lastErrorLineCount = 0;
318
350
  }
319
351
  }
320
352
 
@@ -406,6 +438,7 @@ export const command = createCommand({
406
438
  logger.trace('Server is running, killing before restart');
407
439
  checkRestartThrottle();
408
440
  tui.info('Restarting on file change');
441
+ showedRestartMessage = true;
409
442
  await kill();
410
443
  logger.trace('Server killed, continuing with restart');
411
444
  // Continue with restart after kill completes
@@ -413,11 +446,16 @@ export const command = createCommand({
413
446
  logger.trace('Initial server start');
414
447
  }
415
448
  logger.trace('Starting typecheck and build...');
416
- await tui.spinner({
417
- message: 'Building project',
418
- clearOnSuccess: true,
419
- callback: async () => {
420
- try {
449
+
450
+ // Clear any previous error before starting new build
451
+ clearLastError();
452
+ clearRestartMessage();
453
+
454
+ try {
455
+ await tui.spinner({
456
+ message: 'Building...',
457
+ clearOnSuccess: true,
458
+ callback: async () => {
421
459
  logger.trace('Bundle starting...');
422
460
  building = true;
423
461
  await bundle({
@@ -431,7 +469,7 @@ export const command = createCommand({
431
469
  buildCompletedAt = Date.now();
432
470
  logger.trace('Bundle completed successfully');
433
471
  logger.trace('tsc starting...');
434
- await tui.runCommand({
472
+ const tscExitCode = await tui.runCommand({
435
473
  command: 'tsc',
436
474
  cmd: ['bunx', 'tsc', '--noEmit'],
437
475
  cwd: rootDir,
@@ -440,15 +478,32 @@ export const command = createCommand({
440
478
  maxLinesOutput: 2,
441
479
  maxLinesOnFailure: 15,
442
480
  });
481
+ if (tscExitCode !== 0) {
482
+ logger.trace('tsc failed with exit code %d', tscExitCode);
483
+ failure('Type check failed');
484
+ return;
485
+ }
443
486
  logger.trace('tsc completed successfully');
444
- } catch (error) {
445
- building = false;
446
- logger.trace('Bundle failed: %s', error);
447
- failure('Build failed');
448
- return;
487
+ await restartGravityClient();
488
+ },
489
+ });
490
+ } catch (error) {
491
+ building = false;
492
+ const e = error as Error;
493
+ if (e.constructor.name === 'AggregateError') {
494
+ const ex = e as AggregateError;
495
+ for (const err of ex.errors) {
496
+ if (err) {
497
+ failure(err);
498
+ return;
499
+ }
449
500
  }
450
- },
451
- });
501
+ return;
502
+ }
503
+ const errorMsg = error instanceof Error ? error.message : String(error);
504
+ failure(errorMsg);
505
+ return;
506
+ }
452
507
 
453
508
  logger.trace('Typecheck and build completed');
454
509
 
@@ -457,6 +512,9 @@ export const command = createCommand({
457
512
  return;
458
513
  }
459
514
 
515
+ // Reset failure counter on successful build
516
+ failures = 0;
517
+
460
518
  logger.trace('Checking if app file exists: %s', appPath);
461
519
  if (!existsSync(appPath)) {
462
520
  logger.trace('App file not found: %s', appPath);
@@ -548,9 +606,6 @@ export const command = createCommand({
548
606
 
549
607
  if (showInitialReadyMessage) {
550
608
  showInitialReadyMessage = false;
551
- // Clear any lingering spinner/command output - clear everything below cursor
552
- process.stderr.write('\x1B[J'); // Clear from cursor to end of screen
553
- process.stdout.write('\x1B[J'); // Clear from cursor to end of screen
554
609
  logger.info('DevMode ready 🚀');
555
610
  logger.trace('Initial ready message logged');
556
611
  // Mark initial startup complete immediately to prevent watcher restarts
package/src/cmd/index.ts CHANGED
@@ -1,53 +1,49 @@
1
- import { readdir } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
1
  import type { CommandDefinition } from '../types';
4
2
 
3
+ // Use dynamic imports for bundler compatibility while maintaining lazy loading
5
4
  export async function discoverCommands(): Promise<CommandDefinition[]> {
6
- const cmdDir = join(import.meta.dir);
7
- const entries = await readdir(cmdDir, { withFileTypes: true });
5
+ const commandModules = await Promise.all([
6
+ import('./ai').then((m) => m.command),
7
+ import('./auth').then((m) => m.command),
8
+ import('./build').then((m) => m.command),
9
+ import('./cloud').then((m) => m.command),
10
+ import('./dev').then((m) => m.command),
11
+ import('./profile').then((m) => m.command),
12
+ import('./project').then((m) => m.command),
13
+ import('./repl').then((m) => m.command),
14
+ import('./version').then((m) => m.command),
15
+ ]);
8
16
 
9
17
  const commands: CommandDefinition[] = [];
10
18
 
11
- for (const entry of entries) {
12
- if (entry.isDirectory()) {
13
- try {
14
- const modulePath = join(cmdDir, entry.name, 'index.ts');
15
- const module = await import(modulePath);
19
+ for (const cmd of commandModules) {
20
+ commands.push(cmd);
16
21
 
17
- if (module.default || module.command) {
18
- const cmd = module.default || module.command;
19
- commands.push(cmd);
20
-
21
- // Auto-create hidden top-level aliases for subcommands with toplevel: true
22
- if (cmd.subcommands) {
23
- for (const subcommand of cmd.subcommands) {
24
- if (subcommand.toplevel) {
25
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
- const alias: any = {
27
- name: subcommand.name,
28
- description: subcommand.description,
29
- aliases: subcommand.aliases,
30
- hidden: true,
31
- requires: subcommand.requires,
32
- optional: subcommand.optional,
33
- schema: subcommand.schema,
34
- handler: subcommand.handler,
35
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
- idempotent: (subcommand as any).idempotent,
37
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
- prerequisites: (subcommand as any).prerequisites,
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
- tags: (subcommand as any).tags,
41
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
- examples: (subcommand as any).examples,
43
- };
44
- commands.push(alias as CommandDefinition);
45
- }
46
- }
47
- }
22
+ // Auto-create hidden top-level aliases for subcommands with toplevel: true
23
+ if (cmd.subcommands) {
24
+ for (const subcommand of cmd.subcommands) {
25
+ if (subcommand.toplevel) {
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ const alias: any = {
28
+ name: subcommand.name,
29
+ description: subcommand.description,
30
+ aliases: subcommand.aliases,
31
+ hidden: true,
32
+ requires: subcommand.requires,
33
+ optional: subcommand.optional,
34
+ schema: subcommand.schema,
35
+ handler: subcommand.handler,
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ idempotent: (subcommand as any).idempotent,
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
+ prerequisites: (subcommand as any).prerequisites,
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ tags: (subcommand as any).tags,
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
+ examples: (subcommand as any).examples,
44
+ };
45
+ commands.push(alias as CommandDefinition);
48
46
  }
49
- } catch (error) {
50
- console.warn(`Warning: Failed to load command from ${entry.name}:`, error);
51
47
  }
52
48
  }
53
49
  }
@@ -216,7 +216,9 @@ export function buildValidationInput(
216
216
  const parsed = parseOptionsSchema(schemas.options);
217
217
  for (const opt of parsed) {
218
218
  // Only include the option if it has a value - omitting undefined allows Zod to apply defaults
219
- const value = rawOptions[opt.name];
219
+ // Commander.js converts kebab-case to camelCase, so we need to check both
220
+ const camelCaseName = opt.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
221
+ const value = rawOptions[opt.name] ?? rawOptions[camelCaseName];
220
222
  if (value !== undefined) {
221
223
  result.options[opt.name] = value;
222
224
  }