Imported from formkit/drag-and-drop (
.dmux-hooks/AGENTS.md). Install upstream withnpx skills add formkit/drag-and-drop --skill .dmux-hooks. Copyright stays with the author.
dmux Hooks System - Agent Reference
Auto-generated documentation for AI agents
This document contains everything an AI agent needs to create, modify, and understand dmux hooks. It is automatically generated from the dmux source code and embedded in the binary.
What You're Working On
You are editing hooks for dmux, a tmux pane manager that creates AI-powered development workflows. Each pane runs in its own git worktree with an AI agent.
Your Goal
Create executable bash scripts in .dmux-hooks/ that run automatically at key lifecycle events.
Quick Start
- Create a hook file:
touch .dmux-hooks/worktree_created - Make it executable:
chmod +x .dmux-hooks/worktree_created - Add shebang: Start with
#!/bin/bash - Use environment variables: Access
$DMUX_ROOT,$DMUX_WORKTREE_PATH, etc. - Test it: Set env vars manually and run the script
Hook Execution Model
- Non-blocking: Hooks run in background (detached processes)
- Silent failures: Hook errors are logged but don't stop dmux
- Environment-based: All context passed via environment variables
- Version controlled: Hooks in
.dmux-hooks/are shared with team - Priority resolution:
.dmux-hooks/→.dmux/hooks/→~/.dmux/hooks/
Available Hooks
Pane Lifecycle Hooks
| Hook | When | Common Use Cases |
|---|---|---|
before_pane_create |
Before pane creation | Validation, notifications, pre-flight checks |
pane_created |
After pane, before worktree | Configure tmux settings, prepare environment |
worktree_created |
After full setup | Install deps, copy configs, setup git |
before_pane_close |
Before closing | Save state, backup uncommitted work |
pane_closed |
After closed | Cleanup resources, analytics, notifications |
Worktree Lifecycle Hooks
| Hook | When | Common Use Cases |
|---|---|---|
before_worktree_remove |
Before worktree removal | Archive worktree, save artifacts |
worktree_removed |
After worktree removed | Cleanup external references |
Merge Lifecycle Hooks
| Hook | When | Common Use Cases |
|---|---|---|
pre_merge |
Before merge operation | Run final tests, create backups |
post_merge |
After successful merge | Deploy, close issues, notify team |
Interactive Hooks (with HTTP callbacks)
| Hook | When | Common Use Cases |
|---|---|---|
run_test |
When tests triggered | Run test suite, report status via HTTP |
run_dev |
When dev server triggered | Start dev server, create tunnel, report URL |
Environment Variables
Always Available
DMUX_ROOT="/path/to/project" # Project root directory
DMUX_SERVER_PORT="3142" # HTTP server port
Pane Context (most hooks)
DMUX_PANE_ID="dmux-1234567890" # dmux pane identifier
DMUX_SLUG="fix-auth-bug" # Branch/worktree name
DMUX_PROMPT="Fix authentication bug" # User's prompt
DMUX_AGENT="claude" # Agent type (registry id, e.g. claude, codex, opencode)
DMUX_TMUX_PANE_ID="%38" # tmux pane ID
Worktree Context
DMUX_WORKTREE_PATH="/path/.dmux/worktrees/fix-auth-bug"
DMUX_BRANCH="fix-auth-bug" # Same as slug
Merge Context
DMUX_TARGET_BRANCH="main" # Branch being merged into
HTTP Callback API
Interactive hooks (run_test and run_dev) can update dmux UI via HTTP.
Update Test Status
curl -X PUT "http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test" -H "Content-Type: application/json" -d '{"status": "running", "output": "optional test output"}'
# Status values: "running" | "passed" | "failed"
Update Dev Server
curl -X PUT "http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev" -H "Content-Type: application/json" -d '{"status": "running", "url": "http://localhost:3000"}'
# Status values: "running" | "stopped"
# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)
Common Patterns
Pattern 1: Install Dependencies
#!/bin/bash
# .dmux-hooks/worktree_created
cd "$DMUX_WORKTREE_PATH"
if [ -f "pnpm-lock.yaml" ]; then
pnpm install --prefer-offline &
elif [ -f "package-lock.json" ]; then
npm install &
elif [ -f "yarn.lock" ]; then
yarn install &
elif [ -f "Gemfile" ]; then
bundle install &
elif [ -f "requirements.txt" ]; then
pip install -r requirements.txt &
elif [ -f "Cargo.toml" ]; then
cargo build &
fi
Pattern 2: Copy Configuration
#!/bin/bash
# .dmux-hooks/worktree_created
# Copy environment file
if [ -f "$DMUX_ROOT/.env.local" ]; then
cp "$DMUX_ROOT/.env.local" "$DMUX_WORKTREE_PATH/.env.local"
fi
# Copy other config files
for file in .env.development .npmrc .yarnrc; do
if [ -f "$DMUX_ROOT/$file" ]; then
cp "$DMUX_ROOT/$file" "$DMUX_WORKTREE_PATH/$file"
fi
done
Pattern 3: Run Tests with Status Updates
#!/bin/bash
# .dmux-hooks/run_test
set -e
cd "$DMUX_WORKTREE_PATH"
API="http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test"
# Update: starting
curl -s -X PUT "$API" -H "Content-Type: application/json" -d '{"status": "running"}' > /dev/null
# Run tests and capture output
OUTPUT_FILE="/tmp/dmux-test-$DMUX_PANE_ID.txt"
if pnpm test > "$OUTPUT_FILE" 2>&1; then
STATUS="passed"
else
STATUS="failed"
fi
# Get output (truncate if too long)
OUTPUT=$(head -c 5000 "$OUTPUT_FILE")
# Update: complete
curl -s -X PUT "$API" -H "Content-Type: application/json" -d "$(jq -n --arg status "$STATUS" --arg output "$OUTPUT" '{status: $status, output: $output}')" > /dev/null
rm -f "$OUTPUT_FILE"
Pattern 4: Dev Server with Tunnel
#!/bin/bash
# .dmux-hooks/run_dev
set -e
cd "$DMUX_WORKTREE_PATH"
API="http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev"
# Start dev server in background
LOG_FILE="/tmp/dmux-dev-$DMUX_PANE_ID.log"
pnpm dev > "$LOG_FILE" 2>&1 &
DEV_PID=$!
# Wait for server to start
sleep 5
# Detect port from logs
PORT=$(grep -oP 'localhost:Kd+' "$LOG_FILE" | head -1)
[ -z "$PORT" ] && PORT=3000
# Optional: Create tunnel with cloudflared
if command -v cloudflared &> /dev/null; then
TUNNEL=$(cloudflared tunnel --url "http://localhost:$PORT" 2>&1 | grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)
URL="${TUNNEL:-http://localhost:$PORT}"
else
URL="http://localhost:$PORT"
fi
# Report status
curl -s -X PUT "$API" -H "Content-Type: application/json" -d "{"status": "running", "url": "$URL"}" > /dev/null
echo "[Hook] Dev server running at $URL (PID: $DEV_PID)"
Pattern 5: Post-Merge Deployment
#!/bin/bash
# .dmux-hooks/post_merge
set -e
cd "$DMUX_ROOT"
# Only deploy from main/master
if [ "$DMUX_TARGET_BRANCH" != "main" ] && [ "$DMUX_TARGET_BRANCH" != "master" ]; then
exit 0
fi
# Push to remote
git push origin "$DMUX_TARGET_BRANCH"
# Trigger deployment (example: Vercel)
if [ -n "$VERCEL_TOKEN" ]; then
curl -s -X POST "https://api.vercel.com/v1/deployments" -H "Authorization: Bearer $VERCEL_TOKEN" -H "Content-Type: application/json" -d '{"name": "my-project"}' > /dev/null
fi
# Close GitHub issue if prompt contains #123
ISSUE=$(echo "$DMUX_PROMPT" | grep -oP '#Kd+' | head -1)
if [ -n "$ISSUE" ] && command -v gh &> /dev/null; then
gh issue close "$ISSUE" -c "Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH" 2>/dev/null || true
fi
Best Practices
- Always start with shebang:
#!/bin/bash - Set error handling:
set -e(exit on error) - Make executable:
chmod +x .dmux-hooks/hook_name - Background long operations: Append
&to avoid blocking - Check for required tools:
command -v tool &> /dev/null - Log for debugging:
echo "[Hook] message" >> "$DMUX_ROOT/.dmux/hooks.log" - Handle missing vars gracefully:
[ -z "$VAR" ] && exit 0 - Use silent curl:
curl -sto avoid noise in logs - Clean up temp files: Remove files in
/tmp/ - Test before committing: Run hooks manually with mock env vars
Testing Hooks
Manual Testing
# 1. Set environment variables
export DMUX_ROOT="$(pwd)"
export DMUX_PANE_ID="test-pane"
export DMUX_SLUG="test-branch"
export DMUX_WORKTREE_PATH="$(pwd)"
export DMUX_SERVER_PORT="3142"
export DMUX_AGENT="claude"
export DMUX_PROMPT="Test prompt"
# 2. Run hook directly
./.dmux-hooks/worktree_created
# 3. Check exit code
echo $? # Should be 0 for success
Syntax Check
# Check for syntax errors without running
bash -n ./.dmux-hooks/worktree_created
Shellcheck (if available)
shellcheck ./.dmux-hooks/worktree_created
Project Context Analysis
Before creating hooks, analyze these files in the project:
Package Manager Detection
# Check which package manager is used
if [ -f "pnpm-lock.yaml" ]; then
# Use: pnpm install, pnpm test, pnpm dev
elif [ -f "package-lock.json" ]; then
# Use: npm install, npm test, npm run dev
elif [ -f "yarn.lock" ]; then
# Use: yarn install, yarn test, yarn dev
fi
Test Command Discovery
# Read package.json to find test command
cat package.json | grep '"test"'
# Or with jq:
jq -r '.scripts.test' package.json
Dev Command Discovery
# Read package.json to find dev command
cat package.json | grep '"dev"'
# Or with jq:
jq -r '.scripts.dev' package.json
Environment Variables
# Check for .env files to copy
ls -la | grep '.env'
Build System
# Detect build system
if [ -f "vite.config.ts" ]; then
# Vite project
elif [ -f "next.config.js" ]; then
# Next.js project
elif [ -f "nuxt.config.ts" ]; then
# Nuxt project
fi
Common Mistakes to Avoid
❌ Blocking operations: sleep 60 (blocks dmux)
✅ Background long tasks: slow_operation &
❌ Hardcoded paths: /Users/me/project
✅ Use variables: "$DMUX_ROOT"
❌ Assuming tools exist: pnpm install
✅ Check first: command -v pnpm && pnpm install
❌ No error handling: Script fails silently
✅ Set error mode: set -e or check exit codes
❌ Forgetting executable bit: Hook won't run
✅ Make executable: chmod +x
❌ Noisy output: Clutters dmux logs
✅ Silent operations: curl -s, > /dev/null 2>&1
❌ Not testing: Deploy and hope ✅ Test manually: Run with mock env vars first
Debugging
If a hook isn't working:
- Check if file exists:
ls -la .dmux-hooks/ - Check permissions: Should show
xinrwxr-xr-x - Check syntax:
bash -n .dmux-hooks/hook_name - Test manually: Set env vars and run
- Check logs: dmux logs to stderr with
[Hooks]prefix - Simplify: Remove complex parts, test basic version
- Check tool availability:
command -v required_tool
Debug Mode
#!/bin/bash
# Add to top of hook for debugging
set -x # Print each command before executing
set -e # Exit on error
# Your hook logic here
Summary Checklist
When creating a new hook:
- Create file in
.dmux-hooks/ - Add shebang:
#!/bin/bash - Make executable:
chmod +x - Add
set -efor error handling - Use environment variables (never hardcode paths)
- Background long operations with
& - Check for required tools before using
- Test manually with mock env vars
- Add comments explaining what it does
- Commit to version control
Getting Help
- Full documentation: See
HOOKS.mdin project root - Claude-specific tips: See
CLAUDE.mdin.dmux-hooks/ - Examples: Check
.dmux-hooks/examples/directory - dmux API: See
API.mdfor REST endpoints
This documentation was auto-generated from dmux source code. Version: 2026-03-13