opencode-gitlab-plugin 2.5.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.
- package/CHANGELOG.md +12 -0
- package/dist/index.js +72 -13
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +73 -14
- package/dist/mcp-server.cjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
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.6.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.5.0...v2.6.0) (2026-05-20)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ✨ Features
|
|
9
|
+
|
|
10
|
+
* **pagination:** expose pagination info in list MR and issue responses ([1c1722e](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/1c1722e62e0e41f00ab5409057bea1e76ec17a8d))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ♻️ Code Refactoring
|
|
14
|
+
|
|
15
|
+
* **client:** address review feedback on !23 ([2e94e46](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/2e94e46706807bc8797f10884f71fc56ecf967b9)), closes [23#note_3135645102](https://gitlab.com/vglafirov/23/issues/note_3135645102) [23#note_3135645423](https://gitlab.com/vglafirov/23/issues/note_3135645423) [23#note_3135644923](https://gitlab.com/vglafirov/23/issues/note_3135644923)
|
|
16
|
+
|
|
5
17
|
## [2.5.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.4.0...v2.5.0) (2026-05-20)
|
|
6
18
|
|
|
7
19
|
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,13 @@ var GitLabApiClient = class {
|
|
|
26
26
|
}
|
|
27
27
|
return projectId;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Core HTTP request used by all REST helpers.
|
|
31
|
+
*
|
|
32
|
+
* Returns the raw `Response` after validating the status code so callers can
|
|
33
|
+
* inspect headers (e.g. for pagination) before parsing the body.
|
|
34
|
+
*/
|
|
35
|
+
async rawFetch(method, path2, body) {
|
|
30
36
|
const url = `${this.instanceUrl}/api/v4${path2}`;
|
|
31
37
|
const response = await fetch(url, {
|
|
32
38
|
method,
|
|
@@ -37,12 +43,53 @@ var GitLabApiClient = class {
|
|
|
37
43
|
const errorText = await response.text();
|
|
38
44
|
throw new Error(`GitLab API error ${response.status}: ${errorText}`);
|
|
39
45
|
}
|
|
46
|
+
return response;
|
|
47
|
+
}
|
|
48
|
+
async fetch(method, path2, body) {
|
|
49
|
+
const response = await this.rawFetch(method, path2, body);
|
|
40
50
|
const text = await response.text();
|
|
41
51
|
if (!text) {
|
|
42
52
|
return {};
|
|
43
53
|
}
|
|
44
54
|
return JSON.parse(text);
|
|
45
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse a numeric pagination header, guarding against empty strings.
|
|
58
|
+
*
|
|
59
|
+
* GitLab returns `""` (rather than omitting the header) for `X-Next-Page` and
|
|
60
|
+
* `X-Prev-Page` when there is no next/prev page, and bare `parseInt` without
|
|
61
|
+
* a radix on an empty string yields `NaN`. Treat empty/missing as `undefined`.
|
|
62
|
+
*/
|
|
63
|
+
parsePaginationHeader(response, name) {
|
|
64
|
+
const raw = response.headers.get(name);
|
|
65
|
+
if (raw == null || raw === "") return void 0;
|
|
66
|
+
const value = parseInt(raw, 10);
|
|
67
|
+
return Number.isNaN(value) ? void 0 : value;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Pagination info extracted from GitLab API response headers
|
|
71
|
+
*/
|
|
72
|
+
extractPaginationInfo(response) {
|
|
73
|
+
return {
|
|
74
|
+
total: this.parsePaginationHeader(response, "X-Total"),
|
|
75
|
+
totalPages: this.parsePaginationHeader(response, "X-Total-Pages"),
|
|
76
|
+
page: this.parsePaginationHeader(response, "X-Page"),
|
|
77
|
+
perPage: this.parsePaginationHeader(response, "X-Per-Page"),
|
|
78
|
+
nextPage: this.parsePaginationHeader(response, "X-Next-Page"),
|
|
79
|
+
prevPage: this.parsePaginationHeader(response, "X-Prev-Page")
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Fetch with pagination info from response headers.
|
|
84
|
+
* Use this for list endpoints where knowing if more data exists is important.
|
|
85
|
+
*/
|
|
86
|
+
async fetchWithPagination(method, path2, body) {
|
|
87
|
+
const response = await this.rawFetch(method, path2, body);
|
|
88
|
+
const text = await response.text();
|
|
89
|
+
const data = text ? JSON.parse(text) : [];
|
|
90
|
+
const pagination = this.extractPaginationInfo(response);
|
|
91
|
+
return { data, pagination };
|
|
92
|
+
}
|
|
46
93
|
async fetchText(method, path2) {
|
|
47
94
|
const url = `${this.instanceUrl}/api/v4${path2}`;
|
|
48
95
|
const response = await fetch(url, {
|
|
@@ -306,6 +353,7 @@ var MergeRequestsClient = class extends GitLabApiClient {
|
|
|
306
353
|
async listMergeRequests(options) {
|
|
307
354
|
const params = new URLSearchParams();
|
|
308
355
|
params.set("per_page", String(options.limit || 20));
|
|
356
|
+
if (options.page) params.set("page", String(options.page));
|
|
309
357
|
if (options.state) params.set("state", options.state);
|
|
310
358
|
if (options.scope) params.set("scope", options.scope);
|
|
311
359
|
if (options.search) params.set("search", options.search);
|
|
@@ -317,7 +365,7 @@ var MergeRequestsClient = class extends GitLabApiClient {
|
|
|
317
365
|
} else {
|
|
318
366
|
path2 = `/merge_requests?${params}`;
|
|
319
367
|
}
|
|
320
|
-
return this.
|
|
368
|
+
return this.fetchWithPagination("GET", path2);
|
|
321
369
|
}
|
|
322
370
|
async getMrChanges(projectId, mrIid) {
|
|
323
371
|
const encodedProject = this.encodeProjectId(projectId);
|
|
@@ -729,6 +777,7 @@ var IssuesClient = class extends GitLabApiClient {
|
|
|
729
777
|
async listIssues(options) {
|
|
730
778
|
const params = new URLSearchParams();
|
|
731
779
|
params.set("per_page", String(options.limit || 20));
|
|
780
|
+
if (options.page) params.set("page", String(options.page));
|
|
732
781
|
if (options.state) params.set("state", options.state);
|
|
733
782
|
if (options.scope) params.set("scope", options.scope);
|
|
734
783
|
if (options.search) params.set("search", options.search);
|
|
@@ -741,7 +790,7 @@ var IssuesClient = class extends GitLabApiClient {
|
|
|
741
790
|
} else {
|
|
742
791
|
path2 = `/issues?${params}`;
|
|
743
792
|
}
|
|
744
|
-
return this.
|
|
793
|
+
return this.fetchWithPagination("GET", path2);
|
|
745
794
|
}
|
|
746
795
|
/**
|
|
747
796
|
* List notes on an issue using GraphQL API with pagination support
|
|
@@ -2647,26 +2696,31 @@ Returns: title, description, state, author, assignees, reviewers, labels, diff s
|
|
|
2647
2696
|
}),
|
|
2648
2697
|
gitlab_list_merge_requests: tool({
|
|
2649
2698
|
description: `List merge requests for a project or search globally.
|
|
2650
|
-
Can filter by state (opened, closed, merged, all), scope (assigned_to_me, created_by_me), and labels
|
|
2699
|
+
Can filter by state (opened, closed, merged, all), scope (assigned_to_me, created_by_me), and labels.
|
|
2700
|
+
|
|
2701
|
+
IMPORTANT: Response includes pagination info. If nextPage exists, there are more results.
|
|
2702
|
+
Always paginate until you get an empty response or no nextPage to ensure you have all data.`,
|
|
2651
2703
|
args: {
|
|
2652
2704
|
project_id: z.string().optional().describe("The project ID or path. If not provided, searches globally."),
|
|
2653
2705
|
state: z.enum(["opened", "closed", "merged", "all"]).optional().describe("Filter by MR state (default: opened)"),
|
|
2654
2706
|
scope: z.enum(["assigned_to_me", "created_by_me", "all"]).optional().describe("Filter by scope"),
|
|
2655
2707
|
search: z.string().optional().describe("Search MRs by title or description"),
|
|
2656
2708
|
labels: z.string().optional().describe("Comma-separated list of labels to filter by"),
|
|
2657
|
-
limit: z.number().optional().describe("Maximum number of results (default: 20)")
|
|
2709
|
+
limit: z.number().optional().describe("Maximum number of results per page (default: 20)"),
|
|
2710
|
+
page: z.number().optional().describe("Page number for pagination (default: 1)")
|
|
2658
2711
|
},
|
|
2659
2712
|
execute: async (args, _ctx) => {
|
|
2660
2713
|
const client = getGitLabClient();
|
|
2661
|
-
const
|
|
2714
|
+
const result = await client.listMergeRequests({
|
|
2662
2715
|
projectId: args.project_id,
|
|
2663
2716
|
state: args.state,
|
|
2664
2717
|
scope: args.scope,
|
|
2665
2718
|
search: args.search,
|
|
2666
2719
|
labels: args.labels,
|
|
2667
|
-
limit: args.limit
|
|
2720
|
+
limit: args.limit,
|
|
2721
|
+
page: args.page
|
|
2668
2722
|
});
|
|
2669
|
-
return JSON.stringify(
|
|
2723
|
+
return JSON.stringify(result, null, 2);
|
|
2670
2724
|
}
|
|
2671
2725
|
}),
|
|
2672
2726
|
gitlab_get_mr_changes: tool({
|
|
@@ -2998,7 +3052,10 @@ Returns: title, description, state, author, assignees, labels, milestone, weight
|
|
|
2998
3052
|
}),
|
|
2999
3053
|
gitlab_list_issues: tool2({
|
|
3000
3054
|
description: `List issues for a project or search globally.
|
|
3001
|
-
Can filter by state, labels, assignee, milestone
|
|
3055
|
+
Can filter by state, labels, assignee, milestone.
|
|
3056
|
+
|
|
3057
|
+
IMPORTANT: Response includes pagination info. If nextPage exists, there are more results.
|
|
3058
|
+
Always paginate until you get an empty response or no nextPage to ensure you have all data.`,
|
|
3002
3059
|
args: {
|
|
3003
3060
|
project_id: z2.string().optional().describe("The project ID or path. If not provided, searches globally."),
|
|
3004
3061
|
state: z2.enum(["opened", "closed", "all"]).optional().describe("Filter by issue state (default: opened)"),
|
|
@@ -3006,20 +3063,22 @@ Can filter by state, labels, assignee, milestone.`,
|
|
|
3006
3063
|
search: z2.string().optional().describe("Search issues by title or description"),
|
|
3007
3064
|
labels: z2.string().optional().describe("Comma-separated list of labels to filter by"),
|
|
3008
3065
|
milestone: z2.string().optional().describe("Filter by milestone title"),
|
|
3009
|
-
limit: z2.number().optional().describe("Maximum number of results (default: 20)")
|
|
3066
|
+
limit: z2.number().optional().describe("Maximum number of results per page (default: 20)"),
|
|
3067
|
+
page: z2.number().optional().describe("Page number for pagination (default: 1)")
|
|
3010
3068
|
},
|
|
3011
3069
|
execute: async (args, _ctx) => {
|
|
3012
3070
|
const client = getGitLabClient();
|
|
3013
|
-
const
|
|
3071
|
+
const result = await client.listIssues({
|
|
3014
3072
|
projectId: args.project_id,
|
|
3015
3073
|
state: args.state,
|
|
3016
3074
|
scope: args.scope,
|
|
3017
3075
|
search: args.search,
|
|
3018
3076
|
labels: args.labels,
|
|
3019
3077
|
milestone: args.milestone,
|
|
3020
|
-
limit: args.limit
|
|
3078
|
+
limit: args.limit,
|
|
3079
|
+
page: args.page
|
|
3021
3080
|
});
|
|
3022
|
-
return JSON.stringify(
|
|
3081
|
+
return JSON.stringify(result, null, 2);
|
|
3023
3082
|
}
|
|
3024
3083
|
})
|
|
3025
3084
|
};
|