# cc-hook 🤗 Claude Code 钩子服务,将 Claude Code 事件转发至 Gotify 推送通知。 ## 功能特性 - **实时通知**:当 Claude Code 需要关注时,立即收到手机推送 - **多事件支持**:Notification(权限请求、等待输入等)、Stop(任务完成)、PreToolUse(提问通知) - **轻量部署**:单个 Docker 容器,资源占用极低 - **灵活配置**:通过环境变量自定义 Gotify 服务器和 Token ## 架构概览 ```mermaid graph LR A[Claude Code] -->|POST /hooks| B[cc-hook] B -->|POST /message| C[Gotify] C -->|推送通知| D[手机/客户端] ``` ## 前置条件 - Docker 和 Docker Compose - Claude Code 已安装 - Gotify 服务器(自建或公共) ## 快速开始 ### 1. 克隆项目 ```bash git clone cc-hook cd cc-hook ``` ### 2. 配置环境变量 复制 `.env.example` 并填写实际配置: ```bash cp .env.example .env ``` 编辑 `.env`: ```env GOTIFY_URL=http://your-gotify-server:port # Gotify 服务器地址 GOTIFY_TOKEN=your-token-here # Gotify 应用 Token PORT=:8082 # 服务监听端口(默认 8082) ``` ### 3. 启动服务 ```bash docker-compose up -d ``` ### 4. 配置 Claude Code 将以下内容添加到 `~/.claude/settings.json`: ```json { "hooks": { "Notification": [ { "matcher": "", "hooks": [ { "type": "http", "url": "http://47.121.181.112:8082/hooks" } ] } ], "Stop": [ { "hooks": [ { "type": "http", "url": "http://47.121.181.112:8082/hooks" } ] } ], "PreToolUse": [ { "matcher": "AskUserQuestion", "hooks": [ { "type": "http", "url": "http://47.121.181.112:8082/hooks" } ] } ], "UserPromptSubmit": [ { "hooks": [ { "type": "http", "url": "http://47.121.181.112:8082/hooks" } ] } ] } } ``` ## 支持的事件 ### Notification 事件 | Matcher | 标题 | 优先级 | 说明 | |---------|------|--------|------| | `permission_prompt` | 需要权限 | 7 | Claude 需要你批准一个操作 | | `idle_prompt` | 等待输入 | 5 | Claude 完成工作,等待下一步指令 | | `auth_success` | 认证成功 | 3 | 身份验证完成 | | 其他 | 通知 | 5 | 默认通知 | ### Stop 事件 | 条件 | 标题 | 优先级 | 说明 | |------|------|--------|------| | 正常完成 | 任务完成 | 5 | Claude 完成了本轮回复 | | 循环检测 | 循环停止 | 8 | Stop hook 连续触发多次,已自动停止 | ### PreToolUse 事件 | 工具名称 | 标题 | 优先级 | 说明 | |----------|------|--------|------| | `AskUserQuestion` | 等待回答 | 6 | Claude 向你提问,等待回答 | | 其他工具 | 工具调用 | 4 | Claude 正在使用工具 | ### UserPromptSubmit 事件 | 行为 | 说明 | |------|------| | 记录 Prompt | 将用户提交的 prompt 存入 MySQL 数据库,便于复盘提示词质量 | > ⚠️ 需要配置 `MYSQL_DSN` 环境变量才会启用数据库记录,否则仅打印日志。 #### 数据库表结构 ```sql CREATE TABLE prompts ( id BIGINT AUTO_INCREMENT PRIMARY KEY, session_id VARCHAR(128) NOT NULL, -- Claude Code 会话 ID project_name VARCHAR(255) NOT NULL, -- 项目名称(从 cwd 自动提取) prompt TEXT NOT NULL, -- 用户提交的 prompt created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_session_id (session_id), INDEX idx_project_name (project_name), INDEX idx_created_at (created_at) ); ``` - `session_id`:用于关联同一会话的多条 prompt - `project_name`:从工作目录自动提取(如 `/home/user/my-app` → `my-app`),便于按项目筛选 ## 配置说明 | 环境变量 | 必填 | 说明 | |----------|------|------| | `GOTIFY_URL` | 是 | Gotify 服务器地址 | | `GOTIFY_TOKEN` | 是 | Gotify 应用 Token | | `PORT` | 否 | 服务监听端口(默认 `:8082`) | | `MYSQL_DSN` | 否 | MySQL 连接串,留空则不记录 prompt(格式: `user:password@tcp(host:port)/dbname?parseTime=true`) | ## API 端点 ### POST /hooks 接收 Claude Code Hook 事件并转发至 Gotify。 **请求体示例:** ```json { "session_id": "abc123", "cwd": "/home/user/project", "hook_event_name": "Notification", "matcher": "permission_prompt" } ``` **响应:** - 成功:`{"status":"ok"}` - 忽略:`{"status":"ignored"}` - 错误:HTTP 500 ### GET /health 健康检查端点。 **响应:** ```json {"status":"ok"} ``` ## 本地开发 ### 直接运行 ```bash # 安装依赖 go mod tidy # 复制并编辑配置 cp .env.example .env # 运行(自动加载 .env) go run . ``` ### 测试 ```bash # 健康检查 curl http://47.121.181.112:8082/health # 模拟 Notification 事件 curl -X POST http://47.121.181.112:8082/hooks \ -H 'Content-Type: application/json' \ -d '{"session_id":"test","cwd":"/tmp","hook_event_name":"Notification","matcher":"permission_prompt"}' # 模拟 Stop 事件 curl -X POST http://47.121.181.112:8082/hooks \ -H 'Content-Type: application/json' \ -d '{"session_id":"test","cwd":"/tmp","hook_event_name":"Stop"}' # 模拟 AskUserQuestion 事件 curl -X POST http://47.121.181.112:8082/hooks \ -H 'Content-Type: application/json' \ -d '{"session_id":"test","cwd":"/tmp","hook_event_name":"PreToolUse","tool_name":"AskUserQuestion","tool_input":{"question":"你想使用哪种数据库?"}}' # 模拟 UserPromptSubmit 事件(记录 prompt 到 MySQL) curl -X POST http://47.121.181.112:8082/hooks \ -H 'Content-Type: application/json' \ -d '{"session_id":"test","cwd":"/tmp","hook_event_name":"UserPromptSubmit","tool_input":{"prompt":"帮我写一个 Hello World 程序"}}' ``` ## 项目结构 ``` cc-hook/ ├── .env.example # 环境变量示例 ├── .gitignore # Git 忽略规则 ├── config.go # 配置管理(环境变量读取) ├── db.go # MySQL 连接与 prompt 存储 ├── gotify.go # Gotify HTTP 客户端 ├── handler.go # Hook 事件处理器 ├── main.go # HTTP 服务入口 ├── Dockerfile # 多阶段 Docker 构建 ├── docker-compose.yml # Docker Compose 部署配置 └── go.mod # Go 模块定义 ``` ## 扩展计划 - [x] 数据库记录:将 UserPromptSubmit 的 prompt 写入 MySQL,便于复盘提示词质量 - [x] 更多事件支持:PreToolUse(AskUserQuestion 提问通知) ## License MIT