vigiles 5.1.0 → 6.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 (57) hide show
  1. package/README.md +59 -18
  2. package/dist/adapters/claude-code/adapter.js +1 -0
  3. package/dist/adapters/claude-code/agent-runtime.d.ts +45 -6
  4. package/dist/adapters/claude-code/agent-runtime.js +94 -8
  5. package/dist/adapters/claude-code/dialect.d.ts +34 -0
  6. package/dist/adapters/claude-code/dialect.js +51 -19
  7. package/dist/adapters/claude-code/effect-region.d.ts +9 -0
  8. package/dist/adapters/claude-code/effect-region.js +45 -0
  9. package/dist/adapters/claude-code/layout.js +3 -0
  10. package/dist/adapters/claude-code/skill-runtime.d.ts +25 -0
  11. package/dist/adapters/claude-code/skill-runtime.js +40 -0
  12. package/dist/adapters/claude-code/typed-spec.d.ts +58 -0
  13. package/dist/adapters/claude-code/typed-spec.js +55 -0
  14. package/dist/adapters/codex/adapter.js +3 -0
  15. package/dist/adapters/codex/layout.js +3 -0
  16. package/dist/adapters/opencode/adapter.js +1 -0
  17. package/dist/adapters/opencode/layout.js +3 -0
  18. package/dist/check.d.ts +8 -0
  19. package/dist/check.js +27 -3
  20. package/dist/claude-code.d.ts +1 -0
  21. package/dist/claude-code.js +8 -1
  22. package/dist/cli.js +469 -88
  23. package/dist/core/adapter.d.ts +10 -0
  24. package/dist/core/bash-effects.d.ts +41 -0
  25. package/dist/core/bash-effects.js +405 -0
  26. package/dist/core/compile.d.ts +3 -1
  27. package/dist/core/compile.js +176 -39
  28. package/dist/core/dialect.d.ts +10 -0
  29. package/dist/core/effects.d.ts +172 -0
  30. package/dist/core/effects.js +245 -0
  31. package/dist/core/generate-harness.d.ts +187 -0
  32. package/dist/core/generate-harness.js +337 -0
  33. package/dist/core/layout.d.ts +6 -0
  34. package/dist/core/mcp-tool.d.ts +1 -1
  35. package/dist/core/orphans.js +21 -0
  36. package/dist/core/spec.d.ts +432 -11
  37. package/dist/core/spec.js +166 -3
  38. package/dist/core/tool-contract.d.ts +1 -1
  39. package/dist/core/types.d.ts +6 -6
  40. package/dist/core/validate.js +4 -4
  41. package/dist/harness-test.d.ts +7 -0
  42. package/dist/harness-test.js +19 -7
  43. package/dist/leaderboard.d.ts +2 -0
  44. package/dist/leaderboard.js +2 -0
  45. package/dist/optimize.d.ts +74 -0
  46. package/dist/optimize.js +94 -0
  47. package/dist/scaffold-test.d.ts +58 -0
  48. package/dist/scaffold-test.js +263 -0
  49. package/dist/scan.d.ts +40 -0
  50. package/dist/scan.js +91 -43
  51. package/dist/score-explainer.d.ts +69 -0
  52. package/dist/score-explainer.js +169 -0
  53. package/dist/test-coverage.d.ts +7 -0
  54. package/dist/test-coverage.js +39 -24
  55. package/package.json +2 -1
  56. package/skills/{migrate-to-spec → adopt-spec}/SKILL.md +4 -4
  57. package/skills/edit-spec/SKILL.md +1 -1
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Typed Claude Code authoring surface — the compile-time half of the purity
3
+ * contract, bound to the Claude Code tool vocabulary.
4
+ *
5
+ * The core `agent()` / `skill()` builders (`vigiles/spec`) are generic over a
6
+ * tool `ToolVocabulary` that DEFAULTS to fully-open (`string` at every purity
7
+ * level), so they accept any tools — backwards-compatible, harness-agnostic.
8
+ * This module re-binds them to the CONCRETE Claude Code vocabulary derived from
9
+ * `claudeCodeDialect`, so authoring a spec with an invalid `purity`×`tools`
10
+ * combination is a `tsc` error at EDIT TIME, before any vigiles command runs:
11
+ *
12
+ * import { agent } from "vigiles/claude-code";
13
+ *
14
+ * agent({ purity: "pure", tools: ["Read", "Bash"] });
15
+ * // ^^^^^^ tsc error — Bash side-effecting
16
+ *
17
+ * agent({ purity: "bounded", tools: ["Read", "Bash", "Write"] }); // OK
18
+ * agent({ purity: "bounded", tools: ["mcp__x__y"] });
19
+ * // ^^^^^^^^^^^ tsc error — MCP not decidable
20
+ *
21
+ * agent({ tools: ["anything", "mcp__x__y"] }); // no purity → open, OK
22
+ *
23
+ * This is a STRICT ADDITION to the runtime/compile checks: `purityViolations`
24
+ * (`vigiles compile`) and `decidePurityGate` (the PreToolUse gate) are unchanged
25
+ * and remain the universal backstop. In particular the command-level decision a
26
+ * `bounded` unit makes for `Bash` (read-only command allowed, mutating denied)
27
+ * is the RUNTIME gate's job — the type only admits the `Bash` TOOL at `bounded`.
28
+ *
29
+ * The CC literal tool names live ONLY in this adapter (and the dialect it reads
30
+ * from), never in core — the hexagonal boundary that keeps the domain
31
+ * harness-agnostic.
32
+ */
33
+ import { type AgentSpec, type AgentSpecInput, type SkillSpec, type SkillSpecInput, type AuthoredPurity, type ToolVocabulary } from "../../core/spec.js";
34
+ import type { ClaudeCodeReadOnlyTool, ClaudeCodeBoundedTool } from "./dialect.js";
35
+ /**
36
+ * The Claude Code tool vocabulary, split by the purity floor that admits each
37
+ * tool (mirrors the runtime ladder in `core/effects.ts`):
38
+ * - `readOnly`: tools a `pure` unit may declare (Read/Grep/Glob/LS).
39
+ * - `bounded`: read-only ∪ Write/Edit/MultiEdit/NotebookEdit ∪ `Bash`.
40
+ */
41
+ export interface ClaudeCodeToolVocabulary extends ToolVocabulary {
42
+ readonly readOnly: ClaudeCodeReadOnlyTool;
43
+ readonly bounded: ClaudeCodeBoundedTool;
44
+ }
45
+ /**
46
+ * Define a Claude Code subagent with the purity floor enforced AT COMPILE TIME
47
+ * against the Claude Code tool catalog. Identical to the core `agent()` at
48
+ * runtime (it IS the core builder); the only difference is the typed `tools`
49
+ * constraint. `P` is inferred from the literal `purity` field.
50
+ */
51
+ export declare function agent<const P extends AuthoredPurity | undefined = undefined>(spec: AgentSpecInput<P, ClaudeCodeToolVocabulary>): AgentSpec;
52
+ /**
53
+ * Define a Claude Code skill with the purity floor enforced AT COMPILE TIME
54
+ * against the Claude Code tool catalog. Identical to the core `skill()` at
55
+ * runtime; the typed `tools` constraint is the only difference.
56
+ */
57
+ export declare function skill<const P extends AuthoredPurity | undefined = undefined>(spec: SkillSpecInput<P, ClaudeCodeToolVocabulary>): SkillSpec;
58
+ //# sourceMappingURL=typed-spec.d.ts.map
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.agent = agent;
4
+ exports.skill = skill;
5
+ /**
6
+ * Typed Claude Code authoring surface — the compile-time half of the purity
7
+ * contract, bound to the Claude Code tool vocabulary.
8
+ *
9
+ * The core `agent()` / `skill()` builders (`vigiles/spec`) are generic over a
10
+ * tool `ToolVocabulary` that DEFAULTS to fully-open (`string` at every purity
11
+ * level), so they accept any tools — backwards-compatible, harness-agnostic.
12
+ * This module re-binds them to the CONCRETE Claude Code vocabulary derived from
13
+ * `claudeCodeDialect`, so authoring a spec with an invalid `purity`×`tools`
14
+ * combination is a `tsc` error at EDIT TIME, before any vigiles command runs:
15
+ *
16
+ * import { agent } from "vigiles/claude-code";
17
+ *
18
+ * agent({ purity: "pure", tools: ["Read", "Bash"] });
19
+ * // ^^^^^^ tsc error — Bash side-effecting
20
+ *
21
+ * agent({ purity: "bounded", tools: ["Read", "Bash", "Write"] }); // OK
22
+ * agent({ purity: "bounded", tools: ["mcp__x__y"] });
23
+ * // ^^^^^^^^^^^ tsc error — MCP not decidable
24
+ *
25
+ * agent({ tools: ["anything", "mcp__x__y"] }); // no purity → open, OK
26
+ *
27
+ * This is a STRICT ADDITION to the runtime/compile checks: `purityViolations`
28
+ * (`vigiles compile`) and `decidePurityGate` (the PreToolUse gate) are unchanged
29
+ * and remain the universal backstop. In particular the command-level decision a
30
+ * `bounded` unit makes for `Bash` (read-only command allowed, mutating denied)
31
+ * is the RUNTIME gate's job — the type only admits the `Bash` TOOL at `bounded`.
32
+ *
33
+ * The CC literal tool names live ONLY in this adapter (and the dialect it reads
34
+ * from), never in core — the hexagonal boundary that keeps the domain
35
+ * harness-agnostic.
36
+ */
37
+ const spec_js_1 = require("../../core/spec.js");
38
+ /**
39
+ * Define a Claude Code subagent with the purity floor enforced AT COMPILE TIME
40
+ * against the Claude Code tool catalog. Identical to the core `agent()` at
41
+ * runtime (it IS the core builder); the only difference is the typed `tools`
42
+ * constraint. `P` is inferred from the literal `purity` field.
43
+ */
44
+ function agent(spec) {
45
+ return (0, spec_js_1.agent)(spec);
46
+ }
47
+ /**
48
+ * Define a Claude Code skill with the purity floor enforced AT COMPILE TIME
49
+ * against the Claude Code tool catalog. Identical to the core `skill()` at
50
+ * runtime; the typed `tools` constraint is the only difference.
51
+ */
52
+ function skill(spec) {
53
+ return (0, spec_js_1.skill)(spec);
54
+ }
55
+ //# sourceMappingURL=typed-spec.js.map
@@ -29,6 +29,9 @@ exports.codexAdapter = {
29
29
  referenceVerification: true,
30
30
  harnessTesting: true,
31
31
  shellHooks: true,
32
+ // Codex `[agents]` is a concurrency table, not a subagent tool-contract file
33
+ // — the subagent-surface rules report n/a here (a deliberate non-goal).
34
+ subagents: false,
32
35
  },
33
36
  dialect: dialect_js_1.codexDialect,
34
37
  layout: layout_js_1.codexLayout,
@@ -9,6 +9,9 @@ exports.codexLayout = {
9
9
  settingsFormat: "toml",
10
10
  instructionFile: "AGENTS.md",
11
11
  surfaceDirs: ["skills", "prompts"],
12
+ skillDir: "skills",
13
+ agentDir: "", // Codex `[agents]` is a TOML concurrency table, not a subagent dir
14
+ commandDir: "prompts",
12
15
  materializeRoot: ".codex",
13
16
  pluginRootToken: "${PLUGIN_ROOT}",
14
17
  mcpConfigFile: ".mcp.json",
@@ -29,6 +29,7 @@ exports.opencodeAdapter = {
29
29
  referenceVerification: true,
30
30
  harnessTesting: true,
31
31
  shellHooks: false,
32
+ subagents: true,
32
33
  },
33
34
  dialect: dialect_js_1.opencodeDialect,
34
35
  layout: layout_js_1.opencodeLayout,
@@ -16,6 +16,9 @@ exports.opencodeLayout = {
16
16
  // `.opencode/` segment. (Contrast Claude Code: root-level `skills/` surfaces
17
17
  // relocated under `.claude`.)
18
18
  surfaceDirs: [".opencode/agent", ".opencode/command"],
19
+ skillDir: ".opencode/skill",
20
+ agentDir: ".opencode/agent",
21
+ commandDir: ".opencode/command",
19
22
  materializeRoot: "",
20
23
  pluginRootToken: "${OPENCODE_PLUGIN_ROOT}",
21
24
  mcpConfigFile: "opencode.json",
package/dist/check.d.ts CHANGED
@@ -104,6 +104,14 @@ export declare function turns(opts: {
104
104
  }): Check<Trace>;
105
105
  /** The agent wrote (or left) a file at this path in the work dir. */
106
106
  export declare function wrote(path: string): Check<Trace>;
107
+ /**
108
+ * The agent did NOT leave a file at this path — the **side-effect boundary**
109
+ * negative: a skill that declares it writes only `out.txt` should leave nothing
110
+ * at `secrets.env`. The symmetric sibling of `wrote()`; pairs with
111
+ * `notTool(...)` to assert a unit stayed inside its declared write surface
112
+ * deterministically (no model judge).
113
+ */
114
+ export declare function didNotWrite(path: string): Check<Trace>;
107
115
  /** The named subagent (`Task` `subagent_type`) ran and passed every nested check. */
108
116
  export declare function subagent(name: string, checks: readonly Check<Trace>[]): Check<Trace>;
109
117
  /** The hook blocked the event (exit 2 / deny / block). */
package/dist/check.js CHANGED
@@ -11,6 +11,7 @@ exports.hookFired = hookFired;
11
11
  exports.received = received;
12
12
  exports.turns = turns;
13
13
  exports.wrote = wrote;
14
+ exports.didNotWrite = didNotWrite;
14
15
  exports.subagent = subagent;
15
16
  exports.blocked = blocked;
16
17
  exports.allowed = allowed;
@@ -236,16 +237,34 @@ function wrote(path) {
236
237
  toJSON: () => ({ kind: "wrote", path }),
237
238
  };
238
239
  }
240
+ /**
241
+ * The agent did NOT leave a file at this path — the **side-effect boundary**
242
+ * negative: a skill that declares it writes only `out.txt` should leave nothing
243
+ * at `secrets.env`. The symmetric sibling of `wrote()`; pairs with
244
+ * `notTool(...)` to assert a unit stayed inside its declared write surface
245
+ * deterministically (no model judge).
246
+ */
247
+ function didNotWrite(path) {
248
+ return {
249
+ kind: "didNotWrite",
250
+ eval: (t) => t.file(path) === null
251
+ ? ok(`file "${path}" was not created`)
252
+ : no(`expected the agent NOT to create "${path}", but it exists`),
253
+ toJSON: () => ({ kind: "didNotWrite", path }),
254
+ };
255
+ }
239
256
  // ---------------------------------------------------------------------------
240
257
  // Subagent — a `Task` run as a nested trace. Run checks over what the SUBAGENT
241
258
  // did, not just that `Task` fired. Composes the whole vocabulary recursively.
242
259
  // ---------------------------------------------------------------------------
243
- /** Wrap a subagent's tool calls as a minimal `Trace` so checks run over it. */
260
+ /** Wrap a subagent's tool calls + returned text as a minimal `Trace` so checks
261
+ * (incl. `output()` over the sub's RETURN — where a result() vigiles:ok/err block
262
+ * lands) run over it. */
244
263
  function subTrace(sub) {
245
264
  return {
246
265
  toolCalls: sub.toolCalls,
247
266
  hooks: [],
248
- output: "",
267
+ output: sub.output,
249
268
  modelRequests: [],
250
269
  turns: 0,
251
270
  subagents: [],
@@ -258,7 +277,12 @@ function subagent(name, checks) {
258
277
  kind: "subagent",
259
278
  eval: (t) => {
260
279
  const subs = t.subagents ?? [];
261
- const sub = subs.find((s) => s.name === name);
280
+ // A `--plugin-dir` agent's `subagent_type` is namespaced `plugin:agent`
281
+ // (e.g. "reviewer-spec:code-reviewer"), but callers pass the bare agent name
282
+ // — so match the full id OR its last `:`-segment. Non-namespaced (harness
283
+ // mock) names match exactly as before.
284
+ const bare = (n) => n.includes(":") ? n.slice(n.lastIndexOf(":") + 1) : n;
285
+ const sub = subs.find((s) => s.name === name || bare(s.name) === name);
262
286
  if (!sub) {
263
287
  return no(`expected subagent "${name}" to run; subagents that ran: ${subs.length > 0 ? `[${subs.map((s) => s.name).join(", ")}]` : "none"}`);
264
288
  }
@@ -9,6 +9,7 @@ export * from "./adapters/claude-code/plugin-loader.js";
9
9
  export * from "./mock-model.js";
10
10
  export { claudeCodeDriver, buildClaudeArgs, parseClaudeRun, claudeAvailable, } from "./harness-test.js";
11
11
  export * from "./adapters/claude-code/dialect.js";
12
+ export { agent, skill, type ClaudeCodeToolVocabulary, } from "./adapters/claude-code/typed-spec.js";
12
13
  export * from "./adapters/claude-code/layout.js";
13
14
  export * from "./adapters/claude-code/runtime.js";
14
15
  export * from "./adapters/claude-code/hook-protocol.js";
@@ -14,7 +14,7 @@ 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.claudeAvailable = exports.parseClaudeRun = exports.buildClaudeArgs = exports.claudeCodeDriver = void 0;
17
+ exports.skill = exports.agent = exports.claudeAvailable = exports.parseClaudeRun = exports.buildClaudeArgs = exports.claudeCodeDriver = void 0;
18
18
  /**
19
19
  * `vigiles/claude-code` — the Claude Code-specific harness pieces a *different*
20
20
  * harness would swap out: the plugin/repo loader (reads real Claude Code plugin
@@ -35,6 +35,13 @@ Object.defineProperty(exports, "buildClaudeArgs", { enumerable: true, get: funct
35
35
  Object.defineProperty(exports, "parseClaudeRun", { enumerable: true, get: function () { return harness_test_js_1.parseClaudeRun; } });
36
36
  Object.defineProperty(exports, "claudeAvailable", { enumerable: true, get: function () { return harness_test_js_1.claudeAvailable; } });
37
37
  __exportStar(require("./adapters/claude-code/dialect.js"), exports);
38
+ // The typed Claude Code authoring surface: `agent` / `skill` with the `purity`
39
+ // floor enforced AT COMPILE TIME against the CC tool catalog (a `tsc` error for
40
+ // e.g. `purity: "pure"` + `"Bash"`). A strict addition to the runtime/compile
41
+ // purity checks; the bare core `agent()`/`skill()` (`vigiles/spec`) stay open.
42
+ var typed_spec_js_1 = require("./adapters/claude-code/typed-spec.js");
43
+ Object.defineProperty(exports, "agent", { enumerable: true, get: function () { return typed_spec_js_1.agent; } });
44
+ Object.defineProperty(exports, "skill", { enumerable: true, get: function () { return typed_spec_js_1.skill; } });
38
45
  __exportStar(require("./adapters/claude-code/layout.js"), exports);
39
46
  __exportStar(require("./adapters/claude-code/runtime.js"), exports);
40
47
  __exportStar(require("./adapters/claude-code/hook-protocol.js"), exports);