关于 limpet
limpet 是面向 AI 编码 Agent 的本地工程记忆 MCP 服务,用代码结构锚定决策、事实和失败经验,并在代码变更时标记记忆过期及原因。它使用单个 Rust 二进制文件和 SQLite,不进行网络调用。
主要功能
- 01记忆锚定代码符号
- 02代码变更时标记过期
- 03跨会话检索工程记忆
- 04变更影响与验证队列
- 05SQLite 存储及导入导出
适用场景
- 01跨编码会话保留项目决策
- 02确认代码变更是否使旧知识过期
- 03查看差异涉及的记忆和决策
- 04与团队共享项目记忆
MCP 相关
该项目通过标准 MCP 提供记忆检索、存储、映射、影响分析、验证队列及管理工具,作为面向编码 Agent 的记忆后端。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
🐚 limpet
轻巧的 shell,持久的记忆。
为 AI 编码 Agent 提供记忆,代码一变,记忆便会过时。 Agent 对代码库的所有了解(决策、已验证的事实、失败的尝试、注意事项)都会跨会话留存,并锚定到其描述的实际代码上;代码一有变动,记忆就会标记为 stale,并附上原因。一个 Rust 二进制文件,一个 SQLite 文件,标准 MCP,零网络调用。
锚点生命周期,实际输出:检索立即给出答案,编辑会使记忆标记为过时,还原后记忆便恢复正常
让 AI 编码助手失效的原因不是遗忘,而是记住了已经不再正确的内容。向量存储相信相似度,时间衰减工具相信日历,Markdown 笔记则永远相信自己。因此,Agent 若仍相信某个函数上个月的版本,并非帮不上忙,而是自信地给出错误信息。limpet 会将每条记忆锚定到其描述代码的规范化 AST 哈希:代码重命名或移动时,记忆会随之移动;代码被编辑时,记忆会显著标记为过时并附上原因;代码还原后,记忆便恢复正常。它的核心理念就是察觉自身过时,而且没有其他开源记忆层能做到这一点。
⏱️ 30 秒即可运行
curl -fsSL https://raw.githubusercontent.com/KSym04/limpet/main/install.sh | bash
(Windows:irm https://raw.githubusercontent.com/KSym04/limpet/main/install.ps1 | iex;完整安装选项见下文。)
重启 Claude Code,在任意项目中输入 /limpet,然后照常工作:
第 1 个会话:“扫描器的批次大小是 50,因为共享主机会终止耗时较长的请求” → 作为
decision保存,并锚定到使用它的函数第 30 个会话,开启全新上下文: 你: 为什么批次大小是 50?→ Agent: 一次
recall,约 350 个 token,即可得到完整答案。无需翻查文件,无需重新讲解你自己的代码库。你编辑了该函数 → 这条记忆在所有出现的位置都会变为
stale: body_edited。当内容已经过时,它绝不会假装仍然有效。
🎯 选择 limpet 的四个可验证理由
- 它知道自己何时错了。 记忆锚定到 AST 哈希,可跟随重命名和文件移动;代码真正被编辑时会标记为过时并附上原因,还原后则恢复正常。→ 锚点生命周期
- 它会展示保存的内容。 每次检索都会与其替代的文件读取进行成本比较:基准测试中 token 数减少 4.2 倍,并通过
limpet stats提供实时的项目级账本。→ 数据凭证 - 它能锚定整个代码库。 提供 11 种语法以支持符号级锚定;其他所有文件(模板、样式、配置)都可在文件级锚定。→ 索引整个代码库
- 它绝不隐瞒信息。 每条响应都包含一个如实说明:匹配数与返回数、哪些内容被省略及原因、索引的新鲜程度,以及过时内容的数量。基准测试门槛曾让 limpet 自己的功能因超出界限而被移除。
它不是向量数据库、代码搜索引擎或调用图预言机;它是一个紧扣代码、记住“为什么”的层,并会告诉你这个理由何时已不再成立。→ limpet 不是什么 · 记录惨痛教训的设计原则:PHILOSOPHY.md
🧠 为什么要做这个
代码索引器能回答“某个东西在哪里”。真正昂贵的知识并不在任何文件中:
- 为什么批量大小是 50
- 尝试过哪些重构、后来又回滚了,以及出了什么问题
- 哪些方法名称已固定,因为客户会调用它们
- 那个奇怪的 cron 作业实际上在防范什么
Agent 每次会话都会重新推导或重新询问这些问题,消耗大量令牌;更糟的是,它们可能会猜。limpet 让这些知识拥有工程师赋予知识的生命周期:
valid -> code changed -> stale (reason attached, confidence drops) -> re-verified or superseded
三个特性让它得以奏效,而其他工具无法兼具:有锚点(使用 AST 主体哈希,而不是行号或路径)、诚实(每个响应都带有信封;绝不会悄悄截断)、有证据(事实可以附带证明它的命令;锚点失效时,会一并返回该命令)。
👥 适合哪些人
| 你是 | limpet 能为你做什么 |
|---|---|
| 使用 AI Agent 的独立开发者 | 首次会话会花费数万个令牌来了解你的代码库;之后只需一次 recall。即使执行 /clear、压缩上下文或换新机器,记忆依然留存。 |
| 团队 | admin export 会生成 .limpet/memory.jsonl,供 Git 跟踪;队友拉取后即可导入。入职知识随代码仓库一起传递,而且与维基不同,代码变更后它会标记自身过时。affected 会显示某次 diff 刚刚危及了哪些有记录的决策。 |
| 模板繁多的技术栈(Rails、Laravel、Vue、任何 CMS 主题) | 每个文件都能设置锚点,因此“此布局锁定为 480px”可以锚定到实际样式表;有人编辑后,它就会过时。这正是仅基于符号的索引器无法实现的。 |
| 开源维护者 | intent/decision 记忆会在那个“修复”代码的 PR 合并前,回答“为什么这里有这段奇怪的代码”;episode 记忆则能防止第三位贡献者再次尝试那个已经失败过两次的重构。 |
| Agent 构建者 | 标准 MCP 背后的模型无关记忆后端:一个二进制文件、零网络、SQLite 数据可检查。无需向安全审查人员解释任何事情。 |
不适合的人: 临时脚本、只接触一次的代码仓库、不使用 AI Agent 的团队。只有在问题反复出现时,记忆才有价值。
🧰 六种工具
| 工具 | 功能 |
|---|---|
recall | 输入任务描述,输出按令牌预算控制的排序记忆包。过时和相互矛盾的条目始终会标记出来,绝不会隐藏。相关性相同时,已验证事实优先于未经验证的声明;没有 source 字段的条目属于未经验证的声明,而非证据。 |
remember | 存储一条记忆:fact、decision、episode、insight 或 intent。将其锚定到代码。附上证据即可将其标记为已验证。同一锚点上正文几乎相同的记忆会被拒绝(并指出可供替换的现有 id;force: true 可覆盖此限制);同一锚点上声称不同值的记忆会作为 possible_conflicts 返回,以便在写入时发现矛盾。private: true 会将记忆保留在本地(可在此处检索,但不会导出);origin 可确保种子流程中的写入具有幂等性。 |
map | 提供文件或符号的结构大纲,以及附加到它的每条记忆。对于符号目标,它还会返回 lineage(祖先、后代、调用方),并为每条边标记 unique/ambiguous/unresolved。在一个答案中呈现代码和知识。 |
affected | 我的未提交差异会影响哪些内容:符号、当前面临风险的记忆,以及约束正在编辑的代码的决策。 |
verify_queue | 锚定代码已更改的已验证事实,每条事实都附带最初用于验证它的确切命令。重新运行该命令,并将结果交给 admin {op:"reverify"}:验证队列不再是死胡同。 |
admin | 索引、状态、遗忘、归档 / 恢复、导出 / 导入(受保护)、账本 / ledger_reset(节省记录)、重新验证、整合。归档会将记忆收起而不删除:它会从检索结果中隐藏,同时持续跟踪其过时状态;恢复时保留其当前真实状态;导出时仍会包含该记忆(并加以标记),因此不会丢失任何隐藏内容。导出会报告 private_withheld,让调用方知道有多少记忆仍留在本地。重新验证会完成验证闭环:为过时的已验证事实提供新证据后,会重新盖上其摘要戳,将其锚点重新绑定到当前代码,恢复完整的已验证置信度,并将其设为活动状态;如果锚点无法解析,它会拒绝操作,而不会猜测。整合会列出值得提炼为一个条目的同锚点、高度重叠记忆簇(通过 remember + supersedes 链接);它不会自行合并任何内容。 |
每个响应都包含在诚实性信封中:
{
"data": [ ... ],
"meta": {
"freshness": { "indexed_at": "2026-07-03T10:12:44Z", "dirty": 0 },
"completeness": { "matched": 7, "returned": 3, "omitted_reason": "budget" },
"staleness": { "stale": 1, "contradicted": 0 }
}
}
检索到的条目还会指出正文中逐字出现的重要任务术语("matched": "sweep anchored",最多三个,按任务中的顺序排列),因此无需重新阅读条目,即可检查其出现的原因。如果正文中没有完整出现任何重要任务术语,则不会包含此字段:此时,条目是通过锚点邻近性、仅命中停用词,或词干匹配或部分文本匹配进入记忆包的。
⚓ 锚点生命周期
code change anchor resolution memory becomes
--------------------- ------------------------ -----------------------------
reformat / comments same normalized AST hash active (untouched)
rename symbol body found under new FQN active, anchor follows
move file body found in new file active, anchor follows
edit function body hash differs at FQN stale (body_edited), conf drops
delete symbol body found nowhere invalidated (kept as history)
duplicate bodies multiple matches stale (ambiguous_anchor)
edit anchored file file content hash differs stale (file_edited), conf drops
delete anchored file file row gone invalidated (kept as history)
lose SOME anchors others still resolve stale (anchor_lost), never killed
move a trivial body unique match, too small stale (low_entropy), never re-pointed
只有所有锚点都失效,多锚点记忆才会失效。若一个锚点丢失,但其他锚点仍可解析,记忆会降级为 stale:anchor_lost,确保剩余知识仍可使用。而且,remember 会在写入时直接拒绝无法解析的锚点,并明确报错:记忆绝不会在诞生时就已失效。
孪生符号(trait 实现、重载、访问器对)共享同一个 FQN,因此符号规格可以带上可选的 @<disamb> 后缀,以指定确切的那个符号;拼写须与源代码中的写法一致,但空白不作区分(f@(int a, String b) 和规范化后的存储形式算作同一种写法):Rust 的 trait 路径(固有实现用 impl)、Go 的接收者类型(包级函数用 func)、Ruby 的接收者(实例方法用 self.、<<self.、#),以及 C++、Java、C#、Python 和 JS/TS 的参数列表(也包括泛型参数数量和 JS/TS 的 static)。含糊不清的规格会被拒绝,并列出可精确输入的形式。
symbol: "go" → refused: matches t.T.go@A, t.T.go@B, t.T.go@impl
symbol: "go@A" → anchors to the A impl's body, and only that one
symbol: "go@impl" → anchors to the inherent impl, and only that one
symbol: "x@" → anchors to a twin that carries no discriminator at all
(a type sharing its name with a method, say)
跟随重命名需要证据支持:如果唯一匹配项的函数体过于简单(空函数、纯委托存根、几乎为空的文件),则不会将其视为跟随证据,并会显示为 stale:low_entropy,而不是悄悄把锚点重新指向一个相似的孪生符号;原始代码一旦恢复,记忆就会立即恢复正常。
恢复不会影响置信度:变为过期状态时,只扣减一次置信度惩罚,并将过期前的值一并存储,因此当原因消失时(切换分支、还原更改、弹出 stash),惩罚会全额返还。衰减按原因计算,而非按解析次数计算;短暂消失不会让记忆付出任何代价。
过期状态也具有对称性:还原代码(回滚实验、执行 git checkout)后,下次调用时记忆就会恢复为 active,因为锚点哈希再次匹配。对于已撤销的更改,无需再进行验证仪式。失效也是如此:切换分支、执行 git stash 或 rebase 期间的状态导致文件暂时消失,都不会让记忆就此终结;代码恢复且锚点可解析时,记忆就会恢复。唯一的最终状态是 superseded,它记录的是人有意作出的决定,而非文件系统的变动。
矛盾会通过显式链接表示:新记忆与旧记忆矛盾时,两者都会保留并显示冲突标记,直到其中一条 supersedes 另一条。历史绝不会被悄然覆盖。
🔬 过期模型对比
每种记忆工具最终都会面对同一个问题:这条信息现在还成立吗?市面上主要有四种答案,但它们都无法发现静默发生的代码更改。
| 过期模型 | 常见于 | 无法发现什么 |
|---|---|---|
| 基于时间的衰减:记忆按计划逐渐变为“需要审核” | 采用衰减策略的记忆工具 | 时间长短不代表真假。日历无法指出五分钟前哪次重构破坏了哪条记忆,还会不断提醒那些从未改变的稳定知识。 |
| 由大模型判断失效:模型判断新输入是否与旧事实矛盾 | 对话记忆层 | 只有出现新内容时才会触发。代码若在会话间发生漂移,就不会触发任何操作,因此记忆会自信地保持错误。 |
| 嵌入相似度 | 向量存储和 RAG 管道 | 余弦相似度会将矛盾内容和近似重复内容视为相同。 |
| 手动整理:“定期检查你的学习成果” | 审核机器人知识库、维基 | 人类按人的节奏清理文本,且总是在造成损害之后。 |
| 确定性 AST 锚点(limpet) | 过期状态是根据代码本身计算得出的事实:具体哪条记忆、具体什么原因(body_edited、anchor_lost、low_entropy),并在编辑后的下次工具调用时得出。还原代码即可恢复。 |
前四种方式都靠猜测让知识过期。limpet 会进行检查。
🪙 收据:实测节省的 Token
持久化记忆也远比重新推导便宜。节省来自以下几个方面:
- 答案以结论而非源文件的形式传递。 一条记忆是提炼后的 30 个 Token 答案,而得出它所依据的文件有数千个 Token。每次会话都通过阅读代码重新推导已知事实,都要付出全部代价。每个问题只需付费检索一次。
- 最浪费的开销,是注定无法成功的探索。 批次大小为什么是 50、哪个重构被回滚、哪个 API 已冻结:这些信息不在任何文件里。没有记忆时,Agent 会搜索、阅读,最后仍然一无所知,因此花掉的 Token 毫无价值。有了记忆,这些问题反而是最便宜的。
- 响应会按预算打包并剔除噪声。
recall接受 Token 预算,按相关性从高到低打包,丢弃相关性较低的内容,并报告省略了什么。花费不会超过你设定的预算,绝不会因为匹配到的内容多就超支。 - 上下文丢失后无需重新教学。 压缩、
/clear、开启新会话:知识会保存在上下文窗口之外,并以检索成本恢复,而不是重新推导的成本。
通过可复现的基准测试进行测量:在一个包含 9 个文件、符合实际情况的 fixture 服务中预置 12 条记忆,然后提出 Agent 通常每次会话都要重新回答的 10 个问题:
question files+grep recall ratio in code?
----------------------------------------------------------------------------------------------------
why is the batch size 50 and why is there a queue at all 1929 368 5.2x no (answer only in memory)
why does the scanner skip draft products, is that a bug 1630 316 5.2x no (answer only in memory)
how is the health score computed 1630 364 4.5x yes
why semicolon delimiter and BOM in the csv export 1327 373 3.6x no (answer only in memory)
where do report files get written and why 1327 325 4.1x no (answer only in memory)
how long are download tokens valid 1023 174 5.9x yes
has anyone tried streaming the csv export 1327 318 4.2x no (answer only in memory)
can I rename check_product in the scanner 1630 313 5.2x no (answer only in memory)
what does the nightly cron actually exist for 803 330 2.4x no (answer only in memory)
how often does the dashboard poll progress and can I lower it 1072 345 3.1x no (answer only in memory)
----------------------------------------------------------------------------------------------------
TOTAL 13698 3226 4.2x
整个基准测试的 Token 数减少了 4.2 倍(节省 76%)。 你可以亲自复现:
cargo build --release
python3 bench/token_savings.py
这个数字不只是基准测试结果:limpet 也会为你自己出具收据。每次检索都会按照相同方法,与读取文件的反事实成本进行比较。在运行中的服务内,admin {op:"ledger"} 会报告当前会话数据(节省的 Token 数、避免的读取次数、检索次数)和累计数据(同样的指标,另加不重复查询数);limpet stats CLI 和 UI 标题只报告累计数据,因为它们都不执行检索,因此其中的会话数据只会永远是零。负节省会如实显示,不会被截断为零;没有锚点的记忆按零基准计算,因此这个数字是保守下限,而非营销宣传。
Claude Code 状态栏中的实时收据:活跃记忆数和累计节省的 Token 数,直接从存储中读取
方法说明如下,确保这个数字可以核验,而不只是盲目相信:
- “不使用 limpet”的成本按包含答案的最少文件集合计算,另加搜索往返的固定 300 个 Token。实际 Agent 读取的内容会多于最少集合,因此实际节省更多。
- 两边均采用相同方法,按 ceil(bytes/4) 估算 Token 数。
- 上面 10 个问题中,有 8 个标记为“否”:无论付出多少 Token,都无法在任何文件中找到答案(决策、历史、内部经验)。读取文件能让你看到代码,却无法得到答案。我们仍按读取文件的成本向 limpet 收取全部费用,而不声称节省无限大。
- 该脚本是回归检查:如果节省低于 4 倍,就会以非零状态码退出。
- Fixture 文件长 58 至 179 行。实际源文件通常大好几倍;随着文件变大,“不使用 limpet”一侧的成本也会增加,而检索响应的成本不会增加。
新鲜度也有据可查。bench/lag_bench.py 会构建包含 2,000、10,000 和 50,000 个文件的合成代码库,分别以 1、32 和 100 个为一批,修改带锚点的符号,并测量在每处修改都被标记之前需要经过多少次扫描调用:所有规模和批次宽度下都只需一次调用,因为在扫描预算内,包含锚点的文件会优先重新索引。同一次运行还会测量安静代码库(没有任何更改)中扫描的成本:2,000 个文件时每次调用耗时 156 ms,10,000 个文件时为 372 ms,50,000 个文件时为 1,206 ms。最后这个数字说明了为什么 FS 事件监视器是一个经过基准测试把关的待办项,而非承诺:过时延迟不需要它,但在 50k 个文件时,扫描成本确实需要考虑。
🗺️ 可视化记忆
limpet ui --port 9748
UI 是一条独立命令,与 MCP 服务分开。执行 limpet install 后,Claude Code 会自动为你启动 limpet serve(stdio);在你自行启动 limpet ui 之前,不会有任何程序监听端口。如果 http://127.0.0.1:9748 拒绝连接,并不代表 MCP 服务出了问题,只是 UI 尚未运行。
limpet ui:limpet 存储库呈现为一颗大脑;绿色记忆值得信赖,琥珀色记忆因代码变更而过时,灰色记忆已被取代,方块表示它们锚定的符号
打开 http://127.0.0.1:9748,即可实时查看以大脑形态呈现的知识图谱:记忆分布在皮层带中,它们锚定的文件和符号位于白质中,每个锚点都向内延伸。记忆的大小按置信度缩放,颜色按健康状态区分(绿色表示有效,琥珀色表示过时,红色表示已失效,灰色表示已被取代);已验证的记忆带有醒目的环形标记,并会绘制矛盾关系和取代关系的边。在全部项目视图中,存储库会分布在九个脑叶中,按大小从大到小排列,并标注项目名称(超过九个存储库时会共用脑叶),让你一眼看清整体记忆的形态。轮廓会根据可见内容缩放;当小型项目无法填满轮廓时,页脚会注明这一点。布局具有确定性(相同数据始终绘制出相同图形),图谱稳定后,你查看时不会产生逐帧计算。“需要关注”筛选器会准确显示哪些内容已过时及其原因,并可一键执行重新验证命令。其他工具展示代码结构,而 limpet 展示 Agent 掌握的信息及其是否仍然正确。它由同一个独立二进制文件提供服务,且仅绑定到 127.0.0.1。
📦 安装
一行命令。 安装程序会下载适用于你平台的最新发行版二进制文件,验证其 sha256,完成安装,并将其注册到 Claude Code:
macOS / Linux:
curl -fsSL https://raw.githubusercontent.com/KSym04/limpet/main/install.sh | bash
Windows(PowerShell):
irm https://raw.githubusercontent.com/KSym04/limpet/main/install.ps1 | iex
然后重启 Claude Code,并在任意项目中输入 /limpet。设置就全部完成了。(LIMPET_INSTALL_DIR 可覆盖安装位置;LIMPET_VERSION 可指定发行版标签。)
不想将脚本通过管道传入 shell?以下各步骤与手动操作完全等效。预编译二进制文件适用于 Apple Silicon macOS、x86_64 Linux 和 x86_64 Windows,可在最新发行版中获取;每个资源文件都发布了 sha256,请先验证再信任该二进制文件。
macOS(Apple Silicon)
curl -fsSLO https://github.com/KSym04/limpet/releases/latest/download/limpet-aarch64-apple-darwin.tar.gz
curl -fsSLO https://github.com/KSym04/limpet/releases/latest/download/limpet-aarch64-apple-darwin.tar.gz.sha256
shasum -a 256 -c limpet-aarch64-apple-darwin.tar.gz.sha256
tar xzf limpet-aarch64-apple-darwin.tar.gz # extracts a single `limpet` binary
sudo install -m 755 limpet /usr/local/bin/limpet
limpet install
macOS 可能会隔离下载的二进制文件;如果无法运行,请使用 xattr -d com.apple.quarantine /usr/local/bin/limpet 清除隔离标记。
Linux(x86_64)
curl -fsSLO https://github.com/KSym04/limpet/releases/latest/download/limpet-x86_64-unknown-linux-gnu.tar.gz
curl -fsSLO https://github.com/KSym04/limpet/releases/latest/download/limpet-x86_64-unknown-linux-gnu.tar.gz.sha256
sha256sum -c limpet-x86_64-unknown-linux-gnu.tar.gz.sha256
tar xzf limpet-x86_64-unknown-linux-gnu.tar.gz # extracts a single `limpet` binary
sudo install -m 755 limpet /usr/local/bin/limpet # or ~/.local/bin if it is on your PATH
limpet install
Windows(x86_64,PowerShell)
Invoke-WebRequest https://github.com/KSym04/limpet/releases/latest/download/limpet-x86_64-pc-windows-msvc.zip -OutFile limpet.zip
Invoke-WebRequest https://github.com/KSym04/limpet/releases/latest/download/limpet-x86_64-pc-windows-msvc.zip.sha256 -OutFile limpet.zip.sha256
# compare the two hashes; they must match
(Get-FileHash limpet.zip -Algorithm SHA256).Hash
Get-Content limpet.zip.sha256
Expand-Archive limpet.zip -DestinationPath "$env:LOCALAPPDATA\limpet" # extracts limpet.exe
# add the folder to PATH once, then restart the terminal
[Environment]::SetEnvironmentVariable('Path', $env:Path + ";$env:LOCALAPPDATA\limpet", 'User')
limpet install
使用 Rust(rustup.rs)安装,适用于 Intel Mac、ARM Linux 以及没有预编译二进制文件的平台:
cargo install limpet
limpet install
重启 Claude Code。完成。 (limpet install --dry-run 会先预览准确的配置变更;limpet uninstall 可撤销这些变更。)
之后更新,运行 limpet update:它会获取适用于你平台的最新发行版二进制文件,根据已发布的 sha256 验证,并以原子方式替换正在运行的可执行文件。limpet update --check 会报告是否有新版本,但不会安装。只有这条命令会访问网络。之后请重启 Claude Code,让 MCP 服务重新加载新二进制文件。
🚀 日常使用
在任意项目中运行 /limpet,即可为代码编制索引、回忆已有信息(并标记过时条目),同时将会话切换到记忆优先模式。之后,Agent 会在学习过程中随时存储所学内容;上文的30 秒上手就是完整的工作流程。
日常命令:
| 命令 | 作用 |
|---|---|
/limpet | 为当前会话建立索引、回忆并启用记忆优先模式 |
/limpet status | 显示计数及任何需要关注的事项 |
/limpet review | 使用已存储的证明命令重新验证过时事实 |
/limpet export | 将 .limpet/memory.jsonl 写入文件,以便提交并与团队共享 |
limpet stats | Token 节省情况凭证:累计总量,包含计算方法(会话数据来自运行中的服务内的 admin {op:"ledger"}) |
limpet doctor | 一屏式设置诊断;安装和更新后也会自动运行 |
limpet ui | 在 http://127.0.0.1:9748 查看知识图谱,在一个视图中显示所有项目 |
limpet statusline | 状态栏片段(记忆 + 节省的 Token),只读且即时显示 |
limpet hook | 为 Claude Code hook 提供一行式 SessionStart 摘要,只读 |
limpet update | 自行更新到最新版本,并验证校验和(唯一会联网的命令) |
limpet demo | 在临时仓库中演示锚定生命周期(有效 → 过时 → 修复),并自动验证 |
limpet seed <file> | 将现有的 MEMORY.md / CLAUDE.md 导入锚定记忆;重复运行不会产生任何操作 |
数据存储在 ~/.local/share/limpet/ 下,每个仓库对应一个 SQLite 存储库。队友拉取 JSONL 后运行 limpet import(使用 --path <repo-relative file> 可导入其他导出文件)。
保留你的 MEMORY.md。 limpet seed MEMORY.md 会将纯文本笔记文件拆分为独立记忆:项目符号和段落会成为条目,提及仓库文件的内容块会锚定到该文件(因此文件更改时记忆会过时),所有内容都会以 mined 状态导入(在重新确认之前,其信任度低于已验证事实)。对未更改的文件重复运行不会产生任何操作。改写过的笔记则不同:旧措辞已经存储,因此写入路径上的去重会拒绝新措辞,并在报告中说明;使用 --force 重新运行,也会存储新措辞,然后从 Agent 端将旧措辞标记为已取代。
在任何平台上使用状态栏。 limpet statusline --root <project dir> 会输出 shell 片段(| 🐚 13 · ↑32k tokens saved;如果 UI 正在运行,计数会链接到项目图谱),否则不输出任何内容:它以严格只读的方式打开存储库,绝不写入,并且始终以 0 退出,因此可以安全地放在提示词中。由于渲染逻辑位于二进制文件中,同一条命令既适用于 macOS/Linux 上的 bash 状态栏,也适用于 Windows 上的 PowerShell 或 cmd 状态栏,无需 sqlite3 CLI,也无需 bash:
# inside a Claude Code statusline.ps1
$limpetSeg = & limpet statusline --root $projectDir
最简单的设置方式是让 Claude Code 直接调用该二进制文件作为整个状态栏;将以下内容添加到 ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "limpet statusline --root \"$CLAUDE_PROJECT_DIR\""
}
}
已经有自定义状态栏?将该二进制文件的输出追加为一个片段,但不要自行使用 sqlite3 查询 store.db。每个仓库的存储键方案会随版本变化(在 v0.9.0 中,它从路径哈希改为可移植的 git 远程身份),因此手动编写的查询会悄无声息地失效,状态栏片段也会随之消失。只有 limpet statusline 会跟踪当前方案。运行 limpet doctor 可检查状态栏的接入方式:如果它委托给二进制文件,会报告 ok;如果它手动查询存储库且可能随版本变化而失效,会报告 warn;如果尚未接入,则会给出需要添加的确切行。
使用 /limpet statusline 关闭该功能(写入 ~/.claude/.limpet-statusline-off;该命令会遵循此标记)。
在会话开始时自动回忆。 如果没有 hook,记忆优先行为就取决于 Agent 是否记得输入 /limpet。配置 SessionStart hook 后,每个已建立索引的项目在开启新会话时,都会向上下文注入一行摘要(“此项目有 limpet 记忆:13 条有效记忆,2 条过时记忆……”),Agent 会先回忆再读取内容。将以下内容添加到 ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{ "type": "command", "command": "PATH=\"$HOME/.local/bin:$HOME/.cargo/bin:$PATH\" limpet hook" }
]
}
]
}
}
当项目没有存储时,limpet hook 不会输出任何内容(索引仓库之外的会话会保持干净),会严格以只读方式打开存储,并且始终以 0 退出。显式指定 PATH 前缀很重要:钩子在非登录 Shell 中运行,通常无法找到 ~/.local/bin 和 ~/.cargo/bin,而二进制文件悄然缺失正是此命令要确保绝不会暴露的那类故障。在 Windows 上,使用 %USERPROFILE%\AppData\Local\Programs\limpet\limpet.exe hook。
为项目建立初始记忆
/limpet scan 会根据仓库中已有的内容,为其记忆打好基础。light 模式(默认)会采集合并提交、标签和 README;deep 还会采集所有文档、长正文提交,以及助手的项目记忆目录。预检召回会筛出存储已能回答的候选项,因此重复运行只会补充缺失项。采集过程会在子 Agent 中运行,避免原始 Git 输出和文档转储进入主上下文;返回的只有经过整理的候选项表格。写入任何内容之前,你需要分两轮批准候选项:先将高置信度条目作为一个整体批准(有异议时再单独拒绝),再逐一处理边界条目。每条获批记忆都会标记 origin,以便未来扫描时识别重复项;来自私有来源的条目则会标记 private: true,确保永远不会被导出。
🌳 索引整个仓库,但刻意保持精简
仓库中的每个文件都会被索引,并可用作锚点。 对于已提供语法解析器的文件(PHP、JavaScript、TypeScript、Python、Rust、C/C++、Go、Java、Ruby、C#、Bash),会完整提取符号:函数、类、导入、标记为 syntactic 的基于名称的调用引用,以及通过 map 谱系展示的继承边(extends、implements、impl_trait、embeds、mixin)。其他所有文件(.twig、.scss、.vue、.blade.php、.erb、.md、.yml、配置文件及其他文件)都会生成带内容哈希的文件级节点,因此记忆可以锚定到这些文件,并在文件发生变化的瞬间标记为 stale:file_edited。在模板繁多的技术栈中,真正值得记住的知识往往就在这些文件里。
旧版编码会得到妥善处理:如果与语法解析器匹配的文件不是有效的 UTF-8(例如旧版 C++ 引擎中的 CP949 或 UTF-16 源文件),它会保留文件级锚点,而不是从索引中消失。语法解析器只能提升文件的索引能力,绝不会降低其可锚定性。
遍历过程会有意跳过以下内容:.gitignore 中的所有内容、可选的 .limpetignore 中的所有内容(采用 gitignore 语法,即使不在 git 仓库中也能生效),以及内置的一组几乎总是生成或供应商提供的目录(node_modules、vendor、target、dist、build、__pycache__、.venv/venv、Python 缓存、.next/.nuxt/.svelte-kit、Pods、.gradle、bower_components、coverage、.terraform)、隐藏的杂项文件、*.min.* 资源,以及超过 8MB 的文件。超过 512KB 的源文件会按文件级索引,但不会解析符号:此上限可防止 tree-sitter 处理生成的资源包,而庞大的手写编译单元也会继续保持可锚定,不会消失。这些限制可防止在没有 .gitignore 的情况下,大型供应商依赖目录占满 CPU;如需排除其他内容,请使用 .limpetignore。
这里没有 LSP,没有类型推断,也不声称能提供可发布的调用图:索引的目的是为记忆提供锚点、失效检测和局部召回能力。所有已提供的语法解析器都在测试套件中有对应样例;只有能够测试的语言才会添加,而不是为了凑数。
新鲜度模型:每次工具调用都会执行有界增量扫描(借助 tree-sitter,已更改文件可在毫秒内重新解析)。查询不会因索引而阻塞;任何仍处于脏状态的内容都会列在摘要中。
⚙️ 每个仓库的配置(.limpet.json)
可在仓库根目录放置可选的 .limpet.json,用于调整两项设置。它是一个经过大小限制的普通查找表,会根据随附的语法规则进行校验;如果文件格式错误,显式执行 index 时会明确报错,而不会静默忽略。
{
"extensions": { "inc": "cpp", "module": "php" },
"auto_import": true
}
extensions将文件名后缀映射到随附语法规则之一(php、js、ts、py、rust、cpp、go、java、rb、cs、bash),这样内置表中未涵盖的后缀也能为模板密集型和遗留技术栈完整提取符号。匹配时优先选择最长后缀,因此特定的blade.php会覆盖通用的php。auto_import(默认为true)会在首次索引时,从已提交的.limpet/memory.jsonl为全新存储初始化数据,因此队友克隆仓库后,无需额外操作就能获得共享记忆。此操作仅会在全新存储上执行一次,并通过与limpet import相同的防护流程运行。
可移植的身份标识。 如果仓库有 git origin 远程地址,其存储会以该地址为键;否则使用规范路径。迁移工作副本或重新克隆后,记忆仍会随之保留;不同仓库也不再会意外共用同一存储。现有存储会在首次打开时自动迁移,且存储绝不会被错误认领:含义不明确的旧存储会保留原样,不会重新分配。
🔒 安全性
- 默认在本地运行。 索引、召回、记忆和 UI 始终不会发起网络调用:没有遥测、API 密钥或云服务。唯一例外是由你明确调用的
limpet update;它会通过 HTTPS 获取经过校验和验证的发布二进制文件,且只发送limpet/<version>User-Agent。UI 绑定到 127.0.0.1,且仅提供一个内嵌页面,只允许 GET 请求。 - 绝不持久化机密信息。
remember会扫描所有正文和证据输出,并拒绝存储任何看起来像凭据的内容(云访问密钥、服务提供商令牌、PEM 私钥块、JWT),因此机密信息无法进入本地存储或共享的.limpet/memory.jsonl。 - 私有记忆仅保留在本地。 使用
private: true存储的记忆会在项目内正常召回,但绝不会包含在export输出中,因此敏感上下文无法进入共享的.limpet/memory.jsonl。 - 不进行 Shell 插值。 外部命令(仅限 git)通过参数数组运行;没有任何字符串会传递给 Shell。
- 路径校验。 通过 MCP 接收的每个文件路径都会根据仓库根目录进行检查;绝对路径和路径遍历都会在统一的拦截点被拒绝。
- 仅使用参数化 SQL。 代码库中的查询不会拼接用户输入。
- 损坏的输入不会中断服务。 JSON-RPC 循环会针对解析错误和处理程序 panic 返回 JSON-RPC 错误,并继续提供服务。
install只会编辑自身的mcpServers.limpet条目,并拒绝修改无法识别的配置;uninstall会准确撤销这些更改。- 导入会经过防护流程。 从队友处获取的
.limpet/memory.jsonl是不可信输入,因此import会执行与remember相同的规则:拒绝机密信息(即使来自协作者,也绝不会进入存储)、限制正文大小、钳制置信度、防止未来日期的条目污染合并结果,并根据本地代码重新解析导入的锚点哈希,避免伪造哈希冒充新鲜内容。系统会统计被拒绝的行数,绝不会静默应用。 - 配置损坏?运行
limpet doctor。 它会检查二进制文件、Claude Code 注册信息(包括二进制文件迁移后不匹配的情况)、技能文件、存储、存储版本标记和索引,并逐行输出 ok/FAIL。它会在install和update后自动运行。
🚫 limpet 不是什么
- 不是通用 AI 记忆存储、向量数据库或 RAG 流水线。没有嵌入,也没有相似度猜测:锚点是确定性的 AST 哈希,陈旧与否是事实,而非评分。不了解代码的记忆存储永远不会察觉代码已经变化;察觉变化正是 limpet 的全部出发点。
- 不是代码搜索引擎。你的 Agent 已经有 grep。
- 不是调用图预言机。调用边是句法层面的,并且会明确标注。
- 不是云端记忆平台。无需账户、同步或服务。
- 检索质量取决于写入的内容:简短、具体且有锚点的记忆易于召回,而工具架构会引导 Agent 精准地做到这一点。
🧭 路线图
请参阅 ROADMAP.md,了解已发布的内容(可移植的仓库身份、状态行诊断工具、结构化谱系图、包含 11 种语言的语法第 2 波、0.13 新鲜度检查、0.14 真实性层:将验证作为排名信号、显示矛盾并在写入时拒绝重复项、0.15 大规模新鲜度第 2 部分:通过 @ 寻址同名项来消除 FQN 歧义、槽位优先的锚点阶梯、召回项中的匹配词,以及 lag-bench 观察器判定;还有 0.16 精炼循环:admin 重新验证后,将再次证明有效的记忆恢复为活动状态,其背后的架构 v8 置信度返还、只读整合候选项,以及在 macOS/Linux 上针对陈旧运行中服务镜像的诊断建议;以及 0.16.1 审计后续改进:以确定的线性时间检测标点边界处的机密信息、对 session 台账数据进行计数而非推断,并确保未提供任何内容的界面不会发布该数据;以及针对 clippy、依赖项公告和声明的 MSRV 设置 CI 门禁),了解此版本包含的内容(v1.0 稳定性契约的代码部分,已在 STABILITY.md 中强制执行并记录;重新设计为大脑样式的可视化记忆页面;以及 limpet ui 修复,避免每个浏览器请求都等待完 5 s 的读取超时),以及接下来的计划(待契约经过充分验证后发布 1.0 标签)。所有内容均遵循一条规则:只有能生成凭据(limpet stats 台账或基准测试结果)或纳入诚实性边界的功能才会发布。
⚖️ 使用与许可
limpet 是辅助判断的工具,不能取代判断。其新鲜度和置信度信号是根据代码结构计算出的启发式指标,并非证明: “活动”记忆是 limpet 对存储知识仍然有效的最佳证据,但绝不保证其正确或完整。对于任何不容出错的内容,在依赖它之前都应进行验证,尤其是涉及安全或生产环境变更时。整个设计理念是揭示不确定性,而非将其隐藏(PHILOSOPHY.md),但只有在你留意它提供的标记时,这种理念才能保护你。
本软件按“原样”提供,不附带任何形式的保证;作者对软件的使用不承担任何责任,具体规定见下方许可。
欢迎贡献,并且贡献内容采用与项目相同的 MIT 许可(输入 = 输出):提交拉取请求即表示你同意按照这些条款分发你的贡献。请参阅 CONTRIBUTING.md。
📄 许可证
MIT。请参阅 LICENSE。MIT 许可声明明确包含免责声明和责任限制条款;这些条款是对用户和作者具有法律效力的保护。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。