From 6223d590421db23d505f025527558cc79e113f00 Mon Sep 17 00:00:00 2001 From: cexll Date: Sun, 16 Nov 2025 00:04:33 +0800 Subject: [PATCH] Add Gemini CLI integration skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement gemini skill following codex pattern with Python wrapper supporting multiple execution modes (uv run, python3, direct), configurable models (gemini-2.5-pro/flash/1.5-pro), timeout control, and zero-dependency cross-platform compatibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- skills/gemini/SKILL.md | 132 ++++++++++++++++++++++++++ skills/gemini/scripts/gemini.py | 160 ++++++++++++++++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 skills/gemini/SKILL.md create mode 100755 skills/gemini/scripts/gemini.py diff --git a/skills/gemini/SKILL.md b/skills/gemini/SKILL.md new file mode 100644 index 0000000..763dffb --- /dev/null +++ b/skills/gemini/SKILL.md @@ -0,0 +1,132 @@ +--- +name: gemini +description: Execute Gemini CLI for AI-powered code analysis and generation. Use when you need to leverage Google's Gemini models for complex reasoning tasks. +--- + +# Gemini CLI Integration + +## Overview + +Execute Gemini CLI commands with support for multiple models and flexible prompt input. Integrates Google's Gemini AI models into Claude Code workflows. + +## When to Use + +- Complex reasoning tasks requiring advanced AI capabilities +- Code generation and analysis with Gemini models +- Tasks requiring Google's latest AI technology +- Alternative perspective on code problems + +## Usage + +**推荐方式**(使用 uv run,自动管理 Python 环境): +```bash +uv run ~/.claude/skills/gemini/scripts/gemini.py -m -p "" [working_dir] +``` + +**备选方式**(直接执行或使用 Python): +```bash +~/.claude/skills/gemini/scripts/gemini.py -m -p "" [working_dir] +# 或 +python3 ~/.claude/skills/gemini/scripts/gemini.py -m -p "" [working_dir] +``` + +## Timeout Control + +- **Built-in**: Script enforces 2-hour timeout by default +- **Override**: Set `GEMINI_TIMEOUT` environment variable (in milliseconds) +- **Bash tool**: Always set `timeout: 7200000` parameter for double protection + +### Parameters + +- `-m, --model` (optional): Model to use (default: gemini-2.5-pro) + - `gemini-2.5-pro`: Latest flagship model + - `gemini-2.5-flash`: Fast, efficient model + - `gemini-1.5-pro`: Previous generation +- `-p, --prompt` (required): Task prompt or question +- `working_dir` (optional): Working directory (default: current) + +### Return Format + +Plain text output from Gemini: + +```text +Model response text here... +``` + +Error format (stderr): + +```text +ERROR: Error message +``` + +### Invocation Pattern + +When calling via Bash tool, always include the timeout parameter: + +```yaml +Bash tool parameters: +- command: uv run ~/.claude/skills/gemini/scripts/gemini.py -m gemini-2.5-pro -p "" +- timeout: 7200000 +- description: +``` + +Alternatives: + +```yaml +# Direct execution (simplest) +- command: ~/.claude/skills/gemini/scripts/gemini.py -m gemini-2.5-pro -p "" + +# Using python3 +- command: python3 ~/.claude/skills/gemini/scripts/gemini.py -m gemini-2.5-pro -p "" +``` + +### Examples + +**Basic query:** + +```bash +# Recommended: via uv run +uv run ~/.claude/skills/gemini/scripts/gemini.py -m gemini-2.5-pro -p "explain quantum computing" +# timeout: 7200000 + +# Alternative: direct execution +~/.claude/skills/gemini/scripts/gemini.py -m gemini-2.5-pro -p "explain quantum computing" +``` + +**Code analysis:** + +```bash +uv run ~/.claude/skills/gemini/scripts/gemini.py -m gemini-2.5-flash -p "review this code for security issues: $(cat app.py)" +# timeout: 7200000 +``` + +**With specific working directory:** + +```bash +uv run ~/.claude/skills/gemini/scripts/gemini.py -m gemini-2.5-pro -p "analyze project structure" "/path/to/project" +# timeout: 7200000 +``` + +**Using fast model:** + +```bash +uv run ~/.claude/skills/gemini/scripts/gemini.py -m gemini-2.5-flash -p "quick code suggestion" +# timeout: 7200000 +``` + +**Using python3 directly (alternative):** + +```bash +python3 ~/.claude/skills/gemini/scripts/gemini.py -m gemini-2.5-pro -p "your prompt here" +``` + +## Notes + +- **Recommended**: Use `uv run` for automatic Python environment management (requires uv installed) +- **Alternative**: Direct execution `./gemini.py` (uses system Python via shebang) +- Python implementation using standard library (zero dependencies) +- Cross-platform compatible (Windows/macOS/Linux) +- PEP 723 compliant (inline script metadata) +- Requires Gemini CLI installed and authenticated +- Supports all Gemini model variants +- Output is streamed directly from Gemini CLI diff --git a/skills/gemini/scripts/gemini.py b/skills/gemini/scripts/gemini.py new file mode 100755 index 0000000..d3f2d8d --- /dev/null +++ b/skills/gemini/scripts/gemini.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.8" +# dependencies = [] +# /// +""" +Gemini CLI wrapper with cross-platform support. + +Usage: + uv run gemini.py -m -p "" [workdir] + python3 gemini.py -m -p "" + ./gemini.py -m gemini-2.5-pro -p "your prompt" +""" +import subprocess +import sys +import os +import argparse + +DEFAULT_MODEL = 'gemini-2.5-pro' +DEFAULT_WORKDIR = '.' +DEFAULT_TIMEOUT = 7200 # 2 hours in seconds +FORCE_KILL_DELAY = 5 + + +def log_error(message: str): + """输出错误信息到 stderr""" + sys.stderr.write(f"ERROR: {message}\n") + + +def log_warn(message: str): + """输出警告信息到 stderr""" + sys.stderr.write(f"WARN: {message}\n") + + +def resolve_timeout() -> int: + """解析超时配置(秒)""" + raw = os.environ.get('GEMINI_TIMEOUT', '') + if not raw: + return DEFAULT_TIMEOUT + + try: + parsed = int(raw) + if parsed <= 0: + log_warn(f"Invalid GEMINI_TIMEOUT '{raw}', falling back to {DEFAULT_TIMEOUT}s") + return DEFAULT_TIMEOUT + # 环境变量是毫秒,转换为秒 + return parsed // 1000 if parsed > 10000 else parsed + except ValueError: + log_warn(f"Invalid GEMINI_TIMEOUT '{raw}', falling back to {DEFAULT_TIMEOUT}s") + return DEFAULT_TIMEOUT + + +def parse_args(): + """解析命令行参数""" + parser = argparse.ArgumentParser( + description='Gemini CLI wrapper for Claude Code integration', + formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument( + '-m', '--model', + default=DEFAULT_MODEL, + help=f'Gemini model to use (default: {DEFAULT_MODEL})' + ) + parser.add_argument( + '-p', '--prompt', + required=True, + help='Prompt to send to Gemini' + ) + parser.add_argument( + 'workdir', + nargs='?', + default=DEFAULT_WORKDIR, + help='Working directory (default: current directory)' + ) + + return parser.parse_args() + + +def build_gemini_args(args) -> list: + """构建 gemini CLI 参数""" + return [ + 'gemini', + '-m', args.model, + '-p', args.prompt + ] + + +def main(): + args = parse_args() + gemini_args = build_gemini_args(args) + timeout_sec = resolve_timeout() + + # 如果指定了工作目录,切换到该目录 + if args.workdir != DEFAULT_WORKDIR: + try: + os.chdir(args.workdir) + except FileNotFoundError: + log_error(f"Working directory not found: {args.workdir}") + sys.exit(1) + except PermissionError: + log_error(f"Permission denied: {args.workdir}") + sys.exit(1) + + try: + # 启动 gemini 子进程,直接透传 stdout 和 stderr + process = subprocess.Popen( + gemini_args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1 # 行缓冲 + ) + + # 实时输出 stdout + stdout_lines = [] + for line in process.stdout: + sys.stdout.write(line) + sys.stdout.flush() + stdout_lines.append(line) + + # 等待进程结束 + returncode = process.wait(timeout=timeout_sec) + + # 读取 stderr + stderr_output = process.stderr.read() + if stderr_output: + sys.stderr.write(stderr_output) + + # 检查退出码 + if returncode != 0: + log_error(f'Gemini exited with status {returncode}') + sys.exit(returncode) + + sys.exit(0) + + except subprocess.TimeoutExpired: + log_error(f'Gemini execution timeout ({timeout_sec}s)') + process.kill() + try: + process.wait(timeout=FORCE_KILL_DELAY) + except subprocess.TimeoutExpired: + pass + sys.exit(124) + + except FileNotFoundError: + log_error("gemini command not found in PATH") + log_error("Please install Gemini CLI: https://github.com/google/generative-ai-python") + sys.exit(127) + + except KeyboardInterrupt: + process.terminate() + try: + process.wait(timeout=FORCE_KILL_DELAY) + except subprocess.TimeoutExpired: + process.kill() + sys.exit(130) + + +if __name__ == '__main__': + main()