@rama_nigg/open-cursor 2.1.6 → 2.2.0
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/README.md +40 -61
- package/dist/cli/opencode-cursor.js +262 -20
- package/dist/index.js +669 -213
- package/dist/plugin-entry.js +642 -209
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,20 +15,18 @@ No prompt limits. No broken streams. Full thinking + tool support in Opencode. Y
|
|
|
15
15
|
curl -fsSL https://raw.githubusercontent.com/Nomadcxx/opencode-cursor/main/install.sh | bash
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Note: if `npm` is available, `install.sh` installs/upgrades `@rama_nigg/open-cursor` and configures OpenCode automatically. Otherwise it falls back to building from source.
|
|
19
|
-
|
|
20
18
|
**Option B: npm Package (Recommended)**
|
|
21
19
|
|
|
22
20
|
```bash
|
|
23
21
|
npm install -g @rama_nigg/open-cursor
|
|
24
|
-
|
|
22
|
+
open-cursor install
|
|
25
23
|
```
|
|
26
24
|
|
|
27
25
|
Upgrade later with:
|
|
28
26
|
|
|
29
27
|
```bash
|
|
30
28
|
npm update -g @rama_nigg/open-cursor
|
|
31
|
-
|
|
29
|
+
open-cursor install
|
|
32
30
|
```
|
|
33
31
|
|
|
34
32
|
**Option C: TUI Installer**
|
|
@@ -48,7 +46,7 @@ Install the cursor-acp plugin for OpenCode:
|
|
|
48
46
|
|
|
49
47
|
1. Preferred install (npm):
|
|
50
48
|
npm install -g @rama_nigg/open-cursor
|
|
51
|
-
|
|
49
|
+
open-cursor install
|
|
52
50
|
|
|
53
51
|
2. Fallback (build from source):
|
|
54
52
|
git clone https://github.com/Nomadcxx/opencode-cursor.git
|
|
@@ -89,7 +87,39 @@ Or if you'd rather do it by hand, add this to `~/.config/opencode/opencode.json`
|
|
|
89
87
|
"npm": "@ai-sdk/openai-compatible",
|
|
90
88
|
"options": { "baseURL": "http://127.0.0.1:32124/v1" },
|
|
91
89
|
"models": {
|
|
92
|
-
"auto": { "name": "Auto" }
|
|
90
|
+
"auto": { "name": "Auto" },
|
|
91
|
+
"composer-1.5": { "name": "Composer 1.5" },
|
|
92
|
+
"composer-1": { "name": "Composer 1" },
|
|
93
|
+
"gpt-5.3-codex": { "name": "GPT-5.3 Codex" },
|
|
94
|
+
"gpt-5.3-codex-low": { "name": "GPT-5.3 Codex Low" },
|
|
95
|
+
"gpt-5.3-codex-high": { "name": "GPT-5.3 Codex High" },
|
|
96
|
+
"gpt-5.3-codex-xhigh": { "name": "GPT-5.3 Codex Extra High" },
|
|
97
|
+
"gpt-5.3-codex-fast": { "name": "GPT-5.3 Codex Fast" },
|
|
98
|
+
"gpt-5.3-codex-low-fast": { "name": "GPT-5.3 Codex Low Fast" },
|
|
99
|
+
"gpt-5.3-codex-high-fast": { "name": "GPT-5.3 Codex High Fast" },
|
|
100
|
+
"gpt-5.3-codex-xhigh-fast": { "name": "GPT-5.3 Codex Extra High Fast" },
|
|
101
|
+
"gpt-5.2": { "name": "GPT-5.2" },
|
|
102
|
+
"gpt-5.2-codex": { "name": "GPT-5.2 Codex" },
|
|
103
|
+
"gpt-5.2-codex-high": { "name": "GPT-5.2 Codex High" },
|
|
104
|
+
"gpt-5.2-codex-low": { "name": "GPT-5.2 Codex Low" },
|
|
105
|
+
"gpt-5.2-codex-xhigh": { "name": "GPT-5.2 Codex Extra High" },
|
|
106
|
+
"gpt-5.2-codex-fast": { "name": "GPT-5.2 Codex Fast" },
|
|
107
|
+
"gpt-5.2-codex-high-fast": { "name": "GPT-5.2 Codex High Fast" },
|
|
108
|
+
"gpt-5.2-codex-low-fast": { "name": "GPT-5.2 Codex Low Fast" },
|
|
109
|
+
"gpt-5.2-codex-xhigh-fast": { "name": "GPT-5.2 Codex Extra High Fast" },
|
|
110
|
+
"gpt-5.1-codex-max": { "name": "GPT-5.1 Codex Max" },
|
|
111
|
+
"gpt-5.1-codex-max-high": { "name": "GPT-5.1 Codex Max High" },
|
|
112
|
+
"opus-4.6-thinking": { "name": "Claude 4.6 Opus (Thinking)" },
|
|
113
|
+
"sonnet-4.5-thinking": { "name": "Claude 4.5 Sonnet (Thinking)" },
|
|
114
|
+
"gpt-5.2-high": { "name": "GPT-5.2 High" },
|
|
115
|
+
"opus-4.6": { "name": "Claude 4.6 Opus" },
|
|
116
|
+
"opus-4.5": { "name": "Claude 4.5 Opus" },
|
|
117
|
+
"opus-4.5-thinking": { "name": "Claude 4.5 Opus (Thinking)" },
|
|
118
|
+
"sonnet-4.5": { "name": "Claude 4.5 Sonnet" },
|
|
119
|
+
"gpt-5.1-high": { "name": "GPT-5.1 High" },
|
|
120
|
+
"gemini-3-pro": { "name": "Gemini 3 Pro" },
|
|
121
|
+
"gemini-3-flash": { "name": "Gemini 3 Flash" },
|
|
122
|
+
"grok": { "name": "Grok" }
|
|
93
123
|
}
|
|
94
124
|
}
|
|
95
125
|
}
|
|
@@ -131,28 +161,6 @@ opencode run "your prompt" --model cursor-acp/auto
|
|
|
131
161
|
opencode run "your prompt" --model cursor-acp/sonnet-4.5
|
|
132
162
|
```
|
|
133
163
|
|
|
134
|
-
## Models
|
|
135
|
-
|
|
136
|
-
Models are pulled from `cursor-agent models` and written to your config during installation. If Cursor adds new models later:
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
npm exec -- @rama_nigg/open-cursor install
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Or, if you installed from source (no npm CLI):
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
./scripts/sync-models.sh
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
The proxy also exposes a `/v1/models` endpoint that fetches models in real-time:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
curl http://127.0.0.1:32124/v1/models
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Common models: `auto`, `composer-1.5`, `gpt-5.3-codex`, `opus-4.6-thinking`, `sonnet-4.5`, `gemini-3-pro`, `grok`
|
|
155
|
-
|
|
156
164
|
## Architecture
|
|
157
165
|
|
|
158
166
|
```mermaid
|
|
@@ -196,15 +204,6 @@ Detailed architecture: [docs/architecture/runtime-tool-loop.md](docs/architectur
|
|
|
196
204
|
| **Dependencies** | bun, cursor-agent | npm | bun, cursor-agent | Node.js 18+ |
|
|
197
205
|
| **Port** | 32124 | 18741 | 32123 | 4141 |
|
|
198
206
|
|
|
199
|
-
**Key advantages of cursor-acp:**
|
|
200
|
-
|
|
201
|
-
- Avoids E2BIG errors with large prompts (uses HTTP body, not CLI args)
|
|
202
|
-
- Structured error parsing with actionable suggestions
|
|
203
|
-
- Cross-platform (not locked to macOS Keychain)
|
|
204
|
-
- TUI installer for easy setup
|
|
205
|
-
- Native tool calling with 10 built-in tools, SDK/MCP executor support, and a skills/alias system
|
|
206
|
-
- Uses official cursor-agent CLI (more stable than reverse-engineering Connect-RPC)
|
|
207
|
-
|
|
208
207
|
## Prerequisites
|
|
209
208
|
|
|
210
209
|
- [Bun](https://bun.sh/)
|
|
@@ -247,10 +246,6 @@ Integration CI pins OpenCode-owned loop mode to deterministic settings:
|
|
|
247
246
|
- `CURSOR_ACP_FORWARD_TOOL_CALLS=false`
|
|
248
247
|
- `CURSOR_ACP_EMIT_TOOL_UPDATES=false`
|
|
249
248
|
|
|
250
|
-
## Publishing
|
|
251
|
-
|
|
252
|
-
For maintainers, release and npm publish steps are documented in [docs/PUBLISHING.md](docs/PUBLISHING.md).
|
|
253
|
-
|
|
254
249
|
## Troubleshooting
|
|
255
250
|
|
|
256
251
|
**"fetch() URL is invalid"** - Run `opencode auth login` without arguments
|
|
@@ -271,26 +266,10 @@ Common causes:
|
|
|
271
266
|
|
|
272
267
|
### Debug Logging
|
|
273
268
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
- `CURSOR_ACP_LOG_LEVEL=error` - Errors only
|
|
279
|
-
|
|
280
|
-
Provider-boundary rollout:
|
|
281
|
-
- Default: `CURSOR_ACP_PROVIDER_BOUNDARY=v1`
|
|
282
|
-
- Default: `CURSOR_ACP_PROVIDER_BOUNDARY_AUTOFALLBACK=true`
|
|
283
|
-
- `CURSOR_ACP_PROVIDER_BOUNDARY=legacy` - Original provider/runtime boundary behavior
|
|
284
|
-
- `CURSOR_ACP_PROVIDER_BOUNDARY=v1` - Shared boundary/interception path
|
|
285
|
-
- `CURSOR_ACP_PROVIDER_BOUNDARY_AUTOFALLBACK=false` - Disable fallback and keep strict `v1` behavior
|
|
286
|
-
- `CURSOR_ACP_TOOL_LOOP_MAX_REPEAT=3` - Max repeated failing tool-call fingerprints before guard termination (or fallback when enabled)
|
|
287
|
-
|
|
288
|
-
Auto-fallback trigger conditions:
|
|
289
|
-
- Only active when `CURSOR_ACP_PROVIDER_BOUNDARY=v1`
|
|
290
|
-
- Triggered when `v1` boundary extraction throws during tool-call interception
|
|
291
|
-
- Triggered when the tool-loop guard threshold is reached (same tool + arg shape + error class)
|
|
292
|
-
- Does not trigger for normal cases like disallowed tools or no tool call
|
|
293
|
-
- Does not trigger for unrelated runtime errors (for example, tool mapper/tool execution failures)
|
|
269
|
+
Enable verbose logs:
|
|
270
|
+
```bash
|
|
271
|
+
CURSOR_ACP_LOG_LEVEL=debug opencode run "your prompt" --model cursor-acp/auto
|
|
272
|
+
```
|
|
294
273
|
|
|
295
274
|
Disable log output entirely:
|
|
296
275
|
```bash
|
|
@@ -177,18 +177,170 @@ function fallbackModels() {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
// src/cli/opencode-cursor.ts
|
|
180
|
+
var BRANDING_HEADER = `
|
|
181
|
+
▄▄▄ ▄▄▄▄ ▄▄▄▄▄ ▄▄ ▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄▄▄
|
|
182
|
+
██ ██ ██ ██ ██▄▄ ███▄██ ▄▄▄ ██ ▀▀ ██ ██ ██ ██ ██▄▄▄ ██ ██ ██ ██
|
|
183
|
+
▀█▄█▀ ██▀▀ ██▄▄▄ ██ ▀██ ▀█▄█▀ ▀█▄█▀ ██▀█▄ ▄▄▄█▀ ▀█▄█▀ ██▀█▄
|
|
184
|
+
`;
|
|
185
|
+
function getBrandingHeader() {
|
|
186
|
+
return BRANDING_HEADER.trim();
|
|
187
|
+
}
|
|
188
|
+
function checkBun() {
|
|
189
|
+
try {
|
|
190
|
+
const version = execFileSync2("bun", ["--version"], { encoding: "utf8" }).trim();
|
|
191
|
+
return { name: "bun", passed: true, message: `v${version}` };
|
|
192
|
+
} catch {
|
|
193
|
+
return {
|
|
194
|
+
name: "bun",
|
|
195
|
+
passed: false,
|
|
196
|
+
message: "not found - install with: curl -fsSL https://bun.sh/install | bash"
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function checkCursorAgent() {
|
|
201
|
+
try {
|
|
202
|
+
const output = execFileSync2("cursor-agent", ["--version"], { encoding: "utf8" }).trim();
|
|
203
|
+
const version = output.split(`
|
|
204
|
+
`)[0] || "installed";
|
|
205
|
+
return { name: "cursor-agent", passed: true, message: version };
|
|
206
|
+
} catch {
|
|
207
|
+
return {
|
|
208
|
+
name: "cursor-agent",
|
|
209
|
+
passed: false,
|
|
210
|
+
message: "not found - install with: curl -fsS https://cursor.com/install | bash"
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function checkCursorAgentLogin() {
|
|
215
|
+
try {
|
|
216
|
+
execFileSync2("cursor-agent", ["models"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
217
|
+
return { name: "cursor-agent login", passed: true, message: "logged in" };
|
|
218
|
+
} catch {
|
|
219
|
+
return {
|
|
220
|
+
name: "cursor-agent login",
|
|
221
|
+
passed: false,
|
|
222
|
+
message: "not logged in - run: cursor-agent login",
|
|
223
|
+
warning: true
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function checkOpenCode() {
|
|
228
|
+
try {
|
|
229
|
+
const version = execFileSync2("opencode", ["--version"], { encoding: "utf8" }).trim();
|
|
230
|
+
return { name: "OpenCode", passed: true, message: version };
|
|
231
|
+
} catch {
|
|
232
|
+
return {
|
|
233
|
+
name: "OpenCode",
|
|
234
|
+
passed: false,
|
|
235
|
+
message: "not found - install with: curl -fsSL https://opencode.ai/install | bash"
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function checkPluginFile(pluginPath) {
|
|
240
|
+
try {
|
|
241
|
+
if (!existsSync(pluginPath)) {
|
|
242
|
+
return {
|
|
243
|
+
name: "Plugin file",
|
|
244
|
+
passed: false,
|
|
245
|
+
message: "not found - run: open-cursor install"
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const stat = lstatSync(pluginPath);
|
|
249
|
+
if (stat.isSymbolicLink()) {
|
|
250
|
+
const target = readFileSync(pluginPath, "utf8");
|
|
251
|
+
return { name: "Plugin file", passed: true, message: `symlink → ${target}` };
|
|
252
|
+
}
|
|
253
|
+
return { name: "Plugin file", passed: true, message: "file (copy)" };
|
|
254
|
+
} catch {
|
|
255
|
+
return {
|
|
256
|
+
name: "Plugin file",
|
|
257
|
+
passed: false,
|
|
258
|
+
message: "error reading plugin file"
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function checkProviderConfig(configPath) {
|
|
263
|
+
try {
|
|
264
|
+
if (!existsSync(configPath)) {
|
|
265
|
+
return {
|
|
266
|
+
name: "Provider config",
|
|
267
|
+
passed: false,
|
|
268
|
+
message: "config not found - run: open-cursor install"
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const config = readConfig(configPath);
|
|
272
|
+
const provider = config.provider?.["cursor-acp"];
|
|
273
|
+
if (!provider) {
|
|
274
|
+
return {
|
|
275
|
+
name: "Provider config",
|
|
276
|
+
passed: false,
|
|
277
|
+
message: "cursor-acp provider missing - run: open-cursor install"
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const modelCount = Object.keys(provider.models || {}).length;
|
|
281
|
+
return { name: "Provider config", passed: true, message: `${modelCount} models` };
|
|
282
|
+
} catch {
|
|
283
|
+
return {
|
|
284
|
+
name: "Provider config",
|
|
285
|
+
passed: false,
|
|
286
|
+
message: "error reading config"
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function checkAiSdk(opencodeDir) {
|
|
291
|
+
try {
|
|
292
|
+
const sdkPath = join(opencodeDir, "node_modules", "@ai-sdk", "openai-compatible");
|
|
293
|
+
if (existsSync(sdkPath)) {
|
|
294
|
+
return { name: "AI SDK", passed: true, message: "@ai-sdk/openai-compatible installed" };
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
name: "AI SDK",
|
|
298
|
+
passed: false,
|
|
299
|
+
message: "not installed - run: open-cursor install"
|
|
300
|
+
};
|
|
301
|
+
} catch {
|
|
302
|
+
return {
|
|
303
|
+
name: "AI SDK",
|
|
304
|
+
passed: false,
|
|
305
|
+
message: "error checking AI SDK"
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function runDoctorChecks(configPath, pluginPath) {
|
|
310
|
+
const opencodeDir = dirname(configPath);
|
|
311
|
+
return [
|
|
312
|
+
checkBun(),
|
|
313
|
+
checkCursorAgent(),
|
|
314
|
+
checkCursorAgentLogin(),
|
|
315
|
+
checkOpenCode(),
|
|
316
|
+
checkPluginFile(pluginPath),
|
|
317
|
+
checkProviderConfig(configPath),
|
|
318
|
+
checkAiSdk(opencodeDir)
|
|
319
|
+
];
|
|
320
|
+
}
|
|
180
321
|
var PROVIDER_ID = "cursor-acp";
|
|
181
322
|
var DEFAULT_BASE_URL = "http://127.0.0.1:32124/v1";
|
|
182
323
|
function printHelp() {
|
|
183
324
|
const binName = basename(process.argv[1] || "open-cursor");
|
|
325
|
+
console.log(getBrandingHeader());
|
|
184
326
|
console.log(`${binName}
|
|
185
327
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
328
|
+
Commands:
|
|
329
|
+
install Configure OpenCode for Cursor (idempotent, safe to re-run)
|
|
330
|
+
sync-models Refresh model list from cursor-agent
|
|
331
|
+
status Show current configuration state
|
|
332
|
+
doctor Diagnose common issues
|
|
333
|
+
uninstall Remove cursor-acp from OpenCode config
|
|
334
|
+
help Show this help message
|
|
335
|
+
|
|
336
|
+
Options:
|
|
337
|
+
--config <path> Path to opencode.json (default: ~/.config/opencode/opencode.json)
|
|
338
|
+
--plugin-dir <path> Path to plugin directory (default: ~/.config/opencode/plugin)
|
|
339
|
+
--base-url <url> Proxy base URL (default: http://127.0.0.1:32124/v1)
|
|
340
|
+
--copy Copy plugin instead of symlink
|
|
341
|
+
--skip-models Skip model sync during install
|
|
342
|
+
--no-backup Don't create config backup
|
|
343
|
+
--json Output in JSON format (status command only)
|
|
192
344
|
`);
|
|
193
345
|
}
|
|
194
346
|
function parseArgs(argv) {
|
|
@@ -212,6 +364,8 @@ function parseArgs(argv) {
|
|
|
212
364
|
} else if (arg === "--base-url" && rest[i + 1]) {
|
|
213
365
|
options.baseUrl = rest[i + 1];
|
|
214
366
|
i += 1;
|
|
367
|
+
} else if (arg === "--json") {
|
|
368
|
+
options.json = true;
|
|
215
369
|
} else {
|
|
216
370
|
throw new Error(`Unknown argument: ${arg}`);
|
|
217
371
|
}
|
|
@@ -219,13 +373,14 @@ function parseArgs(argv) {
|
|
|
219
373
|
return { command, options };
|
|
220
374
|
}
|
|
221
375
|
function normalizeCommand(value) {
|
|
222
|
-
switch ((value || "
|
|
376
|
+
switch ((value || "help").toLowerCase()) {
|
|
223
377
|
case "install":
|
|
224
378
|
case "sync-models":
|
|
225
379
|
case "uninstall":
|
|
226
380
|
case "status":
|
|
381
|
+
case "doctor":
|
|
227
382
|
case "help":
|
|
228
|
-
return value ? value.toLowerCase() : "
|
|
383
|
+
return value ? value.toLowerCase() : "help";
|
|
229
384
|
default:
|
|
230
385
|
throw new Error(`Unknown command: ${value}`);
|
|
231
386
|
}
|
|
@@ -377,21 +532,97 @@ function commandUninstall(options) {
|
|
|
377
532
|
console.log(`Removed plugin link: ${pluginPath}`);
|
|
378
533
|
console.log(`Removed provider "${PROVIDER_ID}" from ${configPath}`);
|
|
379
534
|
}
|
|
380
|
-
function
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
535
|
+
function getStatusResult(configPath, pluginPath) {
|
|
536
|
+
let pluginType = "missing";
|
|
537
|
+
let pluginTarget;
|
|
538
|
+
if (existsSync(pluginPath)) {
|
|
539
|
+
const stat = lstatSync(pluginPath);
|
|
540
|
+
pluginType = stat.isSymbolicLink() ? "symlink" : "file";
|
|
541
|
+
if (pluginType === "symlink") {
|
|
542
|
+
try {
|
|
543
|
+
pluginTarget = readFileSync(pluginPath, "utf8");
|
|
544
|
+
} catch {
|
|
545
|
+
pluginTarget = undefined;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
let providerEnabled = false;
|
|
550
|
+
let baseUrl = "http://127.0.0.1:32124/v1";
|
|
551
|
+
let modelCount = 0;
|
|
386
552
|
if (existsSync(configPath)) {
|
|
387
553
|
const config = readConfig(configPath);
|
|
388
|
-
|
|
389
|
-
|
|
554
|
+
const provider = config.provider?.["cursor-acp"];
|
|
555
|
+
providerEnabled = !!provider;
|
|
556
|
+
if (provider?.options?.baseURL) {
|
|
557
|
+
baseUrl = provider.options.baseURL;
|
|
558
|
+
}
|
|
559
|
+
modelCount = Object.keys(provider?.models || {}).length;
|
|
560
|
+
}
|
|
561
|
+
const opencodeDir = dirname(configPath);
|
|
562
|
+
const sdkPath = join(opencodeDir, "node_modules", "@ai-sdk", "openai-compatible");
|
|
563
|
+
const aiSdkInstalled = existsSync(sdkPath);
|
|
564
|
+
return {
|
|
565
|
+
plugin: {
|
|
566
|
+
path: pluginPath,
|
|
567
|
+
type: pluginType,
|
|
568
|
+
target: pluginTarget
|
|
569
|
+
},
|
|
570
|
+
provider: {
|
|
571
|
+
configPath,
|
|
572
|
+
name: "cursor-acp",
|
|
573
|
+
enabled: providerEnabled,
|
|
574
|
+
baseUrl,
|
|
575
|
+
modelCount
|
|
576
|
+
},
|
|
577
|
+
aiSdk: {
|
|
578
|
+
installed: aiSdkInstalled
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function commandStatus(options) {
|
|
583
|
+
const { configPath, pluginPath } = resolvePaths(options);
|
|
584
|
+
const result = getStatusResult(configPath, pluginPath);
|
|
585
|
+
if (options.json) {
|
|
586
|
+
console.log(JSON.stringify(result, null, 2));
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
console.log("");
|
|
590
|
+
console.log("Plugin");
|
|
591
|
+
console.log(` Path: ${result.plugin.path}`);
|
|
592
|
+
if (result.plugin.type === "symlink" && result.plugin.target) {
|
|
593
|
+
console.log(` Type: symlink → ${result.plugin.target}`);
|
|
594
|
+
} else if (result.plugin.type === "file") {
|
|
595
|
+
console.log(` Type: file (copy)`);
|
|
596
|
+
} else {
|
|
597
|
+
console.log(` Type: missing`);
|
|
598
|
+
}
|
|
599
|
+
console.log("");
|
|
600
|
+
console.log("Provider");
|
|
601
|
+
console.log(` Config: ${result.provider.configPath}`);
|
|
602
|
+
console.log(` Name: ${result.provider.name}`);
|
|
603
|
+
console.log(` Enabled: ${result.provider.enabled ? "yes" : "no"}`);
|
|
604
|
+
console.log(` Base URL: ${result.provider.baseUrl}`);
|
|
605
|
+
console.log(` Models: ${result.provider.modelCount}`);
|
|
606
|
+
console.log("");
|
|
607
|
+
console.log("AI SDK");
|
|
608
|
+
console.log(` @ai-sdk/openai-compatible: ${result.aiSdk.installed ? "installed" : "not installed"}`);
|
|
609
|
+
}
|
|
610
|
+
function commandDoctor(options) {
|
|
611
|
+
const { configPath, pluginPath } = resolvePaths(options);
|
|
612
|
+
const checks = runDoctorChecks(configPath, pluginPath);
|
|
613
|
+
console.log("");
|
|
614
|
+
for (const check of checks) {
|
|
615
|
+
const symbol = check.passed ? "✓" : check.warning ? "⚠" : "✗";
|
|
616
|
+
const color = check.passed ? "\x1B[32m" : check.warning ? "\x1B[33m" : "\x1B[31m";
|
|
617
|
+
console.log(` ${color}${symbol}\x1B[0m ${check.name}: ${check.message}`);
|
|
618
|
+
}
|
|
619
|
+
const failed = checks.filter((c) => !c.passed && !c.warning);
|
|
620
|
+
console.log("");
|
|
621
|
+
if (failed.length === 0) {
|
|
622
|
+
console.log("All checks passed!");
|
|
623
|
+
} else {
|
|
624
|
+
console.log(`${failed.length} check(s) failed. See messages above.`);
|
|
390
625
|
}
|
|
391
|
-
console.log(`Plugin file: ${pluginPath} (${pluginType})`);
|
|
392
|
-
console.log(`Provider in config: ${providerExists ? "yes" : "no"}`);
|
|
393
|
-
console.log(`Plugin enabled in config: ${pluginEnabled ? "yes" : "no"}`);
|
|
394
|
-
console.log(`Config path: ${configPath}`);
|
|
395
626
|
}
|
|
396
627
|
function main() {
|
|
397
628
|
let parsed;
|
|
@@ -418,6 +649,9 @@ function main() {
|
|
|
418
649
|
case "status":
|
|
419
650
|
commandStatus(parsed.options);
|
|
420
651
|
return;
|
|
652
|
+
case "doctor":
|
|
653
|
+
commandDoctor(parsed.options);
|
|
654
|
+
return;
|
|
421
655
|
case "help":
|
|
422
656
|
printHelp();
|
|
423
657
|
return;
|
|
@@ -429,3 +663,11 @@ function main() {
|
|
|
429
663
|
}
|
|
430
664
|
}
|
|
431
665
|
main();
|
|
666
|
+
export {
|
|
667
|
+
runDoctorChecks,
|
|
668
|
+
getStatusResult,
|
|
669
|
+
getBrandingHeader,
|
|
670
|
+
checkCursorAgentLogin,
|
|
671
|
+
checkCursorAgent,
|
|
672
|
+
checkBun
|
|
673
|
+
};
|