@voke-sh/voke 0.1.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.
@@ -0,0 +1,727 @@
1
+ import { Severity, DimensionId } from '@voke/core';
2
+ import { Command } from 'commander';
3
+
4
+ /**
5
+ * Version source-of-truth for Voke and MTQS (SCORE-02 / D-08).
6
+ *
7
+ * VOKE_VERSION: the current linter package version (hardcoded; build-time injection
8
+ * from package.json is out of scope for Phase 4 — a plain const keeps the value
9
+ * deterministic and import-cheap).
10
+ *
11
+ * MTQS_VERSION: the MTQS spec version this linter implements (matches spec/mtqs-v0.1.yaml
12
+ * and the existing snapshot mtqsVersion field).
13
+ *
14
+ * versionString(): returns the canonical CLI --version string.
15
+ */
16
+ /** The current linter package version. Update when package.json version changes. */
17
+ declare const VOKE_VERSION = "0.1.0";
18
+ /** The MTQS spec version implemented by this linter. */
19
+ declare const MTQS_VERSION = "0.1";
20
+ /**
21
+ * Returns the canonical version string used by the CLI --version flag.
22
+ * Format: "voke {VOKE_VERSION} (MTQS v{MTQS_VERSION})"
23
+ * Example: "voke 0.0.0 (MTQS v0.1)"
24
+ */
25
+ declare const versionString: () => string;
26
+
27
+ /**
28
+ * Ingestion data model types — verbatim from ARCHITECTURE.md (lines 127-164).
29
+ *
30
+ * These are plain TS interfaces; no Zod here (Zod validation of snapshots-on-read
31
+ * goes in snapshot-reader.ts).
32
+ *
33
+ * Determinism notes:
34
+ * - D-02: capturedAt lives in meta (separate from hashed body), excluded from
35
+ * snapshotContentHash and the byte-identical determinism test.
36
+ * - D-03: toolId = tool.name; tools sorted ascending by toolId at ingest time.
37
+ * - D-07: $ref strings are kept as-written (never dereferenced at ingestion time).
38
+ */
39
+ /** Server identity captured at connect time. */
40
+ interface ServerIdentity {
41
+ url: string | null;
42
+ name: string;
43
+ version: string;
44
+ protocolVersion: string;
45
+ }
46
+ /**
47
+ * Per-tool snapshot — the stable serialized form both L1 (lint) and L2 (diff) operate on.
48
+ * contentHash enables O(1) change detection across snapshot versions.
49
+ */
50
+ interface ToolSnapshot {
51
+ toolId: string;
52
+ contentHash: string;
53
+ name: string;
54
+ title?: string;
55
+ description?: string;
56
+ inputSchema: Record<string, unknown>;
57
+ outputSchema?: Record<string, unknown>;
58
+ annotations?: Record<string, unknown>;
59
+ }
60
+ /**
61
+ * Top-level snapshot produced by ingestion (live or offline).
62
+ *
63
+ * D-02: meta.capturedAt is provenance — excluded from hashing and the x3 determinism test.
64
+ * D-01: VokeSnapshot is the raw tool surface; LintReport is the separate scored artifact.
65
+ */
66
+ interface VokeSnapshot {
67
+ snapshotVersion: '1';
68
+ mtqsVersion: string;
69
+ server: ServerIdentity;
70
+ meta: {
71
+ capturedAt: string;
72
+ };
73
+ tools: ToolSnapshot[];
74
+ }
75
+
76
+ /**
77
+ * VokeConfig — the resolved configuration object passed into every RuleContext.
78
+ *
79
+ * Full Zod loader + ConfigError (exit 7) are deferred to Phase 4.
80
+ * Phase 2 only needs the type so RuleContext.config typechecks.
81
+ */
82
+ interface VokeConfig {
83
+ severityOverrides?: Record<string, Severity>;
84
+ minScore?: number;
85
+ }
86
+
87
+ /**
88
+ * RuleTarget — whether a rule fires per-tool or once for the full server surface (ENG-02).
89
+ * The only server-scoped rule in MTQS v0.1 is MTQS-N03 (duplicate name check).
90
+ */
91
+ type RuleTarget = 'tool' | 'server';
92
+ /**
93
+ * FindingLocation — points to the exact location in the tool definition that triggered the finding.
94
+ *
95
+ * tool: toolId of the tool (or '' for server-scoped findings)
96
+ * path: JSON path within the tool definition, e.g. ['inputSchema', 'properties', 'user']
97
+ */
98
+ interface FindingLocation {
99
+ tool: string;
100
+ path: string[];
101
+ }
102
+ /**
103
+ * Finding — the runtime finding produced by rule functions.
104
+ *
105
+ * NOTE: This intentionally extends the scoring-facing @voke/core Finding ({ruleId, severity, dimension})
106
+ * with location, message, and fixHint. Do NOT re-export this under the same name from the engine
107
+ * barrel as @voke/core's Finding — they serve different purposes and must remain distinguishable.
108
+ *
109
+ * severity: the RESOLVED severity (after config.severityOverrides) — scoring uses resolved values.
110
+ */
111
+ interface Finding {
112
+ ruleId: string;
113
+ dimension: DimensionId;
114
+ severity: Severity;
115
+ message: string;
116
+ location: FindingLocation;
117
+ fixHint: string;
118
+ }
119
+ /**
120
+ * RuleContext — the frozen object passed to every rule function (D-14).
121
+ *
122
+ * tool: the ToolSnapshot being evaluated (null for server-scoped rules — ENG-02)
123
+ * surface: the full sorted server surface (ReadonlyArray for type-level purity enforcement)
124
+ * config: resolved VokeConfig including any severity overrides (Readonly for compile-time safety)
125
+ *
126
+ * Object.freeze in the runner provides the runtime immutability guarantee on top of TypeScript
127
+ * readonly annotations (RESEARCH.md Pattern 11).
128
+ */
129
+ interface RuleContext {
130
+ readonly tool: ToolSnapshot | null;
131
+ readonly surface: ReadonlyArray<ToolSnapshot>;
132
+ readonly config: Readonly<VokeConfig>;
133
+ }
134
+ /**
135
+ * RuleFunction — the pure synchronous contract every MTQS rule must satisfy (ENG-01).
136
+ *
137
+ * Rules MUST NOT: call Date.now(), Math.random(), fetch(), fs.*,
138
+ * or mutate the context. All such violations are caught at test time by the
139
+ * frozen context + network-blocked test infrastructure (D-14).
140
+ */
141
+ type RuleFunction = (ctx: RuleContext) => Finding[];
142
+ /**
143
+ * RuleDefinition — the full descriptor for a single MTQS rule registered in the RuleRegistry.
144
+ */
145
+ interface RuleDefinition {
146
+ id: string;
147
+ description: string;
148
+ dimension: DimensionId;
149
+ target: RuleTarget;
150
+ defaultSeverity: Severity;
151
+ fixHint: string;
152
+ mtqsVersion: string;
153
+ fn: RuleFunction;
154
+ }
155
+
156
+ /**
157
+ * Tier — A-F quality tier assigned to a tool or server score.
158
+ * Cuts: A>=90, B>=80, C>=70, D>=60, F<60 (from @voke/core tierFor).
159
+ */
160
+ type Tier = 'A' | 'B' | 'C' | 'D' | 'F';
161
+ /**
162
+ * ToolReport — scored result for a single tool in a LintReport.
163
+ *
164
+ * contentHash: from the ToolSnapshot (ING-04 / D-03) — enables L2 delta detection.
165
+ * findings: the runtime findings for this tool (includes message, location, fixHint).
166
+ * score: capped integer score (0-100) using @voke/core applyCaps.
167
+ * tier: A-F from @voke/core tierFor.
168
+ */
169
+ interface ToolReport {
170
+ toolId: string;
171
+ contentHash: string;
172
+ findings: Finding[];
173
+ score: number;
174
+ tier: Tier;
175
+ }
176
+ /**
177
+ * LintReport — the top-level scored artifact produced by buildReport.
178
+ *
179
+ * D-01: LintReport is a separate artifact from VokeSnapshot. The raw surface
180
+ * (VokeSnapshot) stays independent of rule/score changes; this is the scored output.
181
+ *
182
+ * D-02: meta.generatedAt is wall-clock provenance — excluded from serializeReportBody
183
+ * and the byte-identical determinism test. The canonical hashed/compared body covers
184
+ * only server identity, snapshotContentHash, tool scores, and server aggregate.
185
+ */
186
+ interface LintReport {
187
+ vokeVersion: string;
188
+ mtqsVersion: string;
189
+ /** D-02: provenance block excluded from serializeReportBody / determinism test */
190
+ meta: {
191
+ generatedAt: string;
192
+ };
193
+ server: ServerIdentity;
194
+ /** SHA-256 of canonical sorted tools surface (surfaceContentHash) */
195
+ snapshotContentHash: string;
196
+ tools: ToolReport[];
197
+ serverScore: number;
198
+ serverTier: Tier;
199
+ }
200
+
201
+ /**
202
+ * Options for a single lint run.
203
+ */
204
+ interface RunLintOpts {
205
+ /** MCP server URL (http/https) or path to a saved snapshot JSON file. */
206
+ target: string;
207
+ /** Output format: 'human' for terminal display; 'json' for parseable LintReport. */
208
+ output: 'human' | 'json';
209
+ /** Raw header strings "Key: Value" passed to ingestLive (ignored for file targets). */
210
+ headers: string[];
211
+ /** Per-request timeout in ms for live targets (threaded to ingestLive.timeoutMs). */
212
+ timeout: number;
213
+ /** If set: exit 1 when serverScore < minScore; exit 0 otherwise (D-13). */
214
+ minScore?: number;
215
+ /** Print per-finding detail lines under each below-A tool. */
216
+ verbose: boolean;
217
+ /** Enable ANSI color codes in human output. Score line is never colored (D-03). */
218
+ color: boolean;
219
+ /** If set: also write the raw VokeSnapshot to this path (D-11 — distinct from LintReport). */
220
+ saveSnapshot?: string;
221
+ /**
222
+ * If set and non-empty: launch an MCP subprocess via StdioClientTransport instead of
223
+ * resolveTarget. stdioArgs[0] is the command; the rest are args. Bypasses resolveTarget.
224
+ */
225
+ stdioArgs?: string[];
226
+ /**
227
+ * Extra environment variables for the stdio subprocess (from --env KEY=VAL).
228
+ * Values are NEVER echoed in output (D-09/Pitfall 4).
229
+ */
230
+ extraEnv?: Record<string, string>;
231
+ }
232
+ /**
233
+ * Result from a lint run.
234
+ */
235
+ interface RunLintResult {
236
+ /** The assembled LintReport with per-tool + server scores. */
237
+ report: LintReport;
238
+ /** Formatted output string (human or JSON depending on opts.output). */
239
+ text: string;
240
+ /** Process exit code: 0 = pass; 1 = minScore not met; errors throw (not exit code here). */
241
+ exitCode: number;
242
+ }
243
+ /**
244
+ * Run the full lint pipeline for a target.
245
+ *
246
+ * For file targets: readSnapshot (sync, no network).
247
+ * For live targets: ingestLive with opts.timeout threaded as timeoutMs.
248
+ *
249
+ * If opts.saveSnapshot is set, writes the raw VokeSnapshot to that path before linting.
250
+ * The VokeSnapshot is the re-lint input artifact (has snapshotVersion, tools[].toolId,
251
+ * meta.capturedAt) — it does NOT contain serverScore or per-tool scores (D-11).
252
+ *
253
+ * @throws UsageError (exitCode=3) — bad target string
254
+ * @throws ConnectError (exitCode=2) — both transports failed
255
+ * @throws PartialPageError (exitCode=4) — pagination page failed
256
+ * @throws DepthExceededError (exitCode=6) — schema too deep
257
+ * @throws RuleExecutionError — rule fn threw (internal error)
258
+ */
259
+ declare const runLint: (opts: RunLintOpts) => Promise<RunLintResult>;
260
+
261
+ /**
262
+ * Returns a copy of the headers object with every value replaced by "[MASKED]".
263
+ * Use this before including headers in any log, error message, or serialized output.
264
+ * Raw header values must NEVER appear in a VokeSnapshot, LintReport, or log.
265
+ */
266
+ declare const maskHeaders: (headers: Record<string, string>) => Record<string, string>;
267
+ /**
268
+ * Options for live ingestion.
269
+ */
270
+ interface IngestLiveOptions {
271
+ /** Full URL of the MCP server endpoint (e.g., "http://localhost:3000/mcp") */
272
+ url: string;
273
+ /**
274
+ * Raw header strings in "Key: Value" format (mirrors curl --header).
275
+ * Used for bearer token auth and custom headers.
276
+ * Values are NEVER stored on the returned VokeSnapshot (D-09).
277
+ */
278
+ rawHeaders?: string[];
279
+ /**
280
+ * Per-listTools-page timeout in ms; defaults to LIST_TOOLS_TIMEOUT_MS (30000).
281
+ * Threaded through to fetchAllTools — controls how long each paginated request waits.
282
+ */
283
+ timeoutMs?: number;
284
+ }
285
+ /**
286
+ * Connects to a live MCP server, fetches the complete paginated tool surface,
287
+ * and returns a sorted, content-hashed VokeSnapshot.
288
+ *
289
+ * Determinism enforced:
290
+ * - Tools sorted ascending by toolId via localeCompare('en', {sensitivity:'variant'}) (#1/#5)
291
+ * - Per-tool contentHash = SHA-256 of canonical {name,description,inputSchema,outputSchema,annotations} (#6)
292
+ * - meta.capturedAt is the ONLY wall-clock value; excluded from hashing (D-02)
293
+ * - Raw header values never included in snapshot (D-09)
294
+ *
295
+ * Failure modes:
296
+ * - ConnectError (exit 2): both transports fail to connect
297
+ * - PartialPageError (exit 4): any listTools page fails
298
+ * - DepthExceededError (exit 6): any tool inputSchema exceeds DEPTH_HARD_CAP
299
+ */
300
+ declare const ingestLive: (opts: IngestLiveOptions) => Promise<VokeSnapshot>;
301
+
302
+ /**
303
+ * Commander v15 program definition for the voke CLI (CLI-01/02/03, D-07/08/13/15/16).
304
+ *
305
+ * Exports:
306
+ * - buildProgram(): builds a fresh Command instance (used by bin entrypoint and tests)
307
+ * - resolveLintOpts(): pure flag-resolution helper (unit-testable; no network, no IO)
308
+ *
309
+ * Design:
310
+ * - Flag resolution and validation is isolated in resolveLintOpts so program.test.ts
311
+ * can test color-disabling, NO_COLOR, and invalid-flag rejection without invoking runLint.
312
+ * - The lint action handler calls resolveLintOpts then runLint and then sets process.exitCode.
313
+ * It does NOT call process.exit on success — the bin entrypoint's main().catch handles errors.
314
+ *
315
+ * Token masking (D-15/16):
316
+ * - maskHeaders is called in any echoed diagnostic line that would include header values.
317
+ * - Raw header values must NEVER appear in stderr (injected via buildHeaders/maskHeaders).
318
+ */
319
+
320
+ /**
321
+ * Resolves and validates the raw commander-parsed options into a RunLintOpts object.
322
+ *
323
+ * Validates:
324
+ * - --output must be 'human' or 'json' (throws UsageError on unknown format)
325
+ * - --min-score must be an integer 0-100 (throws UsageError if out of range)
326
+ * - --timeout must be a positive integer (throws UsageError if invalid)
327
+ * - color: disabled if --ci, NO_COLOR env var, or --no-color flag is set (D-03/D-16)
328
+ * - --env: each entry must be KEY=VAL (throws UsageError if missing '=')
329
+ *
330
+ * This function has no side effects and performs no IO. It is the unit-testable
331
+ * boundary between commander's parsed options and the runLint pipeline.
332
+ *
333
+ * @param target - the positional [target] argument (may be empty string if stdio mode)
334
+ * @param opts - the raw commander option object (typed loosely for test flexibility)
335
+ * @returns validated RunLintOpts ready for runLint
336
+ * @throws UsageError (exitCode=3) on invalid flag values
337
+ */
338
+ declare const resolveLintOpts: (target: string, opts: Record<string, unknown>) => RunLintOpts;
339
+ /**
340
+ * Build a fresh commander v15 Command instance for the voke CLI.
341
+ *
342
+ * Always returns a NEW instance (test isolation — no module-level singleton).
343
+ *
344
+ * The program is structured as:
345
+ * voke [options]
346
+ * --version / -v: print "voke X.Y.Z (MTQS v0.1)" (D-08)
347
+ * voke lint [target] [flags...] [-- <cmd> [args...]]
348
+ * D-07 flags: --output, -H/--header, --timeout, --min-score, --ci, --no-color, --save-snapshot, --verbose
349
+ * ING-06 flags: --env KEY=VAL (repeatable; values masked in output, D-09)
350
+ *
351
+ * @param stdioArgs - pre-split args after '--' in process.argv (see cli/index.ts).
352
+ * When set and non-empty, lint target becomes optional and stdio mode is used.
353
+ */
354
+ declare const buildProgram: (stdioArgs?: string[]) => Command;
355
+
356
+ /**
357
+ * Extensible target resolver for the voke CLI (D-04 / D-05 / D-06).
358
+ *
359
+ * Dispatches a target string to a transport kind (live | file) via a SCHEME_HANDLERS
360
+ * registry. This is the extension seam: Phase 5 stdio support adds an 'stdio:' entry
361
+ * here without requiring a resolver rewrite (D-05 extensibility guarantee).
362
+ *
363
+ * D-06: schemeless host:port strings (e.g. "localhost:3000/mcp") are REJECTED with
364
+ * a did-you-mean hint rather than silently treated as file paths.
365
+ *
366
+ * Exit codes:
367
+ * UsageError.exitCode = 3 (D-13: usage / argument error)
368
+ */
369
+ /** The kind of transport to use for the resolved target. */
370
+ type TransportKind = 'live' | 'file' | 'stdio';
371
+ /** Resolved target with its transport kind. */
372
+ interface ResolvedTarget {
373
+ kind: TransportKind;
374
+ target: string;
375
+ /** Only present when kind === 'stdio'. Contains the command + args for StdioClientTransport. */
376
+ stdioArgs?: string[];
377
+ }
378
+ /**
379
+ * Thrown when the target string is malformed (bad scheme, schemeless host:port, etc.).
380
+ * Exit code 3 per D-13 (usage / argument error).
381
+ *
382
+ * Kept in this file for Phase 4 Wave 1 to avoid editing errors.ts in a parallel wave.
383
+ * Plan 02 may re-home this to errors.ts if a shared UsageError is needed.
384
+ */
385
+ declare class UsageError extends Error {
386
+ readonly exitCode = 3;
387
+ constructor(msg: string);
388
+ }
389
+ /**
390
+ * Resolves a target string to a transport kind and canonical target.
391
+ *
392
+ * Dispatch order:
393
+ * 1. URL with scheme ("https://...") → look up in SCHEME_HANDLERS
394
+ * - Known scheme: dispatch to handler (live for http/https)
395
+ * - Unknown scheme: throw UsageError listing supported schemes
396
+ * 2. Schemeless host:port pattern ("localhost:3000/mcp") → throw UsageError with did-you-mean
397
+ * 3. Everything else → treat as a local file path
398
+ *
399
+ * @param raw - the target string provided by the user
400
+ * @returns ResolvedTarget with kind and canonical target string
401
+ * @throws UsageError (exitCode=3) for invalid/unsupported targets
402
+ */
403
+ declare const resolveTarget: (raw: string) => ResolvedTarget;
404
+
405
+ /** Options controlling human output rendering. */
406
+ interface HumanFormatOpts {
407
+ /** Whether to emit ANSI color codes for severity labels. The score line is NEVER colored. */
408
+ color: boolean;
409
+ /** Whether to print per-finding detail lines under each below-A tool. */
410
+ verbose: boolean;
411
+ }
412
+ /**
413
+ * Format a LintReport as a human-readable string.
414
+ *
415
+ * The output is a newline-joined string of lines:
416
+ * 1. Score banner (never colored, D-03)
417
+ * 2. Dimension weight breakdown (fixed order, D-01)
418
+ * 3. Table of below-A tools sorted by score ascending (D-01)
419
+ * 4. [verbose] Per-finding detail lines for each below-A tool (D-02)
420
+ *
421
+ * @param report - the LintReport to format
422
+ * @param opts - rendering options (color, verbose)
423
+ * @returns a newline-joined string; never empty
424
+ */
425
+ declare const formatHuman: (report: LintReport, opts: HumanFormatOpts) => string;
426
+
427
+ /**
428
+ * Serialize a LintReport to a canonical JSON string.
429
+ *
430
+ * The full report including meta.generatedAt is included (D-10: full consumption doc).
431
+ * Keys are sorted for stability — repeated calls on the same object yield identical output.
432
+ *
433
+ * @param report - the LintReport to serialize
434
+ * @returns a canonical JSON string representing the full report
435
+ */
436
+ declare const formatJson: (report: LintReport) => string;
437
+
438
+ /**
439
+ * Deterministic sorted-key JSON serializer.
440
+ *
441
+ * Invariants:
442
+ * - Object keys are sorted with localeCompare('en', { sensitivity: 'variant' }) for
443
+ * locale-independent, byte-stable output (Pitfall 2 guard).
444
+ * - Arrays are NEVER reordered (order is semantic in JSON Schema prefixItems, enum, etc.).
445
+ * - undefined-valued keys are omitted (mirrors JSON.stringify behavior, Pitfall 7 guard).
446
+ * - Internal $ref strings (D-07) are kept byte-for-byte — no dereferencing before hashing.
447
+ *
448
+ * This is the single source of byte-stable serialization used by:
449
+ * - toolContentHash (per-tool identity, ING-04)
450
+ * - surfaceContentHash (snapshotContentHash, ARCHITECTURE point #6)
451
+ * - x3 byte-identical determinism test (ENG-04, D-12)
452
+ */
453
+ declare const canonicalJson: (value: unknown) => string;
454
+
455
+ /**
456
+ * Deterministic SHA-256 hash helper.
457
+ *
458
+ * Returns a 64-char lowercase hex digest of the UTF-8 encoded input string.
459
+ * Uses node:crypto (built-in, synchronous, deterministic) — no external dep.
460
+ */
461
+ declare const sha256: (input: string) => string;
462
+ /**
463
+ * Per-tool content hash (ING-04, D-03).
464
+ *
465
+ * Hashes exactly the 5 canonical fields: name, description, inputSchema, outputSchema, annotations.
466
+ * Key order is normalized via canonicalJson so the hash is stable regardless of JS object key
467
+ * insertion order. undefined fields are omitted (canonicalJson mirrors JSON.stringify).
468
+ */
469
+ declare const toolContentHash: (tool: {
470
+ name: string;
471
+ description?: string;
472
+ inputSchema: unknown;
473
+ outputSchema?: unknown;
474
+ annotations?: unknown;
475
+ }) => string;
476
+ /**
477
+ * Surface content hash (ARCHITECTURE determinism point #6).
478
+ *
479
+ * Sorts tools ascending by toolId (locale-independent) before hashing, so the hash is
480
+ * independent of the input array order (MCP protocol does not guarantee tool ordering).
481
+ */
482
+ declare const surfaceContentHash: (tools: ReadonlyArray<{
483
+ toolId: string;
484
+ }>) => string;
485
+
486
+ /**
487
+ * Reads a VokeSnapshot JSON file from disk.
488
+ * Validates the parsed JSON against VokeSnapshotSchema (Zod).
489
+ * Throws a ZodError with a clear message if the file is malformed.
490
+ *
491
+ * Guarantees ING-03: no network, no MCP SDK, no fetch.
492
+ *
493
+ * @param path - absolute or relative path to the .json snapshot file
494
+ * @returns Validated VokeSnapshot
495
+ * @throws ZodError if the snapshot fails validation
496
+ * @throws Error if the file cannot be read or parsed as JSON
497
+ */
498
+ declare const readSnapshot: (path: string) => VokeSnapshot;
499
+
500
+ /**
501
+ * Serializes a VokeSnapshot to disk.
502
+ *
503
+ * The output uses canonicalJson (sorted keys) on the entire snapshot object
504
+ * to ensure byte-identical re-writes of the same surface.
505
+ * meta.capturedAt is preserved as written (D-02 provenance).
506
+ *
507
+ * @param path - destination file path (will overwrite if exists)
508
+ * @param snapshot - the VokeSnapshot to write
509
+ */
510
+ declare const writeSnapshot: (path: string, snapshot: VokeSnapshot) => void;
511
+
512
+ /**
513
+ * RuleRegistry — the startup-time plugin boundary for the MTQS rule engine (ENG-03).
514
+ *
515
+ * Lifecycle:
516
+ * 1. register() rules during module initialization
517
+ * 2. seal() before the runner executes — rejects any further registration
518
+ * 3. list() returns rules sorted deterministically by id for the runner
519
+ * 4. applyOverrides() returns a NEW sealed RuleRegistry with adjusted severities —
520
+ * never mutates the original (D-ENG-03 no-mutation guarantee)
521
+ *
522
+ * Anti-Pattern guard: never create a module-level singleton RuleRegistry.
523
+ * Use createDefaultRegistry() to get a fresh instance per run, preserving test isolation.
524
+ */
525
+ declare class RuleRegistry {
526
+ private readonly rules;
527
+ private sealed;
528
+ /**
529
+ * Register a rule definition.
530
+ * Throws if the registry is sealed or if the same id has already been registered.
531
+ */
532
+ register(def: RuleDefinition): void;
533
+ /**
534
+ * Seal the registry. After sealing, register() throws.
535
+ * Returns `this` for fluent chaining: `const r = new RuleRegistry(); r.seal();`
536
+ */
537
+ seal(): this;
538
+ /**
539
+ * Return all registered rules sorted ascending by id.
540
+ *
541
+ * Sort uses localeCompare('en', {sensitivity:'variant'}) for determinism across
542
+ * environments — avoids LC_ALL variation (RESEARCH.md Pitfall 2, ARCHITECTURE.md D-pt-#4).
543
+ */
544
+ list(): ReadonlyArray<RuleDefinition>;
545
+ /**
546
+ * Return a NEW sealed RuleRegistry with severity overrides applied.
547
+ *
548
+ * This method NEVER mutates the current registry — it constructs a fresh one
549
+ * and registers cloned rule definitions with adjusted defaultSeverity where
550
+ * an override exists. (ENG-03 / D-ENG-03)
551
+ *
552
+ * @param overrides - Map of ruleId → Severity. Unknown rule ids are silently ignored.
553
+ */
554
+ applyOverrides(overrides: Record<string, Severity>): RuleRegistry;
555
+ }
556
+ /**
557
+ * createDefaultRegistry — factory that returns a fresh sealed RuleRegistry.
558
+ *
559
+ * In Phase 2, returns an empty sealed registry.
560
+ * Phase 3 will import './rules/index.js' here to register all MTQS rules.
561
+ *
562
+ * Always use this factory rather than a module-level singleton to preserve test isolation.
563
+ */
564
+ declare const createDefaultRegistry: () => RuleRegistry;
565
+
566
+ /**
567
+ * RuleExecutionError — thrown when a rule's fn() call throws (D-13: fail-fast).
568
+ *
569
+ * This is the exit-code-5 failure class (RESEARCH.md Pattern 12).
570
+ * The numeric exit code mapping lives in the CLI layer (Phase 4);
571
+ * the error type is defined here in the engine.
572
+ *
573
+ * Carries ruleId + toolId so CI output can pinpoint exactly which rule
574
+ * and which tool caused the failure — directly actionable.
575
+ */
576
+ declare class RuleExecutionError extends Error {
577
+ readonly ruleId: string;
578
+ readonly toolId: string;
579
+ readonly cause: unknown;
580
+ constructor(ruleId: string, toolId: string, cause: unknown);
581
+ }
582
+ /**
583
+ * runRules — the pure runner that executes all registered rules against a tool surface.
584
+ *
585
+ * Determinism guarantees (RESEARCH.md Pattern 9):
586
+ * 1. surface sorted ascending by toolId (localeCompare 'en' variant) before iteration
587
+ * 2. rules iterated in registry.list() order (sorted ascending by id)
588
+ * 3. each RuleContext is Object.freeze'd to prevent mutation (D-14)
589
+ * 4. returned findings sorted toolId → ruleId → path.join('.') (spec §4.4 fixed evaluation order)
590
+ * 5. severity resolved via config.severityOverrides[id] ?? defaultSeverity; 'off' → skip
591
+ *
592
+ * Fail-fast (D-13): any rule fn() that throws is wrapped in RuleExecutionError and
593
+ * rethrown immediately — no silent swallow, no partial finding set.
594
+ *
595
+ * @param surface - the tool surface (will be sorted internally; does not mutate the array)
596
+ * @param registry - sealed RuleRegistry (from createDefaultRegistry or applyOverrides)
597
+ * @param config - resolved VokeConfig (severityOverrides applied upstream or default {})
598
+ * @returns Finding[] sorted deterministically per spec
599
+ */
600
+ declare const runRules: (surface: ReadonlyArray<ToolSnapshot>, registry: RuleRegistry, config: VokeConfig) => Finding[];
601
+
602
+ /**
603
+ * buildReport — assembles a LintReport from a VokeSnapshot + engine findings.
604
+ *
605
+ * Scoring is entirely delegated to @voke/core (scoreTool, applyCaps, tierFor, serverScore).
606
+ * No scoring arithmetic is reimplemented here.
607
+ *
608
+ * Determinism guarantees:
609
+ * 1. snapshot.tools assumed already sorted ascending by toolId (ingestion guarantees this)
610
+ * 2. findings grouped by location.tool; CoreFinding map strips location/message/fixHint
611
+ * 3. snapshotContentHash = surfaceContentHash(snapshot.tools) — surface hash, sort-stable
612
+ * 4. serverScore = coreServerScore(toolScores) — mean of toolId-sorted scores
613
+ * 5. meta.generatedAt = new Date().toISOString() — the ONLY wall-clock call (D-02)
614
+ *
615
+ * @param snapshot - the VokeSnapshot (tools already sorted ascending by toolId)
616
+ * @param findings - all runtime findings from runRules (sorted toolId→ruleId→path)
617
+ * @param opts - optional vokeVersion override (defaults to '0.0.0')
618
+ */
619
+ declare const buildReport: (snapshot: VokeSnapshot, findings: Finding[], opts?: {
620
+ vokeVersion?: string;
621
+ }) => LintReport;
622
+ /**
623
+ * serializeReportBody — produces the canonical, meta-stripped string that the
624
+ * byte-identical determinism test (ENG-04 / D-12) compares.
625
+ *
626
+ * D-02: strips `meta` (which contains generatedAt, the only wall-clock value)
627
+ * before serializing. The compared body is deterministic because it contains no
628
+ * wall-clock or provenance data.
629
+ *
630
+ * Uses canonicalJson for sorted-key, locale-stable serialization (Pitfall 1/2 guard).
631
+ */
632
+ declare const serializeReportBody: (report: LintReport) => string;
633
+
634
+ /**
635
+ * Schema Correctness rules (MTQS-S01 through MTQS-S08).
636
+ *
637
+ * These 8 rules cover RULE-01 in full and form the T1 (1.5x weight) correctness floor.
638
+ * A broken inputSchema means the tool literally cannot be used by an agent.
639
+ *
640
+ * All rules:
641
+ * - target: 'tool' (per-tool evaluation)
642
+ * - dimension: 'schema'
643
+ * - mtqsVersion: '0.1'
644
+ * - pure synchronous functions with ZERO IO (no fetch, no Date.now, no Math.random)
645
+ *
646
+ * Helpers from schema-checks.ts are REUSED (never reimplemented):
647
+ * - isValidJsonSchema2020: S03, S06
648
+ * - hasExternalRef: S04
649
+ * - schemaDepth: S05
650
+ */
651
+
652
+ declare const schemaRules: RuleDefinition[];
653
+
654
+ /**
655
+ * descriptionRules — exported array of all Description-as-Prompt rules.
656
+ * Order: D01, D02, D03 (alphabetical by ID, matching spec evaluation order §4.4).
657
+ */
658
+ declare const descriptionRules: RuleDefinition[];
659
+
660
+ /**
661
+ * namingRules — exported array of all Naming rules.
662
+ * Order: N01, N02, N03 (alphabetical by ID, matching spec evaluation order §4.4).
663
+ * N03 has target: 'server' — the engine routes it differently from N01/N02.
664
+ */
665
+ declare const namingRules: RuleDefinition[];
666
+
667
+ /**
668
+ * parameters.ts — MTQS Parameter Semantics rules (dimension: 'parameters', weight: 1.2×)
669
+ *
670
+ * Exports `parameterRules: RuleDefinition[]` containing P01 and P02.
671
+ * Both rules operate per-tool, are pure synchronous functions, and perform zero IO.
672
+ *
673
+ * Sources:
674
+ * - Anthropic "Writing effective tools for agents" (Sep 2025)
675
+ * - arxiv:2602.14878 — Opaque Parameters smell at 84.3% prevalence
676
+ * - spec/MTQS-v0.1.md §3 MTQS-P01 and MTQS-P02
677
+ * - spec/mtqs-v0.1.yaml (fixHints copied VERBATIM)
678
+ */
679
+
680
+ declare const parameterRules: RuleDefinition[];
681
+
682
+ /**
683
+ * annotations.ts — MTQS Annotation Transparency rules (A01–A06), dimension 'annotations' (weight 1.5x).
684
+ *
685
+ * These rules evaluate the MCP tool annotations object and its four hint booleans:
686
+ * readOnlyHint, destructiveHint, idempotentHint, openWorldHint.
687
+ *
688
+ * All rule functions are pure: no IO, no Date.now(), no Math.random(), no fetch.
689
+ * Network-blocked test (D-14) enforces this at test time.
690
+ *
691
+ * Rule coverage:
692
+ * A01 (info) — annotations object absent → tool defaults to riskiest posture
693
+ * A02 (warning) — readOnlyHint not a boolean within annotations
694
+ * A03 (warning) — destructiveHint not a boolean within annotations
695
+ * A04 (info) — idempotentHint not a boolean within annotations
696
+ * A05 (info) — openWorldHint not a boolean within annotations
697
+ * A06 (error) — readOnlyHint:true AND destructiveHint:true contradiction
698
+ */
699
+
700
+ declare const annotationRules: RuleDefinition[];
701
+
702
+ /**
703
+ * rules/index.ts — MTQS v0.1 rule aggregator.
704
+ *
705
+ * Combines all five dimension rule arrays into a single `allRules` export
706
+ * that is consumed by createDefaultRegistry() to populate the sealed registry.
707
+ *
708
+ * Wave 1 dimension modules:
709
+ * schemaRules — 8 rules (03-01)
710
+ * descriptionRules — 3 rules (03-02)
711
+ * namingRules — 3 rules (03-02)
712
+ * parameterRules — 2 rules (03-03)
713
+ * annotationRules — 6 rules (03-04)
714
+ *
715
+ * Total: 22 rules.
716
+ */
717
+
718
+ /**
719
+ * allRules — the complete ordered set of 22 MTQS v0.1 rule definitions.
720
+ *
721
+ * Order: schema, description, naming, parameters, annotations.
722
+ * The registry sorts by id internally on list() — insertion order does not
723
+ * affect determinism, but a stable source order aids review.
724
+ */
725
+ declare const allRules: RuleDefinition[];
726
+
727
+ export { type Finding, type FindingLocation, type HumanFormatOpts, type IngestLiveOptions, type LintReport, MTQS_VERSION, type ResolvedTarget, type RuleContext, type RuleDefinition, RuleExecutionError, type RuleFunction, RuleRegistry, type RuleTarget, type RunLintOpts, type RunLintResult, type ServerIdentity, type Tier, type ToolReport, type ToolSnapshot, type TransportKind, UsageError, VOKE_VERSION, type VokeConfig, type VokeSnapshot, allRules, annotationRules, buildProgram, buildReport, canonicalJson, createDefaultRegistry, descriptionRules, formatHuman, formatJson, ingestLive, maskHeaders, namingRules, parameterRules, readSnapshot, resolveLintOpts, resolveTarget, runLint, runRules, schemaRules, serializeReportBody, sha256, surfaceContentHash, toolContentHash, versionString, writeSnapshot };