@zebralabs/context-cli 0.1.3 → 0.1.5

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.
@@ -0,0 +1,74 @@
1
+ import { MarkdownParser } from "./markdown-parser.js";
2
+
3
+ /**
4
+ * Skill extractor adapter
5
+ * Extracts skill definitions from @context.skills sections
6
+ */
7
+ export class SkillExtractor {
8
+ constructor() {
9
+ this.parser = new MarkdownParser();
10
+ }
11
+
12
+ /**
13
+ * Extracts skill definitions from markdown content.
14
+ * @param {string} markdown - The markdown content.
15
+ * @param {Object} sourceMetadata - Metadata about the source file (packId, file, etc.).
16
+ * @returns {Object[]} Extracted skill definitions.
17
+ */
18
+ extractSkills(markdown, sourceMetadata) {
19
+ const skills = [];
20
+ const skillsSection = this.parser.findSection(markdown, "## @context.skills");
21
+
22
+ if (!skillsSection) return skills;
23
+
24
+ const skillBlocks = skillsSection.split(/\r?\n### /).filter(Boolean);
25
+ for (const skillText of skillBlocks) {
26
+ try {
27
+ const skill = this._parseSkill(skillText, sourceMetadata);
28
+ if (skill) skills.push(skill);
29
+ } catch (e) {
30
+ console.warn(`Warning: Malformed skill in ${sourceMetadata.file}: ${e.message}`);
31
+ }
32
+ }
33
+ return skills;
34
+ }
35
+
36
+ _parseSkill(skillText, sourceMetadata) {
37
+ const lines = skillText.split(/\r?\n/);
38
+ // Handle first line - might have "### " prefix or not
39
+ const firstLine = lines[0].trim();
40
+ const nameMatch = firstLine.match(/^(?:###\s+)?skill-([a-z0-9-]+)$/i);
41
+ if (!nameMatch) throw new Error(`Invalid skill ID format: ${firstLine}`);
42
+
43
+ const id = nameMatch[1].toLowerCase();
44
+ const skill = {
45
+ id: id,
46
+ name: id, // Default name to id, can be overridden by explicit 'Name' field
47
+ description: '',
48
+ rules: [],
49
+ whenToUse: [],
50
+ processReference: '',
51
+ source: sourceMetadata
52
+ };
53
+
54
+ for (let i = 1; i < lines.length; i++) {
55
+ const line = lines[i].trim();
56
+ if (line.startsWith('- **Name:**')) {
57
+ skill.name = line.replace('- **Name:**', '').trim();
58
+ } else if (line.startsWith('- **Description:**')) {
59
+ skill.description = line.replace('- **Description:**', '').trim();
60
+ } else if (line.startsWith('- **Rules:**')) {
61
+ skill.rules = line.replace('- **Rules:**', '').split(',').map(s => s.trim()).filter(Boolean);
62
+ } else if (line.startsWith('- **When to use:**')) {
63
+ skill.whenToUse = line.replace('- **When to use:**', '').split(',').map(s => s.trim()).filter(Boolean);
64
+ } else if (line.startsWith('- **Process reference:**')) {
65
+ skill.processReference = line.replace('- **Process reference:**', '').trim();
66
+ }
67
+ }
68
+
69
+ if (!skill.description) throw new Error(`Skill '${skill.id}' is missing a description.`);
70
+
71
+ return skill;
72
+ }
73
+ }
74
+