Skip to content

feat: agent CLI expansion — Goose + OpenClaw + Factory + Devin + Antigravity (12 CLIs), remove Gemini#508

Merged
NiveditJain merged 19 commits into
mainfrom
feat/agent-cli-expansion
Jul 15, 2026
Merged

feat: agent CLI expansion — Goose + OpenClaw + Factory + Devin + Antigravity (12 CLIs), remove Gemini#508
NiveditJain merged 19 commits into
mainfrom
feat/agent-cli-expansion

Conversation

@chhhee10

Copy link
Copy Markdown
Contributor

Summary

Expands failproofai from 8 to 12 supported agent CLIs, removes the retired Gemini CLI, and hardens the audit + logging paths. Release 0.0.14-beta.1.

New integrations — all dual-pillar (live enforcement + offline audit)

  • Goose (codename goose, Block) — 12th CLI. Open Plugins shell-hook enforcement (auto-discovered plugin dir) + SQLite session audit. PreToolUse-only deny (no Stop event); verified live against goose v1.43.0.
  • OpenClaw (gateway) — in-process plugin enforcement (before_agent_finalize is a real Stop-gate) + audit; user-scope.
  • Factory (droid) — exit-2 shell-hook deny + JSONL audit.
  • Devin (Cognition) — pure Claude-clone shell hook + SQLite audit.
  • Antigravity (agy) — its own named-hook contract (camelCase payload) + JSONL audit.
  • Removed the Gemini CLI integration (Google retired it) — the ~/.gemini/ protections are kept and re-pointed at Antigravity.
  • VS Code Copilot agent mode is covered for free by the existing copilot / claude integrations (no new id).

Fixes

  • Antigravity file writes weren't gated — the tool map had guessed names (real one is write_to_file with the path under TargetFile) and no file-tool input map, so block-env-files / block-secrets-write silently allowed .env writes. Corrected + verified live.
  • Quiet Stop-hook output — informational allow-notes on channel-less events were dumped to the agent as stdout, so droid rendered a "…skipping commit check…" wall on a fine turn. Now stderr + activity only.
  • Devin transcript de-duplication — Devin stores messages as a replayed forest (26-31 nodes for a 10-message chat); the parser now reconstructs the active conversation (newest leaf → root).

Docs & dogfood

  • All 12 CLI logos in the README (light+dark, official links, 2 rows of 6); CLAUDE.md + docs/{getting-started,configuration} updated.
  • Project-scope dogfood configs for Factory / Devin / Antigravity / Goose.

Verification

  • tsc 0 · lint clean · 2,012 unit + 313 e2e · Docker clean-install smoke.
  • Live-verified against the deployed binary: all 12 CLIs block both sudo and .env writes through their real payload shapes; Goose enforcement + SQLite audit proven end-to-end.

🤖 Generated with Claude Code

chhhee10 and others added 17 commits July 14, 2026 20:08
… + audit

Adds OpenClaw as the 9th CLI/agent integration, dual-pillar like Hermes:

Pillar 1 (live enforcement) — OpenClaw's in-process plugin hooks (its
file-based "internal hooks" are observation-only and cannot block):
  - openclaw-plugin/ shipped package (manifest + plain-ESM index.js) that
    async-spawns the failproofai binary and maps a flat {permission,reason}
    verdict to each hook's native return shape (before_tool_call →
    {block:true,blockReason}; before_agent_run → {outcome:"block"};
    before_agent_finalize → {action:"revise"}).
  - OPENCLAW_EVENT_MAP / TOOL_MAP / TOOL_INPUT_MAP in types.ts; handler +
    tool-name-canonicalize + policy-evaluator branches; the openclaw
    Integration (install/uninstall manages plugins.load.paths +
    plugins.entries.failproofai in ~/.openclaw/openclaw.json); bin plumbing.

Pillar 2 (audit + dashboard) — reads the real JSONL transcripts at
~/.openclaw/agents/<agentId>/sessions/<uuid>.jsonl:
  - lib/openclaw-sessions.ts (pure type-discriminated parser, UUID-safe
    discovery) + lib/openclaw-projects.ts (sessions.json index, agentId
    grouping) + src/audit/cli-adapters/openclaw.ts.
  - Dashboard wiring: cli-registry, projects, download-session, app routes.

Verified live against openclaw v2026.7.1 (tool block confirmed end-to-end;
hook payloads, tool ids, and transcript schema captured from a real gateway).
Tests mirror the Hermes matrix. All maps are validated, not provisional.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CHANGELOG 0.0.13-beta.3 entry (Features + Docs).
- CLAUDE.md "OpenClaw hooks" section: plugin-not-shell-hook enforcement
  surface, async-spawn design, per-hook verdict mapping, the real Stop gate,
  audit layout, and the auth-profile / bundledDiscovery setup gotchas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Live testing surfaced two UX issues: OpenClaw routes gateway sessions through
the agent's default key (agent:<id>:main) and records the channel in metadata
(lastChannel / chatType / origin.{label,provider,from}), NOT in the session key.
So the prior key-parsing grouped everything under "openclaw-main" (which the
dashboard's decodeFolderName rendered as the confusing "openclaw/main") and
named sessions with the raw "agent:main:main" key.

Now group projects by channel (telegram/slack/…/local, like Hermes groups by
source) and name sessions from the human-readable origin.label — so it reads
as "openclaw / telegram" → "Chetan (@chhhee10) id:8674922496" with the chat
id/type in the gateway-metadata columns. Verified live against openclaw v2026.7.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Google retired Gemini CLI (consumer accounts stopped 2026-06-18) in favor of
the closed-source Antigravity CLI (agy), which failproofai is migrating to.

Removes the `gemini` id from INTEGRATION_TYPES, the INTEGRATIONS/ADAPTERS
registries, KNOWN_CLI_IDS, all GEMINI_* event/tool maps, the gemini audit
adapter + lib/gemini-{projects,sessions}.ts, the --cli gemini install/audit
paths (bin + docs), and the dashboard's Gemini session provider. Deletes the
gemini logo assets and the .gemini dogfood config.

Keeps the ~/.gemini/ config-file protections in the block-* builtins and
re-points them at Antigravity (which reuses that directory). Fixes a latent
bug where OpenClaw sessions without a cwd were mislabeled "Gemini CLI" in the
session viewer. Docs + all 14 locale mirrors swept; tsc/build/tests green
(111 files, 1916 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9th CLI integration. Factory droid is a Claude-shaped external-shell-hook CLI,
but with two contract quirks that were live-verified against droid v0.171.0
(and differ from Factory's published docs):

- hooks.json uses event names at the TOP LEVEL (no `{"hooks":{...}}` wrapper —
  droid rejects the wrapper as unknown keys). Tool events carry matcher:"*".
- deny is driven by EXIT CODE 2 + stderr, not a JSON decision (droid ignores
  the JSON decision field). Stop uses `{decision:"block"}` for require-*-before-stop.

Payload is Claude snake_case (no normalization); FACTORY_TOOL_MAP maps
Execute→Bash, Create→Write, etc. Config at ~/.factory/hooks.json (user) /
.factory/hooks.json (project); binary probe `droid`. Audit reads the JSONL
transcripts at ~/.factory/sessions/<enc-cwd>/<id>.jsonl (Claude-style adapter).

Wires all surfaces: types union + maps, Integration + registry, canonicalize,
policy-evaluator deny/instruct branches, audit adapter + lib providers,
cli-registry, projects/download-session, app pages, bin VALID_CLIS + help.
Adds unit + e2e + canonicalize tests, CLAUDE.md section, CHANGELOG, docs.
tsc/lint clean; 1932 unit + 289 e2e tests pass; build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10th CLI integration. Devin CLI is a pure Claude-clone for hooks — same
snake_case payloads (hook_event_name, tool_name, tool_input, tool_use_id),
so the Integration is modeled verbatim on claudeCode with a "hooks" key in
~/.config/devin/config.json (user) / .devin/config.json (project). Verified
live against devin v3000.1.27: deny via {"decision":"block","reason"} on stdout
blocks the tool (and overrides --permission-mode dangerous). DEVIN_TOOL_MAP
maps exec→Bash (tool_input.command already canonical); no payload/event maps.

Audit reads the SQLite store at ~/.local/share/devin/cli/sessions.db
(sessions.working_directory → per-project grouping, message_nodes.chat_message
JSON). Devin's tool_calls use a flat {id,name,arguments-as-object} shape (not
the OpenAI wire form), so lib/devin-sessions.ts uses a Devin-specific
normalizer; created_at prefers the higher-precision metadata ISO string.

All 16 surfaces wired; unit + e2e + SQLite-parser tests added; CLAUDE.md
section, CHANGELOG, docs. tsc/lint clean; 1956 unit + 297 e2e pass; build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
11th CLI integration. Antigravity CLI (`agy`, Google's closed-source Go
replacement for the retired Gemini CLI) has its OWN hook contract — NOT a
Claude clone. Live-verified against agy v1.1.2.

- Named-hook schema: hooks.json = {"failproofai": {"PreToolUse":[{matcher,hooks}],
  "PreInvocation":[{...}], "Stop":[{...}]}} at ~/.gemini/config/hooks.json (user)
  / .agents/hooks.json (project). Tool events use a {matcher,hooks} wrapper;
  PreInvocation/Stop are flat handler arrays.
- camelCase protojson payload → the handler normalizes toolCall.name→tool_name,
  toolCall.args→tool_input, conversationId→session_id, workspacePaths[0]→cwd,
  transcriptPath→transcript_path before the usual canonicalization.
- Own response shapes: PreToolUse deny → {decision:"deny",reason}; Stop →
  {decision:"continue",reason} (re-enters the loop, so require-*-before-stop
  enforce); instruct → PreInvocation {injectSteps:[{ephemeralMessage}]}.
- ANTIGRAVITY_TOOL_MAP (run_command→Bash, view_file→Read, ...) +
  ANTIGRAVITY_TOOL_INPUT_MAP (Bash: CommandLine→command, Cwd→cwd).
- Audit reads plain JSONL transcript_full.jsonl under
  ~/.gemini/antigravity-cli/brain/<id>/.system_generated/logs/, indexed by
  conversation_summaries.db (falls back to brain/ dir scan when the index is empty).

Keeps the ~/.gemini/ path guards (relabeled for Antigravity in the Gemini
removal). All 16 surfaces wired; unit + e2e + parser tests; CLAUDE.md section,
CHANGELOG, docs. tsc/lint clean; 1976 unit + 306 e2e pass; build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rage

Two documentation gaps closed:

1. OpenClaw was shipped as a fully-wired integration but was never added to the
   user-facing supported-CLI lists. Backfills it into README + docs
   (configuration / getting-started / introduction): a dual-pillar bullet after
   Hermes, the --cli lists, install examples, and the detection list.

2. Documents that VS Code's Copilot Chat agent mode (Preview) needs no dedicated
   integration — its agent hooks load from .github/hooks/*.json,
   ~/.copilot/hooks/*.json, and ~/.claude/settings.json (the exact paths the
   copilot/claude integrations already write), verified live against VS Code
   1.127 / github.copilot-chat 0.55.0. So `--cli copilot` (or --cli claude)
   already enforces in VS Code agent-mode sessions. Adds a CLAUDE.md section +
   a docs note.

No code change. tsc/build/tests green (115 files, 1976 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an explicit `case "openclaw"` (functionally a no-op that returns undefined,
same as the default) documenting that OpenClaw's transcript_path comes from its
plugin shim's stdin and its audit reads sessions directly via
lib/openclaw-sessions.ts. Makes the switch cover all 11 CLIs explicitly so the
wiring is unambiguous for future integrations. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ents

Replace stale `(line ~N)` / `at line ~N` comment cross-references (which went
out of date when the Gemini branches were removed and shift on every edit) with
descriptive references to the mirrored per-CLI branches. Comment-only; no
behavior change. Avoids automated-review flags on outdated line numbers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dual-pillar like Hermes/OpenClaw/Factory/Devin/Antigravity — real-time policy
enforcement + offline audit. Enforcement uses Goose's "hooks" system (the
cross-agent Open Plugins spec): the installer drops an auto-discovered plugin
dir at ~/.agents/plugins/failproofai/hooks/hooks.json (user/project). Deny is
{"decision":"block"} JSON on PreToolUse only (goose >= v1.37.0); goose has no
Stop event, so require-*-before-stop is inapplicable (like Hermes). Audit reads
the SQLite sessions.db (cwd-grouped like Devin; Claude-style content_json).
Verified live end-to-end against goose v1.43.0 (real sudo blocked; real
sessions.db read). Follows the Factory (enforcement) + Devin (SQLite audit)
templates.

Also backfills the README supported-CLI logo grid (openclaw/factory/devin/
antigravity/goose were missing) with dark/light variants, and documents Goose
across CLAUDE.md + docs/{getting-started,configuration}.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Point the 9 logos the maintainer supplied URLs for at their official pages
(codex→learn.chatgpt.com, copilot→github.com/features/copilot/cli, cursor,
opencode, pi, hermes, openclaw, factory, goose→goose-docs.ai); claude/devin/
antigravity keep their existing links.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…low-notes

Two issues surfaced while testing every CLI:

1. Antigravity file writes weren't gated. ANTIGRAVITY_TOOL_MAP had guessed tool
   names (`write_file`/`list_directory`/`find_filepath`) — the real ones (from
   the agy binary + live transcripts) are `write_to_file`/`list_dir`/
   `find_by_name`, and there was no input-key map for file tools, so the path
   (delivered as `TargetFile`) never became `file_path`. Result: block-env-files
   / block-secrets-write silently allowed `.env` writes on Antigravity. Corrected
   the tool names and added Write/Edit/Read input maps (TargetFile→file_path,
   CodeContent→content). Verified live: agy now denies a `.env` write.

2. Noisy Stop-hook output. Informational allow-notes were emitted as stdout
   `{reason}` on events with no agent-facing context channel (Stop, SubagentStop,
   Session*, …), so a passing Stop rendered as a "…skipping commit check…skipping
   PR check…" wall (visible e.g. in droid). Those notes now go to stderr + the
   activity store only; stdout stays clean on channel-less events.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…forest)

Devin stores a session's messages as a FOREST (node_id/parent_node_id) and
replays earlier context under fresh roots on later turns, so message_nodes holds
many branches that repeat the same messages — verified live: 26-31 raw nodes for
8-10 real messages. The parser read every node in node_id order, so the dashboard
rendered each message 2-4× (a wall of duplicated "Create a file named .env…").

Reconstruct the real conversation instead: walk parent_node_id from the newest
leaf (max node_id) to its root and reverse. Devin sessions now render the true
8-10 message conversation. Adds devinActiveConversationPath + unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CHANGELOG: add the missing Fixes section (Antigravity file-tool map, quiet
  channel-less allow-notes, Devin forest de-dup) and a Docs entry for the README
  logo grid.
- Dogfood: ship project-scope configs for Factory (.factory/hooks.json), Devin
  (.devin/config.json), Antigravity (.agents/hooks.json), and Goose
  (.agents/plugins/failproofai/hooks/hooks.json) using the dev
  `bun bin/failproofai.mjs` command — generated from each integration's own
  writeHookEntries so they track the live schema. Documented in CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
main shipped 0.0.13 stable (#506) without this CLI-expansion work, so it releases
as 0.0.14-beta.1. Moves the branch's CHANGELOG entries into a fresh 0.0.14-beta.1
section above the released 0.0.13.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 187 files, which is 37 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5d590446-f316-42b7-8f41-511ce27d667a

📥 Commits

Reviewing files that changed from the base of the PR and between 94900f6 and 9e280c1.

⛔ Files ignored due to path filters (9)
  • assets/logos/antigravity.svg is excluded by !**/*.svg
  • assets/logos/devin.svg is excluded by !**/*.svg
  • assets/logos/factory-dark.png is excluded by !**/*.png
  • assets/logos/factory-light.png is excluded by !**/*.png
  • assets/logos/gemini-dark.svg is excluded by !**/*.svg
  • assets/logos/gemini-light.svg is excluded by !**/*.svg
  • assets/logos/goose-dark.svg is excluded by !**/*.svg
  • assets/logos/goose-light.svg is excluded by !**/*.svg
  • assets/logos/openclaw.svg is excluded by !**/*.svg
📒 Files selected for processing (187)
  • .agents/hooks.json
  • .agents/plugins/failproofai/hooks/hooks.json
  • .devin/config.json
  • .factory/hooks.json
  • .gemini/settings.json
  • CHANGELOG.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • README.md
  • __tests__/actions/get-hooks-config.test.ts
  • __tests__/components/project-list.test.tsx
  • __tests__/e2e/helpers/hook-runner.ts
  • __tests__/e2e/helpers/payloads.ts
  • __tests__/e2e/hooks/antigravity-integration.e2e.test.ts
  • __tests__/e2e/hooks/devin-integration.e2e.test.ts
  • __tests__/e2e/hooks/factory-integration.e2e.test.ts
  • __tests__/e2e/hooks/gemini-integration.e2e.test.ts
  • __tests__/e2e/hooks/goose-integration.e2e.test.ts
  • __tests__/hooks/antigravity-canonicalize.test.ts
  • __tests__/hooks/devin-canonicalize.test.ts
  • __tests__/hooks/factory-canonicalize.test.ts
  • __tests__/hooks/goose-canonicalize.test.ts
  • __tests__/hooks/handler.test.ts
  • __tests__/hooks/install-prompt.test.ts
  • __tests__/hooks/integrations.test.ts
  • __tests__/hooks/openclaw-canonicalize.test.ts
  • __tests__/hooks/policy-evaluator.test.ts
  • __tests__/hooks/resolve-cwd.test.ts
  • __tests__/hooks/resolve-transcript-path.test.ts
  • __tests__/lib/cli-registry.test.ts
  • __tests__/lib/devin-sessions.test.ts
  • __tests__/lib/download-session.test.ts
  • __tests__/lib/gemini-projects.test.ts
  • __tests__/lib/goose-sessions.test.ts
  • __tests__/lib/openclaw-projects.test.ts
  • __tests__/lib/openclaw-sessions.test.ts
  • __tests__/lib/projects.test.ts
  • app/audit/_components/empty-state.tsx
  • app/policies/hooks-client.tsx
  • app/project/[name]/page.tsx
  • app/project/[name]/session/[sessionId]/page.tsx
  • bin/failproofai.mjs
  • docs/ar/built-in-policies.mdx
  • docs/ar/cli/audit.mdx
  • docs/ar/configuration.mdx
  • docs/ar/dashboard.mdx
  • docs/ar/getting-started.mdx
  • docs/ar/introduction.mdx
  • docs/built-in-policies.mdx
  • docs/cli/audit.mdx
  • docs/configuration.mdx
  • docs/dashboard.mdx
  • docs/de/built-in-policies.mdx
  • docs/de/cli/audit.mdx
  • docs/de/configuration.mdx
  • docs/de/dashboard.mdx
  • docs/de/getting-started.mdx
  • docs/de/introduction.mdx
  • docs/es/built-in-policies.mdx
  • docs/es/cli/audit.mdx
  • docs/es/configuration.mdx
  • docs/es/dashboard.mdx
  • docs/es/getting-started.mdx
  • docs/es/introduction.mdx
  • docs/fr/built-in-policies.mdx
  • docs/fr/cli/audit.mdx
  • docs/fr/configuration.mdx
  • docs/fr/dashboard.mdx
  • docs/fr/getting-started.mdx
  • docs/fr/introduction.mdx
  • docs/getting-started.mdx
  • docs/he/built-in-policies.mdx
  • docs/he/cli/audit.mdx
  • docs/he/configuration.mdx
  • docs/he/dashboard.mdx
  • docs/he/getting-started.mdx
  • docs/he/introduction.mdx
  • docs/hi/built-in-policies.mdx
  • docs/hi/cli/audit.mdx
  • docs/hi/configuration.mdx
  • docs/hi/dashboard.mdx
  • docs/hi/getting-started.mdx
  • docs/hi/introduction.mdx
  • docs/i18n/README.ar.md
  • docs/i18n/README.de.md
  • docs/i18n/README.es.md
  • docs/i18n/README.fr.md
  • docs/i18n/README.he.md
  • docs/i18n/README.hi.md
  • docs/i18n/README.it.md
  • docs/i18n/README.ja.md
  • docs/i18n/README.ko.md
  • docs/i18n/README.pt-br.md
  • docs/i18n/README.ru.md
  • docs/i18n/README.tr.md
  • docs/i18n/README.vi.md
  • docs/i18n/README.zh.md
  • docs/introduction.mdx
  • docs/it/built-in-policies.mdx
  • docs/it/cli/audit.mdx
  • docs/it/configuration.mdx
  • docs/it/dashboard.mdx
  • docs/it/getting-started.mdx
  • docs/it/introduction.mdx
  • docs/ja/built-in-policies.mdx
  • docs/ja/cli/audit.mdx
  • docs/ja/configuration.mdx
  • docs/ja/dashboard.mdx
  • docs/ja/getting-started.mdx
  • docs/ja/introduction.mdx
  • docs/ko/built-in-policies.mdx
  • docs/ko/cli/audit.mdx
  • docs/ko/configuration.mdx
  • docs/ko/dashboard.mdx
  • docs/ko/getting-started.mdx
  • docs/ko/introduction.mdx
  • docs/pt-br/built-in-policies.mdx
  • docs/pt-br/cli/audit.mdx
  • docs/pt-br/configuration.mdx
  • docs/pt-br/dashboard.mdx
  • docs/pt-br/getting-started.mdx
  • docs/pt-br/introduction.mdx
  • docs/ru/built-in-policies.mdx
  • docs/ru/cli/audit.mdx
  • docs/ru/configuration.mdx
  • docs/ru/dashboard.mdx
  • docs/ru/getting-started.mdx
  • docs/ru/introduction.mdx
  • docs/styles/config/vocabularies/Mintlify/accept.txt
  • docs/tr/built-in-policies.mdx
  • docs/tr/cli/audit.mdx
  • docs/tr/configuration.mdx
  • docs/tr/dashboard.mdx
  • docs/tr/getting-started.mdx
  • docs/tr/introduction.mdx
  • docs/vi/built-in-policies.mdx
  • docs/vi/cli/audit.mdx
  • docs/vi/configuration.mdx
  • docs/vi/dashboard.mdx
  • docs/vi/getting-started.mdx
  • docs/vi/introduction.mdx
  • docs/zh/built-in-policies.mdx
  • docs/zh/cli/audit.mdx
  • docs/zh/configuration.mdx
  • docs/zh/dashboard.mdx
  • docs/zh/getting-started.mdx
  • docs/zh/introduction.mdx
  • lib/antigravity-projects.ts
  • lib/antigravity-sessions.ts
  • lib/cli-registry.ts
  • lib/devin-projects.ts
  • lib/devin-sessions.ts
  • lib/download-session.ts
  • lib/factory-projects.ts
  • lib/factory-sessions.ts
  • lib/gemini-projects.ts
  • lib/gemini-sessions.ts
  • lib/goose-projects.ts
  • lib/goose-sessions.ts
  • lib/openclaw-projects.ts
  • lib/openclaw-sessions.ts
  • lib/projects.ts
  • openclaw-plugin/index.js
  • openclaw-plugin/openclaw.plugin.json
  • openclaw-plugin/package.json
  • package.json
  • scripts/sync-agent-cli-harnesses-prompt.md
  • src/audit/cli-adapters/antigravity.ts
  • src/audit/cli-adapters/devin.ts
  • src/audit/cli-adapters/factory.ts
  • src/audit/cli-adapters/gemini.ts
  • src/audit/cli-adapters/goose.ts
  • src/audit/cli-adapters/index.ts
  • src/audit/cli-adapters/openclaw.ts
  • src/audit/cli.ts
  • src/audit/report.ts
  • src/audit/types.ts
  • src/hooks/builtin-policies.ts
  • src/hooks/handler.ts
  • src/hooks/install-prompt.ts
  • src/hooks/integrations.ts
  • src/hooks/policy-evaluator.ts
  • src/hooks/resolve-cwd.ts
  • src/hooks/resolve-permission-mode.ts
  • src/hooks/resolve-transcript-path.ts
  • src/hooks/tool-name-canonicalize.ts
  • src/hooks/types.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Automated code review started — analyzing 196 files (+8,513/−3,247)...

⏱️ This may take a few minutes. Results will be posted here when complete.

@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Automated code review started — analyzing [{"additions":46,"deletions":0,"path":".agents/hooks.json"},{"additions":54,"deletions":0,"path":".agents/plugins/failproofai/hooks/hooks.json"},{"additions":88,"deletions":0,"path":".devin/config.json"},{"additions":112,"deletions":0,"path":".factory/hooks.json"},{"additions":0,"deletions":147,"path":".gemini/settings.json"},{"additions":26,"deletions":0,"path":"CHANGELOG.md"},{"additions":417,"deletions":60,"path":"CLAUDE.md"},{"additions":1,"deletions":1,"path":"CONTRIBUTING.md"},{"additions":49,"deletions":20,"path":"README.md"},{"additions":4,"deletions":10,"path":"tests/actions/get-hooks-config.test.ts"},{"additions":5,"deletions":1,"path":"tests/components/project-list.test.tsx"},{"additions":74,"deletions":21,"path":"tests/e2e/helpers/hook-runner.ts"},{"additions":222,"deletions":101,"path":"tests/e2e/helpers/payloads.ts"},{"additions":234,"deletions":0,"path":"tests/e2e/hooks/antigravity-integration.e2e.test.ts"},{"additions":208,"deletions":0,"path":"tests/e2e/hooks/devin-integration.e2e.test.ts"},{"additions":223,"deletions":0,"path":"tests/e2e/hooks/factory-integration.e2e.test.ts"},{"additions":0,"deletions":379,"path":"tests/e2e/hooks/gemini-integration.e2e.test.ts"},{"additions":174,"deletions":0,"path":"tests/e2e/hooks/goose-integration.e2e.test.ts"},{"additions":107,"deletions":0,"path":"tests/hooks/antigravity-canonicalize.test.ts"},{"additions":51,"deletions":0,"path":"tests/hooks/devin-canonicalize.test.ts"},{"additions":63,"deletions":0,"path":"tests/hooks/factory-canonicalize.test.ts"},{"additions":102,"deletions":0,"path":"tests/hooks/goose-canonicalize.test.ts"},{"additions":1,"deletions":174,"path":"tests/hooks/handler.test.ts"},{"additions":12,"deletions":8,"path":"tests/hooks/install-prompt.test.ts"},{"additions":504,"deletions":163,"path":"tests/hooks/integrations.test.ts"},{"additions":75,"deletions":0,"path":"tests/hooks/openclaw-canonicalize.test.ts"},{"additions":63,"deletions":308,"path":"tests/hooks/policy-evaluator.test.ts"},{"additions":0,"deletions":1,"path":"tests/hooks/resolve-cwd.test.ts"},{"additions":0,"deletions":13,"path":"tests/hooks/resolve-transcript-path.test.ts"},{"additions":5,"deletions":6,"path":"tests/lib/cli-registry.test.ts"},{"additions":118,"deletions":0,"path":"tests/lib/devin-sessions.test.ts"},{"additions":1,"deletions":4,"path":"tests/lib/download-session.test.ts"},{"additions":0,"deletions":113,"path":"tests/lib/gemini-projects.test.ts"},{"additions":131,"deletions":0,"path":"tests/lib/goose-sessions.test.ts"},{"additions":104,"deletions":0,"path":"tests/lib/openclaw-projects.test.ts"},{"additions":124,"deletions":0,"path":"tests/lib/openclaw-sessions.test.ts"},{"additions":6,"deletions":64,"path":"tests/lib/projects.test.ts"},{"additions":1,"deletions":1,"path":"app/audit/_components/empty-state.tsx"},{"additions":2,"deletions":4,"path":"app/policies/hooks-client.tsx"},{"additions":30,"deletions":10,"path":"app/project/[name]/page.tsx"},{"additions":71,"deletions":21,"path":"app/project/[name]/session/[sessionId]/page.tsx"},{"additions":1,"deletions":0,"path":"assets/logos/antigravity.svg"},{"additions":1,"deletions":0,"path":"assets/logos/devin.svg"},{"additions":0,"deletions":0,"path":"assets/logos/factory-dark.png"},{"additions":0,"deletions":0,"path":"assets/logos/factory-light.png"},{"additions":0,"deletions":13,"path":"assets/logos/gemini-dark.svg"},{"additions":0,"deletions":13,"path":"assets/logos/gemini-light.svg"},{"additions":1,"deletions":0,"path":"assets/logos/goose-dark.svg"},{"additions":1,"deletions":0,"path":"assets/logos/goose-light.svg"},{"additions":22,"deletions":0,"path":"assets/logos/openclaw.svg"},{"additions":27,"deletions":22,"path":"bin/failproofai.mjs"},{"additions":4,"deletions":5,"path":"docs/ar/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/ar/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/ar/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/ar/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/ar/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/ar/introduction.mdx"},{"additions":4,"deletions":5,"path":"docs/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/cli/audit.mdx"},{"additions":15,"deletions":7,"path":"docs/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/dashboard.mdx"},{"additions":4,"deletions":5,"path":"docs/de/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/de/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/de/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/de/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/de/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/de/introduction.mdx"},{"additions":4,"deletions":5,"path":"docs/es/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/es/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/es/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/es/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/es/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/es/introduction.mdx"},{"additions":4,"deletions":5,"path":"docs/fr/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/fr/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/fr/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/fr/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/fr/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/fr/introduction.mdx"},{"additions":7,"deletions":3,"path":"docs/getting-started.mdx"},{"additions":4,"deletions":5,"path":"docs/he/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/he/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/he/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/he/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/he/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/he/introduction.mdx"},{"additions":4,"deletions":5,"path":"docs/hi/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/hi/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/hi/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/hi/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/hi/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/hi/introduction.mdx"},{"additions":1,"deletions":8,"path":"docs/i18n/README.ar.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.de.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.es.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.fr.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.he.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.hi.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.it.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.ja.md"}] files, +8513/-3247...

⏱️ This may take a few minutes. Results will be posted here when complete.

@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Automated Code Review

📋 Executive Summary

This PR expands failproofai from 8 to 12 supported agent CLIs by adding Goose, OpenClaw, Factory (droid), Devin, and Antigravity integrations — each with dual-pillar support (live hook enforcement + offline audit). It also removes the retired Gemini CLI, fixes Antigravity file-write gating (critical), and adds proper Devin transcript de-duplication. Code quality is excellent — clean, well-documented, with thorough test coverage. All 2,012 unit tests and 307 e2e tests pass green.


📊 Change Architecture

graph TD
    A[8 CLIs] -->|"+5 new"| B[12 CLIs — INTEGRATION_TYPES]
    B --> C[types.ts: 5 new tool maps + input maps]
    B --> D[integrations.ts: 5 new Integration objects]
    C --> E[tool-name-canonicalize.ts: 5 CLI branches]
    C --> F[handler.ts: antigravity/goose normalization]
    D --> G[policy-evaluator.ts: antigravity/goose response shapes]
    H[Gemini CLI] -->|"removed"| I[Antigravity — Gemini's successor]
    I --> J["write_to_file gating (was broken)"]
    J --> K["block-env-files / block-secrets-write now work"]
    L[Devin forest of nodes] -->|"de-duplication"| M[devinActiveConversationPath]
    
    style B fill:#90EE90
    style J fill:#FF6347
    style M fill:#87CEEB
    style H fill:#FFD700
Loading

Legend: 🟢 New | 🔵 Modified | 🟡 Removal | 🔴 Critical Fix


🔴 Breaking Changes

Gemini CLI removal — INTEGRATION_TYPES drops "gemini". The gemini CLI id is no longer recognized; any callers passing --cli gemini will get an "unknown CLI" response. The ~/.gemini/ directory is now managed by Antigravity. ✅ This is intentional, documented, and the old geminiSettings, GEMINI_EVENT_MAP, GEMINI_TOOL_MAP and related types are fully purged from source.

✅ No other breaking changes — all public API surfaces (types, exports, CLI flags) remain backward-compatible. The SessionMetadata.cli type widens from 8 to 12 values, which is additive.


⚠️ Issues Found

All clear — no issues requiring changes. The code is clean with zero logic errors, race conditions, or missing error handling found.


🔬 Logical / Bug Analysis

Antigravity file-write fix (critical): Previously, the tool map used guessed names (write_file, read_many_files, replace, glob, list_directory, web_fetch, google_web_search). The real Antigravity tool names are completely different — write_to_file, view_file, edit_file, replace_file_content, list_dir, find_by_name, read_url_content, search_web. Without this fix, block-env-files and block-secrets-write silently allowed sensitive file writes on Antigravity. ✅ Fixed with live-verified tool names + ANTIGRAVITY_TOOL_INPUT_MAP mapping TargetFilefile_path.

Devin transcript de-duplication: Devin stores messages as a replayed forest (26-31 nodes for a 10-message chat). The devinActiveConversationPath() function reconstructs the active conversation by walking from the newest leaf (max node_id) back to root via parent_node_id chain, with cycle detection (seen Set). ✅ Clean algorithm — correctly drops branch duplicates. The devinRowsToLogEntries() pure function is unit-testable without a DB.

Goose tool name handling: Goose uses BOTH bare tool names (shell, write, edit, view) AND <ext>__<tool> namespaced names (todo__todo_write). GOOSE_TOOL_MAP covers both. GOOSE_TOOL_INPUT_MAP maps path/sourcefile_path so path builtins fire correctly. ✅ Verified live against goose v1.43.0.

Factory (droid): Exit-2 shell-hook deny + JSONL audit. The deny is on exit code 2 (no JSON response channel), and its tool_input.command is already canonical — zero normalization needed (no FACTORY_TOOL_INPUT_MAP). ✅ Clean.

OpenClaw: In-process plugin enforcement via before_agent_finalize (real Stop-gate). User-scope only. Plugin installation is idempotent — writeHookEntries preserves other plugin entries and only touches the failproofai-specific path. ✅ Solid.

Quiet stop-hook output fix: Channel-less allow-notes were dumped to stdout ("…skipping commit check…" wall). Now goes to stderr + activity only. Verified in the resolve-permission-mode commit.


🧪 Evidence — Build & Test Results

Lint
0 errors, 5 warnings (pre-existing <img> warnings, not from this PR)
Type Check (tsc --noEmit)
0 errors
Unit Tests — 117 files, 2,012 tests
✓ goose-canonicalize.test.ts (10 tests)
✓ factory-canonicalize.test.ts (5 tests)
✓ devin-canonicalize.test.ts (5 tests)
✓ antigravity-canonicalize.test.ts (new)
✓ openclaw-canonicalize.test.ts (7 tests)
✓ integrations.test.ts (+504/-163)
✓ devin-sessions.test.ts (new)
✓ goose-sessions.test.ts (new)
✓ openclaw-sessions.test.ts (new)
✓ openclaw-projects.test.ts (new)
... 117 test files, 2012 tests — ALL PASSED
E2E Tests — 15 files, 307 tests (6 skipped)
✓ goose-integration.e2e.test.ts (7 tests)
✓ factory-integration.e2e.test.ts (9 tests)
✓ devin-integration.e2e.test.ts (8 tests)
✓ antigravity-integration.e2e.test.ts (9 tests)
✓ config-scopes.e2e.test.ts (11 tests)
... 15 passed, 1 skipped, 307/313 tests passed

🔗 Issue Linkage

⚠️ No issue linked. Consider creating one for better traceability — this is a significant feature expansion.


👥 Human Review Feedback

No human review feedback on this PR. (Coderabbit skipped due to file count; no prior review threads exist.)


💡 Suggestions

  1. 💡 Factory deny contract documentationsrc/hooks/types.ts: The FACTORY_TOOL_MAP comment says "exit-2 shell-hook deny." Consider adding a brief note that Factory (unlike Goose/Antigravity) has no JSON deny channel — it relies solely on exit code 2, which means a deny on Factory cannot carry a reason string in the response. This is already handled correctly in the code but the comment could help future contributors understand the difference.

  2. 💡 devinActiveConversationPath edge caselib/devin-sessions.ts:273: The "newest leaf = highest node_id" assumption relies on node_id monotonicity (child > parent). If Devin ever introduces parallel sessions in the same DB or reseeds IDs, this could pick the wrong leaf. Consider adding a timestamp tiebreaker if multiple nodes share the max node_id with no children. (Low priority — verified correct with current Devin schema.)

  3. 💡 CONTRIBUTING.md — The diff shows a single character change (+1/−1). If this is just a whitespace fix, it's fine, but verify it wasn't an accidental edit.


🏆 Verdict

APPROVED — No issues found. Ready to merge.

Summary: 8,513 additions, 3,247 deletions across 196 files. Five new CLI integrations (Goose, OpenClaw, Factory, Devin, Antigravity), one removal (Gemini), one critical bug fix (Antigravity file-write gating), clean Devin transcript de-duplication, solid test coverage (2,012 unit + 307 e2e all green), clean lint + typecheck. Well-structured, well-documented, no breaking API changes beyond the intentional Gemini removal.


Automated code review · 2026-07-14 20:42 UTC

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: No issues found. Code is clean with excellent test coverage (2,012 unit + 307 e2e all green). ✅

@chhhee10
chhhee10 requested a review from NiveditJain July 14, 2026 15:20
@hermes-exosphere

Copy link
Copy Markdown
Contributor

📋 Additional Findings from Deep Analysis

Three sub-agents performed thorough code review of the core hooks changes, new CLI integration code, and test/docs consistency. Here are the supplemental findings:

🟡 Minor Issues (non-blocking)

  1. Goose isGooseFailproofaiHook uses substring match for ownership (src/hooks/integrations.ts)

    • Goose identifies hooks by checking if command includes both "failproofai" and "--cli goose". This is the ONLY integration that uses a substring match — all others use the __failproofai_hook__ marker field.
    • Risk: If a user has a non-failproofai hook whose command coincidentally contains both strings (e.g., ~/bin/goose-failproofai-checker --cli goose-report), the install/uninstall code would incorrectly identify it as failproofai-owned.
    • Severity: Low. Goose's Open Plugins schema (from Block) may not support arbitrary marker fields. This is documented as a known limitation in the Goose integration comments.
  2. removeHooksFromFile writes to disk even when nothing was removed (Factory/Antigravity integrations)

    • Factory and Antigravity's removeHooksFromFile call this.writeSettings() even when removed === 0. This is an idempotent no-op for content, but it updates file mtime, which could trigger unnecessary file watchers.
    • Compare with hermes.removeHooksFromFile which has an if (removed > 0) guard. Goose has the same pattern.
    • Severity: Very low (cosmetic — no data loss or corruption).
  3. OpenClaw removeHooksFromFile — no file existence check (src/hooks/integrations.ts)

    • readJsonFile throws if the file doesn't exist. If ~/.openclaw/openclaw.json is deleted between detection and removal, uninstall will throw rather than returning 0.
    • Severity: Low — install creates the file first, and uninstall is only called after successful detection. A defensive existsSync guard would be nice.
  4. README.md tagline is outdated (L15)

    • Still reads "Hooks into Claude Code and Codex" — doesn't mention the 10 other CLIs. The logo grid immediately below makes it obvious, but the tagline undersells the product.
  5. docs/dashboard.mdx Projects page summary (L27)

    • Only mentions the first 7 CLIs (Claude, Codex, Copilot, Cursor, OpenCode, Pi, Hermes) — the 5 new CLIs (OpenClaw, Factory, Devin, Antigravity, Goose) are missing from the summary sentence, though they are listed in the session viewer and activity tab.

✅ Confirmed Green

  • All 12 CLI integrations have correct tool-name canonicalization, event maps, and response shapes
  • All 12 CLIs have matching audit adapters (src/audit/cli-adapters/)
  • Gemini CLI removal is complete — zero dead references in source
  • resolveCwd dispatch order is correct (Goose/Antigravity normalize before resolveCwd runs)
  • All Integration objects implement the complete interface with correct scopes, event types, and buildHookEntry/hooksInstalledInSchema lifecycle methods
  • Test coverage is excellent: 4 new canonicalize test files (323 lines), 4 new e2e test files (839 lines), comprehensive integration unit tests
  • E2E tests cover full install→fire→decide flow for each new CLI, including deny shape verification, Stop handling, and uninstall round-trips

The dashboard project-list description, session-viewer/activity badge lists, the
?cli= filter, and the auto-detect list only covered the original 7 CLIs. Add the
five newer ones (OpenClaw, Factory Droid, Devin, Antigravity, Goose) with their
session-store paths and badge colors. Caught by the PR #508 automated review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Automated code review started — analyzing [{"additions":46,"deletions":0,"path":".agents/hooks.json"},{"additions":54,"deletions":0,"path":".agents/plugins/failproofai/hooks/hooks.json"},{"additions":88,"deletions":0,"path":".devin/config.json"},{"additions":112,"deletions":0,"path":".factory/hooks.json"},{"additions":0,"deletions":147,"path":".gemini/settings.json"},{"additions":26,"deletions":0,"path":"CHANGELOG.md"},{"additions":417,"deletions":60,"path":"CLAUDE.md"},{"additions":1,"deletions":1,"path":"CONTRIBUTING.md"},{"additions":49,"deletions":20,"path":"README.md"},{"additions":4,"deletions":10,"path":"tests/actions/get-hooks-config.test.ts"},{"additions":5,"deletions":1,"path":"tests/components/project-list.test.tsx"},{"additions":74,"deletions":21,"path":"tests/e2e/helpers/hook-runner.ts"},{"additions":222,"deletions":101,"path":"tests/e2e/helpers/payloads.ts"},{"additions":234,"deletions":0,"path":"tests/e2e/hooks/antigravity-integration.e2e.test.ts"},{"additions":208,"deletions":0,"path":"tests/e2e/hooks/devin-integration.e2e.test.ts"},{"additions":223,"deletions":0,"path":"tests/e2e/hooks/factory-integration.e2e.test.ts"},{"additions":0,"deletions":379,"path":"tests/e2e/hooks/gemini-integration.e2e.test.ts"},{"additions":174,"deletions":0,"path":"tests/e2e/hooks/goose-integration.e2e.test.ts"},{"additions":107,"deletions":0,"path":"tests/hooks/antigravity-canonicalize.test.ts"},{"additions":51,"deletions":0,"path":"tests/hooks/devin-canonicalize.test.ts"},{"additions":63,"deletions":0,"path":"tests/hooks/factory-canonicalize.test.ts"},{"additions":102,"deletions":0,"path":"tests/hooks/goose-canonicalize.test.ts"},{"additions":1,"deletions":174,"path":"tests/hooks/handler.test.ts"},{"additions":12,"deletions":8,"path":"tests/hooks/install-prompt.test.ts"},{"additions":504,"deletions":163,"path":"tests/hooks/integrations.test.ts"},{"additions":75,"deletions":0,"path":"tests/hooks/openclaw-canonicalize.test.ts"},{"additions":63,"deletions":308,"path":"tests/hooks/policy-evaluator.test.ts"},{"additions":0,"deletions":1,"path":"tests/hooks/resolve-cwd.test.ts"},{"additions":0,"deletions":13,"path":"tests/hooks/resolve-transcript-path.test.ts"},{"additions":5,"deletions":6,"path":"tests/lib/cli-registry.test.ts"},{"additions":118,"deletions":0,"path":"tests/lib/devin-sessions.test.ts"},{"additions":1,"deletions":4,"path":"tests/lib/download-session.test.ts"},{"additions":0,"deletions":113,"path":"tests/lib/gemini-projects.test.ts"},{"additions":131,"deletions":0,"path":"tests/lib/goose-sessions.test.ts"},{"additions":104,"deletions":0,"path":"tests/lib/openclaw-projects.test.ts"},{"additions":124,"deletions":0,"path":"tests/lib/openclaw-sessions.test.ts"},{"additions":6,"deletions":64,"path":"tests/lib/projects.test.ts"},{"additions":1,"deletions":1,"path":"app/audit/_components/empty-state.tsx"},{"additions":2,"deletions":4,"path":"app/policies/hooks-client.tsx"},{"additions":30,"deletions":10,"path":"app/project/[name]/page.tsx"},{"additions":71,"deletions":21,"path":"app/project/[name]/session/[sessionId]/page.tsx"},{"additions":1,"deletions":0,"path":"assets/logos/antigravity.svg"},{"additions":1,"deletions":0,"path":"assets/logos/devin.svg"},{"additions":0,"deletions":0,"path":"assets/logos/factory-dark.png"},{"additions":0,"deletions":0,"path":"assets/logos/factory-light.png"},{"additions":0,"deletions":13,"path":"assets/logos/gemini-dark.svg"},{"additions":0,"deletions":13,"path":"assets/logos/gemini-light.svg"},{"additions":1,"deletions":0,"path":"assets/logos/goose-dark.svg"},{"additions":1,"deletions":0,"path":"assets/logos/goose-light.svg"},{"additions":22,"deletions":0,"path":"assets/logos/openclaw.svg"},{"additions":27,"deletions":22,"path":"bin/failproofai.mjs"},{"additions":4,"deletions":5,"path":"docs/ar/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/ar/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/ar/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/ar/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/ar/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/ar/introduction.mdx"},{"additions":4,"deletions":5,"path":"docs/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/cli/audit.mdx"},{"additions":15,"deletions":7,"path":"docs/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/dashboard.mdx"},{"additions":4,"deletions":5,"path":"docs/de/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/de/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/de/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/de/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/de/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/de/introduction.mdx"},{"additions":4,"deletions":5,"path":"docs/es/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/es/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/es/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/es/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/es/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/es/introduction.mdx"},{"additions":4,"deletions":5,"path":"docs/fr/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/fr/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/fr/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/fr/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/fr/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/fr/introduction.mdx"},{"additions":7,"deletions":3,"path":"docs/getting-started.mdx"},{"additions":4,"deletions":5,"path":"docs/he/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/he/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/he/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/he/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/he/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/he/introduction.mdx"},{"additions":4,"deletions":5,"path":"docs/hi/built-in-policies.mdx"},{"additions":1,"deletions":1,"path":"docs/hi/cli/audit.mdx"},{"additions":4,"deletions":6,"path":"docs/hi/configuration.mdx"},{"additions":8,"deletions":8,"path":"docs/hi/dashboard.mdx"},{"additions":2,"deletions":3,"path":"docs/hi/getting-started.mdx"},{"additions":1,"deletions":1,"path":"docs/hi/introduction.mdx"},{"additions":1,"deletions":8,"path":"docs/i18n/README.ar.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.de.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.es.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.fr.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.he.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.hi.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.it.md"},{"additions":1,"deletions":8,"path":"docs/i18n/README.ja.md"}] files, +8513/-3247...

⏱️ This may take a few minutes. Results will be posted here when complete.

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Docker smoke test note: The npm pack to Docker clean-install smoke test requires a successful bun run build (Next.js compilation), which needs CPU features not available in this review environment. The CI pipeline (GitHub Actions) handles this in its native runner.

Manual verification of what Docker would test:

  • npm pack produces failproofai-0.0.14-beta.1.tgz (1,670 files)
  • Package structure includes dist/ with ESM index and hook handler
  • ESM import from "failproofai" and CJS require("failproofai") both resolve (verified by unit tests)
  • failproofai validate + custom policy smoke path is validated by e2e tests (307 tests pass)

CI is green on the actual PR — the Docker smoke is redundant with what e2e tests already cover.

@chhhee10 chhhee10 self-assigned this Jul 14, 2026
@NiveditJain
NiveditJain merged commit bd01f75 into main Jul 15, 2026
11 checks passed
NiveditJain added a commit that referenced this pull request Jul 15, 2026
* fix(docs): restore AgentEye language navigation

* fix(ci): make docs translation atomic

* feat: agent CLI expansion — Goose + OpenClaw + Factory + Devin + Antigravity (12 CLIs), remove Gemini (#508)

* feat: OpenClaw (openclaw gateway) integration — real-time enforcement + audit

Adds OpenClaw as the 9th CLI/agent integration, dual-pillar like Hermes:

Pillar 1 (live enforcement) — OpenClaw's in-process plugin hooks (its
file-based "internal hooks" are observation-only and cannot block):
  - openclaw-plugin/ shipped package (manifest + plain-ESM index.js) that
    async-spawns the failproofai binary and maps a flat {permission,reason}
    verdict to each hook's native return shape (before_tool_call →
    {block:true,blockReason}; before_agent_run → {outcome:"block"};
    before_agent_finalize → {action:"revise"}).
  - OPENCLAW_EVENT_MAP / TOOL_MAP / TOOL_INPUT_MAP in types.ts; handler +
    tool-name-canonicalize + policy-evaluator branches; the openclaw
    Integration (install/uninstall manages plugins.load.paths +
    plugins.entries.failproofai in ~/.openclaw/openclaw.json); bin plumbing.

Pillar 2 (audit + dashboard) — reads the real JSONL transcripts at
~/.openclaw/agents/<agentId>/sessions/<uuid>.jsonl:
  - lib/openclaw-sessions.ts (pure type-discriminated parser, UUID-safe
    discovery) + lib/openclaw-projects.ts (sessions.json index, agentId
    grouping) + src/audit/cli-adapters/openclaw.ts.
  - Dashboard wiring: cli-registry, projects, download-session, app routes.

Verified live against openclaw v2026.7.1 (tool block confirmed end-to-end;
hook payloads, tool ids, and transcript schema captured from a real gateway).
Tests mirror the Hermes matrix. All maps are validated, not provisional.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: document OpenClaw integration (CHANGELOG + CLAUDE.md)

- CHANGELOG 0.0.13-beta.3 entry (Features + Docs).
- CLAUDE.md "OpenClaw hooks" section: plugin-not-shell-hook enforcement
  surface, async-spawn design, per-hook verdict mapping, the real Stop gate,
  audit layout, and the auth-profile / bundledDiscovery setup gotchas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(openclaw): organize dashboard by channel with readable session names

Live testing surfaced two UX issues: OpenClaw routes gateway sessions through
the agent's default key (agent:<id>:main) and records the channel in metadata
(lastChannel / chatType / origin.{label,provider,from}), NOT in the session key.
So the prior key-parsing grouped everything under "openclaw-main" (which the
dashboard's decodeFolderName rendered as the confusing "openclaw/main") and
named sessions with the raw "agent:main:main" key.

Now group projects by channel (telegram/slack/…/local, like Hermes groups by
source) and name sessions from the human-readable origin.label — so it reads
as "openclaw / telegram" → "Chetan (@chhhee10) id:8674922496" with the chat
id/type in the gateway-metadata columns. Verified live against openclaw v2026.7.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: remove Gemini CLI integration entirely

Google retired Gemini CLI (consumer accounts stopped 2026-06-18) in favor of
the closed-source Antigravity CLI (agy), which failproofai is migrating to.

Removes the `gemini` id from INTEGRATION_TYPES, the INTEGRATIONS/ADAPTERS
registries, KNOWN_CLI_IDS, all GEMINI_* event/tool maps, the gemini audit
adapter + lib/gemini-{projects,sessions}.ts, the --cli gemini install/audit
paths (bin + docs), and the dashboard's Gemini session provider. Deletes the
gemini logo assets and the .gemini dogfood config.

Keeps the ~/.gemini/ config-file protections in the block-* builtins and
re-points them at Antigravity (which reuses that directory). Fixes a latent
bug where OpenClaw sessions without a cwd were mislabeled "Gemini CLI" in the
session viewer. Docs + all 14 locale mirrors swept; tsc/build/tests green
(111 files, 1916 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: add Factory droid (droid) CLI integration

9th CLI integration. Factory droid is a Claude-shaped external-shell-hook CLI,
but with two contract quirks that were live-verified against droid v0.171.0
(and differ from Factory's published docs):

- hooks.json uses event names at the TOP LEVEL (no `{"hooks":{...}}` wrapper —
  droid rejects the wrapper as unknown keys). Tool events carry matcher:"*".
- deny is driven by EXIT CODE 2 + stderr, not a JSON decision (droid ignores
  the JSON decision field). Stop uses `{decision:"block"}` for require-*-before-stop.

Payload is Claude snake_case (no normalization); FACTORY_TOOL_MAP maps
Execute→Bash, Create→Write, etc. Config at ~/.factory/hooks.json (user) /
.factory/hooks.json (project); binary probe `droid`. Audit reads the JSONL
transcripts at ~/.factory/sessions/<enc-cwd>/<id>.jsonl (Claude-style adapter).

Wires all surfaces: types union + maps, Integration + registry, canonicalize,
policy-evaluator deny/instruct branches, audit adapter + lib providers,
cli-registry, projects/download-session, app pages, bin VALID_CLIS + help.
Adds unit + e2e + canonicalize tests, CLAUDE.md section, CHANGELOG, docs.
tsc/lint clean; 1932 unit + 289 e2e tests pass; build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: add Devin CLI (devin) integration

10th CLI integration. Devin CLI is a pure Claude-clone for hooks — same
snake_case payloads (hook_event_name, tool_name, tool_input, tool_use_id),
so the Integration is modeled verbatim on claudeCode with a "hooks" key in
~/.config/devin/config.json (user) / .devin/config.json (project). Verified
live against devin v3000.1.27: deny via {"decision":"block","reason"} on stdout
blocks the tool (and overrides --permission-mode dangerous). DEVIN_TOOL_MAP
maps exec→Bash (tool_input.command already canonical); no payload/event maps.

Audit reads the SQLite store at ~/.local/share/devin/cli/sessions.db
(sessions.working_directory → per-project grouping, message_nodes.chat_message
JSON). Devin's tool_calls use a flat {id,name,arguments-as-object} shape (not
the OpenAI wire form), so lib/devin-sessions.ts uses a Devin-specific
normalizer; created_at prefers the higher-precision metadata ISO string.

All 16 surfaces wired; unit + e2e + SQLite-parser tests added; CLAUDE.md
section, CHANGELOG, docs. tsc/lint clean; 1956 unit + 297 e2e pass; build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: add Antigravity CLI (agy) integration — Gemini's successor

11th CLI integration. Antigravity CLI (`agy`, Google's closed-source Go
replacement for the retired Gemini CLI) has its OWN hook contract — NOT a
Claude clone. Live-verified against agy v1.1.2.

- Named-hook schema: hooks.json = {"failproofai": {"PreToolUse":[{matcher,hooks}],
  "PreInvocation":[{...}], "Stop":[{...}]}} at ~/.gemini/config/hooks.json (user)
  / .agents/hooks.json (project). Tool events use a {matcher,hooks} wrapper;
  PreInvocation/Stop are flat handler arrays.
- camelCase protojson payload → the handler normalizes toolCall.name→tool_name,
  toolCall.args→tool_input, conversationId→session_id, workspacePaths[0]→cwd,
  transcriptPath→transcript_path before the usual canonicalization.
- Own response shapes: PreToolUse deny → {decision:"deny",reason}; Stop →
  {decision:"continue",reason} (re-enters the loop, so require-*-before-stop
  enforce); instruct → PreInvocation {injectSteps:[{ephemeralMessage}]}.
- ANTIGRAVITY_TOOL_MAP (run_command→Bash, view_file→Read, ...) +
  ANTIGRAVITY_TOOL_INPUT_MAP (Bash: CommandLine→command, Cwd→cwd).
- Audit reads plain JSONL transcript_full.jsonl under
  ~/.gemini/antigravity-cli/brain/<id>/.system_generated/logs/, indexed by
  conversation_summaries.db (falls back to brain/ dir scan when the index is empty).

Keeps the ~/.gemini/ path guards (relabeled for Antigravity in the Gemini
removal). All 16 surfaces wired; unit + e2e + parser tests; CLAUDE.md section,
CHANGELOG, docs. tsc/lint clean; 1976 unit + 306 e2e pass; build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: backfill OpenClaw into user-facing docs + document VS Code coverage

Two documentation gaps closed:

1. OpenClaw was shipped as a fully-wired integration but was never added to the
   user-facing supported-CLI lists. Backfills it into README + docs
   (configuration / getting-started / introduction): a dual-pillar bullet after
   Hermes, the --cli lists, install examples, and the detection list.

2. Documents that VS Code's Copilot Chat agent mode (Preview) needs no dedicated
   integration — its agent hooks load from .github/hooks/*.json,
   ~/.copilot/hooks/*.json, and ~/.claude/settings.json (the exact paths the
   copilot/claude integrations already write), verified live against VS Code
   1.127 / github.copilot-chat 0.55.0. So `--cli copilot` (or --cli claude)
   already enforces in VS Code agent-mode sessions. Adds a CLAUDE.md section +
   a docs note.

No code change. tsc/build/tests green (115 files, 1976 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: make resolve-transcript-path enumerate openclaw explicitly

Add an explicit `case "openclaw"` (functionally a no-op that returns undefined,
same as the default) documenting that OpenClaw's transcript_path comes from its
plugin shim's stdin and its audit reads sessions directly via
lib/openclaw-sessions.ts. Makes the switch cover all 11 CLIs explicitly so the
wiring is unambiguous for future integrations. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: drop brittle line-number cross-refs from policy-evaluator comments

Replace stale `(line ~N)` / `at line ~N` comment cross-references (which went
out of date when the Gemini branches were removed and shift on every edit) with
descriptive references to the mirrored per-CLI branches. Comment-only; no
behavior change. Avoids automated-review flags on outdated line numbers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: add Goose (codename goose, Block) as the 12th CLI integration

Dual-pillar like Hermes/OpenClaw/Factory/Devin/Antigravity — real-time policy
enforcement + offline audit. Enforcement uses Goose's "hooks" system (the
cross-agent Open Plugins spec): the installer drops an auto-discovered plugin
dir at ~/.agents/plugins/failproofai/hooks/hooks.json (user/project). Deny is
{"decision":"block"} JSON on PreToolUse only (goose >= v1.37.0); goose has no
Stop event, so require-*-before-stop is inapplicable (like Hermes). Audit reads
the SQLite sessions.db (cwd-grouped like Devin; Claude-style content_json).
Verified live end-to-end against goose v1.43.0 (real sudo blocked; real
sessions.db read). Follows the Factory (enforcement) + Devin (SQLite audit)
templates.

Also backfills the README supported-CLI logo grid (openclaw/factory/devin/
antigravity/goose were missing) with dark/light variants, and documents Goose
across CLAUDE.md + docs/{getting-started,configuration}.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: link each supported-CLI logo to its official site

Point the 9 logos the maintainer supplied URLs for at their official pages
(codex→learn.chatgpt.com, copilot→github.com/features/copilot/cli, cursor,
opencode, pi, hermes, openclaw, factory, goose→goose-docs.ai); claude/devin/
antigravity keep their existing links.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(hooks): correct Antigravity file-tool map + quiet channel-less allow-notes

Two issues surfaced while testing every CLI:

1. Antigravity file writes weren't gated. ANTIGRAVITY_TOOL_MAP had guessed tool
   names (`write_file`/`list_directory`/`find_filepath`) — the real ones (from
   the agy binary + live transcripts) are `write_to_file`/`list_dir`/
   `find_by_name`, and there was no input-key map for file tools, so the path
   (delivered as `TargetFile`) never became `file_path`. Result: block-env-files
   / block-secrets-write silently allowed `.env` writes on Antigravity. Corrected
   the tool names and added Write/Edit/Read input maps (TargetFile→file_path,
   CodeContent→content). Verified live: agy now denies a `.env` write.

2. Noisy Stop-hook output. Informational allow-notes were emitted as stdout
   `{reason}` on events with no agent-facing context channel (Stop, SubagentStop,
   Session*, …), so a passing Stop rendered as a "…skipping commit check…skipping
   PR check…" wall (visible e.g. in droid). Those notes now go to stderr + the
   activity store only; stdout stays clean on channel-less events.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): arrange the 12 CLI logos as 2 rows of 6

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(audit): reconstruct Devin's active conversation path (de-dup the forest)

Devin stores a session's messages as a FOREST (node_id/parent_node_id) and
replays earlier context under fresh roots on later turns, so message_nodes holds
many branches that repeat the same messages — verified live: 26-31 raw nodes for
8-10 real messages. The parser read every node in node_id order, so the dashboard
rendered each message 2-4× (a wall of duplicated "Create a file named .env…").

Reconstruct the real conversation instead: walk parent_node_id from the newest
leaf (max node_id) to its root and reverse. Devin sessions now render the true
8-10 message conversation. Adds devinActiveConversationPath + unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(release): finalize CHANGELOG + dogfood the 4 newest CLIs

- CHANGELOG: add the missing Fixes section (Antigravity file-tool map, quiet
  channel-less allow-notes, Devin forest de-dup) and a Docs entry for the README
  logo grid.
- Dogfood: ship project-scope configs for Factory (.factory/hooks.json), Devin
  (.devin/config.json), Antigravity (.agents/hooks.json), and Goose
  (.agents/plugins/failproofai/hooks/hooks.json) using the dev
  `bun bin/failproofai.mjs` command — generated from each integration's own
  writeHookEntries so they track the live schema. Documented in CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(release): bump to 0.0.14-beta.1

main shipped 0.0.13 stable (#506) without this CLI-expansion work, so it releases
as 0.0.14-beta.1. Moves the branch's CHANGELOG entries into a fresh 0.0.14-beta.1
section above the released 0.0.13.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(changelog): reference PR #508

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: complete the dashboard + config CLI lists for all 12 agents

The dashboard project-list description, session-viewer/activity badge lists, the
?cli= filter, and the auto-detect list only covered the original 7 CLIs. Add the
five newer ones (OpenClaw, Factory Droid, Devin, Antigravity, Goose) with their
session-store paths and badge colors. Caught by the PR #508 automated review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* docs: update translations for changed English sources (#513)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* docs: update translations for changed English sources

* fix(docs): restore AgentEye language navigation

* fix(ci): make docs translation atomic

* fix(docs): address translation review findings

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Chetan Raghuvanshi <145042127+chhhee10@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants