dsh-github-flow 0.2.1 → 0.4.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 +234 -130
- package/README.md +217 -112
- package/docs/UPGRADE-NOTES.md +15 -9
- package/docs/architecture.md +120 -108
- package/docs/images/2.png +0 -0
- package/docs/npm-publish-guide.md +12 -1
- package/lib/api/routes-actions.js +117 -0
- package/lib/api/routes-overview.js +198 -3
- package/lib/cache.d.ts +4 -0
- package/lib/cache.js +14 -0
- package/lib/client.d.ts +13 -1
- package/lib/client.js +3162 -2162
- package/lib/executor/git-parser.d.ts +25 -0
- package/lib/executor/git-parser.js +143 -0
- package/lib/executor/platform-guide.d.ts +14 -0
- package/lib/executor/platform-guide.js +40 -0
- package/lib/executor/process-utils.d.ts +51 -0
- package/lib/executor/process-utils.js +162 -0
- package/lib/executor/version-utils.d.ts +12 -0
- package/lib/executor/version-utils.js +27 -0
- package/lib/executor.d.ts +131 -17
- package/lib/executor.js +411 -223
- package/lib/git-watcher.d.ts +36 -0
- package/lib/git-watcher.js +91 -0
- package/lib/main.js +11 -0
- package/lib/renderers/cache.d.ts +8 -0
- package/lib/renderers/cache.js +66 -0
- package/lib/renderers/common.d.ts +24 -0
- package/lib/renderers/common.js +88 -0
- package/lib/renderers/gist.d.ts +15 -0
- package/lib/renderers/gist.js +99 -0
- package/lib/renderers/issue.d.ts +4 -0
- package/lib/renderers/issue.js +70 -0
- package/lib/renderers/label.d.ts +4 -0
- package/lib/renderers/label.js +48 -0
- package/lib/renderers/markdown.d.ts +15 -26
- package/lib/renderers/markdown.js +15 -517
- package/lib/renderers/pr.d.ts +4 -0
- package/lib/renderers/pr.js +107 -0
- package/lib/renderers/release.d.ts +4 -0
- package/lib/renderers/release.js +74 -0
- package/lib/renderers/repo.d.ts +4 -0
- package/lib/renderers/repo.js +149 -0
- package/lib/renderers/ruleset.d.ts +4 -0
- package/lib/renderers/ruleset.js +74 -0
- package/lib/renderers/run.d.ts +4 -0
- package/lib/renderers/run.js +79 -0
- package/lib/renderers/skill.d.ts +4 -0
- package/lib/renderers/skill.js +61 -0
- package/lib/renderers/status.d.ts +23 -0
- package/lib/renderers/status.js +124 -0
- package/lib/tools/cache.d.ts +64 -0
- package/lib/tools/cache.js +186 -0
- package/lib/tools/gist.d.ts +155 -0
- package/lib/tools/gist.js +190 -0
- package/lib/tools/help.js +233 -11
- package/lib/tools/image-utils.d.ts +8 -0
- package/lib/tools/image-utils.js +34 -0
- package/lib/tools/issue.d.ts +17 -1
- package/lib/tools/issue.js +114 -8
- package/lib/tools/label.d.ts +80 -0
- package/lib/tools/label.js +237 -0
- package/lib/tools/pr.d.ts +12 -0
- package/lib/tools/pr.js +96 -4
- package/lib/tools/repo.d.ts +8 -0
- package/lib/tools/repo.js +127 -3
- package/lib/tools/ruleset.d.ts +80 -0
- package/lib/tools/ruleset.js +177 -0
- package/lib/tools/run.d.ts +8 -0
- package/lib/tools/run.js +104 -3
- package/lib/tools/secret.d.ts +5 -0
- package/lib/tools/secret.js +108 -23
- package/lib/tools/status.d.ts +39 -0
- package/lib/tools/status.js +53 -0
- package/lib/types.d.ts +112 -0
- package/lib/ui/auth-guide-card.d.ts +4 -0
- package/lib/ui/auth-guide-card.js +161 -0
- package/lib/ui/branch-selector.d.ts +10 -0
- package/lib/ui/branch-selector.js +163 -0
- package/lib/ui/commit-modal.d.ts +8 -0
- package/lib/ui/commit-modal.js +89 -0
- package/lib/ui/icons.d.ts +7 -0
- package/lib/ui/icons.js +53 -0
- package/lib/ui/network-banner.d.ts +10 -0
- package/lib/ui/network-banner.js +54 -0
- package/lib/ui/storage.d.ts +26 -0
- package/lib/ui/storage.js +134 -0
- package/lib/ui/styles.d.ts +6 -0
- package/lib/ui/styles.js +1285 -0
- package/lib/ui/sync-settings-modal.d.ts +11 -0
- package/lib/ui/sync-settings-modal.js +350 -0
- package/lib/ui/sync-status-badge.d.ts +8 -0
- package/lib/ui/sync-status-badge.js +64 -0
- package/lib/ui/upgrade-card.d.ts +7 -0
- package/lib/ui/upgrade-card.js +197 -0
- package/lib/ui/utils.d.ts +10 -0
- package/lib/ui/utils.js +66 -0
- package/lib/ui/workspace-panel.d.ts +4 -0
- package/lib/ui/workspace-panel.js +464 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { sanitizeCell, formatReleaseBadge } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Release 领域渲染器:负责版本发布历史列表与详情卡片(含资产清单、发布说明预览)
|
|
4
|
+
*/
|
|
5
|
+
export function renderReleaseMarkdown(args, value) {
|
|
6
|
+
if (!value)
|
|
7
|
+
return '无返回内容';
|
|
8
|
+
if (value.status === 'AWAITING_CONFIRMATION') {
|
|
9
|
+
return [
|
|
10
|
+
`> ### ${value.warning || '⚠️ 高危操作拦截警告'}`,
|
|
11
|
+
`> - **影响评估**: ${value.impact || '该操作将改变远端仓库状态。'}`,
|
|
12
|
+
`> - **确认指引**: ${value.requiredAction || '如确认继续,请向用户核实并在参数中附加 `confirm: true` 重新调用。'}`,
|
|
13
|
+
].join('\n');
|
|
14
|
+
}
|
|
15
|
+
if (typeof value.result === 'string')
|
|
16
|
+
return value.result;
|
|
17
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
18
|
+
const extra = value.detail || value.output;
|
|
19
|
+
return `**${value.message}**` + (value.url ? `\n🔗 链接: ${value.url}` : '') + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
20
|
+
}
|
|
21
|
+
// 5.1 Release 列表
|
|
22
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
23
|
+
const list = value.items.filter(Boolean);
|
|
24
|
+
if (list.length === 0)
|
|
25
|
+
return '📭 当前仓库暂无任何 Release。';
|
|
26
|
+
const rows = list.map((r) => {
|
|
27
|
+
const tag = r.tagName ? `\`${r.tagName}\`` : '-';
|
|
28
|
+
const name = sanitizeCell(r.name || r.tagName, 40);
|
|
29
|
+
const badge = formatReleaseBadge(r.isDraft, r.isPrerelease);
|
|
30
|
+
const date = (r.publishedAt || r.createdAt || '').slice(0, 10) || '-';
|
|
31
|
+
return `| ${tag} | ${badge} | ${name} | ${date} |`;
|
|
32
|
+
});
|
|
33
|
+
return [
|
|
34
|
+
`**最近 ${list.length} 个 Release:**\n`,
|
|
35
|
+
'| Tag | 类型 | 名称 | 发布日期 |',
|
|
36
|
+
'| :--- | :--- | :--- | :--- |',
|
|
37
|
+
...rows,
|
|
38
|
+
].join('\n');
|
|
39
|
+
}
|
|
40
|
+
// 5.2 Release 详情卡片
|
|
41
|
+
if (args?.action === 'view' && value.tagName) {
|
|
42
|
+
const badge = formatReleaseBadge(value.isDraft, value.isPrerelease);
|
|
43
|
+
const lines = [
|
|
44
|
+
`### 🏷️ Release \`${value.tagName}\` ${badge}`,
|
|
45
|
+
];
|
|
46
|
+
// 元信息压缩成一段(硬换行分隔),避免逐行 filter 空串把段落分隔也滤掉。
|
|
47
|
+
// Markdown 的列表/段落渲染依赖空行:若标题、资产列表、正文之间没有空行,
|
|
48
|
+
// 列表语法会失效并连成一段——所以这里改为「分段数组 + 空行 join」。
|
|
49
|
+
const meta = [];
|
|
50
|
+
if (value.name)
|
|
51
|
+
meta.push(`**${value.name}**`);
|
|
52
|
+
if (value.url)
|
|
53
|
+
meta.push(`🔗 ${value.url}`);
|
|
54
|
+
if (value.publishedAt)
|
|
55
|
+
meta.push(`📅 发布于 ${value.publishedAt.slice(0, 10)}`);
|
|
56
|
+
if (meta.length > 0)
|
|
57
|
+
lines.push(meta.join(' \n'));
|
|
58
|
+
if (Array.isArray(value.assets) && value.assets.length > 0) {
|
|
59
|
+
const assetLines = value.assets.map((a) => {
|
|
60
|
+
const size = typeof a.size === 'number' ? ` (${(a.size / 1024).toFixed(1)} KB)` : '';
|
|
61
|
+
return `- \`${a.name || '-'}\`${size}`;
|
|
62
|
+
});
|
|
63
|
+
lines.push(`**📎 附属资产 (${value.assets.length}):**\n${assetLines.join('\n')}`);
|
|
64
|
+
}
|
|
65
|
+
if (value.body) {
|
|
66
|
+
// 发版说明可能非常长,只保留前 600 字符做快速预览,避免单次调用撑爆上下文
|
|
67
|
+
const preview = value.body.length > 600 ? value.body.slice(0, 600) + '\n…(已截断,完整内容见 Release 页面)' : value.body;
|
|
68
|
+
lines.push(`**📝 发版说明摘要:**\n\n${preview}`);
|
|
69
|
+
}
|
|
70
|
+
return lines.join('\n\n');
|
|
71
|
+
}
|
|
72
|
+
// 兜底降级
|
|
73
|
+
return JSON.stringify(value, null, 2);
|
|
74
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { sanitizeCell, formatStateBadge, appendDegradedNote } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Repo 领域渲染器:负责仓库卡片概览、README 预览、代码/仓库/Issue/Commit 跨仓检索、免 Clone 文件与目录清单渲染
|
|
4
|
+
*/
|
|
5
|
+
export function renderRepoMarkdown(args, value) {
|
|
6
|
+
if (!value)
|
|
7
|
+
return '无返回内容';
|
|
8
|
+
if (typeof value.result === 'string')
|
|
9
|
+
return value.result;
|
|
10
|
+
// 4.0 README 预览(文本截断版 + 图片清单)——必须在 message 早退分支之前:
|
|
11
|
+
// readme 返回同时带 message 与 readme 字段,先判 readme 才能渲染预览而非一行摘要
|
|
12
|
+
if (args?.action === 'readme' && typeof value.readme === 'string') {
|
|
13
|
+
const lines = [`### 📖 README 预览`, '', '```markdown', value.readme, '```'];
|
|
14
|
+
if (Array.isArray(value.images) && value.images.length > 0) {
|
|
15
|
+
lines.push('', `**📎 图片清单 (${value.images.length}):** 可用 \`image_urls\` 参数下载指定图片(留空下载全部)`);
|
|
16
|
+
value.images.slice(0, 8).forEach((u, i) => lines.push(`${i + 1}. ${u}`));
|
|
17
|
+
if (value.images.length > 8)
|
|
18
|
+
lines.push(`…共 ${value.images.length} 个`);
|
|
19
|
+
}
|
|
20
|
+
return lines.join('\n');
|
|
21
|
+
}
|
|
22
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
23
|
+
return `**${value.message}**` + (value.output ? `\n\`\`\`\n${value.output}\n\`\`\`` : '');
|
|
24
|
+
}
|
|
25
|
+
// 4.1 Repo View 概览卡片
|
|
26
|
+
if (args?.action === 'view' && value.nameWithOwner) {
|
|
27
|
+
const link = value.url ? `[${value.nameWithOwner}](${value.url})` : `**${value.nameWithOwner}**`;
|
|
28
|
+
const stars = value.stargazerCount ?? value.stars ?? 0;
|
|
29
|
+
const forks = value.forkCount ?? value.forks ?? 0;
|
|
30
|
+
const branch = value.defaultBranchRef?.name || value.defaultBranch || 'main';
|
|
31
|
+
const lines = [
|
|
32
|
+
`### 📦 ${link}`,
|
|
33
|
+
`> ⭐ **Stars**: ${stars} | 🍴 **Forks**: ${forks} | ⎇ **默认分支**: \`${branch}\``,
|
|
34
|
+
value.description ? `\n${value.description}\n` : '',
|
|
35
|
+
];
|
|
36
|
+
if (value.licenseInfo?.name || value.licenseInfo?.key) {
|
|
37
|
+
lines.push(`- **开源协议**: \`${value.licenseInfo.name || value.licenseInfo.key}\``);
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(value.repositoryTopics) && value.repositoryTopics.length > 0) {
|
|
40
|
+
lines.push(`- **仓库标签 (Topics)**: ${value.repositoryTopics.map((t) => `\`${t.name || t}\``).join(' ')}`);
|
|
41
|
+
}
|
|
42
|
+
return lines.filter(Boolean).join('\n');
|
|
43
|
+
}
|
|
44
|
+
// 4.2 Repo Search 仓库搜索列表
|
|
45
|
+
if (Array.isArray(value.items) && args?.action === 'search_repos') {
|
|
46
|
+
if (value.items.length === 0)
|
|
47
|
+
return '未找到匹配的仓库。';
|
|
48
|
+
const rows = value.items.map((r) => {
|
|
49
|
+
const name = r.fullName ? `[${r.fullName}](https://github.com/${r.fullName})` : '-';
|
|
50
|
+
const stars = r.stargazersCount ?? '-';
|
|
51
|
+
const desc = sanitizeCell(r.description, 45);
|
|
52
|
+
const date = r.updatedAt ? r.updatedAt.slice(0, 10) : '-';
|
|
53
|
+
return `| ${name} | ⭐ ${stars} | ${desc} | ${date} |`;
|
|
54
|
+
});
|
|
55
|
+
return [
|
|
56
|
+
`**仓库检索结果 (${value.items.length} 个):**\n`,
|
|
57
|
+
'| 仓库全名 | Stars | 描述 | 更新日期 |',
|
|
58
|
+
'| :--- | :--- | :--- | :--- |',
|
|
59
|
+
...rows,
|
|
60
|
+
].join('\n');
|
|
61
|
+
}
|
|
62
|
+
// 4.3 Code Search 代码搜索列表
|
|
63
|
+
if (Array.isArray(value.items) && args?.action === 'search_code') {
|
|
64
|
+
if (value.items.length === 0)
|
|
65
|
+
return '未找到匹配的代码片段。';
|
|
66
|
+
const rows = value.items.map((c) => {
|
|
67
|
+
const repo = c.repository?.fullName || '-';
|
|
68
|
+
const path = sanitizeCell(c.path, 50);
|
|
69
|
+
return `| \`${path}\` | ${repo} |`;
|
|
70
|
+
});
|
|
71
|
+
return [
|
|
72
|
+
`**代码检索结果 (${value.items.length} 处匹配):**\n`,
|
|
73
|
+
'| 文件路径 | 所属仓库 |',
|
|
74
|
+
'| :--- | :--- |',
|
|
75
|
+
...rows,
|
|
76
|
+
].join('\n');
|
|
77
|
+
}
|
|
78
|
+
// 4.4 Issue/PR 搜索列表(search_issues / search_prs 共用,字段同构)
|
|
79
|
+
if (Array.isArray(value.items) && (args?.action === 'search_issues' || args?.action === 'search_prs')) {
|
|
80
|
+
if (value.items.length === 0)
|
|
81
|
+
return '未找到匹配的结果。';
|
|
82
|
+
const kind = args.action === 'search_prs' ? 'PR' : 'Issue';
|
|
83
|
+
const rows = value.items.map((it) => {
|
|
84
|
+
const num = it.number ?? '-';
|
|
85
|
+
const repo = it.repository?.nameWithOwner || it.repository?.fullName || '-';
|
|
86
|
+
const link = it.url ? `[#${num}](${it.url})` : `#${num}`;
|
|
87
|
+
const badge = formatStateBadge(it.state, it.isDraft);
|
|
88
|
+
const author = it.author?.login || '-';
|
|
89
|
+
const date = it.updatedAt ? String(it.updatedAt).slice(0, 10) : '-';
|
|
90
|
+
return `| ${link} | ${sanitizeCell(it.title, 45)} | ${badge} | ${repo} | ${author} | ${date} |`;
|
|
91
|
+
});
|
|
92
|
+
return [
|
|
93
|
+
`**${kind} 检索结果 (${value.items.length} 条):**\n`,
|
|
94
|
+
'| 编号 | 标题 | 状态 | 仓库 | 作者 | 更新日期 |',
|
|
95
|
+
'| :--- | :--- | :--- | :--- | :--- | :--- |',
|
|
96
|
+
...rows,
|
|
97
|
+
].join('\n');
|
|
98
|
+
}
|
|
99
|
+
// 4.5 Commit 搜索列表(嵌套 commit.commit.message 取首行)
|
|
100
|
+
if (Array.isArray(value.items) && args?.action === 'search_commits') {
|
|
101
|
+
if (value.items.length === 0)
|
|
102
|
+
return '未找到匹配的提交。';
|
|
103
|
+
const rows = value.items.map((c) => {
|
|
104
|
+
const sha = `\`${String(c.sha || '').slice(0, 7)}\``;
|
|
105
|
+
const msg = sanitizeCell(String(c.commit?.message || '').split('\n')[0], 50);
|
|
106
|
+
const author = c.commit?.author?.name || c.author?.login || '-';
|
|
107
|
+
const repo = c.repository?.fullName || '-';
|
|
108
|
+
return `| ${sha} | ${msg} | ${author} | ${repo} |`;
|
|
109
|
+
});
|
|
110
|
+
return [
|
|
111
|
+
`**提交检索结果 (${value.items.length} 条):**\n`,
|
|
112
|
+
'| SHA | 提交信息 | 作者 | 仓库 |',
|
|
113
|
+
'| :--- | :--- | :--- | :--- |',
|
|
114
|
+
...rows,
|
|
115
|
+
].join('\n');
|
|
116
|
+
}
|
|
117
|
+
// 4.6 目录清单(read_dir:dir 置顶 + size 人性化;mode 八进制是 Git 内部语义不展示)
|
|
118
|
+
if (args?.action === 'read_dir' && Array.isArray(value.items)) {
|
|
119
|
+
if (value.items.length === 0)
|
|
120
|
+
return `**${value.path || '/'}** 为空目录。` + appendDegradedNote(value);
|
|
121
|
+
const sorted = [...value.items].sort((a, b) => (a.type === b.type ? String(a.name).localeCompare(String(b.name)) : a.type === 'dir' ? -1 : 1));
|
|
122
|
+
const rows = sorted.map((e) => {
|
|
123
|
+
const icon = e.type === 'dir' ? '📁' : '📄';
|
|
124
|
+
const size = e.type === 'dir' ? '-' : (typeof e.size === 'number' ? (e.size > 1024 ? `${(e.size / 1024).toFixed(1)}KB` : `${e.size}B`) : '-');
|
|
125
|
+
return `| ${icon} \`${sanitizeCell(e.name, 60)}\` | ${size} |`;
|
|
126
|
+
});
|
|
127
|
+
return [
|
|
128
|
+
`**目录 ${value.path || '/'}(${value.items.length} 项):**\n`,
|
|
129
|
+
'| 名称 | 大小 |',
|
|
130
|
+
'| :--- | :--- |',
|
|
131
|
+
...rows,
|
|
132
|
+
appendDegradedNote(value),
|
|
133
|
+
].filter((l) => l !== '').join('\n');
|
|
134
|
+
}
|
|
135
|
+
// 4.7 文件内容(read_file:代码块 + 元信息行)
|
|
136
|
+
if (args?.action === 'read_file' && typeof value.content === 'string') {
|
|
137
|
+
const ext = (value.path || '').split('.').pop() || '';
|
|
138
|
+
const size = typeof value.size === 'number' ? (value.size > 1024 ? `${(value.size / 1024).toFixed(1)}KB` : `${value.size}B`) : '-';
|
|
139
|
+
return [
|
|
140
|
+
`**📄 \`${value.path}\`** (${size}${value.ref ? ` @ ${value.ref}` : ''})\n`,
|
|
141
|
+
'```' + ext,
|
|
142
|
+
value.content,
|
|
143
|
+
'```',
|
|
144
|
+
appendDegradedNote(value),
|
|
145
|
+
].filter((l) => l !== '').join('\n');
|
|
146
|
+
}
|
|
147
|
+
// 兜底降级
|
|
148
|
+
return JSON.stringify(value, null, 2);
|
|
149
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ruleset 领域渲染器:负责 GitHub Rulesets 列表、详情与分支策略 check 的 Markdown 格式化
|
|
4
|
+
*/
|
|
5
|
+
export function renderRulesetMarkdown(args, value) {
|
|
6
|
+
if (!value)
|
|
7
|
+
return '无返回内容';
|
|
8
|
+
if (typeof value.result === 'string')
|
|
9
|
+
return value.result;
|
|
10
|
+
// 1. check 操作格式化展示(策略视角 PR 无法合并的核心排障答案)
|
|
11
|
+
if (args?.action === 'check') {
|
|
12
|
+
const branch = value.branch || args?.branch || '当前分支';
|
|
13
|
+
const repo = value.repo || args?.repo || '-';
|
|
14
|
+
const ruleCount = typeof value.ruleCount === 'number' ? value.ruleCount : 0;
|
|
15
|
+
if (ruleCount === 0) {
|
|
16
|
+
return [
|
|
17
|
+
`🟢 分支 \`${branch}\`(仓库 \`${repo}\`)当前**未受任何 Ruleset 规则保护或策略限制**。`,
|
|
18
|
+
'',
|
|
19
|
+
'> 💡 **PR 合并排障指南**: 该分支无策略规则阻断。若 PR 依然无法合并,通常是因为:',
|
|
20
|
+
'> 1. **CI 未通过**: 请调用 \\`github_pr(action: \'checks\')\\` 审查 CI 检查项,若有失败调用 \\`github_run(action: \'log_failed\')\\`;',
|
|
21
|
+
'> 2. **代码冲突**: 请检查 PR 的 \\`mergeable\\` 状态;',
|
|
22
|
+
'> 3. **Draft 状态**: 若为草稿,请使用 \\`github_pr(action: \'ready\')\\` 转为可评审状态。',
|
|
23
|
+
].join('\n');
|
|
24
|
+
}
|
|
25
|
+
const items = Array.isArray(value.rules) ? value.rules : [];
|
|
26
|
+
const ruleRows = items.map((r) => {
|
|
27
|
+
const name = r.name ? `🔒 **${r.name}**` : '-';
|
|
28
|
+
const params = r.params ? `\`${sanitizeCell(r.params, 50)}\`` : '-';
|
|
29
|
+
const source = r.source ? sanitizeCell(r.source, 40) : '-';
|
|
30
|
+
return `| ${name} | ${params} | ${source} |`;
|
|
31
|
+
});
|
|
32
|
+
const tablePart = items.length > 0
|
|
33
|
+
? [
|
|
34
|
+
'| 规则类型 | 约束详情 | 规则集来源 |',
|
|
35
|
+
'| :--- | :--- | :--- |',
|
|
36
|
+
...ruleRows,
|
|
37
|
+
].join('\n')
|
|
38
|
+
: (value.rawText ? `\`\`\`\n${value.rawText}\n\`\`\`` : '');
|
|
39
|
+
return [
|
|
40
|
+
`⚠️ 分支 \`${branch}\`(仓库 \`${repo}\`)受到 **${ruleCount}** 项策略规则保护与约束:\n`,
|
|
41
|
+
tablePart,
|
|
42
|
+
'',
|
|
43
|
+
'> 💡 **PR 合并策略排障**: PR 合并必须满足上述硬性规则(如必审批准人数、指定 CI 门禁、线性历史、签名提交等)。与 \\`github_pr(action: \'checks\')\\` 配合可完整诊断“PR 为何无法合并”。',
|
|
44
|
+
].join('\n');
|
|
45
|
+
}
|
|
46
|
+
// 2. list 操作格式化展示
|
|
47
|
+
if (args?.action === 'list') {
|
|
48
|
+
const raw = (value.rawText || value.output || '').trim();
|
|
49
|
+
if (!raw) {
|
|
50
|
+
return '📭 目标仓库/组织当前未配置任何规则集 (Rulesets)。';
|
|
51
|
+
}
|
|
52
|
+
return [
|
|
53
|
+
`**规则集列表 (Rulesets):**\n`,
|
|
54
|
+
'```',
|
|
55
|
+
raw,
|
|
56
|
+
'```',
|
|
57
|
+
].join('\n');
|
|
58
|
+
}
|
|
59
|
+
// 3. view 操作格式化展示
|
|
60
|
+
if (args?.action === 'view') {
|
|
61
|
+
const raw = (value.rawText || value.output || '').trim();
|
|
62
|
+
const idHint = value.ruleset_id ? ` (ID: ${value.ruleset_id})` : '';
|
|
63
|
+
return [
|
|
64
|
+
`### 🛡️ 规则集详情${idHint}\n`,
|
|
65
|
+
'```',
|
|
66
|
+
raw || '无详细规则内容',
|
|
67
|
+
'```',
|
|
68
|
+
].join('\n');
|
|
69
|
+
}
|
|
70
|
+
// 4. 兜底
|
|
71
|
+
if (typeof value.message === 'string')
|
|
72
|
+
return value.message;
|
|
73
|
+
return JSON.stringify(value, null, 2);
|
|
74
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { sanitizeCell, formatCiBadge } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Actions Run 领域渲染器:负责工作流运行列表、工作流清单、工作流详情与单次 Run 状态渲染
|
|
4
|
+
*/
|
|
5
|
+
export function renderRunMarkdown(args, value) {
|
|
6
|
+
if (!value)
|
|
7
|
+
return '无返回数据';
|
|
8
|
+
if (typeof value.result === 'string')
|
|
9
|
+
return value.result;
|
|
10
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
11
|
+
return `**${value.message}**` + (value.output ? `\n\`\`\`\n${value.output}\n\`\`\`` : '');
|
|
12
|
+
}
|
|
13
|
+
// 3.1 Run 列表
|
|
14
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
15
|
+
const list = value.items.filter(Boolean);
|
|
16
|
+
if (list.length === 0)
|
|
17
|
+
return '📭 暂无匹配的工作流运行记录。';
|
|
18
|
+
const rows = list.map((r) => {
|
|
19
|
+
const idLink = r.url ? `[${r.databaseId}](${r.url})` : `\`${r.databaseId}\``;
|
|
20
|
+
const badge = formatCiBadge(r.status, r.conclusion);
|
|
21
|
+
const name = sanitizeCell(r.name, 35);
|
|
22
|
+
const branch = r.headBranch ? `\`${r.headBranch}\`` : '-';
|
|
23
|
+
const event = r.event || '-';
|
|
24
|
+
const time = r.createdAt ? r.createdAt.slice(0, 16).replace('T', ' ') : '-';
|
|
25
|
+
return `| ${idLink} | ${badge} | ${name} | ${branch} | ${event} | ${time} |`;
|
|
26
|
+
});
|
|
27
|
+
return [
|
|
28
|
+
`**最近 ${list.length} 次工作流运行:**\n`,
|
|
29
|
+
'| Run ID | 结论 / 状态 | 工作流名称 | 触发分支 | 触发事件 | 运行时间 |',
|
|
30
|
+
'| :--- | :--- | :--- | :--- | :--- | :--- |',
|
|
31
|
+
...rows,
|
|
32
|
+
].join('\n');
|
|
33
|
+
}
|
|
34
|
+
// 3.2 Workflow 清单(字段集为 gh 2.96 实测合法全集 4 个)
|
|
35
|
+
if (Array.isArray(value.items) && args?.action === 'workflow_list') {
|
|
36
|
+
const list = value.items.filter(Boolean);
|
|
37
|
+
if (list.length === 0)
|
|
38
|
+
return args?.all === true ? '📭 该仓库没有任何工作流。' : '📭 暂无 active 工作流(可能全部 disabled,传 all: true 查看)。';
|
|
39
|
+
const rows = list.map((w) => {
|
|
40
|
+
const badge = w.state === 'active' ? '🟢 active' : (w.state === 'disabled' ? '🔴 disabled' : String(w.state || '-'));
|
|
41
|
+
return `| \`${w.id}\` | ${sanitizeCell(w.name, 40)} | ${badge} | \`${sanitizeCell(w.path, 45)}\` |`;
|
|
42
|
+
});
|
|
43
|
+
return [
|
|
44
|
+
`**工作流清单 (${list.length} 个${args?.all === true ? ',含 disabled' : ''}):**\n`,
|
|
45
|
+
'| ID | 名称 | 状态 | 文件 |',
|
|
46
|
+
'| :--- | :--- | :--- | :--- |',
|
|
47
|
+
...rows,
|
|
48
|
+
].join('\n');
|
|
49
|
+
}
|
|
50
|
+
// 3.3 Workflow 详情(gh 无 --json,头部结构化 + Recent runs 原文引用块——D4 决策)
|
|
51
|
+
if (args?.action === 'workflow_view' && value.workflow) {
|
|
52
|
+
const lines = [
|
|
53
|
+
`### 🔄 ${value.workflow}`,
|
|
54
|
+
`- **ID**: \`${value.id || '-'}\`${value.totalRuns ? ` | **累计运行**: ${value.totalRuns} 次` : ''}`,
|
|
55
|
+
];
|
|
56
|
+
if (value.recentRuns) {
|
|
57
|
+
lines.push('', '**最近运行:**', '```', value.recentRuns, '```');
|
|
58
|
+
}
|
|
59
|
+
return lines.join('\n');
|
|
60
|
+
}
|
|
61
|
+
// 3.4 Run View 详情
|
|
62
|
+
if (args?.action === 'view' && value.databaseId) {
|
|
63
|
+
const idLink = value.url ? `[${value.databaseId}](${value.url})` : `\`${value.databaseId}\``;
|
|
64
|
+
const badge = formatCiBadge(value.status, value.conclusion);
|
|
65
|
+
const lines = [
|
|
66
|
+
`### 工作流: ${value.name} (Run: ${idLink})`,
|
|
67
|
+
`- **状态**: ${badge}`,
|
|
68
|
+
];
|
|
69
|
+
if (Array.isArray(value.jobs) && value.jobs.length > 0) {
|
|
70
|
+
lines.push('\n**Jobs 步骤明细:**\n| Job 名称 | 结论 / 状态 | 详情 |', '| :--- | :--- | :--- |');
|
|
71
|
+
for (const j of value.jobs) {
|
|
72
|
+
lines.push(`| ${sanitizeCell(j.name, 30)} | ${formatCiBadge(j.status, j.conclusion)} | ${j.url ? `[链接](${j.url})` : '-'} |`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return lines.join('\n');
|
|
76
|
+
}
|
|
77
|
+
// 兜底降级
|
|
78
|
+
return JSON.stringify(value, null, 2);
|
|
79
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Skill 领域渲染器:负责 Agent Skills 跨仓检索结果、已安装列表与预览说明
|
|
4
|
+
*/
|
|
5
|
+
export function renderSkillMarkdown(args, value) {
|
|
6
|
+
if (!value)
|
|
7
|
+
return '无返回内容';
|
|
8
|
+
if (value.status === 'AWAITING_CONFIRMATION') {
|
|
9
|
+
return [
|
|
10
|
+
`> ### ${value.warning || '⚠️ 操作确认拦截'}`,
|
|
11
|
+
`> - **影响评估**: ${value.impact || ''}`,
|
|
12
|
+
`> - **确认指引**: ${value.requiredAction || '如确认继续,请在参数中附加 `confirm: true` 重新调用。'}`,
|
|
13
|
+
].join('\n');
|
|
14
|
+
}
|
|
15
|
+
if (typeof value.result === 'string')
|
|
16
|
+
return value.result;
|
|
17
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
18
|
+
const extra = value.detail || value.output;
|
|
19
|
+
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
20
|
+
}
|
|
21
|
+
// 6.1 Skill 检索列表
|
|
22
|
+
if (Array.isArray(value.items) && args?.action === 'search') {
|
|
23
|
+
const list = value.items.filter(Boolean);
|
|
24
|
+
if (list.length === 0)
|
|
25
|
+
return '📭 未检索到匹配的 Agent Skill。';
|
|
26
|
+
const rows = list.map((s) => {
|
|
27
|
+
const name = s.skillName ? `\`${s.skillName}\`` : '-';
|
|
28
|
+
const repo = s.repo ? `[${s.repo}](https://github.com/${s.repo})` : '-';
|
|
29
|
+
const stars = typeof s.stars === 'number' ? `⭐ ${s.stars}` : '-';
|
|
30
|
+
const desc = sanitizeCell(s.description, 50);
|
|
31
|
+
return `| ${name} | ${repo} | ${stars} | ${desc} |`;
|
|
32
|
+
});
|
|
33
|
+
return [
|
|
34
|
+
`**Skills 检索结果 (${list.length} 个):**\n`,
|
|
35
|
+
'| Skill | 仓库 | Stars | 描述 |',
|
|
36
|
+
'| :--- | :--- | :--- | :--- |',
|
|
37
|
+
...rows,
|
|
38
|
+
].join('\n');
|
|
39
|
+
}
|
|
40
|
+
// 6.2 已安装 Skills 列表
|
|
41
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
42
|
+
const list = value.items.filter(Boolean);
|
|
43
|
+
if (list.length === 0)
|
|
44
|
+
return '📭 当前环境尚未安装任何 Agent Skill。';
|
|
45
|
+
const rows = list.map((s) => {
|
|
46
|
+
const name = s.skillName ? `\`${s.skillName}\`` : '-';
|
|
47
|
+
const ver = s.version ? String(s.version) : '-';
|
|
48
|
+
const scope = s.scope ? String(s.scope) : '-';
|
|
49
|
+
const source = s.sourceURL ? `[来源](${s.sourceURL})` : (s.path ? `\`${sanitizeCell(s.path, 40)}\`` : '-');
|
|
50
|
+
return `| ${name} | ${ver} | ${scope} | ${source} |`;
|
|
51
|
+
});
|
|
52
|
+
return [
|
|
53
|
+
`**已安装 ${list.length} 个 Agent Skill:**\n`,
|
|
54
|
+
'| Skill | 版本 | 范围 | 来源 |',
|
|
55
|
+
'| :--- | :--- | :--- | :--- |',
|
|
56
|
+
...rows,
|
|
57
|
+
].join('\n');
|
|
58
|
+
}
|
|
59
|
+
// 兜底降级
|
|
60
|
+
return JSON.stringify(value, null, 2);
|
|
61
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface StatusSectionItem {
|
|
2
|
+
repo?: string;
|
|
3
|
+
number?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
detail?: string;
|
|
6
|
+
raw: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ParsedStatusReport {
|
|
9
|
+
assignedIssues: StatusSectionItem[];
|
|
10
|
+
assignedPrs: StatusSectionItem[];
|
|
11
|
+
reviewRequests: StatusSectionItem[];
|
|
12
|
+
mentions: StatusSectionItem[];
|
|
13
|
+
activity: StatusSectionItem[];
|
|
14
|
+
rawText: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 解析 gh status 的输出,提取待办 Issue、待办 PR、评审请求、提及与近期动态
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseStatusOutput(output: string): ParsedStatusReport;
|
|
20
|
+
/**
|
|
21
|
+
* Status 领域渲染器:负责用户跨仓待办大盘的 Markdown 格式化
|
|
22
|
+
*/
|
|
23
|
+
export declare function renderStatusMarkdown(args: any, value: any): string;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* 解析 gh status 的输出,提取待办 Issue、待办 PR、评审请求、提及与近期动态
|
|
4
|
+
*/
|
|
5
|
+
export function parseStatusOutput(output) {
|
|
6
|
+
const trimmed = (output || '').trim();
|
|
7
|
+
const report = {
|
|
8
|
+
assignedIssues: [],
|
|
9
|
+
assignedPrs: [],
|
|
10
|
+
reviewRequests: [],
|
|
11
|
+
mentions: [],
|
|
12
|
+
activity: [],
|
|
13
|
+
rawText: trimmed,
|
|
14
|
+
};
|
|
15
|
+
if (!trimmed)
|
|
16
|
+
return report;
|
|
17
|
+
const lines = trimmed.split(/[\r\n]+/);
|
|
18
|
+
let currentSection = null;
|
|
19
|
+
for (const line of lines) {
|
|
20
|
+
const rawLine = line.trim();
|
|
21
|
+
if (!rawLine)
|
|
22
|
+
continue;
|
|
23
|
+
// 检查是否包含版块头
|
|
24
|
+
if (/Assigned Issues/i.test(rawLine)) {
|
|
25
|
+
currentSection = 'assignedIssues';
|
|
26
|
+
// 如果同一行有双列分割
|
|
27
|
+
if (/Assigned Pull Requests/i.test(rawLine)) {
|
|
28
|
+
// 双列行,仅做标记
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (/Assigned Pull Requests/i.test(rawLine)) {
|
|
34
|
+
currentSection = 'assignedPrs';
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (/Review Requests/i.test(rawLine)) {
|
|
38
|
+
currentSection = 'reviewRequests';
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (/Mentions/i.test(rawLine)) {
|
|
42
|
+
currentSection = 'mentions';
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (/Repository Activity/i.test(rawLine)) {
|
|
46
|
+
currentSection = 'activity';
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// 过滤空状态占位符
|
|
50
|
+
if (rawLine.includes('Nothing here') || rawLine.includes('^_^')) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// 处理行内容(支持制表符或双列分隔)
|
|
54
|
+
const cleanSegments = rawLine
|
|
55
|
+
.split('│')
|
|
56
|
+
.map((s) => s.trim())
|
|
57
|
+
.filter((s) => s && !s.includes('Nothing here') && !s.includes('^_^'));
|
|
58
|
+
for (const segment of cleanSegments) {
|
|
59
|
+
if (!segment)
|
|
60
|
+
continue;
|
|
61
|
+
// 尝试匹配制表符或 "repo#123\tdesc\tdetail"
|
|
62
|
+
const tabParts = segment.split('\t').map((p) => p.trim());
|
|
63
|
+
const itemMatch = tabParts[0].match(/^([^#\s]+)#(\d+)/);
|
|
64
|
+
const item = {
|
|
65
|
+
raw: segment,
|
|
66
|
+
};
|
|
67
|
+
if (itemMatch) {
|
|
68
|
+
item.repo = itemMatch[1];
|
|
69
|
+
item.number = itemMatch[2];
|
|
70
|
+
item.title = tabParts[1] || '';
|
|
71
|
+
item.detail = tabParts[2] || '';
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
item.detail = segment;
|
|
75
|
+
}
|
|
76
|
+
if (currentSection && report[currentSection]) {
|
|
77
|
+
report[currentSection].push(item);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// 未匹配版块默认归入 activity
|
|
81
|
+
report.activity.push(item);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return report;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Status 领域渲染器:负责用户跨仓待办大盘的 Markdown 格式化
|
|
89
|
+
*/
|
|
90
|
+
export function renderStatusMarkdown(args, value) {
|
|
91
|
+
if (!value)
|
|
92
|
+
return '无返回内容';
|
|
93
|
+
const report = value.parsed || parseStatusOutput(value.rawText || value.output || '');
|
|
94
|
+
const renderSection = (title, icon, items, emptyText) => {
|
|
95
|
+
if (!items || items.length === 0) {
|
|
96
|
+
return `#### ${icon} ${title}\n*${emptyText}*\n`;
|
|
97
|
+
}
|
|
98
|
+
const rows = items.map((it) => {
|
|
99
|
+
const repoRef = it.repo && it.number ? `\`${it.repo}#${it.number}\`` : '-';
|
|
100
|
+
const titleText = sanitizeCell(it.title || it.raw, 40);
|
|
101
|
+
const detailText = sanitizeCell(it.detail, 40);
|
|
102
|
+
return `| ${repoRef} | ${titleText} | ${detailText} |`;
|
|
103
|
+
});
|
|
104
|
+
return [
|
|
105
|
+
`#### ${icon} ${title} (${items.length})\n`,
|
|
106
|
+
'| 仓库 & 编号 | 标题 / 动作 | 详情 / 摘要 |',
|
|
107
|
+
'| :--- | :--- | :--- |',
|
|
108
|
+
...rows,
|
|
109
|
+
'',
|
|
110
|
+
].join('\n');
|
|
111
|
+
};
|
|
112
|
+
const sections = [
|
|
113
|
+
renderSection('指派给我的 Issue (Assigned Issues)', '📌', report.assignedIssues, '🟢 暂无分配给你的未决 Issue'),
|
|
114
|
+
renderSection('指派给我的 PR (Assigned Pull Requests)', '🔀', report.assignedPrs, '🟢 暂无分配给你的未决 PR'),
|
|
115
|
+
renderSection('等待我评审的 PR (Review Requests)', '👀', report.reviewRequests, '🟢 暂无待你评审的 PR'),
|
|
116
|
+
renderSection('提及我的动态 (Mentions)', '🔔', report.mentions, '🟢 暂无与你相关的近期提及'),
|
|
117
|
+
renderSection('订阅仓库近期动态 (Repository Activity)', '⚡', report.activity, '暂无近期仓库动态'),
|
|
118
|
+
];
|
|
119
|
+
return [
|
|
120
|
+
'### 🧭 GitHub 个人待办与跨仓动态聚合看板\n',
|
|
121
|
+
...sections,
|
|
122
|
+
'> 💡 **待办操作建议**: 可通过 `github_issue(action: \'view\')` 或 `github_pr(action: \'checkout\')` 快速检视对应议题并展开工作。',
|
|
123
|
+
].join('\n');
|
|
124
|
+
}
|