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
Ensures commands execute from correct directories in monorepos by always using absolute paths. Triggers when working in repositories with multiple subprojects to prevent directory confusion and redundant cd commands.
/plugin marketplace add technicalpickles/pickled-claude-plugins/plugin install working-in-monorepos@technicalpickles-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
README.mdexamples/schemaflow.jsonexamples/zenpayroll.jsonscripts/monorepo-inittests/baseline-results.mdtests/baseline-scenarios.mdHelps 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."
Use this skill when:
Don't use for:
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.
If .monorepo.json exists at repo root:
root field for absolute repo pathpath from subprojects mapcd {root}/{path} && commandExample:
{
"root": "/Users/josh/workspace/schemaflow",
"subprojects": { "ruby": { "path": "ruby" } }
}
→ cd /Users/josh/workspace/schemaflow/ruby && bundle exec rspec
Use git to find repo root, then construct absolute path:
git rev-parse --show-toplevelcd /absolute/path/to/repo/ruby && bundle exec rspecExample 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.
When working in a repo without .monorepo.json:
git rev-parse --show-toplevel/Users/josh/workspace/schemaflowcd /Users/josh/workspace/schemaflow/subproject && commandDo 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.
When skill activates in a repo without .monorepo.json:
~/.claude/skills/working-in-monorepos/scripts/monorepo-init --dry-run, show output, ask for approval, then ~/.claude/skills/working-in-monorepos/scripts/monorepo-init --writeHelper Script Philosophy:
The monorepo-init script is designed as a black-box tool:
--help first to see usageScript 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
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:
commands maplocation: "root" | "subproject"command overrideoverridesExample:
bundle exec rspecbin/rspec❌ "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.
| 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. |
.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" }
}
}