vibecodingmachine-cli 2026.2.20-438 → 2026.2.26-1739

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 (101) hide show
  1. package/bin/auth/auth-compliance.js +126 -0
  2. package/bin/cli-program.js +104 -0
  3. package/bin/cli-setup.js +52 -0
  4. package/bin/commands/agent-commands.js +310 -0
  5. package/bin/commands/auto-commands.js +70 -0
  6. package/bin/commands/command-aliases.js +118 -0
  7. package/bin/commands/repo-commands.js +39 -0
  8. package/bin/commands/rui-commands.js +152 -0
  9. package/bin/config/cli-config.js +394 -0
  10. package/bin/init/environment-setup.js +84 -0
  11. package/bin/update/update-checker.js +126 -0
  12. package/bin/vibecodingmachine-new.js +50 -0
  13. package/bin/vibecodingmachine.js +29 -663
  14. package/package.json +8 -2
  15. package/src/commands/agents/add.js +277 -0
  16. package/src/commands/agents/check.js +380 -0
  17. package/src/commands/agents/list.js +471 -0
  18. package/src/commands/agents/remove.js +351 -0
  19. package/src/commands/analyze-file-sizes.js +428 -0
  20. package/src/commands/auto-direct/code-processor.js +282 -0
  21. package/src/commands/auto-direct/file-scanner.js +266 -0
  22. package/src/commands/auto-direct/provider-config.js +178 -0
  23. package/src/commands/auto-direct/provider-manager.js +219 -0
  24. package/src/commands/auto-direct/requirement-manager.js +172 -0
  25. package/src/commands/auto-direct/status-display.js +91 -0
  26. package/src/commands/auto-direct/utils.js +106 -0
  27. package/src/commands/auto-direct.js +875 -488
  28. package/src/commands/auto-execution.js +342 -0
  29. package/src/commands/auto-provider-management.js +102 -0
  30. package/src/commands/auto-requirement-management.js +161 -0
  31. package/src/commands/auto-status-helpers.js +141 -0
  32. package/src/commands/auto.js +105 -5155
  33. package/src/commands/check-compliance.js +536 -0
  34. package/src/commands/continuous-scan.js +119 -0
  35. package/src/commands/ide.js +16 -4
  36. package/src/commands/refactor-file.js +486 -0
  37. package/src/commands/requirements.js +301 -2
  38. package/src/commands/timeout.js +290 -0
  39. package/src/trui/TruiInterface.js +108 -0
  40. package/src/trui/agents/AgentInterface.js +580 -0
  41. package/src/utils/antigravity-installer.js +60 -6
  42. package/src/utils/clarification-actions.js +290 -0
  43. package/src/utils/config.js +123 -2
  44. package/src/utils/first-run.js +5 -5
  45. package/src/utils/ide-handlers.js +212 -0
  46. package/src/utils/interactive/clarification-actions.js +348 -0
  47. package/src/utils/interactive/core-ui.js +265 -0
  48. package/src/utils/interactive/file-backup.js +237 -0
  49. package/src/utils/interactive/file-import-export.js +305 -0
  50. package/src/utils/interactive/file-operations.js +49 -0
  51. package/src/utils/interactive/file-validation.js +276 -0
  52. package/src/utils/interactive/interactive-prompts.js +480 -0
  53. package/src/utils/interactive/requirement-actions.js +127 -0
  54. package/src/utils/interactive/requirement-crud.js +356 -0
  55. package/src/utils/interactive/requirements-navigation.js +286 -0
  56. package/src/utils/interactive.js +390 -3459
  57. package/src/utils/provider-checker/agent-checker.js +250 -0
  58. package/src/utils/provider-checker/agent-runner.js +450 -0
  59. package/src/utils/provider-checker/cli-installer.js +123 -0
  60. package/src/utils/provider-checker/cli-utils.js +15 -0
  61. package/src/utils/provider-checker/format-utils.js +32 -0
  62. package/src/utils/provider-checker/ide-manager.js +72 -0
  63. package/src/utils/provider-checker/ide-utils.js +71 -0
  64. package/src/utils/provider-checker/node-detector.js +56 -0
  65. package/src/utils/provider-checker/node-utils.js +61 -0
  66. package/src/utils/provider-checker/process-spawn.js +22 -0
  67. package/src/utils/provider-checker/process-utils.js +37 -0
  68. package/src/utils/provider-checker/provider-validator.js +160 -0
  69. package/src/utils/provider-checker/quota-checker.js +54 -0
  70. package/src/utils/provider-checker/quota-detector.js +44 -0
  71. package/src/utils/provider-checker/requirements-manager.js +94 -0
  72. package/src/utils/provider-checker/test-requirements.js +95 -0
  73. package/src/utils/provider-checker/time-formatter.js +18 -0
  74. package/src/utils/provider-checker-new.js +14 -0
  75. package/src/utils/provider-checker.js +12 -407
  76. package/src/utils/provider-checkers/ide-manager.js +128 -0
  77. package/src/utils/provider-checkers/node-executable-finder.js +51 -0
  78. package/src/utils/provider-checkers/provider-checker-core.js +172 -0
  79. package/src/utils/provider-checkers/provider-checker-main.js +107 -0
  80. package/src/utils/provider-manager.js +60 -4
  81. package/src/utils/provider-registry.js +26 -3
  82. package/src/utils/provider-utils.js +173 -0
  83. package/src/utils/quota-detectors.js +212 -0
  84. package/src/utils/requirement-action-handlers.js +288 -0
  85. package/src/utils/requirement-actions/clarification-actions.js +229 -0
  86. package/src/utils/requirement-actions/confirmation-prompts.js +93 -0
  87. package/src/utils/requirement-actions/file-operations.js +92 -0
  88. package/src/utils/requirement-actions/helpers.js +40 -0
  89. package/src/utils/requirement-actions/requirement-operations.js +335 -0
  90. package/src/utils/requirement-actions.js +46 -856
  91. package/src/utils/requirement-file-operations.js +259 -0
  92. package/src/utils/requirement-helpers.js +128 -0
  93. package/src/utils/requirement-management.js +279 -0
  94. package/src/utils/requirement-navigation.js +146 -0
  95. package/src/utils/requirement-organization.js +271 -0
  96. package/src/utils/simple-trui.js +75 -1
  97. package/src/utils/trui-navigation.js +28 -2
  98. package/src/utils/trui-req-tree.js +196 -11
  99. package/src/utils/trui-specifications.js +31 -1
  100. package/src/utils/interactive-backup.js +0 -5664
  101. package/src/utils/trui-provider-manager.js +0 -182
@@ -0,0 +1,212 @@
1
+ /**
2
+ * IDE Handlers Module
3
+ *
4
+ * Handles IDE-specific operations like checking if IDEs are running, launching IDEs, and IDE installation.
5
+ */
6
+
7
+ const { execSync } = require('child_process');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ /**
12
+ * IDE information mapping
13
+ */
14
+ const IDE_INFO = {
15
+ windsurf: { process: 'Windsurf', app: 'Windsurf' },
16
+ cursor: { process: 'Cursor', app: 'Cursor' },
17
+ antigravity: { process: 'Antigravity', app: 'Antigravity' },
18
+ kiro: { process: 'Kiro', app: 'Kiro' },
19
+ 'github-copilot': { process: 'Code', app: 'Visual Studio Code' },
20
+ 'amazon-q': { process: 'Code', app: 'Visual Studio Code' },
21
+ cline: { process: 'Code', app: 'Visual Studio Code' },
22
+ vscode: { process: 'Code', app: 'Visual Studio Code' },
23
+ replit: { process: null, app: null }, // web-based
24
+ };
25
+
26
+ /**
27
+ * Returns true if the IDE process is currently running.
28
+ */
29
+ function isIDERunning(processName) {
30
+ if (!processName) return false;
31
+
32
+ if (process.platform === 'darwin') {
33
+ // macOS: use pgrep
34
+ try {
35
+ execSync(`pgrep -x "${processName}"`, { stdio: 'ignore' });
36
+ return true;
37
+ } catch {
38
+ return false;
39
+ }
40
+ } else if (process.platform === 'win32') {
41
+ // Windows: use tasklist
42
+ try {
43
+ const result = execSync(`tasklist /FI "IMAGENAME eq ${processName}.exe" /NH`, {
44
+ encoding: 'utf8',
45
+ stdio: 'pipe',
46
+ shell: true,
47
+ windowsHide: true
48
+ });
49
+ return result.toLowerCase().includes(processName.toLowerCase());
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ // Other platforms: assume not running
56
+ return false;
57
+ }
58
+
59
+ /**
60
+ * Open the IDE using `open -a <app> <repoPath>` (macOS).
61
+ * Returns true when the launch command was sent.
62
+ */
63
+ async function openIDEApp(appName, repoPath) {
64
+ if (!appName || process.platform !== 'darwin') return false;
65
+ try {
66
+ const safeRepo = repoPath ? ` "${repoPath.replace(/"/g, '\\"')}"` : '';
67
+ execSync(`open -a "${appName}"${safeRepo}`, { stdio: 'ignore' });
68
+ // Give it a few seconds to start before the automation begins
69
+ await new Promise(r => setTimeout(r, 5000));
70
+ return true;
71
+ } catch {
72
+ return false;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Check IDE installation and launch if needed
78
+ */
79
+ async function checkAndLaunchIDE(providerId, def, repoPath) {
80
+ const info = IDE_INFO[providerId] || IDE_INFO[def.ide] || null;
81
+ if (!info || !info.process) {
82
+ return { success: true, note: '' }; // Non-IDE provider
83
+ }
84
+
85
+ let ideLaunchNote = '';
86
+
87
+ if (!isIDERunning(info.process)) {
88
+ // macOS: launch the app if not running
89
+ if (process.platform === 'darwin' && info.app) {
90
+ const launchSuccess = await openIDEApp(info.app, repoPath);
91
+ if (launchSuccess) {
92
+ ideLaunchNote = ` (VCM launched ${info.app})`;
93
+ } else {
94
+ // Failed to launch on macOS - check if installed
95
+ const { IDEInstaller } = require('../../../electron-app/src/main/ide-installer');
96
+ const installer = new IDEInstaller();
97
+ const ideKey = def.ide || providerId;
98
+ const checkResult = await installer.checkIDEInstallation(ideKey);
99
+ if (!checkResult.installed) {
100
+ return {
101
+ success: false,
102
+ error: `${info.app} is not installed. Please download from ${IDEInstaller.IDE_CONFIG[ideKey]?.downloadBrowserUrl || 'its official website'}`
103
+ };
104
+ }
105
+ }
106
+ }
107
+ // Windows: check if installed, install if possible, launch if installed
108
+ else if (process.platform === 'win32') {
109
+ const { IDEInstaller } = require('../../../electron-app/src/main/ide-installer');
110
+ const installer = new IDEInstaller();
111
+ const ideKey = def.ide || providerId;
112
+ const ideConfig = IDEInstaller.IDE_CONFIG[ideKey];
113
+
114
+ // Check if IDE is installed
115
+ let checkResult = await installer.checkIDEInstallation(ideKey);
116
+
117
+ if (!checkResult.installed) {
118
+ // IDE is not installed - try to install if we have a download URL
119
+ const downloadUrl = ideConfig?.downloadUrl?.[process.platform];
120
+
121
+ if (downloadUrl) {
122
+ console.log(`[AGENT CHECK] Installing ${ideKey}...`);
123
+ try {
124
+ const installResult = await installer.installIDE(ideKey);
125
+ if (installResult.success) {
126
+ ideLaunchNote = ` (VCM installed ${ideKey})`;
127
+ // Launch after installation
128
+ await new Promise(resolve => setTimeout(resolve, 2000));
129
+ const launchSuccess = await installer.launchIDE(ideKey, repoPath);
130
+ if (launchSuccess) {
131
+ ideLaunchNote += ` (VCM launched ${ideKey})`;
132
+ }
133
+ } else {
134
+ return {
135
+ success: false,
136
+ error: `Failed to install ${ideKey}: ${installResult.error}`
137
+ };
138
+ }
139
+ } catch (error) {
140
+ return {
141
+ success: false,
142
+ error: `Error installing ${ideKey}: ${error.message}`
143
+ };
144
+ }
145
+ } else {
146
+ return {
147
+ success: false,
148
+ error: `${ideKey} is not installed and no automatic installation is available`
149
+ };
150
+ }
151
+ } else {
152
+ // IDE is installed, try to launch it
153
+ const launchSuccess = await installer.launchIDE(ideKey, repoPath);
154
+ if (launchSuccess) {
155
+ ideLaunchNote = ` (VCM launched ${ideKey})`;
156
+ }
157
+ }
158
+ }
159
+ // Linux: check if installed and try to launch
160
+ else {
161
+ const { IDEInstaller } = require('../../../electron-app/src/main/ide-installer');
162
+ const installer = new IDEInstaller();
163
+ const ideKey = def.ide || providerId;
164
+
165
+ try {
166
+ const checkResult = await installer.checkIDEInstallation(ideKey);
167
+ if (!checkResult.installed) {
168
+ return {
169
+ success: false,
170
+ error: `${ideKey} is not installed`
171
+ };
172
+ }
173
+
174
+ const launchSuccess = await installer.launchIDE(ideKey, repoPath);
175
+ if (launchSuccess) {
176
+ ideLaunchNote = ` (VCM launched ${ideKey})`;
177
+ }
178
+ } catch (error) {
179
+ return {
180
+ success: false,
181
+ error: `Error checking/launching ${ideKey}: ${error.message}`
182
+ };
183
+ }
184
+ }
185
+ }
186
+
187
+ return { success: true, note: ideLaunchNote };
188
+ }
189
+
190
+ /**
191
+ * Get IDE information for a provider
192
+ */
193
+ function getIDEInfo(providerId, def) {
194
+ return IDE_INFO[providerId] || IDE_INFO[def.ide] || null;
195
+ }
196
+
197
+ /**
198
+ * Check if a provider is IDE-based
199
+ */
200
+ function isIDEProvider(providerId, def) {
201
+ const info = getIDEInfo(providerId, def);
202
+ return info && info.process !== null;
203
+ }
204
+
205
+ module.exports = {
206
+ IDE_INFO,
207
+ isIDERunning,
208
+ openIDEApp,
209
+ checkAndLaunchIDE,
210
+ getIDEInfo,
211
+ isIDEProvider
212
+ };
@@ -0,0 +1,348 @@
1
+ const { t, getRequirementsPath } = require('vibecodingmachine-core');
2
+ const chalk = require('chalk');
3
+ const inquirer = require('inquirer');
4
+ const fs = require('fs-extra');
5
+
6
+ // Helper to edit clarification responses
7
+ async function editClarificationResponses(req, tree) {
8
+ const reqPath = await getRequirementsPath();
9
+ const content = await fs.readFile(reqPath, 'utf8');
10
+ const lines = content.split('\n');
11
+
12
+ // Find the requirement line
13
+ const reqIndex = lines.findIndex(line => line.includes(req.title));
14
+ if (reqIndex === -1) {
15
+ console.error(chalk.red('❌ Requirement not found'));
16
+ return;
17
+ }
18
+
19
+ // Extract existing responses
20
+ const responses = [];
21
+ let i = reqIndex + 1;
22
+ while (i < lines.length && lines[i].startsWith(' - ')) {
23
+ responses.push(lines[i].substring(4).trim());
24
+ i++;
25
+ }
26
+
27
+ console.log(chalk.blue(`\n📝 Editing responses for: ${req.title}\n`));
28
+
29
+ if (responses.length > 0) {
30
+ console.log(chalk.yellow('Current responses:'));
31
+ responses.forEach((resp, idx) => console.log(chalk.gray(` ${idx + 1}. ${resp}`)));
32
+ console.log();
33
+ }
34
+
35
+ const { action } = await inquirer.prompt([
36
+ {
37
+ type: 'list',
38
+ name: 'action',
39
+ message: 'What would you like to do?',
40
+ choices: [
41
+ { name: '➕ Add response', value: 'add' },
42
+ ...(responses.length > 0 ? [{ name: '✏️ Edit response', value: 'edit' }] : []),
43
+ ...(responses.length > 0 ? [{ name: '🗑️ Delete response', value: 'delete' }] : []),
44
+ { name: '⬅️ Back', value: 'back' }
45
+ ]
46
+ }
47
+ ]);
48
+
49
+ if (action === 'back') return;
50
+
51
+ switch (action) {
52
+ case 'add':
53
+ await addClarificationResponse(reqPath, reqIndex, lines, responses);
54
+ break;
55
+ case 'edit':
56
+ await editClarificationResponse(reqPath, reqIndex, lines, responses);
57
+ break;
58
+ case 'delete':
59
+ await deleteClarificationResponse(reqPath, reqIndex, lines, responses);
60
+ break;
61
+ }
62
+ }
63
+
64
+ async function addClarificationResponse(reqPath, reqIndex, lines, responses) {
65
+ const { response } = await inquirer.prompt([
66
+ {
67
+ type: 'input',
68
+ name: 'response',
69
+ message: 'Enter new response:',
70
+ validate: input => input.trim().length > 0 || 'Response cannot be empty'
71
+ }
72
+ ]);
73
+
74
+ // Insert the new response after the requirement
75
+ const insertIndex = reqIndex + 1 + responses.length;
76
+ lines.splice(insertIndex, 0, ` - ${response}`);
77
+
78
+ await fs.writeFile(reqPath, lines.join('\n'));
79
+ console.log(chalk.green('✅ Response added'));
80
+ await new Promise(resolve => setTimeout(resolve, 1000));
81
+ }
82
+
83
+ async function editClarificationResponse(reqPath, reqIndex, lines, responses) {
84
+ const { responseIndex, newResponse } = await inquirer.prompt([
85
+ {
86
+ type: 'list',
87
+ name: 'responseIndex',
88
+ message: 'Select response to edit:',
89
+ choices: responses.map((resp, idx) => ({ name: resp, value: idx }))
90
+ },
91
+ {
92
+ type: 'input',
93
+ name: 'newResponse',
94
+ message: 'Enter new response:',
95
+ default: responses[0],
96
+ validate: input => input.trim().length > 0 || 'Response cannot be empty'
97
+ }
98
+ ]);
99
+
100
+ const lineIndex = reqIndex + 1 + responseIndex;
101
+ lines[lineIndex] = ` - ${newResponse}`;
102
+
103
+ await fs.writeFile(reqPath, lines.join('\n'));
104
+ console.log(chalk.green('✅ Response updated'));
105
+ await new Promise(resolve => setTimeout(resolve, 1000));
106
+ }
107
+
108
+ async function deleteClarificationResponse(reqPath, reqIndex, lines, responses) {
109
+ const { responseIndex } = await inquirer.prompt([
110
+ {
111
+ type: 'list',
112
+ name: 'responseIndex',
113
+ message: 'Select response to delete:',
114
+ choices: responses.map((resp, idx) => ({ name: resp, value: idx }))
115
+ }
116
+ ]);
117
+
118
+ const confirmed = await confirmAction('Delete this response? (r/y/N):');
119
+ if (!confirmed) return;
120
+
121
+ const lineIndex = reqIndex + 1 + responseIndex;
122
+ lines.splice(lineIndex, 1);
123
+
124
+ await fs.writeFile(reqPath, lines.join('\n'));
125
+ console.log(chalk.green('✅ Response deleted'));
126
+ await new Promise(resolve => setTimeout(resolve, 1000));
127
+ }
128
+
129
+ // Helper to move clarification requirement back to TODO
130
+ async function moveClarificationToTodo(req, tree) {
131
+ const reqPath = await getRequirementsPath();
132
+ const content = await fs.readFile(reqPath, 'utf8');
133
+ const lines = content.split('\n');
134
+
135
+ // Find the requirement in clarification section
136
+ const reqIndex = lines.findIndex(line => line.includes(req.title));
137
+ if (reqIndex === -1) {
138
+ console.error(chalk.red('❌ Requirement not found'));
139
+ return;
140
+ }
141
+
142
+ // Extract the requirement and its responses
143
+ const requirementLines = [lines[reqIndex]];
144
+ let i = reqIndex + 1;
145
+ while (i < lines.length && lines[i].startsWith(' - ')) {
146
+ requirementLines.push(lines[i]);
147
+ i++;
148
+ }
149
+
150
+ // Remove from clarification section
151
+ lines.splice(reqIndex, requirementLines.length);
152
+
153
+ // Add to TODO section
154
+ const todoIndex = lines.findIndex(line => line.includes('Requirements not yet completed'));
155
+ if (todoIndex !== -1) {
156
+ // Find the end of the TODO section header
157
+ let insertIndex = todoIndex + 1;
158
+ while (insertIndex < lines.length && lines[insertIndex].trim() === '') {
159
+ insertIndex++;
160
+ }
161
+
162
+ // Insert the requirement
163
+ lines.splice(insertIndex, 0, ...requirementLines);
164
+ }
165
+
166
+ await fs.writeFile(reqPath, lines.join('\n'));
167
+ console.log(chalk.green('✅ Requirement moved back to TODO'));
168
+ await new Promise(resolve => setTimeout(resolve, 1000));
169
+ }
170
+
171
+ // Helper to move clarification requirement to recycled (used to delete)
172
+ async function deleteClarification(req, tree) {
173
+ const confirmed = await confirmAction('Delete this requirement? (r/y/N):');
174
+ if (!confirmed) return;
175
+
176
+ const reqPath = await getRequirementsPath();
177
+ const content = await fs.readFile(reqPath, 'utf8');
178
+ const lines = content.split('\n');
179
+
180
+ // Find the requirement in clarification section
181
+ const reqIndex = lines.findIndex(line => line.includes(req.title));
182
+ if (reqIndex === -1) {
183
+ console.error(chalk.red('❌ Requirement not found'));
184
+ return;
185
+ }
186
+
187
+ // Extract the requirement and its responses
188
+ const requirementLines = [lines[reqIndex]];
189
+ let i = reqIndex + 1;
190
+ while (i < lines.length && lines[i].startsWith(' - ')) {
191
+ requirementLines.push(lines[i]);
192
+ i++;
193
+ }
194
+
195
+ // Remove from clarification section
196
+ lines.splice(reqIndex, requirementLines.length);
197
+
198
+ // Add to recycled section
199
+ await moveRequirementToRecycled(reqPath, req.title, 'clarification', requirementLines);
200
+
201
+ await fs.writeFile(reqPath, lines.join('\n'));
202
+ console.log(chalk.green('✅ Requirement deleted'));
203
+ await new Promise(resolve => setTimeout(resolve, 1000));
204
+ }
205
+
206
+ // Helper to show actions for a requirement
207
+ async function showClarificationActions(req, tree, loadClarification) {
208
+ const actions = [
209
+ { label: '✍️ Add/Edit Responses', value: 'edit-responses' },
210
+ { label: '↩️ Move back to TODO (after clarification)', value: 'move-to-todo' },
211
+ { label: '🗑️ Delete', value: 'delete' },
212
+ { label: '⬅️ Back', value: 'back' }
213
+ ];
214
+
215
+ const { action } = await inquirer.prompt([
216
+ {
217
+ type: 'list',
218
+ name: 'action',
219
+ message: 'What would you like to do?',
220
+ choices: actions
221
+ }
222
+ ]);
223
+
224
+ switch (action) {
225
+ case 'edit-responses':
226
+ await editClarificationResponses(req, tree);
227
+ break;
228
+ case 'move-to-todo':
229
+ await moveClarificationToTodo(req, tree);
230
+ break;
231
+ case 'delete':
232
+ await deleteClarification(req, tree);
233
+ break;
234
+ }
235
+ }
236
+
237
+ // Helper to confirm exit and exit if confirmed (default N)
238
+ async function confirmAndExit() {
239
+ console.log(chalk.gray('\n[DEBUG] confirmAndExit called'));
240
+ console.log();
241
+ process.stdout.write(chalk.yellow(`${t('interactive.confirm.exit')} `));
242
+
243
+ return new Promise((resolve) => {
244
+ process.stdin.setRawMode(true);
245
+ process.stdin.resume();
246
+ process.stdin.setEncoding('utf8');
247
+
248
+ let data = '';
249
+ process.stdin.on('data', (key) => {
250
+ data += key;
251
+
252
+ // Accept 'r' or 'y' for yes, anything else for no
253
+ if (key.toLowerCase() === 'r' || key.toLowerCase() === 'y') {
254
+ process.stdout.write(key);
255
+ process.stdout.write('\n');
256
+ process.stdin.setRawMode(false);
257
+ process.stdin.pause();
258
+ console.log(chalk.cyan('\n👋 Goodbye!\n'));
259
+ process.exit(0);
260
+ } else if (key === '\r' || key === '\n' || key === '\u0003') { // Enter, Esc, or Ctrl+C
261
+ process.stdout.write('N\n');
262
+ process.stdin.setRawMode(false);
263
+ process.stdin.pause();
264
+ resolve(false);
265
+ } else if (data.length > 1) { // Any other input
266
+ process.stdout.write('N\n');
267
+ process.stdin.setRawMode(false);
268
+ process.stdin.pause();
269
+ resolve(false);
270
+ }
271
+ });
272
+ });
273
+ }
274
+
275
+ // Helper to show confirmation prompt (r/y for yes, N for no, default N)
276
+ async function confirmAction(message) {
277
+ console.log();
278
+ process.stdout.write(chalk.yellow(`${message} `));
279
+
280
+ return new Promise((resolve) => {
281
+ process.stdin.setRawMode(true);
282
+ process.stdin.resume();
283
+ process.stdin.setEncoding('utf8');
284
+
285
+ let data = '';
286
+ process.stdin.on('data', (key) => {
287
+ data += key;
288
+
289
+ // Accept 'r' or 'y' for yes, anything else for no
290
+ if (key.toLowerCase() === 'r' || key.toLowerCase() === 'y') {
291
+ process.stdout.write(key);
292
+ process.stdout.write('\n');
293
+ process.stdin.setRawMode(false);
294
+ process.stdin.pause();
295
+ resolve(true);
296
+ } else if (key === '\r' || key === '\n' || key === '\u0003') { // Enter, Esc, or Ctrl+C
297
+ process.stdout.write('N\n');
298
+ process.stdin.setRawMode(false);
299
+ process.stdin.pause();
300
+ resolve(false);
301
+ } else if (data.length > 1) { // Any other input
302
+ process.stdout.write('N\n');
303
+ process.stdin.setRawMode(false);
304
+ process.stdin.pause();
305
+ resolve(false);
306
+ }
307
+ });
308
+ });
309
+ }
310
+
311
+ // Helper to move requirement to recycled section
312
+ async function moveRequirementToRecycled(reqPath, requirementTitle, fromSection, requirementLines = null) {
313
+ const content = await fs.readFile(reqPath, 'utf8');
314
+ const lines = content.split('\n');
315
+
316
+ // Find recycled section
317
+ const recycledIndex = lines.findIndex(line => line.includes('♻️ Recycled'));
318
+ if (recycledIndex === -1) {
319
+ // Create recycled section
320
+ lines.push('## ♻️ Recycled');
321
+ lines.push('');
322
+ lines.push(`- ${requirementTitle}`);
323
+ } else {
324
+ // Add to existing recycled section
325
+ let insertIndex = recycledIndex + 1;
326
+ while (insertIndex < lines.length && lines[insertIndex].trim() === '') {
327
+ insertIndex++;
328
+ }
329
+
330
+ if (requirementLines) {
331
+ lines.splice(insertIndex, 0, ...requirementLines);
332
+ } else {
333
+ lines.splice(insertIndex, 0, `- ${requirementTitle}`);
334
+ }
335
+ }
336
+
337
+ await fs.writeFile(reqPath, lines.join('\n'));
338
+ }
339
+
340
+ module.exports = {
341
+ editClarificationResponses,
342
+ moveClarificationToTodo,
343
+ deleteClarification,
344
+ showClarificationActions,
345
+ confirmAndExit,
346
+ confirmAction,
347
+ moveRequirementToRecycled
348
+ };