把编码 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 |
- 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 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.htmlDSH 读取采用独立的只读转录解析器,不再把归档恢复成可运行的 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 i -g asmgr
asmgr list --agent all各安装方式当前能力如下:
| 安装方式 | 读取、搜索、导入与导出 | asmgr backup |
|---|---|---|
npm / npm i -g github: |
✅ | ❌ 暂未包含备份运行时 |
| 原生二进制 | ✅,但不读取 Copilot live SQLite | ❌ 暂未包含备份运行时 |
| 源码 checkout | ✅ | ✅ |
其他安装方式与运行时差异
从 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 allWindows 下载 asmgr-windows-x64.exe。每个 Release 附带 SHA256SUMS.txt 可校验完整性。
一处限制: 二进制基于 Bun,而 Bun 目前未实现
node:sqlite,因此读取 Copilot 实时 SQLite 库这一个数据源在二进制里会静默跳过(其余数据源——各家*.jsonl、--file指向的任意文件/目录——都正常)。需要该数据源请改用 Node 安装。
安装时 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-*。
-
按上面任一方式装好
asmgr。 -
列出某个 agent 的会话:
asmgr list --agent copilot
-
从第二列复制一个 session id。
-
生成 HTML:
asmgr html <session-id> --agent copilot -o report.html
-
用浏览器打开
report.html。
HTML 文件是自包含的:搜索、筛选、可折叠条目、侧栏目录、紧凑模式、主题切换、Markdown 表格、数学渲染都离线可用。
默认按源文件 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-guardianlist --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 格式说明。本机全量读取、成品验证、独立审阅与残余边界见 验证记录。
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搜索 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 才使用。
导入公开 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 状态。
以 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 则含完整工具参数+结果、子代理/技能/计划/压缩统计。
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 的角色过滤仍与主干过滤取交集。
写出一份自包含 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.htmlHTML 与 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 时间线与离线映射」。
导出遵循 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.mdbackup 是正式的 restic 备份命令组:
asmgr backup run --dry-run
asmgr backup run
asmgr backup cache latest --target ~/.cache/asmgr/restic-cachebackup 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 runRESTIC_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。
--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显式指定的文件或目录。
重要决策的理念记录在 docs/adr/:
实现参考: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/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 中自动完成:
- 推导下个版本并生成 release notes,更新
CHANGELOG.md与package.json的版本。 - 构建 Node 单文件 bundle、Linux x64 / macOS Intel / macOS Apple Silicon / Windows x64 四平台二进制及
SHA256SUMS.txt。 - 将
package.json和CHANGELOG.md以chore(release): X.Y.Z [skip ci]提交回main,并创建、推送vX.Y.Ztag。 - 发布公开 npm 包
asmgr,创建 GitHub Release,附上 notes、二进制、Node bundle 和校验和。
npm 发布走 OIDC Trusted Publishing(npmjs environment),GitHub 操作使用工作流的 GITHUB_TOKEN。
它是直接发布,不是先生成等待人工确认的 npm 暂存版本。
不准备发布时,将提交保留在工作分支并只推该分支,例如:
# 从当前提交创建工作分支;分支名按需替换
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推送或手动发布仍会分析并可能发布它。 - 因而它只适合临时跳过一次触发,不能作为长期发布闸门,也不能防止其他维护者后续推送带出该变更。
-
维护者先核对目标
main提交 SHA、上个发布 tag 之后的全部变更与预期版本,确认这些内容都允许公开发布。 -
明确批准后,再向
main普通推送 / 合并以启动自动发布;若待发布提交已在main(例如此前用了[skip ci]), 可在 GitHub Actions →release→ Run workflow 选择main,或执行:# 真正启动发布,不是 dry-run;只在明确批准后执行 gh workflow run release.yml --ref main -
检查运行结果,以及回写的版本 / CHANGELOG、
vX.Y.Ztag、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。