# Hooks: example scenario

Guarantee formatting, block destructive commands and record every tool call in ticket-service with three hooks.

Source: https://ai-sw-factory.mellicci.dev/fundamentals/hooks/example-scenario

## Scenario

In `ticket-service`, agents often forget Prettier and ESLint, so formatting noise fills pull requests. One agent nearly ran `rm -rf` on the wrong directory, another tried to write `.env.local`, and nobody could reconstruct which commands had run. The [instruction file](https://ai-sw-factory.mellicci.dev/fundamentals/instruction-files/example-scenario) helps most of the time, which is not good enough for these three.

This page shows one way to fix that: three hooks that run every time, whether or not the model remembers.

## Before → after

| | Before | After |
|---|---|---|
| Consistency | Formatting is fixed by hand in review | Every edited file is formatted and linted right after the edit |
| Risk | Destructive commands depend on the model's judgment | Recursive `rm` and `.env*` writes are refused, with a reason |
| Traceability | No record of what the agent ran | `.agent/logs/tool-calls.jsonl` lists every call |
| Effort | You repeat the rules in prompts | The rules live in code that always runs |

## Design

**Diagram:** Two hooks run before the tool and can block it; one runs after the tool on the changed file.

- Model — decides the next action
- → tool call
- Harness · deterministic:
  - Before-tool hooks — guard.sh blocks, log.sh records
  - → allowed
  - Tool runs — edit · shell
  - →
  - After-tool hook — format.sh on the changed file

| Hook | Runs | Script | Does |
|---|---|---|---|
| (a) | After a file edit | `format.sh` | `prettier --write` and `eslint --fix` on the changed file |
| (b) | Before a shell command or file write | `guard.sh` | Blocks recursive `rm` and any `.env*` write, returns a reason |
| (c) | Before any tool | `log.sh` | Appends one JSON line to `.agent/logs/tool-calls.jsonl` |

Generic shape of `guard.sh`. Event names, JSON fields and the block signal differ per agent; take the real ones from your agent's page.

```bash
#!/usr/bin/env bash
# Generic: read the event JSON on stdin, decide, signal a block.
event=$(cat)
cmd=$(echo "$event" | jq -r '.tool_input.command // empty')
path=$(echo "$event" | jq -r '.tool_input.file_path // empty')
if echo "$cmd" | grep -Eq 'rm +(-[a-zA-Z]*r|--recursive)'; then
  echo "Blocked: recursive delete. Name the files to remove." >&2
  exit 2   # placeholder: how a block is signaled varies
fi
case "$path$cmd" in *.env*) echo "Blocked: .env files are off limits." >&2; exit 2 ;; esac
exit 0
```

`format.sh` has the same shape: read the changed file's path from the event, run the two tools on it, exit.

## What happens at runtime

| Step | Call `rm -rf dist src` | Edit to `src/routes/tickets.ts` |
|---|---|---|
| Model | Asks the shell tool to clean up | Asks the edit tool to add a route |
| Before-tool hooks | `log.sh` records it; `guard.sh` matches recursive `rm` | `log.sh` records it; `guard.sh` finds nothing to block |
| Tool | Skipped | Runs |
| After-tool hook | None | `format.sh` formats and lints the file |
| Model receives | The reason; it retries without a recursive delete, naming the files, or asks you | The result, including any fix the linter made |

## What can go wrong

| Failure | How you notice | What to do |
|---|---|---|
| A slow hook delays every call | The loop feels sluggish; each tool call waits | Act on the changed file only; keep the before-hooks to milliseconds |
| The script fails or crashes (missing `jq`, typo) | Everything is blocked, or nothing is; behavior differs per agent | Check your agent for fail-open or fail-closed; test a deliberate failure |
| The regex guard is bypassed (`find -delete`, another shell) | A destructive command runs anyway | Treat it as a guardrail, not a sandbox; contain the agent with [sandboxing](https://ai-sw-factory.mellicci.dev/fundamentals/sandboxing) |
| Hooks run with your permissions, and hooks from a cloned repo are code you did not write | A new repo runs commands you never reviewed | Read hooks before trusting a repo; see the [security model](https://ai-sw-factory.mellicci.dev/fundamentals/security-model) |
| The log contains secrets from command lines | Tokens or passwords appear in `tool-calls.jsonl` | Keep `.agent/` out of git, rotate the file, redact sensitive fields |
