关于 rutter
rutter 是为 AI Agent 提供共享记忆层的 MCP 服务,基于用户自有文件夹中的 Markdown 笔记。它记录带日期的会话结论及所引用笔记,便于在已配置的客户端间搜索过往决策。
主要功能
- 01以只追加方式保存 Markdown 记录
- 02记录会话结论及笔记哈希
- 03用本地 SQLite 索引搜索笔记
- 04回顾近期会话和立场
- 05按内容哈希跟踪重命名笔记
适用场景
- 01回忆早前会话中的决策
- 02跨已配置的 AI 客户端搜索笔记
- 03核对记录决策所依据的笔记
- 04回顾立场随时间的变化
MCP 相关
该 MCP 服务提供搜索和读取笔记、近期会话及已记录立场的工具。已配置客户端的钩子会将会话结论和立场更新写入共享 Markdown 文件夹。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
4收录的工具列表 · 更新于 1 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| type | string | 否 | 按 frontmatter 中的 `type` 筛选(例如 note、moc、reference)。 |
| limit | integer | 否 | 最大结果数(默认值为 8)。 |
| query | string | 是 | 要搜索的内容(自由文本;必须匹配所有词语)。 |
| domain | string | 否 | 按 frontmatter 中的 `domain` 筛选。 |
| status | string | 否 | 按 frontmatter 中的 `status` 筛选(例如 evergreen、snapshot)。 |
详细介绍
rutter
面向 AI Agent 的个人记忆层。 一份记忆,多个 Agent,由你掌控。
模型不是记忆。你的笔记才是。 rutter 会回忆过去的决策,以及 AI 会话引用过的笔记。记录以纯
Markdown 格式保存在你自己的笔记文件夹中(与笔记并列的 _librarian/ 文件夹里),而不是存放在模型或供应商的账户中。
它是一个基于该文件夹的 MCP(Model Context Protocol)服务。会话做出某个决定时,客户端会留下一行记录。一个钩子(客户端在每轮交互后运行的脚本)会将这行记录存入你笔记文件夹中带日期的文件,不会为了润色而改写内容,并附上它所引用的每篇笔记的路径和内容哈希(笔记内容的指纹)。
几周后,你可以在同一个 AI 工具或另一个已配置的工具中询问自己的结论,就能找回那行记录及其日期和相关笔记。重命名但未编辑的笔记会通过哈希追踪。rutter 无法定位的引用会标记为未解析,而不会被丢弃。原地修改的笔记目前还不会被标记。
它适合使用 Markdown 笔记且熟悉配置 AI 客户端的人。请从
docs/getting-started.md 开始;Claude Code 用户只需几分钟即可安装插件。其他用户可从 npm 安装,软件包名为 rutter-memory(那里的 rutter 是一个无关库的名称)。
它不包含模型。它由代码和存储组成,因此推理由你的客户端负责。
这个名字源自大航海时代:rutter 是记录实际航行路线的航海日志,与地图相对。地图记录已知的信息,rutter 记录你为此做过什么。
这是个人工具,以参考实现形式发布,并非受支持的产品。 它为一个人的工作流打造,分享出来是因为这种机制或许有用。不承诺开发路线,不承担支持义务,也不保证下一次提交不会改动你所依赖的内容。欢迎复刻、借鉴其中的想法,也可以提交 issue。请不要在它的基础上构建任何关键系统。
在所有 AI 工具中共用一份记忆
rutter 为 Claude Code、Grok、Codex、Copilot CLI、Antigravity 和 Muse Code 保存同一组记录。每个客户端都会将会话摘要写入同一个笔记文件夹。每个客户端也会通过同一个 MCP 服务读取这些摘要。
你周二在 Codex 中做出的决定,只要 Codex 为它写入了一条记录且其钩子已运行,周四向 Claude Code 提问时就能看到。你无需在客户端之间复制任何内容。这之所以可行,是因为记忆是由你掌控的 Markdown 文件夹,而不是某个工具内置的功能。
| 你使用的工具 | 请参阅 | 所需操作 |
|---|---|---|
| Claude Code | Claude Code 快速上手 | 只需几分钟;无需克隆 |
| Grok | Grok 使用路径 | 安装一次 npm、配置一个钩子,并运行一次 grok mcp add |
| Codex | Codex 使用路径 | 安装一次 npm,并配置一个你信任的钩子 |
| GitHub Copilot CLI | Copilot CLI 使用路径 | 安装一次 npm、配置一个钩子,并添加一个指令文件 |
Antigravity CLI(agy) | Antigravity 使用路径 | 安装一次 npm、配置一个钩子,并添加一个规则文件 |
Muse Code(muse) | Muse Code 使用路径 | 安装一次 npm,并运行一次安装程序 |
| 任何其他 MCP(Model Context Protocol)客户端 | 仅通过 npx 回忆 | 添加一条配置。仅支持搜索和回忆:没有钩子就不会捕获任何内容 |
| 从克隆版本使用 Claude Code | 从源代码开始 | 共 8 个步骤(M1 到 M8),从手动写入一条记录开始。适用于贡献者 |
Grok 仅在 Linux 上经过验证(已知问题)。仅需 npm 的使用路径(Codex、Copilot CLI、Antigravity、Muse Code)是在同时安装了 Claude Code 的机器上测试的,因此尚未在干净的机器上测试;请参阅已知问题。
注册 MCP 服务后,每个客户端都能读取记忆。写入则需要一个在每轮对话后运行的钩子,而这 6 个客户端都有这样的钩子。任何其他 MCP 客户端都可以读取但无法写入,只需通过 npx 运行服务
(仅通过 npx 回忆)。Antigravity 这一行介绍的是 agy
命令行工具,而不是 Antigravity IDE。
将每个客户端都指向同一个笔记文件夹。每个客户端设置该路径的方式各不相同。Claude Code 插件会在安装时询问该路径。Grok、Codex 和 Copilot CLI 会从启动它们的 Shell 中读取 LIBRARIAN_VAULT_PATH。
Antigravity 和 Muse Code 的安装程序则接受 --vault。每条记录都会注明由哪个客户端写入;仅当钩子能够判断时才会注明时间
(请参阅已知限制)。
这与现有记忆有何不同
你的运行环境几乎肯定已经以某种方式实现了这些功能。Claude Code 会将会话整理成摘要,并从中推断偏好,写入记忆文件夹,然后随时间推移进行整合——合并重复内容,删除看起来过时的内容。
整合是合理的默认做法,但这恰恰与这里的做法相反。
这里的内容永远不会重写已存储的行。记录只会追加,读取时按会话分组;写入时也不会为了风格而改写某一行:只会进行一行规范化处理,并截断至 2,000 个字符。
第二个区别更为重要。摘要本身不是记录,而是一种陈述。摘要加上其中引用的笔记的版本化状态才是记录,因为它提供了可供比较的指纹。
相邻领域的做法各自涵盖了这第二部分的一部分。架构决策记录会记录 做了什么 和 为什么这样做,却不会固定记录决策适用时的版本。有事件溯源日志会为事件加上时间戳,却很少记录文件级来源信息。供应链溯源格式能正确地对内容进行哈希处理,但它们是为审计人员打造的,而非为你的下一次工作会话打造。因此,决策会悄然偏离产生它们的代码,却没有任何提示。
这里的每条引用都会附带笔记在记录该行时的内容哈希。即使笔记改了名称,也会根据精确的哈希匹配找到对应笔记;无法定位的笔记则会显示为未解决,而不是被悄悄丢弃。存储的哈希还让你可以将文件与其当时的状态进行比较。目前还未实现报告原地修改的笔记。
这种设计还带来两个次要特点。存储库属于你——它是你自己的笔记目录中的 Markdown,而不是供应商的账户或工具的私有文件夹,因此具有可移植性、可搜索性、可提交到 git 的特性,并且即使这个项目消失,你仍然可以读取其中内容。这也是你使用的每个客户端都能读写同一批记录的原因。而且,由于客户端会在上下文仍然加载时写入摘要,记录过程不需要额外的大模型调用或网络调用。代价是,质量取决于客户端是否遵守风格约定,详见 服务告诉客户端的内容。
这些功能都不是为了与检索能力竞争。具备能力的 Agent 读取组织良好的笔记目录,本来就能很好地完成检索。这个项目解决的是短暂会话无法实现的部分:跨越时间的记忆。
它目前的功能
- 将笔记目录索引到本地 SQLite FTS5 全文索引中——这是可丢弃、可重新生成的缓存。你的文件始终是事实来源。
- 随手记录。 当会话得出结论或产出成果时,客户端会留下一行记录;Claude Code、Grok、Codex、Copilot CLI、Antigravity 或 Muse Code 的 Stop hook 会将该行追加到
<notes>/_librarian/sessions/<date>.md,并通过内容哈希引用涉及的笔记。客户端会被要求为每项可区分的成果各写一行,而不是每个会话只写一行——一次工作会话通常会留下三四行;阅读时,这些记录会被归并为对该会话的完整叙述。同一会话已经记录过的行会被跳过(具体规则见docs/memory-of-use.md)。记录持久保存、可提交到 git,并由客户端写入。 - 这行记录有风格约定。 面向一位聪明但时间紧迫、未参与该会话的读者来写:先写结果,使用常见词语而非会话中的简写,不要杜撰代号或版本标签,约 40 个词。此约定作为指引包含在服务的 MCP 指令中。服务不会为了符合风格而重写摘要——超过字数限制的摘要会在记录时报告,然后未经编辑直接存储。服务会将每行规范为单行(删除控制字符、合并空白),并在超过 2,000 个字符时截断;详见风格约定。
- 工作区来源信息。 每条记录都包含会话的工作目录、据此得出的项目名称,以及存在时的 git 远程 URL,因此跨越三项工作的某一天也能清晰呈现。无需配置;解析过程只读取本地文件——不会运行
git,也不会联系远程服务。 - 四个只读 MCP 工具:
librarian-search— 排序后的全文搜索。每条结果都包含其路径、type/status/created来源信息,以及匹配片段。多词查询采用 AND 匹配,因此搜索“blue man group”会找到同时包含这三个词的笔记,而非任意包含其中一个词的笔记。你之前互动过的结果还会附带一条简短的既往互动说明;这只会增加信息,绝不会改变排序。librarian-get-note— 按路径获取一篇笔记的完整内容。librarian-recent— “我最近在忙什么?” 按日期从新到旧列出会话,并提供日期、项目和来源信息;可选project筛选条件、以天为单位的window,或count。librarian-positions— “我对 X 有什么看法?后来变了吗?” 可通过三种方式查询:用topic查询精确的话题键(返回一个答案或明确说明未找到),用query根据记录的立场进行自由文本匹配,或用note查询按路径引用某篇笔记的立场。每个答案都是该话题当前的立场,并附上形成和最后修订的日期;添加chain: true可查看完整的取代历史。
- 随服务传递的指令。 任何已连接的客户端在回答关于近期活动的问题时,都会被告知使用
librarian-recent;在回答“我以前见过这个吗?”这类问题时,先使用librarian-search,再直接读取文件;并用通俗语言报告回忆到的摘要——包括风格约定存在之前写入的记录,这是让那些密集的旧记录变得清晰的唯一方法。无需为每个项目配置客户端。 - 记录自身的使用情况。 按 ISO 周统计有状态行为被调用的次数,计数仅存于本地。
- 立场记录(decision-graph Phase A)。当客户端对某个话题形成、改变、重申或撤回立场时,会留下另一种更少见的记录——立场指令,并追加到
<notes>/_librarian/positions/<YYYY-MM>.md。它与会话记录完全分离,采用只追加的记录流。指令类型(assert、revise、reaffirm、retire)决定事件的作用;立场文本本身不作分类,其中只有两个字符串会被读取——[[wikilink]]会成为带版本的引用,revises: <event-id>会记录它所取代的事件——两者都不会从存储的文本中删除。记录不会为符合风格而重写;每条不同的指令只记录一次。 - 立场回忆(decision-graph Phase B)。
librarian-positions根据这些记录流回答“我对 X 有什么看法?这种看法如何变化?”。某个话题的当前立场是其最新事件中的立场;如果该事件是retire,返回的是带有撤回事件自身记录文本的已撤回占位条目——不会返回被撤回的立场,也不会为了生成该结果而从历史记录中删除任何内容。每个答案都会说明其来源:立场形成的日期(即最初的assert),以及存在时最后一次修订的日期——重申会再次确认立场,但不会改变该日期。长时间未有任何事件涉及的立场会标记为休眠;每次读取时都会根据事件自身的时间戳即时计算,且不会存储在任何地方。已撤回的立场绝不会标记为休眠。整个机制是一种投影:每次运行npm run reindex时,都会完全重建三个 SQLite 表,且只依据<notes>/_librarian/positions/*.md重建。只有重新索引才会触发更新——上次重新索引之后记录的立场,要等下一次运行后才能被回忆到。这种延迟会明确披露,不会悄无声息地造成遗漏;也正因如此,回忆机制完全不会干扰记录过程。 - 笔记重命名后仍能保留身份。 引用会同时记录笔记路径和记录时的内容哈希。所谓“失效”,就是路径在磁盘上不存在,仅此而已,无论文件类型是什么——只要某个受限知识库文件仍然存在,对它的引用就是有效的(无论该文件是
.gitignore、导出的.html,还是位于_librarian/目录下),即便它不是已索引的 Markdown 笔记也是如此。重命名一篇被引用的笔记且不更改其内容后,下一次运行npm run reindex会自动将旧引用绑定到新路径——不使用启发式方法,不计算相似度,只进行精确的内容哈希匹配——并在librarian-recent/ 搜索结果增强中静默解析。如果重新索引无法确定(笔记也被编辑过,因此没有当前笔记的哈希与之匹配;或者多篇当前笔记具有相同哈希),那么在librarian-recent中,该引用会明确显示为未解析状态并列出候选项;在搜索结果增强中,若某个候选笔记恰好是搜索结果,也会显示这一状态。此状态会一直保持,直到通过npm run identity-confirm确认该引用。如果没有任何候选项(笔记既被重命名又被编辑),则该引用只会显示在librarian-recent中——该列表是发现未解析引用的完整入口;搜索结果增强以候选项为锚点,因此无法提及没有候选项的引用。在搜索结果中,单纯的候选项绝不会被标注为用户曾与之互动;未解析状态及其候选项会作为单独的明确说明呈现。确认后的绑定具有持久性:即使知识库后来发生变化,导致自动精确哈希匹配指向其他位置,确认的绑定仍会优先采用,并会显示不一致情况(“已确认 X;当前哈希匹配到 Y”),而不会被静默覆盖——只有再次运行npm run identity-confirm才会更改绑定。无论发生何种情况,存储的会话记录都不会被重写。
已知限制
先在此说明,避免之后才发现:
- 较早的记录内容密集。 风格约定及其字数预算是在最初两周的记录之后才制定的,而现有记录不会追溯修改。早期条目读起来像构建日志。阅读时的指导是唯一能触达这些记录的方式。
- 质量取决于你的客户端。 服务端不包含模型,因此忽略风格约定的客户端仍会生成摘要,而服务端会照常忠实地存储这些摘要。
- 你的工具之间共享,但人与人之间不共享。 一个人的工具共享同一份记忆。这里没有涉及人与人之间共享记录、批准流程,或决策版本的优先级。这些是团队规模下的难题,这里都没有解决。
- 记录标注的是客户端,而非模型。 当钩子能够确定客户端时,会话条目或立场事件会带有
client标签(claude、grok、codex、agy、copilot或muse),这样你就能看出哪个客户端停止生成摘要。标签指的是宿主,而非模型:agy运行 Gemini 和 Claude 模型,且没有钩子负载会指出具体是哪一个模型。宁可不标,也不猜测;因此,标签出现之前的记录、手动注册的钩子,以及无法明确判断的轮次都不会显示标签。目前无法按客户端筛选或搜索。 - 捕获取决于每种工具所用的模型是否遵循指令。 钩子只会提取模型写下的内容。Claude Code、Grok、Codex、Copilot CLI、Antigravity 和 Muse Code 都经过了真实会话测试。如果模型跳过该内容,就不会有任何内容可供捕获。
- 重建索引时可能会短暂影响另一个会话。 服务端启动并检测到更改时会重建索引,每几千条笔记大约会花一秒删除并重新创建表。此时从另一个打开的会话进行搜索,可能会得到空结果。这种情况很少见,因为索引未发生变化时不会重建,而且下一次调用就会恢复。
- 引用展示的是捕获了什么,而非读取了什么。 客户端会选择要列出的路径,哈希则在轮次结束后、执行捕获时计算。rutter 无法确认模型是否读取了某个文件、某个文件是否产生了结论、捕获是否完整,或路径未变的笔记自那时起是否发生过更改(尚未实现此类报告)。仅追加是服务端遵守的规则,并非防篡改审计。请参阅what rutter can and cannot establish。
- 语义搜索尚未实现。
embeddings.ts只是移植后的接口,没有具体实现;检索仅支持全文搜索。 - 仍在积极评估中。 有状态行为受使用情况门槛控制——项目会衡量它是否真的被频繁使用,并准备好接受它并未被使用的结论。
要求
- Node ≥ 22(使用内置的
node:sqlite,无需原生构建步骤;包含 FTS5)。已在 Node 26 上验证。 - 一个 Markdown 笔记目录。该项目最初面向 Obsidian 构建,能够理解 wikilinks 和 frontmatter,但并不要求使用 Obsidian。
- 用于自动捕获的 Claude Code、Grok、Codex、Copilot CLI、Antigravity 或 Muse Code。MCP 工具适用于任何 MCP 客户端。 One memory across your AI tools 中的表格说明了六种工具各自如何捕获,并链接到相应的设置说明。Claude Code 会从转录内容中提取指令,Copilot CLI 和 Antigravity 也会如此(从当前轮次中提取)。Grok、Codex 和 Muse Code 使用 Stop 事件中的最终助手消息,因此在 Codex 和 Muse Code 中,指令必须出现在最终回复里。
设置
首先将其指向你的笔记。 默认路径是作者自己的路径;目录无法读取并不算错误——程序会成功重新索引并报告笔记数为 0,看起来一切正常,直到你搜索时发现什么也找不到。
通过 npm 安装:
export LIBRARIAN_VAULT_PATH=/path/to/your/notes # do this before reindexing
npm install -g rutter-memory
rutter-memory reindex # expect a non-zero note count; zero means the path is wrong
不带参数运行时,rutter-memory 命令会启动 MCP 服务,客户端就是这样启动它的。它还支持 reindex [--force]、recent 和 install-hook;运行 rutter-memory help 可查看这些命令。安装 rutter-memory 还会提供一个 rutter 命令,它是同一程序的短名称;这与 npm 上的 rutter 包无关。
从克隆的仓库开始,进行代码开发:
export LIBRARIAN_VAULT_PATH=/path/to/your/notes # do this before reindexing
npm install
npm run build # compile to dist/ (optional; tsx runs the TS directly)
npm run reindex # expect a non-zero note count; zero means the path is wrong
通过环境变量配置:
| 变量 | 默认值 | 含义 |
|---|---|---|
LIBRARIAN_VAULT_PATH | ~/Documents/knowledge-vault | 要编入索引的笔记目录(只读)。请设置此项。 |
LIBRARIAN_DB_PATH | npm 安装:~/.local/share/rutter/<id>/librarian.db,每个笔记文件夹各一个。克隆仓库:<repo>/data/librarian.db | 派生索引的存放位置。npm 安装会优先使用 $XDG_DATA_HOME,而不是 ~/.local/share。一个索引仅服务于构建它的笔记文件夹;如果第二个文件夹指向同一个文件,操作会被拒绝,而 reindex --force 会将该文件转交给它。 |
LIBRARIAN_USER_LABEL | the user | 服务在客户端收到的指令和工具描述中,如何说明这些内容属于谁。将其设为你的名字后,客户端会被告知它正在查看你的内容,读起来比“用户的”更自然。 |
使用记忆(会话记录、使用日志)存储在 <notes>/_librarian/ 下——位于你的笔记目录中,绝不会放在此代码仓库中。请参阅 docs/memory-of-use.md。
初次使用? 阅读 docs/overview.md,了解 librarian 是什么以及它如何工作;然后阅读 docs/memory-of-use.md,深入了解捕获 / 回忆 / 丰富 / 门控行为。同样的文档也发布在 shinytoyrobots.github.io/rutter 上,可直接阅读——npm run site-drift 会在该网站内容与这些文件不一致时发出报告。当前的阶段安排见 docs/roadmap.md。
docs/decision-graph.md 介绍了决策图背后的设计——为什么立场是对仅追加事件日志的投影,而不是需要你维护的第二份内容;docs/decision-graph-plan.md 则是将其拆分为多个阶段的构建计划。
从命令行试用
从 npm 安装后,rutter-memory recent 接受与下方 npm run recent 相同的参数。搜索、使用门控和 identity-confirm 只能从克隆的仓库中运行:
npm run search -- bitemporal forgetting memory # full-text search
npm run recent # recent session summaries
npm run recent -- --days 7 # just the last week
npm run recent -- --project rutter # just one project (case-insensitive)
npm run gate # per-ISO-week use count
在工作区来源记录功能出现之前捕获的条目只会显示没有项目——不会显示占位符,而 --project 筛选器会跳过这些条目,不会猜测其所属项目。
如果 npm run recent 在重命名后显示引用为 [UNRESOLVED -- candidates: ...],且重命名同时更改了笔记内容(或目标变成了重复内容),请手动确认正确的目标——这是仅限本地的命令,不是 MCP 工具,因此 Agent 无法自行改写失效引用的含义:
npm run identity-confirm -- Notes/old-name.md Notes/new-name.md
启用环境捕获(两个步骤,一次性)
无需在 CLAUDE.md 中添加任何内容。 整个捕获约定——需要留下摘要、确切语法以及如何撰写——都作为 MCP 指令随服务一起提供,服务会在连接时将其发送给每个客户端。注册钩子,重启,完成。
Antigravity 和 Copilot CLI 是例外。Antigravity 会将这些指令保存为文件,而不是放进提示词中;Copilot 只有在调用 rutter 工具后才会加载指令,因此这两个安装程序也会写入一个指令文件。(详情请见
docs/memory-of-use.md。)
1. 注册 Stop 钩子。 如果通过 npm 安装,请运行:
rutter-memory install-hook # adds the hook to ~/.claude/settings.json
rutter-memory install-hook --client codex # Codex: ~/.codex/hooks.json (then trust it via /hooks)
rutter-memory install-hook --client antigravity # Antigravity: ~/.gemini/config/hooks.json
rutter-memory install-hook --client copilot # Copilot CLI: ~/.copilot/hooks/rutter.json
请全局安装(npm install -g),不要通过 npx 安装:钩子通过绝对路径注册,而 npx 缓存可能随时被清除。切换 Node 版本后,请重新安装并再次运行同一行命令;它会将现有钩子移到新路径,而不是再添加一个。
如果是从克隆安装,请运行以下命令:
npm run build && npm run install-hook # adds the hook to ~/.claude/settings.json
npm run build && npm run install-hook -- --client codex # Codex: ~/.codex/hooks.json (then trust it via /hooks)
npm run build && npm run install-hook -- --client antigravity # Antigravity: ~/.gemini/config/hooks.json
npm run build && npm run install-hook -- --client copilot # Copilot CLI: ~/.copilot/hooks/rutter.json
……或者手动将其添加到 ~/.claude/settings.json(钩子会运行 dist/capture-cli.js,因此请先构建):
{
"hooks": {
"Stop": [ { "hooks": [ { "type": "command", "command": "/ABSOLUTE/PATH/hooks/librarian-stop.sh" } ] } ]
}
}
2. 重启 Claude Code。 钩子会在会话开始时读取;新会话也会加载刚构建的服务。使用 /mcp 验证(四个工具),然后在下一次实际会话后检查捕获内容是否出现在 <notes>/_librarian/sessions/ 中。
服务会告知客户端的内容
如果客户端从未留下摘要,钩子就不会捕获任何内容;服务也不包含用于撰写摘要的 AI——因此服务会提出要求。此处的约定块是从 src/server.ts 中的 SERVER_INSTRUCTIONS 原样引用的,该文件是约定的唯一来源;有一项测试(COR-R-030)会在此处副本与其不一致时失败。
当会话决定或产出了值得日后回忆的内容时,请留下会话摘要——发出一行指令,格式如下:
每完成一件可以单独描述的事情,就发出一行,而不是等到最后把所有内容都写在一行里;琐碎工作完全省略。捕获钩子会在每轮对话后提取最新的一行;无需调用工具来记录。后续一行只描述自上一行以来新增的内容。
每行都要写给一位时间紧迫、头脑清晰但未参与本次会话的读者:先说明决定或产出了什么;优先使用常见词语,而非会话中的简称;展开说明或避免使用本次会话新造的代号、版本标签和缩写(知识库中使用的术语可以保留);当用户做出决定、选择、裁定或纠正时,注明是用户所为,而不是因为用户提出了问题。保留给出的理由。目标长度约 40 个词,最多 60 个词——只写一行,不要写成构建日志;内容会原样存储。
当你对某个主题形成、改变、重申或放弃某种立场时,也要留下一行立场记录:`` -- 单独存储,逐字节原样保留,且应少用。
以“Write each line”开头的段落是风格约定(见
风格约定)——它是避免六个月后面对一整个目录却看不懂其中摘要的唯一保障。服务在这方面不会提供帮助:无论收到的内容是什么风格,它都不会改写。未填写的模板是唯一例外:仍用 <angle brackets> 括起来的摘要会被视为“无指令”,而不会被存储,因此只复制指令行却不填写内容,将不会捕获任何内容。
工作目录会随 Stop 事件一并传入,因此每条记录对应的项目也无需设置。
如果捕获内容不再出现,备用方案是将该约定块粘贴到全局 ~/.claude/CLAUDE.md 中,作为常驻规则。这是双重保险,并非必需步骤——如果确实需要这么做,那就是值得报告的缺陷,因为服务本应独立承担这项工作。
安装为 Claude 插件
该插件打包了服务和捕获钩子,因此无需手动构建、注册或编辑任何内容。安装为插件后,Claude Code 只会询问一件事——要搜索的笔记文件夹——然后自行启动服务和 Stop 钩子。
rutter 已列入 Anthropic Directory,适用于 Claude Code 和 Cowork。Claude Code 内置了该目录:在 /plugin → Discover 下找到 rutter,或运行
/plugin install rutter@anthropic-plugin-directory
该目录提供的是 Anthropic 上次审核的版本,可能落后于此代码库。若要直接跟踪此代码库,请改从其自己的市场安装:
/plugin marketplace add shinytoyrobots/rutter
/plugin install rutter@rutter
选择一种方式即可,不要两种都用:两次安装会注册两个服务和两个捕获钩子。
按提示选择笔记文件夹,然后运行 /reload-plugins。如果改用 shell 安装(claude plugin install rutter@anthropic-plugin-directory,或添加市场后运行 rutter@rutter),请传入 --config vault_path=/path/to/notes,或随后使用相同的插件名称运行 /plugin configure;没有笔记文件夹,服务就无内容可读。
我们已在 Claude Code、Codex 和 Antigravity 中检查环境自动捕获;也通过安装程序在 Grok 1.0.62 和 Muse Code 1.4.10 中检查过(Grok 所在机器从未安装过 Claude Code);还在一次 Copilot CLI 会话中检查过,捕获的内容随后已从 Claude Code 中召回。该目录也为 Cowork 列出了 rutter;尚未检查其中的捕获功能。
- 运行内容。 一个本地 MCP 服务(
node dist/stdio.js)和一个 Stop 钩子(hooks/librarian-stop.sh)。两者都只是普通的 Node 和 shell 脚本,可在此代码库中查看;dist/是已提交的 TypeScript 构建产物,因此实际运行的代码与 GitHub 上的代码一致。 - 存放位置。 搜索索引位于插件自己的数据文件夹(
~/.claude/plugins/data/…)中,并会在更新后保留。会话记录存放在笔记文件夹内的_librarian/中。 - 索引会自动维护。 服务首次启动时会构建索引;笔记、会话记录或位置流发生变化后,服务下次启动时会重建索引。若要获取会话中途所做的更改,请启动新会话。
- 如果你之前手动注册过钩子(
rutter-memory install-hook或npm run install-hook),添加插件时请从~/.claude/settings.json中移除该条目,否则会尝试捕获两次。系统会检测到重复内容,不会重复存储,但这会造成不必要的工作。 - 维护者: 已提交
dist/,因此插件可直接从克隆的代码库运行。每次src/变更时,请运行npm run build并提交构建结果。
设置 Grok、Codex、Copilot CLI、Antigravity 和 Muse Code
Claude Code 提供一条命令即可安装插件。其他四种方式还需要几个步骤。无论使用哪一种,都请将其指向与其他客户端相同的笔记文件夹,否则每种方式都会各自保存记忆。
Grok
Grok 无需 Claude Code。通过 npm 安装 rutter,然后运行安装程序:
npm install -g rutter-memory
rutter-memory install-hook --client grok
安装程序会写入 ~/.grok/hooks/rutter.json(或 $GROK_HOME/hooks/rutter.json),并输出一条包含笔记文件夹路径的 grok mcp add 命令。需要你自行运行这条命令;安装程序不会代为运行。Grok 会从启动它的 shell 中读取 LIBRARIAN_VAULT_PATH,因此如果笔记不在 ~/Documents/knowledge-vault,请在该 shell 中设置此变量。如果 Grok 已通过 Claude Code 插件运行 rutter 的钩子,安装程序会提示这一点,并且不会添加第二个条目。
钩子仅读取 Grok 最后的助手消息,因此请将摘要行放在最终回复的末尾。Grok 会将该消息截断至 32,768 个字符,因此在非常长的轮次中,末尾的行可能会丢失。如果最终消息中有两行,则只保留最后一行。Grok 路径介绍了完整流程。
Codex
Codex 无法使用该插件。它不会填入插件的任何变量,也不会安装钩子。
已在 Codex 0.163.0 中验证。使用 npm install -g rutter-memory 安装 rutter,然后:
- 在启动 Codex 的 shell 中导出
LIBRARIAN_VAULT_PATH。它的钩子只能看到该环境,无法读取~/.claude/settings.json。第 3 步的安装程序也会将文件夹路径写入钩子(取自--vault、此变量或服务的config.toml条目);如果这些都未设置,捕获内容会存到默认文件夹。 - 使用
codex mcp add rutter … -- rutter-memory注册服务,并通过--env LIBRARIAN_VAULT_PATH=…传入笔记路径。请参阅docs/getting-started.md中的 Codex 路径。 - 使用
rutter-memory install-hook --client codex安装钩子。它还会将 rutter 的说明添加到~/.codex/AGENTS.md中,并用标记块包裹,因为否则 Codex 会对模型隐藏这些说明。然后在 Codex 中使用/hooks信任该钩子。安装不会授予信任,未受信任的钩子永远不会运行。 - 预期摘要行会出现在 Codex 的最终回复中,因为它的钩子会在该处查找。如果最终回复有两行,只会保留最后一行。
GitHub Copilot CLI
已在 Copilot CLI 1.0.98 中验证。使用 npm install -g rutter-memory 安装 rutter,然后:
- 在启动 Copilot 的 shell 中导出
LIBRARIAN_VAULT_PATH。钩子会继承该环境。未设置时,捕获内容会存到默认文件夹。 - 运行
rutter-memory install-hook --client copilot。它会在~/.copilot(或$COPILOT_HOME)下写入hooks/rutter.json和instructions/rutter.instructions.md;如果这两个文件不是由它写入的,则会拒绝替换。其他钩子文件和你自己的copilot-instructions.md都不会受到影响。 - 运行安装程序打印的
copilot mcp add命令。它会显式传入LIBRARIAN_VAULT_PATH,因为 Copilot 只会向 MCP 服务传递PATH。 - 保留说明文件。Copilot 只有在调用某个服务的工具时才会加载该服务的说明,因此大多数会话都不会看到捕获约定。测试发现,仅靠服务的约定还不够:只有在文件开头写明适用时机的 Copilot 专属说明后,模型才会写出该行;安装程序会写入这条说明。
Copilot 的界面会隐藏摘要行,因为它是 HTML 注释。钩子会从 ~/.copilot/session-state/<session>/events.jsonl 读取当前轮次的助手消息。Copilot 会在钩子启动片刻后才写入该文件,因此钩子最多会等待三秒,让当前轮次写入。如果当前轮次有两行,只会保留最后一行。
Antigravity CLI
本文介绍 agy 命令行工具,并已使用 agy 1.3.2 验证。不涉及 Antigravity IDE。使用 npm install -g rutter-memory 安装 rutter,然后手动完成设置:
- 运行
rutter-memory install-hook --client antigravity --vault <your notes folder>。 它会在~/.gemini/config/hooks.json中注册 Stop hook,并将捕获规则写入~/.gemini/config/rules/rutter-capture.md。如果没有指定--vault,它会沿用现有 rutter hook 使用的文件夹, 然后尝试使用 shell 中的LIBRARIAN_VAULT_PATH,最后使用默认文件夹,并告知你使用的是哪个文件夹。 - 运行安装程序打印的
agy mcp add命令。它会使用与 hook 相同的笔记文件夹,确保读写都使用同一文件夹。安装程序只确定一次文件夹,因为两端获取文件夹的方式不同。MCP 服务的文件夹存储在配置中。hook 没有自己的配置,会继承启动agy的 shell 环境变量。如果依赖环境变量,曾出现过一次决定使用实际知识库,而agy却只能读取测试知识库的情况。导出的LIBRARIAN_VAULT_PATH仍会覆盖 hook 的默认值。 - 保留规则文件。
agy会将 MCP 服务的指令保存为文件,而不是放入提示词中。测试中,模型既没有根据这些指令写出摘要行,也没有根据AGENTS.md中的相同文本写出摘要行。全局trigger: always_on规则会在每一轮注入。有了它,Gemini Flash、Gemini Pro 和 Claude Sonnet 都会在作出决定后写出一行,在回答简单问题后则不会写。重新运行安装程序即可更新规则。 - 预期摘要行会出现在本轮的任意位置。Stop 事件不包含回复文本,因此 hook 会从
agy的转录中读取当前轮次的助手消息。如果本轮有两行,只会保留最后一行。不会再次读取更早轮次的指令。
要改用其他笔记文件夹,请使用新的 --vault 重新运行安装程序,然后重新运行
agy mcp add。每次捕获都会在 stderr 上打印 in vault <path>,可在 agy 的日志
~/.gemini/antigravity-cli/log/ 中查看。使用默认文件夹时也会打印相应提示。
Muse Code
已使用 Muse Code 1.4.10(Meta 的终端 Agent muse)验证。使用
npm install -g rutter-memory 安装 rutter,然后运行 rutter-memory install-hook --client muse --vault <your notes folder>。
它会将 Stop hook 和可选的 rutter 服务条目添加到 ~/.config/muse/settings.json,并在 ~/.config/muse/AGENTS.md 中添加标记区块,因为仅凭服务指令,Muse 的模型不会写出摘要行。Muse 会清除环境变量后再运行 hook,因此笔记文件夹会直接写入 hook。安装程序会拒绝处理符号链接或无法解析的文件,并且绝不会更改自有区块之外的文本。之后请启动新的 Muse 会话。详情以及已验证和未验证的内容,请参阅
Muse Code 路径。
接入 Claude Code
刚开始使用这个项目?docs/getting-started.md 提供了适用于每个客户端的设置流程,从安装到首次回忆。
运行 npm run build 后,在仓库根目录中:
claude mcp add rutter --scope user -- node "$PWD/dist/stdio.js"
--scope user 会将服务注册到所有 Claude Code 会话中,无论所在目录为何。
如果不加此选项,服务只对本仓库的项目可用——在你实际工作的目录中无法使用。
服务会在启动时刷新索引,因此新会话会自动获取更新;运行 npm run reindex 可在不重启的情况下刷新。你可以提问,例如
“搜索我的笔记,找找我之前做过的关于 X 的事情”,客户端会调用 librarian-search。
项目布局
src/
config.ts notes + db + _librarian paths, ignore list
vault.ts markdown walk + frontmatter parse
db.ts node:sqlite open + FTS5 schema
indexer.ts full reindex (notes -> cache)
startup-index.ts build the index at start if missing, rebuild only if notes or records changed
search.ts FTS5 query + get-note
embeddings.ts stubbed port (not implemented)
server.ts MCP tool registration (search, get-note, recent, positions) + server instructions
stdio.ts stdio entry point (local Claude Code)
# memory-of-use:
fs-safe.ts write choke point: path confinement + atomic, append-only writes
sanitize.ts untrusted summary -> one inert line
refs.ts versioned identity (path + content-hash)
workspace.ts workspace provenance (cwd -> project + git remote, local reads only)
session-record.ts typed record format + append-preserving persistence
capture.ts ambient capture orchestration
directive.ts parse the client's session-summary directive (no inference)
positions.ts position-event stream format + append-preserving persistence (decision-graph Phase A)
position.ts position capture orchestration
position-directive.ts parse the client's position directive (no inference)
position-fold.ts fold the position streams into the rebuildable projection (reindex only, Phase B)
position-recall.ts librarian-positions' three query modes + what is computed rather than stored
position-render.ts how a recalled position is worded (attribution, retired stub, inert rendering)
recent.ts librarian-recent (grouping / project / window / count / empty-state)
enrichment.ts additive prior-engagement annotation on search results
instrumentation.ts append-only use log + per-ISO-week counts
identity.ts note identity: exact-hash rename binding + the rebuildable projection
app.ts application seam (domain + instrumentation), used by server + tests
reindex.ts / search-cli.ts / recent-cli.ts / gate-cli.ts / capture-cli.ts / identity-confirm-cli.ts CLIs
hooks/
librarian-stop.sh Claude Code / Grok / Codex / Copilot CLI / Antigravity / Muse Code Stop hook -> capture-cli
hooks.json registers that hook when installed as a Claude plugin
.claude-plugin/ plugin manifest (name, version, how it starts the server, notes-folder option, listing links) and marketplace
dist/ committed build, so the plugin runs straight from a clone
spec/ the executable spec — scenarios, requirements, conformance mapping
test/ node:test suite (temp fixture directories; never touches your real notes)
构建方式
行为会先定义,再编写代码。spec/spec.md 包含 Given-When-Then
场景,以及据此推导出的需求;每项需求都对应评估它的测试,每次修订也都会记录哪些方案被否决及原因。
如果你想了解这里某项决策的来由,这个文件会如实说明。本文档是摘要;规范文件则提供详细内容。
隐私政策
rutter 完全在你的机器上运行。本节就是完整的隐私政策。
收集内容。 作者和任何第三方都不会收集任何信息。服务会读取你指定的笔记文件夹,且绝不会更改你的笔记。它会写入五类本地文件,你都可以打开并阅读。其中四类存放在笔记文件夹内的 _librarian/ 文件夹中;第五类与插件一起存放:
- 会话记录,位于
_librarian/sessions/:AI 客户端在会话决定或产出内容时写入的单行摘要,按原样存储(规范化为单行);同时记录其引用的笔记路径及内容哈希、工作目录、由工作目录推导出的项目名称、会话 ID、写入记录的客户端(四个固定名称之一,绝非模型名称),以及该目录的 git 远程 URL(如果有,则从.git/config读取;不会访问该 URL,且存储前会移除其中的任何令牌、密码或查询字符串)。 - 立场记录,位于
_librarian/positions/:你针对某个主题形成、改变或撤销立场时,客户端记录的立场,按相同方式存储,并附带日期。 - 笔记身份账本(
_librarian/note-identity.md):当你引用的笔记被重命名时,记录引用现在指向的路径,以旧路径、新路径和内容哈希的形式保存。在重建索引或确认匹配时写入。 - 使用日志(
_librarian/stateful-use.jsonl):每次使用回忆工具时,记录时间戳和工具名称,方便你判断是否值得保留这些工具。 - 搜索索引(SQLite 文件,存储在插件自己的数据文件夹中:npm 安装位于
~/.local/share/rutter/,克隆版本位于data/,或者位于LIBRARIAN_DB_PATH指定的位置),由你的笔记和上述记录构建,以加快搜索速度。它是缓存,可随时删除并重建。
使用和存储方式。 仅用于在你自己的电脑上回答你自己的搜索和回忆问题。不会向任何地方发送数据:代码不会发起网络请求,也没有分析或遥测功能。rutter 自身会运行两项内容:本地服务(不会生成其他程序),以及捕获钩子 hooks/librarian-stop.sh,每轮结束后运行本地的 dist/capture-cli.js,以写入上述记录。两者都不会读取凭据或 API 密钥。你使用的 AI 客户端(例如 Claude)会看到工具返回给它的内容,并遵循该客户端自身的隐私条款;rutter 无法控制这些条款。
与第三方共享。 无。
保留期限。 文件会一直保留,直到你将其删除。会话记录、立场记录、身份账本和使用日志按照设计只增不改;要删除它们,请删除笔记文件夹中的 _librarian/ 文件夹。要删除索引,请删除插件的数据文件夹(卸载插件时会执行此操作)、克隆版本的 data/ 文件夹,或 npm 安装版本的 ~/.local/share/rutter/(卸载软件包不会将其删除)。如果设置了 XDG_DATA_HOME,该安装的索引会位于 $XDG_DATA_HOME/rutter/;如果设置了 LIBRARIAN_DB_PATH,索引则是该变量指定的文件,以及它的 -wal 和 -shm 文件。请先退出正在使用该索引的客户端。任何内容都不会存放在其他地方。
联系方式。 如有问题或疑虑:提交 issue,或发送邮件至 [email protected]。
许可证
MIT。请参阅 LICENSE。
索引是缓存。笔记属于你。记录才是重点。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。