From 77559d1447aec7c9cee8e52b337a4a00dcd04a65 Mon Sep 17 00:00:00 2001 From: luvs01 Date: Mon, 17 Aug 2026 14:10:14 +0900 Subject: [PATCH] fix(cursor): preserve top-level apply_patch guidance --- src/adapters/cursor/tool-definitions.ts | 7 ++++++- tests/cursor-tool-definitions.test.ts | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/adapters/cursor/tool-definitions.ts b/src/adapters/cursor/tool-definitions.ts index 057399dacf..9110344e6d 100644 --- a/src/adapters/cursor/tool-definitions.ts +++ b/src/adapters/cursor/tool-definitions.ts @@ -596,6 +596,11 @@ export function buildCursorToolGuidanceSystemNote( : []; const shellBridgeLabel = quotedNames(shellBridgeNames.length > 0 ? shellBridgeNames : [...CODEX_SHELL_BRIDGE_TOOL_NAMES]); const hasApplyPatch = cursorRequestAdvertisesApplyPatch(tools, toolChoice); + const codeModeNestedOnlyNames = [ + "exec_command", + "shell_command", + ...(hasApplyPatch ? [] : ["apply_patch"]), + ]; const structuredEditNames = tools ?.filter(tool => !tool.namespace && isCursorStructuredEditToolName(tool.name)) .map(tool => tool.name) ?? []; @@ -615,7 +620,7 @@ export function buildCursorToolGuidanceSystemNote( // Code mode: shell/edit/MCP live inside freeform `exec` as nested helpers. Without this the // model probes for a top-level shell tool that is not there. codeMode - ? `\`${CODEX_UNIFIED_EXEC_TOOL}\` is Codex code mode: its body is JavaScript evaluated in a V8 isolate, not a shell command and not Node. Shell, file edits, and MCP are nested helpers called INSIDE that body as \`await tools.(...)\`, for example \`await tools.exec_command({cmd: \"ls\"})\`. Read the tool description for the exact nested helpers this turn provides. Those nested helpers are not themselves top-level tools, so do not call \`exec_command\`, \`shell_command\`, or \`apply_patch\` at the top level here${codeModeOtherTopLevelNames.length > 0 ? `; every other tool this turn lists, including ${quotedNames(codeModeOtherTopLevelNames)}, remains callable at the top level as usual` : ""}.` + ? `\`${CODEX_UNIFIED_EXEC_TOOL}\` is Codex code mode: its body is JavaScript evaluated in a V8 isolate, not a shell command and not Node. Shell, file edits, and MCP are nested helpers called INSIDE that body as \`await tools.(...)\`, for example \`await tools.exec_command({cmd: \"ls\"})\`. Read the tool description for the exact nested helpers this turn provides. Those nested helpers are not themselves top-level tools, so do not call ${quotedNames(codeModeNestedOnlyNames)} at the top level here${codeModeOtherTopLevelNames.length > 0 ? `; every other tool this turn lists, including ${quotedNames(codeModeOtherTopLevelNames)}, remains callable at the top level as usual` : ""}.` : undefined, codeMode ? "In code mode the isolate returns nothing on its own: call `text(...)` (or `notify(...)`) on any value you need to see, or the call completes with empty output. There is no `require`, no `module`, and no filesystem or network globals; reach the host only through the nested helpers." diff --git a/tests/cursor-tool-definitions.test.ts b/tests/cursor-tool-definitions.test.ts index 45c674219b..d230581460 100644 --- a/tests/cursor-tool-definitions.test.ts +++ b/tests/cursor-tool-definitions.test.ts @@ -472,6 +472,19 @@ describe("Cursor code mode tool guidance", () => { expect(note).not.toContain("they are not separate top-level tools"); }); + test("keeps a visible top-level apply_patch callable in code mode", () => { + const note = buildCursorToolGuidanceSystemNote([ + codeModeExec(), + { name: "apply_patch", description: "Patch files", parameters: {}, freeform: true }, + ]); + expect(note).toBeDefined(); + if (!note) throw new Error("Expected Cursor tool guidance note"); + + expect(note).toContain("including `apply_patch`, remains callable at the top level as usual"); + expect(note).toContain("For file edits, use the `apply_patch` tool"); + expect(note).not.toContain("`apply_patch` at the top level here"); + }); + test("keeps flat-catalog shell-bridge guidance when a bare bridge is advertised", () => { const note = buildCursorToolGuidanceSystemNote([{ name: "exec_command", description: "Run", parameters: {} }]); expect(note).toBeDefined();