@ssheleg/make-skill 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.15.0
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Reachability counted links and missed the files a skill leans on hardest.** After
|
|
8
|
+
v0.13.0 taught the check to follow links transitively, it still only followed *links* —
|
|
9
|
+
so four bundled scripts in `seo-aeo-audit` were reported as dead weight while `SKILL.md`
|
|
10
|
+
names them in the sentence that tells the agent what ships and the references name them
|
|
11
|
+
in the commands that run them: `` `gsc_pull.py` ``, `scripts/psi_pull.py`. A file the
|
|
12
|
+
body tells you to execute is the opposite of unopened.
|
|
13
|
+
|
|
14
|
+
Reachability is now by link, by path, or by bare filename, followed transitively.
|
|
15
|
+
Probed: a reference nobody mentions anywhere is still flagged. `seo-aeo-audit` 4 → 0,
|
|
16
|
+
family 11 → 7, nothing gained elsewhere.
|
|
17
|
+
|
|
18
|
+
**Third false-positive class in three releases, all one shape**: the check asserted how
|
|
19
|
+
a skill refers to its own files instead of measuring how these skills actually do it.
|
|
20
|
+
Between them the three fired on 45 correct files — more than half of every finding this
|
|
21
|
+
auditor reported across the family.
|
|
22
|
+
|
|
23
|
+
## v0.14.0
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **`LINK_ESCAPE` fired on every cross-skill link and called them all broken.** The
|
|
28
|
+
check rejected any `../` target on the stated grounds that "packagers ship that
|
|
29
|
+
directory alone, so it arrives broken on every agent." Measured against the packager:
|
|
30
|
+
`npx skills add ssheleg/task-pipeline --skills evidence-docs` installed the sibling
|
|
31
|
+
too, laid both under one `skills/` directory, and **all eleven** of `evidence-docs`'s
|
|
32
|
+
outbound links resolved. The rationale was false for the case it fired on most.
|
|
33
|
+
|
|
34
|
+
An escape that lands on a sibling in the same tree is a cross-skill link inside one
|
|
35
|
+
plugin, and `evidence-docs` is deliberately built that way — it says so in its own
|
|
36
|
+
body: *a navigator, not a second copy*. Twelve permanent findings nobody could ever
|
|
37
|
+
close is how an auditor teaches people to ignore it.
|
|
38
|
+
|
|
39
|
+
The check now fires only when the target resolves to **nothing**, which is the defect
|
|
40
|
+
it was always for. Probed both ways: a dangling `../nonexistent-skill/nope.md` is
|
|
41
|
+
still caught; the eleven real ones are not. Family-wide the count went 22 → 11 GAP,
|
|
42
|
+
and no repository gained a finding.
|
|
43
|
+
|
|
3
44
|
## v0.13.1
|
|
4
45
|
|
|
5
46
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ssheleg/make-skill",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Create, retrofit, audit, and ship agent skills & Claude Code plugins the proven ssheleg way — conformance to the Agent Skills open standard AND Anthropic's platform rules (front-matter limits, disclosure budgets, per-surface runtime limits, the Skills API, evals) plus the Claude Code plugin reference (manifest schemas, component layout, claude plugin validate --strict), marketplace repo layout, version sync, validator + CI, multi-channel distribution (plugin, vercel skills CLI, npx, Cursor), npm gotchas, the review checklist for third-party skills, and MCP / A2A rules for protocol-connected skills. This package is the installer CLI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"skill",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "make-skill",
|
|
4
4
|
"displayName": "Make Skill",
|
|
5
5
|
"description": "Create, retrofit, audit, and ship agent skills & Claude Code plugins the proven ssheleg way: conformance to the Agent Skills open standard, Anthropic's platform rules (surfaces, Skills API, evals) and the Claude Code plugin reference, marketplace repo layout, version sync, validator + CI, multi-channel distribution (plugin, vercel skills CLI, npx, Cursor), npm gotchas, end-to-end first publish, the review checklist for third-party skills, plus MCP / A2A references for protocol-connected skills.",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.15.0",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "ssheleg",
|
|
9
9
|
"url": "https://x.com/sshlg93"
|
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
compatibility: Authoring works on any agent. The bundled scripts/ need python3. Publishing steps need git, gh, node and npm; the plugin gates need the claude CLI. Not usable on the Claude API surface, which has no network and no runtime package install.
|
|
6
6
|
metadata:
|
|
7
7
|
author: ssheleg
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.15.0"
|
|
9
9
|
homepage: https://github.com/ssheleg/make-skill
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -309,8 +309,30 @@ def _bundle_closure(skill_dir, skill_text):
|
|
|
309
309
|
if os.path.isfile(os.path.join(d, entry)):
|
|
310
310
|
available["%s/%s" % (sub, entry)] = os.path.join(d, entry)
|
|
311
311
|
|
|
312
|
+
def named_in(text):
|
|
313
|
+
"""Keys this text reaches: by link, by path, or by bare filename.
|
|
314
|
+
|
|
315
|
+
A script the body tells the agent to run is named in prose and a command —
|
|
316
|
+
`python3 scripts/gsc_pull.py`, or just `gsc_pull.py` in a list of what ships —
|
|
317
|
+
never as a markdown link. It is the opposite of dead weight, so matching only
|
|
318
|
+
link syntax reports the files a skill leans on hardest.
|
|
319
|
+
"""
|
|
320
|
+
hits = set()
|
|
321
|
+
for key, _path in available.items():
|
|
322
|
+
base = key.split("/", 1)[1]
|
|
323
|
+
if key in text or base in text:
|
|
324
|
+
hits.add(key)
|
|
325
|
+
for target in LINK_RE.findall(text):
|
|
326
|
+
t = target.split("#", 1)[0].strip().lstrip("./")
|
|
327
|
+
if not t or "://" in t:
|
|
328
|
+
continue
|
|
329
|
+
for key in available:
|
|
330
|
+
if key == t or key.endswith("/" + t):
|
|
331
|
+
hits.add(key)
|
|
332
|
+
return hits
|
|
333
|
+
|
|
312
334
|
seen = set()
|
|
313
|
-
stack =
|
|
335
|
+
stack = list(named_in(skill_text))
|
|
314
336
|
while stack:
|
|
315
337
|
key = stack.pop()
|
|
316
338
|
if key in seen:
|
|
@@ -318,19 +340,11 @@ def _bundle_closure(skill_dir, skill_text):
|
|
|
318
340
|
seen.add(key)
|
|
319
341
|
if not key.endswith(".md"):
|
|
320
342
|
continue
|
|
321
|
-
sub = key.split("/", 1)[0]
|
|
322
343
|
try:
|
|
323
344
|
txt = open(available[key], encoding="utf-8").read()
|
|
324
345
|
except (OSError, UnicodeDecodeError):
|
|
325
346
|
continue
|
|
326
|
-
for
|
|
327
|
-
t = target.split("#", 1)[0].strip().lstrip("./")
|
|
328
|
-
if not t or "://" in t:
|
|
329
|
-
continue
|
|
330
|
-
# a bare name inside references/ means a sibling of that same directory
|
|
331
|
-
cand = t if "/" in t else "%s/%s" % (sub, t)
|
|
332
|
-
if cand in available and cand not in seen:
|
|
333
|
-
stack.append(cand)
|
|
347
|
+
stack.extend(k for k in named_in(txt) if k not in seen)
|
|
334
348
|
return seen
|
|
335
349
|
|
|
336
350
|
|
|
@@ -377,8 +391,16 @@ def _check_links(a, skill_dir, skill_text, rel):
|
|
|
377
391
|
if target.startswith(("http://", "https://", "mailto:", "#")):
|
|
378
392
|
continue
|
|
379
393
|
if target.startswith("../") or "/../" in target:
|
|
380
|
-
a
|
|
381
|
-
|
|
394
|
+
# An escape that lands on a sibling skill in the same tree is a
|
|
395
|
+
# cross-skill link inside one plugin, and every packager measured
|
|
396
|
+
# ships those siblings together — `npx skills add <repo> --skills
|
|
397
|
+
# <one>` installed the sibling too, and all eleven links resolved.
|
|
398
|
+
# An escape that lands nowhere is the defect this check is for.
|
|
399
|
+
out = os.path.normpath(os.path.join(skill_dir, target.split("#")[0]))
|
|
400
|
+
if os.path.exists(out):
|
|
401
|
+
continue
|
|
402
|
+
a.gap("LINK_ESCAPE", "link %r leaves the skill directory and resolves "
|
|
403
|
+
"to nothing — a sibling would ship alongside, this will not"
|
|
382
404
|
% target, rel, i)
|
|
383
405
|
bad = True
|
|
384
406
|
continue
|