{"owner":"formkit","repo":"auto-animate","hasSkills":true,"totalSkillsCount":3,"totalTokensCount":9426,"categories":["claude-rule","root-instruction","cursor-rule"],"hasMcp":false,"mcpConfig":null,"found":[".dmux-hooks/CLAUDE.md",".dmux-hooks/AGENTS.md",".cursorrules"],"skills":{".dmux-hooks/CLAUDE.md":"# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis 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.\n\n## What You're Working On\n\nYou 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 (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  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\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n",".dmux-hooks/AGENTS.md":"# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis 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.\n\n## What You're Working On\n\nYou 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 (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  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\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n",".cursorrules":"### Project overview\n- **Purpose**: Zero-config motion for DOM list changes. Given a parent element, automatically animates its immediate children when they are added, removed, or moved.\n- **Core entry**: `src/index.ts` exports `autoAnimate(el, configOrPlugin)` and types, plus a Vue directive `vAutoAnimate`.\n- **Framework adapters**: Thin wrappers providing hooks/directives for Vue, React, Solid, Preact, Angular, Qwik under `src/<framework>/index.ts`.\n- **Build**: TS → ESM via Rollup; built artifacts land in `dist/`. Package `exports` map serves `index.mjs` and framework subpaths.\n\n### Mental model (how it works)\n- **Observers**\n  - MutationObserver on the parent watches childList changes. For each mutation, `getElements` collects the parent and child elements that changed (including removed ones), tagging each with a non-enumerable target marker `__aa_tgt`.\n  - ResizeObserver on `document.documentElement` (root) and on each observed element updates cached positions when sizes change.\n  - IntersectionObserver is created per element to detect position changes while scrolling; a dynamic `rootMargin` derived from the last known coordinates triggers recalculation. Occasional low-priority polling backs this up.\n- **State tracking** (all with WeakMap/WeakSet to avoid leaks)\n  - `coords`: last known absolute coordinates adjusted for nearest scrolling ancestor.\n  - `siblings`: prev/next references for elements removed from the DOM so they can be temporarily reinserted for exit animations.\n  - `animations`: current Web Animations API `Animation` per element.\n  - `intersections`, `intervals`, `debounces`: observers/intervals/timers per element.\n  - `options`: the per-parent config or plugin; `enabled`: parents with animations enabled.\n- **Deciding what to animate**\n  - `animate(el)` selects one of: `add`, `remain`, or `remove` based on whether the element has cached coordinates and whether it’s still connected.\n  - Flags: `__aa_new` marks newly reinserted nodes to ensure their next cycle is treated as an entry; `__aa_del` marks nodes currently animating out.\n- **Effects**\n  - Default behavior uses Web Animations API: `add` scales/ fades in; `remain` uses FLIP-style transforms (translate from old to new), also animates width/height deltas respecting `box-sizing`; `remove` temporarily re-inserts the node near its original position, absolutely positions it, and scales/fades out.\n  - Plugins: if `config` is a function, it’s treated as an `AutoAnimationPlugin`. Plugin functions return a `KeyframeEffect` or `[KeyframeEffect, { styleReset | false }]`. The library will construct and play an `Animation` from the effect and handle optional style resets.\n- **Scroll handling**\n  - Coordinates are absolute plus the nearest scrollable ancestor’s offset.\n  - During removal at page bottom, the library compensates for scroll jumps by snapshotting pre/post window scroll and performing a manual scroll animation to match the configured duration/easing.\n- **Options & accessibility**\n  - Defaults: `{ duration: 250, easing: 'ease-in-out' }`.\n  - Respects `prefers-reduced-motion: reduce` unless `disrespectUserMotionPreference: true` or a plugin is used.\n  - `AnimationController` controls enable/disable per parent.\n\n### Public API surface\n- **Default export**: `autoAnimate(el: HTMLElement, config?: Partial<AutoAnimateOptions> | AutoAnimationPlugin): AnimationController`\n- **Types**: `AutoAnimateOptions`, `AutoAnimationPlugin`, `AutoAnimationPluginOptions`, `AnimationController`.\n- **Vue**: `vAutoAnimate` directive, `autoAnimatePlugin`, `useAutoAnimate` hook.\n- **React/Preact**: `useAutoAnimate` hook returns `[refCallback | refObject, setEnabled]`.\n- **Solid**: `createAutoAnimate` and `createAutoAnimateDirective`.\n- **Angular**: `AutoAnimateDirective` (`[auto-animate]`).\n- **Qwik**: `useAutoAnimate` returns `[Signal<HTMLElement>, setEnabledQRL]`.\n- **Nuxt**: module that registers the Vue directive and auto-imports `autoAnimate` and `useAutoAnimate`.\n\n### Key invariants and conventions\n- Do not mutate children beyond adding style resets for exit animations; remove those styles in `cleanUp`.\n- All per-node state is in WeakMaps/Sets to prevent memory leaks; never store strong references to DOM nodes outside these registries.\n- Only immediate children of the target parent are animated; traversal utilities (`forEach`) visit the parent first, then its children.\n- Always schedule position updates after animations finish (`animation.addEventListener('finish', updatePos)`), and debounce updates using the current duration (or 500ms for plugins).\n- When computing size deltas, use `getTransitionSizes` to respect `box-sizing: content-box` padding/borders.\n- Tagging properties `__aa_tgt`, `__aa_del`, `__aa_new` are non-enumerable and must be defined via `Object.defineProperty`.\n- Core must remain framework-agnostic; adapters import from `../index` only.\n\n### Files and what lives where\n- `src/index.ts`: All core logic and types; also exports a Vue directive for convenience.\n- `src/debug-utils.ts`: Optional dev-time helpers (e.g., `drawMargins`) to visualize IntersectionObserver margins.\n- `src/{vue,react,preact,solid,angular,qwik}/index.ts`: Framework bindings.\n- `src/nuxt/{module.ts,runtime/plugin.ts}`: Nuxt module and runtime plugin.\n- `docs/`: Vite/Vue demo and docs site.\n- `cypress/`: E2E tests for docs/examples.\n- `rollup.config.js`: Build configuration; `FRAMEWORK`, `DECLARATIONS`, and `MIN` env flags control outputs.\n- `package.json`: Exports map and scripts.\n\n### How to run\n- **Install**: `pnpm install`.\n- **Docs (playground)**: `pnpm dev` (runs Vite in `docs/`).\n- **Build library**: `pnpm build` (runs `node ./build/build.mjs`).\n- **Build docs**: `pnpm build-docs`.\n- **Cypress**: in one terminal `pnpm cypress:server` (docs dev server), in another `pnpm cypress:open`.\n- **Nuxt playground**: `pnpm nuxt:dev` (if playground present).\n\n### Adding or changing behavior\n- **Modify default animations**: adjust `add`, `remain`, `remove` in `src/index.ts`. Maintain invariants around style resets and cleanup. Ensure `updatePos` is called after each animation to keep `coords` fresh and observers connected.\n- **Plugins**: The plugin signature is the extension point. If adding plugin options, keep the tuple return backwards-compatible. Only treat `config` as plugin when it’s a function.\n- **Performance**: Be careful changing IntersectionObserver `threshold`/`rootMargin`; it’s tuned to avoid excessive updates and to catch movement. Use `lowPriority` for non-critical updates.\n- **Accessibility**: Keep the `prefers-reduced-motion` guard; only bypass if explicitly configured.\n\n### Common pitfalls\n- Forgetting to re-observe or update positions after animation causes stale `coords` and erratic flips. Always call `updatePos` on `finish`.\n- Mutating layout-affecting styles outside of animations can desync computed sizes. Prefer transform/opacity for motion; animate width/height only through `getTransitionSizes` path.\n- Not accounting for scroll in coords leads to incorrect deltas. Always use `getCoords` and `getScrollOffset` utilities.\n- Exit animations must temporarily reinsert the element and absolutely position it; skipping style reset or cleanup leaks styles into future reuses of that node.\n\n### Coding style\n- TypeScript strict; avoid `any` in public APIs.\n- Clear, descriptive names; early returns for edge cases.\n- Keep core free of framework-specific imports.\n- Comments explain “why” when logic is subtle (e.g., scroll compensation), not trivial “how”.\n\n### Testing guidance\n- Prefer adding example scenarios in `docs/` for manual verification.\n- E2E flows live in `cypress/integration`; add/extend specs that exercise add/remove/reorder, variable heights, nested scroll containers, and reduced-motion.\n\n### Triage checklist for bugs\n- Repro in `docs/` with a minimal list/grid.\n- Check if prefers-reduced-motion disabled animations.\n- Verify parent element has `position` not left `static` when required (core sets to `relative` if static).\n- Inspect `coords` freshness (use `updatePos` logs or `debug-utils`).\n- Confirm scroll compensation path when removing near viewport bottom.\n\n### External APIs used\n- Web Animations API (`Element.prototype.animate`, `Animation`, `KeyframeEffect`).\n- `MutationObserver`, `ResizeObserver`, `IntersectionObserver`.\n- `requestIdleCallback` (fallback to `requestAnimationFrame`).\n\n### Release & packaging\n- Build uses Rollup; env flags:\n  - `FRAMEWORK=index|vue|react|preact|solid|angular|qwik` controls entry file.\n  - `DECLARATIONS=true` emits types only.\n  - `MIN=true` emits minified `.min.js`.\n- `package.json#exports` serves subpath entries (`./vue`, `./react`, etc.).\n\n### Security & SSR\n- Core guards on `window` and `ResizeObserver` presence; no-ops in unsupported/non-browser contexts.\n- Vue directive exposes `getSSRProps` to be SSR-safe.\n\n### Docs site (Vite + Vue SSG)\n- **Architecture**: A separate Vue 3 SPA in `docs/` built and statically generated with Vite + `vite-ssg`. It produces a fully static site (no server required at runtime).\n- **Entry points**:\n  - `docs/index.html` is the HTML shell that loads `/src/main.ts`.\n  - `docs/src/main.ts` uses `ViteSSG(App, { routes }, setup)` to register routes and install the FormKit Vue plugin.\n  - `docs/src/App.vue` renders a `RouterView` and site chrome (header/footer/announcement).\n- **Routing**:\n  - Routes are declared in `docs/src/main.ts` via an array of `RouteRecordRaw`.\n  - Current routes: `/` → `PageHome.vue`, `/lists` → `PageList.vue`, `/tests` → `PageTests.vue`, `/tests-keep-alive` → `PageTestKeepAlive.vue` (some loaded via dynamic import for code-splitting).\n  - `vite.config.ts` sets `ssgOptions.includeAllRoutes = true`, so all known routes are pre-rendered during `vite-ssg build`.\n- **Content structure**:\n  - `docs/src/pages/`: top-level pages composing sections and examples.\n  - `docs/src/sections/`: content sections used on the home page (installation, usage, examples, plugins, why).\n  - `docs/src/components/`: UI components (navigation, header/footer, icons, code example wrapper, etc.).\n  - `docs/src/examples/`: live examples/demos per framework, showing how to use `@formkit/auto-animate` in practice.\n  - Global styles under `docs/assets/` (`main.css`, `variables.css`, `prism.css`), images under `docs/assets/img/`, and fonts under `docs/assets/fonts/`.\n- **Third-party integrations**:\n  - `@vitejs/plugin-vue` for Vue SFCs.\n  - `vite-ssg` for static generation.\n  - `@formkit/vue` plugin registered in the SSG setup.\n  - PrismJS loaded via `<script>` in `index.html` for code highlighting; GitHub buttons via their script; a site script tag is included for analytics.\n- **Dev/build commands** (from `package.json`):\n  - Dev playground: `pnpm dev` → `cd docs && vite --config=./vite.config.ts`.\n  - Static build: `pnpm build-docs` → `cd docs && vite-ssg build --config=./vite.config.ts` (outputs static assets to the default Vite output directory).\n  - Cypress E2E: start docs dev server `pnpm cypress:server` (port 5555), then run `pnpm cypress:open`.\n- **How examples reference the library**:\n  - Examples import from `@formkit/auto-animate` (and subpaths) to demonstrate public API usage. During local development, ensure the library is built (`pnpm build`) if the examples need the latest compiled artifacts; otherwise Vite may resolve from the package cache.\n- **Deployment**:\n  - The generated static output can be deployed to any static host (Netlify, Cloudflare Pages, GitHub Pages). No server logic is required.\n"},"files":{".dmux-hooks/CLAUDE.md":"# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis 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.\n\n## What You're Working On\n\nYou 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 (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  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\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n",".dmux-hooks/AGENTS.md":"# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis 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.\n\n## What You're Working On\n\nYou 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 (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  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\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n",".cursorrules":"### Project overview\n- **Purpose**: Zero-config motion for DOM list changes. Given a parent element, automatically animates its immediate children when they are added, removed, or moved.\n- **Core entry**: `src/index.ts` exports `autoAnimate(el, configOrPlugin)` and types, plus a Vue directive `vAutoAnimate`.\n- **Framework adapters**: Thin wrappers providing hooks/directives for Vue, React, Solid, Preact, Angular, Qwik under `src/<framework>/index.ts`.\n- **Build**: TS → ESM via Rollup; built artifacts land in `dist/`. Package `exports` map serves `index.mjs` and framework subpaths.\n\n### Mental model (how it works)\n- **Observers**\n  - MutationObserver on the parent watches childList changes. For each mutation, `getElements` collects the parent and child elements that changed (including removed ones), tagging each with a non-enumerable target marker `__aa_tgt`.\n  - ResizeObserver on `document.documentElement` (root) and on each observed element updates cached positions when sizes change.\n  - IntersectionObserver is created per element to detect position changes while scrolling; a dynamic `rootMargin` derived from the last known coordinates triggers recalculation. Occasional low-priority polling backs this up.\n- **State tracking** (all with WeakMap/WeakSet to avoid leaks)\n  - `coords`: last known absolute coordinates adjusted for nearest scrolling ancestor.\n  - `siblings`: prev/next references for elements removed from the DOM so they can be temporarily reinserted for exit animations.\n  - `animations`: current Web Animations API `Animation` per element.\n  - `intersections`, `intervals`, `debounces`: observers/intervals/timers per element.\n  - `options`: the per-parent config or plugin; `enabled`: parents with animations enabled.\n- **Deciding what to animate**\n  - `animate(el)` selects one of: `add`, `remain`, or `remove` based on whether the element has cached coordinates and whether it’s still connected.\n  - Flags: `__aa_new` marks newly reinserted nodes to ensure their next cycle is treated as an entry; `__aa_del` marks nodes currently animating out.\n- **Effects**\n  - Default behavior uses Web Animations API: `add` scales/ fades in; `remain` uses FLIP-style transforms (translate from old to new), also animates width/height deltas respecting `box-sizing`; `remove` temporarily re-inserts the node near its original position, absolutely positions it, and scales/fades out.\n  - Plugins: if `config` is a function, it’s treated as an `AutoAnimationPlugin`. Plugin functions return a `KeyframeEffect` or `[KeyframeEffect, { styleReset | false }]`. The library will construct and play an `Animation` from the effect and handle optional style resets.\n- **Scroll handling**\n  - Coordinates are absolute plus the nearest scrollable ancestor’s offset.\n  - During removal at page bottom, the library compensates for scroll jumps by snapshotting pre/post window scroll and performing a manual scroll animation to match the configured duration/easing.\n- **Options & accessibility**\n  - Defaults: `{ duration: 250, easing: 'ease-in-out' }`.\n  - Respects `prefers-reduced-motion: reduce` unless `disrespectUserMotionPreference: true` or a plugin is used.\n  - `AnimationController` controls enable/disable per parent.\n\n### Public API surface\n- **Default export**: `autoAnimate(el: HTMLElement, config?: Partial<AutoAnimateOptions> | AutoAnimationPlugin): AnimationController`\n- **Types**: `AutoAnimateOptions`, `AutoAnimationPlugin`, `AutoAnimationPluginOptions`, `AnimationController`.\n- **Vue**: `vAutoAnimate` directive, `autoAnimatePlugin`, `useAutoAnimate` hook.\n- **React/Preact**: `useAutoAnimate` hook returns `[refCallback | refObject, setEnabled]`.\n- **Solid**: `createAutoAnimate` and `createAutoAnimateDirective`.\n- **Angular**: `AutoAnimateDirective` (`[auto-animate]`).\n- **Qwik**: `useAutoAnimate` returns `[Signal<HTMLElement>, setEnabledQRL]`.\n- **Nuxt**: module that registers the Vue directive and auto-imports `autoAnimate` and `useAutoAnimate`.\n\n### Key invariants and conventions\n- Do not mutate children beyond adding style resets for exit animations; remove those styles in `cleanUp`.\n- All per-node state is in WeakMaps/Sets to prevent memory leaks; never store strong references to DOM nodes outside these registries.\n- Only immediate children of the target parent are animated; traversal utilities (`forEach`) visit the parent first, then its children.\n- Always schedule position updates after animations finish (`animation.addEventListener('finish', updatePos)`), and debounce updates using the current duration (or 500ms for plugins).\n- When computing size deltas, use `getTransitionSizes` to respect `box-sizing: content-box` padding/borders.\n- Tagging properties `__aa_tgt`, `__aa_del`, `__aa_new` are non-enumerable and must be defined via `Object.defineProperty`.\n- Core must remain framework-agnostic; adapters import from `../index` only.\n\n### Files and what lives where\n- `src/index.ts`: All core logic and types; also exports a Vue directive for convenience.\n- `src/debug-utils.ts`: Optional dev-time helpers (e.g., `drawMargins`) to visualize IntersectionObserver margins.\n- `src/{vue,react,preact,solid,angular,qwik}/index.ts`: Framework bindings.\n- `src/nuxt/{module.ts,runtime/plugin.ts}`: Nuxt module and runtime plugin.\n- `docs/`: Vite/Vue demo and docs site.\n- `cypress/`: E2E tests for docs/examples.\n- `rollup.config.js`: Build configuration; `FRAMEWORK`, `DECLARATIONS`, and `MIN` env flags control outputs.\n- `package.json`: Exports map and scripts.\n\n### How to run\n- **Install**: `pnpm install`.\n- **Docs (playground)**: `pnpm dev` (runs Vite in `docs/`).\n- **Build library**: `pnpm build` (runs `node ./build/build.mjs`).\n- **Build docs**: `pnpm build-docs`.\n- **Cypress**: in one terminal `pnpm cypress:server` (docs dev server), in another `pnpm cypress:open`.\n- **Nuxt playground**: `pnpm nuxt:dev` (if playground present).\n\n### Adding or changing behavior\n- **Modify default animations**: adjust `add`, `remain`, `remove` in `src/index.ts`. Maintain invariants around style resets and cleanup. Ensure `updatePos` is called after each animation to keep `coords` fresh and observers connected.\n- **Plugins**: The plugin signature is the extension point. If adding plugin options, keep the tuple return backwards-compatible. Only treat `config` as plugin when it’s a function.\n- **Performance**: Be careful changing IntersectionObserver `threshold`/`rootMargin`; it’s tuned to avoid excessive updates and to catch movement. Use `lowPriority` for non-critical updates.\n- **Accessibility**: Keep the `prefers-reduced-motion` guard; only bypass if explicitly configured.\n\n### Common pitfalls\n- Forgetting to re-observe or update positions after animation causes stale `coords` and erratic flips. Always call `updatePos` on `finish`.\n- Mutating layout-affecting styles outside of animations can desync computed sizes. Prefer transform/opacity for motion; animate width/height only through `getTransitionSizes` path.\n- Not accounting for scroll in coords leads to incorrect deltas. Always use `getCoords` and `getScrollOffset` utilities.\n- Exit animations must temporarily reinsert the element and absolutely position it; skipping style reset or cleanup leaks styles into future reuses of that node.\n\n### Coding style\n- TypeScript strict; avoid `any` in public APIs.\n- Clear, descriptive names; early returns for edge cases.\n- Keep core free of framework-specific imports.\n- Comments explain “why” when logic is subtle (e.g., scroll compensation), not trivial “how”.\n\n### Testing guidance\n- Prefer adding example scenarios in `docs/` for manual verification.\n- E2E flows live in `cypress/integration`; add/extend specs that exercise add/remove/reorder, variable heights, nested scroll containers, and reduced-motion.\n\n### Triage checklist for bugs\n- Repro in `docs/` with a minimal list/grid.\n- Check if prefers-reduced-motion disabled animations.\n- Verify parent element has `position` not left `static` when required (core sets to `relative` if static).\n- Inspect `coords` freshness (use `updatePos` logs or `debug-utils`).\n- Confirm scroll compensation path when removing near viewport bottom.\n\n### External APIs used\n- Web Animations API (`Element.prototype.animate`, `Animation`, `KeyframeEffect`).\n- `MutationObserver`, `ResizeObserver`, `IntersectionObserver`.\n- `requestIdleCallback` (fallback to `requestAnimationFrame`).\n\n### Release & packaging\n- Build uses Rollup; env flags:\n  - `FRAMEWORK=index|vue|react|preact|solid|angular|qwik` controls entry file.\n  - `DECLARATIONS=true` emits types only.\n  - `MIN=true` emits minified `.min.js`.\n- `package.json#exports` serves subpath entries (`./vue`, `./react`, etc.).\n\n### Security & SSR\n- Core guards on `window` and `ResizeObserver` presence; no-ops in unsupported/non-browser contexts.\n- Vue directive exposes `getSSRProps` to be SSR-safe.\n\n### Docs site (Vite + Vue SSG)\n- **Architecture**: A separate Vue 3 SPA in `docs/` built and statically generated with Vite + `vite-ssg`. It produces a fully static site (no server required at runtime).\n- **Entry points**:\n  - `docs/index.html` is the HTML shell that loads `/src/main.ts`.\n  - `docs/src/main.ts` uses `ViteSSG(App, { routes }, setup)` to register routes and install the FormKit Vue plugin.\n  - `docs/src/App.vue` renders a `RouterView` and site chrome (header/footer/announcement).\n- **Routing**:\n  - Routes are declared in `docs/src/main.ts` via an array of `RouteRecordRaw`.\n  - Current routes: `/` → `PageHome.vue`, `/lists` → `PageList.vue`, `/tests` → `PageTests.vue`, `/tests-keep-alive` → `PageTestKeepAlive.vue` (some loaded via dynamic import for code-splitting).\n  - `vite.config.ts` sets `ssgOptions.includeAllRoutes = true`, so all known routes are pre-rendered during `vite-ssg build`.\n- **Content structure**:\n  - `docs/src/pages/`: top-level pages composing sections and examples.\n  - `docs/src/sections/`: content sections used on the home page (installation, usage, examples, plugins, why).\n  - `docs/src/components/`: UI components (navigation, header/footer, icons, code example wrapper, etc.).\n  - `docs/src/examples/`: live examples/demos per framework, showing how to use `@formkit/auto-animate` in practice.\n  - Global styles under `docs/assets/` (`main.css`, `variables.css`, `prism.css`), images under `docs/assets/img/`, and fonts under `docs/assets/fonts/`.\n- **Third-party integrations**:\n  - `@vitejs/plugin-vue` for Vue SFCs.\n  - `vite-ssg` for static generation.\n  - `@formkit/vue` plugin registered in the SSG setup.\n  - PrismJS loaded via `<script>` in `index.html` for code highlighting; GitHub buttons via their script; a site script tag is included for analytics.\n- **Dev/build commands** (from `package.json`):\n  - Dev playground: `pnpm dev` → `cd docs && vite --config=./vite.config.ts`.\n  - Static build: `pnpm build-docs` → `cd docs && vite-ssg build --config=./vite.config.ts` (outputs static assets to the default Vite output directory).\n  - Cypress E2E: start docs dev server `pnpm cypress:server` (port 5555), then run `pnpm cypress:open`.\n- **How examples reference the library**:\n  - Examples import from `@formkit/auto-animate` (and subpaths) to demonstrate public API usage. During local development, ensure the library is built (`pnpm build`) if the examples need the latest compiled artifacts; otherwise Vite may resolve from the package cache.\n- **Deployment**:\n  - The generated static output can be deployed to any static host (Netlify, Cloudflare Pages, GitHub Pages). No server logic is required.\n"},"items":[{"name":"AGENTS.md","path":".dmux-hooks/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/formkit/auto-animate/HEAD/.dmux-hooks/AGENTS.md","title":".dmux-hooks - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis 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.\n\n## What You're Working On\n\nYou 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 (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  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\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n","isInternal":false,"tokens":3346,"sizeBytes":11853},{"name":".cursorrules","path":".cursorrules","rawUrl":"https://raw.githubusercontent.com/formkit/auto-animate/HEAD/.cursorrules","title":"Cursor IDE Native Rules","category":"cursor-rule","format":"markdown","content":"### Project overview\n- **Purpose**: Zero-config motion for DOM list changes. Given a parent element, automatically animates its immediate children when they are added, removed, or moved.\n- **Core entry**: `src/index.ts` exports `autoAnimate(el, configOrPlugin)` and types, plus a Vue directive `vAutoAnimate`.\n- **Framework adapters**: Thin wrappers providing hooks/directives for Vue, React, Solid, Preact, Angular, Qwik under `src/<framework>/index.ts`.\n- **Build**: TS → ESM via Rollup; built artifacts land in `dist/`. Package `exports` map serves `index.mjs` and framework subpaths.\n\n### Mental model (how it works)\n- **Observers**\n  - MutationObserver on the parent watches childList changes. For each mutation, `getElements` collects the parent and child elements that changed (including removed ones), tagging each with a non-enumerable target marker `__aa_tgt`.\n  - ResizeObserver on `document.documentElement` (root) and on each observed element updates cached positions when sizes change.\n  - IntersectionObserver is created per element to detect position changes while scrolling; a dynamic `rootMargin` derived from the last known coordinates triggers recalculation. Occasional low-priority polling backs this up.\n- **State tracking** (all with WeakMap/WeakSet to avoid leaks)\n  - `coords`: last known absolute coordinates adjusted for nearest scrolling ancestor.\n  - `siblings`: prev/next references for elements removed from the DOM so they can be temporarily reinserted for exit animations.\n  - `animations`: current Web Animations API `Animation` per element.\n  - `intersections`, `intervals`, `debounces`: observers/intervals/timers per element.\n  - `options`: the per-parent config or plugin; `enabled`: parents with animations enabled.\n- **Deciding what to animate**\n  - `animate(el)` selects one of: `add`, `remain`, or `remove` based on whether the element has cached coordinates and whether it’s still connected.\n  - Flags: `__aa_new` marks newly reinserted nodes to ensure their next cycle is treated as an entry; `__aa_del` marks nodes currently animating out.\n- **Effects**\n  - Default behavior uses Web Animations API: `add` scales/ fades in; `remain` uses FLIP-style transforms (translate from old to new), also animates width/height deltas respecting `box-sizing`; `remove` temporarily re-inserts the node near its original position, absolutely positions it, and scales/fades out.\n  - Plugins: if `config` is a function, it’s treated as an `AutoAnimationPlugin`. Plugin functions return a `KeyframeEffect` or `[KeyframeEffect, { styleReset | false }]`. The library will construct and play an `Animation` from the effect and handle optional style resets.\n- **Scroll handling**\n  - Coordinates are absolute plus the nearest scrollable ancestor’s offset.\n  - During removal at page bottom, the library compensates for scroll jumps by snapshotting pre/post window scroll and performing a manual scroll animation to match the configured duration/easing.\n- **Options & accessibility**\n  - Defaults: `{ duration: 250, easing: 'ease-in-out' }`.\n  - Respects `prefers-reduced-motion: reduce` unless `disrespectUserMotionPreference: true` or a plugin is used.\n  - `AnimationController` controls enable/disable per parent.\n\n### Public API surface\n- **Default export**: `autoAnimate(el: HTMLElement, config?: Partial<AutoAnimateOptions> | AutoAnimationPlugin): AnimationController`\n- **Types**: `AutoAnimateOptions`, `AutoAnimationPlugin`, `AutoAnimationPluginOptions`, `AnimationController`.\n- **Vue**: `vAutoAnimate` directive, `autoAnimatePlugin`, `useAutoAnimate` hook.\n- **React/Preact**: `useAutoAnimate` hook returns `[refCallback | refObject, setEnabled]`.\n- **Solid**: `createAutoAnimate` and `createAutoAnimateDirective`.\n- **Angular**: `AutoAnimateDirective` (`[auto-animate]`).\n- **Qwik**: `useAutoAnimate` returns `[Signal<HTMLElement>, setEnabledQRL]`.\n- **Nuxt**: module that registers the Vue directive and auto-imports `autoAnimate` and `useAutoAnimate`.\n\n### Key invariants and conventions\n- Do not mutate children beyond adding style resets for exit animations; remove those styles in `cleanUp`.\n- All per-node state is in WeakMaps/Sets to prevent memory leaks; never store strong references to DOM nodes outside these registries.\n- Only immediate children of the target parent are animated; traversal utilities (`forEach`) visit the parent first, then its children.\n- Always schedule position updates after animations finish (`animation.addEventListener('finish', updatePos)`), and debounce updates using the current duration (or 500ms for plugins).\n- When computing size deltas, use `getTransitionSizes` to respect `box-sizing: content-box` padding/borders.\n- Tagging properties `__aa_tgt`, `__aa_del`, `__aa_new` are non-enumerable and must be defined via `Object.defineProperty`.\n- Core must remain framework-agnostic; adapters import from `../index` only.\n\n### Files and what lives where\n- `src/index.ts`: All core logic and types; also exports a Vue directive for convenience.\n- `src/debug-utils.ts`: Optional dev-time helpers (e.g., `drawMargins`) to visualize IntersectionObserver margins.\n- `src/{vue,react,preact,solid,angular,qwik}/index.ts`: Framework bindings.\n- `src/nuxt/{module.ts,runtime/plugin.ts}`: Nuxt module and runtime plugin.\n- `docs/`: Vite/Vue demo and docs site.\n- `cypress/`: E2E tests for docs/examples.\n- `rollup.config.js`: Build configuration; `FRAMEWORK`, `DECLARATIONS`, and `MIN` env flags control outputs.\n- `package.json`: Exports map and scripts.\n\n### How to run\n- **Install**: `pnpm install`.\n- **Docs (playground)**: `pnpm dev` (runs Vite in `docs/`).\n- **Build library**: `pnpm build` (runs `node ./build/build.mjs`).\n- **Build docs**: `pnpm build-docs`.\n- **Cypress**: in one terminal `pnpm cypress:server` (docs dev server), in another `pnpm cypress:open`.\n- **Nuxt playground**: `pnpm nuxt:dev` (if playground present).\n\n### Adding or changing behavior\n- **Modify default animations**: adjust `add`, `remain`, `remove` in `src/index.ts`. Maintain invariants around style resets and cleanup. Ensure `updatePos` is called after each animation to keep `coords` fresh and observers connected.\n- **Plugins**: The plugin signature is the extension point. If adding plugin options, keep the tuple return backwards-compatible. Only treat `config` as plugin when it’s a function.\n- **Performance**: Be careful changing IntersectionObserver `threshold`/`rootMargin`; it’s tuned to avoid excessive updates and to catch movement. Use `lowPriority` for non-critical updates.\n- **Accessibility**: Keep the `prefers-reduced-motion` guard; only bypass if explicitly configured.\n\n### Common pitfalls\n- Forgetting to re-observe or update positions after animation causes stale `coords` and erratic flips. Always call `updatePos` on `finish`.\n- Mutating layout-affecting styles outside of animations can desync computed sizes. Prefer transform/opacity for motion; animate width/height only through `getTransitionSizes` path.\n- Not accounting for scroll in coords leads to incorrect deltas. Always use `getCoords` and `getScrollOffset` utilities.\n- Exit animations must temporarily reinsert the element and absolutely position it; skipping style reset or cleanup leaks styles into future reuses of that node.\n\n### Coding style\n- TypeScript strict; avoid `any` in public APIs.\n- Clear, descriptive names; early returns for edge cases.\n- Keep core free of framework-specific imports.\n- Comments explain “why” when logic is subtle (e.g., scroll compensation), not trivial “how”.\n\n### Testing guidance\n- Prefer adding example scenarios in `docs/` for manual verification.\n- E2E flows live in `cypress/integration`; add/extend specs that exercise add/remove/reorder, variable heights, nested scroll containers, and reduced-motion.\n\n### Triage checklist for bugs\n- Repro in `docs/` with a minimal list/grid.\n- Check if prefers-reduced-motion disabled animations.\n- Verify parent element has `position` not left `static` when required (core sets to `relative` if static).\n- Inspect `coords` freshness (use `updatePos` logs or `debug-utils`).\n- Confirm scroll compensation path when removing near viewport bottom.\n\n### External APIs used\n- Web Animations API (`Element.prototype.animate`, `Animation`, `KeyframeEffect`).\n- `MutationObserver`, `ResizeObserver`, `IntersectionObserver`.\n- `requestIdleCallback` (fallback to `requestAnimationFrame`).\n\n### Release & packaging\n- Build uses Rollup; env flags:\n  - `FRAMEWORK=index|vue|react|preact|solid|angular|qwik` controls entry file.\n  - `DECLARATIONS=true` emits types only.\n  - `MIN=true` emits minified `.min.js`.\n- `package.json#exports` serves subpath entries (`./vue`, `./react`, etc.).\n\n### Security & SSR\n- Core guards on `window` and `ResizeObserver` presence; no-ops in unsupported/non-browser contexts.\n- Vue directive exposes `getSSRProps` to be SSR-safe.\n\n### Docs site (Vite + Vue SSG)\n- **Architecture**: A separate Vue 3 SPA in `docs/` built and statically generated with Vite + `vite-ssg`. It produces a fully static site (no server required at runtime).\n- **Entry points**:\n  - `docs/index.html` is the HTML shell that loads `/src/main.ts`.\n  - `docs/src/main.ts` uses `ViteSSG(App, { routes }, setup)` to register routes and install the FormKit Vue plugin.\n  - `docs/src/App.vue` renders a `RouterView` and site chrome (header/footer/announcement).\n- **Routing**:\n  - Routes are declared in `docs/src/main.ts` via an array of `RouteRecordRaw`.\n  - Current routes: `/` → `PageHome.vue`, `/lists` → `PageList.vue`, `/tests` → `PageTests.vue`, `/tests-keep-alive` → `PageTestKeepAlive.vue` (some loaded via dynamic import for code-splitting).\n  - `vite.config.ts` sets `ssgOptions.includeAllRoutes = true`, so all known routes are pre-rendered during `vite-ssg build`.\n- **Content structure**:\n  - `docs/src/pages/`: top-level pages composing sections and examples.\n  - `docs/src/sections/`: content sections used on the home page (installation, usage, examples, plugins, why).\n  - `docs/src/components/`: UI components (navigation, header/footer, icons, code example wrapper, etc.).\n  - `docs/src/examples/`: live examples/demos per framework, showing how to use `@formkit/auto-animate` in practice.\n  - Global styles under `docs/assets/` (`main.css`, `variables.css`, `prism.css`), images under `docs/assets/img/`, and fonts under `docs/assets/fonts/`.\n- **Third-party integrations**:\n  - `@vitejs/plugin-vue` for Vue SFCs.\n  - `vite-ssg` for static generation.\n  - `@formkit/vue` plugin registered in the SSG setup.\n  - PrismJS loaded via `<script>` in `index.html` for code highlighting; GitHub buttons via their script; a site script tag is included for analytics.\n- **Dev/build commands** (from `package.json`):\n  - Dev playground: `pnpm dev` → `cd docs && vite --config=./vite.config.ts`.\n  - Static build: `pnpm build-docs` → `cd docs && vite-ssg build --config=./vite.config.ts` (outputs static assets to the default Vite output directory).\n  - Cypress E2E: start docs dev server `pnpm cypress:server` (port 5555), then run `pnpm cypress:open`.\n- **How examples reference the library**:\n  - Examples import from `@formkit/auto-animate` (and subpaths) to demonstrate public API usage. During local development, ensure the library is built (`pnpm build`) if the examples need the latest compiled artifacts; otherwise Vite may resolve from the package cache.\n- **Deployment**:\n  - The generated static output can be deployed to any static host (Netlify, Cloudflare Pages, GitHub Pages). No server logic is required.\n","isInternal":false,"tokens":2734,"sizeBytes":11571},{"name":"CLAUDE.md","path":".dmux-hooks/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/formkit/auto-animate/HEAD/.dmux-hooks/CLAUDE.md","title":".dmux-hooks - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis 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.\n\n## What You're Working On\n\nYou 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 (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  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\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n","isInternal":false,"tokens":3346,"sizeBytes":11853}],"systemPromptSnippet":"<yakaai_skills repo=\"formkit/auto-animate\">\n<!-- File: .dmux-hooks/AGENTS.md (Tokens: ~3346 | Category: root-instruction) -->\n# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis 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.\n\n## What You're Working On\n\nYou 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 (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  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\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n\n<!-- File: .cursorrules (Tokens: ~2734 | Category: cursor-rule) -->\n### Project overview\n- **Purpose**: Zero-config motion for DOM list changes. Given a parent element, automatically animates its immediate children when they are added, removed, or moved.\n- **Core entry**: `src/index.ts` exports `autoAnimate(el, configOrPlugin)` and types, plus a Vue directive `vAutoAnimate`.\n- **Framework adapters**: Thin wrappers providing hooks/directives for Vue, React, Solid, Preact, Angular, Qwik under `src/<framework>/index.ts`.\n- **Build**: TS → ESM via Rollup; built artifacts land in `dist/`. Package `exports` map serves `index.mjs` and framework subpaths.\n\n### Mental model (how it works)\n- **Observers**\n  - MutationObserver on the parent watches childList changes. For each mutation, `getElements` collects the parent and child elements that changed (including removed ones), tagging each with a non-enumerable target marker `__aa_tgt`.\n  - ResizeObserver on `document.documentElement` (root) and on each observed element updates cached positions when sizes change.\n  - IntersectionObserver is created per element to detect position changes while scrolling; a dynamic `rootMargin` derived from the last known coordinates triggers recalculation. Occasional low-priority polling backs this up.\n- **State tracking** (all with WeakMap/WeakSet to avoid leaks)\n  - `coords`: last known absolute coordinates adjusted for nearest scrolling ancestor.\n  - `siblings`: prev/next references for elements removed from the DOM so they can be temporarily reinserted for exit animations.\n  - `animations`: current Web Animations API `Animation` per element.\n  - `intersections`, `intervals`, `debounces`: observers/intervals/timers per element.\n  - `options`: the per-parent config or plugin; `enabled`: parents with animations enabled.\n- **Deciding what to animate**\n  - `animate(el)` selects one of: `add`, `remain`, or `remove` based on whether the element has cached coordinates and whether it’s still connected.\n  - Flags: `__aa_new` marks newly reinserted nodes to ensure their next cycle is treated as an entry; `__aa_del` marks nodes currently animating out.\n- **Effects**\n  - Default behavior uses Web Animations API: `add` scales/ fades in; `remain` uses FLIP-style transforms (translate from old to new), also animates width/height deltas respecting `box-sizing`; `remove` temporarily re-inserts the node near its original position, absolutely positions it, and scales/fades out.\n  - Plugins: if `config` is a function, it’s treated as an `AutoAnimationPlugin`. Plugin functions return a `KeyframeEffect` or `[KeyframeEffect, { styleReset | false }]`. The library will construct and play an `Animation` from the effect and handle optional style resets.\n- **Scroll handling**\n  - Coordinates are absolute plus the nearest scrollable ancestor’s offset.\n  - During removal at page bottom, the library compensates for scroll jumps by snapshotting pre/post window scroll and performing a manual scroll animation to match the configured duration/easing.\n- **Options & accessibility**\n  - Defaults: `{ duration: 250, easing: 'ease-in-out' }`.\n  - Respects `prefers-reduced-motion: reduce` unless `disrespectUserMotionPreference: true` or a plugin is used.\n  - `AnimationController` controls enable/disable per parent.\n\n### Public API surface\n- **Default export**: `autoAnimate(el: HTMLElement, config?: Partial<AutoAnimateOptions> | AutoAnimationPlugin): AnimationController`\n- **Types**: `AutoAnimateOptions`, `AutoAnimationPlugin`, `AutoAnimationPluginOptions`, `AnimationController`.\n- **Vue**: `vAutoAnimate` directive, `autoAnimatePlugin`, `useAutoAnimate` hook.\n- **React/Preact**: `useAutoAnimate` hook returns `[refCallback | refObject, setEnabled]`.\n- **Solid**: `createAutoAnimate` and `createAutoAnimateDirective`.\n- **Angular**: `AutoAnimateDirective` (`[auto-animate]`).\n- **Qwik**: `useAutoAnimate` returns `[Signal<HTMLElement>, setEnabledQRL]`.\n- **Nuxt**: module that registers the Vue directive and auto-imports `autoAnimate` and `useAutoAnimate`.\n\n### Key invariants and conventions\n- Do not mutate children beyond adding style resets for exit animations; remove those styles in `cleanUp`.\n- All per-node state is in WeakMaps/Sets to prevent memory leaks; never store strong references to DOM nodes outside these registries.\n- Only immediate children of the target parent are animated; traversal utilities (`forEach`) visit the parent first, then its children.\n- Always schedule position updates after animations finish (`animation.addEventListener('finish', updatePos)`), and debounce updates using the current duration (or 500ms for plugins).\n- When computing size deltas, use `getTransitionSizes` to respect `box-sizing: content-box` padding/borders.\n- Tagging properties `__aa_tgt`, `__aa_del`, `__aa_new` are non-enumerable and must be defined via `Object.defineProperty`.\n- Core must remain framework-agnostic; adapters import from `../index` only.\n\n### Files and what lives where\n- `src/index.ts`: All core logic and types; also exports a Vue directive for convenience.\n- `src/debug-utils.ts`: Optional dev-time helpers (e.g., `drawMargins`) to visualize IntersectionObserver margins.\n- `src/{vue,react,preact,solid,angular,qwik}/index.ts`: Framework bindings.\n- `src/nuxt/{module.ts,runtime/plugin.ts}`: Nuxt module and runtime plugin.\n- `docs/`: Vite/Vue demo and docs site.\n- `cypress/`: E2E tests for docs/examples.\n- `rollup.config.js`: Build configuration; `FRAMEWORK`, `DECLARATIONS`, and `MIN` env flags control outputs.\n- `package.json`: Exports map and scripts.\n\n### How to run\n- **Install**: `pnpm install`.\n- **Docs (playground)**: `pnpm dev` (runs Vite in `docs/`).\n- **Build library**: `pnpm build` (runs `node ./build/build.mjs`).\n- **Build docs**: `pnpm build-docs`.\n- **Cypress**: in one terminal `pnpm cypress:server` (docs dev server), in another `pnpm cypress:open`.\n- **Nuxt playground**: `pnpm nuxt:dev` (if playground present).\n\n### Adding or changing behavior\n- **Modify default animations**: adjust `add`, `remain`, `remove` in `src/index.ts`. Maintain invariants around style resets and cleanup. Ensure `updatePos` is called after each animation to keep `coords` fresh and observers connected.\n- **Plugins**: The plugin signature is the extension point. If adding plugin options, keep the tuple return backwards-compatible. Only treat `config` as plugin when it’s a function.\n- **Performance**: Be careful changing IntersectionObserver `threshold`/`rootMargin`; it’s tuned to avoid excessive updates and to catch movement. Use `lowPriority` for non-critical updates.\n- **Accessibility**: Keep the `prefers-reduced-motion` guard; only bypass if explicitly configured.\n\n### Common pitfalls\n- Forgetting to re-observe or update positions after animation causes stale `coords` and erratic flips. Always call `updatePos` on `finish`.\n- Mutating layout-affecting styles outside of animations can desync computed sizes. Prefer transform/opacity for motion; animate width/height only through `getTransitionSizes` path.\n- Not accounting for scroll in coords leads to incorrect deltas. Always use `getCoords` and `getScrollOffset` utilities.\n- Exit animations must temporarily reinsert the element and absolutely position it; skipping style reset or cleanup leaks styles into future reuses of that node.\n\n### Coding style\n- TypeScript strict; avoid `any` in public APIs.\n- Clear, descriptive names; early returns for edge cases.\n- Keep core free of framework-specific imports.\n- Comments explain “why” when logic is subtle (e.g., scroll compensation), not trivial “how”.\n\n### Testing guidance\n- Prefer adding example scenarios in `docs/` for manual verification.\n- E2E flows live in `cypress/integration`; add/extend specs that exercise add/remove/reorder, variable heights, nested scroll containers, and reduced-motion.\n\n### Triage checklist for bugs\n- Repro in `docs/` with a minimal list/grid.\n- Check if prefers-reduced-motion disabled animations.\n- Verify parent element has `position` not left `static` when required (core sets to `relative` if static).\n- Inspect `coords` freshness (use `updatePos` logs or `debug-utils`).\n- Confirm scroll compensation path when removing near viewport bottom.\n\n### External APIs used\n- Web Animations API (`Element.prototype.animate`, `Animation`, `KeyframeEffect`).\n- `MutationObserver`, `ResizeObserver`, `IntersectionObserver`.\n- `requestIdleCallback` (fallback to `requestAnimationFrame`).\n\n### Release & packaging\n- Build uses Rollup; env flags:\n  - `FRAMEWORK=index|vue|react|preact|solid|angular|qwik` controls entry file.\n  - `DECLARATIONS=true` emits types only.\n  - `MIN=true` emits minified `.min.js`.\n- `package.json#exports` serves subpath entries (`./vue`, `./react`, etc.).\n\n### Security & SSR\n- Core guards on `window` and `ResizeObserver` presence; no-ops in unsupported/non-browser contexts.\n- Vue directive exposes `getSSRProps` to be SSR-safe.\n\n### Docs site (Vite + Vue SSG)\n- **Architecture**: A separate Vue 3 SPA in `docs/` built and statically generated with Vite + `vite-ssg`. It produces a fully static site (no server required at runtime).\n- **Entry points**:\n  - `docs/index.html` is the HTML shell that loads `/src/main.ts`.\n  - `docs/src/main.ts` uses `ViteSSG(App, { routes }, setup)` to register routes and install the FormKit Vue plugin.\n  - `docs/src/App.vue` renders a `RouterView` and site chrome (header/footer/announcement).\n- **Routing**:\n  - Routes are declared in `docs/src/main.ts` via an array of `RouteRecordRaw`.\n  - Current routes: `/` → `PageHome.vue`, `/lists` → `PageList.vue`, `/tests` → `PageTests.vue`, `/tests-keep-alive` → `PageTestKeepAlive.vue` (some loaded via dynamic import for code-splitting).\n  - `vite.config.ts` sets `ssgOptions.includeAllRoutes = true`, so all known routes are pre-rendered during `vite-ssg build`.\n- **Content structure**:\n  - `docs/src/pages/`: top-level pages composing sections and examples.\n  - `docs/src/sections/`: content sections used on the home page (installation, usage, examples, plugins, why).\n  - `docs/src/components/`: UI components (navigation, header/footer, icons, code example wrapper, etc.).\n  - `docs/src/examples/`: live examples/demos per framework, showing how to use `@formkit/auto-animate` in practice.\n  - Global styles under `docs/assets/` (`main.css`, `variables.css`, `prism.css`), images under `docs/assets/img/`, and fonts under `docs/assets/fonts/`.\n- **Third-party integrations**:\n  - `@vitejs/plugin-vue` for Vue SFCs.\n  - `vite-ssg` for static generation.\n  - `@formkit/vue` plugin registered in the SSG setup.\n  - PrismJS loaded via `<script>` in `index.html` for code highlighting; GitHub buttons via their script; a site script tag is included for analytics.\n- **Dev/build commands** (from `package.json`):\n  - Dev playground: `pnpm dev` → `cd docs && vite --config=./vite.config.ts`.\n  - Static build: `pnpm build-docs` → `cd docs && vite-ssg build --config=./vite.config.ts` (outputs static assets to the default Vite output directory).\n  - Cypress E2E: start docs dev server `pnpm cypress:server` (port 5555), then run `pnpm cypress:open`.\n- **How examples reference the library**:\n  - Examples import from `@formkit/auto-animate` (and subpaths) to demonstrate public API usage. During local development, ensure the library is built (`pnpm build`) if the examples need the latest compiled artifacts; otherwise Vite may resolve from the package cache.\n- **Deployment**:\n  - The generated static output can be deployed to any static host (Netlify, Cloudflare Pages, GitHub Pages). No server logic is required.\n\n<!-- File: .dmux-hooks/CLAUDE.md (Tokens: ~3346 | Category: claude-rule) -->\n# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis 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.\n\n## What You're Working On\n\nYou 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 (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` → `.dmux/hooks/` → `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\"           # Project root directory\nDMUX_SERVER_PORT=\"3142\"                # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\"         # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\"               # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\"   # User's prompt\nDMUX_AGENT=\"claude\"                    # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\"                # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\"             # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\"              # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -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\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n  pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n  npm install &\nelif [ -f \"yarn.lock\" ]; then\n  yarn install &\nelif [ -f \"Gemfile\" ]; then\n  bundle install &\nelif [ -f \"requirements.txt\" ]; then\n  pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n  cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n  cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n  if [ -f \"$DMUX_ROOT/$file\" ]; then\n    cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n  fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n  STATUS=\"passed\"\nelse\n  STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\"     '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n  TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 |     grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n  URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n  URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\"   -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n  exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n  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\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n  gh issue close \"$ISSUE\"     -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\"     2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $?  # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n  # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n  # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n  # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n  # Vite project\nelif [ -f \"next.config.js\" ]; then\n  # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n  # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n❌ **Blocking operations**: `sleep 60` (blocks dmux)\n✅ **Background long tasks**: `slow_operation &`\n\n❌ **Hardcoded paths**: `/Users/me/project`\n✅ **Use variables**: `\"$DMUX_ROOT\"`\n\n❌ **Assuming tools exist**: `pnpm install`\n✅ **Check first**: `command -v pnpm && pnpm install`\n\n❌ **No error handling**: Script fails silently\n✅ **Set error mode**: `set -e` or check exit codes\n\n❌ **Forgetting executable bit**: Hook won't run\n✅ **Make executable**: `chmod +x`\n\n❌ **Noisy output**: Clutters dmux logs\n✅ **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n❌ **Not testing**: Deploy and hope\n✅ **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x  # Print each command before executing\nset -e  # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-14*\n</yakaai_skills>"}