rulebookguard 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -144,15 +144,19 @@ Scenario: Duplicate document in the same country
144
144
  ```
145
145
  ````
146
146
 
147
- | Field | Required | Meaning |
148
- | ------------ | -------- | ---------------------------------------------------------------- |
149
- | `id` | yes | `R-<PREFIX>-<NNN>`, three digits, unique, equal to the file name |
150
- | `context` | yes | the context directory the rule lives in |
151
- | `entities` | yes | domain entities the rule talks about |
152
- | `status` | yes | `proposed`, `active` or `deprecated` |
153
- | `supersedes` | no | id of the rule this one replaces |
154
- | `anchors` | yes | repo-relative globs of the code that implements the rule |
155
- | `summary` | yes | one line; this is what `list` shows |
147
+ | Field | Required | Meaning |
148
+ | --------------- | -------- | ------------------------------------------------------------------------------ |
149
+ | `id` | yes | `R-<PREFIX>-<NNN>`, three digits, unique, equal to the file name |
150
+ | `context` | yes | the context directory the rule lives in |
151
+ | `entities` | yes | domain entities the rule talks about |
152
+ | `status` | yes | `proposed`, `active` or `deprecated` |
153
+ | `supersedes` | no | id of the rule this one replaces |
154
+ | `dependsOn` | no | ids of the rules this one needs in force; no cycles |
155
+ | `conflictsWith` | no | ids of the rules that cannot be active together with this one |
156
+ | `origin` | no | `authored` (written from intent) or `extracted` (lifted from code) |
157
+ | `evidence` | no | where the rule shows in code or tests, for example `src/x.ts validateDocument` |
158
+ | `anchors` | yes | repo-relative globs of the code that implements the rule |
159
+ | `summary` | yes | one line; this is what `list` shows |
156
160
 
157
161
  The Markdown body holds the full rule and its examples (Gherkin scenarios or tables). Rules do not list their tests: the link lives only in the tests, through `@rule` tags.
158
162
 
@@ -163,7 +167,7 @@ A test links to a rule with a region: `@rule <ID>` opens it and `@endrule <ID>`
163
167
  > [!IMPORTANT]
164
168
  > Every `@rule` needs its `@endrule`, otherwise `lint` reports `unclosed-rule-tag`; an `@endrule` with no open `@rule` of the same id is reported as `unmatched-endrule-tag`. The region tells `lint --base` which lines belong to the rule: a changed rule needs a change inside one of its regions, not anywhere in the test file.
165
169
 
166
- A comment is the usual place, but the markers are matched as text, so they also count inside a test name or a string. To mention a rule in a test file without linking to it, write the id without the `@rule` prefix.
170
+ Markers count only on a line that starts with a comment: `//`, `/*`, `*`, `#`, `--`, `;`, `<!--` or `%`, after any indentation. A marker in a test name, a string or a comment after code on the same line is not a link, so test files may quote `@rule` freely. Inside a `/* … */` block, start each marker line with `*`, as in the Java example below; a bare inner line is not taken as a comment.
167
171
 
168
172
  TypeScript or JavaScript:
169
173
 
@@ -209,26 +213,45 @@ SELECT count(*) FROM invoices WHERE number IS NULL;
209
213
  -- @endrule R-BIL-002
210
214
  ```
211
215
 
216
+ ### Marking the code that enforces a rule with `@implements` … `@endimplements`
217
+
218
+ Anchors say which files a rule governs; an `@implements <ID>` … `@endimplements <ID>` region says which lines enforce it, so the link moves with the code instead of pointing at a line number. Regions are optional and follow the same comment rule, nesting and pairing as `@rule` regions. Only files matched by some rule's `anchors` are scanned, and a region must sit in a file its own rule anchors.
219
+
220
+ ```go
221
+ // @implements R-CLI-001
222
+ func validateDocument(country, number string) error {
223
+ // ...
224
+ }
225
+ // @endimplements R-CLI-001
226
+ ```
227
+
228
+ `lint` reports `unclosed-implements-tag`, `unmatched-endimplements-tag`, `unknown-implements-tag`, `deprecated-implements-tag` and `implements-outside-anchors` (see the table under `lint`).
229
+
212
230
  ## Commands
213
231
 
214
232
  `rulebookguard --help` prints the command list. Exit codes: `0` success, `1` failure (unknown id or context, missing config, lint errors, git errors), `2` usage error (unknown command or option, missing or extra argument).
215
233
 
216
- | Command | What it does |
217
- | ----------------------------------------------------------------- | ------------------------------------------------------------ |
218
- | [`init <context>`](#init-context---prefix-p---language-enes---ci) | sets up the config, a first context and optionally CI |
219
- | [`contexts`](#contexts-l0) | the contexts map (L0) |
220
- | [`list`](#list---context-c---status-s-l1) | rule ids and summaries (L1) |
221
- | [`show <id>`](#show-id-l2) | the full rule (L2) |
222
- | [`for <path>`](#for-path) | the active rules anchored to a file |
223
- | [`impact <id>`](#impact-id) | the files and tests a rule touches |
224
- | [`new <context>`](#new-context---prefix-p) | scaffolds the next rule of a context |
225
- | [`lint`](#lint---base-ref---json) | checks the whole catalog, and optionally changes since a ref |
226
- | [`hook`](#hook) | Claude Code hook that injects the rules of a file |
227
- | [`mcp`](#mcp---project-dir) | MCP server for AI agents |
228
- | [`site:generate`](#sitegenerate---out-dir---base-path) | builds a static site of the rulebook |
229
- | [`site:run`](#siterun---out-dir---base-path---port-n) | builds the site and serves it locally |
230
-
231
- The read commands (`contexts`, `list`, `show`, `for`, `impact`) accept `--json` for machine consumers. When the catalog has problems they still run, and print one warning on stderr pointing at `lint`.
234
+ | Command | What it does |
235
+ | --------------------------------------------------------------------- | ------------------------------------------------------------ |
236
+ | [`init <context>`](#init-context---prefix-p---language-enes---ci) | sets up the config, a first context and optionally CI |
237
+ | [`contexts`](#contexts-l0) | the contexts map (L0) |
238
+ | [`list`](#list---context-c---status-s-l1) | rule ids and summaries (L1) |
239
+ | [`show <id>`](#show-id-l2) | the full rule (L2) |
240
+ | [`search <text>`](#search-text---context-c---json) | rules holding every word of a text |
241
+ | [`for <path>`](#for-path) | the active rules anchored to a file |
242
+ | [`for --changed <ref>`](#for---changed-ref) | the active rules anchored to the files changed since a ref |
243
+ | [`impact <id>`](#impact-id) | the files and tests a rule touches |
244
+ | [`new <context>`](#new-context---prefix-p) | scaffolds the next rule of a context |
245
+ | [`lint`](#lint---base-ref---json) | checks the whole catalog, and optionally changes since a ref |
246
+ | [`export gherkin`](#export-gherkin---out-dir) | writes the rules' scenarios as Cucumber `.feature` files |
247
+ | [`export cursor\|copilot\|kiro`](#export-cursorcopilotkiro---out-dir) | writes the active rules as rule files of other AI tools |
248
+ | [`export agents-md`](#export-agents-md---out-dir) | keeps an index of the active rules in `AGENTS.md` |
249
+ | [`hook`](#hook) | Claude Code hook that injects the rules of a file |
250
+ | [`mcp`](#mcp---project-dir) | MCP server for AI agents |
251
+ | [`site:generate`](#sitegenerate---out-dir---base-path) | builds a static site of the rulebook |
252
+ | [`site:run`](#siterun---out-dir---base-path---port-n) | builds the site and serves it locally |
253
+
254
+ The read commands (`contexts`, `list`, `show`, `search`, `for`, `impact`) accept `--json` for machine consumers. When the catalog has problems they still run, and print one warning on stderr pointing at `lint`.
232
255
 
233
256
  ### `init <context> [--prefix <P>] [--language <en|es>] [--ci]`
234
257
 
@@ -280,7 +303,18 @@ file: docs/rules/clients/R-CLI-001.md
280
303
  ...
281
304
  ```
282
305
 
283
- Exits 1 for an unknown id.
306
+ `supersedes`, `dependsOn`, `conflictsWith`, `origin` and `evidence` lines appear only when the rule sets them; `--json` always has `supersedes` and `origin` (or `null`), and `dependsOn`, `conflictsWith` and `evidence` (or `[]`). An `implementations:` section lists the [`@implements` regions](#marking-the-code-that-enforces-a-rule-with-implements--endimplements) of the rule as `file:line-endLine`, read from the files its anchors match; `--json` has them as `implementations: [{ file, line, endLine }]` (or `[]`). Exits 1 for an unknown id.
307
+
308
+ ### `search <text> [--context <c>] [--json]`
309
+
310
+ Rules in any status whose id, summary, entities or text hold every word of `<text>`, ignoring case. Plain substring search, no index or embeddings: enough to find an existing rule before writing a new one. `--context` narrows it to one context. Rows and `--json` (`{ "rules": [...] }`) look like `list`; no match prints `No rules found.`
311
+
312
+ ```sh
313
+ $ rulebookguard search "document number"
314
+ R-CLI-001 [active] A client document number is unique per country.
315
+ ```
316
+
317
+ Exits 2 for an empty text and 1 for an unknown context.
284
318
 
285
319
  ### `for <path>`
286
320
 
@@ -288,10 +322,27 @@ Active rules whose anchors match a path, relative to the current directory. This
288
322
 
289
323
  ```sh
290
324
  $ rulebookguard for src/clients/create.ts
325
+ R-CLI-001 (clients): A client document number is unique per country. (lines 12-30)
326
+ ```
327
+
328
+ When the file holds `@implements` regions of a rule, its line ends with their line ranges; `--json` rows carry them as `regions: [{ line, endLine }]` (or `[]`). Exits 1 when the path is outside the rulebook root.
329
+
330
+ ### `for --changed <ref>`
331
+
332
+ Active rules anchored to the files changed since a git ref, each with those files. This is what a reviewer needs to know about a pull request: which rules it touches, even when no rule file changed.
333
+
334
+ ```sh
335
+ $ rulebookguard for --changed origin/main
336
+ R-BIL-001 (billing): Every new client gets a zero-balance account.
337
+ src/clients/create.ts
291
338
  R-CLI-001 (clients): A client document number is unique per country.
339
+ src/clients/create.ts
340
+ src/clients/update.ts
292
341
  ```
293
342
 
294
- Exits 1 when the path is outside the rulebook root.
343
+ It looks at the difference between the ref and the working tree, including deleted files and untracked ones that are not ignored, as `lint --base` does. A renamed file counts under both paths, so a file moved away from its anchors still reports their rules. On a branch that is behind the ref, the files changed on the ref since the branch point count too, so in CI compare against the base of the pull request on the merge commit, which is what a pull request checkout gives you.
344
+
345
+ `--json` prints `{ base, files, rules }`, where `files` lists every changed file and each rule carries its own `files`. Prints `No active rules anchored to the files changed since <ref>.` when there are none. Exits 1 for an unknown ref or outside a git work tree, and 2 when given both a path and `--changed`.
295
346
 
296
347
  ### `impact <id>`
297
348
 
@@ -332,23 +383,31 @@ Exits 1, writing nothing, when the context has no free id left, when the name is
332
383
 
333
384
  Checks the whole catalog. Each finding is printed as `<severity> <code> <file>[:line] <message>`, with a `hint:` line under it that says how to fix it, followed by a count.
334
385
 
335
- | Severity | Code | Meaning |
336
- | -------- | ------------------------ | ------------------------------------------------------------------ |
337
- | error | `untested-rule` | an active rule has no test tagging it |
338
- | error | `unknown-rule-tag` | a test tags a rule id that does not exist |
339
- | error | `deprecated-rule-tag` | a test tags a deprecated rule |
340
- | error | `unclosed-rule-tag` | a `@rule` has no matching `@endrule` |
341
- | error | `unmatched-endrule-tag` | an `@endrule` has no open `@rule` of the same id |
342
- | error | `unmatched-anchor` | an anchor glob matches no file |
343
- | error | `schema` | a config, context or rule file breaks the JSON Schema |
344
- | error | `duplicate-id` | two rule files share an id |
345
- | error | `file-name-mismatch` | a rule file is not named `<id>.md` |
346
- | error | `id-prefix-mismatch` | a rule id does not use its context prefix |
347
- | error | `context-mismatch` | a `context` field does not match its directory |
348
- | error | `missing-context-file` | a context directory has no `_context.md` |
349
- | error | `missing-rules-dir` | `rulesDir` does not exist |
350
- | error | `pending-implementation` | with `--base`: an active rule changed, but none of its regions did |
351
- | warning | `context-over-budget` | a context holds more rules than `contextBudget` |
386
+ | Severity | Code | Meaning |
387
+ | -------- | ----------------------------- | ------------------------------------------------------------------ |
388
+ | error | `untested-rule` | an active rule has no test tagging it |
389
+ | error | `unknown-rule-tag` | a test tags a rule id that does not exist |
390
+ | error | `deprecated-rule-tag` | a test tags a deprecated rule |
391
+ | error | `unclosed-rule-tag` | a `@rule` has no matching `@endrule` |
392
+ | error | `unmatched-endrule-tag` | an `@endrule` has no open `@rule` of the same id |
393
+ | error | `unclosed-implements-tag` | an `@implements` has no matching `@endimplements` |
394
+ | error | `unmatched-endimplements-tag` | an `@endimplements` has no open `@implements` of the same id |
395
+ | error | `unknown-implements-tag` | code is marked as implementing a rule id that does not exist |
396
+ | error | `deprecated-implements-tag` | code is marked as implementing a deprecated rule |
397
+ | error | `implements-outside-anchors` | an `@implements` region sits in a file its rule does not anchor |
398
+ | error | `unmatched-anchor` | an anchor glob matches no file |
399
+ | error | `schema` | a config, context or rule file breaks the JSON Schema |
400
+ | error | `duplicate-id` | two rule files share an id |
401
+ | error | `file-name-mismatch` | a rule file is not named `<id>.md` |
402
+ | error | `id-prefix-mismatch` | a rule id does not use its context prefix |
403
+ | error | `context-mismatch` | a `context` field does not match its directory |
404
+ | error | `missing-context-file` | a context directory has no `_context.md` |
405
+ | error | `missing-rules-dir` | `rulesDir` does not exist |
406
+ | error | `pending-implementation` | with `--base`: an active rule changed, but none of its regions did |
407
+ | error | `invalid-rule-reference` | `dependsOn` or `conflictsWith` names an unknown rule or itself |
408
+ | error | `dependency-cycle` | `dependsOn` links form a cycle (reported once, at its lowest id) |
409
+ | error | `active-rule-conflict` | two active rules where one lists the other in `conflictsWith` |
410
+ | warning | `context-over-budget` | a context holds more rules than `contextBudget` |
352
411
 
353
412
  ```sh
354
413
  $ rulebookguard lint
@@ -372,6 +431,59 @@ error pending-implementation docs/rules/clients/R-CLI-001.md rule R-CLI-001 chan
372
431
 
373
432
  `--json` prints `{ "findings": [{ "severity", "code", "file", "line"?, "message", "hint" }], "errors", "warnings" }`.
374
433
 
434
+ ### `export gherkin [--out <dir>]`
435
+
436
+ Writes the ` ```gherkin ` blocks of every rule that is not deprecated as `<out>/<context>/<id>.feature`, relative to the current directory (`--out` defaults to `features/rules`). Each file is tagged with the rule id, so Cucumber runs or filters a rule's scenarios with `--tags @R-CLI-001`. Blocks hold scenarios; their `# language:` lines are dropped, and with `language: es` the file starts with `# language: es` and uses `Característica:`.
437
+
438
+ ```sh
439
+ $ rulebookguard export gherkin
440
+ features/rules/clients/R-CLI-001.feature
441
+ $ cat features/rules/clients/R-CLI-001.feature
442
+ # Generated by "rulebookguard export gherkin" from docs/rules/clients/R-CLI-001.md; edit the rule instead.
443
+ @R-CLI-001
444
+ Feature: R-CLI-001 A client document number is unique per country.
445
+
446
+ Scenario: Duplicate document in the same country
447
+ Given a client with document "00123" in "VE"
448
+ When another client is created with document "123" in "VE"
449
+ Then the creation is rejected with "document already registered"
450
+ ```
451
+
452
+ Generated files are overwritten; nothing else in the output directory is touched, so a feature of a rule that was deprecated or lost its scenarios stays until you delete it. Prints `No rule has Gherkin scenarios.` when there is nothing to write. The tags are Cucumber tags, not `@rule` regions: link the step definitions to the rule with `@rule` … `@endrule` as with any test.
453
+
454
+ ### `export cursor|copilot|kiro [--out <dir>]`
455
+
456
+ Writes every active rule with anchors as one file that the tool attaches when it works on a matching path, so agents other than Claude Code get the same rules. The files go under the rulebook root by default, or directly in `--out`, relative to the current directory:
457
+
458
+ | Format | File | Scoped with |
459
+ | --------- | --------------------------------------------------------- | --------------------------------------------------- |
460
+ | `cursor` | `.cursor/rules/rulebookguard-<id>.mdc` | `globs`, comma-separated, with `alwaysApply: false` |
461
+ | `copilot` | `.github/instructions/rulebookguard-<id>.instructions.md` | `applyTo`, comma-separated |
462
+ | `kiro` | `.kiro/steering/rulebookguard-<id>.md` | `inclusion: fileMatch` and `fileMatchPattern` |
463
+
464
+ ```sh
465
+ $ rulebookguard export kiro
466
+ .kiro/steering/rulebookguard-R-CLI-001.md
467
+ $ cat .kiro/steering/rulebookguard-R-CLI-001.md
468
+ ---
469
+ inclusion: fileMatch
470
+ fileMatchPattern: "src/clients/**/*.go"
471
+ ---
472
+
473
+ <!-- Generated by "rulebookguard export kiro" from docs/rules/clients/R-CLI-001.md; edit the rule instead. -->
474
+
475
+ **R-CLI-001:** A client document number is unique per country.
476
+
477
+ # Unique document number
478
+ …
479
+ ```
480
+
481
+ Run it again after changing rules, for example in CI or before committing: it overwrites the generated files and removes, printing `removed <path>`, those of rules that are no longer active. Only a file named after a rule id, with the generated line of that format right after its frontmatter, is removed. If the file of an active rule lacks that line, it was not generated, so the command fails with exit code 1 before writing or removing anything. An active rule without anchors has no path to attach to, so it is skipped with a warning on stderr. Prints `No active rule to export.` when there is nothing to write, or `No active rule with anchors to export.` when no active rule has anchors. Brace anchors are expanded into one pattern per option; an anchor with a literal comma in a file name cannot be written in the comma-separated formats.
482
+
483
+ ### `export agents-md [--out <dir>]`
484
+
485
+ Keeps an index of the active rules in `AGENTS.md`, at the rulebook root or in `--out`. `AGENTS.md` has no path scoping and is always read, so it holds each rule's id, anchors (or `no anchors`), summary and rule file, not the full text. The index lives between `<!-- rulebookguard:start -->` and `<!-- rulebookguard:end -->`: the rest of the file is kept, the block is appended the first time and replaced after that, and it is removed when no rule is active, along with the file if nothing else is left (printing `removed AGENTS.md`). Anything other than one start marker followed by one end marker, or neither, fails with exit code 1 and leaves the file untouched.
486
+
375
487
  ### `hook`
376
488
 
377
489
  A Claude Code hook for `PreToolUse` and `PostToolUse` events of tools that take a `file_path` (the [Claude Code plugin](#claude-code-plugin) wires it up). It reads the hook event as JSON on stdin, takes `tool_input.file_path` (relative paths resolve against the event's `cwd`), finds `rulebookguard.config.yaml` by walking up from that file, and prints the active rules anchored to it as additional context for Claude, under the same `hookEventName`:
@@ -383,21 +495,25 @@ $ echo '{"hook_event_name":"PostToolUse","tool_name":"Read","tool_input":{"file_
383
495
 
384
496
  Claude Code adds the context next to the tool result, so a `PostToolUse` hook on `Read` delivers the rules together with the file contents, before Claude writes a change (Claude Code requires reading a file before editing it).
385
497
 
386
- Each rule comes with its id, context, summary, rule file and full text; when the texts add up to more than 10,000 characters only the summaries are listed, with a pointer to `rulebookguard show <id>`. The header follows `language` in the config.
498
+ Each rule comes with its id, context, summary, rule file, the lines of its `@implements` regions in that file (when there are any) and full text; when the texts add up to more than 10,000 characters only the summaries are listed, with a pointer to `rulebookguard show <id>`. The header follows `language` in the config.
387
499
 
388
500
  > [!NOTE]
389
501
  > The hook never blocks a tool and never answers the permission prompt: with no config, a path outside the rulebook root, no active rule, another event, unreadable input or any error, it prints nothing and exits 0.
390
502
 
391
503
  ### `mcp [--project <dir>]`
392
504
 
393
- An [MCP](https://modelcontextprotocol.io) server over stdio for AI agents, with two read-only tools:
505
+ An [MCP](https://modelcontextprotocol.io) server over stdio for AI agents, with read-only tools:
394
506
 
395
- | Tool | Input | Answers |
396
- | ------------ | ------------------------------------------------------ | --------------------------------------------- |
397
- | `rules_for` | `path`: absolute, or relative to the project directory | what `rulebookguard for <path> --json` prints |
398
- | `rules_show` | `id`: a rule id | what `rulebookguard show <id> --json` prints |
507
+ | Tool | Input | Answers |
508
+ | -------------- | ------------------------------------------------------ | ------------------------------------------------------------- |
509
+ | `rules_for` | `path`: absolute, or relative to the project directory | what `rulebookguard for <path> --json` prints |
510
+ | `rules_show` | `id`: a rule id | what `rulebookguard show <id> --json` prints |
511
+ | `contexts` | none | what `rulebookguard contexts --json` prints |
512
+ | `rules_list` | `context`?, `status`? | what `rulebookguard list --json` prints with those options |
513
+ | `rules_search` | `text`, `context`? | what `rulebookguard search <text> --json` prints |
514
+ | `lint` | `base`?: a git ref | what `rulebookguard lint --json` prints, with `--base` if set |
399
515
 
400
- The rulebook is found from `--project` (default: the current directory). A command that would fail answers a tool error carrying the same message the CLI prints, for example `Unknown rule id "R-NOPE-001". Run "rulebookguard list" to list them.`
516
+ The rulebook is found from `--project` (default: the current directory). A command that would fail answers a tool error carrying the same message the CLI prints, for example `Unknown rule id "R-NOPE-001". Run "rulebookguard list" to list them.` Lint findings are data, not a failure: `lint` answers its JSON report even when it has errors, and fails only when lint cannot run.
401
517
 
402
518
  ### `site:generate [--out <dir>] [--base <path>]`
403
519
 
@@ -434,7 +550,7 @@ The site has:
434
550
  - **Home:** the contexts map, with how many rules of each status every context holds.
435
551
  - **A page per context** with its description and rules.
436
552
  - **All rules:** one table filterable by text, context and status.
437
- - **A page per rule:** summary, status, entities, rule file, the supersedes chain both ways, the rule text, its anchors and the files they match, the tests tagging it, and the git history of the rule file.
553
+ - **A page per rule:** summary, status, entities, rule file, the supersedes chain both ways, the origin (an `extracted` rule still `proposed` is flagged as not validated by the business yet) and evidence, the rule text, its anchors and the files they match, the tests tagging it, and the git history of the rule file.
438
554
  - **Health:** the `lint` findings when the site was built.
439
555
  - Full-text search (Pagefind).
440
556
 
@@ -461,7 +577,7 @@ jobs:
461
577
  - uses: actions/setup-node@v6
462
578
  with:
463
579
  node-version: 22
464
- - run: npx --yes rulebookguard@0.8.0 lint --base origin/${{ github.base_ref }}
580
+ - run: npx --yes rulebookguard@0.10.0 lint --base origin/${{ github.base_ref }}
465
581
  ```
466
582
 
467
583
  > [!TIP]
@@ -472,8 +588,11 @@ jobs:
472
588
  The plugin in [`plugin/`](plugin) connects the rulebook to Claude Code:
473
589
 
474
590
  - **Hooks:** after every `Read` and before every `Write`, it runs `rulebookguard hook`, so Claude gets the active rules anchored to a file together with its contents, before it writes a change. A `Write` of a new file, which needs no `Read`, gets them right after the file is created.
475
- - **MCP server:** the `rules_for` and `rules_show` tools from `rulebookguard mcp`, for rules of files Claude has not opened yet.
476
- - **Skill** `rulebookguard:rules`: the protocol, that is, follow the rules, and change a rule (then its tests, then the code) only when the user asks for the behavior change.
591
+ - **MCP server:** the tools of `rulebookguard mcp` (`rules_for`, `rules_show`, `contexts`, `rules_list`, `rules_search`, `lint`), for rules of files Claude has not opened yet and for working on the rulebook itself.
592
+ - **Skill** `rulebookguard:rules`: the protocol, that is, follow the rules, and change a rule (then its tests, then the code, marked with `@implements`) only when the user asks for the behavior change.
593
+ - **Skill** `rulebookguard:rule-writing`: the shared writing guide, so a rule reads the same whoever writes it: one verifiable behavior, a one-line summary in business language, Gherkin examples, and `origin` and `evidence` for rules lifted from code.
594
+ - **Agent** `rulebookguard:rule-miner`: lifts the business rules existing code already enforces, one context at a time. It writes them as `proposed` rules with `origin: extracted`, evidence as `path symbol` and questions for the business, through `rulebookguard new`, and marks the code that enforces each rule with `@implements` … `@endimplements`. It checks existing rules first to avoid duplicates, stays within `contextBudget`, wraps tests that already verify a rule in `@rule` … `@endrule`, and finishes only when `lint` has no errors of its own. It never activates a rule or edits existing rules, and adds only marker comments to code: a person reviews the diff and activates what holds. Ask for it by name, for example "use rule-miner on the clients context".
595
+ - **Agent** `rulebookguard:rule-author`: designs the rules a feature needs before its code. By default it writes nothing: it reads the rulebook by levels and returns a rule change plan with new rules and their Gherkin, changed rules as diffs, deprecations, conflicts with active rules, test skeletons and open questions, then stops for a person to approve. Called again with the approved plan, it writes the new rules as `proposed` through `rulebookguard new`, applies the approved diffs and finishes when `lint` has no errors of its own. Tests and code come after, as the `rules` skill says. Ask for it by name, for example "use rule-author for this feature: …".
477
596
 
478
597
  Install it from this repository's marketplace:
479
598
 
@@ -1,4 +1,4 @@
1
- import { g as createGlobMatcher, y as toRepoPath } from "./lint-DfibQDW3.mjs";
1
+ import { S as toRepoPath, y as createGlobMatcher } from "./lint-BPdk6XxH.mjs";
2
2
  // src/catalog/anchors.ts
3
3
  function rulesForPath(rules, path) {
4
4
  const target = toRepoPath(path);