@agentuity/cli 0.0.43 → 0.0.44

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 (209) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/api.d.ts +3 -3
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/auth.d.ts +10 -2
  6. package/dist/auth.d.ts.map +1 -1
  7. package/dist/banner.d.ts.map +1 -1
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cmd/auth/api.d.ts +4 -4
  10. package/dist/cmd/auth/api.d.ts.map +1 -1
  11. package/dist/cmd/auth/index.d.ts.map +1 -1
  12. package/dist/cmd/auth/login.d.ts.map +1 -1
  13. package/dist/cmd/auth/signup.d.ts.map +1 -1
  14. package/dist/cmd/auth/ssh/add.d.ts +2 -0
  15. package/dist/cmd/auth/ssh/add.d.ts.map +1 -0
  16. package/dist/cmd/auth/ssh/api.d.ts +16 -0
  17. package/dist/cmd/auth/ssh/api.d.ts.map +1 -0
  18. package/dist/cmd/auth/ssh/delete.d.ts +2 -0
  19. package/dist/cmd/auth/ssh/delete.d.ts.map +1 -0
  20. package/dist/cmd/auth/ssh/index.d.ts +3 -0
  21. package/dist/cmd/auth/ssh/index.d.ts.map +1 -0
  22. package/dist/cmd/auth/ssh/list.d.ts +2 -0
  23. package/dist/cmd/auth/ssh/list.d.ts.map +1 -0
  24. package/dist/cmd/auth/whoami.d.ts.map +1 -1
  25. package/dist/cmd/bundle/ast.d.ts +14 -3
  26. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  27. package/dist/cmd/bundle/ast.test.d.ts +2 -0
  28. package/dist/cmd/bundle/ast.test.d.ts.map +1 -0
  29. package/dist/cmd/bundle/bundler.d.ts +6 -1
  30. package/dist/cmd/bundle/bundler.d.ts.map +1 -1
  31. package/dist/cmd/bundle/file.d.ts.map +1 -1
  32. package/dist/cmd/bundle/fix-duplicate-exports.d.ts +2 -0
  33. package/dist/cmd/bundle/fix-duplicate-exports.d.ts.map +1 -0
  34. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts +2 -0
  35. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts.map +1 -0
  36. package/dist/cmd/bundle/plugin.d.ts +2 -0
  37. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  38. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  39. package/dist/cmd/cloud/domain.d.ts +17 -0
  40. package/dist/cmd/cloud/domain.d.ts.map +1 -0
  41. package/dist/cmd/cloud/index.d.ts.map +1 -1
  42. package/dist/cmd/cloud/resource/add.d.ts +2 -0
  43. package/dist/cmd/cloud/resource/add.d.ts.map +1 -0
  44. package/dist/cmd/cloud/resource/delete.d.ts +2 -0
  45. package/dist/cmd/cloud/resource/delete.d.ts.map +1 -0
  46. package/dist/cmd/cloud/resource/index.d.ts +3 -0
  47. package/dist/cmd/cloud/resource/index.d.ts.map +1 -0
  48. package/dist/cmd/cloud/resource/list.d.ts +2 -0
  49. package/dist/cmd/cloud/resource/list.d.ts.map +1 -0
  50. package/dist/cmd/cloud/scp/download.d.ts +2 -0
  51. package/dist/cmd/cloud/scp/download.d.ts.map +1 -0
  52. package/dist/cmd/cloud/scp/index.d.ts +3 -0
  53. package/dist/cmd/cloud/scp/index.d.ts.map +1 -0
  54. package/dist/cmd/cloud/scp/upload.d.ts +2 -0
  55. package/dist/cmd/cloud/scp/upload.d.ts.map +1 -0
  56. package/dist/cmd/cloud/ssh.d.ts +2 -0
  57. package/dist/cmd/cloud/ssh.d.ts.map +1 -0
  58. package/dist/cmd/dev/api.d.ts +18 -0
  59. package/dist/cmd/dev/api.d.ts.map +1 -0
  60. package/dist/cmd/dev/download.d.ts +11 -0
  61. package/dist/cmd/dev/download.d.ts.map +1 -0
  62. package/dist/cmd/dev/index.d.ts.map +1 -1
  63. package/dist/cmd/dev/templates.d.ts +3 -0
  64. package/dist/cmd/dev/templates.d.ts.map +1 -0
  65. package/dist/cmd/env/delete.d.ts.map +1 -1
  66. package/dist/cmd/env/get.d.ts.map +1 -1
  67. package/dist/cmd/env/import.d.ts.map +1 -1
  68. package/dist/cmd/env/list.d.ts.map +1 -1
  69. package/dist/cmd/env/pull.d.ts.map +1 -1
  70. package/dist/cmd/env/push.d.ts.map +1 -1
  71. package/dist/cmd/env/set.d.ts.map +1 -1
  72. package/dist/cmd/profile/show.d.ts.map +1 -1
  73. package/dist/cmd/project/create.d.ts.map +1 -1
  74. package/dist/cmd/project/delete.d.ts.map +1 -1
  75. package/dist/cmd/project/list.d.ts.map +1 -1
  76. package/dist/cmd/project/show.d.ts.map +1 -1
  77. package/dist/cmd/project/template-flow.d.ts +4 -0
  78. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  79. package/dist/cmd/secret/delete.d.ts.map +1 -1
  80. package/dist/cmd/secret/get.d.ts.map +1 -1
  81. package/dist/cmd/secret/import.d.ts.map +1 -1
  82. package/dist/cmd/secret/list.d.ts.map +1 -1
  83. package/dist/cmd/secret/pull.d.ts.map +1 -1
  84. package/dist/cmd/secret/push.d.ts.map +1 -1
  85. package/dist/cmd/secret/set.d.ts.map +1 -1
  86. package/dist/config.d.ts +9 -3
  87. package/dist/config.d.ts.map +1 -1
  88. package/dist/crypto/box.d.ts +65 -0
  89. package/dist/crypto/box.d.ts.map +1 -0
  90. package/dist/crypto/box.test.d.ts +2 -0
  91. package/dist/crypto/box.test.d.ts.map +1 -0
  92. package/dist/download.d.ts.map +1 -1
  93. package/dist/steps.d.ts +4 -1
  94. package/dist/steps.d.ts.map +1 -1
  95. package/dist/terminal.d.ts.map +1 -1
  96. package/dist/tui.d.ts +31 -1
  97. package/dist/tui.d.ts.map +1 -1
  98. package/dist/types.d.ts +249 -126
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/utils/detectSubagent.d.ts +15 -0
  101. package/dist/utils/detectSubagent.d.ts.map +1 -0
  102. package/dist/utils/zip.d.ts +7 -0
  103. package/dist/utils/zip.d.ts.map +1 -0
  104. package/package.json +11 -3
  105. package/src/api-errors.md +2 -2
  106. package/src/api.ts +12 -7
  107. package/src/auth.ts +116 -7
  108. package/src/banner.ts +13 -6
  109. package/src/cli.ts +695 -63
  110. package/src/cmd/auth/api.ts +10 -16
  111. package/src/cmd/auth/index.ts +2 -1
  112. package/src/cmd/auth/login.ts +24 -8
  113. package/src/cmd/auth/signup.ts +15 -11
  114. package/src/cmd/auth/ssh/add.ts +263 -0
  115. package/src/cmd/auth/ssh/api.ts +94 -0
  116. package/src/cmd/auth/ssh/delete.ts +102 -0
  117. package/src/cmd/auth/ssh/index.ts +10 -0
  118. package/src/cmd/auth/ssh/list.ts +74 -0
  119. package/src/cmd/auth/whoami.ts +13 -13
  120. package/src/cmd/bundle/ast.test.ts +565 -0
  121. package/src/cmd/bundle/ast.ts +457 -44
  122. package/src/cmd/bundle/bundler.ts +255 -57
  123. package/src/cmd/bundle/file.ts +6 -12
  124. package/src/cmd/bundle/fix-duplicate-exports.test.ts +387 -0
  125. package/src/cmd/bundle/fix-duplicate-exports.ts +204 -0
  126. package/src/cmd/bundle/index.ts +9 -9
  127. package/src/cmd/bundle/patch/aisdk.ts +1 -1
  128. package/src/cmd/bundle/plugin.ts +373 -53
  129. package/src/cmd/cloud/deploy.ts +300 -93
  130. package/src/cmd/cloud/domain.ts +92 -0
  131. package/src/cmd/cloud/index.ts +4 -1
  132. package/src/cmd/cloud/resource/add.ts +56 -0
  133. package/src/cmd/cloud/resource/delete.ts +120 -0
  134. package/src/cmd/cloud/resource/index.ts +11 -0
  135. package/src/cmd/cloud/resource/list.ts +69 -0
  136. package/src/cmd/cloud/scp/download.ts +59 -0
  137. package/src/cmd/cloud/scp/index.ts +9 -0
  138. package/src/cmd/cloud/scp/upload.ts +62 -0
  139. package/src/cmd/cloud/ssh.ts +68 -0
  140. package/src/cmd/dev/api.ts +46 -0
  141. package/src/cmd/dev/download.ts +111 -0
  142. package/src/cmd/dev/index.ts +360 -34
  143. package/src/cmd/dev/templates.ts +84 -0
  144. package/src/cmd/env/delete.ts +5 -20
  145. package/src/cmd/env/get.ts +5 -18
  146. package/src/cmd/env/import.ts +5 -20
  147. package/src/cmd/env/list.ts +5 -18
  148. package/src/cmd/env/pull.ts +10 -23
  149. package/src/cmd/env/push.ts +5 -23
  150. package/src/cmd/env/set.ts +5 -20
  151. package/src/cmd/index.ts +2 -2
  152. package/src/cmd/profile/show.ts +15 -6
  153. package/src/cmd/project/create.ts +7 -2
  154. package/src/cmd/project/delete.ts +75 -18
  155. package/src/cmd/project/download.ts +2 -2
  156. package/src/cmd/project/list.ts +8 -8
  157. package/src/cmd/project/show.ts +3 -7
  158. package/src/cmd/project/template-flow.ts +170 -72
  159. package/src/cmd/secret/delete.ts +5 -20
  160. package/src/cmd/secret/get.ts +5 -18
  161. package/src/cmd/secret/import.ts +5 -20
  162. package/src/cmd/secret/list.ts +5 -18
  163. package/src/cmd/secret/pull.ts +10 -23
  164. package/src/cmd/secret/push.ts +5 -23
  165. package/src/cmd/secret/set.ts +5 -20
  166. package/src/config.ts +224 -24
  167. package/src/crypto/box.test.ts +431 -0
  168. package/src/crypto/box.ts +477 -0
  169. package/src/download.ts +1 -0
  170. package/src/env-util.test.ts +1 -1
  171. package/src/steps.ts +65 -6
  172. package/src/terminal.ts +24 -23
  173. package/src/tui.ts +192 -61
  174. package/src/types.ts +291 -201
  175. package/src/utils/detectSubagent.ts +31 -0
  176. package/src/utils/zip.ts +38 -0
  177. package/dist/cmd/example/create-user.d.ts +0 -2
  178. package/dist/cmd/example/create-user.d.ts.map +0 -1
  179. package/dist/cmd/example/create.d.ts +0 -2
  180. package/dist/cmd/example/create.d.ts.map +0 -1
  181. package/dist/cmd/example/deploy.d.ts +0 -2
  182. package/dist/cmd/example/deploy.d.ts.map +0 -1
  183. package/dist/cmd/example/index.d.ts +0 -2
  184. package/dist/cmd/example/index.d.ts.map +0 -1
  185. package/dist/cmd/example/list.d.ts +0 -2
  186. package/dist/cmd/example/list.d.ts.map +0 -1
  187. package/dist/cmd/example/optional-auth.d.ts +0 -3
  188. package/dist/cmd/example/optional-auth.d.ts.map +0 -1
  189. package/dist/cmd/example/run-command.d.ts +0 -2
  190. package/dist/cmd/example/run-command.d.ts.map +0 -1
  191. package/dist/cmd/example/sound.d.ts +0 -3
  192. package/dist/cmd/example/sound.d.ts.map +0 -1
  193. package/dist/cmd/example/spinner.d.ts +0 -2
  194. package/dist/cmd/example/spinner.d.ts.map +0 -1
  195. package/dist/cmd/example/steps.d.ts +0 -2
  196. package/dist/cmd/example/steps.d.ts.map +0 -1
  197. package/dist/cmd/example/version.d.ts +0 -2
  198. package/dist/cmd/example/version.d.ts.map +0 -1
  199. package/src/cmd/example/create-user.ts +0 -38
  200. package/src/cmd/example/create.ts +0 -31
  201. package/src/cmd/example/deploy.ts +0 -36
  202. package/src/cmd/example/index.ts +0 -29
  203. package/src/cmd/example/list.ts +0 -32
  204. package/src/cmd/example/optional-auth.ts +0 -38
  205. package/src/cmd/example/run-command.ts +0 -45
  206. package/src/cmd/example/sound.ts +0 -14
  207. package/src/cmd/example/spinner.ts +0 -44
  208. package/src/cmd/example/steps.ts +0 -66
  209. package/src/cmd/example/version.ts +0 -13
package/src/tui.ts CHANGED
@@ -4,10 +4,12 @@
4
4
  * Provides semantic helpers for console output with automatic icons and colors.
5
5
  * Uses Bun's built-in color support and ANSI escape codes.
6
6
  */
7
+ import { stringWidth } from 'bun';
7
8
  import enquirer from 'enquirer';
8
- import type { OrganizationList } from '@agentuity/server';
9
+ import { type OrganizationList, projectList } from '@agentuity/server';
9
10
  import type { ColorScheme } from './terminal';
10
- import { stringWidth } from 'bun';
11
+ import { type APIClient as APIClientType } from './api';
12
+ import * as readline from 'readline';
11
13
 
12
14
  // Icons
13
15
  const ICONS = {
@@ -19,8 +21,13 @@ const ICONS = {
19
21
  bullet: '•',
20
22
  } as const;
21
23
 
22
- function shouldUseColors(): boolean {
23
- return !process.env.NO_COLOR && process.env.TERM !== 'dumb' && !!process.stdout.isTTY;
24
+ export function shouldUseColors(): boolean {
25
+ return (
26
+ !process.env.NO_COLOR &&
27
+ !process.env.CI &&
28
+ process.env.TERM !== 'dumb' &&
29
+ !!process.stdout.isTTY
30
+ );
24
31
  }
25
32
 
26
33
  // Color definitions (light/dark adaptive) using Bun.color
@@ -76,6 +83,11 @@ let currentColorScheme: ColorScheme = process.env.CI ? 'light' : 'dark';
76
83
 
77
84
  export function setColorScheme(scheme: ColorScheme): void {
78
85
  currentColorScheme = scheme;
86
+ process.env.COLOR_SCHEME = scheme;
87
+ }
88
+
89
+ export function isDarkMode(): boolean {
90
+ return currentColorScheme === 'dark';
79
91
  }
80
92
 
81
93
  function getColor(colorKey: keyof ReturnType<typeof getColors>): string {
@@ -142,6 +154,15 @@ export function muted(text: string): string {
142
154
  return `${color}${text}${reset}`;
143
155
  }
144
156
 
157
+ /**
158
+ * Format text in warn color
159
+ */
160
+ export function warn(text: string): string {
161
+ const color = getColor('warning');
162
+ const reset = getColor('reset');
163
+ return `${color}${text}${reset}`;
164
+ }
165
+
145
166
  /**
146
167
  * Format text in bold
147
168
  */
@@ -158,8 +179,8 @@ export function link(url: string, title?: string): string {
158
179
  const color = getColor('link');
159
180
  const reset = getColor('reset');
160
181
 
161
- // Check if terminal supports hyperlinks (OSC 8)
162
- if (supportsHyperlinks()) {
182
+ // Check if terminal supports hyperlinks (OSC 8) and colors are enabled
183
+ if (shouldUseColors() && supportsHyperlinks()) {
163
184
  return `\x1b]8;;${url}\x07${color}${title ?? url}${reset}\x1b]8;;\x07`;
164
185
  }
165
186
 
@@ -208,24 +229,40 @@ export function newline(): void {
208
229
  console.log('');
209
230
  }
210
231
 
232
+ /**
233
+ * Get the display width of a string, handling ANSI codes and OSC 8 hyperlinks
234
+ *
235
+ * Note: Bun.stringWidth() counts OSC 8 hyperlink escape sequences in the width,
236
+ * which causes incorrect alignment. We strip OSC 8 codes first, then use Bun.stringWidth()
237
+ * to handle regular ANSI codes and unicode characters correctly.
238
+ */
239
+ function getDisplayWidth(str: string): number {
240
+ // Remove OSC-8 hyperlink sequences using Unicode escapes (\u001b = ESC, \u0007 = BEL) to satisfy linter
241
+ // eslint-disable-next-line no-control-regex
242
+ const withoutOSC8 = str.replace(/\u001b\]8;;[^\u0007]*\u0007/g, '');
243
+ return Bun.stringWidth(withoutOSC8);
244
+ }
245
+
211
246
  /**
212
247
  * Pad a string to a specific length on the right
213
248
  */
214
249
  export function padRight(str: string, length: number, pad = ' '): string {
215
- if (str.length >= length) {
250
+ const displayWidth = getDisplayWidth(str);
251
+ if (displayWidth >= length) {
216
252
  return str;
217
253
  }
218
- return str + pad.repeat(length - str.length);
254
+ return str + pad.repeat(length - displayWidth);
219
255
  }
220
256
 
221
257
  /**
222
258
  * Pad a string to a specific length on the left
223
259
  */
224
260
  export function padLeft(str: string, length: number, pad = ' '): string {
225
- if (str.length >= length) {
261
+ const displayWidth = getDisplayWidth(str);
262
+ if (displayWidth >= length) {
226
263
  return str;
227
264
  }
228
- return pad.repeat(length - str.length) + str;
265
+ return pad.repeat(length - displayWidth) + str;
229
266
  }
230
267
 
231
268
  interface BannerOptions {
@@ -245,11 +282,8 @@ interface BannerOptions {
245
282
  * Responsive to terminal width - adapts to narrow terminals
246
283
  */
247
284
  export function banner(title: string, body: string, options?: BannerOptions): void {
248
- // Get terminal width, default to 80 if not available, minimum 40
249
- const termWidth = process.stdout.columns || 80;
250
- const minWidth = options?.minWidth ?? 40;
251
- const maxWidth = Math.max(minWidth, Math.min(termWidth - 2, 80)); // Between 40 and 80, with 2 char margin
252
- const padding = options?.padding ?? 4;
285
+ // Get terminal width, default to 120 if not available
286
+ const termWidth = process.stdout.columns || 120;
253
287
 
254
288
  const border = {
255
289
  topLeft: '╭',
@@ -260,15 +294,27 @@ export function banner(title: string, body: string, options?: BannerOptions): vo
260
294
  vertical: '│',
261
295
  };
262
296
 
263
- // Split body into lines and wrap if needed
264
- const bodyLines = wrapText(body, maxWidth - padding); // -4 for padding and borders
265
-
266
- // Calculate width based on content
297
+ // Calculate content width first (before wrapping)
267
298
  const titleWidth = getDisplayWidth(title);
268
- const maxBodyWidth = Math.max(...bodyLines.map((line) => getDisplayWidth(line)));
269
- const contentWidth = Math.max(minWidth, Math.max(titleWidth, maxBodyWidth) + padding);
270
- const boxWidth = Math.min(contentWidth, maxWidth); // +N for padding
271
- const innerWidth = boxWidth - padding;
299
+ const bodyLines = body.split('\n');
300
+ const maxBodyWidth = Math.max(0, ...bodyLines.map((line) => getDisplayWidth(line)));
301
+ const requiredContentWidth = Math.max(titleWidth, maxBodyWidth);
302
+
303
+ // Box width = content + borders (2) + side spaces (2)
304
+ const boxWidth = Math.min(requiredContentWidth + 4, termWidth);
305
+
306
+ // If required content width exceeds terminal width, skip box and print plain text
307
+ if (requiredContentWidth + 4 > termWidth) {
308
+ console.log('\n' + bold(title));
309
+ console.log(body + '\n');
310
+ return;
311
+ }
312
+
313
+ // Inner width is box width minus borders (2) and side spaces (2)
314
+ const innerWidth = boxWidth - 4;
315
+
316
+ // Wrap text to fit box width
317
+ const wrappedBodyLines = wrapText(body, innerWidth);
272
318
 
273
319
  // Colors
274
320
  const borderColor = getColor('muted');
@@ -294,10 +340,7 @@ export function banner(title: string, body: string, options?: BannerOptions): vo
294
340
  const titleDisplayWidth = getDisplayWidth(title);
295
341
  if (options?.centerTitle === true || options?.centerTitle === undefined) {
296
342
  const titlePadding = Math.max(0, Math.floor((innerWidth - titleDisplayWidth) / 2));
297
- const titleRightPadding = Math.max(
298
- 0,
299
- Math.max(0, innerWidth - titlePadding - titleDisplayWidth) - padding
300
- );
343
+ const titleRightPadding = Math.max(0, innerWidth - titlePadding - titleDisplayWidth);
301
344
  const titleLine =
302
345
  ' '.repeat(titlePadding) +
303
346
  `${titleColor}${bold(title)}${reset}` +
@@ -306,7 +349,7 @@ export function banner(title: string, body: string, options?: BannerOptions): vo
306
349
  `${borderColor}${border.vertical} ${reset}${titleLine}${borderColor} ${border.vertical}${reset}`
307
350
  );
308
351
  } else {
309
- const titleRightPadding = Math.max(0, Math.max(0, innerWidth - titleDisplayWidth) - padding);
352
+ const titleRightPadding = Math.max(0, innerWidth - titleDisplayWidth);
310
353
  const titleLine = `${titleColor}${bold(title)}${reset}` + ' '.repeat(titleRightPadding);
311
354
  lines.push(
312
355
  `${borderColor}${border.vertical} ${reset}${titleLine}${borderColor} ${border.vertical}${reset}`
@@ -321,9 +364,9 @@ export function banner(title: string, body: string, options?: BannerOptions): vo
321
364
  }
322
365
 
323
366
  // Body lines
324
- for (const line of bodyLines) {
367
+ for (const line of wrappedBodyLines) {
325
368
  const lineWidth = getDisplayWidth(line);
326
- const linePadding = Math.max(0, Math.max(0, innerWidth - lineWidth) - padding);
369
+ const linePadding = Math.max(0, innerWidth - lineWidth);
327
370
  lines.push(
328
371
  `${borderColor}${border.vertical} ${reset}${line}${' '.repeat(linePadding)}${borderColor} ${border.vertical}${reset}`
329
372
  );
@@ -465,7 +508,7 @@ export function showSignupBenefits(): void {
465
508
  ];
466
509
 
467
510
  console.log('');
468
- lines.forEach((line) => console.log(CYAN + line + RESET));
511
+ lines.map((line) => console.log(CYAN + line + RESET));
469
512
  console.log('');
470
513
  }
471
514
 
@@ -498,7 +541,7 @@ export function showLoggedOutMessage(): void {
498
541
  ];
499
542
 
500
543
  console.log('');
501
- lines.filter(Boolean).forEach((line) => console.log(YELLOW + line + RESET));
544
+ lines.filter(Boolean).map((line) => console.log(YELLOW + line + RESET));
502
545
  }
503
546
 
504
547
  /**
@@ -553,20 +596,6 @@ export async function copyToClipboard(text: string): Promise<boolean> {
553
596
  }
554
597
  }
555
598
 
556
- /**
557
- * Get the display width of a string, handling ANSI codes and OSC 8 hyperlinks
558
- *
559
- * Note: Bun.stringWidth() counts OSC 8 hyperlink escape sequences in the width,
560
- * which causes incorrect alignment. We strip OSC 8 codes first, then use Bun.stringWidth()
561
- * to handle regular ANSI codes and unicode characters correctly.
562
- */
563
- function getDisplayWidth(str: string): number {
564
- // Strip OSC 8 hyperlink sequences: \x1b]8;;URL\x07...\x1b]8;;\x07
565
- // eslint-disable-next-line no-control-regex
566
- const withoutOSC8 = str.replace(/\x1b\]8;;[^\x07]*\x07/g, '');
567
- return Bun.stringWidth(withoutOSC8);
568
- }
569
-
570
599
  /**
571
600
  * Extract ANSI codes from the beginning of a string
572
601
  */
@@ -676,6 +705,11 @@ export interface SimpleSpinnerOptions<T> {
676
705
  type?: 'simple';
677
706
  message: string;
678
707
  callback: (() => Promise<T>) | Promise<T>;
708
+ /**
709
+ * If true, clear the spinner output on success (no icon, no message)
710
+ * Defaults to false
711
+ */
712
+ clearOnSuccess?: boolean;
679
713
  }
680
714
 
681
715
  /**
@@ -685,6 +719,11 @@ export interface ProgressSpinnerOptions<T> {
685
719
  type: 'progress';
686
720
  message: string;
687
721
  callback: (progress: SpinnerProgressCallback) => Promise<T>;
722
+ /**
723
+ * If true, clear the spinner output on success (no icon, no message)
724
+ * Defaults to false
725
+ */
726
+ clearOnSuccess?: boolean;
688
727
  }
689
728
 
690
729
  /**
@@ -735,7 +774,7 @@ export async function spinner<T>(
735
774
  const reset = getColor('reset');
736
775
 
737
776
  // If no TTY, just execute the callback without animation
738
- if (!process.stdout.isTTY) {
777
+ if (!process.stderr.isTTY) {
739
778
  try {
740
779
  const result =
741
780
  options.type === 'progress'
@@ -744,6 +783,12 @@ export async function spinner<T>(
744
783
  ? await options.callback()
745
784
  : await options.callback;
746
785
 
786
+ // If clearOnSuccess is true, don't show success message
787
+ if (!options.clearOnSuccess) {
788
+ const successColor = getColor('success');
789
+ console.error(`${successColor}${ICONS.success} ${message}${reset}`);
790
+ }
791
+
747
792
  return result;
748
793
  } catch (err) {
749
794
  const errorColor = getColor('error');
@@ -766,7 +811,7 @@ export async function spinner<T>(
766
811
  let currentProgress: number | undefined;
767
812
 
768
813
  // Hide cursor
769
- process.stdout.write('\x1B[?25l');
814
+ process.stderr.write('\x1B[?25l');
770
815
 
771
816
  // Start animation
772
817
  const interval = setInterval(() => {
@@ -781,7 +826,7 @@ export async function spinner<T>(
781
826
  : '';
782
827
 
783
828
  // Clear line and render
784
- process.stdout.write('\r\x1B[K' + `${frame} ${message}${progressIndicator}`);
829
+ process.stderr.write('\r\x1B[K' + `${frame} ${message}${progressIndicator}`);
785
830
  frameIndex++;
786
831
  }, 120);
787
832
 
@@ -801,27 +846,30 @@ export async function spinner<T>(
801
846
 
802
847
  // Clear interval and line
803
848
  clearInterval(interval);
804
- process.stdout.write('\r\x1B[K');
849
+ process.stderr.write('\r\x1B[K');
805
850
 
806
- // Show success
807
- const successColor = getColor('success');
808
- console.log(`${successColor}${ICONS.success} ${message}${reset}`);
851
+ // If clearOnSuccess is false, show success message
852
+ if (!options.clearOnSuccess) {
853
+ // Show success
854
+ const successColor = getColor('success');
855
+ console.error(`${successColor}${ICONS.success} ${message}${reset}`);
856
+ }
809
857
 
810
858
  // Show cursor
811
- process.stdout.write('\x1B[?25h');
859
+ process.stderr.write('\x1B[?25h');
812
860
 
813
861
  return result;
814
862
  } catch (err) {
815
863
  // Clear interval and line
816
864
  clearInterval(interval);
817
- process.stdout.write('\r\x1B[K');
865
+ process.stderr.write('\r\x1B[K');
818
866
 
819
867
  // Show error
820
868
  const errorColor = getColor('error');
821
869
  console.error(`${errorColor}${ICONS.error} ${message}${reset}`);
822
870
 
823
871
  // Show cursor
824
- process.stdout.write('\x1B[?25h');
872
+ process.stderr.write('\x1B[?25h');
825
873
 
826
874
  throw err;
827
875
  }
@@ -856,12 +904,10 @@ export interface CommandRunnerOptions {
856
904
  * If true or undefined, will truncate each line of output
857
905
  */
858
906
  truncate?: boolean;
859
-
860
907
  /**
861
908
  * If undefined, will show up to 3 last lines of output while running. Customize the number with this property.
862
909
  */
863
910
  maxLinesOutput?: number;
864
-
865
911
  /**
866
912
  * If undefined, will show up to 10 last lines on failure. Customize the number with this property.
867
913
  */
@@ -1085,6 +1131,33 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
1085
1131
  }
1086
1132
  }
1087
1133
 
1134
+ /**
1135
+ * Prompt user for text input
1136
+ * Returns the input string
1137
+ */
1138
+ export async function prompt(message: string): Promise<string> {
1139
+ process.stdout.write(message);
1140
+
1141
+ // Check if we're in a TTY environment
1142
+ if (!process.stdin.isTTY) {
1143
+ console.log('');
1144
+ return '';
1145
+ }
1146
+
1147
+ // Use readline for full line input
1148
+ const rl = readline.createInterface({
1149
+ input: process.stdin,
1150
+ output: process.stdout,
1151
+ });
1152
+
1153
+ return new Promise((resolve) => {
1154
+ rl.question('', (answer: string) => {
1155
+ rl.close();
1156
+ resolve(answer);
1157
+ });
1158
+ });
1159
+ }
1160
+
1088
1161
  export async function selectOrganization(
1089
1162
  orgs: OrganizationList,
1090
1163
  initial?: string
@@ -1096,17 +1169,75 @@ export async function selectOrganization(
1096
1169
  );
1097
1170
  }
1098
1171
 
1099
- if (orgs.length === 1) {
1100
- return orgs[0].id;
1172
+ if (process.env.AGENTUITY_CLOUD_ORG_ID) {
1173
+ const org = orgs.find((o) => o.id === process.env.AGENTUITY_CLOUD_ORG_ID);
1174
+ if (org) {
1175
+ return org.id;
1176
+ }
1177
+ }
1178
+
1179
+ if (!process.stdin.isTTY) {
1180
+ if (orgs.length === 1) {
1181
+ return orgs[0].id;
1182
+ }
1183
+ if (initial) {
1184
+ return initial;
1185
+ }
1186
+ fatal(
1187
+ 'Organization selection required but cannot prompt in non-interactive environment. Set AGENTUITY_CLOUD_ORG_ID or provide a default organization using --org-id'
1188
+ );
1101
1189
  }
1102
1190
 
1103
1191
  const response = await enquirer.prompt<{ action: string }>({
1104
1192
  type: 'select',
1105
1193
  name: 'action',
1106
1194
  message: 'Select an organization',
1107
- initial,
1195
+ initial: initial || (orgs.length === 1 ? orgs[0].id : undefined),
1108
1196
  choices: orgs.map((o) => ({ message: o.name, name: o.id })),
1109
1197
  });
1110
1198
 
1111
1199
  return response.action;
1112
1200
  }
1201
+
1202
+ /**
1203
+ * show a project list picker
1204
+ *
1205
+ * @param apiClient
1206
+ * @param showDeployment
1207
+ * @returns
1208
+ */
1209
+ export async function showProjectList(
1210
+ apiClient: APIClientType,
1211
+ showDeploymentId = false
1212
+ ): Promise<string> {
1213
+ const projects = await spinner({
1214
+ message: 'Fetching projects',
1215
+ clearOnSuccess: true,
1216
+ callback: () => {
1217
+ return projectList(apiClient, showDeploymentId);
1218
+ },
1219
+ });
1220
+
1221
+ if (projects.length === 0) {
1222
+ return '';
1223
+ }
1224
+
1225
+ // TODO: might want to sort by the last org_id we used
1226
+ if (projects) {
1227
+ projects.sort((a, b) => {
1228
+ return a.name.localeCompare(b.name);
1229
+ });
1230
+ }
1231
+
1232
+ const response = await enquirer.prompt<{ id: string }>({
1233
+ type: 'select',
1234
+ name: 'id',
1235
+ message: 'Select a project:',
1236
+ choices: projects.map((p) => ({
1237
+ name: p.id,
1238
+ message: `${p.name.padEnd(25, ' ')} ${muted(p.id)} ${showDeploymentId ? muted(p.latestDeploymentId ?? 'no deployment') : ''}`,
1239
+ })),
1240
+ });
1241
+
1242
+ return response.id;
1243
+ }