vigiles 5.2.0 → 7.0.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.
Files changed (78) hide show
  1. package/README.md +99 -48
  2. package/dist/action-gate.js +1 -1
  3. package/dist/adapters/claude-code/agent-runtime.d.ts +64 -4
  4. package/dist/adapters/claude-code/agent-runtime.js +131 -17
  5. package/dist/adapters/claude-code/dialect.d.ts +34 -0
  6. package/dist/adapters/claude-code/dialect.js +46 -33
  7. package/dist/adapters/claude-code/effect-region.js +1 -1
  8. package/dist/adapters/claude-code/skill-runtime.d.ts +1 -1
  9. package/dist/adapters/claude-code/skill-runtime.js +1 -9
  10. package/dist/adapters/claude-code/typed-spec.d.ts +58 -0
  11. package/dist/adapters/claude-code/typed-spec.js +55 -0
  12. package/dist/adapters/codex/hook-protocol.js +3 -0
  13. package/dist/adapters/codex/mock-model.js +1 -1
  14. package/dist/claude-code.d.ts +1 -0
  15. package/dist/claude-code.js +8 -1
  16. package/dist/cli-commands.d.ts +19 -0
  17. package/dist/cli-commands.js +51 -0
  18. package/dist/cli.js +735 -76
  19. package/dist/core/bash-effects.d.ts +12 -0
  20. package/dist/core/bash-effects.js +31 -0
  21. package/dist/core/capability-diff.d.ts +46 -0
  22. package/dist/core/capability-diff.js +97 -0
  23. package/dist/core/compile.d.ts +1 -1
  24. package/dist/core/compile.js +14 -0
  25. package/dist/core/generate-harness.d.ts +187 -0
  26. package/dist/core/generate-harness.js +337 -0
  27. package/dist/core/guards.d.ts +126 -0
  28. package/dist/core/guards.js +309 -0
  29. package/dist/core/harness-driver.d.ts +1 -1
  30. package/dist/core/hook-program.d.ts +459 -0
  31. package/dist/core/hook-program.js +468 -0
  32. package/dist/core/hook-protocol.d.ts +7 -0
  33. package/dist/core/hook-providers.d.ts +138 -0
  34. package/dist/core/hook-providers.js +155 -0
  35. package/dist/core/hook-spec.d.ts +74 -0
  36. package/dist/core/hook-spec.js +130 -0
  37. package/dist/core/inline.js +1 -1
  38. package/dist/core/mcp-tool.d.ts +12 -0
  39. package/dist/core/mcp-tool.js +20 -0
  40. package/dist/core/mcp.d.ts +13 -0
  41. package/dist/core/mcp.js +67 -0
  42. package/dist/core/spec.d.ts +290 -8
  43. package/dist/core/spec.js +118 -3
  44. package/dist/core/types.d.ts +8 -0
  45. package/dist/dialect-drift.d.ts +65 -0
  46. package/dist/dialect-drift.js +216 -0
  47. package/dist/eval.d.ts +40 -5
  48. package/dist/eval.js +59 -5
  49. package/dist/guardrail-check.d.ts +85 -0
  50. package/dist/guardrail-check.js +152 -0
  51. package/dist/harness-assert.d.ts +10 -0
  52. package/dist/harness-assert.js +30 -0
  53. package/dist/hook-install.d.ts +43 -0
  54. package/dist/hook-install.js +91 -0
  55. package/dist/hook.d.ts +52 -0
  56. package/dist/hook.js +98 -0
  57. package/dist/leaderboard.d.ts +6 -0
  58. package/dist/leaderboard.js +43 -1
  59. package/dist/linting.d.ts +9 -5
  60. package/dist/linting.js +17 -5
  61. package/dist/optimize.js +1 -1
  62. package/dist/scaffold-test.d.ts +28 -0
  63. package/dist/scaffold-test.js +134 -15
  64. package/dist/scan-behavioral.d.ts +60 -0
  65. package/dist/scan-behavioral.js +239 -1
  66. package/dist/scan.d.ts +14 -0
  67. package/dist/scan.js +33 -1
  68. package/dist/score-explainer.js +1 -1
  69. package/dist/self-command-refs.d.ts +21 -0
  70. package/dist/self-command-refs.js +125 -0
  71. package/dist/testing.d.ts +5 -3
  72. package/dist/testing.js +37 -23
  73. package/dist/tool-intercept.d.ts +4 -4
  74. package/dist/tool-intercept.js +5 -5
  75. package/dist/unit.d.ts +2 -0
  76. package/dist/unit.js +8 -1
  77. package/hooks/refs-nudge.sh +1 -1
  78. package/package.json +5 -3
@@ -8,6 +8,8 @@ exports.assertNotCreated = assertNotCreated;
8
8
  exports.assertServedTurns = assertServedTurns;
9
9
  exports.assertHookBlocked = assertHookBlocked;
10
10
  exports.assertHookAllowed = assertHookAllowed;
11
+ exports.assertHookDenies = assertHookDenies;
12
+ exports.assertHookAllows = assertHookAllows;
11
13
  exports.egressHosts = egressHosts;
12
14
  exports.assertNoEgress = assertNoEgress;
13
15
  exports.assertEgressOnly = assertEgressOnly;
@@ -58,6 +60,7 @@ exports.assertTriggerRate = assertTriggerRate;
58
60
  * vitest and jest, so the same object supports both.
59
61
  */
60
62
  const harness_test_js_1 = require("./harness-test.js");
63
+ const hook_program_js_1 = require("./core/hook-program.js");
61
64
  const check_js_1 = require("./check.js");
62
65
  const agent_result_js_1 = require("./adapters/claude-code/agent-result.js");
63
66
  const stats_js_1 = require("./stats.js");
@@ -137,6 +140,33 @@ function assertHookAllowed(r) {
137
140
  fail(`expected the hook to allow, but it blocked (exit ${String(r.exitCode)}, decision ${String(r.decision)})`);
138
141
  }
139
142
  }
143
+ /** Render a {@link HookProgramOutcome} for an assertion message. */
144
+ function describeOutcome(o) {
145
+ if (o.kind === "decision")
146
+ return `${o.decision.kind} (a gate decision)`;
147
+ if (o.kind === "injection")
148
+ return `an injection`;
149
+ return `${o.reaction.kind} (a reaction)`;
150
+ }
151
+ /**
152
+ * Assert a COMPILED hook (a `vigiles/hook` program) denies an event — evaluated
153
+ * in-process, no subprocess, no model. The cheapest way to test a gate's logic:
154
+ * pass the hook's default export and a raw event. (For the wired-into-the-real-CLI
155
+ * check, use {@link assertHookBlocked} over `runHook`.)
156
+ */
157
+ function assertHookDenies(hook, event) {
158
+ const o = (0, hook_program_js_1.runHookProgram)(hook, event);
159
+ if (o.kind !== "decision" || o.decision.kind !== "deny") {
160
+ fail(`expected the hook to deny, got ${describeOutcome(o)}`);
161
+ }
162
+ }
163
+ /** Assert a COMPILED hook allows an event (in-process). The twin of {@link assertHookDenies}. */
164
+ function assertHookAllows(hook, event) {
165
+ const o = (0, hook_program_js_1.runHookProgram)(hook, event);
166
+ if (o.kind !== "decision" || o.decision.kind !== "allow") {
167
+ fail(`expected the hook to allow, got ${describeOutcome(o)}`);
168
+ }
169
+ }
140
170
  const hostPort = (e) => `${e.host}:${String(e.port)}`;
141
171
  /** The `host:port` strings a run attempted, e.g. `["registry.npmjs.org:443"]`. */
142
172
  function egressHosts(r) {
@@ -0,0 +1,43 @@
1
+ /** The agnostic, committed home for hook SOURCE — one dir, cross-adapter. */
2
+ export declare const HOOKS_DIR = ".vigiles/hooks";
3
+ /** The committed home for registered context-provider SOURCE (v2). */
4
+ export declare const PROVIDERS_DIR = ".vigiles/providers";
5
+ /** Discover hook source files under {@link HOOKS_DIR} (stamps excluded). */
6
+ export declare function discoverHookFiles(cwd: string): string[];
7
+ /** Discover registered-provider source files under {@link PROVIDERS_DIR}. */
8
+ export declare function discoverProviderFiles(cwd: string): string[];
9
+ interface CommandHook {
10
+ readonly type: "command";
11
+ readonly command: string;
12
+ }
13
+ interface HookEntry {
14
+ readonly matcher?: string;
15
+ readonly hooks: readonly CommandHook[];
16
+ }
17
+ /** The CC-shaped structured block a compiled hook program carries. */
18
+ export type CompiledHooks = Record<string, readonly HookEntry[]>;
19
+ interface SettingsJson {
20
+ hooks?: Record<string, HookEntry[]>;
21
+ [k: string]: unknown;
22
+ }
23
+ /**
24
+ * Idempotently merge a compiled hook's block into an existing `settings.json`
25
+ * object. Entries managed by THIS hook file (the runtime command references
26
+ * `hookPath`) are replaced; every unrelated entry — including the user's own
27
+ * hand-written hooks — is preserved.
28
+ */
29
+ export declare function mergeHooksJson(existing: SettingsJson, compiled: CompiledHooks, hookPath: string): SettingsJson;
30
+ interface TomlHookEntry {
31
+ matcher?: string;
32
+ command: string;
33
+ }
34
+ interface ConfigToml {
35
+ hooks?: Record<string, TomlHookEntry[]>;
36
+ [k: string]: unknown;
37
+ }
38
+ /** The TOML sibling of {@link mergeHooksJson} (Codex `[[hooks.<event>]]`). */
39
+ export declare function mergeHooksToml(existing: ConfigToml, compiled: CompiledHooks, hookPath: string): ConfigToml;
40
+ /** Serialize a merged config back to its on-disk text (with trailing newline). */
41
+ export declare function serializeConfig(merged: Record<string, unknown>, format: "json" | "toml"): string;
42
+ export {};
43
+ //# sourceMappingURL=hook-install.d.ts.map
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROVIDERS_DIR = exports.HOOKS_DIR = void 0;
4
+ exports.discoverHookFiles = discoverHookFiles;
5
+ exports.discoverProviderFiles = discoverProviderFiles;
6
+ exports.mergeHooksJson = mergeHooksJson;
7
+ exports.mergeHooksToml = mergeHooksToml;
8
+ exports.serializeConfig = serializeConfig;
9
+ /**
10
+ * Hook installation — the bridge from a typed hook program to a wired harness,
11
+ * folded into `vigiles compile` (there is no stray `compile-hook` verb; the
12
+ * cohesive-cli-surface rule).
13
+ *
14
+ * The typed hook program is harness-NEUTRAL — it imports `vigiles/hook` and
15
+ * compiles to whatever harness — so its SOURCE lives in the agnostic,
16
+ * committed {@link HOOKS_DIR} (`.vigiles/hooks/`), never in a harness's own
17
+ * `.claude/`. `compile` discovers each hook there, compiles it, and MERGES the
18
+ * result into the active harness's native config (`.claude/settings.json` JSON
19
+ * / `config.toml` TOML) — so the harness is actually wired, not handed a
20
+ * paste-this block. The merge is idempotent: an entry is keyed by the runtime
21
+ * command's hook PATH, so recompiling updates in place and never duplicates,
22
+ * while a user's own hand-written hooks are preserved untouched. One source dir
23
+ * also means basenames are unique, so the stamp can key on the basename safely.
24
+ */
25
+ const node_fs_1 = require("node:fs");
26
+ const node_path_1 = require("node:path");
27
+ const toml_1 = require("@iarna/toml");
28
+ /** The agnostic, committed home for hook SOURCE — one dir, cross-adapter. */
29
+ exports.HOOKS_DIR = ".vigiles/hooks";
30
+ /** The committed home for registered context-provider SOURCE (v2). */
31
+ exports.PROVIDERS_DIR = ".vigiles/providers";
32
+ /** A JS/TS hook source file (the `.json` stamp sidecar is never matched). */
33
+ const HOOK_SOURCE_RE = /\.(?:mjs|cjs|js|mts|cts|ts)$/;
34
+ /** List JS/TS source files under `dir` (relative to cwd), stamps excluded. */
35
+ function discoverSources(cwd, dir) {
36
+ const abs = (0, node_path_1.join)(cwd, dir);
37
+ if (!(0, node_fs_1.existsSync)(abs))
38
+ return [];
39
+ return (0, node_fs_1.readdirSync)(abs)
40
+ .filter((f) => HOOK_SOURCE_RE.test(f) && !f.endsWith(".d.ts"))
41
+ .sort()
42
+ .map((f) => (0, node_path_1.join)(dir, f));
43
+ }
44
+ /** Discover hook source files under {@link HOOKS_DIR} (stamps excluded). */
45
+ function discoverHookFiles(cwd) {
46
+ return discoverSources(cwd, exports.HOOKS_DIR);
47
+ }
48
+ /** Discover registered-provider source files under {@link PROVIDERS_DIR}. */
49
+ function discoverProviderFiles(cwd) {
50
+ return discoverSources(cwd, exports.PROVIDERS_DIR);
51
+ }
52
+ /** True when an entry's command routes through the runtime for `hookPath`. */
53
+ function managesHook(entry, hookPath) {
54
+ return entry.hooks.some((h) => h.command.includes(hookPath));
55
+ }
56
+ /**
57
+ * Idempotently merge a compiled hook's block into an existing `settings.json`
58
+ * object. Entries managed by THIS hook file (the runtime command references
59
+ * `hookPath`) are replaced; every unrelated entry — including the user's own
60
+ * hand-written hooks — is preserved.
61
+ */
62
+ function mergeHooksJson(existing, compiled, hookPath) {
63
+ const hooks = { ...(existing.hooks ?? {}) };
64
+ for (const [event, entries] of Object.entries(compiled)) {
65
+ const kept = (hooks[event] ?? []).filter((e) => !managesHook(e, hookPath));
66
+ hooks[event] = [...kept, ...entries];
67
+ }
68
+ return { ...existing, hooks };
69
+ }
70
+ /** Flatten a CC-shaped entry to Codex's flat `{matcher?, command}` form. */
71
+ function toTomlEntries(entries) {
72
+ return entries.flatMap((e) => e.hooks.map((h) => e.matcher === undefined
73
+ ? { command: h.command }
74
+ : { matcher: e.matcher, command: h.command }));
75
+ }
76
+ /** The TOML sibling of {@link mergeHooksJson} (Codex `[[hooks.<event>]]`). */
77
+ function mergeHooksToml(existing, compiled, hookPath) {
78
+ const hooks = { ...(existing.hooks ?? {}) };
79
+ for (const [event, entries] of Object.entries(compiled)) {
80
+ const kept = (hooks[event] ?? []).filter((e) => !e.command.includes(hookPath));
81
+ hooks[event] = [...kept, ...toTomlEntries(entries)];
82
+ }
83
+ return { ...existing, hooks };
84
+ }
85
+ /** Serialize a merged config back to its on-disk text (with trailing newline). */
86
+ function serializeConfig(merged, format) {
87
+ return format === "toml"
88
+ ? (0, toml_1.stringify)(merged).trimEnd() + "\n"
89
+ : JSON.stringify(merged, null, 2) + "\n";
90
+ }
91
+ //# sourceMappingURL=hook-install.js.map
package/dist/hook.d.ts ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `vigiles/hook` — the **closed vocabulary** for authoring a compiled hook.
3
+ *
4
+ * A hook today is opaque shell (`bash guard.sh`): un-analyzable, and the author
5
+ * hand-writes the fragile parts (exit code, JSON field, a `grep` matcher) that
6
+ * cause the #1 verified hook pain — FALSE CONFIDENCE (a guardrail that looks
7
+ * like it blocks but silently doesn't). Invert it: author a **pure typed
8
+ * function** `(event) => Decision` against THIS surface, and `vigiles
9
+ * compile` emits the harness protocol for you. The whole false-confidence
10
+ * class becomes UNREPRESENTABLE — you never write the exit code or the field.
11
+ *
12
+ * The roles, each with its own output type so a category mistake is a `tsc`
13
+ * error, not a silent no-op:
14
+ * - `defineHook` / `defineFileGate` — a **gate** returns a `Decision`
15
+ * (`allow`/`deny`/`ask`); `deny` is the only thing that blocks.
16
+ * - `definePromptGate` — a **prompt gate** (UserPromptSubmit) sees the prompt
17
+ * TEXT and may `deny` to block it (a security filter).
18
+ * - `defineStopGate` — a **stop gate** (Stop/SubagentStop) may `deny` to keep
19
+ * the agent going (gate-until-tests-pass).
20
+ * - `defineInject` — an **inject** returns an `Injection` (context text); it
21
+ * has no `deny`, so "block on a SessionStart hook" won't compile.
22
+ * - `defineReact` — a **react** (PostToolUse) returns a `Reaction`; it sees the
23
+ * tool RESPONSE, its `run(cmd)` is effect-classified at construction, and it
24
+ * can't block (the tool already ran).
25
+ *
26
+ * Every gate takes a `mode`: `enforce` (default, blocks) or `observe` (the
27
+ * shadow/rollout mode — records what it WOULD block, never blocks). `observe` is
28
+ * harness-neutral (it just exits 0 + writes a local record).
29
+ *
30
+ * Matching is AST-backed (`command.runs("git push", { force })`), so it catches
31
+ * `cd x && git push -f` that the native `Bash(git:*)` glob misses.
32
+ *
33
+ * A gate may also decide on EXTERNAL STATE by declaring `needs` (e.g.
34
+ * `needs: ["git.branch"]`): the trusted runtime gathers those read-only facts and
35
+ * hands them in as `e.ctx` — the hook still does zero I/O, and reading an
36
+ * undeclared fact is a `tsc` error. Built-ins:
37
+ * `git.branch`/`git.isDirty`/`git.root`/`cwd`/`os.platform`/`env.isCI`. For a
38
+ * one-off off-catalog fact, the lightweight opt-out is an
39
+ * inline `provide(name, cmd)` (read-only) or `dangerously(name, cmd)` (the loud
40
+ * escape) right in `needs`. See `research/hook-context-providers.md`.
41
+ *
42
+ * ⚠️ Honest scope: compile/verify fix the hook's AUTHORING + LOGIC. They do NOT
43
+ * change DELIVERY — Claude Code's own subagent-bypass (#34692) means a
44
+ * PreToolUse hook (compiled or hand-written) does not fire for a subagent's
45
+ * tool calls. A gate is a strong default, never an unbypassable wall. See
46
+ * `docs/compiled-hooks.md`.
47
+ */
48
+ export { defineHook, defineFileGate, definePromptGate, defineStopGate, tool, tools, allow, deny, ask, commandView, pathView, gateAction, hookMode, defineInject, inject, defineReact, run, notice, nothing, responseView, decideProgram, decideFileGate, decidePromptGate, decideStopGate, runInject, runReact, runHookProgram, decisionExitCode, dispatchKind, hookRouting, hookNeeds, compileHookProgram, checkHookImports, stampHook, verifyHookStamp, HookCompileError, } from "./core/hook-program.js";
49
+ export type { Decision, HookMode, GateAction, CommandView, PathView, ResponseView, BashToolEvent, FileToolEvent, PromptEvent, StopEvent, ReactEvent, SessionEvent, HookProgram, FileGateHook, PromptGateHook, StopGateHook, InjectHook, ReactHook, AnyHook, DispatchKind, Injection, Reaction, RunReaction, CompiledHookProgram, CompileHookOptions, RawHookEvent, HookProgramOutcome, } from "./core/hook-program.js";
50
+ export { provide, dangerously, defineProvider, provider, } from "./core/hook-providers.js";
51
+ export type { ProviderName, ProviderResults, HookCtx, NeedSpec, InlineProvider, RegisteredProvider, RegisteredRef, ProviderRegistry, } from "./core/hook-providers.js";
52
+ //# sourceMappingURL=hook.d.ts.map
package/dist/hook.js ADDED
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.provider = exports.defineProvider = exports.dangerously = exports.provide = exports.HookCompileError = exports.verifyHookStamp = exports.stampHook = exports.checkHookImports = exports.compileHookProgram = exports.hookNeeds = exports.hookRouting = exports.dispatchKind = exports.decisionExitCode = exports.runHookProgram = exports.runReact = exports.runInject = exports.decideStopGate = exports.decidePromptGate = exports.decideFileGate = exports.decideProgram = exports.responseView = exports.nothing = exports.notice = exports.run = exports.defineReact = exports.inject = exports.defineInject = exports.hookMode = exports.gateAction = exports.pathView = exports.commandView = exports.ask = exports.deny = exports.allow = exports.tools = exports.tool = exports.defineStopGate = exports.definePromptGate = exports.defineFileGate = exports.defineHook = void 0;
4
+ /**
5
+ * `vigiles/hook` — the **closed vocabulary** for authoring a compiled hook.
6
+ *
7
+ * A hook today is opaque shell (`bash guard.sh`): un-analyzable, and the author
8
+ * hand-writes the fragile parts (exit code, JSON field, a `grep` matcher) that
9
+ * cause the #1 verified hook pain — FALSE CONFIDENCE (a guardrail that looks
10
+ * like it blocks but silently doesn't). Invert it: author a **pure typed
11
+ * function** `(event) => Decision` against THIS surface, and `vigiles
12
+ * compile` emits the harness protocol for you. The whole false-confidence
13
+ * class becomes UNREPRESENTABLE — you never write the exit code or the field.
14
+ *
15
+ * The roles, each with its own output type so a category mistake is a `tsc`
16
+ * error, not a silent no-op:
17
+ * - `defineHook` / `defineFileGate` — a **gate** returns a `Decision`
18
+ * (`allow`/`deny`/`ask`); `deny` is the only thing that blocks.
19
+ * - `definePromptGate` — a **prompt gate** (UserPromptSubmit) sees the prompt
20
+ * TEXT and may `deny` to block it (a security filter).
21
+ * - `defineStopGate` — a **stop gate** (Stop/SubagentStop) may `deny` to keep
22
+ * the agent going (gate-until-tests-pass).
23
+ * - `defineInject` — an **inject** returns an `Injection` (context text); it
24
+ * has no `deny`, so "block on a SessionStart hook" won't compile.
25
+ * - `defineReact` — a **react** (PostToolUse) returns a `Reaction`; it sees the
26
+ * tool RESPONSE, its `run(cmd)` is effect-classified at construction, and it
27
+ * can't block (the tool already ran).
28
+ *
29
+ * Every gate takes a `mode`: `enforce` (default, blocks) or `observe` (the
30
+ * shadow/rollout mode — records what it WOULD block, never blocks). `observe` is
31
+ * harness-neutral (it just exits 0 + writes a local record).
32
+ *
33
+ * Matching is AST-backed (`command.runs("git push", { force })`), so it catches
34
+ * `cd x && git push -f` that the native `Bash(git:*)` glob misses.
35
+ *
36
+ * A gate may also decide on EXTERNAL STATE by declaring `needs` (e.g.
37
+ * `needs: ["git.branch"]`): the trusted runtime gathers those read-only facts and
38
+ * hands them in as `e.ctx` — the hook still does zero I/O, and reading an
39
+ * undeclared fact is a `tsc` error. Built-ins:
40
+ * `git.branch`/`git.isDirty`/`git.root`/`cwd`/`os.platform`/`env.isCI`. For a
41
+ * one-off off-catalog fact, the lightweight opt-out is an
42
+ * inline `provide(name, cmd)` (read-only) or `dangerously(name, cmd)` (the loud
43
+ * escape) right in `needs`. See `research/hook-context-providers.md`.
44
+ *
45
+ * ⚠️ Honest scope: compile/verify fix the hook's AUTHORING + LOGIC. They do NOT
46
+ * change DELIVERY — Claude Code's own subagent-bypass (#34692) means a
47
+ * PreToolUse hook (compiled or hand-written) does not fire for a subagent's
48
+ * tool calls. A gate is a strong default, never an unbypassable wall. See
49
+ * `docs/compiled-hooks.md`.
50
+ */
51
+ var hook_program_js_1 = require("./core/hook-program.js");
52
+ // gate vocabulary
53
+ Object.defineProperty(exports, "defineHook", { enumerable: true, get: function () { return hook_program_js_1.defineHook; } });
54
+ Object.defineProperty(exports, "defineFileGate", { enumerable: true, get: function () { return hook_program_js_1.defineFileGate; } });
55
+ Object.defineProperty(exports, "definePromptGate", { enumerable: true, get: function () { return hook_program_js_1.definePromptGate; } });
56
+ Object.defineProperty(exports, "defineStopGate", { enumerable: true, get: function () { return hook_program_js_1.defineStopGate; } });
57
+ Object.defineProperty(exports, "tool", { enumerable: true, get: function () { return hook_program_js_1.tool; } });
58
+ Object.defineProperty(exports, "tools", { enumerable: true, get: function () { return hook_program_js_1.tools; } });
59
+ Object.defineProperty(exports, "allow", { enumerable: true, get: function () { return hook_program_js_1.allow; } });
60
+ Object.defineProperty(exports, "deny", { enumerable: true, get: function () { return hook_program_js_1.deny; } });
61
+ Object.defineProperty(exports, "ask", { enumerable: true, get: function () { return hook_program_js_1.ask; } });
62
+ Object.defineProperty(exports, "commandView", { enumerable: true, get: function () { return hook_program_js_1.commandView; } });
63
+ Object.defineProperty(exports, "pathView", { enumerable: true, get: function () { return hook_program_js_1.pathView; } });
64
+ Object.defineProperty(exports, "gateAction", { enumerable: true, get: function () { return hook_program_js_1.gateAction; } });
65
+ Object.defineProperty(exports, "hookMode", { enumerable: true, get: function () { return hook_program_js_1.hookMode; } });
66
+ // inject vocabulary
67
+ Object.defineProperty(exports, "defineInject", { enumerable: true, get: function () { return hook_program_js_1.defineInject; } });
68
+ Object.defineProperty(exports, "inject", { enumerable: true, get: function () { return hook_program_js_1.inject; } });
69
+ // react vocabulary
70
+ Object.defineProperty(exports, "defineReact", { enumerable: true, get: function () { return hook_program_js_1.defineReact; } });
71
+ Object.defineProperty(exports, "run", { enumerable: true, get: function () { return hook_program_js_1.run; } });
72
+ Object.defineProperty(exports, "notice", { enumerable: true, get: function () { return hook_program_js_1.notice; } });
73
+ Object.defineProperty(exports, "nothing", { enumerable: true, get: function () { return hook_program_js_1.nothing; } });
74
+ Object.defineProperty(exports, "responseView", { enumerable: true, get: function () { return hook_program_js_1.responseView; } });
75
+ // runtime + decode (used by the `vigiles hook-runtime run-program` runtime and tests)
76
+ Object.defineProperty(exports, "decideProgram", { enumerable: true, get: function () { return hook_program_js_1.decideProgram; } });
77
+ Object.defineProperty(exports, "decideFileGate", { enumerable: true, get: function () { return hook_program_js_1.decideFileGate; } });
78
+ Object.defineProperty(exports, "decidePromptGate", { enumerable: true, get: function () { return hook_program_js_1.decidePromptGate; } });
79
+ Object.defineProperty(exports, "decideStopGate", { enumerable: true, get: function () { return hook_program_js_1.decideStopGate; } });
80
+ Object.defineProperty(exports, "runInject", { enumerable: true, get: function () { return hook_program_js_1.runInject; } });
81
+ Object.defineProperty(exports, "runReact", { enumerable: true, get: function () { return hook_program_js_1.runReact; } });
82
+ Object.defineProperty(exports, "runHookProgram", { enumerable: true, get: function () { return hook_program_js_1.runHookProgram; } });
83
+ Object.defineProperty(exports, "decisionExitCode", { enumerable: true, get: function () { return hook_program_js_1.decisionExitCode; } });
84
+ Object.defineProperty(exports, "dispatchKind", { enumerable: true, get: function () { return hook_program_js_1.dispatchKind; } });
85
+ Object.defineProperty(exports, "hookRouting", { enumerable: true, get: function () { return hook_program_js_1.hookRouting; } });
86
+ Object.defineProperty(exports, "hookNeeds", { enumerable: true, get: function () { return hook_program_js_1.hookNeeds; } });
87
+ // compile + integrity
88
+ Object.defineProperty(exports, "compileHookProgram", { enumerable: true, get: function () { return hook_program_js_1.compileHookProgram; } });
89
+ Object.defineProperty(exports, "checkHookImports", { enumerable: true, get: function () { return hook_program_js_1.checkHookImports; } });
90
+ Object.defineProperty(exports, "stampHook", { enumerable: true, get: function () { return hook_program_js_1.stampHook; } });
91
+ Object.defineProperty(exports, "verifyHookStamp", { enumerable: true, get: function () { return hook_program_js_1.verifyHookStamp; } });
92
+ Object.defineProperty(exports, "HookCompileError", { enumerable: true, get: function () { return hook_program_js_1.HookCompileError; } });
93
+ var hook_providers_js_1 = require("./core/hook-providers.js");
94
+ Object.defineProperty(exports, "provide", { enumerable: true, get: function () { return hook_providers_js_1.provide; } });
95
+ Object.defineProperty(exports, "dangerously", { enumerable: true, get: function () { return hook_providers_js_1.dangerously; } });
96
+ Object.defineProperty(exports, "defineProvider", { enumerable: true, get: function () { return hook_providers_js_1.defineProvider; } });
97
+ Object.defineProperty(exports, "provider", { enumerable: true, get: function () { return hook_providers_js_1.provider; } });
98
+ //# sourceMappingURL=hook.js.map
@@ -32,4 +32,10 @@ export declare function scoreReport(r: ScanReport): {
32
32
  export declare function rankPlugins(dirs: readonly string[]): PluginScore[];
33
33
  /** Format a ranked leaderboard as human-readable text. */
34
34
  export declare function formatLeaderboard(scores: readonly PluginScore[]): string;
35
+ /**
36
+ * Format a ranked leaderboard as a Markdown table — the PUBLISHABLE form (a README,
37
+ * a gist, the leaderboard site). Shows the top 2 deductions per plugin; the full
38
+ * breakdown is in `--json`. Sibling of the plain-text {@link formatLeaderboard}.
39
+ */
40
+ export declare function formatLeaderboardMarkdown(scores: readonly PluginScore[]): string;
35
41
  //# sourceMappingURL=leaderboard.d.ts.map
@@ -16,8 +16,27 @@ exports.gradeFor = gradeFor;
16
16
  exports.scoreReport = scoreReport;
17
17
  exports.rankPlugins = rankPlugins;
18
18
  exports.formatLeaderboard = formatLeaderboard;
19
+ exports.formatLeaderboardMarkdown = formatLeaderboardMarkdown;
20
+ const node_fs_1 = require("node:fs");
19
21
  const node_path_1 = require("node:path");
20
22
  const scan_js_1 = require("./scan.js");
23
+ /** The plugin's declared name (`.claude-plugin/plugin.json`), for a real label in
24
+ * the ranking instead of a SHA-pinned dir basename. Falls back to the basename. */
25
+ function pluginLabel(dir) {
26
+ const p = (0, node_path_1.join)(dir, ".claude-plugin", "plugin.json");
27
+ if ((0, node_fs_1.existsSync)(p)) {
28
+ try {
29
+ const name = JSON.parse((0, node_fs_1.readFileSync)(p, "utf-8"))
30
+ .name;
31
+ if (typeof name === "string" && name.length > 0)
32
+ return name;
33
+ }
34
+ catch {
35
+ // fall through to the basename
36
+ }
37
+ }
38
+ return (0, node_path_1.basename)(dir) || dir;
39
+ }
21
40
  // Penalty weights — broken-at-runtime costs most, footguns less, nudges least.
22
41
  const W_MISSING_HOOK = 15; // a hook script that doesn't exist → never runs
23
42
  const W_NO_DESCRIPTION = 10; // a skill with no usable description → can't trigger
@@ -139,7 +158,7 @@ function rankPlugins(dirs) {
139
158
  const { score, issues } = scoreReport(report);
140
159
  return {
141
160
  dir,
142
- name: (0, node_path_1.basename)(dir) || dir,
161
+ name: pluginLabel(dir),
143
162
  score,
144
163
  grade: gradeFor(score),
145
164
  issues,
@@ -164,4 +183,27 @@ function formatLeaderboard(scores) {
164
183
  out.push("", "Structural health only (no model). Weights: missing hook -15, no-description", "skill -10, broken intra-plugin ref -8, agent-without-tool-contract -5,", "untested surface -3.");
165
184
  return out.join("\n");
166
185
  }
186
+ const LEADERBOARD_METHOD = "_Structural health only (deterministic, no model): missing hook −15, " +
187
+ "no-description skill −10, broken intra-plugin ref −8, " +
188
+ "agent-without-tool-contract −5, untested surface −3. " +
189
+ "Behavioural columns (trigger-rate, collisions, egress) stack on top._";
190
+ /**
191
+ * Format a ranked leaderboard as a Markdown table — the PUBLISHABLE form (a README,
192
+ * a gist, the leaderboard site). Shows the top 2 deductions per plugin; the full
193
+ * breakdown is in `--json`. Sibling of the plain-text {@link formatLeaderboard}.
194
+ */
195
+ function formatLeaderboardMarkdown(scores) {
196
+ const lines = [
197
+ `### Plugin health leaderboard (${String(scores.length)} scanned)`,
198
+ "",
199
+ "| # | grade | score | plugin | top issues |",
200
+ "| --: | :--: | --: | :-- | :-- |",
201
+ ];
202
+ scores.forEach((s, i) => {
203
+ const issues = s.issues.length > 0 ? s.issues.slice(0, 2).join("; ") : "— clean";
204
+ lines.push(`| ${String(i + 1)} | ${s.grade} | ${String(s.score)} | \`${s.name}\` | ${issues} |`);
205
+ });
206
+ lines.push("", LEADERBOARD_METHOD);
207
+ return lines.join("\n");
208
+ }
167
209
  //# sourceMappingURL=leaderboard.js.map
package/dist/linting.d.ts CHANGED
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * `vigiles/linting` — Pillar 1 entry point: the **linting layer** for instruction
3
- * files. Re-exports the spec builders/types, the compiler, and the linter
4
- * cross-referencing engine under one concern-named import. This is the canonical
5
- * pillar-1 surface; the spec builders are also at the package root (`vigiles`).
3
+ * files. Re-exports the spec builders/types + the public compile entry points
4
+ * under one concern-named import. This is the canonical pillar-1 surface; the
5
+ * spec builders are also at the package root (`vigiles`).
6
+ *
7
+ * Curated (named, not `export *`) so the internal compiler validators, hash
8
+ * helpers, and the linter cross-reference ENGINE stay out of the public surface,
9
+ * the api reports, and the docs site (the CLI imports those from the source).
6
10
  */
7
11
  export * from "./core/spec.js";
8
- export * from "./core/compile.js";
9
- export * from "./core/linters.js";
12
+ export { compileClaude, compileSkill, compileAgent, compileRailway, CompileError, } from "./core/compile.js";
13
+ export type { CompileClaudeOptions, CompileClaudeResult, CompileSkillResult, CompileAgentResult, CompileRailwayOptions, CompileRailwayResult, } from "./core/compile.js";
10
14
  //# sourceMappingURL=linting.d.ts.map
package/dist/linting.js CHANGED
@@ -14,13 +14,25 @@ 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.compileRailway = exports.compileAgent = exports.compileSkill = exports.compileClaude = void 0;
17
18
  /**
18
19
  * `vigiles/linting` — Pillar 1 entry point: the **linting layer** for instruction
19
- * files. Re-exports the spec builders/types, the compiler, and the linter
20
- * cross-referencing engine under one concern-named import. This is the canonical
21
- * pillar-1 surface; the spec builders are also at the package root (`vigiles`).
20
+ * files. Re-exports the spec builders/types + the public compile entry points
21
+ * under one concern-named import. This is the canonical pillar-1 surface; the
22
+ * spec builders are also at the package root (`vigiles`).
23
+ *
24
+ * Curated (named, not `export *`) so the internal compiler validators, hash
25
+ * helpers, and the linter cross-reference ENGINE stay out of the public surface,
26
+ * the api reports, and the docs site (the CLI imports those from the source).
22
27
  */
28
+ // The spec authoring builders (claude/enforce/guidance/file/cmd/agent/skill/…).
23
29
  __exportStar(require("./core/spec.js"), exports);
24
- __exportStar(require("./core/compile.js"), exports);
25
- __exportStar(require("./core/linters.js"), exports);
30
+ // Compile: only the public entry points + their option/result types.
31
+ var compile_js_1 = require("./core/compile.js");
32
+ Object.defineProperty(exports, "compileClaude", { enumerable: true, get: function () { return compile_js_1.compileClaude; } });
33
+ Object.defineProperty(exports, "compileSkill", { enumerable: true, get: function () { return compile_js_1.compileSkill; } });
34
+ Object.defineProperty(exports, "compileAgent", { enumerable: true, get: function () { return compile_js_1.compileAgent; } });
35
+ Object.defineProperty(exports, "compileRailway", { enumerable: true, get: function () { return compile_js_1.compileRailway; } });
36
+ // core/linters is the cross-reference ENGINE (checkLinterRule/editDistance/…),
37
+ // consumed by compile — not part of the public authoring surface.
26
38
  //# sourceMappingURL=linting.js.map
package/dist/optimize.js CHANGED
@@ -66,7 +66,7 @@ const ACTION_LABEL = {
66
66
  fix: "FIX",
67
67
  differentiate: "DIFFERENTIATE",
68
68
  };
69
- const measureHint = (dir) => `\`vigiles scan ${dir} --trigger\` — real-model, runs on your subscription`;
69
+ const measureHint = (dir) => `\`vigiles measure ${dir} --prompts=<file>\` — real-model, runs on your subscription`;
70
70
  /** Render an optimization plan for the CLI. */
71
71
  function formatOptimize(rep) {
72
72
  const head = `Harness health: ${String(rep.score)}/100 (${rep.grade}) — ${rep.dir}`;
@@ -1,6 +1,21 @@
1
1
  export type SurfaceKind = "skill" | "agent" | "hook";
2
2
  /** The cheapest meaningful tier for a surface kind (mirrors the test-harness skill). */
3
3
  export type TestTier = "unit" | "harness" | "eval";
4
+ /** One field of a subagent's `result()` contract, parsed from its compiled `.md`. */
5
+ export interface ContractField {
6
+ readonly name: string;
7
+ /** The `OutputFieldType` literal: `"string" | "number" | "boolean" | "string[]"`. */
8
+ readonly type: string;
9
+ }
10
+ /**
11
+ * A subagent's typed `result(ok, err)` outcome contract — the typed-spec payoff
12
+ * the generator turns into a real `assertAgentOk` test (no LLM judge). Parsed from
13
+ * the `vigiles:ok` / `vigiles:err` blocks the compiler emits into the agent `.md`.
14
+ */
15
+ export interface ResultContract {
16
+ readonly ok: readonly ContractField[];
17
+ readonly err: readonly ContractField[];
18
+ }
4
19
  /** What the generator needs to know about a surface to scaffold its test. */
5
20
  export interface ScaffoldInput {
6
21
  readonly kind: SurfaceKind;
@@ -14,6 +29,19 @@ export interface ScaffoldInput {
14
29
  readonly userInvoked?: boolean;
15
30
  /** A subagent's declared tool contract (drives the assertion hint); null = inherits all. */
16
31
  readonly tools?: readonly string[] | null;
32
+ /**
33
+ * The side-effecting tools in the contract (`effectSurface(tools).sideEffecting`,
34
+ * computed by the CLI with the resolved dialect — kept harness-agnostic here). A
35
+ * non-empty list drives a generated SAFETY check: the agent's "hole" is mocked/denied
36
+ * and asserted to stay in its lane — the typed `tools` contract writing its own test.
37
+ */
38
+ readonly sideEffectingTools?: readonly string[];
39
+ /**
40
+ * The subagent's `result()` contract parsed from its compiled `.md`. Present →
41
+ * the generator emits a deterministic `assertAgentOk` outcome test (no LLM judge);
42
+ * the single most-concrete "the typed spec wrote your test" payoff.
43
+ */
44
+ readonly resultContract?: ResultContract | null;
17
45
  /** How the CLI invokes the hook (e.g. `bash hooks/pre-edit.sh`); a TODO when unknown. */
18
46
  readonly hookCommand?: string;
19
47
  }