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.
- package/README.en.md +287 -143
- package/README.md +270 -125
- package/docs/UPGRADE-NOTES.md +12 -9
- package/docs/architecture.md +126 -126
- package/lib/api/routes-actions.js +249 -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 +14 -1
- package/lib/client.js +4287 -2500
- package/lib/commands/gh.d.ts +6 -1
- package/lib/commands/gh.js +144 -71
- package/lib/executor/git-parser.d.ts +57 -0
- package/lib/executor/git-parser.js +255 -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 +70 -48
- package/lib/executor.js +371 -356
- package/lib/git-watcher.d.ts +36 -0
- package/lib/git-watcher.js +91 -0
- package/lib/main.js +37 -3
- package/lib/renderers/agent-task.d.ts +16 -0
- package/lib/renderers/agent-task.js +107 -0
- package/lib/renderers/cache.d.ts +8 -0
- package/lib/renderers/cache.js +66 -0
- package/lib/renderers/codespace.d.ts +16 -0
- package/lib/renderers/codespace.js +103 -0
- package/lib/renderers/common.d.ts +24 -0
- package/lib/renderers/common.js +88 -0
- package/lib/renderers/discussion.d.ts +16 -0
- package/lib/renderers/discussion.js +101 -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 +19 -32
- package/lib/renderers/markdown.js +19 -624
- package/lib/renderers/pr.d.ts +4 -0
- package/lib/renderers/pr.js +107 -0
- package/lib/renderers/project.d.ts +20 -0
- package/lib/renderers/project.js +128 -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 +34 -0
- package/lib/renderers/skill.js +241 -0
- package/lib/renderers/status.d.ts +28 -0
- package/lib/renderers/status.js +176 -0
- package/lib/telemetry.d.ts +70 -0
- package/lib/telemetry.js +161 -0
- package/lib/tools/agent-task.d.ts +54 -0
- package/lib/tools/agent-task.js +135 -0
- package/lib/tools/cache.d.ts +64 -0
- package/lib/tools/cache.js +186 -0
- package/lib/tools/codespace.d.ts +51 -0
- package/lib/tools/codespace.js +154 -0
- package/lib/tools/discussion.d.ts +70 -0
- package/lib/tools/discussion.js +195 -0
- package/lib/tools/gist.d.ts +155 -0
- package/lib/tools/gist.js +190 -0
- package/lib/tools/help.js +214 -13
- 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/project.d.ts +80 -0
- package/lib/tools/project.js +241 -0
- package/lib/tools/repo.js +13 -1
- package/lib/tools/ruleset.d.ts +80 -0
- package/lib/tools/ruleset.js +177 -0
- package/lib/tools/skill.d.ts +24 -1
- package/lib/tools/skill.js +115 -5
- package/lib/tools/status.d.ts +40 -0
- package/lib/tools/status.js +58 -0
- package/lib/types.d.ts +44 -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 +219 -0
- package/lib/ui/help-modal.d.ts +15 -0
- package/lib/ui/help-modal.js +202 -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 +35 -0
- package/lib/ui/storage.js +158 -0
- package/lib/ui/styles.d.ts +6 -0
- package/lib/ui/styles.js +1544 -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 +222 -0
- package/lib/ui/utils.d.ts +45 -0
- package/lib/ui/utils.js +226 -0
- package/lib/ui/workspace-panel.d.ts +4 -0
- package/lib/ui/workspace-panel.js +489 -0
- package/lib/upgrade-manager.d.ts +5 -1
- package/lib/upgrade-manager.js +24 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill 领域渲染器:负责 Agent Skills 跨仓检索结果、已安装列表与预览说明
|
|
3
|
+
*/
|
|
4
|
+
export declare function renderSkillMarkdown(args: any, value: any): string;
|
|
5
|
+
export interface SkillDiagnosticItem {
|
|
6
|
+
type: 'error' | 'warning' | 'fixed';
|
|
7
|
+
skillName: string;
|
|
8
|
+
message: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SkillPublishReport {
|
|
11
|
+
mode: 'dry-run' | 'fix' | 'publish';
|
|
12
|
+
success: boolean;
|
|
13
|
+
directory?: string;
|
|
14
|
+
items: SkillDiagnosticItem[];
|
|
15
|
+
repoWarnings: string[];
|
|
16
|
+
summary: string;
|
|
17
|
+
tag?: string;
|
|
18
|
+
rawText?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 解析 gh skill publish 命令输出的 TSV 诊断行与状态汇总
|
|
22
|
+
*
|
|
23
|
+
* 核心设计理由与约束:
|
|
24
|
+
* 1. gh skill publish 命令无论成功还是失败,均采用制表符 `\t` 分隔的 TSV 纯文本行输出;
|
|
25
|
+
* 2. 诊断行格式:`error\t<skill>\t<msg>`、`warning\t<skill>\t<msg>`、`fixed\t<skill>\t<msg>` 或 `warning\t\t<repo_rule>`;
|
|
26
|
+
* 3. 校验失败时 exitCode 为 1,但其 stdout 中携带精准的错误项列表,需提取后结构化呈现,绝不能当作盲目异常吞没;
|
|
27
|
+
* 4. 若未匹配到有效技能或遇到无技能目录,从 stderr 提取根因提示。
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseSkillPublishOutput(rawStdout: string, rawStderr: string, exitOk: boolean, mode: 'dry-run' | 'fix' | 'publish', directory?: string, tag?: string): SkillPublishReport;
|
|
30
|
+
/**
|
|
31
|
+
* Skill Publish 结构化 Markdown 渲染器
|
|
32
|
+
* 将体检报告、自动修复明细与正式发布结果转化为紧凑、清晰的表格与指引
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderSkillPublishMarkdown(report: SkillPublishReport): string;
|
|
@@ -0,0 +1,241 @@
|
|
|
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
|
+
// 6.0 特化 action 处理(update)
|
|
18
|
+
if (args?.action === 'update' || value?.action === 'update') {
|
|
19
|
+
const extra = value.output || value.detail;
|
|
20
|
+
return `### 🔄 ${value.message || 'Agent Skills 更新结果'}\n` + (extra ? `\`\`\`text\n${String(extra).trim()}\n\`\`\`` : '');
|
|
21
|
+
}
|
|
22
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
23
|
+
const extra = value.detail || value.output;
|
|
24
|
+
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
25
|
+
}
|
|
26
|
+
// 6.1 Skill 检索列表
|
|
27
|
+
if (Array.isArray(value.items) && args?.action === 'search') {
|
|
28
|
+
const list = value.items.filter(Boolean);
|
|
29
|
+
if (list.length === 0)
|
|
30
|
+
return '📭 未检索到匹配的 Agent Skill。';
|
|
31
|
+
const rows = list.map((s) => {
|
|
32
|
+
const name = s.skillName ? `\`${s.skillName}\`` : '-';
|
|
33
|
+
const repo = s.repo ? `[${s.repo}](https://github.com/${s.repo})` : '-';
|
|
34
|
+
const stars = typeof s.stars === 'number' ? `⭐ ${s.stars}` : '-';
|
|
35
|
+
const desc = sanitizeCell(s.description, 50);
|
|
36
|
+
return `| ${name} | ${repo} | ${stars} | ${desc} |`;
|
|
37
|
+
});
|
|
38
|
+
return [
|
|
39
|
+
`**Skills 检索结果 (${list.length} 个):**\n`,
|
|
40
|
+
'| Skill | 仓库 | Stars | 描述 |',
|
|
41
|
+
'| :--- | :--- | :--- | :--- |',
|
|
42
|
+
...rows,
|
|
43
|
+
].join('\n');
|
|
44
|
+
}
|
|
45
|
+
// 6.2 已安装 Skills 列表
|
|
46
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
47
|
+
const list = value.items.filter(Boolean);
|
|
48
|
+
if (list.length === 0)
|
|
49
|
+
return '📭 当前环境尚未安装任何 Agent Skill。';
|
|
50
|
+
const rows = list.map((s) => {
|
|
51
|
+
const name = s.skillName ? `\`${s.skillName}\`` : '-';
|
|
52
|
+
const ver = s.version ? String(s.version) : '-';
|
|
53
|
+
const scope = s.scope ? String(s.scope) : '-';
|
|
54
|
+
const source = s.sourceURL ? `[来源](${s.sourceURL})` : (s.path ? `\`${sanitizeCell(s.path, 40)}\`` : '-');
|
|
55
|
+
return `| ${name} | ${ver} | ${scope} | ${source} |`;
|
|
56
|
+
});
|
|
57
|
+
return [
|
|
58
|
+
`**已安装 ${list.length} 个 Agent Skill:**\n`,
|
|
59
|
+
'| Skill | 版本 | 范围 | 来源 |',
|
|
60
|
+
'| :--- | :--- | :--- | :--- |',
|
|
61
|
+
...rows,
|
|
62
|
+
].join('\n');
|
|
63
|
+
}
|
|
64
|
+
// 6.3 Skill 校验与发布报告
|
|
65
|
+
if (args?.action === 'publish' || value?.mode === 'dry-run' || value?.mode === 'fix' || value?.mode === 'publish') {
|
|
66
|
+
return renderSkillPublishMarkdown(value);
|
|
67
|
+
}
|
|
68
|
+
// 兜底降级
|
|
69
|
+
return JSON.stringify(value, null, 2);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 解析 gh skill publish 命令输出的 TSV 诊断行与状态汇总
|
|
73
|
+
*
|
|
74
|
+
* 核心设计理由与约束:
|
|
75
|
+
* 1. gh skill publish 命令无论成功还是失败,均采用制表符 `\t` 分隔的 TSV 纯文本行输出;
|
|
76
|
+
* 2. 诊断行格式:`error\t<skill>\t<msg>`、`warning\t<skill>\t<msg>`、`fixed\t<skill>\t<msg>` 或 `warning\t\t<repo_rule>`;
|
|
77
|
+
* 3. 校验失败时 exitCode 为 1,但其 stdout 中携带精准的错误项列表,需提取后结构化呈现,绝不能当作盲目异常吞没;
|
|
78
|
+
* 4. 若未匹配到有效技能或遇到无技能目录,从 stderr 提取根因提示。
|
|
79
|
+
*/
|
|
80
|
+
export function parseSkillPublishOutput(rawStdout, rawStderr, exitOk, mode, directory, tag) {
|
|
81
|
+
const items = [];
|
|
82
|
+
const repoWarnings = [];
|
|
83
|
+
let summary = '';
|
|
84
|
+
const combined = `${rawStdout || ''}\n${rawStderr || ''}`;
|
|
85
|
+
const lines = combined.split(/\r?\n/).map(l => l.trimEnd()).filter(Boolean);
|
|
86
|
+
for (const line of lines) {
|
|
87
|
+
// 匹配 TSV 诊断行:error/warning/fixed + \t + skillName + \t + message
|
|
88
|
+
const tsvMatch = line.match(/^(error|warning|fixed)\t([^\t]*)\t(.*)$/i);
|
|
89
|
+
if (tsvMatch) {
|
|
90
|
+
const type = tsvMatch[1].toLowerCase();
|
|
91
|
+
const skillName = tsvMatch[2].trim();
|
|
92
|
+
const message = tsvMatch[3].trim();
|
|
93
|
+
if (!skillName) {
|
|
94
|
+
// 技能名为空通常是仓库级规则警告(如 ruleset 缺少 tag 保护)
|
|
95
|
+
repoWarnings.push(message);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
items.push({ type, skillName, message });
|
|
99
|
+
}
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// 汇总与特征行识别
|
|
103
|
+
if (/validation failed with \d+ error\(s\)/i.test(line)) {
|
|
104
|
+
summary = line.trim();
|
|
105
|
+
}
|
|
106
|
+
else if (/Dry run complete/i.test(line)) {
|
|
107
|
+
if (!summary)
|
|
108
|
+
summary = line.trim();
|
|
109
|
+
}
|
|
110
|
+
else if (/Fixed \d+ file\(s\)/i.test(line)) {
|
|
111
|
+
summary = line.trim();
|
|
112
|
+
}
|
|
113
|
+
else if (/no skills found in/i.test(line)) {
|
|
114
|
+
summary = line.trim();
|
|
115
|
+
}
|
|
116
|
+
else if (/Released/i.test(line) && mode === 'publish') {
|
|
117
|
+
if (!summary)
|
|
118
|
+
summary = line.trim();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// 判定最终业务成功态:
|
|
122
|
+
// 1. 若 items 中存在任一 error,则判定为失败;
|
|
123
|
+
// 2. 若 exitOk 为 false,即使没有 TSV 行也判定为失败;
|
|
124
|
+
// 3. dry-run 模式下无 error 且正常跑完判定为成功;
|
|
125
|
+
// 4. 若没有找到 skill(summary 包含 no skills found),视作失败并在 items 中追加说明项。
|
|
126
|
+
const hasErrors = items.some(i => i.type === 'error');
|
|
127
|
+
const noSkillsFound = summary.includes('no skills found in');
|
|
128
|
+
let success = exitOk && !hasErrors && !noSkillsFound;
|
|
129
|
+
if (noSkillsFound && items.length === 0) {
|
|
130
|
+
items.push({
|
|
131
|
+
type: 'error',
|
|
132
|
+
skillName: '-',
|
|
133
|
+
message: summary || '在目标目录下未检索到符合规范的 Agent Skill(需满足 skills/*/SKILL.md 等路径约束)',
|
|
134
|
+
});
|
|
135
|
+
success = false;
|
|
136
|
+
}
|
|
137
|
+
else if (!exitOk && items.length === 0 && rawStderr) {
|
|
138
|
+
// 其它非零退出且无 TSV 诊断行的情况,将 stderr 作为通用错误提示
|
|
139
|
+
items.push({
|
|
140
|
+
type: 'error',
|
|
141
|
+
skillName: '-',
|
|
142
|
+
message: rawStderr.trim(),
|
|
143
|
+
});
|
|
144
|
+
success = false;
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
mode,
|
|
148
|
+
success,
|
|
149
|
+
directory,
|
|
150
|
+
items,
|
|
151
|
+
repoWarnings,
|
|
152
|
+
summary,
|
|
153
|
+
tag,
|
|
154
|
+
rawText: rawStdout.trim() || rawStderr.trim(),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Skill Publish 结构化 Markdown 渲染器
|
|
159
|
+
* 将体检报告、自动修复明细与正式发布结果转化为紧凑、清晰的表格与指引
|
|
160
|
+
*/
|
|
161
|
+
export function renderSkillPublishMarkdown(report) {
|
|
162
|
+
if (!report)
|
|
163
|
+
return '无返回内容';
|
|
164
|
+
if (report.status === 'AWAITING_CONFIRMATION') {
|
|
165
|
+
return [
|
|
166
|
+
`> ### ${report.warning || '⚠️ 操作确认拦截'}`,
|
|
167
|
+
`> - **影响评估**: ${report.impact || ''}`,
|
|
168
|
+
`> - **确认指引**: ${report.requiredAction || ''}`,
|
|
169
|
+
].join('\n');
|
|
170
|
+
}
|
|
171
|
+
const sections = [];
|
|
172
|
+
const dirHint = report.directory ? ` (目录: \`${report.directory}\`)` : '';
|
|
173
|
+
if (report.mode === 'dry-run') {
|
|
174
|
+
const errorCount = report.items.filter(i => i.type === 'error').length;
|
|
175
|
+
const warningCount = report.items.filter(i => i.type === 'warning').length;
|
|
176
|
+
if (report.success && errorCount === 0) {
|
|
177
|
+
sections.push(`### ✅ Agent Skills 合规校验通过!${dirHint}\n`);
|
|
178
|
+
sections.push('受检 Skill 均符合 agentskills.io 规范(命名规范、目录匹配、必需 frontmatter 字段均通过)。');
|
|
179
|
+
if (warningCount > 0) {
|
|
180
|
+
sections.push(`\n**建议优化项 (${warningCount} 项):**`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
sections.push(`### ⚠️ Agent Skills 校验发现问题 (${errorCount} 项错误${warningCount > 0 ? `,${warningCount} 项警告` : ''})${dirHint}\n`);
|
|
185
|
+
}
|
|
186
|
+
if (report.items.length > 0) {
|
|
187
|
+
const rows = report.items.map(item => {
|
|
188
|
+
let badge = 'ℹ️ 提示';
|
|
189
|
+
if (item.type === 'error')
|
|
190
|
+
badge = '❌ 错误';
|
|
191
|
+
else if (item.type === 'warning')
|
|
192
|
+
badge = '⚠️ 建议';
|
|
193
|
+
else if (item.type === 'fixed')
|
|
194
|
+
badge = '🛠️ 已修复';
|
|
195
|
+
return `| ${badge} | \`${sanitizeCell(item.skillName, 30)}\` | ${sanitizeCell(item.message, 80)} |`;
|
|
196
|
+
});
|
|
197
|
+
sections.push('| 级别 | Skill 名称 | 问题详情与指引 |\n| :--- | :--- | :--- |\n' + rows.join('\n'));
|
|
198
|
+
}
|
|
199
|
+
if (report.repoWarnings && report.repoWarnings.length > 0) {
|
|
200
|
+
sections.push('\n**仓库策略提醒:**\n' + report.repoWarnings.map(w => `- 💡 ${w}`).join('\n'));
|
|
201
|
+
}
|
|
202
|
+
if (report.success && errorCount === 0) {
|
|
203
|
+
sections.push('\n> 💡 **正式发版指引**: 若需创建 GitHub Release 正式发布技能,请指定版本 Tag 并附带 `confirm: true`:\n> `github_skill(action: "publish", tag: "v1.0.0", dry_run: false, confirm: true)`');
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
sections.push('\n> 🔍 **修复提示**: 若存在安装元数据残留,可先调用 `github_skill(action: "publish", fix: true)` 自动剥离清洗。');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (report.mode === 'fix') {
|
|
210
|
+
const fixedItems = report.items.filter(i => i.type === 'fixed');
|
|
211
|
+
sections.push(`### 🛠️ Agent Skills 元数据自动修复${report.success ? '完成' : '结果'}${dirHint}\n`);
|
|
212
|
+
if (fixedItems.length > 0) {
|
|
213
|
+
sections.push(`已自动清洗 **${fixedItems.length}** 处技能文件中的安装残留元数据(如 \`metadata.github-*\`):\n`);
|
|
214
|
+
const rows = fixedItems.map(item => `| \`${sanitizeCell(item.skillName, 30)}\` | ${sanitizeCell(item.message, 80)} |`);
|
|
215
|
+
sections.push('| Skill 名称 | 修复操作 |\n| :--- | :--- |\n' + rows.join('\n'));
|
|
216
|
+
sections.push('\n> 📌 **后续步骤**: 请使用 `git diff` 审查本地文件变更,提交 Git 后再重新运行合规体检或发布。');
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
sections.push(report.summary || '未检测到需要修复的元数据残留。');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else if (report.mode === 'publish') {
|
|
223
|
+
if (report.success) {
|
|
224
|
+
sections.push(`### 🚀 Agent Skill 发布成功!${dirHint}\n`);
|
|
225
|
+
sections.push(`- **版本 Tag**: \`${report.tag || '-'}\``);
|
|
226
|
+
sections.push('- **发布状态**: 已自动附加 `agent-skills` 仓库 Topic,并创建了关联 GitHub Release。');
|
|
227
|
+
if (report.summary)
|
|
228
|
+
sections.push(`\n${report.summary}`);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
sections.push(`### ❌ Agent Skill 发布失败${dirHint}\n`);
|
|
232
|
+
if (report.summary)
|
|
233
|
+
sections.push(`- **原因**: ${report.summary}`);
|
|
234
|
+
if (report.items.length > 0) {
|
|
235
|
+
const rows = report.items.map(item => `| ${item.type} | \`${sanitizeCell(item.skillName, 30)}\` | ${sanitizeCell(item.message, 80)} |`);
|
|
236
|
+
sections.push('\n| 级别 | Skill 名称 | 详情 |\n| :--- | :--- | :--- |\n' + rows.join('\n'));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return sections.join('\n');
|
|
241
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { TelemetrySummary } from '../telemetry.js';
|
|
2
|
+
export interface StatusSectionItem {
|
|
3
|
+
repo?: string;
|
|
4
|
+
number?: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
detail?: string;
|
|
7
|
+
raw: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ParsedStatusReport {
|
|
10
|
+
assignedIssues: StatusSectionItem[];
|
|
11
|
+
assignedPrs: StatusSectionItem[];
|
|
12
|
+
reviewRequests: StatusSectionItem[];
|
|
13
|
+
mentions: StatusSectionItem[];
|
|
14
|
+
activity: StatusSectionItem[];
|
|
15
|
+
rawText: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 解析 gh status 的输出,提取待办 Issue、待办 PR、评审请求、提及与近期动态
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseStatusOutput(output: string): ParsedStatusReport;
|
|
21
|
+
/**
|
|
22
|
+
* Status 领域渲染器:负责用户跨仓待办大盘的 Markdown 格式化
|
|
23
|
+
*/
|
|
24
|
+
export declare function renderStatusMarkdown(args: any, value: any): string;
|
|
25
|
+
/**
|
|
26
|
+
* 渲染 DSH GitHub 工具链健康度与降级率审计报表
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderHealthAuditMarkdown(summary: TelemetrySummary): string;
|
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
if (value?.healthScore !== undefined || value?.degradationRate !== undefined || args?.action === 'health') {
|
|
94
|
+
return renderHealthAuditMarkdown(value);
|
|
95
|
+
}
|
|
96
|
+
const report = value.parsed || parseStatusOutput(value.rawText || value.output || '');
|
|
97
|
+
const renderSection = (title, icon, items, emptyText) => {
|
|
98
|
+
if (!items || items.length === 0) {
|
|
99
|
+
return `#### ${icon} ${title}\n*${emptyText}*\n`;
|
|
100
|
+
}
|
|
101
|
+
const rows = items.map((it) => {
|
|
102
|
+
const repoRef = it.repo && it.number ? `\`${it.repo}#${it.number}\`` : '-';
|
|
103
|
+
const titleText = sanitizeCell(it.title || it.raw, 40);
|
|
104
|
+
const detailText = sanitizeCell(it.detail, 40);
|
|
105
|
+
return `| ${repoRef} | ${titleText} | ${detailText} |`;
|
|
106
|
+
});
|
|
107
|
+
return [
|
|
108
|
+
`#### ${icon} ${title} (${items.length})\n`,
|
|
109
|
+
'| 仓库 & 编号 | 标题 / 动作 | 详情 / 摘要 |',
|
|
110
|
+
'| :--- | :--- | :--- |',
|
|
111
|
+
...rows,
|
|
112
|
+
'',
|
|
113
|
+
].join('\n');
|
|
114
|
+
};
|
|
115
|
+
const sections = [
|
|
116
|
+
renderSection('指派给我的 Issue (Assigned Issues)', '📌', report.assignedIssues, '🟢 暂无分配给你的未决 Issue'),
|
|
117
|
+
renderSection('指派给我的 PR (Assigned Pull Requests)', '🔀', report.assignedPrs, '🟢 暂无分配给你的未决 PR'),
|
|
118
|
+
renderSection('等待我评审的 PR (Review Requests)', '👀', report.reviewRequests, '🟢 暂无待你评审的 PR'),
|
|
119
|
+
renderSection('提及我的动态 (Mentions)', '🔔', report.mentions, '🟢 暂无与你相关的近期提及'),
|
|
120
|
+
renderSection('订阅仓库近期动态 (Repository Activity)', '⚡', report.activity, '暂无近期仓库动态'),
|
|
121
|
+
];
|
|
122
|
+
return [
|
|
123
|
+
'### 🧭 GitHub 个人待办与跨仓动态聚合看板\n',
|
|
124
|
+
...sections,
|
|
125
|
+
'> 💡 **待办操作建议**: 可通过 `github_issue(action: \'view\')` 或 `github_pr(action: \'checkout\')` 快速检视对应议题并展开工作。',
|
|
126
|
+
].join('\n');
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 渲染 DSH GitHub 工具链健康度与降级率审计报表
|
|
130
|
+
*/
|
|
131
|
+
export function renderHealthAuditMarkdown(summary) {
|
|
132
|
+
if (!summary)
|
|
133
|
+
return '无遥测健康度数据';
|
|
134
|
+
let badge = '🟢 极佳';
|
|
135
|
+
if (summary.statusLevel === 'NEEDS_ATTENTION') {
|
|
136
|
+
badge = '🔴 需关注';
|
|
137
|
+
}
|
|
138
|
+
else if (summary.statusLevel === 'GOOD') {
|
|
139
|
+
badge = '🟡 良好';
|
|
140
|
+
}
|
|
141
|
+
const successRate = summary.totalCalls > 0
|
|
142
|
+
? `${((summary.successCount / summary.totalCalls) * 100).toFixed(1)}%`
|
|
143
|
+
: '100%';
|
|
144
|
+
const lines = [
|
|
145
|
+
'### 🩺 DSH GitHub 工具链健康度与降级率审计报表\n',
|
|
146
|
+
`- **综合健康分**: ${badge} (${summary.healthScore} / 100 分)`,
|
|
147
|
+
`- **统计样本量**: 最近 ${summary.totalCalls} 次 Agent 工具调用`,
|
|
148
|
+
`- **API 降级率**: \`${summary.degradationRate}%\` (${summary.degradedCount} 次触发降级链路)`,
|
|
149
|
+
`- **安全防御拦截**: ${summary.confirmBlockedCount + summary.scopeBlockedCount} 次 (含 ${summary.scopeBlockedCount} 次权限缺失,${summary.confirmBlockedCount} 次确认防线)`,
|
|
150
|
+
`- **执行成功率**: \`${successRate}\` (${summary.successCount} 成功, ${summary.failureCount} 失败)`,
|
|
151
|
+
];
|
|
152
|
+
// 工具调用分布表
|
|
153
|
+
const toolNames = Object.keys(summary.toolStats || {});
|
|
154
|
+
if (toolNames.length > 0) {
|
|
155
|
+
const sorted = toolNames.sort((a, b) => summary.toolStats[b].total - summary.toolStats[a].total);
|
|
156
|
+
const rows = sorted.map((name) => {
|
|
157
|
+
const s = summary.toolStats[name];
|
|
158
|
+
const rate = s.total > 0 ? `${((s.success / s.total) * 100).toFixed(0)}%` : '100%';
|
|
159
|
+
let toolBadge = '🟢 正常';
|
|
160
|
+
if (s.degraded > 0)
|
|
161
|
+
toolBadge = '🟡 存在降级';
|
|
162
|
+
if (s.failed > 0 && s.success === 0)
|
|
163
|
+
toolBadge = '🔴 异常';
|
|
164
|
+
return `| \`${name}\` | ${s.total} | ${rate} | ${s.degraded} | ${toolBadge} |`;
|
|
165
|
+
});
|
|
166
|
+
lines.push('\n**核心工具调用分布:**', '| 工具名称 | 调用次数 | 成功率 | 降级次数 | 状态 |', '| :--- | :---: | :---: | :---: | :--- |', ...rows);
|
|
167
|
+
}
|
|
168
|
+
// 自愈优化建议
|
|
169
|
+
if (Array.isArray(summary.recommendations) && summary.recommendations.length > 0) {
|
|
170
|
+
lines.push('\n> 💡 **优化与自愈建议:**');
|
|
171
|
+
summary.recommendations.forEach((rec) => {
|
|
172
|
+
lines.push(`> - ${rec}`);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return lines.join('\n');
|
|
176
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH GitHub Flow - 运行时遥测审计与降级率收集器
|
|
3
|
+
*
|
|
4
|
+
* 遵循 /dsh-plugin-dev-skill 规范,利用 Cordis 的 tools/result 事件总线
|
|
5
|
+
* 建立内存中固定容量的轻量无锁环形队列,实现零外部数据库依赖、常驻内存严格 <200KB
|
|
6
|
+
* 的工业级运行时可观测性,量化分析 18 大工具矩阵的 API 降级率与健康度指标。
|
|
7
|
+
*/
|
|
8
|
+
export interface TelemetryRecord {
|
|
9
|
+
id: string;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
toolName: string;
|
|
12
|
+
action?: string;
|
|
13
|
+
durationMs?: number;
|
|
14
|
+
success: boolean;
|
|
15
|
+
degraded: boolean;
|
|
16
|
+
scopeBlocked: boolean;
|
|
17
|
+
confirmBlocked: boolean;
|
|
18
|
+
errorType?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ToolUsageStat {
|
|
21
|
+
total: number;
|
|
22
|
+
success: number;
|
|
23
|
+
degraded: number;
|
|
24
|
+
failed: number;
|
|
25
|
+
blocked: number;
|
|
26
|
+
}
|
|
27
|
+
export interface TelemetrySummary {
|
|
28
|
+
totalCalls: number;
|
|
29
|
+
successCount: number;
|
|
30
|
+
failureCount: number;
|
|
31
|
+
degradedCount: number;
|
|
32
|
+
degradationRate: number;
|
|
33
|
+
scopeBlockedCount: number;
|
|
34
|
+
confirmBlockedCount: number;
|
|
35
|
+
healthScore: number;
|
|
36
|
+
statusLevel: 'EXCELLENT' | 'GOOD' | 'NEEDS_ATTENTION';
|
|
37
|
+
toolStats: Record<string, ToolUsageStat>;
|
|
38
|
+
recommendations: string[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 内存固定容量环形队列(无锁、确定性内存上限)
|
|
42
|
+
*/
|
|
43
|
+
export declare class TelemetryRingBuffer {
|
|
44
|
+
private buffer;
|
|
45
|
+
private readonly capacity;
|
|
46
|
+
constructor(capacity?: number);
|
|
47
|
+
push(record: TelemetryRecord): void;
|
|
48
|
+
getAll(): TelemetryRecord[];
|
|
49
|
+
size(): number;
|
|
50
|
+
clear(): void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 遥测收集与健康分析引擎
|
|
54
|
+
*/
|
|
55
|
+
export declare class TelemetryCollector {
|
|
56
|
+
private ringBuffer;
|
|
57
|
+
private callCounter;
|
|
58
|
+
constructor(capacity?: number);
|
|
59
|
+
/**
|
|
60
|
+
* 记录一次工具调用结果(Fail-Open 保护,绝不阻滞主流程)
|
|
61
|
+
*/
|
|
62
|
+
recordCall(name: string, args: any, result: any, durationMs?: number): void;
|
|
63
|
+
/**
|
|
64
|
+
* 生成工具链健康度与降级率统计大盘
|
|
65
|
+
*/
|
|
66
|
+
getSummary(): TelemetrySummary;
|
|
67
|
+
clear(): void;
|
|
68
|
+
}
|
|
69
|
+
/** 全局单例收集器实例 */
|
|
70
|
+
export declare const defaultTelemetryCollector: TelemetryCollector;
|