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,91 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* 定向针对 .git/HEAD 与 .git/index 创建非递归文件系统监听器。
|
|
5
|
+
* 零源码目录遍历,避免句柄耗尽与高 CPU 占用。
|
|
6
|
+
*
|
|
7
|
+
* 核心机制:
|
|
8
|
+
* 1. 靶向监听:仅监控 .git/HEAD 与 .git/index,若都不存在则兜底监控 .git 目录;
|
|
9
|
+
* 2. 800ms 防抖合并:避免连续 git 操作(如 git add 多个文件)产生事件风暴;
|
|
10
|
+
* 3. 单次触发保证(At-Most-Once):变更触发或超时熔断后,立即自动执行清理并释放全部句柄;
|
|
11
|
+
* 4. 显式释放:返回可随时主动调用的清理函数 stop()。
|
|
12
|
+
*
|
|
13
|
+
* @param cwd 目标 Git 仓库根目录
|
|
14
|
+
* @param onTrigger 变更发生或超时触发时的回调函数
|
|
15
|
+
* @param options 可选配置项(防抖与超时时间)
|
|
16
|
+
* @returns 停止监听并释放全部句柄的清理函数
|
|
17
|
+
*/
|
|
18
|
+
export function watchGitMetadata(cwd, onTrigger, options) {
|
|
19
|
+
const debounceMs = options?.debounceMs ?? 800;
|
|
20
|
+
const timeoutMs = options?.timeoutMs ?? 35_000;
|
|
21
|
+
const gitDir = path.join(cwd, '.git');
|
|
22
|
+
let stopped = false;
|
|
23
|
+
let debounceTimer = null;
|
|
24
|
+
let timeoutTimer = null;
|
|
25
|
+
const watchers = [];
|
|
26
|
+
const cleanup = () => {
|
|
27
|
+
if (debounceTimer) {
|
|
28
|
+
clearTimeout(debounceTimer);
|
|
29
|
+
debounceTimer = null;
|
|
30
|
+
}
|
|
31
|
+
if (timeoutTimer) {
|
|
32
|
+
clearTimeout(timeoutTimer);
|
|
33
|
+
timeoutTimer = null;
|
|
34
|
+
}
|
|
35
|
+
for (const w of watchers) {
|
|
36
|
+
try {
|
|
37
|
+
w.close();
|
|
38
|
+
}
|
|
39
|
+
catch { }
|
|
40
|
+
}
|
|
41
|
+
watchers.length = 0;
|
|
42
|
+
};
|
|
43
|
+
const stop = () => {
|
|
44
|
+
if (stopped)
|
|
45
|
+
return;
|
|
46
|
+
stopped = true;
|
|
47
|
+
cleanup();
|
|
48
|
+
};
|
|
49
|
+
const triggerChange = () => {
|
|
50
|
+
if (stopped)
|
|
51
|
+
return;
|
|
52
|
+
if (debounceTimer)
|
|
53
|
+
clearTimeout(debounceTimer);
|
|
54
|
+
debounceTimer = setTimeout(() => {
|
|
55
|
+
if (stopped)
|
|
56
|
+
return;
|
|
57
|
+
stopped = true;
|
|
58
|
+
cleanup();
|
|
59
|
+
try {
|
|
60
|
+
onTrigger({ changed: true, at: Date.now() });
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
|
+
}, debounceMs);
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
const targets = [path.join(gitDir, 'HEAD'), path.join(gitDir, 'index')];
|
|
67
|
+
for (const target of targets) {
|
|
68
|
+
if (fs.existsSync(target)) {
|
|
69
|
+
watchers.push(fs.watch(target, triggerChange));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (watchers.length === 0 && fs.existsSync(gitDir)) {
|
|
73
|
+
watchers.push(fs.watch(gitDir, triggerChange));
|
|
74
|
+
}
|
|
75
|
+
timeoutTimer = setTimeout(() => {
|
|
76
|
+
if (stopped)
|
|
77
|
+
return;
|
|
78
|
+
stopped = true;
|
|
79
|
+
cleanup();
|
|
80
|
+
try {
|
|
81
|
+
onTrigger({ timeout: true });
|
|
82
|
+
}
|
|
83
|
+
catch { }
|
|
84
|
+
}, timeoutMs);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
cleanup();
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
return stop;
|
|
91
|
+
}
|
package/lib/main.js
CHANGED
|
@@ -7,6 +7,11 @@ import { createApiTool } from './tools/api.js';
|
|
|
7
7
|
import { createReleaseTool } from './tools/release.js';
|
|
8
8
|
import { createSecretTool } from './tools/secret.js';
|
|
9
9
|
import { createSkillTool } from './tools/skill.js';
|
|
10
|
+
import { createLabelTool } from './tools/label.js';
|
|
11
|
+
import { createRulesetTool } from './tools/ruleset.js';
|
|
12
|
+
import { createCacheTool } from './tools/cache.js';
|
|
13
|
+
import { createGistTool } from './tools/gist.js';
|
|
14
|
+
import { createStatusTool } from './tools/status.js';
|
|
10
15
|
import { createHelpTool } from './tools/help.js';
|
|
11
16
|
import { registerGhCommand } from './commands/gh.js';
|
|
12
17
|
import { registerApiRoutes } from './api/routes.js';
|
|
@@ -97,6 +102,11 @@ export function apply(ctx, config = {}) {
|
|
|
97
102
|
ctx.tools.register(createReleaseTool(executor));
|
|
98
103
|
ctx.tools.register(createSecretTool(executor));
|
|
99
104
|
ctx.tools.register(createSkillTool(executor));
|
|
105
|
+
ctx.tools.register(createLabelTool(executor));
|
|
106
|
+
ctx.tools.register(createRulesetTool(executor));
|
|
107
|
+
ctx.tools.register(createCacheTool(executor));
|
|
108
|
+
ctx.tools.register(createGistTool(executor));
|
|
109
|
+
ctx.tools.register(createStatusTool(executor));
|
|
100
110
|
ctx.tools.register(createHelpTool(executor));
|
|
101
111
|
}
|
|
102
112
|
// 4. 注册面向 Client 端的 WebServer HTTP 路由(如果当前环境启用了 Web UI 服务)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* 友好文件大小换算:B -> KB -> MB -> GB
|
|
4
|
+
*/
|
|
5
|
+
export function formatBytes(bytes) {
|
|
6
|
+
if (typeof bytes !== 'number' || isNaN(bytes) || bytes <= 0)
|
|
7
|
+
return '0 B';
|
|
8
|
+
const k = 1024;
|
|
9
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
10
|
+
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
|
|
11
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Cache 领域渲染器:负责 GitHub Actions 缓存列表、容量盘点与清理操作的 Markdown 格式化
|
|
15
|
+
*/
|
|
16
|
+
export function renderCacheMarkdown(args, value) {
|
|
17
|
+
if (!value)
|
|
18
|
+
return '无返回内容';
|
|
19
|
+
// 1. 高危操作确认拦截报告
|
|
20
|
+
if (value.status === 'AWAITING_CONFIRMATION') {
|
|
21
|
+
return [
|
|
22
|
+
`> ### ${value.warning || '⚠️ 操作确认拦截'}`,
|
|
23
|
+
`> - **影响评估**: ${value.impact || ''}`,
|
|
24
|
+
`> - **确认指引**: ${value.requiredAction || '如确认继续,请在参数中附加 \\`confirm: true\\` 重新调用。'}`,
|
|
25
|
+
].join('\n');
|
|
26
|
+
}
|
|
27
|
+
if (typeof value.result === 'string')
|
|
28
|
+
return value.result;
|
|
29
|
+
// 2. 单操作消息提示(delete 等)
|
|
30
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
31
|
+
const extra = value.detail || value.output;
|
|
32
|
+
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
33
|
+
}
|
|
34
|
+
// 3. 缓存列表展示
|
|
35
|
+
if (Array.isArray(value.items)) {
|
|
36
|
+
const list = value.items.filter(Boolean);
|
|
37
|
+
if (list.length === 0) {
|
|
38
|
+
return args?.key
|
|
39
|
+
? `📭 未检索到匹配键名 "${args.key}" 的 Actions 缓存。`
|
|
40
|
+
: '📭 仓库中当前未发现任何 Actions 缓存。';
|
|
41
|
+
}
|
|
42
|
+
let totalBytes = 0;
|
|
43
|
+
const rows = list.map((item) => {
|
|
44
|
+
const id = item.id ? `\`${item.id}\`` : '-';
|
|
45
|
+
const key = item.key ? `\`${sanitizeCell(item.key, 35)}\`` : '-';
|
|
46
|
+
const sizeNum = typeof item.sizeInBytes === 'number' ? item.sizeInBytes : 0;
|
|
47
|
+
totalBytes += sizeNum;
|
|
48
|
+
const sizeStr = formatBytes(sizeNum);
|
|
49
|
+
const ref = item.ref ? `\`${sanitizeCell(item.ref, 25)}\`` : '-';
|
|
50
|
+
const lastAccess = item.lastAccessedAt ? item.lastAccessedAt.replace('T', ' ').replace('Z', '') : '-';
|
|
51
|
+
return `| ${id} | ${key} | ${sizeStr} | ${ref} | ${lastAccess} |`;
|
|
52
|
+
});
|
|
53
|
+
const keyHint = args?.key ? ` (过滤键: "${args.key}")` : '';
|
|
54
|
+
const totalSizeStr = formatBytes(totalBytes);
|
|
55
|
+
return [
|
|
56
|
+
`**Actions 缓存列表 (${list.length} 个, 总计占用 ${totalSizeStr})${keyHint}:**\n`,
|
|
57
|
+
'| 缓存 ID | 键名 (Key) | 大小 | 关联 Ref | 最后访问时间 |',
|
|
58
|
+
'| :--- | :--- | :--- | :--- | :--- |',
|
|
59
|
+
...rows,
|
|
60
|
+
'',
|
|
61
|
+
'> 💡 **缓存治理建议**: 若 Actions 构建耗时变长或缓存过载,可使用 `github_cache(action: \'delete\', cache_id: ...)` 精准清理,或通过 `all: true, confirm: true` 全量清空。',
|
|
62
|
+
].join('\n');
|
|
63
|
+
}
|
|
64
|
+
// 4. 兜底降级
|
|
65
|
+
return JSON.stringify(value, null, 2);
|
|
66
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH GitHub Flow - 通用 Markdown 格式化原子工具与状态徽章
|
|
3
|
+
*/
|
|
4
|
+
/** 安全清洗表格单元格文本:转义竖线,压缩换行,限制最大长度 */
|
|
5
|
+
export declare function sanitizeCell(text: any, maxLen?: number): string;
|
|
6
|
+
/** 格式化 PR / Issue 状态药丸徽章 */
|
|
7
|
+
export declare function formatStateBadge(state?: string, isDraft?: boolean): string;
|
|
8
|
+
/**
|
|
9
|
+
* 格式化 CI 工作流状态徽章。
|
|
10
|
+
* 第二参数按调用域语义不同:run 域传 conclusion(success/failure/cancelled/skipped),
|
|
11
|
+
* pr checks 域传 bucket(pass/fail/pending/skipping/cancel)——取值族不同但映射到
|
|
12
|
+
* 同一组徽章;state 参数兜底(in_progress/queued 等进行中态)。
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatCiBadge(status?: string, conclusion?: string): string;
|
|
15
|
+
/** 格式化合并冲突状态 */
|
|
16
|
+
export declare function formatConflictBadge(status?: string): string;
|
|
17
|
+
/** 格式化 Release 状态徽章 */
|
|
18
|
+
export declare function formatReleaseBadge(isDraft?: boolean, isPrerelease?: boolean): string;
|
|
19
|
+
/**
|
|
20
|
+
* 降级链路可见标注(runGuarded 契约的渲染兑现)。
|
|
21
|
+
* 为什么必须可见:降级若是静默的,Agent 就无法感知能力边界(同一 action 在
|
|
22
|
+
* 不同 gh 版本上走不同链路);一条 blockquote 注释成本一行,换链路透明。
|
|
23
|
+
*/
|
|
24
|
+
export declare function appendDegradedNote(value: any): string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH GitHub Flow - 通用 Markdown 格式化原子工具与状态徽章
|
|
3
|
+
*/
|
|
4
|
+
/** 安全清洗表格单元格文本:转义竖线,压缩换行,限制最大长度 */
|
|
5
|
+
export function sanitizeCell(text, maxLen = 80) {
|
|
6
|
+
if (text === null || text === undefined || text === '')
|
|
7
|
+
return '-';
|
|
8
|
+
const clean = String(text)
|
|
9
|
+
.replace(/\|/g, '\\|')
|
|
10
|
+
.replace(/[\r\n]+/g, ' ')
|
|
11
|
+
.trim();
|
|
12
|
+
if (clean.length > maxLen) {
|
|
13
|
+
return clean.slice(0, maxLen - 1) + '…';
|
|
14
|
+
}
|
|
15
|
+
return clean || '-';
|
|
16
|
+
}
|
|
17
|
+
/** 格式化 PR / Issue 状态药丸徽章 */
|
|
18
|
+
export function formatStateBadge(state, isDraft) {
|
|
19
|
+
if (!state)
|
|
20
|
+
return '-';
|
|
21
|
+
const s = state.toUpperCase();
|
|
22
|
+
if (isDraft)
|
|
23
|
+
return '⚪ DRAFT';
|
|
24
|
+
if (s === 'OPEN')
|
|
25
|
+
return '🟢 OPEN';
|
|
26
|
+
if (s === 'MERGED')
|
|
27
|
+
return '🟣 MERGED';
|
|
28
|
+
if (s === 'CLOSED')
|
|
29
|
+
return '🔴 CLOSED';
|
|
30
|
+
return s;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 格式化 CI 工作流状态徽章。
|
|
34
|
+
* 第二参数按调用域语义不同:run 域传 conclusion(success/failure/cancelled/skipped),
|
|
35
|
+
* pr checks 域传 bucket(pass/fail/pending/skipping/cancel)——取值族不同但映射到
|
|
36
|
+
* 同一组徽章;state 参数兜底(in_progress/queued 等进行中态)。
|
|
37
|
+
*/
|
|
38
|
+
export function formatCiBadge(status, conclusion) {
|
|
39
|
+
const conc = (conclusion || '').toLowerCase();
|
|
40
|
+
const st = (status || '').toLowerCase();
|
|
41
|
+
if (conc === 'success' || conc === 'pass')
|
|
42
|
+
return '✅ ' + conc;
|
|
43
|
+
if (conc === 'failure' || conc === 'fail')
|
|
44
|
+
return '❌ ' + conc;
|
|
45
|
+
if (conc === 'cancelled' || conc === 'cancel')
|
|
46
|
+
return '🚫 ' + conc;
|
|
47
|
+
if (conc === 'skipped' || conc === 'skipping')
|
|
48
|
+
return '⏭️ ' + conc;
|
|
49
|
+
if (conc === 'pending')
|
|
50
|
+
return '⏳ pending';
|
|
51
|
+
if (st === 'in_progress')
|
|
52
|
+
return '⏳ running';
|
|
53
|
+
if (st === 'queued')
|
|
54
|
+
return '⏱️ queued';
|
|
55
|
+
return conc || st || '-';
|
|
56
|
+
}
|
|
57
|
+
/** 格式化合并冲突状态 */
|
|
58
|
+
export function formatConflictBadge(status) {
|
|
59
|
+
if (!status)
|
|
60
|
+
return '-';
|
|
61
|
+
const s = status.toUpperCase();
|
|
62
|
+
if (s === 'CLEAN')
|
|
63
|
+
return '✅ CLEAN';
|
|
64
|
+
if (s === 'DIRTY' || s === 'CONFLICT')
|
|
65
|
+
return '⚠️ CONFLICT';
|
|
66
|
+
if (s === 'BLOCKED')
|
|
67
|
+
return '⛔ BLOCKED';
|
|
68
|
+
return s;
|
|
69
|
+
}
|
|
70
|
+
/** 格式化 Release 状态徽章 */
|
|
71
|
+
export function formatReleaseBadge(isDraft, isPrerelease) {
|
|
72
|
+
if (isDraft)
|
|
73
|
+
return '⚪ DRAFT';
|
|
74
|
+
if (isPrerelease)
|
|
75
|
+
return '🍼 PRE';
|
|
76
|
+
return '✅ STABLE';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 降级链路可见标注(runGuarded 契约的渲染兑现)。
|
|
80
|
+
* 为什么必须可见:降级若是静默的,Agent 就无法感知能力边界(同一 action 在
|
|
81
|
+
* 不同 gh 版本上走不同链路);一条 blockquote 注释成本一行,换链路透明。
|
|
82
|
+
*/
|
|
83
|
+
export function appendDegradedNote(value) {
|
|
84
|
+
if (value && value.degraded === true && value.degradedReason) {
|
|
85
|
+
return `\n\n> ⚠️ ${value.degradedReason}`;
|
|
86
|
+
}
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ParsedGistSummary {
|
|
2
|
+
id: string;
|
|
3
|
+
description: string;
|
|
4
|
+
filesCount: string;
|
|
5
|
+
visibility: string;
|
|
6
|
+
updatedAt: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 解析 gh gist list 的制表符/多列纯文本输出
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseGistListOutput(output: string): ParsedGistSummary[];
|
|
12
|
+
/**
|
|
13
|
+
* Gist 领域渲染器:负责 GitHub Gist 列表、代码正文、创建/删除反馈的 Markdown 格式化
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderGistMarkdown(args: any, value: any): string;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* 解析 gh gist list 的制表符/多列纯文本输出
|
|
4
|
+
*/
|
|
5
|
+
export function parseGistListOutput(output) {
|
|
6
|
+
const trimmed = (output || '').trim();
|
|
7
|
+
if (!trimmed)
|
|
8
|
+
return [];
|
|
9
|
+
const lines = trimmed.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
10
|
+
const results = [];
|
|
11
|
+
for (const line of lines) {
|
|
12
|
+
let parts = line.split('\t');
|
|
13
|
+
if (parts.length < 2) {
|
|
14
|
+
parts = line.split(/\s{2,}/);
|
|
15
|
+
}
|
|
16
|
+
if (parts.length >= 1 && parts[0].length >= 8) {
|
|
17
|
+
results.push({
|
|
18
|
+
id: parts[0].trim(),
|
|
19
|
+
description: (parts[1] || '').trim(),
|
|
20
|
+
filesCount: (parts[2] || '').trim(),
|
|
21
|
+
visibility: (parts[3] || 'secret').trim(),
|
|
22
|
+
updatedAt: (parts[4] || '').trim(),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return results;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Gist 领域渲染器:负责 GitHub Gist 列表、代码正文、创建/删除反馈的 Markdown 格式化
|
|
30
|
+
*/
|
|
31
|
+
export function renderGistMarkdown(args, value) {
|
|
32
|
+
if (!value)
|
|
33
|
+
return '无返回内容';
|
|
34
|
+
// 1. 高危操作确认拦截报告
|
|
35
|
+
if (value.status === 'AWAITING_CONFIRMATION') {
|
|
36
|
+
return [
|
|
37
|
+
`> ### ${value.warning || '⚠️ 操作确认拦截'}`,
|
|
38
|
+
`> - **影响评估**: ${value.impact || ''}`,
|
|
39
|
+
`> - **确认指引**: ${value.requiredAction || '如确认继续,请在参数中附加 \\`confirm: true\\` 重新调用。'}`,
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
if (typeof value.result === 'string')
|
|
43
|
+
return value.result;
|
|
44
|
+
// 2. create 操作卡片展示
|
|
45
|
+
if (args?.action === 'create' && value.url) {
|
|
46
|
+
const visBadge = value.public ? '🌐 Public' : '🔒 Secret (Unlisted)';
|
|
47
|
+
const descHint = value.description ? `\n> - **描述**: ${value.description}` : '';
|
|
48
|
+
return [
|
|
49
|
+
`✅ **成功创建 Gist 代码片段**:\n`,
|
|
50
|
+
`- **访问链接**: [${value.url}](${value.url})`,
|
|
51
|
+
`- **Gist ID**: \`${value.gist_id}\``,
|
|
52
|
+
`- **可见性**: ${visBadge}${descHint}`,
|
|
53
|
+
'',
|
|
54
|
+
'> ⚠️ **安全须知**: Secret Gist 属于未列出(Unlisted)公开可寻址资源,拥有链接的任何人均可访问,切勿存放私有凭证或敏感机密。',
|
|
55
|
+
].join('\n');
|
|
56
|
+
}
|
|
57
|
+
// 3. view 操作展示
|
|
58
|
+
if (args?.action === 'view') {
|
|
59
|
+
const raw = (value.content || value.rawText || '').trim();
|
|
60
|
+
const id = value.gist_id || args?.gist_id || '';
|
|
61
|
+
const fileHint = args?.filename ? ` (${args.filename})` : '';
|
|
62
|
+
return [
|
|
63
|
+
`### 📄 Gist 代码片段: \`${id}\`${fileHint}\n`,
|
|
64
|
+
'```',
|
|
65
|
+
raw || '无代码内容',
|
|
66
|
+
'```',
|
|
67
|
+
].join('\n');
|
|
68
|
+
}
|
|
69
|
+
// 4. list 操作表格展示
|
|
70
|
+
if (args?.action === 'list') {
|
|
71
|
+
const list = Array.isArray(value.items) ? value.items : [];
|
|
72
|
+
if (list.length === 0) {
|
|
73
|
+
return '📭 当前 GitHub 账号下尚未创建任何 Gist 代码片段。';
|
|
74
|
+
}
|
|
75
|
+
const rows = list.map((g) => {
|
|
76
|
+
const id = g.id ? `\`${g.id}\`` : '-';
|
|
77
|
+
const desc = sanitizeCell(g.description, 35);
|
|
78
|
+
const files = g.filesCount ? `\`${g.filesCount}\`` : '-';
|
|
79
|
+
const vis = g.visibility === 'public' ? '🌐 public' : '🔒 secret';
|
|
80
|
+
const updated = sanitizeCell(g.updatedAt, 20);
|
|
81
|
+
return `| ${id} | ${desc} | ${files} | ${vis} | ${updated} |`;
|
|
82
|
+
});
|
|
83
|
+
return [
|
|
84
|
+
`**Gist 代码片段列表 (${list.length} 个):**\n`,
|
|
85
|
+
'| Gist ID | 描述 / 摘要 | 文件数 | 可见性 | 最近更新 |',
|
|
86
|
+
'| :--- | :--- | :--- | :--- | :--- |',
|
|
87
|
+
...rows,
|
|
88
|
+
'',
|
|
89
|
+
'> 💡 **查看指南**: 调用 `github_gist(action: \'view\', gist_id: \'...\')` 可查看完整代码正文。',
|
|
90
|
+
].join('\n');
|
|
91
|
+
}
|
|
92
|
+
// 5. 消息提示(delete, edit 等)
|
|
93
|
+
if (typeof value.message === 'string') {
|
|
94
|
+
const extra = value.detail || value.output;
|
|
95
|
+
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
96
|
+
}
|
|
97
|
+
// 6. 兜底
|
|
98
|
+
return JSON.stringify(value, null, 2);
|
|
99
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { sanitizeCell, formatStateBadge } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Issue 领域渲染器:负责 Issue 列表多标签表格、单卡详情、指派人与图片附件提示
|
|
4
|
+
*/
|
|
5
|
+
export function renderIssueMarkdown(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
|
+
// 2.1 Issue 列表
|
|
22
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
23
|
+
const list = value.items.filter(Boolean);
|
|
24
|
+
if (list.length === 0)
|
|
25
|
+
return '📭 当前仓库暂无匹配的 Issue。';
|
|
26
|
+
const rows = list.map((i) => {
|
|
27
|
+
const numLink = i.url ? `[#${i.number}](${i.url})` : `#${i.number}`;
|
|
28
|
+
const badge = formatStateBadge(i.state);
|
|
29
|
+
const title = sanitizeCell(i.title, 60);
|
|
30
|
+
const labels = Array.isArray(i.labels) && i.labels.length > 0
|
|
31
|
+
? i.labels.map((l) => `\`${sanitizeCell(l.name || l, 15)}\``).join(' ')
|
|
32
|
+
: '-';
|
|
33
|
+
const author = i.author?.login ? `@${i.author.login}` : (typeof i.author === 'string' ? `@${i.author}` : '-');
|
|
34
|
+
const date = i.updatedAt ? i.updatedAt.slice(0, 10) : '-';
|
|
35
|
+
return `| ${numLink} | ${badge} | ${title} | ${labels} | ${author} | ${date} |`;
|
|
36
|
+
});
|
|
37
|
+
return [
|
|
38
|
+
`**找到 ${list.length} 个 Issue:**\n`,
|
|
39
|
+
'| 编号 | 状态 | 标题 | 标签 | 作者 | 更新日期 |',
|
|
40
|
+
'| :--- | :--- | :--- | :--- | :--- | :--- |',
|
|
41
|
+
...rows,
|
|
42
|
+
].join('\n');
|
|
43
|
+
}
|
|
44
|
+
// 2.2 Issue View 详情
|
|
45
|
+
if (args?.action === 'view' && value.number) {
|
|
46
|
+
const link = value.url ? `[#${value.number}](${value.url})` : `#${value.number}`;
|
|
47
|
+
const badge = formatStateBadge(value.state);
|
|
48
|
+
const author = value.author?.login ? `@${value.author.login}` : '-';
|
|
49
|
+
const lines = [
|
|
50
|
+
`### ${badge} ${value.title} (${link})`,
|
|
51
|
+
`- **作者**: ${author} | **更新时间**: ${value.updatedAt?.slice(0, 16) || '-'}`,
|
|
52
|
+
];
|
|
53
|
+
if (Array.isArray(value.labels) && value.labels.length > 0) {
|
|
54
|
+
lines.push(`- **标签**: ${value.labels.map((l) => `\`${l.name || l}\``).join(' ')}`);
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(value.assignees) && value.assignees.length > 0) {
|
|
57
|
+
lines.push(`- **指派人**: ${value.assignees.map((a) => `@${a.login || a}`).join(' ')}`);
|
|
58
|
+
}
|
|
59
|
+
if (value.body) {
|
|
60
|
+
lines.push('\n**议题正文:**\n```markdown\n' + sanitizeCell(value.body, 300) + '\n```');
|
|
61
|
+
}
|
|
62
|
+
// 图片附件清单(view 附带 URL 清单,view_images 按需下载查看)
|
|
63
|
+
if (Array.isArray(value.images) && value.images.length > 0) {
|
|
64
|
+
lines.push(`- **📎 图片附件 (${value.images.length}):** 调用 \`github_issue(action: 'view_images', issue_number: ${value.number})\` 可下载查看`);
|
|
65
|
+
}
|
|
66
|
+
return lines.join('\n');
|
|
67
|
+
}
|
|
68
|
+
// 兜底降级
|
|
69
|
+
return JSON.stringify(value, null, 2);
|
|
70
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Label 领域渲染器:负责 GitHub Labels 列表、创建、编辑、克隆与删除的 Markdown 格式化
|
|
4
|
+
*/
|
|
5
|
+
export function renderLabelMarkdown(args, value) {
|
|
6
|
+
if (!value)
|
|
7
|
+
return '无返回内容';
|
|
8
|
+
// 1. 高危操作确认拦截报告
|
|
9
|
+
if (value.status === 'AWAITING_CONFIRMATION') {
|
|
10
|
+
return [
|
|
11
|
+
`> ### ${value.warning || '⚠️ 操作确认拦截'}`,
|
|
12
|
+
`> - **影响评估**: ${value.impact || ''}`,
|
|
13
|
+
`> - **确认指引**: ${value.requiredAction || '如确认继续,请在参数中附加 \`confirm: true\` 重新调用。'}`,
|
|
14
|
+
].join('\n');
|
|
15
|
+
}
|
|
16
|
+
if (typeof value.result === 'string')
|
|
17
|
+
return value.result;
|
|
18
|
+
// 2. 单操作消息提示(create, edit, delete, clone 等)
|
|
19
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
20
|
+
const extra = value.detail || value.output;
|
|
21
|
+
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
22
|
+
}
|
|
23
|
+
// 3. 标签列表展示
|
|
24
|
+
if (Array.isArray(value.items)) {
|
|
25
|
+
const list = value.items.filter(Boolean);
|
|
26
|
+
if (list.length === 0) {
|
|
27
|
+
return args?.search
|
|
28
|
+
? `📭 未检索到匹配关键词 "${args.search}" 的标签。`
|
|
29
|
+
: '📭 仓库中尚未创建任何标签。';
|
|
30
|
+
}
|
|
31
|
+
const rows = list.map((item) => {
|
|
32
|
+
const name = item.name ? `\`${sanitizeCell(item.name, 40)}\`` : '-';
|
|
33
|
+
const colorRaw = item.color ? String(item.color).replace(/^#/, '') : '';
|
|
34
|
+
const color = colorRaw ? `\`#${colorRaw.toLowerCase()}\`` : '-';
|
|
35
|
+
const desc = sanitizeCell(item.description, 60);
|
|
36
|
+
return `| ${name} | ${color} | ${desc} |`;
|
|
37
|
+
});
|
|
38
|
+
const searchHint = args?.search ? ` (过滤关键词: "${args.search}")` : '';
|
|
39
|
+
return [
|
|
40
|
+
`**仓库标签列表 (${list.length} 个)${searchHint}:**\n`,
|
|
41
|
+
'| 标签 | 颜色 | 描述 |',
|
|
42
|
+
'| :--- | :--- | :--- |',
|
|
43
|
+
...rows,
|
|
44
|
+
].join('\n');
|
|
45
|
+
}
|
|
46
|
+
// 4. 兜底降级
|
|
47
|
+
return JSON.stringify(value, null, 2);
|
|
48
|
+
}
|
|
@@ -1,35 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DSH GitHub Flow - Native Markdown
|
|
2
|
+
* DSH GitHub Flow - Native Markdown 格式化渲染引擎门面入口
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 2. 节省 70%+ 的会话上下文 Token,极大加速长对话推理速度;
|
|
7
|
-
* 3. 嵌入可点击超链接与直观的状态药丸 Emoji,提升人类与模型阅读体验;
|
|
8
|
-
* 4. 严谨防御 Markdown 表格竖线破坏、缺失值回退与异常对象降级。
|
|
4
|
+
* 为保持向后兼容性与构建产物一致性,本入口统一 Re-export 全部领域渲染器
|
|
5
|
+
* 与通用徽章工具,各 Agent Tool 与自动化单测可无感引用。
|
|
9
6
|
*/
|
|
10
|
-
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
export declare function formatConflictBadge(status?: string): string;
|
|
23
|
-
/**
|
|
24
|
-
* 降级链路可见标注(runGuarded 契约的渲染兑现)。
|
|
25
|
-
* 为什么必须可见:降级若是静默的,Agent 就无法感知能力边界(同一 action 在
|
|
26
|
-
* 不同 gh 版本上走不同链路);一条 blockquote 注释成本一行,换链路透明。
|
|
27
|
-
*/
|
|
28
|
-
export declare function appendDegradedNote(value: any): string;
|
|
29
|
-
export declare function renderPrMarkdown(args: any, value: any): string;
|
|
30
|
-
export declare function renderIssueMarkdown(args: any, value: any): string;
|
|
31
|
-
export declare function renderRunMarkdown(args: any, value: any): string;
|
|
32
|
-
export declare function renderRepoMarkdown(args: any, value: any): string;
|
|
33
|
-
export declare function formatReleaseBadge(isDraft?: boolean, isPrerelease?: boolean): string;
|
|
34
|
-
export declare function renderReleaseMarkdown(args: any, value: any): string;
|
|
35
|
-
export declare function renderSkillMarkdown(args: any, value: any): string;
|
|
7
|
+
export * from './common.js';
|
|
8
|
+
export * from './pr.js';
|
|
9
|
+
export * from './issue.js';
|
|
10
|
+
export * from './run.js';
|
|
11
|
+
export * from './repo.js';
|
|
12
|
+
export * from './release.js';
|
|
13
|
+
export * from './skill.js';
|
|
14
|
+
export * from './label.js';
|
|
15
|
+
export * from './ruleset.js';
|
|
16
|
+
export * from './cache.js';
|
|
17
|
+
export * from './gist.js';
|
|
18
|
+
export * from './status.js';
|