@stacksjs/skills 0.70.163 → 0.70.165

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/README.md CHANGED
@@ -353,7 +353,7 @@ For help, discussion about best practices, or any other conversation that would
353
353
 
354
354
  For casual chit-chat with others using this package:
355
355
 
356
- [Join the Stacks Discord Server](https://discord.gg/stacksjs)
356
+ [Join the Stacks Discord Server](https://stacksjs.com/discord)
357
357
 
358
358
  ## Postcardware
359
359
 
package/dist/index.d.ts CHANGED
@@ -1,2 +1,10 @@
1
- export type { Skill, SkillMetadata, SkillManifest } from './types';
2
- export { listSkills, getSkill, validateSkill, loadSkillMetadata } from './skills';
1
+ export type { Skill, SkillManifest, SkillMetadata } from './types';
2
+ export {
3
+ bundledSkillsPath,
4
+ getSkill,
5
+ listSkills,
6
+ loadSkillMetadata,
7
+ projectSkillsPath,
8
+ resolveSkillPath,
9
+ validateSkill,
10
+ } from './skills';
package/dist/index.js CHANGED
@@ -1 +1,9 @@
1
- export { listSkills, getSkill, validateSkill, loadSkillMetadata } from "./skills";
1
+ export {
2
+ bundledSkillsPath,
3
+ getSkill,
4
+ listSkills,
5
+ loadSkillMetadata,
6
+ projectSkillsPath,
7
+ resolveSkillPath,
8
+ validateSkill
9
+ } from "./skills";
package/dist/skills.d.ts CHANGED
@@ -1,5 +1,28 @@
1
1
  import type { Skill, SkillMetadata } from './types';
2
+ /**
3
+ * Every available skill name, sorted, with project skills shadowing bundled
4
+ * ones of the same name rather than appearing twice.
5
+ */
2
6
  export declare function listSkills(): string[];
3
7
  export declare function getSkill(name: string): Skill | null;
4
8
  export declare function loadSkillMetadata(name: string): SkillMetadata | null;
9
+ /**
10
+ * Checks a skill against the agentskills.io frontmatter rules.
11
+ */
5
12
  export declare function validateSkill(name: string): { valid: boolean, errors: string[] };
13
+ /**
14
+ * The directory the framework's own skills ship from. `buddy setup:ai` reads it;
15
+ * nothing should write into it.
16
+ */
17
+ export declare function bundledSkillsPath(): string;
18
+ /**
19
+ * The directory a project puts its own skills in. Searched before
20
+ * {@link bundledSkillsPath}, so a skill here shadows a bundled one of the same
21
+ * name.
22
+ */
23
+ export declare function projectSkillsPath(): string;
24
+ /**
25
+ * Resolves a skill to its directory on disk, or `null` if no source has it.
26
+ * Exposed so `buddy setup:ai` can link/copy the winning directory per skill.
27
+ */
28
+ export declare function resolveSkillPath(name: string): string | null;
package/dist/skills.js CHANGED
@@ -1,6 +1,19 @@
1
- import { join, resolve } from "@stacksjs/path";
2
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
- const SKILLS_DIR = resolve(join(import.meta.dir, "..", "..", "..", "..", "..", ".claude", "skills"));
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { appPath, frameworkPath, join } from "@stacksjs/path";
3
+ function skillSources() {
4
+ return [
5
+ appPath("Skills"),
6
+ frameworkPath("defaults/ai/skills")
7
+ ];
8
+ }
9
+ function resolveSkillDir(name) {
10
+ for (const source of skillSources()) {
11
+ const dir = join(source, name);
12
+ if (existsSync(join(dir, "SKILL.md")))
13
+ return dir;
14
+ }
15
+ return null;
16
+ }
4
17
  function parseFrontmatter(content) {
5
18
  const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
6
19
  if (!match)
@@ -20,42 +33,42 @@ function parseFrontmatter(content) {
20
33
  return { metadata, body: body ?? "" };
21
34
  }
22
35
  export function listSkills() {
23
- if (!existsSync(SKILLS_DIR))
24
- return [];
25
- return readdirSync(SKILLS_DIR).filter((entry) => {
26
- const entryPath = join(SKILLS_DIR, entry);
27
- return statSync(entryPath).isDirectory() && existsSync(join(entryPath, "SKILL.md"));
28
- });
36
+ const names = new Set;
37
+ for (const source of skillSources()) {
38
+ if (!existsSync(source))
39
+ continue;
40
+ for (const entry of readdirSync(source)) {
41
+ const entryPath = join(source, entry);
42
+ if (statSync(entryPath).isDirectory() && existsSync(join(entryPath, "SKILL.md")))
43
+ names.add(entry);
44
+ }
45
+ }
46
+ return [...names].sort();
29
47
  }
30
48
  export function getSkill(name) {
31
- const skillPath = join(SKILLS_DIR, name, "SKILL.md");
32
- if (!existsSync(skillPath))
49
+ const skillDir = resolveSkillDir(name);
50
+ if (!skillDir)
33
51
  return null;
34
- const content = readFileSync(skillPath, "utf-8"), { metadata, body } = parseFrontmatter(content), skillDir = join(SKILLS_DIR, name), scripts = existsSync(join(skillDir, "scripts")) ? readdirSync(join(skillDir, "scripts")) : [], references = existsSync(join(skillDir, "references")) ? readdirSync(join(skillDir, "references")) : [], assets = existsSync(join(skillDir, "assets")) ? readdirSync(join(skillDir, "assets")) : [];
52
+ const skillPath = join(skillDir, "SKILL.md"), content = readFileSync(skillPath, "utf-8"), { metadata, body } = parseFrontmatter(content), listDir = (dir) => existsSync(join(skillDir, dir)) ? readdirSync(join(skillDir, dir)) : [];
35
53
  return {
36
54
  metadata,
37
55
  instructions: body,
38
56
  path: skillPath,
39
- scripts,
40
- references,
41
- assets
57
+ scripts: listDir("scripts"),
58
+ references: listDir("references"),
59
+ assets: listDir("assets")
42
60
  };
43
61
  }
44
62
  export function loadSkillMetadata(name) {
45
63
  return getSkill(name)?.metadata ?? null;
46
64
  }
47
65
  export function validateSkill(name) {
48
- const errors = [], skillDir = join(SKILLS_DIR, name);
49
- if (!existsSync(skillDir)) {
50
- errors.push(`Skill directory not found: ${skillDir}`);
66
+ const errors = [], skillDir = resolveSkillDir(name);
67
+ if (!skillDir) {
68
+ errors.push(`Skill not found in any source: ${skillSources().map((source) => join(source, name)).join(", ")}`);
51
69
  return { valid: !1, errors };
52
70
  }
53
- const skillPath = join(skillDir, "SKILL.md");
54
- if (!existsSync(skillPath)) {
55
- errors.push(`SKILL.md not found in ${skillDir}`);
56
- return { valid: !1, errors };
57
- }
58
- const content = readFileSync(skillPath, "utf-8"), { metadata } = parseFrontmatter(content);
71
+ const content = readFileSync(join(skillDir, "SKILL.md"), "utf-8"), { metadata } = parseFrontmatter(content);
59
72
  if (!metadata.name)
60
73
  errors.push("Missing required field: name");
61
74
  else {
@@ -72,3 +85,12 @@ export function validateSkill(name) {
72
85
  errors.push("Description must be 1-1024 characters");
73
86
  return { valid: errors.length === 0, errors };
74
87
  }
88
+ export function bundledSkillsPath() {
89
+ return frameworkPath("defaults/ai/skills");
90
+ }
91
+ export function projectSkillsPath() {
92
+ return appPath("Skills");
93
+ }
94
+ export function resolveSkillPath(name) {
95
+ return resolveSkillDir(name);
96
+ }
package/package.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "name": "@stacksjs/skills",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.163",
6
- "description": "Agent skills for the Stacks framework, following the agentskills.io standard.",
5
+ "version": "0.70.165",
6
+ "description": "Reads the Stacks framework's agent skills, following the agentskills.io standard. The skills themselves ship in storage/framework/defaults/ai/skills.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
9
9
  "Chris Breuer <chris@stacksjs.com>"
@@ -45,8 +45,7 @@
45
45
  "types": "dist/index.d.ts",
46
46
  "files": [
47
47
  "README.md",
48
- "dist",
49
- "skills"
48
+ "dist"
50
49
  ],
51
50
  "scripts": {
52
51
  "build": "bun build.ts",
@@ -54,11 +53,11 @@
54
53
  "prepublishOnly": "bun run build"
55
54
  },
56
55
  "dependencies": {
57
- "@stacksjs/path": "0.70.163",
58
- "@stacksjs/storage": "0.70.163"
56
+ "@stacksjs/path": "0.70.165",
57
+ "@stacksjs/storage": "0.70.165"
59
58
  },
60
59
  "devDependencies": {
61
60
  "better-dx": "^0.2.17",
62
- "@stacksjs/types": "0.70.163"
61
+ "@stacksjs/types": "0.70.165"
63
62
  }
64
63
  }