OpenClaw 钩子(Hooks)CLI 完全指南:事件驱动自动化与 `openclaw hooks` 命令实战
OpenClaw 钩子(Hooks)CLI 完全指南:事件驱动自动化与 openclaw hooks 命令实战
本文围绕 OpenClaw(中文社区版 openclaw-cn)的钩子(Hooks)体系展开,系统讲解 openclaw hooks 系列命令的用法、钩子的发现机制、HOOK.md 元数据格式、事件类型、配置方式以及内置钩子的实战细节。读完本文,你将掌握如何用钩子实现会话记忆快照、命令审计、网关启动自动化等事件驱动能力,并能独立编写、安装、调试属于自己的自定义钩子。
什么是 Hooks:网关内的事件驱动自动化
Hooks 是一类运行在 OpenClaw 网关(Gateway)进程内部的轻量脚本,它们在代理事件(如 /new、/reset、/stop)或生命周期事件(如网关启动)触发时被同步调用。与外部 HTTP 回调的 Webhooks 不同(见 Webhook Hooks),本页所述的 Hooks 直接嵌入命令处理流程,与 skills 的管理方式类似——自动发现、CLI 管理、可开关。
常见用途包括:
- 执行
/new重置会话时,自动把会话上下文保存为记忆快照; - 记录所有命令事件,形成集中审计日志,便于排查问题或满足合规要求;
- 会话开始/结束时触发后续自动化动作;
- 事件触发时向代理工作区写入文件或调用外部 API。
只要你"会写一个小的 TypeScript 函数",就能写一个 hook。钩子由系统自动发现,通过 CLI 启用或禁用。
从源码结构看,钩子系统的核心实现位于 src/hooks/:internal-hooks.ts 维护事件注册表与触发逻辑,workspace.ts 负责目录扫描,frontmatter.ts 负责解析 HOOK.md 元数据,config.ts 负责资格(eligibility)判定,loader.ts 负责动态加载处理器模块,hooks-cli.ts 实现全部 CLI 命令。
CLI 命令速查
所有命令均以 openclaw hooks 为前缀,管理钩子的发现、状态、开关与安装:
| 命令 | 作用 |
|---|---|
openclaw hooks list |
列出工作区、托管、内置目录中发现的所有钩子 |
openclaw hooks info <name> |
查看单个钩子的详细信息 |
openclaw hooks check |
汇总钩子资格状态(就绪/未就绪数量) |
openclaw hooks enable <name> |
启用钩子(写入配置) |
openclaw hooks disable <name> |
禁用钩子(更新配置) |
openclaw hooks install <path-or-spec> |
从本地目录/压缩包或 npm 安装钩子包 |
openclaw hooks update <id> / --all |
更新已安装的 npm 钩子包 |
列出所有钩子:openclaw hooks list
openclaw hooks list
列出从 workspace、managed、bundled 三个目录发现的所有钩子。
选项:
--eligible:只显示满足资格条件的钩子(requirements 已满足);--json:以 JSON 结构输出,便于程序化处理;-v, --verbose:显示详细信息,包括未满足的 requirements。
示例输出:
Hooks (3/3 ready)
Ready:
🚀 boot-md ✓ - Run BOOT.md on gateway startup
📝 command-logger ✓ - Log all command events to a centralized audit file
💾 session-memory ✓ - Save session context to memory when /new command is issued
verbose 模式:
openclaw hooks list --verbose
会进一步展示未就绪钩子缺失的 requirements(例如缺少某个 PATH 上的二进制、环境变量或配置项)。
JSON 模式:
openclaw hooks list --json
返回结构化 JSON,适合脚本集成。
查看钩子详情:openclaw hooks info <name>
openclaw hooks info <name>
参数:
<name>:钩子名,如session-memory。
选项:
--json:JSON 输出。
示例:
openclaw hooks info session-memory
输出:
💾 session-memory ✓ Ready
Save session context to memory when /new command is issued
Details:
Source: openclaw-bundled
Path: /path/to/openclaw/hooks/bundled/session-memory/HOOK.md
Handler: /path/to/openclaw/hooks/bundled/session-memory/handler.ts
Homepage: https://docs.openclaw.ai/hooks#session-memory
Events: command:new
Requirements:
Config: ✓ workspace.dir
输出中的 Source 字段标识钩子来源(openclaw-bundled、openclaw-managed、openclaw-workspace、openclaw-plugin),Events 列出监听的事件键,Requirements 展示资格判定结果。
检查资格:openclaw hooks check
openclaw hooks check
汇总显示有多少钩子就绪、多少未就绪。
选项:
--json:JSON 输出。
示例输出:
Hooks Status
Total hooks: 4
Ready: 4
Not ready: 0
启用/禁用钩子
openclaw hooks enable <name>
openclaw hooks disable <name>
enable 的具体行为(从 CLI 实现看):
- 检查钩子是否存在且满足资格条件;
- 在配置中写入
hooks.internal.entries.<name>.enabled = true(配置文件位于~/.openclaw/config.json); - 将配置保存到磁盘。
示例输出:
✓ Enabled hook: 💾 session-memory
⏸ Disabled hook: 📝 command-logger
重要限制:由插件托管的钩子在 openclaw hooks list 中显示为 plugin:<id>,不能在此处单独启用/禁用,应整体启用或禁用对应插件(详见 Plugins)。
启用/禁用后必须重启网关:macOS 上重启菜单栏应用,开发环境重启你的网关进程,钩子才会重新加载。
钩子的发现机制:三个目录与优先级
Hooks 从三个目录自动发现,优先级从高到低:
- Workspace hooks:
<workspace>/hooks/(按代理隔离,优先级最高); - Managed hooks:
~/.openclaw/hooks/(用户安装,跨工作区共享); - Bundled hooks:
<openclaw>/dist/hooks/bundled/(随 OpenClaw 内置发布)。
Managed 目录下的每个子目录既可以是一个单个钩子,也可以是一个钩子包(hook pack)。在 src/hooks/workspace.ts 中实现了上述目录的扫描与加载逻辑;src/hooks/bundled/ 目录则展示了内置钩子的真实结构:
session-memory/
├── HOOK.md # 元数据 + 文档
└── handler.ts # 处理器实现
HOOK.md 元数据格式
每个钩子目录都包含一个 HOOK.md,由 YAML frontmatter 元数据与 Markdown 文档组成:
---
name: my-hook
description: "Short description of what this hook does"
homepage: https://docs.openclaw.ai/hooks#my-hook
metadata:
{ "openclaw": { "emoji": "🔗", "events": ["command:new"], "requires": { "bins": ["node"] } } }
---
# My Hook
Detailed documentation goes here...
frontmatter 解析在 src/hooks/frontmatter.ts 中实现,它基于 parseFrontmatterBlock 读取 YAML,并用 normalizeStringList 将 events 等字段统一归一化为字符串数组(兼容数组写法与逗号分隔字符串),metadata.openclaw 对象支持的字段如下:
emoji:CLI 中展示的图标(如"💾");events:要监听的事件数组(如["command:new", "command:reset"]);export:要使用的命名导出(默认"default");homepage:文档 URL;requires:可选资格要求:bins:PATH 上必须存在的二进制(如["git", "node"]);anyBins:这些二进制中至少有一个存在即可;env:必须存在的环境变量;config:必须为真的配置路径(如["workspace.dir"]);os:要求的平台(如["darwin", "linux"]);
always:布尔值,为true时跳过所有资格检查;install:安装方式说明(内置钩子为[{"id":"bundled","kind":"bundled"}])。
requires 的判定逻辑集中在 src/hooks/config.ts 的 shouldIncludeHook() 中:它会依次检查显式禁用标记、OS 平台匹配、always 旁路、bins(全部必须存在,通过 hasBinary() 遍历 PATH 并用 fs.accessSync 验证可执行位)、anyBins(任一存在)、env 环境变量以及 config 配置路径(resolveConfigPath 按点号路径逐层取值,且 workspace.dir、browser.enabled 等在缺省时默认视为真)。也就是说,一个钩子只有在满足全部要求时才显示为"Ready"。
处理器实现与事件上下文
handler.ts 默认导出一个 HookHandler 函数:
import type { HookHandler } from "../../src/hooks/hooks.js";
const myHandler: HookHandler = async (event) => {
// 只处理 'new' 命令
if (event.type !== "command" || event.action !== "new") {
return;
}
console.log(`[my-hook] New command triggered`);
console.log(` Session: ${event.sessionKey}`);
console.log(` Timestamp: ${event.timestamp.toISOString()}`);
// 你的自定义逻辑
// 可选:向用户发送消息
event.messages.push("✨ My hook executed!");
};
export default myHandler;
每个事件携带以下上下文:
{
type: 'command' | 'session' | 'agent' | 'gateway',
action: string, // 如 'new'、'reset'、'stop'
sessionKey: string, // 会话标识
timestamp: Date, // 事件发生时间
messages: string[], // 向这里 push 消息即可发送给用户
context: {
sessionEntry?: SessionEntry,
sessionId?: string,
sessionFile?: string,
commandSource?: string, // 如 'whatsapp'、'telegram'
senderId?: string,
workspaceDir?: string,
bootstrapFiles?: WorkspaceBootstrapFile[],
cfg?: OpenClawConfig
}
}
事件触发原理(源码级)
src/hooks/internal-hooks.ts 中,事件注册表是一个 Map<string, InternalHookHandler[]>。triggerInternalHook(event) 触发一个事件时,会依次执行通用类型处理器(handlers.get(event.type))与特定 action 处理器(handlers.get(\{event.action}`)),两者按注册顺序串联调用;每个处理器的异常都会被捕获并打印 Hook error [type:action]: ...`,不会阻断其他处理器继续执行,也不会中断命令处理主流程。
事件触发入口位于命令处理链路中:从源码结构看,命令事件由 src/auto-reply/reply/commands-core.ts 触发,网关启动时由 src/gateway/server-startup.ts 加载并注册钩子,这与文档所述的"钩子在网关启动时加载、命令处理时触发"一致。
事件类型
命令事件(Command Events)
代理命令发出时触发:
command:所有命令事件的通用监听;command:new:发出/new时;command:reset:发出/reset时;command:stop:发出/stop时。
代理事件(Agent Events)
agent:bootstrap:工作区 bootstrap 文件注入前触发(钩子可以改写context.bootstrapFiles,从而实现动态注入初始文件)。
网关事件(Gateway Events)
gateway:startup:通道启动且钩子加载完成后触发,boot-md钩子正是监听此事件。
工具结果钩子(Tool Result Hooks,插件 API)
tool_result_persist 不是事件流监听器,而是让插件在 OpenClaw 持久化工具结果之前同步调整工具结果;必须同步执行,返回更新后的工具结果载荷或 undefined 保持原样。参见 Agent Loop。
规划中的事件
文档列出了计划中的事件类型:session:start、session:end、agent:error、message:sent、message:received。注意这些尚在规划阶段,不应在生产钩子中依赖。
配置方式
新配置格式(推荐)
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"session-memory": { "enabled": true },
"command-logger": { "enabled": false }
}
}
}
}
单钩子自定义配置
钩子可携带自定义环境变量:
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"my-hook": {
"enabled": true,
"env": {
"MY_CUSTOM_VAR": "value"
}
}
}
}
}
}
配置中的 env 在资格判定时也会被计入(见 src/hooks/config.ts 中对 hookConfig?.env?.[envName] 的检查),即通过配置提供的环境变量可以满足 requires.env 要求。
额外加载目录
{
"hooks": {
"internal": {
"enabled": true,
"load": {
"extraDirs": ["/path/to/more/hooks"]
}
}
}
}
openclaw hooks install -l 链接本地目录时,也正是写入该 hooks.internal.load.extraDirs。
旧版配置格式(仍受支持)
{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts",
"export": "default"
}
]
}
}
}
注意:module 必须是工作区相对路径,绝对路径与越出工作区的路径会被拒绝。旧版 handlers 在目录式钩子之后加载;新钩子请使用发现式系统。
安装与更新钩子
安装:openclaw hooks install <path-or-spec>
从本地文件夹/压缩包或 npm 安装钩子包,具体行为:
- 将钩子包复制到
~/.openclaw/hooks/<id>; - 在
hooks.internal.entries.*中启用已安装的钩子; - 在
hooks.internal.installs中记录安装来源。
选项:
-l, --link:链接本地目录而非复制(写入hooks.internal.load.extraDirs)。
支持的压缩包格式:.zip、.tgz、.tar.gz、.tar。
示例:
# 本地目录
openclaw hooks install ./my-hook-pack
# 本地压缩包
openclaw hooks install ./my-hook-pack.zip
# NPM 包
openclaw hooks install @openclaw/my-hook-pack
# 链接本地目录(不复制)
openclaw hooks install -l ./my-hook-pack
Hook Packs(npm/压缩包)
Hook packs 是标准的 npm 包,通过 package.json 中的 openclaw.hooks 字段导出一个或多个钩子:
{
"name": "@acme/my-hooks",
"version": "0.1.0",
"openclaw": {
"hooks": ["./hooks/my-hook", "./hooks/other-hook"]
}
}
每个条目指向一个包含 HOOK.md 与 handler.ts(或 index.ts)的钩子目录。Hook packs 可自带依赖,依赖会随包一起安装到 ~/.openclaw/hooks/<id> 下。
更新:openclaw hooks update
openclaw hooks update <id>
openclaw hooks update --all
仅支持 npm 安装的钩子包更新。
选项:
--all:更新所有被跟踪的钩子包;--dry-run:只显示将要发生的变更,不实际写入。
内置钩子(Bundled Hooks)详解
OpenClaw 自带三个自动发现的内置钩子,位于仓库 src/hooks/bundled/:
💾 session-memory
在发出 /new 时将会话上下文保存到记忆文件。监听 command:new 事件,要求配置了 workspace.dir。
工作流程:
- 利用重置前的 session entry 定位正确的 transcript;
- 提取对话最后 15 行;
- 用 LLM 根据对话内容生成有意义的文件名 slug(见 src/hooks/llm-slug-generator.ts);
- 将会话元数据保存到带日期的记忆文件。
输出位置:<workspace>/memory/YYYY-MM-DD-slug.md(默认 ~/.openclaw/workspace)。
示例输出:
# Session: 2026-01-16 14:30:00 UTC
- **Session Key**: agent:main:main
- **Session ID**: abc123def456
- **Source**: telegram
文件名示例:
2026-01-16-vendor-pitch.md2026-01-16-api-design.md2026-01-16-1430.md(slug 生成失败时的回退时间戳)
启用:
openclaw hooks enable session-memory
该钩子的真实 HOOK.md 可在 src/hooks/bundled/session-memory/HOOK.md 中查看,其中明确记录了 requires.config = <a href="https://link.gitcode.com/i/cc7238d0e1ed304b0608381cdc284c5f" target="_blank">"workspace.dir"] 以及 slug 生成失败时的回退逻辑。完整文档见 [automation/hooks 的 session-memory 一节。
📝 command-logger
将所有命令事件记录到集中审计文件。监听 command 事件(所有命令),无额外要求。
工作流程:
- 捕获事件细节(命令动作、时间戳、会话键、发送者 ID、来源);
- 以 JSONL 格式追加写入日志文件;
- 后台静默运行。
输出位置:~/.openclaw/logs/commands.log。
示例日志条目:
{"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"}
查看日志:
# 最近命令
tail -n 20 ~/.openclaw/logs/commands.log
# jq 美化输出
cat ~/.openclaw/logs/commands.log | jq .
# 按动作过滤
grep '"action":"new"' ~/.openclaw/logs/commands.log | jq .
启用:
openclaw hooks enable command-logger
🚀 boot-md
网关启动(通道启动之后)时运行 BOOT.md。监听 gateway:startup 事件,要求配置 workspace.dir,且需开启 internal hooks。
工作流程:
- 从工作区读取
BOOT.md; - 通过 agent runner 执行其中的指令;
- 通过消息工具发送任何请求的出站消息。
启用:
openclaw hooks enable boot-md
创建自定义钩子:从零到实战
1. 选择位置
- Workspace hooks(
<workspace>/hooks/):按代理隔离,优先级最高; - Managed hooks(
~/.openclaw/hooks/):跨工作区共享。
2. 创建目录结构
mkdir -p ~/.openclaw/hooks/my-hook
cd ~/.openclaw/hooks/my-hook
3. 创建 HOOK.md
---
name: my-hook
description: "Does something useful"
metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } }
---
# My Custom Hook
This hook does something useful when you issue `/new`.
4. 创建 handler.ts
import type { HookHandler } from "../../src/hooks/hooks.js";
const handler: HookHandler = async (event) => {
if (event.type !== "command" || event.action !== "new") {
return;
}
console.log("[my-hook] Running!");
// Your logic here
};
export default handler;
5. 启用并测试
# 验证钩子已被发现
openclaw hooks list
# 启用它
openclaw hooks enable my-hook
# 重启网关进程(macOS 重启菜单栏应用,或重启开发进程)
# 触发事件:通过你的消息通道发送 /new
6. 随网关开机自启(onboarding)
在执行 openclaw onboard 时,向导会自动发现符合条件的钩子并提示你选择启用推荐钩子,无需手动编辑配置。
最佳实践
保持处理器轻量
钩子运行在命令处理流程中,应保持轻量:
// ✓ 好:异步工作,立即返回
const handler: HookHandler = async (event) => {
void processInBackground(event); // Fire and forget
};
// ✗ 坏:阻塞命令处理
const handler: HookHandler = async (event) => {
await slowDatabaseQuery(event);
await evenSlowerAPICall(event);
};
优雅处理错误
始终用 try/catch 包裹风险操作,且不要抛出(源码 triggerInternalHook 虽会捕获并记录异常,但最佳实践是让其他处理器继续运行):
const handler: HookHandler = async (event) => {
try {
await riskyOperation(event);
} catch (err) {
console.error("[my-handler] Failed:", err instanceof Error ? err.message : String(err));
// Don't throw - let other handlers run
}
};
尽早过滤事件
与事件无关时立即 return:
const handler: HookHandler = async (event) => {
// 只处理 'new' 命令
if (event.type !== "command" || event.action !== "new") {
return;
}
// Your logic here
};
使用具体事件键
元数据中尽量指定精确事件:
metadata: { "openclaw": { "events": ["command:new"] } } # 具体,开销小
而不是:
metadata: { "openclaw": { "events": ["command"] } } # 通用,开销更大
调试与测试
查看网关启动日志
网关启动时会打印钩子加载信息:
Registered hook: session-memory -> command:new
Registered hook: command-logger -> command
Registered hook: boot-md -> gateway:startup
检查发现与注册
openclaw hooks list --verbose
在 handler 中打印触发信息:
const handler: HookHandler = async (event) => {
console.log("[my-handler] Triggered:", event.type, event.action);
// Your logic
};
验证资格
openclaw hooks info my-hook
输出中查找缺失的 requirements(二进制、环境变量、配置值、OS 兼容性)。
监控执行日志
# macOS
./scripts/clawlog.sh -f
# 其他平台
tail -f ~/.openclaw/gateway.log
单元测试(vitest)
可用 createHookEvent 构造事件对象,在隔离环境中直接测试处理器:
import { test } from "vitest";
import { createHookEvent } from "./src/hooks/hooks.js";
import myHandler from "./hooks/my-hook/handler.js";
test("my handler works", async () => {
const event = createHookEvent("command", "new", "test-session", {
foo: "bar",
});
await myHandler(event);
// Assert side effects
});
注意:createHookEvent 的源码实现(src/hooks/internal-hooks.ts 中的 createInternalHookEvent)会自动填充 timestamp: new Date() 与空 messages 数组,因此测试中无需手动构造这两个字段。
常见故障排查
钩子未被发现
- 检查目录结构:
ls -la ~/.openclaw/hooks/my-hook/ # 应显示:HOOK.md, handler.ts - 检查 HOOK.md 格式:
cat ~/.openclaw/hooks/my-hook/HOOK.md # 应有包含 name 与 metadata 的 YAML frontmatter - 列出所有已发现钩子:
openclaw hooks list
钩子不满足资格
openclaw hooks info my-hook
对照检查缺失项:二进制(PATH)、环境变量、配置值、OS 兼容性。
钩子不执行
- 确认已启用:
openclaw hooks list中启用钩子旁应显示 ✓; - 重启网关进程使钩子重新加载;
- 检查网关日志:
./scripts/clawlog.sh | grep hook
处理器报错
检查 TypeScript/import 错误:
node -e "import('./path/to/handler.ts').then(console.log)"
从旧版配置迁移到发现式钩子
迁移前:
{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts"
}
]
}
}
}
迁移步骤:
- 创建钩子目录并移动 handler:
mkdir -p ~/.openclaw/hooks/my-hook mv ./hooks/handlers/my-handler.ts ~/.openclaw/hooks/my-hook/handler.ts - 创建 HOOK.md:
--- name: my-hook description: "My custom hook" metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } } --- # My Hook Does something useful. - 更新配置:
{ "hooks": { "internal": { "enabled": true, "entries": { "my-hook": { "enabled": true } } } } } - 验证并重启网关:
openclaw hooks list # 应显示:🎯 my-hook ✓
迁移收益:自动发现、CLI 管理、资格检查、更好的文档化与统一结构。
架构速览
核心组件(源码路径)
- src/hooks/types.ts:类型定义(Hook、HookEntry、OpenclawHookMetadata 等);
- src/hooks/workspace.ts:目录扫描与加载;
- src/hooks/frontmatter.ts:HOOK.md 元数据解析;
- src/hooks/config.ts:资格检查(bins/env/config/os/always);
- src/hooks/hooks-status.ts:状态报告;
- src/hooks/loader.ts:动态模块加载器;
- src/hooks/internal-hooks.ts:事件注册表与触发器;
- src/cli/hooks-cli.ts:CLI 命令实现;
- src/gateway/server-startup.ts:网关启动时加载钩子;
- src/auto-reply/reply/commands-core.ts:触发命令事件。
发现流程
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
事件流程
User sends /new
↓
Command validation
↓
Create hook event
↓
Trigger hook (all registered handlers)
↓
Command processing continues
↓
Session reset