[feat] Add zentao plugin (Claude Code + Codex dual scaffold)

新增禅道项目管理系统插件,含项目集/产品/项目/执行、需求
(story/epic/requirement)、Bug、任务、测试、计划与发布、反馈工单
八个工作流技能,自动配置 MCP 连接(禅道「个人中心 → 获取凭证」
自助生成 14 天 Token)。

MCP Server 是团队自建的 zentao-mcp 网桥(部署在
pm.ops.yixiong-tech.com/mcp),基于开源 openapi-mcp-server 二次开发。
Claude 侧走 userConfig 钥匙链 + 自定义 token 头;Codex 侧受限于官方
插件格式只支持 bearer_token_env_var,走 Authorization: Bearer——网桥
那边已经加了 preferred_header/bearer_mode 配置项统一归一化处理,两条
路径都验证过连通。

三轮审查(静态字段对照 openapi.json、跨文件一致性、真实端点实测)
修正过程中发现的问题,技能文档里引用的工具名全部跟服务器真实注册
的 118 个工具核对过。

同步更新:.claude-plugin/marketplace.json、.agents/plugins/marketplace.json、
README.md、CLAUDE.md 的插件索引与说明;README 补充「获取凭证」操作
截图(已脱敏)。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016pFx6jnym6kRWRQ5JyDUNv
This commit is contained in:
SkyJourney
2026-08-25 13:50:41 +08:00
co-authored by Claude Sonnet 5
parent 1606d73c41
commit 9d9e31c98e
18 changed files with 1006 additions and 4 deletions
+129
View File
@@ -0,0 +1,129 @@
---
name: zentao-story
description: "禅道需求管理:用户故事(story)、业务需求(epic)、用户需求(requirement)三条需求线的创建、变更、关闭、激活。当用户说「提个需求」「这个需求变更一下」「需求关闭了」「史诗需求」时使用。"
---
# 禅道需求管理(story / epic / requirement
**前置:先读 `zentao-shared`(尤其"路径参数不出现在 inputSchema 里"一节——本技能的
`get_epics_storyID`/`get_requirements_storyID` 就是那条规则里点名的例外命名)。**
---
## 三条需求线是什么关系
禅道把"需求"拆成三种独立实体,**不是同一张表的三个状态**,是三套平行的 API:
| | 中文 | 路径前缀 | 典型用途 |
|---|---|---|---|
| story | 用户故事 | `/stories` | 敏捷开发里最常用的最小需求单元 |
| epic | 业务需求 | `/epics` | 更粗粒度、跨多个 story 的业务目标 |
| requirement | 用户需求 | `/requirements` | 来自用户侧的原始需求,未必等于最终交付的 story |
三者创建字段结构完全一样(`productID`/`title` 必填,`pri`/`module`/`parent`/`estimate`/
`spec`/`category`/`source`/`verify`/`assignedTo`/`reviewer` 可选),管理动作也对称
(改 / 变更 change / 关闭 close / 激活 activate / 删除),**用户说"需求"时先确认是要
哪一种**,不要默认都当 story 处理——三者互不包含,建错类型对方看不到。
---
## 详情接口的路径参数命名例外(必读)
```
get_stories_storyID(storyID) 需求详情
get_epics_storyID(storyID) 业务需求详情 —— 参数字面叫 storyID,不是 epicID
get_requirements_storyID(storyID) 用户需求详情 —— 参数字面叫 storyID,不是 requirementID
```
这是禅道官方 API 本身的命名(不是这个 MCP 网桥引入的),照抄传 `storyID` 就行,别自己
"纠正"成语义上更合理的名字,传错名字会静默返回空结果(见 `zentao-shared`)。
---
## 查
```
get_products_productID_stories(productID, browseType?, orderBy?, recPerPage?, pageID?)
get_projects_projectID_stories(projectID, ...)
get_executions_executionID_stories(executionID, ...)
get_products_productID_epics(productID, ...)
get_products_productID_requirements(productID, ...)
```
`browseType` 常见取值:`allstory` 全部 | `assignedtome` 指派给我 | `openedbyme` 我创建 |
`reviewbyme` 待我评审 | `draftstory` 草稿——不传默认是 `unclosed`(未关闭的)。
---
## 建
```
post_stories({ payload: { productID, title, pri?, module?, parent?, estimate?, spec?,
category?, source?, verify?, assignedTo?, reviewer?,
project?, execution? } })
post_epics({ payload: { productID, title, ... 同上(无 project/execution } })
post_requirements({ payload: { productID, title, ... 同上(无 project/execution } })
```
`productID`/`title` 必填,其余可选。`reviewer` 一旦设置,**该需求就必须经过评审**——
问清楚用户是否真的需要评审流程再决定填不填。`category`/`source` 是受限枚举(类别/来源),
取值以工具 description 为准,写错直接报错。
只有 story 能挂 `project`/`execution`(关联具体项目或迭代),epic/requirement 没有这两个字段。
---
## 变更(change
```
put_stories_storyID_change({ storyID, payload: { reviewer, title?, spec?, verify? } })
put_epics_epicID_change({ epicID, payload: { reviewer, title?, spec?, verify? } })
put_requirements_requirementID_change({ requirementID, payload: { title?, spec?, verify? } })
```
**`reviewer` 必填这条规则只对 story 和 epic 成立**——变更这两类需求必须指定评审人,
不能跳过评审直接改。**requirement 的变更接口没有 `reviewer` 这个参数,也没有任何必填
字段**,三者看起来对称,实际上 requirement 少一层评审约束,不要照搬 story/epic 的调用
方式给 requirement 传 `reviewer`(传了会被当成多余字段,不生效)。
注意这里改的路径参数名恢复正常(`epicID`/`requirementID`),跟上面"详情接口"那个
`storyID` 例外命名不是一回事,两套接口的路径参数名不一样,调用前对照工具名确认。
---
## 关闭(close
```
put_stories_storyID_close({ storyID, payload: { closedReason, comment? } })
```
`closedReason` 必填,受限枚举:`done` 已完成 | `subdivided` 已拆分 | `duplicate` 重复 |
`postponed` 延期 | `willnotdo` 不做 | `cancel` 已取消 | `bydesign` 设计如此。
**关闭前必须先跟用户确认关闭原因**`zentao-shared` 的全局约定:状态流转类操作先确认),
不要因为用户说"这个需求做完了"就自动挑一个理由关掉——`done` 和用户实际想表达的可能
不是一回事(比如其实是想选 `subdivided` 已拆成子需求)。
---
## 激活(activate
```
put_stories_storyID_activate({ storyID, payload: { assignedTo?, comment? } })
put_epics_epicID_activate({ epicID, payload: { assignedTo?, comment? } })
put_requirements_requirementID_activate({ requirementID, payload: { assignedTo?, comment? } })
```
三者字段结构一样,都没有必填字段,用于把已关闭/已拆分的需求重新打开。
---
## 删
```
delete_stories_storyID({ storyID })
delete_epics_epicID({ epicID })
delete_requirements_requirementID({ requirementID })
```
不可逆,删除前必须确认(`zentao-shared` 全局约定)。