opencode-gitlab-dap 1.7.1 → 1.8.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 +19 -23
- package/dist/index.cjs +281 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +281 -125
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2085,9 +2085,12 @@ prompts:
|
|
|
2085
2085
|
user: "Fix this vulnerability: {{vuln_data}}"
|
|
2086
2086
|
placeholder: history
|
|
2087
2087
|
\`\`\``;
|
|
2088
|
-
var
|
|
2089
|
-
|
|
2090
|
-
|
|
2088
|
+
var PROJECT_KNOWLEDGE_HINT = `## Project Knowledge
|
|
2089
|
+
This project may have persistent memory and skills available via knowledge tools.
|
|
2090
|
+
Use gitlab_memory_load to check for existing project context (facts, decisions, patterns).
|
|
2091
|
+
Use gitlab_skill_list to discover available task-specific skills.
|
|
2092
|
+
When you learn something new about the project, use gitlab_memory_record to preserve it.
|
|
2093
|
+
When you complete a significant task, consider using gitlab_memory_log_session to log learnings.`;
|
|
2091
2094
|
|
|
2092
2095
|
// src/hooks.ts
|
|
2093
2096
|
function buildFlowSubagentPrompt(flow, projectPath, projectUrl) {
|
|
@@ -2247,7 +2250,7 @@ function makeSystemTransformHook(flowAgents, getAuthCache) {
|
|
|
2247
2250
|
}
|
|
2248
2251
|
if (getAuthCache()) {
|
|
2249
2252
|
output.system.push(AGENT_CREATION_GUIDELINES);
|
|
2250
|
-
output.system.push(
|
|
2253
|
+
output.system.push(PROJECT_KNOWLEDGE_HINT);
|
|
2251
2254
|
}
|
|
2252
2255
|
};
|
|
2253
2256
|
}
|
|
@@ -3356,7 +3359,7 @@ function makeMcpTools(getCachedAgents) {
|
|
|
3356
3359
|
};
|
|
3357
3360
|
}
|
|
3358
3361
|
|
|
3359
|
-
// src/tools/
|
|
3362
|
+
// src/tools/memory-tools.ts
|
|
3360
3363
|
import { tool as tool5 } from "@opencode-ai/plugin";
|
|
3361
3364
|
|
|
3362
3365
|
// src/wiki.ts
|
|
@@ -3437,192 +3440,344 @@ async function searchWikiPages(instanceUrl, token, scope, id, query) {
|
|
|
3437
3440
|
return handleResponse(res);
|
|
3438
3441
|
}
|
|
3439
3442
|
|
|
3440
|
-
// src/tools/
|
|
3443
|
+
// src/tools/memory-tools.ts
|
|
3441
3444
|
var z5 = tool5.schema;
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3445
|
+
var PREFIX = "agents";
|
|
3446
|
+
var MEMORY_SLUGS = {
|
|
3447
|
+
all: [`${PREFIX}/memory/facts`, `${PREFIX}/memory/decisions`, `${PREFIX}/memory/patterns`],
|
|
3448
|
+
facts: [`${PREFIX}/memory/facts`],
|
|
3449
|
+
decisions: [`${PREFIX}/memory/decisions`],
|
|
3450
|
+
patterns: [`${PREFIX}/memory/patterns`]
|
|
3451
|
+
};
|
|
3452
|
+
var RECORD_SLUG = {
|
|
3453
|
+
fact: `${PREFIX}/memory/facts`,
|
|
3454
|
+
decision: `${PREFIX}/memory/decisions`,
|
|
3455
|
+
pattern: `${PREFIX}/memory/patterns`
|
|
3456
|
+
};
|
|
3457
|
+
function today() {
|
|
3458
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3459
|
+
}
|
|
3460
|
+
function slugify(text) {
|
|
3461
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
3462
|
+
}
|
|
3463
|
+
async function safeRead(instanceUrl, token, scope, id, slug) {
|
|
3464
|
+
try {
|
|
3465
|
+
const page = await getWikiPage(instanceUrl, token, scope, id, slug);
|
|
3466
|
+
return page.content;
|
|
3467
|
+
} catch {
|
|
3468
|
+
return null;
|
|
3448
3469
|
}
|
|
3470
|
+
}
|
|
3471
|
+
async function appendToPage(instanceUrl, token, scope, id, slug, newContent) {
|
|
3472
|
+
const existing = await safeRead(instanceUrl, token, scope, id, slug);
|
|
3473
|
+
if (existing !== null) {
|
|
3474
|
+
await updateWikiPage(instanceUrl, token, scope, id, slug, existing + "\n\n" + newContent);
|
|
3475
|
+
} else {
|
|
3476
|
+
await createWikiPage(instanceUrl, token, scope, id, slug, newContent);
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
function resolveScope(args) {
|
|
3480
|
+
if (args.scope === "groups" && args.group_id) {
|
|
3481
|
+
return { scope: "groups", id: args.group_id };
|
|
3482
|
+
}
|
|
3483
|
+
return { scope: "projects", id: args.project_id };
|
|
3484
|
+
}
|
|
3485
|
+
function makeMemoryTools(ctx) {
|
|
3449
3486
|
return {
|
|
3450
|
-
|
|
3451
|
-
description: "
|
|
3487
|
+
gitlab_memory_load: tool5({
|
|
3488
|
+
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.",
|
|
3452
3489
|
args: {
|
|
3453
|
-
project_id: z5.string().describe(
|
|
3454
|
-
|
|
3455
|
-
),
|
|
3456
|
-
|
|
3457
|
-
scope: z5.enum(["projects", "groups"]).optional().describe('Scope: "projects" (default) or "groups"'),
|
|
3458
|
-
group_id: z5.string().optional().describe("Group path, required when scope is groups")
|
|
3490
|
+
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3491
|
+
type: z5.enum(["all", "facts", "decisions", "patterns"]).optional().describe('Which memory to load: "all" (default), "facts", "decisions", or "patterns"'),
|
|
3492
|
+
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3493
|
+
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3459
3494
|
},
|
|
3460
3495
|
execute: async (args) => {
|
|
3461
3496
|
const auth = ctx.ensureAuth();
|
|
3462
3497
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3463
3498
|
const { scope, id } = resolveScope(args);
|
|
3499
|
+
const memType = args.type ?? "all";
|
|
3500
|
+
const slugs = MEMORY_SLUGS[memType];
|
|
3501
|
+
if (!slugs) return `Unknown memory type: ${memType}`;
|
|
3502
|
+
const sections = [];
|
|
3503
|
+
for (const slug of slugs) {
|
|
3504
|
+
const content = await safeRead(auth.instanceUrl, auth.token, scope, id, slug);
|
|
3505
|
+
if (content) {
|
|
3506
|
+
const label = slug.split("/").pop();
|
|
3507
|
+
sections.push(`## ${label.charAt(0).toUpperCase() + label.slice(1)}
|
|
3508
|
+
|
|
3509
|
+
${content}`);
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
if (sections.length === 0)
|
|
3513
|
+
return "No project memory found. Use gitlab_memory_record to start building project knowledge.";
|
|
3514
|
+
return sections.join("\n\n---\n\n");
|
|
3515
|
+
}
|
|
3516
|
+
}),
|
|
3517
|
+
gitlab_memory_record: tool5({
|
|
3518
|
+
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 and appended to the appropriate memory page.\nMultiple facts can be recorded in a single call by separating them with newlines.",
|
|
3519
|
+
args: {
|
|
3520
|
+
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3521
|
+
type: z5.enum(["fact", "decision", "pattern"]).describe("Type of knowledge to record"),
|
|
3522
|
+
content: z5.string().describe("The knowledge to record (markdown)"),
|
|
3523
|
+
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3524
|
+
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3525
|
+
},
|
|
3526
|
+
execute: async (args) => {
|
|
3527
|
+
const auth = ctx.ensureAuth();
|
|
3528
|
+
if (!auth) throw new Error("GitLab authentication not available");
|
|
3529
|
+
const { scope, id } = resolveScope(args);
|
|
3530
|
+
const slug = RECORD_SLUG[args.type];
|
|
3531
|
+
if (!slug) return `Unknown memory type: ${args.type}`;
|
|
3532
|
+
const date = today();
|
|
3533
|
+
let formatted;
|
|
3534
|
+
if (args.type === "decision") {
|
|
3535
|
+
const firstLine = args.content.split("\n")[0];
|
|
3536
|
+
const rest = args.content.split("\n").slice(1).join("\n").trim();
|
|
3537
|
+
formatted = `## ${date}: ${firstLine}${rest ? "\n" + rest : ""}`;
|
|
3538
|
+
} else if (args.type === "fact") {
|
|
3539
|
+
formatted = `## ${date}
|
|
3540
|
+
- ${args.content.replace(/\n/g, "\n- ")}`;
|
|
3541
|
+
} else {
|
|
3542
|
+
formatted = `## ${date}
|
|
3543
|
+
${args.content}`;
|
|
3544
|
+
}
|
|
3464
3545
|
try {
|
|
3465
|
-
|
|
3466
|
-
return
|
|
3546
|
+
await appendToPage(auth.instanceUrl, auth.token, scope, id, slug, formatted);
|
|
3547
|
+
return `Recorded ${args.type} in project memory.`;
|
|
3467
3548
|
} catch (err) {
|
|
3468
|
-
return `Error
|
|
3549
|
+
return `Error recording ${args.type}: ${err.message}`;
|
|
3469
3550
|
}
|
|
3470
3551
|
}
|
|
3471
3552
|
}),
|
|
3472
|
-
|
|
3473
|
-
description: "
|
|
3553
|
+
gitlab_memory_recall: tool5({
|
|
3554
|
+
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.",
|
|
3474
3555
|
args: {
|
|
3475
|
-
project_id: z5.string().describe(
|
|
3476
|
-
|
|
3477
|
-
),
|
|
3478
|
-
|
|
3479
|
-
content: z5.string().describe("Full page content in Markdown"),
|
|
3480
|
-
title: z5.string().optional().describe("Page title (defaults to slug if omitted)"),
|
|
3481
|
-
scope: z5.enum(["projects", "groups"]).optional().describe('Scope: "projects" (default) or "groups"'),
|
|
3482
|
-
group_id: z5.string().optional().describe("Group path, required when scope is groups")
|
|
3556
|
+
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3557
|
+
query: z5.string().describe("What to search for"),
|
|
3558
|
+
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3559
|
+
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3483
3560
|
},
|
|
3484
3561
|
execute: async (args) => {
|
|
3485
3562
|
const auth = ctx.ensureAuth();
|
|
3486
3563
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3487
3564
|
const { scope, id } = resolveScope(args);
|
|
3488
3565
|
try {
|
|
3489
|
-
const
|
|
3566
|
+
const results = await searchWikiPages(
|
|
3490
3567
|
auth.instanceUrl,
|
|
3491
3568
|
auth.token,
|
|
3492
3569
|
scope,
|
|
3493
3570
|
id,
|
|
3494
|
-
args.
|
|
3495
|
-
args.content,
|
|
3496
|
-
args.title
|
|
3571
|
+
args.query
|
|
3497
3572
|
);
|
|
3498
|
-
return `
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
const
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
id,
|
|
3507
|
-
title,
|
|
3508
|
-
args.content
|
|
3509
|
-
);
|
|
3510
|
-
return `Created wiki page: ${page.slug}`;
|
|
3511
|
-
} catch (err) {
|
|
3512
|
-
return `Error writing wiki page: ${err.message}`;
|
|
3513
|
-
}
|
|
3573
|
+
if (results.length === 0) return `No knowledge found matching "${args.query}".`;
|
|
3574
|
+
return results.map((r) => {
|
|
3575
|
+
const category = r.path.includes("/memory/") ? "memory" : r.path.includes("/skills") ? "skill" : "other";
|
|
3576
|
+
const snippet = (r.data ?? "").slice(0, 200).replace(/\n/g, " ");
|
|
3577
|
+
return `[${category}] ${r.path}: ${snippet}`;
|
|
3578
|
+
}).join("\n\n");
|
|
3579
|
+
} catch (err) {
|
|
3580
|
+
return `Error searching knowledge: ${err.message}`;
|
|
3514
3581
|
}
|
|
3515
3582
|
}
|
|
3516
3583
|
}),
|
|
3517
|
-
|
|
3518
|
-
description: "
|
|
3584
|
+
gitlab_memory_log_session: tool5({
|
|
3585
|
+
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.",
|
|
3519
3586
|
args: {
|
|
3520
|
-
project_id: z5.string().describe(
|
|
3521
|
-
|
|
3587
|
+
project_id: z5.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3588
|
+
title: z5.string().describe('Brief session title (e.g., "fix-ai-gateway-healthcheck")'),
|
|
3589
|
+
summary: z5.string().describe(
|
|
3590
|
+
"Session summary in markdown (what happened, what was learned, what went wrong)"
|
|
3522
3591
|
),
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
scope: z5.enum(["projects", "groups"]).optional().describe('Scope: "projects" (default) or "groups"'),
|
|
3526
|
-
group_id: z5.string().optional().describe("Group path, required when scope is groups")
|
|
3592
|
+
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3593
|
+
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3527
3594
|
},
|
|
3528
3595
|
execute: async (args) => {
|
|
3529
3596
|
const auth = ctx.ensureAuth();
|
|
3530
3597
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3531
3598
|
const { scope, id } = resolveScope(args);
|
|
3599
|
+
const date = today();
|
|
3600
|
+
const slug = `${PREFIX}/memory/sessions/${date}-${slugify(args.title)}`;
|
|
3532
3601
|
try {
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
return `Error appending to wiki page: ${err.message}`;
|
|
3602
|
+
await createWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.summary);
|
|
3603
|
+
return `Session logged: ${slug}`;
|
|
3604
|
+
} catch (err) {
|
|
3605
|
+
if (err.message?.includes("already exists") || err.message?.includes("422")) {
|
|
3606
|
+
try {
|
|
3607
|
+
await updateWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.summary);
|
|
3608
|
+
return `Session log updated: ${slug}`;
|
|
3609
|
+
} catch (err2) {
|
|
3610
|
+
return `Error logging session: ${err2.message}`;
|
|
3611
|
+
}
|
|
3544
3612
|
}
|
|
3613
|
+
return `Error logging session: ${err.message}`;
|
|
3545
3614
|
}
|
|
3546
3615
|
}
|
|
3547
|
-
})
|
|
3548
|
-
|
|
3549
|
-
|
|
3616
|
+
})
|
|
3617
|
+
};
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3620
|
+
// src/tools/skill-tools.ts
|
|
3621
|
+
import { tool as tool6 } from "@opencode-ai/plugin";
|
|
3622
|
+
var z6 = tool6.schema;
|
|
3623
|
+
var PREFIX2 = "agents";
|
|
3624
|
+
var SKILLS_PREFIX = `${PREFIX2}/skills`;
|
|
3625
|
+
var DRAFTS_PREFIX = `${PREFIX2}/skills-drafts`;
|
|
3626
|
+
function resolveScope2(args) {
|
|
3627
|
+
if (args.scope === "groups" && args.group_id) {
|
|
3628
|
+
return { scope: "groups", id: args.group_id };
|
|
3629
|
+
}
|
|
3630
|
+
return { scope: "projects", id: args.project_id };
|
|
3631
|
+
}
|
|
3632
|
+
function makeSkillTools(ctx) {
|
|
3633
|
+
return {
|
|
3634
|
+
gitlab_skill_list: tool6({
|
|
3635
|
+
description: "List available project skills and optionally draft skills.\nSkills define step-by-step procedures for common tasks (e.g., incident retros, debugging, deployments).",
|
|
3550
3636
|
args: {
|
|
3551
|
-
project_id:
|
|
3552
|
-
|
|
3553
|
-
),
|
|
3554
|
-
|
|
3555
|
-
scope: z5.enum(["projects", "groups"]).optional().describe('Scope: "projects" (default) or "groups"'),
|
|
3556
|
-
group_id: z5.string().optional().describe("Group path, required when scope is groups")
|
|
3637
|
+
project_id: z6.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3638
|
+
include_drafts: z6.boolean().optional().describe("Also list draft skills (default: false)"),
|
|
3639
|
+
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3640
|
+
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3557
3641
|
},
|
|
3558
3642
|
execute: async (args) => {
|
|
3559
3643
|
const auth = ctx.ensureAuth();
|
|
3560
3644
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3561
|
-
const { scope, id } =
|
|
3645
|
+
const { scope, id } = resolveScope2(args);
|
|
3562
3646
|
try {
|
|
3563
3647
|
const pages = await listWikiPages(auth.instanceUrl, auth.token, scope, id);
|
|
3564
|
-
const
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
);
|
|
3648
|
+
const indexSlug = `${SKILLS_PREFIX}/index`;
|
|
3649
|
+
const skills = pages.filter((p) => p.slug.startsWith(`${SKILLS_PREFIX}/`) && p.slug !== indexSlug).map((p) => ({
|
|
3650
|
+
name: p.slug.slice(SKILLS_PREFIX.length + 1),
|
|
3651
|
+
title: p.title,
|
|
3652
|
+
draft: false
|
|
3653
|
+
}));
|
|
3654
|
+
let drafts = [];
|
|
3655
|
+
if (args.include_drafts) {
|
|
3656
|
+
const draftsIndexSlug = `${DRAFTS_PREFIX}/index`;
|
|
3657
|
+
drafts = pages.filter((p) => p.slug.startsWith(`${DRAFTS_PREFIX}/`) && p.slug !== draftsIndexSlug).map((p) => ({
|
|
3658
|
+
name: p.slug.slice(DRAFTS_PREFIX.length + 1),
|
|
3659
|
+
title: p.title,
|
|
3660
|
+
draft: true
|
|
3661
|
+
}));
|
|
3662
|
+
}
|
|
3663
|
+
const all = [...skills, ...drafts];
|
|
3664
|
+
if (all.length === 0) return "No skills found. Use gitlab_skill_save to create one.";
|
|
3665
|
+
return JSON.stringify(all, null, 2);
|
|
3570
3666
|
} catch (err) {
|
|
3571
|
-
return `Error listing
|
|
3667
|
+
return `Error listing skills: ${err.message}`;
|
|
3572
3668
|
}
|
|
3573
3669
|
}
|
|
3574
3670
|
}),
|
|
3575
|
-
|
|
3576
|
-
description: "
|
|
3671
|
+
gitlab_skill_load: tool6({
|
|
3672
|
+
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.",
|
|
3577
3673
|
args: {
|
|
3578
|
-
project_id:
|
|
3579
|
-
|
|
3580
|
-
),
|
|
3581
|
-
|
|
3582
|
-
scope: z5.enum(["projects", "groups"]).optional().describe('Scope: "projects" (default) or "groups"'),
|
|
3583
|
-
group_id: z5.string().optional().describe("Group path, required when scope is groups")
|
|
3674
|
+
project_id: z6.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3675
|
+
name: z6.string().describe('Skill name (e.g., "incident-retro", "helm-rollback")'),
|
|
3676
|
+
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3677
|
+
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3584
3678
|
},
|
|
3585
3679
|
execute: async (args) => {
|
|
3586
3680
|
const auth = ctx.ensureAuth();
|
|
3587
3681
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3588
|
-
const { scope, id } =
|
|
3682
|
+
const { scope, id } = resolveScope2(args);
|
|
3589
3683
|
try {
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3684
|
+
const page = await getWikiPage(
|
|
3685
|
+
auth.instanceUrl,
|
|
3686
|
+
auth.token,
|
|
3687
|
+
scope,
|
|
3688
|
+
id,
|
|
3689
|
+
`${SKILLS_PREFIX}/${args.name}`
|
|
3690
|
+
);
|
|
3691
|
+
return page.content;
|
|
3692
|
+
} catch {
|
|
3693
|
+
try {
|
|
3694
|
+
const draft = await getWikiPage(
|
|
3695
|
+
auth.instanceUrl,
|
|
3696
|
+
auth.token,
|
|
3697
|
+
scope,
|
|
3698
|
+
id,
|
|
3699
|
+
`${DRAFTS_PREFIX}/${args.name}`
|
|
3700
|
+
);
|
|
3701
|
+
return `[DRAFT SKILL]
|
|
3702
|
+
|
|
3703
|
+
${draft.content}`;
|
|
3704
|
+
} catch {
|
|
3705
|
+
return `Skill "${args.name}" not found. Use gitlab_skill_list to see available skills.`;
|
|
3706
|
+
}
|
|
3594
3707
|
}
|
|
3595
3708
|
}
|
|
3596
3709
|
}),
|
|
3597
|
-
|
|
3598
|
-
description: "
|
|
3710
|
+
gitlab_skill_save: tool6({
|
|
3711
|
+
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.",
|
|
3599
3712
|
args: {
|
|
3600
|
-
project_id:
|
|
3601
|
-
|
|
3602
|
-
),
|
|
3603
|
-
|
|
3604
|
-
scope:
|
|
3605
|
-
group_id:
|
|
3713
|
+
project_id: z6.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3714
|
+
name: z6.string().describe('Skill name (e.g., "incident-retro")'),
|
|
3715
|
+
content: z6.string().describe("Skill content in markdown"),
|
|
3716
|
+
draft: z6.boolean().optional().describe("Save as draft skill (default: false)"),
|
|
3717
|
+
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3718
|
+
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3606
3719
|
},
|
|
3607
3720
|
execute: async (args) => {
|
|
3608
3721
|
const auth = ctx.ensureAuth();
|
|
3609
3722
|
if (!auth) throw new Error("GitLab authentication not available");
|
|
3610
|
-
const { scope, id } =
|
|
3723
|
+
const { scope, id } = resolveScope2(args);
|
|
3724
|
+
const prefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
|
|
3725
|
+
const slug = `${prefix}/${args.name}`;
|
|
3611
3726
|
try {
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
id,
|
|
3617
|
-
args.
|
|
3618
|
-
)
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3727
|
+
await updateWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.content);
|
|
3728
|
+
return `Updated ${args.draft ? "draft " : ""}skill: ${args.name}`;
|
|
3729
|
+
} catch {
|
|
3730
|
+
try {
|
|
3731
|
+
await createWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.content);
|
|
3732
|
+
return `Created ${args.draft ? "draft " : ""}skill: ${args.name}`;
|
|
3733
|
+
} catch (err) {
|
|
3734
|
+
return `Error saving skill: ${err.message}`;
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
}),
|
|
3739
|
+
gitlab_skill_promote: tool6({
|
|
3740
|
+
description: "Promote a draft skill to published.\nMoves the skill from the drafts directory to the published skills directory.",
|
|
3741
|
+
args: {
|
|
3742
|
+
project_id: z6.string().describe('Project path (e.g., "gitlab-org/gitlab"). Use the same value consistently.'),
|
|
3743
|
+
name: z6.string().describe("Skill name to promote"),
|
|
3744
|
+
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3745
|
+
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3746
|
+
},
|
|
3747
|
+
execute: async (args) => {
|
|
3748
|
+
const auth = ctx.ensureAuth();
|
|
3749
|
+
if (!auth) throw new Error("GitLab authentication not available");
|
|
3750
|
+
const { scope, id } = resolveScope2(args);
|
|
3751
|
+
const draftSlug = `${DRAFTS_PREFIX}/${args.name}`;
|
|
3752
|
+
const publishedSlug = `${SKILLS_PREFIX}/${args.name}`;
|
|
3753
|
+
try {
|
|
3754
|
+
const draft = await getWikiPage(auth.instanceUrl, auth.token, scope, id, draftSlug);
|
|
3755
|
+
try {
|
|
3756
|
+
await updateWikiPage(
|
|
3757
|
+
auth.instanceUrl,
|
|
3758
|
+
auth.token,
|
|
3759
|
+
scope,
|
|
3760
|
+
id,
|
|
3761
|
+
publishedSlug,
|
|
3762
|
+
draft.content
|
|
3763
|
+
);
|
|
3764
|
+
} catch {
|
|
3765
|
+
await createWikiPage(
|
|
3766
|
+
auth.instanceUrl,
|
|
3767
|
+
auth.token,
|
|
3768
|
+
scope,
|
|
3769
|
+
id,
|
|
3770
|
+
publishedSlug,
|
|
3771
|
+
draft.content
|
|
3772
|
+
);
|
|
3773
|
+
}
|
|
3774
|
+
await deleteWikiPage(auth.instanceUrl, auth.token, scope, id, draftSlug);
|
|
3775
|
+
return `Promoted skill "${args.name}" from draft to published.`;
|
|
3624
3776
|
} catch (err) {
|
|
3625
|
-
|
|
3777
|
+
if (err.message?.includes("not found") || err.message?.includes("404")) {
|
|
3778
|
+
return `Draft skill "${args.name}" not found. Use gitlab_skill_list(include_drafts=true) to see available drafts.`;
|
|
3779
|
+
}
|
|
3780
|
+
return `Error promoting skill: ${err.message}`;
|
|
3626
3781
|
}
|
|
3627
3782
|
}
|
|
3628
3783
|
})
|
|
@@ -3783,7 +3938,8 @@ var plugin = async (input) => {
|
|
|
3783
3938
|
...makeMcpTools(() => cachedAgents),
|
|
3784
3939
|
...makeCatalogCrudTools(ctx),
|
|
3785
3940
|
...makeCatalogItemTools(ctx),
|
|
3786
|
-
...
|
|
3941
|
+
...makeMemoryTools(ctx),
|
|
3942
|
+
...makeSkillTools(ctx)
|
|
3787
3943
|
}
|
|
3788
3944
|
};
|
|
3789
3945
|
};
|