第 03 期 | Hooks 深度解析 — Caveman 的自动激活引擎

⏱ 预计阅读 17 分钟 更新于 2026/5/7
💡 进群学习加 wx: agentupdate
(申请发送: agentupdate)

🎯 学习目标

学完本期你将掌握:

  1. Claude Code Hooks 的四个生命周期事件
  2. Caveman 三个 Hook 文件各自的职责与触发时机
  3. Flag File (~/.claude/.caveman-active) 的桥接原理
  4. 如何自定义和扩展 Hook 配置
  5. 三大平台的 Hook 机制对比

📖 核心概念:Claude Code Hooks 机制

3.1 什么是 Hook?

Hook (钩子) 是 Claude Code 提供的 确定性回调机制。与 CLAUDE.md 中的指令不同(那只是"建议"),Hook 是 保证执行 的 Shell 命令或脚本。

graph LR
    subgraph Comparison["指令 vs Hook"]
        direction TB
        A["📝 CLAUDE.md 指令
──────────────
• 建议性的
• Agent 可能忽略
• 无法保证执行"] B["⚡ Hook 脚本
──────────────
• 确定性的
• 100% 会执行
• 独立于 Agent 的意志"] end A -.->|"可能被忽略"| C["Agent 行为"] B -->|"必定执行"| C

3.2 四个生命周期事件

Claude Code 的 Hook 系统提供了四个挂载点:

sequenceDiagram
    participant U as 用户
    participant CC as Claude Code
    participant H as Hook 系统

    Note over CC: 🟢 SessionStart
    CC->>H: 触发 SessionStart hooks
    H-->>CC: 注入启动上下文
    
    loop 每次用户输入
        U->>CC: 输入 prompt
        Note over CC: 📝 UserPromptSubmit
        CC->>H: 触发 UserPromptSubmit hooks
        
        loop 每次工具调用
            Note over CC: 🔧 PreToolUse
            CC->>H: 触发 PreToolUse hooks
            CC->>CC: 执行工具 (Read/Write/Bash...)
            Note over CC: ✅ PostToolUse
            CC->>H: 触发 PostToolUse hooks
        end
        
        CC->>U: 返回回答
    end
事件 触发时机 典型用途
SessionStart 会话启动时 (仅一次) 注入全局规则、初始化状态
UserPromptSubmit 每次用户发送消息 检测命令、记录日志
PreToolUse 工具调用前 安全检查、权限拦截
PostToolUse 工具调用后 日志记录、自动格式化

📖 Caveman 的三个 Hook 文件

3.3 caveman-activate.js — SessionStart Hook

这是 Caveman 的"启动引擎"。每次 Claude Code 会话启动时自动执行。

职责:

  1. 写入 full 到 Flag File ~/.claude/.caveman-active
  2. 将 Caveman 压缩规则作为隐藏上下文注入 (stdout → 系统上下文)
  3. 检测是否有自定义 statusLine,若无则发出配置建议

工作原理简述:

// caveman-activate.js 核心逻辑 (简化版)

const fs = require('fs');
const path = require('path');

// 1. 写入 flag file,标记 Caveman 已激活
const flagPath = path.join(process.env.HOME, '.claude', '.caveman-active');
fs.writeFileSync(flagPath, 'full');

// 2. 输出规则到 stdout (Claude Code 会将其作为隐藏系统上下文)
console.log(`
Terse like caveman. Technical substance exact. Only fluff die.
Drop: articles, filler (just/really/basically), pleasantries, hedging.
Fragments OK. Short synonyms. Code unchanged.
Pattern: [thing] [action] [reason]. [next step].
ACTIVE EVERY RESPONSE. No revert after many turns.
`);

// 3. 检查 statusLine 配置
const settingsPath = path.join(process.env.HOME, '.claude', 'settings.json');
if (fs.existsSync(settingsPath)) {
    const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
    if (!settings.statusLine) {
        console.log('[CAVEMAN] No statusLine configured. Say "setup caveman badge" to add it.');
    }
}

💡 关键细节: SessionStart Hook 的 stdout 输出会被注入为隐藏系统上下文——Claude 能看到它,但用户看不到。这是 Caveman 能"无声激活"的关键。

3.4 caveman-mode-tracker.js — UserPromptSubmit Hook

这是 Caveman 的"模式跟踪器"。每次用户发送消息时都会执行。

职责:

  • 检测用户消息是否包含 /caveman 命令
  • 解析命令参数,将新模式写入 Flag File

支持的模式:

/caveman          → 写入 "full"
/caveman lite     → 写入 "lite"
/caveman ultra    → 写入 "ultra"
/caveman wenyan   → 写入 "wenyan"
/caveman wenyan-lite  → 写入 "wenyan-lite"
/caveman wenyan-ultra → 写入 "wenyan-ultra"
/caveman-commit   → 写入 "commit"
/caveman-review   → 写入 "review"
/caveman:compress → 写入 "compress"

核心逻辑:

// caveman-mode-tracker.js 核心逻辑 (简化版)

const fs = require('fs');
const path = require('path');

// 从 stdin 读取用户的消息
const userPrompt = process.argv[2] || '';
const flagPath = path.join(process.env.HOME, '.claude', '.caveman-active');

// 检测 /caveman 命令并更新 flag file
const match = userPrompt.match(/^\/(caveman(?:-\w+)?)\s*(\w*)/);
if (match) {
    const command = match[1];   // "caveman" or "caveman-commit" etc.
    const level = match[2];     // "lite", "ultra", "wenyan", etc.
    
    let mode = 'full';
    if (command === 'caveman-commit') mode = 'commit';
    else if (command === 'caveman-review') mode = 'review';
    else if (command === 'caveman:compress') mode = 'compress';
    else if (level) mode = level;
    
    fs.writeFileSync(flagPath, mode);
}

3.5 caveman-statusline.sh — 状态栏脚本

这是 Caveman 的"仪表盘"。它不是 Hook,而是一个被 Claude Code 的 statusLine 配置调用的独立脚本。

职责:

  • 读取 Flag File 中的当前模式
  • 输出带有 ANSI 颜色的徽章文本
#!/bin/bash
# caveman-statusline.sh

caveman_flag="$HOME/.claude/.caveman-active"

if [ -f "$caveman_flag" ]; then
    mode=$(cat "$caveman_flag" 2>/dev/null)
    if [ "$mode" = "full" ] || [ -z "$mode" ]; then
        # 橙色 [CAVEMAN] 徽章
        echo -e '\033[38;5;172m[CAVEMAN]\033[0m'
    else
        suffix=$(echo "$mode" | tr '[:lower:]' '[:upper:]')
        # 橙色 [CAVEMAN:ULTRA] 等徽章
        echo -e '\033[38;5;172m[CAVEMAN:'"${suffix}"']\033[0m'
    fi
fi

徽章示例:

命令 状态栏显示
/caveman [CAVEMAN]
/caveman ultra [CAVEMAN:ULTRA]
/caveman wenyan [CAVEMAN:WENYAN]
/caveman-commit [CAVEMAN:COMMIT]
/caveman-review [CAVEMAN:REVIEW]

📖 Flag File 桥接机制

三个 Hook 组件通过一个简单的文本文件协调状态:

graph LR
    A["SessionStart Hook
caveman-activate.js"] -->|"写入 'full'"| F["~/.claude/.caveman-active"] B["UserPromptSubmit Hook
caveman-mode-tracker.js"] -->|"写入新模式"| F F -->|"读取模式"| C["Statusline Script
caveman-statusline.sh"] C --> D["显示在状态栏
[CAVEMAN:ULTRA]"] style F fill:#FFD700,stroke:#B8860B,color:#000

这是一个经典的 文件即协议 设计模式:

  • 没有进程间通信 (IPC)
  • 没有 Socket 或 HTTP
  • 只有一个纯文本文件
  • 简单、可靠、零依赖

📖 手动配置 settings.json

如果你需要手动配置 Hook(而不是通过 Plugin 自动安装),需要编辑 ~/.claude/settings.json

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "",
        "command": "node /path/to/caveman-activate.js"
      }
    ],
    "UserPromptSubmit": [
      {
        "matcher": "",
        "command": "node /path/to/caveman-mode-tracker.js \"$PROMPT\""
      }
    ]
  },
  "statusLine": {
    "type": "command",
    "command": "bash /path/to/caveman-statusline.sh"
  }
}

⚠️ 将 /path/to/ 替换为实际路径。Plugin 安装时会自动处理路径。

如果你已有自定义 statusLine 脚本,将以下代码合并到你现有的脚本中:

# 在你的自定义 statusline 脚本中添加
caveman_text=""
caveman_flag="$HOME/.claude/.caveman-active"
if [ -f "$caveman_flag" ]; then
  caveman_mode=$(cat "$caveman_flag" 2>/dev/null)
  if [ "$caveman_mode" = "full" ] || [ -z "$caveman_mode" ]; then
    caveman_text=$'\033[38;5;172m[CAVEMAN]\033[0m'
  else
    caveman_suffix=$(echo "$caveman_mode" | tr '[:lower:]' '[:upper:]')
    caveman_text=$'\033[38;5;172m[CAVEMAN:'"${caveman_suffix}"$']\033[0m'
  fi
fi

# 将 $caveman_text 追加到你的状态栏输出中
echo "$your_existing_status $caveman_text"

📊 三大平台 Hook 能力对比

Hook 能力 Claude Code Antigravity Gemini CLI
SessionStart ✅ 原生支持 ❌ 无 Hook 系统 ⚠️ 通过 GEMINI.md 模拟
UserPromptSubmit ✅ 原生支持 ❌ 不支持 ❌ 不支持
PreToolUse ✅ 原生支持 ❌ 不支持 ❌ 不支持
PostToolUse ✅ 原生支持 ❌ 不支持 ❌ 不支持
Flag File 状态追踪 ✅ 完整支持 ❌ 无需 (无 Hook) ❌ 无需
Statusline 徽章 ✅ 原生支持 ❌ 不支持 ❌ 不支持
自动激活方式 Hook → stdout 注入 GEMINI.md 规则文件 GEMINI.md 上下文文件
模式切换方式 Hook 拦截 /caveman 自然语言 /caveman 命令

💡 结论: Claude Code 拥有最完善的 Hook 生态系统,这也是 Caveman 最初为它而设计的原因。Antigravity 和 Gemini CLI 虽无 Hook,但通过规则文件和上下文注入也能实现核心功能。


📝 本期要点回顾

  1. Hook ≠ 建议:Hook 是确定性执行的脚本,CLAUDE.md 是建议性指令
  2. Caveman 使用三个 Hook 协作:activate(启动注入) + mode-tracker(模式切换) + statusline(状态显示)
  3. Flag File (~/.claude/.caveman-active) 是三者间的桥梁,存储当前模式
  4. SessionStart Hook 的 stdout 会作为隐藏系统上下文注入——用户看不到但 Claude 能看到
  5. Antigravity 和 Gemini CLI 无 Hook 系统,通过规则文件实现等效功能

🔗 参考资料