> ## Documentation Index
> Fetch the complete documentation index at: https://docs.plannotator.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Use Plannotator Approval Gates and Automation Hooks

> Gate a document or agent message, distinguish approval from dismissal, and return plain text, structured JSON, or hook-native output.

Use `--hook` when a host hook must block on submitted annotations. In this mode, both **Approve** and **Close** let the hook pass. Use direct `--gate --json --require-approval` when success requires explicit approval.

Use `--json` when your wrapper must distinguish approved, dismissed, and annotated decisions.

```bash theme={null}
plannotator annotate path/to/spec.md --hook
plannotator annotate-last --hook
```

`--hook` implies `--gate`, so the review UI includes an Approve action.

<Warning>
  In hook-native mode, both **Approve** and **Close** produce empty output, so either action lets the host hook pass. Use direct `--gate --json --require-approval` when **Close** must be a failed gate rather than a pass.
</Warning>

## Output contracts

The contracts apply to `annotate` and `annotate-last`.

### Hook-native output

```bash theme={null}
plannotator annotate path/to/spec.md --hook
```

| User action | Standard output |
| - | - |
| Submit annotations | `{"decision":"block","reason":"<feedback>"}` |
| Approve | Empty |
| Close | Empty |

This contract works directly with Claude Code and Codex `PostToolUse` or `Stop` command-hook protocols that treat a `block` decision as feedback for the agent. If `--hook` and `--json` are both present, the hook-native contract wins.

### Structured JSON

```bash theme={null}
plannotator annotate path/to/spec.md --gate --json
```

| User action | Standard output |
| - | - |
| Submit annotations | `{"decision":"annotated","feedback":"<feedback>"}` |
| Approve | `{"decision":"approved"}` or `{"decision":"approved","feedback":"<notes>"}` |
| Close | `{"decision":"dismissed"}` |

`--json` does not add the Approve action by itself. Add `--gate` if approval is part of your wrapper's contract.

### Require explicit approval

Use the strict options when an automation should succeed only after approval:

```bash theme={null}
plannotator annotate path/to/spec.md \
  --gate --json --require-approval \
  --result-file .plannotator-results/spec-review.json
```

Strict options apply only to direct `annotate --gate --json` runs. They reject `annotate-last`, `--hook`, and calls without both `--gate` and `--json`.

| Outcome | JSON decision | Exit status |
| - | - | - |
| Approve, with or without notes | `approved` | `0` |
| Submit annotations | `annotated` | `1` |
| Close | `dismissed` | `1` |
| Invalid command or flags, startup failure, or result publication failure | None, unless the review decision already completed | `2` |

Without `--require-approval`, completed user decisions keep the existing exit status of `0`.

`--result-file` writes the same newline-terminated JSON bytes as stdout. The path is resolved from the current working directory. Its parent must exist, and the destination must not exist, including as a dangling symlink. Plannotator writes stdout first, then atomically publishes an owner-readable and owner-writable file without overwriting an existing path. Publication is atomic but not guaranteed to survive a system crash before the parent directory is flushed.

### Plain text

Without `--json` or `--hook`, submitted annotations are printed as feedback. Approval prints the exact line `The user approved.` and closing prints nothing.

Outside strict approval mode, completed user decisions exit with status 0. Invalid input and startup failures can exit nonzero. Wrappers must inspect both the process status and standard output.

## Pass input safely

The path to review must come from the hook event or from a trusted wrapper argument. Quote it as one shell argument:

```bash theme={null}
artifact_path="path supplied by the host"
plannotator annotate "$artifact_path" --hook
```

Hook event schemas differ by agent and version. Do not copy an input-field name from another host. Use the host's current hook documentation to extract the file path, then call the CLI contract above.

For a host that sends content rather than a file path, pipe it to `annotate-last --stdin`:

```bash theme={null}
printf '%s' "$artifact_text" | plannotator annotate-last --stdin --hook
```

Avoid expanding untrusted hook content as shell code. Pass values as quoted arguments or standard input.

## Installed plan hooks

The full installer and agent plugins configure plan review for supported hosts. For example, the Claude Code plugin ships:

* a `PreToolUse` hook on `EnterPlanMode` that calls `plannotator improve-context`
* a `PermissionRequest` hook on `ExitPlanMode` that calls `plannotator` and waits for the review decision

On Claude Code 2.1.287 or later, the plugin's [Plannotator mod](/open-source/agents/claude-code#the-plannotator-mod) handles `ExitPlanMode` instead of that hook, and Claude does not wait. The hook still runs on older Claude Code, in `claude -p` and SDK runs, on Windows, and when the mod is turned off.

Codex uses its experimental `Stop` hook on macOS, Linux, and WSL. Copilot CLI uses its plugin's pre-tool hook for `exit_plan_mode`. Those plan hooks use host-specific event schemas and should normally be managed by the installer or plugin.

Restart the agent after changing hook configuration. Desktop applications may not inherit your shell `PATH`; use the absolute path to the Plannotator binary if a hook reports that the command is missing.

## Timeouts

A review is intentionally long-running. The shipped plan hooks use a timeout of 345600 seconds. If you write a custom hook, choose a timeout that allows a person to review without leaving an orphaned process.

Last verified on July 31, 2026, using Plannotator product commit `7cd023c`. Maintained by the Plannotator project.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.