opencode-gitlab-plugin 2.2.0 → 2.4.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.
- package/CHANGELOG.md +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +502 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +159 -2
- package/dist/mcp-server.cjs.map +1 -1
- package/package.json +1 -1
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.4.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.3.0...v2.4.0) (2026-05-19)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ✨ Features
|
|
9
|
+
|
|
10
|
+
* **orbit:** add GitLab Orbit Knowledge Graph API tools ([17fd452](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/17fd4521ec32f760b26a6596c5d023d7b55eed47))
|
|
11
|
+
|
|
12
|
+
## [2.3.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.2.0...v2.3.0) (2026-05-18)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### ✨ Features
|
|
16
|
+
|
|
17
|
+
* **merge-requests:** add smart merge with detailed context ([d03da5d](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/d03da5d19d3851e1d3a59a07dd3155d307a0369c))
|
|
18
|
+
|
|
5
19
|
## [2.2.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.1.0...v2.2.0) (2026-04-07)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { Plugin } from '@opencode-ai/plugin';
|
|
|
20
20
|
* - Discussions: unified tools for all resource types (list, get, create/reply, resolve)
|
|
21
21
|
* - Notes: list/get notes (flat view) for MR, issue, epic, snippet
|
|
22
22
|
* - Git: execute safe, read-only git commands in repository
|
|
23
|
+
* - Orbit: Knowledge Graph API (status, schema, query, neighbors)
|
|
23
24
|
*/
|
|
24
25
|
declare const gitlabPlugin: Plugin;
|
|
25
26
|
|
package/dist/index.js
CHANGED
|
@@ -545,6 +545,107 @@ var MergeRequestsClient = class extends GitLabApiClient {
|
|
|
545
545
|
`/projects/${encodedProject}/merge_requests/${mrIid}/unapprove`
|
|
546
546
|
);
|
|
547
547
|
}
|
|
548
|
+
/**
|
|
549
|
+
* Merge a merge request immediately
|
|
550
|
+
* Requires the MR to be in a mergeable state (approved, pipeline passed, no conflicts)
|
|
551
|
+
*/
|
|
552
|
+
async mergeMergeRequest(projectId, mrIid, options) {
|
|
553
|
+
const encodedProject = this.encodeProjectId(projectId);
|
|
554
|
+
const body = {};
|
|
555
|
+
if (options?.sha) body.sha = options.sha;
|
|
556
|
+
if (options?.squash !== void 0) body.squash = options.squash;
|
|
557
|
+
if (options?.shouldRemoveSourceBranch !== void 0) {
|
|
558
|
+
body.should_remove_source_branch = options.shouldRemoveSourceBranch;
|
|
559
|
+
}
|
|
560
|
+
return this.fetch(
|
|
561
|
+
"PUT",
|
|
562
|
+
`/projects/${encodedProject}/merge_requests/${mrIid}/merge`,
|
|
563
|
+
Object.keys(body).length > 0 ? body : void 0
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Smart merge: merge immediately if checks pass, otherwise set auto-merge
|
|
568
|
+
* Returns detailed context about what happened
|
|
569
|
+
*/
|
|
570
|
+
async smartMerge(projectId, mrIid, sha, strategy = "MERGE_WHEN_CHECKS_PASS") {
|
|
571
|
+
const mr = await this.getMergeRequest(projectId, mrIid);
|
|
572
|
+
const context = {
|
|
573
|
+
state: mr.state,
|
|
574
|
+
detailedMergeStatus: mr.detailed_merge_status,
|
|
575
|
+
pipelineStatus: mr.head_pipeline?.status || null,
|
|
576
|
+
hasConflicts: mr.has_conflicts,
|
|
577
|
+
hasAnyApproval: (mr.approved_by?.length || 0) > 0
|
|
578
|
+
};
|
|
579
|
+
if (mr.state !== "opened") {
|
|
580
|
+
return {
|
|
581
|
+
action: "failed",
|
|
582
|
+
message: `MR is not open (state: ${mr.state})`,
|
|
583
|
+
context
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
if (mr.has_conflicts) {
|
|
587
|
+
return {
|
|
588
|
+
action: "failed",
|
|
589
|
+
message: "MR has merge conflicts that must be resolved",
|
|
590
|
+
context
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
if (mr.detailed_merge_status === "mergeable") {
|
|
594
|
+
try {
|
|
595
|
+
const result = await this.mergeMergeRequest(projectId, mrIid, { sha: mr.sha });
|
|
596
|
+
return {
|
|
597
|
+
action: "merged",
|
|
598
|
+
message: "MR merged successfully",
|
|
599
|
+
mergeRequest: result,
|
|
600
|
+
context
|
|
601
|
+
};
|
|
602
|
+
} catch (error) {
|
|
603
|
+
return {
|
|
604
|
+
action: "failed",
|
|
605
|
+
message: `Direct merge failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
606
|
+
context
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const autoMergeStatuses = ["ci_still_running", "not_approved", "checking"];
|
|
611
|
+
if (autoMergeStatuses.includes(mr.detailed_merge_status)) {
|
|
612
|
+
try {
|
|
613
|
+
const result = await this.setAutoMerge(projectId, mrIid, sha, strategy);
|
|
614
|
+
const autoMergeMessages = {
|
|
615
|
+
ci_still_running: "pipeline passes",
|
|
616
|
+
not_approved: "approved",
|
|
617
|
+
checking: "checks complete"
|
|
618
|
+
};
|
|
619
|
+
return {
|
|
620
|
+
action: "auto_merge_enabled",
|
|
621
|
+
message: `Auto-merge enabled (${strategy}). MR will merge when ${autoMergeMessages[mr.detailed_merge_status]}.`,
|
|
622
|
+
mergeRequest: result,
|
|
623
|
+
context
|
|
624
|
+
};
|
|
625
|
+
} catch (error) {
|
|
626
|
+
return {
|
|
627
|
+
action: "failed",
|
|
628
|
+
message: `Failed to enable auto-merge: ${error instanceof Error ? error.message : String(error)}. MR status: ${mr.detailed_merge_status}, pipeline: ${mr.head_pipeline?.status || "none"}`,
|
|
629
|
+
context
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
const statusHints = {
|
|
634
|
+
discussions_not_resolved: "Resolve all open discussion threads before merging.",
|
|
635
|
+
draft_status: "MR is in draft. Mark as ready before merging.",
|
|
636
|
+
need_rebase: "Source branch needs to be rebased onto the target branch.",
|
|
637
|
+
requested_changes: "A reviewer requested changes that must be addressed.",
|
|
638
|
+
policies_denied: "Merge is blocked by a security/compliance policy.",
|
|
639
|
+
external_status_checks: "External status checks must pass before merging.",
|
|
640
|
+
jira_association_missing: "MR must be linked to a Jira issue.",
|
|
641
|
+
conflict: "MR has merge conflicts that must be resolved."
|
|
642
|
+
};
|
|
643
|
+
return {
|
|
644
|
+
action: "failed",
|
|
645
|
+
message: statusHints[mr.detailed_merge_status] ?? `Cannot merge: ${mr.detailed_merge_status}`,
|
|
646
|
+
context
|
|
647
|
+
};
|
|
648
|
+
}
|
|
548
649
|
};
|
|
549
650
|
|
|
550
651
|
// src/client/issues.ts
|
|
@@ -2709,7 +2810,7 @@ Note: You must provide the current HEAD SHA of the MR to prevent race conditions
|
|
|
2709
2810
|
"The HEAD SHA of the merge request. Get this from the MR details (diff_refs.head_sha or sha field)."
|
|
2710
2811
|
),
|
|
2711
2812
|
strategy: z.enum(["MERGE_WHEN_CHECKS_PASS", "ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS"]).optional().describe(
|
|
2712
|
-
"
|
|
2813
|
+
"MERGE_WHEN_CHECKS_PASS (default) waits for all checks. ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS adds to merge train."
|
|
2713
2814
|
)
|
|
2714
2815
|
},
|
|
2715
2816
|
execute: async (args, _ctx) => {
|
|
@@ -2723,6 +2824,62 @@ Note: You must provide the current HEAD SHA of the MR to prevent race conditions
|
|
|
2723
2824
|
return JSON.stringify(result, null, 2);
|
|
2724
2825
|
}
|
|
2725
2826
|
}),
|
|
2827
|
+
gitlab_smart_merge_mr: tool({
|
|
2828
|
+
description: `Smart merge: merges immediately if checks pass, otherwise enables auto-merge (MWPS).
|
|
2829
|
+
This tool automatically determines the best merge action based on MR state:
|
|
2830
|
+
- If MR is mergeable (pipeline passed, approved): merges immediately
|
|
2831
|
+
- If pipeline is running or awaiting approval: enables auto-merge
|
|
2832
|
+
- Returns detailed context about what happened and why
|
|
2833
|
+
|
|
2834
|
+
Provides actionable feedback on failures including MR state, pipeline status, and remediation hints.
|
|
2835
|
+
Note: You must provide the current HEAD SHA of the MR to prevent race conditions.`,
|
|
2836
|
+
args: {
|
|
2837
|
+
project_id: z.string().describe('The project ID or path (e.g., "gitlab-org/gitlab")'),
|
|
2838
|
+
mr_iid: z.number().describe("The internal ID of the merge request"),
|
|
2839
|
+
sha: z.string().describe(
|
|
2840
|
+
"The HEAD SHA of the merge request. Get this from the MR details (diff_refs.head_sha or sha field)."
|
|
2841
|
+
),
|
|
2842
|
+
strategy: z.enum(["MERGE_WHEN_CHECKS_PASS", "ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS"]).optional().describe(
|
|
2843
|
+
"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."
|
|
2844
|
+
)
|
|
2845
|
+
},
|
|
2846
|
+
execute: async (args, _ctx) => {
|
|
2847
|
+
const client = getGitLabClient();
|
|
2848
|
+
const result = await client.smartMerge(
|
|
2849
|
+
args.project_id,
|
|
2850
|
+
args.mr_iid,
|
|
2851
|
+
args.sha,
|
|
2852
|
+
args.strategy || "MERGE_WHEN_CHECKS_PASS"
|
|
2853
|
+
);
|
|
2854
|
+
return JSON.stringify(result, null, 2);
|
|
2855
|
+
}
|
|
2856
|
+
}),
|
|
2857
|
+
gitlab_merge_mr: tool({
|
|
2858
|
+
description: `Merge a merge request immediately.
|
|
2859
|
+
Use this when you know the MR is ready to merge (pipeline passed, approved, no conflicts).
|
|
2860
|
+
For automatic handling based on MR state, use gitlab_smart_merge_mr instead.
|
|
2861
|
+
|
|
2862
|
+
WARNING: If sha is omitted, the merge may succeed against a newer commit than what was reviewed/approved.
|
|
2863
|
+
Always provide sha when you want to ensure you're merging the exact code that was reviewed.`,
|
|
2864
|
+
args: {
|
|
2865
|
+
project_id: z.string().describe('The project ID or path (e.g., "gitlab-org/gitlab")'),
|
|
2866
|
+
mr_iid: z.number().describe("The internal ID of the merge request"),
|
|
2867
|
+
sha: z.string().optional().describe(
|
|
2868
|
+
"HEAD SHA to verify before merging. Recommended to prevent merging unreviewed code if new commits were pushed."
|
|
2869
|
+
),
|
|
2870
|
+
squash: z.boolean().optional().describe("Whether to squash commits (uses project default if not specified)"),
|
|
2871
|
+
should_remove_source_branch: z.boolean().optional().describe("Whether to delete source branch after merge")
|
|
2872
|
+
},
|
|
2873
|
+
execute: async (args, _ctx) => {
|
|
2874
|
+
const client = getGitLabClient();
|
|
2875
|
+
const result = await client.mergeMergeRequest(args.project_id, args.mr_iid, {
|
|
2876
|
+
sha: args.sha,
|
|
2877
|
+
squash: args.squash,
|
|
2878
|
+
shouldRemoveSourceBranch: args.should_remove_source_branch
|
|
2879
|
+
});
|
|
2880
|
+
return JSON.stringify(result, null, 2);
|
|
2881
|
+
}
|
|
2882
|
+
}),
|
|
2726
2883
|
gitlab_approve_merge_request: tool({
|
|
2727
2884
|
description: `Approve a merge request.
|
|
2728
2885
|
Adds the current user's approval to the merge request.
|
|
@@ -4981,6 +5138,348 @@ Examples:
|
|
|
4981
5138
|
})
|
|
4982
5139
|
};
|
|
4983
5140
|
|
|
5141
|
+
// src/tools/orbit.ts
|
|
5142
|
+
import { tool as tool18 } from "@opencode-ai/plugin";
|
|
5143
|
+
var z18 = tool18.schema;
|
|
5144
|
+
var orbitTools = {
|
|
5145
|
+
gitlab_orbit_status: tool18({
|
|
5146
|
+
description: `Check GitLab Orbit Knowledge Graph API availability and health.
|
|
5147
|
+
|
|
5148
|
+
Returns the status of the Orbit API for the connected GitLab instance.
|
|
5149
|
+
Orbit is GitLab's Knowledge Graph API that provides rich SDLC graph data.
|
|
5150
|
+
|
|
5151
|
+
Note: Orbit is only available on Premium/Ultimate tiers and requires the feature flag to be enabled.
|
|
5152
|
+
If unavailable, returns an error with suggestions for REST API alternatives.`,
|
|
5153
|
+
args: {},
|
|
5154
|
+
execute: async (_args, _ctx) => {
|
|
5155
|
+
const client = getGitLabClient();
|
|
5156
|
+
try {
|
|
5157
|
+
const status = await client.fetch("GET", "/orbit/status");
|
|
5158
|
+
return JSON.stringify(
|
|
5159
|
+
{
|
|
5160
|
+
...status,
|
|
5161
|
+
available: true,
|
|
5162
|
+
message: "Orbit Knowledge Graph API is available"
|
|
5163
|
+
},
|
|
5164
|
+
null,
|
|
5165
|
+
2
|
|
5166
|
+
);
|
|
5167
|
+
} catch (error) {
|
|
5168
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5169
|
+
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
|
|
5170
|
+
return JSON.stringify(
|
|
5171
|
+
{
|
|
5172
|
+
available: false,
|
|
5173
|
+
error: "Orbit API not available",
|
|
5174
|
+
reason: "The Orbit Knowledge Graph API is not enabled on this GitLab instance. Orbit requires GitLab Premium/Ultimate tier and the feature flag to be enabled.",
|
|
5175
|
+
alternatives: [
|
|
5176
|
+
"Use REST API endpoints for MRs, issues, pipelines",
|
|
5177
|
+
"Use GraphQL API for complex queries",
|
|
5178
|
+
"Use gitlab_search for finding related entities"
|
|
5179
|
+
]
|
|
5180
|
+
},
|
|
5181
|
+
null,
|
|
5182
|
+
2
|
|
5183
|
+
);
|
|
5184
|
+
}
|
|
5185
|
+
if (errorMessage.includes("403") || errorMessage.includes("forbidden")) {
|
|
5186
|
+
return JSON.stringify(
|
|
5187
|
+
{
|
|
5188
|
+
available: false,
|
|
5189
|
+
error: "Orbit API access forbidden",
|
|
5190
|
+
reason: "Your access token does not have permission to use the Orbit API. This may require specific scopes or Premium/Ultimate tier.",
|
|
5191
|
+
alternatives: [
|
|
5192
|
+
"Check token scopes include api or read_api",
|
|
5193
|
+
"Verify your GitLab tier supports Orbit",
|
|
5194
|
+
"Use REST/GraphQL APIs as fallback"
|
|
5195
|
+
]
|
|
5196
|
+
},
|
|
5197
|
+
null,
|
|
5198
|
+
2
|
|
5199
|
+
);
|
|
5200
|
+
}
|
|
5201
|
+
if (errorMessage.includes("502")) {
|
|
5202
|
+
return JSON.stringify(
|
|
5203
|
+
{
|
|
5204
|
+
available: false,
|
|
5205
|
+
error: "Orbit API temporarily unavailable",
|
|
5206
|
+
reason: "The Orbit API returned a 502 error. This may be temporary.",
|
|
5207
|
+
suggestion: "Try again later or use REST/GraphQL APIs as fallback."
|
|
5208
|
+
},
|
|
5209
|
+
null,
|
|
5210
|
+
2
|
|
5211
|
+
);
|
|
5212
|
+
}
|
|
5213
|
+
return JSON.stringify(
|
|
5214
|
+
{
|
|
5215
|
+
available: false,
|
|
5216
|
+
error: "Orbit API error",
|
|
5217
|
+
message: errorMessage,
|
|
5218
|
+
alternatives: ["Use REST/GraphQL APIs as fallback"]
|
|
5219
|
+
},
|
|
5220
|
+
null,
|
|
5221
|
+
2
|
|
5222
|
+
);
|
|
5223
|
+
}
|
|
5224
|
+
}
|
|
5225
|
+
}),
|
|
5226
|
+
gitlab_orbit_schema: tool18({
|
|
5227
|
+
description: `Get the Orbit Knowledge Graph schema.
|
|
5228
|
+
|
|
5229
|
+
Returns the available node types, relationship types, and their properties.
|
|
5230
|
+
Use this to understand what entities and relationships can be queried.
|
|
5231
|
+
|
|
5232
|
+
Node type domains include:
|
|
5233
|
+
- Core: Project, Group, User
|
|
5234
|
+
- Source Code: File, Definition, Commit
|
|
5235
|
+
- Code Review: MergeRequest, Diff, Review
|
|
5236
|
+
- CI/CD: Pipeline, Job, Deployment
|
|
5237
|
+
- Planning: WorkItem (issues, epics, tasks)
|
|
5238
|
+
- Security: Vulnerability
|
|
5239
|
+
|
|
5240
|
+
Note: Requires Orbit API to be available (Premium/Ultimate tier).`,
|
|
5241
|
+
args: {},
|
|
5242
|
+
execute: async (_args, _ctx) => {
|
|
5243
|
+
const client = getGitLabClient();
|
|
5244
|
+
try {
|
|
5245
|
+
const schema = await client.fetch("GET", "/orbit/schema");
|
|
5246
|
+
return JSON.stringify(schema, null, 2);
|
|
5247
|
+
} catch (error) {
|
|
5248
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5249
|
+
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
|
|
5250
|
+
return JSON.stringify(
|
|
5251
|
+
{
|
|
5252
|
+
error: "Orbit API not available",
|
|
5253
|
+
suggestion: "Use gitlab_orbit_status to check API availability"
|
|
5254
|
+
},
|
|
5255
|
+
null,
|
|
5256
|
+
2
|
|
5257
|
+
);
|
|
5258
|
+
}
|
|
5259
|
+
if (errorMessage.includes("502")) {
|
|
5260
|
+
return JSON.stringify(
|
|
5261
|
+
{
|
|
5262
|
+
error: "Orbit API temporarily unavailable",
|
|
5263
|
+
suggestion: "The Orbit API returned a 502 error. This may be temporary - try again later."
|
|
5264
|
+
},
|
|
5265
|
+
null,
|
|
5266
|
+
2
|
|
5267
|
+
);
|
|
5268
|
+
}
|
|
5269
|
+
throw error;
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
}),
|
|
5273
|
+
gitlab_orbit_query: tool18({
|
|
5274
|
+
description: `Execute a query against the Orbit Knowledge Graph.
|
|
5275
|
+
|
|
5276
|
+
Query types:
|
|
5277
|
+
- traversal: Walk the graph from a starting node
|
|
5278
|
+
- neighbors: Get immediate neighbors of a node
|
|
5279
|
+
|
|
5280
|
+
IMPORTANT: The query format uses variable names for node references.
|
|
5281
|
+
- "id" in node definition is a VARIABLE NAME (alias), not the actual entity ID
|
|
5282
|
+
- Actual IDs go in "filters" with {"field": {"op": "eq", "value": X}} format
|
|
5283
|
+
|
|
5284
|
+
Example neighbors query to get a project's connections:
|
|
5285
|
+
{
|
|
5286
|
+
"query_type": "neighbors",
|
|
5287
|
+
"node": {
|
|
5288
|
+
"entity": "Project",
|
|
5289
|
+
"id": "p",
|
|
5290
|
+
"filters": {"id": {"op": "eq", "value": 278964}}
|
|
5291
|
+
},
|
|
5292
|
+
"neighbors": {"node": "p", "direction": "incoming"},
|
|
5293
|
+
"aggregations": [],
|
|
5294
|
+
"path": {"type": "shortest", "from": "p", "to": "p", "max_depth": 1}
|
|
5295
|
+
}
|
|
5296
|
+
|
|
5297
|
+
Example traversal query:
|
|
5298
|
+
{
|
|
5299
|
+
"query_type": "traversal",
|
|
5300
|
+
"node": {
|
|
5301
|
+
"entity": "MergeRequest",
|
|
5302
|
+
"id": "mr",
|
|
5303
|
+
"filters": {"id": {"op": "eq", "value": 377844873}}
|
|
5304
|
+
},
|
|
5305
|
+
"neighbors": {"node": "mr", "direction": "both"},
|
|
5306
|
+
"aggregations": [],
|
|
5307
|
+
"path": {"type": "shortest", "from": "mr", "to": "mr", "max_depth": 1}
|
|
5308
|
+
}
|
|
5309
|
+
|
|
5310
|
+
Note: Use gitlab_get_merge_request to get the internal ID from an IID.
|
|
5311
|
+
Returns nodes array and edges array with relationship types.`,
|
|
5312
|
+
args: {
|
|
5313
|
+
// Using a JSON string rather than a structured zod schema because the Orbit query
|
|
5314
|
+
// format is still evolving. This allows flexibility as the API changes without
|
|
5315
|
+
// requiring schema updates for each iteration.
|
|
5316
|
+
query: z18.string().describe(
|
|
5317
|
+
"JSON string containing the full query object. Must include query_type, node, neighbors, aggregations, and path fields. See description for format details."
|
|
5318
|
+
),
|
|
5319
|
+
limit: z18.number().optional().describe("Maximum number of results (default: 30)")
|
|
5320
|
+
},
|
|
5321
|
+
execute: async (args, _ctx) => {
|
|
5322
|
+
const client = getGitLabClient();
|
|
5323
|
+
let queryObj;
|
|
5324
|
+
try {
|
|
5325
|
+
queryObj = JSON.parse(args.query);
|
|
5326
|
+
} catch {
|
|
5327
|
+
return JSON.stringify(
|
|
5328
|
+
{
|
|
5329
|
+
error: "Invalid JSON in query parameter",
|
|
5330
|
+
suggestion: "Ensure the query parameter is valid JSON"
|
|
5331
|
+
},
|
|
5332
|
+
null,
|
|
5333
|
+
2
|
|
5334
|
+
);
|
|
5335
|
+
}
|
|
5336
|
+
const requestBody = {
|
|
5337
|
+
query: queryObj
|
|
5338
|
+
};
|
|
5339
|
+
if (args.limit) {
|
|
5340
|
+
requestBody.limit = args.limit;
|
|
5341
|
+
}
|
|
5342
|
+
try {
|
|
5343
|
+
const result = await client.fetch("POST", "/orbit/query", requestBody);
|
|
5344
|
+
if (result.code) {
|
|
5345
|
+
return JSON.stringify(
|
|
5346
|
+
{
|
|
5347
|
+
error: "Query compilation error",
|
|
5348
|
+
code: result.code,
|
|
5349
|
+
message: result.message,
|
|
5350
|
+
suggestion: 'Check query structure. Node "id" is a variable name, actual IDs go in filters.'
|
|
5351
|
+
},
|
|
5352
|
+
null,
|
|
5353
|
+
2
|
|
5354
|
+
);
|
|
5355
|
+
}
|
|
5356
|
+
return JSON.stringify(result, null, 2);
|
|
5357
|
+
} catch (error) {
|
|
5358
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5359
|
+
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
|
|
5360
|
+
return JSON.stringify(
|
|
5361
|
+
{
|
|
5362
|
+
error: "Orbit API not available",
|
|
5363
|
+
suggestion: "Use gitlab_orbit_status to check API availability"
|
|
5364
|
+
},
|
|
5365
|
+
null,
|
|
5366
|
+
2
|
|
5367
|
+
);
|
|
5368
|
+
}
|
|
5369
|
+
if (errorMessage.includes("502")) {
|
|
5370
|
+
return JSON.stringify(
|
|
5371
|
+
{
|
|
5372
|
+
error: "Orbit query failed",
|
|
5373
|
+
message: "The Orbit API returned a 502 error. This is a known issue with some entity types (e.g., source_code domain).",
|
|
5374
|
+
suggestion: "Try a different entity type or use REST/GraphQL APIs as fallback."
|
|
5375
|
+
},
|
|
5376
|
+
null,
|
|
5377
|
+
2
|
|
5378
|
+
);
|
|
5379
|
+
}
|
|
5380
|
+
throw error;
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
}),
|
|
5384
|
+
gitlab_orbit_neighbors: tool18({
|
|
5385
|
+
description: `Get immediate neighbors of a node in the Orbit Knowledge Graph.
|
|
5386
|
+
|
|
5387
|
+
This is a simplified wrapper that builds the query for you.
|
|
5388
|
+
Returns nodes directly connected to the specified node.
|
|
5389
|
+
|
|
5390
|
+
Common use cases:
|
|
5391
|
+
- Get all connections to a project (users, groups, pipelines, MRs)
|
|
5392
|
+
- Get pipelines for an MR
|
|
5393
|
+
- Get files changed in a commit
|
|
5394
|
+
- Get deployments from a pipeline
|
|
5395
|
+
|
|
5396
|
+
Note: Use GitLab internal numeric IDs (not IIDs).
|
|
5397
|
+
Example: For MR !123 in gitlab-org/gitlab, first get the internal ID via gitlab_get_merge_request,
|
|
5398
|
+
then use that ID (e.g., 377844873).`,
|
|
5399
|
+
args: {
|
|
5400
|
+
entity_type: z18.string().describe(
|
|
5401
|
+
"Type of the entity (e.g., Project, MergeRequest, User, Pipeline, Commit, WorkItem, Group)"
|
|
5402
|
+
),
|
|
5403
|
+
entity_id: z18.number().describe(
|
|
5404
|
+
"GitLab internal numeric ID (not IID). Get this from REST/GraphQL API responses."
|
|
5405
|
+
),
|
|
5406
|
+
direction: z18.enum(["incoming", "outgoing", "both"]).optional().describe("Direction of relationships to traverse (default: both)"),
|
|
5407
|
+
limit: z18.number().optional().describe("Maximum number of neighbors to return (default: 30)")
|
|
5408
|
+
},
|
|
5409
|
+
execute: async (args, _ctx) => {
|
|
5410
|
+
const client = getGitLabClient();
|
|
5411
|
+
const direction = args.direction || "both";
|
|
5412
|
+
const queryObj = {
|
|
5413
|
+
query_type: "neighbors",
|
|
5414
|
+
node: {
|
|
5415
|
+
entity: args.entity_type,
|
|
5416
|
+
id: "n",
|
|
5417
|
+
filters: {
|
|
5418
|
+
id: { op: "eq", value: args.entity_id }
|
|
5419
|
+
}
|
|
5420
|
+
},
|
|
5421
|
+
neighbors: {
|
|
5422
|
+
node: "n",
|
|
5423
|
+
direction
|
|
5424
|
+
},
|
|
5425
|
+
aggregations: [],
|
|
5426
|
+
path: { type: "shortest", from: "n", to: "n", max_depth: 1 }
|
|
5427
|
+
};
|
|
5428
|
+
const requestBody = {
|
|
5429
|
+
query: queryObj
|
|
5430
|
+
};
|
|
5431
|
+
if (args.limit) {
|
|
5432
|
+
requestBody.limit = args.limit;
|
|
5433
|
+
}
|
|
5434
|
+
try {
|
|
5435
|
+
const result = await client.fetch("POST", "/orbit/query", requestBody);
|
|
5436
|
+
if (result.code) {
|
|
5437
|
+
return JSON.stringify(
|
|
5438
|
+
{
|
|
5439
|
+
error: "Query error",
|
|
5440
|
+
code: result.code,
|
|
5441
|
+
message: result.message,
|
|
5442
|
+
entity_type: args.entity_type,
|
|
5443
|
+
entity_id: args.entity_id
|
|
5444
|
+
},
|
|
5445
|
+
null,
|
|
5446
|
+
2
|
|
5447
|
+
);
|
|
5448
|
+
}
|
|
5449
|
+
return JSON.stringify(result, null, 2);
|
|
5450
|
+
} catch (error) {
|
|
5451
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5452
|
+
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
|
|
5453
|
+
return JSON.stringify(
|
|
5454
|
+
{
|
|
5455
|
+
error: "Orbit API not available",
|
|
5456
|
+
entity_type: args.entity_type,
|
|
5457
|
+
entity_id: args.entity_id,
|
|
5458
|
+
suggestion: "Use gitlab_orbit_status to check API availability"
|
|
5459
|
+
},
|
|
5460
|
+
null,
|
|
5461
|
+
2
|
|
5462
|
+
);
|
|
5463
|
+
}
|
|
5464
|
+
if (errorMessage.includes("502")) {
|
|
5465
|
+
return JSON.stringify(
|
|
5466
|
+
{
|
|
5467
|
+
error: "Orbit query failed",
|
|
5468
|
+
entity_type: args.entity_type,
|
|
5469
|
+
entity_id: args.entity_id,
|
|
5470
|
+
message: "The Orbit API returned a 502 error. This is a known issue with some entity types.",
|
|
5471
|
+
suggestion: "Try a different entity type or use REST/GraphQL APIs as fallback."
|
|
5472
|
+
},
|
|
5473
|
+
null,
|
|
5474
|
+
2
|
|
5475
|
+
);
|
|
5476
|
+
}
|
|
5477
|
+
throw error;
|
|
5478
|
+
}
|
|
5479
|
+
}
|
|
5480
|
+
})
|
|
5481
|
+
};
|
|
5482
|
+
|
|
4984
5483
|
// src/index.ts
|
|
4985
5484
|
var gitlabPlugin = async (_input, options) => {
|
|
4986
5485
|
if (options?.tools === false) {
|
|
@@ -5012,7 +5511,8 @@ DAP tools (agents, flows, memory, skills) are on the "gitlab-dap" server instead
|
|
|
5012
5511
|
...notesUnifiedTools,
|
|
5013
5512
|
...gitTools,
|
|
5014
5513
|
...auditTools,
|
|
5015
|
-
...awardEmojiTools
|
|
5514
|
+
...awardEmojiTools,
|
|
5515
|
+
...orbitTools
|
|
5016
5516
|
}
|
|
5017
5517
|
};
|
|
5018
5518
|
};
|