# A hook is an automatic action on a lifecycle event
[Content hub](/en/ai/content)
- type: article
- published_at: 2026-09-04
- tags: component, hook
An expanded guide to hook: events, argv, determinism, side effects, and rollback.
## Content
# `hook`

A `hook` is an action bound to a harness lifecycle event: before a tool
runs, after a write, before a prompt is sent, or at another event the
harness actually documents.
A hook answers the question: **what must run automatically when this
event happens?**
It does not answer "which named shortcut do I type?" ([`command`](https://ai-stp.aiguild.space/en/docs/components)),
"how should the agent do this class of task?" ([`skill`](https://ai-stp.aiguild.space/en/docs/components)), or
"which standing rule should the agent remember?"
([`instruction`](https://ai-stp.aiguild.space/en/docs/components)).
A hook is the most sensitive of the eight kinds: it can change state
while the user is looking at something else.
!!! warning "A harness hook is not a React hook, and not a webhook"
Kind `hook` is a harness lifecycle handler, usually a `hooks.json`
plus a command handler.
An ordinary `src/hooks/useFoo.ts`, a product webhook, or an arbitrary
`hooks/` folder is **not** this kind. `unsupported` in the discovery
matrix does not become a filename heuristic.
A plugin may *contain* a hooks directory. That member is still kind
`hook`. The pack around it is kind [`plugin`](https://ai-stp.aiguild.space/en/docs/components). Discovery
reads plugin hooks only inside a pack proven by the exact manifest.
| Object | Kind | When discovery reports it |
| --- | --- | --- |
| `.codex/hooks.json` | `hook` | Codex project layout |
| `hooks/hooks.json` inside a proven plugin | `hook` | Claude Code or Codex pack |
| Grok `hooks/` directory | `hook` | declared Grok layout |
| `src/hooks/useFoo.ts` | none | not a harness component |
## Neighbours
| Kind | The main difference |
| --- | --- |
| `command` | a command starts when a person or agent invokes it; a hook starts on an event |
| `skill` | a skill waits to be selected for a task; a hook does not wait |
| `instruction` | an instruction is text; a hook is an action |
| `plugin` | a plugin may *contain* a hooks directory; the hook is still kind `hook` |
| `mcp` | MCP is a tool interface; a hook is not a protocol server |
| `agent` | an agent is a role; a hook is not a subagent |
| `setting` | a setting holds parameters; a hook holds an event and a handler |
Choose `hook` when the check must be unavoidable on that event. Choose
`command` when a person should start the work. Choose `instruction` when
a reminder in prose is enough.
## Recommended package structure
A hook handler must be directly runnable after installation. `--language`
is `python`, `typescript`, `javascript`, or `dart-flutter`. Rust and Go
are refused: the provider does not perform a hidden source build.
The portable native layout is a `hooks.json` manifest plus a handler.
The authoring directory also holds `source/hook.json` (event, order,
blocking failure, handler). `discover` / `adopt` transfer `source/` when
portable and `projections/<harness>/` when a harness was selected, not
the whole tree.
```text
pre-tool-check/ # component-scaffold/3
├── .ai-stp-template.json
├── .gitignore
├── README.md
├── component-passport.json
├── eval-profile.json
└── source/
├── hook.json
└── hooks/
└── handler.py
```
Everyday:
```bash
ai-stp task start --intent author --idempotency-key author-session-01 --json
```
Expert recovery (already-held digest):
```text
ai-stp component scaffold plan \
--type hook \
--language python \
--harness portable \
--name pre-tool-check \
--output ./pre-tool-check \
--json
ai-stp component scaffold apply \
--type hook \
--language python \
--harness portable \
--name pre-tool-check \
--output ./pre-tool-check \
--expected-plan-digest <digest> \
--json
```
`--language rust` and `--language go` fail closed for this kind.
Adoption accepts a path discovery already named. A directory must carry
a closed-set manifest. `hooks.json` is in that set. A plugin
hook-directory is one component: it includes the manifest and adjacent
scripts in a deterministic artifact. Scripts are **not** run during
discovery.
There is no `ai-stp help --path component --json`. Structural readiness is
`component passport validate`. Kind-specific specification checking
exists only for [`skill`](https://ai-stp.aiguild.space/en/docs/components).
## Standards and frameworks
There is no independent hook specification comparable to the
[Agent Skills Specification](https://agentskills.io/specification) or
to [MCP](https://modelcontextprotocol.io). Each harness documents its
own events.
Cite `layout_source` from the `author` intent envelope when
classification is uncertain. Do not guess a neighbour's path, and do
not treat an ordinary `src/hooks/useFoo.ts` or a business webhook as a
harness hook — `unsupported` in the matrix does not become a filename
heuristic.
NVIDIA SkillSpector and Cisco Skill Scanner are skill scanners. They
do not validate hooks.
## Native layouts per harness
Discovery only reports layouts that are declared. Exact paths on a
machine come from the `author` intent envelope. Each finding
carries `layout_source`. If classification is uncertain, show that
field; do not guess a neighbour's path.
From the discovery matrix:
| Harness | Global | Project | Notes that are in the discovery contract |
| --- | --- | --- | --- |
| Claude Code | not a top-level cell | not a top-level cell | manifest-backed: `hooks/hooks.json` **inside** a plugin proven by `.claude-plugin/plugin.json` |
| Codex | no | yes | only `.codex/hooks.json`, or `hooks/hooks.json` inside a plugin proven by `.codex-plugin/plugin.json` |
| Pi | no | no | not a declared hook layout |
| OpenCode | no | no | not a declared hook layout |
| Grok Build | yes | yes | bounded native hook directory |
| Cursor | not invented from an adjacent directory | not invented from an adjacent directory | official plugin schema names `hooks`; walker does not invent them from an adjacent directory |
| Antigravity | yes | yes | |
| `undefined` | portable conventions | portable conventions | not a harness; automatic install is not considered safe |
A Claude Code project plugin pack is proven only by exact
`.claude-plugin/plugin.json`. Inside that pack, discovery reads
`hooks/hooks.json` as one hook component.
A Cursor pack is proven by `.cursor-plugin/plugin.json`. The walker
does not create a hook finding from a neighbouring `hooks/` directory
that the tree does not carry.
Expert recovery — everyday authoring is the `author` intent:
```bash
ai-stp task start --intent author --idempotency-key author-session-01 --json
```
Do not type `component discover`. The engine finds the native tree.
## Versions are `X.Y`, not SemVer
A published hook version is immutable and has the form `X.Y`. There is
no patch number. Changing the event, the matcher, or the handler is a
new version. Updating a hook inside a setup is a new setup version.
```bash
ai-stp component version list --id <stable_id> --json
ai-stp help --path component --json
```
`--major` opens the next major line. A major line is a separate access
boundary.
## What `ai_stp` checks
The catalog percent and the required-versus-optional split are explained
on [Security checks](https://ai-stp.aiguild.space/en/docs/components). For a hook, expect at
least:
- structure, digest, license, tags, source repository;
- bounded unpack and path denylist;
- secret scanning (`secrets_heuristic`, and Gitleaks when enabled);
- prompt-injection and hidden-content rules;
- `hook_schema_static` and `hook_command_argv` (schema, argv);
- language SAST and SCA when scripts and lockfiles are present.
A passed scan reduces known risk. It is not a guarantee that the handler
is harmless. Required checks that fail or cannot run block publication.
Before install, also look at:
| Check | Why it matters |
| --- | --- |
| The event | a person must be able to name when it fires |
| The action | one sentence; if you cannot say it, do not enable it |
| Disable / rollback | a hook that cannot be turned off is not MVP-safe |
| Who is the author | a verified author does not make the handler automatically safe |
| Which `X.Y` is pinned | updating a hook makes a new version of the setup |
| Trust line | `experimental` needs explicit consent |
`author_verified` and `component_verified` are independent. Neither is a
safety guarantee.
## Related CLI commands
Only commands that exist. Flags always from the CLI pages, and always
`--json`. The executable is `ai-stp` (package `ai-stp-cli`). There is no
`component inspect` and no `setup show`. The only kind-specific validate
is `ai-stp help --path component --json`.
**This kind, specifically:** there is no `component hook validate`. Use
passport validation.
```bash
ai-stp component passport validate --id <stable_id> --json
```
**Author, adopt, publish:**
```bash
ai-stp task start --intent author --idempotency-key author-session-01 --json
ai-stp help --path publication --json
```
**Install:**
```bash
ai-stp task input-schema --intent install --json
```
Expert catalog inspect:
```text
ai-stp registry search --kind component --query <name> --json
```
A hook can also be an embedded member of a compose manifest. See
[Setups](https://ai-stp.aiguild.space/en/docs/components).
## How a hook moves through `ai_stp`
=== "Author"
The author publishes the hook from a public GitHub source, or imports
it locally. The version pins an exact commit and subpath. Discovery
never executes the handler.
=== "Catalog"
The catalog shows the event, the supported harnesses, the
constraints, the author's trusted status and the component's own
independent status.
=== "Compiler"
The compiler checks that the harness supports that lifecycle event
and that the handler can be projected for the provider.
=== "Provider"
The provider writes the native hook configuration only after a plan,
a digest and a confirmation. Status must then show the hook, its
source, and how to disable it.
## Red flags
- An ordinary `src/hooks/` React directory classified as a harness hook.
- A `hooks/` directory next to a Cursor plugin treated as a hook even
though the walker does not invent that layout.
- Codex hooks anywhere except `.codex/hooks.json` or
`hooks/hooks.json` inside a proven `.codex-plugin/plugin.json` pack.
- Scaffolding with `--language rust` or `--language go`.
- Handlers that download and pipe into a shell.
- Live tokens, private keys, or `.env` bodies in the package.
- No documented way to disable or roll the hook back.
- `experimental` trust line without `consent allow`.
- Harness not in the component's compatibility list.
- "Latest" or a branch name instead of an exact `X.Y` and commit.
- Treating `author_verified` as `component_verified`.
- Copying `hooks.json` into a target instead of going through the
provider plan.
??? question "Can a hook be used without publishing it"
Yes. Your own, imported or exactly pinned hook can be used after
local checks. It does not thereby become platform-verified, and it
must be shown as exactly what it is: a local or pinned object
(`local_owner_or_pinned`). Preview, backup, and a way to switch it
off still apply.
## Author checklist
1. Scaffold with `--type hook` and a directly runnable `--language`
(`python`, `typescript`, `javascript`, or `dart-flutter`).
2. Keep `hooks.json` and the handler under `source/`. Fill
`source/hook.json` with the event, order, blocking failure, and
handler.
3. Declare what the handler does, what it reads, and how to disable it
in the passport. No secrets.
4. Register the directory through
`ai-stp task start --intent author --idempotency-key author-session-01 --json`.
Expert `component discover` / `component adopt` remain for recovery.
5. Pin an exact public GitHub commit and subpath.
6. `component passport validate` → `component version release` to mint
immutable `X.Y`.
7. Publish through [the publication path](https://ai-stp.aiguild.space/en/docs/components).
8. In a setup, pin that `X.Y`. Updating later is a new setup version.
Related: [Authoring](https://ai-stp.aiguild.space/en/docs/components),
[Components](https://ai-stp.aiguild.space/en/docs/components), [`command`](https://ai-stp.aiguild.space/en/docs/components), [`plugin`](https://ai-stp.aiguild.space/en/docs/components).