@rama_nigg/open-cursor 2.3.4 → 2.3.5
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 +32 -10
- package/dist/index.js +7 -6
- package/dist/plugin-entry.js +7 -6
- package/package.json +1 -1
- package/src/provider/tool-loop-guard.ts +11 -2
package/README.md
CHANGED
|
@@ -7,11 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
No prompt limits. No broken streams. Full thinking + tool support in OpenCode. Your Cursor subscription, properly integrated.
|
|
9
9
|
|
|
10
|
-
## Prerequisites
|
|
11
|
-
|
|
12
|
-
Required: [OpenCode](https://opencode.ai/) + [cursor-agent](https://cursor.com/) (`curl -fsSL https://cursor.com/install | bash && cursor-agent login`)
|
|
13
|
-
Optional: [Bun](https://bun.sh/) (Options B-F), [Go 1.21+](https://go.dev/) (Option D)
|
|
14
|
-
|
|
15
10
|
## Installation
|
|
16
11
|
|
|
17
12
|
### Option A — One-line installer
|
|
@@ -20,7 +15,8 @@ Optional: [Bun](https://bun.sh/) (Options B-F), [Go 1.21+](https://go.dev/) (Opt
|
|
|
20
15
|
curl -fsSL https://raw.githubusercontent.com/Nomadcxx/opencode-cursor/main/install.sh | bash
|
|
21
16
|
```
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
<details>
|
|
19
|
+
<summary><b>Option B</b> — Add to opencode.json</summary>
|
|
24
20
|
|
|
25
21
|
Add to `~/.config/opencode/opencode.json`:
|
|
26
22
|
|
|
@@ -72,8 +68,10 @@ Add to `~/.config/opencode/opencode.json`:
|
|
|
72
68
|
```
|
|
73
69
|
|
|
74
70
|
> Update models anytime: `cursor-agent models`
|
|
71
|
+
</details>
|
|
75
72
|
|
|
76
|
-
|
|
73
|
+
<details>
|
|
74
|
+
<summary><b>Option C</b> — npm global + CLI</summary>
|
|
77
75
|
|
|
78
76
|
```bash
|
|
79
77
|
npm install -g @rama_nigg/open-cursor
|
|
@@ -81,22 +79,28 @@ open-cursor install
|
|
|
81
79
|
```
|
|
82
80
|
|
|
83
81
|
Upgrade: `npm update -g @rama_nigg/open-cursor`
|
|
82
|
+
</details>
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
<details>
|
|
85
|
+
<summary><b>Option D</b> — Go TUI installer</summary>
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
88
|
git clone https://github.com/Nomadcxx/opencode-cursor.git
|
|
89
89
|
cd opencode-cursor
|
|
90
90
|
go build -o ./installer ./cmd/installer && ./installer
|
|
91
91
|
```
|
|
92
|
+
</details>
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
<details>
|
|
95
|
+
<summary><b>Option E</b> — LLM paste</summary>
|
|
94
96
|
|
|
95
97
|
```
|
|
96
98
|
Install open-cursor for OpenCode: edit ~/.config/opencode/opencode.json, add "@rama_nigg/open-cursor@latest" to "plugin", add a "cursor-acp" provider with npm "@ai-sdk/openai-compatible" and models from `cursor-agent models` prefixed with "cursor-acp/". Auth: `cursor-agent login`. Verify: `opencode models | grep cursor-acp`.
|
|
97
99
|
```
|
|
100
|
+
</details>
|
|
98
101
|
|
|
99
|
-
|
|
102
|
+
<details>
|
|
103
|
+
<summary><b>Option F</b> — Manual (from source)</summary>
|
|
100
104
|
|
|
101
105
|
```bash
|
|
102
106
|
git clone https://github.com/Nomadcxx/opencode-cursor.git && cd opencode-cursor
|
|
@@ -106,6 +110,7 @@ ln -sf $(pwd)/dist/plugin-entry.js ~/.config/opencode/plugin/cursor-acp.js
|
|
|
106
110
|
```
|
|
107
111
|
|
|
108
112
|
Add `"cursor-acp"` to the `plugin` array and reuse the provider block from Option B.
|
|
113
|
+
</details>
|
|
109
114
|
|
|
110
115
|
## Authentication
|
|
111
116
|
|
|
@@ -170,6 +175,23 @@ Default mode: `CURSOR_ACP_TOOL_LOOP_MODE=opencode`. Legacy `proxy-exec` still av
|
|
|
170
175
|
|
|
171
176
|
Debug logging: `CURSOR_ACP_LOG_LEVEL=debug opencode run "your prompt" --model cursor-acp/auto`
|
|
172
177
|
|
|
178
|
+
## Roadmap
|
|
179
|
+
|
|
180
|
+
```mermaid
|
|
181
|
+
flowchart LR
|
|
182
|
+
P1[/Stabilise/] --> P2[/MCP Server/] --> P3[/Simplify/] --> P4[/ACP + MCP/]
|
|
183
|
+
|
|
184
|
+
style P1 fill:#264653,stroke:#1d3557,color:#fff
|
|
185
|
+
style P2 fill:#264653,stroke:#1d3557,color:#fff
|
|
186
|
+
style P3 fill:#495057,stroke:#343a40,color:#adb5bd
|
|
187
|
+
style P4 fill:#495057,stroke:#343a40,color:#adb5bd
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
[X] **Stabilise** — Clean up dead code, fix test isolation
|
|
191
|
+
[ ] **MCP Server** — Expose OpenCode tools via stdio transport
|
|
192
|
+
[ ] **Simplify** — Rip out serialisation layers
|
|
193
|
+
[ ] **ACP + MCP** — Structured protocols end-to-end
|
|
194
|
+
|
|
173
195
|
## License
|
|
174
196
|
|
|
175
197
|
BSD-3-Clause
|
package/dist/index.js
CHANGED
|
@@ -17408,6 +17408,7 @@ function parseToolLoopMaxRepeat(value) {
|
|
|
17408
17408
|
return { value: Math.floor(parsed), valid: true };
|
|
17409
17409
|
}
|
|
17410
17410
|
function createToolLoopGuard(messages, maxRepeat) {
|
|
17411
|
+
const coarseMaxRepeat = maxRepeat * COARSE_LIMIT_MULTIPLIER;
|
|
17411
17412
|
const {
|
|
17412
17413
|
byCallId,
|
|
17413
17414
|
latest,
|
|
@@ -17447,13 +17448,13 @@ function createToolLoopGuard(messages, maxRepeat) {
|
|
|
17447
17448
|
}
|
|
17448
17449
|
const strictFingerprint = `${toolCall.function.name}|${argShape}|${errorClass}`;
|
|
17449
17450
|
const coarseFingerprint = `${toolCall.function.name}|${errorClass}`;
|
|
17450
|
-
return evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat);
|
|
17451
|
+
return evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat, coarseMaxRepeat);
|
|
17451
17452
|
},
|
|
17452
17453
|
evaluateValidation(toolCall, validationSignature) {
|
|
17453
17454
|
const normalizedSignature = normalizeValidationSignature(validationSignature);
|
|
17454
17455
|
const strictFingerprint = `${toolCall.function.name}|schema:${normalizedSignature}|validation`;
|
|
17455
17456
|
const coarseFingerprint = `${toolCall.function.name}|validation`;
|
|
17456
|
-
return evaluateWithFingerprints("validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat);
|
|
17457
|
+
return evaluateWithFingerprints("validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat, coarseMaxRepeat);
|
|
17457
17458
|
},
|
|
17458
17459
|
resetFingerprint(fingerprint) {
|
|
17459
17460
|
counts.delete(fingerprint);
|
|
@@ -17684,7 +17685,7 @@ function normalizeValidationSignature(signature) {
|
|
|
17684
17685
|
const normalized = signature.trim().toLowerCase();
|
|
17685
17686
|
return normalized.length > 0 ? normalized : "invalid";
|
|
17686
17687
|
}
|
|
17687
|
-
function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat) {
|
|
17688
|
+
function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat, coarseMaxRepeat) {
|
|
17688
17689
|
if (errorClass === "success") {
|
|
17689
17690
|
return {
|
|
17690
17691
|
fingerprint: strictFingerprint,
|
|
@@ -17700,12 +17701,12 @@ function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerpri
|
|
|
17700
17701
|
const coarseRepeatCount = (coarseCounts.get(coarseFingerprint) ?? 0) + 1;
|
|
17701
17702
|
coarseCounts.set(coarseFingerprint, coarseRepeatCount);
|
|
17702
17703
|
const strictTriggered = strictRepeatCount > maxRepeat;
|
|
17703
|
-
const coarseTriggered = coarseRepeatCount >
|
|
17704
|
+
const coarseTriggered = coarseRepeatCount > coarseMaxRepeat;
|
|
17704
17705
|
const preferCoarseFingerprint = coarseTriggered && !strictTriggered;
|
|
17705
17706
|
return {
|
|
17706
17707
|
fingerprint: preferCoarseFingerprint ? coarseFingerprint : strictFingerprint,
|
|
17707
17708
|
repeatCount: preferCoarseFingerprint ? coarseRepeatCount : strictRepeatCount,
|
|
17708
|
-
maxRepeat,
|
|
17709
|
+
maxRepeat: preferCoarseFingerprint ? coarseMaxRepeat : maxRepeat,
|
|
17709
17710
|
errorClass,
|
|
17710
17711
|
triggered: strictTriggered || coarseTriggered,
|
|
17711
17712
|
tracked: true
|
|
@@ -17790,7 +17791,7 @@ function containsAny(text, patterns) {
|
|
|
17790
17791
|
function isRecord4(value) {
|
|
17791
17792
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17792
17793
|
}
|
|
17793
|
-
var UNKNOWN_AS_SUCCESS_TOOLS;
|
|
17794
|
+
var UNKNOWN_AS_SUCCESS_TOOLS, COARSE_LIMIT_MULTIPLIER = 3;
|
|
17794
17795
|
var init_tool_loop_guard = __esm(() => {
|
|
17795
17796
|
UNKNOWN_AS_SUCCESS_TOOLS = new Set([
|
|
17796
17797
|
"bash",
|
package/dist/plugin-entry.js
CHANGED
|
@@ -17408,6 +17408,7 @@ function parseToolLoopMaxRepeat(value) {
|
|
|
17408
17408
|
return { value: Math.floor(parsed), valid: true };
|
|
17409
17409
|
}
|
|
17410
17410
|
function createToolLoopGuard(messages, maxRepeat) {
|
|
17411
|
+
const coarseMaxRepeat = maxRepeat * COARSE_LIMIT_MULTIPLIER;
|
|
17411
17412
|
const {
|
|
17412
17413
|
byCallId,
|
|
17413
17414
|
latest,
|
|
@@ -17447,13 +17448,13 @@ function createToolLoopGuard(messages, maxRepeat) {
|
|
|
17447
17448
|
}
|
|
17448
17449
|
const strictFingerprint = `${toolCall.function.name}|${argShape}|${errorClass}`;
|
|
17449
17450
|
const coarseFingerprint = `${toolCall.function.name}|${errorClass}`;
|
|
17450
|
-
return evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat);
|
|
17451
|
+
return evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat, coarseMaxRepeat);
|
|
17451
17452
|
},
|
|
17452
17453
|
evaluateValidation(toolCall, validationSignature) {
|
|
17453
17454
|
const normalizedSignature = normalizeValidationSignature(validationSignature);
|
|
17454
17455
|
const strictFingerprint = `${toolCall.function.name}|schema:${normalizedSignature}|validation`;
|
|
17455
17456
|
const coarseFingerprint = `${toolCall.function.name}|validation`;
|
|
17456
|
-
return evaluateWithFingerprints("validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat);
|
|
17457
|
+
return evaluateWithFingerprints("validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat, coarseMaxRepeat);
|
|
17457
17458
|
},
|
|
17458
17459
|
resetFingerprint(fingerprint) {
|
|
17459
17460
|
counts.delete(fingerprint);
|
|
@@ -17684,7 +17685,7 @@ function normalizeValidationSignature(signature) {
|
|
|
17684
17685
|
const normalized = signature.trim().toLowerCase();
|
|
17685
17686
|
return normalized.length > 0 ? normalized : "invalid";
|
|
17686
17687
|
}
|
|
17687
|
-
function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat) {
|
|
17688
|
+
function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat, coarseMaxRepeat) {
|
|
17688
17689
|
if (errorClass === "success") {
|
|
17689
17690
|
return {
|
|
17690
17691
|
fingerprint: strictFingerprint,
|
|
@@ -17700,12 +17701,12 @@ function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerpri
|
|
|
17700
17701
|
const coarseRepeatCount = (coarseCounts.get(coarseFingerprint) ?? 0) + 1;
|
|
17701
17702
|
coarseCounts.set(coarseFingerprint, coarseRepeatCount);
|
|
17702
17703
|
const strictTriggered = strictRepeatCount > maxRepeat;
|
|
17703
|
-
const coarseTriggered = coarseRepeatCount >
|
|
17704
|
+
const coarseTriggered = coarseRepeatCount > coarseMaxRepeat;
|
|
17704
17705
|
const preferCoarseFingerprint = coarseTriggered && !strictTriggered;
|
|
17705
17706
|
return {
|
|
17706
17707
|
fingerprint: preferCoarseFingerprint ? coarseFingerprint : strictFingerprint,
|
|
17707
17708
|
repeatCount: preferCoarseFingerprint ? coarseRepeatCount : strictRepeatCount,
|
|
17708
|
-
maxRepeat,
|
|
17709
|
+
maxRepeat: preferCoarseFingerprint ? coarseMaxRepeat : maxRepeat,
|
|
17709
17710
|
errorClass,
|
|
17710
17711
|
triggered: strictTriggered || coarseTriggered,
|
|
17711
17712
|
tracked: true
|
|
@@ -17790,7 +17791,7 @@ function containsAny(text, patterns) {
|
|
|
17790
17791
|
function isRecord4(value) {
|
|
17791
17792
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17792
17793
|
}
|
|
17793
|
-
var UNKNOWN_AS_SUCCESS_TOOLS;
|
|
17794
|
+
var UNKNOWN_AS_SUCCESS_TOOLS, COARSE_LIMIT_MULTIPLIER = 3;
|
|
17794
17795
|
var init_tool_loop_guard = __esm(() => {
|
|
17795
17796
|
UNKNOWN_AS_SUCCESS_TOOLS = new Set([
|
|
17796
17797
|
"bash",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rama_nigg/open-cursor",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.5",
|
|
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/plugin-entry.js",
|
|
@@ -52,10 +52,16 @@ export function parseToolLoopMaxRepeat(
|
|
|
52
52
|
return { value: Math.floor(parsed), valid: true };
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// Coarse fingerprint (tool|errorClass without args) uses a higher multiplier
|
|
56
|
+
// to allow legitimate exploration across different files/targets while still
|
|
57
|
+
// catching spray-and-pray patterns.
|
|
58
|
+
const COARSE_LIMIT_MULTIPLIER = 3;
|
|
59
|
+
|
|
55
60
|
export function createToolLoopGuard(
|
|
56
61
|
messages: Array<unknown>,
|
|
57
62
|
maxRepeat: number,
|
|
58
63
|
): ToolLoopGuard {
|
|
64
|
+
const coarseMaxRepeat = maxRepeat * COARSE_LIMIT_MULTIPLIER;
|
|
59
65
|
const {
|
|
60
66
|
byCallId,
|
|
61
67
|
latest,
|
|
@@ -125,6 +131,7 @@ export function createToolLoopGuard(
|
|
|
125
131
|
counts,
|
|
126
132
|
coarseCounts,
|
|
127
133
|
maxRepeat,
|
|
134
|
+
coarseMaxRepeat,
|
|
128
135
|
);
|
|
129
136
|
},
|
|
130
137
|
|
|
@@ -139,6 +146,7 @@ export function createToolLoopGuard(
|
|
|
139
146
|
validationCounts,
|
|
140
147
|
validationCoarseCounts,
|
|
141
148
|
maxRepeat,
|
|
149
|
+
coarseMaxRepeat,
|
|
142
150
|
);
|
|
143
151
|
},
|
|
144
152
|
|
|
@@ -454,6 +462,7 @@ function evaluateWithFingerprints(
|
|
|
454
462
|
strictCounts: Map<string, number>,
|
|
455
463
|
coarseCounts: Map<string, number>,
|
|
456
464
|
maxRepeat: number,
|
|
465
|
+
coarseMaxRepeat: number,
|
|
457
466
|
): ToolLoopGuardDecision {
|
|
458
467
|
if (errorClass === "success") {
|
|
459
468
|
return {
|
|
@@ -471,12 +480,12 @@ function evaluateWithFingerprints(
|
|
|
471
480
|
const coarseRepeatCount = (coarseCounts.get(coarseFingerprint) ?? 0) + 1;
|
|
472
481
|
coarseCounts.set(coarseFingerprint, coarseRepeatCount);
|
|
473
482
|
const strictTriggered = strictRepeatCount > maxRepeat;
|
|
474
|
-
const coarseTriggered = coarseRepeatCount >
|
|
483
|
+
const coarseTriggered = coarseRepeatCount > coarseMaxRepeat;
|
|
475
484
|
const preferCoarseFingerprint = coarseTriggered && !strictTriggered;
|
|
476
485
|
return {
|
|
477
486
|
fingerprint: preferCoarseFingerprint ? coarseFingerprint : strictFingerprint,
|
|
478
487
|
repeatCount: preferCoarseFingerprint ? coarseRepeatCount : strictRepeatCount,
|
|
479
|
-
maxRepeat,
|
|
488
|
+
maxRepeat: preferCoarseFingerprint ? coarseMaxRepeat : maxRepeat,
|
|
480
489
|
errorClass,
|
|
481
490
|
triggered: strictTriggered || coarseTriggered,
|
|
482
491
|
tracked: true,
|