dsh-github-flow 0.4.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 +75 -8
- package/README.md +75 -8
- package/docs/UPGRADE-NOTES.md +9 -12
- package/docs/architecture.md +39 -21
- package/lib/api/routes-actions.js +132 -0
- package/lib/client.d.ts +4 -3
- package/lib/client.js +1436 -80
- package/lib/commands/gh.d.ts +6 -1
- package/lib/commands/gh.js +144 -71
- package/lib/executor/git-parser.d.ts +33 -1
- package/lib/executor/git-parser.js +116 -4
- package/lib/executor.d.ts +27 -2
- package/lib/executor.js +189 -5
- package/lib/main.js +28 -4
- package/lib/renderers/agent-task.d.ts +16 -0
- package/lib/renderers/agent-task.js +107 -0
- package/lib/renderers/codespace.d.ts +16 -0
- package/lib/renderers/codespace.js +103 -0
- package/lib/renderers/discussion.d.ts +16 -0
- package/lib/renderers/discussion.js +101 -0
- package/lib/renderers/markdown.d.ts +4 -0
- package/lib/renderers/markdown.js +4 -0
- package/lib/renderers/project.d.ts +20 -0
- package/lib/renderers/project.js +128 -0
- package/lib/renderers/skill.d.ts +30 -0
- package/lib/renderers/skill.js +180 -0
- package/lib/renderers/status.d.ts +5 -0
- package/lib/renderers/status.js +52 -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/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/help.js +109 -9
- 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/skill.d.ts +24 -1
- package/lib/tools/skill.js +115 -5
- package/lib/tools/status.d.ts +3 -2
- package/lib/tools/status.js +9 -4
- package/lib/types.d.ts +5 -0
- package/lib/ui/commit-modal.js +144 -14
- package/lib/ui/help-modal.d.ts +15 -0
- package/lib/ui/help-modal.js +202 -0
- package/lib/ui/storage.d.ts +9 -0
- package/lib/ui/storage.js +24 -0
- package/lib/ui/styles.d.ts +1 -1
- package/lib/ui/styles.js +259 -0
- package/lib/ui/sync-settings-modal.js +1 -1
- package/lib/ui/upgrade-card.js +39 -14
- package/lib/ui/utils.d.ts +35 -0
- package/lib/ui/utils.js +160 -0
- package/lib/ui/workspace-panel.js +30 -5
- 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,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 渲染 GitHub OAuth Scope 权限缺失自愈引导卡片
|
|
3
|
+
*/
|
|
4
|
+
export declare function renderScopeRequiredMarkdown(value: any): string;
|
|
5
|
+
/**
|
|
6
|
+
* 渲染 Projects 列表表格
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderProjectList(items: any[]): string;
|
|
9
|
+
/**
|
|
10
|
+
* 渲染单个 Project 详情卡片
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderProjectView(p: any): string;
|
|
13
|
+
/**
|
|
14
|
+
* 渲染 Project Items 看板条目列表
|
|
15
|
+
*/
|
|
16
|
+
export declare function renderProjectItemList(items: any[]): string;
|
|
17
|
+
/**
|
|
18
|
+
* Project 领域综合渲染入口
|
|
19
|
+
*/
|
|
20
|
+
export declare function renderProjectMarkdown(args: any, value: any): string;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* 渲染 GitHub OAuth Scope 权限缺失自愈引导卡片
|
|
4
|
+
*/
|
|
5
|
+
export function renderScopeRequiredMarkdown(value) {
|
|
6
|
+
const scope = value.scope || '相关权限';
|
|
7
|
+
const command = value.command || `gh auth refresh -s ${scope}`;
|
|
8
|
+
const toolName = value.tool ? `「${value.tool}」` : '';
|
|
9
|
+
return [
|
|
10
|
+
`> ### 🔑 需要补充 GitHub 权限授权 (Token Scope)`,
|
|
11
|
+
`> - **缺少权限**: \`${scope}\``,
|
|
12
|
+
`> - **影响工具**: ${toolName || '当前操作'}`,
|
|
13
|
+
`> - **自愈指引**: 请在宿主终端中执行以下单行命令完成授权:`,
|
|
14
|
+
`> \`\`\`bash`,
|
|
15
|
+
`> ${command}`,
|
|
16
|
+
`> \`\`\``,
|
|
17
|
+
`> - **说明**: 授权完成后无需重启插件,直接重新调用当前工具即可正常执行。`,
|
|
18
|
+
].join('\n');
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 渲染 Projects 列表表格
|
|
22
|
+
*/
|
|
23
|
+
export function renderProjectList(items) {
|
|
24
|
+
const list = items.filter(Boolean);
|
|
25
|
+
if (list.length === 0) {
|
|
26
|
+
return '📭 暂无项目规划看板 (Projects)。';
|
|
27
|
+
}
|
|
28
|
+
const rows = list.map((p) => {
|
|
29
|
+
const num = p.number ? `#${p.number}` : '-';
|
|
30
|
+
const link = p.url ? `[${num}](${p.url})` : num;
|
|
31
|
+
const title = sanitizeCell(p.title || '(无标题)', 40);
|
|
32
|
+
const stateBadge = p.closed ? '🟣 CLOSED' : '🔵 OPEN';
|
|
33
|
+
const visibility = p.public ? '🌐 Public' : '🔒 Private';
|
|
34
|
+
const desc = sanitizeCell(p.shortDescription || p.readme || '-', 35);
|
|
35
|
+
return `| ${link} | ${title} | ${stateBadge} | ${visibility} | ${desc} |`;
|
|
36
|
+
});
|
|
37
|
+
return [
|
|
38
|
+
`**项目看板列表 (${list.length} 个):**\n`,
|
|
39
|
+
'| 编号 | 项目标题 | 状态 | 可见性 | 描述 |',
|
|
40
|
+
'| :--- | :--- | :--- | :--- | :--- |',
|
|
41
|
+
...rows,
|
|
42
|
+
].join('\n');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 渲染单个 Project 详情卡片
|
|
46
|
+
*/
|
|
47
|
+
export function renderProjectView(p) {
|
|
48
|
+
if (!p || typeof p !== 'object') {
|
|
49
|
+
return '无项目详情数据';
|
|
50
|
+
}
|
|
51
|
+
const num = p.number ? `#${p.number}` : '';
|
|
52
|
+
const title = p.title || '(无标题)';
|
|
53
|
+
const stateBadge = p.closed ? '🟣 CLOSED' : '🔵 OPEN';
|
|
54
|
+
const visibility = p.public ? '🌐 Public' : '🔒 Private';
|
|
55
|
+
const link = p.url ? `[打开项目网页](${p.url})` : '';
|
|
56
|
+
const lines = [
|
|
57
|
+
`### 📋 Project ${num}: ${title}\n`,
|
|
58
|
+
`- **状态**: ${stateBadge}`,
|
|
59
|
+
`- **可见性**: ${visibility}`,
|
|
60
|
+
`- **项目 ID**: \`${p.id || '-'}\``,
|
|
61
|
+
];
|
|
62
|
+
if (link)
|
|
63
|
+
lines.push(`- **项目链接**: ${link}`);
|
|
64
|
+
if (p.shortDescription)
|
|
65
|
+
lines.push(`- **简述**: ${p.shortDescription}`);
|
|
66
|
+
if (p.readme) {
|
|
67
|
+
lines.push('\n**项目 README 说明:**', '---', p.readme.trim(), '---');
|
|
68
|
+
}
|
|
69
|
+
return lines.join('\n');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 渲染 Project Items 看板条目列表
|
|
73
|
+
*/
|
|
74
|
+
export function renderProjectItemList(items) {
|
|
75
|
+
const list = items.filter(Boolean);
|
|
76
|
+
if (list.length === 0) {
|
|
77
|
+
return '📭 当前项目看板中暂无任何条目 (Items)。';
|
|
78
|
+
}
|
|
79
|
+
const rows = list.map((item) => {
|
|
80
|
+
const rawId = String(item.id || '-');
|
|
81
|
+
const shortId = rawId.length > 8 ? `\`${rawId.slice(0, 8)}...\`` : `\`${rawId}\``;
|
|
82
|
+
const title = sanitizeCell(item.title || item.content?.title || '(无标题条目)', 45);
|
|
83
|
+
const type = item.type || (item.content?.type ? String(item.content.type) : 'DraftIssue');
|
|
84
|
+
const status = item.status || '-';
|
|
85
|
+
return `| ${shortId} | ${title} | \`${type}\` | ${status} |`;
|
|
86
|
+
});
|
|
87
|
+
return [
|
|
88
|
+
`**项目看板条目 (${list.length} 条):**\n`,
|
|
89
|
+
'| 条目 ID | 标题 / 内容 | 类型 | 状态看板 |',
|
|
90
|
+
'| :--- | :--- | :--- | :--- |',
|
|
91
|
+
...rows,
|
|
92
|
+
].join('\n');
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Project 领域综合渲染入口
|
|
96
|
+
*/
|
|
97
|
+
export function renderProjectMarkdown(args, value) {
|
|
98
|
+
if (!value)
|
|
99
|
+
return '无返回内容';
|
|
100
|
+
if (value.status === 'SCOPE_REQUIRED') {
|
|
101
|
+
return renderScopeRequiredMarkdown(value);
|
|
102
|
+
}
|
|
103
|
+
if (value.status === 'AWAITING_CONFIRMATION') {
|
|
104
|
+
return [
|
|
105
|
+
`> ### ${value.warning || '⚠️ 操作确认拦截'}`,
|
|
106
|
+
`> - **影响评估**: ${value.impact || ''}`,
|
|
107
|
+
`> - **确认指引**: ${value.requiredAction || '如确认继续,请在参数中附加 `confirm: true` 重新调用。'}`,
|
|
108
|
+
].join('\n');
|
|
109
|
+
}
|
|
110
|
+
if (typeof value.result === 'string')
|
|
111
|
+
return value.result;
|
|
112
|
+
if (typeof value.message === 'string' && !value.items && !value.projects) {
|
|
113
|
+
const extra = value.detail || value.output || value.url;
|
|
114
|
+
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
115
|
+
}
|
|
116
|
+
// 列表渲染(适配 projects 或 items 数组)
|
|
117
|
+
const listData = Array.isArray(value.projects) ? value.projects : (Array.isArray(value.items) ? value.items : null);
|
|
118
|
+
if (listData && args?.action === 'list') {
|
|
119
|
+
return renderProjectList(listData);
|
|
120
|
+
}
|
|
121
|
+
if (listData && args?.action === 'item-list') {
|
|
122
|
+
return renderProjectItemList(listData);
|
|
123
|
+
}
|
|
124
|
+
if (args?.action === 'view' || value.number) {
|
|
125
|
+
return renderProjectView(value);
|
|
126
|
+
}
|
|
127
|
+
return JSON.stringify(value, null, 2);
|
|
128
|
+
}
|
package/lib/renderers/skill.d.ts
CHANGED
|
@@ -2,3 +2,33 @@
|
|
|
2
2
|
* Skill 领域渲染器:负责 Agent Skills 跨仓检索结果、已安装列表与预览说明
|
|
3
3
|
*/
|
|
4
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;
|
package/lib/renderers/skill.js
CHANGED
|
@@ -14,6 +14,11 @@ export function renderSkillMarkdown(args, value) {
|
|
|
14
14
|
}
|
|
15
15
|
if (typeof value.result === 'string')
|
|
16
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
|
+
}
|
|
17
22
|
if (typeof value.message === 'string' && !value.items) {
|
|
18
23
|
const extra = value.detail || value.output;
|
|
19
24
|
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
@@ -56,6 +61,181 @@ export function renderSkillMarkdown(args, value) {
|
|
|
56
61
|
...rows,
|
|
57
62
|
].join('\n');
|
|
58
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
|
+
}
|
|
59
68
|
// 兜底降级
|
|
60
69
|
return JSON.stringify(value, null, 2);
|
|
61
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
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TelemetrySummary } from '../telemetry.js';
|
|
1
2
|
export interface StatusSectionItem {
|
|
2
3
|
repo?: string;
|
|
3
4
|
number?: string;
|
|
@@ -21,3 +22,7 @@ export declare function parseStatusOutput(output: string): ParsedStatusReport;
|
|
|
21
22
|
* Status 领域渲染器:负责用户跨仓待办大盘的 Markdown 格式化
|
|
22
23
|
*/
|
|
23
24
|
export declare function renderStatusMarkdown(args: any, value: any): string;
|
|
25
|
+
/**
|
|
26
|
+
* 渲染 DSH GitHub 工具链健康度与降级率审计报表
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderHealthAuditMarkdown(summary: TelemetrySummary): string;
|
package/lib/renderers/status.js
CHANGED
|
@@ -90,6 +90,9 @@ export function parseStatusOutput(output) {
|
|
|
90
90
|
export function renderStatusMarkdown(args, value) {
|
|
91
91
|
if (!value)
|
|
92
92
|
return '无返回内容';
|
|
93
|
+
if (value?.healthScore !== undefined || value?.degradationRate !== undefined || args?.action === 'health') {
|
|
94
|
+
return renderHealthAuditMarkdown(value);
|
|
95
|
+
}
|
|
93
96
|
const report = value.parsed || parseStatusOutput(value.rawText || value.output || '');
|
|
94
97
|
const renderSection = (title, icon, items, emptyText) => {
|
|
95
98
|
if (!items || items.length === 0) {
|
|
@@ -122,3 +125,52 @@ export function renderStatusMarkdown(args, value) {
|
|
|
122
125
|
'> 💡 **待办操作建议**: 可通过 `github_issue(action: \'view\')` 或 `github_pr(action: \'checkout\')` 快速检视对应议题并展开工作。',
|
|
123
126
|
].join('\n');
|
|
124
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;
|