Files
claude-engineering-plugin/docs/plans/2026-02-14-feat-add-gemini-cli-target-provider-plan.md
Kieran Klaassen e113d20126 docs: mark gemini target plan as completed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 20:35:09 -08:00

16 KiB

title, type, status, completed_date, completed_by, actual_effort, date
title type status completed_date completed_by actual_effort date
Add Gemini CLI as a Target Provider feat completed 2026-02-14 Claude Opus 4.6 Completed in one session 2026-02-14

Add Gemini CLI as a Target Provider

Overview

Add gemini as a sixth target provider in the converter CLI, alongside opencode, codex, droid, cursor, and pi. This enables --to gemini for both convert and install commands, converting Claude Code plugins into Gemini CLI-compatible format.

Gemini CLI (google-gemini/gemini-cli) is Google's open-source AI agent for the terminal. It supports GEMINI.md context files, custom commands (TOML format), agent skills (SKILL.md standard), MCP servers, and extensions -- making it a strong conversion target with good coverage of Claude Code plugin concepts.

Component Mapping

Claude Code Gemini Equivalent Notes
agents/*.md .gemini/skills/*/SKILL.md Agents become skills -- Gemini activates them on demand via activate_skill tool based on description matching
commands/*.md .gemini/commands/*.toml TOML format with prompt and description fields; namespaced via directory structure
skills/*/SKILL.md .gemini/skills/*/SKILL.md Identical standard -- copy directly
MCP servers settings.json mcpServers Same MCP protocol; different config location (settings.json vs .mcp.json)
hooks/ settings.json hooks Gemini has hooks (BeforeTool, AfterTool, SessionStart, etc.) but different format; emit console.warn and skip for now
.claude/ paths .gemini/ paths Content rewriting needed

Key Design Decisions

1. Agents become skills (not GEMINI.md context)

With 29 agents, dumping them into GEMINI.md would flood every session's context. Instead, agents convert to skills -- Gemini autonomously activates them based on the skill description when relevant. This matches how Claude Code agents are invoked on demand via the Task tool.

2. Commands use TOML format with directory-based namespacing

Gemini CLI commands are .toml files where the path determines the command name: .gemini/commands/git/commit.toml becomes /git:commit. This maps cleanly from Claude Code's colon-namespaced commands (workflows:plan -> .gemini/commands/workflows/plan.toml).

3. Commands use {{args}} placeholder

Gemini's TOML commands support {{args}} for argument injection, mapping from Claude Code's argument-hint field. Commands with argument-hint get {{args}} appended to the prompt.

4. MCP servers go into project-level settings.json

Gemini CLI reads MCP config from .gemini/settings.json under the mcpServers key. The format is compatible -- same command, args, env fields, plus Gemini-specific cwd, timeout, trust, includeTools, excludeTools.

5. Skills pass through unchanged

Gemini adopted the same SKILL.md standard (YAML frontmatter with name and description, markdown body). Skills copy directly.

TOML Command Format

description = "Brief description of the command"
prompt = """
The prompt content that will be sent to Gemini.

User request: {{args}}
"""
  • description (string): One-line description shown in /help
  • prompt (string): The prompt sent to the model; supports {{args}}, !{shell}, @{file} placeholders

Skill (SKILL.md) Format

---
name: skill-name
description: When and how Gemini should use this skill
---

# Skill Title

Detailed instructions...

Identical to Claude Code's format. The description field is critical -- Gemini uses it to decide when to activate the skill.

MCP Server Format (settings.json)

{
  "mcpServers": {
    "server-name": {
      "command": "npx",
      "args": ["-y", "package-name"],
      "env": { "KEY": "value" }
    }
  }
}

Acceptance Criteria

  • bun run src/index.ts convert --to gemini ./plugins/compound-engineering produces valid Gemini config
  • Agents convert to .gemini/skills/*/SKILL.md with populated description in frontmatter
  • Commands convert to .gemini/commands/*.toml with prompt and description fields
  • Namespaced commands create directory structure (workflows:plan -> commands/workflows/plan.toml)
  • Commands with argument-hint include {{args}} placeholder in prompt
  • Commands with disable-model-invocation: true are still included (TOML commands are prompts, not code)
  • Skills copied to .gemini/skills/ (identical format)
  • MCP servers written to .gemini/settings.json under mcpServers key
  • Existing .gemini/settings.json is backed up before overwrite, and MCP config is merged (not clobbered)
  • Content transformation rewrites .claude/ and ~/.claude/ paths to .gemini/ and ~/.gemini/
  • /workflows:plan transformed to /workflows:plan (Gemini preserves colon namespacing via directories)
  • Task agent-name(args) transformed to Use the agent-name skill to: args
  • Plugins with hooks emit console.warn about format differences
  • Writer does not double-nest .gemini/.gemini/
  • model and allowedTools fields silently dropped (no Gemini equivalent in skills/commands)
  • Converter and writer tests pass
  • Existing tests still pass (bun test)

Implementation

Phase 1: Types

Create src/types/gemini.ts

export type GeminiSkill = {
  name: string
  content: string // Full SKILL.md with YAML frontmatter
}

export type GeminiSkillDir = {
  name: string
  sourceDir: string
}

export type GeminiCommand = {
  name: string       // e.g. "plan" or "workflows/plan"
  content: string    // Full TOML content
}

export type GeminiBundle = {
  generatedSkills: GeminiSkill[]     // From agents
  skillDirs: GeminiSkillDir[]         // From skills (pass-through)
  commands: GeminiCommand[]
  mcpServers?: Record<string, {
    command?: string
    args?: string[]
    env?: Record<string, string>
    url?: string
    headers?: Record<string, string>
  }>
}

Phase 2: Converter

Create src/converters/claude-to-gemini.ts

Core functions:

  1. convertClaudeToGemini(plugin, options) -- main entry point

    • Convert each agent to a skill via convertAgentToSkill()
    • Convert each command via convertCommand()
    • Pass skills through as directory references
    • Convert MCP servers to settings-compatible object
    • Emit console.warn if plugin.hooks has entries
  2. convertAgentToSkill(agent) -- agent -> SKILL.md

    • Frontmatter: name (from agent name), description (from agent description, max ~300 chars)
    • Body: agent body with content transformations applied
    • Prepend capabilities section if present
    • Silently drop model field (no Gemini equivalent)
    • If description is empty, generate from agent name: "Use this skill for ${agent.name} tasks"
  3. convertCommand(command, usedNames) -- command -> TOML file

    • Preserve namespace structure: workflows:plan -> path workflows/plan
    • description field from command description
    • prompt field from command body with content transformations
    • If command has argument-hint, append \n\nUser request: {{args}} to prompt
    • Body: apply transformContentForGemini() transformations
    • Silently drop allowedTools (no Gemini equivalent)
  4. transformContentForGemini(body) -- content rewriting

    • .claude/ -> .gemini/ and ~/.claude/ -> ~/.gemini/
    • Task agent-name(args) -> Use the agent-name skill to: args
    • @agent-name references -> the agent-name skill
    • Skip file paths (containing /) and common non-command patterns
  5. convertMcpServers(servers) -- MCP config

    • Map each ClaudeMcpServer entry to Gemini-compatible JSON
    • Pass through: command, args, env, url, headers
    • Drop type field (Gemini infers transport)
  6. toToml(description, prompt) -- TOML serializer

    • Escape TOML strings properly
    • Use multi-line strings (""") for prompt field
    • Simple string for description

Phase 3: Writer

Create src/targets/gemini.ts

Output structure:

.gemini/
├── commands/
│   ├── plan.toml
│   └── workflows/
│       └── plan.toml
├── skills/
│   ├── agent-name-1/
│   │   └── SKILL.md
│   ├── agent-name-2/
│   │   └── SKILL.md
│   └── original-skill/
│       └── SKILL.md
└── settings.json          (only mcpServers key)

Core function: writeGeminiBundle(outputRoot, bundle)

  • resolveGeminiPaths(outputRoot) -- detect if path already ends in .gemini to avoid double-nesting (follow droid writer pattern)
  • Write generated skills to skills/<name>/SKILL.md
  • Copy original skill directories to skills/ via copyDir()
  • Write commands to commands/ as .toml files, creating subdirectories for namespaced commands
  • Write settings.json with { "mcpServers": {...} } via writeJson() with backupFile() for existing files
  • If settings.json exists, read it first and merge mcpServers key (don't clobber other settings)

Phase 4: Wire into CLI

Modify src/targets/index.ts

import { convertClaudeToGemini } from "../converters/claude-to-gemini"
import { writeGeminiBundle } from "./gemini"
import type { GeminiBundle } from "../types/gemini"

// Add to targets:
gemini: {
  name: "gemini",
  implemented: true,
  convert: convertClaudeToGemini as TargetHandler<GeminiBundle>["convert"],
  write: writeGeminiBundle as TargetHandler<GeminiBundle>["write"],
},

Modify src/commands/convert.ts

  • Update --to description: "Target format (opencode | codex | droid | cursor | pi | gemini)"
  • Add to resolveTargetOutputRoot: if (targetName === "gemini") return path.join(outputRoot, ".gemini")

Modify src/commands/install.ts

  • Same two changes as convert.ts

Phase 5: Tests

Create tests/gemini-converter.test.ts

Test cases (use inline ClaudePlugin fixtures, following existing converter test patterns):

  • Agent converts to skill with SKILL.md frontmatter (name and description populated)
  • Agent with empty description gets default description text
  • Agent with capabilities prepended to body
  • Agent model field silently dropped
  • Agent with empty body gets default body text
  • Command converts to TOML with prompt and description fields
  • Namespaced command creates correct path (workflows:plan -> workflows/plan)
  • Command with disable-model-invocation is still included
  • Command allowedTools silently dropped
  • Command with argument-hint gets {{args}} placeholder in prompt
  • Skills pass through as directory references
  • MCP servers convert to settings.json-compatible config
  • Content transformation: .claude/ paths -> .gemini/
  • Content transformation: ~/.claude/ paths -> ~/.gemini/
  • Content transformation: Task agent(args) -> natural language skill reference
  • Hooks present -> console.warn emitted
  • Plugin with zero agents produces empty generatedSkills array
  • Plugin with only skills works correctly
  • TOML output is valid (description and prompt properly escaped)

Create tests/gemini-writer.test.ts

Test cases (use temp directories, following existing writer test patterns):

  • Full bundle writes skills, commands, settings.json
  • Generated skills written as skills/<name>/SKILL.md
  • Original skills copied to skills/ directory
  • Commands written as .toml files in commands/ directory
  • Namespaced commands create subdirectories (commands/workflows/plan.toml)
  • MCP config written as valid JSON settings.json with mcpServers key
  • Existing settings.json is backed up before overwrite
  • Output root already ending in .gemini does NOT double-nest
  • Empty bundle produces no output

Phase 6: Documentation

Create docs/specs/gemini.md

Document the Gemini CLI spec as reference, following existing docs/specs/codex.md pattern:

  • GEMINI.md context file format
  • Custom commands format (TOML with prompt, description)
  • Skills format (identical SKILL.md standard)
  • MCP server configuration (settings.json)
  • Extensions system (for reference, not converted)
  • Hooks system (for reference, format differences noted)
  • Config file locations (user-level ~/.gemini/ vs project-level .gemini/)
  • Directory layout conventions

Update README.md

Add gemini to the supported targets in the CLI usage section.

What We're NOT Doing

  • Not converting hooks (Gemini has hooks but different format -- BeforeTool/AfterTool with matchers -- warn and skip)
  • Not generating full settings.json (only mcpServers key -- user-specific settings like model, tools.sandbox are out of scope)
  • Not creating extensions (extension format is for distributing packages, not for converted plugins)
  • Not using @{file} or !{shell} placeholders in converted commands (would require analyzing command intent)
  • Not transforming content inside copied SKILL.md files (known limitation -- skills may reference .claude/ paths internally)
  • Not clearing old output before writing (matches existing target behavior)
  • Not merging into existing settings.json intelligently beyond mcpServers key (too risky to modify user config)

Complexity Assessment

This is a medium change. The converter architecture is well-established with five existing targets, so this is mostly pattern-following. The key novelties are:

  1. The TOML command format (unique among all targets -- need simple TOML serializer)
  2. Agents map to skills rather than a direct 1:1 concept (but this is the same pattern as codex)
  3. Namespaced commands use directory structure (new approach vs flattening in cursor/codex)
  4. MCP config goes into a broader settings.json file (need to merge, not clobber)

Skills being identical across platforms simplifies things significantly. The TOML serialization is simple (only two fields: description string and prompt multi-line string).

References

Completion Summary

What Was Delivered

  • Phase 1: Types (src/types/gemini.ts)
  • Phase 2: Converter (src/converters/claude-to-gemini.ts)
  • Phase 3: Writer (src/targets/gemini.ts)
  • Phase 4: CLI wiring (src/targets/index.ts, src/commands/convert.ts, src/commands/install.ts)
  • Phase 5: Tests (tests/gemini-converter.test.ts, tests/gemini-writer.test.ts)
  • Phase 6: Documentation (docs/specs/gemini.md, README.md)

Implementation Statistics

  • 10 files changed
  • 27 new tests added (129 total, all passing)
  • 148 output files generated from compound-engineering plugin conversion
  • 0 dependencies added

Git Commits

  • 201ad6d feat(gemini): add Gemini CLI as sixth target provider
  • 8351851 docs: add Gemini CLI spec and update README with gemini target

Completion Details

  • Completed By: Claude Opus 4.6
  • Date: 2026-02-14
  • Session: Single session