@devxiyang/agent-skill 0.0.8 → 0.1.1

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.
@@ -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,166 +0,0 @@
1
- ---
2
- name: git
3
- description: Local git operations — branching, committing, merging, rebasing, history inspection, and conflict resolution.
4
- requires: bin:git
5
- tags: vcs,git
6
- ---
7
-
8
- # Git Skill
9
-
10
- ## Preflight
11
-
12
- Verify git is available before proceeding:
13
-
14
- ```bash
15
- git --version
16
- ```
17
-
18
- If missing, load `references/install.md` for installation instructions.
19
-
20
- ## Common workflow
21
-
22
- ```bash
23
- git status
24
- git diff
25
- git add <files>
26
- git commit -m "message"
27
- ```
28
-
29
- ## Branching
30
-
31
- ```bash
32
- git checkout -b feature/name
33
- git switch main
34
- git branch -d feature/name
35
- ```
36
-
37
- ## Inspecting history
38
-
39
- ```bash
40
- git log --oneline -20
41
- git log --oneline --graph --all
42
- git show <commit>
43
- git diff main...HEAD
44
- git blame <file>
45
- ```
46
-
47
- ## Cherry-pick
48
-
49
- ```bash
50
- # Apply a specific commit onto the current branch
51
- git cherry-pick <commit>
52
-
53
- # Cherry-pick a range
54
- git cherry-pick <from>..<to>
55
-
56
- # Cherry-pick without committing (stage only)
57
- git cherry-pick -n <commit>
58
- ```
59
-
60
- ## Tags
61
-
62
- ```bash
63
- # Create annotated tag
64
- git tag -a v1.0.0 -m "Release 1.0.0"
65
-
66
- # List tags
67
- git tag
68
-
69
- # Push tags to remote
70
- git push origin --tags
71
-
72
- # Delete a tag
73
- git tag -d v1.0.0
74
- git push origin :refs/tags/v1.0.0
75
- ```
76
-
77
- ## Undoing changes
78
-
79
- ```bash
80
- # Unstage
81
- git restore --staged <file>
82
-
83
- # Discard working tree changes
84
- git restore <file>
85
-
86
- # Undo last commit (keep changes staged)
87
- git reset --soft HEAD~1
88
-
89
- # Interactive rebase to edit recent commits
90
- git rebase -i HEAD~3
91
- ```
92
-
93
- ## Stashing
94
-
95
- ```bash
96
- git stash push -m "description"
97
- git stash list
98
- git stash pop
99
- ```
100
-
101
- ## Merging & rebasing
102
-
103
- ```bash
104
- # Merge
105
- git merge feature/name
106
-
107
- # Rebase onto main
108
- git rebase main
109
-
110
- # Abort on conflict
111
- git rebase --abort
112
- git merge --abort
113
- ```
114
-
115
- ## Conflict resolution
116
-
117
- 1. Open conflicting files, look for `<<<<<<<` markers
118
- 2. Edit to desired state
119
- 3. `git add <resolved-file>`
120
- 4. `git rebase --continue` or `git merge --continue`
121
-
122
- ## Worktrees
123
-
124
- Check out multiple branches simultaneously in separate directories — useful for working on or reviewing a branch without touching the current working tree:
125
-
126
- ```bash
127
- # Add a worktree for an existing branch
128
- git worktree add ../project-fix fix/some-bug
129
-
130
- # Add a worktree and create a new branch
131
- git worktree add -b feature/new ../project-feature main
132
-
133
- # List worktrees
134
- git worktree list
135
-
136
- # Remove a worktree when done
137
- git worktree remove ../project-fix
138
- ```
139
-
140
- Each worktree shares the same repository history but has its own working directory and HEAD.
141
-
142
- ## Debugging
143
-
144
- ```bash
145
- # Find which commit introduced a bug (binary search)
146
- git bisect start
147
- git bisect bad # current commit is broken
148
- git bisect good <commit> # last known good commit
149
- # git will check out commits for you to test; mark each:
150
- git bisect good
151
- git bisect bad
152
- git bisect reset # done
153
-
154
- # Show who last changed each line
155
- git blame <file>
156
- git blame -L 10,20 <file> # specific line range
157
- ```
158
-
159
- ## Remotes
160
-
161
- ```bash
162
- git remote -v
163
- git fetch origin
164
- git pull --rebase
165
- git push -u origin HEAD
166
- ```
@@ -1,112 +0,0 @@
1
- ---
2
- name: github
3
- description: Interact with GitHub using the `gh` CLI. Use for issues, pull requests, CI runs, code review, and GitHub API queries.
4
- requires: bin:gh
5
- tags: vcs,github
6
- ---
7
-
8
- # GitHub Skill
9
-
10
- ## Preflight
11
-
12
- Verify gh is available before proceeding:
13
-
14
- ```bash
15
- gh --version
16
- ```
17
-
18
- If missing, load `references/install.md` for installation and authentication instructions.
19
-
20
- Use the `gh` CLI to interact with GitHub. Always specify `--repo owner/repo` when not inside a git directory.
21
-
22
- ## Pull Requests
23
-
24
- ```bash
25
- # List open PRs
26
- gh pr list --repo owner/repo
27
-
28
- # View a PR
29
- gh pr view <number> --repo owner/repo
30
-
31
- # Check CI status
32
- gh pr checks <number> --repo owner/repo
33
-
34
- # View failed CI logs
35
- gh run view <run-id> --repo owner/repo --log-failed
36
-
37
- # Merge a PR
38
- gh pr merge <number> --repo owner/repo --squash
39
- gh pr merge <number> --repo owner/repo --merge
40
- gh pr merge <number> --repo owner/repo --rebase
41
- ```
42
-
43
- ## Code review
44
-
45
- ```bash
46
- # Approve
47
- gh pr review <number> --approve --repo owner/repo
48
-
49
- # Request changes
50
- gh pr review <number> --request-changes --body "Please fix X" --repo owner/repo
51
-
52
- # Leave a comment
53
- gh pr review <number> --comment --body "Looks good overall" --repo owner/repo
54
-
55
- # Add inline comment via API
56
- gh api repos/owner/repo/pulls/<number>/comments \
57
- --method POST \
58
- --field body="Comment text" \
59
- --field commit_id="<sha>" \
60
- --field path="src/file.ts" \
61
- --field line=42
62
- ```
63
-
64
- ## Issues
65
-
66
- ```bash
67
- # List issues
68
- gh issue list --repo owner/repo --state open
69
-
70
- # View an issue
71
- gh issue view <number> --repo owner/repo
72
-
73
- # Create issue
74
- gh issue create --title "Title" --body "Body" --repo owner/repo
75
-
76
- # Comment on an issue
77
- gh issue comment <number> --body "Comment" --repo owner/repo
78
-
79
- # Close / reopen
80
- gh issue close <number> --repo owner/repo
81
- gh issue reopen <number> --repo owner/repo
82
- ```
83
-
84
- ## Releases
85
-
86
- ```bash
87
- # List releases
88
- gh release list --repo owner/repo
89
-
90
- # Create a release
91
- gh release create v1.0.0 --repo owner/repo --title "v1.0.0" --notes "Release notes"
92
-
93
- # Upload assets to a release
94
- gh release upload v1.0.0 dist/app.zip --repo owner/repo
95
- ```
96
-
97
- ## API
98
-
99
- Use `gh api` for data not available via subcommands:
100
-
101
- ```bash
102
- gh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login'
103
- ```
104
-
105
- ## JSON Output
106
-
107
- Most commands support `--json` + `--jq`:
108
-
109
- ```bash
110
- gh issue list --repo owner/repo --json number,title --jq '.[] | "\(.number): \(.title)"'
111
- gh pr list --repo owner/repo --json number,title,author --jq '.[] | "\(.number): \(.title) by \(.author.login)"'
112
- ```
@@ -1,109 +0,0 @@
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
- ```
@@ -1,38 +0,0 @@
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
- ```
@@ -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.