vigiles 6.0.0 → 8.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/README.md +189 -88
- package/dist/action-gate.js +1 -1
- package/dist/adapters/claude-code/agent-runtime.d.ts +46 -11
- package/dist/adapters/claude-code/agent-runtime.js +95 -24
- package/dist/adapters/claude-code/effect-region.js +1 -1
- package/dist/adapters/claude-code/skill-runtime.d.ts +1 -1
- package/dist/adapters/claude-code/skill-runtime.js +1 -1
- package/dist/adapters/codex/hook-protocol.js +3 -0
- package/dist/adapters/codex/mock-model.js +1 -1
- package/dist/cli-commands.d.ts +19 -0
- package/dist/cli-commands.js +47 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1054 -201
- package/dist/core/adopt.d.ts +65 -0
- package/dist/core/adopt.js +199 -0
- package/dist/core/bash-effects.d.ts +12 -0
- package/dist/core/bash-effects.js +31 -0
- package/dist/core/capability-diff.d.ts +46 -0
- package/dist/core/capability-diff.js +97 -0
- package/dist/core/compose.d.ts +1 -1
- package/dist/core/compose.js +1 -1
- package/dist/core/evolve.d.ts +4 -0
- package/dist/core/evolve.js +4 -0
- package/dist/core/frontmatter.d.ts +8 -7
- package/dist/core/frontmatter.js +8 -7
- package/dist/core/generate-harness.d.ts +1 -1
- package/dist/core/generate-harness.js +3 -3
- package/dist/core/generate-schema.js +1 -1
- package/dist/core/guards.d.ts +126 -0
- package/dist/core/guards.js +309 -0
- package/dist/core/harness-driver.d.ts +1 -1
- package/dist/core/hook-program.d.ts +459 -0
- package/dist/core/hook-program.js +468 -0
- package/dist/core/hook-protocol.d.ts +7 -0
- package/dist/core/hook-providers.d.ts +138 -0
- package/dist/core/hook-providers.js +155 -0
- package/dist/core/hook-spec.d.ts +74 -0
- package/dist/core/hook-spec.js +130 -0
- package/dist/core/inline.d.ts +6 -6
- package/dist/core/inline.js +7 -7
- package/dist/core/integrity.d.ts +31 -0
- package/dist/core/integrity.js +45 -0
- package/dist/core/mcp-tool.d.ts +12 -0
- package/dist/core/mcp-tool.js +20 -0
- package/dist/core/mcp.d.ts +13 -0
- package/dist/core/mcp.js +67 -0
- package/dist/core/orphans.js +1 -1
- package/dist/core/spec.d.ts +40 -2
- package/dist/core/spec.js +16 -1
- package/dist/core/types.d.ts +37 -5
- package/dist/core/validate.js +26 -26
- package/dist/dialect-drift.d.ts +65 -0
- package/dist/dialect-drift.js +216 -0
- package/dist/eval.d.ts +40 -5
- package/dist/eval.js +59 -5
- package/dist/guardrail-check.d.ts +85 -0
- package/dist/guardrail-check.js +152 -0
- package/dist/harness-assert.d.ts +10 -0
- package/dist/harness-assert.js +30 -0
- package/dist/hook-install.d.ts +43 -0
- package/dist/hook-install.js +91 -0
- package/dist/hook.d.ts +52 -0
- package/dist/hook.js +98 -0
- package/dist/leaderboard.d.ts +6 -0
- package/dist/leaderboard.js +43 -1
- package/dist/linting.d.ts +9 -5
- package/dist/linting.js +17 -5
- package/dist/optimize.js +1 -1
- package/dist/scaffold-test.js +21 -7
- package/dist/scan-behavioral.d.ts +60 -0
- package/dist/scan-behavioral.js +239 -1
- package/dist/scan-trigger-suggest.d.ts +54 -0
- package/dist/scan-trigger-suggest.js +70 -0
- package/dist/scan.d.ts +31 -1
- package/dist/scan.js +65 -3
- package/dist/score-explainer.js +1 -1
- package/dist/self-command-refs.d.ts +21 -0
- package/dist/self-command-refs.js +125 -0
- package/dist/setup-plan.d.ts +59 -1
- package/dist/setup-plan.js +103 -5
- package/dist/testing.d.ts +5 -3
- package/dist/testing.js +37 -23
- package/dist/tool-intercept.d.ts +4 -4
- package/dist/tool-intercept.js +5 -5
- package/dist/unit.d.ts +2 -0
- package/dist/unit.js +8 -1
- package/hooks/post-edit.sh +1 -1
- package/hooks/refs-nudge.sh +1 -1
- package/package.json +5 -3
- package/skills/adopt-spec/SKILL.md +7 -7
- package/skills/linter-docs/eslint.md +1 -1
- package/skills/strengthen/SKILL.md +1 -1
package/dist/core/orphans.js
CHANGED
|
@@ -30,7 +30,7 @@ const DEFAULT_IGNORE = [
|
|
|
30
30
|
];
|
|
31
31
|
/**
|
|
32
32
|
* A doc carrying this marker opts out of orphan detection — the inline escape
|
|
33
|
-
* hatch, mirroring `vigiles-disable require-spec` and `vigiles:ignore-test`.
|
|
33
|
+
* hatch, mirroring `vigiles-disable require-instructions-spec` and `vigiles:ignore-test`.
|
|
34
34
|
* Use it for an intentionally-unreferenced doc (a changelog, a top-level index)
|
|
35
35
|
* that nothing else links to but is not rot.
|
|
36
36
|
*/
|
package/dist/core/spec.d.ts
CHANGED
|
@@ -135,7 +135,7 @@ export declare function guidance(text: string): GuidanceRule;
|
|
|
135
135
|
* Declare a reactive guard: runs a command when watched files change.
|
|
136
136
|
*
|
|
137
137
|
* guard({ watch: "*.spec.ts", run: "npx vigiles compile" }, "Recompile on spec change")
|
|
138
|
-
* guard({ watch: ["eslint.config.*", "package.json"], run: "npx vigiles generate
|
|
138
|
+
* guard({ watch: ["eslint.config.*", "package.json"], run: "npx vigiles generate types" }, "Regen types")
|
|
139
139
|
*/
|
|
140
140
|
export declare function guard(options: {
|
|
141
141
|
watch: string | readonly string[];
|
|
@@ -237,6 +237,9 @@ export declare function glob(pattern: string): GlobRef;
|
|
|
237
237
|
* the region the unit is treated as read-only (the `"pure"` effective floor),
|
|
238
238
|
* inside it the declared purity floor applies. The position-aware companion to
|
|
239
239
|
* the per-call `purity` floor. See `research/effect-boundary-design.md`.
|
|
240
|
+
*
|
|
241
|
+
* @internal Experimental (parked P3) — NOT part of the frozen public surface;
|
|
242
|
+
* may change or be removed without a major bump pre-1.0.
|
|
240
243
|
*/
|
|
241
244
|
export interface EffectRegion {
|
|
242
245
|
readonly _ref: "effect";
|
|
@@ -269,6 +272,9 @@ export declare function instructions(strings: TemplateStringsArray, ...values: I
|
|
|
269
272
|
* Returns an `EffectRegion` fragment; `compile` wraps its rendered body in
|
|
270
273
|
* `<!-- vigiles:effect -->` markers. Independent of the `doc()` authoring
|
|
271
274
|
* surface — it does not block on it.
|
|
275
|
+
*
|
|
276
|
+
* @internal Experimental (parked P3) — NOT part of the frozen public surface;
|
|
277
|
+
* may change or be removed without a major bump pre-1.0.
|
|
272
278
|
*/
|
|
273
279
|
export declare function effect(strings: TemplateStringsArray, ...values: InstructionFragment[]): EffectRegion;
|
|
274
280
|
/**
|
|
@@ -732,6 +738,9 @@ export declare function railway(spec: Omit<Railway, "_specType">): Railway;
|
|
|
732
738
|
* Declared via `needs(...)` and threaded into the typed agent so `pipe` can
|
|
733
739
|
* cross-reference it. Independent of `result()` (the output) — an agent both
|
|
734
740
|
* `needs` an input shape and produces an `ok`/`err` output shape.
|
|
741
|
+
*
|
|
742
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
743
|
+
* public API (pre-1.0); may change without a major bump.
|
|
735
744
|
*/
|
|
736
745
|
export type NeedsContract<N extends Shape> = N;
|
|
737
746
|
/**
|
|
@@ -740,12 +749,18 @@ export type NeedsContract<N extends Shape> = N;
|
|
|
740
749
|
* step with no upstream requirement — valid as the FIRST step of a pipeline.
|
|
741
750
|
*
|
|
742
751
|
* needs({ plan: "string", files: "string[]" })
|
|
752
|
+
*
|
|
753
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
754
|
+
* public API (pre-1.0); may change without a major bump.
|
|
743
755
|
*/
|
|
744
756
|
export declare function needs<const N extends Shape>(shape: N): NeedsContract<N>;
|
|
745
757
|
/**
|
|
746
758
|
* A typed pipeline step: a `TypedAgentSpec` paired with the input `needs` it
|
|
747
759
|
* reads from the prior step's `ok`. `step()` builds one; `pipe` checks that the
|
|
748
760
|
* prior step's `ok` shape supplies this step's `needs`.
|
|
761
|
+
*
|
|
762
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
763
|
+
* public API (pre-1.0); may change without a major bump.
|
|
749
764
|
*/
|
|
750
765
|
export interface PipeStep<Needs extends Shape, Ok extends Shape, Err extends Shape> {
|
|
751
766
|
readonly _step: "typed-delegate";
|
|
@@ -758,6 +773,9 @@ export interface PipeStep<Needs extends Shape, Ok extends Shape, Err extends Sha
|
|
|
758
773
|
* second is the `needs(...)` input contract.
|
|
759
774
|
*
|
|
760
775
|
* pipeStep(implementer, needs({ plan: "string", files: "string[]" }))
|
|
776
|
+
*
|
|
777
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
778
|
+
* public API (pre-1.0); may change without a major bump.
|
|
761
779
|
*/
|
|
762
780
|
export declare function pipeStep<Needs extends Shape, Ok extends Shape, Err extends Shape>(a: TypedAgentSpec<Ok, Err>, needsContract?: Needs): PipeStep<Needs, Ok, Err>;
|
|
763
781
|
/**
|
|
@@ -766,6 +784,9 @@ export declare function pipeStep<Needs extends Shape, Ok extends Shape, Err exte
|
|
|
766
784
|
* descriptive error object naming the offending field (`__missing` /
|
|
767
785
|
* `__mismatch`), which surfaces at the mismatched call. Shallow (a per-field
|
|
768
786
|
* mapped type, not a recursion) to avoid TS2589.
|
|
787
|
+
*
|
|
788
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
789
|
+
* public API (pre-1.0); may change without a major bump.
|
|
769
790
|
*/
|
|
770
791
|
export type Supplies<Producer extends Shape, Consumer extends Shape> = {
|
|
771
792
|
[K in keyof Consumer]: K extends keyof Producer ? Producer[K] extends Consumer[K] ? true : {
|
|
@@ -788,12 +809,17 @@ export type Supplies<Producer extends Shape, Consumer extends Shape> = {
|
|
|
788
809
|
* assigning `true` to it is a `tsc` error at edit time. Shallow (one wrap over
|
|
789
810
|
* the per-field `Supplies` mapped type, no recursion); the generator emits one
|
|
790
811
|
* assertion per consecutive step pair (O(N)), keeping clear of TS2589.
|
|
812
|
+
*
|
|
813
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
814
|
+
* public API (pre-1.0); may change without a major bump.
|
|
791
815
|
*/
|
|
792
816
|
export type Handoff<Producer extends Shape, Consumer extends Shape> = Supplies<Producer, Consumer> extends true ? true : {
|
|
793
817
|
readonly __handoff_error: Supplies<Producer, Consumer>;
|
|
794
818
|
};
|
|
795
819
|
/** A typed pipeline value — carries the LAST step's `ok` and the UNION of every
|
|
796
|
-
* step's `err` (any step can short-circuit to the error track).
|
|
820
|
+
* step's `err` (any step can short-circuit to the error track).
|
|
821
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
822
|
+
* public API (pre-1.0); may change without a major bump. */
|
|
797
823
|
export interface Pipeline<Ok extends Shape, Err extends Shape> {
|
|
798
824
|
readonly _specType: "pipeline";
|
|
799
825
|
/** Ordered agent names — the resolved compose order. */
|
|
@@ -809,6 +835,9 @@ export interface Pipeline<Ok extends Shape, Err extends Shape> {
|
|
|
809
835
|
* Begin a typed pipeline from its first step. The first step has no upstream, so
|
|
810
836
|
* its `needs` must be empty (`needs({})` or omitted). Returns a `Pipeline`
|
|
811
837
|
* carrying that step's `ok`/`err` forward.
|
|
838
|
+
*
|
|
839
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
840
|
+
* public API (pre-1.0); may change without a major bump.
|
|
812
841
|
*/
|
|
813
842
|
export declare function start<Ok extends Shape, Err extends Shape>(first: PipeStep<Record<string, never>, Ok, Err> | TypedAgentSpec<Ok, Err>): Pipeline<Ok, Err>;
|
|
814
843
|
/**
|
|
@@ -821,6 +850,9 @@ export declare function start<Ok extends Shape, Err extends Shape>(first: PipeSt
|
|
|
821
850
|
* Named `andThen` (Wlaschin's railway `bind`/`andThen`), NOT `then`: a module
|
|
822
851
|
* exporting a function called `then` becomes a thenable, so `await import()` of
|
|
823
852
|
* any barrel re-exporting it would invoke it — a footgun the rename avoids.
|
|
853
|
+
*
|
|
854
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
855
|
+
* public API (pre-1.0); may change without a major bump.
|
|
824
856
|
*/
|
|
825
857
|
export declare function andThen<PriorOk extends Shape, PriorErr extends Shape, Needs extends Shape, Ok extends Shape, Err extends Shape>(prior: Pipeline<PriorOk, PriorErr>, next: Supplies<PriorOk, Needs> extends true ? PipeStep<Needs, Ok, Err> : {
|
|
826
858
|
readonly __HANDOFF_ERROR: Supplies<PriorOk, Needs>;
|
|
@@ -841,6 +873,9 @@ export declare function andThen<PriorOk extends Shape, PriorErr extends Shape, N
|
|
|
841
873
|
* pipeStep(implementer, needs({ plan: "string", files: "string[]" })),
|
|
842
874
|
* pipeStep(reviewer, needs({ diff: "string" })),
|
|
843
875
|
* ) // ← won't compile if a handoff doesn't line up
|
|
876
|
+
*
|
|
877
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
878
|
+
* public API (pre-1.0); may change without a major bump.
|
|
844
879
|
*/
|
|
845
880
|
export declare function pipe<A extends Shape, AE extends Shape>(a: TypedAgentSpec<A, AE>): Pipeline<A, AE>;
|
|
846
881
|
export declare function pipe<A extends Shape, AE extends Shape, BN extends Shape, B extends Shape, BE extends Shape>(a: TypedAgentSpec<A, AE>, b: Supplies<A, BN> extends true ? PipeStep<BN, B, BE> : {
|
|
@@ -866,6 +901,9 @@ export declare function pipe<A extends Shape, AE extends Shape, BN extends Shape
|
|
|
866
901
|
* object naming the dangling target + the railway it came from — so assigning
|
|
867
902
|
* `true` to it is a `tsc` error at edit time. Shallow (one conditional, no
|
|
868
903
|
* recursion); the generator emits one assertion per edge (O(N)).
|
|
904
|
+
*
|
|
905
|
+
* @internal Experimental whole-harness-codegen surface — NOT part of the frozen
|
|
906
|
+
* public API (pre-1.0); may change without a major bump.
|
|
869
907
|
*/
|
|
870
908
|
export type KnownAgentName<Target extends string, Names extends string, From extends string = string> = [Target] extends [Names] ? true : {
|
|
871
909
|
readonly __dangling_delegate: Target;
|
package/dist/core/spec.js
CHANGED
|
@@ -73,7 +73,7 @@ function guidance(text) {
|
|
|
73
73
|
* Declare a reactive guard: runs a command when watched files change.
|
|
74
74
|
*
|
|
75
75
|
* guard({ watch: "*.spec.ts", run: "npx vigiles compile" }, "Recompile on spec change")
|
|
76
|
-
* guard({ watch: ["eslint.config.*", "package.json"], run: "npx vigiles generate
|
|
76
|
+
* guard({ watch: ["eslint.config.*", "package.json"], run: "npx vigiles generate types" }, "Regen types")
|
|
77
77
|
*/
|
|
78
78
|
function guard(options, description) {
|
|
79
79
|
return {
|
|
@@ -166,6 +166,9 @@ function instructions(strings, ...values) {
|
|
|
166
166
|
* Returns an `EffectRegion` fragment; `compile` wraps its rendered body in
|
|
167
167
|
* `<!-- vigiles:effect -->` markers. Independent of the `doc()` authoring
|
|
168
168
|
* surface — it does not block on it.
|
|
169
|
+
*
|
|
170
|
+
* @internal Experimental (parked P3) — NOT part of the frozen public surface;
|
|
171
|
+
* may change or be removed without a major bump pre-1.0.
|
|
169
172
|
*/
|
|
170
173
|
function effect(strings, ...values) {
|
|
171
174
|
const body = [];
|
|
@@ -304,6 +307,9 @@ function railway(spec) {
|
|
|
304
307
|
* step with no upstream requirement — valid as the FIRST step of a pipeline.
|
|
305
308
|
*
|
|
306
309
|
* needs({ plan: "string", files: "string[]" })
|
|
310
|
+
*
|
|
311
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
312
|
+
* public API (pre-1.0); may change without a major bump.
|
|
307
313
|
*/
|
|
308
314
|
function needs(shape) {
|
|
309
315
|
return shape;
|
|
@@ -314,6 +320,9 @@ function needs(shape) {
|
|
|
314
320
|
* second is the `needs(...)` input contract.
|
|
315
321
|
*
|
|
316
322
|
* pipeStep(implementer, needs({ plan: "string", files: "string[]" }))
|
|
323
|
+
*
|
|
324
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
325
|
+
* public API (pre-1.0); may change without a major bump.
|
|
317
326
|
*/
|
|
318
327
|
function pipeStep(a, needsContract = {}) {
|
|
319
328
|
return { _step: "typed-delegate", agent: a, needs: needsContract };
|
|
@@ -322,6 +331,9 @@ function pipeStep(a, needsContract = {}) {
|
|
|
322
331
|
* Begin a typed pipeline from its first step. The first step has no upstream, so
|
|
323
332
|
* its `needs` must be empty (`needs({})` or omitted). Returns a `Pipeline`
|
|
324
333
|
* carrying that step's `ok`/`err` forward.
|
|
334
|
+
*
|
|
335
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
336
|
+
* public API (pre-1.0); may change without a major bump.
|
|
325
337
|
*/
|
|
326
338
|
function start(first) {
|
|
327
339
|
const step = "_step" in first ? first : pipeStep(first, {});
|
|
@@ -347,6 +359,9 @@ function start(first) {
|
|
|
347
359
|
* Named `andThen` (Wlaschin's railway `bind`/`andThen`), NOT `then`: a module
|
|
348
360
|
* exporting a function called `then` becomes a thenable, so `await import()` of
|
|
349
361
|
* any barrel re-exporting it would invoke it — a footgun the rename avoids.
|
|
362
|
+
*
|
|
363
|
+
* @internal Experimental typed-composition surface — NOT part of the frozen
|
|
364
|
+
* public API (pre-1.0); may change without a major bump.
|
|
350
365
|
*/
|
|
351
366
|
function andThen(prior, next) {
|
|
352
367
|
const real = next;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -82,12 +82,23 @@ export interface TestCoverageConfig {
|
|
|
82
82
|
exclude?: readonly string[];
|
|
83
83
|
}
|
|
84
84
|
export interface RulesConfig {
|
|
85
|
-
/** Require .spec.ts for CLAUDE.md / AGENTS.md. Default: "warn". */
|
|
86
|
-
"require-spec"?: RuleSeverity;
|
|
87
85
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
86
|
+
* Require a `.spec.ts` behind each instruction file (CLAUDE.md / AGENTS.md) —
|
|
87
|
+
* the file must be compiled from a typed spec, not hand-written. NARROW: only a
|
|
88
|
+
* `.spec.ts` sibling (or an explicit `<!-- vigiles-disable
|
|
89
|
+
* require-instructions-spec -->` marker) satisfies it; inline
|
|
90
|
+
* `<!-- vigiles:enforce -->` comments do NOT (the rule name says "spec"). Default:
|
|
91
|
+
* "warn". `vigiles init` auto-adopts every instruction file into a spec, so this
|
|
92
|
+
* is GREEN by construction after setup — a safety net for a NEW hand-added file,
|
|
93
|
+
* not a nag. The workflow-tier opt-in (gated under `--strict`).
|
|
94
|
+
*/
|
|
95
|
+
"require-instructions-spec"?: RuleSeverity;
|
|
96
|
+
/**
|
|
97
|
+
* Require a `.spec.ts` behind each SKILL.md — the consistent
|
|
98
|
+
* `require-<surface>-spec` parallel to `require-instructions-spec`. Default:
|
|
99
|
+
* false (OFF): skills are legitimately hand-written, and the coverage that
|
|
100
|
+
* matters ("every skill ships with a test/eval") is the `untested-skill` rule.
|
|
101
|
+
* Set it explicitly if your team wants every skill spec-managed.
|
|
91
102
|
*/
|
|
92
103
|
"require-skill-spec"?: RuleSeverity;
|
|
93
104
|
/** Detect hand-edits to compiled markdown via SHA-256 hash. Default: "warn". */
|
|
@@ -161,6 +172,18 @@ export interface RulesConfig {
|
|
|
161
172
|
* "warn"; "error" gates CI. Same detector as `scan` (hooks status "missing").
|
|
162
173
|
*/
|
|
163
174
|
"hook-script-exists"?: RuleSeverity;
|
|
175
|
+
/**
|
|
176
|
+
* A single repo-level RECOMMENDATION (one finding regardless of hook count):
|
|
177
|
+
* when a plugin/repo ships hand-written hook commands that aren't compiled
|
|
178
|
+
* `vigiles/hook` artifacts, nudge toward compiled hooks — they make whole hook
|
|
179
|
+
* bug classes (exit-1-not-2, wrong decision field, matcher bypass) UNREPRESENTABLE
|
|
180
|
+
* at authoring time, and `guardrail-check` proves an existing one blocks. A
|
|
181
|
+
* discovery nudge, not a defect: the hand-written shell lane stays first-class,
|
|
182
|
+
* so it's opt-out and fires ONCE (never per-hook). The message links
|
|
183
|
+
* `docs/compiled-hooks.md`. Default "warn"; set "off" to silence or "error" to
|
|
184
|
+
* enforce. Same detector as `scan` (manualHookCount).
|
|
185
|
+
*/
|
|
186
|
+
"prefer-compiled-hooks"?: RuleSeverity;
|
|
164
187
|
/**
|
|
165
188
|
* Cross-reference a subagent's `disallowedTools:` block-list against the
|
|
166
189
|
* catalog — the deny-side mirror of `subagent-tool-contract`. A close typo there
|
|
@@ -221,6 +244,15 @@ export interface VigilesConfig {
|
|
|
221
244
|
}>;
|
|
222
245
|
/** Orphan-docs check configuration. Include/exclude globs, tsconfig-style. */
|
|
223
246
|
orphans?: OrphansConfig;
|
|
247
|
+
/**
|
|
248
|
+
* Glob patterns of instruction/skill files to EXCLUDE from `lint` discovery
|
|
249
|
+
* (tsconfig-style, relative to the repo root). Use it for vendored or
|
|
250
|
+
* benchmark fixtures the repo's own lint shouldn't police — e.g.
|
|
251
|
+
* `["bench/**"]` so a third-party `CLAUDE.md` injected verbatim as a benchmark
|
|
252
|
+
* arm isn't held to `require-instructions-spec`. `node_modules`/`dist` are always
|
|
253
|
+
* excluded.
|
|
254
|
+
*/
|
|
255
|
+
exclude?: readonly string[];
|
|
224
256
|
/**
|
|
225
257
|
* The harness(es) this repo targets — selects the compile dialect / skill
|
|
226
258
|
* frontmatter profile / instruction-file shape, instead of sniffing the cwd.
|
package/dist/core/validate.js
CHANGED
|
@@ -11,8 +11,6 @@ const node_fs_1 = require("node:fs");
|
|
|
11
11
|
const glob_1 = require("glob");
|
|
12
12
|
const node_path_1 = require("node:path");
|
|
13
13
|
const cosmiconfig_1 = require("cosmiconfig");
|
|
14
|
-
const inline_js_1 = require("./inline.js");
|
|
15
|
-
const frontmatter_js_1 = require("./frontmatter.js");
|
|
16
14
|
// ---------------------------------------------------------------------------
|
|
17
15
|
// Constants & regex
|
|
18
16
|
// ---------------------------------------------------------------------------
|
|
@@ -31,13 +29,13 @@ const INSTRUCTION_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
|
31
29
|
// The default instruction file to validate when no config names one.
|
|
32
30
|
const DEFAULT_FILES = [INSTRUCTION_FILES[0]];
|
|
33
31
|
const DEFAULT_RULES = {
|
|
34
|
-
"require-spec": "warn",
|
|
35
|
-
//
|
|
36
|
-
// so requiring a .spec.ts per SKILL.md
|
|
37
|
-
//
|
|
38
|
-
// `untested-*` rules
|
|
39
|
-
// eval"
|
|
40
|
-
//
|
|
32
|
+
"require-instructions-spec": "warn",
|
|
33
|
+
// Default OFF — the consistent `require-<surface>-spec` parallel. Skills are
|
|
34
|
+
// legitimately hand-written, so requiring a .spec.ts per SKILL.md is the wrong
|
|
35
|
+
// default (it would nag about vendored/fixture/bench skills); the coverage that
|
|
36
|
+
// matters is the `untested-*` rules ("every skill/agent/hook ships with a test
|
|
37
|
+
// or eval"). Set `require-skill-spec` explicitly if your team wants every skill
|
|
38
|
+
// spec-managed.
|
|
41
39
|
"require-skill-spec": false,
|
|
42
40
|
integrity: "warn",
|
|
43
41
|
coverage: false,
|
|
@@ -60,6 +58,10 @@ const DEFAULT_RULES = {
|
|
|
60
58
|
"mcp-tool-resolves": "warn",
|
|
61
59
|
// A hook script referenced but missing never runs — on by default at warn.
|
|
62
60
|
"hook-script-exists": "warn",
|
|
61
|
+
// Discovery nudge toward compiled hooks (one finding) — default OFF: it's a
|
|
62
|
+
// recommendation, not a defect (the hand-written shell lane stays first-class),
|
|
63
|
+
// so it shouldn't fire unasked. Set "warn"/"error" to opt in.
|
|
64
|
+
"prefer-compiled-hooks": false,
|
|
63
65
|
// High-precision (close-typo only) deny-list mirror of subagent-tool-contract.
|
|
64
66
|
"disallowed-tools-contract": "warn",
|
|
65
67
|
// Deterministic NCD precision proxy (near-identical skill descriptions) — warn.
|
|
@@ -176,26 +178,25 @@ function validate(content, { ruleMarkers, rules: rulesConfig, filePath, dialect
|
|
|
176
178
|
const missingCount = parsedRules.filter((r) => r.enforcement === "missing").length;
|
|
177
179
|
const errors = [];
|
|
178
180
|
const warnings = [];
|
|
179
|
-
const disableComment = /<!--\s*vigiles-disable\s+require-spec\s*-->/;
|
|
181
|
+
const disableComment = /<!--\s*vigiles-disable\s+require-instructions-spec\s*-->/;
|
|
180
182
|
if (filePath) {
|
|
181
183
|
const basename = (0, node_path_1.basename)(filePath);
|
|
182
184
|
const recognized = dialect?.instructionTargets ?? INSTRUCTION_FILES;
|
|
183
185
|
const isInstruction = recognized.includes(basename);
|
|
184
186
|
const isSkill = basename === "SKILL.md";
|
|
185
|
-
// --- require-spec (CLAUDE.md / AGENTS.md) ---
|
|
186
|
-
|
|
187
|
+
// --- require-instructions-spec (CLAUDE.md / AGENTS.md) ---
|
|
188
|
+
// NARROW: only a `.spec.ts` sibling satisfies it. The rule name says "spec",
|
|
189
|
+
// so inline `<!-- vigiles:enforce -->` / `vigiles:` frontmatter do NOT count
|
|
190
|
+
// (a user on inline mode keeps this rule off — it's a workflow-tier opt-in).
|
|
191
|
+
// `vigiles init` auto-adopts every instruction file into a spec, so this is
|
|
192
|
+
// green by construction after setup.
|
|
193
|
+
const specSeverity = activeRules["require-instructions-spec"];
|
|
187
194
|
if (specSeverity && isInstruction && !disableComment.test(content)) {
|
|
188
195
|
const specPath = filePath + ".spec.ts";
|
|
189
|
-
|
|
190
|
-
// `<!-- vigiles:enforce ... -->` comment means the file is
|
|
191
|
-
// verified on `vigiles lint` even without a .spec.ts sibling.
|
|
192
|
-
// Delegate to the real parser so a malformed marker can't
|
|
193
|
-
// satisfy require-spec with a rule that lint can't verify.
|
|
194
|
-
const hasInline = (0, inline_js_1.hasInlineRules)(content) || (0, frontmatter_js_1.hasFrontmatterRules)(content);
|
|
195
|
-
if (!(0, node_fs_1.existsSync)(specPath) && !hasInline) {
|
|
196
|
+
if (!(0, node_fs_1.existsSync)(specPath)) {
|
|
196
197
|
const msg = {
|
|
197
|
-
rule: "require-spec",
|
|
198
|
-
message: `No spec file found for "${filePath}". Expected "${specPath}". Run \`npx vigiles init --target=${filePath}\` to
|
|
198
|
+
rule: "require-instructions-spec",
|
|
199
|
+
message: `No spec file found for "${filePath}". Expected "${specPath}". Run \`npx vigiles init --target=${filePath}\` to adopt it into a spec, or disable with <!-- vigiles-disable require-instructions-spec -->.`,
|
|
199
200
|
line: 1,
|
|
200
201
|
};
|
|
201
202
|
if (specSeverity === "error") {
|
|
@@ -206,9 +207,8 @@ function validate(content, { ruleMarkers, rules: rulesConfig, filePath, dialect
|
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
}
|
|
209
|
-
// --- require-skill-spec (SKILL.md) —
|
|
210
|
-
//
|
|
211
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
210
|
+
// --- require-skill-spec (SKILL.md) — the consistent require-<surface>-spec
|
|
211
|
+
// parallel, off by default; honored when a user sets it explicitly.
|
|
212
212
|
const skillSeverity = activeRules["require-skill-spec"];
|
|
213
213
|
if (skillSeverity && isSkill && !disableComment.test(content)) {
|
|
214
214
|
const specPath = filePath + ".spec.ts";
|
|
@@ -302,7 +302,7 @@ function validatePaths(paths, { followSymlinks = false, ruleMarkers, rules: rule
|
|
|
302
302
|
let allValid = true;
|
|
303
303
|
// Maps a real (symlink-resolved) path → the first path validated for it, so a
|
|
304
304
|
// symlinked/synced CLAUDE.md⇄AGENTS.md mirror is validated ONCE on the real
|
|
305
|
-
// file instead of double-firing require-spec on the mirror's name (sync-tool-
|
|
305
|
+
// file instead of double-firing require-instructions-spec on the mirror's name (sync-tool-
|
|
306
306
|
// compatibility.md req 7). Recorded only on a successful validation, so a
|
|
307
307
|
// symlink seen first (and skipped) never shadows its real target.
|
|
308
308
|
const seenReal = new Map();
|
|
@@ -338,7 +338,7 @@ function validatePaths(paths, { followSymlinks = false, ruleMarkers, rules: rule
|
|
|
338
338
|
allValid = false;
|
|
339
339
|
continue;
|
|
340
340
|
}
|
|
341
|
-
// Attribute require-spec/integrity to the REAL file when this path is a
|
|
341
|
+
// Attribute require-instructions-spec/integrity to the REAL file when this path is a
|
|
342
342
|
// symlink, so a symlinked AGENTS.md resolves to CLAUDE.md's spec rather than
|
|
343
343
|
// a nonexistent AGENTS.md.spec.ts. Non-symlinks keep the original path
|
|
344
344
|
// verbatim (behaviour-preserving).
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Claude Code version `ACKNOWLEDGED_TOOL_INPUT_TYPES` + the dialect were last
|
|
3
|
+
* validated against. SINGLE SOURCE OF TRUTH for the pin: CI installs
|
|
4
|
+
* `@anthropic-ai/claude-code@<this>` (grepped from this line) in every job that
|
|
5
|
+
* drives the real binary, so the dialect-drift alarm fires only on a DELIBERATE
|
|
6
|
+
* bump — not on every unpinned CC release landing on an unrelated PR — and the
|
|
7
|
+
* real-`claude` harness/eval tests stay reproducible. Bump this together with
|
|
8
|
+
* `ACKNOWLEDGED_TOOL_INPUT_TYPES` (the gated test cross-checks them).
|
|
9
|
+
*/
|
|
10
|
+
export declare const VALIDATED_CC_VERSION = "2.1.187";
|
|
11
|
+
/**
|
|
12
|
+
* The `<X>Input` interface names we've ACKNOWLEDGED from `sdk-tools.d.ts` (Claude
|
|
13
|
+
* Code 2.1.187). The drift test fails when the installed set differs — a loud nudge
|
|
14
|
+
* to re-check `claudeCodeDialect` (and update this set) when CC adds/removes a tool.
|
|
15
|
+
* NOT a redistribution of their file: a list of bare identifiers (facts), authored here.
|
|
16
|
+
*
|
|
17
|
+
* 2.1.187 added the agent-PLATFORM surface (cron/scheduling/worktrees/web-app):
|
|
18
|
+
* Artifact, Cron{Create,Delete,List}, Enter/ExitWorktree, EnterPlanMode, Monitor,
|
|
19
|
+
* Projects, PushNotification, REPL, ReadMcpResourceDir, RemoteTrigger,
|
|
20
|
+
* ScheduleWakeup, ShowOnboardingRolePicker, Task{Create,Get,List,Update}, Workflow;
|
|
21
|
+
* and removed Config. These are HOST/platform tools, NOT subagent-grantable, so
|
|
22
|
+
* `claudeCodeDialect.builtinAgentTools` (the `tools:` frontmatter catalog) is
|
|
23
|
+
* intentionally unchanged — they're acknowledged here as facts, nothing more.
|
|
24
|
+
*/
|
|
25
|
+
export declare const ACKNOWLEDGED_TOOL_INPUT_TYPES: readonly string[];
|
|
26
|
+
/** Parse `export interface <X>Input {` names from sdk-tools.d.ts → sorted [<X>]. Pure. */
|
|
27
|
+
export declare function parseToolInputTypes(dts: string): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Locate a READABLE JavaScript bundle inside the installed CC package, or null.
|
|
30
|
+
* Older CC shipped `cli.js` — a readable JS bundle whose hook-event names appear as
|
|
31
|
+
* string literals, greppable by `eventsMissingFromBundle`. CC ≥ ~2.1.18x switched to
|
|
32
|
+
* a NATIVE-BINARY distribution (`bin/claude.exe` copied from a platform
|
|
33
|
+
* `optionalDependencies` package) with NO readable JS bundle, so there is nothing to
|
|
34
|
+
* text-scan. Returns the bundle path when present, else null — callers then SKIP the
|
|
35
|
+
* event-drift check loudly rather than crash on a missing `cli.js`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function findClaudeCodeBundle(pkg: string): string | null;
|
|
38
|
+
/** Which of `events` do NOT appear as a whole-word literal in the bundle. Pure. */
|
|
39
|
+
export declare function eventsMissingFromBundle(bundle: string, events: readonly string[]): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Locate the user's installed `@anthropic-ai/claude-code` package dir, or null.
|
|
42
|
+
* Tries the global npm root, then the `claude` binary's real path. Read-only —
|
|
43
|
+
* we only read files the user already installed under their own CC license.
|
|
44
|
+
*/
|
|
45
|
+
export declare function findClaudeCodePackage(): string | null;
|
|
46
|
+
/** A runtime drift report: how the INSTALLED CC's tool surface compares to ours. */
|
|
47
|
+
export interface DialectDriftReport {
|
|
48
|
+
readonly installedVersion: string;
|
|
49
|
+
readonly validatedVersion: string;
|
|
50
|
+
/** Tool-input types present in the install but not in ACKNOWLEDGED (CC added). */
|
|
51
|
+
readonly newToolTypes: string[];
|
|
52
|
+
/** Acknowledged types absent from the install (CC removed/renamed). */
|
|
53
|
+
readonly removedToolTypes: string[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Best-effort, read-local drift check for `scan` (and other runtime callers). Reads
|
|
57
|
+
* only the small `sdk-tools.d.ts` (fast — no `cli.js` bundle scan; events are the
|
|
58
|
+
* CI test's job). Returns null when CC isn't installed or anything is unreadable —
|
|
59
|
+
* NEVER throws, so it can't break the command. ToS-clean: reads the user's own
|
|
60
|
+
* install, ships nothing.
|
|
61
|
+
*/
|
|
62
|
+
export declare function checkDialectDrift(): DialectDriftReport | null;
|
|
63
|
+
/** A one-line freshness warning if the dialect drifted from the install, else null. */
|
|
64
|
+
export declare function formatDialectDrift(r: DialectDriftReport | null): string | null;
|
|
65
|
+
//# sourceMappingURL=dialect-drift.d.ts.map
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACKNOWLEDGED_TOOL_INPUT_TYPES = exports.VALIDATED_CC_VERSION = void 0;
|
|
4
|
+
exports.parseToolInputTypes = parseToolInputTypes;
|
|
5
|
+
exports.findClaudeCodeBundle = findClaudeCodeBundle;
|
|
6
|
+
exports.eventsMissingFromBundle = eventsMissingFromBundle;
|
|
7
|
+
exports.findClaudeCodePackage = findClaudeCodePackage;
|
|
8
|
+
exports.checkDialectDrift = checkDialectDrift;
|
|
9
|
+
exports.formatDialectDrift = formatDialectDrift;
|
|
10
|
+
/**
|
|
11
|
+
* Dialect freshness / drift detection against the INSTALLED Claude Code.
|
|
12
|
+
*
|
|
13
|
+
* Claude Code is a black box (only `settings.json` has an official schema), so
|
|
14
|
+
* `claudeCodeDialect` is hand-maintained — see the licensing decision in
|
|
15
|
+
* research/code-adapter-architecture.md. But the installed `@anthropic-ai/claude-code`
|
|
16
|
+
* package ships a semi-machine source: `sdk-tools.d.ts` (the tool-input type set). We
|
|
17
|
+
* READ THE USER'S LOCAL INSTALL (ToS-clean — no copying, no redistribution; same
|
|
18
|
+
* posture as driving the user's own `claude` CLI) to ALARM when CC's surface drifts
|
|
19
|
+
* from our catalog. We never ship or vendor their types — only diff against them at
|
|
20
|
+
* test/runtime.
|
|
21
|
+
*
|
|
22
|
+
* Why not `import type` from the SDK? `@anthropic-ai/claude-code` and
|
|
23
|
+
* `@anthropic-ai/claude-agent-sdk` DO ship a clean `ToolInputSchemas` union (on a
|
|
24
|
+
* types-only `./sdk-tools` subpath), but both are "© Anthropic PBC. All rights
|
|
25
|
+
* reserved." — proprietary. vigiles is MIT and multi-harness, so taking a hard dep
|
|
26
|
+
* and re-exporting their types into our published `.d.ts` would (a) bake a
|
|
27
|
+
* proprietary package into an MIT dep tree, (b) couple the harness-agnostic core to
|
|
28
|
+
* a Claude-Code-only package, and (c) not even give us `builtinAgentTools` (the SDK
|
|
29
|
+
* union is input-SCHEMA names like `FileReadInput`/`CronCreateInput` — a superset in
|
|
30
|
+
* a different vocabulary than the subagent `tools:` catalog). Reading the local file
|
|
31
|
+
* + a hand-authored list of bare identifiers (facts) is the deliberate ToS-clean
|
|
32
|
+
* design; this drift alarm is what keeps the hand-list honest.
|
|
33
|
+
*
|
|
34
|
+
* NOTE (CC ≥ ~2.1.18x): CC switched to a NATIVE-BINARY distribution — the npm
|
|
35
|
+
* package ships `bin/claude.exe` (from a platform `optionalDependencies` package),
|
|
36
|
+
* NOT a readable `cli.js` JS bundle. So the old "grep hook-event string literals out
|
|
37
|
+
* of cli.js" check has no bundle to read and degrades to a LOUD SKIP (see
|
|
38
|
+
* `findClaudeCodeBundle`); `sdk-tools.d.ts` is still shipped, so the tool-type drift
|
|
39
|
+
* alarm keeps working.
|
|
40
|
+
*
|
|
41
|
+
* Pure parsers (testable with fixtures) + a local-install locator. TWO consumers:
|
|
42
|
+
* the gated CI test in `dialect-drift.test.ts` (fails loud on tool/event drift), and
|
|
43
|
+
* `vigiles scan` at runtime via `checkDialectDrift`/`formatDialectDrift` (a best-effort,
|
|
44
|
+
* read-local freshness WARN when the installed CC's tool surface drifts from ours).
|
|
45
|
+
*/
|
|
46
|
+
const node_fs_1 = require("node:fs");
|
|
47
|
+
const node_path_1 = require("node:path");
|
|
48
|
+
const node_child_process_1 = require("node:child_process");
|
|
49
|
+
/**
|
|
50
|
+
* The Claude Code version `ACKNOWLEDGED_TOOL_INPUT_TYPES` + the dialect were last
|
|
51
|
+
* validated against. SINGLE SOURCE OF TRUTH for the pin: CI installs
|
|
52
|
+
* `@anthropic-ai/claude-code@<this>` (grepped from this line) in every job that
|
|
53
|
+
* drives the real binary, so the dialect-drift alarm fires only on a DELIBERATE
|
|
54
|
+
* bump — not on every unpinned CC release landing on an unrelated PR — and the
|
|
55
|
+
* real-`claude` harness/eval tests stay reproducible. Bump this together with
|
|
56
|
+
* `ACKNOWLEDGED_TOOL_INPUT_TYPES` (the gated test cross-checks them).
|
|
57
|
+
*/
|
|
58
|
+
exports.VALIDATED_CC_VERSION = "2.1.187";
|
|
59
|
+
/**
|
|
60
|
+
* The `<X>Input` interface names we've ACKNOWLEDGED from `sdk-tools.d.ts` (Claude
|
|
61
|
+
* Code 2.1.187). The drift test fails when the installed set differs — a loud nudge
|
|
62
|
+
* to re-check `claudeCodeDialect` (and update this set) when CC adds/removes a tool.
|
|
63
|
+
* NOT a redistribution of their file: a list of bare identifiers (facts), authored here.
|
|
64
|
+
*
|
|
65
|
+
* 2.1.187 added the agent-PLATFORM surface (cron/scheduling/worktrees/web-app):
|
|
66
|
+
* Artifact, Cron{Create,Delete,List}, Enter/ExitWorktree, EnterPlanMode, Monitor,
|
|
67
|
+
* Projects, PushNotification, REPL, ReadMcpResourceDir, RemoteTrigger,
|
|
68
|
+
* ScheduleWakeup, ShowOnboardingRolePicker, Task{Create,Get,List,Update}, Workflow;
|
|
69
|
+
* and removed Config. These are HOST/platform tools, NOT subagent-grantable, so
|
|
70
|
+
* `claudeCodeDialect.builtinAgentTools` (the `tools:` frontmatter catalog) is
|
|
71
|
+
* intentionally unchanged — they're acknowledged here as facts, nothing more.
|
|
72
|
+
*/
|
|
73
|
+
exports.ACKNOWLEDGED_TOOL_INPUT_TYPES = [
|
|
74
|
+
"Agent",
|
|
75
|
+
"Artifact",
|
|
76
|
+
"AskUserQuestion",
|
|
77
|
+
"Bash",
|
|
78
|
+
"CronCreate",
|
|
79
|
+
"CronDelete",
|
|
80
|
+
"CronList",
|
|
81
|
+
"EnterPlanMode",
|
|
82
|
+
"EnterWorktree",
|
|
83
|
+
"ExitPlanMode",
|
|
84
|
+
"ExitWorktree",
|
|
85
|
+
"FileEdit",
|
|
86
|
+
"FileRead",
|
|
87
|
+
"FileWrite",
|
|
88
|
+
"Glob",
|
|
89
|
+
"Grep",
|
|
90
|
+
"ListMcpResources",
|
|
91
|
+
"Mcp",
|
|
92
|
+
"Monitor",
|
|
93
|
+
"NotebookEdit",
|
|
94
|
+
"Projects",
|
|
95
|
+
"PushNotification",
|
|
96
|
+
"REPL",
|
|
97
|
+
"ReadMcpResource",
|
|
98
|
+
"ReadMcpResourceDir",
|
|
99
|
+
"RemoteTrigger",
|
|
100
|
+
"ScheduleWakeup",
|
|
101
|
+
"ShowOnboardingRolePicker",
|
|
102
|
+
"TaskCreate",
|
|
103
|
+
"TaskGet",
|
|
104
|
+
"TaskList",
|
|
105
|
+
"TaskOutput",
|
|
106
|
+
"TaskStop",
|
|
107
|
+
"TaskUpdate",
|
|
108
|
+
"TodoWrite",
|
|
109
|
+
"WebFetch",
|
|
110
|
+
"WebSearch",
|
|
111
|
+
"Workflow",
|
|
112
|
+
];
|
|
113
|
+
/** Parse `export interface <X>Input {` names from sdk-tools.d.ts → sorted [<X>]. Pure. */
|
|
114
|
+
function parseToolInputTypes(dts) {
|
|
115
|
+
const out = new Set();
|
|
116
|
+
for (const m of dts.matchAll(/export\s+interface\s+(\w+)Input\b/g))
|
|
117
|
+
out.add(m[1]);
|
|
118
|
+
return [...out].sort();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Locate a READABLE JavaScript bundle inside the installed CC package, or null.
|
|
122
|
+
* Older CC shipped `cli.js` — a readable JS bundle whose hook-event names appear as
|
|
123
|
+
* string literals, greppable by `eventsMissingFromBundle`. CC ≥ ~2.1.18x switched to
|
|
124
|
+
* a NATIVE-BINARY distribution (`bin/claude.exe` copied from a platform
|
|
125
|
+
* `optionalDependencies` package) with NO readable JS bundle, so there is nothing to
|
|
126
|
+
* text-scan. Returns the bundle path when present, else null — callers then SKIP the
|
|
127
|
+
* event-drift check loudly rather than crash on a missing `cli.js`.
|
|
128
|
+
*/
|
|
129
|
+
function findClaudeCodeBundle(pkg) {
|
|
130
|
+
const cli = (0, node_path_1.join)(pkg, "cli.js");
|
|
131
|
+
return (0, node_fs_1.existsSync)(cli) ? cli : null;
|
|
132
|
+
}
|
|
133
|
+
/** Which of `events` do NOT appear as a whole-word literal in the bundle. Pure. */
|
|
134
|
+
function eventsMissingFromBundle(bundle, events) {
|
|
135
|
+
return events.filter((e) => !new RegExp(`\\b${e}\\b`).test(bundle));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Locate the user's installed `@anthropic-ai/claude-code` package dir, or null.
|
|
139
|
+
* Tries the global npm root, then the `claude` binary's real path. Read-only —
|
|
140
|
+
* we only read files the user already installed under their own CC license.
|
|
141
|
+
*/
|
|
142
|
+
function findClaudeCodePackage() {
|
|
143
|
+
const tryDir = (dir) => (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, "sdk-tools.d.ts")) ? dir : null;
|
|
144
|
+
const candidates = [
|
|
145
|
+
() => {
|
|
146
|
+
const root = (0, node_child_process_1.execSync)("npm root -g", { encoding: "utf-8" }).trim();
|
|
147
|
+
return tryDir((0, node_path_1.join)(root, "@anthropic-ai", "claude-code"));
|
|
148
|
+
},
|
|
149
|
+
() => {
|
|
150
|
+
const bin = (0, node_child_process_1.execSync)('readlink -f "$(command -v claude)"', {
|
|
151
|
+
encoding: "utf-8",
|
|
152
|
+
}).trim();
|
|
153
|
+
const marker = "/@anthropic-ai/claude-code/";
|
|
154
|
+
const i = bin.indexOf(marker);
|
|
155
|
+
return i >= 0 ? tryDir(bin.slice(0, i + marker.length - 1)) : null;
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
for (const probe of candidates) {
|
|
159
|
+
try {
|
|
160
|
+
const hit = probe();
|
|
161
|
+
if (hit)
|
|
162
|
+
return hit;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
/* probe unavailable (no npm / no claude) — try the next */
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Best-effort, read-local drift check for `scan` (and other runtime callers). Reads
|
|
172
|
+
* only the small `sdk-tools.d.ts` (fast — no `cli.js` bundle scan; events are the
|
|
173
|
+
* CI test's job). Returns null when CC isn't installed or anything is unreadable —
|
|
174
|
+
* NEVER throws, so it can't break the command. ToS-clean: reads the user's own
|
|
175
|
+
* install, ships nothing.
|
|
176
|
+
*/
|
|
177
|
+
function checkDialectDrift() {
|
|
178
|
+
const pkg = findClaudeCodePackage();
|
|
179
|
+
if (!pkg)
|
|
180
|
+
return null;
|
|
181
|
+
try {
|
|
182
|
+
const installed = new Set(parseToolInputTypes((0, node_fs_1.readFileSync)((0, node_path_1.join)(pkg, "sdk-tools.d.ts"), "utf-8")));
|
|
183
|
+
const ack = new Set(exports.ACKNOWLEDGED_TOOL_INPUT_TYPES);
|
|
184
|
+
let installedVersion = "unknown";
|
|
185
|
+
try {
|
|
186
|
+
installedVersion =
|
|
187
|
+
JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(pkg, "package.json"), "utf-8")).version ?? "unknown";
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
/* version optional */
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
installedVersion,
|
|
194
|
+
validatedVersion: exports.VALIDATED_CC_VERSION,
|
|
195
|
+
newToolTypes: [...installed].filter((t) => !ack.has(t)).sort(),
|
|
196
|
+
removedToolTypes: [...ack].filter((t) => !installed.has(t)).sort(),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/** A one-line freshness warning if the dialect drifted from the install, else null. */
|
|
204
|
+
function formatDialectDrift(r) {
|
|
205
|
+
if (!r || (r.newToolTypes.length === 0 && r.removedToolTypes.length === 0))
|
|
206
|
+
return null;
|
|
207
|
+
const parts = [];
|
|
208
|
+
if (r.newToolTypes.length > 0)
|
|
209
|
+
parts.push(`CC added tool type(s): ${r.newToolTypes.join(", ")}`);
|
|
210
|
+
if (r.removedToolTypes.length > 0)
|
|
211
|
+
parts.push(`removed: ${r.removedToolTypes.join(", ")}`);
|
|
212
|
+
return (`⚠ dialect freshness: vigiles's tool catalog was validated against ` +
|
|
213
|
+
`claude-code ${r.validatedVersion}, you have ${r.installedVersion} — ` +
|
|
214
|
+
`${parts.join("; ")}. Tool/contract checks may be stale; a vigiles update may be needed.`);
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=dialect-drift.js.map
|