opencode-gitlab-plugin 2.7.0 → 2.8.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
4
4
 
5
+ ## [2.8.1](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.8.0...v2.8.1) (2026-09-06)
6
+
7
+
8
+ ### 🐛 Bug Fixes
9
+
10
+ * **merge-requests:** auto-detect merge train strategy in smart merge ([14fb879](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/14fb879b167ed7ada03b02ef83b0944fbfc00480))
11
+
12
+ ## [2.8.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.7.0...v2.8.0) (2026-09-06)
13
+
14
+
15
+ ### ✨ Features
16
+
17
+ * **merge-requests:** support target_project_id for cross-project MRs ([e03a3b4](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/e03a3b436e2ad7ef3540fee1ee380a6ef5b03b09))
18
+
5
19
  ## [2.7.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.6.1...v2.7.0) (2026-08-17)
6
20
 
7
21
 
package/README.md CHANGED
@@ -343,13 +343,13 @@ Or for API tokens:
343
343
 
344
344
  The plugin provides **64 tools** organized into the following categories:
345
345
 
346
- ### Merge Request Tools (8 tools)
346
+ ### Merge Request Tools (9 tools)
347
347
 
348
348
  | Tool | Description |
349
349
  | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
350
350
  | `gitlab_get_merge_request` | Get details of a specific merge request with title, description, state, author, assignees, reviewers, labels, and diff stats |
351
351
  | `gitlab_list_merge_requests` | List merge requests with filtering by state, scope, and labels |
352
- | `gitlab_create_merge_request` | Create a new merge request |
352
+ | `gitlab_create_merge_request` | Create a new merge request (supports target_project_id for fork-to-upstream MRs) |
353
353
  | `gitlab_update_merge_request` | Update merge request title, description, state, assignees, reviewers, and labels |
354
354
  | `gitlab_get_mr_changes` | Get file changes/diffs for a merge request |
355
355
  | `gitlab_get_mr_details` | Get additional MR details (commits or pipelines) with detail_type parameter |
package/dist/index.js CHANGED
@@ -705,11 +705,48 @@ var MergeRequestsClient = class _MergeRequestsClient extends GitLabApiClient {
705
705
  Object.keys(body).length > 0 ? body : void 0
706
706
  );
707
707
  }
708
+ /**
709
+ * Resolve the auto-merge strategy for a project.
710
+ *
711
+ * When the caller does not specify a strategy, projects with merge trains
712
+ * enabled require ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS; otherwise
713
+ * MERGE_WHEN_CHECKS_PASS is correct. Using the wrong one causes auto-merge to
714
+ * fail with an opaque "The merge failed" error. Falls back to
715
+ * MERGE_WHEN_CHECKS_PASS if the project setting cannot be read.
716
+ *
717
+ * Returns both the chosen strategy and how it was arrived at, so the caller
718
+ * can thread a breadcrumb into its result. A bare fallback would collapse a
719
+ * 401, a network timeout, and a genuine 404 all into a silent MWPS default,
720
+ * reintroducing the opaque "The merge failed" this behaviour exists to
721
+ * eliminate; `detection` makes that observable without changing the fallback.
722
+ */
723
+ async resolveAutoMergeStrategy(projectId) {
724
+ try {
725
+ const encodedProject = this.encodeProjectId(projectId);
726
+ const project = await this.fetch(
727
+ "GET",
728
+ `/projects/${encodedProject}`
729
+ );
730
+ return project.merge_trains_enabled ? {
731
+ strategy: "ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS",
732
+ detection: "merge_trains_enabled"
733
+ } : { strategy: "MERGE_WHEN_CHECKS_PASS", detection: "merge_trains_disabled" };
734
+ } catch (error) {
735
+ return {
736
+ strategy: "MERGE_WHEN_CHECKS_PASS",
737
+ detection: "defaulted_after_error",
738
+ reason: error instanceof Error ? error.message : String(error)
739
+ };
740
+ }
741
+ }
708
742
  /**
709
743
  * Smart merge: merge immediately if checks pass, otherwise set auto-merge
710
- * Returns detailed context about what happened
744
+ * Returns detailed context about what happened.
745
+ *
746
+ * If `strategy` is omitted, it is auto-detected from the project's merge
747
+ * train settings (merge-train projects need ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS).
711
748
  */
712
- async smartMerge(projectId, mrIid, sha, strategy = "MERGE_WHEN_CHECKS_PASS") {
749
+ async smartMerge(projectId, mrIid, sha, strategy) {
713
750
  let mr = await this.getMergeRequest(projectId, mrIid);
714
751
  if (mr.detailed_merge_status === "checking") {
715
752
  for (let attempt = 0; attempt < 5 && mr.detailed_merge_status === "checking"; attempt++) {
@@ -757,15 +794,24 @@ var MergeRequestsClient = class _MergeRequestsClient extends GitLabApiClient {
757
794
  }
758
795
  const autoMergeStatuses = ["ci_still_running", "not_approved"];
759
796
  if (autoMergeStatuses.includes(mr.detailed_merge_status)) {
797
+ let resolvedStrategy;
798
+ if (strategy) {
799
+ resolvedStrategy = strategy;
800
+ context.strategyDetection = `explicit: ${strategy}`;
801
+ } else {
802
+ const detected = await this.resolveAutoMergeStrategy(projectId);
803
+ resolvedStrategy = detected.strategy;
804
+ context.strategyDetection = detected.detection === "defaulted_after_error" ? `failed, defaulted to MERGE_WHEN_CHECKS_PASS${detected.reason ? ` (${detected.reason})` : ""}` : `auto-detected (${detected.detection}): ${detected.strategy}`;
805
+ }
760
806
  try {
761
- const result = await this.setAutoMerge(projectId, mrIid, sha, strategy);
807
+ const result = await this.setAutoMerge(projectId, mrIid, sha, resolvedStrategy);
762
808
  const autoMergeMessages = {
763
809
  ci_still_running: "pipeline passes",
764
810
  not_approved: "approved"
765
811
  };
766
812
  return {
767
813
  action: "auto_merge_enabled",
768
- message: `Auto-merge enabled (${strategy}). MR will merge when ${autoMergeMessages[mr.detailed_merge_status]}.`,
814
+ message: `Auto-merge enabled (${resolvedStrategy}). MR will merge when ${autoMergeMessages[mr.detailed_merge_status]}.`,
769
815
  mergeRequest: result,
770
816
  context
771
817
  };
@@ -2830,7 +2876,10 @@ Returns the created merge request with all details.`,
2830
2876
  milestone_id: z.number().optional().describe("The ID of a milestone"),
2831
2877
  remove_source_branch: z.boolean().optional().describe("Remove source branch after merge (default: false)"),
2832
2878
  squash: z.boolean().optional().describe("Squash commits on merge (default: false)"),
2833
- allow_collaboration: z.boolean().optional().describe("Allow commits from members who can merge to the target branch")
2879
+ allow_collaboration: z.boolean().optional().describe("Allow commits from members who can merge to the target branch"),
2880
+ target_project_id: z.number().optional().describe(
2881
+ "The ID of the target project for cross-project (fork to upstream) merge requests. Defaults to the source project when omitted."
2882
+ )
2834
2883
  },
2835
2884
  execute: async (args, _ctx) => {
2836
2885
  const client = getGitLabClient();
@@ -2845,7 +2894,8 @@ Returns the created merge request with all details.`,
2845
2894
  milestone_id: args.milestone_id,
2846
2895
  remove_source_branch: args.remove_source_branch,
2847
2896
  squash: args.squash,
2848
- allow_collaboration: args.allow_collaboration
2897
+ allow_collaboration: args.allow_collaboration,
2898
+ target_project_id: args.target_project_id
2849
2899
  });
2850
2900
  return JSON.stringify(mr, null, 2);
2851
2901
  }
@@ -3005,17 +3055,12 @@ Note: You must provide the current HEAD SHA of the MR to prevent race conditions
3005
3055
  "The HEAD SHA of the merge request. Get this from the MR details (diff_refs.head_sha or sha field)."
3006
3056
  ),
3007
3057
  strategy: z.enum(["MERGE_WHEN_CHECKS_PASS", "ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS"]).optional().describe(
3008
- "Auto-merge strategy when immediate merge is not possible. MERGE_WHEN_CHECKS_PASS (default) waits for all checks. ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS adds to merge train."
3058
+ "Auto-merge strategy when immediate merge is not possible. If omitted, it is auto-detected from the project: merge-train projects use ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS, otherwise MERGE_WHEN_CHECKS_PASS."
3009
3059
  )
3010
3060
  },
3011
3061
  execute: async (args, _ctx) => {
3012
3062
  const client = getGitLabClient();
3013
- const result = await client.smartMerge(
3014
- args.project_id,
3015
- args.mr_iid,
3016
- args.sha,
3017
- args.strategy || "MERGE_WHEN_CHECKS_PASS"
3018
- );
3063
+ const result = await client.smartMerge(args.project_id, args.mr_iid, args.sha, args.strategy);
3019
3064
  return JSON.stringify(result, null, 2);
3020
3065
  }
3021
3066
  }),