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

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.
@@ -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.16";
11
11
 
12
12
  // src/tools/handlers.ts
13
13
  import { z } from "zod";
@@ -16,6 +16,112 @@ 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_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
19
125
  async function resolveRepo(runtime, ctx, argsPath) {
20
126
  if (ctx.repoId !== void 0) {
21
127
  const handle = await runtime.repoManager.getById(ctx.repoId);
@@ -76,11 +182,15 @@ async function executeTool(def, rawArgs, ctx) {
76
182
  record("error", rawArgs, e.message);
77
183
  return { tool: def.name, source: ctx.source, dryRun: dryRun2, success: false, error: e, durationMs: Date.now() - t0 };
78
184
  }
79
- const handle = await resolveRepo(runtime, ctx, args.repoPath);
80
- const git = handle.service;
81
- const repoPath2 = git.repoPath;
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
+ }
82
192
  let backup = null;
83
- if (!dryRun2 && def.risk === "dangerous" && runtime.config.git.backupOnDangerousOps) {
193
+ if (!dryRun2 && def.risk === "dangerous" && runtime.config.git.backupOnDangerousOps && git) {
84
194
  backup = await new BackupManager(git).createBackup();
85
195
  args = { ...args, __backup: { branch: backup.branch, stashRef: backup.stashRef } };
86
196
  }
@@ -95,7 +205,8 @@ async function executeTool(def, rawArgs, ctx) {
95
205
  result,
96
206
  preview,
97
207
  backupCreated: backup,
98
- durationMs: Date.now() - t0
208
+ durationMs: Date.now() - t0,
209
+ args
99
210
  };
100
211
  } catch (err) {
101
212
  const e = extractError(err);
@@ -111,34 +222,70 @@ async function executeTool(def, rawArgs, ctx) {
111
222
  };
112
223
  }
113
224
  }
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
225
 
131
226
  // src/tools/index.ts
132
227
  import {
133
228
  enrichPrepareMr,
134
229
  createPullOrMergeRequest,
135
- BackupManager as BackupManager2
230
+ BackupManager as BackupManager2,
231
+ GitOperationError as GitOperationError2,
232
+ SURVEY_ASYNC_THRESHOLD
136
233
  } from "@shaohui_jin/git-cockpit-core";
137
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
+
138
284
  // src/tools/schemas.ts
139
285
  import { z as z2 } from "zod";
140
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");
141
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");
142
289
  var maxCount = z2.number().int().min(1).max(1e4).optional().describe("\u6700\u591A\u8FD4\u56DE\u7684\u63D0\u4EA4\u6570\u91CF\u3002");
143
290
  var readonlyBase = { repoPath };
144
291
  var GitStatusSchema = z2.object({ ...readonlyBase });
@@ -157,13 +304,15 @@ var GitDiffSchema = z2.object({
157
304
  to: z2.string().optional().describe("\u7EC8\u70B9\u63D0\u4EA4"),
158
305
  path: z2.string().optional().describe("\u9650\u5B9A\u7684\u6587\u4EF6\u8DEF\u5F84"),
159
306
  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")
307
+ maxPatchBytes: z2.number().int().positive().optional().describe("diff \u6B63\u6587\u6700\u5927\u5B57\u8282\u6570\uFF0C\u8D85\u51FA\u622A\u65AD"),
308
+ detail
161
309
  });
162
310
  var GitShowSchema = z2.object({
163
311
  ...readonlyBase,
164
312
  commit: z2.string().describe("\u8981\u67E5\u770B\u7684\u63D0\u4EA4 hash"),
165
313
  path: z2.string().optional().describe("\u9650\u5B9A\u6587\u4EF6"),
166
- maxPatchBytes: z2.number().int().positive().optional()
314
+ maxPatchBytes: z2.number().int().positive().optional(),
315
+ detail
167
316
  });
168
317
  var GitBranchListSchema = z2.object({ ...readonlyBase });
169
318
  var GitTagListSchema = z2.object({ ...readonlyBase });
@@ -171,7 +320,8 @@ var GitRemoteListSchema = z2.object({ ...readonlyBase });
171
320
  var GitFileContentSchema = z2.object({
172
321
  ...readonlyBase,
173
322
  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")
323
+ path: z2.string().describe("\u4ED3\u5E93\u5185\u6587\u4EF6\u76F8\u5BF9\u8DEF\u5F84"),
324
+ detail
175
325
  });
176
326
  var GitGraphSchema = z2.object({
177
327
  ...readonlyBase,
@@ -201,7 +351,8 @@ var GitMergePreviewSchema = z2.object({
201
351
  var GitMergeRehearseSchema = z2.object({
202
352
  ...readonlyBase,
203
353
  ...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")
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
205
356
  });
206
357
  var GitMergeBlameSchema = z2.object({
207
358
  ...readonlyBase,
@@ -216,7 +367,8 @@ var GitMergeSurveySchema = z2.object({
216
367
  intos: z2.array(z2.string().min(1)).min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09\u5217\u8868"),
217
368
  froms: z2.array(z2.string().min(1)).min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs\uFF09\u5217\u8868"),
218
369
  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")
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")
220
372
  });
221
373
  var GitMergeOrderSchema = z2.object({
222
374
  ...readonlyBase,
@@ -298,7 +450,18 @@ var GitStashListSchema = z2.object({ ...readonlyBase });
298
450
  var GitStashShowSchema = z2.object({
299
451
  ...readonlyBase,
300
452
  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")
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")
302
465
  });
303
466
  var GitStashApplySchema = z2.object({
304
467
  ...writeBase,
@@ -484,17 +647,29 @@ var TOOL_DEFS = [
484
647
  },
485
648
  {
486
649
  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",
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",
488
651
  risk: "readonly",
489
652
  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
- })
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
+ }
498
673
  },
499
674
  {
500
675
  name: "git_merge_blame",
@@ -511,15 +686,31 @@ var TOOL_DEFS = [
511
686
  },
512
687
  {
513
688
  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",
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",
515
690
  risk: "readonly",
516
691
  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
- })
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
+ }
523
714
  },
524
715
  {
525
716
  name: "git_merge_order",
@@ -549,6 +740,34 @@ var TOOL_DEFS = [
549
740
  return enrichPrepareMr({ prep, mr, cwd: ctx.git.repoPath });
550
741
  }
551
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
+ },
552
771
  // ---------------------------------------------------------------------------
553
772
  // 写操作工具(默认开放,需预览/确认)
554
773
  // ---------------------------------------------------------------------------
@@ -757,6 +976,25 @@ var TOOL_DEFS = [
757
976
  });
758
977
  }
759
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
+ },
760
998
  // ---------------------------------------------------------------------------
761
999
  // 高风险工具(默认禁用,需开启/审批;自动备份)
762
1000
  // ---------------------------------------------------------------------------
@@ -844,7 +1082,9 @@ function createMcpServer(runtime) {
844
1082
  "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
1083
  "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
1084
  "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"
1085
+ "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",
1086
+ "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",
1087
+ "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
1088
  ].join("\n")
849
1089
  }
850
1090
  );
@@ -916,9 +1156,10 @@ var McpHttpHandler = class {
916
1156
  };
917
1157
 
918
1158
  export {
1159
+ collectRepoOverviews,
919
1160
  version,
920
- executeTool,
921
1161
  formatResultForMcp,
1162
+ executeTool,
922
1163
  TOOL_DEFS,
923
1164
  TOOL_DEF_MAP,
924
1165
  toolSummaries,
@@ -927,4 +1168,4 @@ export {
927
1168
  startMcpStdio,
928
1169
  McpHttpHandler
929
1170
  };
930
- //# sourceMappingURL=chunk-UEW5GXWN.js.map
1171
+ //# sourceMappingURL=chunk-UXORB6SE.js.map