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.

OptionMeaning
--agent <id>Pick which workspace gets inspected.
--eligibleRestrict results to loadable hooks, meaning those enabled through per-hook/source policy, with requirements met and at least one declared event.
--jsonEmit structured JSON straight to stdout. The parent hooks command accepts this flag as well.
-v, --verboseInclude 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, and loadable report 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, optional pluginId, emoji, homepage, and managedByPlugin.
  • Status: enabledByConfig, requirementsSatisfied, loadable, optional blockedReason, plus compatibility aliases eligible (loadable) and disabled (!enabledByConfig).
  • Events/requirements: events, unknownEvents, and missing, whose arrays are bins, anyBins, env, config, and os.

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

OptionEffect for hook packs
-l, --linkAdd the exact local hook or pack root to hooks.internal.load.extraDirs instead of copying it. Single hooks and nested pack layouts work.
--pinRecord the resolved exact npm name@version in install state when available; does not apply to local paths.
--forceAcknowledge a non-ClawHub source and allow replacement of an existing copied install. For links it acknowledges the source without copying.
--acknowledge-install-policy-warningAcknowledge 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.md and handler. A pack loads only the hook directories listed in openclaw.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.

1,880 words · updated Aug 28, 2026