Claude Code、Cursor、Codex,這些 AI coding agent,相信你已經耳熟能詳,甚至每天都在用了。

那你聽過 agent hooks 嗎?

本文將介紹我目前唯二使用的 agent hooks,並說明它們為何重要。

涵蓋工具包括 GitHub Copilot、Claude Code、Codex 以及 OpenCode CLI。

不過,僅限於 macOS,其餘平台,就靠大家努力了☺️


什麼是 Agent Hooks

引領風騷的 Claude Code 最早發明了 agent hooks 這個概念。因為太過實用,後來其它工具也競相模仿。

簡單說,agent hooks 是這些 AI coding agent 提供的事件機制:當 agent 完成任務、需要確認,或發生特定事件時,自動執行你指定的腳本或指令

如果你用過 Git hooks,比如最常見的 pre-commit hooks——在 commit 前跑腳本,agent hooks 就是同樣的概念:事件發生,執行特定指令

只是觸發點從 Git 操作變成 agent 行為

為什麼不能只靠指令遵循

我們固然可以在 prompt 裡寫「產完程式碼要記得跑 lint」之類的要求,但 AI 不一定每次都會照做,尤其當上下文愈來愈長、指示愈來愈複雜時。

至於「做完記得通知我」就更不用說了,AI 根本沒這能力——它沒辦法「主動」播放音效或彈系統通知。

而 hooks 保證了確定性——事件發生,腳本執行,不依靠 AI 的自主判斷。甚至做到了 AI 本來不能做的事,比如播放音效🔔

我只設兩種 hook

Agent hooks 能做的事很多,基本上能變成指令、腳本的內容都行,比如自動跑 lint、formatter、把結果寫進檔案等等。

但這些並非本文重點——我自己只設兩種 hook:完成通知權限請求通知,都是為了在 agent 停下來時即時提醒我。


為什麼「通知」是最重要的 Hook

沒有通知,AI 幹活時你只有兩種選擇:在前景盯著它,或不停切回來確認。顯然兩種都不太有效率😅

有通知,我們才好真正「非同步」使用它:把任務交給 agent,去做別的事,聽到聲音再回來。

至於 lint、formatter,另有替代方案——pre-commit 這類工具、CI/CD 都能做。但完成通知只能透過 agent hooks,沒有其他機制能在 agent 停下來的那一刻通知你。

相關文章:Python 開發:pre-commit 設定 Git Hooks 教學

所以,其他 hooks 可以不設,但這個必須有


通用性的偏好

我總共幫四個 AI coding agent 設定過 hooks:GitHub Copilot、OpenAI Codex app、Claude Code、OpenCode CLI。

至於 Google 的 Antigravity 2.0 app,因為內建了通知機制,就不必設定了。

我只寫全域 hooks,不寫 project-level。這些設定放在 home 目錄,透過 yadm 在不同機器之間同步。

相關文章:yadm 教學:實作 macOS 與 Linux 的 dotfiles 跨平台同步

我的 agent skills 也是採取同樣策略——幾乎都是全域的,很少寫 project-level。關於「通用 vs 專用」,之前文章有討論過。


接下來直接看設定,腳本也一併附上。

GitHub Copilot:直接共用 Claude Code 設定

GitHub Copilot 能讀取 Claude Code 的設定檔中,有關 hooks 的設定,所以可以直接共用,不用額外寫。

不過目前 Copilot 支援的事件種類沒有 Claude Code 多——Stop 事件有支援;不支援的事件會跳過,不會出錯。

考慮到我目前已經暫時棄用 GitHub Copilot 了,這部分請容我略過😆

如有需求,可參考官方文件

Claude Code

設定位置:~/.claude/settings.json,內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "bash ~/scripts/mac/notify-agent-stop.sh"
}
]
}
],
"PermissionRequest": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "bash ~/scripts/mac/notify-agent-permission-request.sh"
}
]
}
]
}

Stop完成通知:agent 停下來了。

PermissionRequest權限請求通知:當 agent 執行到需要額外權限時,需要你確認才能繼續。這個很有用,因為有時候你離開太久,回來發現 agent 早就在等你,白白浪費了時間。

上面的 command 的 path 請改成你放置腳本的地方,還有檔名。

OpenAI Codex app

設定位置:~/.codex/config.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[hooks]

[[hooks.Stop]]
matcher = ""

[[hooks.Stop.hooks]]
type = "command"
command = "/bin/bash /Users/kyo/scripts/mac/notify-agent-stop.sh"
statusMessage = "Sending agent stop notification"

[[hooks.PermissionRequest]]
matcher = ""

[[hooks.PermissionRequest.hooks]]
type = "command"
command = "/bin/bash /Users/kyo/scripts/mac/notify-agent-permission-request.sh"
statusMessage = "Sending agent permission request notification"

Codex app 採用 TOML 格式,和 Claude Code 的 JSON 語法不同,但結構類似。

第一次設定,重啟 Codex app 時,會需要你確認 hooks 的內容與安全性。

OpenCode CLI

OpenCode 比較特別:不能在設定檔裡寫 hooks,必須用 plugin。

plugin/ 目錄要放在 opencode 設定檔(opencode.json)所在的目錄底下。

我的設定位置:~/.config/opencode/plugin/notify.js,如下(內容是我用 AI Vibe 的,你可以自行調整):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export const NotifyPlugin = async ({ $ }) => {
return {
event: async ({ event }) => {
if (process.platform !== "darwin") return

if (event.type === "session.idle") {
await $`bash ~/scripts/mac/notify-agent-stop.sh`
}

if (event.type === "permission.asked") {
await $`bash ~/scripts/mac/notify-agent-permission-request.sh`
}
}
}
}

另一個要注意的是,event type 是 session.idle,不是 Stop。不過意思差不多啦!

透過 Attention 設定

其實,OpenCode CLI 還可以透過 Attention 功能——需要另外寫 tui.json,方便你設定通知,不一定要靠 plugin。

但我不確定它是否適用於「非互動模式」(即 opencode run),而且我感覺這做法的通用性,還不如直接寫 plugin。畢竟腳本可以共用。


共用的通知腳本

上述三套工具的 hooks 都呼叫同一組 shell 腳本,程式碼如下:

notify-agent-stop.sh

1
2
3
4
5
6
7
#!/bin/bash

# 播放 Glass 音效
afplay /System/Library/Sounds/Glass.aiff &

# 顯示 Notification Center 通知
osascript -e 'display notification "Agent 回合已停止" with title "Agent Stop"' &

afplay 是 macOS 內建的指令,而音效名稱 Glass 也是內建的。

可以到系統的「設定 > 聲音 > 提示聲」去聆聽每一種內建音效,換成自己喜歡的。

也可以用 afplay 播放任何路徑下的音效檔(如 mp3、wav),只要檔案存在,指令就能正常運作。

osascript 則能在通知中心發出文字提醒。這兩個指令搭配起來,就能在任務完成時,同時播放音效和彈出通知,讓你知道 agent 已經處理完畢。

覺得這樣有點吵的話,可以二選一就好。

notify-agent-permission-request.sh

1
2
3
4
5
6
7
#!/bin/bash

# 播放 Funk 音效
afplay /System/Library/Sounds/Funk.aiff &

# 顯示 Notification Center 通知
osascript -e 'display notification "Agent 需要權限確認" with title "Agent Permission Request"' &

我覺得 Funk 音效很適合當「確認」使用XD


工具會換,需求不會

儘管不同的工具有不同的設定,但「通知」這個需求是一樣的。

所以我在每個工具中都實作了它,讓核心行為保持一致

如此一來,你想換工具或退掉其中一部分時,體驗上不會受到太大影響。你只需要維護一組通知腳本,所有 agent 都能共用。

畢竟,工具只是工具,不要依戀它