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.
- package/bin/vibecodingmachine.js +124 -0
- package/package.json +3 -2
- package/src/commands/agents-check.js +69 -0
- package/src/commands/auto-direct.js +930 -145
- package/src/commands/auto.js +26 -4
- package/src/commands/ide.js +2 -1
- package/src/commands/requirements.js +23 -27
- package/src/utils/auto-mode.js +4 -1
- package/src/utils/cline-js-handler.js +218 -0
- package/src/utils/config.js +22 -0
- package/src/utils/display-formatters-complete.js +229 -0
- package/src/utils/display-formatters-extracted.js +219 -0
- package/src/utils/display-formatters.js +157 -0
- package/src/utils/feedback-handler.js +143 -0
- package/src/utils/ide-detection-complete.js +126 -0
- package/src/utils/ide-detection-extracted.js +116 -0
- package/src/utils/ide-detection.js +124 -0
- package/src/utils/interactive-backup.js +5664 -0
- package/src/utils/interactive-broken.js +280 -0
- package/src/utils/interactive.js +31 -5534
- package/src/utils/provider-checker.js +410 -0
- package/src/utils/provider-manager.js +251 -0
- package/src/utils/provider-registry.js +18 -9
- package/src/utils/requirement-actions.js +884 -0
- package/src/utils/requirements-navigator.js +585 -0
- package/src/utils/rui-trui-adapter.js +311 -0
- package/src/utils/simple-trui.js +204 -0
- package/src/utils/status-helpers-extracted.js +125 -0
- package/src/utils/status-helpers.js +107 -0
- package/src/utils/trui-debug.js +261 -0
- package/src/utils/trui-feedback.js +133 -0
- package/src/utils/trui-nav-agents.js +119 -0
- package/src/utils/trui-nav-requirements.js +268 -0
- package/src/utils/trui-nav-settings.js +157 -0
- package/src/utils/trui-nav-specifications.js +139 -0
- package/src/utils/trui-navigation.js +303 -0
- package/src/utils/trui-provider-manager.js +182 -0
- package/src/utils/trui-quick-menu.js +365 -0
- package/src/utils/trui-req-actions.js +372 -0
- package/src/utils/trui-req-tree.js +534 -0
- package/src/utils/trui-specifications.js +359 -0
- package/src/utils/trui-text-editor.js +350 -0
- package/src/utils/trui-windsurf.js +336 -0
- package/src/utils/welcome-screen-extracted.js +135 -0
- package/src/utils/welcome-screen.js +134 -0
package/src/commands/auto.js
CHANGED
|
@@ -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
|
-
//
|
|
757
|
-
|
|
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
|
|
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
|
-
|
|
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}`);
|
package/src/commands/ide.js
CHANGED
|
@@ -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
|
|
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
|
|
248
|
-
newLines.push(`###
|
|
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) {
|
package/src/utils/auto-mode.js
CHANGED
|
@@ -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
|
+
};
|
package/src/utils/config.js
CHANGED
|
@@ -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,
|