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,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requirement File Operations Module
|
|
3
|
+
*
|
|
4
|
+
* Contains file reading/writing operations for requirement management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Helper to edit clarification responses
|
|
12
|
+
*/
|
|
13
|
+
async function editClarificationResponses(req, tree) {
|
|
14
|
+
const reqPath = await getRequirementsPath();
|
|
15
|
+
|
|
16
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
17
|
+
const lines = content.split('\n');
|
|
18
|
+
|
|
19
|
+
// Find the clarification requirement
|
|
20
|
+
let startIdx = -1;
|
|
21
|
+
let endIdx = -1;
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < lines.length; i++) {
|
|
24
|
+
if (lines[i].includes(`### ${req.title}`)) {
|
|
25
|
+
startIdx = i;
|
|
26
|
+
// Find the end of this requirement
|
|
27
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
28
|
+
if (lines[j].startsWith('###') || lines[j].startsWith('##')) {
|
|
29
|
+
endIdx = j;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (endIdx === -1) endIdx = lines.length;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (startIdx === -1) return;
|
|
39
|
+
|
|
40
|
+
// Extract current responses
|
|
41
|
+
const reqLines = lines.slice(startIdx, endIdx);
|
|
42
|
+
const currentResponses = [];
|
|
43
|
+
|
|
44
|
+
for (const line of reqLines) {
|
|
45
|
+
if (line.startsWith('- **Response:**')) {
|
|
46
|
+
currentResponses.push(line.replace('- **Response:**', '').trim());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log('\n📝 Current Responses:');
|
|
51
|
+
currentResponses.forEach((response, index) => {
|
|
52
|
+
console.log(`${index + 1}. ${response}`);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Add new responses
|
|
56
|
+
const inquirer = require('inquirer');
|
|
57
|
+
const { confirmAction } = require('./requirement-helpers');
|
|
58
|
+
|
|
59
|
+
let addMore = true;
|
|
60
|
+
while (addMore) {
|
|
61
|
+
const { newResponse } = await inquirer.prompt([
|
|
62
|
+
{
|
|
63
|
+
type: 'input',
|
|
64
|
+
name: 'newResponse',
|
|
65
|
+
message: 'Enter new response (or empty to finish):'
|
|
66
|
+
}
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
if (newResponse && newResponse.trim()) {
|
|
70
|
+
currentResponses.push(newResponse.trim());
|
|
71
|
+
console.log('✓ Response added');
|
|
72
|
+
} else {
|
|
73
|
+
addMore = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Update the requirement with new responses
|
|
78
|
+
const newReqLines = [];
|
|
79
|
+
let inResponses = false;
|
|
80
|
+
|
|
81
|
+
for (const line of reqLines) {
|
|
82
|
+
if (line.startsWith('- **Response:**')) {
|
|
83
|
+
if (!inResponses) {
|
|
84
|
+
// Add all new responses
|
|
85
|
+
currentResponses.forEach(response => {
|
|
86
|
+
newReqLines.push(`- **Response:** ${response}`);
|
|
87
|
+
});
|
|
88
|
+
inResponses = true;
|
|
89
|
+
}
|
|
90
|
+
} else if (inResponses && line.startsWith('- **')) {
|
|
91
|
+
// Skip old response lines
|
|
92
|
+
continue;
|
|
93
|
+
} else {
|
|
94
|
+
newReqLines.push(line);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Replace the requirement
|
|
99
|
+
lines.splice(startIdx, endIdx - startIdx, ...newReqLines);
|
|
100
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
101
|
+
console.log('\n✓ Responses updated!');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Helper to move clarification requirement back to TODO
|
|
106
|
+
*/
|
|
107
|
+
async function moveClarificationToTodo(req, tree) {
|
|
108
|
+
const reqPath = await getRequirementsPath();
|
|
109
|
+
|
|
110
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
111
|
+
const lines = content.split('\n');
|
|
112
|
+
|
|
113
|
+
// Find and remove the clarification requirement
|
|
114
|
+
let startIdx = -1;
|
|
115
|
+
let endIdx = -1;
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < lines.length; i++) {
|
|
118
|
+
if (lines[i].includes(`### ${req.title}`)) {
|
|
119
|
+
startIdx = i;
|
|
120
|
+
// Find the end of this requirement
|
|
121
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
122
|
+
if (lines[j].startsWith('###') || lines[j].startsWith('##')) {
|
|
123
|
+
endIdx = j;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (endIdx === -1) endIdx = lines.length;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (startIdx === -1) return;
|
|
133
|
+
|
|
134
|
+
// Extract the requirement content
|
|
135
|
+
const reqContent = lines.slice(startIdx, endIdx);
|
|
136
|
+
|
|
137
|
+
// Remove from clarification section
|
|
138
|
+
lines.splice(startIdx, endIdx - startIdx);
|
|
139
|
+
|
|
140
|
+
// Find TODO section and add there
|
|
141
|
+
let todoIdx = -1;
|
|
142
|
+
for (let i = 0; i < lines.length; i++) {
|
|
143
|
+
if (lines[i].includes('⏳ Requirements not yet completed')) {
|
|
144
|
+
todoIdx = i;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (todoIdx !== -1) {
|
|
150
|
+
// Insert after the TODO header
|
|
151
|
+
let insertIdx = todoIdx + 1;
|
|
152
|
+
while (insertIdx < lines.length && lines[insertIdx].trim() === '') {
|
|
153
|
+
insertIdx++;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
lines.splice(insertIdx, 0, ...reqContent);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
160
|
+
console.log('\n✓ Moved to TODO!');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Helper to move clarification requirement to recycled (used to delete)
|
|
165
|
+
*/
|
|
166
|
+
async function deleteClarification(req, tree) {
|
|
167
|
+
const reqPath = await getRequirementsPath();
|
|
168
|
+
const { confirmAction } = require('./requirement-helpers');
|
|
169
|
+
|
|
170
|
+
const confirmed = await confirmAction(`Delete "${req.title}"? This cannot be undone. (r/y/N)`);
|
|
171
|
+
if (!confirmed) {
|
|
172
|
+
console.log('Cancelled.');
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
177
|
+
const lines = content.split('\n');
|
|
178
|
+
|
|
179
|
+
// Find and remove the clarification requirement
|
|
180
|
+
let startIdx = -1;
|
|
181
|
+
let endIdx = -1;
|
|
182
|
+
|
|
183
|
+
for (let i = 0; i < lines.length; i++) {
|
|
184
|
+
if (lines[i].includes(`### ${req.title}`)) {
|
|
185
|
+
startIdx = i;
|
|
186
|
+
// Find the end of this requirement
|
|
187
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
188
|
+
if (lines[j].startsWith('###') || lines[j].startsWith('##')) {
|
|
189
|
+
endIdx = j;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (endIdx === -1) endIdx = lines.length;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (startIdx !== -1) {
|
|
199
|
+
lines.splice(startIdx, endIdx - startIdx);
|
|
200
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Helper to save reordered requirements back to file
|
|
206
|
+
*/
|
|
207
|
+
async function saveRequirementsOrder(reqPath, sectionTitle, requirements) {
|
|
208
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
209
|
+
const lines = content.split('\n');
|
|
210
|
+
|
|
211
|
+
// Find the section
|
|
212
|
+
let sectionStart = -1;
|
|
213
|
+
let sectionEnd = -1;
|
|
214
|
+
|
|
215
|
+
for (let i = 0; i < lines.length; i++) {
|
|
216
|
+
if (lines[i].includes(sectionTitle)) {
|
|
217
|
+
sectionStart = i;
|
|
218
|
+
// Find the end of this section
|
|
219
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
220
|
+
if (lines[j].startsWith('##')) {
|
|
221
|
+
sectionEnd = j;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (sectionEnd === -1) sectionEnd = lines.length;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (sectionStart === -1) return;
|
|
231
|
+
|
|
232
|
+
// Build new section content
|
|
233
|
+
const newSection = [lines[sectionStart]]; // Keep the header
|
|
234
|
+
newSection.push(''); // Empty line after header
|
|
235
|
+
|
|
236
|
+
// Add requirements in new order
|
|
237
|
+
requirements.forEach(req => {
|
|
238
|
+
newSection.push(`### ${req.title}`);
|
|
239
|
+
if (req.details && req.details.length > 0) {
|
|
240
|
+
req.details.forEach(detail => {
|
|
241
|
+
newSection.push(detail);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
newSection.push(''); // Empty line after requirement
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Replace the section
|
|
248
|
+
lines.splice(sectionStart, sectionEnd - sectionStart, ...newSection);
|
|
249
|
+
|
|
250
|
+
// Save
|
|
251
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = {
|
|
255
|
+
editClarificationResponses,
|
|
256
|
+
moveClarificationToTodo,
|
|
257
|
+
deleteClarification,
|
|
258
|
+
saveRequirementsOrder
|
|
259
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requirement Helpers Module
|
|
3
|
+
*
|
|
4
|
+
* Contains helper functions for requirement management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const readline = require('readline');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Helper to show goodbye message
|
|
12
|
+
*/
|
|
13
|
+
function showGoodbyeMessage() {
|
|
14
|
+
const hour = new Date().getHours();
|
|
15
|
+
const message = hour < 21
|
|
16
|
+
? '\n👋 Goodbye! Thanks for using Vibe Coding Machine!\n'
|
|
17
|
+
: '\n👋 Goodbye! Go get some sleep!\n';
|
|
18
|
+
console.log(chalk.cyan(message));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Helper to get section title from section key
|
|
23
|
+
*/
|
|
24
|
+
function getSectionTitle(sectionKey) {
|
|
25
|
+
if (sectionKey === 'todo') return '⏳ Requirements not yet completed';
|
|
26
|
+
if (sectionKey === 'verify') return '✅ Verified by AI screenshot';
|
|
27
|
+
if (sectionKey === 'recycled') return '♻️ Recycled';
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Helper to get requirement list from tree by section key
|
|
33
|
+
*/
|
|
34
|
+
function getRequirementList(tree, sectionKey) {
|
|
35
|
+
if (sectionKey === 'todo') return tree.todoReqs;
|
|
36
|
+
if (sectionKey === 'verify') return tree.verifyReqs;
|
|
37
|
+
if (sectionKey === 'clarification') return tree.clarificationReqs;
|
|
38
|
+
if (sectionKey === 'recycled') return tree.recycledReqs;
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Helper to show confirmation prompt (r/y for yes, N for no, default N)
|
|
44
|
+
*/
|
|
45
|
+
async function confirmAction(message) {
|
|
46
|
+
console.log();
|
|
47
|
+
process.stdout.write(chalk.yellow(`${message} `));
|
|
48
|
+
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
readline.emitKeypressEvents(process.stdin);
|
|
51
|
+
if (process.stdin.isTTY) {
|
|
52
|
+
process.stdin.setRawMode(true);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
process.stdin.resume();
|
|
56
|
+
process.stdin.on('keypress', (str, key) => {
|
|
57
|
+
process.stdin.setRawMode(false);
|
|
58
|
+
process.stdin.pause();
|
|
59
|
+
process.stdin.removeAllListeners('keypress');
|
|
60
|
+
|
|
61
|
+
if (key.name === 'return' || key.name === 'escape') {
|
|
62
|
+
resolve(false);
|
|
63
|
+
} else if ((key.name === 'r' || key.name === 'y') && !key.ctrl) {
|
|
64
|
+
resolve(true);
|
|
65
|
+
} else {
|
|
66
|
+
resolve(false);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Helper to confirm and exit
|
|
74
|
+
*/
|
|
75
|
+
async function confirmAndExit(message = 'Are you sure you want to exit? (r/y/N)') {
|
|
76
|
+
const confirmed = await confirmAction(message);
|
|
77
|
+
if (confirmed) {
|
|
78
|
+
showGoodbyeMessage();
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Helper to format requirement title for display
|
|
85
|
+
*/
|
|
86
|
+
function formatRequirementTitle(req, index = null) {
|
|
87
|
+
const prefix = index !== null ? `${index + 1}. ` : '';
|
|
88
|
+
const status = req.status ? `[${req.status}] ` : '';
|
|
89
|
+
return `${prefix}${status}${req.title}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Helper to validate requirement title
|
|
94
|
+
*/
|
|
95
|
+
function validateRequirementTitle(title) {
|
|
96
|
+
if (!title || title.trim().length === 0) {
|
|
97
|
+
return 'Requirement title cannot be empty';
|
|
98
|
+
}
|
|
99
|
+
if (title.length > 200) {
|
|
100
|
+
return 'Requirement title must be less than 200 characters';
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Helper to find requirement by title in any section
|
|
107
|
+
*/
|
|
108
|
+
function findRequirementInTree(tree, title) {
|
|
109
|
+
for (const sectionKey of ['todo', 'verify', 'clarification', 'recycled']) {
|
|
110
|
+
const reqList = getRequirementList(tree, sectionKey);
|
|
111
|
+
const req = reqList.find(r => r.title === title);
|
|
112
|
+
if (req) {
|
|
113
|
+
return { req, sectionKey };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
showGoodbyeMessage,
|
|
121
|
+
getSectionTitle,
|
|
122
|
+
getRequirementList,
|
|
123
|
+
confirmAction,
|
|
124
|
+
confirmAndExit,
|
|
125
|
+
formatRequirementTitle,
|
|
126
|
+
validateRequirementTitle,
|
|
127
|
+
findRequirementInTree
|
|
128
|
+
};
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requirement Management Module
|
|
3
|
+
*
|
|
4
|
+
* Handles basic requirement management actions like rename, delete, promote, demote.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const inquirer = require('inquirer');
|
|
9
|
+
const fs = require('fs-extra');
|
|
10
|
+
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
11
|
+
const { getSectionTitle, getRequirementList, confirmAction } = require('./requirement-helpers');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Show requirement actions menu
|
|
15
|
+
*/
|
|
16
|
+
async function showRequirementActions(req, sectionKey, tree) {
|
|
17
|
+
const reqList = getRequirementList(tree, sectionKey);
|
|
18
|
+
const sectionTitle = getSectionTitle(sectionKey);
|
|
19
|
+
const reqIndex = reqList.findIndex(r => r.title === req.title);
|
|
20
|
+
|
|
21
|
+
if (reqIndex === -1) {
|
|
22
|
+
console.log(chalk.red('Requirement not found'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let actions = [
|
|
27
|
+
{ label: '✏️ Rename/Edit', value: 'rename' },
|
|
28
|
+
{ label: '👍 Thumbs Up (promote)', value: 'thumbs-up' },
|
|
29
|
+
{ label: '👎 Thumbs Down (demote)', value: 'thumbs-down' },
|
|
30
|
+
{ label: '🗑️ Delete', value: 'delete' },
|
|
31
|
+
{ label: '↩️ Back', value: 'back' }
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// Add move actions based on current section
|
|
35
|
+
if (sectionKey === 'todo') {
|
|
36
|
+
actions.splice(-2, 0, { label: '↪️ Move to Clarification', value: 'move-to-clarification' });
|
|
37
|
+
} else if (sectionKey === 'clarification') {
|
|
38
|
+
actions.splice(-2, 0, { label: '↩️ Move back to TODO', value: 'move-to-todo' });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log(chalk.bold.cyan(`\n📋 ${sectionTitle}\n`));
|
|
42
|
+
console.log(chalk.bold(`${reqIndex + 1}. ${req.title}`));
|
|
43
|
+
|
|
44
|
+
if (req.details && req.details.length > 0) {
|
|
45
|
+
console.log(chalk.gray('Description:'));
|
|
46
|
+
req.details.forEach(detail => {
|
|
47
|
+
console.log(chalk.gray(` ${detail}`));
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
console.log();
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const { action } = await inquirer.prompt([{
|
|
54
|
+
type: 'list',
|
|
55
|
+
name: 'action',
|
|
56
|
+
message: 'What would you like to do?',
|
|
57
|
+
choices: actions
|
|
58
|
+
}]);
|
|
59
|
+
|
|
60
|
+
if (action !== 'back') {
|
|
61
|
+
await performRequirementAction(action, req, sectionKey, tree);
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(chalk.red('Error performing requirement action:'), error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Perform action on requirement
|
|
70
|
+
*/
|
|
71
|
+
async function performRequirementAction(action, req, sectionKey, tree) {
|
|
72
|
+
const reqList = getRequirementList(tree, sectionKey);
|
|
73
|
+
const reqIndex = reqList.findIndex(r => r.title === req.title);
|
|
74
|
+
|
|
75
|
+
if (reqIndex === -1) {
|
|
76
|
+
console.log(chalk.red('Requirement not found'));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
switch (action) {
|
|
81
|
+
case 'rename':
|
|
82
|
+
await renameRequirement(req, sectionKey, tree);
|
|
83
|
+
break;
|
|
84
|
+
case 'thumbs-up':
|
|
85
|
+
await promoteRequirement(req, sectionKey, tree);
|
|
86
|
+
break;
|
|
87
|
+
case 'thumbs-down':
|
|
88
|
+
await demoteRequirement(req.title, sectionKey, tree);
|
|
89
|
+
break;
|
|
90
|
+
case 'delete':
|
|
91
|
+
await deleteRequirement(req, sectionKey, tree);
|
|
92
|
+
break;
|
|
93
|
+
case 'move-to-clarification':
|
|
94
|
+
await moveToClarification(req, sectionKey, tree);
|
|
95
|
+
break;
|
|
96
|
+
case 'move-to-todo':
|
|
97
|
+
await moveToTodo(req, sectionKey, tree);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Rename requirement (title and description)
|
|
106
|
+
*/
|
|
107
|
+
async function renameRequirement(req, sectionKey, tree) {
|
|
108
|
+
const reqPath = await getRequirementsPath();
|
|
109
|
+
|
|
110
|
+
console.log(chalk.cyan('\n✏️ Rename/Edit Requirement\n'));
|
|
111
|
+
console.log(chalk.gray('Current title:'), chalk.white(req.title));
|
|
112
|
+
if (req.details && req.details.length > 0) {
|
|
113
|
+
console.log(chalk.gray('Current description:'));
|
|
114
|
+
req.details.forEach(line => console.log(chalk.white(' ' + line)));
|
|
115
|
+
}
|
|
116
|
+
console.log();
|
|
117
|
+
|
|
118
|
+
const answers = await inquirer.prompt([
|
|
119
|
+
{
|
|
120
|
+
type: 'input',
|
|
121
|
+
name: 'title',
|
|
122
|
+
message: 'New title (leave blank to keep current):',
|
|
123
|
+
default: ''
|
|
124
|
+
}
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
const newTitle = answers.title.trim() || req.title;
|
|
128
|
+
|
|
129
|
+
// Ask for description using multi-line input
|
|
130
|
+
console.log(chalk.gray('\nEnter new description (leave blank to keep current).'));
|
|
131
|
+
console.log(chalk.gray('Press Enter twice on empty line to finish:\n'));
|
|
132
|
+
|
|
133
|
+
const descriptionLines = [];
|
|
134
|
+
let emptyLineCount = 0;
|
|
135
|
+
let isFirstLine = true;
|
|
136
|
+
let newDescription = '';
|
|
137
|
+
let keptCurrent = false;
|
|
138
|
+
|
|
139
|
+
while (true) {
|
|
140
|
+
try {
|
|
141
|
+
const { line } = await inquirer.prompt([{
|
|
142
|
+
type: 'input',
|
|
143
|
+
name: 'line',
|
|
144
|
+
message: isFirstLine ? 'Description:' : ''
|
|
145
|
+
}]);
|
|
146
|
+
|
|
147
|
+
if (isFirstLine && line.trim() === '') {
|
|
148
|
+
// If first line is empty, keep current description
|
|
149
|
+
keptCurrent = true;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
isFirstLine = false;
|
|
154
|
+
|
|
155
|
+
if (line.trim() === '') {
|
|
156
|
+
emptyLineCount++;
|
|
157
|
+
if (emptyLineCount >= 2) break;
|
|
158
|
+
} else {
|
|
159
|
+
emptyLineCount = 0;
|
|
160
|
+
descriptionLines.push(line);
|
|
161
|
+
}
|
|
162
|
+
} catch (err) {
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!keptCurrent) {
|
|
168
|
+
newDescription = descriptionLines.join('\n');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Read the requirements file
|
|
172
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
173
|
+
const lines = content.split('\n');
|
|
174
|
+
|
|
175
|
+
// Find and update the requirement
|
|
176
|
+
let startIdx = -1;
|
|
177
|
+
let endIdx = -1;
|
|
178
|
+
|
|
179
|
+
for (let i = 0; i < lines.length; i++) {
|
|
180
|
+
if (lines[i].includes(`### ${req.title}`)) {
|
|
181
|
+
startIdx = i;
|
|
182
|
+
// Find the end of this requirement
|
|
183
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
184
|
+
if (lines[j].startsWith('###') || lines[j].startsWith('##')) {
|
|
185
|
+
endIdx = j;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (endIdx === -1) endIdx = lines.length;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (startIdx !== -1) {
|
|
195
|
+
// Build new requirement content
|
|
196
|
+
const newReqContent = [`### ${newTitle}`, ''];
|
|
197
|
+
|
|
198
|
+
if (newDescription && !keptCurrent) {
|
|
199
|
+
newReqContent.push(newDescription, '');
|
|
200
|
+
} else if (req.details && req.details.length > 0) {
|
|
201
|
+
req.details.forEach(detail => newReqContent.push(detail));
|
|
202
|
+
newReqContent.push('');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Replace the requirement
|
|
206
|
+
lines.splice(startIdx, endIdx - startIdx, ...newReqContent);
|
|
207
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
208
|
+
console.log(chalk.green('\n✓ Requirement updated!'));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Delete requirement
|
|
214
|
+
*/
|
|
215
|
+
async function deleteRequirement(req, sectionKey, tree) {
|
|
216
|
+
const reqList = getRequirementList(tree, sectionKey);
|
|
217
|
+
const reqIndex = reqList.findIndex(r => r.title === req.title);
|
|
218
|
+
|
|
219
|
+
if (reqIndex === -1) return;
|
|
220
|
+
|
|
221
|
+
const confirmed = await confirmAction(`Delete "${req.title}"? This cannot be undone. (r/y/N)`);
|
|
222
|
+
if (!confirmed) {
|
|
223
|
+
console.log(chalk.gray('Cancelled.'));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const reqPath = await getRequirementsPath();
|
|
228
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
229
|
+
const lines = content.split('\n');
|
|
230
|
+
|
|
231
|
+
// Find and remove the requirement
|
|
232
|
+
for (let i = 0; i < lines.length; i++) {
|
|
233
|
+
if (lines[i].includes(`### ${req.title}`)) {
|
|
234
|
+
// Find the end of this requirement
|
|
235
|
+
let endIdx = i + 1;
|
|
236
|
+
while (endIdx < lines.length && !lines[endIdx].startsWith('###') && !lines[endIdx].startsWith('##')) {
|
|
237
|
+
endIdx++;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
lines.splice(i, endIdx - i);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
246
|
+
reqList.splice(reqIndex, 1);
|
|
247
|
+
console.log(chalk.green('\n✓ Requirement deleted!'));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Promote requirement (thumbs up)
|
|
252
|
+
*/
|
|
253
|
+
async function promoteRequirement(req, sectionKey, tree) {
|
|
254
|
+
console.log(chalk.cyan(`\n👍 Promoting: ${req.title}`));
|
|
255
|
+
|
|
256
|
+
// Implementation would depend on specific promotion logic
|
|
257
|
+
// This is a placeholder for the actual promotion functionality
|
|
258
|
+
console.log(chalk.yellow('Promotion functionality not yet implemented'));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Demote requirement (thumbs down)
|
|
263
|
+
*/
|
|
264
|
+
async function demoteRequirement(title, sectionKey, tree) {
|
|
265
|
+
console.log(chalk.cyan(`\n👎 Demoting: ${title}`));
|
|
266
|
+
|
|
267
|
+
// Implementation would depend on specific demotion logic
|
|
268
|
+
// This is a placeholder for the actual demotion functionality
|
|
269
|
+
console.log(chalk.yellow('Demotion functionality not yet implemented'));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
module.exports = {
|
|
273
|
+
showRequirementActions,
|
|
274
|
+
performRequirementAction,
|
|
275
|
+
renameRequirement,
|
|
276
|
+
deleteRequirement,
|
|
277
|
+
promoteRequirement,
|
|
278
|
+
demoteRequirement
|
|
279
|
+
};
|