dsh-github-flow 0.1.0 → 0.1.3
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/LICENSE +21 -0
- package/README.en.md +119 -0
- package/README.md +39 -50
- package/cordis.patch.yml +2 -6
- package/docs/architecture.md +69 -0
- package/docs/npm-publish-guide.md +100 -0
- package/lib/api/routes.d.ts +1 -2
- package/lib/api/routes.js +234 -202
- package/lib/client/style.d.ts +1 -1
- package/lib/client/style.js +431 -66
- package/lib/client.js +396 -99
- package/lib/commands/gh.d.ts +1 -1
- package/lib/commands/gh.js +40 -55
- package/lib/index.d.ts +2 -15
- package/lib/index.js +12 -39
- package/lib/main.d.ts +4 -0
- package/lib/main.js +30 -0
- package/lib/tools/api.d.ts +37 -1
- package/lib/tools/api.js +21 -5
- package/lib/tools/issue.d.ts +55 -1
- package/lib/tools/issue.js +30 -7
- package/lib/tools/pr.d.ts +59 -1
- package/lib/tools/pr.js +32 -9
- package/lib/tools/repo.d.ts +37 -1
- package/lib/tools/repo.js +31 -8
- package/lib/tools/run.d.ts +37 -1
- package/lib/tools/run.js +31 -8
- package/lib/types.d.ts +0 -13
- package/package.json +16 -11
- package/lib/service.d.ts +0 -28
- package/lib/service.js +0 -34
- package/lib/tools/define.d.ts +0 -6
- package/lib/tools/define.js +0 -15
package/lib/commands/gh.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { GhExecutor } from '../executor.js';
|
|
2
|
-
export declare function registerGhCommand(
|
|
2
|
+
export declare function registerGhCommand(commandsService: any, executor: GhExecutor): void;
|
package/lib/commands/gh.js
CHANGED
|
@@ -1,63 +1,48 @@
|
|
|
1
|
-
export function registerGhCommand(
|
|
2
|
-
const commandsService = ctx.get ? ctx.get('commands') : ctx.commands;
|
|
1
|
+
export function registerGhCommand(commandsService, executor) {
|
|
3
2
|
if (!commandsService || typeof commandsService.register !== 'function')
|
|
4
3
|
return;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
md += `- **授权作用域**: ${auth.scopes?.map((s) => `\`${s}\``).join(', ') || '标准权限'}\n`;
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
md += `- **登录状态**: ❌ 未登录 GitHub\n`;
|
|
21
|
-
md += `> 提示:请在系统终端执行 \`gh auth login\` 进行登录。\n`;
|
|
22
|
-
}
|
|
23
|
-
if (repo) {
|
|
24
|
-
md += `\n**当前关联仓库**:\n`;
|
|
25
|
-
md += `- 仓库名: [${repo.nameWithOwner}](${repo.url})\n`;
|
|
26
|
-
md += `- 默认分支: \`${repo.defaultBranch}\`\n`;
|
|
27
|
-
if (repo.description)
|
|
28
|
-
md += `- 简介: ${repo.description}\n`;
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
md += `\n*当前目录尚未关联 Git 远程仓库或未托管在 GitHub。*\n`;
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
kind: 'markdown',
|
|
35
|
-
text: md,
|
|
36
|
-
};
|
|
4
|
+
commandsService.register({
|
|
5
|
+
name: 'gh',
|
|
6
|
+
description: 'GitHub CLI 状态与诊断快捷指令',
|
|
7
|
+
async handler({ rawInput }) {
|
|
8
|
+
const trimmed = (rawInput || '').trim();
|
|
9
|
+
if (!trimmed || trimmed === 'status') {
|
|
10
|
+
const auth = await executor.checkAuth();
|
|
11
|
+
const repo = await executor.getRepoMetadata();
|
|
12
|
+
let md = `### GitHub 集成状态\n\n`;
|
|
13
|
+
if (auth.loggedIn) {
|
|
14
|
+
md += `- **登录账号**: \`${auth.user || '已登录'}\`\n`;
|
|
15
|
+
md += `- **授权作用域**: ${auth.scopes?.map((s) => `\`${s}\``).join(', ') || '标准权限'}\n`;
|
|
37
16
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
text: `### DSH GitHub Flow 帮助\n\n- \`/gh status\`: 检查当前 GitHub CLI 账号与仓库连接\n- \`/gh help\`: 显示此帮助信息\n\n可以在对话中直接让 Agent 操作 GitHub,如:\n- *"查看当前仓库有哪些开放的 PR"* \n- *"查看最近一次 CI 失败的原因"* \n- *"帮我给当前分支创建 PR"*`,
|
|
42
|
-
};
|
|
17
|
+
else {
|
|
18
|
+
md += `- **登录状态**: ❌ 未登录 GitHub\n`;
|
|
19
|
+
md += `> 提示:请在系统终端执行 \`gh auth login\` 进行登录。\n`;
|
|
43
20
|
}
|
|
21
|
+
if (repo) {
|
|
22
|
+
md += `\n**当前关联仓库**:\n`;
|
|
23
|
+
md += `- 仓库名: [${repo.nameWithOwner}](${repo.url})\n`;
|
|
24
|
+
md += `- 默认分支: \`${repo.defaultBranch}\`\n`;
|
|
25
|
+
if (repo.description)
|
|
26
|
+
md += `- 简介: ${repo.description}\n`;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
md += `\n*当前目录尚未关联 Git 远程仓库或未托管在 GitHub。*\n`;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
kind: 'success',
|
|
33
|
+
text: md,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (trimmed === 'help') {
|
|
44
37
|
return {
|
|
45
|
-
kind: '
|
|
46
|
-
text:
|
|
38
|
+
kind: 'success',
|
|
39
|
+
text: `### DSH GitHub Flow 帮助\n\n- \`/gh status\`: 检查当前 GitHub CLI 账号与仓库连接\n- \`/gh help\`: 显示此帮助信息\n\n可以在对话中直接让 Agent 操作 GitHub,如:\n- *"查看当前仓库有哪些开放的 PR"* \n- *"查看最近一次 CI 失败的原因"* \n- *"帮我给当前分支创建 PR"*`,
|
|
47
40
|
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
ctx.effect(() => {
|
|
53
|
-
const dispose = doRegister();
|
|
54
|
-
return () => {
|
|
55
|
-
if (typeof dispose === 'function')
|
|
56
|
-
dispose();
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
kind: 'error',
|
|
44
|
+
text: `未知命令选项: /gh ${trimmed}。可用用法: /gh status, /gh help`,
|
|
57
45
|
};
|
|
58
|
-
},
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
doRegister();
|
|
62
|
-
}
|
|
46
|
+
},
|
|
47
|
+
});
|
|
63
48
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
import Schema from '@deepseek-ai/schemastery';
|
|
2
|
-
import type { PluginConfig } from './types.js';
|
|
3
1
|
export declare const name = "dsh-github-flow";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* 在无 Web 界面环境(如 Headless、纯终端、CI 自动化模式)下也能即刻激活,杜绝 PENDING 卡死。
|
|
7
|
-
*/
|
|
8
|
-
export declare const inject: readonly ['tools'];
|
|
9
|
-
/**
|
|
10
|
-
* 遵循官方 Cordis 规范导出的强类型配置接口与运行时校验器
|
|
11
|
-
*/
|
|
12
|
-
export interface Config extends PluginConfig {
|
|
13
|
-
}
|
|
14
|
-
export declare const Config: Schema<Config>;
|
|
15
|
-
export declare function apply(ctx: any, config?: Config): void;
|
|
16
|
-
export { GitHubService } from './service.js';
|
|
2
|
+
export declare const inject: readonly ['tools', 'commands', 'webServer', 'workspaceRegistry'];
|
|
3
|
+
export declare function apply(ctx: any): void;
|
|
17
4
|
export * from './types.js';
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import Schema from '@deepseek-ai/schemastery';
|
|
2
1
|
import { GhExecutor } from './executor.js';
|
|
3
|
-
import { GitHubService } from './service.js';
|
|
4
2
|
import { createPrTool } from './tools/pr.js';
|
|
5
3
|
import { createIssueTool } from './tools/issue.js';
|
|
6
4
|
import { createRunTool } from './tools/run.js';
|
|
@@ -9,49 +7,24 @@ import { createApiTool } from './tools/api.js';
|
|
|
9
7
|
import { registerGhCommand } from './commands/gh.js';
|
|
10
8
|
import { registerApiRoutes } from './api/routes.js';
|
|
11
9
|
export const name = 'dsh-github-flow';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
cacheTtlMs: Schema.number().default(15000).description('Web API 概览数据缓存有效时长 (ms)'),
|
|
22
|
-
defaultListLimit: Schema.number().default(20).description('默认查询列表返回条数'),
|
|
23
|
-
});
|
|
24
|
-
export function apply(ctx, config = {}) {
|
|
25
|
-
// 1. 初始化具备动态配置能力的执行引擎
|
|
26
|
-
const executor = new GhExecutor(config);
|
|
27
|
-
// 2. 将核心能力提升挂载为 Cordis 原生微内核服务 (ctx.github)
|
|
28
|
-
new GitHubService(ctx, executor);
|
|
29
|
-
// 3. 注册 Agent 模型工具集(5 大核心领域工具)
|
|
30
|
-
if (ctx.tools && typeof ctx.tools.register === 'function') {
|
|
10
|
+
export const inject = ['tools', 'commands', 'webServer', 'workspaceRegistry'];
|
|
11
|
+
export function apply(ctx) {
|
|
12
|
+
const executor = new GhExecutor();
|
|
13
|
+
// 1. 注册人类 Slash 命令 (/gh status, /gh help)
|
|
14
|
+
if (ctx.commands) {
|
|
15
|
+
registerGhCommand(ctx.commands, executor);
|
|
16
|
+
}
|
|
17
|
+
// 2. 注册 Agent 模型工具集(5 大核心领域工具)
|
|
18
|
+
if (ctx.tools) {
|
|
31
19
|
ctx.tools.register(createPrTool(executor));
|
|
32
20
|
ctx.tools.register(createIssueTool(executor));
|
|
33
21
|
ctx.tools.register(createRunTool(executor));
|
|
34
22
|
ctx.tools.register(createRepoTool(executor));
|
|
35
23
|
ctx.tools.register(createApiTool(executor));
|
|
36
24
|
}
|
|
37
|
-
//
|
|
38
|
-
if (ctx.
|
|
39
|
-
ctx.
|
|
40
|
-
registerGhCommand(innerCtx, executor);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
else if (ctx.commands) {
|
|
44
|
-
registerGhCommand(ctx, executor);
|
|
45
|
-
}
|
|
46
|
-
// 5. 可选依赖:Web 界面服务(存在时动态注入路由并由 effect 管理生命周期)
|
|
47
|
-
if (ctx.inject && typeof ctx.inject === 'function') {
|
|
48
|
-
ctx.inject(['webServer'], (innerCtx) => {
|
|
49
|
-
registerApiRoutes(innerCtx, executor, config);
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
else if (ctx.webServer) {
|
|
53
|
-
registerApiRoutes(ctx, executor, config);
|
|
25
|
+
// 3. 注册面向 Client 端的 WebServer HTTP 路由
|
|
26
|
+
if (ctx.webServer) {
|
|
27
|
+
registerApiRoutes(ctx.webServer, executor, ctx.workspaceRegistry);
|
|
54
28
|
}
|
|
55
29
|
}
|
|
56
|
-
export { GitHubService } from './service.js';
|
|
57
30
|
export * from './types.js';
|
package/lib/main.d.ts
ADDED
package/lib/main.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { GhExecutor } from './executor.js';
|
|
2
|
+
import { createPrTool } from './tools/pr.js';
|
|
3
|
+
import { createIssueTool } from './tools/issue.js';
|
|
4
|
+
import { createRunTool } from './tools/run.js';
|
|
5
|
+
import { createRepoTool } from './tools/repo.js';
|
|
6
|
+
import { createApiTool } from './tools/api.js';
|
|
7
|
+
import { registerGhCommand } from './commands/gh.js';
|
|
8
|
+
import { registerApiRoutes } from './api/routes.js';
|
|
9
|
+
export const name = 'dsh-github-flow';
|
|
10
|
+
export const inject = ['tools', 'commands', 'webServer', 'workspaceRegistry'];
|
|
11
|
+
export function apply(ctx) {
|
|
12
|
+
const executor = new GhExecutor();
|
|
13
|
+
// 1. 注册人类 Slash 命令 (/gh status, /gh help)
|
|
14
|
+
if (ctx.commands) {
|
|
15
|
+
registerGhCommand(ctx.commands, executor);
|
|
16
|
+
}
|
|
17
|
+
// 2. 注册 Agent 模型工具集(5 大核心领域工具)
|
|
18
|
+
if (ctx.tools) {
|
|
19
|
+
ctx.tools.register(createPrTool(executor));
|
|
20
|
+
ctx.tools.register(createIssueTool(executor));
|
|
21
|
+
ctx.tools.register(createRunTool(executor));
|
|
22
|
+
ctx.tools.register(createRepoTool(executor));
|
|
23
|
+
ctx.tools.register(createApiTool(executor));
|
|
24
|
+
}
|
|
25
|
+
// 3. 注册面向 Client 端的 WebServer HTTP 路由
|
|
26
|
+
if (ctx.webServer) {
|
|
27
|
+
registerApiRoutes(ctx.webServer, executor, ctx.workspaceRegistry);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export * from './types.js';
|
package/lib/tools/api.d.ts
CHANGED
|
@@ -1,2 +1,38 @@
|
|
|
1
1
|
import type { GhExecutor } from '../executor.js';
|
|
2
|
-
export declare function createApiTool(executor: GhExecutor):
|
|
2
|
+
export declare function createApiTool(executor: GhExecutor): {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
type: string;
|
|
7
|
+
properties: {
|
|
8
|
+
endpoint: {
|
|
9
|
+
type: string;
|
|
10
|
+
description: string;
|
|
11
|
+
};
|
|
12
|
+
method: {
|
|
13
|
+
type: string;
|
|
14
|
+
enum: string[];
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
jq: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
fields: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
required: string[];
|
|
27
|
+
};
|
|
28
|
+
output: {
|
|
29
|
+
schema: {
|
|
30
|
+
type: string;
|
|
31
|
+
};
|
|
32
|
+
render(args: any, value: any): {
|
|
33
|
+
type: string;
|
|
34
|
+
text: any;
|
|
35
|
+
}[];
|
|
36
|
+
};
|
|
37
|
+
execute(args: any, execContext: any): Promise<Record<string, any>>;
|
|
38
|
+
};
|
package/lib/tools/api.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
function normalizeOutput(data) {
|
|
2
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
3
|
+
return data;
|
|
4
|
+
}
|
|
5
|
+
if (Array.isArray(data)) {
|
|
6
|
+
return { items: data, count: data.length };
|
|
7
|
+
}
|
|
8
|
+
return { result: data !== undefined && data !== null ? data : '' };
|
|
9
|
+
}
|
|
2
10
|
export function createApiTool(executor) {
|
|
3
|
-
return
|
|
11
|
+
return {
|
|
4
12
|
name: 'github_api',
|
|
5
13
|
description: '通过 GitHub CLI 认证环境直接调用任意 GitHub REST 或 GraphQL API 端点(兜底逃生门)。支持 --jq 过滤。',
|
|
6
14
|
parameters: {
|
|
@@ -29,7 +37,15 @@ export function createApiTool(executor) {
|
|
|
29
37
|
output: {
|
|
30
38
|
schema: { type: 'object' },
|
|
31
39
|
render(args, value) {
|
|
32
|
-
|
|
40
|
+
if (!value)
|
|
41
|
+
return [{ type: 'text', text: '无返回内容' }];
|
|
42
|
+
if (typeof value.result === 'string') {
|
|
43
|
+
return [{ type: 'text', text: value.result }];
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(value.items)) {
|
|
46
|
+
return [{ type: 'text', text: JSON.stringify(value.items, null, 2) }];
|
|
47
|
+
}
|
|
48
|
+
return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
|
|
33
49
|
},
|
|
34
50
|
},
|
|
35
51
|
async execute(args, execContext) {
|
|
@@ -49,7 +65,7 @@ export function createApiTool(executor) {
|
|
|
49
65
|
const res = await executor.run(cmd, { cwd });
|
|
50
66
|
if (!res.ok)
|
|
51
67
|
throw new Error(res.error);
|
|
52
|
-
return res.data
|
|
68
|
+
return normalizeOutput(res.data !== undefined ? res.data : res.rawOutput);
|
|
53
69
|
},
|
|
54
|
-
}
|
|
70
|
+
};
|
|
55
71
|
}
|
package/lib/tools/issue.d.ts
CHANGED
|
@@ -1,2 +1,56 @@
|
|
|
1
1
|
import type { GhExecutor } from '../executor.js';
|
|
2
|
-
export declare function createIssueTool(executor: GhExecutor):
|
|
2
|
+
export declare function createIssueTool(executor: GhExecutor): {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
type: string;
|
|
7
|
+
properties: {
|
|
8
|
+
action: {
|
|
9
|
+
type: string;
|
|
10
|
+
enum: string[];
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
issue_number: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
repo: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
title: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
body: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
labels: {
|
|
30
|
+
type: string;
|
|
31
|
+
items: {
|
|
32
|
+
type: string;
|
|
33
|
+
};
|
|
34
|
+
description: string;
|
|
35
|
+
};
|
|
36
|
+
assignees: {
|
|
37
|
+
type: string;
|
|
38
|
+
items: {
|
|
39
|
+
type: string;
|
|
40
|
+
};
|
|
41
|
+
description: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
required: string[];
|
|
45
|
+
};
|
|
46
|
+
output: {
|
|
47
|
+
schema: {
|
|
48
|
+
type: string;
|
|
49
|
+
};
|
|
50
|
+
render(args: any, value: any): {
|
|
51
|
+
type: string;
|
|
52
|
+
text: any;
|
|
53
|
+
}[];
|
|
54
|
+
};
|
|
55
|
+
execute(args: any, execContext: any): Promise<Record<string, any>>;
|
|
56
|
+
};
|
package/lib/tools/issue.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
function normalizeOutput(data) {
|
|
2
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
3
|
+
return data;
|
|
4
|
+
}
|
|
5
|
+
if (Array.isArray(data)) {
|
|
6
|
+
return { items: data, count: data.length };
|
|
7
|
+
}
|
|
8
|
+
return { result: data !== undefined && data !== null ? data : '' };
|
|
9
|
+
}
|
|
2
10
|
export function createIssueTool(executor) {
|
|
3
|
-
return
|
|
11
|
+
return {
|
|
4
12
|
name: 'github_issue',
|
|
5
13
|
description: '管理 GitHub Issues。支持查看 Issue 列表、详情、创建新 Issue、追加评论以及关闭 Issue。',
|
|
6
14
|
parameters: {
|
|
@@ -43,18 +51,33 @@ export function createIssueTool(executor) {
|
|
|
43
51
|
output: {
|
|
44
52
|
schema: { type: 'object' },
|
|
45
53
|
render(args, value) {
|
|
46
|
-
|
|
54
|
+
if (!value)
|
|
55
|
+
return [{ type: 'text', text: '无返回内容' }];
|
|
56
|
+
if (typeof value.result === 'string') {
|
|
57
|
+
return [{ type: 'text', text: value.result }];
|
|
58
|
+
}
|
|
59
|
+
if (Array.isArray(value.items)) {
|
|
60
|
+
return [{ type: 'text', text: JSON.stringify(value.items, null, 2) }];
|
|
61
|
+
}
|
|
62
|
+
return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
|
|
47
63
|
},
|
|
48
64
|
},
|
|
49
65
|
async execute(args, execContext) {
|
|
50
66
|
const cwd = execContext?.cwd || process.cwd();
|
|
51
|
-
|
|
67
|
+
let repoTarget = args.repo;
|
|
68
|
+
if (!repoTarget) {
|
|
69
|
+
const meta = await executor.getRepoMetadata(cwd);
|
|
70
|
+
if (meta?.nameWithOwner) {
|
|
71
|
+
repoTarget = meta.nameWithOwner;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const repoArgs = repoTarget ? ['-R', repoTarget] : [];
|
|
52
75
|
switch (args.action) {
|
|
53
76
|
case 'list': {
|
|
54
77
|
const res = await executor.run(['issue', 'list', ...repoArgs, '--json', 'number,title,state,author,labels,updatedAt', '-L', '20'], { cwd });
|
|
55
78
|
if (!res.ok)
|
|
56
79
|
throw new Error(res.error);
|
|
57
|
-
return res.data || res.rawOutput;
|
|
80
|
+
return normalizeOutput(res.data || res.rawOutput);
|
|
58
81
|
}
|
|
59
82
|
case 'view': {
|
|
60
83
|
if (!args.issue_number)
|
|
@@ -62,7 +85,7 @@ export function createIssueTool(executor) {
|
|
|
62
85
|
const res = await executor.run(['issue', 'view', String(args.issue_number), ...repoArgs, '--json', 'number,title,body,state,author,labels,assignees,comments,url'], { cwd });
|
|
63
86
|
if (!res.ok)
|
|
64
87
|
throw new Error(res.error);
|
|
65
|
-
return res.data || res.rawOutput;
|
|
88
|
+
return normalizeOutput(res.data || res.rawOutput);
|
|
66
89
|
}
|
|
67
90
|
case 'create': {
|
|
68
91
|
const cmd = ['issue', 'create', ...repoArgs, '--title', args.title || '', '--body', args.body || ''];
|
|
@@ -105,5 +128,5 @@ export function createIssueTool(executor) {
|
|
|
105
128
|
throw new Error(`未知的 Issue 操作: ${args.action}`);
|
|
106
129
|
}
|
|
107
130
|
},
|
|
108
|
-
}
|
|
131
|
+
};
|
|
109
132
|
}
|
package/lib/tools/pr.d.ts
CHANGED
|
@@ -1,2 +1,60 @@
|
|
|
1
1
|
import type { GhExecutor } from '../executor.js';
|
|
2
|
-
export declare function createPrTool(executor: GhExecutor):
|
|
2
|
+
export declare function createPrTool(executor: GhExecutor): {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
type: string;
|
|
7
|
+
properties: {
|
|
8
|
+
action: {
|
|
9
|
+
type: string;
|
|
10
|
+
enum: string[];
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
pr_number: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
repo: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
title: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
body: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
base: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
draft: {
|
|
34
|
+
type: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
review_decision: {
|
|
38
|
+
type: string;
|
|
39
|
+
enum: string[];
|
|
40
|
+
description: string;
|
|
41
|
+
};
|
|
42
|
+
merge_method: {
|
|
43
|
+
type: string;
|
|
44
|
+
enum: string[];
|
|
45
|
+
description: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
required: string[];
|
|
49
|
+
};
|
|
50
|
+
output: {
|
|
51
|
+
schema: {
|
|
52
|
+
type: string;
|
|
53
|
+
};
|
|
54
|
+
render(args: any, value: any): {
|
|
55
|
+
type: string;
|
|
56
|
+
text: any;
|
|
57
|
+
}[];
|
|
58
|
+
};
|
|
59
|
+
execute(args: any, execContext: any): Promise<Record<string, any>>;
|
|
60
|
+
};
|
package/lib/tools/pr.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
function normalizeOutput(data) {
|
|
2
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
3
|
+
return data;
|
|
4
|
+
}
|
|
5
|
+
if (Array.isArray(data)) {
|
|
6
|
+
return { items: data, count: data.length };
|
|
7
|
+
}
|
|
8
|
+
return { result: data !== undefined && data !== null ? data : '' };
|
|
9
|
+
}
|
|
2
10
|
export function createPrTool(executor) {
|
|
3
|
-
return
|
|
11
|
+
return {
|
|
4
12
|
name: 'github_pr',
|
|
5
13
|
description: '管理与审查 GitHub Pull Request (PR)。支持查看列表、详情、差异比对、CI 检查状态、代码审查与合并操作。默认针对当前工作区关联的仓库。',
|
|
6
14
|
parameters: {
|
|
@@ -51,39 +59,54 @@ export function createPrTool(executor) {
|
|
|
51
59
|
output: {
|
|
52
60
|
schema: { type: 'object' },
|
|
53
61
|
render(args, value) {
|
|
54
|
-
|
|
62
|
+
if (!value)
|
|
63
|
+
return [{ type: 'text', text: '无返回内容' }];
|
|
64
|
+
if (typeof value.result === 'string') {
|
|
65
|
+
return [{ type: 'text', text: value.result }];
|
|
66
|
+
}
|
|
67
|
+
if (Array.isArray(value.items)) {
|
|
68
|
+
return [{ type: 'text', text: JSON.stringify(value.items, null, 2) }];
|
|
69
|
+
}
|
|
70
|
+
return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
|
|
55
71
|
},
|
|
56
72
|
},
|
|
57
73
|
async execute(args, execContext) {
|
|
58
74
|
const cwd = execContext?.cwd || process.cwd();
|
|
59
|
-
|
|
75
|
+
let repoTarget = args.repo;
|
|
76
|
+
if (!repoTarget) {
|
|
77
|
+
const meta = await executor.getRepoMetadata(cwd);
|
|
78
|
+
if (meta?.nameWithOwner) {
|
|
79
|
+
repoTarget = meta.nameWithOwner;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const repoArgs = repoTarget ? ['-R', repoTarget] : [];
|
|
60
83
|
switch (args.action) {
|
|
61
84
|
case 'list': {
|
|
62
85
|
const res = await executor.run(['pr', 'list', ...repoArgs, '--json', 'number,title,author,headRefName,isDraft,state,updatedAt', '-L', '20'], { cwd });
|
|
63
86
|
if (!res.ok)
|
|
64
87
|
throw new Error(res.error);
|
|
65
|
-
return res.data || res.rawOutput;
|
|
88
|
+
return normalizeOutput(res.data || res.rawOutput);
|
|
66
89
|
}
|
|
67
90
|
case 'view': {
|
|
68
91
|
const target = args.pr_number ? [String(args.pr_number)] : [];
|
|
69
92
|
const res = await executor.run(['pr', 'view', ...target, ...repoArgs, '--json', 'number,title,body,state,author,labels,assignees,reviewDecision,mergeable,statusCheckRollup,url'], { cwd });
|
|
70
93
|
if (!res.ok)
|
|
71
94
|
throw new Error(res.error);
|
|
72
|
-
return res.data || res.rawOutput;
|
|
95
|
+
return normalizeOutput(res.data || res.rawOutput);
|
|
73
96
|
}
|
|
74
97
|
case 'checks': {
|
|
75
98
|
const target = args.pr_number ? [String(args.pr_number)] : [];
|
|
76
99
|
const res = await executor.run(['pr', 'checks', ...target, ...repoArgs, '--json', 'name,state,conclusion,detailsUrl'], { cwd });
|
|
77
100
|
if (!res.ok)
|
|
78
101
|
throw new Error(res.error);
|
|
79
|
-
return res.data || res.rawOutput;
|
|
102
|
+
return normalizeOutput(res.data || res.rawOutput);
|
|
80
103
|
}
|
|
81
104
|
case 'diff': {
|
|
82
105
|
const target = args.pr_number ? [String(args.pr_number)] : [];
|
|
83
106
|
const res = await executor.run(['pr', 'diff', ...target, ...repoArgs], { cwd });
|
|
84
107
|
if (!res.ok)
|
|
85
108
|
throw new Error(res.error);
|
|
86
|
-
return res.rawOutput || '无差异内容';
|
|
109
|
+
return { result: res.rawOutput || '无差异内容' };
|
|
87
110
|
}
|
|
88
111
|
case 'create': {
|
|
89
112
|
const createCmd = ['pr', 'create', ...repoArgs, '--title', args.title || '', '--body', args.body || ''];
|
|
@@ -126,5 +149,5 @@ export function createPrTool(executor) {
|
|
|
126
149
|
throw new Error(`未知的 PR 操作: ${args.action}`);
|
|
127
150
|
}
|
|
128
151
|
},
|
|
129
|
-
}
|
|
152
|
+
};
|
|
130
153
|
}
|
package/lib/tools/repo.d.ts
CHANGED
|
@@ -1,2 +1,38 @@
|
|
|
1
1
|
import type { GhExecutor } from '../executor.js';
|
|
2
|
-
export declare function createRepoTool(executor: GhExecutor):
|
|
2
|
+
export declare function createRepoTool(executor: GhExecutor): {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
type: string;
|
|
7
|
+
properties: {
|
|
8
|
+
action: {
|
|
9
|
+
type: string;
|
|
10
|
+
enum: string[];
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
repo: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
query: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
limit: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
required: string[];
|
|
27
|
+
};
|
|
28
|
+
output: {
|
|
29
|
+
schema: {
|
|
30
|
+
type: string;
|
|
31
|
+
};
|
|
32
|
+
render(args: any, value: any): {
|
|
33
|
+
type: string;
|
|
34
|
+
text: any;
|
|
35
|
+
}[];
|
|
36
|
+
};
|
|
37
|
+
execute(args: any, execContext: any): Promise<Record<string, any>>;
|
|
38
|
+
};
|