rulebookguard 0.1.0 → 0.2.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 +49 -3
- package/dist/cli.mjs +186 -12
- package/dist/index.d.mts +4 -1
- package/dist/index.mjs +1 -1
- package/dist/{lint-V-Uw5ueZ.mjs → lint-DtD_Ekei.mjs} +2 -1
- package/package.json +5 -2
- package/schemas/v1/config.schema.json +2 -1
package/README.md
CHANGED
|
@@ -36,9 +36,12 @@ testGlobs: # required: files scanned for @rule tags
|
|
|
36
36
|
- 'src/**/*.test.ts'
|
|
37
37
|
- 'tests/**/test_*.py'
|
|
38
38
|
contextBudget: 30 # default: 30; lint warns when a context holds more rules
|
|
39
|
+
language: es # default: en; language of the text that rulebookguard generates
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
`schema` and `testGlobs` are required (`testGlobs: []` is valid but finds no tags). Unknown fields are rejected.
|
|
42
|
+
`schema` and `testGlobs` are required (`testGlobs: []` is valid but finds no tags). Unknown fields are rejected.
|
|
43
|
+
|
|
44
|
+
`language` (`en` or `es`) sets the language of the text rulebookguard writes for you, such as the template of `new`. Rule content is free text and is never checked for language, so rules can be written in any language whatever this setting says. `language` needs rulebookguard 0.1.1 or later; older versions reject it as an unknown field. The JSON Schemas ship with the package under `rulebookguard/schemas/v1/`.
|
|
42
45
|
|
|
43
46
|
Inside a git work tree, files are listed with `git ls-files --cached --others --exclude-standard`, so anything in `.gitignore` (`dist/`, `coverage/`, caches) is never matched by anchors or test globs; files inside submodules are listed too. A submodule path that is a symlink, or that resolves outside the rulebook root, is skipped. A test file that is a symlink is scanned for `@rule` tags only when its target lies inside the rulebook root. If one submodule cannot be listed, commands print `warning: could not list files of submodule "<path>" (<reason>); skipping it.` and keep the rest of the git listing. Outside git, or when the rulebook root itself is git-ignored, the whole tree is walked, skipping only `.git` and `node_modules`. If git fails (for example, git is missing from `PATH` or the repository has dubious ownership), commands print one warning on stderr and walk the tree instead.
|
|
44
47
|
|
|
@@ -224,7 +227,7 @@ Exits 1 for an unknown id.
|
|
|
224
227
|
|
|
225
228
|
### `new <context>`
|
|
226
229
|
|
|
227
|
-
Scaffolds the next free id of a context as a `proposed` rule and prints its path. Ids are never reused, and an existing file is never overwritten.
|
|
230
|
+
Scaffolds the next free id of a context as a `proposed` rule and prints its path. Ids are never reused, and an existing file is never overwritten. The placeholder text of the new rule follows `language` in the config.
|
|
228
231
|
|
|
229
232
|
```sh
|
|
230
233
|
$ rulebookguard new clients
|
|
@@ -233,6 +236,30 @@ docs/rules/clients/R-CLI-004.md
|
|
|
233
236
|
|
|
234
237
|
Exits 1 for an unknown context or when the context has no free id left.
|
|
235
238
|
|
|
239
|
+
### `hook`
|
|
240
|
+
|
|
241
|
+
A Claude Code hook for `PreToolUse` and `PostToolUse` events of tools that take a `file_path` (the Claude Code plugin below 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`:
|
|
242
|
+
|
|
243
|
+
```sh
|
|
244
|
+
$ echo '{"hook_event_name":"PostToolUse","tool_name":"Read","tool_input":{"file_path":"/repo/src/billing/invoice.go"}}' | rulebookguard hook
|
|
245
|
+
{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"Business rules anchored to src/billing/invoice.go (rulebookguard). …"}}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
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).
|
|
249
|
+
|
|
250
|
+
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. 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.
|
|
251
|
+
|
|
252
|
+
### `mcp [--project <dir>]`
|
|
253
|
+
|
|
254
|
+
An [MCP](https://modelcontextprotocol.io) server over stdio for AI agents, with two read-only tools:
|
|
255
|
+
|
|
256
|
+
| Tool | Input | Answers |
|
|
257
|
+
| ------------ | ------------------------------------------------------ | --------------------------------------------- |
|
|
258
|
+
| `rules_for` | `path`: absolute, or relative to the project directory | what `rulebookguard for <path> --json` prints |
|
|
259
|
+
| `rules_show` | `id`: a rule id | what `rulebookguard show <id> --json` prints |
|
|
260
|
+
|
|
261
|
+
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.`
|
|
262
|
+
|
|
236
263
|
### `lint [--base <ref>] [--json]`
|
|
237
264
|
|
|
238
265
|
Checks the whole catalog. Each finding is printed as `<severity> <code> <file>[:line] <message>`, followed by a count.
|
|
@@ -292,9 +319,28 @@ jobs:
|
|
|
292
319
|
|
|
293
320
|
Pin the version, or add rulebookguard as a dev dependency and run it from the lockfile, so a new release never changes CI results by itself.
|
|
294
321
|
|
|
322
|
+
## Claude Code plugin
|
|
323
|
+
|
|
324
|
+
The plugin in [`plugin/`](plugin) connects the rulebook to Claude Code:
|
|
325
|
+
|
|
326
|
+
- **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.
|
|
327
|
+
- **MCP server:** the `rules_for` and `rules_show` tools from `rulebookguard mcp`, for rules of files Claude has not opened yet.
|
|
328
|
+
- **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.
|
|
329
|
+
|
|
330
|
+
Install it from this repository's marketplace:
|
|
331
|
+
|
|
332
|
+
```sh
|
|
333
|
+
claude plugin marketplace add JuanCJR/rulebookguard
|
|
334
|
+
claude plugin install rulebookguard@rulebookguard
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Or, inside Claude Code 2.1.275 or later: `/plugin install rulebookguard --marketplace JuanCJR/rulebookguard`.
|
|
338
|
+
|
|
339
|
+
The plugin runs the project's own rulebookguard (`node_modules/rulebookguard`, in the project directory or a parent) when its version is at least the plugin's; otherwise it runs `npx rulebookguard@<plugin version>`, which needs Node.js and network access the first time. The hooks never block a tool: any failure, including a timeout, lets it go ahead without rules.
|
|
340
|
+
|
|
295
341
|
## Releasing
|
|
296
342
|
|
|
297
|
-
Releases are published to npm from GitHub Actions with [trusted publishing](https://docs.npmjs.com/trusted-publishers), so no npm token is stored and every version carries a provenance attestation. To release, bump `version` in `package.json
|
|
343
|
+
Releases are published to npm from GitHub Actions with [trusted publishing](https://docs.npmjs.com/trusted-publishers), so no npm token is stored and every version carries a provenance attestation. To release, bump `version` in `package.json` and in `plugin/.claude-plugin/plugin.json` (a test keeps them equal), merge it to `main`, then push a matching tag:
|
|
298
344
|
|
|
299
345
|
```sh
|
|
300
346
|
git tag v0.1.0
|
package/dist/cli.mjs
CHANGED
|
@@ -1,8 +1,138 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { M as FormatError, N as formatIssue, _ as expandGlobs, a as CliError, c as openCatalog, d as scanTestTags, h as rulesForPath, l as writeJson, o as findRule, r as lintCommand, s as gitFallbackWarning, u as writeLines, y as toRepoPath } from "./lint-
|
|
2
|
+
import { M as FormatError, N as formatIssue, _ as expandGlobs, a as CliError, c as openCatalog, d as scanTestTags, f as findRulebookRoot, h as rulesForPath, l as writeJson, o as findRule, p as loadCatalog, r as lintCommand, s as gitFallbackWarning, u as writeLines, y as toRepoPath } from "./lint-DtD_Ekei.mjs";
|
|
3
3
|
import { readdir, writeFile } from "node:fs/promises";
|
|
4
|
-
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
8
|
+
import * as z from "zod/v4";
|
|
9
|
+
// src/commands/hook.ts
|
|
10
|
+
const HOOK_BODY_LIMIT = 1e4;
|
|
11
|
+
const TEXT = {
|
|
12
|
+
en: {
|
|
13
|
+
header: (path) => `Business rules anchored to ${path} (rulebookguard). They are the source of truth: keep any change to this file consistent with them, and change the rule first if a change alters its behavior.`,
|
|
14
|
+
ruleFile: "Rule file",
|
|
15
|
+
omitted: `Rule texts omitted (over ${HOOK_BODY_LIMIT} characters); run \`rulebookguard show <id>\` for the full text.`
|
|
16
|
+
},
|
|
17
|
+
es: {
|
|
18
|
+
header: (path) => `Reglas de negocio ancladas a ${path} (rulebookguard). Son la fuente de verdad: mantén cualquier cambio a este archivo coherente con ellas y, si un cambio altera su comportamiento, cambia primero la regla.`,
|
|
19
|
+
ruleFile: "Archivo de la regla",
|
|
20
|
+
omitted: `Se omitió el texto de las reglas (más de ${HOOK_BODY_LIMIT} caracteres); ejecuta \`rulebookguard show <id>\` para verlo completo.`
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
async function hookCommand(io) {
|
|
24
|
+
try {
|
|
25
|
+
const event = parseEvent(await (io.stdin?.() ?? Promise.resolve("")));
|
|
26
|
+
const context = event === void 0 ? void 0 : await hookContext(event, io.cwd);
|
|
27
|
+
if (event !== void 0 && context !== void 0) io.stdout(`${JSON.stringify({ hookSpecificOutput: {
|
|
28
|
+
hookEventName: event.name,
|
|
29
|
+
additionalContext: context
|
|
30
|
+
} })}\n`);
|
|
31
|
+
} catch {}
|
|
32
|
+
}
|
|
33
|
+
const TOOL_EVENTS = ["PreToolUse", "PostToolUse"];
|
|
34
|
+
async function hookContext(event, cwd) {
|
|
35
|
+
const file = resolve(event.cwd ?? cwd, event.filePath);
|
|
36
|
+
const root = await findRulebookRoot(dirname(file));
|
|
37
|
+
if (root === void 0) return;
|
|
38
|
+
const path = relative(root, file);
|
|
39
|
+
if (path === "" || path === ".." || path.startsWith(`..${sep}`) || isAbsolute(path)) return;
|
|
40
|
+
const catalog = await loadCatalog(root);
|
|
41
|
+
const repoPath = path.split(sep).join("/");
|
|
42
|
+
const rules = rulesForPath(catalog.rules, repoPath).filter((rule) => rule.status === "active");
|
|
43
|
+
if (rules.length === 0) return;
|
|
44
|
+
return render(repoPath, rules, TEXT[catalog.config.language ?? "en"]);
|
|
45
|
+
}
|
|
46
|
+
function parseEvent(input) {
|
|
47
|
+
let event;
|
|
48
|
+
try {
|
|
49
|
+
event = JSON.parse(input);
|
|
50
|
+
} catch {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (typeof event !== "object" || event === null) return;
|
|
54
|
+
const { hook_event_name: name, tool_input: toolInput, cwd } = event;
|
|
55
|
+
const filePath = toolInput?.file_path;
|
|
56
|
+
const toolEvent = TOOL_EVENTS.find((candidate) => candidate === name);
|
|
57
|
+
if (toolEvent === void 0 || typeof filePath !== "string" || filePath === "") return;
|
|
58
|
+
return typeof cwd === "string" ? {
|
|
59
|
+
name: toolEvent,
|
|
60
|
+
filePath,
|
|
61
|
+
cwd
|
|
62
|
+
} : {
|
|
63
|
+
name: toolEvent,
|
|
64
|
+
filePath
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function render(path, rules, text) {
|
|
68
|
+
const withBodies = rules.reduce((total, rule) => total + rule.body.trim().length, 0) <= HOOK_BODY_LIMIT;
|
|
69
|
+
const sections = rules.map((rule) => {
|
|
70
|
+
const lines = [`## ${rule.id} (${rule.context}): ${rule.summary}`, `${text.ruleFile}: ${rule.file}`];
|
|
71
|
+
return withBodies ? [
|
|
72
|
+
...lines,
|
|
73
|
+
"",
|
|
74
|
+
rule.body.trim()
|
|
75
|
+
].join("\n") : lines.join("\n");
|
|
76
|
+
});
|
|
77
|
+
const parts = [text.header(path), ...sections];
|
|
78
|
+
if (!withBodies) parts.push(text.omitted);
|
|
79
|
+
return parts.join("\n\n");
|
|
80
|
+
}
|
|
81
|
+
// package.json
|
|
82
|
+
var version = "0.2.0";
|
|
83
|
+
// src/mcp/server.ts
|
|
84
|
+
function createRulesServer(projectDir) {
|
|
85
|
+
const server = new McpServer({
|
|
86
|
+
name: "rulebookguard",
|
|
87
|
+
version
|
|
88
|
+
});
|
|
89
|
+
server.registerTool("rules_for", {
|
|
90
|
+
description: "Active business rules anchored to a file (id, context, summary, rule file). Call it before changing a file; the rules are the source of truth for its behavior.",
|
|
91
|
+
inputSchema: z.object({ path: z.string().describe("File path, absolute or relative to the project directory") }),
|
|
92
|
+
annotations: { readOnlyHint: true }
|
|
93
|
+
}, ({ path }) => cli(projectDir, [
|
|
94
|
+
"for",
|
|
95
|
+
path,
|
|
96
|
+
"--json"
|
|
97
|
+
]));
|
|
98
|
+
server.registerTool("rules_show", {
|
|
99
|
+
description: "Full text of one business rule: summary, anchors, entities, supersedes and the rule body with its examples.",
|
|
100
|
+
inputSchema: z.object({ id: z.string().describe("Rule id, for example R-CLI-001") }),
|
|
101
|
+
annotations: { readOnlyHint: true }
|
|
102
|
+
}, ({ id }) => cli(projectDir, [
|
|
103
|
+
"show",
|
|
104
|
+
id,
|
|
105
|
+
"--json"
|
|
106
|
+
]));
|
|
107
|
+
return server;
|
|
108
|
+
}
|
|
109
|
+
async function cli(cwd, args) {
|
|
110
|
+
let stdout = "";
|
|
111
|
+
let stderr = "";
|
|
112
|
+
if (await runCli(args, {
|
|
113
|
+
cwd,
|
|
114
|
+
stdout: (text) => {
|
|
115
|
+
stdout += text;
|
|
116
|
+
},
|
|
117
|
+
stderr: (text) => {
|
|
118
|
+
stderr += text;
|
|
119
|
+
}
|
|
120
|
+
}) === 0) return { content: [{
|
|
121
|
+
type: "text",
|
|
122
|
+
text: stdout
|
|
123
|
+
}] };
|
|
124
|
+
return {
|
|
125
|
+
content: [{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: stderr.trim()
|
|
128
|
+
}],
|
|
129
|
+
isError: true
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
// src/commands/mcp.ts
|
|
133
|
+
async function mcpCommand(io, project) {
|
|
134
|
+
await createRulesServer(resolve(io.cwd, project ?? ".")).connect(new StdioServerTransport());
|
|
135
|
+
}
|
|
6
136
|
// src/commands/new.ts
|
|
7
137
|
const MAX_NUMBER = 999;
|
|
8
138
|
async function newCommand(catalog, io, name) {
|
|
@@ -17,32 +147,51 @@ async function newCommand(catalog, io, name) {
|
|
|
17
147
|
const id = `R-${context.prefix}-${String(last + 1).padStart(3, "0")}`;
|
|
18
148
|
const file = `${directory}/${id}.md`;
|
|
19
149
|
try {
|
|
20
|
-
await writeFile(join(catalog.root, file), template(id, context.context), { flag: "wx" });
|
|
150
|
+
await writeFile(join(catalog.root, file), template(id, context.context, catalog.config.language ?? "en"), { flag: "wx" });
|
|
21
151
|
} catch (error) {
|
|
22
152
|
if (error.code === "EEXIST") throw new CliError(`${file} already exists; nothing was written.`);
|
|
23
153
|
throw error;
|
|
24
154
|
}
|
|
25
155
|
writeLines(io, [file]);
|
|
26
156
|
}
|
|
27
|
-
|
|
157
|
+
const TEMPLATE_TEXT = {
|
|
158
|
+
en: {
|
|
159
|
+
summary: "TODO: one-line summary of the rule.",
|
|
160
|
+
title: "TODO: rule title",
|
|
161
|
+
intro: "TODO: state the rule in full, then add examples as Gherkin scenarios or tables.",
|
|
162
|
+
scenario: `Scenario: TODO
|
|
163
|
+
Given TODO
|
|
164
|
+
When TODO
|
|
165
|
+
Then TODO`
|
|
166
|
+
},
|
|
167
|
+
es: {
|
|
168
|
+
summary: "TODO: resumen de la regla en una línea.",
|
|
169
|
+
title: "TODO: título de la regla",
|
|
170
|
+
intro: "TODO: describe la regla completa y agrega ejemplos como escenarios Gherkin o tablas.",
|
|
171
|
+
scenario: `# language: es
|
|
172
|
+
Escenario: TODO
|
|
173
|
+
Dado TODO
|
|
174
|
+
Cuando TODO
|
|
175
|
+
Entonces TODO`
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
function template(id, context, language) {
|
|
179
|
+
const text = TEMPLATE_TEXT[language];
|
|
28
180
|
return `---
|
|
29
181
|
id: ${id}
|
|
30
182
|
context: ${context}
|
|
31
183
|
entities: []
|
|
32
184
|
status: proposed
|
|
33
185
|
anchors: []
|
|
34
|
-
summary: '
|
|
186
|
+
summary: '${text.summary}'
|
|
35
187
|
---
|
|
36
188
|
|
|
37
|
-
#
|
|
189
|
+
# ${text.title}
|
|
38
190
|
|
|
39
|
-
|
|
191
|
+
${text.intro}
|
|
40
192
|
|
|
41
193
|
\`\`\`gherkin
|
|
42
|
-
|
|
43
|
-
Given TODO
|
|
44
|
-
When TODO
|
|
45
|
-
Then TODO
|
|
194
|
+
${text.scenario}
|
|
46
195
|
\`\`\`
|
|
47
196
|
`;
|
|
48
197
|
}
|
|
@@ -175,6 +324,10 @@ Commands:
|
|
|
175
324
|
new <context> scaffold the next free rule id as a proposed rule
|
|
176
325
|
lint [--base <ref>] [--json] check the catalog, anchors and test tags;
|
|
177
326
|
--base: rules changed since <ref> need a changed test
|
|
327
|
+
hook Claude Code PreToolUse hook: reads the event on stdin
|
|
328
|
+
and prints the rules anchored to the edited file
|
|
329
|
+
mcp [--project <dir>] MCP server over stdio with rules_for and rules_show;
|
|
330
|
+
--project: directory to find the rulebook from (default: cwd)
|
|
178
331
|
|
|
179
332
|
Exit codes: 0 success, 1 failure (lint: any error), 2 usage error.
|
|
180
333
|
`;
|
|
@@ -212,6 +365,14 @@ const COMMANDS = {
|
|
|
212
365
|
argument: "context",
|
|
213
366
|
options: {}
|
|
214
367
|
},
|
|
368
|
+
hook: {
|
|
369
|
+
usage: "rulebookguard hook",
|
|
370
|
+
options: {}
|
|
371
|
+
},
|
|
372
|
+
mcp: {
|
|
373
|
+
usage: "rulebookguard mcp [--project <dir>]",
|
|
374
|
+
options: { project: { type: "string" } }
|
|
375
|
+
},
|
|
215
376
|
lint: {
|
|
216
377
|
usage: "rulebookguard lint [--base <ref>] [--json]",
|
|
217
378
|
options: {
|
|
@@ -245,6 +406,14 @@ async function dispatch(argv, io) {
|
|
|
245
406
|
if (spec === void 0) throw new CliError(`Unknown command "${name}".\n${USAGE.trimEnd()}`, 2);
|
|
246
407
|
const { values, argument } = parse(spec, rest);
|
|
247
408
|
const json = values.json === true;
|
|
409
|
+
if (name === "hook") {
|
|
410
|
+
await hookCommand(io);
|
|
411
|
+
return 0;
|
|
412
|
+
}
|
|
413
|
+
if (name === "mcp") {
|
|
414
|
+
await mcpCommand(io, values.project);
|
|
415
|
+
return 0;
|
|
416
|
+
}
|
|
248
417
|
if (name === "lint") return lintCommand(io, {
|
|
249
418
|
base: values.base,
|
|
250
419
|
json
|
|
@@ -298,6 +467,11 @@ function parse(spec, args) {
|
|
|
298
467
|
process.exitCode = await runCli(process.argv.slice(2), {
|
|
299
468
|
cwd: process.cwd(),
|
|
300
469
|
stdout: (text) => process.stdout.write(text),
|
|
301
|
-
stderr: (text) => process.stderr.write(text)
|
|
470
|
+
stderr: (text) => process.stderr.write(text),
|
|
471
|
+
stdin: async () => {
|
|
472
|
+
let text = "";
|
|
473
|
+
for await (const chunk of process.stdin) text += String(chunk);
|
|
474
|
+
return text;
|
|
475
|
+
}
|
|
302
476
|
});
|
|
303
477
|
export {};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
type RuleStatus = "proposed" | "active" | "deprecated";
|
|
2
|
+
type RuleLanguage = "en" | "es";
|
|
2
3
|
interface RulebookConfig {
|
|
3
4
|
schema: 1;
|
|
4
5
|
rulesDir: string;
|
|
5
6
|
testGlobs: string[];
|
|
6
7
|
contextBudget: number;
|
|
8
|
+
/** Language the generated rule text is written in; `en` when unset. */
|
|
9
|
+
language?: RuleLanguage;
|
|
7
10
|
}
|
|
8
11
|
interface ContextFrontmatter {
|
|
9
12
|
context: string;
|
|
@@ -146,4 +149,4 @@ interface LintOptions {
|
|
|
146
149
|
export declare function lintCatalog(catalog: Catalog, tags?: TagIndex, onGitFallback?: GitFallbackListener): Promise<LintFinding[]>;
|
|
147
150
|
export declare function pendingImplementation(catalog: Catalog, tags: TagIndex, base: string, changed: string[]): LintFinding[];
|
|
148
151
|
export declare function formatFinding(finding: LintFinding): string;
|
|
149
|
-
export type { Catalog, CatalogIssue, CatalogIssueCode, ContextDoc, ContextFrontmatter, ContextsJson, ForJson, FormatIssue, Frontmatter, GitFallbackListener, ImpactJson, LintFinding, LintJson, LintOptions, LintSeverity, ListJson, RuleDoc, RuleFrontmatter, RuleStatus, RuleSummaryJson, RuleTag, RulebookConfig, ShowJson, TagIndex, TagLocation };
|
|
152
|
+
export type { Catalog, CatalogIssue, CatalogIssueCode, ContextDoc, ContextFrontmatter, ContextsJson, ForJson, FormatIssue, Frontmatter, GitFallbackListener, ImpactJson, LintFinding, LintJson, LintOptions, LintSeverity, ListJson, RuleDoc, RuleFrontmatter, RuleLanguage, RuleStatus, RuleSummaryJson, RuleTag, RulebookConfig, ShowJson, TagIndex, TagLocation };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { k as CONFIG_FILE_NAME, T as CONTEXT_FILE_NAME, M as FormatError, b as RULE_TAG_PATTERN, g as createGlobMatcher, _ as expandGlobs, x as findRuleTags, f as findRulebookRoot, t as formatFinding, N as formatIssue, S as isRuleId, n as lintCatalog, v as listRepoFiles, p as loadCatalog, A as loadConfig, E as loadContext, C as loadRule, j as parseConfig, D as parseContext, w as parseRule, i as pendingImplementation, m as relativeToRoot, h as rulesForPath, d as scanTestTags, O as splitFrontmatter, y as toRepoPath } from "./lint-
|
|
1
|
+
export { k as CONFIG_FILE_NAME, T as CONTEXT_FILE_NAME, M as FormatError, b as RULE_TAG_PATTERN, g as createGlobMatcher, _ as expandGlobs, x as findRuleTags, f as findRulebookRoot, t as formatFinding, N as formatIssue, S as isRuleId, n as lintCatalog, v as listRepoFiles, p as loadCatalog, A as loadConfig, E as loadContext, C as loadRule, j as parseConfig, D as parseContext, w as parseRule, i as pendingImplementation, m as relativeToRoot, h as rulesForPath, d as scanTestTags, O as splitFrontmatter, y as toRepoPath } from "./lint-DtD_Ekei.mjs";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rulebookguard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Keep business rules as the single source of truth, bound to code and tests.",
|
|
5
5
|
"homepage": "https://github.com/JuanCJR/rulebookguard#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -28,11 +28,14 @@
|
|
|
28
28
|
"./package.json": "./package.json"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/server": "^2.3.1",
|
|
31
32
|
"ajv": "^8.20.0",
|
|
32
33
|
"picomatch": "^4.0.7",
|
|
33
|
-
"yaml": "^2.9.1"
|
|
34
|
+
"yaml": "^2.9.1",
|
|
35
|
+
"zod": "^4.6.7"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
38
|
+
"@modelcontextprotocol/client": "2.3.1",
|
|
36
39
|
"@types/node": "^20.19.43",
|
|
37
40
|
"@types/picomatch": "^4.0.3",
|
|
38
41
|
"typescript": "^7.0.2",
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"schema": { "const": 1 },
|
|
8
8
|
"rulesDir": { "type": "string", "minLength": 1, "default": "docs/rules" },
|
|
9
9
|
"testGlobs": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
10
|
-
"contextBudget": { "type": "integer", "minimum": 1, "default": 30 }
|
|
10
|
+
"contextBudget": { "type": "integer", "minimum": 1, "default": 30 },
|
|
11
|
+
"language": { "enum": ["en", "es"] }
|
|
11
12
|
},
|
|
12
13
|
"required": ["schema", "testGlobs"],
|
|
13
14
|
"additionalProperties": false
|