opencode-gitlab-dap 1.16.2 → 1.16.4
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/dist/index.cjs +212 -379
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +211 -380
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4017,139 +4017,70 @@ async function listSnippetFiles(instanceUrl, token, projectId, snippetId) {
|
|
|
4017
4017
|
}
|
|
4018
4018
|
|
|
4019
4019
|
// src/tools/skill-tools.ts
|
|
4020
|
+
var import_fs3 = require("fs");
|
|
4021
|
+
var import_path3 = require("path");
|
|
4022
|
+
|
|
4023
|
+
// src/tools/skill-helpers.ts
|
|
4020
4024
|
var import_child_process = require("child_process");
|
|
4021
4025
|
var import_fs2 = require("fs");
|
|
4022
4026
|
var import_path2 = require("path");
|
|
4023
4027
|
var import_os2 = require("os");
|
|
4024
4028
|
var import_zlib = require("zlib");
|
|
4025
|
-
var z6 = import_plugin6.tool.schema;
|
|
4026
4029
|
var PREFIX2 = "agents";
|
|
4027
4030
|
var SKILLS_PREFIX = `${PREFIX2}/skills`;
|
|
4028
4031
|
var DRAFTS_PREFIX = `${PREFIX2}/skills-drafts`;
|
|
4029
|
-
var
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
}
|
|
4044
|
-
function parseIndex(content) {
|
|
4045
|
-
const entries = [];
|
|
4046
|
-
const blocks = content.split(/^## /m).filter(Boolean);
|
|
4047
|
-
for (const block of blocks) {
|
|
4048
|
-
const lines = block.trim().split("\n");
|
|
4049
|
-
const name = lines[0].trim();
|
|
4050
|
-
if (!name) continue;
|
|
4051
|
-
const rest = lines.slice(1).join("\n").trim();
|
|
4052
|
-
const descLines = [];
|
|
4053
|
-
let source;
|
|
4054
|
-
let snippetId;
|
|
4055
|
-
for (const line of rest.split("\n")) {
|
|
4056
|
-
if (line.startsWith("Source:")) {
|
|
4057
|
-
source = line.slice(7).trim();
|
|
4058
|
-
} else if (line.startsWith("Snippet:")) {
|
|
4059
|
-
snippetId = parseInt(line.slice(8).trim(), 10) || void 0;
|
|
4060
|
-
} else if (line.trim()) {
|
|
4061
|
-
descLines.push(line);
|
|
4062
|
-
}
|
|
4063
|
-
}
|
|
4064
|
-
entries.push({ name, description: descLines.join("\n"), source, snippetId, draft: false });
|
|
4065
|
-
}
|
|
4066
|
-
return entries;
|
|
4067
|
-
}
|
|
4068
|
-
function formatIndex(entries) {
|
|
4069
|
-
return entries.map((e) => {
|
|
4070
|
-
let block = `## ${e.name}
|
|
4071
|
-
${e.description}`;
|
|
4072
|
-
if (e.source) block += `
|
|
4073
|
-
Source: ${e.source}`;
|
|
4074
|
-
if (e.snippetId) block += `
|
|
4075
|
-
Snippet: ${e.snippetId}`;
|
|
4076
|
-
return block;
|
|
4077
|
-
}).join("\n\n");
|
|
4078
|
-
}
|
|
4079
|
-
async function readIndex(instanceUrl, token, scope, id, indexSlug) {
|
|
4080
|
-
try {
|
|
4081
|
-
const page = await getWikiPage(instanceUrl, token, scope, id, indexSlug);
|
|
4082
|
-
return parseIndex(page.content);
|
|
4083
|
-
} catch {
|
|
4084
|
-
return [];
|
|
4085
|
-
}
|
|
4086
|
-
}
|
|
4087
|
-
async function writeIndex(instanceUrl, token, scope, id, indexSlug, entries) {
|
|
4088
|
-
const content = formatIndex(entries) || "# Skills Index";
|
|
4089
|
-
try {
|
|
4090
|
-
await updateWikiPage(instanceUrl, token, scope, id, indexSlug, content);
|
|
4091
|
-
} catch (updateErr) {
|
|
4092
|
-
if (!updateErr.message?.includes("not found") && !updateErr.message?.includes("404"))
|
|
4093
|
-
throw updateErr;
|
|
4094
|
-
await createWikiPage(instanceUrl, token, scope, id, indexSlug, content);
|
|
4032
|
+
var EMPTY_FILE_SENTINEL = "__EMPTY_FILE__";
|
|
4033
|
+
function parseFrontmatter(content) {
|
|
4034
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
4035
|
+
if (!match) return { meta: {}, body: content };
|
|
4036
|
+
const meta = {};
|
|
4037
|
+
for (const line of match[1].split("\n")) {
|
|
4038
|
+
const [key, ...rest] = line.split(":");
|
|
4039
|
+
const val = rest.join(":").trim();
|
|
4040
|
+
if (!key || !val) continue;
|
|
4041
|
+
const k = key.trim();
|
|
4042
|
+
if (k === "name") meta.name = val;
|
|
4043
|
+
else if (k === "description") meta.description = val;
|
|
4044
|
+
else if (k === "source") meta.source = val;
|
|
4045
|
+
else if (k === "snippet") meta.snippetId = parseInt(val, 10) || void 0;
|
|
4095
4046
|
}
|
|
4047
|
+
return { meta, body: match[2] };
|
|
4096
4048
|
}
|
|
4097
|
-
|
|
4098
|
-
const
|
|
4099
|
-
|
|
4100
|
-
if (
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
entries.push(entry);
|
|
4104
|
-
}
|
|
4105
|
-
await writeIndex(instanceUrl, token, scope, id, indexSlug, entries);
|
|
4049
|
+
function formatFrontmatter(meta, body) {
|
|
4050
|
+
const lines = ["---", `name: ${meta.name}`, `description: ${meta.description}`];
|
|
4051
|
+
if (meta.source) lines.push(`source: ${meta.source}`);
|
|
4052
|
+
if (meta.snippetId) lines.push(`snippet: ${meta.snippetId}`);
|
|
4053
|
+
lines.push("---", "");
|
|
4054
|
+
return lines.join("\n") + body;
|
|
4106
4055
|
}
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
const
|
|
4110
|
-
|
|
4111
|
-
await writeIndex(instanceUrl, token, scope, id, indexSlug, filtered);
|
|
4112
|
-
}
|
|
4056
|
+
function extractSkillNameFromSlug(slug, prefix) {
|
|
4057
|
+
if (!slug.startsWith(prefix + "/") || !slug.endsWith("/SKILL")) return null;
|
|
4058
|
+
const middle = slug.slice(prefix.length + 1, -"/SKILL".length);
|
|
4059
|
+
return middle && !middle.includes("/") ? middle : null;
|
|
4113
4060
|
}
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
await updateWikiPage(instanceUrl, token, scope, id, slug, content);
|
|
4117
|
-
} catch (updateErr) {
|
|
4118
|
-
if (!updateErr.message?.includes("not found") && !updateErr.message?.includes("404"))
|
|
4119
|
-
throw updateErr;
|
|
4120
|
-
await createWikiPage(instanceUrl, token, scope, id, slug, content);
|
|
4121
|
-
}
|
|
4061
|
+
function isMarkdownFile(path) {
|
|
4062
|
+
return path === "SKILL.md" || path.endsWith(".md") || path.endsWith(".markdown");
|
|
4122
4063
|
}
|
|
4123
|
-
var EMPTY_FILE_SENTINEL = "__EMPTY_FILE__";
|
|
4124
4064
|
function packFiles(files) {
|
|
4125
4065
|
const manifest = files.map((f) => ({
|
|
4126
4066
|
path: f.path,
|
|
4127
4067
|
content: f.content.trim() ? f.content : EMPTY_FILE_SENTINEL
|
|
4128
4068
|
}));
|
|
4129
|
-
|
|
4130
|
-
return (0, import_zlib.gzipSync)(Buffer.from(json, "utf-8")).toString("base64");
|
|
4069
|
+
return (0, import_zlib.gzipSync)(Buffer.from(JSON.stringify(manifest), "utf-8")).toString("base64");
|
|
4131
4070
|
}
|
|
4132
4071
|
function unpackFiles(packed) {
|
|
4133
4072
|
const json = (0, import_zlib.gunzipSync)(Buffer.from(packed, "base64")).toString("utf-8");
|
|
4134
|
-
|
|
4135
|
-
return manifest.map((f) => ({
|
|
4073
|
+
return JSON.parse(json).map((f) => ({
|
|
4136
4074
|
path: f.path,
|
|
4137
4075
|
content: f.content === EMPTY_FILE_SENTINEL ? "" : f.content
|
|
4138
4076
|
}));
|
|
4139
4077
|
}
|
|
4140
|
-
function
|
|
4141
|
-
return path === "SKILL.md" || path.endsWith(".md") || path.endsWith(".markdown");
|
|
4142
|
-
}
|
|
4143
|
-
function isAgentsGitignored(dir) {
|
|
4078
|
+
function ensureGitignore(dir) {
|
|
4144
4079
|
try {
|
|
4145
4080
|
(0, import_child_process.execSync)("git check-ignore -q .agents", { cwd: dir, stdio: "pipe" });
|
|
4146
|
-
return
|
|
4081
|
+
return;
|
|
4147
4082
|
} catch {
|
|
4148
|
-
return false;
|
|
4149
4083
|
}
|
|
4150
|
-
}
|
|
4151
|
-
function ensureGitignore(dir) {
|
|
4152
|
-
if (isAgentsGitignored(dir)) return;
|
|
4153
4084
|
const gitignorePath = (0, import_path2.join)(dir, ".gitignore");
|
|
4154
4085
|
if ((0, import_fs2.existsSync)(gitignorePath)) {
|
|
4155
4086
|
const content = (0, import_fs2.readFileSync)(gitignorePath, "utf-8");
|
|
@@ -4202,10 +4133,9 @@ function downloadSkillFromSkillsSh(identifier) {
|
|
|
4202
4133
|
if (dirs.length === 0) return null;
|
|
4203
4134
|
const skillName = dirs[0];
|
|
4204
4135
|
const skillDir = (0, import_path2.join)(agentsDir, skillName);
|
|
4205
|
-
const skillMd = (0, import_path2.join)(skillDir, "SKILL.md");
|
|
4206
4136
|
let mainContent;
|
|
4207
4137
|
try {
|
|
4208
|
-
mainContent = (0, import_fs2.readFileSync)(
|
|
4138
|
+
mainContent = (0, import_fs2.readFileSync)((0, import_path2.join)(skillDir, "SKILL.md"), "utf-8");
|
|
4209
4139
|
} catch {
|
|
4210
4140
|
return null;
|
|
4211
4141
|
}
|
|
@@ -4214,8 +4144,7 @@ function downloadSkillFromSkillsSh(identifier) {
|
|
|
4214
4144
|
if (descMatch) {
|
|
4215
4145
|
description = descMatch[1].trim();
|
|
4216
4146
|
} else {
|
|
4217
|
-
|
|
4218
|
-
description = firstParagraph.slice(0, 200);
|
|
4147
|
+
description = mainContent.replace(/^---[\s\S]*?---\s*\n/, "").replace(/^#[^\n]*\n+/, "").split("\n\n")[0].replace(/\n/g, " ").trim().slice(0, 200);
|
|
4219
4148
|
}
|
|
4220
4149
|
const files = [];
|
|
4221
4150
|
const walkStack = [{ dir: skillDir, prefix: "" }];
|
|
@@ -4244,6 +4173,50 @@ function downloadSkillFromSkillsSh(identifier) {
|
|
|
4244
4173
|
}
|
|
4245
4174
|
}
|
|
4246
4175
|
}
|
|
4176
|
+
|
|
4177
|
+
// src/tools/skill-tools.ts
|
|
4178
|
+
var z6 = import_plugin6.tool.schema;
|
|
4179
|
+
var PROJECT_ID_DESC2 = "Project path from git remote";
|
|
4180
|
+
function resolveScope2(args) {
|
|
4181
|
+
if (args.scope === "groups" && args.group_id) {
|
|
4182
|
+
return { scope: "groups", id: args.group_id };
|
|
4183
|
+
}
|
|
4184
|
+
return { scope: "projects", id: args.project_id };
|
|
4185
|
+
}
|
|
4186
|
+
function validateProjectId2(projectId) {
|
|
4187
|
+
if (!projectId.includes("/")) {
|
|
4188
|
+
return `Invalid project_id "${projectId}". Must be the full project path containing at least one slash.`;
|
|
4189
|
+
}
|
|
4190
|
+
return null;
|
|
4191
|
+
}
|
|
4192
|
+
async function upsertPage(instanceUrl, token, scope, id, slug, content) {
|
|
4193
|
+
try {
|
|
4194
|
+
await updateWikiPage(instanceUrl, token, scope, id, slug, content);
|
|
4195
|
+
} catch (err) {
|
|
4196
|
+
if (err.message?.includes("not found") || err.message?.includes("404")) {
|
|
4197
|
+
await createWikiPage(instanceUrl, token, scope, id, slug, content);
|
|
4198
|
+
return;
|
|
4199
|
+
}
|
|
4200
|
+
throw err;
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
async function listSkills(instanceUrl, token, scope, id, prefix) {
|
|
4204
|
+
const pages = await listWikiPages(instanceUrl, token, scope, id, true);
|
|
4205
|
+
const skills = [];
|
|
4206
|
+
for (const page of pages) {
|
|
4207
|
+
const name = extractSkillNameFromSlug(page.slug, prefix);
|
|
4208
|
+
if (!name || !page.content) continue;
|
|
4209
|
+
const { meta } = parseFrontmatter(page.content);
|
|
4210
|
+
skills.push({
|
|
4211
|
+
name: meta.name ?? name,
|
|
4212
|
+
description: meta.description ?? "",
|
|
4213
|
+
source: meta.source,
|
|
4214
|
+
snippetId: meta.snippetId,
|
|
4215
|
+
draft: prefix === DRAFTS_PREFIX
|
|
4216
|
+
});
|
|
4217
|
+
}
|
|
4218
|
+
return skills;
|
|
4219
|
+
}
|
|
4247
4220
|
function makeSkillTools(ctx) {
|
|
4248
4221
|
function authAndValidate(projectId) {
|
|
4249
4222
|
const auth = ctx.ensureAuth();
|
|
@@ -4254,7 +4227,7 @@ function makeSkillTools(ctx) {
|
|
|
4254
4227
|
}
|
|
4255
4228
|
return {
|
|
4256
4229
|
gitlab_skill_list: (0, import_plugin6.tool)({
|
|
4257
|
-
description: "List available project skills and optionally draft skills.\nSkills define step-by-step procedures for common tasks
|
|
4230
|
+
description: "List available project skills and optionally draft skills.\nSkills define step-by-step procedures for common tasks.",
|
|
4258
4231
|
args: {
|
|
4259
4232
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4260
4233
|
include_drafts: z6.boolean().optional().describe("Also list draft skills (default: false)"),
|
|
@@ -4265,12 +4238,16 @@ function makeSkillTools(ctx) {
|
|
|
4265
4238
|
const auth = authAndValidate(args.project_id);
|
|
4266
4239
|
const { scope, id } = resolveScope2(args);
|
|
4267
4240
|
try {
|
|
4268
|
-
const published = await
|
|
4241
|
+
const published = await listSkills(
|
|
4242
|
+
auth.instanceUrl,
|
|
4243
|
+
auth.token,
|
|
4244
|
+
scope,
|
|
4245
|
+
id,
|
|
4246
|
+
SKILLS_PREFIX
|
|
4247
|
+
);
|
|
4269
4248
|
let drafts = [];
|
|
4270
4249
|
if (args.include_drafts) {
|
|
4271
|
-
drafts =
|
|
4272
|
-
(e) => ({ ...e, draft: true })
|
|
4273
|
-
);
|
|
4250
|
+
drafts = await listSkills(auth.instanceUrl, auth.token, scope, id, DRAFTS_PREFIX);
|
|
4274
4251
|
}
|
|
4275
4252
|
const all = [...published, ...drafts];
|
|
4276
4253
|
if (all.length === 0) return "No skills found. Use gitlab_skill_save to create one.";
|
|
@@ -4281,18 +4258,17 @@ function makeSkillTools(ctx) {
|
|
|
4281
4258
|
}
|
|
4282
4259
|
}),
|
|
4283
4260
|
gitlab_skill_load: (0, import_plugin6.tool)({
|
|
4284
|
-
description: "Load a specific skill by name.\
|
|
4261
|
+
description: "Load a specific skill by name.\nChecks published skills first, then falls back to draft skills.\nReturns the SKILL content and lists available reference pages.",
|
|
4285
4262
|
args: {
|
|
4286
4263
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4287
|
-
name: z6.string().describe('Skill name (e.g., "incident-retro"
|
|
4264
|
+
name: z6.string().describe('Skill name (e.g., "incident-retro")'),
|
|
4288
4265
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
4289
4266
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
4290
4267
|
},
|
|
4291
4268
|
execute: async (args) => {
|
|
4292
4269
|
const auth = authAndValidate(args.project_id);
|
|
4293
4270
|
const { scope, id } = resolveScope2(args);
|
|
4294
|
-
const
|
|
4295
|
-
for (const prefix of prefixes) {
|
|
4271
|
+
for (const prefix of [SKILLS_PREFIX, DRAFTS_PREFIX]) {
|
|
4296
4272
|
try {
|
|
4297
4273
|
const page = await getWikiPage(
|
|
4298
4274
|
auth.instanceUrl,
|
|
@@ -4301,36 +4277,28 @@ function makeSkillTools(ctx) {
|
|
|
4301
4277
|
id,
|
|
4302
4278
|
`${prefix}/${args.name}/SKILL`
|
|
4303
4279
|
);
|
|
4280
|
+
const { meta, body } = parseFrontmatter(page.content);
|
|
4281
|
+
const isDraft = prefix === DRAFTS_PREFIX;
|
|
4304
4282
|
const pages = await listWikiPages(auth.instanceUrl, auth.token, scope, id);
|
|
4305
4283
|
const skillPrefix = `${prefix}/${args.name}/`;
|
|
4306
|
-
const
|
|
4307
|
-
|
|
4308
|
-
|
|
4284
|
+
const refs = pages.filter(
|
|
4285
|
+
(p) => p.slug.startsWith(skillPrefix) && p.slug !== `${prefix}/${args.name}/SKILL`
|
|
4286
|
+
).map((p) => p.slug.slice(skillPrefix.length));
|
|
4309
4287
|
let result = isDraft ? `[DRAFT SKILL]
|
|
4310
4288
|
|
|
4311
|
-
${
|
|
4289
|
+
${body}` : body;
|
|
4312
4290
|
if (refs.length > 0) {
|
|
4313
4291
|
result += `
|
|
4314
4292
|
|
|
4315
4293
|
---
|
|
4316
4294
|
Available references: ${refs.join(", ")}`;
|
|
4317
4295
|
}
|
|
4318
|
-
|
|
4319
|
-
const indexEntries = await readIndex(
|
|
4320
|
-
auth.instanceUrl,
|
|
4321
|
-
auth.token,
|
|
4322
|
-
scope,
|
|
4323
|
-
id,
|
|
4324
|
-
indexSlug
|
|
4325
|
-
);
|
|
4326
|
-
const entry = indexEntries.find((e) => e.name === args.name);
|
|
4327
|
-
if (entry?.snippetId) {
|
|
4296
|
+
if (meta.snippetId) {
|
|
4328
4297
|
result += `
|
|
4329
4298
|
|
|
4330
|
-
|
|
4331
|
-
This skill has executable scripts in snippet #${entry.snippetId}.`;
|
|
4299
|
+
This skill has executable scripts in snippet #${meta.snippetId}.`;
|
|
4332
4300
|
result += `
|
|
4333
|
-
Run \`gitlab_skill_setup(name="${args.name}")\` to extract them
|
|
4301
|
+
Run \`gitlab_skill_setup(name="${args.name}")\` to extract them locally.`;
|
|
4334
4302
|
}
|
|
4335
4303
|
return result;
|
|
4336
4304
|
} catch {
|
|
@@ -4341,13 +4309,13 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4341
4309
|
}
|
|
4342
4310
|
}),
|
|
4343
4311
|
gitlab_skill_save: (0, import_plugin6.tool)({
|
|
4344
|
-
description: "Create or update a skill.\
|
|
4312
|
+
description: "Create or update a skill.\nUse draft=true for skills that haven't been proven yet.",
|
|
4345
4313
|
args: {
|
|
4346
4314
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4347
4315
|
name: z6.string().describe('Skill name (e.g., "incident-retro")'),
|
|
4348
4316
|
content: z6.string().describe("Skill content in markdown"),
|
|
4349
|
-
description: z6.string().describe("Short description
|
|
4350
|
-
draft: z6.boolean().optional().describe("Save as draft
|
|
4317
|
+
description: z6.string().describe("Short description (1-2 sentences)"),
|
|
4318
|
+
draft: z6.boolean().optional().describe("Save as draft (default: false)"),
|
|
4351
4319
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
4352
4320
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
4353
4321
|
},
|
|
@@ -4355,31 +4323,25 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4355
4323
|
const auth = authAndValidate(args.project_id);
|
|
4356
4324
|
const { scope, id } = resolveScope2(args);
|
|
4357
4325
|
const prefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
|
|
4358
|
-
const indexSlug = args.draft ? DRAFTS_INDEX : SKILLS_INDEX;
|
|
4359
4326
|
const slug = `${prefix}/${args.name}/SKILL`;
|
|
4360
|
-
const
|
|
4327
|
+
const body = formatFrontmatter(
|
|
4328
|
+
{ name: args.name, description: args.description, source: "project" },
|
|
4329
|
+
args.content
|
|
4330
|
+
);
|
|
4361
4331
|
try {
|
|
4362
|
-
await upsertPage(auth.instanceUrl, auth.token, scope, id, slug,
|
|
4363
|
-
|
|
4364
|
-
name: args.name,
|
|
4365
|
-
description: args.description,
|
|
4366
|
-
source: "project",
|
|
4367
|
-
draft: !!args.draft
|
|
4368
|
-
});
|
|
4369
|
-
return `Saved ${label}skill: ${args.name}`;
|
|
4332
|
+
await upsertPage(auth.instanceUrl, auth.token, scope, id, slug, body);
|
|
4333
|
+
return `Saved ${args.draft ? "draft " : ""}skill: ${args.name}`;
|
|
4370
4334
|
} catch (err) {
|
|
4371
4335
|
return `Error saving skill: ${err.message}`;
|
|
4372
4336
|
}
|
|
4373
4337
|
}
|
|
4374
4338
|
}),
|
|
4375
4339
|
gitlab_skill_promote: (0, import_plugin6.tool)({
|
|
4376
|
-
description: "Promote a skill.\nDefault (target='published'): moves a draft
|
|
4340
|
+
description: "Promote a skill.\nDefault (target='published'): moves a draft to published.\nTarget 'group': moves a project skill to the group wiki.",
|
|
4377
4341
|
args: {
|
|
4378
4342
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4379
4343
|
name: z6.string().describe("Skill name to promote"),
|
|
4380
|
-
target: z6.enum(["published", "group"]).optional().describe(
|
|
4381
|
-
'Promotion target: "published" (default, draft\u2192published) or "group" (project\u2192group wiki)'
|
|
4382
|
-
),
|
|
4344
|
+
target: z6.enum(["published", "group"]).optional().describe('"published" (default) or "group"'),
|
|
4383
4345
|
group_id: z6.string().optional().describe("Group path (required when target is group)"),
|
|
4384
4346
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)")
|
|
4385
4347
|
},
|
|
@@ -4387,9 +4349,7 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4387
4349
|
const auth = authAndValidate(args.project_id);
|
|
4388
4350
|
const promotionTarget = args.target ?? "published";
|
|
4389
4351
|
if (promotionTarget === "group") {
|
|
4390
|
-
if (!args.group_id)
|
|
4391
|
-
return 'Error: group_id is required when target is "group".';
|
|
4392
|
-
}
|
|
4352
|
+
if (!args.group_id) return 'Error: group_id is required when target is "group".';
|
|
4393
4353
|
const projectScope = resolveScope2(args);
|
|
4394
4354
|
try {
|
|
4395
4355
|
const pages = await listWikiPages(
|
|
@@ -4400,11 +4360,9 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4400
4360
|
true
|
|
4401
4361
|
);
|
|
4402
4362
|
const skillPrefix = `${SKILLS_PREFIX}/${args.name}/`;
|
|
4403
|
-
const skillPages = pages.filter(
|
|
4404
|
-
(p) => p.slug.startsWith(skillPrefix) && p.content
|
|
4405
|
-
);
|
|
4363
|
+
const skillPages = pages.filter((p) => p.slug.startsWith(skillPrefix) && p.content);
|
|
4406
4364
|
if (skillPages.length === 0) {
|
|
4407
|
-
return `Skill "${args.name}" not found in project
|
|
4365
|
+
return `Skill "${args.name}" not found in project.`;
|
|
4408
4366
|
}
|
|
4409
4367
|
for (const page of skillPages) {
|
|
4410
4368
|
await upsertPage(
|
|
@@ -4416,54 +4374,6 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4416
4374
|
page.content
|
|
4417
4375
|
);
|
|
4418
4376
|
}
|
|
4419
|
-
const projectIndex = await readIndex(
|
|
4420
|
-
auth.instanceUrl,
|
|
4421
|
-
auth.token,
|
|
4422
|
-
projectScope.scope,
|
|
4423
|
-
projectScope.id,
|
|
4424
|
-
SKILLS_INDEX
|
|
4425
|
-
);
|
|
4426
|
-
const entry = projectIndex.find((e) => e.name === args.name);
|
|
4427
|
-
const description = entry?.description ?? "(promoted from project)";
|
|
4428
|
-
if (entry?.snippetId) {
|
|
4429
|
-
const bundleFiles = await listSnippetFiles(
|
|
4430
|
-
auth.instanceUrl,
|
|
4431
|
-
auth.token,
|
|
4432
|
-
args.project_id,
|
|
4433
|
-
entry.snippetId
|
|
4434
|
-
);
|
|
4435
|
-
for (const bf of bundleFiles) {
|
|
4436
|
-
const raw = await getSnippetFileRaw(
|
|
4437
|
-
auth.instanceUrl,
|
|
4438
|
-
auth.token,
|
|
4439
|
-
args.project_id,
|
|
4440
|
-
entry.snippetId,
|
|
4441
|
-
bf.path
|
|
4442
|
-
);
|
|
4443
|
-
await upsertPage(
|
|
4444
|
-
auth.instanceUrl,
|
|
4445
|
-
auth.token,
|
|
4446
|
-
"groups",
|
|
4447
|
-
args.group_id,
|
|
4448
|
-
`${SKILLS_PREFIX}/${args.name}/bundle/${bf.path}`,
|
|
4449
|
-
raw
|
|
4450
|
-
);
|
|
4451
|
-
}
|
|
4452
|
-
}
|
|
4453
|
-
await upsertIndexEntry(
|
|
4454
|
-
auth.instanceUrl,
|
|
4455
|
-
auth.token,
|
|
4456
|
-
"groups",
|
|
4457
|
-
args.group_id,
|
|
4458
|
-
SKILLS_INDEX,
|
|
4459
|
-
{
|
|
4460
|
-
name: args.name,
|
|
4461
|
-
description,
|
|
4462
|
-
source: `project:${args.project_id}`,
|
|
4463
|
-
snippetId: entry?.snippetId,
|
|
4464
|
-
draft: false
|
|
4465
|
-
}
|
|
4466
|
-
);
|
|
4467
4377
|
for (const page of skillPages) {
|
|
4468
4378
|
await deleteWikiPage(
|
|
4469
4379
|
auth.instanceUrl,
|
|
@@ -4473,55 +4383,30 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4473
4383
|
page.slug
|
|
4474
4384
|
);
|
|
4475
4385
|
}
|
|
4476
|
-
|
|
4477
|
-
try {
|
|
4478
|
-
await deleteProjectSnippet(
|
|
4479
|
-
auth.instanceUrl,
|
|
4480
|
-
auth.token,
|
|
4481
|
-
args.project_id,
|
|
4482
|
-
entry.snippetId
|
|
4483
|
-
);
|
|
4484
|
-
} catch {
|
|
4485
|
-
}
|
|
4486
|
-
}
|
|
4487
|
-
await removeIndexEntry(
|
|
4488
|
-
auth.instanceUrl,
|
|
4489
|
-
auth.token,
|
|
4490
|
-
projectScope.scope,
|
|
4491
|
-
projectScope.id,
|
|
4492
|
-
SKILLS_INDEX,
|
|
4493
|
-
args.name
|
|
4494
|
-
);
|
|
4495
|
-
return `Promoted skill "${args.name}" to group "${args.group_id}". ${skillPages.length} page(s) moved (removed from project).`;
|
|
4386
|
+
return `Promoted skill "${args.name}" to group "${args.group_id}". ${skillPages.length} page(s) moved.`;
|
|
4496
4387
|
} catch (err) {
|
|
4497
4388
|
return `Error promoting skill to group: ${err.message}`;
|
|
4498
4389
|
}
|
|
4499
4390
|
}
|
|
4500
4391
|
const { scope, id } = resolveScope2(args);
|
|
4501
4392
|
try {
|
|
4502
|
-
const pages = await listWikiPages(
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4393
|
+
const pages = await listWikiPages(
|
|
4394
|
+
auth.instanceUrl,
|
|
4395
|
+
auth.token,
|
|
4396
|
+
scope,
|
|
4397
|
+
id,
|
|
4398
|
+
true
|
|
4506
4399
|
);
|
|
4400
|
+
const draftPrefix = `${DRAFTS_PREFIX}/${args.name}/`;
|
|
4401
|
+
const draftPages = pages.filter((p) => p.slug.startsWith(draftPrefix) && p.content);
|
|
4507
4402
|
if (draftPages.length === 0) {
|
|
4508
|
-
return `Draft skill "${args.name}" not found
|
|
4403
|
+
return `Draft skill "${args.name}" not found.`;
|
|
4509
4404
|
}
|
|
4510
|
-
const draftIndex = await readIndex(auth.instanceUrl, auth.token, scope, id, DRAFTS_INDEX);
|
|
4511
|
-
const entry = draftIndex.find((e) => e.name === args.name);
|
|
4512
|
-
const description = entry?.description ?? "(promoted from draft)";
|
|
4513
4405
|
for (const page of draftPages) {
|
|
4514
4406
|
const newSlug = page.slug.replace(DRAFTS_PREFIX, SKILLS_PREFIX);
|
|
4515
4407
|
await upsertPage(auth.instanceUrl, auth.token, scope, id, newSlug, page.content);
|
|
4516
4408
|
await deleteWikiPage(auth.instanceUrl, auth.token, scope, id, page.slug);
|
|
4517
4409
|
}
|
|
4518
|
-
await removeIndexEntry(auth.instanceUrl, auth.token, scope, id, DRAFTS_INDEX, args.name);
|
|
4519
|
-
await upsertIndexEntry(auth.instanceUrl, auth.token, scope, id, SKILLS_INDEX, {
|
|
4520
|
-
name: args.name,
|
|
4521
|
-
description,
|
|
4522
|
-
source: "project",
|
|
4523
|
-
draft: false
|
|
4524
|
-
});
|
|
4525
4410
|
return `Promoted skill "${args.name}" from draft to published.`;
|
|
4526
4411
|
} catch (err) {
|
|
4527
4412
|
return `Error promoting skill: ${err.message}`;
|
|
@@ -4529,11 +4414,11 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4529
4414
|
}
|
|
4530
4415
|
}),
|
|
4531
4416
|
gitlab_skill_discover: (0, import_plugin6.tool)({
|
|
4532
|
-
description: "Search for skills in
|
|
4417
|
+
description: "Search for skills in skills.sh and optionally a group wiki.\nUse gitlab_skill_install to install a discovered skill.",
|
|
4533
4418
|
args: {
|
|
4534
|
-
query: z6.string().describe("Search query
|
|
4535
|
-
project_id: z6.string().optional().describe(PROJECT_ID_DESC2 + " Only needed for group
|
|
4536
|
-
group_id: z6.string().optional().describe("Group path to search
|
|
4419
|
+
query: z6.string().describe("Search query"),
|
|
4420
|
+
project_id: z6.string().optional().describe(PROJECT_ID_DESC2 + " Only needed for group search."),
|
|
4421
|
+
group_id: z6.string().optional().describe("Group path to search")
|
|
4537
4422
|
},
|
|
4538
4423
|
execute: async (args) => {
|
|
4539
4424
|
let auth = null;
|
|
@@ -4545,15 +4430,15 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4545
4430
|
const sections = [];
|
|
4546
4431
|
if (args.group_id && auth) {
|
|
4547
4432
|
try {
|
|
4548
|
-
const
|
|
4433
|
+
const skills = await listSkills(
|
|
4549
4434
|
auth.instanceUrl,
|
|
4550
4435
|
auth.token,
|
|
4551
4436
|
"groups",
|
|
4552
4437
|
args.group_id,
|
|
4553
|
-
|
|
4438
|
+
SKILLS_PREFIX
|
|
4554
4439
|
);
|
|
4555
4440
|
const q = args.query.toLowerCase();
|
|
4556
|
-
const matches =
|
|
4441
|
+
const matches = skills.filter(
|
|
4557
4442
|
(e) => e.name.toLowerCase().includes(q) || e.description.toLowerCase().includes(q)
|
|
4558
4443
|
);
|
|
4559
4444
|
if (matches.length > 0) {
|
|
@@ -4582,19 +4467,17 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4582
4467
|
);
|
|
4583
4468
|
}
|
|
4584
4469
|
if (sections.length === 0) {
|
|
4585
|
-
return `No skills found matching "${args.query}"
|
|
4470
|
+
return `No skills found matching "${args.query}".`;
|
|
4586
4471
|
}
|
|
4587
4472
|
return sections.join("\n\n---\n\n");
|
|
4588
4473
|
}
|
|
4589
4474
|
}),
|
|
4590
4475
|
gitlab_skill_install: (0, import_plugin6.tool)({
|
|
4591
|
-
description: "Install a skill from
|
|
4476
|
+
description: "Install a skill from group wiki or skills.sh into the project wiki.\nMarkdown files go to wiki pages, scripts are bundled in a project snippet.",
|
|
4592
4477
|
args: {
|
|
4593
4478
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4594
|
-
name: z6.string().describe(
|
|
4595
|
-
|
|
4596
|
-
),
|
|
4597
|
-
source: z6.enum(["group", "skills.sh"]).describe('Where to install from: "group" (group wiki) or "skills.sh" (public registry)'),
|
|
4479
|
+
name: z6.string().describe("Skill identifier"),
|
|
4480
|
+
source: z6.enum(["group", "skills.sh"]).describe("Where to install from"),
|
|
4598
4481
|
group_id: z6.string().optional().describe("Group path (required when source is group)"),
|
|
4599
4482
|
draft: z6.boolean().optional().describe("Install as draft (default: false)")
|
|
4600
4483
|
},
|
|
@@ -4602,24 +4485,45 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4602
4485
|
const auth = authAndValidate(args.project_id);
|
|
4603
4486
|
const projectScope = resolveScope2(args);
|
|
4604
4487
|
const targetPrefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
|
|
4605
|
-
const targetIndex = args.draft ? DRAFTS_INDEX : SKILLS_INDEX;
|
|
4606
4488
|
if (args.source === "skills.sh") {
|
|
4607
4489
|
const downloaded = downloadSkillFromSkillsSh(args.name);
|
|
4608
4490
|
if (!downloaded) {
|
|
4609
|
-
return `Failed to download skill "${args.name}" from skills.sh
|
|
4491
|
+
return `Failed to download skill "${args.name}" from skills.sh.`;
|
|
4610
4492
|
}
|
|
4611
4493
|
try {
|
|
4494
|
+
const mdFiles = downloaded.files.filter((f) => isMarkdownFile(f.path));
|
|
4495
|
+
const scriptFiles = downloaded.files.filter((f) => !isMarkdownFile(f.path));
|
|
4496
|
+
let snippetId;
|
|
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
|
+
}
|
|
4509
|
+
const skillBody = formatFrontmatter(
|
|
4510
|
+
{
|
|
4511
|
+
name: downloaded.name,
|
|
4512
|
+
description: downloaded.description,
|
|
4513
|
+
source: `skills.sh:${args.name}`,
|
|
4514
|
+
snippetId
|
|
4515
|
+
},
|
|
4516
|
+
downloaded.content.replace(/^---[\s\S]*?---\s*\n/, "")
|
|
4517
|
+
);
|
|
4612
4518
|
await upsertPage(
|
|
4613
4519
|
auth.instanceUrl,
|
|
4614
4520
|
auth.token,
|
|
4615
4521
|
projectScope.scope,
|
|
4616
4522
|
projectScope.id,
|
|
4617
4523
|
`${targetPrefix}/${downloaded.name}/SKILL`,
|
|
4618
|
-
|
|
4524
|
+
skillBody
|
|
4619
4525
|
);
|
|
4620
4526
|
let wikiCount = 1;
|
|
4621
|
-
const mdFiles = downloaded.files.filter((f) => isMarkdownFile(f.path));
|
|
4622
|
-
const scriptFiles = downloaded.files.filter((f) => !isMarkdownFile(f.path));
|
|
4623
4527
|
for (const file of mdFiles) {
|
|
4624
4528
|
const slug = `${targetPrefix}/${downloaded.name}/${file.path.replace(/\.[^.]+$/, "")}`;
|
|
4625
4529
|
await upsertPage(
|
|
@@ -4632,44 +4536,14 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4632
4536
|
);
|
|
4633
4537
|
wikiCount++;
|
|
4634
4538
|
}
|
|
4635
|
-
let snippetId;
|
|
4636
|
-
if (scriptFiles.length > 0) {
|
|
4637
|
-
const packed = packFiles(scriptFiles);
|
|
4638
|
-
const snippet = await createProjectSnippet(
|
|
4639
|
-
auth.instanceUrl,
|
|
4640
|
-
auth.token,
|
|
4641
|
-
args.project_id,
|
|
4642
|
-
`skill:${downloaded.name}`,
|
|
4643
|
-
`Scripts for skill "${downloaded.name}" (${scriptFiles.length} files, installed from skills.sh:${args.name})`,
|
|
4644
|
-
[{ file_path: `${downloaded.name}.bundle`, content: packed }],
|
|
4645
|
-
"private"
|
|
4646
|
-
);
|
|
4647
|
-
snippetId = snippet.id;
|
|
4648
|
-
}
|
|
4649
|
-
await upsertIndexEntry(
|
|
4650
|
-
auth.instanceUrl,
|
|
4651
|
-
auth.token,
|
|
4652
|
-
projectScope.scope,
|
|
4653
|
-
projectScope.id,
|
|
4654
|
-
targetIndex,
|
|
4655
|
-
{
|
|
4656
|
-
name: downloaded.name,
|
|
4657
|
-
description: downloaded.description,
|
|
4658
|
-
source: `skills.sh:${args.name}`,
|
|
4659
|
-
snippetId,
|
|
4660
|
-
draft: !!args.draft
|
|
4661
|
-
}
|
|
4662
|
-
);
|
|
4663
4539
|
const parts = [`${wikiCount} wiki page(s)`];
|
|
4664
4540
|
if (snippetId) parts.push(`snippet #${snippetId} with ${scriptFiles.length} script(s)`);
|
|
4665
|
-
return `Installed skill "${downloaded.name}" from skills.sh. ${parts.join(", ")}. Use gitlab_skill_setup to extract scripts
|
|
4541
|
+
return `Installed skill "${downloaded.name}" from skills.sh. ${parts.join(", ")}. Use gitlab_skill_setup to extract scripts.`;
|
|
4666
4542
|
} catch (err) {
|
|
4667
|
-
return `Error installing
|
|
4543
|
+
return `Error installing from skills.sh: ${err.message}`;
|
|
4668
4544
|
}
|
|
4669
4545
|
}
|
|
4670
|
-
if (!args.group_id)
|
|
4671
|
-
return 'Error: group_id is required when source is "group".';
|
|
4672
|
-
}
|
|
4546
|
+
if (!args.group_id) return 'Error: group_id is required when source is "group".';
|
|
4673
4547
|
try {
|
|
4674
4548
|
const groupPages = await listWikiPages(
|
|
4675
4549
|
auth.instanceUrl,
|
|
@@ -4679,9 +4553,7 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4679
4553
|
true
|
|
4680
4554
|
);
|
|
4681
4555
|
const sourcePrefix = `${SKILLS_PREFIX}/${args.name}/`;
|
|
4682
|
-
const skillPages = groupPages.filter(
|
|
4683
|
-
(p) => p.slug.startsWith(sourcePrefix) && p.content
|
|
4684
|
-
);
|
|
4556
|
+
const skillPages = groupPages.filter((p) => p.slug.startsWith(sourcePrefix) && p.content);
|
|
4685
4557
|
if (skillPages.length === 0) {
|
|
4686
4558
|
return `Skill "${args.name}" not found in group "${args.group_id}" wiki.`;
|
|
4687
4559
|
}
|
|
@@ -4696,39 +4568,17 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4696
4568
|
page.content
|
|
4697
4569
|
);
|
|
4698
4570
|
}
|
|
4699
|
-
|
|
4700
|
-
auth.instanceUrl,
|
|
4701
|
-
auth.token,
|
|
4702
|
-
"groups",
|
|
4703
|
-
args.group_id,
|
|
4704
|
-
SKILLS_INDEX
|
|
4705
|
-
);
|
|
4706
|
-
const entry = groupIndex.find((e) => e.name === args.name);
|
|
4707
|
-
const description = entry?.description ?? "(installed from group)";
|
|
4708
|
-
await upsertIndexEntry(
|
|
4709
|
-
auth.instanceUrl,
|
|
4710
|
-
auth.token,
|
|
4711
|
-
projectScope.scope,
|
|
4712
|
-
projectScope.id,
|
|
4713
|
-
targetIndex,
|
|
4714
|
-
{
|
|
4715
|
-
name: args.name,
|
|
4716
|
-
description,
|
|
4717
|
-
source: `group:${args.group_id}`,
|
|
4718
|
-
draft: !!args.draft
|
|
4719
|
-
}
|
|
4720
|
-
);
|
|
4721
|
-
return `Installed skill "${args.name}" from group "${args.group_id}". ${skillPages.length} page(s) copied.`;
|
|
4571
|
+
return `Installed skill "${args.name}" from group "${args.group_id}". ${skillPages.length} page(s).`;
|
|
4722
4572
|
} catch (err) {
|
|
4723
|
-
return `Error installing
|
|
4573
|
+
return `Error installing from group: ${err.message}`;
|
|
4724
4574
|
}
|
|
4725
4575
|
}
|
|
4726
4576
|
}),
|
|
4727
4577
|
gitlab_skill_setup: (0, import_plugin6.tool)({
|
|
4728
|
-
description: "Extract a skill to
|
|
4578
|
+
description: "Extract a skill to .agents/skills/ for local execution.\nDownloads SKILL.md from wiki and scripts from the associated snippet.\nOpenCode will auto-discover the skill from the local directory.",
|
|
4729
4579
|
args: {
|
|
4730
4580
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4731
|
-
name: z6.string().describe("Skill name to set up
|
|
4581
|
+
name: z6.string().describe("Skill name to set up"),
|
|
4732
4582
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
4733
4583
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
4734
4584
|
},
|
|
@@ -4736,29 +4586,35 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4736
4586
|
const auth = authAndValidate(args.project_id);
|
|
4737
4587
|
const { scope, id } = resolveScope2(args);
|
|
4738
4588
|
const workDir = ctx.getDirectory();
|
|
4739
|
-
const targetDir = (0,
|
|
4589
|
+
const targetDir = (0, import_path3.join)(workDir, ".agents", "skills", args.name);
|
|
4740
4590
|
try {
|
|
4741
|
-
let
|
|
4591
|
+
let skillPage = null;
|
|
4742
4592
|
for (const prefix of [SKILLS_PREFIX, DRAFTS_PREFIX]) {
|
|
4743
4593
|
try {
|
|
4744
|
-
|
|
4594
|
+
skillPage = await getWikiPage(
|
|
4745
4595
|
auth.instanceUrl,
|
|
4746
4596
|
auth.token,
|
|
4747
4597
|
scope,
|
|
4748
4598
|
id,
|
|
4749
4599
|
`${prefix}/${args.name}/SKILL`
|
|
4750
4600
|
);
|
|
4751
|
-
skillContent = page.content;
|
|
4752
4601
|
break;
|
|
4753
4602
|
} catch {
|
|
4754
4603
|
}
|
|
4755
4604
|
}
|
|
4756
|
-
if (!
|
|
4757
|
-
return `Skill "${args.name}" not found
|
|
4605
|
+
if (!skillPage) {
|
|
4606
|
+
return `Skill "${args.name}" not found. Use gitlab_skill_list to see available skills.`;
|
|
4758
4607
|
}
|
|
4759
|
-
|
|
4760
|
-
(0,
|
|
4761
|
-
|
|
4608
|
+
const { meta, body } = parseFrontmatter(skillPage.content);
|
|
4609
|
+
(0, import_fs3.mkdirSync)(targetDir, { recursive: true });
|
|
4610
|
+
(0, import_fs3.writeFileSync)((0, import_path3.join)(targetDir, "SKILL.md"), body);
|
|
4611
|
+
const pages = await listWikiPages(
|
|
4612
|
+
auth.instanceUrl,
|
|
4613
|
+
auth.token,
|
|
4614
|
+
scope,
|
|
4615
|
+
id,
|
|
4616
|
+
true
|
|
4617
|
+
);
|
|
4762
4618
|
let refCount = 0;
|
|
4763
4619
|
for (const prefix of [SKILLS_PREFIX, DRAFTS_PREFIX]) {
|
|
4764
4620
|
const skillPagePrefix = `${prefix}/${args.name}/`;
|
|
@@ -4767,59 +4623,36 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4767
4623
|
);
|
|
4768
4624
|
for (const page of extraPages) {
|
|
4769
4625
|
const relPath = page.slug.slice(skillPagePrefix.length);
|
|
4770
|
-
const filePath = (0,
|
|
4771
|
-
(0,
|
|
4772
|
-
(0,
|
|
4626
|
+
const filePath = (0, import_path3.join)(targetDir, `${relPath}.md`);
|
|
4627
|
+
(0, import_fs3.mkdirSync)((0, import_path3.dirname)(filePath), { recursive: true });
|
|
4628
|
+
(0, import_fs3.writeFileSync)(filePath, page.content);
|
|
4773
4629
|
refCount++;
|
|
4774
4630
|
}
|
|
4775
4631
|
}
|
|
4776
4632
|
let scriptCount = 0;
|
|
4777
|
-
|
|
4778
|
-
auth.instanceUrl,
|
|
4779
|
-
auth.token,
|
|
4780
|
-
scope,
|
|
4781
|
-
id,
|
|
4782
|
-
SKILLS_INDEX
|
|
4783
|
-
);
|
|
4784
|
-
const draftsEntries = await readIndex(
|
|
4785
|
-
auth.instanceUrl,
|
|
4786
|
-
auth.token,
|
|
4787
|
-
scope,
|
|
4788
|
-
id,
|
|
4789
|
-
DRAFTS_INDEX
|
|
4790
|
-
);
|
|
4791
|
-
const entry = [...indexEntries, ...draftsEntries].find((e) => e.name === args.name);
|
|
4792
|
-
if (entry?.snippetId) {
|
|
4633
|
+
if (meta.snippetId) {
|
|
4793
4634
|
const bundleFiles = await listSnippetFiles(
|
|
4794
4635
|
auth.instanceUrl,
|
|
4795
4636
|
auth.token,
|
|
4796
4637
|
args.project_id,
|
|
4797
|
-
|
|
4638
|
+
meta.snippetId
|
|
4798
4639
|
);
|
|
4799
4640
|
for (const bf of bundleFiles) {
|
|
4800
4641
|
const raw = await getSnippetFileRaw(
|
|
4801
4642
|
auth.instanceUrl,
|
|
4802
4643
|
auth.token,
|
|
4803
4644
|
args.project_id,
|
|
4804
|
-
|
|
4645
|
+
meta.snippetId,
|
|
4805
4646
|
bf.path
|
|
4806
4647
|
);
|
|
4807
4648
|
if (bf.path.endsWith(".bundle")) {
|
|
4808
|
-
const
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
(0,
|
|
4812
|
-
(0,
|
|
4813
|
-
if (file.path.endsWith(".sh")) {
|
|
4814
|
-
(0, import_fs2.chmodSync)(filePath, 493);
|
|
4815
|
-
}
|
|
4649
|
+
for (const file of unpackFiles(raw)) {
|
|
4650
|
+
const filePath = (0, import_path3.join)(targetDir, file.path);
|
|
4651
|
+
(0, import_fs3.mkdirSync)((0, import_path3.dirname)(filePath), { recursive: true });
|
|
4652
|
+
(0, import_fs3.writeFileSync)(filePath, file.content);
|
|
4653
|
+
if (file.path.endsWith(".sh")) (0, import_fs3.chmodSync)(filePath, 493);
|
|
4816
4654
|
scriptCount++;
|
|
4817
4655
|
}
|
|
4818
|
-
} else {
|
|
4819
|
-
const filePath = (0, import_path2.join)(targetDir, bf.path);
|
|
4820
|
-
(0, import_fs2.mkdirSync)((0, import_path2.dirname)(filePath), { recursive: true });
|
|
4821
|
-
(0, import_fs2.writeFileSync)(filePath, raw);
|
|
4822
|
-
scriptCount++;
|
|
4823
4656
|
}
|
|
4824
4657
|
}
|
|
4825
4658
|
}
|
|
@@ -4827,18 +4660,18 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4827
4660
|
const parts = ["SKILL.md"];
|
|
4828
4661
|
if (refCount > 0) parts.push(`${refCount} reference(s)`);
|
|
4829
4662
|
if (scriptCount > 0) parts.push(`${scriptCount} script(s)`);
|
|
4830
|
-
return `Skill "${args.name}" extracted to ${targetDir} (${parts.join(", ")})
|
|
4663
|
+
return `Skill "${args.name}" extracted to ${targetDir} (${parts.join(", ")}).`;
|
|
4831
4664
|
} catch (err) {
|
|
4832
4665
|
return `Error setting up skill: ${err.message}`;
|
|
4833
4666
|
}
|
|
4834
4667
|
}
|
|
4835
4668
|
}),
|
|
4836
4669
|
gitlab_skill_delete: (0, import_plugin6.tool)({
|
|
4837
|
-
description: "Delete a skill and
|
|
4670
|
+
description: "Delete a skill and its associated snippet.\nRemoves all wiki pages under the skill directory.",
|
|
4838
4671
|
args: {
|
|
4839
4672
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4840
4673
|
name: z6.string().describe("Skill name to delete"),
|
|
4841
|
-
draft: z6.boolean().optional().describe("Delete from drafts
|
|
4674
|
+
draft: z6.boolean().optional().describe("Delete from drafts (default: false)"),
|
|
4842
4675
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
4843
4676
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
4844
4677
|
},
|
|
@@ -4846,36 +4679,36 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4846
4679
|
const auth = authAndValidate(args.project_id);
|
|
4847
4680
|
const { scope, id } = resolveScope2(args);
|
|
4848
4681
|
const prefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
|
|
4849
|
-
const indexSlug = args.draft ? DRAFTS_INDEX : SKILLS_INDEX;
|
|
4850
4682
|
const skillPrefix = `${prefix}/${args.name}/`;
|
|
4851
4683
|
try {
|
|
4852
4684
|
const pages = await listWikiPages(auth.instanceUrl, auth.token, scope, id);
|
|
4853
4685
|
const skillPages = pages.filter((p) => p.slug.startsWith(skillPrefix));
|
|
4854
4686
|
if (skillPages.length === 0) {
|
|
4855
|
-
return `Skill "${args.name}" not found
|
|
4687
|
+
return `Skill "${args.name}" not found.`;
|
|
4856
4688
|
}
|
|
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);
|
|
4857
4697
|
for (const page of skillPages) {
|
|
4858
4698
|
await deleteWikiPage(auth.instanceUrl, auth.token, scope, id, page.slug);
|
|
4859
4699
|
}
|
|
4860
|
-
|
|
4861
|
-
const entry = indexEntries.find((e) => e.name === args.name);
|
|
4862
|
-
let snippetDeleted = false;
|
|
4863
|
-
if (entry?.snippetId) {
|
|
4700
|
+
if (meta.snippetId) {
|
|
4864
4701
|
try {
|
|
4865
4702
|
await deleteProjectSnippet(
|
|
4866
4703
|
auth.instanceUrl,
|
|
4867
4704
|
auth.token,
|
|
4868
4705
|
args.project_id,
|
|
4869
|
-
|
|
4706
|
+
meta.snippetId
|
|
4870
4707
|
);
|
|
4871
|
-
snippetDeleted = true;
|
|
4872
4708
|
} catch {
|
|
4873
4709
|
}
|
|
4874
4710
|
}
|
|
4875
|
-
|
|
4876
|
-
const parts = [`${skillPages.length} wiki page(s)`];
|
|
4877
|
-
if (snippetDeleted) parts.push("snippet");
|
|
4878
|
-
return `Deleted skill "${args.name}" (${parts.join(" + ")} removed).`;
|
|
4711
|
+
return `Deleted skill "${args.name}" (${skillPages.length} page(s)${meta.snippetId ? " + snippet" : ""}).`;
|
|
4879
4712
|
} catch (err) {
|
|
4880
4713
|
return `Error deleting skill: ${err.message}`;
|
|
4881
4714
|
}
|