opencode-gitlab-plugin 2.0.0 → 2.0.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,21 @@
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.0.2](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.0.1...v2.0.2) (2026-03-22)
6
+
7
+
8
+ ### 🐛 Bug Fixes
9
+
10
+ * **work-items:** use workItems(iid:) connection instead of non-existent workItem(iid:) ([e5ef90e](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/e5ef90e557d2ee5e1e53ad85b65b3b71ea93d9cb))
11
+
12
+ ## [2.0.1](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.0.0...v2.0.1) (2026-03-22)
13
+
14
+
15
+ ### 🐛 Bug Fixes
16
+
17
+ * **work-items:** address review comments ([0173f25](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/0173f25afbecf9b01a61a0239a4112c22e96864f))
18
+ * **work-items:** switch to GraphQL API to resolve iid-vs-id 404 bug ([fcd3417](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/fcd3417792e9fcea1ef0a08de2e09d483e769e25))
19
+
5
20
  ## [2.0.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v1.9.0...v2.0.0) (2026-03-02)
6
21
 
7
22
 
package/dist/index.js CHANGED
@@ -718,63 +718,362 @@ var IssuesClient = class extends GitLabApiClient {
718
718
  };
719
719
 
720
720
  // src/client/work-items.ts
721
+ function toGid(type, id) {
722
+ return `gid://gitlab/${type}/${id}`;
723
+ }
724
+ var WORK_ITEM_LIST_FIELDS = `
725
+ id
726
+ iid
727
+ title
728
+ description
729
+ state
730
+ createdAt
731
+ updatedAt
732
+ workItemType {
733
+ id
734
+ name
735
+ }
736
+ author {
737
+ id
738
+ username
739
+ name
740
+ }
741
+ assignees {
742
+ nodes {
743
+ id
744
+ username
745
+ name
746
+ }
747
+ }
748
+ labels {
749
+ nodes {
750
+ id
751
+ title
752
+ color
753
+ }
754
+ }
755
+ webUrl
756
+ `;
757
+ var GET_WORK_ITEM_QUERY = `
758
+ query getWorkItem($fullPath: ID!, $iid: String!) {
759
+ workspace: project(fullPath: $fullPath) {
760
+ workItems(iid: $iid, first: 1) {
761
+ nodes {
762
+ id
763
+ iid
764
+ title
765
+ description
766
+ state
767
+ createdAt
768
+ updatedAt
769
+ closedAt
770
+ confidential
771
+ workItemType {
772
+ id
773
+ name
774
+ }
775
+ author {
776
+ id
777
+ username
778
+ name
779
+ }
780
+ assignees {
781
+ nodes {
782
+ id
783
+ username
784
+ name
785
+ }
786
+ }
787
+ labels {
788
+ nodes {
789
+ id
790
+ title
791
+ color
792
+ }
793
+ }
794
+ webUrl
795
+ }
796
+ }
797
+ }
798
+ }
799
+ `;
800
+ var LIST_WORK_ITEMS_QUERY = `
801
+ query listWorkItems(
802
+ $fullPath: ID!
803
+ $state: WorkItemState
804
+ $search: String
805
+ $types: [WorkItemTypeEnum!]
806
+ $labelNames: [String!]
807
+ $first: Int
808
+ ) {
809
+ workspace: project(fullPath: $fullPath) {
810
+ workItems(
811
+ state: $state
812
+ search: $search
813
+ types: $types
814
+ labelName: $labelNames
815
+ first: $first
816
+ ) {
817
+ nodes {
818
+ ${WORK_ITEM_LIST_FIELDS}
819
+ }
820
+ }
821
+ }
822
+ }
823
+ `;
824
+ var LIST_GROUP_WORK_ITEMS_QUERY = `
825
+ query listGroupWorkItems(
826
+ $fullPath: ID!
827
+ $state: WorkItemState
828
+ $search: String
829
+ $types: [WorkItemTypeEnum!]
830
+ $labelNames: [String!]
831
+ $first: Int
832
+ ) {
833
+ workspace: group(fullPath: $fullPath) {
834
+ workItems(
835
+ state: $state
836
+ search: $search
837
+ types: $types
838
+ labelName: $labelNames
839
+ first: $first
840
+ ) {
841
+ nodes {
842
+ ${WORK_ITEM_LIST_FIELDS}
843
+ }
844
+ }
845
+ }
846
+ }
847
+ `;
848
+ var GET_WORK_ITEM_NOTES_QUERY = `
849
+ query getWorkItemNotes($fullPath: ID!, $iid: String!) {
850
+ workspace: project(fullPath: $fullPath) {
851
+ workItems(iid: $iid, first: 1) {
852
+ nodes {
853
+ id
854
+ iid
855
+ notes {
856
+ nodes {
857
+ id
858
+ body
859
+ createdAt
860
+ updatedAt
861
+ author {
862
+ id
863
+ username
864
+ name
865
+ }
866
+ system
867
+ resolvable
868
+ resolved
869
+ }
870
+ }
871
+ }
872
+ }
873
+ }
874
+ }
875
+ `;
876
+ var CREATE_WORK_ITEM_MUTATION = `
877
+ mutation createWorkItem(
878
+ $projectPath: ID!
879
+ $title: String!
880
+ $workItemTypeId: WorkItemsTypeID!
881
+ $description: String
882
+ $labelIds: [LabelID!]
883
+ $assigneeIds: [UserID!]
884
+ ) {
885
+ workItemCreate(input: {
886
+ projectPath: $projectPath
887
+ title: $title
888
+ workItemTypeId: $workItemTypeId
889
+ descriptionWidget: { description: $description }
890
+ labelsWidget: { labelIds: $labelIds }
891
+ assigneesWidget: { assigneeIds: $assigneeIds }
892
+ }) {
893
+ workItem {
894
+ id
895
+ iid
896
+ title
897
+ description
898
+ state
899
+ createdAt
900
+ updatedAt
901
+ workItemType {
902
+ id
903
+ name
904
+ }
905
+ author {
906
+ id
907
+ username
908
+ name
909
+ }
910
+ webUrl
911
+ }
912
+ errors
913
+ }
914
+ }
915
+ `;
916
+ var UPDATE_WORK_ITEM_MUTATION = `
917
+ mutation updateWorkItem(
918
+ $id: WorkItemID!
919
+ $title: String
920
+ $description: String
921
+ $stateEvent: WorkItemStateEvent
922
+ $labelIds: [LabelID!]
923
+ $assigneeIds: [UserID!]
924
+ ) {
925
+ workItemUpdate(input: {
926
+ id: $id
927
+ title: $title
928
+ descriptionWidget: { description: $description }
929
+ stateEvent: $stateEvent
930
+ labelsWidget: { labelIds: $labelIds }
931
+ assigneesWidget: { assigneeIds: $assigneeIds }
932
+ }) {
933
+ workItem {
934
+ id
935
+ iid
936
+ title
937
+ description
938
+ state
939
+ updatedAt
940
+ workItemType {
941
+ id
942
+ name
943
+ }
944
+ webUrl
945
+ }
946
+ errors
947
+ }
948
+ }
949
+ `;
950
+ var CREATE_NOTE_MUTATION = `
951
+ mutation createWorkItemNote($noteableId: NoteableID!, $body: String!) {
952
+ createNote(input: { noteableId: $noteableId, body: $body }) {
953
+ note {
954
+ id
955
+ body
956
+ createdAt
957
+ author {
958
+ id
959
+ username
960
+ name
961
+ }
962
+ }
963
+ errors
964
+ }
965
+ }
966
+ `;
967
+ var GET_WORK_ITEM_GID_QUERY = `
968
+ query getWorkItemGid($fullPath: ID!, $iid: String!) {
969
+ workspace: project(fullPath: $fullPath) {
970
+ workItems(iid: $iid, first: 1) {
971
+ nodes {
972
+ id
973
+ }
974
+ }
975
+ }
976
+ }
977
+ `;
721
978
  var WorkItemsClient = class extends GitLabApiClient {
722
- async getWorkItem(projectId, workItemId) {
723
- const encodedProject = this.encodeProjectId(projectId);
724
- return this.fetch(
725
- "GET",
726
- `/projects/${encodedProject}/work_items/${workItemId}`
727
- );
979
+ async getWorkItem(projectId, workItemIid) {
980
+ const result = await this.fetchGraphQL(GET_WORK_ITEM_QUERY, {
981
+ fullPath: projectId,
982
+ iid: String(workItemIid)
983
+ });
984
+ const workItem = result.workspace?.workItems?.nodes?.[0];
985
+ if (!workItem) {
986
+ throw new Error(`Work item with iid ${workItemIid} not found in project ${projectId}`);
987
+ }
988
+ return workItem;
728
989
  }
729
990
  async listWorkItems(options) {
730
- const params = new URLSearchParams();
731
- params.set("per_page", String(options.limit || 20));
732
- if (options.state) params.set("state", options.state);
733
- if (options.search) params.set("search", options.search);
734
- if (options.labels) params.set("labels", options.labels);
735
- if (options.work_item_type) params.set("type", options.work_item_type);
736
- let path2;
991
+ const variables = {
992
+ state: options.state ? options.state.toUpperCase() : void 0,
993
+ search: options.search,
994
+ types: options.work_item_type ? [options.work_item_type.toUpperCase()] : void 0,
995
+ labelNames: options.labels ? options.labels.split(",").map((l) => l.trim()) : void 0,
996
+ first: options.limit || 20
997
+ };
737
998
  if (options.projectId) {
738
- const encodedProject = this.encodeProjectId(options.projectId);
739
- path2 = `/projects/${encodedProject}/work_items?${params}`;
999
+ const result = await this.fetchGraphQL(LIST_WORK_ITEMS_QUERY, { fullPath: options.projectId, ...variables });
1000
+ return result.workspace?.workItems?.nodes ?? [];
740
1001
  } else if (options.groupId) {
741
- const encodedGroup = encodeURIComponent(options.groupId);
742
- path2 = `/groups/${encodedGroup}/work_items?${params}`;
1002
+ const result = await this.fetchGraphQL(LIST_GROUP_WORK_ITEMS_QUERY, { fullPath: options.groupId, ...variables });
1003
+ return result.workspace?.workItems?.nodes ?? [];
743
1004
  } else {
744
1005
  throw new Error("Either projectId or groupId must be provided");
745
1006
  }
746
- return this.fetch("GET", path2);
747
1007
  }
748
- async getWorkItemNotes(projectId, workItemId) {
749
- const encodedProject = this.encodeProjectId(projectId);
750
- return this.fetch(
751
- "GET",
752
- `/projects/${encodedProject}/work_items/${workItemId}/notes`
753
- );
1008
+ async getWorkItemNotes(projectId, workItemIid) {
1009
+ const result = await this.fetchGraphQL(GET_WORK_ITEM_NOTES_QUERY, {
1010
+ fullPath: projectId,
1011
+ iid: String(workItemIid)
1012
+ });
1013
+ const workItem = result.workspace?.workItems?.nodes?.[0];
1014
+ if (!workItem) {
1015
+ throw new Error(`Work item with iid ${workItemIid} not found in project ${projectId}`);
1016
+ }
1017
+ return workItem.notes?.nodes ?? [];
754
1018
  }
755
1019
  async createWorkItem(projectId, options) {
756
- const encodedProject = this.encodeProjectId(projectId);
757
- return this.fetch(
758
- "POST",
759
- `/projects/${encodedProject}/work_items`,
760
- options
761
- );
1020
+ const labelIds = options.labels?.map((l) => toGid("Label", l));
1021
+ const assigneeIds = options.assignee_ids?.map((id) => toGid("User", id));
1022
+ const result = await this.fetchGraphQL(CREATE_WORK_ITEM_MUTATION, {
1023
+ projectPath: projectId,
1024
+ title: options.title,
1025
+ workItemTypeId: toGid("WorkItems::Type", options.work_item_type_id),
1026
+ description: options.description,
1027
+ labelIds: labelIds?.length ? labelIds : void 0,
1028
+ assigneeIds: assigneeIds?.length ? assigneeIds : void 0
1029
+ });
1030
+ if (result.workItemCreate.errors.length > 0) {
1031
+ throw new Error(`Failed to create work item: ${result.workItemCreate.errors.join(", ")}`);
1032
+ }
1033
+ return result.workItemCreate.workItem;
762
1034
  }
763
- async updateWorkItem(projectId, workItemId, options) {
764
- const encodedProject = this.encodeProjectId(projectId);
765
- return this.fetch(
766
- "PUT",
767
- `/projects/${encodedProject}/work_items/${workItemId}`,
768
- options
769
- );
1035
+ async updateWorkItem(projectId, workItemIid, options) {
1036
+ const gidResult = await this.fetchGraphQL(GET_WORK_ITEM_GID_QUERY, {
1037
+ fullPath: projectId,
1038
+ iid: String(workItemIid)
1039
+ });
1040
+ const gid = gidResult.workspace?.workItems?.nodes?.[0]?.id;
1041
+ if (!gid) {
1042
+ throw new Error(`Work item with iid ${workItemIid} not found in project ${projectId}`);
1043
+ }
1044
+ const stateEvent = options.state_event ? options.state_event === "close" ? "CLOSE" : "REOPEN" : void 0;
1045
+ const labelIds = options.labels?.map((l) => toGid("Label", l));
1046
+ const assigneeIds = options.assignee_ids?.map((id) => toGid("User", id));
1047
+ const result = await this.fetchGraphQL(UPDATE_WORK_ITEM_MUTATION, {
1048
+ id: gid,
1049
+ title: options.title,
1050
+ description: options.description,
1051
+ stateEvent,
1052
+ labelIds: labelIds?.length ? labelIds : void 0,
1053
+ assigneeIds: assigneeIds?.length ? assigneeIds : void 0
1054
+ });
1055
+ if (result.workItemUpdate.errors.length > 0) {
1056
+ throw new Error(`Failed to update work item: ${result.workItemUpdate.errors.join(", ")}`);
1057
+ }
1058
+ return result.workItemUpdate.workItem;
770
1059
  }
771
- async createWorkItemNote(projectId, workItemId, body) {
772
- const encodedProject = this.encodeProjectId(projectId);
773
- return this.fetch(
774
- "POST",
775
- `/projects/${encodedProject}/work_items/${workItemId}/notes`,
776
- { body }
777
- );
1060
+ async createWorkItemNote(projectId, workItemIid, body) {
1061
+ const gidResult = await this.fetchGraphQL(GET_WORK_ITEM_GID_QUERY, {
1062
+ fullPath: projectId,
1063
+ iid: String(workItemIid)
1064
+ });
1065
+ const gid = gidResult.workspace?.workItems?.nodes?.[0]?.id;
1066
+ if (!gid) {
1067
+ throw new Error(`Work item with iid ${workItemIid} not found in project ${projectId}`);
1068
+ }
1069
+ const result = await this.fetchGraphQL(CREATE_NOTE_MUTATION, {
1070
+ noteableId: gid,
1071
+ body
1072
+ });
1073
+ if (result.createNote.errors.length > 0) {
1074
+ throw new Error(`Failed to create note: ${result.createNote.errors.join(", ")}`);
1075
+ }
1076
+ return result.createNote.note;
778
1077
  }
779
1078
  };
780
1079
 
@@ -3598,8 +3897,10 @@ var workItemTools = {
3598
3897
  description: `Get a single work item (issue, epic, task, etc.).
3599
3898
  Work items are the new unified model for issues, epics, tasks, and other work tracking items in GitLab.`,
3600
3899
  args: {
3601
- project_id: z12.string().describe("The project ID or URL-encoded path"),
3602
- work_item_id: z12.number().describe("The ID of the work item")
3900
+ project_id: z12.string().describe('The project ID or full path (e.g. "gitlab-org/gitlab")'),
3901
+ work_item_id: z12.number().describe(
3902
+ "The IID of the work item \u2014 the number visible in the GitLab URL (e.g. for https://gitlab.com/group/project/-/work_items/52, use 52). This is NOT the global database ID."
3903
+ )
3603
3904
  },
3604
3905
  execute: async (args, _ctx) => {
3605
3906
  const client = getGitLabClient();
@@ -3637,8 +3938,10 @@ Work items include issues, epics, tasks, and other work tracking items.`,
3637
3938
  description: `Get all comments for a work item.
3638
3939
  Returns all notes/comments on the work item in chronological order.`,
3639
3940
  args: {
3640
- project_id: z12.string().describe("The project ID or URL-encoded path"),
3641
- work_item_id: z12.number().describe("The ID of the work item")
3941
+ project_id: z12.string().describe('The project ID or full path (e.g. "gitlab-org/gitlab")'),
3942
+ work_item_id: z12.number().describe(
3943
+ "The IID of the work item \u2014 the number visible in the GitLab URL (e.g. for https://gitlab.com/group/project/-/work_items/52, use 52). This is NOT the global database ID."
3944
+ )
3642
3945
  },
3643
3946
  execute: async (args, _ctx) => {
3644
3947
  const client = getGitLabClient();
@@ -3673,8 +3976,10 @@ Work items are the new unified model for issues, epics, tasks, and other work tr
3673
3976
  description: `Update an existing work item.
3674
3977
  Can update title, description, state, labels, and assignees.`,
3675
3978
  args: {
3676
- project_id: z12.string().describe("The project ID or URL-encoded path"),
3677
- work_item_id: z12.number().describe("The ID of the work item"),
3979
+ project_id: z12.string().describe('The project ID or full path (e.g. "gitlab-org/gitlab")'),
3980
+ work_item_id: z12.number().describe(
3981
+ "The IID of the work item \u2014 the number visible in the GitLab URL (e.g. for https://gitlab.com/group/project/-/work_items/52, use 52). This is NOT the global database ID."
3982
+ ),
3678
3983
  title: z12.string().optional().describe("The new title"),
3679
3984
  description: z12.string().optional().describe("The new description (supports Markdown)"),
3680
3985
  state_event: z12.enum(["close", "reopen"]).optional().describe("Change the state (close or reopen)"),
@@ -3696,8 +4001,10 @@ Can update title, description, state, labels, and assignees.`,
3696
4001
  gitlab_create_work_item_note: tool12({
3697
4002
  description: `Create a comment on a work item.`,
3698
4003
  args: {
3699
- project_id: z12.string().describe("The project ID or URL-encoded path"),
3700
- work_item_id: z12.number().describe("The ID of the work item"),
4004
+ project_id: z12.string().describe('The project ID or full path (e.g. "gitlab-org/gitlab")'),
4005
+ work_item_id: z12.number().describe(
4006
+ "The IID of the work item \u2014 the number visible in the GitLab URL (e.g. for https://gitlab.com/group/project/-/work_items/52, use 52). This is NOT the global database ID."
4007
+ ),
3701
4008
  body: z12.string().describe("The content of the note/comment (supports Markdown)")
3702
4009
  },
3703
4010
  execute: async (args, _ctx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-gitlab-plugin",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
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",