[FEATURE] Support .claude/skills.local/ for project-scoped, uncommitted skills

Status Closed โ€” not planned
Reported on v2.1.220
Maintainer reply None cached
Activity 1 comment ยท opened Jul 25, 2026 ยท closed Sep 17, 2026

Preflight

This has been requested three times before โ€” #41967 (4 ๐Ÿ‘), #45094, #31155. All three were closed by the stale bot (NOT_PLANNED / duplicate), none received a maintainer response, and #41967 is now locked so it cannot be commented on or reopened. Refiling per the bot's own instruction to open a new issue and reference the old one.

This issue adds two arguments the earlier ones did not make: the resident system-prompt cost of the ~/.claude/skills/ workaround, and the fact that every other config surface in Claude Code already has this exact tier.

Problem Statement

There is no way to have a skill that is both project-scoped and not committed.

The two discovery locations force a choice between them:

  • .claude/skills/<name>/SKILL.md โ€” project-scoped, but lands in the shared repo.
  • ~/.claude/skills/<name>/SKILL.md โ€” private, but loads in every project.

The second is not a workaround, it's a different feature. Every loaded skill keeps its description resident in the system prompt on every turn โ€” Claude Code's own /doctor-style guidance warns about exactly this ("loaded but never invoked. Each one adds to the system prompt every turn"). So parking a project-specific skill in the user directory taxes every unrelated project, forever, for a skill needed in one repo.

That leaves .gitignore, which has its own problems:

  • Hiding something personal requires a public commit. .gitignore is a shared, checked-in file. To keep a private skill out of the repo I have to push a line describing it to my teammates.
  • Naming conventions need team buy-in. .claude/skills/local-*/ only works if everyone agrees to the prefix.
  • The whitelist form has a silent footgun. .claude/skills/ + !.claude/skills/shared/ does nothing โ€” git never descends into an ignored directory, so the negation is a no-op. Write .claude/skills/* and it works. Get it wrong and the failure mode is "your private skill got committed", discovered after the push.
  • .git/info/exclude is genuinely private but does not survive a re-clone.

Proposed Solution

Discover skills from .claude/skills.local/<name>/SKILL.md, at every directory level where .claude/skills/ is already discovered (including nested/monorepo package dirs).

  • Precedence on name collision: skills.local/ > skills/ > ~/.claude/skills/. This mirrors the existing settings cascade, so it needs no new mental model.
  • Auto-gitignore on first write, exactly as .claude/settings.local.json already does today. The default should be safe without the user thinking about git at all.
  • Same treatment for .claude/commands.local/ and .claude/agents.local/.

Why this is a gap, not a preference

Claude Code already ships a "personal, not committed" tier for every other config surface. Skills, commands and agents are the only ones missing it:

| Surface | Project (committed) | Personal (not committed) |
|---|---|---|
| Instructions | CLAUDE.md | CLAUDE.local.md โœ… |
| Settings | .claude/settings.json | .claude/settings.local.json โœ… (auto-gitignored) |
| MCP servers | .mcp.json | ~/.claude.json โ†’ projects["<cwd>"] โœ… |
| Skills | .claude/skills/ | โŒ |
| Commands | .claude/commands/ | โŒ |
| Subagents | .claude/agents/ | โŒ |

The three missing rows are also the three most likely to contain something you cannot commit โ€” they hold procedures, hostnames and paths, not just flags.

The convention is already taught. Users who have learned CLAUDE.local.md and settings.local.json guess skills.local/ on their own โ€” that's visibly what happened in #41967 and #45094, filed independently a week apart.

Alternative Solutions

  • .claude/skills/ + .gitignore entry โ€” what I do today. Works, but requires a public edit to keep something private, has no safe default, and the skills/ vs skills/* negation gotcha fails silently in the direction of leaking the file.
  • local: true in SKILL.md frontmatter (proposed in #45094) โ€” solves discovery but not git: the file still sits in a tracked directory and still needs an ignore rule. A separate directory is what makes it ignorable in one line.
  • ~/.claude/skills/ for everything personal โ€” see above; the per-turn system-prompt cost applies in every repo, and there is no way to scope it.

Priority

Medium - Would be very helpful

Feature Category

Configuration and settings

Use Case Example

Contract work, two repos, one laptop.

  1. Client A's repo has a release procedure involving their internal Jenkins host, a Jira project key, and a staging hostname. It's a perfect skill: multi-step, easy to get wrong, needs to be written down once.
  2. I cannot commit it โ€” the repo has outside contributors, and the hostnames are not mine to publish.
  3. I cannot put it in ~/.claude/skills/ โ€” then its description rides in the system prompt while I'm working in Client B's repo, where it is noise at best and a confidentiality problem at worst if it ever surfaces in output.
  4. So it lives in .claude/skills/local-release/ plus a .gitignore line I had to push, which tells everyone the skill exists.
  5. With .claude/skills.local/release/SKILL.md, it loads only in that repo, is gitignored by default, and nobody else learns anything about it.

The same shape shows up outside contract work: machine-specific paths in a run-style skill, an experimental skill you don't want to impose on teammates' context window, or a personal review checklist in an OSS repo you don't maintain.

Additional Context

Prior art in this repo: #41967, #45094, #31155 โ€” all closed without triage by the stale bot, cumulative ๐Ÿ‘ across them suggests this isn't a single-user need.

Verified against Claude Code 2.1.220: there is no skills.local string anywhere in the binary, and no settings key for additional skill search paths, so there's no undocumented mechanism I'm missing.

View original on GitHub โ†—

This issue has 1 comment on GitHub. Read the full discussion on GitHub โ†—