dsh-github-flow 0.4.0 → 0.6.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 +91 -12
- package/README.md +91 -12
- package/docs/UPGRADE-NOTES.md +9 -12
- package/docs/architecture.md +63 -29
- package/docs/images/1.png +0 -0
- package/lib/api/routes-actions.js +268 -5
- package/lib/api/routes-overview.js +147 -29
- package/lib/cache.d.ts +77 -4
- package/lib/cache.js +114 -7
- package/lib/client.d.ts +9 -4
- package/lib/client.js +5293 -942
- package/lib/commands/gh.d.ts +6 -1
- package/lib/commands/gh.js +144 -71
- package/lib/executor/error-classifier.d.ts +55 -0
- package/lib/executor/error-classifier.js +199 -0
- package/lib/executor/git-parser.d.ts +157 -1
- package/lib/executor/git-parser.js +513 -4
- package/lib/executor.d.ts +147 -4
- package/lib/executor.js +866 -77
- 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 +129 -14
- package/lib/tools/image-utils.d.ts +27 -0
- package/lib/tools/image-utils.js +73 -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/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 +336 -0
- package/lib/ui/commit-modal.js +144 -14
- package/lib/ui/help-modal.d.ts +21 -0
- package/lib/ui/help-modal.js +300 -0
- package/lib/ui/issue-modal.d.ts +14 -0
- package/lib/ui/issue-modal.js +366 -0
- package/lib/ui/markdown-lite.d.ts +66 -0
- package/lib/ui/markdown-lite.js +312 -0
- package/lib/ui/network-banner.d.ts +12 -0
- package/lib/ui/network-banner.js +35 -0
- package/lib/ui/pr-modal.d.ts +17 -0
- package/lib/ui/pr-modal.js +516 -0
- package/lib/ui/repo-badges.d.ts +43 -0
- package/lib/ui/repo-badges.js +82 -0
- package/lib/ui/repo-selector.d.ts +14 -0
- package/lib/ui/repo-selector.js +112 -0
- package/lib/ui/storage.d.ts +56 -0
- package/lib/ui/storage.js +163 -0
- package/lib/ui/styles.d.ts +1 -1
- package/lib/ui/styles.js +1219 -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 +172 -0
- package/lib/ui/workspace-panel.js +198 -31
- 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
package/lib/main.js
CHANGED
|
@@ -12,7 +12,12 @@ import { createRulesetTool } from './tools/ruleset.js';
|
|
|
12
12
|
import { createCacheTool } from './tools/cache.js';
|
|
13
13
|
import { createGistTool } from './tools/gist.js';
|
|
14
14
|
import { createStatusTool } from './tools/status.js';
|
|
15
|
+
import { createAgentTaskTool } from './tools/agent-task.js';
|
|
16
|
+
import { createDiscussionTool } from './tools/discussion.js';
|
|
17
|
+
import { createProjectTool } from './tools/project.js';
|
|
18
|
+
import { createCodespaceTool } from './tools/codespace.js';
|
|
15
19
|
import { createHelpTool } from './tools/help.js';
|
|
20
|
+
import { defaultTelemetryCollector } from './telemetry.js';
|
|
16
21
|
import { registerGhCommand } from './commands/gh.js';
|
|
17
22
|
import { registerApiRoutes } from './api/routes.js';
|
|
18
23
|
import { UpdateChecker } from './updater.js';
|
|
@@ -88,11 +93,26 @@ export function apply(ctx, config = {}) {
|
|
|
88
93
|
else {
|
|
89
94
|
ctx.github = executor;
|
|
90
95
|
}
|
|
91
|
-
// 2.
|
|
96
|
+
// 2. 注册 Cordis tools/result 事件切面(无侵入全量工具审计与降级率打点)
|
|
97
|
+
if (ctx.on && typeof ctx.on === 'function') {
|
|
98
|
+
ctx.on('tools/result', (exec, result) => {
|
|
99
|
+
try {
|
|
100
|
+
const toolName = exec?.name || exec?.tool?.name || '';
|
|
101
|
+
if (typeof toolName === 'string' && toolName.startsWith('github_')) {
|
|
102
|
+
const args = exec?.arguments || exec?.args || {};
|
|
103
|
+
defaultTelemetryCollector.recordCall(toolName, args, result);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Fail-Open 非阻塞防线:严禁打点逻辑异常影响主工具调用
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// 3. 注册人类 Slash 命令 (/gh status, /gh upgrade, /gh help)
|
|
92
112
|
if (ctx.commands) {
|
|
93
|
-
registerGhCommand(ctx.commands, executor, updateChecker);
|
|
113
|
+
registerGhCommand(ctx.commands, executor, updateChecker, defaultTelemetryCollector);
|
|
94
114
|
}
|
|
95
|
-
//
|
|
115
|
+
// 4. 注册 Agent 模型工具集(核心领域工具 + 元指南)
|
|
96
116
|
if (ctx.tools) {
|
|
97
117
|
ctx.tools.register(createPrTool(executor));
|
|
98
118
|
ctx.tools.register(createIssueTool(executor));
|
|
@@ -106,7 +126,11 @@ export function apply(ctx, config = {}) {
|
|
|
106
126
|
ctx.tools.register(createRulesetTool(executor));
|
|
107
127
|
ctx.tools.register(createCacheTool(executor));
|
|
108
128
|
ctx.tools.register(createGistTool(executor));
|
|
109
|
-
ctx.tools.register(createStatusTool(executor));
|
|
129
|
+
ctx.tools.register(createStatusTool(executor, defaultTelemetryCollector));
|
|
130
|
+
ctx.tools.register(createAgentTaskTool(executor));
|
|
131
|
+
ctx.tools.register(createDiscussionTool(executor));
|
|
132
|
+
ctx.tools.register(createProjectTool(executor));
|
|
133
|
+
ctx.tools.register(createCodespaceTool(executor));
|
|
110
134
|
ctx.tools.register(createHelpTool(executor));
|
|
111
135
|
}
|
|
112
136
|
// 4. 注册面向 Client 端的 WebServer HTTP 路由(如果当前环境启用了 Web UI 服务)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 格式化 Agent Task 状态徽标
|
|
3
|
+
*/
|
|
4
|
+
export declare function formatAgentTaskStateBadge(state?: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* 渲染 Agent Tasks 列表表格
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderAgentTaskList(items: any[]): string;
|
|
9
|
+
/**
|
|
10
|
+
* 渲染单个 Agent Task 详情卡片
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderAgentTaskView(task: any): string;
|
|
13
|
+
/**
|
|
14
|
+
* Agent Task 领域综合渲染入口
|
|
15
|
+
*/
|
|
16
|
+
export declare function renderAgentTaskMarkdown(args: any, value: any): string;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* 格式化 Agent Task 状态徽标
|
|
4
|
+
*/
|
|
5
|
+
export function formatAgentTaskStateBadge(state) {
|
|
6
|
+
if (!state)
|
|
7
|
+
return '⚪ UNKNOWN';
|
|
8
|
+
const upper = state.toUpperCase();
|
|
9
|
+
switch (upper) {
|
|
10
|
+
case 'COMPLETED':
|
|
11
|
+
case 'SUCCESS':
|
|
12
|
+
return '🟢 COMPLETED';
|
|
13
|
+
case 'IN_PROGRESS':
|
|
14
|
+
case 'RUNNING':
|
|
15
|
+
return '🟡 RUNNING';
|
|
16
|
+
case 'FAILED':
|
|
17
|
+
case 'FAILURE':
|
|
18
|
+
return '🔴 FAILED';
|
|
19
|
+
case 'CANCELLED':
|
|
20
|
+
case 'CANCELED':
|
|
21
|
+
return '⚪ CANCELLED';
|
|
22
|
+
default:
|
|
23
|
+
return `⚪ ${upper}`;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 渲染 Agent Tasks 列表表格
|
|
28
|
+
*/
|
|
29
|
+
export function renderAgentTaskList(items) {
|
|
30
|
+
const list = items.filter(Boolean);
|
|
31
|
+
if (list.length === 0) {
|
|
32
|
+
return '📭 暂无智能体任务 (Agent Tasks)。';
|
|
33
|
+
}
|
|
34
|
+
const rows = list.map((t) => {
|
|
35
|
+
const rawId = String(t.id || '-');
|
|
36
|
+
const shortId = rawId.length > 8 ? `\`${rawId.slice(0, 8)}...\`` : `\`${rawId}\``;
|
|
37
|
+
const name = sanitizeCell(t.name || '(无描述)', 40);
|
|
38
|
+
const stateBadge = formatAgentTaskStateBadge(t.state);
|
|
39
|
+
let prCell = '-';
|
|
40
|
+
if (t.pullRequestNumber) {
|
|
41
|
+
const prText = `#${t.pullRequestNumber}`;
|
|
42
|
+
prCell = t.pullRequestUrl ? `[${prText}](${t.pullRequestUrl})` : prText;
|
|
43
|
+
}
|
|
44
|
+
const created = t.createdAt ? t.createdAt.split('T')[0] : '-';
|
|
45
|
+
return `| ${shortId} | ${name} | ${stateBadge} | ${prCell} | ${created} |`;
|
|
46
|
+
});
|
|
47
|
+
return [
|
|
48
|
+
`**智能体任务列表 (${list.length} 条):**\n`,
|
|
49
|
+
'| 任务 ID | 描述 | 状态 | 关联 PR | 创建日期 |',
|
|
50
|
+
'| :--- | :--- | :--- | :--- | :--- |',
|
|
51
|
+
...rows,
|
|
52
|
+
].join('\n');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 渲染单个 Agent Task 详情卡片
|
|
56
|
+
*/
|
|
57
|
+
export function renderAgentTaskView(task) {
|
|
58
|
+
if (!task || typeof task !== 'object') {
|
|
59
|
+
return '无任务详情数据';
|
|
60
|
+
}
|
|
61
|
+
const name = task.name || '(未命名任务)';
|
|
62
|
+
const stateBadge = formatAgentTaskStateBadge(task.state);
|
|
63
|
+
const repo = task.repository ? `[${task.repository}](https://github.com/${task.repository})` : '-';
|
|
64
|
+
const user = task.user?.login || task.user || '-';
|
|
65
|
+
const created = task.createdAt || '-';
|
|
66
|
+
const completed = task.completedAt || '-';
|
|
67
|
+
const lines = [
|
|
68
|
+
`### 🤖 Agent Task: ${name}\n`,
|
|
69
|
+
`- **任务 ID**: \`${task.id || '-'}\``,
|
|
70
|
+
`- **状态**: ${stateBadge}`,
|
|
71
|
+
`- **目标仓库**: ${repo}`,
|
|
72
|
+
`- **分派用户**: @${user}`,
|
|
73
|
+
`- **创建时间**: ${created}`,
|
|
74
|
+
`- **完成时间**: ${completed}`,
|
|
75
|
+
];
|
|
76
|
+
if (task.pullRequestNumber) {
|
|
77
|
+
const prTitle = task.pullRequestTitle ? ` - ${task.pullRequestTitle}` : '';
|
|
78
|
+
const prLink = task.pullRequestUrl
|
|
79
|
+
? `[#${task.pullRequestNumber}${prTitle}](${task.pullRequestUrl})`
|
|
80
|
+
: `#${task.pullRequestNumber}${prTitle}`;
|
|
81
|
+
lines.push(`- **关联 PR**: ${prLink} (${task.pullRequestState || 'OPEN'})`);
|
|
82
|
+
}
|
|
83
|
+
if (task.logs || task.log) {
|
|
84
|
+
lines.push('\n**会话日志记录:**', '```text', String(task.logs || task.log).trim(), '```');
|
|
85
|
+
}
|
|
86
|
+
return lines.join('\n');
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Agent Task 领域综合渲染入口
|
|
90
|
+
*/
|
|
91
|
+
export function renderAgentTaskMarkdown(args, value) {
|
|
92
|
+
if (!value)
|
|
93
|
+
return '无返回内容';
|
|
94
|
+
if (typeof value.result === 'string')
|
|
95
|
+
return value.result;
|
|
96
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
97
|
+
const extra = value.detail || value.output || value.url;
|
|
98
|
+
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
101
|
+
return renderAgentTaskList(value.items);
|
|
102
|
+
}
|
|
103
|
+
if (args?.action === 'view' || value.id) {
|
|
104
|
+
return renderAgentTaskView(value);
|
|
105
|
+
}
|
|
106
|
+
return JSON.stringify(value, null, 2);
|
|
107
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 格式化 Codespace 状态徽标
|
|
3
|
+
*/
|
|
4
|
+
export declare function formatCodespaceStateBadge(state?: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* 渲染 Codespaces 列表表格
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderCodespaceList(items: any[]): string;
|
|
9
|
+
/**
|
|
10
|
+
* 渲染单个 Codespace 详情卡片
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderCodespaceView(cs: any): string;
|
|
13
|
+
/**
|
|
14
|
+
* Codespace 领域综合渲染入口
|
|
15
|
+
*/
|
|
16
|
+
export declare function renderCodespaceMarkdown(args: any, value: any): string;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
import { renderScopeRequiredMarkdown } from './project.js';
|
|
3
|
+
/**
|
|
4
|
+
* 格式化 Codespace 状态徽标
|
|
5
|
+
*/
|
|
6
|
+
export function formatCodespaceStateBadge(state) {
|
|
7
|
+
if (!state)
|
|
8
|
+
return '⚪ UNKNOWN';
|
|
9
|
+
const lower = state.toLowerCase();
|
|
10
|
+
if (lower === 'available')
|
|
11
|
+
return '🟢 AVAILABLE';
|
|
12
|
+
if (lower === 'shutdown')
|
|
13
|
+
return '⚪ SHUTDOWN';
|
|
14
|
+
if (lower.includes('start') || lower.includes('rebuild'))
|
|
15
|
+
return '🟡 STARTING';
|
|
16
|
+
if (lower.includes('updat') || lower.includes('export'))
|
|
17
|
+
return '🟡 BUSY';
|
|
18
|
+
return `⚪ ${state.toUpperCase()}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 渲染 Codespaces 列表表格
|
|
22
|
+
*/
|
|
23
|
+
export function renderCodespaceList(items) {
|
|
24
|
+
const list = items.filter(Boolean);
|
|
25
|
+
if (list.length === 0) {
|
|
26
|
+
return '📭 暂无云端开发环境 (Codespaces)。';
|
|
27
|
+
}
|
|
28
|
+
const rows = list.map((cs) => {
|
|
29
|
+
const name = `\`${sanitizeCell(cs.name || '-', 25)}\``;
|
|
30
|
+
const displayName = sanitizeCell(cs.displayName || cs.name || '-', 25);
|
|
31
|
+
const repo = cs.repository ? `[${cs.repository}](https://github.com/${cs.repository})` : '-';
|
|
32
|
+
const stateBadge = formatCodespaceStateBadge(cs.state);
|
|
33
|
+
const machine = sanitizeCell(cs.machineDisplayName || cs.machineName || '-', 20);
|
|
34
|
+
const lastUsed = cs.lastUsedAt ? cs.lastUsedAt.split('T')[0] : '-';
|
|
35
|
+
return `| ${name} | ${displayName} | ${repo} | ${stateBadge} | ${machine} | ${lastUsed} |`;
|
|
36
|
+
});
|
|
37
|
+
return [
|
|
38
|
+
`**云端开发环境列表 (${list.length} 台):**\n`,
|
|
39
|
+
'| 实例标识 | 显示名称 | 关联仓库 | 运行状态 | 机器规格 | 上次使用 |',
|
|
40
|
+
'| :--- | :--- | :--- | :--- | :--- | :--- |',
|
|
41
|
+
...rows,
|
|
42
|
+
].join('\n');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 渲染单个 Codespace 详情卡片
|
|
46
|
+
*/
|
|
47
|
+
export function renderCodespaceView(cs) {
|
|
48
|
+
if (!cs || typeof cs !== 'object') {
|
|
49
|
+
return '无云端环境详情数据';
|
|
50
|
+
}
|
|
51
|
+
const name = cs.displayName || cs.name || '未命名实例';
|
|
52
|
+
const stateBadge = formatCodespaceStateBadge(cs.state);
|
|
53
|
+
const repo = cs.repository ? `[${cs.repository}](https://github.com/${cs.repository})` : '-';
|
|
54
|
+
const owner = cs.owner || cs.billableOwner || '-';
|
|
55
|
+
const machine = cs.machineDisplayName || cs.machineName || '-';
|
|
56
|
+
const created = cs.createdAt || '-';
|
|
57
|
+
const lastUsed = cs.lastUsedAt || '-';
|
|
58
|
+
const lines = [
|
|
59
|
+
`### ☁️ Codespace: ${name}\n`,
|
|
60
|
+
`- **实例标识**: \`${cs.name || '-'}\``,
|
|
61
|
+
`- **运行状态**: ${stateBadge}`,
|
|
62
|
+
`- **关联仓库**: ${repo}`,
|
|
63
|
+
`- **所有者**: @${owner}`,
|
|
64
|
+
`- **机器规格**: ${machine}`,
|
|
65
|
+
`- **空闲停机**: ${cs.idleTimeoutMinutes ? `${cs.idleTimeoutMinutes} 分钟` : '默认'}`,
|
|
66
|
+
`- **创建时间**: ${created}`,
|
|
67
|
+
`- **上次活跃**: ${lastUsed}`,
|
|
68
|
+
];
|
|
69
|
+
if (cs.devcontainerPath) {
|
|
70
|
+
lines.push(`- **容器配置**: \`${cs.devcontainerPath}\``);
|
|
71
|
+
}
|
|
72
|
+
return lines.join('\n');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Codespace 领域综合渲染入口
|
|
76
|
+
*/
|
|
77
|
+
export function renderCodespaceMarkdown(args, value) {
|
|
78
|
+
if (!value)
|
|
79
|
+
return '无返回内容';
|
|
80
|
+
if (value.status === 'SCOPE_REQUIRED') {
|
|
81
|
+
return renderScopeRequiredMarkdown(value);
|
|
82
|
+
}
|
|
83
|
+
if (value.status === 'AWAITING_CONFIRMATION') {
|
|
84
|
+
return [
|
|
85
|
+
`> ### ${value.warning || '⚠️ 操作确认拦截'}`,
|
|
86
|
+
`> - **影响评估**: ${value.impact || ''}`,
|
|
87
|
+
`> - **确认指引**: ${value.requiredAction || '如确认继续,请在参数中附加 `confirm: true` 重新调用。'}`,
|
|
88
|
+
].join('\n');
|
|
89
|
+
}
|
|
90
|
+
if (typeof value.result === 'string')
|
|
91
|
+
return value.result;
|
|
92
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
93
|
+
const extra = value.detail || value.output;
|
|
94
|
+
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
95
|
+
}
|
|
96
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
97
|
+
return renderCodespaceList(value.items);
|
|
98
|
+
}
|
|
99
|
+
if (args?.action === 'view' || value.name) {
|
|
100
|
+
return renderCodespaceView(value);
|
|
101
|
+
}
|
|
102
|
+
return JSON.stringify(value, null, 2);
|
|
103
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 格式化 Discussion 状态徽标
|
|
3
|
+
*/
|
|
4
|
+
export declare function formatDiscussionStateBadge(state?: string, answered?: boolean, closed?: boolean): string;
|
|
5
|
+
/**
|
|
6
|
+
* 渲染 Discussion 列表表格
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderDiscussionList(items: any[]): string;
|
|
9
|
+
/**
|
|
10
|
+
* 渲染单个 Discussion 详情卡片
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderDiscussionView(d: any): string;
|
|
13
|
+
/**
|
|
14
|
+
* Discussion 领域综合渲染入口
|
|
15
|
+
*/
|
|
16
|
+
export declare function renderDiscussionMarkdown(args: any, value: any): string;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { sanitizeCell } from './common.js';
|
|
2
|
+
/**
|
|
3
|
+
* 格式化 Discussion 状态徽标
|
|
4
|
+
*/
|
|
5
|
+
export function formatDiscussionStateBadge(state, answered, closed) {
|
|
6
|
+
if (answered)
|
|
7
|
+
return '🟢 ANSWERED';
|
|
8
|
+
const effective = state ? String(state).toUpperCase() : (closed ? 'CLOSED' : 'OPEN');
|
|
9
|
+
if (effective === 'OPEN')
|
|
10
|
+
return '🔵 OPEN';
|
|
11
|
+
if (effective === 'CLOSED')
|
|
12
|
+
return '🟣 CLOSED';
|
|
13
|
+
return `⚪ ${effective}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 渲染 Discussion 列表表格
|
|
17
|
+
*/
|
|
18
|
+
export function renderDiscussionList(items) {
|
|
19
|
+
const list = items.filter(Boolean);
|
|
20
|
+
if (list.length === 0) {
|
|
21
|
+
return '📭 暂无讨论主题 (Discussions)。';
|
|
22
|
+
}
|
|
23
|
+
const rows = list.map((d) => {
|
|
24
|
+
const num = d.number ? `#${d.number}` : '-';
|
|
25
|
+
const link = d.url ? `[${num}](${d.url})` : num;
|
|
26
|
+
const title = sanitizeCell(d.title || '(无标题)', 45);
|
|
27
|
+
const category = sanitizeCell(d.category?.name || d.category || 'General', 15);
|
|
28
|
+
const stateBadge = formatDiscussionStateBadge(d.state, d.answered, d.closed);
|
|
29
|
+
const author = d.author?.login || d.author || '-';
|
|
30
|
+
const updated = d.updatedAt ? d.updatedAt.split('T')[0] : (d.createdAt ? d.createdAt.split('T')[0] : '-');
|
|
31
|
+
return `| ${link} | ${title} | \`${category}\` | ${stateBadge} | @${author} | ${updated} |`;
|
|
32
|
+
});
|
|
33
|
+
return [
|
|
34
|
+
`**讨论主题列表 (${list.length} 条):**\n`,
|
|
35
|
+
'| 编号 | 标题 | 分类 | 状态 | 作者 | 更新日期 |',
|
|
36
|
+
'| :--- | :--- | :--- | :--- | :--- | :--- |',
|
|
37
|
+
...rows,
|
|
38
|
+
].join('\n');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 渲染单个 Discussion 详情卡片
|
|
42
|
+
*/
|
|
43
|
+
export function renderDiscussionView(d) {
|
|
44
|
+
if (!d || typeof d !== 'object') {
|
|
45
|
+
return '无讨论详情数据';
|
|
46
|
+
}
|
|
47
|
+
const num = d.number ? `#${d.number}` : '';
|
|
48
|
+
const title = d.title || '(无标题)';
|
|
49
|
+
const stateBadge = formatDiscussionStateBadge(d.state, d.answered, d.closed);
|
|
50
|
+
const category = d.category?.name || d.category || 'General';
|
|
51
|
+
const author = d.author?.login || d.author || '-';
|
|
52
|
+
const created = d.createdAt || '-';
|
|
53
|
+
const link = d.url ? `[查看原始讨论](${d.url})` : '';
|
|
54
|
+
const lines = [
|
|
55
|
+
`### 💬 Discussion ${num}: ${title}\n`,
|
|
56
|
+
`- **状态**: ${stateBadge}`,
|
|
57
|
+
`- **分类**: \`${category}\``,
|
|
58
|
+
`- **发起人**: @${author}`,
|
|
59
|
+
`- **创建时间**: ${created}`,
|
|
60
|
+
];
|
|
61
|
+
if (link)
|
|
62
|
+
lines.push(`- **链接**: ${link}`);
|
|
63
|
+
if (Array.isArray(d.labels) && d.labels.length > 0) {
|
|
64
|
+
const labelBadges = d.labels.map((l) => `\`${l.name || l}\``).join(' ');
|
|
65
|
+
lines.push(`- **标签**: ${labelBadges}`);
|
|
66
|
+
}
|
|
67
|
+
if (d.body) {
|
|
68
|
+
lines.push('\n**正文内容:**', '---', d.body.trim(), '---');
|
|
69
|
+
}
|
|
70
|
+
// 渲染关联评论列表
|
|
71
|
+
if (Array.isArray(d.comments) && d.comments.length > 0) {
|
|
72
|
+
lines.push(`\n**讨论回复 (${d.comments.length} 条):**`);
|
|
73
|
+
d.comments.forEach((c, index) => {
|
|
74
|
+
const cAuthor = c.author?.login || c.author || '匿名';
|
|
75
|
+
const cTime = c.createdAt ? c.createdAt.replace('T', ' ').slice(0, 19) : '';
|
|
76
|
+
const cBody = (c.body || '').trim();
|
|
77
|
+
lines.push(`\n> **#${index + 1} 楼 @${cAuthor}** (${cTime}):\n> ${cBody.replace(/\n/g, '\n> ')}`);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return lines.join('\n');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Discussion 领域综合渲染入口
|
|
84
|
+
*/
|
|
85
|
+
export function renderDiscussionMarkdown(args, value) {
|
|
86
|
+
if (!value)
|
|
87
|
+
return '无返回内容';
|
|
88
|
+
if (typeof value.result === 'string')
|
|
89
|
+
return value.result;
|
|
90
|
+
if (typeof value.message === 'string' && !value.items) {
|
|
91
|
+
const extra = value.detail || value.output || value.url;
|
|
92
|
+
return `**${value.message}**` + (extra ? `\n\`\`\`\n${extra}\n\`\`\`` : '');
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(value.items) && args?.action === 'list') {
|
|
95
|
+
return renderDiscussionList(value.items);
|
|
96
|
+
}
|
|
97
|
+
if (args?.action === 'view' || value.number) {
|
|
98
|
+
return renderDiscussionView(value);
|
|
99
|
+
}
|
|
100
|
+
return JSON.stringify(value, null, 2);
|
|
101
|
+
}
|
|
@@ -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;
|