@shaohui_jin/git-cockpit-mcp-server 0.1.14 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,7 +54,8 @@ git-cockpit version # 输出版本号
54
54
 
55
55
  - **多仓库管理**:统一打开/移除仓库,跨仓库操作
56
56
  - **安全机制**:工具按风险分级(readonly / write / dangerous),写操作 dry-run 预览 → 确认 → 执行;高危操作可配置人工审批
57
- - **内置 Web UI**:状态 / 历史 / 日志 / 设置视图,支持提交、分支、stash、pull/push、硬重置等操作
57
+ - **MCP 默认摘要**:`git_diff` / `git_show` / `git_merge_rehearse` 默认不回完整正文;要正文请加 `path` 或 `detail=true`(网页 GET 仍是全文)
58
+ - **内置 Web UI**:工作台 / 状态 / 历史 / 日志 / 设置视图,支持提交、分支、stash、pull/push、硬重置等操作
58
59
  - **审计日志**:所有工具调用记录落库,可回溯
59
60
  - **备份**:高危操作前自动备份分支引用与 stash 快照
60
61
 
@@ -7,7 +7,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
7
7
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8
8
 
9
9
  // package.json
10
- var version = "0.1.14";
10
+ var version = "0.1.15";
11
11
 
12
12
  // src/tools/handlers.ts
13
13
  import { z } from "zod";
@@ -16,6 +16,98 @@ import {
16
16
  GitOperationError,
17
17
  PermissionError
18
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_file_content") {
76
+ if (detail2) return result;
77
+ const row = asRecord(result);
78
+ const content = typeof row?.content === "string" ? row.content : "";
79
+ const bytes = Buffer.byteLength(content, "utf8");
80
+ if (bytes <= FILE_CONTENT_LIMIT) return result;
81
+ return {
82
+ path: args.path,
83
+ truncated: true,
84
+ bytes,
85
+ detailAvailable: true
86
+ };
87
+ }
88
+ return result;
89
+ }
90
+ function formatResultForMcp(exec) {
91
+ if (!exec.success) {
92
+ const prefix = exec.error?.requiredApproval ? "\u9700\u8981\u5BA1\u6279" : "\u64CD\u4F5C\u5931\u8D25";
93
+ return `${prefix}\uFF1A${exec.error?.message ?? "\u672A\u77E5\u9519\u8BEF"}`;
94
+ }
95
+ const parts = [];
96
+ if (exec.dryRun) {
97
+ parts.push("[dry-run \u9884\u89C8] \u672A\u5B9E\u9645\u6267\u884C\u4EFB\u4F55\u64CD\u4F5C");
98
+ }
99
+ if (exec.backupCreated) {
100
+ parts.push(
101
+ `[\u81EA\u52A8\u5907\u4EFD] \u5206\u652F ${exec.backupCreated.branch ?? "(\u672A\u521B\u5EFA)"}${exec.backupCreated.stashRef ? `\uFF0Cstash ${exec.backupCreated.stashRef}` : ""}`
102
+ );
103
+ }
104
+ const summarized = summarizeForAgent(exec.tool, exec.result, exec.args ?? {});
105
+ const payload = typeof summarized === "string" ? summarized : JSON.stringify(summarized);
106
+ parts.push(payload);
107
+ return parts.join("\n\n");
108
+ }
109
+
110
+ // src/tools/handlers.ts
19
111
  async function resolveRepo(runtime, ctx, argsPath) {
20
112
  if (ctx.repoId !== void 0) {
21
113
  const handle = await runtime.repoManager.getById(ctx.repoId);
@@ -76,11 +168,15 @@ async function executeTool(def, rawArgs, ctx) {
76
168
  record("error", rawArgs, e.message);
77
169
  return { tool: def.name, source: ctx.source, dryRun: dryRun2, success: false, error: e, durationMs: Date.now() - t0 };
78
170
  }
79
- const handle = await resolveRepo(runtime, ctx, args.repoPath);
80
- const git = handle.service;
81
- const repoPath2 = git.repoPath;
171
+ let git;
172
+ let repoPath2 = "";
173
+ if (def.needsRepo !== false) {
174
+ const handle = await resolveRepo(runtime, ctx, args.repoPath);
175
+ git = handle.service;
176
+ repoPath2 = git.repoPath;
177
+ }
82
178
  let backup = null;
83
- if (!dryRun2 && def.risk === "dangerous" && runtime.config.git.backupOnDangerousOps) {
179
+ if (!dryRun2 && def.risk === "dangerous" && runtime.config.git.backupOnDangerousOps && git) {
84
180
  backup = await new BackupManager(git).createBackup();
85
181
  args = { ...args, __backup: { branch: backup.branch, stashRef: backup.stashRef } };
86
182
  }
@@ -95,7 +191,8 @@ async function executeTool(def, rawArgs, ctx) {
95
191
  result,
96
192
  preview,
97
193
  backupCreated: backup,
98
- durationMs: Date.now() - t0
194
+ durationMs: Date.now() - t0,
195
+ args
99
196
  };
100
197
  } catch (err) {
101
198
  const e = extractError(err);
@@ -111,34 +208,68 @@ async function executeTool(def, rawArgs, ctx) {
111
208
  };
112
209
  }
113
210
  }
114
- function formatResultForMcp(exec) {
115
- if (!exec.success) {
116
- const prefix = exec.error?.requiredApproval ? "\u9700\u8981\u5BA1\u6279" : "\u64CD\u4F5C\u5931\u8D25";
117
- return `${prefix}\uFF1A${exec.error?.message ?? "\u672A\u77E5\u9519\u8BEF"}`;
118
- }
119
- const parts = [];
120
- if (exec.dryRun) {
121
- parts.push("[dry-run \u9884\u89C8] \u672A\u5B9E\u9645\u6267\u884C\u4EFB\u4F55\u64CD\u4F5C");
122
- }
123
- if (exec.backupCreated) {
124
- parts.push(`[\u81EA\u52A8\u5907\u4EFD] \u5206\u652F ${exec.backupCreated.branch ?? "(\u672A\u521B\u5EFA)"}${exec.backupCreated.stashRef ? `\uFF0Cstash ${exec.backupCreated.stashRef}` : ""}`);
125
- }
126
- const payload = typeof exec.result === "string" ? exec.result : JSON.stringify(exec.result, null, 2);
127
- parts.push(payload);
128
- return parts.join("\n\n");
129
- }
130
211
 
131
212
  // src/tools/index.ts
132
213
  import {
133
214
  enrichPrepareMr,
134
215
  createPullOrMergeRequest,
135
- BackupManager as BackupManager2
216
+ BackupManager as BackupManager2,
217
+ GitOperationError as GitOperationError2,
218
+ SURVEY_ASYNC_THRESHOLD
136
219
  } from "@shaohui_jin/git-cockpit-core";
137
220
 
221
+ // src/overview.ts
222
+ var CONCURRENCY = 4;
223
+ function unavailableOverview(path, extra = {}) {
224
+ const name = path.replace(/[\\/]+$/, "").split(/[\\/]/).pop() || path;
225
+ return {
226
+ path,
227
+ name,
228
+ available: false,
229
+ current: "",
230
+ tracking: null,
231
+ ahead: 0,
232
+ behind: 0,
233
+ dirtyCount: 0,
234
+ conflictCount: 0,
235
+ operation: "none",
236
+ tempMergeBranchCount: 0,
237
+ ...extra
238
+ };
239
+ }
240
+ async function mapPool(items, n, fn) {
241
+ const out = new Array(items.length);
242
+ let i = 0;
243
+ async function worker() {
244
+ while (i < items.length) {
245
+ const idx = i++;
246
+ out[idx] = await fn(items[idx]);
247
+ }
248
+ }
249
+ const workers = Math.min(n, items.length);
250
+ await Promise.all(Array.from({ length: workers }, () => worker()));
251
+ return out;
252
+ }
253
+ async function collectRepoOverviews(runtime) {
254
+ const repos = runtime.repoManager.list();
255
+ if (!repos.length) return [];
256
+ return mapPool(repos, CONCURRENCY, async (r) => {
257
+ try {
258
+ const handle = await runtime.repoManager.getById(r.id);
259
+ if (!handle) return unavailableOverview(r.path, { id: r.id, lastOpenedAt: r.lastOpenedAt });
260
+ const o = await handle.service.getOverview();
261
+ return { ...o, id: r.id, lastOpenedAt: r.lastOpenedAt };
262
+ } catch {
263
+ return unavailableOverview(r.path, { id: r.id, lastOpenedAt: r.lastOpenedAt });
264
+ }
265
+ });
266
+ }
267
+
138
268
  // src/tools/schemas.ts
139
269
  import { z as z2 } from "zod";
140
270
  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");
141
271
  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");
272
+ 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");
142
273
  var maxCount = z2.number().int().min(1).max(1e4).optional().describe("\u6700\u591A\u8FD4\u56DE\u7684\u63D0\u4EA4\u6570\u91CF\u3002");
143
274
  var readonlyBase = { repoPath };
144
275
  var GitStatusSchema = z2.object({ ...readonlyBase });
@@ -157,13 +288,15 @@ var GitDiffSchema = z2.object({
157
288
  to: z2.string().optional().describe("\u7EC8\u70B9\u63D0\u4EA4"),
158
289
  path: z2.string().optional().describe("\u9650\u5B9A\u7684\u6587\u4EF6\u8DEF\u5F84"),
159
290
  staged: z2.boolean().optional().describe("\u67E5\u770B\u5DF2\u6682\u5B58\uFF08\u7D22\u5F15\uFF09\u5DEE\u5F02"),
160
- maxPatchBytes: z2.number().int().positive().optional().describe("diff \u6B63\u6587\u6700\u5927\u5B57\u8282\u6570\uFF0C\u8D85\u51FA\u622A\u65AD")
291
+ maxPatchBytes: z2.number().int().positive().optional().describe("diff \u6B63\u6587\u6700\u5927\u5B57\u8282\u6570\uFF0C\u8D85\u51FA\u622A\u65AD"),
292
+ detail
161
293
  });
162
294
  var GitShowSchema = z2.object({
163
295
  ...readonlyBase,
164
296
  commit: z2.string().describe("\u8981\u67E5\u770B\u7684\u63D0\u4EA4 hash"),
165
297
  path: z2.string().optional().describe("\u9650\u5B9A\u6587\u4EF6"),
166
- maxPatchBytes: z2.number().int().positive().optional()
298
+ maxPatchBytes: z2.number().int().positive().optional(),
299
+ detail
167
300
  });
168
301
  var GitBranchListSchema = z2.object({ ...readonlyBase });
169
302
  var GitTagListSchema = z2.object({ ...readonlyBase });
@@ -171,7 +304,8 @@ var GitRemoteListSchema = z2.object({ ...readonlyBase });
171
304
  var GitFileContentSchema = z2.object({
172
305
  ...readonlyBase,
173
306
  commit: z2.string().describe("\u63D0\u4EA4 hash\uFF08\u5982 HEAD\u3001main\uFF09"),
174
- path: z2.string().describe("\u4ED3\u5E93\u5185\u6587\u4EF6\u76F8\u5BF9\u8DEF\u5F84")
307
+ path: z2.string().describe("\u4ED3\u5E93\u5185\u6587\u4EF6\u76F8\u5BF9\u8DEF\u5F84"),
308
+ detail
175
309
  });
176
310
  var GitGraphSchema = z2.object({
177
311
  ...readonlyBase,
@@ -201,7 +335,8 @@ var GitMergePreviewSchema = z2.object({
201
335
  var GitMergeRehearseSchema = z2.object({
202
336
  ...readonlyBase,
203
337
  ...mergeIntoFrom,
204
- 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")
338
+ 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"),
339
+ detail
205
340
  });
206
341
  var GitMergeBlameSchema = z2.object({
207
342
  ...readonlyBase,
@@ -216,7 +351,8 @@ var GitMergeSurveySchema = z2.object({
216
351
  intos: z2.array(z2.string().min(1)).min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09\u5217\u8868"),
217
352
  froms: z2.array(z2.string().min(1)).min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs\uFF09\u5217\u8868"),
218
353
  fetch: z2.boolean().optional().describe("\u662F\u5426\u5148\u975E\u4EA4\u4E92 fetch\u3002\u7F3A\u7701 true\uFF1B\u6574\u6279\u53EA fetch \u4E00\u6B21"),
219
- remote: z2.string().optional().describe("fetch \u4F7F\u7528\u7684\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin")
354
+ remote: z2.string().optional().describe("fetch \u4F7F\u7528\u7684\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin"),
355
+ 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")
220
356
  });
221
357
  var GitMergeOrderSchema = z2.object({
222
358
  ...readonlyBase,
@@ -298,7 +434,18 @@ var GitStashListSchema = z2.object({ ...readonlyBase });
298
434
  var GitStashShowSchema = z2.object({
299
435
  ...readonlyBase,
300
436
  index: z2.number().int().min(0).optional().describe("stash \u5E8F\u53F7\uFF0C\u7F3A\u7701\u4E3A stash@{0}"),
301
- maxPatchBytes: z2.number().int().positive().optional().describe("diff \u6B63\u6587\u6700\u5927\u5B57\u8282\u6570\uFF0C\u8D85\u51FA\u622A\u65AD")
437
+ maxPatchBytes: z2.number().int().positive().optional().describe("diff \u6B63\u6587\u6700\u5927\u5B57\u8282\u6570\uFF0C\u8D85\u51FA\u622A\u65AD"),
438
+ detail
439
+ });
440
+ var GitRepoOverviewSchema = z2.object({ ...readonlyBase });
441
+ var GitJobListSchema = z2.object({ ...readonlyBase });
442
+ var GitJobGetSchema = z2.object({
443
+ ...readonlyBase,
444
+ id: z2.string().min(1).describe("\u540E\u53F0\u4EFB\u52A1 id")
445
+ });
446
+ var GitJobCancelSchema = z2.object({
447
+ ...writeBase,
448
+ id: z2.string().min(1).describe("\u8981\u53D6\u6D88\u7684\u540E\u53F0\u4EFB\u52A1 id")
302
449
  });
303
450
  var GitStashApplySchema = z2.object({
304
451
  ...writeBase,
@@ -484,17 +631,29 @@ var TOOL_DEFS = [
484
631
  },
485
632
  {
486
633
  name: "git_merge_rehearse",
487
- 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\u3002\u9009\u8FB9\u540E\u628A files[{path,resolvedContent}] \u4EA4\u7ED9 git_apply_resolve\u3002into/from \u542B\u4E49\u540C git_merge_preview\u3002",
634
+ 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",
488
635
  risk: "readonly",
489
636
  schema: GitMergeRehearseSchema,
490
- handler: async (args, ctx) => ctx.git.rehearseMerge({
491
- into: args.into,
492
- from: args.from,
493
- fetch: args.fetch,
494
- remote: args.remote,
495
- path: args.path,
496
- maxFiles: args.maxFiles
497
- })
637
+ handler: async (args, ctx) => {
638
+ const wantBody = args.detail === true || typeof args.path === "string" && args.path.trim().length > 0;
639
+ if (!wantBody) {
640
+ return 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
+ return ctx.git.rehearseMerge({
649
+ into: args.into,
650
+ from: args.from,
651
+ fetch: args.fetch,
652
+ remote: args.remote,
653
+ path: args.path,
654
+ maxFiles: args.maxFiles
655
+ });
656
+ }
498
657
  },
499
658
  {
500
659
  name: "git_merge_blame",
@@ -511,15 +670,31 @@ var TOOL_DEFS = [
511
670
  },
512
671
  {
513
672
  name: "git_merge_survey",
514
- 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\uFF0C\u4E0D\u751F\u6210\u6B63\u6587\u3002\u9002\u5408\u53D1\u5E03\u524D\u626B\u63CF\u3002\u7981\u6B62\u7528 git_merge \u505A\u9884\u6F14\u3002",
673
+ 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",
515
674
  risk: "readonly",
516
675
  schema: GitMergeSurveySchema,
517
- handler: async (args, ctx) => ctx.git.surveyMerges({
518
- intos: args.intos,
519
- froms: args.froms,
520
- fetch: args.fetch,
521
- remote: args.remote
522
- })
676
+ handler: async (args, ctx) => {
677
+ const intos = args.intos;
678
+ const froms = args.froms;
679
+ const asyncWanted = args.async === true || intos.length * froms.length > SURVEY_ASYNC_THRESHOLD;
680
+ if (asyncWanted) {
681
+ const job = ctx.runtime.jobs.startSurvey({
682
+ repoPath: ctx.repoPath,
683
+ intos,
684
+ froms,
685
+ fetch: args.fetch,
686
+ remote: args.remote,
687
+ git: ctx.git
688
+ });
689
+ return { jobId: job.id, status: job.status, async: true, title: job.title };
690
+ }
691
+ return ctx.git.surveyMerges({
692
+ intos,
693
+ froms,
694
+ fetch: args.fetch,
695
+ remote: args.remote
696
+ });
697
+ }
523
698
  },
524
699
  {
525
700
  name: "git_merge_order",
@@ -549,6 +724,34 @@ var TOOL_DEFS = [
549
724
  return enrichPrepareMr({ prep, mr, cwd: ctx.git.repoPath });
550
725
  }
551
726
  },
727
+ {
728
+ name: "git_repo_overview",
729
+ 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\u3002\u4E0D\u56DE diff\u3002",
730
+ risk: "readonly",
731
+ needsRepo: false,
732
+ schema: GitRepoOverviewSchema,
733
+ handler: async (_args, ctx) => ({ repos: await collectRepoOverviews(ctx.runtime) })
734
+ },
735
+ {
736
+ name: "git_job_list",
737
+ description: "\u5217\u51FA\u540E\u53F0\u4EFB\u52A1\u6458\u8981\uFF08\u514B\u9686 / survey / fetch\uFF09\u3002",
738
+ risk: "readonly",
739
+ needsRepo: false,
740
+ schema: GitJobListSchema,
741
+ handler: async (_args, ctx) => ({ jobs: ctx.runtime.jobs.list().map((j) => ctx.runtime.jobs.summary(j)) })
742
+ },
743
+ {
744
+ name: "git_job_get",
745
+ description: "\u67E5\u770B\u4E00\u6761\u540E\u53F0\u4EFB\u52A1\uFF08\u65E5\u5FD7 + \u5B8C\u6210\u65F6\u7684 result \u6458\u8981\uFF09\u3002",
746
+ risk: "readonly",
747
+ needsRepo: false,
748
+ schema: GitJobGetSchema,
749
+ handler: async (args, ctx) => {
750
+ const job = ctx.runtime.jobs.get(args.id);
751
+ if (!job) throw new GitOperationError2("\u4EFB\u52A1\u4E0D\u5B58\u5728", "JOB_NOT_FOUND");
752
+ return ctx.runtime.jobs.detail(job);
753
+ }
754
+ },
552
755
  // ---------------------------------------------------------------------------
553
756
  // 写操作工具(默认开放,需预览/确认)
554
757
  // ---------------------------------------------------------------------------
@@ -757,6 +960,25 @@ var TOOL_DEFS = [
757
960
  });
758
961
  }
759
962
  },
963
+ {
964
+ name: "git_job_cancel",
965
+ 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",
966
+ risk: "write",
967
+ needsRepo: false,
968
+ schema: GitJobCancelSchema,
969
+ handler: async (args, ctx) => {
970
+ if (args.dryRun) {
971
+ return {
972
+ dryRun: true,
973
+ command: "job cancel",
974
+ args: [args.id],
975
+ risk: "low",
976
+ note: "\u5C06\u53D6\u6D88\u8FDB\u884C\u4E2D\u7684\u540E\u53F0\u4EFB\u52A1"
977
+ };
978
+ }
979
+ return ctx.runtime.jobs.summary(ctx.runtime.jobs.cancel(args.id));
980
+ }
981
+ },
760
982
  // ---------------------------------------------------------------------------
761
983
  // 高风险工具(默认禁用,需开启/审批;自动备份)
762
984
  // ---------------------------------------------------------------------------
@@ -844,7 +1066,9 @@ function createMcpServer(runtime) {
844
1066
  "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",
845
1067
  "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",
846
1068
  "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",
847
- "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"
1069
+ "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",
1070
+ "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",
1071
+ "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"
848
1072
  ].join("\n")
849
1073
  }
850
1074
  );
@@ -916,9 +1140,10 @@ var McpHttpHandler = class {
916
1140
  };
917
1141
 
918
1142
  export {
1143
+ collectRepoOverviews,
919
1144
  version,
920
- executeTool,
921
1145
  formatResultForMcp,
1146
+ executeTool,
922
1147
  TOOL_DEFS,
923
1148
  TOOL_DEF_MAP,
924
1149
  toolSummaries,
@@ -927,4 +1152,4 @@ export {
927
1152
  startMcpStdio,
928
1153
  McpHttpHandler
929
1154
  };
930
- //# sourceMappingURL=chunk-UEW5GXWN.js.map
1155
+ //# sourceMappingURL=chunk-4LUNMJTB.js.map