几个相互独立的小工具:zcode_export.py 把 ZCode 聊天历史导出为可读档案(Markdown / JSON);zcode_to_opencode.py 直接把 ZCode 会话迁移到 OpenCode。二者互不依赖。另有 zcode_import.py 负责把旧版 ZCode 会话快照恢复到现在版本(在这个脚本组中是用来把 zcode_export.py 导出的会话重新导入用的)。
ZCode 与 OpenCode 的数据格式高度一致(会话/消息/部件三类表、sess_ / msg_ / part_ 的 ID 前缀、消息 JSON 结构几乎相同),因此可以直接复用 OpenCode 生态的存储与导入能力。本仓库提供三个脚本:
zcode_export.py:从 zcode 数据库只读导出会话,生成可读的 Markdown、结构化 JSON,或 opencode 原生导入格式。zcode_to_opencode.py:把 zcode 会话导入 opencode(数据库 + 项目/目录绑定),并记录导入状态。zcode_import.py:把旧版 ZCode 会话快照恢复到新版数据库(混合后端,官方插件优先、Python 移植回退)。
这些脚本都是纯 Python 标准库实现;只需 Python 3.7+ 。
两个脚本都以只读方式(?mode=ro)连接 ZCode 的 SQLite 数据库:
~\.zcode\cli\db\db.sqlite
脚本不会写入或修改 ZCode 数据库。
| 依赖 | 是否必需 | 用途 |
|---|---|---|
| Python 3.7+ | 必需 | 运行脚本(纯标准库,无第三方依赖) |
opencode CLI |
仅导入时必需 | 真正执行导入;导出脚本只用它读取版本号(缺失时自动回退为 "2") |
git |
仅 git 目录需要 | 仅当会话目录属于 git 仓库时,用于解析项目 ID |
python zcode_export.py list
python zcode_export.py export <编号|会话ID> [all] [-t <格式>] [--out <目录>]
python zcode_export.py list
按时间升序列出全部会话:编号、时间、消息数、标题与 zcode 会话 ID。
按编号或 ID 导出(可多个):
python zcode_export.py export 2 5
python zcode_export.py export all
python zcode_export.py export sess_4c94759e-b5f6-4d37-9124-abc69a65f643
用 -t / --type 选择输出格式(可多选,逗号或空格分隔;不给出时默认 md,json):
python zcode_export.py export 2 -t opencode
python zcode_export.py export all -t md,json
python zcode_export.py export 2 -t md,opencode --out D:\backup
python zcode_export.py export 2 -t snapshot
四种格式,每个会话生成同名前缀的一组文件:
| 格式 | 文件名 | 说明 |
|---|---|---|
md |
<时间>_<标题>.md |
可读的完整对话记录,含思考过程(> THINKING: 引用块)与工具调用摘要 |
json |
<时间>_<标题>.json |
完整结构化数据,保留每条消息原始信息和全部 part(含 part ID) |
opencode |
<时间>_<标题>.opencode.json |
opencode 原生导入格式,便于将来从档案恢复会话;日常迁移请直接用 zcode_to_opencode.py |
snapshot |
<workspaceHash>/<会话ID>.json |
ZCode 旧版快照格式(meta + messages),配合 zcode_import.py 恢复,见「工具三」 |
默认输出到命令执行时所在的工作目录;--out 可指定其他目录。snapshot 格式的 --out 目录即作为旧版快照根(--legacy-dir),便于直接交给 zcode_import.py 导入。
注意:zcode_to_opencode.py 是一个独立运行的转换脚本:它直接从 ZCode 数据库读取会话,一次性完成“格式转换 → opencode import → 项目/目录绑定”,无需事先运行 zcode_export.py。
python zcode_to_opencode.py list
python zcode_to_opencode.py import <编号|会话ID> [--with-children]
python zcode_to_opencode.py import all
python zcode_to_opencode.py status
python zcode_to_opencode.py list # 会话列表 + 是否已导入
python zcode_to_opencode.py status # 查看 zcode ID → opencode ID 的完整映射
导入状态保存在 zcode-import-state.json,脚本会记录每个会话的导入结果,已导入的会话自动跳过,重复执行也不会产生重复数据。
python zcode_to_opencode.py import 2 5
python zcode_to_opencode.py import all
- 支持
--with-children:subagent_child类型的会话会与其父会话一并导入,并保持父子嵌套关系。 - 每个目标会话独立处理(try/except 隔离),单个失败不影响其他会话。
- 读取会话消息,构造 opencode 原生导入 JSON(生成
ses_/msg_/prt_形式的 ID,沿用原msg_ID,重排 part ID)。 - 调用
opencode import写入 opencode 数据库。 - 立即把导入结果写入状态文件,就算后续步骤中途失败,也不会重复导入。
- 按 move-session 规则重绑项目的
project_id/directory/path。
zcode_import.py 把 ZCode 旧版 ACP 时代的会话快照(sess_*.json,通常在 ~/.zcode/v2/sessions 或导出根目录下)恢复到新版 ZCode 的数据库(tasks-index + CLI session 库)。它是混合后端:默认优先调用官方插件的 node 脚本,环境不可用时自动回退到内置的纯 Python 移植(行为与官方几乎一致)。纯 Python 后端只需 Python 3.7+ 标准库。
python zcode_import.py list [--legacy-dir DIR] [--agent A] [--workspace W]
[--query Q] [--conversation ID] [--limit N] [--json]
python zcode_import.py status [同 list 的过滤/目录选项]
python zcode_import.py import <快照路径, ...> | <序号, ...> | <目录> | all
[--legacy-dir DIR] [--task-index PATH] [--cli-db PATH]
[--dry-run] [--show] [--no-show] [--backup]
python zcode_import.py unarchive <taskId, ...> | all [--task-index PATH]
list 的 # 列是一个与过滤无关的全局编号,它对 --legacy-dir 下全部快照按 updatedAt 降序统一编号(跨官方 / python 后端一致)。因此:
import <n>直接用这个编号定位快照,不受任何过滤 /--limit影响。即便先用了--query/--agent筛选,import的序号仍指"全库第 n 个",可稳定依赖。- 带过滤时列表可能跳号(比如只显示
2, 5, 8),这是有意设计:编号始终指全局位置。 - 序号、文件路径、目录、
all四种目标可混合使用。
python zcode_import.py list # 会话列表(含全局 # 与各会话状态)
python zcode_import.py list --query "testfile" # 按标题/消息内容/ID 过滤
python zcode_import.py list --limit 10 --json # 截断显示 / JSON 输出
python zcode_import.py status # 汇总:按 agent 与恢复状态分组
每一行会显示该会话的恢复状态:ready(双库就绪)、needs-cli-db / needs-task-index / needs-full-import(缺哪补哪)。
python zcode_import.py import 1 # 按全局编号导入第 1 个快照
python zcode_import.py import 1 3 5 # 多个
python zcode_import.py import a7b9685c5488 # 直接给工作区目录(递归展开 sess_*.json)
python zcode_import.py import . # 导出根目录(多工作区 hash 子目录)
python zcode_import.py import all # 全部
python zcode_import.py import sess_xxx.json # 或直接给单个快照文件
几个关键选项:
--dry-run:只打印将要执行的内容,不写任何库。--show/--no-show(默认--show):导入成功后自动把任务设为可见(archived=0, deleted=0, pinned=0, unread_at=now)。ZCode 的任务列表只显示deleted=0的行,且会自动归档旧的已完成会话,所以不加可见处理会让刚恢复的老会话从 App 里"消失"。--no-show关闭。--backup(默认关闭):保留导入前对两个数据库的时间戳备份。官方后端每次导入本就强制生成备份——不加--backup时会在导入成功后删除本次生成的备份(如同从未备份),导入失败时则保留;加--backup则始终保留。Python 后端不加--backup时根本不生成备份。--legacy-dir DIR:快照根目录(默认~/.zcode/v2/sessions),import的序号 / 目录展开基于它。
zcode_import.py 既能恢复官方旧库里的原生快照,也能导入 zcode_export.py 导出的 snapshot 格式,从而形成完整的备份 / 迁移闭环:
python zcode_export.py export 2 5 -t snapshot # 1. 先把会话导出为旧版快照
python zcode_import.py import <导出目录> --legacy-dir . # 2. 再把快照恢复到新版 ZCode 。
要点:
zcode_export.py -t snapshot的产出布局是<workspaceHash>/<会话>.json,正是zcode_import.py import <目录>可直接递归展开的形式,也是官方 scanner 扫描的两层结构。zcode_export.py --with-children会连同subagent_child后代一并导出;snapshot 格式保留forkedFromTaskId,保证分叉树在新库中完整重建。- 因此仓库内
export与import互为逆操作,适合备份、迁移与换机场合。 zcode_export.py一般输出目录是当前目录,而zcode_import.py脚本默认从 ZCode 的旧版会话目录读取,需要--legacy-dir .选项指定程序从当前目录读取。
默认导入时会自动取消隐藏,如果导入后找不到可以尝试手动。
python zcode_import.py unarchive all # 把任务索引里所有任务设为可见
python zcode_import.py unarchive sess_xxx... # 只处理指定 taskId
unarchive 只清理任务索引里已有的隐藏标记,不做任何导入。
opencode import 只能识别 opencode 原生导出结构:
{
"info": { "id": "ses_...", "slug": "...", "title": "...", "version": "...", "time": {...}, ... },
"messages": [
{ "info": { "id": "msg_...", "sessionID": "ses_...", ... },
"parts": [ { "id": "prt_...", "sessionID": "ses_...", "messageID": "msg_...", ... } ] }
]
}转换时自动处理这些差异:
- ID 前缀:消息必须以
msg_开头(zcode 原 ID 即可),part 必须以prt_开头(zcode 的是part_,需重排),会话必须以ses_开头。 - 未知 part 类型:zcode 的
timeline、compaction等不符合 opencode schema,导入时被过滤,只保留text/reasoning/tool/step-start/step-finish。 error字段:zcode 标记失败尝试的错误对象(如AiSdkModelAdapterError)会让 schema 校验失败,导入前删除。- 任一消息或 part 校验不通过,整个导入会失败,因此转换严格对齐 schema。
项目 / 目录绑定(move-session 规则)
opencode import 会把会话绑定到执行命令时的当前目录,而不是文件里写的目录。因此导入脚本在写入后立即重绑。绑定规则:
-
会话
path= 目录去掉盘符后的正斜杠路径。 -
非 git 目录 → 项目为
global,path为相对路径。 -
git 目录 → 按优先级解析项目:
- 已存在且 worktree 相同的项目;
sha1("git-remote:<标准化后的 remote>");.git/opencode缓存;- 仓库根提交哈希。
项目行缺失时自动注册。
- zcode 的普通会话(
interactive)和分叉会话(fork)导入 OpenCode 后为根会话,出现在/session顶层列表。 - 子代理会话(
subagent_child)保留parentID嵌套在父会话下,不在顶层列表显示——这与 opencode 原生行为一致。
- 建议导入前完全退出相应程序的实例。运行中的旧实例会缓存数据并在退出时回写,可能覆盖或"复活"刚导入/删除的会话。
- Windows 下 Python 的
subprocess找不到opencode时,请确保opencode.exe或opencode.cmd在PATH中。 zcode_import.py如果使用官方导入脚本作为后端,需要node版本 >= 22.5 。
zcode-export/
├── README.md
├── zcode_export.py # 导出脚本(md / json / opencode)
├── zcode_to_opencode.py # 导入脚本(zcode → opencode)
├── zcode_import.py # 恢复脚本(旧版快照 → 新版 ZCode)
└── zcode-import-state.json # 导入状态(用于避免重复导入,已 gitignore)