@yogiadhik/skillcheck 0.1.1 → 0.1.3
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 +42 -2
- package/dist/cli.js +8 -4
- package/dist/lint/rules.js +3 -0
- package/dist/reporters/terminal.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,8 +49,12 @@ Run `npx @yogiadhik/skillcheck init <skill-dir>` to scaffold a starter file.
|
|
|
49
49
|
|
|
50
50
|
Behavioral runs call the real agent, and that costs real money.
|
|
51
51
|
skillcheck caches results by content hash — unchanged skill, unchanged
|
|
52
|
-
case, no re-run — and `--budget 2.50`
|
|
53
|
-
|
|
52
|
+
case, no re-run — and `--budget 2.50` stops the run once the cap is
|
|
53
|
+
crossed. The check happens between cases, so one long case can still
|
|
54
|
+
overshoot on its own: pair a budget with `--model haiku` to keep
|
|
55
|
+
single cases cheap. skillcheck tells you when no model is set, and
|
|
56
|
+
warns at the end if a run blew past its budget. Lint never spends
|
|
57
|
+
anything.
|
|
54
58
|
|
|
55
59
|
## CI
|
|
56
60
|
|
|
@@ -62,6 +66,42 @@ That runs lint with GitHub annotations on every pull request. For
|
|
|
62
66
|
behavioral runs in CI, call `npx @yogiadhik/skillcheck run` yourself in a job that
|
|
63
67
|
has your agent authenticated; keep a budget flag on it.
|
|
64
68
|
|
|
69
|
+
## what it finds in the wild
|
|
70
|
+
|
|
71
|
+
I periodically run lint across public skill collections — official
|
|
72
|
+
vendor skills, a security firm's suite, several large community
|
|
73
|
+
bundles, close to eight thousand skill files so far. No names,
|
|
74
|
+
because the point is the pattern, and the pattern repeats:
|
|
75
|
+
|
|
76
|
+
- **Skills that silently don't work.** If a skill's frontmatter isn't
|
|
77
|
+
valid YAML, the skill never loads — and nothing tells you. The usual
|
|
78
|
+
cause is an unquoted description containing a colon. Every large
|
|
79
|
+
bundle I've audited shipped a few of these; anyone installing them
|
|
80
|
+
gets a skill that simply never fires.
|
|
81
|
+
- **Instructions pointing at files that don't exist.** Skills tell the
|
|
82
|
+
agent "read references/details.md for the full procedure" and that
|
|
83
|
+
file was never committed. The agent hits a dead end in the middle of
|
|
84
|
+
someone's task. This is the most common hard error.
|
|
85
|
+
- **Sheer size.** The most common warning by a wide margin. Skills
|
|
86
|
+
running twenty to forty thousand tokens re-bill that entire document
|
|
87
|
+
on every single invocation.
|
|
88
|
+
- **Descriptions without trigger language.** The description is the
|
|
89
|
+
only thing the model reads when deciding whether to use a skill; a
|
|
90
|
+
description that doesn't say when to use it is a skill that fires
|
|
91
|
+
rarely and randomly.
|
|
92
|
+
- **Well-maintained collections come back clean.** Several do,
|
|
93
|
+
official and community both. Quality is achievable; it's just not
|
|
94
|
+
the default.
|
|
95
|
+
|
|
96
|
+
Two honest footnotes. First: twice, the loudest "errors" in an audit
|
|
97
|
+
turned out to be this linter's own false positives — links inside
|
|
98
|
+
example code blocks, and templated {baseDir} targets that resolve at
|
|
99
|
+
runtime. Both fixed (0.1.1, 0.1.3), both caught by checking findings
|
|
100
|
+
against the accused code before believing them. Do the same with
|
|
101
|
+
anything this tool tells you. Second: these audits are how skillcheck
|
|
102
|
+
gets tested — every rule here has drawn blood on real files or been
|
|
103
|
+
fixed for crying wolf.
|
|
104
|
+
|
|
65
105
|
## a note on safety
|
|
66
106
|
|
|
67
107
|
`run` executes the agent with permission prompts disabled, inside a
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ const program = new Command();
|
|
|
9
9
|
program
|
|
10
10
|
.name('skillcheck')
|
|
11
11
|
.description('lint and behavioral tests for agent skills')
|
|
12
|
-
.version('0.1.
|
|
12
|
+
.version('0.1.3');
|
|
13
13
|
program
|
|
14
14
|
.command('lint')
|
|
15
15
|
.argument('[path]', 'skill file or directory', '.')
|
|
@@ -43,18 +43,22 @@ program
|
|
|
43
43
|
.action(async (path, opts) => {
|
|
44
44
|
try {
|
|
45
45
|
const files = discoverTestFiles(path);
|
|
46
|
-
if (!opts.json)
|
|
46
|
+
if (!opts.json) {
|
|
47
47
|
console.log(`${files.length} test file(s); live agent runs cost money — cap with --budget`);
|
|
48
|
+
if (!opts.model)
|
|
49
|
+
console.log('no --model given — using the CLI default model; pass --model haiku for cheap runs');
|
|
50
|
+
}
|
|
51
|
+
const budgetUsd = opts.budget ? Number(opts.budget) : undefined;
|
|
48
52
|
const reports = await runTests(path, {
|
|
49
53
|
model: opts.model,
|
|
50
|
-
budgetUsd
|
|
54
|
+
budgetUsd,
|
|
51
55
|
timeoutMs: Number(opts.timeout) * 1000,
|
|
52
56
|
cache: opts.cache,
|
|
53
57
|
});
|
|
54
58
|
if (opts.json)
|
|
55
59
|
console.log(JSON.stringify(reports, null, 2));
|
|
56
60
|
else
|
|
57
|
-
printReports(reports);
|
|
61
|
+
printReports(reports, budgetUsd);
|
|
58
62
|
process.exitCode = reportsPass(reports) ? 0 : 1;
|
|
59
63
|
}
|
|
60
64
|
catch (e) {
|
package/dist/lint/rules.js
CHANGED
|
@@ -50,6 +50,9 @@ export const relativeLinks = (s) => {
|
|
|
50
50
|
const target = m[1];
|
|
51
51
|
if (/^(https?:|mailto:|#)/.test(target))
|
|
52
52
|
continue;
|
|
53
|
+
// templated targets like {baseDir}/x.md resolve at runtime; can't judge them
|
|
54
|
+
if (target.includes('{'))
|
|
55
|
+
continue;
|
|
53
56
|
if (!existsSync(resolve(s.dir, target.split('#')[0])))
|
|
54
57
|
out.push(finding(s, 'relative-links', 'error', `broken relative link: ${target}`));
|
|
55
58
|
}
|
|
@@ -7,7 +7,7 @@ export function printFindings(findings) {
|
|
|
7
7
|
const errors = findings.filter((f) => f.severity === 'error').length;
|
|
8
8
|
console.log(`\n${findings.length} finding(s), ${errors} error(s)`);
|
|
9
9
|
}
|
|
10
|
-
export function printReports(reports) {
|
|
10
|
+
export function printReports(reports, budgetUsd) {
|
|
11
11
|
let cost = 0;
|
|
12
12
|
for (const r of reports) {
|
|
13
13
|
cost += r.costUsd;
|
|
@@ -24,4 +24,6 @@ export function printReports(reports) {
|
|
|
24
24
|
console.log(pc.red(` judge: ${r.verdict.score}/10 — ${r.verdict.reasoning}`));
|
|
25
25
|
}
|
|
26
26
|
console.log(`\n${reports.length} case(s), $${cost.toFixed(4)} spent`);
|
|
27
|
+
if (budgetUsd !== undefined && cost > budgetUsd)
|
|
28
|
+
console.log(pc.yellow(`budget of $${budgetUsd} exceeded — the cap is checked between cases, so a single case can overshoot it`));
|
|
27
29
|
}
|