# Claude Code Hooks

<!-- FLY-1138: hook table moved here from .claude/CLAUDE.md, which keeps a
     one-line summary plus a pointer. Hooks fire automatically — this table is
     read on demand, when you need to know which gate refused something. -->

| Hook                       | Trigger                 | Purpose                                                    |
| -------------------------- | ----------------------- | ---------------------------------------------------------- |
| `PreToolUse` (Bash/Edit)   | Before tool execution   | Auto-approve scripts, validate create args, comment hints  |
| `PostToolUse` (Edit/Write) | After code changes      | Auto-format                                                |
| `PostToolUse` (Bash)       | After shell commands    | Transition validation, session file updates, create audit  |
| `PostToolUse` (Bash)       | After PR commands       | PR template validation                                     |
| `UserPromptSubmit`         | Before prompt           | Directive workflow context, milestone/label injection      |
| `Stop`                     | Agent finishes response | Full lifecycle gate (READY, IMPLEMENTING, REVIEW, BLOCKED) |
| `SessionStart`             | New session begins      | Inject continuity context                                  |

## Permission modes (FLY-1406)

FlyDocs lives under `.claude/`, which Claude Code treats as a **protected path**
(alongside `.git`, `.vscode`, `.mcp.json`, and others). Writes to protected
paths are never auto-approved in `default` or `acceptEdits` mode, and
`permissions.allow` rules and `PreToolUse` hooks are evaluated *after* that
check, so no allow rule and no hook can suppress the prompt. An
`Edit(.claude/**)` allow rule changes nothing.

| Mode                     | Protected-path writes (`.claude/**`)   | Use                                                                        |
| ------------------------ | -------------------------------------- | -------------------------------------------------------------------------- |
| `default`, `acceptEdits` | Prompt a human every time              | Interactive sessions where someone is present                              |
| `auto`                   | Reviewed by the classifier, no prompt  | **Supported mode for unattended FlyDocs work** — orchestration, sub-agents |
| `dontAsk`                | Denied                                 | Not usable with FlyDocs                                                    |
| `bypassPermissions`      | Allowed, no checks                     | Only inside a container or VM                                              |

Where the mode is set matters:

- `auto` takes effect **only** from `~/.claude/settings.json` or the
  `--permission-mode auto` flag. An `auto` value in `.claude/settings.json` or
  `.claude/settings.local.json` is ignored *and* suppresses the user-level
  default — never write it there.
- A `defaultMode` in project or local settings outranks the user's own. A
  workspace that distributes `acceptEdits` through the portal permission panel
  pins every developer below auto; leave it unset unless you mean it.
- Sub-agents inherit the parent session's mode, and `settings.json` hooks run
  inside them. A worktree checks out the committed `.claude/settings.json` but
  never `settings.local.json`: team rules belong in the portal policy
  (committed), personal rules in `~/.claude/settings.json`, and
  `settings.local.json` is for throwaway experiments only.
- `deny` rules hold in every mode from every scope. Put hard limits there
  (secret files, signing keys, force-push, publish), through the portal.
- Headless: `claude -p --permission-mode auto` never prompts; a classifier
  block simply does not run and the session continues.
- Stopgap when a prompt appears in an interactive `acceptEdits` session:
  answer **"Yes, and allow Claude to edit its own settings for this session"**
  — it is session-wide and covers sub-agents.
