mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-08 08:36:37 +08:00
446 lines
13 KiB
Markdown
446 lines
13 KiB
Markdown
# 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
|
|
|
|
1. **Create a hook file**: `touch .dmux-hooks/worktree_created`
|
|
2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`
|
|
3. **Add shebang**: Start with `#!/bin/bash`
|
|
4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.
|
|
5. **Test it**: Set env vars manually and run the script
|
|
|
|
## Hook Execution Model
|
|
|
|
- **Mostly non-blocking**: Most hooks run in background (detached processes)
|
|
- **Bootstrap-gated**: `worktree_created` blocks agent launch so setup can finish first, with no fixed timeout
|
|
- **Live bootstrap output**: During `worktree_created`, stdout/stderr is streamed into the new pane's setup UI
|
|
- **Failure behavior**: Background hook errors are logged; gated hooks can abort the operation
|
|
- **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 worktree creation, before agent launch | 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
|
|
```bash
|
|
DMUX_ROOT="/path/to/project" # Project root directory
|
|
DMUX_SERVER_PORT="3142" # HTTP server port
|
|
```
|
|
|
|
### Pane Context (most hooks)
|
|
```bash
|
|
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
|
|
```bash
|
|
DMUX_WORKTREE_PATH="/path/.dmux/worktrees/fix-auth-bug"
|
|
DMUX_BRANCH="fix-auth-bug" # Same as slug
|
|
```
|
|
|
|
### Bootstrap Progress Context
|
|
```bash
|
|
DMUX_PROGRESS="1" # Set when output is shown in the new pane setup UI
|
|
DMUX_STATUS_PREFIX="DMUX_STATUS:" # Prefix for clean status messages
|
|
```
|
|
|
|
`worktree_created` can emit progress while it runs. Any stdout/stderr line is shown in the setup UI; lines prefixed with `$DMUX_STATUS_PREFIX` are displayed without the prefix.
|
|
|
|
```bash
|
|
status() {
|
|
if [ "${DMUX_PROGRESS:-0}" = "1" ]; then
|
|
echo "${DMUX_STATUS_PREFIX:-DMUX_STATUS:} $*"
|
|
else
|
|
echo "[Hook] $*"
|
|
fi
|
|
}
|
|
```
|
|
|
|
### Merge Context
|
|
```bash
|
|
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
|
|
```bash
|
|
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
|
|
```bash
|
|
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
|
|
```bash
|
|
#!/bin/bash
|
|
# .dmux-hooks/worktree_created
|
|
|
|
cd "$DMUX_WORKTREE_PATH"
|
|
|
|
status() {
|
|
if [ "${DMUX_PROGRESS:-0}" = "1" ]; then
|
|
echo "${DMUX_STATUS_PREFIX:-DMUX_STATUS:} $*"
|
|
else
|
|
echo "[Hook] $*"
|
|
fi
|
|
}
|
|
|
|
if [ -f "pnpm-lock.yaml" ]; then
|
|
status "Installing dependencies with pnpm"
|
|
pnpm install --prefer-offline
|
|
elif [ -f "package-lock.json" ]; then
|
|
status "Installing dependencies with npm"
|
|
npm install
|
|
elif [ -f "yarn.lock" ]; then
|
|
status "Installing dependencies with yarn"
|
|
yarn install
|
|
elif [ -f "Gemfile" ]; then
|
|
status "Installing gems"
|
|
bundle install
|
|
elif [ -f "requirements.txt" ]; then
|
|
status "Installing Python dependencies"
|
|
pip install -r requirements.txt
|
|
elif [ -f "Cargo.toml" ]; then
|
|
status "Building Rust project"
|
|
cargo build
|
|
fi
|
|
```
|
|
|
|
### Pattern 2: Copy Configuration
|
|
```bash
|
|
#!/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
|
|
```bash
|
|
#!/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
|
|
```bash
|
|
#!/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
|
|
```bash
|
|
#!/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
|
|
|
|
1. **Always start with shebang**: `#!/bin/bash`
|
|
2. **Set error handling**: `set -e` (exit on error)
|
|
3. **Make executable**: `chmod +x .dmux-hooks/hook_name`
|
|
4. **Background long operations**: Append `&` to avoid blocking
|
|
5. **Check for required tools**: `command -v tool &> /dev/null`
|
|
6. **Log for debugging**: `echo "[Hook] message" >> "$DMUX_ROOT/.dmux/hooks.log"`
|
|
7. **Handle missing vars gracefully**: `[ -z "$VAR" ] && exit 0`
|
|
8. **Use silent curl**: `curl -s` to avoid noise in logs
|
|
9. **Clean up temp files**: Remove files in `/tmp/`
|
|
10. **Test before committing**: Run hooks manually with mock env vars
|
|
|
|
## Testing Hooks
|
|
|
|
### Manual Testing
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# Check for syntax errors without running
|
|
bash -n ./.dmux-hooks/worktree_created
|
|
```
|
|
|
|
### Shellcheck (if available)
|
|
```bash
|
|
shellcheck ./.dmux-hooks/worktree_created
|
|
```
|
|
|
|
## Project Context Analysis
|
|
|
|
Before creating hooks, analyze these files in the project:
|
|
|
|
### Package Manager Detection
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# Read package.json to find test command
|
|
cat package.json | grep '"test"'
|
|
# Or with jq:
|
|
jq -r '.scripts.test' package.json
|
|
```
|
|
|
|
### Dev Command Discovery
|
|
```bash
|
|
# Read package.json to find dev command
|
|
cat package.json | grep '"dev"'
|
|
# Or with jq:
|
|
jq -r '.scripts.dev' package.json
|
|
```
|
|
|
|
### Environment Variables
|
|
```bash
|
|
# Check for .env files to copy
|
|
ls -la | grep '.env'
|
|
```
|
|
|
|
### Build System
|
|
```bash
|
|
# 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:
|
|
|
|
1. **Check if file exists**: `ls -la .dmux-hooks/`
|
|
2. **Check permissions**: Should show `x` in `rwxr-xr-x`
|
|
3. **Check syntax**: `bash -n .dmux-hooks/hook_name`
|
|
4. **Test manually**: Set env vars and run
|
|
5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix
|
|
6. **Simplify**: Remove complex parts, test basic version
|
|
7. **Check tool availability**: `command -v required_tool`
|
|
|
|
### Debug Mode
|
|
```bash
|
|
#!/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 -e` for error handling
|
|
- [ ] Use environment variables (never hardcode paths)
|
|
- [ ] Keep blocking hooks chatty with status output
|
|
- [ ] Background long operations with `&` only when the hook does not gate the operation
|
|
- [ ] 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.md` in project root
|
|
- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`
|
|
- **Examples**: Check `.dmux-hooks/examples/` directory
|
|
- **dmux API**: See `API.md` for REST endpoints
|
|
|
|
---
|
|
|
|
*This documentation was auto-generated from dmux source code.*
|
|
*Version: 2026-05-25*
|