vibecodingmachine-cli 2026.2.26-1752 → 2026.3.9-1621
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 +7 -1
- package/bin/commands/agent-commands.js +150 -228
- package/bin/commands/command-aliases.js +68 -0
- package/bin/vibecodingmachine.js +1 -2
- package/package.json +2 -2
- package/src/commands/agents/list.js +71 -115
- package/src/commands/agents-check.js +16 -4
- package/src/commands/analyze-file-sizes.js +1 -1
- package/src/commands/auto-direct/auto-provider-manager.js +290 -0
- package/src/commands/auto-direct/auto-status-display.js +331 -0
- package/src/commands/auto-direct/auto-utils.js +439 -0
- package/src/commands/auto-direct/file-operations.js +110 -0
- package/src/commands/auto-direct/provider-config.js +1 -1
- package/src/commands/auto-direct/provider-manager.js +1 -1
- package/src/commands/auto-direct/status-display.js +1 -1
- package/src/commands/auto-direct/utils.js +24 -18
- package/src/commands/auto-direct-refactored.js +413 -0
- package/src/commands/auto-direct.js +594 -188
- package/src/commands/requirements/commands.js +353 -0
- package/src/commands/requirements/default-handlers.js +272 -0
- package/src/commands/requirements/disable.js +97 -0
- package/src/commands/requirements/enable.js +97 -0
- package/src/commands/requirements/utils.js +194 -0
- package/src/commands/requirements-refactored.js +60 -0
- package/src/commands/requirements.js +38 -771
- package/src/commands/specs/disable.js +96 -0
- package/src/commands/specs/enable.js +96 -0
- package/src/trui/TruiInterface.js +5 -11
- package/src/trui/agents/AgentInterface.js +24 -396
- package/src/trui/agents/handlers/CommandHandler.js +93 -0
- package/src/trui/agents/handlers/ContextManager.js +117 -0
- package/src/trui/agents/handlers/DisplayHandler.js +243 -0
- package/src/trui/agents/handlers/HelpHandler.js +51 -0
- package/src/utils/auth.js +13 -111
- package/src/utils/config.js +4 -0
- package/src/utils/interactive/requirements-navigation.js +17 -15
- package/src/utils/interactive-broken.js +2 -2
- package/src/utils/provider-checker/agent-runner.js +15 -1
- package/src/utils/provider-checker/cli-installer.js +149 -7
- package/src/utils/provider-checker/opencode-checker.js +588 -0
- package/src/utils/provider-checker/provider-validator.js +88 -3
- package/src/utils/provider-checker/time-formatter.js +3 -2
- package/src/utils/provider-manager.js +28 -20
- package/src/utils/provider-registry.js +35 -3
- package/src/utils/requirements-navigator/index.js +94 -0
- package/src/utils/requirements-navigator/input-handler.js +217 -0
- package/src/utils/requirements-navigator/section-loader.js +188 -0
- package/src/utils/requirements-navigator/tree-builder.js +105 -0
- package/src/utils/requirements-navigator/tree-renderer.js +50 -0
- package/src/utils/requirements-navigator.js +2 -583
- package/src/utils/trui-clarifications.js +188 -0
- package/src/utils/trui-feedback.js +54 -1
- package/src/utils/trui-kiro-integration.js +398 -0
- package/src/utils/trui-main-handlers.js +194 -0
- package/src/utils/trui-main-menu.js +235 -0
- package/src/utils/trui-nav-agents.js +178 -25
- package/src/utils/trui-nav-requirements.js +203 -27
- package/src/utils/trui-nav-settings.js +114 -1
- package/src/utils/trui-nav-specifications.js +44 -3
- package/src/utils/trui-navigation-backup.js +603 -0
- package/src/utils/trui-navigation.js +70 -228
- package/src/utils/trui-provider-health.js +274 -0
- package/src/utils/trui-provider-manager.js +376 -0
- package/src/utils/trui-quick-menu.js +25 -1
- package/src/utils/trui-req-actions-backup.js +507 -0
- package/src/utils/trui-req-actions.js +148 -216
- package/src/utils/trui-req-editor.js +170 -0
- package/src/utils/trui-req-file-ops.js +278 -0
- package/src/utils/trui-req-tree-old.js +719 -0
- package/src/utils/trui-req-tree.js +348 -627
- package/src/utils/trui-specifications.js +25 -7
- package/src/utils/trui-windsurf.js +231 -10
- package/src/utils/welcome-screen-extracted.js +2 -2
- package/src/utils/welcome-screen.js +2 -2
|
@@ -1,793 +1,60 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
|
-
const chalk = require('chalk');
|
|
4
|
-
const chokidar = require('chokidar');
|
|
5
|
-
const { getRepoPath, setRepoPath } = require('../utils/config');
|
|
6
|
-
const { getRequirementsPath, DefaultRequirementManager, JSONStorage } = require('vibecodingmachine-core');
|
|
7
|
-
|
|
8
|
-
async function getReqPathOrExit() {
|
|
9
|
-
let repoPath = await getRepoPath();
|
|
10
|
-
|
|
11
|
-
// Auto-detect and initialize if we're in a git repository
|
|
12
|
-
if (!repoPath) {
|
|
13
|
-
const cwd = process.cwd();
|
|
14
|
-
const cwdResolved = path.resolve(cwd);
|
|
15
|
-
|
|
16
|
-
// Find the git root by walking up the directory tree
|
|
17
|
-
let currentDir = cwdResolved;
|
|
18
|
-
let foundGitRoot = false;
|
|
19
|
-
const maxDepth = 50; // Prevent infinite loops
|
|
20
|
-
let depth = 0;
|
|
21
|
-
|
|
22
|
-
// Walk up the directory tree looking for .git directory
|
|
23
|
-
while (depth < maxDepth && currentDir !== path.dirname(currentDir)) {
|
|
24
|
-
const gitPath = path.join(currentDir, '.git');
|
|
25
|
-
try {
|
|
26
|
-
if (await fs.pathExists(gitPath)) {
|
|
27
|
-
repoPath = currentDir;
|
|
28
|
-
foundGitRoot = true;
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
31
|
-
} catch (err) {
|
|
32
|
-
// If we can't check, continue to parent
|
|
33
|
-
}
|
|
34
|
-
const parentDir = path.dirname(currentDir);
|
|
35
|
-
if (parentDir === currentDir) {
|
|
36
|
-
// Reached filesystem root
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
currentDir = parentDir;
|
|
40
|
-
depth++;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Fallback: if no .git found but .vibecodingmachine exists, use current dir
|
|
44
|
-
if (!foundGitRoot) {
|
|
45
|
-
try {
|
|
46
|
-
const vibecodingmachineDir = path.join(cwdResolved, '.vibecodingmachine');
|
|
47
|
-
if (await fs.pathExists(vibecodingmachineDir)) {
|
|
48
|
-
repoPath = cwdResolved;
|
|
49
|
-
foundGitRoot = true;
|
|
50
|
-
}
|
|
51
|
-
} catch (err) {
|
|
52
|
-
// Ignore errors
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Final fallback: use current directory if we have package.json
|
|
57
|
-
if (!foundGitRoot) {
|
|
58
|
-
try {
|
|
59
|
-
const packageJsonPath = path.join(cwdResolved, 'package.json');
|
|
60
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
61
|
-
repoPath = cwdResolved;
|
|
62
|
-
foundGitRoot = true;
|
|
63
|
-
}
|
|
64
|
-
} catch (err) {
|
|
65
|
-
// Ignore errors
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// If we found a repo path, save it
|
|
70
|
-
if (repoPath) {
|
|
71
|
-
try {
|
|
72
|
-
await setRepoPath(repoPath);
|
|
73
|
-
} catch (err) {
|
|
74
|
-
// If setting fails, we still have repoPath set, so continue
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Final validation - ensure we have a valid path
|
|
80
|
-
// If we still don't have a repoPath, use current directory as absolute last resort
|
|
81
|
-
if (!repoPath) {
|
|
82
|
-
repoPath = path.resolve(process.cwd());
|
|
83
|
-
try {
|
|
84
|
-
await setRepoPath(repoPath);
|
|
85
|
-
} catch (err) {
|
|
86
|
-
// Even if we can't save it, we'll use it for this session
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// At this point, repoPath MUST be set (we've tried everything including current directory)
|
|
91
|
-
if (!repoPath) {
|
|
92
|
-
console.log(chalk.yellow('No repository path configured'));
|
|
93
|
-
console.log(chalk.gray('Use'), chalk.cyan('vcm repo:init'), chalk.gray('or'), chalk.cyan('vcm repo:set <path>'));
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Use getRequirementsPath which handles hostname-specific files
|
|
98
|
-
const reqPath = await getRequirementsPath(repoPath);
|
|
99
|
-
return { repoPath, reqPath };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async function list(options) {
|
|
103
|
-
try {
|
|
104
|
-
const { repoPath, reqPath } = await getReqPathOrExit();
|
|
105
|
-
|
|
106
|
-
// Handle --all-computers flag
|
|
107
|
-
if (options && options.allComputers) {
|
|
108
|
-
const vibeDir = path.join(repoPath, '.vibecodingmachine');
|
|
109
|
-
if (!await fs.pathExists(vibeDir)) {
|
|
110
|
-
console.log(chalk.yellow('No .vibecodingmachine directory found.'));
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Find all REQUIREMENTS-*.md files
|
|
115
|
-
const files = await fs.readdir(vibeDir);
|
|
116
|
-
const reqFiles = files.filter(f => f.startsWith('REQUIREMENTS') && f.endsWith('.md'));
|
|
117
|
-
|
|
118
|
-
if (reqFiles.length === 0) {
|
|
119
|
-
console.log(chalk.yellow('No requirements files found.'));
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Extract computer info and requirements from each file
|
|
124
|
-
for (const file of reqFiles) {
|
|
125
|
-
const filePath = path.join(vibeDir, file);
|
|
126
|
-
const content = await fs.readFile(filePath, 'utf8');
|
|
127
|
-
const lines = content.split('\n');
|
|
128
|
-
|
|
129
|
-
// Extract hostname from file
|
|
130
|
-
let hostname = 'Unknown';
|
|
131
|
-
const hostnameMatch = content.match(/- \*\*Hostname\*\*:\s*(.+)/);
|
|
132
|
-
if (hostnameMatch) {
|
|
133
|
-
hostname = hostnameMatch[1].trim();
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Extract focus area
|
|
137
|
-
let focus = '';
|
|
138
|
-
const focusMatch = content.match(/## 🎯 Focus\n\*\*(.+)\*\*/);
|
|
139
|
-
if (focusMatch) {
|
|
140
|
-
focus = focusMatch[1].trim();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Filter by focus if specified
|
|
144
|
-
if (options.focus && focus && !focus.toLowerCase().includes(options.focus.toLowerCase())) {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
console.log(chalk.blue.bold(`\n📍 ${hostname}`) + (focus ? chalk.gray(` (${focus})`) : ''));
|
|
149
|
-
|
|
150
|
-
// List requirements from this file
|
|
151
|
-
for (const line of lines) {
|
|
152
|
-
if (line.startsWith('### ') || line.startsWith('## ')) {
|
|
153
|
-
const statusFilter = options.status ? String(options.status).toLowerCase() : null;
|
|
154
|
-
if (!statusFilter || line.toLowerCase().includes(statusFilter)) {
|
|
155
|
-
console.log(' ' + line);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Single computer mode (default or with --computer filter)
|
|
164
|
-
let targetReqPath = reqPath;
|
|
165
|
-
|
|
166
|
-
// If computer filter specified, find that computer's requirements file
|
|
167
|
-
if (options && options.computer) {
|
|
168
|
-
const vibeDir = path.join(repoPath, '.vibecodingmachine');
|
|
169
|
-
const files = await fs.readdir(vibeDir);
|
|
170
|
-
const computerFile = files.find(f =>
|
|
171
|
-
f.startsWith('REQUIREMENTS') &&
|
|
172
|
-
f.endsWith('.md') &&
|
|
173
|
-
f.toLowerCase().includes(options.computer.toLowerCase())
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
if (!computerFile) {
|
|
177
|
-
console.log(chalk.yellow(`No requirements file found for computer: ${options.computer}`));
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
targetReqPath = path.join(vibeDir, computerFile);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (!await fs.pathExists(targetReqPath)) {
|
|
185
|
-
console.log(chalk.yellow('No REQUIREMENTS.md found.'));
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const content = await fs.readFile(targetReqPath, 'utf8');
|
|
190
|
-
const lines = content.split('\n');
|
|
191
|
-
|
|
192
|
-
// Extract focus area for focus filtering
|
|
193
|
-
let fileFocus = '';
|
|
194
|
-
const focusMatch = content.match(/## 🎯 Focus\n\*\*(.+)\*\*/);
|
|
195
|
-
if (focusMatch) {
|
|
196
|
-
fileFocus = focusMatch[1].trim();
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Check focus filter
|
|
200
|
-
if (options && options.focus) {
|
|
201
|
-
if (!fileFocus || !fileFocus.toLowerCase().includes(options.focus.toLowerCase())) {
|
|
202
|
-
console.log(chalk.yellow(`No requirements match focus area: ${options.focus}`));
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const statusFilter = options && options.status ? String(options.status).toLowerCase() : null;
|
|
208
|
-
for (const line of lines) {
|
|
209
|
-
if (line.startsWith('### ') || line.startsWith('## ')) {
|
|
210
|
-
if (!statusFilter || line.toLowerCase().includes(statusFilter)) {
|
|
211
|
-
console.log(line);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
} catch (error) {
|
|
216
|
-
console.error(chalk.red('Error listing requirements:'), error.message);
|
|
217
|
-
process.exit(1);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
async function add(name, pkg, description) {
|
|
222
|
-
try {
|
|
223
|
-
const { reqPath, repoPath } = await getReqPathOrExit();
|
|
224
|
-
await fs.ensureFile(reqPath);
|
|
225
|
-
let content = await fs.readFile(reqPath, 'utf8').catch(() => '');
|
|
226
|
-
|
|
227
|
-
// Find the TODO section
|
|
228
|
-
const todoSectionHeader = '## ⏳ Requirements not yet completed';
|
|
229
|
-
if (!content.includes('Requirements not yet completed')) {
|
|
230
|
-
content += '\n\n' + todoSectionHeader + '\n';
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Insert at the top of the TODO section (right after the header)
|
|
234
|
-
const lines = content.split('\n');
|
|
235
|
-
const newLines = [];
|
|
236
|
-
let inserted = false;
|
|
237
|
-
|
|
238
|
-
for (let i = 0; i < lines.length; i++) {
|
|
239
|
-
newLines.push(lines[i]);
|
|
240
|
-
|
|
241
|
-
// Insert right after the TODO section header
|
|
242
|
-
if (!inserted && lines[i].startsWith('##') && lines[i].includes('Requirements not yet completed')) {
|
|
243
|
-
// Add requirement header without number prefix (for backward compatibility)
|
|
244
|
-
newLines.push(`### ${name}`);
|
|
245
|
-
|
|
246
|
-
// Add package if provided (and not 'all')
|
|
247
|
-
if (pkg && Array.isArray(pkg) && pkg.length > 0) {
|
|
248
|
-
const packageValue = pkg.length === 1 ? pkg[0] : pkg.join(', ');
|
|
249
|
-
if (packageValue !== 'all') {
|
|
250
|
-
newLines.push(`PACKAGE: ${packageValue}`);
|
|
251
|
-
}
|
|
252
|
-
} else if (pkg && typeof pkg === 'string' && pkg !== 'all') {
|
|
253
|
-
newLines.push(`PACKAGE: ${pkg}`);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Add description if provided
|
|
257
|
-
if (description && typeof description === 'string' && description.trim()) {
|
|
258
|
-
const descLines = description.split('\n');
|
|
259
|
-
descLines.forEach(line => {
|
|
260
|
-
if (line.trim()) {
|
|
261
|
-
newLines.push(line);
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
} else if (description && Array.isArray(description)) {
|
|
265
|
-
// Handle array of description lines
|
|
266
|
-
description.forEach(line => {
|
|
267
|
-
if (line && typeof line === 'string' && line.trim()) {
|
|
268
|
-
newLines.push(line);
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Add blank line after requirement
|
|
274
|
-
newLines.push('');
|
|
275
|
-
inserted = true;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
content = newLines.join('\n');
|
|
280
|
-
await fs.writeFile(reqPath, content);
|
|
281
|
-
console.log(chalk.green('✓ Requirement added'));
|
|
282
|
-
} catch (error) {
|
|
283
|
-
console.error(chalk.red('Error adding requirement:'), error.message);
|
|
284
|
-
process.exit(1);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
async function current() {
|
|
289
|
-
try {
|
|
290
|
-
const { reqPath } = await getReqPathOrExit();
|
|
291
|
-
if (!await fs.pathExists(reqPath)) {
|
|
292
|
-
console.log(chalk.yellow('No REQUIREMENTS.md found.'));
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
const content = await fs.readFile(reqPath, 'utf8');
|
|
296
|
-
const match = content.match(/## \uD83D\uDEA6 Current Status\n([A-Z_]+)/);
|
|
297
|
-
console.log(chalk.cyan(match ? match[1] : 'UNKNOWN'));
|
|
298
|
-
} catch (error) {
|
|
299
|
-
console.error(chalk.red('Error reading current status:'), error.message);
|
|
300
|
-
process.exit(1);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
async function next() {
|
|
305
|
-
try {
|
|
306
|
-
const { reqPath } = await getReqPathOrExit();
|
|
307
|
-
if (!await fs.pathExists(reqPath)) {
|
|
308
|
-
console.log(chalk.yellow('No REQUIREMENTS.md found.'));
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
const content = await fs.readFile(reqPath, 'utf8');
|
|
312
|
-
const order = ['PREPARE', 'ACT', 'CLEAN UP', 'VERIFY', 'DONE'];
|
|
313
|
-
const currentMatch = content.match(/## \uD83D\uDEA6 Current Status\n([A-Z_ ]+)/);
|
|
314
|
-
const current = currentMatch ? currentMatch[1] : 'PREPARE';
|
|
315
|
-
const idx = Math.min(order.indexOf(current) + 1, order.length - 1);
|
|
316
|
-
const nextStatus = order[idx];
|
|
317
|
-
const updated = content.replace(/(## \uD83D\uDEA6 Current Status\n)([A-Z_ ]+)/, `$1${nextStatus}`);
|
|
318
|
-
await fs.writeFile(reqPath, updated);
|
|
319
|
-
console.log(chalk.green('✓ Moved to next status:'), chalk.cyan(nextStatus));
|
|
320
|
-
} catch (error) {
|
|
321
|
-
console.error(chalk.red('Error updating status:'), error.message);
|
|
322
|
-
process.exit(1);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
async function edit() {
|
|
327
|
-
try {
|
|
328
|
-
const { reqPath } = await getReqPathOrExit();
|
|
329
|
-
// Always use system default opener to avoid taking over the terminal
|
|
330
|
-
const { execSync } = require('child_process');
|
|
331
|
-
// Use 'open' for macOS (user's OS), could be expanded for others if needed
|
|
332
|
-
execSync(`open ${JSON.stringify(reqPath)}`);
|
|
333
|
-
} catch (error) {
|
|
334
|
-
console.error(chalk.red('Error opening file:'), error.message);
|
|
335
|
-
process.exit(1);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
async function watch() {
|
|
340
|
-
try {
|
|
341
|
-
const { reqPath } = await getReqPathOrExit();
|
|
342
|
-
console.log(chalk.gray('Watching:'), chalk.cyan(reqPath));
|
|
343
|
-
chokidar.watch(reqPath, { ignoreInitial: true })
|
|
344
|
-
.on('change', () => console.log(chalk.green('✓ REQUIREMENTS.md changed')))
|
|
345
|
-
.on('error', (err) => console.error(chalk.red('Watcher error:'), err.message));
|
|
346
|
-
} catch (error) {
|
|
347
|
-
console.error(chalk.red('Error watching requirements:'), error.message);
|
|
348
|
-
process.exit(1);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
async function rename(oldTitle, newTitle, description) {
|
|
353
|
-
try {
|
|
354
|
-
const { reqPath } = await getReqPathOrExit();
|
|
355
|
-
if (!await fs.pathExists(reqPath)) {
|
|
356
|
-
console.log(chalk.yellow('No REQUIREMENTS.md found.'));
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Read the requirements file
|
|
361
|
-
const content = await fs.readFile(reqPath, 'utf8');
|
|
362
|
-
const lines = content.split('\n');
|
|
363
|
-
|
|
364
|
-
// Find the requirement block
|
|
365
|
-
let requirementStartIndex = -1;
|
|
366
|
-
let requirementEndIndex = -1;
|
|
367
|
-
|
|
368
|
-
for (let i = 0; i < lines.length; i++) {
|
|
369
|
-
const line = lines[i].trim();
|
|
370
|
-
if (line.startsWith('###')) {
|
|
371
|
-
const title = line.replace(/^###\s*/, '').trim();
|
|
372
|
-
if (title === oldTitle) {
|
|
373
|
-
requirementStartIndex = i;
|
|
374
|
-
// Find the end of this requirement (next ### or ## header)
|
|
375
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
376
|
-
const nextLine = lines[j].trim();
|
|
377
|
-
if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
|
|
378
|
-
requirementEndIndex = j;
|
|
379
|
-
break;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
if (requirementEndIndex === -1) {
|
|
383
|
-
requirementEndIndex = lines.length;
|
|
384
|
-
}
|
|
385
|
-
break;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (requirementStartIndex === -1) {
|
|
391
|
-
console.log(chalk.yellow(`⚠️ Could not find requirement: "${oldTitle}"`));
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Build new requirement block
|
|
396
|
-
const newBlock = [`### ${newTitle}`];
|
|
397
|
-
|
|
398
|
-
// If description provided, use it
|
|
399
|
-
if (description) {
|
|
400
|
-
if (Array.isArray(description)) {
|
|
401
|
-
description.forEach(line => {
|
|
402
|
-
if (line && line.trim()) {
|
|
403
|
-
newBlock.push(line);
|
|
404
|
-
}
|
|
405
|
-
});
|
|
406
|
-
} else if (typeof description === 'string') {
|
|
407
|
-
description.split('\n').forEach(line => {
|
|
408
|
-
if (line.trim()) {
|
|
409
|
-
newBlock.push(line);
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
} else {
|
|
414
|
-
// Keep existing details (skip the ### header line)
|
|
415
|
-
for (let i = requirementStartIndex + 1; i < requirementEndIndex; i++) {
|
|
416
|
-
const line = lines[i];
|
|
417
|
-
if (line.trim()) {
|
|
418
|
-
newBlock.push(line);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
newBlock.push(''); // Blank line after requirement
|
|
424
|
-
|
|
425
|
-
// Replace the old block with the new one
|
|
426
|
-
lines.splice(requirementStartIndex, requirementEndIndex - requirementStartIndex, ...newBlock);
|
|
427
|
-
|
|
428
|
-
// Save
|
|
429
|
-
await fs.writeFile(reqPath, lines.join('\n'));
|
|
430
|
-
console.log(chalk.green('✓ Requirement renamed/updated'));
|
|
431
|
-
} catch (error) {
|
|
432
|
-
console.error(chalk.red('Error renaming requirement:'), error.message);
|
|
433
|
-
process.exit(1);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
async function working() {
|
|
438
|
-
try {
|
|
439
|
-
const { reqPath } = await getReqPathOrExit();
|
|
440
|
-
if (!await fs.pathExists(reqPath)) {
|
|
441
|
-
console.log(chalk.yellow('No REQUIREMENTS.md found.'));
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
const { getCurrentRequirement } = require('vibecodingmachine-core');
|
|
445
|
-
// Note: Core's getCurrentRequirement returns "Working on: X" or default text.
|
|
446
|
-
// It assumes process.cwd() or similar.
|
|
447
|
-
// But wait, Core's getCurrentRequirement takes 'repoPath'.
|
|
448
|
-
// And imported from core index.cjs might need repoPath.
|
|
449
|
-
|
|
450
|
-
// Let's manually implement it to be safe and use getReqPathOrExit's resolved path
|
|
451
|
-
// OR use the core helper but pass the correct repo path.
|
|
452
|
-
const { repoPath } = await getReqPathOrExit();
|
|
453
|
-
const coreResult = await getCurrentRequirement(repoPath);
|
|
454
|
-
console.log(chalk.cyan(coreResult));
|
|
455
|
-
|
|
456
|
-
} catch (error) {
|
|
457
|
-
console.error(chalk.red('Error getting working requirement:'), error.message);
|
|
458
|
-
process.exit(1);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
async function numberAll() {
|
|
463
|
-
try {
|
|
464
|
-
const { reqPath, repoPath } = await getReqPathOrExit();
|
|
465
|
-
if (!await fs.pathExists(reqPath)) {
|
|
466
|
-
console.log(chalk.yellow('No REQUIREMENTS.md found.'));
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
console.log(chalk.cyan('Numbering all existing requirements...'));
|
|
471
|
-
const { numberAllRequirements } = require('vibecodingmachine-core');
|
|
472
|
-
const numbered = await numberAllRequirements(reqPath, repoPath);
|
|
473
|
-
|
|
474
|
-
console.log(chalk.green(`✓ Numbered ${numbered} requirements`));
|
|
475
|
-
} catch (error) {
|
|
476
|
-
console.error(chalk.red('Error numbering requirements:'), error.message);
|
|
477
|
-
process.exit(1);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// Default Requirement Management Functions
|
|
482
|
-
|
|
483
1
|
/**
|
|
484
|
-
*
|
|
2
|
+
* Refactored Requirements Commands
|
|
3
|
+
*
|
|
4
|
+
* Splits monolithic requirements command into modular components
|
|
485
5
|
*/
|
|
486
|
-
async function createDefault(title, options = {}) {
|
|
487
|
-
const { description, maxIterations } = options;
|
|
488
|
-
const repoPath = await getRepoPath();
|
|
489
|
-
|
|
490
|
-
if (!repoPath) {
|
|
491
|
-
console.log(chalk.red('✗ No repository configured'));
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
if (!title) {
|
|
496
|
-
console.log(chalk.red('✗ Title is required'));
|
|
497
|
-
console.log(chalk.gray('Usage: vcm req:default create "Title" --description "Description" --max-iterations 10'));
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
6
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const requirement = await manager.create({
|
|
514
|
-
title: title.trim(),
|
|
515
|
-
description: description.trim(),
|
|
516
|
-
maxIterations: iterations
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
console.log(chalk.bold.green('✅ Default requirement created'));
|
|
520
|
-
console.log(chalk.cyan(` Title: ${requirement.title}`));
|
|
521
|
-
console.log(chalk.cyan(` Description: ${requirement.description}`));
|
|
522
|
-
console.log(chalk.cyan(` Max Iterations: ${requirement.maxIterations}`));
|
|
523
|
-
console.log(chalk.cyan(` Status: ${requirement.status}`));
|
|
524
|
-
console.log();
|
|
525
|
-
} catch (error) {
|
|
526
|
-
console.log(chalk.red('✗ Failed to create default requirement:'), error.message);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
/**
|
|
531
|
-
* Edit existing default requirement
|
|
532
|
-
*/
|
|
533
|
-
async function editDefault(options = {}) {
|
|
534
|
-
const { title, description, maxIterations } = options;
|
|
535
|
-
const repoPath = await getRepoPath();
|
|
536
|
-
|
|
537
|
-
if (!repoPath) {
|
|
538
|
-
console.log(chalk.red('✗ No repository configured'));
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
if (!title && !description && !maxIterations) {
|
|
543
|
-
console.log(chalk.red('✗ At least one field to update is required'));
|
|
544
|
-
console.log(chalk.gray('Usage: vcm req:default edit --title "New Title" --description "New Description" --max-iterations 20'));
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
try {
|
|
549
|
-
const storage = new JSONStorage();
|
|
550
|
-
const manager = new DefaultRequirementManager(storage);
|
|
551
|
-
|
|
552
|
-
const updates = {};
|
|
553
|
-
if (title !== undefined) updates.title = title.trim();
|
|
554
|
-
if (description !== undefined) updates.description = description.trim();
|
|
555
|
-
if (maxIterations !== undefined) updates.maxIterations = maxIterations;
|
|
556
|
-
|
|
557
|
-
const requirement = await manager.edit(updates);
|
|
558
|
-
|
|
559
|
-
console.log(chalk.bold.green('✅ Default requirement updated'));
|
|
560
|
-
console.log(chalk.cyan(` Title: ${requirement.title}`));
|
|
561
|
-
console.log(chalk.cyan(` Description: ${requirement.description}`));
|
|
562
|
-
console.log(chalk.cyan(` Max Iterations: ${requirement.maxIterations}`));
|
|
563
|
-
console.log(chalk.cyan(` Status: ${requirement.status}`));
|
|
564
|
-
if (requirement.updatedAt) {
|
|
565
|
-
console.log(chalk.gray(` Updated: ${new Date(requirement.updatedAt).toLocaleString()}`));
|
|
566
|
-
}
|
|
567
|
-
console.log();
|
|
568
|
-
} catch (error) {
|
|
569
|
-
console.log(chalk.red('✗ Failed to edit default requirement:'), error.message);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* Delete default requirement
|
|
575
|
-
*/
|
|
576
|
-
async function deleteDefault() {
|
|
577
|
-
const repoPath = await getRepoPath();
|
|
578
|
-
|
|
579
|
-
if (!repoPath) {
|
|
580
|
-
console.log(chalk.red('✗ No repository configured'));
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
try {
|
|
585
|
-
const storage = new JSONStorage();
|
|
586
|
-
const manager = new DefaultRequirementManager(storage);
|
|
587
|
-
|
|
588
|
-
const status = await manager.getStatus();
|
|
589
|
-
if (!status) {
|
|
590
|
-
console.log(chalk.yellow('⚠️ No default requirement exists'));
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
console.log(chalk.bold.yellow(`Deleting default requirement: ${status.title}`));
|
|
595
|
-
|
|
596
|
-
await manager.delete();
|
|
597
|
-
|
|
598
|
-
console.log(chalk.bold.green('✅ Default requirement deleted'));
|
|
599
|
-
console.log();
|
|
600
|
-
} catch (error) {
|
|
601
|
-
console.log(chalk.red('✗ Failed to delete default requirement:'), error.message);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* Show current default requirement status
|
|
607
|
-
*/
|
|
608
|
-
async function showDefault() {
|
|
609
|
-
const repoPath = await getRepoPath();
|
|
610
|
-
|
|
611
|
-
if (!repoPath) {
|
|
612
|
-
console.log(chalk.red('✗ No repository configured'));
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
try {
|
|
617
|
-
const storage = new JSONStorage();
|
|
618
|
-
const manager = new DefaultRequirementManager(storage);
|
|
619
|
-
|
|
620
|
-
const status = await manager.getStatus();
|
|
621
|
-
if (!status) {
|
|
622
|
-
console.log(chalk.yellow('⚠️ No default requirement exists'));
|
|
623
|
-
console.log(chalk.gray('Create one with: vcm req:default create "Title" --description "Description" --max-iterations 10'));
|
|
624
|
-
console.log();
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
const statistics = await manager.getStatistics();
|
|
629
|
-
|
|
630
|
-
console.log(chalk.bold.cyan('📋 Default Requirement Status'));
|
|
631
|
-
console.log(chalk.gray('─'.repeat(50)));
|
|
632
|
-
console.log(chalk.cyan(`Title: ${status.title}`));
|
|
633
|
-
console.log(chalk.cyan(`Description: ${status.description}`));
|
|
634
|
-
console.log(chalk.cyan(`Status: ${status.status}`));
|
|
635
|
-
console.log(chalk.cyan(`Iterations: ${status.iterationCount}/${status.maxIterations}`));
|
|
636
|
-
console.log(chalk.cyan(`Consecutive Failures: ${status.consecutiveFailures}`));
|
|
637
|
-
|
|
638
|
-
if (statistics) {
|
|
639
|
-
console.log(chalk.cyan(`Completion: ${statistics.completionPercentage}%`));
|
|
640
|
-
console.log(chalk.cyan(`Age: ${statistics.ageHours}h`));
|
|
641
|
-
if (statistics.iterationsPerHour > 0) {
|
|
642
|
-
console.log(chalk.cyan(`Rate: ${statistics.iterationsPerHour} iterations/hour`));
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
if (status.createdAt) {
|
|
647
|
-
console.log(chalk.gray(`Created: ${new Date(status.createdAt).toLocaleString()}`));
|
|
648
|
-
}
|
|
649
|
-
if (status.lastIterationAt) {
|
|
650
|
-
console.log(chalk.gray(`Last iteration: ${new Date(status.lastIterationAt).toLocaleString()}`));
|
|
651
|
-
}
|
|
652
|
-
if (status.pausedAt) {
|
|
653
|
-
console.log(chalk.yellow(`Paused: ${new Date(status.pausedAt).toLocaleString()}`));
|
|
654
|
-
}
|
|
655
|
-
if (status.resumedAt) {
|
|
656
|
-
console.log(chalk.green(`Resumed: ${new Date(status.resumedAt).toLocaleString()}`));
|
|
657
|
-
}
|
|
658
|
-
if (status.completedAt) {
|
|
659
|
-
console.log(chalk.green(`Completed: ${new Date(status.completedAt).toLocaleString()}`));
|
|
660
|
-
console.log(chalk.green(`Reason: ${status.completionReason}`));
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
console.log();
|
|
664
|
-
} catch (error) {
|
|
665
|
-
console.log(chalk.red('✗ Failed to get default requirement status:'), error.message);
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
/**
|
|
670
|
-
* Pause default requirement
|
|
671
|
-
*/
|
|
672
|
-
async function pauseDefault() {
|
|
673
|
-
const repoPath = await getRepoPath();
|
|
674
|
-
|
|
675
|
-
if (!repoPath) {
|
|
676
|
-
console.log(chalk.red('✗ No repository configured'));
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
try {
|
|
681
|
-
const storage = new JSONStorage();
|
|
682
|
-
const manager = new DefaultRequirementManager(storage);
|
|
683
|
-
|
|
684
|
-
const status = await manager.getStatus();
|
|
685
|
-
if (!status) {
|
|
686
|
-
console.log(chalk.yellow('⚠️ No default requirement exists'));
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
if (status.status === 'PAUSED') {
|
|
691
|
-
console.log(chalk.yellow('⚠️ Default requirement is already paused'));
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
await manager.pause();
|
|
696
|
-
|
|
697
|
-
console.log(chalk.bold.green('✅ Default requirement paused'));
|
|
698
|
-
console.log(chalk.cyan(` Title: ${status.title}`));
|
|
699
|
-
console.log();
|
|
700
|
-
} catch (error) {
|
|
701
|
-
console.log(chalk.red('✗ Failed to pause default requirement:'), error.message);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* Resume default requirement
|
|
707
|
-
*/
|
|
708
|
-
async function resumeDefault() {
|
|
709
|
-
const repoPath = await getRepoPath();
|
|
710
|
-
|
|
711
|
-
if (!repoPath) {
|
|
712
|
-
console.log(chalk.red('✗ No repository configured'));
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
try {
|
|
717
|
-
const storage = new JSONStorage();
|
|
718
|
-
const manager = new DefaultRequirementManager(storage);
|
|
719
|
-
|
|
720
|
-
const status = await manager.getStatus();
|
|
721
|
-
if (!status) {
|
|
722
|
-
console.log(chalk.yellow('⚠️ No default requirement exists'));
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
if (status.status === 'ACTIVE') {
|
|
727
|
-
console.log(chalk.yellow('⚠️ Default requirement is already active'));
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
await manager.resume();
|
|
732
|
-
|
|
733
|
-
console.log(chalk.bold.green('✅ Default requirement resumed'));
|
|
734
|
-
console.log(chalk.cyan(` Title: ${status.title}`));
|
|
735
|
-
console.log();
|
|
736
|
-
} catch (error) {
|
|
737
|
-
console.log(chalk.red('✗ Failed to resume default requirement:'), error.message);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
/**
|
|
742
|
-
* Set max iterations for default requirement
|
|
743
|
-
*/
|
|
744
|
-
async function setMaxIterations(maxIterations) {
|
|
745
|
-
const repoPath = await getRepoPath();
|
|
746
|
-
|
|
747
|
-
if (!repoPath) {
|
|
748
|
-
console.log(chalk.red('✗ No repository configured'));
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
if (!maxIterations || maxIterations < 1 || maxIterations > 1000) {
|
|
753
|
-
console.log(chalk.red('✗ Max iterations must be between 1 and 1000'));
|
|
754
|
-
return;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
try {
|
|
758
|
-
const storage = new JSONStorage();
|
|
759
|
-
const manager = new DefaultRequirementManager(storage);
|
|
760
|
-
|
|
761
|
-
const requirement = await manager.edit({ maxIterations });
|
|
7
|
+
// Import command modules
|
|
8
|
+
const {
|
|
9
|
+
list,
|
|
10
|
+
add,
|
|
11
|
+
current,
|
|
12
|
+
next,
|
|
13
|
+
edit,
|
|
14
|
+
watch,
|
|
15
|
+
rename,
|
|
16
|
+
working,
|
|
17
|
+
numberAll
|
|
18
|
+
} = require('./requirements/commands');
|
|
762
19
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
20
|
+
const {
|
|
21
|
+
createDefault,
|
|
22
|
+
editDefault,
|
|
23
|
+
deleteDefault,
|
|
24
|
+
showDefault,
|
|
25
|
+
pauseDefault,
|
|
26
|
+
resumeDefault,
|
|
27
|
+
setMaxIterations
|
|
28
|
+
} = require('./requirements/default-handlers');
|
|
771
29
|
|
|
30
|
+
// Export all commands for compatibility
|
|
772
31
|
module.exports = {
|
|
32
|
+
// Basic requirement commands
|
|
773
33
|
list,
|
|
774
34
|
add,
|
|
775
35
|
current,
|
|
776
|
-
working,
|
|
777
36
|
next,
|
|
778
37
|
edit,
|
|
779
38
|
watch,
|
|
780
39
|
rename,
|
|
40
|
+
working,
|
|
781
41
|
numberAll,
|
|
42
|
+
|
|
782
43
|
// Default requirement commands
|
|
44
|
+
'default:create': createDefault,
|
|
45
|
+
'default:edit': editDefault,
|
|
46
|
+
'default:delete': deleteDefault,
|
|
47
|
+
'default:show': showDefault,
|
|
48
|
+
'default:pause': pauseDefault,
|
|
49
|
+
'default:resume': resumeDefault,
|
|
50
|
+
'default:max-iterations': setMaxIterations,
|
|
51
|
+
|
|
52
|
+
// Aliases for backward compatibility
|
|
783
53
|
createDefault,
|
|
784
54
|
editDefault,
|
|
785
|
-
|
|
55
|
+
deleteDefault,
|
|
786
56
|
showDefault,
|
|
787
57
|
pauseDefault,
|
|
788
58
|
resumeDefault,
|
|
789
59
|
setMaxIterations
|
|
790
60
|
};
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|