@syv-ai/rulecast 0.1.1 → 0.3.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/index.d.ts CHANGED
@@ -1,787 +1,57 @@
1
- import { ZodType, ZodTypeDef, z } from 'zod';
1
+ import { A as Adapter, D as Delivery, a as DetectorRuleInput, b as DetectorRun, M as Match, c as Detector, d as AnyDetector, e as AdapterInput, E as Event } from './types-C8b7hrnG.js';
2
+ export { f as AdapterInstall, C as Cache, g as ChangeSet, h as CheckResult, i as CompiledDetector, j as CompiledRule, k as DeliveredReference, l as DetectorCheck, m as DetectorEvent, n as DetectorRegistry, o as DetectorResult, p as DetectorRule, q as DetectorSettings, r as DetectorWarm, s as EventKind, F as Finding, I as InstallScope, L as LLM_PROVIDERS, t as LlmFinding, u as LlmProvider, v as LlmProviderName, w as LlmRequest, x as LlmSettings, y as LlmUnavailableError, O as Omitted, R as ReferenceMode, z as ResolvedReference, B as RuleScope, S as Severity, T as TouchRule, W as WriteIntent, G as createRegistry, H as defaultDetectorSettings, J as emptyDelivery, K as isDetectorRule } from './types-C8b7hrnG.js';
3
+ import 'zod';
4
+
5
+ declare const claudeCodeAdapter: Adapter;
2
6
 
3
- type EventKind = "touch" | "edit" | "verify" | "prompt" | "reset" | "guard";
4
- type DetectorEvent = "edit" | "verify";
5
- type Severity = "error" | "warning";
6
- type ReferenceMode = "inject" | "read";
7
7
  /**
8
- * What a `guard` event's write would do, as the agent's tool call describes it and before anything
9
- * has touched the disk. The adapter reports the intent; the pipeline turns it into the file's
10
- * proposed content, because only the pipeline may read the file it will be applied to.
8
+ * How much of a rule a repository already owes, per rule and per file.
9
+ *
10
+ * Counts, never rates. One codebase went from 61 violations in 80 files to 57 in 194 over eight
11
+ * months: the rate fell from 76% to 29% and the count did not move, because new code complied and
12
+ * the old violations were only diluted. A team watching the rate would have believed it was fixing
13
+ * the problem. So this reports the stock and leaves the division to nobody.
11
14
  */
12
- interface WriteIntent {
13
- /** The whole file, for a tool that supplies it (Write).*/
14
- content?: string;
15
- /** A replacement inside the current file (Edit). */
16
- edit?: {
17
- find: string;
18
- replace: string;
19
- all: boolean;
20
- };
21
- }
22
- interface Event {
23
- kind: EventKind;
24
- /** Repo-relative paths (adapters may give absolute ones; the hook command converts them). Empty for prompt and reset. */
25
- files: string[];
26
- /** guard only: what the agent is about to write to `files[0]`. */
27
- intent?: WriteIntent;
28
- /** touch from a read: the whole file was read. */
29
- completeRead?: boolean;
30
- /** verify from the CLI: the commit the baseline is read from (the merge base for --from-ref). */
31
- baseCommit?: string;
32
- session?: {
33
- id: string;
34
- agentId?: string;
35
- };
36
- cwd: string;
37
- }
38
- interface Match {
39
- file: string;
40
- /** 1-based. */
41
- line: number;
42
- endLine: number;
43
- column: number;
44
- text: string;
45
- /** Exactly the names the detector declared for the rule. */
46
- captures: Record<string, string>;
47
- }
48
- interface ChangeSet {
49
- /** 1-based inclusive ranges in the current file. */
50
- changedLines: [start: number, end: number][];
51
- }
52
- interface Cache {
53
- get<T>(key: string): Promise<T | undefined>;
54
- set(key: string, value: unknown): Promise<void>;
55
- }
56
- interface ResolvedReference {
57
- /** "conventions/api-access.md#frontend-data-flow" */
58
- ref: string;
59
- content: string;
60
- }
61
- declare const LLM_PROVIDERS: readonly ["claude-code", "opencode", "anthropic", "openai-compatible"];
62
- type LlmProviderName = (typeof LLM_PROVIDERS)[number];
63
- /** Project-level `llm` settings from .rulecast-config.yaml (spec §6, §12). */
64
- interface LlmSettings {
65
- provider: LlmProviderName;
66
- /** Overrides the provider's own endpoint; the only way to reach Azure OpenAI or Ollama. */
67
- baseUrl: string | null;
68
- apiKeyEnv: string;
69
- maxFilesPerVerify: number;
70
- }
71
- /** Project settings the core passes to every detector run; only `llm` reads them today. */
72
- interface DetectorSettings {
73
- llm: LlmSettings;
74
- }
75
- declare function defaultDetectorSettings(): DetectorSettings;
76
- interface DetectorRuleInput<Config> {
77
- id: string;
78
- config: Config;
79
- files: string[];
80
- context: ResolvedReference[];
81
- }
82
- interface DetectorRun<Config> {
83
- event: DetectorEvent;
84
- rules: DetectorRuleInput<Config>[];
85
- /**
86
- * The file's content, null when it no longer exists. A detector that can read a file itself must
87
- * use this instead: before a write, the content that matters is the one the agent proposed, which
88
- * is not on disk and never will be if the write is refused.
89
- */
90
- read(file: string): Promise<string | null>;
91
- /** File absent = no baseline, the whole file is new. */
92
- changes: ReadonlyMap<string, ChangeSet>;
93
- cache: Cache;
94
- /** Project settings from .rulecast-config.yaml; a detector that needs none ignores them. */
95
- settings: DetectorSettings;
96
- cwd: string;
97
- signal: AbortSignal;
98
- }
99
- interface DetectorResult {
100
- findings: {
101
- rule: string;
102
- match: Match;
103
- }[];
104
- /** rule null = the whole run failed. */
105
- errors: {
106
- rule: string | null;
107
- message: string;
108
- }[];
109
- }
110
- interface DetectorWarm<Config> {
111
- /** Every rule of this detector kind in the project. */
112
- rules: {
113
- id: string;
114
- config: Config;
115
- }[];
116
- cache: Cache;
117
- cwd: string;
118
- signal: AbortSignal;
119
- }
120
- interface DetectorCheck<Config> {
121
- /** Every rule of this detector kind in the project. */
15
+ interface BacklogSummary {
16
+ /** Violations descending, then rule ascending. */
122
17
  rules: {
123
- id: string;
124
- config: Config;
125
- }[];
126
- /** Project settings from .rulecast-config.yaml; the same value a run gets. */
127
- settings: DetectorSettings;
128
- /** The process environment, so a check can look for credentials without reaching for process.env. */
129
- env: Readonly<Record<string, string | undefined>>;
130
- cwd: string;
131
- signal: AbortSignal;
132
- }
133
- /** One thing `rulecast doctor` asked a detector about, and the answer (spec §5). */
134
- interface CheckResult {
135
- /** What was checked, as a person would name it: "ruff", "ast-grep", `model "haiku"`. */
136
- what: string;
137
- level: "ok" | "warning" | "error";
138
- /** Why: a resolved path when it worked, the reason when it did not. */
139
- detail: string;
140
- /** The rules this result decides the fate of; empty when it is about the kind as a whole. */
141
- rules: string[];
142
- }
143
- interface Detector<Config> {
144
- kind: string;
145
- /** Input is unknown: schemas may apply defaults and refinements. */
146
- schema: ZodType<Config, ZodTypeDef, unknown>;
147
- captures(config: Config): string[];
148
- events(config: Config): DetectorEvent[];
149
- /**
150
- * Whether this detector reads only through `read`, and so can judge a write before it happens
151
- * (`refuse_write`). False for anything that hands a path to another program: the proposed file is
152
- * not on disk, so ruff, eslint, ast-grep's CLI or a `command` script would judge the old one.
153
- */
154
- guards?: boolean;
155
- run(input: DetectorRun<Config>): Promise<DetectorResult>;
156
- /** Optional: build expensive caches ahead of events (rulecast warm, §13). */
157
- warm?(input: DetectorWarm<Config>): Promise<void>;
158
- /** Optional: report what this detector's rules need from the environment (rulecast doctor, §5). */
159
- check?(input: DetectorCheck<Config>): Promise<CheckResult[]>;
160
- }
161
- interface Finding {
162
- rule: string;
163
- severity: Severity;
164
- status: "new" | "preexisting";
165
- file: string;
166
- line: number;
167
- column: number;
168
- message: string;
169
- count: number;
170
- /** The detector's captures and the matched text, for a grouped rendering's per-site line. */
171
- captures?: Record<string, string>;
172
- }
173
- interface DeliveredReference {
174
- ref: string;
175
- state: "full" | "pointer" | "read" | "missing";
176
- content?: string;
177
- reason?: "mode" | "budget" | "tooLarge";
178
- /** State "read" of a reference from a rule repo: the absolute path of the file to read. */
179
- location?: string;
180
- }
181
- /** What the context budget left out. A renderer says how much is missing; it never recomputes it. */
182
- interface Omitted {
183
- /** Findings of a rule that is still delivered, and how many files they were in. */
184
- findings: {
185
18
  rule: string;
186
- count: number;
19
+ violations: number;
187
20
  files: number;
188
21
  }[];
189
- /** Rules dropped whole, with their findings: only when the floor itself did not fit. */
190
- rules: number;
191
- preexisting: number;
192
- }
193
- interface Delivery {
194
- findings: Finding[];
195
- preexistingSummary: {
196
- rule: string;
197
- file: string;
198
- count: number;
199
- }[];
200
- references: DeliveredReference[];
201
- touches: string[];
202
- stop: "block" | "allow" | "capReached" | null;
203
- warnings: string[];
204
- /** `message` template per rule with findings: the skeleton a grouped rendering prints once. */
205
- templates: Record<string, string>;
206
- omitted: Omitted;
207
- /** Absolute path of the untrimmed delivery, written when even the floor did not fit; null when it did. */
208
- overflowPath: string | null;
209
- }
210
- interface AdapterInput {
211
- /** The agent's directory; the project root is found from it. */
212
- cwd: string;
213
- /** null: nothing to run for this input. */
214
- event: Event | null;
215
- /** Start detector warm-up (§13). */
216
- warmup: boolean;
217
- }
218
- type InstallScope = "shared" | "personal";
219
- interface AdapterInstall {
220
- /** Paths whose presence means the project uses this agent (init); a trailing "/" means a directory. */
221
- markers: string[];
222
- /** Settings files, repo-relative, that hooks can be written to. */
223
- scopes: {
224
- scope: InstallScope;
22
+ /** Violations descending, then file ascending. */
23
+ files: {
225
24
  file: string;
25
+ violations: number;
226
26
  }[];
227
- /** The hook command; local: rulecast is installed in the project's node_modules. */
228
- command(local: boolean): string;
229
- merge(settings: unknown, command: string, verifyMs: number): {
230
- settings: unknown;
231
- added: string[];
232
- };
233
- remove(settings: unknown): {
234
- settings: unknown;
235
- removed: string[];
236
- };
237
- }
238
- interface Adapter {
239
- name: string;
240
- /** Shown to people: "Claude Code". */
241
- label: string;
242
- /** Budget handed to commit (§9); null = unlimited. */
243
- maxContextChars: number | null;
244
- /** Recently read or edited files the agent re-attaches to its context after compaction; reset re-delivers their touch context (§9). 0 = none. */
245
- restoredFiles: number;
246
- /** null: not an input this adapter handles. */
247
- parse(input: unknown): AdapterInput | null;
248
- format(delivery: Delivery, event: Event, options: {
249
- maxMatchesPerRule: number;
250
- }): {
251
- stdout: string;
252
- exitCode: number;
253
- };
254
- /** null: the agent has no hooks to install. */
255
- install: AdapterInstall | null;
27
+ totalViolations: number;
28
+ /** Distinct files with at least one violation — not the sum of the per-rule file counts. */
29
+ totalFiles: number;
256
30
  }
257
- declare function emptyDelivery(): Delivery;
258
-
259
- declare const claudeCodeAdapter: Adapter;
31
+ /**
32
+ * Both halves of a delivery count.
33
+ *
34
+ * `--all-files` has no baseline, so every violation arrives as a finding. `--from-ref` classifies
35
+ * against the merge base, and there most of the backlog is in `preexistingSummary` — which is
36
+ * exactly the set this exists to give a number to.
37
+ */
38
+ declare function summarise(delivery: Delivery): BacklogSummary;
39
+ declare function renderBacklog(summary: BacklogSummary, options: {
40
+ topFiles: number;
41
+ }): string;
260
42
 
261
43
  /** Every agent adapter rulecast ships. */
262
44
  declare const ADAPTERS: readonly Adapter[];
263
45
  declare function adapterByName(name: string): Adapter | undefined;
264
46
 
265
- declare const STAGES: readonly ["touch", "edit", "verify"];
266
- type Stage = (typeof STAGES)[number];
267
- /** A config entry selecting a rule from a rule repo; every key but id overrides the manifest rule's. */
268
- declare const overrideSchema: z.ZodObject<{
269
- alias: z.ZodOptional<z.ZodString>;
270
- name: z.ZodOptional<z.ZodString>;
271
- description: z.ZodOptional<z.ZodString>;
272
- files: z.ZodOptional<z.ZodString>;
273
- exclude: z.ZodOptional<z.ZodString>;
274
- types: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
275
- types_or: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
276
- exclude_types: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
277
- stages: z.ZodOptional<z.ZodArray<z.ZodEnum<["touch", "edit", "verify"]>, "atleastone">>;
278
- minimum_rulecast_version: z.ZodOptional<z.ZodString>;
279
- severity: z.ZodOptional<z.ZodEnum<["error", "warning"]>>;
280
- refuse_write: z.ZodOptional<z.ZodBoolean>;
281
- detect: z.ZodOptional<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodUnknown>, Record<string, unknown>, Record<string, unknown>>>;
282
- message: z.ZodOptional<z.ZodString>;
283
- context: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodObject<{
284
- path: z.ZodString;
285
- mode: z.ZodOptional<z.ZodEnum<["inject", "read"]>>;
286
- }, "strict", z.ZodTypeAny, {
287
- path: string;
288
- mode?: "inject" | "read" | undefined;
289
- }, {
290
- path: string;
291
- mode?: "inject" | "read" | undefined;
292
- }>]>, "many">>;
293
- id: z.ZodString;
294
- }, "strict", z.ZodTypeAny, {
295
- id: string;
296
- name?: string | undefined;
297
- message?: string | undefined;
298
- alias?: string | undefined;
299
- description?: string | undefined;
300
- files?: string | undefined;
301
- exclude?: string | undefined;
302
- types?: string[] | undefined;
303
- types_or?: string[] | undefined;
304
- exclude_types?: string[] | undefined;
305
- stages?: ["touch" | "edit" | "verify", ...("touch" | "edit" | "verify")[]] | undefined;
306
- minimum_rulecast_version?: string | undefined;
307
- severity?: "error" | "warning" | undefined;
308
- refuse_write?: boolean | undefined;
309
- detect?: Record<string, unknown> | undefined;
310
- context?: (string | {
311
- path: string;
312
- mode?: "inject" | "read" | undefined;
313
- })[] | undefined;
314
- }, {
315
- id: string;
316
- name?: string | undefined;
317
- message?: string | undefined;
318
- alias?: string | undefined;
319
- description?: string | undefined;
320
- files?: string | undefined;
321
- exclude?: string | undefined;
322
- types?: string[] | undefined;
323
- types_or?: string[] | undefined;
324
- exclude_types?: string[] | undefined;
325
- stages?: ["touch" | "edit" | "verify", ...("touch" | "edit" | "verify")[]] | undefined;
326
- minimum_rulecast_version?: string | undefined;
327
- severity?: "error" | "warning" | undefined;
328
- refuse_write?: boolean | undefined;
329
- detect?: Record<string, unknown> | undefined;
330
- context?: (string | {
331
- path: string;
332
- mode?: "inject" | "read" | undefined;
333
- })[] | undefined;
334
- }>;
335
- type RuleEntry = z.infer<typeof overrideSchema>;
336
- declare const configSchema: z.ZodEffects<z.ZodObject<{
337
- repos: z.ZodArray<z.ZodObject<{
338
- repo: z.ZodString;
339
- rev: z.ZodOptional<z.ZodString>;
340
- /** Parsed rule by rule in compile, so one bad rule does not reject the config. */
341
- rules: z.ZodArray<z.ZodUnknown, "many">;
342
- }, "strict", z.ZodTypeAny, {
343
- rules: unknown[];
344
- repo: string;
345
- rev?: string | undefined;
346
- }, {
347
- rules: unknown[];
348
- repo: string;
349
- rev?: string | undefined;
350
- }>, "many">;
351
- minimum_rulecast_version: z.ZodOptional<z.ZodString>;
352
- files: z.ZodDefault<z.ZodString>;
353
- exclude: z.ZodDefault<z.ZodString>;
354
- default_stages: z.ZodOptional<z.ZodArray<z.ZodEnum<["touch", "edit", "verify"]>, "atleastone">>;
355
- context: z.ZodDefault<z.ZodObject<{
356
- mode: z.ZodDefault<z.ZodEnum<["inject", "read"]>>;
357
- max_bytes: z.ZodDefault<z.ZodNumber>;
358
- }, "strict", z.ZodTypeAny, {
359
- mode: "inject" | "read";
360
- max_bytes: number;
361
- }, {
362
- mode?: "inject" | "read" | undefined;
363
- max_bytes?: number | undefined;
364
- }>>;
365
- max_matches_per_rule: z.ZodDefault<z.ZodNumber>;
366
- timeouts: z.ZodDefault<z.ZodObject<{
367
- edit_deadline_ms: z.ZodDefault<z.ZodNumber>;
368
- verify_ms: z.ZodDefault<z.ZodNumber>;
369
- }, "strict", z.ZodTypeAny, {
370
- edit_deadline_ms: number;
371
- verify_ms: number;
372
- }, {
373
- edit_deadline_ms?: number | undefined;
374
- verify_ms?: number | undefined;
375
- }>>;
376
- stop_gate: z.ZodDefault<z.ZodObject<{
377
- max_blocks: z.ZodDefault<z.ZodNumber>;
378
- }, "strict", z.ZodTypeAny, {
379
- max_blocks: number;
380
- }, {
381
- max_blocks?: number | undefined;
382
- }>>;
383
- refuse_gate: z.ZodDefault<z.ZodObject<{
384
- max_refusals: z.ZodDefault<z.ZodNumber>;
385
- }, "strict", z.ZodTypeAny, {
386
- max_refusals: number;
387
- }, {
388
- max_refusals?: number | undefined;
389
- }>>;
390
- llm: z.ZodDefault<z.ZodObject<{
391
- provider: z.ZodDefault<z.ZodEnum<["claude-code", "opencode", "anthropic", "openai-compatible"]>>;
392
- base_url: z.ZodDefault<z.ZodNullable<z.ZodString>>;
393
- api_key_env: z.ZodDefault<z.ZodString>;
394
- max_files_per_verify: z.ZodDefault<z.ZodNumber>;
395
- }, "strict", z.ZodTypeAny, {
396
- provider: "claude-code" | "opencode" | "anthropic" | "openai-compatible";
397
- base_url: string | null;
398
- api_key_env: string;
399
- max_files_per_verify: number;
400
- }, {
401
- provider?: "claude-code" | "opencode" | "anthropic" | "openai-compatible" | undefined;
402
- base_url?: string | null | undefined;
403
- api_key_env?: string | undefined;
404
- max_files_per_verify?: number | undefined;
405
- }>>;
406
- }, "strict", z.ZodTypeAny, {
407
- llm: {
408
- provider: "claude-code" | "opencode" | "anthropic" | "openai-compatible";
409
- base_url: string | null;
410
- api_key_env: string;
411
- max_files_per_verify: number;
412
- };
413
- repos: {
414
- rules: unknown[];
415
- repo: string;
416
- rev?: string | undefined;
417
- }[];
418
- files: string;
419
- exclude: string;
420
- context: {
421
- mode: "inject" | "read";
422
- max_bytes: number;
423
- };
424
- max_matches_per_rule: number;
425
- timeouts: {
426
- edit_deadline_ms: number;
427
- verify_ms: number;
428
- };
429
- stop_gate: {
430
- max_blocks: number;
431
- };
432
- refuse_gate: {
433
- max_refusals: number;
434
- };
435
- minimum_rulecast_version?: string | undefined;
436
- default_stages?: ["touch" | "edit" | "verify", ...("touch" | "edit" | "verify")[]] | undefined;
437
- }, {
438
- repos: {
439
- rules: unknown[];
440
- repo: string;
441
- rev?: string | undefined;
442
- }[];
443
- llm?: {
444
- provider?: "claude-code" | "opencode" | "anthropic" | "openai-compatible" | undefined;
445
- base_url?: string | null | undefined;
446
- api_key_env?: string | undefined;
447
- max_files_per_verify?: number | undefined;
448
- } | undefined;
449
- files?: string | undefined;
450
- exclude?: string | undefined;
451
- minimum_rulecast_version?: string | undefined;
452
- context?: {
453
- mode?: "inject" | "read" | undefined;
454
- max_bytes?: number | undefined;
455
- } | undefined;
456
- default_stages?: ["touch" | "edit" | "verify", ...("touch" | "edit" | "verify")[]] | undefined;
457
- max_matches_per_rule?: number | undefined;
458
- timeouts?: {
459
- edit_deadline_ms?: number | undefined;
460
- verify_ms?: number | undefined;
461
- } | undefined;
462
- stop_gate?: {
463
- max_blocks?: number | undefined;
464
- } | undefined;
465
- refuse_gate?: {
466
- max_refusals?: number | undefined;
467
- } | undefined;
468
- }>, {
469
- repos: {
470
- rules: unknown[];
471
- repo: string;
472
- rev?: string | undefined;
473
- }[];
474
- minimumRulecastVersion: string | null;
475
- files: string;
476
- exclude: string;
477
- defaultStages: ["touch" | "edit" | "verify", ...("touch" | "edit" | "verify")[]] | null;
478
- context: {
479
- mode: "inject" | "read";
480
- maxBytes: number;
481
- };
482
- maxMatchesPerRule: number;
483
- timeouts: {
484
- editDeadlineMs: number;
485
- verifyMs: number;
486
- };
487
- stopGate: {
488
- maxBlocks: number;
489
- };
490
- refuseGate: {
491
- maxRefusals: number;
492
- };
493
- llm: {
494
- provider: "claude-code" | "opencode" | "anthropic" | "openai-compatible";
495
- baseUrl: string | null;
496
- apiKeyEnv: string;
497
- maxFilesPerVerify: number;
498
- };
499
- }, {
500
- repos: {
501
- rules: unknown[];
502
- repo: string;
503
- rev?: string | undefined;
504
- }[];
505
- llm?: {
506
- provider?: "claude-code" | "opencode" | "anthropic" | "openai-compatible" | undefined;
507
- base_url?: string | null | undefined;
508
- api_key_env?: string | undefined;
509
- max_files_per_verify?: number | undefined;
510
- } | undefined;
511
- files?: string | undefined;
512
- exclude?: string | undefined;
513
- minimum_rulecast_version?: string | undefined;
514
- context?: {
515
- mode?: "inject" | "read" | undefined;
516
- max_bytes?: number | undefined;
517
- } | undefined;
518
- default_stages?: ["touch" | "edit" | "verify", ...("touch" | "edit" | "verify")[]] | undefined;
519
- max_matches_per_rule?: number | undefined;
520
- timeouts?: {
521
- edit_deadline_ms?: number | undefined;
522
- verify_ms?: number | undefined;
523
- } | undefined;
524
- stop_gate?: {
525
- max_blocks?: number | undefined;
526
- } | undefined;
527
- refuse_gate?: {
528
- max_refusals?: number | undefined;
529
- } | undefined;
530
- }>;
531
- type Config = z.output<typeof configSchema>;
532
-
533
- type AnyDetector = Detector<any>;
534
- interface DetectorRegistry {
535
- get(kind: string): AnyDetector | undefined;
536
- kinds(): string[];
537
- }
538
- declare function createRegistry(detectors: AnyDetector[]): DetectorRegistry;
539
-
540
- type Checkout = {
541
- ok: true;
542
- dir: string;
543
- label: string;
544
- } | {
545
- ok: false;
546
- message: string;
547
- missing: boolean;
548
- };
549
- /** Where compile finds a pinned rule repo's files. */
550
- interface RepoProvider {
551
- checkout(url: string, rev: string): Promise<Checkout>;
552
- }
553
- /** For hooks, which never fetch (spec §4): a repo missing from the cache is reported, not fetched. */
554
- declare function cachedRepos(home: string): RepoProvider;
555
- /** For the CLI: fetches repos missing from the cache. */
556
- declare function fetchingRepos(home: string): RepoProvider;
557
- /** For try-repo: every repo is `dir`. */
558
- declare function fixedRepo(dir: string, label: string): RepoProvider;
559
-
560
- interface ReferenceSpec {
561
- /** Identity and display. Project: "docs/api.md#errors". Rule repo: "syv-ai/rulecast@v0.2.0:docs/api.md#errors". */
562
- ref: string;
563
- /** Project references: repo-relative with forward slashes. Rule repo references: absolute path in the cache. */
564
- path: string;
565
- anchor: string | null;
566
- mode: ReferenceMode;
567
- }
568
-
569
- interface CompiledDetector {
570
- kind: string;
571
- config: unknown;
572
- captures: string[];
573
- }
574
- interface CompiledRule {
575
- /** alias when set, else id: the name findings, dedupe and session state use. */
576
- id: string;
577
- name: string;
578
- description: string | null;
579
- /** "local", or the rule repo label ("syv-ai/rulecast@v0.2.0"). */
580
- source: string;
581
- severity: Severity;
582
- /** Refuse the agent's write when this rule fires on what it is writing (spec §9, Guard). */
583
- refuseWrite: boolean;
584
- stages: Stage[];
585
- matches(file: string): boolean;
586
- detector: CompiledDetector | null;
587
- message: string | null;
588
- context: ReferenceSpec[];
589
- }
590
-
591
- interface Diagnostic {
592
- /** ".rulecast-config.yaml", ".rulecast-rules.yaml", or "<repo url>@<rev>" for a rule repo. */
593
- source: string;
594
- rule: string | null;
595
- message: string;
596
- level: "error" | "warning";
597
- /** What a hook tells the developer to run; default "rulecast validate". Missing repos: "rulecast install". */
598
- hint?: string;
599
- }
600
- interface CompiledProject {
601
- root: string;
602
- config: Config;
603
- rules: CompiledRule[];
604
- diagnostics: Diagnostic[];
605
- }
606
- interface CompileOptions {
607
- root: string;
608
- registry: DetectorRegistry;
609
- repos: RepoProvider;
610
- /** Config data to compile instead of the project's .rulecast-config.yaml (try-repo). */
611
- configData?: unknown;
612
- }
613
- /** The project config and the manifests of its pinned repos → ready rules plus diagnostics (spec §5). */
614
- declare function compile(options: CompileOptions): Promise<CompiledProject>;
615
- /** A manifest on its own (validate, the init catalog): references resolve against `dir`. */
616
- declare function compileManifest(dir: string, registry: DetectorRegistry): Promise<{
617
- rules: CompiledRule[];
618
- diagnostics: Diagnostic[];
619
- }>;
620
-
621
- declare const CONFIG_FILE = ".rulecast-config.yaml";
622
- declare const MANIFEST_FILE = ".rulecast-rules.yaml";
623
-
624
- interface RenderOptions {
625
- maxMatchesPerRule: number;
626
- }
627
- declare function renderAgentText(delivery: Delivery, options: RenderOptions): string;
628
-
629
- interface CheckOptions {
630
- project: CompiledProject;
631
- registry: DetectorRegistry;
632
- /** Project settings, as a detector run would get them. */
633
- settings: DetectorSettings;
634
- env: Readonly<Record<string, string | undefined>>;
635
- timeoutMs: number;
636
- }
637
- /** A check's answer, and which detector gave it. */
638
- type KindCheckResult = CheckResult & {
639
- kind: string;
640
- };
641
- /** Detector kinds used by the project's rules whose detector has environment checks. */
642
- declare function checkableKinds(project: CompiledProject, registry: DetectorRegistry): string[];
643
- /**
644
- * Every used detector's own checks (spec §5). Kinds run in parallel; results come back grouped by
645
- * kind in checkableKinds order, so doctor's output is stable between runs.
646
- */
647
- declare function checkDetectors(options: CheckOptions): Promise<KindCheckResult[]>;
648
-
649
47
  /** Turns a per-rule function into a detector run; an error in one rule does not affect the others. */
650
48
  declare function perRule<Config>(detect: (rule: DetectorRuleInput<Config>, input: DetectorRun<Config>) => Promise<Match[]>): Detector<Config>["run"];
651
49
 
652
- type Env = Readonly<Record<string, string | undefined>>;
653
- /** $RULECAST_HOME, else $XDG_CACHE_HOME/rulecast, else ~/.cache/rulecast. Empty values count as unset. */
654
- declare function cacheHome(env: Env): string;
655
- /**
656
- * <home>/projects/<first 16 hex of sha256(realpath(root))>. Keyed by the real path, so separate checkouts and
657
- * worktrees of one repository get separate directories and a symlinked path shares its target's.
658
- */
659
- declare function projectStateDir(home: string, root: string): string;
660
-
661
- interface PipelineOptions {
662
- /** Compiled by the caller: hooks never fetch rule repos, the CLI does (spec §4). */
663
- project: CompiledProject;
664
- /** The project's directory in the cache (spec §12): sessions and detector caches. */
665
- stateDir: string;
666
- event: Event;
667
- registry: DetectorRegistry;
668
- /** From the adapter; null = unlimited. */
669
- maxContextChars: number | null;
670
- /** Recently accessed files the agent re-attaches after compaction (adapter.restoredFiles); reset re-delivers their touch context. */
671
- restoredFiles?: number;
672
- /** Detector kinds to skip entirely (run --no-llm). */
673
- skipDetectorKinds?: ReadonlySet<string>;
674
- /** Run only these rules (rulecast run RULE_ID); touch rules are unaffected. */
675
- onlyRules?: ReadonlySet<string>;
676
- /** verify from an agent's stop: decide block / allow / capReached. Needs a session. */
677
- stopGate?: boolean;
678
- /** Debug log sink. */
679
- log?: (line: string) => void;
680
- }
681
- interface PipelineResult {
682
- delivery: Delivery;
683
- /** rulecast itself failed: compile diagnostics, detector errors or verify timeouts. */
684
- failed: boolean;
685
- /** Detector kinds whose results were dropped at the edit deadline (§13). */
686
- deadlineMissed: string[];
687
- }
688
- declare function runPipeline(options: PipelineOptions): Promise<PipelineResult>;
689
-
690
50
  /** The running rulecast version. test/core/version.test.ts keeps it equal to package.json. */
691
- declare const VERSION = "0.1.1";
51
+ declare const VERSION = "0.3.0";
692
52
 
693
53
  declare const builtinDetectors: readonly AnyDetector[];
694
54
 
695
- declare const MODEL_ALIASES: readonly string[];
696
- /**
697
- * The provider's own name for a model. A name that is not an alias is passed through unchanged, so
698
- * a new model or a local Ollama tag needs no rulecast release. null: a known alias this provider
699
- * cannot express, which the detector turns into a per-rule error naming both.
700
- */
701
- declare function resolveModel(model: string, provider: LlmProviderName): string | null;
702
-
703
- /** What the model is asked to answer with (spec §6). */
704
- declare const responseSchema: z.ZodObject<{
705
- findings: z.ZodArray<z.ZodObject<{
706
- rule: z.ZodString;
707
- line: z.ZodNumber;
708
- /** The model's quote of the line. Asked for because quoting sharpens the line number; never rendered. */
709
- text: z.ZodOptional<z.ZodString>;
710
- reason: z.ZodString;
711
- }, "strip", z.ZodTypeAny, {
712
- rule: string;
713
- line: number;
714
- reason: string;
715
- text?: string | undefined;
716
- }, {
717
- rule: string;
718
- line: number;
719
- reason: string;
720
- text?: string | undefined;
721
- }>, "many">;
722
- }, "strip", z.ZodTypeAny, {
723
- findings: {
724
- rule: string;
725
- line: number;
726
- reason: string;
727
- text?: string | undefined;
728
- }[];
729
- }, {
730
- findings: {
731
- rule: string;
732
- line: number;
733
- reason: string;
734
- text?: string | undefined;
735
- }[];
736
- }>;
737
- type LlmFinding = z.infer<typeof responseSchema>["findings"][number];
738
- interface LlmRequest {
739
- /** The provider's own model name, already resolved from the rule's alias. */
740
- model: string;
741
- /** The whole prompt: rules, grounding and the marked-up file. */
742
- prompt: string;
743
- settings: LlmSettings;
744
- env: NodeJS.ProcessEnv;
745
- cwd: string;
746
- signal: AbortSignal;
747
- }
748
- /**
749
- * The backend itself is unusable — no binary, no credentials. Spec §14 makes this a failure of the
750
- * whole run (every llm rule disabled, one warning), not of one rule. Every other throw is §14's
751
- * "malformed LLM output": an error for the rules in that call only.
752
- */
753
- declare class LlmUnavailableError extends Error {
754
- }
755
- /** What `rulecast doctor` learns about a backend without calling it (spec §5). */
756
- interface LlmAvailability {
757
- ok: boolean;
758
- /** The resolved binary or the endpoint when ok; why not when it is not. */
759
- detail: string;
760
- }
761
- interface LlmProvider {
762
- name: string;
763
- ask(request: LlmRequest): Promise<LlmFinding[]>;
764
- /**
765
- * Can this backend be reached at all — the binary exists, or the key is set. Environmental, so
766
- * it never calls the model: a false answer here is the same situation ask() would report as
767
- * LlmUnavailableError, found before anything is spent.
768
- */
769
- available(input: {
770
- settings: LlmSettings;
771
- env: NodeJS.ProcessEnv;
772
- cwd: string;
773
- /** Give up when doctor's deadline passes: a PATH lookup can block on a stalled mount. */
774
- signal: AbortSignal;
775
- }): Promise<LlmAvailability>;
776
- }
777
-
778
- /**
779
- * The provider for a configured name. Throws LlmUnavailableError, which spec §14 turns into one
780
- * warning disabling every llm rule — the guard survives because a config can name a provider a
781
- * future build has dropped.
782
- */
783
- declare function providerByName(name: LlmProviderName): LlmProvider;
784
-
785
55
  /** One contract check. `run` throws (node:assert) when the check fails. */
786
56
  interface ContractCase {
787
57
  name: string;
@@ -837,4 +107,4 @@ interface DetectorFixture {
837
107
  */
838
108
  declare function detectorContract(detector: AnyDetector, fixture: DetectorFixture): ContractCase[];
839
109
 
840
- export { ADAPTERS, type Adapter, type AdapterFixture, type AdapterInput, type AdapterInstall, type AnyDetector, CONFIG_FILE, type Cache, type ChangeSet, type CheckResult, type Checkout, type CompileOptions, type CompiledDetector, type CompiledProject, type CompiledRule, type Config, type ContractCase, type DeliveredReference, type Delivery, type Detector, type DetectorCheck, type DetectorEvent, type DetectorFixture, type DetectorRegistry, type DetectorResult, type DetectorRuleInput, type DetectorRun, type DetectorSettings, type DetectorWarm, type Diagnostic, type Env, type Event, type EventKind, type Finding, type InstallScope, type KindCheckResult, LLM_PROVIDERS, type LlmFinding, type LlmProvider, type LlmProviderName, type LlmRequest, type LlmSettings, LlmUnavailableError, MANIFEST_FILE, MODEL_ALIASES, type Match, type Omitted, type PipelineOptions, type PipelineResult, type ReferenceMode, type RepoProvider, type ResolvedReference, type RuleEntry, type Severity, type Stage, VERSION, type WriteIntent, adapterByName, adapterContract, builtinDetectors, cacheHome, cachedRepos, checkDetectors, checkableKinds, claudeCodeAdapter, compile, compileManifest, createRegistry, defaultDetectorSettings, detectorContract, emptyDelivery, fetchingRepos, fixedRepo, perRule, projectStateDir, providerByName, renderAgentText, resolveModel, runPipeline };
110
+ export { ADAPTERS, Adapter, type AdapterFixture, AdapterInput, AnyDetector, type BacklogSummary, type ContractCase, Delivery, Detector, type DetectorFixture, DetectorRuleInput, DetectorRun, Event, Match, VERSION, adapterByName, adapterContract, builtinDetectors, claudeCodeAdapter, detectorContract, perRule, renderBacklog, summarise };