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,305 @@
|
|
|
1
|
+
const { getRequirementsPath, getVibeCodingMachineDir } = require('vibecodingmachine-core');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Export requirements to different formats
|
|
8
|
+
*/
|
|
9
|
+
async function exportRequirements(format = 'json') {
|
|
10
|
+
const reqPath = await getRequirementsPath();
|
|
11
|
+
const stats = await getRequirementsStats();
|
|
12
|
+
|
|
13
|
+
if (!stats) {
|
|
14
|
+
console.error(chalk.red('❌ Could not read requirements file'));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const exportData = {
|
|
19
|
+
exportedAt: new Date().toISOString(),
|
|
20
|
+
stats,
|
|
21
|
+
requirements: {
|
|
22
|
+
todo: [],
|
|
23
|
+
verify: [],
|
|
24
|
+
clarification: [],
|
|
25
|
+
recycled: []
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
31
|
+
const lines = content.split('\n');
|
|
32
|
+
|
|
33
|
+
let currentSection = null;
|
|
34
|
+
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
const trimmed = line.trim();
|
|
37
|
+
|
|
38
|
+
if (trimmed.includes('Requirements not yet completed')) {
|
|
39
|
+
currentSection = 'todo';
|
|
40
|
+
} else if (trimmed.includes('Verified by AI screenshot')) {
|
|
41
|
+
currentSection = 'verify';
|
|
42
|
+
} else if (trimmed.includes('Needs clarification')) {
|
|
43
|
+
currentSection = 'clarification';
|
|
44
|
+
} else if (trimmed.includes('Recycled')) {
|
|
45
|
+
currentSection = 'recycled';
|
|
46
|
+
} else if (trimmed.startsWith('- ')) {
|
|
47
|
+
exportData.requirements[currentSection].push(trimmed.substring(2));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let fileName;
|
|
52
|
+
let exportContent;
|
|
53
|
+
|
|
54
|
+
switch (format.toLowerCase()) {
|
|
55
|
+
case 'json':
|
|
56
|
+
fileName = `requirements-export-${Date.now()}.json`;
|
|
57
|
+
exportContent = JSON.stringify(exportData, null, 2);
|
|
58
|
+
break;
|
|
59
|
+
|
|
60
|
+
case 'csv':
|
|
61
|
+
fileName = `requirements-export-${Date.now()}.csv`;
|
|
62
|
+
exportContent = convertToCSV(exportData);
|
|
63
|
+
break;
|
|
64
|
+
|
|
65
|
+
case 'txt':
|
|
66
|
+
fileName = `requirements-export-${Date.now()}.txt`;
|
|
67
|
+
exportContent = convertToText(exportData);
|
|
68
|
+
break;
|
|
69
|
+
|
|
70
|
+
default:
|
|
71
|
+
console.error(chalk.red('❌ Unsupported export format'));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const exportPath = path.join(path.dirname(reqPath), fileName);
|
|
76
|
+
await fs.writeFile(exportPath, exportContent);
|
|
77
|
+
|
|
78
|
+
console.log(chalk.green(`✅ Requirements exported to: ${exportPath}`));
|
|
79
|
+
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error(chalk.red('❌ Error exporting requirements:'), error.message);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function convertToCSV(data) {
|
|
86
|
+
const csvLines = ['Section,Requirement,ExportedAt'];
|
|
87
|
+
|
|
88
|
+
Object.entries(data.requirements).forEach(([section, requirements]) => {
|
|
89
|
+
requirements.forEach(req => {
|
|
90
|
+
csvLines.push(`"${section}","${req.replace(/"/g, '""')}","${data.exportedAt}"`);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return csvLines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function convertToText(data) {
|
|
98
|
+
let text = `Requirements Export\n`;
|
|
99
|
+
text += `Exported at: ${data.exportedAt}\n`;
|
|
100
|
+
text += `Total requirements: ${data.stats.totalRequirements}\n\n`;
|
|
101
|
+
|
|
102
|
+
Object.entries(data.requirements).forEach(([section, requirements]) => {
|
|
103
|
+
if (requirements.length > 0) {
|
|
104
|
+
text += `${section.toUpperCase()} (${requirements.length}):\n`;
|
|
105
|
+
requirements.forEach((req, index) => {
|
|
106
|
+
text += `${index + 1}. ${req}\n`;
|
|
107
|
+
});
|
|
108
|
+
text += '\n';
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return text;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Import requirements from file
|
|
117
|
+
*/
|
|
118
|
+
async function importRequirements(importPath) {
|
|
119
|
+
if (!await fs.pathExists(importPath)) {
|
|
120
|
+
console.error(chalk.red('❌ Import file not found'));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const ext = path.extname(importPath).toLowerCase();
|
|
126
|
+
let requirements = [];
|
|
127
|
+
|
|
128
|
+
switch (ext) {
|
|
129
|
+
case '.json':
|
|
130
|
+
const data = await fs.readJson(importPath);
|
|
131
|
+
requirements = Object.values(data.requirements).flat();
|
|
132
|
+
break;
|
|
133
|
+
|
|
134
|
+
case '.csv':
|
|
135
|
+
const csvContent = await fs.readFile(importPath, 'utf8');
|
|
136
|
+
const lines = csvContent.split('\n');
|
|
137
|
+
for (let i = 1; i < lines.length; i++) { // Skip header
|
|
138
|
+
const cols = lines[i].split(',');
|
|
139
|
+
if (cols.length >= 2) {
|
|
140
|
+
const requirement = cols[1].replace(/^"|"$/g, '').replace(/""/g, '"');
|
|
141
|
+
requirements.push(requirement);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
|
|
146
|
+
case '.txt':
|
|
147
|
+
const txtContent = await fs.readFile(importPath, 'utf8');
|
|
148
|
+
const txtLines = txtContent.split('\n');
|
|
149
|
+
for (const line of txtLines) {
|
|
150
|
+
const trimmed = line.trim();
|
|
151
|
+
if (trimmed && /^\d+\./.test(trimmed)) {
|
|
152
|
+
requirements.push(trimmed.replace(/^\d+\.\s*/, ''));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
default:
|
|
158
|
+
console.error(chalk.red('❌ Unsupported import format'));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (requirements.length === 0) {
|
|
163
|
+
console.log(chalk.yellow('⚠️ No requirements found in import file'));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(chalk.green(`✅ Found ${requirements.length} requirements to import`));
|
|
168
|
+
|
|
169
|
+
const reqPath = await getRequirementsPath();
|
|
170
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
171
|
+
const lines = content.split('\n');
|
|
172
|
+
|
|
173
|
+
// Find TODO section
|
|
174
|
+
const todoIndex = lines.findIndex(line => line.includes('Requirements not yet completed'));
|
|
175
|
+
if (todoIndex === -1) {
|
|
176
|
+
console.error(chalk.red('❌ TODO section not found in requirements file'));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Insert requirements at the end of TODO section
|
|
181
|
+
let insertIndex = todoIndex + 1;
|
|
182
|
+
while (insertIndex < lines.length && !lines[insertIndex].startsWith('##') && lines[insertIndex].trim() !== '') {
|
|
183
|
+
insertIndex++;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Add a blank line if needed
|
|
187
|
+
if (insertIndex < lines.length && lines[insertIndex].trim() !== '') {
|
|
188
|
+
lines.splice(insertIndex, 0, '');
|
|
189
|
+
insertIndex++;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Add imported requirements
|
|
193
|
+
const importedRequirements = requirements.map(req => `- ${req}`);
|
|
194
|
+
lines.splice(insertIndex, 0, ...importedRequirements);
|
|
195
|
+
|
|
196
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
197
|
+
console.log(chalk.green(`✅ Imported ${requirements.length} requirements`));
|
|
198
|
+
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error(chalk.red('❌ Error importing requirements:'), error.message);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get requirements stats (re-exported from validation module)
|
|
206
|
+
*/
|
|
207
|
+
async function getRequirementsStats() {
|
|
208
|
+
const { getRequirementsStats: getStats } = require('./file-validation');
|
|
209
|
+
return await getStats();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Export requirements with custom filters
|
|
214
|
+
*/
|
|
215
|
+
async function exportFilteredRequirements(filters = {}) {
|
|
216
|
+
const reqPath = await getRequirementsPath();
|
|
217
|
+
const stats = await getRequirementsStats();
|
|
218
|
+
|
|
219
|
+
if (!stats) {
|
|
220
|
+
console.error(chalk.red('❌ Could not read requirements file'));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
226
|
+
const lines = content.split('\n');
|
|
227
|
+
|
|
228
|
+
const filteredData = {
|
|
229
|
+
exportedAt: new Date().toISOString(),
|
|
230
|
+
filters,
|
|
231
|
+
requirements: {
|
|
232
|
+
todo: [],
|
|
233
|
+
verify: [],
|
|
234
|
+
clarification: [],
|
|
235
|
+
recycled: []
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
let currentSection = null;
|
|
240
|
+
|
|
241
|
+
for (const line of lines) {
|
|
242
|
+
const trimmed = line.trim();
|
|
243
|
+
|
|
244
|
+
if (trimmed.includes('Requirements not yet completed')) {
|
|
245
|
+
currentSection = 'todo';
|
|
246
|
+
} else if (trimmed.includes('Verified by AI screenshot')) {
|
|
247
|
+
currentSection = 'verify';
|
|
248
|
+
} else if (trimmed.includes('Needs clarification')) {
|
|
249
|
+
currentSection = 'clarification';
|
|
250
|
+
} else if (trimmed.includes('Recycled')) {
|
|
251
|
+
currentSection = 'recycled';
|
|
252
|
+
} else if (trimmed.startsWith('- ')) {
|
|
253
|
+
const requirement = trimmed.substring(2);
|
|
254
|
+
|
|
255
|
+
// Apply filters
|
|
256
|
+
let includeRequirement = true;
|
|
257
|
+
|
|
258
|
+
if (filters.section && filters.section !== currentSection) {
|
|
259
|
+
includeRequirement = false;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (filters.keyword && !requirement.toLowerCase().includes(filters.keyword.toLowerCase())) {
|
|
263
|
+
includeRequirement = false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (filters.minLength && requirement.length < filters.minLength) {
|
|
267
|
+
includeRequirement = false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (filters.maxLength && requirement.length > filters.maxLength) {
|
|
271
|
+
includeRequirement = false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (includeRequirement) {
|
|
275
|
+
filteredData.requirements[currentSection].push(requirement);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Count total filtered requirements
|
|
281
|
+
const totalFiltered = Object.values(filteredData.requirements).reduce((sum, reqs) => sum + reqs.length, 0);
|
|
282
|
+
|
|
283
|
+
console.log(chalk.green(`✅ Exported ${totalFiltered} filtered requirements`));
|
|
284
|
+
|
|
285
|
+
// Export as JSON by default for filtered exports
|
|
286
|
+
const fileName = `requirements-filtered-export-${Date.now()}.json`;
|
|
287
|
+
const exportPath = path.join(path.dirname(reqPath), fileName);
|
|
288
|
+
const exportContent = JSON.stringify(filteredData, null, 2);
|
|
289
|
+
|
|
290
|
+
await fs.writeFile(exportPath, exportContent);
|
|
291
|
+
console.log(chalk.green(`✅ Filtered export saved to: ${exportPath}`));
|
|
292
|
+
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error(chalk.red('❌ Error exporting filtered requirements:'), error.message);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
module.exports = {
|
|
299
|
+
exportRequirements,
|
|
300
|
+
importRequirements,
|
|
301
|
+
getRequirementsStats,
|
|
302
|
+
exportFilteredRequirements,
|
|
303
|
+
convertToCSV,
|
|
304
|
+
convertToText
|
|
305
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Operations - Orchestrates file I/O operations
|
|
3
|
+
*
|
|
4
|
+
* This module coordinates between backup, validation, and import/export operations.
|
|
5
|
+
* It serves as the main entry point for file-related operations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
|
|
10
|
+
// Import split modules
|
|
11
|
+
const {
|
|
12
|
+
createBackup,
|
|
13
|
+
restoreFromBackup,
|
|
14
|
+
listBackups,
|
|
15
|
+
cleanupOldBackups
|
|
16
|
+
} = require('./file-backup');
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
validateRequirementsFile,
|
|
20
|
+
getRequirementsStats,
|
|
21
|
+
checkDuplicateRequirements,
|
|
22
|
+
checkOrphanedResponses
|
|
23
|
+
} = require('./file-validation');
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
exportRequirements,
|
|
27
|
+
importRequirements,
|
|
28
|
+
exportFilteredRequirements
|
|
29
|
+
} = require('./file-import-export');
|
|
30
|
+
|
|
31
|
+
// Re-export all functions for backward compatibility
|
|
32
|
+
module.exports = {
|
|
33
|
+
// Backup operations
|
|
34
|
+
createBackup,
|
|
35
|
+
restoreFromBackup,
|
|
36
|
+
listBackups,
|
|
37
|
+
cleanupOldBackups,
|
|
38
|
+
|
|
39
|
+
// Validation operations
|
|
40
|
+
validateRequirementsFile,
|
|
41
|
+
getRequirementsStats,
|
|
42
|
+
checkDuplicateRequirements,
|
|
43
|
+
checkOrphanedResponses,
|
|
44
|
+
|
|
45
|
+
// Import/Export operations
|
|
46
|
+
exportRequirements,
|
|
47
|
+
importRequirements,
|
|
48
|
+
exportFilteredRequirements
|
|
49
|
+
};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validate requirements file format
|
|
7
|
+
*/
|
|
8
|
+
async function validateRequirementsFile() {
|
|
9
|
+
const reqPath = await getRequirementsPath();
|
|
10
|
+
|
|
11
|
+
if (!await fs.pathExists(reqPath)) {
|
|
12
|
+
console.log(chalk.red('❌ Requirements file not found'));
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
18
|
+
const lines = content.split('\n');
|
|
19
|
+
|
|
20
|
+
const issues = [];
|
|
21
|
+
let hasTodoSection = false;
|
|
22
|
+
let hasVerifySection = false;
|
|
23
|
+
let hasStatusSection = false;
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < lines.length; i++) {
|
|
26
|
+
const line = lines[i].trim();
|
|
27
|
+
|
|
28
|
+
if (line.includes('Requirements not yet completed')) {
|
|
29
|
+
hasTodoSection = true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (line.includes('Verified by AI screenshot')) {
|
|
33
|
+
hasVerifySection = true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (line.includes('🚦 Current Status')) {
|
|
37
|
+
hasStatusSection = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check for malformed requirement lines
|
|
41
|
+
if (line && !line.startsWith('#') && !line.startsWith('-') && !line.startsWith('[') && !line.startsWith('```')) {
|
|
42
|
+
if (i > 0 && !lines[i-1].trim().startsWith('#')) {
|
|
43
|
+
issues.push(`Line ${i + 1}: Unexpected content - "${line.substring(0, 50)}..."`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(chalk.bold('\n🔍 Requirements File Validation\n'));
|
|
49
|
+
|
|
50
|
+
if (!hasTodoSection) {
|
|
51
|
+
issues.push('Missing TODO section');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!hasVerifySection) {
|
|
55
|
+
issues.push('Missing verification section');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!hasStatusSection) {
|
|
59
|
+
issues.push('Missing status section');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (issues.length === 0) {
|
|
63
|
+
console.log(chalk.green('✅ Requirements file format is valid\n'));
|
|
64
|
+
return true;
|
|
65
|
+
} else {
|
|
66
|
+
console.log(chalk.yellow('⚠️ Issues found:\n'));
|
|
67
|
+
issues.forEach(issue => console.log(chalk.gray(` • ${issue}`)));
|
|
68
|
+
console.log();
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(chalk.red('❌ Error validating requirements file:'), error.message);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get requirements file statistics
|
|
79
|
+
*/
|
|
80
|
+
async function getRequirementsStats() {
|
|
81
|
+
const reqPath = await getRequirementsPath();
|
|
82
|
+
|
|
83
|
+
if (!await fs.pathExists(reqPath)) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
89
|
+
const lines = content.split('\n');
|
|
90
|
+
|
|
91
|
+
const stats = {
|
|
92
|
+
totalLines: lines.length,
|
|
93
|
+
todoCount: 0,
|
|
94
|
+
verifyCount: 0,
|
|
95
|
+
recycledCount: 0,
|
|
96
|
+
clarificationCount: 0,
|
|
97
|
+
totalRequirements: 0,
|
|
98
|
+
lastModified: (await fs.stat(reqPath)).mtime
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
let currentSection = null;
|
|
102
|
+
|
|
103
|
+
for (const line of lines) {
|
|
104
|
+
const trimmed = line.trim();
|
|
105
|
+
|
|
106
|
+
if (trimmed.includes('Requirements not yet completed')) {
|
|
107
|
+
currentSection = 'todo';
|
|
108
|
+
} else if (trimmed.includes('Verified by AI screenshot')) {
|
|
109
|
+
currentSection = 'verify';
|
|
110
|
+
} else if (trimmed.includes('Needs clarification')) {
|
|
111
|
+
currentSection = 'clarification';
|
|
112
|
+
} else if (trimmed.includes('Recycled')) {
|
|
113
|
+
currentSection = 'recycled';
|
|
114
|
+
} else if (trimmed.startsWith('- ')) {
|
|
115
|
+
stats.totalRequirements++;
|
|
116
|
+
|
|
117
|
+
switch (currentSection) {
|
|
118
|
+
case 'todo':
|
|
119
|
+
stats.todoCount++;
|
|
120
|
+
break;
|
|
121
|
+
case 'verify':
|
|
122
|
+
stats.verifyCount++;
|
|
123
|
+
break;
|
|
124
|
+
case 'clarification':
|
|
125
|
+
stats.clarificationCount++;
|
|
126
|
+
break;
|
|
127
|
+
case 'recycled':
|
|
128
|
+
stats.recycledCount++;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return stats;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error(chalk.red('❌ Error getting requirements stats:'), error.message);
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check for duplicate requirements
|
|
143
|
+
*/
|
|
144
|
+
async function checkDuplicateRequirements() {
|
|
145
|
+
const reqPath = await getRequirementsPath();
|
|
146
|
+
|
|
147
|
+
if (!await fs.pathExists(reqPath)) {
|
|
148
|
+
console.log(chalk.red('❌ Requirements file not found'));
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
154
|
+
const lines = content.split('\n');
|
|
155
|
+
|
|
156
|
+
const requirements = [];
|
|
157
|
+
const duplicates = [];
|
|
158
|
+
|
|
159
|
+
for (const line of lines) {
|
|
160
|
+
const trimmed = line.trim();
|
|
161
|
+
if (trimmed.startsWith('- ')) {
|
|
162
|
+
const requirement = trimmed.substring(2);
|
|
163
|
+
const normalized = requirement.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
164
|
+
|
|
165
|
+
if (requirements[normalized]) {
|
|
166
|
+
duplicates.push({
|
|
167
|
+
requirement,
|
|
168
|
+
originalLine: requirements[normalized].line,
|
|
169
|
+
duplicateLine: lines.indexOf(line) + 1
|
|
170
|
+
});
|
|
171
|
+
} else {
|
|
172
|
+
requirements[normalized] = {
|
|
173
|
+
requirement,
|
|
174
|
+
line: lines.indexOf(line) + 1
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (duplicates.length > 0) {
|
|
181
|
+
console.log(chalk.bold('\n🔍 Duplicate Requirements Found\n'));
|
|
182
|
+
console.log(chalk.yellow(`Found ${duplicates.length} duplicate(s):\n`));
|
|
183
|
+
|
|
184
|
+
duplicates.forEach((dup, index) => {
|
|
185
|
+
console.log(chalk.cyan(`${index + 1}. "${dup.requirement}"`));
|
|
186
|
+
console.log(chalk.gray(` Original: Line ${dup.originalLine}`));
|
|
187
|
+
console.log(chalk.gray(` Duplicate: Line ${dup.duplicateLine}\n`));
|
|
188
|
+
});
|
|
189
|
+
} else {
|
|
190
|
+
console.log(chalk.green('\n✅ No duplicate requirements found\n'));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return duplicates;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error(chalk.red('❌ Error checking duplicates:'), error.message);
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Check for orphaned responses (responses without requirements)
|
|
202
|
+
*/
|
|
203
|
+
async function checkOrphanedResponses() {
|
|
204
|
+
const reqPath = await getRequirementsPath();
|
|
205
|
+
|
|
206
|
+
if (!await fs.pathExists(reqPath)) {
|
|
207
|
+
console.log(chalk.red('❌ Requirements file not found'));
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
213
|
+
const lines = content.split('\n');
|
|
214
|
+
|
|
215
|
+
const orphanedResponses = [];
|
|
216
|
+
let inClarificationSection = false;
|
|
217
|
+
let lastRequirement = null;
|
|
218
|
+
|
|
219
|
+
for (let i = 0; i < lines.length; i++) {
|
|
220
|
+
const line = lines[i].trim();
|
|
221
|
+
|
|
222
|
+
if (line.includes('Needs clarification')) {
|
|
223
|
+
inClarificationSection = true;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (line.startsWith('##') && !line.includes('Needs clarification')) {
|
|
228
|
+
inClarificationSection = false;
|
|
229
|
+
lastRequirement = null;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (line.startsWith('- ')) {
|
|
234
|
+
lastRequirement = line;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (line.startsWith(' - ')) {
|
|
239
|
+
const response = line.substring(4).trim();
|
|
240
|
+
|
|
241
|
+
if (!lastRequirement) {
|
|
242
|
+
orphanedResponses.push({
|
|
243
|
+
response,
|
|
244
|
+
line: i + 1,
|
|
245
|
+
type: 'no_requirement'
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (orphanedResponses.length > 0) {
|
|
252
|
+
console.log(chalk.bold('\n🔍 Orphaned Responses Found\n'));
|
|
253
|
+
console.log(chalk.yellow(`Found ${orphanedResponses.length} orphaned response(s):\n`));
|
|
254
|
+
|
|
255
|
+
orphanedResponses.forEach((orphan, index) => {
|
|
256
|
+
console.log(chalk.cyan(`${index + 1}. "${orphan.response}"`));
|
|
257
|
+
console.log(chalk.gray(` Line: ${orphan.line}`));
|
|
258
|
+
console.log(chalk.gray(` Type: ${orphan.type}\n`));
|
|
259
|
+
});
|
|
260
|
+
} else {
|
|
261
|
+
console.log(chalk.green('\n✅ No orphaned responses found\n'));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return orphanedResponses;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error(chalk.red('❌ Error checking orphaned responses:'), error.message);
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
module.exports = {
|
|
272
|
+
validateRequirementsFile,
|
|
273
|
+
getRequirementsStats,
|
|
274
|
+
checkDuplicateRequirements,
|
|
275
|
+
checkOrphanedResponses
|
|
276
|
+
};
|