Skip to content

Cursor skill discovery drops all workspace skills when any user-level skills folder exceeds the scan depth limit #17215

Open

Description

@ethan-lipseys

What happened

When using models through the Cursor provider, neither / slash commands nor $ skill mentions show any of the skills in my project. The project has 28 skills under .agents/skills and .claude/skills, and all of them have valid SKILL.md files.

Diagnosis

probeCursorSkills scans eight roots: .cursor/skills, .agents/skills, .codex/skills and .claude/skills, first under the workspace and then under the user's home. All eight share one CursorSkillScanBudget (10,000 entries, MAX_SKILL_DEPTH = 10). When any root contains a directory that goes past the depth limit, budget.exhausted is set (apps/server/src/provider/Drivers/CursorSkills.ts:207-210). probeCursorSkills then fails the whole probe with CursorSkillsProbeError(scan-budget-exhausted) (CursorSkills.ts:268-273), even though the project roots were scanned completely first. CursorDriver.snapshotForCwd (CursorDriver.ts:235-247) turns this into a ProviderDriverError, and the registry logs "provider registry refresh failed; preserving cached providers". As a result, the composer never gets the workspace skills.

On this machine the deep tree is not a skill. It is leftover staging data from Claude's skill sync at ~/.claude/skills/synced/<id>/.staging/<pid>/pptx/pptx/scripts/office/schemas/ecma/fouth-edition, 11 levels below ~/.claude/skills. The two staging folders date from 6 days earlier, and their PIDs are no longer running. Without that folder, the deepest path under ~/.claude/skills is 8 levels, and Cursor skill discovery works.

Two behaviors make this worse:

  1. All-or-nothing failure. One over-deep or oversized directory in an unrelated user-level root discards every skill, including project skills that were already found. Returning the skills found so far (or degrading per root) would be more robust.
  2. Dot-directories are walked. .staging, .trash, .git, node_modules and similar are recursed into like any other folder. Claude's skill sync writes .staging/ and .trash/ inside ~/.claude/skills, so any user who has Claude skill sync and a deep skill package (pptx, docx or xlsx ship scripts/office/schemas/...) can hit this. Skipping dot-directories, or not recursing below a directory that already contains a SKILL.md, would avoid it.

CursorSkills.ts on main is identical to v0.0.45, so this is not fixed upstream yet.

Steps to reproduce

  1. Use the Cursor provider on any workspace that has skills in <workspace>/.agents/skills/<name>/SKILL.md.
  2. Create a directory chain 11 or more levels deep under ~/.claude/skills, for example mkdir -p ~/.claude/skills/x/.staging/1/a/b/c/d/e/f/g/h/i.
  3. Open a Cursor thread in the workspace and type $ or / in the composer.
  4. No workspace skills are listed. server.trace.ndjson shows CursorSkillsProbeError ... (scan-budget-exhausted).
  5. Remove the deep directory, then restart or refresh providers. The workspace skills appear.

Version

0.0.45 (v0.0.45, 6c8fed3)

Environment

Windows 11 Pro 10.0.26200 x64, Node v26.8.2, T3 Code desktop app with a local server, cursor-agent 2026.09.23-86fc751

Evidence

{"type":"effect-span","name":"probeCursorSkills",...,"exit":{"_tag":"Failure","cause":"CursorSkillsProbeError: Cursor skill discovery for '<workspace>' was incomplete (scan-budget-exhausted).\n    at probeCursorSkills (.../server.asar/apps/server/dist/bin.mjs:157608:79)\n    at refreshWorkspaceSnapshot (.../bin.mjs:218988:40)\n    at ws.rpc.server.refreshProviders (.../bin.mjs:100908:104)"}}
{"type":"effect-span","name":"refreshWorkspaceSnapshot",...,"exit":{"_tag":"Failure","cause":"ProviderDriverError: Provider driver 'cursor' failed to create instance 'cursor': Failed to discover Cursor skills for '<workspace>' ... [cause]: CursorSkillsProbeError ..."}}
{"type":"effect-span","name":"recoverRefreshFailure",...,"events":[{"name":"[\"provider registry refresh failed; preserving cached providers\", ...]"}]}

# Per-root scan (entries / max directory depth below the root):
<workspace>/.agents/skills      137 entries, depth 2
<workspace>/.claude/skills      137 entries, depth 2
~/.cursor/skills                  2 entries, depth 1
~/.agents/skills                  2 entries, depth 1
~/.codex/skills                  71 entries, depth 3
~/.claude/skills                557 entries, depth 11   <- synced/<id>/.staging/<pid>/pptx/pptx/scripts/office/schemas/ecma/fouth-edition

Related issues

#16866: failed workspace discovery is cached and not retried. That is related (it is why the missing skills persist), but it covers retry and caching. This issue is about why the probe fails in the first place: an unrelated over-deep user-level directory aborts all Cursor skill discovery. #2736 (closed) had a similar symptom ("Cursor/OpenCode skill discovery returns zero skills") but predates the scan budget.

Fix applied or workaround

Moved the stale ~/.claude/skills/synced/<id>/.staging folder out of the skills tree, then restarted the T3 Code desktop app. The problem will come back whenever Claude's skill sync leaves another stale .staging folder.

Filed by

Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage

Activity

  1. juliusmarminge commented on Oct 8, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Thanks for the detailed write-up. Reading the code on main (12069eefd), the diagnosis looks right.

    What the code does

    • All eight roots share one CursorSkillScanBudget: project .cursor/.agents/.codex/.claude skills first, then the same four under home (CursorSkills.ts:225-246). Limits are MAX_SKILL_DEPTH = 10 and 10,000 entries (:26-28).
    • When a directory at depth 10 has a subdirectory, the walk sets budget.exhausted = true and stops (:207-210). Running out of entries does the same (:199-202). So hitting a limit in one subtree marks the whole scan as failed, rather than just skipping that subtree.
    • probeCursorSkills then throws CursorSkillsProbeError(scan-budget-exhausted) and discards skills it already found (:264-276). CursorDriver.snapshotForCwd turns that into a ProviderDriverError (CursorDriver.ts:267-285). With no workspace snapshot, the composer appears to get no skills.
    • The walk has no dot-directory or node_modules filter (:198-211). It also keeps going below a directory that already has a SKILL.md (:163-191). That would explain why a stale ~/.claude/skills/synced/<id>/.staging/... tree can reach the depth limit.

    Why it fails hard
    Failing the probe appears to be deliberate. #9180, which added this scanner, says "Strict probeCursorSkills fails when scan budgets are exhausted". The same pattern in AntigravitySkills.ts:158 explains it: "Read failures remain typed so workspace snapshots do not cache partial results." That reasoning works for a transient read error. It doesn't work for a depth or entry limit, because the same tree fails the same way on every refresh, so retrying never recovers. A filesystem error in any root (budget.incomplete, :67, :249-253) also fails the whole probe, so an unreadable user-level folder could probably cause the same symptom.

    The other providers don't walk this deep. Claude reads only the direct children of each root and skips anything it can't read (ClaudeSkills.ts:315-347). Antigravity stops at the first SKILL.md and goes at most one level down (AntigravitySkills.ts:206-229). Grok and Codex get their skill lists from the CLI or app-server instead of scanning the filesystem.

    Possible fix (not yet verified against Cursor's own loader)

    1. When a subtree hits the depth limit, skip it instead of setting exhausted. Keep the entry budget as a real hard stop.
    2. Skip dot-directories and node_modules. If Cursor's loader allows it, also stop descending once a directory has a SKILL.md.
    3. Optionally, give each root its own budget, or return partial results with a flag, so a bad user-level root can't hide project skills.

    Related

    Workaround until then: move stale .staging or .trash folders out of ~/.claude/skills, as you did, then refresh providers.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 8, 2026
  3. m4tta commented on Oct 9, 2026

    @m4tta

    Same all-or-nothing failure here, but from the 10,000 entry budget, not the depth limit. #17389 doesn't cover this. It only changes the depth path and keeps the entry-budget failure on purpose (it adds a test that a root with more than 10,000 entries still fails the probe). So this case stays broken after that PR merges.

    What happened

    In one workspace, Cursor threads only showed /model and /usage-limits in the composer. Other workspaces were fine, so it looked intermittent.

    Cause

    A project skill writes its run output into its own folder, <workspace>/.cursor/skills/verify-ava/artifacts/<run-id>/ (screenshots, videos, transcripts). After a month that folder had 301 runs and 10,651 entries. Max depth was 8, so the depth limit wasn't involved.

    Project roots are scanned first. The budget ran out inside <workspace>/.cursor/skills (remainingEntries === 0, CursorSkills.ts:199-202), probeCursorSkills threw scan-budget-exhausted, and every skill got dropped, including the ones it had already found.

    Also, ~/.agents/skills is a symlink to ~/.claude/skills on this machine. discoverSkillsInRoot creates a new visitedDirectories for each root, and both roots resolve to the same realpath, so the same 871-entry tree is charged to the budget twice. Skills get deduped by name, but the budget doesn't.

    Per-root scan before the workaround

    <workspace>/.cursor/skills   10,911 entries (10,651 in verify-ava/artifacts), depth 8
    <workspace>/.claude/skills        8 entries
    ~/.cursor/skills                  1 entry
    ~/.agents/skills                871 entries (symlink to ~/.claude/skills)
    ~/.codex/skills                  79 entries
    ~/.claude/skills                871 entries
    

    Trace

    {"name":"probeCursorSkills",...,"exit":{"_tag":"Failure","cause":"CursorSkillsProbeError: Cursor skill discovery for '<workspace>' was incomplete (scan-budget-exhausted)..."}}
    {"name":"refreshWorkspaceSnapshot",...,"cause":"ProviderDriverError: Provider driver 'cursor' failed to create instance 'cursor': Failed to discover Cursor skills for '<workspace>'..."}
    {"name":"recoverRefreshFailure",...,"provider registry refresh failed; preserving cached providers"}
    

    Repro

    1. Workspace with any skill at .cursor/skills/<name>/SKILL.md.
    2. mkdir -p .cursor/skills/<name>/artifacts && for i in $(seq 1 11000); do touch .cursor/skills/<name>/artifacts/f$i; done
    3. Open a Cursor thread in that workspace and type /. No skills, and the trace shows scan-budget-exhausted.

    Suggestions beyond the triage comment

    • Don't fail the probe on entry exhaustion either. Return what was found, or give each root its own budget, so one big project skill can't hide everything else.
    • Stop recursing once a directory has a SKILL.md, if Cursor's loader allows it. Then node_modules, build output or run artifacts next to a SKILL.md wouldn't count.
    • Share visitedDirectories (or a set of root realpaths) across roots so a symlinked root is only charged once.

    Workaround

    Deleted the old run folders under artifacts/ and refreshed providers.

    Environment

    T3 Code 0.0.45 (v0.0.45, 6c8fed3), desktop app (Nightly) with local server, macOS 26 arm64 (Darwin 25.5.0), Node v26.8.2.


    Posted via t3 triage with Claude Code (Claude Opus 5.5, claude-opus-5-5).

  4. ScottN-PV commented on Oct 11, 2026

    @ScottN-PV
    Contributor

    Thanks for the detailed trace. I ran both cases with probeCursorSkills on a real filesystem (Linux, Node 24), using main's scanner (unchanged from b9afba5 through bf725cb) and #17389's head (eedd33d). Both behave the same:

    • A project skill with 11,000 files in a subfolder beside its SKILL.md: scan-budget-exhausted, no skills returned.
    • ~/.agents/skills symlinked to a ~/.claude/skills tree with 6,000 files: scan-budget-exhausted. The same tree without the symlink passes, so the budget charges it twice.

    #17389 only stops the depth limit from failing the whole scan. It leaves both of these as they are, and if it merges as is, it closes this issue. I can open a separate small PR for them.

    For the symlink case I'd scan each real root once, keeping the current root order. The entry-budget case needs a maintainer decision, as in option 3 of the triage comment: keep failing hard, return the skills found so far with an incomplete flag, or give each root its own budget. Partial results alone keep the skills already found but still skip every later root, and the probe fails hard today so snapshots never cache a partial list. I'd give each root its own budget and skip only a root that runs out, with a log line, so one large skill folder can't hide the others. Stopping the walk at a SKILL.md would also help, but I don't know whether Cursor's loader allows nested skills. Limits: Linux only, the probe called directly rather than through the composer, and none of these remedies tested yet.

    Maintainers, which option do you want?

    Written by Claude Fable 5.1 and Claude Opus 5.5 on behalf of ScottN-PV.

  5. Culpable commented on Oct 15, 2026

    @Culpable

    The entry-budget case still reproduces after the depth-limit fix in #17389.

    Environment:

    • T3 Code 0.0.46-nightly.20261014.3132
    • Cursor SDK 1.0.35
    • macOS

    A user-installed skill has test dependencies under: ~/.agents/skills/<skill>/tests/node_modules/

    With Cursor selected, the $ picker offers no skills. The server repeatedly logs:

    CursorSkillsProbeError: Cursor skill discovery for '' was incomplete (scan-budget-exhausted).

    A read-only replay of the installed scanner’s directory walk reaches the shared 10,000-entry limit inside that node_modules tree. That single skill package accounts for approximately 8,849 entries before discovery stops. No filesystem errors occur.

    The installed scanner includes #17389’s depth-limit change, but entry exhaustion still fails the entire probe and discards skills already discovered.

    Proposed fix:

    1. Skip node_modules when discovering skills.
    2. Stop descending beneath a directory containing SKILL.md, provided this matches Cursor’s supported nested-skill behaviour.
    3. If nested skills must remain discoverable, restrict traversal to their supported locations instead of walking every supporting directory.
    4. Preserve discovered skills when a limit is reached, with an explicit incomplete-discovery indication.

    The composer needs skill metadata from SKILL.md. Installed test dependencies, supporting assets and run outputs must not make unrelated skills disappear.

    Could this remaining case be tracked separately or the issue reopened? The depth fix does not resolve it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions