From valet-wt
Manages Laravel workspace scripts and git worktrees with Valet. Activates when the user mentions: - "worktree", "work tree", "new worktree", "create worktree" - "create setup scripts", "generate archive script", "create run.sh" - "scaffold workspace scripts", "create Codex environment" - "valet worktree", "cleanup worktree", "finish worktree" - "branch workflow", "feature branch setup", "parallel development"
npx claudepluginhub thecrazybob/claude-code-plugins --plugin valet-wtThis skill uses the workspace's default tool permissions.
This skill covers two related workflows:
Conducts multi-round deep research on GitHub repos via API and web searches, generating markdown reports with executive summaries, timelines, metrics, and Mermaid diagrams.
Dynamically discovers and combines enabled skills into cohesive, unexpected delightful experiences like interactive HTML or themed artifacts. Activates on 'surprise me', inspiration, or boredom cues.
Generates images from structured JSON prompts via Python script execution. Supports reference images and aspect ratios for characters, scenes, products, visuals.
This skill covers two related workflows:
setup.sh, archive.sh, run.sh, and Codex environment config for a Laravel projectGenerate setup, archive, and run scripts for Laravel applications. Scripts support MySQL, PostgreSQL, and SQLite databases, detect project services from composer.json and .env, and include worktrunk (wt) and Codex worktree integration.
| File | Purpose |
|---|---|
scripts/setup.sh | Install deps, create DB, configure .env, migrate & seed |
scripts/archive.sh | Stop processes, unlink Valet, drop DB, clean up |
scripts/run.sh | Run detected dev services (horizon, queue, vite, etc.) via concurrently |
.config/wt.toml | Worktrunk hooks — wires setup.sh/archive.sh into wt lifecycle |
.codex/environments/environment.toml | Codex environment config embedding the setup script |
Before generating scripts, inspect the target project to determine:
Database driver — Run php artisan about --json and read drivers.database to determine what the project actually uses. Display this in the detection summary shown to the user.
pgsql → PostgreSQL commands (psql, createdb, dropdb, pg_isready)mysql → MySQL commands (mysql, mysqladmin)sqlite → File-based (touch database file, rm to clean)This is more reliable than reading .env.example or .env because the actual running config may differ.
IMPORTANT — Dynamic scripts: The generated setup.sh and archive.sh scripts must detect the DB driver at runtime by reading DB_CONNECTION from .env after the copy step. Do NOT hardcode a single driver. Include all driver branches (pgsql, mysql, sqlite) in every generated script.
Project name — Derive via ${CLAUDE_PLUGIN_ROOT}/scripts/detect-project-name.sh. Use lowercase, hyphenated form for Valet domains and underscored form for database names.
Services — Run ${CLAUDE_PLUGIN_ROOT}/scripts/detect-services.sh from the target project root, or scan composer.json require keys and .env.example for:
| Service | Detection | Impact |
|---|---|---|
| Horizon | laravel/horizon in composer.json | Add to run.sh, check Redis connectivity |
| Meilisearch/Scout | laravel/scout + MEILISEARCH_HOST in .env | Add fallback to null driver if unreachable |
| Redis | REDIS_HOST in .env or predis/predis in composer.json | Add connectivity check |
| Reverb | laravel/reverb in composer.json | Add reverb:start to run.sh |
| Pulse | laravel/pulse in composer.json | Note in setup output |
| Octane | laravel/octane in composer.json | Use octane:start instead of serve in run.sh |
| Pail | laravel/pail in composer.json | Add pail --timeout=0 to run.sh |
Frontend — Check package.json for Vite (default) or other build tools.
Follow this order exactly:
WT_WORKSPACE_NAME fallback to basename $PWD~/.codex/worktrees/<id>/WT_ROOT_PATH fallback to $(dirname "$0")/..composer install with retry, npm install.env.example, handle broken symlinks{project}-{worktree_key}.test, HTTP only (no valet secure)DB_CONNECTION from .env (after copy), default to sqlitereferences/database-drivers.mdphp artisan optimize:clearphp artisan storage:link --forceWT_PORT if availablephp artisan migrate --seed --forcevalet unsecure (backwards compat) then valet unlinkreferences/database-drivers.md)Use npx concurrently with named, color-coded processes. Only include detected services:
npx concurrently -k \
-n "horizon,schedule,logs,vite" \
-c "#93c5fd,#c4b5fd,#fb7185,#fdba74" \
"php artisan horizon" \
"php artisan schedule:work" \
"php artisan pail --timeout=0" \
"npm run dev"
Common process mappings:
php artisan horizon (requires Redis)php artisan queue:listen --tries=1php artisan schedule:workphp artisan pail --timeout=0npm run devphp artisan reverb:startGenerate .config/wt.toml so that worktrunk (wt switch --create / wt remove) automatically runs the lifecycle scripts. Adapt hooks based on detected services.
[post-start]
setup = """
echo "Pre-copying dependencies from main project..."
cp -R {{ primary_worktree_path }}/vendor vendor 2>/dev/null && echo "vendor/ copied" || echo "No vendor/ to copy"
cp -R {{ primary_worktree_path }}/node_modules node_modules 2>/dev/null && echo "node_modules/ copied" || echo "No node_modules/ to copy"
WT_ROOT_PATH={{ primary_worktree_path }} bash scripts/setup.sh
"""
[pre-commit]
pint = "vendor/bin/pint --dirty"
# Include phpstan only if phpstan.neon or phpstan.neon.dist exists
phpstan = "vendor/bin/phpstan analyse --memory-limit=512M"
[pre-merge]
test = "php artisan test --parallel --compact"
[pre-remove]
archive = "bash scripts/archive.sh"
[list]
url = "http://{project}-{{ branch | sanitize }}.test"
Key decisions:
post-start (background) instead of post-create (blocking) — worktree creation feels instant, setup runs in backgroundWT_ROOT_PATH must be set so setup.sh copies .env from the main projectWT_WORKSPACE_NAME is not needed — setup.sh defaults to basename "$PWD" which is the sanitized branchpre-commit hooks run during wt merge before the squash commit — Pint formats dirty files, PHPStan runs static analysispre-merge runs the full test suite before merge to target branch[list] url shows the Valet domain in wt list outputphpstan hook only if phpstan.neon or phpstan.neon.dist exists in the project{project} placeholder must be replaced with the actual detected project nameAppend /.worktrees/ to the project's .gitignore if not already present. This prevents worktree directories from being committed.
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
version = 1
name = "{project-name}"
[setup]
script = '''
{contents of setup.sh}
'''
Include this function in both setup.sh and archive.sh:
env_value() { grep "^$1=" .env | cut -d '=' -f2- | sed "s/^[\"']//;s/[\"']$//"; }
if grep -q "^KEY=" .env; then
sed -i '' "s/^KEY=.*/KEY=value/" .env
else
echo "KEY=value" >> .env
fi
WORKTREE_KEY="$WORKSPACE_NAME"
if [[ "$PWD" =~ /worktrees/([^/]+)/ ]]; then
WORKTREE_KEY="${BASH_REMATCH[1]}"
fi
WORKTREE_KEY=$(echo "$WORKTREE_KEY" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-' '-')
WORKTREE_KEY="${WORKTREE_KEY#-}"
WORKTREE_KEY="${WORKTREE_KEY%-}"
if [ -z "$WORKTREE_KEY" ]; then
WORKTREE_KEY="$WORKSPACE_NAME"
fi
Sanitize hyphens to underscores for all DB drivers. Validate before use:
DB_NAME=$(echo "{project}_{workspace}" | tr '-' '_')
if [[ ! "$DB_NAME" =~ ^[a-zA-Z0-9_]+$ ]]; then
echo "Error: Invalid database name '$DB_NAME'"; exit 1
fi
Note: The scripts/setup.sh version uses {project}_{workspace} (2 parts). The .codex/environments/environment.toml version uses {project}_{workspace}_{worktree} (3 parts) because Codex worktrees need unique databases per worktree. Match the convention to the target.
Kill only Vite processes scoped to the current workspace directory:
WORKSPACE_DIR="$(pwd)"
VITE_KILLED=0
for pid in $(pgrep -f "node.*vite" 2>/dev/null); do
if lsof -p "$pid" 2>/dev/null | grep -q "$WORKSPACE_DIR"; then
kill "$pid" 2>/dev/null && VITE_KILLED=$((VITE_KILLED + 1)) || true
fi
done
After generating scripts, make them executable:
chmod +x scripts/setup.sh scripts/archive.sh scripts/run.sh
references/database-drivers.md — Complete database setup/teardown commands for MySQL, PostgreSQL, and SQLite with connectivity checks, creation, and cleanupThis skill manages git worktrees for Laravel projects served by Laravel Valet. It creates isolated development environments where each feature branch gets its own:
.worktrees/)projectname-branchname.test)projectname_branchname)This enables parallel work on multiple features without switching branches or corrupting shared state.
ALWAYS start by checking for existing worktrees:
git worktree list
Use AskUserQuestion to ask:
header: "Worktree Action"
question: "You have existing worktrees. What would you like to do?"
options:
- label: "Set up new worktree"
description: "Create a new worktree for a different feature branch"
- label: "Finish existing worktree"
description: "Complete work on a worktree (PR, merge, or abandon)"
Proceed directly to asking for the branch name.
Pre-requisite: The project must have scripts/setup.sh. If missing, generate scripts first using the /scripts command or the script generation workflow in Part 1.
Use AskUserQuestion:
header: "Branch Name"
question: "What branch name do you want to create for this worktree?"
Sanitize the branch name:
SANITIZED_BRANCH=$(echo "$BRANCH" | tr '/' '-' | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
PROJECT=$(${CLAUDE_PLUGIN_ROOT}/scripts/detect-project-name.sh)
BASE_BRANCH=$(git config init.defaultBranch 2>/dev/null || echo "main")
git show-ref --verify --quiet refs/heads/$BASE_BRANCH || BASE_BRANCH="master"
if [ ! -f scripts/setup.sh ]; then
# Inform user and trigger /scripts generation first
fi
git worktree add .worktrees/$SANITIZED_BRANCH -b $BRANCH $BASE_BRANCH
cp -r scripts/ .worktrees/$SANITIZED_BRANCH/scripts/
Copy vendor/ and node_modules/ from the main project before running setup. Since the worktree shares the same composer.lock and package-lock.json, these directories are identical — turning composer install / npm install into fast verification steps instead of full installs.
echo "Pre-copying dependencies from main project..."
cp -R vendor/ .worktrees/$SANITIZED_BRANCH/vendor/ 2>/dev/null && echo "vendor/ copied" || echo "No vendor/ to copy"
cp -R node_modules/ .worktrees/$SANITIZED_BRANCH/node_modules/ 2>/dev/null && echo "node_modules/ copied" || echo "No node_modules/ to copy"
Why here and not in
setup.sh?setup.shis also used by Codex/Conductor environments where there's no parent project to copy from. The worktree command always has a parent project available.
cd .worktrees/$SANITIZED_BRANCH
WT_WORKSPACE_NAME=$SANITIZED_BRANCH \
WT_ROOT_PATH=$(cd ../.. && pwd) \
bash scripts/setup.sh
This single command replaces the old 15 inline steps (Valet link, .env config, DB creation, dependencies, migrations, etc.).
Check if vite.config.js or vite.config.ts has CORS settings. If missing, add:
server: {
host: 'localhost',
cors: true,
}
Skip if ~/.warp/ doesn't exist.
mkdir -p ~/.warp/launch_configurations
WORKTREE_PATH="$(pwd)"
sed -e "s|{{WORKTREE_PATH}}|$WORKTREE_PATH|g" \
-e "s|{{WORKTREE_NAME}}|$SANITIZED_BRANCH|g" \
${CLAUDE_PLUGIN_ROOT}/templates/laravel-worktree.yaml \
> ~/.warp/launch_configurations/laravel-worktree.yaml
## Worktree Created Successfully
| Item | Value |
|------|-------|
| Branch | $BRANCH |
| Directory | .worktrees/$SANITIZED_BRANCH/ |
| URL | http://$PROJECT-$SANITIZED_BRANCH.test |
| Database | ${PROJECT}_${SANITIZED_BRANCH} |
### Next Steps
1. **Open Warp layout:** Press `Cmd+Ctrl+L` and select "Laravel Worktree"
2. **Start services:** Run `bash scripts/run.sh` (or use the Warp layout)
3. **Open in browser:** Run `browse` or visit the URL above
**IMPORTANT:** All subsequent work must use the worktree directory:
`.worktrees/$SANITIZED_BRANCH/`
When the user wants to finish work on a worktree, use AskUserQuestion:
header: "Finish Worktree"
question: "How would you like to complete this worktree?"
options:
- label: "Create PR"
description: "Push branch and create a pull request on GitHub"
- label: "Transfer to main"
description: "Merge changes into main directory (no PR)"
- label: "Abandon"
description: "Discard all changes and remove worktree"
git push -u origin HEAD && gh pr create --fillarchive.sh then git cleanup:
cd .worktrees/$SANITIZED_BRANCH
WT_WORKSPACE_NAME=$SANITIZED_BRANCH bash scripts/archive.sh
cd ../..
git worktree remove .worktrees/$SANITIZED_BRANCH --force
git branch -D $BRANCH
git merge .worktrees/$SANITIZED_BRANCH --no-commit --no-ff| Item | Pattern |
|---|---|
| Worktree path | .worktrees/{sanitized-branch}/ |
| Domain | {project}-{sanitized-branch}.test |
| Database | {project}_{sanitized_branch} |
| Protocol | HTTP only (no SSL) |
| Setup | bash scripts/setup.sh |
| Teardown | bash scripts/archive.sh + git cleanup |
| Services | bash scripts/run.sh |
| Variable | Description | Example |
|---|---|---|
$BRANCH | Original branch name | feature/user-auth |
$SANITIZED_BRANCH | Filesystem-safe version | feature-user-auth |
$PROJECT | Project name | myproject |
$BASE_BRANCH | Main branch | main or master |
See references/troubleshooting.md for common issues including:
.worktrees/$SANITIZED_BRANCH//scripts before creating worktrees