Skip to content

About

Read coding-agent CLI session history (Copilot CLI, Claude Code, Codex) and export to Markdown / interactive HTML. Replicates Copilot CLI /share file + /share html offline.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

asmgr

TypeScript Node pnpm Vitest

把编码 agent 的 CLI 会话与公开 ChatGPT 分享导出成 Markdown 或单文件 HTML。 asmgr(Agent Session ManaGeR)读取 GitHub Copilot CLI、Claude Code、OpenAI Codex CLI、DeepSeek Harness(DSH)及 Cursor Agent 写在本地的会话历史,也可捕获公开 ChatGPT /share/ 页面,再把选定的会话导出成自包含报告;来源无法完整保留的内容会显式标记。它是一个无 scope 的公开 npm 包,命令也叫 asmgr;HTML 与 Markdown 是它的导出能力,而非独立发布的产品。

HTML 产物高度复刻 Copilot CLI 内置 /share html 的排版(Primer 主题、sticky header、类型筛选 pill、侧栏目录、上一条/下一条用户消息跳转、搜索),差异见 ADR 0003。Markdown 产物遵循 Copilot CLI /share file 的结构与约定(### 💬/👤/🔧/✅ 标题、<sub>⏱️</sub> 耗时戳、<details> 折叠、diff 围栏、[!NOTE] 头块)。

它对 agent 状态目录只读:不写 .copilot、.claude、.codex、.dsh、.cursor。传本地 session id 或 --file 时,list / search / show / html / md 都严格本地。只有显式导入或直接读取 ChatGPT 分享 URL,以及按配置访问 restic 仓库的 backup 命令会联网。

归档 ≠ 恢复。 导出的报告是有损、只读、给人看的产物,不能反推回可 --resume 的原生会话。把会话忠实恢复到"另一台机器能续聊"是一条规划中的独立能力(来源 = 备份快照 ∪ 另一台机器),与只读归档严格分层——理念见 ADR 0001。

功能

目标 命令
列出最近活跃会话 asmgr list --agent all --limit 20
定位 DSH 当前会话(运行时注入) asmgr current
按工作目录筛选 / 机器读取 asmgr list --agent dsh --cwd "$PWD" --json
搜索本地历史 asmgr search "关键词" --agent all
在单个会话内搜索 asmgr search "关键词" --session <session-id>
打印一个会话 asmgr show <session-id> --agent claude
只看对话主干(跳过工具调用) asmgr show <session-id> --format dialogue
导入公开 ChatGPT 分享 asmgr import 'https://chatgpt.com/share/<id>'
直接读取 ChatGPT 分享主干 asmgr show 'https://chatgpt.com/share/<id>' --format dialogue
导出 Markdown(Copilot /share file 风格) asmgr md <session-id> -o session.md
导出 HTML(高度复刻 /share html) asmgr html <session-id> -o session.html
直接把 ChatGPT 分享导出 HTML asmgr html 'https://chatgpt.com/share/<id>' -o session.html
读取任意位置的会话文件(scp 来的 / 恢复出来的) asmgr html --file /path/to/events.jsonl -o session.html
搜索任意位置的会话目录 asmgr search "关键词" --file /path/to/sessions
运行加密增量备份 asmgr backup run --dry-run
把备份快照恢复到隔离缓存 asmgr backup cache latest --target ~/.cache/asmgr/restic-cache

支持的 agent 与数据来源

  • Copilot CLI:读取 ~/.copilot/session-state/*/events.jsonl;同时用 ~/.copilot/session-store.db 列出会话与元信息。events.jsonl 缺失(老会话被 prune、或只迁移了 DB)时回退到 DB 的 turns 表(lossy:只有 user/assistant 文本,工具与用户决策不可恢复)。所有读命令可用 --copilot-db <path> 覆盖 DB 路径。
  • Claude Code:读取 ~/.claude/projects/**/*.jsonl
  • Codex CLI:读取 ~/.codex/sessions/**/*.jsonl
  • DeepSeek Harness(DSH):读取 ${DSH_HOME:-~/.dsh}/sessions/<project>/<session>/session[.vN].jsonl[.zstd];用 --dsh-root <path> 覆盖会话根目录。目录发现选择每个会话的最高版本文件;--file 指向单个文件时读取指定版本。
  • Cursor Agent:只读 ~/.cursor/chats/<workspace-hash>/<session-id>/store.db 及相邻 metadata;--agent cursor(别名 cursor-agent)、--cursor-root <path>,也支持 --file /path/to/store.db。只读取当前 root 的可达消息图,不混入孤立旧分支。该闭源存储没有稳定公开协议;未验证的 protobuf 数字、缺失 token 用量和逐消息时间戳不做推断,局部缺失会给出诊断。
  • ChatGPT 公共分享:asmgr import <url> 从 /share/<id> 页面的 React Router 水合数据读取 linear_conversation。默认托管目录中的快照会自动进入 list/search/show/html/md; 也可把 URL 直接传给 show/html/md,不落盘使用。

每个读命令(list / search / show / html / md)都接受 --file <path>(别名 --events <path>),读一个显式的 *.jsonl / DSH *.jsonl.zstd / *.chatgpt-share.json 文件——或一个会被遍历出这些文件的目录——而不是 live agent 主目录。每个文件的 agent 格式自动探测(用 --agent 覆盖)。未知 JSON 会明确报错,不再静默显示成空会话。

统一中间格式与用量口径

所有来源经同一读取入口输出版本化 SessionDocument,show -f json 中的 document 是统一格式,entries 是由它派生的兼容显示视图。采用固定 DSH format 4 的事件信封、结构化内容块和计量语义,参考提交 5badb15009ae1756c3afe0ae0cef1faafc290ccc,并扩展来源证据、线程关系、上下文与额度观测。不会依赖安装中的 DSH 私有 codec,也不会随 DSH 升级自动改变本项目中间格式。设计与边界见 ADR 0004。这不是可供 DSH resume 的日志,也不保证未知来源字段已完整解释。

  • inputTokens = 未缓存输入;cacheReadTokens / cacheWriteTokens 为独立桶;reasoningTokens 是输出的子集。
  • 原始来源计数与转换依据保留;Codex 包含缓存的 input_tokens 不直接改名为未缓存输入。无法确认语义的值仅保留原始记录。
  • 精确响应增量按 response_id 去重;旧日志只能使用最新累计快照时明确标记口径。上下文观测、父会话继承和账号额度不累计为当前线程用量。
  • 缺值保持未知,JSON 省略 / TSV 空列,不假装成零;不把不完整小计包装成完整总量。正文事件时间是时间点,文件名本地时间和文件 mtime 不是可替代的计量时间。

DSH 读取与主干

默认自动使用 DSH home,无需传 --dsh-root。 会话目录优先级为:显式 --dsh-root → 环境变量 DSH_HOME 下的 sessions/ → 当前用户的 ~/.dsh/sessions/。--dsh-root 仅用于覆盖会话日志根目录,例如读取备份;它不是 DSH 源码目录,也不是项目工作目录。

asmgr list --agent dsh
asmgr show <session-id> --agent dsh --format dialogue
asmgr show --file /path/to/session.jsonl.zstd --format dialogue
asmgr html <session-id> --agent dsh -o dsh-session.html

DSH 读取采用独立的只读转录解析器,不再把归档恢复成可运行的 DSH Session,也不依赖或打包 DSH 的 codec/runtime 包。解析器区分字节与压缩、JSONL 事件信封、可见消息投影三个边界;保留 v0–v4 的已知布局,包括旧版平铺消息、packed 流片段、steering/compact/code-dispatch 别名、v3 工具结果包装与 v4 一等 tool-role 结果。子代理描述、团队状态等非转录元数据按事件名有意忽略,descriptor 子格式升级不会阻止读取对话;不检查这些 payload 的私有版本,不改写源日志,不加载本机 DSH 或启动插件。

主干包含直接用户输入、助手正文,以及原生或 PTC 调用中 ask_user_question 的题目、全部选项与匹配的回答;普通工具的参数和结果完全不显示。回答须有可验证的调用关联与完整问答格式:显式来源失效、重复标识或序号造成歧义、PTC 开始/结束的身份或参数不一致时不推断用户决策。

范围保持有限:不递归提取未知插件事件、注入上下文、失败模型尝试或任意 meta 的正文;所有具名非 user 来源都保持 model-only,不因 role: user 就当成人类输入。只显示最终追加消息,packed/embedded 流片段不会重复变成正文;不把压缩 replacement 当成新对话,不重复拼接 fork 的父会话,也不在 session/end-seed 处截断后续历史。只显示已知 compact checkpoint 对应的摘要。图片/文件只显示占位符并提示损失;完整工具仍在 text/HTML/Markdown 中保留。HTML/Markdown 暂无独立的主干导出开关。

兼容策略不是“版本号超出上限就拒读”。 新版本 header 仍满足已知 JSONL 信封时,继续读取可识别的消息,并标记尚未验证新版本语义;未知事件、内容块、surface 操作和畸形记录产生局部诊断,不丢弃后续可读消息。show --format json 的 diagnostics 包含 formatVersion、处理/忽略/未知计数、未知类型样本及 issues(代码、首个非空记录序号、存储 seq、次数;不包含原始 payload)。诊断样本和类型名称有大小上限,省略会明确标记。缺失/未解释内容通过 source.lossy 与具体 source.warning 暴露:text、dialogue、HTML、Markdown 都显示警告。search 即使没有命中也汇总解析/兼容性警告;预期的图片/附件占位符属于 source.notices,默认不刷 stderr,--verbose 才显示,--quiet 可关闭全部检索诊断。

这不保证任意未来格式都无需维护:如果压缩算法、信封或实际消息布局发生不兼容变化,仍须扩展读取能力;不把新布局猜成旧布局或声称转录完整。新增无关元数据或 descriptor 版本不要求手动升级 pin。压缩日志需要运行时提供 Zstandard API(Node.js ≥ 22.15);无法解压、无效 session 文件头、文件名/header 版本不符或同一代存在多种编码时仍明确报错。目录发现始终选最高一代,不自动回退到过期前代;--file 单文件始终读取指定归档。

安装

asmgr 已发布为单一、无 scope 的公开 npm 包。以下是安装已发布版本的方法;维护者推送代码前请先看版本与发布,普通推送 main 可能自动发版。

npm

npm i -g asmgr
asmgr list --agent all

各安装方式当前能力如下:

安装方式 读取、搜索、导入与导出 asmgr backup
npm / npm i -g github: ✅ ❌ 暂未包含备份运行时
原生二进制 ✅,但不读取 Copilot live SQLite ❌ 暂未包含备份运行时
源码 checkout ✅ ✅
其他安装方式与运行时差异

原生二进制(无需 Node)

从 Releases 下载对应平台的单文件二进制,内置 Bun 运行时、零依赖:

# Linux x64(macOS 换成 asmgr-darwin-arm64 或 asmgr-darwin-x64)
curl -fsSL https://github.com/TMYTiMidlY/agent-session-manager/releases/latest/download/asmgr-linux-x64 \
  -o ~/.local/bin/asmgr && chmod +x ~/.local/bin/asmgr
asmgr list --agent all

Windows 下载 asmgr-windows-x64.exe。每个 Release 附带 SHA256SUMS.txt 可校验完整性。

一处限制: 二进制基于 Bun,而 Bun 目前未实现 node:sqlite,因此读取 Copilot 实时 SQLite 库这一个数据源在二进制里会静默跳过(其余数据源——各家 *.jsonl、--file 指向的任意文件/目录——都正常)。需要该数据源请改用 Node 安装。

npm i -g github:(需 Node ≥ 22,免 registry)

安装时 prepare 钩子会用 esbuild 把 CLI 打包成单个自包含文件,无需预先构建:

npm i -g github:TMYTiMidlY/agent-session-manager
asmgr list --agent all

卸载:npm uninstall -g asmgr。

从源码构建

git clone https://github.com/TMYTiMidlY/agent-session-manager.git
cd agent-session-manager
pnpm install          # 触发 prepare 钩子,打出 dist/asmgr.mjs

装成全局 asmgr(软链回本仓库;卸载用 npm rm -g asmgr):

npm link
asmgr list --agent all

开发时直接跑源码:pnpm dev list --agent all(经 tsx)。自行编译原生二进制(需要 Bun):pnpm run binaries,四平台产物落在 dist/asmgr-*。

首次运行

  1. 按上面任一方式装好 asmgr。

  2. 列出某个 agent 的会话:

    asmgr list --agent copilot
  3. 从第二列复制一个 session id。

  4. 生成 HTML:

    asmgr html <session-id> --agent copilot -o report.html
  5. 用浏览器打开 report.html。

HTML 文件是自包含的:搜索、筛选、可折叠条目、侧栏目录、紧凑模式、主题切换、Markdown 表格、数学渲染都离线可用。

CLI 命令

asmgr list

默认按源文件 mtime 降序(最近活动优先)打印 TSV:agent、session-id、path、mtime(UTC ISO 时间)、size(字节,压缩文件为压缩大小)、cwd。原前三列保持不变;--sort id 恢复按 agent/id 排序。mtime 是文件活动时间,不等同于事件时间,也不能证明会话仍在运行。

asmgr list --agent dsh --cwd "$PWD" --limit 20
asmgr list --agent dsh --cwd "$PWD" --json
asmgr list --agent claude --claude-root /path/to/claude/projects --sort id

--cwd 精确匹配记录的工作目录(规范化相对路径、~ 和尾部斜杠,不递归包含子目录);没有 cwd 的会话不匹配。发现阶段只采样 metadata,不构建完整 timeline;Claude/Codex/Copilot 从前 50 条记录提取 cwd,DSH 使用权威 header。--json 输出 metadata 数组,--limit 0 列出零条。分组的 JSON 输出则是包含 ref、project 和条目统计的摘要数组。

用 --by project 或 --by agent 分组(默认平铺)。project 取会话记录 cwd 最近的、含 .git/ 的祖先目录(仅当该 cwd 在本机存在时才探测文件系统);cwd 存在但找不到 .git 祖先、或该路径不在本机时,按记录的 cwd 原样分组;只有完全没有 cwd 的会话才归入 (unscoped) 桶:

asmgr list --by project     # 按仓库聚类会话
asmgr list --by agent       # 按 copilot / claude / codex / chatgpt 分组

分组模式每组打印一个 # <组> (<数量>) 头(组间排序,组内按所选排序),随后是 组、agent、session-id、最后事件时间、条目数 的 tab 分隔行。分组需要完整解析以统计条目,比默认 metadata 列表重;解析并发也有上限。

统计、线程关系与账号额度

asmgr list -a codex --stats --sort peak_ctx --limit 20
asmgr list -a codex --stats -f json | jq '.[].stats'
asmgr list -a codex --role guardian
asmgr tree <session-id> -a codex --json
asmgr quota -a codex --timezone Asia/Shanghai -f json
asmgr show <session-id> -a codex --no-guardian
asmgr search "关键词" -a codex --include-guardian

list --stats 在原六列之后追加 peak_ctx、ctx_util%、in、cached、out、cache%、compact;JSON 使用 stats.peakContextTokens/peakContextUtilization/billedInputTokens/totals.cacheReadTokens/totals.outputTokens/cacheReadRatio/compactions。-f json 与 --json 等价。peak_ctx 是 Codex 最后请求的原始 input_tokens 最大观测值,不是模型窗口上限或累计 total;ctx_util% 配对使用该最大样本自己的窗口,缺窗口则未知。in 是确切的总 prompt 输入(包括已核实缓存),cached 是缓存读取,cache% = cached/in。统计排序先计算所选全集再取 limit;默认列表仍只读 metadata。统计排序包括 peak_ctx/ctx_util/input/cached/output/cache/compact,未知值排末尾;统计与 --by 分组不能组合。--verbose 显示坏行、未知记录及计量缺口。show 的 --role 同时过滤 JSON 的 entries 与 document 消息事件;此 reduced view 以 document.view 标记,并省略可能混合角色的原生 opaque 内容,避免通过第二载体暴露被排除的消息。

列表的 --role main|subagent|guardian|unknown 筛会话身份,与 show/search 的消息角色不同。tree 区分父子与 fork,标明缺失父节点、重复 ID 和循环;时间来自记录,不用 mtime 冒充活动或运行状态。并发数只是记录区间的半开重叠,不代表此刻仍运行。默认最多显示 128 层,可用 --max-depth 0–256 调整,截断会明确标记。Codex show 默认附上 guardian 子线程并保留其来源;--no-guardian 关闭。search 默认跨库排除 guardian 噪音,--include-guardian 可恢复;显式 --session 或单文件选择会尊重该选择,并先做完整的 ID 歧义检查。

quota 从统一文档读取 Codex primary/secondary 观测,合并所选会话为按小时的 min/max/last 轨迹,保留原始数值精度、余额字符串和来源。默认 UTC,--timezone 接受 IANA 时区;重复的 DST 小时按 UTC offset 区分。水位下降/重置时间变化只标记观测到的证据,不宣称全局重置。仅请求时刻有采样,间隙未知;limit_id 是额度桶,不是账号 ID,缺账号标识时明确提示可能混合不同账号。账号额度不按 cwd 百分比分摊。

DSH 的 raw TokenUsage 缺省缓存字段在 harness UI fold 中按 0 折算;本中间层保留缺失为未知,并在每条转换说明中标明此区别,不把显示默认值伪装成原始计量数据。Cursor 未验证的字段也不推断成 token 用量,读取方式与限制见 Cursor 格式说明。本机全量读取、成品验证、独立审阅与残余边界见 验证记录。

asmgr current

DSH 已提供运行时 DSH_SESSION_ID 时,asmgr current 直接打印该 ID;asmgr current --json 查询该会话的本地 metadata(支持 --dsh-root)。变量未设置会明确报错,不会把最新 mtime 冒充当前会话。

定位和复盘优先缩小范围,再搜索:

id=$(asmgr current)  # 必须在 DSH 的命令执行环境内
asmgr search "关键词" --agent dsh --session "$id" --role user
asmgr show "$id" --agent dsh -f dialogue --role user
# 没有运行时身份时,先按 cwd 查看最近会话,再核对目标 ID
asmgr list --agent dsh --cwd "$PWD" --limit 5

asmgr search

搜索 user、assistant、reasoning、tool、system、event 文本:

asmgr search "database migration" --agent all --limit 20
asmgr search "database migration" --session <session-id>   # 只在一个会话里搜

每条命中是一行 tab 分隔、以 project 列(cwd 最近的含 .git 祖先目录;找不到 .git 祖先时为 cwd 原值,无 cwd 时为 (unscoped))开头:project、agent、session-id、#条目、role/kind、摘录。

--session <id> 把搜索限定到一个会话(先精确匹配 id,否则按前缀匹配;DSH 的 id 是 session-<uuid>,直接给裸 uuid 也可以)。前缀或重复 ID 有歧义时拒绝,要求更长 ID 或 --agent/--file 缩小范围,不再随意选第一个。

  • 默认按会话源文件 mtime 降序检索,同一会话内保持原 timeline 顺序。达到 -l/--limit 即停止安排下一批;--concurrency(别名 -j,默认 2,范围 1–32)限制 worker 并发和预读,最多多读一批中的其余会话。-j 1 完全串行、无会话预读。worker 不可用时回退到同一规范读取器,不把运行时能力缺失误报成损坏会话;--verbose 显示实际 backend。并行度过大会增加内存,不保证越大越快。
  • --no-early-exit 扫描整个工作集以检查诊断,但仍只输出 limit 条;-l 0 不发现/读取会话。关键词为空、非整数或负 limit 明确报错。命中总数不足 limit 时仍必须全扫,这不代表提前终止失效。
  • --cwd <path> 与 list 的过滤语义相同;--role user|assistant|tool|reasoning|system|event 只搜指定规范化角色。
  • 只搜索可见的规范转录:不对原始 JSON 做全文搜索,因此不会误命中注入上下文、失败尝试或未知插件 payload。压缩读取采用严格增量帧扫描,不为搜索/list 整文件解压,不依赖本机 DSH。
  • 无法读取的会话不阻止其他会话命中;stderr 汇总次数并保留少量路径样本,解析/兼容性警告按同类去重。附件占位符默认静默;--verbose 查看逐条详情与附件说明,-q/--quiet 关闭全部检索诊断。完整导出的保真度警告仍保留。

派生缓存与隐私

search 默认缓存规范转录的可搜索文本索引(包括工具文本与诊断,不是原始日志或完整 timeline),避免每次全库查询都重新解压与重建 timeline。热查询先用已校验的 UTF16 三字符 Bloom 摘要保守排除不含关键词的会话(允许误报,不漏报);不足 3 个 UTF16 单元的短关键词绕过 Bloom,再用已校验的文本索引字节预筛。可能命中时回读规范源,确保工具详情、摘录和原 index 不改变。缓存由源路径、agent/id 与文件 stat(mtime、size、ctime、inode)校验;源变化自动失效,读取过程中变化的源不写缓存。一个会话只占一个可替换槽位,不保留每次修改的旧版本。缓存格式/解析语义有独立版本;坏缓存、只读缓存目录或写入失败会回退原始解析,不影响命中。

路径优先级:--cache-dir <path> → ASMGR_CACHE_HOME → ${XDG_CACHE_HOME:-~/.cache}/asmgr;实际文件在 search/search-text-2/。每个逻辑槽位包含 gzip 文本索引与小型 .meta.json 摘要;目录为 0700,文件为 0600。gzip 不是加密,缓存可能含私密对话和工具输出,应和会话历史一样保护,并从云同步/公开备份中排除。单个快照超过 128 MiB 时不缓存。--no-cache 完全绕开缓存的读写,也适合完整原始读取验收;缓存可删除并随下一次查询重建,但删除缓存不等于删除原始会话。Copilot 实时 SQLite/db-turns 不使用持久负索引:WAL 里的新消息可能不改变主 DB 的 stat,不能据此跳过检索。

冷首次全库查询仍须处理所有候选历史(不足 limit 时无法免除),后续关键词共享缓存;没有后台索引服务,不触碰原生历史目录。库 API 的 searchRefs 不默认写缓存,只有调用方显式传 cacheDir 才使用。

asmgr import

导入公开 ChatGPT 分享页:

asmgr import 'https://chatgpt.com/share/<conversation-id>'

默认写入 asmgr 托管目录,随后可直接用 session id 执行 list/search/show/html/md。 也可以把分享 URL 直接传给 show/html/md,跳过本地保存。

ChatGPT 存储路径、输出方式与保真限制

托管目录优先使用 ASMGR_DATA_HOME,其次使用 XDG_DATA_HOME;都未设置时按平台选择:

平台 默认目录
Linux ~/.local/share/asmgr/imports/chatgpt
macOS ~/Library/Application Support/asmgr/imports/chatgpt
Windows %LOCALAPPDATA%\asmgr\imports\chatgpt

三种写入方式的后续读取不同:

导入方式 后续读取
默认目录 自动进入 list/search/show/html/md
--chatgpt-root <dir> 后续读命令继续传相同的 --chatgpt-root
-o <path> 用 --file <path> 显式读取;若改为扫描目录,文件名需以 .chatgpt-share.json 结尾

新快照权限为 0600;目标已存在时拒绝覆盖,确认要刷新才加 --force。普通 ChatGPT 页面、私有 /c/...、/g/.../c/... 地址栏会话及其它网站 URL 都不会发起抓取:检测到地址栏私有 会话链接时,会用中文明确提示先在 ChatGPT 中点击“分享”,再复制 /share/ 链接。

捕获不启动浏览器:直接解码页面 HTML 内的 turbo-stream 水合数据。快照保留完整公开 linear_conversation,便于未来适配器改进后重新解析。公开页隐藏的工具结果无法恢复; 图片或附件若只有资源指针而没有内容,会显示占位符并把来源标记为 lossy。工具调用与结果 只有在同一用户轮次内存在唯一匹配时才合并,关联不明确时保留独立结果或 pending 状态。

asmgr show

以 text、dialogue 或 JSON 打印一个会话:

asmgr show <session-id> --agent codex
asmgr show <session-id> --agent copilot --format dialogue
asmgr show <session-id> --agent codex --format json
asmgr show 'https://chatgpt.com/share/<id>' --format dialogue

--format dialogue 只保留用户消息 / 交互式提问与选项 / 用户决策或回答 / 压缩摘要 / 助手回复,跳过普通工具调用的全部内容与 reasoning。DSH 的 ask_user_question 保留题目、全部选项、单/多选信息、选中项和自由回答;Copilot 的 ask_user 保留题目、全部候选项及回答。工具噪音被剔掉后,每条用户 prompt 直接紧跟回答它的助手回复,prompt↔回复的对应关系一目了然——适合会话复盘、交接和收尾盘点等需要通读对话主干的场景。--format text 则含完整工具参数+结果、子代理/技能/计划/压缩统计。

dialogue 头部与角色过滤

show -f dialogue 每个条目的头部为 ## N. role/kind 时间戳:N 是完整 timeline 中从 1 开始的原始位置,过滤后不会重编号;时间戳若来源没有记录则省略。dialogue 不把自由文本标题插进头部;text 格式可能包含标题。文件开头还有 session/agent/cwd/started/warning metadata。

## 3. user/message 2026-10-04T17:00:00.000Z

用户原话

无需用正则猜头部提取用户发言:

asmgr show <id> --agent dsh -f dialogue --role user
asmgr show <id> --agent dsh -f json --role user  # 机器处理建议 JSON

--role 同样适用于 text/JSON,保留原 index;dialogue 的角色过滤仍与主干过滤取交集。

asmgr html

写出一份自包含 HTML 报告,支持搜索、筛选、侧栏目录、主题切换、Markdown 表格与 KaTeX 数学:

asmgr html <session-id> --agent copilot -o report.html
asmgr html <session-id> -s agent-summary.html -o report.html   # 顶部钉一份 HTML 总结
asmgr html 'https://chatgpt.com/share/<id>' -o report.html
HTML 与 Copilot CLI `/share html` 的差异
  • 用 React 渲染,而非官方 vanilla bundle 资产。 抽取的上游 CSS/JS 只当逆向参照,不随运行时产物发布。
  • Shiki 语法高亮,覆盖 markdown 代码围栏与 diff 风格的工具输出,双 light+dark 主题,页面切主题时代码无需重载即重新着色。
  • 24 小时制时间戳(会话起点 YYYY-MM-DD HH:MM:SS;同日条目 HH:MM:SS,跨日 MM-DD HH:MM:SS)——en-US 默认的 12 小时制(PM/AM)太容易读错。
  • 耗时 pill,由 startedAt → 最后一条条目算出,显示在 header。
  • agent 总结卡片,用 --summary <file.html> 钉在时间线顶部(原样渲染受信任 HTML;data-index="summary",真实第 1 条仍是第 1 条)。
  • 合并的工具卡片,六种结果态(success / failure / rejected / denied / pending / redacted),配对应的边框色与状态图标。
  • ask_user 的回答被抽成一等「用户决策」条目(user/decision):既保留原始工具卡片,又让用户的选择/回答在时间线里单独、显眼地出现——复盘或交接时不会把决策埋没在成百上千次工具调用里。
  • 子代理 / 技能 / 计划条目,从 events.jsonl 解析、各自成卡片 + 筛选 pill。子代理卡片在可得时显示记录到的身份、模型、描述、失败详情。这些超出 Copilot 自身 /share html 的筛选集。
  • 数据源回退警告 pill,当解析器不得不读 events.jsonl 之外的东西时显示在 header;回退到 db.turns 时进一步说明「交互式用户决策与工具条目在此模式下不可恢复」。
  • 默认展开策略在其余方面沿用 Copilot bundle:user / assistant / error / task_complete 展开,其它折叠。
  • 单行 info 条目(模型切换 / 取消)默认展开而非折叠——与官方 bundle 不同,让「Model changed from X to Y」「Operation cancelled by user」这类一行信息一眼可见;多行 info 仍折叠。
  • 只存在于 live 内存的条目离线无法重建,包括吉祥物启动横幅、临时重试提示、/share 成功回执。见 ADR 0003 与下文「Copilot 时间线与离线映射」。

asmgr md

导出遵循 Copilot CLI /share file 约定的 Markdown(### 💬/👤/🔧/✅ 标题、<sub>⏱️</sub> 耗时戳、长工具输出 <details> 折叠、diff 围栏、[!NOTE] 头块):

asmgr md <session-id> --agent copilot -o report.md
asmgr md <session-id> --no-reasoning -o report.md             # 去掉 reasoning 条目
asmgr md <session-id> -s summary.md -o report.md              # 注入一份 markdown 总结
asmgr md 'https://chatgpt.com/share/<id>' -o report.md

asmgr backup

backup 是正式的 restic 备份命令组:

asmgr backup run --dry-run
asmgr backup run
asmgr backup cache latest --target ~/.cache/asmgr/restic-cache

backup run 默认备份 ~/.copilot、~/.claude、~/.codex 与 asmgr 托管的 ChatGPT 导入目录,只处理实际存在的路径。运行时会先尽力把 Copilot 的 SQLite WAL 合入主库, 再执行加密、去重、增量备份;SQLite 热文件、锁文件和 Copilot 进程日志不会进入快照。 每次快照带 agent-session-manager 与当前主机标签,并应用 daily / weekly / monthly 保留策略。

backup cache 把指定快照恢复到独立缓存,明确拒绝 home 目录及 live 的 ~/.copilot、~/.claude、~/.codex,也拒绝 asmgr 托管的 ChatGPT 导入目录。 缓存用于 list/search/show/html/md --file,不等于把会话恢复成原 agent 可以 --resume 的状态。

当前备份命令需要从源码 checkout 运行;npm 包和原生二进制尚未包含备份运行时。

备份配置与 systemd 自动运行

配置

复制配置模板并限制权限:

cp secrets.env.example secrets.env
chmod 600 secrets.env

必填项是 RESTIC_REPOSITORY 与 RESTIC_PASSWORD;S3 兼容后端还需要 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY。可用 RESTIC_BIN 覆盖 restic 位置、用 BACKUP_AGENT_DIRS 调整数据源、用 BACKUP_EXCLUDE_REWIND=1 排除 Copilot rewind 快照。通过 --chatgpt-root 或 -o 放到其它位置的 ChatGPT 快照不会 自动进入备份,需要显式加入 BACKUP_AGENT_DIRS。

新仓库先加载配置并初始化,再运行备份:

set -a; source secrets.env; set +a
restic init
asmgr backup run --dry-run
asmgr backup run

RESTIC_PASSWORD 是读取所有快照的唯一密钥,初始化后必须保存到密码管理器或另一台设备。

自动运行

systemd/ 提供 user service 与 timer 示例,每天运行一次,并用随机延迟避免整点拥塞; Persistent=true 会在机器重新启动后补跑错过的任务。复制示例后按源码 checkout 和日志 位置调整 service,再启用 timer:

mkdir -p ~/.config/systemd/user
cp systemd/agent-session-manager.service.example ~/.config/systemd/user/agent-session-manager.service
cp systemd/agent-session-manager.timer.example ~/.config/systemd/user/agent-session-manager.timer
systemctl --user daemon-reload
systemctl --user enable --now agent-session-manager.timer

同一个 restic 仓库只应由一台机器负责定时运行。需要在登出后继续执行时,为该用户启用 systemd lingering。

从 live 目录之外读取会话

--file <path>(别名 --events <path>)让 list / search / show / html / md 读一个显式路径,而不是 live agent / import 主目录:

# 从别的机器拷来的单个会话文件(agent 自动探测)
asmgr show --file ~/dl/events.jsonl --format json
asmgr html --file ~/dl/events.jsonl -o report.html
asmgr show --file ~/dl/conversation.chatgpt-share.json --format dialogue

# 整个目录(遍历 *.jsonl / *.chatgpt-share.json;每个文件各自探测 agent)
asmgr list --file /tmp/session-archive
asmgr search "migration" --file /tmp/session-archive

--file 指向单个文件时,<session-id> 参数可省。指向的目录若产出多个会话,传一个 <session-id> 挑一个(用 asmgr list --file <dir> 看 id)。

术语

  • 会话(Session):agent CLI 持久化的一次对话,可由 UUID、JSONL 路径,或某 agent 本地数据库中的一行标识。
  • agent 适配器(Adapter):知道如何发现并解析某一家 agent 持久化格式的代码。当前适配 GitHub Copilot CLI、Claude Code、OpenAI Codex CLI 与 ChatGPT 公共分享快照。
  • 事件(Event):agent 持久化流里的一条原始记录。Copilot 的事件存在 events.jsonl,是离线时间线重建的输入,而非 live /share html 直接渲染的对象。
  • 时间线条目(Timeline entry):时间线里的一个展示单元(用户消息、助手回复、reasoning 块、工具调用等)。Copilot 把 live 条目放内存里;asmgr 从持久化事件重建规范化条目,供搜索与渲染共用。
  • 归档(Archive / 只读检索):asmgr 只读地取回历史会话——搜索、文本显示、JSON 导出、给人看的 HTML/Markdown。归档从不把会话恢复回原 agent 的 live 状态。
  • 恢复(Restore):忠实重建可 --resume 的原生会话状态(规划中)。归档 ≠ 恢复:报告不可反推回可续聊的原生态。
  • 归档源(Archive source):可读取会话文件的地方——包括 live 本地 agent 目录,以及通过 --file 显式指定的文件或目录。

设计文档(ADR)

重要决策的理念记录在 docs/adr/:

  • ADR 0001 —— 产品范围与"归档 ≠ 恢复"。
  • ADR 0002 —— 单一 asmgr 包与统一命令入口。
  • ADR 0003 —— 归档数据的规范化与保真度。
实现参考:Copilot 时间线、目录结构与漂移探针

Copilot 时间线与离线映射(参考)

为什么这样设计(内存 timeline vs 离线重建、单点映射风险、有意的离线扩展)见 ADR 0003;这里是具体清单与坑。

两种表示:Copilot /share html 渲染 live 内存 timeline(session.getTimelineEntries()),不直接读 events.jsonl;timeline 为空时官方 bundle 只打印 The session is empty.。asmgr 离线只读 events.jsonl 重建。Compaction 可能在某个 event-id 边界截断 / 重写持久化流(确切边界随 Copilot 版本,需复验)。

官方 12 类筛选:user、copilot、tool、reasoning、info、warning、error、group、notification、handoff、compaction、task_complete。asmgr 另加 subagent、skill、plan,并可在时间线顶部钉一张总结卡片——这些是超出官方集的有意扩展。与官方不同,asmgr 默认展开单行 info(模型切换 / 用户取消),多行 info 仍折叠。

离线不可重建的数据(只在 live 内存、从不落盘,是硬限制而非 bug):吉祥物启动横幅、/share 成功回执(Session shared successfully to: …)、临时重试提示。

操作坑:

  • 当前 Copilot session id 是 ~/.copilot/session-state/<id>/ 的目录名,别因为某个 id 出现在对话正文里就复制它。
  • live session-store.db 常滞后最新一两轮——最近一轮可能还没进库,对当前会话导出是有损兜底。

原始事件三态策略(解析器把每个原始事件归为 handled / 有意忽略 / unknown;计数与 unknownTypes 见 src/core/adapters/copilot.ts):

  • handled:产出条目、更新元数据或与另一事件配对。当前族含 session.start、user.message、assistant.message、tool.execution_start、tool.execution_complete、system.notification、session.info、abort、error/warning 类、handoff、compaction 起止、task_complete、subagent 生命周期、skill.invoked、session.plan_changed。
  • 有意忽略:类型已知但不应生成离线条目。session.model_change 让位于面向用户的 session.info(infoType=model);其余有意丢弃:session.resume、session.shutdown、session.mode_changed、session.context_changed、session.workspace_file_changed、session.binary_asset、session.permissions_changed、session.schedule_*、session.truncation、session.usage_checkpoint、所有 hook.* 与 assistant.turn_*、system.message。
  • unknown:无映射也无显式忽略规则——unknown 计数是漂移警报,Copilot 变更时应排查。

置信度:getTimelineEntries() 用法、空会话消息、12 类筛选、reasoningText 不对称、上列 live-only 条目——置信度高;compaction 时的文件截断机制置信度较低,需对新版本复验。Copilot 升级后重跑漂移探针并查 unknown 诊断。

目录结构

asmgr 是一个 npm 包;下面的 src/* 是它的内部模块(相对 import 串联),不是各自发布的包。

路径 用途
src/core agent 发现、解析器、规范化时间线模型、搜索
src/markdown 遵循 Copilot /share file 约定的 Markdown 渲染器
src/html 基于 React 的单文件 HTML 渲染器,高度复刻 Copilot /share html
src/cli asmgr 命令(commander 程序、各子命令、选项解析)
scripts esbuild 单文件打包、bun 原生二进制、构建期资源内联(gen-assets)
fixtures 脱敏的解析器与 CLI fixtures
tools/copilot Copilot /share bundle 漂移探针(仅逆向研究,非运行时依赖)

漂移探针(tools/copilot)

tools/copilot/extract-share-assets.cjs 是逆向研究辅助,不在 asmgr 的渲染路径里。它读取已安装的 @github/copilot 的 app.js bundle,重建其 JS 模板字符串里的运行时字符串,写出 share-export.css / share-export.js(重建而非逐字节复制——否则会保留双重转义、产出坏 CSS/JS):

node tools/copilot/extract-share-assets.cjs [path/to/@github/copilot/app.js] [out-dir]

为什么保留:它是漂移探针。Copilot 升级可能改动时间线条目 / 筛选类、Primer 明暗主题规则、按钮 id 等 DOM 钩子。升级后重跑并 diff 上一次输出,把有意义的变化当作"复核离线事件映射与 React 渲染器"的提示,而不是自动搬进产物。维护中的 HTML 渲染器是 src/html 的 React 实现,不 import 也不发布这些抽取资产;仓库里目前没有大小 / 哈希基线,可在下次比较时记录探针打印的长度与本地校验和。

同类项目调研与差异

同类项目对比

这个问题空间已有多种 CLI、TUI、Web 与桌面实现。下表是 2026-07-24 整理文档时的调研快照; Stars 只反映当时状态,不作为持续更新的排名。

仓库 Stars 语言 形态 覆盖 agent 备注
simonw/claude-code-transcripts 1586 Python CLI → 分页静态 HTML Claude Simon Willison 出品;移动端友好的多页输出
d-kimuson/claude-code-viewer 1233 TS (web) 完整 web 客户端(live + 历史) Claude 不只是查看器;能经 Agent SDK 驱动新会话
daaain/claude-code-log 1121 Python CLI → HTML/Markdown + Textual TUI Claude uvx claude-code-log 零安装;项目层级索引页
specstoryai/getspecstory 1260 混合 商业产品(CLI 部分开源) 多种 IDE/CLI “Intent is the new source code”——捕获 + 索引 + skill forge
nateherkai/token-dashboard 605 Python 本地 web 仪表盘 Claude 成本 / token 用量分析视角
vibe-log/vibe-log-cli 332 TS npm CLI(vibe-log) Claude + Codex 生产力报告 + Claude 状态栏
delexw/claude-code-trace 327 TS+Rust (Tauri) + Python 原生 GUI + Web + TUI Claude Tauri 桌面,cctrace CLI;丰富的实时 tail UI
kylesnowschwartz/tail-claude 146 Go Bubble Tea TUI Claude 单二进制,需 Nerd Font
wesm/archived-agent-session-viewer 88 Python 本地 web app (FastAPI) Claude + Codex Wes McKinney(pandas/Arrow)出品;已归档,转向 AgentsView
shayne-snap/waylog-cli 84 Rust 自动同步到 .waylog/ markdown 文件 Claude + Codex + Gemini Cargo / Homebrew / Scoop 分发
PixelPaw-Labs/codex-trace 56 TS+Rust (Tauri) 原生 GUI + Web Codex claude-code-trace 的姊妹项目
monk1337/clicodelog 47 Python (FastAPI) 本地 web app Claude + Codex + Gemini 现有最接近的多 agent 本地查看器
HizTam/codex-history-viewer 19 TS VS Code 扩展 Claude + Codex 在 VS Code 内浏览 + 恢复
dotneet/agent-session-view 10 TS (Bun) Web + Ink TUI Claude + Codex 多种导出格式(text + HTML)

与同类项目的差异

  • GitHub Copilot CLI 是一等适配器。 上面的项目目前都不解析 ~/.copilot/session-state/*/events.jsonl。
  • 产物贴近 Copilot CLI /share file 与 /share html 约定,但不声称完全等价。 熟悉的 Primer 样式、筛选概念、emoji 前缀的 Markdown 标题、耗时戳、<details> 折叠、diff 围栏都延续下来。HTML 渲染器用 React 而非官方 vanilla bundle,额外加了子代理/技能/计划与总结条目,用 Shiki 与 24 小时制,且无法重建只存在于 live 内存的条目。见 ADR 0003。
  • 单文件 HTML 是默认交付物。 ~1 MB,无服务器、无构建,双击即开。(多数同类发 Tauri app、Express/FastAPI web app 或 TUI;唯二的静态 HTML 同类是 Simon 的 claude-code-transcripts(仅 Claude)和 daaain/claude-code-log(仅 Claude)。)
  • 单一自包含产物,安装摩擦低。 一个无 scope 的 npm 包 asmgr(命令同名):npm i -g asmgr、免 Node 的原生二进制、或 npm i -g github: 免 registry 一行装。解析器与渲染器是包内模块,不额外发布独立包。
  • 不耦合 live agent SDK。 只读,正常运行不调用任何 Anthropic / OpenAI / GitHub API;没有 claude-code-viewer 那样要应对的 ToS 面。

从同类项目借鉴

这些灵感项都作为 GitHub issue 跟踪(每条写明 Inspired by …),见 issues:项目层级索引页(claude-code-log)、Token / 成本分析视图(token-dashboard)、实时 tail 模式(claude-code-trace / tail-claude)、按项目分组侧栏(agent-session-viewer / codex-history-viewer)、VS Code 扩展封装(codex-history-viewer)、Pages 静态导出 tarball(claude-code-transcripts)。

维护者:版本与发布

推送 main 不等于“只同步代码”。 当前使用 semantic-release 自动推导并发布版本, 不是维护者先运行 cz bump 再推 tag。发布前必须检查上次发布以来的全部提交,不能只看本次提交的类型。

操作规则以 release.yml(触发条件、测试与权限)和 .releaserc.json(提交分析、版本写回、构建与发布插件)为准。

什么操作会启动发布

操作 当前行为
向 main 推送,或合并 PR 使 main 更新 自动运行 release 工作流:安装依赖 → pnpm test → semantic-release;没有按文件路径过滤,纯文档推送也会启动
在 GitHub Actions 手动运行 release,选择 main 运行同一条发布流水线;不是预演,也不强制一定产生新版本
只在本地 commit、推送非 main 分支、仅创建 PR,或单独推 tag 不触发当前发布工作流;semantic-release 的发布分支也仅配置了 main

启动工作流 ≠ 一定发版。 测试通过后,semantic-release 分析上个发布 tag 到本次运行提交之间的 提交记录;没有符合发布规则的提交时,不生成新版本。有可发布变更且验证、构建等步骤成功时,就会实际发布。

提交如何决定版本

当前未自定义 releaseRules 或解析器,使用默认 Angular 风格的提交解析(如 fix(parser): ...):

提交内容 版本变化(以上一版 0.2.0 为例)
fix: ...、perf: ... patch → 0.2.1
feat: ... minor → 0.3.0
正文或页脚含 BREAKING CHANGE: ... major → 1.0.0,不会因仍处于 0.x 自动降为 minor
被解析为 revert 的回退提交 默认 patch;在分析区间内成功匹配的原提交与回退会被成对过滤
普通 docs:、chore:、ci:、test:、refactor: 等,不含破坏性变更说明 自身不要求发布

同一分析区间按最高级别决定一个版本,而非每条提交各发一版。破坏性变更请使用明确的 BREAKING CHANGE: 正文或页脚,不要只写 feat!: / fix!::当前默认解析器不凭标题中的 ! 识别破坏性变更。

“本次只有 docs:”不保证不发版:如果此前有尚未发布的 fix: / feat:,本次运行仍会把它们纳入分析。 发布的是本次运行所检出的完整源码,不是只打包触发版本升级的那几条提交;CHANGELOG 则按提交规则生成摘要。

版本、标签和产物由谁生成

日常维护不要用 cz bump、npm version、手工改 package.json 版本或手工打发布 tag 来推动发版。 semantic-release 以 Git 发布历史为依据,在 CI 中自动完成:

  1. 推导下个版本并生成 release notes,更新 CHANGELOG.md 与 package.json 的版本。
  2. 构建 Node 单文件 bundle、Linux x64 / macOS Intel / macOS Apple Silicon / Windows x64 四平台二进制及 SHA256SUMS.txt。
  3. 将 package.json 和 CHANGELOG.md 以 chore(release): X.Y.Z [skip ci] 提交回 main,并创建、推送 vX.Y.Z tag。
  4. 发布公开 npm 包 asmgr,创建 GitHub Release,附上 notes、二进制、Node bundle 和校验和。

npm 发布走 OIDC Trusted Publishing(npmjs environment),GitHub 操作使用工作流的 GITHUB_TOKEN。 它是直接发布,不是先生成等待人工确认的 npm 暂存版本。

只推代码:优先使用非 main 分支

不准备发布时,将提交保留在工作分支并只推该分支,例如:

# 从当前提交创建工作分支;分支名按需替换
git switch -c work/my-change
# 在该分支完成提交后,只推当前分支,不更新 main
git push -u origin HEAD

合并该分支到 main 仍可能发版,合并前需要重新确认发布范围。

如果确实要把代码推到 main,但只想跳过这次 push 的工作流,可在提交消息中加 [skip ci] (例如 fix: handle large sessions [skip ci])。注意:

  • 它跳过匹配的 push / pull_request 工作流,测试也会跳过;不会取消已启动的运行,也不阻止手动 workflow_dispatch。
  • 它不是 semantic-release 的“永不发布此提交”标记。该修复仍在上个 tag 之后,下一次未跳过的 main 推送或手动发布仍会分析并可能发布它。
  • 因而它只适合临时跳过一次触发,不能作为长期发布闸门,也不能防止其他维护者后续推送带出该变更。

明确批准一次发布

  1. 维护者先核对目标 main 提交 SHA、上个发布 tag 之后的全部变更与预期版本,确认这些内容都允许公开发布。

  2. 明确批准后,再向 main 普通推送 / 合并以启动自动发布;若待发布提交已在 main(例如此前用了 [skip ci]), 可在 GitHub Actions → release → Run workflow 选择 main,或执行:

    # 真正启动发布,不是 dry-run;只在明确批准后执行
    gh workflow run release.yml --ref main
  3. 检查运行结果,以及回写的版本 / CHANGELOG、vX.Y.Z tag、npm 版本和 GitHub Release 附件。手动运行没有绕过提交分析;无可发布变更时仍不会发新版本。

“单独提交”只授权本地 commit,不包含 push 或发布;“只推代码”应使用非 main 分支。 自动化助手执行可能发版的 main 推送 / 合并或手动运行前,必须说明发布影响并取得维护者明确同意。

当前工作流没有 publish=true 一类的二次确认输入;environment: npmjs 本身也不代表已有人工审批, 是否等待审批取决于仓库 Settings → Environments → npmjs 的保护规则。若需要每次都强制人工批准, 应在那里配置 required reviewers(以仓库支持情况为准),或另行修改工作流为仅手动发布;这些都需要单独配置,不能靠 [skip ci] 实现。

维护者:公开前安全检查

公开前的安全检查

把本仓库推到任何公开位置前,只检查被跟踪的文件:

git ls-files
git grep -nE 'PRIVATE|SECRET|TOKEN|PASSWORD|AKIA|/(h[o]me|Users)/|10\\.|192\\.168\\.|172\\.|D[E]SKTOP|[Ww]orkstation'

secrets.env、backup.log、node_modules/ 与构建产物都被忽略,应保持未跟踪。

路线图

待办与灵感项都在 GitHub issues 跟踪。两条值得单独点名的方向:

  • 忠实恢复 / 迁移:把会话恢复到"另一台机器能 --resume"的原生状态(来源 = 备份快照 ∪ 另一台机器)——边界见 ADR 0001。
  • 本地 Web 界面 asmgr web:本机启动、仅供自己查看的会话浏览界面。

单文件分发与 npm 发布已实现(单一无 scope 包 asmgr、四平台原生二进制、semantic-release、npm i -g github: 免 registry 安装)——使用方式见安装,发布规则见维护者:版本与发布。其余(持久化索引外部会话目录、提升适配器保真度、项目层级索引页、Token / 成本视图、实时 tail、VS Code 扩展、Pages 导出 tarball、跨多会话仪表盘)见 issues。

About

Read coding-agent CLI session history (Copilot CLI, Claude Code, Codex) and export to Markdown / interactive HTML. Replicates Copilot CLI /share file + /share html offline.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages