opencode-gitlab-plugin 2.6.0 → 2.7.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.
@@ -29088,6 +29088,29 @@ var SET_AUTO_MERGE_MUTATION = `
29088
29088
  }
29089
29089
  }
29090
29090
  `;
29091
+ var OVERRIDE_REQUESTED_CHANGES_MUTATION = `
29092
+ mutation overrideRequestedChanges(
29093
+ $projectPath: ID!
29094
+ $iid: String!
29095
+ $override: Boolean
29096
+ ) {
29097
+ mergeRequestUpdate(
29098
+ input: {
29099
+ projectPath: $projectPath
29100
+ iid: $iid
29101
+ overrideRequestedChanges: $override
29102
+ }
29103
+ ) {
29104
+ mergeRequest {
29105
+ id
29106
+ iid
29107
+ title
29108
+ detailedMergeStatus
29109
+ }
29110
+ errors
29111
+ }
29112
+ }
29113
+ `;
29091
29114
  var RESOLVE_DISCUSSION_MUTATION = `
29092
29115
  mutation resolveDiscussion($discussionId: DiscussionID!, $resolve: Boolean!) {
29093
29116
  discussionToggleResolve(input: { id: $discussionId, resolve: $resolve }) {
@@ -29105,7 +29128,7 @@ var RESOLVE_DISCUSSION_MUTATION = `
29105
29128
  }
29106
29129
  }
29107
29130
  `;
29108
- var MergeRequestsClient = class extends GitLabApiClient {
29131
+ var MergeRequestsClient = class _MergeRequestsClient extends GitLabApiClient {
29109
29132
  async getMergeRequest(projectId, mrIid, includeChanges) {
29110
29133
  const encodedProject = this.encodeProjectId(projectId);
29111
29134
  let path2 = `/projects/${encodedProject}/merge_requests/${mrIid}`;
@@ -29327,20 +29350,83 @@ var MergeRequestsClient = class extends GitLabApiClient {
29327
29350
  * Set auto-merge (MWPS) on a merge request using GraphQL API
29328
29351
  * Uses the mergeRequestAccept mutation with a merge strategy
29329
29352
  */
29330
- async setAutoMerge(projectId, mrIid, sha, strategy = "MERGE_WHEN_CHECKS_PASS") {
29331
- const result = await this.fetchGraphQL(SET_AUTO_MERGE_MUTATION, {
29353
+ async setAutoMerge(projectId, mrIid, sha, strategy = "MERGE_WHEN_CHECKS_PASS", retries = 3) {
29354
+ let lastError = null;
29355
+ for (let attempt = 0; attempt <= retries; attempt++) {
29356
+ const result = await this.fetchGraphQL(SET_AUTO_MERGE_MUTATION, {
29357
+ projectPath: projectId,
29358
+ iid: String(mrIid),
29359
+ sha,
29360
+ strategy
29361
+ });
29362
+ if (result.mergeRequestAccept.errors.length > 0) {
29363
+ const message = result.mergeRequestAccept.errors.join(", ");
29364
+ lastError = new Error(`Failed to set auto-merge: ${message}`);
29365
+ if (attempt < retries && _MergeRequestsClient.isTransientMergeError(message)) {
29366
+ await _MergeRequestsClient.delay(750 * (attempt + 1));
29367
+ continue;
29368
+ }
29369
+ throw lastError;
29370
+ }
29371
+ if (!result.mergeRequestAccept.mergeRequest) {
29372
+ lastError = new Error("Failed to set auto-merge: No merge request returned");
29373
+ if (attempt < retries) {
29374
+ await _MergeRequestsClient.delay(750 * (attempt + 1));
29375
+ continue;
29376
+ }
29377
+ throw lastError;
29378
+ }
29379
+ return result.mergeRequestAccept.mergeRequest;
29380
+ }
29381
+ throw lastError ?? new Error("Failed to set auto-merge: exhausted retries");
29382
+ }
29383
+ /**
29384
+ * True for GitLab merge errors that are transient because mergeability is
29385
+ * still being (re)computed. Such errors clear on their own within a second
29386
+ * or two, so callers should retry rather than treat them as real failures.
29387
+ */
29388
+ static isTransientMergeError(message) {
29389
+ const m = message.toLowerCase();
29390
+ return m.includes("being checked") || m.includes("being recomputed") || m.includes("cannot be merged") || m.includes("not mergeable") || m.includes("merge failed") || m.includes("try again");
29391
+ }
29392
+ /** Small awaitable delay helper (ms). */
29393
+ static delay(ms) {
29394
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
29395
+ }
29396
+ /**
29397
+ * Override (or clear an override of) requested changes on a merge request.
29398
+ *
29399
+ * When a reviewer formally requests changes, the "Change requests must be
29400
+ * approved by the requesting user" merge check blocks the merge. Setting the
29401
+ * persistent `override_requested_changes` flag downgrades that check from a
29402
+ * blocking failure to a non-blocking warning, which is what the "Bypass"
29403
+ * button in the merge request widget does. This is distinct from
29404
+ * `mergeRequestDestroyRequestedChanges`, which only clears the calling user's
29405
+ * own requested-changes review.
29406
+ *
29407
+ * Requires permission to merge to the target branch (otherwise GitLab
29408
+ * silently ignores the flag).
29409
+ *
29410
+ * @param projectId Project ID or URL-encoded path
29411
+ * @param mrIid Internal ID of the merge request
29412
+ * @param override true to bypass requested changes, false to re-enable the block
29413
+ * @returns The updated merge request (id, iid, title, detailedMergeStatus)
29414
+ */
29415
+ async overrideRequestedChanges(projectId, mrIid, override = true) {
29416
+ const result = await this.fetchGraphQL(OVERRIDE_REQUESTED_CHANGES_MUTATION, {
29332
29417
  projectPath: projectId,
29333
29418
  iid: String(mrIid),
29334
- sha,
29335
- strategy
29419
+ override
29336
29420
  });
29337
- if (result.mergeRequestAccept.errors.length > 0) {
29338
- throw new Error(`Failed to set auto-merge: ${result.mergeRequestAccept.errors.join(", ")}`);
29421
+ if (result.mergeRequestUpdate.errors.length > 0) {
29422
+ throw new Error(
29423
+ `Failed to override requested changes: ${result.mergeRequestUpdate.errors.join(", ")}`
29424
+ );
29339
29425
  }
29340
- if (!result.mergeRequestAccept.mergeRequest) {
29341
- throw new Error("Failed to set auto-merge: No merge request returned");
29426
+ if (!result.mergeRequestUpdate.mergeRequest) {
29427
+ throw new Error("Failed to override requested changes: No merge request returned");
29342
29428
  }
29343
- return result.mergeRequestAccept.mergeRequest;
29429
+ return result.mergeRequestUpdate.mergeRequest;
29344
29430
  }
29345
29431
  /**
29346
29432
  * Approve a merge request
@@ -29388,7 +29474,13 @@ var MergeRequestsClient = class extends GitLabApiClient {
29388
29474
  * Returns detailed context about what happened
29389
29475
  */
29390
29476
  async smartMerge(projectId, mrIid, sha, strategy = "MERGE_WHEN_CHECKS_PASS") {
29391
- const mr = await this.getMergeRequest(projectId, mrIid);
29477
+ let mr = await this.getMergeRequest(projectId, mrIid);
29478
+ if (mr.detailed_merge_status === "checking") {
29479
+ for (let attempt = 0; attempt < 5 && mr.detailed_merge_status === "checking"; attempt++) {
29480
+ await _MergeRequestsClient.delay(1e3 * (attempt + 1));
29481
+ mr = await this.getMergeRequest(projectId, mrIid);
29482
+ }
29483
+ }
29392
29484
  const context = {
29393
29485
  state: mr.state,
29394
29486
  detailedMergeStatus: mr.detailed_merge_status,
@@ -29427,14 +29519,13 @@ var MergeRequestsClient = class extends GitLabApiClient {
29427
29519
  };
29428
29520
  }
29429
29521
  }
29430
- const autoMergeStatuses = ["ci_still_running", "not_approved", "checking"];
29522
+ const autoMergeStatuses = ["ci_still_running", "not_approved"];
29431
29523
  if (autoMergeStatuses.includes(mr.detailed_merge_status)) {
29432
29524
  try {
29433
29525
  const result = await this.setAutoMerge(projectId, mrIid, sha, strategy);
29434
29526
  const autoMergeMessages = {
29435
29527
  ci_still_running: "pipeline passes",
29436
- not_approved: "approved",
29437
- checking: "checks complete"
29528
+ not_approved: "approved"
29438
29529
  };
29439
29530
  return {
29440
29531
  action: "auto_merge_enabled",
@@ -29793,22 +29884,8 @@ var GET_WORK_ITEM_NOTES_QUERY = `
29793
29884
  }
29794
29885
  `;
29795
29886
  var CREATE_WORK_ITEM_MUTATION = `
29796
- mutation createWorkItem(
29797
- $projectPath: ID!
29798
- $title: String!
29799
- $workItemTypeId: WorkItemsTypeID!
29800
- $description: String
29801
- $labelIds: [LabelID!]
29802
- $assigneeIds: [UserID!]
29803
- ) {
29804
- workItemCreate(input: {
29805
- projectPath: $projectPath
29806
- title: $title
29807
- workItemTypeId: $workItemTypeId
29808
- descriptionWidget: { description: $description }
29809
- labelsWidget: { labelIds: $labelIds }
29810
- assigneesWidget: { assigneeIds: $assigneeIds }
29811
- }) {
29887
+ mutation createWorkItem($input: WorkItemCreateInput!) {
29888
+ workItemCreate(input: $input) {
29812
29889
  workItem {
29813
29890
  ${WORK_ITEM_FIELDS}
29814
29891
  }
@@ -29817,22 +29894,8 @@ var CREATE_WORK_ITEM_MUTATION = `
29817
29894
  }
29818
29895
  `;
29819
29896
  var UPDATE_WORK_ITEM_MUTATION = `
29820
- mutation updateWorkItem(
29821
- $id: WorkItemID!
29822
- $title: String
29823
- $description: String
29824
- $stateEvent: WorkItemStateEvent
29825
- $labelIds: [LabelID!]
29826
- $assigneeIds: [UserID!]
29827
- ) {
29828
- workItemUpdate(input: {
29829
- id: $id
29830
- title: $title
29831
- descriptionWidget: { description: $description }
29832
- stateEvent: $stateEvent
29833
- labelsWidget: { labelIds: $labelIds }
29834
- assigneesWidget: { assigneeIds: $assigneeIds }
29835
- }) {
29897
+ mutation updateWorkItem($input: WorkItemUpdateInput!) {
29898
+ workItemUpdate(input: $input) {
29836
29899
  workItem {
29837
29900
  ${WORK_ITEM_FIELDS}
29838
29901
  }
@@ -29926,14 +29989,21 @@ var WorkItemsClient = class extends GitLabApiClient {
29926
29989
  async createWorkItem(projectId, options) {
29927
29990
  const labelIds = options.labels?.map((l) => toGid("Label", l));
29928
29991
  const assigneeIds = options.assignee_ids?.map((id) => toGid("User", id));
29929
- const result = await this.fetchGraphQL(CREATE_WORK_ITEM_MUTATION, {
29992
+ const input = {
29930
29993
  projectPath: projectId,
29931
29994
  title: options.title,
29932
- workItemTypeId: toGid("WorkItems::Type", options.work_item_type_id),
29933
- description: options.description,
29934
- labelIds: labelIds?.length ? labelIds : void 0,
29935
- assigneeIds: assigneeIds?.length ? assigneeIds : void 0
29936
- });
29995
+ workItemTypeId: toGid("WorkItems::Type", options.work_item_type_id)
29996
+ };
29997
+ if (options.description !== void 0) {
29998
+ input.descriptionWidget = { description: options.description };
29999
+ }
30000
+ if (labelIds?.length) {
30001
+ input.labelsWidget = { labelIds };
30002
+ }
30003
+ if (assigneeIds?.length) {
30004
+ input.assigneesWidget = { assigneeIds };
30005
+ }
30006
+ const result = await this.fetchGraphQL(CREATE_WORK_ITEM_MUTATION, { input });
29937
30007
  if (result.workItemCreate.errors.length > 0) {
29938
30008
  throw new Error(`Failed to create work item: ${result.workItemCreate.errors.join(", ")}`);
29939
30009
  }
@@ -29951,14 +30021,23 @@ var WorkItemsClient = class extends GitLabApiClient {
29951
30021
  const stateEvent = options.state_event ? options.state_event === "close" ? "CLOSE" : "REOPEN" : void 0;
29952
30022
  const labelIds = options.labels?.map((l) => toGid("Label", l));
29953
30023
  const assigneeIds = options.assignee_ids?.map((id) => toGid("User", id));
29954
- const result = await this.fetchGraphQL(UPDATE_WORK_ITEM_MUTATION, {
29955
- id: gid,
29956
- title: options.title,
29957
- description: options.description,
29958
- stateEvent,
29959
- labelIds: labelIds?.length ? labelIds : void 0,
29960
- assigneeIds: assigneeIds?.length ? assigneeIds : void 0
29961
- });
30024
+ const input = { id: gid };
30025
+ if (options.title !== void 0) {
30026
+ input.title = options.title;
30027
+ }
30028
+ if (options.description !== void 0) {
30029
+ input.descriptionWidget = { description: options.description };
30030
+ }
30031
+ if (stateEvent !== void 0) {
30032
+ input.stateEvent = stateEvent;
30033
+ }
30034
+ if (labelIds?.length) {
30035
+ input.labelsWidget = { addLabelIds: labelIds };
30036
+ }
30037
+ if (assigneeIds?.length) {
30038
+ input.assigneesWidget = { assigneeIds };
30039
+ }
30040
+ const result = await this.fetchGraphQL(UPDATE_WORK_ITEM_MUTATION, { input });
29962
30041
  if (result.workItemUpdate.errors.length > 0) {
29963
30042
  throw new Error(`Failed to update work item: ${result.workItemUpdate.errors.join(", ")}`);
29964
30043
  }
@@ -31736,6 +31815,29 @@ Always provide sha when you want to ensure you're merging the exact code that wa
31736
31815
  return JSON.stringify(result, null, 2);
31737
31816
  }
31738
31817
  }),
31818
+ gitlab_override_requested_changes: tool({
31819
+ description: `Bypass (or re-enable) a "requested changes" merge block on a merge request.
31820
+
31821
+ When a reviewer formally requests changes, the merge check "Change requests must be approved by the requesting user" blocks the merge (detailed_merge_status: requested_changes). This tool sets the persistent override_requested_changes flag, which is exactly what the "Bypass" button in the MR widget does: it downgrades that check from a blocking failure to a non-blocking warning so the MR can be merged.
31822
+
31823
+ Use this when the requesting reviewer is unavailable (e.g. on PTO) and their concerns have been addressed. The acting user must have permission to merge to the target branch, otherwise GitLab silently ignores the flag.
31824
+
31825
+ Note: this is NOT the same as clearing your own requested-changes review. It overrides another user's request. Set override=false to restore the block.`,
31826
+ args: {
31827
+ project_id: z.string().describe("The project ID or URL-encoded path"),
31828
+ mr_iid: z.number().describe("The internal ID of the merge request"),
31829
+ override: z.boolean().optional().describe("true (default) to bypass requested changes, false to restore the block")
31830
+ },
31831
+ execute: async (args, _ctx) => {
31832
+ const client = getGitLabClient();
31833
+ const result = await client.overrideRequestedChanges(
31834
+ args.project_id,
31835
+ args.mr_iid,
31836
+ args.override ?? true
31837
+ );
31838
+ return JSON.stringify(result, null, 2);
31839
+ }
31840
+ }),
31739
31841
  gitlab_approve_merge_request: tool({
31740
31842
  description: `Approve a merge request.
31741
31843
  Adds the current user's approval to the merge request.
@@ -34075,7 +34177,7 @@ async function main() {
34075
34177
  ...auditTools,
34076
34178
  ...awardEmojiTools
34077
34179
  };
34078
- const version2 = true ? "2.6.0" : "0.0.0";
34180
+ const version2 = true ? "2.7.0" : "0.0.0";
34079
34181
  const server = new McpServer({ name: "gitlab", version: version2 });
34080
34182
  adaptToolsToMcp(server, allTools);
34081
34183
  const transport = new StdioServerTransport();