mcpField
L
Longhand

Longhand

本地服务
github.com/Wynelson94记忆与知识144 周前更新

关于 Longhand

Longhand 是面向 Claude Code 的本地记忆服务,将会话事件原样存入可搜索的档案。其 MCP 工具支持回忆、查看历史和文件相关工作流,且不调用 API。

主要功能

  1. 01原样保存本地会话记录
  2. 02跨会话搜索
  3. 03可回放时间线和文件历史
  4. 04回忆与项目状态工具
  5. 05实时摄取会话

适用场景

  1. 01查找早期 Claude Code 会话中的修复方案
  2. 02查看一段时间内的编辑和工具活动
  3. 03利用项目或会话历史继续工作

MCP 相关

MCP 服务向客户端提供记忆搜索、会话和时间线查看、文件历史等操作。它还可通过 MCP 工具协调档案状态。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"wynelson94-longhand": {
"command": "uvx",
"args": [
"longhand"
]
}
}
}
启动命令uvx longhand

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypilonghand0.5.5

工具

17

收录的工具列表 · 更新于 2 周前

参数类型必填说明
limitany否结果数量上限(默认值为 10)
querystring是自然语言查询
max_charsany否最大总输出字符数(默认值:12000)。如需完整内容,请设置更高的值。
tool_namestring否按工具名称筛选(Edit、Bash、Read 等)
event_typestring否筛选:user_message、assistant_text、assistant_thinking、tool_call、tool_result
project_idstring否按 project_id 将搜索范围限定到某个项目
session_idstring否将搜索范围限定到单个会话(前缀匹配)
project_namestring否按名称子字符串将搜索范围限定到某个项目(例如 'gonzo')
file_path_containsstring否筛选显式 file_path 包含此字符串的事件(仅限 tool_call/tool_result 事件——用户消息没有 file_path 元数据)

详细介绍

Longhand

也在使用 Codex? 让 Claude Code 和 Codex 共用一个记忆档案。

Longhand MCP 服务 PyPI 版本 Python 许可证 测试 本地 SafeSkill 93/100

为 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。
  • reconcile MCP 工具现在默认执行试运行。 如果你的 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 的 Stop hook 运行,在助手轮次之间跟踪活动转录。进行中的会话会立即变为可查询状态,可通过 list_sessions、get_session_timeline/timeline、get_file_history 和 replay_file 查询——它只写入事件,不生成嵌入,因此 recall 和语义 search 仍需等待 SessionEnd 阶段(或运行会重新摄取并进行分析的 reconcile --fix 阶段)才能看到这些内容。
  • 新增 longhand plans list 命令和 list_plans MCP 工具,可查看整个历史记录中所有对 ~/.claude/plans/*.md 的写入/编辑操作。现在,计划与 episodes 一样,会作为独立实体提取。
  • 新增 longhand schedule install-reconciler,可安装一个可选的 launchd 任务,定期运行 reconcile --fix——自动捕获实时和会话后 hooks 遗漏的内容,无需你操心。
  • Stop hook 与现有的 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 工具参考、架构和故障排查。

Longhand 演示


反转

所有人都在通过扩大上下文窗口来解决 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;简要版如下:

  1. 稳定接口——CLI 和 MCP 在 1.x 期间保持冻结。只有在主版本升级时才会移除功能,且必须提前一个完整的次版本发出警告。
  2. 数据向前兼容——由 0.11 及更高版本写入的数据库,后续任何 1.x 版本都能打开。迁移会自动执行、仅执行一次,且绝不重新编号。旧版代码遇到较新的数据库时会明确拒绝操作,而不会在不了解情况的前提下继续运行。
  3. 钩子保证——钩子绝不抛出异常、绝不访问网络,也绝不阻塞你的提示词。
  4. 上游变更绝不悄无声息——未知的转录条目会被保留、在 doctor 中显示,并由回归测试把关。
  5. 指标如实反映——计数反映真实信号,且 doctor 绝不建议无法奏效的补救措施。

**弃用政策:**任何计划移除的内容,都会至少提前一个完整的次版本发出警告,并在警告中说明替代方案。已退役的 MCP 工具名称是唯一永远不会消失的内容——它们会从工具列表中移除,但会始终响应并附上迁移说明,因为这些名称存在于用户自己的 CLAUDE.md 文件中。


Longhand 与 claude-mem 的对比

thedotmack/claude-mem 是 GitHub 上最受欢迎的 Claude Code 记忆工具(55k+ 星标)。它是个不错的工具。不过,它解决记忆问题的方向与 Longhand 正好相反。在选择其中一个之前,值得了解它们的区别。

claude-memLonghand
存储内容AI 生成的摘要/“观察”原始 JSONL 中的逐字事件
由谁决定保留什么写入时由大模型决定无人决定——没有摘要器进行筛选
压缩语义压缩(有损,设计如此)无压缩(无损)
每个会话的 API 调用次数一次或多次(调用 Claude 进行摘要)零次
思考块通常会折叠进摘要一等事件,逐字存储
确定性重放不支持——摘要无法还原文件状态支持——保留每个差异并可重放
模型可移植性取决于摘要器的输出相同数据可永久用于任何模型
运行时TypeScript、Bun、运行于 :37777 的 HTTP workerPython,无需服务端
许可证AGPL-3.0MIT

理念上的分歧: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;CLI search 命令支持工具、文件、会话和类型筛选,但不支持项目筛选;两者都不支持时间范围筛选)
  • 工具调用考古——“显示涉及 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_plans MCP 工具查询
  • 秘密信息脱敏(选择启用)——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 JSONLAI 生成的摘要
捕获的工具调用每一次,原样记录摘要器保留下来的内容
文件编辑完整的修改前后差异通常不会捕获
思考区块一等事件通常会丢弃
文件状态重放确定性无法实现
问题→修复提取基于规则,在摄取时完成取决于摘要器
模糊检索支持,且附带相关产物对摘要进行文本搜索
“决定”了什么什么也不决定——全部存储由 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。

工具默认输出上限可调整?
search12,000 个字符max_chars
上下文模式(context_events)下的 search20,000 个字符max_chars
get_session_timeline16,000 个字符max_chars
recall、recall_project_status16,000 个字符max_chars
find_commits12,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。随你怎么用。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。