Excessive Token Usage: Cursor auto-loads too many "Skills" from ~/.claude/skills at conversation start

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

When starting a new conversation or using Composer, Cursor automatically scans and loads an excessive number of “Skills” from the ~/.claude/skills/ directory. Even though it only reads the frontmatter rather than the full file content, the sheer volume of loaded skills consumes a significant amount of the initial context window (tokens).

Specifically, Cursor is recursively scanning hidden directories within the skills folder, loading skills meant for other Agent CLIs (like openclaw) that are not relevant to the current session.

Duplicated SKILLs will also be loaded. E.g. when you install GStack, same SKILLs from ~/.agent/skills and ~/.claude/skills will be loaded

Steps to Reproduce

  1. Have a directory structure in ~/.claude/skills/ that contains many subdirectories or hidden folders (e.g., .hermes/ or .opencode/).

  2. Start a new chat or Composer session in Cursor.

  3. Observe the initial token count or check the context being sent to the model.

  4. Notice that skills from paths like ~/.claude/skills/gstack/.hermes/skills/…/SKILL.md are being included.

Expected Behavior

Ignore hidden directories (starting with .) within the skills folder.

Ignore duplicated skills

Screenshots / Screen Recordings

Operating System

Linux

Version Information

Version 3.3.30

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

Hey, thanks for the detailed report and the screenshot. Confirmed: the recursive skills scanner doesn’t filter hidden directories inside ~/.claude/skills/, so anything in .hermes/, .opencode/, .gstack/, and similar subfolders from other agent CLIs gets pulled into context. I’ve reported this internally.

On dedup: normalization works for ~/.claude/skills vs ~/.cursor/skills, but GStack stores skills in ~/.agent/skills (no s at the end), and we only detect ~/.agents/skills. That’s why duplicates don’t collapse in your case. Flagged this too.

No fix yet. Workaround: turn off Include third-party Plugins, Skills, and other configs in Settings → Rules, Skills, Subagents. This will stop loading from the .claude/, .codex/, .agents/ roots entirely, including skills you might want, so if you need them you can temporarily copy specific folders into ~/.cursor/skills/.

There’s also a related thread Toggle (or allowlist) for Agent Skills roots — stop loading ~/.claude/skills and ~/.codex/skills when I only want .cursor / .agents about more granular control over skill roots. If you’re interested, please upvote it, it helps with prioritization. No ETA yet, we’ll post an update here when we have one.

thanks. is there a way to set this for cursor-agent as well? your response seems to be for the cursor gui and im not sure if that translates over to what cursor-agent pulls in.

I have a similar issue but more focused on ~/.cursor/skills-cursor/.

For me it’s not really about context size — it’s that agents spread their focus onto irrelevant built-ins like create-skill or create-rule even when the task has nothing to do with that. These are clearly commands, not auto-invocable workflows. I manually moved them to ~/.cursor/commands/ and that did the trick — but every Cursor update re-downloads anything missing from skills-cursor/, so the fix doesn’t survive. I’ve ended up writing a small script that purges the folder and marks it immutable with chflags uchg after each update.

What I’d love is a first-class option to disable built-ins — skills, subagents, commands, and (if you’re generous) tools. I’ve been using Cursor as my main tool for a while and have a clear sense of how I want the agents’ environment shaped; let me shape it.

@jubi - right now the toggle Include third-party Plugins, Skills, and other configs is an IDE-only setting. In ~/.cursor/cli-config.json docs Configuration | Cursor Docs there is no equivalent flag documented, it only has editor and permissions. Also, the cursor-agent CLI handles skills differently in general. There are a few parity gap reports, for example this one Cursor-agent CLI does not register skills from plugins (IDE does — parity gap) and this one Cursor CLI does not load ./claude/skills, so skills behavior in CLI vs IDE is not symmetric right now. I marked it internally as a gap. When we fix hidden-dirs filtering, we’ll also take a look at the CLI side.

@Demianight - this looks similar by symptoms, but it’s a different case overall. Built-in skills from ~/.cursor/skills-cursor/ are currently seeded automatically and re-populated on update, and that’s by design at this stage. What you want is a first-class option to disable built-ins skills/subagents/commands. That’s a reasonable feature request, but it’s broader than the hidden directories bug from the original thread. Can you open a separate thread for it? That’ll make it easier to track and prioritize separately, without mixing it with the fix for scanning .claude/skills. The chflags uchg workaround is valid for now, even if it’s a bit fragile.

There’s already a similar feature request you can upvote in the meantime: Toggle (or allowlist) for Agent Skills roots Toggle (or allowlist) for Agent Skills roots — stop loading ~/.claude/skills and ~/.codex/skills when I only want .cursor / .agents. It’s mostly about external roots, but it’s the same general direction.

Yes the topic you posted at the end there is my topic thanks in advance for that :slight_smile:

Composer 2.5 gave me the cursor-agent wrapper script below. I confirmed that it works on Linux. :smiley: @Demianight, it also filters out Cursor’s built-ins by default.

#!/usr/bin/env bash
# cursor-agent-skill-filter — run cursor-agent with third-party skills hidden
#
# Problem: cursor-agent auto-discovers skills from many compatibility roots
# (~/.claude/skills, ~/.codex/skills, ~/.claude/plugins, ~/.cursor/plugins,
# ~/.cursor/skills-cursor, ~/.agents/skills, plus project .claude/.codex trees).
# The IDE toggle "Include third-party Plugins, Skills, and other configs" does
# NOT apply to the CLI — there is no cli-config.json equivalent.
#
# Solution: re-exec inside a private mount namespace (unshare) and bind-mount an
# empty directory over those paths. Only user/project Cursor skills remain
# visible (~/.cursor/skills/, .cursor/skills/). skills-cursor is mounted
# read-only so Cursor's managed-skills sync cannot repopulate it at runtime.
# Claude Code / Codex sessions in other terminals are unaffected.
#
# Usage:
#   cursor-agent-skill-filter [cursor-agent args...]
#   cursor-agent-skill-filter --cursor-skills   # also show ~/.cursor/skills-cursor/
#   cursor-agent-skill-filter --all-skills      # no filtering (plain cursor-agent)
#
# Environment:
#   CURSOR_AGENT_ALL_SKILLS=1      same as --all-skills
#   CURSOR_AGENT_CURSOR_SKILLS=1   same as --cursor-skills
#   CURSOR_AGENT_SKILL_FILTER_DEBUG=1   print mount diagnostics to stderr
#
# Requirements: Linux, util-linux (unshare), user namespaces enabled
#
set -uo pipefail

all_skills=0
cursor_skills=0
passthrough=()
while [[ $# -gt 0 ]]; do
    case "$1" in
        --all-skills) all_skills=1; shift ;;
        --cursor-skills) cursor_skills=1; shift ;;
        *) passthrough+=("$1"); shift ;;
    esac
done
[[ "${CURSOR_AGENT_ALL_SKILLS:-}" == 1 ]] && all_skills=1
[[ "${CURSOR_AGENT_CURSOR_SKILLS:-}" == 1 ]] && cursor_skills=1

_ALL_HIDE_TARGETS=(
    "$HOME/.claude/skills"
    "$HOME/.codex/skills"
    "$HOME/.cursor/skills-cursor"
    "$HOME/.claude/plugins"
    "$HOME/.cursor/plugins"
    "$HOME/.agents/skills"
)

_debug() {
    [[ "${CURSOR_AGENT_SKILL_FILTER_DEBUG:-}" == 1 ]] || return 0
    echo "cursor-agent-skill-filter: $*" >&2
}

_cleanup_stale_mounts() {
    local target
    for target in "${_ALL_HIDE_TARGETS[@]}"; do
        while mountpoint -q "$target" 2>/dev/null; do
            _debug "cleaning stale mount on $target"
            umount "$target" 2>/dev/null || break
        done
    done
}

_skill_targets() {
    local targets=(
        "$HOME/.claude/skills"
        "$HOME/.codex/skills"
        "$HOME/.claude/plugins"
        "$HOME/.cursor/plugins"
        "$HOME/.agents/skills"
    )
    if [[ "$cursor_skills" -eq 0 ]]; then
        targets+=("$HOME/.cursor/skills-cursor")
    fi
    printf '%s\n' "${targets[@]}"
}

_empty_dir=""
_mounts=()

_teardown() {
    local target
    for target in "${_mounts[@]}"; do
        umount "$target" 2>/dev/null || true
    done
    if [[ -n "$_empty_dir" ]]; then
        rmdir "$_empty_dir" 2>/dev/null || true
    fi
}

_setup() {
    _empty_dir="$(mktemp -d)"
    local target
    while IFS= read -r target; do
        [[ -n "$target" ]] || continue
        mkdir -p "$target"
        if mount --bind "$_empty_dir" "$target"; then
            _mounts+=("$target")
            if [[ "$target" == "$HOME/.cursor/skills-cursor" ]]; then
                mount -o remount,bind,ro "$target" 2>/dev/null \
                    && _debug "hid (ro) $target" \
                    || _debug "hid $target (ro remount failed)"
            else
                _debug "hid $target"
            fi
        else
            echo "cursor-agent-skill-filter: warning: failed to hide $target" >&2
        fi
    done < <(_skill_targets)
}

if [[ "$all_skills" -eq 0 && "${CURSOR_AGENT_UNSHARED:-}" != 1 ]]; then
    _cleanup_stale_mounts
    reexec_args=()
    [[ "$cursor_skills" -eq 1 ]] && reexec_args+=(--cursor-skills)
    reexec_args+=("${passthrough[@]}")
    exec unshare --user --mount --map-root-user -- \
        env CURSOR_AGENT_UNSHARED=1 "$0" "${reexec_args[@]}"
fi

if [[ "$all_skills" -eq 0 ]]; then
    trap _teardown EXIT INT TERM
    _setup
    _debug "skill isolation active (cursor_skills=$cursor_skills)"
fi

exec cursor-agent "${passthrough[@]}"

Hey, thanks for sharing this, @zar42stra. Nice approach. Using a user namespace for isolation plus a bind-mount over the skill routes looks like a workable solution for users on the cursor-agent CLI who need to filter out third-party skills while there’s no native toggle there yet.

A couple of caveats for anyone trying it. This is Linux-only. You’ll need util-linux for unshare. And if you want to run it without sudo, you’ll need unprivileged user namespaces enabled. On some distros, like Debian, they’re disabled by default. Also, this is an unofficial workaround, so please test it in your own environment before relying on it for work.

On the hidden directories bug, status is unchanged. The issue is being tracked, there’s no fix yet, and I can’t share an ETA. I’ll post here as soon as there’s an update.

Hey @azhang, quick update: the fix for excessive skill loading from hidden directories inside ~/.claude/skills/ and for duplicate skill loading landed in Cursor 3.17. Please update to 3.17 or newer and give it another try.

If anything from hidden folders still ends up in the initial context, or duplicates don’t collapse, for example your GStack ~/.agent/skills vs ~/.claude/skills case, drop a screenshot of the context and we’ll take another look.

Separately, the request to disable built-in skills from ~/.cursor/skills-cursor/ now lives in its own thread: First-class option to disable built-in skills from ~/.cursor/skills-cursor/

cc @jubi @zar42stra