Hook 系统与会话生命周期管理
Hook 系统与会话生命周期管理
学习目标
- 理解 SessionStart 钩子链在 agent-skills 中的实现
- 掌握 SDD 缓存管理的前置/后置钩子模式
- 理解 Code-Simplify 忽略规则引擎的设计
- 学会应用 hook 的 fail-open 原则
前置知识
本章涉及 Hook 事件驱动安全机制的通用原理,建议先阅读:
下文直接聚焦 agent-skills 的具体 Hook 实现。
项目实践
SessionStart 链式执行
{ "hooks": { "SessionStart": [ { "hooks": [ { "type": "command", "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh" } ] } ] }}hooks.json 定义了单一的 SessionStart 钩子链。使用 ${CLAUDE_PLUGIN_ROOT} 变量确保钩子脚本路径在插件安装时自动解析。
session-start.sh 的功能:
- 检测项目类型和技术栈
- 注入项目级上下文
- 加载可用技能列表
SDD 缓存管理(前置 + 后置钩子)
agent-skills 实现了成对钩子模式来管理 Spec-Driven Development 的缓存:
| 钩子 | 触发时机 | 功能 |
|---|---|---|
sdd-cache-pre.sh | 会话启动时 | 检查 SDD 缓存是否存在、是否过期 |
sdd-cache-post.sh | 规格编写完成后 | 将规格写入缓存,供后续会话复用 |
缓存生命周期:
Session Start ↓sdd-cache-pre.sh 检查缓存 ├── 命中 → 注入缓存内容到上下文 └── 未命中 → 空状态,需要重新编写规格 ↓ 规格编写完成 ↓sdd-cache-post.sh 写入缓存缓存键设计:基于项目路径和规格内容的 hash。这使得同一项目的不同规格版本不会互相覆盖,同时允许跨会话复用规格。
Code-Simplify 忽略规则引擎
simplify-ignore.sh(12KB+)是一个复杂的忽略规则引擎,决定哪些文件/变更不应该被 code-simplify 处理:
# 伪代码:simplify-ignore.sh 的核心逻辑set -e
# 1. 检查文件路径模式if echo "$file" | grep -qE "^(node_modules|vendor|\.git|dist)/"; then echo '{"ignore": true, "reason": "generated/vendor directory"}' exit 0fi
# 2. 检查变更类型if [ "$change_type" = "added" ] && [ "$file_size" -lt 100 ]; then echo '{"ignore": true, "reason": "small new file, no simplification needed"}' exit 0fi
# 3. 检查文件大小if [ "$file_size" -gt 50000 ]; then echo '{"ignore": true, "reason": "file too large for simplification"}' exit 0fi
# 4. 检查项目级自定义规则if [ -f ".simplify-ignore" ]; then # 读取项目级规则,追加到默认规则 ...fi
# 默认:不忽略echo '{"ignore": false}'配套的 simplify-ignore-test.sh 验证规则的正确性,确保新规则不意外忽略应处理的文件。
Fail-Open 原则
agent-skills 的所有钩子遵循 fail-open 原则:钩子失败不应阻断会话。具体实现:
#!/bin/bashset +e # 不因为错误退出
# 钩子逻辑result=$(do_something) || { echo "Hook failed, proceeding with default behavior" >&2 echo '{"status": "ok", "hook_failed": true}' exit 0 # 即使失败也返回成功状态}为什么 fail-open:钩子是优化,不是安全门控。如果钩子失败阻断会话,用户体验会极差。fail-open 保证生产力不被阻断。
问题与规避
| 陷阱 | 表现 | 对策 |
|---|---|---|
| 钩子脚本超时 | 会话启动延迟过长 | 设置合理的超时限制(通常 30 秒) |
| 缓存过期但不刷新 | Agent 使用过时规格 | 基于内容 hash 的缓存键,规格变更自动失效 |
| 忽略规则过宽 | 应该简化的文件被跳过 | 定期运行测试脚本验证规则 |
| 忽略规则过窄 | 不必要的文件被处理 | 基于文件路径模式精确匹配 |
设计取舍
为什么用成对钩子(pre/post)而非单一状态管理?
成对钩子的优势是无状态——不需要在会话之间维护状态文件。pre 钩子检查缓存状态,post 钩子写入缓存状态。如果 post 钩子失败,下一次 pre 钩子会检测到缓存缺失并触发重新生成。这与需要维护状态文件的方案相比,更容错。
为什么 simplify-ignore.sh 是 Bash 而非 Python?
Bash 脚本可以直接在 hook 命令中执行,不需要额外的依赖安装。虽然 Python 更强大,但要求每个用户安装 Python 环境和依赖会增加安装门槛。12KB 的 Bash 脚本虽然不如 Python 优雅,但确保了最大兼容性。
参考来源
- 源码验证:
hooks/hooks.json— 钩子配置 - 源码验证:
hooks/session-start.sh— 会话启动钩子 - 源码验证:
hooks/sdd-cache-pre.sh/hooks/sdd-cache-post.sh— SDD 缓存管理 - 源码验证:
hooks/simplify-ignore.sh— Code-Simplify 忽略规则引擎 - 源码验证:
hooks/SDD-CACHE.md/hooks/SIMPLIFY-IGNORE.md— 钩子行为文档