@rama_nigg/open-cursor 2.3.0 → 2.3.2

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
@@ -9,27 +9,63 @@ No prompt limits. No broken streams. Full thinking + tool support in Opencode. Y
9
9
 
10
10
  ## Installation
11
11
 
12
- **Option A: One-Line Install**
12
+ **Option A Add to opencode.json (Recommended)**
13
+
14
+ The simplest approach—just add the npm package to your OpenCode config:
15
+
16
+ ```json
17
+ {
18
+ "plugin": ["@rama_nigg/open-cursor@latest"],
19
+ "provider": {
20
+ "cursor-acp": {
21
+ "name": "Cursor ACP",
22
+ "npm": "@ai-sdk/openai-compatible",
23
+ "models": {
24
+ "cursor-acp/claude-sonnet": { "name": "Claude Sonnet" },
25
+ "cursor-acp/gpt-4o": { "name": "GPT-4o" },
26
+ "cursor-acp/gemini-2.5-pro": { "name": "Gemini 2.5 Pro" },
27
+ "cursor-acp/cursor-small": { "name": "Cursor Small" }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ > **Note:** The model identifiers shown above are examples. Run `cursor-agent models` after authentication to see the full list of models available for your account.
35
+
36
+ After authenticating with `cursor-agent login`, run `cursor-agent models` to see the full model list, or use one of the automated installers below to auto-discover models.
37
+
38
+ **Prerequisites:** OpenCode and cursor-agent required. Install cursor-agent with: `curl -fsSL https://cursor.com/install | bash && cursor-agent login`. OpenCode's Bun runtime resolves the npm package automatically.
39
+
40
+ **Option B — One-line installer (curl)**
13
41
 
14
42
  ```bash
15
43
  curl -fsSL https://raw.githubusercontent.com/Nomadcxx/opencode-cursor/main/install.sh | bash
16
44
  ```
17
45
 
18
- **Option B: npm Package (Recommended)**
46
+ This automated installer detects your environment and chooses the best installation method.
47
+
48
+ **Option C — npm global install + CLI symlink**
49
+
50
+ Installs globally via npm and creates a symlink in the OpenCode plugin directory:
19
51
 
20
52
  ```bash
21
53
  npm install -g @rama_nigg/open-cursor
22
54
  open-cursor install
23
55
  ```
24
56
 
57
+ The `open-cursor install` command creates a symlink at `~/.config/opencode/plugin/cursor-acp.js` pointing to the npm package's entry file.
58
+
25
59
  Upgrade later with:
26
60
 
27
61
  ```bash
28
62
  npm update -g @rama_nigg/open-cursor
29
- open-cursor install
63
+ # Symlink automatically uses updated files
30
64
  ```
31
65
 
32
- **Option C: TUI Installer**
66
+ **Option D — Go TUI installer**
67
+
68
+ Interactive visual installer with dual-mode support (quick install or build from source):
33
69
 
34
70
  ```bash
35
71
  git clone https://github.com/Nomadcxx/opencode-cursor.git
@@ -37,46 +73,55 @@ cd opencode-cursor
37
73
  go build -o ./installer ./cmd/installer && ./installer
38
74
  ```
39
75
 
40
- **Option D: Let an LLM do it**
76
+ **Option E LLM instructions**
41
77
 
42
78
  Paste this into any LLM agent (Claude Code, OpenCode, Cursor, etc.):
43
79
 
44
80
  ```
45
- Install the cursor-acp plugin for OpenCode:
46
-
47
- 1. Preferred install (npm):
48
- npm install -g @rama_nigg/open-cursor
49
- open-cursor install
50
-
51
- 2. Fallback (build from source):
52
- git clone https://github.com/Nomadcxx/opencode-cursor.git
53
- cd opencode-cursor
54
- bun install && bun run build
55
- mkdir -p ~/.config/opencode/plugin
56
- ln -sf $(pwd)/dist/plugin-entry.js ~/.config/opencode/plugin/cursor-acp.js
57
- ./scripts/sync-models.sh
58
-
59
- 3. Verify:
60
- opencode models | grep cursor-acp
81
+ Install the cursor-acp plugin for OpenCode using the npm package approach:
82
+
83
+ 1. Edit your opencode.json config file (usually at ~/.config/opencode/opencode.json)
84
+ 2. Add "@rama_nigg/open-cursor@latest" to the plugin array
85
+ 3. Add the cursor-acp provider configuration with models
86
+ 4. Restart OpenCode
87
+
88
+ Example configuration:
89
+ {
90
+ "plugin": ["@rama_nigg/open-cursor@latest"],
91
+ "provider": {
92
+ "cursor-acp": {
93
+ "name": "Cursor ACP",
94
+ "npm": "@ai-sdk/openai-compatible",
95
+ "models": {
96
+ "cursor-acp/claude-sonnet": { "name": "Claude Sonnet" }
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ 5. Authenticate: cursor-agent login
103
+ 6. Verify: opencode models | grep cursor-acp
61
104
  ```
62
105
 
63
- **Option E: Manual Install**
106
+ **Option F Manual (from source)**
107
+
108
+ For developers and contributors who want full control:
64
109
 
65
110
  ```bash
111
+ git clone https://github.com/Nomadcxx/opencode-cursor.git
112
+ cd opencode-cursor
66
113
  bun install && bun run build
67
114
  mkdir -p ~/.config/opencode/plugin
68
115
  ln -sf $(pwd)/dist/plugin-entry.js ~/.config/opencode/plugin/cursor-acp.js
69
116
  ```
70
117
 
71
- The installers handle the rest automatically. If you're doing a manual install, you'll need to do the following steps yourself.
72
-
73
- Easiest way is to run the sync script, which populates everything for you:
118
+ The automated installers handle configuration automatically. For manual installs, run the sync script to populate models:
74
119
 
75
120
  ```bash
76
121
  ./scripts/sync-models.sh
77
122
  ```
78
123
 
79
- Or if you'd rather do it by hand, add this to `~/.config/opencode/opencode.json` (then run `./scripts/sync-models.sh` to populate models):
124
+ Or configure manually by adding this to `~/.config/opencode/opencode.json` (then run `./scripts/sync-models.sh` to populate models):
80
125
 
81
126
  ```json
82
127
  {
@@ -126,6 +171,22 @@ Or if you'd rather do it by hand, add this to `~/.config/opencode/opencode.json`
126
171
  }
127
172
  ```
128
173
 
174
+ ### Plugin Configuration Reference
175
+
176
+ Depending on your installation method, use the appropriate plugin identifier:
177
+
178
+ **npm package (recommended for production):**
179
+ ```json
180
+ "plugin": ["@rama_nigg/open-cursor@latest"]
181
+ ```
182
+
183
+ **Local build (for development):**
184
+ ```json
185
+ "plugin": ["cursor-acp"]
186
+ ```
187
+
188
+ Both approaches work—the npm package is resolved automatically by OpenCode's Bun runtime, while the local build requires the symlink setup shown in Option F above.
189
+
129
190
  ## Authentication
130
191
 
131
192
  ### Option 1: Via OpenCode (Recommended)
@@ -206,12 +267,15 @@ Detailed architecture: [docs/architecture/runtime-tool-loop.md](docs/architectur
206
267
 
207
268
  ## Prerequisites
208
269
 
270
+ **For Option A (npm-direct):** [OpenCode](https://opencode.ai/) and [cursor-agent](https://cursor.com/) required. The npm package is resolved by OpenCode's Bun runtime, but cursor-agent handles the actual API communication.
271
+
272
+ **For Options B-F:**
209
273
  - [Bun](https://bun.sh/)
210
274
  - [cursor-agent](https://cursor.com/) - `curl -fsSL https://cursor.com/install | bash`
211
275
 
212
- **Option A (one-line install):** If Go is installed, the script runs the TUI installer; otherwise it performs a shell-only install (Bun + cursor-agent required). For syncing models without the TUI, install [jq](https://jq.org/) or run `./scripts/sync-models.sh` after install.
276
+ **Option B (one-line install):** If Go is installed, the script runs the TUI installer; otherwise it performs a shell-only install (OpenCode + cursor-agent required). For syncing models without the TUI, install [jq](https://jq.org/) or run `./scripts/sync-models.sh` after install.
213
277
 
214
- **Option B (TUI installer):** Go 1.21+ required to build the installer.
278
+ **Option D (TUI installer):** Go 1.21+ required to build the installer.
215
279
 
216
280
  ## Features
217
281
 
@@ -236,9 +236,24 @@ function checkOpenCode() {
236
236
  };
237
237
  }
238
238
  }
239
- function checkPluginFile(pluginPath) {
239
+ function isNpmDirectInstalled(config) {
240
+ if (!config || typeof config !== "object")
241
+ return false;
242
+ const plugins = config.plugin;
243
+ if (!Array.isArray(plugins))
244
+ return false;
245
+ return plugins.some((p) => typeof p === "string" && p.startsWith(NPM_PACKAGE_PREFIX));
246
+ }
247
+ function checkPluginFile(pluginPath, config) {
240
248
  try {
241
249
  if (!existsSync(pluginPath)) {
250
+ if (isNpmDirectInstalled(config)) {
251
+ return {
252
+ name: "Plugin file",
253
+ passed: true,
254
+ message: "Installed via npm package (no symlink needed)"
255
+ };
256
+ }
242
257
  return {
243
258
  name: "Plugin file",
244
259
  passed: false,
@@ -308,17 +323,24 @@ function checkAiSdk(opencodeDir) {
308
323
  }
309
324
  function runDoctorChecks(configPath, pluginPath) {
310
325
  const opencodeDir = dirname(configPath);
326
+ let config;
327
+ try {
328
+ config = readConfig(configPath);
329
+ } catch {
330
+ config = undefined;
331
+ }
311
332
  return [
312
333
  checkBun(),
313
334
  checkCursorAgent(),
314
335
  checkCursorAgentLogin(),
315
336
  checkOpenCode(),
316
- checkPluginFile(pluginPath),
337
+ checkPluginFile(pluginPath, config),
317
338
  checkProviderConfig(configPath),
318
339
  checkAiSdk(opencodeDir)
319
340
  ];
320
341
  }
321
342
  var PROVIDER_ID = "cursor-acp";
343
+ var NPM_PACKAGE_PREFIX = "@rama_nigg/open-cursor";
322
344
  var DEFAULT_BASE_URL = "http://127.0.0.1:32124/v1";
323
345
  function printHelp() {
324
346
  const binName = basename(process.argv[1] || "open-cursor");
@@ -527,13 +549,20 @@ function commandSyncModels(options) {
527
549
  console.log(`Models synced: ${models.length}`);
528
550
  console.log(`Config path: ${configPath}`);
529
551
  }
552
+ var NPM_PACKAGE = "@rama_nigg/open-cursor";
530
553
  function commandUninstall(options) {
531
554
  const { configPath, pluginPath } = resolvePaths(options);
532
555
  rmSync(pluginPath, { force: true });
533
556
  if (existsSync(configPath)) {
534
557
  const config = readConfig(configPath);
535
558
  if (Array.isArray(config.plugin)) {
536
- config.plugin = config.plugin.filter((name) => name !== PROVIDER_ID);
559
+ config.plugin = config.plugin.filter((name) => {
560
+ if (name === PROVIDER_ID)
561
+ return false;
562
+ if (typeof name === "string" && name.startsWith(NPM_PACKAGE))
563
+ return false;
564
+ return true;
565
+ });
537
566
  }
538
567
  if (config.provider && typeof config.provider === "object") {
539
568
  delete config.provider[PROVIDER_ID];
@@ -565,22 +594,32 @@ function getStatusResult(configPath, pluginPath) {
565
594
  pluginTarget = undefined;
566
595
  }
567
596
  }
597
+ let config;
568
598
  let providerEnabled = false;
569
599
  let baseUrl = "http://127.0.0.1:32124/v1";
570
600
  let modelCount = 0;
571
601
  if (existsSync(configPath)) {
572
- const config = readConfig(configPath);
602
+ config = readConfig(configPath);
573
603
  const provider = config.provider?.["cursor-acp"];
574
604
  providerEnabled = !!provider;
575
605
  if (provider?.options?.baseURL) {
576
606
  baseUrl = provider.options.baseURL;
577
607
  }
578
608
  modelCount = Object.keys(provider?.models || {}).length;
609
+ } else {
610
+ config = undefined;
579
611
  }
580
612
  const opencodeDir = dirname(configPath);
581
613
  const sdkPath = join(opencodeDir, "node_modules", "@ai-sdk", "openai-compatible");
582
614
  const aiSdkInstalled = existsSync(sdkPath);
615
+ let installMethod = "none";
616
+ if (pluginType !== "missing") {
617
+ installMethod = "symlink";
618
+ } else if (isNpmDirectInstalled(config)) {
619
+ installMethod = "npm-direct";
620
+ }
583
621
  return {
622
+ installMethod,
584
623
  plugin: {
585
624
  path: pluginPath,
586
625
  type: pluginType,
@@ -615,6 +654,7 @@ function commandStatus(options) {
615
654
  } else {
616
655
  console.log(` Type: missing`);
617
656
  }
657
+ console.log(` Install method: ${result.installMethod}`);
618
658
  console.log("");
619
659
  console.log("Provider");
620
660
  console.log(` Config: ${result.provider.configPath}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rama_nigg/open-cursor",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "No prompt limits. No broken streams. Full thinking + tool support. Your Cursor subscription, properly integrated.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -37,6 +37,7 @@ type CheckResult = {
37
37
  };
38
38
 
39
39
  type StatusResult = {
40
+ installMethod: "symlink" | "npm-direct" | "none";
40
41
  plugin: {
41
42
  path: string;
42
43
  type: "symlink" | "file" | "missing";
@@ -110,9 +111,23 @@ function checkOpenCode(): CheckResult {
110
111
  }
111
112
  }
112
113
 
113
- function checkPluginFile(pluginPath: string): CheckResult {
114
+ function isNpmDirectInstalled(config: unknown): boolean {
115
+ if (!config || typeof config !== "object") return false;
116
+ const plugins = (config as Record<string, unknown>).plugin;
117
+ if (!Array.isArray(plugins)) return false;
118
+ return plugins.some((p) => typeof p === "string" && p.startsWith(NPM_PACKAGE_PREFIX));
119
+ }
120
+
121
+ function checkPluginFile(pluginPath: string, config: unknown): CheckResult {
114
122
  try {
115
123
  if (!existsSync(pluginPath)) {
124
+ if (isNpmDirectInstalled(config)) {
125
+ return {
126
+ name: "Plugin file",
127
+ passed: true,
128
+ message: "Installed via npm package (no symlink needed)",
129
+ };
130
+ }
116
131
  return {
117
132
  name: "Plugin file",
118
133
  passed: false,
@@ -185,12 +200,18 @@ function checkAiSdk(opencodeDir: string): CheckResult {
185
200
 
186
201
  export function runDoctorChecks(configPath: string, pluginPath: string): CheckResult[] {
187
202
  const opencodeDir = dirname(configPath);
203
+ let config: unknown;
204
+ try {
205
+ config = readConfig(configPath);
206
+ } catch {
207
+ config = undefined;
208
+ }
188
209
  return [
189
210
  checkBun(),
190
211
  checkCursorAgent(),
191
212
  checkCursorAgentLogin(),
192
213
  checkOpenCode(),
193
- checkPluginFile(pluginPath),
214
+ checkPluginFile(pluginPath, config),
194
215
  checkProviderConfig(configPath),
195
216
  checkAiSdk(opencodeDir),
196
217
  ];
@@ -209,6 +230,7 @@ type Options = {
209
230
  };
210
231
 
211
232
  const PROVIDER_ID = "cursor-acp";
233
+ const NPM_PACKAGE_PREFIX = "@rama_nigg/open-cursor";
212
234
  const DEFAULT_BASE_URL = "http://127.0.0.1:32124/v1";
213
235
 
214
236
  function printHelp() {
@@ -443,6 +465,8 @@ function commandSyncModels(options: Options) {
443
465
  console.log(`Config path: ${configPath}`);
444
466
  }
445
467
 
468
+ const NPM_PACKAGE = "@rama_nigg/open-cursor";
469
+
446
470
  function commandUninstall(options: Options) {
447
471
  const { configPath, pluginPath } = resolvePaths(options);
448
472
  rmSync(pluginPath, { force: true });
@@ -450,7 +474,12 @@ function commandUninstall(options: Options) {
450
474
  if (existsSync(configPath)) {
451
475
  const config = readConfig(configPath);
452
476
  if (Array.isArray(config.plugin)) {
453
- config.plugin = config.plugin.filter((name: string) => name !== PROVIDER_ID);
477
+ // Remove both cursor-acp (symlink) and @rama_nigg/open-cursor (npm-direct) entries
478
+ config.plugin = config.plugin.filter((name: string) => {
479
+ if (name === PROVIDER_ID) return false;
480
+ if (typeof name === "string" && name.startsWith(NPM_PACKAGE)) return false;
481
+ return true;
482
+ });
454
483
  }
455
484
  if (config.provider && typeof config.provider === "object") {
456
485
  delete config.provider[PROVIDER_ID];
@@ -487,17 +516,20 @@ export function getStatusResult(configPath: string, pluginPath: string): StatusR
487
516
  }
488
517
 
489
518
  // Provider
519
+ let config: any;
490
520
  let providerEnabled = false;
491
521
  let baseUrl = "http://127.0.0.1:32124/v1";
492
522
  let modelCount = 0;
493
523
  if (existsSync(configPath)) {
494
- const config = readConfig(configPath);
524
+ config = readConfig(configPath);
495
525
  const provider = config.provider?.["cursor-acp"];
496
526
  providerEnabled = !!provider;
497
527
  if (provider?.options?.baseURL) {
498
528
  baseUrl = provider.options.baseURL;
499
529
  }
500
530
  modelCount = Object.keys(provider?.models || {}).length;
531
+ } else {
532
+ config = undefined;
501
533
  }
502
534
 
503
535
  // AI SDK
@@ -505,7 +537,15 @@ export function getStatusResult(configPath: string, pluginPath: string): StatusR
505
537
  const sdkPath = join(opencodeDir, "node_modules", "@ai-sdk", "openai-compatible");
506
538
  const aiSdkInstalled = existsSync(sdkPath);
507
539
 
540
+ let installMethod: "symlink" | "npm-direct" | "none" = "none";
541
+ if (pluginType !== "missing") {
542
+ installMethod = "symlink";
543
+ } else if (isNpmDirectInstalled(config)) {
544
+ installMethod = "npm-direct";
545
+ }
546
+
508
547
  return {
548
+ installMethod,
509
549
  plugin: {
510
550
  path: pluginPath,
511
551
  type: pluginType,
@@ -543,6 +583,7 @@ function commandStatus(options: Options) {
543
583
  } else {
544
584
  console.log(` Type: missing`);
545
585
  }
586
+ console.log(` Install method: ${result.installMethod}`);
546
587
 
547
588
  console.log("");
548
589
  console.log("Provider");