Skip to content
Content
Article

A hook is an automatic action on a lifecycle event

An expanded guide to hook: events, argv, determinism, side effects, and rollback.

  • component
  • hook
Component type: hook

`hook`

Component type: 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?" (), "how should the agent do this class of task?" (), or "which standing rule should the agent remember?" ().

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 . Discovery reads plugin hooks only inside a pack proven by the exact manifest.

ObjectKindWhen discovery reports it
.codex/hooks.jsonhookCodex project layout
hooks/hooks.json inside a proven pluginhookClaude Code or Codex pack
Grok hooks/ directoryhookdeclared Grok layout
src/hooks/useFoo.tsnonenot a harness component

Neighbours

KindThe main difference
commanda command starts when a person or agent invokes it; a hook starts on an event
skilla skill waits to be selected for a task; a hook does not wait
instructionan instruction is text; a hook is an action
plugina plugin may contain a hooks directory; the hook is still kind hook
mcpMCP is a tool interface; a hook is not a protocol server
agentan agent is a role; a hook is not a subagent
settinga 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.

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.

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
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 .

Standards and frameworks

There is no independent hook specification comparable to the Agent Skills Specification or to MCP. 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:

HarnessGlobalProjectNotes that are in the discovery contract
Claude Codenot a top-level cellnot a top-level cellmanifest-backed: hooks/hooks.json inside a plugin proven by .claude-plugin/plugin.json
Codexnoyesonly .codex/hooks.json, or hooks/hooks.json inside a plugin proven by .codex-plugin/plugin.json
Pinononot a declared hook layout
OpenCodenononot a declared hook layout
Grok Buildyesyesbounded native hook directory
Cursornot invented from an adjacent directorynot invented from an adjacent directoryofficial plugin schema names hooks; walker does not invent them from an adjacent directory
Antigravityyesyes
undefinedportable conventionsportable conventionsnot 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.

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.

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. 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:

CheckWhy it matters
The eventa person must be able to name when it fires
The actionone sentence; if you cannot say it, do not enable it
Disable / rollbacka hook that cannot be turned off is not MVP-safe
Who is the authora verified author does not make the handler automatically safe
Which X.Y is pinnedupdating a hook makes a new version of the setup
Trust lineexperimental needs explicit consent

author_verified and component_verified are independent. Neither is a safety guarantee.

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.

ai-stp component passport validate --id <stable_id> --json

Author, adopt, publish:

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:

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.

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 validatecomponent version release to mint immutable X.Y.
  8. Publish through the publication path.
  9. In a setup, pin that X.Y. Updating later is a new setup version.

Related: Authoring, Components, , .

A hook is an automatic action on a lifecycle event — article