vibecodingmachine-cli 2026.1.29-713 → 2026.2.20-423

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.
Files changed (45) hide show
  1. package/bin/vibecodingmachine.js +124 -0
  2. package/package.json +3 -2
  3. package/src/commands/agents-check.js +69 -0
  4. package/src/commands/auto-direct.js +930 -145
  5. package/src/commands/auto.js +26 -4
  6. package/src/commands/ide.js +2 -1
  7. package/src/commands/requirements.js +23 -27
  8. package/src/utils/auto-mode.js +4 -1
  9. package/src/utils/cline-js-handler.js +218 -0
  10. package/src/utils/config.js +22 -0
  11. package/src/utils/display-formatters-complete.js +229 -0
  12. package/src/utils/display-formatters-extracted.js +219 -0
  13. package/src/utils/display-formatters.js +157 -0
  14. package/src/utils/feedback-handler.js +143 -0
  15. package/src/utils/ide-detection-complete.js +126 -0
  16. package/src/utils/ide-detection-extracted.js +116 -0
  17. package/src/utils/ide-detection.js +124 -0
  18. package/src/utils/interactive-backup.js +5664 -0
  19. package/src/utils/interactive-broken.js +280 -0
  20. package/src/utils/interactive.js +31 -5534
  21. package/src/utils/provider-checker.js +410 -0
  22. package/src/utils/provider-manager.js +251 -0
  23. package/src/utils/provider-registry.js +18 -9
  24. package/src/utils/requirement-actions.js +884 -0
  25. package/src/utils/requirements-navigator.js +585 -0
  26. package/src/utils/rui-trui-adapter.js +311 -0
  27. package/src/utils/simple-trui.js +204 -0
  28. package/src/utils/status-helpers-extracted.js +125 -0
  29. package/src/utils/status-helpers.js +107 -0
  30. package/src/utils/trui-debug.js +261 -0
  31. package/src/utils/trui-feedback.js +133 -0
  32. package/src/utils/trui-nav-agents.js +119 -0
  33. package/src/utils/trui-nav-requirements.js +268 -0
  34. package/src/utils/trui-nav-settings.js +157 -0
  35. package/src/utils/trui-nav-specifications.js +139 -0
  36. package/src/utils/trui-navigation.js +303 -0
  37. package/src/utils/trui-provider-manager.js +182 -0
  38. package/src/utils/trui-quick-menu.js +365 -0
  39. package/src/utils/trui-req-actions.js +372 -0
  40. package/src/utils/trui-req-tree.js +534 -0
  41. package/src/utils/trui-specifications.js +359 -0
  42. package/src/utils/trui-text-editor.js +350 -0
  43. package/src/utils/trui-windsurf.js +336 -0
  44. package/src/utils/welcome-screen-extracted.js +135 -0
  45. package/src/utils/welcome-screen.js +134 -0
@@ -753,8 +753,15 @@ async function start(options) {
753
753
  // Use the first available provider by default, unless overridden by options
754
754
  let effectiveAgent = options.ide || availableProviders[0];
755
755
 
756
- // If the requested agent isn't available, use the first available one
757
- if (!availableProviders.includes(effectiveAgent)) {
756
+ // Special handling for Cline CLI - if requested but not available, we'll install it later
757
+ const isClineRequested = options.ide === 'cline';
758
+
759
+ // If Cline CLI is specifically requested, use it regardless of availability (we'll install it)
760
+ if (isClineRequested) {
761
+ effectiveAgent = 'cline';
762
+ }
763
+ // Otherwise, if the requested agent isn't available, use the first available one
764
+ else if (!availableProviders.includes(effectiveAgent)) {
758
765
  effectiveAgent = availableProviders[0];
759
766
  }
760
767
 
@@ -3285,7 +3292,7 @@ Example BAD questions (never ask these):
3285
3292
  if (!installResult.success) {
3286
3293
  spinner.fail('Failed to install Cline CLI');
3287
3294
  console.log(chalk.red('\nāœ— Error:'), installResult.error);
3288
- console.log(chalk.gray(' You can manually install with:'), chalk.cyan('npm install -g @yaegaki/cline-cli'));
3295
+ console.log(chalk.gray(' You can manually install with:'), chalk.cyan('npm install -g cline'));
3289
3296
  process.exit(1);
3290
3297
  }
3291
3298
 
@@ -4931,7 +4938,22 @@ Example BAD questions (never ask these):
4931
4938
  }
4932
4939
  }
4933
4940
 
4934
- const result = await asManager.sendText(textToSend, config.ide);
4941
+ // Ensure AppleScript manager has extension set where applicable
4942
+ try {
4943
+ if (config.extension) asManager.currentExtension = config.extension;
4944
+ } catch (_) {}
4945
+
4946
+ let result;
4947
+ // If configured to use standalone Windsurf IDE, send explicitly to 'windsurf'
4948
+ if (config.ide === 'windsurf') {
4949
+ try { asManager.currentExtension = 'windsurf'; } catch (_) {}
4950
+ result = await asManager.sendText(textToSend, 'windsurf');
4951
+ } else if (config.ide === 'vscode' && config.extension === 'windsurf') {
4952
+ // VS Code with Windsurf extension should be routed via vscode target
4953
+ result = await asManager.sendText(textToSend, 'vscode');
4954
+ } else {
4955
+ result = await asManager.sendText(textToSend, config.ide);
4956
+ }
4935
4957
 
4936
4958
  if (!result.success) {
4937
4959
  logIDEMessage(config.ide, `[FAILED] ${textToSend}`);
@@ -50,7 +50,8 @@ async function send(message, options) {
50
50
  const appleScriptManager = new AppleScriptManager();
51
51
 
52
52
  // Use AppleScriptManager to send text to IDE AI chat
53
- await appleScriptManager.sendTextToIDE(ide, message);
53
+ const repoPath = await getRepoPath();
54
+ await appleScriptManager.sendText(message, ide, repoPath);
54
55
 
55
56
  spinner.succeed(chalk.green(`Sent message to ${ide} AI chat`));
56
57
  } catch (error) {
@@ -102,7 +102,7 @@ async function getReqPathOrExit() {
102
102
  async function list(options) {
103
103
  try {
104
104
  const { repoPath, reqPath } = await getReqPathOrExit();
105
-
105
+
106
106
  // Handle --all-computers flag
107
107
  if (options && options.allComputers) {
108
108
  const vibeDir = path.join(repoPath, '.vibecodingmachine');
@@ -110,43 +110,43 @@ async function list(options) {
110
110
  console.log(chalk.yellow('No .vibecodingmachine directory found.'));
111
111
  return;
112
112
  }
113
-
113
+
114
114
  // Find all REQUIREMENTS-*.md files
115
115
  const files = await fs.readdir(vibeDir);
116
116
  const reqFiles = files.filter(f => f.startsWith('REQUIREMENTS') && f.endsWith('.md'));
117
-
117
+
118
118
  if (reqFiles.length === 0) {
119
119
  console.log(chalk.yellow('No requirements files found.'));
120
120
  return;
121
121
  }
122
-
122
+
123
123
  // Extract computer info and requirements from each file
124
124
  for (const file of reqFiles) {
125
125
  const filePath = path.join(vibeDir, file);
126
126
  const content = await fs.readFile(filePath, 'utf8');
127
127
  const lines = content.split('\n');
128
-
128
+
129
129
  // Extract hostname from file
130
130
  let hostname = 'Unknown';
131
131
  const hostnameMatch = content.match(/- \*\*Hostname\*\*:\s*(.+)/);
132
132
  if (hostnameMatch) {
133
133
  hostname = hostnameMatch[1].trim();
134
134
  }
135
-
135
+
136
136
  // Extract focus area
137
137
  let focus = '';
138
138
  const focusMatch = content.match(/## šŸŽÆ Focus\n\*\*(.+)\*\*/);
139
139
  if (focusMatch) {
140
140
  focus = focusMatch[1].trim();
141
141
  }
142
-
142
+
143
143
  // Filter by focus if specified
144
144
  if (options.focus && focus && !focus.toLowerCase().includes(options.focus.toLowerCase())) {
145
145
  continue;
146
146
  }
147
-
147
+
148
148
  console.log(chalk.blue.bold(`\nšŸ“ ${hostname}`) + (focus ? chalk.gray(` (${focus})`) : ''));
149
-
149
+
150
150
  // List requirements from this file
151
151
  for (const line of lines) {
152
152
  if (line.startsWith('### ') || line.startsWith('## ')) {
@@ -159,43 +159,43 @@ async function list(options) {
159
159
  }
160
160
  return;
161
161
  }
162
-
162
+
163
163
  // Single computer mode (default or with --computer filter)
164
164
  let targetReqPath = reqPath;
165
-
165
+
166
166
  // If computer filter specified, find that computer's requirements file
167
167
  if (options && options.computer) {
168
168
  const vibeDir = path.join(repoPath, '.vibecodingmachine');
169
169
  const files = await fs.readdir(vibeDir);
170
- const computerFile = files.find(f =>
171
- f.startsWith('REQUIREMENTS') &&
172
- f.endsWith('.md') &&
170
+ const computerFile = files.find(f =>
171
+ f.startsWith('REQUIREMENTS') &&
172
+ f.endsWith('.md') &&
173
173
  f.toLowerCase().includes(options.computer.toLowerCase())
174
174
  );
175
-
175
+
176
176
  if (!computerFile) {
177
177
  console.log(chalk.yellow(`No requirements file found for computer: ${options.computer}`));
178
178
  return;
179
179
  }
180
-
180
+
181
181
  targetReqPath = path.join(vibeDir, computerFile);
182
182
  }
183
-
183
+
184
184
  if (!await fs.pathExists(targetReqPath)) {
185
185
  console.log(chalk.yellow('No REQUIREMENTS.md found.'));
186
186
  return;
187
187
  }
188
-
188
+
189
189
  const content = await fs.readFile(targetReqPath, 'utf8');
190
190
  const lines = content.split('\n');
191
-
191
+
192
192
  // Extract focus area for focus filtering
193
193
  let fileFocus = '';
194
194
  const focusMatch = content.match(/## šŸŽÆ Focus\n\*\*(.+)\*\*/);
195
195
  if (focusMatch) {
196
196
  fileFocus = focusMatch[1].trim();
197
197
  }
198
-
198
+
199
199
  // Check focus filter
200
200
  if (options && options.focus) {
201
201
  if (!fileFocus || !fileFocus.toLowerCase().includes(options.focus.toLowerCase())) {
@@ -203,7 +203,7 @@ async function list(options) {
203
203
  return;
204
204
  }
205
205
  }
206
-
206
+
207
207
  const statusFilter = options && options.status ? String(options.status).toLowerCase() : null;
208
208
  for (const line of lines) {
209
209
  if (line.startsWith('### ') || line.startsWith('## ')) {
@@ -224,10 +224,6 @@ async function add(name, pkg, description) {
224
224
  await fs.ensureFile(reqPath);
225
225
  let content = await fs.readFile(reqPath, 'utf8').catch(() => '');
226
226
 
227
- // Get next requirement number
228
- const { getNextRequirementNumber } = require('vibecodingmachine-core');
229
- const reqNumber = await getNextRequirementNumber(repoPath);
230
-
231
227
  // Find the TODO section
232
228
  const todoSectionHeader = '## ā³ Requirements not yet completed';
233
229
  if (!content.includes('Requirements not yet completed')) {
@@ -244,8 +240,8 @@ async function add(name, pkg, description) {
244
240
 
245
241
  // Insert right after the TODO section header
246
242
  if (!inserted && lines[i].startsWith('##') && lines[i].includes('Requirements not yet completed')) {
247
- // Add requirement header with number
248
- newLines.push(`### R${reqNumber}: ${name}`);
243
+ // Add requirement header without number prefix (for backward compatibility)
244
+ newLines.push(`### ${name}`);
249
245
 
250
246
  // Add package if provided (and not 'all')
251
247
  if (pkg && Array.isArray(pkg) && pkg.length > 0) {
@@ -27,7 +27,10 @@ async function startAutoMode(repoPath, config) {
27
27
  running: true,
28
28
  startedAt: new Date().toISOString(),
29
29
  chatCount: 0,
30
- ide: config.ide || 'cline'
30
+ ide: config.ide || 'cline',
31
+ skipDisabled: config.skipDisabled || false,
32
+ maxChats: config.maxChats || 0,
33
+ neverStop: config.neverStop || false
31
34
  };
32
35
  await fs.writeJson(statusPath, status, { spaces: 2 });
33
36
 
@@ -0,0 +1,218 @@
1
+ const chalk = require('chalk');
2
+ const {
3
+ getProviderPreferences,
4
+ saveProviderPreferences
5
+ } = require('./provider-registry');
6
+ const { checkClineInstallation, installClineExtension } = require('vibecodingmachine-core');
7
+
8
+ /**
9
+ * Check if Cline agent has hit a rate limit.
10
+ * @param {string} stderr - Standard error output from the agent.
11
+ * @returns {{isRateLimited: boolean, message: string|null}} - Rate limit status and message.
12
+ */
13
+ function checkClineRateLimit(stderr) {
14
+ const rateLimitPatterns = [
15
+ /quota limit/i,
16
+ /rate limit/i,
17
+ /too many requests/i,
18
+ /limit exceeded/i,
19
+ /usage limit/i,
20
+ /api limit/i,
21
+ /request limit/i,
22
+ /spending cap/i,
23
+ /usage cap/i,
24
+ /quota exceeded/i,
25
+ /limit exceeded/i,
26
+ /usage exceeded/i,
27
+ /rate limited/i,
28
+ /api limited/i,
29
+ /request limited/i,
30
+ /quota reached/i,
31
+ /limit reached/i,
32
+ /usage reached/i,
33
+ /cap reached/i,
34
+ /cap exceeded/i,
35
+ /daily limit/i,
36
+ /monthly limit/i,
37
+ /hourly limit/i,
38
+ /token limit/i,
39
+ /credit limit/i,
40
+ /billing limit/i,
41
+ /subscription limit/i,
42
+ /plan limit/i,
43
+ /tier limit/i,
44
+ /upgrade required/i,
45
+ /upgrade needed/i,
46
+ /upgrade to pro/i,
47
+ /upgrade to premium/i,
48
+ /upgrade plan/i,
49
+ /increase limit/i,
50
+ /exhausted/i,
51
+ /no more requests/i,
52
+ /no more credits/i,
53
+ /insufficient credits/i,
54
+ /insufficient quota/i,
55
+ /maximum reached/i,
56
+ /max requests/i,
57
+ /max usage/i,
58
+ /over limit/i,
59
+ /over quota/i,
60
+ /over usage/i,
61
+ /out of credits/i,
62
+ /out of credit/i,
63
+ /credits exhausted/i,
64
+ /credits depleted/i,
65
+ /credits used up/i,
66
+ /credit exhausted/i,
67
+ /credit depleted/i,
68
+ /credit used up/i,
69
+ /credit balance is too low/i,
70
+ /not enough credits/i,
71
+ /credits: 0/i,
72
+ /credit balance: 0/i,
73
+ /no credits available/i,
74
+ /purchase more credits/i,
75
+ /credits: \d+ remaining/i,
76
+ /credit balance: \d+/i,
77
+ /quota.*exceeded/i,
78
+ /limit.*exceeded/i,
79
+ /quota.*reached/i,
80
+ /limit.*reached/i,
81
+ /cap.*exceeded/i,
82
+ /cap.*reached/i,
83
+ /quota.*exhaust/i,
84
+ /limit.*exhaust/i,
85
+ /quota.*exhausted/i,
86
+ /limit.*exhausted/i,
87
+ /credit.*out of/i,
88
+ /credits.*out of/i,
89
+ /credit.*exhausted/i,
90
+ /credits.*exhausted/i,
91
+ /credit.*depleted/i,
92
+ /credits.*depleted/i,
93
+ /credit.*used up/i,
94
+ /credits.*used up/i,
95
+ /429/,
96
+ /403/,
97
+ /402/,
98
+ /throttl/i,
99
+ /quota.*violat/i,
100
+ /limit.*violat/i,
101
+ /usage.*violat/i,
102
+ /quota.*surpass/i,
103
+ /limit.*surpass/i,
104
+ /usage.*surpass/i,
105
+ /quota.*exceed/i,
106
+ /limit.*exceed/i,
107
+ /usage.*exceed/i,
108
+ /quota.*over/i,
109
+ /limit.*over/i,
110
+ /usage.*over/i,
111
+ /quota.*hit/i,
112
+ /limit.*hit/i,
113
+ /usage.*hit/i,
114
+ /quota.*reach/i,
115
+ /limit.*reach/i,
116
+ /usage.*reach/i,
117
+ /quota.*max/i,
118
+ /limit.*max/i,
119
+ /usage.*max/i,
120
+ /quota.*full/i,
121
+ /limit.*full/i,
122
+ /usage.*full/i,
123
+ /quota.*deplet/i,
124
+ /limit.*deplet/i,
125
+ /usage.*deplet/i,
126
+ /quota.*consum/i,
127
+ /limit.*consum/i,
128
+ /usage.*consum/i,
129
+ /quota.*spent/i,
130
+ /limit.*spent/i,
131
+ /usage.*spent/i,
132
+ /quota.*used/i,
133
+ /limit.*used/i,
134
+ /usage.*used/i,
135
+ /quota.*finish/i,
136
+ /limit.*finish/i,
137
+ /usage.*finish/i,
138
+ /quota.*end/i,
139
+ /limit.*end/i,
140
+ /usage.*end/i,
141
+ /quota.*complete/i,
142
+ /limit.*complete/i,
143
+ /usage.*complete/i,
144
+ /quota.*done/i,
145
+ /limit.*done/i,
146
+ /usage.*done/i
147
+ ];
148
+
149
+ for (const pattern of rateLimitPatterns) {
150
+ if (pattern.test(stderr)) {
151
+ return {
152
+ isRateLimited: true,
153
+ message: 'Cline quota limit reached.'
154
+ };
155
+ }
156
+ }
157
+
158
+ return { isRateLimited: false, message: null };
159
+ }
160
+
161
+ /**
162
+ * Handle rate limit for Cline by disabling it and selecting the next available provider.
163
+ * @returns {Promise<{success: boolean, nextProvider: string|null, error: string|null}>}
164
+ */
165
+ async function handleClineRateLimit() {
166
+ console.log(chalk.yellow('Cline rate limit detected. Switching to next provider...'));
167
+
168
+ try {
169
+ const prefs = await getProviderPreferences();
170
+ prefs.enabled.cline = false;
171
+ await saveProviderPreferences(prefs.order, prefs.enabled);
172
+
173
+ const nextProvider = prefs.order.find(p => p !== 'cline' && prefs.enabled[p]);
174
+ if (nextProvider) {
175
+ console.log(chalk.cyan(`Switching to next available provider: ${nextProvider}`));
176
+ return { success: true, nextProvider, error: null };
177
+ } else {
178
+ return { success: false, nextProvider: null, error: 'No fallback providers available.' };
179
+ }
180
+ } catch (error) {
181
+ return { success: false, nextProvider: null, error: 'Failed to update provider preferences.' };
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Setup Cline extension if not installed
187
+ * @returns {Promise<{success: boolean, error?: string}>}
188
+ */
189
+ async function setupClineExtension() {
190
+ console.log(chalk.blue('šŸ” Checking Cline extension installation...'));
191
+
192
+ const checkResult = await checkClineInstallation();
193
+
194
+ if (checkResult.installed) {
195
+ console.log(chalk.green('āœ… Cline extension is already installed!'));
196
+ return { success: true };
197
+ }
198
+
199
+ console.log(chalk.yellow('āš ļø Cline extension not found.'));
200
+ console.log(chalk.cyan('šŸ“¦ Installing Cline extension...'));
201
+
202
+ const installResult = await installClineExtension();
203
+
204
+ if (installResult.success) {
205
+ console.log(chalk.green('āœ… Cline extension installed successfully!'));
206
+ console.log(chalk.blue('šŸ’” Please restart VS Code to activate the extension.'));
207
+ return { success: true };
208
+ } else {
209
+ console.log(chalk.red(`āŒ Failed to install Cline: ${installResult.error}`));
210
+ return { success: false, error: installResult.error };
211
+ }
212
+ }
213
+
214
+ module.exports = {
215
+ checkClineRateLimit,
216
+ handleClineRateLimit,
217
+ setupClineExtension
218
+ };
@@ -1,6 +1,7 @@
1
1
  const path = require('path');
2
2
  const os = require('os');
3
3
  const fs = require('fs-extra');
4
+ const { execSync } = require('child_process');
4
5
 
5
6
  const DEFAULT_CONFIG_DIR = path.join(os.homedir(), '.config', 'vibecodingmachine');
6
7
  const DEFAULT_CONFIG_PATH = path.join(DEFAULT_CONFIG_DIR, 'config.json');
@@ -34,6 +35,26 @@ async function getRepoPath() {
34
35
  return cfg.repoPath || null;
35
36
  }
36
37
 
38
+ /**
39
+ * Returns the configured repoPath if set, otherwise detects the git root
40
+ * from process.cwd(), otherwise falls back to process.cwd().
41
+ * Use this when you need to CREATE a .vibecodingmachine directory to ensure
42
+ * it is always placed at the repository root, never in a subdirectory.
43
+ */
44
+ async function getEffectiveRepoPath() {
45
+ const configured = await getRepoPath();
46
+ if (configured) return configured;
47
+ try {
48
+ return execSync('git rev-parse --show-toplevel', {
49
+ encoding: 'utf8',
50
+ cwd: process.cwd(),
51
+ stdio: ['pipe', 'pipe', 'pipe'],
52
+ }).trim();
53
+ } catch (_) {
54
+ return process.cwd();
55
+ }
56
+ }
57
+
37
58
  async function setRepoPath(repoPath) {
38
59
  const cfg = await readConfig();
39
60
  cfg.repoPath = repoPath;
@@ -112,6 +133,7 @@ async function setAutoTimeout(timeoutMs) {
112
133
 
113
134
  module.exports = {
114
135
  getRepoPath,
136
+ getEffectiveRepoPath,
115
137
  setRepoPath,
116
138
  getAutoConfig,
117
139
  setAutoConfig,