Claude-Mem 如何使用 Hooks:一种由生命周期驱动的架构¶
核心原则¶
从外部观察主 Claude Code 会话,在后台处理观察结果,并在合适的时机注入上下文。
全局概览¶
Claude-Mem 本质上是一个由 hook 驱动的系统。每一项功能都是在对生命周期事件作出响应时发生的:
┌─────────────────────────────────────────────────────────┐
│ CLAUDE CODE SESSION │
│ (Main session - user interacting with Claude) │
│ │
│ SessionStart → UserPromptSubmit → Tool Use → Stop │
│ ↓ ↓ ↓ ↓ ↓ ↓ │
│ [3 Hooks] [Hook] [Hook] [Hook] │
└─────────────────────────────────────────────────────────┘
↓ ↓ ↓ ↓ ↓ ↓
┌─────────────────────────────────────────────────────────┐
│ CLAUDE-MEM SYSTEM │
│ │
│ Smart Worker Context New Obs │
│ Install Start Inject Session Capture │
└─────────────────────────────────────────────────────────┘
Note
自 Claude Code 2.1.0(ultrathink 更新)起,SessionStart hooks 不再显示用户可见的消息。上下文会通过 hookSpecificOutput.additionalContext 静默注入。
关键洞察: Claude-Mem 不会中断或修改 Claude Code 的行为。它从外部观察,并通过生命周期 hooks 提供价值。
为什么使用 Hooks?¶
非侵入性要求¶
Claude-Mem 面临几个架构上的约束:
- 无法修改 Claude Code:它是一个闭源二进制程序
- 必须快速:不能拖慢主会话
- 必须可靠:即使它失败也不能破坏 Claude Code
- 必须可移植:无需配置即可在任何项目上工作
解决方案: 通过 settings.json 配置的外部命令 hooks
Hook 系统的优势¶
Claude Code 的 hook 系统恰好提供了我们需要的功能:
生命周期事件
SessionStart、UserPromptSubmit、PreToolUse (Read)、PostToolUse、PostToolUseFailure、Stop、SessionEnd
非阻塞
Hooks 并行运行,不等待完成
上下文注入
SessionStart 和 UserPromptSubmit 可以添加上下文
工具观察
PostToolUse 可以看到所有工具的输入和输出
Hook 脚本¶
Claude-Mem 为 7 个生命周期事件(SessionStart、UserPromptSubmit、PreToolUse、PostToolUse、PostToolUseFailure、Stop、SessionEnd)注册 hooks,外加一个 Setup hook。npx claude-mem install(以及 npx claude-mem repair)负责设置运行时;Setup hook 会运行 version-check.js,它完成一次依赖缺失的插件市场安装,并记录安装标记。SessionStart 在启动、恢复、清除和压缩时异步运行 worker-service start。同步上下文 hook 在启动、清除和压缩时运行,并在需要时自行启动 worker。继续和恢复会还原现有对话,而不打印或注入新的时间线,从而保留其 prompt cache。第三个仅限启动的 SessionStart 条目使 hook 启动器保持在 PATH 上。
每个 Hook 的启动方式¶
每个运行时 hook 都是一条简短的 exec 形式命令:command + args,没有 shell 键。Claude Code 在 PATH 上解析 claude-mem 并直接生成该进程,不经过 bash、没有 shell 字符串,也没有每次调用时的 shell 启动。在 Windows 上,这也意味着不需要 Git Bash,也不会出现由 bash → node 链路产生的控制台窗口。
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|clear|compact",
"hooks": [
{ "type": "command", "command": "claude-mem", "args": ["start"], "timeout": 60, "async": true }
]
},
{
"matcher": "startup|clear|compact",
"hooks": [
{ "type": "command", "command": "claude-mem", "args": ["hook", "claude-code", "context"], "timeout": 60 }
]
},
{
"matcher": "startup",
"hooks": [
{ "type": "command", "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/ensure-launcher.cjs"], "timeout": 180 }
]
}
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{ "type": "command", "command": "claude-mem", "args": ["hook", "claude-code", "observation"], "timeout": 120, "async": true }
]
}
]
}
}
其他事件采用相同的形式(session-init、file-context、用于 PostToolUseFailure 的 observation、summarize、session-end)。完整文件请参阅 plugin/hooks/hooks.json;它由 scripts/build-hooks.js 生成,构建过程会拒绝任何带有 shell 键或 command 不是 claude-mem 的运行时条目。
claude-mem 启动器:
- 一个由
plugin/scripts/claude-mem-launcher.cjs通过bun build --compile编译而成的小型二进制程序,由npx claude-mem install/update放置在 macOS 和 Linux 的~/.local/bin/claude-mem,或 Windows 的%LOCALAPPDATA%\claude-mem\bin\claude-mem.exe,并添加到 PATH(POSIX 为 shell rc 文件,Windows 为用户 Path)。 - 它的唯一职责:找到已安装的插件根目录(
CLAUDE_PLUGIN_ROOT,然后是最新的插件缓存版本,再然后是插件市场副本),在 Bun 下以相同的参数运行worker-service.cjs,透传 hook 的 stdin,并在任何失败时以退出码 0 退出,同时在 stderr 上输出一行claude-mem: <reason>, continuing without memory。它永远不会以退出码 2 退出,因此永远不会阻塞 prompt 或工具。 - 它是版本稳定的:它不携带任何 claude-mem 逻辑,因此插件市场自动更新可以继续与已位于 PATH 上的二进制程序配合工作。
claude-mem --version会打印其协议编号。 - 它不是 npm 包中的
claude-memCLI。PATH 上更靠前的全局 npmclaude-mem会遮蔽它;npx claude-mem doctor会报告该情况。
仅插件市场安装的自愈。 插件市场安装或更新不会运行任何安装程序代码,而且 Claude Code 只会将插件的 bin/ 放到 Bash 工具的 PATH 上,而不会放到 hook 的 PATH 上。仅限启动的 SessionStart 条目 node ${CLAUDE_PLUGIN_ROOT}/scripts/ensure-launcher.cjs 会在启动器缺失或版本旧于插件所需时编译并放置启动器。它是同步的,并且总是以退出码 0 退出。当启动器是最新版本时,它不会打印任何内容;当记忆 hooks 无法工作时,它会在 stdout 上打印一行 {"systemMessage": "..."},Claude Code 会将其显示给用户(见下文)。它与其它 SessionStart hooks 一起运行,因此如果启动器缺失,该首个会话中的其它 hooks 会以 Executable not found in $PATH: "claude-mem" 失败,并且该会话可能会缺少记忆上下文。
当启动器缺失时。 Claude Code 会在每次钩子调用时将缺失的钩子可执行文件报告为非阻塞错误(Executable not found in $PATH: "claude-mem"),而会话会继续运行,但不会进行记忆。当无法激活启动器时,会话启动会显示:
claude-mem: memory hooks are not active: <reason>. Fix: run `npx claude-mem install`, then restart Claude Code.
Setup 钩子:版本检查¶
目的: 确保已加载的插件能够启动其 worker:安装插件市场遗漏的运行时依赖项,并记录 .install-version 标记。
注意: npx claude-mem install 和 npx claude-mem repair 会预先安装 Bun、uv 和插件依赖项。插件市场(包括 claude plugin update)只会解压文件。
触发时机: 仅在 Claude Code 以 claude --init、claude --init-only 或 claude -p --maintenance 启动时触发。Claude Code 在插件安装、插件更新或普通会话启动时不会触发 Setup,因此对于市场安装来说,这是一条手动修复路径,而非自动路径。
它执行的操作:
1. 检查插件的 package.json 中声明的每个依赖项(以及 worker 加载的 zod 子路径)是否存在于其 node_modules 中,而不仅仅是检查该文件夹是否存在。
2. 如果有任何依赖缺失,则在插件根目录中运行 bun install --production(最长 120 秒),然后再次检查。部分依赖树会被保留,绝不会删除。
3. 如果依赖项仍然缺失,则打印 claude-mem: plugin dependencies missing (…) - run: npx claude-mem@latest install,并指出缺失的模块(输出到 stderr;Codex 会将其作为 SessionStart 上下文接收)。
4. 如果依赖闭包完整,但 .install-version 缺失、无法读取或来自其他版本,则自行写入该标记({version, installedAt, bun}),而不是将可用的安装请求发送给安装器。
5. 始终以 0 退出——绝不阻塞会话。
配置(简化版;随附的配置项是唯一仍使用 "shell": "bash" 的钩子,它首先解析插件根目录和 Node):
{
"hooks": {
"Setup": [{
"matcher": "*",
"hooks": [{
"type": "command",
"shell": "bash",
"command": "node \"$_P/scripts/version-check.js\"",
"timeout": 300
}]
}]
}
}
主要特性:
- ✅ 安装完成时速度很快:仅检查已声明的依赖项,不进行安装
- ✅ 在 --init / --maintenance 时修复市场安装:安装缺失的依赖项并写入标记
- ✅ 当安装无法完成时,指出缺失的模块
- ✅ 始终以 0 退出——绝不阻塞会话
来源: plugin/scripts/version-check.js。npx claude-mem install / npx claude-mem repair 会提前完成相同的工作——它们在全局安装 Bun + uv,在插件缓存中运行 bun install,并写入 .install-version 标记——整个过程带有可见的 clack spinner。
Hook 1:SessionStart - 上下文注入¶
目的: 注入来自先前会话的相关上下文
触发时机: 当新的 Claude Code 会话启动时,或在 clear 或 compact 之后触发(与异步 worker-start SessionStart 条目并行运行)。在 continue 和 resume 时跳过。
它执行的操作: 1. 从当前工作目录提取项目名称 2. 查询 SQLite 以获取最近的会话摘要(最近 10 条) 3. 查询 SQLite 以获取最近的观测记录(可配置,默认 50 条) 4. 格式化为渐进式披露索引 5. 输出到 stdout(自动注入到上下文中)
关键决策:
- ✅ 在启动、clear 和 compact 时运行
- ✅ 300 秒超时(允许在需要时进行 npm install)
- ✅ 渐进式披露格式(索引,而非完整详情)
- ✅ 可通过 CLAUDE_MEM_CONTEXT_OBSERVATIONS 配置观测记录数量
Codex 压缩: Codex 的压缩会替换线程的历史记录,包括时间线。根线程从 SessionStart(来源 compact)取回时间线。Codex 从不向子代理线程发送 SessionStart,而且 PostCompact 输出无法携带上下文,因此 PostCompact 钩子会标记该线程(~/.claude-mem/state/codex-compaction-pending/),而该线程的下一次 PostToolUse 会一次性将时间线作为 additionalContext 返回。已经被 SessionStart 重新注入过时间线的线程不会再次收到时间线。
Codex 子代理: Codex 使用 SubagentStart 而不是 SessionStart 来启动它派生的子代理,因此 SubagentStart 钩子会为子代理提供时间线。分叉的子代理从父对话的副本开始,其中包含时间线,因此该钩子会跳过它:它会从子代理的 rollout(transcript_path)的第一行读取 forked_from_id。无法读取其 rollout 的子代理会获得时间线。
信任新的 Codex 钩子: Codex 只在你信任某个插件钩子后才会运行它,并且它会将新的或已更改的钩子视为不受信任。在更新添加或更改了 claude-mem 钩子之后,Codex 会在启动时提示钩子需要审查。请信任这些钩子,否则它们将不会运行。codex exec 无法提问,因此请先在交互式 Codex 中信任它们一次。
输出格式:
# [claude-mem] recent context
**Legend:** 🎯 session-request | 🔴 gotcha | 🟡 problem-solution ...
### Oct 26, 2025
**General**
| ID | Time | T | Title | Tokens |
|----|------|---|-------|--------|
| #2586 | 12:58 AM | 🔵 | Context hook file empty | ~51 |
*Use MCP search tools to access full details*
来源: src/cli/handlers/context.ts,以 claude-mem hook claude-code context 方式运行
Hook 2:UserPromptSubmit(新会话钩子)¶
目的: 当用户提交 Prompt 时初始化会话跟踪
触发时机: 在 Claude 处理用户消息之前
它执行的操作: 1. 从 stdin 读取用户 Prompt 和会话 ID 2. 在 SQLite 中创建新的会话记录 3. 保存原始用户 Prompt 以进行全文搜索(v4.2.0+) 4. 如果 Bun worker service 未在运行,则启动它 5. 立即返回(非阻塞)
配置:
{
"hooks": {
"UserPromptSubmit": [{
"hooks": [{
"type": "command",
"command": "claude-mem",
"args": ["hook", "claude-code", "session-init"],
"timeout": 15
}]
}]
}
}
关键决策:
- ✅ 无匹配器(对所有提示词运行)
- ✅ 立即创建会话记录
- ✅ 存储原始提示词以供搜索(隐私说明:仅本地 SQLite)
- ✅ 自动启动 Worker Service
- ✅ 抑制输出(suppressOutput: true)
数据库操作:
INSERT INTO sdk_sessions (claude_session_id, project, user_prompt, ...)
VALUES (?, ?, ?, ...)
INSERT INTO user_prompts (session_id, prompt, prompt_number, ...)
VALUES (?, ?, ?, ...)
来源: src/cli/handlers/session-init.ts,以 claude-mem hook claude-code session-init 运行
Hook 3:PostToolUse(保存观察 Hook)¶
目的: 捕获工具执行观察结果以供后续处理
触发时机: 任何工具成功完成后立即触发。失败调用(例如 Bash 以非零退出码退出)会改由 PostToolUseFailure 接收,该事件运行相同的处理器,并将 { error, is_interrupt } 记录为工具输出,因此记忆也能看到失败的尝试。
具体操作: 1. 从 stdin 接收工具名称、输入和输出 2. 查找当前项目的活动会话 3. 将观察结果入队到 observation_queue 表 4. 立即返回(处理在 worker 中完成)
配置:
{
"hooks": {
"PostToolUse": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "claude-mem",
"args": ["hook", "claude-code", "observation"],
"timeout": 120,
"async": true
}]
}]
}
}
关键决策:
- ✅ 匹配器:*(捕获所有工具)
- ✅ 非阻塞(仅入队,不处理)
- ✅ Worker 异步处理观察结果
- ✅ 并行执行安全(每个 hook 都有自己的 stdin)
数据库操作:
INSERT INTO observation_queue (session_id, tool_name, tool_input, tool_output, ...)
VALUES (?, ?, ?, ?, ...)
入队的内容:
{
"session_id": "abc123",
"tool_name": "Edit",
"tool_input": {
"file_path": "/path/to/file.ts",
"old_string": "...",
"new_string": "..."
},
"tool_output": {
"success": true,
"linesChanged": 5
},
"created_at_epoch": 1698765432
}
来源: src/cli/handlers/observation.ts,以 claude-mem hook claude-code observation 运行
Hook 4:Stop Hook(摘要生成)¶
目的: 在会话期间生成由 AI 驱动的会话摘要
触发时机: 当 Claude 停止时(由 Stop 生命周期事件触发)
具体操作:
1. 从数据库收集会话观察结果
2. 发送给 Claude Agent SDK 以生成摘要
3. 处理响应并提取结构化摘要
4. 存储在 session_summaries 表中
5. 将该轮次的交付物(例如幻灯片、报告、智能体为你制作的页面)复制到 ~/.claude-mem/deliverables/<project>/ 并记录它们,以便 My Work 在请求卡片上链接这些交付物
配置:
{
"hooks": {
"Stop": [{
"hooks": [{
"type": "command",
"command": "claude-mem",
"args": ["hook", "claude-code", "summarize"],
"timeout": 120,
"async": true
}]
}]
}
}
关键决策:
- ✅ 由 Stop 生命周期事件触发
- ✅ 每个会话可生成多个摘要(v4.2.0+)
- ✅ 摘要是检查点,而非终点
- ✅ 使用 Claude Agent SDK 进行 AI 压缩
- ✅ 交付物由 hook 自身从转录的最终回合中复制(即其 Write/Edit 调用在源目录树之外创建的文件,以及附加了 work_state_write 的文件),复制会在它们所在的 worktree 或临时文件夹被删除之前完成;worker 仅记录这些副本。CLAUDE_MEM_DELIVERABLES_ENABLED=false 可关闭此功能。
摘要结构:
<summary>
<request>User's original request</request>
<investigated>What was examined</investigated>
<learned>Key discoveries</learned>
<completed>Work finished</completed>
<next_steps>Remaining tasks</next_steps>
<files_read>
<file>path/to/file1.ts</file>
<file>path/to/file2.ts</file>
</files_read>
<files_modified>
<file>path/to/file3.ts</file>
</files_modified>
<notes>Additional context</notes>
</summary>
来源: src/cli/handlers/summarize.ts,以 claude-mem hook claude-code summarize 运行
Hook 5:SessionEnd(清理 Hook)¶
目的: 在会话结束时将会话标记为已完成
触发时机: Claude Code 会话结束时(不包括 /clear 命令)
具体操作: 1. 在数据库中将会话标记为已完成 2. 允许 worker 完成处理 3. 执行优雅清理
配置:
{
"hooks": {
"SessionEnd": [{
"hooks": [{
"type": "command",
"command": "claude-mem",
"args": ["hook", "claude-code", "session-end"],
"async": true
}]
}]
}
}
关键决策:
- ✅ 优雅完成(v4.1.0+)
- ✅ 不再向 workers 发送 DELETE
- ✅ 跳过 /clear 命令的清理
- ✅ 保留进行中的会话
为什么需要优雅清理?
旧方案(v3):
问题: - 中断的摘要生成 - 丢失待处理的观察结果 - 竞态条件
新方案(v4.1.0+):
// ✅ Graceful completion
SessionEnd → UPDATE sessions SET completed_at = NOW()
Worker sees completion → Finishes processing → Exits naturally
优点: - Worker 完成重要操作 - 摘要成功生成 - 干净的状态转换
来源: src/cli/handlers/session-end.ts,以 claude-mem hook claude-code session-end 运行
Hook 执行流程¶
会话生命周期¶
sequenceDiagram
participant User
participant Claude
participant Hooks
participant Worker
participant DB
User->>Claude: Start Claude Code
Claude->>Hooks: SessionStart hook
Hooks->>DB: Query recent context
DB-->>Hooks: Session summaries + observations
Hooks-->>Claude: Inject context
Note over Claude: Context available for session
User->>Claude: Submit prompt
Claude->>Hooks: UserPromptSubmit hook
Hooks->>DB: Create session record
Hooks->>Worker: Start worker (if not running)
Worker-->>DB: Ready to process
Claude->>Claude: Execute tools
Claude->>Hooks: PostToolUse (multiple times)
Hooks->>DB: Queue observations
Note over Worker: Polls queue, processes observations
Worker->>Worker: AI compression
Worker->>DB: Store compressed observations
Worker->>Hooks: Trigger summary hook
Hooks->>DB: Store session summary
User->>Claude: Finish
Claude->>Hooks: SessionEnd hook
Hooks->>DB: Mark session complete
Worker->>DB: Check completion
Worker->>Worker: Finish processing
Worker->>Worker: Exit gracefully
钩子时机¶
| 事件 | 时机 | 阻塞 | 超时 | 输出处理 |
|---|---|---|---|---|
| Setup (version-check) | 仅 claude --init / --maintenance |
否 | 300s | 安装缺失的插件依赖项;仅当依赖项仍然缺失时输出 stderr 提示(始终以退出码 0 结束) |
| SessionStart (worker-start) | 会话启动(异步) | 否 | 60s | stderr(仅日志) |
| SessionStart (context) | 会话开始前 | 否 | 60s | JSON → additionalContext(静默) |
| SessionStart (ensure-launcher) | 仅启动时(同步) | 否 | 180s | 成功时无输出;当钩子未激活时,输出一行 systemMessage JSON(向用户显示);stderr(仅调试日志) |
| UserPromptSubmit | 处理之前 | 否 | 15s(内部预算 10s;Windows 上 7s) | stdout → context |
| PostToolUse | 工具执行后 | 否 | 120s | 仅写入转录 |
| PostToolUseFailure | 失败的工具执行后 | 否 | 120s | 仅写入转录 |
| Summary | 由 Worker 触发 | 否 | 120s | 数据库 |
| SessionEnd | 退出时 | 否 | 120s | 仅日志 |
Note
自 Claude Code 2.1.0(ultrathink 更新)起,SessionStart 钩子不再显示用户可见的消息。上下文会通过 hookSpecificOutput.additionalContext 静默注入。
Worker Service 架构¶
为什么需要后台 Worker?¶
问题: 钩子必须快速(< 1 秒)
现实: AI 压缩每条观察记录需要 5-30 秒
方案: 钩子将观察记录入队,Worker 异步处理
┌─────────────────────────────────────────────────────────┐
│ HOOK (Fast) │
│ 1. Read stdin (< 1ms) │
│ 2. Insert into queue (< 10ms) │
│ 3. Return success (< 20ms total) │
└─────────────────────────────────────────────────────────┘
↓ (queue)
┌─────────────────────────────────────────────────────────┐
│ WORKER (Slow) │
│ 1. Poll queue every 1s │
│ 2. Process observation via Claude SDK (5-30s) │
│ 3. Parse and store results │
│ 4. Mark observation processed │
└─────────────────────────────────────────────────────────┘
Bun 进程管理¶
技术: Bun(JavaScript 运行时和进程管理器)
为什么选择 Bun: - 故障时自动重启 - 启动快且内存占用低 - 内置 TypeScript 支持 - 跨平台(支持 macOS、Linux、Windows) - 无需单独的进程管理器
Worker 生命周期:
# Started by hooks automatically (if not running)
npm run worker:start
# Status check
npm run worker:status
# View logs
npm run worker:logs
# Restart
npm run worker:restart
# Stop
npm run worker:stop
Worker HTTP API¶
技术: Worker 的每用户端口上运行的 Express.js REST API(默认 37700 + (uid % 100),可通过 CLAUDE_MEM_WORKER_PORT 覆盖)
端点:
| 端点 | 方法 | 用途 |
|---|---|---|
/health |
GET | 健康检查 |
/sessions |
POST | 创建会话 |
/sessions/:id |
GET | 获取会话状态 |
/sessions/:id |
PATCH | 更新会话 |
/observations |
POST | 将观察记录入队 |
/observations/:id |
GET | 获取观察记录 |
为什么使用 HTTP API? - 与语言无关(钩子可用任何语言编写) - 易于调试(curl 命令) - 标准错误处理 - 正确的异步处理
设计模式¶
模式 1:即发即弃(Fire-and-Forget)钩子¶
原则: 钩子应立即返回,而不是等待处理完成
// ❌ Bad: Hook waits for processing
export async function saveHook(stdin: HookInput) {
const observation = parseInput(stdin);
await processObservation(observation); // BLOCKS!
return success();
}
// ✅ Good: Hook enqueues and returns
export async function saveHook(stdin: HookInput) {
const observation = parseInput(stdin);
await enqueueObservation(observation); // Fast
return success(); // Immediate
}
模式 2:基于队列的处理¶
原则: 将捕获与处理解耦
优点: - 并行钩子执行安全 - Worker 故障不影响钩子 - 集中式重试逻辑 - 背压处理
模式 3:优雅降级¶
原则: 记忆系统故障不应导致 Claude Code 崩溃
try {
await captureObservation();
} catch (error) {
// Log error, but don't throw
console.error('Memory capture failed:', error);
return { continue: true, suppressOutput: true };
}
故障模式:
- 启动器找不到插件或 Bun → 输出一行 stderr,退出码 0,会话在无记忆情况下继续
- claude-mem 不在 PATH 中 → Claude Code 显示非阻塞钩子错误并继续
- 数据库被锁定 → 跳过观察记录,记录错误日志
- Worker 崩溃 → 通过 Bun 自动重启
- 网络问题 → 使用指数退避重试
- 磁盘已满 → 警告用户,禁用记忆
模式 4:渐进增强¶
原则: 核心功能在无记忆时正常工作,记忆则为其增强
Without memory: Claude Code works normally
With memory: Claude Code + context from past sessions
Memory broken: Falls back to working normally
钩子调试¶
调试模式¶
启用详细的钩子执行日志:
输出:
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: claude-mem hook claude-code observation with timeout 120000ms
[DEBUG] Hook command completed with status 0: {"continue":true,"suppressOutput":true}
常见问题¶
钩子未执行
症状: 钩子命令从未运行
调试步骤:
1. 检查 /hooks 菜单——钩子是否已注册?
2. 验证匹配模式(区分大小写!)
3. 检查启动器是否在 PATH 中:command -v claude-mem && claude-mem --version(输出协议号),或运行 npx claude-mem doctor
4. 在 claude --debug 输出中查找 Executable not found in $PATH: "claude-mem";参见 故障排除
钩子超时
症状: 钩子执行超过超时时间
调试: 1. 检查超时设置(默认 60 秒) 2. 找出慢操作(数据库?网络?) 3. 将慢操作移至 worker 4. 如有必要,增加超时时间
上下文未注入
症状: SessionStart 钩子已运行,但上下文缺失
调试: 1. 检查 stdout(必须是有效的 JSON 或纯文本) 2. 确认没有 stderr 输出(会污染 JSON) 3. 检查退出码(必须为 0) 4. 检查 npm install 输出(v4.3.1 修复)
观察记录未捕获
症状: PostToolUse 钩子已运行,但观察记录缺失
调试:
1. 检查数据库:sqlite3 ~/.claude-mem/claude-mem.db "SELECT * FROM observation_queue"
2. 确认会话存在:SELECT * FROM sdk_sessions
3. 检查 worker 状态:npm run worker:status
4. 查看 worker 日志:npm run worker:logs
手动测试钩子¶
# Test context hook
echo '{
"session_id": "test123",
"cwd": "/Users/alex/projects/my-app",
"hook_event_name": "SessionStart",
"source": "startup"
}' | claude-mem hook claude-code context
# Test save hook
echo '{
"session_id": "test123",
"tool_name": "Edit",
"tool_input": {"file_path": "test.ts"},
"tool_output": {"success": true}
}' | claude-mem hook claude-code observation
# Test with actual Claude Code
claude --debug
/hooks # View registered hooks
# Submit prompt and watch debug output
性能考量¶
钩子执行时间¶
目标: 每个钩子 < 100ms
实际测量结果:
| 钩子 | 平均 | p95 | p99 |
|---|---|---|---|
| Setup(版本检查、依赖完整、标记最新) | 8ms | 20ms | 40ms |
| SessionStart(上下文) | 45ms | 120ms | 250ms |
| SessionStart(用户消息) | 5ms | 10ms | 15ms |
| UserPromptSubmit | 12ms | 25ms | 50ms |
| PostToolUse | 8ms | 15ms | 30ms |
| SessionEnd | 5ms | 10ms | 20ms |
启动器开销: 编译后的 claude-mem 启动器大约在 10ms 内启动,运行时钩子不会派生 shell。
Setup 钩子何时快速,何时不快速:
- 依赖树完整时,它仅检查声明的模块能否解析,并在 .install-version 标记缺失或来自其他版本时重写该标记——不进行安装。
- 只有依赖树不完整时——例如插件市场安装或 claude plugin update 的依赖从未安装过——才会运行 bun install(最长 120 秒,处于 Setup 钩子的 300 秒超时范围内)。Setup 仅会在 claude --init / --maintenance 时触发,因此这绝不会落在正常会话或逐提示词路径上。
- npx claude-mem install / npx claude-mem repair 会事先完成繁重工作(Bun + uv,在插件缓存内运行 bun install),并配有可见的 clack 加载指示器,因此通过这种方式安装的插件在会话启动时永远不会付出这一代价。
数据库性能¶
模式优化:
- project、created_at_epoch、claude_session_id 上的索引
- 用于全文搜索的 FTS5 虚拟表
- 用于并发读写的 WAL 模式
查询模式:
-- Fast: Uses index on (project, created_at_epoch)
SELECT * FROM session_summaries
WHERE project = ?
ORDER BY created_at_epoch DESC
LIMIT 10
-- Fast: Uses index on claude_session_id
SELECT * FROM sdk_sessions
WHERE claude_session_id = ?
LIMIT 1
-- Fast: FTS5 full-text search
SELECT * FROM observations_fts
WHERE observations_fts MATCH ?
ORDER BY rank
LIMIT 20
Worker 吞吐量¶
瓶颈: Claude API 延迟(每条观察记录 5-30 秒)
缓解措施: - 顺序处理观察记录(更简单、更可预测) - 跳过低价值的观察记录(TodoWrite、ListMcpResourcesTool) - 批量生成摘要(每 N 条观察记录生成一次,而非每条都生成)
未来优化方向: - 并行处理(多个 worker) - 智能批处理(合并相关的观察记录) - 惰性摘要(仅在需要时生成摘要)
安全考量¶
钩子命令安全¶
风险: 钩子以用户权限执行任意命令
缓解措施:
1. 启动时冻结: 钩子配置在启动时捕获,更改需要审查
2. 需要用户审查: /hooks 菜单会显示更改,需要批准
3. 插件隔离: ${CLAUDE_PLUGIN_ROOT} 防止路径遍历
4. 输入验证: 钩子在处理前验证 stdin 模式
数据隐私¶
存储内容: - 用户提示(原始文本)- v4.2.0+ - 工具输入和输出 - 读取/修改的文件路径 - 会话摘要
隐私保障:
- 所有数据均本地存储在 ~/.claude-mem/claude-mem.db
- 无云端上传(API 调用仅用于 AI 压缩)。本地安装仅在注册时联系一次 cmem.ai 以创建登录链接,并附带仅含数字的使用摘要(观察记录数量和 token 总数,不含提示词、路径或项目名称);除此之外不会向 cmem.ai 发送任何内容。
- SQLite 文件权限:仅当前用户可读/写
- 产品遥测采用 opt-out 机制,且绝不携带提示词、代码或路径——参见 遥测
API 密钥保护¶
配置:
- Anthropic API 密钥位于 ~/.anthropic/api_key 或 ANTHROPIC_API_KEY 环境变量中
- Worker 从 Claude Code 继承环境
- 绝不会被记录或存储在数据库中
要点总结¶
- 钩子是接口:它们定义了系统之间清晰的边界
- 非阻塞至关重要:钩子必须快速返回,繁重工作由 worker 完成
- 优雅降级:记忆系统即使失败也不会破坏 Claude Code
- 基于队列的解耦:捕获和处理彼此独立进行
- 渐进式披露:上下文注入采用索引优先的方法
- 生命周期对齐:每个钩子都有清晰且单一的目的
延伸阅读¶
- Claude Code Hooks Reference - 官方文档
- 渐进式披露 - 上下文预热理念
- 架构演进 - 从 v3 到 v4 的历程
- Worker Service 设计 - 后台处理详情
钩子驱动的架构让 Claude-Mem 既强大又无感。用户永远不会注意到记忆系统在工作——它只是随着时间的推移让 Claude 变得更聪明。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/claude-mem