@percena/weft 0.4.0-next.2 → 0.4.0-next.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/action-bridge.cjs +180 -3
- package/dist/action-bridge.d.cts +112 -23
- package/dist/action-bridge.d.ts +112 -23
- package/dist/action-bridge.js +169 -2
- package/dist/chat.cjs +982 -3
- package/dist/chat.d.cts +30 -1
- package/dist/chat.d.ts +30 -1
- package/dist/chat.js +981 -2
- package/dist/index.cjs +341 -1616
- package/dist/index.d.cts +4 -274
- package/dist/index.d.ts +4 -274
- package/dist/index.js +174 -1408
- package/dist/styles/index.css +2 -212
- package/package.json +28 -55
- package/dist/auth.cjs +0 -241
- package/dist/auth.d.cts +0 -1
- package/dist/auth.d.ts +0 -1
- package/dist/auth.js +0 -208
- package/dist/automations.cjs +0 -3044
- package/dist/automations.d.cts +0 -4774
- package/dist/automations.d.ts +0 -4774
- package/dist/automations.js +0 -2965
- package/dist/factory.cjs +0 -5057
- package/dist/factory.d.cts +0 -7909
- package/dist/factory.d.ts +0 -7909
- package/dist/factory.js +0 -5008
- package/dist/local-runtime.cjs +0 -1387
- package/dist/local-runtime.d.cts +0 -3314
- package/dist/local-runtime.d.ts +0 -3314
- package/dist/local-runtime.js +0 -1345
- package/dist/providers.cjs +0 -6154
- package/dist/providers.d.cts +0 -6024
- package/dist/providers.d.ts +0 -6024
- package/dist/providers.js +0 -6110
- package/dist/server.cjs +0 -9137
- package/dist/server.d.cts +0 -9868
- package/dist/server.d.ts +0 -9868
- package/dist/server.js +0 -9118
- package/dist/skills-browser.cjs +0 -118
- package/dist/skills-browser.d.cts +0 -105
- package/dist/skills-browser.d.ts +0 -105
- package/dist/skills-browser.js +0 -88
- package/dist/skills.cjs +0 -505
- package/dist/skills.d.cts +0 -218
- package/dist/skills.d.ts +0 -218
- package/dist/skills.js +0 -458
- package/dist/sources.cjs +0 -1710
- package/dist/sources.d.cts +0 -3978
- package/dist/sources.d.ts +0 -3978
- package/dist/sources.js +0 -1675
package/dist/skills.js
DELETED
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
// ../packages/skills/dist/chunk-IAAI7RH5.js
|
|
2
|
-
var AGENTS_PLUGIN_NAME = ".agents";
|
|
3
|
-
function createSkillActivationPlan(options) {
|
|
4
|
-
const enabledSources = new Set(options.enabledSourceSlugs ?? []);
|
|
5
|
-
const selected = new Set(options.selectedSkillSlugs ?? []);
|
|
6
|
-
const promptMentions = parseSkillMentions(options.prompt ?? "");
|
|
7
|
-
const filePaths = options.filePaths ?? [];
|
|
8
|
-
const activations = [];
|
|
9
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10
|
-
for (const skill of options.skills) {
|
|
11
|
-
const reason = activationReason(skill, {
|
|
12
|
-
selected,
|
|
13
|
-
promptMentions,
|
|
14
|
-
filePaths
|
|
15
|
-
});
|
|
16
|
-
if (!reason || seen.has(skill.slug)) continue;
|
|
17
|
-
seen.add(skill.slug);
|
|
18
|
-
activations.push({ skill, reason });
|
|
19
|
-
}
|
|
20
|
-
const requiredSourceSlugs = unique(activations.flatMap(
|
|
21
|
-
(activation) => activation.skill.metadata.requiredSources ?? []
|
|
22
|
-
));
|
|
23
|
-
const missingRequiredSourceSlugs = requiredSourceSlugs.filter((source) => !enabledSources.has(source));
|
|
24
|
-
const policyExtensions = activations.flatMap(({ skill }) => (skill.metadata.alwaysAllow ?? []).map((toolName) => ({
|
|
25
|
-
toolName,
|
|
26
|
-
scope: { type: "skill", skillSlug: skill.slug }
|
|
27
|
-
})));
|
|
28
|
-
const prerequisiteFiles = activations.map(({ skill }) => `${skill.path}/SKILL.md`).filter((path) => options.prerequisiteFileExists?.(path) ?? true);
|
|
29
|
-
return {
|
|
30
|
-
activeSkillSlugs: activations.map((activation) => activation.skill.slug),
|
|
31
|
-
activations,
|
|
32
|
-
requiredSourceSlugs,
|
|
33
|
-
missingRequiredSourceSlugs,
|
|
34
|
-
policyExtensions,
|
|
35
|
-
providerInstructions: buildProviderInstructions(activations),
|
|
36
|
-
prerequisiteFiles
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function activationReason(skill, context) {
|
|
40
|
-
if (context.promptMentions.has(skill.slug)) return "prompt-mention";
|
|
41
|
-
if (context.selected.has(skill.slug)) return "host-selection";
|
|
42
|
-
if (matchesAnyGlob(skill.metadata.globs ?? [], context.filePaths)) return "file-glob";
|
|
43
|
-
return void 0;
|
|
44
|
-
}
|
|
45
|
-
function parseSkillMentions(prompt) {
|
|
46
|
-
const mentions = /* @__PURE__ */ new Set();
|
|
47
|
-
for (const match of prompt.matchAll(/@([A-Za-z0-9_-]+)/g)) {
|
|
48
|
-
mentions.add(match[1]);
|
|
49
|
-
}
|
|
50
|
-
return mentions;
|
|
51
|
-
}
|
|
52
|
-
function matchesAnyGlob(globs, filePaths) {
|
|
53
|
-
return globs.some((glob) => filePaths.some((filePath) => globMatches(glob, filePath)));
|
|
54
|
-
}
|
|
55
|
-
function buildProviderInstructions(activations) {
|
|
56
|
-
return activations.map(({ skill }) => `# ${skill.metadata.name}
|
|
57
|
-
|
|
58
|
-
${skill.content}`).join("\n\n");
|
|
59
|
-
}
|
|
60
|
-
function unique(values) {
|
|
61
|
-
return [...new Set(values)];
|
|
62
|
-
}
|
|
63
|
-
function globMatches(pattern, value) {
|
|
64
|
-
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__DOUBLE_STAR__").replace(/\*/g, "[^/]*").replace(/__DOUBLE_STAR__/g, ".*");
|
|
65
|
-
return new RegExp(`^${escaped}$`).test(value);
|
|
66
|
-
}
|
|
67
|
-
function formatSkillDirective(prerequisiteFiles) {
|
|
68
|
-
if (prerequisiteFiles.length === 0) return void 0;
|
|
69
|
-
const fileList = prerequisiteFiles.map((f) => `- ${f}`).join("\n");
|
|
70
|
-
return [
|
|
71
|
-
"Before proceeding with the user's request, you MUST read the following skill instruction files:",
|
|
72
|
-
fileList,
|
|
73
|
-
"Do not take any other action until you have read these files."
|
|
74
|
-
].join("\n");
|
|
75
|
-
}
|
|
76
|
-
function prependSkillDirective(message, prerequisiteFiles) {
|
|
77
|
-
const directive = formatSkillDirective(prerequisiteFiles);
|
|
78
|
-
if (!directive) return message;
|
|
79
|
-
return `${directive}
|
|
80
|
-
|
|
81
|
-
${message}`;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ../packages/skills/dist/index.js
|
|
85
|
-
import matter from "gray-matter";
|
|
86
|
-
import {
|
|
87
|
-
existsSync,
|
|
88
|
-
mkdirSync,
|
|
89
|
-
readFileSync,
|
|
90
|
-
readdirSync,
|
|
91
|
-
rmSync,
|
|
92
|
-
statSync,
|
|
93
|
-
writeFileSync
|
|
94
|
-
} from "fs";
|
|
95
|
-
import { homedir } from "os";
|
|
96
|
-
import { join as join2 } from "path";
|
|
97
|
-
import matter2 from "gray-matter";
|
|
98
|
-
import { join } from "path";
|
|
99
|
-
function validateSkillDefinitionContent(content, file = "SKILL.md") {
|
|
100
|
-
const errors = [];
|
|
101
|
-
const warnings = [];
|
|
102
|
-
let parsed;
|
|
103
|
-
try {
|
|
104
|
-
parsed = matter(content);
|
|
105
|
-
} catch (error) {
|
|
106
|
-
errors.push(issue(
|
|
107
|
-
file,
|
|
108
|
-
"frontmatter",
|
|
109
|
-
`Invalid skill frontmatter: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
110
|
-
"error"
|
|
111
|
-
));
|
|
112
|
-
return result(errors, warnings);
|
|
113
|
-
}
|
|
114
|
-
const data = parsed.data && typeof parsed.data === "object" ? parsed.data : {};
|
|
115
|
-
if (!isNonEmptyString(data.name)) {
|
|
116
|
-
errors.push(issue(file, "frontmatter.name", "Skill metadata requires name", "error"));
|
|
117
|
-
}
|
|
118
|
-
if (!isNonEmptyString(data.description)) {
|
|
119
|
-
errors.push(issue(file, "frontmatter.description", "Skill metadata requires description", "error"));
|
|
120
|
-
}
|
|
121
|
-
validateStringArray(data.globs, "frontmatter.globs", file, errors);
|
|
122
|
-
validateStringArray(data.alwaysAllow, "frontmatter.alwaysAllow", file, errors);
|
|
123
|
-
validateRequiredSources(data.requiredSources, file, errors);
|
|
124
|
-
if (!data.globs && !data.requiredSources) {
|
|
125
|
-
warnings.push(issue(
|
|
126
|
-
file,
|
|
127
|
-
"frontmatter",
|
|
128
|
-
"Skill has no activation metadata",
|
|
129
|
-
"warning",
|
|
130
|
-
"Add globs, requiredSources, or select the skill explicitly from the host UI."
|
|
131
|
-
));
|
|
132
|
-
}
|
|
133
|
-
if (!parsed.content.trim()) {
|
|
134
|
-
warnings.push(issue(
|
|
135
|
-
file,
|
|
136
|
-
"content",
|
|
137
|
-
"Skill body is empty",
|
|
138
|
-
"warning",
|
|
139
|
-
"Add provider instructions to make the skill useful when activated."
|
|
140
|
-
));
|
|
141
|
-
}
|
|
142
|
-
return result(errors, warnings);
|
|
143
|
-
}
|
|
144
|
-
function validateRequiredSources(value, file, errors) {
|
|
145
|
-
if (value === void 0) return;
|
|
146
|
-
if (typeof value === "string") return;
|
|
147
|
-
validateStringArray(value, "frontmatter.requiredSources", file, errors);
|
|
148
|
-
}
|
|
149
|
-
function validateStringArray(value, path, file, errors) {
|
|
150
|
-
if (value === void 0) return;
|
|
151
|
-
if (!Array.isArray(value)) {
|
|
152
|
-
errors.push(issue(file, path, `${leafName(path)} must be an array of strings`, "error"));
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
value.forEach((entry, index) => {
|
|
156
|
-
if (typeof entry !== "string") {
|
|
157
|
-
errors.push(issue(file, `${path}[${index}]`, `${leafName(path)} entries must be strings`, "error"));
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
function issue(file, path, message, severity, suggestion) {
|
|
162
|
-
return {
|
|
163
|
-
file,
|
|
164
|
-
path,
|
|
165
|
-
message,
|
|
166
|
-
severity,
|
|
167
|
-
...suggestion ? { suggestion } : {}
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
function result(errors, warnings) {
|
|
171
|
-
return {
|
|
172
|
-
valid: errors.length === 0,
|
|
173
|
-
errors,
|
|
174
|
-
warnings
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
function isNonEmptyString(value) {
|
|
178
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
179
|
-
}
|
|
180
|
-
function leafName(path) {
|
|
181
|
-
return path.split(".").at(-1) ?? path;
|
|
182
|
-
}
|
|
183
|
-
function getWorkspaceSkillsPath(workspaceRoot) {
|
|
184
|
-
return join(workspaceRoot, "skills");
|
|
185
|
-
}
|
|
186
|
-
function validateIconValue(value, context) {
|
|
187
|
-
if (value === void 0 || value === null) return void 0;
|
|
188
|
-
if (typeof value !== "string") return void 0;
|
|
189
|
-
if (/^[\p{Emoji}\u200d\ufe0f]+$/u.test(value)) return value;
|
|
190
|
-
if (value.startsWith("http://") || value.startsWith("https://")) return value;
|
|
191
|
-
console.warn(`[${context}] Invalid icon value: "${value}". Only emoji and URLs are supported.`);
|
|
192
|
-
return void 0;
|
|
193
|
-
}
|
|
194
|
-
function findIconFile(dirPath) {
|
|
195
|
-
return void 0;
|
|
196
|
-
}
|
|
197
|
-
async function downloadIcon(dirPath, iconUrl, context) {
|
|
198
|
-
console.warn(`[${context}] Icon download stub: would download ${iconUrl}`);
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
function needsIconDownload(icon, iconPath) {
|
|
202
|
-
if (!icon) return false;
|
|
203
|
-
if (icon.startsWith("http://") || icon.startsWith("https://")) {
|
|
204
|
-
return !iconPath;
|
|
205
|
-
}
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
var GLOBAL_AGENT_SKILLS_DIR = join2(homedir(), ".agents", "skills");
|
|
209
|
-
var PROJECT_AGENT_SKILLS_DIR = ".agents/skills";
|
|
210
|
-
function normalizeRequiredSources(value) {
|
|
211
|
-
const asArray = typeof value === "string" ? [value] : Array.isArray(value) ? value : void 0;
|
|
212
|
-
if (!asArray) return void 0;
|
|
213
|
-
const normalized = Array.from(new Set(
|
|
214
|
-
asArray.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean)
|
|
215
|
-
));
|
|
216
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
217
|
-
}
|
|
218
|
-
function parseSkillFile(content) {
|
|
219
|
-
try {
|
|
220
|
-
const parsed = matter2(content);
|
|
221
|
-
if (!parsed.data.name || !parsed.data.description) {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
const icon = validateIconValue(parsed.data.icon, "Skills");
|
|
225
|
-
return {
|
|
226
|
-
metadata: {
|
|
227
|
-
name: parsed.data.name,
|
|
228
|
-
description: parsed.data.description,
|
|
229
|
-
globs: parsed.data.globs,
|
|
230
|
-
alwaysAllow: parsed.data.alwaysAllow,
|
|
231
|
-
icon,
|
|
232
|
-
requiredSources: normalizeRequiredSources(parsed.data.requiredSources)
|
|
233
|
-
},
|
|
234
|
-
body: parsed.content
|
|
235
|
-
};
|
|
236
|
-
} catch {
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
function loadSkillFromDir(skillsDir, slug, source) {
|
|
241
|
-
const skillDir = join2(skillsDir, slug);
|
|
242
|
-
const skillFile = join2(skillDir, "SKILL.md");
|
|
243
|
-
if (!existsSync(skillDir) || !statSync(skillDir).isDirectory()) {
|
|
244
|
-
return null;
|
|
245
|
-
}
|
|
246
|
-
if (!existsSync(skillFile)) {
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
let content;
|
|
250
|
-
try {
|
|
251
|
-
content = readFileSync(skillFile, "utf-8");
|
|
252
|
-
} catch {
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
const parsed = parseSkillFile(content);
|
|
256
|
-
if (!parsed) {
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
return {
|
|
260
|
-
slug,
|
|
261
|
-
metadata: parsed.metadata,
|
|
262
|
-
content: parsed.body,
|
|
263
|
-
iconPath: findIconFile(skillDir),
|
|
264
|
-
path: skillDir,
|
|
265
|
-
source
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
function loadSkillsFromDir(skillsDir, source) {
|
|
269
|
-
if (!existsSync(skillsDir)) {
|
|
270
|
-
return [];
|
|
271
|
-
}
|
|
272
|
-
const skills = [];
|
|
273
|
-
try {
|
|
274
|
-
const entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
275
|
-
for (const entry of entries) {
|
|
276
|
-
if (!entry.isDirectory()) continue;
|
|
277
|
-
const skill = loadSkillFromDir(skillsDir, entry.name, source);
|
|
278
|
-
if (skill) {
|
|
279
|
-
skills.push(skill);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
} catch {
|
|
283
|
-
}
|
|
284
|
-
return skills;
|
|
285
|
-
}
|
|
286
|
-
function loadSkill(workspaceRoot, slug) {
|
|
287
|
-
const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
|
|
288
|
-
return loadSkillFromDir(skillsDir, slug, "workspace");
|
|
289
|
-
}
|
|
290
|
-
function loadWorkspaceSkills(workspaceRoot) {
|
|
291
|
-
const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
|
|
292
|
-
return loadSkillsFromDir(skillsDir, "workspace");
|
|
293
|
-
}
|
|
294
|
-
var skillsCache = /* @__PURE__ */ new Map();
|
|
295
|
-
var SKILLS_CACHE_TTL = 5 * 6e4;
|
|
296
|
-
function invalidateSkillsCache() {
|
|
297
|
-
skillsCache.clear();
|
|
298
|
-
}
|
|
299
|
-
function loadAllSkills(workspaceRoot, projectRoot) {
|
|
300
|
-
const cacheKey = `${workspaceRoot}::${projectRoot ?? ""}`;
|
|
301
|
-
const now = Date.now();
|
|
302
|
-
const cached = skillsCache.get(cacheKey);
|
|
303
|
-
if (cached && now - cached.ts < SKILLS_CACHE_TTL) {
|
|
304
|
-
return cached.skills;
|
|
305
|
-
}
|
|
306
|
-
const skillsBySlug = /* @__PURE__ */ new Map();
|
|
307
|
-
for (const skill of loadSkillsFromDir(GLOBAL_AGENT_SKILLS_DIR, "global")) {
|
|
308
|
-
skillsBySlug.set(skill.slug, skill);
|
|
309
|
-
}
|
|
310
|
-
for (const skill of loadWorkspaceSkills(workspaceRoot)) {
|
|
311
|
-
skillsBySlug.set(skill.slug, skill);
|
|
312
|
-
}
|
|
313
|
-
if (projectRoot) {
|
|
314
|
-
const projectSkillsDir = join2(projectRoot, PROJECT_AGENT_SKILLS_DIR);
|
|
315
|
-
for (const skill of loadSkillsFromDir(projectSkillsDir, "project")) {
|
|
316
|
-
skillsBySlug.set(skill.slug, skill);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
const result2 = Array.from(skillsBySlug.values());
|
|
320
|
-
skillsCache.set(cacheKey, { skills: result2, ts: now });
|
|
321
|
-
return result2;
|
|
322
|
-
}
|
|
323
|
-
function loadSkillBySlug(workspaceRoot, slug, projectRoot) {
|
|
324
|
-
if (projectRoot) {
|
|
325
|
-
const projectSkillsDir = join2(projectRoot, PROJECT_AGENT_SKILLS_DIR);
|
|
326
|
-
const skill = loadSkillFromDir(projectSkillsDir, slug, "project");
|
|
327
|
-
if (skill) return skill;
|
|
328
|
-
}
|
|
329
|
-
const workspaceSkill = loadSkillFromDir(getWorkspaceSkillsPath(workspaceRoot), slug, "workspace");
|
|
330
|
-
if (workspaceSkill) return workspaceSkill;
|
|
331
|
-
return loadSkillFromDir(GLOBAL_AGENT_SKILLS_DIR, slug, "global");
|
|
332
|
-
}
|
|
333
|
-
function getSkillIconPath(workspaceRoot, slug) {
|
|
334
|
-
const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
|
|
335
|
-
const skillDir = join2(skillsDir, slug);
|
|
336
|
-
if (!existsSync(skillDir)) {
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
return findIconFile(skillDir) || null;
|
|
340
|
-
}
|
|
341
|
-
function deleteSkill(workspaceRoot, slug) {
|
|
342
|
-
const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
|
|
343
|
-
const skillDir = join2(skillsDir, slug);
|
|
344
|
-
if (!existsSync(skillDir)) {
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
|
-
try {
|
|
348
|
-
rmSync(skillDir, { recursive: true });
|
|
349
|
-
return true;
|
|
350
|
-
} catch {
|
|
351
|
-
return false;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
function createSkill(workspaceRoot, input) {
|
|
355
|
-
const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
|
|
356
|
-
const skillDir = join2(skillsDir, input.slug);
|
|
357
|
-
const skillFile = join2(skillDir, "SKILL.md");
|
|
358
|
-
const frontmatter = {
|
|
359
|
-
name: input.name,
|
|
360
|
-
description: input.description
|
|
361
|
-
};
|
|
362
|
-
if (input.globs) frontmatter.globs = input.globs;
|
|
363
|
-
if (input.alwaysAllow) frontmatter.alwaysAllow = input.alwaysAllow;
|
|
364
|
-
if (input.icon) {
|
|
365
|
-
const validated = validateIconValue(input.icon, "Skills");
|
|
366
|
-
if (validated) frontmatter.icon = validated;
|
|
367
|
-
}
|
|
368
|
-
if (input.requiredSources) frontmatter.requiredSources = input.requiredSources;
|
|
369
|
-
const fileContent = matter2.stringify(input.content, frontmatter);
|
|
370
|
-
mkdirSync(skillDir, { recursive: true });
|
|
371
|
-
writeFileSync(skillFile, fileContent, "utf-8");
|
|
372
|
-
invalidateSkillsCache();
|
|
373
|
-
const loaded = loadSkillFromDir(skillsDir, input.slug, "workspace");
|
|
374
|
-
if (!loaded) {
|
|
375
|
-
throw new Error(`Failed to load newly created skill: ${input.slug}`);
|
|
376
|
-
}
|
|
377
|
-
return loaded;
|
|
378
|
-
}
|
|
379
|
-
function updateSkill(workspaceRoot, slug, patch) {
|
|
380
|
-
const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
|
|
381
|
-
const existing = loadSkillFromDir(skillsDir, slug, "workspace");
|
|
382
|
-
if (!existing) {
|
|
383
|
-
throw new Error(`Skill not found: ${slug}`);
|
|
384
|
-
}
|
|
385
|
-
const frontmatter = {
|
|
386
|
-
name: patch.name ?? existing.metadata.name,
|
|
387
|
-
description: patch.description ?? existing.metadata.description
|
|
388
|
-
};
|
|
389
|
-
const globs = patch.globs ?? existing.metadata.globs;
|
|
390
|
-
if (globs) frontmatter.globs = globs;
|
|
391
|
-
const alwaysAllow = patch.alwaysAllow ?? existing.metadata.alwaysAllow;
|
|
392
|
-
if (alwaysAllow) frontmatter.alwaysAllow = alwaysAllow;
|
|
393
|
-
const icon = patch.icon ?? existing.metadata.icon;
|
|
394
|
-
if (icon) {
|
|
395
|
-
const validated = validateIconValue(icon, "Skills");
|
|
396
|
-
if (validated) frontmatter.icon = validated;
|
|
397
|
-
}
|
|
398
|
-
const requiredSources = patch.requiredSources ?? existing.metadata.requiredSources;
|
|
399
|
-
if (requiredSources) frontmatter.requiredSources = requiredSources;
|
|
400
|
-
const content = patch.content ?? existing.content;
|
|
401
|
-
const fileContent = matter2.stringify(content, frontmatter);
|
|
402
|
-
const skillFile = join2(skillsDir, slug, "SKILL.md");
|
|
403
|
-
writeFileSync(skillFile, fileContent, "utf-8");
|
|
404
|
-
invalidateSkillsCache();
|
|
405
|
-
const loaded = loadSkillFromDir(skillsDir, slug, "workspace");
|
|
406
|
-
if (!loaded) {
|
|
407
|
-
throw new Error(`Failed to load updated skill: ${slug}`);
|
|
408
|
-
}
|
|
409
|
-
return loaded;
|
|
410
|
-
}
|
|
411
|
-
function skillExists(workspaceRoot, slug) {
|
|
412
|
-
const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
|
|
413
|
-
const skillDir = join2(skillsDir, slug);
|
|
414
|
-
const skillFile = join2(skillDir, "SKILL.md");
|
|
415
|
-
return existsSync(skillDir) && existsSync(skillFile);
|
|
416
|
-
}
|
|
417
|
-
function listSkillSlugs(workspaceRoot) {
|
|
418
|
-
const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
|
|
419
|
-
if (!existsSync(skillsDir)) {
|
|
420
|
-
return [];
|
|
421
|
-
}
|
|
422
|
-
try {
|
|
423
|
-
return readdirSync(skillsDir, { withFileTypes: true }).filter((entry) => {
|
|
424
|
-
if (!entry.isDirectory()) return false;
|
|
425
|
-
const skillFile = join2(skillsDir, entry.name, "SKILL.md");
|
|
426
|
-
return existsSync(skillFile);
|
|
427
|
-
}).map((entry) => entry.name);
|
|
428
|
-
} catch {
|
|
429
|
-
return [];
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
async function downloadSkillIcon(skillDir, iconUrl) {
|
|
433
|
-
return downloadIcon(skillDir, iconUrl, "Skills");
|
|
434
|
-
}
|
|
435
|
-
function skillNeedsIconDownload(skill) {
|
|
436
|
-
return needsIconDownload(skill.metadata.icon, skill.iconPath);
|
|
437
|
-
}
|
|
438
|
-
export {
|
|
439
|
-
AGENTS_PLUGIN_NAME,
|
|
440
|
-
GLOBAL_AGENT_SKILLS_DIR,
|
|
441
|
-
PROJECT_AGENT_SKILLS_DIR,
|
|
442
|
-
createSkill,
|
|
443
|
-
createSkillActivationPlan,
|
|
444
|
-
deleteSkill,
|
|
445
|
-
downloadSkillIcon,
|
|
446
|
-
formatSkillDirective,
|
|
447
|
-
getSkillIconPath,
|
|
448
|
-
invalidateSkillsCache,
|
|
449
|
-
listSkillSlugs,
|
|
450
|
-
loadAllSkills,
|
|
451
|
-
loadSkill,
|
|
452
|
-
loadSkillBySlug,
|
|
453
|
-
prependSkillDirective,
|
|
454
|
-
skillExists,
|
|
455
|
-
skillNeedsIconDownload,
|
|
456
|
-
updateSkill,
|
|
457
|
-
validateSkillDefinitionContent
|
|
458
|
-
};
|