@gitlab/opencode-gitlab-plugin 1.3.0 → 1.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 CHANGED
@@ -2,6 +2,19 @@
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.4.0](https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin/compare/v1.3.0...v1.4.0) (2026-02-02)
6
+
7
+
8
+ ### ✨ Features
9
+
10
+ * **todos:** migrate listTodos to GraphQL API with pagination support ([5726c8b](https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin/commit/5726c8bb3b8fbce6f014c0f3500bf4d913eb4033))
11
+
12
+
13
+ ### 🐛 Bug Fixes
14
+
15
+ * **todos:** add validation for conflicting pagination parameters ([f46d8c2](https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin/commit/f46d8c2d4c0f4e4b6363013fc3bcee676b78d777))
16
+ * **todos:** address MR review feedback ([87b3afa](https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin/commit/87b3afae2992e789c59f0c908c7b48a957e4a931))
17
+
5
18
  ## [1.3.0](https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin/compare/v1.2.0...v1.3.0) (2026-01-30)
6
19
 
7
20
 
package/dist/index.js CHANGED
@@ -1206,20 +1206,163 @@ var SecurityClient = class extends GitLabApiClient {
1206
1206
  };
1207
1207
 
1208
1208
  // src/client/todos.ts
1209
+ var LIST_TODOS_QUERY = `
1210
+ query listTodos(
1211
+ $state: [TodoStateEnum!]
1212
+ $action: [TodoActionEnum!]
1213
+ $type: [TodoTargetEnum!]
1214
+ $projectId: [ID!]
1215
+ $groupId: [ID!]
1216
+ $authorId: [ID!]
1217
+ $first: Int
1218
+ $after: String
1219
+ $last: Int
1220
+ $before: String
1221
+ ) {
1222
+ currentUser {
1223
+ todos(
1224
+ state: $state
1225
+ action: $action
1226
+ type: $type
1227
+ projectId: $projectId
1228
+ groupId: $groupId
1229
+ authorId: $authorId
1230
+ first: $first
1231
+ after: $after
1232
+ last: $last
1233
+ before: $before
1234
+ ) {
1235
+ count
1236
+ pageInfo {
1237
+ hasNextPage
1238
+ hasPreviousPage
1239
+ startCursor
1240
+ endCursor
1241
+ }
1242
+ nodes {
1243
+ id
1244
+ body
1245
+ state
1246
+ action
1247
+ createdAt
1248
+ targetType
1249
+ targetUrl
1250
+ snoozedUntil
1251
+ project {
1252
+ id
1253
+ name
1254
+ fullPath
1255
+ }
1256
+ group {
1257
+ id
1258
+ name
1259
+ fullPath
1260
+ }
1261
+ author {
1262
+ id
1263
+ username
1264
+ name
1265
+ avatarUrl
1266
+ }
1267
+ targetEntity {
1268
+ __typename
1269
+ ... on Issue {
1270
+ id
1271
+ title
1272
+ iid
1273
+ }
1274
+ ... on MergeRequest {
1275
+ id
1276
+ title
1277
+ iid
1278
+ }
1279
+ ... on Epic {
1280
+ id
1281
+ title
1282
+ iid
1283
+ }
1284
+ ... on Commit {
1285
+ id
1286
+ title
1287
+ }
1288
+ ... on DesignManagement__Design {
1289
+ id
1290
+ }
1291
+ ... on AlertManagement__Alert {
1292
+ id
1293
+ title
1294
+ iid
1295
+ }
1296
+ }
1297
+ }
1298
+ }
1299
+ }
1300
+ }
1301
+ `;
1209
1302
  var TodosClient = class extends GitLabApiClient {
1210
1303
  async listTodos(options) {
1211
- const params = new URLSearchParams();
1212
- params.set("per_page", String(options?.limit || 20));
1213
- if (options?.action) params.set("action", options.action);
1214
- if (options?.author_id) params.set("author_id", String(options.author_id));
1304
+ if (options?.first !== void 0 && options?.last !== void 0) {
1305
+ throw new Error(
1306
+ 'Cannot specify both "first" and "last" pagination parameters. Use "first"/"after" for forward pagination or "last"/"before" for backward pagination.'
1307
+ );
1308
+ }
1309
+ const variables = {};
1310
+ if (options?.first !== void 0) {
1311
+ variables.first = options.first;
1312
+ } else if (options?.last === void 0) {
1313
+ variables.first = 20;
1314
+ }
1315
+ if (options?.after) variables.after = options.after;
1316
+ if (options?.last !== void 0) variables.last = options.last;
1317
+ if (options?.before) variables.before = options.before;
1318
+ if (options?.action) {
1319
+ variables.action = [this.mapTodoAction(options.action)];
1320
+ }
1321
+ if (options?.author_id) {
1322
+ variables.authorId = [`gid://gitlab/User/${options.author_id}`];
1323
+ }
1215
1324
  if (options?.project_id) {
1216
- const encodedProject = this.encodeProjectId(options.project_id);
1217
- params.set("project_id", encodedProject);
1325
+ variables.projectId = [options.project_id];
1218
1326
  }
1219
- if (options?.group_id) params.set("group_id", options.group_id);
1220
- if (options?.state) params.set("state", options.state);
1221
- if (options?.type) params.set("type", options.type);
1222
- return this.fetch("GET", `/todos?${params}`);
1327
+ if (options?.group_id) {
1328
+ variables.groupId = [options.group_id];
1329
+ }
1330
+ if (options?.state) {
1331
+ variables.state = [options.state.toLowerCase()];
1332
+ }
1333
+ if (options?.type) {
1334
+ variables.type = [this.mapTodoTargetType(options.type)];
1335
+ }
1336
+ const result = await this.fetchGraphQL(LIST_TODOS_QUERY, variables);
1337
+ const todos = result.currentUser.todos;
1338
+ return {
1339
+ todos
1340
+ };
1341
+ }
1342
+ mapTodoAction(action) {
1343
+ const actionMap = {
1344
+ assigned: "ASSIGNED",
1345
+ mentioned: "MENTIONED",
1346
+ build_failed: "BUILD_FAILED",
1347
+ marked: "MARKED",
1348
+ approval_required: "APPROVAL_REQUIRED",
1349
+ unmergeable: "UNMERGEABLE",
1350
+ directly_addressed: "DIRECTLY_ADDRESSED",
1351
+ merge_train_removed: "MERGE_TRAIN_REMOVED",
1352
+ review_requested: "REVIEW_REQUESTED"
1353
+ };
1354
+ return actionMap[action] || action.toUpperCase();
1355
+ }
1356
+ mapTodoTargetType(type) {
1357
+ const typeMap = {
1358
+ Issue: "ISSUE",
1359
+ MergeRequest: "MERGEREQUEST",
1360
+ "DesignManagement::Design": "DESIGN",
1361
+ Alert: "ALERT",
1362
+ Commit: "COMMIT",
1363
+ Epic: "EPIC"
1364
+ };
1365
+ return typeMap[type] || type;
1223
1366
  }
1224
1367
  async markTodoAsDone(todoId) {
1225
1368
  return this.fetch("POST", `/todos/${todoId}/mark_as_done`);
@@ -1228,7 +1371,8 @@ var TodosClient = class extends GitLabApiClient {
1228
1371
  return this.fetch("POST", "/todos/mark_as_done");
1229
1372
  }
1230
1373
  async getTodoCount() {
1231
- return this.fetch("GET", "/todos/count");
1374
+ const result = await this.fetchGraphQL(`query { currentUser { todos(state: [pending]) { count } } }`);
1375
+ return { count: result.currentUser.todos.count };
1232
1376
  }
1233
1377
  };
1234
1378
 
@@ -3524,9 +3668,13 @@ import { tool as tool10 } from "@opencode-ai/plugin";
3524
3668
  var z10 = tool10.schema;
3525
3669
  var todoTools = {
3526
3670
  gitlab_list_todos: tool10({
3527
- description: `List TODO items for the current user.
3671
+ description: `List TODO items for the current user using GraphQL API with pagination support.
3528
3672
  Returns a list of pending or done TODO items assigned to the authenticated user.
3529
- TODOs are created when you are assigned to an issue/MR, mentioned in a comment, or when someone requests your review.`,
3673
+ TODOs are created when you are assigned to an issue/MR, mentioned in a comment, or when someone requests your review.
3674
+
3675
+ The response includes pagination information (pageInfo) with cursors for fetching additional pages.
3676
+ Use 'after' with the 'endCursor' from pageInfo to get the next page.
3677
+ Use 'before' with the 'startCursor' from pageInfo to get the previous page.`,
3530
3678
  args: {
3531
3679
  action: z10.enum([
3532
3680
  "assigned",
@@ -3543,21 +3691,27 @@ TODOs are created when you are assigned to an issue/MR, mentioned in a comment,
3543
3691
  project_id: z10.string().optional().describe("Filter by project ID or path"),
3544
3692
  group_id: z10.string().optional().describe("Filter by group ID"),
3545
3693
  state: z10.enum(["pending", "done"]).optional().describe("Filter by state (default: pending)"),
3546
- type: z10.enum(["Issue", "MergeRequest", "DesignManagement::Design", "Alert"]).optional().describe("Filter by target type"),
3547
- limit: z10.number().optional().describe("Maximum number of results (default: 20)")
3694
+ type: z10.enum(["Issue", "MergeRequest", "DesignManagement::Design", "Alert", "Epic", "Commit"]).optional().describe("Filter by target type"),
3695
+ first: z10.number().optional().describe("Number of items to return from the beginning (default: 20, max: 100)"),
3696
+ after: z10.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
3697
+ last: z10.number().optional().describe("Number of items to return from the end (for backward pagination)"),
3698
+ before: z10.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
3548
3699
  },
3549
3700
  execute: async (args, _ctx) => {
3550
3701
  const client = getGitLabClient();
3551
- const todos = await client.listTodos({
3702
+ const result = await client.listTodos({
3552
3703
  action: args.action,
3553
3704
  author_id: args.author_id,
3554
3705
  project_id: args.project_id,
3555
3706
  group_id: args.group_id,
3556
3707
  state: args.state,
3557
3708
  type: args.type,
3558
- limit: args.limit
3709
+ first: args.first,
3710
+ after: args.after,
3711
+ last: args.last,
3712
+ before: args.before
3559
3713
  });
3560
- return JSON.stringify(todos, null, 2);
3714
+ return JSON.stringify(result, null, 2);
3561
3715
  }
3562
3716
  }),
3563
3717
  gitlab_mark_todo_done: tool10({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/opencode-gitlab-plugin",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
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",