Dev setup
Skill aksh-3141/claude-toolshed/plugins/dev-setup/skills/dev-setup
Provide and install pre-packaged plugins to extend Claude Code with new skills directly within your Claude environment.
npx -y skills add aksh-3141/claude-toolshed --skill dev-setupAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 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
Generate dev server lifecycle scripts (start/stop/status/ports) from detected project structure
SKILL.md
28.0 KB, as published. Nobody here has run it
/dev-setup
User request: "$ARGUMENTS"
Task
Detect the project's structure, propose a dev environment configuration, and generate bash scripts for managing servers, Chrome profiles, and worktree port isolation.
Process
Step 0: Ensure dependencies
Resolve plugin path:
SKILL_DIR="$(find "$HOME/.claude/plugins/cache" -type d -name "dev-setup" -path "*/skills/dev-setup" 2>/dev/null | head -1)"
[[ -z "$SKILL_DIR" ]] && SKILL_DIR="$(find "$HOME" -maxdepth 8 -type d -name "dev-setup" -path "*/skills/dev-setup" 2>/dev/null | head -1)"
bash "$SKILL_DIR/scripts/ensure-deps.sh"
If the script exits with an error, show the missing dependency message to the user and stop. Warnings about optional tools can be shown but should not block execution.
Route: upgrade
If $ARGUMENTS starts with "upgrade":
-
Resolve plugin path — find the
reference/directory:SKILL_DIR="$(find "$HOME/.claude/plugins/cache" -type d -name "dev-setup" -path "*/skills/dev-setup" 2>/dev/null | head -1)" [[ -z "$SKILL_DIR" ]] && SKILL_DIR="$(find "$HOME" -maxdepth 8 -type d -name "dev-setup" -path "*/skills/dev-setup" 2>/dev/null | head -1)" echo "SKILL_DIR=$SKILL_DIR" -
Find project script dir:
- Parse
package.jsonfor adev:startscript → extract the path →dirname - Example:
"dev:start": "bash tools/dev/dev-start.sh"→SCRIPT_DIR=tools/dev - Fallback:
tools/dev/ - If
SCRIPT_DIRdoesn't exist: stop with "No script directory found. Run/dev-setupfirst to generate scripts."
- Parse
-
Match and diff each reference script: For each
$SKILL_DIR/reference/*.sh: a. Read line 2 to extract the identifier (e.g.# dev-read-ports.sh — Read dev server ports) b. Extract just the name part:# {name} —c. Search every$SCRIPT_DIR/*.shfor a file whose line 2 contains the same# {name} —pattern d. If no match → skip (script wasn't deployed for this project) e. If match found → diff the reference file against the deployed file f. If identical → skip (already up to date) g. If different → add to upgrade list with both paths -
Report results:
-
If upgrade list is empty: "✅ All scripts up to date."
-
For each upgrade candidate, show:
📦 {ref-name} (deployed as {deployed-path}) Changes available: - {summary of key differences — read both versions and describe} [diff output — use `diff -u deployed reference`] -
Ask user to approve each change individually with
AskUserQuestion:- "Apply this update?" → Yes / No / Show full file
-
-
Apply approved changes:
- For each approved update: replace the deployed file content with the reference content using the
Edittool (orWriteif the diff is too large) - Run
shellcheckon each modified script and fix any errors
- For each approved update: replace the deployed file content with the reference content using the
-
Skip to Step 13 (Output summary) — adapt the summary to show upgrade results instead of generation results:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ✅ Upgrade complete ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Updated: ✓ tools/dev/dev-read-ports.sh (worktree fallback for env resolution) ✓ tools/dev/dev-stop.sh (improved process cleanup) Skipped (no changes): · dev-session-name.sh · dev-start.sh Additional scripts available: · chrome-profile-setup.sh — Chrome dev profile setup · dev-wt-ports.sh — worktree port isolation Run /dev-setup to add these to your project.The "Additional scripts available" section lists all reference scripts that have no match in the project (by line-2 identifier). Show the script name and its line-2 description. If there are none, omit this section entirely.
What NOT to upgrade:
package.jsonscripts (user may have customized names/args).env/.env.example(user data).gtrconfig(user preferences)
Route: health
If $ARGUMENTS starts with "health":
-
Resolve plugin path (same as upgrade route)
-
Run the health check script:
bash "$SKILL_DIR/reference/dev-setup-health.sh" -
Print the output to the user. Then stop — do not continue to Step 1.
Step 1: Resolve project root
If $ARGUMENTS is a non-empty path, use it as the project root. Otherwise use the current directory ($PWD).
PROJECT_ROOT="${ARGUMENTS:-$PWD}"
cd "$PROJECT_ROOT"
Important: Claude's Bash tool does not preserve
cdbetween calls. Prefix every subsequent Bash command in Steps 2-5 withcd "$PROJECT_ROOT" &&to ensure all detection runs in the correct directory.
Step 2: Detect package manager
Run the following detection in Bash:
if [[ -f pnpm-lock.yaml ]]; then PM=pnpm
elif [[ -f bun.lock ]] || [[ -f bun.lockb ]]; then PM=bun
elif [[ -f yarn.lock ]]; then PM=yarn
elif [[ -f package-lock.json ]]; then PM=npm
elif [[ -f Cargo.toml ]]; then PM=cargo
elif [[ -f go.mod ]]; then PM=go
else PM=none
fi
echo "PM=$PM"
Step 3: Detect services and ports
First check if package.json exists:
[[ -f package.json ]] && echo "NODE=true" || echo "NODE=false"
If NODE=false, skip the python3 snippet and the concurrently check in Step 4 — set CONCURRENTLY=false and note "not a Node project".
Scope note: Service detection, runner options, and
package.jsonscript generation (Steps 3-9) assume a Node/JS project. For non-Node stacks (cargo,go, etc.), onlypost-checkout.sh(dependency install) is generated reliably. Tell the user: "Service management scripts for [stack] are not yet supported — only dependency install was generated. You can add custom start/stop scripts to<SCRIPT_DIR>/manually."
Read package.json scripts for entries matching dev:*, start, serve, preview:
python3 -c "
import json, sys
d = json.load(open('package.json'))
for k, v in d.get('scripts', {}).items():
if any(x in k for x in ['dev', 'start', 'serve', 'preview', 'storybook']):
print(f'SCRIPT={k} CMD={v}')
" 2>/dev/null || true
Read existing port assignments from env files:
grep -hE "^(PORT|WEB_PORT|STORYBOOK_PORT|TTYD_PORT|VITE_|.*_PORT)=" \
.env.example .env 2>/dev/null | head -20 \
|| echo "ENV_FILES=none"
Try Context7 first. If resolve-library-id returns no match or query-docs returns no relevant result, use the WebFetch fallback URL instead.
If vite.config.* exists, resolve current Vite port API:
- Use Context7:
resolve-library-id "vite"→query-docs "dev server port configuration" - Fallback (
WebFetch):https://vitejs.dev/config/server-options.html
If docker-compose.yml exists:
- Use Context7:
resolve-library-id "docker compose"→query-docs "ports mapping" - Fallback (parse directly — no docs needed):
grep -A2 'ports:' docker-compose.yml
Step 4: Detect existing scripts and runtime tools
# Find existing bash scripts
find . -maxdepth 4 \( -name "*.sh" -o -name "devctl" \) \
-not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null
# Detect script directory convention (first match wins)
SCRIPT_DIR=""
for d in tools/dev scripts bin devtools; do
[[ -d "$d" ]] && SCRIPT_DIR="$d" && echo "SCRIPT_DIR=$d" && break
done
[[ -z "$SCRIPT_DIR" ]] && echo "SCRIPT_DIR=tools/dev (will create)"
# Runtime tools
for t in tmux shellcheck shfmt ttyd; do
command -v "$t" >/dev/null 2>&1 \
&& echo "TOOL=$t STATUS=available" \
|| echo "TOOL=$t STATUS=missing"
done
# concurrently in deps
grep -q '"concurrently"' package.json 2>/dev/null \
&& echo "CONCURRENTLY=true" || echo "CONCURRENTLY=false"
If shellcheck is missing (STATUS=missing), warn the user: "Required tool shellcheck not found. Run /dev-setup health for install instructions."
Step 5: Detect Chrome and gtr config
# Chrome
grep -hE "^(CHROME_PROFILE|CHROME_CDP_PORT)=" .env.example .env 2>/dev/null \
|| echo "CHROME=not-configured"
# git gtr
[[ -f .gtrconfig ]] && echo "GTRCONFIG=found" && cat .gtrconfig || echo "GTRCONFIG=none"
[[ -f .gtr-setup.sh ]] && echo "GTR_SETUP=found" || echo "GTR_SETUP=none"
If .gtrconfig exists, use Context7 to validate hook syntax:
Try Context7 first. If resolve-library-id returns no match or query-docs returns no relevant result, use the WebFetch fallback URL instead.
resolve-library-id "git-worktree-runner"→query-docs "postCreate hook env vars WORKTREE_PATH BRANCH"- Fallback:
WebFetch https://github.com/coderabbitai/git-worktree-runner
Step 6: Propose configuration — Call A
Summarize the detected project in 2-3 lines (package manager, services found, tools available), then call AskUserQuestion with these 4 questions:
Constraint: Each call supports 1-4 questions with 2-4 options each.
AskUserQuestion (4 questions):
Q1 — header: "Services", multiSelect: true
question: "Which services should be managed? (detected from project)"
options — build dynamically from detected SCRIPT entries (up to 4), e.g.:
- label: "api :3000" description: "Backend server (PORT=3000)"
- label: "web :5173" description: "Frontend dev server (WEB_PORT=5173)"
- label: "storybook :61000" description: "Component explorer (STORYBOOK_PORT=61000)"
- label: "ttyd :7681" description: "Browser terminal (TTYD_PORT=7681)"
Note: show only detected services. If fewer than 2 detected, add a generic "custom :3000" option alongside any detected ones (do not replace them).
Q2 — header: "Runner", multiSelect: false
question: "How should servers be launched?"
options:
- label: "tmux" description: "Detached 3-pane session per branch. Requires tmux." (append " (not found on PATH)" if tmux STATUS=missing)
- label: "concurrently" description: "All servers in one terminal. No tmux required."
- label: "tmux + fallback" description: "tmux when available, concurrently otherwise."
- label: "manual only" description: "Generate scripts but no auto-launcher."
Q3 — header: "Chrome", multiSelect: false
question: "Set up an isolated Chrome dev profile?"
options:
- label: "Yes — create profile" description: "Creates ~/.chrome-profiles/<name> and a launcher."
- label: "No — skip" description: "Skip. Re-run /dev-setup to add later."
Q4 — header: "Script dir", multiSelect: false
question: "Where should scripts be placed?"
options — if a dir was detected in Step 4 (SCRIPT_DIR was set without "(will create)"), move that matching option to the first position; otherwise use this order:
- label: "tools/dev/" description: "Recommended. Mirrors reference pattern."
- label: "scripts/" description: "Common alternative."
- label: "bin/" description: "Minimal convention."
- label: "devtools/" description: "Custom directory (will be created)."
Store the user's answers as:
SELECTED_SERVICES— array of selected labelsRUNNER— selected labelCHROME— selected labelSCRIPT_DIR— selected label (strip trailing slash for filesystem use)
Step 7: Propose configuration — Call B (conditional)
Only run Call B if CHROME = "Yes — create profile" OR .gtrconfig was not already configured (GTRCONFIG=none).
If neither condition applies, skip to Step 8.
AskUserQuestion (1-2 questions, only include questions that apply):
[If CHROME = "Yes — create profile"]:
Q1 — header: "Profile name", multiSelect: false
question: "Chrome profile name and CDP port?"
options — derive <project-name> from package.json "name" field or directory basename:
- label: "<project-name> :19222" description: "Profile: chrome-<project-name>, CDP: 19222"
- label: "<project-name> :19223" description: "Profile: chrome-<project-name>, CDP: 19223"
- label: "<project-name> :19224" description: "Profile: chrome-<project-name>, CDP: 19224"
- label: "Other" description: "Enter name and port manually — skill will prompt"
[If GTRCONFIG=none]:
Q2 — header: "git gtr", multiSelect: false
question: "Set up git gtr hooks for worktree port isolation?"
options:
- label: "Yes — update .gtrconfig" description: "Adds postCreate hooks for deps + port allocation."
- label: "No — skip" description: "Skip. Port isolation will be manual."
Store answers as:
CHROME_PROFILE_NAME— profile name extracted from label (e.g. "myapp" from "myapp :19222"), or if "Other": ask user for profile name (string, no spaces) and CDP port (number) separatelyCHROME_CDP_PORT— port extracted from label (e.g. "19222")GTR— "yes" or "no"
Script Conventions
Apply these patterns to ALL generated scripts.
Strict mode
All standalone scripts: set -euo pipefail
Sourced utilities (dev-read-ports.sh, dev-session-name.sh): NO strict mode — it would bleed into the caller shell.
Port reading (dev-read-ports.sh) and session naming (dev-session-name.sh)
Both are sourced utilities — no set -euo pipefail (would bleed into caller). See reference files for full pattern. Key behaviors:
_read_env VAR DEFAULTreads from.wt-ports.env → .env → .env.example(first match wins)dev_session_namereturnsb-<branch>(normalized lowercase, hyphens)- Adapt exported port vars to match
SELECTED_SERVICES(don't hardcode all 4 from reference)
Output format — ALL status/lifecycle scripts
Emit KEY=value lines, no ANSI color, one line per entity:
SERVICE=api PORT=3000 STATUS=running PID=12345 PROCESS=node
TMUX=b-main STATUS=active ATTACHED=false
CHROME=chrome-myapp STATUS=running CDP_PORT=19222 CDP_URL=http://localhost:19222
Port variable naming
| Service | Env var |
|---|---|
| API server | PORT |
| Web/Vite | WEB_PORT |
| Storybook | STORYBOOK_PORT |
| ttyd | TTYD_PORT |
| Chrome CDP | CHROME_CDP_PORT |
Port allocator (dev-allocate-ports.sh)
Standalone script that allocates N consecutive free ports from a safe pool (20000-29999). Uses lsof -nP -iTCP:"$port" -sTCP:LISTEN to verify availability. Two modes:
- Allocate:
bash dev-allocate-ports.sh 4→ prints 4 consecutive free ports - Validate:
bash dev-allocate-ports.sh 4 --validate 23847 23848 23849 23850→ checks ports, suggests replacements if occupied
See $SKILL_DIR/reference/dev-allocate-ports.sh for the full pattern.
Worktree port allocator (dev-wt-ports.sh)
Calls dev-allocate-ports.sh to get a block of free ports, then writes them to .wt-ports.env. Must use $WORKTREE_PATH and $BRANCH env vars (set by git gtr postCreate hook), not $PWD.
Step 8: Generate bash scripts
Using the user's answers (SELECTED_SERVICES, RUNNER, CHROME, SCRIPT_DIR, GTR) and the convention patterns above, generate each script. Throughout this section, <SCRIPT_DIR> refers to the $SCRIPT_DIR value collected in Step 6 — replace it with the actual path (e.g. tools/dev) everywhere it appears. For every file:
- Generate content following the conventions
- Write with the
Writetool - Run
shellcheck <file>immediately — fix allerror-level issues before continuing
Adaptation contract
Reference files (in $SKILL_DIR/reference/) are patterns from a tools/dev/ + pnpm project. Do NOT copy them verbatim. Adapt every generated script:
| Concern | Adaptation required |
|---|---|
| PROJECT_DIR | References use "$SCRIPT_DIR/../.." (2 levels up for tools/dev/). Compute the correct number of .. segments based on actual SCRIPT_DIR depth. E.g. scripts/ → "$SCRIPT_DIR/..", bin/ → "$SCRIPT_DIR/..". |
| Service commands | References hardcode pnpm dev:back, pnpm dev:front, etc. Replace with commands derived from detected $PM and the actual package.json script names (or equivalent for non-Node stacks like cargo run, go run). |
| Service list | References assume api+web+storybook+ttyd. Generate only the services in SELECTED_SERVICES from Step 6. |
| Cross-script calls | References call each other by their long names (e.g. dev-servers-status.sh). Generated scripts must use the short names listed below. |
Reference → generated name mapping
| Reference file (pattern source) | Generated script name |
|---|---|
dev-servers-status.sh | dev-status.sh |
dev-stop-all-servers.sh | dev-stop.sh |
dev-tmux-start.sh / dev-concurrently.sh | dev-start.sh |
dev-restart-all-servers.sh | dev-restart.sh |
Utility scripts keep their names: dev-read-ports.sh, dev-session-name.sh, dev-allocate-ports.sh, post-checkout.sh.
Reference loading (progressive — load only what you need)
Always read first (core patterns):
$SKILL_DIR/reference/dev-read-ports.sh— port reading utility (sourced, no strict mode)$SKILL_DIR/reference/dev-allocate-ports.sh— pool-based port allocator (20000-29999)$SKILL_DIR/reference/dev-session-name.sh— tmux session naming$SKILL_DIR/reference/dev-servers-status.sh— KEY=value status format$SKILL_DIR/reference/dev-stop-all-servers.sh— stop pattern$SKILL_DIR/reference/dev-restart-all-servers.sh— restart pattern$SKILL_DIR/reference/post-checkout.sh— dependency install after clone
Read if RUNNER includes tmux:
$SKILL_DIR/reference/dev-tmux-start.sh— tmux session with pane titles$SKILL_DIR/reference/dev-tmux.sh— tmux helper utilities
Read if RUNNER includes concurrently:
$SKILL_DIR/reference/dev-concurrently.sh— concurrently runner
Read if CHROME = "Yes — create profile":
$SKILL_DIR/reference/chrome-profile-setup.sh— Chrome profile setup$SKILL_DIR/reference/dev-open-browser.sh— browser open
Do not call directly (inline the pattern):
$SKILL_DIR/reference/dev-check-ports.sh— inline thelsof -nP -tiTCP:"$PORT" -sTCP:LISTENcheck indev-start.shinstead of calling this script. On collision, suggest runningdev-allocate-ports.shfor a fresh block.
Scripts to generate
Always generate (in this order):
-
<SCRIPT_DIR>/dev-allocate-ports.sh— pool-based port allocator. Allocates N consecutive free ports from20000-29999. Used bydev-wt-ports.shand can be run standalone to reassign ports on collision. -
<SCRIPT_DIR>/dev-read-ports.sh— port reading utility (no strict mode, sourced only) -
<SCRIPT_DIR>/dev-session-name.sh— tmux session naming function (no strict mode, sourced only) -
<SCRIPT_DIR>/dev-status.sh— KEY=value status of all selected services + tmux + chrome -
<SCRIPT_DIR>/dev-stop.sh— kill server processes (lsof -ti + kill) + tmux kill-session -
<SCRIPT_DIR>/dev-start.sh— start all services:- If
RUNNER=tmux: create detached tmux session with one pane per service, set pane titles toservice:port - If
RUNNER=concurrently:concurrentlywith named processes - If
RUNNER=tmux + fallback: try tmux, fall back to concurrently if not on PATH - If
RUNNER=manual only: print usage instructions only
- If
-
<SCRIPT_DIR>/dev-restart.sh— resolve sibling path via BASH_SOURCE[0], then call stop and start:#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" bash "$SCRIPT_DIR/dev-stop.sh" exec bash "$SCRIPT_DIR/dev-start.sh" -
<SCRIPT_DIR>/post-checkout.sh— install deps using detected$PM
If GTR=yes, also generate:
<SCRIPT_DIR>/dev-wt-ports.sh— callsdev-allocate-ports.shto get a block of free ports from the20000-29999pool, writes them to.wt-ports.env. Uses$WORKTREE_PATH,$BRANCHenv vars (set by git gtr postCreate hook).
If CHROME = "Yes — create profile", also generate:
<SCRIPT_DIR>/chrome-profile-setup.sh— creates~/.chrome-profiles/<CHROME_PROFILE_NAME>+~/.local/bin/<CHROME_PROFILE_NAME>launcher, upsertsCHROME_PROFILE+CHROME_CDP_PORTin.env<SCRIPT_DIR>/dev-open-browser.sh— reads dev-status output, opens one tab per running service in Chrome profile
Step 9: Update package.json
Add to the scripts object using the Edit tool. Skip any key that already exists and note the conflict. Do not reformat the entire file — use a targeted edit on the scripts block only.
| Key | Value |
|---|---|
dev:start | bash <SCRIPT_DIR>/dev-start.sh |
dev:stop | bash <SCRIPT_DIR>/dev-stop.sh |
dev:restart | bash <SCRIPT_DIR>/dev-restart.sh |
dev:status | bash <SCRIPT_DIR>/dev-status.sh |
dev:browser | bash <SCRIPT_DIR>/dev-open-browser.sh |
dev:browser:setup | bash <SCRIPT_DIR>/chrome-profile-setup.sh |
Step 10: Update .env.example
Build the expected port variables from SELECTED_SERVICES and the ports detected in Step 3:
# Only include variables for selected services:
PORT=3000 # if api selected
WEB_PORT=5173 # if web selected
STORYBOOK_PORT=61000 # if storybook selected
TTYD_PORT=7681 # if ttyd selected
# Only if CHROME = "Yes — create profile":
CHROME_PROFILE=<CHROME_PROFILE_NAME>
CHROME_CDP_PORT=<CHROME_CDP_PORT>
If .env.example does not exist
Create it with a header:
# Generated by /dev-setup for <project-name>
# Copy to .env and adjust ports if running multiple worktrees
PORT=3000
WEB_PORT=5173
...
If .env.example already exists
For each expected variable:
-
Variable not present → append it at the end, under a section comment:
# --- dev-setup managed ports (added <date>) --- WEB_PORT=5173 -
Variable present with same value → skip (no change needed)
-
Variable present with different value → ask the user with
AskUserQuestion:question: ".env.example has PORT=8080 but detected service runs on :3000. Update?" options: - label: "Update to 3000" description: "Match detected service port" - label: "Keep 8080" description: "Preserve existing value"If the user chooses to update, replace the value in-place using the
Edittool. If keep, leave it and note the mismatch in the Step 13 summary.
Step 11: Update .gtrconfig (only if GTR=yes)
If .gtrconfig exists, append missing entries. If it doesn't exist, create it.
[copy]
include = .env.example
[hooks]
postCreate = bash <SCRIPT_DIR>/post-checkout.sh
postCreate = bash <SCRIPT_DIR>/dev-wt-ports.sh
Also create <SCRIPT_DIR>/gtr-setup.sh for team onboarding:
#!/bin/sh
# One-time setup: configure git gtr hooks for this project
# Run after: git clone + cd into repo
# Usage: bash <SCRIPT_DIR>/gtr-setup.sh
git config --add gtr.copy.include ".env.example"
git config --add gtr.hook.postCreate "bash <SCRIPT_DIR>/post-checkout.sh"
git config --add gtr.hook.postCreate "bash <SCRIPT_DIR>/dev-wt-ports.sh"
echo "✓ git gtr configured for this project"
Step 12: Adaptation review
Before running shellcheck, verify that each generated script is adapted to this project — not a copy of the reference. Read each generated file and confirm:
| Check | What to verify |
|---|---|
| PROJECT_DIR depth | Scripts that need the project root compute the correct .. depth for SCRIPT_DIR. E.g. tools/dev/ → ../.., scripts/ → .., bin/ → ... |
| Script names | Cross-script calls use generated names (dev-stop.sh, dev-start.sh, dev-status.sh) — NOT reference names (dev-stop-all-servers.sh, dev-tmux-start.sh, dev-servers-status.sh). |
| Service commands | dev-start.sh launch commands match detected $PM and actual package.json script names — not hardcoded pnpm dev:back / pnpm dev:front from references. |
| Ports | Services in dev-start.sh / dev-status.sh match SELECTED_SERVICES from Step 6. No hardcoded ports from the reference (e.g. 3001, 7777). |
| Package manager | post-checkout.sh uses $PM install command (e.g. npm install, pnpm install) — not hardcoded npm. |
| tmux session name | dev-start.sh calls dev_session_name (sourced from dev-session-name.sh) — not a hardcoded string. |
| Port vars | dev-read-ports.sh exports the same var names as detected in Step 3 (PORT, WEB_PORT, etc.) with correct defaults. |
| Port allocator | dev-allocate-ports.sh uses pool range 20000-29999 and lsof for availability checks. |
| Worktree env vars | dev-wt-ports.sh (if generated) calls dev-allocate-ports.sh for port allocation. Uses $WORKTREE_PATH and $BRANCH — not $PWD or a hardcoded path. |
| Chrome vars | chrome-profile-setup.sh (if generated) uses $CHROME_PROFILE_NAME and $CHROME_CDP_PORT from Step 7 — not reference defaults. |
| Sibling resolution | All internal cross-references between scripts use "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" to resolve siblings — not bare filenames. |
For each file that fails a check: fix it before proceeding to shellcheck.
Step 12b: Quality gates
After all scripts are written, run validation on every generated .sh file.
shellcheck (required — errors block completion):
find <SCRIPT_DIR> -name "*.sh" | sort | while read -r f; do
if shellcheck "$f"; then
echo "✓ $f"
else
echo "✗ $f — fix errors above before proceeding"
fi
done
errorlevel: must fix before continuing. Show the exact shellcheck line and suggestion.warning/infolevel: show to user but do not block.- If shellcheck is missing: print
⚠ shellcheck not found. Install: brew install shellcheckand continue (do not block).
shfmt (optional — apply if available, skip silently if not):
if command -v shfmt >/dev/null 2>&1; then
mapfile -t _sh_files < <(find <SCRIPT_DIR> -name "*.sh" | sort)
if [[ ${#_sh_files[@]} -gt 0 ]]; then
if shfmt -d "${_sh_files[@]}" | grep -q .; then
shfmt -w "${_sh_files[@]}"
echo "✓ shfmt formatting applied"
else
echo "✓ formatting already clean"
fi
fi
fi
Step 13: Output summary
Print the following summary. Replace <PM>, <SCRIPT_DIR>, and bracket-wrapped optional lines based on what was actually generated.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Dev setup complete
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Scripts generated in <SCRIPT_DIR>/:
✓ dev-allocate-ports.sh → allocate free ports from pool (20000-29999)
✓ dev-start.sh → <PM> dev:start
✓ dev-stop.sh → <PM> dev:stop
✓ dev-restart.sh → <PM> dev:restart
✓ dev-status.sh → <PM> dev:status
✓ post-checkout.sh → called on git clone / gtr new
[✓ chrome-profile-setup.sh → <PM> dev:browser:setup]
[✓ dev-open-browser.sh → <PM> dev:browser]
[✓ dev-wt-ports.sh → called by git gtr postCreate]
[✓ gtr-setup.sh → one-time team onboarding]
Config updated:
✓ package.json (scripts added: dev:start, dev:stop, dev:restart, dev:status)
✓ .env.example (port vars added)
[✓ .gtrconfig (postCreate hooks added)]
[✓ gtr-setup.sh (team onboarding script — run on each contributor's machine)]
Next steps:
1. cp .env.example .env (adjust ports if running multiple worktrees)
2. <PM> dev:start
[3. <PM> dev:browser:setup (first-time Chrome profile setup)]
[4. bash <SCRIPT_DIR>/gtr-setup.sh (on each team member's machine)]
If Context7 MCP tools (mcp__plugin_context7_context7__*) were NOT available during this session, also print:
Tip: Add Context7 for richer doc lookups next time:
claude mcp add --transport http context7 https://mcp.context7.com/mcp
(free, no API key required)