@devxiyang/agent-skill 0.0.6 → 0.0.8

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=copy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.test.d.ts","sourceRoot":"","sources":["../src/copy.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.test.js","sourceRoot":"","sources":["../src/copy.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=discovery.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.test.d.ts","sourceRoot":"","sources":["../../src/discovery/discovery.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,128 @@
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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../../src/discovery/discovery.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=frontmatter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.test.d.ts","sourceRoot":"","sources":["../../src/discovery/frontmatter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,39 @@
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
@@ -0,0 +1 @@
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devxiyang/agent-skill",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "SDK for skill discovery and registration — integrates into any agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,8 +36,8 @@
36
36
  "build": "tsc -p tsconfig.json",
37
37
  "clean": "rm -rf dist",
38
38
  "typecheck": "tsc -p tsconfig.json --noEmit",
39
- "test": "vitest run",
40
- "test:watch": "vitest"
39
+ "test": "vitest run tests",
40
+ "test:watch": "vitest tests"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@types/node": ">=18"
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: jq
3
+ description: Process and transform JSON data using jq. Use when filtering API responses, extracting fields, reshaping JSON, or querying structured data.
4
+ requires: bin:jq
5
+ tags: json,jq
6
+ ---
7
+
8
+ # jq Skill
9
+
10
+ ## Preflight
11
+
12
+ Verify jq is available before proceeding:
13
+
14
+ ```bash
15
+ jq --version
16
+ ```
17
+
18
+ If missing, load `references/install.md` for installation instructions.
19
+
20
+ ## Basic filtering
21
+
22
+ ```bash
23
+ # Pretty-print JSON
24
+ cat data.json | jq .
25
+
26
+ # Extract a field
27
+ echo '{"name":"alice","age":30}' | jq '.name'
28
+
29
+ # Nested field
30
+ echo '{"user":{"email":"a@b.com"}}' | jq '.user.email'
31
+
32
+ # Array index
33
+ echo '[1,2,3]' | jq '.[0]'
34
+
35
+ # Array slice
36
+ echo '[1,2,3,4,5]' | jq '.[2:4]'
37
+ ```
38
+
39
+ ## Iterating arrays
40
+
41
+ ```bash
42
+ # Iterate all elements
43
+ curl -s "https://api.example.com/users" | jq '.[]'
44
+
45
+ # Extract field from each element
46
+ curl -s "https://api.example.com/users" | jq '.[].name'
47
+
48
+ # Same with pipe
49
+ curl -s "https://api.example.com/users" | jq '.[] | .name'
50
+ ```
51
+
52
+ ## Selecting & filtering
53
+
54
+ ```bash
55
+ # Filter array by condition
56
+ jq '[.[] | select(.age > 25)]' data.json
57
+
58
+ # Filter by string match
59
+ jq '[.[] | select(.status == "active")]' data.json
60
+
61
+ # Filter by field existence
62
+ jq '[.[] | select(.email != null)]' data.json
63
+ ```
64
+
65
+ ## Transforming
66
+
67
+ ```bash
68
+ # Build a new object
69
+ jq '{id: .id, label: .name}' data.json
70
+
71
+ # Map over array
72
+ jq '[.[] | {id: .id, label: .name}]' data.json
73
+
74
+ # Append a field
75
+ jq '. + {processed: true}' data.json
76
+
77
+ # keys, values, length
78
+ jq 'keys' data.json
79
+ jq '.items | length' data.json
80
+ ```
81
+
82
+ ## String interpolation
83
+
84
+ ```bash
85
+ jq '.[] | "User \(.name) is \(.age) years old"' data.json
86
+ ```
87
+
88
+ ## Combining with curl
89
+
90
+ ```bash
91
+ # Extract specific field from API response
92
+ curl -s "https://api.example.com/item/1" | jq '.data.title'
93
+
94
+ # Filter and format a list
95
+ curl -s "https://api.example.com/items" | jq '[.[] | select(.active) | {id, name}]'
96
+ ```
97
+
98
+ ## Raw output & compact
99
+
100
+ ```bash
101
+ # Raw string output (no quotes)
102
+ jq -r '.name' data.json
103
+
104
+ # Compact output (no whitespace)
105
+ jq -c '.' data.json
106
+
107
+ # Read from file
108
+ jq '.items' data.json
109
+ ```
@@ -0,0 +1,38 @@
1
+ # Installing jq
2
+
3
+ ## macOS
4
+
5
+ ```bash
6
+ brew install jq
7
+ ```
8
+
9
+ If Homebrew is not installed, download the binary from https://jqlang.github.io/jq/download/ and place it in `/usr/local/bin`.
10
+
11
+ ## Windows
12
+
13
+ If winget is available:
14
+
15
+ ```powershell
16
+ winget install jqlang.jq
17
+ ```
18
+
19
+ Download the binary directly from https://jqlang.github.io/jq/download/ — pick `jq-windows-amd64.exe`, rename to `jq.exe`, and add its location to PATH.
20
+
21
+ If Chocolatey is available:
22
+
23
+ ```powershell
24
+ choco install jq
25
+ ```
26
+
27
+ ## Linux
28
+
29
+ ```bash
30
+ # Debian/Ubuntu
31
+ sudo apt install jq
32
+
33
+ # Fedora
34
+ sudo dnf install jq
35
+
36
+ # Arch
37
+ sudo pacman -S jq
38
+ ```
@@ -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
+ ```