vigiles 29.0.0 → 30.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/adapter-conformance.d.ts +1 -1
- package/dist/adapter-conformance.js +106 -25
- package/dist/adapter-registry.d.ts +61 -14
- package/dist/adapter-registry.js +78 -10
- package/dist/adapter.d.ts +23 -2
- package/dist/adapter.js +13 -1
- package/dist/adapters/claude-code/adapter.d.ts +32 -2
- package/dist/adapters/claude-code/adapter.js +44 -23
- package/dist/adapters/claude-code/dialect.js +87 -21
- package/dist/adapters/claude-code/hook-protocol.js +16 -0
- package/dist/adapters/claude-code/instruction-chain.d.ts +25 -0
- package/dist/adapters/claude-code/instruction-chain.js +626 -0
- package/dist/adapters/claude-code/layout.d.ts +2 -2
- package/dist/adapters/claude-code/layout.js +42 -8
- package/dist/adapters/claude-code/model-access.d.ts +41 -0
- package/dist/adapters/claude-code/model-access.js +46 -0
- package/dist/adapters/claude-code/skill-reachability.d.ts +125 -0
- package/dist/adapters/claude-code/skill-reachability.js +111 -0
- package/dist/adapters/codex/adapter.d.ts +39 -2
- package/dist/adapters/codex/adapter.js +29 -29
- package/dist/adapters/codex/dialect.js +11 -6
- package/dist/adapters/codex/eval.d.ts +10 -0
- package/dist/adapters/codex/eval.js +48 -1
- package/dist/adapters/codex/hook-protocol.d.ts +2 -1
- package/dist/adapters/codex/hook-protocol.js +10 -0
- package/dist/adapters/codex/instruction-chain.d.ts +40 -0
- package/dist/adapters/codex/instruction-chain.js +105 -0
- package/dist/adapters/codex/layout.d.ts +1 -1
- package/dist/adapters/codex/layout.js +41 -14
- package/dist/adapters/opencode/adapter.d.ts +33 -2
- package/dist/adapters/opencode/adapter.js +36 -36
- package/dist/adapters/opencode/dialect.js +2 -2
- package/dist/adapters/opencode/instruction-chain.d.ts +37 -0
- package/dist/adapters/opencode/instruction-chain.js +70 -0
- package/dist/adapters/opencode/layout.d.ts +19 -0
- package/dist/adapters/opencode/layout.js +34 -15
- package/dist/adoptability.d.ts +31 -1
- package/dist/adoptability.js +57 -0
- package/dist/cli-main.js +180 -102
- package/dist/core/adapter.d.ts +213 -61
- package/dist/core/compile.d.ts +2 -2
- package/dist/core/compile.js +57 -38
- package/dist/core/compose.d.ts +5 -3
- package/dist/core/compose.js +5 -3
- package/dist/core/config-schema.d.ts +14 -2
- package/dist/core/config-schema.js +24 -3
- package/dist/core/dialect.d.ts +54 -12
- package/dist/core/dialect.js +56 -0
- package/dist/core/eval-driver.d.ts +194 -0
- package/dist/core/eval-driver.js +3 -0
- package/dist/core/frontmatter-read.d.ts +10 -0
- package/dist/core/frontmatter-read.js +30 -3
- package/dist/core/hook-program.d.ts +27 -2
- package/dist/core/hook-program.js +29 -24
- package/dist/core/hook-protocol.d.ts +54 -0
- package/dist/core/install-reader.d.ts +18 -0
- package/dist/core/install-reader.js +88 -0
- package/dist/core/instruction-chain.d.ts +444 -0
- package/dist/core/instruction-chain.js +292 -0
- package/dist/core/instruction-weight.d.ts +96 -14
- package/dist/core/instruction-weight.js +65 -30
- package/dist/core/layout.d.ts +220 -33
- package/dist/core/layout.js +115 -1
- package/dist/core/lethal-trifecta.d.ts +12 -7
- package/dist/core/lethal-trifecta.js +13 -8
- package/dist/core/live-driver.d.ts +137 -0
- package/dist/core/live-driver.js +14 -0
- package/dist/core/markdown.d.ts +23 -0
- package/dist/core/markdown.js +77 -28
- package/dist/core/orphans.js +9 -7
- package/dist/core/settings-codec.d.ts +17 -0
- package/dist/core/settings-codec.js +56 -0
- package/dist/core/surface-discovery.d.ts +2 -2
- package/dist/core/surface-discovery.js +24 -8
- package/dist/core/surface-scopes.d.ts +26 -6
- package/dist/core/surface-scopes.js +52 -11
- package/dist/core/validate.js +16 -3
- package/dist/eval.d.ts +16 -108
- package/dist/eval.js +34 -1
- package/dist/harness-test.d.ts +3 -63
- package/dist/hook-install.d.ts +12 -1
- package/dist/hook-install.js +12 -1
- package/dist/plugin-loader.d.ts +1 -1
- package/dist/plugin-loader.js +43 -36
- package/dist/scan-behavioral.d.ts +34 -25
- package/dist/scan-behavioral.js +122 -58
- package/dist/scan-core.js +37 -18
- package/dist/scan-files.d.ts +1 -1
- package/dist/scan-files.js +53 -33
- package/dist/scan-trigger-suggest.d.ts +0 -21
- package/dist/scan-trigger-suggest.js +0 -23
- package/dist/scan.d.ts +4 -4
- package/dist/scan.js +120 -73
- package/dist/skill-harness.d.ts +21 -5
- package/dist/skill-harness.js +29 -11
- package/dist/surface-discovery-fs.d.ts +2 -0
- package/dist/surface-discovery-fs.js +108 -6
- package/dist/test-coverage-files.js +24 -17
- package/dist/test-coverage.d.ts +9 -3
- package/dist/test-coverage.js +32 -17
- package/dist/verify-plugin-guards.js +1 -1
- package/package.json +1 -1
- package/dist/skill-reachability.d.ts +0 -68
- package/dist/skill-reachability.js +0 -205
- /package/dist/{dialect-drift.d.ts → adapters/claude-code/dialect-drift.d.ts} +0 -0
- /package/dist/{dialect-drift.js → adapters/claude-code/dialect-drift.js} +0 -0
package/dist/core/dialect.js
CHANGED
|
@@ -1,3 +1,59 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_INSTRUCTION_TARGETS = exports.RENDERABLE_SKILL_FRONTMATTER_KEYS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Every SKILL.md frontmatter key the COMPILER knows how to render, in the order
|
|
6
|
+
* it renders them. A property of `renderSkillFrontmatter`, not of any harness:
|
|
7
|
+
* a dialect's {@link HarnessDialect.skillFrontmatterKeys} is a subset of this,
|
|
8
|
+
* and a key outside it can be declared but will never be emitted.
|
|
9
|
+
*
|
|
10
|
+
* 🔴 THIS REPLACES A TYPE ALIAS THAT SPELLED A HARNESS. It used to be
|
|
11
|
+
* `type SkillFrontmatterProfile = "claude-code" | "minimal"`, and it was the
|
|
12
|
+
* root of five of the eleven per-site lint disables on this branch: `compile.ts`
|
|
13
|
+
* defaulted to it, branched on it and defaulted it again, and
|
|
14
|
+
* `lethal-trifecta.ts` compared against it. Each of those is now a set
|
|
15
|
+
* membership test over key names, which are facts about a FILE FORMAT and carry
|
|
16
|
+
* no harness in them.
|
|
17
|
+
*/
|
|
18
|
+
exports.RENDERABLE_SKILL_FRONTMATTER_KEYS = [
|
|
19
|
+
"name",
|
|
20
|
+
"description",
|
|
21
|
+
"disable-model-invocation",
|
|
22
|
+
"context",
|
|
23
|
+
"argument-hint",
|
|
24
|
+
"allowed-tools",
|
|
25
|
+
"disallowed-tools",
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* The instruction filenames vigiles recognizes when NO dialect is injected, in
|
|
29
|
+
* precedence order — `[0]` is what a spec compiles into when it names no target.
|
|
30
|
+
*
|
|
31
|
+
* 🔴 IT IS THE CORE'S OWN DEFAULT, AND DERIVING IT FROM THE REGISTRY IS NOT
|
|
32
|
+
* ALLOWED HERE — worth saying, because that is the obvious fix and it is the
|
|
33
|
+
* wrong one. `src/core/CLAUDE.md` states the invariant ("the core must not
|
|
34
|
+
* import an adapter, `core ⊄ adapter`") and the registry IS the adapters.
|
|
35
|
+
* Measured on a probe that added `import { ADAPTERS } from
|
|
36
|
+
* "../adapter-registry.js"` to `validate.ts`: the module graph of
|
|
37
|
+
* `dist/core/validate.js` went from 108 to 136 modules and pulled BOTH
|
|
38
|
+
* `adapters/claude-code/adapter.js` and `adapters/codex/adapter.js` into the
|
|
39
|
+
* domain's own graph.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ AND THE LINT DOES NOT STOP IT — same probe: `npx eslint
|
|
42
|
+
* src/core/validate.ts` reported 0 errors, because `boundaries/dependencies`
|
|
43
|
+
* treats `src/adapter-registry.ts` as the unclassified composition root and
|
|
44
|
+
* judges DIRECT edges only. That silence is an artifact of where the rule
|
|
45
|
+
* looks, not permission. So the agreement between this list and the registry is
|
|
46
|
+
* held by a TEST that lives outside the core (`adapter-contract.test.ts`),
|
|
47
|
+
* where importing the registry is legal — a ratchet instead of an inversion.
|
|
48
|
+
*
|
|
49
|
+
* ONE PLACE, because it was two: `validate.ts` held `["CLAUDE.md", "AGENTS.md"]`
|
|
50
|
+
* and `compile.ts` held `DEFAULT_TARGET = "CLAUDE.md"`, which is this list's
|
|
51
|
+
* head under another name — {@link HarnessDialect.instructionTargets} already
|
|
52
|
+
* contracts that `[0]` is the default target, so the second was the first,
|
|
53
|
+
* restated.
|
|
54
|
+
*/
|
|
55
|
+
exports.DEFAULT_INSTRUCTION_TARGETS = [
|
|
56
|
+
"CLAUDE.md",
|
|
57
|
+
"AGENTS.md",
|
|
58
|
+
];
|
|
3
59
|
//# sourceMappingURL=dialect.js.map
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The EXECUTING tiers' shared shapes — the eval-tier transport (`EvalDriver`
|
|
3
|
+
* and the runner/parser types it is built from) and the unified run record
|
|
4
|
+
* (`Trace`) both tiers produce.
|
|
5
|
+
*
|
|
6
|
+
* WHY THEY LIVE IN CORE, exactly as `harness-driver.ts` says of the mock tier's
|
|
7
|
+
* trace shapes: a core PORT has to be able to reference them, and the core may
|
|
8
|
+
* not import `src/eval.ts` or `src/harness-test.ts` (those are composition-root
|
|
9
|
+
* runners that wire the Claude Code defaults, and `boundaries/dependencies`
|
|
10
|
+
* classifies everything they reach). `HarnessLiveDriver` (`./live-driver.js`)
|
|
11
|
+
* is the port that needed them; before it existed the types could sit beside
|
|
12
|
+
* their runners, and nothing forced the split.
|
|
13
|
+
*
|
|
14
|
+
* NOTHING MOVED BUT THE TYPES. `claudeEvalDriver`, `spawnAgent`,
|
|
15
|
+
* `parseClaudeRun`, `parseToolCalls`, `parseHooks` and `parseSubagents` stay in
|
|
16
|
+
* their runners — they are the wired Claude Code DEFAULT, and wiring the default
|
|
17
|
+
* is a composition root's job (`eval.ts:2336-2348` argues this at length). Both
|
|
18
|
+
* runners re-export these names, so every existing import keeps resolving.
|
|
19
|
+
*/
|
|
20
|
+
import type { ToolCall, HookFire, ModelRequest } from "./harness-driver.js";
|
|
21
|
+
/** Per-run resource use, parsed from the terminal `result` event (0 when absent). */
|
|
22
|
+
export interface EvalUsage {
|
|
23
|
+
/** `total_cost_usd` reported by the harness binary. */
|
|
24
|
+
readonly costUsd: number;
|
|
25
|
+
/** Wall-clock `duration_ms` of the run. */
|
|
26
|
+
readonly durationMs: number;
|
|
27
|
+
/** Fresh (uncached) input tokens, billed at full input price. */
|
|
28
|
+
readonly inputTokens: number;
|
|
29
|
+
readonly outputTokens: number;
|
|
30
|
+
/** Tokens written to the prompt cache this run (~1.25× input price). */
|
|
31
|
+
readonly cacheCreationTokens: number;
|
|
32
|
+
/** Tokens served from the prompt cache this run (~0.1× input price). */
|
|
33
|
+
readonly cacheReadTokens: number;
|
|
34
|
+
}
|
|
35
|
+
/** A sub-agent (`Task`) run as a nested trace: its name + the tools it used. */
|
|
36
|
+
export interface SubagentTrace {
|
|
37
|
+
/** The `subagent_type` from the `Task` tool input. */
|
|
38
|
+
readonly name: string;
|
|
39
|
+
/** The tools the subagent invoked (events tagged with the Task's id). */
|
|
40
|
+
readonly toolCalls: readonly ToolCall[];
|
|
41
|
+
/**
|
|
42
|
+
* The subagent's RETURNED text — the dispatch tool_result the orchestrator
|
|
43
|
+
* receives back. This is where a `result()` contract's `vigiles:ok`/`vigiles:err`
|
|
44
|
+
* block lands, so `subagent(name, [output(/vigiles:ok/)])` can assert the typed
|
|
45
|
+
* outcome. "" if not captured.
|
|
46
|
+
*/
|
|
47
|
+
readonly output: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The observable record of ONE run — the unified shape produced by BOTH testing
|
|
51
|
+
* tiers: `runHarnessTest`'s result and `runEval`'s `measure` ctx (`eval.ts`)
|
|
52
|
+
* both satisfy it. That's what lets the bare predicates in `harness-assert.ts`
|
|
53
|
+
* (`usedTool` / `skillResolved` / `toolCount` / `toolUsedWith` / `hookFired` /
|
|
54
|
+
* `outputContains`) run over either, with the testing helpers asserting and eval
|
|
55
|
+
* measuring over the same vocabulary.
|
|
56
|
+
*/
|
|
57
|
+
export interface Trace {
|
|
58
|
+
/**
|
|
59
|
+
* The tools the agent invoked, each paired with its result — parsed from the
|
|
60
|
+
* transcript. Empty unless the run captured the stream (`transcript: true` on
|
|
61
|
+
* the harness tier; always on the eval tier). Lets a test assert on the
|
|
62
|
+
* agent's *actions* (skills, MCP tools, subagents) instead of grepping stdout.
|
|
63
|
+
*/
|
|
64
|
+
readonly toolCalls: readonly ToolCall[];
|
|
65
|
+
/**
|
|
66
|
+
* The hooks that fired during the run, each with its decision — parsed from
|
|
67
|
+
* the CLI's `hook_response` stream events. Same capture requirement as
|
|
68
|
+
* `toolCalls` (empty without the stream). Lets a test assert hook firing
|
|
69
|
+
* honestly instead of via a marker file.
|
|
70
|
+
*/
|
|
71
|
+
readonly hooks: readonly HookFire[];
|
|
72
|
+
/** The agent's final answer text (the terminal `result` event), or "". */
|
|
73
|
+
readonly output: string;
|
|
74
|
+
/**
|
|
75
|
+
* The requests the model received, captured by the scripted mock — each with
|
|
76
|
+
* its `system` prompt and `messages`, flattened to text. Lets a test assert
|
|
77
|
+
* what actually reached the model (a SessionStart hook's injected context, a
|
|
78
|
+
* slash command's expansion), not just that a hook fired. **Harness tier
|
|
79
|
+
* only**: the mock sees the requests, so this is populated by `runHarnessTest`
|
|
80
|
+
* (with or without `transcript`); the eval tier drives the real API, so its
|
|
81
|
+
* `modelRequests` is always empty.
|
|
82
|
+
*/
|
|
83
|
+
readonly modelRequests: readonly ModelRequest[];
|
|
84
|
+
/** Number of model turns. */
|
|
85
|
+
readonly turns: number;
|
|
86
|
+
/**
|
|
87
|
+
* Sub-agent (`Task`) runs as nested traces, keyed by `subagent_type`. A
|
|
88
|
+
* subagent runs its own session; CC tags its events with `parent_tool_use_id`
|
|
89
|
+
* (= the `Task` tool call) so its tool calls are recovered into a sub-trace
|
|
90
|
+
* here, lettng a test assert what the subagent DID (not just that `Task` fired).
|
|
91
|
+
* Empty unless the stream was captured / the harness emits subagent events.
|
|
92
|
+
*/
|
|
93
|
+
readonly subagents?: readonly SubagentTrace[];
|
|
94
|
+
/** Final contents of a file under the working dir, or null if absent. */
|
|
95
|
+
file(path: string): string | null;
|
|
96
|
+
}
|
|
97
|
+
/** The raw output of one trial: the agent's exit code + captured streams. */
|
|
98
|
+
export interface RunOut {
|
|
99
|
+
code: number;
|
|
100
|
+
stdout: string;
|
|
101
|
+
/** Captured stderr, when the runner provides it (used for rate-limit detection). */
|
|
102
|
+
stderr?: string;
|
|
103
|
+
}
|
|
104
|
+
/** The per-trial arguments handed to an {@link AgentRunner}. */
|
|
105
|
+
export interface AgentRunArgs {
|
|
106
|
+
readonly task: string;
|
|
107
|
+
readonly cwd: string;
|
|
108
|
+
readonly model: string;
|
|
109
|
+
/**
|
|
110
|
+
* Reasoning-budget level for the run (`claude --effort`). Part of the
|
|
111
|
+
* MEASUREMENT, not a run knob: it changes the model's output distribution, not
|
|
112
|
+
* the sample size — so it lives on the spec next to `model` (never an env),
|
|
113
|
+
* and it is hashed into both the cache key and the eval lock. Deliberately
|
|
114
|
+
* `string | number` rather than a literal union: the binary accepts an alias
|
|
115
|
+
* map, is case-insensitive, and takes an integer budget, and its own valid set
|
|
116
|
+
* MOVED between builds (2.1.42 had no `xhigh`, 2.1.257 does) — a hard-coded
|
|
117
|
+
* union would reject a valid level after any upstream addition. A wrong value
|
|
118
|
+
* is caught at RUNTIME instead, by `effortRejection`, which is what the
|
|
119
|
+
* binary actually tells us. Omit for the harness default.
|
|
120
|
+
*/
|
|
121
|
+
readonly effort?: string | number;
|
|
122
|
+
readonly tools: readonly string[];
|
|
123
|
+
readonly hasSettings: boolean;
|
|
124
|
+
readonly pluginDir: string | undefined;
|
|
125
|
+
readonly timeoutMs: number;
|
|
126
|
+
/** Extra env layered over `process.env` for this run (e.g. `VIGILES_INTERCEPT_TOOLS`). */
|
|
127
|
+
readonly env?: Record<string, string>;
|
|
128
|
+
/**
|
|
129
|
+
* When true, `env` is the COMPLETE spawn environment (an ephemeral run env from
|
|
130
|
+
* `ephemeralRunEnv`) — the runner does NOT prepend `process.env`, so the
|
|
131
|
+
* real `$HOME` / secrets are scrubbed. Default false: `env` is an overlay over
|
|
132
|
+
* `process.env` (the byte-identical-to-today path). Set only by `ephemeralEnv`.
|
|
133
|
+
*/
|
|
134
|
+
readonly replaceEnv?: boolean;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Runs one trial and returns its raw output. The default (`spawnAgent`)
|
|
138
|
+
* drives the real `claude` CLI; `runEvalWith` takes one explicitly, so the eval
|
|
139
|
+
* orchestration is testable without a model (pass a fake returning canned
|
|
140
|
+
* stream-json) and a custom runtime can be plugged in.
|
|
141
|
+
*/
|
|
142
|
+
export type AgentRunner = (args: AgentRunArgs) => Promise<RunOut>;
|
|
143
|
+
/**
|
|
144
|
+
* The harness-specific half of a run trace: how a real model's raw stdout maps
|
|
145
|
+
* to the common fields. Claude Code's `parseClaudeRun` reads its stream-json; a
|
|
146
|
+
* second harness (Codex) supplies its own parser of `codex exec --json` JSONL, so
|
|
147
|
+
* the eval tier (`measureTriggerRate`/`runEval`) isn't bound to Claude's format.
|
|
148
|
+
* The non-harness fields (cwd/exitCode/stdout/file/sh) stay in `makeContext`.
|
|
149
|
+
*/
|
|
150
|
+
export interface ParsedModelRun {
|
|
151
|
+
readonly turns: number;
|
|
152
|
+
readonly output: string;
|
|
153
|
+
readonly toolCalls: ToolCall[];
|
|
154
|
+
readonly hooks: HookFire[];
|
|
155
|
+
readonly subagents: SubagentTrace[];
|
|
156
|
+
readonly usage: EvalUsage;
|
|
157
|
+
}
|
|
158
|
+
export type ModelOutputParser = (out: RunOut) => ParsedModelRun;
|
|
159
|
+
/**
|
|
160
|
+
* An eval-tier transport: how to RUN a real harness turn and PARSE its output.
|
|
161
|
+
* The default is Claude Code (`claudeEvalDriver`); a second harness supplies its
|
|
162
|
+
* own (e.g. `codexEvalDriver` from `vigiles/codex`) and passes it as
|
|
163
|
+
* `measureTriggerRate(spec, { evalDriver })` — the eval-tier analog of
|
|
164
|
+
* `runHarnessTest`'s `{ adapter }`. `runError` lets the loop drop an
|
|
165
|
+
* errored/rate-limited turn instead of scoring it as a miss.
|
|
166
|
+
*/
|
|
167
|
+
export interface EvalDriver {
|
|
168
|
+
readonly runner: AgentRunner;
|
|
169
|
+
readonly parse: ModelOutputParser;
|
|
170
|
+
readonly runError?: (out: RunOut) => string | null;
|
|
171
|
+
/**
|
|
172
|
+
* The harness this driver runs (e.g. the adapter's `name`). Folded into a
|
|
173
|
+
* trigger-rate eval's LOCK hash so a report recorded on one harness is marked
|
|
174
|
+
* STALE if the eval is later switched to another (a different harness can fire a
|
|
175
|
+
* skill differently). Optional for back-compat — absent defaults to the
|
|
176
|
+
* default harness, so an existing single-harness lock is unaffected.
|
|
177
|
+
*/
|
|
178
|
+
readonly harness?: string;
|
|
179
|
+
/**
|
|
180
|
+
* When set, this driver's trigger-rate number is EXPERIMENTAL and not
|
|
181
|
+
* validated — the string is the human caveat explaining why (e.g. Codex has no
|
|
182
|
+
* skill-selection event, so firing is inferred from a SKILL.md read, which can
|
|
183
|
+
* be wrong in both directions). Absent = supported/trustworthy (the default).
|
|
184
|
+
* `measureTriggerRate` copies it onto the report and warns; the formatter
|
|
185
|
+
* prints it. Precision-first: never let a possibly-wrong number read as a
|
|
186
|
+
* measurement.
|
|
187
|
+
*
|
|
188
|
+
* The same fact as `HarnessLiveDriver.firing` (`./live-driver.js`), which is
|
|
189
|
+
* the typed form the measurement tiers branch on; `adapter-contract.test.ts`
|
|
190
|
+
* asserts the two agree on every adapter.
|
|
191
|
+
*/
|
|
192
|
+
readonly experimental?: string;
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=eval-driver.d.ts.map
|
|
@@ -6,6 +6,16 @@ export interface FrontmatterRead {
|
|
|
6
6
|
/** True when a leading `---` block EXISTS but is NOT valid YAML. */
|
|
7
7
|
readonly malformed: boolean;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* The markdown BODY — everything after the leading frontmatter block, or the
|
|
11
|
+
* whole text when there is none.
|
|
12
|
+
*
|
|
13
|
+
* Lives here, beside {@link readFrontmatter}, because `BLOCK_RE` above is the
|
|
14
|
+
* ONE statement of where frontmatter ends; a caller slicing the body for itself
|
|
15
|
+
* would be a second one, free to disagree (and the header records what the
|
|
16
|
+
* first one already has to know about BOMs and legacy stamp comments).
|
|
17
|
+
*/
|
|
18
|
+
export declare function frontmatterBody(markdown: string): string;
|
|
9
19
|
/** Extract + parse the leading frontmatter block, never throwing. */
|
|
10
20
|
export declare function readFrontmatter(markdown: string): FrontmatterRead;
|
|
11
21
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.frontmatterBody = frontmatterBody;
|
|
3
4
|
exports.readFrontmatter = readFrontmatter;
|
|
4
5
|
exports.frontmatterScalar = frontmatterScalar;
|
|
5
6
|
exports.frontmatterList = frontmatterList;
|
|
@@ -20,7 +21,20 @@ exports.frontmatterList = frontmatterList;
|
|
|
20
21
|
* the regex parse in agent-runtime.ts); `core/frontmatter.ts` is a DIFFERENT
|
|
21
22
|
* concern (the Level-1 `vigiles:` rule block) and is untouched.
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
|
+
/**
|
|
25
|
+
* 🔴 `js-yaml` IS REQUIRED LAZILY, AND THAT IS A MEASUREMENT, NOT A STYLE. The
|
|
26
|
+
* same wrapper is in `core/settings-codec.ts` and `core/markdown.ts` with the
|
|
27
|
+
* reason recorded: a top-level import puts the parser into the module graph of
|
|
28
|
+
* every HOOK DECISION, because the hook runtime reaches the adapter registry
|
|
29
|
+
* and from there a layout. This module became reachable that way on 2026-09-21
|
|
30
|
+
* through `PluginLayout.instructionChain` (a rule's `paths:` frontmatter is
|
|
31
|
+
* what decides whether it loads at launch) — measured by
|
|
32
|
+
* `src/hook-runtime-graph.test.ts`: 37 modules became 71 with an eager import.
|
|
33
|
+
* `tsc` lowers this to CommonJS, so the `require` does not run until a
|
|
34
|
+
* frontmatter block is actually parsed, and {@link frontmatterBody}, which needs
|
|
35
|
+
* no YAML at all, never triggers it.
|
|
36
|
+
*/
|
|
37
|
+
const yaml = () => require("js-yaml");
|
|
24
38
|
// Frontmatter is the very first thing in the file. Anchoring at the start — not
|
|
25
39
|
// `(?:^|\n)` — means a `---` horizontal rule in the BODY is never mistaken for
|
|
26
40
|
// frontmatter (which matters for the malformed-YAML verdict). A leading BOM is
|
|
@@ -33,6 +47,19 @@ const js_yaml_1 = require("js-yaml");
|
|
|
33
47
|
const BLOCK_RE = /^\uFEFF?(?:<!--[\s\S]*?-->\s*)?---\r?\n([\s\S]*?)\r?\n---/;
|
|
34
48
|
/** A YAML block-scalar indicator: `>`/`|` with optional chomp (`+`/`-`) + indent digit. */
|
|
35
49
|
const BLOCK_SCALAR_RE = /^[|>][+-]?\d*$/;
|
|
50
|
+
/**
|
|
51
|
+
* The markdown BODY — everything after the leading frontmatter block, or the
|
|
52
|
+
* whole text when there is none.
|
|
53
|
+
*
|
|
54
|
+
* Lives here, beside {@link readFrontmatter}, because `BLOCK_RE` above is the
|
|
55
|
+
* ONE statement of where frontmatter ends; a caller slicing the body for itself
|
|
56
|
+
* would be a second one, free to disagree (and the header records what the
|
|
57
|
+
* first one already has to know about BOMs and legacy stamp comments).
|
|
58
|
+
*/
|
|
59
|
+
function frontmatterBody(markdown) {
|
|
60
|
+
const m = BLOCK_RE.exec(markdown);
|
|
61
|
+
return m === null ? markdown : markdown.slice(m[0].length);
|
|
62
|
+
}
|
|
36
63
|
/** Extract + parse the leading frontmatter block, never throwing. */
|
|
37
64
|
function readFrontmatter(markdown) {
|
|
38
65
|
const m = BLOCK_RE.exec(markdown);
|
|
@@ -40,7 +67,7 @@ function readFrontmatter(markdown) {
|
|
|
40
67
|
return { data: null, block: null, malformed: false };
|
|
41
68
|
const block = m[1];
|
|
42
69
|
try {
|
|
43
|
-
const parsed = (
|
|
70
|
+
const parsed = yaml().load(block);
|
|
44
71
|
if (parsed !== null &&
|
|
45
72
|
typeof parsed === "object" &&
|
|
46
73
|
!Array.isArray(parsed)) {
|
|
@@ -55,7 +82,7 @@ function readFrontmatter(markdown) {
|
|
|
55
82
|
return { data: null, block, malformed: false };
|
|
56
83
|
}
|
|
57
84
|
catch (e) {
|
|
58
|
-
if (e instanceof
|
|
85
|
+
if (e instanceof yaml().YAMLException)
|
|
59
86
|
return { data: null, block, malformed: true };
|
|
60
87
|
throw e;
|
|
61
88
|
}
|
|
@@ -35,8 +35,25 @@
|
|
|
35
35
|
*/
|
|
36
36
|
import { type BashEffect } from "./bash-effects.js";
|
|
37
37
|
import { type SHA256Hash } from "./hash.js";
|
|
38
|
+
/**
|
|
39
|
+
* `@iarna/toml` is required LAZILY, at the one call site that serializes a Codex
|
|
40
|
+
* TOML settings block — never at module load. MEASURED 2026-09-08 (Node 22.22.2,
|
|
41
|
+
* `tools/measure-hook-startup.mjs`): the top-level import cost 56 ms of a
|
|
42
|
+
* ~170 ms `require("dist/core/hook-program.js")`, and this module is on the hot
|
|
43
|
+
* path of `vigiles hook-runtime run-program`, which runs on EVERY matching tool
|
|
44
|
+
* call. A hook DECIDES; it never serializes a settings block, so it paid 56 ms
|
|
45
|
+
* per tool call for a compile-time dependency.
|
|
46
|
+
*
|
|
47
|
+
* 🔴 THE LAZY REQUIRE MOVED, IT DID NOT GO AWAY. Serializing a settings block
|
|
48
|
+
* is now `SettingsCodec.render`, and `core/settings-codec.ts` keeps exactly
|
|
49
|
+
* this wrapper for exactly this reason — a codec is reached from strictly MORE
|
|
50
|
+
* places than this module was, so a top-level import there would have widened
|
|
51
|
+
* the regression rather than repeated it. `src/hook-runtime-graph.test.ts`
|
|
52
|
+
* still fails if `@iarna/toml` reappears in a decision's module graph.
|
|
53
|
+
*/
|
|
38
54
|
import type { HarnessDialect } from "./dialect.js";
|
|
39
55
|
import type { HookProtocol } from "./hook-protocol.js";
|
|
56
|
+
import { type SettingsCodec } from "./settings-codec.js";
|
|
40
57
|
import { type EventCapabilityTable } from "./event-capability.js";
|
|
41
58
|
import { type ProviderName, type NeedSpec, type HookCtx } from "./hook-providers.js";
|
|
42
59
|
import { type StateFact, type StateWrite } from "./hook-state.js";
|
|
@@ -397,8 +414,16 @@ export interface CompileHookOptions {
|
|
|
397
414
|
readonly dialect?: HarnessDialect;
|
|
398
415
|
/** Matcher style (exact vs anchored regex). Defaults to Claude Code's `"exact"`. */
|
|
399
416
|
readonly hookProtocol?: HookProtocol;
|
|
400
|
-
/**
|
|
401
|
-
|
|
417
|
+
/**
|
|
418
|
+
* Settings ENCODING, from `PluginLayout.settings`. Absent ⇒ JSON, the
|
|
419
|
+
* backwards-compatible default for a caller that injects no layout.
|
|
420
|
+
*
|
|
421
|
+
* It used to be `settingsFormat?: "json" | "toml"` and `renderSettingsBlock`
|
|
422
|
+
* branched on it for BOTH the encoding and the entry shape. The shape comes
|
|
423
|
+
* from `hookProtocol.registration` now, so the two can no longer be chosen
|
|
424
|
+
* independently of each other by accident.
|
|
425
|
+
*/
|
|
426
|
+
readonly settings?: SettingsCodec;
|
|
402
427
|
/** Names of registered providers (`.vigiles/providers/`) a `provider()` ref may resolve to. */
|
|
403
428
|
readonly registeredProviders?: readonly string[];
|
|
404
429
|
}
|
|
@@ -78,18 +78,7 @@ exports.isLoadPathRepairEvent = isLoadPathRepairEvent;
|
|
|
78
78
|
*/
|
|
79
79
|
const bash_effects_js_1 = require("./bash-effects.js");
|
|
80
80
|
const hash_js_1 = require("./hash.js");
|
|
81
|
-
|
|
82
|
-
* `@iarna/toml` is required LAZILY, at the one call site that serializes a Codex
|
|
83
|
-
* TOML settings block — never at module load. MEASURED 2026-09-08 (Node 22.22.2,
|
|
84
|
-
* `tools/measure-hook-startup.mjs`): the top-level import cost 56 ms of a
|
|
85
|
-
* ~170 ms `require("dist/core/hook-program.js")`, and this module is on the hot
|
|
86
|
-
* path of `vigiles hook-runtime run-program`, which runs on EVERY matching tool
|
|
87
|
-
* call. A hook DECIDES; it never serializes a settings block, so it paid 56 ms
|
|
88
|
-
* per tool call for a compile-time dependency. Keep it a call-site require —
|
|
89
|
-
* hoisting it back to the top is the regression, and `src/hook-runtime-graph.test.ts`
|
|
90
|
-
* fails if `@iarna/toml` reappears in a decision's module graph.
|
|
91
|
-
*/
|
|
92
|
-
const stringifyToml = (value) => require("@iarna/toml").stringify(value);
|
|
81
|
+
const settings_codec_js_1 = require("./settings-codec.js");
|
|
93
82
|
const event_capability_js_1 = require("./event-capability.js");
|
|
94
83
|
const hook_events_js_1 = require("./hook-events.js");
|
|
95
84
|
const tool_contract_js_1 = require("./tool-contract.js");
|
|
@@ -750,17 +739,33 @@ function styleMatcher(matcher, protocol) {
|
|
|
750
739
|
* Claude Code (the nested `{event:[{matcher,hooks:[{type,command}]}]}` shape);
|
|
751
740
|
* TOML `[[hooks.<event>]]` with a flat `command` for Codex.
|
|
752
741
|
*/
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
742
|
+
/**
|
|
743
|
+
* The settings block a user pastes: the harness's entry SHAPE
|
|
744
|
+
* (`hookProtocol.registration`) rendered in the harness's ENCODING
|
|
745
|
+
* (`settings.render`).
|
|
746
|
+
*
|
|
747
|
+
* 🔴 IT USED TO BUILD BOTH FROM ONE `"json" | "toml"` ARGUMENT — the TOML
|
|
748
|
+
* branch also hard-coded Codex's flat entry and the JSON branch Claude Code's
|
|
749
|
+
* nested one, so "TOML" silently meant "flat" and "JSON" meant "nested". Two
|
|
750
|
+
* facts on one switch: a JSON harness with flat entries, or a TOML one with
|
|
751
|
+
* nested entries, was unrepresentable, and the day one appeared the branch
|
|
752
|
+
* would have quietly produced the other harness's shape.
|
|
753
|
+
*/
|
|
754
|
+
function renderSettingsBlock(on, matcher, gateCommand, protocol, settings) {
|
|
755
|
+
const block = protocol
|
|
756
|
+
? protocol.registration(on, matcher, gateCommand)
|
|
757
|
+
: {
|
|
758
|
+
hooks: {
|
|
759
|
+
[on]: [
|
|
760
|
+
matcher === undefined
|
|
761
|
+
? { hooks: [{ type: "command", command: gateCommand }] }
|
|
762
|
+
: { matcher, hooks: [{ type: "command", command: gateCommand }] },
|
|
763
|
+
],
|
|
764
|
+
},
|
|
765
|
+
};
|
|
766
|
+
return (settings ?? settings_codec_js_1.jsonSettingsCodec)
|
|
767
|
+
.render(block)
|
|
768
|
+
.trimEnd();
|
|
764
769
|
}
|
|
765
770
|
/** What payload a dispatch kind must be handed to be able to decide at all. */
|
|
766
771
|
function requiredPayload(kind) {
|
|
@@ -947,7 +952,7 @@ function compileHookProgram(source, hook, opts = {}) {
|
|
|
947
952
|
};
|
|
948
953
|
return {
|
|
949
954
|
hooks: { [on]: [entry] },
|
|
950
|
-
settingsBlock: renderSettingsBlock(on, matcher, gateCommand, opts.
|
|
955
|
+
settingsBlock: renderSettingsBlock(on, matcher, gateCommand, opts.hookProtocol, opts.settings),
|
|
951
956
|
stamp: stampHook(source),
|
|
952
957
|
...(fitWarnings.length > 0 ? { warnings: fitWarnings } : {}),
|
|
953
958
|
};
|
|
@@ -32,6 +32,60 @@ export interface HookProtocol {
|
|
|
32
32
|
* Used by `compileHookProgram` when rendering the settings block.
|
|
33
33
|
*/
|
|
34
34
|
readonly matcherStyle?: "exact" | "regex";
|
|
35
|
+
/**
|
|
36
|
+
* The config fragment that registers ONE command on ONE event, in this
|
|
37
|
+
* harness's native settings SHAPE. Claude Code nests
|
|
38
|
+
* `{matcher, hooks: [{type: "command", command}]}`; Codex is flat
|
|
39
|
+
* `{matcher, command}`.
|
|
40
|
+
*
|
|
41
|
+
* 🔴 THE SHAPE HALF OF WHAT `settingsFormat` WAS STANDING IN FOR, and it does
|
|
42
|
+
* not belong with the encoding. `PluginLayout.settingsFormat` was a
|
|
43
|
+
* `"json" | "toml"` enum that three call sites read as "which entry shape",
|
|
44
|
+
* which is neither what its name says nor what its values mean — a TOML
|
|
45
|
+
* harness with CC-shaped entries, or a JSON harness with flat ones, were both
|
|
46
|
+
* expressible and both would have been read wrong. A constructor cannot be
|
|
47
|
+
* wrong about its own shape.
|
|
48
|
+
*
|
|
49
|
+
* Reading already tolerates both shapes (`core/hook-normalize.ts`); this
|
|
50
|
+
* makes WRITING symmetric.
|
|
51
|
+
*/
|
|
52
|
+
registration(on: string, matcher: string | undefined, command: string): {
|
|
53
|
+
readonly hooks: Readonly<Record<string, readonly unknown[]>>;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Merge a compiled hook's registrations into an already-parsed settings
|
|
57
|
+
* object, idempotently: entries this hook file manages are REPLACED, every
|
|
58
|
+
* other command — including the user's own hand-written hooks sharing a
|
|
59
|
+
* matcher block — is preserved.
|
|
60
|
+
*
|
|
61
|
+
* 🔴 `compiled` IS THE COMPILER'S CANONICAL BLOCK, NOT `registration`'s
|
|
62
|
+
* OUTPUT, and the asymmetry is load-bearing enough to state rather than
|
|
63
|
+
* leave to be discovered. `compileHookProgram` always produces the nested
|
|
64
|
+
* `{matcher?, hooks:[{type, command}]}` form (`CompiledHooks` —
|
|
65
|
+
* "the CC-shaped structured block a compiled hook program carries"), and each
|
|
66
|
+
* implementation converts to its own shape on the way in; Codex's flattens
|
|
67
|
+
* with `toTomlEntries`. `registration` goes the other way: it produces the
|
|
68
|
+
* NATIVE shape, for the settings block a human pastes. Feeding this method a
|
|
69
|
+
* `registration` result is a type-level no-op and a run-time TypeError on
|
|
70
|
+
* the flat side — found by the property test below, which asserted it.
|
|
71
|
+
*
|
|
72
|
+
* `managedBy` is the canonical hook-source reference whose commands this
|
|
73
|
+
* merge owns; a compiled block whose commands do not mention it is APPENDED,
|
|
74
|
+
* not replaced, which is what keeps a user's own hooks intact.
|
|
75
|
+
*
|
|
76
|
+
* On `HookProtocol` because it is the same SHAPE question `registration`
|
|
77
|
+
* answers, from the other direction: the CC shape nests several commands
|
|
78
|
+
* under one matcher (so the granularity has to be the command), the Codex
|
|
79
|
+
* shape carries one command per entry (so entry- and command-granularity
|
|
80
|
+
* coincide). Typed structurally here, because the concrete entry types are
|
|
81
|
+
* the application layer's.
|
|
82
|
+
*
|
|
83
|
+
* ⚠️ There is no `registrations(config)` INVERSE yet — reading a third shape
|
|
84
|
+
* would need one, and that is deferred until a third shape exists. What
|
|
85
|
+
* exists now is: write one (`registration`), and merge into a parsed object
|
|
86
|
+
* (this).
|
|
87
|
+
*/
|
|
88
|
+
mergeRegistrations(existing: Record<string, unknown>, compiled: Readonly<Record<string, readonly unknown[]>>, managedBy: string): Record<string, unknown>;
|
|
35
89
|
/**
|
|
36
90
|
* The events whose hook can inject **developer context** into the agent by
|
|
37
91
|
* printing `{ hookSpecificOutput: { hookEventName, additionalContext } }` on
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { HarnessAdapter, InstallReader } from "./adapter.js";
|
|
2
|
+
/**
|
|
3
|
+
* Is `vigiles` a declared dependency of this `package.json` text? Pure. Any of
|
|
4
|
+
* the four dependency fields counts — the question is "did this repo take
|
|
5
|
+
* vigiles on", not how. The vigiles repo itself is not a consumer: it IS the
|
|
6
|
+
* package, so it answers false.
|
|
7
|
+
*/
|
|
8
|
+
export declare function declaresVigilesDependency(pkgJson: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Build the reader for one adapter over one repo root.
|
|
11
|
+
*
|
|
12
|
+
* `home` is injectable so the machine read is testable without a real `$HOME`;
|
|
13
|
+
* it defaults to the user's own. Everything it feeds is advisory-only.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildInstallReader(adapter: HarnessAdapter, root: string, opts?: {
|
|
16
|
+
readonly home?: string;
|
|
17
|
+
}): InstallReader;
|
|
18
|
+
//# sourceMappingURL=install-reader.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.declaresVigilesDependency = declaresVigilesDependency;
|
|
4
|
+
exports.buildInstallReader = buildInstallReader;
|
|
5
|
+
/**
|
|
6
|
+
* The bounded reader an adapter's `advisories` is handed — built HERE, by the
|
|
7
|
+
* domain, never by the adapter.
|
|
8
|
+
*
|
|
9
|
+
* 🔴 WHY A READER AND NOT A ROOT. It is the same rule `detect(exists)` follows:
|
|
10
|
+
* an adapter given a directory and `node:fs` could enumerate anything under it,
|
|
11
|
+
* so registering an adapter would change what vigiles reads in someone's
|
|
12
|
+
* repository. {@link InstallReader.repo} answers only for paths that adapter
|
|
13
|
+
* `claims`, and spells a refusal exactly like an absence — so an adapter cannot
|
|
14
|
+
* even probe for the existence of a file outside its own surface.
|
|
15
|
+
*
|
|
16
|
+
* Two facts travel as VALUES rather than reads, because the shipped checks need
|
|
17
|
+
* them from files NO adapter claims: the repo's `package.json`, and vigiles's
|
|
18
|
+
* own package inside `node_modules`. Reading those is the domain's business, so
|
|
19
|
+
* the domain reads them once and passes the answer instead of widening the port.
|
|
20
|
+
*/
|
|
21
|
+
const node_fs_1 = require("node:fs");
|
|
22
|
+
const node_os_1 = require("node:os");
|
|
23
|
+
const node_path_1 = require("node:path");
|
|
24
|
+
/** Read a file, or null when it is missing or unreadable. Never throws. */
|
|
25
|
+
function readOrNull(absolute) {
|
|
26
|
+
try {
|
|
27
|
+
return (0, node_fs_1.existsSync)(absolute) ? (0, node_fs_1.readFileSync)(absolute, "utf-8") : null;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Directory names directly under `dir`, or [] when it is not readable. */
|
|
34
|
+
function dirNames(dir) {
|
|
35
|
+
try {
|
|
36
|
+
return (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })
|
|
37
|
+
.filter((e) => e.isDirectory())
|
|
38
|
+
.map((e) => e.name);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Is `vigiles` a declared dependency of this `package.json` text? Pure. Any of
|
|
46
|
+
* the four dependency fields counts — the question is "did this repo take
|
|
47
|
+
* vigiles on", not how. The vigiles repo itself is not a consumer: it IS the
|
|
48
|
+
* package, so it answers false.
|
|
49
|
+
*/
|
|
50
|
+
function declaresVigilesDependency(pkgJson) {
|
|
51
|
+
let pkg;
|
|
52
|
+
try {
|
|
53
|
+
pkg = JSON.parse(pkgJson);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (pkg.name === "vigiles")
|
|
59
|
+
return false;
|
|
60
|
+
return [
|
|
61
|
+
"dependencies",
|
|
62
|
+
"devDependencies",
|
|
63
|
+
"peerDependencies",
|
|
64
|
+
"optionalDependencies",
|
|
65
|
+
].some((f) => {
|
|
66
|
+
const deps = pkg[f];
|
|
67
|
+
return typeof deps === "object" && deps !== null && "vigiles" in deps;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Build the reader for one adapter over one repo root.
|
|
72
|
+
*
|
|
73
|
+
* `home` is injectable so the machine read is testable without a real `$HOME`;
|
|
74
|
+
* it defaults to the user's own. Everything it feeds is advisory-only.
|
|
75
|
+
*/
|
|
76
|
+
function buildInstallReader(adapter, root, opts = {}) {
|
|
77
|
+
const pkgJson = readOrNull((0, node_path_1.join)(root, "package.json"));
|
|
78
|
+
const home = opts.home ?? (0, node_os_1.homedir)();
|
|
79
|
+
return {
|
|
80
|
+
repo: (repoRelative) => adapter.claims(repoRelative)
|
|
81
|
+
? readOrNull((0, node_path_1.resolve)(root, repoRelative))
|
|
82
|
+
: null,
|
|
83
|
+
home: (homeRelative) => readOrNull((0, node_path_1.join)(home, homeRelative)),
|
|
84
|
+
repoDependsOnVigiles: pkgJson !== null && declaresVigilesDependency(pkgJson),
|
|
85
|
+
vendoredSkillNames: dirNames((0, node_path_1.join)(root, "node_modules", "vigiles", "skills")),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=install-reader.js.map
|