opencode-gitlab-dap 1.7.0 → 1.8.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/README.md +19 -23
- package/dist/index.cjs +290 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +290 -107
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ The plugin provides a multi-round design workflow:
|
|
|
110
110
|
|
|
111
111
|
The vendored `flow_v2.json` schema from GitLab Rails powers client-side validation using `ajv`, catching errors before hitting the API.
|
|
112
112
|
|
|
113
|
-
###
|
|
113
|
+
### 28 Tools
|
|
114
114
|
|
|
115
115
|
#### DAP Tools (20)
|
|
116
116
|
|
|
@@ -137,33 +137,29 @@ The vendored `flow_v2.json` schema from GitLab Rails powers client-side validati
|
|
|
137
137
|
| `gitlab_get_workflow_status` | Monitor workflow execution status and logs |
|
|
138
138
|
| `gitlab_list_project_mcp_servers` | List MCP servers available for project agents |
|
|
139
139
|
|
|
140
|
-
####
|
|
140
|
+
#### Project Knowledge Tools (8)
|
|
141
141
|
|
|
142
|
-
Persistent project memory and
|
|
142
|
+
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
143
|
|
|
144
|
-
|
|
145
|
-
| -------------------- | -------------------------------------------------------- |
|
|
146
|
-
| `gitlab_wiki_read` | Read a wiki page by slug |
|
|
147
|
-
| `gitlab_wiki_write` | Create or update a wiki page (upsert) |
|
|
148
|
-
| `gitlab_wiki_append` | Append text to a wiki page (create if missing) |
|
|
149
|
-
| `gitlab_wiki_list` | List wiki pages with optional prefix filter |
|
|
150
|
-
| `gitlab_wiki_delete` | Delete a wiki page |
|
|
151
|
-
| `gitlab_wiki_search` | Search wiki pages by content (server-side GitLab search) |
|
|
144
|
+
##### Memory Tools
|
|
152
145
|
|
|
153
|
-
|
|
146
|
+
| Tool | Description |
|
|
147
|
+
| --------------------------- | -------------------------------------------------------- |
|
|
148
|
+
| `gitlab_memory_load` | Load project memory (facts, decisions, patterns, or all) |
|
|
149
|
+
| `gitlab_memory_record` | Record a fact, decision, or pattern (auto-timestamped) |
|
|
150
|
+
| `gitlab_memory_recall` | Search project knowledge for relevant information |
|
|
151
|
+
| `gitlab_memory_log_session` | Log a session summary with learnings |
|
|
154
152
|
|
|
155
|
-
#####
|
|
153
|
+
##### Skill Tools
|
|
156
154
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
└── <name> # Individual skill pages
|
|
166
|
-
```
|
|
155
|
+
| Tool | Description |
|
|
156
|
+
| ---------------------- | ---------------------------------------------- |
|
|
157
|
+
| `gitlab_skill_list` | List available skills (and optionally drafts) |
|
|
158
|
+
| `gitlab_skill_load` | Load a skill (published first, then drafts) |
|
|
159
|
+
| `gitlab_skill_save` | Create or update a skill (supports draft flag) |
|
|
160
|
+
| `gitlab_skill_promote` | Promote a draft skill to published |
|
|
161
|
+
|
|
162
|
+
All tools support project scope (default) and group scope (`scope="groups"`).
|
|
167
163
|
|
|
168
164
|
### Dynamic Refresh
|
|
169
165
|
|
package/dist/index.cjs
CHANGED
|
@@ -2254,8 +2254,12 @@ prompts:
|
|
|
2254
2254
|
user: "Fix this vulnerability: {{vuln_data}}"
|
|
2255
2255
|
placeholder: history
|
|
2256
2256
|
\`\`\``;
|
|
2257
|
-
var
|
|
2258
|
-
|
|
2257
|
+
var PROJECT_KNOWLEDGE_HINT = `## Project Knowledge
|
|
2258
|
+
This project may have persistent memory and skills available via knowledge tools.
|
|
2259
|
+
Use gitlab_memory_load to check for existing project context (facts, decisions, patterns).
|
|
2260
|
+
Use gitlab_skill_list to discover available task-specific skills.
|
|
2261
|
+
When you learn something new about the project, use gitlab_memory_record to preserve it.
|
|
2262
|
+
When you complete a significant task, consider using gitlab_memory_log_session to log learnings.`;
|
|
2259
2263
|
|
|
2260
2264
|
// src/hooks.ts
|
|
2261
2265
|
function buildFlowSubagentPrompt(flow, projectPath, projectUrl) {
|
|
@@ -2415,7 +2419,7 @@ function makeSystemTransformHook(flowAgents, getAuthCache) {
|
|
|
2415
2419
|
}
|
|
2416
2420
|
if (getAuthCache()) {
|
|
2417
2421
|
output.system.push(AGENT_CREATION_GUIDELINES);
|
|
2418
|
-
output.system.push(
|
|
2422
|
+
output.system.push(PROJECT_KNOWLEDGE_HINT);
|
|
2419
2423
|
}
|
|
2420
2424
|
};
|
|
2421
2425
|
}
|
|
@@ -3524,7 +3528,7 @@ function makeMcpTools(getCachedAgents) {
|
|
|
3524
3528
|
};
|
|
3525
3529
|
}
|
|
3526
3530
|
|
|
3527
|
-
// src/tools/
|
|
3531
|
+
// src/tools/memory-tools.ts
|
|
3528
3532
|
var import_plugin5 = require("@opencode-ai/plugin");
|
|
3529
3533
|
|
|
3530
3534
|
// src/wiki.ts
|
|
@@ -3605,180 +3609,358 @@ async function searchWikiPages(instanceUrl, token, scope, id, query) {
|
|
|
3605
3609
|
return handleResponse(res);
|
|
3606
3610
|
}
|
|
3607
3611
|
|
|
3608
|
-
// src/tools/
|
|
3612
|
+
// src/tools/memory-tools.ts
|
|
3609
3613
|
var z5 = import_plugin5.tool.schema;
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3614
|
+
var MEMORY_SLUGS = {
|
|
3615
|
+
all: ["memory/facts", "memory/decisions", "memory/patterns"],
|
|
3616
|
+
facts: ["memory/facts"],
|
|
3617
|
+
decisions: ["memory/decisions"],
|
|
3618
|
+
patterns: ["memory/patterns"]
|
|
3619
|
+
};
|
|
3620
|
+
var RECORD_SLUG = {
|
|
3621
|
+
fact: "memory/facts",
|
|
3622
|
+
decision: "memory/decisions",
|
|
3623
|
+
pattern: "memory/patterns"
|
|
3624
|
+
};
|
|
3625
|
+
function today() {
|
|
3626
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3627
|
+
}
|
|
3628
|
+
function slugify(text) {
|
|
3629
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
3630
|
+
}
|
|
3631
|
+
async function safeRead(instanceUrl, token, scope, id, slug) {
|
|
3632
|
+
try {
|
|
3633
|
+
const page = await getWikiPage(instanceUrl, token, scope, id, slug);
|
|
3634
|
+
return page.content;
|
|
3635
|
+
} catch {
|
|
3636
|
+
return null;
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
async function appendToPage(instanceUrl, token, scope, id, slug, newContent, pageTitle) {
|
|
3640
|
+
const existing = await safeRead(instanceUrl, token, scope, id, slug);
|
|
3641
|
+
if (existing !== null) {
|
|
3642
|
+
await updateWikiPage(instanceUrl, token, scope, id, slug, existing + "\n\n" + newContent);
|
|
3643
|
+
} else {
|
|
3644
|
+
const title = pageTitle ?? slug.split("/").pop() ?? slug;
|
|
3645
|
+
await createWikiPage(instanceUrl, token, scope, id, title, newContent);
|
|
3616
3646
|
}
|
|
3647
|
+
}
|
|
3648
|
+
function resolveScope(args) {
|
|
3649
|
+
if (args.scope === "groups" && args.group_id) {
|
|
3650
|
+
return { scope: "groups", id: args.group_id };
|
|
3651
|
+
}
|
|
3652
|
+
return { scope: "projects", id: args.project_id };
|
|
3653
|
+
}
|
|
3654
|
+
function makeMemoryTools(ctx) {
|
|
3617
3655
|
return {
|
|
3618
|
-
|
|
3619
|
-
description: "
|
|
3656
|
+
gitlab_memory_load: (0, import_plugin5.tool)({
|
|
3657
|
+
description: "Load project memory to understand context, known facts, past decisions, and observed patterns.\nUse this at the start of complex tasks to check what is already known about the project.\nReturns accumulated knowledge from previous sessions.",
|
|
3658
|
+
args: {
|
|
3659
|
+
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3660
|
+
type: z5.enum(["all", "facts", "decisions", "patterns"]).optional().describe('Which memory to load: "all" (default), "facts", "decisions", or "patterns"'),
|
|
3661
|
+
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3662
|
+
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3663
|
+
},
|
|
3664
|
+
execute: async (args) => {
|
|
3665
|
+
const auth = ctx.ensureAuth();
|
|
3666
|
+
if (!auth) throw new Error("GitLab authentication not available");
|
|
3667
|
+
const { scope, id } = resolveScope(args);
|
|
3668
|
+
const memType = args.type ?? "all";
|
|
3669
|
+
const slugs = MEMORY_SLUGS[memType];
|
|
3670
|
+
if (!slugs) return `Unknown memory type: ${memType}`;
|
|
3671
|
+
const sections = [];
|
|
3672
|
+
for (const slug of slugs) {
|
|
3673
|
+
const content = await safeRead(auth.instanceUrl, auth.token, scope, id, slug);
|
|
3674
|
+
if (content) {
|
|
3675
|
+
const label = slug.split("/").pop();
|
|
3676
|
+
sections.push(`## ${label.charAt(0).toUpperCase() + label.slice(1)}
|
|
3677
|
+
|
|
3678
|
+
${content}`);
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
if (sections.length === 0)
|
|
3682
|
+
return "No project memory found. Use gitlab_memory_record to start building project knowledge.";
|
|
3683
|
+
return sections.join("\n\n---\n\n");
|
|
3684
|
+
}
|
|
3685
|
+
}),
|
|
3686
|
+
gitlab_memory_record: (0, import_plugin5.tool)({
|
|
3687
|
+
description: "Record a fact, decision, or pattern in project memory.\nFacts: stable truths about the project (e.g., deploy targets, tech stack, team conventions).\nDecisions: architectural choices with reasoning (why X was chosen over Y).\nPatterns: recurring observations that may evolve into skills over time.\nEntries are automatically timestamped.",
|
|
3620
3688
|
args: {
|
|
3621
|
-
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab")'),
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3689
|
+
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3690
|
+
type: z5.enum(["fact", "decision", "pattern"]).describe("Type of knowledge to record"),
|
|
3691
|
+
content: z5.string().describe("The knowledge to record (markdown)"),
|
|
3692
|
+
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3693
|
+
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3625
3694
|
},
|
|
3626
3695
|
execute: async (args) => {
|
|
3627
3696
|
const auth = ctx.ensureAuth();
|
|
3628
3697
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3629
3698
|
const { scope, id } = resolveScope(args);
|
|
3699
|
+
const slug = RECORD_SLUG[args.type];
|
|
3700
|
+
if (!slug) return `Unknown memory type: ${args.type}`;
|
|
3701
|
+
const date = today();
|
|
3702
|
+
let formatted;
|
|
3703
|
+
if (args.type === "decision") {
|
|
3704
|
+
const firstLine = args.content.split("\n")[0];
|
|
3705
|
+
const rest = args.content.split("\n").slice(1).join("\n").trim();
|
|
3706
|
+
formatted = `## ${date}: ${firstLine}${rest ? "\n" + rest : ""}`;
|
|
3707
|
+
} else if (args.type === "fact") {
|
|
3708
|
+
formatted = `## ${date}
|
|
3709
|
+
- ${args.content.replace(/\n/g, "\n- ")}`;
|
|
3710
|
+
} else {
|
|
3711
|
+
formatted = `## ${date}
|
|
3712
|
+
${args.content}`;
|
|
3713
|
+
}
|
|
3630
3714
|
try {
|
|
3631
|
-
|
|
3632
|
-
return
|
|
3715
|
+
await appendToPage(auth.instanceUrl, auth.token, scope, id, slug, formatted);
|
|
3716
|
+
return `Recorded ${args.type} in project memory.`;
|
|
3633
3717
|
} catch (err) {
|
|
3634
|
-
return `Error
|
|
3718
|
+
return `Error recording ${args.type}: ${err.message}`;
|
|
3635
3719
|
}
|
|
3636
3720
|
}
|
|
3637
3721
|
}),
|
|
3638
|
-
|
|
3639
|
-
description: "
|
|
3722
|
+
gitlab_memory_recall: (0, import_plugin5.tool)({
|
|
3723
|
+
description: "Search project knowledge for relevant information.\nSearches across all memory pages, session logs, and skills.\nUse this to check if something is already known before investigating.",
|
|
3640
3724
|
args: {
|
|
3641
|
-
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab")'),
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
scope: z5.enum(["projects", "groups"]).optional().describe('Scope: "projects" (default) or "groups"'),
|
|
3646
|
-
group_id: z5.string().optional().describe("Group path, required when scope is groups")
|
|
3725
|
+
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3726
|
+
query: z5.string().describe("What to search for"),
|
|
3727
|
+
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3728
|
+
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3647
3729
|
},
|
|
3648
3730
|
execute: async (args) => {
|
|
3649
3731
|
const auth = ctx.ensureAuth();
|
|
3650
3732
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3651
3733
|
const { scope, id } = resolveScope(args);
|
|
3652
3734
|
try {
|
|
3653
|
-
const
|
|
3735
|
+
const results = await searchWikiPages(
|
|
3654
3736
|
auth.instanceUrl,
|
|
3655
3737
|
auth.token,
|
|
3656
3738
|
scope,
|
|
3657
3739
|
id,
|
|
3658
|
-
args.
|
|
3659
|
-
args.content,
|
|
3660
|
-
args.title
|
|
3740
|
+
args.query
|
|
3661
3741
|
);
|
|
3662
|
-
return `
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
const
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
id,
|
|
3671
|
-
title,
|
|
3672
|
-
args.content
|
|
3673
|
-
);
|
|
3674
|
-
return `Created wiki page: ${page.slug}`;
|
|
3675
|
-
} catch (err) {
|
|
3676
|
-
return `Error writing wiki page: ${err.message}`;
|
|
3677
|
-
}
|
|
3742
|
+
if (results.length === 0) return `No knowledge found matching "${args.query}".`;
|
|
3743
|
+
return results.map((r) => {
|
|
3744
|
+
const category = r.path.startsWith("memory/") ? "memory" : r.path.startsWith("skills") ? "skill" : "other";
|
|
3745
|
+
const snippet = (r.data ?? "").slice(0, 200).replace(/\n/g, " ");
|
|
3746
|
+
return `[${category}] ${r.path}: ${snippet}`;
|
|
3747
|
+
}).join("\n\n");
|
|
3748
|
+
} catch (err) {
|
|
3749
|
+
return `Error searching knowledge: ${err.message}`;
|
|
3678
3750
|
}
|
|
3679
3751
|
}
|
|
3680
3752
|
}),
|
|
3681
|
-
|
|
3682
|
-
description: "
|
|
3753
|
+
gitlab_memory_log_session: (0, import_plugin5.tool)({
|
|
3754
|
+
description: "Log a session summary including what was accomplished, what was learned, and any suggestions.\nUse this at the end of significant work sessions to preserve context for future sessions.",
|
|
3683
3755
|
args: {
|
|
3684
|
-
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab")'),
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3756
|
+
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3757
|
+
title: z5.string().describe('Brief session title (e.g., "fix-ai-gateway-healthcheck")'),
|
|
3758
|
+
summary: z5.string().describe(
|
|
3759
|
+
"Session summary in markdown (what happened, what was learned, what went wrong)"
|
|
3760
|
+
),
|
|
3761
|
+
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3762
|
+
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3689
3763
|
},
|
|
3690
3764
|
execute: async (args) => {
|
|
3691
3765
|
const auth = ctx.ensureAuth();
|
|
3692
3766
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3693
3767
|
const { scope, id } = resolveScope(args);
|
|
3768
|
+
const date = today();
|
|
3769
|
+
const slug = `memory/sessions/${date}-${slugify(args.title)}`;
|
|
3770
|
+
const pageTitle = `${date}: ${args.title}`;
|
|
3694
3771
|
try {
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3772
|
+
await createWikiPage(auth.instanceUrl, auth.token, scope, id, pageTitle, args.summary);
|
|
3773
|
+
return `Session logged: ${slug}`;
|
|
3774
|
+
} catch (err) {
|
|
3775
|
+
if (err.message?.includes("already exists") || err.message?.includes("422")) {
|
|
3776
|
+
try {
|
|
3777
|
+
await updateWikiPage(
|
|
3778
|
+
auth.instanceUrl,
|
|
3779
|
+
auth.token,
|
|
3780
|
+
scope,
|
|
3781
|
+
id,
|
|
3782
|
+
slug,
|
|
3783
|
+
args.summary,
|
|
3784
|
+
pageTitle
|
|
3785
|
+
);
|
|
3786
|
+
return `Session log updated: ${slug}`;
|
|
3787
|
+
} catch (err2) {
|
|
3788
|
+
return `Error logging session: ${err2.message}`;
|
|
3789
|
+
}
|
|
3706
3790
|
}
|
|
3791
|
+
return `Error logging session: ${err.message}`;
|
|
3707
3792
|
}
|
|
3708
3793
|
}
|
|
3709
|
-
})
|
|
3710
|
-
|
|
3711
|
-
|
|
3794
|
+
})
|
|
3795
|
+
};
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
// src/tools/skill-tools.ts
|
|
3799
|
+
var import_plugin6 = require("@opencode-ai/plugin");
|
|
3800
|
+
var z6 = import_plugin6.tool.schema;
|
|
3801
|
+
function resolveScope2(args) {
|
|
3802
|
+
if (args.scope === "groups" && args.group_id) {
|
|
3803
|
+
return { scope: "groups", id: args.group_id };
|
|
3804
|
+
}
|
|
3805
|
+
return { scope: "projects", id: args.project_id };
|
|
3806
|
+
}
|
|
3807
|
+
function makeSkillTools(ctx) {
|
|
3808
|
+
return {
|
|
3809
|
+
gitlab_skill_list: (0, import_plugin6.tool)({
|
|
3810
|
+
description: "List available project skills and optionally draft skills.\nSkills define step-by-step procedures for common tasks (e.g., incident retros, debugging, deployments).",
|
|
3712
3811
|
args: {
|
|
3713
|
-
project_id:
|
|
3714
|
-
|
|
3715
|
-
scope:
|
|
3716
|
-
group_id:
|
|
3812
|
+
project_id: z6.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3813
|
+
include_drafts: z6.boolean().optional().describe("Also list draft skills (default: false)"),
|
|
3814
|
+
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3815
|
+
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3717
3816
|
},
|
|
3718
3817
|
execute: async (args) => {
|
|
3719
3818
|
const auth = ctx.ensureAuth();
|
|
3720
3819
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3721
|
-
const { scope, id } =
|
|
3820
|
+
const { scope, id } = resolveScope2(args);
|
|
3722
3821
|
try {
|
|
3723
3822
|
const pages = await listWikiPages(auth.instanceUrl, auth.token, scope, id);
|
|
3724
|
-
const
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3823
|
+
const skills = pages.filter((p) => p.slug.startsWith("skills/") && p.slug !== "skills/index").map((p) => ({ name: p.slug.replace("skills/", ""), title: p.title, draft: false }));
|
|
3824
|
+
let drafts = [];
|
|
3825
|
+
if (args.include_drafts) {
|
|
3826
|
+
drafts = pages.filter(
|
|
3827
|
+
(p) => p.slug.startsWith("skills-drafts/") && p.slug !== "skills-drafts/index"
|
|
3828
|
+
).map((p) => ({
|
|
3829
|
+
name: p.slug.replace("skills-drafts/", ""),
|
|
3830
|
+
title: p.title,
|
|
3831
|
+
draft: true
|
|
3832
|
+
}));
|
|
3833
|
+
}
|
|
3834
|
+
const all = [...skills, ...drafts];
|
|
3835
|
+
if (all.length === 0) return "No skills found. Use gitlab_skill_save to create one.";
|
|
3836
|
+
return JSON.stringify(all, null, 2);
|
|
3730
3837
|
} catch (err) {
|
|
3731
|
-
return `Error listing
|
|
3838
|
+
return `Error listing skills: ${err.message}`;
|
|
3732
3839
|
}
|
|
3733
3840
|
}
|
|
3734
3841
|
}),
|
|
3735
|
-
|
|
3736
|
-
description: "
|
|
3842
|
+
gitlab_skill_load: (0, import_plugin6.tool)({
|
|
3843
|
+
description: "Load a specific skill by name.\nSkills contain step-by-step instructions for common tasks.\nChecks published skills first, then falls back to draft skills.",
|
|
3737
3844
|
args: {
|
|
3738
|
-
project_id:
|
|
3739
|
-
|
|
3740
|
-
scope:
|
|
3741
|
-
group_id:
|
|
3845
|
+
project_id: z6.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3846
|
+
name: z6.string().describe('Skill name (e.g., "incident-retro", "helm-rollback")'),
|
|
3847
|
+
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3848
|
+
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3742
3849
|
},
|
|
3743
3850
|
execute: async (args) => {
|
|
3744
3851
|
const auth = ctx.ensureAuth();
|
|
3745
3852
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3746
|
-
const { scope, id } =
|
|
3853
|
+
const { scope, id } = resolveScope2(args);
|
|
3747
3854
|
try {
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3855
|
+
const page = await getWikiPage(
|
|
3856
|
+
auth.instanceUrl,
|
|
3857
|
+
auth.token,
|
|
3858
|
+
scope,
|
|
3859
|
+
id,
|
|
3860
|
+
`skills/${args.name}`
|
|
3861
|
+
);
|
|
3862
|
+
return page.content;
|
|
3863
|
+
} catch {
|
|
3864
|
+
try {
|
|
3865
|
+
const draft = await getWikiPage(
|
|
3866
|
+
auth.instanceUrl,
|
|
3867
|
+
auth.token,
|
|
3868
|
+
scope,
|
|
3869
|
+
id,
|
|
3870
|
+
`skills-drafts/${args.name}`
|
|
3871
|
+
);
|
|
3872
|
+
return `[DRAFT SKILL]
|
|
3873
|
+
|
|
3874
|
+
${draft.content}`;
|
|
3875
|
+
} catch {
|
|
3876
|
+
return `Skill "${args.name}" not found. Use gitlab_skill_list to see available skills.`;
|
|
3877
|
+
}
|
|
3752
3878
|
}
|
|
3753
3879
|
}
|
|
3754
3880
|
}),
|
|
3755
|
-
|
|
3756
|
-
description: "
|
|
3881
|
+
gitlab_skill_save: (0, import_plugin6.tool)({
|
|
3882
|
+
description: "Create or update a skill.\nSkills define step-by-step procedures for common tasks.\nUse draft=true for skills that haven't been proven yet.",
|
|
3757
3883
|
args: {
|
|
3758
|
-
project_id:
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3884
|
+
project_id: z6.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3885
|
+
name: z6.string().describe('Skill name (e.g., "incident-retro")'),
|
|
3886
|
+
content: z6.string().describe("Skill content in markdown"),
|
|
3887
|
+
draft: z6.boolean().optional().describe("Save as draft skill (default: false)"),
|
|
3888
|
+
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3889
|
+
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3762
3890
|
},
|
|
3763
3891
|
execute: async (args) => {
|
|
3764
3892
|
const auth = ctx.ensureAuth();
|
|
3765
3893
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3766
|
-
const { scope, id } =
|
|
3894
|
+
const { scope, id } = resolveScope2(args);
|
|
3895
|
+
const prefix = args.draft ? "skills-drafts" : "skills";
|
|
3896
|
+
const slug = `${prefix}/${args.name}`;
|
|
3767
3897
|
try {
|
|
3768
|
-
|
|
3898
|
+
await updateWikiPage(
|
|
3769
3899
|
auth.instanceUrl,
|
|
3770
3900
|
auth.token,
|
|
3771
3901
|
scope,
|
|
3772
3902
|
id,
|
|
3773
|
-
|
|
3903
|
+
slug,
|
|
3904
|
+
args.content,
|
|
3905
|
+
args.name
|
|
3774
3906
|
);
|
|
3775
|
-
return
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3907
|
+
return `Updated ${args.draft ? "draft " : ""}skill: ${args.name}`;
|
|
3908
|
+
} catch {
|
|
3909
|
+
try {
|
|
3910
|
+
await createWikiPage(auth.instanceUrl, auth.token, scope, id, args.name, args.content);
|
|
3911
|
+
return `Created ${args.draft ? "draft " : ""}skill: ${args.name}`;
|
|
3912
|
+
} catch (err) {
|
|
3913
|
+
return `Error saving skill: ${err.message}`;
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
}),
|
|
3918
|
+
gitlab_skill_promote: (0, import_plugin6.tool)({
|
|
3919
|
+
description: "Promote a draft skill to published.\nMoves the skill from the drafts directory to the published skills directory.",
|
|
3920
|
+
args: {
|
|
3921
|
+
project_id: z6.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3922
|
+
name: z6.string().describe("Skill name to promote"),
|
|
3923
|
+
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3924
|
+
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3925
|
+
},
|
|
3926
|
+
execute: async (args) => {
|
|
3927
|
+
const auth = ctx.ensureAuth();
|
|
3928
|
+
if (!auth) throw new Error("GitLab authentication not available");
|
|
3929
|
+
const { scope, id } = resolveScope2(args);
|
|
3930
|
+
try {
|
|
3931
|
+
const draft = await getWikiPage(
|
|
3932
|
+
auth.instanceUrl,
|
|
3933
|
+
auth.token,
|
|
3934
|
+
scope,
|
|
3935
|
+
id,
|
|
3936
|
+
`skills-drafts/${args.name}`
|
|
3779
3937
|
);
|
|
3938
|
+
try {
|
|
3939
|
+
await updateWikiPage(
|
|
3940
|
+
auth.instanceUrl,
|
|
3941
|
+
auth.token,
|
|
3942
|
+
scope,
|
|
3943
|
+
id,
|
|
3944
|
+
`skills/${args.name}`,
|
|
3945
|
+
draft.content,
|
|
3946
|
+
args.name
|
|
3947
|
+
);
|
|
3948
|
+
} catch {
|
|
3949
|
+
await createWikiPage(auth.instanceUrl, auth.token, scope, id, args.name, draft.content);
|
|
3950
|
+
}
|
|
3951
|
+
await deleteWikiPage(
|
|
3952
|
+
auth.instanceUrl,
|
|
3953
|
+
auth.token,
|
|
3954
|
+
scope,
|
|
3955
|
+
id,
|
|
3956
|
+
`skills-drafts/${args.name}`
|
|
3957
|
+
);
|
|
3958
|
+
return `Promoted skill "${args.name}" from draft to published.`;
|
|
3780
3959
|
} catch (err) {
|
|
3781
|
-
|
|
3960
|
+
if (err.message?.includes("not found") || err.message?.includes("404")) {
|
|
3961
|
+
return `Draft skill "${args.name}" not found. Use gitlab_skill_list(include_drafts=true) to see available drafts.`;
|
|
3962
|
+
}
|
|
3963
|
+
return `Error promoting skill: ${err.message}`;
|
|
3782
3964
|
}
|
|
3783
3965
|
}
|
|
3784
3966
|
})
|
|
@@ -3939,7 +4121,8 @@ var plugin = async (input) => {
|
|
|
3939
4121
|
...makeMcpTools(() => cachedAgents),
|
|
3940
4122
|
...makeCatalogCrudTools(ctx),
|
|
3941
4123
|
...makeCatalogItemTools(ctx),
|
|
3942
|
-
...
|
|
4124
|
+
...makeMemoryTools(ctx),
|
|
4125
|
+
...makeSkillTools(ctx)
|
|
3943
4126
|
}
|
|
3944
4127
|
};
|
|
3945
4128
|
};
|