跳到內容

使用 OpenClaw Hooks 自動化代理程式:設定指南

Hooks 是在你執行某些操作時會自動執行的腳本。主要分為兩類:

  • Hooks(本頁面):在 Gateway 內部執行,當 agent 事件觸發時(例如 /new、/reset、/stop 或生命週期事件)就會執行。
  • Webhooks:外部 HTTP webhooks,讓其他系統可以觸發 OpenClaw 的工作。請參考 Webhook Hooks 或使用 openclaw webhooks 取得 Gmail 輔助指令。

Hooks 也可以封裝在 plugin 裡面;請參考 Plugin hooks。使用 openclaw hooks list 可以同時查看獨立的 hooks 與由 plugin 管理的 hooks。

常見用途:

  • 在你重置 session 時儲存記憶快照
  • 保留指令的稽核軌跡,用於疑難排解或合規審查
  • 在 session 開始或結束時觸發後續的自動化流程
  • 當事件觸發時,將檔案寫入 agent workspace 或呼叫外部 API

只要你會寫簡單的 TypeScript function,你就能寫 hook。Managed 與封裝好的 hooks 是受信任的本地程式碼。Workspace hooks 會被自動偵測,但 OpenClaw 會保持禁用狀態,直到你明確透過 CLI 或設定檔啟用它們。

Hooks 系統讓你能夠:

  • 在發送 /new 指令時將 session 上下文儲存到記憶體中
  • 記錄所有指令以供稽核
  • 在 agent 生命週期事件中觸發自定義自動化
  • 在不修改核心程式碼的情況下擴展 OpenClaw 的行為

OpenClaw 附帶了四個會自動偵測的內建 hooks:

  • 💾 session-memory:當你執行 /new 或 /reset 時,將 session 上下文儲存到你的 agent workspace(預設為 ~/.openclaw/workspace/memory/)
  • 📎 bootstrap-extra-files:在 agent:bootstrap 期間,從設定的 glob/路徑模式注入額外的 workspace 引導檔案
  • 📝 command-logger:將所有指令事件記錄到 ~/.openclaw/logs/commands.log
  • 🚀 boot-md:當 gateway 啟動時執行 BOOT.md(需要啟用內部 hooks)

列出可用的 hooks:

Terminal window
openclaw hooks list

啟用 hook:

Terminal window
openclaw hooks enable session-memory

檢查 hook 狀態:

Terminal window
openclaw hooks check

取得詳細資訊:

Terminal window
openclaw hooks info session-memory

在引導流程(openclaw onboard)期間,系統會提示你啟用建議的 hooks。小幫手會自動偵測符合條件的 hooks 並讓你選擇。

Hooks 在 Gateway 程序中執行。請將內建 hooks、managed hooks 以及 hooks.internal.load.extraDirs 視為受信任的本地程式碼。位於 <workspace>/hooks/ 下的 Workspace hooks 是屬於 repo 本地的程式碼,因此 OpenClaw 在載入它們之前,需要你執行明確的啟用步驟。

Hooks 會自動從以下目錄偵測,優先順序由低到高(後者會覆蓋前者):

  1. 內建 hooks:隨 OpenClaw 附帶;npm 安裝版位於 <openclaw>/dist/hooks/bundled/(或編譯二進位檔旁的 hooks/bundled/)
  2. Plugin hooks:封裝在已安裝 plugin 中的 hooks(請參考 Plugin hooks)
  3. Managed hooks:~/.openclaw/hooks/(使用者安裝,跨 workspace 共用;可以覆蓋內建與 plugin hooks)。透過 hooks.internal.load.extraDirs 設定的額外 hook 目錄也會被視為 managed hooks,並享有相同的覆蓋優先順序。
  4. Workspace hooks:<workspace>/hooks/(每個 agent 獨立,預設禁用直到明確啟用;無法覆蓋來自其他來源的 hooks)

Workspace hooks 可以為 repo 增加新的 hook 名稱,但它們不能覆蓋具有相同名稱的內建、managed 或 plugin 提供的 hooks。

Managed hook 目錄可以是一個單獨的 hook,也可以是一個 hook pack(套件目錄)。

每個 hook 都是一個包含以下內容的目錄:

my-hook/
├── HOOK.md # Metadata + documentation
└── handler.ts # Handler implementation

Hook packs 是標準的 npm 套件,透過 package.json 中的 openclaw.hooks 匯出一個或多個 hook。你可以用這個指令安裝:

Terminal window
openclaw plugins install <path-or-spec>

Npm specs 僅限 registry(套件名稱 + 選填的精確版本或 dist-tag)。Git/URL/檔案路徑以及 semver 範圍都會被拒絕。

一般的 spec 和 @latest 會保持在穩定版本。如果 npm 將其中之一解析為預發佈版本(prerelease),OpenClaw 會停止執行並要求你明確選擇加入,例如使用 @beta / @rc 等標籤或精確的預發佈版本號。

package.json 範例:

{
"name": "@acme/my-hooks",
"version": "0.1.0",
"openclaw": {
"hooks": ["./hooks/my-hook", "./hooks/other-hook"]
}
}

每個項目都指向一個包含 HOOK.md 和 handler.ts(或 index.ts)的 hook 目錄。Hook packs 可以包含依賴項目;它們會被安裝在 ~/.openclaw/hooks/<id> 下。每個 openclaw.hooks 的項目在解析符號連結(symlink)後必須留在套件目錄內,超出範圍的項目將被拒絕。

安全提示:openclaw plugins install 會使用 npm install --ignore-scripts 安裝 hook-pack 的依賴項目(不執行生命週期腳本)。請確保 hook pack 的依賴樹是「純 JS/TS」,並避免使用依賴 postinstall 建置的套件。

HOOK.md 檔案包含 YAML frontmatter 的元數據以及 Markdown 文件:

---
name: my-hook
description: "Short description of what this hook does"
homepage: https://docs.openclaw.ai/automation/hooks#my-hook
metadata:
{ "openclaw": { "emoji": "🔗", "events": ["command:new"], "requires": { "bins": ["node"] } } }
---
# 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.openclaw 物件支援以下欄位:

  • emoji: 在 CLI 顯示的表情符號(例如 "💾")
  • events: 要監聽的事件陣列(例如 ["command:new", "command:reset"])
  • export: 要使用的具名匯出(預設為 "default")
  • homepage: 文件 URL
  • os: 需要的作業系統平台(例如 ["darwin", "linux"])
  • requires: 選填的需求
    • bins: 在 PATH 中需要的執行檔(例如 ["git", "node"])
    • anyBins: 至少需要存在其中一個執行檔
    • env: 需要的環境變數
    • config: 需要的設定路徑(例如 ["workspace.dir"])
  • always: 繞過資格檢查(布林值)
  • install: 安裝方法(針對內建 hook:[{"id":"bundled","kind":"bundled"}])

handler.ts 檔案匯出一個 HookHandler 函式:

const myHandler = async (event) => {
// Only trigger on 'new' command
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()}`);
// Your custom logic here
// Optionally send message to user
event.messages.push("✨ My hook executed!");
};
export default myHandler;

每個事件都包含:

{
type: 'command' | 'session' | 'agent' | 'gateway' | 'message',
action: string, // e.g., 'new', 'reset', 'stop', 'received', 'sent'
sessionKey: string, // Session identifier
timestamp: Date, // When the event occurred
messages: string[], // Push messages here to send to user
context: {
// Command events (command:new, command:reset):
sessionEntry?: SessionEntry, // current session entry
previousSessionEntry?: SessionEntry, // pre-reset entry (preferred for session-memory)
commandSource?: string, // e.g., 'whatsapp', 'telegram'
senderId?: string,
workspaceDir?: string,
cfg?: OpenClawConfig,
// Command events (command:stop only):
sessionId?: string,
// Agent bootstrap events (agent:bootstrap):
bootstrapFiles?: WorkspaceBootstrapFile[],
// Message events (see Message Events section for full details):
from?: string, // message:received
to?: string, // message:sent
content?: string,
channelId?: string,
success?: boolean, // message:sent
}
}

當代理指令發出時觸發:

  • command: 所有指令事件(通用監聽器)
  • command:new: 當發出 /new 指令時
  • command:reset: 當發出 /reset 指令時
  • command:stop: 當發出 /stop 指令時
  • session:compact:before: 在壓縮(compaction)總結歷史紀錄之前
  • session:compact:after: 在壓縮完成並帶有總結元數據之後

內部 hook 負載會將這些發送為 type: "session" 且 action: "compact:before" / action: "compact:after";監聽器則使用上述組合鍵進行訂閱。特定的 handler 註冊使用 ${type}:${action} 的字面格式。對於這些事件,請註冊 session:compact:before 和 session:compact:after。

  • agent:bootstrap: 在注入 workspace 引導檔案之前(hook 可以修改 context.bootstrapFiles)

當 Gateway 啟動時觸發:

  • gateway:startup: 在頻道啟動且 hook 載入之後

當會話屬性被修改時觸發:

  • session:patch: 當會話更新時

會話事件包含關於會話及其變更的豐富上下文:

{
sessionEntry: SessionEntry, // The complete updated session entry
patch: { // The patch object (only changed fields)
// Session identity & labeling
label?: string | null, // Human-readable session label
// AI model configuration
model?: string | null, // Model override (e.g., "claude-opus-4-5")
thinkingLevel?: string | null, // Thinking level ("off"|"low"|"med"|"high")
verboseLevel?: string | null, // Verbose output level
reasoningLevel?: string | null, // Reasoning mode override
elevatedLevel?: string | null, // Elevated mode override
responseUsage?: "off" | "tokens" | "full" | null, // Usage display mode
// Tool execution settings
execHost?: string | null, // Exec host (sandbox|gateway|node)
execSecurity?: string | null, // Security mode (deny|allowlist|full)
execAsk?: string | null, // Approval mode (off|on-miss|always)
execNode?: string | null, // Node ID for host=node
// Subagent coordination
spawnedBy?: string | null, // Parent session key (for subagents)
spawnDepth?: number | null, // Nesting depth (0 = root)
// Communication policies
sendPolicy?: "allow" | "deny" | null, // Message send policy
groupActivation?: "mention" | "always" | null, // Group chat activation
},
cfg: OpenClawConfig // Current gateway config
}

安全提示: 只有具備權限的用戶端(包括 Control UI)可以觸發 session:patch 事件。標準的 WebChat 用戶端無法修補會話(參見 PR #20800),因此 hook 不會從這些連線中觸發。

完整類型定義請參見 src/gateway/protocol/schema/sessions.ts 中的 SessionsPatchParamsSchema。

const handler = async (event) => {
if (event.type !== "session" || event.action !== "patch") {
return;
}
const { patch } = event.context;
console.log(`[session-patch] Session updated: ${event.sessionKey}`);
console.log(`[session-patch] Changes:`, patch);
};
export default handler;

當接收或發送訊息時觸發:

  • message: 所有訊息事件(通用監聽器)
  • message:received: 當從任何頻道接收到傳入訊息時。在處理早期、媒體理解之前觸發。內容可能包含原始佔位符,例如尚未處理的媒體附件 <media:audio>。
  • message:transcribed: 當訊息已完全處理,包括音訊轉錄和連結理解。此時,transcript 包含音訊訊息的完整轉錄文字。當你需要存取轉錄後的音訊內容時,請使用此 hook。
  • message:preprocessed: 在所有媒體和連結理解完成後,針對每條訊息觸發,讓 hook 在代理看到之前存取完整豐富的內容(轉錄、圖片描述、連結摘要)。
  • message:sent: 當傳出訊息成功發送時

訊息事件包含關於訊息的豐富上下文:

// message:received context
{
from: string, // Sender identifier (phone number, user ID, etc.)
content: string, // Message content
timestamp?: number, // Unix timestamp when received
channelId: string, // Channel (e.g., "whatsapp", "telegram", "discord")
accountId?: string, // Provider account ID for multi-account setups
conversationId?: string, // Chat/conversation ID
messageId?: string, // Message ID from the provider
metadata?: { // Additional provider-specific data
to?: string,
provider?: string,
surface?: string,
threadId?: string | number,
senderId?: string,
senderName?: string,
senderUsername?: string,
senderE164?: string,
guildId?: string, // Discord guild / server ID
channelName?: string, // Channel name (e.g., Discord channel name)
}
}
// message:sent context
{
to: string, // Recipient identifier
content: string, // Message content that was sent
success: boolean, // Whether the send succeeded
error?: string, // Error message if sending failed
channelId: string, // Channel (e.g., "whatsapp", "telegram", "discord")
accountId?: string, // Provider account ID
conversationId?: string, // Chat/conversation ID
messageId?: string, // Message ID returned by the provider
isGroup?: boolean, // Whether this outbound message belongs to a group/channel context
groupId?: string, // Group/channel identifier for correlation with message:received
}
// message:transcribed context
{
from?: string, // Sender identifier
to?: string, // Recipient identifier
body?: string, // Raw inbound body before enrichment
bodyForAgent?: string, // Enriched body visible to the agent
transcript: string, // Audio transcript text
timestamp?: number, // Unix timestamp when received
channelId: string, // Channel (e.g., "telegram", "whatsapp")
conversationId?: string,
messageId?: string,
senderId?: string, // Sender user ID
senderName?: string, // Sender display name
senderUsername?: string,
provider?: string, // Provider name
surface?: string, // Surface name
mediaPath?: string, // Path to the media file that was transcribed
mediaType?: string, // MIME type of the media
}
// message:preprocessed context
{
from?: string, // Sender identifier
to?: string, // Recipient identifier
body?: string, // Raw inbound body
bodyForAgent?: string, // Final enriched body after media/link understanding
transcript?: string, // Transcript when audio was present
timestamp?: number, // Unix timestamp when received
channelId: string, // Channel (e.g., "telegram", "whatsapp")
conversationId?: string,
messageId?: string,
senderId?: string, // Sender user ID
senderName?: string, // Sender display name
senderUsername?: string,
provider?: string, // Provider name
surface?: string, // Surface name
mediaPath?: string, // Path to the media file
mediaType?: string, // MIME type of the media
isGroup?: boolean,
groupId?: string,
}
const isMessageReceivedEvent = (event: { type: string; action: string }) =>
event.type === "message" && event.action === "received";
const isMessageSentEvent = (event: { type: string; action: string }) =>
event.type === "message" && event.action === "sent";
const handler = async (event) => {
if (isMessageReceivedEvent(event as { type: string; action: string })) {
console.log(`[message-logger] Received from ${event.context.from}: ${event.context.content}`);
} else if (isMessageSentEvent(event as { type: string; action: string })) {
console.log(`[message-logger] Sent to ${event.context.to}: ${event.context.content}`);
}
};
export default handler;

這些 hook 不是事件流監聽器;它們讓插件在 OpenClaw 持久化工具結果之前,同步調整這些結果。

  • tool_result_persist: 在工具結果寫入會話記錄之前對其進行轉換。必須是同步的;返回更新後的工具結果負載,或返回 undefined 以保持原樣。參見 Agent Loop。

透過插件 hook 執行器公開的壓縮生命週期 hook:

  • before_compaction: 在壓縮前執行,帶有計數/token 元數據
  • after_compaction: 在壓縮後執行,帶有壓縮總結元數據

計劃中的事件類型:

  • session:start: 當新會話開始時
  • session:end: 當會話結束時
  • agent:error: 當代理遇到錯誤時
  • Workspace hooks (<workspace>/hooks/): 針對單一代理;可以新增 hook 名稱,但不能覆蓋同名的內建、託管或插件 hook。
  • 託管 Hook (Managed hooks) (~/.openclaw/hooks/): 跨 workspace 共用;可以覆蓋內建和插件 hook。
Terminal window
mkdir -p ~/.openclaw/hooks/my-hook
cd ~/.openclaw/hooks/my-hook
---
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`.
const handler = async (event) => {
if (event.type !== "command" || event.action !== "new") {
return;
}
console.log("[my-hook] Running!");
// Your logic here
};
export default handler;
Terminal window
# Verify hook is discovered
openclaw hooks list
# Enable it
openclaw hooks enable my-hook
# Restart your gateway process (menu bar app restart on macOS, or restart your dev process)
# Trigger the event
# Send /new via your messaging channel
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"session-memory": { "enabled": true },
"command-logger": { "enabled": false }
}
}
}
}

Hook 可以有自定義配置:

{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"my-hook": {
"enabled": true,
"env": {
"MY_CUSTOM_VAR": "value"
}
}
}
}
}
}

從額外目錄載入 Hook(會被視為受管理的 Hook,具有相同的覆蓋優先級):

{
"hooks": {
"internal": {
"enabled": true,
"load": {
"extraDirs": ["/path/to/more/hooks"]
}
}
}
}

舊的配置格式為了向後相容仍然有效:

{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts",
"export": "default"
}
]
}
}
}

注意:module 必須是相對於 workspace 的路徑。絕對路徑或超出 workspace 範圍的訪問都會被拒絕。

遷移:針對新的 Hook 請使用新的自動偵測系統。舊版 handler 會在基於目錄的 Hook 之後載入。

Terminal window
# List all hooks
openclaw hooks list
# Show only eligible hooks
openclaw hooks list --eligible
# Verbose output (show missing requirements)
openclaw hooks list --verbose
# JSON output
openclaw hooks list --json
Terminal window
# Show detailed info about a hook
openclaw hooks info session-memory
# JSON output
openclaw hooks info session-memory --json
Terminal window
# Show eligibility summary
openclaw hooks check
# JSON output
openclaw hooks check --json
Terminal window
# Enable a hook
openclaw hooks enable session-memory
# Disable a hook
openclaw hooks disable command-logger

內建 Hook 參考 (Bundled hook reference)

Section titled “內建 Hook 參考 (Bundled hook reference)”

當你執行 /new 或 /reset 時,將會話上下文儲存到記憶體中。

Events: command:new, command:reset

Requirements: 必須配置 workspace.dir

Output: <workspace>/memory/YYYY-MM-DD-slug.md(預設為 ~/.openclaw/workspace)

運作原理:

  1. 使用重置前的會話條目來定位正確的對話紀錄
  2. 從對話中提取最後 15 條使用者/助手訊息(可配置)
  3. 使用 LLM 生成具描述性的檔案名稱 slug
  4. 將會話 metadata 儲存到帶有日期的記憶檔案中

輸出範例:

# Session: 2026-01-16 14:30:00 UTC
- **Session Key**: agent:main:main
- **Session ID**: abc123def456
- **Source**: telegram
## Conversation Summary
user: Can you help me design the API?
assistant: Sure! Let's start with the endpoints...

檔案名稱範例:

  • 2026-01-16-vendor-pitch.md
  • 2026-01-16-api-design.md
  • 2026-01-16-1430.md(如果 slug 生成失敗,則回退到時間戳記)

啟用:

Terminal window
openclaw hooks enable session-memory

在 agent:bootstrap 期間注入額外的引導檔案(例如 monorepo 本地的 AGENTS.md / TOOLS.md)。

Events: agent:bootstrap

Requirements: 必須配置 workspace.dir

Output: 不會寫入檔案;引導上下文僅在記憶體中修改。

Config:

{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"bootstrap-extra-files": {
"enabled": true,
"paths": ["packages/*/AGENTS.md", "packages/*/TOOLS.md"]
}
}
}
}
}

配置選項:

  • paths (string[]): 從 workspace 解析的 glob/路徑模式。
  • patterns (string[]): paths 的別名。
  • files (string[]): paths 的別名。

備註:

  • 路徑是相對於 workspace 解析的。
  • 檔案必須留在 workspace 內(會經過 realpath 檢查)。
  • 僅載入可識別的引導基準名稱(AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, USER.md, HEARTBEAT.md, BOOTSTRAP.md, MEMORY.md, memory.md)。
  • 對於 subagent/cron 會話,適用更嚴格的白名單(AGENTS.md, TOOLS.md, SOUL.md, IDENTITY.md, USER.md)。

啟用:

Terminal window
openclaw hooks enable bootstrap-extra-files

將所有命令事件記錄到集中式的稽核檔案中。

Events: command

Requirements: 無

Output: ~/.openclaw/logs/commands.log

運作原理:

  1. 擷取事件詳情(命令動作、時間戳記、session key、發送者 ID、source)
  2. 以 JSONL 格式附加到日誌檔案
  3. 在背景靜默執行

日誌條目範例:

{"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"}

查看日誌:

Terminal window
# View recent commands
tail -n 20 ~/.openclaw/logs/commands.log
# Pretty-print with jq
cat ~/.openclaw/logs/commands.log | jq .
# Filter by action
grep '"action":"new"' ~/.openclaw/logs/commands.log | jq .

啟用:

Terminal window
openclaw hooks enable command-logger

當 Gateway 啟動時(在 channel 啟動後)執行 BOOT.md。 必須啟用內部 Hook 才能執行此功能。

Events: gateway:startup

Requirements: 必須配置 workspace.dir

運作原理:

  1. 從你的 workspace 讀取 BOOT.md
  2. 透過 agent 執行器執行指令
  3. 透過訊息工具發送任何要求的對外訊息

啟用:

Terminal window
openclaw hooks enable boot-md

Hook 在命令處理期間執行。請保持它們輕量化:

// ✓ Good - async work, returns immediately
const handler: HookHandler = async (event) => {
void processInBackground(event); // Fire and forget
};
// ✗ Bad - blocks command processing
const handler: HookHandler = async (event) => {
await slowDatabaseQuery(event);
await evenSlowerAPICall(event);
};

務必封裝具風險的操作:

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
}
};

如果事件不相關,請儘早返回:

const handler: HookHandler = async (event) => {
// Only handle 'new' commands
if (event.type !== "command" || event.action !== "new") {
return;
}
// Your logic here
};

盡可能在 metadata 中指定確切的事件:

metadata: { "openclaw": { "events": ["command:new"] } } # Specific

而非:

metadata: { "openclaw": { "events": ["command"] } } # General - more overhead

Gateway 在啟動時會記錄 Hook 的載入狀況:

Registered hook: session-memory -> command:new
Registered hook: bootstrap-extra-files -> agent:bootstrap
Registered hook: command-logger -> command
Registered hook: boot-md -> gateway:startup

列出所有被偵測到的 Hook:

Terminal window
openclaw hooks list --verbose

在你的 handler 裡加入日誌,確認它真的有被呼叫:

const handler: HookHandler = async (event) => {
console.log("[my-handler] Triggered:", event.type, event.action);
// Your logic
};

如果 Hook 沒有生效,可以用這個指令檢查原因:

Terminal window
openclaw hooks info my-hook

在輸出結果中找看看是否有缺少的必要條件。

監控 Gateway 日誌來觀察 Hook 的執行過程:

Terminal window
# macOS
./scripts/clawlog.sh -f
# Other platforms
tail -f ~/.openclaw/gateway.log

你也可以單獨測試你的 handler:

import { test } from "vitest";
import myHandler from "./hooks/my-hook/handler.js";
test("my handler works", async () => {
const event = {
type: "command",
action: "new",
sessionKey: "test-session",
timestamp: new Date(),
messages: [],
context: { foo: "bar" },
};
await myHandler(event);
// Assert side effects
});
  • src/hooks/types.ts: Type definitions(類型定義)
  • src/hooks/workspace.ts: 目錄掃描與載入
  • src/hooks/frontmatter.ts: HOOK.md metadata 解析
  • src/hooks/config.ts: 資格檢查
  • src/hooks/hooks-status.ts: 狀態報告
  • src/hooks/loader.ts: Dynamic module loader(動態模組載入器)
  • src/cli/hooks-cli.ts: CLI 指令
  • src/gateway/server-startup.ts: 在 Gateway 啟動時載入 Hook
  • src/auto-reply/reply/commands-core.ts: 觸發指令事件
Gateway startup
↓
Scan directories (bundled → plugin → managed + extra dirs → workspace)
↓
Parse HOOK.md files
↓
Sort by override precedence (bundled < plugin < managed < workspace)
↓
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
  1. 檢查目錄結構:

    Terminal window
    ls -la ~/.openclaw/hooks/my-hook/
    # Should show: HOOK.md, handler.ts
  2. 驗證 HOOK.md 格式:

    Terminal window
    cat ~/.openclaw/hooks/my-hook/HOOK.md
    # Should have YAML frontmatter with name and metadata
  3. 列出所有探索到的 Hook:

    Terminal window
    openclaw hooks list

檢查需求設定:

Terminal window
openclaw hooks info my-hook

看看是不是少了這些:

  • Binaries(檢查 PATH)
  • Environment variables
  • Config values
  • OS compatibility
  1. 驗證 Hook 是否已啟用:

    Terminal window
    openclaw hooks list
    # Should show ✓ next to enabled hooks
  2. 重啟你的 Gateway 程序,讓 Hook 重新載入。

  3. 檢查 Gateway 日誌是否有錯誤:

    Terminal window
    ./scripts/clawlog.sh | grep hook

檢查 TypeScript 或匯入錯誤:

Terminal window
# Test import directly
node -e "import('./path/to/handler.ts').then(console.log)"

如果你在設定過程中遇到任何問題,可以詢問 AI Setup Assistant 獲取即時幫助。

從舊版配置遷移到自動偵測 (Discovery)

Section titled “從舊版配置遷移到自動偵測 (Discovery)”

遷移前:

{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts"
}
]
}
}
}

遷移後:

  1. 建立 hook 目錄:

    Terminal window
    mkdir -p ~/.openclaw/hooks/my-hook
    mv ./hooks/handlers/my-handler.ts ~/.openclaw/hooks/my-hook/handler.ts
  2. 建立 HOOK.md:

    ---
    name: my-hook
    description: "My custom hook"
    metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } }
    ---
    # My Hook
    Does something useful.
  3. 更新配置:

    {
    "hooks": {
    "internal": {
    "enabled": true,
    "entries": {
    "my-hook": { "enabled": true }
    }
    }
    }
    }
  4. 驗證並重啟你的 Gateway 程序:

    Terminal window
    openclaw hooks list
    # Should show: 🎯 my-hook ✓

遷移的好處:

  • 自動偵測
  • CLI 管理
  • 資格檢查
  • 更好的文件說明
  • 結構一致
OpenClaw

OpenClaw Expert

還是卡住了?

如果這篇文件沒有解決你的情境,直接問 OpenClaw Expert,拿到可執行步驟。