x

Shell Hooks详解

Shell Hooks 允许用纯 Shell 脚本挂载 Hermes Agent 的生命周期钩子,无需写 Python 插件。脚本通过 subprocess 调用,JSON 作为 stdin/stdout 的通信协议。

支持的事件

事件 时机 能否阻止/注入
pre_tool_call 工具执行前 ✅ 可 block
post_tool_call 工具执行后 ❌ 仅观察
pre_llm_call LLM 调用前 ✅ 可注入 context
post_llm_call LLM 调用后 ❌ 仅观察
subagent_stop 子 Agent 结束时 ❌ 仅观察
on_session_start 会话开始时 ❌ 仅观察
on_session_end 会话结束时 ❌ 仅观察

通信协议

Hermes                              Shell 脚本
                                       
     ─── stdin: JSON payload ───────► 
     ◄──── stdout: JSON response ──── 

stdin 格式

{
  "hook_event_name": "pre_tool_call",
  "tool_name": "terminal",
  "tool_input": {"command": "rm -rf /"},
  "session_id": "sess_abc123",
  "cwd": "/home/user/project",
  "extra": {"task_id": "...", "tool_call_id": "..."}
}

stdout 响应

// 阻止工具
{"action": "block", "message": "禁止执行 rm -rf"}

// 注入 context
{"context": "当前 git 状态: Untracked files..."}

// 不拦截
{}

配置方式

~/.hermes/config.yaml 中注册:

hooks:
  pre_tool_call:
    - matcher: "terminal"
      command: "~/.hermes/agent-hooks/block-rm-rf.sh"
      timeout: 5
  post_tool_call:
    - matcher: "write_file|patch"
      command: "~/.hermes/agent-hooks/auto-format.sh"
  pre_llm_call:
    - command: "~/.hermes/agent-hooks/inject-context.sh"
  subagent_stop:
    - command: "~/.hermes/agent-hooks/log-subagent.sh"

真实案例

案例1:阻止危险的 rm -rf 命令

hooks:
  pre_tool_call:
    - matcher: "terminal"
      command: "~/.hermes/agent-hooks/block-rm-rf.sh"
      timeout: 5
#!/usr/bin/env bash
# ~/.hermes/agent-hooks/block-rm-rf.sh

payload="$(cat -)"
cmd=$(echo "$payload" | jq -r '.tool_input.command // empty')

if echo "$cmd" | grep -qE 'rm[[:space:]]+-rf?[[:space:]]+(/|/home|/root|/www)'; then
  printf '{"action": "block", "message": "危险操作:禁止对系统目录执行 rm -rf"}\n'
else
  printf '{}\n'
fi

案例2:Agent 写完 Python 文件后自动格式化

hooks:
  post_tool_call:
    - matcher: "write_file|patch"
      command: "~/.hermes/agent-hooks/auto-format.sh"
#!/usr/bin/env bash
# ~/.hermes/agent-hooks/auto-format.sh

payload="$(cat -)"
path=$(echo "$payload" | jq -r '.tool_input.path // empty')

if [[ "$path" == *.py ]]; then
  if command -v black &>/dev/null; then
    black "$path" 2>/dev/null
  elif command -v yapf &>/dev/null; then
    yapf -i "$path" 2>/dev/null
  fi
fi

printf '{}\n'

案例3:每次 LLM 调用前自动注入 git status

hooks:
  pre_llm_call:
    - command: "~/.hermes/agent-hooks/git-status.sh"
#!/usr/bin/env bash
# ~/.hermes/agent-hooks/git-status.sh

cat - >/dev/null

if status=$(git status --porcelain 2>/dev/null) && [[ -n "$status" ]]; then
  jq --null-input --arg s "$status" \
     '{context: ("📋 当前目录有未提交变更:\n" + $s)}'
else
  printf '{}\n'
fi

案例4:记录每个子 Agent 完成日志

hooks:
  subagent_stop:
    - command: "~/.hermes/agent-hooks/log-subagent.sh"
#!/usr/bin/env bash
# ~/.hermes/agent-hooks/log-subagent.sh

log=~/.hermes/logs/subagent.log

jq -c '{
  ts: now | strftime("%Y-%m-%d %H:%M:%S"),
  session_id: .session_id,
  event: .hook_event_name,
  duration_ms: .extra.duration_ms,
  exit_code: .extra.exit_code,
  goal: .extra.goal[:50]
}' < /dev/stdin >> "$log"

printf '{}\n'

案例5:禁止对项目目录外的文件进行 write_file

#!/usr/bin/env bash
# ~/.hermes/agent-hooks/restrict-write.sh

ALLOWED_DIRS=("/www/wwwroot" "/home/project" "/root/reports")

payload="$(cat -)"
path=$(echo "$payload" | jq -r '.tool_input.path // empty')
tool=$(echo "$payload" | jq -r '.tool_name')

if [[ "$tool" == "write_file" ]]; then
  allowed=false
  for dir in "${ALLOWED_DIRS[@]}"; do
    if [[ "$path" == "$dir"* ]]; then
      allowed=true
      break
    fi
  done
  if [[ "$allowed" == false ]]; then
    printf '{"action": "block", "message": "禁止写入非授权目录: %s"}\n' "$path"
  else
    printf '{}\n'
  fi
else
  printf '{}\n'
fi

CLI 运维命令

hermes hooks list      # 列出所有已注册的 hook
hermes hooks test pre_tool_call --for-tool terminal  # 测试 hook
hermes hooks doctor    # 检查 hook 健康状态
hermes hooks revoke "~/.hermes/agent-hooks/block-rm-rf.sh"  # 撤销授权

与 Python 插件 Hook 的区别

Shell Hooks Python 插件 Hook
语言 任意(Bash/Python/Go...) Python
配置位置 config.yamlhooks: ~/.hermes/plugins/<name>/
进程隔离 ✅ 独立 subprocess ❌ 同进程
能 block 工具
能注入 LLM context
适合场景 轻量拦截/日志/格式化 复杂状态/指标/中间件

核心优势:Shell Hooks 让运维人员无需写 Python 代码,直接写 Bash 脚本即可实现安全拦截、自动化格式化、日志记录等需求。

Left-click: follow link, Right-click: select node, Scroll: zoom
x