vigiles 4.0.0 → 4.0.2
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/.claude-plugin/plugin.json +1 -1
- package/README.md +29 -18
- package/action.yml +1 -1
- package/dist/adapter-conformance.js +1 -1
- package/dist/adapters/claude-code/adapter.js +1 -1
- package/dist/check.d.ts +132 -0
- package/dist/check.js +318 -0
- package/dist/claude-code.d.ts +1 -1
- package/dist/claude-code.js +1 -1
- package/dist/cli.js +133 -55
- package/dist/core/compile.d.ts +1 -1
- package/dist/core/compile.js +1 -1
- package/dist/core/compose.d.ts +1 -1
- package/dist/core/compose.js +1 -1
- package/dist/core/generate-schema.d.ts +1 -1
- package/dist/core/generate-schema.js +4 -4
- package/dist/core/linters.js +2 -2
- package/dist/core/orphans.js +57 -14
- package/dist/core/proofs.js +1 -1
- package/dist/core/refs.d.ts +1 -1
- package/dist/core/refs.js +2 -2
- package/dist/core/sidecar.d.ts +1 -1
- package/dist/core/sidecar.js +1 -1
- package/dist/core/spec.d.ts +1 -1
- package/dist/core/spec.js +1 -1
- package/dist/core/types.d.ts +1 -1
- package/dist/core/validate.js +2 -2
- package/dist/e2e.d.ts +10 -13
- package/dist/e2e.js +10 -17
- package/dist/{adapters/claude-code/eval-cache.d.ts → eval-cache.d.ts} +1 -1
- package/dist/{adapters/claude-code/eval-cache.js → eval-cache.js} +1 -1
- package/dist/{adapters/claude-code/eval.d.ts → eval.d.ts} +217 -2
- package/dist/{adapters/claude-code/eval.js → eval.js} +430 -20
- package/dist/harness-assert.d.ts +11 -8
- package/dist/harness-assert.js +21 -5
- package/dist/{adapters/claude-code/harness-test.d.ts → harness-test.d.ts} +50 -4
- package/dist/{adapters/claude-code/harness-test.js → harness-test.js} +106 -4
- package/dist/integration.d.ts +11 -3
- package/dist/integration.js +12 -3
- package/dist/jest.d.ts +3 -1
- package/dist/jest.js +3 -2
- package/dist/{adapters/claude-code/mock-model.d.ts → mock-model.d.ts} +2 -2
- package/dist/{adapters/claude-code/run-hook.d.ts → run-hook.d.ts} +23 -1
- package/dist/{adapters/claude-code/run-hook.js → run-hook.js} +29 -1
- package/dist/{adapters/claude-code/sandbox.js → sandbox.js} +1 -1
- package/dist/scan.d.ts +1 -1
- package/dist/scan.js +1 -1
- package/dist/setup-plan.d.ts +5 -1
- package/dist/setup-plan.js +11 -1
- package/dist/test-coverage.js +8 -1
- package/dist/testing.d.ts +10 -3
- package/dist/testing.js +15 -3
- package/dist/unit.d.ts +4 -2
- package/dist/unit.js +8 -2
- package/dist/vitest.d.mts +3 -1
- package/hooks/refs-nudge.sh +1 -1
- package/hooks/session-start.sh +3 -3
- package/package.json +8 -7
- package/skills/edit-spec/SKILL.md +21 -10
- package/skills/linter-docs/SKILL.md +23 -0
- package/skills/migrate-to-spec/SKILL.md +1 -1
- package/skills/strengthen/SKILL.md +1 -2
- package/skills/generate-rule/SKILL.md +0 -64
- /package/dist/{adapters/claude-code/egress-entry.d.ts → egress-entry.d.ts} +0 -0
- /package/dist/{adapters/claude-code/egress-entry.js → egress-entry.js} +0 -0
- /package/dist/{adapters/claude-code/egress-proxy.d.ts → egress-proxy.d.ts} +0 -0
- /package/dist/{adapters/claude-code/egress-proxy.js → egress-proxy.js} +0 -0
- /package/dist/{adapters/claude-code/egress.d.ts → egress.d.ts} +0 -0
- /package/dist/{adapters/claude-code/egress.js → egress.js} +0 -0
- /package/dist/{adapters/claude-code/eval-baseline.d.ts → eval-baseline.d.ts} +0 -0
- /package/dist/{adapters/claude-code/eval-baseline.js → eval-baseline.js} +0 -0
- /package/dist/{adapters/claude-code/judge.d.ts → judge.d.ts} +0 -0
- /package/dist/{adapters/claude-code/judge.js → judge.js} +0 -0
- /package/dist/{adapters/claude-code/mock-entry.d.ts → mock-entry.d.ts} +0 -0
- /package/dist/{adapters/claude-code/mock-entry.js → mock-entry.js} +0 -0
- /package/dist/{adapters/claude-code/mock-model.js → mock-model.js} +0 -0
- /package/dist/{adapters/claude-code/sandbox.d.ts → sandbox.d.ts} +0 -0
- /package/dist/{adapters/claude-code/stats.d.ts → stats.d.ts} +0 -0
- /package/dist/{adapters/claude-code/stats.js → stats.js} +0 -0
package/dist/core/spec.js
CHANGED
|
@@ -93,7 +93,7 @@ function cmd(command) {
|
|
|
93
93
|
* Reference a symbol defined in a file — verified at compile time that the
|
|
94
94
|
* named file exists AND defines the named symbol (via ast-grep, cross-language).
|
|
95
95
|
* Compiles to the file-qualified inline form `` `file#symbol` `` so the markdown
|
|
96
|
-
* `
|
|
96
|
+
* `lint` / `refs-hook` re-verify the same reference.
|
|
97
97
|
*/
|
|
98
98
|
function symbol(file, name) {
|
|
99
99
|
return { _ref: "symbol", file: file, symbol: name };
|
package/dist/core/types.d.ts
CHANGED
|
@@ -97,7 +97,7 @@ export interface RulesConfig {
|
|
|
97
97
|
"untested-surface"?: RuleWithOptions<TestCoverageConfig>;
|
|
98
98
|
/**
|
|
99
99
|
* Nudge (or block) when an instruction file has code-shaped references that
|
|
100
|
-
* aren't expressed as vigiles marks (so the
|
|
100
|
+
* aren't expressed as vigiles marks (so the lint can't verify them), or a
|
|
101
101
|
* `vigiles:symbol` mark that points at a missing symbol. Drives the
|
|
102
102
|
* PostToolUse refs-hook: "warn" (default) → a non-blocking nudge, "error" →
|
|
103
103
|
* block the edit, false → off.
|
package/dist/core/validate.js
CHANGED
|
@@ -157,9 +157,9 @@ function validate(content, { ruleMarkers, rules: rulesConfig, filePath, dialect
|
|
|
157
157
|
const specPath = filePath + ".spec.ts";
|
|
158
158
|
// Inline mode counts as a spec — any parseable
|
|
159
159
|
// `<!-- vigiles:enforce ... -->` comment means the file is
|
|
160
|
-
// verified on `vigiles
|
|
160
|
+
// verified on `vigiles lint` even without a .spec.ts sibling.
|
|
161
161
|
// Delegate to the real parser so a malformed marker can't
|
|
162
|
-
// satisfy require-spec with a rule that
|
|
162
|
+
// satisfy require-spec with a rule that lint can't verify.
|
|
163
163
|
const hasInline = (0, inline_js_1.hasInlineRules)(content) || (0, frontmatter_js_1.hasFrontmatterRules)(content);
|
|
164
164
|
if (!(0, node_fs_1.existsSync)(specPath) && !hasInline) {
|
|
165
165
|
const msg = {
|
package/dist/e2e.d.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `vigiles/e2e` —
|
|
3
|
-
* network, but a definite pass/fail).
|
|
2
|
+
* `vigiles/e2e` — DEPRECATED back-compat alias for [`vigiles/integration`](./integration.ts).
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* There is no separate "e2e" tier: real **egress** is a *capability* of the
|
|
5
|
+
* harness/integration scope (`egressRoutes()` + `runHook`'s `egress: { allow }`),
|
|
6
|
+
* not a different kind of test — the old `e2e` barrel added exactly one symbol
|
|
7
|
+
* over `integration`, which is the definition of a non-tier. It now lives on
|
|
8
|
+
* `vigiles/integration`; this entry re-exports it unchanged so existing imports
|
|
9
|
+
* keep working. See `research/testing-api-design.md` Part 4 (two scopes, not
|
|
10
|
+
* four tiers). Prefer `vigiles/integration`.
|
|
11
11
|
*
|
|
12
|
-
* NOT here: **evals** (`runEval` / `measureTriggerRate` / `judge`)
|
|
13
|
-
*
|
|
14
|
-
* via `vigiles eval` on `*.eval.mjs`. Import them from
|
|
15
|
-
* [`vigiles/eval`](./eval.ts) + [`vigiles/judge`](./judge.ts), not from here.
|
|
12
|
+
* NOT here: **evals** (`runEval` / `measure` / `measureTriggerRate` / `judge`) —
|
|
13
|
+
* those are non-deterministic measurement (`vigiles/eval`), a different axis.
|
|
16
14
|
*/
|
|
17
15
|
export * from "./integration.js";
|
|
18
|
-
export { egressRoutes } from "./adapters/claude-code/run-hook.js";
|
|
19
16
|
//# sourceMappingURL=e2e.d.ts.map
|
package/dist/e2e.js
CHANGED
|
@@ -14,26 +14,19 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.egressRoutes = void 0;
|
|
18
17
|
/**
|
|
19
|
-
* `vigiles/e2e` —
|
|
20
|
-
* network, but a definite pass/fail).
|
|
18
|
+
* `vigiles/e2e` — DEPRECATED back-compat alias for [`vigiles/integration`](./integration.ts).
|
|
21
19
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
20
|
+
* There is no separate "e2e" tier: real **egress** is a *capability* of the
|
|
21
|
+
* harness/integration scope (`egressRoutes()` + `runHook`'s `egress: { allow }`),
|
|
22
|
+
* not a different kind of test — the old `e2e` barrel added exactly one symbol
|
|
23
|
+
* over `integration`, which is the definition of a non-tier. It now lives on
|
|
24
|
+
* `vigiles/integration`; this entry re-exports it unchanged so existing imports
|
|
25
|
+
* keep working. See `research/testing-api-design.md` Part 4 (two scopes, not
|
|
26
|
+
* four tiers). Prefer `vigiles/integration`.
|
|
28
27
|
*
|
|
29
|
-
* NOT here: **evals** (`runEval` / `measureTriggerRate` / `judge`)
|
|
30
|
-
*
|
|
31
|
-
* via `vigiles eval` on `*.eval.mjs`. Import them from
|
|
32
|
-
* [`vigiles/eval`](./eval.ts) + [`vigiles/judge`](./judge.ts), not from here.
|
|
28
|
+
* NOT here: **evals** (`runEval` / `measure` / `measureTriggerRate` / `judge`) —
|
|
29
|
+
* those are non-deterministic measurement (`vigiles/eval`), a different axis.
|
|
33
30
|
*/
|
|
34
31
|
__exportStar(require("./integration.js"), exports);
|
|
35
|
-
// The real-egress capability probe (the egress-using runHook is already re-exported
|
|
36
|
-
// via the integration→unit chain).
|
|
37
|
-
var run_hook_js_1 = require("./adapters/claude-code/run-hook.js");
|
|
38
|
-
Object.defineProperty(exports, "egressRoutes", { enumerable: true, get: function () { return run_hook_js_1.egressRoutes; } });
|
|
39
32
|
//# sourceMappingURL=e2e.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SHA256Hash } from "
|
|
1
|
+
import { type SHA256Hash } from "./core/hash.js";
|
|
2
2
|
import type { RunOut } from "./eval.js";
|
|
3
3
|
/** Cache behaviour: never touch the cache / read-only / read-and-write. */
|
|
4
4
|
export type CacheMode = "off" | "read" | "readwrite";
|
|
@@ -24,7 +24,7 @@ exports.restoreDir = restoreDir;
|
|
|
24
24
|
*/
|
|
25
25
|
const node_fs_1 = require("node:fs");
|
|
26
26
|
const node_path_1 = require("node:path");
|
|
27
|
-
const hash_js_1 = require("
|
|
27
|
+
const hash_js_1 = require("./core/hash.js");
|
|
28
28
|
const MAX_SNAPSHOT_FILE_BYTES = 1024 * 1024;
|
|
29
29
|
const SKIP_DIRS = new Set(["node_modules", ".git"]);
|
|
30
30
|
/**
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type ToolCall, type Trace } from "./harness-test.js";
|
|
2
2
|
import { type CacheMode } from "./eval-cache.js";
|
|
3
|
+
import type { Check, CheckJSON } from "./check.js";
|
|
4
|
+
import { type Comparison } from "./stats.js";
|
|
3
5
|
/** One arm of the comparison: fixture overrides + settings (hooks) for this arm. */
|
|
4
6
|
export interface EvalArm {
|
|
5
7
|
/** Files written on top of the base fixture for this arm. */
|
|
@@ -172,6 +174,127 @@ export type AgentRunner = (args: AgentRunArgs) => Promise<RunOut>;
|
|
|
172
174
|
* {@link runEvalWith} with the real agent runner.
|
|
173
175
|
*/
|
|
174
176
|
export declare function runEval<M extends Metrics>(spec: EvalSpec<M>): Promise<EvalReport>;
|
|
177
|
+
/** A task run N times, scored against a `Trace` check vocabulary. */
|
|
178
|
+
export interface MeasureSpec {
|
|
179
|
+
/** Base fixture files written for every run (path → contents). */
|
|
180
|
+
readonly fixture?: Record<string, string>;
|
|
181
|
+
/** `.claude/settings.json` (hooks/permissions) for the run. */
|
|
182
|
+
readonly settings?: unknown;
|
|
183
|
+
/** A real plugin/repo to load (materialized) — see `EvalArm.plugin`. */
|
|
184
|
+
readonly plugin?: string;
|
|
185
|
+
/** A complete plugin dir to install natively (`--plugin-dir`) so skills activate. */
|
|
186
|
+
readonly pluginDir?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Stub each skill BODY in `pluginDir` (frontmatter/trigger surface kept) before
|
|
189
|
+
* the run — for checks about whether a skill FIRES (`skill()`), not what it
|
|
190
|
+
* produces. A selected skill stops at selection instead of running its (often
|
|
191
|
+
* expensive) procedure, so a description/firing run costs a fraction of the
|
|
192
|
+
* tokens. Do NOT combine with `judged`/quality checks: the body is gone, so
|
|
193
|
+
* there's nothing to grade. Requires `pluginDir`. See {@link stubSkillBody}.
|
|
194
|
+
*/
|
|
195
|
+
readonly stubSkillBodies?: boolean;
|
|
196
|
+
/** The task prompt given to the agent. */
|
|
197
|
+
readonly task: string;
|
|
198
|
+
/**
|
|
199
|
+
* The checks to score across the trials. Any check over the run is accepted:
|
|
200
|
+
* `Trace` checks (`tool`/`skill`/`output`/`mcp`/`judged`) and resource checks
|
|
201
|
+
* (`cost`/`latency`/`tokens`, which read the eval-only `usage`) — all fit
|
|
202
|
+
* `Check<RunContext>`.
|
|
203
|
+
*/
|
|
204
|
+
readonly checks: readonly Check<RunContext>[];
|
|
205
|
+
/** Trials. Default 5. */
|
|
206
|
+
readonly trials?: number;
|
|
207
|
+
/** Model alias. Default "sonnet" — measure on the model your users run. */
|
|
208
|
+
readonly model?: string;
|
|
209
|
+
/** Tools the agent may use. */
|
|
210
|
+
readonly allowedTools?: readonly string[];
|
|
211
|
+
/** Per-run timeout ms. */
|
|
212
|
+
readonly timeoutMs?: number;
|
|
213
|
+
/** Seconds between runs. */
|
|
214
|
+
readonly spacingSec?: number;
|
|
215
|
+
}
|
|
216
|
+
/** One check's measured rate across the trials. */
|
|
217
|
+
export interface CheckRate {
|
|
218
|
+
readonly check: CheckJSON;
|
|
219
|
+
/** Fraction of trials the check passed (0..1). */
|
|
220
|
+
readonly rate: number;
|
|
221
|
+
/** Standard error of the rate. */
|
|
222
|
+
readonly se: number;
|
|
223
|
+
/** pass^k — 1 iff the check passed on EVERY trial. */
|
|
224
|
+
readonly passK: number;
|
|
225
|
+
/** Trials observed. */
|
|
226
|
+
readonly n: number;
|
|
227
|
+
}
|
|
228
|
+
export interface CheckReport {
|
|
229
|
+
readonly n: number;
|
|
230
|
+
readonly perCheck: readonly CheckRate[];
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Score a check vocabulary across trials — the scored counterpart to
|
|
234
|
+
* `assertChecks` (strict). Each check yields a `rate ± se` and `pass^k` over `n`
|
|
235
|
+
* runs. Reuses the tested `runEvalWith` aggregation (one arm), so the loop,
|
|
236
|
+
* cache, concurrency, and stats come for free. Exported with an injectable
|
|
237
|
+
* `runner` so the orchestration is unit-testable without a model.
|
|
238
|
+
*/
|
|
239
|
+
export declare function measureWith(spec: MeasureSpec, runner: AgentRunner): Promise<CheckReport>;
|
|
240
|
+
/** Score a check vocabulary across trials against the real `claude` CLI. */
|
|
241
|
+
export declare function measure(spec: MeasureSpec): Promise<CheckReport>;
|
|
242
|
+
/** A task scored against checks across NAMED arms (the harness variable on/off). */
|
|
243
|
+
export interface ArmsMeasureSpec {
|
|
244
|
+
readonly fixture?: Record<string, string>;
|
|
245
|
+
/** The arms to compare (settings / plugin / pluginDir per arm). */
|
|
246
|
+
readonly arms: Record<string, EvalArm>;
|
|
247
|
+
readonly task: string;
|
|
248
|
+
readonly checks: readonly Check<RunContext>[];
|
|
249
|
+
/**
|
|
250
|
+
* Stub each arm's skill BODIES (frontmatter kept) before the run — the A/B
|
|
251
|
+
* counterpart to {@link MeasureSpec.stubSkillBodies}. For firing comparisons
|
|
252
|
+
* (does description variant A fire more than B?), every arm that sets
|
|
253
|
+
* `pluginDir` is repackaged with bodies stripped so each run stops at
|
|
254
|
+
* selection — a fraction of the tokens. Arms without a `pluginDir` are left
|
|
255
|
+
* untouched. Don't combine with `judged`/quality checks. See {@link stubSkillBody}.
|
|
256
|
+
*/
|
|
257
|
+
readonly stubSkillBodies?: boolean;
|
|
258
|
+
readonly trials?: number;
|
|
259
|
+
readonly model?: string;
|
|
260
|
+
readonly allowedTools?: readonly string[];
|
|
261
|
+
readonly timeoutMs?: number;
|
|
262
|
+
readonly spacingSec?: number;
|
|
263
|
+
}
|
|
264
|
+
/** Per-arm {@link CheckReport}s — `arms[name].perCheck[i]` aligns across arms. */
|
|
265
|
+
export interface ArmsCheckReport {
|
|
266
|
+
readonly arms: Record<string, CheckReport>;
|
|
267
|
+
}
|
|
268
|
+
/** Score checks across arms (injectable runner). Reuses `runEvalWith`. */
|
|
269
|
+
export declare function measureArmsWith(spec: ArmsMeasureSpec, runner: AgentRunner): Promise<ArmsCheckReport>;
|
|
270
|
+
/** Score checks across arms against the real `claude` CLI. */
|
|
271
|
+
export declare function measureArms(spec: ArmsMeasureSpec): Promise<ArmsCheckReport>;
|
|
272
|
+
/**
|
|
273
|
+
* Welch significance on one check's rate between two arms (`arm` vs `baseline`),
|
|
274
|
+
* by index in `perCheck`. So "the gated arm resolves the skill significantly more
|
|
275
|
+
* than vanilla" is a p-value, not a vibe. Reuses `welchTTest` from stats.ts.
|
|
276
|
+
*/
|
|
277
|
+
export declare function compareCheck(report: ArmsCheckReport, baseline: string, arm: string, checkIndex: number): Comparison;
|
|
278
|
+
/** Format a {@link CheckReport}: one line per check with its rate ± se and pass^k. */
|
|
279
|
+
export declare function formatCheckReport(report: CheckReport): string;
|
|
280
|
+
/**
|
|
281
|
+
* The scored gate (Phase 4): throw if any check's measured rate is below `min` —
|
|
282
|
+
* the `measure` counterpart to `assertChecks` (strict). Reads the rate, not a
|
|
283
|
+
* single run, so it never trips on one noisy trial.
|
|
284
|
+
*/
|
|
285
|
+
export declare function assertRates(report: CheckReport, opts: {
|
|
286
|
+
min: number;
|
|
287
|
+
}): void;
|
|
288
|
+
/**
|
|
289
|
+
* Serialize a {@link CheckReport} to JUnit XML (Phase 4) — each check a
|
|
290
|
+
* `<testcase>`, failing when its rate is below `min`. Because a check is *data*,
|
|
291
|
+
* this falls out for free: CI test reporters, regression baselines, and a
|
|
292
|
+
* promptfoo bridge all consume the same shape.
|
|
293
|
+
*/
|
|
294
|
+
export declare function checkReportToJUnit(report: CheckReport, opts?: {
|
|
295
|
+
min?: number;
|
|
296
|
+
name?: string;
|
|
297
|
+
}): string;
|
|
175
298
|
/** Parse per-run cost/latency/tokens from a stream — pure, model-free. */
|
|
176
299
|
export declare function parseUsage(stdout: string): EvalUsage;
|
|
177
300
|
/** Aggregate per-run metrics: mean for numbers, fraction-true (0..1) for booleans. */
|
|
@@ -209,8 +332,19 @@ export declare function formatEvalReport(report: EvalReport): string;
|
|
|
209
332
|
* (reuse the bare predicates, e.g. `(t) => skillResolved(t, "x:y")`).
|
|
210
333
|
*/
|
|
211
334
|
export interface TriggerRateSpec {
|
|
212
|
-
/**
|
|
213
|
-
|
|
335
|
+
/**
|
|
336
|
+
* Plugin dir installed natively (`--plugin-dir`) so its skills/commands
|
|
337
|
+
* activate. Provide this OR {@link skillsDir}, not both.
|
|
338
|
+
*/
|
|
339
|
+
readonly pluginDir?: string;
|
|
340
|
+
/**
|
|
341
|
+
* A directory of LOOSE skills (`<skillsDir>/<name>/SKILL.md`, e.g. a repo's
|
|
342
|
+
* `.claude/skills`) to trigger-test directly. vigiles packages them into a
|
|
343
|
+
* throwaway `--plugin-dir` for you and removes it afterward — the one-liner
|
|
344
|
+
* for repo-local skills that aren't a published plugin. Provide this OR
|
|
345
|
+
* {@link pluginDir}, not both.
|
|
346
|
+
*/
|
|
347
|
+
readonly skillsDir?: string;
|
|
214
348
|
/** The varied prompts to test the trigger against. */
|
|
215
349
|
readonly prompts: readonly string[];
|
|
216
350
|
/**
|
|
@@ -223,6 +357,30 @@ export interface TriggerRateSpec {
|
|
|
223
357
|
readonly irrelevantPrompts?: readonly string[];
|
|
224
358
|
/** Did the behaviour fire on this run? e.g. `(t) => skillResolved(t, "x:y")`. */
|
|
225
359
|
readonly fired: (trace: Trace) => boolean;
|
|
360
|
+
/**
|
|
361
|
+
* Replace each skill's BODY with a no-op stub (keeping its frontmatter — name +
|
|
362
|
+
* description) before running. Trigger-rate is decided by the frontmatter alone
|
|
363
|
+
* (the model selects a skill before its body loads), so stubbing the body can't
|
|
364
|
+
* change what's measured but stops the run from executing an expensive
|
|
365
|
+
* procedure once the skill fires — far cheaper, faster, side-effect-free. All
|
|
366
|
+
* skills' descriptions stay present, so the selection competition is faithful.
|
|
367
|
+
* Default false (off) for now; recommended `true` for trigger evals. See
|
|
368
|
+
* {@link stubSkillBody}.
|
|
369
|
+
*/
|
|
370
|
+
readonly stubSkillBodies?: boolean;
|
|
371
|
+
/**
|
|
372
|
+
* Minimum number of prompts each set (relevant + irrelevant) must have. A
|
|
373
|
+
* handful of prompts can't tell a real recall/precision rate from noise, so
|
|
374
|
+
* the run is rejected before it spends a token. Default 10; lower it
|
|
375
|
+
* deliberately for a genuinely narrow skill.
|
|
376
|
+
*/
|
|
377
|
+
readonly minPrompts?: number;
|
|
378
|
+
/**
|
|
379
|
+
* Reject the run when two prompts in a set are closer than this in NCD
|
|
380
|
+
* (gzip-based distance, 0..1; 0 = identical) — near-duplicate prompts inflate
|
|
381
|
+
* a rate without testing varied phrasings. Default 0.3 (the rule-dup threshold).
|
|
382
|
+
*/
|
|
383
|
+
readonly minDistance?: number;
|
|
226
384
|
/** Trials per prompt. Default 1. */
|
|
227
385
|
readonly trials?: number;
|
|
228
386
|
/** Model alias. Default "haiku". */
|
|
@@ -263,6 +421,63 @@ export interface TriggerRateReport {
|
|
|
263
421
|
/** Per-prompt stats for the irrelevant set. Present with irrelevant prompts. */
|
|
264
422
|
readonly perIrrelevant?: readonly PromptTriggerStat[];
|
|
265
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Package loose `<skillsDir>/<name>/SKILL.md` skills into a throwaway plugin dir
|
|
426
|
+
* that `claude --plugin-dir` accepts — so repo-local skills (e.g. `.claude/skills`)
|
|
427
|
+
* can be trigger-tested without hand-rolling a `plugin.json`. Writes a minimal
|
|
428
|
+
* `.claude-plugin/plugin.json` and copies each `<name>/` (recursively, so
|
|
429
|
+
* `references/` etc. come along) under `skills/<name>/`. Returns the temp plugin
|
|
430
|
+
* dir; the caller removes it (`measureTriggerRate` does). Throws if the directory
|
|
431
|
+
* is missing or holds no `<name>/SKILL.md`.
|
|
432
|
+
*/
|
|
433
|
+
export declare function packageSkillsDir(skillsDir: string, opts?: {
|
|
434
|
+
name?: string;
|
|
435
|
+
stub?: boolean;
|
|
436
|
+
}): string;
|
|
437
|
+
/**
|
|
438
|
+
* Rewrite a SKILL.md to keep its YAML frontmatter (the trigger surface — name +
|
|
439
|
+
* description) but replace the body with a no-op stub. Trigger-rate is a property
|
|
440
|
+
* of the frontmatter ONLY: the model picks a skill from its name + description
|
|
441
|
+
* before the body is ever loaded, so the body is causally downstream of selection
|
|
442
|
+
* and irrelevant to whether the skill fires. Stubbing it lets a trigger run stop
|
|
443
|
+
* AT selection instead of executing an expensive multi-step procedure — cheaper,
|
|
444
|
+
* faster, and side-effect-free, without changing what's measured. Pure.
|
|
445
|
+
*/
|
|
446
|
+
export declare function stubSkillBody(skillMd: string): string;
|
|
447
|
+
/**
|
|
448
|
+
* Build a throwaway plugin dir mirroring `pluginDir`'s skills with their BODIES
|
|
449
|
+
* stripped (frontmatter kept) — the trigger surface a description/firing check
|
|
450
|
+
* needs, without paying to run each skill's procedure. Keeps the original plugin
|
|
451
|
+
* NAME so `<name>:<skill>` ids still match. The caller removes the returned dir.
|
|
452
|
+
* See {@link stubSkillBody} for why the body is irrelevant to selection.
|
|
453
|
+
*/
|
|
454
|
+
export declare function stubbedPluginDir(pluginDir: string): string;
|
|
455
|
+
/**
|
|
456
|
+
* Distance between two prompts in ~0..1 (0 = identical, higher = more
|
|
457
|
+
* different) via Normalized Compression Distance over the normalized text.
|
|
458
|
+
* Reuses {@link ncd} from the proof engine.
|
|
459
|
+
*/
|
|
460
|
+
export declare function promptDistance(a: string, b: string): number;
|
|
461
|
+
export interface PromptDiversityIssue {
|
|
462
|
+
readonly kind: "too-few" | "too-similar";
|
|
463
|
+
readonly message: string;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Deterministically check a prompt set is big and varied enough to measure a
|
|
467
|
+
* trigger rate: at least `minPrompts` entries, and no two closer than
|
|
468
|
+
* `minDistance` in NCD. Pure — no model. `label` names the set in messages.
|
|
469
|
+
*/
|
|
470
|
+
export declare function checkPromptDiversity(prompts: readonly string[], opts?: {
|
|
471
|
+
minPrompts?: number;
|
|
472
|
+
minDistance?: number;
|
|
473
|
+
label?: string;
|
|
474
|
+
}): PromptDiversityIssue[];
|
|
475
|
+
/** Throw if a prompt set isn't big/varied enough. See {@link checkPromptDiversity}. */
|
|
476
|
+
export declare function assertPromptDiversity(prompts: readonly string[], opts?: {
|
|
477
|
+
minPrompts?: number;
|
|
478
|
+
minDistance?: number;
|
|
479
|
+
label?: string;
|
|
480
|
+
}): void;
|
|
266
481
|
/**
|
|
267
482
|
* Trigger-rate orchestration — every prompt × trial via `runner`, the `fired`
|
|
268
483
|
* predicate evaluated per run and aggregated into an overall + per-prompt rate.
|