Skip to main content
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:
The master switch and selection rules for directory-loaded hooks are: 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:
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.
hooks.internal.handlers is retired and fails normal config validation. Before running openclaw doctor --fix, migrate each registered module into a managed or workspace hook directory with HOOK.md and a handler. Doctor removes the old registrations; it does not create executable files. For a legacy-only config with hooks.internal.enabled: true, it also removes that flag to avoid broad discovery. Named entries, nonempty extra directories, and explicit enabled: false are preserved.

Hook discovery

Directory discovery merges hooks by name using these rules: 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:
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:
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.