vibecodingmachine-cli 2025.12.6-1702 → 2025.12.24-2348

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.
@@ -2,18 +2,18 @@ const chalk = require('chalk');
2
2
  const ora = require('ora');
3
3
  const path = require('path');
4
4
  const os = require('os');
5
- const { AppleScriptManager, ClineCLIManager, AiderCLIManager, ClaudeCodeCLIManager, logIDEMessage, runContinueCLICommand, runContinueCLIAutoMode } = require('vibecodingmachine-core');
6
- const { getRepoPath, getAutoConfig, setAutoConfig } = require('../utils/config');
5
+ const { spawn } = require('child_process');
6
+ const { AppleScriptManager, ClineCLIManager, AiderCLIManager, ClaudeCodeCLIManager, logIDEMessage, runContinueCLIAutoMode, t } = require('vibecodingmachine-core');
7
+ const { getRepoPath, getAutoConfig, setAutoConfig, getStages } = require('../utils/config');
7
8
  const { checkAutoModeStatus, startAutoMode, stopAutoMode, updateAutoModeStatus } = require('../utils/auto-mode');
8
9
  const logger = require('../utils/logger');
9
10
  const { createKeyboardHandler } = require('../utils/keyboard-handler');
10
11
  const { getProviderDefinitions, getProviderPreferences } = require('../utils/provider-registry');
11
12
  const PROVIDER_DEFINITIONS = getProviderDefinitions();
12
- const DIRECT_AGENT_IDS = PROVIDER_DEFINITIONS.filter(def => def.type === 'direct').map(def => def.id);
13
13
  const PROVIDER_DEFINITION_MAP = new Map(PROVIDER_DEFINITIONS.map(def => [def.id, def]));
14
14
  const { handleAutoStart: handleDirectAutoStart } = require('./auto-direct');
15
15
 
16
- const DEFAULT_INSTRUCTION_TEXT = 'Follow INSTRUCTIONS.md from .vibecodingmachine directory.\n\nCRITICAL FOR IDE AGENTS: You MUST work through ALL stages (ACT → CLEAN UP → VERIFY → DONE) without stopping. Update the "🚦 Current Status" section in the REQUIREMENTS file as you progress through each stage. DO NOT stop after acknowledging stages - you must COMPLETE the work and set status to DONE in the requirements file. The CLI is monitoring the file and waiting for you to finish.';
16
+
17
17
 
18
18
  /**
19
19
  * Get available LLM providers from config and environment variables
@@ -182,6 +182,7 @@ async function moveRequirementBackToTodo(repoPath, requirementText) {
182
182
 
183
183
  for (let i = 0; i < lines.length; i++) {
184
184
  const line = lines[i];
185
+ const trimmed = line.trim();
185
186
 
186
187
  // Find verified section
187
188
  if (line.includes('## ✅ Verified by AI screenshot')) {
@@ -199,7 +200,7 @@ async function moveRequirementBackToTodo(repoPath, requirementText) {
199
200
  }
200
201
 
201
202
  // Check if we're in verified section and this is the requirement to move
202
- if (inVerifiedSection && line.trim().startsWith('- ')) {
203
+ if (inVerifiedSection && trimmed.startsWith('- ')) {
203
204
  const lineRequirement = line.substring(2).trim();
204
205
  // Remove date prefix if present (format: "2025-11-05: **requirement**")
205
206
  const cleanRequirement = lineRequirement.replace(/^\d{4}-\d{2}-\d{2}:\s*\*\*/, '').replace(/\*\*$/, '').trim();
@@ -210,20 +211,21 @@ async function moveRequirementBackToTodo(repoPath, requirementText) {
210
211
  requirementFound = true;
211
212
  continue;
212
213
  }
213
- updatedLines.push(line);
214
- continue;
215
214
  }
216
215
 
217
216
  // If we're in not yet completed section and haven't added the requirement yet, add it at the top
218
- if (inNotYetCompleted && !requirementFound && line.trim().startsWith('- ')) {
217
+ if (inNotYetCompleted && !requirementFound && trimmed.startsWith('- ')) {
219
218
  // Add the requirement before the first existing requirement
220
- updatedLines.push(`- ${requirementText}`);
221
- updatedLines.push('');
222
- requirementFound = true; // Mark as added so we don't add it again
219
+ const newUpdatedLines = lines.slice(0, i);
220
+ newUpdatedLines.push(`- ${requirementText}`);
221
+ newUpdatedLines.push('');
222
+ newUpdatedLines.push(...lines.slice(i));
223
+ await fs.writeFile(reqPath, newUpdatedLines.join('\n'));
224
+ return { success: true };
223
225
  }
224
226
 
225
227
  // Reset section flags when hitting new sections
226
- if (line.trim().startsWith('##')) {
228
+ if (trimmed.startsWith('##')) {
227
229
  inVerifiedSection = false;
228
230
  inNotYetCompleted = false;
229
231
  }
@@ -234,6 +236,7 @@ async function moveRequirementBackToTodo(repoPath, requirementText) {
234
236
  // If requirement wasn't found in verified section, try to add it to not yet completed anyway
235
237
  if (!requirementFound) {
236
238
  // Find the not yet completed section and add requirement at the top
239
+ const newUpdatedLines = lines.slice();
237
240
  for (let i = 0; i < updatedLines.length; i++) {
238
241
  if (updatedLines[i].includes('## ⏳ Requirements not yet completed')) {
239
242
  // Find the first requirement line after this section
@@ -243,18 +246,13 @@ async function moveRequirementBackToTodo(repoPath, requirementText) {
243
246
  insertIndex++;
244
247
  }
245
248
  updatedLines.splice(insertIndex, 0, `- ${requirementText}`, '');
246
- requirementFound = true;
247
- break;
249
+ await fs.writeFile(reqPath, updatedLines.join('\n'));
250
+ return { success: true };
248
251
  }
249
252
  }
250
253
  }
251
254
 
252
- if (requirementFound) {
253
- await fs.writeFile(reqPath, updatedLines.join('\n'));
254
- return { success: true };
255
- } else {
256
- return { success: false, error: 'Requirement not found in verified section' };
257
- }
255
+ return { success: false, error: 'Requirement not found in verified section' };
258
256
  } catch (error) {
259
257
  return { success: false, error: error.message };
260
258
  }
@@ -279,10 +277,8 @@ async function moveRequirementToFeedback(repoPath, requirementText, questions, f
279
277
 
280
278
  const content = await fs.readFile(reqPath, 'utf8');
281
279
  const lines = content.split('\n');
282
- const updatedLines = [];
283
280
 
284
281
  let inTodoSection = false;
285
- let inFeedbackSection = false;
286
282
  let requirementRemoved = false;
287
283
  let feedbackAdded = false;
288
284
 
@@ -292,15 +288,13 @@ async function moveRequirementToFeedback(repoPath, requirementText, questions, f
292
288
  // Find TODO section
293
289
  if (trimmed.startsWith('##') && trimmed.includes('Requirements not yet completed')) {
294
290
  inTodoSection = true;
295
- inFeedbackSection = false;
296
- updatedLines.push(line);
297
291
  continue;
298
292
  }
299
293
 
300
294
  // Find "Requirements needing manual feedback" section
301
295
  if (trimmed.startsWith('##') && (trimmed.includes('Requirements needing manual feedback') || trimmed.includes('❓ Requirements needing'))) {
302
296
  inTodoSection = false;
303
- inFeedbackSection = true;
297
+ const updatedLines = lines.slice(0, lines.indexOf(line));
304
298
  updatedLines.push(line);
305
299
 
306
300
  // Add blank line if not already present
@@ -328,13 +322,14 @@ async function moveRequirementToFeedback(repoPath, requirementText, questions, f
328
322
  updatedLines.push('');
329
323
  feedbackAdded = true;
330
324
  }
331
- continue;
325
+ updatedLines.push(...lines.slice(lines.indexOf(line) + 1));
326
+ await fs.writeFile(reqPath, updatedLines.join('\n'));
327
+ return { success: true };
332
328
  }
333
329
 
334
330
  // Stop sections at next header
335
331
  if (trimmed.startsWith('##')) {
336
332
  inTodoSection = false;
337
- inFeedbackSection = false;
338
333
  }
339
334
 
340
335
  // Remove requirement from TODO section (match first occurrence)
@@ -694,8 +689,6 @@ async function start(options) {
694
689
  if (global.pendingUpdate) {
695
690
  const updateSpinner = ora('Installing update...').start();
696
691
  try {
697
- const { spawn } = require('child_process');
698
-
699
692
  // Install update using npm
700
693
  await new Promise((resolve, reject) => {
701
694
  const npmInstall = spawn('npm', ['install', '-g', 'vibecodingmachine-cli'], {
@@ -708,7 +701,6 @@ async function start(options) {
708
701
  console.log(chalk.green('\n✓ Restarting with new version...\n'));
709
702
 
710
703
  // Restart CLI with same arguments
711
- const { spawn } = require('child_process');
712
704
  const args = process.argv.slice(2);
713
705
  spawn(process.argv[0], [process.argv[1], ...args], {
714
706
  stdio: 'inherit',
@@ -731,7 +723,7 @@ async function start(options) {
731
723
  }
732
724
  }
733
725
 
734
- const spinner = ora('Starting autonomous mode...').start();
726
+ const spinner = ora(t('auto.starting.mode')).start();
735
727
 
736
728
  try {
737
729
  const repoPath = await getRepoPath();
@@ -741,15 +733,32 @@ async function start(options) {
741
733
  throw new Error('No repository configured');
742
734
  }
743
735
 
744
- // Read saved auto config first, then override with options
736
+ // Get provider preferences and definitions
745
737
  const savedConfig = await getAutoConfig();
746
738
  const prefs = await getProviderPreferences();
747
- const firstEnabledFromPrefs = prefs.order.find(id => prefs.enabled[id] !== false);
748
- const fallbackAgent = firstEnabledFromPrefs || 'claude-code';
749
- const savedAgent = savedConfig.agent || savedConfig.ide;
750
- const requestedAgent = options.ide || savedAgent || fallbackAgent;
751
- const providerDef = PROVIDER_DEFINITION_MAP.get(requestedAgent) || PROVIDER_DEFINITION_MAP.get(fallbackAgent);
752
- const effectiveAgent = providerDef ? providerDef.id : 'claude-code';
739
+
740
+ // Get all available providers in the order specified in preferences
741
+ const availableProviders = [];
742
+ for (const id of prefs.order) {
743
+ if (prefs.enabled[id] !== false && PROVIDER_DEFINITION_MAP.has(id)) {
744
+ availableProviders.push(id);
745
+ }
746
+ }
747
+
748
+ // If no providers are available, use the first one from definitions as fallback
749
+ if (availableProviders.length === 0) {
750
+ availableProviders.push(PROVIDER_DEFINITIONS[0]?.id || 'claude-code');
751
+ }
752
+
753
+ // Use the first available provider by default, unless overridden by options
754
+ let effectiveAgent = options.ide || availableProviders[0];
755
+
756
+ // If the requested agent isn't available, use the first available one
757
+ if (!availableProviders.includes(effectiveAgent)) {
758
+ effectiveAgent = availableProviders[0];
759
+ }
760
+
761
+ const providerDef = PROVIDER_DEFINITION_MAP.get(effectiveAgent);
753
762
 
754
763
  const resolvedNeverStop = (() => {
755
764
  if (options.neverStop !== undefined) return options.neverStop;
@@ -893,8 +902,15 @@ async function start(options) {
893
902
  await startAutoMode(repoPath, config);
894
903
  }
895
904
 
905
+ // Get configured stages
906
+ const stages = await getStages();
907
+ const stagesList = stages.join(' → ');
908
+ const defaultInstructionText = `Follow INSTRUCTIONS.md from .vibecodingmachine directory.
909
+
910
+ CRITICAL FOR IDE AGENTS: You MUST work through ALL configured stages (${stagesList}) without stopping. Update the "🚦 Current Status" section in the REQUIREMENTS file as you progress through each stage. DO NOT stop after acknowledging stages - you must COMPLETE the work and set status to DONE in the requirements file. The CLI is monitoring the file and waiting for you to finish.`;
911
+
896
912
  // Send initial instruction to IDE or Continue/Cline CLI
897
- const textToSend = options.text || DEFAULT_INSTRUCTION_TEXT;
913
+ const textToSend = options.text || defaultInstructionText;
898
914
 
899
915
  if (config.ide === 'continue') {
900
916
  // Use Continue CLI with command-line approach
@@ -1017,14 +1033,9 @@ async function start(options) {
1017
1033
  'DONE': '⏳'
1018
1034
  };
1019
1035
 
1020
- const stages = ['PREPARE', 'ACT', 'CLEAN UP', 'VERIFY', 'DONE'];
1021
- const stageMap = {
1022
- 'PREPARE': 0,
1023
- 'ACT': 1,
1024
- 'CLEAN UP': 2,
1025
- 'VERIFY': 3,
1026
- 'DONE': 4
1027
- };
1036
+ // Build stage map dynamically
1037
+ const stageMap = {};
1038
+ stages.forEach((s, i) => stageMap[s] = i);
1028
1039
 
1029
1040
  const currentIndex = stageMap[currentStatus] || 0;
1030
1041
  const workflowLine = stages.map((stage, idx) => {
@@ -1064,29 +1075,64 @@ async function start(options) {
1064
1075
  let title = null;
1065
1076
  let status = 'PREPARE';
1066
1077
  let inTodoSection = false;
1078
+ let inCurrentSection = false;
1067
1079
 
1068
1080
  for (let i = 0; i < lines.length; i++) {
1069
1081
  const line = lines[i].trim();
1070
1082
 
1083
+ // Check if we're in the Current section
1084
+ if (line.includes('## 🔨 Current In Progress Requirement') ||
1085
+ line.includes('Current In Progress Requirement')) {
1086
+ inCurrentSection = true;
1087
+ inTodoSection = false;
1088
+ continue;
1089
+ }
1090
+
1071
1091
  // Check if we're in the TODO section
1072
1092
  if (line.includes('## ⏳ Requirements not yet completed') ||
1073
1093
  (line.includes('Requirements not yet completed') && line.startsWith('##'))) {
1074
1094
  inTodoSection = true;
1095
+ inCurrentSection = false;
1075
1096
  continue;
1076
1097
  }
1077
1098
 
1078
- // If we hit another section header, stop looking
1079
- if (inTodoSection && line.startsWith('##') && !line.startsWith('###')) {
1080
- break;
1099
+ // If we hit another section header, stop looking in current section/todo section logic
1100
+ if ((inTodoSection || inCurrentSection) && line.startsWith('##') && !line.startsWith('###')) {
1101
+ if (inCurrentSection) {
1102
+ // If we finished current section and found nothing, move to next sections (which might be TODO)
1103
+ inCurrentSection = false;
1104
+ continue;
1105
+ }
1106
+ if (inTodoSection) {
1107
+ break;
1108
+ }
1081
1109
  }
1082
1110
 
1083
- // If we're in TODO section and find a requirement header (###)
1084
- if (inTodoSection && line.startsWith('###')) {
1085
- title = line.replace(/^###\s*/, '').trim();
1086
- // Skip empty titles
1087
- if (title && title.length > 0) {
1088
- status = 'PREPARE';
1089
- break;
1111
+ // If we're in Current section, look for bullet or header
1112
+ if (inCurrentSection) {
1113
+ if (line.startsWith('- ')) {
1114
+ title = line.substring(2).trim();
1115
+ // Remove bold if present
1116
+ title = title.replace(/\*\*/g, '');
1117
+ if (title) break;
1118
+ }
1119
+ if (line.startsWith('###')) {
1120
+ title = line.replace(/^###\s*/, '').trim();
1121
+ if (title) break;
1122
+ }
1123
+ }
1124
+
1125
+ // If we're in TODO section and find a requirement header (###) or bullet
1126
+ if (inTodoSection) {
1127
+ if (line.startsWith('###')) {
1128
+ title = line.replace(/^###\s*/, '').trim();
1129
+ if (title) break;
1130
+ }
1131
+ // Also support bullet points in TODO if that's a format used
1132
+ if (line.startsWith('- ')) {
1133
+ title = line.substring(2).trim();
1134
+ title = title.replace(/\*\*/g, '');
1135
+ if (title) break;
1090
1136
  }
1091
1137
  }
1092
1138
  }
@@ -3025,7 +3071,7 @@ Example BAD questions (never ask these):
3025
3071
  // Ask if user wants to stop with countdown
3026
3072
  const readline = require('readline');
3027
3073
  console.log(chalk.yellow('\n⏸️ Continue to next requirement or stop?'));
3028
- console.log(chalk.gray(' Press any key to stop, or wait 10 seconds to continue...'));
3074
+ console.log(chalk.gray(` ${t('auto.press.any.key.stop')}`));
3029
3075
 
3030
3076
  let countdown = 10;
3031
3077
  let dots = '.'.repeat(countdown);
@@ -3116,7 +3162,7 @@ Example BAD questions (never ask these):
3116
3162
  // Ask if user wants to stop with countdown
3117
3163
  const readline = require('readline');
3118
3164
  console.log(chalk.yellow('\n⏸️ Continue to next requirement or stop?'));
3119
- console.log(chalk.gray(' Press any key to stop, or wait 10 seconds to continue...'));
3165
+ console.log(chalk.gray(` ${t('auto.press.any.key.stop')}`));
3120
3166
 
3121
3167
  let countdown = 10;
3122
3168
  let dots = '.'.repeat(countdown);
@@ -4724,7 +4770,7 @@ Example BAD questions (never ask these):
4724
4770
  return;
4725
4771
  }
4726
4772
  } else if (ideAppPath) {
4727
- spinner.succeed(`${config.ide} is installed`);
4773
+ spinner.succeed(t('auto.ide.is.installed', { ide: config.ide }));
4728
4774
  }
4729
4775
 
4730
4776
  // Launch the IDE if it's not already running
@@ -4739,7 +4785,7 @@ Example BAD questions (never ask these):
4739
4785
  config.ide === 'vscode' ? 'Code' : config.ide}"`,
4740
4786
  { encoding: 'utf8', stdio: 'pipe' }).trim();
4741
4787
  if (isRunning) {
4742
- console.log(chalk.gray(` ${config.ide} is already running (PID: ${isRunning.split('\n')[0]})`));
4788
+ console.log(chalk.gray(` ${t('auto.ide.is.running', { ide: config.ide, pid: isRunning.split('\n')[0] })}`));
4743
4789
  }
4744
4790
  } catch (err) {
4745
4791
  // Not running, launch it
@@ -4783,8 +4829,8 @@ Example BAD questions (never ask these):
4783
4829
  console.log(chalk.gray(' Consider using'), chalk.cyan('Cline IDE'), chalk.gray('instead (set with'), chalk.cyan('vcm'), chalk.gray('menu)'));
4784
4830
  } else {
4785
4831
  logIDEMessage(config.ide, textToSend);
4786
- spinner.succeed('Autonomous mode started and initial message sent');
4787
- console.log(chalk.green('\n✓ Vibe Coding Machine is now coding autonomously'));
4832
+ spinner.succeed(t('auto.direct.ide.autonomous.started'));
4833
+ console.log(chalk.green(`\n✓ ${t('auto.coding.autonomously')}`));
4788
4834
  }
4789
4835
  } catch (sendError) {
4790
4836
  logIDEMessage(config.ide, `[ERROR] ${textToSend}`);
@@ -4807,12 +4853,12 @@ Example BAD questions (never ask these):
4807
4853
  };
4808
4854
  return ideNames[ide] || ide;
4809
4855
  };
4810
- console.log(chalk.gray(' IDE:'), chalk.cyan(formatIDEName(config.ide)));
4856
+ console.log(chalk.gray(` ${t('auto.ide.label')}`), chalk.cyan(formatIDEName(config.ide)));
4811
4857
 
4812
4858
  if (config.neverStop) {
4813
4859
  console.log(chalk.gray(' Mode:'), chalk.cyan('Never stop'));
4814
4860
  } else if (config.maxChats) {
4815
- console.log(chalk.gray(' Max chats:'), chalk.cyan(config.maxChats));
4861
+ console.log(chalk.gray(` ${t('auto.max.chats.label')}`), chalk.cyan(config.maxChats));
4816
4862
  }
4817
4863
  } catch (error) {
4818
4864
  spinner.stop();
@@ -4868,7 +4914,7 @@ async function status() {
4868
4914
  if (config.neverStop) {
4869
4915
  console.log(chalk.gray(' Mode:'), chalk.cyan('Never stop'));
4870
4916
  } else if (config.maxChats) {
4871
- console.log(chalk.gray(' Max chats:'), chalk.cyan(config.maxChats));
4917
+ console.log(chalk.gray(` ${t('auto.max.chats.label')}`), chalk.cyan(config.maxChats));
4872
4918
  const remaining = config.maxChats - (autoStatus.chatCount || 0);
4873
4919
  console.log(chalk.gray(' Remaining:'), chalk.cyan(remaining));
4874
4920
  }
@@ -4912,9 +4958,61 @@ async function config(options) {
4912
4958
  }
4913
4959
  }
4914
4960
 
4961
+ async function listAgents() {
4962
+ const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
4963
+ const providerManager = new ProviderManager();
4964
+
4965
+ console.log(chalk.blue('\n📋 Available IDE Agents and Quota Status:\n'));
4966
+
4967
+ // Get all provider definitions
4968
+ const providers = getProviderDefinitions();
4969
+
4970
+ // Check each provider's quota status
4971
+ for (const provider of providers) {
4972
+ const rateLimitInfo = providerManager.getRateLimitInfo(provider.id);
4973
+
4974
+ let statusIcon = '✅';
4975
+ let statusText = chalk.green('Available');
4976
+ let quotaInfo = '';
4977
+
4978
+ if (rateLimitInfo.isRateLimited) {
4979
+ statusIcon = '⚠️';
4980
+ statusText = chalk.yellow('Quota Limit');
4981
+
4982
+ if (rateLimitInfo.resetTime) {
4983
+ const resetDate = new Date(rateLimitInfo.resetTime);
4984
+ const now = new Date();
4985
+ const minutesRemaining = Math.ceil((resetDate - now) / (1000 * 60));
4986
+
4987
+ if (minutesRemaining > 60) {
4988
+ const hoursRemaining = Math.ceil(minutesRemaining / 60);
4989
+ quotaInfo = chalk.gray(` (resets in ${hoursRemaining}h)`);
4990
+ } else if (minutesRemaining > 0) {
4991
+ quotaInfo = chalk.gray(` (resets in ${minutesRemaining}m)`);
4992
+ } else {
4993
+ quotaInfo = chalk.gray(' (resetting soon)');
4994
+ }
4995
+ }
4996
+
4997
+ if (rateLimitInfo.reason) {
4998
+ quotaInfo += chalk.gray(` - ${rateLimitInfo.reason}`);
4999
+ }
5000
+ }
5001
+
5002
+ console.log(`${statusIcon} ${chalk.cyan(provider.name)} ${statusText}${quotaInfo}`);
5003
+ if (provider.description) {
5004
+ console.log(` ${chalk.gray(provider.description)}`);
5005
+ }
5006
+ }
5007
+
5008
+ console.log(chalk.gray('\n💡 Tip: Auto mode will automatically skip quota-limited agents\n'));
5009
+ }
5010
+
4915
5011
  module.exports = {
4916
5012
  start,
4917
5013
  stop,
4918
5014
  status,
4919
- config
5015
+ config,
5016
+ listAgents,
5017
+ PROVIDER_DEFINITIONS
4920
5018
  };