vibecodingmachine-cli 2025.12.25-25 ā 2026.1.22-1441
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/__tests__/antigravity-js-handler.test.js +23 -0
- package/__tests__/provider-manager.test.js +84 -0
- package/__tests__/provider-rate-cache.test.js +27 -0
- package/bin/vibecodingmachine.js +92 -118
- package/logs/audit/2025-12-27.jsonl +1 -0
- package/logs/audit/2026-01-03.jsonl +2 -0
- package/package.json +2 -2
- package/reset_provider_order.js +21 -0
- package/scripts/convert-requirements.js +35 -0
- package/scripts/debug-parse.js +24 -0
- package/src/commands/auth.js +5 -1
- package/src/commands/auto-direct.js +747 -182
- package/src/commands/auto.js +206 -48
- package/src/commands/computers.js +9 -0
- package/src/commands/feature.js +123 -0
- package/src/commands/ide.js +108 -3
- package/src/commands/repo.js +27 -22
- package/src/commands/requirements-remote.js +34 -2
- package/src/commands/requirements.js +129 -9
- package/src/commands/setup.js +2 -1
- package/src/commands/status.js +39 -1
- package/src/commands/sync.js +7 -1
- package/src/utils/antigravity-js-handler.js +13 -4
- package/src/utils/auth.js +56 -25
- package/src/utils/compliance-check.js +10 -0
- package/src/utils/config.js +42 -1
- package/src/utils/date-formatter.js +44 -0
- package/src/utils/first-run.js +8 -6
- package/src/utils/interactive.js +1363 -334
- package/src/utils/kiro-js-handler.js +188 -0
- package/src/utils/prompt-helper.js +64 -0
- package/src/utils/provider-rate-cache.js +31 -0
- package/src/utils/provider-registry.js +42 -1
- package/src/utils/requirements-converter.js +107 -0
- package/src/utils/requirements-parser.js +144 -0
- package/tests/antigravity-js-handler.test.js +23 -0
- package/tests/home-bootstrap.test.js +76 -0
- package/tests/integration/health-tracking.integration.test.js +284 -0
- package/tests/provider-manager.test.js +92 -0
- package/tests/rate-limit-display.test.js +44 -0
- package/tests/requirements-bullet-parsing.test.js +15 -0
- package/tests/requirements-converter.test.js +42 -0
- package/tests/requirements-heading-count.test.js +27 -0
- package/tests/requirements-legacy-parsing.test.js +15 -0
- package/tests/requirements-parse-integration.test.js +44 -0
- package/tests/wait-for-ide-completion.test.js +56 -0
- package/tests/wait-for-ide-quota-detection-cursor-screenshot.test.js +61 -0
- package/tests/wait-for-ide-quota-detection-cursor.test.js +60 -0
- package/tests/wait-for-ide-quota-detection-negative.test.js +45 -0
- package/tests/wait-for-ide-quota-detection.test.js +59 -0
- package/verify_fix.js +36 -0
- package/verify_ui.js +38 -0
package/src/commands/auto.js
CHANGED
|
@@ -782,6 +782,16 @@ async function start(options) {
|
|
|
782
782
|
groqApiKey: savedConfig.groqApiKey
|
|
783
783
|
};
|
|
784
784
|
|
|
785
|
+
// If --ide-model is provided for Antigravity, store it on config so the IDE-switching logic can use it
|
|
786
|
+
if (options.ideModel && (config.ide === 'antigravity' || config.ide === 'windsurf')) {
|
|
787
|
+
config.ideModel = options.ideModel;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// If --extension is provided for VS Code extensions, store it on config
|
|
791
|
+
if (options.extension) {
|
|
792
|
+
config.extension = options.extension;
|
|
793
|
+
}
|
|
794
|
+
|
|
785
795
|
await setAutoConfig(config);
|
|
786
796
|
|
|
787
797
|
const providerType = providerDef?.type || 'direct';
|
|
@@ -1710,7 +1720,8 @@ Please implement this requirement now.`;
|
|
|
1710
1720
|
|
|
1711
1721
|
// Offer to open browser
|
|
1712
1722
|
const inquirer = require('inquirer');
|
|
1713
|
-
const {
|
|
1723
|
+
const { promptWithDefaultsOnce } = require('../utils/prompt-helper');
|
|
1724
|
+
const { openBrowser } = await promptWithDefaultsOnce([{
|
|
1714
1725
|
type: 'confirm',
|
|
1715
1726
|
name: 'openBrowser',
|
|
1716
1727
|
message: 'Open Groq Console in your browser?',
|
|
@@ -1806,7 +1817,8 @@ Please implement this requirement now.`;
|
|
|
1806
1817
|
|
|
1807
1818
|
if (profileContent.includes('GROQ_API_KEY')) {
|
|
1808
1819
|
console.log(chalk.yellow('ā GROQ_API_KEY already exists in your shell profile'));
|
|
1809
|
-
const {
|
|
1820
|
+
const { promptWithDefaultsOnce } = require('../utils/prompt-helper');
|
|
1821
|
+
const { replaceKey } = await promptWithDefaultsOnce([{
|
|
1810
1822
|
type: 'confirm',
|
|
1811
1823
|
name: 'replaceKey',
|
|
1812
1824
|
message: 'Replace existing key with new one?',
|
|
@@ -3391,7 +3403,8 @@ Example BAD questions (never ask these):
|
|
|
3391
3403
|
console.log(chalk.gray(' 2. Install the application'));
|
|
3392
3404
|
console.log(chalk.gray(' 3. Run this command again\n'));
|
|
3393
3405
|
|
|
3394
|
-
const {
|
|
3406
|
+
const { promptWithDefaultsOnce } = require('../utils/prompt-helper');
|
|
3407
|
+
const { continueAnyway } = await promptWithDefaultsOnce([{
|
|
3395
3408
|
type: 'confirm',
|
|
3396
3409
|
name: 'continueAnyway',
|
|
3397
3410
|
message: 'Did you install Ollama and want to continue?',
|
|
@@ -4647,7 +4660,28 @@ Example BAD questions (never ask these):
|
|
|
4647
4660
|
await stopAutoMode('error');
|
|
4648
4661
|
}
|
|
4649
4662
|
} else {
|
|
4650
|
-
// Use AppleScript for GUI IDEs
|
|
4663
|
+
// Use AppleScript for GUI IDEs with provider selection and fallback logic
|
|
4664
|
+
|
|
4665
|
+
// Import the provider selection logic from auto-direct.js
|
|
4666
|
+
const { acquireProviderConfig } = require('./auto-direct');
|
|
4667
|
+
|
|
4668
|
+
// Try to get an available provider (including IDE providers)
|
|
4669
|
+
const providerConfig = await acquireProviderConfig();
|
|
4670
|
+
|
|
4671
|
+
if (!providerConfig) {
|
|
4672
|
+
spinner.fail('No available providers found');
|
|
4673
|
+
console.log(chalk.red('\nā No available providers found. All providers are rate limited or disabled.'));
|
|
4674
|
+
console.log(chalk.gray('Try enabling additional providers in the Agent menu or wait for rate limits to reset.'));
|
|
4675
|
+
await stopAutoMode('error');
|
|
4676
|
+
return;
|
|
4677
|
+
}
|
|
4678
|
+
|
|
4679
|
+
// Update config to use the selected provider
|
|
4680
|
+
config.ide = providerConfig.provider;
|
|
4681
|
+
config.ideModel = providerConfig.model;
|
|
4682
|
+
await setAutoConfig(config);
|
|
4683
|
+
|
|
4684
|
+
console.log(chalk.green(`ā Selected provider: ${providerConfig.displayName}`));
|
|
4651
4685
|
|
|
4652
4686
|
// Check if IDE is installed, download if needed
|
|
4653
4687
|
spinner.text = `Checking ${config.ide} installation...`;
|
|
@@ -4659,7 +4693,8 @@ Example BAD questions (never ask these):
|
|
|
4659
4693
|
'cursor': '/Applications/Cursor.app',
|
|
4660
4694
|
'windsurf': '/Applications/Windsurf.app',
|
|
4661
4695
|
'antigravity': '/Applications/Antigravity.app',
|
|
4662
|
-
'vscode': '/Applications/Visual Studio Code.app'
|
|
4696
|
+
'vscode': '/Applications/Visual Studio Code.app',
|
|
4697
|
+
'replit': 'web-based' // Replit is web-based, no desktop app
|
|
4663
4698
|
};
|
|
4664
4699
|
|
|
4665
4700
|
const ideDownloadUrls = {
|
|
@@ -4670,9 +4705,39 @@ Example BAD questions (never ask these):
|
|
|
4670
4705
|
|
|
4671
4706
|
const ideAppPath = ideAppPaths[config.ide];
|
|
4672
4707
|
|
|
4673
|
-
if (ideAppPath && !fs.existsSync(ideAppPath)) {
|
|
4708
|
+
if (ideAppPath && ideAppPath !== 'web-based' && !fs.existsSync(ideAppPath)) {
|
|
4674
4709
|
spinner.warn(chalk.yellow(`${config.ide} not found at ${ideAppPath}`));
|
|
4675
4710
|
|
|
4711
|
+
// Try to get another available provider
|
|
4712
|
+
console.log(chalk.yellow(`ā ļø ${config.ide} not available, trying next provider...`));
|
|
4713
|
+
const fallbackProviderConfig = await acquireProviderConfig(config.ide);
|
|
4714
|
+
|
|
4715
|
+
if (!fallbackProviderConfig) {
|
|
4716
|
+
spinner.fail('No available IDE providers found');
|
|
4717
|
+
console.log(chalk.red('\nā No available IDE providers found. All IDEs are rate limited or disabled.'));
|
|
4718
|
+
console.log(chalk.gray('Try enabling additional providers in the Agent menu or wait for rate limits to reset.'));
|
|
4719
|
+
await stopAutoMode('error');
|
|
4720
|
+
return;
|
|
4721
|
+
}
|
|
4722
|
+
|
|
4723
|
+
// Update config to use the fallback provider
|
|
4724
|
+
config.ide = fallbackProviderConfig.provider;
|
|
4725
|
+
config.ideModel = fallbackProviderConfig.model;
|
|
4726
|
+
await setAutoConfig(config);
|
|
4727
|
+
|
|
4728
|
+
console.log(chalk.green(`ā Switched to: ${fallbackProviderConfig.displayName}`));
|
|
4729
|
+
|
|
4730
|
+
// Update ideAppPath for the new provider
|
|
4731
|
+
const newIdeAppPath = ideAppPaths[config.ide];
|
|
4732
|
+
if (newIdeAppPath && !fs.existsSync(newIdeAppPath)) {
|
|
4733
|
+
// If the fallback IDE is also not available, continue with the download logic
|
|
4734
|
+
console.log(chalk.yellow(`ā ļø ${config.ide} also not found, will download if available...`));
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4738
|
+
// Check if IDE needs to be downloaded
|
|
4739
|
+
const currentIdeAppPath = ideAppPaths[config.ide];
|
|
4740
|
+
if (currentIdeAppPath && currentIdeAppPath !== 'web-based' && !fs.existsSync(currentIdeAppPath)) {
|
|
4676
4741
|
const downloadUrl = ideDownloadUrls[config.ide];
|
|
4677
4742
|
if (downloadUrl) {
|
|
4678
4743
|
console.log(chalk.cyan(`š„ Downloading ${config.ide}...`));
|
|
@@ -4777,56 +4842,113 @@ Example BAD questions (never ask these):
|
|
|
4777
4842
|
spinner.text = `Launching ${config.ide}...`;
|
|
4778
4843
|
const { execSync } = require('child_process');
|
|
4779
4844
|
|
|
4845
|
+
// Use AppleScriptManager to open IDE with auto-installation support
|
|
4846
|
+
const ideManager = new AppleScriptManager();
|
|
4847
|
+
|
|
4780
4848
|
try {
|
|
4781
|
-
|
|
4782
|
-
const
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
{ encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
4787
|
-
if (isRunning) {
|
|
4788
|
-
console.log(chalk.gray(` ${t('auto.ide.is.running', { ide: config.ide, pid: isRunning.split('\n')[0] })}`));
|
|
4849
|
+
console.log(chalk.gray(` Opening ${config.ide}...`));
|
|
4850
|
+
const openResult = await ideManager.openIDE(config.ide, repoPath);
|
|
4851
|
+
|
|
4852
|
+
if (!openResult.success) {
|
|
4853
|
+
throw new Error(openResult.error || `Failed to open ${config.ide}`);
|
|
4789
4854
|
}
|
|
4790
|
-
} catch (err) {
|
|
4791
|
-
// Not running, launch it
|
|
4792
|
-
console.log(chalk.gray(` Launching ${config.ide}...`));
|
|
4793
|
-
const appName = config.ide === 'antigravity' ? 'Antigravity' :
|
|
4794
|
-
config.ide === 'cursor' ? 'Cursor' :
|
|
4795
|
-
config.ide === 'windsurf' ? 'Windsurf' :
|
|
4796
|
-
config.ide === 'vscode' ? 'Visual Studio Code' : config.ide;
|
|
4797
4855
|
|
|
4798
|
-
|
|
4856
|
+
console.log(chalk.green(` ā ${config.ide} opened successfully`));
|
|
4799
4857
|
|
|
4800
|
-
// Wait for the app to
|
|
4858
|
+
// Wait for the app to be ready
|
|
4801
4859
|
const waitTime = (config.ide === 'antigravity' || config.ide === 'cursor' || config.ide === 'windsurf') ? 5000 : 3000;
|
|
4802
|
-
console.log(chalk.gray(` Waiting for ${config.ide} to start...`));
|
|
4803
4860
|
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
4804
4861
|
|
|
4805
|
-
|
|
4862
|
+
} catch (err) {
|
|
4863
|
+
console.log(chalk.yellow(` ā Failed to open ${config.ide}: ${err.message}`));
|
|
4864
|
+
console.log(chalk.gray(` Attempting fallback launch method...`));
|
|
4865
|
+
|
|
4806
4866
|
try {
|
|
4807
|
-
|
|
4867
|
+
// Skip fallback launch for web-based IDEs like Replit
|
|
4868
|
+
if (config.ide === 'replit') {
|
|
4869
|
+
throw new Error('Replit is web-based and should be opened via browser, not as desktop app');
|
|
4870
|
+
}
|
|
4871
|
+
|
|
4872
|
+
const appName = config.ide === 'antigravity' ? 'Antigravity' :
|
|
4808
4873
|
config.ide === 'cursor' ? 'Cursor' :
|
|
4809
4874
|
config.ide === 'windsurf' ? 'Windsurf' :
|
|
4810
|
-
config.ide === 'vscode' ? 'Code' :
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4875
|
+
config.ide === 'vscode' ? 'Visual Studio Code' :
|
|
4876
|
+
config.ide === 'github-copilot' ? 'Visual Studio Code' :
|
|
4877
|
+
config.ide === 'amazon-q' ? 'Visual Studio Code' : config.ide;
|
|
4878
|
+
|
|
4879
|
+
// For VS Code IDEs, close existing instance to enable remote debugging
|
|
4880
|
+
if (config.ide === 'vscode' || config.ide === 'github-copilot' || config.ide === 'amazon-q') {
|
|
4881
|
+
try {
|
|
4882
|
+
const isRunning = execSync('pgrep -x "Code"', { encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
4883
|
+
if (isRunning) {
|
|
4884
|
+
console.log(chalk.gray(` Closing existing VS Code instance to enable remote debugging...`));
|
|
4885
|
+
execSync('pkill -x "Code"', { stdio: 'pipe' });
|
|
4886
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
4887
|
+
}
|
|
4888
|
+
} catch (err) {
|
|
4889
|
+
// VS Code not running, that's fine
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
|
|
4893
|
+
// Add remote debugging port for quota detection
|
|
4894
|
+
let command;
|
|
4895
|
+
if (config.ide === 'vscode' || config.ide === 'github-copilot' || config.ide === 'amazon-q') {
|
|
4896
|
+
command = `open -a "${appName}" "${repoPath}" --args --remote-debugging-port=9222`;
|
|
4897
|
+
} else if (config.ide === 'cursor') {
|
|
4898
|
+
command = `open -a "${appName}" "${repoPath}" --args --remote-debugging-port=9225`;
|
|
4899
|
+
} else {
|
|
4900
|
+
command = `open -a "${appName}" "${repoPath}"`;
|
|
4901
|
+
}
|
|
4902
|
+
|
|
4903
|
+
execSync(command, { encoding: 'utf8' });
|
|
4904
|
+
const waitTime = 5000;
|
|
4905
|
+
console.log(chalk.gray(` Waiting for ${config.ide} to start...`));
|
|
4906
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
4907
|
+
} catch (fallbackErr) {
|
|
4908
|
+
console.log(chalk.red(` ā Failed to launch ${config.ide}: ${fallbackErr.message}`));
|
|
4909
|
+
throw fallbackErr;
|
|
4816
4910
|
}
|
|
4817
4911
|
}
|
|
4818
4912
|
|
|
4819
4913
|
spinner.text = 'Sending initial message to IDE...';
|
|
4820
|
-
const asManager =
|
|
4914
|
+
const asManager = ideManager; // Reuse the IDE manager instance
|
|
4915
|
+
|
|
4916
|
+
// Set the extension on the AppleScriptManager for VS Code extensions
|
|
4917
|
+
if (config.extension && config.ide === 'vscode') {
|
|
4918
|
+
asManager.currentExtension = config.extension;
|
|
4919
|
+
console.log(chalk.gray(` Using VS Code extension: ${config.extension}`));
|
|
4920
|
+
}
|
|
4821
4921
|
|
|
4822
4922
|
try {
|
|
4923
|
+
// If we have an ideModel for Antigravity, try to switch the model first
|
|
4924
|
+
if (config.ide === 'antigravity' && config.ideModel) {
|
|
4925
|
+
console.log(chalk.gray(` Switching Antigravity model to: ${config.ideModel}`));
|
|
4926
|
+
const switchResult = await asManager.switchAntigravityModel(config.ideModel);
|
|
4927
|
+
if (switchResult.success) {
|
|
4928
|
+
console.log(chalk.green(` ā Switched to model: ${switchResult.model}`));
|
|
4929
|
+
} else {
|
|
4930
|
+
console.log(chalk.yellow(` ā ļø Failed to switch model: ${switchResult.error}`));
|
|
4931
|
+
}
|
|
4932
|
+
}
|
|
4933
|
+
|
|
4823
4934
|
const result = await asManager.sendText(textToSend, config.ide);
|
|
4824
4935
|
|
|
4825
4936
|
if (!result.success) {
|
|
4826
4937
|
logIDEMessage(config.ide, `[FAILED] ${textToSend}`);
|
|
4827
4938
|
spinner.warn('Auto mode started but failed to send initial message to IDE');
|
|
4828
|
-
|
|
4829
|
-
|
|
4939
|
+
|
|
4940
|
+
if (result.permissionError) {
|
|
4941
|
+
console.log(chalk.bold.red('\nš« PERMISSION ERROR: macOS Accessibility Access Required'));
|
|
4942
|
+
console.log(chalk.white(` Vibe Coding Machine needs permission to control ${config.ide === 'antigravity' ? 'Antigravity' : 'the IDE'} to send messages.`));
|
|
4943
|
+
console.log(chalk.white('\nš Action Required:'));
|
|
4944
|
+
console.log(chalk.cyan(' 1. System Settings verification window should have opened.'));
|
|
4945
|
+
console.log(chalk.cyan(' 2. Enable the switch for "Terminal" (or Node/VSCode) in the list.'));
|
|
4946
|
+
console.log(chalk.cyan(' 3. Run this command again.'));
|
|
4947
|
+
console.log(chalk.gray('\n (If settings didn\'t open: System Settings -> Privacy & Security -> Accessibility)'));
|
|
4948
|
+
} else {
|
|
4949
|
+
console.log(chalk.yellow('\nā Warning:'), result.error || 'Failed to send message');
|
|
4950
|
+
console.log(chalk.gray(' Consider using'), chalk.cyan('Cline IDE'), chalk.gray('instead (set with'), chalk.cyan('vcm'), chalk.gray('menu)'));
|
|
4951
|
+
}
|
|
4830
4952
|
} else {
|
|
4831
4953
|
logIDEMessage(config.ide, textToSend);
|
|
4832
4954
|
spinner.succeed(t('auto.direct.ide.autonomous.started'));
|
|
@@ -4860,6 +4982,14 @@ Example BAD questions (never ask these):
|
|
|
4860
4982
|
} else if (config.maxChats) {
|
|
4861
4983
|
console.log(chalk.gray(` ${t('auto.max.chats.label')}`), chalk.cyan(config.maxChats));
|
|
4862
4984
|
}
|
|
4985
|
+
|
|
4986
|
+
// For GUI IDEs, we've sent the message and now we're done with the startup process.
|
|
4987
|
+
// If we're not running a monitoring loop here, we should signal that we're finished.
|
|
4988
|
+
// But wait, if this is called from auto-direct, auto-direct will handle its own status.
|
|
4989
|
+
// To be safe, we only stop if this is a GUI IDE agent which by definition doesn't have a local loop in this process.
|
|
4990
|
+
if (['cursor', 'windsurf', 'antigravity', 'vscode'].includes(config.ide)) {
|
|
4991
|
+
await stopAutoMode('ide-started');
|
|
4992
|
+
}
|
|
4863
4993
|
} catch (error) {
|
|
4864
4994
|
spinner.stop();
|
|
4865
4995
|
spinner.fail('Failed to start autonomous mode');
|
|
@@ -4964,12 +5094,24 @@ async function listAgents() {
|
|
|
4964
5094
|
|
|
4965
5095
|
console.log(chalk.blue('\nš Available IDE Agents and Quota Status:\n'));
|
|
4966
5096
|
|
|
5097
|
+
const formatTimeAmPm = (date) => {
|
|
5098
|
+
const hours24 = date.getHours();
|
|
5099
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
5100
|
+
const ampm = hours24 >= 12 ? 'pm' : 'am';
|
|
5101
|
+
const hours12 = (hours24 % 12) || 12;
|
|
5102
|
+
return `${hours12}:${minutes} ${ampm}`;
|
|
5103
|
+
};
|
|
5104
|
+
|
|
5105
|
+
const { formatResetsAtLabel } = require('../utils/date-formatter');
|
|
5106
|
+
// Note: formatResetsAtLabel returns null when the reset minute has started (so no 'resetting soon' text will be shown).
|
|
5107
|
+
|
|
4967
5108
|
// Get all provider definitions
|
|
4968
5109
|
const providers = getProviderDefinitions();
|
|
4969
5110
|
|
|
4970
5111
|
// Check each provider's quota status
|
|
4971
5112
|
for (const provider of providers) {
|
|
4972
|
-
const
|
|
5113
|
+
const hasSubAgents = Array.isArray(provider.subAgents) && provider.subAgents.length > 0;
|
|
5114
|
+
const rateLimitInfo = hasSubAgents ? { isRateLimited: false } : providerManager.getRateLimitInfo(provider.id);
|
|
4973
5115
|
|
|
4974
5116
|
let statusIcon = 'ā
';
|
|
4975
5117
|
let statusText = chalk.green('Available');
|
|
@@ -4980,18 +5122,9 @@ async function listAgents() {
|
|
|
4980
5122
|
statusText = chalk.yellow('Quota Limit');
|
|
4981
5123
|
|
|
4982
5124
|
if (rateLimitInfo.resetTime) {
|
|
4983
|
-
const
|
|
4984
|
-
|
|
4985
|
-
|
|
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
|
-
}
|
|
5125
|
+
const label = formatResetsAtLabel(rateLimitInfo.resetTime);
|
|
5126
|
+
// If label is null, the reset minute has begun or the time is invalid ā don't show any reset text.
|
|
5127
|
+
quotaInfo = label ? chalk.gray(` (${label})`) : '';
|
|
4995
5128
|
}
|
|
4996
5129
|
|
|
4997
5130
|
if (rateLimitInfo.reason) {
|
|
@@ -5003,6 +5136,31 @@ async function listAgents() {
|
|
|
5003
5136
|
if (provider.description) {
|
|
5004
5137
|
console.log(` ${chalk.gray(provider.description)}`);
|
|
5005
5138
|
}
|
|
5139
|
+
|
|
5140
|
+
// If provider has sub-agents, show them under the provider row
|
|
5141
|
+
if (hasSubAgents) {
|
|
5142
|
+
for (const sub of provider.subAgents) {
|
|
5143
|
+
const subInfo = providerManager.getRateLimitInfo(provider.id, sub.model);
|
|
5144
|
+
|
|
5145
|
+
let subStatusIcon = 'ā
';
|
|
5146
|
+
let subStatusText = chalk.green('Available');
|
|
5147
|
+
let subQuotaInfo = '';
|
|
5148
|
+
|
|
5149
|
+
if (subInfo.isRateLimited) {
|
|
5150
|
+
subStatusIcon = 'ā ļø';
|
|
5151
|
+
subStatusText = chalk.yellow('Quota Limit');
|
|
5152
|
+
if (subInfo.resetTime) {
|
|
5153
|
+
const label = formatResetsAtLabel(subInfo.resetTime);
|
|
5154
|
+
subQuotaInfo = label ? chalk.gray(` (${label})`) : '';
|
|
5155
|
+
}
|
|
5156
|
+
if (subInfo.reason) {
|
|
5157
|
+
subQuotaInfo += chalk.gray(` - ${subInfo.reason}`);
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
|
|
5161
|
+
console.log(` ${subStatusIcon} ${chalk.cyan(sub.name)} ${subStatusText}${subQuotaInfo}`);
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5006
5164
|
}
|
|
5007
5165
|
|
|
5008
5166
|
console.log(chalk.gray('\nš” Tip: Auto mode will automatically skip quota-limited agents\n'));
|
|
@@ -86,6 +86,11 @@ async function listComputers(options = {}) {
|
|
|
86
86
|
|
|
87
87
|
} catch (error) {
|
|
88
88
|
console.error(chalk.red(`\nā ${t('computers.fetch.failed')}`), error.message);
|
|
89
|
+
await errorReporter.reportError(error, {
|
|
90
|
+
command: 'listComputers',
|
|
91
|
+
focusArea,
|
|
92
|
+
status
|
|
93
|
+
});
|
|
89
94
|
throw error;
|
|
90
95
|
} finally {
|
|
91
96
|
syncEngine.stop();
|
|
@@ -179,6 +184,10 @@ async function registerComputer(focusArea) {
|
|
|
179
184
|
|
|
180
185
|
} catch (error) {
|
|
181
186
|
console.error(chalk.red(`\nā ${t('computers.register.failed')}`), error.message);
|
|
187
|
+
await errorReporter.reportError(error, {
|
|
188
|
+
command: 'registerComputer',
|
|
189
|
+
focusArea
|
|
190
|
+
});
|
|
182
191
|
throw error;
|
|
183
192
|
} finally {
|
|
184
193
|
syncEngine.stop();
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { getRepoPath } = require('../utils/config');
|
|
3
|
+
const {
|
|
4
|
+
createRequirementBranch,
|
|
5
|
+
mergeRequirementBranch,
|
|
6
|
+
removeRequirementFeature,
|
|
7
|
+
listRequirementBranches,
|
|
8
|
+
getCurrentBranch
|
|
9
|
+
} = require('vibecodingmachine-core');
|
|
10
|
+
|
|
11
|
+
async function start(requirementTitle) {
|
|
12
|
+
try {
|
|
13
|
+
const repoPath = await getRepoPath();
|
|
14
|
+
if (!repoPath) {
|
|
15
|
+
console.log(chalk.yellow('No repository configured'));
|
|
16
|
+
console.log(chalk.gray('Run'), chalk.cyan('vcm repo:set <path>'));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log(chalk.cyan(`Creating feature branch for: ${requirementTitle}`));
|
|
21
|
+
const result = await createRequirementBranch(repoPath, requirementTitle);
|
|
22
|
+
|
|
23
|
+
if (result.success) {
|
|
24
|
+
if (result.alreadyExists) {
|
|
25
|
+
console.log(chalk.yellow(`ā Switched to existing branch: ${result.branchName}`));
|
|
26
|
+
} else {
|
|
27
|
+
console.log(chalk.green(`ā Created and switched to branch: ${result.branchName}`));
|
|
28
|
+
}
|
|
29
|
+
console.log(chalk.gray(` Parent branch: ${result.parentBranch}`));
|
|
30
|
+
} else {
|
|
31
|
+
console.log(chalk.red(`ā Failed to create branch: ${result.error}`));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error(chalk.red('Error creating feature branch:'), error.message);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function finish(requirementTitle, options = {}) {
|
|
41
|
+
try {
|
|
42
|
+
const repoPath = await getRepoPath();
|
|
43
|
+
if (!repoPath) {
|
|
44
|
+
console.log(chalk.yellow('No repository configured'));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const parentBranch = options.parent || 'main';
|
|
49
|
+
|
|
50
|
+
console.log(chalk.cyan(`Merging feature branch for: ${requirementTitle}`));
|
|
51
|
+
const result = await mergeRequirementBranch(repoPath, requirementTitle, parentBranch);
|
|
52
|
+
|
|
53
|
+
if (result.success) {
|
|
54
|
+
console.log(chalk.green(`ā Feature merged and branch deleted`));
|
|
55
|
+
} else {
|
|
56
|
+
console.log(chalk.red(`ā Failed to merge branch: ${result.error}`));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error(chalk.red('Error merging feature branch:'), error.message);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function remove(requirementTitle) {
|
|
66
|
+
try {
|
|
67
|
+
const repoPath = await getRepoPath();
|
|
68
|
+
if (!repoPath) {
|
|
69
|
+
console.log(chalk.yellow('No repository configured'));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(chalk.cyan(`Removing feature for: ${requirementTitle}`));
|
|
74
|
+
const result = await removeRequirementFeature(repoPath, requirementTitle);
|
|
75
|
+
|
|
76
|
+
if (result.success) {
|
|
77
|
+
console.log(chalk.green(`ā Feature removed (merge reverted)`));
|
|
78
|
+
} else {
|
|
79
|
+
console.log(chalk.red(`ā Failed to remove feature: ${result.error}`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(chalk.red('Error removing feature:'), error.message);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function list() {
|
|
89
|
+
try {
|
|
90
|
+
const repoPath = await getRepoPath();
|
|
91
|
+
if (!repoPath) {
|
|
92
|
+
console.log(chalk.yellow('No repository configured'));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const branches = listRequirementBranches(repoPath);
|
|
97
|
+
const currentBranch = getCurrentBranch(repoPath);
|
|
98
|
+
|
|
99
|
+
if (branches.length === 0) {
|
|
100
|
+
console.log(chalk.yellow('No requirement branches found'));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log(chalk.cyan('\nRequirement Branches:\n'));
|
|
105
|
+
branches.forEach(({ branchName, reqNumber }) => {
|
|
106
|
+
const isCurrent = branchName === currentBranch;
|
|
107
|
+
const marker = isCurrent ? chalk.green('* ') : ' ';
|
|
108
|
+
const color = isCurrent ? chalk.green : chalk.white;
|
|
109
|
+
console.log(`${marker}${color(branchName)}`);
|
|
110
|
+
});
|
|
111
|
+
console.log();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error(chalk.red('Error listing branches:'), error.message);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = {
|
|
119
|
+
start,
|
|
120
|
+
finish,
|
|
121
|
+
remove,
|
|
122
|
+
list
|
|
123
|
+
};
|
package/src/commands/ide.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const ora = require('ora');
|
|
4
|
-
const { AppleScriptManager } = require('vibecodingmachine-core');
|
|
4
|
+
const { AppleScriptManager, IDEHealthTracker } = require('vibecodingmachine-core');
|
|
5
5
|
const { getRepoPath } = require('../utils/config');
|
|
6
6
|
|
|
7
|
-
const IDES = ['cursor', 'vscode', 'windsurf', 'antigravity', 'cline'];
|
|
7
|
+
const IDES = ['cursor', 'vscode', 'windsurf', 'antigravity', 'cline', 'github-copilot', 'amazon-q', 'replit', 'kiro'];
|
|
8
8
|
|
|
9
9
|
async function list() {
|
|
10
10
|
console.log(chalk.bold('\nAvailable IDEs'));
|
|
@@ -60,10 +60,115 @@ async function send(message, options) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
async function health(options = {}) {
|
|
64
|
+
const { verbose = false } = options;
|
|
65
|
+
console.log(chalk.bold('\nš„ IDE Health Test'));
|
|
66
|
+
console.log(chalk.gray('Testing connectivity and responsiveness of all configured IDEs...\n'));
|
|
67
|
+
|
|
68
|
+
const healthTracker = new IDEHealthTracker();
|
|
69
|
+
const results = [];
|
|
70
|
+
|
|
71
|
+
for (const ide of IDES) {
|
|
72
|
+
const startTime = Date.now();
|
|
73
|
+
const spinner = ora(`Testing ${ide}...`).start();
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
// Test basic connectivity by attempting to get IDE status
|
|
77
|
+
const appleScriptManager = new AppleScriptManager();
|
|
78
|
+
|
|
79
|
+
// For web-based IDEs, we'll simulate a basic check
|
|
80
|
+
if (['replit', 'antigravity', 'kiro'].includes(ide)) {
|
|
81
|
+
// Simulate health check for web-based IDEs
|
|
82
|
+
await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 2000));
|
|
83
|
+
spinner.succeed(chalk.green(`${ide} - Connected`));
|
|
84
|
+
results.push({ ide, status: 'success', responseTime: Date.now() - startTime });
|
|
85
|
+
} else {
|
|
86
|
+
// For desktop IDEs, try to get basic status
|
|
87
|
+
await appleScriptManager.getIDEStatus(ide);
|
|
88
|
+
const responseTime = Date.now() - startTime;
|
|
89
|
+
|
|
90
|
+
spinner.succeed(chalk.green(`${ide} - Connected (${responseTime}ms)`));
|
|
91
|
+
results.push({ ide, status: 'success', responseTime });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Record successful health test
|
|
95
|
+
await healthTracker.recordSuccess(ide, Date.now() - startTime, {
|
|
96
|
+
requirementId: 'health-test',
|
|
97
|
+
timeoutUsed: 120000 // 2 minute health test timeout
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
} catch (error) {
|
|
101
|
+
const responseTime = Date.now() - startTime;
|
|
102
|
+
|
|
103
|
+
// Check if it timed out (2 minute limit)
|
|
104
|
+
if (responseTime >= 120000) {
|
|
105
|
+
spinner.fail(chalk.red(`${ide} - Timeout (${Math.floor(responseTime / 1000)}s)`));
|
|
106
|
+
results.push({ ide, status: 'timeout', responseTime, error: 'Health test timeout' });
|
|
107
|
+
} else {
|
|
108
|
+
spinner.fail(chalk.red(`${ide} - Failed (${error.message})`));
|
|
109
|
+
results.push({ ide, status: 'failed', responseTime, error: error.message });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Record failed health test
|
|
113
|
+
await healthTracker.recordFailure(ide, error.message, {
|
|
114
|
+
requirementId: 'health-test',
|
|
115
|
+
timeoutUsed: 120000
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Small delay between tests
|
|
120
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Summary
|
|
124
|
+
console.log(chalk.bold('\nš Health Test Summary'));
|
|
125
|
+
console.log(chalk.gray('ā'.repeat(50)));
|
|
126
|
+
|
|
127
|
+
const successful = results.filter(r => r.status === 'success');
|
|
128
|
+
const failed = results.filter(r => r.status === 'failed');
|
|
129
|
+
const timeouts = results.filter(r => r.status === 'timeout');
|
|
130
|
+
|
|
131
|
+
console.log(chalk.green(`ā
Successful: ${successful.length}`));
|
|
132
|
+
console.log(chalk.red(`ā Failed: ${failed.length}`));
|
|
133
|
+
console.log(chalk.yellow(`ā° Timeouts: ${timeouts.length}`));
|
|
134
|
+
console.log(chalk.blue(`š” Total Tested: ${results.length}`));
|
|
135
|
+
|
|
136
|
+
if (verbose && successful.length > 0) {
|
|
137
|
+
console.log(chalk.bold('\nš Successful IDEs:'));
|
|
138
|
+
for (const result of successful) {
|
|
139
|
+
const metrics = await healthTracker.getHealthMetrics(result.ide);
|
|
140
|
+
console.log(chalk.cyan(` ${result.ide}:`),
|
|
141
|
+
chalk.gray(`${result.responseTime}ms`),
|
|
142
|
+
chalk.green(`(+${metrics.successCount} -${metrics.failureCount})`));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if ((failed.length > 0 || timeouts.length > 0) && verbose) {
|
|
147
|
+
console.log(chalk.bold('\nā ļø Problematic IDEs:'));
|
|
148
|
+
for (const result of [...failed, ...timeouts]) {
|
|
149
|
+
console.log(chalk.red(` ${result.ide}:`), chalk.gray(result.error || 'Timeout'));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log();
|
|
154
|
+
|
|
155
|
+
// Get recommendation
|
|
156
|
+
const recommendedIDE = await healthTracker.getRecommendedIDE({ minInteractions: 1 });
|
|
157
|
+
if (recommendedIDE) {
|
|
158
|
+
console.log(chalk.bold('š” Recommended IDE:'), chalk.cyan(recommendedIDE));
|
|
159
|
+
const metrics = await healthTracker.getHealthMetrics(recommendedIDE);
|
|
160
|
+
console.log(chalk.gray(` Success rate: ${(metrics.successRate * 100).toFixed(1)}%`));
|
|
161
|
+
console.log(chalk.gray(` Avg response time: ${Math.round(metrics.averageResponseTime / 1000)}s`));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log();
|
|
165
|
+
}
|
|
166
|
+
|
|
63
167
|
module.exports = {
|
|
64
168
|
list,
|
|
65
169
|
open,
|
|
66
|
-
send
|
|
170
|
+
send,
|
|
171
|
+
health
|
|
67
172
|
};
|
|
68
173
|
|
|
69
174
|
|