关于 Longhand
Longhand 是面向 Claude Code 的本地记忆服务,将会话事件原样存入可搜索的档案。其 MCP 工具支持回忆、查看历史和文件相关工作流,且不调用 API。
主要功能
- 01原样保存本地会话记录
- 02跨会话搜索
- 03可回放时间线和文件历史
- 04回忆与项目状态工具
- 05实时摄取会话
适用场景
- 01查找早期 Claude Code 会话中的修复方案
- 02查看一段时间内的编辑和工具活动
- 03利用项目或会话历史继续工作
MCP 相关
MCP 服务向客户端提供记忆搜索、会话和时间线查看、文件历史等操作。它还可通过 MCP 工具协调档案状态。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
17收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | any | 否 | 结果数量上限(默认值为 10) |
| query | string | 是 | 自然语言查询 |
| max_chars | any | 否 | 最大总输出字符数(默认值:12000)。如需完整内容,请设置更高的值。 |
| tool_name | string | 否 | 按工具名称筛选(Edit、Bash、Read 等) |
| event_type | string | 否 | 筛选:user_message、assistant_text、assistant_thinking、tool_call、tool_result |
| project_id | string | 否 | 按 project_id 将搜索范围限定到某个项目 |
| session_id | string | 否 | 将搜索范围限定到单个会话(前缀匹配) |
| project_name | string | 否 | 按名称子字符串将搜索范围限定到某个项目(例如 'gonzo') |
| file_path_contains | string | 否 | 筛选显式 file_path 包含此字符串的事件(仅限 tool_call/tool_result 事件——用户消息没有 file_path 元数据) |
详细介绍
Longhand
也在使用 Codex? 让 Claude Code 和 Codex 共用一个记忆档案。
为 Claude Code 提供持久的本地记忆。 每次工具调用、每次文件编辑、每个思考块,来自每个 Claude Code 会话的内容,都会原样存储在你的设备上。可搜索、可回放,还能通过模糊的自然语言问题进行回忆。零 API 调用。零摘要。不会由 AI 决定哪些内容值得记住。
Claude Code 会在几周后悄悄轮换会话文件。 Longhand 会在文件消失前将其捕获到 SQLite 中。导入后,你的历史记录将永久保留,即使源 JSONL 文件已被删除也不例外。尽早安装;未能捕获的过往将无法恢复。
如果你的
~/.claude/projects/中有 20 个以上 Claude Code 会话,Longhand 就能在约 29ms 内搜索你遇到过的所有修复、决策和对话,而且不需要任何 API 调用。
它会消耗很多 Token 吗?不会——你最常用的工具都经过了输出上限设计。 跨 100 多个会话进行一次完整的
recall,返回约 4K 个 Token。读取一个原始会话 JSONL 的成本是它的 10–50 倍。请参阅 Token 预算,了解哪些工具限制了输出,哪些没有。
pip install longhand
longhand setup # ingest history + install hooks + configure MCP
longhand recall "that stripe webhook bug from last week"
想先试试看吗? 运行 longhand demo,通过一个虚构的 3 会话样例语料库进行 60 秒的演示——不会触碰你真实的 ~/.claude 和 ~/.longhand。演示会在沙盒存储中写入一个 Stripe-webhook 错误、一次 Supabase 身份验证迁移以及一个下游 401 修复,然后运行跨会话回忆和项目状态查询,让你在决定安装前了解输出效果。
pip install longhand
longhand demo # sandboxed; cleans up afterwards (pass --keep to explore)
要升级到 1.0.0 吗? 此版本结束了 0.13 开启的弃用期。这里移除的所有内容自 0.13.0 起就已发出警告:
- 四个 CLI 别名已移除:
patterns→recall "<topic>",recap→status --days N,continue→status --session <prefix>,reanalyze→analyze --all。 reconcileMCP 工具现在默认执行试运行。 如果你的 Agent 循环依赖隐式修复,请显式传入fix=true。试运行结果会在负载中说明这一点。- 六个已弃用的 MCP 工具已从列表中移除(19 → 13),但仍会永远响应并附带迁移说明——弃用的名称存在于用户自己的
CLAUDE.md文件中,因此绝不能直接报错。 - 新增:COMPATIBILITY.md 说明了 1.x 版本的保证内容和非保证内容。
你的数据库无需做任何处理。迁移会自动完成,任何 0.11 及更高版本的存储库都能在之后的任意 1.x 版本中打开——这是承诺 2,测试套件中还有一个真实的 0.11 架构样例来证明这一点。
要升级到 1.2.x 吗? 没有移除内容,只有一些值得了解的修复:
- 1.2.3: MCP
get_session_timeline的tail现在会返回超过 5,000 个事件的会话中真正的最后 N 个事件——以前返回的是中间部分。setup的结尾提示不再建议使用recap(已在 1.0 中移除),现在改为建议使用status --days 7。Recall 页脚和get_session_timeline自身的工具描述不再提及search_in_context(它在 1.0 时已从工具列表中退役,但仍会响应),现在改为指向search——页脚会写明调用方式:search(session_id=…, query=…, context_events=5)。 - 1.2.2: 如果 Recall 的最佳匹配比其他匹配早很多,现在会说明这一点,避免过时的修复方案被误认为仍然有效(参见 Recall Example)。Codex 上下文压缩记录(
compacted)不再触发doctor的转录格式漂移警告。 - 1.2.1: MCP
get_session_timeline存在一个哨兵值错误,导致每次调用都恰好只返回一个事件,并忽略limit/offset,除非显式传入tail——现已修复。search的项目自动限定范围不再覆盖显式指定的session_id。
升级到 0.13.0? 不会有任何功能中断——该版本开启了 v1.0 弃用期,因此所有旧名称都继续可用,同时指向对应的替代项:
status现在是唯一的恢复命令:直接运行status= 最近摘要(原为recap),status <project>保持不变,status --session <prefix>= 会话尾部(原为continue)。旧命令仍可运行并会显示提示;它们将在 v1.0 中移除。- 六个 MCP 工具合并为六个保留工具,参数保持不变(
search_in_context→search+context_events、get_episode→find_episodes+episode_id等——完整表格见 CHANGELOG)。退役名称仍会响应,并在回复前附上迁移说明。 - Hooks 不再能以非零状态退出——失败会记录为
~/.longhand/logs/中的线索,新增的两行doctor检查(Hook errors、Transcript format)会显示这些问题。 - “今天”/“昨天”的 Recall 时间范围现在按你的本地日历日计算,而不是按 UTC 计算;如果你不在 UTC 时区,排名可能会调整一次。
- Windows 用户:此版本修复了一个严重错误,该错误可能导致摄取锁的存活检查终止其他 longhand 进程。
- 新增:
LONGHAND_DATA_DIR可一次性为 CLI、hooks 和 MCP 服务迁移存储位置——但longhand config --set除外;无论存储位置是否迁移,它始终写入~/.longhand/config.json(#96;--edit目前也是空操作,原因相同)。新增status --json/doctor --json,用于脚本调用。
升级到 0.9.0? 实时摄取可捕获进行中的会话,计划历史记录会作为一等数据保留,并且可选的协调器任务会在后台维护索引的准确性:
- 新增
longhand ingest-live命令,由 Claude Code 的Stophook 运行,在助手轮次之间跟踪活动转录。进行中的会话会立即变为可查询状态,可通过list_sessions、get_session_timeline/timeline、get_file_history和replay_file查询——它只写入事件,不生成嵌入,因此recall和语义search仍需等待 SessionEnd 阶段(或运行会重新摄取并进行分析的reconcile --fix阶段)才能看到这些内容。 - 新增
longhand plans list命令和list_plansMCP 工具,可查看整个历史记录中所有对~/.claude/plans/*.md的写入/编辑操作。现在,计划与 episodes 一样,会作为独立实体提取。 - 新增
longhand schedule install-reconciler,可安装一个可选的 launchd 任务,定期运行reconcile --fix——自动捕获实时和会话后 hooks 遗漏的内容,无需你操心。 Stophook 与现有的 SessionEnd hook 可共存:前者会随着转录增长实时跟踪;后者会在会话关闭时执行完整的分析流程。
升级到 0.8.1? 过时信号现在会传递到所有应出现的位置,且 reconcile 已成为 MCP 工具——Claude 可以在会话中自行修复索引:
- 当目标项目在磁盘上有尚未导入的转录记录时,
search和list_sessions现在会在响应中加入stale: true和stale_reason。在 v0.8.1 之前,这些工具只会返回看似正常的空结果(这与recall_project_status旨在捕捉的静默失败情况相同,只是发生在上一级)。 - 新增 MCP 工具
reconcile,用于封装longhand reconcile --fix。过期提示出现后,Claude 会直接调用reconcile,而不是让用户运行 CLI 命令。 list_sessions的默认limit从 20 提高到 50——活跃日常常涉及 5 个以上项目和 5 个以上会话;旧的默认值会悄无声息地截断回顾结果。
从 0.7.x 或更早版本升级? 回顾叙述更简洁,底层还新增了真正用于查找缺陷的测试层(自 0.8.0 起):
- 在 v0.8 之前,
_compose_fix_summary会在一半提取出的片段前加上字面标签"Intent:"(在参考语料中占 49%)。对于这些片段,该标签会出现在每一条回顾叙述中。首次打开存储时,迁移 v4 会将其从现有记录中移除——无需运行命令。 fix_summary中的差异内容现在会在空白边界处截断,并显示…,而不是截在词元中间(如phoneNum'、family?:'、strin')。仅影响后续内容。- 叙述页脚中的“其他匹配”行现在会包含会话 ID,方便进一步查看。
- 新增的金丝雀测试框架(
tests/fixtures/corpus/)将回归测试锚定到实际发布版本中的缺陷。新增的回顾验证器(scripts/recall_diff.py)会对照实时语料保存并比较排序结果——能够捕捉 pytest 无法发现的回归问题。
pip install --upgrade longhand
longhand recall "..." # migration runs transparently on first open
如果你也从 0.5.x 版本升级,请运行一次 longhand reconcile --fix,根据 v0.6 的推断改进重新归属多项目会话(现在,cd 进入项目的会话会归属到工作量最多的项目,而不是首个事件的 cwd)。如果你使用的是 0.5.8 或更早版本,请依次运行:longhand reconcile --fix && longhand analyze --all。这两个命令都可重复运行,不会造成问题。
历史记录较大?(~/.claude/projects 超过 1 GB) 首次回填在 M 系列 Mac 上预计需要 10–30 分钟——大部分耗时用于在所有核心上运行嵌入模型(因此你会看到 CPU 使用率达到三位数;这是 ONNX 正常工作,并非卡住)。--skip-analysis 会跳过另一项处理——片段/分段提取以及项目/会话级向量——而不会跳过事件嵌入,后者始终会运行。因此它能节省一些时间,但无法减少主要耗时:
longhand setup --skip-analysis # events stored + embedded; episodes/segments deferred
longhand analyze --all # fill in episodes + segments + project/session vectors whenever, safe to background
使用 --skip-analysis 后,search、时间线、文件历史记录和提交查找都能立即使用——无论是否启用此标志,每个事件都会生成嵌入。recall 的片段/分段叙述和 recall_project_status 需要先运行 analyze --all 才有可用数据。M 系列 Mac 上的完整分析吞吐量通常约为每秒 1–2 个会话。
状态:v1.2.3——稳定,经过日常使用测试,通过安全审计(零个严重问题),已发布到 PyPI,可作为 Claude Code 插件使用。已针对 45 个推断项目中的 792 个真实会话(786 个 Claude Code 会话、6 个 Codex 会话)完成验证(测量日期:2026-09-27)。617 项单元测试全部通过。
完整文档: Longhand Wiki——入门指南、CLI 参考、MCP 工具参考、架构和故障排查。

反转
所有人都在通过扩大上下文窗口来解决 AI 记忆问题:100 万个 Token、200 万个 Token,甚至无限上下文。整个行业都在朝同一个方向狂奔:让模型保存更多状态。
Longhand 走的是相反的方向。模型不需要保存记忆,磁盘来保存。
| 更大的上下文窗口 | Longhand | |
|---|---|---|
| 存储位置 | 向模型提供商租用 | 笔记本电脑上的 SQLite 文件 + ChromaDB |
| 每次查询的成本 | Token 数 × 价格 | 零 |
| 隐私 | 数据会经过别人的服务器 | 数据永远不会离开你的设备 |
| 速度 | 大型上下文需要数秒到数分钟 | ~29ms 搜索 · ~128ms 完整回忆(预热后;参见 统计数据) |
| 损失 | 长上下文中间部分的注意力会衰减 | 不做任何摘要——消息、编辑和思考块均按原样存储† |
| 持久性 | 窗口关闭时消失 | 一直保留,直到你删除文件 |
| 跨模型版本 | 无法迁移 | 相同数据,适用于任何模型 |
| 离线使用 | 不支持 | 支持 |
| 扩展取决于 | 提供商的定价 | 你的硬盘 |
† Claude Code 的九种条目类型(KNOWN_SKIP_ENTRY_TYPES),以及 Codex 端的三种顶层记录类型(CODEX_SKIP_RECORD_TYPES)和十种 event_msg 类型(CODEX_SKIP_EVENT_MSG_TYPES:即规范消息在 UI 中的镜像,以及轮次记录)都会被识别并跳过,而非存储——它们大多只是运行框架的记录信息(队列标记、进度提示、用量和状态快照)。不过,这并不完全等于“零内容丢弃”:其中一种类型 attachment 可以在自身的 attachment.prompt 字段中携带排队等待的后续提示词,而 Codex 的加密推理记录和上下文压缩(compacted)记录则会被直接丢弃。其他所有包含实际内容的记录——每条普通消息、工具调用、文件编辑和思考块——都按原样保留。
AI 的“记忆危机”是人为制造的限制。存储问题早已解决。SQLite 始于 2000 年。ChromaDB 是成熟的开源向量存储。两者都能在笔记本电脑上运行。Longhand 通过忽略这场危机来绕过它——你过去的会话已经保存在磁盘上,由 Claude Code 自己写入 JSONL 文件,其中包含每一条事件的原文。Longhand 会读取这些文件,在本地为其建立索引,让你能对完整历史记录进行语义回忆,而且不会向别人的 API 发送任何 Token。
本地存储。完整保留。归你所有。
**存储占用:**重度高级用户约需 7.5GB(792+ 个会话、269k+ 条事件、数月来每天使用 Claude Code,涉及 45 个推断项目——测量于 2026-09-27,参见 统计数据)。普通用户通常为 200–400MB。Claude Code 将源文件轮换移出磁盘后,Longhand 并非副本——而是唯一副本。
平台支持
Python 3.10–3.14 均受到全面支持,并由 CI 强制把关——分支保护要求 PR 合并前,所有五个版本上的完整测试套件都必须通过(管理员可以绕过此限制——参见 CONTRIBUTING.md)。
Longhand 对所有 Python 版本都固定使用 chromadb<1.0,而不只是 3.14。这个固定版本要求最初是因为 chromadb 较新的 Rust 绑定在 3.14 上会发生段错误(#4,现已关闭);在经过整个版本矩阵验证之前,这项要求会一直保留。
不支持 Windows。请使用 WSL2。 每个 PR 都会运行一个 windows-latest × py3.12 测试任务,但它是非阻塞的 continue-on-error 步骤——而且会失败。每次运行都有 12 项核心测试失败(钩子安装/卸载幂等性、配置文件路径、脱敏、实时导入行数统计、演示清理等——见 #112),并非偶发的边缘情况。该任务在 GitHub 界面中显示为绿色,只是因为 continue-on-error 吞掉了失败的步骤;它的作用是收集证据,并不表示 Longhand 可以在 Windows 上正常运行。Linux(3.10–3.14)是 CI 实际强制把关的唯一平台;没有 macOS 测试任务。macOS 是作者日常使用的操作系统,也是在此平台上手动验证的(参见 Stats),但 CI 并未独立验证 macOS。
无论 CI 版本矩阵如何,有两项功能都只适用于 macOS:schedule install-reconciler 会安装一个 launchd 任务,在其他平台上则不执行任何操作(Linux 上请使用 cron/systemd-user;Windows 上请使用 WSL2)。mcp install 也只会写入 Claude Desktop 的 macOS 配置路径(#105)——Linux 没有 Claude Desktop 版本,且不支持 Windows(#112);要让 Windows 上的 Claude Desktop 连接到 WSL2 中的安装,需要手动编辑其配置,通过 wsl.exe 启动 Longhand,而这种做法尚未经过测试。
从 1.1.0 起,Codex Desktop 和 Codex CLI 的对话也会存档到同一归档中——参见 与 Codex 配合使用。
兼容性
Longhand 1.0 做出五项承诺,仓库中每项承诺都有相应的强制执行机制。完整内容见 COMPATIBILITY.md;简要版如下:
- 稳定接口——CLI 和 MCP 在 1.x 期间保持冻结。只有在主版本升级时才会移除功能,且必须提前一个完整的次版本发出警告。
- 数据向前兼容——由 0.11 及更高版本写入的数据库,后续任何 1.x 版本都能打开。迁移会自动执行、仅执行一次,且绝不重新编号。旧版代码遇到较新的数据库时会明确拒绝操作,而不会在不了解情况的前提下继续运行。
- 钩子保证——钩子绝不抛出异常、绝不访问网络,也绝不阻塞你的提示词。
- 上游变更绝不悄无声息——未知的转录条目会被保留、在
doctor中显示,并由回归测试把关。 - 指标如实反映——计数反映真实信号,且
doctor绝不建议无法奏效的补救措施。
**弃用政策:**任何计划移除的内容,都会至少提前一个完整的次版本发出警告,并在警告中说明替代方案。已退役的 MCP 工具名称是唯一永远不会消失的内容——它们会从工具列表中移除,但会始终响应并附上迁移说明,因为这些名称存在于用户自己的 CLAUDE.md 文件中。
Longhand 与 claude-mem 的对比
thedotmack/claude-mem 是 GitHub 上最受欢迎的 Claude Code 记忆工具(55k+ 星标)。它是个不错的工具。不过,它解决记忆问题的方向与 Longhand 正好相反。在选择其中一个之前,值得了解它们的区别。
| claude-mem | Longhand | |
|---|---|---|
| 存储内容 | AI 生成的摘要/“观察” | 原始 JSONL 中的逐字事件 |
| 由谁决定保留什么 | 写入时由大模型决定 | 无人决定——没有摘要器进行筛选 |
| 压缩 | 语义压缩(有损,设计如此) | 无压缩(无损) |
| 每个会话的 API 调用次数 | 一次或多次(调用 Claude 进行摘要) | 零次 |
| 思考块 | 通常会折叠进摘要 | 一等事件,逐字存储 |
| 确定性重放 | 不支持——摘要无法还原文件状态 | 支持——保留每个差异并可重放 |
| 模型可移植性 | 取决于摘要器的输出 | 相同数据可永久用于任何模型 |
| 运行时 | TypeScript、Bun、运行于 :37777 的 HTTP worker | Python,无需服务端 |
| 许可证 | AGPL-3.0 | MIT |
理念上的分歧:claude-mem 会询问 AI 哪些内容重要,并保留这些内容。Longhand 会保留实际字节,让你日后自行决定。 如果你信任模型对其自身过往的判断,claude-mem 的做法在查询时成本更低(因为内容已预先摘要),也更节省存储空间。如果你曾因摘要删掉了后来证明至关重要的内容而吃过亏,那么 Longhand 就是那个永远不会丢弃任何内容的工具。
两者可以在同一台机器上共存——它们会使用相同的 JSONL 文件,互不干扰。
原则
Longhand 基于一些原则构建。如果你不同意这些原则,可能需要另找工具。
1. 信息不会消失,只会转移。
数据“丢失”时,几乎从来不是真的不见了。它只是被压缩、摘要、归档到别处,或改了名字。找到原始来源,事实仍在那里。Claude Code 已经会将每个会话以 JSONL 格式写入磁盘。这个文件就是原始来源。Longhand 只负责读取它。
2. 摘要是一种伪装成便利功能的有损决策。
大多数 AI 记忆系统会读取对话,然后让 AI 写下“哪些内容重要”。这样一来,AI 就成了自身记忆的把关者,而 AI 有一些动机——简洁、自信、连贯——这些动机与事实并不一致。最终你得到的是一段关于事情经过的故事,而不是事情本身。
Longhand 从不进行摘要。它会存储完整记录,并让你查询。
3. 原始记录成本很低。假装它成本很高,只会造成浪费。
完整的 Claude Code JSONL 文件大小从几 KB 到几 MB 不等。一年的日常会话也不过几百 MB。在现代硬件上,这点空间微不足道。没有任何工程上的理由要丢弃这些数据。基于摘要的记忆并没有节省空间——它只是把本来免费的信息拱手让出。
4. 思考过程最有价值。
当 Claude 生成 thinking 块时,其中包含决策背后的推理——用户通常看不到这些内容,而它们几乎总比最终答案更有用。基于摘要的记忆会丢弃思考块,因为它们属于“内部内容”。Longhand 则将它们视为一等事件。“我选择使用条件更新时在想什么?”这个问题会找出包含答案的逐字思考块。
5. 无法复现的修复,就不算真正保留下来的修复。
如果你在 3 月修复了一个 bug,那么修复时该文件的状态就是事实。Longhand 会按顺序应用会话 JSONL 中的每项编辑,以确定性方式重建该状态。不猜测,不靠 AI 推断,只按原样应用差异。你可以查看过去任意会话中任意时刻的任意文件的确切状态。
6. 记忆应该主动,而不只是可搜索。
可搜索的存档很有用,但它是被动的。真正的记忆能回答模糊的问题。“几个月前,我在做一个总是出问题的游戏,后来你帮我修好了——把那个修复应用到现在。”Longhand 会解析时间描述,匹配项目,找到问题→修复的事件,并返回差异。你不必知道会话 ID,只要记得这件事发生过就行。
7. 确定性胜过取巧。
Longhand 中的所有分析都基于规则:用正则表达式检测错误、用哈希生成项目 ID、向前遍历提取事件。核心流程中没有大模型。这意味着速度快(温缓存召回中位数约为 128ms——测量于 2026-09-27,见 统计数据)、结果可复现(相同输入 → 相同输出),并且完全本地运行(无需 API 密钥或云服务)。以后可以在此基础上添加大模型层,但其基础依靠规则,而不是模型的主观看法。
8. 要么本地运行,否则就不运行。
你的 Claude Code 历史属于你自己。它会存入 ~/.longhand/ 下的 SQLite 文件和 ChromaDB 目录。没有遥测、同步或账户。即使笔记本电脑处于离线状态,Longhand 也能运行。即使 Anthropic 服务中断,Longhand 也能运行。删除该目录后,数据就会消失。
有一个不起眼的例外,现完整说明:交互式 CLI 会检查 pypi.org 上是否有更新版本的 Longhand。大多数命令会遵守 24 小时缓存,并且只会在命令自身输出之后打印提示,因此不会延误任何你正在等待的操作。doctor(以及会运行它的 setup)是个例外——每次都会强制同步获取最新信息,超时时间为 2 秒,然后才打印表格。请求只包含请求本身——没有遥测、标识符,也不包含任何关于你的语料库的信息。Claude Code 的钩子以及 mcp install 实际为 Claude Desktop 配置的 mcp-server 入口点都不会执行此检查,但 longhand shared-mcp、mcp serve 或 demo 目前仍会在退出时像其他命令一样执行相同的检查(#101)。LONGHAND_NO_UPDATE_CHECK=1 可以关闭所有这些检查。“零 API 调用”一如既往地意味着:大模型或云服务都不会接触你的数据。
实际功能
使用 Claude Code 时,每个会话都会将一个 JSONL 文件写入 ~/.claude/projects/<project>/<session-id>.jsonl。该文件包含每条消息、每次工具调用、每个思考块、每次文件编辑的完整编辑前后内容,以及每个事件精确到毫秒的时间戳。
Longhand 会读取这些文件,然后为你提供:
- 语义搜索,覆盖你生成过的所有事件
- 可筛选搜索——可按工具、文件、会话、项目和事件类型筛选,且可以组合筛选(MCP
search;CLIsearch命令支持工具、文件、会话和类型筛选,但不支持项目筛选;两者都不支持时间范围筛选) - 工具调用考古——“显示涉及 Supabase 的 Bash 命令”——按相关性排序,而非罗列所有匹配项;限定会话或项目范围可减少无关结果
- 跨会话文件历史记录——按时间顺序查看所有会话中对特定文件的每次编辑
- 会话回放——重建过去任意会话中任意时刻的文件状态
- 推理检索——查询 Claude 的原始思考块
- 时间线视图——按时间顺序回放,支持分页(偏移量、尾部和仅摘要扫描模式)
- 模糊回忆——用自然语言提问,回顾过去的工作(“上周修复的那个竞态条件”)
- 项目推断——自动检测你参与过的项目,并为其分类和设置别名
- 片段提取——自动检测会话中的问题→修复序列
- 对话片段——按主题聚类(故事、设计讨论、调试、规划),让回忆找到 为什么,而不仅仅是 做了什么
- Git 感知项目回忆——询问“X 项目上次进展到哪了”,一次调用即可获取最近的提交、未解决的问题和上次会话结果
- Git 提交提取——从会话中结构化提取每次 git commit、push、merge、checkout,并关联到片段
- MCP 服务——提供 13 个工具,让 Claude 能在实时对话中直接查询 Longhand
- 自动导入钩子——接入 Claude Code 的
SessionEnd钩子,自动为新会话编制索引 - 实时导入——
Stop钩子(通过hook install与SessionEnd一同安装,不可单独设为可选)会在轮次之间跟踪当前活动的对话记录,因此可立即通过list_sessions、时间线、文件历史记录和回放查询进行中的会话。它只存储事件,不生成嵌入向量——语义recall/search仍需等待SessionEnd阶段,或等待reconcile --fix阶段处理(实时跟踪不会设置project_id,因此 reconcile 会将其视为需要完整重新导入) - 计划历史记录——对
~/.claude/plans/*.md的每次 Write/Edit 都会作为一等实体捕获,可通过longhand plans list和list_plansMCP 工具查询 - 秘密信息脱敏(选择启用)——
longhand config --set redact.enabled=true会在导入时屏蔽符合秘密信息特征的字符串(API 密钥、令牌、JWT、数据库密码),使其在进入索引前被处理;longhand redact --apply会追溯屏蔽此前导入的数据(有关 git commit 消息覆盖范围当前存在的缺口,请参阅 SECURITY.md 和 #110) - 后台协调器——可选的 launchd 作业(
longhand schedule install-reconciler)可在无需手动运行reconcile --fix的情况下保持索引准确 - 上下文注入——
UserPromptSubmit钩子会在 Claude 看到你的消息前自动注入相关的过往上下文(可配置阈值和大小上限);当注入的上下文相较于更新的工作显得陈旧时,会像recall一样打印Note: …时间差提示 - 可配置——使用
longhand config调整注入相关性、令牌预算和行为,无需编辑代码
安装
pip install longhand
longhand setup
就是这样。longhand setup 会补录你现有的 Claude Code 历史记录,安装用于自动更新的钩子,将 Longhand 注册为 Claude Desktop 的 MCP 服务,打印用于将其添加到 Claude Code 的单行命令(claude mcp add longhand -s user -- longhand mcp-server),并验证一切是否正常。首次安装大约需要两分钟,之后无需维护。
以后升级:pip install -U longhand。
开发者安装(从源代码安装)
git clone https://github.com/Wynelson94/longhand.git
cd longhand
pip install -e .
longhand setup
或者自行运行各条命令
longhand ingest # ingest all your existing Claude Code history
longhand analyze --all # run analysis (projects, outcomes, episodes, segments)
longhand hook install # wires both SessionEnd and Stop hooks (installed together)
longhand prompt-hook install # (optional) auto-inject past context into new prompts
longhand mcp install # register Longhand as an MCP server for Claude Desktop
claude mcp add longhand -s user -- longhand mcp-server # ...and for Claude Code
longhand schedule install-reconciler # (optional, macOS-only) launchd job to run reconcile --fix periodically
longhand config # view/tune hook behavior (relevance threshold, injection size)
longhand doctor # verify everything is wired up
longhand ingest-live 故意没有列在这里——它主要是 Stop 钩子调用的隐藏入口(从 stdin JSON 读取 transcript_path),不过你也可以直接传入 --transcript <path> 手动运行。
快速开始
# What's in the archive?
longhand stats
longhand sessions
longhand projects
# Daily-use commands — status is the single resume command (git-status shape)
longhand status # what have I been up to (recent digest)
longhand status --days 30 -p bsoi # filtered digest
longhand status <project-name> # where did we leave off on a project (git-aware)
longhand status --session <session-id> # pick up where a session left off
longhand history src/app/route.ts # every edit ever to a file
# (recap / continue / patterns / reanalyze were deprecated aliases through
# 0.13 and were removed at 1.0 — use status, recall, and analyze --all)
# Semantic search
longhand search "race condition"
longhand search "stripe webhook" --tool Edit
longhand search "why did we" --type assistant_thinking
# Proactive recall (the fun one)
longhand recall "that clerk type error I fixed a couple weeks ago"
longhand recall "the python missing module bug last month"
# Session inspection
longhand timeline <session-id-prefix>
longhand replay <session-id> /path/to/file.ts
longhand diff <event-id>
# Git history
longhand git-log # recent git operations across all sessions
longhand git-log <session-id> # git ops in a specific session
longhand git-log --type commit # only commits
longhand git-log --query "fix parser" # search commit messages
# Export
longhand export latest-fix # most recent resolved episode
longhand export ep_<id> --out fix.md # specific episode to file
longhand export <session-id-prefix> # full session timeline
# Configuration
longhand config # show current hook settings
longhand config --set hook.min_relevance=3.0 # tune injection threshold
longhand config --set hook.max_inject_chars=1000 # cap token usage
# Plans + background maintenance
longhand plans list # every plan-mode plan you've written
longhand plans list --limit 100 # raise the row cap (default 50)
longhand schedule install-reconciler # background launchd job (macOS); runs reconcile --fix
longhand reattribute # dry-run: find sessions attributed to the wrong project
longhand reattribute --fix # apply the moves (idempotent)
longhand db vacuum --prune-aux # reclaim disk space; --prune-aux deletes ALL event_type='unknown' rows —
# not just harness noise, but doctor's drift records and the deliberately
# preserved summary/pr-link/worktree-state/frame-link rows too
会话 ID 支持前缀匹配——如果只有一个会话以该前缀开头,longhand timeline cf86 就足够了。
回忆示例
$ longhand recall "that stripe webhook I was fixing"
╭─ Project matches ───────────────────────────────────────╮
│ new-product (nextjs web app) · alias: 'stripe' · 1.52 │
╰─────────────────────────────────────────────────────────╯
You asked: that stripe webhook I was fixing
Found it: new-product · 2 weeks ago · session a4ba29d1
### What went wrong
Type error: Property 'current_period_end' does not exist on type 'Subscription'.
### How it was diagnosed
在 Stripe 的类型定义中,current_period_end 从 Subscription 接口中移走了。它仍然存在于实际的 API 负载中,但类型并未公开它。 我们需要通过 Record<string, unknown> 进行类型转换才能访问它。
### The fix
Edit on route.ts: 'const periodEnd = sub.current_period_end' → 'const periodEnd
= (sub as Stripe.Subscription & Record<string, any>).current_period_end as number'
Diff:
- const periodEnd = sub.current_period_end
+ const periodEnd = (sub as Stripe.Subscription & Record<string, any>).current_period_end as number
✓ Verified — a test or command succeeded after the fix.
Other candidates (4)
• 2 weeks ago: Type error: Module '"@/lib/utils"' has no exported member 'getInitials'.
• 2 weeks ago: Type error: Property 'role' does not exist on type 'User'.
只需运行一条本地命令。不会调用 API。这个修复来自 Claude Code 几周前写入你磁盘的会话文件,而 Longhand 一直在等待,随时可以给出答案。
上面没有展示两件事。第一,只有当最佳匹配至少有 30 天前的记录,且比最新的候选匹配至少早 30 天,并且查询中没有时间短语时,才会触发时间差警告——而不只是“早了几周”。触发时,“找到它:”后会立即出现一行——比其他匹配更早——最佳匹配来自 6 个月前,但 2 个月前还有更新的相关工作。如果你想找当前的工作,请添加“本周”或“本月”之类的时间短语。 排名不会改变;这只是警告,不会重新排序。第二,当候选内容来自对话片段而非完整的 episode 时,页脚会显示确切的后续调用,并填入真实会话 ID:search(session_id="a4ba29d1", query="...", context_events=5)。较弱的“也可能相关”页脚(当其他会话中存在片段时,episodes-recall 会显示此内容)目前会打印相同形式的调用,但使用字面占位符 session_id="<session>";请从上方的项目符号中复制真实 ID。
MCP 集成
无论使用哪个客户端,Longhand 的 13 个工具都相同;不同之处在于如何注册服务。
Claude Code:
claude mcp add longhand -s user -- longhand mcp-server
或者安装 Claude Code 插件(包含此仓库的 .claude-plugin/plugin.json 和一个为你注册服务的 .mcp.json),并查看 marketplace/plugin 文档了解当前安装流程。longhand setup 的第 4 步会打印上面的 claude mcp add 命令,但不会替你运行。
**Claude Desktop:**运行 longhand mcp install,将 Longhand 接入 Claude Desktop 的配置(longhand setup 也会执行此操作,除非使用 --skip-mcp)。这只会写入 Claude Desktop 的配置文件,不会影响 Claude Code。
重启客户端后,它便会提供 13 个工具: 核心(可搜索归档):
search— 语义搜索,支持按会话、项目、工具、文件和 event_type 筛选(可组合使用);传入session_id和context_events,即可让每条匹配结果附带其周边对话。当查询文本本身提到某个项目,且未指定project_id/project_name/session_id时,结果会自动限定到该项目(返回内容变为{auto_scoped_to, auto_scope_hint, hits})——传入明确的session_id可取消此行为,传入项目筛选条件可主动限定范围;目前没有其他覆盖方式(#102)list_sessions— 按项目和时间筛选近期会话;传入project_id(以及可选的since/until),即可获取包含项目成果信息的会话时间线get_session_timeline— 按时间顺序查看会话,支持偏移量/尾部分页,以及仅摘要扫描模式(tail: N可回答“最近有哪些事件/最后是怎么结束的”)replay_file— 重建文件在某一时刻的状态get_file_history— 查看文件在所有会话中的每次编辑get_stats— 存储统计信息(其描述目前承诺返回一个实际并未返回的data_dir字段——#106)
主动记忆:
recall— 使用自然语言模糊回忆(优先使用此工具):根据对话片段和会话时间线生成叙述;如果工作留下了清晰证据,还会提供高精度的问题→修复事件。自 1.2.2 起,如果未指定时间的查询中,排名最高的匹配结果距今超过 30 天,且比第二名新近结果早 30 天以上,系统会在开头注明这一点(排序不变)——添加时间描述即可跳过检查recall_project_status— “X 项目上次进展到哪里了?”——结合 Git 信息的项目摘要,包含提交、问题和最后结果find_episodes— 结构化搜索问题→修复配对;传入episode_id可查看某个事件的完整详情(相关事件、差异、修复后的文件状态)list_projects— 浏览推断出的项目;传入match可查找模糊匹配的候选项及其评分理由list_plans— 查看完整历史中对~/.claude/plans/*.md的每次 Write/Edit
Git 历史:
find_commits— 按提交消息、哈希前缀或分支名称搜索所有会话;也可不传查询内容而传入session_id,查看某个会话按时间顺序排列的 Git 记录
自我修复:
reconcile— 封装longhand reconcile,使 Claude 在会话中出现数据过时提示后,能够重新归属并重新摄取数据;默认为试运行(传入fix=true即可应用更改——该默认值在 v1.0 时发生了切换)
工具列表停留在 v1.0,但仍会永久响应,并附带迁移说明: search_in_context → search(context_events) · get_latest_events → get_session_timeline(tail) · get_project_timeline → list_sessions(project_id) · get_session_commits → find_commits(session_id) · get_episode → find_episodes(episode_id) · match_project → list_projects(match)
输出截断规则不一致,并非所有工具都统一处理:search、get_session_timeline、recall、recall_project_status 和 find_commits 接受 max_chars(传入 0 或负数可禁用截断,但通常不建议这样做);list_sessions 和 list_projects 仅在默认模式下会截断到固定的 16,000 个字符——list_sessions(project_id=…) 和 list_projects(match=…) 使用下方所述的不截断路径;get_file_history、replay_file、get_stats、find_episodes(其列表模式返回最多 limit 个结果——默认值为 20,最大值为 1000——而非单个事件;只有 episode_id 详情模式才限定为一个事件)、list_plans 和 reconcile 会不受限制地返回查询结果(#103)。完整说明请参见 Token 预算。
安装后,你可以向 Claude 提问,例如 “上周的会话中,我们对 auth 中间件做了什么决定?”,它会实际搜索自己过去的工作。
自动摄取
longhand hook install 会向 ~/.claude/settings.json 添加两个钩子:Stop 用于在回合之间实时追踪,SessionEnd 用于在会话结束时执行完整分析。两个钩子会一起安装;没有可单独添加其中一个的标志。安装程序会将 longhand 解析为绝对路径,并将每条命令包装为 Claude Code 的钩子架构:
{
"hooks": {
"Stop": [
{"matcher": "", "hooks": [{"type": "command", "command": "/abs/path/to/longhand ingest-live"}]}
],
"SessionEnd": [
{"matcher": "", "hooks": [{"type": "command", "command": "/abs/path/to/longhand ingest-session"}]}
]
}
}
两条命令都会从钩子的 stdin JSON 中读取 transcript_path,因此钩子条目本身无需任何标志。
- Stop 钩子(
ingest-live) 在每轮助手回复后运行。它会追踪 transcript 文件,并增量存储新事件(以及工具调用配对和会话统计信息),因此在进行中的会话仍可通过list_sessions、时间线、文件历史和回放进行查询。它不会生成嵌入,因此语义recall/search仍需要SessionEnd阶段,或运行reconcile --fix阶段;后者会重新摄取并执行完整分析,因为实时追踪的会话从未获得project_id,会落入 reconcile 的null_project分类。此处发生故障时会按设计静默处理——不输出 stdout,也不留下记录——因为绝不能冒险干扰当前回合;SessionEnd和reconcile会作为兜底。 - SessionEnd 钩子(
ingest-session) 在会话结束时运行一次。它会执行完整分析:项目推断、结果、片段、分段和嵌入。如果发生故障,会向 stderr 打印一行信息,并在logs/hook-errors-YYYY-MM-DD.log中留下记录(由doctor显示),然后以 0 退出。
两个钩子都是非阻塞的,运行时间为一到两秒。之后你无需再操心它们。
可选的后台协调器: longhand schedule install-reconciler 会添加一个 launchd plist,按计划运行 reconcile --fix。如果 transcript 仍在磁盘上,它会捕获钩子遗漏的会话——reconcile 会从 ~/.claude/projects/ 枚举会话,因此 Claude Code 已经轮换移除的 transcript 对它不可见,与这里的其他工具一样。
兼容 Codex
也使用 Codex Desktop 或 Codex CLI?Longhand 也会将这些对话线程捕获到同一归档中,因此在 Claude Code 中提出的问题可以根据在 Codex 中完成的工作来回答,反之亦然。
pip install -U longhand
longhand codex-sync # capture Codex threads on this machine (up to 50/run)
claude mcp add --scope user longhand-shared -- longhand shared-mcp # keyword search across both clients, from Claude
codex mcp add longhand -- longhand shared-mcp # the same server from Codex (Desktop: config.toml, see the docs)
此后,reconcile --fix 也会捕获新的 Codex 对话线程,因此定时运行的协调器可以让两个客户端都保持最新。线程在写入期间仅以原始记录形式存储——逐字保存且可搜索,不加载模型;在停止活动 30 分钟后会执行完整处理流程,因此 recall 无需你重新运行任何操作即可看到它,前提是有进程按计划轮询(launchd 协调器、codex-sync --watch 循环,或下方的 codex-sync launchd 模板)。如果没有轮询,codex-sync --semantic 会立即运行完整处理流程。默认情况下会跳过 Codex 为自身启动的线程,UI 镜像绝不会重复存储,未知记录格式则会像其他偏差一样显示在 doctor 中。设置、范围和 macOS launchd 模板:docs/codex.md。
架构
longhand/
├── parser.py — JSONL → typed Events (a short list of known harness-bookkeeping entry types is skipped, not stored)
├── codex.py — Codex rollout capture + 30-minute-quiet finalizer, shared archive
├── replay.py — deterministic file state reconstruction
├── redaction.py — opt-in secret-shaped-string masking
├── update_check.py — best-effort pypi.org freshness check (most commands; the hooks and the hidden `mcp-server` are excluded)
├── types.py — Pydantic models
├── storage/
│ ├── migrations.py — version-aware schema evolution
│ ├── sqlite_store.py — structured data + full raw JSON preserved
│ ├── vector_store.py — ChromaDB: events, sessions, projects, segments, episodes (5 collections)
│ └── store.py — unified ingest pipeline
├── extractors/ — per-event (errors, file refs, topics, git ops)
├── analysis/ — per-session (project, outcomes, episodes, embeddings)
├── recall/ — per-query (time parsing, project match, narrative)
├── demo/ — sandboxed sample corpus for `longhand demo`
├── cli/ — Typer CLI with Rich output (`_commands.py`, `helpers.py`)
├── mcp_server.py — Model Context Protocol server (13 tools)
├── lightweight_mcp.py — `longhand-shared`: 4-tool keyword-only server, no model loaded
└── setup_commands.py — hook install, mcp install, config, doctor
事实来源: SQLite。每个事件的原始 JSON 都会作为 blob 保留。ChromaDB 是搜索索引,只保存语义检索所需的内容。
分析层: 在摄取时运行,而非查询时运行。预先计算项目、会话结果和片段,以加快回忆查询。完全确定性,不使用 LLM。
回忆流程: query → time parse → project match → episode search → rank → load artifacts → narrative。一次完整的 recall 调用会执行多次向量搜索(事件、项目、片段、分段,以及放宽条件后的重试),然后加载素材并组织叙述内容。根据作者的实时语料库在 2026-09-27 进行的测量结果,在预热且进程内的情况下(参见 统计):单次向量搜索的中位耗时约为 29ms;一次完整的 recall 调用中位耗时约为 128ms。
对比
| Longhand | 基于摘要(Mem0、MemPalace、LangMem) | |
|---|---|---|
| 来源 | 原始 Claude Code JSONL | AI 生成的摘要 |
| 捕获的工具调用 | 每一次,原样记录 | 摘要器保留下来的内容 |
| 文件编辑 | 完整的修改前后差异 | 通常不会捕获 |
| 思考区块 | 一等事件 | 通常会丢弃 |
| 文件状态重放 | 确定性 | 无法实现 |
| 问题→修复提取 | 基于规则,在摄取时完成 | 取决于摘要器 |
| 模糊检索 | 支持,且附带相关产物 | 对摘要进行文本搜索 |
| “决定”了什么 | 什么也不决定——全部存储 | 由 AI 决定哪些内容重要 |
| 本地优先 | 是 | 大多数 |
| 完整性 | 每条消息、每次编辑和每个思考区块,均原样记录† | 摘要器保留下来的内容 |
| 执行功能所需的 LLM 调用数 | 零 | 不定 |
摘要记忆和 Longhand 解决的是不同的问题。摘要记忆适合需要在多次对话中压缩上下文的长期个人助手。Longhand 适合需要取证式访问过去 Claude Code 工作记录的开发者——也就是需要精确差异而非改述的情况。
统计数据
在作者的实时存储上测得的语料数据,测量日期为 2026-09-27(Apple M4、macOS,代码位于 main):
- 792 个会话(其中 6 个来自 Codex)
- 269,067 个事件
- 自动推断出 45 个项目
- 提取出 1,809 个问题→修复事件
- 6,632 个对话片段(设计、故事、调试、讨论、规划)
- 提取出 3,723 个 Git 操作
- 存储占用:5.9 GB SQLite + 1.6 GB ChromaDB
延迟:在同一语料和日期上进行基准测试——预热状态、进程内执行,使用 8 个 scripts/recall_diff 查询:
recall(完整流程):中位数约 128ms(16 次运行)- 向量搜索:中位数约 29ms(24 次运行)
- SQL 时间线读取:中位数约 1.6ms(24 次运行)
- 在新进程中首次调用
recall(加载模型和索引,冷启动):约 0.6s
Token 预算
最常见的问题:Claude 使用 Longhand 时会消耗很多 Token 吗?
大多数情况下不会——但上限并非适用于所有工具,请看下表。 13 个工具中有 5 个会截断输出,并在 Claude 看到输出前附加分页提示;另有 2 个会限制输出大小,但只在默认模式下生效;还有 6 个会返回查询产生的所有内容,不设上限(#103)。实际使用中,get_file_history/replay_file 的范围自然限定为单个文件(不过,较长的编辑历史或较大的文件仍可能返回大量内容);需要留意的是 find_episodes——其默认的 list 模式没有自然范围限制,最多可以返回 limit(上限为 1000)条完整的 episode 记录,且不设上限;只有其 episode_id detail 模式的范围才限定为单个 episode。
| 工具 | 默认输出上限 | 可调整? |
|---|---|---|
search | 12,000 个字符 | max_chars |
上下文模式(context_events)下的 search | 20,000 个字符 | max_chars |
get_session_timeline | 16,000 个字符 | max_chars |
recall、recall_project_status | 16,000 个字符 | max_chars |
find_commits | 12,000 个字符 | max_chars |
list_sessions、list_projects(默认模式) | 16,000 个字符 | 不可——固定值 |
list_sessions(project_id=…)、list_projects(match=…) | 无 | 不可——不设上限 |
get_file_history、replay_file、get_stats、find_episodes、list_plans、reconcile | 无 | 不可——不设上限 |
max_chars 参数的上限(MAX_OUTPUT_CHARS) | 200,000 个字符 | 传入 0 或负数可完全禁用截断 |
为何这很重要——对比:
- 直接读取单个原始会话 JSONL: 每个会话 50K–200K 个 Token(Claude Code 会话通常每个有 1–5MB)。
- 使用更大上下文窗口的方案: 每次提示词都要支付完整历史记录的成本。
- 基于摘要的记忆工具: 每次查询成本低,但它们已经丢弃了思考内容块。
Longhand 的成本固定:上限按每次调用计算,而非按整个语料库计算。跨 10 个会话召回和跨 1,000 个会话召回,返回的 Token 量都在同一个范围内。而且 Longhand 本身不会发起任何 API 调用——消耗的 Token 仅来自 Claude 读取回去的 MCP 负载。你与自己的数据之间没有大模型介入。
调整: 上述 5 个工具支持 max_chars 参数,可在每次调用时调低上限。将 get_session_timeline 的 summary_only: true 设为真会移除 content 字段,将负载缩小约 10 倍。
617 项单元测试全部通过,覆盖了全部 13 个 MCP 工具的列举和分发(只有 list_plans 的处理程序没有单独进行调用测试)。完整安全审计:零项严重发现,零项高危发现。主存储打开路径会以 0700 权限创建 ~/.longhand/(部分早期写入路径尚未应用该权限——#99);所有 SQL 均使用参数化查询;所有输入均有限制。依赖项:chromadb、posthog、typer、rich、pydantic、mcp。
作者
Nate Nelson。爱达荷福尔斯。没有计算机科学学位。涉足 14 个行业,通过描述我所看到的内容来构建软件,并让转换过程自然发生。
GitHub:Wynelson94
许可证
MIT。随你怎么用。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。