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.
- package/bin/auth/auth-compliance.js +126 -0
- package/bin/cli-program.js +104 -0
- package/bin/cli-setup.js +52 -0
- package/bin/commands/agent-commands.js +310 -0
- package/bin/commands/auto-commands.js +70 -0
- package/bin/commands/command-aliases.js +118 -0
- package/bin/commands/repo-commands.js +39 -0
- package/bin/commands/rui-commands.js +152 -0
- package/bin/config/cli-config.js +394 -0
- package/bin/init/environment-setup.js +84 -0
- package/bin/update/update-checker.js +126 -0
- package/bin/vibecodingmachine-new.js +50 -0
- package/bin/vibecodingmachine.js +29 -663
- package/package.json +8 -2
- package/src/commands/agents/add.js +277 -0
- package/src/commands/agents/check.js +380 -0
- package/src/commands/agents/list.js +471 -0
- package/src/commands/agents/remove.js +351 -0
- package/src/commands/analyze-file-sizes.js +428 -0
- package/src/commands/auto-direct/code-processor.js +282 -0
- package/src/commands/auto-direct/file-scanner.js +266 -0
- package/src/commands/auto-direct/provider-config.js +178 -0
- package/src/commands/auto-direct/provider-manager.js +219 -0
- package/src/commands/auto-direct/requirement-manager.js +172 -0
- package/src/commands/auto-direct/status-display.js +91 -0
- package/src/commands/auto-direct/utils.js +106 -0
- package/src/commands/auto-direct.js +875 -488
- package/src/commands/auto-execution.js +342 -0
- package/src/commands/auto-provider-management.js +102 -0
- package/src/commands/auto-requirement-management.js +161 -0
- package/src/commands/auto-status-helpers.js +141 -0
- package/src/commands/auto.js +105 -5155
- package/src/commands/check-compliance.js +536 -0
- package/src/commands/continuous-scan.js +119 -0
- package/src/commands/ide.js +16 -4
- package/src/commands/refactor-file.js +486 -0
- package/src/commands/requirements.js +301 -2
- package/src/commands/timeout.js +290 -0
- package/src/trui/TruiInterface.js +108 -0
- package/src/trui/agents/AgentInterface.js +580 -0
- package/src/utils/antigravity-installer.js +60 -6
- package/src/utils/clarification-actions.js +290 -0
- package/src/utils/config.js +123 -2
- package/src/utils/first-run.js +5 -5
- package/src/utils/ide-handlers.js +212 -0
- package/src/utils/interactive/clarification-actions.js +348 -0
- package/src/utils/interactive/core-ui.js +265 -0
- package/src/utils/interactive/file-backup.js +237 -0
- package/src/utils/interactive/file-import-export.js +305 -0
- package/src/utils/interactive/file-operations.js +49 -0
- package/src/utils/interactive/file-validation.js +276 -0
- package/src/utils/interactive/interactive-prompts.js +480 -0
- package/src/utils/interactive/requirement-actions.js +127 -0
- package/src/utils/interactive/requirement-crud.js +356 -0
- package/src/utils/interactive/requirements-navigation.js +286 -0
- package/src/utils/interactive.js +390 -3459
- package/src/utils/provider-checker/agent-checker.js +250 -0
- package/src/utils/provider-checker/agent-runner.js +450 -0
- package/src/utils/provider-checker/cli-installer.js +123 -0
- package/src/utils/provider-checker/cli-utils.js +15 -0
- package/src/utils/provider-checker/format-utils.js +32 -0
- package/src/utils/provider-checker/ide-manager.js +72 -0
- package/src/utils/provider-checker/ide-utils.js +71 -0
- package/src/utils/provider-checker/node-detector.js +56 -0
- package/src/utils/provider-checker/node-utils.js +61 -0
- package/src/utils/provider-checker/process-spawn.js +22 -0
- package/src/utils/provider-checker/process-utils.js +37 -0
- package/src/utils/provider-checker/provider-validator.js +160 -0
- package/src/utils/provider-checker/quota-checker.js +54 -0
- package/src/utils/provider-checker/quota-detector.js +44 -0
- package/src/utils/provider-checker/requirements-manager.js +94 -0
- package/src/utils/provider-checker/test-requirements.js +95 -0
- package/src/utils/provider-checker/time-formatter.js +18 -0
- package/src/utils/provider-checker-new.js +14 -0
- package/src/utils/provider-checker.js +12 -407
- package/src/utils/provider-checkers/ide-manager.js +128 -0
- package/src/utils/provider-checkers/node-executable-finder.js +51 -0
- package/src/utils/provider-checkers/provider-checker-core.js +172 -0
- package/src/utils/provider-checkers/provider-checker-main.js +107 -0
- package/src/utils/provider-manager.js +60 -4
- package/src/utils/provider-registry.js +26 -3
- package/src/utils/provider-utils.js +173 -0
- package/src/utils/quota-detectors.js +212 -0
- package/src/utils/requirement-action-handlers.js +288 -0
- package/src/utils/requirement-actions/clarification-actions.js +229 -0
- package/src/utils/requirement-actions/confirmation-prompts.js +93 -0
- package/src/utils/requirement-actions/file-operations.js +92 -0
- package/src/utils/requirement-actions/helpers.js +40 -0
- package/src/utils/requirement-actions/requirement-operations.js +335 -0
- package/src/utils/requirement-actions.js +46 -856
- package/src/utils/requirement-file-operations.js +259 -0
- package/src/utils/requirement-helpers.js +128 -0
- package/src/utils/requirement-management.js +279 -0
- package/src/utils/requirement-navigation.js +146 -0
- package/src/utils/requirement-organization.js +271 -0
- package/src/utils/simple-trui.js +75 -1
- package/src/utils/trui-navigation.js +28 -2
- package/src/utils/trui-req-tree.js +196 -11
- package/src/utils/trui-specifications.js +31 -1
- package/src/utils/interactive-backup.js +0 -5664
- 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
|
+
};
|