Skip to content

feat: add support for Gemini CLI - #150

Draft
ryzizub wants to merge 3 commits into
mainfrom
claude/compassionate-volhard-ddd8ff
Draft

feat: add support for Gemini CLI#150
ryzizub wants to merge 3 commits into
mainfrom
claude/compassionate-volhard-ddd8ff

Conversation

@ryzizub

@ryzizub ryzizub commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Description

Wires up MCP, hooks, and the reviewer agent for Gemini CLI. Skills were already portable after #128. Closes #125.

Gemini CLI reads the Agent Skills standard directly, scanning .agents/skills alongside its own .gemini/skills, so gemini skills install <repo-url> --path skills lands all 15 with no Gemini-specific file. The enforcement layer does not carry over as cleanly — different hook event names, different tool names, a stricter agent schema — so gemini/ holds those three, ported.

What changes:

  • gemini/settings.json — registers the same dart and very-good-cli servers as .mcp.json and wires the same hook scripts under Gemini's event names. Generated with gemini hooks migrate --from-claude, then corrected for the three things that tool leaves behind, all of which fail silently: ${CLAUDE_PLUGIN_ROOT} is not rewritten and has no Gemini equivalent for settings-level hooks (hence ${VGV_PLUGIN_ROOT}); timeout is copied verbatim, but Gemini reads milliseconds where Claude Code reads seconds, so a 10 becomes a 10 ms budget; and MCP matchers keep mcp__<server>__<tool> when Gemini names them mcp_<server>_<tool>.
  • hooks/scripts/vgv-cli-common.shdeny() and allow() read hook_event_name and emit whichever response shape the firing harness reads (hookSpecificOutput.permissionDecision for Claude Code, top-level decision/reason for Gemini), so one script serves both. Claude Code behavior is unchanged: a payload without that field still gets the Claude shape, and check-vgv-cli.sh still auto-approves there. Gemini has no auto-approve for a BeforeTool hook, so that path enforces the version gate only and "trust": true on the MCP server covers approval.
  • gemini/agents/flutter-reviewer.md — Gemini cannot bundle a subagent in an install, so users copy this file. It cannot be the same file as agents/flutter-reviewer.md: Gemini validates agent frontmatter against a strict allowlist and drops any agent carrying a key outside it, so skills: and hooks: are both rejected. Read-only is enforced by granting no shell tool at all rather than by the agent-scoped hook Claude Code uses — same guarantee, different mechanism, worth a look.
  • Docs across README.md, CONTRIBUTING.md, CLAUDE.md, AGENTS.md.

Everything else is shared verbatim: both harnesses put the shell command in tool_input.command and the edited path in tool_input.file_path, so block-cli-workarounds.sh, analyze.sh and format.sh need no payload changes.

Marketplace/extension packaging is deliberately out of scope. Gemini's own gemini extensions install would need this repo rooted as an extension, and Gemini then reads hooks/hooks.json and agents/ with its own schemas — printing Invalid hook event name: "PreToolUse" ... Skipping. twice per session and erroring on the Claude agent file. Not worth degrading either path for.

Verification

Tested against a real Gemini CLI 0.59.0 (current latest on npm) in a throwaway HOME, installed the way a user would.

  • gemini skills install https://github.com/VeryGoodOpenSource/vgv-ai-flutter-plugin --path skills — 15/15 installed from the real URL. Also 15/15 discovered from .agents/skills.
  • Merged gemini/settings.json into ~/.gemini/settings.json and copied the agent to ~/.gemini/agents/: agent loads clean, zero Invalid hook event name, zero hook failures, the SessionStart hook actually ran from ${VGV_PLUGIN_ROOT} and returned its warning, and gemini mcp list shows both dart and very-good-cli Connected. Verified for both the user-scope and project-scope install described in the README.
  • The Claude Code agent file, run through Gemini's real loader for contrast, fails with Unrecognized key(s) in object: 'skills', 'hooks' — which is why there are two files.
  • Both hook suites pass (19, 26), including the new Gemini response-shape assertions; every deny/allow branch exercised by hand with both payload shapes.
  • cspell, markdownlint and claude plugin validate . clean.

Cross-checked every claim in the docs against the 0.59.0 source and the official docs, not just the version that happened to be installed. Byte-identical between 0.52.0 and 0.59.0: hook event names, the milliseconds timeout, the decision/reason block shape, the absence of any auto-approve for BeforeTool, gemini hooks migrate --from-claude's event and tool-name mappings, the strict local-agent schema, the .agents/skills discovery path, the agent directories, the hook env vars (there is still no plugin-root variable for settings-level hooks, hence ${VGV_PLUGIN_ROOT}), and the settings env-var interpolation regex.

One behavior did change in 0.59.0 — workspace trust is now fail-closed, so a headless run in an untrusted directory refuses to start rather than loading with workspace features disabled. That only affects the by-hand verification steps, which now say to set GEMINI_CLI_TRUST_WORKSPACE=true; interactive users just get the normal trust prompt.

Not verified: a live BeforeTool block or AfterTool analyze/format inside a real Gemini session — that needs credentials and a model call. Those paths are covered at the script level with real Gemini payloads, and the wiring that reaches them (event names, matchers, script resolution) is verified above.

Type of Change

  • New feature (feat)
  • Bug fix (fix)
  • Code refactor (refactor)
  • Documentation (docs)
  • CI change (ci)
  • Chore (chore)

🤖 Generated with Claude Code

Ports the enforcement layer to Gemini CLI so the plugin is not skills-only
there. Skills already load unchanged from the Agent Skills standard paths;
this adds the MCP wiring, the hooks, the reviewer subagent, and CI that
proves all three actually load.

- `.gemini/settings.json` registers the same `dart` and `very-good-cli`
  servers as `.mcp.json` and runs the same hook scripts under Gemini's event
  names. Generated with `gemini hooks migrate --from-claude`, then corrected
  for the three things that tool leaves behind: `${CLAUDE_PLUGIN_ROOT}` (no
  Gemini equivalent for settings-level hooks, so scripts resolve through
  `${VGV_PLUGIN_ROOT:-$PWD}`), timeouts (Gemini reads milliseconds, Claude
  reads seconds), and MCP matchers (`mcp_<server>_<tool>`, not
  `mcp__<server>__<tool>`).
- The hook scripts read `hook_event_name` and emit whichever response shape
  the firing harness reads, so one script serves both. Claude Code behavior
  is untouched: `check-vgv-cli.sh` still auto-approves and
  `block-cli-workarounds.sh` still steers the quality gates through the MCP
  tools there.
- `.gemini/agents/flutter-reviewer.md` ports the reviewer. Gemini validates
  agent frontmatter with a strict schema that rejects `skills:` and `hooks:`,
  so it is a second port rather than a shared file. Its read-only contract is
  structural instead of hook-enforced: it is granted no shell tool at all.
- A `Gemini CLI Loader` CI job asserts all 15 skills load, the subagent
  passes Gemini's schema, and every hook registers and runs — no credentials
  and no model call. `gemini-config_test.sh` guards the silent-failure modes
  statically, and both hook suites now run every case once per harness.

Closes #125

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ryzizub
ryzizub requested a review from a team as a code owner September 8, 2026 13:03
@ryzizub
ryzizub marked this pull request as draft September 8, 2026 13:18
ryzizub and others added 2 commits September 9, 2026 13:40
Matches the shape of the Codex PR (#151): the harness-specific assets are a
plain `gemini/` directory of files users copy, and everything else is docs.

- `.gemini/` becomes `gemini/`. It was never config for this repo's own
  workspace; it is the MCP + hooks settings a user merges into their own
  `~/.gemini/settings.json` (or a project's `.gemini/settings.json` for a whole
  team), and the reviewer agent they copy into `agents/` beside it. The hook
  path drops the `$PWD` fallback and reads `${VGV_PLUGIN_ROOT}` plainly, since
  the plugin is never installed at the user's project root.
- Drops the Gemini CLI loader CI job and `gemini-config_test.sh`. Nothing in CI
  exercises Codex either; both are verified by hand, and `CONTRIBUTING.md` says
  how.
- The two hook suites keep their original cases and gain one assertion each for
  the Gemini response shape, rather than running the whole matrix twice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Re-checked every claim against the current release rather than the 0.52.0 that
was on hand. Nothing the port depends on changed: hook event names, the
milliseconds timeout, the `decision`/`reason` block shape, the absence of an
auto-approve, `gemini hooks migrate --from-claude`'s mappings, the strict local
agent schema, the `.agents/skills` discovery path, the agent directories, and
the settings env-var interpolation regex are all byte-identical between the two.
Both MCP servers connect and all 15 skills load on 0.59.0.

One behavior did change and it bites the verification steps this file
recommends: 0.59.0 made workspace trust fail closed, so a headless `gemini -p`
in an untrusted directory now refuses to start instead of loading with workspace
features disabled. That reads as "nothing loaded" unless you expect it, so the
instructions now say to set `GEMINI_CLI_TRUST_WORKSPACE=true`.

Also makes the README's `VGV_PLUGIN_ROOT` step say to put it in a shell profile,
since Gemini resolves it when it loads settings, and labels the two agent copy
targets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add support for Gemini CLI

1 participant