@shaohui_jin/git-cockpit-mcp-server 0.1.17 → 0.1.19

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.
@@ -1,1342 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/mcpServer.ts
4
- import { randomUUID } from "crypto";
5
- import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
6
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8
-
9
- // package.json
10
- var version = "0.1.17";
11
-
12
- // src/tools/handlers.ts
13
- import { z } from "zod";
14
- import {
15
- BackupManager,
16
- GitOperationError,
17
- PermissionError
18
- } from "@shaohui_jin/git-cockpit-core";
19
-
20
- // src/tools/format.ts
21
- var FILE_CONTENT_LIMIT = 64 * 1024;
22
- function wantsDetail(args) {
23
- return args?.detail === true;
24
- }
25
- function hasPath(args) {
26
- return typeof args?.path === "string" && args.path.trim().length > 0;
27
- }
28
- function asRecord(value) {
29
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
30
- }
31
- function stripDiffPatch(diff) {
32
- const d = asRecord(diff);
33
- if (!d || !("rawPatch" in d)) return diff;
34
- const { rawPatch: _raw, ...rest } = d;
35
- return { ...rest, detailAvailable: true };
36
- }
37
- function stripRehearseBodies(result) {
38
- const r = asRecord(result);
39
- if (!r) return result;
40
- const files = Array.isArray(r.conflictFiles) ? r.conflictFiles : [];
41
- const paths = files.map((f) => {
42
- const row = asRecord(f);
43
- return {
44
- path: row?.path ?? "",
45
- contentConflict: row?.contentConflict ?? true,
46
- hunks: []
47
- };
48
- });
49
- const first = typeof paths[0]?.path === "string" ? paths[0].path : "";
50
- return {
51
- ...r,
52
- conflictFiles: paths,
53
- detailAvailable: true,
54
- next: first ? [{ tool: "git_merge_rehearse", args: { into: r.into, from: r.from, path: first } }] : []
55
- };
56
- }
57
- function summarizeForAgent(tool, result, args = {}) {
58
- if (result == null) return result;
59
- const detail2 = wantsDetail(args);
60
- const pathSet = hasPath(args);
61
- if (tool === "git_diff" || tool === "git_stash_show") {
62
- if (detail2 || pathSet) return result;
63
- return stripDiffPatch(result);
64
- }
65
- if (tool === "git_show") {
66
- if (detail2 || pathSet) return result;
67
- const row = asRecord(result);
68
- if (!row) return result;
69
- return { ...row, diff: stripDiffPatch(row.diff) };
70
- }
71
- if (tool === "git_merge_rehearse") {
72
- if (detail2 || pathSet) return result;
73
- return stripRehearseBodies(result);
74
- }
75
- if (tool === "git_repo_overview") {
76
- if (detail2) return result;
77
- const row = asRecord(result);
78
- const repos = Array.isArray(row?.repos) ? row.repos : [];
79
- return {
80
- ...row,
81
- repos: repos.map((item) => {
82
- const rec = asRecord(item);
83
- if (!rec) return item;
84
- const { activity: _days, ...rest } = rec;
85
- return rest;
86
- })
87
- };
88
- }
89
- if (tool === "git_file_content") {
90
- if (detail2) return result;
91
- const row = asRecord(result);
92
- const content = typeof row?.content === "string" ? row.content : "";
93
- const bytes = Buffer.byteLength(content, "utf8");
94
- if (bytes <= FILE_CONTENT_LIMIT) return result;
95
- return {
96
- path: args.path,
97
- truncated: true,
98
- bytes,
99
- detailAvailable: true
100
- };
101
- }
102
- return result;
103
- }
104
- function formatResultForMcp(exec) {
105
- if (!exec.success) {
106
- const prefix = exec.error?.requiredApproval ? "\u9700\u8981\u5BA1\u6279" : "\u64CD\u4F5C\u5931\u8D25";
107
- return `${prefix}\uFF1A${exec.error?.message ?? "\u672A\u77E5\u9519\u8BEF"}`;
108
- }
109
- const parts = [];
110
- if (exec.dryRun) {
111
- parts.push("[dry-run \u9884\u89C8] \u672A\u5B9E\u9645\u6267\u884C\u4EFB\u4F55\u64CD\u4F5C");
112
- }
113
- if (exec.backupCreated) {
114
- parts.push(
115
- `[\u81EA\u52A8\u5907\u4EFD] \u5206\u652F ${exec.backupCreated.branch ?? "(\u672A\u521B\u5EFA)"}${exec.backupCreated.stashRef ? `\uFF0Cstash ${exec.backupCreated.stashRef}` : ""}`
116
- );
117
- }
118
- const summarized = summarizeForAgent(exec.tool, exec.result, exec.args ?? {});
119
- const payload = typeof summarized === "string" ? summarized : JSON.stringify(summarized);
120
- parts.push(payload);
121
- return parts.join("\n\n");
122
- }
123
-
124
- // src/tools/handlers.ts
125
- async function resolveRepo(runtime, ctx, argsPath) {
126
- if (ctx.repoId !== void 0) {
127
- const handle = await runtime.repoManager.getById(ctx.repoId);
128
- if (handle) return handle;
129
- throw new GitOperationError(`\u4ED3\u5E93\u4E0D\u5B58\u5728: id=${ctx.repoId}`, "REPO_NOT_FOUND");
130
- }
131
- if (ctx.repoPath) {
132
- return runtime.repoManager.getByPath(ctx.repoPath);
133
- }
134
- if (argsPath) {
135
- return runtime.repoManager.open(argsPath);
136
- }
137
- const current = await runtime.repoManager.getCurrent();
138
- if (!current) {
139
- throw new GitOperationError("\u5C1A\u672A\u6253\u5F00\u4EFB\u4F55\u4ED3\u5E93\u3002\u8BF7\u5148\u6253\u5F00\u4ED3\u5E93\u6216\u63D0\u4F9B repoPath \u53C2\u6570\u3002", "NO_ACTIVE_REPO");
140
- }
141
- return current;
142
- }
143
- function extractError(err) {
144
- if (err instanceof PermissionError) {
145
- return { code: "PERMISSION_DENIED", message: err.message, requiredApproval: err.requiredApproval };
146
- }
147
- if (err instanceof GitOperationError) {
148
- return { code: err.code, message: err.message, requiredApproval: false };
149
- }
150
- if (err instanceof z.ZodError) {
151
- const summary = err.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).slice(0, 5).join("; ");
152
- return { code: "INVALID_ARGS", message: `\u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF1A${summary}`, requiredApproval: false };
153
- }
154
- const msg = err instanceof Error ? err.message : String(err);
155
- return { code: "INTERNAL_ERROR", message: msg.slice(0, 500), requiredApproval: false };
156
- }
157
- async function executeTool(def, rawArgs, ctx) {
158
- const runtime = ctx.runtime;
159
- const t0 = Date.now();
160
- const dryRun2 = typeof rawArgs.dryRun === "boolean" ? rawArgs.dryRun : runtime.permissions.getDryRunDefault();
161
- const record = (result, params, errorMessage, repoPath2) => {
162
- runtime.auditLogger.log({
163
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
164
- source: ctx.source,
165
- tool: def.name,
166
- repoPath: repoPath2 ?? "",
167
- params,
168
- result,
169
- error: errorMessage ?? null,
170
- durationMs: Date.now() - t0,
171
- dryRun: dryRun2
172
- });
173
- runtime.eventBus.emit("log", { tool: def.name, result, at: (/* @__PURE__ */ new Date()).toISOString() });
174
- };
175
- try {
176
- runtime.permissions.assertAllowed(def.name);
177
- let args;
178
- try {
179
- args = def.schema.parse(rawArgs);
180
- } catch (err) {
181
- const e = extractError(err);
182
- record("error", rawArgs, e.message);
183
- return { tool: def.name, source: ctx.source, dryRun: dryRun2, success: false, error: e, durationMs: Date.now() - t0 };
184
- }
185
- let git;
186
- let repoPath2 = "";
187
- if (def.needsRepo !== false) {
188
- const handle = await resolveRepo(runtime, ctx, args.repoPath);
189
- git = handle.service;
190
- repoPath2 = git.repoPath;
191
- }
192
- let backup = null;
193
- if (!dryRun2 && def.risk === "dangerous" && runtime.config.git.backupOnDangerousOps && git) {
194
- backup = await new BackupManager(git).createBackup();
195
- args = { ...args, __backup: { branch: backup.branch, stashRef: backup.stashRef } };
196
- }
197
- const result = await def.handler(args, { ...ctx, git, repoPath: repoPath2 });
198
- record("success", args, void 0, repoPath2);
199
- const preview = dryRun2 ? result : void 0;
200
- return {
201
- tool: def.name,
202
- source: ctx.source,
203
- dryRun: dryRun2,
204
- success: true,
205
- result,
206
- preview,
207
- backupCreated: backup,
208
- durationMs: Date.now() - t0,
209
- args
210
- };
211
- } catch (err) {
212
- const e = extractError(err);
213
- const isDenied = err instanceof PermissionError;
214
- record(isDenied ? "denied" : "error", rawArgs, e.message);
215
- return {
216
- tool: def.name,
217
- source: ctx.source,
218
- dryRun: dryRun2,
219
- success: false,
220
- error: e,
221
- durationMs: Date.now() - t0
222
- };
223
- }
224
- }
225
-
226
- // src/tools/index.ts
227
- import {
228
- enrichPrepareMr,
229
- createPullOrMergeRequest,
230
- BackupManager as BackupManager2,
231
- GitOperationError as GitOperationError2,
232
- SURVEY_ASYNC_THRESHOLD
233
- } from "@shaohui_jin/git-cockpit-core";
234
-
235
- // src/overview.ts
236
- import { emptyActivity } from "@shaohui_jin/git-cockpit-core";
237
- var CONCURRENCY = 4;
238
- function unavailableOverview(path, extra = {}) {
239
- const name = path.replace(/[\\/]+$/, "").split(/[\\/]/).pop() || path;
240
- return {
241
- path,
242
- name,
243
- available: false,
244
- current: "",
245
- tracking: null,
246
- ahead: 0,
247
- behind: 0,
248
- dirtyCount: 0,
249
- conflictCount: 0,
250
- operation: "none",
251
- tempMergeBranchCount: 0,
252
- ...emptyActivity(),
253
- ...extra
254
- };
255
- }
256
- async function mapPool(items, n, fn) {
257
- const out = new Array(items.length);
258
- let i = 0;
259
- async function worker() {
260
- while (i < items.length) {
261
- const idx = i++;
262
- out[idx] = await fn(items[idx]);
263
- }
264
- }
265
- const workers = Math.min(n, items.length);
266
- await Promise.all(Array.from({ length: workers }, () => worker()));
267
- return out;
268
- }
269
- async function collectRepoOverviews(runtime) {
270
- const repos = runtime.repoManager.list();
271
- if (!repos.length) return [];
272
- return mapPool(repos, CONCURRENCY, async (r) => {
273
- try {
274
- const handle = await runtime.repoManager.getById(r.id);
275
- if (!handle) return unavailableOverview(r.path, { id: r.id, lastOpenedAt: r.lastOpenedAt });
276
- const o = await handle.service.getOverview();
277
- return { ...o, id: r.id, lastOpenedAt: r.lastOpenedAt };
278
- } catch {
279
- return unavailableOverview(r.path, { id: r.id, lastOpenedAt: r.lastOpenedAt });
280
- }
281
- });
282
- }
283
-
284
- // src/tools/schemas.ts
285
- import { z as z2 } from "zod";
286
- var repoPath = z2.string().optional().describe("\u4ED3\u5E93\u7EDD\u5BF9\u8DEF\u5F84\u3002\u4E0D\u63D0\u4F9B\u65F6\u4F7F\u7528\u5F53\u524D\u6253\u5F00\u7684\u4ED3\u5E93\u3002");
287
- var dryRun = z2.boolean().optional().describe("\u4E3A true \u65F6\u4EC5\u8FD4\u56DE\u5C06\u6267\u884C\u7684\u547D\u4EE4\u4E0E\u5F71\u54CD\u8303\u56F4\uFF08\u9884\u89C8/\u5E72\u8FD0\u884C\uFF09\uFF0C\u4E0D\u771F\u6B63\u4FEE\u6539\u4ED3\u5E93\u3002");
288
- var detail = z2.boolean().optional().describe("\u4E3A true \u65F6 MCP \u8FD4\u56DE\u5B8C\u6574\u6B63\u6587\uFF1B\u9ED8\u8BA4\u53EA\u56DE\u6458\u8981\uFF08\u8DEF\u5F84/\u7EDF\u8BA1\uFF09\u3002\u7F51\u9875 GET \u4E0D\u53D7\u5F71\u54CD\u3002");
289
- var maxCount = z2.number().int().min(1).max(1e4).optional().describe("\u6700\u591A\u8FD4\u56DE\u7684\u63D0\u4EA4\u6570\u91CF\u3002");
290
- var readonlyBase = { repoPath };
291
- var GitStatusSchema = z2.object({ ...readonlyBase });
292
- var GitLogSchema = z2.object({
293
- ...readonlyBase,
294
- maxCount,
295
- from: z2.string().optional().describe("\u8D77\u59CB\u63D0\u4EA4\uFF08\u542B\uFF09\uFF1B\u4E0E to \u914D\u5408\u8868\u793A\u8303\u56F4 from..to"),
296
- to: z2.string().optional().describe("\u7ED3\u675F\u63D0\u4EA4\uFF08\u542B\uFF09"),
297
- author: z2.string().optional().describe("\u6309\u4F5C\u8005\u8FC7\u6EE4\uFF08\u652F\u6301\u5B50\u4E32\uFF09"),
298
- path: z2.string().optional().describe("\u4EC5\u67E5\u770B\u8BE5\u8DEF\u5F84\u7684\u5386\u53F2"),
299
- all: z2.boolean().optional().describe("\u5305\u542B\u6240\u6709\u5206\u652F\uFF08--all\uFF09")
300
- });
301
- var GitDiffSchema = z2.object({
302
- ...readonlyBase,
303
- from: z2.string().optional().describe("\u8D77\u70B9\u63D0\u4EA4"),
304
- to: z2.string().optional().describe("\u7EC8\u70B9\u63D0\u4EA4"),
305
- path: z2.string().optional().describe("\u9650\u5B9A\u7684\u6587\u4EF6\u8DEF\u5F84"),
306
- staged: z2.boolean().optional().describe("\u67E5\u770B\u5DF2\u6682\u5B58\uFF08\u7D22\u5F15\uFF09\u5DEE\u5F02"),
307
- maxPatchBytes: z2.number().int().positive().optional().describe("diff \u6B63\u6587\u6700\u5927\u5B57\u8282\u6570\uFF0C\u8D85\u51FA\u622A\u65AD"),
308
- detail
309
- });
310
- var GitShowSchema = z2.object({
311
- ...readonlyBase,
312
- commit: z2.string().describe("\u8981\u67E5\u770B\u7684\u63D0\u4EA4 hash"),
313
- path: z2.string().optional().describe("\u9650\u5B9A\u6587\u4EF6"),
314
- maxPatchBytes: z2.number().int().positive().optional(),
315
- detail
316
- });
317
- var GitBranchListSchema = z2.object({ ...readonlyBase });
318
- var GitTagListSchema = z2.object({ ...readonlyBase });
319
- var GitRemoteListSchema = z2.object({ ...readonlyBase });
320
- var GitFileContentSchema = z2.object({
321
- ...readonlyBase,
322
- commit: z2.string().describe("\u63D0\u4EA4 hash\uFF08\u5982 HEAD\u3001main\uFF09"),
323
- path: z2.string().describe("\u4ED3\u5E93\u5185\u6587\u4EF6\u76F8\u5BF9\u8DEF\u5F84"),
324
- detail
325
- });
326
- var GitGraphSchema = z2.object({
327
- ...readonlyBase,
328
- maxCount: z2.number().int().min(1).max(5e3).optional().describe("\u6700\u5927\u63D0\u4EA4\u6570")
329
- });
330
- var GitBranchGraphSchema = z2.object({
331
- ...readonlyBase,
332
- maxNodes: z2.number().int().min(1).max(2e3).optional().describe("rev-list \u6700\u591A\u679A\u4E3E\u7684\u63D0\u4EA4\u6570\uFF0C\u7F3A\u7701 200"),
333
- into: z2.string().optional().describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09\u3002\u4E0E from \u4E00\u8D77\u65F6\u8FD4\u56DE lineage"),
334
- from: z2.string().optional().describe("\u6211\u7684\u5206\u652F\uFF08theirs\uFF09\u3002\u4E0E into \u4E00\u8D77\u65F6\u8FD4\u56DE lineage")
335
- });
336
- var GitReflogSchema = z2.object({
337
- ...readonlyBase,
338
- maxCount: z2.number().int().min(1).max(500).optional().describe("\u6700\u591A\u8FD4\u56DE\u6761\u6570\uFF0C\u7F3A\u7701 50")
339
- });
340
- var mergeIntoFrom = {
341
- into: z2.string().min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours / \u9884\u6F14\u5DE6\u4FA7\uFF09\u3002\u4F8B\u5982 origin/main"),
342
- from: z2.string().min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs / \u9884\u6F14\u53F3\u4FA7\uFF09\u3002\u4F8B\u5982 feature/x"),
343
- fetch: z2.boolean().optional().describe("\u662F\u5426\u5148\u975E\u4EA4\u4E92 fetch\u3002\u7F3A\u7701 true\uFF1B\u5931\u8D25\u5219\u7528\u672C\u5730\u5F15\u7528\u5E76\u6807\u660E stale"),
344
- remote: z2.string().optional().describe("fetch/push \u4F7F\u7528\u7684\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin"),
345
- path: z2.string().optional().describe("\u4EC5\u5173\u5FC3\u8BE5\u51B2\u7A81\u6587\u4EF6\u8DEF\u5F84")
346
- };
347
- var GitMergePreviewSchema = z2.object({
348
- ...readonlyBase,
349
- ...mergeIntoFrom
350
- });
351
- var GitMergeRehearseSchema = z2.object({
352
- ...readonlyBase,
353
- ...mergeIntoFrom,
354
- maxFiles: z2.number().int().min(1).max(100).optional().describe("\u6700\u591A\u751F\u6210\u51B2\u7A81\u6B63\u6587\u7684\u6587\u4EF6\u6570\uFF0C\u7F3A\u7701 20"),
355
- detail
356
- });
357
- var GitMergeBlameSchema = z2.object({
358
- ...readonlyBase,
359
- into: z2.string().min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09"),
360
- from: z2.string().min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs\uFF09"),
361
- path: z2.string().min(1).describe("\u8981\u6EAF\u6E90\u7684\u51B2\u7A81\u6587\u4EF6\u8DEF\u5F84"),
362
- fetch: z2.boolean().optional().describe("\u662F\u5426\u5148\u975E\u4EA4\u4E92 fetch\u3002\u7F3A\u7701 false\uFF08\u9884\u6F14\u4E4B\u540E\u6309\u9700\u62C9\uFF09"),
363
- remote: z2.string().optional().describe("fetch \u4F7F\u7528\u7684\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin")
364
- });
365
- var GitMergeSurveySchema = z2.object({
366
- ...readonlyBase,
367
- intos: z2.array(z2.string().min(1)).min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09\u5217\u8868"),
368
- froms: z2.array(z2.string().min(1)).min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs\uFF09\u5217\u8868"),
369
- fetch: z2.boolean().optional().describe("\u662F\u5426\u5148\u975E\u4EA4\u4E92 fetch\u3002\u7F3A\u7701 true\uFF1B\u6574\u6279\u53EA fetch \u4E00\u6B21"),
370
- remote: z2.string().optional().describe("fetch \u4F7F\u7528\u7684\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin"),
371
- async: z2.boolean().optional().describe("\u4E3A true \u6216\u683C\u5B50\u6570>20 \u65F6\u540E\u53F0\u8DD1\uFF0C\u7ACB\u5373\u8FD4\u56DE jobId\uFF0C\u7528 git_job_get \u67E5\u7ED3\u679C")
372
- });
373
- var GitMergeOrderSchema = z2.object({
374
- ...readonlyBase,
375
- into: z2.string().min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09"),
376
- branches: z2.array(z2.string().min(1)).min(2).describe("\u5F85\u4F9D\u6B21\u5408\u5165\u7684\u6765\u6E90\u5206\u652F\uFF08\u81F3\u5C11 2 \u4E2A\uFF09"),
377
- fetch: z2.boolean().optional().describe("\u662F\u5426\u5148\u975E\u4EA4\u4E92 fetch\u3002\u7F3A\u7701 true"),
378
- remote: z2.string().optional().describe("fetch \u4F7F\u7528\u7684\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin")
379
- });
380
- var writeBase = { repoPath, dryRun };
381
- var pathsField = z2.array(z2.string()).default([]).describe("\u6587\u4EF6\u8DEF\u5F84\u5217\u8868\uFF1B\u7F3A\u7701\u4E3A\u7A7A\u5373\u5168\u90E8\u6587\u4EF6");
382
- var GitAddSchema = z2.object({ ...writeBase, paths: pathsField });
383
- var GitUnstageSchema = z2.object({ ...writeBase, paths: pathsField });
384
- var GitCommitSchema = z2.object({
385
- ...writeBase,
386
- message: z2.string().min(1).describe("\u63D0\u4EA4\u4FE1\u606F"),
387
- paths: z2.array(z2.string()).optional().describe("\u4EC5\u63D0\u4EA4\u8FD9\u4E9B\u8DEF\u5F84\uFF08\u53EF\u7A7A\uFF09"),
388
- allowEmpty: z2.boolean().optional().describe("\u5141\u8BB8\u7A7A\u63D0\u4EA4\uFF08--allow-empty\uFF09")
389
- });
390
- var GitCheckoutSchema = z2.object({
391
- ...writeBase,
392
- branch: z2.string().describe("\u8981\u5207\u6362\u5230\u7684\u5206\u652F\u540D")
393
- });
394
- var GitBranchCreateSchema = z2.object({
395
- ...writeBase,
396
- name: z2.string().describe("\u65B0\u5206\u652F\u540D"),
397
- startPoint: z2.string().optional().describe("\u521B\u5EFA\u8D77\u70B9\uFF08\u63D0\u4EA4/\u5206\u652F\uFF09\uFF0C\u7F3A\u7701\u4E3A\u5F53\u524D HEAD")
398
- });
399
- var GitBranchDeleteSchema = z2.object({
400
- ...writeBase,
401
- name: z2.string().describe("\u8981\u5220\u9664\u7684\u5206\u652F\u540D\uFF08\u672A\u5408\u5E76\u5206\u652F\u4F1A\u88AB\u62D2\u7EDD\uFF09")
402
- });
403
- var GitMergeSchema = z2.object({
404
- ...writeBase,
405
- branch: z2.string().describe("\u8981\u5408\u5E76\u8FDB\u6765\u7684\u5206\u652F\u540D")
406
- });
407
- var GitPullSchema = z2.object({
408
- ...writeBase,
409
- remote: z2.string().optional().describe("\u8FDC\u7A0B\u540D\uFF08\u7F3A\u7701 origin\uFF09"),
410
- branch: z2.string().optional().describe("\u8FDC\u7AEF\u5206\u652F\u540D")
411
- });
412
- var GitFetchSchema = z2.object({
413
- ...writeBase,
414
- remote: z2.string().optional().describe("\u8FDC\u7A0B\u540D\uFF08\u7F3A\u7701 origin\uFF09")
415
- });
416
- var continueFiles = z2.array(
417
- z2.object({
418
- path: z2.string().describe("\u4ED3\u5E93\u5185\u76F8\u5BF9\u8DEF\u5F84"),
419
- resolvedContent: z2.string().describe("\u8BE5\u6587\u4EF6\u89E3\u51B3\u540E\u7684\u5B8C\u6574\u5185\u5BB9")
420
- })
421
- ).optional().describe("\u5DF2\u89E3\u51B3\u51B2\u7A81\u7684\u6587\u4EF6\uFF1B\u5199\u5165\u5DE5\u4F5C\u533A\u5E76 git add \u540E\u518D continue\u3002\u5DF2\u5728\u7F16\u8F91\u5668\u89E3\u51B3\u5E76\u53EF\u4E3A\u7A7A");
422
- var GitMergeAbortSchema = z2.object({ ...writeBase });
423
- var GitMergeContinueSchema = z2.object({
424
- ...writeBase,
425
- files: continueFiles
426
- });
427
- var GitRebaseAbortSchema = z2.object({ ...writeBase });
428
- var GitRebaseContinueSchema = z2.object({
429
- ...writeBase,
430
- files: continueFiles
431
- });
432
- var GitPushSchema = z2.object({
433
- ...writeBase,
434
- remote: z2.string().optional().describe("\u8FDC\u7A0B\u540D\uFF08\u7F3A\u7701 origin\uFF09"),
435
- branch: z2.string().optional().describe("\u5206\u652F\u540D")
436
- });
437
- var GitTagCreateSchema = z2.object({
438
- ...writeBase,
439
- name: z2.string().describe("\u6807\u7B7E\u540D"),
440
- message: z2.string().optional().describe("\u9644\u6CE8\u6807\u7B7E\u6D88\u606F\uFF08\u5E26\u5219\u521B\u5EFA annotated tag\uFF09"),
441
- commit: z2.string().optional().describe("\u6253\u6807\u7B7E\u7684\u63D0\u4EA4\uFF0C\u7F3A\u7701\u4E3A HEAD")
442
- });
443
- var GitStashSchema = z2.object({
444
- ...writeBase,
445
- message: z2.string().optional().describe("stash \u6D88\u606F"),
446
- includeUntracked: z2.boolean().optional().describe("\u5305\u542B\u672A\u8DDF\u8E2A\u6587\u4EF6\uFF08-u\uFF09"),
447
- paths: pathsField.describe("\u4EC5\u6682\u5B58\u8FD9\u4E9B\u6587\u4EF6\uFF08\u5BF9\u5E94 WebStorm Shelve \u7684\u9009\u62E9\u6027\u6682\u5B58\uFF1B\u7A7A\u5219\u6682\u5B58\u5168\u90E8\uFF09")
448
- });
449
- var GitStashListSchema = z2.object({ ...readonlyBase });
450
- var GitStashShowSchema = z2.object({
451
- ...readonlyBase,
452
- index: z2.number().int().min(0).optional().describe("stash \u5E8F\u53F7\uFF0C\u7F3A\u7701\u4E3A stash@{0}"),
453
- maxPatchBytes: z2.number().int().positive().optional().describe("diff \u6B63\u6587\u6700\u5927\u5B57\u8282\u6570\uFF0C\u8D85\u51FA\u622A\u65AD"),
454
- detail
455
- });
456
- var GitRepoOverviewSchema = z2.object({ ...readonlyBase, detail });
457
- var GitJobListSchema = z2.object({ ...readonlyBase });
458
- var GitJobGetSchema = z2.object({
459
- ...readonlyBase,
460
- id: z2.string().min(1).describe("\u540E\u53F0\u4EFB\u52A1 id")
461
- });
462
- var GitJobCancelSchema = z2.object({
463
- ...writeBase,
464
- id: z2.string().min(1).describe("\u8981\u53D6\u6D88\u7684\u540E\u53F0\u4EFB\u52A1 id")
465
- });
466
- var GitStashApplySchema = z2.object({
467
- ...writeBase,
468
- index: z2.number().int().min(0).optional().describe("stash \u5E8F\u53F7\uFF0C\u7F3A\u7701\u4E3A stash@{0}")
469
- });
470
- var GitStashDropSchema = z2.object({
471
- ...writeBase,
472
- index: z2.number().int().min(0).optional().describe("stash \u5E8F\u53F7\uFF0C\u7F3A\u7701\u4E3A stash@{0}")
473
- });
474
- var GitStashPopSchema = z2.object({
475
- ...writeBase,
476
- index: z2.number().int().min(0).optional().describe("stash \u5E8F\u53F7\uFF0C\u7F3A\u7701\u4E3A\u6700\u65B0\u7684 stash@{0}")
477
- });
478
- var GitApplyResolveSchema = z2.object({
479
- ...writeBase,
480
- into: z2.string().min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09\u3002\u4F8B\u5982 origin/main"),
481
- from: z2.string().min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs\uFF09\u3002\u4F8B\u5982 feature/x"),
482
- remote: z2.string().optional().describe("push \u4F7F\u7528\u7684\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin"),
483
- files: z2.array(
484
- z2.object({
485
- path: z2.string().describe("\u4ED3\u5E93\u5185\u76F8\u5BF9\u8DEF\u5F84"),
486
- resolvedContent: z2.string().describe("\u8BE5\u6587\u4EF6\u89E3\u51B3\u540E\u7684\u5B8C\u6574\u5185\u5BB9")
487
- })
488
- ).optional().describe("\u5DF2\u89E3\u51B3\u51B2\u7A81\u7684\u6587\u4EF6\u3002\u5E72\u51C0\u5408\u5E76\u53EF\u7701\u7565\u6216\u4F20\u7A7A\u6570\u7EC4"),
489
- push: z2.boolean().optional().describe("\u662F\u5426\u63A8\u9001\u4E34\u65F6\u5206\u652F\uFF0C\u7F3A\u7701 true"),
490
- keepLocal: z2.boolean().optional().describe("\u63A8\u9001\u5931\u8D25\u65F6\u4ECD\u4FDD\u7559\u672C\u5730\u4E34\u65F6\u5206\u652F"),
491
- tempBranch: z2.string().optional().describe("\u81EA\u5B9A\u4E49\u4E34\u65F6\u5206\u652F\u540D\uFF1B\u7F3A\u7701 merge/<from>-into-<into>")
492
- });
493
- var mrIntoFrom = {
494
- into: z2.string().min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09\u3002\u4F8B\u5982 origin/main"),
495
- from: z2.string().min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs\uFF09\u3002\u4F8B\u5982 feature/x"),
496
- remote: z2.string().optional().describe("\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin"),
497
- sourceBranch: z2.string().optional().describe("PR \u6E90\u5206\u652F\uFF1B\u7F3A\u7701\u5DF2\u6709\u4E34\u65F6\u5206\u652F merge/<from>-into-<into>\uFF0C\u5426\u5219 from")
498
- };
499
- var GitMrPrepareSchema = z2.object({
500
- ...readonlyBase,
501
- ...mrIntoFrom
502
- });
503
- var GitMrCreateSchema = z2.object({
504
- ...writeBase,
505
- ...mrIntoFrom,
506
- title: z2.string().optional().describe("PR \u6807\u9898\uFF1B\u7F3A\u7701 Merge <source> into <target>"),
507
- body: z2.string().optional().describe("PR \u6B63\u6587"),
508
- reviewers: z2.array(z2.string()).optional().describe("\u5BA1\u6838\u4EBA/\u6307\u6D3E\u4EBA\u7528\u6237\u540D\uFF1BGitLab \u4F1A\u89E3\u6790\u6210\u6570\u5B57 id\u3002Token \u4E0D\u5728\u6B64\u4F20\u5165")
509
- });
510
- var GitResetHardSchema = z2.object({ ...writeBase, target: z2.string().describe("\u8981\u91CD\u7F6E\u5230\u7684\u63D0\u4EA4") });
511
- var GitCleanSchema = z2.object({ ...writeBase });
512
- var GitPushForceSchema = z2.object({
513
- ...writeBase,
514
- remote: z2.string().optional(),
515
- branch: z2.string().optional()
516
- });
517
- var GitBranchDeleteForceSchema = z2.object({
518
- ...writeBase,
519
- name: z2.string().describe("\u8981\u5F3A\u5236\u5220\u9664\u7684\u5206\u652F\u540D")
520
- });
521
- var GitRebaseSchema = z2.object({
522
- ...writeBase,
523
- branch: z2.string().describe("\u8981\u53D8\u57FA\u5230\u7684\u5206\u652F/\u63D0\u4EA4")
524
- });
525
-
526
- // src/tools/index.ts
527
- var TOOL_DEFS = [
528
- // ---------------------------------------------------------------------------
529
- // 只读工具(默认开放)
530
- // ---------------------------------------------------------------------------
531
- {
532
- name: "git_status",
533
- description: "\u83B7\u53D6\u5F53\u524D\u4ED3\u5E93\u7684\u5DE5\u4F5C\u533A\u72B6\u6001\uFF1A\u5F53\u524D\u5206\u652F\u3001\u5DF2\u6682\u5B58/\u672A\u6682\u5B58/\u672A\u8DDF\u8E2A\u6587\u4EF6\u3001\u51B2\u7A81\u6587\u4EF6\u3001\u9886\u5148/\u843D\u540E\u4E8E\u4E0A\u6E38\u7B49\u3002",
534
- risk: "readonly",
535
- schema: GitStatusSchema,
536
- handler: async (_args, ctx) => ctx.git.getStatus()
537
- },
538
- {
539
- name: "git_log",
540
- description: "\u83B7\u53D6\u63D0\u4EA4\u5386\u53F2\uFF08\u9ED8\u8BA4 HEAD \u8D77\uFF0C\u6700\u591A\u8FD4\u56DE maxCount \u6761\uFF09\u3002\u652F\u6301\u6309\u4F5C\u8005\u3001\u8DEF\u5F84\u3001\u63D0\u4EA4\u8303\u56F4\u8FC7\u6EE4\u3002",
541
- risk: "readonly",
542
- schema: GitLogSchema,
543
- handler: async (args, ctx) => ctx.git.getLog({
544
- maxCount: args.maxCount,
545
- from: args.from,
546
- to: args.to,
547
- author: args.author,
548
- path: args.path,
549
- all: args.all
550
- })
551
- },
552
- {
553
- name: "git_diff",
554
- description: "\u83B7\u53D6\u5DE5\u4F5C\u533A/\u6682\u5B58\u533A/\u63D0\u4EA4\u8303\u56F4\u7684\u5DEE\u5F02\u3002\u652F\u6301\u6307\u5B9A\u6587\u4EF6\u3001staged\u3001\u5927 diff \u622A\u65AD\u3002\u8FD4\u56DE\u6587\u4EF6\u7EA7\u7EDF\u8BA1\u4E0E\u5B8C\u6574 unified diff \u6587\u672C\u3002",
555
- risk: "readonly",
556
- schema: GitDiffSchema,
557
- handler: async (args, ctx) => ctx.git.getDiff({
558
- from: args.from,
559
- to: args.to,
560
- path: args.path,
561
- staged: args.staged,
562
- maxPatchBytes: args.maxPatchBytes
563
- })
564
- },
565
- {
566
- name: "git_show",
567
- description: "\u663E\u793A\u67D0\u4E2A\u63D0\u4EA4\u7684\u8BE6\u7EC6\u4FE1\u606F\uFF08\u5143\u4FE1\u606F + \u76F8\u5BF9\u7236\u63D0\u4EA4\u7684 diff\uFF09\u3002",
568
- risk: "readonly",
569
- schema: GitShowSchema,
570
- handler: async (args, ctx) => ctx.git.getShow(args.commit, {
571
- path: args.path,
572
- maxPatchBytes: args.maxPatchBytes
573
- })
574
- },
575
- {
576
- name: "git_branch_list",
577
- description: "\u5217\u51FA\u6240\u6709\u672C\u5730\u4E0E\u8FDC\u7A0B\u5206\u652F\uFF0C\u6807\u6CE8\u5F53\u524D\u5206\u652F\u53CA\u5176\u6307\u5411\u7684\u63D0\u4EA4\u3002",
578
- risk: "readonly",
579
- schema: GitBranchListSchema,
580
- handler: async (_args, ctx) => ctx.git.listBranches()
581
- },
582
- {
583
- name: "git_tag_list",
584
- description: "\u5217\u51FA\u6240\u6709\u6807\u7B7E\uFF08\u6309\u521B\u5EFA\u65F6\u95F4\u5012\u5E8F\uFF09\u3002",
585
- risk: "readonly",
586
- schema: GitTagListSchema,
587
- handler: async (_args, ctx) => ctx.git.listTags()
588
- },
589
- {
590
- name: "git_remote_list",
591
- description: "\u5217\u51FA\u8FDC\u7A0B\u4ED3\u5E93\u4FE1\u606F\uFF08\u540D\u79F0\u3001fetch/push URL\uFF09\u3002",
592
- risk: "readonly",
593
- schema: GitRemoteListSchema,
594
- handler: async (_args, ctx) => ctx.git.listRemotes()
595
- },
596
- {
597
- name: "git_file_content",
598
- description: "\u8BFB\u53D6\u6307\u5B9A\u63D0\u4EA4\u4E2D\u67D0\u4E2A\u6587\u4EF6\u7684\u5185\u5BB9\uFF08\u5927\u6587\u4EF6\u81EA\u52A8\u622A\u65AD 2MB\uFF09\u3002",
599
- risk: "readonly",
600
- schema: GitFileContentSchema,
601
- handler: async (args, ctx) => ctx.git.getFileContent(args.commit, args.path)
602
- },
603
- {
604
- name: "git_graph",
605
- description: "\u83B7\u53D6\u5168\u90E8\u5206\u652F\u7684\u63D0\u4EA4\u5217\u8868\uFF08hash/parent/subject/refs/HEAD\uFF09\uFF0C\u7ED9\u9700\u8981\u7EBF\u6027\u5386\u53F2\u7684\u573A\u666F\u3002\u72B6\u6001\u9875 G6 \u5206\u652F\u56FE\u8BF7\u7528 git_branch_graph\uFF08tip DAG\uFF09\u3002",
606
- risk: "readonly",
607
- schema: GitGraphSchema,
608
- handler: async (args, ctx) => ctx.git.getGraph(args.maxCount)
609
- },
610
- {
611
- name: "git_branch_graph",
612
- description: "\u5206\u652F tip DAG\uFF1Afor-each-ref + rev-list --parents\u3002\u8282\u70B9\u662F\u5404\u5206\u652F tip\uFF0C\u8FB9\u662F\u6700\u8FD1\u7956\u5148 tip\u3002\u4E0E Git Insight \u753B\u5E03\u540C\u4E00\u53E3\u5F84\u3002\u4E0D\u6539\u5DE5\u4F5C\u533A\u3002",
613
- risk: "readonly",
614
- schema: GitBranchGraphSchema,
615
- handler: async (args, ctx) => ctx.git.getBranchGraph({
616
- maxNodes: args.maxNodes,
617
- into: args.into,
618
- from: args.from
619
- })
620
- },
621
- {
622
- name: "git_reflog",
623
- description: "\u53EA\u8BFB\u5217\u51FA git reflog\uFF08HEAD \u79FB\u52A8\u8BB0\u5F55\uFF09\u3002",
624
- risk: "readonly",
625
- schema: GitReflogSchema,
626
- handler: async (args, ctx) => ctx.git.getReflog(args.maxCount)
627
- },
628
- {
629
- name: "git_backup_list",
630
- description: "\u5217\u51FA\u9AD8\u5371\u64CD\u4F5C\u524D\u81EA\u52A8\u521B\u5EFA\u7684 backup/pre-op-* \u5206\u652F\uFF0C\u4EE5\u53CA\u8BF4\u660E\u542B git-cockpit backup \u7684 stash\u3002",
631
- risk: "readonly",
632
- schema: GitStatusSchema,
633
- handler: async (_args, ctx) => new BackupManager2(ctx.git).listBackups()
634
- },
635
- {
636
- name: "git_merge_preview",
637
- description: "\u7528 git merge-tree \u9884\u6F14\u628A from \u5408\u5165 into\uFF08\u4E0D\u6539\u5DE5\u4F5C\u533A\uFF09\u3002into=\u5408\u5165\u76EE\u6807/\u7EBF\u4E0A/ours\uFF0Cfrom=\u6211\u7684\u5206\u652F/theirs\u3002\u8FD4\u56DE\u662F\u5426\u53EF\u5E72\u51C0\u5408\u5E76\u53CA\u51B2\u7A81\u6587\u4EF6\u5217\u8868\u3002\u7981\u6B62\u7528 git_merge \u505A\u9884\u6F14\u3002Git >= 2.38\u3002",
638
- risk: "readonly",
639
- schema: GitMergePreviewSchema,
640
- handler: async (args, ctx) => ctx.git.previewMerge({
641
- into: args.into,
642
- from: args.from,
643
- fetch: args.fetch,
644
- remote: args.remote,
645
- path: args.path
646
- })
647
- },
648
- {
649
- name: "git_merge_rehearse",
650
- description: "\u5B8C\u6574\u5408\u5E76\u9884\u6F14\uFF1A\u51B2\u7A81\u6587\u4EF6 + diff3 \u51B2\u7A81\u6B63\u6587 + ours/theirs/base\uFF08\u4ECD\u4E0D\u6539\u5DE5\u4F5C\u533A\uFF09\u3002MCP \u9ED8\u8BA4\u53EA\u56DE\u8DEF\u5F84\u6458\u8981\uFF1B\u8981\u6B63\u6587\u8BF7\u5E26 path \u6216 detail=true\u3002\u9009\u8FB9\u540E\u628A files \u4EA4\u7ED9 git_apply_resolve\u3002into/from \u540C git_merge_preview\u3002",
651
- risk: "readonly",
652
- schema: GitMergeRehearseSchema,
653
- handler: async (args, ctx) => {
654
- const wantBody = args.detail === true || typeof args.path === "string" && args.path.trim().length > 0;
655
- if (!wantBody) {
656
- return ctx.git.previewMerge({
657
- into: args.into,
658
- from: args.from,
659
- fetch: args.fetch,
660
- remote: args.remote,
661
- path: args.path
662
- });
663
- }
664
- return ctx.git.rehearseMerge({
665
- into: args.into,
666
- from: args.from,
667
- fetch: args.fetch,
668
- remote: args.remote,
669
- path: args.path,
670
- maxFiles: args.maxFiles
671
- });
672
- }
673
- },
674
- {
675
- name: "git_merge_blame",
676
- description: "\u5BF9\u5355\u4E2A\u51B2\u7A81\u6587\u4EF6\u4E24\u4FA7 tip \u8DD1 git blame\uFF08\u4E0D\u6539\u5DE5\u4F5C\u533A\uFF09\u3002\u8FD4\u56DE\u7EA2\u5757\u76F8\u5173\u533A\u95F4\u7684\u4F5C\u8005/\u8BF4\u660E/\u65F6\u95F4\uFF0C\u4F9B\u9009\u8FB9\u53C2\u8003\u3002\u4E0D\u662F\u81EA\u52A8\u9009\u8FB9\u3002into/from \u540C git_merge_preview\u3002",
677
- risk: "readonly",
678
- schema: GitMergeBlameSchema,
679
- handler: async (args, ctx) => ctx.git.blameConflictFile({
680
- into: args.into,
681
- from: args.from,
682
- path: args.path,
683
- fetch: args.fetch,
684
- remote: args.remote
685
- })
686
- },
687
- {
688
- name: "git_merge_survey",
689
- description: "\u6279\u91CF\u9884\u6F14\u77E9\u9635\uFF1Aintos \xD7 froms \u6BCF\u5BF9\u8DD1\u4E00\u6B21 merge-tree\uFF0C\u6574\u6279\u53EA fetch \u4E00\u6B21\u3002\u53EA\u8FD4\u56DE\u7ED3\u8BBA\u4E0E\u51B2\u7A81\u8DEF\u5F84\u3002\u683C\u5B50>20 \u6216 async=true \u65F6\u540E\u53F0\u8DD1\u5E76\u8FD4\u56DE jobId\uFF08git_job_get\uFF09\u3002\u7981\u6B62\u7528 git_merge \u505A\u9884\u6F14\u3002",
690
- risk: "readonly",
691
- schema: GitMergeSurveySchema,
692
- handler: async (args, ctx) => {
693
- const intos = args.intos;
694
- const froms = args.froms;
695
- const asyncWanted = args.async === true || intos.length * froms.length > SURVEY_ASYNC_THRESHOLD;
696
- if (asyncWanted) {
697
- const job = ctx.runtime.jobs.startSurvey({
698
- repoPath: ctx.repoPath,
699
- intos,
700
- froms,
701
- fetch: args.fetch,
702
- remote: args.remote,
703
- git: ctx.git
704
- });
705
- return { jobId: job.id, status: job.status, async: true, title: job.title };
706
- }
707
- return ctx.git.surveyMerges({
708
- intos,
709
- froms,
710
- fetch: args.fetch,
711
- remote: args.remote
712
- });
713
- }
714
- },
715
- {
716
- name: "git_merge_order",
717
- description: "\u5EFA\u8BAE\u628A\u591A\u4E2A from \u5408\u5165\u540C\u4E00\u4E2A into \u7684\u987A\u5E8F\u3002\u5BF9\u8C61\u5E93\u5185 merge-tree + commit-tree \u4E32\u884C\u6A21\u62DF\uFF0C\u4E0D\u6539\u5DE5\u4F5C\u533A\u3001\u4E0D\u5EFA\u5206\u652F\u3002\u8FD4\u56DE\u5EFA\u8BAE\u987A\u5E8F\u3001\u80FD\u8FDE\u7EED\u5E72\u51C0\u5408\u5165\u51E0\u4E2A\u3001\u4ECE\u54EA\u4E00\u6B65\u5F00\u59CB\u8981\u4EBA\u5DE5\u3002",
718
- risk: "readonly",
719
- schema: GitMergeOrderSchema,
720
- handler: async (args, ctx) => ctx.git.suggestMergeOrder({
721
- into: args.into,
722
- branches: args.branches,
723
- fetch: args.fetch,
724
- remote: args.remote
725
- })
726
- },
727
- {
728
- name: "git_mr_prepare",
729
- description: "\u53EA\u8BFB\uFF1A\u89E3\u6790\u5F00 PR/MR \u7684\u6E90/\u76EE\u6807\u3001\u6D4F\u89C8\u5668\u521B\u5EFA\u9875\u3001\u672C\u673A gh/glab \u662F\u5426\u53EF\u7528\uFF08\u627E\u4E0D\u5230\u4F1A\u5E26\u5B98\u65B9\u5B89\u88C5\u5730\u5740\uFF09\u3001\u53EF\u9009\u5BA1\u6838\u4EBA\u5217\u8868\u3002Token \u4E0D\u8FDB\u53C2\u6570\u3002into/from \u540C git_merge_preview\u3002",
730
- risk: "readonly",
731
- schema: GitMrPrepareSchema,
732
- handler: async (args, ctx) => {
733
- const mr = ctx.runtime.config.mr;
734
- const prep = await ctx.git.prepareMr({
735
- into: args.into,
736
- from: args.from,
737
- remote: args.remote?.trim() || void 0,
738
- sourceBranch: args.sourceBranch
739
- });
740
- return enrichPrepareMr({ prep, mr, cwd: ctx.git.repoPath });
741
- }
742
- },
743
- {
744
- name: "git_repo_overview",
745
- description: "\u5DF2\u6253\u5F00\u4ED3\u5E93\u8109\u640F\uFF1A\u5206\u652F\u3001\u810F\u6587\u4EF6\u6570\u3001ahead/behind\u3001\u5DE5\u4F5C\u533A operation\u3001\u672C\u5730 merge/* \u6570\u3001\u8FD1 12 \u5468\u63D0\u4EA4\u603B\u6570\u3002\u9ED8\u8BA4\u4E0D\u542B\u6BCF\u65E5\u683C\u5B50\uFF1Bdetail=true \u624D\u56DE activity[]\u3002\u4E0D\u56DE diff\u3002",
746
- risk: "readonly",
747
- needsRepo: false,
748
- schema: GitRepoOverviewSchema,
749
- handler: async (_args, ctx) => ({ repos: await collectRepoOverviews(ctx.runtime) })
750
- },
751
- {
752
- name: "git_job_list",
753
- description: "\u5217\u51FA\u540E\u53F0\u4EFB\u52A1\u6458\u8981\uFF08\u514B\u9686 / survey / fetch\uFF09\u3002",
754
- risk: "readonly",
755
- needsRepo: false,
756
- schema: GitJobListSchema,
757
- handler: async (_args, ctx) => ({ jobs: ctx.runtime.jobs.list().map((j) => ctx.runtime.jobs.summary(j)) })
758
- },
759
- {
760
- name: "git_job_get",
761
- description: "\u67E5\u770B\u4E00\u6761\u540E\u53F0\u4EFB\u52A1\uFF08\u65E5\u5FD7 + \u5B8C\u6210\u65F6\u7684 result \u6458\u8981\uFF09\u3002",
762
- risk: "readonly",
763
- needsRepo: false,
764
- schema: GitJobGetSchema,
765
- handler: async (args, ctx) => {
766
- const job = ctx.runtime.jobs.get(args.id);
767
- if (!job) throw new GitOperationError2("\u4EFB\u52A1\u4E0D\u5B58\u5728", "JOB_NOT_FOUND");
768
- return ctx.runtime.jobs.detail(job);
769
- }
770
- },
771
- // ---------------------------------------------------------------------------
772
- // 写操作工具(默认开放,需预览/确认)
773
- // ---------------------------------------------------------------------------
774
- {
775
- name: "git_add",
776
- description: "\u6682\u5B58\u6307\u5B9A\u6587\u4EF6\uFF1Bpaths \u4E3A\u7A7A\u8868\u793A\u6682\u5B58\u5168\u90E8\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
777
- risk: "write",
778
- schema: GitAddSchema,
779
- handler: async (args, ctx) => ctx.git.add(args.paths ?? [], { dryRun: args.dryRun })
780
- },
781
- {
782
- name: "git_unstage",
783
- description: "\u53D6\u6D88\u6682\u5B58\u6307\u5B9A\u6587\u4EF6\uFF08git reset HEAD -- <paths>\uFF09\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
784
- risk: "write",
785
- schema: GitUnstageSchema,
786
- handler: async (args, ctx) => ctx.git.unstage(args.paths ?? [], { dryRun: args.dryRun })
787
- },
788
- {
789
- name: "git_commit",
790
- description: "\u521B\u5EFA\u63D0\u4EA4\u3002\u9700\u8981\u6307\u5B9A message\uFF1Bpaths \u53EF\u9650\u5B9A\u4EC5\u63D0\u4EA4\u90E8\u5206\u6587\u4EF6\uFF1BallowEmpty \u5141\u8BB8\u7A7A\u63D0\u4EA4\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
791
- risk: "write",
792
- schema: GitCommitSchema,
793
- handler: async (args, ctx) => ctx.git.commit(args.message, {
794
- dryRun: args.dryRun,
795
- allowEmpty: args.allowEmpty,
796
- paths: args.paths
797
- })
798
- },
799
- {
800
- name: "git_checkout",
801
- description: "\u5207\u6362\u5230\u6307\u5B9A\u5206\u652F\u3002\u82E5\u672C\u5730\u66F4\u6539\u4F1A\u88AB\u8986\u76D6\u5C06\u62D2\u7EDD\u5E76\u7ED9\u51FA\u63D0\u793A\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
802
- risk: "write",
803
- schema: GitCheckoutSchema,
804
- handler: async (args, ctx) => ctx.git.checkoutBranch(args.branch, { dryRun: args.dryRun })
805
- },
806
- {
807
- name: "git_branch_create",
808
- description: "\u521B\u5EFA\u65B0\u5206\u652F\uFF08\u9ED8\u8BA4\u4ECE\u5F53\u524D HEAD\uFF1BstartPoint \u53EF\u6307\u5B9A\u8D77\u70B9\uFF09\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
809
- risk: "write",
810
- schema: GitBranchCreateSchema,
811
- handler: async (args, ctx) => ctx.git.createBranch(args.name, {
812
- dryRun: args.dryRun,
813
- startPoint: args.startPoint
814
- })
815
- },
816
- {
817
- name: "git_branch_delete",
818
- description: "\u5B89\u5168\u5220\u9664\u5206\u652F\uFF08-d\uFF0C\u672A\u5408\u5E76\u5206\u652F\u4F1A\u62D2\u7EDD\uFF09\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
819
- risk: "write",
820
- schema: GitBranchDeleteSchema,
821
- handler: async (args, ctx) => ctx.git.deleteBranch(args.name, { dryRun: args.dryRun })
822
- },
823
- {
824
- name: "git_merge",
825
- description: "\u628A\u6307\u5B9A\u5206\u652F\u5408\u5E76\u5230\u5F53\u524D\u5206\u652F\uFF08\u666E\u901A\u5408\u5E76\uFF09\u3002\u4EA7\u751F\u51B2\u7A81\u65F6\u4F1A\u63D0\u793A\u89E3\u51B3\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
826
- risk: "write",
827
- schema: GitMergeSchema,
828
- handler: async (args, ctx) => ctx.git.merge(args.branch, { dryRun: args.dryRun })
829
- },
830
- {
831
- name: "git_merge_abort",
832
- description: "\u4E2D\u6B62\u5F53\u524D\u5DE5\u4F5C\u533A merge\uFF08git merge --abort\uFF09\u3002\u53EA\u7528\u4E8E\u5DF2\u7ECF\u5F00\u59CB\u7684\u5DE5\u4F5C\u533A\u5408\u5E76\uFF0C\u7981\u6B62\u62FF\u6765\u4EE3\u66FF merge-tree \u9884\u6F14\u3002",
833
- risk: "write",
834
- schema: GitMergeAbortSchema,
835
- handler: async (args, ctx) => ctx.git.mergeAbort({ dryRun: args.dryRun })
836
- },
837
- {
838
- name: "git_merge_continue",
839
- description: "\u7EE7\u7EED\u5F53\u524D\u5DE5\u4F5C\u533A merge\u3002\u53EF\u628A files[{path,resolvedContent}] \u5199\u5165\u5DE5\u4F5C\u533A\u5E76 git add \u540E\u518D continue\u3002\u4E0D\u662F git_apply_resolve\u3002",
840
- risk: "write",
841
- schema: GitMergeContinueSchema,
842
- handler: async (args, ctx) => ctx.git.mergeContinue({
843
- dryRun: args.dryRun,
844
- files: args.files
845
- })
846
- },
847
- {
848
- name: "git_pull",
849
- description: "\u4ECE\u8FDC\u7A0B\u62C9\u53D6\u5E76\u5408\u5E76\uFF08\u975E\u5F3A\u5236\uFF09\u3002\u8FDC\u7A0B\u6709\u51B2\u7A81\u5C06\u63D0\u793A\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
850
- risk: "write",
851
- schema: GitPullSchema,
852
- handler: async (args, ctx) => ctx.git.pull({
853
- dryRun: args.dryRun,
854
- remote: args.remote,
855
- branch: args.branch
856
- })
857
- },
858
- {
859
- name: "git_fetch",
860
- description: "\u4ECE\u8FDC\u7A0B\u6293\u53D6\u8DDF\u8E2A\u5206\u652F\uFF08git fetch --prune --no-tags\uFF09\uFF0C\u4E0D\u6539\u5DE5\u4F5C\u533A\u3001\u4E0D\u5408\u5E76\u3002\u66F4\u65B0 origin/xxx \u540E\u518D\u505A\u9884\u6F14\u6216\u5206\u652F\u56FE\u5BF9\u6BD4\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
861
- risk: "write",
862
- schema: GitFetchSchema,
863
- handler: async (args, ctx) => ctx.git.fetch({
864
- dryRun: args.dryRun,
865
- remote: args.remote
866
- })
867
- },
868
- {
869
- name: "git_push",
870
- description: "\u63A8\u9001\u5230\u8FDC\u7A0B\uFF08\u975E\u5F3A\u5236\uFF09\u3002\u63A8\u9001\u88AB\u62D2\u7EDD\uFF08\u8FDC\u7A0B\u6709\u65B0\u63D0\u4EA4\uFF09\u4F1A\u7ED9\u51FA\u63D0\u793A\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
871
- risk: "write",
872
- schema: GitPushSchema,
873
- handler: async (args, ctx) => ctx.git.push({
874
- dryRun: args.dryRun,
875
- remote: args.remote,
876
- branch: args.branch
877
- })
878
- },
879
- {
880
- name: "git_tag_create",
881
- description: "\u521B\u5EFA\u6807\u7B7E\uFF1B\u63D0\u4F9B message \u5219\u521B\u5EFA\u9644\u6CE8\u6807\u7B7E\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
882
- risk: "write",
883
- schema: GitTagCreateSchema,
884
- handler: async (args, ctx) => ctx.git.createTag(args.name, {
885
- dryRun: args.dryRun,
886
- message: args.message,
887
- commit: args.commit
888
- })
889
- },
890
- {
891
- name: "git_stash",
892
- description: "\u6682\u5B58\uFF08stash\uFF09\u5F53\u524D\u5DE5\u4F5C\u533A\u66F4\u6539\uFF0C\u53EF\u9009\u5305\u542B\u672A\u8DDF\u8E2A\u6587\u4EF6\uFF08-u\uFF09\uFF1Bpaths \u53EF\u6307\u5B9A\u4EC5\u6682\u5B58\u90E8\u5206\u6587\u4EF6\uFF08\u5BF9\u5E94 WebStorm Shelve \u7684\u9009\u62E9\u6027\u6682\u5B58\uFF09\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
893
- risk: "write",
894
- schema: GitStashSchema,
895
- handler: async (args, ctx) => ctx.git.stash(args.message, {
896
- dryRun: args.dryRun,
897
- includeUntracked: args.includeUntracked,
898
- paths: args.paths ?? []
899
- })
900
- },
901
- {
902
- name: "git_stash_list",
903
- description: "\u5217\u51FA\u5168\u90E8 stash \u8BB0\u5F55\uFF08stash@{n} / \u8BF4\u660E / \u65F6\u95F4\uFF09\u3002",
904
- risk: "readonly",
905
- schema: GitStashListSchema,
906
- handler: async (_args, ctx) => ctx.git.listStashes()
907
- },
908
- {
909
- name: "git_stash_show",
910
- description: "\u67E5\u770B\u67D0\u6761 stash \u7684\u5DEE\u5F02\u5185\u5BB9\uFF08\u4E0D\u4FEE\u6539\u4ED3\u5E93\uFF09\u3002",
911
- risk: "readonly",
912
- schema: GitStashShowSchema,
913
- handler: async (args, ctx) => ctx.git.stashShow({
914
- index: args.index,
915
- maxPatchBytes: args.maxPatchBytes
916
- })
917
- },
918
- {
919
- name: "git_stash_apply",
920
- description: "\u5E94\u7528\u67D0\u6761 stash\uFF08\u4FDD\u7559\u8BB0\u5F55\uFF0C\u53EF\u53CD\u590D\u5E94\u7528\uFF09\u3002\u4EA7\u751F\u51B2\u7A81\u4F1A\u63D0\u793A\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
921
- risk: "write",
922
- schema: GitStashApplySchema,
923
- handler: async (args, ctx) => ctx.git.stashApply({ dryRun: args.dryRun, index: args.index })
924
- },
925
- {
926
- name: "git_stash_drop",
927
- description: "\u5220\u9664\u67D0\u6761 stash \u8BB0\u5F55\uFF08\u5220\u9664\u540E\u4E0D\u53EF\u76F4\u63A5\u6062\u590D\uFF09\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
928
- risk: "write",
929
- schema: GitStashDropSchema,
930
- handler: async (args, ctx) => ctx.git.stashDrop({ dryRun: args.dryRun, index: args.index })
931
- },
932
- {
933
- name: "git_stash_pop",
934
- description: "\u6062\u590D\u6700\u65B0\uFF08\u6216\u6307\u5B9A index\uFF09\u7684 stash\u3002\u6062\u590D\u4EA7\u751F\u51B2\u7A81\u4F1A\u63D0\u793A\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
935
- risk: "write",
936
- schema: GitStashPopSchema,
937
- handler: async (args, ctx) => ctx.git.stashPop({ dryRun: args.dryRun, index: args.index })
938
- },
939
- {
940
- name: "git_apply_resolve",
941
- description: "\u5728\u72EC\u7ACB git worktree \u4E2D\u628A from \u5408\u5165 into \u5E76\u63D0\u4EA4\u5230\u4E34\u65F6\u5206\u652F\uFF08\u4E3B\u5DE5\u4F5C\u533A\u4E0D\u5207\u6362\uFF09\u3002\u5E72\u51C0\u5408\u5E76 files \u53EF\u7A7A\uFF1B\u6709\u51B2\u7A81\u5FC5\u987B\u63D0\u4F9B files\u3002\u9ED8\u8BA4 push \u4E34\u65F6\u5206\u652F\u3002\u4E0D\u8981\u7528\u5DE5\u4F5C\u533A git_merge \u4EE3\u66FF\u672C\u5DE5\u5177\u3002",
942
- risk: "write",
943
- schema: GitApplyResolveSchema,
944
- handler: async (args, ctx) => ctx.git.applyResolve({
945
- into: args.into,
946
- from: args.from,
947
- files: args.files,
948
- remote: args.remote,
949
- push: args.push,
950
- keepLocal: args.keepLocal,
951
- tempBranch: args.tempBranch,
952
- dryRun: args.dryRun
953
- })
954
- },
955
- {
956
- name: "git_mr_create",
957
- description: "\u6309\u8BBE\u7F6E MR \u914D\u7F6E\u5F00 PR/MR\uFF1AToken REST\u3001\u672C\u673A gh/glab\uFF0C\u6216\u6D4F\u89C8\u5668\u521B\u5EFA\u9875\u3002Token \u4E0D\u8FDB\u672C\u5DE5\u5177\u53C2\u6570\u3002\u627E\u4E0D\u5230 CLI \u65F6\u7ED3\u679C\u91CC\u5E26\u5B98\u65B9\u5B89\u88C5\u5730\u5740\u3002dry_run \u53EF\u9884\u89C8\u3002",
958
- risk: "write",
959
- schema: GitMrCreateSchema,
960
- handler: async (args, ctx) => {
961
- const mr = ctx.runtime.config.mr;
962
- const prep = await ctx.git.prepareMr({
963
- into: args.into,
964
- from: args.from,
965
- remote: args.remote?.trim() || void 0,
966
- sourceBranch: args.sourceBranch
967
- });
968
- return createPullOrMergeRequest({
969
- prep,
970
- mr,
971
- cwd: ctx.git.repoPath,
972
- title: args.title,
973
- body: args.body,
974
- reviewers: args.reviewers,
975
- dryRun: args.dryRun
976
- });
977
- }
978
- },
979
- {
980
- name: "git_job_cancel",
981
- description: "\u53D6\u6D88\u8FDB\u884C\u4E2D\u7684\u540E\u53F0\u4EFB\u52A1\u3002\u514B\u9686\u4F1A\u6740\u8FDB\u7A0B\u5E76\u5220\u9664\u4E0D\u5B8C\u6574\u76EE\u5F55\u3002",
982
- risk: "write",
983
- needsRepo: false,
984
- schema: GitJobCancelSchema,
985
- handler: async (args, ctx) => {
986
- if (args.dryRun) {
987
- return {
988
- dryRun: true,
989
- command: "job cancel",
990
- args: [args.id],
991
- risk: "low",
992
- note: "\u5C06\u53D6\u6D88\u8FDB\u884C\u4E2D\u7684\u540E\u53F0\u4EFB\u52A1"
993
- };
994
- }
995
- return ctx.runtime.jobs.summary(ctx.runtime.jobs.cancel(args.id));
996
- }
997
- },
998
- // ---------------------------------------------------------------------------
999
- // 高风险工具(默认禁用,需开启/审批;自动备份)
1000
- // ---------------------------------------------------------------------------
1001
- {
1002
- name: "git_reset_hard",
1003
- description: "\u786C\u91CD\u7F6E\u5230\u6307\u5B9A\u63D0\u4EA4\uFF08\u4E22\u5F03\u7D22\u5F15\u4E0E\u5DE5\u4F5C\u533A\u66F4\u6539\uFF0C\u9AD8\u98CE\u9669\u4E0D\u53EF\u9006\uFF0C\u6267\u884C\u524D\u81EA\u52A8\u5907\u4EFD\uFF09\u3002",
1004
- risk: "dangerous",
1005
- schema: GitResetHardSchema,
1006
- handler: async (args, ctx) => ctx.git.resetHard(args.target, { dryRun: args.dryRun })
1007
- },
1008
- {
1009
- name: "git_clean",
1010
- description: "\u6E05\u7406\uFF08\u5220\u9664\uFF09\u6240\u6709\u672A\u8DDF\u8E2A\u6587\u4EF6\u4E0E\u76EE\u5F55\uFF08\u9AD8\u98CE\u9669\u4E0D\u53EF\u9006\uFF0C\u6267\u884C\u524D\u81EA\u52A8\u5907\u4EFD\uFF09\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
1011
- risk: "dangerous",
1012
- schema: GitCleanSchema,
1013
- handler: async (args, ctx) => ctx.git.clean({ dryRun: args.dryRun })
1014
- },
1015
- {
1016
- name: "git_push_force",
1017
- description: "\u4EE5 --force-with-lease \u5F3A\u5236\u63A8\u9001\uFF08\u8986\u76D6\u8FDC\u7A0B\u5386\u53F2\uFF0C\u9AD8\u98CE\u9669\uFF0C\u6267\u884C\u524D\u81EA\u52A8\u5907\u4EFD\uFF09\u3002",
1018
- risk: "dangerous",
1019
- schema: GitPushForceSchema,
1020
- handler: async (args, ctx) => ctx.git.pushForce({
1021
- dryRun: args.dryRun,
1022
- remote: args.remote,
1023
- branch: args.branch
1024
- })
1025
- },
1026
- {
1027
- name: "git_branch_delete_force",
1028
- description: "\u5F3A\u5236\u5220\u9664\u5206\u652F\uFF08-D\uFF0C\u4E0D\u68C0\u67E5\u5408\u5E76\u72B6\u6001\uFF0C\u9AD8\u98CE\u9669\uFF09\u3002\u652F\u6301 dry_run \u9884\u89C8\u3002",
1029
- risk: "dangerous",
1030
- schema: GitBranchDeleteForceSchema,
1031
- handler: async (args, ctx) => ctx.git.deleteBranchForce(args.name, { dryRun: args.dryRun })
1032
- },
1033
- {
1034
- name: "git_rebase",
1035
- description: "\u628A\u5F53\u524D\u5206\u652F\u53D8\u57FA\u5230\u6307\u5B9A\u5206\u652F\uFF08\u91CD\u5199\u63D0\u4EA4\u5386\u53F2\uFF0C\u9AD8\u98CE\u9669\uFF0C\u4EA7\u751F\u51B2\u7A81\u9700\u624B\u52A8\u89E3\u51B3\u540E git rebase --continue\uFF09\u3002",
1036
- risk: "dangerous",
1037
- schema: GitRebaseSchema,
1038
- handler: async (args, ctx) => ctx.git.rebase(args.branch, { dryRun: args.dryRun })
1039
- },
1040
- {
1041
- name: "git_rebase_abort",
1042
- description: "\u4E2D\u6B62\u5F53\u524D\u5DE5\u4F5C\u533A rebase\uFF08git rebase --abort\uFF09\u3002\u53EA\u7528\u4E8E\u5DF2\u7ECF\u5F00\u59CB\u7684\u53D8\u57FA\u3002",
1043
- risk: "write",
1044
- schema: GitRebaseAbortSchema,
1045
- handler: async (args, ctx) => ctx.git.rebaseAbort({ dryRun: args.dryRun })
1046
- },
1047
- {
1048
- name: "git_rebase_continue",
1049
- description: "\u7EE7\u7EED\u5F53\u524D\u5DE5\u4F5C\u533A rebase\u3002\u53EF\u628A files \u5199\u5165\u5DE5\u4F5C\u533A\u5E76 git add \u540E\u518D continue\u3002\u51B2\u7A81\u987B\u5148\u89E3\u51B3\u3002",
1050
- risk: "write",
1051
- schema: GitRebaseContinueSchema,
1052
- handler: async (args, ctx) => ctx.git.rebaseContinue({
1053
- dryRun: args.dryRun,
1054
- files: args.files
1055
- })
1056
- }
1057
- ];
1058
- var TOOL_DEF_MAP = new Map(TOOL_DEFS.map((d) => [d.name, d]));
1059
- function toolSummaries() {
1060
- return TOOL_DEFS.map((d) => ({ name: d.name, description: d.description }));
1061
- }
1062
-
1063
- // src/tools/prompts.ts
1064
- var PREAMBLE = [
1065
- "\u4E0D\u8981\u7528\u7EC8\u7AEF git \u4EE3\u66FF\u300C\u9884\u6F14\u3001\u843D\u76D8\u3001\u5F00 PR\u300D\u3002",
1066
- "\u9ED8\u8BA4\u53EA\u7ED9\u6458\u8981\uFF1B\u8981\u770B\u67D0\u6587\u4EF6\u6B63\u6587\u65F6\u518D\u8BF4\u8DEF\u5F84\uFF0C\u6216\u52A0 `detail=true`\u3002\u5199\u64CD\u4F5C\u5148\u5E72\u8DD1\uFF08`dry_run=true`\uFF09\u3002\u9AD8\u98CE\u9669\u64CD\u4F5C\u9ED8\u8BA4\u5173\u7740\u3002"
1067
- ].join("\n");
1068
- var MCP_PROMPTS = [
1069
- {
1070
- name: "safe_commit",
1071
- title: "\u63D0\u4EA4",
1072
- description: "\u5148\u770B\u6539\u4E86\u4EC0\u4E48\uFF0C\u518D\u6682\u5B58\u3001\u518D\u63D0\u4EA4\u3002\u4E0D\u8981\u4E00\u4E0A\u6765\u5C31\u63D0\u4EA4\u3002",
1073
- text: [
1074
- PREAMBLE,
1075
- "",
1076
- "1. \u63D0\u4EA4",
1077
- "\u5148\u770B\u5DE5\u4F5C\u533A\u6709\u6CA1\u6709\u6539\u3001\u6539\u4E86\u4EC0\u4E48\uFF0C\u518D\u6682\u5B58\uFF0C\u518D\u63D0\u4EA4\u3002\u4E0D\u8981\u4E00\u4E0A\u6765\u5C31\u63D0\u4EA4\u3002",
1078
- "",
1079
- "\u5BF9\u5E94\u5DE5\u5177\uFF1A`git_status` \u2192 `git_diff` \u2192 \u6309\u9700 `git_add` \u2192 `git_commit`\u3002"
1080
- ].join("\n")
1081
- },
1082
- {
1083
- name: "merge_preview_apply",
1084
- title: "\u5408\u4E0D\u5408\u5F97\u8FDB\u53BB\uFF0C\u4EE5\u53CA\u5199\u8FDB\u4ED3\u5E93",
1085
- description: "\u53EA\u505A\u9884\u6F14\uFF0C\u4EBA\u9009\u8FB9\uFF0C\u72EC\u7ACB\u76EE\u5F55\u843D\u76D8\u3002\u5DE5\u4F5C\u533A\u5DF2\u7ECF\u5361\u4F4F\u65F6\u4E0D\u8981\u8D70\u8FD9\u6761\u3002",
1086
- text: [
1087
- PREAMBLE,
1088
- "",
1089
- "2. \u5408\u4E0D\u5408\u5F97\u8FDB\u53BB\uFF0C\u4EE5\u53CA\u5199\u8FDB\u4ED3\u5E93",
1090
- "\u5148\u770B\u5DE5\u4F5C\u533A\u662F\u4E0D\u662F\u5DF2\u7ECF\u5361\u5728 merge / rebase \u91CC\u3002\u5361\u4F4F\u4E86\u8D70\u300C\u5DE5\u4F5C\u533A\u5DF2\u7ECF\u5361\u5728 merge \u6216 rebase \u91CC\u300D\uFF0C\u4E0D\u8981\u8D70\u8FD9\u6761\u3002",
1091
- "",
1092
- "\u95EE\u80FD\u4E0D\u80FD\u5408\u8FDB\u53BB\u65F6\uFF0C\u53EA\u505A\u9884\u6F14\uFF0C\u4E0D\u8981\u5728\u5F53\u524D\u5DE5\u4F5C\u533A\u6267\u884C merge\u3002`into` \u662F\u8981\u5408\u8FDB\u53BB\u7684\u76EE\u6807\uFF08\u7EBF\u4E0A\uFF09\uFF0C`from` \u662F\u4F60\u7684\u5206\u652F\u3002\u6709\u51B2\u7A81\u65F6\u628A\u6B63\u6587\u7ED9\u4EBA\u770B\u3001\u4EBA\u9009\u8FB9\uFF0C\u4E0D\u8981\u8BA9\u6A21\u578B\u81EA\u52A8\u9009\u3002",
1093
- "",
1094
- "\u770B\u5B8C\u8981\u5199\u8FDB\u4ED3\u5E93\uFF1A\u7528\u72EC\u7ACB\u76EE\u5F55\u843D\u76D8\uFF0C\u4E0D\u8981\u5207\u8D70\u4F60\u6B63\u5728\u5E72\u6D3B\u7684\u5206\u652F\u3002",
1095
- "",
1096
- "\u5BF9\u5E94\u5DE5\u5177\uFF1A`git_merge_preview` / `git_merge_rehearse`\uFF08\u51B2\u7A81\u884C\u662F\u8C01\u6539\u7684\u7528 `git_merge_blame`\uFF09\u2192 `git_apply_resolve`\u3002\u7981\u6B62\u7528 `git_merge` \u5192\u5145\u9884\u6F14\u3002"
1097
- ].join("\n")
1098
- },
1099
- {
1100
- name: "workspace_continue",
1101
- title: "\u5DE5\u4F5C\u533A\u5DF2\u7ECF\u5361\u5728 merge \u6216 rebase \u91CC",
1102
- description: "\u770B\u72B6\u6001\uFF0C\u7136\u540E\u7EE7\u7EED\u6216\u653E\u5F03\u3002\u4E0D\u8981\u7528\u9884\u6F14\u843D\u76D8\u53BB\u6536\u5C3E\u3002",
1103
- text: [
1104
- PREAMBLE,
1105
- "",
1106
- "3. \u5DE5\u4F5C\u533A\u5DF2\u7ECF\u5361\u5728 merge \u6216 rebase \u91CC",
1107
- "\u8FD9\u662F\u53E6\u4E00\u56DE\u4E8B\uFF1A\u51B2\u7A81\u5DF2\u7ECF\u53D1\u751F\u5728\u5F53\u524D\u76EE\u5F55\u4E86\u3002\u770B\u72B6\u6001\uFF0C\u7136\u540E\u7EE7\u7EED\u6216\u653E\u5F03\u3002\u4E0D\u8981\u7528\u300C\u5408\u4E0D\u5408\u5F97\u8FDB\u53BB\uFF0C\u4EE5\u53CA\u5199\u8FDB\u4ED3\u5E93\u300D\u7684\u843D\u76D8\u53BB\u6536\u5C3E\u3002",
1108
- "",
1109
- "\u5BF9\u5E94\u5DE5\u5177\uFF1A`git_status`\uFF08\u770B `operation`\uFF09\u2192 `git_merge_continue` / `git_rebase_continue` \u6216 abort\u3002\u7981\u6B62\u7528 `git_apply_resolve`\u3002"
1110
- ].join("\n")
1111
- },
1112
- {
1113
- name: "open_mr",
1114
- title: "\u5F00 Pull Request / Merge Request",
1115
- description: "\u5148\u51C6\u5907\u6807\u9898\u548C\u76EE\u6807\u5206\u652F\uFF0C\u518D\u521B\u5EFA\u3002Token \u5728\u7F51\u9875\u8BBE\u7F6E\u91CC\uFF0C\u4E0D\u8981\u585E\u8FDB\u5DE5\u5177\u53C2\u6570\u3002",
1116
- text: [
1117
- PREAMBLE,
1118
- "",
1119
- "4. \u5F00 Pull Request / Merge Request",
1120
- "\u5148\u51C6\u5907\u6807\u9898\u548C\u76EE\u6807\u5206\u652F\u7B49\u4FE1\u606F\uFF0C\u518D\u521B\u5EFA\u3002\u767B\u5F55\u7528\u7684 Token \u5199\u5728\u7F51\u9875\u8BBE\u7F6E\u91CC\uFF0C\u4E0D\u8981\u585E\u8FDB\u5DE5\u5177\u53C2\u6570\u3002",
1121
- "",
1122
- "\u5BF9\u5E94\u5DE5\u5177\uFF1A`git_mr_prepare` \u2192 `git_mr_create`\u3002"
1123
- ].join("\n")
1124
- }
1125
- ];
1126
- function registerMcpPrompts(server) {
1127
- const register = server.registerPrompt.bind(server);
1128
- for (const p of MCP_PROMPTS) {
1129
- register(p.name, { title: p.title, description: p.description }, () => ({
1130
- description: p.description,
1131
- messages: [{ role: "user", content: { type: "text", text: p.text } }]
1132
- }));
1133
- }
1134
- }
1135
-
1136
- // src/tools/resources.ts
1137
- import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
1138
- var MIME = "application/json";
1139
- function jsonContents(uri, data) {
1140
- return {
1141
- contents: [
1142
- {
1143
- uri,
1144
- mimeType: MIME,
1145
- text: JSON.stringify(data)
1146
- }
1147
- ]
1148
- };
1149
- }
1150
- function overviewWithoutDays(runtime) {
1151
- return collectRepoOverviews(runtime).then(
1152
- (repos) => repos.map((row) => {
1153
- const { activity: _days, ...rest } = row;
1154
- return rest;
1155
- })
1156
- );
1157
- }
1158
- function registerMcpResources(server, runtime) {
1159
- const register = server.registerResource.bind(server);
1160
- register(
1161
- "repos",
1162
- "git-cockpit://repos",
1163
- { title: "\u5DF2\u6253\u5F00\u4ED3\u5E93\u8109\u640F", description: "\u4E0E git_repo_overview \u540C\u6458\u8981\uFF0C\u4E0D\u542B\u6BCF\u65E5\u683C\u5B50", mimeType: MIME },
1164
- async (uri) => jsonContents(uri.href, { repos: await overviewWithoutDays(runtime) })
1165
- );
1166
- register(
1167
- "repo-current",
1168
- "git-cockpit://repo/current",
1169
- { title: "\u6700\u8FD1\u6253\u5F00\u4ED3", description: "\u6700\u8FD1\u6253\u5F00\u4ED3\u5E93\u7684\u5DE5\u4F5C\u533A\u6458\u8981\uFF0C\u4E0D\u542B\u6587\u4EF6\u5217\u8868\u6B63\u6587", mimeType: MIME },
1170
- async (uri) => {
1171
- const handle = await runtime.repoManager.getCurrent();
1172
- if (!handle) {
1173
- return jsonContents(uri.href, { available: false, hint: "\u6CA1\u6709\u5DF2\u6253\u5F00\u7684\u4ED3\u5E93" });
1174
- }
1175
- const s = await handle.service.getStatus();
1176
- return jsonContents(uri.href, {
1177
- available: true,
1178
- path: handle.record.path,
1179
- current: s.current,
1180
- tracking: s.tracking,
1181
- ahead: s.ahead,
1182
- behind: s.behind,
1183
- operation: s.operation,
1184
- isClean: s.isClean,
1185
- stagedCount: s.staged.length,
1186
- unstagedCount: s.unstaged.length,
1187
- untrackedCount: s.untracked.length,
1188
- conflictCount: s.conflicted.length,
1189
- conflicted: s.conflicted.slice(0, 20)
1190
- });
1191
- }
1192
- );
1193
- register(
1194
- "jobs",
1195
- "git-cockpit://jobs",
1196
- { title: "\u540E\u53F0\u4EFB\u52A1\u5217\u8868", description: "\u4E0E git_job_list \u540C\u6458\u8981", mimeType: MIME },
1197
- async (uri) => jsonContents(uri.href, {
1198
- jobs: runtime.jobs.list().map((j) => runtime.jobs.summary(j))
1199
- })
1200
- );
1201
- register(
1202
- "job",
1203
- new ResourceTemplate("git-cockpit://jobs/{id}", {
1204
- list: async () => ({
1205
- resources: runtime.jobs.list().map((j) => ({
1206
- uri: `git-cockpit://jobs/${j.id}`,
1207
- name: j.title,
1208
- description: `${j.kind} ${j.status}`,
1209
- mimeType: MIME
1210
- }))
1211
- })
1212
- }),
1213
- { title: "\u5355\u6761\u540E\u53F0\u4EFB\u52A1", description: "\u72B6\u6001 + \u65E5\u5FD7\u5C3E + result \u6458\u8981", mimeType: MIME },
1214
- async (uri, variables) => {
1215
- const id = String(variables.id ?? "");
1216
- const job = runtime.jobs.get(id);
1217
- if (!job) {
1218
- return jsonContents(uri.href, { error: "\u4EFB\u52A1\u4E0D\u5B58\u5728", id });
1219
- }
1220
- const summary = runtime.jobs.summary(job);
1221
- return jsonContents(uri.href, {
1222
- ...summary,
1223
- logs: job.logs.slice(-20)
1224
- });
1225
- }
1226
- );
1227
- }
1228
-
1229
- // src/mcpServer.ts
1230
- var MCP_SERVER_INFO = { name: "git-cockpit-mcp-server", version };
1231
- function createMcpServer(runtime) {
1232
- const server = new McpServer2(
1233
- { name: MCP_SERVER_INFO.name, version: MCP_SERVER_INFO.version },
1234
- {
1235
- capabilities: { tools: {}, prompts: {}, resources: {} },
1236
- instructions: [
1237
- "Git Cockpit MCP Server\uFF1A\u53EF\u89C6\u5316\u7684 Git \u64CD\u4F5C\u5DE5\u5177\u3002",
1238
- "\u4F7F\u7528\u89C4\u5219\uFF1A",
1239
- "1. \u5148\u8C03\u7528 git_status \u67E5\u770B\u5DE5\u4F5C\u533A\u72B6\u6001\uFF08\u542B operation=merge/rebase \u65F6\u8D70\u5DE5\u4F5C\u533A\u6536\u5C3E\uFF0C\u4E0D\u8981\u7528\u9884\u6F14\u5DE5\u5177\u5192\u5145\uFF09\uFF1B",
1240
- "2. \u5199\u64CD\u4F5C\uFF08git_add/git_commit/git_fetch/git_push \u7B49\uFF09\u9ED8\u8BA4\u53EF\u6267\u884C\uFF0C\u4F46\u8BF7\u5148\u4F7F\u7528 dry_run=true \u9884\u89C8\u5F71\u54CD\u8303\u56F4\uFF1B",
1241
- "3. \u9AD8\u98CE\u9669\u5DE5\u5177\uFF08git_reset_hard/git_clean/git_push_force/git_branch_delete_force/git_rebase\uFF09\u9ED8\u8BA4\u7981\u7528\uFF0C",
1242
- " \u9700\u7BA1\u7406\u5458\u5728\u914D\u7F6E\u4E2D\u5F00\u542F\uFF1B\u6267\u884C\u65F6\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u5907\u4EFD\u5F53\u524D\u72B6\u6001\uFF1B",
1243
- "4. \u672A\u6307\u5B9A repoPath \u65F6\u4F7F\u7528\u6700\u8FD1\u6253\u5F00\u7684\u4ED3\u5E93\uFF1B",
1244
- "5. \u5408\u5E76\u9884\u6F14\u7528 git_merge_preview / git_merge_rehearse\uFF08merge-tree\uFF0C\u4E0D\u6539\u5DE5\u4F5C\u533A\uFF09\uFF0C",
1245
- " into=\u5408\u5165\u76EE\u6807/\u7EBF\u4E0A\uFF0Cfrom=\u6211\u7684\u5206\u652F\uFF1B\u7981\u6B62\u7528 git_merge \u505A\u9884\u6F14\u3002",
1246
- " \u51B2\u7A81\u884C\u4F5C\u8005\u7528 git_merge_blame\uFF08\u6309\u6587\u4EF6\u3001\u53EA\u8BFB\uFF09\u3002",
1247
- " \u591A\u5206\u652F\u626B\u63CF\u7528 git_merge_survey\uFF1B\u5EFA\u8BAE\u5408\u5165\u987A\u5E8F\u7528 git_merge_order\u3002",
1248
- "6. \u843D\u76D8\u7528 git_apply_resolve\uFF08\u72EC\u7ACB worktree\uFF0C\u4E3B\u533A\u4E0D\u5207\u6362\uFF09\uFF1B\u51B2\u7A81\u65F6\u628A\u9009\u8FB9\u540E\u7684 files \u4E00\u5E76\u4F20\u5165\u3002",
1249
- "7. \u5DE5\u4F5C\u533A\u5DF2\u7ECF\u51B2\u7A81\uFF1Agit_merge_continue / git_rebase_continue\uFF08\u53EF\u5E26 files\uFF09\u6216 abort\uFF1B\u4E0D\u662F apply_resolve\u3002",
1250
- "8. \u66F4\u65B0\u8FDC\u7A0B\u8DDF\u8E2A\u5206\u652F\u7528 git_fetch\u3002\u4E24\u5206\u652F\u5206\u53C9\u5BF9\u6BD4\u7528 git_branch_graph \u5E76\u4F20 into/from\u3002",
1251
- "9. \u5F00 PR/MR \u7528 git_mr_prepare / git_mr_create\u3002\u65B9\u5F0F\u4E0E Token \u5728\u8BBE\u7F6E MR \u914D\u7F6E\uFF08\u4E0D\u8FDB\u5DE5\u5177\u53C2\u6570\uFF09\uFF1A\u672C\u673A gh\xB7glab / Token / \u6D4F\u89C8\u5668\u9875\u3002\u627E\u4E0D\u5230 CLI \u65F6\u7ED3\u679C\u542B\u5B98\u65B9\u5B89\u88C5\u5730\u5740\u3002",
1252
- "10. \u5927\u7ED3\u679C\u9ED8\u8BA4\u6458\u8981\uFF1Agit_diff / git_show / git_merge_rehearse \u8981\u6B63\u6587\u8BF7\u52A0 path \u6216 detail=true\u3002",
1253
- "11. \u5DF2\u6253\u5F00\u4ED3\u5E93\u8109\u640F\u7528 git_repo_overview\u3002\u957F\u4EFB\u52A1\u7528 git_job_list / git_job_get / git_job_cancel\u3002",
1254
- "12. \u63D0\u4EA4 / \u9884\u6F14\u843D\u76D8 / \u5DE5\u4F5C\u533A\u6536\u5C3E / \u5F00 PR \u7528 Prompt\uFF1Asafe_commit\u3001merge_preview_apply\u3001workspace_continue\u3001open_mr\uFF08\u6B63\u6587\u4E0E README\u300CAgent \u8BE5\u600E\u4E48\u7528\u300D\u76F8\u540C\uFF09\u3002",
1255
- "13. \u53EA\u8BFB\u5217\u8868\u53EF\u8BFB Resource\uFF1Agit-cockpit://repos\u3001git-cockpit://repo/current\u3001git-cockpit://jobs\u3001git-cockpit://jobs/{id}\u3002"
1256
- ].join("\n")
1257
- }
1258
- );
1259
- for (const def of TOOL_DEFS) {
1260
- const register = server.registerTool.bind(server);
1261
- register(
1262
- def.name,
1263
- { description: def.description, inputSchema: def.schema },
1264
- async (args) => {
1265
- const exec = await executeTool(def, args ?? {}, {
1266
- runtime,
1267
- source: "mcp"
1268
- });
1269
- return {
1270
- content: [{ type: "text", text: formatResultForMcp(exec) }],
1271
- isError: !exec.success
1272
- };
1273
- }
1274
- );
1275
- }
1276
- registerMcpPrompts(server);
1277
- registerMcpResources(server, runtime);
1278
- return server;
1279
- }
1280
- async function startMcpStdio(runtime) {
1281
- const server = createMcpServer(runtime);
1282
- const transport = new StdioServerTransport();
1283
- await server.connect(transport);
1284
- }
1285
- var McpHttpHandler = class {
1286
- constructor(runtime) {
1287
- this.runtime = runtime;
1288
- }
1289
- runtime;
1290
- sessions = /* @__PURE__ */ new Map();
1291
- async handle(req, res, parsedBody) {
1292
- const headerId = req.headers["mcp-session-id"];
1293
- if (typeof headerId === "string" && headerId) {
1294
- const session2 = this.sessions.get(headerId);
1295
- if (!session2) {
1296
- res.statusCode = 404;
1297
- res.end("MCP session not found");
1298
- return;
1299
- }
1300
- await session2.transport.handleRequest(req, res, parsedBody);
1301
- return;
1302
- }
1303
- const transport = new StreamableHTTPServerTransport({
1304
- sessionIdGenerator: () => randomUUID()
1305
- });
1306
- const server = createMcpServer(this.runtime);
1307
- await server.connect(transport);
1308
- const session = { id: "", server, transport };
1309
- transport.onclose = () => {
1310
- if (session.id) this.sessions.delete(session.id);
1311
- void server.close().catch(() => void 0);
1312
- };
1313
- await transport.handleRequest(req, res, parsedBody);
1314
- const sid = transport.sessionId;
1315
- if (sid) {
1316
- session.id = sid;
1317
- this.sessions.set(sid, session);
1318
- }
1319
- }
1320
- dispose() {
1321
- for (const session of this.sessions.values()) {
1322
- session.transport.close().catch(() => void 0);
1323
- }
1324
- this.sessions.clear();
1325
- }
1326
- };
1327
-
1328
- export {
1329
- collectRepoOverviews,
1330
- version,
1331
- formatResultForMcp,
1332
- executeTool,
1333
- TOOL_DEFS,
1334
- TOOL_DEF_MAP,
1335
- toolSummaries,
1336
- MCP_PROMPTS,
1337
- MCP_SERVER_INFO,
1338
- createMcpServer,
1339
- startMcpStdio,
1340
- McpHttpHandler
1341
- };
1342
- //# sourceMappingURL=chunk-I7QFIFI2.js.map