[FEATURE] Support hierarchical `.claude/` directory discovery that continues past git repository boundaries, enabling language-scoped and org-scoped commands/skills/agents.
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
(Unsurprisingly, this feature request brought to you by claude)
In polyglot development structures, developers organize custom commands and skills by scope:
~/code/.claude/commands/ # shared by all projects
~/code/go/.claude/commands/ # Go-specific (gopls commands, test helpers, etc.)
~/code/py/.claude/commands/ # Python-specific
~/code/go/acme/.claude/commands/ # org-specific commands
~/code/go/acme/myservice/.claude/commands/ # repo-specific
Currently, Claude Code's .claude/ discovery stops at git repository boundaries, flattening this hierarchy. Once inside a git repo like myservice,
only that repo's .claude/ and ~/.claude/ are searched. Commands from ~/code/go/.claude/ and ~/code/.claude/ become inaccessible.
This breaks the intended design: developers want Go-specific commands available only in Go projects, not polluting their Python workspace's global namespace.
Proposed Solution
Extend parent directory traversal for .claude/ discovery to continue past git repository boundaries. Inherit commands, skills, and agents from ancestor
directories following this precedence (highest to lowest):
- Current repository's
.claude/ - Parent directories'
.claude/(walking up until$HOME) - Home directory's
~/.claude/
Use an explicit scope boundary marker (similar to ESLint's root: true) to stop traversal when desired. Example: a .claude/SCOPE_BOUNDARY file or scopeBoundary: true in .claude/settings.json that stops upward walking at that point.
Or, alternatively, be able to specify something at the git root that tells claude to keep traversing upward when it would normally stop (this would be backward compatible with current behavior).
Alternative Solutions
- Static symlink flattening script: Runs once per repo; breaks dynamically when new commands are added.
- SessionStart hook with auto-symlink: Adds clutter to
.gitignore, creates naming conflicts, still requires session restart to pick up mid-session additions. - Duplicate
.claude/in each repo: Massive maintenance burden, duplicated definitions. - Move everything to
~/.claude/: Pollutes global namespace; loses language/org scoping.
Priority
Medium - Would be very helpful
Feature Category
Configuration and settings
Use Case Example
I maintain projects across multiple languages organized under ~/code/:
~/code/
├── .claude/commands/review-loop.md # shared across all projects
├── go/
│ ├── .claude/commands/go-bench.md # Go benchmarking command
│ ├── .claude/skills/go-testing.md # Go test conventions
│ ├── acme/
│ │ ├── .claude/commands/deploy.md # org-specific deploy command
│ │ └── myservice/ # ← git repo
│ │ └── .claude/commands/lint.md # repo-specific
│ └── personal/
│ └── sidekick/ # ← git repo
│ └── .claude/commands/demo.md
└── py/
├── .claude/skills/pytest-style.md # Python test conventions
└── webapi/ # ← git repo
└── .claude/commands/migrate.md
Step 1: I run claude inside ~/code/go/acme/myservice/.
Step 2: I type /review-loop — a shared command defined at ~/code/.claude/commands/.
Step 3 (current behavior): Claude says the command doesn't exist. Discovery stopped at the myservice/ git root and only found lint.md. The shared
review-loop.md, Go-specific go-bench.md, and org-level deploy.md are all invisible.
Step 4 (desired behavior): Claude discovers commands from the full ancestor chain:
myservice/.claude/→lint.mdacme/.claude/→deploy.mdgo/.claude/→go-bench.mdcode/.claude/→review-loop.md~/.claude/→ (global commands)
All five scopes merge, with closer definitions winning on name conflicts.
Why this scoping matters: go-bench.md references go test -bench and Go-specific flags — it makes no sense inside ~/code/py/webapi/. The current workaround is either moving everything to ~/.claude/ (losing scoping entirely) or manually symlinking commands into every repo (static, breaks when new commands are added).
Additional Context
This mirrors how other tools handle configuration inheritance:
- ESLint: Walks up
eslintrc.jsonfiles until one hasroot: true - TypeScript: Walks up
tsconfig.jsonfiles; nested ones extend parent configs - Git: Walks up
.gitdirs (though it stops at boundary, not past it)
The key insight: a git boundary represents a project isolation boundary, not a configuration scope boundary. Monorepo structures routinely have intentional hierarchies that cross git boundaries.
Related Issues/Features
- Complements existing
CLAUDE.mdtraversal (which already walks past git boundaries) - Aligns with the principle that
.claude/custom extensions should be composable and inheritable like other config files
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗