Claude Session Viewer实战:三天打造AI CLI统一会话浏览器
摘要
作者为统一管理多个AICLI工具的会话记录,与ClaudeCode协作,在三天内用Rust和Vue TypeScript开发
五月的那个周末,我同时开着 Claude Code、Codex 和 Gemini CLI 三个窗口。想找回两天前调通的一段 prompt,结果在 ~/.claude、~/.codex、~/.gemini 三个目录里 grep 了二十分钟。
一气之下,我直接对 Claude Code 说:“我要写个桌面应用,统一翻看这三家的会话记录。”
三天后,v0.1.0 版本发布了,5500 行 Rust 加上大约 8000 行 Vue/TypeScript,macOS、Windows、Linux 三平台的安装包也都打好了。
这篇文章记录的是这三天的真实开发过程,重点不在于介绍这个工具本身,而在于如何具体地与 Claude Code 协作,从零开始把它“搓”出来。希望能给正在体验“氛围编程”的你一些参考。
核心结论:Claude Code 解决的是“懒得动手写第一版”
这三天最深的体会是,Claude Code 拯救的不是“我不会写”,而是“我懒得动手写第一版”。
我熟悉 Vue,也写过 Tauri,但如果让我从零开始手敲 Rust 的 JSONL 解析、Tauri 命令、Vue 前端、三种主题、国际化(i18n)和测试,我可能得磨蹭一个月。交给 Claude Code 来做,三天就能跑出一个像样的最小可行产品(MVP)。前提是,我得清楚每一步要给它什么指令,以及哪些地方必须由我自己来拍板。
技术栈选型:没让它替我决定
在正式开工前,我先定好了技术栈:Tauri 2 + Vue 3 + Rust,前端用 TypeScript。
为什么不让 Claude Code 帮我选?因为在这类问题上,它往往会趋于保守。如果问它“我要做个桌面 App 选什么栈”,它大概率会推荐 Electron + React,因为这是它训练数据里最常见的组合。
但我很清楚自己的需求:
- 安装包要小。Electron 起步就是 100M+,而 Tauri 能压缩到 10M 以内。
- 文件 IO 要快。读取 100M+ 的 JSONL 文件,用 Node.js 那套流式处理会显得笨拙,而 Rust 的
BufRead::lines()则干净利落。 - 前端我熟悉 Vue,不想为了“AI 更熟悉”而切换到 React。
技术栈一旦确定,Claude Code 就会非常听话地在这个框架内为你编写代码。像选型这种“半小时决定影响三个月”的事,最好自己拍板。
第一天:跑通最小闭环,目标只是“能看见聊天记录”
第一天的目标非常纯粹:给定一个 Claude Code 的 JSONL 文件,能在 Tauri 窗口里把对话内容渲染出来。
我没让它一开始就规划整个项目结构,只是丢给它一句指令,让它先去理解数据格式,而不是先写代码。
它读完数据后,为我整理出关键信息:
- 每行一个 JSON 对象,
type字段区分user、assistant、summary等类型。 assistant行的message.content是一个数组,里面混合着text、thinking、tool_use三种区块。user行也可能是一个数组,里面装着tool_result,需要按照tool_use_id配对回去。- 一个会话中可能多次出现
tool_use→tool_result的配对,需要保持顺序。
这一步,我没让它写一行代码。先把数据吃透,再让它写解析逻辑,后续的 Bug 会少很多。
然后给出下一个提示,让它开始编写解析器。
它写出了第一版代码,我跑起来后发现了三个问题:
thinking区块没有被正确识别,它把type: "thinking"当成了普通文本。tool_use和tool_result的配对错位,它没有考虑到跨消息的配对情况。- 文件较大时会卡住,因为它一次性用
serde_json::from_str解析了整个文件。
第一个问题我让它修复,五分钟就搞定了。
第二个问题,我自己读了一遍它的代码,发现它使用了一个 HashMap 按 tool_use_id 做关联,但 key 取错了字段。我直接指出来:“你第 87 行应该取 block.id 而不是 message.id。” 它认错并改正了。
第三个问题,我让它改成按行读取、按行解析,整个文件用 BufRead::lines() 流式处理。这是 Rust 的常识,但它一开始没有主动使用。
这一晚学到的是,不要害怕小步快跑。我宁愿和它来回十次提示,把一个命令打磨好,也不让它一次性产出五个命令然后我挨个调试。一次只让它做一件事,错了立刻就能发现。
第一天结束时,已经能看到渲染出的界面,Thinking 块、工具调用、结构化差异都能正常显示。Claude Code 写代码并修改自己代码这件事,进行得出乎意料地顺利。
第二天:让产品像个产品(搜索、导出、国际化)
第二天我列了三件事:会话内搜索、导出 Markdown/HTML、以及根据系统语言自动识别的国际化(i18n)。
这三件事都不需要触及后端的核心解析逻辑,纯粹是前端加一些工具函数。这是完美的“丢给 Claude Code 自己干”的任务类型。
我的策略变了:为每个功能配备一个独立的 TypeScript 模块和一个 vitest 测试文件,让它一次产出一对。
举个例子,对于会话内搜索功能:
src/chatToolbar.ts// 搜索、定位、范围过滤的纯函数test/chatToolbar.test.ts// 单元测试src/components/ChatToolbar.vue// 只负责 UI 和调用上面的纯函数
我给它的提示大致是:“写一个纯函数,接收会话消息列表和搜索词,返回匹配的消息索引和高亮位置。再为这个函数写一套 vitest 测试。”
它给出了两个文件,测试一跑,11 个用例过了 9 个,错了 2 个。我让它修复,再跑就全通过了。然后才进入 UI 部分的编写。
为什么要这样做?因为 vitest 是我信得过的“Claude Code 体检”。测试通过的纯函数,UI 怎么写都不会写崩。让它自己写测试再自己跑过,比我盯着每一行代码审查要高效十倍。
到第二天结束时,前端已经积累了一堆这样的纯函数模块:
format.ts时间格式化chatToolbar.ts会话内搜索export.tsMarkdown / HTML 导出i18n.ts多语言及自动识别settings.ts主题/语言持久化到 localStorage
每个都配备了 vitest 测试。这一晚还顺手完成了导出功能。
HTML 导出有一个细节让我特别得意:头像、CSS 全部内联到单个文件中,离线在任何浏览器都能打开查看,不依赖任何外部链接。这个想法是 Claude Code 提出的,我原本只想放一个链接。它说:“既然导出是为了以后归档用,那就应该假设没有网络环境。” 我立刻同意了。
在氛围编程中,有一类非常宝贵的对话是 AI 反过来挑战你的需求。别打断它,让它说完。
第三天:把抽象做对,把测试补齐
第三天最关键的一件事是让架构能够扩展。我已经知道下一步要接入 Codex 和 Gemini CLI,所以这一天的目标是:把所有“Claude Code 专属”的代码逻辑全部抽到一个 trait 后面。
我没让 Claude Code 一上来就设计 trait,而是先让它做一件事:“读一下 Codex 的 JSONL 文件,告诉我它的 schema 和 Claude Code 有什么不同。”
它的回答让我对 Codex 的怪异 schema 有了清晰的认知:
- Codex 把 image 和 text 分开存储,Claude 是混在一个数组里。
- Codex 的
tool_use叫function_call,字段名全变了。 - Codex 的 session 文件是
.jsonl,但 project 信息在另一个meta.json里。
这就是关键的认知。然后我让它设计 trait。
它给了一版设计,我修改了三处:
list_projects、list_sessions、read_session这几个核心方法上 trait。- 回收站(trash)功能我让它留在 trait 之外,因为所有 Agent 的回收站文件都使用同一种
.meta伴生文件格式,目录都在~/.claude/.session-viewer-trash/,没必要为每家都重写一遍。 image_src(block) -> Option这个方法上 trait,因为这正是 Claude、Codex、Gemini 三家分歧最大的地方。
最后,我在 agents/mod.rs 文件顶部写下了那段注释:
// 接入新 agent(如 gemini)的步骤:
// 1. 新建 `agents/.rs`,定义一个 unit struct...
// 2. 在文件末尾 `pub mod ;` 声明 module,并在 `source()` 里加一个 match 分支。
// 3. 前端 `types.ts` 的 `Agent` 联合类型里加上 `""`...
//
// 不要把 agent-specific 的解析细节漏到 lib.rs 或 trash.rs —— 加 agent 应该是
// 一个文件加一个 match 分支,超出这个范围就说明 trait 的抽象出了问题,需要重新设计。
这段注释是写给未来的 Claude Code 看的。CLAUDE.md 和源码顶部的注释就是与 AI 长期协作的“项目宪法”,写得越具体,AI 在第二个月把抽象做歪的可能性就越小。
到第三天收尾时,第四天接入 Gemini CLI 的时候,从零到能跑通只花了不到两小时。Trait 抽象设计的回报立刻兑现了。
发布时刻:搜索与回收站
虽然题目说是“三天 MVP”,但第四天我搭建了发布流水线,把 v0.1.0 发出去了。这里放两张那时的截图,证明前面三天的架构投资是值得的。
全局搜索(⌘⇧F)的弹窗是一个纯前端组件,但搜索结果是 Rust 后端跑的。这依赖于第三天抽离好的 trait:search_sessions(agent, query) 直接分发到对应 Agent 的数据源。
回收站是跨 Agent 共享的。删除 Claude 的会话和删除 Codex 的会话,会被扔进同一个回收站,恢复时按照 .meta 文件里记录的原始路径还原。
后来在 v0.1.2 版本中,我增加了统计面板,可以按项目、模型、工具切片查看 Token 消耗情况。这一块使用了 AntV G2 绘制图表。从添加 Tauri 命令到接入前端图表,Claude Code 写完后,我大概只动了 20 行代码来调整样式。底层的抽象做对了,后面的每个功能都是顺水推舟。
反复验证有效的几条 Claude Code 协作经验
下面这几条不是套话,是这三天里我反复验证有效的:
1. 让 AI 先理解数据/系统,再写代码
这是最重要的一条。直接让它写代码,它会脑补一个“最常见的 schema”,然后你得花一晚上去调试那些脑补出来的字段。让它先读真实文件、读真实接口、读真实日志,再让它动手。
2. 一次只做一个文件的一层抽象
不要让它一把反赌“完整功能”。把任务切成“纯函数加测试”、“trait 加一个实现”、“组件加它依赖的纯函数”,每一刀切下去都能被 vitest 或 cargo check 立刻验证。错了立刻知道,比一锅炖之后挨个调试省太多时间。
3. 测试是给 AI 的护栏,不是给人看的
我不会手写测试给它,我让它自己写测试。但写完之后我会扫一眼测试用例是否完整。如果覆盖太薄,我就让它补充:“补一下空输入、Unicode、边界值这三类。” 测试一过,我就敢让它在那个模块上继续叠加功能。
4. CLAUDE.md 比单次提示重要
你在每次对话里讲的“风格、约束”,下一次开新会话就全丢了。把项目级别的约定写进 CLAUDE.md,每次都自动加载,比每次在提示里重复有效百倍。
我那个项目的 CLAUDE.md 现在大概有 200 行,写的是“Agent 抽象的边界”、“回收站不要做 Agent-specific 的分支”、“恢复会话时的 session id 必须经过 allowlist 校验”这种架构约定,而不是代码风格。
5. “品味”这一层必须你自己留着
命名、UI 的微妙手感、什么时候停止添加新功能,这些 AI 做不好。它能让你三天产出 30 个特性,但它分不清哪三个是真正的痛点。
我在 v0.1.0 版本砍掉了一堆它已经写好的代码:生成 PDF、CSV 导出、命令面板,看了一遍觉得没必要,会让 MVP 变得臃肿。删掉的代码一点都不可惜。“停止”的判断必须由你来做。
6. 让它挑战你
我有时候会主动加一句:“如果你觉得这个需求有问题,先说出来。” 然后它真的会反驳。
那次导出 HTML,它建议内联所有资源;设计 trait 那次,它告诉我回收站不该上 trait。这些挑战比它写代码本身更有价值。
后续与一些没写进 MVP 的坑
发布 v0.1.0 之后,我才碰到一些更细致的问题,简单提一下,给后来人避坑:
macOS 红绿灯按钮与自定义标题栏的冲突。我想要一个 40px 高的统一顶栏,侧边栏背景一直延伸到顶部,但 macOS 原生按钮总是落在标题栏正中。试过用 setFrameOrigin 手动挪动,肉眼好看了,但拖拽时窗口的点击-拖拽跟踪被破坏了。最后找到的“非黑魔法”解法是在窗口上挂一个空的 NSToolbar 并设置 unifiedCompact 风格,AppKit 会自动把标题栏撑到刚好 40px。这个是看 AppKit 源码注释看出来的,Claude Code 不知道,但你把发现讲给它听之后,它能立刻写出 Rust 实现。
osascript 调用 Terminal 的命令注入风险。resume_session 功能是拼接 shell 命令丢给 osascript 执行的,session id 来自前端。我让 Claude Code 加了一个严格的 allowlist 校验:^[A-Za-z0-9-]+$,拒绝一切奇形怪状的 id。这种安全细节只有你提出来,它才会做,默认情况下它不会主动添加。
Codex 的 image/text 分开存储问题。前面提过,最后的解法是在 agents/codex.rs::read 里缓冲住所有的 image block,遇到下一条 event_msg.user_message 时再拼回去。这是非常 Agent-specific 的脏活,绝对不应该泄漏到上层。Trait 设计对了之后,这种脏活全被埋在文件内部。
写在最后
三天的成本:周末两天加周一晚上。我写代码的时间总共大概 14 小时,其中至少有 10 小时是在跟 Claude Code 来回对话和审查它的代码,真正自己手敲的部分只有红绿灯按钮那段 Objective-C / Rust 交互、几处 CSS 微调、以及 CLAUDE.md。
发布之后,这周又增加了实时刷新(live tail)、Token 统计、Linux 打包,到现在 v0.1.2 版本,总共 5500 行 Rust 加上完整的测试套件和三平台安装包。
我对这种工作流的看法是,它不会让你变成超人,它让“我懒得做”和“我做不出来”变成了同一件事。你愿意推动它一步,它就给你一步的产出。你停下来,它也跟着停。AI 是放大器,不是替代品。
如果你也是三家 CLI 都在用、被散落的会话记录折磨过的人,欢迎下载试用;觉得哪里不顺手可以直接开 Issue。想把 aider、opencode、cursor-agent 接进来的,PR 通道也是开放的,增加一个 Agent 真的只需要新建一个文件再加一个 match 分支。
来源:互联网
本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。