2026-01-17 01:31:39 +00:00
---
2026-01-17 07:32:50 +00:00
summary: "Hooks: event-driven automation for commands and lifecycle events"
2026-01-17 01:31:39 +00:00
read_when:
- You want event-driven automation for /new, /reset, /stop, and agent lifecycle events
2026-01-17 07:32:50 +00:00
- You want to build, install, or debug hooks
2026-01-31 16:04:03 -05:00
title: "Hooks"
2026-01-17 01:31:39 +00:00
---
2026-01-31 21:13:13 +09:00
2026-01-17 07:32:50 +00:00
# Hooks
2026-01-17 01:31:39 +00:00
2026-01-30 03:15:10 +01:00
Hooks provide an extensible event-driven system for automating actions in response to agent commands and events. Hooks are automatically discovered from directories and can be managed via CLI commands, similar to how skills work in OpenClaw.
2026-01-17 01:31:39 +00:00
2026-01-17 01:40:09 +00:00
## Getting Oriented
2026-01-17 01:35:46 +00:00
2026-01-17 01:40:09 +00:00
Hooks are small scripts that run when something happens. There are two kinds:
2026-01-17 01:35:46 +00:00
2026-01-17 07:32:50 +00:00
- **Hooks** (this page): run inside the Gateway when agent events fire, like `/new` , `/reset` , `/stop` , or lifecycle events.
2026-01-30 03:15:10 +01:00
- **Webhooks**: external HTTP webhooks that let other systems trigger work in OpenClaw. See [Webhook Hooks ](/automation/webhook ) or use `openclaw webhooks` for Gmail helper commands.
2026-01-31 21:13:13 +09:00
2026-02-07 15:40:35 -05:00
Hooks can also be bundled inside plugins; see [Plugins ](/tools/plugin#plugin-hooks ).
2026-01-17 01:40:09 +00:00
Common uses:
2026-01-31 21:13:13 +09:00
2026-01-17 01:40:09 +00:00
- Save a memory snapshot when you reset a session
2026-01-17 01:35:46 +00:00
- Keep an audit trail of commands for troubleshooting or compliance
2026-01-17 01:40:09 +00:00
- Trigger follow-up automation when a session starts or ends
2026-01-17 01:35:46 +00:00
- Write files into the agent workspace or call external APIs when events fire
2026-01-17 07:32:50 +00:00
If you can write a small TypeScript function, you can write a hook. Hooks are discovered automatically, and you enable or disable them via the CLI.
2026-01-17 01:35:46 +00:00
2026-01-17 01:31:39 +00:00
## Overview
2026-01-17 07:32:50 +00:00
The hooks system allows you to:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
- Save session context to memory when `/new` is issued
- Log all commands for auditing
- Trigger custom automations on agent lifecycle events
2026-01-30 03:15:10 +01:00
- Extend OpenClaw's behavior without modifying core code
2026-01-17 01:31:39 +00:00
## Getting Started
### Bundled Hooks
Plugin API: compaction/reset hooks, bootstrap file globs, memory plugin status (#13287)
* feat: add before_compaction and before_reset plugin hooks with session context
- Pass session messages to before_compaction hook
- Add before_reset plugin hook for /new and /reset commands
- Add sessionId to plugin hook agent context
* feat: extraBootstrapFiles config with glob pattern support
Add extraBootstrapFiles to agent defaults config, allowing glob patterns
(e.g. "projects/*/TOOLS.md") to auto-load project-level bootstrap files
into agent context every turn. Missing files silently skipped.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(status): show custom memory plugins as enabled, not unavailable
The status command probes memory availability using the built-in
memory-core manager. Custom memory plugins (e.g. via plugin slot)
can't be probed this way, so they incorrectly showed "unavailable".
Now they show "enabled (plugin X)" without the misleading label.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use async fs.glob and capture pre-compaction messages
- Replace globSync (node:fs) with fs.glob (node:fs/promises) to match
codebase conventions for async file operations
- Capture session.messages BEFORE replaceMessages(limited) so
before_compaction hook receives the full conversation history,
not the already-truncated list
* fix: resolve lint errors from CI (oxlint strict mode)
- Add void to fire-and-forget IIFE (no-floating-promises)
- Use String() for unknown catch params in template literals
- Add curly braces to single-statement if (curly rule)
* fix: resolve remaining CI lint errors in workspace.ts
- Remove `| string` from WorkspaceBootstrapFileName union (made all
typeof members redundant per no-redundant-type-constituents)
- Use type assertion for extra bootstrap file names
- Drop redundant await on fs.glob() AsyncIterable (await-thenable)
* fix: address Greptile review — path traversal guard + fs/promises import
- workspace.ts: use path.resolve() + traversal check in loadExtraBootstrapFiles()
- commands-core.ts: import fs from node:fs/promises, drop fs.promises prefix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve symlinks before workspace boundary check
Greptile correctly identified that symlinks inside the workspace could
point to files outside it, bypassing the path prefix check. Now uses
fs.realpath() to resolve symlinks before verifying the real path stays
within the workspace boundary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address Greptile review — hook reliability and type safety
1. before_compaction: add compactingCount field so plugins know both
the full pre-compaction message count and the truncated count being
fed to the compaction LLM. Clarify semantics in comment.
2. loadExtraBootstrapFiles: use path.basename() for the name field
so "projects/quaid/TOOLS.md" maps to the known "TOOLS.md" type
instead of an invalid WorkspaceBootstrapFileName cast.
3. before_reset: fire the hook even when no session file exists.
Previously, short sessions without a persisted file would silently
skip the hook. Now fires with empty messages array so plugins
always know a reset occurred.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: validate bootstrap filenames and add compaction hook timeout
- Only load extra bootstrap files whose basename matches a recognized
workspace filename (AGENTS.md, TOOLS.md, etc.), preventing arbitrary
files from being injected into agent context.
- Wrap before_compaction hook in a 30-second Promise.race timeout so
misbehaving plugins cannot stall the compaction pipeline.
- Clarify hook comments: before_compaction is intentionally awaited
(plugins need messages before they're discarded) but bounded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make before_compaction non-blocking, add sessionFile to after_compaction
- before_compaction is now true fire-and-forget — no await, no timeout.
Plugins that need full conversation data should persist it themselves
and return quickly, or use after_compaction for async processing.
- after_compaction now includes sessionFile path so plugins can read
the full JSONL transcript asynchronously. All pre-compaction messages
are preserved on disk, eliminating the need to block compaction.
- Removes Promise.race timeout pattern that didn't actually cancel
slow hooks (just raced past them while they continued running).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add sessionFile to before_compaction for parallel processing
The session JSONL already has all messages on disk before compaction
starts. By providing sessionFile in before_compaction, plugins can
read and extract data in parallel with the compaction LLM call rather
than waiting for after_compaction. This is the optimal path for memory
plugins that need the full conversation history.
sessionFile is also kept on after_compaction for plugins that only
need to act after compaction completes (analytics, cleanup, etc.).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move bootstrap extras into bundled hook
---------
Co-authored-by: Solomon Steadman <solstead@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Clawdbot <clawdbot@alfie.local>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 06:45:45 +07:00
OpenClaw ships with four bundled hooks that are automatically discovered:
2026-01-17 01:31:39 +00:00
2026-01-30 03:15:10 +01:00
- **💾 session-memory**: Saves session context to your agent workspace (default `~/.openclaw/workspace/memory/` ) when you issue `/new`
Plugin API: compaction/reset hooks, bootstrap file globs, memory plugin status (#13287)
* feat: add before_compaction and before_reset plugin hooks with session context
- Pass session messages to before_compaction hook
- Add before_reset plugin hook for /new and /reset commands
- Add sessionId to plugin hook agent context
* feat: extraBootstrapFiles config with glob pattern support
Add extraBootstrapFiles to agent defaults config, allowing glob patterns
(e.g. "projects/*/TOOLS.md") to auto-load project-level bootstrap files
into agent context every turn. Missing files silently skipped.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(status): show custom memory plugins as enabled, not unavailable
The status command probes memory availability using the built-in
memory-core manager. Custom memory plugins (e.g. via plugin slot)
can't be probed this way, so they incorrectly showed "unavailable".
Now they show "enabled (plugin X)" without the misleading label.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use async fs.glob and capture pre-compaction messages
- Replace globSync (node:fs) with fs.glob (node:fs/promises) to match
codebase conventions for async file operations
- Capture session.messages BEFORE replaceMessages(limited) so
before_compaction hook receives the full conversation history,
not the already-truncated list
* fix: resolve lint errors from CI (oxlint strict mode)
- Add void to fire-and-forget IIFE (no-floating-promises)
- Use String() for unknown catch params in template literals
- Add curly braces to single-statement if (curly rule)
* fix: resolve remaining CI lint errors in workspace.ts
- Remove `| string` from WorkspaceBootstrapFileName union (made all
typeof members redundant per no-redundant-type-constituents)
- Use type assertion for extra bootstrap file names
- Drop redundant await on fs.glob() AsyncIterable (await-thenable)
* fix: address Greptile review — path traversal guard + fs/promises import
- workspace.ts: use path.resolve() + traversal check in loadExtraBootstrapFiles()
- commands-core.ts: import fs from node:fs/promises, drop fs.promises prefix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve symlinks before workspace boundary check
Greptile correctly identified that symlinks inside the workspace could
point to files outside it, bypassing the path prefix check. Now uses
fs.realpath() to resolve symlinks before verifying the real path stays
within the workspace boundary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address Greptile review — hook reliability and type safety
1. before_compaction: add compactingCount field so plugins know both
the full pre-compaction message count and the truncated count being
fed to the compaction LLM. Clarify semantics in comment.
2. loadExtraBootstrapFiles: use path.basename() for the name field
so "projects/quaid/TOOLS.md" maps to the known "TOOLS.md" type
instead of an invalid WorkspaceBootstrapFileName cast.
3. before_reset: fire the hook even when no session file exists.
Previously, short sessions without a persisted file would silently
skip the hook. Now fires with empty messages array so plugins
always know a reset occurred.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: validate bootstrap filenames and add compaction hook timeout
- Only load extra bootstrap files whose basename matches a recognized
workspace filename (AGENTS.md, TOOLS.md, etc.), preventing arbitrary
files from being injected into agent context.
- Wrap before_compaction hook in a 30-second Promise.race timeout so
misbehaving plugins cannot stall the compaction pipeline.
- Clarify hook comments: before_compaction is intentionally awaited
(plugins need messages before they're discarded) but bounded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make before_compaction non-blocking, add sessionFile to after_compaction
- before_compaction is now true fire-and-forget — no await, no timeout.
Plugins that need full conversation data should persist it themselves
and return quickly, or use after_compaction for async processing.
- after_compaction now includes sessionFile path so plugins can read
the full JSONL transcript asynchronously. All pre-compaction messages
are preserved on disk, eliminating the need to block compaction.
- Removes Promise.race timeout pattern that didn't actually cancel
slow hooks (just raced past them while they continued running).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add sessionFile to before_compaction for parallel processing
The session JSONL already has all messages on disk before compaction
starts. By providing sessionFile in before_compaction, plugins can
read and extract data in parallel with the compaction LLM call rather
than waiting for after_compaction. This is the optimal path for memory
plugins that need the full conversation history.
sessionFile is also kept on after_compaction for plugins that only
need to act after compaction completes (analytics, cleanup, etc.).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move bootstrap extras into bundled hook
---------
Co-authored-by: Solomon Steadman <solstead@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Clawdbot <clawdbot@alfie.local>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 06:45:45 +07:00
- **📎 bootstrap-extra-files**: Injects additional workspace bootstrap files from configured glob/path patterns during `agent:bootstrap`
2026-01-30 03:15:10 +01:00
- **📝 command-logger**: Logs all command events to `~/.openclaw/logs/commands.log`
2026-01-18 10:10:05 +02:00
- **🚀 boot-md**: Runs `BOOT.md` when the gateway starts (requires internal hooks enabled)
2026-01-17 01:31:39 +00:00
List available hooks:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks list
2026-01-17 01:31:39 +00:00
```
Enable a hook:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks enable session-memory
2026-01-17 01:31:39 +00:00
```
Check hook status:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks check
2026-01-17 01:31:39 +00:00
```
Get detailed information:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks info session-memory
2026-01-17 01:31:39 +00:00
```
### Onboarding
2026-01-30 03:15:10 +01:00
During onboarding (`openclaw onboard` ), you'll be prompted to enable recommended hooks. The wizard automatically discovers eligible hooks and presents them for selection.
2026-01-17 01:31:39 +00:00
## Hook Discovery
Hooks are automatically discovered from three directories (in order of precedence):
1. **Workspace hooks** : `<workspace>/hooks/` (per-agent, highest precedence)
2026-01-30 03:15:10 +01:00
2. **Managed hooks** : `~/.openclaw/hooks/` (user-installed, shared across workspaces)
3. **Bundled hooks** : `<openclaw>/dist/hooks/bundled/` (shipped with OpenClaw)
2026-01-17 01:31:39 +00:00
2026-01-17 07:08:04 +00:00
Managed hook directories can be either a **single hook** or a **hook pack** (package directory).
2026-01-17 01:31:39 +00:00
Each hook is a directory containing:
```
my-hook/
├── HOOK.md # Metadata + documentation
└── handler.ts # Handler implementation
```
2026-01-17 07:08:04 +00:00
## Hook Packs (npm/archives)
2026-01-30 03:15:10 +01:00
Hook packs are standard npm packages that export one or more hooks via `openclaw.hooks` in
2026-01-17 07:08:04 +00:00
`package.json` . Install them with:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks install < path-or-spec >
2026-01-17 07:08:04 +00:00
```
2026-02-14 14:07:07 +01:00
Npm specs are registry-only (package name + optional version/tag). Git/URL/file specs are rejected.
2026-01-17 07:08:04 +00:00
Example `package.json` :
```json
{
"name": "@acme/my -hooks",
"version": "0.1.0",
2026-01-30 03:15:10 +01:00
"openclaw": {
2026-01-17 07:08:04 +00:00
"hooks": ["./hooks/my-hook", "./hooks/other-hook"]
}
}
```
Each entry points to a hook directory containing `HOOK.md` and `handler.ts` (or `index.ts` ).
2026-01-30 03:15:10 +01:00
Hook packs can ship dependencies; they will be installed under `~/.openclaw/hooks/<id>` .
2026-01-17 07:08:04 +00:00
2026-02-14 14:07:07 +01:00
Security note: `openclaw hooks install` installs dependencies with `npm install --ignore-scripts`
(no lifecycle scripts). Keep hook pack dependency trees "pure JS/TS" and avoid packages that rely
on `postinstall` builds.
2026-01-17 01:31:39 +00:00
## Hook Structure
### HOOK.md Format
The `HOOK.md` file contains metadata in YAML frontmatter plus Markdown documentation:
```markdown
---
name: my-hook
description: "Short description of what this hook does"
2026-02-14 20:05:37 +08:00
homepage: https://docs.openclaw.ai/automation/hooks#my -hook
2026-01-31 21:13:13 +09:00
metadata:
{ "openclaw": { "emoji": "🔗", "events": ["command:new"], "requires": { "bins": ["node"] } } }
2026-01-17 01:31:39 +00:00
---
# My Hook
Detailed documentation goes here...
## What It Does
- Listens for `/new` commands
- Performs some action
- Logs the result
## Requirements
- Node.js must be installed
## Configuration
No configuration needed.
```
### Metadata Fields
2026-01-30 03:15:10 +01:00
The `metadata.openclaw` object supports:
2026-01-17 01:31:39 +00:00
- **`emoji` **: Display emoji for CLI (e.g., `"💾"` )
- **`events` **: Array of events to listen for (e.g., `["command:new", "command:reset"]` )
- **`export` **: Named export to use (defaults to `"default"` )
- **`homepage` **: Documentation URL
- **`requires` **: Optional requirements
- **`bins` **: Required binaries on PATH (e.g., `["git", "node"]` )
- **`anyBins` **: At least one of these binaries must be present
- **`env` **: Required environment variables
- **`config` **: Required config paths (e.g., `["workspace.dir"]` )
- **`os` **: Required platforms (e.g., `["darwin", "linux"]` )
- **`always` **: Bypass eligibility checks (boolean)
- **`install` **: Installation methods (for bundled hooks: `[{"id":"bundled","kind":"bundled"}]` )
### Handler Implementation
2026-01-17 07:32:50 +00:00
The `handler.ts` file exports a `HookHandler` function:
2026-01-17 01:31:39 +00:00
```typescript
2026-01-31 21:13:13 +09:00
import type { HookHandler } from "../../src/hooks/hooks.js";
2026-01-17 01:31:39 +00:00
2026-01-17 07:32:50 +00:00
const myHandler: HookHandler = async (event) => {
2026-01-17 01:31:39 +00:00
// Only trigger on 'new' command
2026-01-31 21:13:13 +09:00
if (event.type !== "command" || event.action !== "new") {
2026-01-17 01:31:39 +00:00
return;
}
console.log(`[my-hook] New command triggered` );
console.log(` Session: ${event.sessionKey}` );
console.log(` Timestamp: ${event.timestamp.toISOString()}` );
// Your custom logic here
// Optionally send message to user
2026-01-31 21:13:13 +09:00
event.messages.push("✨ My hook executed!");
2026-01-17 01:31:39 +00:00
};
export default myHandler;
```
#### Event Context
Each event includes:
```typescript
{
2026-01-18 10:10:05 +02:00
type: 'command' | 'session' | 'agent' | 'gateway',
2026-01-17 01:31:39 +00:00
action: string, // e.g., 'new', 'reset', 'stop'
sessionKey: string, // Session identifier
timestamp: Date, // When the event occurred
messages: string[], // Push messages here to send to user
context: {
sessionEntry?: SessionEntry,
sessionId?: string,
sessionFile?: string,
commandSource?: string, // e.g., 'whatsapp', 'telegram'
senderId?: string,
2026-01-18 05:24:47 +00:00
workspaceDir?: string,
bootstrapFiles?: WorkspaceBootstrapFile[],
2026-01-30 03:15:10 +01:00
cfg?: OpenClawConfig
2026-01-17 01:31:39 +00:00
}
}
```
## Event Types
### Command Events
Triggered when agent commands are issued:
- **`command` **: All command events (general listener)
- **`command:new` **: When `/new` command is issued
- **`command:reset` **: When `/reset` command is issued
- **`command:stop` **: When `/stop` command is issued
2026-01-18 05:24:47 +00:00
### Agent Events
- **`agent:bootstrap` **: Before workspace bootstrap files are injected (hooks may mutate `context.bootstrapFiles` )
2026-01-18 10:10:05 +02:00
### Gateway Events
Triggered when the gateway starts:
- **`gateway:startup` **: After channels start and hooks are loaded
2026-01-20 09:05:24 +00:00
### Tool Result Hooks (Plugin API)
2026-01-30 03:15:10 +01:00
These hooks are not event-stream listeners; they let plugins synchronously adjust tool results before OpenClaw persists them.
2026-01-20 09:05:24 +00:00
- **`tool_result_persist` **: transform tool results before they are written to the session transcript. Must be synchronous; return the updated tool result payload or `undefined` to keep it as-is. See [Agent Loop ](/concepts/agent-loop ).
2026-01-17 01:31:39 +00:00
### Future Events
Planned event types:
- **`session:start` **: When a new session begins
- **`session:end` **: When a session ends
- **`agent:error` **: When an agent encounters an error
- **`message:sent` **: When a message is sent
- **`message:received` **: When a message is received
## Creating Custom Hooks
### 1. Choose Location
- **Workspace hooks** (`<workspace>/hooks/` ): Per-agent, highest precedence
2026-01-30 03:15:10 +01:00
- **Managed hooks** (`~/.openclaw/hooks/` ): Shared across workspaces
2026-01-17 01:31:39 +00:00
### 2. Create Directory Structure
```bash
2026-01-30 03:15:10 +01:00
mkdir -p ~/.openclaw/hooks/my-hook
cd ~/.openclaw/hooks/my-hook
2026-01-17 01:31:39 +00:00
```
### 3. Create HOOK.md
```markdown
---
name: my-hook
description: "Does something useful"
2026-01-31 21:13:13 +09:00
metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } }
2026-01-17 01:31:39 +00:00
---
# My Custom Hook
This hook does something useful when you issue `/new` .
```
### 4. Create handler.ts
```typescript
2026-01-31 21:13:13 +09:00
import type { HookHandler } from "../../src/hooks/hooks.js";
2026-01-17 01:31:39 +00:00
2026-01-17 07:32:50 +00:00
const handler: HookHandler = async (event) => {
2026-01-31 21:13:13 +09:00
if (event.type !== "command" || event.action !== "new") {
2026-01-17 01:31:39 +00:00
return;
}
2026-01-31 21:13:13 +09:00
console.log("[my-hook] Running!");
2026-01-17 01:31:39 +00:00
// Your logic here
};
export default handler;
```
### 5. Enable and Test
```bash
# Verify hook is discovered
2026-01-30 03:15:10 +01:00
openclaw hooks list
2026-01-17 01:31:39 +00:00
# Enable it
2026-01-30 03:15:10 +01:00
openclaw hooks enable my-hook
2026-01-17 01:31:39 +00:00
# Restart your gateway process (menu bar app restart on macOS, or restart your dev process)
# Trigger the event
# Send /new via your messaging channel
```
## Configuration
### New Config Format (Recommended)
```json
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"session-memory": { "enabled": true },
"command-logger": { "enabled": false }
}
}
}
}
```
### Per-Hook Configuration
Hooks can have custom configuration:
```json
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"my-hook": {
"enabled": true,
"env": {
"MY_CUSTOM_VAR": "value"
}
}
}
}
}
}
```
### Extra Directories
Load hooks from additional directories:
```json
{
"hooks": {
"internal": {
"enabled": true,
"load": {
"extraDirs": ["/path/to/more/hooks"]
}
}
}
}
```
### Legacy Config Format (Still Supported)
The old config format still works for backwards compatibility:
```json
{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts",
"export": "default"
}
]
}
}
}
```
2026-02-14 14:04:29 +01:00
Note: `module` must be a workspace-relative path. Absolute paths and traversal outside the workspace are rejected.
2026-01-17 01:31:39 +00:00
**Migration**: Use the new discovery-based system for new hooks. Legacy handlers are loaded after directory-based hooks.
## CLI Commands
### List Hooks
```bash
# List all hooks
2026-01-30 03:15:10 +01:00
openclaw hooks list
2026-01-17 01:31:39 +00:00
# Show only eligible hooks
2026-01-30 03:15:10 +01:00
openclaw hooks list --eligible
2026-01-17 01:31:39 +00:00
# Verbose output (show missing requirements)
2026-01-30 03:15:10 +01:00
openclaw hooks list --verbose
2026-01-17 01:31:39 +00:00
# JSON output
2026-01-30 03:15:10 +01:00
openclaw hooks list --json
2026-01-17 01:31:39 +00:00
```
### Hook Information
```bash
# Show detailed info about a hook
2026-01-30 03:15:10 +01:00
openclaw hooks info session-memory
2026-01-17 01:31:39 +00:00
# JSON output
2026-01-30 03:15:10 +01:00
openclaw hooks info session-memory --json
2026-01-17 01:31:39 +00:00
```
### Check Eligibility
```bash
# Show eligibility summary
2026-01-30 03:15:10 +01:00
openclaw hooks check
2026-01-17 01:31:39 +00:00
# JSON output
2026-01-30 03:15:10 +01:00
openclaw hooks check --json
2026-01-17 01:31:39 +00:00
```
### Enable/Disable
```bash
# Enable a hook
2026-01-30 03:15:10 +01:00
openclaw hooks enable session-memory
2026-01-17 01:31:39 +00:00
# Disable a hook
2026-01-30 03:15:10 +01:00
openclaw hooks disable command-logger
2026-01-17 01:31:39 +00:00
```
2026-02-06 09:35:57 -05:00
## Bundled hook reference
2026-01-17 01:31:39 +00:00
### session-memory
Saves session context to memory when you issue `/new` .
**Events**: `command:new`
**Requirements**: `workspace.dir` must be configured
2026-01-30 03:15:10 +01:00
**Output**: `<workspace>/memory/YYYY-MM-DD-slug.md` (defaults to `~/.openclaw/workspace` )
2026-01-17 01:31:39 +00:00
**What it does**:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
1. Uses the pre-reset session entry to locate the correct transcript
2. Extracts the last 15 lines of conversation
3. Uses LLM to generate a descriptive filename slug
4. Saves session metadata to a dated memory file
**Example output**:
```markdown
# Session: 2026-01-16 14:30:00 UTC
- **Session Key**: agent:main:main
- **Session ID**: abc123def456
- **Source**: telegram
```
**Filename examples**:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
- `2026-01-16-vendor-pitch.md`
- `2026-01-16-api-design.md`
- `2026-01-16-1430.md` (fallback timestamp if slug generation fails)
**Enable**:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks enable session-memory
2026-01-17 01:31:39 +00:00
```
Plugin API: compaction/reset hooks, bootstrap file globs, memory plugin status (#13287)
* feat: add before_compaction and before_reset plugin hooks with session context
- Pass session messages to before_compaction hook
- Add before_reset plugin hook for /new and /reset commands
- Add sessionId to plugin hook agent context
* feat: extraBootstrapFiles config with glob pattern support
Add extraBootstrapFiles to agent defaults config, allowing glob patterns
(e.g. "projects/*/TOOLS.md") to auto-load project-level bootstrap files
into agent context every turn. Missing files silently skipped.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(status): show custom memory plugins as enabled, not unavailable
The status command probes memory availability using the built-in
memory-core manager. Custom memory plugins (e.g. via plugin slot)
can't be probed this way, so they incorrectly showed "unavailable".
Now they show "enabled (plugin X)" without the misleading label.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use async fs.glob and capture pre-compaction messages
- Replace globSync (node:fs) with fs.glob (node:fs/promises) to match
codebase conventions for async file operations
- Capture session.messages BEFORE replaceMessages(limited) so
before_compaction hook receives the full conversation history,
not the already-truncated list
* fix: resolve lint errors from CI (oxlint strict mode)
- Add void to fire-and-forget IIFE (no-floating-promises)
- Use String() for unknown catch params in template literals
- Add curly braces to single-statement if (curly rule)
* fix: resolve remaining CI lint errors in workspace.ts
- Remove `| string` from WorkspaceBootstrapFileName union (made all
typeof members redundant per no-redundant-type-constituents)
- Use type assertion for extra bootstrap file names
- Drop redundant await on fs.glob() AsyncIterable (await-thenable)
* fix: address Greptile review — path traversal guard + fs/promises import
- workspace.ts: use path.resolve() + traversal check in loadExtraBootstrapFiles()
- commands-core.ts: import fs from node:fs/promises, drop fs.promises prefix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve symlinks before workspace boundary check
Greptile correctly identified that symlinks inside the workspace could
point to files outside it, bypassing the path prefix check. Now uses
fs.realpath() to resolve symlinks before verifying the real path stays
within the workspace boundary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address Greptile review — hook reliability and type safety
1. before_compaction: add compactingCount field so plugins know both
the full pre-compaction message count and the truncated count being
fed to the compaction LLM. Clarify semantics in comment.
2. loadExtraBootstrapFiles: use path.basename() for the name field
so "projects/quaid/TOOLS.md" maps to the known "TOOLS.md" type
instead of an invalid WorkspaceBootstrapFileName cast.
3. before_reset: fire the hook even when no session file exists.
Previously, short sessions without a persisted file would silently
skip the hook. Now fires with empty messages array so plugins
always know a reset occurred.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: validate bootstrap filenames and add compaction hook timeout
- Only load extra bootstrap files whose basename matches a recognized
workspace filename (AGENTS.md, TOOLS.md, etc.), preventing arbitrary
files from being injected into agent context.
- Wrap before_compaction hook in a 30-second Promise.race timeout so
misbehaving plugins cannot stall the compaction pipeline.
- Clarify hook comments: before_compaction is intentionally awaited
(plugins need messages before they're discarded) but bounded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make before_compaction non-blocking, add sessionFile to after_compaction
- before_compaction is now true fire-and-forget — no await, no timeout.
Plugins that need full conversation data should persist it themselves
and return quickly, or use after_compaction for async processing.
- after_compaction now includes sessionFile path so plugins can read
the full JSONL transcript asynchronously. All pre-compaction messages
are preserved on disk, eliminating the need to block compaction.
- Removes Promise.race timeout pattern that didn't actually cancel
slow hooks (just raced past them while they continued running).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add sessionFile to before_compaction for parallel processing
The session JSONL already has all messages on disk before compaction
starts. By providing sessionFile in before_compaction, plugins can
read and extract data in parallel with the compaction LLM call rather
than waiting for after_compaction. This is the optimal path for memory
plugins that need the full conversation history.
sessionFile is also kept on after_compaction for plugins that only
need to act after compaction completes (analytics, cleanup, etc.).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move bootstrap extras into bundled hook
---------
Co-authored-by: Solomon Steadman <solstead@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Clawdbot <clawdbot@alfie.local>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 06:45:45 +07:00
### bootstrap-extra-files
Injects additional bootstrap files (for example monorepo-local `AGENTS.md` / `TOOLS.md` ) during `agent:bootstrap` .
**Events**: `agent:bootstrap`
**Requirements**: `workspace.dir` must be configured
**Output**: No files written; bootstrap context is modified in-memory only.
**Config**:
```json
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"bootstrap-extra-files": {
"enabled": true,
"paths": ["packages/*/AGENTS.md", "packages/*/TOOLS.md"]
}
}
}
}
}
```
**Notes**:
- Paths are resolved relative to workspace.
- Files must stay inside workspace (realpath-checked).
- Only recognized bootstrap basenames are loaded.
- Subagent allowlist is preserved (`AGENTS.md` and `TOOLS.md` only).
**Enable**:
```bash
openclaw hooks enable bootstrap-extra-files
```
2026-01-17 01:31:39 +00:00
### command-logger
Logs all command events to a centralized audit file.
**Events**: `command`
**Requirements**: None
2026-01-30 03:15:10 +01:00
**Output**: `~/.openclaw/logs/commands.log`
2026-01-17 01:31:39 +00:00
**What it does**:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
1. Captures event details (command action, timestamp, session key, sender ID, source)
2. Appends to log file in JSONL format
3. Runs silently in the background
**Example log entries**:
```jsonl
{"timestamp":"2026-01-16T14:30:00.000Z","action":"new","sessionKey":"agent:main:main","senderId":"+1234567890","source":"telegram"}
{"timestamp":"2026-01-16T15:45:22.000Z","action":"stop","sessionKey":"agent:main:main","senderId":"user@example .com","source":"whatsapp"}
```
**View logs**:
```bash
# View recent commands
2026-01-30 03:15:10 +01:00
tail -n 20 ~/.openclaw/logs/commands.log
2026-01-17 01:31:39 +00:00
# Pretty-print with jq
2026-01-30 03:15:10 +01:00
cat ~/.openclaw/logs/commands.log | jq .
2026-01-17 01:31:39 +00:00
# Filter by action
2026-01-30 03:15:10 +01:00
grep '"action":"new"' ~/.openclaw/logs/commands.log | jq .
2026-01-17 01:31:39 +00:00
```
**Enable**:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks enable command-logger
2026-01-17 01:31:39 +00:00
```
2026-01-18 10:10:05 +02:00
### boot-md
Runs `BOOT.md` when the gateway starts (after channels start).
Internal hooks must be enabled for this to run.
**Events**: `gateway:startup`
**Requirements**: `workspace.dir` must be configured
**What it does**:
2026-01-31 21:13:13 +09:00
2026-01-18 10:10:05 +02:00
1. Reads `BOOT.md` from your workspace
2. Runs the instructions via the agent runner
3. Sends any requested outbound messages via the message tool
**Enable**:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks enable boot-md
2026-01-18 10:10:05 +02:00
```
2026-01-17 01:31:39 +00:00
## Best Practices
### Keep Handlers Fast
Hooks run during command processing. Keep them lightweight:
```typescript
// ✓ Good - async work, returns immediately
2026-01-17 07:32:50 +00:00
const handler: HookHandler = async (event) => {
2026-01-17 01:31:39 +00:00
void processInBackground(event); // Fire and forget
};
// ✗ Bad - blocks command processing
2026-01-17 07:32:50 +00:00
const handler: HookHandler = async (event) => {
2026-01-17 01:31:39 +00:00
await slowDatabaseQuery(event);
await evenSlowerAPICall(event);
};
```
### Handle Errors Gracefully
Always wrap risky operations:
```typescript
2026-01-17 07:32:50 +00:00
const handler: HookHandler = async (event) => {
2026-01-17 01:31:39 +00:00
try {
await riskyOperation(event);
} catch (err) {
2026-01-31 21:13:13 +09:00
console.error("[my-handler] Failed:", err instanceof Error ? err.message : String(err));
2026-01-17 01:31:39 +00:00
// Don't throw - let other handlers run
}
};
```
### Filter Events Early
Return early if the event isn't relevant:
```typescript
2026-01-17 07:32:50 +00:00
const handler: HookHandler = async (event) => {
2026-01-17 01:31:39 +00:00
// Only handle 'new' commands
2026-01-31 21:13:13 +09:00
if (event.type !== "command" || event.action !== "new") {
2026-01-17 01:31:39 +00:00
return;
}
// Your logic here
};
```
### Use Specific Event Keys
Specify exact events in metadata when possible:
```yaml
2026-01-31 21:13:13 +09:00
metadata: { "openclaw": { "events": ["command:new"] } } # Specific
2026-01-17 01:31:39 +00:00
```
Rather than:
```yaml
2026-01-31 21:13:13 +09:00
metadata: { "openclaw": { "events": ["command"] } } # General - more overhead
2026-01-17 01:31:39 +00:00
```
## Debugging
### Enable Hook Logging
The gateway logs hook loading at startup:
```
Registered hook: session-memory -> command:new
Plugin API: compaction/reset hooks, bootstrap file globs, memory plugin status (#13287)
* feat: add before_compaction and before_reset plugin hooks with session context
- Pass session messages to before_compaction hook
- Add before_reset plugin hook for /new and /reset commands
- Add sessionId to plugin hook agent context
* feat: extraBootstrapFiles config with glob pattern support
Add extraBootstrapFiles to agent defaults config, allowing glob patterns
(e.g. "projects/*/TOOLS.md") to auto-load project-level bootstrap files
into agent context every turn. Missing files silently skipped.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(status): show custom memory plugins as enabled, not unavailable
The status command probes memory availability using the built-in
memory-core manager. Custom memory plugins (e.g. via plugin slot)
can't be probed this way, so they incorrectly showed "unavailable".
Now they show "enabled (plugin X)" without the misleading label.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use async fs.glob and capture pre-compaction messages
- Replace globSync (node:fs) with fs.glob (node:fs/promises) to match
codebase conventions for async file operations
- Capture session.messages BEFORE replaceMessages(limited) so
before_compaction hook receives the full conversation history,
not the already-truncated list
* fix: resolve lint errors from CI (oxlint strict mode)
- Add void to fire-and-forget IIFE (no-floating-promises)
- Use String() for unknown catch params in template literals
- Add curly braces to single-statement if (curly rule)
* fix: resolve remaining CI lint errors in workspace.ts
- Remove `| string` from WorkspaceBootstrapFileName union (made all
typeof members redundant per no-redundant-type-constituents)
- Use type assertion for extra bootstrap file names
- Drop redundant await on fs.glob() AsyncIterable (await-thenable)
* fix: address Greptile review — path traversal guard + fs/promises import
- workspace.ts: use path.resolve() + traversal check in loadExtraBootstrapFiles()
- commands-core.ts: import fs from node:fs/promises, drop fs.promises prefix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve symlinks before workspace boundary check
Greptile correctly identified that symlinks inside the workspace could
point to files outside it, bypassing the path prefix check. Now uses
fs.realpath() to resolve symlinks before verifying the real path stays
within the workspace boundary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address Greptile review — hook reliability and type safety
1. before_compaction: add compactingCount field so plugins know both
the full pre-compaction message count and the truncated count being
fed to the compaction LLM. Clarify semantics in comment.
2. loadExtraBootstrapFiles: use path.basename() for the name field
so "projects/quaid/TOOLS.md" maps to the known "TOOLS.md" type
instead of an invalid WorkspaceBootstrapFileName cast.
3. before_reset: fire the hook even when no session file exists.
Previously, short sessions without a persisted file would silently
skip the hook. Now fires with empty messages array so plugins
always know a reset occurred.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: validate bootstrap filenames and add compaction hook timeout
- Only load extra bootstrap files whose basename matches a recognized
workspace filename (AGENTS.md, TOOLS.md, etc.), preventing arbitrary
files from being injected into agent context.
- Wrap before_compaction hook in a 30-second Promise.race timeout so
misbehaving plugins cannot stall the compaction pipeline.
- Clarify hook comments: before_compaction is intentionally awaited
(plugins need messages before they're discarded) but bounded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make before_compaction non-blocking, add sessionFile to after_compaction
- before_compaction is now true fire-and-forget — no await, no timeout.
Plugins that need full conversation data should persist it themselves
and return quickly, or use after_compaction for async processing.
- after_compaction now includes sessionFile path so plugins can read
the full JSONL transcript asynchronously. All pre-compaction messages
are preserved on disk, eliminating the need to block compaction.
- Removes Promise.race timeout pattern that didn't actually cancel
slow hooks (just raced past them while they continued running).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add sessionFile to before_compaction for parallel processing
The session JSONL already has all messages on disk before compaction
starts. By providing sessionFile in before_compaction, plugins can
read and extract data in parallel with the compaction LLM call rather
than waiting for after_compaction. This is the optimal path for memory
plugins that need the full conversation history.
sessionFile is also kept on after_compaction for plugins that only
need to act after compaction completes (analytics, cleanup, etc.).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move bootstrap extras into bundled hook
---------
Co-authored-by: Solomon Steadman <solstead@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Clawdbot <clawdbot@alfie.local>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 06:45:45 +07:00
Registered hook: bootstrap-extra-files -> agent:bootstrap
2026-01-17 01:31:39 +00:00
Registered hook: command-logger -> command
2026-01-18 10:10:05 +02:00
Registered hook: boot-md -> gateway:startup
2026-01-17 01:31:39 +00:00
```
### Check Discovery
List all discovered hooks:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks list --verbose
2026-01-17 01:31:39 +00:00
```
### Check Registration
In your handler, log when it's called:
```typescript
2026-01-17 07:32:50 +00:00
const handler: HookHandler = async (event) => {
2026-01-31 21:13:13 +09:00
console.log("[my-handler] Triggered:", event.type, event.action);
2026-01-17 01:31:39 +00:00
// Your logic
};
```
### Verify Eligibility
Check why a hook isn't eligible:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks info my-hook
2026-01-17 01:31:39 +00:00
```
Look for missing requirements in the output.
## Testing
### Gateway Logs
Monitor gateway logs to see hook execution:
```bash
# macOS
./scripts/clawlog.sh -f
# Other platforms
2026-01-30 03:15:10 +01:00
tail -f ~/.openclaw/gateway.log
2026-01-17 01:31:39 +00:00
```
### Test Hooks Directly
Test your handlers in isolation:
```typescript
2026-01-31 21:13:13 +09:00
import { test } from "vitest";
import { createHookEvent } from "./src/hooks/hooks.js";
import myHandler from "./hooks/my-hook/handler.js";
2026-01-17 01:31:39 +00:00
2026-01-31 21:13:13 +09:00
test("my handler works", async () => {
const event = createHookEvent("command", "new", "test-session", {
foo: "bar",
2026-01-17 01:31:39 +00:00
});
await myHandler(event);
// Assert side effects
});
```
## Architecture
### Core Components
- **`src/hooks/types.ts` **: Type definitions
- **`src/hooks/workspace.ts` **: Directory scanning and loading
- **`src/hooks/frontmatter.ts` **: HOOK.md metadata parsing
- **`src/hooks/config.ts` **: Eligibility checking
- **`src/hooks/hooks-status.ts` **: Status reporting
- **`src/hooks/loader.ts` **: Dynamic module loader
2026-01-17 07:08:04 +00:00
- **`src/cli/hooks-cli.ts` **: CLI commands
2026-01-17 01:31:39 +00:00
- **`src/gateway/server-startup.ts` **: Loads hooks at gateway start
- **`src/auto-reply/reply/commands-core.ts` **: Triggers command events
### Discovery Flow
```
Gateway startup
↓
Scan directories (workspace → managed → bundled)
↓
Parse HOOK.md files
↓
Check eligibility (bins, env, config, os)
↓
Load handlers from eligible hooks
↓
Register handlers for events
```
### Event Flow
```
User sends /new
↓
Command validation
↓
Create hook event
↓
Trigger hook (all registered handlers)
↓
Command processing continues
↓
Session reset
```
## Troubleshooting
### Hook Not Discovered
1. Check directory structure:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
```bash
2026-01-30 03:15:10 +01:00
ls -la ~/.openclaw/hooks/my-hook/
2026-01-17 01:31:39 +00:00
# Should show: HOOK.md, handler.ts
```
2. Verify HOOK.md format:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
```bash
2026-01-30 03:15:10 +01:00
cat ~/.openclaw/hooks/my-hook/HOOK.md
2026-01-17 01:31:39 +00:00
# Should have YAML frontmatter with name and metadata
```
3. List all discovered hooks:
2026-02-06 10:08:59 -05:00
2026-01-17 01:31:39 +00:00
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks list
2026-01-17 01:31:39 +00:00
```
### Hook Not Eligible
Check requirements:
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks info my-hook
2026-01-17 01:31:39 +00:00
```
Look for missing:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
- Binaries (check PATH)
- Environment variables
- Config values
- OS compatibility
### Hook Not Executing
1. Verify hook is enabled:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks list
2026-01-17 01:31:39 +00:00
# Should show ✓ next to enabled hooks
```
2. Restart your gateway process so hooks reload.
3. Check gateway logs for errors:
2026-02-06 10:08:59 -05:00
2026-01-17 01:31:39 +00:00
```bash
./scripts/clawlog.sh | grep hook
```
### Handler Errors
Check for TypeScript/import errors:
```bash
# Test import directly
node -e "import('./path/to/handler.ts').then(console.log)"
```
## Migration Guide
### From Legacy Config to Discovery
**Before**:
```json
{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts"
}
]
}
}
}
```
**After**:
1. Create hook directory:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
```bash
2026-01-30 03:15:10 +01:00
mkdir -p ~/.openclaw/hooks/my-hook
mv ./hooks/handlers/my-handler.ts ~/.openclaw/hooks/my-hook/handler.ts
2026-01-17 01:31:39 +00:00
```
2. Create HOOK.md:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
```markdown
---
name: my-hook
description: "My custom hook"
2026-01-31 21:13:13 +09:00
metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } }
2026-01-17 01:31:39 +00:00
---
# My Hook
Does something useful.
```
3. Update config:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
```json
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"my-hook": { "enabled": true }
}
}
}
}
```
4. Verify and restart your gateway process:
2026-02-06 10:08:59 -05:00
2026-01-17 01:31:39 +00:00
```bash
2026-01-30 03:15:10 +01:00
openclaw hooks list
2026-01-17 01:31:39 +00:00
# Should show: 🎯 my-hook ✓
```
**Benefits of migration**:
2026-01-31 21:13:13 +09:00
2026-01-17 01:31:39 +00:00
- Automatic discovery
- CLI management
- Eligibility checking
- Better documentation
- Consistent structure
## See Also
2026-01-17 07:08:04 +00:00
- [CLI Reference: hooks ](/cli/hooks )
2026-01-30 03:15:10 +01:00
- [Bundled Hooks README ](https://github.com/openclaw/openclaw/tree/main/src/hooks/bundled )
2026-01-17 01:31:39 +00:00
- [Webhook Hooks ](/automation/webhook )
- [Configuration ](/gateway/configuration#hooks )