opencode-gitlab-dap 1.16.3 → 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 -410
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +211 -411
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4017,170 +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 sleep2(ms) {
|
|
4088
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4089
|
-
}
|
|
4090
|
-
async function writeIndex(instanceUrl, token, scope, id, indexSlug, entries) {
|
|
4091
|
-
const content = formatIndex(entries) || "# Skills Registry";
|
|
4092
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
4093
|
-
try {
|
|
4094
|
-
await updateWikiPage(instanceUrl, token, scope, id, indexSlug, content);
|
|
4095
|
-
return;
|
|
4096
|
-
} catch (updateErr) {
|
|
4097
|
-
const msg = updateErr.message ?? "";
|
|
4098
|
-
if (msg.includes("not found") || msg.includes("404")) {
|
|
4099
|
-
await createWikiPage(instanceUrl, token, scope, id, indexSlug, content);
|
|
4100
|
-
return;
|
|
4101
|
-
}
|
|
4102
|
-
if (attempt < 2) {
|
|
4103
|
-
await sleep2(1e3 * (attempt + 1));
|
|
4104
|
-
continue;
|
|
4105
|
-
}
|
|
4106
|
-
throw updateErr;
|
|
4107
|
-
}
|
|
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;
|
|
4108
4046
|
}
|
|
4047
|
+
return { meta, body: match[2] };
|
|
4109
4048
|
}
|
|
4110
|
-
|
|
4111
|
-
const
|
|
4112
|
-
|
|
4113
|
-
if (
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
entries.push(entry);
|
|
4117
|
-
}
|
|
4118
|
-
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;
|
|
4119
4055
|
}
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
const
|
|
4123
|
-
|
|
4124
|
-
await writeIndex(instanceUrl, token, scope, id, indexSlug, filtered);
|
|
4125
|
-
}
|
|
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;
|
|
4126
4060
|
}
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
try {
|
|
4130
|
-
await updateWikiPage(instanceUrl, token, scope, id, slug, content);
|
|
4131
|
-
return;
|
|
4132
|
-
} catch (updateErr) {
|
|
4133
|
-
const msg = updateErr.message ?? "";
|
|
4134
|
-
if (msg.includes("not found") || msg.includes("404")) {
|
|
4135
|
-
try {
|
|
4136
|
-
await createWikiPage(instanceUrl, token, scope, id, slug, content);
|
|
4137
|
-
return;
|
|
4138
|
-
} catch (createErr) {
|
|
4139
|
-
if (attempt < 2 && (createErr.message?.includes("Duplicate") || createErr.message?.includes("reference"))) {
|
|
4140
|
-
await sleep2(1e3 * (attempt + 1));
|
|
4141
|
-
continue;
|
|
4142
|
-
}
|
|
4143
|
-
throw createErr;
|
|
4144
|
-
}
|
|
4145
|
-
}
|
|
4146
|
-
if (attempt < 2) {
|
|
4147
|
-
await sleep2(1e3 * (attempt + 1));
|
|
4148
|
-
continue;
|
|
4149
|
-
}
|
|
4150
|
-
throw updateErr;
|
|
4151
|
-
}
|
|
4152
|
-
}
|
|
4061
|
+
function isMarkdownFile(path) {
|
|
4062
|
+
return path === "SKILL.md" || path.endsWith(".md") || path.endsWith(".markdown");
|
|
4153
4063
|
}
|
|
4154
|
-
var EMPTY_FILE_SENTINEL = "__EMPTY_FILE__";
|
|
4155
4064
|
function packFiles(files) {
|
|
4156
4065
|
const manifest = files.map((f) => ({
|
|
4157
4066
|
path: f.path,
|
|
4158
4067
|
content: f.content.trim() ? f.content : EMPTY_FILE_SENTINEL
|
|
4159
4068
|
}));
|
|
4160
|
-
|
|
4161
|
-
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");
|
|
4162
4070
|
}
|
|
4163
4071
|
function unpackFiles(packed) {
|
|
4164
4072
|
const json = (0, import_zlib.gunzipSync)(Buffer.from(packed, "base64")).toString("utf-8");
|
|
4165
|
-
|
|
4166
|
-
return manifest.map((f) => ({
|
|
4073
|
+
return JSON.parse(json).map((f) => ({
|
|
4167
4074
|
path: f.path,
|
|
4168
4075
|
content: f.content === EMPTY_FILE_SENTINEL ? "" : f.content
|
|
4169
4076
|
}));
|
|
4170
4077
|
}
|
|
4171
|
-
function
|
|
4172
|
-
return path === "SKILL.md" || path.endsWith(".md") || path.endsWith(".markdown");
|
|
4173
|
-
}
|
|
4174
|
-
function isAgentsGitignored(dir) {
|
|
4078
|
+
function ensureGitignore(dir) {
|
|
4175
4079
|
try {
|
|
4176
4080
|
(0, import_child_process.execSync)("git check-ignore -q .agents", { cwd: dir, stdio: "pipe" });
|
|
4177
|
-
return
|
|
4081
|
+
return;
|
|
4178
4082
|
} catch {
|
|
4179
|
-
return false;
|
|
4180
4083
|
}
|
|
4181
|
-
}
|
|
4182
|
-
function ensureGitignore(dir) {
|
|
4183
|
-
if (isAgentsGitignored(dir)) return;
|
|
4184
4084
|
const gitignorePath = (0, import_path2.join)(dir, ".gitignore");
|
|
4185
4085
|
if ((0, import_fs2.existsSync)(gitignorePath)) {
|
|
4186
4086
|
const content = (0, import_fs2.readFileSync)(gitignorePath, "utf-8");
|
|
@@ -4233,10 +4133,9 @@ function downloadSkillFromSkillsSh(identifier) {
|
|
|
4233
4133
|
if (dirs.length === 0) return null;
|
|
4234
4134
|
const skillName = dirs[0];
|
|
4235
4135
|
const skillDir = (0, import_path2.join)(agentsDir, skillName);
|
|
4236
|
-
const skillMd = (0, import_path2.join)(skillDir, "SKILL.md");
|
|
4237
4136
|
let mainContent;
|
|
4238
4137
|
try {
|
|
4239
|
-
mainContent = (0, import_fs2.readFileSync)(
|
|
4138
|
+
mainContent = (0, import_fs2.readFileSync)((0, import_path2.join)(skillDir, "SKILL.md"), "utf-8");
|
|
4240
4139
|
} catch {
|
|
4241
4140
|
return null;
|
|
4242
4141
|
}
|
|
@@ -4245,8 +4144,7 @@ function downloadSkillFromSkillsSh(identifier) {
|
|
|
4245
4144
|
if (descMatch) {
|
|
4246
4145
|
description = descMatch[1].trim();
|
|
4247
4146
|
} else {
|
|
4248
|
-
|
|
4249
|
-
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);
|
|
4250
4148
|
}
|
|
4251
4149
|
const files = [];
|
|
4252
4150
|
const walkStack = [{ dir: skillDir, prefix: "" }];
|
|
@@ -4275,6 +4173,50 @@ function downloadSkillFromSkillsSh(identifier) {
|
|
|
4275
4173
|
}
|
|
4276
4174
|
}
|
|
4277
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
|
+
}
|
|
4278
4220
|
function makeSkillTools(ctx) {
|
|
4279
4221
|
function authAndValidate(projectId) {
|
|
4280
4222
|
const auth = ctx.ensureAuth();
|
|
@@ -4285,7 +4227,7 @@ function makeSkillTools(ctx) {
|
|
|
4285
4227
|
}
|
|
4286
4228
|
return {
|
|
4287
4229
|
gitlab_skill_list: (0, import_plugin6.tool)({
|
|
4288
|
-
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.",
|
|
4289
4231
|
args: {
|
|
4290
4232
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4291
4233
|
include_drafts: z6.boolean().optional().describe("Also list draft skills (default: false)"),
|
|
@@ -4296,12 +4238,16 @@ function makeSkillTools(ctx) {
|
|
|
4296
4238
|
const auth = authAndValidate(args.project_id);
|
|
4297
4239
|
const { scope, id } = resolveScope2(args);
|
|
4298
4240
|
try {
|
|
4299
|
-
const published = await
|
|
4241
|
+
const published = await listSkills(
|
|
4242
|
+
auth.instanceUrl,
|
|
4243
|
+
auth.token,
|
|
4244
|
+
scope,
|
|
4245
|
+
id,
|
|
4246
|
+
SKILLS_PREFIX
|
|
4247
|
+
);
|
|
4300
4248
|
let drafts = [];
|
|
4301
4249
|
if (args.include_drafts) {
|
|
4302
|
-
drafts =
|
|
4303
|
-
(e) => ({ ...e, draft: true })
|
|
4304
|
-
);
|
|
4250
|
+
drafts = await listSkills(auth.instanceUrl, auth.token, scope, id, DRAFTS_PREFIX);
|
|
4305
4251
|
}
|
|
4306
4252
|
const all = [...published, ...drafts];
|
|
4307
4253
|
if (all.length === 0) return "No skills found. Use gitlab_skill_save to create one.";
|
|
@@ -4312,18 +4258,17 @@ function makeSkillTools(ctx) {
|
|
|
4312
4258
|
}
|
|
4313
4259
|
}),
|
|
4314
4260
|
gitlab_skill_load: (0, import_plugin6.tool)({
|
|
4315
|
-
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.",
|
|
4316
4262
|
args: {
|
|
4317
4263
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4318
|
-
name: z6.string().describe('Skill name (e.g., "incident-retro"
|
|
4264
|
+
name: z6.string().describe('Skill name (e.g., "incident-retro")'),
|
|
4319
4265
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
4320
4266
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
4321
4267
|
},
|
|
4322
4268
|
execute: async (args) => {
|
|
4323
4269
|
const auth = authAndValidate(args.project_id);
|
|
4324
4270
|
const { scope, id } = resolveScope2(args);
|
|
4325
|
-
const
|
|
4326
|
-
for (const prefix of prefixes) {
|
|
4271
|
+
for (const prefix of [SKILLS_PREFIX, DRAFTS_PREFIX]) {
|
|
4327
4272
|
try {
|
|
4328
4273
|
const page = await getWikiPage(
|
|
4329
4274
|
auth.instanceUrl,
|
|
@@ -4332,36 +4277,28 @@ function makeSkillTools(ctx) {
|
|
|
4332
4277
|
id,
|
|
4333
4278
|
`${prefix}/${args.name}/SKILL`
|
|
4334
4279
|
);
|
|
4280
|
+
const { meta, body } = parseFrontmatter(page.content);
|
|
4281
|
+
const isDraft = prefix === DRAFTS_PREFIX;
|
|
4335
4282
|
const pages = await listWikiPages(auth.instanceUrl, auth.token, scope, id);
|
|
4336
4283
|
const skillPrefix = `${prefix}/${args.name}/`;
|
|
4337
|
-
const
|
|
4338
|
-
|
|
4339
|
-
|
|
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));
|
|
4340
4287
|
let result = isDraft ? `[DRAFT SKILL]
|
|
4341
4288
|
|
|
4342
|
-
${
|
|
4289
|
+
${body}` : body;
|
|
4343
4290
|
if (refs.length > 0) {
|
|
4344
4291
|
result += `
|
|
4345
4292
|
|
|
4346
4293
|
---
|
|
4347
4294
|
Available references: ${refs.join(", ")}`;
|
|
4348
4295
|
}
|
|
4349
|
-
|
|
4350
|
-
const indexEntries = await readIndex(
|
|
4351
|
-
auth.instanceUrl,
|
|
4352
|
-
auth.token,
|
|
4353
|
-
scope,
|
|
4354
|
-
id,
|
|
4355
|
-
indexSlug
|
|
4356
|
-
);
|
|
4357
|
-
const entry = indexEntries.find((e) => e.name === args.name);
|
|
4358
|
-
if (entry?.snippetId) {
|
|
4296
|
+
if (meta.snippetId) {
|
|
4359
4297
|
result += `
|
|
4360
4298
|
|
|
4361
|
-
|
|
4362
|
-
This skill has executable scripts in snippet #${entry.snippetId}.`;
|
|
4299
|
+
This skill has executable scripts in snippet #${meta.snippetId}.`;
|
|
4363
4300
|
result += `
|
|
4364
|
-
Run \`gitlab_skill_setup(name="${args.name}")\` to extract them
|
|
4301
|
+
Run \`gitlab_skill_setup(name="${args.name}")\` to extract them locally.`;
|
|
4365
4302
|
}
|
|
4366
4303
|
return result;
|
|
4367
4304
|
} catch {
|
|
@@ -4372,13 +4309,13 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4372
4309
|
}
|
|
4373
4310
|
}),
|
|
4374
4311
|
gitlab_skill_save: (0, import_plugin6.tool)({
|
|
4375
|
-
description: "Create or update a skill.\
|
|
4312
|
+
description: "Create or update a skill.\nUse draft=true for skills that haven't been proven yet.",
|
|
4376
4313
|
args: {
|
|
4377
4314
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4378
4315
|
name: z6.string().describe('Skill name (e.g., "incident-retro")'),
|
|
4379
4316
|
content: z6.string().describe("Skill content in markdown"),
|
|
4380
|
-
description: z6.string().describe("Short description
|
|
4381
|
-
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)"),
|
|
4382
4319
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
4383
4320
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
4384
4321
|
},
|
|
@@ -4386,31 +4323,25 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4386
4323
|
const auth = authAndValidate(args.project_id);
|
|
4387
4324
|
const { scope, id } = resolveScope2(args);
|
|
4388
4325
|
const prefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
|
|
4389
|
-
const indexSlug = args.draft ? DRAFTS_INDEX : SKILLS_INDEX;
|
|
4390
4326
|
const slug = `${prefix}/${args.name}/SKILL`;
|
|
4391
|
-
const
|
|
4327
|
+
const body = formatFrontmatter(
|
|
4328
|
+
{ name: args.name, description: args.description, source: "project" },
|
|
4329
|
+
args.content
|
|
4330
|
+
);
|
|
4392
4331
|
try {
|
|
4393
|
-
await upsertPage(auth.instanceUrl, auth.token, scope, id, slug,
|
|
4394
|
-
|
|
4395
|
-
name: args.name,
|
|
4396
|
-
description: args.description,
|
|
4397
|
-
source: "project",
|
|
4398
|
-
draft: !!args.draft
|
|
4399
|
-
});
|
|
4400
|
-
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}`;
|
|
4401
4334
|
} catch (err) {
|
|
4402
4335
|
return `Error saving skill: ${err.message}`;
|
|
4403
4336
|
}
|
|
4404
4337
|
}
|
|
4405
4338
|
}),
|
|
4406
4339
|
gitlab_skill_promote: (0, import_plugin6.tool)({
|
|
4407
|
-
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.",
|
|
4408
4341
|
args: {
|
|
4409
4342
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4410
4343
|
name: z6.string().describe("Skill name to promote"),
|
|
4411
|
-
target: z6.enum(["published", "group"]).optional().describe(
|
|
4412
|
-
'Promotion target: "published" (default, draft\u2192published) or "group" (project\u2192group wiki)'
|
|
4413
|
-
),
|
|
4344
|
+
target: z6.enum(["published", "group"]).optional().describe('"published" (default) or "group"'),
|
|
4414
4345
|
group_id: z6.string().optional().describe("Group path (required when target is group)"),
|
|
4415
4346
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)")
|
|
4416
4347
|
},
|
|
@@ -4418,9 +4349,7 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4418
4349
|
const auth = authAndValidate(args.project_id);
|
|
4419
4350
|
const promotionTarget = args.target ?? "published";
|
|
4420
4351
|
if (promotionTarget === "group") {
|
|
4421
|
-
if (!args.group_id)
|
|
4422
|
-
return 'Error: group_id is required when target is "group".';
|
|
4423
|
-
}
|
|
4352
|
+
if (!args.group_id) return 'Error: group_id is required when target is "group".';
|
|
4424
4353
|
const projectScope = resolveScope2(args);
|
|
4425
4354
|
try {
|
|
4426
4355
|
const pages = await listWikiPages(
|
|
@@ -4431,11 +4360,9 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4431
4360
|
true
|
|
4432
4361
|
);
|
|
4433
4362
|
const skillPrefix = `${SKILLS_PREFIX}/${args.name}/`;
|
|
4434
|
-
const skillPages = pages.filter(
|
|
4435
|
-
(p) => p.slug.startsWith(skillPrefix) && p.content
|
|
4436
|
-
);
|
|
4363
|
+
const skillPages = pages.filter((p) => p.slug.startsWith(skillPrefix) && p.content);
|
|
4437
4364
|
if (skillPages.length === 0) {
|
|
4438
|
-
return `Skill "${args.name}" not found in project
|
|
4365
|
+
return `Skill "${args.name}" not found in project.`;
|
|
4439
4366
|
}
|
|
4440
4367
|
for (const page of skillPages) {
|
|
4441
4368
|
await upsertPage(
|
|
@@ -4447,54 +4374,6 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4447
4374
|
page.content
|
|
4448
4375
|
);
|
|
4449
4376
|
}
|
|
4450
|
-
const projectIndex = await readIndex(
|
|
4451
|
-
auth.instanceUrl,
|
|
4452
|
-
auth.token,
|
|
4453
|
-
projectScope.scope,
|
|
4454
|
-
projectScope.id,
|
|
4455
|
-
SKILLS_INDEX
|
|
4456
|
-
);
|
|
4457
|
-
const entry = projectIndex.find((e) => e.name === args.name);
|
|
4458
|
-
const description = entry?.description ?? "(promoted from project)";
|
|
4459
|
-
if (entry?.snippetId) {
|
|
4460
|
-
const bundleFiles = await listSnippetFiles(
|
|
4461
|
-
auth.instanceUrl,
|
|
4462
|
-
auth.token,
|
|
4463
|
-
args.project_id,
|
|
4464
|
-
entry.snippetId
|
|
4465
|
-
);
|
|
4466
|
-
for (const bf of bundleFiles) {
|
|
4467
|
-
const raw = await getSnippetFileRaw(
|
|
4468
|
-
auth.instanceUrl,
|
|
4469
|
-
auth.token,
|
|
4470
|
-
args.project_id,
|
|
4471
|
-
entry.snippetId,
|
|
4472
|
-
bf.path
|
|
4473
|
-
);
|
|
4474
|
-
await upsertPage(
|
|
4475
|
-
auth.instanceUrl,
|
|
4476
|
-
auth.token,
|
|
4477
|
-
"groups",
|
|
4478
|
-
args.group_id,
|
|
4479
|
-
`${SKILLS_PREFIX}/${args.name}/bundle/${bf.path}`,
|
|
4480
|
-
raw
|
|
4481
|
-
);
|
|
4482
|
-
}
|
|
4483
|
-
}
|
|
4484
|
-
await upsertIndexEntry(
|
|
4485
|
-
auth.instanceUrl,
|
|
4486
|
-
auth.token,
|
|
4487
|
-
"groups",
|
|
4488
|
-
args.group_id,
|
|
4489
|
-
SKILLS_INDEX,
|
|
4490
|
-
{
|
|
4491
|
-
name: args.name,
|
|
4492
|
-
description,
|
|
4493
|
-
source: `project:${args.project_id}`,
|
|
4494
|
-
snippetId: entry?.snippetId,
|
|
4495
|
-
draft: false
|
|
4496
|
-
}
|
|
4497
|
-
);
|
|
4498
4377
|
for (const page of skillPages) {
|
|
4499
4378
|
await deleteWikiPage(
|
|
4500
4379
|
auth.instanceUrl,
|
|
@@ -4504,55 +4383,30 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4504
4383
|
page.slug
|
|
4505
4384
|
);
|
|
4506
4385
|
}
|
|
4507
|
-
|
|
4508
|
-
try {
|
|
4509
|
-
await deleteProjectSnippet(
|
|
4510
|
-
auth.instanceUrl,
|
|
4511
|
-
auth.token,
|
|
4512
|
-
args.project_id,
|
|
4513
|
-
entry.snippetId
|
|
4514
|
-
);
|
|
4515
|
-
} catch {
|
|
4516
|
-
}
|
|
4517
|
-
}
|
|
4518
|
-
await removeIndexEntry(
|
|
4519
|
-
auth.instanceUrl,
|
|
4520
|
-
auth.token,
|
|
4521
|
-
projectScope.scope,
|
|
4522
|
-
projectScope.id,
|
|
4523
|
-
SKILLS_INDEX,
|
|
4524
|
-
args.name
|
|
4525
|
-
);
|
|
4526
|
-
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.`;
|
|
4527
4387
|
} catch (err) {
|
|
4528
4388
|
return `Error promoting skill to group: ${err.message}`;
|
|
4529
4389
|
}
|
|
4530
4390
|
}
|
|
4531
4391
|
const { scope, id } = resolveScope2(args);
|
|
4532
4392
|
try {
|
|
4533
|
-
const pages = await listWikiPages(
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4393
|
+
const pages = await listWikiPages(
|
|
4394
|
+
auth.instanceUrl,
|
|
4395
|
+
auth.token,
|
|
4396
|
+
scope,
|
|
4397
|
+
id,
|
|
4398
|
+
true
|
|
4537
4399
|
);
|
|
4400
|
+
const draftPrefix = `${DRAFTS_PREFIX}/${args.name}/`;
|
|
4401
|
+
const draftPages = pages.filter((p) => p.slug.startsWith(draftPrefix) && p.content);
|
|
4538
4402
|
if (draftPages.length === 0) {
|
|
4539
|
-
return `Draft skill "${args.name}" not found
|
|
4403
|
+
return `Draft skill "${args.name}" not found.`;
|
|
4540
4404
|
}
|
|
4541
|
-
const draftIndex = await readIndex(auth.instanceUrl, auth.token, scope, id, DRAFTS_INDEX);
|
|
4542
|
-
const entry = draftIndex.find((e) => e.name === args.name);
|
|
4543
|
-
const description = entry?.description ?? "(promoted from draft)";
|
|
4544
4405
|
for (const page of draftPages) {
|
|
4545
4406
|
const newSlug = page.slug.replace(DRAFTS_PREFIX, SKILLS_PREFIX);
|
|
4546
4407
|
await upsertPage(auth.instanceUrl, auth.token, scope, id, newSlug, page.content);
|
|
4547
4408
|
await deleteWikiPage(auth.instanceUrl, auth.token, scope, id, page.slug);
|
|
4548
4409
|
}
|
|
4549
|
-
await removeIndexEntry(auth.instanceUrl, auth.token, scope, id, DRAFTS_INDEX, args.name);
|
|
4550
|
-
await upsertIndexEntry(auth.instanceUrl, auth.token, scope, id, SKILLS_INDEX, {
|
|
4551
|
-
name: args.name,
|
|
4552
|
-
description,
|
|
4553
|
-
source: "project",
|
|
4554
|
-
draft: false
|
|
4555
|
-
});
|
|
4556
4410
|
return `Promoted skill "${args.name}" from draft to published.`;
|
|
4557
4411
|
} catch (err) {
|
|
4558
4412
|
return `Error promoting skill: ${err.message}`;
|
|
@@ -4560,11 +4414,11 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4560
4414
|
}
|
|
4561
4415
|
}),
|
|
4562
4416
|
gitlab_skill_discover: (0, import_plugin6.tool)({
|
|
4563
|
-
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.",
|
|
4564
4418
|
args: {
|
|
4565
|
-
query: z6.string().describe("Search query
|
|
4566
|
-
project_id: z6.string().optional().describe(PROJECT_ID_DESC2 + " Only needed for group
|
|
4567
|
-
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")
|
|
4568
4422
|
},
|
|
4569
4423
|
execute: async (args) => {
|
|
4570
4424
|
let auth = null;
|
|
@@ -4576,15 +4430,15 @@ Run \`gitlab_skill_setup(name="${args.name}")\` to extract them to .agents/skill
|
|
|
4576
4430
|
const sections = [];
|
|
4577
4431
|
if (args.group_id && auth) {
|
|
4578
4432
|
try {
|
|
4579
|
-
const
|
|
4433
|
+
const skills = await listSkills(
|
|
4580
4434
|
auth.instanceUrl,
|
|
4581
4435
|
auth.token,
|
|
4582
4436
|
"groups",
|
|
4583
4437
|
args.group_id,
|
|
4584
|
-
|
|
4438
|
+
SKILLS_PREFIX
|
|
4585
4439
|
);
|
|
4586
4440
|
const q = args.query.toLowerCase();
|
|
4587
|
-
const matches =
|
|
4441
|
+
const matches = skills.filter(
|
|
4588
4442
|
(e) => e.name.toLowerCase().includes(q) || e.description.toLowerCase().includes(q)
|
|
4589
4443
|
);
|
|
4590
4444
|
if (matches.length > 0) {
|
|
@@ -4613,19 +4467,17 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4613
4467
|
);
|
|
4614
4468
|
}
|
|
4615
4469
|
if (sections.length === 0) {
|
|
4616
|
-
return `No skills found matching "${args.query}"
|
|
4470
|
+
return `No skills found matching "${args.query}".`;
|
|
4617
4471
|
}
|
|
4618
4472
|
return sections.join("\n\n---\n\n");
|
|
4619
4473
|
}
|
|
4620
4474
|
}),
|
|
4621
4475
|
gitlab_skill_install: (0, import_plugin6.tool)({
|
|
4622
|
-
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.",
|
|
4623
4477
|
args: {
|
|
4624
4478
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4625
|
-
name: z6.string().describe(
|
|
4626
|
-
|
|
4627
|
-
),
|
|
4628
|
-
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"),
|
|
4629
4481
|
group_id: z6.string().optional().describe("Group path (required when source is group)"),
|
|
4630
4482
|
draft: z6.boolean().optional().describe("Install as draft (default: false)")
|
|
4631
4483
|
},
|
|
@@ -4633,24 +4485,45 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4633
4485
|
const auth = authAndValidate(args.project_id);
|
|
4634
4486
|
const projectScope = resolveScope2(args);
|
|
4635
4487
|
const targetPrefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
|
|
4636
|
-
const targetIndex = args.draft ? DRAFTS_INDEX : SKILLS_INDEX;
|
|
4637
4488
|
if (args.source === "skills.sh") {
|
|
4638
4489
|
const downloaded = downloadSkillFromSkillsSh(args.name);
|
|
4639
4490
|
if (!downloaded) {
|
|
4640
|
-
return `Failed to download skill "${args.name}" from skills.sh
|
|
4491
|
+
return `Failed to download skill "${args.name}" from skills.sh.`;
|
|
4641
4492
|
}
|
|
4642
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
|
+
);
|
|
4643
4518
|
await upsertPage(
|
|
4644
4519
|
auth.instanceUrl,
|
|
4645
4520
|
auth.token,
|
|
4646
4521
|
projectScope.scope,
|
|
4647
4522
|
projectScope.id,
|
|
4648
4523
|
`${targetPrefix}/${downloaded.name}/SKILL`,
|
|
4649
|
-
|
|
4524
|
+
skillBody
|
|
4650
4525
|
);
|
|
4651
4526
|
let wikiCount = 1;
|
|
4652
|
-
const mdFiles = downloaded.files.filter((f) => isMarkdownFile(f.path));
|
|
4653
|
-
const scriptFiles = downloaded.files.filter((f) => !isMarkdownFile(f.path));
|
|
4654
4527
|
for (const file of mdFiles) {
|
|
4655
4528
|
const slug = `${targetPrefix}/${downloaded.name}/${file.path.replace(/\.[^.]+$/, "")}`;
|
|
4656
4529
|
await upsertPage(
|
|
@@ -4663,44 +4536,14 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4663
4536
|
);
|
|
4664
4537
|
wikiCount++;
|
|
4665
4538
|
}
|
|
4666
|
-
let snippetId;
|
|
4667
|
-
if (scriptFiles.length > 0) {
|
|
4668
|
-
const packed = packFiles(scriptFiles);
|
|
4669
|
-
const snippet = await createProjectSnippet(
|
|
4670
|
-
auth.instanceUrl,
|
|
4671
|
-
auth.token,
|
|
4672
|
-
args.project_id,
|
|
4673
|
-
`skill:${downloaded.name}`,
|
|
4674
|
-
`Scripts for skill "${downloaded.name}" (${scriptFiles.length} files, installed from skills.sh:${args.name})`,
|
|
4675
|
-
[{ file_path: `${downloaded.name}.bundle`, content: packed }],
|
|
4676
|
-
"private"
|
|
4677
|
-
);
|
|
4678
|
-
snippetId = snippet.id;
|
|
4679
|
-
}
|
|
4680
|
-
await upsertIndexEntry(
|
|
4681
|
-
auth.instanceUrl,
|
|
4682
|
-
auth.token,
|
|
4683
|
-
projectScope.scope,
|
|
4684
|
-
projectScope.id,
|
|
4685
|
-
targetIndex,
|
|
4686
|
-
{
|
|
4687
|
-
name: downloaded.name,
|
|
4688
|
-
description: downloaded.description,
|
|
4689
|
-
source: `skills.sh:${args.name}`,
|
|
4690
|
-
snippetId,
|
|
4691
|
-
draft: !!args.draft
|
|
4692
|
-
}
|
|
4693
|
-
);
|
|
4694
4539
|
const parts = [`${wikiCount} wiki page(s)`];
|
|
4695
4540
|
if (snippetId) parts.push(`snippet #${snippetId} with ${scriptFiles.length} script(s)`);
|
|
4696
|
-
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.`;
|
|
4697
4542
|
} catch (err) {
|
|
4698
|
-
return `Error installing
|
|
4543
|
+
return `Error installing from skills.sh: ${err.message}`;
|
|
4699
4544
|
}
|
|
4700
4545
|
}
|
|
4701
|
-
if (!args.group_id)
|
|
4702
|
-
return 'Error: group_id is required when source is "group".';
|
|
4703
|
-
}
|
|
4546
|
+
if (!args.group_id) return 'Error: group_id is required when source is "group".';
|
|
4704
4547
|
try {
|
|
4705
4548
|
const groupPages = await listWikiPages(
|
|
4706
4549
|
auth.instanceUrl,
|
|
@@ -4710,9 +4553,7 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4710
4553
|
true
|
|
4711
4554
|
);
|
|
4712
4555
|
const sourcePrefix = `${SKILLS_PREFIX}/${args.name}/`;
|
|
4713
|
-
const skillPages = groupPages.filter(
|
|
4714
|
-
(p) => p.slug.startsWith(sourcePrefix) && p.content
|
|
4715
|
-
);
|
|
4556
|
+
const skillPages = groupPages.filter((p) => p.slug.startsWith(sourcePrefix) && p.content);
|
|
4716
4557
|
if (skillPages.length === 0) {
|
|
4717
4558
|
return `Skill "${args.name}" not found in group "${args.group_id}" wiki.`;
|
|
4718
4559
|
}
|
|
@@ -4727,39 +4568,17 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4727
4568
|
page.content
|
|
4728
4569
|
);
|
|
4729
4570
|
}
|
|
4730
|
-
|
|
4731
|
-
auth.instanceUrl,
|
|
4732
|
-
auth.token,
|
|
4733
|
-
"groups",
|
|
4734
|
-
args.group_id,
|
|
4735
|
-
SKILLS_INDEX
|
|
4736
|
-
);
|
|
4737
|
-
const entry = groupIndex.find((e) => e.name === args.name);
|
|
4738
|
-
const description = entry?.description ?? "(installed from group)";
|
|
4739
|
-
await upsertIndexEntry(
|
|
4740
|
-
auth.instanceUrl,
|
|
4741
|
-
auth.token,
|
|
4742
|
-
projectScope.scope,
|
|
4743
|
-
projectScope.id,
|
|
4744
|
-
targetIndex,
|
|
4745
|
-
{
|
|
4746
|
-
name: args.name,
|
|
4747
|
-
description,
|
|
4748
|
-
source: `group:${args.group_id}`,
|
|
4749
|
-
draft: !!args.draft
|
|
4750
|
-
}
|
|
4751
|
-
);
|
|
4752
|
-
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).`;
|
|
4753
4572
|
} catch (err) {
|
|
4754
|
-
return `Error installing
|
|
4573
|
+
return `Error installing from group: ${err.message}`;
|
|
4755
4574
|
}
|
|
4756
4575
|
}
|
|
4757
4576
|
}),
|
|
4758
4577
|
gitlab_skill_setup: (0, import_plugin6.tool)({
|
|
4759
|
-
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.",
|
|
4760
4579
|
args: {
|
|
4761
4580
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4762
|
-
name: z6.string().describe("Skill name to set up
|
|
4581
|
+
name: z6.string().describe("Skill name to set up"),
|
|
4763
4582
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
4764
4583
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
4765
4584
|
},
|
|
@@ -4767,29 +4586,35 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4767
4586
|
const auth = authAndValidate(args.project_id);
|
|
4768
4587
|
const { scope, id } = resolveScope2(args);
|
|
4769
4588
|
const workDir = ctx.getDirectory();
|
|
4770
|
-
const targetDir = (0,
|
|
4589
|
+
const targetDir = (0, import_path3.join)(workDir, ".agents", "skills", args.name);
|
|
4771
4590
|
try {
|
|
4772
|
-
let
|
|
4591
|
+
let skillPage = null;
|
|
4773
4592
|
for (const prefix of [SKILLS_PREFIX, DRAFTS_PREFIX]) {
|
|
4774
4593
|
try {
|
|
4775
|
-
|
|
4594
|
+
skillPage = await getWikiPage(
|
|
4776
4595
|
auth.instanceUrl,
|
|
4777
4596
|
auth.token,
|
|
4778
4597
|
scope,
|
|
4779
4598
|
id,
|
|
4780
4599
|
`${prefix}/${args.name}/SKILL`
|
|
4781
4600
|
);
|
|
4782
|
-
skillContent = page.content;
|
|
4783
4601
|
break;
|
|
4784
4602
|
} catch {
|
|
4785
4603
|
}
|
|
4786
4604
|
}
|
|
4787
|
-
if (!
|
|
4788
|
-
return `Skill "${args.name}" not found
|
|
4605
|
+
if (!skillPage) {
|
|
4606
|
+
return `Skill "${args.name}" not found. Use gitlab_skill_list to see available skills.`;
|
|
4789
4607
|
}
|
|
4790
|
-
|
|
4791
|
-
(0,
|
|
4792
|
-
|
|
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
|
+
);
|
|
4793
4618
|
let refCount = 0;
|
|
4794
4619
|
for (const prefix of [SKILLS_PREFIX, DRAFTS_PREFIX]) {
|
|
4795
4620
|
const skillPagePrefix = `${prefix}/${args.name}/`;
|
|
@@ -4798,59 +4623,36 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4798
4623
|
);
|
|
4799
4624
|
for (const page of extraPages) {
|
|
4800
4625
|
const relPath = page.slug.slice(skillPagePrefix.length);
|
|
4801
|
-
const filePath = (0,
|
|
4802
|
-
(0,
|
|
4803
|
-
(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);
|
|
4804
4629
|
refCount++;
|
|
4805
4630
|
}
|
|
4806
4631
|
}
|
|
4807
4632
|
let scriptCount = 0;
|
|
4808
|
-
|
|
4809
|
-
auth.instanceUrl,
|
|
4810
|
-
auth.token,
|
|
4811
|
-
scope,
|
|
4812
|
-
id,
|
|
4813
|
-
SKILLS_INDEX
|
|
4814
|
-
);
|
|
4815
|
-
const draftsEntries = await readIndex(
|
|
4816
|
-
auth.instanceUrl,
|
|
4817
|
-
auth.token,
|
|
4818
|
-
scope,
|
|
4819
|
-
id,
|
|
4820
|
-
DRAFTS_INDEX
|
|
4821
|
-
);
|
|
4822
|
-
const entry = [...indexEntries, ...draftsEntries].find((e) => e.name === args.name);
|
|
4823
|
-
if (entry?.snippetId) {
|
|
4633
|
+
if (meta.snippetId) {
|
|
4824
4634
|
const bundleFiles = await listSnippetFiles(
|
|
4825
4635
|
auth.instanceUrl,
|
|
4826
4636
|
auth.token,
|
|
4827
4637
|
args.project_id,
|
|
4828
|
-
|
|
4638
|
+
meta.snippetId
|
|
4829
4639
|
);
|
|
4830
4640
|
for (const bf of bundleFiles) {
|
|
4831
4641
|
const raw = await getSnippetFileRaw(
|
|
4832
4642
|
auth.instanceUrl,
|
|
4833
4643
|
auth.token,
|
|
4834
4644
|
args.project_id,
|
|
4835
|
-
|
|
4645
|
+
meta.snippetId,
|
|
4836
4646
|
bf.path
|
|
4837
4647
|
);
|
|
4838
4648
|
if (bf.path.endsWith(".bundle")) {
|
|
4839
|
-
const
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
(0,
|
|
4843
|
-
(0,
|
|
4844
|
-
if (file.path.endsWith(".sh")) {
|
|
4845
|
-
(0, import_fs2.chmodSync)(filePath, 493);
|
|
4846
|
-
}
|
|
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);
|
|
4847
4654
|
scriptCount++;
|
|
4848
4655
|
}
|
|
4849
|
-
} else {
|
|
4850
|
-
const filePath = (0, import_path2.join)(targetDir, bf.path);
|
|
4851
|
-
(0, import_fs2.mkdirSync)((0, import_path2.dirname)(filePath), { recursive: true });
|
|
4852
|
-
(0, import_fs2.writeFileSync)(filePath, raw);
|
|
4853
|
-
scriptCount++;
|
|
4854
4656
|
}
|
|
4855
4657
|
}
|
|
4856
4658
|
}
|
|
@@ -4858,18 +4660,18 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4858
4660
|
const parts = ["SKILL.md"];
|
|
4859
4661
|
if (refCount > 0) parts.push(`${refCount} reference(s)`);
|
|
4860
4662
|
if (scriptCount > 0) parts.push(`${scriptCount} script(s)`);
|
|
4861
|
-
return `Skill "${args.name}" extracted to ${targetDir} (${parts.join(", ")})
|
|
4663
|
+
return `Skill "${args.name}" extracted to ${targetDir} (${parts.join(", ")}).`;
|
|
4862
4664
|
} catch (err) {
|
|
4863
4665
|
return `Error setting up skill: ${err.message}`;
|
|
4864
4666
|
}
|
|
4865
4667
|
}
|
|
4866
4668
|
}),
|
|
4867
4669
|
gitlab_skill_delete: (0, import_plugin6.tool)({
|
|
4868
|
-
description: "Delete a skill and
|
|
4670
|
+
description: "Delete a skill and its associated snippet.\nRemoves all wiki pages under the skill directory.",
|
|
4869
4671
|
args: {
|
|
4870
4672
|
project_id: z6.string().describe(PROJECT_ID_DESC2),
|
|
4871
4673
|
name: z6.string().describe("Skill name to delete"),
|
|
4872
|
-
draft: z6.boolean().optional().describe("Delete from drafts
|
|
4674
|
+
draft: z6.boolean().optional().describe("Delete from drafts (default: false)"),
|
|
4873
4675
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
4874
4676
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
4875
4677
|
},
|
|
@@ -4877,36 +4679,36 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
|
|
|
4877
4679
|
const auth = authAndValidate(args.project_id);
|
|
4878
4680
|
const { scope, id } = resolveScope2(args);
|
|
4879
4681
|
const prefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
|
|
4880
|
-
const indexSlug = args.draft ? DRAFTS_INDEX : SKILLS_INDEX;
|
|
4881
4682
|
const skillPrefix = `${prefix}/${args.name}/`;
|
|
4882
4683
|
try {
|
|
4883
4684
|
const pages = await listWikiPages(auth.instanceUrl, auth.token, scope, id);
|
|
4884
4685
|
const skillPages = pages.filter((p) => p.slug.startsWith(skillPrefix));
|
|
4885
4686
|
if (skillPages.length === 0) {
|
|
4886
|
-
return `Skill "${args.name}" not found
|
|
4687
|
+
return `Skill "${args.name}" not found.`;
|
|
4887
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);
|
|
4888
4697
|
for (const page of skillPages) {
|
|
4889
4698
|
await deleteWikiPage(auth.instanceUrl, auth.token, scope, id, page.slug);
|
|
4890
4699
|
}
|
|
4891
|
-
|
|
4892
|
-
const entry = indexEntries.find((e) => e.name === args.name);
|
|
4893
|
-
let snippetDeleted = false;
|
|
4894
|
-
if (entry?.snippetId) {
|
|
4700
|
+
if (meta.snippetId) {
|
|
4895
4701
|
try {
|
|
4896
4702
|
await deleteProjectSnippet(
|
|
4897
4703
|
auth.instanceUrl,
|
|
4898
4704
|
auth.token,
|
|
4899
4705
|
args.project_id,
|
|
4900
|
-
|
|
4706
|
+
meta.snippetId
|
|
4901
4707
|
);
|
|
4902
|
-
snippetDeleted = true;
|
|
4903
4708
|
} catch {
|
|
4904
4709
|
}
|
|
4905
4710
|
}
|
|
4906
|
-
|
|
4907
|
-
const parts = [`${skillPages.length} wiki page(s)`];
|
|
4908
|
-
if (snippetDeleted) parts.push("snippet");
|
|
4909
|
-
return `Deleted skill "${args.name}" (${parts.join(" + ")} removed).`;
|
|
4711
|
+
return `Deleted skill "${args.name}" (${skillPages.length} page(s)${meta.snippetId ? " + snippet" : ""}).`;
|
|
4910
4712
|
} catch (err) {
|
|
4911
4713
|
return `Error deleting skill: ${err.message}`;
|
|
4912
4714
|
}
|