Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

167 changes: 93 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,80 +25,99 @@ before they become incidents. Zero latency. Runs locally.

## Supported agent CLIs

<p align="center">
<a href="https://claude.com/claude-code" title="Claude Code">
<img src="assets/logos/claude.svg" alt="Claude Code" width="64" height="64" />
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://learn.chatgpt.com" title="OpenAI Codex">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/openai-dark.svg" />
<img src="assets/logos/openai-light.svg" alt="OpenAI Codex" width="64" height="64" />
</picture>
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://github.com/features/copilot/cli" title="GitHub Copilot CLI">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/copilot-dark.svg" />
<img src="assets/logos/copilot-light.svg" alt="GitHub Copilot" width="64" height="64" />
</picture>
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://cursor.com" title="Cursor Agent CLI">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/cursor-dark.svg" />
<img src="assets/logos/cursor-light.svg" alt="Cursor Agent" width="64" height="64" />
</picture>
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://opencode.ai/" title="OpenCode">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/opencode-dark.svg" />
<img src="assets/logos/opencode-light.svg" alt="OpenCode" width="64" height="64" />
</picture>
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://pi.dev/" title="Pi (pi-coding-agent)">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/pi-dark.svg" />
<img src="assets/logos/pi-light.svg" alt="Pi" width="64" height="64" />
</picture>
</a>
</p>
<p align="center">
<a href="https://hermes-agent.nousresearch.com/" title="Hermes (hermes-agent)">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/hermes-dark.svg" />
<img src="assets/logos/hermes-light.svg" alt="Hermes" width="64" height="64" />
</picture>
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://openclaw.ai/" title="OpenClaw (openclaw gateway)">
<img src="assets/logos/openclaw.svg" alt="OpenClaw" width="64" height="64" />
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://factory.ai/" title="Factory Droid (droid)">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/factory-dark.png" />
<img src="assets/logos/factory-light.png" alt="Factory Droid" width="64" height="64" />
</picture>
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://devin.ai" title="Devin CLI (Cognition)">
<img src="assets/logos/devin.svg" alt="Devin CLI" width="64" height="64" />
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://antigravity.google" title="Antigravity CLI (agy)">
<img src="assets/logos/antigravity.svg" alt="Antigravity CLI" width="64" height="64" />
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://goose-docs.ai/" title="Goose (codename goose)">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/goose-dark.svg" />
<img src="assets/logos/goose-light.svg" alt="Goose" width="64" height="64" />
</picture>
</a>
</p>
<!-- A 6-column table instead of inline <img> runs: table columns never re-wrap,
so the grid stays 2×6 at any window width (scrolling on very narrow screens
instead of collapsing into ragged orphan rows). -->
<table align="center">
<tr>
<td align="center" width="96">
<a href="https://claude.com/claude-code" title="Claude Code">
<img src="assets/logos/claude.svg" alt="Claude Code" width="56" height="56" />
</a>
</td>
<td align="center" width="96">
<a href="https://learn.chatgpt.com" title="OpenAI Codex">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/openai-dark.svg" />
<img src="assets/logos/openai-light.svg" alt="OpenAI Codex" width="56" height="56" />
</picture>
</a>
</td>
<td align="center" width="96">
<a href="https://github.com/features/copilot/cli" title="GitHub Copilot CLI">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/copilot-dark.svg" />
<img src="assets/logos/copilot-light.svg" alt="GitHub Copilot" width="56" height="56" />
</picture>
</a>
</td>
<td align="center" width="96">
<a href="https://cursor.com" title="Cursor Agent CLI">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/cursor-dark.svg" />
<img src="assets/logos/cursor-light.svg" alt="Cursor Agent" width="56" height="56" />
</picture>
</a>
</td>
<td align="center" width="96">
<a href="https://opencode.ai/" title="OpenCode">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/opencode-dark.svg" />
<img src="assets/logos/opencode-light.svg" alt="OpenCode" width="56" height="56" />
</picture>
</a>
</td>
<td align="center" width="96">
<a href="https://pi.dev/" title="Pi (pi-coding-agent)">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/pi-dark.svg" />
<img src="assets/logos/pi-light.svg" alt="Pi" width="56" height="56" />
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" width="96">
<a href="https://hermes-agent.nousresearch.com/" title="Hermes (hermes-agent)">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/hermes-dark.svg" />
<img src="assets/logos/hermes-light.svg" alt="Hermes" width="56" height="56" />
</picture>
</a>
</td>
<td align="center" width="96">
<a href="https://openclaw.ai/" title="OpenClaw (openclaw gateway)">
<img src="assets/logos/openclaw.svg" alt="OpenClaw" width="56" height="56" />
</a>
</td>
<td align="center" width="96">
<a href="https://factory.ai/" title="Factory Droid (droid)">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/factory-dark.png" />
<img src="assets/logos/factory-light.png" alt="Factory Droid" width="56" height="56" />
</picture>
</a>
</td>
<td align="center" width="96">
<a href="https://devin.ai" title="Devin CLI (Cognition)">
<img src="assets/logos/devin.svg" alt="Devin CLI" width="56" height="56" />
</a>
</td>
<td align="center" width="96">
<a href="https://antigravity.google" title="Antigravity CLI (agy)">
<img src="assets/logos/antigravity.svg" alt="Antigravity CLI" width="56" height="56" />
</a>
</td>
<td align="center" width="96">
<a href="https://goose-docs.ai/" title="Goose (codename goose)">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/goose-dark.svg" />
<img src="assets/logos/goose-light.svg" alt="Goose" width="56" height="56" />
</picture>
</a>
</td>
</tr>
</table>

> Install hooks for one or any combination: `failproofai policies --install --cli opencode pi` (or `--cli claude codex copilot cursor opencode pi hermes openclaw factory devin antigravity goose`). Omit `--cli` to auto-detect installed CLIs and prompt.
>
Expand Down
4 changes: 4 additions & 0 deletions __tests__/api/audit-run-route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ describe("POST /api/audit/run (fire-and-forget)", () => {
expect(s.error).toBe("scan blew up");
expect(writeCacheMock).not.toHaveBeenCalled();
expect(trackedNames()).toContain("audit_run_failed");
expect(trackEventMock).toHaveBeenCalledWith(
"audit_run_failed",
expect.objectContaining({ error_type: "Error", error_message: "scan blew up" }),
);
});

it("writes the cache, tracks audit_run_completed with metrics, and clears the lock on success", async () => {
Expand Down
1 change: 1 addition & 0 deletions __tests__/audit/audit-cli-telemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ describe("failproofai audit telemetry", () => {
expect(h.trackHookEvent).toHaveBeenCalledWith("test-instance", "cli_audit_failed", {
source: "cli",
error_type: "TypeError",
error_message: "disk exploded",
});
expect(exitInfo?.code).toBe(1);
// The fix: failed must have RESOLVED before the exit fired.
Expand Down
13 changes: 12 additions & 1 deletion __tests__/e2e/helpers/cli-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@
import { spawnSync } from "node:child_process";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { existsSync } from "node:fs";
import { existsSync, mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { expect } from "vitest";

const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");

// Isolated HOME so config-mutating commands (`policies --install/--uninstall`,
// `policy add/remove`, `configure`) resolve `~/.failproofai` and `~/.claude`
// under a throwaway dir instead of the developer's real home. Without this, any
// runCli call that reaches installHooks/removeHooks clobbers the real user
// config (Bun honors the spawn-env HOME, so overriding it here fully isolates).
// Created once per test module; the OS reclaims it from tmp.
const ISOLATED_HOME = mkdtempSync(resolve(tmpdir(), "failproofai-cli-e2e-"));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

function getBinaryPath(): string {
return resolve(REPO_ROOT, "bin/failproofai.mjs");
}
Expand All @@ -37,6 +46,8 @@ export function runCli(...args: string[]): CliRunResult {
const result = spawnSync("bun", [binaryPath, ...args], {
env: {
...process.env,
HOME: ISOLATED_HOME,
USERPROFILE: ISOLATED_HOME,
FAILPROOFAI_TELEMETRY_DISABLED: "1",
},
encoding: "utf8",
Expand Down
Loading
Loading