opencode-gitlab-plugin 3.0.0 → 3.0.1

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
+ ## [3.0.1](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v3.0.0...v3.0.1) (2026-10-06)
6
+
7
+
8
+ ### 🐛 Bug Fixes
9
+
10
+ * **orbit:** update tools to current Knowledge Graph query DSL ([a62c60b](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/a62c60bc195b63928868424cb5412bf579f23597))
11
+
5
12
  ## [3.0.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.8.2...v3.0.0) (2026-09-21)
6
13
 
7
14
 
package/dist/index.js CHANGED
@@ -5450,6 +5450,50 @@ Examples:
5450
5450
  // src/tools/orbit.ts
5451
5451
  import { tool as tool18 } from "@opencode-ai/plugin";
5452
5452
  var z18 = tool18.schema;
5453
+ function orbitRelTypes(entityType) {
5454
+ switch (entityType) {
5455
+ case "MergeRequest":
5456
+ return [
5457
+ "AUTHORED",
5458
+ "REVIEWER",
5459
+ "ASSIGNED",
5460
+ "CLOSES",
5461
+ "MENTIONS",
5462
+ "IN_PROJECT",
5463
+ "HAS_HEAD_PIPELINE",
5464
+ "TRIGGERED",
5465
+ "IN_MILESTONE",
5466
+ "HAS_LABEL"
5467
+ ];
5468
+ case "WorkItem":
5469
+ return [
5470
+ "AUTHORED",
5471
+ "ASSIGNED",
5472
+ "CLOSES",
5473
+ "MENTIONS",
5474
+ "RELATED_TO",
5475
+ "CONTAINS",
5476
+ "IN_PROJECT",
5477
+ "IN_GROUP",
5478
+ "IN_MILESTONE",
5479
+ "HAS_LABEL"
5480
+ ];
5481
+ case "User":
5482
+ return ["AUTHORED", "ASSIGNED", "REVIEWER", "MEMBER_OF", "OWNER", "CREATOR"];
5483
+ case "Project":
5484
+ return ["CONTAINS", "IN_PROJECT", "MEMBER_OF", "CREATOR"];
5485
+ case "Pipeline":
5486
+ return ["HAS_HEAD_PIPELINE", "TRIGGERED", "IN_PROJECT", "HAS_JOB", "HAS_STAGE"];
5487
+ case "File":
5488
+ return ["CONTAINS", "IMPORTS", "DEFINES", "CALLS", "ON_BRANCH"];
5489
+ case "Group":
5490
+ return ["CONTAINS", "MEMBER_OF", "OWNER", "IN_GROUP"];
5491
+ case "Commit":
5492
+ return ["MERGED_AT_COMMIT"];
5493
+ default:
5494
+ return ["AUTHORED", "ASSIGNED", "REVIEWER", "CLOSES", "MENTIONS", "RELATED_TO", "IN_PROJECT"];
5495
+ }
5496
+ }
5453
5497
  var orbitTools = {
5454
5498
  gitlab_orbit_status: tool18({
5455
5499
  description: `Check GitLab Orbit Knowledge Graph API availability and health.
@@ -5583,47 +5627,64 @@ Note: Requires Orbit API to be available (Premium/Ultimate tier).`,
5583
5627
  description: `Execute a query against the Orbit Knowledge Graph.
5584
5628
 
5585
5629
  Query types:
5586
- - traversal: Walk the graph from a starting node
5587
- - neighbors: Get immediate neighbors of a node
5630
+ - traversal: Fetch matching nodes or follow relationships between nodes
5631
+ - neighbors: Return nodes connected to one bounded node
5632
+ - aggregation: Count/sum/avg/group graph results
5633
+ - path_finding: Find a bounded path between two nodes
5634
+
5635
+ QUERY SHAPE (current DSL, format_version 5.x):
5636
+ Every query has a "query_type" and a "nodes" array of node selectors (max 5).
5637
+ A node selector names an entity and is made SELECTIVE via "node_ids" (exact
5638
+ internal ids) or "filters" (natural properties). Relationships between selectors
5639
+ go in a "relationships" array referencing selector "id" aliases.
5588
5640
 
5589
- IMPORTANT: The query format uses variable names for node references.
5590
- - "id" in node definition is a VARIABLE NAME (alias), not the actual entity ID
5591
- - Actual IDs go in "filters" with {"field": {"op": "eq", "value": X}} format
5641
+ IMPORTANT changes from older docs:
5642
+ - Use a top-level "nodes" ARRAY, not a single "node" object.
5643
+ - Pin ids with "node_ids": [123], NOT filters:{id:{op:"eq",value:123}}.
5644
+ - Filters use {op: value}, e.g. {"state": "merged"} or {"created_at": {"gte": "2026-01-01"}}.
5645
+ - A "neighbors" query uses a 1-element "nodes" array plus a top-level
5646
+ "neighbors": {direction, rel_types:[...]} object. rel_types is REQUIRED to
5647
+ bound the lookup and is capped at 10 entries.
5592
5648
 
5593
- Example neighbors query to get a project's connections:
5649
+ Example neighbors query (an MR's graph neighbours):
5594
5650
  {
5595
5651
  "query_type": "neighbors",
5596
- "node": {
5597
- "entity": "Project",
5598
- "id": "p",
5599
- "filters": {"id": {"op": "eq", "value": 278964}}
5652
+ "nodes": [{ "id": "mr", "entity": "MergeRequest", "node_ids": [507483917] }],
5653
+ "neighbors": {
5654
+ "direction": "both",
5655
+ "rel_types": ["AUTHORED", "REVIEWER", "IN_PROJECT", "CLOSES", "HAS_HEAD_PIPELINE"]
5600
5656
  },
5601
- "neighbors": {"node": "p", "direction": "incoming"},
5602
- "aggregations": [],
5603
- "path": {"type": "shortest", "from": "p", "to": "p", "max_depth": 1}
5657
+ "limit": 30
5604
5658
  }
5605
5659
 
5606
- Example traversal query:
5660
+ Example traversal (merged MRs in a project):
5607
5661
  {
5608
5662
  "query_type": "traversal",
5609
- "node": {
5610
- "entity": "MergeRequest",
5611
- "id": "mr",
5612
- "filters": {"id": {"op": "eq", "value": 377844873}}
5613
- },
5614
- "neighbors": {"node": "mr", "direction": "both"},
5615
- "aggregations": [],
5616
- "path": {"type": "shortest", "from": "mr", "to": "mr", "max_depth": 1}
5663
+ "nodes": [
5664
+ { "id": "project", "entity": "Project", "filters": {"full_path": "gitlab-org/gitlab"} },
5665
+ { "id": "mr", "entity": "MergeRequest", "filters": {"state": "merged"},
5666
+ "columns": ["iid", "title", "merged_at"] }
5667
+ ],
5668
+ "relationships": [{ "type": "IN_PROJECT", "from": "mr", "to": "project" }],
5669
+ "limit": 25
5617
5670
  }
5618
5671
 
5619
- Note: Use gitlab_get_merge_request to get the internal ID from an IID.
5620
- Returns nodes array and edges array with relationship types.`,
5672
+ Notes:
5673
+ - node_ids are GitLab *internal* numeric ids (not IIDs). For an MR, get it via
5674
+ gitlab_get_merge_request (the "id" field). For epics, use work_item_id.
5675
+ - Traversal/aggregation queries must include at least one selective node.
5676
+ - aggregation adds an "aggregations" array (e.g. [{"count":"mr"}]) with optional
5677
+ "group_by"; path_finding adds a "path":{from,to,max_depth} object between two
5678
+ node selectors. Both use the same envelope; see gitlab_orbit_schema and the
5679
+ Orbit query-language docs for the full grammar.
5680
+ - Use gitlab_orbit_schema to discover entities and relationship types.
5681
+ Returns nodes and edges arrays.`,
5621
5682
  args: {
5622
5683
  // Using a JSON string rather than a structured zod schema because the Orbit query
5623
5684
  // format is still evolving. This allows flexibility as the API changes without
5624
5685
  // requiring schema updates for each iteration.
5625
5686
  query: z18.string().describe(
5626
- "JSON string containing the full query object. Must include query_type, node, neighbors, aggregations, and path fields. See description for format details."
5687
+ 'JSON string containing the full query object. Must include "query_type" and a top-level "nodes" array of selectors (each pinned by "node_ids" or "filters"); add "relationships"/"neighbors"/"aggregations"/"path" per the query_type. See the tool description for the current DSL and worked examples.'
5627
5688
  ),
5628
5689
  limit: z18.number().optional().describe("Maximum number of results (default: 30)")
5629
5690
  },
@@ -5642,12 +5703,23 @@ Returns nodes array and edges array with relationship types.`,
5642
5703
  2
5643
5704
  );
5644
5705
  }
5706
+ if (typeof queryObj !== "object" || queryObj === null || Array.isArray(queryObj)) {
5707
+ return JSON.stringify(
5708
+ {
5709
+ error: "Invalid query parameter",
5710
+ suggestion: 'The query must be a JSON object with a "query_type" and a "nodes" array.'
5711
+ },
5712
+ null,
5713
+ 2
5714
+ );
5715
+ }
5716
+ if (args.limit !== void 0) {
5717
+ queryObj.limit = args.limit;
5718
+ }
5645
5719
  const requestBody = {
5646
- query: queryObj
5720
+ query: queryObj,
5721
+ response_format: "raw"
5647
5722
  };
5648
- if (args.limit) {
5649
- requestBody.limit = args.limit;
5650
- }
5651
5723
  try {
5652
5724
  const result = await client.fetch("POST", "/orbit/query", requestBody);
5653
5725
  if (result.code) {
@@ -5656,7 +5728,7 @@ Returns nodes array and edges array with relationship types.`,
5656
5728
  error: "Query compilation error",
5657
5729
  code: result.code,
5658
5730
  message: result.message,
5659
- suggestion: 'Check query structure. Node "id" is a variable name, actual IDs go in filters.'
5731
+ suggestion: 'Check query structure against the current DSL: top-level "nodes" array, pin ids with "node_ids", filters use {op: value}, and a "neighbors" query needs neighbors:{direction, rel_types} (max 10 rel_types).'
5660
5732
  },
5661
5733
  null,
5662
5734
  2
@@ -5704,7 +5776,7 @@ Common use cases:
5704
5776
 
5705
5777
  Note: Use GitLab internal numeric IDs (not IIDs).
5706
5778
  Example: For MR !123 in gitlab-org/gitlab, first get the internal ID via gitlab_get_merge_request,
5707
- then use that ID (e.g., 377844873).`,
5779
+ then use that ID (e.g., 377844873). For epics, use the work_item_id.`,
5708
5780
  args: {
5709
5781
  entity_type: z18.string().describe(
5710
5782
  "Type of the entity (e.g., Project, MergeRequest, User, Pipeline, Commit, WorkItem, Group)"
@@ -5713,33 +5785,38 @@ then use that ID (e.g., 377844873).`,
5713
5785
  "GitLab internal numeric ID (not IID). Get this from REST/GraphQL API responses."
5714
5786
  ),
5715
5787
  direction: z18.enum(["incoming", "outgoing", "both"]).optional().describe("Direction of relationships to traverse (default: both)"),
5788
+ rel_types: z18.array(z18.string()).optional().describe(
5789
+ 'Relationship types to follow. Required by the API to bound the lookup; if omitted, a sensible default set for the entity type is used. The API caps this at 10 per selector, so more than 10 entries are truncated to the first 10 and the response includes a "warnings" field. See gitlab_orbit_schema for valid edge types.'
5790
+ ),
5716
5791
  limit: z18.number().optional().describe("Maximum number of neighbors to return (default: 30)")
5717
5792
  },
5718
5793
  execute: async (args, _ctx) => {
5719
5794
  const client = getGitLabClient();
5720
5795
  const direction = args.direction || "both";
5796
+ const requestedRelTypes = args.rel_types?.length ? args.rel_types : orbitRelTypes(args.entity_type);
5797
+ const relTypes = requestedRelTypes.slice(0, 10);
5798
+ const warnings = requestedRelTypes.length > relTypes.length ? [`rel_types truncated to 10 (got ${requestedRelTypes.length})`] : [];
5721
5799
  const queryObj = {
5722
5800
  query_type: "neighbors",
5723
- node: {
5724
- entity: args.entity_type,
5725
- id: "n",
5726
- filters: {
5727
- id: { op: "eq", value: args.entity_id }
5801
+ nodes: [
5802
+ {
5803
+ id: "n",
5804
+ entity: args.entity_type,
5805
+ node_ids: [args.entity_id]
5728
5806
  }
5729
- },
5807
+ ],
5730
5808
  neighbors: {
5731
- node: "n",
5732
- direction
5733
- },
5734
- aggregations: [],
5735
- path: { type: "shortest", from: "n", to: "n", max_depth: 1 }
5809
+ direction,
5810
+ rel_types: relTypes
5811
+ }
5736
5812
  };
5813
+ if (args.limit !== void 0) {
5814
+ queryObj.limit = args.limit;
5815
+ }
5737
5816
  const requestBody = {
5738
- query: queryObj
5817
+ query: queryObj,
5818
+ response_format: "raw"
5739
5819
  };
5740
- if (args.limit) {
5741
- requestBody.limit = args.limit;
5742
- }
5743
5820
  try {
5744
5821
  const result = await client.fetch("POST", "/orbit/query", requestBody);
5745
5822
  if (result.code) {
@@ -5749,13 +5826,14 @@ then use that ID (e.g., 377844873).`,
5749
5826
  code: result.code,
5750
5827
  message: result.message,
5751
5828
  entity_type: args.entity_type,
5752
- entity_id: args.entity_id
5829
+ entity_id: args.entity_id,
5830
+ ...warnings.length > 0 && { warnings }
5753
5831
  },
5754
5832
  null,
5755
5833
  2
5756
5834
  );
5757
5835
  }
5758
- return JSON.stringify(result, null, 2);
5836
+ return JSON.stringify(warnings.length > 0 ? { ...result, warnings } : result, null, 2);
5759
5837
  } catch (error) {
5760
5838
  const errorMessage = error instanceof Error ? error.message : String(error);
5761
5839
  if (errorMessage.includes("404") || errorMessage.includes("not found")) {