skillseal 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.
- skillseal-0.1.0/.github/dependabot.yml +11 -0
- skillseal-0.1.0/.github/workflows/ci.yml +56 -0
- skillseal-0.1.0/.github/workflows/release.yml +56 -0
- skillseal-0.1.0/.gitignore +11 -0
- skillseal-0.1.0/.python-version +1 -0
- skillseal-0.1.0/LICENSE +21 -0
- skillseal-0.1.0/PKG-INFO +302 -0
- skillseal-0.1.0/README.md +289 -0
- skillseal-0.1.0/SECURITY.md +13 -0
- skillseal-0.1.0/examples/bad-skill/SKILL.md +57 -0
- skillseal-0.1.0/examples/bad-skill/skillseal.yaml +18 -0
- skillseal-0.1.0/examples/good-skill/SKILL.md +26 -0
- skillseal-0.1.0/examples/good-skill/skillseal.yaml +16 -0
- skillseal-0.1.0/pyproject.toml +45 -0
- skillseal-0.1.0/src/skillseal/__init__.py +3 -0
- skillseal-0.1.0/src/skillseal/cli.py +131 -0
- skillseal-0.1.0/src/skillseal/linter.py +21 -0
- skillseal-0.1.0/src/skillseal/models.py +135 -0
- skillseal-0.1.0/src/skillseal/parser.py +85 -0
- skillseal-0.1.0/src/skillseal/reporters/__init__.py +1 -0
- skillseal-0.1.0/src/skillseal/reporters/json_reporter.py +79 -0
- skillseal-0.1.0/src/skillseal/reporters/terminal.py +89 -0
- skillseal-0.1.0/src/skillseal/routing/__init__.py +1 -0
- skillseal-0.1.0/src/skillseal/routing/evaluator.py +268 -0
- skillseal-0.1.0/src/skillseal/routing/runner.py +92 -0
- skillseal-0.1.0/src/skillseal/rules/__init__.py +1 -0
- skillseal-0.1.0/src/skillseal/rules/base.py +115 -0
- skillseal-0.1.0/src/skillseal/rules/metadata.py +172 -0
- skillseal-0.1.0/src/skillseal/rules/portability.py +121 -0
- skillseal-0.1.0/src/skillseal/rules/quality.py +193 -0
- skillseal-0.1.0/src/skillseal/rules/security.py +177 -0
- skillseal-0.1.0/src/skillseal/scoring.py +67 -0
- skillseal-0.1.0/tests/conftest.py +35 -0
- skillseal-0.1.0/tests/test_cli.py +70 -0
- skillseal-0.1.0/tests/test_json_reporter.py +58 -0
- skillseal-0.1.0/tests/test_parser.py +87 -0
- skillseal-0.1.0/tests/test_routing_config.py +39 -0
- skillseal-0.1.0/tests/test_routing_heuristic.py +52 -0
- skillseal-0.1.0/tests/test_rules_ids.py +8 -0
- skillseal-0.1.0/tests/test_rules_metadata.py +76 -0
- skillseal-0.1.0/tests/test_rules_portability.py +40 -0
- skillseal-0.1.0/tests/test_rules_quality.py +64 -0
- skillseal-0.1.0/tests/test_rules_security.py +67 -0
- skillseal-0.1.0/tests/test_scoring.py +51 -0
- skillseal-0.1.0/uv.lock +607 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
16
|
+
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3.2.4
|
|
19
|
+
with:
|
|
20
|
+
version: "latest"
|
|
21
|
+
|
|
22
|
+
- name: Install dependencies
|
|
23
|
+
run: uv sync
|
|
24
|
+
|
|
25
|
+
- name: Lint (ruff)
|
|
26
|
+
run: uv run ruff check .
|
|
27
|
+
|
|
28
|
+
- name: Type check (mypy)
|
|
29
|
+
run: uv run mypy src
|
|
30
|
+
|
|
31
|
+
- name: Unit tests
|
|
32
|
+
run: uv run pytest -q
|
|
33
|
+
|
|
34
|
+
# Smoke-test the tool against its own examples. good-skill is expected to
|
|
35
|
+
# pass both gates; bad-skill is deliberately broken and expected to fail
|
|
36
|
+
# both, so the pipeline asserts that failure rather than requiring it to
|
|
37
|
+
# (accidentally) pass.
|
|
38
|
+
- name: Check good-skill example (expected to pass)
|
|
39
|
+
run: uv run skillseal check examples/good-skill --fail-on error
|
|
40
|
+
|
|
41
|
+
- name: Check bad-skill example (expected to fail the gate)
|
|
42
|
+
run: |
|
|
43
|
+
if uv run skillseal check examples/bad-skill --fail-on error; then
|
|
44
|
+
echo "expected examples/bad-skill to fail the --fail-on error gate" >&2
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
- name: Routing tests for good-skill example (expected to pass)
|
|
49
|
+
run: uv run skillseal test examples/good-skill
|
|
50
|
+
|
|
51
|
+
- name: Routing tests for bad-skill example (expected to fail the threshold)
|
|
52
|
+
run: |
|
|
53
|
+
if uv run skillseal test examples/bad-skill; then
|
|
54
|
+
echo "expected examples/bad-skill routing tests to fail the threshold" >&2
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
16
|
+
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3.2.4
|
|
19
|
+
with:
|
|
20
|
+
version: "latest"
|
|
21
|
+
|
|
22
|
+
- name: Verify pyproject version matches the tag
|
|
23
|
+
run: |
|
|
24
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
25
|
+
version=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
26
|
+
if [ "$tag" != "$version" ]; then
|
|
27
|
+
echo "Tag v$tag does not match pyproject.toml version $version" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
- name: Build sdist and wheel
|
|
32
|
+
run: uv build
|
|
33
|
+
|
|
34
|
+
- name: Upload dist
|
|
35
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
36
|
+
with:
|
|
37
|
+
name: dist
|
|
38
|
+
path: dist/
|
|
39
|
+
|
|
40
|
+
publish:
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
environment:
|
|
44
|
+
name: pypi
|
|
45
|
+
url: https://pypi.org/project/skillseal/
|
|
46
|
+
permissions:
|
|
47
|
+
id-token: write # required for PyPI trusted publishing (OIDC), no token/secret needed
|
|
48
|
+
steps:
|
|
49
|
+
- name: Download dist
|
|
50
|
+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
|
51
|
+
with:
|
|
52
|
+
name: dist
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
- name: Publish to PyPI
|
|
56
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
skillseal-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pablo Espinel
|
|
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.
|
skillseal-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: skillseal
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lint, score, and routing-test Agent Skills (SKILL.md) before your agents do.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: pydantic>=2.6
|
|
9
|
+
Requires-Dist: pyyaml>=6.0
|
|
10
|
+
Requires-Dist: rich>=13.7
|
|
11
|
+
Requires-Dist: typer>=0.12
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# SkillSeal
|
|
15
|
+
|
|
16
|
+
**Test your Agent Skills before your agents do.**
|
|
17
|
+
|
|
18
|
+
`SKILL.md` files can be syntactically valid and still be bad: a vague
|
|
19
|
+
description that never routes correctly, an oversized file that eats context,
|
|
20
|
+
a `curl | sh` buried in a code block, a hardcoded `/Users/you/...` path that
|
|
21
|
+
only works on your machine. None of that shows up until an agent picks the
|
|
22
|
+
wrong skill, or picks the right one and runs something it shouldn't.
|
|
23
|
+
|
|
24
|
+
SkillSeal is a local-first, offline-first CLI that lints, scores, and
|
|
25
|
+
routing-tests `SKILL.md` files, so you catch that before an agent does. It's
|
|
26
|
+
deliberately scoped to what's useful today: static linting across four
|
|
27
|
+
categories, deterministic (LLM-optional) routing tests, and CI-friendly exit
|
|
28
|
+
codes and JSON output. No dashboard, no registry, no cloud — see
|
|
29
|
+
[Roadmap](#roadmap) for what's intentionally not here yet.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
Requires Python 3.12+ and [uv](https://docs.astral.sh/uv/).
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone <this-repo>
|
|
37
|
+
cd skillseal
|
|
38
|
+
uv sync
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Run it directly with `uv run skillseal ...`, or install it as a tool:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
uv tool install .
|
|
45
|
+
skillseal --help
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quickstart
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv run skillseal check examples
|
|
52
|
+
uv run skillseal test examples
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Both commands accept a path to a single `SKILL.md` file, a single skill
|
|
56
|
+
directory, or a directory containing many skills (searched recursively).
|
|
57
|
+
|
|
58
|
+
## Commands
|
|
59
|
+
|
|
60
|
+
### `skillseal check <path>`
|
|
61
|
+
|
|
62
|
+
Runs every rule (SPECIFICATION, QUALITY, SECURITY, PORTABILITY) against each
|
|
63
|
+
discovered skill and prints a per-skill report with a 0-100 score.
|
|
64
|
+
|
|
65
|
+
| Flag | Default | Meaning |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `--format terminal\|json` | `terminal` | Output format. |
|
|
68
|
+
| `--fail-on warning\|error` | `error` | Minimum finding severity that fails the gate. |
|
|
69
|
+
|
|
70
|
+
### `skillseal test <path>`
|
|
71
|
+
|
|
72
|
+
Runs the routing test cases declared in each skill's `skillseal.yaml`
|
|
73
|
+
against a `RoutingEvaluator`, and reports accuracy against `should_trigger`
|
|
74
|
+
and `should_not_trigger` prompts. Skills without a `skillseal.yaml` are
|
|
75
|
+
skipped, not failed.
|
|
76
|
+
|
|
77
|
+
| Flag | Default | Meaning |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| `--threshold <float>` | `0.9` | Minimum accuracy per skill to pass the gate. |
|
|
80
|
+
| `--format terminal\|json` | `terminal` | Output format. |
|
|
81
|
+
| `--provider heuristic\|llm` | `heuristic` | Evaluator to use (see below). |
|
|
82
|
+
|
|
83
|
+
### Exit codes (both commands)
|
|
84
|
+
|
|
85
|
+
| Code | Meaning |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `0` | Clean, or the gate passed. |
|
|
88
|
+
| `1` | Gate failed (`--fail-on` / `--threshold` not met). |
|
|
89
|
+
| `2` | Usage or config error — bad path, no `SKILL.md` found, malformed `skillseal.yaml`. |
|
|
90
|
+
|
|
91
|
+
A typo'd path can never silently report success: exit `2` is reserved for
|
|
92
|
+
"SkillSeal couldn't even run the check," distinct from "the check ran and
|
|
93
|
+
found problems" (exit `1`).
|
|
94
|
+
|
|
95
|
+
## Example output
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
$ uv run skillseal check examples/bad-skill
|
|
99
|
+
|
|
100
|
+
examples/bad-skill/SKILL.md
|
|
101
|
+
|
|
102
|
+
Specification WARN
|
|
103
|
+
Quality WARN
|
|
104
|
+
Security FAIL
|
|
105
|
+
Portability WARN
|
|
106
|
+
|
|
107
|
+
Issues
|
|
108
|
+
|
|
109
|
+
WARN name-directory-mismatch
|
|
110
|
+
Frontmatter 'name' does not match the skill's directory name.
|
|
111
|
+
name: 'helper', directory: 'bad-skill'
|
|
112
|
+
|
|
113
|
+
WARN description-too-vague
|
|
114
|
+
Description may not provide enough information for reliable routing.
|
|
115
|
+
matched vague phrase: "helps with tasks"
|
|
116
|
+
|
|
117
|
+
FAIL rm-rf
|
|
118
|
+
Potential risk: recursive force-delete command found in a code block.
|
|
119
|
+
1 occurrence(s), e.g. "rm -rf"
|
|
120
|
+
|
|
121
|
+
FAIL pipe-to-shell
|
|
122
|
+
Potential risk: downloads remote content and pipes it directly into a shell.
|
|
123
|
+
1 occurrence(s), e.g. "curl https://example.com/install.sh | sh"
|
|
124
|
+
|
|
125
|
+
WARN absolute-path
|
|
126
|
+
Skill assumes absolute filesystem paths, which won't exist on other machines.
|
|
127
|
+
/Users/someone/projects/output, /Users/someone/projects/output/tmp
|
|
128
|
+
|
|
129
|
+
... (more findings omitted for brevity — run it yourself to see the rest)
|
|
130
|
+
|
|
131
|
+
SkillSeal Score: 68/100
|
|
132
|
+
|
|
133
|
+
Specification 90
|
|
134
|
+
Quality 60
|
|
135
|
+
Security 40
|
|
136
|
+
Portability 90
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
$ uv run skillseal test examples/bad-skill
|
|
141
|
+
|
|
142
|
+
helper
|
|
143
|
+
|
|
144
|
+
Should trigger 5/5
|
|
145
|
+
Should NOT trigger 5/7
|
|
146
|
+
|
|
147
|
+
Accuracy 83.3%
|
|
148
|
+
|
|
149
|
+
Failures:
|
|
150
|
+
|
|
151
|
+
✗ "Help me write a poem"
|
|
152
|
+
Expected: NOT TRIGGER
|
|
153
|
+
Actual: TRIGGER
|
|
154
|
+
Likely reason:
|
|
155
|
+
Matched terms: help
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## `skillseal.yaml` format
|
|
159
|
+
|
|
160
|
+
Place a `skillseal.yaml` next to a `SKILL.md` to define its routing tests:
|
|
161
|
+
|
|
162
|
+
```yaml
|
|
163
|
+
version: 1
|
|
164
|
+
|
|
165
|
+
routing:
|
|
166
|
+
should_trigger:
|
|
167
|
+
- "Review this payment implementation"
|
|
168
|
+
- "Check whether this Stripe integration is secure"
|
|
169
|
+
|
|
170
|
+
should_not_trigger:
|
|
171
|
+
- "Write a React button"
|
|
172
|
+
- "Explain Kubernetes"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
- A missing `skillseal.yaml` means that skill is **skipped**, not failed.
|
|
176
|
+
- Empty `should_trigger`/`should_not_trigger` lists are skipped too (no 0/0
|
|
177
|
+
false pass or divide-by-zero).
|
|
178
|
+
- Malformed YAML is a usage error (exit `2`), not a crash.
|
|
179
|
+
|
|
180
|
+
## Using it in CI
|
|
181
|
+
|
|
182
|
+
```yaml
|
|
183
|
+
- name: Check Agent Skills
|
|
184
|
+
run: uv run skillseal check ./skills --fail-on error
|
|
185
|
+
|
|
186
|
+
- name: Test Agent Skill Routing
|
|
187
|
+
run: uv run skillseal test ./skills
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
This repo's own [`.github/workflows/ci.yml`](.github/workflows/ci.yml) does
|
|
191
|
+
the same against `examples/`, plus lint/type-check/unit tests.
|
|
192
|
+
|
|
193
|
+
## Releasing
|
|
194
|
+
|
|
195
|
+
Publishing to PyPI is automated via
|
|
196
|
+
[`.github/workflows/release.yml`](.github/workflows/release.yml) using
|
|
197
|
+
[PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/) — no
|
|
198
|
+
API token stored anywhere.
|
|
199
|
+
|
|
200
|
+
1. Bump `version` in `pyproject.toml`.
|
|
201
|
+
2. Commit, then tag: `git tag vX.Y.Z && git push origin vX.Y.Z`.
|
|
202
|
+
3. The workflow verifies the tag matches `pyproject.toml`, builds the sdist
|
|
203
|
+
and wheel, and publishes to PyPI via OIDC.
|
|
204
|
+
|
|
205
|
+
## The score
|
|
206
|
+
|
|
207
|
+
Deterministic, no LLM involved. Each of the four categories starts at 100 and
|
|
208
|
+
loses points per finding:
|
|
209
|
+
|
|
210
|
+
| Severity | Deduction |
|
|
211
|
+
|---|---|
|
|
212
|
+
| `ERROR` | -25 |
|
|
213
|
+
| `WARNING` | -10 |
|
|
214
|
+
| `INFO` | -0 |
|
|
215
|
+
|
|
216
|
+
`INFO` findings (like "requires docker") are purely descriptive — declaring a
|
|
217
|
+
real dependency isn't a defect, so it doesn't cost points. Rules aggregate
|
|
218
|
+
repeated occurrences of the *same* issue into one finding with a count, so a
|
|
219
|
+
long file can't rack up an artificially low score just from file size.
|
|
220
|
+
|
|
221
|
+
The total is a weighted sum of the four category scores:
|
|
222
|
+
|
|
223
|
+
| Category | Weight | Why |
|
|
224
|
+
|---|---|---|
|
|
225
|
+
| Specification | 30% | Broken/missing metadata breaks loading and routing outright. |
|
|
226
|
+
| Quality | 30% | Vague or bloated instructions are the main cause of routing failures. |
|
|
227
|
+
| Security | 25% | Real risk, weighted close behind. |
|
|
228
|
+
| Portability | 15% | Declared environment dependencies are often expected, not defects. |
|
|
229
|
+
|
|
230
|
+
## Architecture
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
src/skillseal/
|
|
234
|
+
├── models.py # pydantic models: Skill, Finding, SkillReport, routing models
|
|
235
|
+
├── parser.py # discover_skills(), parse_skill() — never raises on bad YAML
|
|
236
|
+
├── linter.py # ties parser + rules + scoring together
|
|
237
|
+
├── scoring.py # deterministic 0-100 scoring
|
|
238
|
+
├── rules/
|
|
239
|
+
│ ├── base.py # Rule protocol, FuncRule, registry, text helpers
|
|
240
|
+
│ ├── metadata.py # SPECIFICATION rules
|
|
241
|
+
│ ├── quality.py # QUALITY rules
|
|
242
|
+
│ ├── security.py # SECURITY rules
|
|
243
|
+
│ └── portability.py # PORTABILITY rules
|
|
244
|
+
├── routing/
|
|
245
|
+
│ ├── evaluator.py # HeuristicRoutingEvaluator, LLMRoutingEvaluator, LLMProvider
|
|
246
|
+
│ └── runner.py # loads skillseal.yaml, runs cases
|
|
247
|
+
├── reporters/
|
|
248
|
+
│ ├── terminal.py # Rich terminal output
|
|
249
|
+
│ └── json_reporter.py # stable JSON schema
|
|
250
|
+
└── cli.py # typer app: check, test
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
A `Rule` is `id`, `category`, `severity`, `description`, and
|
|
254
|
+
`check(skill) -> list[Finding]`. Most rules are built with `FuncRule`, which
|
|
255
|
+
wraps a plain function so adding a check doesn't require a new class.
|
|
256
|
+
|
|
257
|
+
Routing evaluation is behind a `RoutingEvaluator` protocol with two
|
|
258
|
+
implementations:
|
|
259
|
+
|
|
260
|
+
- **`HeuristicRoutingEvaluator`** (default): fully offline, no API key needed.
|
|
261
|
+
Scores how much of a prompt's distinctive vocabulary (after stopword
|
|
262
|
+
removal and light suffix stripping) is covered by the skill's own name,
|
|
263
|
+
description, and `keywords:`. It's deliberately simple — not real NLP —
|
|
264
|
+
which is also why it's fast, free, and explainable ("Matched terms: ...").
|
|
265
|
+
- **`LLMRoutingEvaluator`**: delegates the trigger/no-trigger decision to an
|
|
266
|
+
`LLMProvider` (`complete(prompt) -> str`). `OpenAICompatibleProvider`
|
|
267
|
+
implements this against any OpenAI-compatible `/chat/completions` endpoint,
|
|
268
|
+
configured via `SKILLSEAL_BASE_URL`, `SKILLSEAL_API_KEY`, and
|
|
269
|
+
`SKILLSEAL_MODEL`. Use `--provider llm` to opt in — it's never required.
|
|
270
|
+
|
|
271
|
+
## Limitations
|
|
272
|
+
|
|
273
|
+
- Rules are regex/heuristic-based, not a real parser or NLP model — they will
|
|
274
|
+
have false positives and false negatives. Findings are phrased as
|
|
275
|
+
*potential* risk, never certainty.
|
|
276
|
+
- The heuristic routing evaluator uses simple tokenization and suffix
|
|
277
|
+
stripping, not real stemming or embeddings; words like "secure" and
|
|
278
|
+
"security" won't match each other.
|
|
279
|
+
- Token counts are a rough `len(text) // 4` estimate, not a real tokenizer.
|
|
280
|
+
- No sandboxing or dynamic execution — nothing in a skill is ever run.
|
|
281
|
+
- No compatibility testing against real agents (Claude Code, Codex, Gemini,
|
|
282
|
+
etc.) — see the roadmap.
|
|
283
|
+
|
|
284
|
+
## Roadmap
|
|
285
|
+
|
|
286
|
+
Documented, not implemented, on purpose — this is an MVP:
|
|
287
|
+
|
|
288
|
+
- Real execution against Claude Code, Codex, Gemini, and other agents
|
|
289
|
+
- A compatibility matrix across agents/environments
|
|
290
|
+
- Sandboxed dynamic analysis of skill-invoked commands
|
|
291
|
+
- Auto-fix for common findings
|
|
292
|
+
- Version-to-version comparison for a skill
|
|
293
|
+
- A GitHub App
|
|
294
|
+
- A web dashboard
|
|
295
|
+
- A skill registry / marketplace
|
|
296
|
+
- A hosted/cloud service
|
|
297
|
+
- Telemetry
|
|
298
|
+
- Skill certification
|
|
299
|
+
|
|
300
|
+
## License
|
|
301
|
+
|
|
302
|
+
[MIT](LICENSE)
|