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,356 @@
|
|
|
1
|
+
const { t, getRequirementsPath, promoteTodoToVerify, promoteToVerified, demoteVerifyToTodo, demoteFromVerifiedToTodo } = require('vibecodingmachine-core');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const inquirer = require('inquirer');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
// Helper to get section title from section key
|
|
7
|
+
function getSectionTitle(sectionKey) {
|
|
8
|
+
if (sectionKey === 'todo') return '⏳ Requirements not yet completed';
|
|
9
|
+
if (sectionKey === 'verify') return '✅ Verified by AI screenshot';
|
|
10
|
+
if (sectionKey === 'recycled') return '♻️ Recycled';
|
|
11
|
+
if (sectionKey === 'clarification') return '❓ Needs clarification';
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Helper to get requirement list from tree by section key
|
|
16
|
+
function getRequirementList(tree, sectionKey) {
|
|
17
|
+
if (sectionKey === 'todo') return tree.todoReqs;
|
|
18
|
+
if (sectionKey === 'verify') return tree.verifyReqs;
|
|
19
|
+
if (sectionKey === 'clarification') return tree.clarificationReqs;
|
|
20
|
+
if (sectionKey === 'recycled') return tree.recycledReqs;
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Helper to rename requirement (title and description)
|
|
25
|
+
async function renameRequirement(req, sectionKey, tree) {
|
|
26
|
+
const { newTitle, newDescription } = await inquirer.prompt([
|
|
27
|
+
{
|
|
28
|
+
type: 'input',
|
|
29
|
+
name: 'newTitle',
|
|
30
|
+
message: 'Enter new title:',
|
|
31
|
+
default: req.title,
|
|
32
|
+
validate: input => input.trim().length > 0 || 'Title cannot be empty'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: 'input',
|
|
36
|
+
name: 'newDescription',
|
|
37
|
+
message: 'Enter new description:',
|
|
38
|
+
default: req.content
|
|
39
|
+
}
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
const reqPath = await getRequirementsPath();
|
|
43
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
44
|
+
const lines = content.split('\n');
|
|
45
|
+
|
|
46
|
+
// Find and replace the requirement
|
|
47
|
+
const reqIndex = lines.findIndex(line => line.includes(req.title));
|
|
48
|
+
if (reqIndex !== -1) {
|
|
49
|
+
lines[reqIndex] = `- ${newTitle}. ${newDescription}`;
|
|
50
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
51
|
+
console.log(chalk.green('✅ Requirement renamed'));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Helper to move requirement to recycled section (used to delete)
|
|
56
|
+
async function deleteRequirement(req, sectionKey, tree) {
|
|
57
|
+
const reqList = getRequirementList(tree, sectionKey);
|
|
58
|
+
const sectionTitle = getSectionTitle(sectionKey);
|
|
59
|
+
const reqIndex = reqList.findIndex(r => r.title === req.title);
|
|
60
|
+
|
|
61
|
+
if (reqIndex === -1) {
|
|
62
|
+
console.error(chalk.red('❌ Requirement not found'));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const confirmed = await confirmAction('Delete this requirement? (r/y/N):');
|
|
67
|
+
if (!confirmed) return;
|
|
68
|
+
|
|
69
|
+
const reqPath = await getRequirementsPath();
|
|
70
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
71
|
+
const lines = content.split('\n');
|
|
72
|
+
|
|
73
|
+
// Find the requirement line
|
|
74
|
+
const lineIndex = lines.findIndex(line => line.includes(req.title));
|
|
75
|
+
if (lineIndex !== -1) {
|
|
76
|
+
// Remove the requirement
|
|
77
|
+
const requirementLine = lines[lineIndex];
|
|
78
|
+
lines.splice(lineIndex, 1);
|
|
79
|
+
|
|
80
|
+
// Add to recycled section
|
|
81
|
+
await moveRequirementToRecycled(reqPath, req.title, sectionKey, [requirementLine]);
|
|
82
|
+
|
|
83
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
84
|
+
console.log(chalk.green('✅ Requirement deleted'));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Helper to permanently delete requirement from file (used for recycled items)
|
|
89
|
+
async function permanentlyDeleteRequirement(req, sectionKey, tree) {
|
|
90
|
+
const reqList = getRequirementList(tree, sectionKey);
|
|
91
|
+
const reqIndex = reqList.findIndex(r => r.title === req.title);
|
|
92
|
+
|
|
93
|
+
if (reqIndex === -1) {
|
|
94
|
+
console.error(chalk.red('❌ Requirement not found'));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const confirmed = await confirmAction('Permanently delete this requirement? (r/y/N):');
|
|
99
|
+
if (!confirmed) return;
|
|
100
|
+
|
|
101
|
+
const reqPath = await getRequirementsPath();
|
|
102
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
103
|
+
const lines = content.split('\n');
|
|
104
|
+
|
|
105
|
+
// Find and remove the requirement
|
|
106
|
+
const lineIndex = lines.findIndex(line => line.includes(req.title));
|
|
107
|
+
if (lineIndex !== -1) {
|
|
108
|
+
lines.splice(lineIndex, 1);
|
|
109
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
110
|
+
console.log(chalk.green('✅ Requirement permanently deleted'));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Helper to move requirement to recycled section
|
|
115
|
+
async function moveRequirementToRecycled(reqPath, requirementTitle, fromSection, requirementLines = null) {
|
|
116
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
117
|
+
const lines = content.split('\n');
|
|
118
|
+
|
|
119
|
+
// Find recycled section
|
|
120
|
+
const recycledIndex = lines.findIndex(line => line.includes('♻️ Recycled'));
|
|
121
|
+
if (recycledIndex === -1) {
|
|
122
|
+
// Create recycled section
|
|
123
|
+
lines.push('## ♻️ Recycled');
|
|
124
|
+
lines.push('');
|
|
125
|
+
lines.push(`- ${requirementTitle}`);
|
|
126
|
+
} else {
|
|
127
|
+
// Add to existing recycled section
|
|
128
|
+
let insertIndex = recycledIndex + 1;
|
|
129
|
+
while (insertIndex < lines.length && lines[insertIndex].trim() === '') {
|
|
130
|
+
insertIndex++;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (requirementLines) {
|
|
134
|
+
lines.splice(insertIndex, 0, ...requirementLines);
|
|
135
|
+
} else {
|
|
136
|
+
lines.splice(insertIndex, 0, `- ${requirementTitle}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Helper to move requirement down with 'j' key
|
|
144
|
+
async function moveRequirementDown(req, sectionKey, tree) {
|
|
145
|
+
const reqList = getRequirementList(tree, sectionKey);
|
|
146
|
+
const sectionTitle = getSectionTitle(sectionKey);
|
|
147
|
+
const reqIndex = reqList.findIndex(r => r.title === req.title);
|
|
148
|
+
|
|
149
|
+
if (reqIndex === -1 || reqIndex === reqList.length - 1) {
|
|
150
|
+
return; // Can't move down if already at bottom
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Swap with next item
|
|
154
|
+
[reqList[reqIndex], reqList[reqIndex + 1]] = [reqList[reqIndex + 1], reqList[reqIndex]];
|
|
155
|
+
|
|
156
|
+
const reqPath = await getRequirementsPath();
|
|
157
|
+
await saveRequirementsOrder(reqPath, sectionTitle, reqList);
|
|
158
|
+
console.log(chalk.green('✅ Requirement moved down'));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Helper to move requirement up with 'k' key
|
|
162
|
+
async function moveRequirementUp(req, sectionKey, tree) {
|
|
163
|
+
const reqList = getRequirementList(tree, sectionKey);
|
|
164
|
+
const sectionTitle = getSectionTitle(sectionKey);
|
|
165
|
+
const reqIndex = reqList.findIndex(r => r.title === req.title);
|
|
166
|
+
|
|
167
|
+
if (reqIndex <= 0) {
|
|
168
|
+
return; // Can't move up if already at top
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Swap with previous item
|
|
172
|
+
[reqList[reqIndex], reqList[reqIndex - 1]] = [reqList[reqIndex - 1], reqList[reqIndex]];
|
|
173
|
+
|
|
174
|
+
const reqPath = await getRequirementsPath();
|
|
175
|
+
await saveRequirementsOrder(reqPath, sectionTitle, reqList);
|
|
176
|
+
console.log(chalk.green('✅ Requirement moved up'));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Helper to promote requirement to next list (TODO -> TO VERIFY -> VERIFIED)
|
|
180
|
+
async function promoteRequirement(req, sectionKey, tree, loadSection, loadVerified) {
|
|
181
|
+
const reqPath = await getRequirementsPath();
|
|
182
|
+
|
|
183
|
+
if (sectionKey === 'todo') {
|
|
184
|
+
await promoteTodoToVerify(reqPath, req.title);
|
|
185
|
+
console.log(chalk.green('✅ Requirement promoted to verification'));
|
|
186
|
+
} else if (sectionKey === 'verify') {
|
|
187
|
+
await promoteToVerified(reqPath, req.title);
|
|
188
|
+
console.log(chalk.green('✅ Requirement promoted to verified'));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Helper to demote requirement to previous list (VERIFIED -> TODO, TO VERIFY -> TODO)
|
|
193
|
+
async function demoteRequirement(reqTitle, sectionKey, tree, loadSection, loadVerified) {
|
|
194
|
+
const reqPath = await getRequirementsPath();
|
|
195
|
+
|
|
196
|
+
if (sectionKey === 'verify') {
|
|
197
|
+
await demoteVerifyToTodo(reqPath, reqTitle);
|
|
198
|
+
console.log(chalk.green('✅ Requirement demoted to TODO'));
|
|
199
|
+
} else if (sectionKey === 'verified') {
|
|
200
|
+
await demoteFromVerifiedToTodo(reqPath, reqTitle);
|
|
201
|
+
console.log(chalk.green('✅ Requirement demoted to TODO'));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Helper to restore requirement from recycled to TODO
|
|
206
|
+
async function restoreRequirement(req, sectionKey, tree) {
|
|
207
|
+
const reqPath = await getRequirementsPath();
|
|
208
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
209
|
+
const lines = content.split('\n');
|
|
210
|
+
|
|
211
|
+
// Find requirement in recycled section
|
|
212
|
+
const reqIndex = lines.findIndex(line => line.includes(req.title));
|
|
213
|
+
if (reqIndex === -1) {
|
|
214
|
+
console.error(chalk.red('❌ Requirement not found'));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Remove from recycled
|
|
219
|
+
const requirementLine = lines[reqIndex];
|
|
220
|
+
lines.splice(reqIndex, 1);
|
|
221
|
+
|
|
222
|
+
// Add to TODO section
|
|
223
|
+
const todoIndex = lines.findIndex(line => line.includes('Requirements not yet completed'));
|
|
224
|
+
if (todoIndex !== -1) {
|
|
225
|
+
let insertIndex = todoIndex + 1;
|
|
226
|
+
while (insertIndex < lines.length && lines[insertIndex].trim() === '') {
|
|
227
|
+
insertIndex++;
|
|
228
|
+
}
|
|
229
|
+
lines.splice(insertIndex, 0, requirementLine);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
233
|
+
console.log(chalk.green('✅ Requirement restored to TODO'));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Helper to move requirement to clarification section
|
|
237
|
+
async function moveRequirementToClarification(req, sectionKey, tree) {
|
|
238
|
+
const reqPath = await getRequirementsPath();
|
|
239
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
240
|
+
const lines = content.split('\n');
|
|
241
|
+
|
|
242
|
+
// Find requirement
|
|
243
|
+
const reqIndex = lines.findIndex(line => line.includes(req.title));
|
|
244
|
+
if (reqIndex === -1) {
|
|
245
|
+
console.error(chalk.red('❌ Requirement not found'));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Remove from current section
|
|
250
|
+
const requirementLine = lines[reqIndex];
|
|
251
|
+
lines.splice(reqIndex, 1);
|
|
252
|
+
|
|
253
|
+
// Add to clarification section
|
|
254
|
+
const clarificationIndex = lines.findIndex(line => line.includes('Needs clarification'));
|
|
255
|
+
if (clarificationIndex !== -1) {
|
|
256
|
+
let insertIndex = clarificationIndex + 1;
|
|
257
|
+
while (insertIndex < lines.length && lines[insertIndex].trim() === '') {
|
|
258
|
+
insertIndex++;
|
|
259
|
+
}
|
|
260
|
+
lines.splice(insertIndex, 0, requirementLine);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
264
|
+
console.log(chalk.green('✅ Requirement moved to clarification'));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Helper to save reordered requirements back to file
|
|
268
|
+
async function saveRequirementsOrder(reqPath, sectionTitle, requirements) {
|
|
269
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
270
|
+
const lines = content.split('\n');
|
|
271
|
+
|
|
272
|
+
// Find section start
|
|
273
|
+
const sectionIndex = lines.findIndex(line => line.includes(sectionTitle));
|
|
274
|
+
if (sectionIndex === -1) {
|
|
275
|
+
console.error(chalk.red('❌ Section not found'));
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Find section end (next section or end of file)
|
|
280
|
+
let sectionEnd = sectionIndex + 1;
|
|
281
|
+
while (sectionEnd < lines.length && !lines[sectionEnd].startsWith('##')) {
|
|
282
|
+
sectionEnd++;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Build new section content
|
|
286
|
+
const newLines = lines.slice(0, sectionIndex + 1);
|
|
287
|
+
newLines.push(''); // Empty line after header
|
|
288
|
+
|
|
289
|
+
// Add requirements in new order
|
|
290
|
+
requirements.forEach(req => {
|
|
291
|
+
if (typeof req === 'string') {
|
|
292
|
+
newLines.push(req);
|
|
293
|
+
} else {
|
|
294
|
+
newLines.push(`- ${req.title}`);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Add remaining lines
|
|
299
|
+
const remainingLines = lines.slice(sectionEnd);
|
|
300
|
+
newLines.push(...remainingLines);
|
|
301
|
+
|
|
302
|
+
await fs.writeFile(reqPath, newLines.join('\n'));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Helper to show confirmation prompt (r/y for yes, N for no, default N)
|
|
306
|
+
async function confirmAction(message) {
|
|
307
|
+
console.log();
|
|
308
|
+
process.stdout.write(chalk.yellow(`${message} `));
|
|
309
|
+
|
|
310
|
+
return new Promise((resolve) => {
|
|
311
|
+
process.stdin.setRawMode(true);
|
|
312
|
+
process.stdin.resume();
|
|
313
|
+
process.stdin.setEncoding('utf8');
|
|
314
|
+
|
|
315
|
+
let data = '';
|
|
316
|
+
process.stdin.on('data', (key) => {
|
|
317
|
+
data += key;
|
|
318
|
+
|
|
319
|
+
// Accept 'r' or 'y' for yes, anything else for no
|
|
320
|
+
if (key.toLowerCase() === 'r' || key.toLowerCase() === 'y') {
|
|
321
|
+
process.stdout.write(key);
|
|
322
|
+
process.stdout.write('\n');
|
|
323
|
+
process.stdin.setRawMode(false);
|
|
324
|
+
process.stdin.pause();
|
|
325
|
+
resolve(true);
|
|
326
|
+
} else if (key === '\r' || key === '\n' || key === '\u0003') { // Enter, Esc, or Ctrl+C
|
|
327
|
+
process.stdout.write('N\n');
|
|
328
|
+
process.stdin.setRawMode(false);
|
|
329
|
+
process.stdin.pause();
|
|
330
|
+
resolve(false);
|
|
331
|
+
} else if (data.length > 1) { // Any other input
|
|
332
|
+
process.stdout.write('N\n');
|
|
333
|
+
process.stdin.setRawMode(false);
|
|
334
|
+
process.stdin.pause();
|
|
335
|
+
resolve(false);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
module.exports = {
|
|
342
|
+
renameRequirement,
|
|
343
|
+
deleteRequirement,
|
|
344
|
+
permanentlyDeleteRequirement,
|
|
345
|
+
moveRequirementToRecycled,
|
|
346
|
+
moveRequirementDown,
|
|
347
|
+
moveRequirementUp,
|
|
348
|
+
promoteRequirement,
|
|
349
|
+
demoteRequirement,
|
|
350
|
+
restoreRequirement,
|
|
351
|
+
moveRequirementToClarification,
|
|
352
|
+
saveRequirementsOrder,
|
|
353
|
+
confirmAction,
|
|
354
|
+
getSectionTitle,
|
|
355
|
+
getRequirementList
|
|
356
|
+
};
|
|
@@ -0,0 +1,286 @@
|
|
|
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 get section title from section key
|
|
7
|
+
function getSectionTitle(sectionKey) {
|
|
8
|
+
if (sectionKey === 'todo') return '⏳ Requirements not yet completed';
|
|
9
|
+
if (sectionKey === 'verify') return '✅ Verified by AI screenshot';
|
|
10
|
+
if (sectionKey === 'recycled') return '♻️ Recycled';
|
|
11
|
+
if (sectionKey === 'clarification') return '❓ Needs clarification';
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Helper to get requirement list from tree by section key
|
|
16
|
+
function getRequirementList(tree, sectionKey) {
|
|
17
|
+
if (sectionKey === 'todo') return tree.todoReqs;
|
|
18
|
+
if (sectionKey === 'verify') return tree.verifyReqs;
|
|
19
|
+
if (sectionKey === 'clarification') return tree.clarificationReqs;
|
|
20
|
+
if (sectionKey === 'recycled') return tree.recycledReqs;
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Tree-style requirements navigator
|
|
25
|
+
async function showRequirementsTree() {
|
|
26
|
+
console.log(chalk.bold.cyan('\n📋 ' + t('requirements.navigator.title') + '\n'));
|
|
27
|
+
console.log(chalk.gray(t('requirements.navigator.basic.instructions') + '\n'));
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const reqPath = await getRequirementsPath();
|
|
31
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
32
|
+
|
|
33
|
+
// Parse requirements into tree structure
|
|
34
|
+
const tree = parseRequirementsTree(content);
|
|
35
|
+
|
|
36
|
+
// Show summary
|
|
37
|
+
console.log(chalk.yellow(`📊 Summary:`));
|
|
38
|
+
console.log(chalk.gray(` • Todo: ${tree.todoReqs.length} items`));
|
|
39
|
+
console.log(chalk.gray(` • Needs clarification: ${tree.clarificationReqs.length} items`));
|
|
40
|
+
console.log(chalk.gray(` • Verified: ${tree.verifyReqs.length} items`));
|
|
41
|
+
console.log(chalk.gray(` • Recycled: ${tree.recycledReqs.length} items\n`));
|
|
42
|
+
|
|
43
|
+
// Interactive navigation
|
|
44
|
+
await navigateRequirements(tree);
|
|
45
|
+
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(chalk.red('❌ Error loading requirements:'), error.message);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseRequirementsTree(content) {
|
|
52
|
+
const lines = content.split('\n');
|
|
53
|
+
const tree = {
|
|
54
|
+
todoReqs: [],
|
|
55
|
+
verifyReqs: [],
|
|
56
|
+
clarificationReqs: [],
|
|
57
|
+
recycledReqs: []
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
let currentSection = null;
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < lines.length; i++) {
|
|
63
|
+
const line = lines[i].trim();
|
|
64
|
+
|
|
65
|
+
// Detect section headers
|
|
66
|
+
if (line.includes('Requirements not yet completed')) {
|
|
67
|
+
currentSection = 'todo';
|
|
68
|
+
} else if (line.includes('Verified by AI screenshot')) {
|
|
69
|
+
currentSection = 'verify';
|
|
70
|
+
} else if (line.includes('Needs clarification')) {
|
|
71
|
+
currentSection = 'clarification';
|
|
72
|
+
} else if (line.includes('Recycled')) {
|
|
73
|
+
currentSection = 'recycled';
|
|
74
|
+
} else if (line.startsWith('- ') && currentSection) {
|
|
75
|
+
// Parse requirement
|
|
76
|
+
const req = parseRequirement(line, i);
|
|
77
|
+
if (req) {
|
|
78
|
+
tree[`${currentSection}Reqs`].push(req);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return tree;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parseRequirement(line, index) {
|
|
87
|
+
// Remove the "- " prefix
|
|
88
|
+
const content = line.substring(2);
|
|
89
|
+
|
|
90
|
+
// Extract title (first sentence or up to 100 chars)
|
|
91
|
+
const titleEnd = content.indexOf('.') !== -1 ? content.indexOf('.') + 1 : 100;
|
|
92
|
+
const title = content.substring(0, Math.min(titleEnd, 100)).trim();
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
title,
|
|
96
|
+
content,
|
|
97
|
+
index,
|
|
98
|
+
originalLine: line
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function navigateRequirements(tree) {
|
|
103
|
+
const sections = [
|
|
104
|
+
{ key: 'todo', name: '⏳ Todo', count: tree.todoReqs.length },
|
|
105
|
+
{ key: 'clarification', name: '❓ Needs clarification', count: tree.clarificationReqs.length },
|
|
106
|
+
{ key: 'verify', name: '✅ Verified', count: tree.verifyReqs.length },
|
|
107
|
+
{ key: 'recycled', name: '♻️ Recycled', count: tree.recycledReqs.length }
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
while (true) {
|
|
111
|
+
const { action } = await inquirer.prompt([
|
|
112
|
+
{
|
|
113
|
+
type: 'list',
|
|
114
|
+
name: 'action',
|
|
115
|
+
message: 'Navigate to section:',
|
|
116
|
+
choices: [
|
|
117
|
+
...sections.filter(s => s.count > 0).map(s => ({
|
|
118
|
+
name: `${s.name} (${s.count} items)`,
|
|
119
|
+
value: s.key
|
|
120
|
+
})),
|
|
121
|
+
{ name: '🔄 Refresh', value: 'refresh' },
|
|
122
|
+
{ name: '📝 Add new requirement', value: 'add' },
|
|
123
|
+
{ name: '🚪 Exit', value: 'exit' }
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
if (action === 'exit') break;
|
|
129
|
+
if (action === 'refresh') {
|
|
130
|
+
// Reload requirements
|
|
131
|
+
const reqPath = await getRequirementsPath();
|
|
132
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
133
|
+
Object.assign(tree, parseRequirementsTree(content));
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (action === 'add') {
|
|
137
|
+
await handleAddRequirement();
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Show section details
|
|
142
|
+
await showSectionDetails(action, tree);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function showSectionDetails(sectionKey, tree) {
|
|
147
|
+
const reqList = getRequirementList(tree, sectionKey);
|
|
148
|
+
const sectionTitle = getSectionTitle(sectionKey);
|
|
149
|
+
|
|
150
|
+
if (reqList.length === 0) {
|
|
151
|
+
console.log(chalk.gray(`\nNo items in ${sectionTitle}\n`));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log(chalk.bold(`\n${sectionTitle}\n`));
|
|
156
|
+
|
|
157
|
+
while (true) {
|
|
158
|
+
const { choice } = await inquirer.prompt([
|
|
159
|
+
{
|
|
160
|
+
type: 'list',
|
|
161
|
+
name: 'choice',
|
|
162
|
+
message: 'Select requirement:',
|
|
163
|
+
choices: [
|
|
164
|
+
...reqList.map((req, index) => ({
|
|
165
|
+
name: `${index + 1}. ${req.title}`,
|
|
166
|
+
value: index
|
|
167
|
+
})),
|
|
168
|
+
{ name: '⬅️ Back to sections', value: 'back' }
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
if (choice === 'back') break;
|
|
174
|
+
|
|
175
|
+
const req = reqList[choice];
|
|
176
|
+
await showRequirementDetails(req, sectionKey, tree);
|
|
177
|
+
|
|
178
|
+
// Reload tree in case of changes
|
|
179
|
+
const reqPath = await getRequirementsPath();
|
|
180
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
181
|
+
Object.assign(tree, parseRequirementsTree(content));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function showRequirementDetails(req, sectionKey, tree) {
|
|
186
|
+
console.log(chalk.bold(`\n${req.title}\n`));
|
|
187
|
+
console.log(chalk.gray(req.content));
|
|
188
|
+
console.log();
|
|
189
|
+
|
|
190
|
+
const { action } = await inquirer.prompt([
|
|
191
|
+
{
|
|
192
|
+
type: 'list',
|
|
193
|
+
name: 'action',
|
|
194
|
+
message: 'What would you like to do?',
|
|
195
|
+
choices: getRequirementActions(sectionKey)
|
|
196
|
+
}
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
if (action !== 'back') {
|
|
200
|
+
await performRequirementAction(action, req, sectionKey, tree);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function getRequirementActions(sectionKey) {
|
|
205
|
+
const baseActions = [
|
|
206
|
+
{ name: '⬅️ Back', value: 'back' }
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
switch (sectionKey) {
|
|
210
|
+
case 'todo':
|
|
211
|
+
return [
|
|
212
|
+
{ name: '✏️ Rename/Edit', value: 'rename' },
|
|
213
|
+
{ name: '👍 Thumbs up (promote to Verified)', value: 'thumbs-up' },
|
|
214
|
+
{ name: '❓ Needs clarification', value: 'needs-clarification' },
|
|
215
|
+
{ name: '🗑️ Delete', value: 'delete' },
|
|
216
|
+
{ name: '⬆️ Move up', value: 'move-up' },
|
|
217
|
+
{ name: '⬇️ Move down', value: 'move-down' },
|
|
218
|
+
...baseActions
|
|
219
|
+
];
|
|
220
|
+
case 'clarification':
|
|
221
|
+
return [
|
|
222
|
+
{ name: '✍️ Add/Edit Responses', value: 'edit-responses' },
|
|
223
|
+
{ name: '↩️ Move back to TODO (after clarification)', value: 'move-to-todo' },
|
|
224
|
+
{ name: '🗑️ Delete', value: 'delete' },
|
|
225
|
+
...baseActions
|
|
226
|
+
];
|
|
227
|
+
case 'verify':
|
|
228
|
+
return [
|
|
229
|
+
{ name: '↩️ Move back to TODO', value: 'move-to-todo' },
|
|
230
|
+
{ name: '🗑️ Delete', value: 'delete' },
|
|
231
|
+
...baseActions
|
|
232
|
+
];
|
|
233
|
+
case 'recycled':
|
|
234
|
+
return [
|
|
235
|
+
{ name: '♻️ Restore to TODO', value: 'restore' },
|
|
236
|
+
{ name: '🗑️ Permanently delete', value: 'permanent-delete' },
|
|
237
|
+
...baseActions
|
|
238
|
+
];
|
|
239
|
+
default:
|
|
240
|
+
return baseActions;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function performRequirementAction(action, req, sectionKey, tree) {
|
|
245
|
+
const { performRequirementAction: actionHandler } = require('./requirement-actions');
|
|
246
|
+
await actionHandler(action, req, sectionKey, tree);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function handleAddRequirement(type) {
|
|
250
|
+
const reqCommands = require('../commands/requirements');
|
|
251
|
+
const packages = ['all', 'cli', 'core', 'electron-app', 'web', 'mobile'];
|
|
252
|
+
|
|
253
|
+
const { package: pkg, requirement } = await inquirer.prompt([
|
|
254
|
+
{
|
|
255
|
+
type: 'list',
|
|
256
|
+
name: 'package',
|
|
257
|
+
message: 'Select package:',
|
|
258
|
+
choices: packages
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
type: 'input',
|
|
262
|
+
name: 'requirement',
|
|
263
|
+
message: 'Enter requirement:',
|
|
264
|
+
validate: input => input.trim().length > 0 || 'Requirement cannot be empty'
|
|
265
|
+
}
|
|
266
|
+
]);
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
await reqCommands.add(pkg, requirement);
|
|
270
|
+
console.log(chalk.green('✅ Requirement added successfully'));
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error(chalk.red('❌ Error adding requirement:'), error.message);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
module.exports = {
|
|
277
|
+
showRequirementsTree,
|
|
278
|
+
getSectionTitle,
|
|
279
|
+
getRequirementList,
|
|
280
|
+
parseRequirementsTree,
|
|
281
|
+
navigateRequirements,
|
|
282
|
+
showSectionDetails,
|
|
283
|
+
showRequirementDetails,
|
|
284
|
+
getRequirementActions,
|
|
285
|
+
handleAddRequirement
|
|
286
|
+
};
|