From ba29055559363a99ba7c12b8a6043c826d7b64b7 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 12 Jun 2026 10:30:21 +0200 Subject: [PATCH 1/2] chore: auto-set-up new worktrees with env copy, install, and build Translate the Cursor .cursor/worktrees.json setup into Claude Code worktrees: - .worktreeinclude (repo root): copies gitignored .env / .env.local into new worktrees at creation time, using .gitignore glob syntax. - .claude/settings.json: registers a SessionStart hook (startup/resume). - .claude/hooks/setup-worktree.sh: runs 'pnpm install' + 'pnpm run build' the first time a session opens in a fresh linked worktree (guarded on missing node_modules), and is a silent no-op in the primary checkout and in already-set-up worktrees. Co-authored-by: Isaac Signed-off-by: MarioCadenas --- .claude/hooks/setup-worktree.sh | 51 +++++++++++++++++++++++++++++++++ .claude/settings.json | 16 +++++++++++ .worktreeinclude | 8 ++++++ 3 files changed, 75 insertions(+) create mode 100755 .claude/hooks/setup-worktree.sh create mode 100644 .claude/settings.json create mode 100644 .worktreeinclude diff --git a/.claude/hooks/setup-worktree.sh b/.claude/hooks/setup-worktree.sh new file mode 100755 index 00000000..0f9ead49 --- /dev/null +++ b/.claude/hooks/setup-worktree.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# +# SessionStart hook: prepare a fresh git worktree so it is ready to run. +# +# Runs `pnpm install` + `pnpm run build` the first time a Claude Code session +# opens inside a linked worktree that has no node_modules yet. It is a no-op in +# the primary checkout and in worktrees that are already set up, so it is safe +# to run on every session start. +# +# The .env files are copied separately by .worktreeinclude (repo root) at +# worktree-creation time; this hook only handles dependency install + build, +# which .worktreeinclude cannot do. +# +# Registered in .claude/settings.json under hooks.SessionStart. + +# Anchor to the worktree this session is running in. +toplevel=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0 +common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || exit 0 + +# The parent of the shared git common dir is the primary worktree. Normalize to +# an absolute path so the comparison works whether common_dir is "." (primary) +# or an absolute path (linked worktree). +main_worktree=$(cd "$(dirname "$common_dir")" 2>/dev/null && pwd) || exit 0 + +# Only act inside a linked worktree, never the primary checkout. +[ "$toplevel" = "$main_worktree" ] && exit 0 + +# Already set up — nothing to do (keeps this cheap on resume/clear/compact). +[ -d "$toplevel/node_modules" ] && exit 0 + +echo "setup-worktree: fresh worktree at $toplevel — running pnpm install + build…" >&2 +cd "$toplevel" || exit 0 + +# Ensure pnpm is available (the repo manages it via corepack). +if ! command -v pnpm >/dev/null 2>&1; then + corepack enable pnpm >/dev/null 2>&1 || true +fi + +# Best-effort: report failures on stderr but never block session startup. If a +# step fails, re-run `pnpm install && pnpm run build` manually in the worktree. +if ! pnpm install >&2; then + echo "setup-worktree: pnpm install failed — run it manually in $toplevel" >&2 + exit 0 +fi +if ! pnpm run build >&2; then + echo "setup-worktree: pnpm run build failed — run it manually in $toplevel" >&2 + exit 0 +fi + +echo "setup-worktree: worktree ready." >&2 +exit 0 diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..298a5aec --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,16 @@ +{ + "hooks": { + "SessionStart": [ + { + "matcher": "startup|resume", + "hooks": [ + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/setup-worktree.sh\"", + "timeout": 600 + } + ] + } + ] + } +} diff --git a/.worktreeinclude b/.worktreeinclude new file mode 100644 index 00000000..ce9b0983 --- /dev/null +++ b/.worktreeinclude @@ -0,0 +1,8 @@ +# Files copied into new Claude Code worktrees (--worktree / isolation: "worktree"). +# Uses .gitignore glob syntax. Only files that match AND are gitignored are +# copied, so tracked templates like .env.dist / .env.tmpl are left untouched. +# +# A bare pattern with no slash matches at any depth, so `.env` picks up +# apps/dev-playground/.env, apps/clean-app/.env, etc. +.env +.env.local From aac6d1a793106093fb0fc71811adea4fc7c24192 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 12 Jun 2026 12:56:30 +0200 Subject: [PATCH 2/2] chore: list explicit env paths in .worktreeinclude Mirror .cursor/worktrees.json by naming the exact files to copy (apps/dev-playground/.env, apps/clean-app/.env) instead of a bare `.env` glob, so worktree creation copies precisely the dev env files and nothing picked up at unexpected depths. Signed-off-by: MarioCadenas --- .worktreeinclude | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.worktreeinclude b/.worktreeinclude index ce9b0983..e12ef498 100644 --- a/.worktreeinclude +++ b/.worktreeinclude @@ -2,7 +2,8 @@ # Uses .gitignore glob syntax. Only files that match AND are gitignored are # copied, so tracked templates like .env.dist / .env.tmpl are left untouched. # -# A bare pattern with no slash matches at any depth, so `.env` picks up -# apps/dev-playground/.env, apps/clean-app/.env, etc. -.env -.env.local +# Listed as explicit, anchored paths (mirroring .cursor/worktrees.json) rather +# than a bare `.env` glob, so exactly the dev env files are copied and nothing +# else is picked up at unexpected depths. +apps/dev-playground/.env +apps/clean-app/.env