skillscout-cli 0.1.0__tar.gz
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.
- skillscout_cli-0.1.0/LICENSE +21 -0
- skillscout_cli-0.1.0/PKG-INFO +183 -0
- skillscout_cli-0.1.0/README.md +155 -0
- skillscout_cli-0.1.0/pyproject.toml +44 -0
- skillscout_cli-0.1.0/setup.cfg +4 -0
- skillscout_cli-0.1.0/skillscout/__init__.py +1 -0
- skillscout_cli-0.1.0/skillscout/cli.py +97 -0
- skillscout_cli-0.1.0/skillscout/core.py +293 -0
- skillscout_cli-0.1.0/skillscout/github.py +168 -0
- skillscout_cli-0.1.0/skillscout/profile.example.toml +52 -0
- skillscout_cli-0.1.0/skillscout/scan.py +110 -0
- skillscout_cli-0.1.0/skillscout/schedule.py +75 -0
- skillscout_cli-0.1.0/skillscout/score.py +61 -0
- skillscout_cli-0.1.0/skillscout/skill.py +54 -0
- skillscout_cli-0.1.0/skillscout_cli.egg-info/PKG-INFO +183 -0
- skillscout_cli-0.1.0/skillscout_cli.egg-info/SOURCES.txt +19 -0
- skillscout_cli-0.1.0/skillscout_cli.egg-info/dependency_links.txt +1 -0
- skillscout_cli-0.1.0/skillscout_cli.egg-info/entry_points.txt +2 -0
- skillscout_cli-0.1.0/skillscout_cli.egg-info/requires.txt +3 -0
- skillscout_cli-0.1.0/skillscout_cli.egg-info/top_level.txt +1 -0
- skillscout_cli-0.1.0/tests/test_skillscout.py +330 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Akshit Gupta
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: skillscout-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Keep your agent's skills current: weekly discovery, upstream diffs, safety scans, pinned installs
|
|
5
|
+
Author: Akshit Gupta
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://akshit9162.github.io/skillscout
|
|
8
|
+
Project-URL: Source, https://github.com/akshit9162/skillscout
|
|
9
|
+
Project-URL: Issues, https://github.com/akshit9162/skillscout/issues
|
|
10
|
+
Project-URL: Report, https://akshit9162.github.io/skillscout/report.html
|
|
11
|
+
Keywords: claude,claude-code,agent-skills,security,supply-chain,llm
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Security
|
|
21
|
+
Classifier: Topic :: Software Development
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src="https://raw.githubusercontent.com/akshit9162/skillscout/main/docs/assets/header.svg" width="760" alt="skillscout: keep your agent's skills current, safely" />
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<a href="https://akshit9162.github.io/skillscout">Website</a>  · 
|
|
35
|
+
<a href="https://akshit9162.github.io/skillscout/report.html">Report: what's inside 6,726 skills</a>  · 
|
|
36
|
+
<a href="#install">Install</a>  · 
|
|
37
|
+
<a href="#a-week-with-skillscout">A week with it</a>  · 
|
|
38
|
+
<a href="#how-it-works">How it works</a>  · 
|
|
39
|
+
<a href="#what-gets-flagged">What gets flagged</a>
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT licence" /></a>
|
|
44
|
+
<a href="https://github.com/akshit9162/skillscout/actions/workflows/ci.yml"><img src="https://github.com/akshit9162/skillscout/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
45
|
+
<img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+" />
|
|
46
|
+
<img src="https://img.shields.io/badge/dependencies-0-brightgreen.svg" alt="No dependencies" />
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<img src="https://raw.githubusercontent.com/akshit9162/skillscout/main/docs/assets/demo.svg" width="760" alt="Terminal demo: discover finds 216 recommended skills with 89 flagged high-risk; a high-risk skill is refused without --accept-risk; a low-risk one installs pinned to a commit" />
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
claude plugin marketplace add akshit9162/skillscout && claude plugin install skillscout@skillscout
|
|
55
|
+
# or the CLI on its own
|
|
56
|
+
pipx install git+https://github.com/akshit9162/skillscout
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
AI tooling moves weekly. New skills, plugins and agent workflows land on GitHub every day, the ones you
|
|
60
|
+
installed keep changing upstream, and last month's best practice is already stale. Nobody has time to
|
|
61
|
+
watch hundreds of repos.
|
|
62
|
+
|
|
63
|
+
**skillscout keeps your agent current.** Once a week it checks the skill ecosystem against what *you*
|
|
64
|
+
work on, tells you what's new and worth having, and shows you exactly what changed in the skills you
|
|
65
|
+
already use.
|
|
66
|
+
|
|
67
|
+
Keeping up fast is only useful if it's safe. A skill is a set of instructions your agent follows
|
|
68
|
+
**with your permissions**, so skillscout scans every file for dangerous patterns, installs nothing without
|
|
69
|
+
your approval, pins each install to an exact commit, and re-scans every update before it can replace anything.
|
|
70
|
+
|
|
71
|
+
In one run over the public ecosystem it found **6,726 skills in 95 repos**. 198 had at least one high-risk flag,
|
|
72
|
+
mostly `sudo` and `curl | sh` install steps, plus prompt-injection-style text and hidden Unicode.
|
|
73
|
+
The [report](https://akshit9162.github.io/skillscout/report.html) reads every one of the scariest flags: they were defenders.
|
|
74
|
+
|
|
75
|
+
It's a zero-dependency Python CLI, and it also ships as a skill, so your agent can run the whole loop for you
|
|
76
|
+
and still ask before every install.
|
|
77
|
+
|
|
78
|
+
## A week with skillscout
|
|
79
|
+
|
|
80
|
+
Example session (counts vary week to week):
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
Monday 10:00 skillscout discover --notify (weekly, via schedule)
|
|
84
|
+
→ "4 new, 23 recommended, 11 flagged high-risk"
|
|
85
|
+
You skillscout show github/awesome-copilot:eval-driven-dev
|
|
86
|
+
→ flags with file:line, the full SKILL.md, overlaps with what you have
|
|
87
|
+
You skillscout approve eval-driven-dev → copied, pinned to its commit
|
|
88
|
+
Later skillscout updates → upstream changed it; here's the diff
|
|
89
|
+
You skillscout approve eval-driven-dev --update → re-scanned, then replaced
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Install
|
|
93
|
+
|
|
94
|
+
### As a Claude Code plugin (the agent drives it)
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
claude plugin marketplace add akshit9162/skillscout
|
|
98
|
+
claude plugin install skillscout@skillscout
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Then ask: *"find me skills for my Python and React work"*. The agent installs the CLI, runs the search,
|
|
102
|
+
summarises the report and shows you each skill's flags before asking to install it.
|
|
103
|
+
|
|
104
|
+
### As a CLI
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
pipx install git+https://github.com/akshit9162/skillscout # or: uv tool install git+https://...
|
|
108
|
+
skillscout init # writes ~/.skillscout/profile.toml
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Edit `[keywords]` in `~/.skillscout/profile.toml` with the words from your own work, then:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
skillscout discover # search, scan, score; writes ~/.skillscout/reports/<date>.md
|
|
115
|
+
skillscout list # ranked; REC = recommended
|
|
116
|
+
skillscout show <id> # every flag plus the full SKILL.md, before you decide
|
|
117
|
+
skillscout approve <id> # copy into ~/.claude/skills and pin the commit
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`<id>` is `owner/repo:skill-name`. The bare skill name works when it's unique.
|
|
121
|
+
|
|
122
|
+
## How it works
|
|
123
|
+
|
|
124
|
+
| Step | What happens |
|
|
125
|
+
|---|---|
|
|
126
|
+
| **Discover** | Runs your GitHub searches (topics like `claude-skills`, `agent-skills`) plus your trusted repos, then follows every repo linked from curated lists (your `lists`, plus any `awesome-*` repo it finds). Drops archived, stale (no push in a year), unpopular and huge repos. |
|
|
127
|
+
| **Clone** | Sparse, shallow clones: only folders that contain a `SKILL.md`, minus media and archives, 16 repos at a time. Repos with no push since the last run aren't fetched again. Repo code is never run. |
|
|
128
|
+
| **Scan** | Static checks on every file in the skill, reported with file and line ([below](#what-gets-flagged)). A skill whose folder (git tree hash) and the scanner's rules are both unchanged keeps its last result instead of being read again. |
|
|
129
|
+
| **Score** | 65% relevance to your keywords, 20% stars, 15% freshness, plus a bonus for trusted repos. |
|
|
130
|
+
| **Conflicts** | Compared against your `~/.claude/skills` and plugin skills: same name, or a description close enough that both would fire on the same prompt. |
|
|
131
|
+
| **Report** | What's new since your last run, recommended skills, and relevant ones that are blocked and why. |
|
|
132
|
+
| **Approve** | You pick one. It's copied (symlinks dropped) and pinned in `~/.skillscout/skills.lock.json` with its commit and a content hash. |
|
|
133
|
+
| **Update** | `skillscout updates` diffs each pinned skill against upstream and applies nothing. Taking an update re-runs the scan. |
|
|
134
|
+
|
|
135
|
+
## What gets flagged
|
|
136
|
+
|
|
137
|
+
| Level | Patterns |
|
|
138
|
+
|---|---|
|
|
139
|
+
| **High**, blocks install | Download piped into a shell · `sudo` · broad `rm -rf` · reading SSH keys or the keychain · decode-and-execute · uploading local files · hidden or direction-changing Unicode · "ignore previous instructions" · "don't tell the user" |
|
|
140
|
+
| **Medium** | Reading `.env` or API keys · force pushes · editing shell profiles or Claude settings · skipping permission prompts · hooks · scripts that make network calls · symlinks · binaries |
|
|
141
|
+
| **Low** | Other bundled scripts · no licence |
|
|
142
|
+
|
|
143
|
+
High-risk skills need `--accept-risk`, which the bundled skill tells the agent never to pass on its own.
|
|
144
|
+
|
|
145
|
+
## Safety rails
|
|
146
|
+
|
|
147
|
+
- Nothing installs without an explicit `approve`.
|
|
148
|
+
- `approve` never overwrites a folder skillscout didn't install, and never follows symlinks.
|
|
149
|
+
- Every install is pinned to a commit and a content hash.
|
|
150
|
+
- Updates are a diff first, and re-scanned before they replace anything. The test suite includes an upstream
|
|
151
|
+
update that slips in `sudo rm -rf /`, and checks that re-approval refuses it.
|
|
152
|
+
|
|
153
|
+
## Every command
|
|
154
|
+
|
|
155
|
+
| Command | Does |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `skillscout init` | Copy the example profile to `~/.skillscout/profile.toml` |
|
|
158
|
+
| `skillscout discover [--full] [--notify]` | Search, scan, score, write the report. `--full` ignores the caches; `--notify` sends a desktop notification (macOS, or Linux with `notify-send`) |
|
|
159
|
+
| `skillscout list [--all] [-n N]` | Ranked candidates |
|
|
160
|
+
| `skillscout show <id>` | Flags, conflicts and the full SKILL.md |
|
|
161
|
+
| `skillscout approve <id> [--update] [--accept-risk]` | Install and pin |
|
|
162
|
+
| `skillscout installed` · `skillscout remove <id>` | Pinned skills · uninstall |
|
|
163
|
+
| `skillscout updates` | Diff pinned skills against upstream |
|
|
164
|
+
| `skillscout schedule` | Set up weekly discovery: launchd on macOS, a systemd timer on Linux (cron line without systemd), a `schtasks` command on Windows. It prints the one command that turns it on |
|
|
165
|
+
|
|
166
|
+
Set `GITHUB_TOKEN`, or just be logged in with the GitHub CLI (`gh auth login`), for higher API rate limits (5,000 requests an hour instead of 60). Following lists costs one lookup per linked repo: up to 300 a run with a token, 40 without. A token is optional: without it, a rate limit means waiting rather than failing. A weekly run with few upstream changes takes about 25 seconds; the first run takes a few minutes. Scheduled jobs don't see your shell's environment, so add the token to the job if you want it there.
|
|
167
|
+
|
|
168
|
+
## Limits
|
|
169
|
+
|
|
170
|
+
- The scanner catches known patterns. It can't prove a skill is safe, so read `show` before you approve.
|
|
171
|
+
- Relevance is keyword matching against your profile, not semantic search. Tune the weights.
|
|
172
|
+
- Every installed skill's description loads into every agent session. Keep the total to a few dozen.
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
178
|
+
.venv/bin/python -m pytest -q
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/akshit9162/skillscout/main/docs/assets/header.svg" width="760" alt="skillscout: keep your agent's skills current, safely" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://akshit9162.github.io/skillscout">Website</a>  · 
|
|
7
|
+
<a href="https://akshit9162.github.io/skillscout/report.html">Report: what's inside 6,726 skills</a>  · 
|
|
8
|
+
<a href="#install">Install</a>  · 
|
|
9
|
+
<a href="#a-week-with-skillscout">A week with it</a>  · 
|
|
10
|
+
<a href="#how-it-works">How it works</a>  · 
|
|
11
|
+
<a href="#what-gets-flagged">What gets flagged</a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT licence" /></a>
|
|
16
|
+
<a href="https://github.com/akshit9162/skillscout/actions/workflows/ci.yml"><img src="https://github.com/akshit9162/skillscout/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
17
|
+
<img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+" />
|
|
18
|
+
<img src="https://img.shields.io/badge/dependencies-0-brightgreen.svg" alt="No dependencies" />
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
<img src="https://raw.githubusercontent.com/akshit9162/skillscout/main/docs/assets/demo.svg" width="760" alt="Terminal demo: discover finds 216 recommended skills with 89 flagged high-risk; a high-risk skill is refused without --accept-risk; a low-risk one installs pinned to a commit" />
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
claude plugin marketplace add akshit9162/skillscout && claude plugin install skillscout@skillscout
|
|
27
|
+
# or the CLI on its own
|
|
28
|
+
pipx install git+https://github.com/akshit9162/skillscout
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
AI tooling moves weekly. New skills, plugins and agent workflows land on GitHub every day, the ones you
|
|
32
|
+
installed keep changing upstream, and last month's best practice is already stale. Nobody has time to
|
|
33
|
+
watch hundreds of repos.
|
|
34
|
+
|
|
35
|
+
**skillscout keeps your agent current.** Once a week it checks the skill ecosystem against what *you*
|
|
36
|
+
work on, tells you what's new and worth having, and shows you exactly what changed in the skills you
|
|
37
|
+
already use.
|
|
38
|
+
|
|
39
|
+
Keeping up fast is only useful if it's safe. A skill is a set of instructions your agent follows
|
|
40
|
+
**with your permissions**, so skillscout scans every file for dangerous patterns, installs nothing without
|
|
41
|
+
your approval, pins each install to an exact commit, and re-scans every update before it can replace anything.
|
|
42
|
+
|
|
43
|
+
In one run over the public ecosystem it found **6,726 skills in 95 repos**. 198 had at least one high-risk flag,
|
|
44
|
+
mostly `sudo` and `curl | sh` install steps, plus prompt-injection-style text and hidden Unicode.
|
|
45
|
+
The [report](https://akshit9162.github.io/skillscout/report.html) reads every one of the scariest flags: they were defenders.
|
|
46
|
+
|
|
47
|
+
It's a zero-dependency Python CLI, and it also ships as a skill, so your agent can run the whole loop for you
|
|
48
|
+
and still ask before every install.
|
|
49
|
+
|
|
50
|
+
## A week with skillscout
|
|
51
|
+
|
|
52
|
+
Example session (counts vary week to week):
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
Monday 10:00 skillscout discover --notify (weekly, via schedule)
|
|
56
|
+
→ "4 new, 23 recommended, 11 flagged high-risk"
|
|
57
|
+
You skillscout show github/awesome-copilot:eval-driven-dev
|
|
58
|
+
→ flags with file:line, the full SKILL.md, overlaps with what you have
|
|
59
|
+
You skillscout approve eval-driven-dev → copied, pinned to its commit
|
|
60
|
+
Later skillscout updates → upstream changed it; here's the diff
|
|
61
|
+
You skillscout approve eval-driven-dev --update → re-scanned, then replaced
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
### As a Claude Code plugin (the agent drives it)
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
claude plugin marketplace add akshit9162/skillscout
|
|
70
|
+
claude plugin install skillscout@skillscout
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then ask: *"find me skills for my Python and React work"*. The agent installs the CLI, runs the search,
|
|
74
|
+
summarises the report and shows you each skill's flags before asking to install it.
|
|
75
|
+
|
|
76
|
+
### As a CLI
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
pipx install git+https://github.com/akshit9162/skillscout # or: uv tool install git+https://...
|
|
80
|
+
skillscout init # writes ~/.skillscout/profile.toml
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Edit `[keywords]` in `~/.skillscout/profile.toml` with the words from your own work, then:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
skillscout discover # search, scan, score; writes ~/.skillscout/reports/<date>.md
|
|
87
|
+
skillscout list # ranked; REC = recommended
|
|
88
|
+
skillscout show <id> # every flag plus the full SKILL.md, before you decide
|
|
89
|
+
skillscout approve <id> # copy into ~/.claude/skills and pin the commit
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`<id>` is `owner/repo:skill-name`. The bare skill name works when it's unique.
|
|
93
|
+
|
|
94
|
+
## How it works
|
|
95
|
+
|
|
96
|
+
| Step | What happens |
|
|
97
|
+
|---|---|
|
|
98
|
+
| **Discover** | Runs your GitHub searches (topics like `claude-skills`, `agent-skills`) plus your trusted repos, then follows every repo linked from curated lists (your `lists`, plus any `awesome-*` repo it finds). Drops archived, stale (no push in a year), unpopular and huge repos. |
|
|
99
|
+
| **Clone** | Sparse, shallow clones: only folders that contain a `SKILL.md`, minus media and archives, 16 repos at a time. Repos with no push since the last run aren't fetched again. Repo code is never run. |
|
|
100
|
+
| **Scan** | Static checks on every file in the skill, reported with file and line ([below](#what-gets-flagged)). A skill whose folder (git tree hash) and the scanner's rules are both unchanged keeps its last result instead of being read again. |
|
|
101
|
+
| **Score** | 65% relevance to your keywords, 20% stars, 15% freshness, plus a bonus for trusted repos. |
|
|
102
|
+
| **Conflicts** | Compared against your `~/.claude/skills` and plugin skills: same name, or a description close enough that both would fire on the same prompt. |
|
|
103
|
+
| **Report** | What's new since your last run, recommended skills, and relevant ones that are blocked and why. |
|
|
104
|
+
| **Approve** | You pick one. It's copied (symlinks dropped) and pinned in `~/.skillscout/skills.lock.json` with its commit and a content hash. |
|
|
105
|
+
| **Update** | `skillscout updates` diffs each pinned skill against upstream and applies nothing. Taking an update re-runs the scan. |
|
|
106
|
+
|
|
107
|
+
## What gets flagged
|
|
108
|
+
|
|
109
|
+
| Level | Patterns |
|
|
110
|
+
|---|---|
|
|
111
|
+
| **High**, blocks install | Download piped into a shell · `sudo` · broad `rm -rf` · reading SSH keys or the keychain · decode-and-execute · uploading local files · hidden or direction-changing Unicode · "ignore previous instructions" · "don't tell the user" |
|
|
112
|
+
| **Medium** | Reading `.env` or API keys · force pushes · editing shell profiles or Claude settings · skipping permission prompts · hooks · scripts that make network calls · symlinks · binaries |
|
|
113
|
+
| **Low** | Other bundled scripts · no licence |
|
|
114
|
+
|
|
115
|
+
High-risk skills need `--accept-risk`, which the bundled skill tells the agent never to pass on its own.
|
|
116
|
+
|
|
117
|
+
## Safety rails
|
|
118
|
+
|
|
119
|
+
- Nothing installs without an explicit `approve`.
|
|
120
|
+
- `approve` never overwrites a folder skillscout didn't install, and never follows symlinks.
|
|
121
|
+
- Every install is pinned to a commit and a content hash.
|
|
122
|
+
- Updates are a diff first, and re-scanned before they replace anything. The test suite includes an upstream
|
|
123
|
+
update that slips in `sudo rm -rf /`, and checks that re-approval refuses it.
|
|
124
|
+
|
|
125
|
+
## Every command
|
|
126
|
+
|
|
127
|
+
| Command | Does |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `skillscout init` | Copy the example profile to `~/.skillscout/profile.toml` |
|
|
130
|
+
| `skillscout discover [--full] [--notify]` | Search, scan, score, write the report. `--full` ignores the caches; `--notify` sends a desktop notification (macOS, or Linux with `notify-send`) |
|
|
131
|
+
| `skillscout list [--all] [-n N]` | Ranked candidates |
|
|
132
|
+
| `skillscout show <id>` | Flags, conflicts and the full SKILL.md |
|
|
133
|
+
| `skillscout approve <id> [--update] [--accept-risk]` | Install and pin |
|
|
134
|
+
| `skillscout installed` · `skillscout remove <id>` | Pinned skills · uninstall |
|
|
135
|
+
| `skillscout updates` | Diff pinned skills against upstream |
|
|
136
|
+
| `skillscout schedule` | Set up weekly discovery: launchd on macOS, a systemd timer on Linux (cron line without systemd), a `schtasks` command on Windows. It prints the one command that turns it on |
|
|
137
|
+
|
|
138
|
+
Set `GITHUB_TOKEN`, or just be logged in with the GitHub CLI (`gh auth login`), for higher API rate limits (5,000 requests an hour instead of 60). Following lists costs one lookup per linked repo: up to 300 a run with a token, 40 without. A token is optional: without it, a rate limit means waiting rather than failing. A weekly run with few upstream changes takes about 25 seconds; the first run takes a few minutes. Scheduled jobs don't see your shell's environment, so add the token to the job if you want it there.
|
|
139
|
+
|
|
140
|
+
## Limits
|
|
141
|
+
|
|
142
|
+
- The scanner catches known patterns. It can't prove a skill is safe, so read `show` before you approve.
|
|
143
|
+
- Relevance is keyword matching against your profile, not semantic search. Tune the weights.
|
|
144
|
+
- Every installed skill's description loads into every agent session. Keep the total to a few dozen.
|
|
145
|
+
|
|
146
|
+
## Development
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
150
|
+
.venv/bin/python -m pytest -q
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "skillscout-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Keep your agent's skills current: weekly discovery, upstream diffs, safety scans, pinned installs"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Akshit Gupta" }]
|
|
14
|
+
keywords = ["claude", "claude-code", "agent-skills", "security", "supply-chain", "llm"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Security",
|
|
25
|
+
"Topic :: Software Development",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://akshit9162.github.io/skillscout"
|
|
30
|
+
Source = "https://github.com/akshit9162/skillscout"
|
|
31
|
+
Issues = "https://github.com/akshit9162/skillscout/issues"
|
|
32
|
+
Report = "https://akshit9162.github.io/skillscout/report.html"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = ["pytest"]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
skillscout = "skillscout.cli:main"
|
|
39
|
+
|
|
40
|
+
[tool.setuptools]
|
|
41
|
+
packages = ["skillscout"]
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.package-data]
|
|
44
|
+
skillscout = ["profile.example.toml"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Keep an agent's skills current: find what's new, diff what changed, scan everything, install only what you approve."""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""skillscout command line."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from . import __doc__ as ABOUT, core, schedule
|
|
11
|
+
|
|
12
|
+
EXAMPLE = Path(__file__).resolve().parent / "profile.example.toml"
|
|
13
|
+
USER_PROFILE = Path.home() / ".skillscout" / "profile.toml"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def notify(text: str) -> None:
|
|
17
|
+
if sys.platform == "darwin" and shutil.which("osascript"):
|
|
18
|
+
subprocess.run(["osascript", "-e", f'display notification "{text}" with title "skillscout"'], check=False)
|
|
19
|
+
elif shutil.which("notify-send"):
|
|
20
|
+
subprocess.run(["notify-send", "skillscout", text], check=False)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main(argv: list[str] | None = None) -> None:
|
|
24
|
+
p = argparse.ArgumentParser(prog="skillscout", description=ABOUT)
|
|
25
|
+
p.add_argument("--profile", type=Path, default=None, help="default: ~/.skillscout/profile.toml, else the example")
|
|
26
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
27
|
+
sub.add_parser("init", help="copy the example profile to ~/.skillscout/profile.toml")
|
|
28
|
+
d = sub.add_parser("discover", help="search GitHub, scan and score skills, write a report")
|
|
29
|
+
d.add_argument("--full", action="store_true", help="ignore caches: fetch every repo and re-scan every skill")
|
|
30
|
+
d.add_argument("--notify", action="store_true", help="desktop notification with the summary (macOS, or Linux with notify-send)")
|
|
31
|
+
ls = sub.add_parser("list", help="show scored candidates")
|
|
32
|
+
ls.add_argument("--all", action="store_true", help="include zero-relevance skills")
|
|
33
|
+
ls.add_argument("-n", type=int, default=30)
|
|
34
|
+
sh = sub.add_parser("show", help="print a candidate's flags and full SKILL.md")
|
|
35
|
+
sh.add_argument("id")
|
|
36
|
+
ap = sub.add_parser("approve", help="install one skill into ~/.claude/skills and pin it")
|
|
37
|
+
ap.add_argument("id")
|
|
38
|
+
ap.add_argument("--accept-risk", action="store_true", help="install despite high-risk flags")
|
|
39
|
+
ap.add_argument("--update", action="store_true", help="replace a copy skillscout installed earlier")
|
|
40
|
+
rm = sub.add_parser("remove", help="uninstall a skill skillscout installed")
|
|
41
|
+
rm.add_argument("id")
|
|
42
|
+
sub.add_parser("installed", help="show pinned skills")
|
|
43
|
+
sub.add_parser("updates", help="diff pinned skills against upstream; applies nothing")
|
|
44
|
+
sub.add_parser("schedule", help="set up weekly discovery for this OS (you turn it on)")
|
|
45
|
+
a = p.parse_args(argv)
|
|
46
|
+
|
|
47
|
+
paths = core.Paths()
|
|
48
|
+
if a.cmd == "init":
|
|
49
|
+
if USER_PROFILE.exists():
|
|
50
|
+
raise SystemExit(f"{USER_PROFILE} already exists")
|
|
51
|
+
USER_PROFILE.parent.mkdir(parents=True, exist_ok=True)
|
|
52
|
+
USER_PROFILE.write_text(EXAMPLE.read_text())
|
|
53
|
+
print(f"wrote {USER_PROFILE}; edit [keywords] to match your work, then run `skillscout discover`")
|
|
54
|
+
return
|
|
55
|
+
profile = core.load_profile(a.profile or (USER_PROFILE if USER_PROFILE.exists() else EXAMPLE))
|
|
56
|
+
cands = core._read(paths.candidates, [])
|
|
57
|
+
|
|
58
|
+
if a.cmd == "discover":
|
|
59
|
+
cands = core.discover(profile, paths, full=a.full)
|
|
60
|
+
out, summary = core.report(cands, profile, paths)
|
|
61
|
+
print(f"{summary}\nreport: {out}")
|
|
62
|
+
if a.notify:
|
|
63
|
+
notify(summary)
|
|
64
|
+
elif a.cmd == "list":
|
|
65
|
+
rows = [c for c in cands if a.all or c["relevance"] > 0][: a.n]
|
|
66
|
+
for c in rows:
|
|
67
|
+
tag = "installed" if c["installed"] else ("REC" if core.recommended(c, profile) else "")
|
|
68
|
+
print(f"{c['score']:.2f} {c['risk'] or '-':6} {tag:9} {c['id']}")
|
|
69
|
+
elif a.cmd == "show":
|
|
70
|
+
c = core.find(cands, a.id)
|
|
71
|
+
print(json.dumps({k: c[k] for k in ("id", "score", "stars", "license", "commit", "risk", "matched", "conflicts")}, indent=2))
|
|
72
|
+
for f in c["flags"]:
|
|
73
|
+
print(f" [{f['level']}] {f['reason']} — {f['where']}")
|
|
74
|
+
src = core.source_dir(c, paths)
|
|
75
|
+
print(f"\nfiles: {', '.join(sorted(str(x.relative_to(src)) for x in src.rglob('*') if x.is_file()))}\n")
|
|
76
|
+
print((src / "SKILL.md").read_text(errors="ignore"))
|
|
77
|
+
elif a.cmd == "approve":
|
|
78
|
+
c = core.find(cands, a.id)
|
|
79
|
+
print(f"installed {c['id']} -> {core.approve(c, paths, a.accept_risk, a.update)}")
|
|
80
|
+
elif a.cmd == "remove":
|
|
81
|
+
print(f"removed {core.remove(a.id, paths)}")
|
|
82
|
+
elif a.cmd == "installed":
|
|
83
|
+
for cid, e in core._read(paths.lock, {}).items():
|
|
84
|
+
print(f"{cid} @{e['commit'][:10]} {e['installed_at']}")
|
|
85
|
+
elif a.cmd == "updates":
|
|
86
|
+
found = core.updates(paths)
|
|
87
|
+
for cid, commit, diff in found:
|
|
88
|
+
print(f"\n=== {cid} -> {commit[:10]}\n{diff}")
|
|
89
|
+
print(f"\n{len(found)} pinned skill(s) changed upstream." + (" Re-run `approve <id> --update` after reading the diff." if found else ""))
|
|
90
|
+
elif a.cmd == "schedule":
|
|
91
|
+
files, message = schedule.here(paths.state)
|
|
92
|
+
schedule.write(files)
|
|
93
|
+
print(message)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
main()
|