@hsingjui/pi-hooks
Claude Code-compatible command hooks for the Pi coding agent
Package details
Install @hsingjui/pi-hooks from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@hsingjui/pi-hooks- Package
@hsingjui/pi-hooks- Version
0.0.1- Published
- Apr 2, 2026
- Downloads
- 119/mo · 18/wk
- Author
- hsingjui
- License
- MIT
- Types
- extension
- Size
- 61.8 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./src/pi-hooks.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-hooks
将 Claude Code 的 command hooks 配置格式适配到 pi 的扩展事件系统。
当前支持范围
- 仅支持
type: "command" - 支持 hook handler 的
if字段(仅工具事件生效) - 支持事件:
SessionStartSessionEndPreCompactPostCompactPreToolUsePostToolUsePostToolUseFailureUserPromptSubmitStop
- 不支持
http/prompt/agent
映射关系
SessionStart.startup→resources_discover(reason="startup")SessionStart.startup→session_switch(reason="new")SessionStart.resume→session_switch(reason="resume")SessionStart.compact→session_compactSessionEnd.other→session_shutdownStop→agent_end(best-effort,对齐 Claude Code 的“完成响应后触发”)
配置格式
在 ~/.pi/agent/settings.json 或 .pi/settings.json 中配置:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "echo 'Session started'"
}
]
},
{
"matcher": "resume",
"hooks": [
{
"type": "command",
"command": "echo 'Session resumed'"
}
]
},
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Context compacted'"
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "echo 'Session ended on shutdown/exit'"
}
]
}
]
}
}
matcher 配置
对齐 Claude Code:matcher 是单个正则字符串。
- 省略
matcher表示匹配全部 ""表示匹配全部"*"表示匹配全部- 其他值按正则表达式处理
- 正则无效时,退化为普通字符串精确匹配
示例:
{
"hooks": {
"PreToolUse": [
{
"matcher": "bash",
"hooks": [
{
"type": "command",
"command": "echo 'bash only'"
}
]
},
{
"matcher": "write|edit",
"hooks": [
{
"type": "command",
"command": "echo 'write or edit'"
}
]
}
]
}
}
各事件的 matcher 匹配字段:
SessionStart
匹配 source:
startupresumecompact
SessionEnd
匹配 reason:
other
PreToolUse / PostToolUse / PostToolUseFailure
匹配 tool_name。
注意:这里直接使用 pi 里的原始工具名,因此通常是小写,例如:
bashreadwriteeditgrepfindlsmcp__.*
说明:
SessionEnd由session_shutdown触发- 当
matcher省略时,默认按other处理 UserPromptSubmit/Stop不支持matcher,配置了也会被忽略
if 条件
对齐 Claude Code 的思路,if 配在单个 hook handler 上,只在工具事件中生效:
PreToolUsePostToolUsePostToolUseFailure
其他事件如果配置了 if,该 hook 不会运行。
当前支持的形式:
Bash(git *)bash(git *)Edit(*.ts)Write(*.md)mcp__memory__create_entities(*)
规则:
if语法为ToolName(pattern)ToolName按工具名比较,大小写不敏感pattern使用简单通配符匹配,*表示任意字符串bash主要匹配tool_input.commandread/write/edit主要匹配tool_input.path(或file_path)- 其他工具优先匹配常见主字段,匹配不到时退化为
tool_input的 JSON 字符串
示例:只拦截 git push
{
"hooks": {
"PreToolUse": [
{
"matcher": "bash",
"hooks": [
{
"type": "command",
"if": "Bash(git push*)",
"command": "printf '%s\n' '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"git push is blocked\"}}'"
}
]
}
]
}
}
Hook 输入
默认输入字段尽量对齐 Claude Code hooks:
- 通用字段:
session_id、transcript_path、cwd、hook_event_name - 事件字段:如
source、reason、tool_name、tool_input、tool_response - 允许多出 pi 特有字段,但不会破坏 Claude Code 风格脚本的读取方式
SessionStart
{
"session_id": "session-file-path",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/current/working/directory",
"hook_event_name": "SessionStart",
"source": "startup"
}
SessionEnd
{
"session_id": "session-file-path",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/current/working/directory",
"hook_event_name": "SessionEnd",
"reason": "other"
}
UserPromptSubmit
{
"session_id": "session-file-path",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/current/working/directory",
"hook_event_name": "UserPromptSubmit",
"prompt": "Write a function to calculate the factorial of a number"
}
说明:
UserPromptSubmit不支持matcher,即使配置也会被忽略- 会在用户提交输入后、agent loop 开始前触发
Stop
对应 pi 的 agent_end 事件。
{
"session_id": "session-file-path",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/current/working/directory",
"hook_event_name": "Stop",
"stop_hook_active": false,
"last_assistant_message": "I have completed the task."
}
说明:
Stop在本轮 agent 处理完成后触发- 不支持
matcher,即使配置也会被忽略 stop_hook_active用于标识当前继续执行是否由上一次Stophook 触发last_assistant_message会尽量提取最后一条 assistant 文本内容;若没有文本则为空字符串decision: "block"时,会以隐藏上下文 + 追加一轮 agent 的方式 best-effort 模拟 Claude Code 的“阻止停止并继续”语义
PreToolUse
对应 pi 的 tool_call 事件。
{
"session_id": "session-file-path",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/current/working/directory",
"hook_event_name": "PreToolUse",
"tool_name": "bash",
"tool_input": {
"command": "ls -la"
},
"tool_use_id": "toolu_123"
}
说明:
PreToolUse在工具真正执行前触发- 映射到 pi 的
tool_call,而不是tool_execution_start - 支持
matcher,并按tool_name做正则匹配 - 不包含
permission_mode tool_name直接使用 pi 事件里的原始值,不做大小写转换tool_input对应tool_call的event.inputtool_use_id对应tool_call的event.toolCallId
PostToolUse
对应 pi 的 tool_result 事件,仅在工具成功完成时触发。
{
"session_id": "session-file-path",
"transcript_path": "/path/to/session.jsonl",
"cwd": "/current/working/directory",
"hook_event_name": "PostToolUse",
"tool_name": "bash",
"tool_input": {
"command": "pwd"
},
"tool_response": {
"content": [
{
"type": "text",
"text": "/tmp/project"
}
],
"details": {},
"is_error": false,
"output": "/tmp/project"
},
"tool_use_id": "toolu_123"
}
说明:
PostToolUse在工具成功执行后触发- 映射到 pi 的
tool_result - 不包含
permission_mode tool_name直接使用 pi 事件里的原始值,不做大小写转换tool_input对应tool_result的event.inputtool_response对应当前工具结果的 Claude Code 风格兼容对象tool_use_id对应tool_result的event.toolCallId- 失败结果不会进入
PostToolUse,而是进入PostToolUseFailure
Hook 输出
UserPromptSubmit:阻止提示词或附加上下文
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "My additional context here"
}
}
说明:
decision对UserPromptSubmit只有一个有效值:"block"- 省略
decision表示允许继续 - 其他值会被忽略
reason仅展示给用户,不会加入上下文additionalContext会作为隐藏上下文注入当前轮
Stop:阻止停止并继续一轮
{
"decision": "block",
"reason": "Run a final self-check before stopping",
"hookSpecificOutput": {
"hookEventName": "Stop",
"additionalContext": "Verify there are no missing tests."
}
}
说明:
decision对Stop只有一个有效值:"block"- 省略
decision表示允许结束 reason会作为继续执行时注入给后续 agent turn 的隐藏上下文additionalContext会在decision: "block"时与reason一起注入下一轮;若未继续,则不会额外保留到后续用户输入stop_hook_active会在由Stophook 续跑出来的后续Stop事件里变为true,用于避免无限循环- 当前实现基于 pi 的
agent_end+sendMessage(..., { triggerTurn: true }),属于 best-effort 对齐
PreToolUse:阻止或改写参数
可用输出字段:
permissionDecision:"allow" | "deny" | "ask"permissionDecisionReason: 展示给用户/调用方的原因updatedInput: 用于改写工具入参additionalContext: 追加给后续处理的上下文
示例:阻止执行
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Dangerous command blocked"
}
}
示例:允许并改写参数
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "My reason here",
"updatedInput": {
"field_to_modify": "new value"
},
"additionalContext": "Current environment: production. Proceed with caution."
}
}
说明:
permissionDecision: "deny"会阻止当前工具调用,并把permissionDecisionReason作为阻止原因返回给 agent;不会直接停止整个当前处理流程permissionDecision: "allow"会放行;若带updatedInput,则在执行前改写参数permissionDecision: "ask"当前仅保留兼容字段语义,本扩展里不会额外弹出权限确认 UIupdatedInput会合并到当前event.input上,未提供的字段保持原值additionalContext不会阻止执行,只作为附加上下文使用;会注入隐藏上下文,但默认不会额外显示一条 UI 提示- 若需要显式停止整个当前处理流程,请使用 Claude Code 通用字段
continue: false
PostToolUse:附加上下文或 patch 结果
Claude Code 风格输出示例:
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for Claude"
}
}
或:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Command succeeded"
}
}
pi 扩展层也支持直接 patch 工具结果:
{
"systemMessage": "Hook patched tool result",
"content": [
{
"type": "text",
"text": "patched result"
}
],
"isError": false
}
也支持 Claude Code 通用输出字段:
{
"continue": false,
"stopReason": "Stop current processing",
"systemMessage": "Hook requested stop"
}
说明:
hookSpecificOutput.hookEventName会按 Claude Code 规则识别decision: "block"不会回滚已执行的工具;会把reason当作反馈附加给模型上下文additionalContext会通过隐藏上下文注入当前 agent 流程,尽量贴近 Claude Code 的“给 Claude 追加上下文”语义;默认不会额外显示一条 UI 提示systemMessage在工具相关事件(PreToolUse/PostToolUse/PostToolUseFailure)默认静默,不额外显示 UI 提示continue: false会在工具事件里以 best-effort 方式停止当前处理;这和PreToolUse.permissionDecision: "deny"不同,后者只阻止当前工具并把原因反馈给 agentPostToolUse/PostToolUseFailure的 stopProcessing 默认不会再额外补一条本地 warning,优先以 hook 自身返回的 message / result 为准- 除 Claude Code 兼容字段外,仍支持 pi 特有 patch:
- 顶层
content/details/isError hookSpecificOutput.updatedToolResultupdatedMCPToolOutput(用于 MCP 工具输出替换)hookSpecificOutput.updatedMCPToolOutput
- 顶层
使用方法
本地开发
pi
然后执行:
/pi-hooks
/pi-hooks-reset
作为 npm 包使用
pi install npm:pi-hooks
目录结构
源码位于 src/:
src/pi-hooks.ts- 扩展实现src/config.ts- 配置加载与合并src/executor.ts- command hook 执行器src/hooks/shared.ts- hook 共享解析与执行辅助src/hooks/session-hooks.ts- SessionStart / SessionEndsrc/hooks/compact-hooks.ts- PreCompact / PostCompactsrc/hooks/prompt-hooks.ts- UserPromptSubmitsrc/hooks/tool-hooks.ts- PreToolUse / PostToolUse / PostToolUseFailuresrc/hooks/stop-hooks.ts- Stopsrc/types.ts- 类型定义
说明
- hook 命令会在当前会话
cwd中执行 - 全局配置与项目配置会按事件数组拼接合并
PostToolUse/PostToolUseFailure现在支持返回 pi 的结果 patch- 输入 / 输出尽量兼容 Claude Code hooks;无法原样映射的部分按 pi 事件能力 best-effort 处理