rulereceipt 0.1.56 → 0.1.58
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 +1 -0
- package/dist/audit.d.ts +33 -0
- package/dist/audit.js +59 -0
- package/dist/checkability.d.ts +8 -0
- package/dist/checkability.js +1 -0
- package/dist/cli.js +14 -0
- package/dist/rules.js +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,6 +76,7 @@ Published and live on npm, actively developed.
|
|
|
76
76
|
## Usage
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
+
rulereceipt audit # score your rules file for checkability — NO session needed
|
|
79
80
|
rulereceipt check # check the latest session in this project
|
|
80
81
|
rulereceipt check --markdown # same, formatted for pasting into a PR/Slack
|
|
81
82
|
rulereceipt check --html # write a shareable single-file HTML report you can send
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Rule } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* A rules-only health score — how much of a rules file can actually be checked,
|
|
4
|
+
* with NO session needed.
|
|
5
|
+
*
|
|
6
|
+
* The recurring day-one gap: a first `check` with no session is empty, and a
|
|
7
|
+
* real CLAUDE.md is mostly a handbook — measured across the public corpus, ~38%
|
|
8
|
+
* of items are rules and ~56% of those need judgment. `audit` answers "is your
|
|
9
|
+
* rules file enforceable?" on any format (CLAUDE.md, AGENTS.md, Cursor, Copilot,
|
|
10
|
+
* Windsurf, Gemini) instantly, and points at `rules --advise` for the fixes.
|
|
11
|
+
*
|
|
12
|
+
* Buckets, by how classifyRule routes each item:
|
|
13
|
+
* - checkable : any structured/deterministic kind — a session can be checked
|
|
14
|
+
* against it without a human or an LLM
|
|
15
|
+
* - judgment : needs a person (or `--llm`)
|
|
16
|
+
* - skipped : not a rule (docs, directory maps, glossary rows)
|
|
17
|
+
*/
|
|
18
|
+
export interface RulesAudit {
|
|
19
|
+
total: number;
|
|
20
|
+
checkable: number;
|
|
21
|
+
judgment: number;
|
|
22
|
+
skipped: number;
|
|
23
|
+
/** checkable / (checkable + judgment), whole %, 0 when there are no rules. */
|
|
24
|
+
percentCheckable: number;
|
|
25
|
+
/** The highest-leverage rewrites — a concrete rule missing only its literal. */
|
|
26
|
+
topFixes: {
|
|
27
|
+
title: string;
|
|
28
|
+
suggestion: string;
|
|
29
|
+
}[];
|
|
30
|
+
}
|
|
31
|
+
export declare function auditRules(rules: Rule[]): RulesAudit;
|
|
32
|
+
/** A short, readable audit. Never says "compliant" — it measures the file, not a session. */
|
|
33
|
+
export declare function renderAudit(a: RulesAudit, md?: boolean): string;
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { classifyRules } from "./checks/classify.js";
|
|
2
|
+
import { adviseRules } from "./checkability.js";
|
|
3
|
+
export function auditRules(rules) {
|
|
4
|
+
let checkable = 0;
|
|
5
|
+
let judgment = 0;
|
|
6
|
+
let skipped = 0;
|
|
7
|
+
for (const c of classifyRules(rules)) {
|
|
8
|
+
if (c.kind === "notARule")
|
|
9
|
+
skipped++;
|
|
10
|
+
else if (c.kind === "judgment")
|
|
11
|
+
judgment++;
|
|
12
|
+
else
|
|
13
|
+
checkable++;
|
|
14
|
+
}
|
|
15
|
+
const decided = checkable + judgment;
|
|
16
|
+
const topFixes = adviseRules(rules)
|
|
17
|
+
.filter((a) => a.actionable)
|
|
18
|
+
.slice(0, 5)
|
|
19
|
+
.map((a) => ({ title: a.ruleTitle, suggestion: a.suggestion }));
|
|
20
|
+
return {
|
|
21
|
+
total: checkable + judgment + skipped,
|
|
22
|
+
checkable,
|
|
23
|
+
judgment,
|
|
24
|
+
skipped,
|
|
25
|
+
percentCheckable: decided > 0 ? Math.round((checkable / decided) * 100) : 0,
|
|
26
|
+
topFixes,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** A short, readable audit. Never says "compliant" — it measures the file, not a session. */
|
|
30
|
+
export function renderAudit(a, md = false) {
|
|
31
|
+
if (a.checkable + a.judgment === 0) {
|
|
32
|
+
return md
|
|
33
|
+
? "**No rules found** in this project's rules files. Is there a `CLAUDE.md`, `AGENTS.md` or similar here?"
|
|
34
|
+
: "No rules found in this project's rules files.\nIs there a CLAUDE.md / AGENTS.md (or Cursor/Copilot/Windsurf rules) here?";
|
|
35
|
+
}
|
|
36
|
+
const H = (s) => (md ? `## ${s}` : s);
|
|
37
|
+
const out = [];
|
|
38
|
+
out.push(md ? "# RuleReceipt — rules audit" : "RuleReceipt · rules audit (no session needed)");
|
|
39
|
+
out.push("");
|
|
40
|
+
out.push(`${a.checkable + a.judgment} rules read (plus ${a.skipped} documentation item${a.skipped === 1 ? "" : "s"} not scored).`);
|
|
41
|
+
out.push("");
|
|
42
|
+
out.push(H("Can this session be checked against them?"));
|
|
43
|
+
out.push(` ${String(a.checkable).padStart(4)} checkable — verifiable from a session, no human needed`);
|
|
44
|
+
out.push(` ${String(a.judgment).padStart(4)} need judgment — a person (or \`--llm\`) decides these`);
|
|
45
|
+
out.push(` ${String(a.skipped).padStart(4)} documentation — structure/notes, not scored as rules`);
|
|
46
|
+
out.push("");
|
|
47
|
+
out.push(`${a.percentCheckable}% of your rules can be checked mechanically.`);
|
|
48
|
+
out.push("");
|
|
49
|
+
if (a.topFixes.length > 0) {
|
|
50
|
+
out.push(H("Top fixes to unlock more checks"));
|
|
51
|
+
for (const f of a.topFixes) {
|
|
52
|
+
out.push(` • ${f.title.replace(/\s+/g, " ").trim().slice(0, 60)}`);
|
|
53
|
+
out.push(` ${f.suggestion}`);
|
|
54
|
+
}
|
|
55
|
+
out.push("");
|
|
56
|
+
}
|
|
57
|
+
out.push("Full advice, rule by rule: rulereceipt rules --advise");
|
|
58
|
+
return out.join("\n");
|
|
59
|
+
}
|
package/dist/checkability.d.ts
CHANGED
|
@@ -22,6 +22,14 @@ export interface RuleAdvice {
|
|
|
22
22
|
kind: "judgment" | "notARule";
|
|
23
23
|
/** One line: what is missing and the smallest edit that fixes it. */
|
|
24
24
|
suggestion: string;
|
|
25
|
+
/**
|
|
26
|
+
* True when there is a concrete, high-leverage rewrite (name the command/
|
|
27
|
+
* file/branch in backticks) that would turn this into a real check — as
|
|
28
|
+
* opposed to a genuine judgment call or plain documentation, where the
|
|
29
|
+
* honest answer is "no edit makes it mechanical." `audit` surfaces the
|
|
30
|
+
* actionable ones as the top fixes.
|
|
31
|
+
*/
|
|
32
|
+
actionable?: boolean;
|
|
25
33
|
}
|
|
26
34
|
/**
|
|
27
35
|
* Advice for one rule, or null when the rule is already mechanically checked.
|
package/dist/checkability.js
CHANGED
|
@@ -53,6 +53,7 @@ export function adviseRule(rule) {
|
|
|
53
53
|
return {
|
|
54
54
|
ruleTitle: rule.title,
|
|
55
55
|
kind,
|
|
56
|
+
actionable: true, // a concrete, high-leverage rewrite: just add the literal
|
|
56
57
|
suggestion: `mentions ${subject} but names no exact term to match. Put the concrete command, file or branch in backticks (e.g. ${exampleFor(subject)}) and it becomes a mechanical check.`,
|
|
57
58
|
};
|
|
58
59
|
}
|
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,7 @@ import { adviseRules } from "./checkability.js";
|
|
|
13
13
|
import { shadowedAgentsMd } from "./shadowedAgents.js";
|
|
14
14
|
import { partitionByAge, futureResult } from "./ruleAge.js";
|
|
15
15
|
import { auditSessions, renderComplianceReport } from "./report/complianceReport.js";
|
|
16
|
+
import { auditRules, renderAudit } from "./audit.js";
|
|
16
17
|
import { classifyRules } from "./checks/classify.js";
|
|
17
18
|
import { loadOverrides, saveOverride, clearOverride, staleOverrides, ruleFingerprint, OVERRIDES_PATH } from "./overrides.js";
|
|
18
19
|
import { runDeterministicChecks } from "./checks/deterministicChecks.js";
|
|
@@ -891,6 +892,19 @@ program
|
|
|
891
892
|
const r = await auditSessions(process.cwd(), Number.isFinite(n) ? n : 25);
|
|
892
893
|
console.log(renderComplianceReport(r, Boolean(opts.markdown)));
|
|
893
894
|
});
|
|
895
|
+
program
|
|
896
|
+
.command("audit")
|
|
897
|
+
.description("Score your rules files for checkability — NO session needed. How much can be checked mechanically vs needs a human vs is documentation. Works on CLAUDE.md, AGENTS.md, Cursor, Copilot, Windsurf and Gemini rules.")
|
|
898
|
+
.option("--markdown", "output as markdown, for a report you can send")
|
|
899
|
+
.option("--json", "output machine-readable JSON (counts and the checkable %)")
|
|
900
|
+
.action((opts) => {
|
|
901
|
+
const a = auditRules(loadRules(process.cwd()));
|
|
902
|
+
if (opts.json) {
|
|
903
|
+
console.log(JSON.stringify(a, null, 2));
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
console.log(renderAudit(a, Boolean(opts.markdown)));
|
|
907
|
+
});
|
|
894
908
|
program
|
|
895
909
|
.command("digest")
|
|
896
910
|
.description("A non-technical summary of recent check runs (counts only, no rule text) — for a manager who doesn't have time to read 30 individual reports.")
|
package/dist/rules.js
CHANGED
|
@@ -62,8 +62,12 @@ function ruleFilesAtLevel(dir) {
|
|
|
62
62
|
for (const rel of RULE_DIRS)
|
|
63
63
|
found.push(...markdownFilesIn(join(dir, rel)));
|
|
64
64
|
push("CLAUDE.md");
|
|
65
|
-
|
|
65
|
+
// AGENTS.md (and the singular AGENT.md some tools use) are read only when
|
|
66
|
+
// there's no CLAUDE.md at this level, mirroring Claude Code's shadow rule.
|
|
67
|
+
if (!has("CLAUDE.md")) {
|
|
66
68
|
push("AGENTS.md");
|
|
69
|
+
push("AGENT.md");
|
|
70
|
+
}
|
|
67
71
|
// .local variants: their precedence relative to the base files is not
|
|
68
72
|
// documented, so both are kept rather than guessing at a shadow rule.
|
|
69
73
|
push("CLAUDE.local.md");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rulereceipt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.58",
|
|
4
4
|
"description": "Checks whether your AI coding agent followed your rules, with evidence. Works with Claude Code (Codex in testing); reads CLAUDE.md, AGENTS.md, Cursor, Copilot and Windsurf rules.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|