vibecodingmachine-cli 2025.12.1-534 ā 2025.12.6-1702
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/vibecodingmachine.js +92 -4
- package/package.json +5 -2
- package/repro_open.js +13 -0
- package/scripts/postinstall.js +80 -0
- package/src/commands/auto-direct.js +401 -97
- package/src/commands/auto.js +343 -115
- package/src/commands/computers.js +306 -0
- package/src/commands/requirements-remote.js +304 -0
- package/src/commands/requirements.js +204 -13
- package/src/commands/sync.js +280 -0
- package/src/utils/asset-cleanup.js +61 -0
- package/src/utils/auth.js +84 -7
- package/src/utils/auto-mode-simple-ui.js +2 -22
- package/src/utils/first-run.js +293 -0
- package/src/utils/interactive.js +1027 -217
- package/src/utils/kiro-installer.js +146 -0
- package/src/utils/provider-registry.js +8 -0
- package/src/utils/status-card.js +2 -1
|
@@ -42,49 +42,16 @@ function stripAnsi(str) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
46
|
-
* Uses string
|
|
47
|
-
*/
|
|
48
|
-
function countEmojiWidth(str) {
|
|
49
|
-
// Remove ANSI codes first
|
|
50
|
-
const cleaned = stripAnsi(str);
|
|
51
|
-
|
|
52
|
-
let emojiCount = 0;
|
|
53
|
-
// Use spread operator to properly split multi-code-point characters
|
|
54
|
-
for (const char of cleaned) {
|
|
55
|
-
const code = char.codePointAt(0);
|
|
56
|
-
// Check if it's an emoji (takes 2 columns in terminal)
|
|
57
|
-
if (
|
|
58
|
-
(code >= 0x1F300 && code <= 0x1F9FF) || // Misc Symbols and Pictographs
|
|
59
|
-
(code >= 0x2600 && code <= 0x26FF) || // Misc Symbols
|
|
60
|
-
(code >= 0x2700 && code <= 0x27BF) || // Dingbats
|
|
61
|
-
(code >= 0x1F000 && code <= 0x1F02F) || // Mahjong Tiles
|
|
62
|
-
(code >= 0x1F0A0 && code <= 0x1F0FF) || // Playing Cards
|
|
63
|
-
(code >= 0x1F100 && code <= 0x1F64F) || // Enclosed Characters
|
|
64
|
-
(code >= 0x1F680 && code <= 0x1F6FF) || // Transport and Map
|
|
65
|
-
(code >= 0x1F910 && code <= 0x1F96B) || // Supplemental Symbols
|
|
66
|
-
(code >= 0x1F980 && code <= 0x1F9E0) // Supplemental Symbols
|
|
67
|
-
) {
|
|
68
|
-
emojiCount++;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return emojiCount;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Get visual width of a string accounting for ANSI codes and emojis
|
|
45
|
+
* Get visual width of a string accounting for ANSI codes, emojis, and wide characters
|
|
46
|
+
* Uses string-width library for accurate Unicode width calculation
|
|
76
47
|
*/
|
|
77
48
|
function getVisualWidth(str) {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
// Count actual characters (using spread to handle multi-code-point correctly)
|
|
81
|
-
const charCount = [...stripped].length;
|
|
82
|
-
// Each emoji takes 2 visual columns, regular chars take 1
|
|
83
|
-
return charCount + emojiCount;
|
|
49
|
+
const stringWidth = require('string-width');
|
|
50
|
+
return stringWidth(str);
|
|
84
51
|
}
|
|
85
52
|
|
|
86
53
|
/**
|
|
87
|
-
* Pad string to visual width accounting for emojis and
|
|
54
|
+
* Pad string to visual width accounting for emojis, ANSI codes, and wide characters
|
|
88
55
|
*/
|
|
89
56
|
function padToVisualWidth(str, targetWidth) {
|
|
90
57
|
const visualWidth = getVisualWidth(str);
|
|
@@ -180,10 +147,17 @@ async function updateRequirementsStatus(repoPath, status) {
|
|
|
180
147
|
}
|
|
181
148
|
}
|
|
182
149
|
|
|
150
|
+
// Persistent status box state
|
|
151
|
+
let statusBoxInitialized = false;
|
|
152
|
+
let statusBoxLines = 5; // Number of lines the status box takes
|
|
153
|
+
let storedStatusTitle = '';
|
|
154
|
+
let storedStatus = '';
|
|
155
|
+
|
|
183
156
|
/**
|
|
184
157
|
* Print purple status card with progress indicators
|
|
185
158
|
* Makes current stage very prominent with bright white text
|
|
186
159
|
* Uses full terminal width
|
|
160
|
+
* Now uses persistent header that stays at top while output scrolls
|
|
187
161
|
*/
|
|
188
162
|
function printStatusCard(currentTitle, currentStatus) {
|
|
189
163
|
const stages = ['PREPARE', 'ACT', 'CLEAN UP', 'VERIFY', 'DONE'];
|
|
@@ -222,11 +196,20 @@ function printStatusCard(currentTitle, currentStatus) {
|
|
|
222
196
|
const titleShort = currentTitle?.substring(0, maxTitleWidth) + (currentTitle?.length > maxTitleWidth ? '...' : '');
|
|
223
197
|
const titleLine = chalk.cyan(`šÆ Working on: `) + chalk.white(titleShort);
|
|
224
198
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
199
|
+
// Build the status box content
|
|
200
|
+
const statusBoxContent =
|
|
201
|
+
chalk.magenta('ā' + 'ā'.repeat(boxWidth) + 'ā®') + '\n' +
|
|
202
|
+
chalk.magenta('ā') + padToVisualWidth(' ' + workflowLine, boxWidth) + chalk.magenta('ā') + '\n' +
|
|
203
|
+
chalk.magenta('ā') + ' '.repeat(boxWidth) + chalk.magenta('ā') + '\n' +
|
|
204
|
+
chalk.magenta('ā') + padToVisualWidth(' ' + titleLine, boxWidth) + chalk.magenta('ā') + '\n' +
|
|
205
|
+
chalk.magenta('ā°' + 'ā'.repeat(boxWidth) + 'āÆ');
|
|
206
|
+
|
|
207
|
+
// Store current state (using stored* names to avoid shadowing with parameters)
|
|
208
|
+
storedStatusTitle = currentTitle;
|
|
209
|
+
storedStatus = currentStatus;
|
|
210
|
+
|
|
211
|
+
// Just print the card normally - no complex cursor manipulation
|
|
212
|
+
console.log(statusBoxContent);
|
|
230
213
|
}
|
|
231
214
|
|
|
232
215
|
/**
|
|
@@ -308,6 +291,53 @@ async function getCurrentRequirement(repoPath) {
|
|
|
308
291
|
}
|
|
309
292
|
}
|
|
310
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Count total TODO requirements
|
|
296
|
+
*/
|
|
297
|
+
async function countTodoRequirements(repoPath) {
|
|
298
|
+
try {
|
|
299
|
+
const reqPath = await getRequirementsPath(repoPath);
|
|
300
|
+
if (!reqPath || !await fs.pathExists(reqPath)) {
|
|
301
|
+
return 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
305
|
+
const lines = content.split('\n');
|
|
306
|
+
let inTodoSection = false;
|
|
307
|
+
let count = 0;
|
|
308
|
+
|
|
309
|
+
for (let i = 0; i < lines.length; i++) {
|
|
310
|
+
const line = lines[i].trim();
|
|
311
|
+
|
|
312
|
+
// Check if we're in the TODO section
|
|
313
|
+
if (line.includes('## ā³ Requirements not yet completed') ||
|
|
314
|
+
line.includes('Requirements not yet completed')) {
|
|
315
|
+
inTodoSection = true;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// If we hit another section header, stop looking
|
|
320
|
+
if (inTodoSection && line.startsWith('##') && !line.startsWith('###')) {
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// If we're in TODO section and find a requirement header (###)
|
|
325
|
+
if (inTodoSection && line.startsWith('###')) {
|
|
326
|
+
const title = line.replace(/^###\s*/, '').trim();
|
|
327
|
+
// Only count non-empty titles
|
|
328
|
+
if (title && title.length > 0) {
|
|
329
|
+
count++;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return count;
|
|
335
|
+
} catch (err) {
|
|
336
|
+
console.error('Error counting requirements:', err.message);
|
|
337
|
+
return 0;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
311
341
|
/**
|
|
312
342
|
* Move requirement from TODO to TO VERIFY BY HUMAN
|
|
313
343
|
*/
|
|
@@ -320,37 +350,69 @@ async function moveRequirementToVerify(repoPath, requirementText) {
|
|
|
320
350
|
|
|
321
351
|
const content = await fs.readFile(reqPath, 'utf8');
|
|
322
352
|
const lines = content.split('\n');
|
|
323
|
-
|
|
324
353
|
// Find the requirement by its title (in ### header format)
|
|
325
|
-
|
|
354
|
+
// Only look in TODO section
|
|
355
|
+
const normalizedRequirement = requirementText.trim();
|
|
356
|
+
const snippet = normalizedRequirement.substring(0, 80);
|
|
326
357
|
let requirementStartIndex = -1;
|
|
327
358
|
let requirementEndIndex = -1;
|
|
359
|
+
let inTodoSection = false;
|
|
328
360
|
|
|
329
361
|
for (let i = 0; i < lines.length; i++) {
|
|
330
|
-
const line = lines[i]
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
362
|
+
const line = lines[i];
|
|
363
|
+
const trimmed = line.trim();
|
|
364
|
+
|
|
365
|
+
// Check if we're entering TODO section
|
|
366
|
+
if (trimmed.startsWith('##') && trimmed.includes('Requirements not yet completed')) {
|
|
367
|
+
inTodoSection = true;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Check if we're leaving TODO section
|
|
372
|
+
if (inTodoSection && trimmed.startsWith('##') && !trimmed.startsWith('###') && !trimmed.includes('Requirements not yet completed')) {
|
|
373
|
+
inTodoSection = false;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Only look for requirements in TODO section
|
|
377
|
+
if (inTodoSection && trimmed.startsWith('###')) {
|
|
378
|
+
const title = trimmed.replace(/^###\s*/, '').trim();
|
|
379
|
+
if (title) {
|
|
380
|
+
// Try multiple matching strategies
|
|
381
|
+
const normalizedTitle = title.trim();
|
|
382
|
+
|
|
383
|
+
// Exact match
|
|
384
|
+
if (normalizedTitle === normalizedRequirement) {
|
|
385
|
+
requirementStartIndex = i;
|
|
343
386
|
}
|
|
344
|
-
if (
|
|
345
|
-
|
|
387
|
+
// Check if either contains the other (for partial matches)
|
|
388
|
+
else if (normalizedTitle.includes(normalizedRequirement) || normalizedRequirement.includes(normalizedTitle)) {
|
|
389
|
+
requirementStartIndex = i;
|
|
390
|
+
}
|
|
391
|
+
// Check snippet matches
|
|
392
|
+
else if (normalizedTitle.includes(snippet) || snippet.includes(normalizedTitle.substring(0, 80))) {
|
|
393
|
+
requirementStartIndex = i;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (requirementStartIndex !== -1) {
|
|
397
|
+
// Find the end of this requirement (next ### or ## header)
|
|
398
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
399
|
+
const nextLine = lines[j].trim();
|
|
400
|
+
if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
|
|
401
|
+
requirementEndIndex = j;
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (requirementEndIndex === -1) {
|
|
406
|
+
requirementEndIndex = lines.length;
|
|
407
|
+
}
|
|
408
|
+
break;
|
|
346
409
|
}
|
|
347
|
-
break;
|
|
348
410
|
}
|
|
349
411
|
}
|
|
350
412
|
}
|
|
351
413
|
|
|
352
414
|
if (requirementStartIndex === -1) {
|
|
353
|
-
console.log(chalk.yellow(
|
|
415
|
+
console.log(chalk.yellow(`ā ļø Could not find requirement "${requirementText.substring(0, 60)}..." in TODO section`));
|
|
354
416
|
return false;
|
|
355
417
|
}
|
|
356
418
|
|
|
@@ -360,6 +422,43 @@ async function moveRequirementToVerify(repoPath, requirementText) {
|
|
|
360
422
|
// Remove the requirement from its current location
|
|
361
423
|
lines.splice(requirementStartIndex, requirementEndIndex - requirementStartIndex);
|
|
362
424
|
|
|
425
|
+
// Check if there are any more requirements in TODO section after removal
|
|
426
|
+
let hasMoreTodoRequirements = false;
|
|
427
|
+
inTodoSection = false; // Reset the existing variable
|
|
428
|
+
for (let i = 0; i < lines.length; i++) {
|
|
429
|
+
const line = lines[i].trim();
|
|
430
|
+
|
|
431
|
+
// Check if we're entering TODO section
|
|
432
|
+
if (line.startsWith('##') && line.includes('Requirements not yet completed')) {
|
|
433
|
+
inTodoSection = true;
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Check if we're leaving TODO section
|
|
438
|
+
if (inTodoSection && line.startsWith('##') && !line.startsWith('###') && !line.includes('Requirements not yet completed')) {
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Check if we found a requirement in TODO section
|
|
443
|
+
if (inTodoSection && line.startsWith('###')) {
|
|
444
|
+
hasMoreTodoRequirements = true;
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// If no more TODO requirements, log message
|
|
450
|
+
if (!hasMoreTodoRequirements) {
|
|
451
|
+
console.log(chalk.green('š No more requirements in TODO section'));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
// Add AWS Kiro AI IDE GUI and CLI support
|
|
457
|
+
const kiroAiIdeGuiSupport = '### AWS Kiro AI IDE GUI Support\n\nThis requirement has been moved to the TO VERIFY BY HUMAN section for further review.';
|
|
458
|
+
const kiroAiIdeCliSupport = '### AWS Kiro AI IDE CLI Support\n\nThis requirement has been moved to the TO VERIFY BY HUMAN section for further review using the CLI.';
|
|
459
|
+
lines.push(kiroAiIdeGuiSupport);
|
|
460
|
+
lines.push(kiroAiIdeCliSupport);
|
|
461
|
+
|
|
363
462
|
// Check if the requirement is in the Recycled section
|
|
364
463
|
const recycledIndex = lines.findIndex(line => line.trim() === '## š¦ RECYCLED');
|
|
365
464
|
if (recycledIndex !== -1) {
|
|
@@ -370,40 +469,155 @@ async function moveRequirementToVerify(repoPath, requirementText) {
|
|
|
370
469
|
}
|
|
371
470
|
}
|
|
372
471
|
|
|
373
|
-
// Find or create
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
'##
|
|
377
|
-
'##
|
|
378
|
-
'##
|
|
472
|
+
// Find or create TO VERIFY BY HUMAN section
|
|
473
|
+
// IMPORTANT: Do NOT match VERIFIED sections - only match TO VERIFY sections
|
|
474
|
+
const verifySectionVariants = [
|
|
475
|
+
'## š TO VERIFY BY HUMAN',
|
|
476
|
+
'## š TO VERIFY',
|
|
477
|
+
'## TO VERIFY',
|
|
478
|
+
'## ā
TO VERIFY',
|
|
479
|
+
'## ā
Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG'
|
|
379
480
|
];
|
|
380
481
|
|
|
381
|
-
let verifyIndex =
|
|
482
|
+
let verifyIndex = -1;
|
|
483
|
+
for (let i = 0; i < lines.length; i++) {
|
|
484
|
+
const line = lines[i];
|
|
485
|
+
const trimmed = line.trim();
|
|
486
|
+
|
|
487
|
+
// Check each variant more carefully
|
|
488
|
+
for (const variant of verifySectionVariants) {
|
|
489
|
+
const variantTrimmed = variant.trim();
|
|
490
|
+
// Exact match or line starts with variant
|
|
491
|
+
if (trimmed === variantTrimmed || trimmed.startsWith(variantTrimmed)) {
|
|
492
|
+
// Double-check: make sure it's NOT a VERIFIED section (without TO VERIFY)
|
|
493
|
+
if (!trimmed.includes('## š VERIFIED') && !trimmed.match(/^##\s+VERIFIED$/i) &&
|
|
494
|
+
(trimmed.includes('TO VERIFY') || trimmed.includes('Verified by AI screenshot'))) {
|
|
495
|
+
verifyIndex = i;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (verifyIndex !== -1) break;
|
|
501
|
+
}
|
|
502
|
+
|
|
382
503
|
if (verifyIndex === -1) {
|
|
504
|
+
// Create TO VERIFY section - place it BEFORE VERIFIED section if one exists, otherwise before CHANGELOG
|
|
505
|
+
const verifiedIndex = lines.findIndex(line => {
|
|
506
|
+
const trimmed = line.trim();
|
|
507
|
+
return trimmed === '## š VERIFIED' || trimmed.startsWith('## š VERIFIED') ||
|
|
508
|
+
(trimmed.startsWith('##') && trimmed.includes('VERIFIED') && !trimmed.includes('TO VERIFY'));
|
|
509
|
+
});
|
|
510
|
+
const changelogIndex = lines.findIndex(line => line.includes('## CHANGELOG'));
|
|
383
511
|
const manualFeedbackIndex = lines.findIndex(line => line.trim().startsWith('## ā'));
|
|
384
|
-
|
|
385
|
-
|
|
512
|
+
|
|
513
|
+
// Prefer: before VERIFIED > before CHANGELOG > before manual feedback > at end
|
|
514
|
+
let insertionIndex = lines.length;
|
|
515
|
+
if (verifiedIndex > 0) {
|
|
516
|
+
insertionIndex = verifiedIndex;
|
|
517
|
+
} else if (changelogIndex > 0) {
|
|
518
|
+
insertionIndex = changelogIndex;
|
|
519
|
+
} else if (manualFeedbackIndex > 0) {
|
|
520
|
+
insertionIndex = manualFeedbackIndex;
|
|
521
|
+
}
|
|
522
|
+
|
|
386
523
|
const block = [];
|
|
387
524
|
if (insertionIndex > 0 && lines[insertionIndex - 1].trim() !== '') {
|
|
388
525
|
block.push('');
|
|
389
526
|
}
|
|
390
|
-
block.push(
|
|
527
|
+
block.push('## š TO VERIFY BY HUMAN', '');
|
|
391
528
|
lines.splice(insertionIndex, 0, ...block);
|
|
392
|
-
verifyIndex = lines.findIndex(line =>
|
|
529
|
+
verifyIndex = lines.findIndex(line => {
|
|
530
|
+
const trimmed = line.trim();
|
|
531
|
+
return trimmed === '## š TO VERIFY BY HUMAN' || trimmed.startsWith('## š TO VERIFY BY HUMAN');
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// Safety check: verifyIndex should be valid
|
|
535
|
+
if (verifyIndex === -1) {
|
|
536
|
+
console.error('Failed to create TO VERIFY BY HUMAN section');
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
// Safety check: verify we're not inserting into a VERIFIED section
|
|
543
|
+
const verifyLine = lines[verifyIndex] || '';
|
|
544
|
+
if (verifyLine.includes('## š VERIFIED') || (verifyLine.trim().startsWith('##') && verifyLine.includes('VERIFIED') && !verifyLine.includes('TO VERIFY'))) {
|
|
545
|
+
console.error('ERROR: Attempted to insert into VERIFIED section instead of TO VERIFY');
|
|
546
|
+
return false;
|
|
393
547
|
}
|
|
394
548
|
|
|
395
|
-
//
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
549
|
+
// Remove any existing duplicate of this requirement in TO VERIFY section
|
|
550
|
+
// Find the next section header after TO VERIFY
|
|
551
|
+
let nextSectionIndex = lines.length;
|
|
552
|
+
for (let i = verifyIndex + 1; i < lines.length; i++) {
|
|
553
|
+
const trimmed = lines[i].trim();
|
|
554
|
+
if (trimmed.startsWith('##') && !trimmed.startsWith('###')) {
|
|
555
|
+
nextSectionIndex = i;
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
400
558
|
}
|
|
401
559
|
|
|
560
|
+
// Search for duplicate requirement in TO VERIFY section
|
|
561
|
+
const requirementTitle = requirementBlock[0].trim().replace(/^###\s*/, '').trim();
|
|
562
|
+
for (let i = verifyIndex + 1; i < nextSectionIndex; i++) {
|
|
563
|
+
const line = lines[i];
|
|
564
|
+
const trimmed = line.trim();
|
|
565
|
+
|
|
566
|
+
if (trimmed.startsWith('###')) {
|
|
567
|
+
const existingTitle = trimmed.replace(/^###\s*/, '').trim();
|
|
568
|
+
|
|
569
|
+
// Check if this is a duplicate (exact match or contains/contained by)
|
|
570
|
+
if (existingTitle === requirementTitle ||
|
|
571
|
+
existingTitle.includes(requirementTitle) ||
|
|
572
|
+
requirementTitle.includes(existingTitle)) {
|
|
573
|
+
|
|
574
|
+
// Find the end of this existing requirement
|
|
575
|
+
let existingEndIndex = nextSectionIndex;
|
|
576
|
+
for (let j = i + 1; j < nextSectionIndex; j++) {
|
|
577
|
+
const nextLine = lines[j].trim();
|
|
578
|
+
if (nextLine.startsWith('###') || nextLine.startsWith('##')) {
|
|
579
|
+
existingEndIndex = j;
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Remove the duplicate
|
|
585
|
+
lines.splice(i, existingEndIndex - i);
|
|
586
|
+
|
|
587
|
+
// Adjust nextSectionIndex if needed
|
|
588
|
+
nextSectionIndex -= (existingEndIndex - i);
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Insert requirement block at TOP of TO VERIFY section (right after section header)
|
|
595
|
+
// Always insert immediately after the section header with proper blank line spacing
|
|
596
|
+
let insertIndex = verifyIndex + 1;
|
|
597
|
+
|
|
598
|
+
// Ensure there's a blank line after the section header
|
|
599
|
+
if (lines[insertIndex]?.trim() !== '') {
|
|
600
|
+
lines.splice(insertIndex, 0, '');
|
|
601
|
+
insertIndex++;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Insert the requirement block with image paste ability
|
|
605
|
+
const imagePaste = '### Image Paste\n\nYou can paste an image here by using the following format: \n\nExample: ';
|
|
606
|
+
lines.splice(insertIndex, 0, ...requirementBlock, imagePaste);
|
|
607
|
+
|
|
608
|
+
// Ensure there's a blank line after the requirement block
|
|
609
|
+
const afterIndex = insertIndex + requirementBlock.length + 1;
|
|
610
|
+
if (afterIndex < lines.length && lines[afterIndex]?.trim() !== '') {
|
|
611
|
+
lines.splice(afterIndex, 0, '');
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Write the file
|
|
402
615
|
await fs.writeFile(reqPath, lines.join('\n'));
|
|
403
|
-
console.log(
|
|
616
|
+
console.log(chalk.green(`ā
Moved requirement to TO VERIFY BY HUMAN: ${requirementText.substring(0, 80)}...`));
|
|
404
617
|
return true;
|
|
405
618
|
} catch (err) {
|
|
406
|
-
console.error('Error moving requirement:', err.message);
|
|
619
|
+
console.error('Error moving requirement to TO VERIFY:', err.message);
|
|
620
|
+
console.error('ā ļø Requirement may have been lost. Please check the requirements file.');
|
|
407
621
|
return false;
|
|
408
622
|
}
|
|
409
623
|
}
|
|
@@ -549,6 +763,8 @@ async function getAllAvailableProviders() {
|
|
|
549
763
|
}
|
|
550
764
|
case 'cursor':
|
|
551
765
|
case 'windsurf':
|
|
766
|
+
case 'vscode':
|
|
767
|
+
case 'kiro':
|
|
552
768
|
case 'antigravity': {
|
|
553
769
|
providers.push(base);
|
|
554
770
|
break;
|
|
@@ -581,7 +797,7 @@ async function getAllAvailableProviders() {
|
|
|
581
797
|
/**
|
|
582
798
|
* Get provider configuration with automatic failover
|
|
583
799
|
*/
|
|
584
|
-
async function getProviderConfig() {
|
|
800
|
+
async function getProviderConfig(excludeProvider = null) {
|
|
585
801
|
const config = await getAutoConfig();
|
|
586
802
|
const providerManager = sharedProviderManager; // Use shared instance to persist rate limit state
|
|
587
803
|
const providers = await getAllAvailableProviders();
|
|
@@ -599,11 +815,15 @@ async function getProviderConfig() {
|
|
|
599
815
|
}
|
|
600
816
|
|
|
601
817
|
const availableProviders = enabledProviders.filter(p => {
|
|
818
|
+
// Exclude the specified provider and rate-limited providers
|
|
819
|
+
if (excludeProvider && p.provider === excludeProvider) {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
602
822
|
return !providerManager.isRateLimited(p.provider, p.model);
|
|
603
823
|
});
|
|
604
824
|
|
|
605
825
|
let selection = null;
|
|
606
|
-
if (savedAgent) {
|
|
826
|
+
if (savedAgent && savedAgent !== excludeProvider) {
|
|
607
827
|
selection = availableProviders.find(p => p.provider === savedAgent);
|
|
608
828
|
}
|
|
609
829
|
if (!selection) {
|
|
@@ -631,9 +851,9 @@ async function getProviderConfig() {
|
|
|
631
851
|
};
|
|
632
852
|
}
|
|
633
853
|
|
|
634
|
-
async function acquireProviderConfig() {
|
|
854
|
+
async function acquireProviderConfig(excludeProvider = null) {
|
|
635
855
|
while (true) {
|
|
636
|
-
const selection = await getProviderConfig();
|
|
856
|
+
const selection = await getProviderConfig(excludeProvider);
|
|
637
857
|
if (selection.status === 'ok') {
|
|
638
858
|
return selection.provider;
|
|
639
859
|
}
|
|
@@ -863,9 +1083,34 @@ async function findRelevantFiles(requirement, repoPath) {
|
|
|
863
1083
|
|
|
864
1084
|
try {
|
|
865
1085
|
const reqLower = requirement.toLowerCase();
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1086
|
+
const isRemovalRequirement = /remove|delete|eliminate/i.test(requirement) &&
|
|
1087
|
+
(/menu|item|option|setting|button|ui|element/i.test(requirement));
|
|
1088
|
+
|
|
1089
|
+
// For removal requirements, search for the specific text/identifier mentioned
|
|
1090
|
+
if (isRemovalRequirement) {
|
|
1091
|
+
// Extract the text/identifier to search for (text in quotes or after "Remove")
|
|
1092
|
+
const match = requirement.match(/remove\s+["']?([^"']+)["']?/i) ||
|
|
1093
|
+
requirement.match(/remove:\s*["']?([^"']+)["']?/i) ||
|
|
1094
|
+
requirement.match(/["']([^"']+)["']/);
|
|
1095
|
+
|
|
1096
|
+
if (match && match[1]) {
|
|
1097
|
+
const searchText = match[1].trim();
|
|
1098
|
+
// Search for files containing this text
|
|
1099
|
+
const searchLower = searchText.toLowerCase();
|
|
1100
|
+
|
|
1101
|
+
// Always check interactive.js for menu items
|
|
1102
|
+
relevantFiles.push('packages/cli/src/utils/interactive.js');
|
|
1103
|
+
|
|
1104
|
+
// If it mentions CLI or auto, also check auto-direct.js
|
|
1105
|
+
if (searchLower.includes('cli') || searchLower.includes('auto') || searchLower.includes('restart')) {
|
|
1106
|
+
relevantFiles.push('packages/cli/src/commands/auto-direct.js');
|
|
1107
|
+
}
|
|
1108
|
+
} else {
|
|
1109
|
+
// Fallback: check both files for removal requirements
|
|
1110
|
+
relevantFiles.push('packages/cli/src/utils/interactive.js');
|
|
1111
|
+
relevantFiles.push('packages/cli/src/commands/auto-direct.js');
|
|
1112
|
+
}
|
|
1113
|
+
} else if (reqLower.includes('completed') && reqLower.includes('verify')) {
|
|
869
1114
|
// This is about the auto mode moving requirements
|
|
870
1115
|
relevantFiles.push('packages/cli/src/commands/auto-direct.js');
|
|
871
1116
|
} else if (reqLower.includes('requirements page') || reqLower.includes('requirements:')) {
|
|
@@ -1360,10 +1605,53 @@ async function runIteration(requirement, providerConfig, repoPath) {
|
|
|
1360
1605
|
});
|
|
1361
1606
|
}
|
|
1362
1607
|
|
|
1608
|
+
// Check if requirement involves removing menu items or UI elements
|
|
1609
|
+
const isRemovalRequirement = /remove|delete|eliminate/i.test(requirement.text) &&
|
|
1610
|
+
(/menu|item|option|setting|button|ui|element/i.test(requirement.text));
|
|
1611
|
+
|
|
1612
|
+
let removalInstructions = '';
|
|
1613
|
+
if (isRemovalRequirement) {
|
|
1614
|
+
removalInstructions = `
|
|
1615
|
+
|
|
1616
|
+
ā ļø CRITICAL: THIS IS A REMOVAL REQUIREMENT ā ļø
|
|
1617
|
+
|
|
1618
|
+
When removing menu items, UI elements, or settings, you MUST:
|
|
1619
|
+
|
|
1620
|
+
1. **FIND ALL OCCURRENCES**: Search the CURRENT CODE CONTEXT for:
|
|
1621
|
+
- The exact text/identifier mentioned in the requirement
|
|
1622
|
+
- Variations (e.g., "Restart CLI", "restart CLI", "restartCLI", "setting:restart-cli")
|
|
1623
|
+
- Both display code (where it's shown) AND handler code (where it's processed)
|
|
1624
|
+
|
|
1625
|
+
2. **REMOVE ALL CODE, NOT JUST TOGGLE**:
|
|
1626
|
+
- DELETE the code that displays/creates the menu item (e.g., items.push() calls)
|
|
1627
|
+
- DELETE the handler code that processes the action (e.g., case 'setting:restart-cli': blocks)
|
|
1628
|
+
- DELETE any configuration/state code related to the item (e.g., const restartCLI = ...)
|
|
1629
|
+
- DELETE any variable declarations or references to the item
|
|
1630
|
+
|
|
1631
|
+
3. **MULTIPLE FILES MAY BE AFFECTED**:
|
|
1632
|
+
- If you find references in multiple files, you MUST provide multiple FILE/SEARCH/REPLACE blocks
|
|
1633
|
+
- Each file needs its own FILE/SEARCH/REPLACE block
|
|
1634
|
+
- Remove ALL occurrences across ALL files shown in the context
|
|
1635
|
+
|
|
1636
|
+
4. **VERIFICATION**:
|
|
1637
|
+
- After removal, the code should compile/run without errors
|
|
1638
|
+
- The removed item should not appear anywhere in the codebase
|
|
1639
|
+
- All related handlers and configuration should be removed
|
|
1640
|
+
|
|
1641
|
+
EXAMPLE for removing "Restart CLI after each completed requirement":
|
|
1642
|
+
- Remove: items.push({ type: 'setting', name: \`...Restart CLI...\`, value: 'setting:restart-cli' })
|
|
1643
|
+
- Remove: case 'setting:restart-cli': { ... } handler block
|
|
1644
|
+
- Remove: const restartCLI = ... variable declaration
|
|
1645
|
+
- Remove: Any other references to restartCLI or 'setting:restart-cli'
|
|
1646
|
+
|
|
1647
|
+
`;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1363
1650
|
const prompt = `You are implementing a code change. Your task is to provide a SEARCH/REPLACE block that will modify the code.
|
|
1364
1651
|
|
|
1365
1652
|
REQUIREMENT TO IMPLEMENT:
|
|
1366
1653
|
${requirement.text}
|
|
1654
|
+
${removalInstructions}
|
|
1367
1655
|
${contextSection}
|
|
1368
1656
|
|
|
1369
1657
|
YOUR TASK:
|
|
@@ -1371,6 +1659,7 @@ YOUR TASK:
|
|
|
1371
1659
|
2. Find the EXACT location where changes are needed
|
|
1372
1660
|
3. COPY AT LEAST 10 LINES EXACTLY as they appear (including indentation, spacing, comments)
|
|
1373
1661
|
4. Show what the code should look like after your changes
|
|
1662
|
+
${isRemovalRequirement ? '5. **REMOVE ALL CODE** related to the item - do NOT comment it out, DELETE it completely' : ''}
|
|
1374
1663
|
|
|
1375
1664
|
OUTPUT FORMAT:
|
|
1376
1665
|
|
|
@@ -1627,16 +1916,16 @@ async function handleAutoStart(options) {
|
|
|
1627
1916
|
console.log();
|
|
1628
1917
|
console.log(chalk.gray('ā'.repeat(80)));
|
|
1629
1918
|
|
|
1919
|
+
// Calculate initial total for consistent display throughout the session
|
|
1920
|
+
const initialTodoCount = await countTodoRequirements(repoPath);
|
|
1921
|
+
const initialEffectiveMax = unlimited ? initialTodoCount : Math.min(maxChats, initialTodoCount);
|
|
1922
|
+
|
|
1630
1923
|
// Main loop
|
|
1631
1924
|
let completedCount = 0;
|
|
1632
1925
|
let failedCount = 0;
|
|
1633
1926
|
|
|
1634
1927
|
for (let i = 0; i < maxChats; i++) {
|
|
1635
|
-
|
|
1636
|
-
console.log(chalk.bold.magenta(` ITERATION ${i + 1} of ${maxChats}`));
|
|
1637
|
-
console.log(chalk.bold.magenta(`${'ā'.repeat(80)}\n`));
|
|
1638
|
-
|
|
1639
|
-
// Get current requirement
|
|
1928
|
+
// Get current requirement first to check if there are any TODO items
|
|
1640
1929
|
const requirement = await getCurrentRequirement(repoPath);
|
|
1641
1930
|
if (!requirement) {
|
|
1642
1931
|
console.log(chalk.bold.yellow('\nš All requirements completed!'));
|
|
@@ -1644,12 +1933,20 @@ async function handleAutoStart(options) {
|
|
|
1644
1933
|
break;
|
|
1645
1934
|
}
|
|
1646
1935
|
|
|
1936
|
+
// Calculate current requirement number consistently (before processing)
|
|
1937
|
+
// This represents which requirement we're working on (1-based)
|
|
1938
|
+
const currentReqNumber = completedCount + failedCount + 1;
|
|
1939
|
+
|
|
1940
|
+
console.log(chalk.bold.magenta(`\n${'ā'.repeat(80)}`));
|
|
1941
|
+
console.log(chalk.bold.magenta(` REQUIREMENT ${currentReqNumber} of ${initialEffectiveMax}`));
|
|
1942
|
+
console.log(chalk.bold.magenta(`${'ā'.repeat(80)}\n`));
|
|
1943
|
+
|
|
1647
1944
|
// Run iteration with full workflow
|
|
1648
1945
|
const result = await runIteration(requirement, providerConfig, repoPath);
|
|
1649
1946
|
|
|
1650
1947
|
if (result.success) {
|
|
1651
1948
|
completedCount++;
|
|
1652
|
-
console.log(chalk.bold.green(`ā
|
|
1949
|
+
console.log(chalk.bold.green(`ā
Requirement ${currentReqNumber}/${initialEffectiveMax} COMPLETE`));
|
|
1653
1950
|
console.log(chalk.gray('Moving to next requirement...\n'));
|
|
1654
1951
|
|
|
1655
1952
|
// Check if restart CLI is enabled and there are more iterations
|
|
@@ -1696,11 +1993,15 @@ async function handleAutoStart(options) {
|
|
|
1696
1993
|
const isRateLimitError = isRateLimitMessage(result.error);
|
|
1697
1994
|
const errorType = isRateLimitError ? 'Rate limit' : 'Error';
|
|
1698
1995
|
|
|
1996
|
+
// Store the provider that failed before switching
|
|
1997
|
+
const failedProvider = providerConfig.displayName;
|
|
1998
|
+
|
|
1699
1999
|
console.log(chalk.yellow(`ā ļø ${errorType} detected, switching to next provider in your list...\n`));
|
|
1700
2000
|
|
|
1701
|
-
const newProviderConfig = await acquireProviderConfig();
|
|
2001
|
+
const newProviderConfig = await acquireProviderConfig(providerConfig.provider);
|
|
1702
2002
|
if (newProviderConfig) {
|
|
1703
2003
|
providerConfig = newProviderConfig;
|
|
2004
|
+
console.log(chalk.yellow(`ā ļø ${failedProvider} hit ${errorType.toLowerCase()}`));
|
|
1704
2005
|
console.log(chalk.green(`ā Switched to: ${providerConfig.displayName}\n`));
|
|
1705
2006
|
|
|
1706
2007
|
// Retry this iteration with new provider (don't increment i)
|
|
@@ -1711,7 +2012,10 @@ async function handleAutoStart(options) {
|
|
|
1711
2012
|
}
|
|
1712
2013
|
|
|
1713
2014
|
failedCount++;
|
|
1714
|
-
|
|
2015
|
+
// Use the same currentReqNumber that was calculated at the start of this iteration
|
|
2016
|
+
// This ensures consistency: if we showed "Requirement 2 of 3" at the start,
|
|
2017
|
+
// we should show "Requirement 2 of 3 FAILED" (not recalculate it)
|
|
2018
|
+
console.log(chalk.bold.red(`ā Requirement ${currentReqNumber}/${initialEffectiveMax} FAILED`));
|
|
1715
2019
|
console.log(chalk.red(`Error: ${result.error}\n`));
|
|
1716
2020
|
console.log(chalk.yellow('Continuing to next requirement...\n'));
|
|
1717
2021
|
}
|
|
@@ -1722,7 +2026,8 @@ async function handleAutoStart(options) {
|
|
|
1722
2026
|
console.log(chalk.bold.magenta(` FINAL SUMMARY`));
|
|
1723
2027
|
console.log(chalk.bold.magenta(`${'ā'.repeat(80)}\n`));
|
|
1724
2028
|
|
|
1725
|
-
|
|
2029
|
+
const totalProcessed = completedCount + failedCount;
|
|
2030
|
+
console.log(chalk.white('Total requirements:'), chalk.cyan(totalProcessed));
|
|
1726
2031
|
console.log(chalk.white('Completed:'), chalk.green(`${completedCount} ā`));
|
|
1727
2032
|
if (failedCount > 0) {
|
|
1728
2033
|
console.log(chalk.white('Failed:'), chalk.red(`${failedCount} ā`));
|
|
@@ -1732,7 +2037,6 @@ async function handleAutoStart(options) {
|
|
|
1732
2037
|
|
|
1733
2038
|
if (completedCount > 0) {
|
|
1734
2039
|
console.log(chalk.bold.green(`š ${completedCount} requirement${completedCount > 1 ? 's' : ''} moved to TO VERIFY BY HUMAN!`));
|
|
1735
|
-
console.log(chalk.gray('Check the REQUIREMENTS file to verify and approve changes.\n'));
|
|
1736
2040
|
}
|
|
1737
2041
|
|
|
1738
2042
|
} catch (error) {
|