dsh-github-flow 0.6.0 → 0.7.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.
Files changed (83) hide show
  1. package/README.en.md +98 -15
  2. package/README.md +101 -15
  3. package/docs/UPGRADE-NOTES.md +12 -11
  4. package/docs/architecture.md +56 -13
  5. package/lib/api/route-helpers.d.ts +19 -0
  6. package/lib/api/route-helpers.js +54 -0
  7. package/lib/api/routes-actions.js +75 -1
  8. package/lib/api/routes-lifecycle.js +8 -1
  9. package/lib/api/routes-overview.d.ts +26 -0
  10. package/lib/api/routes-overview.js +276 -34
  11. package/lib/api/routes-proxy.js +8 -1
  12. package/lib/api/routes.d.ts +11 -1
  13. package/lib/api/routes.js +15 -2
  14. package/lib/cache.d.ts +0 -21
  15. package/lib/cache.js +0 -37
  16. package/lib/client.d.ts +2 -1
  17. package/lib/client.js +1585 -429
  18. package/lib/destructive.d.ts +59 -0
  19. package/lib/destructive.js +285 -0
  20. package/lib/executor/args-guard.d.ts +17 -0
  21. package/lib/executor/args-guard.js +23 -0
  22. package/lib/executor/failure-anchors.d.ts +52 -0
  23. package/lib/executor/failure-anchors.js +116 -0
  24. package/lib/executor/pr-review.d.ts +95 -0
  25. package/lib/executor/pr-review.js +175 -0
  26. package/lib/executor/run-log.d.ts +36 -0
  27. package/lib/executor/run-log.js +78 -0
  28. package/lib/executor.d.ts +156 -3
  29. package/lib/executor.js +594 -28
  30. package/lib/github-service.d.ts +1 -1
  31. package/lib/main.d.ts +92 -18
  32. package/lib/main.js +186 -95
  33. package/lib/renderers/common.d.ts +0 -3
  34. package/lib/renderers/common.js +8 -44
  35. package/lib/renderers/markdown.d.ts +1 -0
  36. package/lib/renderers/markdown.js +1 -0
  37. package/lib/renderers/pr.js +26 -1
  38. package/lib/renderers/task.d.ts +9 -0
  39. package/lib/renderers/task.js +120 -0
  40. package/lib/review-rules.d.ts +60 -0
  41. package/lib/review-rules.js +204 -0
  42. package/lib/shared/state-badges.d.ts +42 -0
  43. package/lib/shared/state-badges.js +102 -0
  44. package/lib/task-primitives.d.ts +109 -0
  45. package/lib/task-primitives.js +194 -0
  46. package/lib/tool-tiers.d.ts +57 -0
  47. package/lib/tool-tiers.js +168 -0
  48. package/lib/tools/agent-task.js +4 -4
  49. package/lib/tools/api.js +18 -6
  50. package/lib/tools/cache.js +42 -40
  51. package/lib/tools/codespace.js +4 -12
  52. package/lib/tools/discussion.js +4 -4
  53. package/lib/tools/gist.d.ts +1 -39
  54. package/lib/tools/gist.js +4 -14
  55. package/lib/tools/help.js +52 -13
  56. package/lib/tools/issue.js +16 -28
  57. package/lib/tools/label.js +4 -15
  58. package/lib/tools/pr.d.ts +47 -0
  59. package/lib/tools/pr.js +170 -33
  60. package/lib/tools/project.js +4 -13
  61. package/lib/tools/release.js +11 -22
  62. package/lib/tools/repo.js +4 -4
  63. package/lib/tools/ruleset.js +9 -6
  64. package/lib/tools/run.js +76 -24
  65. package/lib/tools/secret.js +11 -17
  66. package/lib/tools/skill.js +18 -22
  67. package/lib/tools/status.js +4 -4
  68. package/lib/tools/task.d.ts +219 -0
  69. package/lib/tools/task.js +457 -0
  70. package/lib/types.d.ts +183 -1
  71. package/lib/ui/help-modal.d.ts +1 -1
  72. package/lib/ui/help-modal.js +20 -10
  73. package/lib/ui/issue-modal.js +7 -3
  74. package/lib/ui/pr-modal.js +50 -11
  75. package/lib/ui/run-modal.d.ts +26 -0
  76. package/lib/ui/run-modal.js +486 -0
  77. package/lib/ui/styles.d.ts +1 -1
  78. package/lib/ui/styles.js +370 -44
  79. package/lib/ui/sync-settings-modal.js +1 -1
  80. package/lib/ui/workspace-panel.js +78 -23
  81. package/lib/version.d.ts +1 -1
  82. package/lib/version.js +1 -1
  83. package/package.json +9 -2
@@ -1,3 +1,4 @@
1
+ import { wrapWithGate } from '../destructive.js';
1
2
  import { renderCodespaceMarkdown } from '../renderers/markdown.js';
2
3
  import { checkScopeRequired } from './project.js';
3
4
  function normalizeOutput(data) {
@@ -12,7 +13,7 @@ function normalizeOutput(data) {
12
13
  const CODESPACE_LIST_FIELDS = 'name,displayName,repository,owner,state,gitStatus,lastUsedAt,createdAt,machineName';
13
14
  const CODESPACE_VIEW_FIELDS = 'name,displayName,repository,owner,state,gitStatus,lastUsedAt,createdAt,machineName,devcontainerPath,recentFolders,idleTimeoutMinutes';
14
15
  export function createCodespaceTool(executor) {
15
- return {
16
+ return wrapWithGate({
16
17
  name: 'github_codespace',
17
18
  description: '管理 GitHub Codespaces 云端远程开发环境:查阅实例列表与运行状态、检视实例配置详情、停止运行中的环境防计费、安全销毁实例。未授权时提供平台感知自愈命令。',
18
19
  parameters: {
@@ -53,8 +54,7 @@ export function createCodespaceTool(executor) {
53
54
  },
54
55
  },
55
56
  async execute(args, execContext) {
56
- const cwd = execContext?.cwd || process.cwd();
57
- const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
57
+ const { cwd, sessionId } = executor.resolveExecScope(execContext);
58
58
  const runOpts = { cwd, sessionId };
59
59
  switch (args.action) {
60
60
  case 'list': {
@@ -123,14 +123,6 @@ export function createCodespaceTool(executor) {
123
123
  throw new Error('delete 操作必须提供 codespace 名称');
124
124
  }
125
125
  const cleanName = args.codespace.trim();
126
- if (args.confirm !== true) {
127
- return {
128
- status: 'AWAITING_CONFIRMATION',
129
- warning: `⚠️ 销毁确认拦截:你即将永久删除云端开发环境「${cleanName}」。`,
130
- impact: '物理销毁 Codespace 将永久释放云端计算资源,环境中任何未提交并推送到远端 Git 的临时修改都将无法找回。',
131
- requiredAction: '如确认销毁该实例,请核对环境名称并在参数中附加 `confirm: true` 重新调用。',
132
- };
133
- }
134
126
  const deleteCmd = ['codespace', 'delete', '-c', cleanName, '-f'];
135
127
  if (args.org)
136
128
  deleteCmd.push('-o', String(args.org).trim());
@@ -150,5 +142,5 @@ export function createCodespaceTool(executor) {
150
142
  throw new Error(`未知的 Codespace 操作: ${args.action}`);
151
143
  }
152
144
  },
153
- };
145
+ });
154
146
  }
@@ -1,3 +1,4 @@
1
+ import { wrapWithGate } from '../destructive.js';
1
2
  import { renderDiscussionMarkdown } from '../renderers/markdown.js';
2
3
  function normalizeOutput(data) {
3
4
  if (data && typeof data === 'object' && !Array.isArray(data)) {
@@ -11,7 +12,7 @@ function normalizeOutput(data) {
11
12
  const DISCUSSION_LIST_FIELDS = 'number,title,body,category,author,closed,createdAt,updatedAt,url,labels,answered,stateReason';
12
13
  const DISCUSSION_VIEW_FIELDS = 'number,title,body,category,author,state,createdAt,updatedAt,url,labels,answered,comments';
13
14
  export function createDiscussionTool(executor) {
14
- return {
15
+ return wrapWithGate({
15
16
  name: 'github_discussion',
16
17
  description: '管理 GitHub Discussions 讨论区:浏览社区讨论、检视讨论详情与回复楼层、发起新讨论议题、编辑现有讨论以及追加评论回复。基于 gh CLI preview 特性。',
17
18
  parameters: {
@@ -74,8 +75,7 @@ export function createDiscussionTool(executor) {
74
75
  },
75
76
  },
76
77
  async execute(args, execContext) {
77
- const cwd = execContext?.cwd || process.cwd();
78
- const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
78
+ const { cwd, sessionId } = executor.resolveExecScope(execContext);
79
79
  const repoArgs = args.repo ? ['-R', args.repo] : [];
80
80
  const runOpts = { cwd, sessionId };
81
81
  switch (args.action) {
@@ -191,5 +191,5 @@ export function createDiscussionTool(executor) {
191
191
  throw new Error(`未知的 Discussion 操作: ${args.action}`);
192
192
  }
193
193
  },
194
- };
194
+ });
195
195
  }
@@ -71,14 +71,9 @@ export declare function createGistTool(executor: GhExecutor): {
71
71
  public?: undefined;
72
72
  description?: undefined;
73
73
  success?: undefined;
74
+ gist_id?: undefined;
74
75
  message?: undefined;
75
76
  detail?: undefined;
76
- status?: undefined;
77
- action?: undefined;
78
- gist_id?: undefined;
79
- warning?: undefined;
80
- impact?: undefined;
81
- requiredAction?: undefined;
82
77
  } | {
83
78
  items?: undefined;
84
79
  count?: undefined;
@@ -92,11 +87,6 @@ export declare function createGistTool(executor: GhExecutor): {
92
87
  success?: undefined;
93
88
  message?: undefined;
94
89
  detail?: undefined;
95
- status?: undefined;
96
- action?: undefined;
97
- warning?: undefined;
98
- impact?: undefined;
99
- requiredAction?: undefined;
100
90
  } | {
101
91
  items?: undefined;
102
92
  count?: undefined;
@@ -110,11 +100,6 @@ export declare function createGistTool(executor: GhExecutor): {
110
100
  description: any;
111
101
  message: string;
112
102
  detail?: undefined;
113
- status?: undefined;
114
- action?: undefined;
115
- warning?: undefined;
116
- impact?: undefined;
117
- requiredAction?: undefined;
118
103
  } | {
119
104
  items?: undefined;
120
105
  count?: undefined;
@@ -128,28 +113,5 @@ export declare function createGistTool(executor: GhExecutor): {
128
113
  gist_id: string;
129
114
  message: string;
130
115
  detail: string;
131
- status?: undefined;
132
- action?: undefined;
133
- warning?: undefined;
134
- impact?: undefined;
135
- requiredAction?: undefined;
136
- } | {
137
- items?: undefined;
138
- count?: undefined;
139
- rawText?: undefined;
140
- filename?: undefined;
141
- content?: undefined;
142
- url?: undefined;
143
- public?: undefined;
144
- description?: undefined;
145
- success?: undefined;
146
- message?: undefined;
147
- detail?: undefined;
148
- status: string;
149
- action: string;
150
- gist_id: string;
151
- warning: string;
152
- impact: string;
153
- requiredAction: string;
154
116
  }>;
155
117
  };
package/lib/tools/gist.js CHANGED
@@ -1,6 +1,7 @@
1
+ import { wrapWithGate } from '../destructive.js';
1
2
  import { renderGistMarkdown, parseGistListOutput } from '../renderers/markdown.js';
2
3
  export function createGistTool(executor) {
3
- return {
4
+ return wrapWithGate({
4
5
  name: 'github_gist',
5
6
  description: '管理 GitHub Gist 代码片段与诊断留痕:查看 Gist 列表、检视代码正文 (view)、通过文本内容或本地文件创建新 Gist (create,默认安全 secret 模式)、编辑以及安全删除。删除操作不可逆,需 confirm: true。',
6
7
  parameters: {
@@ -58,8 +59,7 @@ export function createGistTool(executor) {
58
59
  },
59
60
  },
60
61
  async execute(args, execContext) {
61
- const cwd = execContext?.cwd || process.cwd();
62
- const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
62
+ const { cwd, sessionId } = executor.resolveExecScope(execContext);
63
63
  const runOpts = { cwd, sessionId, rawText: true };
64
64
  switch (args.action) {
65
65
  case 'list': {
@@ -161,16 +161,6 @@ export function createGistTool(executor) {
161
161
  if (!args.gist_id) {
162
162
  throw new Error('删除 Gist 时 `gist_id` 参数为必填项。');
163
163
  }
164
- if (args.confirm !== true) {
165
- return {
166
- status: 'AWAITING_CONFIRMATION',
167
- action: 'delete',
168
- gist_id: String(args.gist_id),
169
- warning: `⚠️ 高危破坏性操作:即将永久删除 Gist 代码片段 "${args.gist_id}"`,
170
- impact: `删除 Gist 为物理删除,无法通过 GitHub API 恢复,所有引用该 Gist URL 的外部链接将失效并返回 404。`,
171
- requiredAction: `如确认彻底删除,请在参数中附加 \`confirm: true\` 重新调用。示例:\`github_gist(action: 'delete', gist_id: '${args.gist_id}', confirm: true)\``,
172
- };
173
- }
174
164
  const delCmd = ['gist', 'delete', String(args.gist_id), '--yes'];
175
165
  const res = await executor.run(delCmd, runOpts);
176
166
  if (!res.ok)
@@ -186,5 +176,5 @@ export function createGistTool(executor) {
186
176
  throw new Error(`不支持的 gist 操作: "${args.action}"。可选值: list, view, create, edit, delete。`);
187
177
  }
188
178
  },
189
- };
179
+ });
190
180
  }
package/lib/tools/help.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { wrapWithGate } from '../destructive.js';
1
2
  const KNOWLEDGE_BASE = {
2
3
  overview: `### 🧭 DSH GitHub Flow 工具箱全景导航
3
4
 
@@ -15,19 +16,20 @@ const KNOWLEDGE_BASE = {
15
16
  | \`github_skill\` | 跨 Agent 开源 Skills 生态发现与维护 | 检索/预览/安装 GitHub 上的 Agent Skills(gh preview 特性);\`publish\` 规范自检与发布、\`update\` 检查并同步更新已安装技能 |
16
17
  | \`github_label\` | 标签体系管理与跨仓库克隆 | 查看标签列表、创建/编辑标签、从源仓库一键 clone 标签体系、删除标签(需 confirm) |
17
18
  | \`github_ruleset\` | 分支保护规则与策略诊断 | 检查分支适用的合并策略规则 (check)、查看规则集列表与详情,权威回答“PR 为何无法合并” |
18
- | \`github_cache\` | Actions 构建缓存盘点与安全清理 | 查看缓存列表与体积占用、按 ID/Key 删除指定缓存、全量清空缓存(需 confirm) |
19
+ | \`github_cache\` | Actions 构建缓存盘点与安全清理 | 查看缓存列表与体积占用、按 ID/Key 删除指定缓存、删除缓存(三态均需 confirm) |
19
20
  | \`github_gist\` | 代码片段与调试日志分享治理 | 查看 Gist 列表与代码正文、创建 Secret/Public 代码片段、编辑与物理删除(需 confirm) |
20
21
  | \`github_status\` | 跨仓库待办与动态聚合看板 | 账号维度汇总指派给我的 Issue/PR、等待评审、被 @提及 以及订阅仓库近期动态 |
21
22
  | \`github_agent_task\` | 云端智能体编码任务会话治理 | 查阅已分派任务列表、检视任务状态与会话日志、向云端 Agent 发起新编码重构任务 |
22
23
  | \`github_discussion\` | 团队技术讨论与开源社区 RFC 闭环 | 浏览讨论列表、检视正文与评论楼层、发起新讨论、编辑正文与追加回复 |
23
24
  | \`github_project\` | Projects v2 项目看板与任务规划 | 查阅项目列表/详情、浏览看板条目 (Items)、添加 Issue/PR 到看板、创建草稿任务与删除条目 |
24
25
  | \`github_codespace\` | 云端远程开发环境管控 (MVP) | 查阅运行中实例、检视机器规格配置、停机防计费 (stop)、安全销毁环境 (delete,需 confirm) |
26
+ | \`github_task\` | **任务级原语**(一次调用完成一件完整的事) | 审一个 PR(元数据 + diff + CI + 仓库规则,可选写回行级评注)、诊断一次 CI 失败(定位 job/step + 日志锚点)、准备一次发版(Conventional Commits 归类 + SemVer + notes 草稿);默认只读 |
25
27
  | \`github_help\` | 路由导航、官方语法速查与故障自愈 | 遇到不确定的操作、搜索语法或接口报错时查询用法与诊断配方 |
26
28
 
27
29
  💡 **使用准则**:优先调用高层语义工具(pr / issue / run / repo / release / secret);当高层工具无法满足特殊接口需求时,再降级使用 \`github_api\`。
28
- 📊 **规模口径**:18 个工具 / 103 个业务 action(不含 \`github_help\` 的 21 个查询主题与 \`github_api\` 的 4 个 HTTP method——它们不是业务动作)。
30
+ 📊 **规模口径**:19 个工具 / 106 个业务 action(不含 \`github_help\` 的 22 个查询主题与 \`github_api\` 的 4 个 HTTP method——它们不是业务动作)。
29
31
  💬 **人类指令**:聊天框支持 \`/gh status\`(大盘看板)、\`/gh upgrade\`(一键升级插件)、\`/gh help\`(快捷帮助)。
30
- 🖥️ **可视化工作台**:最左侧 Octocat 图标打开全景驾驶舱(4 标签:本地工作区 / 我的仓库 / Pull Requests / 待办 Issues);每个会话右侧栏有 5 个 Tab(Git / 未提交 / PR / Issues / Actions)。点击任一 PR 或 Issue 卡片可在线查看详情与评论,无需跳浏览器。
32
+ 🖥️ **可视化工作台**:最左侧 Octocat 图标打开全景驾驶舱(4 标签:本地工作区 / 我的仓库 / Pull Requests / 待办 Issues);每个会话右侧栏有 5 个 Tab(Git / 未提交 / PR / Issues / Actions)。PR、Issue、Actions 三类卡片点击均可在线查看详情(Actions 为 job / step 树 + 按需失败日志),无需跳浏览器。
31
33
  🗺️ **命令对照**:不确定某 gh 命令对应哪个工具?\`github_help(topic: 'mapping')\` 查询完整映射表(含未覆盖域的 API 引导)。`,
32
34
  pr: `### 🔀 github_pr 详细操作指南
33
35
 
@@ -92,7 +94,11 @@ const KNOWLEDGE_BASE = {
92
94
  - **启停工作流**: \`github_run(action: 'workflow_enable'/'workflow_disable', workflow: '…')\`
93
95
  - disable 后 trigger 将静默失败,直至重新 enable——输出会显式警告这一点。
94
96
  - **下载构建产物**: \`github_run(action: 'download', run_id: '12345678', name: '构建产物名'?)\`
95
- - 落盘目录由插件固定管理(\`~/.dsh/data/dsh-github-flow/artifacts/\` 下按仓库与 run 隔离),返回文件清单。`,
97
+ - 落盘目录由插件固定管理(\`~/.dsh/data/dsh-github-flow/artifacts/\` 下按仓库与 run 隔离),返回文件清单。
98
+ - **看单次运行的 job / step 结构**: \`github_run(action: 'view', run_id: '12345678')\`
99
+ - 返回 run 元数据 + jobs 树(每个 job 的 conclusion、起止时间、url,以及 steps 的 number/name/conclusion);这是「挂在第几步」最快的入口。
100
+ - **人类侧在线查看(不是工具调用)**: 右侧栏 Actions Tab 点击任一卡片 → job / step 树 + 失败步骤高亮 + 按需日志锚点(一次最多 3 个失败 job、服务端批次预算 90 秒)。
101
+ - Agent 侧的等价快捷方式是 \`github_task(action: 'diagnose_ci', run_id: '12345678')\`:定位失败 job/step → 萃取日志锚点 → 汇总疑似相关文件,一次返回结论包。`,
96
102
  repo: `### 📦 github_repo 详细操作指南
97
103
 
98
104
  - **查看元数据与标签**: \`github_repo(action: 'view')\`
@@ -203,8 +209,8 @@ const KNOWLEDGE_BASE = {
203
209
  - 返回仓库 Actions 构建缓存清单、占用体积(自动换算为 KB/MB/GB)、关联 Git Ref 以及最后访问时间。
204
210
  - 可附加 \`key: 'npm-cache'\` 过滤特定前缀,或通过 \`ref: 'refs/heads/main'\` 限定分支。
205
211
  - **精准删除单条缓存**:
206
- - 按数字 ID 删除: \`github_cache(action: 'delete', cache_id: '12345678')\`
207
- - 按 Key 键名删除: \`github_cache(action: 'delete', key: 'Linux-npm-18-abcd1234')\`
212
+ - 按数字 ID 删除: \`github_cache(action: 'delete', cache_id: '12345678', confirm: true)\`
213
+ - 按 Key 键名删除: \`github_cache(action: 'delete', key: 'Linux-npm-18-abcd1234', confirm: true)\`
208
214
  - **全量清空仓库缓存**:
209
215
  \`github_cache(action: 'delete', all: true, confirm: true)\`
210
216
  - 批量破坏性操作:会导致后续流水线构建重新拉取依赖,必须显式附加 \`confirm: true\`;底层自动注入 \`--succeed-on-no-caches\` 保证幂等执行。`,
@@ -231,7 +237,7 @@ const KNOWLEDGE_BASE = {
231
237
  - **排除噪音仓库**: \`github_status(exclude: ['owner/noisy-repo'])\`
232
238
  - 过滤高频刷屏但无需关注的仓库活动。
233
239
  - **查看工具链运行时健康度**: \`github_status(action: 'health')\`
234
- - 审计当前会话 18 大工具矩阵的综合健康分、API 降级率、安全防线拦截频次与针对性自愈建议。`,
240
+ - 审计当前会话 19 大工具矩阵的综合健康分、API 降级率、安全防线拦截频次与针对性自愈建议。`,
235
241
  agent_task: `### 🤖 github_agent_task 智能体任务指南
236
242
 
237
243
  管理与调度 GitHub Copilot Coding Agent 云端智能体会话:
@@ -275,6 +281,24 @@ const KNOWLEDGE_BASE = {
275
281
  - **安全销毁实例**: \`github_codespace(action: 'delete', codespace: 'fuzzy-spoon-69vj', confirm: true)\`
276
282
  - 物理销毁不可逆,必须附加 \`confirm: true\`。
277
283
  - 🔑 若报缺少权限,在终端执行单行自愈授权命令:\`gh auth refresh -h github.com -s codespace\``,
284
+ task: `### 🎯 github_task 任务级原语指南
285
+
286
+ \`github_task\` 把「一件完整的事」封装成一次调用,返回**已裁剪、可直接执行**的结论包:
287
+ \`summary\`(结论)+ \`facts\`(结构化事实)+ \`nextActions\`(下一步)+ \`cost\`(字符数 / gh 调用数 / 耗时)。
288
+
289
+ - **审一个 PR**: \`github_task(action: 'review_pr', pr_number: 123)\`
290
+ - 一次拿齐:PR 元数据 + head SHA + 变动文件 diff(按 \`budget_chars\` 裁剪)+ CI 结论 + 仓库 \`AGENTS.md\` 审查规则 + 既有行级评论数。
291
+ - \`focus: 'security' | 'correctness' | 'perf' | 'style'\` 只影响 summary 的提示语,不改变取数。
292
+ - **写回**(默认关闭): \`github_task(action: 'review_pr', pr_number: 123, write: true, confirm: true, body: '…', comments: [{path, line, body}])\`,字段语义与 \`github_pr(action: 'review')\` 完全一致。
293
+ - **诊断一次 CI 失败**: \`github_task(action: 'diagnose_ci', run_id: 456)\` 或 \`github_task(action: 'diagnose_ci', pr_number: 123)\`
294
+ - 定位失败 job/step → 萃取失败日志锚点(pytest / node / Go / tsc / npm)→ 汇总疑似相关文件。
295
+ - 日志被截断时 \`truncated.totalUnknown = true\`:**不要据此判断「就这些失败」**。
296
+ - **准备一次发版**: \`github_task(action: 'prepare_release', bump: 'auto')\`
297
+ - 读最近 tag 以来的提交 → 按 Conventional Commits 归类 → 推导 SemVer(breaking→major / feat→minor / 其余→patch)→ 生成 notes 草稿 + 发版体检(工作区干净 / 同步远端 / CI 绿)。
298
+ - **本原语不会打 tag、不会 push、不会创建 Release**:发版是人的决定,它只把「该发什么版本、notes 长什么样」准备好,并在 \`nextActions\` 里给出可直接执行的命令。
299
+
300
+ > 需要细粒度控制时,用对应的原子工具:\`github_pr\` / \`github_run\` / \`github_release\`。原语是快捷方式,不是唯一入口。
301
+ `,
278
302
  mapping: `### 🗺️ gh 命令 ↔ 插件工具映射总表
279
303
 
280
304
  #### 一、已覆盖域映射(语义化 action 精确对照)
@@ -363,7 +387,7 @@ const KNOWLEDGE_BASE = {
363
387
  | gh 命令 | 插件调用 | 备注 |
364
388
  | :--- | :--- | :--- |
365
389
  | \`gh cache list\` | \`github_cache(action:'list')\` | 缓存盘点与过滤 |
366
- | \`gh cache delete\` | \`github_cache(action:'delete', cache_id:…)\` | all:true 需 confirm |
390
+ | \`gh cache delete\` | \`github_cache(action:'delete', cache_id:…, confirm:true)\` | 三态均需 confirm |
367
391
 
368
392
  **Gist(github_gist,5 action)**
369
393
  | gh 命令 | 插件调用 | 备注 |
@@ -373,6 +397,14 @@ const KNOWLEDGE_BASE = {
373
397
  | \`gh gist edit\` | \`github_gist(action:'edit', gist_id:…)\` | 描述与文件修改 |
374
398
  | \`gh gist delete\` | \`github_gist(action:'delete', gist_id:…, confirm:true)\` | 物理删除,需 confirm |
375
399
 
400
+ **Task(github_task,3 action)**
401
+
402
+ | gh 命令 | 插件调用 | 说明 |
403
+ | :--- | :--- | :--- |
404
+ | \`gh pr view\` + \`gh pr diff\` + \`gh pr checks\` | \`github_task(action:'review_pr')\` | 一次拿齐审阅上下文(可选写回行级评注) |
405
+ | \`gh run view --json jobs\` + \`--log-failed\` | \`github_task(action:'diagnose_ci')\` | 定位失败 job/step 并萃取日志锚点 |
406
+ | \`git describe\` + \`git log\` | \`github_task(action:'prepare_release')\` | 推导版本 + 生成 notes 草稿(不发版) |
407
+
376
408
  **Status(github_status,2 action)**
377
409
  | gh 命令 | 插件调用 | 备注 |
378
410
  | :--- | :--- | :--- |
@@ -417,6 +449,7 @@ const KNOWLEDGE_BASE = {
417
449
 
418
450
  - \`gh workflow …\` / \`gh search …\` / \`gh variable …\` 分别归 **github_run / github_repo / github_secret**(不是独立工具)
419
451
  - \`gh api\` 的等价物是 \`github_api\`(全端点逃生门,支持 raw/paginate/jq)
452
+ - 人类侧在线查看(PR / Issue / Actions Run 详情弹窗)由插件 HTTP 路由提供(\`/api/github/pr-detail\`、\`/api/github/issue-detail\`、\`/api/github/run-detail\`、\`/api/github/run-logs\`),**不是工具 action**;Agent 侧对应能力分别是 \`github_pr(action:'view')\`、\`github_issue(action:'view')\`、\`github_run(action:'view')\`
420
453
  - 图片查看:\`github_issue\`/\`github_pr\` 的 \`view_images\`、\`github_repo\` 的 \`readme\` + image_urls(插件独有能力,gh 无对应命令)
421
454
  - 附件上传:\`github_issue\`/\`github_pr\` 的 \`attach_images\` 映射 gh ≥2.99 原生可重复 \`--attach\` flag(支持 create/edit/comment)
422
455
 
@@ -472,10 +505,16 @@ const KNOWLEDGE_BASE = {
472
505
  - *解法*:需管理员前往 GitHub 网页端 \`Settings -> General -> Features\` 勾选开启 \`Discussions\`。
473
506
  8. **操作被拦截提示 \`Awaiting confirmation\`**:
474
507
  - *原因*:对于高危破坏性操作(如合并 PR、删除分支、物理删除 Issue、清空构建缓存、删除密钥、销毁 Codespace),插件设置了强制安全确认防线,未确认时绝对不触发底层 CLI。
475
- - *解法*:审阅影响评估报告后,在参数中显式传入 \`confirm: true\` 即可放行。`
508
+ - *解法*:审阅影响评估报告后,在参数中显式传入 \`confirm: true\` 即可放行。
509
+ 9. **\`log_failed\` 拉取很慢 / 报 \`unexpected EOF\` / 连接被重置**:
510
+ - *原因*:GitHub 日志接口是「先打包再下载」,实测单次 4–40 秒,走代理时可能被中途掐断;矩阵构建下日志体量随 job 数线性增长。
511
+ - *解法*:① 先 \`github_run(action:'view')\` 确认到底哪个 job 挂了,只对失败 job 重试;② 换 \`github_task(action:'diagnose_ci')\` 走一次性的结论包(内部同样是锚点萃取,但会顺带汇总相关文件);③ 仍失败时改用 \`github_api\` 直接拉 job 日志端点,或让用户到 Actions 页面查看完整日志。
512
+ 10. **人类侧「提取失败日志」为何只给一小段 / 为什么最多 3 个 job**:
513
+ - *原因*:弹窗内展示的是**锚点萃取后的片段**(失败块 + 上下文,无锚点时回退尾部 200 行),不是完整日志;一次点击最多串行拉 3 个失败 job,服务端总预算 90 秒——这是为了不让一次点击把面板卡住几分钟。
514
+ - *解法*:需要完整日志或更多 job 时,让用户点弹窗右上角「GitHub ↗」,或由 Agent 用 \`github_run(action:'log_failed')\` / \`github_task(action:'diagnose_ci')\` 分批处理。`
476
515
  };
477
516
  export function createHelpTool(executor) {
478
- return {
517
+ return wrapWithGate({
479
518
  name: 'github_help',
480
519
  description: 'GitHub 工具箱使用指南与官方命令手册。当你不确定该用哪个工具、需要特定操作的高级语法与示例、或排查 GitHub 报错时调用。',
481
520
  parameters: {
@@ -502,6 +541,7 @@ export function createHelpTool(executor) {
502
541
  'discussion',
503
542
  'project',
504
543
  'codespace',
544
+ 'task',
505
545
  'mapping',
506
546
  'search_syntax',
507
547
  'troubleshooting',
@@ -526,8 +566,7 @@ export function createHelpTool(executor) {
526
566
  },
527
567
  },
528
568
  async execute(args, execContext) {
529
- const cwd = execContext?.cwd || process.cwd();
530
- const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
569
+ const { cwd, sessionId } = executor.resolveExecScope(execContext);
531
570
  // 1. 若显式指定了 gh_command,直接透传查询官方离线手册
532
571
  if (args.gh_command && typeof args.gh_command === 'string') {
533
572
  let parts = args.gh_command.trim().split(/\s+/);
@@ -557,5 +596,5 @@ export function createHelpTool(executor) {
557
596
  result: `未找到主题「${rawTopic}」的指南。可用主题:${Object.keys(KNOWLEDGE_BASE).join(', ')}。`,
558
597
  };
559
598
  },
560
- };
599
+ });
561
600
  }
@@ -1,6 +1,8 @@
1
+ import { wrapWithGate } from '../destructive.js';
1
2
  import { compareGhVersions } from '../executor.js';
2
3
  import { renderIssueMarkdown } from '../renderers/markdown.js';
3
4
  import { extractImageUrls, downloadImages, buildImageBlocks, filterRequestedUrls, validateAttachPaths } from './image-utils.js';
5
+ import { assertPositionArg } from '../executor/args-guard.js';
4
6
  function normalizeOutput(data) {
5
7
  if (data && typeof data === 'object' && !Array.isArray(data)) {
6
8
  return data;
@@ -13,7 +15,7 @@ function normalizeOutput(data) {
13
15
  // 共享图片工具链的同名再导出:保持路径与单测稳定
14
16
  export { extractImageUrls as extractIssueImageUrls, validateAttachPaths } from './image-utils.js';
15
17
  export function createIssueTool(executor) {
16
- return {
18
+ return wrapWithGate({
17
19
  name: 'github_issue',
18
20
  description: '管理 GitHub Issues。支持查看 Issue 列表、详情、创建新 Issue、追加评论、关闭 Issue、编辑 Issue 以及关联创建本地开发分支 (develop)。',
19
21
  parameters: {
@@ -108,8 +110,9 @@ export function createIssueTool(executor) {
108
110
  },
109
111
  },
110
112
  async execute(args, execContext) {
111
- const cwd = execContext?.cwd || process.cwd();
112
- const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
113
+ // 位置参数守卫:issue_number 是 `gh issue <cmd> <number>` 的位置参数
114
+ assertPositionArg(args.issue_number, 'Issue 编号');
115
+ const { cwd, sessionId } = executor.resolveExecScope(execContext);
113
116
  let repoTarget = args.repo;
114
117
  if (!repoTarget) {
115
118
  const meta = await executor.getRepoMetadata(cwd, sessionId);
@@ -265,6 +268,15 @@ export function createIssueTool(executor) {
265
268
  case 'develop': {
266
269
  if (!args.issue_number)
267
270
  throw new Error('develop 操作必须提供 issue_number');
271
+ // `--checkout` 会把工作区切到新分支,脏改动会被一起带走——与界面切分支同源风险,
272
+ // 必须过同一道闸门(executor.ensureCleanWorkspace,唯一实现)。
273
+ const guard = await executor.ensureCleanWorkspace(cwd);
274
+ if (!guard.ok) {
275
+ const sample = guard.samples.length
276
+ ? `\n涉及文件(前 ${guard.samples.length} 条):\n${guard.samples.map((s) => ' ' + s).join('\n')}`
277
+ : '';
278
+ throw new Error(`${guard.error}${sample}\n请先提交或 stash 后再创建并检出开发分支。`);
279
+ }
268
280
  const devCmd = ['issue', 'develop', String(args.issue_number), ...repoArgs, '--checkout'];
269
281
  if (args.branch_name) {
270
282
  devCmd.push('-n', args.branch_name);
@@ -287,14 +299,6 @@ export function createIssueTool(executor) {
287
299
  case 'close': {
288
300
  if (!args.issue_number)
289
301
  throw new Error('close 操作必须提供 issue_number');
290
- if (args.confirm !== true) {
291
- return {
292
- status: 'AWAITING_CONFIRMATION',
293
- warning: `⚠️ 高危操作拦截:你即将关闭 Issue #${args.issue_number}。`,
294
- impact: '关闭后该议题状态将变更为 CLOSED,请与用户确认是否确实已解决或需要关闭。',
295
- requiredAction: '如确认关闭,请在参数中附加 `confirm: true` 重新调用。',
296
- };
297
- }
298
302
  const res = await executor.run(['issue', 'close', String(args.issue_number), ...repoArgs], runOpts);
299
303
  if (!res.ok)
300
304
  throw new Error(res.error);
@@ -340,14 +344,6 @@ export function createIssueTool(executor) {
340
344
  if (!args.issue_number)
341
345
  throw new Error('delete 操作必须提供 issue_number');
342
346
  // 物理删除 > close 的拦截级别:issue 编号永久消失、评论湮灭、GraphQL 无法恢复
343
- if (args.confirm !== true) {
344
- return {
345
- status: 'AWAITING_CONFIRMATION',
346
- warning: `⚠️ 高危操作拦截:你即将**物理删除** Issue #${args.issue_number}(非 close,不可恢复)。`,
347
- impact: 'Issue 编号将永久消失,全部评论与附件随之湮灭,GraphQL 层无法恢复——这与 close(可 reopen 逆转)有本质区别。',
348
- requiredAction: '如确认删除,请与用户核实后附加 `confirm: true` 重新调用。',
349
- };
350
- }
351
347
  // 非 TTY 下 gh issue delete 会挂起等待 prompt,--yes 是非交互执行的唯一途径
352
348
  const res = await executor.run(['issue', 'delete', String(args.issue_number), ...repoArgs, '--yes'], runOpts);
353
349
  if (!res.ok)
@@ -363,14 +359,6 @@ export function createIssueTool(executor) {
363
359
  throw new Error(`transfer 目标仓库 (${args.target_repo}) 与当前仓库相同,迁移无意义。`);
364
360
  }
365
361
  // 跨仓库迁移:原 URL 301 但归属改变,不可静默撤回——与 delete 同级 confirm
366
- if (args.confirm !== true) {
367
- return {
368
- status: 'AWAITING_CONFIRMATION',
369
- warning: `⚠️ 高危操作拦截:你即将把 Issue #${args.issue_number} 从 ${repoTarget} 迁移到 ${args.target_repo}。`,
370
- impact: `迁移后 Issue 归属 ${args.target_repo}(原 URL 301 重定向),标签/里程碑不随迁,操作不可静默撤回。需要目标仓库的写权限。`,
371
- requiredAction: '如确认迁移,请与用户核实后附加 `confirm: true` 重新调用。',
372
- };
373
- }
374
362
  const res = await executor.run(['issue', 'transfer', String(args.issue_number), args.target_repo, ...repoArgs], runOpts);
375
363
  if (!res.ok)
376
364
  throw new Error(res.error);
@@ -387,5 +375,5 @@ export function createIssueTool(executor) {
387
375
  throw new Error(`未知的 Issue 操作: ${args.action}`);
388
376
  }
389
377
  },
390
- };
378
+ });
391
379
  }
@@ -1,3 +1,4 @@
1
+ import { wrapWithGate } from '../destructive.js';
1
2
  import { renderLabelMarkdown } from '../renderers/markdown.js';
2
3
  /**
3
4
  * 校验并归一化 6 位十六进制颜色(去除前导 #)
@@ -21,7 +22,7 @@ function normalizeOutput(data) {
21
22
  return { result: data !== undefined && data !== null ? data : '' };
22
23
  }
23
24
  export function createLabelTool(executor) {
24
- return {
25
+ return wrapWithGate({
25
26
  name: 'github_label',
26
27
  description: '管理 GitHub 仓库标签体系 (Labels):查看标签列表、创建新标签、编辑修改现有标签、克隆跨仓库标签体系 (clone) 以及安全删除标签。删除操作不可逆且会解绑议题,需 confirm: true。',
27
28
  parameters: {
@@ -92,8 +93,7 @@ export function createLabelTool(executor) {
92
93
  },
93
94
  },
94
95
  async execute(args, execContext) {
95
- const cwd = execContext?.cwd || process.cwd();
96
- const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
96
+ const { cwd, sessionId } = executor.resolveExecScope(execContext);
97
97
  let repoTarget = args.repo;
98
98
  if (!repoTarget) {
99
99
  const meta = await executor.getRepoMetadata(cwd, sessionId);
@@ -188,17 +188,6 @@ export function createLabelTool(executor) {
188
188
  if (!args.name) {
189
189
  throw new Error('删除标签时 `name` 参数为必填项。');
190
190
  }
191
- if (args.confirm !== true) {
192
- return {
193
- status: 'AWAITING_CONFIRMATION',
194
- action: 'delete',
195
- name: args.name,
196
- repo: repoTarget,
197
- warning: `⚠️ 高危破坏性操作:即将删除仓库 ${repoTarget} 的标签 "${args.name}"`,
198
- impact: `删除标签不可逆,且会导致目标仓库内所有已打上该标签的 Issue 与 PR 自动解绑该标记。`,
199
- requiredAction: `如确认彻底删除,请在参数中附加 \`confirm: true\` 重新调用。示例:\`github_label(action: 'delete', name: '${args.name}', confirm: true)\``,
200
- };
201
- }
202
191
  const deleteCmd = ['label', 'delete', args.name, '--yes', ...repoArgs];
203
192
  const res = await executor.run(deleteCmd, runOpts);
204
193
  if (!res.ok)
@@ -233,5 +222,5 @@ export function createLabelTool(executor) {
233
222
  throw new Error(`不支持的 label 操作: "${args.action}"。可选值: list, create, edit, delete, clone。`);
234
223
  }
235
224
  },
236
- };
225
+ });
237
226
  }
package/lib/tools/pr.d.ts CHANGED
@@ -44,6 +44,49 @@ export declare function createPrTool(executor: GhExecutor): {
44
44
  enum: string[];
45
45
  description: string;
46
46
  };
47
+ /**
48
+ * 行级评注(review 动作使用)。
49
+ *
50
+ * 为什么必须单独一个参数而不是塞进 body:`gh pr review` 没有行级参数
51
+ * (`gh pr review --help` 里 line/side/path 零命中),只能走
52
+ * `POST /pulls/{n}/reviews` 的 `comments[]`。一次 review 一个请求 =
53
+ * 通知只发一轮,也避开 secondary rate limit 的「创建内容过快」。
54
+ */
55
+ comments: {
56
+ type: string;
57
+ items: {
58
+ type: string;
59
+ properties: {
60
+ path: {
61
+ type: string;
62
+ description: string;
63
+ };
64
+ line: {
65
+ type: string;
66
+ description: string;
67
+ };
68
+ side: {
69
+ type: string;
70
+ enum: string[];
71
+ description: string;
72
+ };
73
+ start_line: {
74
+ type: string;
75
+ description: string;
76
+ };
77
+ body: {
78
+ type: string;
79
+ description: string;
80
+ };
81
+ };
82
+ required: string[];
83
+ };
84
+ description: string;
85
+ };
86
+ commit_id: {
87
+ type: string;
88
+ description: string;
89
+ };
47
90
  merge_method: {
48
91
  type: string;
49
92
  enum: string[];
@@ -53,6 +96,10 @@ export declare function createPrTool(executor: GhExecutor): {
53
96
  type: string;
54
97
  description: string;
55
98
  };
99
+ force: {
100
+ type: string;
101
+ description: string;
102
+ };
56
103
  undo: {
57
104
  type: string;
58
105
  description: string;