vibecodingmachine-cli 2026.2.26-1739 → 2026.3.9-1621

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 (74) hide show
  1. package/bin/auth/auth-compliance.js +7 -1
  2. package/bin/commands/agent-commands.js +150 -228
  3. package/bin/commands/command-aliases.js +68 -0
  4. package/bin/vibecodingmachine.js +1 -2
  5. package/package.json +2 -2
  6. package/src/commands/agents/list.js +71 -115
  7. package/src/commands/agents-check.js +16 -4
  8. package/src/commands/analyze-file-sizes.js +1 -1
  9. package/src/commands/auto-direct/auto-provider-manager.js +290 -0
  10. package/src/commands/auto-direct/auto-status-display.js +331 -0
  11. package/src/commands/auto-direct/auto-utils.js +439 -0
  12. package/src/commands/auto-direct/file-operations.js +110 -0
  13. package/src/commands/auto-direct/provider-config.js +1 -1
  14. package/src/commands/auto-direct/provider-manager.js +1 -1
  15. package/src/commands/auto-direct/status-display.js +1 -1
  16. package/src/commands/auto-direct/utils.js +24 -18
  17. package/src/commands/auto-direct-refactored.js +413 -0
  18. package/src/commands/auto-direct.js +594 -188
  19. package/src/commands/requirements/commands.js +353 -0
  20. package/src/commands/requirements/default-handlers.js +272 -0
  21. package/src/commands/requirements/disable.js +97 -0
  22. package/src/commands/requirements/enable.js +97 -0
  23. package/src/commands/requirements/utils.js +194 -0
  24. package/src/commands/requirements-refactored.js +60 -0
  25. package/src/commands/requirements.js +38 -771
  26. package/src/commands/specs/disable.js +96 -0
  27. package/src/commands/specs/enable.js +96 -0
  28. package/src/trui/TruiInterface.js +5 -11
  29. package/src/trui/agents/AgentInterface.js +24 -396
  30. package/src/trui/agents/handlers/CommandHandler.js +93 -0
  31. package/src/trui/agents/handlers/ContextManager.js +117 -0
  32. package/src/trui/agents/handlers/DisplayHandler.js +243 -0
  33. package/src/trui/agents/handlers/HelpHandler.js +51 -0
  34. package/src/utils/auth.js +13 -111
  35. package/src/utils/config.js +5 -1
  36. package/src/utils/interactive/requirements-navigation.js +17 -15
  37. package/src/utils/interactive-broken.js +2 -2
  38. package/src/utils/provider-checker/agent-runner.js +15 -1
  39. package/src/utils/provider-checker/cli-installer.js +149 -7
  40. package/src/utils/provider-checker/opencode-checker.js +588 -0
  41. package/src/utils/provider-checker/provider-validator.js +88 -3
  42. package/src/utils/provider-checker/time-formatter.js +3 -2
  43. package/src/utils/provider-manager.js +28 -20
  44. package/src/utils/provider-registry.js +35 -3
  45. package/src/utils/requirements-navigator/index.js +94 -0
  46. package/src/utils/requirements-navigator/input-handler.js +217 -0
  47. package/src/utils/requirements-navigator/section-loader.js +188 -0
  48. package/src/utils/requirements-navigator/tree-builder.js +105 -0
  49. package/src/utils/requirements-navigator/tree-renderer.js +50 -0
  50. package/src/utils/requirements-navigator.js +2 -583
  51. package/src/utils/trui-clarifications.js +188 -0
  52. package/src/utils/trui-feedback.js +54 -1
  53. package/src/utils/trui-kiro-integration.js +398 -0
  54. package/src/utils/trui-main-handlers.js +194 -0
  55. package/src/utils/trui-main-menu.js +235 -0
  56. package/src/utils/trui-nav-agents.js +178 -25
  57. package/src/utils/trui-nav-requirements.js +203 -27
  58. package/src/utils/trui-nav-settings.js +114 -1
  59. package/src/utils/trui-nav-specifications.js +44 -3
  60. package/src/utils/trui-navigation-backup.js +603 -0
  61. package/src/utils/trui-navigation.js +70 -228
  62. package/src/utils/trui-provider-health.js +274 -0
  63. package/src/utils/trui-provider-manager.js +376 -0
  64. package/src/utils/trui-quick-menu.js +25 -1
  65. package/src/utils/trui-req-actions-backup.js +507 -0
  66. package/src/utils/trui-req-actions.js +148 -216
  67. package/src/utils/trui-req-editor.js +170 -0
  68. package/src/utils/trui-req-file-ops.js +278 -0
  69. package/src/utils/trui-req-tree-old.js +719 -0
  70. package/src/utils/trui-req-tree.js +348 -627
  71. package/src/utils/trui-specifications.js +25 -7
  72. package/src/utils/trui-windsurf.js +231 -10
  73. package/src/utils/welcome-screen-extracted.js +2 -2
  74. package/src/utils/welcome-screen.js +2 -2
@@ -9,8 +9,9 @@ function formatCheckedAt(checkedAt) {
9
9
  const d = new Date(checkedAt);
10
10
  if (isNaN(d.getTime())) return checkedAt;
11
11
  const months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
12
- const timePart = d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }).toLowerCase();
13
- return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} at ${timePart}`;
12
+ const timePart = d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
13
+ const tzPart = d.toLocaleTimeString('en-US', { timeZoneName: 'short' }).split(' ').pop().toUpperCase();
14
+ return `on ${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} at ${timePart} ${tzPart}`;
14
15
  }
15
16
 
16
17
  module.exports = {
@@ -104,7 +104,7 @@ async function showProviderManagerMenu() {
104
104
  console.log(chalk.bold.cyan('⚙ ' + t('provider.title') + '\n'));
105
105
 
106
106
  // Header
107
- console.log(chalk.gray('Use ↑↓ to move, Space to toggle, </> to reorder (< down, > up), → to configure, !/1 to check agents, X/ESC to exit\n'));
107
+ console.log(chalk.gray('Use ↑↓ to move, Space to toggle, </> to reorder (< down, > up), → to configure, ← to go back, !/1 to check agents, X/ESC to exit\n'));
108
108
 
109
109
  // Display providers
110
110
  order.forEach((id, index) => {
@@ -193,11 +193,12 @@ async function showProviderManagerMenu() {
193
193
  await saveProviderPreferences({ order, enabled });
194
194
  await setProviderCache(Object.fromEntries(installationStatus));
195
195
  }
196
- // Trigger agents check after returning
196
+ // Trigger agents check after returning - only check enabled agents
197
197
  setTimeout(() => {
198
198
  console.clear();
199
199
  console.log(chalk.bold.cyan('🔍 Checking agents...\n'));
200
- require('../commands/agents-check')().then(() => {
200
+ const { checkAgents } = require('../commands/agents-check');
201
+ checkAgents({ enabledOnly: true, enabledAgents: enabled }).then(() => {
201
202
  process.exit(0);
202
203
  }).catch((err) => {
203
204
  console.log(chalk.red('Agents check error: ' + err.message));
@@ -224,6 +225,14 @@ async function showProviderManagerMenu() {
224
225
  break;
225
226
 
226
227
  case 'left':
228
+ // Go back to previous menu
229
+ isMenuActive = false;
230
+ if (dirty) {
231
+ await saveProviderPreferences({ order, enabled });
232
+ await setProviderCache(Object.fromEntries(installationStatus));
233
+ }
234
+ return;
235
+
227
236
  case '<':
228
237
  // Move selected provider down in order
229
238
  if (selectedIndex < order.length - 1) {
@@ -236,6 +245,22 @@ async function showProviderManagerMenu() {
236
245
  break;
237
246
 
238
247
  case 'right':
248
+ // Configure selected provider
249
+ const selectedId = order[selectedIndex];
250
+ const def = defMap.get(selectedId);
251
+ if (def && def.configOptions && def.configOptions.length > 0) {
252
+ console.log(chalk.cyan(`\nConfiguring ${getAgentDisplayName(selectedId)}...\n`));
253
+ // Here you would implement configuration logic
254
+ console.log(chalk.gray('Configuration not yet implemented in CLI'));
255
+ const inquirer = require('inquirer');
256
+ await inquirer.prompt([{
257
+ type: 'input',
258
+ name: 'continue',
259
+ message: 'Press Enter to continue...'
260
+ }]);
261
+ }
262
+ break;
263
+
239
264
  case '>':
240
265
  // Move selected provider up in order
241
266
  if (selectedIndex > 0) {
@@ -254,23 +279,6 @@ async function showProviderManagerMenu() {
254
279
  dirty = true;
255
280
  break;
256
281
 
257
- case 'c':
258
- // Configure selected provider
259
- const selectedId = order[selectedIndex];
260
- const def = defMap.get(selectedId);
261
- if (def && def.configOptions && def.configOptions.length > 0) {
262
- console.log(chalk.cyan(`\nConfiguring ${getAgentDisplayName(selectedId)}...\n`));
263
- // Here you would implement configuration logic
264
- console.log(chalk.gray('Configuration not yet implemented in CLI'));
265
- const inquirer = require('inquirer');
266
- await inquirer.prompt([{
267
- type: 'input',
268
- name: 'continue',
269
- message: 'Press Enter to continue...'
270
- }]);
271
- }
272
- break;
273
-
274
282
  default:
275
283
  return;
276
284
  }
@@ -118,6 +118,14 @@ const PROVIDER_DEFINITIONS = [
118
118
  defaultModel: 'opencode-cli',
119
119
  estimatedSpeed: 40000
120
120
  },
121
+ {
122
+ id: 'vscode-copilot-cli',
123
+ name: 'VS Code Copilot CLI',
124
+ type: 'direct',
125
+ category: 'llm',
126
+ defaultModel: 'copilot-cli',
127
+ estimatedSpeed: 45000
128
+ },
121
129
  {
122
130
  id: 'replit',
123
131
  name: 'Replit Agent',
@@ -166,6 +174,7 @@ function getProviderDisplayName(id) {
166
174
  'cline': 'ide.agent.cline',
167
175
  'claude-code': 'ide.agent.claude.code',
168
176
  'opencode': 'ide.agent.opencode',
177
+ 'vscode-copilot-cli': 'ide.agent.vscode.copilot.cli',
169
178
  'antigravity': 'ide.agent.antigravity',
170
179
  'github-copilot': 'ide.agent.github.copilot',
171
180
  'amazon-q': 'ide.agent.amazon.q',
@@ -199,8 +208,8 @@ function mergeProviderPreferences(autoConfig) {
199
208
  cleanedOrder.push(id);
200
209
  }
201
210
  if (enabled[id] === undefined) {
202
- // Enable the first provider by default (antigravity)
203
- enabled[id] = index === 0;
211
+ // Default to disabled, not enabled
212
+ enabled[id] = false;
204
213
  }
205
214
  });
206
215
 
@@ -209,7 +218,30 @@ function mergeProviderPreferences(autoConfig) {
209
218
 
210
219
  async function getProviderPreferences() {
211
220
  const autoConfig = await getAutoConfig();
212
- return mergeProviderPreferences(autoConfig);
221
+ // Directly return providerPreferences from auto config if it exists
222
+ if (autoConfig.providerPreferences) {
223
+ const prefs = autoConfig.providerPreferences;
224
+ // Ensure we never return empty preferences
225
+ if (!prefs.order || prefs.order.length === 0) {
226
+ const defaultOrder = getDefaultProviderOrder();
227
+ const defaultEnabled = {};
228
+ defaultOrder.forEach((id, index) => {
229
+ defaultEnabled[id] = false; // Default to disabled, not enabled
230
+ });
231
+ // Auto-fix empty preferences
232
+ await saveProviderPreferences(defaultOrder, defaultEnabled);
233
+ return { order: defaultOrder, enabled: defaultEnabled };
234
+ }
235
+ return prefs;
236
+ }
237
+ // If no preferences exist, create default disabled state
238
+ const defaultOrder = getDefaultProviderOrder();
239
+ const defaultEnabled = {};
240
+ defaultOrder.forEach((id, index) => {
241
+ defaultEnabled[id] = false; // Default to disabled
242
+ });
243
+ await saveProviderPreferences(defaultOrder, defaultEnabled);
244
+ return { order: defaultOrder, enabled: defaultEnabled };
213
245
  }
214
246
 
215
247
  async function saveProviderPreferences(order, enabled) {
@@ -0,0 +1,94 @@
1
+ const chalk = require('chalk');
2
+ const inquirer = require('inquirer');
3
+ const { t } = require('vibecodingmachine-core');
4
+ const { buildTreeStructure } = require('./tree-builder');
5
+ const { loadSection, loadVerified, loadClarification } = require('./section-loader');
6
+ const { renderTree, calculateScrollWindow } = require('./tree-renderer');
7
+ const { readKeypress, handleKeypress } = require('./input-handler');
8
+
9
+ /**
10
+ * Tree-style requirements navigator
11
+ */
12
+ async function showRequirementsTree() {
13
+ console.log(chalk.bold.cyan('\n📋 ' + t('requirements.navigator.title') + '\n'));
14
+ console.log(chalk.gray(t('requirements.navigator.basic.instructions') + '\n'));
15
+ console.log(chalk.gray('💡 Press F for feedback - Share your thoughts anytime\n'));
16
+
17
+ const tree = {
18
+ expanded: { root: true },
19
+ selected: 0,
20
+ items: []
21
+ };
22
+
23
+ // Build tree structure
24
+ const buildTree = async () => {
25
+ await buildTreeStructure(tree);
26
+ };
27
+
28
+ // Load all sections upfront to show counts immediately
29
+ tree.todoReqs = await loadSection('todo', '⏳ Requirements not yet completed');
30
+ tree.verifyReqs = await loadSection('verify', '✅ Verified by AI screenshot');
31
+ tree.clarificationReqs = await loadClarification();
32
+ tree.verifiedReqs = await loadVerified();
33
+ tree.recycledReqs = await loadSection('recycled', '♻️ Recycled');
34
+
35
+ let inTree = true;
36
+ await buildTree();
37
+
38
+ const loaders = { loadSection, loadVerified, loadClarification };
39
+
40
+ while (inTree) {
41
+ console.clear();
42
+ console.log(chalk.bold.cyan('\n📋 ' + t('requirements.navigator.title') + '\n'));
43
+ console.log(chalk.gray(t('requirements.navigator.instructions') + '\n'));
44
+
45
+ // Safety check: ensure tree.selected is within bounds
46
+ if (tree.items.length === 0) {
47
+ console.log(chalk.yellow('No items to display.'));
48
+ await inquirer.prompt([{
49
+ type: 'input',
50
+ name: 'continue',
51
+ message: `${t('interactive.press.any.key.return')}`
52
+ }]);
53
+ inTree = false;
54
+ continue;
55
+ }
56
+
57
+ if (tree.selected >= tree.items.length) {
58
+ tree.selected = tree.items.length - 1;
59
+ }
60
+
61
+ if (tree.selected < 0) {
62
+ tree.selected = 0;
63
+ }
64
+
65
+ // Calculate window for scrolling (show max 20 items at a time)
66
+ const { startIdx, endIdx } = calculateScrollWindow(tree, 20);
67
+
68
+ // Show indicator if there are items above
69
+ if (startIdx > 0) {
70
+ console.log(chalk.gray(` ↑ ${startIdx} more above...`));
71
+ }
72
+
73
+ // Display visible tree items
74
+ renderTree(tree, startIdx, endIdx);
75
+
76
+ // Show indicator if there are items below
77
+ if (endIdx < tree.items.length) {
78
+ console.log(chalk.gray(` ↓ ${tree.items.length - endIdx} more below...`));
79
+ }
80
+
81
+ console.log();
82
+
83
+ // Handle input
84
+ const key = await readKeypress();
85
+ const result = await handleKeypress(key, tree, loaders, buildTree);
86
+ inTree = result.inTree;
87
+ }
88
+
89
+ process.stdin.pause();
90
+ }
91
+
92
+ module.exports = {
93
+ showRequirementsTree
94
+ };
@@ -0,0 +1,217 @@
1
+ const readline = require('readline');
2
+ const chalk = require('chalk');
3
+ const { t } = require('vibecodingmachine-core');
4
+ const {
5
+ confirmAndExit,
6
+ showRequirementActions,
7
+ showClarificationActions,
8
+ handleAddRequirement,
9
+ deleteRequirement,
10
+ deleteClarification,
11
+ permanentlyDeleteRequirement,
12
+ moveRequirementDown,
13
+ moveRequirementUp,
14
+ promoteRequirement,
15
+ demoteRequirement,
16
+ moveClarificationToTodo,
17
+ handleFeedbackSubmission
18
+ } = require('../requirement-actions');
19
+
20
+ /**
21
+ * Reads a single keypress from user
22
+ */
23
+ async function readKeypress() {
24
+ return new Promise((resolve) => {
25
+ readline.emitKeypressEvents(process.stdin);
26
+ if (process.stdin.isTTY) {
27
+ process.stdin.setRawMode(true);
28
+ }
29
+
30
+ const handler = (str, key) => {
31
+ process.stdin.removeListener('keypress', handler);
32
+ if (process.stdin.isTTY) {
33
+ process.stdin.setRawMode(false);
34
+ }
35
+ resolve(key);
36
+ };
37
+
38
+ process.stdin.on('keypress', handler);
39
+ process.stdin.resume();
40
+ });
41
+ }
42
+
43
+ /**
44
+ * Handles key input and performs actions
45
+ */
46
+ async function handleKeypress(key, tree, loaders, buildTree) {
47
+ const { loadSection, loadVerified, loadClarification } = loaders;
48
+
49
+ if (!key) return { inTree: true };
50
+
51
+ // Handle key presses
52
+ if (key.ctrl && key.name === 'c') {
53
+ // Ctrl+C always exits immediately
54
+ process.exit(0);
55
+ } else if (key.name === 'x' || key.name === 'escape') {
56
+ // X or ESC key - exit CLI with confirmation
57
+ await confirmAndExit();
58
+ } else if (key.name === 'left') {
59
+ const current = tree.items[tree.selected];
60
+ if (!current) return { inTree: true }; // Safety check
61
+
62
+ if (tree.expanded[current.key]) {
63
+ // Collapse expanded section
64
+ tree.expanded[current.key] = false;
65
+ await buildTree();
66
+ } else if (current.level > 0) {
67
+ // Go to parent
68
+ for (let i = tree.selected - 1; i >= 0; i--) {
69
+ if (tree.items[i].level < current.level) {
70
+ tree.selected = i;
71
+ break;
72
+ }
73
+ }
74
+ } else {
75
+ // At root level, go back to main menu
76
+ return { inTree: false };
77
+ }
78
+ } else if (key.name === 'k' || key.name === 'up') {
79
+ tree.selected = Math.max(0, tree.selected - 1);
80
+ await buildTree();
81
+ } else if (key.name === 'j' || key.name === 'down') {
82
+ tree.selected = Math.min(tree.items.length - 1, tree.selected + 1);
83
+ await buildTree();
84
+ } else if (key.name === 'right' || key.name === 'return' || key.name === 'space') {
85
+ await handleSelectAction(tree, loaders, buildTree);
86
+ } else if (key.name === 'f') {
87
+ // Feedback button ( megaphone 📣 )
88
+ await handleFeedbackSubmission();
89
+ await buildTree();
90
+ } else if (key.name === 'r') {
91
+ await handleRecycleAction(tree, loaders, buildTree);
92
+ } else if (key.name === 'u') {
93
+ const current = tree.items[tree.selected];
94
+ if (!current) return { inTree: true }; // Safety check
95
+
96
+ if (current.type === 'requirement') {
97
+ await promoteRequirement(current.req, current.sectionKey, tree, loadSection, loadVerified);
98
+ await buildTree();
99
+ }
100
+ } else if (key.name === 'd') {
101
+ await handleDemoteAction(tree, loaders, buildTree);
102
+ }
103
+
104
+ return { inTree: true };
105
+ }
106
+
107
+ /**
108
+ * Handles select/enter action on tree items
109
+ */
110
+ async function handleSelectAction(tree, loaders, buildTree) {
111
+ const { loadSection, loadVerified, loadClarification } = loaders;
112
+ const current = tree.items[tree.selected];
113
+ if (!current) return; // Safety check
114
+
115
+ if (current.type === 'section') {
116
+ if (!tree.expanded[current.key]) {
117
+ tree.expanded[current.key] = true;
118
+ // Load requirements for this section
119
+ if (current.key === 'todo') {
120
+ tree.todoReqs = await loadSection(current.key, current.section);
121
+ } else if (current.key === 'verify') {
122
+ tree.verifyReqs = await loadSection(current.key, current.section);
123
+ } else if (current.key === 'verified') {
124
+ tree.verifiedReqs = await loadVerified();
125
+ } else if (current.key === 'recycled') {
126
+ tree.recycledReqs = await loadSection(current.key, current.section);
127
+ }
128
+ await buildTree();
129
+ } else {
130
+ tree.expanded[current.key] = false;
131
+ await buildTree();
132
+ }
133
+ } else if (current.type === 'requirement') {
134
+ // Show requirement actions
135
+ await showRequirementActions(current.req, current.sectionKey, tree);
136
+ await buildTree();
137
+ } else if (current.type === 'clarification') {
138
+ // Show clarification requirement with questions
139
+ await showClarificationActions(current.req, tree, loadClarification);
140
+ await buildTree();
141
+ } else if (current.type === 'verified') {
142
+ // Show verified item details (read-only)
143
+ await showVerifiedDetails(current);
144
+ } else if (current.type === 'add') {
145
+ // Handle add requirement
146
+ await handleAddRequirement(current.key);
147
+ // Reload TODO section
148
+ tree.todoReqs = await loadSection('todo', '⏳ Requirements not yet completed');
149
+ await buildTree();
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Shows verified requirement details
155
+ */
156
+ async function showVerifiedDetails(item) {
157
+ console.clear();
158
+ console.log(chalk.bold.green(`\n${item.label}\n`));
159
+ console.log(chalk.gray('(From CHANGELOG.md - read only)'));
160
+ console.log(chalk.gray(`\n${t('interactive.press.any.key.back')}`));
161
+ await readKeypress();
162
+ }
163
+
164
+ /**
165
+ * Handles recycle action
166
+ */
167
+ async function handleRecycleAction(tree, loaders, buildTree) {
168
+ const { loadSection, loadClarification } = loaders;
169
+ const current = tree.items[tree.selected];
170
+ if (!current) return; // Safety check
171
+
172
+ if (current.type === 'requirement') {
173
+ await deleteRequirement(current.req, current.sectionKey, tree);
174
+ // Reload the section that the requirement was deleted from
175
+ if (current.sectionKey === 'todo') {
176
+ tree.todoReqs = await loadSection('todo', '⏳ Requirements not yet completed');
177
+ } else if (current.sectionKey === 'verify') {
178
+ tree.verifyReqs = await loadSection('verify', '✅ Verified by AI screenshot');
179
+ }
180
+ await buildTree();
181
+ } else if (current.type === 'clarification') {
182
+ await deleteClarification(current.req, tree);
183
+ tree.clarificationReqs = await loadClarification();
184
+ await buildTree();
185
+ } else if (current.type === 'recycled') {
186
+ await permanentlyDeleteRequirement(current.req, current.sectionKey, tree);
187
+ tree.recycledReqs = await loadSection('recycled', '♻️ Recycled');
188
+ await buildTree();
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Handles demote action
194
+ */
195
+ async function handleDemoteAction(tree, loaders, buildTree) {
196
+ const { loadSection, loadVerified, loadClarification } = loaders;
197
+ const current = tree.items[tree.selected];
198
+ if (!current) return; // Safety check
199
+
200
+ if (current.type === 'clarification') {
201
+ // D on clarification item = Move to TODO
202
+ await moveClarificationToTodo(current.req, tree);
203
+ tree.clarificationReqs = await loadClarification();
204
+ tree.todoReqs = await loadSection('todo', '⏳ Requirements not yet completed');
205
+ await buildTree();
206
+ } else if (current.type === 'requirement' || current.type === 'verified') {
207
+ const sectionKey = current.type === 'verified' ? 'verified' : current.sectionKey;
208
+ const reqTitle = current.type === 'verified' ? current.label : current.req.title;
209
+ await demoteRequirement(reqTitle, sectionKey, tree, loadSection, loadVerified);
210
+ await buildTree();
211
+ }
212
+ }
213
+
214
+ module.exports = {
215
+ readKeypress,
216
+ handleKeypress
217
+ };
@@ -0,0 +1,188 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const { getRequirementsPath, getVibeCodingMachineDir, checkVibeCodingMachineExists } = require('vibecodingmachine-core');
4
+ const { parseRequirementsFromContent } = require('../requirements-parser');
5
+
6
+ /**
7
+ * Load requirements for a section
8
+ */
9
+ async function loadSection(sectionKey, sectionTitle) {
10
+ const reqPath = await getRequirementsPath();
11
+
12
+ if (!reqPath || !await fs.pathExists(reqPath)) {
13
+ return [];
14
+ }
15
+
16
+ const content = await fs.readFile(reqPath, 'utf8');
17
+
18
+ // Delegate to reusable parser
19
+ const allReqs = parseRequirementsFromContent(content, sectionKey, sectionTitle);
20
+
21
+ // For TODO section, only show primary heading requirements (those marked from '###' titles)
22
+ if (sectionKey === 'todo') return allReqs.filter(r => r.source === 'heading');
23
+
24
+ return allReqs;
25
+ }
26
+
27
+ /**
28
+ * Load VERIFIED requirements from CHANGELOG
29
+ */
30
+ async function loadVerified() {
31
+ const allnightStatus = await checkVibeCodingMachineExists();
32
+ let changelogPath;
33
+
34
+ if (allnightStatus.insideExists) {
35
+ const allnightDir = await getVibeCodingMachineDir();
36
+ changelogPath = path.join(path.dirname(allnightDir), 'CHANGELOG.md');
37
+ } else if (allnightStatus.siblingExists) {
38
+ changelogPath = path.join(process.cwd(), 'CHANGELOG.md');
39
+ }
40
+
41
+ if (!changelogPath || !await fs.pathExists(changelogPath)) {
42
+ return [];
43
+ }
44
+
45
+ const content = await fs.readFile(changelogPath, 'utf8');
46
+ const lines = content.split('\n');
47
+ const requirements = [];
48
+ let inVerifiedSection = false;
49
+
50
+ for (const line of lines) {
51
+ const trimmed = line.trim();
52
+
53
+ // Check for Verified Requirements section
54
+ if (trimmed.includes('## Verified Requirements')) {
55
+ inVerifiedSection = true;
56
+ continue;
57
+ }
58
+
59
+ // Exit section if we hit another ## header
60
+ if (inVerifiedSection && trimmed.startsWith('##') && !trimmed.includes('Verified Requirements')) {
61
+ break;
62
+ }
63
+
64
+ // Only collect items from within the Verified Requirements section
65
+ if (inVerifiedSection && trimmed.startsWith('- ') && trimmed.length > 10) {
66
+ requirements.push(trimmed.substring(2));
67
+ }
68
+ }
69
+
70
+ return requirements;
71
+ }
72
+
73
+ /**
74
+ * Load clarification requirements with questions
75
+ */
76
+ async function loadClarification() {
77
+ const reqPath = await getRequirementsPath();
78
+
79
+ if (!reqPath || !await fs.pathExists(reqPath)) {
80
+ return [];
81
+ }
82
+
83
+ const content = await fs.readFile(reqPath, 'utf8');
84
+ const lines = content.split('\n');
85
+
86
+ let inSection = false;
87
+ const requirements = [];
88
+
89
+ for (let i = 0; i < lines.length; i++) {
90
+ const line = lines[i];
91
+ const trimmed = line.trim();
92
+
93
+ // Check if we're entering the clarification section
94
+ if (trimmed.includes('❓ Requirements needing manual feedback')) {
95
+ inSection = true;
96
+ continue;
97
+ }
98
+
99
+ // Check if we're leaving the section (hit another ## section)
100
+ if (inSection && trimmed.startsWith('##') && !trimmed.startsWith('###')) {
101
+ break;
102
+ }
103
+
104
+ // Read requirements in new format (### header)
105
+ if (inSection && trimmed.startsWith('###')) {
106
+ const title = trimmed.replace(/^###\s*/, '').trim();
107
+
108
+ // Skip empty titles
109
+ if (!title || title.length === 0) {
110
+ continue;
111
+ }
112
+
113
+ const details = [];
114
+ const questions = [];
115
+ let pkg = null;
116
+ let findings = null;
117
+ let currentQuestion = null;
118
+
119
+ // Read package, description, and clarifying questions
120
+ for (let j = i + 1; j < lines.length; j++) {
121
+ const nextLine = lines[j].trim();
122
+
123
+ // Stop if we hit another requirement or section
124
+ if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
125
+ break;
126
+ }
127
+
128
+ // Check for PACKAGE line
129
+ if (nextLine.startsWith('PACKAGE:')) {
130
+ pkg = nextLine.replace(/^PACKAGE:\s*/, '').trim();
131
+ }
132
+ // Check for AI findings
133
+ else if (nextLine.startsWith('**AI found in codebase:**')) {
134
+ // Next line will be the findings
135
+ continue;
136
+ }
137
+ else if (nextLine.startsWith('**What went wrong')) {
138
+ // Description line
139
+ details.push(nextLine);
140
+ }
141
+ else if (nextLine.startsWith('**Clarifying questions:**')) {
142
+ // Start of questions section
143
+ continue;
144
+ }
145
+ else if (nextLine.match(/^\d+\./)) {
146
+ // Save previous question if exists
147
+ if (currentQuestion) {
148
+ questions.push(currentQuestion);
149
+ }
150
+ // This is a new question
151
+ currentQuestion = { question: nextLine, response: null };
152
+ }
153
+ else if (currentQuestion && nextLine && !nextLine.startsWith('PACKAGE:') && !nextLine.startsWith('**')) {
154
+ // This might be a response to the current question or description/findings
155
+ if (!findings && !currentQuestion.response && questions.length === 0 && !nextLine.match(/^\d+\./)) {
156
+ // This is findings content
157
+ findings = nextLine;
158
+ } else if (currentQuestion && !currentQuestion.response) {
159
+ // This is a response to the current question
160
+ currentQuestion.response = nextLine;
161
+ } else {
162
+ // Description line
163
+ details.push(nextLine);
164
+ }
165
+ }
166
+ else if (nextLine && !nextLine.startsWith('PACKAGE:') && !nextLine.startsWith('**')) {
167
+ // Description line
168
+ details.push(nextLine);
169
+ }
170
+ }
171
+
172
+ // Save last question if exists
173
+ if (currentQuestion) {
174
+ questions.push(currentQuestion);
175
+ }
176
+
177
+ requirements.push({ title, details, pkg, questions, findings, lineIndex: i });
178
+ }
179
+ }
180
+
181
+ return requirements;
182
+ }
183
+
184
+ module.exports = {
185
+ loadSection,
186
+ loadVerified,
187
+ loadClarification
188
+ };