npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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`).

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 mcp server——默认只为桌面客户端注册(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 auto
  • Codex / Copilot CLI / Qoder skill:让 agent 运行 zt-bug-analyze 9634、zt-fix-bug 27 9634 或 zt-fix-bug 27 9634 auto。

    • Qoder IDE 里在 Agent 对话框输入 / 选择该 skill。
  • VS Code Copilot prompt:在 Copilot Chat 中调用已安装的 zt-bug-analyze / zt-fix-bug prompt。

第二个参数可以是逗号分隔的一批相关 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-tag

update 会读取当前版本,到 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