Skip to content

Commit c75c39d

Browse files
github-actions[bot]Marfuenclaude
authored
chore(worktrees): auto-link .env files on git worktree add
* chore(worktrees): auto-link .env files on git worktree add Add a shared .githooks/ directory with a post-checkout hook that symlinks every .env* from the main worktree into each freshly created worktree. Scoped via two layered checks — prev-HEAD is the null SHA AND the current worktree isn't the main one — so it fires exclusively inside `git worktree add`, never on regular branch/file checkouts or fresh clones. Enable with a one-time `git config core.hooksPath .githooks` per clone. Since all worktrees share a single .git/, the config persists for all current and future worktrees automatically. The hook delegates to scripts/link-worktree-envs.sh so the same logic backfills worktrees that existed before the hook was installed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(worktrees): run bun install + db:generate + build on new worktree After the post-checkout hook symlinks .env files into a freshly created worktree, it now also invokes scripts/setup-worktree.sh which runs `bun install`, `bun run db:generate`, and `bun run build` so the worktree is usable immediately. Runs synchronously on purpose — callers (especially Claude Code) tend to start executing in the worktree right after creation, and we don't want them racing ahead of the install. Skippable via `SKIP_WORKTREE_SETUP=1 git worktree add …` for a fast "just give me the files" worktree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(worktrees): make bun run build opt-in during auto-setup `bun run build` adds several minutes per worktree and is unnecessary for the common path (dev server, tests, typecheck). Default the setup script to install + db:generate only, and gate the build step behind SETUP_WORKTREE_WITH_BUILD=1 for the rare case it's needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(worktrees): run prisma migrate before db:generate in setup After `bun install` and before `bun run db:generate`, run `cd packages/db && bun run db:migrate` so any new migrations on the branch are applied to the local DB before the clients are regenerated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(worktrees): isolate DB per worktree to prevent cross-branch drift Add scripts/setup-worktree-db.sh that creates compdev_<slug> for each new worktree using the same Postgres host/credentials as main, and have scripts/link-worktree-envs.sh copy-and-rewrite .env files containing DATABASE_URL so each worktree's migrations hit its own DB. Env files without DATABASE_URL stay as symlinks so API keys and such still auto-propagate. Uses the `pg` node module from the main worktree's node_modules (via a small scripts/create-database.mjs helper) so psql/libpq isn't required on the host. Skip via SKIP_WORKTREE_DB=1 git worktree add … to opt back into the shared DB (the previous behavior). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(skills): add stale-worktree-cleanup skill Defines a safe process for reaping old worktrees together with their isolated compdev_* databases. Since git has no pre-worktree-remove hook, dead databases accumulate silently — this skill gives Claude (or a human) a classify-then-confirm-then-remove workflow so nothing in-flight gets lost. Includes inventory via `gh pr list`, per-worktree dirty/unpushed checks, and safety rules against --force without consent, dropping non-compdev_* databases, or touching the main worktree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(worktrees): add dev:no-trigger scripts + single-active-worktree rule Trigger.dev's `trigger dev` CLI has no per-branch or per-session isolation — env is hardcoded to the project's shared "dev" (verified in node_modules/trigger.dev/dist/esm/commands/dev.js). Running `bun run dev` in multiple worktrees causes last-writer-wins task registration and zombie workers. Add dev:no-trigger scripts to apps/app and apps/api (UI-only, no trigger dev) so only one active worktree runs the full stack at a time. Document the rule in .githooks/README.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(skills): add new-feature-setup skill documenting the worktree-based workflow When starting any new feature/ticket/branch, this skill points to the existing hook-driven auto-setup (compdev_<slug> DB, env linking, install+migrate+generate) and the single-active-trigger-dev rule so new Claude sessions don't reinvent env copying, database bootstrapping, or fight `bun install` by hand in every worktree. Companion to stale-worktree-cleanup for the other end of the lifecycle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Mariano <marfuen98@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 840374b commit c75c39d

10 files changed

Lines changed: 703 additions & 0 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
name: new-feature-setup
3+
description: Use when starting a new feature, Linear ticket, or bugfix in this repo — establishes the branch + worktree + env + DB + dev-server conventions so the work is immediately ready to code without fighting infra. Triggers on "start a new feature", "spin up a worktree", "begin ticket", "new branch".
4+
---
5+
6+
# New Feature Setup (comp monorepo)
7+
8+
## Overview
9+
10+
This repo has a lot of infrastructure pre-wired into `git worktree add`. Use it. Don't reinvent env copying, database setup, or dependency install flows in every new session.
11+
12+
## When to Use
13+
14+
- User says "start a new feature", "spin up a worktree for X", "begin this Linear ticket", "new branch for …"
15+
- Before running `bun install` / `bun run db:generate` manually in a new directory
16+
- Before copying `.env` files around by hand
17+
18+
## When NOT to Use
19+
20+
- User is editing an existing worktree (already set up)
21+
- Infra repair / debugging of the hook itself (read `.githooks/README.md` instead)
22+
23+
## Workflow
24+
25+
### 1. Create the worktree from `origin/main`
26+
27+
```sh
28+
cd /Users/mariano/code/comp # must be the MAIN clone, not another worktree
29+
git fetch origin main
30+
git worktree add .worktrees/<short-slug> -b <branch-name> origin/main
31+
```
32+
33+
- For Linear tickets, use Linear's suggested branch name (`mariano/<ticket-slug>`).
34+
- For infra / chore work, use `mariano/<short-descriptive-name>` or `chore/<topic>`.
35+
- The `<short-slug>` on the worktree path should match the branch's suffix (it becomes the Postgres DB slug after `tr '-' '_'`).
36+
37+
### 2. Let the hook do everything else
38+
39+
`git worktree add` fires the `post-checkout` hook at `.githooks/post-checkout`, which runs synchronously:
40+
41+
1. Creates `compdev_<slug>` Postgres database (isolated per worktree)
42+
2. Links `.env*` files from the main clone (copies the ones with `DATABASE_URL`, rewriting it to the isolated URL; symlinks the rest so API keys auto-propagate)
43+
3. Runs `bun install`, applies Prisma migrations, regenerates clients
44+
45+
**Do not** run any of these by hand. If the hook logs a failure, diagnose and fix at the source — don't paper over with a manual install.
46+
47+
Skip toggles (rare):
48+
- `SKIP_WORKTREE_DB=1` — share the main `comp` DB (drift risk; only for read-only worktrees)
49+
- `SKIP_WORKTREE_SETUP=1` — skip install + migrate + generate (for a "just files" worktree)
50+
- `SETUP_WORKTREE_WITH_BUILD=1` — also run `bun run build` (adds minutes; only when you need the built artifacts)
51+
52+
### 3. Start the dev server — coordinate with the "active worktree" rule
53+
54+
Trigger.dev's `trigger dev` CLI **cannot** be isolated per worktree. Running `bun run dev` in multiple worktrees stomps on task registration.
55+
56+
- **One active worktree** runs `bun run dev` (full stack with `trigger dev`).
57+
- **All other worktrees** run:
58+
```sh
59+
bun run --filter '@trycompai/app' dev:no-trigger # Next.js only
60+
bun run --filter '@trycompai/api' dev:no-trigger # NestJS only
61+
```
62+
- Non-active worktrees need a different `PORT` to avoid collision — add `PORT=3001` (or `3334`, etc.) to the worktree's `.env.local`. `.env.local` is not symlinked and stays per-worktree.
63+
- When swapping which worktree is active, kill the old full `bun run dev` first so task registration is clean.
64+
65+
### 4. Code the feature
66+
67+
Standard repo conventions apply (see `CLAUDE.md`). Highlights:
68+
- TDD for any non-trivial change (`superpowers:test-driven-development`)
69+
- Brainstorm before building new UX (`superpowers:brainstorming`)
70+
- Plans + subagent-driven execution for multi-step work
71+
- Run `audit-design-system` after any frontend component edit
72+
- Always run typecheck before declaring a change done (`npx turbo run typecheck --filter=<pkg>`)
73+
74+
### 5. When done, clean up
75+
76+
Use the `stale-worktree-cleanup` skill when worktrees accumulate. It handles both `git worktree remove` and dropping the `compdev_<slug>` database in one pass. Never leave orphan databases — they pile up silently because git has no `pre-worktree-remove` hook.
77+
78+
## Quick Reference
79+
80+
```sh
81+
# Spin up a new worktree (does env + DB + install + migrate + generate automatically)
82+
git worktree add .worktrees/<slug> -b mariano/<branch-name> origin/main
83+
84+
# Start dev — ONLY in the worktree you're actively iterating on
85+
cd .worktrees/<slug>
86+
bun run dev
87+
88+
# Start dev in a background worktree (no trigger dev, custom port via .env.local)
89+
echo "PORT=3001" >> apps/app/.env.local
90+
bun run --filter '@trycompai/app' dev:no-trigger
91+
92+
# Clean up when branch is done
93+
# (use the stale-worktree-cleanup skill)
94+
```
95+
96+
## Red Flags
97+
98+
If you catch yourself doing any of these, stop — the hook should have handled it:
99+
100+
- Running `bun install` manually in a new worktree
101+
- `cp` or `ln -s` to copy `.env` files into a worktree
102+
- Writing a script that "creates a database for my branch"
103+
- Running `bun run db:migrate` in a worktree right after creating it
104+
- Ignoring a failing `bun run dev` in two worktrees instead of swapping to `dev:no-trigger`
105+
106+
## Common Mistakes
107+
108+
| Mistake | Fix |
109+
|---|---|
110+
| Creating the worktree from another worktree instead of the main clone | Always `cd` to `/Users/mariano/code/comp` first |
111+
| Editing `.env` in a worktree and expecting it to propagate | If it's a symlink, yes; if it's a real copy (has `DATABASE_URL`), no. Check with `ls -la`. |
112+
| Forgetting to bump `PORT` → two dev servers collide | Put `PORT=<free-port>` in the worktree's `.env.local` |
113+
| Running `trigger dev` in multiple worktrees | Switch to `dev:no-trigger` in all but one |
114+
| Not cleaning up → orphan `compdev_*` databases piling up | Use the `stale-worktree-cleanup` skill regularly |
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
name: stale-worktree-cleanup
3+
description: Use when cleaning up old git worktrees, removing worktrees whose branches have merged or been abandoned, or dropping orphaned compdev_* Postgres databases. Triggers on "clean up worktrees", "delete stale worktrees", "worktrees piling up", "orphaned databases", "remove unused worktree".
4+
---
5+
6+
# Stale Worktree Cleanup
7+
8+
## Overview
9+
10+
This repo's `.githooks/post-checkout` creates a per-worktree Postgres database (`compdev_<slug>`) on every `git worktree add`. Git has no `pre-worktree-remove` hook, so dead databases pile up over time. This skill defines a safe, reversible process to reap both the worktree directories and their dangling databases together.
11+
12+
**Core principle**: never delete work the user might still want. Classify first, ask second, remove last.
13+
14+
## When to Use
15+
16+
- User says "clean up worktrees", "delete stale worktrees", "remove old worktrees", "worktrees piling up"
17+
- User mentions orphaned `compdev_*` DBs or running out of disk space from dead `node_modules`
18+
- Starting a new feature and noticing many old worktree dirs
19+
20+
## When NOT to Use
21+
22+
- User wants to remove ONE specific worktree (just run `git worktree remove <path>` + drop its DB directly — don't load the whole process)
23+
- User is actively working in a worktree (never touch active work)
24+
25+
## Process
26+
27+
### Step 1 — Inventory
28+
29+
Run these four commands (parallel-safe) and capture the output:
30+
31+
```sh
32+
git worktree list --porcelain
33+
gh pr list --author @me --state all --limit 50 --json headRefName,state,url,number
34+
git branch --no-color | cat
35+
```
36+
37+
Then query the DB for `compdev_*` databases. The management URL is the main worktree's `DATABASE_URL` with the database path swapped to `postgres`:
38+
39+
```sh
40+
bun -e '
41+
import { Client } from "pg";
42+
const raw = require("fs").readFileSync("packages/db/.env", "utf8");
43+
const url = raw.match(/^DATABASE_URL=(.*)$/m)[1].replace(/^["\x27]|["\x27]$/g, "");
44+
const mgmt = url.replace(/\/[^/?]+(\?|$)/, "/postgres$1");
45+
const c = new Client({ connectionString: mgmt });
46+
await c.connect();
47+
const r = await c.query("SELECT datname FROM pg_database WHERE datname LIKE \x27compdev\\\\_%\x27 ORDER BY 1");
48+
for (const row of r.rows) console.log(row.datname);
49+
await c.end();
50+
'
51+
```
52+
53+
### Step 2 — Classify each worktree
54+
55+
For each worktree (skip the main one):
56+
57+
| Signal | Classification |
58+
|---|---|
59+
| Branch merged to main AND clean working tree AND no unpushed commits | **safe** |
60+
| PR is `CLOSED` (not merged) | **needs-confirm** |
61+
| Uncommitted changes OR unpushed commits | **needs-confirm** |
62+
| No matching PR, no merge, has local commits | **keep** (user may still be working on it) |
63+
| Is the main worktree | **skip** |
64+
65+
Gather per worktree:
66+
- `cd <path> && git status --porcelain | wc -l` — uncommitted changes count
67+
- `cd <path> && git log @{upstream}..HEAD --oneline 2>/dev/null | wc -l` — unpushed commits (0 if no upstream)
68+
- Branch → PR lookup from step 1
69+
70+
### Step 3 — Present to user
71+
72+
Show a table and explicit recommendations. Example:
73+
74+
```
75+
Path Branch PR state Changes Recommendation
76+
.worktrees/sale-45-… mariano/sale-45-… MERGED 0 / 0 ✅ safe to remove
77+
.worktrees/old-experiment mariano/scratch — 3 / 0 ⚠ uncommitted — confirm first
78+
.worktrees/worktree-env-auto-link mariano/worktree-env-auto-link OPEN 0 / 0 ⏳ keep (PR open)
79+
80+
Orphan databases (no worktree dir):
81+
compdev_abandoned_feature
82+
compdev_old_migration_test
83+
```
84+
85+
Then ask: **"Remove the items marked ✅? Confirm by listing anything you want me to also nuke from the ⚠ / ⏳ set."**
86+
87+
### Step 4 — Remove confirmed items
88+
89+
For each worktree the user approved:
90+
91+
```sh
92+
# 1. Remove the worktree dir (use --force only if user confirmed dirty-removal)
93+
git worktree remove "<path>" # clean case
94+
git worktree remove --force "<path>" # only after explicit user OK
95+
96+
# 2. Derive the slug and drop the database
97+
slug=$(basename "<path>" | tr '[:upper:]' '[:lower:]' | tr '-' '_' | tr -cd 'a-z0-9_')
98+
bun -e '
99+
import { Client } from "pg";
100+
const raw = require("fs").readFileSync("packages/db/.env", "utf8");
101+
const url = raw.match(/^DATABASE_URL=(.*)$/m)[1].replace(/^["\x27]|["\x27]$/g, "");
102+
const mgmt = url.replace(/\/[^/?]+(\?|$)/, "/postgres$1");
103+
const c = new Client({ connectionString: mgmt });
104+
await c.connect();
105+
await c.query(`DROP DATABASE IF EXISTS "compdev_'"$slug"'"`);
106+
await c.end();
107+
console.log("dropped compdev_'"$slug"'");
108+
'
109+
```
110+
111+
For orphan databases (no matching worktree dir), just drop them — no worktree to remove.
112+
113+
**Do NOT** delete the local branch unless the user explicitly asked. Branches can be recreated from `origin` cheaply; worktrees cannot.
114+
115+
### Step 5 — Verify and report
116+
117+
```sh
118+
git worktree list
119+
# then re-run the compdev_* query from step 1
120+
```
121+
122+
Report back:
123+
- Worktrees removed (paths)
124+
- Databases dropped (names)
125+
- Anything skipped and why
126+
- What's left (still-active worktrees)
127+
128+
## Safety Rules
129+
130+
- **Never remove the main worktree.** Its path is always the first line of `git worktree list --porcelain`.
131+
- **Never `--force` without confirmation.** Dirty worktrees can contain un-stashed work.
132+
- **Never `DROP DATABASE` on anything not matching `^compdev_[a-z0-9_]+$`.** Never drop `comp`, `postgres`, or any production-looking name.
133+
- **Never delete a local branch** as part of cleanup unless the user explicitly asks. Orphaned branches are cheap; lost work isn't.
134+
- **Never run this inside a worktree you're about to delete.** `cd` to the main worktree first.
135+
136+
## Common Mistakes
137+
138+
| Mistake | Fix |
139+
|---|---|
140+
| Dropping the DB but leaving the worktree dir | Run `git worktree prune` then `git worktree remove` |
141+
| Removing the worktree but leaving the DB (accumulates orphans) | Always do both in the same pass |
142+
| Using hyphens in the DB name | The hook slug rule is `tr '-' '_'` — always underscores |
143+
| Running from inside a doomed worktree | `cd` to the main worktree before starting the process |
144+
| Using `gh pr list` on a branch with no PR | Missing data is not "abandoned" — needs `needs-confirm` classification |
145+
146+
## Red Flags
147+
148+
If any of these show up mid-process, **stop and ask the user**:
149+
150+
- A worktree has unpushed commits AND no PR → might be unreleased work
151+
- The classification returned >10 "safe to remove" items → unusual volume, double-check
152+
- `git worktree remove` errors with "working tree is not clean" → never retry with `--force` without explicit consent
153+
- A `compdev_*` name has weird characters or unexpected format → don't drop
154+
155+
## Quick Reference
156+
157+
```sh
158+
# List everything
159+
git worktree list --porcelain
160+
gh pr list --author @me --state all --limit 50 --json headRefName,state
161+
162+
# Per-worktree inspection
163+
git -C <path> status --porcelain
164+
git -C <path> log @{upstream}..HEAD --oneline 2>/dev/null
165+
166+
# Clean removal
167+
git worktree remove <path>
168+
169+
# Dirty removal (requires user confirmation first)
170+
git worktree remove --force <path>
171+
172+
# Database drop (run from main worktree)
173+
# See the bun -e snippets above for the exact invocation
174+
```

0 commit comments

Comments
 (0)