vibecodingmachine-cli 2025.12.1-534 → 2025.12.22-2230
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 +301 -12
- package/package.json +5 -2
- package/repro_open.js +13 -0
- package/reproduce_issue.js +160 -0
- package/scripts/postinstall.js +80 -0
- package/src/commands/auth.js +0 -1
- package/src/commands/auto-direct.js +455 -136
- package/src/commands/auto.js +488 -163
- package/src/commands/computers.js +306 -0
- package/src/commands/repo.js +0 -1
- package/src/commands/requirements-remote.js +308 -0
- package/src/commands/requirements.js +233 -16
- package/src/commands/status.js +0 -1
- package/src/commands/sync.js +280 -0
- package/src/utils/agent-selector.js +50 -0
- package/src/utils/antigravity-installer.js +212 -0
- package/src/utils/antigravity-js-handler.js +60 -0
- package/src/utils/asset-cleanup.js +60 -0
- package/src/utils/auth.js +232 -8
- package/src/utils/auto-mode-ansi-ui.js +0 -1
- package/src/utils/auto-mode-simple-ui.js +3 -23
- package/src/utils/compliance-check.js +166 -0
- package/src/utils/config.js +27 -1
- package/src/utils/copy-with-progress.js +167 -0
- package/src/utils/download-with-progress.js +84 -0
- package/src/utils/first-run.js +410 -0
- package/src/utils/interactive.js +1197 -391
- package/src/utils/kiro-installer.js +178 -0
- package/src/utils/persistent-header.js +1 -3
- package/src/utils/provider-registry.js +13 -4
- package/src/utils/status-card.js +2 -1
- package/src/utils/user-tracking.js +300 -0
- package/tests/requirements-navigator-buildtree-await.test.js +28 -0
|
@@ -5,21 +5,29 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
|
-
const { DirectLLMManager } = require('vibecodingmachine-core');
|
|
9
|
-
const { getRepoPath, getAutoConfig, setAutoConfig } = require('../utils/config');
|
|
8
|
+
const { DirectLLMManager, AppleScriptManager } = require('vibecodingmachine-core');
|
|
9
|
+
const { getRepoPath, getAutoConfig, setAutoConfig, getStages, DEFAULT_STAGES } = require('../utils/config');
|
|
10
10
|
const { getRequirementsPath, readRequirements } = require('vibecodingmachine-core');
|
|
11
11
|
const fs = require('fs-extra');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { spawn } = require('child_process');
|
|
14
14
|
const chokidar = require('chokidar');
|
|
15
|
+
const stringWidth = require('string-width');
|
|
16
|
+
const { getProviderPreferences, getProviderDefinition } = require('../utils/provider-registry');
|
|
17
|
+
const { createKeyboardHandler } = require('../utils/keyboard-handler');
|
|
18
|
+
const logger = require('../utils/logger');
|
|
19
|
+
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
20
|
+
const { checkAntigravityRateLimit, handleAntigravityRateLimit } = require('../utils/antigravity-js-handler');
|
|
21
|
+
|
|
15
22
|
// Status management will use in-process tracking instead of external file
|
|
16
23
|
const CLI_ENTRY_POINT = path.join(__dirname, '../../bin/vibecodingmachine.js');
|
|
17
|
-
const { getProviderPreferences, getProviderDefinition } = require('../utils/provider-registry');
|
|
18
24
|
|
|
19
25
|
// CRITICAL: Shared ProviderManager instance to track rate limits across all function calls
|
|
20
|
-
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
21
26
|
const sharedProviderManager = new ProviderManager();
|
|
22
27
|
|
|
28
|
+
// Configured stages (will be loaded in handleAutoStart)
|
|
29
|
+
let configuredStages = DEFAULT_STAGES;
|
|
30
|
+
|
|
23
31
|
/**
|
|
24
32
|
* Get timestamp for logging
|
|
25
33
|
*/
|
|
@@ -42,49 +50,15 @@ function stripAnsi(str) {
|
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
/**
|
|
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
|
|
53
|
+
* Get visual width of a string accounting for ANSI codes, emojis, and wide characters
|
|
54
|
+
* Uses string-width library for accurate Unicode width calculation
|
|
76
55
|
*/
|
|
77
56
|
function getVisualWidth(str) {
|
|
78
|
-
|
|
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;
|
|
57
|
+
return stringWidth(str);
|
|
84
58
|
}
|
|
85
59
|
|
|
86
60
|
/**
|
|
87
|
-
* Pad string to visual width accounting for emojis and
|
|
61
|
+
* Pad string to visual width accounting for emojis, ANSI codes, and wide characters
|
|
88
62
|
*/
|
|
89
63
|
function padToVisualWidth(str, targetWidth) {
|
|
90
64
|
const visualWidth = getVisualWidth(str);
|
|
@@ -144,7 +118,8 @@ async function updateRequirementsStatus(repoPath, status) {
|
|
|
144
118
|
break;
|
|
145
119
|
}
|
|
146
120
|
|
|
147
|
-
|
|
121
|
+
// Check against configured stages
|
|
122
|
+
if (inStatusSection && configuredStages.includes(line.trim())) {
|
|
148
123
|
statusLineIndex = i;
|
|
149
124
|
break;
|
|
150
125
|
}
|
|
@@ -180,20 +155,22 @@ async function updateRequirementsStatus(repoPath, status) {
|
|
|
180
155
|
}
|
|
181
156
|
}
|
|
182
157
|
|
|
158
|
+
// Persistent status box state
|
|
159
|
+
let statusBoxInitialized = false;
|
|
160
|
+
let statusBoxLines = 5; // Number of lines the status box takes
|
|
161
|
+
let storedStatusTitle = '';
|
|
162
|
+
let storedStatus = '';
|
|
163
|
+
|
|
183
164
|
/**
|
|
184
165
|
* Print purple status card with progress indicators
|
|
185
166
|
* Makes current stage very prominent with bright white text
|
|
186
167
|
* Uses full terminal width
|
|
168
|
+
* Now uses persistent header that stays at top while output scrolls
|
|
187
169
|
*/
|
|
188
170
|
function printStatusCard(currentTitle, currentStatus) {
|
|
189
|
-
const stages =
|
|
190
|
-
const stageMap = {
|
|
191
|
-
|
|
192
|
-
'ACT': 1,
|
|
193
|
-
'CLEAN UP': 2,
|
|
194
|
-
'VERIFY': 3,
|
|
195
|
-
'DONE': 4
|
|
196
|
-
};
|
|
171
|
+
const stages = configuredStages;
|
|
172
|
+
const stageMap = {};
|
|
173
|
+
stages.forEach((s, i) => stageMap[s] = i);
|
|
197
174
|
|
|
198
175
|
const currentIndex = stageMap[currentStatus] || 0;
|
|
199
176
|
|
|
@@ -222,11 +199,20 @@ function printStatusCard(currentTitle, currentStatus) {
|
|
|
222
199
|
const titleShort = currentTitle?.substring(0, maxTitleWidth) + (currentTitle?.length > maxTitleWidth ? '...' : '');
|
|
223
200
|
const titleLine = chalk.cyan(`🎯 Working on: `) + chalk.white(titleShort);
|
|
224
201
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
202
|
+
// Build the status box content
|
|
203
|
+
const statusBoxContent =
|
|
204
|
+
chalk.magenta('╭' + '─'.repeat(boxWidth) + '╮') + '\n' +
|
|
205
|
+
chalk.magenta('│') + padToVisualWidth(' ' + workflowLine, boxWidth) + chalk.magenta('│') + '\n' +
|
|
206
|
+
chalk.magenta('│') + ' '.repeat(boxWidth) + chalk.magenta('│') + '\n' +
|
|
207
|
+
chalk.magenta('│') + padToVisualWidth(' ' + titleLine, boxWidth) + chalk.magenta('│') + '\n' +
|
|
208
|
+
chalk.magenta('╰' + '─'.repeat(boxWidth) + '╯');
|
|
209
|
+
|
|
210
|
+
// Store current state (using stored* names to avoid shadowing with parameters)
|
|
211
|
+
storedStatusTitle = currentTitle;
|
|
212
|
+
storedStatus = currentStatus;
|
|
213
|
+
|
|
214
|
+
// Just print the card normally - no complex cursor manipulation
|
|
215
|
+
console.log(statusBoxContent);
|
|
230
216
|
}
|
|
231
217
|
|
|
232
218
|
/**
|
|
@@ -266,7 +252,7 @@ async function getCurrentRequirement(repoPath) {
|
|
|
266
252
|
// Skip empty titles
|
|
267
253
|
if (title && title.length > 0) {
|
|
268
254
|
// Read package and description (optional)
|
|
269
|
-
let
|
|
255
|
+
let pkg = null;
|
|
270
256
|
let description = '';
|
|
271
257
|
let j = i + 1;
|
|
272
258
|
|
|
@@ -279,7 +265,7 @@ async function getCurrentRequirement(repoPath) {
|
|
|
279
265
|
}
|
|
280
266
|
// Check for PACKAGE line
|
|
281
267
|
if (nextLine.startsWith('PACKAGE:')) {
|
|
282
|
-
|
|
268
|
+
pkg = nextLine.replace(/^PACKAGE:\s*/, '').trim();
|
|
283
269
|
} else if (nextLine && !nextLine.startsWith('PACKAGE:')) {
|
|
284
270
|
// Description line (not empty, not package)
|
|
285
271
|
if (description) {
|
|
@@ -294,7 +280,7 @@ async function getCurrentRequirement(repoPath) {
|
|
|
294
280
|
return {
|
|
295
281
|
text: title,
|
|
296
282
|
fullLine: lines[i],
|
|
297
|
-
package:
|
|
283
|
+
package: pkg,
|
|
298
284
|
description: description
|
|
299
285
|
};
|
|
300
286
|
}
|
|
@@ -308,6 +294,53 @@ async function getCurrentRequirement(repoPath) {
|
|
|
308
294
|
}
|
|
309
295
|
}
|
|
310
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Count total TODO requirements
|
|
299
|
+
*/
|
|
300
|
+
async function countTodoRequirements(repoPath) {
|
|
301
|
+
try {
|
|
302
|
+
const reqPath = await getRequirementsPath(repoPath);
|
|
303
|
+
if (!reqPath || !await fs.pathExists(reqPath)) {
|
|
304
|
+
return 0;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
308
|
+
const lines = content.split('\n');
|
|
309
|
+
let inTodoSection = false;
|
|
310
|
+
let count = 0;
|
|
311
|
+
|
|
312
|
+
for (let i = 0; i < lines.length; i++) {
|
|
313
|
+
const line = lines[i].trim();
|
|
314
|
+
|
|
315
|
+
// Check if we're in the TODO section
|
|
316
|
+
if (line.includes('## ⏳ Requirements not yet completed') ||
|
|
317
|
+
line.includes('Requirements not yet completed')) {
|
|
318
|
+
inTodoSection = true;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// If we hit another section header, stop looking
|
|
323
|
+
if (inTodoSection && line.startsWith('##') && !line.startsWith('###')) {
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// If we're in TODO section and find a requirement header (###)
|
|
328
|
+
if (inTodoSection && line.startsWith('###')) {
|
|
329
|
+
const title = line.replace(/^###\s*/, '').trim();
|
|
330
|
+
// Only count non-empty titles
|
|
331
|
+
if (title && title.length > 0) {
|
|
332
|
+
count++;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return count;
|
|
338
|
+
} catch (err) {
|
|
339
|
+
console.error('Error counting requirements:', err.message);
|
|
340
|
+
return 0;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
311
344
|
/**
|
|
312
345
|
* Move requirement from TODO to TO VERIFY BY HUMAN
|
|
313
346
|
*/
|
|
@@ -320,37 +353,69 @@ async function moveRequirementToVerify(repoPath, requirementText) {
|
|
|
320
353
|
|
|
321
354
|
const content = await fs.readFile(reqPath, 'utf8');
|
|
322
355
|
const lines = content.split('\n');
|
|
323
|
-
|
|
324
356
|
// Find the requirement by its title (in ### header format)
|
|
325
|
-
|
|
357
|
+
// Only look in TODO section
|
|
358
|
+
const normalizedRequirement = requirementText.trim();
|
|
359
|
+
const snippet = normalizedRequirement.substring(0, 80);
|
|
326
360
|
let requirementStartIndex = -1;
|
|
327
361
|
let requirementEndIndex = -1;
|
|
362
|
+
let inTodoSection = false;
|
|
328
363
|
|
|
329
364
|
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
|
-
|
|
365
|
+
const line = lines[i];
|
|
366
|
+
const trimmed = line.trim();
|
|
367
|
+
|
|
368
|
+
// Check if we're entering TODO section
|
|
369
|
+
if (trimmed.startsWith('##') && trimmed.includes('Requirements not yet completed')) {
|
|
370
|
+
inTodoSection = true;
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Check if we're leaving TODO section
|
|
375
|
+
if (inTodoSection && trimmed.startsWith('##') && !trimmed.startsWith('###') && !trimmed.includes('Requirements not yet completed')) {
|
|
376
|
+
inTodoSection = false;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Only look for requirements in TODO section
|
|
380
|
+
if (inTodoSection && trimmed.startsWith('###')) {
|
|
381
|
+
const title = trimmed.replace(/^###\s*/, '').trim();
|
|
382
|
+
if (title) {
|
|
383
|
+
// Try multiple matching strategies
|
|
384
|
+
const normalizedTitle = title.trim();
|
|
385
|
+
|
|
386
|
+
// Exact match
|
|
387
|
+
if (normalizedTitle === normalizedRequirement) {
|
|
388
|
+
requirementStartIndex = i;
|
|
343
389
|
}
|
|
344
|
-
if (
|
|
345
|
-
|
|
390
|
+
// Check if either contains the other (for partial matches)
|
|
391
|
+
else if (normalizedTitle.includes(normalizedRequirement) || normalizedRequirement.includes(normalizedTitle)) {
|
|
392
|
+
requirementStartIndex = i;
|
|
393
|
+
}
|
|
394
|
+
// Check snippet matches
|
|
395
|
+
else if (normalizedTitle.includes(snippet) || snippet.includes(normalizedTitle.substring(0, 80))) {
|
|
396
|
+
requirementStartIndex = i;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (requirementStartIndex !== -1) {
|
|
400
|
+
// Find the end of this requirement (next ### or ## header)
|
|
401
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
402
|
+
const nextLine = lines[j].trim();
|
|
403
|
+
if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
|
|
404
|
+
requirementEndIndex = j;
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (requirementEndIndex === -1) {
|
|
409
|
+
requirementEndIndex = lines.length;
|
|
410
|
+
}
|
|
411
|
+
break;
|
|
346
412
|
}
|
|
347
|
-
break;
|
|
348
413
|
}
|
|
349
414
|
}
|
|
350
415
|
}
|
|
351
416
|
|
|
352
417
|
if (requirementStartIndex === -1) {
|
|
353
|
-
console.log(chalk.yellow(
|
|
418
|
+
console.log(chalk.yellow(`⚠️ Could not find requirement "${requirementText.substring(0, 60)}..." in TODO section`));
|
|
354
419
|
return false;
|
|
355
420
|
}
|
|
356
421
|
|
|
@@ -360,6 +425,43 @@ async function moveRequirementToVerify(repoPath, requirementText) {
|
|
|
360
425
|
// Remove the requirement from its current location
|
|
361
426
|
lines.splice(requirementStartIndex, requirementEndIndex - requirementStartIndex);
|
|
362
427
|
|
|
428
|
+
// Check if there are any more requirements in TODO section after removal
|
|
429
|
+
let hasMoreTodoRequirements = false;
|
|
430
|
+
inTodoSection = false; // Reset the existing variable
|
|
431
|
+
for (let i = 0; i < lines.length; i++) {
|
|
432
|
+
const line = lines[i].trim();
|
|
433
|
+
|
|
434
|
+
// Check if we're entering TODO section
|
|
435
|
+
if (line.startsWith('##') && line.includes('Requirements not yet completed')) {
|
|
436
|
+
inTodoSection = true;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Check if we're leaving TODO section
|
|
441
|
+
if (inTodoSection && line.startsWith('##') && !line.startsWith('###') && !line.includes('Requirements not yet completed')) {
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Check if we found a requirement in TODO section
|
|
446
|
+
if (inTodoSection && line.startsWith('###')) {
|
|
447
|
+
hasMoreTodoRequirements = true;
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// If no more TODO requirements, log message
|
|
453
|
+
if (!hasMoreTodoRequirements) {
|
|
454
|
+
console.log(chalk.green('🎉 No more requirements in TODO section'));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
// Add AWS Kiro AI IDE GUI and CLI support
|
|
460
|
+
const kiroAiIdeGuiSupport = '### AWS Kiro AI IDE GUI Support\n\nThis requirement has been moved to the TO VERIFY BY HUMAN section for further review.';
|
|
461
|
+
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.';
|
|
462
|
+
lines.push(kiroAiIdeGuiSupport);
|
|
463
|
+
lines.push(kiroAiIdeCliSupport);
|
|
464
|
+
|
|
363
465
|
// Check if the requirement is in the Recycled section
|
|
364
466
|
const recycledIndex = lines.findIndex(line => line.trim() === '## 📦 RECYCLED');
|
|
365
467
|
if (recycledIndex !== -1) {
|
|
@@ -370,40 +472,155 @@ async function moveRequirementToVerify(repoPath, requirementText) {
|
|
|
370
472
|
}
|
|
371
473
|
}
|
|
372
474
|
|
|
373
|
-
// Find or create
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
'##
|
|
377
|
-
'##
|
|
378
|
-
'##
|
|
475
|
+
// Find or create TO VERIFY BY HUMAN section
|
|
476
|
+
// IMPORTANT: Do NOT match VERIFIED sections - only match TO VERIFY sections
|
|
477
|
+
const verifySectionVariants = [
|
|
478
|
+
'## 🔍 TO VERIFY BY HUMAN',
|
|
479
|
+
'## 🔍 TO VERIFY',
|
|
480
|
+
'## TO VERIFY',
|
|
481
|
+
'## ✅ TO VERIFY',
|
|
482
|
+
'## ✅ Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG'
|
|
379
483
|
];
|
|
380
484
|
|
|
381
|
-
let verifyIndex =
|
|
485
|
+
let verifyIndex = -1;
|
|
486
|
+
for (let i = 0; i < lines.length; i++) {
|
|
487
|
+
const line = lines[i];
|
|
488
|
+
const trimmed = line.trim();
|
|
489
|
+
|
|
490
|
+
// Check each variant more carefully
|
|
491
|
+
for (const variant of verifySectionVariants) {
|
|
492
|
+
const variantTrimmed = variant.trim();
|
|
493
|
+
// Exact match or line starts with variant
|
|
494
|
+
if (trimmed === variantTrimmed || trimmed.startsWith(variantTrimmed)) {
|
|
495
|
+
// Double-check: make sure it's NOT a VERIFIED section (without TO VERIFY)
|
|
496
|
+
if (!trimmed.includes('## 📝 VERIFIED') && !trimmed.match(/^##\s+VERIFIED$/i) &&
|
|
497
|
+
(trimmed.includes('TO VERIFY') || trimmed.includes('Verified by AI screenshot'))) {
|
|
498
|
+
verifyIndex = i;
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (verifyIndex !== -1) break;
|
|
504
|
+
}
|
|
505
|
+
|
|
382
506
|
if (verifyIndex === -1) {
|
|
507
|
+
// Create TO VERIFY section - place it BEFORE VERIFIED section if one exists, otherwise before CHANGELOG
|
|
508
|
+
const verifiedIndex = lines.findIndex(line => {
|
|
509
|
+
const trimmed = line.trim();
|
|
510
|
+
return trimmed === '## 📝 VERIFIED' || trimmed.startsWith('## 📝 VERIFIED') ||
|
|
511
|
+
(trimmed.startsWith('##') && trimmed.includes('VERIFIED') && !trimmed.includes('TO VERIFY'));
|
|
512
|
+
});
|
|
513
|
+
const changelogIndex = lines.findIndex(line => line.includes('## CHANGELOG'));
|
|
383
514
|
const manualFeedbackIndex = lines.findIndex(line => line.trim().startsWith('## ❓'));
|
|
384
|
-
|
|
385
|
-
|
|
515
|
+
|
|
516
|
+
// Prefer: before VERIFIED > before CHANGELOG > before manual feedback > at end
|
|
517
|
+
let insertionIndex = lines.length;
|
|
518
|
+
if (verifiedIndex > 0) {
|
|
519
|
+
insertionIndex = verifiedIndex;
|
|
520
|
+
} else if (changelogIndex > 0) {
|
|
521
|
+
insertionIndex = changelogIndex;
|
|
522
|
+
} else if (manualFeedbackIndex > 0) {
|
|
523
|
+
insertionIndex = manualFeedbackIndex;
|
|
524
|
+
}
|
|
525
|
+
|
|
386
526
|
const block = [];
|
|
387
527
|
if (insertionIndex > 0 && lines[insertionIndex - 1].trim() !== '') {
|
|
388
528
|
block.push('');
|
|
389
529
|
}
|
|
390
|
-
block.push(
|
|
530
|
+
block.push('## 🔍 TO VERIFY BY HUMAN', '');
|
|
391
531
|
lines.splice(insertionIndex, 0, ...block);
|
|
392
|
-
verifyIndex = lines.findIndex(line =>
|
|
532
|
+
verifyIndex = lines.findIndex(line => {
|
|
533
|
+
const trimmed = line.trim();
|
|
534
|
+
return trimmed === '## 🔍 TO VERIFY BY HUMAN' || trimmed.startsWith('## 🔍 TO VERIFY BY HUMAN');
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// Safety check: verifyIndex should be valid
|
|
538
|
+
if (verifyIndex === -1) {
|
|
539
|
+
console.error('Failed to create TO VERIFY BY HUMAN section');
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
// Safety check: verify we're not inserting into a VERIFIED section
|
|
546
|
+
const verifyLine = lines[verifyIndex] || '';
|
|
547
|
+
if (verifyLine.includes('## 📝 VERIFIED') || (verifyLine.trim().startsWith('##') && verifyLine.includes('VERIFIED') && !verifyLine.includes('TO VERIFY'))) {
|
|
548
|
+
console.error('ERROR: Attempted to insert into VERIFIED section instead of TO VERIFY');
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Remove any existing duplicate of this requirement in TO VERIFY section
|
|
553
|
+
// Find the next section header after TO VERIFY
|
|
554
|
+
let nextSectionIndex = lines.length;
|
|
555
|
+
for (let i = verifyIndex + 1; i < lines.length; i++) {
|
|
556
|
+
const trimmed = lines[i].trim();
|
|
557
|
+
if (trimmed.startsWith('##') && !trimmed.startsWith('###')) {
|
|
558
|
+
nextSectionIndex = i;
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Search for duplicate requirement in TO VERIFY section
|
|
564
|
+
const requirementTitle = requirementBlock[0].trim().replace(/^###\s*/, '').trim();
|
|
565
|
+
for (let i = verifyIndex + 1; i < nextSectionIndex; i++) {
|
|
566
|
+
const line = lines[i];
|
|
567
|
+
const trimmed = line.trim();
|
|
568
|
+
|
|
569
|
+
if (trimmed.startsWith('###')) {
|
|
570
|
+
const existingTitle = trimmed.replace(/^###\s*/, '').trim();
|
|
571
|
+
|
|
572
|
+
// Check if this is a duplicate (exact match or contains/contained by)
|
|
573
|
+
if (existingTitle === requirementTitle ||
|
|
574
|
+
existingTitle.includes(requirementTitle) ||
|
|
575
|
+
requirementTitle.includes(existingTitle)) {
|
|
576
|
+
|
|
577
|
+
// Find the end of this existing requirement
|
|
578
|
+
let existingEndIndex = nextSectionIndex;
|
|
579
|
+
for (let j = i + 1; j < nextSectionIndex; j++) {
|
|
580
|
+
const nextLine = lines[j].trim();
|
|
581
|
+
if (nextLine.startsWith('###') || nextLine.startsWith('##')) {
|
|
582
|
+
existingEndIndex = j;
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Remove the duplicate
|
|
588
|
+
lines.splice(i, existingEndIndex - i);
|
|
589
|
+
|
|
590
|
+
// Adjust nextSectionIndex if needed
|
|
591
|
+
nextSectionIndex -= (existingEndIndex - i);
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Insert requirement block at TOP of TO VERIFY section (right after section header)
|
|
598
|
+
// Always insert immediately after the section header with proper blank line spacing
|
|
599
|
+
let insertIndex = verifyIndex + 1;
|
|
600
|
+
|
|
601
|
+
// Ensure there's a blank line after the section header
|
|
602
|
+
if (lines[insertIndex]?.trim() !== '') {
|
|
603
|
+
lines.splice(insertIndex, 0, '');
|
|
604
|
+
insertIndex++;
|
|
393
605
|
}
|
|
394
606
|
|
|
395
|
-
// Insert the requirement block
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
607
|
+
// Insert the requirement block with image paste ability
|
|
608
|
+
const imagePaste = '### Image Paste\n\nYou can paste an image here by using the following format: \n\nExample: ';
|
|
609
|
+
lines.splice(insertIndex, 0, ...requirementBlock, imagePaste);
|
|
610
|
+
|
|
611
|
+
// Ensure there's a blank line after the requirement block
|
|
612
|
+
const afterIndex = insertIndex + requirementBlock.length + 1;
|
|
613
|
+
if (afterIndex < lines.length && lines[afterIndex]?.trim() !== '') {
|
|
614
|
+
lines.splice(afterIndex, 0, '');
|
|
400
615
|
}
|
|
401
616
|
|
|
617
|
+
// Write the file
|
|
402
618
|
await fs.writeFile(reqPath, lines.join('\n'));
|
|
403
|
-
console.log(
|
|
619
|
+
console.log(chalk.green(`✅ Moved requirement to TO VERIFY BY HUMAN: ${requirementText.substring(0, 80)}...`));
|
|
404
620
|
return true;
|
|
405
621
|
} catch (err) {
|
|
406
|
-
console.error('Error moving requirement:', err.message);
|
|
622
|
+
console.error('Error moving requirement to TO VERIFY:', err.message);
|
|
623
|
+
console.error('⚠️ Requirement may have been lost. Please check the requirements file.');
|
|
407
624
|
return false;
|
|
408
625
|
}
|
|
409
626
|
}
|
|
@@ -549,6 +766,8 @@ async function getAllAvailableProviders() {
|
|
|
549
766
|
}
|
|
550
767
|
case 'cursor':
|
|
551
768
|
case 'windsurf':
|
|
769
|
+
case 'vscode':
|
|
770
|
+
case 'kiro':
|
|
552
771
|
case 'antigravity': {
|
|
553
772
|
providers.push(base);
|
|
554
773
|
break;
|
|
@@ -581,7 +800,7 @@ async function getAllAvailableProviders() {
|
|
|
581
800
|
/**
|
|
582
801
|
* Get provider configuration with automatic failover
|
|
583
802
|
*/
|
|
584
|
-
async function getProviderConfig() {
|
|
803
|
+
async function getProviderConfig(excludeProvider = null) {
|
|
585
804
|
const config = await getAutoConfig();
|
|
586
805
|
const providerManager = sharedProviderManager; // Use shared instance to persist rate limit state
|
|
587
806
|
const providers = await getAllAvailableProviders();
|
|
@@ -599,11 +818,15 @@ async function getProviderConfig() {
|
|
|
599
818
|
}
|
|
600
819
|
|
|
601
820
|
const availableProviders = enabledProviders.filter(p => {
|
|
821
|
+
// Exclude the specified provider and rate-limited providers
|
|
822
|
+
if (excludeProvider && p.provider === excludeProvider) {
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
602
825
|
return !providerManager.isRateLimited(p.provider, p.model);
|
|
603
826
|
});
|
|
604
827
|
|
|
605
828
|
let selection = null;
|
|
606
|
-
if (savedAgent) {
|
|
829
|
+
if (savedAgent && savedAgent !== excludeProvider) {
|
|
607
830
|
selection = availableProviders.find(p => p.provider === savedAgent);
|
|
608
831
|
}
|
|
609
832
|
if (!selection) {
|
|
@@ -631,9 +854,9 @@ async function getProviderConfig() {
|
|
|
631
854
|
};
|
|
632
855
|
}
|
|
633
856
|
|
|
634
|
-
async function acquireProviderConfig() {
|
|
857
|
+
async function acquireProviderConfig(excludeProvider = null) {
|
|
635
858
|
while (true) {
|
|
636
|
-
const selection = await getProviderConfig();
|
|
859
|
+
const selection = await getProviderConfig(excludeProvider);
|
|
637
860
|
if (selection.status === 'ok') {
|
|
638
861
|
return selection.provider;
|
|
639
862
|
}
|
|
@@ -863,9 +1086,34 @@ async function findRelevantFiles(requirement, repoPath) {
|
|
|
863
1086
|
|
|
864
1087
|
try {
|
|
865
1088
|
const reqLower = requirement.toLowerCase();
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1089
|
+
const isRemovalRequirement = /remove|delete|eliminate/i.test(requirement) &&
|
|
1090
|
+
(/menu|item|option|setting|button|ui|element/i.test(requirement));
|
|
1091
|
+
|
|
1092
|
+
// For removal requirements, search for the specific text/identifier mentioned
|
|
1093
|
+
if (isRemovalRequirement) {
|
|
1094
|
+
// Extract the text/identifier to search for (text in quotes or after "Remove")
|
|
1095
|
+
const match = requirement.match(/remove\s+["']?([^"']+)["']?/i) ||
|
|
1096
|
+
requirement.match(/remove:\s*["']?([^"']+)["']?/i) ||
|
|
1097
|
+
requirement.match(/["']([^"']+)["']/);
|
|
1098
|
+
|
|
1099
|
+
if (match && match[1]) {
|
|
1100
|
+
const searchText = match[1].trim();
|
|
1101
|
+
// Search for files containing this text
|
|
1102
|
+
const searchLower = searchText.toLowerCase();
|
|
1103
|
+
|
|
1104
|
+
// Always check interactive.js for menu items
|
|
1105
|
+
relevantFiles.push('packages/cli/src/utils/interactive.js');
|
|
1106
|
+
|
|
1107
|
+
// If it mentions CLI or auto, also check auto-direct.js
|
|
1108
|
+
if (searchLower.includes('cli') || searchLower.includes('auto') || searchLower.includes('restart')) {
|
|
1109
|
+
relevantFiles.push('packages/cli/src/commands/auto-direct.js');
|
|
1110
|
+
}
|
|
1111
|
+
} else {
|
|
1112
|
+
// Fallback: check both files for removal requirements
|
|
1113
|
+
relevantFiles.push('packages/cli/src/utils/interactive.js');
|
|
1114
|
+
relevantFiles.push('packages/cli/src/commands/auto-direct.js');
|
|
1115
|
+
}
|
|
1116
|
+
} else if (reqLower.includes('completed') && reqLower.includes('verify')) {
|
|
869
1117
|
// This is about the auto mode moving requirements
|
|
870
1118
|
relevantFiles.push('packages/cli/src/commands/auto-direct.js');
|
|
871
1119
|
} else if (reqLower.includes('requirements page') || reqLower.includes('requirements:')) {
|
|
@@ -1089,11 +1337,14 @@ async function runIdeProviderIteration(providerConfig, repoPath) {
|
|
|
1089
1337
|
resolve({ success: true, output: combinedOutput });
|
|
1090
1338
|
} else {
|
|
1091
1339
|
const message = `${providerConfig.displayName} exited with code ${code}`;
|
|
1340
|
+
const antigravityRateLimit = checkAntigravityRateLimit(combinedOutput);
|
|
1341
|
+
|
|
1092
1342
|
resolve({
|
|
1093
1343
|
success: false,
|
|
1094
1344
|
error: combinedOutput ? `${message}\n${combinedOutput}` : message,
|
|
1095
1345
|
output: combinedOutput,
|
|
1096
|
-
rateLimited: isRateLimitMessage(combinedOutput)
|
|
1346
|
+
rateLimited: isRateLimitMessage(combinedOutput) || antigravityRateLimit.isRateLimited,
|
|
1347
|
+
antigravityRateLimited: antigravityRateLimit.isRateLimited
|
|
1097
1348
|
});
|
|
1098
1349
|
}
|
|
1099
1350
|
});
|
|
@@ -1190,26 +1441,15 @@ async function waitForIdeCompletion(repoPath, requirementText, ideType = '', tim
|
|
|
1190
1441
|
// Check 3: Quota limit detection for Antigravity (after 2 minutes of waiting)
|
|
1191
1442
|
const elapsed = Date.now() - startTime;
|
|
1192
1443
|
if (ideType === 'antigravity' && !quotaHandled && elapsed >= 120000) {
|
|
1193
|
-
console.log(chalk.yellow('\n⚠️
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
quotaHandled = true;
|
|
1203
|
-
// Reset start time to give new model time to work
|
|
1204
|
-
startTime = Date.now();
|
|
1205
|
-
} else {
|
|
1206
|
-
console.log(chalk.yellow(`⚠️ Could not switch models: ${result.error}\n`));
|
|
1207
|
-
quotaHandled = true; // Don't try again
|
|
1208
|
-
}
|
|
1209
|
-
} catch (error) {
|
|
1210
|
-
console.error(chalk.red(`Error handling quota limit: ${error.message}\n`));
|
|
1211
|
-
quotaHandled = true; // Don't try again
|
|
1212
|
-
}
|
|
1444
|
+
console.log(chalk.yellow('\n⚠️ Antigravity quota limit detected after 2 minutes\n'));
|
|
1445
|
+
console.log(chalk.cyan(' Switching to next available IDE agent...\n'));
|
|
1446
|
+
watcher.close();
|
|
1447
|
+
resolve({
|
|
1448
|
+
success: false,
|
|
1449
|
+
reason: 'antigravity-quota',
|
|
1450
|
+
antigravityRateLimited: true // This triggers switching to next provider
|
|
1451
|
+
});
|
|
1452
|
+
return;
|
|
1213
1453
|
}
|
|
1214
1454
|
|
|
1215
1455
|
// Check 4: Timeout
|
|
@@ -1266,6 +1506,10 @@ async function runIdeFallbackIteration(requirement, providerConfig, repoPath, pr
|
|
|
1266
1506
|
const ideResult = await runIdeProviderIteration(providerConfig, repoPath);
|
|
1267
1507
|
|
|
1268
1508
|
if (!ideResult.success) {
|
|
1509
|
+
if (ideResult.antigravityRateLimited) {
|
|
1510
|
+
await handleAntigravityRateLimit();
|
|
1511
|
+
return { success: false, error: 'Antigravity rate limit detected, retrying with next provider.', shouldRetry: true };
|
|
1512
|
+
}
|
|
1269
1513
|
// CRITICAL: Mark provider as unavailable for ANY error so acquireProviderConfig() will skip it
|
|
1270
1514
|
providerManager.markRateLimited(providerConfig.provider, providerConfig.model, ideResult.output || ideResult.error || 'IDE provider failed');
|
|
1271
1515
|
return { success: false, error: ideResult.error || 'IDE provider failed' };
|
|
@@ -1277,6 +1521,12 @@ async function runIdeFallbackIteration(requirement, providerConfig, repoPath, pr
|
|
|
1277
1521
|
const completionResult = await waitForIdeCompletion(repoPath, requirement.text, providerConfig.ide || providerConfig.provider);
|
|
1278
1522
|
|
|
1279
1523
|
if (!completionResult.success) {
|
|
1524
|
+
if (completionResult.antigravityRateLimited) {
|
|
1525
|
+
console.log(chalk.yellow('⚠️ Antigravity quota exhausted, switching to next IDE\n'));
|
|
1526
|
+
providerManager.markRateLimited(providerConfig.provider, providerConfig.model, 'Quota limit reached');
|
|
1527
|
+
return { success: false, error: 'Antigravity quota limit', shouldRetry: true };
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1280
1530
|
const errorMsg = completionResult.reason === 'timeout'
|
|
1281
1531
|
? 'IDE agent timed out'
|
|
1282
1532
|
: 'IDE agent failed to complete';
|
|
@@ -1360,10 +1610,53 @@ async function runIteration(requirement, providerConfig, repoPath) {
|
|
|
1360
1610
|
});
|
|
1361
1611
|
}
|
|
1362
1612
|
|
|
1613
|
+
// Check if requirement involves removing menu items or UI elements
|
|
1614
|
+
const isRemovalRequirement = /remove|delete|eliminate/i.test(requirement.text) &&
|
|
1615
|
+
(/menu|item|option|setting|button|ui|element/i.test(requirement.text));
|
|
1616
|
+
|
|
1617
|
+
let removalInstructions = '';
|
|
1618
|
+
if (isRemovalRequirement) {
|
|
1619
|
+
removalInstructions = `
|
|
1620
|
+
|
|
1621
|
+
⚠️ CRITICAL: THIS IS A REMOVAL REQUIREMENT ⚠️
|
|
1622
|
+
|
|
1623
|
+
When removing menu items, UI elements, or settings, you MUST:
|
|
1624
|
+
|
|
1625
|
+
1. **FIND ALL OCCURRENCES**: Search the CURRENT CODE CONTEXT for:
|
|
1626
|
+
- The exact text/identifier mentioned in the requirement
|
|
1627
|
+
- Variations (e.g., "Restart CLI", "restart CLI", "restartCLI", "setting:restart-cli")
|
|
1628
|
+
- Both display code (where it's shown) AND handler code (where it's processed)
|
|
1629
|
+
|
|
1630
|
+
2. **REMOVE ALL CODE, NOT JUST TOGGLE**:
|
|
1631
|
+
- DELETE the code that displays/creates the menu item (e.g., items.push() calls)
|
|
1632
|
+
- DELETE the handler code that processes the action (e.g., case 'setting:restart-cli': blocks)
|
|
1633
|
+
- DELETE any configuration/state code related to the item (e.g., const restartCLI = ...)
|
|
1634
|
+
- DELETE any variable declarations or references to the item
|
|
1635
|
+
|
|
1636
|
+
3. **MULTIPLE FILES MAY BE AFFECTED**:
|
|
1637
|
+
- If you find references in multiple files, you MUST provide multiple FILE/SEARCH/REPLACE blocks
|
|
1638
|
+
- Each file needs its own FILE/SEARCH/REPLACE block
|
|
1639
|
+
- Remove ALL occurrences across ALL files shown in the context
|
|
1640
|
+
|
|
1641
|
+
4. **VERIFICATION**:
|
|
1642
|
+
- After removal, the code should compile/run without errors
|
|
1643
|
+
- The removed item should not appear anywhere in the codebase
|
|
1644
|
+
- All related handlers and configuration should be removed
|
|
1645
|
+
|
|
1646
|
+
EXAMPLE for removing "Restart CLI after each completed requirement":
|
|
1647
|
+
- Remove: items.push({ type: 'setting', name: \`...Restart CLI...\`, value: 'setting:restart-cli' })
|
|
1648
|
+
- Remove: case 'setting:restart-cli': { ... } handler block
|
|
1649
|
+
- Remove: const restartCLI = ... variable declaration
|
|
1650
|
+
- Remove: Any other references to restartCLI or 'setting:restart-cli'
|
|
1651
|
+
|
|
1652
|
+
`;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1363
1655
|
const prompt = `You are implementing a code change. Your task is to provide a SEARCH/REPLACE block that will modify the code.
|
|
1364
1656
|
|
|
1365
1657
|
REQUIREMENT TO IMPLEMENT:
|
|
1366
1658
|
${requirement.text}
|
|
1659
|
+
${removalInstructions}
|
|
1367
1660
|
${contextSection}
|
|
1368
1661
|
|
|
1369
1662
|
YOUR TASK:
|
|
@@ -1371,6 +1664,7 @@ YOUR TASK:
|
|
|
1371
1664
|
2. Find the EXACT location where changes are needed
|
|
1372
1665
|
3. COPY AT LEAST 10 LINES EXACTLY as they appear (including indentation, spacing, comments)
|
|
1373
1666
|
4. Show what the code should look like after your changes
|
|
1667
|
+
${isRemovalRequirement ? '5. **REMOVE ALL CODE** related to the item - do NOT comment it out, DELETE it completely' : ''}
|
|
1374
1668
|
|
|
1375
1669
|
OUTPUT FORMAT:
|
|
1376
1670
|
|
|
@@ -1602,6 +1896,9 @@ async function handleAutoStart(options) {
|
|
|
1602
1896
|
console.log();
|
|
1603
1897
|
|
|
1604
1898
|
// Get repo path
|
|
1899
|
+
// Load configured stages
|
|
1900
|
+
configuredStages = await getStages();
|
|
1901
|
+
|
|
1605
1902
|
const repoPath = await getRepoPath();
|
|
1606
1903
|
if (!repoPath) {
|
|
1607
1904
|
console.log(chalk.red('✗ No repository configured'));
|
|
@@ -1611,8 +1908,15 @@ async function handleAutoStart(options) {
|
|
|
1611
1908
|
|
|
1612
1909
|
console.log(chalk.white('Repository:'), chalk.cyan(repoPath));
|
|
1613
1910
|
|
|
1614
|
-
// Get
|
|
1615
|
-
|
|
1911
|
+
// Get effective agent using centralized selector
|
|
1912
|
+
const { getEffectiveAgent } = require('../utils/agent-selector');
|
|
1913
|
+
const PROVIDER_DEFINITIONS = require('./auto').PROVIDER_DEFINITIONS;
|
|
1914
|
+
const PROVIDER_DEFINITION_MAP = new Map(PROVIDER_DEFINITIONS.map(def => [def.id, def]));
|
|
1915
|
+
|
|
1916
|
+
const { effectiveAgent } = await getEffectiveAgent(options, PROVIDER_DEFINITIONS, PROVIDER_DEFINITION_MAP);
|
|
1917
|
+
|
|
1918
|
+
// Get provider configuration for the selected agent
|
|
1919
|
+
let providerConfig = await acquireProviderConfig(effectiveAgent);
|
|
1616
1920
|
if (!providerConfig) {
|
|
1617
1921
|
return;
|
|
1618
1922
|
}
|
|
@@ -1627,16 +1931,16 @@ async function handleAutoStart(options) {
|
|
|
1627
1931
|
console.log();
|
|
1628
1932
|
console.log(chalk.gray('═'.repeat(80)));
|
|
1629
1933
|
|
|
1934
|
+
// Calculate initial total for consistent display throughout the session
|
|
1935
|
+
const initialTodoCount = await countTodoRequirements(repoPath);
|
|
1936
|
+
const initialEffectiveMax = unlimited ? initialTodoCount : Math.min(maxChats, initialTodoCount);
|
|
1937
|
+
|
|
1630
1938
|
// Main loop
|
|
1631
1939
|
let completedCount = 0;
|
|
1632
1940
|
let failedCount = 0;
|
|
1633
1941
|
|
|
1634
1942
|
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
|
|
1943
|
+
// Get current requirement first to check if there are any TODO items
|
|
1640
1944
|
const requirement = await getCurrentRequirement(repoPath);
|
|
1641
1945
|
if (!requirement) {
|
|
1642
1946
|
console.log(chalk.bold.yellow('\n🎉 All requirements completed!'));
|
|
@@ -1644,12 +1948,20 @@ async function handleAutoStart(options) {
|
|
|
1644
1948
|
break;
|
|
1645
1949
|
}
|
|
1646
1950
|
|
|
1951
|
+
// Calculate current requirement number consistently (before processing)
|
|
1952
|
+
// This represents which requirement we're working on (1-based)
|
|
1953
|
+
const currentReqNumber = completedCount + failedCount + 1;
|
|
1954
|
+
|
|
1955
|
+
console.log(chalk.bold.magenta(`\n${'━'.repeat(80)}`));
|
|
1956
|
+
console.log(chalk.bold.magenta(` REQUIREMENT ${currentReqNumber} of ${initialEffectiveMax}`));
|
|
1957
|
+
console.log(chalk.bold.magenta(`${'━'.repeat(80)}\n`));
|
|
1958
|
+
|
|
1647
1959
|
// Run iteration with full workflow
|
|
1648
1960
|
const result = await runIteration(requirement, providerConfig, repoPath);
|
|
1649
1961
|
|
|
1650
1962
|
if (result.success) {
|
|
1651
1963
|
completedCount++;
|
|
1652
|
-
console.log(chalk.bold.green(`✅
|
|
1964
|
+
console.log(chalk.bold.green(`✅ Requirement ${currentReqNumber}/${initialEffectiveMax} COMPLETE`));
|
|
1653
1965
|
console.log(chalk.gray('Moving to next requirement...\n'));
|
|
1654
1966
|
|
|
1655
1967
|
// Check if restart CLI is enabled and there are more iterations
|
|
@@ -1696,11 +2008,15 @@ async function handleAutoStart(options) {
|
|
|
1696
2008
|
const isRateLimitError = isRateLimitMessage(result.error);
|
|
1697
2009
|
const errorType = isRateLimitError ? 'Rate limit' : 'Error';
|
|
1698
2010
|
|
|
2011
|
+
// Store the provider that failed before switching
|
|
2012
|
+
const failedProvider = providerConfig.displayName;
|
|
2013
|
+
|
|
1699
2014
|
console.log(chalk.yellow(`⚠️ ${errorType} detected, switching to next provider in your list...\n`));
|
|
1700
2015
|
|
|
1701
|
-
const newProviderConfig = await acquireProviderConfig();
|
|
2016
|
+
const newProviderConfig = await acquireProviderConfig(providerConfig.provider);
|
|
1702
2017
|
if (newProviderConfig) {
|
|
1703
2018
|
providerConfig = newProviderConfig;
|
|
2019
|
+
console.log(chalk.yellow(`⚠️ ${failedProvider} hit ${errorType.toLowerCase()}`));
|
|
1704
2020
|
console.log(chalk.green(`✓ Switched to: ${providerConfig.displayName}\n`));
|
|
1705
2021
|
|
|
1706
2022
|
// Retry this iteration with new provider (don't increment i)
|
|
@@ -1711,7 +2027,10 @@ async function handleAutoStart(options) {
|
|
|
1711
2027
|
}
|
|
1712
2028
|
|
|
1713
2029
|
failedCount++;
|
|
1714
|
-
|
|
2030
|
+
// Use the same currentReqNumber that was calculated at the start of this iteration
|
|
2031
|
+
// This ensures consistency: if we showed "Requirement 2 of 3" at the start,
|
|
2032
|
+
// we should show "Requirement 2 of 3 FAILED" (not recalculate it)
|
|
2033
|
+
console.log(chalk.bold.red(`❌ Requirement ${currentReqNumber}/${initialEffectiveMax} FAILED`));
|
|
1715
2034
|
console.log(chalk.red(`Error: ${result.error}\n`));
|
|
1716
2035
|
console.log(chalk.yellow('Continuing to next requirement...\n'));
|
|
1717
2036
|
}
|
|
@@ -1722,7 +2041,8 @@ async function handleAutoStart(options) {
|
|
|
1722
2041
|
console.log(chalk.bold.magenta(` FINAL SUMMARY`));
|
|
1723
2042
|
console.log(chalk.bold.magenta(`${'━'.repeat(80)}\n`));
|
|
1724
2043
|
|
|
1725
|
-
|
|
2044
|
+
const totalProcessed = completedCount + failedCount;
|
|
2045
|
+
console.log(chalk.white('Total requirements:'), chalk.cyan(totalProcessed));
|
|
1726
2046
|
console.log(chalk.white('Completed:'), chalk.green(`${completedCount} ✓`));
|
|
1727
2047
|
if (failedCount > 0) {
|
|
1728
2048
|
console.log(chalk.white('Failed:'), chalk.red(`${failedCount} ✗`));
|
|
@@ -1732,7 +2052,6 @@ async function handleAutoStart(options) {
|
|
|
1732
2052
|
|
|
1733
2053
|
if (completedCount > 0) {
|
|
1734
2054
|
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
2055
|
}
|
|
1737
2056
|
|
|
1738
2057
|
} catch (error) {
|