dsh-github-flow 0.2.1 → 0.3.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.
@@ -73,6 +73,17 @@ export function formatConflictBadge(status) {
73
73
  return '⛔ BLOCKED';
74
74
  return s;
75
75
  }
76
+ /**
77
+ * 降级链路可见标注(runGuarded 契约的渲染兑现)。
78
+ * 为什么必须可见:降级若是静默的,Agent 就无法感知能力边界(同一 action 在
79
+ * 不同 gh 版本上走不同链路);一条 blockquote 注释成本一行,换链路透明。
80
+ */
81
+ export function appendDegradedNote(value) {
82
+ if (value && value.degraded === true && value.degradedReason) {
83
+ return `\n\n> ⚠️ ${value.degradedReason}`;
84
+ }
85
+ return '';
86
+ }
76
87
  // -------------------------------------------------------------
77
88
  // 1. PR 渲染器
78
89
  // -------------------------------------------------------------
@@ -280,7 +291,34 @@ export function renderRunMarkdown(args, value) {
280
291
  ...rows,
281
292
  ].join('\n');
282
293
  }
283
- // 3.2 Run View 详情
294
+ // 3.2 Workflow 清单(字段集为 gh 2.96 实测合法全集 4 个)
295
+ if (Array.isArray(value.items) && args?.action === 'workflow_list') {
296
+ const list = value.items.filter(Boolean);
297
+ if (list.length === 0)
298
+ return args?.all === true ? '📭 该仓库没有任何工作流。' : '📭 暂无 active 工作流(可能全部 disabled,传 all: true 查看)。';
299
+ const rows = list.map((w) => {
300
+ const badge = w.state === 'active' ? '🟢 active' : (w.state === 'disabled' ? '🔴 disabled' : String(w.state || '-'));
301
+ return `| \`${w.id}\` | ${sanitizeCell(w.name, 40)} | ${badge} | \`${sanitizeCell(w.path, 45)}\` |`;
302
+ });
303
+ return [
304
+ `**工作流清单 (${list.length} 个${args?.all === true ? ',含 disabled' : ''}):**\n`,
305
+ '| ID | 名称 | 状态 | 文件 |',
306
+ '| :--- | :--- | :--- | :--- |',
307
+ ...rows,
308
+ ].join('\n');
309
+ }
310
+ // 3.3 Workflow 详情(gh 无 --json,头部结构化 + Recent runs 原文引用块——D4 决策)
311
+ if (args?.action === 'workflow_view' && value.workflow) {
312
+ const lines = [
313
+ `### 🔄 ${value.workflow}`,
314
+ `- **ID**: \`${value.id || '-'}\`${value.totalRuns ? ` | **累计运行**: ${value.totalRuns} 次` : ''}`,
315
+ ];
316
+ if (value.recentRuns) {
317
+ lines.push('', '**最近运行:**', '```', value.recentRuns, '```');
318
+ }
319
+ return lines.join('\n');
320
+ }
321
+ // 3.4 Run View 详情
284
322
  if (args?.action === 'view' && value.databaseId) {
285
323
  const idLink = value.url ? `[${value.databaseId}](${value.url})` : `\`${value.databaseId}\``;
286
324
  const badge = formatCiBadge(value.status, value.conclusion);
@@ -330,7 +368,7 @@ export function renderRepoMarkdown(args, value) {
330
368
  const branch = value.defaultBranchRef?.name || value.defaultBranch || 'main';
331
369
  const lines = [
332
370
  `### 📦 ${link}`,
333
- `> ⭐ **Stars**: ${stars} | 🍴 **Forks**: ${forks} | 🌿 **默认分支**: \`${branch}\``,
371
+ `> ⭐ **Stars**: ${stars} | 🍴 **Forks**: ${forks} | ⎇ **默认分支**: \`${branch}\``,
334
372
  value.description ? `\n${value.description}\n` : '',
335
373
  ];
336
374
  if (value.licenseInfo?.name || value.licenseInfo?.key) {
@@ -375,6 +413,75 @@ export function renderRepoMarkdown(args, value) {
375
413
  ...rows,
376
414
  ].join('\n');
377
415
  }
416
+ // 4.4 Issue/PR 搜索列表(search_issues / search_prs 共用,字段同构)
417
+ if (Array.isArray(value.items) && (args?.action === 'search_issues' || args?.action === 'search_prs')) {
418
+ if (value.items.length === 0)
419
+ return '未找到匹配的结果。';
420
+ const kind = args.action === 'search_prs' ? 'PR' : 'Issue';
421
+ const rows = value.items.map((it) => {
422
+ const num = it.number ?? '-';
423
+ const repo = it.repository?.nameWithOwner || it.repository?.fullName || '-';
424
+ const link = it.url ? `[#${num}](${it.url})` : `#${num}`;
425
+ const badge = formatStateBadge(it.state, it.isDraft);
426
+ const author = it.author?.login || '-';
427
+ const date = it.updatedAt ? String(it.updatedAt).slice(0, 10) : '-';
428
+ return `| ${link} | ${sanitizeCell(it.title, 45)} | ${badge} | ${repo} | ${author} | ${date} |`;
429
+ });
430
+ return [
431
+ `**${kind} 检索结果 (${value.items.length} 条):**\n`,
432
+ '| 编号 | 标题 | 状态 | 仓库 | 作者 | 更新日期 |',
433
+ '| :--- | :--- | :--- | :--- | :--- | :--- |',
434
+ ...rows,
435
+ ].join('\n');
436
+ }
437
+ // 4.5 Commit 搜索列表(嵌套 commit.commit.message 取首行)
438
+ if (Array.isArray(value.items) && args?.action === 'search_commits') {
439
+ if (value.items.length === 0)
440
+ return '未找到匹配的提交。';
441
+ const rows = value.items.map((c) => {
442
+ const sha = `\`${String(c.sha || '').slice(0, 7)}\``;
443
+ const msg = sanitizeCell(String(c.commit?.message || '').split('\n')[0], 50);
444
+ const author = c.commit?.author?.name || c.author?.login || '-';
445
+ const repo = c.repository?.fullName || '-';
446
+ return `| ${sha} | ${msg} | ${author} | ${repo} |`;
447
+ });
448
+ return [
449
+ `**提交检索结果 (${value.items.length} 条):**\n`,
450
+ '| SHA | 提交信息 | 作者 | 仓库 |',
451
+ '| :--- | :--- | :--- | :--- |',
452
+ ...rows,
453
+ ].join('\n');
454
+ }
455
+ // 4.6 目录清单(read_dir:dir 置顶 + size 人性化;mode 八进制是 Git 内部语义不展示)
456
+ if (args?.action === 'read_dir' && Array.isArray(value.items)) {
457
+ if (value.items.length === 0)
458
+ return `**${value.path || '/'}** 为空目录。` + appendDegradedNote(value);
459
+ const sorted = [...value.items].sort((a, b) => (a.type === b.type ? String(a.name).localeCompare(String(b.name)) : a.type === 'dir' ? -1 : 1));
460
+ const rows = sorted.map((e) => {
461
+ const icon = e.type === 'dir' ? '📁' : '📄';
462
+ const size = e.type === 'dir' ? '-' : (typeof e.size === 'number' ? (e.size > 1024 ? `${(e.size / 1024).toFixed(1)}KB` : `${e.size}B`) : '-');
463
+ return `| ${icon} \`${sanitizeCell(e.name, 60)}\` | ${size} |`;
464
+ });
465
+ return [
466
+ `**目录 ${value.path || '/'}(${value.items.length} 项):**\n`,
467
+ '| 名称 | 大小 |',
468
+ '| :--- | :--- |',
469
+ ...rows,
470
+ appendDegradedNote(value),
471
+ ].filter((l) => l !== '').join('\n');
472
+ }
473
+ // 4.7 文件内容(read_file:代码块 + 元信息行)
474
+ if (args?.action === 'read_file' && typeof value.content === 'string') {
475
+ const ext = (value.path || '').split('.').pop() || '';
476
+ const size = typeof value.size === 'number' ? (value.size > 1024 ? `${(value.size / 1024).toFixed(1)}KB` : `${value.size}B`) : '-';
477
+ return [
478
+ `**📄 \`${value.path}\`** (${size}${value.ref ? ` @ ${value.ref}` : ''})\n`,
479
+ '```' + ext,
480
+ value.content,
481
+ '```',
482
+ appendDegradedNote(value),
483
+ ].filter((l) => l !== '').join('\n');
484
+ }
378
485
  // 兜底降级
379
486
  return JSON.stringify(value, null, 2);
380
487
  }
package/lib/tools/help.js CHANGED
@@ -7,15 +7,16 @@ const KNOWLEDGE_BASE = {
7
7
  | :--- | :--- | :--- |
8
8
  | \`github_pr\` | Pull Request 管理、审查与本地检出 | 查看 PR 列表/详情、比对 diff、本地检出代码验证、检查 CI 状态、执行带确认合并 |
9
9
  | \`github_issue\` | Issue 任务追踪与分支开发关联 | 筛选待办议题、追加评论、编辑标题/标签、从 Issue 一键切出关联开发分支 |
10
- | \`github_run\` | GitHub Actions 构建排查与工作流管理 | 查看构建列表、提取失败日志 (log_failed)、重新触发、手动触发 workflow_dispatch |
11
- | \`github_repo\` | 仓库元数据、Fork 同步与全局检索 | 查看仓库与 topics、一键拉平 Fork 仓库 (sync)、检索全局代码与仓库 |
10
+ | \`github_run\` | Actions 构建排查与工作流管理 | 查看构建列表、提取失败日志 (log_failed)、重新触发、手动触发 workflow_dispatch、启停工作流、下载构建产物 |
11
+ | \`github_repo\` | 仓库元数据、Fork 同步与全局检索 | 查看仓库与 topics、一键拉平 Fork (sync)、检索代码/仓库/Issue/PR/提交、免 clone 读取任意文件与目录 |
12
12
  | \`github_api\` | 原生 GitHub REST/GraphQL 逃生门 | 调用任意端点、获取原始文件字节 (raw)、支持自动翻页 (paginate) |
13
13
  | \`github_release\` | 版本发布与构建资产交付 | 查看发版历史、自动生成 Release Notes、上传 .tgz 等构建产物 |
14
- | \`github_secret\` | CI/CD 部署密钥安全管理 | 查看 NPM_TOKEN 等密钥清单、经 stdin 安全写入密文、删除密钥 |
14
+ | \`github_secret\` | CI/CD 密钥与变量管理 | 查看 NPM_TOKEN 等密钥清单、经 stdin 安全写入密文、删除密钥;kind:'variable' 管理明文变量(set/get/delete) |
15
15
  | \`github_skill\` | 跨 Agent 开源 Skills 生态发现 | 检索/预览/安装 GitHub 上的 Agent Skills (gh preview 特性) |
16
16
  | \`github_help\` | 路由导航、官方语法速查与故障自愈 | 遇到不确定的操作、搜索语法或接口报错时查询用法与诊断配方 |
17
17
 
18
- 💡 **使用准则**:优先调用高层语义工具(pr / issue / run / repo / release / secret);当高层工具无法满足特殊接口需求时,再降级使用 \`github_api\`。`,
18
+ 💡 **使用准则**:优先调用高层语义工具(pr / issue / run / repo / release / secret);当高层工具无法满足特殊接口需求时,再降级使用 \`github_api\`。
19
+ 🗺️ **命令对照**:不确定某 gh 命令对应哪个工具?\`github_help(topic: 'mapping')\` 查询完整映射表(含未覆盖域的 API 引导)。`,
19
20
  pr: `### 🔀 github_pr 详细操作指南
20
21
 
21
22
  - **查看列表**: \`github_pr(action: 'list', state: 'open' | 'closed' | 'merged' | 'all', limit: 20, author: '@me')\`
@@ -29,7 +30,12 @@ const KNOWLEDGE_BASE = {
29
30
  - **检查冲突状态**: \`github_pr(action: 'status')\`
30
31
  - 查看当前分支关联 PR 的 merge conflict 状态。
31
32
  - **安全合并**: \`github_pr(action: 'merge', pr_number: 123, confirm: true, merge_method: 'squash', delete_branch: false)\`
32
- - 高危操作必须显式附带 \`confirm: true\`;若未传将返回拦截警告与待合并影响报告。`,
33
+ - 高危操作必须显式附带 \`confirm: true\`;若未传将返回拦截警告与待合并影响报告。
34
+ - **Draft 切换**: \`github_pr(action: 'ready', pr_number: 123)\`;反向转回 Draft 附加 \`undo: true\`
35
+ - **反向提交**: \`github_pr(action: 'revert', pr_number: 123, confirm: true)\`
36
+ - 在远端创建反向提交与新 PR(非本地操作),需 confirm。
37
+ - **分支落后自愈**: \`github_pr(action: 'update_branch', pr_number: 123)\`
38
+ - 将基准分支最新提交同步进 PR 分支,解决「分支落后需更新」的 CI 门禁失败。`,
33
39
  issue: `### 🎯 github_issue 详细操作指南
34
40
 
35
41
  - **筛选 Issue**: \`github_issue(action: 'list', state: 'open' | 'closed' | 'all', labels: ['bug'], assignees: ['octocat'], limit: 20)\`
@@ -44,7 +50,15 @@ const KNOWLEDGE_BASE = {
44
50
  - **编辑 Issue**: \`github_issue(action: 'edit', issue_number: 42, title: '新标题', add_labels: ['p1'], remove_labels: ['triage'])\`
45
51
  - **追加讨论**: \`github_issue(action: 'comment', issue_number: 42, body: '定位到问题原因...')\`
46
52
  - **安全关闭**: \`github_issue(action: 'close', issue_number: 42, confirm: true)\`
47
- - 关闭操作必须附带 \`confirm: true\`。`,
53
+ - 关闭操作必须附带 \`confirm: true\`。
54
+ - **物理删除**: \`github_issue(action: 'delete', issue_number: 42, confirm: true)\`
55
+ - 与 close 有本质区别:编号永久消失、评论湮灭、GraphQL 层无法恢复——拦截级别高于 close。
56
+ - **跨仓库迁移**: \`github_issue(action: 'transfer', issue_number: 42, target_repo: 'other/repo', confirm: true)\`
57
+ - 原 URL 301 重定向,归属改变不可静默撤回。
58
+ - **置顶**: \`github_issue(action: 'pin'/'unpin', issue_number: 42)\`(可逆状态位)
59
+ - **锁定对话**: \`github_issue(action: 'lock', issue_number: 42, lock_reason: 'resolved')\`
60
+ - reason 合法值:off_topic / resolved / spam / too_heated;解锁用 \`action: 'unlock'\`(独立命令)。
61
+ - **我的分配议题**: \`github_issue(action: 'status')\`(账号维度面板)`,
48
62
  run: `### 🚀 github_run 详细操作指南
49
63
 
50
64
  - **过滤构建记录**: \`github_run(action: 'list', branch: 'main', status: 'failure', limit: 10)\`
@@ -53,7 +67,14 @@ const KNOWLEDGE_BASE = {
53
67
  - 专为 Agent 优化的失败信息提取能力,直接定位报错位置;若无失败步骤则优雅提示。
54
68
  - **主动触发工作流**: \`github_run(action: 'trigger', workflow: 'pr-check.yml', ref: 'main', inputs: { env: 'staging' })\`
55
69
  - 提交代码修复后,主动触发远端 CI/CD 验证。
56
- - **重新执行失败任务**: \`github_run(action: 'rerun', run_id: '12345678')\``,
70
+ - **重新执行失败任务**: \`github_run(action: 'rerun', run_id: '12345678')\`
71
+ - **工作流查询**: \`github_run(action: 'workflow_list', all: true)\`
72
+ - 默认仅 active;\`all: true\` 含 disabled(排查「工作流为何不跑」时 disabled 本身就是答案)。
73
+ - **工作流详情**: \`github_run(action: 'workflow_view', workflow: 'npm-publish.yml')\`(名称/ID/文件名均可定位)
74
+ - **启停工作流**: \`github_run(action: 'workflow_enable'/'workflow_disable', workflow: '…')\`
75
+ - disable 后 trigger 将静默失败,直至重新 enable——输出会显式警告这一点。
76
+ - **下载构建产物**: \`github_run(action: 'download', run_id: '12345678', name: '构建产物名'?)\`
77
+ - 落盘目录由插件固定管理(\`~/.dsh/data/dsh-github-flow/artifacts/\` 下按仓库与 run 隔离),返回文件清单。`,
57
78
  repo: `### 📦 github_repo 详细操作指南
58
79
 
59
80
  - **查看元数据与标签**: \`github_repo(action: 'view')\`
@@ -63,7 +84,13 @@ const KNOWLEDGE_BASE = {
63
84
  - **下载图片查看**: 附加 \`image_urls: [...]\` 精准下载指定图(留空下载全部截图);护栏与 Issue/PR 图片一致,触顶返回 remaining 可续传。
64
85
  - **一键拉平 Fork 主干 (防止 Stale)**: \`github_repo(action: 'sync', branch: 'main')\`
65
86
  - 解决开源项目贡献中因本地 Fork 落后导致上游 CI 门禁失败的顽疾。
66
- - **代码检索**: \`github_repo(action: 'search_code', query: 'filename:package.json dsh.bundle')\``,
87
+ - **代码检索**: \`github_repo(action: 'search_code', query: 'filename:package.json dsh.bundle')\`
88
+ - **Issue/PR/Commit 检索**: \`github_repo(action: 'search_issues'/'search_prs'/'search_commits', query: 'repo:o/r state:open')\`
89
+ - 限定符直接写进 query(gh 惯例);注意 commits 必须含搜索文本,纯限定符会被 gh 拒绝。
90
+ - **免 clone 读单文件**: \`github_repo(action: 'read_file', path: 'src/index.ts', ref: 'dev'?)\`
91
+ - gh ≥2.95 直连;低版本自动降级 contents API(输出带降级标注)。二进制文件会被拒绝并引导 github_api。
92
+ - **免 clone 列目录**: \`github_repo(action: 'read_dir', path: '.github/workflows'?)\`
93
+ - 返回目录清单(dir 置顶 + 大小),降级链路同上。`,
67
94
  api: `### ⚡ github_api 底层逃生门指南
68
95
 
69
96
  - **标准 GET 请求带参数**:
@@ -89,7 +116,9 @@ const KNOWLEDGE_BASE = {
89
116
  - **下载资产**: \`github_release(action: 'download', tag: 'v0.1.9', pattern: '*.tgz', dir: './dist')\`
90
117
  - **删除 Release**: \`github_release(action: 'delete', tag: 'v0.1.9', confirm: true)\`
91
118
  - 高危不可逆操作,必须显式确认。`,
92
- secret: `### 🔐 github_secret 部署密钥管理指南
119
+ secret: `### 🔐 github_secret 密钥与变量管理指南
120
+
121
+ 支持两类对象(kind 分流):**Secret**(密文,默认)与 **Variable**(CI 明文变量)。
93
122
 
94
123
  - **查看密钥清单**: \`github_secret(action: 'list')\`
95
124
  - 仅返回名称、可见性与更新时间;GitHub API 设计上就不允许读取密文。
@@ -99,7 +128,11 @@ const KNOWLEDGE_BASE = {
99
128
  - gh 在本地用仓库公钥加密后才上传,明文不落盘。
100
129
  - **组织/环境级密钥**: 附加 \`org: 'my-org'\` 或 \`env: 'production'\` 参数切换作用域。
101
130
  - **删除密钥**: \`github_secret(action: 'delete', name: 'NPM_TOKEN', confirm: true)\`
102
- - 删除后引用该密钥的 CI 工作流会立即失败,务必先确认无工作流依赖。`,
131
+ - 删除后引用该密钥的 CI 工作流会立即失败,务必先确认无工作流依赖。
132
+ - **变量操作(CI 明文)**: 附加 \`kind: 'variable'\`
133
+ - \`github_secret(action: 'set', kind: 'variable', name: 'DEPLOY_ENV', value: 'staging')\`——经 -b 直传(明文无 stdin 红线)。
134
+ - \`github_secret(action: 'get', kind: 'variable', name: 'DEPLOY_ENV')\`——可回显值(这正是变量与密钥的设计区别:secret 写入后 GitHub 侧加密,物理不可读取)。
135
+ - list/delete 与 secret 同形(delete 需 confirm)。`,
103
136
  skill: `### 🧠 github_skill Agent Skills 生态指南
104
137
 
105
138
  基于 GitHub CLI 的 \`gh skill\` preview 特性(需 gh >= 2.9x),跨仓库发现与安装开源 Agent Skills:
@@ -114,6 +147,93 @@ const KNOWLEDGE_BASE = {
114
147
  - 支持的 agent 值:claude-code / cursor / github-copilot / codex / gemini-cli 等。
115
148
  - **查看已安装**: \`github_skill(action: 'list')\`
116
149
  - ⚠️ 若报「unknown command」,说明宿主机 gh 版本过低,需升级到 2.9x 以上。`,
150
+ mapping: `### 🗺️ gh 命令 ↔ 插件工具映射总表
151
+
152
+ #### 一、已覆盖域映射(语义化 action 精确对照)
153
+
154
+ **PR(github_pr,13 action)**
155
+ | gh 命令 | 插件调用 | 备注 |
156
+ | :--- | :--- | :--- |
157
+ | \`gh pr list/view/create/diff/edit/comment\` | \`github_pr(action:'list'/'view'/'create'/'diff'/'edit'/'comment')\` | 高频基础操作 |
158
+ | \`gh pr close\` | 暂无专用 action——用 \`github_pr(action:'edit')\` 或 \`github_api(endpoint:'repos/{o}/{r}/pulls/{n}', method:'PATCH', fields:{state:'closed'})\` | 插件侧未覆盖 |
159
+ | \`gh pr checks\` | \`github_pr(action:'checks', pr_number:n)\` | CI 状态药丸 |
160
+ | \`gh pr review\` | \`github_pr(action:'review', review_decision:'APPROVE'…)\` | |
161
+ | \`gh pr merge\` | \`github_pr(action:'merge', confirm:true)\` | 高危确认 |
162
+ | \`gh pr checkout\` | \`github_pr(action:'checkout')\` | 本地检出 |
163
+ | \`gh pr status\` | \`github_pr(action:'status')\` | 当前分支关联 PR |
164
+ | \`gh pr ready [--undo]\` | \`github_pr(action:'ready', undo:true?)\` | Draft↔Ready 可逆 |
165
+ | \`gh pr revert\` | \`github_pr(action:'revert', confirm:true)\` | 远端创建反向 PR |
166
+ | \`gh pr update-branch\` | \`github_pr(action:'update_branch')\` | 分支落后自愈 |
167
+
168
+ **Issue(github_issue,16 action)**
169
+ | gh 命令 | 插件调用 | 备注 |
170
+ | :--- | :--- | :--- |
171
+ | \`gh issue list/view/create/comment/close/reopen/edit\` | \`github_issue(action:…)\` | 基础操作 |
172
+ | \`gh issue develop\` | \`github_issue(action:'develop', branch_name:…)\` | 关联开发分支 |
173
+ | \`gh issue delete\` | \`github_issue(action:'delete', confirm:true)\` | 物理删除(非 close) |
174
+ | \`gh issue transfer\` | \`github_issue(action:'transfer', target_repo:…, confirm:true)\` | 跨仓库迁移 |
175
+ | \`gh issue pin/unpin\` | \`github_issue(action:'pin'/'unpin')\` | 置顶状态位 |
176
+ | \`gh issue lock/unlock\` | \`github_issue(action:'lock', lock_reason:'resolved'…)\` | reason 四枚举 |
177
+ | \`gh issue status\` | \`github_issue(action:'status')\` | 我的分配议题面板 |
178
+
179
+ **Actions(github_run,11 action)——注意 workflow 域也在此工具**
180
+ | gh 命令 | 插件调用 | 备注 |
181
+ | :--- | :--- | :--- |
182
+ | \`gh run list/view/rerun/cancel\` | \`github_run(action:…)\` | |
183
+ | \`gh run view --log-failed\` | \`github_run(action:'log_failed', run_id:…)\` | 失败日志提取 |
184
+ | \`gh run download\` | \`github_run(action:'download', run_id:…)\` | 落盘插件管理目录 |
185
+ | \`gh workflow run\` | \`github_run(action:'trigger', workflow:…)\` | workflow_dispatch |
186
+ | \`gh workflow list/view\` | \`github_run(action:'workflow_list'/'workflow_view')\` | all:true 含 disabled |
187
+ | \`gh workflow enable/disable\` | \`github_run(action:'workflow_enable'/'workflow_disable')\` | disable 后 trigger 静默失败 |
188
+
189
+ **Repo(github_repo,10 action)——注意 search 域也在此工具**
190
+ | gh 命令 | 插件调用 | 备注 |
191
+ | :--- | :--- | :--- |
192
+ | \`gh repo view\` | \`github_repo(action:'view')\` | 元数据 |
193
+ | (README 读取,gh 无专用命令) | \`github_repo(action:'readme', repo:…)\` | 原文 + 图片清单/下载(插件独有) |
194
+ | \`gh repo sync\` | \`github_repo(action:'sync', force:true?)\` | Fork 对齐 |
195
+ | \`gh repo read-file\` | \`github_repo(action:'read_file', path:…)\` | 免 clone 读文件,gh<2.95 自动降级 API |
196
+ | \`gh repo read-dir\` | \`github_repo(action:'read_dir', path:?)\` | 目录清单,同上降级 |
197
+ | \`gh search code/repos\` | \`github_repo(action:'search_code'/'search_repos')\` | |
198
+ | \`gh search issues/prs/commits\` | \`github_repo(action:'search_issues'/'search_prs'/'search_commits')\` | 限定符写进 query |
199
+
200
+ **Release(github_release,6 action)**
201
+ \`gh release list/view/create/upload/download/delete\` → \`github_release(action:…)\`;delete/clobber 需 confirm。
202
+
203
+ **Secret 与 Variable(github_secret,kind 分流)——注意 variable 域也在此工具**
204
+ | gh 命令 | 插件调用 | 备注 |
205
+ | :--- | :--- | :--- |
206
+ | \`gh secret list/set/delete\` | \`github_secret(action:…)\` | 密文走 stdin,永不回显 |
207
+ | \`gh variable list/set/get/delete\` | \`github_secret(action:…, kind:'variable')\` | CI 明文:-b 直传、get 可回显 |
208
+ | (gh secret get 不存在) | — | GitHub 侧加密存储,物理不可读取 |
209
+
210
+ **Skill(github_skill,4 action)**
211
+ \`gh skill search/preview/install/list\` → \`github_skill(action:…)\`;install 需 confirm(写磁盘)。
212
+
213
+ #### 二、归属提示(命令域与工具名不同构)
214
+
215
+ - \`gh workflow …\` / \`gh search …\` / \`gh variable …\` 分别归 **github_run / github_repo / github_secret**(不是独立工具)
216
+ - \`gh api\` 的等价物是 \`github_api\`(全端点逃生门,支持 raw/paginate/jq)
217
+ - 图片查看:\`github_issue\`/\`github_pr\` 的 \`view_images\`、\`github_repo\` 的 \`readme\` + image_urls(插件独有能力,gh 无对应命令)
218
+
219
+ #### 三、未覆盖域引导(经 github_api 调用)
220
+
221
+ | gh 域 | 用途 | 推荐 REST 端点 |
222
+ | :--- | :--- | :--- |
223
+ | \`gh label\` | 标签管理 | \`repos/{o}/{r}/labels\` |
224
+ | \`gh cache\` | Actions 缓存 | \`repos/{o}/{r}/actions/caches\` |
225
+ | \`gh gist\` | 代码片段 | \`gists\` |
226
+ | \`gh ruleset\` | 分支保护规则 | \`repos/{o}/{r}/rulesets\` |
227
+ | \`gh project\` | Projects v2 | \`graphql\`(Projects API 仅 GraphQL) |
228
+ | \`gh discussion\` | 讨论 | \`graphql\`(需仓库开启 discussions) |
229
+ | \`gh codespace\` | 云端开发环境 | \`user/codespaces\` |
230
+ | \`gh org\` | 组织信息 | \`orgs/{org}\` |
231
+ | \`gh auth/config/extension/alias\` | 本机配置类 | 多数无需 API(本机文件/命令) |
232
+ | \`gh browse\` | 打开浏览器 | 直接给用户 URL 即可 |
233
+ | \`gh attestation/gpg-key/ssh-key\` | 签名与密钥 | \`repos/{o}/{r}/attestations\` 等 |
234
+ | \`gh agent-task\` | Copilot 会话 | \`graphql\`(需 Copilot 授权) |
235
+
236
+ > 用法示例:\`github_api(endpoint: 'repos/owner/repo/labels', method: 'POST', fields: { name: 'bug', color: 'ff0000' })\``,
117
237
  search_syntax: `### 🔍 GitHub 官方高级检索语法速查手册
118
238
 
119
239
  在 \`github_repo\`、\`github_api\` 或搜索场景中,请严格遵守 GitHub 官方搜索语法:
@@ -166,10 +286,11 @@ export function createHelpTool(executor) {
166
286
  'release',
167
287
  'secret',
168
288
  'skill',
289
+ 'mapping',
169
290
  'search_syntax',
170
291
  'troubleshooting',
171
292
  ],
172
- description: '帮助主题分类。留空则返回全局工具箱导航。',
293
+ description: '帮助主题分类。mapping 提供 gh 命令与插件工具的完整对照表(不确定某 gh 命令对应哪个工具时查询)。留空则返回全局工具箱导航。',
173
294
  },
174
295
  gh_command: {
175
296
  type: 'string',
@@ -15,6 +15,15 @@ export declare function createIssueTool(executor: GhExecutor): {
15
15
  type: string;
16
16
  description: string;
17
17
  };
18
+ target_repo: {
19
+ type: string;
20
+ description: string;
21
+ };
22
+ lock_reason: {
23
+ type: string;
24
+ enum: string[];
25
+ description: string;
26
+ };
18
27
  repo: {
19
28
  type: string;
20
29
  description: string;
@@ -20,12 +20,21 @@ export function createIssueTool(executor) {
20
20
  properties: {
21
21
  action: {
22
22
  type: 'string',
23
- enum: ['list', 'view', 'create', 'comment', 'close', 'reopen', 'edit', 'develop', 'view_images'],
24
- description: 'Issue 操作类型。view_images 下载正文内的图片到本地并返回可渲染的图片内容块(Agent 可直接"看"用户报障截图)。',
23
+ enum: ['list', 'view', 'create', 'comment', 'close', 'reopen', 'edit', 'develop', 'view_images', 'delete', 'transfer', 'pin', 'unpin', 'lock', 'unlock', 'status'],
24
+ description: 'Issue 操作类型。view_images 下载正文内的图片到本地并返回可渲染的图片内容块(Agent 可直接"看"用户报障截图)。delete 物理删除不可恢复、transfer 跨仓库迁移,两者均需 confirm: true。',
25
25
  },
26
26
  issue_number: {
27
27
  type: 'number',
28
- description: 'Issue 编号(view, comment, close, reopen, edit, develop 时使用)。',
28
+ description: 'Issue 编号(view, comment, close, reopen, edit, develop, delete, transfer, pin/unpin/lock/unlock 时使用)。',
29
+ },
30
+ target_repo: {
31
+ type: 'string',
32
+ description: 'transfer 动作必填:迁移目标仓库 [owner/repo](迁移后原 URL 301 重定向,归属不可静默撤回)。',
33
+ },
34
+ lock_reason: {
35
+ type: 'string',
36
+ enum: ['off_topic', 'resolved', 'spam', 'too_heated'],
37
+ description: 'lock 动作可选:锁定原因(gh 实测四合法值)。',
29
38
  },
30
39
  repo: {
31
40
  type: 'string',
@@ -74,7 +83,7 @@ export function createIssueTool(executor) {
74
83
  },
75
84
  confirm: {
76
85
  type: 'boolean',
77
- description: '高危破坏性操作确认标志(close 动作必需)。若为 false 则仅输出待关闭警告。',
86
+ description: '高危破坏性操作确认标志(close/delete/transfer 动作必需)。delete 物理删除不可恢复、transfer 跨仓库迁移,拦截级别高于 close。',
78
87
  },
79
88
  image_urls: {
80
89
  type: 'array',
@@ -277,6 +286,81 @@ export function createIssueTool(executor) {
277
286
  throw new Error(res.error);
278
287
  return { message: `Issue #${args.issue_number} 已重新开启` };
279
288
  }
289
+ // ---- 变更面新增(S3)----
290
+ case 'pin':
291
+ case 'unpin': {
292
+ if (!args.issue_number)
293
+ throw new Error(`${args.action} 操作必须提供 issue_number`);
294
+ // 置顶状态位:可逆(pin↔unpin 互逆),按轻变更免 confirm
295
+ const res = await executor.run(['issue', args.action, String(args.issue_number), ...repoArgs], runOpts);
296
+ if (!res.ok)
297
+ throw new Error(res.error);
298
+ return { message: `Issue #${args.issue_number} 已${args.action === 'pin' ? '置顶' : '取消置顶'}` };
299
+ }
300
+ case 'lock':
301
+ case 'unlock': {
302
+ if (!args.issue_number)
303
+ throw new Error(`${args.action} 操作必须提供 issue_number`);
304
+ // unlock 是 gh 独立命令(非 lock 的 flag,实测 help 已钉);锁定对话可逆,免 confirm
305
+ const cmd = ['issue', args.action, String(args.issue_number), ...repoArgs];
306
+ if (args.action === 'lock' && args.lock_reason) {
307
+ cmd.push('--reason', args.lock_reason);
308
+ }
309
+ const res = await executor.run(cmd, runOpts);
310
+ if (!res.ok)
311
+ throw new Error(res.error);
312
+ return {
313
+ message: `Issue #${args.issue_number} 对话已${args.action === 'lock' ? '锁定' : '解锁'}`,
314
+ ...(args.action === 'lock' && args.lock_reason ? { reason: args.lock_reason } : {}),
315
+ };
316
+ }
317
+ case 'delete': {
318
+ if (!args.issue_number)
319
+ throw new Error('delete 操作必须提供 issue_number');
320
+ // 物理删除 > close 的拦截级别:issue 编号永久消失、评论湮灭、GraphQL 无法恢复
321
+ if (args.confirm !== true) {
322
+ return {
323
+ status: 'AWAITING_CONFIRMATION',
324
+ warning: `⚠️ 高危操作拦截:你即将**物理删除** Issue #${args.issue_number}(非 close,不可恢复)。`,
325
+ impact: 'Issue 编号将永久消失,全部评论与附件随之湮灭,GraphQL 层无法恢复——这与 close(可 reopen 逆转)有本质区别。',
326
+ requiredAction: '如确认删除,请与用户核实后附加 `confirm: true` 重新调用。',
327
+ };
328
+ }
329
+ // 非 TTY 下 gh issue delete 会挂起等待 prompt,--yes 是非交互执行的唯一途径
330
+ const res = await executor.run(['issue', 'delete', String(args.issue_number), ...repoArgs, '--yes'], runOpts);
331
+ if (!res.ok)
332
+ throw new Error(res.error);
333
+ return { message: `Issue #${args.issue_number} 已永久删除(不可恢复)` };
334
+ }
335
+ case 'transfer': {
336
+ if (!args.issue_number)
337
+ throw new Error('transfer 操作必须提供 issue_number');
338
+ if (!args.target_repo)
339
+ throw new Error('transfer 操作必须提供 target_repo [owner/repo]');
340
+ if (args.target_repo === repoTarget) {
341
+ throw new Error(`transfer 目标仓库 (${args.target_repo}) 与当前仓库相同,迁移无意义。`);
342
+ }
343
+ // 跨仓库迁移:原 URL 301 但归属改变,不可静默撤回——与 delete 同级 confirm
344
+ if (args.confirm !== true) {
345
+ return {
346
+ status: 'AWAITING_CONFIRMATION',
347
+ warning: `⚠️ 高危操作拦截:你即将把 Issue #${args.issue_number} 从 ${repoTarget} 迁移到 ${args.target_repo}。`,
348
+ impact: `迁移后 Issue 归属 ${args.target_repo}(原 URL 301 重定向),标签/里程碑不随迁,操作不可静默撤回。需要目标仓库的写权限。`,
349
+ requiredAction: '如确认迁移,请与用户核实后附加 `confirm: true` 重新调用。',
350
+ };
351
+ }
352
+ const res = await executor.run(['issue', 'transfer', String(args.issue_number), args.target_repo, ...repoArgs], runOpts);
353
+ if (!res.ok)
354
+ throw new Error(res.error);
355
+ return { message: `Issue #${args.issue_number} 已迁移至 ${args.target_repo}`, url: res.rawOutput };
356
+ }
357
+ case 'status': {
358
+ // 非 TTY 纯文本分段(Assigned/Mentioning/Pushed),gh 原生已是紧凑格式
359
+ const res = await executor.run(['issue', 'status', ...repoArgs], { ...runOpts, rawText: true, timeoutMs: 20_000 });
360
+ if (!res.ok)
361
+ throw new Error(res.error);
362
+ return { result: res.rawOutput || '无分配议题' };
363
+ }
280
364
  default:
281
365
  throw new Error(`未知的 Issue 操作: ${args.action}`);
282
366
  }
package/lib/tools/pr.d.ts CHANGED
@@ -52,6 +52,10 @@ export declare function createPrTool(executor: GhExecutor): {
52
52
  type: string;
53
53
  description: string;
54
54
  };
55
+ undo: {
56
+ type: string;
57
+ description: string;
58
+ };
55
59
  state: {
56
60
  type: string;
57
61
  enum: string[];
package/lib/tools/pr.js CHANGED
@@ -18,8 +18,8 @@ export function createPrTool(executor) {
18
18
  properties: {
19
19
  action: {
20
20
  type: 'string',
21
- enum: ['list', 'view', 'create', 'diff', 'checks', 'review', 'merge', 'checkout', 'status', 'view_images'],
22
- description: 'PR 操作类型。view_images 下载正文与评论中的图片(UI 变更对比图、复现截图等)到本地并以图片内容块返回,供 Agent 审查时直接查看。',
21
+ enum: ['list', 'view', 'create', 'diff', 'checks', 'review', 'merge', 'checkout', 'status', 'view_images', 'ready', 'revert', 'update_branch'],
22
+ description: 'PR 操作类型。view_images 下载正文与评论中的图片(UI 变更对比图、复现截图等)到本地并以图片内容块返回,供 Agent 审查时直接查看。merge/revert 需 confirm: true(revert 在远端创建反向提交与新 PR)。',
23
23
  },
24
24
  pr_number: {
25
25
  type: 'number',
@@ -61,7 +61,11 @@ export function createPrTool(executor) {
61
61
  },
62
62
  delete_branch: {
63
63
  type: 'boolean',
64
- description: '合并后是否删除源分支(merge 操作时使用,默认 false)。删除分支不可撤销,只在确认该分支已无用后再开启。',
64
+ description: '合并/revert 后是否删除源分支(merge、revert 操作时使用,默认 false)。删除分支不可撤销,只在确认该分支已无用后再开启。',
65
+ },
66
+ undo: {
67
+ type: 'boolean',
68
+ description: 'ready 动作可选:反向操作,把 PR 转回 Draft(gh 原生 --undo)。',
65
69
  },
66
70
  state: {
67
71
  type: 'string',
@@ -303,6 +307,49 @@ export function createPrTool(executor) {
303
307
  throw new Error(res.error);
304
308
  return { message: 'PR 合并完成', output: res.rawOutput };
305
309
  }
310
+ // ---- 变更面新增(S3)----
311
+ case 'ready': {
312
+ if (!args.pr_number)
313
+ throw new Error('ready 操作必须提供 pr_number');
314
+ // Draft→Ready;gh 原生支持 --undo 反向(转回 draft),完整可逆,免 confirm
315
+ const cmd = ['pr', 'ready', String(args.pr_number), ...repoArgs];
316
+ if (args.undo === true)
317
+ cmd.push('--undo');
318
+ const res = await executor.run(cmd, runOpts);
319
+ if (!res.ok)
320
+ throw new Error(res.error);
321
+ return { message: `PR #${args.pr_number} 已${args.undo === true ? '转回 Draft' : '从 Draft 转为 Ready(进入正式评审)'}` };
322
+ }
323
+ case 'revert': {
324
+ if (!args.pr_number)
325
+ throw new Error('revert 操作必须提供 pr_number');
326
+ // revert 在远端创建反向提交与新 PR——虽不碰原 PR,但产生新实体,按 merge 级 confirm(D5)
327
+ if (args.confirm !== true) {
328
+ return {
329
+ status: 'AWAITING_CONFIRMATION',
330
+ warning: `⚠️ 高危操作拦截:你即将对 PR #${args.pr_number} 创建反向提交(revert)。`,
331
+ impact: '将在远端创建反向提交并生成新的 revert PR——这是新增远端实体,不是本地操作。',
332
+ requiredAction: '如确认执行,请向用户明确确认并附加 `confirm: true` 重新调用。',
333
+ };
334
+ }
335
+ const revertCmd = ['pr', 'revert', String(args.pr_number), ...repoArgs];
336
+ if (args.delete_branch === true)
337
+ revertCmd.push('--delete-branch');
338
+ const res = await executor.run(revertCmd, runOpts);
339
+ if (!res.ok)
340
+ throw new Error(res.error);
341
+ return { message: `PR #${args.pr_number} 的反向提交已创建`, url: res.rawOutput };
342
+ }
343
+ case 'update_branch': {
344
+ if (!args.pr_number)
345
+ throw new Error('update_branch 操作必须提供 pr_number');
346
+ // 等价 repo sync 的单 PR 版(push merge commit 到 PR 分支);恰是「分支落后」
347
+ // 的自愈动作,confirm 摩擦会阻碍 Agent 自愈闭环(D6),输出同步摘要即可
348
+ const res = await executor.run(['pr', 'update-branch', String(args.pr_number), ...repoArgs], runOpts);
349
+ if (!res.ok)
350
+ throw new Error(res.error);
351
+ return { message: `PR #${args.pr_number} 分支已从基准分支同步更新`, output: res.rawOutput };
352
+ }
306
353
  default:
307
354
  throw new Error(`未知的 PR 操作: ${args.action}`);
308
355
  }
@@ -29,6 +29,14 @@ export declare function createRepoTool(executor: GhExecutor): {
29
29
  type: string;
30
30
  description: string;
31
31
  };
32
+ path: {
33
+ type: string;
34
+ description: string;
35
+ };
36
+ ref: {
37
+ type: string;
38
+ description: string;
39
+ };
32
40
  limit: {
33
41
  type: string;
34
42
  description: string;