@devxiyang/agent-skill 0.0.7 → 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/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # agent.skill
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@devxiyang/agent-skill)](https://www.npmjs.com/package/@devxiyang/agent-skill)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@devxiyang/agent-skill)](https://www.npmjs.com/package/@devxiyang/agent-skill)
5
+ [![node >=18](https://img.shields.io/node/v/@devxiyang/agent-skill)](https://www.npmjs.com/package/@devxiyang/agent-skill)
6
+ [![license](https://img.shields.io/npm/l/@devxiyang/agent-skill)](./LICENSE)
7
+
3
8
  SDK for skill discovery and registration — integrates into any agent.
4
9
 
5
10
  A **skill** is a folder containing a `SKILL.md` file (with YAML frontmatter + instructions) and optional resources (scripts, references, assets). This SDK provides the tooling to discover, validate, and load skills into an agent's context.
package/dist/builtin.d.ts CHANGED
@@ -6,6 +6,8 @@
6
6
  * as a system-scoped root:
7
7
  *
8
8
  * ```ts
9
+ * import { SkillDiscovery, builtinSkillsRoot } from '@devxiyang/agent-skill-core';
10
+ *
9
11
  * new SkillDiscovery([
10
12
  * { path: userSkillsDir, scope: 'user' },
11
13
  * { path: builtinSkillsRoot(), scope: 'system' },
@@ -13,7 +15,7 @@
13
15
  * ```
14
16
  *
15
17
  * Note for Electron apps: if the app is packaged with asar, add
16
- * `node_modules/@devxiyang/agent-skill/skills` to `asarUnpack` in
18
+ * `node_modules/@devxiyang/agent-skill-core/skills` to `asarUnpack` in
17
19
  * electron-builder config so the directory is accessible via the filesystem.
18
20
  */
19
21
  export declare function builtinSkillsRoot(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"builtin.d.ts","sourceRoot":"","sources":["../src/builtin.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAG1C"}
1
+ {"version":3,"file":"builtin.d.ts","sourceRoot":"","sources":["../src/builtin.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAG1C"}
package/dist/builtin.js CHANGED
@@ -8,6 +8,8 @@ import { fileURLToPath } from 'node:url';
8
8
  * as a system-scoped root:
9
9
  *
10
10
  * ```ts
11
+ * import { SkillDiscovery, builtinSkillsRoot } from '@devxiyang/agent-skill-core';
12
+ *
11
13
  * new SkillDiscovery([
12
14
  * { path: userSkillsDir, scope: 'user' },
13
15
  * { path: builtinSkillsRoot(), scope: 'system' },
@@ -15,7 +17,7 @@ import { fileURLToPath } from 'node:url';
15
17
  * ```
16
18
  *
17
19
  * Note for Electron apps: if the app is packaged with asar, add
18
- * `node_modules/@devxiyang/agent-skill/skills` to `asarUnpack` in
20
+ * `node_modules/@devxiyang/agent-skill-core/skills` to `asarUnpack` in
19
21
  * electron-builder config so the directory is accessible via the filesystem.
20
22
  */
21
23
  export function builtinSkillsRoot() {
@@ -1 +1 @@
1
- {"version":3,"file":"builtin.js","sourceRoot":"","sources":["../src/builtin.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC"}
1
+ {"version":3,"file":"builtin.js","sourceRoot":"","sources":["../src/builtin.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,8 +2,8 @@ export type { SkillEntry, SkillRoot, SkillScope, SkillMissingReason, SkillMissin
2
2
  export type { SkillValidator } from './validator.js';
3
3
  export { SkillDiscovery, parseFrontmatter } from './discovery/index.js';
4
4
  export type { SkillFrontmatter } from './discovery/index.js';
5
- export { builtinSkillsRoot } from './builtin.js';
6
5
  export { defaultValidator } from './validators/index.js';
7
6
  export { copySkills } from './copy.js';
8
7
  export type { CopySkillsOptions, CopySkillsResult } from './copy.js';
8
+ export { builtinSkillsRoot } from './builtin.js';
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAChH,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAChH,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { SkillDiscovery, parseFrontmatter } from './discovery/index.js';
2
- export { builtinSkillsRoot } from './builtin.js';
3
2
  export { defaultValidator } from './validators/index.js';
4
3
  export { copySkills } from './copy.js';
4
+ export { builtinSkillsRoot } from './builtin.js';
5
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devxiyang/agent-skill",
3
- "version": "0.0.7",
3
+ "version": "0.1.0",
4
4
  "description": "SDK for skill discovery and registration — integrates into any agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,151 @@
1
+ ---
2
+ name: python
3
+ description: Run Python scripts, manage environments, and use common stdlib patterns. Use for scripting, data processing, automation, and general Python development.
4
+ requires: bin:python3
5
+ tags: python,scripting
6
+ ---
7
+
8
+ # Python Skill
9
+
10
+ ## Preflight
11
+
12
+ Verify Python is available before proceeding:
13
+
14
+ ```bash
15
+ python3 --version
16
+ ```
17
+
18
+ If missing, load `references/install.md` for installation instructions.
19
+
20
+ ## Running scripts
21
+
22
+ ```bash
23
+ python3 script.py
24
+ python3 script.py arg1 arg2
25
+ python3 -c "print('hello')"
26
+
27
+ # Run a module
28
+ python3 -m http.server 8000
29
+ python3 -m json.tool data.json
30
+ ```
31
+
32
+ ## Virtual environments
33
+
34
+ ### venv (built-in)
35
+
36
+ ```bash
37
+ # Create
38
+ python3 -m venv .venv
39
+
40
+ # Activate
41
+ source .venv/bin/activate # macOS/Linux
42
+ .venv\Scripts\activate # Windows
43
+
44
+ # Deactivate
45
+ deactivate
46
+ ```
47
+
48
+ ### uv (fast, recommended)
49
+
50
+ ```bash
51
+ # Install uv
52
+ curl -LsSf https://astral.sh/uv/install.sh | sh # macOS/Linux
53
+ powershell -c "irm https://astral.sh/uv/install.ps1 | iex" # Windows
54
+
55
+ # Create and activate venv
56
+ uv venv
57
+ source .venv/bin/activate
58
+
59
+ # Run without activating
60
+ uv run script.py
61
+ ```
62
+
63
+ ## Package management
64
+
65
+ ### pip
66
+
67
+ ```bash
68
+ pip install requests
69
+ pip install -r requirements.txt
70
+ pip freeze > requirements.txt
71
+ pip list --outdated
72
+ ```
73
+
74
+ ### uv (faster alternative)
75
+
76
+ ```bash
77
+ uv pip install requests
78
+ uv pip install -r requirements.txt
79
+ uv pip freeze > requirements.txt
80
+ ```
81
+
82
+ ## Common stdlib patterns
83
+
84
+ ### File I/O (pathlib)
85
+
86
+ ```python
87
+ from pathlib import Path
88
+
89
+ p = Path("data/file.txt")
90
+ text = p.read_text()
91
+ p.write_text("content")
92
+
93
+ # Iterate files
94
+ for f in Path("src").rglob("*.py"):
95
+ print(f)
96
+ ```
97
+
98
+ ### JSON
99
+
100
+ ```python
101
+ import json
102
+
103
+ # Parse
104
+ data = json.loads('{"key": "value"}')
105
+ data = json.load(open("data.json"))
106
+
107
+ # Serialize
108
+ json.dumps(data, indent=2)
109
+ json.dump(data, open("out.json", "w"), indent=2)
110
+ ```
111
+
112
+ ### HTTP requests
113
+
114
+ ```python
115
+ # Built-in (no deps)
116
+ from urllib.request import urlopen
117
+ import json
118
+
119
+ with urlopen("https://api.example.com/data") as r:
120
+ data = json.loads(r.read())
121
+
122
+ # With requests (install first)
123
+ import requests
124
+ r = requests.get("https://api.example.com/data")
125
+ data = r.json()
126
+ ```
127
+
128
+ ### Subprocess
129
+
130
+ ```python
131
+ import subprocess
132
+
133
+ # Run and capture output
134
+ result = subprocess.run(["git", "status"], capture_output=True, text=True)
135
+ print(result.stdout)
136
+
137
+ # Check for errors
138
+ result = subprocess.run(["npm", "test"], check=True)
139
+ ```
140
+
141
+ ### Argument parsing
142
+
143
+ ```python
144
+ import argparse
145
+
146
+ parser = argparse.ArgumentParser(description="My script")
147
+ parser.add_argument("input", help="Input file")
148
+ parser.add_argument("--output", "-o", default="out.txt")
149
+ parser.add_argument("--verbose", "-v", action="store_true")
150
+ args = parser.parse_args()
151
+ ```
@@ -0,0 +1,52 @@
1
+ # Installing Python
2
+
3
+ ## macOS
4
+
5
+ macOS ships with Python 3 from Xcode Command Line Tools:
6
+
7
+ ```bash
8
+ xcode-select --install
9
+ ```
10
+
11
+ To get a newer version, download from https://www.python.org/downloads/mac-osx/ and run the `.pkg` installer — no Homebrew needed.
12
+
13
+ If you have Homebrew:
14
+
15
+ ```bash
16
+ brew install python3
17
+ ```
18
+
19
+ ## Windows
20
+
21
+ Download the installer from https://www.python.org/downloads/windows/ — pick the latest stable release and run the `.exe` installer. Check "Add Python to PATH" during installation.
22
+
23
+ If winget is available:
24
+
25
+ ```powershell
26
+ winget install Python.Python.3
27
+ ```
28
+
29
+ ## Linux
30
+
31
+ ```bash
32
+ # Debian/Ubuntu
33
+ sudo apt update && sudo apt install python3 python3-pip python3-venv
34
+
35
+ # Fedora
36
+ sudo dnf install python3 python3-pip
37
+
38
+ # Arch
39
+ sudo pacman -S python
40
+ ```
41
+
42
+ ## uv (optional, fast package manager)
43
+
44
+ uv is a fast Python package and environment manager. Install it separately:
45
+
46
+ ```bash
47
+ # macOS/Linux
48
+ curl -LsSf https://astral.sh/uv/install.sh | sh
49
+
50
+ # Windows
51
+ powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
52
+ ```
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: shell
3
+ description: Write and run shell scripts. Use for automation, file operations, pipelines, and system tasks. Covers bash/zsh (macOS/Linux) and PowerShell (Windows).
4
+ tags: shell,cli
5
+ ---
6
+
7
+ # Shell Skill
8
+
9
+ ## Unix (bash/zsh)
10
+
11
+ ### Variables & strings
12
+
13
+ ```bash
14
+ name="alice"
15
+ echo "Hello, $name"
16
+ echo "Home is ${HOME}"
17
+
18
+ # Command substitution
19
+ files=$(ls -1 | wc -l)
20
+ today=$(date +%Y-%m-%d)
21
+ ```
22
+
23
+ ### Conditionals
24
+
25
+ ```bash
26
+ if [ -f "file.txt" ]; then
27
+ echo "exists"
28
+ elif [ -d "dir" ]; then
29
+ echo "is a directory"
30
+ else
31
+ echo "not found"
32
+ fi
33
+
34
+ # One-liner
35
+ [ -f "file.txt" ] && echo "exists" || echo "missing"
36
+ ```
37
+
38
+ ### Loops
39
+
40
+ ```bash
41
+ # Over a list
42
+ for name in alice bob carol; do
43
+ echo "Hello, $name"
44
+ done
45
+
46
+ # Over files
47
+ for f in *.log; do
48
+ echo "Processing $f"
49
+ done
50
+
51
+ # While loop
52
+ while IFS= read -r line; do
53
+ echo "$line"
54
+ done < input.txt
55
+ ```
56
+
57
+ ### Functions
58
+
59
+ ```bash
60
+ greet() {
61
+ local name="$1"
62
+ echo "Hello, $name"
63
+ }
64
+ greet "alice"
65
+ ```
66
+
67
+ ### Pipes & redirection
68
+
69
+ ```bash
70
+ # Pipe output
71
+ cat file.txt | grep "error" | sort | uniq -c
72
+
73
+ # Redirect stdout
74
+ echo "log" >> output.log
75
+
76
+ # Redirect stderr
77
+ command 2>> errors.log
78
+
79
+ # Redirect both
80
+ command > output.log 2>&1
81
+
82
+ # Discard output
83
+ command > /dev/null 2>&1
84
+ ```
85
+
86
+ ### Error handling
87
+
88
+ ```bash
89
+ # Exit on first error
90
+ set -e
91
+
92
+ # Exit on unset variable
93
+ set -u
94
+
95
+ # Catch pipe failures
96
+ set -o pipefail
97
+
98
+ # Combine (recommended for scripts)
99
+ set -euo pipefail
100
+
101
+ # Check exit code
102
+ if ! command; then
103
+ echo "command failed"
104
+ fi
105
+ ```
106
+
107
+ ### Useful patterns
108
+
109
+ ```bash
110
+ # Find files by name
111
+ find . -name "*.log" -type f
112
+
113
+ # Find and delete
114
+ find . -name "*.tmp" -type f -delete
115
+
116
+ # Find and execute
117
+ find . -name "*.js" -exec wc -l {} +
118
+
119
+ # Filter with grep
120
+ grep -r "TODO" src/ --include="*.ts"
121
+
122
+ # Transform with awk
123
+ awk '{print $1, $3}' data.txt
124
+
125
+ # Process with xargs
126
+ find . -name "*.log" | xargs rm -f
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Windows (PowerShell)
132
+
133
+ ### Variables & strings
134
+
135
+ ```powershell
136
+ $name = "alice"
137
+ Write-Output "Hello, $name"
138
+
139
+ # Command substitution
140
+ $files = (Get-ChildItem).Count
141
+ $today = Get-Date -Format "yyyy-MM-dd"
142
+ ```
143
+
144
+ ### Conditionals
145
+
146
+ ```powershell
147
+ if (Test-Path "file.txt") {
148
+ Write-Output "exists"
149
+ } elseif (Test-Path "dir" -PathType Container) {
150
+ Write-Output "is a directory"
151
+ } else {
152
+ Write-Output "not found"
153
+ }
154
+ ```
155
+
156
+ ### Loops
157
+
158
+ ```powershell
159
+ # Over a list
160
+ foreach ($name in "alice", "bob", "carol") {
161
+ Write-Output "Hello, $name"
162
+ }
163
+
164
+ # Over files
165
+ Get-ChildItem *.log | ForEach-Object {
166
+ Write-Output "Processing $($_.Name)"
167
+ }
168
+ ```
169
+
170
+ ### Functions
171
+
172
+ ```powershell
173
+ function Greet {
174
+ param($Name)
175
+ Write-Output "Hello, $Name"
176
+ }
177
+ Greet "alice"
178
+ ```
179
+
180
+ ### Pipes & redirection
181
+
182
+ ```powershell
183
+ # Pipe objects
184
+ Get-Content file.txt | Select-String "error" | Sort-Object | Get-Unique
185
+
186
+ # Redirect to file
187
+ "log" | Out-File -Append output.log
188
+
189
+ # Discard output
190
+ command | Out-Null
191
+ ```
192
+
193
+ ### Error handling
194
+
195
+ ```powershell
196
+ # Stop on error
197
+ $ErrorActionPreference = "Stop"
198
+
199
+ # Try/catch
200
+ try {
201
+ SomeCommand
202
+ } catch {
203
+ Write-Error "Failed: $_"
204
+ }
205
+ ```
206
+
207
+ ### Useful patterns
208
+
209
+ ```powershell
210
+ # Find files
211
+ Get-ChildItem -Recurse -Filter "*.log"
212
+
213
+ # Find and delete
214
+ Get-ChildItem -Recurse -Filter "*.tmp" | Remove-Item
215
+
216
+ # Search in files
217
+ Select-String -Path "src\*.ts" -Pattern "TODO"
218
+ ```
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=copy.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"copy.test.d.ts","sourceRoot":"","sources":["../src/copy.test.ts"],"names":[],"mappings":""}
package/dist/copy.test.js DELETED
@@ -1,66 +0,0 @@
1
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import os from 'node:os';
5
- import { copySkills } from './copy.js';
6
- let tmpDir;
7
- let srcRoot;
8
- beforeAll(async () => {
9
- tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'agent-skill-copy-test-'));
10
- srcRoot = path.join(tmpDir, 'src');
11
- // Create fixture skills
12
- for (const name of ['git', 'github', 'web']) {
13
- const dir = path.join(srcRoot, name);
14
- await fs.mkdir(dir, { recursive: true });
15
- await fs.writeFile(path.join(dir, 'SKILL.md'), `---\nname: ${name}\n---\n`);
16
- }
17
- // A dir without SKILL.md (should be ignored)
18
- await fs.mkdir(path.join(srcRoot, 'not-a-skill'), { recursive: true });
19
- });
20
- afterAll(async () => {
21
- await fs.rm(tmpDir, { recursive: true, force: true });
22
- });
23
- describe('copySkills', () => {
24
- it('copies all skills to target', async () => {
25
- const to = path.join(tmpDir, 'dest-all');
26
- const result = await copySkills({ from: srcRoot, to });
27
- expect(result.copied.sort()).toEqual(['git', 'github', 'web']);
28
- expect(result.skipped).toEqual([]);
29
- for (const name of result.copied) {
30
- const skillMd = path.join(to, name, 'SKILL.md');
31
- await expect(fs.access(skillMd)).resolves.toBeUndefined();
32
- }
33
- });
34
- it('filters by skill names', async () => {
35
- const to = path.join(tmpDir, 'dest-filter');
36
- const result = await copySkills({ from: srcRoot, to, skills: ['git', 'web'] });
37
- expect(result.copied.sort()).toEqual(['git', 'web']);
38
- expect(result.skipped).toEqual([]);
39
- await expect(fs.access(path.join(to, 'github'))).rejects.toThrow();
40
- });
41
- it('skips existing skills by default', async () => {
42
- const to = path.join(tmpDir, 'dest-skip');
43
- await copySkills({ from: srcRoot, to });
44
- const result = await copySkills({ from: srcRoot, to });
45
- expect(result.copied).toEqual([]);
46
- expect(result.skipped.sort()).toEqual(['git', 'github', 'web']);
47
- });
48
- it('overwrites existing skills when overwrite: true', async () => {
49
- const to = path.join(tmpDir, 'dest-overwrite');
50
- await copySkills({ from: srcRoot, to });
51
- const result = await copySkills({ from: srcRoot, to, overwrite: true });
52
- expect(result.copied.sort()).toEqual(['git', 'github', 'web']);
53
- expect(result.skipped).toEqual([]);
54
- });
55
- it('ignores directories without SKILL.md', async () => {
56
- const to = path.join(tmpDir, 'dest-no-skill-md');
57
- const result = await copySkills({ from: srcRoot, to });
58
- expect(result.copied).not.toContain('not-a-skill');
59
- });
60
- it('creates target directory if it does not exist', async () => {
61
- const to = path.join(tmpDir, 'new', 'nested', 'dest');
62
- await copySkills({ from: srcRoot, to });
63
- await expect(fs.access(to)).resolves.toBeUndefined();
64
- });
65
- });
66
- //# sourceMappingURL=copy.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"copy.test.js","sourceRoot":"","sources":["../src/copy.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=discovery.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"discovery.test.d.ts","sourceRoot":"","sources":["../../src/discovery/discovery.test.ts"],"names":[],"mappings":""}
@@ -1,128 +0,0 @@
1
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import os from 'node:os';
5
- import { SkillDiscovery } from './discovery.js';
6
- // ---------------------------------------------------------------------------
7
- // Fixture helpers
8
- // ---------------------------------------------------------------------------
9
- async function makeSkillDir(root, name, frontmatter, body = '') {
10
- const dir = path.join(root, name);
11
- await fs.mkdir(dir, { recursive: true });
12
- await fs.writeFile(path.join(dir, 'SKILL.md'), `---\n${frontmatter}\n---\n${body}`);
13
- return dir;
14
- }
15
- let tmpDir;
16
- beforeAll(async () => {
17
- tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'agent-skill-test-'));
18
- });
19
- afterAll(async () => {
20
- await fs.rm(tmpDir, { recursive: true, force: true });
21
- });
22
- // ---------------------------------------------------------------------------
23
- describe('SkillDiscovery.list()', () => {
24
- it('returns empty list when root does not exist', async () => {
25
- const d = new SkillDiscovery([{ path: '/nonexistent/path', scope: 'system' }]);
26
- expect(await d.list()).toEqual([]);
27
- });
28
- it('discovers a valid skill', async () => {
29
- const root = path.join(tmpDir, 'basic');
30
- await makeSkillDir(root, 'git', 'name: git\ndescription: Git skill');
31
- const d = new SkillDiscovery([{ path: root, scope: 'system' }]);
32
- const entries = await d.list();
33
- expect(entries).toHaveLength(1);
34
- expect(entries[0].name).toBe('git');
35
- expect(entries[0].description).toBe('Git skill');
36
- expect(entries[0].scope).toBe('system');
37
- });
38
- it('skips directories without SKILL.md', async () => {
39
- const root = path.join(tmpDir, 'skip');
40
- await fs.mkdir(path.join(root, 'empty-dir'), { recursive: true });
41
- await makeSkillDir(root, 'real', 'name: real');
42
- const d = new SkillDiscovery([{ path: root, scope: 'system' }]);
43
- const entries = await d.list();
44
- expect(entries).toHaveLength(1);
45
- expect(entries[0].name).toBe('real');
46
- });
47
- it('user scope takes priority over system scope for same name', async () => {
48
- const userRoot = path.join(tmpDir, 'priority-user');
49
- const sysRoot = path.join(tmpDir, 'priority-sys');
50
- await makeSkillDir(userRoot, 'shared', 'name: shared\ndescription: user version');
51
- await makeSkillDir(sysRoot, 'shared', 'name: shared\ndescription: system version');
52
- const d = new SkillDiscovery([
53
- { path: userRoot, scope: 'user' },
54
- { path: sysRoot, scope: 'system' },
55
- ]);
56
- const entries = await d.list();
57
- expect(entries).toHaveLength(1);
58
- expect(entries[0].description).toBe('user version');
59
- expect(entries[0].scope).toBe('user');
60
- });
61
- it('returns entries sorted by name', async () => {
62
- const root = path.join(tmpDir, 'sorted');
63
- for (const name of ['zebra', 'alpha', 'mango']) {
64
- await makeSkillDir(root, name, `name: ${name}`);
65
- }
66
- const d = new SkillDiscovery([{ path: root, scope: 'system' }]);
67
- const names = (await d.list()).map((e) => e.name);
68
- expect(names).toEqual([...names].sort());
69
- });
70
- it('marks skill ineligible when validator reports missing bin', async () => {
71
- const root = path.join(tmpDir, 'ineligible-bin');
72
- await makeSkillDir(root, 'gh', 'name: gh\nrequires: bin:definitely-not-a-real-bin-xyz');
73
- const validator = {
74
- checkBin: async () => false,
75
- checkEnv: () => true,
76
- checkOs: () => true,
77
- };
78
- const d = new SkillDiscovery([{ path: root, scope: 'system' }], validator);
79
- const [entry] = await d.list();
80
- expect(entry.eligible).toBe(false);
81
- expect(entry.missing[0].kind).toBe('bin');
82
- });
83
- it('marks skill ineligible when validator reports missing env', async () => {
84
- const root = path.join(tmpDir, 'ineligible-env');
85
- await makeSkillDir(root, 'api', 'name: api\nrequires: env:MISSING_API_KEY');
86
- const validator = {
87
- checkBin: async () => true,
88
- checkEnv: () => false,
89
- checkOs: () => true,
90
- };
91
- const d = new SkillDiscovery([{ path: root, scope: 'system' }], validator);
92
- const [entry] = await d.list();
93
- expect(entry.eligible).toBe(false);
94
- expect(entry.missing[0].kind).toBe('env');
95
- });
96
- it('marks skill ineligible when OS does not match', async () => {
97
- const root = path.join(tmpDir, 'ineligible-os');
98
- await makeSkillDir(root, 'platform', `name: platform\nos: unsupported-os-xyz`);
99
- const validator = {
100
- checkBin: async () => true,
101
- checkEnv: () => true,
102
- checkOs: () => false,
103
- };
104
- const d = new SkillDiscovery([{ path: root, scope: 'system' }], validator);
105
- const [entry] = await d.list();
106
- expect(entry.eligible).toBe(false);
107
- expect(entry.missing[0].kind).toBe('os');
108
- });
109
- it('exposes always flag', async () => {
110
- const root = path.join(tmpDir, 'always');
111
- await makeSkillDir(root, 'memory', 'name: memory\nalways: true');
112
- const d = new SkillDiscovery([{ path: root, scope: 'system' }]);
113
- const [entry] = await d.list();
114
- expect(entry.always).toBe(true);
115
- });
116
- });
117
- describe('SkillDiscovery.load()', () => {
118
- it('returns full SKILL.md content including frontmatter', async () => {
119
- const root = path.join(tmpDir, 'load');
120
- const dir = await makeSkillDir(root, 'mskill', 'name: mskill', 'This is the body.');
121
- const filePath = path.join(dir, 'SKILL.md');
122
- const d = new SkillDiscovery([{ path: root, scope: 'system' }]);
123
- const content = await d.load(filePath);
124
- expect(content).toContain('name: mskill');
125
- expect(content).toContain('This is the body.');
126
- });
127
- });
128
- //# sourceMappingURL=discovery.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../../src/discovery/discovery.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=frontmatter.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"frontmatter.test.d.ts","sourceRoot":"","sources":["../../src/discovery/frontmatter.test.ts"],"names":[],"mappings":""}
@@ -1,39 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { parseFrontmatter } from './frontmatter.js';
3
- const wrap = (body, content = '') => `---\n${body}\n---\n${content}`;
4
- describe('parseFrontmatter', () => {
5
- it('returns defaults when no frontmatter', () => {
6
- const fm = parseFrontmatter('just markdown');
7
- expect(fm.name).toBeNull();
8
- expect(fm.description).toBeNull();
9
- expect(fm.always).toBe(false);
10
- expect(fm.requiresBins).toEqual([]);
11
- expect(fm.requiresEnvs).toEqual([]);
12
- expect(fm.requiresOs).toEqual([]);
13
- });
14
- it('parses basic fields', () => {
15
- const fm = parseFrontmatter(wrap('name: github\ndescription: GitHub CLI skill\nalways: true'));
16
- expect(fm.name).toBe('github');
17
- expect(fm.description).toBe('GitHub CLI skill');
18
- expect(fm.always).toBe(true);
19
- });
20
- it('parses requires string', () => {
21
- const fm = parseFrontmatter(wrap('name: git\nrequires: bin:git,env:GITHUB_TOKEN'));
22
- expect(fm.requiresBins).toContain('git');
23
- expect(fm.requiresEnvs).toContain('GITHUB_TOKEN');
24
- });
25
- it('handles quoted description', () => {
26
- const fm = parseFrontmatter(wrap('name: x\ndescription: "hello world"'));
27
- expect(fm.description).toBe('hello world');
28
- });
29
- it('ignores unknown boolean values and uses fallback', () => {
30
- const fm = parseFrontmatter(wrap('name: x\nalways: maybe'));
31
- expect(fm.always).toBe(false);
32
- });
33
- it('parses top-level os field', () => {
34
- const fm = parseFrontmatter(wrap('name: tmux\nos: darwin,linux'));
35
- expect(fm.requiresOs).toContain('darwin');
36
- expect(fm.requiresOs).toContain('linux');
37
- });
38
- });
39
- //# sourceMappingURL=frontmatter.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"frontmatter.test.js","sourceRoot":"","sources":["../../src/discovery/frontmatter.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAC1C,QAAQ,IAAI,UAAU,OAAO,EAAE,CAAC;AAElC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,EAAE,GAAG,gBAAgB,CAAC,eAAe,CAAC,CAAC;QAC7C,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC,CAAC;QAC/F,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/B,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAChD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC,CAAC;QACnF,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC,CAAC;QACzE,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAC5D,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC,CAAC;QAClE,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,50 +0,0 @@
1
- ---
2
- name: weather
3
- description: Get current weather and forecasts for any location. No API key required.
4
- requires: bin:curl
5
- tags: weather,web
6
- ---
7
-
8
- # Weather Skill
9
-
10
- ## Preflight
11
-
12
- Verify curl is available before proceeding:
13
-
14
- ```bash
15
- curl --version
16
- ```
17
-
18
- If missing, load `references/install.md` for installation instructions.
19
-
20
- Two free services, no API keys needed.
21
-
22
- ## wttr.in (primary)
23
-
24
- ```bash
25
- # One-liner
26
- curl -s "wttr.in/London?format=3"
27
- # London: ⛅️ +8°C
28
-
29
- # Compact format
30
- curl -s "wttr.in/London?format=%l:+%c+%t+%h+%w"
31
-
32
- # Full 3-day forecast
33
- curl -s "wttr.in/London?T"
34
- ```
35
-
36
- Format codes: `%c` condition · `%t` temp · `%h` humidity · `%w` wind · `%l` location
37
-
38
- Tips:
39
- - URL-encode spaces: `wttr.in/New+York`
40
- - Airport codes work: `wttr.in/JFK`
41
- - Units: `?m` metric · `?u` imperial
42
- - Today only: `?1` · Current only: `?0`
43
-
44
- ## Open-Meteo (fallback, JSON, no key)
45
-
46
- ```bash
47
- curl -s "https://api.open-meteo.com/v1/forecast?latitude=51.5&longitude=-0.12&current_weather=true"
48
- ```
49
-
50
- Get coordinates first, then query. Returns JSON with temperature, windspeed, and weather code.
@@ -1,51 +0,0 @@
1
- # Installing curl
2
-
3
- curl is pre-installed on macOS and Windows 10 (version 1803+). Check first:
4
-
5
- ```bash
6
- curl --version
7
- ```
8
-
9
- Install only if the command is not found.
10
-
11
- ## macOS
12
-
13
- curl ships with every macOS installation. If the command is somehow missing, it likely indicates a system issue rather than a missing package. Try reinstalling Xcode Command Line Tools:
14
-
15
- ```bash
16
- xcode-select --install
17
- ```
18
-
19
- If you need a newer version and have Homebrew:
20
-
21
- ```bash
22
- brew install curl
23
- ```
24
-
25
- ## Windows
26
-
27
- curl is bundled with Windows 10 version 1803+ as `curl.exe`. If missing:
28
-
29
- Download from https://curl.se/windows/ and add the binary to your PATH.
30
-
31
- If winget is available:
32
-
33
- ```powershell
34
- winget install cURL.cURL
35
- ```
36
-
37
- ## Linux
38
-
39
- ```bash
40
- # Debian/Ubuntu
41
- sudo apt update && sudo apt install curl
42
-
43
- # Fedora
44
- sudo dnf install curl
45
-
46
- # Arch
47
- sudo pacman -S curl
48
-
49
- # openSUSE
50
- sudo zypper install curl
51
- ```