vibecodingmachine-cli 2025.12.6-1702 → 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.
@@ -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 } = 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',
@@ -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
  }
@@ -4912,9 +4958,60 @@ 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
4920
5017
  };
@@ -93,7 +93,7 @@ async function listComputers(options = {}) {
93
93
  /**
94
94
  * Show detailed status of a specific computer
95
95
  */
96
- async function showComputerStatus(computerId, options = {}) {
96
+ async function showComputerStatus(computerId) {
97
97
  const syncEngine = new SyncEngine();
98
98
 
99
99
  try {
@@ -149,7 +149,7 @@ async function showComputerStatus(computerId, options = {}) {
149
149
  /**
150
150
  * Register current computer with focus area
151
151
  */
152
- async function registerComputer(focusArea, options = {}) {
152
+ async function registerComputer(focusArea) {
153
153
  const syncEngine = new SyncEngine();
154
154
 
155
155
  try {
@@ -185,7 +185,7 @@ async function registerComputer(focusArea, options = {}) {
185
185
  /**
186
186
  * Update focus area for current computer
187
187
  */
188
- async function updateFocus(newFocusArea, options = {}) {
188
+ async function updateFocus(newFocusArea) {
189
189
  const syncEngine = new SyncEngine();
190
190
 
191
191
  try {
@@ -195,7 +195,7 @@ async function updateFocus(newFocusArea, options = {}) {
195
195
 
196
196
  if (!computer) {
197
197
  console.log(chalk.yellow('\n⚠ Computer not registered. Registering now...\n'));
198
- await registerComputer(newFocusArea, options);
198
+ await registerComputer(newFocusArea);
199
199
  return;
200
200
  }
201
201
 
@@ -4,7 +4,6 @@ const chalk = require('chalk');
4
4
  const inquirer = require('inquirer');
5
5
  const { getRepoPath, setRepoPath } = require('../utils/config');
6
6
  const {
7
- checkVibeCodingMachineExists,
8
7
  getHostname,
9
8
  getRequirementsFilename,
10
9
  isComputerNameEnabled
@@ -6,7 +6,7 @@ const inquirer = require('inquirer');
6
6
  /**
7
7
  * List requirements for a specific computer
8
8
  */
9
- async function listRemoteRequirements(computerId, options = {}) {
9
+ async function listRemoteRequirements(computerId) {
10
10
  try {
11
11
  const repoPath = process.cwd();
12
12
  const reqFilename = `REQUIREMENTS-${computerId}.md`;
@@ -60,7 +60,7 @@ async function listRemoteRequirements(computerId, options = {}) {
60
60
  /**
61
61
  * Add a requirement to another computer's requirements file
62
62
  */
63
- async function addRemoteRequirement(computerId, requirementText, options = {}) {
63
+ async function addRemoteRequirement(computerId, requirementText) {
64
64
  try {
65
65
  const repoPath = process.cwd();
66
66
  const reqFilename = `REQUIREMENTS-${computerId}.md`;
@@ -168,7 +168,7 @@ async function manageRemoteRequirements(computerId) {
168
168
  ]);
169
169
 
170
170
  switch (action) {
171
- case 'view':
171
+ case 'view': {
172
172
  await listRemoteRequirements(computerId);
173
173
  console.log(chalk.gray('Press Enter to continue...'));
174
174
  await new Promise(resolve => {
@@ -184,8 +184,9 @@ async function manageRemoteRequirements(computerId) {
184
184
  });
185
185
  await manageRemoteRequirements(computerId);
186
186
  break;
187
+ }
187
188
 
188
- case 'add':
189
+ case 'add': {
189
190
  const { requirement } = await inquirer.prompt([
190
191
  {
191
192
  type: 'input',
@@ -211,8 +212,9 @@ async function manageRemoteRequirements(computerId) {
211
212
  }
212
213
  await manageRemoteRequirements(computerId);
213
214
  break;
215
+ }
214
216
 
215
- case 'edit':
217
+ case 'edit': {
216
218
  const repoPath = process.cwd();
217
219
  const reqFilename = `REQUIREMENTS-${computerId}.md`;
218
220
  const reqPath = path.join(repoPath, '.vibecodingmachine', reqFilename);
@@ -237,9 +239,11 @@ async function manageRemoteRequirements(computerId) {
237
239
  }
238
240
  await manageRemoteRequirements(computerId);
239
241
  break;
242
+ }
240
243
 
241
- case 'back':
244
+ case 'back': {
242
245
  break;
246
+ }
243
247
  }
244
248
  }
245
249
 
@@ -130,7 +130,7 @@ async function add(name, pkg, description) {
130
130
 
131
131
  // Find the TODO section
132
132
  const todoSectionHeader = '## ⏳ Requirements not yet completed';
133
- if (!content.includes(todoSectionHeader)) {
133
+ if (!content.includes('Requirements not yet completed')) {
134
134
  content += '\n\n' + todoSectionHeader + '\n';
135
135
  }
136
136
 
@@ -142,8 +142,8 @@ async function add(name, pkg, description) {
142
142
  for (let i = 0; i < lines.length; i++) {
143
143
  newLines.push(lines[i]);
144
144
 
145
- // Insert right after the TODO section header (new header format)
146
- if (!inserted && lines[i].includes(todoSectionHeader)) {
145
+ // Insert right after the TODO section header
146
+ if (!inserted && lines[i].startsWith('##') && lines[i].includes('Requirements not yet completed')) {
147
147
  // Add requirement header
148
148
  newLines.push(`### ${name}`);
149
149
 
@@ -338,10 +338,36 @@ async function rename(oldTitle, newTitle, description) {
338
338
  }
339
339
  }
340
340
 
341
+ async function working() {
342
+ try {
343
+ const { reqPath } = await getReqPathOrExit();
344
+ if (!await fs.pathExists(reqPath)) {
345
+ console.log(chalk.yellow('No REQUIREMENTS.md found.'));
346
+ return;
347
+ }
348
+ const { getCurrentRequirement } = require('vibecodingmachine-core');
349
+ // Note: Core's getCurrentRequirement returns "Working on: X" or default text.
350
+ // It assumes process.cwd() or similar.
351
+ // But wait, Core's getCurrentRequirement takes 'repoPath'.
352
+ // And imported from core index.cjs might need repoPath.
353
+
354
+ // Let's manually implement it to be safe and use getReqPathOrExit's resolved path
355
+ // OR use the core helper but pass the correct repo path.
356
+ const { repoPath } = await getReqPathOrExit();
357
+ const coreResult = await getCurrentRequirement(repoPath);
358
+ console.log(chalk.cyan(coreResult));
359
+
360
+ } catch (error) {
361
+ console.error(chalk.red('Error getting working requirement:'), error.message);
362
+ process.exit(1);
363
+ }
364
+ }
365
+
341
366
  module.exports = {
342
367
  list,
343
368
  add,
344
369
  current,
370
+ working,
345
371
  next,
346
372
  edit,
347
373
  watch,
@@ -64,7 +64,6 @@ async function progress() {
64
64
 
65
65
  async function logs(cmd) {
66
66
  const lines = parseInt((cmd && cmd.lines) || '50', 10);
67
- const repoPath = await getRepoPath();
68
67
  const defaultLog = path.join(process.cwd(), 'logs', 'security', 'security-2025-10-29.log');
69
68
  const logPath = await fs.pathExists(defaultLog) ? defaultLog : null;
70
69
  console.log(chalk.bold('\nRecent Logs'));
@@ -5,7 +5,7 @@ const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
5
5
  /**
6
6
  * Trigger immediate sync
7
7
  */
8
- async function syncNow(options = {}) {
8
+ async function syncNow() {
9
9
  const syncEngine = new SyncEngine();
10
10
 
11
11
  try {
@@ -52,7 +52,7 @@ async function syncNow(options = {}) {
52
52
  /**
53
53
  * Show sync status and statistics
54
54
  */
55
- async function syncStatus(options = {}) {
55
+ async function syncStatus() {
56
56
  const syncEngine = new SyncEngine();
57
57
 
58
58
  try {
@@ -117,7 +117,7 @@ async function syncStatus(options = {}) {
117
117
  /**
118
118
  * View pending changes in offline queue
119
119
  */
120
- async function viewQueue(options = {}) {
120
+ async function viewQueue() {
121
121
  const syncEngine = new SyncEngine();
122
122
 
123
123
  try {
@@ -172,7 +172,7 @@ async function viewQueue(options = {}) {
172
172
  /**
173
173
  * Force sync even if offline
174
174
  */
175
- async function forceSync(options = {}) {
175
+ async function forceSync() {
176
176
  const syncEngine = new SyncEngine();
177
177
 
178
178
  try {
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Centralized agent selection logic
3
+ * Respects provider preferences order and ensures DRY principle
4
+ */
5
+
6
+ const { getProviderPreferences } = require('./provider-registry');
7
+
8
+ /**
9
+ * Get the effective agent based on provider preferences and options
10
+ * @param {Object} options - Command line options (may include ide)
11
+ * @param {Array} providerDefinitions - Array of provider definitions
12
+ * @param {Map} providerDefinitionMap - Map of provider definitions by ID
13
+ * @returns {Object} - { effectiveAgent: string, providerDef: Object }
14
+ */
15
+ async function getEffectiveAgent(options = {}, providerDefinitions, providerDefinitionMap) {
16
+ const prefs = await getProviderPreferences();
17
+
18
+ // Get all available providers in the order specified in preferences
19
+ const availableProviders = [];
20
+ for (const id of prefs.order) {
21
+ if (prefs.enabled[id] !== false && providerDefinitionMap.has(id)) {
22
+ availableProviders.push(id);
23
+ }
24
+ }
25
+
26
+ // If no providers are available, use the first one from definitions as fallback
27
+ if (availableProviders.length === 0) {
28
+ availableProviders.push(providerDefinitions[0]?.id || 'claude-code');
29
+ }
30
+
31
+ // Use the first available provider by default, unless overridden by options
32
+ let effectiveAgent = options.ide || availableProviders[0];
33
+
34
+ // If the requested agent isn't available, use the first available one
35
+ if (!availableProviders.includes(effectiveAgent)) {
36
+ effectiveAgent = availableProviders[0];
37
+ }
38
+
39
+ const providerDef = providerDefinitionMap.get(effectiveAgent);
40
+
41
+ return {
42
+ effectiveAgent,
43
+ providerDef,
44
+ availableProviders
45
+ };
46
+ }
47
+
48
+ module.exports = {
49
+ getEffectiveAgent
50
+ };