Added Claude Code hook to auto-install deps in fresh worktrees#29049
Added Claude Code hook to auto-install deps in fresh worktrees#290499larsons wants to merge 2 commits into
Conversation
no ref - fresh worktree checkouts have no node_modules or theme submodules, so `pnpm dev` fails until `pnpm run setup` has been run manually - a SessionStart hook now runs `pnpm run setup` automatically whenever a session starts in a ghost-monorepo checkout without node_modules, and no-ops everywhere else, so new Claude Code worktree sessions are ready to use immediately - Claude Code project hooks are approve-on-first-use, so this is opt-in per developer
|
Caution Review failedAn error occurred during the review process. Please try again later. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run @tryghost/admin:build |
✅ Succeeded | 10s | View ↗ |
nx run ghost:build:assets |
✅ Succeeded | 1s | View ↗ |
nx run-many -t lint -p ghost-monorepo |
✅ Succeeded | <1s | View ↗ |
nx run ghost:build:tsc |
✅ Succeeded | 5s | View ↗ |
nx run-many --target=build --projects=tag:publi... |
✅ Succeeded | 1s | View ↗ |
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗
☁️ Nx Cloud last updated this comment at 2026-07-02 15:51:16 UTC
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.claude/settings.json:
- Line 20: The setup guard in the `.claude/settings.json` command is using only
`node_modules` to decide whether to skip `pnpm run setup`, which can miss failed
submodule setup. Update the shell logic around the existing `cd`, `package.json`
check, and `pnpm run setup` flow to also verify submodule state before exiting
early, so a partially initialized checkout still reruns setup instead of
no-oping. Use the current command block as the place to add the additional
sentinel check alongside the existing `node_modules` condition.
- Line 20: The setup hook in the settings command continues even when `nvm use`
fails because the commands are chained with separators that don’t stop
execution. Update the shell flow in the hook so failure to switch Node versions
aborts before `pnpm run setup`, and keep the existing early-exit checks around
the `jq`, `package.json`, and `pnpm run setup` steps intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a301578a-36ec-4cb1-ac03-93a1f9a763c5
📒 Files selected for processing (1)
.claude/settings.json
| "hooks": [ | ||
| { | ||
| "type": "command", | ||
| "command": "jq -r '.cwd // empty' | { read -r d; cd \"$d\" 2>/dev/null || exit 0; [ \"$(jq -r '.name // empty' package.json 2>/dev/null)\" = \"ghost-monorepo\" ] || exit 0; [ -d node_modules ] && exit 0; export NVM_DIR=\"$HOME/.nvm\"; [ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\" >/dev/null 2>&1; nvm use >/dev/null 2>&1; command -v pnpm >/dev/null 2>&1 || corepack enable pnpm >/dev/null 2>&1; if pnpm run setup >\"$HOME/.claude/ghost-worktree-setup.log\" 2>&1; then echo \"Fresh Ghost checkout: pnpm run setup completed (deps + submodules installed).\"; else echo \"Fresh Ghost checkout: pnpm run setup FAILED - check ~/.claude/ghost-worktree-setup.log and run it manually before pnpm dev.\"; fi; }", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Don’t use node_modules as the only setup sentinel.
pnpm run setup can create node_modules before later setup steps fail, leaving missing submodules but causing future sessions to no-op silently. Check submodule state before skipping.
🐛 Proposed fix
- "command": "jq -r '.cwd // empty' | { read -r d; cd \"$d\" 2>/dev/null || exit 0; [ \"$(jq -r '.name // empty' package.json 2>/dev/null)\" = \"ghost-monorepo\" ] || exit 0; [ -d node_modules ] && exit 0; export NVM_DIR=\"$HOME/.nvm\"; [ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\" >/dev/null 2>&1; nvm use >/dev/null 2>&1; command -v pnpm >/dev/null 2>&1 || corepack enable pnpm >/dev/null 2>&1; if pnpm run setup >\"$HOME/.claude/ghost-worktree-setup.log\" 2>&1; then echo \"Fresh Ghost checkout: pnpm run setup completed (deps + submodules installed).\"; else echo \"Fresh Ghost checkout: pnpm run setup FAILED - check ~/.claude/ghost-worktree-setup.log and run it manually before pnpm dev.\"; fi; }",
+ "command": "jq -r '.cwd // empty' | { read -r d; cd \"$d\" 2>/dev/null || exit 0; [ \"$(jq -r '.name // empty' package.json 2>/dev/null)\" = \"ghost-monorepo\" ] || exit 0; if [ -d node_modules ] && ! git submodule status --recursive 2>/dev/null | grep -q '^[+-]'; then exit 0; fi; export NVM_DIR=\"$HOME/.nvm\"; [ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\" >/dev/null 2>&1; nvm use >/dev/null 2>&1; command -v pnpm >/dev/null 2>&1 || corepack enable pnpm >/dev/null 2>&1; if pnpm run setup >\"$HOME/.claude/ghost-worktree-setup.log\" 2>&1; then echo \"Fresh Ghost checkout: pnpm run setup completed (deps + submodules installed).\"; else echo \"Fresh Ghost checkout: pnpm run setup FAILED - check ~/.claude/ghost-worktree-setup.log and run it manually before pnpm dev.\"; fi; }",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "command": "jq -r '.cwd // empty' | { read -r d; cd \"$d\" 2>/dev/null || exit 0; [ \"$(jq -r '.name // empty' package.json 2>/dev/null)\" = \"ghost-monorepo\" ] || exit 0; [ -d node_modules ] && exit 0; export NVM_DIR=\"$HOME/.nvm\"; [ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\" >/dev/null 2>&1; nvm use >/dev/null 2>&1; command -v pnpm >/dev/null 2>&1 || corepack enable pnpm >/dev/null 2>&1; if pnpm run setup >\"$HOME/.claude/ghost-worktree-setup.log\" 2>&1; then echo \"Fresh Ghost checkout: pnpm run setup completed (deps + submodules installed).\"; else echo \"Fresh Ghost checkout: pnpm run setup FAILED - check ~/.claude/ghost-worktree-setup.log and run it manually before pnpm dev.\"; fi; }", | |
| "command": "jq -r '.cwd // empty' | { read -r d; cd \"$d\" 2>/dev/null || exit 0; [ \"$(jq -r '.name // empty' package.json 2>/dev/null)\" = \"ghost-monorepo\" ] || exit 0; if [ -d node_modules ] && ! git submodule status --recursive 2>/dev/null | grep -q '^[+-]'; then exit 0; fi; export NVM_DIR=\"$HOME/.nvm\"; [ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\" >/dev/null 2>&1; nvm use >/dev/null 2>&1; command -v pnpm >/dev/null 2>&1 || corepack enable pnpm >/dev/null 2>&1; if pnpm run setup >\"$HOME/.claude/ghost-worktree-setup.log\" 2>&1; then echo \"Fresh Ghost checkout: pnpm run setup completed (deps + submodules installed).\"; else echo \"Fresh Ghost checkout: pnpm run setup FAILED - check ~/.claude/ghost-worktree-setup.log and run it manually before pnpm dev.\"; fi; }", |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/settings.json at line 20, The setup guard in the
`.claude/settings.json` command is using only `node_modules` to decide whether
to skip `pnpm run setup`, which can miss failed submodule setup. Update the
shell logic around the existing `cd`, `package.json` check, and `pnpm run setup`
flow to also verify submodule state before exiting early, so a partially
initialized checkout still reruns setup instead of no-oping. Use the current
command block as the place to add the additional sentinel check alongside the
existing `node_modules` condition.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'File lines around the reported location:\n'
nl -ba .claude/settings.json | sed -n '1,80p'
printf '\nSearch for related setup commands:\n'
rg -n '"nvm use|pnpm run setup|ghost-worktree-setup|node_modules' .claude/settings.jsonRepository: TryGhost/Ghost
Length of output: 233
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import json
p = Path('.claude/settings.json')
text = p.read_text()
data = json.loads(text)
def walk(obj, path=''):
if isinstance(obj, dict):
for k, v in obj.items():
np = f'{path}.{k}' if path else k
if k == 'command':
print(f'COMMAND at {np}:')
print(v)
print()
walk(v, np)
elif isinstance(obj, list):
for i, v in enumerate(obj):
walk(v, f'{path}[{i}]')
walk(data)
PY
printf '\nShell behavior probe:\n'
bash -lc 'false >/dev/null 2>&1; echo after-false; true' 2>&1 | sed -n '1,5p'
printf '\nCommand-structure probe:\n'
python3 - <<'PY'
cmd = r'''export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" >/dev/null 2>&1; nvm use >/dev/null 2>&1; command -v pnpm >/dev/null 2>&1 || corepack enable pnpm >/dev/null 2>&1; if pnpm run setup >"$HOME/.claude/ghost-worktree-setup.log" 2>&1; then echo "Fresh Ghost checkout: pnpm run setup completed (deps + submodules installed)."; else echo "Fresh Ghost checkout: pnpm run setup FAILED - check ~/.claude/ghost-worktree-setup.log and run it manually before pnpm dev."; fi;'''
print(cmd)
PYRepository: TryGhost/Ghost
Length of output: 1774
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import json
p = Path('.claude/settings.json')
data = json.loads(p.read_text())
def find_commands(obj, path=''):
if isinstance(obj, dict):
for k, v in obj.items():
np = f'{path}.{k}' if path else k
if k == 'command':
print(f'PATH: {np}')
print(v)
print('---')
find_commands(v, np)
elif isinstance(obj, list):
for i, v in enumerate(obj):
find_commands(v, f'{path}[{i}]')
find_commands(data)
PY
python3 - <<'PY'
# Small semantics probe for a similar construct: failing command before a semicolon
# does not stop subsequent commands unless the shell is told to exit.
import subprocess, textwrap
script = 'false >/dev/null 2>&1; echo after-false; true'
out = subprocess.run(['bash', '-lc', script], capture_output=True, text=True)
print(out.stdout.strip())
PYRepository: TryGhost/Ghost
Length of output: 1130
Stop setup when nvm use fails. The current ; lets the hook keep going under the active Node version, so a missing .nvmrc runtime can send pnpm run setup through with the wrong environment.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/settings.json at line 20, The setup hook in the settings command
continues even when `nvm use` fails because the commands are chained with
separators that don’t stop execution. Update the shell flow in the hook so
failure to switch Node versions aborts before `pnpm run setup`, and keep the
existing early-exit checks around the `jq`, `package.json`, and `pnpm run setup`
steps intact.
no ref - `pnpm install` and theme submodule init are independent, so the setup hook now runs them in parallel: fresh-worktree bootstrap drops from ~25s to ~17s (bounded by pnpm install alone) - linked worktrees re-clone theme submodules from GitHub every time because each worktree gets its own submodule gitdir; submodules are now cloned with --reference/--dissociate from a sibling checkout that already has the pinned commit (local-disk fast, works offline), with a network fallback - moved the hook body out of the settings.json one-liner into .claude/hooks/worktree-setup.sh so it is reviewable, testable, and runnable by hand

no ref
Problem
A fresh git worktree checkout of the monorepo has no
node_modulesand no theme submodules, sopnpm dev(and most other commands) fail untilpnpm run setuphas been run manually. Anyone using Claude Code worktree sessions hits this on every new worktree.Solution
A
SessionStarthook in the shared.claude/settings.jsonthat no-ops unless the session cwd is aghost-monorepocheckout missingnode_modules, and otherwise runs.claude/hooks/worktree-setup.sh, which bootstraps the checkout as fast as possible:pnpm installand theme-submodule init run in parallel — they're independent, so wall time is bounded by the install alonegit submodule update --initre-clones the themes from GitHub on every new worktree; the script instead clones with--reference/--dissociatefrom a sibling checkout that already has the pinned commit (local-disk fast, works offline), with a plain network clone as fallback~/.claude/ghost-worktree-setup.logand injects a one-line success/failure status into the session context so the agent knows the state before doing any workMeasured on a warm pnpm store: serial
pnpm run setup≈ 25s (18s install + 6s network submodule clones); this script ≈ 17.5s.Notes
bash .claude/hooks/worktree-setup.sh