opencode-gitlab-plugin 2.8.1 → 2.8.2

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,13 @@
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.2](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.8.1...v2.8.2) (2026-09-18)
6
+
7
+
8
+ ### 🐛 Bug Fixes
9
+
10
+ * **tools:** validate generated GitLab arguments ([69fcfd5](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/69fcfd56c8086f600277da8176a9e5c09b0e8ac5))
11
+
5
12
  ## [2.8.1](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.8.0...v2.8.1) (2026-09-06)
6
13
 
7
14
 
package/README.md CHANGED
@@ -389,15 +389,15 @@ The plugin provides **64 tools** organized into the following categories:
389
389
 
390
390
  ### Repository Tools (7 tools)
391
391
 
392
- | Tool | Description |
393
- | ----------------------------- | ------------------------------------------------------ |
394
- | `gitlab_get_file` | Get file contents from any branch, tag, or commit |
395
- | `gitlab_get_commit` | Get commit details with metadata, author, and stats |
396
- | `gitlab_list_commits` | List commits with filtering by branch, path, and dates |
397
- | `gitlab_get_commit_diff` | Get diff for a specific commit |
398
- | `gitlab_list_repository_tree` | List files and directories at a given path |
399
- | `gitlab_list_branches` | List all branches in a repository |
400
- | `gitlab_get_commit_comments` | Get all commit comments in flat structure |
392
+ | Tool | Description |
393
+ | ----------------------------- | ------------------------------------------------------------- |
394
+ | `gitlab_get_file` | Get file contents from an explicit branch, tag, or commit ref |
395
+ | `gitlab_get_commit` | Get commit details with metadata, author, and stats |
396
+ | `gitlab_list_commits` | List commits with filtering by branch, path, and dates |
397
+ | `gitlab_get_commit_diff` | Get diff for a specific commit |
398
+ | `gitlab_list_repository_tree` | List files and directories at a given path |
399
+ | `gitlab_list_branches` | List all branches in a repository |
400
+ | `gitlab_get_commit_comments` | Get all commit comments in flat structure |
401
401
 
402
402
  ### Search Tools (2 tools)
403
403
 
package/dist/index.js CHANGED
@@ -129,19 +129,45 @@ var GitLabApiClient = class {
129
129
  }
130
130
  };
131
131
 
132
- // src/client/notes-types.ts
133
- function buildPaginationVariables(options) {
132
+ // src/client/connection-pagination.ts
133
+ var DEFAULT_PAGE_SIZE = 20;
134
+ var MAX_PAGE_SIZE = 100;
135
+ function normalizePageSize(value) {
136
+ if (value === void 0 || value === 0) return void 0;
137
+ if (!Number.isFinite(value)) return DEFAULT_PAGE_SIZE;
138
+ return Math.min(Math.max(Math.floor(value), 1), MAX_PAGE_SIZE);
139
+ }
140
+ function buildConnectionVariables(options) {
134
141
  const variables = {};
135
- if (options?.first !== void 0) {
136
- variables.first = options.first;
137
- } else if (options?.last == null) {
138
- variables.first = 20;
142
+ const first = normalizePageSize(options?.first);
143
+ const last = normalizePageSize(options?.last);
144
+ if (first !== void 0 && last !== void 0) {
145
+ throw new Error("Pagination accepts either 'first' or 'last', not both");
146
+ }
147
+ if (options?.after && last !== void 0) {
148
+ throw new Error("Pagination cursor 'after' cannot be combined with 'last'");
149
+ }
150
+ if (options?.before && first !== void 0) {
151
+ throw new Error("Pagination cursor 'before' cannot be combined with 'first'");
152
+ }
153
+ if (first !== void 0) {
154
+ variables.first = first;
155
+ } else if (last !== void 0) {
156
+ variables.last = last;
157
+ } else if (options?.before) {
158
+ variables.last = DEFAULT_PAGE_SIZE;
159
+ } else {
160
+ variables.first = DEFAULT_PAGE_SIZE;
139
161
  }
140
162
  if (options?.after) variables.after = options.after;
141
- if (options?.last !== void 0) variables.last = options.last;
142
163
  if (options?.before) variables.before = options.before;
143
164
  return variables;
144
165
  }
166
+
167
+ // src/client/notes-types.ts
168
+ function buildPaginationVariables(options) {
169
+ return buildConnectionVariables(options);
170
+ }
145
171
  var NOTES_FRAGMENT = `
146
172
  fragment NoteFields on Note {
147
173
  id
@@ -180,16 +206,7 @@ var NOTES_CONNECTION_FRAGMENT = `
180
206
 
181
207
  // src/client/discussions-types.ts
182
208
  function buildDiscussionsPaginationVariables(options) {
183
- const variables = {};
184
- if (options?.first !== void 0) {
185
- variables.first = options.first;
186
- } else if (options?.last == null) {
187
- variables.first = 20;
188
- }
189
- if (options?.after) variables.after = options.after;
190
- if (options?.last !== void 0) variables.last = options.last;
191
- if (options?.before) variables.before = options.before;
192
- return variables;
209
+ return buildConnectionVariables(options);
193
210
  }
194
211
  var DISCUSSION_NOTE_FRAGMENT = `
195
212
  fragment DiscussionNoteFields on Note {
@@ -1449,10 +1466,7 @@ var RepositoryClient = class extends GitLabApiClient {
1449
1466
  async getFile(projectId, filePath, ref) {
1450
1467
  const encodedProject = this.encodeProjectId(projectId);
1451
1468
  const encodedPath = encodeURIComponent(filePath);
1452
- let url = `/projects/${encodedProject}/repository/files/${encodedPath}`;
1453
- if (ref) {
1454
- url += `?ref=${encodeURIComponent(ref)}`;
1455
- }
1469
+ const url = `/projects/${encodedProject}/repository/files/${encodedPath}?ref=${encodeURIComponent(ref)}`;
1456
1470
  const file = await this.fetch("GET", url);
1457
1471
  if (file.encoding === "base64") {
1458
1472
  return Buffer.from(file.content, "base64").toString("utf-8");
@@ -3546,13 +3560,13 @@ var repositoryTools = {
3546
3560
  gitlab_get_file: tool5({
3547
3561
  description: `Get the contents of a file from a repository.
3548
3562
  Supports fetching files from any branch, tag, or commit SHA.
3549
- If ref is not specified, uses the project's default branch.
3563
+ An explicit ref is required by the GitLab repository files API.
3550
3564
  Note: Invalid refs will result in a 404 error from the GitLab API.`,
3551
3565
  args: {
3552
3566
  project_id: z5.string().describe("The project ID or URL-encoded path"),
3553
3567
  file_path: z5.string().describe("Path to the file in the repository"),
3554
- ref: z5.string().optional().describe(
3555
- `Branch name, tag, or commit SHA to fetch the file from. Supports full or short commit SHAs. If omitted, uses the project's default branch (e.g., "main" or "master").`
3568
+ ref: z5.string().trim().min(1).describe(
3569
+ "Branch name, tag, or commit SHA to fetch the file from. Supports full or short commit SHAs."
3556
3570
  )
3557
3571
  },
3558
3572
  execute: async (args, _ctx) => {
@@ -3730,6 +3744,8 @@ Scopes and their requirements:
3730
3744
  - wiki_blobs: Search wiki content (supports ref filter)
3731
3745
  - group_projects: Search projects within a group (requires group_id)
3732
3746
 
3747
+ For scopes other than milestones, state="all" is treated as omitted because it does not filter results.
3748
+
3733
3749
  Examples:
3734
3750
  - Issues: scope="issues", search="bug", project_id="my-group/my-project"
3735
3751
  - Code: scope="blobs", search="function calculateTotal"
@@ -3764,6 +3780,9 @@ Examples:
3764
3780
  state: z6.enum(["active", "closed", "all"]).optional().describe("Filter by state (for milestones scope)")
3765
3781
  },
3766
3782
  execute: async (args, _ctx) => {
3783
+ if (args.scope !== "milestones" && args.state === "all") {
3784
+ args.state = void 0;
3785
+ }
3767
3786
  validateSearchParams(args.scope, {
3768
3787
  project_id: args.project_id,
3769
3788
  group_id: args.group_id,
@@ -4472,10 +4491,10 @@ Examples:
4472
4491
  "Filter by resolved status: true for resolved, false for unresolved. Only returns resolvable discussions (excludes system notes). Client-side filtering."
4473
4492
  ),
4474
4493
  // Pagination
4475
- first: z13.number().optional().describe("Number of items to return (default: 20)"),
4494
+ first: z13.number().int().min(0).max(100).optional().describe("Number of items to return (default: 20, max: 100; 0 uses the default)"),
4476
4495
  after: z13.string().optional().describe("Cursor for pagination - use endCursor from previous response"),
4477
4496
  before: z13.string().optional().describe("Cursor for backward pagination"),
4478
- last: z13.number().optional().describe("Number of items from the end")
4497
+ last: z13.number().int().min(0).max(100).optional().describe("Number of items from the end (max: 100; 0 is omitted)")
4479
4498
  },
4480
4499
  execute: async (args, _ctx) => {
4481
4500
  validateResourceParams(args.resource_type, args);
@@ -4788,9 +4807,11 @@ Examples:
4788
4807
  "Filter by resolved status: true for resolved, false for unresolved. Only returns resolvable notes (excludes system notes). Client-side filtering."
4789
4808
  ),
4790
4809
  // Pagination
4791
- first: z14.number().optional().describe("Number of items to return from the beginning (default: 20, max: 100)"),
4810
+ first: z14.number().int().min(0).max(100).optional().describe(
4811
+ "Number of items to return from the beginning (default: 20, max: 100; 0 uses the default)"
4812
+ ),
4792
4813
  after: z14.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
4793
- last: z14.number().optional().describe("Number of items to return from the end (for backward pagination)"),
4814
+ last: z14.number().int().min(0).max(100).optional().describe("Number of items to return from the end (max: 100; 0 is omitted)"),
4794
4815
  before: z14.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
4795
4816
  },
4796
4817
  execute: async (args, _ctx) => {