dsh-github-flow 0.3.0 → 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 +220 -143
- package/README.md +202 -124
- package/docs/UPGRADE-NOTES.md +14 -8
- package/docs/architecture.md +111 -129
- package/lib/api/routes-actions.js +117 -0
- package/lib/api/routes-overview.js +94 -2
- package/lib/cache.d.ts +4 -0
- package/lib/cache.js +14 -0
- package/lib/client.d.ts +13 -1
- package/lib/client.js +2906 -2475
- 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 +45 -48
- package/lib/executor.js +184 -353
- package/lib/git-watcher.d.ts +36 -0
- package/lib/git-watcher.js +91 -0
- package/lib/main.js +10 -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 -32
- package/lib/renderers/markdown.js +15 -624
- 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 +107 -6
- package/lib/tools/image-utils.d.ts +8 -0
- package/lib/tools/image-utils.js +34 -0
- package/lib/tools/issue.d.ts +8 -1
- package/lib/tools/issue.js +26 -4
- package/lib/tools/label.d.ts +80 -0
- package/lib/tools/label.js +237 -0
- package/lib/tools/pr.d.ts +8 -0
- package/lib/tools/pr.js +48 -3
- package/lib/tools/ruleset.d.ts +80 -0
- package/lib/tools/ruleset.js +177 -0
- package/lib/tools/status.d.ts +39 -0
- package/lib/tools/status.js +53 -0
- package/lib/types.d.ts +39 -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,107 @@
|
|
|
1
|
+
import { sanitizeCell, formatStateBadge, formatCiBadge, formatConflictBadge } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* PR 领域渲染器:负责 PR 列表高密度表格、CI Checks、分支状态与单卡详情格式化
|
|
4
|
+
*/
|
|
5
|
+
export function renderPrMarkdown(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
|
+
// 1.1 PR 列表
|
|
22
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
23
|
+
const list = value.items.filter(Boolean);
|
|
24
|
+
if (list.length === 0)
|
|
25
|
+
return '📭 当前仓库暂无匹配的 Pull Request。';
|
|
26
|
+
const rows = list.map((p) => {
|
|
27
|
+
const numLink = p.url ? `[#${p.number}](${p.url})` : `#${p.number}`;
|
|
28
|
+
const badge = formatStateBadge(p.state, p.isDraft);
|
|
29
|
+
const title = sanitizeCell(p.title, 55);
|
|
30
|
+
const author = p.author?.login ? `@${p.author.login}` : (typeof p.author === 'string' ? `@${p.author}` : '-');
|
|
31
|
+
const date = p.updatedAt ? p.updatedAt.slice(0, 10) : (p.mergedAt ? p.mergedAt.slice(0, 10) : '-');
|
|
32
|
+
return `| ${numLink} | ${badge} | ${title} | ${author} | ${date} |`;
|
|
33
|
+
});
|
|
34
|
+
return [
|
|
35
|
+
`**找到 ${list.length} 个 Pull Request:**\n`,
|
|
36
|
+
'| 编号 | 状态 | 标题 | 作者 | 更新日期 |',
|
|
37
|
+
'| :--- | :--- | :--- | :--- | :--- |',
|
|
38
|
+
...rows,
|
|
39
|
+
].join('\n');
|
|
40
|
+
}
|
|
41
|
+
// 1.2 PR CI Checks
|
|
42
|
+
if (Array.isArray(value.items) && args?.action === 'checks') {
|
|
43
|
+
const list = value.items.filter(Boolean);
|
|
44
|
+
if (list.length === 0)
|
|
45
|
+
return '当前 PR 暂无 CI Checks 运行记录。';
|
|
46
|
+
const rows = list.map((c) => {
|
|
47
|
+
const name = sanitizeCell(c.name, 40);
|
|
48
|
+
// bucket 是 gh pr checks 的分类字段(pass/fail/pending/skipping/cancel),
|
|
49
|
+
// 优先于 state 呈现;state(SUCCESS/FAILURE/...)作为兜底——渲染器同步
|
|
50
|
+
// Issue #3 修复:旧 conclusion/detailsUrl 字段在 gh 中不存在。
|
|
51
|
+
const badge = formatCiBadge(c.state, c.bucket);
|
|
52
|
+
const url = c.link ? `[查看详情](${c.link})` : '-';
|
|
53
|
+
const wf = c.workflow ? sanitizeCell(c.workflow, 24) : '-';
|
|
54
|
+
return `| ${name} | ${badge} | ${wf} | ${url} |`;
|
|
55
|
+
});
|
|
56
|
+
return [
|
|
57
|
+
`**CI 检查结果 (${list.length} 项):**\n`,
|
|
58
|
+
'| 检查项名称 | 结论 / 状态 | 工作流 | 详情链接 |',
|
|
59
|
+
'| :--- | :--- | :--- | :--- |',
|
|
60
|
+
...rows,
|
|
61
|
+
].join('\n');
|
|
62
|
+
}
|
|
63
|
+
// 1.3 PR Status (冲突与相关状态)
|
|
64
|
+
if (args?.action === 'status') {
|
|
65
|
+
const created = Array.isArray(value.createdBy) ? value.createdBy : [];
|
|
66
|
+
const review = Array.isArray(value.needsReview) ? value.needsReview : [];
|
|
67
|
+
const parts = ['### 🔀 当前分支关联 PR 状态\n'];
|
|
68
|
+
if (created.length > 0) {
|
|
69
|
+
parts.push('**由我发起的 PR:**\n| 编号 | 状态 | 冲突检测 | 标题 |', '| :--- | :--- | :--- | :--- |');
|
|
70
|
+
for (const p of created) {
|
|
71
|
+
const link = p.url ? `[#${p.number}](${p.url})` : `#${p.number}`;
|
|
72
|
+
parts.push(`| ${link} | ${formatStateBadge(p.state)} | ${formatConflictBadge(p.mergeStateStatus)} | ${sanitizeCell(p.title, 50)} |`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
parts.push('当前分支未检测到本人发起的活跃 PR。');
|
|
77
|
+
}
|
|
78
|
+
if (review.length > 0) {
|
|
79
|
+
parts.push('\n**待我审查的 PR:**\n| 编号 | 状态 | 标题 |', '| :--- | :--- | :--- |');
|
|
80
|
+
for (const p of review) {
|
|
81
|
+
const link = p.url ? `[#${p.number}](${p.url})` : `#${p.number}`;
|
|
82
|
+
parts.push(`| ${link} | ${formatStateBadge(p.state)} | ${sanitizeCell(p.title, 50)} |`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return parts.join('\n');
|
|
86
|
+
}
|
|
87
|
+
// 1.4 PR View 单卡详情
|
|
88
|
+
if (args?.action === 'view' && value.number) {
|
|
89
|
+
const link = value.url ? `[#${value.number}](${value.url})` : `#${value.number}`;
|
|
90
|
+
const badge = formatStateBadge(value.state, value.isDraft);
|
|
91
|
+
const author = value.author?.login ? `@${value.author.login}` : '-';
|
|
92
|
+
const lines = [
|
|
93
|
+
`### ${badge} ${value.title} (${link})`,
|
|
94
|
+
`- **作者**: ${author} | **分支**: \`${value.headRefName || '-'}\` → \`${value.baseRefName || 'main'}\``,
|
|
95
|
+
`- **合并判定**: ${formatConflictBadge(value.mergeable || value.mergeStateStatus)} | **审查判定**: ${value.reviewDecision || 'NONE'}`,
|
|
96
|
+
];
|
|
97
|
+
if (Array.isArray(value.labels) && value.labels.length > 0) {
|
|
98
|
+
lines.push(`- **标签**: ${value.labels.map((l) => `\`${l.name || l}\``).join(' ')}`);
|
|
99
|
+
}
|
|
100
|
+
if (value.body) {
|
|
101
|
+
lines.push('\n**正文说明:**\n```markdown\n' + sanitizeCell(value.body, 300) + '\n```');
|
|
102
|
+
}
|
|
103
|
+
return lines.join('\n');
|
|
104
|
+
}
|
|
105
|
+
// 兜底降级
|
|
106
|
+
return JSON.stringify(value, null, 2);
|
|
107
|
+
}
|
|
@@ -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;
|