From 7fda3e1e04dae524439f48cb1b389d2af870f6ca Mon Sep 17 00:00:00 2001
From: NiveditJain
Date: Thu, 21 May 2026 19:38:46 -0700
Subject: [PATCH 1/9] feat: add `failproofai audit` to count past stupid agent
behavior across sessions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Scans transcripts from all 7 supported CLIs (Claude/Codex/Copilot/Cursor/
OpenCode/Pi/Gemini) and reports how often the agent did wasteful or risky
things — both via replay through the existing 39 builtin policies and via
8 new audit-only detectors (redundant-cd-cwd, prefer-edit-over-read-cat,
prefer-edit-over-sed-awk, prefer-write-over-heredoc, sleep-polling-loop,
find-from-root, git-commit-no-verify, reread-after-edit). Output: ANSI
table to stdout + sectioned markdown report to ./failproofai-audit.md.
Per-transcript results cached at ~/.failproofai/cache/audit/ keyed by
(mtime, size, engineVersion, detectorVersion) so the cache invalidates
when policy or detector code changes. Skips warn-repeated-tool-calls in
replay (its per-session sidecar mutation would pollute real user data).
Refactor: extracts per-CLI tool-name + tool-input canonicalization from
src/hooks/handler.ts into src/hooks/tool-name-canonicalize.ts so the audit
replay and the live handler share one implementation. Adds
lib/claude-sessions.ts for Claude transcript discovery (mirroring the
existing per-CLI lib/-sessions.ts helpers).
Tests: +36 unit + integration tests under __tests__/audit/ — detector
positive/negative cases, replay through real builtins, and an end-to-end
fixture-transcript run via runAudit(). All 1659 tests pass; lint + tsc
clean.
Co-Authored-By: Claude Opus 4.7
---
CHANGELOG.md | 3 +
__tests__/audit/detectors.test.ts | 166 ++++++++++
__tests__/audit/index.test.ts | 87 +++++
__tests__/audit/replay.test.ts | 52 +++
bin/failproofai.mjs | 156 ++++++++-
lib/claude-sessions.ts | 181 +++++++++++
src/audit/cache.ts | 110 +++++++
src/audit/cli-adapters/claude.ts | 97 ++++++
src/audit/cli-adapters/codex.ts | 56 ++++
src/audit/cli-adapters/copilot.ts | 51 +++
src/audit/cli-adapters/cursor.ts | 51 +++
src/audit/cli-adapters/gemini.ts | 51 +++
src/audit/cli-adapters/index.ts | 70 ++++
src/audit/cli-adapters/opencode.ts | 52 +++
src/audit/cli-adapters/pi.ts | 51 +++
src/audit/cli-adapters/shared.ts | 74 +++++
src/audit/detectors/find-from-root.ts | 25 ++
src/audit/detectors/git-commit-no-verify.ts | 20 ++
src/audit/detectors/index.ts | 33 ++
.../detectors/prefer-edit-over-read-cat.ts | 29 ++
.../detectors/prefer-edit-over-sed-awk.ts | 25 ++
.../detectors/prefer-write-over-heredoc.ts | 32 ++
src/audit/detectors/redundant-cd-cwd.ts | 26 ++
src/audit/detectors/reread-after-edit.ts | 56 ++++
src/audit/detectors/sleep-polling-loop.ts | 32 ++
src/audit/index.ts | 306 ++++++++++++++++++
src/audit/replay.ts | 121 +++++++
src/audit/report.ts | 175 ++++++++++
src/audit/types.ts | 173 ++++++++++
src/hooks/handler.ts | 83 +----
src/hooks/tool-name-canonicalize.ts | 60 ++++
31 files changed, 2420 insertions(+), 84 deletions(-)
create mode 100644 __tests__/audit/detectors.test.ts
create mode 100644 __tests__/audit/index.test.ts
create mode 100644 __tests__/audit/replay.test.ts
create mode 100644 lib/claude-sessions.ts
create mode 100644 src/audit/cache.ts
create mode 100644 src/audit/cli-adapters/claude.ts
create mode 100644 src/audit/cli-adapters/codex.ts
create mode 100644 src/audit/cli-adapters/copilot.ts
create mode 100644 src/audit/cli-adapters/cursor.ts
create mode 100644 src/audit/cli-adapters/gemini.ts
create mode 100644 src/audit/cli-adapters/index.ts
create mode 100644 src/audit/cli-adapters/opencode.ts
create mode 100644 src/audit/cli-adapters/pi.ts
create mode 100644 src/audit/cli-adapters/shared.ts
create mode 100644 src/audit/detectors/find-from-root.ts
create mode 100644 src/audit/detectors/git-commit-no-verify.ts
create mode 100644 src/audit/detectors/index.ts
create mode 100644 src/audit/detectors/prefer-edit-over-read-cat.ts
create mode 100644 src/audit/detectors/prefer-edit-over-sed-awk.ts
create mode 100644 src/audit/detectors/prefer-write-over-heredoc.ts
create mode 100644 src/audit/detectors/redundant-cd-cwd.ts
create mode 100644 src/audit/detectors/reread-after-edit.ts
create mode 100644 src/audit/detectors/sleep-polling-loop.ts
create mode 100644 src/audit/index.ts
create mode 100644 src/audit/replay.ts
create mode 100644 src/audit/report.ts
create mode 100644 src/audit/types.ts
create mode 100644 src/hooks/tool-name-canonicalize.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5194af3..196565f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
## 0.0.11-beta.2 — 2026-05-21
+### Features
+- Add `failproofai audit` command — retrospectively scan past agent CLI transcripts (Claude, Codex, Copilot, Cursor, OpenCode, Pi, Gemini) and count how many times the agent did wasteful or risky things failproofai is built to stop. Replays every transcript's tool-use events through the existing 39 builtin policies and through 8 new audit-only detectors (`redundant-cd-cwd`, `prefer-edit-over-read-cat`, `prefer-edit-over-sed-awk`, `prefer-write-over-heredoc`, `sleep-polling-loop`, `find-from-root`, `git-commit-no-verify`, `reread-after-edit`) that catch behaviors not yet covered by runtime policies. Prints an ANSI table to stdout and writes a sectioned markdown report to `./failproofai-audit.md`. Honors `--cli`, `--project`, `--since`, `--policy`, `--limit`, `--show-examples`, `--report`, `--no-report`, `--json`, `--no-cache`. Per-transcript results cached at `~/.failproofai/cache/audit/` keyed by `(mtime, size, engineVersion, detectorVersion)` so the cache invalidates automatically when policy or detector code changes. Refactored per-CLI tool-name + tool-input canonicalization out of `src/hooks/handler.ts` into `src/hooks/tool-name-canonicalize.ts` so the audit replay and live handler share one implementation.
+
### Breaking
- Remove the undocumented cloud auth + event relay subsystem ahead of a from-scratch redesign. Deletes `src/auth/` (OAuth 2.0 device-flow login against `api.befailproof.ai`, `~/.failproofai/auth.json` token store) and `src/relay/` (WebSocket event relay daemon, sanitized JSONL queue at `~/.failproofai/cache/server-queue/`, PID tracking). Strips the `failproofai login` / `logout` / `whoami` / `relay start|stop|status` / `sync` subcommands and the internal `--relay-daemon` mode from `bin/failproofai.mjs`, along with their `--help` entries and "did you mean" suggestions. Removes the fire-and-forget `appendToServerQueue` + `ensureRelayRunning` calls from `src/hooks/handler.ts` so hook evaluation no longer enqueues events or lazy-spawns a daemon. The whole subsystem had zero references in `README.md`, `docs/`, `examples/`, or `__tests__/`, and only had internal cross-imports — `tsc`, `eslint`, `vitest` (1623 tests), and the `bun run build` bundles all stay green. Users who ran `failproofai login` should also wipe `~/.failproofai/{auth.json,cache/server-queue,relay.pid}` and stop any running relay daemon by hand; new auth/cloud surface will land in a follow-up.
diff --git a/__tests__/audit/detectors.test.ts b/__tests__/audit/detectors.test.ts
new file mode 100644
index 0000000..0df1aef
--- /dev/null
+++ b/__tests__/audit/detectors.test.ts
@@ -0,0 +1,166 @@
+// @vitest-environment node
+import { describe, it, expect } from "vitest";
+import type { NormalizedToolEvent } from "../../src/audit/types";
+import { redundantCdCwd } from "../../src/audit/detectors/redundant-cd-cwd";
+import { preferEditOverReadCat } from "../../src/audit/detectors/prefer-edit-over-read-cat";
+import { preferEditOverSedAwk } from "../../src/audit/detectors/prefer-edit-over-sed-awk";
+import { preferWriteOverHeredoc } from "../../src/audit/detectors/prefer-write-over-heredoc";
+import { sleepPollingLoop } from "../../src/audit/detectors/sleep-polling-loop";
+import { findFromRoot } from "../../src/audit/detectors/find-from-root";
+import { gitCommitNoVerify } from "../../src/audit/detectors/git-commit-no-verify";
+import { rereadAfterEdit } from "../../src/audit/detectors/reread-after-edit";
+
+function bash(cmd: string, cwd = "/home/u/proj"): NormalizedToolEvent {
+ return {
+ cli: "claude",
+ sessionId: "sess-1",
+ transcriptPath: "/tmp/t.jsonl",
+ cwd,
+ timestamp: "2026-05-21T00:00:00.000Z",
+ toolName: "Bash",
+ rawToolName: "Bash",
+ toolInput: { command: cmd },
+ };
+}
+
+function tool(name: string, input: Record): NormalizedToolEvent {
+ return {
+ cli: "claude",
+ sessionId: "sess-1",
+ transcriptPath: "/tmp/t.jsonl",
+ cwd: "/home/u/proj",
+ timestamp: "2026-05-21T00:00:00.000Z",
+ toolName: name,
+ rawToolName: name,
+ toolInput: input,
+ };
+}
+
+describe("redundant-cd-cwd", () => {
+ it("matches `cd && cmd`", () => {
+ const hit = redundantCdCwd.detect(bash("cd /home/u/proj && pnpm test"), {});
+ expect(hit?.example).toContain("cd /home/u/proj && pnpm test");
+ });
+ it("does not match cd to a different path", () => {
+ expect(redundantCdCwd.detect(bash("cd /tmp && ls"), {})).toBeNull();
+ });
+ it("does not match bare cmd without cd", () => {
+ expect(redundantCdCwd.detect(bash("pnpm test"), {})).toBeNull();
+ });
+});
+
+describe("prefer-edit-over-read-cat", () => {
+ it("matches `cat foo.ts`", () => {
+ expect(preferEditOverReadCat.detect(bash("cat src/foo.ts"), {})?.example).toBe("cat src/foo.ts");
+ });
+ it("matches `head -50 bar.py`", () => {
+ expect(preferEditOverReadCat.detect(bash("head -50 bar.py"), {})).not.toBeNull();
+ });
+ it("does not match `cat .env`", () => {
+ expect(preferEditOverReadCat.detect(bash("cat .env"), {})).toBeNull();
+ });
+ it("does not match piped `cat`", () => {
+ expect(preferEditOverReadCat.detect(bash("cat foo.ts | wc -l"), {})).toBeNull();
+ });
+ it("does not match `cat foo.txt > out`", () => {
+ expect(preferEditOverReadCat.detect(bash("cat foo.ts > /tmp/out"), {})).toBeNull();
+ });
+ it("does not match `cat unknownext`", () => {
+ expect(preferEditOverReadCat.detect(bash("cat README"), {})).toBeNull();
+ });
+});
+
+describe("prefer-edit-over-sed-awk", () => {
+ it("matches `sed -i`", () => {
+ expect(preferEditOverSedAwk.detect(bash("sed -i 's/foo/bar/g' file.ts"), {})).not.toBeNull();
+ });
+ it("matches `awk '...' file > out`", () => {
+ expect(preferEditOverSedAwk.detect(bash("awk '{print $1}' file > out"), {})).not.toBeNull();
+ });
+ it("does not match `sed 's/x/y/'` without -i", () => {
+ expect(preferEditOverSedAwk.detect(bash("echo x | sed 's/x/y/'"), {})).toBeNull();
+ });
+});
+
+describe("prefer-write-over-heredoc", () => {
+ it("matches `cat < file`", () => {
+ expect(preferWriteOverHeredoc.detect(bash("cat <<'EOF' > out.md\nhello\nEOF"), {})).not.toBeNull();
+ });
+ it("does not match `cat < {
+ expect(
+ preferWriteOverHeredoc.detect(bash(`git commit -m "$(cat <<'EOF'\nfeat\nEOF\n)"`), {}),
+ ).toBeNull();
+ });
+ it("matches `echo \"multi\\nline\" > file`", () => {
+ expect(preferWriteOverHeredoc.detect(bash('echo "a\nb" > out'), {})).not.toBeNull();
+ });
+});
+
+describe("sleep-polling-loop", () => {
+ it("matches `sleep 60`", () => {
+ expect(sleepPollingLoop.detect(bash("sleep 60"), {})).not.toBeNull();
+ });
+ it("matches `sleep 5m`", () => {
+ expect(sleepPollingLoop.detect(bash("sleep 5m"), {})).not.toBeNull();
+ });
+ it("matches while-sleep loop", () => {
+ expect(
+ sleepPollingLoop.detect(bash("while true; do echo x; sleep 5; done"), {}),
+ ).not.toBeNull();
+ });
+ it("does not match `sleep 1`", () => {
+ expect(sleepPollingLoop.detect(bash("sleep 1"), {})).toBeNull();
+ });
+});
+
+describe("find-from-root", () => {
+ it("matches `find /`", () => {
+ expect(findFromRoot.detect(bash("find / -name '*.ts'"), {})).not.toBeNull();
+ });
+ it("matches `find /home`", () => {
+ expect(findFromRoot.detect(bash("find /home -name foo"), {})).not.toBeNull();
+ });
+ it("does not match `find . -name foo`", () => {
+ expect(findFromRoot.detect(bash("find . -name foo"), {})).toBeNull();
+ });
+ it("does not match `find src`", () => {
+ expect(findFromRoot.detect(bash("find src -name foo"), {})).toBeNull();
+ });
+});
+
+describe("git-commit-no-verify", () => {
+ it("matches `git commit --no-verify`", () => {
+ expect(gitCommitNoVerify.detect(bash("git commit --no-verify -m foo"), {})).not.toBeNull();
+ });
+ it("matches short `git commit -n`", () => {
+ expect(gitCommitNoVerify.detect(bash("git commit -n -m foo"), {})).not.toBeNull();
+ });
+ it("does not match plain `git commit -m`", () => {
+ expect(gitCommitNoVerify.detect(bash("git commit -m foo"), {})).toBeNull();
+ });
+});
+
+describe("reread-after-edit", () => {
+ it("matches Read of file just Edited", () => {
+ const state = {};
+ expect(rereadAfterEdit.detect(tool("Edit", { file_path: "/a/b.ts" }), state)).toBeNull();
+ const hit = rereadAfterEdit.detect(tool("Read", { file_path: "/a/b.ts" }), state);
+ expect(hit?.example).toContain("/a/b.ts");
+ });
+ it("matches Read after Write", () => {
+ const state = {};
+ rereadAfterEdit.detect(tool("Write", { file_path: "/a/b.ts" }), state);
+ expect(rereadAfterEdit.detect(tool("Read", { file_path: "/a/b.ts" }), state)).not.toBeNull();
+ });
+ it("does not match Read of a different file", () => {
+ const state = {};
+ rereadAfterEdit.detect(tool("Edit", { file_path: "/a/b.ts" }), state);
+ expect(rereadAfterEdit.detect(tool("Read", { file_path: "/a/other.ts" }), state)).toBeNull();
+ });
+ it("decays after window of 5 tool calls", () => {
+ const state = {};
+ rereadAfterEdit.detect(tool("Edit", { file_path: "/a/b.ts" }), state);
+ for (let i = 0; i < 6; i++) rereadAfterEdit.detect(tool("Bash", { command: "x" }), state);
+ expect(rereadAfterEdit.detect(tool("Read", { file_path: "/a/b.ts" }), state)).toBeNull();
+ });
+});
diff --git a/__tests__/audit/index.test.ts b/__tests__/audit/index.test.ts
new file mode 100644
index 0000000..9a05c0e
--- /dev/null
+++ b/__tests__/audit/index.test.ts
@@ -0,0 +1,87 @@
+// @vitest-environment node
+import { describe, it, expect, beforeAll, afterAll } from "vitest";
+import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from "node:fs";
+import { tmpdir } from "node:os";
+import { join } from "node:path";
+import { runAudit } from "../../src/audit";
+import { resetReplay } from "../../src/audit/replay";
+
+/**
+ * Builds a minimal Claude JSONL transcript with three tool-use events:
+ * 1. Bash(env) — should trigger protect-env-vars (builtin)
+ * 2. Bash(cd && pnpm test) — should trigger redundant-cd-cwd (detector)
+ * 3. Edit(file_path) then Read(file_path) — should trigger reread-after-edit
+ */
+function buildFixtureTranscript(cwd: string, sessionId: string): string {
+ const lines: object[] = [];
+ let prevUuid: string | null = null;
+ function pushAssistantToolUse(name: string, input: Record) {
+ const uuid = `uuid-${lines.length}`;
+ lines.push({
+ type: "assistant",
+ uuid,
+ parentUuid: prevUuid,
+ sessionId,
+ cwd,
+ timestamp: new Date(2026, 4, 21, lines.length).toISOString(),
+ message: {
+ role: "assistant",
+ content: [{ type: "tool_use", id: `tu-${lines.length}`, name, input }],
+ },
+ });
+ prevUuid = uuid;
+ }
+ pushAssistantToolUse("Bash", { command: "env" });
+ pushAssistantToolUse("Bash", { command: `cd ${cwd} && pnpm test` });
+ pushAssistantToolUse("Edit", { file_path: `${cwd}/foo.ts`, old_string: "a", new_string: "b" });
+ pushAssistantToolUse("Read", { file_path: `${cwd}/foo.ts` });
+ return lines.map((l) => JSON.stringify(l)).join("\n");
+}
+
+describe("runAudit() end-to-end on a fixture transcript", () => {
+ let tmpRoot: string;
+ let origEnv: string | undefined;
+
+ beforeAll(() => {
+ tmpRoot = mkdtempSync(join(tmpdir(), "failproofai-audit-fixture-"));
+ origEnv = process.env.CLAUDE_PROJECTS_PATH;
+ process.env.CLAUDE_PROJECTS_PATH = tmpRoot;
+
+ // Create one project with one transcript.
+ const projectDir = join(tmpRoot, "-tmp-myproj");
+ mkdirSync(projectDir, { recursive: true });
+ const sessionId = "11111111-2222-3333-4444-555555555555";
+ const transcriptPath = join(projectDir, `${sessionId}.jsonl`);
+ const transcriptCwd = "/tmp/myproj";
+ writeFileSync(transcriptPath, buildFixtureTranscript(transcriptCwd, sessionId));
+ resetReplay();
+ });
+
+ afterAll(() => {
+ if (origEnv) process.env.CLAUDE_PROJECTS_PATH = origEnv;
+ else delete process.env.CLAUDE_PROJECTS_PATH;
+ rmSync(tmpRoot, { recursive: true, force: true });
+ });
+
+ it("counts builtin + detector hits across the fixture transcript", async () => {
+ const result = await runAudit({ clis: ["claude"], noCache: true, noReport: true });
+ expect(result.transcripts.scanned).toBeGreaterThanOrEqual(1);
+
+ const names = result.results.map((r) => r.name);
+ // Builtin policy hit.
+ expect(names.some((n) => n.includes("protect-env-vars"))).toBe(true);
+ // Audit-only detector hits.
+ expect(names).toContain("redundant-cd-cwd");
+ expect(names).toContain("reread-after-edit");
+ });
+
+ it("filters by --policy", async () => {
+ const result = await runAudit({
+ clis: ["claude"],
+ noCache: true,
+ noReport: true,
+ policies: ["redundant-cd-cwd"],
+ });
+ expect(result.results.map((r) => r.name)).toEqual(["redundant-cd-cwd"]);
+ });
+});
diff --git a/__tests__/audit/replay.test.ts b/__tests__/audit/replay.test.ts
new file mode 100644
index 0000000..18e7dfe
--- /dev/null
+++ b/__tests__/audit/replay.test.ts
@@ -0,0 +1,52 @@
+// @vitest-environment node
+import { describe, it, expect, beforeEach } from "vitest";
+import { resetReplay, replayEvent } from "../../src/audit/replay";
+import type { NormalizedToolEvent } from "../../src/audit/types";
+
+function bash(command: string): NormalizedToolEvent {
+ return {
+ cli: "claude",
+ sessionId: "sess-1",
+ transcriptPath: "/tmp/t.jsonl",
+ cwd: "/home/u/proj",
+ timestamp: "2026-05-21T00:00:00.000Z",
+ toolName: "Bash",
+ rawToolName: "Bash",
+ toolInput: { command },
+ };
+}
+
+describe("replay engine", () => {
+ beforeEach(() => {
+ resetReplay();
+ });
+
+ it("triggers protect-env-vars on `env`", async () => {
+ const hits = await replayEvent(bash("env"));
+ const names = hits.map((h) => h.policyName);
+ expect(names.some((n) => n.includes("protect-env-vars"))).toBe(true);
+ });
+
+ it("triggers block-force-push on `git push --force` to a non-protected branch", async () => {
+ // Push to `feature` (not main/master) so block-push-master doesn't
+ // short-circuit before block-force-push gets a chance to fire.
+ const hits = await replayEvent(bash("git push --force origin feature"));
+ const names = hits.map((h) => h.policyName);
+ expect(names.some((n) => n.includes("block-force-push"))).toBe(true);
+ });
+
+ it("does not fire on a plain `ls`", async () => {
+ const hits = await replayEvent(bash("ls -la"));
+ expect(hits.filter((h) => h.decision === "deny")).toHaveLength(0);
+ });
+
+ it("synthesizes PostToolUse when toolResultText is set", async () => {
+ // Fake JWT shape — three dot-separated base64 chunks — to trigger
+ // sanitize-jwt on PostToolUse without using a real-looking API-key shape.
+ const fakeJwt = ["eyJhbGciOiJIUzI1NiJ9", "eyJzdWIiOiJ0ZXN0In0", "test-sig-xyz"].join(".");
+ const event = bash("echo token");
+ event.toolResultText = `Authorization: Bearer ${fakeJwt}`;
+ const hits = await replayEvent(event);
+ expect(hits.some((h) => h.eventType === "PostToolUse")).toBe(true);
+ });
+});
diff --git a/bin/failproofai.mjs b/bin/failproofai.mjs
index 019a354..0c4c46a 100755
--- a/bin/failproofai.mjs
+++ b/bin/failproofai.mjs
@@ -81,7 +81,7 @@ if (hookIdx >= 0) {
*/
async function runCli() {
// --help / -h (only when not inside a subcommand that handles its own --help)
- const SUBCOMMANDS = ["policies"];
+ const SUBCOMMANDS = ["policies", "audit"];
if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS.includes(args[0])) {
const extraArgs = args.filter((a) => a !== "--help" && a !== "-h");
if (extraArgs.length > 0) {
@@ -118,6 +118,23 @@ COMMANDS
policies --help, -h Show this help for the policies command
+ audit Scan past agent CLI transcripts and count
+ "stupid behaviors" (env-var checks, force
+ pushes, redundant cd , sleep loops,
+ etc.) per policy / audit detector.
+ --cli claude|codex|copilot|cursor|opencode|pi|gemini
+ Restrict to one or more CLIs (default: all).
+ --project Restrict to one cwd (repeatable).
+ --since 7d|2026-04-01 Only sessions whose mtime is within window.
+ --policy Restrict to one policy/detector (repeatable).
+ --limit N Top-N rows in the table (default 20).
+ --show-examples Include one example per row.
+ --report Markdown report path (default ./failproofai-audit.md).
+ --no-report Skip writing the markdown file.
+ --json Print JSON to stdout instead of the table.
+ --no-cache Bypass the per-transcript cache.
+ audit --help, -h Show this help for the audit command
+
--version, -v Print version and exit
--help, -h Show this help message
@@ -409,6 +426,141 @@ EXAMPLES
process.exit(0);
}
+ // audit — scan past transcripts for "stupid behaviors" caught by builtin
+ // policies + a set of audit-only detectors.
+ if (args[0] === "audit") {
+ const subArgs = args.slice(1);
+
+ if (subArgs.includes("--help") || subArgs.includes("-h")) {
+ console.log(`
+failproofai audit — scan past agent transcripts for stupid behaviors
+
+USAGE
+ failproofai audit [options]
+
+OPTIONS
+ --cli claude|codex|copilot|cursor|opencode|pi|gemini
+ Restrict to one or more CLIs (space-separated or repeated).
+ Default: scan all 7.
+ --project Restrict to sessions whose cwd matches this path. Repeatable.
+ --since 7d|30d|2026-04-01 Only sessions whose mtime is within window.
+ --policy Restrict to one policy/detector name. Repeatable.
+ --limit N Top-N rows in the table (default 20).
+ --show-examples Include one example command per row in the table.
+ --report Markdown report path (default: ./failproofai-audit.md).
+ --no-report Skip writing the markdown report.
+ --json Print JSON to stdout instead of the table.
+ --no-cache Bypass the per-transcript result cache.
+ --help, -h Show this help
+
+EXAMPLES
+ failproofai audit
+ failproofai audit --cli claude --since 30d
+ failproofai audit --policy protect-env-vars --policy block-force-push
+ failproofai audit --json > audit.json
+ failproofai audit --project /home/me/myrepo --show-examples
+`.trimStart());
+ process.exit(0);
+ }
+
+ const VALID_CLIS = new Set(["claude", "codex", "copilot", "cursor", "opencode", "pi", "gemini"]);
+ /** Consume one or more values for a flag like `--cli a b c`, stopping at
+ * the next flag or an unknown token (when an allow-set is supplied). */
+ function collectMulti(flag, allowSet) {
+ const out = [];
+ const consumed = new Set();
+ for (let i = 0; i < subArgs.length; i++) {
+ if (subArgs[i] !== flag) continue;
+ for (let j = i + 1; j < subArgs.length; j++) {
+ const v = subArgs[j];
+ if (v.startsWith("-")) break;
+ if (allowSet && !allowSet.has(v)) break;
+ out.push(v);
+ consumed.add(j);
+ }
+ consumed.add(i);
+ }
+ return { values: out, consumed };
+ }
+ /** Take the single value following a one-shot flag like `--since 7d`. */
+ function takeOne(flag) {
+ const i = subArgs.indexOf(flag);
+ if (i < 0) return { value: undefined, consumed: new Set() };
+ const v = subArgs[i + 1];
+ if (v === undefined || v.startsWith("-")) {
+ throw new CliError(`Missing value for ${flag}`);
+ }
+ return { value: v, consumed: new Set([i, i + 1]) };
+ }
+
+ const allConsumed = new Set();
+ const cliRes = collectMulti("--cli", VALID_CLIS);
+ cliRes.consumed.forEach((i) => allConsumed.add(i));
+ const projectRes = collectMulti("--project", null);
+ projectRes.consumed.forEach((i) => allConsumed.add(i));
+ const policyRes = collectMulti("--policy", null);
+ policyRes.consumed.forEach((i) => allConsumed.add(i));
+ const sinceRes = takeOne("--since");
+ sinceRes.consumed.forEach((i) => allConsumed.add(i));
+ const limitRes = takeOne("--limit");
+ limitRes.consumed.forEach((i) => allConsumed.add(i));
+ const reportRes = takeOne("--report");
+ reportRes.consumed.forEach((i) => allConsumed.add(i));
+
+ const showExamples = subArgs.includes("--show-examples");
+ if (showExamples) allConsumed.add(subArgs.indexOf("--show-examples"));
+ const noReport = subArgs.includes("--no-report");
+ if (noReport) allConsumed.add(subArgs.indexOf("--no-report"));
+ const jsonOut = subArgs.includes("--json");
+ if (jsonOut) allConsumed.add(subArgs.indexOf("--json"));
+ const noCache = subArgs.includes("--no-cache");
+ if (noCache) allConsumed.add(subArgs.indexOf("--no-cache"));
+
+ // Reject unknown flags / positional args.
+ for (let i = 0; i < subArgs.length; i++) {
+ if (allConsumed.has(i)) continue;
+ const arg = subArgs[i];
+ throw new CliError(
+ `Unexpected argument: ${arg}\nRun \`failproofai audit --help\` for usage.`,
+ );
+ }
+
+ const opts = {
+ clis: cliRes.values.length > 0 ? cliRes.values : undefined,
+ projects: projectRes.values.length > 0 ? projectRes.values : undefined,
+ policies: policyRes.values.length > 0 ? policyRes.values : undefined,
+ since: sinceRes.value,
+ limit: limitRes.value !== undefined ? parseInt(limitRes.value, 10) : undefined,
+ showExamples,
+ reportPath: reportRes.value ?? "./failproofai-audit.md",
+ noReport: noReport || jsonOut, // --json implies --no-report unless explicit --report
+ json: jsonOut,
+ noCache,
+ };
+ // Re-enable report when --report was passed alongside --json.
+ if (jsonOut && reportRes.value) opts.noReport = false;
+
+ const { runAudit } = await import("../src/audit");
+ const { formatText, formatJson, formatMarkdown } = await import("../src/audit/report");
+ const result = await runAudit(opts);
+
+ if (jsonOut) {
+ process.stdout.write(formatJson(result) + "\n");
+ } else {
+ process.stdout.write(formatText(result, opts));
+ }
+
+ if (!opts.noReport) {
+ const { writeFileSync } = await import("node:fs");
+ const { resolve } = await import("node:path");
+ const reportPath = resolve(opts.reportPath);
+ writeFileSync(reportPath, formatMarkdown(result), "utf-8");
+ if (!jsonOut) process.stdout.write(`\nReport written: ${reportPath}\n`);
+ }
+
+ process.exit(0);
+ }
+
// Unknown flag guard — must appear after all known-flag branches
const knownFlags = ["--version", "-v", "--help", "-h", "--hook"];
const unknownFlag = args.find(a => a.startsWith("-") && !knownFlags.includes(a));
@@ -427,7 +579,7 @@ EXAMPLES
return dp[m][n];
}
- const primary = ["--version", "--help", "--hook", "policies"];
+ const primary = ["--version", "--help", "--hook", "policies", "audit"];
const closest = primary.reduce((best, flag) => {
const dist = levenshtein(unknownFlag, flag);
return dist < best.dist ? { flag, dist } : best;
diff --git a/lib/claude-sessions.ts b/lib/claude-sessions.ts
new file mode 100644
index 0000000..5886eee
--- /dev/null
+++ b/lib/claude-sessions.ts
@@ -0,0 +1,181 @@
+/**
+ * Claude Code session transcript discovery helpers.
+ *
+ * Claude stores transcripts at:
+ * //.jsonl
+ *
+ * Subagent transcripts (when a session spawned subagents) live alongside:
+ * ///subagents/.jsonl
+ *
+ * The parser for these files lives in `lib/log-entries.ts` (`parseLogContent`,
+ * `parseSessionLog`). This module exposes discovery only — the audit pipeline
+ * and any future Claude-specific tool walk the directory layout via these
+ * helpers instead of re-implementing the path conventions.
+ *
+ * Mirrors the shape of `lib/cursor-sessions.ts` for parity across CLIs.
+ */
+import { readdirSync, statSync, existsSync } from "node:fs";
+import { join, basename } from "node:path";
+import { getClaudeProjectsPath, decodeFolderName } from "./paths";
+
+const UUID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
+
+export interface ClaudeProjectFolder {
+ /** Encoded folder name on disk (e.g. "-home-user-project"). */
+ name: string;
+ /** Decoded filesystem path (e.g. "/home/user/project"). */
+ cwd: string;
+ /** Absolute path of the encoded folder. */
+ path: string;
+}
+
+export interface ClaudeTranscriptFile {
+ projectName: string;
+ /** Decoded cwd of the project. */
+ cwd: string;
+ sessionId: string;
+ /** Absolute path of `.jsonl`. */
+ transcriptPath: string;
+ mtimeMs: number;
+ sizeBytes: number;
+ /** True when this is a subagent transcript spawned from a parent session. */
+ isSubagent: boolean;
+}
+
+/** Returns the Claude projects root, honoring the CLAUDE_PROJECTS_PATH env var. */
+export function getClaudeProjectsRoot(): string {
+ return getClaudeProjectsPath();
+}
+
+/** Lists all Claude project folders (one per encoded cwd). Returns [] if the
+ * projects root doesn't exist. Filenames that don't look like Claude project
+ * encodings are still included — encoding is permissive. */
+export function listClaudeProjects(): ClaudeProjectFolder[] {
+ const root = getClaudeProjectsRoot();
+ let entries: import("node:fs").Dirent[];
+ try {
+ entries = readdirSync(root, { withFileTypes: true });
+ } catch {
+ return [];
+ }
+ return entries
+ .filter((e) => e.isDirectory())
+ .map((e) => ({
+ name: e.name,
+ cwd: decodeFolderName(e.name),
+ path: join(root, e.name),
+ }));
+}
+
+/** Lists every JSONL transcript under one Claude project folder, including
+ * subagent transcripts under `/subagents/`. Returns [] on missing
+ * or unreadable paths. */
+export function listClaudeTranscripts(project: ClaudeProjectFolder): ClaudeTranscriptFile[] {
+ const out: ClaudeTranscriptFile[] = [];
+ let entries: import("node:fs").Dirent[];
+ try {
+ entries = readdirSync(project.path, { withFileTypes: true });
+ } catch {
+ return out;
+ }
+
+ for (const entry of entries) {
+ if (entry.isFile() && entry.name.endsWith(".jsonl")) {
+ const sessionId = entry.name.slice(0, -".jsonl".length);
+ if (!UUID_RE.test(sessionId)) continue;
+ const transcriptPath = join(project.path, entry.name);
+ try {
+ const s = statSync(transcriptPath);
+ out.push({
+ projectName: project.name,
+ cwd: project.cwd,
+ sessionId,
+ transcriptPath,
+ mtimeMs: s.mtimeMs,
+ sizeBytes: s.size,
+ isSubagent: false,
+ });
+ } catch {
+ // unreadable — skip
+ }
+ } else if (entry.isDirectory() && UUID_RE.test(entry.name)) {
+ // Subagent transcripts at /subagents/.jsonl
+ const subDir = join(project.path, entry.name, "subagents");
+ if (!existsSync(subDir)) continue;
+ let subEntries: import("node:fs").Dirent[];
+ try {
+ subEntries = readdirSync(subDir, { withFileTypes: true });
+ } catch {
+ continue;
+ }
+ for (const sub of subEntries) {
+ if (!sub.isFile() || !sub.name.endsWith(".jsonl")) continue;
+ const agentId = sub.name.slice(0, -".jsonl".length);
+ const transcriptPath = join(subDir, sub.name);
+ try {
+ const s = statSync(transcriptPath);
+ out.push({
+ projectName: project.name,
+ cwd: project.cwd,
+ sessionId: agentId,
+ transcriptPath,
+ mtimeMs: s.mtimeMs,
+ sizeBytes: s.size,
+ isSubagent: true,
+ });
+ } catch {
+ // unreadable — skip
+ }
+ }
+ }
+ }
+
+ return out;
+}
+
+/** Convenience: locate one Claude transcript file by session ID across all
+ * project folders. Returns null if not found. */
+export function findClaudeTranscript(sessionId: string): string | null {
+ if (!UUID_RE.test(sessionId)) return null;
+ for (const project of listClaudeProjects()) {
+ const candidate = join(project.path, `${sessionId}.jsonl`);
+ if (existsSync(candidate)) return candidate;
+ // Subagent fallback
+ let entries: import("node:fs").Dirent[];
+ try {
+ entries = readdirSync(project.path, { withFileTypes: true });
+ } catch {
+ continue;
+ }
+ for (const entry of entries) {
+ if (!entry.isDirectory() || !UUID_RE.test(entry.name)) continue;
+ const subCandidate = join(project.path, entry.name, "subagents", `${sessionId}.jsonl`);
+ if (existsSync(subCandidate)) return subCandidate;
+ }
+ }
+ return null;
+}
+
+/** For tests: list all session IDs across all projects. */
+export function _listAllSessionIds(): string[] {
+ const ids: string[] = [];
+ for (const project of listClaudeProjects()) {
+ for (const t of listClaudeTranscripts(project)) {
+ ids.push(t.sessionId);
+ }
+ }
+ return ids;
+}
+
+/** For tests: stat one transcript file. */
+export function _statTranscript(path: string): { mtimeMs: number; sizeBytes: number } | null {
+ try {
+ const s = statSync(path);
+ return { mtimeMs: s.mtimeMs, sizeBytes: s.size };
+ } catch {
+ return null;
+ }
+}
+
+/** Re-export for callers that want to construct paths from a basename. */
+export { basename };
diff --git a/src/audit/cache.ts b/src/audit/cache.ts
new file mode 100644
index 0000000..e6e18a9
--- /dev/null
+++ b/src/audit/cache.ts
@@ -0,0 +1,110 @@
+/**
+ * Per-transcript audit-result cache.
+ *
+ * Stored at `~/.failproofai/cache/audit/.json` with
+ * mode 0600. Keyed by (mtime, size, engineVersion, detectorVersion) so the
+ * cache invalidates automatically when either the transcript or the policy /
+ * detector code changes.
+ *
+ * Skipped for transcripts whose `sizeBytes === 0` (currently: OpenCode, whose
+ * sessions live in a SQLite DB rather than a file with a stable mtime).
+ */
+import { createHash } from "node:crypto";
+import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from "node:fs";
+import { join } from "node:path";
+import { homedir } from "node:os";
+import { BUILTIN_POLICIES } from "../hooks/builtin-policies";
+import { AUDIT_DETECTORS } from "./detectors";
+import type { TranscriptAuditResult } from "./types";
+
+let cachedEngineVersion: string | null = null;
+let cachedDetectorVersion: string | null = null;
+
+/** Hash of every builtin policy's name + function body. Changes when policy
+ * code changes, invalidating downstream caches. */
+function getEngineVersion(): string {
+ if (cachedEngineVersion) return cachedEngineVersion;
+ const blob = BUILTIN_POLICIES
+ .map((p) => `${p.name}|${p.fn.toString()}`)
+ .sort()
+ .join("\n");
+ cachedEngineVersion = createHash("sha1").update(blob).digest("hex").slice(0, 16);
+ return cachedEngineVersion;
+}
+
+/** Same for audit detectors. */
+function getDetectorVersion(): string {
+ if (cachedDetectorVersion) return cachedDetectorVersion;
+ const blob = AUDIT_DETECTORS
+ .map((d) => `${d.name}|${d.detect.toString()}`)
+ .sort()
+ .join("\n");
+ cachedDetectorVersion = createHash("sha1").update(blob).digest("hex").slice(0, 16);
+ return cachedDetectorVersion;
+}
+
+function getCachePathFor(transcriptPath: string): string {
+ const root = join(homedir(), ".failproofai", "cache", "audit");
+ const key = createHash("sha1").update(transcriptPath).digest("hex");
+ return join(root, `${key}.json`);
+}
+
+interface CacheEntry {
+ mtimeMs: number;
+ sizeBytes: number;
+ engineVersion: string;
+ detectorVersion: string;
+ result: TranscriptAuditResult;
+}
+
+export function readCachedTranscriptResult(
+ transcriptPath: string,
+ mtimeMs: number,
+ sizeBytes: number,
+): TranscriptAuditResult | null {
+ if (sizeBytes === 0) return null; // OpenCode and other DB-backed sources
+ const cachePath = getCachePathFor(transcriptPath);
+ if (!existsSync(cachePath)) return null;
+ try {
+ const raw = readFileSync(cachePath, "utf-8");
+ const entry = JSON.parse(raw) as CacheEntry;
+ if (entry.mtimeMs !== mtimeMs) return null;
+ if (entry.sizeBytes !== sizeBytes) return null;
+ if (entry.engineVersion !== getEngineVersion()) return null;
+ if (entry.detectorVersion !== getDetectorVersion()) return null;
+ return entry.result;
+ } catch {
+ return null;
+ }
+}
+
+export function writeCachedTranscriptResult(
+ transcriptPath: string,
+ mtimeMs: number,
+ sizeBytes: number,
+ result: TranscriptAuditResult,
+): void {
+ if (sizeBytes === 0) return;
+ const cachePath = getCachePathFor(transcriptPath);
+ try {
+ mkdirSync(join(homedir(), ".failproofai", "cache", "audit"), { recursive: true });
+ const entry: CacheEntry = {
+ mtimeMs,
+ sizeBytes,
+ engineVersion: getEngineVersion(),
+ detectorVersion: getDetectorVersion(),
+ result,
+ };
+ writeFileSync(cachePath, JSON.stringify(entry), "utf-8");
+ try { chmodSync(cachePath, 0o600); } catch { /* best-effort on POSIX */ }
+ } catch {
+ // Cache writes are best-effort — never let a cache error kill the audit.
+ }
+}
+
+/** Test helper: reset memoized version hashes (so they recompute after a
+ * detector or policy is monkey-patched in a test). */
+export function _resetVersionCache(): void {
+ cachedEngineVersion = null;
+ cachedDetectorVersion = null;
+}
diff --git a/src/audit/cli-adapters/claude.ts b/src/audit/cli-adapters/claude.ts
new file mode 100644
index 0000000..29a880b
--- /dev/null
+++ b/src/audit/cli-adapters/claude.ts
@@ -0,0 +1,97 @@
+/**
+ * Claude Code transcript adapter.
+ *
+ * Discovers ~/.claude/projects//.jsonl (and any subagent
+ * transcripts under /subagents/) via lib/claude-sessions.ts, then
+ * parses each via lib/log-entries.ts.
+ */
+import { readFile } from "node:fs/promises";
+import {
+ listClaudeProjects,
+ listClaudeTranscripts,
+ type ClaudeTranscriptFile,
+} from "../../../lib/claude-sessions";
+import { parseLogContent, type LogSource } from "../../../lib/log-entries";
+import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
+import { logEntriesToEvents } from "./shared";
+
+export interface ListOpts {
+ /** Restrict to sessions whose decoded cwd matches one of these paths. */
+ projects?: string[];
+ /** Filter on transcript mtime — only return if mtimeMs >= sinceMs. */
+ sinceMs?: number;
+}
+
+export async function listClaudeTranscriptMetadata(
+ opts: ListOpts = {},
+): Promise {
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
+ const sinceMs = opts.sinceMs ?? 0;
+ const out: TranscriptMetadata[] = [];
+
+ for (const project of listClaudeProjects()) {
+ if (projectFilter && !projectFilter.has(project.cwd)) continue;
+ let transcripts: ClaudeTranscriptFile[];
+ try {
+ transcripts = listClaudeTranscripts(project);
+ } catch {
+ continue;
+ }
+ for (const t of transcripts) {
+ if (t.mtimeMs < sinceMs) continue;
+ out.push({
+ cli: "claude",
+ projectName: project.name,
+ sessionId: t.sessionId,
+ transcriptPath: t.transcriptPath,
+ mtimeMs: t.mtimeMs,
+ sizeBytes: t.sizeBytes,
+ });
+ }
+ }
+
+ return out;
+}
+
+export async function streamClaudeEvents(
+ meta: TranscriptMetadata,
+): Promise {
+ let content: string;
+ try {
+ content = await readFile(meta.transcriptPath, "utf-8");
+ } catch {
+ return [];
+ }
+
+ const source: LogSource = "session";
+ let entries;
+ try {
+ entries = await parseLogContent(content, source);
+ } catch {
+ return [];
+ }
+
+ // Best-effort cwd resolution: the JSONL lines carry `cwd` directly on each
+ // record (verified live — see plan exploration notes). Pull the first one
+ // we find rather than re-decoding the folder name (which is lossy on POSIX).
+ let cwd = "";
+ for (const line of content.split("\n", 50)) {
+ if (!line.trim()) continue;
+ try {
+ const parsed = JSON.parse(line) as { cwd?: unknown };
+ if (typeof parsed.cwd === "string" && parsed.cwd.length > 0) {
+ cwd = parsed.cwd;
+ break;
+ }
+ } catch {
+ // skip malformed lines
+ }
+ }
+
+ return logEntriesToEvents(entries, {
+ cli: "claude",
+ sessionId: meta.sessionId,
+ transcriptPath: meta.transcriptPath,
+ cwd,
+ });
+}
diff --git a/src/audit/cli-adapters/codex.ts b/src/audit/cli-adapters/codex.ts
new file mode 100644
index 0000000..e8e895e
--- /dev/null
+++ b/src/audit/cli-adapters/codex.ts
@@ -0,0 +1,56 @@
+/**
+ * Codex (OpenAI) transcript adapter.
+ *
+ * Codex stores transcripts at ~/.codex/sessions/YYYY/MM/DD/<*sessionId*>.jsonl
+ * with a different layout per project. We enumerate via lib/codex-projects.ts
+ * and parse via lib/codex-sessions.ts which produces the same LogEntry[] shape
+ * the Claude parser uses, so the shared converter handles the rest.
+ */
+import { statSync } from "node:fs";
+import { getCodexProjects, getCodexSessionsByEncodedName } from "../../../lib/codex-projects";
+import { getCodexSessionLog } from "../../../lib/codex-sessions";
+import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
+import type { ListOpts } from "./claude";
+import { logEntriesToEvents } from "./shared";
+
+export async function listCodexTranscriptMetadata(
+ opts: ListOpts = {},
+): Promise {
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
+ const sinceMs = opts.sinceMs ?? 0;
+ const out: TranscriptMetadata[] = [];
+
+ const projects = await getCodexProjects();
+ for (const project of projects) {
+ const { cwd, sessions } = await getCodexSessionsByEncodedName(project.name);
+ const effectiveCwd = cwd ?? "";
+ if (projectFilter && !projectFilter.has(effectiveCwd)) continue;
+ for (const s of sessions) {
+ const mtimeMs = s.lastModified.getTime();
+ if (mtimeMs < sinceMs) continue;
+ let sizeBytes = 0;
+ try { sizeBytes = statSync(s.path).size; } catch { /* unreadable */ }
+ if (!s.sessionId) continue;
+ out.push({
+ cli: "codex",
+ projectName: project.name,
+ sessionId: s.sessionId,
+ transcriptPath: s.path,
+ mtimeMs,
+ sizeBytes,
+ });
+ }
+ }
+ return out;
+}
+
+export async function streamCodexEvents(meta: TranscriptMetadata): Promise {
+ const log = await getCodexSessionLog(meta.sessionId);
+ if (!log) return [];
+ return logEntriesToEvents(log.entries, {
+ cli: "codex",
+ sessionId: meta.sessionId,
+ transcriptPath: meta.transcriptPath,
+ cwd: log.cwd ?? "",
+ });
+}
diff --git a/src/audit/cli-adapters/copilot.ts b/src/audit/cli-adapters/copilot.ts
new file mode 100644
index 0000000..70cfcce
--- /dev/null
+++ b/src/audit/cli-adapters/copilot.ts
@@ -0,0 +1,51 @@
+/**
+ * GitHub Copilot CLI transcript adapter.
+ */
+import { statSync } from "node:fs";
+import { getCopilotProjects, getCopilotSessionsByEncodedName } from "../../../lib/copilot-projects";
+import { getCopilotSessionLog } from "../../../lib/copilot-sessions";
+import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
+import type { ListOpts } from "./claude";
+import { logEntriesToEvents } from "./shared";
+
+export async function listCopilotTranscriptMetadata(
+ opts: ListOpts = {},
+): Promise {
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
+ const sinceMs = opts.sinceMs ?? 0;
+ const out: TranscriptMetadata[] = [];
+
+ const projects = await getCopilotProjects();
+ for (const project of projects) {
+ const { cwd, sessions } = await getCopilotSessionsByEncodedName(project.name);
+ const effectiveCwd = cwd ?? "";
+ if (projectFilter && !projectFilter.has(effectiveCwd)) continue;
+ for (const s of sessions) {
+ const mtimeMs = s.lastModified.getTime();
+ if (mtimeMs < sinceMs) continue;
+ let sizeBytes = 0;
+ try { sizeBytes = statSync(s.path).size; } catch { /* unreadable */ }
+ if (!s.sessionId) continue;
+ out.push({
+ cli: "copilot",
+ projectName: project.name,
+ sessionId: s.sessionId,
+ transcriptPath: s.path,
+ mtimeMs,
+ sizeBytes,
+ });
+ }
+ }
+ return out;
+}
+
+export async function streamCopilotEvents(meta: TranscriptMetadata): Promise {
+ const log = await getCopilotSessionLog(meta.sessionId);
+ if (!log) return [];
+ return logEntriesToEvents(log.entries, {
+ cli: "copilot",
+ sessionId: meta.sessionId,
+ transcriptPath: meta.transcriptPath,
+ cwd: log.cwd ?? "",
+ });
+}
diff --git a/src/audit/cli-adapters/cursor.ts b/src/audit/cli-adapters/cursor.ts
new file mode 100644
index 0000000..3c4e92f
--- /dev/null
+++ b/src/audit/cli-adapters/cursor.ts
@@ -0,0 +1,51 @@
+/**
+ * Cursor Agent CLI transcript adapter.
+ */
+import { statSync } from "node:fs";
+import { getCursorProjects, getCursorSessionsByEncodedName } from "../../../lib/cursor-projects";
+import { getCursorSessionLog } from "../../../lib/cursor-sessions";
+import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
+import type { ListOpts } from "./claude";
+import { logEntriesToEvents } from "./shared";
+
+export async function listCursorTranscriptMetadata(
+ opts: ListOpts = {},
+): Promise {
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
+ const sinceMs = opts.sinceMs ?? 0;
+ const out: TranscriptMetadata[] = [];
+
+ const projects = await getCursorProjects();
+ for (const project of projects) {
+ const { cwd, sessions } = await getCursorSessionsByEncodedName(project.name);
+ const effectiveCwd = cwd ?? "";
+ if (projectFilter && !projectFilter.has(effectiveCwd)) continue;
+ for (const s of sessions) {
+ const mtimeMs = s.lastModified.getTime();
+ if (mtimeMs < sinceMs) continue;
+ let sizeBytes = 0;
+ try { sizeBytes = statSync(s.path).size; } catch { /* unreadable */ }
+ if (!s.sessionId) continue;
+ out.push({
+ cli: "cursor",
+ projectName: project.name,
+ sessionId: s.sessionId,
+ transcriptPath: s.path,
+ mtimeMs,
+ sizeBytes,
+ });
+ }
+ }
+ return out;
+}
+
+export async function streamCursorEvents(meta: TranscriptMetadata): Promise {
+ const log = await getCursorSessionLog(meta.sessionId);
+ if (!log) return [];
+ return logEntriesToEvents(log.entries, {
+ cli: "cursor",
+ sessionId: meta.sessionId,
+ transcriptPath: meta.transcriptPath,
+ cwd: log.cwd ?? "",
+ });
+}
diff --git a/src/audit/cli-adapters/gemini.ts b/src/audit/cli-adapters/gemini.ts
new file mode 100644
index 0000000..2253afa
--- /dev/null
+++ b/src/audit/cli-adapters/gemini.ts
@@ -0,0 +1,51 @@
+/**
+ * Gemini CLI transcript adapter.
+ */
+import { statSync } from "node:fs";
+import { getGeminiProjects, getGeminiSessionsByEncodedName } from "../../../lib/gemini-projects";
+import { getGeminiSessionLog } from "../../../lib/gemini-sessions";
+import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
+import type { ListOpts } from "./claude";
+import { logEntriesToEvents } from "./shared";
+
+export async function listGeminiTranscriptMetadata(
+ opts: ListOpts = {},
+): Promise {
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
+ const sinceMs = opts.sinceMs ?? 0;
+ const out: TranscriptMetadata[] = [];
+
+ const projects = await getGeminiProjects();
+ for (const project of projects) {
+ const { cwd, sessions } = await getGeminiSessionsByEncodedName(project.name);
+ const effectiveCwd = cwd ?? "";
+ if (projectFilter && !projectFilter.has(effectiveCwd)) continue;
+ for (const s of sessions) {
+ const mtimeMs = s.lastModified.getTime();
+ if (mtimeMs < sinceMs) continue;
+ let sizeBytes = 0;
+ try { sizeBytes = statSync(s.path).size; } catch { /* unreadable */ }
+ if (!s.sessionId) continue;
+ out.push({
+ cli: "gemini",
+ projectName: project.name,
+ sessionId: s.sessionId,
+ transcriptPath: s.path,
+ mtimeMs,
+ sizeBytes,
+ });
+ }
+ }
+ return out;
+}
+
+export async function streamGeminiEvents(meta: TranscriptMetadata): Promise {
+ const log = await getGeminiSessionLog(meta.sessionId);
+ if (!log) return [];
+ return logEntriesToEvents(log.entries, {
+ cli: "gemini",
+ sessionId: meta.sessionId,
+ transcriptPath: meta.transcriptPath,
+ cwd: log.cwd ?? "",
+ });
+}
diff --git a/src/audit/cli-adapters/index.ts b/src/audit/cli-adapters/index.ts
new file mode 100644
index 0000000..d82c233
--- /dev/null
+++ b/src/audit/cli-adapters/index.ts
@@ -0,0 +1,70 @@
+/**
+ * Adapter registry — maps each IntegrationType to its list+stream functions.
+ *
+ * Each adapter exposes:
+ * • listTranscripts(opts) → Promise
+ * • streamEvents(meta) → Promise
+ *
+ * Add a new CLI by writing a sibling module and registering it here.
+ */
+import type { IntegrationType } from "../../hooks/types";
+import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
+import type { ListOpts } from "./claude";
+
+import { listClaudeTranscriptMetadata, streamClaudeEvents } from "./claude";
+import { listCodexTranscriptMetadata, streamCodexEvents } from "./codex";
+import { listCopilotTranscriptMetadata, streamCopilotEvents } from "./copilot";
+import { listCursorTranscriptMetadata, streamCursorEvents } from "./cursor";
+import { listOpenCodeTranscriptMetadata, streamOpenCodeEvents } from "./opencode";
+import { listPiTranscriptMetadata, streamPiEvents } from "./pi";
+import { listGeminiTranscriptMetadata, streamGeminiEvents } from "./gemini";
+
+export type { ListOpts };
+
+export interface CliAdapter {
+ cli: IntegrationType;
+ listTranscripts: (opts?: ListOpts) => Promise;
+ streamEvents: (meta: TranscriptMetadata) => Promise;
+}
+
+export const ADAPTERS: Record = {
+ claude: {
+ cli: "claude",
+ listTranscripts: listClaudeTranscriptMetadata,
+ streamEvents: streamClaudeEvents,
+ },
+ codex: {
+ cli: "codex",
+ listTranscripts: listCodexTranscriptMetadata,
+ streamEvents: streamCodexEvents,
+ },
+ copilot: {
+ cli: "copilot",
+ listTranscripts: listCopilotTranscriptMetadata,
+ streamEvents: streamCopilotEvents,
+ },
+ cursor: {
+ cli: "cursor",
+ listTranscripts: listCursorTranscriptMetadata,
+ streamEvents: streamCursorEvents,
+ },
+ opencode: {
+ cli: "opencode",
+ listTranscripts: listOpenCodeTranscriptMetadata,
+ streamEvents: streamOpenCodeEvents,
+ },
+ pi: {
+ cli: "pi",
+ listTranscripts: listPiTranscriptMetadata,
+ streamEvents: streamPiEvents,
+ },
+ gemini: {
+ cli: "gemini",
+ listTranscripts: listGeminiTranscriptMetadata,
+ streamEvents: streamGeminiEvents,
+ },
+};
+
+export function getAdapter(cli: IntegrationType): CliAdapter {
+ return ADAPTERS[cli];
+}
diff --git a/src/audit/cli-adapters/opencode.ts b/src/audit/cli-adapters/opencode.ts
new file mode 100644
index 0000000..2167e8d
--- /dev/null
+++ b/src/audit/cli-adapters/opencode.ts
@@ -0,0 +1,52 @@
+/**
+ * OpenCode (sst/opencode) transcript adapter.
+ *
+ * OpenCode is the outlier — sessions live in a SQLite database, not on disk
+ * as JSONL files. The `transcriptPath` is therefore a virtual `opencode://`
+ * URI and `sizeBytes` is 0 (the file cache layer treats it as uncacheable).
+ */
+import { getOpenCodeProjects, getOpenCodeSessionsByEncodedName } from "../../../lib/opencode-projects";
+import { getOpenCodeSessionLog } from "../../../lib/opencode-sessions";
+import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
+import type { ListOpts } from "./claude";
+import { logEntriesToEvents } from "./shared";
+
+export async function listOpenCodeTranscriptMetadata(
+ opts: ListOpts = {},
+): Promise {
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
+ const sinceMs = opts.sinceMs ?? 0;
+ const out: TranscriptMetadata[] = [];
+
+ const projects = await getOpenCodeProjects();
+ for (const project of projects) {
+ const { cwd, sessions } = await getOpenCodeSessionsByEncodedName(project.name);
+ const effectiveCwd = cwd ?? "";
+ if (projectFilter && !projectFilter.has(effectiveCwd)) continue;
+ for (const s of sessions) {
+ const mtimeMs = s.lastModified.getTime();
+ if (mtimeMs < sinceMs) continue;
+ if (!s.sessionId) continue;
+ out.push({
+ cli: "opencode",
+ projectName: project.name,
+ sessionId: s.sessionId,
+ transcriptPath: s.path,
+ mtimeMs,
+ sizeBytes: 0,
+ });
+ }
+ }
+ return out;
+}
+
+export async function streamOpenCodeEvents(meta: TranscriptMetadata): Promise {
+ const log = await getOpenCodeSessionLog(meta.sessionId);
+ if (!log) return [];
+ return logEntriesToEvents(log.entries, {
+ cli: "opencode",
+ sessionId: meta.sessionId,
+ transcriptPath: meta.transcriptPath,
+ cwd: log.cwd ?? "",
+ });
+}
diff --git a/src/audit/cli-adapters/pi.ts b/src/audit/cli-adapters/pi.ts
new file mode 100644
index 0000000..1d682c5
--- /dev/null
+++ b/src/audit/cli-adapters/pi.ts
@@ -0,0 +1,51 @@
+/**
+ * Pi (pi-coding-agent) transcript adapter.
+ */
+import { statSync } from "node:fs";
+import { getPiProjects, getPiSessionsByEncodedName } from "../../../lib/pi-projects";
+import { getPiSessionLog } from "../../../lib/pi-sessions";
+import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
+import type { ListOpts } from "./claude";
+import { logEntriesToEvents } from "./shared";
+
+export async function listPiTranscriptMetadata(
+ opts: ListOpts = {},
+): Promise {
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
+ const sinceMs = opts.sinceMs ?? 0;
+ const out: TranscriptMetadata[] = [];
+
+ const projects = await getPiProjects();
+ for (const project of projects) {
+ const { cwd, sessions } = await getPiSessionsByEncodedName(project.name);
+ const effectiveCwd = cwd ?? "";
+ if (projectFilter && !projectFilter.has(effectiveCwd)) continue;
+ for (const s of sessions) {
+ const mtimeMs = s.lastModified.getTime();
+ if (mtimeMs < sinceMs) continue;
+ let sizeBytes = 0;
+ try { sizeBytes = statSync(s.path).size; } catch { /* unreadable */ }
+ if (!s.sessionId) continue;
+ out.push({
+ cli: "pi",
+ projectName: project.name,
+ sessionId: s.sessionId,
+ transcriptPath: s.path,
+ mtimeMs,
+ sizeBytes,
+ });
+ }
+ }
+ return out;
+}
+
+export async function streamPiEvents(meta: TranscriptMetadata): Promise {
+ const log = await getPiSessionLog(meta.sessionId);
+ if (!log) return [];
+ return logEntriesToEvents(log.entries, {
+ cli: "pi",
+ sessionId: meta.sessionId,
+ transcriptPath: meta.transcriptPath,
+ cwd: log.cwd ?? "",
+ });
+}
diff --git a/src/audit/cli-adapters/shared.ts b/src/audit/cli-adapters/shared.ts
new file mode 100644
index 0000000..8f17a1a
--- /dev/null
+++ b/src/audit/cli-adapters/shared.ts
@@ -0,0 +1,74 @@
+/**
+ * Shared helpers used by every per-CLI adapter.
+ *
+ * The lib/-sessions.ts parsers all produce the same `LogEntry[]` shape
+ * (defined in lib/log-entries.ts), so the conversion from LogEntry[] to
+ * NormalizedToolEvent[] is uniform across CLIs. The only per-CLI difference is
+ * the canonicalization function, which we delegate to
+ * `src/hooks/tool-name-canonicalize.ts`.
+ */
+import type { LogEntry } from "../../../lib/log-entries";
+import type { IntegrationType } from "../../hooks/types";
+import {
+ canonicalizeToolName,
+ canonicalizeToolInput,
+} from "../../hooks/tool-name-canonicalize";
+import {
+ AUDIT_TOOL_RESULT_MAX_BYTES,
+ type NormalizedToolEvent,
+} from "../types";
+
+export interface ConvertContext {
+ cli: IntegrationType;
+ sessionId: string;
+ transcriptPath: string;
+ /** Cwd resolved by the per-CLI parser. May be empty if the transcript had no
+ * session-start record. The audit falls back to the decoded project name. */
+ cwd: string;
+}
+
+/** Walks the LogEntry[] in timestamp order and yields one NormalizedToolEvent
+ * per `tool_use` content block, with the matching `tool_result.content` text
+ * attached (truncated). Returns events in chronological order. */
+export function logEntriesToEvents(
+ entries: LogEntry[],
+ ctx: ConvertContext,
+): NormalizedToolEvent[] {
+ const events: NormalizedToolEvent[] = [];
+
+ for (const entry of entries) {
+ if (entry.type !== "assistant") continue;
+ for (const block of entry.message.content) {
+ if (block.type !== "tool_use") continue;
+ const rawName = block.name;
+ const canonicalName = canonicalizeToolName(rawName, ctx.cli) ?? rawName;
+ const canonicalInput = canonicalizeToolInput(
+ canonicalName,
+ block.input,
+ ctx.cli,
+ ) as Record;
+
+ let toolResultText: string | undefined;
+ if (block.result?.content) {
+ toolResultText =
+ block.result.content.length > AUDIT_TOOL_RESULT_MAX_BYTES
+ ? block.result.content.slice(0, AUDIT_TOOL_RESULT_MAX_BYTES)
+ : block.result.content;
+ }
+
+ events.push({
+ cli: ctx.cli,
+ sessionId: ctx.sessionId,
+ transcriptPath: ctx.transcriptPath,
+ cwd: ctx.cwd,
+ timestamp: entry.timestamp,
+ toolName: canonicalName,
+ rawToolName: rawName,
+ toolInput: canonicalInput ?? {},
+ toolResultText,
+ });
+ }
+ }
+
+ return events;
+}
diff --git a/src/audit/detectors/find-from-root.ts b/src/audit/detectors/find-from-root.ts
new file mode 100644
index 0000000..47ba4a3
--- /dev/null
+++ b/src/audit/detectors/find-from-root.ts
@@ -0,0 +1,25 @@
+import type { Detector } from "../types";
+
+const RISKY_ROOTS = ["/", "/home", "/usr", "/etc", "/var", "/opt", "/Users"];
+
+/** Bash `find` invoked against the filesystem root or another high-level
+ * directory — tends to exhaust resources and rarely returns useful results. */
+export const findFromRoot: Detector = {
+ name: "find-from-root",
+ description: "Bash `find` against `/`, `/home`, `/usr`, etc. — scope to cwd instead.",
+ category: "Risky",
+ severity: "warn",
+ detect(event) {
+ if (event.toolName !== "Bash") return null;
+ const command = (event.toolInput as { command?: unknown }).command;
+ if (typeof command !== "string") return null;
+ const cmd = command.trim();
+ // find / OR find /home OR find "/etc" … — first non-flag arg
+ const match = /(?:^|[\s;|&])find\s+(?:-\S+\s+)*("[^"]+"|'[^']+'|\S+)/.exec(cmd);
+ if (!match) return null;
+ const raw = match[1].replace(/^["']|["']$/g, "");
+ const stripped = raw.replace(/\/+$/, "") || "/";
+ if (!RISKY_ROOTS.includes(stripped)) return null;
+ return { example: cmd.slice(0, 160) };
+ },
+};
diff --git a/src/audit/detectors/git-commit-no-verify.ts b/src/audit/detectors/git-commit-no-verify.ts
new file mode 100644
index 0000000..8270e62
--- /dev/null
+++ b/src/audit/detectors/git-commit-no-verify.ts
@@ -0,0 +1,20 @@
+import type { Detector } from "../types";
+
+/** `git commit ... --no-verify` (or short `-n`) — skipping pre-commit hooks. */
+export const gitCommitNoVerify: Detector = {
+ name: "git-commit-no-verify",
+ description: "git commit invoked with --no-verify / -n, skipping hooks.",
+ category: "Risky",
+ severity: "warn",
+ detect(event) {
+ if (event.toolName !== "Bash") return null;
+ const command = (event.toolInput as { command?: unknown }).command;
+ if (typeof command !== "string") return null;
+ const cmd = command;
+ if (!/\bgit\s+commit\b/.test(cmd)) return null;
+ if (/\s--no-verify\b/.test(cmd) || /\s-n\b/.test(cmd)) {
+ return { example: cmd.replace(/\s+/g, " ").trim().slice(0, 160) };
+ }
+ return null;
+ },
+};
diff --git a/src/audit/detectors/index.ts b/src/audit/detectors/index.ts
new file mode 100644
index 0000000..763fcc3
--- /dev/null
+++ b/src/audit/detectors/index.ts
@@ -0,0 +1,33 @@
+/**
+ * Registry of audit-only detectors.
+ *
+ * Detectors are pure functions over a NormalizedToolEvent (plus optional
+ * per-session state). They detect "stupid behaviors" not currently covered by
+ * the runtime builtin policies, with no real-time enforcement — counting only.
+ *
+ * Add a new detector by writing a sibling file and registering it here.
+ */
+import type { Detector } from "../types";
+import { redundantCdCwd } from "./redundant-cd-cwd";
+import { preferEditOverReadCat } from "./prefer-edit-over-read-cat";
+import { preferEditOverSedAwk } from "./prefer-edit-over-sed-awk";
+import { preferWriteOverHeredoc } from "./prefer-write-over-heredoc";
+import { sleepPollingLoop } from "./sleep-polling-loop";
+import { findFromRoot } from "./find-from-root";
+import { gitCommitNoVerify } from "./git-commit-no-verify";
+import { rereadAfterEdit } from "./reread-after-edit";
+
+export const AUDIT_DETECTORS: Detector[] = [
+ redundantCdCwd,
+ preferEditOverReadCat,
+ preferEditOverSedAwk,
+ preferWriteOverHeredoc,
+ sleepPollingLoop,
+ findFromRoot,
+ gitCommitNoVerify,
+ rereadAfterEdit,
+];
+
+export function getDetectorByName(name: string): Detector | undefined {
+ return AUDIT_DETECTORS.find((d) => d.name === name);
+}
diff --git a/src/audit/detectors/prefer-edit-over-read-cat.ts b/src/audit/detectors/prefer-edit-over-read-cat.ts
new file mode 100644
index 0000000..c1d2fb3
--- /dev/null
+++ b/src/audit/detectors/prefer-edit-over-read-cat.ts
@@ -0,0 +1,29 @@
+import type { Detector } from "../types";
+
+const SOURCE_EXT_RE = /\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|swift|rb|php|c|h|cc|cpp|hpp|cs|scala|sh|bash|zsh|json|yaml|yml|toml|md|txt|sql|html|css|scss|sass)$/i;
+
+/** `cat | head | tail | less | more` invoked on a single source file with no
+ * shell pipeline / redirection. The Read tool is the right answer. .env files
+ * are intentionally excluded (covered by `block-env-files`). */
+export const preferEditOverReadCat: Detector = {
+ name: "prefer-edit-over-read-cat",
+ description: "Bash `cat`/`head`/`tail`/`less`/`more` on a single source file — use Read.",
+ category: "Wasteful",
+ severity: "info",
+ detect(event) {
+ if (event.toolName !== "Bash") return null;
+ const command = (event.toolInput as { command?: unknown }).command;
+ if (typeof command !== "string") return null;
+ const cmd = command.trim();
+ // Reject any shell pipeline, redirection, command chaining or substitution.
+ if (/[|<>;&`$()]/.test(cmd)) return null;
+ const match = /^(cat|head|tail|less|more)\s+(?:-\S+\s+)*(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
+ if (!match) return null;
+ const path = match[2] ?? match[3] ?? match[4] ?? "";
+ if (!path) return null;
+ // .env is covered by block-env-files; skip to avoid double-counting.
+ if (/(?:^|\/)\.env(?:\..+)?$/.test(path)) return null;
+ if (!SOURCE_EXT_RE.test(path)) return null;
+ return { example: cmd };
+ },
+};
diff --git a/src/audit/detectors/prefer-edit-over-sed-awk.ts b/src/audit/detectors/prefer-edit-over-sed-awk.ts
new file mode 100644
index 0000000..1895175
--- /dev/null
+++ b/src/audit/detectors/prefer-edit-over-sed-awk.ts
@@ -0,0 +1,25 @@
+import type { Detector } from "../types";
+
+/** In-place edits with `sed -i` or `awk … > file` that should have been done
+ * with the Edit tool. */
+export const preferEditOverSedAwk: Detector = {
+ name: "prefer-edit-over-sed-awk",
+ description: "Bash `sed -i`/`awk` in-place edits — use Edit.",
+ category: "Wasteful",
+ severity: "info",
+ detect(event) {
+ if (event.toolName !== "Bash") return null;
+ const command = (event.toolInput as { command?: unknown }).command;
+ if (typeof command !== "string") return null;
+ const cmd = command.trim();
+ // `sed -i ...` (GNU/macOS) or `sed -i'.bak' ...` (BSD-style)
+ if (/(?:^|\s|;|&&|\|\|)sed\b[^|]*\s-i(?=\b|['"])/.test(cmd)) {
+ return { example: cmd };
+ }
+ // `awk '...' file > out` or `awk '...' file > file` (in-place via redirection)
+ if (/(?:^|\s|;|&&|\|\|)awk\b[^|]*\s>\s*\S+/.test(cmd) && !/\|/.test(cmd)) {
+ return { example: cmd };
+ }
+ return null;
+ },
+};
diff --git a/src/audit/detectors/prefer-write-over-heredoc.ts b/src/audit/detectors/prefer-write-over-heredoc.ts
new file mode 100644
index 0000000..8fbac3f
--- /dev/null
+++ b/src/audit/detectors/prefer-write-over-heredoc.ts
@@ -0,0 +1,32 @@
+import type { Detector } from "../types";
+
+/** `cat << EOF > file` heredoc patterns or `echo … > file` writing multi-line
+ * content. The Write tool is the right answer. */
+export const preferWriteOverHeredoc: Detector = {
+ name: "prefer-write-over-heredoc",
+ description: "Bash heredoc / `echo > file` writing multi-line content — use Write.",
+ category: "Wasteful",
+ severity: "info",
+ detect(event) {
+ if (event.toolName !== "Bash") return null;
+ const command = (event.toolInput as { command?: unknown }).command;
+ if (typeof command !== "string") return null;
+ const cmd = command;
+ // Heredoc redirected to a file IMMEDIATELY after the delimiter:
+ // `cat <<'EOF' > path` ← match (heredoc opens AND redirects in one step)
+ // `cat < file` is unrelated)
+ // The redirect must appear before the next whitespace/newline that ends the
+ // heredoc opener line — otherwise it's a body or downstream redirect.
+ if (/<<-?\s*['"]?[A-Z_]+['"]?\s*>\s*\S/.test(cmd)) {
+ const summary = cmd.replace(/\s+/g, " ").trim().slice(0, 160);
+ return { example: summary };
+ }
+ // `echo "multi\nline" > file` or `printf "..." > file` with embedded newlines.
+ if (/(?:^|\s|;|&&|\|\|)(?:echo|printf)\s+["'][^"']*\n[^"']*["']\s*>\s*\S/.test(cmd)) {
+ const summary = cmd.replace(/\s+/g, " ").trim().slice(0, 160);
+ return { example: summary };
+ }
+ return null;
+ },
+};
diff --git a/src/audit/detectors/redundant-cd-cwd.ts b/src/audit/detectors/redundant-cd-cwd.ts
new file mode 100644
index 0000000..68dbc03
--- /dev/null
+++ b/src/audit/detectors/redundant-cd-cwd.ts
@@ -0,0 +1,26 @@
+import type { Detector } from "../types";
+
+/** Bash command starting with `cd && …` where the absolute
+ * path equals (or is the same realpath as) the session's cwd. The agent's
+ * shell already runs in cwd — the prefix is pure waste. Explicitly called
+ * out in the Claude Code system prompt for git commands. */
+export const redundantCdCwd: Detector = {
+ name: "redundant-cd-cwd",
+ description:
+ "Bash commands prefixed with `cd && …` even though commands already run in cwd.",
+ category: "Wasteful",
+ severity: "info",
+ detect(event) {
+ if (event.toolName !== "Bash") return null;
+ const command = (event.toolInput as { command?: unknown }).command;
+ if (typeof command !== "string" || !event.cwd) return null;
+ const trimmed = command.trimStart();
+ const match = /^cd\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*&&\s*([\s\S]+)$/.exec(trimmed);
+ if (!match) return null;
+ const path = (match[1] ?? match[2] ?? match[3] ?? "").replace(/\/+$/, "");
+ const cwd = event.cwd.replace(/\/+$/, "");
+ if (path !== cwd) return null;
+ const rest = match[4].trim();
+ return { example: `cd ${path} && ${rest}` };
+ },
+};
diff --git a/src/audit/detectors/reread-after-edit.ts b/src/audit/detectors/reread-after-edit.ts
new file mode 100644
index 0000000..abea184
--- /dev/null
+++ b/src/audit/detectors/reread-after-edit.ts
@@ -0,0 +1,56 @@
+import type { Detector, DetectorSessionState } from "../types";
+
+const STATE_KEY = "rereadAfterEdit";
+const WINDOW = 5;
+
+interface RereadState {
+ /** Map of file_path → number of remaining tool-calls in which a Read should
+ * trigger this detector. Decremented every tool event; deleted at 0. */
+ countdown: Map;
+}
+
+function getState(state: DetectorSessionState): RereadState {
+ let s = state[STATE_KEY] as RereadState | undefined;
+ if (!s) {
+ s = { countdown: new Map() };
+ state[STATE_KEY] = s;
+ }
+ return s;
+}
+
+/** When Edit or Write lands on file_path, then a subsequent Read of the same
+ * file_path within N tool calls is wasteful — the editor already returned the
+ * updated content. Explicitly called out in the Claude system prompt. */
+export const rereadAfterEdit: Detector = {
+ name: "reread-after-edit",
+ description: "Read of a file that was just Edit'd or Write'n in the same session.",
+ category: "Wasteful",
+ severity: "info",
+ detect(event, sessionState) {
+ const state = getState(sessionState);
+ const filePath = (event.toolInput as { file_path?: unknown }).file_path;
+ const pathStr = typeof filePath === "string" ? filePath : null;
+
+ // Tick down every existing countdown.
+ for (const [key, n] of state.countdown) {
+ if (n <= 1) state.countdown.delete(key);
+ else state.countdown.set(key, n - 1);
+ }
+
+ if (!pathStr) return null;
+
+ if (event.toolName === "Edit" || event.toolName === "Write") {
+ state.countdown.set(pathStr, WINDOW);
+ return null;
+ }
+
+ if (event.toolName === "Read") {
+ if (state.countdown.has(pathStr)) {
+ state.countdown.delete(pathStr); // count once per edit-then-read pair
+ return { example: `Read ${pathStr} immediately after Edit/Write` };
+ }
+ }
+
+ return null;
+ },
+};
diff --git a/src/audit/detectors/sleep-polling-loop.ts b/src/audit/detectors/sleep-polling-loop.ts
new file mode 100644
index 0000000..80a2c8b
--- /dev/null
+++ b/src/audit/detectors/sleep-polling-loop.ts
@@ -0,0 +1,32 @@
+import type { Detector } from "../types";
+
+const SLEEP_THRESHOLD_SECONDS = 30;
+
+/** Bash `sleep N` where N ≥ 30 (busy polling), or `while …; sleep …; done`. */
+export const sleepPollingLoop: Detector = {
+ name: "sleep-polling-loop",
+ description: "Bash long `sleep` or while-sleep polling loops.",
+ category: "Wasteful",
+ severity: "info",
+ detect(event) {
+ if (event.toolName !== "Bash") return null;
+ const command = (event.toolInput as { command?: unknown }).command;
+ if (typeof command !== "string") return null;
+ const cmd = command;
+ // while-sleep loop
+ if (/\bwhile\b[\s\S]*?\bsleep\b[\s\S]*?\bdone\b/.test(cmd)) {
+ return { example: cmd.replace(/\s+/g, " ").trim().slice(0, 160) };
+ }
+ // Standalone long sleep
+ const match = /\bsleep\s+(\d+)(?:\.\d+)?(m|h|d)?\b/.exec(cmd);
+ if (match) {
+ const n = parseInt(match[1], 10);
+ const unit = match[2] ?? "s";
+ const seconds = unit === "m" ? n * 60 : unit === "h" ? n * 3600 : unit === "d" ? n * 86400 : n;
+ if (seconds >= SLEEP_THRESHOLD_SECONDS) {
+ return { example: cmd.replace(/\s+/g, " ").trim().slice(0, 160) };
+ }
+ }
+ return null;
+ },
+};
diff --git a/src/audit/index.ts b/src/audit/index.ts
new file mode 100644
index 0000000..4fc4828
--- /dev/null
+++ b/src/audit/index.ts
@@ -0,0 +1,306 @@
+/**
+ * `runAudit` — entry point for the `failproofai audit` command.
+ *
+ * Drives the pipeline: per-CLI adapters → tool events → (replay + audit
+ * detectors) → per-transcript results → aggregated `AuditResult`.
+ *
+ * Parallelizes transcript scans via `lib/concurrency.ts` `batchAll` (8 at a
+ * time — bounds disk I/O without overwhelming the policy engine).
+ */
+import { batchAll } from "../../lib/concurrency";
+import { BUILTIN_POLICIES } from "../hooks/builtin-policies";
+import { INTEGRATION_TYPES, type IntegrationType } from "../hooks/types";
+import { ADAPTERS } from "./cli-adapters";
+import { AUDIT_DETECTORS } from "./detectors";
+import { readCachedTranscriptResult, writeCachedTranscriptResult } from "./cache";
+import { initReplay, replayEvent } from "./replay";
+import {
+ AUDIT_EXAMPLE_MAX_CHARS,
+ AUDIT_MAX_EXAMPLES_PER_NAME,
+ type AuditCount,
+ type AuditResult,
+ type DetectorSessionState,
+ type NormalizedToolEvent,
+ type RunAuditOptions,
+ type TranscriptAuditResult,
+ type TranscriptMetadata,
+} from "./types";
+
+const TRANSCRIPT_CONCURRENCY = 8;
+
+/** Canonicalize a policy name to its short, qualified form for display
+ * (`failproofai/foo` → `foo`). */
+function shortPolicyName(name: string): string {
+ const slash = name.indexOf("/");
+ return slash >= 0 ? name.slice(slash + 1) : name;
+}
+
+/** Look up a builtin policy's category by canonical name; null when the name
+ * doesn't match a builtin (e.g. user custom policy). */
+function findBuiltinCategory(name: string): string {
+ const short = shortPolicyName(name);
+ for (const p of BUILTIN_POLICIES) {
+ if (p.name === name || shortPolicyName(p.name) === short) return p.category;
+ }
+ return "Custom";
+}
+
+function truncateExample(s: string): string {
+ if (s.length <= AUDIT_EXAMPLE_MAX_CHARS) return s;
+ return s.slice(0, AUDIT_EXAMPLE_MAX_CHARS - 1) + "…";
+}
+
+function parseSinceOpt(since: string | undefined): number | undefined {
+ if (!since) return undefined;
+ const m = /^(\d+)\s*([dhm])$/i.exec(since.trim());
+ if (m) {
+ const n = parseInt(m[1], 10);
+ const unit = m[2].toLowerCase();
+ const ms = unit === "d" ? 86400000 : unit === "h" ? 3600000 : 60000;
+ return Date.now() - n * ms;
+ }
+ const t = Date.parse(since);
+ if (!Number.isNaN(t)) return t;
+ throw new Error(`Invalid --since value: "${since}" (expected e.g. "7d", "30d", or "2026-04-01")`);
+}
+
+async function scanOneTranscript(meta: TranscriptMetadata): Promise {
+ const empty: TranscriptAuditResult = {
+ transcriptPath: meta.transcriptPath,
+ cli: meta.cli,
+ projectName: meta.projectName,
+ sessionId: meta.sessionId,
+ mtimeMs: meta.mtimeMs,
+ sizeBytes: meta.sizeBytes,
+ hitsByName: {},
+ examplesByName: {},
+ rangeByName: {},
+ };
+
+ let events: NormalizedToolEvent[];
+ try {
+ events = await ADAPTERS[meta.cli].streamEvents(meta);
+ } catch {
+ return empty;
+ }
+ if (events.length === 0) return empty;
+
+ const result = empty;
+ const sessionState: DetectorSessionState = {};
+
+ for (const event of events) {
+ // Run audit detectors first (stateful, must see every event).
+ for (const detector of AUDIT_DETECTORS) {
+ const hit = detector.detect(event, sessionState);
+ if (!hit) continue;
+ recordHit(
+ result,
+ detector.name,
+ event.timestamp,
+ event.cwd,
+ truncateExample(hit.example),
+ );
+ }
+ // Then replay through every builtin policy.
+ let replayHits;
+ try {
+ replayHits = await replayEvent(event);
+ } catch {
+ continue;
+ }
+ for (const hit of replayHits) {
+ const example = formatPolicyExample(hit.policyName, event);
+ recordHit(
+ result,
+ hit.policyName,
+ event.timestamp,
+ event.cwd,
+ truncateExample(example),
+ );
+ }
+ }
+
+ return result;
+}
+
+function formatPolicyExample(_policyName: string, event: NormalizedToolEvent): string {
+ if (event.toolName === "Bash") {
+ const command = (event.toolInput as { command?: unknown }).command;
+ if (typeof command === "string") return command.replace(/\s+/g, " ");
+ }
+ const filePath = (event.toolInput as { file_path?: unknown }).file_path;
+ if (typeof filePath === "string") return `${event.toolName} ${filePath}`;
+ return `${event.toolName}`;
+}
+
+function recordHit(
+ result: TranscriptAuditResult,
+ name: string,
+ timestamp: string,
+ cwd: string,
+ example: string,
+): void {
+ result.hitsByName[name] = (result.hitsByName[name] ?? 0) + 1;
+ const exs = result.examplesByName[name] ?? [];
+ if (exs.length < AUDIT_MAX_EXAMPLES_PER_NAME) {
+ exs.push({ timestamp, cwd, example });
+ result.examplesByName[name] = exs;
+ }
+ const range = result.rangeByName[name];
+ if (!range) {
+ result.rangeByName[name] = { first: timestamp, last: timestamp };
+ } else {
+ if (timestamp < range.first) range.first = timestamp;
+ if (timestamp > range.last) range.last = timestamp;
+ }
+}
+
+function aggregateResults(perTranscript: TranscriptAuditResult[]): AuditCount[] {
+ // For each name: sum hits, count distinct projects, merge ranges + examples.
+ const byName = new Map;
+ examples: { sessionId: string; cwd: string; timestamp: string; example: string }[];
+ first?: string;
+ last?: string;
+ }>();
+
+ for (const t of perTranscript) {
+ for (const [name, count] of Object.entries(t.hitsByName)) {
+ const bucket = byName.get(name) ?? {
+ hits: 0,
+ projects: new Set(),
+ examples: [],
+ };
+ bucket.hits += count;
+ bucket.projects.add(t.projectName);
+ const tExs = t.examplesByName[name] ?? [];
+ for (const e of tExs) {
+ if (bucket.examples.length < AUDIT_MAX_EXAMPLES_PER_NAME) {
+ bucket.examples.push({ ...e, sessionId: t.sessionId });
+ }
+ }
+ const range = t.rangeByName[name];
+ if (range) {
+ if (!bucket.first || range.first < bucket.first) bucket.first = range.first;
+ if (!bucket.last || range.last > bucket.last) bucket.last = range.last;
+ }
+ byName.set(name, bucket);
+ }
+ }
+
+ const detectorNames = new Set(AUDIT_DETECTORS.map((d) => d.name));
+ const out: AuditCount[] = [];
+ for (const [name, bucket] of byName) {
+ const isDetector = detectorNames.has(name);
+ const detector = AUDIT_DETECTORS.find((d) => d.name === name);
+ out.push({
+ name,
+ source: isDetector ? "audit-detector" : "builtin",
+ category: isDetector
+ ? (detector?.category ?? "Wasteful")
+ : findBuiltinCategory(name),
+ severity: isDetector ? (detector?.severity ?? "info") : "deny",
+ hits: bucket.hits,
+ projects: bucket.projects.size,
+ firstSeen: bucket.first,
+ lastSeen: bucket.last,
+ examples: bucket.examples,
+ });
+ }
+
+ out.sort((a, b) => b.hits - a.hits);
+ return out;
+}
+
+export async function runAudit(opts: RunAuditOptions = {}): Promise {
+ const startedAt = Date.now();
+ initReplay();
+
+ const clis = (opts.clis ?? Array.from(INTEGRATION_TYPES)) as IntegrationType[];
+ const sinceMs = parseSinceOpt(opts.since);
+
+ // 1. Discover transcripts across all selected CLIs.
+ const allTranscripts: TranscriptMetadata[] = [];
+ for (const cli of clis) {
+ const adapter = ADAPTERS[cli];
+ let list: TranscriptMetadata[];
+ try {
+ list = await adapter.listTranscripts({ projects: opts.projects, sinceMs });
+ } catch {
+ continue; // adapter failures shouldn't kill the whole audit
+ }
+ allTranscripts.push(...list);
+ }
+
+ // 2. Scan each transcript (cache-aware), 8 in parallel.
+ let skipped = 0;
+ let errors = 0;
+ const tasks = allTranscripts.map((meta) => async (): Promise => {
+ if (!opts.noCache) {
+ const cached = readCachedTranscriptResult(meta.transcriptPath, meta.mtimeMs, meta.sizeBytes);
+ if (cached) return cached;
+ }
+ try {
+ const fresh = await scanOneTranscript(meta);
+ if (!opts.noCache) {
+ writeCachedTranscriptResult(meta.transcriptPath, meta.mtimeMs, meta.sizeBytes, fresh);
+ }
+ return fresh;
+ } catch {
+ errors++;
+ return {
+ transcriptPath: meta.transcriptPath,
+ cli: meta.cli,
+ projectName: meta.projectName,
+ sessionId: meta.sessionId,
+ mtimeMs: meta.mtimeMs,
+ sizeBytes: meta.sizeBytes,
+ hitsByName: {},
+ examplesByName: {},
+ rangeByName: {},
+ };
+ }
+ });
+
+ const settled = await batchAll(tasks, TRANSCRIPT_CONCURRENCY);
+ const perTranscript: TranscriptAuditResult[] = [];
+ for (const s of settled) {
+ if (s.status === "fulfilled") perTranscript.push(s.value);
+ else skipped++;
+ }
+
+ // 3. Aggregate.
+ let results = aggregateResults(perTranscript);
+ if (opts.policies?.length) {
+ const wanted = new Set(opts.policies.map(shortPolicyName));
+ results = results.filter((r) => wanted.has(shortPolicyName(r.name)));
+ }
+
+ const totalsHits = results.reduce((sum, r) => sum + r.hits, 0);
+ const projectsWithHits = new Set();
+ for (const t of perTranscript) {
+ if (Object.keys(t.hitsByName).length > 0) projectsWithHits.add(t.projectName);
+ }
+
+ return {
+ version: 1,
+ scannedAt: new Date(startedAt).toISOString(),
+ scope: {
+ cli: clis,
+ projects: opts.projects ?? "all",
+ since: opts.since ?? null,
+ },
+ transcripts: {
+ scanned: allTranscripts.length,
+ skipped,
+ errors,
+ durationMs: Date.now() - startedAt,
+ },
+ results,
+ totals: {
+ hits: totalsHits,
+ projectsWithHits: projectsWithHits.size,
+ },
+ };
+}
diff --git a/src/audit/replay.ts b/src/audit/replay.ts
new file mode 100644
index 0000000..b755541
--- /dev/null
+++ b/src/audit/replay.ts
@@ -0,0 +1,121 @@
+/**
+ * Replay engine — turns a NormalizedToolEvent into one (or two) synthetic hook
+ * payloads, runs them through the existing `evaluatePolicies` function, and
+ * returns the policy decisions.
+ *
+ * Why two events: builtin sanitize-* policies match on PostToolUse and inspect
+ * the tool result text. PreToolUse alone misses them. Per event we synthesize:
+ * 1. PreToolUse — { tool_name, tool_input }
+ * 2. PostToolUse — { tool_name, tool_input, tool_response } (only when the
+ * transcript captured a tool_result)
+ *
+ * Workflow policies (`require-*-before-stop`) match only on `Stop` and
+ * execSync against live git, so they never fire on PreToolUse/PostToolUse
+ * replay — no explicit skip needed.
+ */
+import type { EvaluationResult } from "../hooks/policy-evaluator";
+import { evaluatePolicies } from "../hooks/policy-evaluator";
+import { BUILTIN_POLICIES, registerBuiltinPolicies } from "../hooks/builtin-policies";
+import { clearPolicies, normalizePolicyName } from "../hooks/policy-registry";
+import type { SessionMetadata } from "../hooks/types";
+import type { NormalizedToolEvent } from "./types";
+
+/** Policies the audit skips on purpose. `warn-repeated-tool-calls` mutates a
+ * per-session sidecar file on every evaluation, which would pollute the
+ * user's real transcript directory during a replay (and inflate counts
+ * because the replay always re-traverses the full session). */
+const SKIP_POLICIES = new Set(
+ ["warn-repeated-tool-calls"].map((n) => normalizePolicyName(n)),
+);
+
+let initialized = false;
+
+/** Register every builtin policy (regardless of user config) so the replay
+ * shows what *could* be caught, not just what's currently enabled. Called
+ * once per `runAudit` invocation. */
+export function initReplay(): void {
+ if (initialized) return;
+ clearPolicies();
+ const enabled = BUILTIN_POLICIES
+ .map((p) => p.name)
+ .filter((n) => !SKIP_POLICIES.has(normalizePolicyName(n)));
+ registerBuiltinPolicies(enabled);
+ initialized = true;
+}
+
+/** Reset for tests / repeated audits in the same process. */
+export function resetReplay(): void {
+ initialized = false;
+ clearPolicies();
+}
+
+export interface ReplayHit {
+ policyName: string;
+ decision: "deny" | "instruct" | "allow";
+ reason: string | null;
+ eventType: "PreToolUse" | "PostToolUse";
+}
+
+/** Replay one normalized tool event through every registered policy. Returns
+ * one ReplayHit per non-allow decision (deny + instruct). Allow-with-reason
+ * is reported too, so sanitize policies that emit informational notes still
+ * surface in the audit. */
+export async function replayEvent(event: NormalizedToolEvent): Promise {
+ if (!initialized) initReplay();
+
+ const session: SessionMetadata = {
+ sessionId: event.sessionId,
+ transcriptPath: event.transcriptPath,
+ cwd: event.cwd,
+ cli: event.cli,
+ };
+
+ const baseToolPayload: Record = {
+ tool_name: event.toolName,
+ tool_input: event.toolInput,
+ session_id: event.sessionId,
+ cwd: event.cwd,
+ transcript_path: event.transcriptPath,
+ };
+
+ const out: ReplayHit[] = [];
+
+ // PreToolUse
+ const pre = await evaluatePolicies("PreToolUse", baseToolPayload, session);
+ collectHits(pre, "PreToolUse", out);
+
+ // PostToolUse — only if the transcript captured a tool result.
+ if (event.toolResultText !== undefined) {
+ const postPayload = { ...baseToolPayload, tool_response: event.toolResultText };
+ const post = await evaluatePolicies("PostToolUse", postPayload, session);
+ collectHits(post, "PostToolUse", out);
+ }
+
+ return out;
+}
+
+function collectHits(
+ result: EvaluationResult,
+ eventType: "PreToolUse" | "PostToolUse",
+ out: ReplayHit[],
+): void {
+ // `policyNames` is set when multiple policies fired (sanitize stack);
+ // otherwise fall back to `policyName`.
+ const names = result.policyNames && result.policyNames.length > 0
+ ? result.policyNames
+ : result.policyName
+ ? [result.policyName]
+ : [];
+ for (const name of names) {
+ // The aggregate `decision` reflects the most severe firing, but the audit
+ // wants per-policy counts. We re-tag each name with the aggregate decision
+ // for now — accurate enough for the table; a future audit-detail mode can
+ // re-evaluate per-policy if precision becomes important.
+ out.push({
+ policyName: name,
+ decision: result.decision,
+ reason: result.reason,
+ eventType,
+ });
+ }
+}
diff --git a/src/audit/report.ts b/src/audit/report.ts
new file mode 100644
index 0000000..924a01b
--- /dev/null
+++ b/src/audit/report.ts
@@ -0,0 +1,175 @@
+/**
+ * Output renderers for `failproofai audit`:
+ * • formatText — ANSI table to stdout
+ * • formatMarkdown — sectioned report written to a file
+ * • formatJson — machine-readable
+ *
+ * Examples are already truncated to AUDIT_EXAMPLE_MAX_CHARS upstream — we
+ * trust that here and only do display formatting.
+ */
+import type { AuditCount, AuditResult, RunAuditOptions } from "./types";
+
+const ANSI = {
+ reset: "\x1B[0m",
+ dim: "\x1B[2m",
+ bold: "\x1B[1m",
+ red: "\x1B[31m",
+ yellow: "\x1B[33m",
+ green: "\x1B[32m",
+ cyan: "\x1B[36m",
+ magenta: "\x1B[35m",
+};
+
+function colorFor(row: AuditCount): string {
+ if (row.severity === "deny" || row.severity === "warn") return ANSI.red;
+ if (row.severity === "instruct" || row.severity === "info") return ANSI.yellow;
+ return ANSI.dim;
+}
+
+function padRight(s: string, n: number): string {
+ if (s.length >= n) return s;
+ return s + " ".repeat(n - s.length);
+}
+
+function padLeft(s: string, n: number): string {
+ if (s.length >= n) return s;
+ return " ".repeat(n - s.length) + s;
+}
+
+export function formatText(result: AuditResult, opts: RunAuditOptions = {}): string {
+ const limit = opts.limit ?? 20;
+ const rows = result.results.slice(0, limit);
+ const showExamples = !!opts.showExamples;
+
+ const lines: string[] = [];
+ const scopeCli = result.scope.cli.length === 7 ? "all CLIs" : result.scope.cli.join(", ");
+ const scopeProj = result.scope.projects === "all" ? "all projects" : `${result.scope.projects.length} project(s)`;
+ const scopeSince = result.scope.since ?? "all time";
+
+ lines.push(
+ `${ANSI.bold}failproofai audit${ANSI.reset} scope: ${scopeCli} · ${scopeProj} · since: ${scopeSince}`,
+ );
+ lines.push("");
+ lines.push(
+ `Scanned ${result.transcripts.scanned} transcripts in ${(result.transcripts.durationMs / 1000).toFixed(1)}s ` +
+ `(${result.transcripts.skipped} skipped, ${result.transcripts.errors} errors)`,
+ );
+ lines.push("");
+
+ if (rows.length === 0) {
+ lines.push(`${ANSI.dim}No hits.${ANSI.reset}`);
+ lines.push("");
+ return lines.join("\n");
+ }
+
+ // Column widths
+ const longestName = rows.reduce((m, r) => Math.max(m, r.name.length), 16);
+ const nameW = Math.min(40, longestName);
+ const hitsW = Math.max(4, ...rows.map((r) => String(r.hits).length));
+ const projW = Math.max(8, ...rows.map((r) => String(r.projects).length));
+ const sourceW = Math.max(6, ...rows.map((r) => r.source.length));
+
+ // Header
+ const header =
+ `${ANSI.bold}${padRight("POLICY / DETECTOR", nameW)} ${padLeft("HITS", hitsW)} ${padLeft("PROJECTS", projW)} ${padRight("SOURCE", sourceW)}` +
+ (showExamples ? " EXAMPLE" : "") +
+ ANSI.reset;
+ lines.push(header);
+
+ for (const row of rows) {
+ const color = colorFor(row);
+ const name = row.name.length > nameW ? row.name.slice(0, nameW - 1) + "…" : row.name;
+ let line =
+ `${color}${padRight(name, nameW)}${ANSI.reset} ` +
+ `${padLeft(String(row.hits), hitsW)} ` +
+ `${padLeft(String(row.projects), projW)} ` +
+ `${ANSI.dim}${padRight(row.source, sourceW)}${ANSI.reset}`;
+ if (showExamples && row.examples[0]) {
+ line += ` ${ANSI.dim}${row.examples[0].example}${ANSI.reset}`;
+ }
+ lines.push(line);
+ }
+
+ lines.push("");
+ lines.push(
+ `${ANSI.bold}TOTAL${ANSI.reset} hits: ${result.totals.hits} across ${result.totals.projectsWithHits} project(s).`,
+ );
+
+ if (result.results.length > limit) {
+ lines.push(`${ANSI.dim}… ${result.results.length - limit} more rows omitted (use --limit ${result.results.length})${ANSI.reset}`);
+ }
+ lines.push("");
+ return lines.join("\n");
+}
+
+export function formatJson(result: AuditResult): string {
+ return JSON.stringify(result, null, 2);
+}
+
+export function formatMarkdown(result: AuditResult): string {
+ const out: string[] = [];
+ out.push(`# failproofai audit report`);
+ out.push("");
+ out.push(`*Generated ${result.scannedAt}*`);
+ out.push("");
+ const scopeCli = result.scope.cli.length === 7 ? "all CLIs" : result.scope.cli.join(", ");
+ const scopeProj = result.scope.projects === "all" ? "all projects" : `${result.scope.projects.length} project(s)`;
+ out.push(`**Scope:** ${scopeCli} · ${scopeProj} · since ${result.scope.since ?? "all time"}`);
+ out.push("");
+ out.push(
+ `**Scan:** ${result.transcripts.scanned} transcripts in ${(result.transcripts.durationMs / 1000).toFixed(1)}s ` +
+ `(${result.transcripts.skipped} skipped, ${result.transcripts.errors} errors)`,
+ );
+ out.push("");
+ out.push(`**Totals:** ${result.totals.hits} hits across ${result.totals.projectsWithHits} project(s).`);
+ out.push("");
+
+ if (result.results.length === 0) {
+ out.push("No hits.");
+ out.push("");
+ return out.join("\n");
+ }
+
+ // Group by category.
+ const byCategory = new Map();
+ for (const r of result.results) {
+ const arr = byCategory.get(r.category) ?? [];
+ arr.push(r);
+ byCategory.set(r.category, arr);
+ }
+ for (const [category, rows] of byCategory) {
+ out.push(`## ${category}`);
+ out.push("");
+ out.push("| Policy / Detector | Hits | Projects | Source | First seen | Last seen |");
+ out.push("|---|---:|---:|---|---|---|");
+ for (const r of rows) {
+ out.push(
+ `| \`${r.name}\` | ${r.hits} | ${r.projects} | ${r.source} | ${r.firstSeen ?? "—"} | ${r.lastSeen ?? "—"} |`,
+ );
+ }
+ out.push("");
+ // Examples sub-section
+ for (const r of rows) {
+ if (r.examples.length === 0) continue;
+ out.push(`### \`${r.name}\` — examples`);
+ out.push("");
+ for (const e of r.examples) {
+ out.push(`- \`${escapeBackticks(e.example)}\` _(${e.cwd || "?"}, ${e.timestamp})_`);
+ }
+ out.push("");
+ }
+ }
+
+ out.push("---");
+ out.push("");
+ out.push(
+ "_Tip: enable any of these in real time with " +
+ "`failproofai policies --install ...`._",
+ );
+ out.push("");
+ return out.join("\n");
+}
+
+function escapeBackticks(s: string): string {
+ return s.replace(/`/g, "\\`");
+}
diff --git a/src/audit/types.ts b/src/audit/types.ts
new file mode 100644
index 0000000..81791fb
--- /dev/null
+++ b/src/audit/types.ts
@@ -0,0 +1,173 @@
+/**
+ * Types for the `failproofai audit` command.
+ *
+ * The audit walks past agent-CLI transcripts, replays each tool-use event
+ * through the existing policy engine, and runs each event through a separate
+ * set of audit-only detectors. Counts are aggregated per (policy|detector) and
+ * rendered as a table to stdout plus a markdown report.
+ */
+import type { IntegrationType } from "../hooks/types";
+
+/**
+ * A single tool-use event extracted from a transcript, canonicalized to the
+ * shape failproofai's policy engine + audit detectors expect.
+ */
+export interface NormalizedToolEvent {
+ cli: IntegrationType;
+ sessionId: string;
+ transcriptPath: string;
+ /** Working directory of the session at the time of the event. */
+ cwd: string;
+ /** ISO-8601 timestamp from the transcript line. */
+ timestamp: string;
+ /** Canonical Claude PascalCase tool name (Bash, Read, Edit, …). */
+ toolName: string;
+ /** Canonicalized tool input (snake_case keys for OpenCode/Pi). */
+ toolInput: Record;
+ /** Pre-canonicalization tool name (e.g. "run_shell_command" for Gemini). */
+ rawToolName: string;
+ /**
+ * Result text from the matching tool_result block, if present in the
+ * transcript. Used to synthesize PostToolUse events for sanitize-* policies.
+ * Truncated to AUDIT_TOOL_RESULT_MAX_BYTES at the adapter to bound memory.
+ */
+ toolResultText?: string;
+}
+
+/** Metadata about a single transcript (one session's JSONL file). */
+export interface TranscriptMetadata {
+ cli: IntegrationType;
+ /** Encoded project folder name (e.g. "-home-user-project"). */
+ projectName: string;
+ sessionId: string;
+ transcriptPath: string;
+ /** mtime of the transcript file. Used for cache invalidation + --since filtering. */
+ mtimeMs: number;
+ /** Byte size of the transcript file. Used for cache invalidation. */
+ sizeBytes: number;
+}
+
+/** Per-session detector state. Detectors mutate this freely. */
+export type DetectorSessionState = Record;
+
+/** One detected occurrence of a "stupid behavior". */
+export interface DetectorHit {
+ /** A short, one-line summary of what triggered the detector. Used as the
+ * example column in the table and the example list in the markdown report.
+ * Truncated to 80 chars upstream. */
+ example: string;
+}
+
+/** A pure function over a normalized event (plus optional per-session state).
+ * Returns a DetectorHit when the event matches, null otherwise. */
+export type DetectorFn = (
+ event: NormalizedToolEvent,
+ state: DetectorSessionState,
+) => DetectorHit | null;
+
+/** Audit-only detector definition. */
+export interface Detector {
+ name: string;
+ description: string;
+ /** Display group in the markdown report (Wasteful / Risky / …). */
+ category: string;
+ /** Severity tier — drives row color in the table. */
+ severity: "warn" | "info";
+ detect: DetectorFn;
+}
+
+/** Aggregated count for one policy or detector. */
+export interface AuditCount {
+ /** Either a policy name ("failproofai/block-force-push") or a detector name
+ * ("redundant-cd-cwd"). */
+ name: string;
+ /** "builtin" for replayed policies, "audit-detector" for audit-only patterns. */
+ source: "builtin" | "audit-detector";
+ category: string;
+ /** Decision label for builtin replays (deny|instruct|allow|sanitize). For
+ * audit-detector entries this is the detector's severity. */
+ severity: string;
+ hits: number;
+ /** Number of distinct projects (transcript folders) where this fired. */
+ projects: number;
+ /** ISO-8601 timestamp of the first matching event seen. */
+ firstSeen?: string;
+ /** ISO-8601 timestamp of the last matching event seen. */
+ lastSeen?: string;
+ /** Up to N example commands/snippets that triggered this (80 chars each). */
+ examples: { sessionId: string; cwd: string; timestamp: string; example: string }[];
+}
+
+/** Per-transcript scan result (also the per-transcript cache value). */
+export interface TranscriptAuditResult {
+ /** Cache key: matches TranscriptMetadata.transcriptPath. */
+ transcriptPath: string;
+ cli: IntegrationType;
+ projectName: string;
+ sessionId: string;
+ mtimeMs: number;
+ sizeBytes: number;
+ /** Per-policy/detector hit count for this one transcript. */
+ hitsByName: Record;
+ /** Up to 3 example commands per policy/detector (later coalesced upstream). */
+ examplesByName: Record;
+ /** First/last timestamp per name. */
+ rangeByName: Record;
+}
+
+/** Top-level result of `runAudit()`. */
+export interface AuditResult {
+ /** Schema version of this JSON shape. Increment on incompatible changes. */
+ version: number;
+ scannedAt: string;
+ scope: {
+ cli: IntegrationType[];
+ projects: string[] | "all";
+ since: string | null;
+ };
+ transcripts: {
+ scanned: number;
+ skipped: number;
+ errors: number;
+ durationMs: number;
+ };
+ results: AuditCount[];
+ totals: {
+ hits: number;
+ projectsWithHits: number;
+ };
+}
+
+/** CLI-supplied options for `runAudit()`. Set by `bin/failproofai.mjs`. */
+export interface RunAuditOptions {
+ /** Restrict to one or more CLIs. Default: all 7. */
+ clis?: IntegrationType[];
+ /** Restrict to sessions whose cwd matches one of these paths. */
+ projects?: string[];
+ /** "7d", "30d", or an ISO date. Filters on transcript mtime. */
+ since?: string;
+ /** Restrict to one or more policy/detector names. */
+ policies?: string[];
+ /** Top-N rows in the table output. */
+ limit?: number;
+ /** Include example column in the table. */
+ showExamples?: boolean;
+ /** Output path for the markdown report. Default: ./failproofai-audit.md */
+ reportPath?: string;
+ /** Skip writing the markdown report. */
+ noReport?: boolean;
+ /** Emit JSON to stdout instead of the table. */
+ json?: boolean;
+ /** Bypass the per-transcript cache. */
+ noCache?: boolean;
+}
+
+/** Truncation budget for `NormalizedToolEvent.toolResultText`. Bounds memory
+ * for sanitize-* policy replay without losing the typical match window. */
+export const AUDIT_TOOL_RESULT_MAX_BYTES = 64 * 1024;
+
+/** Truncation budget for example strings shown in table + markdown. */
+export const AUDIT_EXAMPLE_MAX_CHARS = 80;
+
+/** Max examples kept per policy/detector in the final report. */
+export const AUDIT_MAX_EXAMPLES_PER_NAME = 3;
diff --git a/src/hooks/handler.ts b/src/hooks/handler.ts
index 06f62a1..f32db17 100644
--- a/src/hooks/handler.ts
+++ b/src/hooks/handler.ts
@@ -19,15 +19,8 @@ import {
CURSOR_EVENT_MAP,
PI_EVENT_MAP,
GEMINI_EVENT_MAP,
- GEMINI_TOOL_MAP,
- COPILOT_TOOL_MAP,
- CURSOR_TOOL_MAP,
- CODEX_TOOL_MAP,
- OPENCODE_TOOL_MAP,
- OPENCODE_TOOL_INPUT_MAP,
- PI_TOOL_MAP,
- PI_TOOL_INPUT_MAP,
} from "./types";
+import { canonicalizeToolName, canonicalizeToolInput } from "./tool-name-canonicalize";
import type { PolicyFunction, PolicyResult } from "./policy-types";
import { readMergedHooksConfig } from "./hooks-config";
import { registerBuiltinPolicies } from "./builtin-policies";
@@ -77,80 +70,6 @@ function canonicalizeEventType(raw: string, cli: IntegrationType): HookEventType
return raw as HookEventType;
}
-/**
- * Canonicalize a per-CLI tool name to the Claude PascalCase form that builtin
- * policies match on (e.g. `Bash`, `Read`, `Write`, `Edit`). The registry filter
- * at policy-registry.ts:93-95 is case-sensitive `Array.includes`, so any
- * mismatch silently no-ops every Bash/Read/Write/Edit builtin.
- *
- * Per-CLI tool-name shapes (verified from in-repo evidence and vendor docs):
- * • Claude: PascalCase native — passthrough
- * • Codex: `Bash` PascalCase passthrough; `apply_patch` → `Edit`,
- * `write_stdin` → `Bash` via CODEX_TOOL_MAP
- * • Copilot: lowercase IDs (`bash`, `read`, `view`, …) — COPILOT_TOOL_MAP
- * • Cursor: PascalCase per Cursor docs but uses `Shell` for the bash-
- * equivalent — CURSOR_TOOL_MAP rewrites `Shell → Bash`; other
- * tool names already canonical and pass through
- * • OpenCode: lowercase IDs (`bash`, `read`, …) — OPENCODE_TOOL_MAP. The
- * OpenCode plugin shim ALSO canonicalizes inline as defense-in-
- * depth; both passes are idempotent. Handler-side coverage
- * here means a stale user-scope shim that pre-dates #337 still
- * gets the canonicalization, without forcing a re-install.
- * • Pi: lowercase IDs (`bash`, `read`, …) — PI_TOOL_MAP. Same dual-
- * canonicalization story as OpenCode (shim + handler).
- * • Gemini: snake_case — GEMINI_TOOL_MAP
- *
- * Unknown tool names (MCP `mcp_*`, third-party extensions, Skills) pass
- * through unchanged so non-builtin tooling isn't lost.
- */
-function canonicalizeToolName(raw: string | undefined, cli: IntegrationType): string | undefined {
- if (!raw) return raw;
- if (cli === "copilot") return COPILOT_TOOL_MAP[raw] ?? raw;
- if (cli === "cursor") return CURSOR_TOOL_MAP[raw] ?? raw;
- if (cli === "codex") return CODEX_TOOL_MAP[raw] ?? raw;
- if (cli === "gemini") return GEMINI_TOOL_MAP[raw] ?? raw;
- if (cli === "opencode") return OPENCODE_TOOL_MAP[raw] ?? raw;
- if (cli === "pi") return PI_TOOL_MAP[raw] ?? raw;
- return raw;
-}
-
-/**
- * Canonicalize per-CLI tool-input keys to the snake_case shape that builtin
- * policies read (e.g. `file_path`, `old_string`). OpenCode delivers args as
- * camelCase (`filePath`, `oldString`, `newString`, `replaceAll`); Pi delivers
- * `path` for Read/Write/Edit. Without translation, `getFilePath()` reads "" and
- * the path-checking builtins (`block-read-outside-cwd`, `block-env-files`,
- * `block-secrets-write`) silently no-op.
- *
- * Both CLIs' shims canonicalize inline before the JSON crosses to this binary.
- * Handler-side coverage here is defense-in-depth: a user-scope shim that pre-
- * dates #337 still passes the raw camelCase keys, and we want those installs
- * to start enforcing the moment failproofai upgrades — without requiring a
- * `failproofai policies --install --cli opencode` re-run.
- *
- * Idempotent: when the shim already canonicalized, the keys are snake_case
- * and the per-tool map's camelCase keys don't match, so the loop is a no-op.
- *
- * Tools outside the per-CLI map (MCP `mcp_*`, third-party extensions) pass
- * through unchanged so their schemas aren't corrupted.
- */
-function canonicalizeToolInput(
- toolName: string | undefined,
- rawInput: unknown,
- cli: IntegrationType,
-): unknown {
- if (!toolName || !rawInput || typeof rawInput !== "object") return rawInput;
- let perToolMap: Record | undefined;
- if (cli === "opencode") perToolMap = OPENCODE_TOOL_INPUT_MAP[toolName];
- else if (cli === "pi") perToolMap = PI_TOOL_INPUT_MAP[toolName];
- if (!perToolMap) return rawInput;
- const out: Record = {};
- for (const [k, v] of Object.entries(rawInput as Record)) {
- out[perToolMap[k] ?? k] = v;
- }
- return out;
-}
-
export async function handleHookEvent(
eventType: string,
cli: IntegrationType = "claude",
diff --git a/src/hooks/tool-name-canonicalize.ts b/src/hooks/tool-name-canonicalize.ts
new file mode 100644
index 0000000..1750168
--- /dev/null
+++ b/src/hooks/tool-name-canonicalize.ts
@@ -0,0 +1,60 @@
+/**
+ * Per-CLI canonicalization of tool names and tool-input keys.
+ *
+ * Extracted from handler.ts so the audit replay engine and the live hook
+ * handler share one implementation. Re-importing this module from
+ * `src/audit/cli-adapters/*.ts` keeps the per-CLI maps in one place.
+ */
+import type { IntegrationType } from "./types";
+import {
+ CODEX_TOOL_MAP,
+ COPILOT_TOOL_MAP,
+ CURSOR_TOOL_MAP,
+ OPENCODE_TOOL_MAP,
+ OPENCODE_TOOL_INPUT_MAP,
+ PI_TOOL_MAP,
+ PI_TOOL_INPUT_MAP,
+ GEMINI_TOOL_MAP,
+} from "./types";
+
+/**
+ * Canonicalize a per-CLI tool name to the Claude PascalCase form that builtin
+ * policies match on (e.g. `Bash`, `Read`, `Write`, `Edit`). Unknown tool names
+ * (MCP `mcp_*`, third-party extensions, Skills) pass through unchanged.
+ */
+export function canonicalizeToolName(
+ raw: string | undefined,
+ cli: IntegrationType,
+): string | undefined {
+ if (!raw) return raw;
+ if (cli === "copilot") return COPILOT_TOOL_MAP[raw] ?? raw;
+ if (cli === "cursor") return CURSOR_TOOL_MAP[raw] ?? raw;
+ if (cli === "codex") return CODEX_TOOL_MAP[raw] ?? raw;
+ if (cli === "gemini") return GEMINI_TOOL_MAP[raw] ?? raw;
+ if (cli === "opencode") return OPENCODE_TOOL_MAP[raw] ?? raw;
+ if (cli === "pi") return PI_TOOL_MAP[raw] ?? raw;
+ return raw;
+}
+
+/**
+ * Canonicalize per-CLI tool-input keys to the snake_case shape that builtin
+ * policies read (e.g. `file_path`, `old_string`). OpenCode delivers args as
+ * camelCase; Pi delivers `path` for Read/Write/Edit. Idempotent — when already
+ * canonical the loop is a no-op.
+ */
+export function canonicalizeToolInput(
+ toolName: string | undefined,
+ rawInput: unknown,
+ cli: IntegrationType,
+): unknown {
+ if (!toolName || !rawInput || typeof rawInput !== "object") return rawInput;
+ let perToolMap: Record | undefined;
+ if (cli === "opencode") perToolMap = OPENCODE_TOOL_INPUT_MAP[toolName];
+ else if (cli === "pi") perToolMap = PI_TOOL_INPUT_MAP[toolName];
+ if (!perToolMap) return rawInput;
+ const out: Record = {};
+ for (const [k, v] of Object.entries(rawInput as Record)) {
+ out[perToolMap[k] ?? k] = v;
+ }
+ return out;
+}
From 328cf95a4833dea1535de0f8a5e8a9cfa2f90061 Mon Sep 17 00:00:00 2001
From: NiveditJain
Date: Thu, 21 May 2026 19:40:44 -0700
Subject: [PATCH 2/9] docs: add CLI reference page for `failproofai audit`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds docs/cli/audit.mdx documenting flags, the 8 audit-only detectors,
output formats (table / markdown / JSON), and the read-only / no-mutation
guarantees. Registers the page in the English CLI nav in docs/docs.json.
Translations will follow in the next translation pass (per the team's
existing translation workflow — see #371).
Co-Authored-By: Claude Opus 4.7
---
docs/cli/audit.mdx | 96 ++++++++++++++++++++++++++++++++++++++++++++++
docs/docs.json | 1 +
2 files changed, 97 insertions(+)
create mode 100644 docs/cli/audit.mdx
diff --git a/docs/cli/audit.mdx b/docs/cli/audit.mdx
new file mode 100644
index 0000000..79ead3f
--- /dev/null
+++ b/docs/cli/audit.mdx
@@ -0,0 +1,96 @@
+---
+title: Audit past sessions
+description: "Count how often the agent did wasteful or risky things across past transcripts"
+---
+
+```bash
+failproofai audit [options]
+```
+
+Scans past agent CLI transcripts on this machine (Claude Code, Codex, Copilot, Cursor, OpenCode, Pi, Gemini) and reports how often the agent did things failproofai is built to stop — env-var checks, force pushes, redundant `cd ` prefixes, sleep-polling loops, re-reading files just edited, and more.
+
+For each transcript, every tool-use event is replayed through the 39 builtin policies **and** through 8 audit-only detectors that catch patterns not yet covered by runtime policies. Counts are aggregated per policy / detector across all sessions.
+
+## Options
+
+| Flag | Description |
+|------|-------------|
+| `--cli claude\|codex\|copilot\|cursor\|opencode\|pi\|gemini` | Restrict to one or more CLIs (space-separated or repeated). Default: all 7. |
+| `--project ` | Restrict to sessions whose `cwd` matches this path. Repeatable. |
+| `--since 7d\|30d\|2026-04-01` | Only sessions whose transcript mtime is within the window. |
+| `--policy ` | Restrict to one policy or detector name. Repeatable. |
+| `--limit N` | Top-N rows in the table (default 20). |
+| `--show-examples` | Include one example command per row in the table. |
+| `--report ` | Markdown report path (default `./failproofai-audit.md`). |
+| `--no-report` | Skip writing the markdown report. |
+| `--json` | Print JSON to stdout instead of the table. Implies `--no-report` unless `--report` is also passed. |
+| `--no-cache` | Bypass the per-transcript result cache at `~/.failproofai/cache/audit/`. |
+
+## Audit-only detectors
+
+These detect "stupid behavior" patterns not (yet) enforced in real time. They run only during `audit` and never block a live tool call.
+
+| Detector | What it counts |
+|---|---|
+| `redundant-cd-cwd` | Bash commands starting with `cd && …` even though commands already run in `cwd`. |
+| `prefer-edit-over-read-cat` | `cat`/`head`/`tail`/`less`/`more` on a single source file — use the `Read` tool. |
+| `prefer-edit-over-sed-awk` | `sed -i` / `awk … > file` in-place edits — use the `Edit` tool. |
+| `prefer-write-over-heredoc` | Heredoc / multi-line `echo > file` writing files — use the `Write` tool. |
+| `sleep-polling-loop` | Long `sleep N` (≥ 30s) or `while …; sleep …; done` polling loops. |
+| `find-from-root` | `find /`, `find /home`, `find /usr`, etc. — scope to `cwd` instead. |
+| `git-commit-no-verify` | `git commit … --no-verify` / `-n`, skipping hooks. |
+| `reread-after-edit` | `Read` of a file that was just `Edit`/`Write` in the same session. |
+
+## Output
+
+Default output is an ANSI table to stdout plus a sectioned markdown report at `./failproofai-audit.md`. Both group hits by category (Sanitize / Wasteful / Risky / …) with hit counts, project counts, and up to three example commands per row.
+
+`--json` emits machine-readable output:
+
+```json
+{
+ "version": 1,
+ "scannedAt": "2026-05-21T...",
+ "scope": { "cli": [...], "projects": "all", "since": null },
+ "transcripts": { "scanned": 4187, "skipped": 0, "errors": 0, "durationMs": 12345 },
+ "results": [
+ {
+ "name": "failproofai/protect-env-vars",
+ "source": "builtin",
+ "category": "Environment",
+ "hits": 428,
+ "projects": 142,
+ "firstSeen": "...",
+ "lastSeen": "...",
+ "examples": [{ "sessionId": "...", "cwd": "...", "timestamp": "...", "example": "env" }]
+ }
+ ],
+ "totals": { "hits": 1398, "projectsWithHits": 392 }
+}
+```
+
+## Examples
+
+```bash
+# Scan everything across every CLI on this machine
+failproofai audit
+
+# Just the last 30 days, Claude only
+failproofai audit --cli claude --since 30d
+
+# Show examples for the top hits
+failproofai audit --show-examples --limit 10
+
+# Drill into one policy
+failproofai audit --policy protect-env-vars --policy block-force-push
+
+# Machine-readable output
+failproofai audit --json > audit.json
+```
+
+## Notes
+
+- **Cache.** Per-transcript results are cached at `~/.failproofai/cache/audit/.json` keyed by `(mtime, size, engineVersion, detectorVersion)`. The cache invalidates automatically when policy or detector code changes.
+- **No mutation.** The audit replays in read-only mode. `warn-repeated-tool-calls` is skipped because its per-session sidecar would otherwise be modified.
+- **Workflow policies skipped.** `require-*-before-stop` policies fire only on `Stop` events and `execSync` against the live git state — they have no meaningful "what would have happened in 2025" interpretation, so they don't appear in audit counts.
+- **Custom policies skipped.** User-supplied custom hooks are not replayed (they may have changed since the original session).
diff --git a/docs/docs.json b/docs/docs.json
index 5cdae94..9702a10 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -42,6 +42,7 @@
"cli/install-policies",
"cli/remove-policies",
"cli/list-policies",
+ "cli/audit",
"cli/hook",
"cli/version",
"cli/environment-variables"
From 218c02dcfbdf2be223e9f6d5eec8bbea8c63c570 Mon Sep 17 00:00:00 2001
From: NiveditJain
Date: Thu, 21 May 2026 19:57:42 -0700
Subject: [PATCH 3/9] fix: address CodeRabbit review on PR #377
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
• bin/failproofai.mjs:
- Reject bare `--cli`/`--project`/`--policy` (was silently widening scope)
- Reject `--limit` values that aren't positive integers (NaN/0/negative)
• CHANGELOG.md: condense the audit Features entry to one line + PR number
(#377) per CLAUDE.md changelog convention.
• docs/docs.json: register `cli/audit` in all 13 non-English language nav
sections (was only in English) so nav structure mirrors English ahead of
the translation pass.
• src/audit/cache.ts: pass `mode: 0o600` to writeFileSync at file-creation
time so there's no permission-leak window between create and chmod.
• src/audit/cli-adapters/shared.ts: cap tool_result_text by UTF-8 BYTE
length (was using String.prototype.length which is UTF-16 code units —
let through up to 4× the intended budget for non-ASCII). Safe UTF-8
boundary walk so multi-byte sequences aren't split.
• src/audit/detectors/prefer-write-over-heredoc.ts: heredoc delimiter
regex now accepts `[A-Za-z_][A-Za-z0-9_]*` instead of only `[A-Z_]+`
so `cat <<'eof' > file` is detected.
• src/audit/detectors/sleep-polling-loop.ts: use parseFloat so
`sleep 0.5m` (= 30s, exactly at the threshold) is no longer dropped.
• src/audit/index.ts: let scanOneTranscript propagate streamEvents errors
so the orchestrator's outer try/catch correctly increments `errors`
(was silently returning an empty result, making audit reliability
stats wrong).
• src/audit/report.ts: escape `|`, `\`, and CR/LF in markdown table cells
so a policy name containing `|` doesn't break the table layout.
• src/hooks/tool-name-canonicalize.ts: guard canonicalizeToolInput against
Array inputs (was flattening them into a plain object with numeric keys
— pre-existing handler.ts behavior, hardened on extraction).
Co-Authored-By: Claude Opus 4.7
---
CHANGELOG.md | 2 +-
bin/failproofai.mjs | 18 ++++++++++++++++--
docs/docs.json | 14 ++++++++++++++
src/audit/cache.ts | 5 ++++-
src/audit/cli-adapters/shared.ts | 19 +++++++++++++++----
.../detectors/prefer-write-over-heredoc.ts | 4 +++-
src/audit/detectors/sleep-polling-loop.ts | 6 +++---
src/audit/index.ts | 9 +++------
src/audit/report.ts | 8 +++++++-
src/hooks/tool-name-canonicalize.ts | 7 ++++++-
10 files changed, 72 insertions(+), 20 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 196565f..80ba38b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@
## 0.0.11-beta.2 — 2026-05-21
### Features
-- Add `failproofai audit` command — retrospectively scan past agent CLI transcripts (Claude, Codex, Copilot, Cursor, OpenCode, Pi, Gemini) and count how many times the agent did wasteful or risky things failproofai is built to stop. Replays every transcript's tool-use events through the existing 39 builtin policies and through 8 new audit-only detectors (`redundant-cd-cwd`, `prefer-edit-over-read-cat`, `prefer-edit-over-sed-awk`, `prefer-write-over-heredoc`, `sleep-polling-loop`, `find-from-root`, `git-commit-no-verify`, `reread-after-edit`) that catch behaviors not yet covered by runtime policies. Prints an ANSI table to stdout and writes a sectioned markdown report to `./failproofai-audit.md`. Honors `--cli`, `--project`, `--since`, `--policy`, `--limit`, `--show-examples`, `--report`, `--no-report`, `--json`, `--no-cache`. Per-transcript results cached at `~/.failproofai/cache/audit/` keyed by `(mtime, size, engineVersion, detectorVersion)` so the cache invalidates automatically when policy or detector code changes. Refactored per-CLI tool-name + tool-input canonicalization out of `src/hooks/handler.ts` into `src/hooks/tool-name-canonicalize.ts` so the audit replay and live handler share one implementation.
+- Add `failproofai audit` command — retrospectively scan past agent transcripts across all 7 CLIs and report wasteful/risky behavior via the 39 builtin policies + 8 new audit-only detectors (`redundant-cd-cwd`, `prefer-edit-over-read-cat`, `prefer-edit-over-sed-awk`, `prefer-write-over-heredoc`, `sleep-polling-loop`, `find-from-root`, `git-commit-no-verify`, `reread-after-edit`). Outputs ANSI table + markdown report; supports `--cli`, `--project`, `--since`, `--policy`, `--limit`, `--show-examples`, `--report`, `--no-report`, `--json`, `--no-cache`. Per-transcript cache at `~/.failproofai/cache/audit/` auto-invalidates on policy/detector code changes (#377).
### Breaking
- Remove the undocumented cloud auth + event relay subsystem ahead of a from-scratch redesign. Deletes `src/auth/` (OAuth 2.0 device-flow login against `api.befailproof.ai`, `~/.failproofai/auth.json` token store) and `src/relay/` (WebSocket event relay daemon, sanitized JSONL queue at `~/.failproofai/cache/server-queue/`, PID tracking). Strips the `failproofai login` / `logout` / `whoami` / `relay start|stop|status` / `sync` subcommands and the internal `--relay-daemon` mode from `bin/failproofai.mjs`, along with their `--help` entries and "did you mean" suggestions. Removes the fire-and-forget `appendToServerQueue` + `ensureRelayRunning` calls from `src/hooks/handler.ts` so hook evaluation no longer enqueues events or lazy-spawns a daemon. The whole subsystem had zero references in `README.md`, `docs/`, `examples/`, or `__tests__/`, and only had internal cross-imports — `tsc`, `eslint`, `vitest` (1623 tests), and the `bun run build` bundles all stay green. Users who ran `failproofai login` should also wipe `~/.failproofai/{auth.json,cache/server-queue,relay.pid}` and stop any running relay daemon by hand; new auth/cloud surface will land in a follow-up.
diff --git a/bin/failproofai.mjs b/bin/failproofai.mjs
index 0c4c46a..9416ae3 100755
--- a/bin/failproofai.mjs
+++ b/bin/failproofai.mjs
@@ -465,20 +465,27 @@ EXAMPLES
const VALID_CLIS = new Set(["claude", "codex", "copilot", "cursor", "opencode", "pi", "gemini"]);
/** Consume one or more values for a flag like `--cli a b c`, stopping at
- * the next flag or an unknown token (when an allow-set is supplied). */
+ * the next flag or an unknown token (when an allow-set is supplied).
+ * Throws when the flag appears with zero following values — a bare
+ * `--cli` would otherwise silently widen the scope to all CLIs. */
function collectMulti(flag, allowSet) {
const out = [];
const consumed = new Set();
for (let i = 0; i < subArgs.length; i++) {
if (subArgs[i] !== flag) continue;
+ let seenForThisFlag = 0;
for (let j = i + 1; j < subArgs.length; j++) {
const v = subArgs[j];
if (v.startsWith("-")) break;
if (allowSet && !allowSet.has(v)) break;
out.push(v);
consumed.add(j);
+ seenForThisFlag++;
}
consumed.add(i);
+ if (seenForThisFlag === 0) {
+ throw new CliError(`Missing value(s) for ${flag}`);
+ }
}
return { values: out, consumed };
}
@@ -525,12 +532,19 @@ EXAMPLES
);
}
+ let parsedLimit;
+ if (limitRes.value !== undefined) {
+ parsedLimit = parseInt(limitRes.value, 10);
+ if (!Number.isInteger(parsedLimit) || parsedLimit <= 0) {
+ throw new CliError(`Invalid value for --limit: "${limitRes.value}". Use a positive integer.`);
+ }
+ }
const opts = {
clis: cliRes.values.length > 0 ? cliRes.values : undefined,
projects: projectRes.values.length > 0 ? projectRes.values : undefined,
policies: policyRes.values.length > 0 ? policyRes.values : undefined,
since: sinceRes.value,
- limit: limitRes.value !== undefined ? parseInt(limitRes.value, 10) : undefined,
+ limit: parsedLimit,
showExamples,
reportPath: reportRes.value ?? "./failproofai-audit.md",
noReport: noReport || jsonOut, // --json implies --no-report unless explicit --report
diff --git a/docs/docs.json b/docs/docs.json
index 9702a10..ac2c44a 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -106,6 +106,7 @@
"zh/cli/install-policies",
"zh/cli/remove-policies",
"zh/cli/list-policies",
+ "zh/cli/audit",
"zh/cli/hook",
"zh/cli/version",
"zh/cli/environment-variables"
@@ -169,6 +170,7 @@
"ja/cli/install-policies",
"ja/cli/remove-policies",
"ja/cli/list-policies",
+ "ja/cli/audit",
"ja/cli/hook",
"ja/cli/version",
"ja/cli/environment-variables"
@@ -232,6 +234,7 @@
"ko/cli/install-policies",
"ko/cli/remove-policies",
"ko/cli/list-policies",
+ "ko/cli/audit",
"ko/cli/hook",
"ko/cli/version",
"ko/cli/environment-variables"
@@ -295,6 +298,7 @@
"es/cli/install-policies",
"es/cli/remove-policies",
"es/cli/list-policies",
+ "es/cli/audit",
"es/cli/hook",
"es/cli/version",
"es/cli/environment-variables"
@@ -358,6 +362,7 @@
"pt-br/cli/install-policies",
"pt-br/cli/remove-policies",
"pt-br/cli/list-policies",
+ "pt-br/cli/audit",
"pt-br/cli/hook",
"pt-br/cli/version",
"pt-br/cli/environment-variables"
@@ -421,6 +426,7 @@
"de/cli/install-policies",
"de/cli/remove-policies",
"de/cli/list-policies",
+ "de/cli/audit",
"de/cli/hook",
"de/cli/version",
"de/cli/environment-variables"
@@ -484,6 +490,7 @@
"fr/cli/install-policies",
"fr/cli/remove-policies",
"fr/cli/list-policies",
+ "fr/cli/audit",
"fr/cli/hook",
"fr/cli/version",
"fr/cli/environment-variables"
@@ -547,6 +554,7 @@
"ru/cli/install-policies",
"ru/cli/remove-policies",
"ru/cli/list-policies",
+ "ru/cli/audit",
"ru/cli/hook",
"ru/cli/version",
"ru/cli/environment-variables"
@@ -610,6 +618,7 @@
"hi/cli/install-policies",
"hi/cli/remove-policies",
"hi/cli/list-policies",
+ "hi/cli/audit",
"hi/cli/hook",
"hi/cli/version",
"hi/cli/environment-variables"
@@ -673,6 +682,7 @@
"tr/cli/install-policies",
"tr/cli/remove-policies",
"tr/cli/list-policies",
+ "tr/cli/audit",
"tr/cli/hook",
"tr/cli/version",
"tr/cli/environment-variables"
@@ -736,6 +746,7 @@
"vi/cli/install-policies",
"vi/cli/remove-policies",
"vi/cli/list-policies",
+ "vi/cli/audit",
"vi/cli/hook",
"vi/cli/version",
"vi/cli/environment-variables"
@@ -799,6 +810,7 @@
"it/cli/install-policies",
"it/cli/remove-policies",
"it/cli/list-policies",
+ "it/cli/audit",
"it/cli/hook",
"it/cli/version",
"it/cli/environment-variables"
@@ -862,6 +874,7 @@
"ar/cli/install-policies",
"ar/cli/remove-policies",
"ar/cli/list-policies",
+ "ar/cli/audit",
"ar/cli/hook",
"ar/cli/version",
"ar/cli/environment-variables"
@@ -925,6 +938,7 @@
"he/cli/install-policies",
"he/cli/remove-policies",
"he/cli/list-policies",
+ "he/cli/audit",
"he/cli/hook",
"he/cli/version",
"he/cli/environment-variables"
diff --git a/src/audit/cache.ts b/src/audit/cache.ts
index e6e18a9..57668cc 100644
--- a/src/audit/cache.ts
+++ b/src/audit/cache.ts
@@ -95,7 +95,10 @@ export function writeCachedTranscriptResult(
detectorVersion: getDetectorVersion(),
result,
};
- writeFileSync(cachePath, JSON.stringify(entry), "utf-8");
+ // Set 0o600 at file-creation time so there's no window where the file
+ // exists with the umask default (typically 0o644). The chmodSync below is
+ // a belt-and-suspenders pass for the case where the file already existed.
+ writeFileSync(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 0o600 });
try { chmodSync(cachePath, 0o600); } catch { /* best-effort on POSIX */ }
} catch {
// Cache writes are best-effort — never let a cache error kill the audit.
diff --git a/src/audit/cli-adapters/shared.ts b/src/audit/cli-adapters/shared.ts
index 8f17a1a..57bbe84 100644
--- a/src/audit/cli-adapters/shared.ts
+++ b/src/audit/cli-adapters/shared.ts
@@ -18,6 +18,20 @@ import {
type NormalizedToolEvent,
} from "../types";
+/** Truncate a string to at most `maxBytes` UTF-8 bytes, preserving valid
+ * encoding (never splits a multi-byte sequence). `String.prototype.length`
+ * counts UTF-16 code units, not bytes — using it to "cap memory" would let
+ * through up to 4× the intended byte budget for non-ASCII text. */
+function truncateToUtf8Bytes(s: string, maxBytes: number): string {
+ const buf = Buffer.from(s, "utf-8");
+ if (buf.byteLength <= maxBytes) return s;
+ // Walk back at most 3 bytes to land on a UTF-8 boundary (a leading byte is
+ // 0xxxxxxx or 11xxxxxx; continuation bytes are 10xxxxxx).
+ let end = maxBytes;
+ while (end > 0 && (buf[end] & 0xc0) === 0x80) end--;
+ return buf.subarray(0, end).toString("utf-8");
+}
+
export interface ConvertContext {
cli: IntegrationType;
sessionId: string;
@@ -50,10 +64,7 @@ export function logEntriesToEvents(
let toolResultText: string | undefined;
if (block.result?.content) {
- toolResultText =
- block.result.content.length > AUDIT_TOOL_RESULT_MAX_BYTES
- ? block.result.content.slice(0, AUDIT_TOOL_RESULT_MAX_BYTES)
- : block.result.content;
+ toolResultText = truncateToUtf8Bytes(block.result.content, AUDIT_TOOL_RESULT_MAX_BYTES);
}
events.push({
diff --git a/src/audit/detectors/prefer-write-over-heredoc.ts b/src/audit/detectors/prefer-write-over-heredoc.ts
index 8fbac3f..013ccfd 100644
--- a/src/audit/detectors/prefer-write-over-heredoc.ts
+++ b/src/audit/detectors/prefer-write-over-heredoc.ts
@@ -18,7 +18,9 @@ export const preferWriteOverHeredoc: Detector = {
// later `> file` is unrelated)
// The redirect must appear before the next whitespace/newline that ends the
// heredoc opener line — otherwise it's a body or downstream redirect.
- if (/<<-?\s*['"]?[A-Z_]+['"]?\s*>\s*\S/.test(cmd)) {
+ // Heredoc delimiters are case-sensitive but `EOF`, `eof`, `Eof`, `MARKER`
+ // and digits-after-letter are all valid; match any letter/digit/underscore.
+ if (/<<-?\s*['"]?[A-Za-z_][A-Za-z0-9_]*['"]?\s*>\s*\S/.test(cmd)) {
const summary = cmd.replace(/\s+/g, " ").trim().slice(0, 160);
return { example: summary };
}
diff --git a/src/audit/detectors/sleep-polling-loop.ts b/src/audit/detectors/sleep-polling-loop.ts
index 80a2c8b..06579fb 100644
--- a/src/audit/detectors/sleep-polling-loop.ts
+++ b/src/audit/detectors/sleep-polling-loop.ts
@@ -17,10 +17,10 @@ export const sleepPollingLoop: Detector = {
if (/\bwhile\b[\s\S]*?\bsleep\b[\s\S]*?\bdone\b/.test(cmd)) {
return { example: cmd.replace(/\s+/g, " ").trim().slice(0, 160) };
}
- // Standalone long sleep
- const match = /\bsleep\s+(\d+)(?:\.\d+)?(m|h|d)?\b/.exec(cmd);
+ // Standalone long sleep. parseFloat so `sleep 0.5m` (= 30s) isn't dropped.
+ const match = /\bsleep\s+(\d+(?:\.\d+)?)(m|h|d)?\b/.exec(cmd);
if (match) {
- const n = parseInt(match[1], 10);
+ const n = parseFloat(match[1]);
const unit = match[2] ?? "s";
const seconds = unit === "m" ? n * 60 : unit === "h" ? n * 3600 : unit === "d" ? n * 86400 : n;
if (seconds >= SLEEP_THRESHOLD_SECONDS) {
diff --git a/src/audit/index.ts b/src/audit/index.ts
index 4fc4828..e689fc4 100644
--- a/src/audit/index.ts
+++ b/src/audit/index.ts
@@ -77,12 +77,9 @@ async function scanOneTranscript(meta: TranscriptMetadata): Promise | undefined;
if (cli === "opencode") perToolMap = OPENCODE_TOOL_INPUT_MAP[toolName];
else if (cli === "pi") perToolMap = PI_TOOL_INPUT_MAP[toolName];
From db0563552b4259d67cad56f5ecdc432f6ceee460 Mon Sep 17 00:00:00 2001
From: NiveditJain
Date: Thu, 21 May 2026 20:00:16 -0700
Subject: [PATCH 4/9] fix(docs): revert non-English `cli/audit` nav entries to
unblock CI
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Mintlify's `validate` step (the `docs` CI job) rejects nav references
to pages that don't exist on disk. The previous commit pre-registered
`/cli/audit` in all 13 non-English language sections to mirror
the English nav ahead of translations landing — but the translated
pages don't exist yet, so Mintlify failed with 14 "file does not exist"
warnings (treated as build errors).
The project's translation workflow lands the translated pages and the
matching nav entries together in a dedicated translation-pass commit
(see #371), so registering the nav slots upstream of the actual files
breaks CI. Reverting the 13 language entries; the English nav entry
stays.
Co-Authored-By: Claude Opus 4.7
---
docs/docs.json | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/docs/docs.json b/docs/docs.json
index ac2c44a..9702a10 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -106,7 +106,6 @@
"zh/cli/install-policies",
"zh/cli/remove-policies",
"zh/cli/list-policies",
- "zh/cli/audit",
"zh/cli/hook",
"zh/cli/version",
"zh/cli/environment-variables"
@@ -170,7 +169,6 @@
"ja/cli/install-policies",
"ja/cli/remove-policies",
"ja/cli/list-policies",
- "ja/cli/audit",
"ja/cli/hook",
"ja/cli/version",
"ja/cli/environment-variables"
@@ -234,7 +232,6 @@
"ko/cli/install-policies",
"ko/cli/remove-policies",
"ko/cli/list-policies",
- "ko/cli/audit",
"ko/cli/hook",
"ko/cli/version",
"ko/cli/environment-variables"
@@ -298,7 +295,6 @@
"es/cli/install-policies",
"es/cli/remove-policies",
"es/cli/list-policies",
- "es/cli/audit",
"es/cli/hook",
"es/cli/version",
"es/cli/environment-variables"
@@ -362,7 +358,6 @@
"pt-br/cli/install-policies",
"pt-br/cli/remove-policies",
"pt-br/cli/list-policies",
- "pt-br/cli/audit",
"pt-br/cli/hook",
"pt-br/cli/version",
"pt-br/cli/environment-variables"
@@ -426,7 +421,6 @@
"de/cli/install-policies",
"de/cli/remove-policies",
"de/cli/list-policies",
- "de/cli/audit",
"de/cli/hook",
"de/cli/version",
"de/cli/environment-variables"
@@ -490,7 +484,6 @@
"fr/cli/install-policies",
"fr/cli/remove-policies",
"fr/cli/list-policies",
- "fr/cli/audit",
"fr/cli/hook",
"fr/cli/version",
"fr/cli/environment-variables"
@@ -554,7 +547,6 @@
"ru/cli/install-policies",
"ru/cli/remove-policies",
"ru/cli/list-policies",
- "ru/cli/audit",
"ru/cli/hook",
"ru/cli/version",
"ru/cli/environment-variables"
@@ -618,7 +610,6 @@
"hi/cli/install-policies",
"hi/cli/remove-policies",
"hi/cli/list-policies",
- "hi/cli/audit",
"hi/cli/hook",
"hi/cli/version",
"hi/cli/environment-variables"
@@ -682,7 +673,6 @@
"tr/cli/install-policies",
"tr/cli/remove-policies",
"tr/cli/list-policies",
- "tr/cli/audit",
"tr/cli/hook",
"tr/cli/version",
"tr/cli/environment-variables"
@@ -746,7 +736,6 @@
"vi/cli/install-policies",
"vi/cli/remove-policies",
"vi/cli/list-policies",
- "vi/cli/audit",
"vi/cli/hook",
"vi/cli/version",
"vi/cli/environment-variables"
@@ -810,7 +799,6 @@
"it/cli/install-policies",
"it/cli/remove-policies",
"it/cli/list-policies",
- "it/cli/audit",
"it/cli/hook",
"it/cli/version",
"it/cli/environment-variables"
@@ -874,7 +862,6 @@
"ar/cli/install-policies",
"ar/cli/remove-policies",
"ar/cli/list-policies",
- "ar/cli/audit",
"ar/cli/hook",
"ar/cli/version",
"ar/cli/environment-variables"
@@ -938,7 +925,6 @@
"he/cli/install-policies",
"he/cli/remove-policies",
"he/cli/list-policies",
- "he/cli/audit",
"he/cli/hook",
"he/cli/version",
"he/cli/environment-variables"
From 199c377459494906071a097d0503028a3f11dc27 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Fri, 22 May 2026 03:28:56 +0000
Subject: [PATCH 5/9] docs: update translations for changed English sources
---
docs/ar/cli/environment-variables.mdx | 20 +++++---
docs/ar/introduction.mdx | 27 +++++------
docs/cli/environment-variables.mdx | 6 +++
docs/de/cli/environment-variables.mdx | 18 ++++---
docs/de/introduction.mdx | 22 ++++-----
docs/docs.json | 1 -
docs/es/cli/environment-variables.mdx | 18 ++++---
docs/es/introduction.mdx | 20 ++++----
docs/fr/cli/environment-variables.mdx | 14 ++++--
docs/fr/introduction.mdx | 20 ++++----
docs/he/cli/environment-variables.mdx | 47 ++++++++++--------
docs/he/introduction.mdx | 32 ++++++------
docs/hi/cli/environment-variables.mdx | 46 ++++++++++--------
docs/hi/introduction.mdx | 28 +++++------
docs/i18n/README.ar.md | 46 +++++++++---------
docs/i18n/README.de.md | 32 ++++++------
docs/i18n/README.es.md | 34 ++++++-------
docs/i18n/README.fr.md | 36 +++++++-------
docs/i18n/README.he.md | 62 ++++++++++++------------
docs/i18n/README.hi.md | 58 +++++++++++-----------
docs/i18n/README.it.md | 50 +++++++++----------
docs/i18n/README.ja.md | 42 ++++++++--------
docs/i18n/README.ko.md | 52 ++++++++++----------
docs/i18n/README.pt-br.md | 14 +++---
docs/i18n/README.ru.md | 42 ++++++++--------
docs/i18n/README.tr.md | 54 ++++++++++-----------
docs/i18n/README.vi.md | 46 +++++++++---------
docs/i18n/README.zh.md | 36 +++++++-------
docs/introduction.mdx | 2 +-
docs/it/cli/environment-variables.mdx | 24 +++++----
docs/it/introduction.mdx | 28 +++++------
docs/ja/cli/environment-variables.mdx | 22 ++++++---
docs/ja/introduction.mdx | 26 +++++-----
docs/ko/cli/environment-variables.mdx | 16 ++++--
docs/ko/introduction.mdx | 24 ++++-----
docs/pt-br/cli/environment-variables.mdx | 8 ++-
docs/pt-br/introduction.mdx | 22 ++++-----
docs/ru/cli/environment-variables.mdx | 16 ++++--
docs/ru/introduction.mdx | 24 ++++-----
docs/tr/cli/environment-variables.mdx | 22 ++++++---
docs/tr/introduction.mdx | 30 ++++++------
docs/vi/cli/environment-variables.mdx | 18 ++++---
docs/vi/introduction.mdx | 24 ++++-----
docs/zh/cli/environment-variables.mdx | 16 ++++--
docs/zh/introduction.mdx | 24 ++++-----
45 files changed, 678 insertions(+), 591 deletions(-)
diff --git a/docs/ar/cli/environment-variables.mdx b/docs/ar/cli/environment-variables.mdx
index 14257b4..3d5ab88 100644
--- a/docs/ar/cli/environment-variables.mdx
+++ b/docs/ar/cli/environment-variables.mdx
@@ -1,6 +1,6 @@
---
title: متغيرات البيئة
-description: "قم بتكوين سلوك failproofai باستخدام متغيرات البيئة"
+description: "تكوين سلوك failproofai باستخدام متغيرات البيئة"
---
## لوحة التحكم
@@ -10,25 +10,31 @@ description: "قم بتكوين سلوك failproofai باستخدام متغير
| `PORT` | منفذ لوحة التحكم (الافتراضي: `8020`) |
| `CLAUDE_PROJECTS_PATH` | تجاوز موقع مجلدات مشاريع Claude Code |
| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | صفحات لوحة التحكم المفصولة بفواصل لإخفاؤها |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | المضيفات/عناوين IP المسموح لها بالوصول إلى موارد التطوير. نفس `--allowed-origins`. |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | المضيفات/عناوين IP المسموح لها بالوصول إلى موارد التطوير. مماثل لـ `--allowed-origins`. |
## تسجيل السجلات
| المتغير | الوصف |
|----------|-------------|
| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | مستوى سجل الخادم (الافتراضي: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | مسار ملف السجل المخصص للخطاف، أو `true` للإعداد الافتراضي (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | مسار ملف سجل خطاف مخصص، أو `true` للافتراضي (`~/.failproofai/logs/hooks.log`) |
-## قياس الاستخدام
+## القياس عن بعد
| المتغير | الوصف |
|----------|-------------|
| `FAILPROOFAI_TELEMETRY_DISABLED=1` | تعطيل قياس الاستخدام المجهول |
-## نموذج اللغة (لتقييم السياسات)
+## مطالبة التشغيل الأول
| المتغير | الوصف |
|----------|-------------|
-| `FAILPROOFAI_LLM_BASE_URL` | نقطة نهاية واجهة برمجة التطبيقات لنموذج اللغة (الافتراضي: `https://api.openai.com/v1`) |
-| `FAILPROOFAI_LLM_API_KEY` | مفتاح واجهة برمجة التطبيقات للسياسات المدعومة بنموذج اللغة |
+| `FAILPROOFAI_NO_FIRST_RUN=1` | تخطي المطالبة التي تعرض تثبيت السياسات عند استدعاء `failproofai` الأول الفارغ |
+
+## نموذج لغة (لتقييم السياسة)
+
+| المتغير | الوصف |
+|----------|-------------|
+| `FAILPROOFAI_LLM_BASE_URL` | نقطة نهاية API لنموذج اللغة (الافتراضي: `https://api.openai.com/v1`) |
+| `FAILPROOFAI_LLM_API_KEY` | مفتاح API للسياسات المدعومة بنموذج اللغة |
| `FAILPROOFAI_LLM_MODEL` | اسم النموذج (الافتراضي: `gpt-4o-mini`) |
\ No newline at end of file
diff --git a/docs/ar/introduction.mdx b/docs/ar/introduction.mdx
index 1a74a9d..ac044f4 100644
--- a/docs/ar/introduction.mdx
+++ b/docs/ar/introduction.mdx
@@ -1,42 +1,41 @@
---
----
title: "Failproof AI"
-description: "FailproofAI يمنح وكلاء الذكاء الاصطناعي 39 سياسة فشل مدمجة تكتشف الحلقات، تسريب الأسرار، استدعاءات الأدوات المدمرة، والمزيد في تثبيت واحد."
+description: "FailproofAI يوفر 39 سياسة فشل مدمجة للعملاء الذين يعملون بالذكاء الاصطناعي تعترض الحلقات، وتسرب الأسرار، واستدعاءات الأدوات المدمرة، والمزيد في تثبيت واحد فقط."
---
[](https://www.npmjs.com/package/failproofai)
-خطافات وسياسات لـ **معالجة فشل الذكاء الاصطناعي**، **استعادة الأخطاء**، و **موثوقية نموذج اللغة الكبير**. حافظ على موثوقية وكلاء الذكاء الاصطناعي الخاصة بك وتشغيلهم بشكل مستقل عبر **Claude Code**، **OpenAI Codex**، **GitHub Copilot**، **Cursor Agent**، **OpenCode**، **Pi**، **Gemini CLI**، و **Agents SDK**.
+خطافات وسياسات لـ **معالجة فشل الذكاء الاصطناعي** و**استرجاع الأخطاء** و**موثوقية النماذج اللغوية الكبيرة**. اجعل عملاء الذكاء الاصطناعي لديك موثوقين وتشغيلهم بشكل مستقل عبر **Claude Code** و**OpenAI Codex** و**GitHub Copilot** و**Cursor Agent** و**OpenCode** و**Pi** و**Gemini CLI** و**Agents SDK**.
-وكلاء الذكاء الاصطناعي يفشلون بطرق متوقعة. يقومون بتشغيل أوامر مدمرة، يسربون الأسرار، ينحرفون عن المهمة، يعلقون في حلقات، أو يدفعون مباشرة إلى main. إذا تُركت دون رقابة، فإن الأعطال الصغيرة تتحول إلى انقطاعات في الخدمة، وتسريب بيانات اعتماد، وفقدان العمل.
+عملاء الذكاء الاصطناعي يفشلون بطرق يمكن التنبؤ بها. يقومون بتشغيل أوامر مدمرة، وتسريب الأسرار، والانجراف عن المهمة، والعلوق في حلقات، أو الدفع المباشر إلى الفرع الرئيسي. إذا تركت دون رقابة، فقد تتحول الإخفاقات الصغيرة إلى أعطال، وتسريب بيانات اعتماد، وفقدان العمل.
-FailproofAI يحل هذه المشكلة باستخدام **السياسات**. هذه القواعد تتصل بكل استدعاء أداة وكيل لـ **كشف الأعطال**، **التخفيف منها** (الحجب، التعليمات، التطهير)، و **تنبيهك** عند الحاجة إلى الانتباه. لوحة معلومات محلية تتيح لك مراجعة كل استدعاء أداة، فشل وكيل، وإجراء استعادة بعد ذلك.
+FailproofAI يحل هذه المشكلة باستخدام **السياسات**. هذه القواعد تتواصل مع كل استدعاء أداة للعميل لـ **كشف الفشل** و**التخفيف منه** (حظر، تعليمات، تنظيف)، و**تنبيهك** عند احتياج شيء ما إلى الانتباه. لوحة تحكم محلية تتيح لك مراجعة كل استدعاء أداة وفشل عميل وإجراء استرجاع بعد ذلك.
-لا تغادر البيانات جهازك.
+لا تترك البيانات جهازك.
-## ابدأ الآن
+## ابدأ
- احجب الأوامر المدمرة، منع تسريب الأسرار، حافظ على الوكلاء داخل حدود المشروع، والمزيد. كل ذلك خارج الصندوق.
+ حظر الأوامر المدمرة، ومنع تسريب الأسرار، والحفاظ على العملاء داخل حدود المشروع، والمزيد. كل ذلك في الصندوق.
- اكتب قواعدك الخاصة في JavaScript باستخدام واجهة برمجية بسيطة allow / deny / instruct.
+ اكتب قواعلك الخاصة في JavaScript بواسطة واجهة برمجة تطبيقات بسيطة allow / deny / instruct.
-
- انظر ماذا فعل وكلاؤك بينما كنت بعيداً. تصفح الجلسات، افحص استدعاءات الأدوات، راجع أين تم تنفيذ السياسات.
+
+ انظر إلى ما فعله عملاؤك أثناء غيابك. استعرض الجلسات، وفتش استدعاءات الأدوات، وراجع حيث أطلقت السياسات.
- اضبط أي سياسة بدون كود. عيّن قوائم سماح، فروع محمية، أو عتبات لكل مشروع أو عالمياً.
+ اضبط أي سياسة بدون كود. قم بتعيين قوائم السماح أو الفروع المحمية أو الحدود لكل مشروع أو عالميًا.
-## ابدأ بسرعة
+## البدء السريع
@@ -51,7 +50,7 @@ bun add -g failproofai
```bash
-failproofai policies --install # enable policies
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
failproofai # launch the dashboard
```
diff --git a/docs/cli/environment-variables.mdx b/docs/cli/environment-variables.mdx
index d440c3f..e43e501 100644
--- a/docs/cli/environment-variables.mdx
+++ b/docs/cli/environment-variables.mdx
@@ -25,6 +25,12 @@ description: "Configure failproofai behavior with environment variables"
|----------|-------------|
| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Disable anonymous usage telemetry |
+## First-run prompt
+
+| Variable | Description |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | Skip the prompt that offers to install policies on the first bare `failproofai` invocation |
+
## LLM (for policy evaluation)
| Variable | Description |
diff --git a/docs/de/cli/environment-variables.mdx b/docs/de/cli/environment-variables.mdx
index 3e9705f..de16fa9 100644
--- a/docs/de/cli/environment-variables.mdx
+++ b/docs/de/cli/environment-variables.mdx
@@ -1,6 +1,6 @@
---
title: Umgebungsvariablen
-description: "failproofai-Verhalten mit Umgebungsvariablen konfigurieren"
+description: "Das Verhalten von failproofai mit Umgebungsvariablen konfigurieren"
---
## Dashboard
@@ -8,16 +8,16 @@ description: "failproofai-Verhalten mit Umgebungsvariablen konfigurieren"
| Variable | Beschreibung |
|----------|-------------|
| `PORT` | Dashboard-Port (Standard: `8020`) |
-| `CLAUDE_PROJECTS_PATH` | Überschreibt den Pfad, unter dem Claude Code Projektordner gesucht werden |
+| `CLAUDE_PROJECTS_PATH` | Überschreibt den Pfad, unter dem Claude Code-Projektordner gesucht werden |
| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Kommagetrennte Liste von Dashboard-Seiten, die ausgeblendet werden sollen |
| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Hosts/IPs, die auf Entwicklungsressourcen zugreifen dürfen. Entspricht `--allowed-origins`. |
-## Logging
+## Protokollierung
| Variable | Beschreibung |
|----------|-------------|
-| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Server-Log-Level (Standard: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | Benutzerdefinierter Pfad zur Hook-Log-Datei oder `true` für den Standardpfad (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Server-Protokollierungsstufe (Standard: `warn`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | Benutzerdefinierter Pfad zur Hook-Protokolldatei oder `true` für den Standardpfad (`~/.failproofai/logs/hooks.log`) |
## Telemetrie
@@ -25,7 +25,13 @@ description: "failproofai-Verhalten mit Umgebungsvariablen konfigurieren"
|----------|-------------|
| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Anonyme Nutzungstelemetrie deaktivieren |
-## LLM (für Richtlinienauswertung)
+## Erststart-Eingabeaufforderung
+
+| Variable | Beschreibung |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | Überspringt die Eingabeaufforderung, die beim ersten bloßen Aufruf von `failproofai` das Installieren von Richtlinien anbietet |
+
+## LLM (zur Richtlinienauswertung)
| Variable | Beschreibung |
|----------|-------------|
diff --git a/docs/de/introduction.mdx b/docs/de/introduction.mdx
index 0d6b3dd..dcb7825 100644
--- a/docs/de/introduction.mdx
+++ b/docs/de/introduction.mdx
@@ -1,24 +1,24 @@
---
title: "Failproof AI"
-description: "FailproofAI stattet KI-Agenten mit 39 integrierten Fehlerrichtlinien aus, die Schleifen, geheime Datenlecks, destruktive Tool-Aufrufe und mehr mit einer einzigen Installation abfangen."
+description: "FailproofAI stattet KI-Agenten mit 39 eingebauten Fehlerrichtlinien aus, die Schleifen, geheime Datenlecks, destruktive Tool-Aufrufe und mehr bei einer einzigen Installation abfangen."
---
[](https://www.npmjs.com/package/failproofai)
-Hooks und Richtlinien für **KI-Fehlerbehandlung**, **Fehlerwiederherstellung** und **LLM-Zuverlässigkeit**. Halten Sie Ihre KI-Agenten zuverlässig und autonom am Laufen – in **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** und dem **Agents SDK**.
+Hooks und Richtlinien für **KI-Fehlerbehandlung**, **Fehlerbehebung** und **LLM-Zuverlässigkeit**. Halten Sie Ihre KI-Agenten zuverlässig und autonom am Laufen – mit **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** und dem **Agents SDK**.
-KI-Agenten scheitern auf vorhersehbare Weisen. Sie führen destruktive Befehle aus, geben Geheimnisse preis, weichen von ihrer Aufgabe ab, stecken in Schleifen fest oder pushen direkt in den main-Branch. Werden diese Fehler nicht unterbunden, eskalieren kleine Probleme zu Ausfällen, geleakten Zugangsdaten und verloren gegangener Arbeit.
+KI-Agenten scheitern auf vorhersehbare Weise. Sie führen destruktive Befehle aus, geben Geheimnisse preis, verlieren den Fokus, stecken in Schleifen fest oder pushen direkt auf main. Ohne Aufsicht eskalieren kleine Fehler zu Ausfällen, geleakten Zugangsdaten und verlorener Arbeit.
-FailproofAI löst das mit **Richtlinien**. Diese Regeln greifen bei jedem Agenten-Tool-Aufruf ein, um **Fehler zu erkennen**, **sie zu beheben** (blockieren, anweisen, bereinigen) und **Sie zu benachrichtigen**, wenn etwas Aufmerksamkeit erfordert. Ein lokales Dashboard ermöglicht es Ihnen, im Nachhinein jeden Tool-Aufruf, jeden Agentenfehler und jede Wiederherstellungsmaßnahme einzusehen.
+FailproofAI löst dieses Problem mit **Richtlinien**. Diese Regeln klinken sich in jeden Tool-Aufruf des Agenten ein, um **Fehler zu erkennen**, **sie zu beheben** (blockieren, anweisen, bereinigen) und **Sie zu benachrichtigen**, wenn etwas Aufmerksamkeit erfordert. Ein lokales Dashboard ermöglicht es Ihnen, jeden Tool-Aufruf, jeden Agentenfehler und jede Wiederherstellungsaktion im Nachhinein zu überprüfen.
Es verlassen keine Daten Ihren Rechner.
-## Einstieg
+## Erste Schritte
-
- Destruktive Befehle blockieren, geheime Datenlecks verhindern, Agenten innerhalb der Projektgrenzen halten und vieles mehr – alles ohne zusätzliche Konfiguration.
+
+ Destruktive Befehle blockieren, das Durchsickern von Geheimnissen verhindern, Agenten innerhalb der Projektgrenzen halten und vieles mehr. Alles sofort einsatzbereit.
@@ -26,11 +26,11 @@ Es verlassen keine Daten Ihren Rechner.
- Sehen Sie, was Ihre Agenten während Ihrer Abwesenheit getan haben. Sitzungen durchsuchen, Tool-Aufrufe untersuchen, nachvollziehen, wo Richtlinien ausgelöst wurden.
+ Sehen Sie, was Ihre Agenten in Ihrer Abwesenheit getan haben. Sitzungen durchsuchen, Tool-Aufrufe untersuchen, nachverfolgen, wo Richtlinien ausgelöst wurden.
- Jede Richtlinie ohne Code anpassen. Erlaubnislisten, geschützte Branches oder Schwellenwerte pro Projekt oder global festlegen.
+ Jede Richtlinie ohne Code anpassen. Allowlists, geschützte Branches oder Schwellenwerte pro Projekt oder global festlegen.
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # enable policies
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
failproofai # launch the dashboard
```
-Eine vollständige Anleitung finden Sie im [Erste Schritte](/de/getting-started)-Leitfaden.
\ No newline at end of file
+Die vollständige Anleitung finden Sie im [Erste-Schritte-Leitfaden](/de/getting-started).
\ No newline at end of file
diff --git a/docs/docs.json b/docs/docs.json
index 9702a10..5cdae94 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -42,7 +42,6 @@
"cli/install-policies",
"cli/remove-policies",
"cli/list-policies",
- "cli/audit",
"cli/hook",
"cli/version",
"cli/environment-variables"
diff --git a/docs/es/cli/environment-variables.mdx b/docs/es/cli/environment-variables.mdx
index 67d0301..bd12e13 100644
--- a/docs/es/cli/environment-variables.mdx
+++ b/docs/es/cli/environment-variables.mdx
@@ -8,27 +8,33 @@ description: "Configura el comportamiento de failproofai con variables de entorn
| Variable | Descripción |
|----------|-------------|
| `PORT` | Puerto del dashboard (por defecto: `8020`) |
-| `CLAUDE_PROJECTS_PATH` | Sobreescribe la ubicación donde se buscan las carpetas de proyectos de Claude Code |
-| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Páginas del dashboard separadas por comas que se ocultarán |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Hosts/IPs con permiso para acceder a recursos de desarrollo. Equivale a `--allowed-origins`. |
+| `CLAUDE_PROJECTS_PATH` | Reemplaza la ruta donde se buscan las carpetas de proyectos de Claude Code |
+| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Páginas del dashboard a ocultar, separadas por comas |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Hosts/IPs con acceso a recursos de desarrollo. Equivalente a `--allowed-origins`. |
## Registro
| Variable | Descripción |
|----------|-------------|
| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Nivel de registro del servidor (por defecto: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | Ruta personalizada del archivo de registro de hooks, o `true` para usar la predeterminada (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | Ruta personalizada del archivo de log de hooks, o `true` para usar el valor predeterminado (`~/.failproofai/logs/hooks.log`) |
## Telemetría
| Variable | Descripción |
|----------|-------------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Deshabilita la telemetría de uso anónima |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Desactiva la telemetría anónima de uso |
+
+## Aviso en el primer uso
+
+| Variable | Descripción |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | Omite el aviso que ofrece instalar políticas en la primera invocación básica de `failproofai` |
## LLM (para evaluación de políticas)
| Variable | Descripción |
|----------|-------------|
| `FAILPROOFAI_LLM_BASE_URL` | Endpoint de la API del LLM (por defecto: `https://api.openai.com/v1`) |
-| `FAILPROOFAI_LLM_API_KEY` | Clave de API para políticas basadas en LLM |
+| `FAILPROOFAI_LLM_API_KEY` | Clave de API para las políticas basadas en LLM |
| `FAILPROOFAI_LLM_MODEL` | Nombre del modelo (por defecto: `gpt-4o-mini`) |
\ No newline at end of file
diff --git a/docs/es/introduction.mdx b/docs/es/introduction.mdx
index 09fb28b..3f34054 100644
--- a/docs/es/introduction.mdx
+++ b/docs/es/introduction.mdx
@@ -1,32 +1,32 @@
---
title: "Failproof AI"
-description: "FailproofAI proporciona a los agentes de IA 39 políticas de fallos integradas que detectan bucles, filtraciones de secretos, llamadas a herramientas destructivas y más en una sola instalación."
+description: "FailproofAI proporciona a los agentes de IA 39 políticas de fallos integradas que detectan bucles, filtraciones de secretos, llamadas destructivas a herramientas y más, con una sola instalación."
---
[](https://www.npmjs.com/package/failproofai)
-Hooks y políticas para el **manejo de fallos en IA**, **recuperación de errores** y **fiabilidad de LLM**. Mantén tus agentes de IA fiables y funcionando de forma autónoma en **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** y el **Agents SDK**.
+Hooks y políticas para la **gestión de fallos de IA**, **recuperación de errores** y **fiabilidad de LLM**. Mantén tus agentes de IA fiables y funcionando de forma autónoma en **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** y el **Agents SDK**.
-Los agentes de IA fallan de maneras predecibles. Ejecutan comandos destructivos, filtran secretos, se desvían de su tarea, quedan atrapados en bucles o hacen push directamente a main. Sin supervisión, los pequeños fallos se convierten en interrupciones del servicio, credenciales expuestas y trabajo perdido.
+Los agentes de IA fallan de maneras predecibles. Ejecutan comandos destructivos, filtran secretos, se desvían de la tarea, quedan atrapados en bucles o hacen push directamente a main. Sin supervisión, los fallos pequeños se encadenan y desencadenan interrupciones del servicio, credenciales expuestas y trabajo perdido.
-FailproofAI resuelve esto con **políticas**. Estas reglas se conectan a cada llamada de herramienta del agente para **detectar fallos**, **mitigarlos** (bloquear, instruir, sanear) y **alertarte** cuando algo requiere atención. Un panel de control local te permite revisar cada llamada a herramienta, fallo del agente y acción de recuperación.
+FailproofAI resuelve esto mediante **políticas**. Estas reglas se enganchan a cada llamada de herramienta del agente para **detectar fallos**, **mitigarlos** (bloquear, instruir, sanear) y **alertarte** cuando algo requiere atención. Un panel de control local te permite revisar después cada llamada a herramienta, fallo del agente y acción de recuperación.
Ningún dato sale de tu máquina.
-## Primeros pasos
+## Comenzar
- Bloquea comandos destructivos, previene la filtración de secretos, mantiene los agentes dentro de los límites del proyecto y mucho más. Todo listo para usar desde el primer momento.
+ Bloquea comandos destructivos, evita la filtración de secretos, mantén los agentes dentro de los límites del proyecto y más. Todo listo desde el primer momento.
- Escribe tus propias reglas en JavaScript con una sencilla API de allow / deny / instruct.
+ Escribe tus propias reglas en JavaScript con una sencilla API allow / deny / instruct.
- Descubre qué hicieron tus agentes mientras no estabas. Explora sesiones, inspecciona llamadas a herramientas y revisa dónde se activaron las políticas.
+ Ve qué hicieron tus agentes mientras no estabas. Navega por sesiones, inspecciona llamadas a herramientas y revisa dónde se activaron las políticas.
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # activar políticas
-failproofai # lanzar el panel de control
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
+failproofai # launch the dashboard
```
Consulta la guía de [Primeros pasos](/es/getting-started) para ver el recorrido completo.
\ No newline at end of file
diff --git a/docs/fr/cli/environment-variables.mdx b/docs/fr/cli/environment-variables.mdx
index 9e83048..d836bef 100644
--- a/docs/fr/cli/environment-variables.mdx
+++ b/docs/fr/cli/environment-variables.mdx
@@ -8,7 +8,7 @@ description: "Configurer le comportement de failproofai avec des variables d'env
| Variable | Description |
|----------|-------------|
| `PORT` | Port du tableau de bord (par défaut : `8020`) |
-| `CLAUDE_PROJECTS_PATH` | Remplace l'emplacement des dossiers de projets Claude Code |
+| `CLAUDE_PROJECTS_PATH` | Remplace l'emplacement où sont recherchés les dossiers de projets Claude Code |
| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Pages du tableau de bord à masquer, séparées par des virgules |
| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Hôtes/IPs autorisés à accéder aux ressources de développement. Identique à `--allowed-origins`. |
@@ -17,18 +17,24 @@ description: "Configurer le comportement de failproofai avec des variables d'env
| Variable | Description |
|----------|-------------|
| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Niveau de journalisation du serveur (par défaut : `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | Chemin personnalisé du fichier de log des hooks, ou `true` pour la valeur par défaut (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | Chemin personnalisé du fichier de journal des hooks, ou `true` pour le chemin par défaut (`~/.failproofai/logs/hooks.log`) |
## Télémétrie
| Variable | Description |
|----------|-------------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Désactiver la télémétrie d'utilisation anonyme |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Désactive la télémétrie d'utilisation anonyme |
+
+## Invite de première utilisation
+
+| Variable | Description |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | Ignore l'invite proposant d'installer les politiques lors du premier appel à `failproofai` sans arguments |
## LLM (pour l'évaluation des politiques)
| Variable | Description |
|----------|-------------|
| `FAILPROOFAI_LLM_BASE_URL` | Point de terminaison de l'API LLM (par défaut : `https://api.openai.com/v1`) |
-| `FAILPROOFAI_LLM_API_KEY` | Clé API pour les politiques pilotées par LLM |
+| `FAILPROOFAI_LLM_API_KEY` | Clé API pour les politiques basées sur un LLM |
| `FAILPROOFAI_LLM_MODEL` | Nom du modèle (par défaut : `gpt-4o-mini`) |
\ No newline at end of file
diff --git a/docs/fr/introduction.mdx b/docs/fr/introduction.mdx
index 894931c..25b3fb9 100644
--- a/docs/fr/introduction.mdx
+++ b/docs/fr/introduction.mdx
@@ -1,15 +1,15 @@
---
title: "Failproof AI"
-description: "FailproofAI offre aux agents IA 39 politiques de gestion des défaillances intégrées qui détectent les boucles, les fuites de secrets, les appels d'outils destructeurs, et plus encore en une seule installation."
+description: "FailproofAI donne aux agents IA 39 politiques de défaillance intégrées qui détectent les boucles, les fuites de secrets, les appels d'outils destructeurs et bien plus encore, en une seule installation."
---
[](https://www.npmjs.com/package/failproofai)
-Hooks et politiques pour la **gestion des défaillances IA**, la **récupération d'erreurs** et la **fiabilité des LLM**. Gardez vos agents IA fiables et autonomes avec **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** et le **Agents SDK**.
+Hooks et politiques pour la **gestion des défaillances de l'IA**, la **récupération d'erreurs** et la **fiabilité des LLM**. Gardez vos agents IA fiables et autonomes sur **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** et le **Agents SDK**.
-Les agents IA échouent de manière prévisible. Ils exécutent des commandes destructrices, divulguent des secrets, s'éloignent de leur tâche, se retrouvent coincés dans des boucles ou poussent directement vers la branche principale. Sans surveillance, de petites défaillances se transforment en pannes, en identifiants compromis et en travail perdu.
+Les agents IA échouent de manière prévisible. Ils exécutent des commandes destructrices, exposent des secrets, dérivent hors de leur tâche, se retrouvent bloqués dans des boucles ou poussent directement sur main. Sans surveillance, de petites défaillances s'enchaînent et provoquent des pannes, des identifiants compromis et du travail perdu.
-FailproofAI résout ce problème grâce aux **politiques**. Ces règles s'intègrent à chaque appel d'outil de l'agent pour **détecter les défaillances**, **les atténuer** (bloquer, instruire, assainir) et **vous alerter** lorsqu'une intervention est nécessaire. Un tableau de bord local vous permet de consulter chaque appel d'outil, chaque défaillance de l'agent et chaque action corrective a posteriori.
+FailproofAI résout ce problème grâce à des **politiques**. Ces règles s'accrochent à chaque appel d'outil de l'agent pour **détecter les défaillances**, les **atténuer** (bloquer, instruire, assainir) et **vous alerter** lorsqu'un problème nécessite votre attention. Un tableau de bord local vous permet de consulter chaque appel d'outil, chaque défaillance d'agent et chaque action corrective après coup.
Aucune donnée ne quitte votre machine.
@@ -26,11 +26,11 @@ Aucune donnée ne quitte votre machine.
- Voyez ce que vos agents ont fait pendant votre absence. Parcourez les sessions, inspectez les appels d'outils, vérifiez où les politiques ont été déclenchées.
+ Voyez ce que vos agents ont fait pendant votre absence. Parcourez les sessions, inspectez les appels d'outils, examinez où les politiques se sont déclenchées.
-
- Ajustez n'importe quelle politique sans écrire de code. Définissez des listes d'autorisation, des branches protégées ou des seuils par projet ou de manière globale.
+
+ Ajustez n'importe quelle politique sans code. Définissez des listes d'autorisation, des branches protégées ou des seuils par projet ou de manière globale.
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # activer les politiques
-failproofai # lancer le tableau de bord
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
+failproofai # launch the dashboard
```
-Consultez le guide [Premiers pas](/fr/getting-started) pour une présentation complète.
\ No newline at end of file
+Consultez le guide [Prise en main](/fr/getting-started) pour une présentation complète.
\ No newline at end of file
diff --git a/docs/he/cli/environment-variables.mdx b/docs/he/cli/environment-variables.mdx
index 575fc89..fd39441 100644
--- a/docs/he/cli/environment-variables.mdx
+++ b/docs/he/cli/environment-variables.mdx
@@ -1,35 +1,40 @@
---
+title: متغيرات البيئة
+description: "تكوين سلوك failproofai باستخدام متغيرات البيئة"
---
-title: משתני סביבה
-description: "הגדרת התנהגות failproofai באמצעות משתני סביבה"
----
-## לוח בקרה
+## لوحة المعلومات
+
+| متغير | الوصف |
+|----------|-------------|
+| `PORT` | منفذ لوحة المعلومات (الافتراضي: `8020`) |
+| `CLAUDE_PROJECTS_PATH` | تجاوز موقع مجلدات مشاريع Claude Code |
+| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | صفحات لوحة المعلومات المفصولة بفواصل لإخفاؤها |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | المضيفات/عناوين IP المسموحة بالوصول إلى موارد التطوير. مثل `--allowed-origins`. |
+
+## تسجيل السجلات
-| משתנה | תיאור |
+| متغير | الوصف |
|----------|-------------|
-| `PORT` | פורט לוח הבקרה (ברירת מחדל: `8020`) |
-| `CLAUDE_PROJECTS_PATH` | דריסת מיקום תיקיות פרויקט Claude Code |
-| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | עמודי לוח בקרה להסתרה מופרדים בפסיקים |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | מארחים/IP-ים שמורשים לגשת למשאבי פיתוח. זהה ל-`--allowed-origins`. |
+| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | مستوى سجل الخادم (الافتراضي: `warn`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | مسار ملف سجل hook مخصص، أو `true` للافتراضي (`~/.failproofai/logs/hooks.log`) |
-## רישום
+## التحليلات
-| משתנה | תיאור |
+| متغير | الوصف |
|----------|-------------|
-| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | רמת רישום השרת (ברירת מחדל: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | נתיב קובץ רישום חיבור מותאם, או `true` לברירת מחדל (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | تعطيل تحليلات الاستخدام المجهولة |
-## טלמטריה
+## موجه التشغيل الأول
-| משתנה | תיאור |
+| متغير | الوصف |
|----------|-------------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | השבתת טלמטריה שימוש אנונימית |
+| `FAILPROOFAI_NO_FIRST_RUN=1` | تخطي الموجه الذي يعرض تثبيت السياسات عند أول استدعاء لـ `failproofai` الفارغ |
-## LLM (להערכת מדיניות)
+## نموذج اللغة (لتقييم السياسة)
-| משתנה | תיאור |
+| متغير | الوصف |
|----------|-------------|
-| `FAILPROOFAI_LLM_BASE_URL` | נקודת קצה ל-API של LLM (ברירת מחדל: `https://api.openai.com/v1`) |
-| `FAILPROOFAI_LLM_API_KEY` | מפתח API למדיניויות המופעלות על ידי LLM |
-| `FAILPROOFAI_LLM_MODEL` | שם המודל (ברירת מחדל: `gpt-4o-mini`) |
\ No newline at end of file
+| `FAILPROOFAI_LLM_BASE_URL` | نقطة نهاية API لنموذج اللغة (الافتراضي: `https://api.openai.com/v1`) |
+| `FAILPROOFAI_LLM_API_KEY` | مفتاح API لسياسات مدعومة بنموذج اللغة |
+| `FAILPROOFAI_LLM_MODEL` | اسم النموذج (الافتراضي: `gpt-4o-mini`) |
\ No newline at end of file
diff --git a/docs/he/introduction.mdx b/docs/he/introduction.mdx
index 69adb80..0bffcdf 100644
--- a/docs/he/introduction.mdx
+++ b/docs/he/introduction.mdx
@@ -1,36 +1,36 @@
---
title: "Failproof AI"
-description: "FailproofAI מספק לסוכנים AI 39 מדיניות כשל מובנות שתופסות לולאות, דליפות סודות, קריאות כלים הרסניות ועוד בהתקנה אחת."
+description: "FailproofAI מספקת לסוכני AI 39 מדיניות כישלון מובנות שתופסות לולאות, דיווח של סודות, קריאות כלים הרסניות ועוד בהתקנה יחידה."
---
[](https://www.npmjs.com/package/failproofai)
-ווֹי וּמְדִינִיּוֹת לִ**טִיפּוּל בִּכְשָׁלוֹנוֹת AI**, **הַחְזָרָה מִשְּׁגִיאוֹת**, וּ**אִמְצָעוּת LLM**. שְׁמֹר עַל סוֹכְנֵי AI שֶׁלְּךָ אוֹמְנִים וּפּוֹעֲלִים בְּאוּטוֹנוֹמִיָּה בִּ**Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI**, וּ**Agents SDK**.
+ווי וחוקים להתמודדות עם **כישלונות AI**, **התאוששות מ־שגיאות**, ו**אמינות LLM**. שמרו על סוכני ה־AI שלכם אמינים וצעדו בצורה עצמאית על־פני **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI**, ו**Agents SDK**.
-סוֹכְנֵי AI נִכְשָׁלִים בַּדְרָכִים צָפוּיוֹת. הֵם מַשְׁמִיעִים פְּקוּדוֹת הרִסְנִיּוֹת, דּוֹלְפִים סוֹדוֹת, נִסְחוֹ מִן הַמְשִׁימָה, נִתְקְעִים בְּלוּלָאוֹת, אוֹ דּוֹחֲפִים יָשִׁירוּת לְ-main. בְּלִי פִּקּוּחַ, כְּשָׁלוֹנוֹת קְטַנִּים מִתְבַּרְבְּרִים לִהִיוֹת הַפְסָקוֹת, נִדּוּפִי אַסְמַכְתָּא, וְעבוֹדָה אָבוּדָה.
+סוכני AI נכשלים בדרכים צפויות. הם מריצים פקודות הרסניות, דוללים סודות, חוטפים מהמטרה, נתקעים בלולאות, או דוחפים ישירות לענף הראשי. אם מחזיקים אותם ללא השגחה, כישלונות קטנים מתגברים לתקלות, דיווח של אישורים, והפסד עבודה.
-FailproofAI פּוֹתֵר זֹאת עִם **מְדִינִיּוֹת**. הַחוּקִים הָלּוּ מִתְחַבְּרִים לְכָל קְרִיאַת כְּלִי סוֹכֵן כְּדֵי **לְגַלּוֹת כְּשָׁלוֹנוֹת**, **לְהַקְטִין אוֹתָם** (חסִימָה, הוֹרָאָה, טִהוּר), וּ**לְהַזְהִיר אוֹתְךָ** כַּאֲשֶׁר דָּבָר מָה זוֹקֵק תְּשׁוּמַת לֵב. לוּחַ מִשׁוֹמוּ מְקוֹמִי מַאֲפִּשֵּׁר לְךָ לִבְדּוֹק כָּל קְרִיאַת כְּלִי, כְּשַׁל סוֹכֵן, וּפְעוּלַת שׁוּחְזוּר בִּבְחִינָה לָאַחַר מִכֵּן.
+FailproofAI פותרת זאת עם **מדיניויות**. כללים אלו חוברים לכל קריאת כלי סוכן כדי **לזהות כישלונות**, **למתן אותם** (חסימה, הוראה, טיהור), ו**להזהיר אתכם** כשמשהו צריך תשומת לב. לוח בקרה מקומי מאפשר לכם לסקור כל קריאת כלי, כישלון סוכן, ופעולת התאוששות לאחר מכן.
-אֵין נְתוּנִים עוֹזְבִים אֶת הַמְחוֹשֵׁב שֶׁלְּךָ.
+אף נתון לא משאיר את המחשב שלך.
-## התחלה
+## התחל
-
- חסום פקודות הרסניות, מנע דליפות סודות, שמור סוכנים בתוך גבולות פרויקט, ועוד. הכל מתוך הקופסה.
+
+ חסמו פקודות הרסניות, מנעו דיווח של סודות, שמרו סוכנים בתוך גבולות פרויקט, ועוד. הכל מחוץ לקופסה.
-
- כתוב את הכללים שלך בעצמך ב-JavaScript עם API פשוט של allow / deny / instruct.
+
+ כתבו כללים משלכם ב־JavaScript עם API פשוט allow / deny / instruct.
-
- ראה מה עשו הסוכנים שלך כשהיית רחוק. עיין בהפעלות, בחן קריאות כלים, בדוק איפה יריות מדיניויות.
+
+ ראו מה עשו הסוכנים שלכם בזמן שהייתם הרחק. עיינו בהפעלות, בדקו קריאות כלים, בדוקו היכן מדיניויות הפעילו.
-
- כוּנּוּן כל מדיניות ללא קוד. הגדר רשימות אישור, ענפים מוגנים, או סף לכל פרויקט או כללי.
+
+ התאימו כל מדיניות ללא קוד. הגדרו רשימות אישור, ענפים מוגנים, או סף לפי־פרויקט או בגלובל.
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # enable policies
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
failproofai # launch the dashboard
```
-ראה את המדריך [התחלה](/he/getting-started) לצעידה המלאה.
\ No newline at end of file
+ראו את מדריך [התחלת עבודה](/he/getting-started) לסיור מלא.
\ No newline at end of file
diff --git a/docs/hi/cli/environment-variables.mdx b/docs/hi/cli/environment-variables.mdx
index 3e4fbbc..47cc74a 100644
--- a/docs/hi/cli/environment-variables.mdx
+++ b/docs/hi/cli/environment-variables.mdx
@@ -1,34 +1,40 @@
---
-title: Environment variables
-description: "failproofai के व्यवहार को environment variables के साथ कॉन्फ़िगर करें"
+title: पर्यावरण चर
+description: "पर्यावरण चर के साथ failproofai व्यवहार को कॉन्फ़िगर करें"
---
-## Dashboard
+## डैशबोर्ड
-| Variable | Description |
+| चर | विवरण |
|----------|-------------|
-| `PORT` | Dashboard port (default: `8020`) |
-| `CLAUDE_PROJECTS_PATH` | Claude Code project folders खोजने के स्थान को override करें |
-| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Dashboard pages को छुपाने के लिए comma-separated list |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | dev resources को access करने की अनुमति वाले hosts/IPs। `--allowed-origins` के समान। |
+| `PORT` | डैशबोर्ड पोर्ट (डिफ़ॉल्ट: `8020`) |
+| `CLAUDE_PROJECTS_PATH` | Claude Code प्रोजेक्ट फ़ोल्डर कहां मिलते हैं, इसे ओवरराइड करें |
+| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | छिपाने के लिए अल्पविराम से अलग किए गए डैशबोर्ड पृष्ठ |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | डेव संसाधनों तक पहुंचने की अनुमति वाले होस्ट/IP। `--allowed-origins` के समान। |
-## Logging
+## लॉगिंग
-| Variable | Description |
+| चर | विवरण |
|----------|-------------|
-| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Server log level (default: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | Custom hook log file path, या `true` default के लिए (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | सर्वर लॉग स्तर (डिफ़ॉल्ट: `warn`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | कस्टम हुक लॉग फ़ाइल पथ, या डिफ़ॉल्ट के लिए `true` (`~/.failproofai/logs/hooks.log`) |
-## Telemetry
+## टेलीमेट्री
-| Variable | Description |
+| चर | विवरण |
|----------|-------------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Anonymous usage telemetry को disable करें |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | अनाम उपयोग टेलीमेट्री अक्षम करें |
-## LLM (policy evaluation के लिए)
+## पहली बार चलाने वाली प्रॉम्प्ट
-| Variable | Description |
+| चर | विवरण |
|----------|-------------|
-| `FAILPROOFAI_LLM_BASE_URL` | LLM API endpoint (default: `https://api.openai.com/v1`) |
-| `FAILPROOFAI_LLM_API_KEY` | LLM-powered policies के लिए API key |
-| `FAILPROOFAI_LLM_MODEL` | Model name (default: `gpt-4o-mini`) |
\ No newline at end of file
+| `FAILPROOFAI_NO_FIRST_RUN=1` | पहली बार `failproofai` के बिना किसी तर्क के आमंत्रण पर नीतियों को स्थापित करने की पेशकश करने वाली प्रॉम्प्ट को छोड़ें |
+
+## LLM (नीति मूल्यांकन के लिए)
+
+| चर | विवरण |
+|----------|-------------|
+| `FAILPROOFAI_LLM_BASE_URL` | LLM API एंडपॉइंट (डिफ़ॉल्ट: `https://api.openai.com/v1`) |
+| `FAILPROOFAI_LLM_API_KEY` | LLM-संचालित नीतियों के लिए API कुंजी |
+| `FAILPROOFAI_LLM_MODEL` | मॉडल नाम (डिफ़ॉल्ट: `gpt-4o-mini`) |
\ No newline at end of file
diff --git a/docs/hi/introduction.mdx b/docs/hi/introduction.mdx
index f673294..8f70047 100644
--- a/docs/hi/introduction.mdx
+++ b/docs/hi/introduction.mdx
@@ -1,41 +1,41 @@
---
title: "Failproof AI"
-description: "FailproofAI AI एजेंटों को 39 बिल्ट-इन फेलियर पॉलिसीज देता है जो लूप्स, सीक्रेट लीक्स, विनाशकारी टूल कॉल्स और बहुत कुछ को एक ही इंस्टॉल में पकड़ते हैं।"
+description: "FailproofAI एआई एजेंट्स को 39 अंतर्निर्मित विफलता नीतियाँ देता है जो लूप्स, सीक्रेट लीक्स, विनाशकारी टूल कॉल्स और बहुत कुछ को एक ही इंस्टॉल में पकड़ते हैं।"
---
[](https://www.npmjs.com/package/failproofai)
-**AI फेलियर हैंडलिंग**, **एरर रिकवरी**, और **LLM रिलायबिलिटी** के लिए हुक्स और पॉलिसीज। अपने AI एजेंटों को **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI**, और **Agents SDK** में विश्वसनीय और स्वायत्त रूप से चलते हुए रखें।
+**एआई विफलता हैंडलिंग**, **त्रुटि पुनरुद्धार**, और **एलएलएम विश्वसनीयता** के लिए हुक्स और नीतियाँ। अपने एआई एजेंट्स को **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI**, और **Agents SDK** में विश्वसनीय और स्वायत्त रूप से चलाते रहें।
-AI एजेंट्स पूर्वानुमानित तरीकों से फेल करते हैं। वे विनाशकारी कमांड्स चलाते हैं, सीक्रेट्स लीक करते हैं, अपने कार्य से विचलित हो जाते हैं, लूप्स में फंस जाते हैं, या सीधे मेन में पुश कर देते हैं। अगर अनदेखा किया जाए, तो छोटी फेलियरें बड़े आउटेज, लीक किए गए क्रेडेंशियल्स, और खोए हुए काम में बदल जाती हैं।
+एआई एजेंट्स अनुमानित तरीकों से विफल होते हैं। वे विनाशकारी कमांड चलाते हैं, सीक्रेट्स लीक करते हैं, कार्य से भटक जाते हैं, लूप्स में फंस जाते हैं, या सीधे मुख्य शाखा में पुश करते हैं। अनुपस्थिति में, छोटी विफलताएं व्यापक आउटेज, लीक किए गए क्रेडेंशियल्स और खोए हुए काम में बदल जाती हैं।
-FailproofAI इसे **पॉलिसीज** से हल करता है। ये नियम हर एजेंट टूल कॉल में हुक करते हैं ताकि **फेलियरों को डिटेक्ट किया जा सके**, **उन्हें कम किया जा सके** (ब्लॉक, निर्देश, सैनिटाइज), और **आपको अलर्ट दिया जा सके** जब कुछ ध्यान देने की जरूरत हो। एक लोकल डैशबोर्ड आपको हर टूल कॉल, एजेंट फेलियर, और रिकवरी एक्शन की समीक्षा करने देता है।
+FailproofAI इसे **नीतियों** के साथ हल करता है। ये नियम हर एजेंट टूल कॉल में हुक करते हैं ताकि **विफलताओं का पता लगाएँ**, **उन्हें कम करें** (ब्लॉक, निर्देश दें, सैनिटाइज़ करें), और **आपको सतर्क करें** जब कुछ ध्यान की आवश्यकता हो। एक स्थानीय डैशबोर्ड आपको बाद में हर टूल कॉल, एजेंट विफलता और पुनरुद्धार कार्रवाई की समीक्षा करने देता है।
-कोई डेटा आपकी मशीन से नहीं निकलता।
+कोई डेटा आपकी मशीन से बाहर नहीं जाता है।
## शुरुआत करें
-
- विनाशकारी कमांड्स को ब्लॉक करें, सीक्रेट लीकेज को रोकें, एजेंट्स को प्रोजेक्ट बाउंड्रीज के अंदर रखें, और बहुत कुछ। सब कुछ बॉक्स में ही।
+
+ विनाशकारी कमांड्स को ब्लॉक करें, सीक्रेट लीकेज को रोकें, एजेंट्स को प्रोजेक्ट सीमाओं के अंदर रखें, और बहुत कुछ। सब कुछ बॉक्स के बाहर।
-
- सिंपल allow / deny / instruct API के साथ JavaScript में अपने नियम लिखें।
+
+ सरल allow / deny / instruct एपीआई के साथ JavaScript में अपने नियम लिखें।
- देखें कि आपके एजेंट्स आपके दूर रहते हुए क्या करते थे। सेशन्स ब्राउज करें, टूल कॉल्स का निरीक्षण करें, देखें कि पॉलिसीज कहां फायर हुई।
+ देखें कि आपके एजेंट्स आपकी अनुपस्थिति में क्या करते थे। सेशन ब्राउज़ करें, टूल कॉल्स का निरीक्षण करें, देखें कि नीतियाँ कहाँ लागू हुईं।
- किसी भी पॉलिसी को बिना कोड के ट्यून करें। allowlists, प्रोटेक्टेड ब्रांचेस, या थ्रेसहोल्ड्स प्रति-प्रोजेक्ट या ग्लोबली सेट करें।
+ किसी भी नीति को बिना कोड के ट्यून करें। प्रति-प्रोजेक्ट या वैश्विक स्तर पर अनुमतिसूचियाँ, सुरक्षित शाखाएँ, या थ्रेशहोल्ड सेट करें।
-## क्विक स्टार्ट
+## त्वरित शुरुआत
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # पॉलिसीज को सक्षम करें
+failproofai policies --install # नीतियों को सक्षम करें (या छोड़ें — पहले रन पर `failproofai` सेटअप की पेशकश करेगा)
failproofai # डैशबोर्ड लॉन्च करें
```
-पूरी वॉकथ्रू के लिए [Getting started](/hi/getting-started) गाइड देखें।
\ No newline at end of file
+पूरी व्याख्या के लिए [शुरुआत करने गाइड](/hi/getting-started) देखें।
\ No newline at end of file
diff --git a/docs/i18n/README.ar.md b/docs/i18n/README.ar.md
index 2133859..ca8deca 100644
--- a/docs/i18n/README.ar.md
+++ b/docs/i18n/README.ar.md
@@ -18,9 +18,9 @@
**الترجمات:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**حل فشل الوقت التشغيلي لعوامل الترميز.**
-يتصل بـ Claude Code و Codex. يمسك الحلقات والإجراءات الخطرة وتسرب الأسرار
-قبل أن تصبح حوادث. صفر كمون. يعمل محليًا.
+**حل فشل وقت التشغيل لوكلاء البرمجة.**
+يتصل بـ Claude Code و Codex. يقبض على الحلقات والإجراءات الخطيرة وتسريب الأسرار
+قبل أن تصبح حوادث. بدون تأخير. يعمل محليًا.
@@ -30,7 +30,7 @@
---
-## واجهات سطر الأوامر للعوامل المدعومة
+## واجهات سطر الأوامر المدعومة
@@ -81,7 +81,7 @@
-> ثبت الخطافات لواحد أو أي مجموعة: `failproofai policies --install --cli opencode pi gemini` (أو `--cli claude codex copilot cursor opencode pi gemini`). احذف `--cli` للكشف التلقائي عن واجهات سطر الأوامر المثبتة والمطالبة.
+> ثبت الخطافات لواحد أو أي مزيج: `failproofai policies --install --cli opencode pi gemini` (أو `--cli claude codex copilot cursor opencode pi gemini`). استبعد `--cli` للكشف التلقائي عن واجهات سطر الأوامر المثبتة والمطالبة.
---
@@ -89,23 +89,23 @@
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # أو ببساطة قم بتشغيل `failproofai` واقبل موجه التشغيل الأول
failproofai
```
-30 سياسة مدمجة تنشط فورًا. لوحة التحكم على `localhost:8020`.
+30 سياسة مدمجة تصبح نشطة فورًا. لوحة التحكم على `localhost:8020`. عطل موجه التشغيل الأول باستخدام `FAILPROOFAI_NO_FIRST_RUN=1`.
---
-## ما الذي يوقفه
+## ما يوقفه
-| السياسة | ما الذي يحظره |
+| السياسة | ما يمنعه |
|---|---|
| `block-push-master` | الدفع المباشر إلى `main` / `master` |
| `block-force-push` | `git push --force` |
-| `block-work-on-main` | الالتزامات والدمج والإعادة على `main` / `master` |
+| `block-work-on-main` | الالتزامات والدمج وإعادة الأساس على `main` / `master` |
| `block-rm-rf` | حذف الملفات بشكل متكرر |
-| `sanitize-api-keys` | تسرب مفاتيح API إلى سياق الوكيل |
+| `sanitize-api-keys` | تسريب مفاتيح API إلى سياق الوكيل |
→ [جميع السياسات المدمجة الـ 30](https://docs.befailproof.ai/built-in-policies)
@@ -113,8 +113,8 @@ failproofai
## سياساتك الخاصة
-أسقط ملفًا في `.failproofai/policies/` — يتم تحميله تلقائيًا، لا توجد علامات مطلوبة.
-قم بالالتزام بها وسيحصل الفريق بأكمله عليها في السحب التالي.
+أسقط ملفًا في `.failproofai/policies/` — يتم تحميله تلقائيًا، بدون أعلام مطلوبة.
+قم بالالتزام به وستحصل على الفريق الكامل عليه في الطلب التالي.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -130,13 +130,13 @@ customPolicies.add({
});
```
-ثلاثة قرارات متاحة لكل سياسة:
+ثلاث قرارات متاحة لكل سياسة:
| القرار | التأثير |
|---|---|
| `allow()` | السماح بالعملية |
-| `deny(message)` | حظرها — الرسالة تعود إلى الوكيل |
-| `instruct(message)` | اسمح بها، لكن أضف السياق إلى الموجه التالي للوكيل |
+| `deny(message)` | حظره — الرسالة تعود إلى الوكيل |
+| `instruct(message)` | دعه يمر، لكن أضف السياق إلى الموجه التالي للوكيل |
→ [دليل السياسات المخصصة](https://docs.befailproof.ai/custom-policies)
@@ -144,22 +144,22 @@ customPolicies.add({
## رؤية الجلسة
-كل استدعاء أداة يجريها وكيلك يتم تسجيله محليًا. تعرض لوحة التحكم ما تم تشغيله،
-وما تم حظره، وما قالت السياسة للوكيل — حتى لا تخمن
+يتم تسجيل كل استدعاء أداة يجريها الوكيل محليًا. تعرض لوحة التحكم ما تم تشغيله
+وما تم حظره وما قالته السياسة للوكيل — لذلك لا تتخمن
عندما يحدث خطأ ما. → [دليل لوحة التحكم](https://docs.befailproof.ai/dashboard)
---
-## التوثيق
+## الوثائق
| | |
|---|---|
| [البدء السريع](https://docs.befailproof.ai/getting-started) | التثبيت والخطوات الأولى |
| [السياسات المدمجة](https://docs.befailproof.ai/built-in-policies) | جميع السياسات الـ 30 مع المعاملات |
| [السياسات المخصصة](https://docs.befailproof.ai/custom-policies) | اكتب الخاصة بك |
-| [الإعدادات](https://docs.befailproof.ai/configuration) | نطاقات الإعدادات وقواعد الدمج |
+| [التكوين](https://docs.befailproof.ai/configuration) | نطاقات التكوين وقواعد الدمج |
| [لوحة التحكم](https://docs.befailproof.ai/dashboard) | مراقب الجلسة ونشاط السياسة |
-| [العمارة](https://docs.befailproof.ai/architecture) | كيفية عمل نظام الخطافات |
+| [العمارة](https://docs.befailproof.ai/architecture) | كيف يعمل نظام الخطافات |
---
@@ -171,11 +171,11 @@ MIT مع [Commons Clause](https://commonsclause.com/) — مجاني للاست
## المساهمة
-انظر [CONTRIBUTING.md](./CONTRIBUTING.md). السياسات الجديدة وحالات الحدود والترجمات موضع ترحيب.
+انظر [CONTRIBUTING.md](./CONTRIBUTING.md). السياسات الجديدة والحالات الحدية والترجمات كلها مرحب بها.
---
-صُنع بواسطة [Nivedit Jain](https://github.com/NiveditJain) و [Nikita Agarwal](https://github.com/nk-ag).
+تم البناء بواسطة [Nivedit Jain](https://github.com/NiveditJain) و[Nikita Agarwal](https://github.com/nk-ag).
[befailproof.ai](https://befailproof.ai)
diff --git a/docs/i18n/README.de.md b/docs/i18n/README.de.md
index bf5278d..7c00929 100644
--- a/docs/i18n/README.de.md
+++ b/docs/i18n/README.de.md
@@ -16,9 +16,9 @@
**Übersetzungen:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**Laufzeit-Fehlerbehebung für Coding-Agenten.**
-Klinkt sich in Claude Code und Codex ein. Erkennt Endlosschleifen, gefährliche Aktionen und geheime Datenlecks,
-bevor sie zu Vorfällen werden. Keine zusätzliche Latenz. Läuft lokal.
+**Laufzeitfehler-Behebung für Coding-Agenten.**
+Klinkt sich in Claude Code und Codex ein. Erkennt Endlosschleifen, gefährliche Aktionen und Secret-Leaks,
+bevor sie zu Vorfällen werden. Null Latenz. Läuft lokal.
@@ -79,7 +79,7 @@ bevor sie zu Vorfällen werden. Keine zusätzliche Latenz. Läuft lokal.
-> Hooks für eine oder mehrere Kombinationen installieren: `failproofai policies --install --cli opencode pi gemini` (oder `--cli claude codex copilot cursor opencode pi gemini`). `--cli` weglassen, um installierte CLIs automatisch zu erkennen und eine Auswahl anzuzeigen.
+> Hooks für eine oder eine beliebige Kombination installieren: `failproofai policies --install --cli opencode pi gemini` (oder `--cli claude codex copilot cursor opencode pi gemini`). `--cli` weglassen, um installierte CLIs automatisch zu erkennen und eine Auswahl anzuzeigen.
---
@@ -87,17 +87,17 @@ bevor sie zu Vorfällen werden. Keine zusätzliche Latenz. Läuft lokal.
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # oder einfach `failproofai` ausführen und die Erststart-Eingabeaufforderung bestätigen
failproofai
```
-30 integrierte Richtlinien werden sofort aktiviert. Dashboard unter `localhost:8020`.
+30 integrierte Richtlinien werden sofort aktiviert. Dashboard unter `localhost:8020`. Die Erststart-Eingabeaufforderung lässt sich mit `FAILPROOFAI_NO_FIRST_RUN=1` deaktivieren.
---
-## Was abgeblockt wird
+## Was blockiert wird
-| Richtlinie | Was sie blockiert |
+| Richtlinie | Was sie sperrt |
|---|---|
| `block-push-master` | Direkte Pushes auf `main` / `master` |
| `block-force-push` | `git push --force` |
@@ -111,8 +111,8 @@ failproofai
## Eigene Richtlinien
-Eine Datei in `.failproofai/policies/` ablegen — sie wird automatisch geladen, keine Flags erforderlich.
-Einchecken und das gesamte Team erhält sie beim nächsten Pull.
+Eine Datei in `.failproofai/policies/` ablegen — sie wird automatisch geladen, ohne zusätzliche Flags.
+In die Versionsverwaltung einchecken, und das gesamte Team erhält sie beim nächsten Pull.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -132,7 +132,7 @@ Drei Entscheidungen stehen jeder Richtlinie zur Verfügung:
| Entscheidung | Wirkung |
|---|---|
-| `allow()` | Operation erlauben |
+| `allow()` | Operation zulassen |
| `deny(message)` | Blockieren — die Nachricht wird an den Agenten zurückgegeben |
| `instruct(message)` | Durchlassen, aber dem nächsten Prompt des Agenten Kontext hinzufügen |
@@ -143,8 +143,8 @@ Drei Entscheidungen stehen jeder Richtlinie zur Verfügung:
## Sitzungstransparenz
Jeder Tool-Aufruf des Agenten wird lokal protokolliert. Das Dashboard zeigt, was ausgeführt wurde,
-was blockiert wurde und was die Richtlinie dem Agenten mitgeteilt hat — damit man nicht im Dunkeln tappt,
-wenn etwas schiefläuft. → [Dashboard-Leitfaden](https://docs.befailproof.ai/dashboard)
+was blockiert wurde und was die Richtlinie dem Agenten mitgeteilt hat — damit kein Rätseln
+entsteht, wenn etwas schiefläuft. → [Dashboard-Leitfaden](https://docs.befailproof.ai/dashboard)
---
@@ -154,7 +154,7 @@ wenn etwas schiefläuft. → [Dashboard-Leitfaden](https://docs.befailproof.ai/d
|---|---|
| [Erste Schritte](https://docs.befailproof.ai/getting-started) | Installation und Einstieg |
| [Integrierte Richtlinien](https://docs.befailproof.ai/built-in-policies) | Alle 30 Richtlinien mit Parametern |
-| [Eigene Richtlinien](https://docs.befailproof.ai/custom-policies) | Eigene Richtlinien erstellen |
+| [Eigene Richtlinien](https://docs.befailproof.ai/custom-policies) | Eigene Richtlinien schreiben |
| [Konfiguration](https://docs.befailproof.ai/configuration) | Konfigurationsbereiche und Zusammenführungsregeln |
| [Dashboard](https://docs.befailproof.ai/dashboard) | Sitzungsmonitor und Richtlinienaktivität |
| [Architektur](https://docs.befailproof.ai/architecture) | Funktionsweise des Hook-Systems |
@@ -163,13 +163,13 @@ wenn etwas schiefläuft. → [Dashboard-Leitfaden](https://docs.befailproof.ai/d
## Lizenz
-MIT mit [Commons Clause](https://commonsclause.com/) — kostenlos für den internen und privaten Einsatz; der kommerzielle Weiterverkauf von failproofai selbst erfordert eine gesonderte Vereinbarung. Den vollständigen Text findet man unter [LICENSE](./LICENSE).
+MIT mit [Commons Clause](https://commonsclause.com/) — kostenlos für den internen und persönlichen Einsatz; der kommerzielle Weiterverkauf von failproofai selbst erfordert eine gesonderte Vereinbarung. Den vollständigen Text finden Sie in der [LICENSE](./LICENSE)-Datei.
---
## Mitwirken
-Siehe [CONTRIBUTING.md](./CONTRIBUTING.md). Neue Richtlinien, Grenzfälle und Übersetzungen sind herzlich willkommen.
+Siehe [CONTRIBUTING.md](./CONTRIBUTING.md). Neue Richtlinien, Randfälle und Übersetzungen sind herzlich willkommen.
---
diff --git a/docs/i18n/README.es.md b/docs/i18n/README.es.md
index ebaff0c..9c47c09 100644
--- a/docs/i18n/README.es.md
+++ b/docs/i18n/README.es.md
@@ -79,7 +79,7 @@ antes de que se conviertan en incidentes. Sin latencia. Se ejecuta localmente.
-> Instala hooks para uno o cualquier combinación: `failproofai policies --install --cli opencode pi gemini` (o `--cli claude codex copilot cursor opencode pi gemini`). Omite `--cli` para detectar automáticamente los CLIs instalados y que se te solicite.
+> Instala los hooks para uno o cualquier combinación: `failproofai policies --install --cli opencode pi gemini` (o `--cli claude codex copilot cursor opencode pi gemini`). Omite `--cli` para detectar automáticamente los CLIs instalados y recibir un aviso.
---
@@ -87,15 +87,15 @@ antes de que se conviertan en incidentes. Sin latencia. Se ejecuta localmente.
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # o simplemente ejecuta `failproofai` y acepta el aviso de primera ejecución
failproofai
```
-30 políticas integradas se activan de inmediato. Panel en `localhost:8020`.
+30 políticas integradas se activan de inmediato. Panel de control en `localhost:8020`. Desactiva el aviso de primera ejecución con `FAILPROOFAI_NO_FIRST_RUN=1`.
---
-## Qué detiene
+## Qué bloquea
| Política | Qué bloquea |
|---|---|
@@ -111,8 +111,8 @@ failproofai
## Tus propias políticas
-Coloca un archivo en `.failproofai/policies/` — se carga automáticamente, sin flags adicionales.
-Confírmalo en el repositorio y todo el equipo lo tendrá en el próximo pull.
+Coloca un archivo en `.failproofai/policies/` — se carga automáticamente, sin necesidad de flags.
+Súbelo al repositorio y todo el equipo lo tendrá en el próximo pull.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -133,8 +133,8 @@ Tres decisiones disponibles para cada política:
| Decisión | Efecto |
|---|---|
| `allow()` | Permite la operación |
-| `deny(message)` | La bloquea — el mensaje se devuelve al agente |
-| `instruct(message)` | La deja pasar, pero añade contexto al siguiente prompt del agente |
+| `deny(message)` | La bloquea — el mensaje se envía de vuelta al agente |
+| `instruct(message)` | La deja pasar, pero agrega contexto al siguiente prompt del agente |
→ [Guía de políticas personalizadas](https://docs.befailproof.ai/custom-policies)
@@ -142,9 +142,9 @@ Tres decisiones disponibles para cada política:
## Visibilidad de la sesión
-Cada llamada a herramientas que realiza tu agente se registra localmente. El panel muestra qué se ejecutó,
-qué fue bloqueado y qué indicó la política al agente — para que no tengas que adivinar
-qué salió mal. → [Guía del panel](https://docs.befailproof.ai/dashboard)
+Cada llamada de herramienta que realiza tu agente se registra localmente. El panel de control muestra qué se ejecutó,
+qué fue bloqueado y qué le indicó la política al agente — para que no tengas que adivinar
+cuando algo sale mal. → [Guía del panel de control](https://docs.befailproof.ai/dashboard)
---
@@ -152,24 +152,24 @@ qué salió mal. → [Guía del panel](https://docs.befailproof.ai/dashboard)
| | |
|---|---|
-| [Primeros pasos](https://docs.befailproof.ai/getting-started) | Instalación y pasos iniciales |
+| [Primeros pasos](https://docs.befailproof.ai/getting-started) | Instalación y primeros pasos |
| [Políticas integradas](https://docs.befailproof.ai/built-in-policies) | Las 30 políticas con sus parámetros |
| [Políticas personalizadas](https://docs.befailproof.ai/custom-policies) | Escribe las tuyas propias |
-| [Configuración](https://docs.befailproof.ai/configuration) | Ámbitos de configuración y reglas de fusión |
-| [Panel](https://docs.befailproof.ai/dashboard) | Monitor de sesión y actividad de políticas |
+| [Configuración](https://docs.befailproof.ai/configuration) | Alcances de configuración y reglas de combinación |
+| [Panel de control](https://docs.befailproof.ai/dashboard) | Monitor de sesión y actividad de políticas |
| [Arquitectura](https://docs.befailproof.ai/architecture) | Cómo funciona el sistema de hooks |
---
## Licencia
-MIT con [Commons Clause](https://commonsclause.com/) — gratuito para uso interno y personal; la reventa comercial de failproofai en sí requiere un acuerdo separado. Consulta [LICENSE](./LICENSE) para el texto completo.
+MIT con [Commons Clause](https://commonsclause.com/) — gratuito para uso interno y personal; la reventa comercial de failproofai en sí requiere un acuerdo por separado. Consulta [LICENSE](./LICENSE) para el texto completo.
---
-## Contribuir
+## Contribuciones
-Consulta [CONTRIBUTING.md](./CONTRIBUTING.md). Son bienvenidas nuevas políticas, casos límite y traducciones.
+Consulta [CONTRIBUTING.md](./CONTRIBUTING.md). Nuevas políticas, casos límite y traducciones son bienvenidas.
---
diff --git a/docs/i18n/README.fr.md b/docs/i18n/README.fr.md
index 1b14879..6d9c03d 100644
--- a/docs/i18n/README.fr.md
+++ b/docs/i18n/README.fr.md
@@ -16,9 +16,9 @@
**Traductions :** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**Résolution des erreurs d'exécution pour les agents de code.**
+**Résolution des échecs à l'exécution pour les agents de code.**
S'intègre à Claude Code et Codex. Détecte les boucles, les actions dangereuses et les fuites de secrets
-avant qu'ils ne deviennent des incidents. Zéro latence. Fonctionne en local.
+avant qu'ils ne deviennent des incidents. Zéro latence. S'exécute localement.
@@ -79,7 +79,7 @@ avant qu'ils ne deviennent des incidents. Zéro latence. Fonctionne en local.
-> Installez les hooks pour un ou plusieurs CLI à la fois : `failproofai policies --install --cli opencode pi gemini` (ou `--cli claude codex copilot cursor opencode pi gemini`). Omettez `--cli` pour détecter automatiquement les CLI installés et afficher une invite.
+> Installez les hooks pour un ou plusieurs agents en combinaison : `failproofai policies --install --cli opencode pi gemini` (ou `--cli claude codex copilot cursor opencode pi gemini`). Omettez `--cli` pour détecter automatiquement les CLI installés et être invité à choisir.
---
@@ -87,22 +87,22 @@ avant qu'ils ne deviennent des incidents. Zéro latence. Fonctionne en local.
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # ou lancez simplement `failproofai` et acceptez l'invite au premier démarrage
failproofai
```
-30 politiques intégrées s'activent immédiatement. Tableau de bord disponible sur `localhost:8020`.
+30 politiques intégrées s'activent immédiatement. Tableau de bord disponible sur `localhost:8020`. Désactivez l'invite au premier démarrage avec `FAILPROOFAI_NO_FIRST_RUN=1`.
---
## Ce que ça bloque
-| Politique | Ce qui est bloqué |
+| Politique | Ce qu'elle bloque |
|---|---|
| `block-push-master` | Les push directs vers `main` / `master` |
| `block-force-push` | `git push --force` |
-| `block-work-on-main` | Les commits, merges et rebases sur `main` / `master` |
-| `block-rm-rf` | La suppression récursive de fichiers |
+| `block-work-on-main` | Commits, merges, rebases sur `main` / `master` |
+| `block-rm-rf` | Suppression récursive de fichiers |
| `sanitize-api-keys` | Les clés API qui fuient dans le contexte de l'agent |
→ [Les 30 politiques intégrées](https://docs.befailproof.ai/built-in-policies)
@@ -111,7 +111,7 @@ failproofai
## Vos propres politiques
-Déposez un fichier dans `.failproofai/policies/` — il est chargé automatiquement, sans aucun flag.
+Déposez un fichier dans `.failproofai/policies/` — il se charge automatiquement, sans aucun indicateur.
Commitez-le et toute l'équipe en bénéficiera au prochain pull.
```js
@@ -132,17 +132,19 @@ Trois décisions disponibles pour chaque politique :
| Décision | Effet |
|---|---|
-| `allow()` | Autorise l'opération |
-| `deny(message)` | La bloque — le message est renvoyé à l'agent |
-| `instruct(message)` | La laisse passer, mais ajoute du contexte au prochain prompt de l'agent |
+| `allow()` | Autoriser l'opération |
+| `deny(message)` | La bloquer — le message est renvoyé à l'agent |
+| `instruct(message)` | La laisser passer, mais ajouter du contexte dans le prochain prompt de l'agent |
→ [Guide des politiques personnalisées](https://docs.befailproof.ai/custom-policies)
---
-## Visibilité des sessions
+## Visibilité de la session
-Chaque appel d'outil effectué par votre agent est journalisé en local. Le tableau de bord indique ce qui a été exécuté, ce qui a été bloqué et ce que la politique a transmis à l'agent — vous n'avez plus à tâtonner quand quelque chose tourne mal. → [Guide du tableau de bord](https://docs.befailproof.ai/dashboard)
+Chaque appel d'outil effectué par votre agent est enregistré localement. Le tableau de bord affiche ce qui s'est exécuté,
+ce qui a été bloqué et ce que la politique a communiqué à l'agent — plus besoin de deviner
+quand quelque chose se passe mal. → [Guide du tableau de bord](https://docs.befailproof.ai/dashboard)
---
@@ -165,11 +167,11 @@ MIT avec [Commons Clause](https://commonsclause.com/) — gratuit pour un usage
---
-## Contribuer
+## Contribution
-Consultez [CONTRIBUTING.md](./CONTRIBUTING.md). Nouvelles politiques, cas limites et traductions sont les bienvenus.
+Voir [CONTRIBUTING.md](./CONTRIBUTING.md). Nouvelles politiques, cas limites et traductions sont les bienvenus.
---
-Développé par [Nivedit Jain](https://github.com/NiveditJain) et [Nikita Agarwal](https://github.com/nk-ag).
+Créé par [Nivedit Jain](https://github.com/NiveditJain) et [Nikita Agarwal](https://github.com/nk-ag).
[befailproof.ai](https://befailproof.ai)
diff --git a/docs/i18n/README.he.md b/docs/i18n/README.he.md
index 55f4371..b3a8c89 100644
--- a/docs/i18n/README.he.md
+++ b/docs/i18n/README.he.md
@@ -18,19 +18,19 @@
**תרגומים:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**פתרון כשלים בזמן ריצה לסוכנים קוד.**
-חוטפים ל-Claude Code ו-Codex. תופסים לולאות, פעולות מסוכנות ודליפות סודות
-לפני שהם הופכים לתקריות. אפס זמן השהיה. פועל באופן מקומי.
+**פתרון כישלונות בזמן ריצה לסוכנים קוד.**
+משתלבת עם Claude Code ו-Codex. תופסת לולאות, פעולות מסוכנות וזליגות סודות
+לפני שהן הופכות לתקריות. אפס עיכוב. פועלת מקומית.
-
+
---
-## מנשקי סוכנים נתמכים
+## ממשקי CLI נתמכים
@@ -81,7 +81,7 @@
-> התקן hooks לאחד או לכל שילוב: `failproofai policies --install --cli opencode pi gemini` (או `--cli claude codex copilot cursor opencode pi gemini`). השמט `--cli` לגילוי אוטומטי של CLIs מותקנים ובקשת הנחיות.
+> התקן כללים לאחד או לשילוב כלשהו: `failproofai policies --install --cli opencode pi gemini` (או `--cli claude codex copilot cursor opencode pi gemini`). השמט את `--cli` לגילוי אוטומטי של CLIs מותקנים ודרישה.
---
@@ -89,32 +89,32 @@
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # או פשוט הרץ `failproofai` וקבל את הנושא בהרצה ראשונה
failproofai
```
-30 מדיניויות מובנות יופעלו מיד. לוח מחוונים ב-`localhost:8020`.
+30 כללים מובנים מופעלים מיד. לוח הבקרה ב-`localhost:8020`. השבת את הנושא בהרצה ראשונה עם `FAILPROOFAI_NO_FIRST_RUN=1`.
---
-## מה זה עוצר
+## מה זה מעצור
-| מדיניות | מה זה חוסם |
+| כלל | מה זה חוסם |
|---|---|
| `block-push-master` | דחיפות ישירות ל-`main` / `master` |
| `block-force-push` | `git push --force` |
-| `block-work-on-main` | commits, merges, rebases ב-`main` / `master` |
+| `block-work-on-main` | קומיטים, מיזוגים, רישונים על `main` / `master` |
| `block-rm-rf` | מחיקת קבצים רקורסיבית |
-| `sanitize-api-keys` | מפתחות API שדוללים להקשר הסוכן |
+| `sanitize-api-keys` | מפתחות API שניזלים להקשר הסוכן |
-→ [כל 30 המדיניויות המובנות](https://docs.befailproof.ai/built-in-policies)
+→ [כל 30 הכללים המובנים](https://docs.befailproof.ai/built-in-policies)
---
-## המדיניויות שלך
+## הכללים שלך
-זרוק קובץ ל-`.failproofai/policies/` — הוא נטען אוטומטית, ללא דגלים נדרשים.
-בצע commit וכל הצוות שלך יקבל אותו בפול הבא.
+שחרר קובץ לתוך `.failproofai/policies/` — הוא נטען באופן אוטומטי, ללא דגלים דרושים.
+קומיט אותו והכל בקבוצה מקבל אותו בקלט הבא.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -130,23 +130,23 @@ customPolicies.add({
});
```
-שלוש החלטות זמינות לכל מדיניות:
+שלוש החלטות זמינות לכל כלל:
| החלטה | השפעה |
|---|---|
| `allow()` | אפשר את הפעולה |
| `deny(message)` | חסום אותה — ההודעה חוזרת לסוכן |
-| `instruct(message)` | תן לה להעביר, אך הוסף הקשר להנחיה הבאה של הסוכן |
+| `instruct(message)` | תן לה לעבור דרך, אבל הוסף הקשר להנחיה הבאה של הסוכן |
-→ [מדריך מדיניויות מותאמות אישית](https://docs.befailproof.ai/custom-policies)
+→ [מדריך כללים מותאמים אישית](https://docs.befailproof.ai/custom-policies)
---
-## נראות הפעילות
+## ראות בהפעלה
-כל קריאת כלי שהסוכן שלך מבצע מתועדת באופן מקומי. לוח המחוונים מראה מה רץ,
-מה חוסם, ומה המדיניות אמרה לסוכן — כך שאתה לא מנחש
-כשמשהו משתבש. → [מדריך לוח המחוונים](https://docs.befailproof.ai/dashboard)
+כל קריאת כלי שהסוכן שלך מבצע מתועדת מקומית. לוח הבקרה מציג מה רץ,
+מה נחסם, ומה הכלל אמר לסוכן — כך שאתה לא מנחשת
+כשמשהו הולך להשתבש. → [מדריך לוח הבקרה](https://docs.befailproof.ai/dashboard)
---
@@ -154,24 +154,24 @@ customPolicies.add({
| | |
|---|---|
-| [תחילת העבודה](https://docs.befailproof.ai/getting-started) | התקנה ושלבים ראשונים |
-| [מדיניויות מובנות](https://docs.befailproof.ai/built-in-policies) | כל 30 המדיניויות עם פרמטרים |
-| [מדיניויות מותאמות אישית](https://docs.befailproof.ai/custom-policies) | כתוב שלך |
-| [תצורה](https://docs.befailproof.ai/configuration) | ההיקפים של תצורה וחוקי מיזוג |
-| [לוח מחוונים](https://docs.befailproof.ai/dashboard) | צג הפעילות של הפעילות והמדיניות |
-| [ארכיטקטורה](https://docs.befailproof.ai/architecture) | איך מערכת ה-hook פועלת |
+| [התחלה](https://docs.befailproof.ai/getting-started) | התקנה והצעדים הראשונים |
+| [כללים מובנים](https://docs.befailproof.ai/built-in-policies) | כל 30 הכללים עם פרמטרים |
+| [כללים מותאמים אישית](https://docs.befailproof.ai/custom-policies) | כתוב שלך |
+| [תצורה](https://docs.befailproof.ai/configuration) | טווחי תצורה וכללי מיזוג |
+| [לוח בקרה](https://docs.befailproof.ai/dashboard) | מסקר הפעלה ופעילות כללים |
+| [ארכיטקטורה](https://docs.befailproof.ai/architecture) | כיצד מערכת ההוק עובדת |
---
## רישיון
-MIT עם [Commons Clause](https://commonsclause.com/) — בחינם לשימוש פנימי ואישי; מכירה מסחרית מחדש של failproofai עצמו דורשת הסכם נפרד. ראה [LICENSE](./LICENSE) לקבלת הטקסט המלא.
+MIT עם [Commons Clause](https://commonsclause.com/) — חינם לשימוש פנימי ואישי; מכירת מחדש מסחרית של failproofai עצמו דורשת הסכם נפרד. ראה [LICENSE](./LICENSE) לקבלת הטקסט המלא.
---
## תרומה
-ראה [CONTRIBUTING.md](./CONTRIBUTING.md). מדיניויות חדשות, מקרי קצה, ותרגומים כולם מעודדים.
+ראה [CONTRIBUTING.md](./CONTRIBUTING.md). כללים חדשים, מקרי קצה, ותרגומים כולם מובילים.
---
diff --git a/docs/i18n/README.hi.md b/docs/i18n/README.hi.md
index 8763df1..0f2d20b 100644
--- a/docs/i18n/README.hi.md
+++ b/docs/i18n/README.hi.md
@@ -16,14 +16,14 @@
**अनुवाद:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**कोडिंग एजेंट्स के लिए रनटाइम विफलता समाधान।**
-Claude Code और Codex में हुक करें। लूप्स, खतरनाक क्रियाओं और सीक्रेट लीक को पकड़ें
-इससे पहले कि वे समस्याएं बन जाएं। ज़ीरो लेटेंसी। स्थानीय रूप से चलता है।
+**कोडिंग एजेंटों के लिए रनटाइम विफलता समाधान।**
+Claude Code और Codex में हुक करता है। लूप्स, खतरनाक क्रियाओं और सीक्रेट लीक को पकड़ता है
+इससे पहले कि वे समस्याएं बनें। शून्य विलंबता। स्थानीय रूप से चलता है।
-
+
---
@@ -79,7 +79,7 @@ Claude Code और Codex में हुक करें। लूप्स,
-> एक या किसी भी संयोजन के लिए हुक इंस्टॉल करें: `failproofai policies --install --cli opencode pi gemini` (या `--cli claude codex copilot cursor opencode pi gemini`)। स्वचालित रूप से स्थापित CLIs का पता लगाने और संकेत देने के लिए `--cli` को छोड़ दें।
+> एक या किसी भी संयोजन के लिए हुक इंस्टॉल करें: `failproofai policies --install --cli opencode pi gemini` (या `--cli claude codex copilot cursor opencode pi gemini`)। स्वचालित-पहचान और संकेत देने के लिए `--cli` को छोड़ दें।
---
@@ -87,32 +87,32 @@ Claude Code और Codex में हुक करें। लूप्स,
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # या बस `failproofai` चलाएं और पहली बार के प्रॉम्प्ट को स्वीकार करें
failproofai
```
-30 अंतर्निर्मित नीतियां तुरंत सक्रिय हो जाती हैं। डैशबोर्ड `localhost:8020` पर है।
+30 बिल्ट-इन नीतियां तुरंत सक्रिय हो जाती हैं। डैशबोर्ड `localhost:8020` पर। `FAILPROOFAI_NO_FIRST_RUN=1` से पहली बार के प्रॉम्प्ट को अक्षम करें।
---
-## इसे क्या रोकता है
+## यह क्या रोकता है
-| नीति | इसे क्या ब्लॉक करता है |
+| नीति | क्या यह ब्लॉक करता है |
|---|---|
-| `block-push-master` | `main` / `master` में सीधा पुश |
+| `block-push-master` | `main` / `master` को सीधे पुश |
| `block-force-push` | `git push --force` |
| `block-work-on-main` | `main` / `master` पर कमिट, मर्ज, रीबेस |
-| `block-rm-rf` | पुनरावर्ती फ़ाइल विलोपन |
+| `block-rm-rf` | पुनरावर्ती फाइल हटाना |
| `sanitize-api-keys` | एजेंट संदर्भ में API कुंजियों का लीक होना |
-→ [सभी 30 अंतर्निर्मित नीतियां](https://docs.befailproof.ai/built-in-policies)
+→ [सभी 30 बिल्ट-इन नीतियां](https://docs.befailproof.ai/built-in-policies)
---
-## आपकी अपनी नीतियां
+## अपनी खुद की नीतियां
-`.failproofai/policies/` में एक फ़ाइल रखें — यह स्वचालित रूप से लोड होती है, किसी फ़्लैग की आवश्यकता नहीं है।
-इसे कमिट करें और पूरी टीम को अगले पुल पर मिल जाएगी।
+`.failproofai/policies/` में एक फाइल डालें — यह स्वचालित रूप से लोड होती है, किसी फ्लैग की आवश्यकता नहीं।
+इसे कमिट करें और पूरी टीम को अगले पुल पर मिलता है।
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -128,33 +128,33 @@ customPolicies.add({
});
```
-हर नीति के लिए उपलब्ध तीन निर्णय:
+हर नीति के लिए तीन निर्णय उपलब्ध हैं:
| निर्णय | प्रभाव |
|---|---|
-| `allow()` | ऑपरेशन की अनुमति दें |
-| `deny(message)` | इसे ब्लॉक करें — संदेश एजेंट को वापस चला जाता है |
-| `instruct(message)` | इसे आगे बढ़ने दें, लेकिन एजेंट के अगले प्रॉम्प्ट में संदर्भ जोड़ें |
+| `allow()` | ऑपरेशन को अनुमति दें |
+| `deny(message)` | इसे ब्लॉक करें — संदेश एजेंट के पास वापस जाता है |
+| `instruct(message)` | इसे जाने दें, लेकिन एजेंट के अगले प्रॉम्प्ट में संदर्भ जोड़ें |
-→ [कस्टम नीतियों गाइड](https://docs.befailproof.ai/custom-policies)
+→ [कस्टम नीतियां गाइड](https://docs.befailproof.ai/custom-policies)
---
## सेशन दृश्यता
-आपका एजेंट जो भी टूल कॉल करता है वह स्थानीय रूप से लॉग किया जाता है। डैशबोर्ड दिखाता है कि क्या चला,
-क्या ब्लॉक किया गया, और नीति ने एजेंट को क्या बताया — इसलिए आप अनुमान नहीं लगा रहे हैं
+आपका एजेंट जो हर टूल कॉल करता है वह स्थानीय रूप से लॉग किया जाता है। डैशबोर्ड दिखाता है कि क्या चला,
+क्या ब्लॉक किया गया, और नीति ने एजेंट को क्या बताया — तो आप अनुमान नहीं लगा रहे
जब कुछ गलत हो जाता है। → [डैशबोर्ड गाइड](https://docs.befailproof.ai/dashboard)
---
-## दस्तावेज़
+## डॉक्यूमेंटेशन
| | |
|---|---|
-| [शुरुआत करें](https://docs.befailproof.ai/getting-started) | इंस्टॉलेशन और पहले कदम |
-| [अंतर्निर्मित नीतियां](https://docs.befailproof.ai/built-in-policies) | सभी 30 नीतियां पैरामीटर के साथ |
-| [कस्टम नीतियां](https://docs.befailproof.ai/custom-policies) | अपनी खुद की लिखें |
+| [शुरुआत करना](https://docs.befailproof.ai/getting-started) | इंस्टॉलेशन और पहले कदम |
+| [बिल्ट-इन नीतियां](https://docs.befailproof.ai/built-in-policies) | सभी 30 नीतियां पैरामीटर के साथ |
+| [कस्टम नीतियां](https://docs.befailproof.ai/custom-policies) | अपना लिखें |
| [कॉन्फ़िगरेशन](https://docs.befailproof.ai/configuration) | कॉन्फ़िग स्कोप और मर्ज नियम |
| [डैशबोर्ड](https://docs.befailproof.ai/dashboard) | सेशन मॉनिटर और नीति गतिविधि |
| [आर्किटेक्चर](https://docs.befailproof.ai/architecture) | हुक सिस्टम कैसे काम करता है |
@@ -163,13 +163,13 @@ customPolicies.add({
## लाइसेंस
-MIT with [Commons Clause](https://commonsclause.com/) — आंतरिक और व्यक्तिगत उपयोग के लिए निःशुल्क; failproofai के वाणिज्यिक पुनर्विक्रय के लिए अलग समझौते की आवश्यकता होती है। पूर्ण पाठ के लिए [LICENSE](./LICENSE) देखें।
+MIT with [Commons Clause](https://commonsclause.com/) — आंतरिक और व्यक्तिगत उपयोग के लिए मुफ्त; failproofai के व्यावसायिक पुनर्विक्रय के लिए एक अलग समझौते की आवश्यकता है। पूर्ण पाठ के लिए [LICENSE](./LICENSE) देखें।
---
-## योगदान
+## योगदान देना
-[CONTRIBUTING.md](./CONTRIBUTING.md) देखें। नई नीतियां, सीमांत मामले और अनुवाद सभी स्वागत हैं।
+[CONTRIBUTING.md](./CONTRIBUTING.md) देखें। नई नीतियां, किनारे की स्थितियां, और अनुवाद सभी स्वागत हैं।
---
diff --git a/docs/i18n/README.it.md b/docs/i18n/README.it.md
index 4bc7f93..be952b2 100644
--- a/docs/i18n/README.it.md
+++ b/docs/i18n/README.it.md
@@ -16,9 +16,9 @@
**Traduzioni:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**Risoluzione dei guasti in tempo reale per agenti di codifica.**
-Si integra con Claude Code e Codex. Rileva cicli infiniti, azioni pericolose e perdite di segreti
-prima che diventino incidenti. Latenza zero. Funziona localmente.
+**Risoluzione dei guasti al runtime per agenti di codifica.**
+Si integra in Claude Code e Codex. Cattura loop, azioni pericolose e fughe di segreti
+prima che diventino incidenti. Latenza zero. Esecuzione locale.
@@ -28,7 +28,7 @@ prima che diventino incidenti. Latenza zero. Funziona localmente.
---
-## CLI agenti supportati
+## CLI degli agenti supportati
@@ -79,7 +79,7 @@ prima che diventino incidenti. Latenza zero. Funziona localmente.
-> Installa i hook per uno o una combinazione qualsiasi: `failproofai policies --install --cli opencode pi gemini` (oppure `--cli claude codex copilot cursor opencode pi gemini`). Ometti `--cli` per rilevare automaticamente i CLI installati e ricevere un prompt.
+> Installa i hook per uno o più: `failproofai policies --install --cli opencode pi gemini` (oppure `--cli claude codex copilot cursor opencode pi gemini`). Ometti `--cli` per il rilevamento automatico dei CLI installati e ricevere una richiesta.
---
@@ -87,11 +87,11 @@ prima che diventino incidenti. Latenza zero. Funziona localmente.
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # oppure esegui semplicemente `failproofai` e accetta il prompt della prima esecuzione
failproofai
```
-30 politiche integrate si attivano immediatamente. Dashboard disponibile a `localhost:8020`.
+30 politiche integrate si attivano immediatamente. Cruscotto disponibile su `localhost:8020`. Disabilita il prompt della prima esecuzione con `FAILPROOFAI_NO_FIRST_RUN=1`.
---
@@ -99,20 +99,20 @@ failproofai
| Politica | Cosa blocca |
|---|---|
-| `block-push-master` | Push diretti a `main` / `master` |
+| `block-push-master` | Pressioni dirette su `main` / `master` |
| `block-force-push` | `git push --force` |
| `block-work-on-main` | Commit, merge, rebase su `main` / `master` |
| `block-rm-rf` | Eliminazione ricorsiva di file |
-| `sanitize-api-keys` | Chiavi API che si infiltrano nel contesto dell'agente |
+| `sanitize-api-keys` | Chiavi API che perdono nel contesto dell'agente |
→ [Tutte le 30 politiche integrate](https://docs.befailproof.ai/built-in-policies)
---
-## Politiche personalizzate
+## Le tue politiche
-Inserisci un file in `.failproofai/policies/` — si carica automaticamente, senza flag necessari.
-Eseguine il commit e l'intero team lo riceverà al prossimo pull.
+Rilascia un file in `.failproofai/policies/` — viene caricato automaticamente, nessun flag necessario.
+Esegui il commit e l'intero team lo riceve al prossimo pull.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -133,8 +133,8 @@ Tre decisioni disponibili per ogni politica:
| Decisione | Effetto |
|---|---|
| `allow()` | Consenti l'operazione |
-| `deny(message)` | Bloccala — il messaggio ritorna all'agente |
-| `instruct(message)` | Lasciala passare, ma aggiungi contesto al prossimo prompt dell'agente |
+| `deny(message)` | Blocca — il messaggio torna all'agente |
+| `instruct(message)` | Lascia passare, ma aggiungi contesto al prossimo prompt dell'agente |
→ [Guida alle politiche personalizzate](https://docs.befailproof.ai/custom-policies)
@@ -142,9 +142,9 @@ Tre decisioni disponibili per ogni politica:
## Visibilità della sessione
-Ogni chiamata a strumento che l'agente effettua viene registrata localmente. Il dashboard mostra cosa è stato eseguito,
-cosa è stato bloccato e cosa la politica ha comunicato all'agente — quindi non dovrai indovinare
-quando qualcosa va storto. → [Guida al dashboard](https://docs.befailproof.ai/dashboard)
+Ogni chiamata a strumenti che il tuo agente effettua viene registrata localmente. Il cruscotto mostra cosa è stato eseguito,
+cosa è stato bloccato e cosa la politica ha detto all'agente — così non devi indovinare
+quando qualcosa va storto. → [Guida al cruscotto](https://docs.befailproof.ai/dashboard)
---
@@ -152,24 +152,24 @@ quando qualcosa va storto. → [Guida al dashboard](https://docs.befailproof.ai/
| | |
|---|---|
-| [Getting Started](https://docs.befailproof.ai/getting-started) | Installazione e primi passi |
-| [Built-in Policies](https://docs.befailproof.ai/built-in-policies) | Tutte le 30 politiche con parametri |
-| [Custom Policies](https://docs.befailproof.ai/custom-policies) | Scrivi le tue |
-| [Configuration](https://docs.befailproof.ai/configuration) | Ambiti di configurazione e regole di merge |
-| [Dashboard](https://docs.befailproof.ai/dashboard) | Monitor delle sessioni e attività delle politiche |
-| [Architecture](https://docs.befailproof.ai/architecture) | Come funziona il sistema di hook |
+| [Guida introduttiva](https://docs.befailproof.ai/getting-started) | Installazione e primi passi |
+| [Politiche integrate](https://docs.befailproof.ai/built-in-policies) | Tutte le 30 politiche con parametri |
+| [Politiche personalizzate](https://docs.befailproof.ai/custom-policies) | Scrivi la tua |
+| [Configurazione](https://docs.befailproof.ai/configuration) | Scope di configurazione e regole di fusione |
+| [Cruscotto](https://docs.befailproof.ai/dashboard) | Monitor delle sessioni e attività delle politiche |
+| [Architettura](https://docs.befailproof.ai/architecture) | Come funziona il sistema di hook |
---
## Licenza
-MIT con [Commons Clause](https://commonsclause.com/) — gratuito per uso interno e personale; la rivendita commerciale di failproofai richiede un accordo separato. Consulta [LICENSE](./LICENSE) per il testo completo.
+MIT con [Commons Clause](https://commonsclause.com/) — gratuito per uso interno e personale; la rivendita commerciale di failproofai stesso richiede un accordo separato. Vedi [LICENSE](./LICENSE) per il testo completo.
---
## Contribuire
-Vedi [CONTRIBUTING.md](./CONTRIBUTING.md). Nuove politiche, casi limite e traduzioni sono tutti benvenuti.
+Vedi [CONTRIBUTING.md](./CONTRIBUTING.md). Nuove politiche, casi particolari e traduzioni sono tutti benvenuti.
---
diff --git a/docs/i18n/README.ja.md b/docs/i18n/README.ja.md
index 9f93c1e..4c9bd63 100644
--- a/docs/i18n/README.ja.md
+++ b/docs/i18n/README.ja.md
@@ -16,9 +16,9 @@
**翻訳:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**コーディングエージェントのランタイム障害を解決する。**
-Claude Code および Codex にフックし、ループ・危険な操作・シークレット漏洩を
-インシデントになる前に検知します。レイテンシーゼロ。ローカルで動作。
+**コーディングエージェントのランタイム障害を即時解決。**
+Claude Code や Codex にフックして、ループ・危険な操作・シークレットの漏洩を
+インシデントになる前に検知します。レイテンシーゼロ。ローカル実行。
@@ -79,7 +79,7 @@ Claude Code および Codex にフックし、ループ・危険な操作・シ
-> 1つまたは複数の組み合わせでフックをインストールできます: `failproofai policies --install --cli opencode pi gemini`(または `--cli claude codex copilot cursor opencode pi gemini`)。`--cli` を省略すると、インストール済みの CLI を自動検出してプロンプトを表示します。
+> 1つまたは複数の組み合わせでフックをインストールできます: `failproofai policies --install --cli opencode pi gemini`(または `--cli claude codex copilot cursor opencode pi gemini`)。`--cli` を省略すると、インストール済み CLI を自動検出してプロンプトを表示します。
---
@@ -87,32 +87,32 @@ Claude Code および Codex にフックし、ループ・危険な操作・シ
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # または `failproofai` を実行して初回プロンプトに従う
failproofai
```
-30のビルトインポリシーが即座に有効化されます。ダッシュボードは `localhost:8020` で確認できます。
+30件の組み込みポリシーが即座に有効になります。ダッシュボードは `localhost:8020` で確認できます。`FAILPROOFAI_NO_FIRST_RUN=1` を設定すると初回プロンプトを無効にできます。
---
-## 防止できること
+## 防止できる操作
| ポリシー | ブロック対象 |
|---|---|
| `block-push-master` | `main` / `master` への直接プッシュ |
| `block-force-push` | `git push --force` |
| `block-work-on-main` | `main` / `master` へのコミット・マージ・リベース |
-| `block-rm-rf` | 再帰的なファイル削除 |
+| `block-rm-rf` | ファイルの再帰的削除 |
| `sanitize-api-keys` | エージェントコンテキストへの API キー漏洩 |
-→ [30のビルトインポリシー一覧](https://docs.befailproof.ai/built-in-policies)
+→ [組み込みポリシー全30件](https://docs.befailproof.ai/built-in-policies)
---
-## カスタムポリシー
+## 独自ポリシーの作成
-`.failproofai/policies/` にファイルを配置するだけで自動的に読み込まれます。フラグの設定は不要です。
-コミットすれば、次回プル時にチーム全員へ反映されます。
+`.failproofai/policies/` にファイルを置くだけで自動的に読み込まれます。フラグは不要です。
+コミットすれば、次回プル時にチーム全員に適用されます。
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -128,13 +128,13 @@ customPolicies.add({
});
```
-各ポリシーで利用できる3つの判定:
+各ポリシーで使用できる判定は3種類です:
| 判定 | 効果 |
|---|---|
| `allow()` | 操作を許可する |
-| `deny(message)` | ブロックする — メッセージがエージェントに返される |
-| `instruct(message)` | 通過させるが、エージェントの次のプロンプトにコンテキストを追加する |
+| `deny(message)` | 操作をブロックし、メッセージをエージェントに返す |
+| `instruct(message)` | 操作を通過させ、エージェントの次のプロンプトにコンテキストを追加する |
→ [カスタムポリシーガイド](https://docs.befailproof.ai/custom-policies)
@@ -142,9 +142,7 @@ customPolicies.add({
## セッションの可視化
-エージェントが行うすべてのツール呼び出しはローカルに記録されます。ダッシュボードには実行内容・
-ブロックされた内容・ポリシーがエージェントに伝えた内容が表示されるため、問題が発生しても
-何が起きたのか推測する必要がありません。→ [ダッシュボードガイド](https://docs.befailproof.ai/dashboard)
+エージェントが行うすべてのツール呼び出しはローカルに記録されます。ダッシュボードでは、実行内容・ブロックされた操作・ポリシーがエージェントに伝えた内容を確認できるため、問題発生時の原因究明に迷いません。→ [ダッシュボードガイド](https://docs.befailproof.ai/dashboard)
---
@@ -153,8 +151,8 @@ customPolicies.add({
| | |
|---|---|
| [はじめに](https://docs.befailproof.ai/getting-started) | インストールと最初のステップ |
-| [ビルトインポリシー](https://docs.befailproof.ai/built-in-policies) | パラメーター付き全30ポリシー |
-| [カスタムポリシー](https://docs.befailproof.ai/custom-policies) | 独自ポリシーの作成方法 |
+| [組み込みポリシー](https://docs.befailproof.ai/built-in-policies) | パラメーター付き全30ポリシー |
+| [カスタムポリシー](https://docs.befailproof.ai/custom-policies) | 独自ポリシーの書き方 |
| [設定](https://docs.befailproof.ai/configuration) | 設定スコープとマージルール |
| [ダッシュボード](https://docs.befailproof.ai/dashboard) | セッションモニターとポリシーアクティビティ |
| [アーキテクチャ](https://docs.befailproof.ai/architecture) | フックシステムの仕組み |
@@ -163,13 +161,13 @@ customPolicies.add({
## ライセンス
-[Commons Clause](https://commonsclause.com/) 付き MIT ライセンス — 社内利用・個人利用は無料。failproofai 自体の商用転売には別途契約が必要です。全文は [LICENSE](./LICENSE) をご参照ください。
+[Commons Clause](https://commonsclause.com/) 付き MIT ライセンス — 社内利用・個人利用は無料。failproofai 自体の商用再販には別途契約が必要です。全文は [LICENSE](./LICENSE) をご確認ください。
---
## コントリビューション
-[CONTRIBUTING.md](./CONTRIBUTING.md) をご覧ください。新しいポリシー・エッジケース・翻訳はいずれも歓迎します。
+[CONTRIBUTING.md](./CONTRIBUTING.md) をご覧ください。新しいポリシー、エッジケースの対応、翻訳はいずれも歓迎します。
---
diff --git a/docs/i18n/README.ko.md b/docs/i18n/README.ko.md
index 38bf1e3..bfa47ef 100644
--- a/docs/i18n/README.ko.md
+++ b/docs/i18n/README.ko.md
@@ -17,8 +17,8 @@
**번역:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
**코딩 에이전트를 위한 런타임 장애 해결 도구.**
-Claude Code 및 Codex에 연동됩니다. 루프, 위험한 동작, 시크릿 누출을
-인시던트가 되기 전에 차단합니다. 지연 없음. 로컬에서 실행.
+Claude Code 및 Codex와 연동됩니다. 루프, 위험한 동작, 시크릿 유출을
+인시던트가 되기 전에 차단합니다. 지연 시간 없음. 로컬에서 실행.
@@ -28,7 +28,7 @@ Claude Code 및 Codex에 연동됩니다. 루프, 위험한 동작, 시크릿
---
-## 지원되는 에이전트 CLI
+## 지원하는 에이전트 CLI
@@ -79,7 +79,7 @@ Claude Code 및 Codex에 연동됩니다. 루프, 위험한 동작, 시크릿
-> 하나 또는 여러 CLI를 조합하여 훅을 설치하세요: `failproofai policies --install --cli opencode pi gemini` (또는 `--cli claude codex copilot cursor opencode pi gemini`). `--cli`를 생략하면 설치된 CLI를 자동으로 감지하고 선택을 안내합니다.
+> 하나 또는 여러 CLI를 조합하여 훅을 설치할 수 있습니다: `failproofai policies --install --cli opencode pi gemini` (또는 `--cli claude codex copilot cursor opencode pi gemini`). `--cli`를 생략하면 설치된 CLI를 자동 감지하고 선택 프롬프트를 표시합니다.
---
@@ -87,11 +87,11 @@ Claude Code 및 Codex에 연동됩니다. 루프, 위험한 동작, 시크릿
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # 또는 `failproofai`를 실행하고 첫 실행 프롬프트에서 수락
failproofai
```
-30개의 기본 제공 정책이 즉시 활성화됩니다. 대시보드는 `localhost:8020`에서 확인할 수 있습니다.
+30개의 내장 정책이 즉시 활성화됩니다. 대시보드는 `localhost:8020`에서 확인할 수 있습니다. `FAILPROOFAI_NO_FIRST_RUN=1`로 첫 실행 프롬프트를 비활성화할 수 있습니다.
---
@@ -99,20 +99,20 @@ failproofai
| 정책 | 차단 내용 |
|---|---|
-| `block-push-master` | `main` / `master`에 직접 푸시 |
+| `block-push-master` | `main` / `master` 브랜치로의 직접 푸시 |
| `block-force-push` | `git push --force` |
-| `block-work-on-main` | `main` / `master`에 커밋, 머지, 리베이스 |
+| `block-work-on-main` | `main` / `master`에서의 커밋, 머지, 리베이스 |
| `block-rm-rf` | 재귀적 파일 삭제 |
-| `sanitize-api-keys` | 에이전트 컨텍스트로 누출되는 API 키 |
+| `sanitize-api-keys` | 에이전트 컨텍스트로 유출되는 API 키 |
-→ [30개 기본 제공 정책 전체 목록](https://docs.befailproof.ai/built-in-policies)
+→ [30개의 내장 정책 전체 목록](https://docs.befailproof.ai/built-in-policies)
---
-## 나만의 정책
+## 나만의 정책 작성
-`.failproofai/policies/` 디렉터리에 파일을 추가하면 별도 플래그 없이 자동으로 로드됩니다.
-커밋하면 다음 풀 시 팀 전체에 적용됩니다.
+`.failproofai/policies/` 폴더에 파일을 추가하면 별도 설정 없이 자동으로 로드됩니다.
+커밋해 두면 팀원 전체가 다음 pull 시 동일한 정책을 적용받습니다.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -132,9 +132,9 @@ customPolicies.add({
| 결정 | 효과 |
|---|---|
-| `allow()` | 작업 허용 |
-| `deny(message)` | 차단 — 메시지가 에이전트에게 반환됨 |
-| `instruct(message)` | 통과 허용, 단 에이전트의 다음 프롬프트에 컨텍스트 추가 |
+| `allow()` | 작업을 허용합니다 |
+| `deny(message)` | 작업을 차단하고 메시지를 에이전트에 반환합니다 |
+| `instruct(message)` | 작업은 허용하되, 에이전트의 다음 프롬프트에 컨텍스트를 추가합니다 |
→ [커스텀 정책 가이드](https://docs.befailproof.ai/custom-policies)
@@ -142,9 +142,9 @@ customPolicies.add({
## 세션 가시성
-에이전트가 실행하는 모든 도구 호출은 로컬에 기록됩니다. 대시보드에서 실행된 항목,
-차단된 항목, 정책이 에이전트에게 전달한 내용을 확인할 수 있어 — 문제가 발생했을 때
-더 이상 추측할 필요가 없습니다. → [대시보드 가이드](https://docs.befailproof.ai/dashboard)
+에이전트가 실행한 모든 툴 호출은 로컬에 기록됩니다. 대시보드에서 실행된 항목,
+차단된 항목, 정책이 에이전트에 전달한 내용을 확인할 수 있어 문제 발생 시
+추측에 의존하지 않아도 됩니다. → [대시보드 가이드](https://docs.befailproof.ai/dashboard)
---
@@ -153,25 +153,25 @@ customPolicies.add({
| | |
|---|---|
| [시작하기](https://docs.befailproof.ai/getting-started) | 설치 및 첫 번째 단계 |
-| [기본 제공 정책](https://docs.befailproof.ai/built-in-policies) | 파라미터가 포함된 30개 정책 전체 |
-| [커스텀 정책](https://docs.befailproof.ai/custom-policies) | 직접 작성하기 |
+| [내장 정책](https://docs.befailproof.ai/built-in-policies) | 매개변수를 포함한 30개의 정책 |
+| [커스텀 정책](https://docs.befailproof.ai/custom-policies) | 직접 정책 작성하기 |
| [설정](https://docs.befailproof.ai/configuration) | 설정 범위 및 병합 규칙 |
| [대시보드](https://docs.befailproof.ai/dashboard) | 세션 모니터 및 정책 활동 |
-| [아키텍처](https://docs.befailproof.ai/architecture) | 훅 시스템 동작 방식 |
+| [아키텍처](https://docs.befailproof.ai/architecture) | 훅 시스템의 작동 방식 |
---
## 라이선스
-[Commons Clause](https://commonsclause.com/)가 포함된 MIT — 내부 및 개인 사용은 무료이며, failproofai 자체의 상업적 재판매는 별도 계약이 필요합니다. 전문은 [LICENSE](./LICENSE)를 참고하세요.
+[Commons Clause](https://commonsclause.com/)가 포함된 MIT 라이선스 — 내부 및 개인 사용은 무료이며, failproofai 자체의 상업적 재판매에는 별도의 계약이 필요합니다. 전문은 [LICENSE](./LICENSE)를 참조하세요.
---
-## 기여
+## 기여하기
-[CONTRIBUTING.md](./CONTRIBUTING.md)를 참고하세요. 새로운 정책, 엣지 케이스, 번역 모두 환영합니다.
+[CONTRIBUTING.md](./CONTRIBUTING.md)를 참조하세요. 새로운 정책, 엣지 케이스, 번역 기여 모두 환영합니다.
---
-[Nivedit Jain](https://github.com/NiveditJain)과 [Nikita Agarwal](https://github.com/nk-ag)이 만들었습니다.
+[Nivedit Jain](https://github.com/NiveditJain)과 [Nikita Agarwal](https://github.com/nk-ag)이 개발했습니다.
[befailproof.ai](https://befailproof.ai)
diff --git a/docs/i18n/README.pt-br.md b/docs/i18n/README.pt-br.md
index fafc02f..6784d2b 100644
--- a/docs/i18n/README.pt-br.md
+++ b/docs/i18n/README.pt-br.md
@@ -79,7 +79,7 @@ antes que se tornem incidentes. Latência zero. Executa localmente.
-> Instale hooks para um ou qualquer combinação: `failproofai policies --install --cli opencode pi gemini` (ou `--cli claude codex copilot cursor opencode pi gemini`). Omita `--cli` para detectar automaticamente os CLIs instalados e ser solicitado a escolher.
+> Instale hooks para um ou qualquer combinação: `failproofai policies --install --cli opencode pi gemini` (ou `--cli claude codex copilot cursor opencode pi gemini`). Omita `--cli` para detectar automaticamente os CLIs instalados e receber uma solicitação interativa.
---
@@ -87,11 +87,11 @@ antes que se tornem incidentes. Latência zero. Executa localmente.
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # ou simplesmente execute `failproofai` e aceite o prompt de primeira execução
failproofai
```
-30 políticas integradas são ativadas imediatamente. Dashboard em `localhost:8020`.
+30 políticas integradas são ativadas imediatamente. Dashboard disponível em `localhost:8020`. Desative o prompt de primeira execução com `FAILPROOFAI_NO_FIRST_RUN=1`.
---
@@ -112,7 +112,7 @@ failproofai
## Suas próprias políticas
Adicione um arquivo em `.failproofai/policies/` — ele é carregado automaticamente, sem necessidade de flags.
-Faça o commit e toda a equipe receberá as alterações no próximo pull.
+Faça o commit e toda a equipe receberá na próxima atualização.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -144,7 +144,7 @@ Três decisões disponíveis para cada política:
Cada chamada de ferramenta feita pelo seu agente é registrada localmente. O dashboard mostra o que foi executado,
o que foi bloqueado e o que a política informou ao agente — para que você não fique no escuro
-quando algo der errado. → [Guia do Dashboard](https://docs.befailproof.ai/dashboard)
+quando algo der errado. → [Guia do dashboard](https://docs.befailproof.ai/dashboard)
---
@@ -154,7 +154,7 @@ quando algo der errado. → [Guia do Dashboard](https://docs.befailproof.ai/dash
|---|---|
| [Primeiros Passos](https://docs.befailproof.ai/getting-started) | Instalação e primeiros passos |
| [Políticas Integradas](https://docs.befailproof.ai/built-in-policies) | Todas as 30 políticas com parâmetros |
-| [Políticas Personalizadas](https://docs.befailproof.ai/custom-policies) | Crie as suas próprias |
+| [Políticas Personalizadas](https://docs.befailproof.ai/custom-policies) | Escreva as suas próprias |
| [Configuração](https://docs.befailproof.ai/configuration) | Escopos de configuração e regras de mesclagem |
| [Dashboard](https://docs.befailproof.ai/dashboard) | Monitor de sessão e atividade de políticas |
| [Arquitetura](https://docs.befailproof.ai/architecture) | Como o sistema de hooks funciona |
@@ -167,7 +167,7 @@ MIT com [Commons Clause](https://commonsclause.com/) — gratuito para uso inter
---
-## Contribuição
+## Contribuindo
Consulte [CONTRIBUTING.md](./CONTRIBUTING.md). Novas políticas, casos extremos e traduções são bem-vindos.
diff --git a/docs/i18n/README.ru.md b/docs/i18n/README.ru.md
index e26a4e6..946c529 100644
--- a/docs/i18n/README.ru.md
+++ b/docs/i18n/README.ru.md
@@ -16,8 +16,8 @@
**Переводы:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**Разрешение ошибок выполнения для кодирующих агентов.**
-Интегрируется с Claude Code и Codex. Перехватывает зацикливания, опасные действия и утечки секретов
+**Разрешение проблем времени выполнения для кодирующих агентов.**
+Интегрируется с Claude Code и Codex. Перехватывает бесконечные циклы, опасные действия и утечки секретов
до того, как они станут инцидентами. Нулевая задержка. Работает локально.
@@ -87,23 +87,23 @@
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # или просто запустите `failproofai` и примите подсказку при первом запуске
failproofai
```
-30 встроенных политик активируются немедленно. Панель управления доступна на `localhost:8020`.
+30 встроенных политик активируются немедленно. Панель управления на `localhost:8020`. Отключите подсказку при первом запуске с помощью `FAILPROOFAI_NO_FIRST_RUN=1`.
---
-## Что это останавливает
+## Что он блокирует
| Политика | Что она блокирует |
|---|---|
-| `block-push-master` | Прямые push в `main` / `master` |
+| `block-push-master` | Прямые push-команды в `main` / `master` |
| `block-force-push` | `git push --force` |
-| `block-work-on-main` | Коммиты, слияния, перестановки на `main` / `master` |
+| `block-work-on-main` | Коммиты, мержи, ребейсы на `main` / `master` |
| `block-rm-rf` | Рекурсивное удаление файлов |
-| `sanitize-api-keys` | Утечки API ключей в контекст агента |
+| `sanitize-api-keys` | API ключи, утекающие в контекст агента |
→ [Все 30 встроенных политик](https://docs.befailproof.ai/built-in-policies)
@@ -111,8 +111,8 @@ failproofai
## Ваши собственные политики
-Поместите файл в `.failproofai/policies/` — он загружается автоматически, никаких флагов не требуется.
-Сделайте коммит, и вся команда получит его при следующем pull.
+Поместите файл в `.failproofai/policies/` — он загружается автоматически, никакие флаги не требуются.
+Коммитьте его, и вся команда получит его при следующем pull.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -122,29 +122,29 @@ customPolicies.add({
match: { events: ["PreToolUse"] },
fn: async (ctx) => {
if (ctx.toolInput?.file_path?.includes("production"))
- return deny("Writes to production paths are blocked.");
+ return deny("Записи в пути production заблокированы.");
return allow();
},
});
```
-Каждой политике доступны три решения:
+Три решения, доступные для каждой политики:
| Решение | Эффект |
|---|---|
| `allow()` | Разрешить операцию |
-| `deny(message)` | Заблокировать её — сообщение возвращается агенту |
-| `instruct(message)` | Пропустить, но добавить контекст к следующему запросу агента |
+| `deny(message)` | Заблокировать — сообщение возвращается агенту |
+| `instruct(message)` | Пропустить, но добавить контекст в следующий запрос агента |
→ [Руководство по пользовательским политикам](https://docs.befailproof.ai/custom-policies)
---
-## Видимость сеанса
+## Видимость сессии
-Каждый вызов инструмента, который делает ваш агент, записывается локально. Панель управления показывает,
-что было выполнено, что было заблокировано и что политика рассказала агенту — поэтому вы не гадаете
-когда что-то идёт не так. → [Руководство панели управления](https://docs.befailproof.ai/dashboard)
+Каждый вызов инструмента, который делает ваш агент, регистрируется локально. Панель управления показывает, что было запущено,
+что было заблокировано и что политика сказала агенту — так что вы не будете гадать,
+когда что-то пойдет не так. → [Руководство по панели управления](https://docs.befailproof.ai/dashboard)
---
@@ -154,16 +154,16 @@ customPolicies.add({
|---|---|
| [Начало работы](https://docs.befailproof.ai/getting-started) | Установка и первые шаги |
| [Встроенные политики](https://docs.befailproof.ai/built-in-policies) | Все 30 политик с параметрами |
-| [Пользовательские политики](https://docs.befailproof.ai/custom-policies) | Напишите свои |
+| [Пользовательские политики](https://docs.befailproof.ai/custom-policies) | Напишите свои собственные |
| [Конфигурация](https://docs.befailproof.ai/configuration) | Области конфигурации и правила слияния |
-| [Панель управления](https://docs.befailproof.ai/dashboard) | Монитор сеанса и активность политик |
+| [Панель управления](https://docs.befailproof.ai/dashboard) | Монитор сессии и активность политик |
| [Архитектура](https://docs.befailproof.ai/architecture) | Как работает система хуков |
---
## Лицензия
-MIT с [Commons Clause](https://commonsclause.com/) — бесплатно для внутреннего и личного использования; коммерческая перепродажа самого failproofai требует отдельного соглашения. Полный текст см. в [LICENSE](./LICENSE).
+MIT с [Commons Clause](https://commonsclause.com/) — свободно для внутреннего и личного использования; коммерческая перепродажа самого failproofai требует отдельного соглашения. Полный текст см. в [LICENSE](./LICENSE).
---
diff --git a/docs/i18n/README.tr.md b/docs/i18n/README.tr.md
index 6914b90..b840811 100644
--- a/docs/i18n/README.tr.md
+++ b/docs/i18n/README.tr.md
@@ -16,9 +16,9 @@
**Çeviriler:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**Kod ajanları için çalışma zamanı hata çözümü.**
-Claude Code ve Codex'e entegre edilir. Döngüleri, tehlikeli işlemleri ve sır sızıntılarını
-bunlar haline gelmeden önce yakalar. Sıfır gecikme. Yerel olarak çalışır.
+**Kodlama ajanları için çalışma zamanı hata çözümü.**
+Claude Code ve Codex içine entegre edilir. Döngüleri, tehlikeli işlemleri ve sır sızıntılarını
+incident olmadan önce yakalar. Sıfır gecikme. Yerel çalışır.
@@ -79,7 +79,7 @@ bunlar haline gelmeden önce yakalar. Sıfır gecikme. Yerel olarak çalışır.
-> Bir veya herhangi bir kombinasyon için kancaları yükleyin: `failproofai policies --install --cli opencode pi gemini` (veya `--cli claude codex copilot cursor opencode pi gemini`). Otomatik olarak kurulu CLI'ları algılamak ve istemek için `--cli` kısmını çıkarın.
+> Hook'ları bir veya birkaç kombinasyon için kurun: `failproofai policies --install --cli opencode pi gemini` (veya `--cli claude codex copilot cursor opencode pi gemini`). Kurulan CLI'ları otomatik olarak algılamak ve sorunca `--cli` parametresini atlayın.
---
@@ -87,22 +87,22 @@ bunlar haline gelmeden önce yakalar. Sıfır gecikme. Yerel olarak çalışır.
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # veya sadece `failproofai` çalıştırın ve ilk çalıştırma istemini kabul edin
failproofai
```
-30 yerleşik politika hemen etkinleşir. Kontrol paneli `localhost:8020` adresinde bulunur.
+30 yerleşik politika hemen etkinleştirilir. Pano `localhost:8020` adresindedir. İlk çalıştırma istemini `FAILPROOFAI_NO_FIRST_RUN=1` ile devre dışı bırakın.
---
-## Neleri engeller
+## Neleri durdurur
| Politika | Neyi engeller |
|---|---|
-| `block-push-master` | `main` / `master` adresine doğrudan push işlemleri |
+| `block-push-master` | `main` / `master` dalına doğrudan gönderimler |
| `block-force-push` | `git push --force` |
-| `block-work-on-main` | `main` / `master` adresinde yapılan commitler, merge'ler, rebaseler |
-| `block-rm-rf` | Dosyaların yinelemeli silinmesi |
+| `block-work-on-main` | `main` / `master` üzerinde commit'ler, merge'ler, rebase'ler |
+| `block-rm-rf` | Özyinelemeli dosya silme |
| `sanitize-api-keys` | API anahtarlarının ajan bağlamına sızması |
→ [Tüm 30 yerleşik politika](https://docs.befailproof.ai/built-in-policies)
@@ -111,8 +111,8 @@ failproofai
## Kendi politikalarınız
-`.failproofai/policies/` klasörüne bir dosya bırakın — otomatik olarak yüklenir, herhangi bir bayrak gerekmez.
-Bunu commit edin ve takımın geri kalanı bir sonraki pull'da alır.
+`.failproofai/policies/` klasörüne bir dosya bırakın — otomatik olarak yüklenir, bayrak gerekmez.
+Commit edin ve tüm takım bunu sonraki çekme sırasında alır.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -122,29 +122,29 @@ customPolicies.add({
match: { events: ["PreToolUse"] },
fn: async (ctx) => {
if (ctx.toolInput?.file_path?.includes("production"))
- return deny("Writes to production paths are blocked.");
+ return deny("Production yollarına yazımlar engellenir.");
return allow();
},
});
```
-Her politika için kullanılabilir üç karar:
+Her politika için üç karar vardır:
| Karar | Etki |
|---|---|
-| `allow()` | İşleme izin verir |
-| `deny(message)` | Engeller — ileti ajana geri gönderilir |
-| `instruct(message)` | İzin verir, ancak ajana sonraki istemi için bağlam ekler |
+| `allow()` | İşleme izin ver |
+| `deny(message)` | Engelle — ileti ajana geri gider |
+| `instruct(message)` | Geçir, ancak ajana sonraki istem içine bağlam ekle |
-→ [Özel politikalar kılavuzu](https://docs.befailproof.ai/custom-policies)
+→ [Özel politikalar rehberi](https://docs.befailproof.ai/custom-policies)
---
## Oturum görünürlüğü
-Ajanınızın yaptığı her araç çağrısı yerel olarak kaydedilir. Kontrol paneli ne çalıştığını,
-ne engellendiğini ve politikanın ajana ne söylediğini gösterir — böylece bir şey yanlış gittiğinde
-tahmin yürütmezsiniz. → [Kontrol paneli kılavuzu](https://docs.befailproof.ai/dashboard)
+Ajanınızın yaptığı her araç çağrısı yerel olarak kaydedilir. Pano ne çalıştırıldığını,
+ne engellendğini ve politikanın ajana ne söylediğini gösterir — böylece bir şey yanlış gittiğinde
+tahmin etmezsiniz. → [Pano rehberi](https://docs.befailproof.ai/dashboard)
---
@@ -153,25 +153,25 @@ tahmin yürütmezsiniz. → [Kontrol paneli kılavuzu](https://docs.befailproof.
| | |
|---|---|
| [Başlangıç](https://docs.befailproof.ai/getting-started) | Kurulum ve ilk adımlar |
-| [Yerleşik Politikalar](https://docs.befailproof.ai/built-in-policies) | Tüm 30 politika ve parametreleri |
+| [Yerleşik Politikalar](https://docs.befailproof.ai/built-in-policies) | Parametrelerle 30 politika |
| [Özel Politikalar](https://docs.befailproof.ai/custom-policies) | Kendi politikalarınızı yazın |
| [Yapılandırma](https://docs.befailproof.ai/configuration) | Yapılandırma kapsamları ve birleştirme kuralları |
-| [Kontrol Paneli](https://docs.befailproof.ai/dashboard) | Oturum monitörü ve politika etkinliği |
-| [Mimari](https://docs.befailproof.ai/architecture) | Kanca sistemi nasıl çalışır |
+| [Pano](https://docs.befailproof.ai/dashboard) | Oturum monitörü ve politika aktivitesi |
+| [Mimari](https://docs.befailproof.ai/architecture) | Hook sistemi nasıl çalışır |
---
## Lisans
-MIT ve [Commons Clause](https://commonsclause.com/) — iç ve kişisel kullanım için ücretsiz; failproofai'nin kendisinin ticari satışı ayrı bir anlaşma gerektirir. Tam metin için [LICENSE](./LICENSE) dosyasına bakın.
+MIT ile [Commons Clause](https://commonsclause.com/) — iç ve kişisel kullanım için ücretsiz; failproofai'nin kendisinin ticari yeniden satışı ayrı bir anlaşma gerektirir. Tam metin için [LICENSE](./LICENSE) bölümüne bakın.
---
## Katkıda bulunma
-Bkz. [CONTRIBUTING.md](./CONTRIBUTING.md). Yeni politikalar, edge case'ler ve çeviriler hoş karşılanır.
+[CONTRIBUTING.md](./CONTRIBUTING.md) bölümüne bakın. Yeni politikalar, uç durumlar ve çeviriler hoş geldiniz.
---
-[Nivedit Jain](https://github.com/NiveditJain) ve [Nikita Agarwal](https://github.com/nk-ag) tarafından oluşturuldu.
+[Nivedit Jain](https://github.com/NiveditJain) ve [Nikita Agarwal](https://github.com/nk-ag) tarafından yapıldı.
[befailproof.ai](https://befailproof.ai)
diff --git a/docs/i18n/README.vi.md b/docs/i18n/README.vi.md
index d96192e..becd0f8 100644
--- a/docs/i18n/README.vi.md
+++ b/docs/i18n/README.vi.md
@@ -16,19 +16,19 @@
**Bản dịch:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**Giải pháp xử lý lỗi runtime cho các agent mã hóa.**
-Tích hợp với Claude Code và Codex. Phát hiện và ngăn chặn các vòng lặp, hành động nguy hiểm,
-và rò rỉ bí mật trước khi chúng trở thành sự cố. Độ trễ bằng không. Chạy trên máy cục bộ.
+**Giải pháp xử lý lỗi thời gian chạy cho agent lập trình.**
+Tích hợp với Claude Code và Codex. Phát hiện vòng lặp, hành động nguy hiểm và rò rỉ bí mật
+trước khi chúng trở thành sự cố. Độ trễ bằng không. Chạy cục bộ.
-
+
---
-## Các agent CLI được hỗ trợ
+## CLI agent được hỗ trợ
@@ -79,7 +79,7 @@ và rò rỉ bí mật trước khi chúng trở thành sự cố. Độ trễ b
-> Cài đặt hook cho một hoặc nhiều CLI: `failproofai policies --install --cli opencode pi gemini` (hoặc `--cli claude codex copilot cursor opencode pi gemini`). Bỏ qua `--cli` để tự động phát hiện các CLI được cài đặt và nhắc.
+> Cài đặt hook cho một hoặc bất kỳ kết hợp nào: `failproofai policies --install --cli opencode pi gemini` (hoặc `--cli claude codex copilot cursor opencode pi gemini`). Bỏ qua `--cli` để tự động phát hiện các CLI được cài đặt và nhắc.
---
@@ -87,11 +87,11 @@ và rò rỉ bí mật trước khi chúng trở thành sự cố. Độ trễ b
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # hoặc chỉ cần chạy `failproofai` và chấp nhận lời nhắc lần chạy đầu tiên
failproofai
```
-30 chính sách tích hợp sẵn được kích hoạt ngay lập tức. Bảng điều khiển tại `localhost:8020`.
+30 chính sách tích hợp sẽ kích hoạt ngay lập tức. Bảng điều khiển tại `localhost:8020`. Tắt lời nhắc lần chạy đầu tiên bằng `FAILPROOFAI_NO_FIRST_RUN=1`.
---
@@ -99,20 +99,20 @@ failproofai
| Chính sách | Những gì nó chặn |
|---|---|
-| `block-push-master` | Đẩy trực tiếp lên `main` / `master` |
+| `block-push-master` | Đẩy trực tiếp tới `main` / `master` |
| `block-force-push` | `git push --force` |
| `block-work-on-main` | Commit, merge, rebase trên `main` / `master` |
-| `block-rm-rf` | Xóa tệp đệ quy |
+| `block-rm-rf` | Xóa file đệ quy |
| `sanitize-api-keys` | API key rò rỉ vào ngữ cảnh agent |
-→ [Tất cả 30 chính sách tích hợp sẵn](https://docs.befailproof.ai/built-in-policies)
+→ [Tất cả 30 chính sách tích hợp](https://docs.befailproof.ai/built-in-policies)
---
## Chính sách của riêng bạn
-Thả một tệp vào `.failproofai/policies/` — nó tải tự động, không cần cờ nào.
-Commit nó và toàn bộ nhóm sẽ có nó ở lần pull tiếp theo.
+Thả một tệp vào `.failproofai/policies/` — nó sẽ tải tự động, không cần cờ nào.
+Commit nó và toàn bộ team sẽ có nó khi pull tiếp theo.
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -130,21 +130,21 @@ customPolicies.add({
Ba quyết định có sẵn cho mỗi chính sách:
-| Quyết định | Hiệu ứng |
+| Quyết định | Hiệu quả |
|---|---|
| `allow()` | Cho phép hoạt động |
-| `deny(message)` | Chặn nó — tin nhắn được gửi lại cho agent |
-| `instruct(message)` | Cho nó thông qua, nhưng thêm ngữ cảnh vào lời nhắc tiếp theo của agent |
+| `deny(message)` | Chặn nó — thông báo được gửi lại cho agent |
+| `instruct(message)` | Cho phép thông qua, nhưng thêm ngữ cảnh vào lời nhắc tiếp theo của agent |
→ [Hướng dẫn chính sách tùy chỉnh](https://docs.befailproof.ai/custom-policies)
---
-## Khả năng hiển thị phiên
+## Hiển thị phiên làm việc
-Mỗi lệnh gọi công cụ mà agent của bạn thực hiện đều được ghi lại cục bộ. Bảng điều khiển cho thấy những gì đã chạy,
-những gì đã bị chặn, và những gì chính sách đã nói với agent — vì vậy bạn không phải đoán
-khi có gì đó không đúng. → [Hướng dẫn bảng điều khiển](https://docs.befailproof.ai/dashboard)
+Mỗi lệnh gọi công cụ mà agent thực hiện được ghi lại cục bộ. Bảng điều khiển cho thấy những gì đã chạy,
+những gì bị chặn, và những gì chính sách đã nói với agent — vì vậy bạn không phải đoán
+khi có điều gì đó không ổn. → [Hướng dẫn bảng điều khiển](https://docs.befailproof.ai/dashboard)
---
@@ -153,7 +153,7 @@ khi có gì đó không đúng. → [Hướng dẫn bảng điều khiển](http
| | |
|---|---|
| [Bắt đầu](https://docs.befailproof.ai/getting-started) | Cài đặt và các bước đầu tiên |
-| [Chính sách tích hợp sẵn](https://docs.befailproof.ai/built-in-policies) | Tất cả 30 chính sách với tham số |
+| [Chính sách tích hợp](https://docs.befailproof.ai/built-in-policies) | Tất cả 30 chính sách với tham số |
| [Chính sách tùy chỉnh](https://docs.befailproof.ai/custom-policies) | Viết chính sách của riêng bạn |
| [Cấu hình](https://docs.befailproof.ai/configuration) | Phạm vi cấu hình và quy tắc hợp nhất |
| [Bảng điều khiển](https://docs.befailproof.ai/dashboard) | Giám sát phiên và hoạt động chính sách |
@@ -163,13 +163,13 @@ khi có gì đó không đúng. → [Hướng dẫn bảng điều khiển](http
## Giấy phép
-MIT với [Commons Clause](https://commonsclause.com/) — miễn phí cho sử dụng nội bộ và cá nhân; bán lại thương mại failproofai yêu cầu một thỏa thuận riêng. Xem [LICENSE](./LICENSE) để biết toàn bộ văn bản.
+MIT với [Commons Clause](https://commonsclause.com/) — miễn phí cho sử dụng nội bộ và cá nhân; việc bán lại thương mại của failproofai cần một thỏa thuận riêng. Xem [LICENSE](./LICENSE) để biết toàn bộ nội dung.
---
## Đóng góp
-Xem [CONTRIBUTING.md](./CONTRIBUTING.md). Chúng tôi hoan nghênh chính sách mới, các trường hợp biên và bản dịch.
+Xem [CONTRIBUTING.md](./CONTRIBUTING.md). Chúng tôi chào đón chính sách mới, các trường hợp đặc biệt và bản dịch.
---
diff --git a/docs/i18n/README.zh.md b/docs/i18n/README.zh.md
index 59e8035..cb86e78 100644
--- a/docs/i18n/README.zh.md
+++ b/docs/i18n/README.zh.md
@@ -16,8 +16,8 @@
**翻译版本:** [简体中文](./docs/i18n/README.zh.md) · [日本語](./docs/i18n/README.ja.md) · [한국어](./docs/i18n/README.ko.md) · [Español](./docs/i18n/README.es.md) · [Português](./docs/i18n/README.pt-br.md) · [Deutsch](./docs/i18n/README.de.md) · [Français](./docs/i18n/README.fr.md) · [Русский](./docs/i18n/README.ru.md) · [हिन्दी](./docs/i18n/README.hi.md) · [Türkçe](./docs/i18n/README.tr.md) · [Tiếng Việt](./docs/i18n/README.vi.md) · [Italiano](./docs/i18n/README.it.md) · [العربية](./docs/i18n/README.ar.md) · [עברית](./docs/i18n/README.he.md)
-**为编码智能体提供运行时故障处理能力。**
-接入 Claude Code 和 Codex,在循环调用、危险操作和密钥泄露演变成线上事故之前将其拦截。零延迟,本地运行。
+**为编程 Agent 提供运行时故障处置能力。**
+接入 Claude Code 和 Codex,在问题演变为事故之前,拦截循环、危险操作和密钥泄露。零延迟,本地运行。
@@ -27,7 +27,7 @@
---
-## 支持的智能体 CLI
+## 支持的 Agent CLI
@@ -78,7 +78,7 @@
-> 可为一个或多个 CLI 安装 hooks:`failproofai policies --install --cli opencode pi gemini`(或 `--cli claude codex copilot cursor opencode pi gemini`)。省略 `--cli` 则自动检测已安装的 CLI 并提示选择。
+> 可为单个或任意组合的 CLI 安装 hooks:`failproofai policies --install --cli opencode pi gemini`(或 `--cli claude codex copilot cursor opencode pi gemini`)。省略 `--cli` 将自动检测已安装的 CLI 并进行提示。
---
@@ -86,11 +86,11 @@
```sh
npm install -g failproofai
-failproofai policies --install
+failproofai policies --install # 或直接运行 `failproofai` 并在首次运行提示中确认
failproofai
```
-30 条内置策略即刻生效。控制台地址:`localhost:8020`。
+30 条内置策略即刻生效。控制台地址:`localhost:8020`。可通过设置 `FAILPROOFAI_NO_FIRST_RUN=1` 禁用首次运行提示。
---
@@ -100,9 +100,9 @@ failproofai
|---|---|
| `block-push-master` | 直接推送到 `main` / `master` 分支 |
| `block-force-push` | `git push --force` |
-| `block-work-on-main` | 在 `main` / `master` 上提交、合并、变基 |
+| `block-work-on-main` | 在 `main` / `master` 上执行提交、合并、变基 |
| `block-rm-rf` | 递归删除文件 |
-| `sanitize-api-keys` | API 密钥泄露进智能体上下文 |
+| `sanitize-api-keys` | API 密钥泄露到 Agent 上下文 |
→ [全部 30 条内置策略](https://docs.befailproof.ai/built-in-policies)
@@ -110,8 +110,8 @@ failproofai
## 自定义策略
-将文件放入 `.failproofai/policies/` 目录即自动加载,无需任何标志。
-提交到版本库后,团队成员下次拉取代码即可同步生效。
+将文件放入 `.failproofai/policies/` 目录即可自动加载,无需任何额外参数。
+提交到版本库后,团队成员下次拉取时即可同步生效。
```js
import { customPolicies, deny, allow } from "failproofai";
@@ -127,13 +127,13 @@ customPolicies.add({
});
```
-每条策略可返回以下三种决策:
+每条策略可使用三种决策:
| 决策 | 效果 |
|---|---|
| `allow()` | 允许该操作 |
-| `deny(message)` | 阻止操作——消息将回传给智能体 |
-| `instruct(message)` | 放行操作,但在智能体的下一条提示中附加上下文说明 |
+| `deny(message)` | 阻止该操作——消息将返回给 Agent |
+| `instruct(message)` | 放行,但向 Agent 的下一条提示中附加上下文信息 |
→ [自定义策略指南](https://docs.befailproof.ai/custom-policies)
@@ -141,7 +141,7 @@ customPolicies.add({
## 会话可见性
-智能体的每次工具调用均在本地记录。控制台展示已执行的操作、被拦截的内容以及策略向智能体反馈的信息——出现问题时无需猜测。→ [控制台指南](https://docs.befailproof.ai/dashboard)
+Agent 发起的每次工具调用都会在本地记录。控制台将展示已执行的操作、被拦截的内容以及策略反馈给 Agent 的信息——让你在出现问题时无需凭空猜测。→ [控制台指南](https://docs.befailproof.ai/dashboard)
---
@@ -149,9 +149,9 @@ customPolicies.add({
| | |
|---|---|
-| [快速上手](https://docs.befailproof.ai/getting-started) | 安装与入门步骤 |
+| [快速上手](https://docs.befailproof.ai/getting-started) | 安装与初始配置 |
| [内置策略](https://docs.befailproof.ai/built-in-policies) | 全部 30 条策略及参数说明 |
-| [自定义策略](https://docs.befailproof.ai/custom-policies) | 编写自己的策略 |
+| [自定义策略](https://docs.befailproof.ai/custom-policies) | 编写你自己的策略 |
| [配置](https://docs.befailproof.ai/configuration) | 配置作用域与合并规则 |
| [控制台](https://docs.befailproof.ai/dashboard) | 会话监控与策略活动 |
| [架构](https://docs.befailproof.ai/architecture) | Hook 系统的工作原理 |
@@ -160,13 +160,13 @@ customPolicies.add({
## 许可证
-MIT 附加 [Commons Clause](https://commonsclause.com/) 条款——个人及内部使用免费;将 failproofai 本身用于商业转售需另行签订协议。完整条款请参阅 [LICENSE](./LICENSE)。
+MIT 附加 [Commons Clause](https://commonsclause.com/) ——内部使用和个人使用免费;将 failproofai 本身用于商业转售需另行签订协议。完整条款请参阅 [LICENSE](./LICENSE)。
---
## 贡献
-请参阅 [CONTRIBUTING.md](./CONTRIBUTING.md)。欢迎贡献新策略、边界用例以及翻译内容。
+详见 [CONTRIBUTING.md](./CONTRIBUTING.md)。欢迎提交新策略、边界用例和翻译内容。
---
diff --git a/docs/introduction.mdx b/docs/introduction.mdx
index b1e9582..2d1989c 100644
--- a/docs/introduction.mdx
+++ b/docs/introduction.mdx
@@ -50,7 +50,7 @@ bun add -g failproofai
```bash
-failproofai policies --install # enable policies
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
failproofai # launch the dashboard
```
diff --git a/docs/it/cli/environment-variables.mdx b/docs/it/cli/environment-variables.mdx
index 56c895d..b467d09 100644
--- a/docs/it/cli/environment-variables.mdx
+++ b/docs/it/cli/environment-variables.mdx
@@ -7,28 +7,34 @@ description: "Configura il comportamento di failproofai con variabili d'ambiente
| Variabile | Descrizione |
|----------|-------------|
-| `PORT` | Porta del dashboard (predefinita: `8020`) |
-| `CLAUDE_PROJECTS_PATH` | Sovrascrive il percorso dove si trovano le cartelle dei progetti Claude Code |
-| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Pagine del dashboard da nascondere, separate da virgola |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Host/IP autorizzati ad accedere alle risorse di sviluppo. Equivalente a `--allowed-origins`. |
+| `PORT` | Porta del dashboard (default: `8020`) |
+| `CLAUDE_PROJECTS_PATH` | Sostituisci la posizione dove vengono trovate le cartelle di progetto di Claude Code |
+| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Pagine del dashboard separate da virgole da nascondere |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Host/IP autorizzati ad accedere alle risorse di sviluppo. Uguale a `--allowed-origins`. |
## Logging
| Variabile | Descrizione |
|----------|-------------|
-| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Livello di log del server (predefinito: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | Percorso personalizzato del file di log degli hook, oppure `true` per il predefinito (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Livello di log del server (default: `warn`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | Percorso file di log personalizzato, o `true` per quello predefinito (`~/.failproofai/logs/hooks.log`) |
## Telemetria
| Variabile | Descrizione |
|----------|-------------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Disabilita la telemetria anonima di utilizzo |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Disabilita la telemetria d'utilizzo anonima |
+
+## Prompt della prima esecuzione
+
+| Variabile | Descrizione |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | Salta il prompt che offre di installare le policy alla prima invocazione di `failproofai` |
## LLM (per la valutazione delle policy)
| Variabile | Descrizione |
|----------|-------------|
-| `FAILPROOFAI_LLM_BASE_URL` | Endpoint dell'API LLM (predefinito: `https://api.openai.com/v1`) |
+| `FAILPROOFAI_LLM_BASE_URL` | Endpoint dell'API LLM (default: `https://api.openai.com/v1`) |
| `FAILPROOFAI_LLM_API_KEY` | Chiave API per le policy basate su LLM |
-| `FAILPROOFAI_LLM_MODEL` | Nome del modello (predefinito: `gpt-4o-mini`) |
\ No newline at end of file
+| `FAILPROOFAI_LLM_MODEL` | Nome del modello (default: `gpt-4o-mini`) |
\ No newline at end of file
diff --git a/docs/it/introduction.mdx b/docs/it/introduction.mdx
index a62b5e4..1a3419e 100644
--- a/docs/it/introduction.mdx
+++ b/docs/it/introduction.mdx
@@ -1,36 +1,36 @@
---
title: "Failproof AI"
-description: "FailproofAI fornisce agli agenti AI 39 politiche di guasto integrate che rilevano cicli infiniti, perdite di segreti, chiamate di strumento distruttive e altro ancora in una singola installazione."
+description: "FailproofAI fornisce agli agenti AI 39 politiche di fallimento integrate che intercettano loop, perdite di segreti, chiamate di strumenti distruttive e altro ancora in una singola installazione."
---
[](https://www.npmjs.com/package/failproofai)
-Hook e politiche per **la gestione dei guasti dell'AI**, **il recupero dagli errori** e **l'affidabilità degli LLM**. Mantieni i tuoi agenti AI affidabili e in esecuzione autonoma su **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** e **Agents SDK**.
+Hook e politiche per **la gestione dei fallimenti nell'AI**, **il recupero dagli errori** e **l'affidabilità degli LLM**. Mantieni i tuoi agenti AI affidabili e in esecuzione autonoma su **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** e **Agents SDK**.
-Gli agenti AI falliscono in modi prevedibili. Eseguono comandi distruttivi, perdono segreti, si allontanano dal compito, rimangono bloccati in cicli infiniti o spingono direttamente al branch principale. Se lasciati incustoditi, i piccoli guasti si trasformano in interruzioni di servizio, credenziali esposte e lavoro perso.
+Gli agenti AI falliscono in modi prevedibili. Eseguono comandi distruttivi, perdono segreti, si allontanano dal compito assegnato, rimangono bloccati in loop o eseguono push direttamente al ramo principale. Senza supervisione, i piccoli fallimenti si trasformano in interruzioni di servizio, perdita di credenziali e lavoro perso.
-FailproofAI risolve questo problema con le **politiche**. Queste regole si integrano in ogni chiamata di strumento dell'agente per **rilevare i guasti**, **mitigarli** (bloccare, istruire, sanificare) e **avvisarti** quando qualcosa richiede attenzione. Un dashboard locale ti consente di controllare ogni chiamata di strumento, guasto dell'agente e azione di recupero in seguito.
+FailproofAI risolve questo problema con le **politiche**. Queste regole si collegano a ogni chiamata di strumento dell'agente per **rilevare i fallimenti**, **mitigarli** (bloccare, istruire, sanitizzare) e **avvertirti** quando qualcosa richiede attenzione. Una dashboard locale ti consente di rivedere ogni chiamata di strumento, fallimento dell'agente e azione di recupero in seguito.
Nessun dato lascia la tua macchina.
-## Inizia
+## Inizia subito
- Blocca comandi distruttivi, previeni perdite di segreti, mantieni gli agenti all'interno dei confini del progetto e altro ancora. Tutto pronto all'uso.
+ Blocca comandi distruttivi, previeni perdite di segreti, mantieni gli agenti entro i confini del progetto e molto altro. Tutto disponibile subito.
- Scrivi le tue regole in JavaScript con una semplice API allow / deny / instruct.
+ Scrivi le tue regole in JavaScript con un semplice API allow / deny / instruct.
-
- Scopri cosa hanno fatto i tuoi agenti mentre eri assente. Sfoglia le sessioni, ispeziona le chiamate di strumento, rivedi dove si sono attivate le politiche.
+
+ Scopri cosa hanno fatto i tuoi agenti mentre eri assente. Sfoglia le sessioni, ispeziona le chiamate di strumenti, controlla dove le politiche sono state applicate.
-
- Personalizza qualsiasi politica senza codice. Imposta allowlist, branch protetti o soglie per progetto o globalmente.
+
+ Regola qualsiasi politica senza codice. Imposta allowlist, rami protetti o soglie per progetto o globalmente.
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # enable policies
-failproofai # launch the dashboard
+failproofai policies --install # abilita le politiche (oppure salta — `failproofai` ti offrirà di configurarle al primo avvio)
+failproofai # avvia la dashboard
```
-Consulta la guida [Iniziare](/it/getting-started) per la procedura dettagliata completa.
\ No newline at end of file
+Consulta la guida [Inizia subito](/it/getting-started) per la procedura dettagliata completa.
\ No newline at end of file
diff --git a/docs/ja/cli/environment-variables.mdx b/docs/ja/cli/environment-variables.mdx
index 81970e9..32cf6c6 100644
--- a/docs/ja/cli/environment-variables.mdx
+++ b/docs/ja/cli/environment-variables.mdx
@@ -1,34 +1,40 @@
---
title: 環境変数
-description: "環境変数を使って failproofai の動作を設定する"
+description: "環境変数で failproofai の動作を設定する"
---
## ダッシュボード
| 変数 | 説明 |
|----------|-------------|
-| `PORT` | ダッシュボードのポート(デフォルト: `8020`) |
+| `PORT` | ダッシュボードのポート番号(デフォルト: `8020`) |
| `CLAUDE_PROJECTS_PATH` | Claude Code のプロジェクトフォルダの検索場所を上書きする |
| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | 非表示にするダッシュボードページをカンマ区切りで指定する |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | 開発リソースへのアクセスを許可するホスト/IP。`--allowed-origins` と同じ。 |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | 開発リソースへのアクセスを許可するホスト/IP。`--allowed-origins` と同等。 |
## ログ
| 変数 | 説明 |
|----------|-------------|
| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | サーバーのログレベル(デフォルト: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | フックログファイルのカスタムパス。`true` を指定するとデフォルトパス(`~/.failproofai/logs/hooks.log`)が使用される |
+| `FAILPROOFAI_HOOK_LOG_FILE` | フックログファイルのカスタムパス。`true` を指定するとデフォルトパス(`~/.failproofai/logs/hooks.log`)を使用する |
-## テレメトリー
+## テレメトリ
| 変数 | 説明 |
|----------|-------------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | 匿名の使用状況テレメトリーを無効にする |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | 匿名の利用状況テレメトリを無効にする |
+
+## 初回起動時のプロンプト
+
+| 変数 | 説明 |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | `failproofai` を引数なしで初めて実行したときに表示される、ポリシーのインストールを提案するプロンプトをスキップする |
## LLM(ポリシー評価用)
| 変数 | 説明 |
|----------|-------------|
-| `FAILPROOFAI_LLM_BASE_URL` | LLM API エンドポイント(デフォルト: `https://api.openai.com/v1`) |
-| `FAILPROOFAI_LLM_API_KEY` | LLM 搭載ポリシー用の API キー |
+| `FAILPROOFAI_LLM_BASE_URL` | LLM API のエンドポイント(デフォルト: `https://api.openai.com/v1`) |
+| `FAILPROOFAI_LLM_API_KEY` | LLM を使用したポリシー向けの API キー |
| `FAILPROOFAI_LLM_MODEL` | モデル名(デフォルト: `gpt-4o-mini`) |
\ No newline at end of file
diff --git a/docs/ja/introduction.mdx b/docs/ja/introduction.mdx
index 412d5b9..1a8c0b4 100644
--- a/docs/ja/introduction.mdx
+++ b/docs/ja/introduction.mdx
@@ -1,36 +1,36 @@
---
title: "Failproof AI"
-description: "FailproofAI は AI エージェントに 39 個の組み込みポリシーを提供し、ループ・シークレット漏洩・破壊的なツール呼び出しなどを 1 回のインストールで検出します。"
+description: "FailproofAI は、ループ・シークレット漏洩・破壊的なツール呼び出しなどを検出する 39 個の組み込み失敗ポリシーを、1 回のインストールで提供します。"
---
[](https://www.npmjs.com/package/failproofai)
-**AI 障害処理**・**エラー回復**・**LLM の信頼性**のためのフックとポリシー。**Claude Code**、**OpenAI Codex**、**GitHub Copilot**、**Cursor Agent**、**OpenCode**、**Pi**、**Gemini CLI**、**Agents SDK** 上で AI エージェントを安定させ、自律的に動作し続けさせます。
+**AI 障害ハンドリング**・**エラーリカバリー**・**LLM の信頼性**のためのフックとポリシー。**Claude Code**、**OpenAI Codex**、**GitHub Copilot**、**Cursor Agent**、**OpenCode**、**Pi**、**Gemini CLI**、**Agents SDK** 上で、AI エージェントを安定して自律的に稼働させ続けます。
-AI エージェントは予測可能なパターンで失敗します。破壊的なコマンドを実行し、シークレットを漏洩し、タスクから逸脱し、ループにはまり込み、main ブランチに直接プッシュします。放置すると、小さな障害が障害停止・認証情報の漏洩・作業の損失へと連鎖していきます。
+AI エージェントは、予測可能なパターンで失敗します。破壊的なコマンドを実行したり、シークレットを漏洩させたり、タスクから脱線したり、ループにはまったり、直接 main にプッシュしたりします。放置すると、小さな障害が連鎖してサービス停止・認証情報の漏洩・作業の消失につながります。
-FailproofAI はこれを**ポリシー**で解決します。これらのルールはエージェントのすべてのツール呼び出しにフックし、**障害を検出**・**軽減**(ブロック、指示、サニタイズ)し、注意が必要な場合に**アラートを通知**します。ローカルダッシュボードで、すべてのツール呼び出し・エージェント障害・回復アクションをあとから確認できます。
+FailproofAI はこの問題を **ポリシー** で解決します。これらのルールはすべてのエージェントツール呼び出しにフックし、**障害を検出**・**軽減**(ブロック・指示・サニタイズ)し、対応が必要なときに**アラートを通知**します。ローカルダッシュボードで、すべてのツール呼び出し・エージェント障害・リカバリーアクションを事後に確認できます。
-データはマシンの外に出ません。
+データは一切、外部に送信されません。
## はじめに
- 破壊的なコマンドのブロック、シークレット漏洩の防止、エージェントをプロジェクト境界内に制限するなど、すぐに使えるポリシーを多数用意しています。
+ 破壊的なコマンドのブロック、シークレット漏洩の防止、エージェントをプロジェクト境界内に制限など、すぐに使える機能が揃っています。
- シンプルな allow / deny / instruct API を使って、JavaScript で独自のルールを記述できます。
+ シンプルな allow / deny / instruct API を使って、JavaScript で独自ルールを作成できます。
- 離席中にエージェントが何をしていたかを確認できます。セッションの閲覧、ツール呼び出しの詳細確認、ポリシーが発動した箇所のレビューが可能です。
+ 離席中にエージェントが何をしていたかを確認できます。セッションの閲覧、ツール呼び出しの詳細確認、ポリシーが発火した箇所のレビューが可能です。
-
- コードなしでポリシーを調整できます。プロジェクト単位またはグローバルに、許可リスト・保護ブランチ・しきい値を設定できます。
+
+ コードを書かずにポリシーを調整できます。許可リスト・保護ブランチ・しきい値をプロジェクト単位またはグローバルに設定できます。
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # ポリシーを有効化する
-failproofai # ダッシュボードを起動する
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
+failproofai # launch the dashboard
```
-詳細な手順については [Getting started](/ja/getting-started) ガイドをご覧ください。
\ No newline at end of file
+詳細な手順については、[はじめかた](/ja/getting-started)ガイドをご覧ください。
\ No newline at end of file
diff --git a/docs/ko/cli/environment-variables.mdx b/docs/ko/cli/environment-variables.mdx
index 8696c3e..eee61b3 100644
--- a/docs/ko/cli/environment-variables.mdx
+++ b/docs/ko/cli/environment-variables.mdx
@@ -1,6 +1,6 @@
---
title: 환경 변수
-description: "환경 변수로 failproofai 동작 설정하기"
+description: "환경 변수로 failproofai 동작 구성하기"
---
## 대시보드
@@ -8,7 +8,7 @@ description: "환경 변수로 failproofai 동작 설정하기"
| 변수 | 설명 |
|----------|-------------|
| `PORT` | 대시보드 포트 (기본값: `8020`) |
-| `CLAUDE_PROJECTS_PATH` | Claude Code 프로젝트 폴더 경로 재정의 |
+| `CLAUDE_PROJECTS_PATH` | Claude Code 프로젝트 폴더 위치 재정의 |
| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | 숨길 대시보드 페이지를 쉼표로 구분하여 지정 |
| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | 개발 리소스 접근을 허용할 호스트/IP. `--allowed-origins`와 동일. |
@@ -17,13 +17,19 @@ description: "환경 변수로 failproofai 동작 설정하기"
| 변수 | 설명 |
|----------|-------------|
| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | 서버 로그 레벨 (기본값: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | 커스텀 훅 로그 파일 경로, 또는 기본 경로(`~/.failproofai/logs/hooks.log`) 사용 시 `true` |
+| `FAILPROOFAI_HOOK_LOG_FILE` | 커스텀 훅 로그 파일 경로, 또는 기본 경로(`~/.failproofai/logs/hooks.log`)를 사용하려면 `true` |
-## 텔레메트리
+## 원격 측정
| 변수 | 설명 |
|----------|-------------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | 익명 사용 텔레메트리 비활성화 |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | 익명 사용 현황 원격 측정 비활성화 |
+
+## 최초 실행 프롬프트
+
+| 변수 | 설명 |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | `failproofai`를 인수 없이 처음 실행할 때 정책 설치를 제안하는 프롬프트 건너뛰기 |
## LLM (정책 평가용)
diff --git a/docs/ko/introduction.mdx b/docs/ko/introduction.mdx
index d33f815..080be9a 100644
--- a/docs/ko/introduction.mdx
+++ b/docs/ko/introduction.mdx
@@ -1,36 +1,36 @@
---
title: "Failproof AI"
-description: "FailproofAI는 루프, 시크릿 유출, 파괴적인 도구 호출 등을 한 번의 설치로 감지하는 39가지 내장 실패 정책을 AI 에이전트에 제공합니다."
+description: "FailproofAI는 단 한 번의 설치로 루프, 시크릿 유출, 파괴적인 도구 호출 등을 감지하는 39가지 내장 실패 정책을 AI 에이전트에 제공합니다."
---
[](https://www.npmjs.com/package/failproofai)
-**AI 장애 처리**, **오류 복구**, **LLM 안정성**을 위한 훅과 정책. **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI**, **Agents SDK** 전반에서 AI 에이전트를 안정적으로 자율 실행하세요.
+**AI 장애 처리**, **오류 복구**, **LLM 안정성**을 위한 훅과 정책. **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI**, 그리고 **Agents SDK**에서 AI 에이전트가 안정적으로 자율 실행될 수 있도록 지원합니다.
-AI 에이전트는 예측 가능한 방식으로 실패합니다. 파괴적인 명령을 실행하거나, 시크릿을 유출하거나, 작업 범위를 벗어나 표류하거나, 루프에 빠지거나, main 브랜치에 직접 푸시하기도 합니다. 방치하면 작은 실패가 서비스 장애, 자격증명 유출, 작업 손실로 이어집니다.
+AI 에이전트는 예측 가능한 방식으로 실패합니다. 파괴적인 명령을 실행하거나, 시크릿을 유출하거나, 작업에서 벗어나거나, 루프에 빠지거나, main 브랜치에 직접 푸시하기도 합니다. 방치하면 사소한 실패가 서비스 장애, 자격 증명 유출, 작업 손실로 이어집니다.
-FailproofAI는 **정책**으로 이 문제를 해결합니다. 에이전트의 모든 도구 호출에 연결되어 **장애를 감지**하고, **완화 조치**(차단, 지시, 정제)를 취하며, 주의가 필요한 상황에서 **알림을 전송**하는 규칙입니다. 로컬 대시보드에서 모든 도구 호출, 에이전트 장애, 복구 조치를 사후에 검토할 수 있습니다.
+FailproofAI는 **정책(policies)**으로 이 문제를 해결합니다. 이 규칙들은 모든 에이전트 도구 호출에 연결되어 **장애를 감지**하고, **완화 조치(차단, 지시, 정제)**를 취하며, 주의가 필요할 때 **알림을 전송**합니다. 로컬 대시보드에서 모든 도구 호출, 에이전트 장애, 복구 조치를 나중에 검토할 수 있습니다.
-데이터는 사용자의 머신 밖으로 나가지 않습니다.
+모든 데이터는 사용자의 머신을 벗어나지 않습니다.
## 시작하기
- 파괴적인 명령 차단, 시크릿 유출 방지, 에이전트를 프로젝트 범위 내로 제한하는 등 다양한 기능을 바로 사용할 수 있습니다.
+ 파괴적인 명령 차단, 시크릿 유출 방지, 에이전트를 프로젝트 범위 내로 제한하는 등 다양한 기능을 기본으로 제공합니다.
- 간단한 allow / deny / instruct API로 JavaScript를 사용해 직접 규칙을 작성하세요.
+ 간단한 allow / deny / instruct API로 JavaScript 기반 규칙을 직접 작성하세요.
- 자리를 비운 동안 에이전트가 무엇을 했는지 확인하세요. 세션을 탐색하고, 도구 호출을 검사하며, 정책이 실행된 지점을 검토하세요.
+ 자리를 비운 동안 에이전트가 무엇을 했는지 확인하세요. 세션을 탐색하고, 도구 호출을 검사하며, 정책이 실행된 위치를 검토할 수 있습니다.
- 코드 없이 모든 정책을 조정하세요. 프로젝트별 또는 전역으로 허용 목록, 보호 브랜치, 임계값을 설정할 수 있습니다.
+ 코드 없이 모든 정책을 조정하세요. 허용 목록, 보호 브랜치, 임계값을 프로젝트별 또는 전역으로 설정할 수 있습니다.
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # 정책 활성화
-failproofai # 대시보드 실행
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
+failproofai # launch the dashboard
```
-전체 안내는 [시작하기](/ko/getting-started) 가이드를 참고하세요.
\ No newline at end of file
+전체 설명은 [시작하기](/ko/getting-started) 가이드를 참고하세요.
\ No newline at end of file
diff --git a/docs/pt-br/cli/environment-variables.mdx b/docs/pt-br/cli/environment-variables.mdx
index 25b44ec..90dac0a 100644
--- a/docs/pt-br/cli/environment-variables.mdx
+++ b/docs/pt-br/cli/environment-variables.mdx
@@ -10,7 +10,7 @@ description: "Configure o comportamento do failproofai com variáveis de ambient
| `PORT` | Porta do dashboard (padrão: `8020`) |
| `CLAUDE_PROJECTS_PATH` | Substitui o local onde as pastas de projetos do Claude Code são encontradas |
| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Páginas do dashboard a ocultar, separadas por vírgula |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Hosts/IPs autorizados a acessar recursos de desenvolvimento. Equivalente a `--allowed-origins`. |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Hosts/IPs com permissão para acessar recursos de desenvolvimento. Equivalente a `--allowed-origins`. |
## Logging
@@ -25,6 +25,12 @@ description: "Configure o comportamento do failproofai com variáveis de ambient
|----------|-----------|
| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Desativa a telemetria anônima de uso |
+## Prompt de primeira execução
+
+| Variável | Descrição |
+|----------|-----------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | Ignora o prompt que oferece a instalação de políticas na primeira execução simples do `failproofai` |
+
## LLM (para avaliação de políticas)
| Variável | Descrição |
diff --git a/docs/pt-br/introduction.mdx b/docs/pt-br/introduction.mdx
index 65d4fdd..7121b58 100644
--- a/docs/pt-br/introduction.mdx
+++ b/docs/pt-br/introduction.mdx
@@ -1,24 +1,24 @@
---
title: "Failproof AI"
-description: "FailproofAI oferece aos agentes de IA 39 políticas de falha integradas que detectam loops, vazamentos de segredos, chamadas de ferramentas destrutivas e muito mais em uma única instalação."
+description: "FailproofAI oferece aos agentes de IA 39 políticas de falha integradas que detectam loops, vazamentos de segredos, chamadas destrutivas de ferramentas e muito mais em uma única instalação."
---
[](https://www.npmjs.com/package/failproofai)
-Hooks e políticas para **tratamento de falhas de IA**, **recuperação de erros** e **confiabilidade de LLM**. Mantenha seus agentes de IA confiáveis e funcionando de forma autônoma no **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** e no **Agents SDK**.
+Hooks e políticas para **tratamento de falhas de IA**, **recuperação de erros** e **confiabilidade de LLM**. Mantenha seus agentes de IA confiáveis e em execução autônoma no **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** e no **Agents SDK**.
-Agentes de IA falham de maneiras previsíveis. Eles executam comandos destrutivos, vazam segredos, desviam do objetivo, ficam presos em loops ou fazem push direto para a branch main. Sem supervisão, pequenas falhas se transformam em interrupções de serviço, credenciais expostas e trabalho perdido.
+Agentes de IA falham de maneiras previsíveis. Eles executam comandos destrutivos, vazam segredos, desviam das tarefas, ficam presos em loops ou fazem push direto para a main. Sem supervisão, pequenas falhas se transformam em interrupções, credenciais expostas e trabalho perdido.
-FailproofAI resolve isso com **políticas**. Essas regras se conectam a cada chamada de ferramenta do agente para **detectar falhas**, **mitigá-las** (bloquear, instruir, sanitizar) e **alertar você** quando algo precisa de atenção. Um painel local permite revisar cada chamada de ferramenta, falha do agente e ação de recuperação posteriormente.
+FailproofAI resolve isso com **políticas**. Essas regras se conectam a cada chamada de ferramenta do agente para **detectar falhas**, **mitigá-las** (bloquear, instruir, sanitizar) e **alertar você** quando algo precisa de atenção. Um painel local permite revisar cada chamada de ferramenta, falha do agente e ação de recuperação após o fato.
Nenhum dado sai da sua máquina.
-## Comece agora
+## Primeiros passos
- Bloqueie comandos destrutivos, evite vazamentos de segredos, mantenha os agentes dentro dos limites do projeto e muito mais. Tudo pronto para uso.
+ Bloqueie comandos destrutivos, evite vazamento de segredos, mantenha os agentes dentro dos limites do projeto e muito mais. Tudo pronto para uso.
@@ -29,8 +29,8 @@ Nenhum dado sai da sua máquina.
Veja o que seus agentes fizeram enquanto você estava ausente. Navegue pelas sessões, inspecione chamadas de ferramentas e revise onde as políticas foram acionadas.
-
- Ajuste qualquer política sem escrever código. Defina listas de permissões, branches protegidas ou limites por projeto ou globalmente.
+
+ Ajuste qualquer política sem escrever código. Defina listas de permissões, branches protegidos ou limites por projeto ou globalmente.
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # ativar políticas
-failproofai # iniciar o painel
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
+failproofai # launch the dashboard
```
-Consulte o guia de [Primeiros passos](/pt-br/getting-started) para um passo a passo completo.
\ No newline at end of file
+Consulte o guia [Primeiros passos](/pt-br/getting-started) para um passo a passo completo.
\ No newline at end of file
diff --git a/docs/ru/cli/environment-variables.mdx b/docs/ru/cli/environment-variables.mdx
index 630a277..de9251d 100644
--- a/docs/ru/cli/environment-variables.mdx
+++ b/docs/ru/cli/environment-variables.mdx
@@ -8,16 +8,16 @@ description: "Настройте поведение failproofai с помощь
| Переменная | Описание |
|----------|-------------|
| `PORT` | Порт dashboard (по умолчанию: `8020`) |
-| `CLAUDE_PROJECTS_PATH` | Переопределить местоположение папок проектов Claude Code |
-| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Разделённый запятыми список страниц dashboard для скрытия |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Хосты/IP-адреса, которым разрешен доступ к dev ресурсам. Аналогично `--allowed-origins`. |
+| `CLAUDE_PROJECTS_PATH` | Переопределить расположение папок проектов Claude Code |
+| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Разделенный запятыми список страниц dashboard для скрытия |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Узлы/IP-адреса, которым разрешен доступ к ресурсам разработки. Аналогично `--allowed-origins`. |
## Логирование
| Переменная | Описание |
|----------|-------------|
| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Уровень логирования сервера (по умолчанию: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | Пользовательский путь к файлу логов hook, или `true` для использования по умолчанию (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | Пользовательский путь к файлу логирования hook, или `true` для пути по умолчанию (`~/.failproofai/logs/hooks.log`) |
## Телеметрия
@@ -25,10 +25,16 @@ description: "Настройте поведение failproofai с помощь
|----------|-------------|
| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Отключить анонимную телеметрию использования |
+## Приглашение при первом запуске
+
+| Переменная | Описание |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | Пропустить приглашение на установку политик при первом вызове bare `failproofai` |
+
## LLM (для оценки политик)
| Переменная | Описание |
|----------|-------------|
-| `FAILPROOFAI_LLM_BASE_URL` | Endpoint LLM API (по умолчанию: `https://api.openai.com/v1`) |
+| `FAILPROOFAI_LLM_BASE_URL` | Конечная точка API LLM (по умолчанию: `https://api.openai.com/v1`) |
| `FAILPROOFAI_LLM_API_KEY` | API ключ для политик на основе LLM |
| `FAILPROOFAI_LLM_MODEL` | Имя модели (по умолчанию: `gpt-4o-mini`) |
\ No newline at end of file
diff --git a/docs/ru/introduction.mdx b/docs/ru/introduction.mdx
index 9eac49e..c95320e 100644
--- a/docs/ru/introduction.mdx
+++ b/docs/ru/introduction.mdx
@@ -1,15 +1,15 @@
---
title: "Failproof AI"
-description: "FailproofAI предоставляет AI агентам 39 встроенных политик отказоустойчивости, которые ловят циклы, утечки секретов, деструктивные вызовы инструментов и многое другое в одной установке."
+description: "FailproofAI предоставляет AI-агентам 39 встроенных политик обработки ошибок, которые ловят циклы, утечки секретов, деструктивные вызовы инструментов и многое другое с одной установкой."
---
[](https://www.npmjs.com/package/failproofai)
-Hooks и политики для **обработки отказов AI**, **восстановления после ошибок** и **надёжности LLM**. Поддерживайте ваши AI агенты надёжными и работающими автономно с **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** и **Agents SDK**.
+Хуки и политики для **обработки ошибок AI**, **восстановления после сбоев** и **надежности LLM**. Делайте ваших AI-агентов надежными и работающими автономно в **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** и **Agents SDK**.
-AI агенты отказывают предсказуемым образом. Они выполняют деструктивные команды, утекают секреты, отвлекаются от задачи, застревают в циклах или пушят напрямую в main. Оставленные без присмотра, небольшие отказы приводят к массовым сбоям, утечкам учётных данных и потере работы.
+AI-агенты отказывают предсказуемым образом. Они запускают деструктивные команды, пропускают секреты, отклоняются от задач, застревают в циклах или отправляют изменения прямо в основную ветку. Без присмотра небольшие сбои превращаются в отключения сервиса, утечки учетных данных и потерю работы.
-FailproofAI решает эту проблему с помощью **политик**. Эти правила встраиваются в каждый вызов инструмента агента для **обнаружения отказов**, **их смягчения** (блокировка, инструкции, санитизация) и **оповещения вас**, когда требуется внимание. Локальная панель управления позволяет вам позже просмотреть каждый вызов инструмента, отказ агента и действие восстановления.
+FailproofAI решает эту проблему с помощью **политик**. Эти правила интегрируются в каждый вызов инструмента агента, чтобы **обнаруживать сбои**, **смягчать их** (блокировать, инструктировать, санитизировать) и **оповещать вас**, когда что-то требует внимания. Локальная панель управления позволяет просмотреть каждый вызов инструмента, сбой агента и действие по восстановлению впоследствии.
Никакие данные не покидают вашу машину.
@@ -18,19 +18,19 @@ FailproofAI решает эту проблему с помощью **полит
- Блокируйте деструктивные команды, предотвращайте утечки секретов, держите агентов внутри границ проекта и многое другое. Всё готово к использованию.
+ Блокируйте деструктивные команды, предотвращайте утечки секретов, держите агентов в границах проекта и многое другое. Все из коробки.
- Напишите свои собственные правила на JavaScript с простым API allow / deny / instruct.
+ Напишите собственные правила на JavaScript с простым API allow / deny / instruct.
- Посмотрите, что делали ваши агенты, пока вас не было. Просмотрите сессии, проверьте вызовы инструментов, посмотрите, где срабатывали политики.
+ Посмотрите, что делали ваши агенты, пока вас не было. Обзор сеансов, проверка вызовов инструментов, просмотр срабатываний политик.
-
- Настраивайте любую политику без кода. Установите разрешённые списки, защищённые ветки или пороги на проект или глобально.
+
+ Настраивайте любую политику без кода. Устанавливайте списки разрешенного, защищенные ветки или пороги для каждого проекта или глобально.
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # enable policies
-failproofai # launch the dashboard
+failproofai policies --install # включить политики (или пропустить — `failproofai` предложит настроить их при первом запуске)
+failproofai # запустить панель управления
```
-Полное руководство смотрите в [Начало работы](/ru/getting-started).
\ No newline at end of file
+Смотрите руководство [Начало работы](/ru/getting-started) для полного описания.
\ No newline at end of file
diff --git a/docs/tr/cli/environment-variables.mdx b/docs/tr/cli/environment-variables.mdx
index c4e43cb..5b4fd9e 100644
--- a/docs/tr/cli/environment-variables.mdx
+++ b/docs/tr/cli/environment-variables.mdx
@@ -1,29 +1,35 @@
---
title: Ortam değişkenleri
-description: "failproofai davranışını ortam değişkenleriyle yapılandırın"
+description: "failproofai davranışını ortam değişkenleri ile yapılandırın"
---
-## Gösterge Paneli
+## Dashboard
| Değişken | Açıklama |
|----------|----------|
-| `PORT` | Gösterge paneli portu (varsayılan: `8020`) |
-| `CLAUDE_PROJECTS_PATH` | Claude Code proje klasörlerinin bulunduğu yeri geçersiz kıl |
-| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Gizlenecek gösterge paneli sayfaları (virgülle ayrılmış) |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Geliştirme kaynaklarına erişmesine izin verilen ana bilgisayarlar/IP'ler. `--allowed-origins` ile aynı. |
+| `PORT` | Dashboard portu (varsayılan: `8020`) |
+| `CLAUDE_PROJECTS_PATH` | Claude Code proje klasörlerinin bulunduğu konumu değiştirin |
+| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Gizlenecek dashboard sayfaları (virgülle ayrılmış) |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Dev kaynaklarına erişim izni verilen ana bilgisayarlar/IP'ler. `--allowed-origins` ile aynı. |
## Günlükleme
| Değişken | Açıklama |
|----------|----------|
| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Sunucu günlük seviyesi (varsayılan: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | Özel hook günlük dosyası yolu veya varsayılan için `true` (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | Özel hook günlük dosyası yolu veya `true` (varsayılan: `~/.failproofai/logs/hooks.log`) |
## Telemetri
| Değişken | Açıklama |
|----------|----------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Anonim kullanım telemetrisini devre dışı bırak |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Anonim kullanım telemetrisini devre dışı bırakın |
+
+## İlk çalıştırma istemesi
+
+| Değişken | Açıklama |
+|----------|----------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | İlk `failproofai` çağrısında politikaları yüklemeyi teklif eden istememi atlayın |
## LLM (politika değerlendirmesi için)
diff --git a/docs/tr/introduction.mdx b/docs/tr/introduction.mdx
index 8ad6a37..69a8181 100644
--- a/docs/tr/introduction.mdx
+++ b/docs/tr/introduction.mdx
@@ -1,36 +1,36 @@
---
title: "Failproof AI"
-description: "FailproofAI, AI ajanlarına 39 adet yerleşik başarısızlık politikası sağlayarak döngüleri, gizli bilgi sızıntılarını, yıkıcı araç çağrılarını ve daha fazlasını tek bir kurulumda yakalar."
+description: "FailproofAI, AI ajanlarına 39 yerleşik hata politikası sunarak döngüleri, gizli sızıntılarını, yıkıcı araç çağrılarını ve daha fazlasını tek bir kurulumda yakalar."
---
[](https://www.npmjs.com/package/failproofai)
-**AI başarısızlık yönetimi**, **hata kurtarma** ve **LLM güvenilirliği** için hook'lar ve politikalar. AI ajanlarınızı **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** ve **Agents SDK** üzerinde güvenilir ve otonom olarak çalışır durumda tutun.
+**AI hata yönetimi**, **hata kurtarması** ve **LLM güvenilirliği** için kancalar ve politikalar. AI ajanlarınızı güvenilir ve **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI** ve **Agents SDK** üzerinde otonom olarak çalışan durumda tutun.
-AI ajanları öngörülebilir şekillerde başarısız olur. Yıkıcı komutlar çalıştırırlar, gizli bilgileri sızıntıya uğratırlar, görevlerden saparlar, döngülere takılırlar ya da doğrudan ana dalına push yaparlar. Gözetimsiz bırakıldığında, küçük başarısızlıklar kesintilere, sızdırılan kimlik bilgilerine ve kaybedilen işe dönüşebilir.
+AI ajanları öngörülebilir şekillerde başarısız olur. Yıkıcı komutları çalıştırırlar, gizli bilgileri sızıtırlar, görevden saparlar, döngüye girip kalırlar veya doğrudan ana dal'a gönderirler. İhmal edilirse, küçük arızalar kesintilere, sızdırılan kimlik bilgilerine ve kayıp işlere dönüşür.
-FailproofAI bunu **politikalar** ile çözer. Bu kurallar, her ajan araç çağrısına kanca atarak **başarısızlıkları tespit eder**, **bunları hafifletir** (engeller, talimat verir, temizler) ve **dikkat gerekli olduğunda sizi uyarır**. Yerel bir gösterge panosu, daha sonra her araç çağrısını, ajan başarısızlığını ve kurtarma işlemini incelemenizi sağlar.
+FailproofAI bunu **politikalar** ile çözer. Bu kurallar, her ajan araç çağrısına bağlanarak **hataları algılar**, **bunları hafifletir** (engelle, talimat ver, temizle) ve **dikkat gerektiğinde sizi uyarır**. Yerel bir pano, her araç çağrısını, ajan arızasını ve kurtarma eylemini sonradan incelemenize olanak tanır.
-Hiçbir veri makinenizi terk etmez.
+Hiçbir veri makinenizden ayrılmaz.
-## Başlayın
+## Başlarken
-
- Yıkıcı komutları engelleyin, gizli bilgi sızıntısını önleyin, ajanları proje sınırları içinde tutun ve daha fazlasını yapın. Hepsi kutudan çıktığı gibi.
+
+ Yıkıcı komutları engelleyin, gizli bilgi sızıntısını önleyin, ajanları proje sınırları içinde tutun ve daha fazlası. Hepsi hazır olarak gelir.
- JavaScript'te basit bir allow / deny / instruct API'si ile kendi kurallarınızı yazın.
+ Basit allow / deny / instruct API'si ile JavaScript'te kendi kurallarınızı yazın.
- Ajanlarınız sizin yokken ne yaptığını görün. Oturumları tarayın, araç çağrılarını inceleyin, politikaların nerede devreye girdiğini gözden geçirin.
+ Ajanlarınız yokken ne yaptığını görün. Oturumlara göz atın, araç çağrılarını inceleyin, politikaların nerede devreye girdiğini gözden geçirin.
-
- Herhangi bir politikayı kod yazmadan ayarlayın. İzin listelerini, korumalı dalları veya eşikleri proje başına veya küresel olarak ayarlayın.
+
+ Herhangi bir politikayı kod olmadan ayarlayın. İzin listeleri, korumalı dallar veya eşikleri proje başına veya genel olarak ayarlayın.
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # politikaları etkinleştir
-failproofai # gösterge panelini başlat
+failproofai policies --install # politikaları etkinleştirin (veya atlayın — `failproofai` ilk çalışmada bunları ayarlamayı teklif edecektir)
+failproofai # panoyu başlatın
```
-Tam anlattım için [Başlayın](/tr/getting-started) kılavuzuna bakın.
\ No newline at end of file
+Tam anlatım için [Başlarken](/tr/getting-started) kılavuzuna bakın.
\ No newline at end of file
diff --git a/docs/vi/cli/environment-variables.mdx b/docs/vi/cli/environment-variables.mdx
index 9093772..89dbeb0 100644
--- a/docs/vi/cli/environment-variables.mdx
+++ b/docs/vi/cli/environment-variables.mdx
@@ -10,25 +10,31 @@ description: "Cấu hình hành vi failproofai bằng biến môi trường"
| `PORT` | Cổng Dashboard (mặc định: `8020`) |
| `CLAUDE_PROJECTS_PATH` | Ghi đè vị trí tìm kiếm thư mục dự án Claude Code |
| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | Các trang dashboard được ẩn, cách nhau bằng dấu phẩy |
-| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Các host/IP được phép truy cập tài nguyên dev. Tương tự như `--allowed-origins`. |
+| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | Các máy chủ/IP được phép truy cập tài nguyên dev. Giống như `--allowed-origins`. |
-## Ghi nhật ký
+## Logging
| Biến | Mô tả |
|----------|-------------|
-| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Mức độ ghi nhật ký máy chủ (mặc định: `warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | Đường dẫn tệp nhật ký hook tùy chỉnh, hoặc `true` cho đường dẫn mặc định (`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | Mức độ log của máy chủ (mặc định: `warn`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | Đường dẫn tệp log hook tùy chỉnh, hoặc `true` để sử dụng đường dẫn mặc định (`~/.failproofai/logs/hooks.log`) |
## Telemetry
| Biến | Mô tả |
|----------|-------------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Tắt telemetry sử dụng ẩn danh |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | Vô hiệu hóa telemetry sử dụng ẩn danh |
+
+## Lời nhắc chạy lần đầu
+
+| Biến | Mô tả |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | Bỏ qua lời nhắc đề nghị cài đặt các chính sách khi chạy `failproofai` trống lần đầu tiên |
## LLM (để đánh giá chính sách)
| Biến | Mô tả |
|----------|-------------|
| `FAILPROOFAI_LLM_BASE_URL` | Điểm cuối API LLM (mặc định: `https://api.openai.com/v1`) |
-| `FAILPROOFAI_LLM_API_KEY` | Khóa API cho các chính sách do LLM cung cấp |
+| `FAILPROOFAI_LLM_API_KEY` | Khóa API cho các chính sách được hỗ trợ bởi LLM |
| `FAILPROOFAI_LLM_MODEL` | Tên mô hình (mặc định: `gpt-4o-mini`) |
\ No newline at end of file
diff --git a/docs/vi/introduction.mdx b/docs/vi/introduction.mdx
index b4598db..1d9986e 100644
--- a/docs/vi/introduction.mdx
+++ b/docs/vi/introduction.mdx
@@ -1,15 +1,15 @@
---
title: "Failproof AI"
-description: "FailproofAI cung cấp 39 chính sách xử lý lỗi tích hợp sẵn giúp bắt các vòng lặp, rò rỉ bí mật, lệnh gọi công cụ phá hủy và nhiều hơn nữa chỉ với một lần cài đặt."
+description: "FailproofAI cung cấp 39 chính sách xử lý lỗi tích hợp sẵn giúp bắt các vòng lặp, rò rỉ bí mật, các lệnh gọi công cụ phá hủy và nhiều hơn nữa trong một lần cài đặt."
---
[](https://www.npmjs.com/package/failproofai)
-Hooks và chính sách cho **xử lý lỗi AI**, **phục hồi lỗi**, và **độ tin cậy LLM**. Giữ cho các AI agents của bạn đáng tin cậy và chạy tự động trên **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI**, và **Agents SDK**.
+Các hook và chính sách cho **xử lý lỗi AI**, **phục hồi lỗi**, và **độ tin cậy LLM**. Giữ cho các agent AI của bạn hoạt động đáng tin cậy và tự động trên **Claude Code**, **OpenAI Codex**, **GitHub Copilot**, **Cursor Agent**, **OpenCode**, **Pi**, **Gemini CLI**, và **Agents SDK**.
-Các AI agents thất bại theo những cách có thể dự đoán được. Chúng chạy các lệnh phá hủy, rò rỉ bí mật, lạc hướng công việc, mắc kẹt trong các vòng lặp, hoặc đẩy trực tiếp lên main. Nếu để không kiểm soát, những lỗi nhỏ có thể tích tụ thành sự cố, rò rỉ thông tin xác thực, và mất công việc.
+Các agent AI gặp sự cố theo những cách có thể dự đoán được. Chúng chạy các lệnh phá hủy, rò rỉ bí mật, lạc khỏi nhiệm vụ, bị kẹt trong các vòng lặp, hoặc đẩy trực tiếp vào nhánh main. Nếu không được kiểm soát, những sự cố nhỏ có thể dẫn đến ngừng hoạt động, rò rỉ thông tin đăng nhập, và mất dữ liệu.
-FailproofAI giải quyết vấn đề này bằng **chính sách**. Các quy tắc này kết nối vào mọi lệnh gọi công cụ của agent để **phát hiện lỗi**, **giảm thiểu chúng** (chặn, hướng dẫn, vệ sinh), và **cảnh báo bạn** khi có điều gì cần chú ý. Bảng điều khiển cục bộ cho phép bạn xem lại mọi lệnh gọi công cụ, lỗi agent, và hành động phục hồi sau này.
+FailproofAI giải quyết vấn đề này bằng **chính sách**. Những quy tắc này can thiệp vào mọi lệnh gọi công cụ của agent để **phát hiện lỗi**, **giảm thiểu chúng** (chặn, hướng dẫn, vệ sinh), và **cảnh báo bạn** khi có điều cần chú ý. Một bảng điều khiển cục bộ cho phép bạn xem xét mọi lệnh gọi công cụ, lỗi agent và hành động phục hồi sau đó.
Không có dữ liệu nào rời khỏi máy của bạn.
@@ -18,24 +18,24 @@ Không có dữ liệu nào rời khỏi máy của bạn.
- Chặn các lệnh phá hủy, ngăn chặn rò rỉ bí mật, giữ cho các agents nằm trong ranh giới dự án, và nhiều hơn nữa. Tất cả đều sẵn có ngay từ đầu.
+ Chặn các lệnh phá hủy, ngăn chặn rò rỉ bí mật, giữ các agent trong ranh giới dự án và nhiều hơn nữa. Tất cả đã sẵn sàng.
- Viết các quy tắc của riêng bạn bằng JavaScript với API allow / deny / instruct đơn giản.
+ Viết các quy tắc riêng của bạn bằng JavaScript với API allow / deny / instruct đơn giản.
-
- Xem những gì các agents của bạn đã làm khi bạn vắng mặt. Duyệt các phiên, kiểm tra các lệnh gọi công cụ, xem xét nơi các chính sách được kích hoạt.
+
+ Xem các agent của bạn đã làm gì khi bạn vắng mặt. Duyệt qua các phiên làm việc, kiểm tra các lệnh gọi công cụ, xem xét các chỗ chính sách được kích hoạt.
- Điều chỉnh bất kỳ chính sách nào mà không cần viết mã. Đặt danh sách cho phép, nhánh được bảo vệ, hoặc ngưỡng cho mỗi dự án hoặc toàn cầu.
+ Điều chỉnh bất kỳ chính sách nào mà không cần mã. Đặt danh sách cho phép, các nhánh được bảo vệ, hoặc ngưỡng theo dự án hoặc toàn cầu.
-## Khởi động nhanh
+## Bắt đầu nhanh
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # enable policies
-failproofai # launch the dashboard
+failproofai policies --install # bật các chính sách (hoặc bỏ qua — `failproofai` sẽ đề xuất thiết lập chúng trong lần chạy đầu tiên)
+failproofai # khởi chạy bảng điều khiển
```
Xem hướng dẫn [Bắt đầu](/vi/getting-started) để có hướng dẫn đầy đủ.
\ No newline at end of file
diff --git a/docs/zh/cli/environment-variables.mdx b/docs/zh/cli/environment-variables.mdx
index f9aebdb..c1f808b 100644
--- a/docs/zh/cli/environment-variables.mdx
+++ b/docs/zh/cli/environment-variables.mdx
@@ -3,13 +3,13 @@ title: 环境变量
description: "通过环境变量配置 failproofai 的行为"
---
-## 控制台
+## 仪表板
| 变量 | 描述 |
|----------|-------------|
-| `PORT` | 控制台端口(默认:`8020`) |
+| `PORT` | 仪表板端口(默认:`8020`) |
| `CLAUDE_PROJECTS_PATH` | 覆盖 Claude Code 项目文件夹的查找路径 |
-| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | 以逗号分隔的控制台页面列表,用于隐藏指定页面 |
+| `FAILPROOFAI_DISABLE_PAGES=policies,projects` | 以逗号分隔的仪表板页面列表,用于隐藏指定页面 |
| `FAILPROOFAI_ALLOWED_DEV_ORIGINS` | 允许访问开发资源的主机/IP,与 `--allowed-origins` 相同 |
## 日志
@@ -17,13 +17,19 @@ description: "通过环境变量配置 failproofai 的行为"
| 变量 | 描述 |
|----------|-------------|
| `FAILPROOFAI_LOG_LEVEL=info\|warn\|error` | 服务器日志级别(默认:`warn`) |
-| `FAILPROOFAI_HOOK_LOG_FILE` | 自定义钩子日志文件路径,或设为 `true` 使用默认路径(`~/.failproofai/logs/hooks.log`) |
+| `FAILPROOFAI_HOOK_LOG_FILE` | 自定义 hook 日志文件路径,或设为 `true` 使用默认路径(`~/.failproofai/logs/hooks.log`) |
## 遥测
| 变量 | 描述 |
|----------|-------------|
-| `FAILPROOFAI_TELEMETRY_DISABLED=1` | 禁用匿名使用遥测数据收集 |
+| `FAILPROOFAI_TELEMETRY_DISABLED=1` | 禁用匿名使用遥测 |
+
+## 首次运行提示
+
+| 变量 | 描述 |
+|----------|-------------|
+| `FAILPROOFAI_NO_FIRST_RUN=1` | 跳过首次裸运行 `failproofai` 时弹出的策略安装提示 |
## LLM(用于策略评估)
diff --git a/docs/zh/introduction.mdx b/docs/zh/introduction.mdx
index cdba6d3..86413b5 100644
--- a/docs/zh/introduction.mdx
+++ b/docs/zh/introduction.mdx
@@ -1,41 +1,41 @@
---
title: "Failproof AI"
-description: "FailproofAI 为 AI 智能体提供 39 条内置失效策略,一键安装即可捕获循环、密钥泄露、破坏性工具调用等问题。"
+description: "FailproofAI 为 AI 智能体提供 39 条内置故障策略,一次安装即可捕获循环、密钥泄露、破坏性工具调用等问题。"
---
[](https://www.npmjs.com/package/failproofai)
-用于 **AI 故障处理**、**错误恢复**和 **LLM 可靠性**的 Hooks 与策略。让你的 AI 智能体在 **Claude Code**、**OpenAI Codex**、**GitHub Copilot**、**Cursor Agent**、**OpenCode**、**Pi**、**Gemini CLI** 以及 **Agents SDK** 上保持稳定、自主运行。
+用于 **AI 故障处理**、**错误恢复**和 **LLM 可靠性**的 Hooks 与策略。让你的 AI 智能体在 **Claude Code**、**OpenAI Codex**、**GitHub Copilot**、**Cursor Agent**、**OpenCode**、**Pi**、**Gemini CLI** 以及 **Agents SDK** 上稳定运行、自主执行任务。
-AI 智能体的失效方式是可预见的:执行破坏性命令、泄露密钥、偏离任务目标、陷入死循环,或直接推送到主分支。若无人看管,小故障会不断累积,最终酿成服务中断、凭证泄露和工作丢失。
+AI 智能体的失败方式往往是可预测的:执行破坏性命令、泄露密钥、偏离任务、陷入循环,或直接推送到主分支。如果不加干预,小故障会逐步演变成服务中断、凭证泄露和数据丢失。
-FailproofAI 通过**策略**来解决这一问题。这些规则介入每一次智能体工具调用,**检测故障**、**进行缓解**(拦截、指令纠正、脱敏处理),并在需要人工介入时**及时告警**。本地仪表盘让你事后可以回顾每一次工具调用、智能体故障及恢复操作。
+FailproofAI 通过**策略**来解决这些问题。这些规则会介入智能体的每一次工具调用,**检测故障**、**进行缓解**(阻止、指令纠正、信息脱敏),并在需要关注时**及时提醒你**。本地仪表板让你可以事后回溯每一次工具调用、智能体故障和恢复操作。
所有数据均不离开你的本机。
-## 快速入门
+## 快速上手
- 拦截破坏性命令、防止密钥泄露、将智能体限制在项目范围内,以及更多功能。开箱即用。
+ 开箱即用:阻止破坏性命令、防止密钥泄露、将智能体限制在项目边界内,以及更多功能。
- 使用 JavaScript 编写你自己的规则,提供简洁的 allow / deny / instruct API。
+ 使用 JavaScript 编写自己的规则,支持简洁的 allow / deny / instruct API。
- 查看智能体在你离开期间的所有操作。浏览会话记录、检查工具调用、回顾策略触发情况。
+ 查看智能体在你离开期间做了什么。浏览会话记录、检查工具调用、回顾策略触发情况。
- 无需编写代码即可调整任意策略。按项目或全局设置白名单、受保护分支或阈值。
+ 无需编写代码即可调整任何策略。按项目或全局设置白名单、受保护分支或触发阈值。
-## 快速开始
+## 快速安装
@@ -50,8 +50,8 @@ bun add -g failproofai
```bash
-failproofai policies --install # enable policies
+failproofai policies --install # enable policies (or skip — `failproofai` will offer to set them up on first run)
failproofai # launch the dashboard
```
-完整操作流程请参阅[入门指南](/zh/getting-started)。
\ No newline at end of file
+完整操作流程请参阅[快速入门](/zh/getting-started)指南。
\ No newline at end of file
From 87c531932691748abb92982f89850ef706f587c4 Mon Sep 17 00:00:00 2001
From: NiveditJain
Date: Thu, 21 May 2026 20:26:06 -0700
Subject: [PATCH 6/9] feat(audit): GTM report redesign + PostHog telemetry
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replaces the developer-facing audit table with a GTM-oriented report:
• Headline call-out box: "Your agent did N wasteful or risky things in
your last X days. M of those would've been caught if more policies
were on."
• Two sections instead of a flat list: "✓ ALREADY PROTECTED" (builtins
the user has enabled that fired) vs "○ SLIPPING THROUGH" (unenabled
builtins + audit-only detectors). Each row is now a human-readable
title ("Tried to read files outside your project") with a one-line
impact ("Stops the agent from peeking at neighboring repos…"), a
relative timestamp ("Last seen 2h ago · 6 projects"), and a per-row
install CTA. The bottom NEXT block lists a single copy-pasteable
`failproofai policies --install ...` command covering every unenabled
builtin, plus the shareable report path and a star link.
• Honors NO_COLOR / FORCE_COLOR=0.
Markdown report rewritten with a TL;DR block aimed at someone who's never
heard of failproofai (so the report is shareable in Slack/PRs), separate
"Already protected" and "Slipping through" tables, an "install everything
in one command" code block, and an Examples appendix grouped by issue.
Authoring:
• New optional `displayTitle` + `impact` fields on `BuiltinPolicyDefinition`
(src/hooks/policy-types.ts) and `Detector` (src/audit/types.ts).
• Copy authored for all 39 builtins + all 8 detectors in-place on each
definition (single source of truth — the dashboard can use the same
copy later).
• AuditCount carries `displayTitle`, `impact`, `enabledInConfig`, and
a pre-built `installHint` so renderers stay pure.
Orchestrator:
• readMergedHooksConfig() called once per audit run to drive the
enabled/unenabled split.
Telemetry (PostHog, src/audit/telemetry.ts):
• Four new events plug into the existing trackHookEvent surface:
audit_started, audit_pattern_detected (per AuditCount),
audit_install_cta_shown (when there are unenabled builtins),
audit_completed (with enabled vs unenabled vs detector hit splits —
the GTM funnel signal).
• Strict privacy: only slugs, counts, booleans, bucketed ages, and CLI
tags ever leave the box. Never transcript paths, cwds, project names,
session IDs, or example commands.
• Honors FAILPROOFAI_TELEMETRY_DISABLED=1 via the inherited contract.
Co-Authored-By: Claude Opus 4.7
---
src/audit/detectors/find-from-root.ts | 2 +
src/audit/detectors/git-commit-no-verify.ts | 2 +
.../detectors/prefer-edit-over-read-cat.ts | 2 +
.../detectors/prefer-edit-over-sed-awk.ts | 2 +
.../detectors/prefer-write-over-heredoc.ts | 2 +
src/audit/detectors/redundant-cd-cwd.ts | 2 +
src/audit/detectors/reread-after-edit.ts | 2 +
src/audit/detectors/sleep-polling-loop.ts | 2 +
src/audit/index.ts | 94 ++++-
src/audit/report.ts | 369 +++++++++++++-----
src/audit/telemetry.ts | 113 ++++++
src/audit/types.ts | 20 +
src/hooks/builtin-policies.ts | 78 ++++
src/hooks/policy-types.ts | 9 +
14 files changed, 581 insertions(+), 118 deletions(-)
create mode 100644 src/audit/telemetry.ts
diff --git a/src/audit/detectors/find-from-root.ts b/src/audit/detectors/find-from-root.ts
index 47ba4a3..aae066f 100644
--- a/src/audit/detectors/find-from-root.ts
+++ b/src/audit/detectors/find-from-root.ts
@@ -9,6 +9,8 @@ export const findFromRoot: Detector = {
description: "Bash `find` against `/`, `/home`, `/usr`, etc. — scope to cwd instead.",
category: "Risky",
severity: "warn",
+ displayTitle: "Ran find from /, /home, /usr, etc.",
+ impact: "Filesystem-wide finds exhaust resources and rarely return useful results.",
detect(event) {
if (event.toolName !== "Bash") return null;
const command = (event.toolInput as { command?: unknown }).command;
diff --git a/src/audit/detectors/git-commit-no-verify.ts b/src/audit/detectors/git-commit-no-verify.ts
index 8270e62..86f4898 100644
--- a/src/audit/detectors/git-commit-no-verify.ts
+++ b/src/audit/detectors/git-commit-no-verify.ts
@@ -6,6 +6,8 @@ export const gitCommitNoVerify: Detector = {
description: "git commit invoked with --no-verify / -n, skipping hooks.",
category: "Risky",
severity: "warn",
+ displayTitle: "Committed with --no-verify",
+ impact: "Skips pre-commit hooks that exist to catch broken or unsafe code.",
detect(event) {
if (event.toolName !== "Bash") return null;
const command = (event.toolInput as { command?: unknown }).command;
diff --git a/src/audit/detectors/prefer-edit-over-read-cat.ts b/src/audit/detectors/prefer-edit-over-read-cat.ts
index c1d2fb3..0f9586c 100644
--- a/src/audit/detectors/prefer-edit-over-read-cat.ts
+++ b/src/audit/detectors/prefer-edit-over-read-cat.ts
@@ -10,6 +10,8 @@ export const preferEditOverReadCat: Detector = {
description: "Bash `cat`/`head`/`tail`/`less`/`more` on a single source file — use Read.",
category: "Wasteful",
severity: "info",
+ displayTitle: "Used `cat`/`head`/`tail` on a source file",
+ impact: "Burns tokens; the Read tool returns content directly without going through Bash output.",
detect(event) {
if (event.toolName !== "Bash") return null;
const command = (event.toolInput as { command?: unknown }).command;
diff --git a/src/audit/detectors/prefer-edit-over-sed-awk.ts b/src/audit/detectors/prefer-edit-over-sed-awk.ts
index 1895175..7000701 100644
--- a/src/audit/detectors/prefer-edit-over-sed-awk.ts
+++ b/src/audit/detectors/prefer-edit-over-sed-awk.ts
@@ -7,6 +7,8 @@ export const preferEditOverSedAwk: Detector = {
description: "Bash `sed -i`/`awk` in-place edits — use Edit.",
category: "Wasteful",
severity: "info",
+ displayTitle: "Used sed -i or awk for an in-place edit",
+ impact: "Edit tool is safer and produces a diff the agent can verify.",
detect(event) {
if (event.toolName !== "Bash") return null;
const command = (event.toolInput as { command?: unknown }).command;
diff --git a/src/audit/detectors/prefer-write-over-heredoc.ts b/src/audit/detectors/prefer-write-over-heredoc.ts
index 013ccfd..322813f 100644
--- a/src/audit/detectors/prefer-write-over-heredoc.ts
+++ b/src/audit/detectors/prefer-write-over-heredoc.ts
@@ -7,6 +7,8 @@ export const preferWriteOverHeredoc: Detector = {
description: "Bash heredoc / `echo > file` writing multi-line content — use Write.",
category: "Wasteful",
severity: "info",
+ displayTitle: "Used heredoc / `echo > file` to write a multi-line file",
+ impact: "Write tool handles escaping and is verifiable.",
detect(event) {
if (event.toolName !== "Bash") return null;
const command = (event.toolInput as { command?: unknown }).command;
diff --git a/src/audit/detectors/redundant-cd-cwd.ts b/src/audit/detectors/redundant-cd-cwd.ts
index 68dbc03..1a1b605 100644
--- a/src/audit/detectors/redundant-cd-cwd.ts
+++ b/src/audit/detectors/redundant-cd-cwd.ts
@@ -10,6 +10,8 @@ export const redundantCdCwd: Detector = {
"Bash commands prefixed with `cd && …` even though commands already run in cwd.",
category: "Wasteful",
severity: "info",
+ displayTitle: "Prepended cd before commands",
+ impact: "Pure waste — your agent's shell already runs in `cwd`.",
detect(event) {
if (event.toolName !== "Bash") return null;
const command = (event.toolInput as { command?: unknown }).command;
diff --git a/src/audit/detectors/reread-after-edit.ts b/src/audit/detectors/reread-after-edit.ts
index abea184..caff10f 100644
--- a/src/audit/detectors/reread-after-edit.ts
+++ b/src/audit/detectors/reread-after-edit.ts
@@ -26,6 +26,8 @@ export const rereadAfterEdit: Detector = {
description: "Read of a file that was just Edit'd or Write'n in the same session.",
category: "Wasteful",
severity: "info",
+ displayTitle: "Re-read a file it just edited",
+ impact: "Edit/Write already returned the updated content; the second Read is wasted tokens.",
detect(event, sessionState) {
const state = getState(sessionState);
const filePath = (event.toolInput as { file_path?: unknown }).file_path;
diff --git a/src/audit/detectors/sleep-polling-loop.ts b/src/audit/detectors/sleep-polling-loop.ts
index 06579fb..c701f0f 100644
--- a/src/audit/detectors/sleep-polling-loop.ts
+++ b/src/audit/detectors/sleep-polling-loop.ts
@@ -8,6 +8,8 @@ export const sleepPollingLoop: Detector = {
description: "Bash long `sleep` or while-sleep polling loops.",
category: "Wasteful",
severity: "info",
+ displayTitle: "Used a long sleep or while-sleep polling loop",
+ impact: "Burns wall-clock; better to wait for an explicit signal.",
detect(event) {
if (event.toolName !== "Bash") return null;
const command = (event.toolInput as { command?: unknown }).command;
diff --git a/src/audit/index.ts b/src/audit/index.ts
index e689fc4..70c9d88 100644
--- a/src/audit/index.ts
+++ b/src/audit/index.ts
@@ -9,11 +9,19 @@
*/
import { batchAll } from "../../lib/concurrency";
import { BUILTIN_POLICIES } from "../hooks/builtin-policies";
+import { readMergedHooksConfig } from "../hooks/hooks-config";
+import { normalizePolicyName } from "../hooks/policy-registry";
import { INTEGRATION_TYPES, type IntegrationType } from "../hooks/types";
import { ADAPTERS } from "./cli-adapters";
import { AUDIT_DETECTORS } from "./detectors";
import { readCachedTranscriptResult, writeCachedTranscriptResult } from "./cache";
import { initReplay, replayEvent } from "./replay";
+import {
+ trackAuditCompleted,
+ trackAuditInstallCtaShown,
+ trackAuditPatternDetected,
+ trackAuditStarted,
+} from "./telemetry";
import {
AUDIT_EXAMPLE_MAX_CHARS,
AUDIT_MAX_EXAMPLES_PER_NAME,
@@ -35,14 +43,34 @@ function shortPolicyName(name: string): string {
return slash >= 0 ? name.slice(slash + 1) : name;
}
-/** Look up a builtin policy's category by canonical name; null when the name
+/** Look up a builtin policy definition by canonical name; null when the name
* doesn't match a builtin (e.g. user custom policy). */
-function findBuiltinCategory(name: string): string {
+function findBuiltin(name: string) {
const short = shortPolicyName(name);
for (const p of BUILTIN_POLICIES) {
- if (p.name === name || shortPolicyName(p.name) === short) return p.category;
+ if (p.name === name || shortPolicyName(p.name) === short) return p;
+ }
+ return null;
+}
+
+/** Build the per-row install hint shown in the report:
+ * - Already enabled builtin: a check phrase ("Already enforced — currently blocking these in real time")
+ * - Unenabled builtin: `failproofai policies --install `
+ * - Audit-only detector: soft notice ("Audit-only — `failproofai audit` will keep tracking these")
+ * - Unknown / custom: empty string
+ */
+function buildInstallHint(
+ name: string,
+ source: "builtin" | "audit-detector",
+ enabled: boolean,
+): string {
+ if (source === "audit-detector") {
+ return "Audit-only — `failproofai audit` will keep tracking these.";
+ }
+ if (enabled) {
+ return "Already enforced — failproofai is blocking these in real time.";
}
- return "Custom";
+ return `Enable in one command: failproofai policies --install ${shortPolicyName(name)}`;
}
function truncateExample(s: string): string {
@@ -152,7 +180,10 @@ function recordHit(
}
}
-function aggregateResults(perTranscript: TranscriptAuditResult[]): AuditCount[] {
+function aggregateResults(
+ perTranscript: TranscriptAuditResult[],
+ enabledBuiltins: Set,
+): AuditCount[] {
// For each name: sum hits, count distinct projects, merge ranges + examples.
const byName = new Map d.name));
+ const detectorByName = new Map(AUDIT_DETECTORS.map((d) => [d.name, d]));
const out: AuditCount[] = [];
for (const [name, bucket] of byName) {
- const isDetector = detectorNames.has(name);
- const detector = AUDIT_DETECTORS.find((d) => d.name === name);
+ const detector = detectorByName.get(name);
+ const isDetector = !!detector;
+ const builtin = isDetector ? null : findBuiltin(name);
+ const source: "builtin" | "audit-detector" = isDetector ? "audit-detector" : "builtin";
+ const enabled = isDetector ? false : enabledBuiltins.has(normalizePolicyName(name));
+
+ const displayTitle =
+ detector?.displayTitle
+ ?? builtin?.displayTitle
+ ?? detector?.description
+ ?? builtin?.description
+ ?? shortPolicyName(name);
+ const impact = detector?.impact ?? builtin?.impact ?? "";
+
out.push({
name,
- source: isDetector ? "audit-detector" : "builtin",
- category: isDetector
- ? (detector?.category ?? "Wasteful")
- : findBuiltinCategory(name),
+ source,
+ category: detector?.category ?? builtin?.category ?? "Custom",
severity: isDetector ? (detector?.severity ?? "info") : "deny",
hits: bucket.hits,
projects: bucket.projects.size,
firstSeen: bucket.first,
lastSeen: bucket.last,
examples: bucket.examples,
+ displayTitle,
+ impact,
+ enabledInConfig: enabled,
+ installHint: buildInstallHint(name, source, enabled),
});
}
@@ -214,9 +259,19 @@ export async function runAudit(opts: RunAuditOptions = {}): Promise
const startedAt = Date.now();
initReplay();
+ const outputMode = opts.json ? "json" : opts.noReport ? "text" : "text+markdown";
+ trackAuditStarted(opts, outputMode);
+
const clis = (opts.clis ?? Array.from(INTEGRATION_TYPES)) as IntegrationType[];
const sinceMs = parseSinceOpt(opts.since);
+ // Snapshot which builtin policies the user currently has enabled — drives
+ // the "already protected" vs "slipping through" split in the report.
+ const userConfig = readMergedHooksConfig();
+ const enabledBuiltins = new Set(
+ (userConfig.enabledPolicies ?? []).map((n) => normalizePolicyName(n)),
+ );
+
// 1. Discover transcripts across all selected CLIs.
const allTranscripts: TranscriptMetadata[] = [];
for (const cli of clis) {
@@ -268,7 +323,7 @@ export async function runAudit(opts: RunAuditOptions = {}): Promise
}
// 3. Aggregate.
- let results = aggregateResults(perTranscript);
+ let results = aggregateResults(perTranscript, enabledBuiltins);
if (opts.policies?.length) {
const wanted = new Set(opts.policies.map(shortPolicyName));
results = results.filter((r) => wanted.has(shortPolicyName(r.name)));
@@ -280,7 +335,7 @@ export async function runAudit(opts: RunAuditOptions = {}): Promise
if (Object.keys(t.hitsByName).length > 0) projectsWithHits.add(t.projectName);
}
- return {
+ const auditResult: AuditResult = {
version: 1,
scannedAt: new Date(startedAt).toISOString(),
scope: {
@@ -300,4 +355,15 @@ export async function runAudit(opts: RunAuditOptions = {}): Promise
projectsWithHits: projectsWithHits.size,
},
};
+
+ // Telemetry — fire-and-forget, never blocks the CLI. See src/audit/telemetry.ts
+ // for the privacy contract (slugs + counts + booleans only).
+ for (const count of results) trackAuditPatternDetected(count);
+ const unenabledBuiltinNames = results
+ .filter((r) => r.source === "builtin" && !r.enabledInConfig)
+ .map((r) => r.name);
+ trackAuditInstallCtaShown(unenabledBuiltinNames);
+ trackAuditCompleted(auditResult, outputMode);
+
+ return auditResult;
}
diff --git a/src/audit/report.ts b/src/audit/report.ts
index d00c0ed..a5c53d7 100644
--- a/src/audit/report.ts
+++ b/src/audit/report.ts
@@ -1,11 +1,13 @@
/**
* Output renderers for `failproofai audit`:
- * • formatText — ANSI table to stdout
- * • formatMarkdown — sectioned report written to a file
+ * • formatText — ANSI table to stdout (GTM-oriented "moment of truth")
+ * • formatMarkdown — shareable sectioned report written to a file
* • formatJson — machine-readable
*
- * Examples are already truncated to AUDIT_EXAMPLE_MAX_CHARS upstream — we
- * trust that here and only do display formatting.
+ * The text renderer is the user's first impression of the audit. It leads
+ * with a headline-box, splits findings into "already protected" vs "slipping
+ * through" so the conversion ask is obvious, and ends with a copy-pasteable
+ * install command + report path + star link.
*/
import type { AuditCount, AuditResult, RunAuditOptions } from "./types";
@@ -20,141 +22,313 @@ const ANSI = {
magenta: "\x1B[35m",
};
-function colorFor(row: AuditCount): string {
- if (row.severity === "deny" || row.severity === "warn") return ANSI.red;
- if (row.severity === "instruct" || row.severity === "info") return ANSI.yellow;
- return ANSI.dim;
+/** Honor https://no-color.org / NO_COLOR=1 and FORCE_COLOR=0 by stripping all
+ * ANSI sequences from the final output. Detected once per renderer call. */
+function noColorEnabled(): boolean {
+ if (process.env.NO_COLOR && process.env.NO_COLOR !== "") return true;
+ if (process.env.FORCE_COLOR === "0") return true;
+ return false;
}
-function padRight(s: string, n: number): string {
- if (s.length >= n) return s;
- return s + " ".repeat(n - s.length);
+function stripAnsi(s: string): string {
+ // eslint-disable-next-line no-control-regex
+ return s.replace(/\x1B\[[0-9;]*m/g, "");
}
-function padLeft(s: string, n: number): string {
- if (s.length >= n) return s;
- return " ".repeat(n - s.length) + s;
+/** Human-readable "time ago" — "30m ago", "2h ago", "3d ago", "2w ago".
+ * Returns "just now" for <1 minute. */
+function formatTimeAgo(iso: string | undefined): string {
+ if (!iso) return "—";
+ const ms = Date.now() - new Date(iso).getTime();
+ if (Number.isNaN(ms) || ms < 0) return "—";
+ const m = Math.floor(ms / 60_000);
+ if (m < 1) return "just now";
+ if (m < 60) return `${m}m ago`;
+ const h = Math.floor(m / 60);
+ if (h < 24) return `${h}h ago`;
+ const d = Math.floor(h / 24);
+ if (d < 14) return `${d}d ago`;
+ const w = Math.floor(d / 7);
+ if (w < 8) return `${w}w ago`;
+ return `${Math.floor(d / 30)}mo ago`;
+}
+
+/** Width of the headline box and section dividers. Adapts to narrow terminals
+ * down to a 60-char floor. */
+function getWidth(): number {
+ const cols = process.stdout.columns ?? 80;
+ return Math.max(60, Math.min(78, cols));
+}
+
+/** Box-drawing helpers using unicode round-corner glyphs. */
+function topBorder(w: number): string { return `╭${"─".repeat(w - 2)}╮`; }
+function bottomBorder(w: number): string { return `╰${"─".repeat(w - 2)}╯`; }
+function boxLine(text: string, w: number): string {
+ const inner = w - 4; // 2 chars of border + 1 char padding each side
+ const visible = stripAnsi(text);
+ const pad = Math.max(0, inner - visible.length);
+ return `│ ${text}${" ".repeat(pad)} │`;
+}
+function divider(w: number): string { return "─".repeat(w); }
+
+/** Short, qualified policy slug — `failproofai/foo` → `foo` for display. */
+function shortName(name: string): string {
+ const slash = name.indexOf("/");
+ return slash >= 0 ? name.slice(slash + 1) : name;
+}
+
+/** Sum hits across an AuditCount[] subset. */
+function sumHits(rows: AuditCount[]): number {
+ return rows.reduce((acc, r) => acc + r.hits, 0);
+}
+
+/** Render one row in the table-of-findings form:
+ * 31× Tried to read files outside your project
+ * Stops the agent from peeking at neighboring repos…
+ * Last seen 2h ago · 6 projects
+ * › Already enforced — failproofai is blocking these in real time.
+ * Example: grep -n …
+ */
+function renderRow(r: AuditCount, opts: { showExamples?: boolean }): string[] {
+ const out: string[] = [];
+ const sev = r.severity;
+ const titleColor =
+ sev === "deny" ? ANSI.red
+ : sev === "warn" ? ANSI.red
+ : sev === "info" || sev === "instruct" ? ANSI.yellow
+ : ANSI.cyan;
+ const countStr = String(r.hits).padStart(4);
+ out.push(` ${titleColor}${ANSI.bold}${countStr}×${ANSI.reset} ${r.displayTitle}`);
+ if (r.impact) {
+ out.push(` ${ANSI.dim}${r.impact}${ANSI.reset}`);
+ }
+ out.push(
+ ` ${ANSI.dim}Last seen ${formatTimeAgo(r.lastSeen)} · ${r.projects} project${r.projects === 1 ? "" : "s"}${ANSI.reset}`,
+ );
+ if (opts.showExamples && r.examples[0]) {
+ out.push(` ${ANSI.dim}Example: ${r.examples[0].example}${ANSI.reset}`);
+ }
+ if (r.installHint) {
+ const arrowColor = r.enabledInConfig ? ANSI.green : ANSI.cyan;
+ out.push(` ${arrowColor}›${ANSI.reset} ${r.installHint}`);
+ }
+ out.push("");
+ return out;
}
export function formatText(result: AuditResult, opts: RunAuditOptions = {}): string {
+ const w = getWidth();
const limit = opts.limit ?? 20;
- const rows = result.results.slice(0, limit);
const showExamples = !!opts.showExamples;
+ // Split rows by enforcement state.
+ const enabledRows = result.results.filter((r) => r.source === "builtin" && r.enabledInConfig);
+ const unenabledBuiltinRows = result.results.filter((r) => r.source === "builtin" && !r.enabledInConfig);
+ const detectorRows = result.results.filter((r) => r.source === "audit-detector");
+ // "Slipping through" combines unenabled-builtins + audit-detectors, ranked by hits.
+ const slippingRows = [...unenabledBuiltinRows, ...detectorRows].sort((a, b) => b.hits - a.hits);
+
+ const totalProtected = sumHits(enabledRows);
+ const totalSlipping = sumHits(slippingRows);
+ const totalHits = totalProtected + totalSlipping;
+ const sinceLabel = result.scope.since ? `the last ${result.scope.since}` : "all time";
+
const lines: string[] = [];
- const scopeCli = result.scope.cli.length === 7 ? "all CLIs" : result.scope.cli.join(", ");
- const scopeProj = result.scope.projects === "all" ? "all projects" : `${result.scope.projects.length} project(s)`;
- const scopeSince = result.scope.since ?? "all time";
+ // ── Header ──────────────────────────────────────────────────────
+ lines.push(`${ANSI.cyan}🛡 failproofai audit${ANSI.reset} · your ${sinceLabel}`);
lines.push(
- `${ANSI.bold}failproofai audit${ANSI.reset} scope: ${scopeCli} · ${scopeProj} · since: ${scopeSince}`,
- );
- lines.push("");
- lines.push(
- `Scanned ${result.transcripts.scanned} transcripts in ${(result.transcripts.durationMs / 1000).toFixed(1)}s ` +
- `(${result.transcripts.skipped} skipped, ${result.transcripts.errors} errors)`,
+ ` ${ANSI.dim}${result.transcripts.scanned} sessions · ${result.totals.projectsWithHits} project${result.totals.projectsWithHits === 1 ? "" : "s"} with hits · scanned in ${(result.transcripts.durationMs / 1000).toFixed(1)}s${ANSI.reset}`,
);
lines.push("");
- if (rows.length === 0) {
- lines.push(`${ANSI.dim}No hits.${ANSI.reset}`);
+ // ── Headline box ────────────────────────────────────────────────
+ if (totalHits === 0) {
+ lines.push(topBorder(w));
+ lines.push(boxLine(`${ANSI.green}🎉 Clean run!${ANSI.reset} Nothing matched your policies in this window.`, w));
+ lines.push(bottomBorder(w));
lines.push("");
- return lines.join("\n");
+ return noColorEnabled() ? stripAnsi(lines.join("\n")) : lines.join("\n");
+ }
+ lines.push(topBorder(w));
+ lines.push(boxLine(
+ `${ANSI.bold}Your agent did ${totalHits} wasteful or risky things in ${sinceLabel}.${ANSI.reset}`,
+ w,
+ ));
+ if (totalSlipping > 0) {
+ lines.push(boxLine(
+ `${totalSlipping} of those would've been caught if more policies were on.`,
+ w,
+ ));
}
+ lines.push(bottomBorder(w));
+ lines.push("");
- // Column widths
- const longestName = rows.reduce((m, r) => Math.max(m, r.name.length), 16);
- const nameW = Math.min(40, longestName);
- const hitsW = Math.max(4, ...rows.map((r) => String(r.hits).length));
- const projW = Math.max(8, ...rows.map((r) => String(r.projects).length));
- const sourceW = Math.max(6, ...rows.map((r) => r.source.length));
-
- // Header
- const header =
- `${ANSI.bold}${padRight("POLICY / DETECTOR", nameW)} ${padLeft("HITS", hitsW)} ${padLeft("PROJECTS", projW)} ${padRight("SOURCE", sourceW)}` +
- (showExamples ? " EXAMPLE" : "") +
- ANSI.reset;
- lines.push(header);
-
- for (const row of rows) {
- const color = colorFor(row);
- const name = row.name.length > nameW ? row.name.slice(0, nameW - 1) + "…" : row.name;
- let line =
- `${color}${padRight(name, nameW)}${ANSI.reset} ` +
- `${padLeft(String(row.hits), hitsW)} ` +
- `${padLeft(String(row.projects), projW)} ` +
- `${ANSI.dim}${padRight(row.source, sourceW)}${ANSI.reset}`;
- if (showExamples && row.examples[0]) {
- line += ` ${ANSI.dim}${row.examples[0].example}${ANSI.reset}`;
+ // ── Section: ALREADY PROTECTED ──────────────────────────────────
+ if (enabledRows.length > 0) {
+ lines.push(
+ `${ANSI.green}✓ ALREADY PROTECTED${ANSI.reset} ${ANSI.dim}(${totalProtected} action${totalProtected === 1 ? "" : "s"} stopped by your current policies)${ANSI.reset}`,
+ );
+ lines.push("");
+ for (const row of enabledRows.slice(0, limit)) {
+ lines.push(...renderRow(row, { showExamples }));
+ }
+ if (enabledRows.length > limit) {
+ lines.push(` ${ANSI.dim}… ${enabledRows.length - limit} more (use --limit ${enabledRows.length})${ANSI.reset}`);
+ lines.push("");
}
- lines.push(line);
}
- lines.push("");
- lines.push(
- `${ANSI.bold}TOTAL${ANSI.reset} hits: ${result.totals.hits} across ${result.totals.projectsWithHits} project(s).`,
- );
+ // ── Section: SLIPPING THROUGH ───────────────────────────────────
+ if (slippingRows.length > 0) {
+ lines.push(
+ `${ANSI.yellow}○ SLIPPING THROUGH${ANSI.reset} ${ANSI.dim}(${totalSlipping} action${totalSlipping === 1 ? "" : "s"} caught by audit, not blocked in real time)${ANSI.reset}`,
+ );
+ lines.push("");
+ for (const row of slippingRows.slice(0, limit)) {
+ lines.push(...renderRow(row, { showExamples }));
+ }
+ if (slippingRows.length > limit) {
+ lines.push(` ${ANSI.dim}… ${slippingRows.length - limit} more (use --limit ${slippingRows.length})${ANSI.reset}`);
+ lines.push("");
+ }
+ }
- if (result.results.length > limit) {
- lines.push(`${ANSI.dim}… ${result.results.length - limit} more rows omitted (use --limit ${result.results.length})${ANSI.reset}`);
+ // ── Footer / NEXT step ──────────────────────────────────────────
+ lines.push(divider(w));
+ if (unenabledBuiltinRows.length > 0) {
+ const installNames = unenabledBuiltinRows.map((r) => shortName(r.name));
+ lines.push(
+ `${ANSI.bold}NEXT${ANSI.reset} Enable the ${installNames.length} unenabled real-time polic${installNames.length === 1 ? "y" : "ies"} in one command:`,
+ );
+ lines.push("");
+ lines.push(` ${ANSI.cyan}failproofai policies --install ${installNames.join(" ")}${ANSI.reset}`);
+ lines.push("");
+ } else if (slippingRows.length > 0) {
+ lines.push(
+ `${ANSI.bold}NEXT${ANSI.reset} Everything blockable is already enabled. Audit-only findings will show up in your next ${ANSI.cyan}failproofai audit${ANSI.reset}.`,
+ );
+ lines.push("");
+ } else {
+ lines.push(`${ANSI.bold}NEXT${ANSI.reset} ${ANSI.green}You have the relevant policies enabled. failproofai is blocking these in real time.${ANSI.reset}`);
+ lines.push("");
}
+
+ lines.push(` 📄 Shareable report: ${ANSI.cyan}./failproofai-audit.md${ANSI.reset}`);
+ lines.push(` ⭐ Star us: ${ANSI.cyan}https://github.com/FailproofAI/failproofai${ANSI.reset}`);
lines.push("");
- return lines.join("\n");
+
+ return noColorEnabled() ? stripAnsi(lines.join("\n")) : lines.join("\n");
}
export function formatJson(result: AuditResult): string {
return JSON.stringify(result, null, 2);
}
+/** Escape characters that would break a markdown table row. Pipes split
+ * columns; backslashes escape the next char; leading newlines end the row. */
+function escapeTableCell(s: string): string {
+ return s.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/[\r\n]+/g, " ");
+}
+
+function escapeBackticks(s: string): string {
+ return s.replace(/`/g, "\\`");
+}
+
export function formatMarkdown(result: AuditResult): string {
const out: string[] = [];
- out.push(`# failproofai audit report`);
+
+ const enabledRows = result.results.filter((r) => r.source === "builtin" && r.enabledInConfig);
+ const unenabledBuiltinRows = result.results.filter((r) => r.source === "builtin" && !r.enabledInConfig);
+ const detectorRows = result.results.filter((r) => r.source === "audit-detector");
+ const slippingRows = [...unenabledBuiltinRows, ...detectorRows].sort((a, b) => b.hits - a.hits);
+
+ const totalProtected = sumHits(enabledRows);
+ const totalSlipping = sumHits(slippingRows);
+ const totalHits = totalProtected + totalSlipping;
+ const sinceLabel = result.scope.since ? `last ${result.scope.since}` : "all time";
+
+ out.push(`# Agent behavior audit · ${sinceLabel}`);
out.push("");
- out.push(`*Generated ${result.scannedAt}*`);
+ out.push(`*Generated ${result.scannedAt} — scanned ${result.transcripts.scanned} sessions across ${result.totals.projectsWithHits} project(s) in ${(result.transcripts.durationMs / 1000).toFixed(1)}s.*`);
out.push("");
- const scopeCli = result.scope.cli.length === 7 ? "all CLIs" : result.scope.cli.join(", ");
- const scopeProj = result.scope.projects === "all" ? "all projects" : `${result.scope.projects.length} project(s)`;
- out.push(`**Scope:** ${scopeCli} · ${scopeProj} · since ${result.scope.since ?? "all time"}`);
+
+ // TL;DR — readable to someone who doesn't know failproofai.
+ out.push("## TL;DR");
out.push("");
- out.push(
- `**Scan:** ${result.transcripts.scanned} transcripts in ${(result.transcripts.durationMs / 1000).toFixed(1)}s ` +
- `(${result.transcripts.skipped} skipped, ${result.transcripts.errors} errors)`,
- );
+ if (totalHits === 0) {
+ out.push("Clean run — the AI coding agent didn't do anything `failproofai` catches in this window.");
+ } else {
+ out.push(
+ `Over ${result.transcripts.scanned} sessions, my AI coding agent did **${totalHits} things \`failproofai\` would have stopped**: ` +
+ `${totalProtected} were already blocked in real time by my current config; ` +
+ `**${totalSlipping} slipped through** (would've been caught if more policies were on).`,
+ );
+ }
out.push("");
- out.push(`**Totals:** ${result.totals.hits} hits across ${result.totals.projectsWithHits} project(s).`);
+ out.push("> [failproofai](https://github.com/FailproofAI/failproofai) is a hook-based policy engine for Claude Code, Codex, Copilot, Cursor, OpenCode, Pi, and Gemini CLI. The `audit` command replays past agent sessions through every builtin policy to surface patterns that were (or could've been) stopped.");
out.push("");
- if (result.results.length === 0) {
- out.push("No hits.");
+ if (totalHits === 0) return out.join("\n");
+
+ // What was blocked
+ if (enabledRows.length > 0) {
+ out.push(`## ✓ Already protected (${totalProtected} action${totalProtected === 1 ? "" : "s"} stopped)`);
+ out.push("");
+ out.push("These are real-time policies you have on — `failproofai` blocked the agent before each action took effect.");
+ out.push("");
+ out.push("| Issue | Hits | Projects | Last seen | Policy |");
+ out.push("|---|---:|---:|---|---|");
+ for (const r of enabledRows) {
+ out.push(
+ `| ${escapeTableCell(r.displayTitle)} | ${r.hits} | ${r.projects} | ${formatTimeAgo(r.lastSeen)} | \`${escapeTableCell(shortName(r.name))}\` |`,
+ );
+ }
out.push("");
- return out.join("\n");
}
- // Group by category.
- const byCategory = new Map();
- for (const r of result.results) {
- const arr = byCategory.get(r.category) ?? [];
- arr.push(r);
- byCategory.set(r.category, arr);
- }
- for (const [category, rows] of byCategory) {
- out.push(`## ${category}`);
+ // What's slipping through
+ if (slippingRows.length > 0) {
+ out.push(`## ○ Slipping through (${totalSlipping} action${totalSlipping === 1 ? "" : "s"} caught by audit, not yet blocked)`);
out.push("");
- out.push("| Policy / Detector | Hits | Projects | Source | First seen | Last seen |");
- out.push("|---|---:|---:|---|---|---|");
- for (const r of rows) {
+ out.push("Patterns the audit detected but real-time enforcement isn't on for. The CTA column shows how to fix each.");
+ out.push("");
+ out.push("| Issue | Hits | Projects | Last seen | Fix |");
+ out.push("|---|---:|---:|---|---|");
+ for (const r of slippingRows) {
+ const fix = r.source === "builtin"
+ ? `\`failproofai policies --install ${shortName(r.name)}\``
+ : "_audit-only_";
out.push(
- `| \`${escapeTableCell(r.name)}\` | ${r.hits} | ${r.projects} | ${escapeTableCell(r.source)} | ${escapeTableCell(r.firstSeen ?? "—")} | ${escapeTableCell(r.lastSeen ?? "—")} |`,
+ `| ${escapeTableCell(r.displayTitle)} | ${r.hits} | ${r.projects} | ${formatTimeAgo(r.lastSeen)} | ${fix} |`,
);
}
out.push("");
- // Examples sub-section
- for (const r of rows) {
- if (r.examples.length === 0) continue;
- out.push(`### \`${r.name}\` — examples`);
+
+ if (unenabledBuiltinRows.length > 0) {
+ out.push(`### Enable everything in one command`);
+ out.push("");
+ out.push("```bash");
+ out.push(`failproofai policies --install ${unenabledBuiltinRows.map((r) => shortName(r.name)).join(" ")}`);
+ out.push("```");
out.push("");
+ }
+ }
+
+ // Examples appendix (only if non-trivial)
+ const rowsWithExamples = [...enabledRows, ...slippingRows].filter((r) => r.examples.length > 0);
+ if (rowsWithExamples.length > 0) {
+ out.push("## Examples");
+ out.push("");
+ for (const r of rowsWithExamples) {
+ out.push(`### ${escapeBackticks(r.displayTitle)} (\`${escapeBackticks(shortName(r.name))}\`)`);
+ out.push("");
+ if (r.impact) {
+ out.push(`> ${r.impact}`);
+ out.push("");
+ }
for (const e of r.examples) {
- out.push(`- \`${escapeBackticks(e.example)}\` _(${e.cwd || "?"}, ${e.timestamp})_`);
+ out.push(`- \`${escapeBackticks(e.example)}\` _(${e.cwd || "?"}, ${formatTimeAgo(e.timestamp)})_`);
}
out.push("");
}
@@ -162,20 +336,7 @@ export function formatMarkdown(result: AuditResult): string {
out.push("---");
out.push("");
- out.push(
- "_Tip: enable any of these in real time with " +
- "`failproofai policies --install ...`._",
- );
+ out.push("⭐ Star failproofai on GitHub: ");
out.push("");
return out.join("\n");
}
-
-function escapeBackticks(s: string): string {
- return s.replace(/`/g, "\\`");
-}
-
-/** Escape characters that would break a markdown table row. Pipes split
- * columns; backslashes escape the next char; leading newlines end the row. */
-function escapeTableCell(s: string): string {
- return s.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/[\r\n]+/g, " ");
-}
diff --git a/src/audit/telemetry.ts b/src/audit/telemetry.ts
new file mode 100644
index 0000000..28c5cfd
--- /dev/null
+++ b/src/audit/telemetry.ts
@@ -0,0 +1,113 @@
+/**
+ * PostHog telemetry for `failproofai audit`.
+ *
+ * Plugs into the existing `trackHookEvent` surface (src/hooks/hook-telemetry.ts)
+ * — same distinct ID strategy, same opt-out (`FAILPROOFAI_TELEMETRY_DISABLED=1`),
+ * same fail-open contract (never crash the CLI).
+ *
+ * **Privacy contract — strictly enforced:**
+ * • Never send transcript paths, decoded project folder names, cwds,
+ * example command strings, file paths, sessionIds, or tool inputs.
+ * • Only ever send: policy/detector slugs (already public), counts,
+ * booleans, bucketed ages, CLI tags, output mode tags.
+ *
+ * Events fired during one `audit` run:
+ * 1. `audit_started` — once, before scanning
+ * 2. `audit_pattern_detected` — one per AuditCount aggregated
+ * 3. `audit_install_cta_shown` — once, if there are unenabled builtins
+ * 4. `audit_completed` — once, at the end
+ */
+import { trackHookEvent } from "../hooks/hook-telemetry";
+import { getInstanceId } from "../../lib/telemetry-id";
+import type { AuditCount, AuditResult, RunAuditOptions } from "./types";
+
+/** Bucketize an ISO timestamp into a coarse "days since" value to avoid
+ * shipping anything that could correlate with a specific session timing.
+ * Returns null when unknown. */
+function ageBucketDays(iso: string | undefined): number | null {
+ if (!iso) return null;
+ const ms = Date.now() - new Date(iso).getTime();
+ if (Number.isNaN(ms) || ms < 0) return null;
+ const days = Math.floor(ms / 86_400_000);
+ // Bucket to nearest expected reporting horizon, never raw days
+ if (days <= 0) return 0;
+ if (days <= 1) return 1;
+ if (days <= 7) return 7;
+ if (days <= 30) return 30;
+ if (days <= 90) return 90;
+ if (days <= 365) return 365;
+ return 366;
+}
+
+function shortName(name: string): string {
+ const slash = name.indexOf("/");
+ return slash >= 0 ? name.slice(slash + 1) : name;
+}
+
+/** Fire-and-forget — telemetry never blocks or fails the CLI. Promises are
+ * awaited inside trackHookEvent (which has a 5s AbortSignal); callers can
+ * `void` the return. */
+export function trackAuditStarted(opts: RunAuditOptions, outputMode: string): void {
+ void trackHookEvent(getInstanceId(), "audit_started", {
+ clis_requested: opts.clis ?? "all",
+ since_window: opts.since ?? null,
+ has_project_filter: !!opts.projects?.length,
+ has_policy_filter: !!opts.policies?.length,
+ no_cache: !!opts.noCache,
+ output_mode: outputMode,
+ });
+}
+
+export function trackAuditPatternDetected(count: AuditCount): void {
+ void trackHookEvent(getInstanceId(), "audit_pattern_detected", {
+ pattern_name: shortName(count.name),
+ pattern_source: count.source,
+ pattern_category: count.category,
+ hits: count.hits,
+ projects: count.projects,
+ enabled_in_config: count.enabledInConfig,
+ severity: count.severity,
+ first_seen_age_days: ageBucketDays(count.firstSeen),
+ last_seen_age_days: ageBucketDays(count.lastSeen),
+ });
+}
+
+export function trackAuditInstallCtaShown(unenabledNames: string[]): void {
+ if (unenabledNames.length === 0) return;
+ void trackHookEvent(getInstanceId(), "audit_install_cta_shown", {
+ unenabled_count: unenabledNames.length,
+ unenabled_pattern_names: unenabledNames.map(shortName),
+ });
+}
+
+export function trackAuditCompleted(
+ result: AuditResult,
+ outputMode: string,
+): void {
+ const enabledHits = result.results
+ .filter((r) => r.source === "builtin" && r.enabledInConfig)
+ .reduce((acc, r) => acc + r.hits, 0);
+ const unenabledHits = result.results
+ .filter((r) => r.source === "builtin" && !r.enabledInConfig)
+ .reduce((acc, r) => acc + r.hits, 0);
+ const detectorHits = result.results
+ .filter((r) => r.source === "audit-detector")
+ .reduce((acc, r) => acc + r.hits, 0);
+
+ void trackHookEvent(getInstanceId(), "audit_completed", {
+ duration_ms: result.transcripts.durationMs,
+ transcripts_scanned: result.transcripts.scanned,
+ transcripts_skipped: result.transcripts.skipped,
+ transcripts_errors: result.transcripts.errors,
+ total_hits: result.totals.hits,
+ projects_with_hits: result.totals.projectsWithHits,
+ enabled_builtin_hits: enabledHits,
+ unenabled_builtin_hits: unenabledHits,
+ audit_detector_hits: detectorHits,
+ result_count: result.results.length,
+ clis_scanned: result.scope.cli,
+ since_window: result.scope.since,
+ has_project_filter: result.scope.projects !== "all",
+ output_mode: outputMode,
+ });
+}
diff --git a/src/audit/types.ts b/src/audit/types.ts
index 81791fb..d54534c 100644
--- a/src/audit/types.ts
+++ b/src/audit/types.ts
@@ -74,6 +74,11 @@ export interface Detector {
/** Severity tier — drives row color in the table. */
severity: "warn" | "info";
detect: DetectorFn;
+ /** User-facing past-tense phrase, e.g. "Re-read a file just edited". Falls
+ * back to `description` when omitted. */
+ displayTitle?: string;
+ /** One short clause describing the consequence. */
+ impact?: string;
}
/** Aggregated count for one policy or detector. */
@@ -96,6 +101,21 @@ export interface AuditCount {
lastSeen?: string;
/** Up to N example commands/snippets that triggered this (80 chars each). */
examples: { sessionId: string; cwd: string; timestamp: string; example: string }[];
+ /** User-facing past-tense phrase used in the report. Always populated by the
+ * orchestrator — falls back to the policy/detector description when no
+ * `displayTitle` was authored. */
+ displayTitle: string;
+ /** One short clause describing the consequence. May be empty for legacy
+ * policies without authored impact copy. */
+ impact: string;
+ /** Whether the user currently has this builtin enabled. Drives the
+ * "already protected" vs "slipping through" split in the report. Always
+ * `false` for audit-only detectors (they don't have a runtime enforcement
+ * path today). */
+ enabledInConfig: boolean;
+ /** Pre-built one-line install command (or audit-only notice) the report
+ * shows next to each row, so users can copy-paste to remediate. */
+ installHint: string;
}
/** Per-transcript scan result (also the per-transcript cache value). */
diff --git a/src/hooks/builtin-policies.ts b/src/hooks/builtin-policies.ts
index 40418a9..54db64c 100644
--- a/src/hooks/builtin-policies.ts
+++ b/src/hooks/builtin-policies.ts
@@ -1502,6 +1502,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
{
name: "sanitize-jwt",
description: "Stop Claude from reading JWTs in tool responses",
+ displayTitle: "Redacted JWT tokens from tool output",
+ impact: "Stops the agent from echoing auth tokens it saw in command output.",
fn: sanitizeJwt,
match: { events: ["PostToolUse"] },
defaultEnabled: true,
@@ -1510,6 +1512,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
{
name: "sanitize-api-keys",
description: "Stop Claude from reading API keys (OpenAI, Anthropic, GitHub, AWS, Stripe, Google) in tool responses",
+ displayTitle: "Redacted API keys from tool output",
+ impact: "Catches OpenAI / Anthropic / GitHub / AWS / Stripe / Google keys before the model sees them.",
fn: sanitizeApiKeys,
match: { events: ["PostToolUse"] },
defaultEnabled: true,
@@ -1525,6 +1529,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
{
name: "sanitize-connection-strings",
description: "Stop Claude from reading database connection strings with embedded credentials in tool responses",
+ displayTitle: "Redacted database connection strings from tool output",
+ impact: "Strips embedded DB credentials before they reach the model context.",
fn: sanitizeConnectionStrings,
match: { events: ["PostToolUse"] },
defaultEnabled: true,
@@ -1533,6 +1539,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
{
name: "sanitize-private-key-content",
description: "Stop Claude from reading PEM private key content in tool responses",
+ displayTitle: "Redacted PEM private keys from tool output",
+ impact: "Prevents private key bodies from being echoed into chat context.",
fn: sanitizePrivateKeyContent,
match: { events: ["PostToolUse"] },
defaultEnabled: true,
@@ -1540,6 +1548,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "sanitize-bearer-tokens",
+ displayTitle: "Redacted bearer tokens from tool output",
+ impact: "Strips Authorization: Bearer values before they hit the model.",
description: "Stop Claude from reading Authorization Bearer tokens in tool responses",
fn: sanitizeBearerTokens,
match: { events: ["PostToolUse"] },
@@ -1548,6 +1558,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "protect-env-vars",
+ displayTitle: "Tried to dump environment variables to chat",
+ impact: "Env vars often contain secrets; blocking `env` / `printenv` keeps them out of the model context.",
description: "Prevent commands that read environment variables",
fn: protectEnvVars,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1556,6 +1568,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-env-files",
+ displayTitle: "Tried to read or write a .env file",
+ impact: "`.env` files routinely contain API keys and DB credentials.",
description: "Block reading/writing .env files",
fn: blockEnvFiles,
match: { events: ["PreToolUse"] },
@@ -1564,6 +1578,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-read-outside-cwd",
+ displayTitle: "Tried to read files outside your project directory",
+ impact: "Stops the agent from peeking at neighboring repos or your home directory.",
description: "Block file reads outside the session working directory",
fn: blockReadOutsideCwd,
match: { events: ["PreToolUse"], toolNames: ["Read", "Glob", "Grep", "Bash"] },
@@ -1579,6 +1595,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-sudo",
+ displayTitle: "Tried to run a command with sudo",
+ impact: "Sudo gives the agent root — blocked unless explicitly allow-listed.",
description: "Block sudo commands",
fn: blockSudo,
// PermissionRequest is Codex's escalation-approval event; fire the same
@@ -1596,6 +1614,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-curl-pipe-sh",
+ displayTitle: "Tried to pipe a downloaded script straight to a shell",
+ impact: "`curl ... | sh` runs unverified remote code on your machine.",
description: "Block piping downloads to shell",
fn: blockCurlPipeSh,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1604,6 +1624,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-rm-rf",
+ displayTitle: "Tried to recursively delete a system path",
+ impact: "Catches catastrophic `rm -rf /` and Windows equivalents.",
description: "Prevent catastrophic deletions",
fn: blockRmRf,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1619,6 +1641,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-failproofai-commands",
+ displayTitle: "Tried to disable or modify failproofai itself",
+ impact: "Prevents the agent from turning off the policies that protect you.",
description: "Block failproofai CLI commands and uninstallation",
fn: blockFailproofaiCommands,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1627,6 +1651,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-kubectl",
+ displayTitle: "Tried to run a Kubernetes command",
+ impact: "kubectl can change live cluster state — gated unless allow-listed.",
description: "Block kubectl commands (Kubernetes cluster mutations)",
fn: blockKubectl,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1642,6 +1668,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-terraform",
+ displayTitle: "Tried to run a Terraform/OpenTofu command",
+ impact: "Terraform mutates real infrastructure — gated unless allow-listed.",
description: "Block terraform and tofu (OpenTofu) commands",
fn: blockTerraform,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1657,6 +1685,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-aws-cli",
+ displayTitle: "Tried to run an AWS CLI command",
+ impact: "AWS CLI can spend money or break prod — gated.",
description: "Block aws CLI commands",
fn: blockAwsCli,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1672,6 +1702,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-gcloud",
+ displayTitle: "Tried to run a Google Cloud command",
+ impact: "gcloud can spend money or break prod — gated.",
description: "Block gcloud (Google Cloud) CLI commands",
fn: blockGcloud,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1687,6 +1719,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-az-cli",
+ displayTitle: "Tried to run an Azure CLI command",
+ impact: "az can spend money or break prod — gated.",
description: "Block az (Azure) CLI commands",
fn: blockAzCli,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1702,6 +1736,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-helm",
+ displayTitle: "Tried to run a Helm command",
+ impact: "Helm releases mutate cluster state — gated.",
description: "Block helm commands",
fn: blockHelm,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1717,6 +1753,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-gh-pipeline",
+ displayTitle: "Tried to run a privileged GitHub CLI pipeline command",
+ impact: "Catches `gh workflow run`, `gh pr merge`, `gh secret set`, etc.",
description: "Block gh CLI pipeline-trigger subcommands (workflow run, run rerun/cancel, pr merge, release create/delete, cache delete, secret set/delete)",
fn: blockGhPipeline,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1732,6 +1770,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-secrets-write",
+ displayTitle: "Tried to write a secret-key file",
+ impact: "Stops the agent from creating `.pem`, `id_rsa`, `credentials.json`, etc.",
description: "Block writing secret key files",
fn: blockSecretsWrite,
match: { events: ["PreToolUse"], toolNames: ["Write"] },
@@ -1747,6 +1787,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-push-master",
+ displayTitle: "Tried to push directly to main/master",
+ impact: "Direct pushes to a protected branch bypass review.",
description: "Block pushing to main/master",
fn: blockPushMaster,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1762,6 +1804,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-force-push",
+ displayTitle: "Tried to force-push",
+ impact: "Force-pushes rewrite history and can clobber teammates' work.",
description: "Prevent force-pushing to any branch",
fn: blockForcePush,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1770,6 +1814,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "block-work-on-main",
+ displayTitle: "Tried to commit or merge on main/master",
+ impact: "Work should land via PR — direct commits skip review.",
description: "Block git commits and merges on main/master branch",
fn: blockWorkOnMain,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1785,6 +1831,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-git-amend",
+ displayTitle: "Used git commit --amend",
+ impact: "Amending after a push rewrites history that others may have pulled.",
description: "Warns before amending git commits, which rewrites history",
fn: warnGitAmend,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1793,6 +1841,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-git-stash-drop",
+ displayTitle: "Tried to drop or clear git stash",
+ impact: "Stash deletions are permanent and silent.",
description: "Warns before permanently deleting stashed changes",
fn: warnGitStashDrop,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1801,6 +1851,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-all-files-staged",
+ displayTitle: "Staged all files with git add -A / .",
+ impact: "Wide stages routinely catch generated files or secrets you didn't intend to commit.",
description: "Warns before staging all working tree files with git add -A / . / --all",
fn: warnAllFilesStaged,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1809,6 +1861,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-destructive-sql",
+ displayTitle: "Ran destructive SQL (DROP / TRUNCATE / DELETE without WHERE)",
+ impact: "Easy way to wipe a table by accident.",
description: "Warn before executing destructive SQL (DROP/TRUNCATE/DELETE without WHERE) via database clients",
fn: warnDestructiveSql,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1817,6 +1871,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-schema-alteration",
+ displayTitle: "Altered a database schema column",
+ impact: "ALTER TABLE operations can lock tables and break readers.",
description: "Warns before SQL schema changes (ALTER TABLE with column or rename operations)",
fn: warnSchemaAlteration,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1825,6 +1881,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-package-publish",
+ displayTitle: "Tried to publish a package",
+ impact: "Publishes are irreversible — `npm publish` / `cargo publish` shouldn't happen without intent.",
description: "Warn before publishing packages to public registries (npm, PyPI, crates.io, RubyGems, etc.)",
fn: warnPackagePublish,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1833,6 +1891,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-global-package-install",
+ displayTitle: "Installed a package globally",
+ impact: "`npm i -g`, `cargo install`, `pip --user` pollute your machine outside the project.",
description: "Warns before installing packages globally (npm -g, cargo install, etc.)",
fn: warnGlobalPackageInstall,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1841,6 +1901,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "prefer-package-manager",
+ displayTitle: "Used a non-preferred package manager",
+ impact: "Mixing package managers creates lockfile churn for your team.",
description: "Blocks non-preferred package managers and tells Claude to use an allowed one (e.g., uv instead of pip)",
fn: preferPackageManager,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1861,6 +1923,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-large-file-write",
+ displayTitle: "Wrote a file larger than the configured threshold",
+ impact: "Catches accidentally large file writes (logs, binaries, model dumps).",
description: "Warn before writing files larger than 1MB (configurable via thresholdKb param)",
fn: warnLargeFileWrite,
match: { events: ["PreToolUse"], toolNames: ["Write"] },
@@ -1876,6 +1940,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-background-process",
+ displayTitle: "Started a long-lived background process",
+ impact: "Catches `nohup` / `&` / `screen` / `tmux` / `disown` patterns that the agent often forgets to clean up.",
description: "Warns before starting detached or background processes",
fn: warnBackgroundProcess,
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
@@ -1884,6 +1950,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "warn-repeated-tool-calls",
+ displayTitle: "Called the same tool 3+ times with identical arguments",
+ impact: "Usually a sign of a stuck loop burning tokens.",
description: "Warn when the same tool is called 3+ times with identical parameters",
fn: warnRepeatedToolCalls,
match: { events: ["PreToolUse"] },
@@ -1892,6 +1960,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "require-commit-before-stop",
+ displayTitle: "Stopped with uncommitted changes",
+ impact: "Work not in a commit is invisible to teammates and easy to lose.",
description: "Require all changes to be committed before Claude stops",
fn: requireCommitBeforeStop,
match: { events: ["Stop"] },
@@ -1900,6 +1970,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "require-push-before-stop",
+ displayTitle: "Stopped with unpushed commits",
+ impact: "Local-only commits won't trigger CI or be reviewable.",
description: "Require all commits to be pushed to remote before Claude stops",
fn: requirePushBeforeStop,
match: { events: ["Stop"] },
@@ -1920,6 +1992,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "require-pr-before-stop",
+ displayTitle: "Stopped without a PR for the branch",
+ impact: "Branches without PRs don't get reviewed.",
description: "Require a pull request to exist for the current branch before Claude stops",
fn: requirePrBeforeStop,
match: { events: ["Stop"] },
@@ -1935,6 +2009,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "require-no-conflicts-before-stop",
+ displayTitle: "Stopped with a branch that conflicts with main",
+ impact: "Conflicting branches can't merge — surface them early.",
description: "Require the current branch to merge cleanly with the base branch before Claude stops",
fn: requireNoConflictsBeforeStop,
match: { events: ["Stop"] },
@@ -1950,6 +2026,8 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
},
{
name: "require-ci-green-before-stop",
+ displayTitle: "Stopped with failing CI",
+ impact: "Failing CI blocks deploy.",
description: "Require CI checks to pass on the current HEAD commit before Claude stops (ignores stale runs on prior commits)",
fn: requireCiGreenBeforeStop,
match: { events: ["Stop"] },
diff --git a/src/hooks/policy-types.ts b/src/hooks/policy-types.ts
index 8e6037e..ccb04a6 100644
--- a/src/hooks/policy-types.ts
+++ b/src/hooks/policy-types.ts
@@ -54,6 +54,15 @@ export interface BuiltinPolicyDefinition {
category: string;
beta?: boolean;
params?: PolicyParamsSchema;
+ /** User-facing past-tense phrase used in `failproofai audit` output.
+ * Frames the agent's action as something the user observes after-the-fact,
+ * e.g. "Tried to push to main branch" or "Redacted JWT from tool output".
+ * Falls back to `description` when omitted. */
+ displayTitle?: string;
+ /** One short clause describing the consequence of the action, used as a
+ * secondary line in the audit report. e.g. "Could leak code from neighboring
+ * repos to the model." */
+ impact?: string;
}
export interface CustomHook {
From a0da3a97e05d003d1309173551ae58532e6fab32 Mon Sep 17 00:00:00 2001
From: NiveditJain
Date: Thu, 21 May 2026 20:32:50 -0700
Subject: [PATCH 7/9] docs(audit): mark `failproofai audit` as beta across all
surfaces
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
• docs/cli/audit.mdx — title "Audit past sessions (beta)" + Mintlify
callout naming beta status and that flags / output / detector
catalog may change before the next stable cut.
• bin/failproofai.mjs — `audit (beta)` in the top-level COMMANDS list
and the `audit --help` page leads with a "NOTE: This command is in
beta. ..." block before USAGE.
• src/audit/report.ts — table header shows the `[beta]` tag next to
the audit command name on every run, and the generated markdown
report's intro has a "_Generated by failproofai audit (beta)_" line.
Also fixes a small wording bug in the table header — was "your the last
1d" (double-article from joining a stray "your" prefix with the "the
last …" sinceLabel), now reads "the last 1d".
No behavior change beyond the cosmetic wording fix.
Co-Authored-By: Claude Opus 4.7
---
bin/failproofai.mjs | 9 +++++++--
docs/cli/audit.mdx | 9 ++++++++-
src/audit/report.ts | 4 +++-
3 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/bin/failproofai.mjs b/bin/failproofai.mjs
index 9416ae3..8acbda8 100755
--- a/bin/failproofai.mjs
+++ b/bin/failproofai.mjs
@@ -118,10 +118,12 @@ COMMANDS
policies --help, -h Show this help for the policies command
- audit Scan past agent CLI transcripts and count
+ audit (beta) Scan past agent CLI transcripts and count
"stupid behaviors" (env-var checks, force
pushes, redundant cd , sleep loops,
etc.) per policy / audit detector.
+ Going live shortly — flags + output may
+ still change between beta releases.
--cli claude|codex|copilot|cursor|opencode|pi|gemini
Restrict to one or more CLIs (default: all).
--project Restrict to one cwd (repeatable).
@@ -433,7 +435,10 @@ EXAMPLES
if (subArgs.includes("--help") || subArgs.includes("-h")) {
console.log(`
-failproofai audit — scan past agent transcripts for stupid behaviors
+failproofai audit (beta) — scan past agent transcripts for stupid behaviors
+
+ NOTE: This command is in beta. Flags, output format, and the audit-only
+ detector catalog may change before the next stable cut. Going live shortly.
USAGE
failproofai audit [options]
diff --git a/docs/cli/audit.mdx b/docs/cli/audit.mdx
index 79ead3f..b4f0dbf 100644
--- a/docs/cli/audit.mdx
+++ b/docs/cli/audit.mdx
@@ -1,8 +1,15 @@
---
-title: Audit past sessions
+title: Audit past sessions (beta)
description: "Count how often the agent did wasteful or risky things across past transcripts"
---
+
+ **Beta feature.** `failproofai audit` is shipping as beta while we collect early
+ feedback. CLI flags, output format, and the audit-only detector catalog may
+ change before the next stable cut. Going live shortly — please open an issue
+ if anything looks off.
+
+
```bash
failproofai audit [options]
```
diff --git a/src/audit/report.ts b/src/audit/report.ts
index a5c53d7..6e33a26 100644
--- a/src/audit/report.ts
+++ b/src/audit/report.ts
@@ -136,7 +136,7 @@ export function formatText(result: AuditResult, opts: RunAuditOptions = {}): str
const lines: string[] = [];
// ── Header ──────────────────────────────────────────────────────
- lines.push(`${ANSI.cyan}🛡 failproofai audit${ANSI.reset} · your ${sinceLabel}`);
+ lines.push(`${ANSI.cyan}🛡 failproofai audit${ANSI.reset} ${ANSI.dim}[beta]${ANSI.reset} · ${sinceLabel}`);
lines.push(
` ${ANSI.dim}${result.transcripts.scanned} sessions · ${result.totals.projectsWithHits} project${result.totals.projectsWithHits === 1 ? "" : "s"} with hits · scanned in ${(result.transcripts.durationMs / 1000).toFixed(1)}s${ANSI.reset}`,
);
@@ -250,6 +250,8 @@ export function formatMarkdown(result: AuditResult): string {
out.push(`# Agent behavior audit · ${sinceLabel}`);
out.push("");
+ out.push("> _Generated by `failproofai audit` (**beta**) — flags and output may change between releases. Going live shortly._");
+ out.push("");
out.push(`*Generated ${result.scannedAt} — scanned ${result.transcripts.scanned} sessions across ${result.totals.projectsWithHits} project(s) in ${(result.transcripts.durationMs / 1000).toFixed(1)}s.*`);
out.push("");
From 998a046b223bf135b42f59dd01318b5a73044e80 Mon Sep 17 00:00:00 2001
From: NiveditJain
Date: Thu, 21 May 2026 20:48:23 -0700
Subject: [PATCH 8/9] ci: empty commit to trigger CI on a0da3a9 lineage
(Actions missed previous push via the old redirected remote URL)
From f2157485390dfdb218c2b479762ba4c1b60ef3b6 Mon Sep 17 00:00:00 2001
From: NiveditJain
Date: Thu, 21 May 2026 20:53:08 -0700
Subject: [PATCH 9/9] fix(audit): respect --report path in the report-written
footer line
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The "📄 Shareable report: …" line at the bottom of `failproofai audit`'s
table output was hardcoded to `./failproofai-audit.md`, ignoring the
actual `--report ` value. Now mirrors `opts.reportPath` (and is
suppressed entirely when `--no-report` is passed).
CodeRabbit finding on PR #377.
Co-Authored-By: Claude Opus 4.7
---
src/audit/report.ts | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/audit/report.ts b/src/audit/report.ts
index 6e33a26..3bf7dea 100644
--- a/src/audit/report.ts
+++ b/src/audit/report.ts
@@ -214,7 +214,12 @@ export function formatText(result: AuditResult, opts: RunAuditOptions = {}): str
lines.push("");
}
- lines.push(` 📄 Shareable report: ${ANSI.cyan}./failproofai-audit.md${ANSI.reset}`);
+ // Mirror the actual --report path so the printed footer matches what was
+ // (or will be) written. Suppressed entirely with --no-report.
+ if (!opts.noReport) {
+ const reportPath = opts.reportPath ?? "./failproofai-audit.md";
+ lines.push(` 📄 Shareable report: ${ANSI.cyan}${reportPath}${ANSI.reset}`);
+ }
lines.push(` ⭐ Star us: ${ANSI.cyan}https://github.com/FailproofAI/failproofai${ANSI.reset}`);
lines.push("");