@zentao-agent/cli
v0.17.0
Published
AI-agent toolkit CLI (`zt`) for ZenTao — installs agent integrations, manages the bug hub + repos.yaml registry, exposes direct ZenTao API commands via @zentao-agent/sdk, and ships a built-in stdio MCP server (`zt mcp`).
Maintainers
Readme
@zentao-agent/cli
English | 简体中文
不是禅道官方的
zentao-cli。 本包是面向 AI 编码代理的禅道工具链 CLI:提供基于@zentao-agent/sdk的zt api ...命令直接调用禅道 REST API,安装 agent 集成(commands / prompts / skills、hook、协作规则文件),并内置供桌面客户端使用的 stdio MCP server。请和官方独立 CLI 区分使用。
它做的事情:
- 把
zt-fix-bug、zt-bug-analyze装到目标 agent 的 workflow 入口(统一zt-前缀,标记为本 CLI 所属入口,避免与用户/团队自定义的同名入口冲突)--agent claude-code→~/.claude/commands/*.md--agent codex-desktop/codex-cli→$HOME/.agents/skills/<name>/SKILL.md--agent copilot-local→<repo>/.github/prompts/*.prompt.md--agent copilot-cli→~/.copilot/skills/<name>/SKILL.md--agent qoder-cli/qoder-ide→~/.qoder/skills/<name>/SKILL.md--agent qoder-cn-cli/qoder-cn-ide→~/.qoder-cn/skills/<name>/SKILL.md--agent claude-desktop→ 没有 command/prompt 等价物,只装 MCP
- 按 agent 渲染 workflow 内容:Claude Code 保留
AskUserQuestion指引;Codex / Copilot / Qoder 输出改用各自正常的用户交互机制,不再出现 Claude 专属工具名。 - 在目标 agent 的配置里注册内置的
zt mcpserver——默认只为桌面客户端注册(claude-desktop、codex-desktop、copilot-local、qoder-ide、qoder-cn-ide);编码代理直接用zt api ...。可用--mcp/--no-mcp覆盖。 - 把 commit-msg hook(拦 AI 署名 + 校 Conventional Commits)装到目标代码库
- 按 agent 复制协作规则文件:
CLAUDE.md(Claude Code)/AGENTS.md(Codex / Copilot CLI / Qoder)/.github/copilot-instructions.md(Copilot in VS Code) - 初始化 / 查看 bug-context workspace —— 每个 profile 一个 workspace,在
<$ZENTAO_AGENT_HOME 或 ~/zentao-agent>/<profile>/ - 为每个 bug 生成一个
.code-workspace,让 VS Code 一次打开 bug 上下文和它的 worktree - 维护每个 profile workspace 里的
repos.yaml注册表(productId → 多个代码库) - 通过
zt api <resource> <operation>直接调用 SDK 的禅道 API 操作;通过zt api request调用未显式包装的 REST 端点。 - 一键卸载:不带
--agent时清扫全部受支持 agent 的 user+workspace 两个 scope(MCP 注册含旧名、工作流含旧前缀、指令文件、hook);可用--agent <id>或 target 收窄。
api命令可直接 CRUD 禅道对象;但 CLI 不替 agent 跑修 bug 工作流。读截图、调研代码、审查决策这些仍然走已安装的 LLM workflow。
支持的 agent
| Agent ID | 说明 | Workflow 入口 | MCP 配置文件 | 协作规则文件 |
| ----------------- | ----------------------- | ------------------------------------------ | -------------------------------------------------------- | --------------------------------- |
| claude-desktop | Claude Desktop | 无 | claude_desktop_config.json | 无 |
| claude-code | Claude Code CLI | ~/.claude/commands/*.md | ~/.claude.json 或 <repo>/.mcp.json | CLAUDE.md |
| codex-desktop | Codex Desktop | $HOME/.agents/skills/<name>/SKILL.md | ~/.codex/config.toml 或 <repo>/.codex/config.toml | AGENTS.md |
| codex-cli | Codex CLI | $HOME/.agents/skills/<name>/SKILL.md | ~/.codex/config.toml 或 <repo>/.codex/config.toml | AGENTS.md |
| copilot-local | GitHub Copilot Local | <repo>/.github/prompts/*.prompt.md | 用户 User/mcp.json 或 <repo>/.vscode/mcp.json | .github/copilot-instructions.md |
| copilot-cli | GitHub Copilot CLI | ~/.copilot/skills/<name>/SKILL.md | ~/.copilot/mcp-config.json 或 <repo>/.mcp.json | AGENTS.md |
| qoder-cli | Qoder CLI | ~/.qoder/skills/<name>/SKILL.md | ~/.qoder/settings.json 或 <repo>/.mcp.json | AGENTS.md |
| qoder-ide | Qoder IDE | ~/.qoder/skills/<name>/SKILL.md | ~/.qoder/mcp.json(仅用户级) | AGENTS.md |
| qoder-cn-cli | Qoder CN CLI | ~/.qoder-cn/skills/<name>/SKILL.md | ~/.qoder-cn/settings.json 或 <repo>/.mcp.json | AGENTS.md |
| qoder-cn-ide | Qoder CN IDE | ~/.qoder-cn/skills/<name>/SKILL.md | ~/.qoder-cn/mcp.json(仅用户级) | AGENTS.md |
兼容旧 ID:claude → claude-code,codex → codex-cli,copilot → copilot-local,qoder → qoder-cli,qoder-cn → qoder-cn-cli。
Qoder 补充:上表列的是 user scope。加 --scope workspace 时,除 qoder-cn-ide 用 <repo>/.agents/skills/<name>/SKILL.md 外,其余 Qoder id 的 skill 都装到 <repo>/.qoder/skills/<name>/SKILL.md;qoder-cn-ide 的项目级路径是官方写明的,用户级 ~/.qoder-cn/skills 和对 AGENTS.md 的支持则是按配置目录和国际版 IDE 推断出来的,属于尽力而为。用户目录按版本区分(国际版 ~/.qoder,国内版 ~/.qoder-cn,可用 $QODER_CONFIG_DIR / $QODERCN_CONFIG_DIR 覆盖,或用 $QODER_CLI_HOME / $QODERCN_CLI_HOME 指定其父目录),但项目级目录两个版本都是 .qoder。两个 IDE id 的 MCP 只写用户级配置,因为 Qoder IDE 对项目级 MCP 配置的支持并不可靠;CLI id 可以写 <repo>/.mcp.json,但这类 server 要在受信任目录里逐个确认(或打开 mcp.enableAllProjectMcpServers)。装完后,正在运行的 Qoder CLI 会话执行 /mcp reload 和 /skills reload(新开会话会自动加载),用 qoder mcp list(国内版 qoderclicn mcp list)验证;IDE 没有热加载,需要重启。
安装
npm install -g @zentao-agent/cli或本 monorepo 内开发(使用 pnpm):
corepack enable
pnpm install
pnpm run build
node packages/cli/dist/index.js help用法
zt <group> <command> [options]
login / logout 登录 / 删除已保存凭据(成对)
profile whoami|list|use
查看已保存的 profile 并设置默认
install / uninstall 安装 / 移除 agent 集成(成对;uninstall 默认清扫所有 agent)
update / migrate 升级当前 CLI / 从 zentao-hub 时代的安装迁移
workspace init|show|path|bug|config
管理当前 profile 的 hub workspace
repo register|list 维护当前 profile 的 repos.yaml 注册表
api 直接调用 SDK 的禅道 API(与 REST 端点 1:1);
见 `zt api help [resource]`
bug list-my|history|download-attachments
基于 API 组合的 bug 复合操作
task list-my 任务复合操作
mcp 启动内置 stdio MCP server(桌面客户端用)
help <group> 打印某个分组的帮助
全局选项:
--lang <en|zh|auto> 输出语言;默认根据环境自动判断跑 zt help 看完整选项,或用 zt help profile|install|workspace|repo 查看某个分组。help 语言会读取 ZENTAO_AGENT_LANG、LANGUAGE、LC_ALL、LC_MESSAGES、LANG;单次命令可用 --lang <en|zh|auto> 覆盖。
机器可读输出(--json)
zt repo list、zt workspace show / zt workspace path、zt profile list、zt profile whoami 默认打印给人看的文本,加 --json 则在 stdout 输出单个 JSON 对象;再加 --compact 输出压缩成一行(和 zt api、zt bug 的同名参数一致)。已安装的 workflow 就靠它,agent 不需要自己解析 repos.yaml,也不需要去切分对齐的文本列。
zt repo list 27 --json
# { "profile": "default",
# "registryPath": "/Users/you/zentao-agent/default/repos.yaml",
# "products": { "27": [ { "name": "eda-schematic", "path": "/…/eda-schematic", "tags": ["frontend"] } ] } }可以依赖的约定:
zt repo list <productId> --json一定会输出这个 productId 这个 key。空数组表示「登记了 0 个仓库」,不是失败——只有非 0 退出码才算失败。repos.yaml不存在同样不算错误:会输出"products": {}(带 productId 过滤时是{"<productId>": []})。zt workspace path --json输出完整的 workspace 记录,不再只打印一行路径;不带--json的zt workspace path行为不变。--json输出永远不含 token 或密码。zt profile whoami --json用hasToken: true|false代替文本输出里那个打码 token。- 参数顺序无所谓。
--json和--compact都不接收值,所以zt repo list --json 27和zt repo list 27 --json是同一条命令,27不会被当成参数值吃掉。
禅道 API 命令(api)
API 命令统一放在 zt api ... 命名空间下,避免和 install、profile、repo、workspace 这些 Hub 本地命令分组混淆。认证沿用 zt login 写入的 profile,也支持 ZENTAO_URL / ZENTAO_TOKEN / ZENTAO_USERNAME / ZENTAO_PASSWORD 环境变量。
常用格式:
zt api products list --query limit=20
zt api bugs get 123
zt api bugs create 27 --field title='登录崩溃' --field severity=2
zt api tasks finish 456 --data '{"consumed":1,"left":0}'
zt api request GET /products/27/bugs --query status=active通用 API 参数:
--profile <name>/--credentials <path>:选择认证来源。--query <key=value>:查询参数,可重复,用于 list/raw request。--data <json>/--data-file <path|->:JSON 对象请求体。--body和--json是--data的别名。--field <key=value>:请求体字段,可重复;值会尽量按 JSON 解析,例如2、true、["a"]。--compact:输出压缩 JSON;默认输出格式化 JSON。
这里的
--json与 CLI 其他命令含义不同。zt api本来就只输出 JSON,没有可切换的输出格式,所以--json <body>是--data的 curl 风格别名。建议统一用--data,把--json留给真正用它选择 JSON 输出的命令。
请求体校验
高频写操作和同名 MCP 工具共用同一份请求体 schema,两条路径的字段名、枚举值、日期格式和必填项完全一致:bugs create / update / resolve / close / assign-to、stories create / update / change / review、tasks create / update / finish / assign-to、todos create / update。由 --field / --data / --data-file 拼出的请求体会在发请求前校验,失败时直接指出出错字段:
$ zt api bugs create 27 --field title='登录崩溃' --field type=codeeror
invalid request body for bugs create:
type: expected codeerror|interface|config|install|security|performance|standard|automation|designdefect|others, received "codeeror"
run 'zt api help bugs create' to see the accepted fields校验刻意不用 strict 模式:禅道接受的字段比 schema 枚举的多,所以 schema 没有描述的字段原样透传,不会被拒绝。没有挂 schema 的操作行为和以前完全一样。
zt api help <resource> <operation> 会打印可用字段的类型、必填 / 可选、枚举取值和说明,agent 不用再猜字段名。在 zt api help <resource> 里,这些操作带 [fields] 标记。
一个容易踩的点:--field k=v 会尽量把 v 按 JSON 解析,所以 --field title=2026 得到的是数字 2026,string 字段会拒绝它。此时用引号写成 --field title='"2026"',或改用 --data。
完整 typed API 命令面如下。CLI 内 zt api help 默认打印紧凑资源摘要;zt api help <resource> 展开单个资源;zt api help all 打印全部操作。
| API 资源 | 操作 |
| --- | --- |
| bugs | list-by-product <productId>, list-by-execution <executionId>, get <bugId>, create <productId>, update <bugId>, resolve <bugId>, close <bugId>, confirm <bugId>, activate <bugId>, assign-to <bugId>, delete <bugId>, get-actions <bugId>, get-history <bugId> |
| builds | list-by-product <productId>, list-by-execution <executionId>, get <buildId>, create <executionId>, delete <buildId> |
| cases | list-by-product <productId>, get <caseId>, create <productId>, update <caseId>, delete <caseId>, create-result <caseId>, list-results <caseId> |
| docs | list-libs, list-by-lib <doclibId>, get <docId>, create <doclibId>, update <docId>, delete <docId> |
| efforts | create, list, update <effortId>, delete <effortId> |
| executions | list, get <executionId>, create, update <executionId>, start <executionId>, close <executionId>, delete <executionId>, list-tasks <executionId>, list-stories <executionId>, list-team <executionId> |
| issues | list-by-project <projectId>, get <issueId>, create <projectId>, update <issueId>, resolve <issueId>, close <issueId>, delete <issueId> |
| products | list, get <productId>, create, update <productId>, delete <productId>, list-branches <productId>, list-modules <productId>, list-plans <productId>, list-executions <productId>, list-testsuites <productId>, list-testtasks <productId> |
| programs | list, get <programId>, create, delete <programId> |
| projects | list, get <projectId>, create, update <projectId>, delete <projectId>, list-executions <projectId>, list-products <projectId> |
| releases | list-by-product <productId>, get <releaseId>, create <productId>, delete <releaseId> |
| repos | list, get <repoId>, list-branches <repoId>, list-commits <repoId> |
| risks | list-by-project <projectId>, get <riskId>, create <projectId>, update <riskId>, delete <riskId> |
| stories | list-by-product <productId>, get <storyId>, create <productId>, update <storyId>, change <storyId>, review <storyId>, close <storyId>, activate <storyId>, assign-to <storyId>, delete <storyId> |
| tasks | get <taskId>, list-by-execution <executionId>, create <executionId>, update <taskId>, start <taskId>, finish <taskId>, pause <taskId>, activate <taskId>, close <taskId>, cancel <taskId>, assign-to <taskId>, delete <taskId> |
| testreports | list-by-product <productId>, get <testreportId>, create <productId>, delete <testreportId> |
| todos | list, get <todoId>, create, update <todoId>, finish <todoId>, delete <todoId> |
| users | list, get <account>, list-departments, list-by-department <departmentId> |
未覆盖的端点使用 raw request:
zt api request GET /user
zt api request POST /some/path --data '{"name":"demo"}'复合命令(bug、task)
api 分组保持与禅道 REST 端点的干净 1:1 映射;由多次 API 调用或本地 I/O 组合出来的业务逻辑放在独立的顶层分组里:
# 某个产品下指派给我的 bug(逐页拉取后本地过滤,
# 因为禅道 v1 的 assignedTo/status 查询参数在部分版本上不可靠)。
zt bug list-my <productId> [--status active|resolved|closed|all] [--limit <n>] [--max-scan <n>]
# 带端点回退的操作/备注历史(/actions → /history → 内嵌字段)。
zt bug history <bugId>
# 下载 bug.files 及重现步骤里的内联 <img>。
zt bug download-attachments <bugId> --dest <dir> [--no-inline]
# 某个执行下指派给我的任务。
zt task list-my <executionId> [--status ...] [--limit <n>] [--max-scan <n>]它们接受与 api 命令相同的 --profile / --credentials / --compact 选项,结果以 JSON 输出到 stdout。
zt bug history 和 zt bug download-attachments 还会作为副作用顺手刷新该 bug 的 .code-workspace 文件——即便 bug 修复流程漏掉了显式的 zt workspace bug ... 调用,VS Code「Open Workspace」依然能用。想关掉就加 --no-workspace。
内置 MCP server(zt mcp)
zt mcp(或 zt mcp serve)用同一个二进制起一个 stdio MCP server——zt install 给桌面客户端注册的就是它。编码代理更适合直接用 zt api ...:命令不跑就不占上下文。
stdout 是 JSON-RPC 通道。 包括下面这行启动信息在内的所有诊断输出都走 stderr,因此本 CLI 打印的任何内容都不会污染客户端的传输通道。
$ zt mcp serve
zentao-agent mcp: tools=core, read-only=no, 50 tools registered # ← stderr选择工具集
桌面客户端每注册一个工具都要占用上下文窗口,所以默认工具集刻意收窄。
| 变量 | 取值 | 作用 |
| --- | --- | --- |
| ZENTAO_MCP_TOOLS | core(默认) | 约 50 个工具:只读接口,加上 bug / story / task / todo 的状态流转。 |
| | all | 全部工具(约 126 个)。 |
| | a,b,c | 精确指定这些工具名;出现未知名字直接报错。 |
| ZENTAO_MCP_READONLY | 1、true、yes | 从已选中的工具集里剔除所有非只读工具。其他取值一律视为关闭。 |
两者互相正交、可以叠加——只读过滤在工具集选定之后才执行:
ZENTAO_MCP_READONLY=1 zt mcp # 25 个工具,全部只读
ZENTAO_MCP_TOOLS=all ZENTAO_MCP_READONLY=1 zt mcp # 58 个工具,全部只读每个工具都显式声明自己是读、写还是不可撤销的删除,只读模式不需要靠工具名去猜。它同时是白名单机制:万一某个工具在运行时没有合法的读写分类,会被当成写工具剔除。如果在 ZENTAO_MCP_READONLY 生效时,ZENTAO_MCP_TOOLS 的显式列表里点名了写工具,那属于配置错误而不是可以悄悄忽略的情况——server 会拒绝启动并指出是哪几个工具冲突。
不可撤销的工具要求 confirm: true
禅道无法恢复已删除的记录,因此每个 delete_* 工具都有一个必填的 confirm 参数,类型就是字面量 true:
{ "name": "delete_bug", "arguments": { "bugId": 123, "confirm": true } }由于 schema 只接受 true,模型既不能靠省略这个字段、也不能靠传 confirm: false 来删除任何东西——两种情况都会在构造请求之前就被 schema 校验拦下。该参数的描述会明确告诉模型:只有用户明确要求删除这一条记录时才允许传。若想干脆不暴露这些工具,用 ZENTAO_MCP_READONLY=1。
典型一次性安装流程
下面以 Claude Code 为例(--agent claude-code)。把 --agent 换成其他支持的 agent 即可;凭据、workspace、注册表跨 agent 共用。
# 1. 装 CLI + 登录。桌面客户端用的 MCP server 内置在同一个二进制里
# (`zt mcp`),只需要装这一个包。
npm install -g @zentao-agent/cli
zt login
# 2. 安装 agent 集成。若当前目录不是 git repo,只安装 user/global 部分
# (commands/prompts/skills;桌面 agent 额外注册 MCP),repo 文件会跳过。
zt install --agent claude-code
# 3. 初始化 bug-context workspace(与具体 agent 无关)
zt workspace init
echo 'export ZENTAO_AGENT_HOME=$HOME/zentao-agent' >> ~/.zshrc
# 4. 每个目标代码库:安装 repo 文件/hook + 登记 productId
# 如果 global 部分已经装过,可加 --no-mcp --no-workflows。
cd ~/projects/venus-fe
zt install --agent claude-code --no-workflows
zt repo register 2 . --name venus-frontend --tag 前端 --tag electron之后通过所选 agent 已安装的入口启动 workflow:
Claude Code slash command:
/zt-bug-analyze 9634 /zt-fix-bug 27 /zt-fix-bug 27 9634 /zt-fix-bug 27 9634,9635,9636 /zt-fix-bug 27 9634 autoCodex / Copilot CLI / Qoder skill:让 agent 运行
zt-bug-analyze 9634、zt-fix-bug 27 9634或zt-fix-bug 27 9634 auto。- Qoder IDE 里在 Agent 对话框输入
/选择该 skill。
- Qoder IDE 里在 Agent 对话框输入
VS Code Copilot prompt:在 Copilot Chat 中调用已安装的
zt-bug-analyze/zt-fix-bugprompt。
第二个参数可以是逗号分隔的一批相关 bug id —— 适用于同一功能(如一个过滤器有多个条件)按症状各提一个 bug 的情况。流程会逐个下载 bug 上下文、写一份合并分析、在以首个(主)id 锚定的单一 worktree/分支上一次性修复,并在收尾时把每个 bug 都在禅道解决。
install / uninstall 会怎样动你的配置文件
注册(或移除)MCP server 意味着要改一个不属于本 CLI 的文件——claude_desktop_config.json、Codex 的 config.toml、Qoder 的 settings.json、仓库里的 .mcp.json——这些文件里还存着别的工具注册的 server。每次改写都有两条保证:
原子写入。 新内容先写到同目录的临时文件,再 rename 覆盖目标,因此崩溃或磁盘写满都不会把配置截断成只剩本 CLI 那一条。写之前会先解析符号链接,dotfile 里的软链仍然是软链;文件权限位保持不变。
保留备份。 覆盖前先把原内容复制成
<file>.zt-backup-<时间戳>,并把路径打印出来:$ zt install --agent claude-code --mcp == mcp == Configuring MCP 'zentao-agent' for Claude Code CLI → /Users/you/projects/app/.mcp.json (json, workspace) backup /Users/you/projects/app/.mcp.json.zt-backup-2026-08-31T03-44-22-224Z ok zentao-agent
其中有些文件是 JSONC——Qoder 的 settings.json、VS Code 的 mcp.json 里可能有 // 注释——读取时能容忍注释,但改写不会保留它们,原文保存在带时间戳的备份里。
只有目标文件本来就存在时才会产生备份——首次安装是新建配置,不会打印 backup 那一行。每个文件只保留最近 5 份备份,确认结果无误后请自行删除。如果注册的是要提交进仓库的 .mcp.json,记得在该仓库的 .gitignore 里加上 *.zt-backup-*。
一键卸载
zt uninstall # 全卸:所有 agent 的 MCP 注册 + 工作流文件 + 当前仓库的协作规则文件与 hook
zt uninstall --repo ~/projects/foo
zt uninstall --keep-hooks-path需要更细粒度:
zt uninstall commands
zt uninstall hooks --repo .
zt uninstall claude-md --repo .
zt uninstall mcp --agent claude-code凭据和 workspace 不会被动 —— 那是用户数据,用 zt logout --all 删凭据,rm -rf <hub-root>/<profile> 删 workspace。
Workspace 目录结构
$ZENTAO_AGENT_HOME(默认 ~/zentao-agent;改名前的 ~/zentao-hub 和 ~/.zentao-hub 存在时会自动沿用,升级不会丢数据,迁移命令:zt migrate)是 hub root,每个 profile 一个 workspace:<root>/<profile>/。这样多个禅道实例(比如不同客户)的 bug 上下文、注册表、附件天然隔离。zt workspace init --profile <name> 之后:
$ZENTAO_AGENT_HOME/
├── credentials.json ← 跨 profile 共用
├── default/ ← profile workspace
│ ├── repos.yaml ← 注册表:productId → 代码库列表
│ ├── repos.yaml.example ← 模板
│ └── bugs/ ← 每个 bug 一个子目录,由已安装 workflow 写入
│ └── <bugId>/
│ ├── raw.json
│ ├── description.md
│ ├── history.md
│ ├── analysis.md
│ ├── bug-<bugId>.code-workspace ← `zt workspace bug <bugId>` 生成
│ ├── attachments/
│ │ ├── <id>-<title>.<ext>
│ │ └── inline/
│ │ └── inline-N-<basename>
│ └── worktrees/ ← fix commands 把每个仓库的 git worktree 放这里
│ ├── <repo-a>/ ← 分支 fix/bug-<bugId>,从 main 切出
│ └── <repo-b>/
└── client-foo/ ← 第二个 profile,完全隔离
├── repos.yaml
└── bugs/workflow command / prompt / skill 通过当前的 $ZENTAO_PROFILE(CLI 和内置 MCP server 鉴权也用这个)来挑 workspace。zt 在 $PATH 上时,会跑 zt workspace path --profile $ZENTAO_PROFILE 拿到目录。
持久化 hub root 和 workspace(config)
与其在每个 shell 里 export $ZENTAO_AGENT_HOME、或在每条命令上带 --workspace,不如把偏好一次性持久化:
zt workspace config hub-root ~/work/zentao-agent # 给所有 profile 换 hub root
zt workspace config workspace ~/work/foo --profile foo # 给某个 profile 指定完全自定义的 workspace
zt workspace config # 查看生效值,以及各自来源
zt workspace config hub-root --unset # 还原成默认
zt workspace config workspace --unset --profile foo这些写到 ~/.config/zentao-agent/settings.json(或 $XDG_CONFIG_HOME/zentao-agent/settings.json;设 $ZENTAO_AGENT_SETTINGS 可让所有命令都指向同一个文件)—— 刻意放在 hub root 之外,因为 hub root 本身就存在这里。该文件不含任何密钥(不同于 credentials.json)。
解析优先级,从高到低:--workspace/--flag → 环境变量($ZENTAO_AGENT_HOME)→ settings.json → 内置默认。所以临时的环境变量或命令行参数永远压过持久化配置。
一旦某个 bug 已经拉到本地,zt workspace bug <bugId> 会在该 bug 目录里刷新一个 VS Code multi-root workspace 文件。它会把 bug 目录本身和所有 worktrees/* 子目录都放进顶层 folder,同时把 bug 根目录下的 worktrees/ 隐藏掉,避免 Explorer 和搜索里重复看到同一份代码。
升级 CLI(update)
不用再记全局安装那串命令,直接跑:
zt update # 升级到最新发布版本
zt update --check # 只检查有没有新版本,不安装
zt update 0.3.2 # 锁定 / 降级到指定版本或 dist-tagupdate 会读取当前版本,到 registry 查目标版本(沿用你配置的 registry,所以镜像也能用),
然后用安装它的包管理器重新执行全局安装——npm、pnpm、yarn、bun 会按安装路径自动识别。
用 --pm <npm|pnpm|yarn|bun> 可覆盖识别结果,--registry <url> 可指定查询用的 registry,
--force 在已是最新时也强制重装。
全局包升级成功后,update 会检测已经安装过的 agent 集成,只清理这些 agent 下由本 CLI 管理的旧 workflow/MCP 文件,再安装匹配的新文件;如果当前仓库里有本 CLI 写入过的协作说明文件或 commit-msg hook,也会一起刷新。没有安装痕迹的 agent 不会被新增安装。如果 CLI 是通过 npx(临时缓存,而非全局安装)运行的,
那就没什么可升级的——npx 每次本来就拉最新版本。
从老的扁平结构迁移
更早的版本把 bugs/ 和 repos.yaml 直接写在根目录下。如果升级后这些文件还在那儿,zt workspace init --migrate-legacy 会把它们搬到 default/ profile workspace。
注册表 repos.yaml
一个 productId 可以对应多个代码库(前端 / 后端 / 数据库迁移 / …)。zt-fix-bug workflow 在 Phase 2 支持多选,跨库 bug 时按仓库循环 worktree + commit + squash。
结构:
products:
27:
- name: eda-schematic
path: /Users/you/projects/eda-studio/eda-schematic
tags: [前端]
- name: eda-interop
path: /Users/you/projects/eda-studio/eda-interop
tags: [api]可以手工编辑,也可以用 zt repo register 追加。
流程概览(已安装 workflow 跑的事)
zt-fix-bug workflow 四阶段:
Phase 1 — Bug 上下文(不依赖任何代码库)
Phase 2 — 选代码库(多选;新输入的写回 repos.yaml)
Phase 3 — 按每个选中的库循环:worktree → 改 → 测 → squash merge
Phase 4 — 收尾建议(push / resolve_bug / 删 worktree)第三个参数传 auto(zt-fix-bug 27 9634 auto;Claude Code 中是 /zt-fix-bug ...)会跳过 Phase 1–3 的全部 ⏸ —— 复用已有 bug 上下文、注册表里该 productId 下的仓库全选、残留分支/worktree 删除重建、plan 原样执行;只有 squash 冲突时会自动回退到交互。Phase 4 收尾始终交互,push / resolve / 清理仍由用户拍板。
传逗号分隔的一批 bug id(zt-fix-bug 27 9634,9635,9636;Claude Code 中是 /zt-fix-bug ...)可把一组相关 bug 一起修:每个 bug 的上下文落在主 bug 目录下(raw-<id>.json / description-<id>.md / attachments/<id>/),修复在 fix/bug-<primary> 分支上只做一次,Phase 4 通过 finalize plan 里的 bugs[] 数组解决每个 id。
zt-bug-analyze workflow 只跑 Phase 1。之后想真修,直接 zt-fix-bug <productId> <bugId>(Claude Code 中是 /zt-fix-bug ...),已下载的 context 会复用。
之后如果想把同一个 bug 重新在 VS Code 里打开,跑 zt workspace bug <bugId> --open。不带 --open 也会刷新 .code-workspace 文件;带 --print-path 时则只输出它的路径。
设计取舍
- API 操作和 LLM 流程分开:
zt api ...直接调用禅道 API;已安装的 command / prompt / skill 负责 LLM-in-the-loop 的分析、修复、审查流程。 - 集中 workspace 而非仓库内:bug 分析阶段不知道改哪个库;多库场景下放任何一个库都尴尬。
- workspace 按 profile 隔离:多 ZenTao 实例(不同客户/环境)的 bug 上下文与注册表互不污染。
- worktree 而非 checkout:主仓库不动,多库场景天然并行。
- worktree 收纳在 bug 目录下,而不是放在源仓库旁边。一次
cd <bugId>/(或 IDE 以它为根开一个窗口)就能同时看到分析和所有仓库的修复改动。代价:源仓库的git worktree list会指向 workspace 树里 ——git worktree remove照常能用,但别在 worktree 还在时直接rm -rf <bugId>/。 - squash 而非 fast-forward:主分支只想看一个干净的「fix: #N」commit。
- 不主动 push / resolve / 删分支:副作用 / 跨边界操作留给用户显式触发。
- uninstall 不动用户数据:凭据、workspace 不会被 uninstall 删,避免误伤。
License
MIT © Deven Liu
