opencode-gitlab-dap 1.5.1 → 1.6.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/README.md +40 -4
- package/dist/chunk-DPR6OUYG.js +119 -0
- package/dist/chunk-DPR6OUYG.js.map +1 -0
- package/dist/index.cjs +1768 -3268
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1650 -3285
- package/dist/index.js.map +1 -1
- package/dist/mcp-servers-2HCDB7XM.js +11 -0
- package/dist/mcp-servers-2HCDB7XM.js.map +1 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -91,7 +91,26 @@ The plugin guides you through an interactive workflow:
|
|
|
91
91
|
|
|
92
92
|
The `confirmed` parameter on `gitlab_create_agent` enforces the interactive workflow — the tool returns instructions instead of creating the agent when called without explicit confirmation.
|
|
93
93
|
|
|
94
|
-
###
|
|
94
|
+
### Custom Flow Creation
|
|
95
|
+
|
|
96
|
+
Design and create custom flows interactively:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
Create a flow that fetches MR data, analyzes the changes, and posts a summary comment
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The plugin provides a multi-round design workflow:
|
|
103
|
+
|
|
104
|
+
1. `gitlab_design_flow` with `action="get_schema"` returns the flow YAML schema reference, two annotated examples, and step-by-step instructions
|
|
105
|
+
2. The LLM asks the user about the flow's purpose, resource type, and steps
|
|
106
|
+
3. The LLM proposes a component architecture for user approval
|
|
107
|
+
4. The LLM generates the full YAML, validates it client-side against the `flow_v2` JSON schema via `gitlab_design_flow` with `action="validate"`
|
|
108
|
+
5. After user confirmation, `gitlab_create_flow` with `confirmed=true` submits it to GitLab
|
|
109
|
+
6. After creation, offers to enable the flow on the current project via `gitlab_enable_project_flow`
|
|
110
|
+
|
|
111
|
+
The vendored `flow_v2.json` schema from GitLab Rails powers client-side validation using `ajv`, catching errors before hitting the API.
|
|
112
|
+
|
|
113
|
+
### 20 DAP Tools
|
|
95
114
|
|
|
96
115
|
| Tool | Description |
|
|
97
116
|
| --------------------------------- | ------------------------------------------------------- |
|
|
@@ -102,7 +121,10 @@ The `confirmed` parameter on `gitlab_create_agent` enforces the interactive work
|
|
|
102
121
|
| `gitlab_disable_project_agent` | Disable an agent in a project |
|
|
103
122
|
| `gitlab_create_agent` | Create a custom agent (interactive, confirmation-gated) |
|
|
104
123
|
| `gitlab_update_agent` | Update an existing custom agent |
|
|
105
|
-
| `gitlab_list_builtin_tools` | List available built-in tools for agent
|
|
124
|
+
| `gitlab_list_builtin_tools` | List available built-in tools for agent/flow config |
|
|
125
|
+
| `gitlab_design_flow` | Interactive flow design + YAML validation |
|
|
126
|
+
| `gitlab_create_flow` | Create a custom flow (confirmation-gated) |
|
|
127
|
+
| `gitlab_update_flow` | Update an existing custom flow |
|
|
106
128
|
| `gitlab_list_flows` | Search flows in the global AI Catalog |
|
|
107
129
|
| `gitlab_get_flow` | Get flow details by ID |
|
|
108
130
|
| `gitlab_list_project_flows` | List flows enabled for a project |
|
|
@@ -124,6 +146,9 @@ Foundational flow definitions (from `gitlab-org/modelops/applied-ml/code-suggest
|
|
|
124
146
|
are vendored and embedded at build time. This provides flow input schemas for
|
|
125
147
|
foundational flows whose configs are not available via the GitLab API.
|
|
126
148
|
|
|
149
|
+
The `flow_v2.json` JSON schema is also vendored from GitLab Rails and bundled
|
|
150
|
+
inline for client-side YAML validation via `ajv`.
|
|
151
|
+
|
|
127
152
|
## Agent Types
|
|
128
153
|
|
|
129
154
|
### Foundational Agents
|
|
@@ -167,7 +192,7 @@ export GITLAB_INSTANCE_URL=https://your-instance.com
|
|
|
167
192
|
|
|
168
193
|
```bash
|
|
169
194
|
npm install
|
|
170
|
-
npm test # Run tests (
|
|
195
|
+
npm test # Run tests (69 tests)
|
|
171
196
|
npm run build # Build (auto-runs vendor:generate)
|
|
172
197
|
npm run type-check
|
|
173
198
|
```
|
|
@@ -202,7 +227,7 @@ Plugin init
|
|
|
202
227
|
├─ config hook injects agents (primary) + flows (subagent with prompt)
|
|
203
228
|
├─ chat.message hook intercepts @flow → general subagent dispatch
|
|
204
229
|
├─ experimental.chat.system.transform injects flow dispatch + agent creation guidelines
|
|
205
|
-
└─ tool hook
|
|
230
|
+
└─ tool hook 20 DAP tools
|
|
206
231
|
|
|
207
232
|
Single flow execution (@FlowName goal):
|
|
208
233
|
├─ chat.message hook rewrites @mention → Task tool with general subagent
|
|
@@ -230,6 +255,17 @@ Agent creation (interactive):
|
|
|
230
255
|
├─ gitlab_create_agent called with confirmed=true → creates agent
|
|
231
256
|
└─ gitlab_enable_project_agent optionally enable on project
|
|
232
257
|
|
|
258
|
+
Flow creation (interactive, multi-round):
|
|
259
|
+
├─ gitlab_design_flow (get_schema) returns schema reference + examples + instructions
|
|
260
|
+
├─ gitlab_list_builtin_tools discover available tool names for flow components
|
|
261
|
+
├─ question tool ask user for name, visibility, purpose, steps
|
|
262
|
+
├─ LLM proposes component architecture → user confirms
|
|
263
|
+
├─ LLM generates flow YAML definition
|
|
264
|
+
├─ gitlab_design_flow (validate) client-side validation against flow_v2.json via ajv
|
|
265
|
+
├─ User confirms the generated YAML
|
|
266
|
+
├─ gitlab_create_flow called with confirmed=true → creates flow
|
|
267
|
+
└─ gitlab_enable_project_flow optionally enable on project
|
|
268
|
+
|
|
233
269
|
Enable/disable:
|
|
234
270
|
└─ refreshAgents() clears cache, re-fetches, updates cfg.agent
|
|
235
271
|
```
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// src/graphql.ts
|
|
2
|
+
async function gql(instanceUrl, token, query, variables) {
|
|
3
|
+
const res = await fetch(`${instanceUrl.replace(/\/$/, "")}/api/graphql`, {
|
|
4
|
+
method: "POST",
|
|
5
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
6
|
+
body: JSON.stringify({ query, variables })
|
|
7
|
+
});
|
|
8
|
+
const json = await res.json();
|
|
9
|
+
if (json.errors?.length) throw new Error(json.errors[0].message);
|
|
10
|
+
return json.data;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/mcp-servers.ts
|
|
14
|
+
var MCP_SERVERS_QUERY = `
|
|
15
|
+
query AiCatalogMcpServers($projectId: ProjectID!, $after: String) {
|
|
16
|
+
aiCatalogConfiguredItems(first: 50, projectId: $projectId, itemTypes: [AGENT], after: $after) {
|
|
17
|
+
pageInfo { hasNextPage endCursor }
|
|
18
|
+
nodes {
|
|
19
|
+
item {
|
|
20
|
+
id
|
|
21
|
+
name
|
|
22
|
+
latestVersion {
|
|
23
|
+
... on AiCatalogAgentVersion {
|
|
24
|
+
mcpServers {
|
|
25
|
+
nodes { id name description url transport authType currentUserConnected }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}`;
|
|
33
|
+
async function fetchMcpServers(instanceUrl, token, projectId, agents) {
|
|
34
|
+
try {
|
|
35
|
+
let after = null;
|
|
36
|
+
const serversByAgent = /* @__PURE__ */ new Map();
|
|
37
|
+
for (; ; ) {
|
|
38
|
+
const data = await gql(instanceUrl, token, MCP_SERVERS_QUERY, {
|
|
39
|
+
projectId,
|
|
40
|
+
...after ? { after } : {}
|
|
41
|
+
});
|
|
42
|
+
const page = data?.aiCatalogConfiguredItems;
|
|
43
|
+
if (!page) break;
|
|
44
|
+
for (const node of page.nodes ?? []) {
|
|
45
|
+
const item = node.item;
|
|
46
|
+
const version = item?.latestVersion;
|
|
47
|
+
if (!version?.mcpServers?.nodes?.length) continue;
|
|
48
|
+
const servers = version.mcpServers.nodes.map((s) => ({
|
|
49
|
+
id: s.id,
|
|
50
|
+
name: s.name,
|
|
51
|
+
description: s.description ?? "",
|
|
52
|
+
url: s.url,
|
|
53
|
+
transport: s.transport,
|
|
54
|
+
authType: s.authType,
|
|
55
|
+
currentUserConnected: !!s.currentUserConnected
|
|
56
|
+
}));
|
|
57
|
+
serversByAgent.set(item.id, servers);
|
|
58
|
+
}
|
|
59
|
+
if (!page.pageInfo?.hasNextPage) break;
|
|
60
|
+
after = page.pageInfo.endCursor;
|
|
61
|
+
}
|
|
62
|
+
for (const agent of agents) {
|
|
63
|
+
const servers = serversByAgent.get(agent.identifier);
|
|
64
|
+
if (servers?.length) agent.mcpServers = servers;
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function listMcpServerTools(server) {
|
|
70
|
+
try {
|
|
71
|
+
const res = await fetch(server.url, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: {
|
|
74
|
+
"Content-Type": "application/json",
|
|
75
|
+
Accept: "application/json, text/event-stream"
|
|
76
|
+
},
|
|
77
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list", params: {} })
|
|
78
|
+
});
|
|
79
|
+
if (!res.ok) return [];
|
|
80
|
+
const data = await res.json();
|
|
81
|
+
const tools = data?.result?.tools;
|
|
82
|
+
if (!Array.isArray(tools)) return [];
|
|
83
|
+
return tools.map((t) => ({ name: t.name, description: t.description ?? "" }));
|
|
84
|
+
} catch {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function discoverMcpToolNames(agents) {
|
|
89
|
+
const result = /* @__PURE__ */ new Map();
|
|
90
|
+
const seen = /* @__PURE__ */ new Set();
|
|
91
|
+
for (const agent of agents) {
|
|
92
|
+
if (!agent.mcpServers?.length) continue;
|
|
93
|
+
const agentToolNames = [];
|
|
94
|
+
for (const server of agent.mcpServers) {
|
|
95
|
+
if (seen.has(server.id)) {
|
|
96
|
+
const key2 = server.name.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
97
|
+
const cached = result.get(server.id);
|
|
98
|
+
if (cached) agentToolNames.push(...cached.map((t) => `${key2}_${t}`));
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
seen.add(server.id);
|
|
102
|
+
const tools = await listMcpServerTools(server);
|
|
103
|
+
const toolNames = tools.map((t) => t.name);
|
|
104
|
+
result.set(server.id, toolNames);
|
|
105
|
+
const key = server.name.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
106
|
+
agentToolNames.push(...toolNames.map((t) => `${key}_${t}`));
|
|
107
|
+
}
|
|
108
|
+
result.set(agent.identifier, agentToolNames);
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
gql,
|
|
115
|
+
fetchMcpServers,
|
|
116
|
+
listMcpServerTools,
|
|
117
|
+
discoverMcpToolNames
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=chunk-DPR6OUYG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/graphql.ts","../src/mcp-servers.ts"],"sourcesContent":["export async function gql(\n instanceUrl: string,\n token: string,\n query: string,\n variables: Record<string, unknown>\n): Promise<any> {\n const res = await fetch(`${instanceUrl.replace(/\\/$/, \"\")}/api/graphql`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${token}` },\n body: JSON.stringify({ query, variables }),\n });\n const json = (await res.json()) as any;\n if (json.errors?.length) throw new Error(json.errors[0].message);\n return json.data;\n}\n","import { gql } from \"./graphql\";\nimport type { CatalogAgent, McpServerInfo } from \"./types\";\n\nconst MCP_SERVERS_QUERY = `\nquery AiCatalogMcpServers($projectId: ProjectID!, $after: String) {\n aiCatalogConfiguredItems(first: 50, projectId: $projectId, itemTypes: [AGENT], after: $after) {\n pageInfo { hasNextPage endCursor }\n nodes {\n item {\n id\n name\n latestVersion {\n ... on AiCatalogAgentVersion {\n mcpServers {\n nodes { id name description url transport authType currentUserConnected }\n }\n }\n }\n }\n }\n }\n}`;\n\nexport async function fetchMcpServers(\n instanceUrl: string,\n token: string,\n projectId: string,\n agents: CatalogAgent[]\n): Promise<void> {\n try {\n let after: string | null = null;\n const serversByAgent = new Map<string, McpServerInfo[]>();\n\n for (;;) {\n const data = await gql(instanceUrl, token, MCP_SERVERS_QUERY, {\n projectId,\n ...(after ? { after } : {}),\n });\n const page = data?.aiCatalogConfiguredItems;\n if (!page) break;\n\n for (const node of page.nodes ?? []) {\n const item = node.item;\n const version = item?.latestVersion;\n if (!version?.mcpServers?.nodes?.length) continue;\n const servers: McpServerInfo[] = version.mcpServers.nodes.map((s: any) => ({\n id: s.id,\n name: s.name,\n description: s.description ?? \"\",\n url: s.url,\n transport: s.transport,\n authType: s.authType,\n currentUserConnected: !!s.currentUserConnected,\n }));\n serversByAgent.set(item.id, servers);\n }\n\n if (!page.pageInfo?.hasNextPage) break;\n after = page.pageInfo.endCursor;\n }\n\n for (const agent of agents) {\n const servers = serversByAgent.get(agent.identifier);\n if (servers?.length) agent.mcpServers = servers;\n }\n } catch {\n // MCP servers query not available — silently skip\n }\n}\n\nexport async function listMcpServerTools(\n server: McpServerInfo\n): Promise<Array<{ name: string; description: string }>> {\n try {\n const res = await fetch(server.url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json, text/event-stream\",\n },\n body: JSON.stringify({ jsonrpc: \"2.0\", id: 1, method: \"tools/list\", params: {} }),\n });\n if (!res.ok) return [];\n const data = await res.json();\n const tools = data?.result?.tools;\n if (!Array.isArray(tools)) return [];\n return tools.map((t: any) => ({ name: t.name, description: t.description ?? \"\" }));\n } catch {\n return [];\n }\n}\n\nexport async function discoverMcpToolNames(agents: CatalogAgent[]): Promise<Map<string, string[]>> {\n const result = new Map<string, string[]>();\n const seen = new Set<string>();\n\n for (const agent of agents) {\n if (!agent.mcpServers?.length) continue;\n const agentToolNames: string[] = [];\n for (const server of agent.mcpServers) {\n if (seen.has(server.id)) {\n const key = server.name.toLowerCase().replace(/[^a-z0-9]+/g, \"_\");\n const cached = result.get(server.id);\n if (cached) agentToolNames.push(...cached.map((t) => `${key}_${t}`));\n continue;\n }\n seen.add(server.id);\n const tools = await listMcpServerTools(server);\n const toolNames = tools.map((t) => t.name);\n result.set(server.id, toolNames);\n const key = server.name.toLowerCase().replace(/[^a-z0-9]+/g, \"_\");\n agentToolNames.push(...toolNames.map((t) => `${key}_${t}`));\n }\n result.set(agent.identifier, agentToolNames);\n }\n return result;\n}\n"],"mappings":"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","names":["key"]}
|