Repository navigation
fix(cursor): user-invoked skills hidden from the model reach the Cursor prompt - #34999
juliusmarminge wants to merge 2 commits into
Conversation
…15606) AI-assisted; verified with the tests and tools listed above.
…or prompt Cursor leaves disable-model-invocation skills out of the model's catalog, and the SDK takes only a string, so /name alone never loaded them. The adapter now appends the SKILL.md body of each invoked user-only skill to the prompt. Fixes #15606 AI-assisted; verified with the tests and tools listed above.
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Explicitly invoked Cursor skills hidden from the model now have their instructions included in turn and steering prompts. Existing invocation filters remain in place, with no changes to runtime permissions or product defaults. You can add or adjust custom eligibility rules. Learn more. |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
Cursor leaves
disable-model-invocationskills out of the model's skill catalog, and @cursor/sdkagent.sendtakes only a string, so the adapter's$name->/namerewrite alone never loaded these skills (the model either could not find them or picked a similarly named one).resolveCursorSkillTextin packages/provider-cursor/src/server/adapter.ts now keeps the discovered skills by name. It still rewrites$namementions to/name, then finds every/nametoken (typed or rewritten). For each one that names a discovered, user-invocable skill withuserInvocationOnly, it reads that SKILL.md and appends its body, with the frontmatter removed, to the prompt in a<skill name=...>block under a one-line 'The user invoked these skills' header (appendCursorInvokedSkills/cursorSlashSkillNamesin skills.ts). Discovery now also runs when the message has only a typed/name. Skills the model can already see are unchanged: no file is read and the text is the same as before. Because this runs before the orchestration-instructions wrapper and in steerTurn, the skill reaches the model on the first turn and in steers. If a file is missing or unreadable, that skill is skipped without an error. New regex/(?:^|\s)\/([a-zA-Z0-9][a-zA-Z0-9:_-]*)(?=\s|$)/gu: it has a single bounded character-class run anchored to a/after start or whitespace, with no nested or alternating quantifiers. Worst case is linear in the prompt length (each start position scans one token at most once). A path like/usr/bindoes not match because the lookahead needs whitespace or the end of the text after the name. No user-visible UI change.Fixes #15606
Reproduction on main
Commit b68aedf (origin/main). Started
T3CODE_DEV_SEED=0 vp run devin a fresh worktree, paired a browser, added Cursor under Settings > Providers (authenticated via CURSOR_API_KEY), and added a scratch git project with two project skills: .cursor/skills/zebra-only (frontmatterdisable-model-invocation: true, body: reply PURPLE-OKAPI-7731) and .cursor/skills/zebra-open (no flag, body: reply GREEN-TAPIR-4410). In a new Cursor thread (Composer 2.5) the$picker listed both skills. Turn 1: picked$zebra-onlyfrom the picker and sent it. The provider event log shows the SDK received/zebra-only ...inside<t3_code_orchestration_instructions>...<user_request>, then the agent read.cursor/skills/zebra-open/SKILL.mdand replied GREEN-TAPIR-4410, which is the wrong skill. Turn 2: typed/zebra-only ...by hand. The SDK received a bare/zebra-only ...at the start of the prompt, and the agent again replied GREEN-TAPIR-4410. The user-invocation-only skill's SKILL.md never reached the model either way. A unit test extending the existing Cursor steer case fails on main withexpected '/grill-me My plan.' to include 'Ask one question at a time.'.Evidence
Select
$zebra-onlyfrom the skill picker and send it with “Reply with the skill’s exact response.” Then send/zebra-only Reply with the skill’s exact response.in the same thread. The requested skill hasdisable-model-invocation: true; its expected reply isPURPLE-OKAPI-7731. A similarly named open skill repliesGREEN-TAPIR-4410.Real web app, Chromium, light theme, 1280×800 CSS viewport; separate fresh databases and identical synthetic projects; real Cursor SDK on both commits.
Fixture script · Reproduction and capture notes
Recordings are 2× speed. Only the model-selector label was privacy-masked. AI-assisted; verified with the tests and tools listed above.
Not shown:
Tests
vp test run src/server/adapter.test.ts src/server/skills.test.ts (packages/provider-cursor): 31 passed. Same adapter test with the fix stashed: fails with
expected '/grill-me My plan.' to include 'Ask one question at a time.'. vp run check:changed: format, lint, knip, typecheck (10 packages), tests (3 files, 33 tests) all pass.Not in this change
/nameon its own; on the first turn that/namestays inside the orchestration-instructions wrapper, which the report says may also hurt how reliably Cursor matches it.AI-assisted; verified with the tests and tools listed above.