opencode-gitlab-plugin 3.1.0 → 3.2.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 +7 -0
- package/README.md +14 -5
- package/dist/index.js +80 -10
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +81 -11
- package/dist/mcp-server.cjs.map +1 -1
- package/package.json +1 -1
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
|
+
## [3.2.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v3.1.0...v3.2.0) (2026-10-25)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ✨ Features
|
|
9
|
+
|
|
10
|
+
* **notes:** support confidential/internal notes in gitlab_create_note ([90cedca](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/90cedcac2ab128a2f2377a2951776e47a0d395ba))
|
|
11
|
+
|
|
5
12
|
## [3.1.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v3.0.1...v3.1.0) (2026-10-13)
|
|
6
13
|
|
|
7
14
|
|
package/README.md
CHANGED
|
@@ -477,11 +477,11 @@ The plugin provides **64 tools** organized into the following categories:
|
|
|
477
477
|
|
|
478
478
|
### Notes Tools (3 tools)
|
|
479
479
|
|
|
480
|
-
| Tool | Description
|
|
481
|
-
| -------------------- |
|
|
482
|
-
| `gitlab_list_notes` | List all notes/comments on any resource (MRs, issues, epics, snippets) with cursor-based pagination
|
|
483
|
-
| `gitlab_get_note` | Get a single note by ID (issues and epics only)
|
|
484
|
-
| `gitlab_create_note` | Add a simple comment to any resource (for thread replies, use `gitlab_create_discussion`)
|
|
480
|
+
| Tool | Description |
|
|
481
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
482
|
+
| `gitlab_list_notes` | List all notes/comments on any resource (MRs, issues, epics, snippets) with cursor-based pagination |
|
|
483
|
+
| `gitlab_get_note` | Get a single note by ID (issues and epics only) |
|
|
484
|
+
| `gitlab_create_note` | Add a simple comment to any resource (for thread replies, use `gitlab_create_discussion`). Pass `internal: true` to post a confidential/internal note on merge requests, issues, and epics |
|
|
485
485
|
|
|
486
486
|
### Audit Event Tools (3 tools)
|
|
487
487
|
|
|
@@ -534,6 +534,15 @@ await plugin.tool.gitlab_create_note.execute({
|
|
|
534
534
|
body: 'I will start working on this today.',
|
|
535
535
|
});
|
|
536
536
|
|
|
537
|
+
// Add a confidential/internal note (visible only to project members; merge requests, issues, and epics)
|
|
538
|
+
await plugin.tool.gitlab_create_note.execute({
|
|
539
|
+
resource_type: 'issue',
|
|
540
|
+
project_id: 'my-group/my-project',
|
|
541
|
+
iid: issue.iid,
|
|
542
|
+
body: 'Internal triage note - not visible to the reporter.',
|
|
543
|
+
internal: true,
|
|
544
|
+
});
|
|
545
|
+
|
|
537
546
|
// List all issues with specific labels
|
|
538
547
|
const issues = await plugin.tool.gitlab_list_issues.execute({
|
|
539
548
|
project_id: 'my-group/my-project',
|
package/dist/index.js
CHANGED
|
@@ -511,9 +511,22 @@ var MergeRequestsClient = class _MergeRequestsClient extends GitLabApiClient {
|
|
|
511
511
|
requestBody
|
|
512
512
|
);
|
|
513
513
|
}
|
|
514
|
-
|
|
514
|
+
/**
|
|
515
|
+
* Create a note on a merge request.
|
|
516
|
+
*
|
|
517
|
+
* When `discussionId` is set the note is a reply on that discussion thread, which the
|
|
518
|
+
* GitLab Notes API does not allow to be internal. Passing `internal=true` together with a
|
|
519
|
+
* `discussionId` therefore throws (fail-closed) rather than silently dropping the
|
|
520
|
+
* confidentiality flag. Post a standalone internal note (omit `discussionId`) instead.
|
|
521
|
+
*/
|
|
522
|
+
async createMrNote(projectId, mrIid, body, discussionId, internal) {
|
|
515
523
|
const encodedProject = this.encodeProjectId(projectId);
|
|
516
524
|
if (discussionId) {
|
|
525
|
+
if (internal) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
"internal=true is not supported when replying to a discussion. The GitLab Notes API does not accept an internal flag on discussion replies, so honouring it silently would drop a confidentiality flag. Post a standalone internal note (omit discussionId) instead."
|
|
528
|
+
);
|
|
529
|
+
}
|
|
517
530
|
return this.fetch(
|
|
518
531
|
"POST",
|
|
519
532
|
`/projects/${encodedProject}/merge_requests/${mrIid}/discussions/${discussionId}/notes`,
|
|
@@ -523,7 +536,7 @@ var MergeRequestsClient = class _MergeRequestsClient extends GitLabApiClient {
|
|
|
523
536
|
return this.fetch(
|
|
524
537
|
"POST",
|
|
525
538
|
`/projects/${encodedProject}/merge_requests/${mrIid}/notes`,
|
|
526
|
-
{ body }
|
|
539
|
+
internal ? { body, internal: true } : { body }
|
|
527
540
|
);
|
|
528
541
|
}
|
|
529
542
|
async updateMrNote(projectId, mrIid, noteId, body) {
|
|
@@ -989,9 +1002,22 @@ var IssuesClient = class extends GitLabApiClient {
|
|
|
989
1002
|
`/projects/${encodedProject}/issues/${issueIid}/discussions/${discussionId}`
|
|
990
1003
|
);
|
|
991
1004
|
}
|
|
992
|
-
|
|
1005
|
+
/**
|
|
1006
|
+
* Create a note on an issue.
|
|
1007
|
+
*
|
|
1008
|
+
* When `discussionId` is set the note is a reply on that discussion thread, which the
|
|
1009
|
+
* GitLab Notes API does not allow to be internal. Passing `internal=true` together with a
|
|
1010
|
+
* `discussionId` therefore throws (fail-closed) rather than silently dropping the
|
|
1011
|
+
* confidentiality flag. Post a standalone internal note (omit `discussionId`) instead.
|
|
1012
|
+
*/
|
|
1013
|
+
async createIssueNote(projectId, issueIid, body, discussionId, internal) {
|
|
993
1014
|
const encodedProject = this.encodeProjectId(projectId);
|
|
994
1015
|
if (discussionId) {
|
|
1016
|
+
if (internal) {
|
|
1017
|
+
throw new Error(
|
|
1018
|
+
"internal=true is not supported when replying to a discussion. The GitLab Notes API does not accept an internal flag on discussion replies, so honouring it silently would drop a confidentiality flag. Post a standalone internal note (omit discussionId) instead."
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
995
1021
|
return this.fetch(
|
|
996
1022
|
"POST",
|
|
997
1023
|
`/projects/${encodedProject}/issues/${issueIid}/discussions/${discussionId}/notes`,
|
|
@@ -1001,7 +1027,7 @@ var IssuesClient = class extends GitLabApiClient {
|
|
|
1001
1027
|
return this.fetch(
|
|
1002
1028
|
"POST",
|
|
1003
1029
|
`/projects/${encodedProject}/issues/${issueIid}/notes`,
|
|
1004
|
-
{ body }
|
|
1030
|
+
internal ? { body, internal: true } : { body }
|
|
1005
1031
|
);
|
|
1006
1032
|
}
|
|
1007
1033
|
async resolveIssueDiscussion(projectId, issueIid, discussionId) {
|
|
@@ -2332,9 +2358,22 @@ var EpicsClient = class extends GitLabApiClient {
|
|
|
2332
2358
|
`/groups/${encodedGroup}/epics/${epicIid}/discussions/${discussionId}`
|
|
2333
2359
|
);
|
|
2334
2360
|
}
|
|
2335
|
-
|
|
2361
|
+
/**
|
|
2362
|
+
* Create a note on an epic.
|
|
2363
|
+
*
|
|
2364
|
+
* When `discussionId` is set the note is a reply on that discussion thread, which the
|
|
2365
|
+
* GitLab Notes API does not allow to be internal. Passing `internal=true` together with a
|
|
2366
|
+
* `discussionId` therefore throws (fail-closed) rather than silently dropping the
|
|
2367
|
+
* confidentiality flag. Post a standalone internal note (omit `discussionId`) instead.
|
|
2368
|
+
*/
|
|
2369
|
+
async createEpicNote(groupId, epicIid, body, discussionId, internal) {
|
|
2336
2370
|
const encodedGroup = encodeURIComponent(groupId);
|
|
2337
2371
|
if (discussionId) {
|
|
2372
|
+
if (internal) {
|
|
2373
|
+
throw new Error(
|
|
2374
|
+
"internal=true is not supported when replying to a discussion. The GitLab Notes API does not accept an internal flag on discussion replies, so honouring it silently would drop a confidentiality flag. Post a standalone internal note (omit discussionId) instead."
|
|
2375
|
+
);
|
|
2376
|
+
}
|
|
2338
2377
|
return this.fetch(
|
|
2339
2378
|
"POST",
|
|
2340
2379
|
`/groups/${encodedGroup}/epics/${epicIid}/discussions/${discussionId}/notes`,
|
|
@@ -2344,7 +2383,7 @@ var EpicsClient = class extends GitLabApiClient {
|
|
|
2344
2383
|
return this.fetch(
|
|
2345
2384
|
"POST",
|
|
2346
2385
|
`/groups/${encodedGroup}/epics/${epicIid}/notes`,
|
|
2347
|
-
{ body }
|
|
2386
|
+
internal ? { body, internal: true } : { body }
|
|
2348
2387
|
);
|
|
2349
2388
|
}
|
|
2350
2389
|
async getEpicNote(groupId, epicIid, noteId) {
|
|
@@ -4902,9 +4941,14 @@ Creates a standalone comment (not part of a thread).
|
|
|
4902
4941
|
For replying to existing discussion threads, use gitlab_create_discussion
|
|
4903
4942
|
with discussion_id parameter instead.
|
|
4904
4943
|
|
|
4944
|
+
Set internal=true to post a confidential/internal note visible only to project members
|
|
4945
|
+
(supported for merge requests, issues, and epics; not supported for snippets, where passing
|
|
4946
|
+
internal=true raises an error rather than silently posting a public note).
|
|
4947
|
+
|
|
4905
4948
|
Examples:
|
|
4906
4949
|
- MR comment: resource_type="merge_request", project_id="group/project", iid=123, body="LGTM!"
|
|
4907
4950
|
- Issue comment: resource_type="issue", project_id="group/project", iid=456, body="Working on this"
|
|
4951
|
+
- Internal issue note: resource_type="issue", project_id="group/project", iid=456, body="Internal only", internal=true
|
|
4908
4952
|
- Epic comment: resource_type="epic", group_id="my-group", iid=1, body="Planning complete"
|
|
4909
4953
|
- Snippet comment: resource_type="snippet", project_id="group/project", snippet_id=789, body="Nice code!"`,
|
|
4910
4954
|
args: {
|
|
@@ -4913,27 +4957,53 @@ Examples:
|
|
|
4913
4957
|
project_id: z14.string().optional().describe("Project ID or path. Required for merge_request, issue, snippet"),
|
|
4914
4958
|
group_id: z14.string().optional().describe("Group ID or path. Required for epic"),
|
|
4915
4959
|
iid: z14.number().optional().describe("Internal ID of the resource (for merge_request, issue, epic)"),
|
|
4916
|
-
snippet_id: z14.number().optional().describe("Snippet ID (required for snippet)")
|
|
4960
|
+
snippet_id: z14.number().optional().describe("Snippet ID (required for snippet)"),
|
|
4961
|
+
internal: z14.boolean().optional().describe(
|
|
4962
|
+
"Post as a confidential/internal note visible only to project members (merge requests, issues, and epics; not supported for snippets, where passing true raises an error). Defaults to false (public note)."
|
|
4963
|
+
)
|
|
4917
4964
|
},
|
|
4918
4965
|
execute: async (args, _ctx) => {
|
|
4919
4966
|
validateNoteParams(args.resource_type, args, { allowedTypes: NOTE_RESOURCE_TYPES });
|
|
4967
|
+
if (args.internal && args.resource_type === "snippet") {
|
|
4968
|
+
throw new Error(
|
|
4969
|
+
`internal=true is not supported for snippet notes. Refusing to post a note that was intended to be confidential as a public note.`
|
|
4970
|
+
);
|
|
4971
|
+
}
|
|
4920
4972
|
const client = getGitLabClient();
|
|
4921
4973
|
switch (args.resource_type) {
|
|
4922
4974
|
case "merge_request":
|
|
4923
4975
|
return JSON.stringify(
|
|
4924
|
-
await client.createMrNote(
|
|
4976
|
+
await client.createMrNote(
|
|
4977
|
+
args.project_id,
|
|
4978
|
+
args.iid,
|
|
4979
|
+
args.body,
|
|
4980
|
+
void 0,
|
|
4981
|
+
args.internal
|
|
4982
|
+
),
|
|
4925
4983
|
null,
|
|
4926
4984
|
2
|
|
4927
4985
|
);
|
|
4928
4986
|
case "issue":
|
|
4929
4987
|
return JSON.stringify(
|
|
4930
|
-
await client.createIssueNote(
|
|
4988
|
+
await client.createIssueNote(
|
|
4989
|
+
args.project_id,
|
|
4990
|
+
args.iid,
|
|
4991
|
+
args.body,
|
|
4992
|
+
void 0,
|
|
4993
|
+
args.internal
|
|
4994
|
+
),
|
|
4931
4995
|
null,
|
|
4932
4996
|
2
|
|
4933
4997
|
);
|
|
4934
4998
|
case "epic":
|
|
4935
4999
|
return JSON.stringify(
|
|
4936
|
-
await client.createEpicNote(
|
|
5000
|
+
await client.createEpicNote(
|
|
5001
|
+
args.group_id,
|
|
5002
|
+
args.iid,
|
|
5003
|
+
args.body,
|
|
5004
|
+
void 0,
|
|
5005
|
+
args.internal
|
|
5006
|
+
),
|
|
4937
5007
|
null,
|
|
4938
5008
|
2
|
|
4939
5009
|
);
|