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,585 @@
1
+ const chalk = require('chalk');
2
+ const inquirer = require('inquirer');
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const readline = require('readline');
6
+ const { t, getRequirementsPath, getVibeCodingMachineDir, checkVibeCodingMachineExists } = require('vibecodingmachine-core');
7
+ const { parseRequirementsFromContent } = require('./requirements-parser');
8
+ const { countRequirements } = require('./status-helpers');
9
+
10
+ // Import helper functions that will be extracted separately
11
+ // These will need to be imported from requirement-actions.js once created
12
+ const {
13
+ confirmAndExit,
14
+ showRequirementActions,
15
+ showClarificationActions,
16
+ handleAddRequirement,
17
+ deleteRequirement,
18
+ deleteClarification,
19
+ permanentlyDeleteRequirement,
20
+ moveRequirementDown,
21
+ moveRequirementUp,
22
+ promoteRequirement,
23
+ demoteRequirement,
24
+ moveClarificationToTodo,
25
+ handleFeedbackSubmission
26
+ } = require('./requirement-actions');
27
+
28
+ /**
29
+ * Tree-style requirements navigator
30
+ */
31
+ async function showRequirementsTree() {
32
+ console.log(chalk.bold.cyan('\nšŸ“‹ ' + t('requirements.navigator.title') + '\n'));
33
+ console.log(chalk.gray(t('requirements.navigator.basic.instructions') + '\n'));
34
+ console.log(chalk.gray('šŸ’” Press F for feedback - Share your thoughts anytime\n'));
35
+
36
+ const tree = {
37
+ expanded: { root: true },
38
+ selected: 0,
39
+ items: []
40
+ };
41
+
42
+ // Build tree structure
43
+ const buildTree = async () => {
44
+ tree.items = [];
45
+
46
+ // Root: Requirements
47
+ tree.items.push({ level: 0, type: 'root', label: `šŸ“‹ ${t('requirements.menu.label')}`, key: 'root' });
48
+
49
+ if (tree.expanded.root) {
50
+ tree.items.push({ level: 1, type: 'add', label: 'āž• ' + t('requirements.add.new'), key: 'add-one' });
51
+ tree.items.push({ level: 1, type: 'add', label: 'āž• ' + t('requirements.add.multiple'), key: 'add-many' });
52
+
53
+ // Use pre-calculated stats and labels from shared logic
54
+ const stats = await countRequirements();
55
+ const { todoCount, toVerifyCount, verifiedCount, total, todoLabel, toVerifyLabel, verifiedLabel } = stats || {
56
+ todoCount: 0, toVerifyCount: 0, verifiedCount: 0, total: 0,
57
+ todoLabel: 'ā³ TODO (0 - 0%)', toVerifyLabel: 'āœ… TO VERIFY (0 - 0%)', verifiedLabel: 'šŸŽ‰ VERIFIED (0 - 0%)'
58
+ };
59
+
60
+ // Create localized labels
61
+ const localizedTodoLabel = todoCount > 0 ?
62
+ `ā³ ${t('requirements.section.todo')} (${todoCount} - ${Math.round((todoCount / total) * 100)}%)` :
63
+ `ā³ ${t('requirements.section.todo')} (0 - 0%)`;
64
+
65
+ const localizedToVerifyLabel = toVerifyCount > 0 ?
66
+ `āœ… ${t('requirements.section.to.verify')} (${toVerifyCount} - ${Math.round((toVerifyCount / total) * 100)}%)` :
67
+ `āœ… ${t('requirements.section.to.verify')} (0 - 0%)`;
68
+
69
+ const localizedVerifiedLabel = verifiedCount > 0 ?
70
+ `šŸŽ‰ ${t('requirements.section.verified')} (${verifiedCount} - ${Math.round((verifiedCount / total) * 100)}%)` :
71
+ `šŸŽ‰ ${t('requirements.section.verified')} (0 - 0%)`;
72
+
73
+ const verifiedReqs = tree.verifiedReqs || [];
74
+ const verifyReqs = tree.verifyReqs || [];
75
+ const clarificationReqs = tree.clarificationReqs || [];
76
+ const todoReqs = tree.todoReqs || [];
77
+ const recycledReqs = tree.recycledReqs || [];
78
+
79
+ // Calculate percentages for clarification and recycled sections
80
+ const clarificationPercent = total > 0 ? Math.round((clarificationReqs.length / total) * 100) : 0;
81
+ const recycledPercent = total > 0 ? Math.round((recycledReqs.length / total) * 100) : 0;
82
+
83
+ // VERIFIED section (first) - only show if has requirements
84
+ if (verifiedReqs.length > 0 || verifiedCount > 0) {
85
+ tree.items.push({ level: 1, type: 'section', label: localizedVerifiedLabel, key: 'verified' });
86
+
87
+ if (tree.expanded.verified) {
88
+ verifiedReqs.forEach((req, idx) => {
89
+ tree.items.push({ level: 2, type: 'verified', label: req, key: `verified-${idx}` });
90
+ });
91
+ }
92
+ }
93
+
94
+ // TO VERIFY section (second) - only show if has requirements
95
+ if (verifyReqs.length > 0 || toVerifyCount > 0) {
96
+ tree.items.push({ level: 1, type: 'section', label: localizedToVerifyLabel, key: 'verify', section: 'āœ… Verified by AI screenshot' });
97
+
98
+ if (tree.expanded.verify) {
99
+ verifyReqs.forEach((req, idx) => {
100
+ tree.items.push({ level: 2, type: 'requirement', label: req.title, key: `verify-${idx}`, req, sectionKey: 'verify' });
101
+ });
102
+ }
103
+ }
104
+
105
+ // NEEDING CLARIFICATION section (third) - only show if has requirements
106
+ if (clarificationReqs.length > 0) {
107
+ tree.items.push({ level: 1, type: 'section', label: `ā“ NEEDING CLARIFICATION (${clarificationReqs.length} - ${clarificationPercent}%)`, key: 'clarification', section: 'ā“ Requirements needing manual feedback' });
108
+
109
+ if (tree.expanded.clarification) {
110
+ clarificationReqs.forEach((req, idx) => {
111
+ tree.items.push({ level: 2, type: 'clarification', label: req.title, key: `clarification-${idx}`, req, sectionKey: 'clarification' });
112
+ });
113
+ }
114
+ }
115
+
116
+ // TODO section (fourth) - only show if has requirements
117
+ if (todoReqs.length > 0 || todoCount > 0) {
118
+ tree.items.push({ level: 1, type: 'section', label: localizedTodoLabel, key: 'todo', section: 'ā³ Requirements not yet completed' });
119
+
120
+ if (tree.expanded.todo) {
121
+ todoReqs.forEach((req, idx) => {
122
+ tree.items.push({ level: 2, type: 'requirement', label: req.title, key: `todo-${idx}`, req, sectionKey: 'todo' });
123
+ });
124
+ }
125
+ }
126
+
127
+ // RECYCLED section (last) - only show if has requirements
128
+ if (recycledReqs.length > 0) {
129
+ tree.items.push({ level: 1, type: 'section', label: `ā™»ļø RECYCLED (${recycledReqs.length} - ${recycledPercent}%)`, key: 'recycled', section: 'ā™»ļø Recycled' });
130
+
131
+ if (tree.expanded.recycled) {
132
+ recycledReqs.forEach((req, idx) => {
133
+ tree.items.push({ level: 2, type: 'recycled', label: req.title, key: `recycled-${idx}`, req, sectionKey: 'recycled' });
134
+ });
135
+ }
136
+ }
137
+ }
138
+ };
139
+
140
+ // Load requirements for a section
141
+ const loadSection = async (sectionKey, sectionTitle) => {
142
+ const reqPath = await getRequirementsPath();
143
+
144
+ if (!reqPath || !await fs.pathExists(reqPath)) {
145
+ return [];
146
+ }
147
+
148
+ const content = await fs.readFile(reqPath, 'utf8');
149
+
150
+ // Delegate to reusable parser
151
+ const allReqs = parseRequirementsFromContent(content, sectionKey, sectionTitle);
152
+
153
+ // For TODO section, only show primary heading requirements (those marked from '###' titles)
154
+ if (sectionKey === 'todo') return allReqs.filter(r => r.source === 'heading');
155
+
156
+ return allReqs;
157
+ };
158
+
159
+ // Load VERIFIED requirements from CHANGELOG
160
+ const loadVerified = async () => {
161
+ const allnightStatus = await checkVibeCodingMachineExists();
162
+ let changelogPath;
163
+
164
+ if (allnightStatus.insideExists) {
165
+ const allnightDir = await getVibeCodingMachineDir();
166
+ changelogPath = path.join(path.dirname(allnightDir), 'CHANGELOG.md');
167
+ } else if (allnightStatus.siblingExists) {
168
+ changelogPath = path.join(process.cwd(), 'CHANGELOG.md');
169
+ }
170
+
171
+ if (!changelogPath || !await fs.pathExists(changelogPath)) {
172
+ return [];
173
+ }
174
+
175
+ const content = await fs.readFile(changelogPath, 'utf8');
176
+ const lines = content.split('\n');
177
+ const requirements = [];
178
+ let inVerifiedSection = false;
179
+
180
+ for (const line of lines) {
181
+ const trimmed = line.trim();
182
+
183
+ // Check for Verified Requirements section
184
+ if (trimmed.includes('## Verified Requirements')) {
185
+ inVerifiedSection = true;
186
+ continue;
187
+ }
188
+
189
+ // Exit section if we hit another ## header
190
+ if (inVerifiedSection && trimmed.startsWith('##') && !trimmed.includes('Verified Requirements')) {
191
+ break;
192
+ }
193
+
194
+ // Only collect items from within the Verified Requirements section
195
+ if (inVerifiedSection && trimmed.startsWith('- ') && trimmed.length > 10) {
196
+ requirements.push(trimmed.substring(2));
197
+ }
198
+ }
199
+
200
+ return requirements;
201
+ };
202
+
203
+ // Load clarification requirements with questions
204
+ const loadClarification = async () => {
205
+ const reqPath = await getRequirementsPath();
206
+
207
+ if (!reqPath || !await fs.pathExists(reqPath)) {
208
+ return [];
209
+ }
210
+
211
+ const content = await fs.readFile(reqPath, 'utf8');
212
+ const lines = content.split('\n');
213
+
214
+ let inSection = false;
215
+ const requirements = [];
216
+
217
+ for (let i = 0; i < lines.length; i++) {
218
+ const line = lines[i];
219
+ const trimmed = line.trim();
220
+
221
+ // Check if we're entering the clarification section
222
+ if (trimmed.includes('ā“ Requirements needing manual feedback')) {
223
+ inSection = true;
224
+ continue;
225
+ }
226
+
227
+ // Check if we're leaving the section (hit another ## section)
228
+ if (inSection && trimmed.startsWith('##') && !trimmed.startsWith('###')) {
229
+ break;
230
+ }
231
+
232
+ // Read requirements in new format (### header)
233
+ if (inSection && trimmed.startsWith('###')) {
234
+ const title = trimmed.replace(/^###\s*/, '').trim();
235
+
236
+ // Skip empty titles
237
+ if (!title || title.length === 0) {
238
+ continue;
239
+ }
240
+
241
+ const details = [];
242
+ const questions = [];
243
+ let pkg = null;
244
+ let findings = null;
245
+ let currentQuestion = null;
246
+
247
+ // Read package, description, and clarifying questions
248
+ for (let j = i + 1; j < lines.length; j++) {
249
+ const nextLine = lines[j].trim();
250
+
251
+ // Stop if we hit another requirement or section
252
+ if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
253
+ break;
254
+ }
255
+
256
+ // Check for PACKAGE line
257
+ if (nextLine.startsWith('PACKAGE:')) {
258
+ pkg = nextLine.replace(/^PACKAGE:\s*/, '').trim();
259
+ }
260
+ // Check for AI findings
261
+ else if (nextLine.startsWith('**AI found in codebase:**')) {
262
+ // Next line will be the findings
263
+ continue;
264
+ }
265
+ else if (nextLine.startsWith('**What went wrong')) {
266
+ // Description line
267
+ details.push(nextLine);
268
+ }
269
+ else if (nextLine.startsWith('**Clarifying questions:**')) {
270
+ // Start of questions section
271
+ continue;
272
+ }
273
+ else if (nextLine.match(/^\d+\./)) {
274
+ // Save previous question if exists
275
+ if (currentQuestion) {
276
+ questions.push(currentQuestion);
277
+ }
278
+ // This is a new question
279
+ currentQuestion = { question: nextLine, response: null };
280
+ }
281
+ else if (currentQuestion && nextLine && !nextLine.startsWith('PACKAGE:') && !nextLine.startsWith('**')) {
282
+ // This might be a response to the current question or description/findings
283
+ if (!findings && !currentQuestion.response && questions.length === 0 && !nextLine.match(/^\d+\./)) {
284
+ // This is findings content
285
+ findings = nextLine;
286
+ } else if (currentQuestion && !currentQuestion.response) {
287
+ // This is a response to the current question
288
+ currentQuestion.response = nextLine;
289
+ } else {
290
+ // Description line
291
+ details.push(nextLine);
292
+ }
293
+ }
294
+ else if (nextLine && !nextLine.startsWith('PACKAGE:') && !nextLine.startsWith('**')) {
295
+ // Description line
296
+ details.push(nextLine);
297
+ }
298
+ }
299
+
300
+ // Save last question if exists
301
+ if (currentQuestion) {
302
+ questions.push(currentQuestion);
303
+ }
304
+
305
+ requirements.push({ title, details, pkg, questions, findings, lineIndex: i });
306
+ }
307
+ }
308
+
309
+ return requirements;
310
+ };
311
+
312
+ // Load all sections upfront to show counts immediately
313
+ tree.todoReqs = await loadSection('todo', 'ā³ Requirements not yet completed');
314
+ tree.verifyReqs = await loadSection('verify', 'āœ… Verified by AI screenshot');
315
+ tree.clarificationReqs = await loadClarification();
316
+ tree.verifiedReqs = await loadVerified();
317
+ tree.recycledReqs = await loadSection('recycled', 'ā™»ļø Recycled');
318
+
319
+ let inTree = true;
320
+ await buildTree();
321
+
322
+ while (inTree) {
323
+ console.clear();
324
+ console.log(chalk.bold.cyan('\nšŸ“‹ ' + t('requirements.navigator.title') + '\n'));
325
+ console.log(chalk.gray(t('requirements.navigator.instructions') + '\n'));
326
+
327
+ // Safety check: ensure tree.selected is within bounds
328
+ if (tree.items.length === 0) {
329
+ console.log(chalk.yellow('No items to display.'));
330
+ console.log(chalk.gray(`\n${t('interactive.press.any.key.return')}`));
331
+ await new Promise((resolve) => {
332
+ process.stdin.once('keypress', () => resolve());
333
+ });
334
+ inTree = false;
335
+ continue;
336
+ }
337
+
338
+ if (tree.selected >= tree.items.length) {
339
+ tree.selected = tree.items.length - 1;
340
+ }
341
+
342
+ if (tree.selected < 0) {
343
+ tree.selected = 0;
344
+ }
345
+
346
+ // Calculate window for scrolling (show max 20 items at a time)
347
+ const maxVisible = 20;
348
+ let startIdx = 0;
349
+ let endIdx = tree.items.length;
350
+
351
+ if (tree.items.length > maxVisible) {
352
+ // Center the selected item in the window
353
+ startIdx = Math.max(0, tree.selected - Math.floor(maxVisible / 2));
354
+ endIdx = Math.min(tree.items.length, startIdx + maxVisible);
355
+
356
+ // Adjust if we're near the end
357
+ if (endIdx - startIdx < maxVisible) {
358
+ startIdx = Math.max(0, endIdx - maxVisible);
359
+ }
360
+ }
361
+
362
+ // Show indicator if there are items above
363
+ if (startIdx > 0) {
364
+ console.log(chalk.gray(` ↑ ${startIdx} more above...`));
365
+ }
366
+
367
+ // Display visible tree items
368
+ for (let idx = startIdx; idx < endIdx; idx++) {
369
+ const item = tree.items[idx];
370
+ const indent = ' '.repeat(item.level);
371
+ const arrow = tree.expanded[item.key] ? 'ā–¼' : (item.type === 'section' ? 'ā–¶' : ' ');
372
+ const prefix = item.type === 'section' || item.type === 'root' ? arrow + ' ' : ' ';
373
+ const selected = idx === tree.selected ? chalk.cyan('āÆ ') : ' ';
374
+
375
+ // Truncate long labels to fit terminal width (max 120 chars)
376
+ const maxLabelWidth = 120;
377
+ let label = item.label;
378
+ if (label.length > maxLabelWidth) {
379
+ label = label.substring(0, maxLabelWidth - 3) + '...';
380
+ }
381
+
382
+ console.log(selected + indent + prefix + (idx === tree.selected ? chalk.cyan(label) : chalk.gray(label)));
383
+ }
384
+
385
+ // Show indicator if there are items below
386
+ if (endIdx < tree.items.length) {
387
+ console.log(chalk.gray(` ↓ ${tree.items.length - endIdx} more below...`));
388
+ }
389
+
390
+ console.log();
391
+
392
+ // Handle input
393
+ const key = await new Promise((resolve) => {
394
+ readline.emitKeypressEvents(process.stdin);
395
+ if (process.stdin.isTTY) {
396
+ process.stdin.setRawMode(true);
397
+ }
398
+
399
+ const handler = (str, key) => {
400
+ process.stdin.removeListener('keypress', handler);
401
+ if (process.stdin.isTTY) {
402
+ process.stdin.setRawMode(false);
403
+ }
404
+ resolve(key);
405
+ };
406
+
407
+ process.stdin.on('keypress', handler);
408
+ process.stdin.resume();
409
+ });
410
+
411
+ if (!key) continue;
412
+
413
+ // Handle key presses
414
+ if (key.ctrl && key.name === 'c') {
415
+ // Ctrl+C always exits immediately
416
+ process.exit(0);
417
+ } else if (key.name === 'x' || key.name === 'escape') {
418
+ // X or ESC key - exit CLI with confirmation
419
+ await confirmAndExit();
420
+ } else if (key.name === 'left') {
421
+ const current = tree.items[tree.selected];
422
+ if (!current) continue; // Safety check
423
+
424
+ if (tree.expanded[current.key]) {
425
+ // Collapse expanded section
426
+ tree.expanded[current.key] = false;
427
+ await buildTree();
428
+ } else if (current.level > 0) {
429
+ // Go to parent
430
+ for (let i = tree.selected - 1; i >= 0; i--) {
431
+ if (tree.items[i].level < current.level) {
432
+ tree.selected = i;
433
+ break;
434
+ }
435
+ }
436
+ } else {
437
+ // At root level, go back to main menu
438
+ inTree = false;
439
+ }
440
+ } else if (key.name === 'k' || key.name === 'up') {
441
+ tree.selected = Math.max(0, tree.selected - 1);
442
+ await buildTree();
443
+ } else if (key.name === 'j' || key.name === 'down') {
444
+ tree.selected = Math.min(tree.items.length - 1, tree.selected + 1);
445
+ await buildTree();
446
+ } else if (key.name === 'right' || key.name === 'return' || key.name === 'space') {
447
+ const current = tree.items[tree.selected];
448
+ if (!current) continue; // Safety check
449
+ if (current.type === 'section') {
450
+ if (!tree.expanded[current.key]) {
451
+ tree.expanded[current.key] = true;
452
+ // Load requirements for this section
453
+ if (current.key === 'todo') {
454
+ tree.todoReqs = await loadSection(current.key, current.section);
455
+ } else if (current.key === 'verify') {
456
+ tree.verifyReqs = await loadSection(current.key, current.section);
457
+ } else if (current.key === 'verified') {
458
+ tree.verifiedReqs = await loadVerified();
459
+ } else if (current.key === 'recycled') {
460
+ tree.recycledReqs = await loadSection(current.key, current.section);
461
+ }
462
+ await buildTree();
463
+ } else {
464
+ tree.expanded[current.key] = false;
465
+ await buildTree();
466
+ }
467
+ } else if (current.type === 'requirement') {
468
+ // Show requirement actions
469
+ await showRequirementActions(current.req, current.sectionKey, tree);
470
+ await buildTree();
471
+ } else if (current.type === 'clarification') {
472
+ // Show clarification requirement with questions
473
+ await showClarificationActions(current.req, tree, loadClarification);
474
+ await buildTree();
475
+ } else if (current.type === 'verified') {
476
+ // Show verified item details (read-only)
477
+ console.clear();
478
+ console.log(chalk.bold.green(`\n${current.label}\n`));
479
+ console.log(chalk.gray('(From CHANGELOG.md - read only)'));
480
+ console.log(chalk.gray(`\n${t('interactive.press.any.key.back')}`));
481
+ await new Promise((resolve) => {
482
+ readline.emitKeypressEvents(process.stdin);
483
+ if (process.stdin.isTTY) {
484
+ process.stdin.setRawMode(true);
485
+ }
486
+ const handler = (str, key) => {
487
+ process.stdin.removeListener('keypress', handler);
488
+ if (process.stdin.isTTY) {
489
+ process.stdin.setRawMode(false);
490
+ }
491
+ resolve();
492
+ };
493
+ process.stdin.on('keypress', handler);
494
+ process.stdin.resume();
495
+ });
496
+ } else if (current.type === 'add') {
497
+ // Handle add requirement
498
+ await handleAddRequirement(current.key);
499
+ // Reload TODO section
500
+ tree.todoReqs = await loadSection('todo', 'ā³ Requirements not yet completed');
501
+ await buildTree();
502
+ }
503
+ } else if (key.name === 'f') {
504
+ // Feedback button ( megaphone šŸ“£ )
505
+ await handleFeedbackSubmission();
506
+ await buildTree();
507
+ } else if (key.name === 'r') {
508
+ const current = tree.items[tree.selected];
509
+ if (!current) continue; // Safety check
510
+
511
+ if (current.type === 'requirement') {
512
+ await deleteRequirement(current.req, current.sectionKey, tree);
513
+ // Reload the section that the requirement was deleted from
514
+ if (current.sectionKey === 'todo') {
515
+ tree.todoReqs = await loadSection('todo', 'ā³ Requirements not yet completed');
516
+ } else if (current.sectionKey === 'verify') {
517
+ tree.verifyReqs = await loadSection('verify', 'āœ… Verified by AI screenshot');
518
+ }
519
+ await buildTree();
520
+ } else if (current.type === 'clarification') {
521
+ await deleteClarification(current.req, tree);
522
+ tree.clarificationReqs = await loadClarification();
523
+ await buildTree();
524
+ } else if (current.type === 'recycled') {
525
+ await permanentlyDeleteRequirement(current.req, current.sectionKey, tree);
526
+ tree.recycledReqs = await loadSection('recycled', 'ā™»ļø Recycled');
527
+ await buildTree();
528
+ }
529
+ } else if (key.name === 'j') {
530
+ const current = tree.items[tree.selected];
531
+ if (!current) continue; // Safety check
532
+
533
+ if (current.type === 'requirement') {
534
+ await moveRequirementDown(current.req, current.sectionKey, tree);
535
+ await buildTree();
536
+ // Move selection down to follow the item
537
+ if (tree.selected < tree.items.length - 1) {
538
+ tree.selected++;
539
+ }
540
+ }
541
+ } else if (key.name === 'k') {
542
+ const current = tree.items[tree.selected];
543
+ if (!current) continue; // Safety check
544
+
545
+ if (current.type === 'requirement') {
546
+ await moveRequirementUp(current.req, current.sectionKey, tree);
547
+ await buildTree();
548
+ // Move selection up to follow the item
549
+ if (tree.selected > 0) {
550
+ tree.selected--;
551
+ }
552
+ }
553
+ } else if (key.name === 'u') {
554
+ const current = tree.items[tree.selected];
555
+ if (!current) continue; // Safety check
556
+
557
+ if (current.type === 'requirement') {
558
+ await promoteRequirement(current.req, current.sectionKey, tree, loadSection, loadVerified);
559
+ await buildTree();
560
+ }
561
+ } else if (key.name === 'd') {
562
+ const current = tree.items[tree.selected];
563
+ if (!current) continue; // Safety check
564
+
565
+ if (current.type === 'clarification') {
566
+ // D on clarification item = Move to TODO
567
+ await moveClarificationToTodo(current.req, tree);
568
+ tree.clarificationReqs = await loadClarification();
569
+ tree.todoReqs = await loadSection('todo', 'ā³ Requirements not yet completed');
570
+ await buildTree();
571
+ } else if (current.type === 'requirement' || current.type === 'verified') {
572
+ const sectionKey = current.type === 'verified' ? 'verified' : current.sectionKey;
573
+ const reqTitle = current.type === 'verified' ? current.label : current.req.title;
574
+ await demoteRequirement(reqTitle, sectionKey, tree, loadSection, loadVerified);
575
+ await buildTree();
576
+ }
577
+ }
578
+ }
579
+
580
+ process.stdin.pause();
581
+ }
582
+
583
+ module.exports = {
584
+ showRequirementsTree
585
+ };