rulereceipt 0.1.86 → 0.1.87
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/guard.d.ts +11 -0
- package/dist/guard.js +43 -2
- package/package.json +1 -1
package/dist/guard.d.ts
CHANGED
|
@@ -74,5 +74,16 @@ export interface GuardDecision {
|
|
|
74
74
|
export declare function guardDecision(cwd: string, toolName: string, toolInput: {
|
|
75
75
|
command?: unknown;
|
|
76
76
|
} & Record<string, unknown>, events?: TranscriptEvent[], permissionMode?: string): GuardDecision;
|
|
77
|
+
/** One line for the guard receipt log — every block (or would-block) is recorded. */
|
|
78
|
+
export declare function guardReceiptLine(decision: GuardDecision, command: string, shadow: boolean): {
|
|
79
|
+
ts: string;
|
|
80
|
+
mode: "shadow" | "enforce";
|
|
81
|
+
action: "deny" | "would-deny" | "ask" | "would-ask";
|
|
82
|
+
command: string;
|
|
83
|
+
rules: {
|
|
84
|
+
id: string;
|
|
85
|
+
title: string;
|
|
86
|
+
}[];
|
|
87
|
+
};
|
|
77
88
|
export declare function runGuard(): Promise<void>;
|
|
78
89
|
export {};
|
package/dist/guard.js
CHANGED
|
@@ -25,9 +25,9 @@ function gitCurrentBranch(cwd) {
|
|
|
25
25
|
return undefined;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
import { readFileSync } from "node:fs";
|
|
28
|
+
import { readFileSync, appendFileSync, mkdirSync } from "node:fs";
|
|
29
29
|
import { homedir } from "node:os";
|
|
30
|
-
import { join } from "node:path";
|
|
30
|
+
import { join, dirname } from "node:path";
|
|
31
31
|
/**
|
|
32
32
|
* Modes where Claude Code shows NO permission prompt, so a hook's "ask" is
|
|
33
33
|
* ignored and the call just runs (Claude Code #89561; "ask" also drops bypass
|
|
@@ -301,6 +301,38 @@ export function guardDecision(cwd, toolName, toolInput, events = [], permissionM
|
|
|
301
301
|
}
|
|
302
302
|
return allow;
|
|
303
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Shadow mode (live-blocking rollout, vs Failproof): set RULERECEIPT_GUARD_SHADOW=1
|
|
306
|
+
* and the guard never blocks — it LOGS what it WOULD have blocked and allows the
|
|
307
|
+
* call. The honest way to turn on enforcement: run shadow first, read the log,
|
|
308
|
+
* then drop the flag. Enforcement is the default (no env needed).
|
|
309
|
+
*/
|
|
310
|
+
function guardShadowEnabled() {
|
|
311
|
+
const v = process.env.RULERECEIPT_GUARD_SHADOW;
|
|
312
|
+
return v === "1" || v === "true" || v === "yes";
|
|
313
|
+
}
|
|
314
|
+
/** One line for the guard receipt log — every block (or would-block) is recorded. */
|
|
315
|
+
export function guardReceiptLine(decision, command, shadow) {
|
|
316
|
+
const base = decision.deny ? "deny" : "ask";
|
|
317
|
+
return {
|
|
318
|
+
ts: new Date().toISOString(),
|
|
319
|
+
mode: shadow ? "shadow" : "enforce",
|
|
320
|
+
action: (shadow ? `would-${base}` : base),
|
|
321
|
+
command: command.replace(/\s+/g, " ").trim().slice(0, 120),
|
|
322
|
+
rules: decision.blocks.map((b) => ({ id: b.rule.id, title: b.rule.title })),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/** Append a receipt line to .rulereceipt/guard-log.jsonl. Fail-open: never breaks the guard. */
|
|
326
|
+
function writeGuardReceipt(cwd, line) {
|
|
327
|
+
try {
|
|
328
|
+
const p = join(cwd, ".rulereceipt", "guard-log.jsonl");
|
|
329
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
330
|
+
appendFileSync(p, `${JSON.stringify(line)}\n`);
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
/* logging must never stop (or crash) the guard */
|
|
334
|
+
}
|
|
335
|
+
}
|
|
304
336
|
export async function runGuard() {
|
|
305
337
|
const allow = () => {
|
|
306
338
|
process.stdout.write(JSON.stringify({}));
|
|
@@ -321,6 +353,15 @@ export async function runGuard() {
|
|
|
321
353
|
}
|
|
322
354
|
}
|
|
323
355
|
const decision = guardDecision(cwd, tool, toolInput, events, input.permission_mode);
|
|
356
|
+
const shadow = guardShadowEnabled();
|
|
357
|
+
const commandStr = typeof toolInput.command === "string" ? toolInput.command : tool;
|
|
358
|
+
// Every block (or would-block) writes a receipt line — the audit trail of
|
|
359
|
+
// what the guard stopped, in both shadow and enforce mode.
|
|
360
|
+
if (decision.deny || decision.ask)
|
|
361
|
+
writeGuardReceipt(cwd, guardReceiptLine(decision, commandStr, shadow));
|
|
362
|
+
// Shadow mode: log above, then ALLOW — never block, never ask.
|
|
363
|
+
if (shadow)
|
|
364
|
+
return allow();
|
|
324
365
|
if (!decision.deny && decision.ask) {
|
|
325
366
|
// "ask" is not a refusal: no exit 2. Claude Code shows its permission
|
|
326
367
|
// prompt with this reason; the user's click decides.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rulereceipt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.87",
|
|
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",
|