rulereceipt 0.1.28 → 0.1.29

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.
Files changed (2) hide show
  1. package/dist/rules.js +27 -9
  2. package/package.json +1 -1
package/dist/rules.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { homedir } from "node:os";
2
- import { dirname, join, parse } from "node:path";
2
+ import { dirname, join, parse, resolve } from "node:path";
3
3
  import { existsSync, readdirSync, statSync } from "node:fs";
4
4
  import { parseClaudeMd } from "./parsers/readClaudeMd.js";
5
5
  import { findClaudeHomeDirNames } from "./parsers/transcriptParser.js";
@@ -78,11 +78,20 @@ function findProjectRuleFiles(cwd) {
78
78
  const home = homedir();
79
79
  let dir = cwd;
80
80
  for (;;) {
81
+ // The home directory's rules are GLOBAL, and loadRules reads them as
82
+ // such. Guard BEFORE collecting: the original break sat below the push,
83
+ // so the home level was always collected first and every global rule was
84
+ // reported a second time as a project rule. Found 2026-09-08 — a machine
85
+ // with one 14-rule file reported "28 rules checked", doubling every
86
+ // figure on the report. Running from inside the home dir still collects
87
+ // it; loadRules dedupes that against the global pass.
88
+ if (dir === home && dir !== cwd)
89
+ break;
81
90
  found.push(...ruleFilesAtLevel(dir));
82
91
  // stop AT the repo root (inclusive) — its rules do apply
83
92
  if (existsSync(join(dir, ".git")))
84
93
  break;
85
- if (dir === root || dir === home)
94
+ if (dir === root)
86
95
  break;
87
96
  const parent = dirname(dir);
88
97
  if (parent === dir)
@@ -110,15 +119,24 @@ function findProjectRuleFiles(cwd) {
110
119
  */
111
120
  export function loadRules(cwd) {
112
121
  const rules = [];
122
+ // One file, one set of rules. Globals are read first, so a file reachable
123
+ // both ways keeps its "global" label. Without this, running the check from
124
+ // inside the home directory reported every global rule twice.
125
+ const seen = new Set();
126
+ const read = (path, source) => {
127
+ const key = resolve(path);
128
+ if (seen.has(key))
129
+ return;
130
+ seen.add(key);
131
+ rules.push(...parseClaudeMd(path, source));
132
+ };
113
133
  for (const dirName of findClaudeHomeDirNames()) {
114
134
  const base = join(homedir(), dirName);
115
- rules.push(...parseClaudeMd(join(base, "CLAUDE.md"), "global"));
116
- for (const file of markdownFilesIn(join(base, "rules"))) {
117
- rules.push(...parseClaudeMd(file, "global"));
118
- }
119
- }
120
- for (const path of findProjectRuleFiles(cwd)) {
121
- rules.push(...parseClaudeMd(path, "project"));
135
+ read(join(base, "CLAUDE.md"), "global");
136
+ for (const file of markdownFilesIn(join(base, "rules")))
137
+ read(file, "global");
122
138
  }
139
+ for (const path of findProjectRuleFiles(cwd))
140
+ read(path, "project");
123
141
  return rules;
124
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulereceipt",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "Checks whether a Claude Code session actually followed your CLAUDE.md / AGENTS.md rules, with evidence.",
5
5
  "repository": {
6
6
  "type": "git",