opencode-gitlab-plugin 2.4.0 → 2.6.0

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.
@@ -3945,7 +3945,7 @@ var require_core = __commonJS({
3945
3945
  constructor(opts = {}) {
3946
3946
  this.schemas = {};
3947
3947
  this.refs = {};
3948
- this.formats = {};
3948
+ this.formats = /* @__PURE__ */ Object.create(null);
3949
3949
  this._compilations = /* @__PURE__ */ new Set();
3950
3950
  this._loading = {};
3951
3951
  this._cache = /* @__PURE__ */ new Map();
@@ -10507,7 +10507,6 @@ ZodNaN.create = (params) => {
10507
10507
  ...processCreateParams(params)
10508
10508
  });
10509
10509
  };
10510
- var BRAND = Symbol("zod_brand");
10511
10510
  var ZodBranded = class extends ZodType {
10512
10511
  _parse(input) {
10513
10512
  const { ctx } = this._processInputParams(input);
@@ -10987,7 +10986,7 @@ function $constructor(name, initializer3, params) {
10987
10986
  Object.defineProperty(_, "name", { value: name });
10988
10987
  return _;
10989
10988
  }
10990
- var $brand = Symbol("zod_brand");
10989
+ var $brand = /* @__PURE__ */ Symbol("zod_brand");
10991
10990
  var $ZodAsyncError = class extends Error {
10992
10991
  constructor() {
10993
10992
  super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);
@@ -11132,7 +11131,7 @@ function floatSafeRemainder2(val, step) {
11132
11131
  const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", ""));
11133
11132
  return valInt % stepInt / 10 ** decCount;
11134
11133
  }
11135
- var EVALUATING = Symbol("evaluating");
11134
+ var EVALUATING = /* @__PURE__ */ Symbol("evaluating");
11136
11135
  function defineLazy(object3, key, getter) {
11137
11136
  let value = void 0;
11138
11137
  Object.defineProperty(object3, key, {
@@ -19977,8 +19976,8 @@ function yo_default() {
19977
19976
  }
19978
19977
 
19979
19978
  // node_modules/zod/v4/core/registries.js
19980
- var $output = Symbol("ZodOutput");
19981
- var $input = Symbol("ZodInput");
19979
+ var $output = /* @__PURE__ */ Symbol("ZodOutput");
19980
+ var $input = /* @__PURE__ */ Symbol("ZodInput");
19982
19981
  var $ZodRegistry = class {
19983
19982
  constructor() {
19984
19983
  this._map = /* @__PURE__ */ new WeakMap();
@@ -24820,7 +24819,7 @@ function isTerminal(status) {
24820
24819
  }
24821
24820
 
24822
24821
  // node_modules/zod-to-json-schema/dist/esm/Options.js
24823
- var ignoreOverride = Symbol("Let zodToJsonSchema decide on which parser to use");
24822
+ var ignoreOverride = /* @__PURE__ */ Symbol("Let zodToJsonSchema decide on which parser to use");
24824
24823
  var defaultOptions = {
24825
24824
  name: void 0,
24826
24825
  $refStrategy: "root",
@@ -27796,7 +27795,7 @@ var Server = class extends Protocol {
27796
27795
  };
27797
27796
 
27798
27797
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
27799
- var COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
27798
+ var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
27800
27799
  function isCompletable(schema) {
27801
27800
  return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
27802
27801
  }
@@ -28791,7 +28790,13 @@ var GitLabApiClient = class {
28791
28790
  }
28792
28791
  return projectId;
28793
28792
  }
28794
- async fetch(method, path2, body) {
28793
+ /**
28794
+ * Core HTTP request used by all REST helpers.
28795
+ *
28796
+ * Returns the raw `Response` after validating the status code so callers can
28797
+ * inspect headers (e.g. for pagination) before parsing the body.
28798
+ */
28799
+ async rawFetch(method, path2, body) {
28795
28800
  const url2 = `${this.instanceUrl}/api/v4${path2}`;
28796
28801
  const response = await fetch(url2, {
28797
28802
  method,
@@ -28802,12 +28807,53 @@ var GitLabApiClient = class {
28802
28807
  const errorText = await response.text();
28803
28808
  throw new Error(`GitLab API error ${response.status}: ${errorText}`);
28804
28809
  }
28810
+ return response;
28811
+ }
28812
+ async fetch(method, path2, body) {
28813
+ const response = await this.rawFetch(method, path2, body);
28805
28814
  const text = await response.text();
28806
28815
  if (!text) {
28807
28816
  return {};
28808
28817
  }
28809
28818
  return JSON.parse(text);
28810
28819
  }
28820
+ /**
28821
+ * Parse a numeric pagination header, guarding against empty strings.
28822
+ *
28823
+ * GitLab returns `""` (rather than omitting the header) for `X-Next-Page` and
28824
+ * `X-Prev-Page` when there is no next/prev page, and bare `parseInt` without
28825
+ * a radix on an empty string yields `NaN`. Treat empty/missing as `undefined`.
28826
+ */
28827
+ parsePaginationHeader(response, name) {
28828
+ const raw = response.headers.get(name);
28829
+ if (raw == null || raw === "") return void 0;
28830
+ const value = parseInt(raw, 10);
28831
+ return Number.isNaN(value) ? void 0 : value;
28832
+ }
28833
+ /**
28834
+ * Pagination info extracted from GitLab API response headers
28835
+ */
28836
+ extractPaginationInfo(response) {
28837
+ return {
28838
+ total: this.parsePaginationHeader(response, "X-Total"),
28839
+ totalPages: this.parsePaginationHeader(response, "X-Total-Pages"),
28840
+ page: this.parsePaginationHeader(response, "X-Page"),
28841
+ perPage: this.parsePaginationHeader(response, "X-Per-Page"),
28842
+ nextPage: this.parsePaginationHeader(response, "X-Next-Page"),
28843
+ prevPage: this.parsePaginationHeader(response, "X-Prev-Page")
28844
+ };
28845
+ }
28846
+ /**
28847
+ * Fetch with pagination info from response headers.
28848
+ * Use this for list endpoints where knowing if more data exists is important.
28849
+ */
28850
+ async fetchWithPagination(method, path2, body) {
28851
+ const response = await this.rawFetch(method, path2, body);
28852
+ const text = await response.text();
28853
+ const data = text ? JSON.parse(text) : [];
28854
+ const pagination = this.extractPaginationInfo(response);
28855
+ return { data, pagination };
28856
+ }
28811
28857
  async fetchText(method, path2) {
28812
28858
  const url2 = `${this.instanceUrl}/api/v4${path2}`;
28813
28859
  const response = await fetch(url2, {
@@ -29071,6 +29117,7 @@ var MergeRequestsClient = class extends GitLabApiClient {
29071
29117
  async listMergeRequests(options) {
29072
29118
  const params = new URLSearchParams();
29073
29119
  params.set("per_page", String(options.limit || 20));
29120
+ if (options.page) params.set("page", String(options.page));
29074
29121
  if (options.state) params.set("state", options.state);
29075
29122
  if (options.scope) params.set("scope", options.scope);
29076
29123
  if (options.search) params.set("search", options.search);
@@ -29082,7 +29129,7 @@ var MergeRequestsClient = class extends GitLabApiClient {
29082
29129
  } else {
29083
29130
  path2 = `/merge_requests?${params}`;
29084
29131
  }
29085
- return this.fetch("GET", path2);
29132
+ return this.fetchWithPagination("GET", path2);
29086
29133
  }
29087
29134
  async getMrChanges(projectId, mrIid) {
29088
29135
  const encodedProject = this.encodeProjectId(projectId);
@@ -29203,6 +29250,14 @@ var MergeRequestsClient = class extends GitLabApiClient {
29203
29250
  { body }
29204
29251
  );
29205
29252
  }
29253
+ async updateMrNote(projectId, mrIid, noteId, body) {
29254
+ const encodedProject = this.encodeProjectId(projectId);
29255
+ return this.fetch(
29256
+ "PUT",
29257
+ `/projects/${encodedProject}/merge_requests/${mrIid}/notes/${noteId}`,
29258
+ { body }
29259
+ );
29260
+ }
29206
29261
  async createMergeRequest(projectId, options) {
29207
29262
  const encodedProject = this.encodeProjectId(projectId);
29208
29263
  return this.fetch(
@@ -29486,6 +29541,7 @@ var IssuesClient = class extends GitLabApiClient {
29486
29541
  async listIssues(options) {
29487
29542
  const params = new URLSearchParams();
29488
29543
  params.set("per_page", String(options.limit || 20));
29544
+ if (options.page) params.set("page", String(options.page));
29489
29545
  if (options.state) params.set("state", options.state);
29490
29546
  if (options.scope) params.set("scope", options.scope);
29491
29547
  if (options.search) params.set("search", options.search);
@@ -29498,7 +29554,7 @@ var IssuesClient = class extends GitLabApiClient {
29498
29554
  } else {
29499
29555
  path2 = `/issues?${params}`;
29500
29556
  }
29501
- return this.fetch("GET", path2);
29557
+ return this.fetchWithPagination("GET", path2);
29502
29558
  }
29503
29559
  /**
29504
29560
  * List notes on an issue using GraphQL API with pagination support
@@ -29581,6 +29637,14 @@ var IssuesClient = class extends GitLabApiClient {
29581
29637
  `/projects/${encodedProject}/issues/${issueIid}/notes/${noteId}`
29582
29638
  );
29583
29639
  }
29640
+ async updateIssueNote(projectId, issueIid, noteId, body) {
29641
+ const encodedProject = this.encodeProjectId(projectId);
29642
+ return this.fetch(
29643
+ "PUT",
29644
+ `/projects/${encodedProject}/issues/${issueIid}/notes/${noteId}`,
29645
+ { body }
29646
+ );
29647
+ }
29584
29648
  };
29585
29649
 
29586
29650
  // src/client/work-items.ts
@@ -30915,6 +30979,14 @@ var EpicsClient = class extends GitLabApiClient {
30915
30979
  `/groups/${encodedGroup}/epics/${epicIid}/notes/${noteId}`
30916
30980
  );
30917
30981
  }
30982
+ async updateEpicNote(groupId, epicIid, noteId, body) {
30983
+ const encodedGroup = encodeURIComponent(groupId);
30984
+ return this.fetch(
30985
+ "PUT",
30986
+ `/groups/${encodedGroup}/epics/${epicIid}/notes/${noteId}`,
30987
+ { body }
30988
+ );
30989
+ }
30918
30990
  };
30919
30991
 
30920
30992
  // src/client/snippets.ts
@@ -31043,6 +31115,14 @@ var SnippetsClient = class extends GitLabApiClient {
31043
31115
  { body }
31044
31116
  );
31045
31117
  }
31118
+ async updateSnippetNote(projectId, snippetId, noteId, body) {
31119
+ const encodedProject = this.encodeProjectId(projectId);
31120
+ return this.fetch(
31121
+ "PUT",
31122
+ `/projects/${encodedProject}/snippets/${snippetId}/notes/${noteId}`,
31123
+ { body }
31124
+ );
31125
+ }
31046
31126
  };
31047
31127
 
31048
31128
  // src/client/discussions.ts
@@ -31386,26 +31466,31 @@ Returns: title, description, state, author, assignees, reviewers, labels, diff s
31386
31466
  }),
31387
31467
  gitlab_list_merge_requests: tool({
31388
31468
  description: `List merge requests for a project or search globally.
31389
- Can filter by state (opened, closed, merged, all), scope (assigned_to_me, created_by_me), and labels.`,
31469
+ Can filter by state (opened, closed, merged, all), scope (assigned_to_me, created_by_me), and labels.
31470
+
31471
+ IMPORTANT: Response includes pagination info. If nextPage exists, there are more results.
31472
+ Always paginate until you get an empty response or no nextPage to ensure you have all data.`,
31390
31473
  args: {
31391
31474
  project_id: z.string().optional().describe("The project ID or path. If not provided, searches globally."),
31392
31475
  state: z.enum(["opened", "closed", "merged", "all"]).optional().describe("Filter by MR state (default: opened)"),
31393
31476
  scope: z.enum(["assigned_to_me", "created_by_me", "all"]).optional().describe("Filter by scope"),
31394
31477
  search: z.string().optional().describe("Search MRs by title or description"),
31395
31478
  labels: z.string().optional().describe("Comma-separated list of labels to filter by"),
31396
- limit: z.number().optional().describe("Maximum number of results (default: 20)")
31479
+ limit: z.number().optional().describe("Maximum number of results per page (default: 20)"),
31480
+ page: z.number().optional().describe("Page number for pagination (default: 1)")
31397
31481
  },
31398
31482
  execute: async (args, _ctx) => {
31399
31483
  const client = getGitLabClient();
31400
- const mrs = await client.listMergeRequests({
31484
+ const result = await client.listMergeRequests({
31401
31485
  projectId: args.project_id,
31402
31486
  state: args.state,
31403
31487
  scope: args.scope,
31404
31488
  search: args.search,
31405
31489
  labels: args.labels,
31406
- limit: args.limit
31490
+ limit: args.limit,
31491
+ page: args.page
31407
31492
  });
31408
- return JSON.stringify(mrs, null, 2);
31493
+ return JSON.stringify(result, null, 2);
31409
31494
  }
31410
31495
  }),
31411
31496
  gitlab_get_mr_changes: tool({
@@ -31736,7 +31821,10 @@ Returns: title, description, state, author, assignees, labels, milestone, weight
31736
31821
  }),
31737
31822
  gitlab_list_issues: tool({
31738
31823
  description: `List issues for a project or search globally.
31739
- Can filter by state, labels, assignee, milestone.`,
31824
+ Can filter by state, labels, assignee, milestone.
31825
+
31826
+ IMPORTANT: Response includes pagination info. If nextPage exists, there are more results.
31827
+ Always paginate until you get an empty response or no nextPage to ensure you have all data.`,
31740
31828
  args: {
31741
31829
  project_id: z2.string().optional().describe("The project ID or path. If not provided, searches globally."),
31742
31830
  state: z2.enum(["opened", "closed", "all"]).optional().describe("Filter by issue state (default: opened)"),
@@ -31744,20 +31832,22 @@ Can filter by state, labels, assignee, milestone.`,
31744
31832
  search: z2.string().optional().describe("Search issues by title or description"),
31745
31833
  labels: z2.string().optional().describe("Comma-separated list of labels to filter by"),
31746
31834
  milestone: z2.string().optional().describe("Filter by milestone title"),
31747
- limit: z2.number().optional().describe("Maximum number of results (default: 20)")
31835
+ limit: z2.number().optional().describe("Maximum number of results per page (default: 20)"),
31836
+ page: z2.number().optional().describe("Page number for pagination (default: 1)")
31748
31837
  },
31749
31838
  execute: async (args, _ctx) => {
31750
31839
  const client = getGitLabClient();
31751
- const issues = await client.listIssues({
31840
+ const result = await client.listIssues({
31752
31841
  projectId: args.project_id,
31753
31842
  state: args.state,
31754
31843
  scope: args.scope,
31755
31844
  search: args.search,
31756
31845
  labels: args.labels,
31757
31846
  milestone: args.milestone,
31758
- limit: args.limit
31847
+ limit: args.limit,
31848
+ page: args.page
31759
31849
  });
31760
- return JSON.stringify(issues, null, 2);
31850
+ return JSON.stringify(result, null, 2);
31761
31851
  }
31762
31852
  })
31763
31853
  };
@@ -33247,19 +33337,22 @@ function filterNotesByResolved(result, resolved) {
33247
33337
  }
33248
33338
  };
33249
33339
  }
33250
- var VALID_LIST_CREATE_TYPES = ["merge_request", "issue", "epic", "snippet"];
33251
- var VALID_GET_NOTE_TYPES = ["issue", "epic"];
33340
+ var NOTE_RESOURCE_TYPES = ["merge_request", "issue", "epic", "snippet"];
33341
+ var GET_NOTE_RESOURCE_TYPES = ["issue", "epic"];
33252
33342
  function validationError3(param, resourceType) {
33253
33343
  return new Error(
33254
33344
  `Missing required parameter: '${param}' is required for resource_type '${resourceType}'`
33255
33345
  );
33256
33346
  }
33257
- function validateListCreateParams(resourceType, args) {
33258
- if (!VALID_LIST_CREATE_TYPES.includes(resourceType)) {
33347
+ function validateNoteParams(resourceType, args, opts) {
33348
+ if (!opts.allowedTypes.includes(resourceType)) {
33259
33349
  throw new Error(
33260
- `Invalid resource_type '${resourceType}'. Must be one of: ${VALID_LIST_CREATE_TYPES.join(", ")}`
33350
+ `Invalid resource_type '${resourceType}'. Must be one of: ${opts.allowedTypes.join(", ")}`
33261
33351
  );
33262
33352
  }
33353
+ if (opts.requireNoteId && args.note_id == null) {
33354
+ throw validationError3("note_id", resourceType);
33355
+ }
33263
33356
  switch (resourceType) {
33264
33357
  case "merge_request":
33265
33358
  case "issue":
@@ -33276,24 +33369,6 @@ function validateListCreateParams(resourceType, args) {
33276
33369
  break;
33277
33370
  }
33278
33371
  }
33279
- function validateGetNoteParams(resourceType, args) {
33280
- if (!VALID_GET_NOTE_TYPES.includes(resourceType)) {
33281
- throw new Error(
33282
- `Invalid resource_type '${resourceType}'. Must be one of: ${VALID_GET_NOTE_TYPES.join(", ")}`
33283
- );
33284
- }
33285
- if (args.note_id == null) throw validationError3("note_id", resourceType);
33286
- switch (resourceType) {
33287
- case "issue":
33288
- if (!args.project_id) throw validationError3("project_id", resourceType);
33289
- if (args.iid == null) throw validationError3("iid", resourceType);
33290
- break;
33291
- case "epic":
33292
- if (!args.group_id) throw validationError3("group_id", resourceType);
33293
- if (args.iid == null) throw validationError3("iid", resourceType);
33294
- break;
33295
- }
33296
- }
33297
33372
  var notesUnifiedTools = {
33298
33373
  /**
33299
33374
  * List notes/comments for any GitLab resource type
@@ -33329,7 +33404,7 @@ Examples:
33329
33404
  before: z14.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
33330
33405
  },
33331
33406
  execute: async (args, _ctx) => {
33332
- validateListCreateParams(args.resource_type, args);
33407
+ validateNoteParams(args.resource_type, args, { allowedTypes: NOTE_RESOURCE_TYPES });
33333
33408
  const client = getGitLabClient();
33334
33409
  const paginationOptions = {
33335
33410
  first: args.first,
@@ -33383,7 +33458,10 @@ Examples:
33383
33458
  iid: z14.number().describe("Internal ID of the issue or epic")
33384
33459
  },
33385
33460
  execute: async (args, _ctx) => {
33386
- validateGetNoteParams(args.resource_type, args);
33461
+ validateNoteParams(args.resource_type, args, {
33462
+ allowedTypes: GET_NOTE_RESOURCE_TYPES,
33463
+ requireNoteId: true
33464
+ });
33387
33465
  const client = getGitLabClient();
33388
33466
  switch (args.resource_type) {
33389
33467
  case "issue":
@@ -33427,7 +33505,7 @@ Examples:
33427
33505
  snippet_id: z14.number().optional().describe("Snippet ID (required for snippet)")
33428
33506
  },
33429
33507
  execute: async (args, _ctx) => {
33430
- validateListCreateParams(args.resource_type, args);
33508
+ validateNoteParams(args.resource_type, args, { allowedTypes: NOTE_RESOURCE_TYPES });
33431
33509
  const client = getGitLabClient();
33432
33510
  switch (args.resource_type) {
33433
33511
  case "merge_request":
@@ -33458,6 +33536,68 @@ Examples:
33458
33536
  throw new Error(`Unsupported resource type: ${args.resource_type}`);
33459
33537
  }
33460
33538
  }
33539
+ }),
33540
+ /**
33541
+ * Update an existing note/comment on any GitLab resource
33542
+ */
33543
+ gitlab_update_note: tool({
33544
+ description: `Update an existing note/comment on any GitLab resource.
33545
+ Modifies the body content of an existing note. You can only update notes you authored.
33546
+
33547
+ Examples:
33548
+ - MR note: resource_type="merge_request", project_id="group/project", iid=123, note_id=456, body="Updated comment"
33549
+ - Issue note: resource_type="issue", project_id="group/project", iid=456, note_id=789, body="Updated comment"
33550
+ - Epic note: resource_type="epic", group_id="my-group", iid=1, note_id=456, body="Updated comment"
33551
+ - Snippet note: resource_type="snippet", project_id="group/project", snippet_id=789, note_id=123, body="Updated comment"`,
33552
+ args: {
33553
+ resource_type: z14.enum(["merge_request", "issue", "epic", "snippet"]).describe("Type of GitLab resource"),
33554
+ note_id: z14.number().describe("The ID of the note to update"),
33555
+ body: z14.string().describe("The new content for the note (supports Markdown)"),
33556
+ project_id: z14.string().optional().describe("Project ID or path. Required for merge_request, issue, snippet"),
33557
+ group_id: z14.string().optional().describe("Group ID or path. Required for epic"),
33558
+ iid: z14.number().optional().describe("Internal ID of the resource (for merge_request, issue, epic)"),
33559
+ snippet_id: z14.number().optional().describe("Snippet ID (required for snippet)")
33560
+ },
33561
+ execute: async (args, _ctx) => {
33562
+ validateNoteParams(args.resource_type, args, {
33563
+ allowedTypes: NOTE_RESOURCE_TYPES,
33564
+ requireNoteId: true
33565
+ });
33566
+ const client = getGitLabClient();
33567
+ switch (args.resource_type) {
33568
+ case "merge_request":
33569
+ return JSON.stringify(
33570
+ await client.updateMrNote(args.project_id, args.iid, args.note_id, args.body),
33571
+ null,
33572
+ 2
33573
+ );
33574
+ case "issue":
33575
+ return JSON.stringify(
33576
+ await client.updateIssueNote(args.project_id, args.iid, args.note_id, args.body),
33577
+ null,
33578
+ 2
33579
+ );
33580
+ case "epic":
33581
+ return JSON.stringify(
33582
+ await client.updateEpicNote(args.group_id, args.iid, args.note_id, args.body),
33583
+ null,
33584
+ 2
33585
+ );
33586
+ case "snippet":
33587
+ return JSON.stringify(
33588
+ await client.updateSnippetNote(
33589
+ args.project_id,
33590
+ args.snippet_id,
33591
+ args.note_id,
33592
+ args.body
33593
+ ),
33594
+ null,
33595
+ 2
33596
+ );
33597
+ default:
33598
+ throw new Error(`Unsupported resource type: ${args.resource_type}`);
33599
+ }
33600
+ }
33461
33601
  })
33462
33602
  };
33463
33603
 
@@ -33897,7 +34037,8 @@ function adaptToolsToMcp(server, tools) {
33897
34037
  server.tool(name, def.description, def.args, async (args) => {
33898
34038
  try {
33899
34039
  const result = await def.execute(args, {});
33900
- return { content: [{ type: "text", text: result }] };
34040
+ const text = typeof result === "string" ? result : result.output;
34041
+ return { content: [{ type: "text", text }] };
33901
34042
  } catch (err) {
33902
34043
  const message = err instanceof Error ? err.message : String(err);
33903
34044
  return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
@@ -33934,7 +34075,7 @@ async function main() {
33934
34075
  ...auditTools,
33935
34076
  ...awardEmojiTools
33936
34077
  };
33937
- const version2 = true ? "2.4.0" : "0.0.0";
34078
+ const version2 = true ? "2.6.0" : "0.0.0";
33938
34079
  const server = new McpServer({ name: "gitlab", version: version2 });
33939
34080
  adaptToolsToMcp(server, allTools);
33940
34081
  const transport = new StdioServerTransport();