@zhin.js/adapter-github 1.0.1 → 1.1.2

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.
Files changed (113) hide show
  1. package/CHANGELOG.md +1212 -0
  2. package/README.md +73 -195
  3. package/adapters/github/index.js +51 -0
  4. package/adapters/github/index.ts +59 -0
  5. package/agents/github/agent.json +20 -0
  6. package/agents/github/boundaries.md +3 -0
  7. package/agents/github/conventions.md +3 -0
  8. package/agents/github/skills/github-account/SKILL.md +20 -0
  9. package/agents/github/skills/github-account/handlers.js +116 -0
  10. package/agents/github/skills/github-account/handlers.ts +136 -0
  11. package/agents/github/skills/github-account/tools/bind/index.js +13 -0
  12. package/agents/github/skills/github-account/tools/bind/index.ts +13 -0
  13. package/agents/github/skills/github-account/tools/install/index.js +13 -0
  14. package/agents/github/skills/github-account/tools/install/index.ts +13 -0
  15. package/agents/github/skills/github-account/tools/unbind/index.js +13 -0
  16. package/agents/github/skills/github-account/tools/unbind/index.ts +13 -0
  17. package/agents/github/skills/github-account/tools/whoami/index.js +13 -0
  18. package/agents/github/skills/github-account/tools/whoami/index.ts +13 -0
  19. package/agents/github/skills/github-cli/SKILL.md +47 -0
  20. package/agents/github/skills/github-repository/SKILL.md +21 -0
  21. package/agents/github/skills/github-repository/handlers.js +77 -0
  22. package/agents/github/skills/github-repository/handlers.ts +113 -0
  23. package/agents/github/skills/github-repository/tools/create_pr/index.js +20 -0
  24. package/agents/github/skills/github-repository/tools/create_pr/index.ts +20 -0
  25. package/agents/github/skills/github-repository/tools/patch_file/index.js +19 -0
  26. package/agents/github/skills/github-repository/tools/patch_file/index.ts +19 -0
  27. package/agents/github/skills/github-repository/tools/prepare_workspace/index.js +15 -0
  28. package/agents/github/skills/github-repository/tools/prepare_workspace/index.ts +15 -0
  29. package/agents/github/skills/github-repository/tools/push_branch/index.js +18 -0
  30. package/agents/github/skills/github-repository/tools/push_branch/index.ts +18 -0
  31. package/agents/github/skills/github-subscriptions/SKILL.md +20 -0
  32. package/agents/github/skills/github-subscriptions/handlers.js +101 -0
  33. package/agents/github/skills/github-subscriptions/handlers.ts +119 -0
  34. package/agents/github/skills/github-subscriptions/tools/star/index.js +16 -0
  35. package/agents/github/skills/github-subscriptions/tools/star/index.ts +16 -0
  36. package/agents/github/skills/github-subscriptions/tools/subscribe/index.js +16 -0
  37. package/agents/github/skills/github-subscriptions/tools/subscribe/index.ts +16 -0
  38. package/agents/github/skills/github-subscriptions/tools/subscriptions/index.js +13 -0
  39. package/agents/github/skills/github-subscriptions/tools/subscriptions/index.ts +13 -0
  40. package/agents/github/skills/github-subscriptions/tools/unsubscribe/index.js +15 -0
  41. package/agents/github/skills/github-subscriptions/tools/unsubscribe/index.ts +15 -0
  42. package/agents/github/system.md +3 -0
  43. package/commands/github/endpoint/add/[id]/index.js +3 -0
  44. package/commands/github/endpoint/add/[id]/index.ts +3 -0
  45. package/commands/github/endpoint/definition.js +23 -0
  46. package/commands/github/endpoint/definition.ts +23 -0
  47. package/commands/github/endpoint/list/index.js +3 -0
  48. package/commands/github/endpoint/list/index.ts +3 -0
  49. package/commands/github/endpoint/remove/[id]/index.js +3 -0
  50. package/commands/github/endpoint/remove/[id]/index.ts +3 -0
  51. package/lib/client.d.ts +36 -0
  52. package/lib/client.js +44 -0
  53. package/lib/endpoint.d.ts +32 -22
  54. package/lib/endpoint.js +125 -145
  55. package/lib/gh-client.d.ts +73 -1
  56. package/lib/gh-client.js +99 -1
  57. package/lib/github-channel-context.d.ts +16 -0
  58. package/lib/github-channel-context.js +31 -0
  59. package/lib/github-runtime-state.d.ts +1 -0
  60. package/lib/github-runtime-state.js +6 -0
  61. package/lib/index.d.ts +7 -32
  62. package/lib/index.js +7 -385
  63. package/lib/oauth-users.d.ts +33 -0
  64. package/lib/oauth-users.js +38 -0
  65. package/lib/protocol.d.ts +72 -0
  66. package/lib/protocol.js +281 -0
  67. package/lib/types.d.ts +0 -18
  68. package/lib/types.js +2 -4
  69. package/lib/webhook.d.ts +14 -0
  70. package/lib/webhook.js +88 -0
  71. package/lib/workspace-manager.d.ts +21 -0
  72. package/lib/workspace-manager.js +154 -0
  73. package/package.json +93 -23
  74. package/plugin.js +38 -0
  75. package/prompt-sections/platform/index.js +16 -0
  76. package/prompt-sections/platform/index.ts +16 -0
  77. package/schema.json +137 -0
  78. package/src/client.ts +62 -0
  79. package/src/endpoint.ts +145 -150
  80. package/src/gh-client.ts +131 -0
  81. package/src/github-channel-context.ts +46 -0
  82. package/src/github-runtime-state.ts +7 -0
  83. package/src/index.ts +48 -431
  84. package/src/oauth-users.ts +47 -0
  85. package/src/protocol.ts +372 -0
  86. package/src/types.ts +0 -22
  87. package/src/webhook.ts +130 -0
  88. package/src/workspace-manager.ts +168 -0
  89. package/lib/adapter.d.ts +0 -64
  90. package/lib/adapter.d.ts.map +0 -1
  91. package/lib/adapter.js +0 -416
  92. package/lib/adapter.js.map +0 -1
  93. package/lib/agent-prompt.d.ts +0 -3
  94. package/lib/agent-prompt.d.ts.map +0 -1
  95. package/lib/agent-prompt.js +0 -82
  96. package/lib/agent-prompt.js.map +0 -1
  97. package/lib/endpoint.d.ts.map +0 -1
  98. package/lib/endpoint.js.map +0 -1
  99. package/lib/gh-client.d.ts.map +0 -1
  100. package/lib/gh-client.js.map +0 -1
  101. package/lib/index.d.ts.map +0 -1
  102. package/lib/index.js.map +0 -1
  103. package/lib/register-github-mcp.d.ts +0 -6
  104. package/lib/register-github-mcp.d.ts.map +0 -1
  105. package/lib/register-github-mcp.js +0 -35
  106. package/lib/register-github-mcp.js.map +0 -1
  107. package/lib/types.d.ts.map +0 -1
  108. package/lib/types.js.map +0 -1
  109. package/plugin.yml +0 -3
  110. package/skills/github/SKILL.md +0 -266
  111. package/src/adapter.ts +0 -448
  112. package/src/agent-prompt.ts +0 -99
  113. package/src/register-github-mcp.ts +0 -60
@@ -0,0 +1,136 @@
1
+ import { formatCompact } from '@zhin.js/logger';
2
+ import type { Message } from 'zhin.js';
3
+ import { getCurrentCommMessage } from '@zhin.js/agent/security';
4
+ import { GhClient } from '../../../../src/gh-client.js';
5
+ import type { GithubClient } from '../../../../src/client.js';
6
+
7
+ function oauthModel(client: GithubClient) {
8
+ const db = client.database as {
9
+ models?: Map<string, unknown>;
10
+ } | null | undefined;
11
+ return db?.models?.get('github_oauth_users') as {
12
+ select: () => { where: (q: object) => Promise<any[]> };
13
+ insert: (row: object) => Promise<void>;
14
+ delete: () => { where: (q: object) => Promise<void> };
15
+ } | undefined;
16
+ }
17
+
18
+ function depsLogger() {
19
+ return {
20
+ debug: (...args: unknown[]) => console.debug(...args),
21
+ warn: (...args: unknown[]) => console.warn(...args),
22
+ error: (...args: unknown[]) => console.error(...args),
23
+ };
24
+ }
25
+
26
+ export async function executeGithubBind(_args: Record<string, never>, client: GithubClient, commMessage?: Message) {
27
+ const log = depsLogger();
28
+ const msg = commMessage ?? getCurrentCommMessage();
29
+ if (!msg?.$adapter || !msg?.$sender?.id) return '❌ 无法获取当前用户信息';
30
+
31
+ const clientId = client.clientId;
32
+ if (!clientId) return '❌ Endpoint 未配置 GitHub App 或 App 无 client_id,无法进行账号绑定';
33
+
34
+ const model = oauthModel(client);
35
+ if (!model) return '❌ 数据库未就绪';
36
+
37
+ const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
38
+ if (existing) {
39
+ return `⚠️ 你已绑定 GitHub 账号: ${existing.github_login}\n如需重新绑定,请先执行 github_unbind`;
40
+ }
41
+
42
+ try {
43
+ const host = client.host;
44
+ const codeResp = await GhClient.deviceFlowRequestCode(clientId, host);
45
+ const tokenPromise = GhClient.deviceFlowPollToken(
46
+ clientId, codeResp.device_code, codeResp.interval, codeResp.expires_in, host,
47
+ );
48
+
49
+ const replyMsg = [
50
+ '🔗 请在浏览器中打开以下链接进行授权:',
51
+ ` ${codeResp.verification_uri}`,
52
+ '',
53
+ `📋 输入验证码: **${codeResp.user_code}**`,
54
+ '',
55
+ `⏳ 等待授权中…(${Math.floor(codeResp.expires_in / 60)} 分钟内有效)`,
56
+ ].join('\n');
57
+
58
+ tokenPromise.then(async (tokenData) => {
59
+ if (!tokenData) {
60
+ log.warn(formatCompact({ op: 'device_flow', ok: false, platform: msg.$adapter, sender: msg.$sender.id }));
61
+ return;
62
+ }
63
+
64
+ const userGh = new GhClient({ host, token: tokenData.access_token });
65
+ const authResult = await userGh.verifyAuth();
66
+ const login = authResult.ok ? authResult.user : 'unknown';
67
+
68
+ await model.insert({
69
+ id: Date.now(),
70
+ platform: msg.$adapter,
71
+ platform_uid: msg.$sender.id,
72
+ github_login: login,
73
+ access_token: tokenData.access_token,
74
+ created_at: Date.now(),
75
+ });
76
+ log.debug(formatCompact({ op: 'bind', platform: msg.$adapter, sender: msg.$sender.id, login }));
77
+
78
+ if (msg?.$reply) {
79
+ await msg.$reply(`✅ GitHub 账号绑定成功!\n👤 ${login}`);
80
+ }
81
+ }).catch((err) => {
82
+ log.error('GitHub Device Flow 错误:', err);
83
+ });
84
+
85
+ return replyMsg;
86
+ } catch (e: unknown) {
87
+ return `❌ Device Flow 启动失败: ${e instanceof Error ? e.message : String(e)}`;
88
+ }
89
+ }
90
+
91
+ export async function executeGithubUnbind(_args: Record<string, never>, client: GithubClient, commMessage?: Message) {
92
+ const msg = commMessage ?? getCurrentCommMessage();
93
+ if (!msg?.$adapter || !msg?.$sender?.id) return '❌ 无法获取当前用户信息';
94
+
95
+ const model = oauthModel(client);
96
+ if (!model) return '❌ 数据库未就绪';
97
+
98
+ const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
99
+ if (!existing) return '📭 你尚未绑定 GitHub 账号';
100
+
101
+ await model.delete().where({ id: existing.id });
102
+ return `✅ 已解除 GitHub 账号绑定: ${existing.github_login}`;
103
+ }
104
+
105
+ export async function executeGithubWhoami(_args: Record<string, never>, client: GithubClient, commMessage?: Message) {
106
+ const msg = commMessage ?? getCurrentCommMessage();
107
+ if (!msg?.$adapter || !msg?.$sender?.id) return '❌ 无法获取当前用户信息';
108
+
109
+ const model = oauthModel(client);
110
+ if (!model) return '❌ 数据库未就绪';
111
+
112
+ const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
113
+ if (!existing) return '📭 你尚未绑定 GitHub 账号\n🔗 使用 github_bind 绑定你的账号';
114
+
115
+ const userGh = new GhClient({ host: client.host, token: existing.access_token });
116
+ const auth = await userGh.verifyAuth();
117
+ if (auth.ok) {
118
+ return `👤 已绑定 GitHub 账号: ${auth.user}\n📅 绑定时间: ${new Date(existing.created_at).toLocaleString('zh-CN')}`;
119
+ }
120
+ return `⚠️ 已绑定账号 ${existing.github_login},但 Token 已失效\n🔗 请执行 github_unbind 后重新 github_bind`;
121
+ }
122
+
123
+ export async function executeGithubInstall(client: GithubClient) {
124
+ const slug = client.appSlug;
125
+ if (!slug) return '❌ Endpoint 未配置 GitHub App';
126
+ const host = client.host || 'github.com';
127
+ const installations = client.installations;
128
+ let msg = `🔗 请点击以下链接安装 GitHub App 到你的仓库:\n https://${host}/apps/${slug}/installations/new`;
129
+ if (installations.length) {
130
+ msg += `\n\n📋 当前已安装 (${installations.length}):`;
131
+ for (const inst of installations) {
132
+ msg += `\n • ${inst.account.login} (${inst.account.type})`;
133
+ }
134
+ }
135
+ return msg;
136
+ }
@@ -0,0 +1,13 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { executeGithubBind } from '../../handlers.js';
5
+ export default defineAgentTool({
6
+ description: '绑定你的 GitHub 账号 — 使用 Device Flow 授权,无需输入密码',
7
+ adapter: 'github',
8
+ inputSchema: z.object({}),
9
+ tags: ['github'],
10
+ async execute(input, context) {
11
+ return executeGithubBind({}, context.$client);
12
+ },
13
+ });
@@ -0,0 +1,13 @@
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
+ import { z } from 'zod';
3
+ import { executeGithubBind } from '../../handlers.js';
4
+
5
+ export default defineAgentTool<{}>({
6
+ description: '绑定你的 GitHub 账号 — 使用 Device Flow 授权,无需输入密码',
7
+ adapter: 'github',
8
+ inputSchema: z.object({}),
9
+ tags: ['github'],
10
+ async execute(input, context) {
11
+ return executeGithubBind({}, context.$client);
12
+ },
13
+ });
@@ -0,0 +1,13 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { executeGithubInstall } from '../../handlers.js';
5
+ export default defineAgentTool({
6
+ description: '获取安装 GitHub App 的链接 — 安装后 Endpoint 可以访问你的仓库,你也可以使用更多功能',
7
+ adapter: 'github',
8
+ inputSchema: z.object({}),
9
+ tags: ['github'],
10
+ async execute(input, context) {
11
+ return executeGithubInstall(context.$client);
12
+ },
13
+ });
@@ -0,0 +1,13 @@
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
+ import { z } from 'zod';
3
+ import { executeGithubInstall } from '../../handlers.js';
4
+
5
+ export default defineAgentTool<{}>({
6
+ description: '获取安装 GitHub App 的链接 — 安装后 Endpoint 可以访问你的仓库,你也可以使用更多功能',
7
+ adapter: 'github',
8
+ inputSchema: z.object({}),
9
+ tags: ['github'],
10
+ async execute(input, context) {
11
+ return executeGithubInstall(context.$client);
12
+ },
13
+ });
@@ -0,0 +1,13 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { executeGithubUnbind } from '../../handlers.js';
5
+ export default defineAgentTool({
6
+ description: '解除你绑定的 GitHub 账号',
7
+ adapter: 'github',
8
+ inputSchema: z.object({}),
9
+ tags: ['github'],
10
+ async execute(input, context) {
11
+ return executeGithubUnbind({}, context.$client);
12
+ },
13
+ });
@@ -0,0 +1,13 @@
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
+ import { z } from 'zod';
3
+ import { executeGithubUnbind } from '../../handlers.js';
4
+
5
+ export default defineAgentTool<{}>({
6
+ description: '解除你绑定的 GitHub 账号',
7
+ adapter: 'github',
8
+ inputSchema: z.object({}),
9
+ tags: ['github'],
10
+ async execute(input, context) {
11
+ return executeGithubUnbind({}, context.$client);
12
+ },
13
+ });
@@ -0,0 +1,13 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { executeGithubWhoami } from '../../handlers.js';
5
+ export default defineAgentTool({
6
+ description: '查看你绑定的 GitHub 账号信息',
7
+ adapter: 'github',
8
+ inputSchema: z.object({}),
9
+ tags: ['github'],
10
+ async execute(input, context) {
11
+ return executeGithubWhoami({}, context.$client);
12
+ },
13
+ });
@@ -0,0 +1,13 @@
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
+ import { z } from 'zod';
3
+ import { executeGithubWhoami } from '../../handlers.js';
4
+
5
+ export default defineAgentTool<{}>({
6
+ description: '查看你绑定的 GitHub 账号信息',
7
+ adapter: 'github',
8
+ inputSchema: z.object({}),
9
+ tags: ['github'],
10
+ async execute(input, context) {
11
+ return executeGithubWhoami({}, context.$client);
12
+ },
13
+ });
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: github-cli
3
+ platforms:
4
+ - github
5
+ description: 使用 gh CLI 处理内置 GitHub Tool 未覆盖的 Issue、PR、Release、Workflow、搜索和 API 操作。
6
+ keywords:
7
+ - "github"
8
+ - "gh"
9
+ - "issue"
10
+ - "pull request"
11
+ - "release"
12
+ - "workflow"
13
+ - "search"
14
+ - "api"
15
+ ---
16
+
17
+ # GitHub CLI
18
+
19
+ 只有内置 GitHub Skill 不覆盖任务时才加载本 Skill。当前进程通过 `GH_TOKEN` 提供 gh CLI 身份;先执行 `gh auth status` 核对身份和权限。仓库参数统一使用 `owner/repo`。
20
+
21
+ ## 常用命令
22
+
23
+ ```bash
24
+ gh issue list -R owner/repo --state open --limit 20 --json number,title,state,labels,assignees
25
+ gh issue view 123 -R owner/repo --json number,title,body,state,labels,assignees
26
+ gh issue create -R owner/repo --title "标题" --body "正文"
27
+ gh pr list -R owner/repo --state open --limit 20 --json number,title,state,author,mergeable
28
+ gh pr view 456 -R owner/repo --json number,title,body,state,author,mergeable,reviewDecision
29
+ gh pr checks 456 -R owner/repo
30
+ gh pr diff 456 -R owner/repo
31
+ gh release list -R owner/repo --limit 10
32
+ gh run list -R owner/repo --limit 10 --json databaseId,displayTitle,status,conclusion
33
+ gh workflow list -R owner/repo
34
+ gh search issues "关键词" --repo owner/repo --limit 10 --json number,title,state
35
+ gh search code "函数名" --repo owner/repo --limit 10 --json path,textMatches
36
+ ```
37
+
38
+ 需要其他 API 时使用 `gh api` 或 `gh api graphql`。任何创建、修改、合并、关闭、重跑或发布操作都属于外部副作用,必须遵守执行工具的审批策略。
39
+
40
+ ## 输出控制
41
+
42
+ - 列表必须使用 `--limit`,默认不超过 20 条。
43
+ - 使用 `--json` 与 `--jq` 只保留任务需要的字段。
44
+ - 正文最多读取 2000 字符,diff 最多读取 200 行。
45
+ - 单次调用输出应小于 15KB。
46
+
47
+ 遇到 401 检查身份与 Token;404 同时检查资源存在性和权限;422 检查必填参数。不要把命令失败推断成资源不存在。
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: github-repository
3
+ platforms:
4
+ - github
5
+ description: GitHub 工作区准备、文件补丁、分支推送和 PR 创建能力。
6
+ keywords:
7
+ - "github"
8
+ - "repository"
9
+ - "patch"
10
+ - "branch"
11
+ - "pull request"
12
+ tools:
13
+ - "prepare_workspace"
14
+ - "patch_file"
15
+ - "create_pr"
16
+ - "push_branch"
17
+ ---
18
+
19
+ # github-repository
20
+
21
+ 这些 Tool 使用 GitHub App Installation Token,以 Bot 身份写仓库。按 `prepare_workspace`、`patch_file`、`push_branch`、`create_pr` 的顺序操作,每一步使用上一步返回的稳定引用。Issue 线程创建新分支后开 PR;PR 线程推送到现有 head 分支。不要改用人身份 PAT 的 MCP 工具完成 Bot 写入。推送和创建 PR 前复核仓库、base/head 分支与变更内容,并遵守 HITL 审批。
@@ -0,0 +1,77 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { getCurrentCommMessage } from '@zhin.js/agent/security';
3
+ import { formatChannelContext, parseMessageChannel, resolveWorkspaceBranch, } from "../../../../lib/github-channel-context.js";
4
+ function requireBotGh(client) {
5
+ const gh = client.api;
6
+ if (!gh?.isAppAuth) {
7
+ throw new Error('需要 GitHub App 认证(app_id + private_key),Bot 写操作不可用');
8
+ }
9
+ return gh;
10
+ }
11
+ function resolveCommMessage(commMessage) {
12
+ const msg = commMessage ?? getCurrentCommMessage();
13
+ if (!msg)
14
+ throw new Error('无 IM 上下文,请在 Issue/PR 评论线程内调用');
15
+ return msg;
16
+ }
17
+ function resolveChannel(msg, repo) {
18
+ const ctx = parseMessageChannel(msg);
19
+ if (ctx)
20
+ return ctx;
21
+ if (repo) {
22
+ throw new Error(`请从 GitHub Issue/PR 频道调用,或提供完整 channel(当前仅 repo=${repo})`);
23
+ }
24
+ throw new Error('无法解析 GitHub 频道(需要 Issue/PR 评论上下文)');
25
+ }
26
+ export async function executeGithubPrepareWorkspace(args, client, commMessage) {
27
+ const msg = resolveCommMessage(commMessage);
28
+ const ctx = resolveChannel(msg, args.repo);
29
+ const gh = requireBotGh(client);
30
+ const wm = client.workspaceManager;
31
+ const { branch, base } = await resolveWorkspaceBranch(gh, ctx);
32
+ const repoPath = await wm.checkoutBranch(ctx.repo, branch, base);
33
+ return [
34
+ `✅ 工作区就绪`,
35
+ `📁 ${repoPath}`,
36
+ `🌿 分支 \`${branch}\`(base: \`${base}\`)`,
37
+ `📍 ${formatChannelContext(ctx)}`,
38
+ ].join('\n');
39
+ }
40
+ export async function executeGithubPatchFile(args, client, commMessage) {
41
+ const msg = resolveCommMessage(commMessage);
42
+ const ctx = resolveChannel(msg, args.repo);
43
+ const gh = requireBotGh(client);
44
+ const { branch } = args.branch
45
+ ? { branch: args.branch }
46
+ : await resolveWorkspaceBranch(gh, ctx);
47
+ const existing = await gh.getFileContent(ctx.repo, args.path, branch);
48
+ const sha = existing.ok ? existing.data.sha : undefined;
49
+ const r = await gh.createOrUpdateFile(ctx.repo, args.path, args.content, args.message, { branch, sha });
50
+ if (!r.ok) {
51
+ return `❌ 更新文件失败: ${JSON.stringify(r.data)}`;
52
+ }
53
+ const url = r.data.content?.html_url ?? r.data.commit?.html_url ?? '';
54
+ return `✅ 已更新 \`${args.path}\` @ \`${branch}\`${url ? `\n🔗 ${url}` : ''}`;
55
+ }
56
+ export async function executeGithubPushBranch(args, client, commMessage) {
57
+ const msg = resolveCommMessage(commMessage);
58
+ const ctx = resolveChannel(msg, args.repo);
59
+ const gh = requireBotGh(client);
60
+ const wm = client.workspaceManager;
61
+ const branch = args.branch ?? (await resolveWorkspaceBranch(gh, ctx)).branch;
62
+ const result = await wm.commitAndPush(ctx.repo, branch, args.message);
63
+ return `✅ ${result}\n📍 ${ctx.repo} @ \`${branch}\``;
64
+ }
65
+ export async function executeGithubCreatePr(args, client, commMessage) {
66
+ const msg = resolveCommMessage(commMessage);
67
+ const ctx = resolveChannel(msg, args.repo);
68
+ const gh = requireBotGh(client);
69
+ const resolved = await resolveWorkspaceBranch(gh, ctx);
70
+ const head = args.head ?? resolved.branch;
71
+ const base = args.base ?? resolved.base;
72
+ const r = await gh.createPullRequest(ctx.repo, args.title, head, base, args.body);
73
+ if (!r.ok) {
74
+ return `❌ 创建 PR 失败: ${JSON.stringify(r.data)}`;
75
+ }
76
+ return `✅ PR #${r.data.number} 已创建\n🔗 ${r.data.html_url}`;
77
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * GitHub App Bot workflow handlers (Installation Token identity).
3
+ */
4
+ import type { Message } from 'zhin.js';
5
+ import { getCurrentCommMessage } from '@zhin.js/agent/security';
6
+ import {
7
+ formatChannelContext,
8
+ parseMessageChannel,
9
+ resolveWorkspaceBranch,
10
+ } from '../../../../src/github-channel-context.js';
11
+ import type { GithubClient } from '../../../../src/client.js';
12
+
13
+ function requireBotGh(client: GithubClient) {
14
+ const gh = client.api;
15
+ if (!gh?.isAppAuth) {
16
+ throw new Error('需要 GitHub App 认证(app_id + private_key),Bot 写操作不可用');
17
+ }
18
+ return gh;
19
+ }
20
+
21
+ function resolveCommMessage(commMessage?: Message): Message {
22
+ const msg = commMessage ?? getCurrentCommMessage();
23
+ if (!msg) throw new Error('无 IM 上下文,请在 Issue/PR 评论线程内调用');
24
+ return msg;
25
+ }
26
+
27
+ function resolveChannel(msg: Message, repo?: string) {
28
+ const ctx = parseMessageChannel(msg);
29
+ if (ctx) return ctx;
30
+ if (repo) {
31
+ throw new Error(`请从 GitHub Issue/PR 频道调用,或提供完整 channel(当前仅 repo=${repo})`);
32
+ }
33
+ throw new Error('无法解析 GitHub 频道(需要 Issue/PR 评论上下文)');
34
+ }
35
+
36
+ export async function executeGithubPrepareWorkspace(
37
+ args: { repo?: string },
38
+ client: GithubClient,
39
+ commMessage?: Message,
40
+ ) {
41
+ const msg = resolveCommMessage(commMessage);
42
+ const ctx = resolveChannel(msg, args.repo);
43
+ const gh = requireBotGh(client);
44
+ const wm = client.workspaceManager;
45
+ const { branch, base } = await resolveWorkspaceBranch(gh, ctx);
46
+ const repoPath = await wm.checkoutBranch(ctx.repo, branch, base);
47
+ return [
48
+ `✅ 工作区就绪`,
49
+ `📁 ${repoPath}`,
50
+ `🌿 分支 \`${branch}\`(base: \`${base}\`)`,
51
+ `📍 ${formatChannelContext(ctx)}`,
52
+ ].join('\n');
53
+ }
54
+
55
+ export async function executeGithubPatchFile(
56
+ args: { repo?: string; path: string; content: string; message: string; branch?: string },
57
+ client: GithubClient,
58
+ commMessage?: Message,
59
+ ) {
60
+ const msg = resolveCommMessage(commMessage);
61
+ const ctx = resolveChannel(msg, args.repo);
62
+ const gh = requireBotGh(client);
63
+ const { branch } = args.branch
64
+ ? { branch: args.branch }
65
+ : await resolveWorkspaceBranch(gh, ctx);
66
+
67
+ const existing = await gh.getFileContent(ctx.repo, args.path, branch);
68
+ const sha = existing.ok ? existing.data.sha : undefined;
69
+ const r = await gh.createOrUpdateFile(
70
+ ctx.repo,
71
+ args.path,
72
+ args.content,
73
+ args.message,
74
+ { branch, sha },
75
+ );
76
+ if (!r.ok) {
77
+ return `❌ 更新文件失败: ${JSON.stringify(r.data)}`;
78
+ }
79
+ const url = r.data.content?.html_url ?? r.data.commit?.html_url ?? '';
80
+ return `✅ 已更新 \`${args.path}\` @ \`${branch}\`${url ? `\n🔗 ${url}` : ''}`;
81
+ }
82
+
83
+ export async function executeGithubPushBranch(
84
+ args: { repo?: string; branch?: string; message: string },
85
+ client: GithubClient,
86
+ commMessage?: Message,
87
+ ) {
88
+ const msg = resolveCommMessage(commMessage);
89
+ const ctx = resolveChannel(msg, args.repo);
90
+ const gh = requireBotGh(client);
91
+ const wm = client.workspaceManager;
92
+ const branch = args.branch ?? (await resolveWorkspaceBranch(gh, ctx)).branch;
93
+ const result = await wm.commitAndPush(ctx.repo, branch, args.message);
94
+ return `✅ ${result}\n📍 ${ctx.repo} @ \`${branch}\``;
95
+ }
96
+
97
+ export async function executeGithubCreatePr(
98
+ args: { repo?: string; title: string; body?: string; head?: string; base?: string },
99
+ client: GithubClient,
100
+ commMessage?: Message,
101
+ ) {
102
+ const msg = resolveCommMessage(commMessage);
103
+ const ctx = resolveChannel(msg, args.repo);
104
+ const gh = requireBotGh(client);
105
+ const resolved = await resolveWorkspaceBranch(gh, ctx);
106
+ const head = args.head ?? resolved.branch;
107
+ const base = args.base ?? resolved.base;
108
+ const r = await gh.createPullRequest(ctx.repo, args.title, head, base, args.body);
109
+ if (!r.ok) {
110
+ return `❌ 创建 PR 失败: ${JSON.stringify(r.data)}`;
111
+ }
112
+ return `✅ PR #${r.data.number} 已创建\n🔗 ${r.data.html_url}`;
113
+ }
@@ -0,0 +1,20 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { executeGithubCreatePr } from '../../handlers.js';
5
+ export default defineAgentTool({
6
+ description: '以 GitHub App Bot 身份创建 Pull Request(需 HITL 确认;Issue 场景常用)',
7
+ adapter: 'github',
8
+ inputSchema: z.object({
9
+ repo: z.string().optional().describe('owner/repo'),
10
+ title: z.string().describe('PR 标题'),
11
+ body: z.string().optional().describe('PR 正文'),
12
+ head: z.string().optional().describe('head 分支,缺省为工作区分支'),
13
+ base: z.string().optional().describe('base 分支,缺省为仓库默认分支'),
14
+ }),
15
+ tags: ['github'],
16
+ requiresApproval: 'always',
17
+ async execute(input, context) {
18
+ return executeGithubCreatePr(input, context.$client);
19
+ },
20
+ });
@@ -0,0 +1,20 @@
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
+ import { z } from 'zod';
3
+ import { executeGithubCreatePr } from '../../handlers.js';
4
+
5
+ export default defineAgentTool<{ repo?: string; title: string; body?: string; head?: string; base?: string }>({
6
+ description: '以 GitHub App Bot 身份创建 Pull Request(需 HITL 确认;Issue 场景常用)',
7
+ adapter: 'github',
8
+ inputSchema: z.object({
9
+ repo: z.string().optional().describe('owner/repo'),
10
+ title: z.string().describe('PR 标题'),
11
+ body: z.string().optional().describe('PR 正文'),
12
+ head: z.string().optional().describe('head 分支,缺省为工作区分支'),
13
+ base: z.string().optional().describe('base 分支,缺省为仓库默认分支'),
14
+ }),
15
+ tags: ['github'],
16
+ requiresApproval: 'always',
17
+ async execute(input, context) {
18
+ return executeGithubCreatePr(input, context.$client);
19
+ },
20
+ });
@@ -0,0 +1,19 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { executeGithubPatchFile } from '../../handlers.js';
5
+ export default defineAgentTool({
6
+ description: '通过 Contents API 单文件更新(小改;Bot Installation Token 身份)',
7
+ adapter: 'github',
8
+ inputSchema: z.object({
9
+ repo: z.string().optional().describe('owner/repo'),
10
+ path: z.string().describe('仓库内文件路径'),
11
+ content: z.string().describe('新文件内容'),
12
+ message: z.string().describe('commit message'),
13
+ branch: z.string().optional().describe('目标分支,缺省从 Issue/PR 上下文推断'),
14
+ }),
15
+ tags: ['github'],
16
+ async execute(input, context) {
17
+ return executeGithubPatchFile(input, context.$client);
18
+ },
19
+ });
@@ -0,0 +1,19 @@
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
+ import { z } from 'zod';
3
+ import { executeGithubPatchFile } from '../../handlers.js';
4
+
5
+ export default defineAgentTool<{ repo?: string; path: string; content: string; message: string; branch?: string }>({
6
+ description: '通过 Contents API 单文件更新(小改;Bot Installation Token 身份)',
7
+ adapter: 'github',
8
+ inputSchema: z.object({
9
+ repo: z.string().optional().describe('owner/repo'),
10
+ path: z.string().describe('仓库内文件路径'),
11
+ content: z.string().describe('新文件内容'),
12
+ message: z.string().describe('commit message'),
13
+ branch: z.string().optional().describe('目标分支,缺省从 Issue/PR 上下文推断'),
14
+ }),
15
+ tags: ['github'],
16
+ async execute(input, context) {
17
+ return executeGithubPatchFile(input, context.$client);
18
+ },
19
+ });
@@ -0,0 +1,15 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { executeGithubPrepareWorkspace } from '../../handlers.js';
5
+ export default defineAgentTool({
6
+ description: 'Clone/fetch 托管工作区并 checkout 到 Issue/PR 对应分支(GitHub App Bot 身份)',
7
+ adapter: 'github',
8
+ inputSchema: z.object({
9
+ repo: z.string().optional().describe('owner/repo,缺省从当前 Issue/PR 频道推断'),
10
+ }),
11
+ tags: ['github'],
12
+ async execute(input, context) {
13
+ return executeGithubPrepareWorkspace(input, context.$client);
14
+ },
15
+ });
@@ -0,0 +1,15 @@
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
+ import { z } from 'zod';
3
+ import { executeGithubPrepareWorkspace } from '../../handlers.js';
4
+
5
+ export default defineAgentTool<{ repo?: string }>({
6
+ description: 'Clone/fetch 托管工作区并 checkout 到 Issue/PR 对应分支(GitHub App Bot 身份)',
7
+ adapter: 'github',
8
+ inputSchema: z.object({
9
+ repo: z.string().optional().describe('owner/repo,缺省从当前 Issue/PR 频道推断'),
10
+ }),
11
+ tags: ['github'],
12
+ async execute(input, context) {
13
+ return executeGithubPrepareWorkspace(input, context.$client);
14
+ },
15
+ });
@@ -0,0 +1,18 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { executeGithubPushBranch } from '../../handlers.js';
5
+ export default defineAgentTool({
6
+ description: '在托管工作区 git commit 并 push 到远程分支(需 HITL 确认;Bot 身份)',
7
+ adapter: 'github',
8
+ inputSchema: z.object({
9
+ repo: z.string().optional().describe('owner/repo'),
10
+ branch: z.string().optional().describe('分支名,缺省从上下文推断'),
11
+ message: z.string().describe('commit message'),
12
+ }),
13
+ tags: ['github'],
14
+ requiresApproval: 'always',
15
+ async execute(input, context) {
16
+ return executeGithubPushBranch(input, context.$client);
17
+ },
18
+ });