@cockpit-ai/skills 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.
@@ -0,0 +1,47 @@
1
+ import { ResolvedSkill } from '@cockpit-ai/core';
2
+
3
+ /**
4
+ * Load a single skill from a YAML file and resolve it to ResolvedSkill.
5
+ * Throws on validation errors; returns null if file doesn't exist.
6
+ */
7
+ declare function loadSkillFromFile(filePath: string): ResolvedSkill;
8
+ /**
9
+ * Load all skills from a directory (*.yaml, *.yml files).
10
+ * Skips files that fail to load, collecting errors separately.
11
+ */
12
+ declare function loadSkillsFromDir(dirPath: string): {
13
+ skills: ResolvedSkill[];
14
+ errors: Array<{
15
+ file: string;
16
+ error: unknown;
17
+ }>;
18
+ };
19
+
20
+ declare class SkillRegistry {
21
+ private skills;
22
+ add(skill: ResolvedSkill): void;
23
+ get(name: string): ResolvedSkill | undefined;
24
+ has(name: string): boolean;
25
+ list(): ResolvedSkill[];
26
+ remove(name: string): boolean;
27
+ size(): number;
28
+ /**
29
+ * Load all skills from one or more directories into the registry.
30
+ * Returns any load errors encountered.
31
+ */
32
+ loadFromDirs(dirs: string[]): Array<{
33
+ file: string;
34
+ error: unknown;
35
+ }>;
36
+ }
37
+
38
+ /**
39
+ * Returns the YAML content for a new skill template.
40
+ */
41
+ declare function defaultSkillTemplate(name: string): string;
42
+ /**
43
+ * Returns the YAML content for a pre-built "code-review" skill.
44
+ */
45
+ declare function codeReviewTemplate(): string;
46
+
47
+ export { SkillRegistry, codeReviewTemplate, defaultSkillTemplate, loadSkillFromFile, loadSkillsFromDir };
package/dist/index.js ADDED
@@ -0,0 +1,152 @@
1
+ // src/loader.ts
2
+ import { readdirSync, existsSync } from "fs";
3
+ import { join, extname } from "path";
4
+ import {
5
+ loadConfig,
6
+ ConfigLoadError,
7
+ ConfigValidationError,
8
+ SkillDefinitionSchema
9
+ } from "@cockpit-ai/core";
10
+ function loadSkillFromFile(filePath) {
11
+ const raw = loadConfig(filePath, SkillDefinitionSchema);
12
+ return {
13
+ name: raw.name,
14
+ version: raw.version,
15
+ description: raw.description,
16
+ trigger: raw.trigger ?? [],
17
+ prompt: raw.prompt,
18
+ tools: raw.tools ?? [],
19
+ sourcePath: filePath,
20
+ adapterConfig: raw.adapters ?? {}
21
+ };
22
+ }
23
+ function loadSkillsFromDir(dirPath) {
24
+ if (!existsSync(dirPath)) {
25
+ return { skills: [], errors: [] };
26
+ }
27
+ const skills = [];
28
+ const errors = [];
29
+ for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
30
+ if (!entry.isFile()) continue;
31
+ const ext = extname(entry.name);
32
+ if (ext !== ".yaml" && ext !== ".yml") continue;
33
+ const filePath = join(dirPath, entry.name);
34
+ try {
35
+ skills.push(loadSkillFromFile(filePath));
36
+ } catch (err) {
37
+ if (err instanceof ConfigLoadError || err instanceof ConfigValidationError) {
38
+ errors.push({ file: filePath, error: err });
39
+ } else {
40
+ throw err;
41
+ }
42
+ }
43
+ }
44
+ return { skills, errors };
45
+ }
46
+
47
+ // src/registry.ts
48
+ var SkillRegistry = class {
49
+ skills = /* @__PURE__ */ new Map();
50
+ add(skill) {
51
+ this.skills.set(skill.name, skill);
52
+ }
53
+ get(name) {
54
+ return this.skills.get(name);
55
+ }
56
+ has(name) {
57
+ return this.skills.has(name);
58
+ }
59
+ list() {
60
+ return [...this.skills.values()];
61
+ }
62
+ remove(name) {
63
+ return this.skills.delete(name);
64
+ }
65
+ size() {
66
+ return this.skills.size;
67
+ }
68
+ /**
69
+ * Load all skills from one or more directories into the registry.
70
+ * Returns any load errors encountered.
71
+ */
72
+ loadFromDirs(dirs) {
73
+ const allErrors = [];
74
+ for (const dir of dirs) {
75
+ const { skills, errors } = loadSkillsFromDir(dir);
76
+ for (const skill of skills) {
77
+ this.add(skill);
78
+ }
79
+ allErrors.push(...errors);
80
+ }
81
+ return allErrors;
82
+ }
83
+ };
84
+
85
+ // src/templates/default.ts
86
+ function defaultSkillTemplate(name) {
87
+ return `name: ${name}
88
+ version: 1.0.0
89
+ description: ${name} skill
90
+
91
+ # Triggers that activate this skill (for tools that support slash commands)
92
+ trigger:
93
+ - "/${name}"
94
+
95
+ # The prompt sent to the AI when this skill is triggered
96
+ prompt: |
97
+ Describe what this skill should do.
98
+ Add detailed instructions here.
99
+
100
+ # Tools the AI is allowed to use (leave empty for no restriction)
101
+ tools:
102
+ - read
103
+ - grep
104
+ - glob
105
+
106
+ # Per-adapter overrides (optional)
107
+ adapters:
108
+ claude-code:
109
+ type: command # creates .claude/commands/${name}.md
110
+ cursor:
111
+ type: rule # creates .cursor/rules/${name}.mdc
112
+ alwaysApply: false
113
+ `;
114
+ }
115
+ function codeReviewTemplate() {
116
+ return `name: code-review
117
+ version: 1.0.0
118
+ description: Comprehensive code review
119
+
120
+ trigger:
121
+ - "/review"
122
+ - "review"
123
+
124
+ prompt: |
125
+ Review the provided code thoroughly:
126
+
127
+ 1. **Bugs & Edge Cases** \u2014 identify logic errors, unhandled cases, off-by-one errors
128
+ 2. **Performance** \u2014 flag inefficient algorithms, unnecessary re-renders, N+1 queries
129
+ 3. **Security** \u2014 check for injection, XSS, auth issues, sensitive data exposure
130
+ 4. **Readability** \u2014 suggest clearer names, simpler logic, better structure
131
+ 5. **Tests** \u2014 note missing test coverage for critical paths
132
+
133
+ Provide specific, actionable feedback with line references where possible.
134
+
135
+ tools:
136
+ - read
137
+ - grep
138
+ - glob
139
+
140
+ adapters:
141
+ claude-code:
142
+ type: command
143
+ `;
144
+ }
145
+ export {
146
+ SkillRegistry,
147
+ codeReviewTemplate,
148
+ defaultSkillTemplate,
149
+ loadSkillFromFile,
150
+ loadSkillsFromDir
151
+ };
152
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/loader.ts","../src/registry.ts","../src/templates/default.ts"],"sourcesContent":["import { readdirSync, existsSync } from \"node:fs\";\nimport { join, extname, basename } from \"node:path\";\nimport {\n loadConfig,\n ConfigLoadError,\n ConfigValidationError,\n SkillDefinitionSchema,\n type ResolvedSkill,\n type AdapterName,\n} from \"@cockpit-ai/core\";\n\n// ─── Skill Loader ──────────────────────────────────────────────────────────\n\n/**\n * Load a single skill from a YAML file and resolve it to ResolvedSkill.\n * Throws on validation errors; returns null if file doesn't exist.\n */\nexport function loadSkillFromFile(filePath: string): ResolvedSkill {\n const raw = loadConfig(filePath, SkillDefinitionSchema);\n\n return {\n name: raw.name,\n version: raw.version,\n description: raw.description,\n trigger: raw.trigger ?? [],\n prompt: raw.prompt,\n tools: raw.tools ?? [],\n sourcePath: filePath,\n adapterConfig: (raw.adapters ?? {}) as Partial<Record<AdapterName, { type?: string; alwaysApply?: boolean }>>,\n };\n}\n\n/**\n * Load all skills from a directory (*.yaml, *.yml files).\n * Skips files that fail to load, collecting errors separately.\n */\nexport function loadSkillsFromDir(dirPath: string): {\n skills: ResolvedSkill[];\n errors: Array<{ file: string; error: unknown }>;\n} {\n if (!existsSync(dirPath)) {\n return { skills: [], errors: [] };\n }\n\n const skills: ResolvedSkill[] = [];\n const errors: Array<{ file: string; error: unknown }> = [];\n\n for (const entry of readdirSync(dirPath, { withFileTypes: true })) {\n if (!entry.isFile()) continue;\n const ext = extname(entry.name);\n if (ext !== \".yaml\" && ext !== \".yml\") continue;\n\n const filePath = join(dirPath, entry.name);\n try {\n skills.push(loadSkillFromFile(filePath));\n } catch (err) {\n if (err instanceof ConfigLoadError || err instanceof ConfigValidationError) {\n errors.push({ file: filePath, error: err });\n } else {\n throw err;\n }\n }\n }\n\n return { skills, errors };\n}\n","import { type ResolvedSkill } from \"@cockpit-ai/core\";\nimport { loadSkillsFromDir } from \"./loader.js\";\n\n// ─── Skill Registry ────────────────────────────────────────────────────────\n\nexport class SkillRegistry {\n private skills = new Map<string, ResolvedSkill>();\n\n add(skill: ResolvedSkill): void {\n this.skills.set(skill.name, skill);\n }\n\n get(name: string): ResolvedSkill | undefined {\n return this.skills.get(name);\n }\n\n has(name: string): boolean {\n return this.skills.has(name);\n }\n\n list(): ResolvedSkill[] {\n return [...this.skills.values()];\n }\n\n remove(name: string): boolean {\n return this.skills.delete(name);\n }\n\n size(): number {\n return this.skills.size;\n }\n\n /**\n * Load all skills from one or more directories into the registry.\n * Returns any load errors encountered.\n */\n loadFromDirs(dirs: string[]): Array<{ file: string; error: unknown }> {\n const allErrors: Array<{ file: string; error: unknown }> = [];\n\n for (const dir of dirs) {\n const { skills, errors } = loadSkillsFromDir(dir);\n for (const skill of skills) {\n this.add(skill);\n }\n allErrors.push(...errors);\n }\n\n return allErrors;\n }\n}\n","/**\n * Returns the YAML content for a new skill template.\n */\nexport function defaultSkillTemplate(name: string): string {\n return `name: ${name}\nversion: 1.0.0\ndescription: ${name} skill\n\n# Triggers that activate this skill (for tools that support slash commands)\ntrigger:\n - \"/${name}\"\n\n# The prompt sent to the AI when this skill is triggered\nprompt: |\n Describe what this skill should do.\n Add detailed instructions here.\n\n# Tools the AI is allowed to use (leave empty for no restriction)\ntools:\n - read\n - grep\n - glob\n\n# Per-adapter overrides (optional)\nadapters:\n claude-code:\n type: command # creates .claude/commands/${name}.md\n cursor:\n type: rule # creates .cursor/rules/${name}.mdc\n alwaysApply: false\n`;\n}\n\n/**\n * Returns the YAML content for a pre-built \"code-review\" skill.\n */\nexport function codeReviewTemplate(): string {\n return `name: code-review\nversion: 1.0.0\ndescription: Comprehensive code review\n\ntrigger:\n - \"/review\"\n - \"review\"\n\nprompt: |\n Review the provided code thoroughly:\n\n 1. **Bugs & Edge Cases** — identify logic errors, unhandled cases, off-by-one errors\n 2. **Performance** — flag inefficient algorithms, unnecessary re-renders, N+1 queries\n 3. **Security** — check for injection, XSS, auth issues, sensitive data exposure\n 4. **Readability** — suggest clearer names, simpler logic, better structure\n 5. **Tests** — note missing test coverage for critical paths\n\n Provide specific, actionable feedback with line references where possible.\n\ntools:\n - read\n - grep\n - glob\n\nadapters:\n claude-code:\n type: command\n`;\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@cockpit-ai/skills",
3
+ "version": "0.1.0",
4
+ "description": "Cockpit skill loader and registry",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "dependencies": {
24
+ "yaml": "^2.4.0",
25
+ "@cockpit-ai/core": "0.1.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.0.0",
29
+ "tsup": "^8.0.0",
30
+ "typescript": "^5.5.0",
31
+ "vitest": "^2.0.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "dev": "tsup --watch",
36
+ "test": "vitest run",
37
+ "test:watch": "vitest",
38
+ "clean": "rm -rf dist"
39
+ }
40
+ }