skill-tree 0.1.5 → 0.1.6
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/cli/index.js +1827 -1410
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +9539 -1658
- package/dist/cli/index.mjs.map +1 -0
- package/dist/index.d.mts +316 -7
- package/dist/index.d.ts +316 -7
- package/dist/index.js +1539 -92
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +9614 -70
- package/dist/index.mjs.map +1 -0
- package/package.json +2 -1
- package/dist/chunk-3SRB47JW.mjs +0 -8344
- package/dist/chunk-43YOKLZP.mjs +0 -6081
- package/dist/chunk-4AGZU52D.mjs +0 -7918
- package/dist/chunk-4OC5QFIF.mjs +0 -11267
- package/dist/chunk-55SMGVTP.mjs +0 -7126
- package/dist/chunk-6FX4IK4Z.mjs +0 -5368
- package/dist/chunk-7EGDKOHV.mjs +0 -9439
- package/dist/chunk-7LMOQW5H.mjs +0 -4893
- package/dist/chunk-7QIQJVNP.mjs +0 -14206
- package/dist/chunk-7VB4ZRZO.mjs +0 -7127
- package/dist/chunk-BPVRW25O.mjs +0 -6089
- package/dist/chunk-CI4476KM.mjs +0 -6607
- package/dist/chunk-DDXYQ74I.mjs +0 -13969
- package/dist/chunk-DQOFJXBX.mjs +0 -6595
- package/dist/chunk-E2CVK23F.mjs +0 -8751
- package/dist/chunk-F3YEUQAP.mjs +0 -654
- package/dist/chunk-FKJJ4RJG.mjs +0 -13874
- package/dist/chunk-II7DECZQ.mjs +0 -9111
- package/dist/chunk-INKVOZXK.mjs +0 -15898
- package/dist/chunk-K6NRCSAZ.mjs +0 -4355
- package/dist/chunk-OYHYXKXO.mjs +0 -7297
- package/dist/chunk-PDPN7FW7.mjs +0 -1045
- package/dist/chunk-TEUB6DZR.mjs +0 -6453
- package/dist/chunk-TWPEHDW4.mjs +0 -1067
- package/dist/chunk-Y54UK2J3.mjs +0 -13071
- package/dist/chunk-ZQVS7MQK.mjs +0 -6081
- package/dist/sqlite-OLU72GHB.mjs +0 -6
- package/dist/sqlite-XJRPMNAJ.mjs +0 -6
- package/dist/sync-BSWMMDA6.mjs +0 -14
package/dist/chunk-F3YEUQAP.mjs
DELETED
|
@@ -1,654 +0,0 @@
|
|
|
1
|
-
// src/agents/sync.ts
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
|
|
5
|
-
// src/agents/types.ts
|
|
6
|
-
var DEFAULT_AGENTS_CONFIG = {
|
|
7
|
-
format: "xml",
|
|
8
|
-
includeIds: true,
|
|
9
|
-
includeVersions: true,
|
|
10
|
-
groupByTags: false
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
// src/agents/generator.ts
|
|
14
|
-
var AgentsGenerator = class {
|
|
15
|
-
constructor(config) {
|
|
16
|
-
this.config = { ...DEFAULT_AGENTS_CONFIG, ...config };
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Generate AGENTS.md content from a skill bank
|
|
20
|
-
*/
|
|
21
|
-
async generate(storage) {
|
|
22
|
-
let skills = await storage.listSkills({
|
|
23
|
-
status: this.config.filter?.status || ["active"]
|
|
24
|
-
});
|
|
25
|
-
skills = this.filterSkills(skills, this.config.filter);
|
|
26
|
-
skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
27
|
-
const sections = [];
|
|
28
|
-
if (this.config.header) {
|
|
29
|
-
sections.push(this.config.header);
|
|
30
|
-
} else {
|
|
31
|
-
sections.push(this.generateDefaultHeader());
|
|
32
|
-
}
|
|
33
|
-
if (this.config.groupByTags) {
|
|
34
|
-
sections.push(this.generateGroupedSkills(skills));
|
|
35
|
-
} else {
|
|
36
|
-
sections.push(this.generateSkillsList(skills));
|
|
37
|
-
}
|
|
38
|
-
if (this.config.footer) {
|
|
39
|
-
sections.push(this.config.footer);
|
|
40
|
-
}
|
|
41
|
-
return sections.join("\n\n");
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Generate content for a single skill
|
|
45
|
-
*/
|
|
46
|
-
generateSkill(skill) {
|
|
47
|
-
switch (this.config.format) {
|
|
48
|
-
case "xml":
|
|
49
|
-
return this.generateXmlSkill(skill);
|
|
50
|
-
case "markdown":
|
|
51
|
-
return this.generateMarkdownSkill(skill);
|
|
52
|
-
case "json":
|
|
53
|
-
return this.generateJsonSkill(skill);
|
|
54
|
-
default:
|
|
55
|
-
return this.generateXmlSkill(skill);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Generate skills list
|
|
60
|
-
*/
|
|
61
|
-
generateSkillsList(skills) {
|
|
62
|
-
return skills.map((skill) => this.generateSkill(skill)).join("\n\n");
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Generate skills grouped by tags
|
|
66
|
-
*/
|
|
67
|
-
generateGroupedSkills(skills) {
|
|
68
|
-
const tagGroups = /* @__PURE__ */ new Map();
|
|
69
|
-
const untagged = [];
|
|
70
|
-
for (const skill of skills) {
|
|
71
|
-
if (skill.tags.length === 0) {
|
|
72
|
-
untagged.push(skill);
|
|
73
|
-
} else {
|
|
74
|
-
const primaryTag = skill.tags[0];
|
|
75
|
-
if (!tagGroups.has(primaryTag)) {
|
|
76
|
-
tagGroups.set(primaryTag, []);
|
|
77
|
-
}
|
|
78
|
-
tagGroups.get(primaryTag).push(skill);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
const sections = [];
|
|
82
|
-
const sortedTags = Array.from(tagGroups.keys()).sort();
|
|
83
|
-
for (const tag of sortedTags) {
|
|
84
|
-
const tagSkills = tagGroups.get(tag);
|
|
85
|
-
sections.push(`## ${this.formatTagName(tag)}
|
|
86
|
-
|
|
87
|
-
${this.generateSkillsList(tagSkills)}`);
|
|
88
|
-
}
|
|
89
|
-
if (untagged.length > 0) {
|
|
90
|
-
sections.push(`## Other
|
|
91
|
-
|
|
92
|
-
${this.generateSkillsList(untagged)}`);
|
|
93
|
-
}
|
|
94
|
-
return sections.join("\n\n");
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Generate XML format skill (Claude Code native format)
|
|
98
|
-
*/
|
|
99
|
-
generateXmlSkill(skill) {
|
|
100
|
-
const lines = [];
|
|
101
|
-
const attrs = [];
|
|
102
|
-
if (this.config.includeIds) {
|
|
103
|
-
attrs.push(`id="${this.escapeXml(skill.id)}"`);
|
|
104
|
-
}
|
|
105
|
-
if (this.config.includeVersions) {
|
|
106
|
-
attrs.push(`version="${this.escapeXml(skill.version)}"`);
|
|
107
|
-
}
|
|
108
|
-
const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
109
|
-
lines.push(`<skill${attrStr}>`);
|
|
110
|
-
lines.push(` <name>${this.escapeXml(skill.name)}</name>`);
|
|
111
|
-
if (skill.description) {
|
|
112
|
-
lines.push(` <description>${this.escapeXml(skill.description)}</description>`);
|
|
113
|
-
}
|
|
114
|
-
if (skill.instructions) {
|
|
115
|
-
lines.push(` <content>
|
|
116
|
-
${this.indentContent(skill.instructions, 4)}
|
|
117
|
-
</content>`);
|
|
118
|
-
}
|
|
119
|
-
if (skill.tags.length > 0) {
|
|
120
|
-
lines.push(` <tags>${skill.tags.map((t) => this.escapeXml(t)).join(", ")}</tags>`);
|
|
121
|
-
}
|
|
122
|
-
lines.push("</skill>");
|
|
123
|
-
return lines.join("\n");
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Generate Markdown format skill
|
|
127
|
-
*/
|
|
128
|
-
generateMarkdownSkill(skill) {
|
|
129
|
-
const lines = [];
|
|
130
|
-
const idSuffix = this.config.includeIds ? ` {#${skill.id}}` : "";
|
|
131
|
-
const versionSuffix = this.config.includeVersions ? ` (v${skill.version})` : "";
|
|
132
|
-
lines.push(`### ${skill.name}${versionSuffix}${idSuffix}`);
|
|
133
|
-
lines.push("");
|
|
134
|
-
if (skill.description) {
|
|
135
|
-
lines.push(`*${skill.description}*`);
|
|
136
|
-
lines.push("");
|
|
137
|
-
}
|
|
138
|
-
if (skill.instructions) {
|
|
139
|
-
lines.push(skill.instructions);
|
|
140
|
-
lines.push("");
|
|
141
|
-
}
|
|
142
|
-
if (skill.tags.length > 0) {
|
|
143
|
-
lines.push(`Tags: ${skill.tags.map((t) => `\`${t}\``).join(", ")}`);
|
|
144
|
-
}
|
|
145
|
-
return lines.join("\n");
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Generate JSON format skill
|
|
149
|
-
*/
|
|
150
|
-
generateJsonSkill(skill) {
|
|
151
|
-
const obj = {
|
|
152
|
-
name: skill.name,
|
|
153
|
-
description: skill.description
|
|
154
|
-
};
|
|
155
|
-
if (this.config.includeIds) {
|
|
156
|
-
obj.id = skill.id;
|
|
157
|
-
}
|
|
158
|
-
if (this.config.includeVersions) {
|
|
159
|
-
obj.version = skill.version;
|
|
160
|
-
}
|
|
161
|
-
if (skill.instructions) {
|
|
162
|
-
obj.instructions = skill.instructions;
|
|
163
|
-
}
|
|
164
|
-
if (skill.tags.length > 0) {
|
|
165
|
-
obj.tags = skill.tags;
|
|
166
|
-
}
|
|
167
|
-
return "```json\n" + JSON.stringify(obj, null, 2) + "\n```";
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Filter skills based on selector
|
|
171
|
-
*/
|
|
172
|
-
filterSkills(skills, filter) {
|
|
173
|
-
if (!filter) return skills;
|
|
174
|
-
let result = skills;
|
|
175
|
-
if (filter.ids && filter.ids.length > 0) {
|
|
176
|
-
result = result.filter((s) => filter.ids.includes(s.id));
|
|
177
|
-
}
|
|
178
|
-
if (filter.tags && filter.tags.length > 0) {
|
|
179
|
-
result = result.filter((s) => s.tags.some((t) => filter.tags.includes(t)));
|
|
180
|
-
}
|
|
181
|
-
if (filter.minSuccessRate !== void 0) {
|
|
182
|
-
result = result.filter((s) => s.metrics.successRate >= filter.minSuccessRate);
|
|
183
|
-
}
|
|
184
|
-
if (filter.limit) {
|
|
185
|
-
result = result.slice(0, filter.limit);
|
|
186
|
-
}
|
|
187
|
-
return result;
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Generate default header
|
|
191
|
-
*/
|
|
192
|
-
generateDefaultHeader() {
|
|
193
|
-
return `# Agent Skills
|
|
194
|
-
|
|
195
|
-
This file contains skills for the AI agent. Each skill describes a pattern for solving a specific type of problem.
|
|
196
|
-
|
|
197
|
-
---`;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Format tag name for section header
|
|
201
|
-
*/
|
|
202
|
-
formatTagName(tag) {
|
|
203
|
-
return tag.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Escape XML special characters
|
|
207
|
-
*/
|
|
208
|
-
escapeXml(str) {
|
|
209
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Indent content
|
|
213
|
-
*/
|
|
214
|
-
indentContent(content, spaces) {
|
|
215
|
-
const indent = " ".repeat(spaces);
|
|
216
|
-
return content.split("\n").map((line) => indent + line).join("\n");
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
function createAgentsGenerator(config) {
|
|
220
|
-
return new AgentsGenerator(config);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// src/agents/parser.ts
|
|
224
|
-
var AgentsParser = class {
|
|
225
|
-
/**
|
|
226
|
-
* Parse AGENTS.md content
|
|
227
|
-
*/
|
|
228
|
-
parse(content) {
|
|
229
|
-
const warnings = [];
|
|
230
|
-
const skills = [];
|
|
231
|
-
const xmlSkills = this.parseXmlSkills(content);
|
|
232
|
-
if (xmlSkills.length > 0) {
|
|
233
|
-
skills.push(...xmlSkills);
|
|
234
|
-
}
|
|
235
|
-
const mdSkills = this.parseMarkdownSkills(content, xmlSkills);
|
|
236
|
-
if (mdSkills.length > 0) {
|
|
237
|
-
const existingNames = new Set(skills.map((s) => s.name.toLowerCase()));
|
|
238
|
-
for (const skill of mdSkills) {
|
|
239
|
-
if (!existingNames.has(skill.name.toLowerCase())) {
|
|
240
|
-
skills.push(skill);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
const header = this.extractHeader(content, skills);
|
|
245
|
-
const footer = this.extractFooter(content, skills);
|
|
246
|
-
if (skills.length === 0) {
|
|
247
|
-
warnings.push("No skills found in AGENTS.md");
|
|
248
|
-
}
|
|
249
|
-
return { header, skills, footer, warnings };
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Convert parsed skill to Skill type
|
|
253
|
-
*/
|
|
254
|
-
toSkill(parsed, defaults) {
|
|
255
|
-
const now = /* @__PURE__ */ new Date();
|
|
256
|
-
const id = parsed.id || this.generateId(parsed.name);
|
|
257
|
-
let instructions = parsed.instructions || "";
|
|
258
|
-
if (!instructions) {
|
|
259
|
-
const instructionParts = [];
|
|
260
|
-
if (parsed.problem) instructionParts.push(parsed.problem);
|
|
261
|
-
if (parsed.solution) instructionParts.push(parsed.solution);
|
|
262
|
-
if (parsed.verification) instructionParts.push(parsed.verification);
|
|
263
|
-
if (parsed.notes) instructionParts.push(parsed.notes);
|
|
264
|
-
instructions = instructionParts.length > 0 ? instructionParts.join("\n\n") : "";
|
|
265
|
-
}
|
|
266
|
-
return {
|
|
267
|
-
id,
|
|
268
|
-
name: parsed.name,
|
|
269
|
-
version: parsed.version || "1.0.0",
|
|
270
|
-
description: parsed.description || "",
|
|
271
|
-
instructions,
|
|
272
|
-
author: defaults?.author || "imported",
|
|
273
|
-
tags: parsed.tags || [],
|
|
274
|
-
createdAt: defaults?.createdAt || now,
|
|
275
|
-
updatedAt: now,
|
|
276
|
-
status: "active",
|
|
277
|
-
metrics: defaults?.metrics || {
|
|
278
|
-
usageCount: 0,
|
|
279
|
-
successRate: 0,
|
|
280
|
-
feedbackScores: []
|
|
281
|
-
},
|
|
282
|
-
source: {
|
|
283
|
-
type: "imported",
|
|
284
|
-
location: "AGENTS.md",
|
|
285
|
-
importedAt: now
|
|
286
|
-
},
|
|
287
|
-
...defaults
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Parse XML format skills
|
|
292
|
-
*/
|
|
293
|
-
parseXmlSkills(content) {
|
|
294
|
-
const skills = [];
|
|
295
|
-
const skillRegex = /<skill([^>]*)>([\s\S]*?)<\/skill>/gi;
|
|
296
|
-
let match;
|
|
297
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
298
|
-
const attrs = match[1];
|
|
299
|
-
const body = match[2];
|
|
300
|
-
const skill = {
|
|
301
|
-
rawContent: match[0],
|
|
302
|
-
name: this.extractXmlTag(body, "name") || "Unnamed Skill",
|
|
303
|
-
description: this.extractXmlTag(body, "description") || ""
|
|
304
|
-
};
|
|
305
|
-
const idMatch = /id="([^"]+)"/.exec(attrs);
|
|
306
|
-
if (idMatch) {
|
|
307
|
-
skill.id = idMatch[1];
|
|
308
|
-
}
|
|
309
|
-
const versionMatch = /version="([^"]+)"/.exec(attrs);
|
|
310
|
-
if (versionMatch) {
|
|
311
|
-
skill.version = versionMatch[1];
|
|
312
|
-
}
|
|
313
|
-
const contentTag = this.extractXmlTag(body, "content");
|
|
314
|
-
if (contentTag) {
|
|
315
|
-
skill.instructions = contentTag;
|
|
316
|
-
} else {
|
|
317
|
-
skill.problem = this.extractXmlTag(body, "problem");
|
|
318
|
-
skill.solution = this.extractXmlTag(body, "solution");
|
|
319
|
-
skill.verification = this.extractXmlTag(body, "verification");
|
|
320
|
-
skill.notes = this.extractXmlTag(body, "notes");
|
|
321
|
-
}
|
|
322
|
-
const tagsStr = this.extractXmlTag(body, "tags");
|
|
323
|
-
if (tagsStr) {
|
|
324
|
-
skill.tags = tagsStr.split(",").map((t) => t.trim()).filter((t) => t);
|
|
325
|
-
}
|
|
326
|
-
skills.push(skill);
|
|
327
|
-
}
|
|
328
|
-
return skills;
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Parse Markdown format skills
|
|
332
|
-
*/
|
|
333
|
-
parseMarkdownSkills(content, existingSkills) {
|
|
334
|
-
const skills = [];
|
|
335
|
-
const sections = content.split(/(?=^###\s)/m);
|
|
336
|
-
for (const section of sections) {
|
|
337
|
-
const headerMatch = /^###\s+(.+?)(?:\s+\(v([\d.]+)\))?(?:\s+\{#([^}]+)\})?\s*$/m.exec(section);
|
|
338
|
-
if (!headerMatch) continue;
|
|
339
|
-
const name = headerMatch[1].trim();
|
|
340
|
-
const version = headerMatch[2];
|
|
341
|
-
const id = headerMatch[3];
|
|
342
|
-
if (existingSkills.some((s) => s.name.toLowerCase() === name.toLowerCase())) {
|
|
343
|
-
continue;
|
|
344
|
-
}
|
|
345
|
-
const skill = {
|
|
346
|
-
rawContent: section,
|
|
347
|
-
name,
|
|
348
|
-
description: ""
|
|
349
|
-
};
|
|
350
|
-
if (version) skill.version = version;
|
|
351
|
-
if (id) skill.id = id;
|
|
352
|
-
const descMatch = /^\*([^*]+)\*$/m.exec(section);
|
|
353
|
-
if (descMatch) {
|
|
354
|
-
skill.description = descMatch[1];
|
|
355
|
-
}
|
|
356
|
-
const bodyStart = section.indexOf("\n", section.indexOf(headerMatch[0]) + headerMatch[0].length);
|
|
357
|
-
if (bodyStart >= 0) {
|
|
358
|
-
let bodyContent = section.slice(bodyStart).trim();
|
|
359
|
-
bodyContent = bodyContent.replace(/^\*[^*]+\*\s*\n?/, "").trim();
|
|
360
|
-
bodyContent = bodyContent.replace(/\nTags:\s*.+$/m, "").trim();
|
|
361
|
-
if (bodyContent) {
|
|
362
|
-
skill.instructions = bodyContent;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
if (!skill.instructions) {
|
|
366
|
-
skill.problem = this.extractMarkdownSection(section, "Problem");
|
|
367
|
-
skill.solution = this.extractMarkdownSection(section, "Solution");
|
|
368
|
-
skill.verification = this.extractMarkdownSection(section, "Verification");
|
|
369
|
-
skill.notes = this.extractMarkdownSection(section, "Notes");
|
|
370
|
-
}
|
|
371
|
-
const tagsMatch = /Tags:\s*(.+)$/m.exec(section);
|
|
372
|
-
if (tagsMatch) {
|
|
373
|
-
skill.tags = tagsMatch[1].split(",").map((t) => t.replace(/`/g, "").trim()).filter((t) => t);
|
|
374
|
-
}
|
|
375
|
-
skills.push(skill);
|
|
376
|
-
}
|
|
377
|
-
return skills;
|
|
378
|
-
}
|
|
379
|
-
/**
|
|
380
|
-
* Extract content from XML tag
|
|
381
|
-
*/
|
|
382
|
-
extractXmlTag(body, tagName) {
|
|
383
|
-
const regex = new RegExp(`<${tagName}>([\\s\\S]*?)<\\/${tagName}>`, "i");
|
|
384
|
-
const match = regex.exec(body);
|
|
385
|
-
if (match) {
|
|
386
|
-
return this.unescapeXml(match[1].trim());
|
|
387
|
-
}
|
|
388
|
-
return void 0;
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Extract CDATA content
|
|
392
|
-
*/
|
|
393
|
-
extractCData(body, tagName) {
|
|
394
|
-
const regex = new RegExp(`<${tagName}><!\\[CDATA\\[([\\s\\S]*?)\\]\\]><\\/${tagName}>`, "i");
|
|
395
|
-
const match = regex.exec(body);
|
|
396
|
-
if (match) {
|
|
397
|
-
return match[1];
|
|
398
|
-
}
|
|
399
|
-
return void 0;
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Unescape XML entities
|
|
403
|
-
*/
|
|
404
|
-
unescapeXml(str) {
|
|
405
|
-
return str.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Extract a markdown section by header
|
|
409
|
-
*/
|
|
410
|
-
extractMarkdownSection(content, header) {
|
|
411
|
-
const regex = new RegExp(`\\*\\*${header}:\\*\\*\\s*([\\s\\S]*?)(?=\\*\\*|Tags:|$)`, "i");
|
|
412
|
-
const match = regex.exec(content);
|
|
413
|
-
if (match) {
|
|
414
|
-
return match[1].trim();
|
|
415
|
-
}
|
|
416
|
-
return void 0;
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* Generate ID from skill name
|
|
420
|
-
*/
|
|
421
|
-
generateId(name) {
|
|
422
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Extract header content
|
|
426
|
-
*/
|
|
427
|
-
extractHeader(content, skills) {
|
|
428
|
-
if (skills.length === 0) return content.trim() || void 0;
|
|
429
|
-
const firstSkill = skills[0];
|
|
430
|
-
const idx = content.indexOf(firstSkill.rawContent);
|
|
431
|
-
if (idx > 0) {
|
|
432
|
-
return content.slice(0, idx).trim() || void 0;
|
|
433
|
-
}
|
|
434
|
-
return void 0;
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Extract footer content
|
|
438
|
-
*/
|
|
439
|
-
extractFooter(content, skills) {
|
|
440
|
-
if (skills.length === 0) return void 0;
|
|
441
|
-
const lastSkill = skills[skills.length - 1];
|
|
442
|
-
const idx = content.indexOf(lastSkill.rawContent);
|
|
443
|
-
if (idx >= 0) {
|
|
444
|
-
const afterIdx = idx + lastSkill.rawContent.length;
|
|
445
|
-
const footer = content.slice(afterIdx).trim();
|
|
446
|
-
return footer || void 0;
|
|
447
|
-
}
|
|
448
|
-
return void 0;
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
function createAgentsParser() {
|
|
452
|
-
return new AgentsParser();
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// src/agents/sync.ts
|
|
456
|
-
var AgentsSync = class {
|
|
457
|
-
constructor() {
|
|
458
|
-
this.generator = new AgentsGenerator();
|
|
459
|
-
this.parser = new AgentsParser();
|
|
460
|
-
}
|
|
461
|
-
/**
|
|
462
|
-
* Sync AGENTS.md with skill bank
|
|
463
|
-
*/
|
|
464
|
-
async sync(agentsPath, storage, options) {
|
|
465
|
-
const result = {
|
|
466
|
-
added: [],
|
|
467
|
-
updated: [],
|
|
468
|
-
removed: [],
|
|
469
|
-
unchanged: [],
|
|
470
|
-
warnings: []
|
|
471
|
-
};
|
|
472
|
-
switch (options.direction) {
|
|
473
|
-
case "import":
|
|
474
|
-
await this.importFromAgents(agentsPath, storage, options, result);
|
|
475
|
-
break;
|
|
476
|
-
case "export":
|
|
477
|
-
await this.exportToAgents(agentsPath, storage, options, result);
|
|
478
|
-
break;
|
|
479
|
-
case "bidirectional":
|
|
480
|
-
await this.bidirectionalSync(agentsPath, storage, options, result);
|
|
481
|
-
break;
|
|
482
|
-
}
|
|
483
|
-
return result;
|
|
484
|
-
}
|
|
485
|
-
/**
|
|
486
|
-
* Import skills from AGENTS.md to skill bank
|
|
487
|
-
*/
|
|
488
|
-
async importFromAgents(agentsPath, storage, options, result) {
|
|
489
|
-
if (!fs.existsSync(agentsPath)) {
|
|
490
|
-
result.warnings.push(`AGENTS.md not found at ${agentsPath}`);
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
const content = fs.readFileSync(agentsPath, "utf-8");
|
|
494
|
-
const parsed = this.parser.parse(content);
|
|
495
|
-
result.warnings.push(...parsed.warnings);
|
|
496
|
-
const existingSkills = await storage.listSkills();
|
|
497
|
-
const existingById = new Map(existingSkills.map((s) => [s.id, s]));
|
|
498
|
-
const existingByName = new Map(existingSkills.map((s) => [s.name.toLowerCase(), s]));
|
|
499
|
-
const processedIds = /* @__PURE__ */ new Set();
|
|
500
|
-
for (const parsedSkill of parsed.skills) {
|
|
501
|
-
const skill = this.parser.toSkill(parsedSkill);
|
|
502
|
-
processedIds.add(skill.id);
|
|
503
|
-
let existing = existingById.get(skill.id);
|
|
504
|
-
if (!existing) {
|
|
505
|
-
existing = existingByName.get(skill.name.toLowerCase());
|
|
506
|
-
}
|
|
507
|
-
if (existing) {
|
|
508
|
-
const shouldUpdate = this.resolveConflict(existing, skill, options.conflictStrategy);
|
|
509
|
-
if (shouldUpdate) {
|
|
510
|
-
if (!options.dryRun) {
|
|
511
|
-
const merged = this.mergeSkills(existing, skill);
|
|
512
|
-
await storage.saveSkill(merged);
|
|
513
|
-
}
|
|
514
|
-
result.updated.push(skill.id);
|
|
515
|
-
} else {
|
|
516
|
-
result.unchanged.push(skill.id);
|
|
517
|
-
}
|
|
518
|
-
} else {
|
|
519
|
-
if (!options.dryRun) {
|
|
520
|
-
await storage.saveSkill(skill);
|
|
521
|
-
}
|
|
522
|
-
result.added.push(skill.id);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
if (options.removeOrphans) {
|
|
526
|
-
for (const existing of existingSkills) {
|
|
527
|
-
if (!processedIds.has(existing.id)) {
|
|
528
|
-
if (!options.dryRun) {
|
|
529
|
-
await storage.deleteSkill(existing.id);
|
|
530
|
-
}
|
|
531
|
-
result.removed.push(existing.id);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Export skills from skill bank to AGENTS.md
|
|
538
|
-
*/
|
|
539
|
-
async exportToAgents(agentsPath, storage, options, result) {
|
|
540
|
-
const generator = new AgentsGenerator(options.generatorConfig);
|
|
541
|
-
const content = await generator.generate(storage);
|
|
542
|
-
let existingContent = "";
|
|
543
|
-
if (fs.existsSync(agentsPath)) {
|
|
544
|
-
existingContent = fs.readFileSync(agentsPath, "utf-8");
|
|
545
|
-
}
|
|
546
|
-
const existingParsed = this.parser.parse(existingContent);
|
|
547
|
-
const existingIds = new Set(existingParsed.skills.map((s) => s.id || s.name.toLowerCase()));
|
|
548
|
-
const skills = await storage.listSkills({
|
|
549
|
-
status: options.generatorConfig?.filter?.status || ["active"]
|
|
550
|
-
});
|
|
551
|
-
for (const skill of skills) {
|
|
552
|
-
if (existingIds.has(skill.id) || existingIds.has(skill.name.toLowerCase())) {
|
|
553
|
-
result.updated.push(skill.id);
|
|
554
|
-
} else {
|
|
555
|
-
result.added.push(skill.id);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
if (!options.dryRun) {
|
|
559
|
-
const dir = path.dirname(agentsPath);
|
|
560
|
-
if (!fs.existsSync(dir)) {
|
|
561
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
562
|
-
}
|
|
563
|
-
fs.writeFileSync(agentsPath, content);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Bidirectional sync
|
|
568
|
-
*/
|
|
569
|
-
async bidirectionalSync(agentsPath, storage, options, result) {
|
|
570
|
-
await this.importFromAgents(agentsPath, storage, { ...options, removeOrphans: false }, result);
|
|
571
|
-
const exportResult = {
|
|
572
|
-
added: [],
|
|
573
|
-
updated: [],
|
|
574
|
-
removed: [],
|
|
575
|
-
unchanged: [],
|
|
576
|
-
warnings: []
|
|
577
|
-
};
|
|
578
|
-
await this.exportToAgents(agentsPath, storage, options, exportResult);
|
|
579
|
-
for (const id of exportResult.added) {
|
|
580
|
-
if (!result.added.includes(id) && !result.updated.includes(id)) {
|
|
581
|
-
result.added.push(id);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Resolve conflict between existing and new skill
|
|
587
|
-
*/
|
|
588
|
-
resolveConflict(existing, incoming, strategy) {
|
|
589
|
-
switch (strategy) {
|
|
590
|
-
case "bank-wins":
|
|
591
|
-
return false;
|
|
592
|
-
case "agents-wins":
|
|
593
|
-
return true;
|
|
594
|
-
case "newer-wins":
|
|
595
|
-
return incoming.updatedAt > existing.updatedAt;
|
|
596
|
-
case "skip":
|
|
597
|
-
default:
|
|
598
|
-
return false;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
/**
|
|
602
|
-
* Merge skills, preserving important data from existing
|
|
603
|
-
*/
|
|
604
|
-
mergeSkills(existing, incoming) {
|
|
605
|
-
return {
|
|
606
|
-
...incoming,
|
|
607
|
-
id: existing.id,
|
|
608
|
-
// Keep existing ID
|
|
609
|
-
createdAt: existing.createdAt,
|
|
610
|
-
// Preserve creation date
|
|
611
|
-
metrics: existing.metrics,
|
|
612
|
-
// Preserve usage metrics
|
|
613
|
-
source: incoming.source || existing.source,
|
|
614
|
-
parentVersion: existing.version
|
|
615
|
-
// Track update lineage
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
};
|
|
619
|
-
function createAgentsSync() {
|
|
620
|
-
return new AgentsSync();
|
|
621
|
-
}
|
|
622
|
-
async function generateAgentsMd(storage, config) {
|
|
623
|
-
const generator = new AgentsGenerator(config);
|
|
624
|
-
return generator.generate(storage);
|
|
625
|
-
}
|
|
626
|
-
async function writeAgentsMd(storage, filePath, config) {
|
|
627
|
-
const content = await generateAgentsMd(storage, config);
|
|
628
|
-
const dir = path.dirname(filePath);
|
|
629
|
-
if (dir && !fs.existsSync(dir)) {
|
|
630
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
631
|
-
}
|
|
632
|
-
fs.writeFileSync(filePath, content);
|
|
633
|
-
}
|
|
634
|
-
async function importFromAgentsMd(filePath, storage, options) {
|
|
635
|
-
const sync = new AgentsSync();
|
|
636
|
-
return sync.sync(filePath, storage, {
|
|
637
|
-
direction: "import",
|
|
638
|
-
conflictStrategy: options?.conflictStrategy || "skip",
|
|
639
|
-
dryRun: options?.dryRun
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
export {
|
|
644
|
-
DEFAULT_AGENTS_CONFIG,
|
|
645
|
-
AgentsGenerator,
|
|
646
|
-
createAgentsGenerator,
|
|
647
|
-
AgentsParser,
|
|
648
|
-
createAgentsParser,
|
|
649
|
-
AgentsSync,
|
|
650
|
-
createAgentsSync,
|
|
651
|
-
generateAgentsMd,
|
|
652
|
-
writeAgentsMd,
|
|
653
|
-
importFromAgentsMd
|
|
654
|
-
};
|