Herd worktree
A daily-driver collection of skills for agentic coding — a portable, agent-agnostic catalog managed with the vd CLI.
npx -y skills add vanducng/skills --skill herd-worktreeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Set up an isolated git worktree for a Laravel project served by Laravel Herd (Pro-aware). Composes vd:worktree for the worktree/.env/port mechanics, then adds the Herd layer: link + scheme-correct site (HTTPS via herd secure when the source is secured), .env rewrite (APP_URL, SESSION_DOMAIN, Sanctum - only when present), opt-in per-worktree database isolation, Vite TLS/CORS, then hands finishing to vd:ship / vd:git. Triggers: 'herd-worktree', 'herd worktree', 'laravel herd worktree', 'isolate a Laravel feature branch', 'work on this Laravel branch with Herd'.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
11.6 KB, ~3.1k tokens by cl100k_base, as published. Nobody here has run it
Laravel Herd Worktree
Spin up an isolated, runnable copy of a Laravel project on its own Herd site for feature-branch work - without colliding with your main checkout or, on Herd Pro, your shared dev database.
Announce at start: "Using laravel-herd-worktree to set up an isolated Laravel + Herd workspace."
Design: this is a thin Herd layer on top of vd:worktree
Do not reinvent worktree mechanics. vd:worktree already creates the worktree under .worktrees/<repo>-<feature>/, copies all .env* (nested included), assigns a deterministic 10-port block (.env.worktree → WORKTREE_PORT_BASE), keeps .git/info/exclude clean, anchors agent artifacts to the main checkout, and enters the worktree. This skill only adds what Herd needs and then defers finishing to vd:ship / vd:git.
| Concern | Owner |
|---|---|
Worktree + branch + .env copy + ports | vd:worktree |
Herd site (link/secure), .env URL/session rewrite, DB isolation, Vite | this skill |
| Commit / push / PR / version | vd:ship / vd:git |
Prerequisites
- Laravel Herd (Pro adds shared MySQL/Postgres/Redis services - relevant to DB isolation below). macOS.
- Vite + npm frontend (adjust if yarn/pnpm).
- Laravel Sanctum only if the project uses it (steps below are conditional).
Setup
1. Create the worktree via vd:worktree
Detect the branch (ticket key is authoritative), then:
WT="$(for d in "$HOME/skills/skills/worktree" "$HOME/.claude/skills/worktree" "$HOME/.agents/skills/worktree"; do [ -d "$d" ] && { echo "$d/scripts/worktree.cjs"; break; }; done)"
node "$WT" create "<branch>" --no-prefix --json
$WT resolves to the first worktree install that exists - the $HOME/skills dev repo, else the Claude (~/.claude/skills) or Codex (~/.agents/skills) install; pick it once per session and reuse it below. Parse the JSON for the worktree path and portBase, then move the session into the worktree (per the sessionSwitch block). The .env is already copied - you only rewrite it below.
PROJECT_NAME=$(basename "$(git rev-parse --show-toplevel)") # main repo name
SANITIZED_BRANCH=$(echo "$BRANCH_NAME" | tr '/' '-')
SITE_NAME="$PROJECT_NAME-$SANITIZED_BRANCH" # matches .worktrees/<repo>-<feature>
2. Link to Herd - match the source site's scheme
Detect whether the source project is secured (don't assume HTTP):
herd links | grep -E "\b$PROJECT_NAME\b" # SSL column, or:
grep '^APP_URL=' /path/to/main/.env # https:// ⇒ secured
cd <worktree-path>
herd link "$SITE_NAME"
# If the source is HTTPS (Herd Pro default for team projects):
herd secure "$SITE_NAME" # ⇒ https://$SITE_NAME.test
# Only if the source is plain HTTP: skip herd secure (HTTP matches the Vite dev server)
Forcing HTTP on a project that is normally HTTPS causes scheme/cookie mismatches. Mirror the source.
3. Rewrite the worktree .env (conditionally)
SCHEME = https if secured, else http. Edit only keys that exist in the source .env:
ENV=<worktree-path>/.env
sed -i '' "s|^APP_URL=.*|APP_URL=$SCHEME://$SITE_NAME.test|" "$ENV"
# SESSION_DOMAIN: only set if the source set a real domain (skip when it's null/empty - single-host).
grep -qE '^SESSION_DOMAIN=.+' /path/to/main/.env && [ "$(grep '^SESSION_DOMAIN=' /path/to/main/.env)" != "SESSION_DOMAIN=null" ] \
&& sed -i '' "s|^SESSION_DOMAIN=.*|SESSION_DOMAIN=$SITE_NAME.test|" "$ENV"
# Sanctum: only if present.
grep -q '^SANCTUM_STATEFUL_DOMAINS=' "$ENV" \
&& sed -i '' "s|^SANCTUM_STATEFUL_DOMAINS=\(.*\)|SANCTUM_STATEFUL_DOMAINS=\1,$SITE_NAME.test|" "$ENV"
# Secure-cookie flag only matters for HTTP.
[ "$SCHEME" = "http" ] && echo "SESSION_SECURE_COOKIE=false" >> "$ENV"
4. Database - isolate or share (Herd Pro footgun)
On Herd Pro the worktree's copied .env points at the same shared DB as main. A feature branch running migrate / migrate:fresh / seeders would mutate your main dev database. Ask (AskUserQuestion in Claude Code; plain-text question elsewhere):
AskUserQuestion:
question: "This worktree will run migrations against DB_DATABASE. Isolate it?"
header: "Database"
options:
- label: "Isolate (Recommended)" → create a per-worktree DB, rewrite DB_DATABASE
- label: "Share main DB" → reuse the same DB (read-only / no migrations)
If Isolate (MySQL example; mirror for Postgres):
DBNAME=$(grep '^DB_DATABASE=' "$ENV" | cut -d= -f2)_$SANITIZED_BRANCH
mysql -h 127.0.0.1 -u "$(grep '^DB_USERNAME=' "$ENV" | cut -d= -f2)" -e "CREATE DATABASE IF NOT EXISTS \`$DBNAME\`"
sed -i '' "s|^DB_DATABASE=.*|DB_DATABASE=$DBNAME|" "$ENV"
# (migrate in step 6)
5. Vite - scheme-aware
ls <worktree-path>/vite.config.ts <worktree-path>/vite.config.js 2>/dev/null # detect either
- HTTPS site:
laravel-vite-pluginauto-detects the Herd TLS cert created byherd secureand serves Vite over HTTPS on the right host - leave it to the plugin. Addserver: { cors: true }only if you actually see cross-origin blocks. - HTTP site: ensure
server: { host: 'localhost', cors: true }(do not use0.0.0.0).
6. Register teardown hook (so vd:worktree clean/remove cleans Herd + DB)
vd:worktree runs .worktree/hooks/pre-remove inside the worktree on both remove and clean (env exported: WORKTREE_NAME, WORKTREE_PATH, WORKTREE_SOURCE, …). Write one at setup so the Herd site and any isolated DB are never orphaned - worktree clean then handles Laravel teardown for free:
mkdir -p <worktree-path>/.worktree/hooks
cat > <worktree-path>/.worktree/hooks/pre-remove <<'EOF'
#!/usr/bin/env bash
# Herd + DB teardown - runs on `vd:worktree remove` and `clean`. Failure warns, never blocks.
set -u
herd unlink "$WORKTREE_NAME" 2>/dev/null || true
pkill -f "node.*vite.*$WORKTREE_NAME" 2>/dev/null || true
# Drop the DB only if this worktree was isolated (its DB differs from the source's).
DB=$(grep -m1 '^DB_DATABASE=' "$WORKTREE_PATH/.env" 2>/dev/null | cut -d= -f2)
SRC_DB=$(grep -m1 '^DB_DATABASE=' "$WORKTREE_SOURCE/.env" 2>/dev/null | cut -d= -f2)
if [ -n "${DB:-}" ] && [ "$DB" != "${SRC_DB:-}" ]; then
mysql -h 127.0.0.1 -u root -e "DROP DATABASE IF EXISTS \`$DB\`" 2>/dev/null || true
fi
EOF
chmod +x <worktree-path>/.worktree/hooks/pre-remove
The DB drop is guarded - it never touches the shared/source database, only a per-worktree one created in step 4.
7. Install, migrate, run
cd <worktree-path>
composer install --no-interaction # worktrees don't share vendor/
npm install # …or node_modules/
php artisan config:clear && php artisan cache:clear
[ -n "$DBNAME" ] && php artisan migrate # only when DB was isolated; add --seed if wanted
pkill -f "node.*vite" 2>/dev/null; rm -f public/hot # free a stale Vite/hot file
npm run build # or keep `npm run dev` running while browsing through Herd
Site: $SCHEME://$SITE_NAME.test.
Finishing - delegate, don't reinvent
When work is done, route to the existing skills rather than a bespoke PR flow:
- Ship it:
vd:ship(test → review → version → PR) orvd:gitfor a single conventional commit + push from the worktree branch. - Bring changes into main checkout:
git merge <branch> --no-commit --no-fffrom the main tree, review, commit.
Then clean up through vd:worktree - the pre-remove hook from step 6 does the Herd unlink + isolated-DB drop automatically:
node "$WT" remove "$SITE_NAME" # one worktree: hook → branch → metadata
# or sweep all merged/stale worktrees at once (each runs its own teardown hook):
node "$WT" clean --yes
Because teardown lives in the worktree's pre-remove hook, vd:worktree clean tears down the Herd site and isolated DB for every swept worktree - no orphaned .test sites or leftover databases. (Stop Vite first if it's still running: pkill -f "node.*vite".)
Herd Pro notes
- Shared services: Pro's MySQL/Postgres/Redis are shared across all sites - DB isolation (step 4) is the main safeguard; Redis/queue can collide too (use a per-worktree
REDIS_PREFIX/DB_REDISif the branch hits queues). - HTTPS is the Pro default for team projects - mirror it (steps 2–3), don't force HTTP.
- Per-site PHP:
herd isolate <php-version>inside the worktree if it needs a different PHP than the global default.
Common issues
| Symptom | Cause → Fix |
|---|---|
| 401 on API routes | Sanctum domain missing → add $SITE_NAME.test to SANCTUM_STATEFUL_DOMAINS, php artisan config:clear |
| "Cookie rejected for invalid domain" | SESSION_DOMAIN mismatch → set to $SITE_NAME.test (or leave null for single host), config:clear, clear browser cookies |
| Mixed Content (HTTPS page, HTTP assets) | Vite served HTTP under an HTTPS site → confirm herd secure ran and laravel-vite-plugin sees the cert; restart npm run dev |
| CORS blocked (HTTP site) | Vite host: '0.0.0.0' → set host: 'localhost', cors: true, restart |
| Migrations hit the wrong/shared DB | .env still points at main DB → isolate (step 4) and rewrite DB_DATABASE |
| Vite manifest not found | no public/build/manifest.json and no Vite dev server → npm install, then npm run build (or keep npm run dev running) |
| Assets not loading / wrong port | stale Vite → pkill -f "node.*vite", rm -f public/hot, npm run dev from the worktree |
| Missing vendor/ or node_modules/ | worktrees don't share them → composer install / npm install in the worktree |
Reading public/build/manifest.json or a vendor/... path blocked ("BLOCKED: Access to 'build' denied") | harness scout-block hook denies path reads containing a build/vendor segment (the npm run build command itself is allowed) → add !public/build (or !vendor, no trailing slash) to .vdignore, or keep npm run dev running so no manifest read is needed |
Checklist
- Worktree created via
vd:worktree(.worktrees/$SITE_NAME,.envcopied, ports assigned) -
herd linkdone; scheme matches source (secured ⇒herd secure) -
.envrewritten:APP_URL(correct scheme),SESSION_DOMAIN/Sanctum only if applicable - Database decision made (isolated DB +
DB_DATABASErewritten, or explicitly sharing) -
.worktree/hooks/pre-removewritten (Herd unlink + isolated-DB drop onworktree remove/clean) -
vite.config.{ts,js}correct for the scheme -
composer install+npm installdone;npm run builddone ornpm run devrunning; migrations run if isolated - Site reachable at
$SCHEME://$SITE_NAME.test
CRITICAL: working directory after setup
All subsequent work (edits, artisan, tests, git) MUST use the worktree path. vd:worktree enters it by default; stay there. The main checkout is a different copy - editing it changes the wrong files.
Integration
- Composes:
vd:worktree(mechanics),vd:ship/vd:git(finishing). - Pairs with:
vd:cook/vd:fix(implement in the worktree).