@rama_nigg/open-cursor 2.3.19 → 2.3.20
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/dist/cli/mcptool.js +228 -5648
- package/dist/index.js +320 -5725
- package/dist/plugin-entry.js +320 -5725
- package/package.json +1 -1
- package/src/mcp/config.ts +49 -0
- package/src/plugin.ts +17 -4
- package/src/provider/tool-loop-guard.ts +8 -3
- package/src/proxy/prompt-builder.ts +10 -1
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.20",
|
|
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",
|
package/src/mcp/config.ts
CHANGED
|
@@ -93,6 +93,55 @@ export function readMcpConfigs(deps: ReadMcpConfigsDeps = {}): McpServerConfig[]
|
|
|
93
93
|
return configs;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
interface ReadSubagentNamesDeps {
|
|
97
|
+
configJson?: string;
|
|
98
|
+
existsSync?: (path: string) => boolean;
|
|
99
|
+
readFileSync?: (path: string, enc: BufferEncoding) => string;
|
|
100
|
+
env?: NodeJS.ProcessEnv;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function readSubagentNames(deps: ReadSubagentNamesDeps = {}): string[] {
|
|
104
|
+
let raw: string;
|
|
105
|
+
|
|
106
|
+
if (deps.configJson != null) {
|
|
107
|
+
raw = deps.configJson;
|
|
108
|
+
} else {
|
|
109
|
+
const exists = deps.existsSync ?? nodeExistsSync;
|
|
110
|
+
const readFile = deps.readFileSync ?? nodeReadFileSync;
|
|
111
|
+
const configPath = resolveOpenCodeConfigPath(deps.env ?? process.env);
|
|
112
|
+
if (!exists(configPath)) return ["general-purpose"];
|
|
113
|
+
try {
|
|
114
|
+
raw = readFile(configPath, "utf8");
|
|
115
|
+
} catch {
|
|
116
|
+
return ["general-purpose"];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let parsed: Record<string, unknown>;
|
|
121
|
+
try {
|
|
122
|
+
parsed = JSON.parse(raw);
|
|
123
|
+
} catch {
|
|
124
|
+
return ["general-purpose"];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const agentSection = parsed.agent;
|
|
128
|
+
if (!agentSection || typeof agentSection !== "object" || Array.isArray(agentSection)) {
|
|
129
|
+
return ["general-purpose"];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const agents = agentSection as Record<string, unknown>;
|
|
133
|
+
const names = Object.keys(agents);
|
|
134
|
+
if (names.length === 0) return ["general-purpose"];
|
|
135
|
+
|
|
136
|
+
const subagentNames = names.filter((name) => {
|
|
137
|
+
const entry = agents[name];
|
|
138
|
+
return entry && typeof entry === "object" && !Array.isArray(entry)
|
|
139
|
+
&& (entry as Record<string, unknown>).mode === "subagent";
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return subagentNames.length > 0 ? subagentNames : names;
|
|
143
|
+
}
|
|
144
|
+
|
|
96
145
|
function isStringRecord(v: unknown): v is Record<string, string> {
|
|
97
146
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
98
147
|
}
|
package/src/plugin.ts
CHANGED
|
@@ -25,7 +25,7 @@ import { ToolRouter } from "./tools/router.js";
|
|
|
25
25
|
import { SkillLoader } from "./tools/skills/loader.js";
|
|
26
26
|
import { SkillResolver } from "./tools/skills/resolver.js";
|
|
27
27
|
import { autoRefreshModels } from "./models/sync.js";
|
|
28
|
-
import { readMcpConfigs } from "./mcp/config.js";
|
|
28
|
+
import { readMcpConfigs, readSubagentNames } from "./mcp/config.js";
|
|
29
29
|
import { McpClientManager } from "./mcp/client-manager.js";
|
|
30
30
|
import { buildMcpToolHookEntries, buildMcpToolDefinitions } from "./mcp/tool-bridge.js";
|
|
31
31
|
import { createOpencodeClient } from "@opencode-ai/sdk";
|
|
@@ -92,6 +92,7 @@ export function buildAvailableToolsSystemMessage(
|
|
|
92
92
|
lastToolMap: Array<{ id: string; name: string }>,
|
|
93
93
|
mcpToolDefs: any[],
|
|
94
94
|
mcpToolSummaries?: McpToolSummary[],
|
|
95
|
+
subagentNames: string[] = [],
|
|
95
96
|
): string | null {
|
|
96
97
|
const parts: string[] = [];
|
|
97
98
|
|
|
@@ -132,6 +133,12 @@ export function buildAvailableToolsSystemMessage(
|
|
|
132
133
|
parts.push(lines.join("\n"));
|
|
133
134
|
}
|
|
134
135
|
|
|
136
|
+
if (subagentNames.length > 0) {
|
|
137
|
+
parts.push(
|
|
138
|
+
`When calling the task tool, set subagent_type to one of: ${subagentNames.join(", ")}. Do not omit this parameter.`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
135
142
|
return parts.length > 0 ? parts.join("\n\n") : null;
|
|
136
143
|
}
|
|
137
144
|
|
|
@@ -628,7 +635,8 @@ async function ensureCursorProxyServer(workspaceDirectory: string, toolRouter?:
|
|
|
628
635
|
const toolLoopGuard = createToolLoopGuard(messages, TOOL_LOOP_MAX_REPEAT);
|
|
629
636
|
const boundaryContext = createBoundaryRuntimeContext("bun-handler");
|
|
630
637
|
|
|
631
|
-
const
|
|
638
|
+
const subagentNames = readSubagentNames();
|
|
639
|
+
const prompt = buildPromptFromMessages(messages, tools, subagentNames);
|
|
632
640
|
const model = boundaryContext.run("normalizeRuntimeModel", (boundary) =>
|
|
633
641
|
boundary.normalizeRuntimeModel(body?.model),
|
|
634
642
|
);
|
|
@@ -1092,7 +1100,8 @@ async function ensureCursorProxyServer(workspaceDirectory: string, toolRouter?:
|
|
|
1092
1100
|
const toolLoopGuard = createToolLoopGuard(messages, TOOL_LOOP_MAX_REPEAT);
|
|
1093
1101
|
const boundaryContext = createBoundaryRuntimeContext("node-handler");
|
|
1094
1102
|
|
|
1095
|
-
const
|
|
1103
|
+
const subagentNames = readSubagentNames();
|
|
1104
|
+
const prompt = buildPromptFromMessages(messages, tools, subagentNames);
|
|
1096
1105
|
const model = boundaryContext.run("normalizeRuntimeModel", (boundary) =>
|
|
1097
1106
|
boundary.normalizeRuntimeModel(bodyData?.model),
|
|
1098
1107
|
);
|
|
@@ -2058,7 +2067,11 @@ export const CursorPlugin: Plugin = async ({ $, directory, worktree, client, ser
|
|
|
2058
2067
|
|
|
2059
2068
|
async "experimental.chat.system.transform"(input: any, output: { system: string[] }) {
|
|
2060
2069
|
if (!toolsEnabled) return;
|
|
2061
|
-
const
|
|
2070
|
+
const subagentNames = readSubagentNames();
|
|
2071
|
+
const systemMessage = buildAvailableToolsSystemMessage(
|
|
2072
|
+
lastToolNames, lastToolMap, mcpToolDefs, mcpToolSummaries,
|
|
2073
|
+
subagentNames,
|
|
2074
|
+
);
|
|
2062
2075
|
if (!systemMessage) return;
|
|
2063
2076
|
output.system = output.system || [];
|
|
2064
2077
|
output.system.push(systemMessage);
|
|
@@ -44,6 +44,7 @@ const EXPLORATION_TOOLS = new Set([
|
|
|
44
44
|
"bash",
|
|
45
45
|
"shell",
|
|
46
46
|
"webfetch",
|
|
47
|
+
"task",
|
|
47
48
|
]);
|
|
48
49
|
|
|
49
50
|
export interface ToolLoopGuardDecision {
|
|
@@ -511,16 +512,20 @@ function evaluateWithFingerprints(
|
|
|
511
512
|
};
|
|
512
513
|
}
|
|
513
514
|
|
|
515
|
+
const isExplorationTool = EXPLORATION_TOOLS.has(toolName.toLowerCase());
|
|
516
|
+
const effectiveMaxRepeat = isExplorationTool
|
|
517
|
+
? maxRepeat * EXPLORATION_LIMIT_MULTIPLIER
|
|
518
|
+
: maxRepeat;
|
|
519
|
+
|
|
514
520
|
const strictRepeatCount = (strictCounts.get(strictFingerprint) ?? 0) + 1;
|
|
515
521
|
strictCounts.set(strictFingerprint, strictRepeatCount);
|
|
516
|
-
const strictTriggered = strictRepeatCount >
|
|
522
|
+
const strictTriggered = strictRepeatCount > effectiveMaxRepeat;
|
|
517
523
|
|
|
518
|
-
const isExplorationTool = EXPLORATION_TOOLS.has(toolName.toLowerCase());
|
|
519
524
|
if (isExplorationTool) {
|
|
520
525
|
return {
|
|
521
526
|
fingerprint: strictFingerprint,
|
|
522
527
|
repeatCount: strictRepeatCount,
|
|
523
|
-
maxRepeat,
|
|
528
|
+
maxRepeat: effectiveMaxRepeat,
|
|
524
529
|
errorClass,
|
|
525
530
|
triggered: strictTriggered,
|
|
526
531
|
tracked: true,
|
|
@@ -36,7 +36,7 @@ function debugLogToFile(message: string, data: any): void {
|
|
|
36
36
|
* Handles role:"tool" result messages and assistant tool_calls that
|
|
37
37
|
* plain text flattening would silently drop.
|
|
38
38
|
*/
|
|
39
|
-
export function buildPromptFromMessages(messages: Array<any>, tools: Array<any
|
|
39
|
+
export function buildPromptFromMessages(messages: Array<any>, tools: Array<any>, subagentNames: string[] = []): string {
|
|
40
40
|
// DEBUG: Log incoming message structure to file for root cause analysis
|
|
41
41
|
const messageSummary = messages.map((m: any, i: number) => {
|
|
42
42
|
const role = m?.role ?? "?";
|
|
@@ -98,6 +98,15 @@ export function buildPromptFromMessages(messages: Array<any>, tools: Array<any>)
|
|
|
98
98
|
`SYSTEM: You have access to the following tools. When you need to use one, respond with a tool_call in the standard OpenAI format.\n` +
|
|
99
99
|
`Tool guidance: prefer write/edit for file changes; use bash mainly to run commands/tests.\n\nAvailable tools:\n${toolDescs}`,
|
|
100
100
|
);
|
|
101
|
+
const hasTaskTool = tools.some((t: any) => {
|
|
102
|
+
const name = (t?.function?.name ?? t?.name ?? "").toLowerCase();
|
|
103
|
+
return name === "task";
|
|
104
|
+
});
|
|
105
|
+
if (hasTaskTool && subagentNames.length > 0) {
|
|
106
|
+
lines.push(
|
|
107
|
+
`When calling the task tool, set subagent_type to one of: ${subagentNames.join(", ")}. Do not omit this parameter.`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
for (const message of messages) {
|