vibecodingmachine-cli 2026.1.29-713 → 2026.2.20-423

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 (45) hide show
  1. package/bin/vibecodingmachine.js +124 -0
  2. package/package.json +3 -2
  3. package/src/commands/agents-check.js +69 -0
  4. package/src/commands/auto-direct.js +930 -145
  5. package/src/commands/auto.js +26 -4
  6. package/src/commands/ide.js +2 -1
  7. package/src/commands/requirements.js +23 -27
  8. package/src/utils/auto-mode.js +4 -1
  9. package/src/utils/cline-js-handler.js +218 -0
  10. package/src/utils/config.js +22 -0
  11. package/src/utils/display-formatters-complete.js +229 -0
  12. package/src/utils/display-formatters-extracted.js +219 -0
  13. package/src/utils/display-formatters.js +157 -0
  14. package/src/utils/feedback-handler.js +143 -0
  15. package/src/utils/ide-detection-complete.js +126 -0
  16. package/src/utils/ide-detection-extracted.js +116 -0
  17. package/src/utils/ide-detection.js +124 -0
  18. package/src/utils/interactive-backup.js +5664 -0
  19. package/src/utils/interactive-broken.js +280 -0
  20. package/src/utils/interactive.js +31 -5534
  21. package/src/utils/provider-checker.js +410 -0
  22. package/src/utils/provider-manager.js +251 -0
  23. package/src/utils/provider-registry.js +18 -9
  24. package/src/utils/requirement-actions.js +884 -0
  25. package/src/utils/requirements-navigator.js +585 -0
  26. package/src/utils/rui-trui-adapter.js +311 -0
  27. package/src/utils/simple-trui.js +204 -0
  28. package/src/utils/status-helpers-extracted.js +125 -0
  29. package/src/utils/status-helpers.js +107 -0
  30. package/src/utils/trui-debug.js +261 -0
  31. package/src/utils/trui-feedback.js +133 -0
  32. package/src/utils/trui-nav-agents.js +119 -0
  33. package/src/utils/trui-nav-requirements.js +268 -0
  34. package/src/utils/trui-nav-settings.js +157 -0
  35. package/src/utils/trui-nav-specifications.js +139 -0
  36. package/src/utils/trui-navigation.js +303 -0
  37. package/src/utils/trui-provider-manager.js +182 -0
  38. package/src/utils/trui-quick-menu.js +365 -0
  39. package/src/utils/trui-req-actions.js +372 -0
  40. package/src/utils/trui-req-tree.js +534 -0
  41. package/src/utils/trui-specifications.js +359 -0
  42. package/src/utils/trui-text-editor.js +350 -0
  43. package/src/utils/trui-windsurf.js +336 -0
  44. package/src/utils/welcome-screen-extracted.js +135 -0
  45. package/src/utils/welcome-screen.js +134 -0
@@ -0,0 +1,365 @@
1
+ /**
2
+ * TRUI Quick Menu — raw-stdin keypress navigation engine
3
+ *
4
+ * Provides showQuickMenu(items, initialIndex) which renders a menu and listens
5
+ * for keypresses directly on stdin (no inquirer dependency).
6
+ *
7
+ * Item types:
8
+ * 'setting' — gray text; gets a letter shortcut; selectable
9
+ * 'action' — white text; gets a letter shortcut; selectable
10
+ * 'info' — gray text; NO letter; NOT selectable (display only)
11
+ * 'blank' — empty separator line; not selectable
12
+ *
13
+ * Returns: Promise<{ value: string, selectedIndex: number }>
14
+ * Special value '__cancel__' is returned when ESC or left-arrow is pressed.
15
+ */
16
+
17
+ const chalk = require('chalk');
18
+ const readline = require('readline');
19
+ const { debugLogger, perfMonitor, stateTracker, errorHandler } = require('./trui-debug');
20
+
21
+ /**
22
+ * Convert a zero-based index to a letter shortcut (0→'a', 1→'b', …)
23
+ * @param {number} index
24
+ * @returns {string}
25
+ */
26
+ function indexToLetter(index) {
27
+ return String.fromCharCode(97 + index);
28
+ }
29
+
30
+ /**
31
+ * Strip ANSI escape codes from a string for length calculation
32
+ */
33
+ function stripAnsi(str) {
34
+ return str.replace(
35
+ /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
36
+ ''
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Calculate the number of terminal lines a piece of text occupies
42
+ * (accounts for line wrapping based on terminal column width)
43
+ */
44
+ function getVisualLineCount(text) {
45
+ const columns = process.stdout.columns || 80;
46
+ let lineCount = 0;
47
+ for (const line of text.split('\n')) {
48
+ const len = stripAnsi(line).length;
49
+ lineCount += len === 0 ? 1 : Math.ceil(len / columns);
50
+ }
51
+ return lineCount;
52
+ }
53
+
54
+ /**
55
+ * Determine whether an item can receive keyboard focus
56
+ */
57
+ function isSelectable(item) {
58
+ return item.type !== 'blank' && item.type !== 'info';
59
+ }
60
+
61
+ /**
62
+ * Build and display the menu; return the total number of visual lines printed
63
+ * so subsequent renders can reposition the cursor without clearing.
64
+ *
65
+ * @param {Array} items
66
+ * @param {number} selectedIndex
67
+ * @param {string} [hintText] - override the bottom hint line
68
+ * @returns {number} lines printed
69
+ */
70
+ function renderMenu(items, selectedIndex, hintText) {
71
+ let linesPrinted = 0;
72
+ let letterIndex = 0;
73
+
74
+ items.forEach((item, index) => {
75
+ const isSelected = index === selectedIndex;
76
+ let output = '';
77
+
78
+ if (item.type === 'blank') {
79
+ process.stdout.write('\n');
80
+ linesPrinted += 1;
81
+ return;
82
+ }
83
+
84
+ if (item.type === 'info') {
85
+ output = chalk.gray(` ${item.name}`);
86
+ process.stdout.write(output + '\n');
87
+ linesPrinted += getVisualLineCount(` ${item.name}`);
88
+ return;
89
+ }
90
+
91
+ // 'header' type: no letter, but IS selectable (focusable with ↑↓, Enter returns value)
92
+ if (item.type === 'header') {
93
+ const raw = `${isSelected ? '❯ ' : ' '}${item.name}`;
94
+ if (isSelected) {
95
+ output = chalk.cyan(`❯ ${item.name}`);
96
+ } else {
97
+ output = chalk.gray(` ${item.name}`);
98
+ }
99
+ process.stdout.write(output + '\n');
100
+ linesPrinted += getVisualLineCount(raw);
101
+ return;
102
+ }
103
+
104
+ // Assign letter — 'x' always = exit; everything else gets a→z in order
105
+ let letter;
106
+ if (item.value === 'exit') {
107
+ letter = 'x';
108
+ } else {
109
+ letter = indexToLetter(letterIndex);
110
+ letterIndex++;
111
+ }
112
+
113
+ const prefix = isSelected ? chalk.cyan('❯ ') : ' ';
114
+ const raw = `${isSelected ? '❯ ' : ' '}${letter}) ${item.name}`;
115
+
116
+ if (isSelected) {
117
+ output = chalk.cyan(`❯ ${letter}) ${item.name}`);
118
+ } else if (item.type === 'setting') {
119
+ output = chalk.gray(` ${letter}) ${item.name}`);
120
+ } else {
121
+ output = ` ${letter}) ${item.name}`;
122
+ }
123
+
124
+ process.stdout.write(output + '\n');
125
+ linesPrinted += getVisualLineCount(raw);
126
+ });
127
+
128
+ // Help hint at the bottom
129
+ let helpText;
130
+ if (hintText !== undefined) {
131
+ helpText = `\n ${hintText}`;
132
+ } else {
133
+ const letterCount = items.filter(
134
+ i => i.type !== 'blank' && i.type !== 'info' && i.value !== 'exit'
135
+ ).length;
136
+ const lastLetter = letterCount > 0 ? indexToLetter(letterCount - 1) : 'a';
137
+ const rangeText = letterCount > 1 ? `a-${lastLetter}` : letterCount === 1 ? 'a' : '';
138
+ helpText = `\n [${rangeText ? rangeText + ' ' : ''}↑↓ navigate ← back]`;
139
+ }
140
+ process.stdout.write(chalk.gray(helpText) + '\n');
141
+ linesPrinted += getVisualLineCount(helpText);
142
+
143
+ return linesPrinted;
144
+ }
145
+
146
+ // Debounce guard — shared between calls to prevent key-repeat re-entry
147
+ let menuSuppressUntil = 0;
148
+
149
+ /**
150
+ * Show a quick menu and wait for the user to select an item.
151
+ *
152
+ * @param {Array<{type: string, name: string, value: string}>} items
153
+ * @param {number} [initialSelectedIndex=0]
154
+ * @param {Object} [options={}]
155
+ * @param {Function} [options.extraKeys] - called for unhandled keys:
156
+ * extraKeys(str, key, selectedIndex, { display, setSelectedIndex, resolveWith, items })
157
+ * Return true to indicate the key was handled.
158
+ * @param {string} [options.hintText] - override the bottom hint line
159
+ * @returns {Promise<{value: string, selectedIndex: number}>}
160
+ */
161
+ function showQuickMenu(items, initialSelectedIndex = 0, options = {}) {
162
+ const { extraKeys, hintText } = options;
163
+ try {
164
+ perfMonitor.start('showQuickMenu');
165
+ debugLogger.info('showQuickMenu called', { itemCount: items.length, initialSelectedIndex });
166
+
167
+ stateTracker.push({
168
+ type: 'menu',
169
+ items,
170
+ initialSelectedIndex,
171
+ startTime: Date.now()
172
+ });
173
+
174
+ return new Promise(resolve => {
175
+ // Find first selectable item at or after initialSelectedIndex
176
+ let selectedIndex = initialSelectedIndex;
177
+ while (selectedIndex < items.length && !isSelectable(items[selectedIndex])) {
178
+ selectedIndex++;
179
+ }
180
+ if (selectedIndex >= items.length) {
181
+ selectedIndex = items.findIndex(isSelectable);
182
+ if (selectedIndex === -1) selectedIndex = 0;
183
+ }
184
+
185
+ debugLogger.info('Initial selectedIndex set', { selectedIndex });
186
+ stateTracker.update({ selectedIndex });
187
+
188
+ let lastLinesPrinted = 0;
189
+ let isFirstRender = true;
190
+
191
+ const display = () => {
192
+ perfMonitor.start('render');
193
+ if (!isFirstRender) {
194
+ // Move cursor back up to start of menu and clear downward
195
+ readline.moveCursor(process.stdout, 0, -lastLinesPrinted);
196
+ readline.clearScreenDown(process.stdout);
197
+ }
198
+ lastLinesPrinted = renderMenu(items, selectedIndex, hintText);
199
+ isFirstRender = false;
200
+ perfMonitor.end('render');
201
+ debugLogger.info('Menu rendered', { linesPrinted: lastLinesPrinted, selectedIndex });
202
+ };
203
+
204
+ const cleanup = () => {
205
+ debugLogger.info('Cleaning up menu listeners');
206
+ if (process.stdin.isTTY && process.stdin.setRawMode) {
207
+ process.stdin.setRawMode(false);
208
+ }
209
+ process.stdin.removeListener('keypress', onKeypress);
210
+ process.stdin.pause();
211
+ perfMonitor.end('showQuickMenu');
212
+ stateTracker.pop();
213
+ debugLogger.info('Menu cleanup completed', perfMonitor.getMetrics());
214
+ };
215
+
216
+ const selectOption = index => {
217
+ debugLogger.info('Option selected', { index, value: items[index].value, name: items[index].name });
218
+ cleanup();
219
+ // Clear the selection indicator line
220
+ readline.moveCursor(process.stdout, 0, -(lastLinesPrinted - (index)));
221
+ readline.clearScreenDown(process.stdout);
222
+ process.stdout.write(chalk.cyan(` → ${items[index].name}\n`));
223
+ menuSuppressUntil = Date.now() + 300;
224
+ resolve({ value: items[index].value, selectedIndex: index });
225
+ };
226
+
227
+ const onKeypress = (str, key) => {
228
+ stateTracker.update({ lastKeypress: { str, key } });
229
+ debugLogger.info('Keypress received', { str, key: key ? { name: key.name, ctrl: key.ctrl, shift: key.shift } : null });
230
+
231
+ if (!key) return;
232
+
233
+ // Always allow Ctrl+C
234
+ if (key.ctrl && key.name === 'c') {
235
+ cleanup();
236
+ process.exit(0);
237
+ return;
238
+ }
239
+
240
+ // Debounce
241
+ if (Date.now() < menuSuppressUntil) return;
242
+
243
+ // ESC → always cancel immediately
244
+ if (key.name === 'escape') {
245
+ cleanup();
246
+ menuSuppressUntil = Date.now() + 300;
247
+ resolve({ value: '__cancel__', selectedIndex });
248
+ return;
249
+ }
250
+
251
+ // Give caller first crack at keys (before letter shortcuts and left-arrow cancel)
252
+ if (extraKeys) {
253
+ const handled = extraKeys(str, key, selectedIndex, {
254
+ display,
255
+ setSelectedIndex: (i) => { selectedIndex = i; stateTracker.update({ selectedIndex }); },
256
+ resolveWith: (value) => { cleanup(); resolve({ value, selectedIndex }); },
257
+ items,
258
+ });
259
+ if (handled) return;
260
+ }
261
+
262
+ // Left-arrow → cancel (if extraKeys did not handle it)
263
+ if (key.name === 'left') {
264
+ cleanup();
265
+ menuSuppressUntil = Date.now() + 300;
266
+ resolve({ value: '__cancel__', selectedIndex });
267
+ return;
268
+ }
269
+
270
+ // Letter shortcuts
271
+ if (str && str.length === 1 && str >= 'a' && str <= 'z') {
272
+ if (str === 'x') {
273
+ // Find exit item
274
+ const exitIdx = items.findIndex(i => i.value === 'exit');
275
+ if (exitIdx !== -1) {
276
+ selectOption(exitIdx);
277
+ } else {
278
+ cleanup();
279
+ resolve({ value: 'exit', selectedIndex });
280
+ }
281
+ return;
282
+ }
283
+
284
+ if (str === 'f') {
285
+ // F key for feedback - global shortcut
286
+ cleanup();
287
+ resolve({ value: 'feedback', selectedIndex });
288
+ return;
289
+ }
290
+
291
+ const letterIdx = str.charCodeAt(0) - 97;
292
+ let count = 0;
293
+ for (let i = 0; i < items.length; i++) {
294
+ if (items[i].type !== 'blank' && items[i].type !== 'info' && items[i].value !== 'exit') {
295
+ if (count === letterIdx) {
296
+ selectOption(i);
297
+ return;
298
+ }
299
+ count++;
300
+ }
301
+ }
302
+ }
303
+
304
+ // Arrow up
305
+ if (key.name === 'up') {
306
+ let next = selectedIndex - 1;
307
+ while (next >= 0 && !isSelectable(items[next])) next--;
308
+ if (next < 0) {
309
+ // Wrap to bottom
310
+ next = items.length - 1;
311
+ while (next >= 0 && !isSelectable(items[next])) next--;
312
+ }
313
+ if (next >= 0) {
314
+ selectedIndex = next;
315
+ stateTracker.update({ selectedIndex });
316
+ display();
317
+ }
318
+ return;
319
+ }
320
+
321
+ // Arrow down
322
+ if (key.name === 'down') {
323
+ let next = selectedIndex + 1;
324
+ while (next < items.length && !isSelectable(items[next])) next++;
325
+ if (next >= items.length) {
326
+ // Wrap to top
327
+ next = 0;
328
+ while (next < items.length && !isSelectable(items[next])) next++;
329
+ }
330
+ if (next < items.length) {
331
+ selectedIndex = next;
332
+ stateTracker.update({ selectedIndex });
333
+ display();
334
+ }
335
+ return;
336
+ }
337
+
338
+ // Enter or right-arrow → select
339
+ if (key.name === 'return' || key.name === 'right') {
340
+ if (isSelectable(items[selectedIndex])) {
341
+ selectOption(selectedIndex);
342
+ }
343
+ return;
344
+ }
345
+ };
346
+
347
+ // Initial render
348
+ display();
349
+
350
+ // Set up keypress listener
351
+ readline.emitKeypressEvents(process.stdin);
352
+ if (process.stdin.isTTY && process.stdin.setRawMode) {
353
+ process.stdin.setRawMode(true);
354
+ }
355
+ menuSuppressUntil = Date.now() + 300;
356
+ process.stdin.on('keypress', onKeypress);
357
+ process.stdin.resume();
358
+ });
359
+ } catch (error) {
360
+ debugLogger.error('showQuickMenu error', { error: error.message, stack: error.stack });
361
+ throw error;
362
+ }
363
+ }
364
+
365
+ module.exports = { showQuickMenu, indexToLetter, stripAnsi, getVisualLineCount };