Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
147 changes: 147 additions & 0 deletions .gemini/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook SessionStart --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"SessionEnd": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook SessionEnd --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"BeforeAgent": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook BeforeAgent --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"AfterAgent": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook AfterAgent --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"BeforeModel": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook BeforeModel --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"AfterModel": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook AfterModel --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"BeforeToolSelection": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook BeforeToolSelection --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"BeforeTool": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook BeforeTool --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"AfterTool": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook AfterTool --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"PreCompress": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook PreCompress --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
],
"Notification": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun $GEMINI_PROJECT_DIR/bin/failproofai.mjs --hook Notification --cli gemini",
"timeout": 60000,
"__failproofai_hook__": true
}
]
}
]
}
}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
## Unreleased

### Features
- Add Gemini CLI integration (beta) across hooks, activity dashboard, session viewer, and `/projects` listing. `--cli gemini` writes Claude-shape hook entries into `~/.gemini/settings.json` (user) or `<cwd>/.gemini/settings.json` (project) using Gemini's `{matcher, hooks: [{type, command, timeout}]}` matcher-wrapper schema. Subscribes to all 11 documented events (SessionStart, SessionEnd, BeforeAgent, AfterAgent, BeforeModel, AfterModel, BeforeToolSelection, BeforeTool, AfterTool, PreCompress, Notification); BeforeModel / AfterModel / BeforeToolSelection lack a Claude canonical equivalent so no policies match on them today, but the binary still records activity for those events so future policies can opt in. The handler canonicalizes Gemini's snake_case tool names (`run_shell_command`, `read_file`, `read_many_files`, `write_file`, `replace`, `glob`, `grep_search`, `list_directory`, `web_fetch`, `google_web_search`, `write_todos`, `save_memory`, `ask_user`) to Claude PascalCase (`Bash`, `Read`, `Write`, `Edit`, `Glob`, `Grep`, `LS`, `WebFetch`, `WebSearch`, `TodoWrite`, `Memory`, `AskUser`) via `GEMINI_TOOL_MAP` so existing builtin policies (block-sudo, block-rm-rf, sanitize-api-keys, …) fire unchanged on Gemini sessions. MCP tool names (`mcp_<server>_<tool>` pattern) and Skills tool names pass through unchanged. The policy evaluator emits Gemini's flat `{decision: "deny", reason}` deny shape (preferred per Gemini's "Golden Rule" exit-0 contract), `{hookSpecificOutput: {hookEventName, additionalContext}}` for context injection on BeforeAgent / AfterTool / SessionStart, and `{decision: "block", reason}` on AfterAgent for force-retry semantics matching Claude's exit-2-from-Stop "do this before stopping" pattern. Path-protection (`isAgentInternalPath` + `isAgentSettingsFile`) covers `~/.gemini/` and `.gemini/settings.json`. Frontend: `lib/cli-registry.ts` adds a `Gemini CLI` entry with a sky-blue badge; `lib/projects.ts` merges Gemini projects into `/projects`; `app/project/[name]` and `/session/[id]` extend the external-CLI fallback chain. Also ships this repo's own `.gemini/settings.json` so contributors using `gemini` get hooks active automatically — uses `$GEMINI_PROJECT_DIR` for resolver stability (Gemini also sets `$CLAUDE_PROJECT_DIR` as a back-compat alias). Verified against gemini-cli v0.40.1 (#277).
- Add OpenCode (sst/opencode) integration (beta) across hooks, activity dashboard, session viewer, and `/projects` listing. `--cli opencode` writes a generated plugin shim at `.opencode/plugins/failproofai.mjs` plus a registration entry in `opencode.json`'s `plugin: []` array; SQLite-backed dashboard adapters read OpenCode's session store via `opencode db --format json`. Verified against opencode v1.14.33 (#270).
- Add Pi (`@mariozechner/pi-coding-agent`) integration (beta) across hooks, activity dashboard, session viewer, and `/projects` listing. `--cli pi` writes a `packages` entry into `.pi/settings.json` pointing at failproofai's bundled `pi-extension/`. Subscribes to all 7 Pi events (`tool_call`/`user_bash`/`input`/`session_start`/`tool_result`/`agent_end`/`session_shutdown`); the latter three are observation-only on Pi (no veto capability) but still activate the 5 PostToolUse and 5 `require-*-before-stop` builtins for visibility. Verified against pi-coding-agent v0.72.1 (#270).
- Add GitHub Copilot CLI integration (beta) across hooks, activity dashboard, session fallback, and `/projects` listing. Also ships this repo's own `.github/hooks/failproofai.json` so contributors developing failproofai with the GitHub Copilot CLI get hooks active automatically, mirroring the existing `.claude/settings.json` and `.codex/hooks.json` (#236)
- Add Cursor Agent CLI integration (beta) across hooks, activity dashboard, session viewer, and `/projects` listing. New `--cli cursor` flag installs into `~/.cursor/hooks.json` (user) or `<cwd>/.cursor/hooks.json` (project) using Cursor's flat-array schema with camelCase event keys (`preToolUse`, `beforeSubmitPrompt`, …); the handler canonicalizes to PascalCase via `CURSOR_EVENT_MAP` so existing builtin policies fire unchanged. The policy evaluator emits Cursor's `{permission, user_message, agent_message, additional_context, followup_message}` stdout shape. Path-protection (`isAgentInternalPath` + `isAgentSettingsFile`) covers `~/.cursor/` and `.cursor/hooks.json`. Frontend: `lib/cli-registry.ts` adds a `Cursor Agent` entry with an emerald badge; `lib/projects.ts` merges Cursor projects into `/projects`; `app/project/[name]` and `/session/[id]` extend the external-CLI fallback chain. Also ships this repo's own `.cursor/hooks.json` so contributors using Cursor get hooks active automatically (#245).
- Project page (`/project/[name]`): list Copilot and Cursor sessions alongside Claude + Codex, mirroring the existing merge logic on the projects index. Previously the project detail view only enumerated Claude + Codex transcripts (#245).

### Fixes
- Gemini integration (CodeRabbit follow-up): `lib/gemini-projects.ts` now uses `encodeFolderName(cwd)` for `ProjectFolder.name` so cross-CLI merge in `mergeProjectFolders` unions on the same key and Gemini-only project links resolve through `getGeminiSessionsByEncodedName`. `policy-evaluator.ts` preserves the raw CLI `--hook` arg via a new `SessionMetadata.rawHookEventName` field captured in `handler.ts` before canonicalization, so Gemini's `hookSpecificOutput.hookEventName` round-trips correctly even when stdin omits `hook_event_name`; deny-message construction now branches on event type so non-tool events (UserPromptSubmit / SessionStart / SessionEnd / Stop) emit "Blocked prompt|session start|…" instead of the misleading "Blocked unknown tool". `lib/gemini-sessions.ts` loosens `SESSION_FILE_RE` to accept any timestamp shape (Gemini docs include seconds; the load-bearing safety check is the first-line `sessionId` validation) and replaces the whole-file `readFileSync` in `findGeminiTranscript` with a bounded 4 KB `readFirstLineSync` helper so large transcripts no longer blow memory just to inspect the metadata header. `__tests__/lib/projects.test.ts` adds three Gemini aggregation tests (Gemini-only inclusion, cross-CLI merge by encoded slug, reject-fallback) mirroring the existing Pi / Cursor / OpenCode patterns (#277).
- `block-read-outside-cwd`: deny message now says "Reading agent settings file blocked" instead of "Reading Claude settings file blocked" — the policy has covered all 6 CLIs' settings files since #270 / #245 / #220 but the deny string was stale (#270).
- `require-ci-green-before-stop`: stop reporting historical CI failures as still-failing after a fix commit lands. The policy now filters `gh run list` results to runs whose `headSha` matches the current local HEAD, and deduplicates by workflow name so GitHub's "Re-run all jobs" doesn't resurface old failed run records. Also bumps the gh-run-list `--limit` from 5 to 20 to avoid truncating the latest run on busy branches with many workflows or recent pushes. Third-party checks (CodeRabbit, SonarCloud, …) and commit statuses already query by SHA and are unchanged. Resolves a wedge where a green PR could not satisfy the Stop policy because an earlier failed run on the same branch was still in the top-5 window. (#266)
- `failproofai policies --uninstall` interactive CLI selector now says "Remove Hooks" / "Choose where to remove from:" instead of "Install Hooks" / "Choose where to install:" (#236)
Expand All @@ -18,6 +20,7 @@
- Auto-translated MDX: stop the recurring `mintlify validate` parse error in `docs/de/dashboard.mdx` (`<Tab title="Tab „Richtlinien"">`) by adding a `sanitizeJsxAttributes` post-processor to the translation pipeline that strips stray ASCII `"` left after typographic-quote pairs (and any unmatched opening typographic quote) in JSX attribute values, and by tightening the translator system prompt to forbid ASCII `"` inside attribute values. Same regression PR #229 fixed by hand — now it can't recur. Includes the immediate file fix on `docs/de/dashboard.mdx`. (#247)

### Docs
- README: add Gemini CLI to the supported-CLIs intro line and visual list, with light/dark logo variants (`assets/logos/gemini-light.svg` + `gemini-dark.svg`). Restructure the logo block into two centred `<p>` rows (Claude/Codex/Copilot/Cursor on the first, OpenCode/Pi/Gemini on the second) plus a separate "+ more coming soon" line so the seventh logo doesn't crowd the layout. Update the beta callout to include Gemini CLI alongside Copilot, Cursor, OpenCode, and Pi (#277).
- README: add Pi to the supported-CLIs intro line and visual list, with light/dark logo variants (`assets/logos/pi-light.svg` + `pi-dark.svg`); update beta callout to include Pi alongside Copilot and Cursor (#264).
- README: add Cursor Agent to the supported-CLIs intro line and visual list, with light/dark logo variants (`assets/logos/cursor-light.svg` + `cursor-dark.svg`). Note that GitHub Copilot CLI testing is ongoing in the beta callout (#245).

Expand Down
59 changes: 59 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,65 @@ workaround (queue the instruction in `tool_call`, drain in the next `context`
handler by inserting a system message into `event.messages`) is feasible
but deferred until upstream Pi adds a first-class channel.

### Gemini hooks (`.gemini/settings.json`)

This repo also ships a `.gemini/settings.json` for Gemini CLI sessions, mirroring
the `.claude/settings.json`, `.codex/hooks.json`, `.github/hooks/failproofai.json`,
`.cursor/hooks.json`, and `.opencode/` setups. Gemini's hook contract is
intentionally close to Claude Code's: same `{matcher, hooks: [{type, command,
timeout}]}` matcher-wrapper schema, same PascalCase event names, same
snake_case stdin payload field names (`session_id`, `tool_name`, `tool_input`,
`hook_event_name`, `cwd`, `transcript_path`), and a subprocess execution model.

Verified empirically against gemini-cli v0.40.1 — drop a `.gemini/settings.json`
at the repo root and Gemini picks up failproofai hooks on the next session.
Settings file paths:

| Scope | Path |
|---------|-----------------------------------|
| user | `~/.gemini/settings.json` |
| project | `<cwd>/.gemini/settings.json` |
| system | `/etc/gemini-cli/settings.json` (documented but not exposed by failproofai) |

Gemini exposes both `$GEMINI_PROJECT_DIR` and `$CLAUDE_PROJECT_DIR` (alias
provided for back-compat) in the hook's environment. The dogfood config in this
repo uses `$GEMINI_PROJECT_DIR` for clarity; the existing `bun
$CLAUDE_PROJECT_DIR/...` form would also work because of the alias.

For production users (outside this repo), the recommended Gemini install is:
```bash
failproofai policies --install --cli gemini --scope project
```
which writes a portable `npx -y failproofai --hook ... --cli gemini` command.
Same self-reference caveat applies — do **not** install the standard `npx`
form from inside this repo (it would overwrite the dev `bun bin/failproofai.mjs`
path, re-fetching failproofai on every hook).

**Tool-name canonicalization.** Gemini's tools are snake_case
(`run_shell_command`, `read_file`, `read_many_files`, `write_file`, `replace`,
`glob`, `grep_search`, `list_directory`, `web_fetch`, `google_web_search`,
`write_todos`, `save_memory`, `ask_user`). The handler translates to Claude
PascalCase via `GEMINI_TOOL_MAP` so existing builtin policies (matching
`toolName === "Bash"` etc.) fire unchanged. Unknown tools (MCP `mcp_*`,
extensions, Skills) pass through unchanged.

**Per-event capability matrix** (verified against gemini-cli v0.40.1 / docs as
of 2026-04-13):

| Gemini event | Canonical | Veto / mutate? | Notes |
|------------------------|----------------------------|----------------|-------|
| `BeforeTool` | `PreToolUse` | ✅ deny | `{decision:"deny", reason}` shape; can rewrite via `hookSpecificOutput.tool_input` (not used today). |
| `AfterTool` | `PostToolUse` | observation | `additionalContext` injection supported. |
| `BeforeAgent` | `UserPromptSubmit` | ✅ deny | `{decision:"deny", reason}` + `additionalContext` injection. |
| `AfterAgent` | `Stop` | ✅ force-retry | `{decision:"block", reason}` mirrors Claude's exit-2-from-Stop "do this before stopping" semantics. Gemini's exit-2 is documented as per-action only ("turn continues"), so we use the JSON shape. |
| `SessionStart` | `SessionStart` | observation | `additionalContext` injection supported. |
| `SessionEnd` | `SessionEnd` | observation | No context-injection channel — emitted via stderr only. |
| `PreCompress` | `PreCompact` | observation | Same. |
| `Notification` | `Notification` | observation | Same. |
| `BeforeModel` | (Gemini-only, no canonical) | observation | Binary still records activity but no policies match. |
| `AfterModel` | (Gemini-only, no canonical) | observation | Same. |
| `BeforeToolSelection` | (Gemini-only, no canonical) | observation | Same. |

## Workflow rules

### One PR per branch
Expand Down
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

**Translations**: [简体中文](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)

The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously - for **Claude Code**, **OpenAI Codex**, **GitHub Copilot CLI** _(beta)_, **Cursor Agent** _(beta)_, **OpenCode** _(beta)_, **Pi** _(beta)_ & the **Agents SDK**.
The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously - for **Claude Code**, **OpenAI Codex**, **GitHub Copilot CLI** _(beta)_, **Cursor Agent** _(beta)_, **OpenCode** _(beta)_, **Pi** _(beta)_, **Gemini CLI** _(beta)_ & the **Agents SDK**.

<p align="center">
<img src="failproofai-hq.gif" alt="Failproof AI in action" width="800" />
Expand Down Expand Up @@ -50,7 +50,8 @@ The easiest way to manage policies that keep your AI agents reliable, on-task, a
<img src="assets/logos/cursor-light.svg" alt="Cursor Agent" width="64" height="64" />
</picture>
</a>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</p>
<p align="center">
<a href="https://opencode.ai/docs/plugins/" title="OpenCode">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/opencode-dark.svg" />
Expand All @@ -65,10 +66,16 @@ The easiest way to manage policies that keep your AI agents reliable, on-task, a
</picture>
</a>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<strong>+ more coming soon</strong>
<a href="https://geminicli.com/" title="Gemini CLI">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/logos/gemini-dark.svg" />
<img src="assets/logos/gemini-light.svg" alt="Gemini CLI" width="64" height="64" />
</picture>
</a>
</p>
<p align="center"><strong>+ more coming soon</strong></p>

> Install hooks for one or any combination: `failproofai policies --install --cli opencode pi` (or `--cli claude codex copilot cursor opencode pi`). Omit `--cli` to auto-detect installed CLIs and prompt. **GitHub Copilot CLI, Cursor Agent, OpenCode, and Pi support are in beta — testing is ongoing.**
> Install hooks for one or any combination: `failproofai policies --install --cli opencode pi gemini` (or `--cli claude codex copilot cursor opencode pi gemini`). Omit `--cli` to auto-detect installed CLIs and prompt. **GitHub Copilot CLI, Cursor Agent, OpenCode, Pi, and Gemini CLI support are in beta — testing is ongoing.**

- **39 Built-in Policies** - Catch common agent failure modes out of the box. Block destructive commands, prevent secret leakage, keep agents inside project boundaries, detect loops, and more.
- **Custom Policies** - Write your own reliability rules in JavaScript. Use the `allow`/`deny`/`instruct` API to enforce conventions, prevent drift, gate operations, or integrate with external systems.
Expand Down
Loading