opencode-gitlab-dap 1.16.4 → 1.16.6
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 +138 -22
- package/dist/index.cjs +33 -124
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +33 -124
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OpenCode GitLab Duo Agent Platform Plugin
|
|
2
2
|
|
|
3
3
|
An [opencode](https://opencode.ai) plugin for the [GitLab Duo Agent Platform (DAP)](https://docs.gitlab.com/user/duo_agent_platform/).
|
|
4
4
|
Discovers agents and flows from the GitLab AI Catalog, injects them into opencode,
|
|
@@ -39,6 +39,12 @@ On startup, the plugin discovers all agents and flows enabled for your project:
|
|
|
39
39
|
- Agents appear in `/agents` dialog and Tab agent picker
|
|
40
40
|
- Flows appear in the `@` mention menu as subagents
|
|
41
41
|
|
|
42
|
+
**Use cases:**
|
|
43
|
+
|
|
44
|
+
- **Switch to a specialized agent mid-conversation** — press Tab or open `/agents` to pick a Data Analyst, Security Analyst, or any custom agent enabled for your project
|
|
45
|
+
- **Discover what automation is available** — browse the `@` menu to see all flows your project has access to, including both foundational and custom flows
|
|
46
|
+
- **Use foundational agents for specialized tasks** — switch to the Planner agent for project planning, or the Security Analyst for vulnerability assessment, without any setup
|
|
47
|
+
|
|
42
48
|
### Flow Execution via `@` Menu
|
|
43
49
|
|
|
44
50
|
Type `@Flow Name` followed by your goal to execute a flow:
|
|
@@ -56,6 +62,14 @@ The plugin:
|
|
|
56
62
|
5. Executes the flow via `POST /api/v4/ai/duo_workflows/workflows`
|
|
57
63
|
6. Monitors workflow status and reports the workflow URL for tracking in GitLab UI
|
|
58
64
|
|
|
65
|
+
**Use cases:**
|
|
66
|
+
|
|
67
|
+
- **Trigger a code review** — `@Code Review 🦊 review MR !42` to get automated code review feedback on a specific merge request
|
|
68
|
+
- **Fix a broken pipeline** — `@Fix CI/CD Pipeline diagnose and fix the failing pipeline on branch feature/auth` to get automated pipeline diagnosis
|
|
69
|
+
- **Resolve a security vulnerability** — `@Security Vulnerability Fix fix vulnerability #123` to get an automated fix generated and pushed as an MR
|
|
70
|
+
- **Generate test coverage** — `@Auto Fix generate tests for the UserService class` to get unit tests created automatically
|
|
71
|
+
- **Natural language goal resolution** — describe what you want in plain English and the plugin resolves it to the exact parameter the flow needs (e.g., "MR !12" → IID `12`)
|
|
72
|
+
|
|
59
73
|
### Parallel Multi-Flow Dispatch
|
|
60
74
|
|
|
61
75
|
Mention multiple flows in a single message to execute them simultaneously:
|
|
@@ -73,6 +87,13 @@ When multiple flows are mentioned:
|
|
|
73
87
|
|
|
74
88
|
The `experimental.chat.system.transform` hook injects system prompt guidelines that instruct the main agent on how to handle multi-flow and batch dispatch scenarios.
|
|
75
89
|
|
|
90
|
+
**Use cases:**
|
|
91
|
+
|
|
92
|
+
- **Run security and code review simultaneously** — `@AppSec Security MR Reviewer @Code Review 🦊 review MR !42` to get both security analysis and code quality feedback in parallel
|
|
93
|
+
- **Batch review all open MRs** — `@Code Review 🦊 review all open MRs` to dispatch one flow execution per open MR, all running concurrently
|
|
94
|
+
- **Multi-flow batch operations** — combine multiple flows with "for each" semantics: the plugin lists all matching resources, then dispatches N flows × M resources in parallel
|
|
95
|
+
- **Reduce wait time with concurrency** — instead of running flows one by one, mention them all at once for simultaneous execution with a single summary at the end
|
|
96
|
+
|
|
76
97
|
### Custom Agent Creation
|
|
77
98
|
|
|
78
99
|
Create custom agents interactively:
|
|
@@ -91,6 +112,14 @@ The plugin guides you through an interactive workflow:
|
|
|
91
112
|
|
|
92
113
|
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
114
|
|
|
115
|
+
**Use cases:**
|
|
116
|
+
|
|
117
|
+
- **Build a domain-specific assistant** — create an agent with a system prompt tuned to your team's domain (e.g., payments, infrastructure, data pipelines) and the relevant tools
|
|
118
|
+
- **Create a creative writing agent** — build an agent that responds in a specific format (poems, haiku, bullet points) for fun or specialized output needs
|
|
119
|
+
- **Set up a security-focused reviewer** — create an agent with security scanning tools and a system prompt that emphasizes OWASP, CVE checks, and secure coding practices
|
|
120
|
+
- **Connect an agent to external services** — assign MCP servers to an agent so it can interact with third-party APIs and services during conversations
|
|
121
|
+
- **Share agents across your organization** — create a public agent in the AI Catalog that any project in your GitLab instance can enable and use
|
|
122
|
+
|
|
94
123
|
### Custom Flow Creation
|
|
95
124
|
|
|
96
125
|
Design and create custom flows interactively:
|
|
@@ -110,40 +139,91 @@ The plugin provides a multi-round design workflow:
|
|
|
110
139
|
|
|
111
140
|
The vendored `flow_v2.json` schema from GitLab Rails powers client-side validation using `ajv`, catching errors before hitting the API.
|
|
112
141
|
|
|
142
|
+
**Use cases:**
|
|
143
|
+
|
|
144
|
+
- **Automate MR summarization** — create a flow that fetches MR diffs, analyzes the changes, and posts a summary comment with key observations
|
|
145
|
+
- **Build a deployment checklist flow** — design a multi-step flow that verifies prerequisites, runs pre-deployment checks, and triggers deployment
|
|
146
|
+
- **Create a release notes generator** — build a flow that collects commits since the last tag, categorizes changes, and generates formatted release notes
|
|
147
|
+
- **Design a conditional flow with routing** — create a flow with branching logic (e.g., different review steps based on file types changed in the MR)
|
|
148
|
+
- **Validate flow definitions before submission** — catch YAML schema errors locally before hitting the GitLab API, reducing back-and-forth during flow development
|
|
149
|
+
- **Iterate on flow design interactively** — use the multi-round design workflow to refine component architecture and step definitions with guided assistance
|
|
150
|
+
|
|
113
151
|
### 35 Tools
|
|
114
152
|
|
|
115
153
|
#### DAP Tools (20)
|
|
116
154
|
|
|
155
|
+
##### Flow Execution
|
|
156
|
+
|
|
157
|
+
| Tool | Description |
|
|
158
|
+
| ----------------------------- | ------------------------------------------ |
|
|
159
|
+
| `gitlab_execute_project_flow` | Execute a flow via DWS REST API |
|
|
160
|
+
| `gitlab_get_flow_definition` | Get flow YAML config (inputs, components) |
|
|
161
|
+
| `gitlab_get_workflow_status` | Monitor workflow execution status and logs |
|
|
162
|
+
|
|
163
|
+
**Use cases:**
|
|
164
|
+
|
|
165
|
+
- **Run a code review flow on a merge request** — trigger the "Code Review" flow for a specific MR to get automated review feedback posted as comments
|
|
166
|
+
- **Fix a failing CI/CD pipeline** — execute the "Fix CI/CD Pipeline" flow with the pipeline URL to get automated diagnosis and fix suggestions
|
|
167
|
+
- **Batch-execute flows across multiple MRs** — run a security review flow on all open MRs simultaneously by listing resources first, then dispatching N flows in parallel
|
|
168
|
+
- **Inspect flow requirements before execution** — use `gitlab_get_flow_definition` to check what inputs a flow expects (e.g., `merge_request_iid`, `pipeline_url`, `vulnerability_id`) before triggering it
|
|
169
|
+
- **Monitor long-running workflows** — poll `gitlab_get_workflow_status` to track progress, retrieve the agent conversation log, and get the GitLab UI URL for the workflow session
|
|
170
|
+
- **Debug flow execution failures** — check workflow status messages to understand where a flow failed and what the agent attempted
|
|
171
|
+
|
|
172
|
+
##### Catalog CRUD
|
|
173
|
+
|
|
117
174
|
| Tool | Description |
|
|
118
175
|
| --------------------------------- | ------------------------------------------------------- |
|
|
119
|
-
| `gitlab_list_agents` | Search agents in the global AI Catalog |
|
|
120
|
-
| `gitlab_get_agent` | Get agent details by ID |
|
|
121
|
-
| `gitlab_list_project_agents` | List agents enabled for a project |
|
|
122
|
-
| `gitlab_enable_project_agent` | Enable an agent in a project |
|
|
123
|
-
| `gitlab_disable_project_agent` | Disable an agent in a project |
|
|
124
176
|
| `gitlab_create_agent` | Create a custom agent (interactive, confirmation-gated) |
|
|
125
177
|
| `gitlab_update_agent` | Update an existing custom agent |
|
|
126
178
|
| `gitlab_list_builtin_tools` | List available built-in tools for agent/flow config |
|
|
127
179
|
| `gitlab_design_flow` | Interactive flow design + YAML validation |
|
|
128
180
|
| `gitlab_create_flow` | Create a custom flow (confirmation-gated) |
|
|
129
181
|
| `gitlab_update_flow` | Update an existing custom flow |
|
|
130
|
-
| `gitlab_list_flows` | Search flows in the global AI Catalog |
|
|
131
|
-
| `gitlab_get_flow` | Get flow details by ID |
|
|
132
|
-
| `gitlab_list_project_flows` | List flows enabled for a project |
|
|
133
|
-
| `gitlab_enable_project_flow` | Enable a flow in a project |
|
|
134
|
-
| `gitlab_disable_project_flow` | Disable a flow in a project |
|
|
135
|
-
| `gitlab_execute_project_flow` | Execute a flow via DWS REST API |
|
|
136
|
-
| `gitlab_get_flow_definition` | Get flow YAML config (inputs, components) |
|
|
137
|
-
| `gitlab_get_workflow_status` | Monitor workflow execution status and logs |
|
|
138
182
|
| `gitlab_list_project_mcp_servers` | List MCP servers available for project agents |
|
|
139
183
|
|
|
140
|
-
|
|
184
|
+
**Use cases:**
|
|
185
|
+
|
|
186
|
+
- **Create a project-specific code reviewer agent** — interactively define a custom agent with a tailored system prompt, assign it the MR and code search tools, and connect it to your project's MCP servers
|
|
187
|
+
- **Build a team-specific incident responder agent** — create an agent with security scanning tools and a system prompt tuned to your team's runbooks and escalation procedures
|
|
188
|
+
- **Design a custom flow from scratch** — use `gitlab_design_flow` to get the YAML schema reference and examples, propose a component architecture, validate the YAML client-side, and submit it
|
|
189
|
+
- **Create a vulnerability triage flow** — design a multi-step flow that fetches vulnerability details, evaluates severity, and posts remediation guidance
|
|
190
|
+
- **Iterate on an agent's system prompt** — use `gitlab_update_agent` to refine the prompt after testing, bump the version, and release
|
|
191
|
+
- **Add MCP servers to an existing agent** — discover available MCP servers with `gitlab_list_project_mcp_servers`, then update the agent to include them
|
|
192
|
+
- **Validate flow YAML before submission** — catch schema errors locally with `gitlab_design_flow(action="validate")` before hitting the API
|
|
193
|
+
- **Discover available tools for agent configuration** — use `gitlab_list_builtin_tools` to see all tool categories (search, issues, MRs, epics, files, git, CI/CD, security, audit, planning, wiki)
|
|
194
|
+
|
|
195
|
+
##### Catalog Item Management
|
|
196
|
+
|
|
197
|
+
| Tool | Description |
|
|
198
|
+
| ------------------------------ | -------------------------------------- |
|
|
199
|
+
| `gitlab_list_agents` | Search agents in the global AI Catalog |
|
|
200
|
+
| `gitlab_get_agent` | Get agent details by ID |
|
|
201
|
+
| `gitlab_list_project_agents` | List agents enabled for a project |
|
|
202
|
+
| `gitlab_enable_project_agent` | Enable an agent in a project |
|
|
203
|
+
| `gitlab_disable_project_agent` | Disable an agent in a project |
|
|
204
|
+
| `gitlab_list_flows` | Search flows in the global AI Catalog |
|
|
205
|
+
| `gitlab_get_flow` | Get flow details by ID |
|
|
206
|
+
| `gitlab_list_project_flows` | List flows enabled for a project |
|
|
207
|
+
| `gitlab_enable_project_flow` | Enable a flow in a project |
|
|
208
|
+
| `gitlab_disable_project_flow` | Disable a flow in a project |
|
|
209
|
+
|
|
210
|
+
**Use cases:**
|
|
211
|
+
|
|
212
|
+
- **Browse the AI Catalog for useful agents** — search the global catalog by name or description to discover agents built by other teams or the community
|
|
213
|
+
- **Set up a new project with standard agents** — enable your organization's standard set of agents and flows on a newly created project
|
|
214
|
+
- **Audit which agents are enabled** — list all agents and flows enabled for a project to verify compliance with team standards or security policies
|
|
215
|
+
- **Enable a flow after creation** — after creating a custom flow, enable it on your project so it appears in the `@` mention menu
|
|
216
|
+
- **Disable a misbehaving flow** — temporarily disable a flow that is producing incorrect results while you debug and update its definition
|
|
217
|
+
- **Inspect agent details before enabling** — check an agent's creator, version, description, and permissions before adding it to your project
|
|
218
|
+
- **Find the consumer ID for flow execution** — list project flows to retrieve the consumer ID required by `gitlab_execute_project_flow`
|
|
219
|
+
|
|
220
|
+
#### Project Knowledge Tools (15)
|
|
141
221
|
|
|
142
222
|
Persistent project memory and reusable skills. Knowledge is stored in GitLab project/group wikis but tools abstract the storage — the agent works with facts, decisions, patterns, and skills.
|
|
143
223
|
|
|
144
224
|
Say **"bootstrap project memory"** to automatically inspect a project and build its knowledge base. If memory already exists, it does a smart refresh — updating stale facts and archiving outdated entries.
|
|
145
225
|
|
|
146
|
-
##### Memory Tools
|
|
226
|
+
##### Memory Tools (7)
|
|
147
227
|
|
|
148
228
|
| Tool | Description |
|
|
149
229
|
| --------------------------- | --------------------------------------------------------------- |
|
|
@@ -155,7 +235,24 @@ Say **"bootstrap project memory"** to automatically inspect a project and build
|
|
|
155
235
|
| `gitlab_memory_recall` | Search project knowledge for relevant information |
|
|
156
236
|
| `gitlab_memory_log_session` | Log a session summary with learnings |
|
|
157
237
|
|
|
158
|
-
|
|
238
|
+
**Use cases:**
|
|
239
|
+
|
|
240
|
+
- **Bootstrap project knowledge from scratch** — run "bootstrap project memory" to inspect README, issues, MRs, pipelines, and team members, then record everything as structured facts, architecture, conventions, and people entries
|
|
241
|
+
- **Preserve context across sessions** — record facts about tech stack, dependencies, and deployment targets so future sessions start with full context instead of re-exploring
|
|
242
|
+
- **Document architectural decisions with reasoning** — record why the team chose PostgreSQL over MySQL, or React over Vue, with full rationale for future reference
|
|
243
|
+
- **Track recurring patterns** — record observations like "CI pipelines fail on Mondays due to cache expiry" or "test suite is flaky on the `payments` module" so they aren't rediscovered each time
|
|
244
|
+
- **Maintain a living architecture document** — record and update system design, module structure, data flow, and key abstractions as the codebase evolves
|
|
245
|
+
- **Codify coding conventions** — record naming patterns, commit message formats, review processes, and coding standards so the agent follows them consistently
|
|
246
|
+
- **Document known issues and workarounds** — record troubleshooting steps for common errors so the agent can suggest solutions without investigation
|
|
247
|
+
- **Track team roles and ownership** — record who owns which modules, who to contact for specific subsystems, and team members' areas of expertise
|
|
248
|
+
- **Record implementation plans** — document feature designs, task breakdowns, and roadmap items that persist across sessions
|
|
249
|
+
- **Refresh stale knowledge** — use `gitlab_memory_update` to correct outdated facts (e.g., updated issue counts, new team members, changed dependencies)
|
|
250
|
+
- **Archive superseded decisions** — when a decision is reversed or a fact becomes obsolete, archive it with a reason so there's a historical record
|
|
251
|
+
- **Periodic memory housekeeping** — use `gitlab_memory_consolidate` to identify stale, duplicate, or contradictory records and clean them up
|
|
252
|
+
- **Search before investigating** — use `gitlab_memory_recall` to check if something is already known about a topic before spending time exploring the codebase
|
|
253
|
+
- **Log session summaries** — at the end of a significant work session, record what was accomplished, what was learned, and suggestions for next steps
|
|
254
|
+
|
|
255
|
+
##### Skill Tools (8)
|
|
159
256
|
|
|
160
257
|
| Tool | Description |
|
|
161
258
|
| ----------------------- | ------------------------------------------------------------- |
|
|
@@ -168,17 +265,30 @@ Say **"bootstrap project memory"** to automatically inspect a project and build
|
|
|
168
265
|
| `gitlab_skill_setup` | Extract skill to `.agents/skills/<name>/` for local execution |
|
|
169
266
|
| `gitlab_skill_delete` | Delete skill (wiki pages + snippet + index entry) |
|
|
170
267
|
|
|
171
|
-
**
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
268
|
+
**Use cases:**
|
|
269
|
+
|
|
270
|
+
- **Create a reusable incident retrospective procedure** — save a step-by-step skill that guides the agent through conducting incident retros with checklists and templates
|
|
271
|
+
- **Share skills across projects via group wiki** — save a skill at the group level so all projects in the group can discover and install it
|
|
272
|
+
- **Discover team-shared skills** — search the group wiki and skills.sh registry to find skills other teams have built (e.g., Helm rollback procedures, database migration checklists)
|
|
273
|
+
- **Install a skill from the community** — install a skill from skills.sh into your project wiki with one command, bringing in both documentation and executable scripts
|
|
274
|
+
- **Draft and iterate on a skill before publishing** — save a skill as a draft, test it in practice, refine the instructions, then promote it to published when ready
|
|
275
|
+
- **Bundle executable scripts with a skill** — save Python, JavaScript, or Shell scripts alongside skill instructions using hybrid storage (wiki for markdown, project snippet for executables)
|
|
276
|
+
- **Extract skills for local execution** — use `gitlab_skill_setup` to download a skill's scripts to `.agents/skills/<name>/` so opencode auto-discovers and uses them locally
|
|
277
|
+
- **Maintain a skill index for fast discovery** — the index at `agents/skills/index` is auto-rebuilt on every `gitlab_skill_list` call, keeping it in sync with actual skill pages
|
|
278
|
+
- **Promote a proven draft skill** — after validating a draft skill works well, promote it to published to make it available to all users of the project
|
|
279
|
+
- **Remove an obsolete skill** — delete a skill and its associated snippet, automatically cleaning up the index entry
|
|
280
|
+
- **Audit available skills** — list all published and draft skills with their descriptions to understand what automation is available for the project
|
|
176
281
|
|
|
177
282
|
### Dynamic Refresh
|
|
178
283
|
|
|
179
284
|
After enabling or disabling an agent/flow, the plugin automatically refreshes the
|
|
180
285
|
agent list. Restart opencode to update the `@` menu.
|
|
181
286
|
|
|
287
|
+
**Use cases:**
|
|
288
|
+
|
|
289
|
+
- **Enable an agent and use it immediately** — after enabling a new agent or flow, the plugin refreshes the catalog cache so tools immediately reflect the change without restarting
|
|
290
|
+
- **Disable a broken flow mid-session** — disable a flow that is misbehaving and the agent list updates in real time for subsequent tool calls
|
|
291
|
+
|
|
182
292
|
### Vendored Foundational Flow Configs
|
|
183
293
|
|
|
184
294
|
Foundational flow definitions (from `gitlab-org/modelops/applied-ml/code-suggestions/ai-assist`)
|
|
@@ -188,6 +298,12 @@ foundational flows whose configs are not available via the GitLab API.
|
|
|
188
298
|
The `flow_v2.json` JSON schema is also vendored from GitLab Rails and bundled
|
|
189
299
|
inline for client-side YAML validation via `ajv`.
|
|
190
300
|
|
|
301
|
+
**Use cases:**
|
|
302
|
+
|
|
303
|
+
- **Execute foundational flows without API calls** — flow input schemas for built-in flows are bundled in the plugin, enabling instant flow definition lookup without network requests
|
|
304
|
+
- **Validate custom flow YAML offline** — the vendored `flow_v2.json` schema enables client-side validation via `ajv` without requiring a GitLab API connection
|
|
305
|
+
- **Stay up to date with new flow versions** — run `npm run vendor` to pull the latest foundational flow definitions when DWS releases updates
|
|
306
|
+
|
|
191
307
|
## Agent Types
|
|
192
308
|
|
|
193
309
|
### Foundational Agents
|
package/dist/index.cjs
CHANGED
|
@@ -3950,73 +3950,6 @@ ${e.content}`).join("\n\n---\n\n");
|
|
|
3950
3950
|
|
|
3951
3951
|
// src/tools/skill-tools.ts
|
|
3952
3952
|
var import_plugin6 = require("@opencode-ai/plugin");
|
|
3953
|
-
|
|
3954
|
-
// src/snippets.ts
|
|
3955
|
-
function snippetApi(instanceUrl, token, projectId) {
|
|
3956
|
-
const base = instanceUrl.replace(/\/$/, "");
|
|
3957
|
-
const encoded = typeof projectId === "number" ? projectId : encodeURIComponent(projectId);
|
|
3958
|
-
return {
|
|
3959
|
-
url: `${base}/api/v4/projects/${encoded}/snippets`,
|
|
3960
|
-
headers: {
|
|
3961
|
-
"Content-Type": "application/json",
|
|
3962
|
-
Authorization: `Bearer ${token}`
|
|
3963
|
-
}
|
|
3964
|
-
};
|
|
3965
|
-
}
|
|
3966
|
-
async function handleResponse2(res) {
|
|
3967
|
-
if (!res.ok) {
|
|
3968
|
-
const text = await res.text();
|
|
3969
|
-
throw new Error(`Snippet API error (${res.status}): ${text}`);
|
|
3970
|
-
}
|
|
3971
|
-
return res.json();
|
|
3972
|
-
}
|
|
3973
|
-
async function createProjectSnippet(instanceUrl, token, projectId, title, description, files, visibility = "private") {
|
|
3974
|
-
const { url, headers } = snippetApi(instanceUrl, token, projectId);
|
|
3975
|
-
const res = await fetch(url, {
|
|
3976
|
-
method: "POST",
|
|
3977
|
-
headers,
|
|
3978
|
-
body: JSON.stringify({ title, description, visibility, files })
|
|
3979
|
-
});
|
|
3980
|
-
return handleResponse2(res);
|
|
3981
|
-
}
|
|
3982
|
-
async function deleteProjectSnippet(instanceUrl, token, projectId, snippetId) {
|
|
3983
|
-
const { url, headers } = snippetApi(instanceUrl, token, projectId);
|
|
3984
|
-
const res = await fetch(`${url}/${snippetId}`, { method: "DELETE", headers });
|
|
3985
|
-
if (!res.ok) {
|
|
3986
|
-
const text = await res.text();
|
|
3987
|
-
throw new Error(`Snippet API error (${res.status}): ${text}`);
|
|
3988
|
-
}
|
|
3989
|
-
}
|
|
3990
|
-
async function getSnippetFileRaw(instanceUrl, token, projectId, snippetId, filePath, ref = "main") {
|
|
3991
|
-
const base = instanceUrl.replace(/\/$/, "");
|
|
3992
|
-
const encoded = typeof projectId === "number" ? projectId : encodeURIComponent(projectId);
|
|
3993
|
-
const encodedPath = encodeURIComponent(filePath);
|
|
3994
|
-
const url = `${base}/api/v4/projects/${encoded}/snippets/${snippetId}/files/${ref}/${encodedPath}/raw`;
|
|
3995
|
-
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
3996
|
-
if (!res.ok) {
|
|
3997
|
-
const text = await res.text();
|
|
3998
|
-
throw new Error(`Snippet file API error (${res.status}): ${text}`);
|
|
3999
|
-
}
|
|
4000
|
-
return res.text();
|
|
4001
|
-
}
|
|
4002
|
-
async function listSnippetFiles(instanceUrl, token, projectId, snippetId) {
|
|
4003
|
-
const base = instanceUrl.replace(/\/$/, "");
|
|
4004
|
-
const encoded = typeof projectId === "number" ? projectId : encodeURIComponent(projectId);
|
|
4005
|
-
const url = `${base}/api/v4/projects/${encoded}/snippets/${snippetId}`;
|
|
4006
|
-
const res = await fetch(url, {
|
|
4007
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }
|
|
4008
|
-
});
|
|
4009
|
-
const snippet = await handleResponse2(res);
|
|
4010
|
-
if (snippet.files) {
|
|
4011
|
-
return snippet.files.map((f) => ({ path: f.path, raw_url: f.raw_url }));
|
|
4012
|
-
}
|
|
4013
|
-
if (snippet.file_name) {
|
|
4014
|
-
return [{ path: snippet.file_name, raw_url: snippet.raw_url }];
|
|
4015
|
-
}
|
|
4016
|
-
return [];
|
|
4017
|
-
}
|
|
4018
|
-
|
|
4019
|
-
// src/tools/skill-tools.ts
|
|
4020
3953
|
var import_fs3 = require("fs");
|
|
4021
3954
|
var import_path3 = require("path");
|
|
4022
3955
|
|
|
@@ -4042,14 +3975,12 @@ function parseFrontmatter(content) {
|
|
|
4042
3975
|
if (k === "name") meta.name = val;
|
|
4043
3976
|
else if (k === "description") meta.description = val;
|
|
4044
3977
|
else if (k === "source") meta.source = val;
|
|
4045
|
-
else if (k === "snippet") meta.snippetId = parseInt(val, 10) || void 0;
|
|
4046
3978
|
}
|
|
4047
3979
|
return { meta, body: match[2] };
|
|
4048
3980
|
}
|
|
4049
3981
|
function formatFrontmatter(meta, body) {
|
|
4050
3982
|
const lines = ["---", `name: ${meta.name}`, `description: ${meta.description}`];
|
|
4051
3983
|
if (meta.source) lines.push(`source: ${meta.source}`);
|
|
4052
|
-
if (meta.snippetId) lines.push(`snippet: ${meta.snippetId}`);
|
|
4053
3984
|
lines.push("---", "");
|
|
4054
3985
|
return lines.join("\n") + body;
|
|
4055
3986
|
}
|
|
@@ -4207,11 +4138,12 @@ async function listSkills(instanceUrl, token, scope, id, prefix) {
|
|
|
4207
4138
|
const name = extractSkillNameFromSlug(page.slug, prefix);
|
|
4208
4139
|
if (!name || !page.content) continue;
|
|
4209
4140
|
const { meta } = parseFrontmatter(page.content);
|
|
4141
|
+
const hasBundle = pages.some((p) => p.slug === `${prefix}/${name}/bundle`);
|
|
4210
4142
|
skills.push({
|
|
4211
4143
|
name: meta.name ?? name,
|
|
4212
4144
|
description: meta.description ?? "",
|
|
4213
4145
|
source: meta.source,
|
|
4214
|
-
|
|
4146
|
+
hasScripts: hasBundle,
|
|
4215
4147
|
draft: prefix === DRAFTS_PREFIX
|
|
4216
4148
|
});
|
|
4217
4149
|
}
|
|
@@ -4277,7 +4209,7 @@ function makeSkillTools(ctx) {
|
|
|
4277
4209
|
id,
|
|
4278
4210
|
`${prefix}/${args.name}/SKILL`
|
|
4279
4211
|
);
|
|
4280
|
-
const {
|
|
4212
|
+
const { body } = parseFrontmatter(page.content);
|
|
4281
4213
|
const isDraft = prefix === DRAFTS_PREFIX;
|
|
4282
4214
|
const pages = await listWikiPages(auth.instanceUrl, auth.token, scope, id);
|
|
4283
4215
|
const skillPrefix = `${prefix}/${args.name}/`;
|
|
@@ -4293,10 +4225,11 @@ ${body}` : body;
|
|
|
4293
4225
|
---
|
|
4294
4226
|
Available references: ${refs.join(", ")}`;
|
|
4295
4227
|
}
|
|
4296
|
-
|
|
4228
|
+
const hasBundle = refs.some((r) => r === "bundle");
|
|
4229
|
+
if (hasBundle) {
|
|
4297
4230
|
result += `
|
|
4298
4231
|
|
|
4299
|
-
This skill has executable scripts
|
|
4232
|
+
This skill has executable scripts.`;
|
|
4300
4233
|
result += `
|
|
4301
4234
|
Run \`gitlab_skill_setup(name="${args.name}")\` to extract them locally.`;
|
|
4302
4235
|
}
|
|
@@ -4493,25 +4426,12 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4493
4426
|
try {
|
|
4494
4427
|
const mdFiles = downloaded.files.filter((f) => isMarkdownFile(f.path));
|
|
4495
4428
|
const scriptFiles = downloaded.files.filter((f) => !isMarkdownFile(f.path));
|
|
4496
|
-
|
|
4497
|
-
if (scriptFiles.length > 0) {
|
|
4498
|
-
const snippet = await createProjectSnippet(
|
|
4499
|
-
auth.instanceUrl,
|
|
4500
|
-
auth.token,
|
|
4501
|
-
args.project_id,
|
|
4502
|
-
`skill:${downloaded.name}`,
|
|
4503
|
-
`Scripts for skill "${downloaded.name}"`,
|
|
4504
|
-
[{ file_path: `${downloaded.name}.bundle`, content: packFiles(scriptFiles) }],
|
|
4505
|
-
"private"
|
|
4506
|
-
);
|
|
4507
|
-
snippetId = snippet.id;
|
|
4508
|
-
}
|
|
4429
|
+
const hasBundle = scriptFiles.length > 0;
|
|
4509
4430
|
const skillBody = formatFrontmatter(
|
|
4510
4431
|
{
|
|
4511
4432
|
name: downloaded.name,
|
|
4512
4433
|
description: downloaded.description,
|
|
4513
|
-
source: `skills.sh:${args.name}
|
|
4514
|
-
snippetId
|
|
4434
|
+
source: `skills.sh:${args.name}`
|
|
4515
4435
|
},
|
|
4516
4436
|
downloaded.content.replace(/^---[\s\S]*?---\s*\n/, "")
|
|
4517
4437
|
);
|
|
@@ -4536,8 +4456,20 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4536
4456
|
);
|
|
4537
4457
|
wikiCount++;
|
|
4538
4458
|
}
|
|
4459
|
+
if (hasBundle) {
|
|
4460
|
+
const bundleSlug = `${targetPrefix}/${downloaded.name}/bundle`;
|
|
4461
|
+
await upsertPage(
|
|
4462
|
+
auth.instanceUrl,
|
|
4463
|
+
auth.token,
|
|
4464
|
+
projectScope.scope,
|
|
4465
|
+
projectScope.id,
|
|
4466
|
+
bundleSlug,
|
|
4467
|
+
packFiles(scriptFiles)
|
|
4468
|
+
);
|
|
4469
|
+
wikiCount++;
|
|
4470
|
+
}
|
|
4539
4471
|
const parts = [`${wikiCount} wiki page(s)`];
|
|
4540
|
-
if (
|
|
4472
|
+
if (hasBundle) parts.push(`${scriptFiles.length} bundled script(s)`);
|
|
4541
4473
|
return `Installed skill "${downloaded.name}" from skills.sh. ${parts.join(", ")}. Use gitlab_skill_setup to extract scripts.`;
|
|
4542
4474
|
} catch (err) {
|
|
4543
4475
|
return `Error installing from skills.sh: ${err.message}`;
|
|
@@ -4605,7 +4537,7 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4605
4537
|
if (!skillPage) {
|
|
4606
4538
|
return `Skill "${args.name}" not found. Use gitlab_skill_list to see available skills.`;
|
|
4607
4539
|
}
|
|
4608
|
-
const {
|
|
4540
|
+
const { body } = parseFrontmatter(skillPage.content);
|
|
4609
4541
|
(0, import_fs3.mkdirSync)(targetDir, { recursive: true });
|
|
4610
4542
|
(0, import_fs3.writeFileSync)((0, import_path3.join)(targetDir, "SKILL.md"), body);
|
|
4611
4543
|
const pages = await listWikiPages(
|
|
@@ -4630,23 +4562,17 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4630
4562
|
}
|
|
4631
4563
|
}
|
|
4632
4564
|
let scriptCount = 0;
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
auth.token,
|
|
4637
|
-
args.project_id,
|
|
4638
|
-
meta.snippetId
|
|
4639
|
-
);
|
|
4640
|
-
for (const bf of bundleFiles) {
|
|
4641
|
-
const raw = await getSnippetFileRaw(
|
|
4565
|
+
for (const prefix of [SKILLS_PREFIX, DRAFTS_PREFIX]) {
|
|
4566
|
+
try {
|
|
4567
|
+
const bundlePage = await getWikiPage(
|
|
4642
4568
|
auth.instanceUrl,
|
|
4643
4569
|
auth.token,
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4570
|
+
scope,
|
|
4571
|
+
id,
|
|
4572
|
+
`${prefix}/${args.name}/bundle`
|
|
4647
4573
|
);
|
|
4648
|
-
if (
|
|
4649
|
-
for (const file of unpackFiles(
|
|
4574
|
+
if (bundlePage.content) {
|
|
4575
|
+
for (const file of unpackFiles(bundlePage.content)) {
|
|
4650
4576
|
const filePath = (0, import_path3.join)(targetDir, file.path);
|
|
4651
4577
|
(0, import_fs3.mkdirSync)((0, import_path3.dirname)(filePath), { recursive: true });
|
|
4652
4578
|
(0, import_fs3.writeFileSync)(filePath, file.content);
|
|
@@ -4654,6 +4580,8 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4654
4580
|
scriptCount++;
|
|
4655
4581
|
}
|
|
4656
4582
|
}
|
|
4583
|
+
break;
|
|
4584
|
+
} catch {
|
|
4657
4585
|
}
|
|
4658
4586
|
}
|
|
4659
4587
|
ensureGitignore(workDir);
|
|
@@ -4686,29 +4614,10 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4686
4614
|
if (skillPages.length === 0) {
|
|
4687
4615
|
return `Skill "${args.name}" not found.`;
|
|
4688
4616
|
}
|
|
4689
|
-
const skillPage = await getWikiPage(
|
|
4690
|
-
auth.instanceUrl,
|
|
4691
|
-
auth.token,
|
|
4692
|
-
scope,
|
|
4693
|
-
id,
|
|
4694
|
-
`${prefix}/${args.name}/SKILL`
|
|
4695
|
-
);
|
|
4696
|
-
const { meta } = parseFrontmatter(skillPage.content);
|
|
4697
4617
|
for (const page of skillPages) {
|
|
4698
4618
|
await deleteWikiPage(auth.instanceUrl, auth.token, scope, id, page.slug);
|
|
4699
4619
|
}
|
|
4700
|
-
|
|
4701
|
-
try {
|
|
4702
|
-
await deleteProjectSnippet(
|
|
4703
|
-
auth.instanceUrl,
|
|
4704
|
-
auth.token,
|
|
4705
|
-
args.project_id,
|
|
4706
|
-
meta.snippetId
|
|
4707
|
-
);
|
|
4708
|
-
} catch {
|
|
4709
|
-
}
|
|
4710
|
-
}
|
|
4711
|
-
return `Deleted skill "${args.name}" (${skillPages.length} page(s)${meta.snippetId ? " + snippet" : ""}).`;
|
|
4620
|
+
return `Deleted skill "${args.name}" (${skillPages.length} page(s) removed).`;
|
|
4712
4621
|
} catch (err) {
|
|
4713
4622
|
return `Error deleting skill: ${err.message}`;
|
|
4714
4623
|
}
|