# Hooks

Hooks are optional executable scripts in `.autobuilder/hooks/` that let you customize the build loop per project. Everything works identically when the hooks directory doesn't exist.

## Quick Start

```bash
mkdir -p .autobuilder/hooks

# Example: run tests after every agent iteration
cat > .autobuilder/hooks/post-agent.py << 'EOF'
#!/usr/bin/env python3
import subprocess, sys, os
result = subprocess.run(["npm", "test"], cwd=os.environ["AUTOBUILDER_PROJECT_DIR"])
sys.exit(result.returncode)
EOF
```

## Available Hooks

| Hook | When | Blocking? | Timeout | Purpose |
|---|---|---|---|---|
| `on-start` | Daemon boot (once) | Yes | 2 min | Start dev server, install deps |
| `pre-iteration` | Before each iteration | Yes | 2 min | Health check, verify dev server |
| `augment-review` | Before review phase | Fail-safe | 2 min | Inject review context |
| `augment-plan` | Before plan phase | Fail-safe | 2 min | Inject planning context |
| `augment-execute` | Before execute phase | Fail-safe | 2 min | Inject execution context |
| `post-agent` | After agent completes | Yes | 10 min | Quality gate: tests, lint |
| `pre-push` | Before git push | Yes | 10 min | Ship gate: E2E, security |
| `post-push` | After push succeeds | Yes | 2 hours | Deploy, CI monitoring |
| `on-idle` | Nothing to improve | No | 2 min | Deep scans, dep updates |
| `on-error` | Iteration crash | No | 2 min | Alerts, diagnostics |
| `on-stop` | Daemon shutdown | No | 2 min | Cleanup, summary report |

**Blocking** means failure prevents the associated action. **Fail-safe** means failure is logged but the loop continues.

## Hook Discovery

The daemon searches for hooks in this order:

1. `.autobuilder/hooks/<name>` (bare executable)
2. `.autobuilder/hooks/<name>.py` (run via `python3`)
3. `.autobuilder/hooks/<name>.sh` (run via `bash`)

Bare files must have the executable bit set. `.py` and `.sh` files are run via their respective interpreters.

## Environment Variables

Every hook receives context as environment variables:

| Variable | Example | Description |
|---|---|---|
| `AUTOBUILDER_PROJECT_DIR` | `/home/user/my-app` | Project root directory |
| `AUTOBUILDER_PROJECT_ID` | `my-app` | Project identifier |
| `AUTOBUILDER_PORT` | `3100` | Dev server port |
| `AUTOBUILDER_ITERATION` | `42` | Current iteration number |
| `AUTOBUILDER_PHASES` | `review,plan,execute` | Comma-separated phase list |
| `AUTOBUILDER_EXECUTOR` | `cursor` | Agent executor type |
| `AUTOBUILDER_MODEL` | `claude-opus-4-7-thinking-xhigh` | Agent model |
| `AUTOBUILDER_REVIEW_MODE` | `polish` | Review mode (polish/feature) |
| `AUTOBUILDER_REVIEW_STRATEGY` | `deep-dive` | Active review strategy |
| `AUTOBUILDER_ACTIVE_INITIATIVE` | `0001-my-app` | Active initiative directory name |

## Prompt Augmentation Hooks

The `augment-review`, `augment-plan`, and `augment-execute` hooks can inject additional instructions into the agent prompt.

**Protocol:**
1. Hook receives JSON context on **stdin**
2. Hook prints additional instructions on **stdout**
3. The stdout text is appended to the prompt for that phase

**stdin JSON:**
```json
{
  "project_dir": "/path/to/project",
  "project_id": "my-app",
  "iteration": 42,
  "phases": ["review", "plan", "execute"],
  "port": 5200,
  "executor": "cursor",
  "model": "claude-opus-4-7-thinking-xhigh",
  "review_mode": "polish",
  "review_strategy": "deep-dive",
  "active_initiative": "0001-my-app"
}
```

**Example — inject Nx-specific tooling:**
```python
#!/usr/bin/env python3
# .autobuilder/hooks/augment-execute.py
print("PROJECT TOOLING (use instead of defaults):")
print("  Dev server: nx run my-app:serve --port 5200")
print("  Tests:      nx run my-app:test")
print("  Lint:       nx run my-app:lint")
print("Do NOT use npm run dev or npx react-doctor.")
```

**Example — inject Lighthouse scores:**
```python
#!/usr/bin/env python3
# .autobuilder/hooks/augment-review.py
import subprocess, json
result = subprocess.run(
    ["lighthouse", "http://localhost:3100", "--output=json", "--quiet"],
    capture_output=True, text=True
)
if result.returncode == 0:
    scores = json.loads(result.stdout)["categories"]
    print("LIGHTHOUSE SCORES (current baseline):")
    for cat, data in scores.items():
        print(f"  {cat}: {data['score'] * 100:.0f}")
```

## Quality Gate — Self-Correcting Loop

The `post-agent` hook is the most impactful hook. When it fails:

1. The daemon captures the hook's stdout/stderr
2. Writes the failure details to `backlog.md`
3. Does NOT mark the task as executed
4. The next iteration reads `backlog.md` and sees the failure

```
Iteration 5: agent implements feature
  → post-agent runs tests → 2 tests FAIL
  → failure details → backlog.md
  → task NOT marked as executed

Iteration 6: agent reads backlog ("2 tests failed: ...")
  → agent fixes the failing tests
  → post-agent runs tests → all PASS
  → task marked as executed
```

This creates a self-correcting loop where test failures are automatically fed back as context.

## Deploy Pipeline (post-push)

The autobuilder ships two `post-push` hook templates. Both work out of the box with zero configuration.

| Template | Use case |
|---|---|
| `post-push-ci-and-deploy.py` | Most projects. Monitors CI; optionally runs a deploy script and health-checks. |
| `post-push-ci-and-vercel.py` | Vercel projects. Monitors CI, then polls the Vercel CLI for deployment status. |

**Setup:**

```bash
# Copy the template — works immediately with no config
cp hook-templates/post-push-ci-and-deploy.py \
   /path/to/project/.autobuilder/hooks/post-push.py
```

### CI + Deploy template

With no configuration, this template just monitors GitHub Actions and spawns fix agents on CI failure. To enable the deploy step, create `.autobuilder/deploy.json` with at least `deploy_domain`:

```json
{
  "deploy_domain": "example.com",
  "server_host": "1.2.3.4",
  "server_user": "deploy"
}
```

When deploy is enabled:
1. Runs a user-provided `deploy.sh` (from project root or `.autobuilder/deploy.sh`) if one exists. The script receives `<app_name> <project_dir> <port>` as arguments.
2. Spawns fix agents if deploy fails (up to `max_deploy_fix_attempts` retries)
3. Health-checks `https://<project_id>.<deploy_domain>` — **fails the hook** if the site doesn't respond
4. Writes the deploy URL to `.autobuilder/deploy_url`

### CI + Vercel template

Monitors CI (same as above), then:
1. Polls `vercel list` for the latest deployment to complete
2. Health-checks the preview URL
3. Writes the preview URL to `.autobuilder/deploy_url`

Requires the `vercel` CLI installed and linked (`vercel link` in the project directory).

Optional `.autobuilder/deploy.json`:

```json
{
  "vercel_timeout": 600,
  "vercel_poll_interval": 10
}
```

### Writing a custom hook

All templates are built from composable helper functions. For platforms not covered by the templates (AWS, Docker, Cloudflare Workers, etc.), write your own `post-push.py` using the building blocks:

```python
#!/usr/bin/env python3
import os, sys
from pathlib import Path
from autobuilder_hook_helpers.ci import wait_for_ci
from autobuilder_hook_helpers.deploy import health_check, store_deploy_url
from autobuilder_hook_helpers.fix_agent import run_fix_agent, build_ci_fix_prompt
from autobuilder_hook_helpers.github import get_github_remote, has_github_workflows, get_head_sha

project_dir = Path(os.environ["AUTOBUILDER_PROJECT_DIR"])
# ... your custom deploy logic using the helpers above ...
```

## Hook Helpers Library

The daemon adds `hook-helpers/` to `PYTHONPATH` for all hooks. Available helpers:

```python
from autobuilder_hook_helpers.github import get_github_remote, has_github_workflows
from autobuilder_hook_helpers.ci import wait_for_ci
from autobuilder_hook_helpers.deploy import run_deploy_script, health_check, store_deploy_url
from autobuilder_hook_helpers.fix_agent import run_fix_agent
from autobuilder_hook_helpers.vercel import wait_for_vercel_deploy, get_vercel_preview_url
```

| Module | Functions | Purpose |
|---|---|---|
| `github` | `get_github_remote`, `has_github_workflows`, `get_latest_workflow_run`, `get_head_sha`, `get_ci_failure_logs` | GitHub remote detection and Actions API |
| `ci` | `wait_for_ci` | Poll GitHub Actions until workflow completes |
| `deploy` | `run_deploy_script`, `health_check`, `store_deploy_url` | SSH deploy, HTTP health check, URL persistence |
| `fix_agent` | `run_fix_agent`, `build_ci_fix_prompt`, `build_deploy_fix_prompt` | Spawn a cursor / codex / claude / opencode agent for auto-repair (uses the shared executor registry) |
| `executors` | `EXECUTORS`, `CURSOR`, `CODEX`, `CLAUDE`, `OPENCODE`, `get` | Per-CLI registry shared with the daemon — see `docs/python-modules.md` |
| `vercel` | `wait_for_vercel_deploy`, `get_vercel_preview_url` | Poll Vercel deployments via CLI |

## Directory Structure

```
.autobuilder/hooks/
├── on-start.py          # Start dev server on correct port
├── post-agent.sh        # Run test suite after agent work
├── pre-push.py          # Security scan before push
├── augment-execute.py   # Inject project-specific tooling
├── post-push.py         # Deploy + CI pipeline (copy from templates)
└── on-idle.py           # Run deeper scans when idle
```
