关于 nexusmem
nexusmem 将命令行历史、Git 变更和项目文档保存在本地 SQLite 中,供编码 Agent 按相关性检索。它记录命令退出码及来源,并可关联失败与后续修复;数据保留在本机。
主要功能
- 01索引命令、退出码、Git 差异和文档
- 02按相关性与时间排序检索
- 03在失败命令出现时提供先前经验
- 04可选关联失败与后续修复
- 05本地 SQLite 存储
适用场景
- 01找回曾尝试但未成功的命令
- 02为编码 Agent 提供项目历史上下文
- 03追踪失败命令及其修复过程
MCP 相关
README 未说明 MCP 能力。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
6收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| asOf | string | 否 | ISO-8601 日期/时间。将结果限制为在此时刻或之前记录的节点——即“截至当时记忆中有什么”,而非“当时发生了什么”。省略则执行常规的不受限读取。 |
| query | string | 是 | 自由文本问题或搜索词 |
| budget | integer | 否 | 返回上下文块中的最大 token 数。默认值为 2000。 |
| allProjects | boolean | 否 | 搜索此机器上 NexusMem 曾运行过的所有代码库,而不只是 projectRoot。当答案可能位于其他项目中时使用(例如,在其他地方解决过的模式、以前以相同方式失败过的工具)。每个结果都会标记其所属代码库。 |
| projectRoot | string | 是 | 代码库根目录的绝对路径 |
详细介绍
NexusMem

你的编码 Agent 可以读取 git log,却读不到你上周二尝试过但没成功的那四种方法。
NexusMem 会将你机器上实际发生的事情(Shell 命令及其退出码、细致到每个变更文件的补丁的 Git 历史、项目文档,以及可选的助手对话记录)记录到本地 SQLite 数据库中,并按需返回经过排序且符合令牌预算的相关内容。所有数据都保存在磁盘上。无需账号、云服务,也没有遥测。
值得关注的是 Shell 历史记录。Git 会告诉 Agent 哪些内容已发布,Shell 历史则会告诉它尝试过什么、按什么顺序尝试,以及哪些命令以非零状态退出。这些信息在其他地方无从获取,而且终端的滚动缓冲区一旦滚动覆盖,它们就会消失。
目录: 试用 · 精确捕获 Shell 命令 · 失败 → 修复链 · 检索方式 · 会话摘要 · GitHub issues 和 PR · 在 Agent 中使用 · 成本 · 过时信息与来源 · 局限 · 命令 · 跨项目回忆 · 磁盘上的数据 · 开发
试用
在任意 Git 仓库内运行:
npx nexusmem init
npx nexusmem sync
然后问它一个问题。以下是此仓库的真实输出,共 5 条结果中的前 2 条:
$ nexusmem query "windows spawn failure"
Relevant history for: windows spawn failure
- 2026-08-09 [observed] fix: distinguish a failed git spawn from "not a git repository"
readRepoInfo collapsed three unrelated failures into one error: git running and reporting
the path is not a work tree, git not being installed, and the process failing to spawn at
all. Dogfooding hit the third case in two separate sessions...
- 2026-08-09 [authored] README.md — Before a tagged release
- [ ] Retry on transient process-spawn failures on Windows
[observed]/[authored] 是来源标签(参见过时与来源)
——提交是直接观察到的事件,文档章节则是可能过时的书面声明。
提交和文档章节会相互排序,且不超出你设定的令牌预算。 输出过程中没有任何模型对内容进行摘要;排序器只决定不发送哪些内容。(一个可选来源——会话摘要——确实会在摄取时运行本地模型,但绝不会在输出时运行。你查询的始终是已存储的文本。)
要了解实际积累了什么,以下是此仓库运行两天后执行 nexusmem status 的结果:
527 node(s) 2026-08-08 .. 2026-08-09
321 shell_command
130 conversation_turn
60 doc_section
16 git_commit
16 条提交记录。321 条 Shell 命令。任何带终端的 Agent 都已经能检索到这些提交记录,其他内容则不行。
只有在使用 --conversation 同步此语料库时,才会出现 conversation_turn 这一行。助手转录内容是唯一默认关闭的来源,只有选择启用后才会收集,因为其中最可能包含粘贴进去的凭据。默认安装会索引 Git 提交、提交差异、Shell 命令和文档。
要求:Node 22 或更高版本,以及 Git。Node 20 无法使用,因为 better-sqlite3 没有为其提供预构建二进制文件,而且 Node 20 已于 2026 年 4 月停止维护。Ollama 是可选的,只影响语义搜索(见下文)。
可选:精确捕获 Shell 命令
抓取到的历史文件(PSReadLine、.bash_history、.zsh_history)能提供命令文本,除此之外信息不多。此钩子还能提供工作目录、退出代码和真实时间戳:
nexusmem hook install
它会包装现有的 PowerShell 提示符,而非替换它;可重复执行,且运行 nexusmem hook remove 即可彻底撤销。
退出代码正是值得安装此钩子的原因。失败的命令比成功的命令更能传递信号;没有此钩子,就无法区分两者。
可选:编码 Agent 尝试过什么
上面的 shell hook 只能看到你输入的命令。编码 Agent 运行的命令会通过它自己的工具执行,因此永远不会到达交互式提示符——这意味着 Agent 遇到的死路,也就是最值得记住的内容,恰恰是 NexusMem 看不到的。
nexusmem agent install # writes Claude Code's hooks; --project keeps it to this repo
之后重启 Claude Code。从那时起,在运行过 NexusMem 的任何代码库中:
- 它会记录 Agent 运行的每条命令、命令结果,以及运行命令前刚编辑过的文件,因此一次尝试会显示为“编辑了
a.ts,然后npm test失败”,而不是“npm test失败了两次”; - 命令失败时,它会在当时告知 Agent:这条完全相同的命令以前是否在这里失败过、每次失败前编辑了什么,以及最终是什么修复了问题;
- 会话开始时,它会在后台同步,并列出那些失败后没有记录修复方案的命令。
除此之外,它会保持安静。没有匹配项,就不会有消息;每个会话中,每次失败最多提示一条,每个会话最多提示五条;这条处理路径不会进行嵌入、调用模型或发起网络请求。如果 NexusMem 缺失或损坏,hook 不会打印任何内容并以 0 退出,Agent 会继续运行,就像没有安装它一样。
命令在写入前会经过脱敏,方式与 shell hook 完全相同;匹配则基于原始命令的哈希值,因此两条仅在机密内容上有所不同的命令绝不会被混淆。
nexusmem agent status 会显示 hook 是否已安装;nexusmem agent remove 会移除 NexusMem 自己的条目,并保留其他工具的 hook。
请在 Claude Code 运行所在的同一环境中安装。写入设置文件的内容是一条字面命令——包含 Node 可执行文件的绝对路径和指向此 NexusMem 副本的绝对路径——Claude Code 会将该字符串交给它自己的环境中的 shell 执行。如果 Claude Code 是 Windows 二进制文件,却在 WSL 中安装(或反过来),hook 虽然已配置、看起来也已安装,却不会捕获任何内容:路径在实际运行 hook 的环境中无法解析,而无法启动的 hook 不会在任何地方报错。nexusmem agent status 会指出已安装命令中有哪些路径在你查询的机器上不存在;如果它指向的 NexusMem 被移动、升级到其他位置或卸载,也会显示相同提示。
失败 → 修复链(可选加入)
nexusmem sync --link-failures
正常同步后,此功能会遍历每个失败的 shell_command(退出码非零),并通过两种独立的启发式方法查找后来解决问题的内容:其一,在同一项目和工作目录中查找一条文本完全相同、且在 24 小时内以 0 退出的后续命令(相同命令重试);其二,在附近的对话轮次或会话摘要中查找全文匹配度最高的内容,要求失败命令的每个重要词都出现,而不只是其中一个(对话桥接)。一次失败可能会通过其中任一种、两种或都不通过而建立关联。
查询结果会显示这两种关联。最初,对话桥接启发式只要匹配任意一个共同词就会建立关联;在这个仓库自身的真实历史记录中试用后发现,大约一半的关联都是错误的——像“npm”这样常见的共同词就足以将无关讨论关联起来。要求每个重要词都出现后,这个问题得到了解决:在同一语料中再次试用时,对全部结果(而非抽样结果)进行全文人工审核后,确认每条关联都是正确的,不只是摘要看起来正确。
当一条已关联的失败记录出现在结果集中时,它的修复记录会紧跟其后,并沿用失败记录自身的相关性分数,而不必单独匹配查询。这正是此功能的意义所在:查询某件事为什么失败时,不应该还需要单独猜出修复方案所用的词语。此功能也支持跨项目查询——query --all-projects 会使用记录该关联的项目自己的数据库,将失败与修复链起来,因为关联始终只属于发现它的项目。
$ nexusmem query "why did npm whoami fail"
- 2026-08-12 [observed] shell: npm whoami (exit 1)
- 2026-08-12 [observed] shell: npm login (exit 0) -- linked as the fix
检索工作原理
每种来源都会规范化为相同的 MemoryNode 结构,因此提交记录、shell 命令和文档章节可以在同一标准下竞争。检索会通过 FTS5 执行 BM25;如果可以访问嵌入模型,还会通过 sqlite-vec 执行向量搜索,并使用 Reciprocal Rank Fusion 融合结果,只依据排名位置,绝不使用原始分数——BM25 的代价和向量距离处于彼此无关且没有上限的尺度上,只有位置是两者共有的标准。
随后,排名会将三个因素相乘:
score = relevance × signal^0.215 × recency^0.288
relevance 来自查询。signal(fix: 提交记录的排名高于 chore:;失败的命令排名高于成功的命令)和 recency 则是先验因素,在任何查询出现之前就已存在。每个因素的下限都是 [floor, 1],而不是 [0, 1],因此某个维度较弱不会让强匹配的分数归零。
指数会限制信号和时效性这两个因素合计能够推翻相关性的程度:在两者的整个取值范围内,最多相差 2 倍,并且这个上限共同应用于两个先验因素,而不是分别应用。这样设计是有意为之——分数会将两个先验因素相乘,所以即使分别把每个因素限制在 2 倍,二者仍可能合力推翻 4 倍的差距;这对在活跃工作日中创建的新近高信号提交记录影响最大。暴露出这个问题的情况是:两条互不相关、同日创建的 fix: 提交记录,排名竟高于实际回答查询的文档章节。完整推导请参阅 retrieval/rank.ts。
如果没有 Ollama,系统会跳过向量搜索,只使用 BM25——这是完全受支持的状态,并非降级状态;sync 和 query 都会成功,只是执行的工作更少。
会话摘要(可选,使用本地模型)
启用 sources.session.enabled 后,每个已结束的会话都会生成一个提炼后的节点,与原始对话并列保存——记录已作出的决定及其原因,而不是四十轮单独的对话。它使用本地 Ollama 聊天模型(默认是 qwen2.5:3b);不会自动下载任何内容,也不会将任何内容发送到本机以外。
nexusmem scan-session --dry-run
这会在脱敏和预算裁剪后,打印会话将使用的确切提示词,而不会调用模型。
有三项机制可以控制成本。会话只有在静默时间达到 settleMinutes(默认 30 分钟)后才会生成摘要,因此进行中的会话不会在每次同步时都重新生成摘要。提示词会经过哈希处理,如果哈希未变化,就完全跳过模型调用——在本仓库中,稳定状态下对 14 个已有摘要的会话执行同步只需 0.25 秒,且不会调用模型。maxSessions(默认 10)则限制每次运行中交给模型处理的会话数量;其余会话会标记为排队,并在下次同步时处理。
在 14 个真实会话上使用 qwen2.5:3b 测得的实际效果。 摘要本身效果不错:能够按提示词要求的形式,列出决定及其原因。标题则不太可靠——模型大约只有三分之一的时间能生成可用标题,其余时候会输出对话式开场白、多余的项目符号,或简单的“会话摘要”。这些标题都会被拒绝,并回退为开启会话的问题的第一行;即使不够精致,这一行也总是具体的。模型在处理长会话和非英语对话记录时,遵循提示词的表现最差。如果你很看重标题,可以改用更大的模型(qwen2.5:7b);请设置 sources.session.model。
GitHub issues 和 PR(可选)
启用 sources.github.enabled 后,此仓库的 github.com 远程端上的每个 issue 和 PR 都会成为一个节点(标题、首篇帖子和所有评论合并在一起),与讨论在 shell/对话历史中留下的原始内容并列。默认关闭——并非出于敏感性考虑,而是因为它是第一个具有实际外部依赖的来源:它通过 gh CLI 读取,因此需要安装并完成身份验证的 gh(gh auth login),而且会发起实时网络调用,而不只是读取磁盘上已有的内容。无论是仓库没有 github.com 远程端,还是 gh 未通过身份验证,都会静默跳过。
nexusmem scan-github
会预览一次同步将生成的节点,与其他 scan-* 命令相同。maxThreads(默认值为 100)和 maxCommentsPerThread(默认值为 100)限制单次同步的开销;since 会作为独立游标跟踪,因此重复同步时只会重新读取发生变化的讨论串。我们已用此仓库自身的 14 个真实 issue/PR 进行试用:摄取耗时不到一秒,查询“labelled retrieval regression corpus”时,正确地将 issue #8——提出该请求的 issue——排在第一位。
在 Agent 中使用
{
"mcpServers": {
"nexusmem": {
"command": "npx",
"args": ["-y", "nexusmem", "mcp"]
}
}
}
通过 stdio 提供三个工具:search_memory 返回打包后的上下文块,sync_project 负责摄取,get_status 报告当前记忆的内容。每个工具都需要显式传入 projectRoot,因为 MCP 工具调用不携带 shell 工作目录。如果尚未设置仓库,sync_project 会代为运行 init。
成本表现
这类工具中有两个数字容易混淆,因此这里将它们分开说明。
打包器效率是指排序器从自己的候选集中精简内容的比例。在此仓库的语料库中,这一比例为 81–84%。它有助于调优排序器,但不能用来说明你的账单会减少多少,因为基准是虚构的:如果没有 NexusMem,这些候选内容本来就不会进入你的上下文窗口。
端到端节省是指将 NexusMem 实际发送的打包上下文,与完整读取其排序结果中判定与查询相关的同一批文件相比,节省了多少。该数据通过 scripts/benchmark.ts(npm run bench)测得;任何人克隆此仓库并将其指向已同步的语料库,都可以从头重跑:
| 语料库 | 提交数 | 查询集 | 相比完整文件内容 | 相比这些文件的 git log -p |
|---|---|---|---|---|
| 此仓库 | 62 | 16 条真实提示词,逐字取自本项目自身的历史记录 | 95%(中位数 94%) | 98%(中位数 97%) |
vitejs/vite | 9,567 | 16 条机械抽样的查询——见下文 | 99%(中位数 98%) | ~100%(中位数 ~100%)——见注意事项 |
两者都超过了最初设定的 >70% 目标(“相比发送完整上下文,减少 API 令牌开销”);vite 的测试则是首次在该目标始终声称适用的规模上进行测量。
引用这两个数字前,请先阅读测量方法——实际主张比表面看起来更有限:
- 以 NexusMem 自身的排序结果为评估基准,而非外部标准答案:文件集由该查询对应的打包节点所涉及的文件构成。这衡量的是检索选出候选集后,打包步骤节省了多少;并不会独立验证该候选集是否正确。
- 查询集是机械生成的,并非挑选出来的(见
scripts/benchmark.ts):vite 的查询集均匀抽样自说明充分的fix/feat/perf/refactor提交,以及包含理由的文档标题;此仓库的查询集则逐字复用了真实历史提示词,其中有几条是宽泛的任务说明,而非具体问题——这也是它的数据低于 vite 的部分原因。 git log -p基准可能非常庞大——vite 的一条查询基准达到 750 万个令牌,因为其解析出的文件集中有个文件拥有这么长的历史记录。到了这种规模,“直接读取文件历史记录”根本不再是可行的替代方案。- 取代旧的 ~40% 数字:旧数字是针对此仓库的两个手动挑选查询,按未说明的基准手工统计得出的。它并非错误,只是说明不充分——现在以明确的方法和可复现结果的脚本取代了它。
有一样东西无法用百分比表示:Shell 命令和对话轮次没有廉价的 grep 等效方案。没有东西记录它们时,它们就会消失,而不只是变得更难查找。
如需了解这些数字与类似工具自身声明的比较,请参阅 docs/competitor-comparison.md(与 projectmem 对比)和 docs/competitor-comparison-yesmem.md(与 YesMem 对比,包括原生 Windows 支持与其文档所述的 WSL2 要求)。
在约 530 个节点的语料库上,预热后运行 10 次的 p50 延迟:
| 操作 | |
|---|---|
| BM25 检索(FTS5) | ~1.1 ms |
Vector KNN(sqlite-vec) | ~3.2 ms |
| 融合、排序、打包 | ~0.6 ms |
| 查询嵌入(本地 Ollama) | ~55–77 ms |
| 端到端混合检索 | ~56 ms |
所有 SQLite 操作加起来约为 5 ms。此流程中唯一值得优化的是嵌入调用,而它运行在其他人的进程中。
时效性与来源
记忆层需要具备两项能力,而这个记忆层目前只部分具备:区分观察到的事实和猜测,以及在结论受到反驳后将其废弃。本节介绍已有的功能和缺失的功能。
每个节点都带有 provenance,这是在采集器摄取数据时为其设置的四级信任层级:observed(已落地的提交、Shell 命令的实际退出码)> authored(文档章节——人类自己写下的声明)> recorded(对话轮次——逐字记录,但讲的是事件而非事件本身)> derived(会话摘要——模型提炼的内容)。每条查询结果都会显示该层级作为标签,并影响检索权重——信任度越低,节点随时间推移在排名中的权重衰减越快。这个排序是设计主张;具体的衰减比例只是经验判断,并非经过测量的最优值。
nexusmem stale
列出年龄达到一定时长(默认 45 天以上)的非 observed 节点,且没有任何信息确认它们仍然成立——这是一种基于时间和来源的启发式判断,而非基于内容。它不会写入任何内容;哪些候选项确实有误,由你决定。任何 SLM 已标记的候选项(见下文)都会附上现有的 likely superseded by 建议——查看这些建议不花费任何代价,因此普通命令依然即时运行且可离线使用。
nexusmem mark-stale <oldNodeId> --supersedes <newNodeId>
将 newNodeId 关联为 oldNodeId 的替代节点。从此之后,排序器会降低旧节点的权重(旧节点仍可查询,只是通常会排在替代节点之后)——不会删除任何内容,这与 forget 不同。
nexusmem stale --check-contradictions
对于每个候选项,找到最相似的较新节点(本地嵌入搜索),并询问本地 SLM(默认使用 Ollama、qwen2.5:3b)它是否确实与较旧节点相矛盾——这是对实际内容的比较,而不只是看时间。匹配结果会在候选项下方显示为 likely superseded by <id> <title> -- <reason>。每项判断(无论结论如何)都会被缓存,因此已判断过的节点对不会再次发送给模型;除此之外不会写入任何内容——supersedes 仍由你通过 mark-stale 设置。
这也会在 sync 期间自动运行——每次最多进行 3 项新判断(可通过 .nexusmem/config.json 中的 contradictions 块配置;设置 autoCheck: false 可关闭),且只有在嵌入提供方本来就可访问时才会运行;由于有缓存,重复同步无需额外花费。
新建议和未处理建议会显示在同步摘要、nexusmem status(flagged 行)以及普通命令 nexusmem stale 中。
**此功能不会做什么:**它只是由一个小型模型对一对较旧和较新的节点做出是或否的判断,并非已验证事实——请将匹配结果视为需要核查的线索,而不是结论。它还只会将候选项与通过嵌入相似度找到的节点进行比较;如果某个听起来无关的节点与其矛盾,这种情况就不会被发现。全面的矛盾检测(而不仅是向量搜索碰巧找到的节点对)仍是一个未解决的问题,而且这里的任何功能都不会自动替代节点。
provenance 与 trust_state 是两个不同的问题:来源说明声明来自何处,但绝不表示是否有人核查过该声明。
nexusmem review <nodeId> --verify
nexusmem review <nodeId> --reject
记录你对某个节点的判断,与上文的 SLM 矛盾检查器相互独立(检查器只会写入建议,不会写入判断)。--reject 会降低节点在排名中的权重——与 mark-stale 一样,只降权、不删除;节点仍可查询,只是通常会排在更匹配的结果之后。此后,每条返回该节点的查询结果都会将这两种判断之一显示为 [verified]/[rejected] 标签。--verify 只是添加标签,不会提升排名。每个节点最初都处于 candidate 状态(无标签),直到经过审核;重新同步绝不会覆盖已经设置的判断。
何时会失效
- 没有钩子的 Shell 历史记录没有作用域。 抓取的历史记录不包含目录上下文,因此会归到你运行
sync时所在的仓库。只读取尾部的一段记录,因此只是近似结果,并非保证。 - 日语和中文依赖向量处理阶段。 FTS5 的
unicode61分词器会按空白字符拆分文本,因此没有空格分隔边界的语言无法获得有效的 BM25 召回结果。 - 变基会遗留节点。 重写后的历史记录会使节点对应的提交变得不可达。这些节点描述的是真实事件,因此并没有错,但目前还无法定向清理。
sync --rebuild会强制重新扫描。 - 多行 PowerShell 输入会被当作单独的命令读取。 在提示符处跨多行输入的函数不会被还原。
- 如果两次同步之间历史记录文件的开头被截去,抓取回退机制生成的 ID 就会发生变化。 安装钩子即可解决。
- 会话摘要标题取决于模型是否遵循指令, 而 3B 模型往往不会。回退机制会尽量让标题具体,而非笼统;预期情况请参阅上文。
- 更改嵌入模型会重新嵌入所有内容。 两个模型生成的向量无法比较,而且
nodes_vec不记录每行数据的来源,因此sync会全部丢弃并重建,而不是对混合数据进行排序。发生这种情况时会有提示。节点不会受到影响,BM25 也会始终正常工作。 - 差异索引有范围限制,而且会有意舍弃部分内容。 首次同步会索引最近 200 个提交的补丁(后续同步只会遍历
cursor..HEAD);合并提交不会贡献补丁,因为其补丁仅以组合格式存在,而此解析器不会读取这种格式;此外还会跳过二进制文件、锁文件和构建输出,以免依赖版本更新淹没语料库。所有这些仍会记录为git_commit节点。超过limits.maxBodyChars的补丁会被截断,因此非常大型的更改末尾部分不会被索引。相关上限位于config.json的sources.diff下。 - 跨项目召回更偏重广度。 每个仓库的结果会按排名融合,因此即使某个项目的最佳匹配质量一般,也仍会贡献一个排名第 1 的条目;而不同列表中的排名第 1 条目权重相同。即使某个仓库对你的问题没有太多相关内容,添加它仍会让它的一些结果占用预算。信号、时效性和预算会对此加以控制;没有针对每个项目的质量权重。
- 项目注册表是索引,而非事实来源。 它可能指向已移动或已删除的数据库;这些情况会被报告并跳过,而不会被静默清理,因为驱动器未挂载并不代表项目已删除。
- 对话分块尚未经过评估。 在标题边界处分割较长的回复确实带来了可衡量的改善,但从未经过系统测试。
- 单个结果中,分块节点的同级节点数量上限未经调优。
conversation_turn和doc_section都会将一条回复或一个文件拆分为多个节点;打包后的结果中,最多只能同时出现其中 2 个。实际发现:针对“token”的查询,排名前 12 的结果中有 9 个是同一条分节很多的回复中的不同片段,挤掉了真正回答问题的节点。上限设为 2 是基于判断,而非经测量得出的最优值;与上文排名先验预算的设定依据相同。 - 先验预算的大小是基于判断的,而非经测量得出的最优值。 现在会联合限制先验项,而非逐项限制,这堵住了一个实际存在的 4× 缺口(参见排名部分);但 2× 预算本身从未使用带标签的相关性数据集进行调优,因为目前并没有这样的数据集。这是一个合理的常量,而非测量结果。经过测量的是其变化方向:针对本仓库自身记忆中的 4 个真实查询,改用联合上限后,有 3 个查询的答案所在章节排名上升(“为什么要在向量搜索之前使用 BM25”的原理章节从第 4 名升至第 1 名),且没有任何查询的正确首选结果被挤掉。
forget仅针对单个仓库,而非全局生效。 它的拒绝列表保存在其运行所针对的那个.nexusmem/memory.db中(以及该仓库中同一作用域下的过时先验身份;--prune-source也是如此)。如果某个值从多个仓库泄漏到 Shell 历史记录中,就需要在每个仓库中运行一次forget——所有已同步项目之间没有共享的全机器级拒绝列表。- 拒绝列表不会自动随克隆或还原而保留。
.nexusmem/按设计被 git 忽略,因此deny_list不会随git clone/git push一起传输;但 Git 历史本身是全可移植的,每次克隆都会复制,新运行的sync也会据此重新生成数据。队友的全新检出、新设备或还原的备份都不会有任何保护:被遗忘的值会在首次同步时重新出现。已于 2026-08-17 在实际环境中确认,这并非仅根据代码推断。forget --export <path>/forget --import <path>可解决此问题:导出会将有效条目写入纯文本 JSON 文件,你可以通过自行控制的渠道传输该文件(切勿使用 Git——该文件的敏感程度与其中保存的值完全相同);导入则会在新的检出中重新应用这些条目,并删除已重新同步的副本。此操作有意设计为手动执行,不会在每次sync时自动进行。
命令
init、sync、query <text>(添加 --as-of <date> 可进行双时间读取,见下文)、status(添加
--share 可生成适合粘贴到其他地方的纯文本摘要)、projects、mcp、forget <value>、
scrub-secrets(重新遮蔽 0.10.5 之前版本已存储的机密信息——数据库行、FTS
索引、嵌入向量和 shell hook 日志;默认试运行,使用 --yes 才会执行,并会先备份每个数据库;
--all-projects 涵盖所有已注册的仓库)、
stale(添加 --check-contradictions 可进行本地 SLM 内容检查,见上文)、mark-stale <nodeId> --supersedes <newNodeId>、review <nodeId> --verify|--reject(记录对单个节点的人工判定,见上文)、precheck(仅提供提示——警告暂存文件中存在尚未解决的过往失败或近期高频变更;除非使用 --strict,否则以 0 退出)、hook install|remove|status(PowerShell 退出代码 hook)、hook git install|remove|status(git 提交前 hook,会在每次提交前运行
precheck),以及 hook git-post install|remove|status(git 提交后 hook,会在每次提交后于后台运行完整的
sync,包括嵌入处理——以分离进程运行,因此不会让 git commit 本身等待;连续多次提交会通过
sync --auto 的锁合并为一次同步,而不会堆积)。agent install|remove|status 用于管理上文介绍的 Claude Code hook;agent recall 和 agent session-start 由这些 hook 运行,无需手动执行。
此外,还有八种试运行预览(scan-git、scan-diff、scan-shell、scan-docs、
scan-conversation、scan-session、scan-github、scan-structure),不会写入任何内容,只会打印摄取操作将会生成的结果——前七种会显示节点及其信号评分,scan-structure 会显示导入图的边。这是针对真实仓库调整评分并确定是否进行变更的建议方式。添加 --json 可将结果传递到其他地方。
每条命令都可以使用 -C <path> 指定另一个仓库。执行 sync 时,--conversation 和
--github 可在单次运行中启用相应的可选数据源,但不会持久保存设置;--no-embed 会跳过向量处理;--link-failures 会构建上文介绍的失败 → 修复链;--rebuild
会删除项目的节点并从头重新摄取。
sync --prune-source <name> 会删除整个采集器数据源(例如 shell:pwsh);forget <value>
则是更细粒度的补充操作——删除所有匹配某个精确字符串(或 --regex
模式)的节点,并且写入一条持续有效的拒绝列表条目,确保该值永远不会被重新摄取,即便之后执行
sync --rebuild,重新读取仅追加的 shell-hook 日志或扫描完整转录记录也不例外。每次删除都会留下仅包含哈希值的墓碑,绝不会保留被遗忘的内容。两者默认均为试运行;使用 --yes 确认执行。forget --list 会显示有效条目;forget --export <path> /
forget --import <path> 可将这些条目迁移到同一仓库的另一个检出副本(见上文的限制)。
此机制的存在原因见 docs/forget-mechanism.md。
跨项目回忆
query --all-projects 会搜索你运行过 NexusMem 的所有仓库,而不仅仅是当前仓库,并为每条结果标注其来源仓库:
$ nexusmem query --all-projects "why was the retry budget raised"
scope 2 project(s): NexusMem, uploader
- 2026-08-12 [observed] [uploader] fix: raise the retry budget after the S3 upload timeouts
- 2026-08-12 [observed] [uploader] retry.ts @ 8d0f98b — fix: raise the retry budget after the S3 upload timeouts
@@ -1 +1 @@
-export const RETRY_BUDGET = 3;
+export const RETRY_BUDGET = 5;
- 2026-08-09 [observed] [NexusMem] fix(git): retry a transient failure to spawn git
双时态读取
每个节点都有两个时钟:ts 是事件本身的时间(“当时发生了什么”),created_at
是存储实际记录该事件的时间(“当时存储中保存了什么”)——通常这两个问题的答案相同,
但如果同步延迟、几周后才补录事件,或队友的克隆一次性追上进度,情况就不同了。默认情况下,
query/search_memory 回答第一个问题;--as-of <date> 则切换到第二个:
nexusmem query "why does the ranker cap joint priors" --as-of 2026-08-10
只会考虑在该时刻或之前记录的节点,即使它们描述的事件更早也一样。没有与之对应的写入操作——
这是对 created_at 的读取时过滤,而不是快照,也无法查询后来已变更的值,因为节点只写入一次
(关于什么会改变节点的权重而非其记录,参见过时状态与来源)。
数据库仍按仓库分别存储——没有共享的全局存储,删除某个仓库的
.nexusmem/ 仍只会移除该仓库的记忆。其他仓库之所以能被找到,是因为 ~/.nexusmem/projects.json
中有一个简单索引,由 init 写入,并由每次 sync 刷新。nexusmem projects 会显示索引中的内容,
--prune 会移除数据库已不存在的条目。
跨仓库排名使用每个项目的倒数排名融合,而非原始 BM25,因为 BM25 的成本是根据各自的语料库计算的, 在包含 50 个节点和 50,000 个节点的数据库中代表的含义不同。此方案的取舍详见失效场景。
MCP search_memory 工具接受相同的开关:allProjects: true。
磁盘上的内容
<repo>/.nexusmem/
.gitignore '*' — the workspace ignores itself, so init never edits a file it doesn't own
config.json validated on read; a corrupt config fails loudly rather than silently
memory.db SQLite in WAL mode
~/.nexusmem/
projects.json which repositories exist, for cross-project recall; a corrupt one reads as empty
shell-history.jsonl the hook's log, if you installed it
NEXUSMEM_HOME 可覆盖用户范围的目录。
节点 ID 根据 sha256(projectId + kind + naturalKey) 进行内容寻址,因此运行两次 sync
不会产生重复项,即使游标丢失,摄取也能保持正确。项目标识在存在规范化源 URL 时取自该 URL,
否则回退到绝对路径,因此同一仓库的两个克隆会共享一个记忆命名空间。
删除 .nexusmem/ 不会造成任何无法通过 sync 重建的数据丢失。
状态
摄取、混合检索、预算受限的内容打包和 MCP 服务均已可用,并通过了 701 项测试,测试运行于 Linux 和 Windows, 覆盖 Node 22 和 24。
开发
npm install
npm run typecheck
npm test
npm run build
测试以行为验证为主,而非基于快照,其中有几项是针对特定已观察到故障的回归测试。tests/git-errors.test.ts
会注入一个伪造的 spawn,以测试 Windows 进程启动故障;这类故障无法按需触发。
欢迎提交 Bug 修复、测试覆盖和小型聚焦功能——工作流程请参阅
CONTRIBUTING.md,也可以浏览标记为
good first issue
的问题,选择范围明确、独立完整的内容开始贡献。
关于构建过程
这个项目最初是一项实验,旨在探索面向编码 Agent 的本地上下文记忆引擎是否可行,并使用 Claude Code 进行原型开发。 代码通过 AI 辅助工作流编写;架构、设计决策和规格说明均由人主导。
有必要坦率说明这一点,因为它应该影响你阅读代码的方式,而不是影响你是否信任它。我们真诚欢迎审计、修正和 PR; 提交历史也刻意详细记录了各种设计背后的原因,包括先前假设被证明有误的情况。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。