@agentuity/cli 0.1.34 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/bin/cli.ts +110 -6
  2. package/dist/cmd/ai/index.d.ts.map +1 -1
  3. package/dist/cmd/ai/index.js +0 -6
  4. package/dist/cmd/ai/index.js.map +1 -1
  5. package/dist/cmd/ai/opencode/uninstall.js +1 -1
  6. package/dist/cmd/ai/opencode/uninstall.js.map +1 -1
  7. package/dist/cmd/build/vite/bun-dev-server.d.ts +6 -0
  8. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  9. package/dist/cmd/build/vite/bun-dev-server.js +100 -33
  10. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  11. package/dist/cmd/cloud/apikey/create.d.ts.map +1 -1
  12. package/dist/cmd/cloud/apikey/create.js +1 -1
  13. package/dist/cmd/cloud/apikey/create.js.map +1 -1
  14. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  15. package/dist/cmd/cloud/deploy.js +4 -1
  16. package/dist/cmd/cloud/deploy.js.map +1 -1
  17. package/dist/cmd/cloud/env/delete.d.ts.map +1 -1
  18. package/dist/cmd/cloud/env/delete.js +100 -45
  19. package/dist/cmd/cloud/env/delete.js.map +1 -1
  20. package/dist/cmd/cloud/env/pull.d.ts.map +1 -1
  21. package/dist/cmd/cloud/env/pull.js +1 -1
  22. package/dist/cmd/cloud/env/pull.js.map +1 -1
  23. package/dist/cmd/cloud/machine/delete.js +1 -1
  24. package/dist/cmd/cloud/machine/delete.js.map +1 -1
  25. package/dist/cmd/dev/index.d.ts.map +1 -1
  26. package/dist/cmd/dev/index.js +58 -10
  27. package/dist/cmd/dev/index.js.map +1 -1
  28. package/dist/cmd/index.d.ts.map +1 -1
  29. package/dist/cmd/index.js +1 -0
  30. package/dist/cmd/index.js.map +1 -1
  31. package/dist/cmd/support/index.d.ts +2 -0
  32. package/dist/cmd/support/index.d.ts.map +1 -0
  33. package/dist/cmd/support/index.js +11 -0
  34. package/dist/cmd/support/index.js.map +1 -0
  35. package/dist/cmd/support/logs/index.d.ts +3 -0
  36. package/dist/cmd/support/logs/index.d.ts.map +1 -0
  37. package/dist/cmd/support/logs/index.js +9 -0
  38. package/dist/cmd/support/logs/index.js.map +1 -0
  39. package/dist/cmd/support/logs/path.d.ts +3 -0
  40. package/dist/cmd/support/logs/path.d.ts.map +1 -0
  41. package/dist/cmd/support/logs/path.js +52 -0
  42. package/dist/cmd/support/logs/path.js.map +1 -0
  43. package/dist/cmd/support/logs/show.d.ts +3 -0
  44. package/dist/cmd/support/logs/show.d.ts.map +1 -0
  45. package/dist/cmd/support/logs/show.js +121 -0
  46. package/dist/cmd/support/logs/show.js.map +1 -0
  47. package/dist/cmd/support/report.d.ts +3 -0
  48. package/dist/cmd/support/report.d.ts.map +1 -0
  49. package/dist/cmd/support/report.js +299 -0
  50. package/dist/cmd/support/report.js.map +1 -0
  51. package/dist/cmd/support/system.d.ts +3 -0
  52. package/dist/cmd/support/system.d.ts.map +1 -0
  53. package/dist/cmd/support/system.js +120 -0
  54. package/dist/cmd/support/system.js.map +1 -0
  55. package/dist/cmd/version/index.d.ts.map +1 -1
  56. package/dist/cmd/version/index.js +1 -0
  57. package/dist/cmd/version/index.js.map +1 -1
  58. package/dist/composite-logger.d.ts +35 -0
  59. package/dist/composite-logger.d.ts.map +1 -0
  60. package/dist/composite-logger.js +78 -0
  61. package/dist/composite-logger.js.map +1 -0
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +2 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/internal-logger.d.ts +77 -0
  67. package/dist/internal-logger.d.ts.map +1 -0
  68. package/dist/internal-logger.js +363 -0
  69. package/dist/internal-logger.js.map +1 -0
  70. package/dist/types.d.ts +6 -0
  71. package/dist/types.d.ts.map +1 -1
  72. package/dist/types.js.map +1 -1
  73. package/package.json +6 -6
  74. package/src/cmd/ai/index.ts +0 -6
  75. package/src/cmd/ai/opencode/uninstall.ts +1 -1
  76. package/src/cmd/build/vite/bun-dev-server.ts +113 -36
  77. package/src/cmd/cloud/apikey/create.ts +3 -1
  78. package/src/cmd/cloud/deploy.ts +4 -1
  79. package/src/cmd/cloud/env/delete.ts +100 -45
  80. package/src/cmd/cloud/env/pull.ts +1 -6
  81. package/src/cmd/cloud/machine/delete.ts +1 -1
  82. package/src/cmd/dev/index.ts +59 -11
  83. package/src/cmd/index.ts +1 -0
  84. package/src/cmd/support/index.ts +11 -0
  85. package/src/cmd/support/logs/index.ts +9 -0
  86. package/src/cmd/support/logs/path.ts +56 -0
  87. package/src/cmd/support/logs/show.ts +144 -0
  88. package/src/cmd/support/report.ts +364 -0
  89. package/src/cmd/support/system.ts +130 -0
  90. package/src/cmd/version/index.ts +1 -0
  91. package/src/composite-logger.ts +86 -0
  92. package/src/index.ts +7 -0
  93. package/src/internal-logger.ts +435 -0
  94. package/src/types.ts +6 -0
  95. package/dist/cmd/ai/skills/generate.d.ts +0 -3
  96. package/dist/cmd/ai/skills/generate.d.ts.map +0 -1
  97. package/dist/cmd/ai/skills/generate.js +0 -65
  98. package/dist/cmd/ai/skills/generate.js.map +0 -1
  99. package/dist/cmd/ai/skills/generator.d.ts +0 -4
  100. package/dist/cmd/ai/skills/generator.d.ts.map +0 -1
  101. package/dist/cmd/ai/skills/generator.js +0 -410
  102. package/dist/cmd/ai/skills/generator.js.map +0 -1
  103. package/dist/cmd/ai/skills/index.d.ts +0 -4
  104. package/dist/cmd/ai/skills/index.d.ts.map +0 -1
  105. package/dist/cmd/ai/skills/index.js +0 -21
  106. package/dist/cmd/ai/skills/index.js.map +0 -1
  107. package/dist/cmd/dev/skills.d.ts +0 -10
  108. package/dist/cmd/dev/skills.d.ts.map +0 -1
  109. package/dist/cmd/dev/skills.js +0 -57
  110. package/dist/cmd/dev/skills.js.map +0 -1
  111. package/src/cmd/ai/skills/generate.ts +0 -75
  112. package/src/cmd/ai/skills/generator.ts +0 -527
  113. package/src/cmd/ai/skills/index.ts +0 -23
  114. package/src/cmd/dev/skills.ts +0 -82
@@ -3,7 +3,6 @@ import cadenceCommand from './cadence';
3
3
  import capabilitiesCommand from './capabilities';
4
4
  import promptCommand from './prompt';
5
5
  import schemaCommand from './schema';
6
- import skillsCommand from './skills';
7
6
  import opencodeCommand from './opencode';
8
7
  import { getCommand } from '../../command-prefix';
9
8
 
@@ -25,10 +24,6 @@ export const command = createCommand({
25
24
  command: getCommand('ai schema show'),
26
25
  description: 'Output CLI schema for AI consumption',
27
26
  },
28
- {
29
- command: getCommand('ai skills generate --output ./skills'),
30
- description: 'Generate Agent Skills from CLI schema',
31
- },
32
27
  ],
33
28
  subcommands: [
34
29
  opencodeCommand,
@@ -36,6 +31,5 @@ export const command = createCommand({
36
31
  capabilitiesCommand,
37
32
  promptCommand,
38
33
  schemaCommand,
39
- skillsCommand,
40
34
  ],
41
35
  });
@@ -64,7 +64,7 @@ export const uninstallSubcommand = createSubcommand({
64
64
  }
65
65
  } catch (error) {
66
66
  if (!jsonMode) {
67
- tui.warn(`Failed to parse Open Code config: ${error}`);
67
+ tui.warning(`Failed to parse Open Code config: ${error}`);
68
68
  }
69
69
  }
70
70
  }
@@ -15,6 +15,9 @@ export interface BunDevServerOptions {
15
15
  deploymentId?: string;
16
16
  logger: Logger;
17
17
  vitePort: number; // Port of already-running Vite asset server
18
+ inspect?: boolean; // Enable bun debugger
19
+ inspectWait?: boolean; // Enable bun debugger and wait for connection
20
+ inspectBrk?: boolean; // Enable bun debugger with breakpoint at first line
18
21
  }
19
22
 
20
23
  export interface BunDevServerResult {
@@ -30,17 +33,15 @@ export interface BunDevServerResult {
30
33
  *
31
34
  * The bundle is loaded here to ensure AI Gateway routing patches are active.
32
35
  * Vite port is read from process.env.VITE_PORT at runtime.
36
+ *
37
+ * When debugger flags (inspect, inspectWait, inspectBrk) are passed, bun is spawned
38
+ * as a subprocess to enable passing the debugger CLI flags.
33
39
  */
34
40
  export async function startBunDevServer(options: BunDevServerOptions): Promise<BunDevServerResult> {
35
- const { rootDir, port = 3500, logger, vitePort } = options;
41
+ const { rootDir, port = 3500, logger, vitePort, inspect, inspectWait, inspectBrk } = options;
36
42
 
37
43
  logger.debug('Starting Bun dev server (Vite already running on port %d)...', vitePort);
38
44
 
39
- // Load the bundled app - this will start Bun.serve() internally
40
- // IMPORTANT: We must import the bundled .agentuity/app.js (NOT src/generated/app.ts)
41
- // because the bundled version has LLM provider patches applied that enable AI Gateway routing.
42
- // Importing the source file directly would bypass these patches.
43
- logger.debug('📦 Loading bundled app (Bun server will start)...');
44
45
  const appPath = `${rootDir}/.agentuity/app.js`;
45
46
 
46
47
  // Verify bundle exists before attempting to load
@@ -54,29 +55,45 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
54
55
  // Set PORT env var so the generated app uses the correct port
55
56
  process.env.PORT = String(port);
56
57
 
57
- // Import the generated app with cache-busting query parameter.
58
- // Bun's module cache is keyed by the full specifier including query string,
59
- // so adding a unique timestamp forces a fresh import on each reload.
60
- const cacheBuster = `?t=${Date.now()}`;
61
- try {
62
- await import(appPath + cacheBuster);
63
- } catch (err) {
64
- const errorMessage = err instanceof Error ? err.message : String(err);
65
- logger.error('Failed to import generated app from %s: %s', appPath, errorMessage);
66
- throw new Error(`Failed to load generated app: ${errorMessage}`);
67
- }
58
+ // Check if any debugger flag is enabled
59
+ const useDebugger = inspect || inspectWait || inspectBrk;
68
60
 
69
- // Wait for server to actually start listening
70
- // The generated app sets (globalThis as any).__AGENTUITY_SERVER__ when server starts
71
- const maxRetries = 50; // Increased retries for slower systems
72
- const retryDelay = 100; // ms
73
- let serverReady = false;
61
+ if (useDebugger) {
62
+ // Spawn bun as subprocess with debugger flag
63
+ logger.debug('📦 Spawning bun with debugger enabled...');
64
+
65
+ // Determine which debugger flag to use (priority: inspectBrk > inspectWait > inspect)
66
+ let debugFlag: string;
67
+ if (inspectBrk) {
68
+ debugFlag = '--inspect-brk';
69
+ } else if (inspectWait) {
70
+ debugFlag = '--inspect-wait';
71
+ } else {
72
+ debugFlag = '--inspect';
73
+ }
74
74
 
75
- for (let i = 0; i < maxRetries; i++) {
76
- // Check if global server object exists
75
+ logger.debug('Using debugger flag: %s', debugFlag);
76
+
77
+ const bunProcess = Bun.spawn(['bun', debugFlag, 'run', appPath], {
78
+ cwd: rootDir,
79
+ stdout: 'inherit',
80
+ stderr: 'inherit',
81
+ env: {
82
+ ...process.env,
83
+ PORT: String(port),
84
+ },
85
+ });
86
+
87
+ // Store the process globally so it can be killed on shutdown
77
88
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
- if ((globalThis as any).__AGENTUITY_SERVER__) {
79
- // Server object exists, now verify it's actually listening by making a request
89
+ (globalThis as any).__AGENTUITY_BUN_SUBPROCESS__ = bunProcess;
90
+
91
+ // Wait for server to actually start listening
92
+ const maxRetries = 50;
93
+ const retryDelay = 100;
94
+ let serverReady = false;
95
+
96
+ for (let i = 0; i < maxRetries; i++) {
80
97
  try {
81
98
  await fetch(`http://127.0.0.1:${port}/`, {
82
99
  method: 'HEAD',
@@ -88,19 +105,79 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
88
105
  } catch {
89
106
  // Connection refused or timeout - server not ready yet
90
107
  }
108
+ // Wait before next check
109
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
91
110
  }
92
- // Wait before next check
93
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
94
- }
95
111
 
96
- if (!serverReady) {
97
- throw new Error(
98
- `Bun server failed to start on port ${port} after ${maxRetries * retryDelay}ms`
99
- );
100
- }
112
+ if (!serverReady) {
113
+ // Kill the subprocess if server didn't start
114
+ try {
115
+ bunProcess.kill();
116
+ } catch (err) {
117
+ logger.debug('Error killing subprocess during startup failure: %s', err);
118
+ }
119
+ throw new Error(
120
+ `Bun server failed to start on port ${port} after ${maxRetries * retryDelay}ms`
121
+ );
122
+ }
123
+
124
+ logger.debug(`Bun dev server started on http://127.0.0.1:${port} with debugger enabled`);
125
+ logger.debug(`Asset requests (/@vite/*, /src/web/*, etc.) proxied to Vite:${vitePort}`);
126
+ } else {
127
+ // Load the bundled app - this will start Bun.serve() internally
128
+ // IMPORTANT: We must import the bundled .agentuity/app.js (NOT src/generated/app.ts)
129
+ // because the bundled version has LLM provider patches applied that enable AI Gateway routing.
130
+ // Importing the source file directly would bypass these patches.
131
+ logger.debug('📦 Loading bundled app (Bun server will start)...');
132
+
133
+ // Import the generated app with cache-busting query parameter.
134
+ // Bun's module cache is keyed by the full specifier including query string,
135
+ // so adding a unique timestamp forces a fresh import on each reload.
136
+ const cacheBuster = `?t=${Date.now()}`;
137
+ try {
138
+ await import(appPath + cacheBuster);
139
+ } catch (err) {
140
+ const errorMessage = err instanceof Error ? err.message : String(err);
141
+ logger.error('Failed to import generated app from %s: %s', appPath, errorMessage);
142
+ throw new Error(`Failed to load generated app: ${errorMessage}`);
143
+ }
144
+
145
+ // Wait for server to actually start listening
146
+ // The generated app sets (globalThis as any).__AGENTUITY_SERVER__ when server starts
147
+ const maxRetries = 50; // Increased retries for slower systems
148
+ const retryDelay = 100; // ms
149
+ let serverReady = false;
101
150
 
102
- logger.debug(`Bun dev server started on http://127.0.0.1:${port}`);
103
- logger.debug(`Asset requests (/@vite/*, /src/web/*, etc.) proxied to Vite:${vitePort}`);
151
+ for (let i = 0; i < maxRetries; i++) {
152
+ // Check if global server object exists
153
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
+ if ((globalThis as any).__AGENTUITY_SERVER__) {
155
+ // Server object exists, now verify it's actually listening by making a request
156
+ try {
157
+ await fetch(`http://127.0.0.1:${port}/`, {
158
+ method: 'HEAD',
159
+ signal: AbortSignal.timeout(1000),
160
+ });
161
+ // Any response (even 404) means server is listening
162
+ serverReady = true;
163
+ break;
164
+ } catch {
165
+ // Connection refused or timeout - server not ready yet
166
+ }
167
+ }
168
+ // Wait before next check
169
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
170
+ }
171
+
172
+ if (!serverReady) {
173
+ throw new Error(
174
+ `Bun server failed to start on port ${port} after ${maxRetries * retryDelay}ms`
175
+ );
176
+ }
177
+
178
+ logger.debug(`Bun dev server started on http://127.0.0.1:${port}`);
179
+ logger.debug(`Asset requests (/@vite/*, /src/web/*, etc.) proxied to Vite:${vitePort}`);
180
+ }
104
181
 
105
182
  return {
106
183
  bunServerPort: port,
@@ -95,7 +95,9 @@ export const createSubcommand = createSubcommandHelper({
95
95
  tui.newline();
96
96
  tui.success('API key created successfully!');
97
97
  tui.newline();
98
- tui.warn('Make sure to copy the API key value now. You will not be able to see it again.');
98
+ tui.warning(
99
+ 'Make sure to copy the API key value now. You will not be able to see it again.'
100
+ );
99
101
  tui.newline();
100
102
 
101
103
  const rows = [
@@ -168,7 +168,10 @@ export const deploySubcommand = createSubcommand({
168
168
  const hasTTY = process.stdin.isTTY && process.stdout.isTTY;
169
169
  if (project.region) {
170
170
  try {
171
- const serverProject = await projectGet(apiClient, { id: project.projectId, keys: false });
171
+ const serverProject = await projectGet(apiClient, {
172
+ id: project.projectId,
173
+ keys: false,
174
+ });
172
175
  const serverRegion = serverProject.cloudRegion;
173
176
 
174
177
  if (serverRegion && serverRegion !== project.region) {
@@ -14,24 +14,30 @@ import { resolveOrgId, isOrgScope } from './org-util';
14
14
 
15
15
  const EnvDeleteResponseSchema = z.object({
16
16
  success: z.boolean().describe('Whether the operation succeeded'),
17
- key: z.string().describe('Variable key that was deleted'),
17
+ keys: z.array(z.string()).describe('Variable keys that were deleted'),
18
18
  path: z
19
19
  .string()
20
20
  .optional()
21
- .describe('Local file path where variable was removed (project scope only)'),
22
- secret: z.boolean().describe('Whether a secret was deleted'),
23
- scope: z.enum(['project', 'org']).describe('The scope from which the variable was deleted'),
21
+ .describe('Local file path where variables were removed (project scope only)'),
22
+ secrets: z.array(z.string()).describe('Keys that were secrets'),
23
+ env: z.array(z.string()).describe('Keys that were environment variables'),
24
+ scope: z.enum(['project', 'org']).describe('The scope from which the variables were deleted'),
25
+ notFound: z.array(z.string()).optional().describe('Keys that were not found'),
24
26
  });
25
27
 
26
28
  export const deleteSubcommand = createSubcommand({
27
29
  name: 'delete',
28
30
  aliases: ['del', 'remove', 'rm'],
29
- description: 'Delete an environment variable or secret',
31
+ description: 'Delete one or more environment variables or secrets',
30
32
  tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
31
33
  idempotent: true,
32
34
  examples: [
33
- { command: getCommand('env delete OLD_FEATURE_FLAG'), description: 'Delete variable' },
35
+ { command: getCommand('env delete OLD_FEATURE_FLAG'), description: 'Delete a variable' },
34
36
  { command: getCommand('env rm API_KEY'), description: 'Delete a secret' },
37
+ {
38
+ command: getCommand('env rm KEY1 KEY2 KEY3'),
39
+ description: 'Delete multiple variables at once',
40
+ },
35
41
  {
36
42
  command: getCommand('env rm OPENAI_API_KEY --org'),
37
43
  description: 'Delete org-level secret',
@@ -41,7 +47,7 @@ export const deleteSubcommand = createSubcommand({
41
47
  optional: { project: true },
42
48
  schema: {
43
49
  args: z.object({
44
- key: z.string().describe('the variable or secret key to delete'),
50
+ key: z.array(z.string()).describe('the variable or secret key(s) to delete'),
45
51
  }),
46
52
  options: z.object({
47
53
  org: z
@@ -57,6 +63,7 @@ export const deleteSubcommand = createSubcommand({
57
63
  async handler(ctx) {
58
64
  const { args, project, projectDir, apiClient, config, opts } = ctx;
59
65
  const useOrgScope = isOrgScope(opts?.org);
66
+ const keys = args.key;
60
67
 
61
68
  // Require project context if not using org scope
62
69
  if (!useOrgScope && !project) {
@@ -65,10 +72,11 @@ export const deleteSubcommand = createSubcommand({
65
72
  );
66
73
  }
67
74
 
68
- // Validate key doesn't start with reserved AGENTUITY_ prefix (except AGENTUITY_PUBLIC_)
69
- if (isReservedAgentuityKey(args.key)) {
75
+ // Validate no keys start with reserved AGENTUITY_ prefix (except AGENTUITY_PUBLIC_)
76
+ const reservedKeys = keys.filter(isReservedAgentuityKey);
77
+ if (reservedKeys.length > 0) {
70
78
  tui.fatal(
71
- 'Cannot delete AGENTUITY_ prefixed variables. These are reserved for system use.'
79
+ `Cannot delete AGENTUITY_ prefixed variables: ${reservedKeys.join(', ')}. These are reserved for system use.`
72
80
  );
73
81
  }
74
82
 
@@ -76,85 +84,132 @@ export const deleteSubcommand = createSubcommand({
76
84
  // Organization scope
77
85
  const orgId = await resolveOrgId(apiClient, config, opts!.org!);
78
86
 
79
- // First, determine if this key exists in env or secrets
80
- const orgData = await tui.spinner('Checking organization variable', () => {
87
+ // First, determine which keys exist in env or secrets
88
+ const orgData = await tui.spinner('Checking organization variables', () => {
81
89
  return orgEnvGet(apiClient, { id: orgId, mask: true });
82
90
  });
83
91
 
84
- const isSecret = orgData.secrets?.[args.key] !== undefined;
85
- const isEnv = orgData.env?.[args.key] !== undefined;
92
+ const secretKeys: string[] = [];
93
+ const envKeys: string[] = [];
94
+ const notFoundKeys: string[] = [];
95
+
96
+ for (const key of keys) {
97
+ if (orgData.secrets?.[key] !== undefined) {
98
+ secretKeys.push(key);
99
+ } else if (orgData.env?.[key] !== undefined) {
100
+ envKeys.push(key);
101
+ } else {
102
+ notFoundKeys.push(key);
103
+ }
104
+ }
86
105
 
87
- if (!isSecret && !isEnv) {
106
+ // If all keys are not found, fail
107
+ if (secretKeys.length === 0 && envKeys.length === 0) {
88
108
  tui.fatal(
89
- `Variable '${args.key}' not found in organization`,
109
+ `No variables found in organization: ${keys.join(', ')}`,
90
110
  ErrorCode.RESOURCE_NOT_FOUND
91
111
  );
92
112
  }
93
113
 
94
- // Delete from cloud
95
- const label = isSecret ? 'secret' : 'environment variable';
96
- await tui.spinner(`Deleting organization ${label} from cloud`, () => {
114
+ // Delete from cloud (batch operation)
115
+ const totalToDelete = secretKeys.length + envKeys.length;
116
+ const label = totalToDelete === 1 ? 'variable' : 'variables';
117
+ await tui.spinner(`Deleting ${totalToDelete} organization ${label} from cloud`, () => {
97
118
  return orgEnvDelete(apiClient, {
98
119
  id: orgId,
99
- ...(isSecret ? { secrets: [args.key] } : { env: [args.key] }),
120
+ ...(secretKeys.length > 0 ? { secrets: secretKeys } : {}),
121
+ ...(envKeys.length > 0 ? { env: envKeys } : {}),
100
122
  });
101
123
  });
102
124
 
125
+ const deletedKeys = [...secretKeys, ...envKeys];
126
+ if (notFoundKeys.length > 0) {
127
+ tui.warning(`Variables not found (skipped): ${notFoundKeys.join(', ')}`);
128
+ }
103
129
  tui.success(
104
- `Organization ${isSecret ? 'secret' : 'environment variable'} '${args.key}' deleted successfully`
130
+ `Deleted ${deletedKeys.length} organization variable(s): ${deletedKeys.join(', ')}`
105
131
  );
106
132
 
107
133
  return {
108
134
  success: true,
109
- key: args.key,
110
- secret: isSecret,
135
+ keys: deletedKeys,
136
+ secrets: secretKeys,
137
+ env: envKeys,
111
138
  scope: 'org' as const,
139
+ ...(notFoundKeys.length > 0 ? { notFound: notFoundKeys } : {}),
112
140
  };
113
141
  } else {
114
- // Project scope (existing behavior)
115
- const projectData = await tui.spinner('Checking variable', () => {
142
+ // Project scope
143
+ const projectData = await tui.spinner('Checking variables', () => {
116
144
  return projectGet(apiClient, { id: project!.projectId, mask: true });
117
145
  });
118
146
 
119
- const isSecret = projectData.secrets?.[args.key] !== undefined;
120
- const isEnv = projectData.env?.[args.key] !== undefined;
147
+ const secretKeys: string[] = [];
148
+ const envKeys: string[] = [];
149
+ const notFoundKeys: string[] = [];
150
+
151
+ for (const key of keys) {
152
+ if (projectData.secrets?.[key] !== undefined) {
153
+ secretKeys.push(key);
154
+ } else if (projectData.env?.[key] !== undefined) {
155
+ envKeys.push(key);
156
+ } else {
157
+ notFoundKeys.push(key);
158
+ }
159
+ }
121
160
 
122
- if (!isSecret && !isEnv) {
123
- tui.fatal(`Variable '${args.key}' not found`, ErrorCode.RESOURCE_NOT_FOUND);
161
+ // If all keys are not found, fail
162
+ if (secretKeys.length === 0 && envKeys.length === 0) {
163
+ tui.fatal(`No variables found: ${keys.join(', ')}`, ErrorCode.RESOURCE_NOT_FOUND);
124
164
  }
125
165
 
126
- // Delete from cloud using the correct field
127
- const label = isSecret ? 'secret' : 'environment variable';
128
- await tui.spinner(`Deleting ${label} from cloud`, () => {
166
+ // Delete from cloud (batch operation)
167
+ const totalToDelete = secretKeys.length + envKeys.length;
168
+ const label = totalToDelete === 1 ? 'variable' : 'variables';
169
+ await tui.spinner(`Deleting ${totalToDelete} ${label} from cloud`, () => {
129
170
  return projectEnvDelete(apiClient, {
130
171
  id: project!.projectId,
131
- ...(isSecret ? { secrets: [args.key] } : { env: [args.key] }),
172
+ ...(secretKeys.length > 0 ? { secrets: secretKeys } : {}),
173
+ ...(envKeys.length > 0 ? { env: envKeys } : {}),
132
174
  });
133
175
  });
134
176
 
135
- // Update local .env file only if we have a project directory
136
- // (not when using --project-id without being in a project folder)
177
+ // Update local .env file only if we have a project directory and an existing .env file
137
178
  let envFilePath: string | undefined;
138
179
  if (projectDir) {
139
180
  envFilePath = await findExistingEnvFile(projectDir);
140
- const currentEnv = await readEnvFile(envFilePath);
141
- delete currentEnv[args.key];
181
+ if (envFilePath) {
182
+ const currentEnv = await readEnvFile(envFilePath);
183
+ const originalKeyCount = Object.keys(currentEnv).length;
184
+ for (const key of [...secretKeys, ...envKeys]) {
185
+ delete currentEnv[key];
186
+ }
187
+ // Only write if we actually removed keys (avoid creating empty file)
188
+ const keysRemoved = originalKeyCount > Object.keys(currentEnv).length;
189
+ if (keysRemoved) {
190
+ await writeEnvFile(envFilePath, currentEnv, { preserveExisting: false });
191
+ }
192
+ }
193
+ }
142
194
 
143
- // Write the updated env, preserveExisting: false since we already have the full state
144
- await writeEnvFile(envFilePath, currentEnv, { preserveExisting: false });
195
+ const deletedKeys = [...secretKeys, ...envKeys];
196
+ if (notFoundKeys.length > 0) {
197
+ tui.warning(`Variables not found (skipped): ${notFoundKeys.join(', ')}`);
145
198
  }
146
199
 
147
- const successMsg = envFilePath
148
- ? `${isSecret ? 'Secret' : 'Environment variable'} '${args.key}' deleted successfully (cloud + ${envFilePath})`
149
- : `${isSecret ? 'Secret' : 'Environment variable'} '${args.key}' deleted successfully (cloud only)`;
150
- tui.success(successMsg);
200
+ const locationMsg = envFilePath ? ` (cloud + ${envFilePath})` : ' (cloud only)';
201
+ tui.success(
202
+ `Deleted ${deletedKeys.length} variable(s): ${deletedKeys.join(', ')}${locationMsg}`
203
+ );
151
204
 
152
205
  return {
153
206
  success: true,
154
- key: args.key,
207
+ keys: deletedKeys,
155
208
  path: envFilePath,
156
- secret: isSecret,
209
+ secrets: secretKeys,
210
+ env: envKeys,
157
211
  scope: 'project' as const,
212
+ ...(notFoundKeys.length > 0 ? { notFound: notFoundKeys } : {}),
158
213
  };
159
214
  }
160
215
  },
@@ -2,12 +2,7 @@ import { z } from 'zod';
2
2
  import { createSubcommand } from '../../../types';
3
3
  import * as tui from '../../../tui';
4
4
  import { projectGet, orgEnvGet } from '@agentuity/server';
5
- import {
6
- findExistingEnvFile,
7
- readEnvFile,
8
- writeEnvFile,
9
- mergeEnvVars,
10
- } from '../../../env-util';
5
+ import { findExistingEnvFile, readEnvFile, writeEnvFile, mergeEnvVars } from '../../../env-util';
11
6
  import { getCommand } from '../../../command-prefix';
12
7
  import { resolveOrgId, isOrgScope } from './org-util';
13
8
 
@@ -49,7 +49,7 @@ export const deleteSubcommand = createSubcommand({
49
49
  if (!opts.confirm) {
50
50
  if (process.stdin.isTTY) {
51
51
  tui.newline();
52
- tui.warn(`You are about to delete machine ${tui.bold(machine.id)}`);
52
+ tui.warning(`You are about to delete machine ${tui.bold(machine.id)}`);
53
53
  if (machine.orgName) {
54
54
  tui.info(`Organization: ${machine.orgName}`);
55
55
  }
@@ -18,7 +18,6 @@ import { typecheck } from '../build/typecheck';
18
18
  import { validateGravityRequiresUpgrade } from '../../runtime';
19
19
  import { isTTY, hasLoggedInBefore } from '../../auth';
20
20
  import { createFileWatcher } from './file-watcher';
21
- import { regenerateSkillsAsync } from './skills';
22
21
  import { prepareDevLock, releaseLockSync } from './dev-lock';
23
22
  import { checkAndUpgradeDependencies } from '../../utils/dependency-checker';
24
23
  import { ErrorCode } from '../../errors';
@@ -81,6 +80,7 @@ async function killLingeringGravityProcesses(logger: {
81
80
  /**
82
81
  * Stop the existing Bun server if one is running.
83
82
  * Waits for the port to become available before returning (with timeout).
83
+ * Handles both in-process server and subprocess (when debugger is enabled).
84
84
  */
85
85
  async function stopBunServer(
86
86
  port: number,
@@ -88,6 +88,48 @@ async function stopBunServer(
88
88
  ): Promise<void> {
89
89
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
90
  const globalAny = globalThis as any;
91
+
92
+ // Check for subprocess first (used when debugger flags are enabled)
93
+ const bunSubprocess = globalAny.__AGENTUITY_BUN_SUBPROCESS__ as ProcessLike | undefined;
94
+ if (bunSubprocess) {
95
+ logger.debug('Stopping Bun subprocess...');
96
+ try {
97
+ bunSubprocess.kill('SIGTERM');
98
+ // After SIGTERM, wait and check multiple times before giving up
99
+ let attempts = 0;
100
+ while (bunSubprocess.exitCode === null && attempts < 3) {
101
+ await new Promise((resolve) => setTimeout(resolve, 100));
102
+ attempts++;
103
+ }
104
+ if (bunSubprocess.exitCode === null) {
105
+ bunSubprocess.kill('SIGKILL');
106
+ }
107
+ logger.debug('Bun subprocess killed');
108
+ } catch (err) {
109
+ logger.debug('Error killing Bun subprocess: %s', err);
110
+ }
111
+ globalAny.__AGENTUITY_BUN_SUBPROCESS__ = undefined;
112
+
113
+ // Wait for port to become available
114
+ const MAX_WAIT_ITERATIONS = 10;
115
+ for (let i = 0; i < MAX_WAIT_ITERATIONS; i++) {
116
+ try {
117
+ await fetch(`http://127.0.0.1:${port}/`, {
118
+ method: 'HEAD',
119
+ signal: AbortSignal.timeout(150),
120
+ });
121
+ // Still responding, wait a bit more
122
+ await new Promise((r) => setTimeout(r, 50));
123
+ } catch {
124
+ // Connection refused or timeout => server is down
125
+ logger.debug('Bun subprocess stopped');
126
+ break;
127
+ }
128
+ }
129
+ return;
130
+ }
131
+
132
+ // Handle in-process server
91
133
  const server = globalAny.__AGENTUITY_SERVER__ as BunServer | undefined;
92
134
  if (!server) {
93
135
  logger.debug('No Bun server to stop');
@@ -171,6 +213,15 @@ export const command = createCommand({
171
213
  .max(MAX_PORT)
172
214
  .default(getDefaultPort())
173
215
  .describe('The TCP port to start the dev server (also reads from PORT env)'),
216
+ inspect: z.boolean().optional().describe('Enable bun debugger on available port'),
217
+ inspectWait: z
218
+ .boolean()
219
+ .optional()
220
+ .describe('Enable bun debugger and wait for connection before executing'),
221
+ inspectBrk: z
222
+ .boolean()
223
+ .optional()
224
+ .describe('Enable bun debugger with breakpoint at first line'),
174
225
  }),
175
226
  },
176
227
  optional: { project: true },
@@ -489,12 +540,6 @@ export const command = createCommand({
489
540
  centerTitle: false,
490
541
  });
491
542
 
492
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
493
- const cliVersion = ((global as any).__CLI_SCHEMA__?.version as string) ?? '';
494
- if (cliVersion) {
495
- regenerateSkillsAsync(rootDir, cliVersion, logger).catch(() => {});
496
- }
497
-
498
543
  // Start Vite asset server ONCE before restart loop
499
544
  // Vite handles frontend HMR independently and stays running across backend restarts
500
545
  let viteServer: ServerLike | null = null;
@@ -961,7 +1006,7 @@ export const command = createCommand({
961
1006
  }
962
1007
  } catch (error) {
963
1008
  tui.error(`Failed to build dev bundle: ${error}`);
964
- tui.warn('Waiting for file changes to retry...');
1009
+ tui.warning('Waiting for file changes to retry...');
965
1010
 
966
1011
  // Resume watcher to detect changes for retry
967
1012
  fileWatcher.resume();
@@ -986,7 +1031,7 @@ export const command = createCommand({
986
1031
  if (sdkKey) {
987
1032
  process.env.AGENTUITY_SDK_KEY = sdkKey;
988
1033
  } else if (project) {
989
- tui.warn(
1034
+ tui.warning(
990
1035
  'AGENTUITY_SDK_KEY not found in .env file. Numerous features will be unavailable.'
991
1036
  );
992
1037
  tui.bullet(
@@ -1035,6 +1080,9 @@ export const command = createCommand({
1035
1080
  deploymentId,
1036
1081
  logger,
1037
1082
  vitePort, // Pass port of already-running Vite server
1083
+ inspect: opts.inspect,
1084
+ inspectWait: opts.inspectWait,
1085
+ inspectBrk: opts.inspectBrk,
1038
1086
  });
1039
1087
 
1040
1088
  // Wait for app.ts to finish loading (Vite is ready but app may still be initializing)
@@ -1047,7 +1095,7 @@ export const command = createCommand({
1047
1095
  }
1048
1096
  } catch (error) {
1049
1097
  tui.error(`Failed to start dev server: ${error}`);
1050
- tui.warn('Waiting for file changes to retry...');
1098
+ tui.warning('Waiting for file changes to retry...');
1051
1099
 
1052
1100
  // Wait for next restart trigger or shutdown
1053
1101
  await new Promise<void>((resolve) => {
@@ -1274,7 +1322,7 @@ export const command = createCommand({
1274
1322
  await Bun.sleep(500);
1275
1323
  } catch (error) {
1276
1324
  tui.error(`Error during server operation: ${error}`);
1277
- tui.warn('Waiting for file changes to retry...');
1325
+ tui.warning('Waiting for file changes to retry...');
1278
1326
 
1279
1327
  // Cleanup on error (Vite stays running)
1280
1328
  await cleanupForRestart();
package/src/cmd/index.ts CHANGED
@@ -15,6 +15,7 @@ export async function discoverCommands(): Promise<CommandDefinition[]> {
15
15
  import('./project').then((m) => m.command),
16
16
  import('./repl').then((m) => m.command),
17
17
  import('./setup').then((m) => m.command),
18
+ import('./support').then((m) => m.command),
18
19
  import('./upgrade').then((m) => m.command),
19
20
  import('./version').then((m) => m.command),
20
21
  ]);