@agentuity/cli 0.0.6

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 (158) hide show
  1. package/AGENTS.md +139 -0
  2. package/README.md +239 -0
  3. package/bin/cli.ts +71 -0
  4. package/dist/api.d.ts +25 -0
  5. package/dist/api.d.ts.map +1 -0
  6. package/dist/auth.d.ts +7 -0
  7. package/dist/auth.d.ts.map +1 -0
  8. package/dist/banner.d.ts +2 -0
  9. package/dist/banner.d.ts.map +1 -0
  10. package/dist/cli.d.ts +5 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cmd/auth/api.d.ts +9 -0
  13. package/dist/cmd/auth/api.d.ts.map +1 -0
  14. package/dist/cmd/auth/index.d.ts +2 -0
  15. package/dist/cmd/auth/index.d.ts.map +1 -0
  16. package/dist/cmd/auth/login.d.ts +3 -0
  17. package/dist/cmd/auth/login.d.ts.map +1 -0
  18. package/dist/cmd/auth/logout.d.ts +3 -0
  19. package/dist/cmd/auth/logout.d.ts.map +1 -0
  20. package/dist/cmd/bundle/ast.d.ts +2 -0
  21. package/dist/cmd/bundle/ast.d.ts.map +1 -0
  22. package/dist/cmd/bundle/bundler.d.ts +6 -0
  23. package/dist/cmd/bundle/bundler.d.ts.map +1 -0
  24. package/dist/cmd/bundle/file.d.ts +2 -0
  25. package/dist/cmd/bundle/file.d.ts.map +1 -0
  26. package/dist/cmd/bundle/index.d.ts +2 -0
  27. package/dist/cmd/bundle/index.d.ts.map +1 -0
  28. package/dist/cmd/bundle/plugin.d.ts +4 -0
  29. package/dist/cmd/bundle/plugin.d.ts.map +1 -0
  30. package/dist/cmd/dev/index.d.ts +2 -0
  31. package/dist/cmd/dev/index.d.ts.map +1 -0
  32. package/dist/cmd/example/create-user.d.ts +2 -0
  33. package/dist/cmd/example/create-user.d.ts.map +1 -0
  34. package/dist/cmd/example/create.d.ts +2 -0
  35. package/dist/cmd/example/create.d.ts.map +1 -0
  36. package/dist/cmd/example/deploy.d.ts +2 -0
  37. package/dist/cmd/example/deploy.d.ts.map +1 -0
  38. package/dist/cmd/example/index.d.ts +2 -0
  39. package/dist/cmd/example/index.d.ts.map +1 -0
  40. package/dist/cmd/example/list.d.ts +2 -0
  41. package/dist/cmd/example/list.d.ts.map +1 -0
  42. package/dist/cmd/example/run-command.d.ts +2 -0
  43. package/dist/cmd/example/run-command.d.ts.map +1 -0
  44. package/dist/cmd/example/sound.d.ts +3 -0
  45. package/dist/cmd/example/sound.d.ts.map +1 -0
  46. package/dist/cmd/example/spinner.d.ts +2 -0
  47. package/dist/cmd/example/spinner.d.ts.map +1 -0
  48. package/dist/cmd/example/steps.d.ts +2 -0
  49. package/dist/cmd/example/steps.d.ts.map +1 -0
  50. package/dist/cmd/example/version.d.ts +2 -0
  51. package/dist/cmd/example/version.d.ts.map +1 -0
  52. package/dist/cmd/index.d.ts +3 -0
  53. package/dist/cmd/index.d.ts.map +1 -0
  54. package/dist/cmd/profile/create.d.ts +2 -0
  55. package/dist/cmd/profile/create.d.ts.map +1 -0
  56. package/dist/cmd/profile/delete.d.ts +2 -0
  57. package/dist/cmd/profile/delete.d.ts.map +1 -0
  58. package/dist/cmd/profile/index.d.ts +2 -0
  59. package/dist/cmd/profile/index.d.ts.map +1 -0
  60. package/dist/cmd/profile/list.d.ts +3 -0
  61. package/dist/cmd/profile/list.d.ts.map +1 -0
  62. package/dist/cmd/profile/show.d.ts +2 -0
  63. package/dist/cmd/profile/show.d.ts.map +1 -0
  64. package/dist/cmd/profile/use.d.ts +2 -0
  65. package/dist/cmd/profile/use.d.ts.map +1 -0
  66. package/dist/cmd/project/create.d.ts +2 -0
  67. package/dist/cmd/project/create.d.ts.map +1 -0
  68. package/dist/cmd/project/delete.d.ts +2 -0
  69. package/dist/cmd/project/delete.d.ts.map +1 -0
  70. package/dist/cmd/project/index.d.ts +2 -0
  71. package/dist/cmd/project/index.d.ts.map +1 -0
  72. package/dist/cmd/project/list.d.ts +2 -0
  73. package/dist/cmd/project/list.d.ts.map +1 -0
  74. package/dist/cmd/project/show.d.ts +2 -0
  75. package/dist/cmd/project/show.d.ts.map +1 -0
  76. package/dist/cmd/version/index.d.ts +2 -0
  77. package/dist/cmd/version/index.d.ts.map +1 -0
  78. package/dist/command-prefix.d.ts +11 -0
  79. package/dist/command-prefix.d.ts.map +1 -0
  80. package/dist/config.d.ts +16 -0
  81. package/dist/config.d.ts.map +1 -0
  82. package/dist/index.d.ts +18 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/legacy-check.d.ts +6 -0
  85. package/dist/legacy-check.d.ts.map +1 -0
  86. package/dist/logger.d.ts +24 -0
  87. package/dist/logger.d.ts.map +1 -0
  88. package/dist/runtime.d.ts +3 -0
  89. package/dist/runtime.d.ts.map +1 -0
  90. package/dist/schema-parser.d.ts +24 -0
  91. package/dist/schema-parser.d.ts.map +1 -0
  92. package/dist/sound.d.ts +2 -0
  93. package/dist/sound.d.ts.map +1 -0
  94. package/dist/steps.d.ts +59 -0
  95. package/dist/steps.d.ts.map +1 -0
  96. package/dist/terminal.d.ts +3 -0
  97. package/dist/terminal.d.ts.map +1 -0
  98. package/dist/tui.d.ts +156 -0
  99. package/dist/tui.d.ts.map +1 -0
  100. package/dist/types.d.ts +164 -0
  101. package/dist/types.d.ts.map +1 -0
  102. package/dist/version.d.ts +10 -0
  103. package/dist/version.d.ts.map +1 -0
  104. package/package.json +46 -0
  105. package/src/api-errors.md +115 -0
  106. package/src/api.ts +186 -0
  107. package/src/auth.ts +91 -0
  108. package/src/banner.ts +23 -0
  109. package/src/cli.ts +198 -0
  110. package/src/cmd/auth/README.md +95 -0
  111. package/src/cmd/auth/api.ts +71 -0
  112. package/src/cmd/auth/index.ts +9 -0
  113. package/src/cmd/auth/login.ts +76 -0
  114. package/src/cmd/auth/logout.ts +14 -0
  115. package/src/cmd/bundle/ast.ts +228 -0
  116. package/src/cmd/bundle/bundler.ts +88 -0
  117. package/src/cmd/bundle/file.ts +16 -0
  118. package/src/cmd/bundle/index.ts +38 -0
  119. package/src/cmd/bundle/plugin.ts +259 -0
  120. package/src/cmd/dev/index.ts +83 -0
  121. package/src/cmd/example/create-user.ts +38 -0
  122. package/src/cmd/example/create.ts +31 -0
  123. package/src/cmd/example/deploy.ts +36 -0
  124. package/src/cmd/example/index.ts +27 -0
  125. package/src/cmd/example/list.ts +32 -0
  126. package/src/cmd/example/run-command.ts +45 -0
  127. package/src/cmd/example/sound.ts +14 -0
  128. package/src/cmd/example/spinner.ts +44 -0
  129. package/src/cmd/example/steps.ts +66 -0
  130. package/src/cmd/example/version.ts +13 -0
  131. package/src/cmd/index.ts +46 -0
  132. package/src/cmd/profile/README.md +80 -0
  133. package/src/cmd/profile/create.ts +57 -0
  134. package/src/cmd/profile/delete.ts +52 -0
  135. package/src/cmd/profile/index.ts +12 -0
  136. package/src/cmd/profile/list.ts +27 -0
  137. package/src/cmd/profile/show.ts +54 -0
  138. package/src/cmd/profile/use.ts +30 -0
  139. package/src/cmd/project/create.ts +247 -0
  140. package/src/cmd/project/delete.ts +13 -0
  141. package/src/cmd/project/index.ts +11 -0
  142. package/src/cmd/project/list.ts +13 -0
  143. package/src/cmd/project/show.ts +12 -0
  144. package/src/cmd/version/index.ts +16 -0
  145. package/src/command-prefix.ts +43 -0
  146. package/src/config.ts +304 -0
  147. package/src/index.ts +40 -0
  148. package/src/legacy-check.ts +127 -0
  149. package/src/logger.ts +235 -0
  150. package/src/runtime.ts +22 -0
  151. package/src/schema-parser.ts +213 -0
  152. package/src/sound.ts +25 -0
  153. package/src/steps.ts +245 -0
  154. package/src/terminal.ts +151 -0
  155. package/src/tui.md +254 -0
  156. package/src/tui.ts +838 -0
  157. package/src/types.ts +243 -0
  158. package/src/version.ts +29 -0
package/src/steps.ts ADDED
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Step progress UI component for showing animated steps with callbacks
3
+ *
4
+ * Displays a checklist where each step animates in place with a spinner,
5
+ * then shows success, skipped, or error icon based on callback result.
6
+ */
7
+
8
+ import type { ColorScheme } from './terminal';
9
+
10
+ // Spinner frames
11
+ const FRAMES = ['◐', '◓', '◑', '◒'];
12
+
13
+ // Icons
14
+ const ICONS = {
15
+ success: '✓',
16
+ skipped: '○',
17
+ error: '✗',
18
+ pending: '☐',
19
+ } as const;
20
+
21
+ // Color definitions (light/dark adaptive)
22
+ const COLORS = {
23
+ cyan: { light: '\x1b[36m', dark: '\x1b[96m' },
24
+ blue: { light: '\x1b[34m', dark: '\x1b[94m' },
25
+ magenta: { light: '\x1b[35m', dark: '\x1b[95m' },
26
+ green: { light: '\x1b[32m', dark: '\x1b[92m' },
27
+ yellow: { light: '\x1b[33m', dark: '\x1b[93m' },
28
+ red: { light: '\x1b[31m', dark: '\x1b[91m' },
29
+ gray: { light: '\x1b[90m', dark: '\x1b[37m' },
30
+ bold: '\x1b[1m',
31
+ strikethrough: '\x1b[9m',
32
+ reset: '\x1b[0m',
33
+ } as const;
34
+
35
+ // Spinner color sequence
36
+ const SPINNER_COLORS = ['cyan', 'blue', 'magenta', 'cyan'] as const;
37
+
38
+ let currentColorScheme: ColorScheme = 'dark';
39
+
40
+ export function setStepsColorScheme(scheme: ColorScheme): void {
41
+ currentColorScheme = scheme;
42
+ }
43
+
44
+ function getColor(colorKey: keyof typeof COLORS): string {
45
+ const color = COLORS[colorKey];
46
+ if (typeof color === 'string') {
47
+ return color;
48
+ }
49
+ return color[currentColorScheme];
50
+ }
51
+
52
+ /**
53
+ * Step outcome
54
+ */
55
+ export type StepOutcome =
56
+ | { status: 'success' }
57
+ | { status: 'skipped'; reason?: string }
58
+ | { status: 'error'; message: string };
59
+
60
+ /**
61
+ * Helper functions for creating step outcomes
62
+ */
63
+ export const stepSuccess = (): StepOutcome => ({ status: 'success' });
64
+ export const stepSkipped = (reason?: string): StepOutcome => ({ status: 'skipped', reason });
65
+ export const stepError = (message: string): StepOutcome => ({ status: 'error', message });
66
+
67
+ /**
68
+ * Progress callback function
69
+ */
70
+ export type ProgressCallback = (progress: number) => void;
71
+
72
+ /**
73
+ * Step definition (without progress tracking)
74
+ */
75
+ export interface SimpleStep {
76
+ type?: 'simple';
77
+ label: string;
78
+ run: () => Promise<StepOutcome>;
79
+ }
80
+
81
+ /**
82
+ * Step definition (with progress tracking)
83
+ */
84
+ export interface ProgressStep {
85
+ type: 'progress';
86
+ label: string;
87
+ run: (progress: ProgressCallback) => Promise<StepOutcome>;
88
+ }
89
+
90
+ /**
91
+ * Step definition (discriminated union)
92
+ */
93
+ export type Step = SimpleStep | ProgressStep;
94
+
95
+ /**
96
+ * Internal step state
97
+ */
98
+ type StepState =
99
+ | {
100
+ type: 'simple';
101
+ label: string;
102
+ run: () => Promise<StepOutcome>;
103
+ outcome?: StepOutcome;
104
+ progress?: number;
105
+ }
106
+ | {
107
+ type: 'progress';
108
+ label: string;
109
+ run: (progress: ProgressCallback) => Promise<StepOutcome>;
110
+ outcome?: StepOutcome;
111
+ progress?: number;
112
+ };
113
+
114
+ /**
115
+ * Run a series of steps with animated progress
116
+ *
117
+ * Each step runs its callback while showing a spinner animation.
118
+ * Steps can complete with success, skipped, or error status.
119
+ * Exits with code 1 if any step errors.
120
+ */
121
+ export async function runSteps(steps: Step[]): Promise<void> {
122
+ const state: StepState[] = steps.map((s) => {
123
+ const stepType = s.type === 'progress' ? 'progress' : 'simple';
124
+ return stepType === 'progress'
125
+ ? {
126
+ type: 'progress' as const,
127
+ label: s.label,
128
+ run: s.run as (progress: ProgressCallback) => Promise<StepOutcome>,
129
+ }
130
+ : { type: 'simple' as const, label: s.label, run: s.run as () => Promise<StepOutcome> };
131
+ });
132
+
133
+ // Hide cursor
134
+ process.stdout.write('\x1B[?25l');
135
+
136
+ try {
137
+ // Initial render
138
+ process.stdout.write(renderSteps(state, -1) + '\n');
139
+
140
+ for (let stepIndex = 0; stepIndex < state.length; stepIndex++) {
141
+ const step = state[stepIndex];
142
+ let frameIndex = 0;
143
+
144
+ // Start spinner animation
145
+ const interval = setInterval(() => {
146
+ const colorKey = SPINNER_COLORS[frameIndex % SPINNER_COLORS.length];
147
+ const color = getColor(colorKey);
148
+ const frame = `${color}${COLORS.bold}${FRAMES[frameIndex % FRAMES.length]}${COLORS.reset}`;
149
+
150
+ // Move cursor up to the top of checklist
151
+ process.stdout.write(`\x1B[${state.length}A`);
152
+ process.stdout.write(renderSteps(state, stepIndex, frame) + '\n');
153
+
154
+ frameIndex++;
155
+ }, 120);
156
+
157
+ // Run the step with progress tracking
158
+ const progressCallback: ProgressCallback = (progress: number) => {
159
+ step.progress = Math.min(100, Math.max(0, progress));
160
+
161
+ // Move cursor up
162
+ process.stdout.write(`\x1B[${state.length}A`);
163
+ process.stdout.write(renderSteps(state, stepIndex) + '\n');
164
+ };
165
+
166
+ try {
167
+ // Use discriminant to determine if step has progress callback
168
+ const outcome =
169
+ step.type === 'progress' ? await step.run(progressCallback) : await step.run();
170
+ step.outcome = outcome;
171
+ } catch (err) {
172
+ step.outcome = {
173
+ status: 'error',
174
+ message: err instanceof Error ? err.message : String(err),
175
+ };
176
+ }
177
+
178
+ clearInterval(interval);
179
+
180
+ // Clear progress and final render with outcome
181
+ step.progress = undefined;
182
+ process.stdout.write(`\x1B[${state.length}A`);
183
+ process.stdout.write(renderSteps(state, stepIndex) + '\n');
184
+
185
+ // If error, show error message and exit
186
+ if (step.outcome?.status === 'error') {
187
+ const errorColor = getColor('red');
188
+ console.error(`\n${errorColor}Error: ${step.outcome.message}${COLORS.reset}\n`);
189
+ process.stdout.write('\x1B[?25h'); // Show cursor
190
+ process.exit(1);
191
+ }
192
+ }
193
+
194
+ // Show cursor again
195
+ process.stdout.write('\x1B[?25h\n');
196
+ } catch (err) {
197
+ // Ensure cursor is shown even if something goes wrong
198
+ process.stdout.write('\x1B[?25h');
199
+ throw err;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Render a progress indicator
205
+ */
206
+ function renderProgress(progress: number): string {
207
+ const cyanColor = getColor('cyan');
208
+
209
+ const percentage = `${Math.floor(progress)}%`;
210
+ return ` ${cyanColor}${percentage}${COLORS.reset}`;
211
+ }
212
+
213
+ /**
214
+ * Render all steps as a multiline string
215
+ */
216
+ function renderSteps(steps: StepState[], activeIndex: number, spinner?: string): string {
217
+ const grayColor = getColor('gray');
218
+ const greenColor = getColor('green');
219
+ const yellowColor = getColor('yellow');
220
+ const redColor = getColor('red');
221
+
222
+ const lines: string[] = [];
223
+
224
+ steps.forEach((s, i) => {
225
+ if (s.outcome?.status === 'success') {
226
+ lines.push(
227
+ `${greenColor}${ICONS.success}${COLORS.reset} ${grayColor}${COLORS.strikethrough}${s.label}${COLORS.reset}`
228
+ );
229
+ } else if (s.outcome?.status === 'skipped') {
230
+ const reason = s.outcome.reason ? ` ${grayColor}(${s.outcome.reason})${COLORS.reset}` : '';
231
+ lines.push(
232
+ `${yellowColor}${ICONS.skipped}${COLORS.reset} ${grayColor}${COLORS.strikethrough}${s.label}${COLORS.reset}${reason}`
233
+ );
234
+ } else if (s.outcome?.status === 'error') {
235
+ lines.push(`${redColor}${ICONS.error}${COLORS.reset} ${s.label}`);
236
+ } else if (i === activeIndex && spinner) {
237
+ const progressIndicator = s.progress !== undefined ? renderProgress(s.progress) : '';
238
+ lines.push(`${spinner} ${s.label}${progressIndicator}`);
239
+ } else {
240
+ lines.push(`${grayColor}${ICONS.pending}${COLORS.reset} ${s.label}`);
241
+ }
242
+ });
243
+
244
+ return lines.join('\n');
245
+ }
@@ -0,0 +1,151 @@
1
+ export type ColorScheme = 'light' | 'dark';
2
+
3
+ export async function detectColorScheme(): Promise<ColorScheme> {
4
+ const debug = process.env.DEBUG_COLORS === 'true';
5
+
6
+ // Check for explicit override
7
+ if (process.env.COLOR_SCHEME === 'light') {
8
+ if (debug) console.log('[DEBUG] Using COLOR_SCHEME=light override');
9
+ return 'light';
10
+ }
11
+ if (process.env.COLOR_SCHEME === 'dark') {
12
+ if (debug) console.log('[DEBUG] Using COLOR_SCHEME=dark override');
13
+ return 'dark';
14
+ }
15
+
16
+ // Check if we have stdout TTY at minimum
17
+ if (!process.stdout.isTTY) {
18
+ if (debug) console.log('[DEBUG] stdout not a TTY, defaulting to dark');
19
+ return 'dark'; // Default to dark mode
20
+ }
21
+
22
+ // Try to query terminal background color using OSC 11 (most reliable)
23
+ if (debug) console.log('[DEBUG] Querying terminal background with OSC 11...');
24
+ try {
25
+ const bgColor = await queryTerminalBackground();
26
+ if (bgColor) {
27
+ const luminance = calculateLuminance(bgColor);
28
+ const scheme = luminance > 0.5 ? 'light' : 'dark';
29
+ if (debug)
30
+ console.log(
31
+ `[DEBUG] OSC 11 response: rgb(${bgColor.r},${bgColor.g},${bgColor.b}), luminance: ${luminance.toFixed(2)}, scheme: ${scheme}`
32
+ );
33
+ return scheme;
34
+ } else {
35
+ if (debug) console.log('[DEBUG] OSC 11 query timed out or no response');
36
+ }
37
+ } catch (error) {
38
+ if (debug) console.log('[DEBUG] OSC 11 query failed:', error);
39
+ }
40
+
41
+ // Fall back to COLORFGBG environment variable (less reliable)
42
+ if (process.env.COLORFGBG) {
43
+ // COLORFGBG format is "foreground;background"
44
+ // This is unreliable but better than nothing
45
+ const parts = process.env.COLORFGBG.split(';');
46
+ const fg = parseInt(parts[0] || '7', 10);
47
+ const bg = parseInt(parts[1] || '0', 10);
48
+
49
+ // Heuristic: if background is 0 (black) and foreground is light (>=7), it's likely dark mode
50
+ // if background is light (>=7) and foreground is dark (<7), it's likely light mode
51
+ const scheme = bg >= 7 || bg > fg ? 'light' : 'dark';
52
+ if (debug)
53
+ console.log(
54
+ `[DEBUG] COLORFGBG fallback: ${process.env.COLORFGBG} (fg:${fg}, bg:${bg}), scheme: ${scheme}`
55
+ );
56
+ return scheme;
57
+ }
58
+
59
+ if (debug) console.log('[DEBUG] Defaulting to dark mode');
60
+ return 'dark'; // Default to dark mode
61
+ }
62
+
63
+ interface RGBColor {
64
+ r: number;
65
+ g: number;
66
+ b: number;
67
+ }
68
+
69
+ async function queryTerminalBackground(): Promise<RGBColor | null> {
70
+ // Skip if stdin is not available or not a TTY
71
+ if (!process.stdin.isTTY) {
72
+ return null;
73
+ }
74
+
75
+ return new Promise((resolve) => {
76
+ const timeout = setTimeout(() => {
77
+ cleanup();
78
+ resolve(null);
79
+ }, 100); // 100ms timeout
80
+
81
+ let response = '';
82
+
83
+ const onData = (data: Buffer) => {
84
+ response += data.toString();
85
+
86
+ // Look for OSC 11 response patterns:
87
+ // Pattern 1: ESC ] 11 ; rgb:RRRR/GGGG/BBBB ESC \ (xterm with ESC \ terminator)
88
+ // Pattern 2: ESC ] 11 ; rgb:RRRR/GGGG/BBBB BEL (xterm with BEL terminator)
89
+ // The color values can be 8-bit (RR), 12-bit (RRR), or 16-bit (RRRR)
90
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: Control characters needed for ANSI escape sequences
91
+ const match = response.match(
92
+ // eslint-disable-next-line no-control-regex
93
+ /\x1b\]11;rgb:([0-9a-f]+)\/([0-9a-f]+)\/([0-9a-f]+)(?:\x1b\\|\x07)/i
94
+ );
95
+ if (match) {
96
+ cleanup();
97
+ clearTimeout(timeout);
98
+
99
+ // Parse RGB values - they can be different bit depths
100
+ // Normalize to 8-bit (0-255) by taking the most significant bits
101
+ const parseColorValue = (hex: string): number => {
102
+ if (hex.length === 4) {
103
+ // 16-bit: RRRR -> take first 2 chars
104
+ return parseInt(hex.slice(0, 2), 16);
105
+ } else if (hex.length === 3) {
106
+ // 12-bit: RRR -> take first 2 chars
107
+ return parseInt(hex.slice(0, 2), 16);
108
+ } else {
109
+ // 8-bit: RR
110
+ return parseInt(hex, 16);
111
+ }
112
+ };
113
+
114
+ const r = parseColorValue(match[1]);
115
+ const g = parseColorValue(match[2]);
116
+ const b = parseColorValue(match[3]);
117
+
118
+ resolve({ r, g, b });
119
+ }
120
+ };
121
+
122
+ const cleanup = () => {
123
+ process.stdin.setRawMode(false);
124
+ process.stdin.removeListener('data', onData);
125
+ process.stdin.pause(); // Pause stdin to allow process to exit
126
+ };
127
+
128
+ // Set stdin to raw mode to read escape sequences
129
+ process.stdin.setRawMode(true);
130
+ process.stdin.resume(); // Resume to receive data
131
+ process.stdin.on('data', onData);
132
+
133
+ // Send OSC 11 query with BEL terminator (more widely supported)
134
+ // Using BEL (\x07) as terminator instead of ESC \ for better compatibility
135
+ process.stdout.write('\x1b]11;?\x07');
136
+ });
137
+ }
138
+
139
+ function calculateLuminance(color: RGBColor): number {
140
+ // Convert RGB to relative luminance using the formula from WCAG
141
+ // https://www.w3.org/TR/WCAG20/#relativeluminancedef
142
+ const rsRGB = color.r / 255;
143
+ const gsRGB = color.g / 255;
144
+ const bsRGB = color.b / 255;
145
+
146
+ const r = rsRGB <= 0.03928 ? rsRGB / 12.92 : Math.pow((rsRGB + 0.055) / 1.055, 2.4);
147
+ const g = gsRGB <= 0.03928 ? gsRGB / 12.92 : Math.pow((gsRGB + 0.055) / 1.055, 2.4);
148
+ const b = bsRGB <= 0.03928 ? bsRGB / 12.92 : Math.pow((bsRGB + 0.055) / 1.055, 2.4);
149
+
150
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
151
+ }
package/src/tui.md ADDED
@@ -0,0 +1,254 @@
1
+ # Terminal UI Utilities
2
+
3
+ The `tui` module provides semantic helpers for formatted, colorized console output.
4
+
5
+ ## Usage
6
+
7
+ ```typescript
8
+ import * as tui from '@agentuity/cli/tui';
9
+
10
+ // Or from within the package:
11
+ import * as tui from './tui';
12
+ ```
13
+
14
+ ## Message Functions
15
+
16
+ Print semantic messages with automatic icons and colors:
17
+
18
+ ### `tui.success(message: string)`
19
+
20
+ Print a success message with a green checkmark (✓).
21
+
22
+ ```typescript
23
+ tui.success('Welcome to Agentuity! You are now logged in');
24
+ // Output: ✓ Welcome to Agentuity! You are now logged in (in green)
25
+ ```
26
+
27
+ ### `tui.error(message: string)`
28
+
29
+ Print an error message with a red X (✗).
30
+
31
+ ```typescript
32
+ tui.error('Connection failed');
33
+ // Output: ✗ Connection failed (in red)
34
+ ```
35
+
36
+ ### `tui.warning(message: string)`
37
+
38
+ Print a warning message with a yellow warning icon (⚠).
39
+
40
+ ```typescript
41
+ tui.warning('CLI Upgrade Required');
42
+ // Output: ⚠ CLI Upgrade Required (in yellow)
43
+ ```
44
+
45
+ ### `tui.info(message: string)`
46
+
47
+ Print an info message with a cyan info icon (ℹ).
48
+
49
+ ```typescript
50
+ tui.info('Processing request...');
51
+ // Output: ℹ Processing request... (in cyan)
52
+ ```
53
+
54
+ ## List Functions
55
+
56
+ ### `tui.bullet(message: string)`
57
+
58
+ Print a bulleted list item (•).
59
+
60
+ ```typescript
61
+ tui.bullet('First item');
62
+ tui.bullet('Second item');
63
+ ```
64
+
65
+ ### `tui.arrow(message: string)`
66
+
67
+ Print an arrow item (→), useful for showing next steps.
68
+
69
+ ```typescript
70
+ tui.arrow('Run: agentuity deploy');
71
+ tui.arrow('Visit: https://app.agentuity.com');
72
+ ```
73
+
74
+ ## Text Formatting
75
+
76
+ These functions return formatted strings (don't print directly):
77
+
78
+ ### `tui.bold(text: string): string`
79
+
80
+ Format text in bold.
81
+
82
+ ```typescript
83
+ console.log(`Name: ${tui.bold('Production')}`);
84
+ ```
85
+
86
+ ### `tui.muted(text: string): string`
87
+
88
+ Format text in muted/gray color.
89
+
90
+ ```typescript
91
+ console.log(`${tui.muted('(optional)')}`);
92
+ ```
93
+
94
+ ### `tui.link(url: string): string`
95
+
96
+ Format text as a clickable link (blue and underlined). If the terminal supports OSC 8 hyperlinks, creates a clickable link.
97
+
98
+ ```typescript
99
+ console.log(`Visit: ${tui.link('https://agentuity.com')}`);
100
+ ```
101
+
102
+ Supported terminals for clickable links:
103
+
104
+ - iTerm2
105
+ - WezTerm
106
+ - Ghostty
107
+ - Apple Terminal
108
+ - Hyper
109
+ - Kitty
110
+ - Windows Terminal
111
+
112
+ ## Utility Functions
113
+
114
+ ### `tui.newline()`
115
+
116
+ Print a blank line (shorthand for `console.log('')`).
117
+
118
+ ```typescript
119
+ tui.success('Done!');
120
+ tui.newline();
121
+ console.log('Next steps:');
122
+ ```
123
+
124
+ ### `tui.waitForAnyKey(message?: string): Promise<void>`
125
+
126
+ Wait for the user to press Enter before continuing. Useful for pausing execution to let the user read output or confirm an action.
127
+
128
+ ```typescript
129
+ await tui.waitForAnyKey(); // Uses default message: "Press Enter to continue..."
130
+ await tui.waitForAnyKey('Press Enter to open the browser...');
131
+ ```
132
+
133
+ **Behavior:**
134
+
135
+ - Waits for any key press (Enter, Space, etc.)
136
+ - Automatically exits with code 1 if CTRL+C is pressed
137
+ - Uses raw mode to capture single keypress
138
+
139
+ **Use cases:**
140
+
141
+ - Pausing before opening a browser
142
+ - Letting user read important information
143
+ - Confirming before proceeding with an action
144
+
145
+ ### `tui.copyToClipboard(text: string): Promise<boolean>`
146
+
147
+ Copy text to the system clipboard. Returns `true` if successful, `false` otherwise.
148
+
149
+ ```typescript
150
+ const copied = await tui.copyToClipboard('ABC123');
151
+ if (copied) {
152
+ tui.success('Code copied to clipboard!');
153
+ } else {
154
+ console.log('Copy the following code: ABC123');
155
+ }
156
+ ```
157
+
158
+ **Platform support:**
159
+
160
+ - macOS: Uses `pbcopy`
161
+ - Windows: Uses `clip`
162
+ - Linux: Tries `xclip`, falls back to `xsel`
163
+
164
+ **Note:** Silently fails if clipboard utilities are not available (returns `false`).
165
+
166
+ ### `tui.padRight(str: string, length: number, pad?: string): string`
167
+
168
+ Pad a string to a specific length on the right.
169
+
170
+ ```typescript
171
+ const name = tui.padRight('prod', 10, ' ');
172
+ console.log(`${name} ${tui.muted('(active)')}`);
173
+ ```
174
+
175
+ ### `tui.padLeft(str: string, length: number, pad?: string): string`
176
+
177
+ Pad a string to a specific length on the left.
178
+
179
+ ```typescript
180
+ const count = tui.padLeft('5', 3, '0');
181
+ console.log(count); // "005"
182
+ ```
183
+
184
+ ## Color Scheme
185
+
186
+ The TUI module automatically adapts colors for light and dark terminals. You can set the color scheme:
187
+
188
+ ```typescript
189
+ import { setColorScheme } from './tui';
190
+
191
+ setColorScheme('dark'); // or 'light'
192
+ ```
193
+
194
+ **Note:** The CLI automatically detects and sets the color scheme based on terminal settings.
195
+
196
+ ## Banner
197
+
198
+ ### `tui.banner(title: string, body: string)`
199
+
200
+ Display a formatted banner with a bordered box around the content. Perfect for important messages, welcome screens, or upgrade notices.
201
+
202
+ ```typescript
203
+ tui.banner('Welcome', 'Thank you for using Agentuity! Your setup is complete.');
204
+ ```
205
+
206
+ Output:
207
+
208
+ ```
209
+ ╭──────────────────────────────────────────────────╮
210
+ │ │
211
+ │ Welcome │
212
+ │ │
213
+ │ Thank you for using Agentuity! Your setup is │
214
+ │ complete. │
215
+ │ │
216
+ ╰──────────────────────────────────────────────────╯
217
+ ```
218
+
219
+ **Features:**
220
+
221
+ - Automatically wraps text to fit within 80 characters
222
+ - Centers the title
223
+ - Pads body text with borders
224
+ - Supports embedded formatting (bold, muted, links)
225
+ - Handles unicode characters correctly (emoji, CJK characters) using `Bun.stringWidth()`
226
+
227
+ ## Complete Example
228
+
229
+ ```typescript
230
+ import * as tui from './tui';
231
+
232
+ tui.info('Deploying application...');
233
+ tui.newline();
234
+
235
+ tui.bullet('Building assets');
236
+ tui.bullet('Uploading to server');
237
+ tui.bullet('Running migrations');
238
+
239
+ tui.newline();
240
+ tui.success('Deployment complete!');
241
+ tui.newline();
242
+
243
+ console.log('Next steps:');
244
+ tui.arrow(`Visit ${tui.link('https://app.agentuity.com')}`);
245
+ tui.arrow(`View logs: ${tui.bold('agentuity logs')}`);
246
+ ```
247
+
248
+ ## Design Philosophy
249
+
250
+ - **Semantic over presentational**: Use `tui.success()` instead of manually coloring text green
251
+ - **Consistent icons**: Each message type has a standard icon
252
+ - **Adaptive colors**: Colors adjust for light/dark terminals
253
+ - **No logger prefix**: TUI functions use raw console output, no `[INFO]` prefixes
254
+ - **Composable**: Text formatting functions return strings for flexible composition