local-skills-mcp 0.1.0

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.js ADDED
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import path from "path";
6
+ import fs from "fs";
7
+ import os from "os";
8
+ import { fileURLToPath } from "url";
9
+ import { SkillLoader } from "./skill-loader.js";
10
+ // Get version from package.json (single source of truth)
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
14
+ const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "../package.json"), "utf-8"));
15
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
16
+ const VERSION = packageJson.version;
17
+ /**
18
+ * Get all skills directories to aggregate from.
19
+ *
20
+ * Scans for skill directories in the following order (lowest to highest priority):
21
+ * 1. `<package-root>/skills` - Package built-in skills (self-documenting, always available)
22
+ * 2. `~/.claude/skills` - Global Claude skills
23
+ * 3. `{cwd}/.claude/skills` - Project-level Claude skills
24
+ * 4. `{cwd}/skills` - Default project skills directory
25
+ * 5. `$SKILLS_DIR` - Custom directory from environment variable
26
+ *
27
+ * For duplicate skill names, later directories take precedence. This allows
28
+ * users to override built-in skills with their own versions.
29
+ *
30
+ * @returns Array of directory paths that exist on the filesystem
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const dirs = getAllSkillsDirectories();
35
+ * console.log(dirs);
36
+ * // ['/usr/lib/node_modules/local-skills-mcp/skills', '/home/user/.claude/skills', '/home/user/project/skills']
37
+ * ```
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // With SKILLS_DIR environment variable
42
+ * process.env.SKILLS_DIR = '/custom/skills';
43
+ * const dirs = getAllSkillsDirectories();
44
+ * // Returns: [...standard paths..., '/custom/skills']
45
+ * ```
46
+ */
47
+ export function getAllSkillsDirectories() {
48
+ const directories = [];
49
+ // Always include package built-in skills first (lowest priority, always available)
50
+ // This provides self-documenting capabilities out-of-the-box
51
+ const packageRoot = path.join(__dirname, "..");
52
+ const packageSkills = path.join(packageRoot, "skills");
53
+ if (fs.existsSync(packageSkills)) {
54
+ directories.push(packageSkills);
55
+ }
56
+ // Include standard Claude locations if they exist
57
+ const homeClaudeSkills = path.join(os.homedir(), ".claude", "skills");
58
+ if (fs.existsSync(homeClaudeSkills)) {
59
+ directories.push(homeClaudeSkills);
60
+ }
61
+ const projectClaudeSkills = path.join(process.cwd(), ".claude", "skills");
62
+ if (fs.existsSync(projectClaudeSkills)) {
63
+ directories.push(projectClaudeSkills);
64
+ }
65
+ const defaultSkills = path.join(process.cwd(), "skills");
66
+ if (fs.existsSync(defaultSkills)) {
67
+ directories.push(defaultSkills);
68
+ }
69
+ // If SKILLS_DIR is set, add it (it takes precedence for duplicates)
70
+ if (process.env.SKILLS_DIR) {
71
+ directories.push(process.env.SKILLS_DIR);
72
+ }
73
+ return directories;
74
+ }
75
+ const SKILLS_DIRS = getAllSkillsDirectories();
76
+ /**
77
+ * Main MCP server class for serving skills to AI clients.
78
+ *
79
+ * LocalSkillsServer handles MCP protocol communication, manages skill discovery
80
+ * and loading through SkillLoader, and formats responses for clients.
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const server = new LocalSkillsServer();
85
+ * await server.run();
86
+ * // Server is now running and listening on stdio
87
+ * ```
88
+ */
89
+ export class LocalSkillsServer {
90
+ server;
91
+ skillLoader;
92
+ /**
93
+ * Creates a new LocalSkillsServer instance.
94
+ *
95
+ * Initializes the MCP server with capabilities, creates a SkillLoader
96
+ * for all configured directories, and sets up request handlers.
97
+ */
98
+ constructor() {
99
+ this.server = new Server({
100
+ name: "local-skills-mcp",
101
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
102
+ version: VERSION,
103
+ }, {
104
+ capabilities: {
105
+ tools: {},
106
+ },
107
+ });
108
+ this.skillLoader = new SkillLoader(SKILLS_DIRS);
109
+ this.setupHandlers();
110
+ this.setupErrorHandling();
111
+ }
112
+ setupErrorHandling() {
113
+ this.server.onerror = (error) => {
114
+ console.error("[MCP Error]", error);
115
+ };
116
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
117
+ process.on("SIGINT", async () => {
118
+ await this.server.close();
119
+ process.exit(0);
120
+ });
121
+ }
122
+ setupHandlers() {
123
+ // List available tools (dynamically generated to include current skill list)
124
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
125
+ // Discover skills to get current list
126
+ const skillNames = await this.skillLoader.discoverSkills();
127
+ // Build enhanced description for get_skill following Claude Skills best practices
128
+ // Pattern: [What it does]. [Value proposition]. Use when [trigger conditions].
129
+ let getSkillDescription = "Loads specialized expert prompt instructions that transform your capabilities for specific tasks. " +
130
+ "Each skill provides comprehensive guidance, proven methodologies, and domain-specific best practices. " +
131
+ "Use when you need focused expertise, systematic approaches, or professional standards for any task that would benefit from specialized knowledge. " +
132
+ "Invoke with the skill name to receive detailed instructions that enhance your problem-solving approach with structured, expert-level guidance.";
133
+ // Package built-in skills ensure there are always skills available
134
+ if (skillNames.length > 0) {
135
+ getSkillDescription += `\n\nAvailable skills: ${skillNames.join(", ")}`;
136
+ }
137
+ const tools = [
138
+ {
139
+ name: "get_skill",
140
+ description: getSkillDescription,
141
+ inputSchema: {
142
+ type: "object",
143
+ properties: {
144
+ skill_name: {
145
+ type: "string",
146
+ description: 'The name of the skill to retrieve (e.g., "code-reviewer", "test-generator")',
147
+ },
148
+ },
149
+ required: ["skill_name"],
150
+ },
151
+ },
152
+ ];
153
+ return { tools };
154
+ });
155
+ // Handle tool calls
156
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
157
+ try {
158
+ switch (request.params.name) {
159
+ case "get_skill":
160
+ return await this.handleGetSkill(request.params.arguments);
161
+ default:
162
+ throw new Error(`Unknown tool: ${request.params.name}`);
163
+ }
164
+ }
165
+ catch (error) {
166
+ const errorMessage = error instanceof Error ? error.message : String(error);
167
+ return {
168
+ content: [
169
+ {
170
+ type: "text",
171
+ text: `Error: ${errorMessage}`,
172
+ },
173
+ ],
174
+ };
175
+ }
176
+ });
177
+ }
178
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
+ async handleGetSkill(args) {
180
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
181
+ const skillName = args?.skill_name;
182
+ if (!skillName) {
183
+ throw new Error("skill_name is required");
184
+ }
185
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
186
+ const skill = await this.skillLoader.loadSkill(skillName);
187
+ const output = [
188
+ `# Skill: ${skill.name}`,
189
+ ``,
190
+ `**Description:** ${skill.description}`,
191
+ `**Source:** ${skill.source}`,
192
+ ``,
193
+ `---`,
194
+ ``,
195
+ skill.content,
196
+ ];
197
+ return {
198
+ content: [
199
+ {
200
+ type: "text",
201
+ text: output.join("\n"),
202
+ },
203
+ ],
204
+ };
205
+ }
206
+ /**
207
+ * Starts the MCP server and connects it to stdio transport.
208
+ *
209
+ * This method initializes the stdio transport for MCP communication,
210
+ * connects the server, and logs startup information including the
211
+ * list of skill directories being monitored.
212
+ *
213
+ * @throws {Error} If the server fails to connect or start
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * const server = new LocalSkillsServer();
218
+ * await server.run();
219
+ * // Output to stderr:
220
+ * // Local Skills MCP Server v0.1.0 running on stdio
221
+ * // Aggregating skills from 3 directories:
222
+ * // - /home/user/.claude/skills
223
+ * // - /home/user/project/.claude/skills
224
+ * // - /home/user/project/skills
225
+ * ```
226
+ */
227
+ async run() {
228
+ const transport = new StdioServerTransport();
229
+ await this.server.connect(transport);
230
+ console.error(`Local Skills MCP Server v${VERSION} running on stdio`);
231
+ console.error(`Aggregating skills from ${SKILLS_DIRS.length} director${SKILLS_DIRS.length === 1 ? "y" : "ies"}:`);
232
+ SKILLS_DIRS.forEach((dir) => console.error(` - ${dir}`));
233
+ }
234
+ /**
235
+ * Closes the server and releases all resources.
236
+ *
237
+ * This method properly shuts down the MCP server and waits for all
238
+ * file handles and resources to be released. This is particularly
239
+ * important on Windows where file handles may take longer to release.
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * const server = new LocalSkillsServer();
244
+ * await server.run();
245
+ * // ... do work ...
246
+ * await server.close();
247
+ * ```
248
+ */
249
+ async close() {
250
+ await this.server.close();
251
+ // Allow time for all handles to be released (important on Windows)
252
+ await new Promise((resolve) => setTimeout(resolve, 100));
253
+ }
254
+ }
255
+ // Start the server only if this module is being run directly
256
+ /* istanbul ignore if */
257
+ if (import.meta.url === `file://${process.argv[1]}`) {
258
+ const server = new LocalSkillsServer();
259
+ server.run().catch((error) => {
260
+ console.error("Fatal error running server:", error);
261
+ process.exit(1);
262
+ });
263
+ }
@@ -0,0 +1,126 @@
1
+ import { Skill, SkillMetadata } from "./types.js";
2
+ /**
3
+ * Manages skill discovery and loading across multiple directories.
4
+ *
5
+ * SkillLoader is responsible for:
6
+ * - Discovering skills in configured directories
7
+ * - Loading and parsing SKILL.md files with YAML frontmatter
8
+ * - Managing the skill registry for fast lookups
9
+ *
10
+ * Skills are loaded fresh from disk on each request to support hot reload.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const loader = new SkillLoader([
15
+ * '/home/user/.claude/skills',
16
+ * '/home/user/project/skills'
17
+ * ]);
18
+ * const skills = await loader.discoverSkills();
19
+ * const skill = await loader.loadSkill('code-reviewer');
20
+ * ```
21
+ */
22
+ export declare class SkillLoader {
23
+ private skillsPaths;
24
+ private skillRegistry;
25
+ /**
26
+ * Creates a new SkillLoader instance.
27
+ *
28
+ * @param skillsPaths - Array of directory paths to search for skills
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const loader = new SkillLoader([
33
+ * '/home/user/.claude/skills',
34
+ * '/home/user/project/skills'
35
+ * ]);
36
+ * ```
37
+ */
38
+ constructor(skillsPaths: string[]);
39
+ /**
40
+ * Parse SKILL.md file with YAML frontmatter
41
+ */
42
+ private parseSkillFile;
43
+ /**
44
+ * Discover all available skills aggregated from all directories.
45
+ *
46
+ * Scans all configured directories for subdirectories containing SKILL.md files.
47
+ * Later directories override earlier ones for duplicate skill names.
48
+ * The internal skill registry is rebuilt on each call.
49
+ *
50
+ * @returns Promise resolving to a sorted array of skill names
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const skillNames = await loader.discoverSkills();
55
+ * console.log(skillNames);
56
+ * // Output: ['code-reviewer', 'test-generator', 'refactoring-expert']
57
+ * ```
58
+ */
59
+ discoverSkills(): Promise<string[]>;
60
+ /**
61
+ * Load a specific skill by name, reading fresh from disk each time.
62
+ *
63
+ * Loads the SKILL.md file, parses YAML frontmatter, and validates metadata.
64
+ * Skills are always read fresh to support hot reload of content changes.
65
+ *
66
+ * @param skillName - The name of the skill to load
67
+ * @returns Promise resolving to the complete Skill object
68
+ * @throws {Error} If skill is not found in registry or file cannot be loaded/parsed
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const skill = await loader.loadSkill('code-reviewer');
73
+ * console.log(skill.name); // 'Code Reviewer'
74
+ * console.log(skill.description); // 'Expert code review assistant'
75
+ * console.log(skill.content); // Full skill prompt content
76
+ * console.log(skill.source); // '/home/user/.claude/skills'
77
+ * ```
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // Error handling
82
+ * try {
83
+ * const skill = await loader.loadSkill('non-existent');
84
+ * } catch (error) {
85
+ * console.error(error.message);
86
+ * // "Skill "non-existent" not found. Run list_skills to see available skills."
87
+ * }
88
+ * ```
89
+ */
90
+ loadSkill(skillName: string): Promise<Skill>;
91
+ /**
92
+ * Get skill metadata without loading the full content.
93
+ *
94
+ * Lightweight operation that loads only the YAML frontmatter without
95
+ * the full skill content. Useful for listing skills with descriptions.
96
+ *
97
+ * @param skillName - The name of the skill
98
+ * @returns Promise resolving to metadata with source directory
99
+ * @throws {Error} If skill is not found or metadata cannot be loaded
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const metadata = await loader.getSkillMetadata('code-reviewer');
104
+ * console.log(metadata.name); // 'Code Reviewer'
105
+ * console.log(metadata.description); // 'Expert code review assistant'
106
+ * console.log(metadata.source); // '/home/user/.claude/skills'
107
+ * // Note: metadata does not include 'content' field
108
+ * ```
109
+ */
110
+ getSkillMetadata(skillName: string): Promise<SkillMetadata & {
111
+ source: string;
112
+ }>;
113
+ /**
114
+ * Get all skills directories being monitored.
115
+ *
116
+ * @returns Array of directory paths configured for this loader
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const paths = loader.getSkillsPaths();
121
+ * console.log(paths);
122
+ * // Output: ['/home/user/.claude/skills', '/home/user/project/skills']
123
+ * ```
124
+ */
125
+ getSkillsPaths(): string[];
126
+ }
@@ -0,0 +1,231 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ import YAML from "yaml";
4
+ /**
5
+ * Manages skill discovery and loading across multiple directories.
6
+ *
7
+ * SkillLoader is responsible for:
8
+ * - Discovering skills in configured directories
9
+ * - Loading and parsing SKILL.md files with YAML frontmatter
10
+ * - Managing the skill registry for fast lookups
11
+ *
12
+ * Skills are loaded fresh from disk on each request to support hot reload.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const loader = new SkillLoader([
17
+ * '/home/user/.claude/skills',
18
+ * '/home/user/project/skills'
19
+ * ]);
20
+ * const skills = await loader.discoverSkills();
21
+ * const skill = await loader.loadSkill('code-reviewer');
22
+ * ```
23
+ */
24
+ export class SkillLoader {
25
+ skillsPaths;
26
+ skillRegistry = new Map();
27
+ /**
28
+ * Creates a new SkillLoader instance.
29
+ *
30
+ * @param skillsPaths - Array of directory paths to search for skills
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const loader = new SkillLoader([
35
+ * '/home/user/.claude/skills',
36
+ * '/home/user/project/skills'
37
+ * ]);
38
+ * ```
39
+ */
40
+ constructor(skillsPaths) {
41
+ this.skillsPaths = skillsPaths;
42
+ }
43
+ /**
44
+ * Parse SKILL.md file with YAML frontmatter
45
+ */
46
+ parseSkillFile(content) {
47
+ // Check if file starts with frontmatter delimiter
48
+ if (!content.startsWith("---\n")) {
49
+ throw new Error("SKILL.md must start with YAML frontmatter (---)");
50
+ }
51
+ // Find the closing delimiter
52
+ const endDelimiterIndex = content.indexOf("\n---\n", 4);
53
+ if (endDelimiterIndex === -1) {
54
+ throw new Error("SKILL.md frontmatter must end with --- delimiter");
55
+ }
56
+ // Extract and parse YAML frontmatter
57
+ const frontmatterText = content.substring(4, endDelimiterIndex);
58
+ const metadata = YAML.parse(frontmatterText);
59
+ // Validate required fields
60
+ if (!metadata.name) {
61
+ throw new Error('SKILL.md frontmatter must include "name" field');
62
+ }
63
+ if (!metadata.description) {
64
+ throw new Error('SKILL.md frontmatter must include "description" field');
65
+ }
66
+ // Extract content after frontmatter
67
+ const skillContent = content.substring(endDelimiterIndex + 5).trim();
68
+ return { metadata, content: skillContent };
69
+ }
70
+ /**
71
+ * Discover all available skills aggregated from all directories.
72
+ *
73
+ * Scans all configured directories for subdirectories containing SKILL.md files.
74
+ * Later directories override earlier ones for duplicate skill names.
75
+ * The internal skill registry is rebuilt on each call.
76
+ *
77
+ * @returns Promise resolving to a sorted array of skill names
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * const skillNames = await loader.discoverSkills();
82
+ * console.log(skillNames);
83
+ * // Output: ['code-reviewer', 'test-generator', 'refactoring-expert']
84
+ * ```
85
+ */
86
+ async discoverSkills() {
87
+ this.skillRegistry.clear();
88
+ const allSkills = new Map();
89
+ // Iterate through all skill directories
90
+ for (const skillsPath of this.skillsPaths) {
91
+ try {
92
+ const entries = await fs.readdir(skillsPath, { withFileTypes: true });
93
+ for (const entry of entries) {
94
+ if (entry.isDirectory()) {
95
+ const skillPath = path.join(skillsPath, entry.name);
96
+ const skillFilePath = path.join(skillPath, "SKILL.md");
97
+ try {
98
+ await fs.access(skillFilePath);
99
+ // Later directories override earlier ones for duplicate names
100
+ allSkills.set(entry.name, {
101
+ path: skillPath,
102
+ source: skillsPath,
103
+ });
104
+ }
105
+ catch {
106
+ // Skip directories without SKILL.md
107
+ continue;
108
+ }
109
+ }
110
+ }
111
+ }
112
+ catch (error) {
113
+ if (error.code !== "ENOENT") {
114
+ console.error(`Error reading directory ${skillsPath}:`, error);
115
+ }
116
+ // Continue with other directories even if one fails
117
+ continue;
118
+ }
119
+ }
120
+ // Update registry
121
+ this.skillRegistry = allSkills;
122
+ // Return sorted skill names
123
+ return Array.from(allSkills.keys()).sort();
124
+ }
125
+ /**
126
+ * Load a specific skill by name, reading fresh from disk each time.
127
+ *
128
+ * Loads the SKILL.md file, parses YAML frontmatter, and validates metadata.
129
+ * Skills are always read fresh to support hot reload of content changes.
130
+ *
131
+ * @param skillName - The name of the skill to load
132
+ * @returns Promise resolving to the complete Skill object
133
+ * @throws {Error} If skill is not found in registry or file cannot be loaded/parsed
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * const skill = await loader.loadSkill('code-reviewer');
138
+ * console.log(skill.name); // 'Code Reviewer'
139
+ * console.log(skill.description); // 'Expert code review assistant'
140
+ * console.log(skill.content); // Full skill prompt content
141
+ * console.log(skill.source); // '/home/user/.claude/skills'
142
+ * ```
143
+ *
144
+ * @example
145
+ * ```typescript
146
+ * // Error handling
147
+ * try {
148
+ * const skill = await loader.loadSkill('non-existent');
149
+ * } catch (error) {
150
+ * console.error(error.message);
151
+ * // "Skill "non-existent" not found. Run list_skills to see available skills."
152
+ * }
153
+ * ```
154
+ */
155
+ async loadSkill(skillName) {
156
+ // Get skill location from registry
157
+ const skillInfo = this.skillRegistry.get(skillName);
158
+ if (!skillInfo) {
159
+ throw new Error(`Skill "${skillName}" not found. Run list_skills to see available skills.`);
160
+ }
161
+ const skillFilePath = path.join(skillInfo.path, "SKILL.md");
162
+ try {
163
+ // Load and parse SKILL.md fresh from disk
164
+ const fileContent = await fs.readFile(skillFilePath, "utf-8");
165
+ const { metadata, content } = this.parseSkillFile(fileContent);
166
+ const skill = {
167
+ name: metadata.name,
168
+ description: metadata.description,
169
+ content,
170
+ path: skillInfo.path,
171
+ source: skillInfo.source,
172
+ };
173
+ return skill;
174
+ }
175
+ catch (error) {
176
+ throw new Error(`Failed to load skill "${skillName}": ${error.message}`);
177
+ }
178
+ }
179
+ /**
180
+ * Get skill metadata without loading the full content.
181
+ *
182
+ * Lightweight operation that loads only the YAML frontmatter without
183
+ * the full skill content. Useful for listing skills with descriptions.
184
+ *
185
+ * @param skillName - The name of the skill
186
+ * @returns Promise resolving to metadata with source directory
187
+ * @throws {Error} If skill is not found or metadata cannot be loaded
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * const metadata = await loader.getSkillMetadata('code-reviewer');
192
+ * console.log(metadata.name); // 'Code Reviewer'
193
+ * console.log(metadata.description); // 'Expert code review assistant'
194
+ * console.log(metadata.source); // '/home/user/.claude/skills'
195
+ * // Note: metadata does not include 'content' field
196
+ * ```
197
+ */
198
+ async getSkillMetadata(skillName) {
199
+ const skillInfo = this.skillRegistry.get(skillName);
200
+ if (!skillInfo) {
201
+ throw new Error(`Skill "${skillName}" not found`);
202
+ }
203
+ const skillFilePath = path.join(skillInfo.path, "SKILL.md");
204
+ try {
205
+ const fileContent = await fs.readFile(skillFilePath, "utf-8");
206
+ const { metadata } = this.parseSkillFile(fileContent);
207
+ return {
208
+ ...metadata,
209
+ source: skillInfo.source,
210
+ };
211
+ }
212
+ catch (error) {
213
+ throw new Error(`Failed to load metadata for skill "${skillName}": ${error.message}`);
214
+ }
215
+ }
216
+ /**
217
+ * Get all skills directories being monitored.
218
+ *
219
+ * @returns Array of directory paths configured for this loader
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * const paths = loader.getSkillsPaths();
224
+ * console.log(paths);
225
+ * // Output: ['/home/user/.claude/skills', '/home/user/project/skills']
226
+ * ```
227
+ */
228
+ getSkillsPaths() {
229
+ return this.skillsPaths;
230
+ }
231
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Skill metadata from YAML frontmatter in SKILL.md
3
+ */
4
+ export interface SkillMetadata {
5
+ name: string;
6
+ description: string;
7
+ }
8
+ /**
9
+ * Full skill definition including prompt content
10
+ */
11
+ export interface Skill {
12
+ name: string;
13
+ description: string;
14
+ content: string;
15
+ path: string;
16
+ source: string;
17
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};