OpenClaw Hooks CLI Reference: Discovery, Eligibility, Enablement
This page documents the openclaw hooks command for reviewing and adjusting internal hooks tied to commands, messages, sessions, and Gateway events. It is for developers who need to manage hook behavior and understand remote versus local hook sources.
Read this when
- You want to inspect internal hooks on a local or remote Gateway
- You want to enable or disable a hook in local config
- You need hook command flags or JSON report fields
openclaw hooks
Internal hooks can be reviewed and adjusted through these commands. They cover handlers tied to commands, messages, sessions, and Gateway events. Running plain openclaw hooks produces output identical to openclaw hooks list. HTTP-based Webhooks and the typed api.on(...) catalog found in Plugin hooks fall outside the scope of these tools.
Target and scope
For read-only inspection (hooks, list, info, check), the selected Gateway receives a call to hooks.status first. When remote Gateways are configured or explicit OPENCLAW_GATEWAY_URL targets are given, they act as the source of truth. In such cases, a missing remote URL, connection or authentication problems, or an unsupported method leads to a failure rather than a display of client-side hooks. An implicitly chosen local Gateway, however, may fall back to local discovery if it is unreachable or if its hook-report method/agent parameter is not supported. Other errors never get silently swapped for a local inventory.
Actions that enable, disable, install, or update hooks modify local files, configuration, or state. They never alter a remote Gateway through RPC. To apply changes on the server side, execute the command on that host with its own profile/config and then restart that Gateway.
The agent workspace used for inspection is picked with --agent <id>. This option becomes mandatory when configured agents lack an implicit owner; leaving it blank or supplying an unknown ID causes an error. It works whether placed before or after list, info, check, enable, and disable. It does not tie the persisted hook entry to that agent, and install/update operations do not support it. For the difference between workspace inventory and Gateway loading, refer to Local, remote, and agent scope.
List hooks
openclaw hooks [--agent <id>] [--json]
openclaw hooks list [--agent <id>] [--eligible] [--json] [-v|--verbose]
Bundled hooks, active plugin hooks, managed hooks, extra directories, and the chosen workspace all count toward discovery. When hook names clash, the source policy decides the outcome.
| Option | Meaning |
|---|---|
--agent <id> | Pick which workspace gets inspected. |
--eligible | Restrict results to loadable hooks, meaning those enabled through per-hook/source policy, with requirements met and at least one declared event. |
--json | Emit structured JSON straight to stdout. The parent hooks command accepts this flag as well. |
-v, --verbose | Include a Missing column in the human-readable table. |
For human consumption, output appears as a table with Status, Hook, Description, and Source columns, introduced by Hooks (<ready>/<total> ready). Sources managed by plugins show up as plugin:<id>.
Note
What
ready,eligible, andloadablereport reflects inventory, not a live check of handler registration. The master switch and configured-name selection of the Gateway are not applied by this report, the handler is not imported to verify it works, and no confirmation is made that the event has actually run. A bundled hook may look ready even when the internal hook system is disabled. After enabling the intended hook, restart and confirm its actual effect.
List JSON
The root object carries workspaceDir, managedHooksDir, and hooks. For every hook, the following fields are present:
- Identity/display:
name,description,source, optionalpluginId,emoji,homepage, andmanagedByPlugin. - Status:
enabledByConfig,requirementsSatisfied,loadable, optionalblockedReason, plus compatibility aliaseseligible(loadable) anddisabled(!enabledByConfig). - Events/requirements:
events,unknownEvents, andmissing, whose arrays arebins,anyBins,env,config, andos.
The value of blockedReason may be disabled in config, workspace hook (disabled by default),
missing requirements, or no events defined. Events that are unrecognized serve only as warnings; they do not, on their own, render a hook unloadable.
Get hook info
openclaw hooks info <name> [--agent <id>] [--json]
This command takes either a hook name or its metadata via hookKey. When a name matches exactly, it wins over a key match; a key must point to exactly one hook. The output shows source, descriptor and handler paths, homepage, events, warnings for unknown events, the reason for blocking, and the status of each requirement. If the hook is missing or the selector is ambiguous, the process exits with code 1; for an ambiguous selector, candidates are listed so you can pick a distinct name or key.
The JSON response contains the list fields along with filePath, baseDir, handlerPath,
hookKey, always, requirements, configChecks, and normalized install
options. Each config check reports path and satisfied; each install option includes
id, kind, label, and bins. Install options serve as descriptive metadata, not as an instruction to automatically install dependencies.
Check eligibility
openclaw hooks check [--agent <id>] [--json]
This reports totals for hooks that are ready versus those that are not, and lists the reasons for blocking. The JSON includes total,
eligible, notEligible, and hooks, which contains an eligible name
array and a notEligible array of { name, blockedReason?, missing } objects.
Even when hooks are not ready, a successful report still returns exit code 0. For an automated eligibility check, rely on the JSON counts instead of interpreting the exit code as an all-hooks-ready signal. This still does not verify actual loading.
Enable a hook
openclaw hooks enable <name> [--agent <id>]
The hook is discovered locally first, then hooks.internal.entries.<hookKey>.enabled = true and
hooks.internal.enabled = true are written into local config. Any other fields in that entry remain untouched. Exact hook names take precedence over matching keys; if a key matches multiple hooks ambiguously, the operation fails without writing anything.
Enabling fails when the hook is missing, managed by a plugin, or when runtime requirements are not met. It can enable a workspace hook that is currently disabled. This does not confirm a valid module export or event subscription; check info and the Gateway logs as well.
The entry is global, even with --agent: it applies wherever that key is
discovered. Adding named entries can narrow a previously open-ended directory
selection. See Configuration.
Restart after enabling:
openclaw gateway restart
For a foreground Gateway, stop and start the process instead. Restart is not
performed automatically by hooks enable.
Disable a hook
openclaw hooks disable <name> [--agent <id>]
Writes hooks.internal.entries.<hookKey>.enabled = false. It does not remove the
hook files or change the master switch. Missing/ambiguous and plugin-managed
hooks are rejected; missing runtime requirements do not prevent disabling.
Restart the Gateway afterward.
Plugin-managed hooks cannot be toggled by these commands. Enable or disable the
owning plugin through openclaw plugins.
Install and update hook packs
Use the unified plugin installer for reviewed hook packs:
openclaw plugins install npm:<package>
openclaw plugins install npm:<package>@<version> --pin
openclaw plugins install ./my-hook-pack
openclaw plugins install ./my-hook-pack.tgz
openclaw plugins update <id> --dry-run
openclaw plugins update <id>
A pack declares hook directories in package.json under openclaw.hooks.
A local directory without package.json can contain a single HOOK.md and
handler. Copied hook packs are installed into <stateDir>/hooks/<id>; their
hooks are enabled in config and install provenance is recorded in shared SQLite
state. Restart the Gateway to load them. Do not author
hooks.internal.installs in openclaw.json.
For the npm hook-pack path, specs are registry-only: package name with an
optional exact version or dist-tag. Git/URL/file specs, npm aliases, and semver
ranges are not npm registry specs. Bare specs and @latest stay on the stable
track; a prerelease resolution requires an explicit prerelease version or a
non-latest tag such as @beta or @rc. Use npm: to select npm explicitly; the
unified installer supports other plugin sources described in
openclaw plugins.
Supported local archives are .zip, .tgz, .tar.gz, and .tar. npm pack and
project-local dependency installation use --ignore-scripts; this does not
sandbox the installed handler.
Install options and trust
| Option | Effect for hook packs |
|---|---|
-l, --link | Add the exact local hook or pack root to hooks.internal.load.extraDirs instead of copying it. Single hooks and nested pack layouts work. |
--pin | Record the resolved exact npm name@version in install state when available; does not apply to local paths. |
--force | Acknowledge a non-ClawHub source and allow replacement of an existing copied install. For links it acknowledges the source without copying. |
--acknowledge-install-policy-warning | Acknowledge an operator security.installPolicy warning without its prompt. Blocks and policy failures still stop the install. |
Interactive non-ClawHub installs ask you to confirm trust. Noninteractive
installs require --force; global --yes is not a substitute for that gate.
--force is also not a substitute for acknowledging an install-policy warning.
Review the source before supplying either acknowledgement.
Warning
A linked hook runs directly from the supplied path; linking does not copy it or create a symlink. A single-hook root loads its own
HOOK.mdand handler. A pack loads only the hook directories listed inopenclaw.hooks, including nested paths such as./hooks/my-hook. Declared paths must stay inside the pack and point directly to hooks; discovery does not recurse into nested packs or collections, or scan unlisted children, even when all declared paths are rejected.Only link trusted code. Extra directories still make directory-hook name selection open-ended across discovery sources, not just within the linked pack. Restart the Gateway after linking or editing hook code, check
hooks list, and verify the handler's actual side effect.
Update behavior
Updates use tracked npm install records. A tracked hook-pack ID uses its stored spec; a matching npm package spec can select a new version/tag. Local path and archive records are not refreshed by the npm hook updater.
--dry-run reports what would change without installing or rewriting config.
--all selects both plugins and hook packs in the unified updater, including
when reached through the deprecated alias; it is not a hooks-only bulk command.
When an applicable stored integrity hash differs from the downloaded artifact,
the updater warns and asks for confirmation. Global --yes can accept that
yes/no prompt, so use it only when you intend to accept the drift. It does not
bypass operator policy blocks or replace the dedicated policy-warning flag.
Deprecated aliases
These commands print a deprecation warning and forward to the unified owners:
openclaw hooks install <path-or-spec> [-l|--link] [--pin] [--force] [--acknowledge-install-policy-warning]
openclaw hooks update [id] [--all] [--dry-run] [--acknowledge-install-policy-warning]
For update, provide id or --all. The aliases do not accept --agent and are
not the preferred interface for new automation.
Bundled hooks
The maintained catalog, event subscriptions, options, and verification notes are documented in Bundled hooks. This covers boot-md, bootstrap-extra-files, command-logger, compaction-notifier, and session-memory (manual and automatic reset capture).
command-logger log file
On the Gateway host, using the default state directory:
tail -n 20 ~/.openclaw/logs/commands.log
jq . ~/.openclaw/logs/commands.log
jq 'select(.action == "new")' ~/.openclaw/logs/commands.log
For a custom state directory, use <stateDir>/logs/commands.log. These records hold session and sender identifiers; restrict access and set up retention or rotation. The hook does not handle rotation itself.
Notes
Report commands support --json; success JSON is sent directly to stdout. Failures follow the standard CLI JSON failure envelope, and missing hook info also includes the requested hook name. Reports do not run a hook as a test.
The hidden hooks relay command is reserved for generated native harness integration. It is not meant for internal-hook testing or manual event triggering.