@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rama_nigg/open-cursor",
3
- "version": "2.3.19",
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 prompt = buildPromptFromMessages(messages, tools);
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 prompt = buildPromptFromMessages(messages, tools);
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 systemMessage = buildAvailableToolsSystemMessage(lastToolNames, lastToolMap, mcpToolDefs, mcpToolSummaries);
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 > maxRepeat;
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>): string {
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) {