Automation
Hook configuration and discovery
Enabling and selecting internal hooks, and how the Gateway discovers them across sources. Part of the Hooks guide.
Configuration
For a predictable selection, enable named hooks rather than turning on broad discovery:
{ "hooks": { "internal": { "enabled": true, "entries": { "command-logger": { "enabled": true }, "session-memory": { "enabled": false } } } }}The master switch and selection rules for directory-loaded hooks are:
| Configuration | Selection |
|---|---|
hooks.internal.enabled: false |
Internal hooks are off. |
| No master flag and no enabled entries, extra directories, or tracked installs | Gateway skips directory-hook loading. |
| Named entries, with master flag omitted or true | Enabled names form an allowlist; enabled: true on the master does not broaden it. An entry without enabled: false contributes its name. |
| Master flag true with no named entries or named installs | Open-ended discovery of eligible hooks. |
| Tracked hook packs declaring hook names | Those names join the selection; an explicit per-hook enabled: false still disables a non-plugin hook. |
Nonempty load.extraDirs, or a tracked install without a hook-name list |
Open-ended discovery, not a selection restricted to that directory or pack. |
Workspace hooks always need entries.<hookKey>.enabled: true, even with
open-ended discovery. For other file hooks, an entry can be selected by its
name or hookKey, but settings are read under hookKey. The CLI resolves the
name and writes the correct key for you. Adding the first named entry can narrow
a previously broad selection; inspect existing hooks before changing it.
Per-hook entries accept arbitrary handler-defined fields. The core types
enabled as a boolean and env as a string-to-string map; it does not validate
custom handler options. For example:
{ "hooks": { "internal": { "entries": { "my-hook": { "enabled": true, "env": { "MY_HOOK_LABEL": "example" } } } } }}Per-hook env satisfies eligibility checks but does not mutate process.env.
On events carrying config, a handler can read it from
event.context.cfg?.hooks?.internal?.entries?.["my-hook"]?.env. Other events do
not promise a cfg field. Do not log entire config objects or put secrets in
examples.
Hook discovery
Directory discovery merges hooks by name using these rules:
| Source | Location and collision behavior |
|---|---|
| Bundled | Shipped with OpenClaw. |
| Plugin | Hook directories declared by active plugins; can replace bundled names. |
| Managed | <stateDir>/hooks/, normally ~/.openclaw/hooks/; can replace bundled and plugin names. |
| Extra directories | hooks.internal.load.extraDirs; same source policy as managed hooks. Later extra directories win over earlier ones; the managed directory wins over extra directories. |
| Workspace | <workspace>/hooks/; can add names but cannot replace bundled, plugin, or managed names. Explicit opt-in required. |
Bundled, managed, workspace, and plugin hook locations are collection
directories: discovery inspects their immediate children for hooks or packages
whose package.json declares openclaw.hooks.
Each explicit hooks.internal.load.extraDirs path can instead be a pack root,
a single-hook root, or a collection directory. A pack root loads only its
declared hook paths, including nested paths such as ./hooks/my-hook. Each
path must point directly to a hook; discovery does not recurse into another
pack or collection. A recognized pack with no valid hooks stays empty rather
than scanning unlisted children. A single-hook root loads its own HOOK.md
and handler. Only an ordinary collection root gets the immediate-child scan.
For example, to select /opt/openclaw-hook-library/my-hook/HOOK.md directly,
add that hook's directory:
{ "hooks": { "internal": { "load": { "extraDirs": ["/opt/openclaw-hook-library/my-hook"] } } }}To scan the library's immediate children instead, add
/opt/openclaw-hook-library. Only add trusted directories: any extra path
opens hook-name selection across discovery sources beyond named entries,
even when that path selects a single hook or pack.
Handler files must stay within their hook directory; package and plugin hook
paths must stay within their package root. Symlinks escaping those boundaries
are rejected. Hook config and selected-workspace changes reload discovery in
hybrid mode, including config written by a new hook-pack install or link. Hook
files and metadata are not watched; restart after editing them or updating
existing hook code, then verify the handler's actual side effect.
Hook packs
A hook pack is a package whose package.json declares hook directories in
openclaw.hooks. Install a reviewed package or local directory through the
unified installer:
openclaw plugins install <path-or-spec>Installation and update flags, npm restrictions, linked-root behavior and trust, and
the deprecated hooks install / hooks update aliases are documented in
Install and update hook packs.