vigiles 19.0.1 → 21.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.
package/dist/core/spec.js CHANGED
@@ -10,7 +10,7 @@
10
10
  * guidance() — prose only, no mechanical enforcement
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.experimental_skill = exports.BUILTIN_LINTERS = void 0;
13
+ exports.agent = exports.experimental_agent = exports.instructions = exports.claude = exports.experimental_skill = exports.BUILTIN_LINTERS = void 0;
14
14
  exports.enforce = enforce;
15
15
  exports.guidance = guidance;
16
16
  exports.guard = guard;
@@ -20,23 +20,15 @@ exports.symbol = symbol;
20
20
  exports.ref = ref;
21
21
  exports.dir = dir;
22
22
  exports.glob = glob;
23
- exports.instructions = instructions;
23
+ exports.prose = prose;
24
24
  exports.experimental_effect = experimental_effect;
25
- exports.claude = claude;
25
+ exports.instructionFile = instructionFile;
26
26
  exports.project = project;
27
- exports.agent = agent;
28
- exports.result = result;
29
- exports.delegate = delegate;
30
- exports.railway = railway;
31
- exports.experimental_needs = experimental_needs;
32
- exports.experimental_pipeStep = experimental_pipeStep;
33
- exports.experimental_start = experimental_start;
34
- exports.experimental_andThen = experimental_andThen;
35
- exports.experimental_pipe = experimental_pipe;
36
27
  exports.defineConfig = defineConfig;
37
28
  // ---------------------------------------------------------------------------
38
29
  // Template literal types for type-safe linter references
39
30
  // ---------------------------------------------------------------------------
31
+ const skill_normalize_js_1 = require("./skill-normalize.js");
40
32
  /** Linters and policy catalogs vigiles can cross-reference. */
41
33
  /**
42
34
  * The built-in linter / policy catalogs vigiles cross-references — the SINGLE
@@ -156,13 +148,13 @@ function glob(pattern) {
156
148
  /**
157
149
  * Tagged template literal for skill instructions with typed references.
158
150
  *
159
- * instructions`
151
+ * prose`
160
152
  * Check ${file("eslint.config.ts")} for rules.
161
153
  * Run ${cmd("npm test")} to verify.
162
154
  * See ${ref("skills/other/SKILL.md")} for format.
163
155
  * `
164
156
  */
165
- function instructions(strings, ...values) {
157
+ function prose(strings, ...values) {
166
158
  const result = [];
167
159
  for (let i = 0; i < strings.length; i++) {
168
160
  if (strings[i])
@@ -176,7 +168,7 @@ function instructions(strings, ...values) {
176
168
  * Tagged template literal marking a side-effect boundary — usable as an
177
169
  * interpolated fragment inside a body / `instructions\`\``:
178
170
  *
179
- * instructions`
171
+ * prose`
180
172
  * ## Apply
181
173
  * ${effect`
182
174
  * Side effects are allowed ONLY here:
@@ -206,9 +198,9 @@ function experimental_effect(strings, ...values) {
206
198
  * Define a CLAUDE.md specification.
207
199
  *
208
200
  * // CLAUDE.md.spec.ts
209
- * export default claude({ commands: {...}, rules: {...} });
201
+ * export default instructionFile({ commands: {...}, rules: {...} });
210
202
  */
211
- function claude(spec) {
203
+ function instructionFile(spec) {
212
204
  return { _specType: "claude", ...spec };
213
205
  }
214
206
  /**
@@ -262,7 +254,7 @@ function step(instr, opts = {}) {
262
254
  * export default experimental_skill({ name: "my-skill", description: "…" });
263
255
  *
264
256
  * Generic over a tool `Vocabulary` (default `OpenToolVocabulary` — no
265
- * constraint), exactly like `agent()`: a vocabulary-bound `experimental_skill`
257
+ * constraint), exactly like `experimental_agent()`: a vocabulary-bound `experimental_skill`
266
258
  * (e.g. `vigiles/claude-code`) makes `purity: "pure"` + a side-effecting tool a
267
259
  * `tsc` error; the bare core one accepts any tools, as before.
268
260
  *
@@ -289,7 +281,11 @@ function step(instr, opts = {}) {
289
281
  * @experimental
290
282
  */
291
283
  function skillSpec(spec) {
292
- return { _specType: "skill", ...spec };
284
+ // The deprecated `result:` is folded into `postcondition:` by the shared
285
+ // helper — see `skill-normalize.ts` for why it is shared rather than inlined
286
+ // here (short version: this builder is not the only public entrance, and the
287
+ // other one silently dropped the gate until a reviewer noticed).
288
+ return (0, skill_normalize_js_1.foldLegacyPostcondition)({ _specType: "skill", ...spec });
293
289
  }
294
290
  /**
295
291
  * @experimental
@@ -299,12 +295,12 @@ exports.experimental_skill = Object.assign(skillSpec, { input, step });
299
295
  * Define a subagent specification (compiles to `agents/<name>.md`).
300
296
  *
301
297
  * // agents/reviewer.md.spec.ts
302
- * export default agent({
298
+ * export default experimental_agent({
303
299
  * name: "reviewer",
304
300
  * description: "Review a diff for correctness. Dispatch PROACTIVELY after edits.",
305
301
  * model: "sonnet",
306
302
  * tools: ["Read", "Grep", "Bash"],
307
- * body: instructions`Review the diff. Run ${cmd("npm test")} first.`,
303
+ * body: prose`Review the diff. Run ${cmd("npm test")} first.`,
308
304
  * rules: {
309
305
  * "no-floating": enforce("@typescript-eslint/no-floating-promises", "Await promises."),
310
306
  * },
@@ -313,15 +309,21 @@ exports.experimental_skill = Object.assign(skillSpec, { input, step });
313
309
  * Generic over a tool `Vocabulary` (default `OpenToolVocabulary` — no
314
310
  * constraint). A harness adapter re-exports a vocabulary-bound `agent` (e.g.
315
311
  * `vigiles/claude-code`) so `purity: "pure"` + a side-effecting tool is a `tsc`
316
- * error at edit time; the bare core `agent()` accepts any tools, as before.
312
+ * error at edit time; the bare core `experimental_agent()` accepts any tools, as before.
317
313
  *
318
314
  * Also generic over the result's `Ok`/`Err` shapes, inferred from `output:
319
315
  * result(...)`. The returned value is a `TypedAgentSpec<Ok, Err>` — an
320
316
  * `AgentSpec` that carries those shapes at the type level, so a typed `pipe`
321
317
  * can cross-reference the handoff. With no `output` the shapes default to the
322
318
  * erased `Shape`, and the value is still a plain `AgentSpec` — backwards-compatible.
319
+ *
320
+ * @experimental The SHAPE is not settled — the author is unsure of the design,
321
+ * which is exactly what this marker promises: the form may change. It is NOT a
322
+ * claim that the surface is unproven. Measured 2026-06-20: real Claude Code
323
+ * loaded a compiled `agents/code-reviewer.md`, dispatched to it and read it,
324
+ * 100% of trials — stronger end-to-end evidence than `experimental_skill` has.
323
325
  */
324
- function agent(spec) {
326
+ function agentSpec(spec) {
325
327
  return { _specType: "agent", ...spec };
326
328
  }
327
329
  /**
@@ -340,6 +342,7 @@ function agent(spec) {
340
342
  * cross-reference one agent's `ok` against the next agent's needs at `tsc` time.
341
343
  * The return is still an `OutputContract`, so every existing consumer (the
342
344
  * `output:` field, `renderOutputContract`, `parseAgentResult`) is unchanged.
345
+
343
346
  */
344
347
  function result(ok, err) {
345
348
  return { _ref: "output", ok, err };
@@ -357,6 +360,7 @@ function result(ok, err) {
357
360
  * handoff that doesn't line up is a `tsc` error naming the offending field.
358
361
  * Omitting it (the historical 1-/2-arg call) keeps the exact string-path
359
362
  * behavior — fully backwards-compatible.
363
+
360
364
  */
361
365
  function delegate(agent, task, needsContract) {
362
366
  const base = task === undefined
@@ -373,6 +377,7 @@ function delegate(agent, task, needsContract) {
373
377
  * onError: delegate("reporter"),
374
378
  * recover: { step: delegate("fixer"), max: 2 },
375
379
  * })
380
+
376
381
  */
377
382
  function railway(spec) {
378
383
  return { _specType: "railway", ...spec };
@@ -386,19 +391,21 @@ function railway(spec) {
386
391
  *
387
392
  * @experimental Experimental typed-composition surface — NOT part of the frozen
388
393
  * public API (pre-1.0); may change without a major bump.
394
+
389
395
  */
390
396
  function experimental_needs(shape) {
391
397
  return shape;
392
398
  }
393
399
  /**
394
400
  * Pair a typed agent with the input it `needs` from the previous step. The first
395
- * argument is an `agent()` VALUE (which carries its `result()` shape); the
401
+ * argument is an `experimental_agent()` VALUE (which carries its `result()` shape); the
396
402
  * second is the `needs(...)` input contract.
397
403
  *
398
404
  * pipeStep(implementer, needs({ plan: "string", files: "string[]" }))
399
405
  *
400
406
  * @experimental Experimental typed-composition surface — NOT part of the frozen
401
407
  * public API (pre-1.0); may change without a major bump.
408
+
402
409
  */
403
410
  function experimental_pipeStep(a, needsContract = {}) {
404
411
  return { _step: "typed-delegate", agent: a, needs: needsContract };
@@ -410,6 +417,7 @@ function experimental_pipeStep(a, needsContract = {}) {
410
417
  *
411
418
  * @experimental Experimental typed-composition surface — NOT part of the frozen
412
419
  * public API (pre-1.0); may change without a major bump.
420
+
413
421
  */
414
422
  function experimental_start(first) {
415
423
  const step = "_step" in first
@@ -440,6 +448,7 @@ function experimental_start(first) {
440
448
  *
441
449
  * @experimental Experimental typed-composition surface — NOT part of the frozen
442
450
  * public API (pre-1.0); may change without a major bump.
451
+
443
452
  */
444
453
  function experimental_andThen(prior, next) {
445
454
  const real = next;
@@ -468,4 +477,79 @@ function experimental_pipe(first, ...rest) {
468
477
  function defineConfig(config) {
469
478
  return config;
470
479
  }
480
+ // ─── ОКНО АЛИАСА (один мажор) ──────────────────────────────────────────────────
481
+ // Старые имена остаются рабочими ровно один мажорный релиз, помеченные
482
+ // `@deprecated`, и убираются в следующем.
483
+ //
484
+ // 🔴 ПОЧЕМУ ЭТО НЕ ВЕЖЛИВОСТЬ, А НЕОБХОДИМОСТЬ, замерено 2026-08-21: предыдущее
485
+ // переименование ушло БЕЗ окна — 18.1.1 экспортировал только старые имена,
486
+ // 19.0.0 только новые, пересечения ноль. У потребителя (репа знаний, 12
487
+ // скомпилированных хуков) откат контейнера вернул старый `node_modules` под
488
+ // новые исходники, `PreToolUse` перестал загружаться, а не загрузившийся
489
+ // PreToolUse отбивает ЛЮБУЮ Bash-команду — включая ту, которой это чинится.
490
+ // Репа встала колом на час. Одно окно в один мажор делает этот отказ
491
+ // невыразимым: любая пара (лок, исходники) в пределах мажора совместима.
492
+ //
493
+ // 🔴 «ОДИН МАЖОР» СЧИТАЕТСЯ ОТ ТОГО, КОТОРЫЙ АЛИАСЫ ВВОДИТ, а не до него.
494
+ // Мажор, выпускающий этот PR, — ПЕРВЫЙ, где старые и новые имена сосуществуют;
495
+ // именно он и есть обещанное окно. Убирать алиасы можно в СЛЕДУЮЩЕМ за ним.
496
+ // Формулировка «Removed next major» была двусмысленной ровно здесь (её так и
497
+ // прочитал ревьюер: как «удалить в том же релизе, который их вводит»), и по
498
+ // такому расписанию окна не существовало бы вовсе — то есть отказ выше
499
+ // воспроизвёлся бы дословно, при живом абзаце, который его запрещает.
500
+ /**
501
+ * @deprecated Renamed to {@link instructionFile}. The builder compiles to
502
+ * `CLAUDE.md` **and** `AGENTS.md` (see `InstructionTarget`), so a name taken from
503
+ * one of the two harnesses was never right. Removed one major AFTER the one that introduces it.
504
+ */
505
+ exports.claude = instructionFile;
506
+ /**
507
+ * @deprecated Renamed to {@link prose}. It builds a prose FRAGMENT with typed
508
+ * refs; the plural read as "the instruction file", which is what
509
+ * {@link instructionFile} builds. Removed one major AFTER the one that introduces it.
510
+ */
511
+ exports.instructions = prose;
512
+ /**
513
+ * Define a subagent — and the ROOT of the subagent vocabulary.
514
+ *
515
+ * Everything that only makes sense for subagents hangs off this one symbol:
516
+ * `experimental_agent.result()` (the outcome contract), `.railway()` / `.delegate()`
517
+ * (the flat orchestrator), and `.pipe()` / `.start()` / `.andThen()` / `.pipeStep()` /
518
+ * `.needs()` (the typed pipeline). Same chokepoint as `experimental_skill.input()`.
519
+ *
520
+ * WHY a root and not a prefix on each name: `railway()` and `delegate()` are
521
+ * meaningless without a subagent, yet they shipped under STABLE names while the
522
+ * builder they depend on is experimental — a stable name resting on an unstable
523
+ * one. Prefixing each would carry the warning but multiply the vocabulary; a
524
+ * namespace OBJECT was rejected for the reason the `vigiles/experimental` subpath
525
+ * was retired in #169 — it marks the import line, out of view by the time anyone
526
+ * reads the call. A member reached through the marked root cannot be destructured
527
+ * free of its warning without renaming it, so the warning rides every call site.
528
+ *
529
+ * The rule this establishes: ONE experimental root per feature, everything else a
530
+ * member of it, TYPES excluded (a type annotation is not a call site).
531
+ *
532
+ * @experimental
533
+ */
534
+ exports.experimental_agent = Object.assign(agentSpec, {
535
+ result,
536
+ delegate,
537
+ railway,
538
+ needs: experimental_needs,
539
+ pipeStep: experimental_pipeStep,
540
+ start: experimental_start,
541
+ andThen: experimental_andThen,
542
+ pipe: experimental_pipe,
543
+ });
544
+ /**
545
+ * @deprecated Renamed to {@link experimental_agent} — the shape is not settled.
546
+ * Removed one major AFTER the one that introduces it.
547
+ *
548
+ * @experimental
549
+ * vigiles:experimental-name-ok this IS the old spelling — prefixing a deprecated
550
+ * alias would defeat the alias, which exists precisely so code written against
551
+ * the unprefixed name keeps compiling for one major. It carries the tag because
552
+ * it is the same function, and the tag is what the deprecation notice points at.
553
+ */
554
+ exports.agent = exports.experimental_agent;
471
555
  //# sourceMappingURL=spec.js.map
@@ -13,9 +13,14 @@
13
13
  * The import path warns ONCE, at the top of the file. The name warns EVERY time,
14
14
  * at the call site. Reading `await judged(trace, "did it refuse?")` on line 140,
15
15
  * the import line is long out of view — `await paid_judged(...)` still says what
16
- * it costs. This is not a new idiom in this package: `vigiles/experimental`
17
- * already pairs a quarantined subpath with an `experimental_` name prefix for
18
- * exactly this reason. The same device, applied to a second axis.
16
+ * it costs. This is not a new idiom in this package: the `experimental_` prefix
17
+ * says the same kind of thing on a second axis, at the same place.
18
+ *
19
+ * That comparison used to read "`vigiles/experimental` already pairs a
20
+ * quarantined subpath WITH a name prefix". The subpath was deleted 2026-08-21
21
+ * and the prefix kept, on the argument this paragraph makes: of the two, only
22
+ * the name is present where the reader is. Which is also why THIS surface has
23
+ * no `vigiles/paid` subpath and never needed one.
19
24
  *
20
25
  * ⚠️ **The prefix slightly OVERSTATES the cost, and that is a deliberate trade
21
26
  * rather than an oversight.** `paid_judged` takes an injectable judge:
@@ -14,9 +14,14 @@
14
14
  * The import path warns ONCE, at the top of the file. The name warns EVERY time,
15
15
  * at the call site. Reading `await judged(trace, "did it refuse?")` on line 140,
16
16
  * the import line is long out of view — `await paid_judged(...)` still says what
17
- * it costs. This is not a new idiom in this package: `vigiles/experimental`
18
- * already pairs a quarantined subpath with an `experimental_` name prefix for
19
- * exactly this reason. The same device, applied to a second axis.
17
+ * it costs. This is not a new idiom in this package: the `experimental_` prefix
18
+ * says the same kind of thing on a second axis, at the same place.
19
+ *
20
+ * That comparison used to read "`vigiles/experimental` already pairs a
21
+ * quarantined subpath WITH a name prefix". The subpath was deleted 2026-08-21
22
+ * and the prefix kept, on the argument this paragraph makes: of the two, only
23
+ * the name is present where the reader is. Which is also why THIS surface has
24
+ * no `vigiles/paid` subpath and never needed one.
20
25
  *
21
26
  * ⚠️ **The prefix slightly OVERSTATES the cost, and that is a deliberate trade
22
27
  * rather than an oversight.** `paid_judged` takes an injectable judge:
@@ -176,7 +176,7 @@ function assertHookAllowed(r) {
176
176
  * in-process rather than loaded from disk has no file to name, so nothing is
177
177
  * recorded and nothing is invented:
178
178
  *
179
- * const h = defineHook({…}); assertHookDenies(h, e); → surfacesRecorded() === []
179
+ * const h = experimental_defineHook({…}); assertHookDenies(h, e); → surfacesRecorded() === []
180
180
  *
181
181
  * …and a direct `runHookProgram(hook, event)` call (the pure evaluator, public
182
182
  * via `vigiles/hook`) records nothing either, for the reason above. Both cost a
package/dist/hook.d.ts CHANGED
@@ -11,15 +11,15 @@
11
11
  *
12
12
  * The roles, each with its own output type so a category mistake is a `tsc`
13
13
  * error, not a silent no-op:
14
- * - `defineHook` / `defineFileGate` — a **gate** returns a `Decision`
14
+ * - `experimental_defineHook` / `experimental_defineFileGate` — a **gate** returns a `Decision`
15
15
  * (`allow`/`deny`/`ask`); `deny` is the only thing that blocks.
16
- * - `definePromptGate` — a **prompt gate** (UserPromptSubmit) sees the prompt
16
+ * - `experimental_definePromptGate` — a **prompt gate** (UserPromptSubmit) sees the prompt
17
17
  * TEXT and may `deny` to block it (a security filter).
18
- * - `defineStopGate` — a **stop gate** (Stop/SubagentStop) may `deny` to keep
18
+ * - `experimental_defineStopGate` — a **stop gate** (Stop/SubagentStop) may `deny` to keep
19
19
  * the agent going (gate-until-tests-pass).
20
- * - `defineInject` — an **inject** returns an `Injection` (context text); it
20
+ * - `experimental_defineInject` — an **inject** returns an `Injection` (context text); it
21
21
  * has no `deny`, so "block on a SessionStart hook" won't compile.
22
- * - `defineReact` — a **react** (PostToolUse) returns a `Reaction`; it sees the
22
+ * - `experimental_defineReact` — a **react** (PostToolUse) returns a `Reaction`; it sees the
23
23
  * tool RESPONSE, its `run(cmd)` is effect-classified at construction, and it
24
24
  * can't block (the tool already ran).
25
25
  *
@@ -45,7 +45,7 @@
45
45
  * tool calls. A gate is a strong default, never an unbypassable wall. See
46
46
  * `docs/compiled-hooks.md`.
47
47
  */
48
- export { defineHook as experimental_defineHook, defineFileGate as experimental_defineFileGate, definePromptGate as experimental_definePromptGate, defineStopGate as experimental_defineStopGate, tool, tools, allow, deny, ask, commandView, pathView, gateAction, hookMode, defineInject as experimental_defineInject, inject, defineReact as experimental_defineReact, run, notice, nothing, responseView, decideProgram, decideFileGate, decidePromptGate, decideStopGate, runInject, runReact, runHookProgram, decisionExitCode, dispatchKind, hookRouting, hookNeeds, injectionOf, outcomeWrites, matchesTool, invalidToolPatterns, compileHookProgram, checkHookImports, stampHook, verifyHookStamp, HookCompileError, } from "./core/hook-program.js";
48
+ export { experimental_defineHook, experimental_defineFileGate, experimental_definePromptGate, experimental_defineStopGate, tool, tools, allow, deny, ask, commandView, pathView, gateAction, hookMode, experimental_defineInject, inject, experimental_defineReact, run, notice, nothing, responseView, decideProgram, decideFileGate, decidePromptGate, decideStopGate, runInject, runReact, runHookProgram, decisionExitCode, dispatchKind, hookRouting, hookNeeds, injectionOf, outcomeWrites, matchesTool, invalidToolPatterns, compileHookProgram, checkHookImports, stampHook, verifyHookStamp, HookCompileError, } from "./core/hook-program.js";
49
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
50
  export { provide, dangerously, defineProvider, provider, } from "./core/hook-providers.js";
51
51
  export { state, record, stateFact, isValidStateKey, isStateNeed, isStateWrite, admissibleWrites, durationSeconds, HookStateError, } from "./core/hook-state.js";
package/dist/hook.js CHANGED
@@ -15,15 +15,15 @@ exports.leafCommandsNormalized = exports.HookStateError = exports.durationSecond
15
15
  *
16
16
  * The roles, each with its own output type so a category mistake is a `tsc`
17
17
  * error, not a silent no-op:
18
- * - `defineHook` / `defineFileGate` — a **gate** returns a `Decision`
18
+ * - `experimental_defineHook` / `experimental_defineFileGate` — a **gate** returns a `Decision`
19
19
  * (`allow`/`deny`/`ask`); `deny` is the only thing that blocks.
20
- * - `definePromptGate` — a **prompt gate** (UserPromptSubmit) sees the prompt
20
+ * - `experimental_definePromptGate` — a **prompt gate** (UserPromptSubmit) sees the prompt
21
21
  * TEXT and may `deny` to block it (a security filter).
22
- * - `defineStopGate` — a **stop gate** (Stop/SubagentStop) may `deny` to keep
22
+ * - `experimental_defineStopGate` — a **stop gate** (Stop/SubagentStop) may `deny` to keep
23
23
  * the agent going (gate-until-tests-pass).
24
- * - `defineInject` — an **inject** returns an `Injection` (context text); it
24
+ * - `experimental_defineInject` — an **inject** returns an `Injection` (context text); it
25
25
  * has no `deny`, so "block on a SessionStart hook" won't compile.
26
- * - `defineReact` — a **react** (PostToolUse) returns a `Reaction`; it sees the
26
+ * - `experimental_defineReact` — a **react** (PostToolUse) returns a `Reaction`; it sees the
27
27
  * tool RESPONSE, its `run(cmd)` is effect-classified at construction, and it
28
28
  * can't block (the tool already ran).
29
29
  *
@@ -57,12 +57,23 @@ var hook_program_js_1 = require("./core/hook-program.js");
57
57
  // entry points makes the marking structural for the whole vocabulary. A
58
58
  // per-name prefix could not guarantee that; a chokepoint can.
59
59
  //
60
- // Import them aliased, so the word crosses the package boundary exactly once:
61
- // import { experimental_defineInject as defineInject, state } from "vigiles/hook";
62
- Object.defineProperty(exports, "experimental_defineHook", { enumerable: true, get: function () { return hook_program_js_1.defineHook; } });
63
- Object.defineProperty(exports, "experimental_defineFileGate", { enumerable: true, get: function () { return hook_program_js_1.defineFileGate; } });
64
- Object.defineProperty(exports, "experimental_definePromptGate", { enumerable: true, get: function () { return hook_program_js_1.definePromptGate; } });
65
- Object.defineProperty(exports, "experimental_defineStopGate", { enumerable: true, get: function () { return hook_program_js_1.defineStopGate; } });
60
+ // 🔴 DO NOT alias the prefix away at the import. This block used to advise
61
+ // exactly that — "so the word crosses the package boundary exactly once" — and
62
+ // the advice defeated the mechanism it was attached to. Measured 2026-08-21:
63
+ // with the alias in place the marker survived at 0 of 5 call sites in the only
64
+ // user-facing example, because a reader 200 lines down sees `defineHook(...)`
65
+ // and cannot tell it is provisional. A prefix that is stripped on import is a
66
+ // subpath with extra steps; if the guarantee is only boundary-deep, the honest
67
+ // shape is a quarantined subpath, not a name nobody sees. We chose the name,
68
+ // and then deleted the subpath (`vigiles/experimental`, gone 2026-08-21) so
69
+ // there is only the one mechanism left to keep honest.
70
+ // so the name has to be there. The declarations carry it too — there is one
71
+ // spelling of each symbol now, and `local/experimental-name` no longer needs
72
+ // to reason about re-export aliasing to know what crosses the boundary.
73
+ Object.defineProperty(exports, "experimental_defineHook", { enumerable: true, get: function () { return hook_program_js_1.experimental_defineHook; } });
74
+ Object.defineProperty(exports, "experimental_defineFileGate", { enumerable: true, get: function () { return hook_program_js_1.experimental_defineFileGate; } });
75
+ Object.defineProperty(exports, "experimental_definePromptGate", { enumerable: true, get: function () { return hook_program_js_1.experimental_definePromptGate; } });
76
+ Object.defineProperty(exports, "experimental_defineStopGate", { enumerable: true, get: function () { return hook_program_js_1.experimental_defineStopGate; } });
66
77
  Object.defineProperty(exports, "tool", { enumerable: true, get: function () { return hook_program_js_1.tool; } });
67
78
  Object.defineProperty(exports, "tools", { enumerable: true, get: function () { return hook_program_js_1.tools; } });
68
79
  Object.defineProperty(exports, "allow", { enumerable: true, get: function () { return hook_program_js_1.allow; } });
@@ -73,10 +84,10 @@ Object.defineProperty(exports, "pathView", { enumerable: true, get: function ()
73
84
  Object.defineProperty(exports, "gateAction", { enumerable: true, get: function () { return hook_program_js_1.gateAction; } });
74
85
  Object.defineProperty(exports, "hookMode", { enumerable: true, get: function () { return hook_program_js_1.hookMode; } });
75
86
  // inject vocabulary
76
- Object.defineProperty(exports, "experimental_defineInject", { enumerable: true, get: function () { return hook_program_js_1.defineInject; } });
87
+ Object.defineProperty(exports, "experimental_defineInject", { enumerable: true, get: function () { return hook_program_js_1.experimental_defineInject; } });
77
88
  Object.defineProperty(exports, "inject", { enumerable: true, get: function () { return hook_program_js_1.inject; } });
78
89
  // react vocabulary
79
- Object.defineProperty(exports, "experimental_defineReact", { enumerable: true, get: function () { return hook_program_js_1.defineReact; } });
90
+ Object.defineProperty(exports, "experimental_defineReact", { enumerable: true, get: function () { return hook_program_js_1.experimental_defineReact; } });
80
91
  Object.defineProperty(exports, "run", { enumerable: true, get: function () { return hook_program_js_1.run; } });
81
92
  Object.defineProperty(exports, "notice", { enumerable: true, get: function () { return hook_program_js_1.notice; } });
82
93
  Object.defineProperty(exports, "nothing", { enumerable: true, get: function () { return hook_program_js_1.nothing; } });
package/dist/linting.d.ts CHANGED
@@ -1,14 +1,43 @@
1
1
  /**
2
2
  * `vigiles/linting` — Pillar 1 entry point: the **linting layer** for instruction
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`).
3
+ * files. The spec builders/types that describe a CLAUDE.md, a SKILL.md or an
4
+ * agent, plus the public compile entry points, under one concern-named import.
6
5
  *
7
6
  * Curated (named, not `export *`) so the internal compiler validators, hash
8
7
  * helpers, and the linter cross-reference ENGINE stay out of the public surface,
9
8
  * the api reports, and the docs site (the CLI imports those from the source).
9
+ *
10
+ * 🔴 THAT SENTENCE USED TO BE FALSE, and so did the one after it (fixed
11
+ * 2026-08-21). The file claimed to be curated while `export * from
12
+ * "./core/spec.js"` sat one line below it, and it claimed "the spec builders are
13
+ * also at the package root (`vigiles`)" — measured against `vigiles.api.md`: 191
14
+ * exports there and zero matches for `claude`, `agent`, `enforce` or `result`.
15
+ * The builders' second door is `vigiles/spec`, not the root. Both claims read as
16
+ * documentation of a decision and were descriptions of the opposite one; a
17
+ * header nobody re-reads is where an `export *` hides best.
18
+ *
19
+ * WHAT THE CURATION DROPS (28 symbols, measured — nothing in this repo imported
20
+ * any of them from here; every in-repo user takes them from `vigiles/spec`, and
21
+ * the one live consumer of this subpath in the docs takes `compileAgent`): the
22
+ * typed-COMPOSITION family — `experimental_pipe`/`_pipeStep`/`_start`/
23
+ * `_andThen`/`_needs`, `Pipeline`, `PipeStep`, `Supplies`, `Handoff`,
24
+ * `NeedsContract`, `OkOf`, `TypedAgentSpec`, `TypedOutcome`, `Shape`,
25
+ * `OutputFieldType` and the `result()` builder. Those verify HANDOFFS between
26
+ * workers; they compile to nothing and lint nothing, so they are not pillar 1.
27
+ *
28
+ * ⚠️ The cut is not a clean slice along that line, and pretending otherwise
29
+ * would strand a signature: the `result()` FUNCTION leaves, but the
30
+ * `OutputContract` TYPE stays, because `compileAgent`/`compileSkill` name it in
31
+ * their own types. A consumer who needs to BUILD one imports `vigiles/spec`.
10
32
  */
11
- export * from "./core/spec.js";
33
+ export { instructionFile, prose, experimental_effect, enforce, guidance, guard, file, cmd, symbol, ref, dir, glob, project, experimental_skill, experimental_agent,
34
+ /** @deprecated Renamed to `instructionFile`. Removed one major AFTER the one that introduces it. */
35
+ claude,
36
+ /** @deprecated Renamed to `prose`. Removed one major AFTER the one that introduces it. */
37
+ instructions,
38
+ /** @deprecated Renamed to `experimental_agent`. Removed one major AFTER the one that introduces it. */
39
+ agent, } from "./core/spec.js";
40
+ export { BUILTIN_LINTERS, type BuiltinLinter, type LinterRule, type VigilesRef, type EnforcementRef, type KnownLinterRules, type KnownProjectFiles, type KnownNpmScripts, type KnownAgentName, type StrictLinterRule, type StrictFile, type StrictCmd, type ToolVocabulary, type OpenToolVocabulary, type AllowedAt, type AuthoredPurity, type EnforceRule, type GuidanceRule, type GuardRule, type Rule, type VerifiedPath, type VerifiedCmd, type VerifiedRef, type VerifiedDir, type VerifiedGlob, type FileRef, type CmdRef, type SkillRef, type SymbolRef, type DirRef, type GlobRef, type Ref, type EffectRegion, type InstructionFragment, type InstructionTarget, type ClaudeSpec, type Gate, type RoleGate, type ProjectRole, type SkillInput, type SkillStep, type SkillSpec, type SkillSpecInput, type AgentSpec, type AgentSpecInput, type Railway, type RailwayStep, type OutputContract, } from "./core/spec.js";
12
41
  export { compileClaude, compileSkill, compileAgent, compileRailway, CompileError, } from "./core/compile.js";
13
42
  export type { CompileClaudeOptions, CompileClaudeResult, CompileSkillResult, CompileAgentResult, CompileRailwayOptions, CompileRailwayResult, } from "./core/compile.js";
14
43
  //# sourceMappingURL=linting.d.ts.map
package/dist/linting.js CHANGED
@@ -1,32 +1,72 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.compileRailway = exports.compileAgent = exports.compileSkill = exports.compileClaude = void 0;
18
2
  /**
19
3
  * `vigiles/linting` — Pillar 1 entry point: the **linting layer** for instruction
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`).
4
+ * files. The spec builders/types that describe a CLAUDE.md, a SKILL.md or an
5
+ * agent, plus the public compile entry points, under one concern-named import.
23
6
  *
24
7
  * Curated (named, not `export *`) so the internal compiler validators, hash
25
8
  * helpers, and the linter cross-reference ENGINE stay out of the public surface,
26
9
  * the api reports, and the docs site (the CLI imports those from the source).
10
+ *
11
+ * 🔴 THAT SENTENCE USED TO BE FALSE, and so did the one after it (fixed
12
+ * 2026-08-21). The file claimed to be curated while `export * from
13
+ * "./core/spec.js"` sat one line below it, and it claimed "the spec builders are
14
+ * also at the package root (`vigiles`)" — measured against `vigiles.api.md`: 191
15
+ * exports there and zero matches for `claude`, `agent`, `enforce` or `result`.
16
+ * The builders' second door is `vigiles/spec`, not the root. Both claims read as
17
+ * documentation of a decision and were descriptions of the opposite one; a
18
+ * header nobody re-reads is where an `export *` hides best.
19
+ *
20
+ * WHAT THE CURATION DROPS (28 symbols, measured — nothing in this repo imported
21
+ * any of them from here; every in-repo user takes them from `vigiles/spec`, and
22
+ * the one live consumer of this subpath in the docs takes `compileAgent`): the
23
+ * typed-COMPOSITION family — `experimental_pipe`/`_pipeStep`/`_start`/
24
+ * `_andThen`/`_needs`, `Pipeline`, `PipeStep`, `Supplies`, `Handoff`,
25
+ * `NeedsContract`, `OkOf`, `TypedAgentSpec`, `TypedOutcome`, `Shape`,
26
+ * `OutputFieldType` and the `result()` builder. Those verify HANDOFFS between
27
+ * workers; they compile to nothing and lint nothing, so they are not pillar 1.
28
+ *
29
+ * ⚠️ The cut is not a clean slice along that line, and pretending otherwise
30
+ * would strand a signature: the `result()` FUNCTION leaves, but the
31
+ * `OutputContract` TYPE stays, because `compileAgent`/`compileSkill` name it in
32
+ * their own types. A consumer who needs to BUILD one imports `vigiles/spec`.
27
33
  */
28
- // The spec authoring builders (claude/enforce/guidance/file/cmd/agent/skill/…).
29
- __exportStar(require("./core/spec.js"), exports);
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.compileRailway = exports.compileAgent = exports.compileSkill = exports.compileClaude = exports.BUILTIN_LINTERS = exports.agent = exports.instructions = exports.claude = exports.experimental_agent = exports.experimental_skill = exports.project = exports.glob = exports.dir = exports.ref = exports.symbol = exports.cmd = exports.file = exports.guard = exports.guidance = exports.enforce = exports.experimental_effect = exports.prose = exports.instructionFile = void 0;
36
+ // --- the spec authoring builders: rules, refs, prose, and the three spec kinds ---
37
+ var spec_js_1 = require("./core/spec.js");
38
+ // instruction files
39
+ Object.defineProperty(exports, "instructionFile", { enumerable: true, get: function () { return spec_js_1.instructionFile; } });
40
+ Object.defineProperty(exports, "prose", { enumerable: true, get: function () { return spec_js_1.prose; } });
41
+ Object.defineProperty(exports, "experimental_effect", { enumerable: true, get: function () { return spec_js_1.experimental_effect; } });
42
+ // rules
43
+ Object.defineProperty(exports, "enforce", { enumerable: true, get: function () { return spec_js_1.enforce; } });
44
+ Object.defineProperty(exports, "guidance", { enumerable: true, get: function () { return spec_js_1.guidance; } });
45
+ Object.defineProperty(exports, "guard", { enumerable: true, get: function () { return spec_js_1.guard; } });
46
+ // verified references
47
+ Object.defineProperty(exports, "file", { enumerable: true, get: function () { return spec_js_1.file; } });
48
+ Object.defineProperty(exports, "cmd", { enumerable: true, get: function () { return spec_js_1.cmd; } });
49
+ Object.defineProperty(exports, "symbol", { enumerable: true, get: function () { return spec_js_1.symbol; } });
50
+ Object.defineProperty(exports, "ref", { enumerable: true, get: function () { return spec_js_1.ref; } });
51
+ Object.defineProperty(exports, "dir", { enumerable: true, get: function () { return spec_js_1.dir; } });
52
+ Object.defineProperty(exports, "glob", { enumerable: true, get: function () { return spec_js_1.glob; } });
53
+ Object.defineProperty(exports, "project", { enumerable: true, get: function () { return spec_js_1.project; } });
54
+ // the other two spec kinds + how a railway wires them
55
+ Object.defineProperty(exports, "experimental_skill", { enumerable: true, get: function () { return spec_js_1.experimental_skill; } });
56
+ // the subagent ROOT — `railway`/`delegate`/`result`/`pipe`… are its members
57
+ Object.defineProperty(exports, "experimental_agent", { enumerable: true, get: function () { return spec_js_1.experimental_agent; } });
58
+ // ─── ОКНО АЛИАСА (один мажор) — see core/spec.ts for why a window is not
59
+ // politeness here. Kept on THIS door too: a consumer importing `claude` from
60
+ // `vigiles/linting` never saw `vigiles/spec`, so the window over there does
61
+ // not cover them.
62
+ /** @deprecated Renamed to `instructionFile`. Removed one major AFTER the one that introduces it. */
63
+ Object.defineProperty(exports, "claude", { enumerable: true, get: function () { return spec_js_1.claude; } });
64
+ /** @deprecated Renamed to `prose`. Removed one major AFTER the one that introduces it. */
65
+ Object.defineProperty(exports, "instructions", { enumerable: true, get: function () { return spec_js_1.instructions; } });
66
+ /** @deprecated Renamed to `experimental_agent`. Removed one major AFTER the one that introduces it. */
67
+ Object.defineProperty(exports, "agent", { enumerable: true, get: function () { return spec_js_1.agent; } });
68
+ var spec_js_2 = require("./core/spec.js");
69
+ Object.defineProperty(exports, "BUILTIN_LINTERS", { enumerable: true, get: function () { return spec_js_2.BUILTIN_LINTERS; } });
30
70
  // Compile: only the public entry points + their option/result types.
31
71
  var compile_js_1 = require("./core/compile.js");
32
72
  Object.defineProperty(exports, "compileClaude", { enumerable: true, get: function () { return compile_js_1.compileClaude; } });
package/dist/load-hook.js CHANGED
@@ -56,7 +56,7 @@ async function loadHook(file) {
56
56
  const program = mod.default?.default ?? mod.default;
57
57
  if (!program || typeof program !== "object") {
58
58
  throw new hook_program_js_1.HookCompileError(`${file} has no default-exported hook program ` +
59
- `(use \`export default defineHook({…})\`).`);
59
+ `(use \`export default experimental_defineHook({…})\`).`);
60
60
  }
61
61
  // Remember WHERE it came from, so the assertion that later EVALUATES it can
62
62
  // attribute execution coverage without parsing anything. Remembering is not
@@ -151,7 +151,7 @@ function outcomeSection(input, contract) {
151
151
  ? `// TODO: assert the VALUES you expect (the shape is already validated above), e.g.:\n// assert.ok(value.${firstField}, "expected a ${firstField}");`
152
152
  : "";
153
153
  return `import assert from "node:assert/strict";
154
- import { result } from "vigiles/spec";
154
+ import { experimental_agent } from "vigiles/spec";\nconst { result } = experimental_agent;
155
155
  import { assertAgentOk } from "vigiles";
156
156
 
157
157
  // Reconstructed from ${input.name}'s ## Output contract (its compiled .md) — the
@@ -180,7 +180,7 @@ function fallbackSection(input) {
180
180
  return `import { runHarnessTest, assertToolUsed } from "vigiles";
181
181
 
182
182
  // ${input.name} has no result() contract, so its outcome can't be asserted
183
- // deterministically — add one (result() on its agent() spec) for a no-judge
183
+ // deterministically — add one (result() on its experimental_agent() spec) for a no-judge
184
184
  // outcome test. For now, assert it reaches for the right tool.
185
185
  const r = await runHarnessTest({
186
186
  plugin: ".", // TODO: the plugin dir holding this subagent
@@ -9,7 +9,7 @@ exports.parseDockerPort = parseDockerPort;
9
9
  exports.experimental_makeDockerRuntime = experimental_makeDockerRuntime;
10
10
  /**
11
11
  * vigiles — a Docker-backed {@link ContainerRuntime} for the R3 disposable-service
12
- * tier (⚠️ EXPERIMENTAL / UNSTABLE — see src/services.ts and `vigiles/experimental`).
12
+ * tier (⚠️ EXPERIMENTAL / UNSTABLE — see src/services.ts; served from `vigiles`).
13
13
  *
14
14
  * This is the v0 backend the R3 build spec (research/r3-disposable-services.md)
15
15
  * scopes: `docker run` a throwaway service, wait for it to be ready, run its seed,
@@ -23,7 +23,7 @@ exports.experimental_makeDockerRuntime = experimental_makeDockerRuntime;
23
23
  * end-to-end integration test needs a live daemon (it skips when absent).
24
24
  *
25
25
  * @experimental
26
- * @module vigiles/experimental (docker backend)
26
+ * @module vigiles (docker backend)
27
27
  */
28
28
  const node_child_process_1 = require("node:child_process");
29
29
  const node_net_1 = require("node:net");
@@ -2,7 +2,7 @@
2
2
  * vigiles — R3 disposable-service tier (⚠️ EXPERIMENTAL / UNSTABLE).
3
3
  *
4
4
  * ─────────────────────────────────────────────────────────────────────────────
5
- * EXPERIMENTAL: this surface is a DRAFT. Import it from `vigiles/experimental`,
5
+ * EXPERIMENTAL: this surface is a DRAFT. Import it from `vigiles`,
6
6
  * NOT from a stable subpath. It is NOT covered by the stability guarantee and
7
7
  * may change shape or be removed WITHOUT a major-version bump. Do not build a
8
8
  * production workflow on it yet. See docs/measuring-skills.md § Experimental.
@@ -44,7 +44,7 @@
44
44
  * side-effect-free).
45
45
  *
46
46
  * @experimental
47
- * @module vigiles/experimental (services)
47
+ * @module vigiles (services)
48
48
  */
49
49
  /**
50
50
  * How a service signals it is ready to accept work — polled by the