KSym04/limpet
GitHub: KSym04/limpet
limpet 是一个基于 AST 锚定的 AI 编程 agent 持久记忆层,让跨会话知识在代码演进时自动检测陈旧并保持可信。
Stars: 2 | Forks: 0
# 🐚 limpet
**小巧的 shell,持久的记忆。**
[](https://github.com/KSym04/limpet/actions/workflows/ci.yml)
[](LICENSE)
[](https://rustup.rs)
[](https://github.com/KSym04/limpet/actions/workflows/ci.yml)
[](SECURITY.md)
**为 AI 编程 agent 提供持久的工程记忆。** AI 在不同会话之间会忘记一切。limpet 能记住你的 agent 所学到的关于代码库的知识,并且知道这些知识何时不再成立。
` 会打印 shell 片段(`| 🐚 13 · ↑32k tokens saved`,当 UI 运行时,计数会超链接到该项目的图谱),或者什么都不打印——它以严格的只读方式打开存储,从不写入,且总是以状态码 0 退出,因此可以安全地驻留在提示符中。因为渲染逻辑存在于二进制文件中,同一行代码既可以在 macOS/Linux 的 bash 状态栏中工作,也可以在 Windows 的 PowerShell 或 cmd 状态栏中工作——不需要 sqlite3 CLI,也不需要 bash:
```
# 在 Claude Code statusline.ps1 内部
$limpetSeg = & limpet statusline --root $projectDir
```
使用 `/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 前缀很重要:hook 运行在非登录 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++)可获取完整的符号提取:函数、类、导入以及标记为 `syntactic` 的基于名称的调用引用。其他所有文件(`.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`、隐藏的垃圾文件、`*.min.*` 资产以及超过 8MB 的文件。超过 512KB 的源文件会在文件级别被索引,但绝不会为了提取符号而解析:此上限是为了保护 tree-sitter 免受自动生成的捆绑包的影响,并且让一个巨大的手写翻译单元依然可锚定,而不是消失。正是这些边界条件,才使得一个没有 `.gitignore` 的庞大第三方依赖树不会占满你的 CPU;你可以使用 `.limpetignore` 排除其他任何内容。
这里没有 LSP,没有类型推断,也没有声称可以发布调用图:索引的存在是为了给记忆提供锚点、失效机制和召回局部性。每一个发布的语法在测试套件中都有 fixture 覆盖;语言的加入取决于它们是否能被测试,而不是为了凑数。
新鲜度模型:每一次工具调用都会运行一个有界的增量扫描(通过 tree-sitter,更改后的文件能在几毫秒内重新解析)。查询永远不会因为索引而阻塞;任何仍然处于脏状态的内容都会列在信封中。
## 🔒 安全态势
- **默认本地化。** 索引、召回、记忆和 UI 绝不进行网络调用:没有遥测,没有 API 密钥,没有云。唯一的例外是你显式调用的 `limpet update`;它通过 HTTPS 获取一个经过校验和验证的发布二进制文件,并且除了一个 `limpet/` 的 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 注册情况(包括移动过的二进制文件不匹配的情况)、skill 文件、存储、其戳和索引,逐行打印 ok/FAIL。它会在 `install` 和 `update` 之后自动运行。
## 🚫 limpet 不是什么
- 不是通用的 AI 记忆存储、向量数据库或 RAG 流水线。没有嵌入,没有相似度猜测:锚点是确定性的 AST 哈希,陈旧性是事实,而不是评分。不感知代码的记忆存储永远不会注意到代码何时向前演进;而察觉到这一点正是 limpet 的全部前提。
- 不是代码搜索引擎。你的 agent 已经有 grep 了。
- 不是调用图预言机。调用边是句法层面的,并已如此标记。
- 不是云记忆平台。没有账号,没有同步,没有服务器。
- 检索质量取决于写入的内容:简短、具体、有锚点的记忆召回效果更好,而工具模式会引导 agent 恰好做到这一点。
## 🧭 路线图
完整计划请参见 [ROADMAP.md](ROADMAP.md) —— 基于符号的片段检索、避免返工指标、可移植的仓库身份、第二波语法以及 1.0 稳定性契约。有一条规则支配着这一切:一个功能只有在它能为收据(`limpet stats`、基准测试、避免返工)或诚实信封提供数据时才会发布。
## ⚖️ 依赖声明与许可
limpet 是判断的辅助工具,而非替代品。它的新鲜度和
置信度信号是根据代码结构计算出的启发式指标,而非证明:
一条“active”的记忆是 limpet 提供的表明存储知识仍然成立的最佳证据,
绝不是它正确或完整的保证。在依赖它之前,请验证任何你不想弄错的事情,
尤其是在安全敏感或生产环境的更改中。整体设计理念是
揭示不确定性而非掩盖它([PHILOSOPHY.md](PHILOSOPHY.md)),
而只有当你阅读它给你的标志时,这才能保护你。
本软件按“原样”提供,不附带任何形式的保证,作者对其
使用不承担任何责任,如下面的许可协议所述。
欢迎做出贡献,并按照与项目相同的 MIT 许可协议接受(入站 = 出站):
通过提交 pull 请求,即表示你同意你的贡献可以按照这些条款进行分发。请参阅
[CONTRIBUTING.md](CONTRIBUTING.md)。
## 📄 许可证
MIT。请参阅 [LICENSE](LICENSE)。MIT 许可声明包含明确的免责声明及
责任限制;这些条款是对用户和作者双方都具有法律效力的保护。
limpet ui: green memories are trustworthy, amber went stale when their code changed, squares are the symbols they clamp onto
the receipt, live in a Claude Code statusline: active memories and lifetime tokens saved, read straight from the store
标签:AI编程助手, MCP, Rust, SOC Prime, SQLite, 上下文管理, 可视化界面, 开发工具, 网络流量审计, 记忆系统, 通知系统