skillgauntlet 1.1.1 → 1.2.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/README.md +30 -1
- package/bin/skillgauntlet.js +325 -131
- package/lib/browse.js +58 -0
- package/lib/chart.js +157 -0
- package/lib/depth.js +4 -1
- package/lib/discover.js +27 -2
- package/lib/findhost.js +83 -0
- package/lib/i18n.js +384 -36
- package/lib/models.js +38 -0
- package/lib/report.js +1 -1
- package/lib/schema.js +90 -0
- package/lib/scrub.js +186 -0
- package/lib/static.js +22 -11
- package/lib/submission.js +325 -0
- package/lib/submit.js +208 -0
- package/lib/ticker.js +39 -0
- package/lib/trial.js +27 -0
- package/lib/ui.js +40 -2
- package/package.json +4 -2
- package/schemas/submission-1.json +210 -0
package/lib/ui.js
CHANGED
|
@@ -6,7 +6,14 @@
|
|
|
6
6
|
// the run mid-flow.
|
|
7
7
|
import readline, { emitKeypressEvents } from 'node:readline';
|
|
8
8
|
import { stdin as input, stdout as output } from 'node:process';
|
|
9
|
-
import { words } from './i18n.js';
|
|
9
|
+
import { words, t } from './i18n.js';
|
|
10
|
+
|
|
11
|
+
// Thrown by a list when the user presses ← or Esc: the caller goes back one question.
|
|
12
|
+
export class Back extends Error {
|
|
13
|
+
constructor() { super('back'); this.name = 'Back'; }
|
|
14
|
+
}
|
|
15
|
+
let backEnabled = false;
|
|
16
|
+
export const setBack = (on) => { backEnabled = on; };
|
|
10
17
|
|
|
11
18
|
let rl = null;
|
|
12
19
|
const queue = [];
|
|
@@ -37,10 +44,22 @@ export function pauseUi() {
|
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
|
|
47
|
+
// On Windows a terminal stdin that was ever put in raw mode can keep the process alive after the last prompt (the
|
|
48
|
+
// program handled Ctrl+C, then sat there with no prompt back). Let go of it explicitly.
|
|
40
49
|
export function closeUi() {
|
|
41
50
|
pauseUi();
|
|
51
|
+
if (input.isTTY) {
|
|
52
|
+
try {
|
|
53
|
+
if (input.setRawMode) input.setRawMode(false);
|
|
54
|
+
input.pause();
|
|
55
|
+
input.unref?.();
|
|
56
|
+
input.destroy?.();
|
|
57
|
+
} catch { /* already closed */ }
|
|
58
|
+
}
|
|
42
59
|
}
|
|
43
60
|
|
|
61
|
+
export const hasTerminal = () => Boolean(input.isTTY);
|
|
62
|
+
|
|
44
63
|
export function say(line = '') {
|
|
45
64
|
output.write(`${line}\n`);
|
|
46
65
|
}
|
|
@@ -61,6 +80,9 @@ export async function ask(question, def = '') {
|
|
|
61
80
|
}
|
|
62
81
|
|
|
63
82
|
export async function confirm(question, def = false) {
|
|
83
|
+
if (input.isTTY) {
|
|
84
|
+
return select(question, [{ label: t('ui.no'), value: false }, { label: t('ui.yes'), value: true }], def ? 1 : 0, { hotkeys: { y: 1, n: 0 } });
|
|
85
|
+
}
|
|
64
86
|
const hint = def ? 'Y/n' : 'y/N';
|
|
65
87
|
for (;;) {
|
|
66
88
|
const a = (await ask(`${question} (${hint})`, '')).toLowerCase();
|
|
@@ -86,7 +108,9 @@ export async function choose(title, options, def = 1) {
|
|
|
86
108
|
}
|
|
87
109
|
}
|
|
88
110
|
|
|
89
|
-
|
|
111
|
+
// opts.clear: erase the list once an entry is chosen, so a caller that shows the next level right away redraws
|
|
112
|
+
// in the same place instead of stacking lists down the terminal.
|
|
113
|
+
export async function select(title, options, defIndex = 0, opts = {}) {
|
|
90
114
|
if (!input.isTTY) {
|
|
91
115
|
say('');
|
|
92
116
|
say(title);
|
|
@@ -108,6 +132,7 @@ export async function select(title, options, defIndex = 0) {
|
|
|
108
132
|
const WINDOW = 8;
|
|
109
133
|
const width = Math.min(48, Math.max(...options.map((o) => o.label.length)));
|
|
110
134
|
let first = true;
|
|
135
|
+
let height = 0;
|
|
111
136
|
const render = () => {
|
|
112
137
|
const size = Math.min(WINDOW, options.length);
|
|
113
138
|
const start = Math.min(Math.max(0, idx - Math.floor(size / 2)), Math.max(0, options.length - size));
|
|
@@ -118,6 +143,8 @@ export async function select(title, options, defIndex = 0) {
|
|
|
118
143
|
const body = i === idx ? `\x1b[7m▶ ${label} \x1b[0m` : ` ${label} `;
|
|
119
144
|
lines.push(`${body}${o.hint ? ` ${o.hint}` : ''}`);
|
|
120
145
|
}
|
|
146
|
+
if (backEnabled && !opts.noBack) lines.push(`\x1b[2m${t('backHint')}\x1b[0m`);
|
|
147
|
+
height = lines.length;
|
|
121
148
|
if (first) {
|
|
122
149
|
output.write(`\n${lines.join('\n')}\n`);
|
|
123
150
|
first = false;
|
|
@@ -142,10 +169,21 @@ export async function select(title, options, defIndex = 0) {
|
|
|
142
169
|
render();
|
|
143
170
|
} else if (key.name === 'return' || key.name === 'enter') {
|
|
144
171
|
cleanup();
|
|
172
|
+
if (opts.clear) output.write(`\x1b[${height + 1}A\x1b[J`);
|
|
145
173
|
resolve(options[idx].value);
|
|
174
|
+
} else if ((key.name === 'escape' || key.name === 'left') && backEnabled && !opts.noBack) {
|
|
175
|
+
cleanup();
|
|
176
|
+
output.write(`\x1b[${height + 1}A\x1b[J`);
|
|
177
|
+
reject(new Back());
|
|
146
178
|
} else if (key.ctrl && key.name === 'c') {
|
|
147
179
|
cleanup();
|
|
148
180
|
reject(new Error('interrupted'));
|
|
181
|
+
} else if (ch && opts.hotkeys && Object.hasOwn(opts.hotkeys, ch.toLowerCase())) {
|
|
182
|
+
// a hotkey (y / n) chooses at once
|
|
183
|
+
idx = opts.hotkeys[ch.toLowerCase()];
|
|
184
|
+
render();
|
|
185
|
+
cleanup();
|
|
186
|
+
resolve(options[idx].value);
|
|
149
187
|
} else if (ch && /^[1-9]$/.test(ch) && Number(ch) <= options.length) {
|
|
150
188
|
idx = Number(ch) - 1;
|
|
151
189
|
render();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillgauntlet",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Check an agent skill before you install it: context cost, permission surface, code risk, instruction hijacking, description quality, trigger rate and gain.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"bin",
|
|
11
11
|
"lib",
|
|
12
12
|
"contracts",
|
|
13
|
+
"schemas",
|
|
13
14
|
"README.md",
|
|
14
15
|
"LICENSE",
|
|
15
16
|
"LICENSE-DATA"
|
|
@@ -20,7 +21,8 @@
|
|
|
20
21
|
"scripts": {
|
|
21
22
|
"start": "node ./bin/skillgauntlet.js",
|
|
22
23
|
"static": "node ./bin/skillgauntlet.js static",
|
|
23
|
-
"test": "node --test tests/*.test.mjs"
|
|
24
|
+
"test": "node --test tests/*.test.mjs",
|
|
25
|
+
"leaderboard": "node scripts/build-leaderboard.mjs"
|
|
24
26
|
},
|
|
25
27
|
"keywords": [
|
|
26
28
|
"agent",
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/SkillGauntlet/SkillGauntlet/blob/main/schemas/submission-1.json",
|
|
4
|
+
"title": "SkillGauntlet community submission, version 1",
|
|
5
|
+
"description": "One submission directory holds meta.json, raw.jsonl, summary.json and queries.json. Definitions: meta, rawLine, summary, queries.",
|
|
6
|
+
"$defs": {
|
|
7
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
8
|
+
"commit": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
|
|
9
|
+
"isoTime": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$" },
|
|
10
|
+
"shortText": { "type": "string", "maxLength": 300 },
|
|
11
|
+
"rate": { "type": ["number", "null"], "minimum": 0, "maximum": 1 },
|
|
12
|
+
"interval": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"minItems": 2,
|
|
15
|
+
"maxItems": 2,
|
|
16
|
+
"items": { "type": ["number", "null"], "minimum": 0, "maximum": 1 }
|
|
17
|
+
},
|
|
18
|
+
"meta": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"required": [
|
|
21
|
+
"schema", "runId", "runnerVersion", "skill", "host", "backend", "model", "runs", "maxExplore",
|
|
22
|
+
"querySets", "platform", "startedAt", "finishedAt", "trials", "interrupted", "submission"
|
|
23
|
+
],
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"properties": {
|
|
26
|
+
"schema": { "const": "skillgauntlet/run/1" },
|
|
27
|
+
"runId": { "type": "string", "pattern": "^[0-9]{8}-[0-9]{6}(-[0-9]+)?$" },
|
|
28
|
+
"runnerVersion": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+([-+][0-9A-Za-z.-]+)?$" },
|
|
29
|
+
"skill": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
30
|
+
"host": { "enum": ["claude-code", "codebuddy", "codewhale"] },
|
|
31
|
+
"backend": { "type": "string", "maxLength": 100 },
|
|
32
|
+
"model": { "type": ["string", "null"], "maxLength": 100 },
|
|
33
|
+
"runs": { "type": "integer", "minimum": 1, "maximum": 20 },
|
|
34
|
+
"maxExplore": { "type": "integer", "minimum": 0, "maximum": 20 },
|
|
35
|
+
"depth": { "type": "object", "maxProperties": 8 },
|
|
36
|
+
"querySets": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"minItems": 1,
|
|
39
|
+
"maxItems": 4,
|
|
40
|
+
"items": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"required": ["id", "queries", "sha256"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"id": { "type": "string", "maxLength": 40 },
|
|
45
|
+
"source": { "type": "string", "maxLength": 40 },
|
|
46
|
+
"file": { "type": "string", "maxLength": 120 },
|
|
47
|
+
"queries": { "type": "integer", "minimum": 1 },
|
|
48
|
+
"clauses": { "type": "integer", "minimum": 0 },
|
|
49
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{16}$" },
|
|
50
|
+
"negativesQuality": { "type": "object", "maxProperties": 4 }
|
|
51
|
+
},
|
|
52
|
+
"additionalProperties": false
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"skillFingerprint": { "type": ["string", "null"], "pattern": "^[0-9a-f]{16}$" },
|
|
56
|
+
"platform": { "$ref": "#/$defs/shortText" },
|
|
57
|
+
"startedAt": { "$ref": "#/$defs/isoTime" },
|
|
58
|
+
"finishedAt": { "$ref": "#/$defs/isoTime" },
|
|
59
|
+
"trials": { "type": "integer", "minimum": 1 },
|
|
60
|
+
"interrupted": { "const": false },
|
|
61
|
+
"resumedAt": { "type": "array", "maxItems": 50, "items": { "$ref": "#/$defs/isoTime" } },
|
|
62
|
+
"modelResolved": { "type": ["string", "null"], "maxLength": 100 },
|
|
63
|
+
"apiKeySource": { "type": ["string", "null"], "maxLength": 40 },
|
|
64
|
+
"claudeCodeVersion": { "type": ["string", "null"], "maxLength": 100 },
|
|
65
|
+
"loadedSkills": { "type": "array", "maxItems": 300, "items": { "type": "string", "maxLength": 100 } },
|
|
66
|
+
"tools": { "type": "array", "maxItems": 300, "items": { "type": "string", "maxLength": 100 } },
|
|
67
|
+
"incompleteTrials": { "type": "integer", "minimum": 0 },
|
|
68
|
+
"submission": { "$ref": "#/$defs/submission" }
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"submission": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["schema", "contributor", "skill", "host", "model", "queryPack", "environment", "submittedAt"],
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"properties": {
|
|
76
|
+
"schema": { "const": "skillgauntlet/submission/1" },
|
|
77
|
+
"contributor": { "type": "string", "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$" },
|
|
78
|
+
"skill": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"required": ["name", "repo", "commit", "subdir", "skillMdSha256"],
|
|
81
|
+
"additionalProperties": false,
|
|
82
|
+
"properties": {
|
|
83
|
+
"name": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
84
|
+
"repo": { "type": "string", "pattern": "^https://github\\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$" },
|
|
85
|
+
"commit": { "$ref": "#/$defs/commit" },
|
|
86
|
+
"subdir": { "type": "string", "maxLength": 200, "pattern": "^[A-Za-z0-9_./-]*$" },
|
|
87
|
+
"skillMdSha256": { "$ref": "#/$defs/sha256" }
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"host": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"required": ["id", "version"],
|
|
93
|
+
"additionalProperties": false,
|
|
94
|
+
"properties": {
|
|
95
|
+
"id": { "enum": ["claude-code", "codebuddy", "codewhale"] },
|
|
96
|
+
"version": { "type": ["string", "null"], "maxLength": 100 }
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"model": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"required": ["requested", "resolved"],
|
|
102
|
+
"additionalProperties": false,
|
|
103
|
+
"properties": {
|
|
104
|
+
"requested": { "type": ["string", "null"], "maxLength": 100 },
|
|
105
|
+
"resolved": { "type": ["string", "null"], "maxLength": 100 }
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"queryPack": {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"required": ["id", "sha256", "queries"],
|
|
111
|
+
"additionalProperties": false,
|
|
112
|
+
"properties": {
|
|
113
|
+
"id": { "type": "string", "maxLength": 40 },
|
|
114
|
+
"sha256": { "$ref": "#/$defs/sha256" },
|
|
115
|
+
"queries": { "type": "integer", "minimum": 1 }
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"environment": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"description": "Used to group and explain results, never to prove anything: every field is self-reported.",
|
|
121
|
+
"required": ["id", "platform", "hostVersion", "backend", "loadedSkills", "toolCount"],
|
|
122
|
+
"additionalProperties": false,
|
|
123
|
+
"properties": {
|
|
124
|
+
"id": { "type": "string", "pattern": "^[0-9a-f]{12}$" },
|
|
125
|
+
"platform": { "$ref": "#/$defs/shortText" },
|
|
126
|
+
"hostVersion": { "type": ["string", "null"], "maxLength": 100 },
|
|
127
|
+
"backend": { "type": "string", "maxLength": 100 },
|
|
128
|
+
"loadedSkills": { "type": "array", "maxItems": 300, "items": { "type": "string", "maxLength": 100 } },
|
|
129
|
+
"toolCount": { "type": "integer", "minimum": 0 }
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"submittedAt": { "$ref": "#/$defs/isoTime" }
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"rawLine": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"required": ["host", "verdict", "queryId", "run", "query", "shouldTrigger"],
|
|
138
|
+
"properties": {
|
|
139
|
+
"host": { "enum": ["claude-code", "codebuddy", "codewhale"] },
|
|
140
|
+
"verdict": { "enum": ["triggered", "not_triggered", "other_skill", "error", "timeout"] },
|
|
141
|
+
"invokedSkill": { "type": ["string", "null"], "maxLength": 200 },
|
|
142
|
+
"firstTool": { "type": ["string", "null"], "maxLength": 200 },
|
|
143
|
+
"tools": { "type": "array", "maxItems": 100, "items": { "type": "string", "maxLength": 200 } },
|
|
144
|
+
"exploreSteps": { "type": "integer", "minimum": 0 },
|
|
145
|
+
"error": { "type": ["string", "null"], "maxLength": 400 },
|
|
146
|
+
"events": { "type": "object", "maxProperties": 10 },
|
|
147
|
+
"queryId": { "type": "string", "maxLength": 40 },
|
|
148
|
+
"run": { "type": "integer", "minimum": 0, "maximum": 50 },
|
|
149
|
+
"query": { "type": "string", "minLength": 1, "maxLength": 4000 },
|
|
150
|
+
"shouldTrigger": { "type": "boolean" }
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"summary": {
|
|
154
|
+
"type": "object",
|
|
155
|
+
"required": ["trials", "recall", "fpr", "questions", "host", "runs", "recallCi", "fprCi"],
|
|
156
|
+
"properties": {
|
|
157
|
+
"trials": { "type": "integer", "minimum": 1 },
|
|
158
|
+
"recall": { "$ref": "#/$defs/rate" },
|
|
159
|
+
"fpr": { "$ref": "#/$defs/rate" },
|
|
160
|
+
"noToolRate": { "$ref": "#/$defs/rate" },
|
|
161
|
+
"failedTrials": { "type": "integer", "minimum": 0 },
|
|
162
|
+
"incomplete": { "type": "integer", "minimum": 0 },
|
|
163
|
+
"model": { "type": ["string", "null"], "maxLength": 100 },
|
|
164
|
+
"backend": { "type": "string", "maxLength": 100 },
|
|
165
|
+
"host": { "enum": ["claude-code", "codebuddy", "codewhale"] },
|
|
166
|
+
"runs": { "type": "integer", "minimum": 1, "maximum": 20 },
|
|
167
|
+
"recallCi": { "$ref": "#/$defs/interval" },
|
|
168
|
+
"fprCi": { "$ref": "#/$defs/interval" },
|
|
169
|
+
"questions": {
|
|
170
|
+
"type": "array",
|
|
171
|
+
"minItems": 1,
|
|
172
|
+
"maxItems": 2000,
|
|
173
|
+
"items": {
|
|
174
|
+
"type": "object",
|
|
175
|
+
"required": ["query", "shouldTrigger", "hits", "n"],
|
|
176
|
+
"properties": {
|
|
177
|
+
"query": { "type": "string", "maxLength": 4000 },
|
|
178
|
+
"shouldTrigger": { "type": "boolean" },
|
|
179
|
+
"hits": { "type": "integer", "minimum": 0 },
|
|
180
|
+
"n": { "type": "integer", "minimum": 1 }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"queries": {
|
|
187
|
+
"type": "object",
|
|
188
|
+
"required": ["schema", "queries"],
|
|
189
|
+
"properties": {
|
|
190
|
+
"schema": { "const": "skillgauntlet/queries/1" },
|
|
191
|
+
"queries": {
|
|
192
|
+
"type": "array",
|
|
193
|
+
"minItems": 1,
|
|
194
|
+
"maxItems": 2000,
|
|
195
|
+
"items": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"required": ["id", "query", "shouldTrigger"],
|
|
198
|
+
"properties": {
|
|
199
|
+
"id": { "type": "string", "maxLength": 40 },
|
|
200
|
+
"query": { "type": "string", "minLength": 1, "maxLength": 4000 },
|
|
201
|
+
"shouldTrigger": { "type": "boolean" },
|
|
202
|
+
"clause": { "type": ["string", "null"], "maxLength": 300 },
|
|
203
|
+
"note": { "type": ["string", "null"], "maxLength": 600 }
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|