dsh-github-flow 0.3.0 → 0.5.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 (121) hide show
  1. package/README.en.md +287 -143
  2. package/README.md +270 -125
  3. package/docs/UPGRADE-NOTES.md +12 -9
  4. package/docs/architecture.md +126 -126
  5. package/lib/api/routes-actions.js +249 -0
  6. package/lib/api/routes-overview.js +94 -2
  7. package/lib/cache.d.ts +4 -0
  8. package/lib/cache.js +14 -0
  9. package/lib/client.d.ts +14 -1
  10. package/lib/client.js +4287 -2500
  11. package/lib/commands/gh.d.ts +6 -1
  12. package/lib/commands/gh.js +144 -71
  13. package/lib/executor/git-parser.d.ts +57 -0
  14. package/lib/executor/git-parser.js +255 -0
  15. package/lib/executor/platform-guide.d.ts +14 -0
  16. package/lib/executor/platform-guide.js +40 -0
  17. package/lib/executor/process-utils.d.ts +51 -0
  18. package/lib/executor/process-utils.js +162 -0
  19. package/lib/executor/version-utils.d.ts +12 -0
  20. package/lib/executor/version-utils.js +27 -0
  21. package/lib/executor.d.ts +70 -48
  22. package/lib/executor.js +371 -356
  23. package/lib/git-watcher.d.ts +36 -0
  24. package/lib/git-watcher.js +91 -0
  25. package/lib/main.js +37 -3
  26. package/lib/renderers/agent-task.d.ts +16 -0
  27. package/lib/renderers/agent-task.js +107 -0
  28. package/lib/renderers/cache.d.ts +8 -0
  29. package/lib/renderers/cache.js +66 -0
  30. package/lib/renderers/codespace.d.ts +16 -0
  31. package/lib/renderers/codespace.js +103 -0
  32. package/lib/renderers/common.d.ts +24 -0
  33. package/lib/renderers/common.js +88 -0
  34. package/lib/renderers/discussion.d.ts +16 -0
  35. package/lib/renderers/discussion.js +101 -0
  36. package/lib/renderers/gist.d.ts +15 -0
  37. package/lib/renderers/gist.js +99 -0
  38. package/lib/renderers/issue.d.ts +4 -0
  39. package/lib/renderers/issue.js +70 -0
  40. package/lib/renderers/label.d.ts +4 -0
  41. package/lib/renderers/label.js +48 -0
  42. package/lib/renderers/markdown.d.ts +19 -32
  43. package/lib/renderers/markdown.js +19 -624
  44. package/lib/renderers/pr.d.ts +4 -0
  45. package/lib/renderers/pr.js +107 -0
  46. package/lib/renderers/project.d.ts +20 -0
  47. package/lib/renderers/project.js +128 -0
  48. package/lib/renderers/release.d.ts +4 -0
  49. package/lib/renderers/release.js +74 -0
  50. package/lib/renderers/repo.d.ts +4 -0
  51. package/lib/renderers/repo.js +149 -0
  52. package/lib/renderers/ruleset.d.ts +4 -0
  53. package/lib/renderers/ruleset.js +74 -0
  54. package/lib/renderers/run.d.ts +4 -0
  55. package/lib/renderers/run.js +79 -0
  56. package/lib/renderers/skill.d.ts +34 -0
  57. package/lib/renderers/skill.js +241 -0
  58. package/lib/renderers/status.d.ts +28 -0
  59. package/lib/renderers/status.js +176 -0
  60. package/lib/telemetry.d.ts +70 -0
  61. package/lib/telemetry.js +161 -0
  62. package/lib/tools/agent-task.d.ts +54 -0
  63. package/lib/tools/agent-task.js +135 -0
  64. package/lib/tools/cache.d.ts +64 -0
  65. package/lib/tools/cache.js +186 -0
  66. package/lib/tools/codespace.d.ts +51 -0
  67. package/lib/tools/codespace.js +154 -0
  68. package/lib/tools/discussion.d.ts +70 -0
  69. package/lib/tools/discussion.js +195 -0
  70. package/lib/tools/gist.d.ts +155 -0
  71. package/lib/tools/gist.js +190 -0
  72. package/lib/tools/help.js +214 -13
  73. package/lib/tools/image-utils.d.ts +8 -0
  74. package/lib/tools/image-utils.js +34 -0
  75. package/lib/tools/issue.d.ts +8 -1
  76. package/lib/tools/issue.js +26 -4
  77. package/lib/tools/label.d.ts +80 -0
  78. package/lib/tools/label.js +237 -0
  79. package/lib/tools/pr.d.ts +8 -0
  80. package/lib/tools/pr.js +48 -3
  81. package/lib/tools/project.d.ts +80 -0
  82. package/lib/tools/project.js +241 -0
  83. package/lib/tools/repo.js +13 -1
  84. package/lib/tools/ruleset.d.ts +80 -0
  85. package/lib/tools/ruleset.js +177 -0
  86. package/lib/tools/skill.d.ts +24 -1
  87. package/lib/tools/skill.js +115 -5
  88. package/lib/tools/status.d.ts +40 -0
  89. package/lib/tools/status.js +58 -0
  90. package/lib/types.d.ts +44 -0
  91. package/lib/ui/auth-guide-card.d.ts +4 -0
  92. package/lib/ui/auth-guide-card.js +161 -0
  93. package/lib/ui/branch-selector.d.ts +10 -0
  94. package/lib/ui/branch-selector.js +163 -0
  95. package/lib/ui/commit-modal.d.ts +8 -0
  96. package/lib/ui/commit-modal.js +219 -0
  97. package/lib/ui/help-modal.d.ts +15 -0
  98. package/lib/ui/help-modal.js +202 -0
  99. package/lib/ui/icons.d.ts +7 -0
  100. package/lib/ui/icons.js +53 -0
  101. package/lib/ui/network-banner.d.ts +10 -0
  102. package/lib/ui/network-banner.js +54 -0
  103. package/lib/ui/storage.d.ts +35 -0
  104. package/lib/ui/storage.js +158 -0
  105. package/lib/ui/styles.d.ts +6 -0
  106. package/lib/ui/styles.js +1544 -0
  107. package/lib/ui/sync-settings-modal.d.ts +11 -0
  108. package/lib/ui/sync-settings-modal.js +350 -0
  109. package/lib/ui/sync-status-badge.d.ts +8 -0
  110. package/lib/ui/sync-status-badge.js +64 -0
  111. package/lib/ui/upgrade-card.d.ts +7 -0
  112. package/lib/ui/upgrade-card.js +222 -0
  113. package/lib/ui/utils.d.ts +45 -0
  114. package/lib/ui/utils.js +226 -0
  115. package/lib/ui/workspace-panel.d.ts +4 -0
  116. package/lib/ui/workspace-panel.js +489 -0
  117. package/lib/upgrade-manager.d.ts +5 -1
  118. package/lib/upgrade-manager.js +24 -1
  119. package/lib/version.d.ts +1 -1
  120. package/lib/version.js +1 -1
  121. package/package.json +2 -2
@@ -0,0 +1,241 @@
1
+ import { renderProjectMarkdown } from '../renderers/markdown.js';
2
+ function normalizeOutput(data) {
3
+ if (data && typeof data === 'object' && !Array.isArray(data)) {
4
+ return data;
5
+ }
6
+ if (Array.isArray(data)) {
7
+ return { items: data, count: data.length };
8
+ }
9
+ return { result: data !== undefined && data !== null ? data : '' };
10
+ }
11
+ /**
12
+ * 检测错误信息是否属于 Scope 权限不足,并返回标准自愈对象
13
+ */
14
+ export function checkScopeRequired(errorMsg, scope, command, tool) {
15
+ const lower = (errorMsg || '').toLowerCase();
16
+ if (lower.includes('missing required scopes') || lower.includes('needs the') || lower.includes('scope')) {
17
+ return {
18
+ status: 'SCOPE_REQUIRED',
19
+ scope,
20
+ command,
21
+ tool,
22
+ };
23
+ }
24
+ return null;
25
+ }
26
+ export function createProjectTool(executor) {
27
+ return {
28
+ name: 'github_project',
29
+ description: '管理 GitHub Projects v2 项目看板与任务规划:查看项目列表与卡片条目、关联 Issue/PR 到看板、创建草稿任务、删除看板条目。未授权时提供平台感知自愈命令。',
30
+ parameters: {
31
+ type: 'object',
32
+ properties: {
33
+ action: {
34
+ type: 'string',
35
+ enum: ['list', 'view', 'item-list', 'item-add', 'item-create', 'item-delete'],
36
+ description: 'Project 操作类型。list 查看项目列表、view 查看项目详情、item-list 查看看板条目、item-add 添加现有Issue/PR、item-create 创建草稿条目、item-delete 删除条目。',
37
+ },
38
+ owner: {
39
+ type: 'string',
40
+ description: '项目所属用户或组织(缺省为 "@me" 即当前用户)。',
41
+ },
42
+ project_number: {
43
+ type: 'number',
44
+ description: '项目数字编号(view, item-list, item-add, item-create 动作必需)。',
45
+ },
46
+ item_id: {
47
+ type: 'string',
48
+ description: '项目条目唯一标识 ID(item-delete 动作必需)。',
49
+ },
50
+ url: {
51
+ type: 'string',
52
+ description: '要添加到项目的 Issue 或 PR URL(item-add 动作必需)。',
53
+ },
54
+ title: {
55
+ type: 'string',
56
+ description: '草稿条目标题(item-create 动作必需)。',
57
+ },
58
+ body: {
59
+ type: 'string',
60
+ description: '草稿条目正文描述(item-create 动作可选)。',
61
+ },
62
+ query: {
63
+ type: 'string',
64
+ description: '过滤查询语法(item-list 动作可选,如 "is:open assignee:@me")。',
65
+ },
66
+ closed: {
67
+ type: 'boolean',
68
+ description: '是否包含已关闭的项目(list 动作使用,默认 false)。',
69
+ },
70
+ limit: {
71
+ type: 'number',
72
+ description: '返回数量上限(list 与 item-list 动作使用,默认 30)。',
73
+ },
74
+ confirm: {
75
+ type: 'boolean',
76
+ description: '危险操作确认标记(item-delete 动作必需)。未确认时输出影响评估并拦截。',
77
+ },
78
+ },
79
+ required: ['action'],
80
+ },
81
+ output: {
82
+ schema: { type: 'object' },
83
+ render(args, value) {
84
+ return [{ type: 'text', text: renderProjectMarkdown(args, value) }];
85
+ },
86
+ },
87
+ async execute(args, execContext) {
88
+ const cwd = execContext?.cwd || process.cwd();
89
+ const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
90
+ const owner = typeof args.owner === 'string' && args.owner.trim() ? args.owner.trim() : '@me';
91
+ const runOpts = { cwd, sessionId };
92
+ switch (args.action) {
93
+ case 'list': {
94
+ const limit = typeof args.limit === 'number' && args.limit > 0 ? Math.min(args.limit, 100) : 30;
95
+ const listCmd = [
96
+ 'project', 'list',
97
+ '--format', 'json',
98
+ '--owner', owner,
99
+ '-L', String(limit),
100
+ ];
101
+ if (args.closed === true)
102
+ listCmd.push('--closed');
103
+ const res = await executor.run(listCmd, runOpts);
104
+ if (!res.ok) {
105
+ const scopeFix = checkScopeRequired(res.error || '', 'read:project', 'gh auth refresh -s read:project', 'github_project');
106
+ if (scopeFix)
107
+ return scopeFix;
108
+ throw new Error(res.error);
109
+ }
110
+ return normalizeOutput(res.data || res.rawOutput);
111
+ }
112
+ case 'view': {
113
+ if (!args.project_number || typeof args.project_number !== 'number') {
114
+ throw new Error('view 操作必须提供 project_number(项目数字编号)');
115
+ }
116
+ const viewCmd = [
117
+ 'project', 'view', String(args.project_number),
118
+ '--format', 'json',
119
+ '--owner', owner,
120
+ ];
121
+ const res = await executor.run(viewCmd, runOpts);
122
+ if (!res.ok) {
123
+ const scopeFix = checkScopeRequired(res.error || '', 'read:project', 'gh auth refresh -s read:project', 'github_project');
124
+ if (scopeFix)
125
+ return scopeFix;
126
+ throw new Error(res.error);
127
+ }
128
+ return normalizeOutput(res.data || res.rawOutput);
129
+ }
130
+ case 'item-list': {
131
+ if (!args.project_number || typeof args.project_number !== 'number') {
132
+ throw new Error('item-list 操作必须提供 project_number(项目数字编号)');
133
+ }
134
+ const limit = typeof args.limit === 'number' && args.limit > 0 ? Math.min(args.limit, 100) : 30;
135
+ const itemCmd = [
136
+ 'project', 'item-list', String(args.project_number),
137
+ '--format', 'json',
138
+ '--owner', owner,
139
+ '-L', String(limit),
140
+ ];
141
+ if (args.query)
142
+ itemCmd.push('--query', String(args.query).trim());
143
+ const res = await executor.run(itemCmd, runOpts);
144
+ if (!res.ok) {
145
+ const scopeFix = checkScopeRequired(res.error || '', 'read:project', 'gh auth refresh -s read:project', 'github_project');
146
+ if (scopeFix)
147
+ return scopeFix;
148
+ throw new Error(res.error);
149
+ }
150
+ return normalizeOutput(res.data || res.rawOutput);
151
+ }
152
+ case 'item-add': {
153
+ if (!args.project_number || typeof args.project_number !== 'number') {
154
+ throw new Error('item-add 操作必须提供 project_number(项目数字编号)');
155
+ }
156
+ if (!args.url || typeof args.url !== 'string' || !args.url.trim()) {
157
+ throw new Error('item-add 操作必须提供 url(Issue 或 PR 的完整链接)');
158
+ }
159
+ const addCmd = [
160
+ 'project', 'item-add', String(args.project_number),
161
+ '--owner', owner,
162
+ '--url', args.url.trim(),
163
+ '--format', 'json',
164
+ ];
165
+ const res = await executor.run(addCmd, runOpts);
166
+ if (!res.ok) {
167
+ const scopeFix = checkScopeRequired(res.error || '', 'project', 'gh auth refresh -s project', 'github_project');
168
+ if (scopeFix)
169
+ return scopeFix;
170
+ throw new Error(res.error);
171
+ }
172
+ return {
173
+ message: `已成功将条目添加到项目 #${args.project_number}`,
174
+ output: res.rawOutput || 'Item added',
175
+ };
176
+ }
177
+ case 'item-create': {
178
+ if (!args.project_number || typeof args.project_number !== 'number') {
179
+ throw new Error('item-create 操作必须提供 project_number(项目数字编号)');
180
+ }
181
+ if (!args.title || typeof args.title !== 'string' || !args.title.trim()) {
182
+ throw new Error('item-create 操作必须提供 title(草稿条目标题)');
183
+ }
184
+ const createCmd = [
185
+ 'project', 'item-create', String(args.project_number),
186
+ '--owner', owner,
187
+ '--title', args.title.trim(),
188
+ '--format', 'json',
189
+ ];
190
+ if (args.body)
191
+ createCmd.push('--body', String(args.body).trim());
192
+ const res = await executor.run(createCmd, runOpts);
193
+ if (!res.ok) {
194
+ const scopeFix = checkScopeRequired(res.error || '', 'project', 'gh auth refresh -s project', 'github_project');
195
+ if (scopeFix)
196
+ return scopeFix;
197
+ throw new Error(res.error);
198
+ }
199
+ return {
200
+ message: `已成功在项目 #${args.project_number} 中创建条目「${args.title.trim()}」`,
201
+ output: res.rawOutput || 'Draft item created',
202
+ };
203
+ }
204
+ case 'item-delete': {
205
+ if (!args.item_id || typeof args.item_id !== 'string' || !args.item_id.trim()) {
206
+ throw new Error('item-delete 操作必须提供 item_id(条目唯一 ID)');
207
+ }
208
+ const cleanItemId = args.item_id.trim();
209
+ // 确认防线拦截
210
+ if (args.confirm !== true) {
211
+ return {
212
+ status: 'AWAITING_CONFIRMATION',
213
+ warning: `⚠️ 删除确认拦截:你即将从看板中删除条目(ID: ${cleanItemId})。`,
214
+ impact: '该操作将从项目看板中物理移除该条目(若是关联的 Issue/PR,实体不会被删除,仅解除看板关联)。',
215
+ requiredAction: '如确认删除,请在参数中附加 `confirm: true` 重新调用。',
216
+ };
217
+ }
218
+ const deleteCmd = [
219
+ 'project', 'item-delete',
220
+ '--owner', owner,
221
+ '--id', cleanItemId,
222
+ '--format', 'json',
223
+ ];
224
+ const res = await executor.run(deleteCmd, runOpts);
225
+ if (!res.ok) {
226
+ const scopeFix = checkScopeRequired(res.error || '', 'project', 'gh auth refresh -s project', 'github_project');
227
+ if (scopeFix)
228
+ return scopeFix;
229
+ throw new Error(res.error);
230
+ }
231
+ return {
232
+ message: `条目「${cleanItemId}」已从项目看板中成功删除`,
233
+ output: res.rawOutput || 'Item deleted',
234
+ };
235
+ }
236
+ default:
237
+ throw new Error(`未知的 Project 操作: ${args.action}`);
238
+ }
239
+ },
240
+ };
241
+ }
package/lib/tools/repo.js CHANGED
@@ -239,7 +239,19 @@ export function createRepoTool(executor) {
239
239
  if (content.includes('\0')) {
240
240
  throw new Error(`「${args.path}」是二进制文件(检测到 NUL 字节),不适合文本读取。请改用 github_api(endpoint: 'repos/${owner}/${repoName}/contents/${args.path.split('/').map(encodeURIComponent).join('/')}', raw: true, binary 场景自行落盘) 获取字节流。`);
241
241
  }
242
- return { path: args.path, ref: args.ref, content, size: content.length, ...(res.degraded ? { degraded: res.degraded, degradedReason: res.degradedReason } : {}) };
242
+ const resultObj = {
243
+ path: args.path,
244
+ content,
245
+ size: content.length,
246
+ };
247
+ if (args.ref) {
248
+ resultObj.ref = args.ref;
249
+ }
250
+ if (res.degraded) {
251
+ resultObj.degraded = res.degraded;
252
+ resultObj.degradedReason = res.degradedReason;
253
+ }
254
+ return resultObj;
243
255
  }
244
256
  case 'read_dir': {
245
257
  let repoTarget = args.repo;
@@ -0,0 +1,80 @@
1
+ import type { GhExecutor } from '../executor.js';
2
+ export interface ParsedRule {
3
+ name: string;
4
+ params: string;
5
+ source: string;
6
+ }
7
+ export interface ParsedRulesetCheck {
8
+ ruleCount: number;
9
+ branch: string;
10
+ repo: string;
11
+ rules: ParsedRule[];
12
+ rawText: string;
13
+ }
14
+ /**
15
+ * 解析 gh ruleset check 的纯文本输出,提取适用规则数、分支、仓库以及具体规则详情
16
+ */
17
+ export declare function parseRulesetCheckOutput(output: string): ParsedRulesetCheck;
18
+ export declare function createRulesetTool(executor: GhExecutor): {
19
+ name: string;
20
+ description: string;
21
+ parameters: {
22
+ type: string;
23
+ properties: {
24
+ action: {
25
+ type: string;
26
+ enum: string[];
27
+ description: string;
28
+ };
29
+ repo: {
30
+ type: string;
31
+ description: string;
32
+ };
33
+ branch: {
34
+ type: string;
35
+ description: string;
36
+ };
37
+ default_branch: {
38
+ type: string;
39
+ description: string;
40
+ };
41
+ ruleset_id: {
42
+ type: string;
43
+ description: string;
44
+ };
45
+ org: {
46
+ type: string;
47
+ description: string;
48
+ };
49
+ no_parents: {
50
+ type: string;
51
+ description: string;
52
+ };
53
+ limit: {
54
+ type: string;
55
+ description: string;
56
+ };
57
+ };
58
+ required: string[];
59
+ };
60
+ output: {
61
+ schema: {
62
+ type: string;
63
+ };
64
+ render(args: any, value: any): {
65
+ type: string;
66
+ text: string;
67
+ }[];
68
+ };
69
+ execute(args: any, execContext: any): Promise<ParsedRulesetCheck | {
70
+ rawText: string;
71
+ org: any;
72
+ repo: string;
73
+ ruleset_id?: undefined;
74
+ } | {
75
+ ruleset_id: string;
76
+ rawText: string;
77
+ org: any;
78
+ repo: string;
79
+ }>;
80
+ };
@@ -0,0 +1,177 @@
1
+ import { renderRulesetMarkdown } from '../renderers/markdown.js';
2
+ /**
3
+ * 解析 gh ruleset check 的纯文本输出,提取适用规则数、分支、仓库以及具体规则详情
4
+ */
5
+ export function parseRulesetCheckOutput(output) {
6
+ const trimmed = (output || '').trim();
7
+ const result = {
8
+ ruleCount: 0,
9
+ branch: '',
10
+ repo: '',
11
+ rules: [],
12
+ rawText: trimmed,
13
+ };
14
+ if (!trimmed)
15
+ return result;
16
+ // 1. 匹配首行: "N rules apply to branch <branch> in repo <repo>"
17
+ const firstLineMatch = trimmed.match(/^(\d+)\s+rules?\s+apply\s+to\s+branch\s+(\S+)\s+in\s+repo\s+(\S+)/i);
18
+ if (firstLineMatch) {
19
+ result.ruleCount = parseInt(firstLineMatch[1], 10);
20
+ result.branch = firstLineMatch[2];
21
+ result.repo = firstLineMatch[3];
22
+ }
23
+ // 2. 匹配具体规则明细项:形如 "- <name>: <params>\n (configured in <source>)"
24
+ const rulePattern = /-\s*([a-zA-Z0-9_-]+):\s*([^\n\r]+)(?:[\r\n]+\s*\((configured in [^)]+)\))?/g;
25
+ let match;
26
+ while ((match = rulePattern.exec(trimmed)) !== null) {
27
+ result.rules.push({
28
+ name: match[1],
29
+ params: (match[2] || '').trim(),
30
+ source: (match[3] || '').trim(),
31
+ });
32
+ }
33
+ // 若首行未声明数量但解析到了规则列表,自动校正
34
+ if (result.rules.length > 0 && result.ruleCount === 0) {
35
+ result.ruleCount = result.rules.length;
36
+ }
37
+ return result;
38
+ }
39
+ export function createRulesetTool(executor) {
40
+ return {
41
+ name: 'github_ruleset',
42
+ description: '查看与诊断 GitHub 仓库/组织规则集 (Rulesets):检查分支适用的合并策略规则 (check)、查看规则集列表 (list) 与详情 (view)。是诊断“PR 为什么不能合并”的权威规则策略视角。',
43
+ parameters: {
44
+ type: 'object',
45
+ properties: {
46
+ action: {
47
+ type: 'string',
48
+ enum: ['list', 'view', 'check'],
49
+ description: 'Ruleset 操作类型。check 检查分支规则约束(策略排障核心)、list 查看规则集列表、view 查看规则集详情。',
50
+ },
51
+ repo: {
52
+ type: 'string',
53
+ description: '目标仓库 [owner/repo],留空则识别当前工作区仓库。',
54
+ },
55
+ branch: {
56
+ type: 'string',
57
+ description: '要检查的分支名称(check 动作使用,缺省为当前分支)。',
58
+ },
59
+ default_branch: {
60
+ type: 'boolean',
61
+ description: '是否直接检查目标仓库默认分支的规则约束(check 动作可选,映射 --default)。',
62
+ },
63
+ ruleset_id: {
64
+ type: 'string',
65
+ description: '规则集 ID(view 动作必需,避免交互式选择挂起)。',
66
+ },
67
+ org: {
68
+ type: 'string',
69
+ description: '组织名称(list, view 动作可选,查看组织级规则集;需 admin:org 权限)。',
70
+ },
71
+ no_parents: {
72
+ type: 'boolean',
73
+ description: '是否排除上级组织配置的继承规则集(list, view 动作可选,默认包含上级继承;true 时仅看当前层级)。',
74
+ },
75
+ limit: {
76
+ type: 'number',
77
+ description: '返回规则集数量上限(list 动作使用,默认 30)。',
78
+ },
79
+ },
80
+ required: ['action'],
81
+ },
82
+ output: {
83
+ schema: { type: 'object' },
84
+ render(args, value) {
85
+ return [{ type: 'text', text: renderRulesetMarkdown(args, value) }];
86
+ },
87
+ },
88
+ async execute(args, execContext) {
89
+ const cwd = execContext?.cwd || process.cwd();
90
+ const sessionId = execContext?.sessionId || execContext?.session?.id || process.env.DSH_SESSION_ID;
91
+ // 解析当前工作区仓库(若未传 org 且需要 repo 维度)
92
+ const resolveRepoTarget = async () => {
93
+ if (args.repo)
94
+ return args.repo;
95
+ const meta = await executor.getRepoMetadata(cwd, sessionId);
96
+ if (meta?.nameWithOwner)
97
+ return meta.nameWithOwner;
98
+ throw new Error('未能自动识别当前工作区关联的 GitHub 仓库。请通过 `repo` 参数显式指定目标仓库 [owner/repo](例如: "ItQianChen/dsh-github-flow"),或在当前工作区目录下关联 Git 远程仓库。');
99
+ };
100
+ const runOpts = { cwd, sessionId };
101
+ switch (args.action) {
102
+ case 'check': {
103
+ const repoTarget = await resolveRepoTarget();
104
+ const checkCmd = ['ruleset', 'check'];
105
+ if (args.branch) {
106
+ checkCmd.push(args.branch);
107
+ }
108
+ if (args.default_branch) {
109
+ checkCmd.push('--default');
110
+ }
111
+ checkCmd.push('-R', repoTarget);
112
+ const res = await executor.run(checkCmd, { ...runOpts, rawText: true });
113
+ if (!res.ok)
114
+ throw new Error(res.error);
115
+ const parsed = parseRulesetCheckOutput(res.rawOutput || '');
116
+ if (!parsed.repo)
117
+ parsed.repo = repoTarget;
118
+ if (!parsed.branch && args.branch)
119
+ parsed.branch = args.branch;
120
+ return parsed;
121
+ }
122
+ case 'list': {
123
+ const listCmd = ['ruleset', 'list'];
124
+ let repoTarget = '';
125
+ if (args.org) {
126
+ listCmd.push('--org', args.org);
127
+ }
128
+ else {
129
+ repoTarget = await resolveRepoTarget();
130
+ listCmd.push('-R', repoTarget);
131
+ }
132
+ if (args.no_parents) {
133
+ listCmd.push('--no-parents');
134
+ }
135
+ const limit = typeof args.limit === 'number' && args.limit > 0 ? args.limit : 30;
136
+ listCmd.push('-L', String(limit));
137
+ const res = await executor.run(listCmd, { ...runOpts, rawText: true });
138
+ if (!res.ok)
139
+ throw new Error(res.error);
140
+ return {
141
+ rawText: (res.rawOutput || '').trim(),
142
+ org: args.org,
143
+ repo: repoTarget,
144
+ };
145
+ }
146
+ case 'view': {
147
+ if (args.ruleset_id === undefined || args.ruleset_id === null || String(args.ruleset_id).trim() === '') {
148
+ throw new Error('查看规则集详情时 `ruleset_id` 参数为必填项(输入规则集数字 ID,如 "4898070")。');
149
+ }
150
+ const viewCmd = ['ruleset', 'view', String(args.ruleset_id)];
151
+ let repoTarget = '';
152
+ if (args.org) {
153
+ viewCmd.push('--org', args.org);
154
+ }
155
+ else {
156
+ repoTarget = await resolveRepoTarget();
157
+ viewCmd.push('-R', repoTarget);
158
+ }
159
+ if (args.no_parents) {
160
+ viewCmd.push('--no-parents');
161
+ }
162
+ const res = await executor.run(viewCmd, { ...runOpts, rawText: true });
163
+ if (!res.ok)
164
+ throw new Error(res.error);
165
+ return {
166
+ ruleset_id: String(args.ruleset_id),
167
+ rawText: (res.rawOutput || '').trim(),
168
+ org: args.org,
169
+ repo: repoTarget,
170
+ };
171
+ }
172
+ default:
173
+ throw new Error(`不支持的 ruleset 操作: "${args.action}"。可选值: check, list, view。`);
174
+ }
175
+ },
176
+ };
177
+ }
@@ -43,6 +43,29 @@ export declare function createSkillTool(executor: GhExecutor): {
43
43
  type: string;
44
44
  description: string;
45
45
  };
46
+ directory: {
47
+ type: string;
48
+ description: string;
49
+ };
50
+ dry_run: {
51
+ type: string;
52
+ description: string;
53
+ };
54
+ fix: {
55
+ type: string;
56
+ description: string;
57
+ };
58
+ tag: {
59
+ type: string;
60
+ description: string;
61
+ };
62
+ skill_names: {
63
+ type: string;
64
+ items: {
65
+ type: string;
66
+ };
67
+ description: string;
68
+ };
46
69
  confirm: {
47
70
  type: string;
48
71
  description: string;
@@ -59,5 +82,5 @@ export declare function createSkillTool(executor: GhExecutor): {
59
82
  text: string;
60
83
  }[];
61
84
  };
62
- execute(args: any, execContext: any): Promise<Record<string, any>>;
85
+ execute(args: any, execContext: any): Promise<Record<string, any> | import("../renderers/skill.js").SkillPublishReport>;
63
86
  };