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,372 @@
1
+ /**
2
+ * TRUI Requirement Actions
3
+ *
4
+ * Provides an action menu for a selected requirement and the addRequirementFlow.
5
+ * Uses inquirer for prompted input (rename, add) and showQuickMenu for action selection.
6
+ */
7
+
8
+ const chalk = require('chalk');
9
+ const inquirer = require('inquirer');
10
+ const fs = require('fs-extra');
11
+ const readline = require('readline');
12
+
13
+ /**
14
+ * Single-keypress confirm: Y/Enter/- = yes, N/Esc = no. No Enter required.
15
+ * Shows: `message (Y/n/-)` and echoes the choice.
16
+ */
17
+ async function _confirmKeypress(message) {
18
+ return new Promise(resolve => {
19
+ process.stdout.write(message + ' (Y/n/-)\n\n');
20
+ readline.emitKeypressEvents(process.stdin);
21
+ if (process.stdin.isTTY && process.stdin.setRawMode) {
22
+ process.stdin.setRawMode(true);
23
+ }
24
+ process.stdin.resume();
25
+
26
+ const cleanup = () => {
27
+ process.stdin.removeListener('keypress', onKey);
28
+ if (process.stdin.isTTY && process.stdin.setRawMode) {
29
+ process.stdin.setRawMode(false);
30
+ }
31
+ process.stdin.pause();
32
+ };
33
+
34
+ const onKey = (str, key) => {
35
+ if (!key) return;
36
+ if (key.ctrl && key.name === 'c') { cleanup(); process.exit(0); }
37
+ const ch = (str || '').toLowerCase();
38
+ if (ch === 'y' || key.name === 'return' || ch === '-') {
39
+ cleanup();
40
+ process.stdout.write(chalk.green('y') + '\n');
41
+ resolve(true);
42
+ } else if (ch === 'n' || key.name === 'escape') {
43
+ cleanup();
44
+ process.stdout.write(chalk.gray('n') + '\n');
45
+ resolve(false);
46
+ }
47
+ };
48
+
49
+ process.stdin.on('keypress', onKey);
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Show action menu for a requirement item.
55
+ * @param {{title: string, status: string}} req
56
+ * @param {string} sectionKey - 'todo' | 'verify' | 'verified' | 'recycled'
57
+ * @param {Function} onChanged - called with true when data changes (tree should reload)
58
+ * @returns {Promise<boolean>} true if data changed
59
+ */
60
+ async function showRequirementActions(req, sectionKey, onChanged) {
61
+ const { showQuickMenu } = require('./trui-quick-menu');
62
+
63
+ const {
64
+ promoteToVerified,
65
+ demoteFromVerifiedToTodo,
66
+ promoteTodoToVerify,
67
+ demoteVerifyToTodo,
68
+ getOrCreateRequirementsFilePath,
69
+ getRequirementsPath,
70
+ } = require('vibecodingmachine-core');
71
+ const { getRepoPath, getEffectiveRepoPath } = require('./config');
72
+
73
+ // Build action items based on current section
74
+ const actions = [];
75
+
76
+ if (sectionKey === 'todo') {
77
+ actions.push({ type: 'action', name: 'Promote to TO VERIFY', value: 'promote' });
78
+ }
79
+ if (sectionKey === 'verify') {
80
+ actions.push({ type: 'action', name: 'Promote to VERIFIED', value: 'verify-promote' });
81
+ actions.push({ type: 'action', name: 'Demote to TODO', value: 'demote' });
82
+ }
83
+ if (sectionKey === 'verified') {
84
+ actions.push({ type: 'action', name: 'Demote to TODO', value: 'demote-verified' });
85
+ }
86
+
87
+ actions.push({ type: 'action', name: 'Move Item Up in section', value: 'move-up' });
88
+ actions.push({ type: 'action', name: 'Move Item Down in section', value: 'move-down' });
89
+ actions.push({ type: 'action', name: 'Rename', value: 'rename' });
90
+ actions.push({ type: 'action', name: chalk.red('Delete'), value: 'delete' });
91
+
92
+ console.log(chalk.bold.cyan(`\n📌 ${req.title} [${sectionKey}]\n`));
93
+
94
+ const result = await showQuickMenu(actions, 0);
95
+ if (result.value === '__cancel__' || result.value === 'exit') return false;
96
+
97
+ let reqPath;
98
+ try {
99
+ const repoPath = await getRepoPath();
100
+ const p = await getRequirementsPath(repoPath);
101
+ // Use effectiveRepoPath (git root) for creation so .vibecodingmachine is never
102
+ // created inside a sub-package directory when repoPath is null.
103
+ reqPath = (p && await fs.pathExists(p)) ? p : await getOrCreateRequirementsFilePath(await getEffectiveRepoPath());
104
+ } catch (err) {
105
+ console.log(chalk.red('Cannot find requirements file: ' + err.message));
106
+ await _pause();
107
+ return false;
108
+ }
109
+
110
+ try {
111
+ if (result.value === 'promote') {
112
+ console.log(chalk.gray('\nEquivalent: vcm req:promote "' + req.title + '"'));
113
+ await promoteTodoToVerify(reqPath, req.title);
114
+ console.log(chalk.green('✓ Moved to TO VERIFY'));
115
+ await _pause();
116
+ return true;
117
+ }
118
+
119
+ if (result.value === 'verify-promote') {
120
+ console.log(chalk.gray('\nEquivalent: vcm req:verify "' + req.title + '"'));
121
+ await promoteToVerified(reqPath, req.title);
122
+ console.log(chalk.green('✓ Moved to VERIFIED'));
123
+ await _pause();
124
+ return true;
125
+ }
126
+
127
+ if (result.value === 'demote') {
128
+ console.log(chalk.gray('\nEquivalent: vcm req:demote "' + req.title + '"'));
129
+ await demoteVerifyToTodo(reqPath, req.title);
130
+ console.log(chalk.green('✓ Moved back to TODO'));
131
+ await _pause();
132
+ return true;
133
+ }
134
+
135
+ if (result.value === 'demote-verified') {
136
+ console.log(chalk.gray('\nEquivalent: vcm req:demote "' + req.title + '"'));
137
+ await demoteFromVerifiedToTodo(reqPath, req.title);
138
+ console.log(chalk.green('✓ Moved back to TODO'));
139
+ await _pause();
140
+ return true;
141
+ }
142
+
143
+ if (result.value === 'move-up' || result.value === 'move-down') {
144
+ const direction = result.value === 'move-up' ? 'up' : 'down';
145
+ const moved = await _moveRequirement(reqPath, req.title, sectionKey, direction);
146
+ if (moved) {
147
+ console.log(chalk.green(`✓ Moved ${direction}`));
148
+ console.log(chalk.gray(`Equivalent: vcm req:move-${direction} "${req.title}"`));
149
+ } else {
150
+ console.log(chalk.yellow('Already at the ' + (direction === 'up' ? 'top' : 'bottom')));
151
+ }
152
+ await _pause();
153
+ return moved;
154
+ }
155
+
156
+ if (result.value === 'rename') {
157
+ return await _renameRequirement(reqPath, req.title, sectionKey);
158
+ }
159
+
160
+ if (result.value === 'delete') {
161
+ return await _deleteRequirement(reqPath, req.title);
162
+ }
163
+ } catch (err) {
164
+ console.log(chalk.red('Error: ' + err.message));
165
+ await _pause();
166
+ }
167
+
168
+ return false;
169
+ }
170
+
171
+ /**
172
+ * Move a requirement up or down within its section
173
+ */
174
+ async function _moveRequirement(reqPath, title, sectionKey, direction) {
175
+ const content = await fs.readFile(reqPath, 'utf8');
176
+ const lines = content.split('\n');
177
+
178
+ // Find section boundaries
179
+ const sectionHeadings = {
180
+ todo: /requirements not yet completed/i,
181
+ verify: /to verify|verified by ai/i,
182
+ verified: /verified\b/i,
183
+ recycled: /recycled/i,
184
+ };
185
+ const pattern = sectionHeadings[sectionKey];
186
+
187
+ let sectionStart = -1;
188
+ let sectionEnd = lines.length;
189
+
190
+ for (let i = 0; i < lines.length; i++) {
191
+ const line = lines[i].trim();
192
+ if (sectionStart === -1 && line.startsWith('##') && pattern && pattern.test(line)) {
193
+ sectionStart = i;
194
+ } else if (sectionStart !== -1 && line.startsWith('## ') && i > sectionStart) {
195
+ sectionEnd = i;
196
+ break;
197
+ }
198
+ }
199
+
200
+ if (sectionStart === -1) return false;
201
+
202
+ // Find all requirement blocks (### headers) within the section
203
+ const reqBlocks = [];
204
+ let currentBlock = null;
205
+ for (let i = sectionStart + 1; i < sectionEnd; i++) {
206
+ const line = lines[i];
207
+ if (line.trim().startsWith('### ')) {
208
+ if (currentBlock) { currentBlock.end = i; reqBlocks.push(currentBlock); }
209
+ currentBlock = { title: line.trim().replace(/^###\s*/, ''), start: i, end: sectionEnd };
210
+ }
211
+ }
212
+ if (currentBlock) { currentBlock.end = sectionEnd; reqBlocks.push(currentBlock); }
213
+
214
+ const idx = reqBlocks.findIndex(b => b.title === title);
215
+ if (idx === -1) return false;
216
+ if (direction === 'up' && idx === 0) return false;
217
+ if (direction === 'down' && idx === reqBlocks.length - 1) return false;
218
+
219
+ const swapIdx = direction === 'up' ? idx - 1 : idx + 1;
220
+ const blockA = reqBlocks[idx];
221
+ const blockB = reqBlocks[swapIdx];
222
+
223
+ // Extract block content
224
+ const [first, second] = blockA.start < blockB.start ? [blockA, blockB] : [blockB, blockA];
225
+ const firstContent = lines.slice(first.start, first.end);
226
+ const secondContent = lines.slice(second.start, second.end);
227
+
228
+ // Swap
229
+ const newLines = [
230
+ ...lines.slice(0, first.start),
231
+ ...secondContent,
232
+ ...firstContent,
233
+ ...lines.slice(second.end),
234
+ ];
235
+
236
+ await fs.writeFile(reqPath, newLines.join('\n'), 'utf8');
237
+ return true;
238
+ }
239
+
240
+ /**
241
+ * Rename a requirement
242
+ */
243
+ async function _renameRequirement(reqPath, oldTitle, sectionKey) {
244
+ const { newTitle } = await inquirer.prompt([{
245
+ type: 'input',
246
+ name: 'newTitle',
247
+ message: chalk.cyan('New title:'),
248
+ default: oldTitle,
249
+ validate: v => v.trim().length > 0 || 'Title required',
250
+ }]);
251
+
252
+ if (newTitle.trim() === oldTitle) return false;
253
+
254
+ const content = await fs.readFile(reqPath, 'utf8');
255
+ const escaped = oldTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
256
+ const updated = content.replace(new RegExp(`^(###\\s*)${escaped}\\s*$`, 'm'), `$1${newTitle.trim()}`);
257
+
258
+ if (updated === content) {
259
+ console.log(chalk.yellow('Could not find requirement to rename'));
260
+ await _pause();
261
+ return false;
262
+ }
263
+
264
+ await fs.writeFile(reqPath, updated, 'utf8');
265
+ console.log(chalk.green(`✓ Renamed to "${newTitle.trim()}"`));
266
+ console.log(chalk.gray(`Equivalent: vcm req:rename "${oldTitle}" "${newTitle.trim()}"`));
267
+ await _pause();
268
+ return true;
269
+ }
270
+
271
+ /**
272
+ * Delete a requirement after confirmation
273
+ */
274
+ async function _deleteRequirement(reqPath, title) {
275
+ const confirmed = await _confirmKeypress(chalk.red(`Delete "${title}"?`));
276
+ if (!confirmed) return false;
277
+
278
+ const content = await fs.readFile(reqPath, 'utf8');
279
+ const lines = content.split('\n');
280
+ const escaped = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
281
+ const startRe = new RegExp(`^###\\s*${escaped}\\s*$`);
282
+
283
+ let startIdx = -1;
284
+ for (let i = 0; i < lines.length; i++) {
285
+ if (startRe.test(lines[i].trim())) { startIdx = i; break; }
286
+ }
287
+ if (startIdx === -1) {
288
+ console.log(chalk.yellow('Could not find requirement to delete'));
289
+ return false;
290
+ }
291
+
292
+ // Find end of this requirement block
293
+ let endIdx = startIdx + 1;
294
+ while (endIdx < lines.length) {
295
+ const line = lines[endIdx].trim();
296
+ if (line.startsWith('### ') || (line.startsWith('## ') && !line.startsWith('### '))) break;
297
+ endIdx++;
298
+ }
299
+
300
+ lines.splice(startIdx, endIdx - startIdx);
301
+ await fs.writeFile(reqPath, lines.join('\n'), 'utf8');
302
+ console.log(chalk.green(`✓ Deleted "${title}"`));
303
+ return true;
304
+ }
305
+
306
+ /**
307
+ * Prompt for a new requirement and append to the TODO section
308
+ */
309
+ async function addRequirementFlow() {
310
+ console.log(chalk.bold.cyan('\n📋 Add New Requirement\n'));
311
+ console.log(chalk.gray('Equivalent: vcm req:add "<title>"\n'));
312
+
313
+ const { title } = await inquirer.prompt([{
314
+ type: 'input',
315
+ name: 'title',
316
+ message: chalk.cyan('Title:'),
317
+ validate: v => v.trim().length > 0 || 'Title is required',
318
+ }]);
319
+
320
+ const { description } = await inquirer.prompt([{
321
+ type: 'input',
322
+ name: 'description',
323
+ message: chalk.cyan('Description (optional):'),
324
+ }]);
325
+
326
+ try {
327
+ const { getOrCreateRequirementsFilePath, getRequirementsPath } = require('vibecodingmachine-core');
328
+ const { getRepoPath: _getRepoPath, getEffectiveRepoPath: _getEffectiveRepoPath } = require('./config');
329
+ const _repoPath = await _getRepoPath();
330
+ const _p = await getRequirementsPath(_repoPath);
331
+ // Use effectiveRepoPath (git root) for creation to avoid nested .vibecodingmachine dirs.
332
+ const reqPath = (_p && await fs.pathExists(_p)) ? _p : await getOrCreateRequirementsFilePath(await _getEffectiveRepoPath());
333
+ const content = await fs.readFile(reqPath, 'utf8');
334
+ const lines = content.split('\n');
335
+
336
+ let insertIndex = -1;
337
+ for (let i = 0; i < lines.length; i++) {
338
+ if (lines[i].trim().match(/^##\s+.*(?:Requirements not yet completed|TODO)/i)) {
339
+ insertIndex = i + 1;
340
+ for (let j = i + 1; j < lines.length; j++) {
341
+ const inner = lines[j].trim();
342
+ if (inner.startsWith('## ') && !inner.startsWith('### ')) { insertIndex = j; break; }
343
+ if (inner.startsWith('### ') || inner.startsWith('- ')) { insertIndex = j; break; }
344
+ }
345
+ break;
346
+ }
347
+ }
348
+
349
+ const block = description.trim()
350
+ ? `### ${title.trim()}\n\n${description.trim()}\n`
351
+ : `### ${title.trim()}\n`;
352
+
353
+ if (insertIndex >= 0) {
354
+ lines.splice(insertIndex, 0, block);
355
+ } else {
356
+ lines.push('', '## Requirements not yet completed', '', block);
357
+ }
358
+
359
+ await fs.writeFile(reqPath, lines.join('\n'), 'utf8');
360
+ console.log(chalk.green(`\n✓ Added "${title.trim()}"`));
361
+ } catch (err) {
362
+ console.log(chalk.red('Error: ' + err.message));
363
+ }
364
+
365
+ await _pause();
366
+ }
367
+
368
+ async function _pause() {
369
+ await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...') }]);
370
+ }
371
+
372
+ module.exports = { showRequirementActions, addRequirementFlow, _moveRequirement, _deleteRequirement };