@rama_nigg/open-cursor 2.1.6 → 2.1.7

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 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
- npm exec -- @rama_nigg/open-cursor install
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
- npm exec -- @rama_nigg/open-cursor install
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
- npm exec -- @rama_nigg/open-cursor install
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
- Set the log level via environment variable:
275
- - `CURSOR_ACP_LOG_LEVEL=debug` - Verbose output for troubleshooting
276
- - `CURSOR_ACP_LOG_LEVEL=info` - Default level
277
- - `CURSOR_ACP_LOG_LEVEL=warn` - Warnings and errors only
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
@@ -1,21 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- var __create = Object.create;
4
- var __getProtoOf = Object.getPrototypeOf;
5
3
  var __defProp = Object.defineProperty;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __toESM = (mod, isNodeMode, target) => {
9
- target = mod != null ? __create(__getProtoOf(mod)) : {};
10
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
- for (let key of __getOwnPropNames(mod))
12
- if (!__hasOwnProp.call(to, key))
13
- __defProp(to, key, {
14
- get: () => mod[key],
15
- enumerable: true
16
- });
17
- return to;
18
- };
19
4
  var __export = (target, all) => {
20
5
  for (var name in all)
21
6
  __defProp(target, name, {
@@ -1,21 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- var __create = Object.create;
4
- var __getProtoOf = Object.getPrototypeOf;
5
3
  var __defProp = Object.defineProperty;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __toESM = (mod, isNodeMode, target) => {
9
- target = mod != null ? __create(__getProtoOf(mod)) : {};
10
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
- for (let key of __getOwnPropNames(mod))
12
- if (!__hasOwnProp.call(to, key))
13
- __defProp(to, key, {
14
- get: () => mod[key],
15
- enumerable: true
16
- });
17
- return to;
18
- };
19
4
  var __export = (target, all) => {
20
5
  for (var name in all)
21
6
  __defProp(target, name, {
@@ -177,18 +162,170 @@ function fallbackModels() {
177
162
  }
178
163
 
179
164
  // src/cli/opencode-cursor.ts
165
+ var BRANDING_HEADER = `
166
+ ▄▄▄ ▄▄▄▄ ▄▄▄▄▄ ▄▄ ▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄▄▄
167
+ ██ ██ ██ ██ ██▄▄ ███▄██ ▄▄▄ ██ ▀▀ ██ ██ ██ ██ ██▄▄▄ ██ ██ ██ ██
168
+ ▀█▄█▀ ██▀▀ ██▄▄▄ ██ ▀██ ▀█▄█▀ ▀█▄█▀ ██▀█▄ ▄▄▄█▀ ▀█▄█▀ ██▀█▄
169
+ `;
170
+ function getBrandingHeader() {
171
+ return BRANDING_HEADER.trim();
172
+ }
173
+ function checkBun() {
174
+ try {
175
+ const version = execFileSync2("bun", ["--version"], { encoding: "utf8" }).trim();
176
+ return { name: "bun", passed: true, message: `v${version}` };
177
+ } catch {
178
+ return {
179
+ name: "bun",
180
+ passed: false,
181
+ message: "not found - install with: curl -fsSL https://bun.sh/install | bash"
182
+ };
183
+ }
184
+ }
185
+ function checkCursorAgent() {
186
+ try {
187
+ const output = execFileSync2("cursor-agent", ["--version"], { encoding: "utf8" }).trim();
188
+ const version = output.split(`
189
+ `)[0] || "installed";
190
+ return { name: "cursor-agent", passed: true, message: version };
191
+ } catch {
192
+ return {
193
+ name: "cursor-agent",
194
+ passed: false,
195
+ message: "not found - install with: curl -fsS https://cursor.com/install | bash"
196
+ };
197
+ }
198
+ }
199
+ function checkCursorAgentLogin() {
200
+ try {
201
+ execFileSync2("cursor-agent", ["models"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
202
+ return { name: "cursor-agent login", passed: true, message: "logged in" };
203
+ } catch {
204
+ return {
205
+ name: "cursor-agent login",
206
+ passed: false,
207
+ message: "not logged in - run: cursor-agent login",
208
+ warning: true
209
+ };
210
+ }
211
+ }
212
+ function checkOpenCode() {
213
+ try {
214
+ const version = execFileSync2("opencode", ["--version"], { encoding: "utf8" }).trim();
215
+ return { name: "OpenCode", passed: true, message: version };
216
+ } catch {
217
+ return {
218
+ name: "OpenCode",
219
+ passed: false,
220
+ message: "not found - install with: curl -fsSL https://opencode.ai/install | bash"
221
+ };
222
+ }
223
+ }
224
+ function checkPluginFile(pluginPath) {
225
+ try {
226
+ if (!existsSync(pluginPath)) {
227
+ return {
228
+ name: "Plugin file",
229
+ passed: false,
230
+ message: "not found - run: open-cursor install"
231
+ };
232
+ }
233
+ const stat = lstatSync(pluginPath);
234
+ if (stat.isSymbolicLink()) {
235
+ const target = readFileSync(pluginPath, "utf8");
236
+ return { name: "Plugin file", passed: true, message: `symlink → ${target}` };
237
+ }
238
+ return { name: "Plugin file", passed: true, message: "file (copy)" };
239
+ } catch {
240
+ return {
241
+ name: "Plugin file",
242
+ passed: false,
243
+ message: "error reading plugin file"
244
+ };
245
+ }
246
+ }
247
+ function checkProviderConfig(configPath) {
248
+ try {
249
+ if (!existsSync(configPath)) {
250
+ return {
251
+ name: "Provider config",
252
+ passed: false,
253
+ message: "config not found - run: open-cursor install"
254
+ };
255
+ }
256
+ const config = readConfig(configPath);
257
+ const provider = config.provider?.["cursor-acp"];
258
+ if (!provider) {
259
+ return {
260
+ name: "Provider config",
261
+ passed: false,
262
+ message: "cursor-acp provider missing - run: open-cursor install"
263
+ };
264
+ }
265
+ const modelCount = Object.keys(provider.models || {}).length;
266
+ return { name: "Provider config", passed: true, message: `${modelCount} models` };
267
+ } catch {
268
+ return {
269
+ name: "Provider config",
270
+ passed: false,
271
+ message: "error reading config"
272
+ };
273
+ }
274
+ }
275
+ function checkAiSdk(opencodeDir) {
276
+ try {
277
+ const sdkPath = join(opencodeDir, "node_modules", "@ai-sdk", "openai-compatible");
278
+ if (existsSync(sdkPath)) {
279
+ return { name: "AI SDK", passed: true, message: "@ai-sdk/openai-compatible installed" };
280
+ }
281
+ return {
282
+ name: "AI SDK",
283
+ passed: false,
284
+ message: "not installed - run: open-cursor install"
285
+ };
286
+ } catch {
287
+ return {
288
+ name: "AI SDK",
289
+ passed: false,
290
+ message: "error checking AI SDK"
291
+ };
292
+ }
293
+ }
294
+ function runDoctorChecks(configPath, pluginPath) {
295
+ const opencodeDir = dirname(configPath);
296
+ return [
297
+ checkBun(),
298
+ checkCursorAgent(),
299
+ checkCursorAgentLogin(),
300
+ checkOpenCode(),
301
+ checkPluginFile(pluginPath),
302
+ checkProviderConfig(configPath),
303
+ checkAiSdk(opencodeDir)
304
+ ];
305
+ }
180
306
  var PROVIDER_ID = "cursor-acp";
181
307
  var DEFAULT_BASE_URL = "http://127.0.0.1:32124/v1";
182
308
  function printHelp() {
183
309
  const binName = basename(process.argv[1] || "open-cursor");
310
+ console.log(getBrandingHeader());
184
311
  console.log(`${binName}
185
312
 
186
- Usage:
187
- ${binName} install [--config <path>] [--plugin-dir <path>] [--base-url <url>] [--copy] [--skip-models] [--no-backup]
188
- ${binName} sync-models [--config <path>] [--no-backup]
189
- ${binName} uninstall [--config <path>] [--plugin-dir <path>] [--no-backup]
190
- ${binName} status [--config <path>] [--plugin-dir <path>]
191
- ${binName} help
313
+ Commands:
314
+ install Configure OpenCode for Cursor (idempotent, safe to re-run)
315
+ sync-models Refresh model list from cursor-agent
316
+ status Show current configuration state
317
+ doctor Diagnose common issues
318
+ uninstall Remove cursor-acp from OpenCode config
319
+ help Show this help message
320
+
321
+ Options:
322
+ --config <path> Path to opencode.json (default: ~/.config/opencode/opencode.json)
323
+ --plugin-dir <path> Path to plugin directory (default: ~/.config/opencode/plugin)
324
+ --base-url <url> Proxy base URL (default: http://127.0.0.1:32124/v1)
325
+ --copy Copy plugin instead of symlink
326
+ --skip-models Skip model sync during install
327
+ --no-backup Don't create config backup
328
+ --json Output in JSON format (status command only)
192
329
  `);
193
330
  }
194
331
  function parseArgs(argv) {
@@ -212,6 +349,8 @@ function parseArgs(argv) {
212
349
  } else if (arg === "--base-url" && rest[i + 1]) {
213
350
  options.baseUrl = rest[i + 1];
214
351
  i += 1;
352
+ } else if (arg === "--json") {
353
+ options.json = true;
215
354
  } else {
216
355
  throw new Error(`Unknown argument: ${arg}`);
217
356
  }
@@ -219,13 +358,14 @@ function parseArgs(argv) {
219
358
  return { command, options };
220
359
  }
221
360
  function normalizeCommand(value) {
222
- switch ((value || "install").toLowerCase()) {
361
+ switch ((value || "help").toLowerCase()) {
223
362
  case "install":
224
363
  case "sync-models":
225
364
  case "uninstall":
226
365
  case "status":
366
+ case "doctor":
227
367
  case "help":
228
- return value ? value.toLowerCase() : "install";
368
+ return value ? value.toLowerCase() : "help";
229
369
  default:
230
370
  throw new Error(`Unknown command: ${value}`);
231
371
  }
@@ -377,21 +517,97 @@ function commandUninstall(options) {
377
517
  console.log(`Removed plugin link: ${pluginPath}`);
378
518
  console.log(`Removed provider "${PROVIDER_ID}" from ${configPath}`);
379
519
  }
380
- function commandStatus(options) {
381
- const { configPath, pluginPath } = resolvePaths(options);
382
- const pluginExists = existsSync(pluginPath);
383
- const pluginType = pluginExists ? lstatSync(pluginPath).isSymbolicLink() ? "symlink" : "file" : "missing";
384
- let providerExists = false;
385
- let pluginEnabled = false;
520
+ function getStatusResult(configPath, pluginPath) {
521
+ let pluginType = "missing";
522
+ let pluginTarget;
523
+ if (existsSync(pluginPath)) {
524
+ const stat = lstatSync(pluginPath);
525
+ pluginType = stat.isSymbolicLink() ? "symlink" : "file";
526
+ if (pluginType === "symlink") {
527
+ try {
528
+ pluginTarget = readFileSync(pluginPath, "utf8");
529
+ } catch {
530
+ pluginTarget = undefined;
531
+ }
532
+ }
533
+ }
534
+ let providerEnabled = false;
535
+ let baseUrl = "http://127.0.0.1:32124/v1";
536
+ let modelCount = 0;
386
537
  if (existsSync(configPath)) {
387
538
  const config = readConfig(configPath);
388
- providerExists = Boolean(config.provider?.[PROVIDER_ID]);
389
- pluginEnabled = Array.isArray(config.plugin) && config.plugin.includes(PROVIDER_ID);
539
+ const provider = config.provider?.["cursor-acp"];
540
+ providerEnabled = !!provider;
541
+ if (provider?.options?.baseURL) {
542
+ baseUrl = provider.options.baseURL;
543
+ }
544
+ modelCount = Object.keys(provider?.models || {}).length;
545
+ }
546
+ const opencodeDir = dirname(configPath);
547
+ const sdkPath = join(opencodeDir, "node_modules", "@ai-sdk", "openai-compatible");
548
+ const aiSdkInstalled = existsSync(sdkPath);
549
+ return {
550
+ plugin: {
551
+ path: pluginPath,
552
+ type: pluginType,
553
+ target: pluginTarget
554
+ },
555
+ provider: {
556
+ configPath,
557
+ name: "cursor-acp",
558
+ enabled: providerEnabled,
559
+ baseUrl,
560
+ modelCount
561
+ },
562
+ aiSdk: {
563
+ installed: aiSdkInstalled
564
+ }
565
+ };
566
+ }
567
+ function commandStatus(options) {
568
+ const { configPath, pluginPath } = resolvePaths(options);
569
+ const result = getStatusResult(configPath, pluginPath);
570
+ if (options.json) {
571
+ console.log(JSON.stringify(result, null, 2));
572
+ return;
573
+ }
574
+ console.log("");
575
+ console.log("Plugin");
576
+ console.log(` Path: ${result.plugin.path}`);
577
+ if (result.plugin.type === "symlink" && result.plugin.target) {
578
+ console.log(` Type: symlink → ${result.plugin.target}`);
579
+ } else if (result.plugin.type === "file") {
580
+ console.log(` Type: file (copy)`);
581
+ } else {
582
+ console.log(` Type: missing`);
583
+ }
584
+ console.log("");
585
+ console.log("Provider");
586
+ console.log(` Config: ${result.provider.configPath}`);
587
+ console.log(` Name: ${result.provider.name}`);
588
+ console.log(` Enabled: ${result.provider.enabled ? "yes" : "no"}`);
589
+ console.log(` Base URL: ${result.provider.baseUrl}`);
590
+ console.log(` Models: ${result.provider.modelCount}`);
591
+ console.log("");
592
+ console.log("AI SDK");
593
+ console.log(` @ai-sdk/openai-compatible: ${result.aiSdk.installed ? "installed" : "not installed"}`);
594
+ }
595
+ function commandDoctor(options) {
596
+ const { configPath, pluginPath } = resolvePaths(options);
597
+ const checks = runDoctorChecks(configPath, pluginPath);
598
+ console.log("");
599
+ for (const check of checks) {
600
+ const symbol = check.passed ? "✓" : check.warning ? "⚠" : "✗";
601
+ const color = check.passed ? "\x1B[32m" : check.warning ? "\x1B[33m" : "\x1B[31m";
602
+ console.log(` ${color}${symbol}\x1B[0m ${check.name}: ${check.message}`);
603
+ }
604
+ const failed = checks.filter((c) => !c.passed && !c.warning);
605
+ console.log("");
606
+ if (failed.length === 0) {
607
+ console.log("All checks passed!");
608
+ } else {
609
+ console.log(`${failed.length} check(s) failed. See messages above.`);
390
610
  }
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
611
  }
396
612
  function main() {
397
613
  let parsed;
@@ -418,6 +634,9 @@ function main() {
418
634
  case "status":
419
635
  commandStatus(parsed.options);
420
636
  return;
637
+ case "doctor":
638
+ commandDoctor(parsed.options);
639
+ return;
421
640
  case "help":
422
641
  printHelp();
423
642
  return;
@@ -429,3 +648,11 @@ function main() {
429
648
  }
430
649
  }
431
650
  main();
651
+ export {
652
+ runDoctorChecks,
653
+ getStatusResult,
654
+ getBrandingHeader,
655
+ checkCursorAgentLogin,
656
+ checkCursorAgent,
657
+ checkBun
658
+ };