@gitlab/opencode-gitlab-plugin 1.5.4 → 1.5.5

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,14 @@
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
+ ## [1.5.5](https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin/compare/v1.5.4...v1.5.5) (2026-02-03)
6
+
7
+
8
+ ### ♻️ Code Refactoring
9
+
10
+ * consolidate notes tools into unified interface ([2e407ad](https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin/commit/2e407ad9bce03b7c6b2220ab39d6dac9bc82ebd4))
11
+ * improve validation error messages in notes tools ([3c95ca6](https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin/commit/3c95ca66281378f480250ed549611e352319b001))
12
+
5
13
  ## [1.5.4](https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin/compare/v1.5.3...v1.5.4) (2026-02-03)
6
14
 
7
15
 
package/README.md CHANGED
@@ -62,7 +62,7 @@ The following tools use GitLab's GraphQL API for enhanced functionality:
62
62
  | Category | Tools | Benefits |
63
63
  | --------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
64
64
  | **TODOs** | `gitlab_list_todos`, `gitlab_get_todo_count` | Cursor-based pagination, rich filtering |
65
- | **Notes** | `gitlab_list_mr_notes`, `gitlab_list_issue_notes`, `gitlab_list_epic_notes`, `gitlab_list_snippet_notes` | Efficient pagination, consistent response format |
65
+ | **Notes** | `gitlab_list_notes`, `gitlab_get_note`, `gitlab_create_note` | Unified interface for all resource types, efficient pagination |
66
66
  | **Discussions** | `gitlab_list_discussions`, `gitlab_get_discussion`, `gitlab_create_discussion`, `gitlab_resolve_discussion` | Unified interface for all resource types, cursor-based pagination, code position support |
67
67
  | **Auto-merge** | `gitlab_set_mr_auto_merge` | MWPS (Merge When Pipeline Succeeds), merge train support |
68
68
  | **Security** | All vulnerability management tools | Type-safe GID validation, mutation support |
@@ -134,7 +134,7 @@ graph LR
134
134
  B --> B1[readTokenFromAuthStorage]
135
135
  B --> B2[getGitLabClient]
136
136
 
137
- C --> C1[81 Tool Definitions]
137
+ C --> C1[78 Tool Definitions]
138
138
 
139
139
  D --> A
140
140
  D --> B
@@ -287,9 +287,9 @@ Or for API tokens:
287
287
 
288
288
  ## 🛠️ Available Tools
289
289
 
290
- The plugin provides **81 tools** organized into the following categories:
290
+ The plugin provides **78 tools** organized into the following categories:
291
291
 
292
- ### Merge Request Tools (10 tools)
292
+ ### Merge Request Tools (9 tools)
293
293
 
294
294
  | Tool | Description |
295
295
  | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
@@ -298,23 +298,20 @@ The plugin provides **81 tools** organized into the following categories:
298
298
  | `gitlab_create_merge_request` | Create a new merge request |
299
299
  | `gitlab_update_merge_request` | Update merge request title, description, state, assignees, reviewers, and labels |
300
300
  | `gitlab_get_mr_changes` | Get file changes/diffs for a merge request |
301
- | `gitlab_list_mr_notes` | List all comments with cursor-based pagination (GraphQL) |
302
301
  | `gitlab_get_mr_commits` | Get all commits in a merge request |
303
302
  | `gitlab_get_mr_pipelines` | Get all pipelines for a merge request |
304
303
  | `gitlab_list_merge_request_diffs` | List file diffs with pagination support for large changesets |
305
304
  | `gitlab_set_mr_auto_merge` | Enable auto-merge (MWPS) using GraphQL API when pipeline succeeds |
306
305
 
307
- ### Issue Tools (5 tools)
306
+ ### Issue Tools (3 tools)
308
307
 
309
- | Tool | Description |
310
- | ------------------------- | ---------------------------------------------------------------------------- |
311
- | `gitlab_create_issue` | Create a new issue with title, description, labels, assignees, and milestone |
312
- | `gitlab_get_issue` | Get issue details including state, author, assignees, labels, and comments |
313
- | `gitlab_list_issues` | List issues with filtering by state, labels, assignee, and milestone |
314
- | `gitlab_list_issue_notes` | List all comments with cursor-based pagination (GraphQL) |
315
- | `gitlab_get_issue_note` | Get a specific note by ID with full details |
308
+ | Tool | Description |
309
+ | --------------------- | ---------------------------------------------------------------------------- |
310
+ | `gitlab_create_issue` | Create a new issue with title, description, labels, assignees, and milestone |
311
+ | `gitlab_get_issue` | Get issue details including state, author, assignees, labels, and comments |
312
+ | `gitlab_list_issues` | List issues with filtering by state, labels, assignee, and milestone |
316
313
 
317
- ### Epic Tools (9 tools)
314
+ ### Epic Tools (7 tools)
318
315
 
319
316
  | Tool | Description |
320
317
  | ------------------------------- | ----------------------------------------------------------------------------- |
@@ -325,8 +322,6 @@ The plugin provides **81 tools** organized into the following categories:
325
322
  | `gitlab_list_epic_issues` | Get all issues linked to an epic |
326
323
  | `gitlab_add_issue_to_epic` | Link an issue to an epic |
327
324
  | `gitlab_remove_issue_from_epic` | Unlink an issue from an epic |
328
- | `gitlab_list_epic_notes` | List all comments with cursor-based pagination (GraphQL) |
329
- | `gitlab_get_epic_note` | Get a specific epic note by ID |
330
325
 
331
326
  ### Pipeline Tools (8 tools)
332
327
 
@@ -412,12 +407,6 @@ The plugin provides **81 tools** organized into the following categories:
412
407
  | `gitlab_list_project_members` | List all members of a project |
413
408
  | `gitlab_get_current_user` | Get authenticated user information |
414
409
 
415
- ### Snippet Tools (1 tool)
416
-
417
- | Tool | Description |
418
- | --------------------------- | -------------------------------------------------------- |
419
- | `gitlab_list_snippet_notes` | List all comments with cursor-based pagination (GraphQL) |
420
-
421
410
  ### Discussion Tools (4 tools)
422
411
 
423
412
  | Tool | Description |
@@ -427,6 +416,14 @@ The plugin provides **81 tools** organized into the following categories:
427
416
  | `gitlab_create_discussion` | Create a new discussion thread OR reply to an existing one (supports code-position comments for MRs and commits) |
428
417
  | `gitlab_resolve_discussion` | Mark a discussion thread as resolved or unresolve it (MRs and issues only) |
429
418
 
419
+ ### Notes Tools (3 tools)
420
+
421
+ | Tool | Description |
422
+ | -------------------- | --------------------------------------------------------------------------------------------------- |
423
+ | `gitlab_list_notes` | List all notes/comments on any resource (MRs, issues, epics, snippets) with cursor-based pagination |
424
+ | `gitlab_get_note` | Get a single note by ID (issues and epics only) |
425
+ | `gitlab_create_note` | Add a simple comment to any resource (for thread replies, use `gitlab_create_discussion`) |
426
+
430
427
  ### Audit Event Tools (3 tools)
431
428
 
432
429
  | Tool | Description |
@@ -470,10 +467,11 @@ const issue = await plugin.tool.gitlab_create_issue.execute({
470
467
 
471
468
  console.log(`Issue created: ${issue.web_url}`);
472
469
 
473
- // Add a comment to the issue
474
- await plugin.tool.gitlab_create_issue_note.execute({
470
+ // Add a comment to the issue (using unified notes tool)
471
+ await plugin.tool.gitlab_create_note.execute({
472
+ resource_type: 'issue',
475
473
  project_id: 'my-group/my-project',
476
- issue_iid: issue.iid,
474
+ iid: issue.iid,
477
475
  body: 'I will start working on this today.',
478
476
  });
479
477
 
@@ -766,6 +764,7 @@ opencode-gitlab-plugin/
766
764
  │ │ ├── pipelines.ts # Pipeline tool definitions
767
765
  │ │ ├── repository.ts # Repository tool definitions
768
766
  │ │ ├── discussions-unified.ts # Unified discussion tools (4 tools)
767
+ │ │ ├── notes-unified.ts # Unified notes tools (3 tools)
769
768
  │ │ └── ... # Other tool definitions
770
769
  │ ├── index.ts # Main plugin entry point
771
770
  │ ├── utils.ts # Utility functions
package/dist/index.js CHANGED
@@ -2086,33 +2086,6 @@ Returns the list of files changed with their diffs.`,
2086
2086
  return JSON.stringify(changes, null, 2);
2087
2087
  }
2088
2088
  }),
2089
- gitlab_list_mr_notes: tool({
2090
- description: `List all notes/comments on a merge request using GraphQL API with pagination support.
2091
- Returns all comments including system notes, code review comments, and general discussion.
2092
- This is easier to read than discussions which have nested structure.
2093
-
2094
- The response includes pagination information (pageInfo) with cursors for fetching additional pages.
2095
- Use 'after' with the 'endCursor' from pageInfo to get the next page.
2096
- Use 'before' with the 'startCursor' from pageInfo to get the previous page.`,
2097
- args: {
2098
- project_id: z.string().describe("The project ID or URL-encoded path"),
2099
- mr_iid: z.number().describe("The internal ID of the merge request"),
2100
- first: z.number().optional().describe("Number of items to return from the beginning (default: 20, max: 100)"),
2101
- after: z.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
2102
- last: z.number().optional().describe("Number of items to return from the end (for backward pagination)"),
2103
- before: z.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
2104
- },
2105
- execute: async (args, _ctx) => {
2106
- const client = getGitLabClient();
2107
- const result = await client.listMrNotes(args.project_id, args.mr_iid, {
2108
- first: args.first,
2109
- after: args.after,
2110
- last: args.last,
2111
- before: args.before
2112
- });
2113
- return JSON.stringify(result, null, 2);
2114
- }
2115
- }),
2116
2089
  gitlab_create_merge_request: tool({
2117
2090
  description: `Create a new merge request.
2118
2091
  Returns the created merge request with all details.`,
@@ -2330,47 +2303,6 @@ Can filter by state, labels, assignee, milestone.`,
2330
2303
  });
2331
2304
  return JSON.stringify(issues, null, 2);
2332
2305
  }
2333
- }),
2334
- gitlab_list_issue_notes: tool2({
2335
- description: `List all notes/comments on an issue using GraphQL API with pagination support.
2336
- Returns all comments including system notes in chronological order.
2337
-
2338
- The response includes pagination information (pageInfo) with cursors for fetching additional pages.
2339
- Use 'after' with the 'endCursor' from pageInfo to get the next page.
2340
- Use 'before' with the 'startCursor' from pageInfo to get the previous page.`,
2341
- args: {
2342
- project_id: z2.string().describe("The project ID or URL-encoded path"),
2343
- issue_iid: z2.number().describe("The internal ID of the issue"),
2344
- first: z2.number().optional().describe("Number of items to return from the beginning (default: 20, max: 100)"),
2345
- after: z2.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
2346
- last: z2.number().optional().describe("Number of items to return from the end (for backward pagination)"),
2347
- before: z2.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
2348
- },
2349
- execute: async (args, _ctx) => {
2350
- const client = getGitLabClient();
2351
- const result = await client.listIssueNotes(args.project_id, args.issue_iid, {
2352
- first: args.first,
2353
- after: args.after,
2354
- last: args.last,
2355
- before: args.before
2356
- });
2357
- return JSON.stringify(result, null, 2);
2358
- }
2359
- }),
2360
- gitlab_get_issue_note: tool2({
2361
- description: `Get a single note/comment from an issue by its ID.
2362
- Returns the full details of a specific note including author, body, timestamps, and metadata.
2363
- Useful when you need to retrieve a specific comment without fetching all notes.`,
2364
- args: {
2365
- project_id: z2.string().describe("The project ID or URL-encoded path"),
2366
- issue_iid: z2.number().describe("The internal ID of the issue"),
2367
- note_id: z2.number().describe("The ID of the note to retrieve")
2368
- },
2369
- execute: async (args, _ctx) => {
2370
- const client = getGitLabClient();
2371
- const note = await client.getIssueNote(args.project_id, args.issue_iid, args.note_id);
2372
- return JSON.stringify(note, null, 2);
2373
- }
2374
2306
  })
2375
2307
  };
2376
2308
 
@@ -2514,47 +2446,6 @@ Removes the association between the issue and the epic.`,
2514
2446
  );
2515
2447
  return JSON.stringify(result, null, 2);
2516
2448
  }
2517
- }),
2518
- gitlab_list_epic_notes: tool3({
2519
- description: `List all comments/notes on an epic using GraphQL API with pagination support.
2520
- Returns all comments in chronological order.
2521
-
2522
- The response includes pagination information (pageInfo) with cursors for fetching additional pages.
2523
- Use 'after' with the 'endCursor' from pageInfo to get the next page.
2524
- Use 'before' with the 'startCursor' from pageInfo to get the previous page.`,
2525
- args: {
2526
- group_id: z3.string().describe("The group ID or URL-encoded path"),
2527
- epic_iid: z3.number().describe("The internal ID of the epic"),
2528
- first: z3.number().optional().describe("Number of items to return from the beginning (default: 20, max: 100)"),
2529
- after: z3.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
2530
- last: z3.number().optional().describe("Number of items to return from the end (for backward pagination)"),
2531
- before: z3.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
2532
- },
2533
- execute: async (args, _ctx) => {
2534
- const client = getGitLabClient();
2535
- const result = await client.listEpicNotes(args.group_id, args.epic_iid, {
2536
- first: args.first,
2537
- after: args.after,
2538
- last: args.last,
2539
- before: args.before
2540
- });
2541
- return JSON.stringify(result, null, 2);
2542
- }
2543
- }),
2544
- gitlab_get_epic_note: tool3({
2545
- description: `Get a single note/comment from an epic by its ID.
2546
- Returns the full details of a specific note including author, body, timestamps, and metadata.
2547
- Useful when you need to retrieve a specific comment without fetching all notes.`,
2548
- args: {
2549
- group_id: z3.string().describe("The group ID or URL-encoded path"),
2550
- epic_iid: z3.number().describe("The internal ID of the epic"),
2551
- note_id: z3.number().describe("The ID of the note to retrieve")
2552
- },
2553
- execute: async (args, _ctx) => {
2554
- const client = getGitLabClient();
2555
- const note = await client.getEpicNote(args.group_id, args.epic_iid, args.note_id);
2556
- return JSON.stringify(note, null, 2);
2557
- }
2558
2449
  })
2559
2450
  };
2560
2451
 
@@ -3320,43 +3211,11 @@ Requires Developer role or higher.`,
3320
3211
  })
3321
3212
  };
3322
3213
 
3323
- // src/tools/snippets.ts
3214
+ // src/tools/todos.ts
3324
3215
  import { tool as tool9 } from "@opencode-ai/plugin";
3325
3216
  var z9 = tool9.schema;
3326
- var snippetTools = {
3327
- gitlab_list_snippet_notes: tool9({
3328
- description: `List all notes/comments on a project snippet using GraphQL API with pagination support.
3329
- Returns all comments including system notes in chronological order.
3330
-
3331
- The response includes pagination information (pageInfo) with cursors for fetching additional pages.
3332
- Use 'after' with the 'endCursor' from pageInfo to get the next page.
3333
- Use 'before' with the 'startCursor' from pageInfo to get the previous page.`,
3334
- args: {
3335
- project_id: z9.string().describe("The project ID or URL-encoded path"),
3336
- snippet_id: z9.number().describe("The ID of the snippet"),
3337
- first: z9.number().optional().describe("Number of items to return from the beginning (default: 20, max: 100)"),
3338
- after: z9.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
3339
- last: z9.number().optional().describe("Number of items to return from the end (for backward pagination)"),
3340
- before: z9.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
3341
- },
3342
- execute: async (args, _ctx) => {
3343
- const client = getGitLabClient();
3344
- const result = await client.listSnippetNotes(args.project_id, args.snippet_id, {
3345
- first: args.first,
3346
- after: args.after,
3347
- last: args.last,
3348
- before: args.before
3349
- });
3350
- return JSON.stringify(result, null, 2);
3351
- }
3352
- })
3353
- };
3354
-
3355
- // src/tools/todos.ts
3356
- import { tool as tool10 } from "@opencode-ai/plugin";
3357
- var z10 = tool10.schema;
3358
3217
  var todoTools = {
3359
- gitlab_list_todos: tool10({
3218
+ gitlab_list_todos: tool9({
3360
3219
  description: `List TODO items for the current user using GraphQL API with pagination support.
3361
3220
  Returns a list of pending or done TODO items assigned to the authenticated user.
3362
3221
  TODOs are created when you are assigned to an issue/MR, mentioned in a comment, or when someone requests your review.
@@ -3365,7 +3224,7 @@ The response includes pagination information (pageInfo) with cursors for fetchin
3365
3224
  Use 'after' with the 'endCursor' from pageInfo to get the next page.
3366
3225
  Use 'before' with the 'startCursor' from pageInfo to get the previous page.`,
3367
3226
  args: {
3368
- action: z10.enum([
3227
+ action: z9.enum([
3369
3228
  "assigned",
3370
3229
  "mentioned",
3371
3230
  "build_failed",
@@ -3376,15 +3235,15 @@ Use 'before' with the 'startCursor' from pageInfo to get the previous page.`,
3376
3235
  "merge_train_removed",
3377
3236
  "review_requested"
3378
3237
  ]).optional().describe("Filter by action type"),
3379
- author_id: z10.number().optional().describe("Filter by author ID"),
3380
- project_id: z10.string().optional().describe("Filter by project ID or path"),
3381
- group_id: z10.string().optional().describe("Filter by group ID"),
3382
- state: z10.enum(["pending", "done"]).optional().describe("Filter by state (default: pending)"),
3383
- type: z10.enum(["Issue", "MergeRequest", "DesignManagement::Design", "Alert", "Epic", "Commit"]).optional().describe("Filter by target type"),
3384
- first: z10.number().optional().describe("Number of items to return from the beginning (default: 20, max: 100)"),
3385
- after: z10.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
3386
- last: z10.number().optional().describe("Number of items to return from the end (for backward pagination)"),
3387
- before: z10.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
3238
+ author_id: z9.number().optional().describe("Filter by author ID"),
3239
+ project_id: z9.string().optional().describe("Filter by project ID or path"),
3240
+ group_id: z9.string().optional().describe("Filter by group ID"),
3241
+ state: z9.enum(["pending", "done"]).optional().describe("Filter by state (default: pending)"),
3242
+ type: z9.enum(["Issue", "MergeRequest", "DesignManagement::Design", "Alert", "Epic", "Commit"]).optional().describe("Filter by target type"),
3243
+ first: z9.number().optional().describe("Number of items to return from the beginning (default: 20, max: 100)"),
3244
+ after: z9.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
3245
+ last: z9.number().optional().describe("Number of items to return from the end (for backward pagination)"),
3246
+ before: z9.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
3388
3247
  },
3389
3248
  execute: async (args, _ctx) => {
3390
3249
  const client = getGitLabClient();
@@ -3403,11 +3262,11 @@ Use 'before' with the 'startCursor' from pageInfo to get the previous page.`,
3403
3262
  return JSON.stringify(result, null, 2);
3404
3263
  }
3405
3264
  }),
3406
- gitlab_mark_todo_done: tool10({
3265
+ gitlab_mark_todo_done: tool9({
3407
3266
  description: `Mark a specific TODO item as done.
3408
3267
  Use this to mark a single TODO as completed.`,
3409
3268
  args: {
3410
- todo_id: z10.number().describe("The ID of the TODO item to mark as done")
3269
+ todo_id: z9.number().describe("The ID of the TODO item to mark as done")
3411
3270
  },
3412
3271
  execute: async (args, _ctx) => {
3413
3272
  const client = getGitLabClient();
@@ -3415,7 +3274,7 @@ Use this to mark a single TODO as completed.`,
3415
3274
  return JSON.stringify(result, null, 2);
3416
3275
  }
3417
3276
  }),
3418
- gitlab_mark_all_todos_done: tool10({
3277
+ gitlab_mark_all_todos_done: tool9({
3419
3278
  description: `Mark all TODO items as done.
3420
3279
  Use this to mark all pending TODOs for the current user as completed.`,
3421
3280
  args: {},
@@ -3425,7 +3284,7 @@ Use this to mark all pending TODOs for the current user as completed.`,
3425
3284
  return JSON.stringify(result, null, 2);
3426
3285
  }
3427
3286
  }),
3428
- gitlab_get_todo_count: tool10({
3287
+ gitlab_get_todo_count: tool9({
3429
3288
  description: `Get the count of pending TODO items.
3430
3289
  Returns the total number of pending TODOs for the current user.`,
3431
3290
  args: {},
@@ -3438,15 +3297,15 @@ Returns the total number of pending TODOs for the current user.`,
3438
3297
  };
3439
3298
 
3440
3299
  // src/tools/wikis.ts
3441
- import { tool as tool11 } from "@opencode-ai/plugin";
3442
- var z11 = tool11.schema;
3300
+ import { tool as tool10 } from "@opencode-ai/plugin";
3301
+ var z10 = tool10.schema;
3443
3302
  var wikiTools = {
3444
- gitlab_get_wiki_page: tool11({
3303
+ gitlab_get_wiki_page: tool10({
3445
3304
  description: `Get a wiki page with its content.
3446
3305
  Returns the wiki page content and metadata.`,
3447
3306
  args: {
3448
- project_id: z11.string().describe("The project ID or URL-encoded path"),
3449
- slug: z11.string().describe("The slug (URL-friendly name) of the wiki page")
3307
+ project_id: z10.string().describe("The project ID or URL-encoded path"),
3308
+ slug: z10.string().describe("The slug (URL-friendly name) of the wiki page")
3450
3309
  },
3451
3310
  execute: async (args, _ctx) => {
3452
3311
  const client = getGitLabClient();
@@ -3457,15 +3316,15 @@ Returns the wiki page content and metadata.`,
3457
3316
  };
3458
3317
 
3459
3318
  // src/tools/work-items.ts
3460
- import { tool as tool12 } from "@opencode-ai/plugin";
3461
- var z12 = tool12.schema;
3319
+ import { tool as tool11 } from "@opencode-ai/plugin";
3320
+ var z11 = tool11.schema;
3462
3321
  var workItemTools = {
3463
- gitlab_get_work_item: tool12({
3322
+ gitlab_get_work_item: tool11({
3464
3323
  description: `Get a single work item (issue, epic, task, etc.).
3465
3324
  Work items are the new unified model for issues, epics, tasks, and other work tracking items in GitLab.`,
3466
3325
  args: {
3467
- project_id: z12.string().describe("The project ID or URL-encoded path"),
3468
- work_item_id: z12.number().describe("The ID of the work item")
3326
+ project_id: z11.string().describe("The project ID or URL-encoded path"),
3327
+ work_item_id: z11.number().describe("The ID of the work item")
3469
3328
  },
3470
3329
  execute: async (args, _ctx) => {
3471
3330
  const client = getGitLabClient();
@@ -3473,17 +3332,17 @@ Work items are the new unified model for issues, epics, tasks, and other work tr
3473
3332
  return JSON.stringify(workItem, null, 2);
3474
3333
  }
3475
3334
  }),
3476
- gitlab_list_work_items: tool12({
3335
+ gitlab_list_work_items: tool11({
3477
3336
  description: `List work items in a project or group.
3478
3337
  Work items include issues, epics, tasks, and other work tracking items.`,
3479
3338
  args: {
3480
- project_id: z12.string().optional().describe("The project ID or URL-encoded path"),
3481
- group_id: z12.string().optional().describe("The group ID or URL-encoded path"),
3482
- state: z12.enum(["opened", "closed", "all"]).optional().describe("Filter by state (default: opened)"),
3483
- search: z12.string().optional().describe("Search work items by title or description"),
3484
- labels: z12.string().optional().describe("Comma-separated list of labels to filter by"),
3485
- work_item_type: z12.string().optional().describe("Filter by work item type (e.g., 'Issue', 'Epic', 'Task')"),
3486
- limit: z12.number().optional().describe("Maximum number of results (default: 20)")
3339
+ project_id: z11.string().optional().describe("The project ID or URL-encoded path"),
3340
+ group_id: z11.string().optional().describe("The group ID or URL-encoded path"),
3341
+ state: z11.enum(["opened", "closed", "all"]).optional().describe("Filter by state (default: opened)"),
3342
+ search: z11.string().optional().describe("Search work items by title or description"),
3343
+ labels: z11.string().optional().describe("Comma-separated list of labels to filter by"),
3344
+ work_item_type: z11.string().optional().describe("Filter by work item type (e.g., 'Issue', 'Epic', 'Task')"),
3345
+ limit: z11.number().optional().describe("Maximum number of results (default: 20)")
3487
3346
  },
3488
3347
  execute: async (args, _ctx) => {
3489
3348
  const client = getGitLabClient();
@@ -3499,12 +3358,12 @@ Work items include issues, epics, tasks, and other work tracking items.`,
3499
3358
  return JSON.stringify(workItems, null, 2);
3500
3359
  }
3501
3360
  }),
3502
- gitlab_get_work_item_notes: tool12({
3361
+ gitlab_get_work_item_notes: tool11({
3503
3362
  description: `Get all comments for a work item.
3504
3363
  Returns all notes/comments on the work item in chronological order.`,
3505
3364
  args: {
3506
- project_id: z12.string().describe("The project ID or URL-encoded path"),
3507
- work_item_id: z12.number().describe("The ID of the work item")
3365
+ project_id: z11.string().describe("The project ID or URL-encoded path"),
3366
+ work_item_id: z11.number().describe("The ID of the work item")
3508
3367
  },
3509
3368
  execute: async (args, _ctx) => {
3510
3369
  const client = getGitLabClient();
@@ -3512,16 +3371,16 @@ Returns all notes/comments on the work item in chronological order.`,
3512
3371
  return JSON.stringify(notes, null, 2);
3513
3372
  }
3514
3373
  }),
3515
- gitlab_create_work_item: tool12({
3374
+ gitlab_create_work_item: tool11({
3516
3375
  description: `Create a new work item (issue, task, etc.).
3517
3376
  Work items are the new unified model for issues, epics, tasks, and other work tracking items.`,
3518
3377
  args: {
3519
- project_id: z12.string().describe("The project ID or URL-encoded path"),
3520
- title: z12.string().describe("The title of the work item"),
3521
- work_item_type_id: z12.number().describe("The ID of the work item type (e.g., 1 for Issue, 2 for Task)"),
3522
- description: z12.string().optional().describe("The description of the work item (supports Markdown)"),
3523
- labels: z12.array(z12.string()).optional().describe("Array of label names"),
3524
- assignee_ids: z12.array(z12.number()).optional().describe("Array of user IDs to assign")
3378
+ project_id: z11.string().describe("The project ID or URL-encoded path"),
3379
+ title: z11.string().describe("The title of the work item"),
3380
+ work_item_type_id: z11.number().describe("The ID of the work item type (e.g., 1 for Issue, 2 for Task)"),
3381
+ description: z11.string().optional().describe("The description of the work item (supports Markdown)"),
3382
+ labels: z11.array(z11.string()).optional().describe("Array of label names"),
3383
+ assignee_ids: z11.array(z11.number()).optional().describe("Array of user IDs to assign")
3525
3384
  },
3526
3385
  execute: async (args, _ctx) => {
3527
3386
  const client = getGitLabClient();
@@ -3535,17 +3394,17 @@ Work items are the new unified model for issues, epics, tasks, and other work tr
3535
3394
  return JSON.stringify(workItem, null, 2);
3536
3395
  }
3537
3396
  }),
3538
- gitlab_update_work_item: tool12({
3397
+ gitlab_update_work_item: tool11({
3539
3398
  description: `Update an existing work item.
3540
3399
  Can update title, description, state, labels, and assignees.`,
3541
3400
  args: {
3542
- project_id: z12.string().describe("The project ID or URL-encoded path"),
3543
- work_item_id: z12.number().describe("The ID of the work item"),
3544
- title: z12.string().optional().describe("The new title"),
3545
- description: z12.string().optional().describe("The new description (supports Markdown)"),
3546
- state_event: z12.enum(["close", "reopen"]).optional().describe("Change the state (close or reopen)"),
3547
- labels: z12.array(z12.string()).optional().describe("Array of label names"),
3548
- assignee_ids: z12.array(z12.number()).optional().describe("Array of user IDs to assign")
3401
+ project_id: z11.string().describe("The project ID or URL-encoded path"),
3402
+ work_item_id: z11.number().describe("The ID of the work item"),
3403
+ title: z11.string().optional().describe("The new title"),
3404
+ description: z11.string().optional().describe("The new description (supports Markdown)"),
3405
+ state_event: z11.enum(["close", "reopen"]).optional().describe("Change the state (close or reopen)"),
3406
+ labels: z11.array(z11.string()).optional().describe("Array of label names"),
3407
+ assignee_ids: z11.array(z11.number()).optional().describe("Array of user IDs to assign")
3549
3408
  },
3550
3409
  execute: async (args, _ctx) => {
3551
3410
  const client = getGitLabClient();
@@ -3559,12 +3418,12 @@ Can update title, description, state, labels, and assignees.`,
3559
3418
  return JSON.stringify(workItem, null, 2);
3560
3419
  }
3561
3420
  }),
3562
- gitlab_create_work_item_note: tool12({
3421
+ gitlab_create_work_item_note: tool11({
3563
3422
  description: `Create a comment on a work item.`,
3564
3423
  args: {
3565
- project_id: z12.string().describe("The project ID or URL-encoded path"),
3566
- work_item_id: z12.number().describe("The ID of the work item"),
3567
- body: z12.string().describe("The content of the note/comment (supports Markdown)")
3424
+ project_id: z11.string().describe("The project ID or URL-encoded path"),
3425
+ work_item_id: z11.number().describe("The ID of the work item"),
3426
+ body: z11.string().describe("The content of the note/comment (supports Markdown)")
3568
3427
  },
3569
3428
  execute: async (args, _ctx) => {
3570
3429
  const client = getGitLabClient();
@@ -3575,17 +3434,17 @@ Can update title, description, state, labels, and assignees.`,
3575
3434
  };
3576
3435
 
3577
3436
  // src/tools/discussions-unified.ts
3578
- import { tool as tool13 } from "@opencode-ai/plugin";
3579
- var z13 = tool13.schema;
3580
- var positionSchema = z13.object({
3581
- base_sha: z13.string().describe("SHA of the base commit"),
3582
- start_sha: z13.string().describe("SHA of the start commit"),
3583
- head_sha: z13.string().describe("SHA of the head commit"),
3584
- position_type: z13.enum(["text", "image"]).describe("Type of position"),
3585
- old_path: z13.string().optional().describe("Path of the file before changes"),
3586
- new_path: z13.string().optional().describe("Path of the file after changes"),
3587
- old_line: z13.number().optional().describe("Line number in the old version"),
3588
- new_line: z13.number().optional().describe("Line number in the new version")
3437
+ import { tool as tool12 } from "@opencode-ai/plugin";
3438
+ var z12 = tool12.schema;
3439
+ var positionSchema = z12.object({
3440
+ base_sha: z12.string().describe("SHA of the base commit"),
3441
+ start_sha: z12.string().describe("SHA of the start commit"),
3442
+ head_sha: z12.string().describe("SHA of the head commit"),
3443
+ position_type: z12.enum(["text", "image"]).describe("Type of position"),
3444
+ old_path: z12.string().optional().describe("Path of the file before changes"),
3445
+ new_path: z12.string().optional().describe("Path of the file after changes"),
3446
+ old_line: z12.number().optional().describe("Line number in the old version"),
3447
+ new_line: z12.number().optional().describe("Line number in the new version")
3589
3448
  });
3590
3449
  function validateResourceParams(resourceType, args) {
3591
3450
  switch (resourceType) {
@@ -3612,7 +3471,7 @@ var discussionsUnifiedTools = {
3612
3471
  /**
3613
3472
  * List discussions for any GitLab resource type
3614
3473
  */
3615
- gitlab_list_discussions: tool13({
3474
+ gitlab_list_discussions: tool12({
3616
3475
  description: `List discussions (comment threads) on any GitLab resource.
3617
3476
  Supports: merge_requests, issues, epics, commits, snippets.
3618
3477
 
@@ -3628,17 +3487,17 @@ Examples:
3628
3487
  - Commit: resource_type="commit", project_id="group/project", sha="abc123"
3629
3488
  - Snippet: resource_type="snippet", project_id="group/project", snippet_id=789`,
3630
3489
  args: {
3631
- resource_type: z13.enum(["merge_request", "issue", "epic", "commit", "snippet"]).describe("Type of GitLab resource"),
3632
- project_id: z13.string().optional().describe("Project ID or path. Required for merge_request, issue, commit, snippet"),
3633
- group_id: z13.string().optional().describe("Group ID or path. Required for epic"),
3634
- iid: z13.number().optional().describe("Internal ID of the resource (for merge_request, issue, epic)"),
3635
- sha: z13.string().optional().describe("Commit SHA (required for commit)"),
3636
- snippet_id: z13.number().optional().describe("Snippet ID (required for snippet)"),
3490
+ resource_type: z12.enum(["merge_request", "issue", "epic", "commit", "snippet"]).describe("Type of GitLab resource"),
3491
+ project_id: z12.string().optional().describe("Project ID or path. Required for merge_request, issue, commit, snippet"),
3492
+ group_id: z12.string().optional().describe("Group ID or path. Required for epic"),
3493
+ iid: z12.number().optional().describe("Internal ID of the resource (for merge_request, issue, epic)"),
3494
+ sha: z12.string().optional().describe("Commit SHA (required for commit)"),
3495
+ snippet_id: z12.number().optional().describe("Snippet ID (required for snippet)"),
3637
3496
  // Pagination
3638
- first: z13.number().optional().describe("Number of items to return (default: 20)"),
3639
- after: z13.string().optional().describe("Cursor for pagination - use endCursor from previous response"),
3640
- before: z13.string().optional().describe("Cursor for backward pagination"),
3641
- last: z13.number().optional().describe("Number of items from the end")
3497
+ first: z12.number().optional().describe("Number of items to return (default: 20)"),
3498
+ after: z12.string().optional().describe("Cursor for pagination - use endCursor from previous response"),
3499
+ before: z12.string().optional().describe("Cursor for backward pagination"),
3500
+ last: z12.number().optional().describe("Number of items from the end")
3642
3501
  },
3643
3502
  execute: async (args, _ctx) => {
3644
3503
  validateResourceParams(args.resource_type, args);
@@ -3692,7 +3551,7 @@ Examples:
3692
3551
  /**
3693
3552
  * Get a specific discussion thread from any GitLab resource
3694
3553
  */
3695
- gitlab_get_discussion: tool13({
3554
+ gitlab_get_discussion: tool12({
3696
3555
  description: `Get a specific discussion thread with all its replies.
3697
3556
  Returns the discussion with its 'notes' array containing all comments.
3698
3557
 
@@ -3705,13 +3564,13 @@ Required parameters vary by resource type:
3705
3564
  - commit: project_id, sha, discussion_id
3706
3565
  - snippet: project_id, snippet_id, discussion_id`,
3707
3566
  args: {
3708
- resource_type: z13.enum(["merge_request", "issue", "epic", "commit", "snippet"]).describe("Type of GitLab resource"),
3709
- discussion_id: z13.string().describe("The ID of the discussion thread"),
3710
- project_id: z13.string().optional().describe("Project ID or path"),
3711
- group_id: z13.string().optional().describe("Group ID or path (for epic)"),
3712
- iid: z13.number().optional().describe("Internal ID (for merge_request, issue, epic)"),
3713
- sha: z13.string().optional().describe("Commit SHA (for commit)"),
3714
- snippet_id: z13.number().optional().describe("Snippet ID (for snippet)")
3567
+ resource_type: z12.enum(["merge_request", "issue", "epic", "commit", "snippet"]).describe("Type of GitLab resource"),
3568
+ discussion_id: z12.string().describe("The ID of the discussion thread"),
3569
+ project_id: z12.string().optional().describe("Project ID or path"),
3570
+ group_id: z12.string().optional().describe("Group ID or path (for epic)"),
3571
+ iid: z12.number().optional().describe("Internal ID (for merge_request, issue, epic)"),
3572
+ sha: z12.string().optional().describe("Commit SHA (for commit)"),
3573
+ snippet_id: z12.number().optional().describe("Snippet ID (for snippet)")
3715
3574
  },
3716
3575
  execute: async (args, _ctx) => {
3717
3576
  validateResourceParams(args.resource_type, args);
@@ -3759,7 +3618,7 @@ Required parameters vary by resource type:
3759
3618
  /**
3760
3619
  * Create a new discussion thread or reply to an existing one
3761
3620
  */
3762
- gitlab_create_discussion: tool13({
3621
+ gitlab_create_discussion: tool12({
3763
3622
  description: `Create a new discussion thread or reply to an existing one.
3764
3623
 
3765
3624
  For NEW discussion: Omit discussion_id
@@ -3772,14 +3631,14 @@ Examples:
3772
3631
  - Reply to thread: resource_type="merge_request", ..., discussion_id="...", body="..."
3773
3632
  - Code comment: resource_type="merge_request", ..., body="...", position={base_sha, head_sha, ...}`,
3774
3633
  args: {
3775
- resource_type: z13.enum(["merge_request", "issue", "epic", "commit", "snippet"]).describe("Type of GitLab resource"),
3776
- body: z13.string().describe("The comment text (Markdown supported)"),
3777
- project_id: z13.string().optional().describe("Project ID or path"),
3778
- group_id: z13.string().optional().describe("Group ID or path (for epic)"),
3779
- iid: z13.number().optional().describe("Internal ID (for merge_request, issue, epic)"),
3780
- sha: z13.string().optional().describe("Commit SHA (for commit)"),
3781
- snippet_id: z13.number().optional().describe("Snippet ID (for snippet)"),
3782
- discussion_id: z13.string().optional().describe("If provided, replies to existing discussion. If omitted, creates new thread"),
3634
+ resource_type: z12.enum(["merge_request", "issue", "epic", "commit", "snippet"]).describe("Type of GitLab resource"),
3635
+ body: z12.string().describe("The comment text (Markdown supported)"),
3636
+ project_id: z12.string().optional().describe("Project ID or path"),
3637
+ group_id: z12.string().optional().describe("Group ID or path (for epic)"),
3638
+ iid: z12.number().optional().describe("Internal ID (for merge_request, issue, epic)"),
3639
+ sha: z12.string().optional().describe("Commit SHA (for commit)"),
3640
+ snippet_id: z12.number().optional().describe("Snippet ID (for snippet)"),
3641
+ discussion_id: z12.string().optional().describe("If provided, replies to existing discussion. If omitted, creates new thread"),
3783
3642
  position: positionSchema.optional().describe("Position for code-specific comments (MR/commit only)")
3784
3643
  },
3785
3644
  execute: async (args, _ctx) => {
@@ -3844,17 +3703,17 @@ Examples:
3844
3703
  /**
3845
3704
  * Resolve or unresolve a discussion thread
3846
3705
  */
3847
- gitlab_resolve_discussion: tool13({
3706
+ gitlab_resolve_discussion: tool12({
3848
3707
  description: `Mark a discussion thread as resolved or unresolve it.
3849
3708
  Only works for resolvable discussions (MRs and issues only).
3850
3709
 
3851
3710
  Use after addressing feedback to indicate the discussion is complete.`,
3852
3711
  args: {
3853
- resource_type: z13.enum(["merge_request", "issue"]).describe("Type of resource (only MR and issue discussions can be resolved)"),
3854
- action: z13.enum(["resolve", "unresolve"]).describe("Whether to resolve or unresolve"),
3855
- discussion_id: z13.string().describe("The ID of the discussion thread"),
3856
- project_id: z13.string().describe("Project ID or path"),
3857
- iid: z13.number().describe("Internal ID of the MR or issue")
3712
+ resource_type: z12.enum(["merge_request", "issue"]).describe("Type of resource (only MR and issue discussions can be resolved)"),
3713
+ action: z12.enum(["resolve", "unresolve"]).describe("Whether to resolve or unresolve"),
3714
+ discussion_id: z12.string().describe("The ID of the discussion thread"),
3715
+ project_id: z12.string().describe("Project ID or path"),
3716
+ iid: z12.number().describe("Internal ID of the MR or issue")
3858
3717
  },
3859
3718
  execute: async (args, _ctx) => {
3860
3719
  const client = getGitLabClient();
@@ -3893,6 +3752,224 @@ Use after addressing feedback to indicate the discussion is complete.`,
3893
3752
  })
3894
3753
  };
3895
3754
 
3755
+ // src/tools/notes-unified.ts
3756
+ import { tool as tool13 } from "@opencode-ai/plugin";
3757
+ var z13 = tool13.schema;
3758
+ var VALID_LIST_CREATE_TYPES = ["merge_request", "issue", "epic", "snippet"];
3759
+ var VALID_GET_NOTE_TYPES = ["issue", "epic"];
3760
+ function validationError(param, resourceType) {
3761
+ return new Error(
3762
+ `Missing required parameter: '${param}' is required for resource_type '${resourceType}'`
3763
+ );
3764
+ }
3765
+ function validateListCreateParams(resourceType, args) {
3766
+ if (!VALID_LIST_CREATE_TYPES.includes(resourceType)) {
3767
+ throw new Error(
3768
+ `Invalid resource_type '${resourceType}'. Must be one of: ${VALID_LIST_CREATE_TYPES.join(", ")}`
3769
+ );
3770
+ }
3771
+ switch (resourceType) {
3772
+ case "merge_request":
3773
+ case "issue":
3774
+ if (!args.project_id) throw validationError("project_id", resourceType);
3775
+ if (args.iid == null) throw validationError("iid", resourceType);
3776
+ break;
3777
+ case "epic":
3778
+ if (!args.group_id) throw validationError("group_id", resourceType);
3779
+ if (args.iid == null) throw validationError("iid", resourceType);
3780
+ break;
3781
+ case "snippet":
3782
+ if (!args.project_id) throw validationError("project_id", resourceType);
3783
+ if (args.snippet_id == null) throw validationError("snippet_id", resourceType);
3784
+ break;
3785
+ }
3786
+ }
3787
+ function validateGetNoteParams(resourceType, args) {
3788
+ if (!VALID_GET_NOTE_TYPES.includes(resourceType)) {
3789
+ throw new Error(
3790
+ `Invalid resource_type '${resourceType}'. Must be one of: ${VALID_GET_NOTE_TYPES.join(", ")}`
3791
+ );
3792
+ }
3793
+ if (args.note_id == null) throw validationError("note_id", resourceType);
3794
+ switch (resourceType) {
3795
+ case "issue":
3796
+ if (!args.project_id) throw validationError("project_id", resourceType);
3797
+ if (args.iid == null) throw validationError("iid", resourceType);
3798
+ break;
3799
+ case "epic":
3800
+ if (!args.group_id) throw validationError("group_id", resourceType);
3801
+ if (args.iid == null) throw validationError("iid", resourceType);
3802
+ break;
3803
+ }
3804
+ }
3805
+ var notesUnifiedTools = {
3806
+ /**
3807
+ * List notes/comments for any GitLab resource type
3808
+ */
3809
+ gitlab_list_notes: tool13({
3810
+ description: `List all notes/comments on any GitLab resource using GraphQL API with pagination support.
3811
+ Returns all comments including system notes in chronological order.
3812
+ This is easier to read than discussions which have nested structure.
3813
+
3814
+ The response includes pagination information (pageInfo) with cursors for fetching additional pages.
3815
+ Use 'after' with the 'endCursor' from pageInfo to get the next page.
3816
+ Use 'before' with the 'startCursor' from pageInfo to get the previous page.
3817
+
3818
+ Examples:
3819
+ - MR: resource_type="merge_request", project_id="group/project", iid=123
3820
+ - Issue: resource_type="issue", project_id="group/project", iid=456
3821
+ - Epic: resource_type="epic", group_id="my-group", iid=1
3822
+ - Snippet: resource_type="snippet", project_id="group/project", snippet_id=789`,
3823
+ args: {
3824
+ resource_type: z13.enum(["merge_request", "issue", "epic", "snippet"]).describe("Type of GitLab resource"),
3825
+ project_id: z13.string().optional().describe("Project ID or path. Required for merge_request, issue, snippet"),
3826
+ group_id: z13.string().optional().describe("Group ID or path. Required for epic"),
3827
+ iid: z13.number().optional().describe("Internal ID of the resource (for merge_request, issue, epic)"),
3828
+ snippet_id: z13.number().optional().describe("Snippet ID (required for snippet)"),
3829
+ // Pagination
3830
+ first: z13.number().optional().describe("Number of items to return from the beginning (default: 20, max: 100)"),
3831
+ after: z13.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
3832
+ last: z13.number().optional().describe("Number of items to return from the end (for backward pagination)"),
3833
+ before: z13.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
3834
+ },
3835
+ execute: async (args, _ctx) => {
3836
+ validateListCreateParams(args.resource_type, args);
3837
+ const client = getGitLabClient();
3838
+ const paginationOptions = {
3839
+ first: args.first,
3840
+ after: args.after,
3841
+ last: args.last,
3842
+ before: args.before
3843
+ };
3844
+ switch (args.resource_type) {
3845
+ case "merge_request":
3846
+ return JSON.stringify(
3847
+ await client.listMrNotes(args.project_id, args.iid, paginationOptions),
3848
+ null,
3849
+ 2
3850
+ );
3851
+ case "issue":
3852
+ return JSON.stringify(
3853
+ await client.listIssueNotes(args.project_id, args.iid, paginationOptions),
3854
+ null,
3855
+ 2
3856
+ );
3857
+ case "epic":
3858
+ return JSON.stringify(
3859
+ await client.listEpicNotes(args.group_id, args.iid, paginationOptions),
3860
+ null,
3861
+ 2
3862
+ );
3863
+ case "snippet":
3864
+ return JSON.stringify(
3865
+ await client.listSnippetNotes(args.project_id, args.snippet_id, paginationOptions),
3866
+ null,
3867
+ 2
3868
+ );
3869
+ default:
3870
+ throw new Error(`Unsupported resource type: ${args.resource_type}`);
3871
+ }
3872
+ }
3873
+ }),
3874
+ /**
3875
+ * Get a single note/comment by its ID
3876
+ */
3877
+ gitlab_get_note: tool13({
3878
+ description: `Get a single note/comment from an issue or epic by its ID.
3879
+ Returns the full details of a specific note including author, body, timestamps, and metadata.
3880
+ Useful when you need to retrieve a specific comment without fetching all notes.
3881
+
3882
+ Supports: issues, epics (MR notes use discussions API)
3883
+
3884
+ Examples:
3885
+ - Issue note: resource_type="issue", project_id="group/project", iid=456, note_id=123
3886
+ - Epic note: resource_type="epic", group_id="my-group", iid=1, note_id=456`,
3887
+ args: {
3888
+ resource_type: z13.enum(["issue", "epic"]).describe("Type of GitLab resource (issue or epic only)"),
3889
+ note_id: z13.number().describe("The ID of the note to retrieve"),
3890
+ project_id: z13.string().optional().describe("Project ID or path. Required for issue"),
3891
+ group_id: z13.string().optional().describe("Group ID or path. Required for epic"),
3892
+ iid: z13.number().describe("Internal ID of the issue or epic")
3893
+ },
3894
+ execute: async (args, _ctx) => {
3895
+ validateGetNoteParams(args.resource_type, args);
3896
+ const client = getGitLabClient();
3897
+ switch (args.resource_type) {
3898
+ case "issue":
3899
+ return JSON.stringify(
3900
+ await client.getIssueNote(args.project_id, args.iid, args.note_id),
3901
+ null,
3902
+ 2
3903
+ );
3904
+ case "epic":
3905
+ return JSON.stringify(
3906
+ await client.getEpicNote(args.group_id, args.iid, args.note_id),
3907
+ null,
3908
+ 2
3909
+ );
3910
+ default:
3911
+ throw new Error(`Unsupported resource type: ${args.resource_type}`);
3912
+ }
3913
+ }
3914
+ }),
3915
+ /**
3916
+ * Create a simple note/comment on any GitLab resource
3917
+ */
3918
+ gitlab_create_note: tool13({
3919
+ description: `Add a simple comment/note to any GitLab resource.
3920
+ Creates a standalone comment (not part of a thread).
3921
+
3922
+ For replying to existing discussion threads, use gitlab_create_discussion
3923
+ with discussion_id parameter instead.
3924
+
3925
+ Examples:
3926
+ - MR comment: resource_type="merge_request", project_id="group/project", iid=123, body="LGTM!"
3927
+ - Issue comment: resource_type="issue", project_id="group/project", iid=456, body="Working on this"
3928
+ - Epic comment: resource_type="epic", group_id="my-group", iid=1, body="Planning complete"
3929
+ - Snippet comment: resource_type="snippet", project_id="group/project", snippet_id=789, body="Nice code!"`,
3930
+ args: {
3931
+ resource_type: z13.enum(["merge_request", "issue", "epic", "snippet"]).describe("Type of GitLab resource"),
3932
+ body: z13.string().describe("The content of the note/comment (supports Markdown)"),
3933
+ project_id: z13.string().optional().describe("Project ID or path. Required for merge_request, issue, snippet"),
3934
+ group_id: z13.string().optional().describe("Group ID or path. Required for epic"),
3935
+ iid: z13.number().optional().describe("Internal ID of the resource (for merge_request, issue, epic)"),
3936
+ snippet_id: z13.number().optional().describe("Snippet ID (required for snippet)")
3937
+ },
3938
+ execute: async (args, _ctx) => {
3939
+ validateListCreateParams(args.resource_type, args);
3940
+ const client = getGitLabClient();
3941
+ switch (args.resource_type) {
3942
+ case "merge_request":
3943
+ return JSON.stringify(
3944
+ await client.createMrNote(args.project_id, args.iid, args.body),
3945
+ null,
3946
+ 2
3947
+ );
3948
+ case "issue":
3949
+ return JSON.stringify(
3950
+ await client.createIssueNote(args.project_id, args.iid, args.body),
3951
+ null,
3952
+ 2
3953
+ );
3954
+ case "epic":
3955
+ return JSON.stringify(
3956
+ await client.createEpicNote(args.group_id, args.iid, args.body),
3957
+ null,
3958
+ 2
3959
+ );
3960
+ case "snippet":
3961
+ return JSON.stringify(
3962
+ await client.createSnippetNote(args.project_id, args.snippet_id, args.body),
3963
+ null,
3964
+ 2
3965
+ );
3966
+ default:
3967
+ throw new Error(`Unsupported resource type: ${args.resource_type}`);
3968
+ }
3969
+ }
3970
+ })
3971
+ };
3972
+
3896
3973
  // src/tools/git.ts
3897
3974
  import { tool as tool14 } from "@opencode-ai/plugin";
3898
3975
 
@@ -4249,8 +4326,6 @@ var gitlabPlugin = async (_input) => {
4249
4326
  ...userTools,
4250
4327
  // Security Tools
4251
4328
  ...securityTools,
4252
- // Snippet Tools
4253
- ...snippetTools,
4254
4329
  // TODO Tools
4255
4330
  ...todoTools,
4256
4331
  // Wiki Tools
@@ -4259,6 +4334,8 @@ var gitlabPlugin = async (_input) => {
4259
4334
  ...workItemTools,
4260
4335
  // Unified Discussion Tools (covers MR, issue, epic, commit, snippet discussions)
4261
4336
  ...discussionsUnifiedTools,
4337
+ // Unified Notes Tools (covers MR, issue, epic, snippet notes)
4338
+ ...notesUnifiedTools,
4262
4339
  // Git Tools
4263
4340
  ...gitTools,
4264
4341
  // Audit Tools
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/opencode-gitlab-plugin",
3
- "version": "1.5.4",
3
+ "version": "1.5.5",
4
4
  "description": "GitLab tools plugin for OpenCode - provides GitLab API access for merge requests, issues, pipelines, and more",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",