跳到正文

mindwalk

一个本地运行的 Go 程序:读取 Claude Code、Codex 与 pi 的会话日志,把它们当作光,在一张确定性的代码库 3D 地图上重放——agent 搜索、读取、编辑过的文件发光,其余部分留在暗处。

Screenshot of mindwalk
编辑截图, 30 Sep 2026mindwalk ↗

这是什么

一个本地优先的编码 agent 会话可视化工具,后端是 Go,前端是 React 与 Three.js。它读取 Claude Code、Codex 与 pi 的会话日志,把它们归一化成一条有序的「文件触碰事件」流,再把要观察的代码库构建成一张确定性的 3D 地图,让会话像光一样在地图上回放:agent 搜索、读取、编辑过的文件按触碰的深度与频次发光,其余部分留在暗处。会话碰过但已不在仓库里的文件,以线框幽灵的形式留着;播放台下方是一条把「观察」与「改动」分开的直方图。会话若启动过子 agent,每条子会话轨迹都能用同一张地图单独回放。唯一离开本机的功能是可选评估:它把会话摘要送进用户自己的 claude 或 codex CLI,最多两次封闭调用。报告按会话缓存,而任何结论都不由模型拍板。

谁做的Ricko Yu,账号名 cosmtrek,62 次提交里有 53 次是他用 cosmtrek@gmail.com 提交的,第一次在 2026-07-09,最后一次在 2026-08-10。其余九次来自另外四个账号:yearth 六次,jinwik、tcsenpai、elf-pavlik 各一次。34 次提交带共同作者尾注,其中 33 条写的是 Claude 模型——29 条写「Claude Fable 5」,4 条写「Claude Opus 4.8」。

它是怎么搭起来的

组成 · 6

三件产物被刻意分开,彼此之间只走单向的数据流。trace 是一份会话日志归一化成有序的文件触碰事件流;citymap 是仓库的一份确定性布局;report 是对某一个会话的评估。一个本地 Go 服务器把它们接起来,并服务 React 与 Three.js 的前端;这种分离是被写成规矩的,而不是留在默契里:adapter 不认识渲染,citymap 的生成不依赖回放,评审只读归一化 trace、绝不读原始会话日志,服务器主要负责把数据源接到 Web 客户端。由此得到两个性质。确定性——同一棵树永远产出同一张地图——是两个会话能放在一起比较的前提,因为只有当脚下的地面不动时,画面度量的才是注意力。而由于唯一离开本机的东西是一份由归一化 trace 生成的证据文档,评审才可能被封起来:一个跑着用户自己 CLI 的子进程,无工具、无 MCP、不读项目或用户设置、不持久化会话,它交出的只有 findings,结论由 Go 汇总。

internal/adapter/
14 个文件、223 KB,每种会话格式一个包:一个 30.5 KB 的共享 adapter.go,旁边的 Claude Code adapter 10.8 KB 加一个 11.8 KB 的 agent 关联文件,Codex adapter 24.5 KB 加同类的 12.6 KB,pi adapter 13.4 KB——另有大约等量的测试代码。九月有一条开着的 pull request 提议把这个共享文件按主题拆开,并把按位置配对的工具调用改成显式的按 id 配对。
internal/citymap/
两个文件、52 KB,其中构建器 30.2 KB、测试 22.9 KB。它按根目录的形状分类,并给每一类定硬预算,因为对家目录做无界遍历是走不完的;确定性那句话就压在这个模块上,而它不依赖回放路径上的任何东西。
internal/judge/
10 个文件、93 KB:封闭的 CLI 运行器、证据与提示词构造、带自己说明文档的 rubric 阶段、报告模型,以及一个按会话把报告写进 ~/.mindwalk/reports 的缓存——会话内容变了只让报告过期,不会自动重跑。
internal/server/
23 个文件、1.34 MB,是最大的一块——30.9 KB 的服务器配 51 KB 的测试——因为它还装着 internal/server/static,即内嵌的前端产物:533 KB 的 three.js chunk、192 KB 的 React chunk、36 KB CSS 和五套网页字体。README 明确要求不要手改这个目录,要重新生成。
web/
那份内嵌产物背后的 React、Vite 与 Three.js 源码:26 个文件、252 KB,领头的是 33.7 KB 的应用外壳与 47.4 KB 的样式表,两个各约 25 KB 的场景文件,回放的 reducer 与 recorder,界面目录下十个文件,以及一份 25.5 KB 的 Playwright 测试。
schema/、cmd/ 与仓库根
schema/ 下是四份 JSON 契约——trace、citymap、report、agent graph——镜像导出的形状,改它们就要在同一次提交里改对应测试。cmd/mindwalk 是命令行(serve、open、map、build、trace、analyze),cmd/rubriceval 是给 rubric 层用的 9.4 KB 工具。根目录放着 4.1 KB 的 AGENTS.md、Makefile、638 字节的 GoReleaser 配置、一个会校验校验和的安装脚本、发布工作流,以及一个用于验证的 Claude Code skill。

取舍,以及它替代了什么

  • 只取 trace health 的数据层那一刀,把面板留下 替代 把独立的 Trace Health 视图整个发出去

    贡献者在 #14 里提了健康度评分加一个指示灯和一个面板。作者回复说,数据层是 mindwalk「无论它最终以什么形式露出来」都需要的那部分——把记录到的、推断的、未知的结果保住——但上面那一层他还没想清楚,因为它的价值来自消费者:评审的证据文档、跨会话比较,或者某个指标被读到之处的批注。贡献者于是关掉 #14,只把 adapter 这一层单独作为 #16 送回来,并把那个消费者留在范围之外,直到它的产品角色被决定。

  • 按根目录的形状选遍历模式 替代 所有根目录共用同一个遍历器

    在作者自己的机器上,对家目录会话做无界遍历永远没有返回,因为需要同意的对话框、FIFO 与只在云端留有存根的文件都会在 open 上阻塞。于是 git 根用 git ls-files 流式读取、上限十万条路径;没有 git 但有项目标记的根,在限定范围内逐层遍历;像家目录那样的根,只映射选定的子树。

  • 结论由 Go 从 findings 的严重程度汇总 替代 让模型自己下结论

    README 与 AGENTS.md 都写明:评审只贡献 findings,每个结论都机械推导,而日志验证不了的条目掉覆盖率而不是加一条警告——那是盲点,不是失败。四个固定维度由 Go 强制,跟起草出的 rubric 里写了什么无关;交给评审的 trace 内容一律当作不可信输入。

  • 复刻 pi 自己的 loader 替代 照文档近似它的格式

    要求支持 pi 的那个 issue 一边给出 pi 文档化的会话格式,一边提醒它的分支树会带来复杂度。这个 adapter 选择复刻 loader 的识别规则——头必须是第一行能解析成 JSON 的条目、id 必须是字符串类型、空行与坏行不许占掉那个位置——然后再把 id 与 parent 组成的树的主干线性化。

  • 拒绝跨站的非 GET 请求,并钉死 host 替代 信任任何从 loopback 过来的东西

    因为 analyze 接口花的是真实的 token 与分钟数,所以用户碰巧开着的网页不能把它触发起来:每个请求都过 host 白名单,写操作查 origin 与 Sec-Fetch-Site,协议、host 与端口必须严格相等——而完全不带 origin 头的请求则原样放行。

依据AGENTS.md(4,134 字符)、README.md(9,810 字符)、docs/dynamic-rubric-evaluation.md、pull request 10、14、16、19、20、22、23 的正文与评论串、发布工作流与 Makefile,以及完整的 120 个文件树及其体积。

制作过程

6 个阶段
  1. 01

    十二天出第一个版本,然后是七周的安静

    仓库建于 2026-07-09,第一次提交「feat: bootstrap mindwalk」就在当天下午。两天后是 v0.1.0(2026-07-11),接着 v0.2.0(2026-07-15)、v0.3.0(2026-07-18),空了两周,再是 v0.4.0(2026-08-03)与 v0.5.0(2026-08-07)。第一个月发了五个版本,总共 62 次提交:七月 55 次,八月 7 次。最后一次的时间是 2026-08-10T02:06:46Z,内容是合并 pull request #16「fix(trace): preserve tool outcome certainty」。到写这份档案时已过去七周,之后什么都没有,仓库也未归档:1,363 个星、120 个 fork、17 个开着的 issue、MIT 许可。停更的原因在材料里任何地方都没有写:README、AGENTS.md、报告抓到的三十个 issue 与 pull request 里都没有。真正还在动的是收件箱:2026-08-07,作者回复终端 agent Crush 的一位维护者,说把 adapter 抽成可复用的库没问题;两天后那个叫 agent-trace 的库就出现在 GitHub 上,MIT,保留了 mindwalk 的版权。2026-09-09,一位贡献者送来九条一组的 pull request,加上第四个会话来源,在快照里一条都没合;报告里最新的一条——为 Antigravity 会话加 adapter,日期 2026-09-22——同样是开着的。

  2. 02

    每种 harness 一个 adapter,以及拒绝照着文档猜格式

    会话日志变成的一切都要过 internal/adapter:每种 agent 格式一个包,各自把自己的 JSONL 转成同一套 trace 模型。Claude Code 与 Codex 在先,pi 由 #20 作为第三个来源加入,约 490 行实现配 600 行测试。有意思的是识别规则:它不去近似 pi 的 schema,而是复刻 pi 自己的 loader。第一行能解析成 JSON 的必须是会话头,id 是字符串类型,工作目录可缺省——老会话就没有;空行与坏行被跳过,不许占用「头行」位置;第一个能解析但不是头的条目直接判定该文件不是会话。pi 的会话是 id 与 parent 组成的树,所以 adapter 会把主干线性化。这一层另一个被记录的改动是 #16,讲的是「归一化事件说不出什么」:事件上只有 isError,它为 false 时,读者分不清「确定成功」和「源日志根本没记结果」。贡献者本在 #14 里提了一整套 Trace Health 视图;作者回复说数据层才是 mindwalk「无论它最终以什么形式露出来」都需要的那部分,而上面那一层他还没想清楚,于是 #14 被关掉,只把 adapter 这一片单独作为 #16 送回来。他随后拿十一份真实的本地会话、822 个事件测过它,报告说把新字段去掉后每一条归一化 trace 都完全相同。

  3. 03

    一张每次都长一样的地图,和那次永远没走完的遍历

    地图是其余一切的地基,README 把它的硬性质写明了:internal/citymap 是确定性的,同一棵树永远得到同一张地图,于是两个会话可以放在一起比较。没有这一条,发光度量的会是布局而不是 agent。不带会话打开仓库时,高度编码代码行数;带会话时,发光跟着某个文件被触碰的深度与次数。走一遍仓库要付什么代价,是 #22 的主题,而且写得异常具体。一个工作目录是 $HOME 的会话——从家目录启动的 pi 会话——让构建器遍历整棵树、把每个文件从头读到尾,而且比「慢」更糟:它永远不返回。需要同意的目录会让 open() 卡在一个后台进程永远看不到的对话框上,FIFO 与 socket 会在 open 上阻塞,只在云端留有存根的文件会卡在物化上。作者写他在真机上验证过,那次构建一直没有返回。修法是放弃「一个遍历器走天下」:先按根目录的形状分类,再给每一类定预算。git 仓库用 git ls-files 流式读取,上限十万条路径,免得一个 dotfiles 目录无界地撑爆缓冲;有项目标记但没有 git 的目录,在限定范围内逐层遍历;两者都不是的当作工作区,只映射选定的子树。

  4. 04

    两种画仓库的方式,和一个不出浏览器的录像机

    前端是 React、Vite 与 Three.js,而且做了两个场景而不是一个——径向树与 treemap 平原——各自约 25 KB TypeScript,共用布局、轨迹、贴图与目录标签几个模块。打包产物里有一个 533 KB 的 three.js chunk 和一个 192 KB 的 React chunk,在仓库里除了两张截图就数它们最大。回放被拆成一个 reducer 和一个 5.7 KB 的 recorder,正因如此,导出菜单能把整段回放写成 .webm 完全在浏览器里完成:没有服务端编码器,也不上传任何东西。界面其余部分同样建立在不猜的前提上。进度条下方是整段会话的分桶直方图,观察保持冷色、改动发暖光,于是「在编辑」的阶段一眼就能从「在阅读」里跳出来。上下文压缩、子 agent 启动、用户发言各有时间轴标记,每一个都可以点击跳转。点某个文件会在检查器里钉住它的访问历史,再点其中一行就把播放头移到那一刻。会话启动过子 agent 时,agents 面板可以让任意一条子轨迹用同一张地图单独回放——这个功能自带一份 25.5 KB 的 Playwright 测试。

  5. 05

    一个不许自己拍板的评审

    第三件产物是评估,而且只在被要求时才跑:mindwalk analyze 或面板会把该会话的归一化 trace 渲染成一份证据文档,交给用户自己的 claude 或 codex CLI。报告分两层——四个固定的过程维度,对每个会话都一样;以及一份任务记分卡,标准由评审从该会话自己的用户消息里起草,尺子是为这个任务现写的,不是借来的。项目从头到尾守的那条线是:模型只提供 findings,别的都不给。维度与条目的结论由 Go 从 findings 的严重程度机械汇总,每条 finding 都必须指向一个可点击的时间轴事件,而日志无法验证的条目只会掉覆盖率、读作「no signal」,不算失败。最多两次调用,因为起草出的 rubric 会被缓存,任务措辞没变就复用。同一周里的两条 pull request 被记在这里,因为它们是同一个 bug 的两遍:命令摘要与评审失败详情原本按 500 字节切 Go 字符串,那会把多字节字符切成两半、产出非法 UTF-8;两处都改成按 rune 截断,并各配一个边界正好落在汉字上的回归测试。第三条 #10 是反方向的维护:Codex CLI 0.142.0 会拒绝评审仍在传的一个功能开关,于是每一次 codex 评审都在见到模型之前就失败。

  6. 06

    手工做同源校验,因为一次评审要花 token 和时间

    #23 是作者分四步评审自己的服务器,每一步都可独立回退,而第一步的安全论证之所以有意思,全在于那个接口做了什么。每个请求都要过一个 host 白名单,把 DNS 重绑定会伪造的那个名字钉死;非 GET 请求只要带着跨站的 Origin 或 Sec-Fetch-Site 头就被拒。写明的理由是:POST /analyze 花的是真实的 token 与分钟数,所以用户碰巧在另一个标签页开着的网页不能随手把它启动起来。同源判定是严格的:协议加上归一化后的 host 与端口必须与请求完全一致,于是 loopback 上的另一个服务过不了;而完全不带 origin 头的请求,比如 curl,则原样放行。同一组改动把 AGENTS.md 里写成规则的边界落成了代码——adapter 不认识渲染,citymap 不依赖回放,评审只读归一化 trace、绝不读原始会话日志,服务器主要负责把数据源接到客户端。这些部件持有的状态——报告缓存与评审工作目录——放在 ~/.mindwalk,正是一个九月的 issue 要求迁到 XDG 目录的东西。

相关档案

全部档案 →