Files
gh-technicalpickles-pickled…/skills/working-in-monorepos/SKILL.md
2025-11-30 09:00:36 +08:00

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:

  1. Read root field for absolute repo path
  2. Read subproject path from subprojects map
  3. 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:

  1. First get the repo root: git rev-parse --show-toplevel
  2. 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:

  1. Get repo root ONCE at start of session: Run git rev-parse --show-toplevel
  2. Store the result mentally: e.g., /Users/josh/workspace/schemaflow
  3. 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:

  1. Detect: "I notice this appears to be a monorepo without a .monorepo.json config."
  2. Offer: "I can run ~/.claude/skills/working-in-monorepos/scripts/monorepo-init to auto-detect subprojects and generate config. Would you like me to?"
  3. 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
  4. User declines: "No problem. I'll get the repo root once with git rev-parse and use absolute paths for all commands."
  5. 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 --help first 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:

  1. Look up command in commands map
  2. Check location: "root" | "subproject"
  3. Check for command override
  4. 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" }
  }
}