8.2 KiB
name, description
| name | description |
|---|---|
| working-in-monorepos | Use when working in repositories with multiple subprojects (monorepos) where commands need to run from specific directories - prevents directory confusion, redundant cd commands, and ensures commands execute from correct locations |
Working in Monorepos
Overview
Helps Claude work effectively in monorepo environments by ensuring commands always execute from the correct location using absolute paths.
Core principle: Bash shell state is not guaranteed between commands. Always use absolute paths.
Announce at start: "I'm using the working-in-monorepos skill."
When to Use
Use this skill when:
- Repository contains multiple subprojects (ruby/, cli/, components/*, etc.)
- Commands must run from specific directories
- Working across multiple subprojects in one session
Don't use for:
- Single-project repositories
- Repositories where all commands run from root
The Iron Rule: Always Use Absolute Paths
When executing ANY command in a monorepo subproject:
✅ CORRECT:
cd /Users/josh/workspace/schemaflow/ruby && bundle exec rspec
cd /Users/josh/workspace/schemaflow/cli && npm test
❌ WRONG:
# Relative paths (assumes current directory)
cd ruby && bundle exec rspec
# No cd prefix (assumes location)
bundle exec rspec
# Chaining cd (compounds errors)
cd ruby && cd ruby && rspec
Why: You cannot rely on shell state. Absolute paths guarantee correct execution location regardless of where the shell currently is.
Constructing Absolute Paths
With .monorepo.json Config
If .monorepo.json exists at repo root:
- Read
rootfield for absolute repo path - Read subproject
pathfromsubprojectsmap - Construct:
cd {root}/{path} && command
Example:
{
"root": "/Users/josh/workspace/schemaflow",
"subprojects": { "ruby": { "path": "ruby" } }
}
→ cd /Users/josh/workspace/schemaflow/ruby && bundle exec rspec
Without Config
Use git to find repo root, then construct absolute path:
- First get the repo root:
git rev-parse --show-toplevel - Use that absolute path:
cd /absolute/path/to/repo/ruby && bundle exec rspec
Example workflow:
# Step 1: Get repo root
git rev-parse --show-toplevel
# Output: /Users/josh/workspace/schemaflow
# Step 2: Use absolute path in commands
cd /Users/josh/workspace/schemaflow/ruby && bundle exec rspec
Why not use command substitution: cd $(git rev-parse --show-toplevel)/ruby requires user approval. Instead, run git rev-parse once, then use the absolute path directly in all subsequent commands.
⚠️ Git subtree caveat: In repositories containing git subtrees (nested git repos), git rev-parse --show-toplevel returns the innermost repo root, not the monorepo root. This makes it unreliable for subtree scenarios. Creating a .monorepo.json config is the robust solution that works in all cases.
Workflow When Working Without Config
When working in a repo without .monorepo.json:
- Get repo root ONCE at start of session: Run
git rev-parse --show-toplevel - Store the result mentally: e.g.,
/Users/josh/workspace/schemaflow - Use absolute paths for ALL commands:
cd /Users/josh/workspace/schemaflow/subproject && command
Do NOT use command substitution like cd $(git rev-parse --show-toplevel)/subproject - this requires user approval every time. Get the path once, then use it directly.
Important limitation: git rev-parse --show-toplevel may not work correctly in repositories with git subtrees (nested git repos), as it returns the innermost repository root. For subtree scenarios, a .monorepo.json config is strongly recommended to explicitly define the true monorepo root.
Setup Workflow (No Config Present)
When skill activates in a repo without .monorepo.json:
- Detect: "I notice this appears to be a monorepo without a .monorepo.json config."
- Offer: "I can run ~/.claude/skills/working-in-monorepos/scripts/monorepo-init to auto-detect subprojects and generate config. Would you like me to?"
- User accepts: Run
~/.claude/skills/working-in-monorepos/scripts/monorepo-init --dry-run, show output, ask for approval, then~/.claude/skills/working-in-monorepos/scripts/monorepo-init --write - User declines: "No problem. I'll get the repo root once with git rev-parse and use absolute paths for all commands."
- User wants custom: "You can also create .monorepo.json manually. See example below."
Helper Script Philosophy:
The monorepo-init script is designed as a black-box tool:
- Always run with
--helpfirst to see usage - DO NOT read the script source unless absolutely necessary - it pollutes your context window
- The script exists to be called directly, not analyzed
- All necessary usage information is in the help output
Script Location:
The script is located at ~/.claude/skills/working-in-monorepos/scripts/monorepo-init (absolute path). Since skills are symlinked from the dotfiles repo via home/.claude/skills/ → ~/.claude/skills/, this path works universally regardless of which project directory you're currently in.
# Run from any directory - use the absolute path
~/.claude/skills/working-in-monorepos/scripts/monorepo-init --help
~/.claude/skills/working-in-monorepos/scripts/monorepo-init --dry-run
~/.claude/skills/working-in-monorepos/scripts/monorepo-init --write
Command Execution Rules (With Config)
If .monorepo.json defines command rules:
{
"commands": {
"rubocop": { "location": "root" },
"rspec": {
"location": "subproject",
"command": "bundle exec rspec",
"overrides": { "root": { "command": "bin/rspec" } }
}
}
}
Check rules before executing:
- Look up command in
commandsmap - Check
location: "root" | "subproject" - Check for
commandoverride - Check for context-specific
overrides
Example:
- rubocop: Always run from repo root
- rspec in ruby/: Use
bundle exec rspec - rspec in root project: Use
bin/rspec
Common Mistakes to Prevent
❌ "I just used cd, so I'm in the right directory" Reality: You cannot track shell state reliably. Always use absolute paths.
❌ "The shell remembers where I am" Reality: Shell state is not guaranteed between commands. Always use absolute paths.
❌ "It's wasteful to cd every time" Reality: Explicitness prevents bugs. Always use absolute paths.
❌ "Relative paths are simpler" Reality: They break when assumptions are wrong. Always use absolute paths.
Quick Reference
| Task | Command Pattern |
|---|---|
| Get repo root | git rev-parse --show-toplevel (run once, use result in all commands) |
| Run tests in subproject | cd /absolute/path/to/repo/subproject && test-command |
| With config | cd {root}/{subproject.path} && command |
| Check for config | test -f .monorepo.json |
| Generate config | ~/.claude/skills/working-in-monorepos/scripts/monorepo-init --dry-run (works from any directory) |
| Always rule | Use absolute path + cd prefix for EVERY command. Get repo root first, then use absolute paths directly. |
Configuration Schema
.monorepo.json at repository root:
{
"root": "/absolute/path/to/repo",
"subprojects": {
"subproject-id": {
"path": "relative/path",
"type": "ruby|node|go|python|rust|java",
"description": "Optional"
}
},
"commands": {
"command-name": {
"location": "root|subproject",
"command": "optional override",
"overrides": {
"context": { "command": "context-specific" }
}
}
}
}
Minimal example:
{
"root": "/Users/josh/workspace/schemaflow",
"subprojects": {
"ruby": { "path": "ruby", "type": "ruby" },
"cli": { "path": "cli", "type": "node" }
}
}