# 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
```
```bash
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 component hook validate`. 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 `ai-stp component discover --json` 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 `ai-stp component discover --json`. 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.
```bash
ai-stp component discover --root . --json
ai-stp toolchain harness-capabilities --json
```
## 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 component version release --id <stable_id> --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 component skill validate`.
**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 component discover --root . --json
ai-stp component adopt --path <source_path> --json
ai-stp component passport validate --id <stable_id> --json
ai-stp component version release --id <stable_id> --json
ai-stp publication plan --id <stable_id> --version 1.0 --json
ai-stp publication confirm --plan-id <id> --plan-hash <hash> --confirm --json
```
**Find, select, install:**
```bash
ai-stp registry search --kind component --query <name> --json
ai-stp select eligibility --harness <id> --json
ai-stp install plan --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. Run `ai-stp component discover --root . --json` and read
`layout_source` on the finding.
5. `component adopt --path <exact source_path>`.
6. Pin an exact public GitHub commit and subpath.
7. `component passport validate` → `component version release` to mint
immutable `X.Y`.
8. Publish through [the publication path](https://ai-stp.aiguild.space/en/docs/components).
9. 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).