dsh-github-flow 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +139 -54
- package/README.md +142 -54
- package/docs/architecture.md +19 -4
- package/docs/images/1.png +0 -0
- package/docs/images/3.png +0 -0
- package/docs/images/4.png +0 -0
- package/docs/npm-publish-guide.md +12 -0
- package/lib/api/route-helpers.d.ts +47 -0
- package/lib/api/route-helpers.js +134 -0
- package/lib/api/routes-actions.d.ts +9 -0
- package/lib/api/routes-actions.js +115 -0
- package/lib/api/routes-lifecycle.d.ts +9 -0
- package/lib/api/routes-lifecycle.js +75 -0
- package/lib/api/routes-overview.d.ts +11 -0
- package/lib/api/routes-overview.js +227 -0
- package/lib/api/routes-proxy.d.ts +18 -0
- package/lib/api/routes-proxy.js +63 -0
- package/lib/api/routes.d.ts +16 -6
- package/lib/api/routes.js +14 -504
- package/lib/client.js +383 -55
- package/lib/executor.d.ts +17 -0
- package/lib/executor.js +28 -0
- package/lib/main.d.ts +1 -0
- package/lib/main.js +40 -9
- package/lib/proxy.d.ts +87 -0
- package/lib/proxy.js +275 -0
- package/lib/renderers/markdown.d.ts +24 -0
- package/lib/renderers/markdown.js +493 -0
- package/lib/tools/api.d.ts +4 -0
- package/lib/tools/api.js +14 -4
- package/lib/tools/help.d.ts +31 -0
- package/lib/tools/help.js +212 -0
- package/lib/tools/issue.d.ts +32 -1
- package/lib/tools/issue.js +94 -17
- package/lib/tools/pr.d.ts +22 -1
- package/lib/tools/pr.js +67 -15
- package/lib/tools/release.d.ts +89 -0
- package/lib/tools/release.js +219 -0
- package/lib/tools/repo.d.ts +13 -1
- package/lib/tools/repo.js +46 -14
- package/lib/tools/run.d.ts +22 -1
- package/lib/tools/run.js +74 -14
- package/lib/tools/secret.d.ts +60 -0
- package/lib/tools/secret.js +175 -0
- package/lib/tools/skill.d.ts +63 -0
- package/lib/tools/skill.js +146 -0
- package/lib/types.d.ts +12 -0
- package/lib/updater.d.ts +8 -0
- package/lib/updater.js +9 -0
- package/lib/upgrade-manager.d.ts +4 -0
- package/lib/upgrade-manager.js +11 -1
- package/package.json +3 -2
- package/lib/client/icons.d.ts +0 -13
- package/lib/client/icons.js +0 -41
- package/lib/client/index.d.ts +0 -3
- package/lib/client/index.js +0 -36
- package/lib/client/panel.d.ts +0 -1
- package/lib/client/panel.js +0 -102
- package/lib/client/style.d.ts +0 -1
- package/lib/client/style.js +0 -555
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
const KNOWLEDGE_BASE = {
|
|
2
|
+
overview: `### 🧭 DSH GitHub Flow 工具箱全景导航
|
|
3
|
+
|
|
4
|
+
本插件提供面向 DeepSeek Harness 的原生 GitHub 协作与智能体基础设施工具矩阵:
|
|
5
|
+
|
|
6
|
+
| 工具名称 | 核心职责 | 典型调用场景 |
|
|
7
|
+
| :--- | :--- | :--- |
|
|
8
|
+
| \`github_pr\` | Pull Request 管理、审查与本地检出 | 查看 PR 列表/详情、比对 diff、本地检出代码验证、检查 CI 状态、执行带确认合并 |
|
|
9
|
+
| \`github_issue\` | Issue 任务追踪与分支开发关联 | 筛选待办议题、追加评论、编辑标题/标签、从 Issue 一键切出关联开发分支 |
|
|
10
|
+
| \`github_run\` | GitHub Actions 构建排查与工作流管理 | 查看构建列表、提取失败日志 (log_failed)、重新触发、手动触发 workflow_dispatch |
|
|
11
|
+
| \`github_repo\` | 仓库元数据、Fork 同步与全局检索 | 查看仓库与 topics、一键拉平 Fork 仓库 (sync)、检索全局代码与仓库 |
|
|
12
|
+
| \`github_api\` | 原生 GitHub REST/GraphQL 逃生门 | 调用任意端点、获取原始文件字节 (raw)、支持自动翻页 (paginate) |
|
|
13
|
+
| \`github_release\` | 版本发布与构建资产交付 | 查看发版历史、自动生成 Release Notes、上传 .tgz 等构建产物 |
|
|
14
|
+
| \`github_secret\` | CI/CD 部署密钥安全管理 | 查看 NPM_TOKEN 等密钥清单、经 stdin 安全写入密文、删除密钥 |
|
|
15
|
+
| \`github_skill\` | 跨 Agent 开源 Skills 生态发现 | 检索/预览/安装 GitHub 上的 Agent Skills (gh preview 特性) |
|
|
16
|
+
| \`github_help\` | 路由导航、官方语法速查与故障自愈 | 遇到不确定的操作、搜索语法或接口报错时查询用法与诊断配方 |
|
|
17
|
+
|
|
18
|
+
💡 **使用准则**:优先调用高层语义工具(pr / issue / run / repo / release / secret);当高层工具无法满足特殊接口需求时,再降级使用 \`github_api\`。`,
|
|
19
|
+
pr: `### 🔀 github_pr 详细操作指南
|
|
20
|
+
|
|
21
|
+
- **查看列表**: \`github_pr(action: 'list', state: 'open' | 'closed' | 'merged' | 'all', limit: 20, author: '@me')\`
|
|
22
|
+
- **本地检出代码**: \`github_pr(action: 'checkout', pr_number: 123)\`
|
|
23
|
+
- 自动切换到本地分支并拉取最新提交,非常适用于 Agent 在本地沙箱中跑单元测试验证 PR 改动。
|
|
24
|
+
- **差异比对**: \`github_pr(action: 'diff', pr_number: 123, stat_only: true)\`
|
|
25
|
+
- 对于大 PR,强烈建议先开启 \`stat_only: true\` 查看改动文件概览,防止超大 diff 触发截断或撑爆上下文。
|
|
26
|
+
- **检查冲突状态**: \`github_pr(action: 'status')\`
|
|
27
|
+
- 查看当前分支关联 PR 的 merge conflict 状态。
|
|
28
|
+
- **安全合并**: \`github_pr(action: 'merge', pr_number: 123, confirm: true, merge_method: 'squash', delete_branch: false)\`
|
|
29
|
+
- 高危操作必须显式附带 \`confirm: true\`;若未传将返回拦截警告与待合并影响报告。`,
|
|
30
|
+
issue: `### 🎯 github_issue 详细操作指南
|
|
31
|
+
|
|
32
|
+
- **筛选 Issue**: \`github_issue(action: 'list', state: 'open' | 'closed' | 'all', labels: ['bug'], assignees: ['octocat'], limit: 20)\`
|
|
33
|
+
- **关联开发分支**: \`github_issue(action: 'develop', issue_number: 42, branch_name: 'fix-login-bug')\`
|
|
34
|
+
- 自动在远端和本地创建带有官方关联关系的开发分支并切换过去,实现“读需求 -> 开分支写代码”的无缝衔接。
|
|
35
|
+
- **编辑 Issue**: \`github_issue(action: 'edit', issue_number: 42, title: '新标题', add_labels: ['p1'], remove_labels: ['triage'])\`
|
|
36
|
+
- **追加讨论**: \`github_issue(action: 'comment', issue_number: 42, body: '定位到问题原因...')\`
|
|
37
|
+
- **安全关闭**: \`github_issue(action: 'close', issue_number: 42, confirm: true)\`
|
|
38
|
+
- 关闭操作必须附带 \`confirm: true\`。`,
|
|
39
|
+
run: `### 🚀 github_run 详细操作指南
|
|
40
|
+
|
|
41
|
+
- **过滤构建记录**: \`github_run(action: 'list', branch: 'main', status: 'failure', limit: 10)\`
|
|
42
|
+
- 快速定位失败的 CI 构建,排查报错首选 \`status: 'failure'\`。
|
|
43
|
+
- **提取失败步骤日志**: \`github_run(action: 'log_failed', run_id: '12345678')\`
|
|
44
|
+
- 专为 Agent 优化的失败信息提取能力,直接定位报错位置;若无失败步骤则优雅提示。
|
|
45
|
+
- **主动触发工作流**: \`github_run(action: 'trigger', workflow: 'pr-check.yml', ref: 'main', inputs: { env: 'staging' })\`
|
|
46
|
+
- 提交代码修复后,主动触发远端 CI/CD 验证。
|
|
47
|
+
- **重新执行失败任务**: \`github_run(action: 'rerun', run_id: '12345678')\``,
|
|
48
|
+
repo: `### 📦 github_repo 详细操作指南
|
|
49
|
+
|
|
50
|
+
- **查看元数据与标签**: \`github_repo(action: 'view')\`
|
|
51
|
+
- 返回仓库基本信息、默认分支、Star 计数、许可证以及 \`repositoryTopics\`(用于 DSH 插件合规审查)。
|
|
52
|
+
- **一键拉平 Fork 主干 (防止 Stale)**: \`github_repo(action: 'sync', branch: 'main')\`
|
|
53
|
+
- 解决开源项目贡献中因本地 Fork 落后导致上游 CI 门禁失败的顽疾。
|
|
54
|
+
- **代码检索**: \`github_repo(action: 'search_code', query: 'filename:package.json dsh.bundle')\``,
|
|
55
|
+
api: `### ⚡ github_api 底层逃生门指南
|
|
56
|
+
|
|
57
|
+
- **标准 GET 请求带参数**:
|
|
58
|
+
\`github_api(endpoint: 'repos/owner/repo/pulls', fields: { state: 'closed', per_page: 5 })\`
|
|
59
|
+
- 插件会自动追加 \`-X GET\`,将 fields 作为 URL Query String 传递,杜绝被 gh 自动改写为 POST。
|
|
60
|
+
- **读取仓库原始文件字节**:
|
|
61
|
+
\`github_api(endpoint: 'repos/owner/repo/contents/path/to/file.yml', raw: true)\`
|
|
62
|
+
- 附加 \`Accept: application/vnd.github.raw\`,返回纯文本而非 base64 包装。
|
|
63
|
+
- **全量聚合翻页**:
|
|
64
|
+
\`github_api(endpoint: 'repos/owner/repo/commits', paginate: true)\`
|
|
65
|
+
- **GraphQL 请求**:
|
|
66
|
+
\`github_api(endpoint: 'graphql', fields: { query: 'query { viewer { login } }' })\``,
|
|
67
|
+
release: `### 🏷️ github_release 版本发布指南
|
|
68
|
+
|
|
69
|
+
- **查看发版历史**: \`github_release(action: 'list', limit: 10)\`
|
|
70
|
+
- **查看某个版本详情**: \`github_release(action: 'view', tag: 'v0.1.9')\`(留空 tag 查看最新)
|
|
71
|
+
- **创建 Release(自动生成说明)**:
|
|
72
|
+
\`github_release(action: 'create', tag: 'v0.1.9', generate_notes: true, prerelease: false)\`
|
|
73
|
+
- 若远端尚无该 Tag,gh 会自动从 target 分支(默认主分支)最新提交打 Tag。
|
|
74
|
+
- **上传构建资产**:
|
|
75
|
+
\`github_release(action: 'upload', tag: 'v0.1.9', assets: ['./dsh-github-flow-0.1.9.tgz'])\`
|
|
76
|
+
- 覆盖同名旧资产必须附加 \`clobber: true, confirm: true\`(旧资产删除后不可恢复)。
|
|
77
|
+
- **下载资产**: \`github_release(action: 'download', tag: 'v0.1.9', pattern: '*.tgz', dir: './dist')\`
|
|
78
|
+
- **删除 Release**: \`github_release(action: 'delete', tag: 'v0.1.9', confirm: true)\`
|
|
79
|
+
- 高危不可逆操作,必须显式确认。`,
|
|
80
|
+
secret: `### 🔐 github_secret 部署密钥管理指南
|
|
81
|
+
|
|
82
|
+
- **查看密钥清单**: \`github_secret(action: 'list')\`
|
|
83
|
+
- 仅返回名称、可见性与更新时间;GitHub API 设计上就不允许读取密文。
|
|
84
|
+
- **设置/更新密钥(安全通道)**:
|
|
85
|
+
\`github_secret(action: 'set', name: 'NPM_TOKEN', value: 'npm_xxxx')\`
|
|
86
|
+
- 密文经子进程标准输入传递,绝不出现在命令行参数(防 ps 窃取),输出永不回显。
|
|
87
|
+
- gh 在本地用仓库公钥加密后才上传,明文不落盘。
|
|
88
|
+
- **组织/环境级密钥**: 附加 \`org: 'my-org'\` 或 \`env: 'production'\` 参数切换作用域。
|
|
89
|
+
- **删除密钥**: \`github_secret(action: 'delete', name: 'NPM_TOKEN', confirm: true)\`
|
|
90
|
+
- 删除后引用该密钥的 CI 工作流会立即失败,务必先确认无工作流依赖。`,
|
|
91
|
+
skill: `### 🧠 github_skill Agent Skills 生态指南
|
|
92
|
+
|
|
93
|
+
基于 GitHub CLI 的 \`gh skill\` preview 特性(需 gh >= 2.9x),跨仓库发现与安装开源 Agent Skills:
|
|
94
|
+
|
|
95
|
+
- **检索 Skills**: \`github_skill(action: 'search', query: 'code-review', limit: 15)\`
|
|
96
|
+
- 可附加 \`owner: 'github'\` 限定作者组织。
|
|
97
|
+
- **预览 Skill 内容**: \`github_skill(action: 'preview', repository: 'github/awesome-copilot', skill_name: 'code-review')\`
|
|
98
|
+
- 只读不落盘,适合安装前审阅 SKILL.md 行为约束。
|
|
99
|
+
- **安装 Skill**:
|
|
100
|
+
\`github_skill(action: 'install', repository: 'github/awesome-copilot', skill_name: 'code-review', agent: 'claude-code', scope: 'project', confirm: true)\`
|
|
101
|
+
- install 会写本地磁盘,必须 \`confirm: true\`;scope 默认 project(当前 git 仓库)。
|
|
102
|
+
- 支持的 agent 值:claude-code / cursor / github-copilot / codex / gemini-cli 等。
|
|
103
|
+
- **查看已安装**: \`github_skill(action: 'list')\`
|
|
104
|
+
- ⚠️ 若报「unknown command」,说明宿主机 gh 版本过低,需升级到 2.9x 以上。`,
|
|
105
|
+
search_syntax: `### 🔍 GitHub 官方高级检索语法速查手册
|
|
106
|
+
|
|
107
|
+
在 \`github_repo\`、\`github_api\` 或搜索场景中,请严格遵守 GitHub 官方搜索语法:
|
|
108
|
+
|
|
109
|
+
1. **日期区间语法 (严格使用两点 .. 表示)**:
|
|
110
|
+
- \`created:2026-10-01..2026-10-12\`(2026年10月1日到10月12日之间)
|
|
111
|
+
- \`created:>2026-10-01\` 或 \`created:<2026-10-12\`
|
|
112
|
+
- ⚠️ **禁忌**:GitHub 搜索**不支持**数字范围 \`6129..6695\`,这会导致静默返回 0 条结果。
|
|
113
|
+
2. **状态与类型修饰符**:
|
|
114
|
+
- \`is:pr\`、\`is:issue\`、\`is:open\`、\`is:closed\`、\`is:merged\`、\`is:draft\`
|
|
115
|
+
3. **关联者修饰符**:
|
|
116
|
+
- \`author:@me\`、\`author:username\`、\`assignee:@me\`、\`mentions:username\`
|
|
117
|
+
4. **CI 状态修饰符**:
|
|
118
|
+
- \`status:success\`、\`status:failure\`、\`status:pending\`
|
|
119
|
+
5. **代码搜索范围限制**:
|
|
120
|
+
- \`repo:owner/repo\`、\`path:src/\`、\`filename:package.json\`、\`extension:ts\``,
|
|
121
|
+
troubleshooting: `### 🛠️ 常见故障诊断与自愈配方
|
|
122
|
+
|
|
123
|
+
1. **报 \`HTTP 422: "base", "head" weren't supplied\`**:
|
|
124
|
+
- *原因*:使用 gh api 传递参数时缺少 \`-X GET\`,导致 gh 隐式将其视作 POST 新建 PR 请求。
|
|
125
|
+
- *解法*:本插件已在底层自动补全 \`-X GET\`;若使用原始端点,确保直接将参数拼接在 URL query 或传递 method: 'GET'。
|
|
126
|
+
2. **报 \`HTTP 403: API rate limit exceeded\`**:
|
|
127
|
+
- *原因*:GitHub 账号 API 配额耗尽或短时间内突发高并发被二级风控。
|
|
128
|
+
- *解法*:等待当前小时重置,或减少无意义的循环轮询;使用高层工具的针对性过滤条件减少请求次数。
|
|
129
|
+
3. **报 \`Submission gate: stale fork\` 或上游门禁失败**:
|
|
130
|
+
- *原因*:Fork 的仓库落后于主干,合并时带入了过期的配置或删除了近期条目。
|
|
131
|
+
- *解法*:调用 \`github_repo(action: 'sync')\` 自动对齐 upstream 主干代码。
|
|
132
|
+
4. **报 \`System cannot find GitHub CLI (gh)\`**:
|
|
133
|
+
- *解法*:宿主机尚未安装 gh。Windows 运行 \`winget install GitHub.cli\`,macOS 运行 \`brew install gh\`,然后执行 \`gh auth login\` 登录。
|
|
134
|
+
5. **报 \`unknown command "skill" for "gh"\`**:
|
|
135
|
+
- *原因*:\`gh skill\` 是 GitHub CLI 2.9x 引入的 preview 特性,宿主机版本过低。
|
|
136
|
+
- *解法*:升级 gh(Windows: \`winget upgrade GitHub.cli\`;macOS: \`brew upgrade gh\`)后重试;升级前可改用 \`github_api\` 检索 \`SKILL.md\` 文件路径作为替代。`
|
|
137
|
+
};
|
|
138
|
+
export function createHelpTool(executor) {
|
|
139
|
+
return {
|
|
140
|
+
name: 'github_help',
|
|
141
|
+
description: 'GitHub 工具箱使用指南与官方命令手册。当你不确定该用哪个工具、需要特定操作的高级语法与示例、或排查 GitHub 报错时调用。',
|
|
142
|
+
parameters: {
|
|
143
|
+
type: 'object',
|
|
144
|
+
properties: {
|
|
145
|
+
topic: {
|
|
146
|
+
type: 'string',
|
|
147
|
+
enum: [
|
|
148
|
+
'overview',
|
|
149
|
+
'pr',
|
|
150
|
+
'issue',
|
|
151
|
+
'run',
|
|
152
|
+
'repo',
|
|
153
|
+
'api',
|
|
154
|
+
'release',
|
|
155
|
+
'secret',
|
|
156
|
+
'skill',
|
|
157
|
+
'search_syntax',
|
|
158
|
+
'troubleshooting',
|
|
159
|
+
],
|
|
160
|
+
description: '帮助主题分类。留空则返回全局工具箱导航。',
|
|
161
|
+
},
|
|
162
|
+
gh_command: {
|
|
163
|
+
type: 'string',
|
|
164
|
+
description: '可选:直接查询 GitHub CLI 官方原始命令帮助手册(例如 "pr create"、"search code"、"ruleset")。',
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
output: {
|
|
169
|
+
schema: { type: 'object' },
|
|
170
|
+
render(args, value) {
|
|
171
|
+
if (!value)
|
|
172
|
+
return [{ type: 'text', text: '无返回内容' }];
|
|
173
|
+
if (typeof value.result === 'string') {
|
|
174
|
+
return [{ type: 'text', text: value.result }];
|
|
175
|
+
}
|
|
176
|
+
return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
async execute(args, execContext) {
|
|
180
|
+
const cwd = execContext?.cwd || process.cwd();
|
|
181
|
+
const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
|
|
182
|
+
// 1. 若显式指定了 gh_command,直接透传查询官方离线手册
|
|
183
|
+
if (args.gh_command && typeof args.gh_command === 'string') {
|
|
184
|
+
let parts = args.gh_command.trim().split(/\s+/);
|
|
185
|
+
// 容错调用方输入 "gh pr create" 带有前导 "gh" 的情形
|
|
186
|
+
if (parts[0] === 'gh') {
|
|
187
|
+
parts.shift();
|
|
188
|
+
}
|
|
189
|
+
const res = await executor.run(['help', ...parts], { cwd, sessionId, rawText: true });
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
return {
|
|
192
|
+
result: `未找到命令「gh ${args.gh_command}」的官方帮助手册:${res.error}\n建议通过 topic 参数查询内置指南。`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return { result: res.rawOutput };
|
|
196
|
+
}
|
|
197
|
+
// 2. 查询内置知识库速查表
|
|
198
|
+
// 类型防护:DSH 网关对 enum 参数的拦截强度依赖实现,Agent 仍可能传非字符串
|
|
199
|
+
// (数字/对象/数组)。`-1 || 'overview'` 真值短路挡不住负数,会炸 toLowerCase。
|
|
200
|
+
// 与上方 gh_command 分支的 typeof 防护保持同一强度。
|
|
201
|
+
const rawTopic = typeof args.topic === 'string' && args.topic.trim() ? args.topic : 'overview';
|
|
202
|
+
const topic = rawTopic.toLowerCase().trim();
|
|
203
|
+
const content = KNOWLEDGE_BASE[topic];
|
|
204
|
+
if (content) {
|
|
205
|
+
return { result: content };
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
result: `未找到主题「${rawTopic}」的指南。可用主题:${Object.keys(KNOWLEDGE_BASE).join(', ')}。`,
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
package/lib/tools/issue.d.ts
CHANGED
|
@@ -40,6 +40,37 @@ export declare function createIssueTool(executor: GhExecutor): {
|
|
|
40
40
|
};
|
|
41
41
|
description: string;
|
|
42
42
|
};
|
|
43
|
+
state: {
|
|
44
|
+
type: string;
|
|
45
|
+
enum: string[];
|
|
46
|
+
description: string;
|
|
47
|
+
};
|
|
48
|
+
limit: {
|
|
49
|
+
type: string;
|
|
50
|
+
description: string;
|
|
51
|
+
};
|
|
52
|
+
branch_name: {
|
|
53
|
+
type: string;
|
|
54
|
+
description: string;
|
|
55
|
+
};
|
|
56
|
+
add_labels: {
|
|
57
|
+
type: string;
|
|
58
|
+
items: {
|
|
59
|
+
type: string;
|
|
60
|
+
};
|
|
61
|
+
description: string;
|
|
62
|
+
};
|
|
63
|
+
remove_labels: {
|
|
64
|
+
type: string;
|
|
65
|
+
items: {
|
|
66
|
+
type: string;
|
|
67
|
+
};
|
|
68
|
+
description: string;
|
|
69
|
+
};
|
|
70
|
+
confirm: {
|
|
71
|
+
type: string;
|
|
72
|
+
description: string;
|
|
73
|
+
};
|
|
43
74
|
};
|
|
44
75
|
required: string[];
|
|
45
76
|
};
|
|
@@ -49,7 +80,7 @@ export declare function createIssueTool(executor: GhExecutor): {
|
|
|
49
80
|
};
|
|
50
81
|
render(args: any, value: any): {
|
|
51
82
|
type: string;
|
|
52
|
-
text:
|
|
83
|
+
text: string;
|
|
53
84
|
}[];
|
|
54
85
|
};
|
|
55
86
|
execute(args: any, execContext: any): Promise<Record<string, any>>;
|
package/lib/tools/issue.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderIssueMarkdown } from '../renderers/markdown.js';
|
|
1
2
|
function normalizeOutput(data) {
|
|
2
3
|
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
3
4
|
return data;
|
|
@@ -10,18 +11,18 @@ function normalizeOutput(data) {
|
|
|
10
11
|
export function createIssueTool(executor) {
|
|
11
12
|
return {
|
|
12
13
|
name: 'github_issue',
|
|
13
|
-
description: '管理 GitHub Issues。支持查看 Issue 列表、详情、创建新 Issue
|
|
14
|
+
description: '管理 GitHub Issues。支持查看 Issue 列表、详情、创建新 Issue、追加评论、关闭 Issue、编辑 Issue 以及关联创建本地开发分支 (develop)。',
|
|
14
15
|
parameters: {
|
|
15
16
|
type: 'object',
|
|
16
17
|
properties: {
|
|
17
18
|
action: {
|
|
18
19
|
type: 'string',
|
|
19
|
-
enum: ['list', 'view', 'create', 'comment', 'close', 'reopen'],
|
|
20
|
+
enum: ['list', 'view', 'create', 'comment', 'close', 'reopen', 'edit', 'develop'],
|
|
20
21
|
description: 'Issue 操作类型。',
|
|
21
22
|
},
|
|
22
23
|
issue_number: {
|
|
23
24
|
type: 'number',
|
|
24
|
-
description: 'Issue 编号(view, comment, close, reopen 时使用)。',
|
|
25
|
+
description: 'Issue 编号(view, comment, close, reopen, edit, develop 时使用)。',
|
|
25
26
|
},
|
|
26
27
|
repo: {
|
|
27
28
|
type: 'string',
|
|
@@ -29,21 +30,48 @@ export function createIssueTool(executor) {
|
|
|
29
30
|
},
|
|
30
31
|
title: {
|
|
31
32
|
type: 'string',
|
|
32
|
-
description: 'Issue 标题(create 时使用)。',
|
|
33
|
+
description: 'Issue 标题(create 或 edit 时使用)。',
|
|
33
34
|
},
|
|
34
35
|
body: {
|
|
35
36
|
type: 'string',
|
|
36
|
-
description: 'Issue
|
|
37
|
+
description: 'Issue 描述正文或追加评论内容(create, comment, edit 时使用)。',
|
|
37
38
|
},
|
|
38
39
|
labels: {
|
|
39
40
|
type: 'array',
|
|
40
41
|
items: { type: 'string' },
|
|
41
|
-
description: '
|
|
42
|
+
description: '标签名称列表(create 或 list 动作筛选使用)。',
|
|
42
43
|
},
|
|
43
44
|
assignees: {
|
|
44
45
|
type: 'array',
|
|
45
46
|
items: { type: 'string' },
|
|
46
|
-
description: '被指派人的 GitHub
|
|
47
|
+
description: '被指派人的 GitHub 登录名列表(create 或 list 动作筛选使用)。',
|
|
48
|
+
},
|
|
49
|
+
state: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
enum: ['open', 'closed', 'all'],
|
|
52
|
+
description: 'Issue 状态过滤(list 动作使用,可选 open, closed, all,默认 open)。',
|
|
53
|
+
},
|
|
54
|
+
limit: {
|
|
55
|
+
type: 'number',
|
|
56
|
+
description: '返回 Issue 数量上限(list 动作使用,默认 20,建议最大不超过 100)。',
|
|
57
|
+
},
|
|
58
|
+
branch_name: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: '开发分支名称(develop 动作使用,例如 "fix-auth-bug")。',
|
|
61
|
+
},
|
|
62
|
+
add_labels: {
|
|
63
|
+
type: 'array',
|
|
64
|
+
items: { type: 'string' },
|
|
65
|
+
description: '要追加的标签列表(edit 动作使用)。',
|
|
66
|
+
},
|
|
67
|
+
remove_labels: {
|
|
68
|
+
type: 'array',
|
|
69
|
+
items: { type: 'string' },
|
|
70
|
+
description: '要移除的标签列表(edit 动作使用)。',
|
|
71
|
+
},
|
|
72
|
+
confirm: {
|
|
73
|
+
type: 'boolean',
|
|
74
|
+
description: '高危破坏性操作确认标志(close 动作必需)。若为 false 则仅输出待关闭警告。',
|
|
47
75
|
},
|
|
48
76
|
},
|
|
49
77
|
required: ['action'],
|
|
@@ -51,15 +79,7 @@ export function createIssueTool(executor) {
|
|
|
51
79
|
output: {
|
|
52
80
|
schema: { type: 'object' },
|
|
53
81
|
render(args, value) {
|
|
54
|
-
|
|
55
|
-
return [{ type: 'text', text: '无返回内容' }];
|
|
56
|
-
if (typeof value.result === 'string') {
|
|
57
|
-
return [{ type: 'text', text: value.result }];
|
|
58
|
-
}
|
|
59
|
-
if (Array.isArray(value.items)) {
|
|
60
|
-
return [{ type: 'text', text: JSON.stringify(value.items, null, 2) }];
|
|
61
|
-
}
|
|
62
|
-
return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
|
|
82
|
+
return [{ type: 'text', text: renderIssueMarkdown(args, value) }];
|
|
63
83
|
},
|
|
64
84
|
},
|
|
65
85
|
async execute(args, execContext) {
|
|
@@ -79,7 +99,23 @@ export function createIssueTool(executor) {
|
|
|
79
99
|
const runOpts = { cwd, sessionId };
|
|
80
100
|
switch (args.action) {
|
|
81
101
|
case 'list': {
|
|
82
|
-
const
|
|
102
|
+
const listCmd = ['issue', 'list', ...repoArgs, '--json', 'number,title,state,author,labels,updatedAt,url'];
|
|
103
|
+
if (args.state) {
|
|
104
|
+
listCmd.push('--state', args.state);
|
|
105
|
+
}
|
|
106
|
+
if (args.labels && Array.isArray(args.labels) && args.labels.length > 0) {
|
|
107
|
+
for (const l of args.labels) {
|
|
108
|
+
listCmd.push('--label', l);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (args.assignees && Array.isArray(args.assignees) && args.assignees.length > 0) {
|
|
112
|
+
// gh issue list 的 --assignee 仅支持单账号或 @me,传逗号连接会导致 GraphQL 报用户未找到
|
|
113
|
+
listCmd.push('--assignee', args.assignees[0]);
|
|
114
|
+
}
|
|
115
|
+
// 显式 limit 优先;缺省回退 executor.defaultListLimit(用户可在插件配置中调整)
|
|
116
|
+
const limit = typeof args.limit === 'number' && args.limit > 0 ? Math.min(args.limit, 100) : Math.min(executor.defaultListLimit ?? 20, 100);
|
|
117
|
+
listCmd.push('-L', String(limit));
|
|
118
|
+
const res = await executor.run(listCmd, runOpts);
|
|
83
119
|
if (!res.ok)
|
|
84
120
|
throw new Error(res.error);
|
|
85
121
|
return normalizeOutput(res.data || res.rawOutput);
|
|
@@ -105,6 +141,39 @@ export function createIssueTool(executor) {
|
|
|
105
141
|
throw new Error(res.error);
|
|
106
142
|
return { message: 'Issue 创建成功', url: res.rawOutput };
|
|
107
143
|
}
|
|
144
|
+
case 'edit': {
|
|
145
|
+
if (!args.issue_number)
|
|
146
|
+
throw new Error('edit 操作必须提供 issue_number');
|
|
147
|
+
const editCmd = ['issue', 'edit', String(args.issue_number), ...repoArgs];
|
|
148
|
+
if (args.title)
|
|
149
|
+
editCmd.push('--title', args.title);
|
|
150
|
+
if (args.body)
|
|
151
|
+
editCmd.push('--body', args.body);
|
|
152
|
+
if (args.add_labels && Array.isArray(args.add_labels) && args.add_labels.length > 0) {
|
|
153
|
+
for (const l of args.add_labels)
|
|
154
|
+
editCmd.push('--add-label', l);
|
|
155
|
+
}
|
|
156
|
+
if (args.remove_labels && Array.isArray(args.remove_labels) && args.remove_labels.length > 0) {
|
|
157
|
+
for (const l of args.remove_labels)
|
|
158
|
+
editCmd.push('--remove-label', l);
|
|
159
|
+
}
|
|
160
|
+
const res = await executor.run(editCmd, runOpts);
|
|
161
|
+
if (!res.ok)
|
|
162
|
+
throw new Error(res.error);
|
|
163
|
+
return { message: `Issue #${args.issue_number} 更新成功`, output: res.rawOutput };
|
|
164
|
+
}
|
|
165
|
+
case 'develop': {
|
|
166
|
+
if (!args.issue_number)
|
|
167
|
+
throw new Error('develop 操作必须提供 issue_number');
|
|
168
|
+
const devCmd = ['issue', 'develop', String(args.issue_number), ...repoArgs, '--checkout'];
|
|
169
|
+
if (args.branch_name) {
|
|
170
|
+
devCmd.push('-n', args.branch_name);
|
|
171
|
+
}
|
|
172
|
+
const res = await executor.run(devCmd, runOpts);
|
|
173
|
+
if (!res.ok)
|
|
174
|
+
throw new Error(res.error);
|
|
175
|
+
return { message: `已成功为 Issue #${args.issue_number} 创建并检出开发分支`, output: res.rawOutput };
|
|
176
|
+
}
|
|
108
177
|
case 'comment': {
|
|
109
178
|
if (!args.issue_number || !args.body)
|
|
110
179
|
throw new Error('comment 必须提供 issue_number 和 body');
|
|
@@ -116,6 +185,14 @@ export function createIssueTool(executor) {
|
|
|
116
185
|
case 'close': {
|
|
117
186
|
if (!args.issue_number)
|
|
118
187
|
throw new Error('close 操作必须提供 issue_number');
|
|
188
|
+
if (args.confirm !== true) {
|
|
189
|
+
return {
|
|
190
|
+
status: 'AWAITING_CONFIRMATION',
|
|
191
|
+
warning: `⚠️ 高危操作拦截:你即将关闭 Issue #${args.issue_number}。`,
|
|
192
|
+
impact: '关闭后该议题状态将变更为 CLOSED,请与用户确认是否确实已解决或需要关闭。',
|
|
193
|
+
requiredAction: '如确认关闭,请在参数中附加 `confirm: true` 重新调用。',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
119
196
|
const res = await executor.run(['issue', 'close', String(args.issue_number), ...repoArgs], runOpts);
|
|
120
197
|
if (!res.ok)
|
|
121
198
|
throw new Error(res.error);
|
package/lib/tools/pr.d.ts
CHANGED
|
@@ -52,6 +52,27 @@ export declare function createPrTool(executor: GhExecutor): {
|
|
|
52
52
|
type: string;
|
|
53
53
|
description: string;
|
|
54
54
|
};
|
|
55
|
+
state: {
|
|
56
|
+
type: string;
|
|
57
|
+
enum: string[];
|
|
58
|
+
description: string;
|
|
59
|
+
};
|
|
60
|
+
limit: {
|
|
61
|
+
type: string;
|
|
62
|
+
description: string;
|
|
63
|
+
};
|
|
64
|
+
author: {
|
|
65
|
+
type: string;
|
|
66
|
+
description: string;
|
|
67
|
+
};
|
|
68
|
+
stat_only: {
|
|
69
|
+
type: string;
|
|
70
|
+
description: string;
|
|
71
|
+
};
|
|
72
|
+
confirm: {
|
|
73
|
+
type: string;
|
|
74
|
+
description: string;
|
|
75
|
+
};
|
|
55
76
|
dry_run: {
|
|
56
77
|
type: string;
|
|
57
78
|
description: string;
|
|
@@ -65,7 +86,7 @@ export declare function createPrTool(executor: GhExecutor): {
|
|
|
65
86
|
};
|
|
66
87
|
render(args: any, value: any): {
|
|
67
88
|
type: string;
|
|
68
|
-
text:
|
|
89
|
+
text: string;
|
|
69
90
|
}[];
|
|
70
91
|
};
|
|
71
92
|
execute(args: any, execContext: any): Promise<Record<string, any>>;
|
package/lib/tools/pr.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderPrMarkdown } from '../renderers/markdown.js';
|
|
1
2
|
function normalizeOutput(data) {
|
|
2
3
|
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
3
4
|
return data;
|
|
@@ -10,18 +11,18 @@ function normalizeOutput(data) {
|
|
|
10
11
|
export function createPrTool(executor) {
|
|
11
12
|
return {
|
|
12
13
|
name: 'github_pr',
|
|
13
|
-
description: '管理与审查 GitHub Pull Request (PR)。支持查看列表、详情、差异比对、CI
|
|
14
|
+
description: '管理与审查 GitHub Pull Request (PR)。支持查看列表、详情、差异比对、CI 检查状态、代码审查、合并操作、本地代码检出 (checkout) 与冲突状态检测 (status)。默认针对当前工作区关联的仓库。',
|
|
14
15
|
parameters: {
|
|
15
16
|
type: 'object',
|
|
16
17
|
properties: {
|
|
17
18
|
action: {
|
|
18
19
|
type: 'string',
|
|
19
|
-
enum: ['list', 'view', 'create', 'diff', 'checks', 'review', 'merge'],
|
|
20
|
-
description: 'PR
|
|
20
|
+
enum: ['list', 'view', 'create', 'diff', 'checks', 'review', 'merge', 'checkout', 'status'],
|
|
21
|
+
description: 'PR 操作类型。新增 checkout (检出 PR 到本地分支)、status (检测当前分支 PR 冲突与状态)。',
|
|
21
22
|
},
|
|
22
23
|
pr_number: {
|
|
23
24
|
type: 'number',
|
|
24
|
-
description: 'PR 编号(view, diff, checks, review, merge 时使用;若在 PR 对应分支可不填,自动识别当前分支 PR)。',
|
|
25
|
+
description: 'PR 编号(view, diff, checks, review, merge, checkout 时使用;若在 PR 对应分支可不填,自动识别当前分支 PR)。',
|
|
25
26
|
},
|
|
26
27
|
repo: {
|
|
27
28
|
type: 'string',
|
|
@@ -61,6 +62,27 @@ export function createPrTool(executor) {
|
|
|
61
62
|
type: 'boolean',
|
|
62
63
|
description: '合并后是否删除源分支(merge 操作时使用,默认 false)。删除分支不可撤销,只在确认该分支已无用后再开启。',
|
|
63
64
|
},
|
|
65
|
+
state: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
enum: ['open', 'closed', 'merged', 'all'],
|
|
68
|
+
description: 'PR 状态过滤(list 操作时使用,可选 open, closed, merged, all,默认 open)。',
|
|
69
|
+
},
|
|
70
|
+
limit: {
|
|
71
|
+
type: 'number',
|
|
72
|
+
description: '返回 PR 数量上限(list 操作时使用,默认 20,建议最大不超过 100)。',
|
|
73
|
+
},
|
|
74
|
+
author: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
description: '按作者筛选(list 动作使用,支持 "@me" 或 GitHub 用户名)。',
|
|
77
|
+
},
|
|
78
|
+
stat_only: {
|
|
79
|
+
type: 'boolean',
|
|
80
|
+
description: '是否仅输出文件变动概览(diff 动作使用,映射为 gh pr diff --stat,防止大 PR 爆 Token)。',
|
|
81
|
+
},
|
|
82
|
+
confirm: {
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
description: '高危破坏性操作确认标志(merge 动作或 delete_branch 时必需)。若为 false 则仅输出合并预演警告。',
|
|
85
|
+
},
|
|
64
86
|
dry_run: {
|
|
65
87
|
type: 'boolean',
|
|
66
88
|
description: '是否只预演不真正创建(create 操作时使用,默认 false)。透传 gh --dry-run:打印将要创建的 PR 详情而不落库,用于提交前确认 head/base/title 是否正确。',
|
|
@@ -71,15 +93,7 @@ export function createPrTool(executor) {
|
|
|
71
93
|
output: {
|
|
72
94
|
schema: { type: 'object' },
|
|
73
95
|
render(args, value) {
|
|
74
|
-
|
|
75
|
-
return [{ type: 'text', text: '无返回内容' }];
|
|
76
|
-
if (typeof value.result === 'string') {
|
|
77
|
-
return [{ type: 'text', text: value.result }];
|
|
78
|
-
}
|
|
79
|
-
if (Array.isArray(value.items)) {
|
|
80
|
-
return [{ type: 'text', text: JSON.stringify(value.items, null, 2) }];
|
|
81
|
-
}
|
|
82
|
-
return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
|
|
96
|
+
return [{ type: 'text', text: renderPrMarkdown(args, value) }];
|
|
83
97
|
},
|
|
84
98
|
},
|
|
85
99
|
async execute(args, execContext) {
|
|
@@ -99,7 +113,17 @@ export function createPrTool(executor) {
|
|
|
99
113
|
const runOpts = { cwd, sessionId };
|
|
100
114
|
switch (args.action) {
|
|
101
115
|
case 'list': {
|
|
102
|
-
const
|
|
116
|
+
const listCmd = ['pr', 'list', ...repoArgs, '--json', 'number,title,author,headRefName,isDraft,state,updatedAt,mergedAt,url'];
|
|
117
|
+
if (args.state) {
|
|
118
|
+
listCmd.push('--state', args.state);
|
|
119
|
+
}
|
|
120
|
+
if (args.author) {
|
|
121
|
+
listCmd.push('--author', args.author);
|
|
122
|
+
}
|
|
123
|
+
// 显式 limit 优先;缺省回退 executor.defaultListLimit(用户可在插件配置中调整)
|
|
124
|
+
const limit = typeof args.limit === 'number' && args.limit > 0 ? Math.min(args.limit, 100) : Math.min(executor.defaultListLimit ?? 20, 100);
|
|
125
|
+
listCmd.push('-L', String(limit));
|
|
126
|
+
const res = await executor.run(listCmd, runOpts);
|
|
103
127
|
if (!res.ok)
|
|
104
128
|
throw new Error(res.error);
|
|
105
129
|
return normalizeOutput(res.data || res.rawOutput);
|
|
@@ -120,11 +144,31 @@ export function createPrTool(executor) {
|
|
|
120
144
|
}
|
|
121
145
|
case 'diff': {
|
|
122
146
|
const target = args.pr_number ? [String(args.pr_number)] : [];
|
|
123
|
-
const
|
|
147
|
+
const diffCmd = ['pr', 'diff', ...target, ...repoArgs];
|
|
148
|
+
if (args.stat_only === true) {
|
|
149
|
+
diffCmd.push('--stat');
|
|
150
|
+
}
|
|
151
|
+
const res = await executor.run(diffCmd, runOpts);
|
|
124
152
|
if (!res.ok)
|
|
125
153
|
throw new Error(res.error);
|
|
126
154
|
return { result: res.rawOutput || '无差异内容' };
|
|
127
155
|
}
|
|
156
|
+
case 'checkout': {
|
|
157
|
+
if (!args.pr_number)
|
|
158
|
+
throw new Error('checkout 操作必须提供 pr_number');
|
|
159
|
+
const checkoutCmd = ['pr', 'checkout', String(args.pr_number), ...repoArgs, '-f'];
|
|
160
|
+
const res = await executor.run(checkoutCmd, runOpts);
|
|
161
|
+
if (!res.ok)
|
|
162
|
+
throw new Error(res.error);
|
|
163
|
+
return { message: `已成功将 PR #${args.pr_number} 检出至本地分支`, output: res.rawOutput || 'Checked out' };
|
|
164
|
+
}
|
|
165
|
+
case 'status': {
|
|
166
|
+
const statusCmd = ['pr', 'status', ...repoArgs, '-c', '--json', 'number,title,state,mergeStateStatus,url'];
|
|
167
|
+
const res = await executor.run(statusCmd, runOpts);
|
|
168
|
+
if (!res.ok)
|
|
169
|
+
throw new Error(res.error);
|
|
170
|
+
return normalizeOutput(res.data || res.rawOutput);
|
|
171
|
+
}
|
|
128
172
|
case 'create': {
|
|
129
173
|
const createCmd = ['pr', 'create', ...repoArgs, '--title', args.title || '', '--body', args.body || ''];
|
|
130
174
|
if (args.base)
|
|
@@ -166,6 +210,14 @@ export function createPrTool(executor) {
|
|
|
166
210
|
case 'merge': {
|
|
167
211
|
if (!args.pr_number)
|
|
168
212
|
throw new Error('merge 操作必须提供 pr_number');
|
|
213
|
+
if (args.confirm !== true) {
|
|
214
|
+
return {
|
|
215
|
+
status: 'AWAITING_CONFIRMATION',
|
|
216
|
+
warning: `⚠️ 高危操作拦截:你即将合并 PR #${args.pr_number}(合并策略: ${args.merge_method || 'squash'}${args.delete_branch ? ',且删除源分支' : ''})。`,
|
|
217
|
+
impact: '合并操作将直接向基准分支写入代码并更新远端 PR 状态。',
|
|
218
|
+
requiredAction: '如确认执行合并,请向用户明确确认并附加 `confirm: true` 重新调用。',
|
|
219
|
+
};
|
|
220
|
+
}
|
|
169
221
|
const method = args.merge_method === 'merge' ? '--merge' : (args.merge_method === 'rebase' ? '--rebase' : '--squash');
|
|
170
222
|
const mergeCmd = ['pr', 'merge', String(args.pr_number), ...repoArgs, method];
|
|
171
223
|
// 删分支默认关闭:合并他人仓库的 PR 时删掉源分支是不可撤销的破坏性动作,
|