From df3bcd3d1915da015160ad2d13325d300036a513 Mon Sep 17 00:00:00 2001 From: ryan Date: Sun, 21 Jun 2026 14:51:08 +0800 Subject: [PATCH] perf --- .dmux-hooks/AGENTS.md | 445 ------------------ .dmux-hooks/CLAUDE.md | 445 ------------------ .dmux-hooks/README.md | 53 --- .dmux-hooks/examples/post_merge.example | 66 --- .dmux-hooks/examples/run_dev.example | 62 --- .dmux-hooks/examples/run_test.example | 61 --- .dmux-hooks/examples/worktree_created.example | 48 -- Claude.md | 1 - 8 files changed, 1181 deletions(-) delete mode 100644 .dmux-hooks/AGENTS.md delete mode 100644 .dmux-hooks/CLAUDE.md delete mode 100644 .dmux-hooks/README.md delete mode 100755 .dmux-hooks/examples/post_merge.example delete mode 100755 .dmux-hooks/examples/run_dev.example delete mode 100755 .dmux-hooks/examples/run_test.example delete mode 100755 .dmux-hooks/examples/worktree_created.example delete mode 120000 Claude.md diff --git a/.dmux-hooks/AGENTS.md b/.dmux-hooks/AGENTS.md deleted file mode 100644 index 1a5001bb..00000000 --- a/.dmux-hooks/AGENTS.md +++ /dev/null @@ -1,445 +0,0 @@ -# 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* diff --git a/.dmux-hooks/CLAUDE.md b/.dmux-hooks/CLAUDE.md deleted file mode 100644 index 1a5001bb..00000000 --- a/.dmux-hooks/CLAUDE.md +++ /dev/null @@ -1,445 +0,0 @@ -# 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* diff --git a/.dmux-hooks/README.md b/.dmux-hooks/README.md deleted file mode 100644 index 202222ee..00000000 --- a/.dmux-hooks/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# dmux Hooks - -This directory contains hooks that run automatically at key lifecycle events in dmux. - -## Quick Start - -1. **Read the documentation**: - - `AGENTS.md` - Complete reference (for any AI agent) - - `CLAUDE.md` - Same content (Claude Code looks for this filename) - -2. **Check examples**: - - `examples/` directory contains starter templates - -3. **Create a hook**: - ```bash - touch worktree_created - chmod +x worktree_created - nano worktree_created - ``` - -4. **Test it**: - ```bash - export DMUX_ROOT="$(pwd)" - export DMUX_WORKTREE_PATH="$(pwd)" - ./worktree_created - ``` - -## Available Hooks - -- `before_pane_create` - Before pane creation -- `pane_created` - After pane created -- `worktree_created` - After worktree setup -- `before_pane_close` - Before closing -- `pane_closed` - After closed -- `before_worktree_remove` - Before worktree removal -- `worktree_removed` - After worktree removed -- `pre_merge` - Before merge -- `post_merge` - After merge -- `run_test` - When running tests -- `run_dev` - When starting dev server - -## Documentation - -See `AGENTS.md` or `CLAUDE.md` for complete documentation including: -- Environment variables -- HTTP callback API -- Common patterns -- Best practices -- Testing strategies - -## Note - -This directory is **version controlled**. Hooks you create here will be shared with your team. diff --git a/.dmux-hooks/examples/post_merge.example b/.dmux-hooks/examples/post_merge.example deleted file mode 100755 index 9b5c58d1..00000000 --- a/.dmux-hooks/examples/post_merge.example +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/bash -# Example: post_merge hook -# -# This hook runs after a successful merge into the target branch. -# Use it to trigger deployments, close issues, notify teams, etc. - -set -e - -echo "[Hook] Post-merge processing for $DMUX_SLUG → $DMUX_TARGET_BRANCH" - -cd "$DMUX_ROOT" - -# Push to remote if merging to main/master -if [ "$DMUX_TARGET_BRANCH" = "main" ] || [ "$DMUX_TARGET_BRANCH" = "master" ]; then - echo "[Hook] Pushing to origin/$DMUX_TARGET_BRANCH" - git push origin "$DMUX_TARGET_BRANCH" - - # Optional: Trigger deployment - # if [ -n "$VERCEL_TOKEN" ]; then - # echo "[Hook] Triggering Vercel deployment..." - # curl -X POST "https://api.vercel.com/v1/deployments" \ - # -H "Authorization: Bearer $VERCEL_TOKEN" \ - # -H "Content-Type: application/json" \ - # -d '{ - # "name": "my-project", - # "gitSource": { - # "type": "github", - # "ref": "main" - # } - # }' - # fi -fi - -# Close related GitHub issue (if prompt contains #123 format) -ISSUE_NUM=$(echo "$DMUX_PROMPT" | grep -oP '#\K\d+' | head -1) -if [ -n "$ISSUE_NUM" ]; then - echo "[Hook] Closing GitHub issue #$ISSUE_NUM" - if command -v gh &> /dev/null; then - gh issue close "$ISSUE_NUM" \ - -c "Resolved in branch $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH" \ - 2>/dev/null || echo "[Hook] Warning: Failed to close issue (maybe already closed?)" - else - echo "[Hook] GitHub CLI (gh) not found, skipping issue close" - fi -fi - -# Send notification to Slack -# if [ -n "$SLACK_WEBHOOK" ]; then -# echo "[Hook] Sending Slack notification" -# curl -s -X POST "$SLACK_WEBHOOK" \ -# -H "Content-Type: application/json" \ -# -d "{ -# \"text\": \"Merged: $DMUX_SLUG → $DMUX_TARGET_BRANCH\", -# \"blocks\": [ -# { -# \"type\": \"section\", -# \"text\": { -# \"type\": \"mrkdwn\", -# \"text\": \"*Branch Merged* :rocket:\n\n*From:* \`$DMUX_SLUG\`\n*To:* \`$DMUX_TARGET_BRANCH\`\n*Task:* $DMUX_PROMPT\" -# } -# } -# ] -# }" > /dev/null -# fi - -echo "[Hook] Post-merge processing complete" diff --git a/.dmux-hooks/examples/run_dev.example b/.dmux-hooks/examples/run_dev.example deleted file mode 100755 index c2005f43..00000000 --- a/.dmux-hooks/examples/run_dev.example +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash -# Example: run_dev hook -# -# This hook starts a dev server and optionally creates a tunnel for sharing. -# It reports the server URL back to dmux via the HTTP API. - -set -e - -echo "[Hook] Starting dev server for $DMUX_SLUG" - -cd "$DMUX_WORKTREE_PATH" -API_URL="http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev" - -# Update status: starting -curl -s -X PUT "$API_URL" \ - -H "Content-Type: application/json" \ - -d '{"status": "running"}' > /dev/null - -# Start dev server in background -# Adjust the command for your project (pnpm dev, npm run dev, vite, etc.) -LOG_FILE="/tmp/dmux-dev-$DMUX_PANE_ID.log" -pnpm dev > "$LOG_FILE" 2>&1 & -DEV_PID=$! - -# Wait for server to be ready -echo "[Hook] Waiting for dev server to start..." -sleep 5 - -# Detect port from log output -# Adjust the grep pattern for your dev server's output format -PORT=$(grep -oP '(?<=localhost:)\d+' "$LOG_FILE" | head -1) - -if [ -z "$PORT" ]; then - echo "[Hook] Warning: Could not detect port from logs, using default 3000" - PORT=3000 -fi - -LOCAL_URL="http://localhost:$PORT" -echo "[Hook] Dev server running at $LOCAL_URL" - -# Optional: Create a public tunnel (uncomment to enable) -# Requires ngrok, cloudflared, or another tunneling tool - -# Example with cloudflared: -# TUNNEL_URL=$(cloudflared tunnel --url "$LOCAL_URL" 2>&1 | \ -# grep -oP 'https://[a-z0-9-]+\.trycloudflare\.com' | head -1) - -# Example with ngrok: -# TUNNEL_URL=$(ngrok http $PORT --log=stdout 2>&1 | \ -# grep -oP 'url=https://[^"]+' | head -1 | cut -d= -f2) - -# For now, just use local URL (uncomment tunnel code above to enable) -FINAL_URL="$LOCAL_URL" - -# Report status back to dmux -curl -s -X PUT "$API_URL" \ - -H "Content-Type: application/json" \ - -d "{\"status\": \"running\", \"url\": \"$FINAL_URL\"}" > /dev/null - -echo "[Hook] Dev server ready at: $FINAL_URL" -echo "[Hook] Dev server PID: $DEV_PID" -echo "[Hook] Log file: $LOG_FILE" diff --git a/.dmux-hooks/examples/run_test.example b/.dmux-hooks/examples/run_test.example deleted file mode 100755 index 6b286a1c..00000000 --- a/.dmux-hooks/examples/run_test.example +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -# Example: run_test hook -# -# This hook runs tests and reports the status back to dmux via the HTTP API. -# Status updates appear in real-time in the dmux UI. - -set -e - -echo "[Hook] Running tests for $DMUX_SLUG" - -cd "$DMUX_WORKTREE_PATH" -API_URL="http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test" - -# Update status: running -curl -s -X PUT "$API_URL" \ - -H "Content-Type: application/json" \ - -d '{"status": "running"}' > /dev/null - -echo "[Hook] Running test suite..." - -# Capture test output -OUTPUT_FILE="/tmp/dmux-test-$DMUX_PANE_ID.txt" - -# Run tests (adjust command for your project) -# Examples: -# - pnpm test -# - npm test -# - vitest run -# - jest -# - pytest -# - cargo test -if pnpm test > "$OUTPUT_FILE" 2>&1; then - STATUS="passed" - echo "[Hook] Tests passed ✓" -else - STATUS="failed" - echo "[Hook] Tests failed ✗" -fi - -# Get output (truncate if too long) -OUTPUT=$(head -c 5000 "$OUTPUT_FILE") - -# Report results back to dmux -curl -s -X PUT "$API_URL" \ - -H "Content-Type: application/json" \ - -d "$(jq -n \ - --arg status "$STATUS" \ - --arg output "$OUTPUT" \ - '{status: $status, output: $output}')" > /dev/null - -# Cleanup -rm -f "$OUTPUT_FILE" - -echo "[Hook] Test results reported to dmux" - -# Exit with test status -if [ "$STATUS" = "passed" ]; then - exit 0 -else - exit 1 -fi diff --git a/.dmux-hooks/examples/worktree_created.example b/.dmux-hooks/examples/worktree_created.example deleted file mode 100755 index 61c2979b..00000000 --- a/.dmux-hooks/examples/worktree_created.example +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -# Example: worktree_created hook -# -# This hook runs after a new worktree is created and before the agent launches. -# Use it to set up the worktree environment (install deps, copy configs, etc.) -# Stdout/stderr is streamed into the new pane's setup UI while this hook runs. -# dmux waits for this hook without a fixed timeout. - -set -e # Exit on error - -status() { - if [ "${DMUX_PROGRESS:-0}" = "1" ]; then - echo "${DMUX_STATUS_PREFIX:-DMUX_STATUS:} $*" - else - echo "[Hook] $*" - fi -} - -status "Setting up worktree: $DMUX_SLUG" - -cd "$DMUX_WORKTREE_PATH" - -# Install dependencies before the agent launches. -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 -fi - -# Copy environment file if it exists -if [ -f "$DMUX_ROOT/.env.local" ]; then - status "Copying .env.local" - cp "$DMUX_ROOT/.env.local" "$DMUX_WORKTREE_PATH/.env.local" -fi - -# Keep existing git author identity. -# Do not set git user.name/user.email in this hook. - -# Create a log entry -echo "[$(date)] Created worktree: $DMUX_SLUG | Agent: $DMUX_AGENT | Prompt: $DMUX_PROMPT" \ - >> "$DMUX_ROOT/.dmux/worktree_history.log" - -status "Worktree setup complete" diff --git a/Claude.md b/Claude.md deleted file mode 120000 index 96808718..00000000 --- a/Claude.md +++ /dev/null @@ -1 +0,0 @@ -Agents.md \ No newline at end of file