vibecodingmachine-cli 2025.11.2-9.855 → 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.
@@ -42,49 +42,16 @@ function stripAnsi(str) {
42
42
  }
43
43
 
44
44
  /**
45
- * Count visual width of emojis in a string (emojis take 2 terminal columns)
46
- * Uses string iterator to properly handle multi-code-point emojis
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 stripped = stripAnsi(str);
79
- const emojiCount = countEmojiWidth(str);
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 ANSI codes
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
- console.log(chalk.magenta('\nā•­' + '─'.repeat(boxWidth) + 'ā•®'));
226
- console.log(chalk.magenta('│') + padToVisualWidth(' ' + workflowLine, boxWidth) + chalk.magenta('│'));
227
- console.log(chalk.magenta('│') + ' '.repeat(boxWidth) + chalk.magenta('│'));
228
- console.log(chalk.magenta('│') + padToVisualWidth(' ' + titleLine, boxWidth) + chalk.magenta('│'));
229
- console.log(chalk.magenta('ā•°' + '─'.repeat(boxWidth) + '╯\n'));
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
- const snippet = requirementText.substring(0, 80);
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].trim();
331
- // Check if this is the requirement header
332
- if (line.startsWith('###')) {
333
- const title = line.replace(/^###\s*/, '').trim();
334
- if (title && title.includes(snippet)) {
335
- requirementStartIndex = i;
336
- // Find the end of this requirement (next ### or ## header)
337
- for (let j = i + 1; j < lines.length; j++) {
338
- const nextLine = lines[j].trim();
339
- if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
340
- requirementEndIndex = j;
341
- break;
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 (requirementEndIndex === -1) {
345
- requirementEndIndex = lines.length;
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('āš ļø Could not find requirement in requirements file'));
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 VERIFIED section
374
- const headingVariants = [
375
- '## šŸ“ VERIFIED',
376
- '## VERIFIED',
377
- '## āœ… VERIFIED',
378
- '## āœ… VERIFIED BY HUMAN'
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 = lines.findIndex(line => headingVariants.includes(line.trim()));
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
- const headingLine = '## šŸ“ VERIFIED';
385
- const insertionIndex = manualFeedbackIndex === -1 ? lines.length : manualFeedbackIndex;
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(headingLine, '');
527
+ block.push('## šŸ” TO VERIFY BY HUMAN', '');
391
528
  lines.splice(insertionIndex, 0, ...block);
392
- verifyIndex = lines.findIndex(line => line.trim() === headingLine);
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
- // Insert the requirement block at the top of the VERIFIED section
396
- lines.splice(verifyIndex + 1, 0, ...requirementBlock);
397
- // Add blank line after if needed
398
- if (verifyIndex + 1 + requirementBlock.length < lines.length && lines[verifyIndex + 1 + requirementBlock.length].trim() !== '') {
399
- lines.splice(verifyIndex + 1 + requirementBlock.length, 0, '');
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: ![image](image-url)\n\nExample: ![image](https://example.com/image.png)';
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(`Moved requirement to VERIFIED: ${requirementText}`);
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
- // Map keywords to specific files
868
- if (reqLower.includes('completed') && reqLower.includes('verify')) {
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
- console.log(chalk.bold.magenta(`\n${'━'.repeat(80)}`));
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(`āœ… Iteration ${i + 1}/${maxChats} COMPLETE`));
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
- console.log(chalk.bold.red(`āŒ Iteration ${i + 1}/${maxChats} FAILED`));
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
- console.log(chalk.white('Total iterations:'), unlimited ? chalk.cyan('āˆž (never stop)') : chalk.cyan(maxChats));
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) {