From 3f9251c4674fc4f727ae08625893fd3dc684696c Mon Sep 17 00:00:00 2001 From: Ousama Ben Younes Date: Mon, 16 Feb 2026 22:21:37 +0000 Subject: [PATCH 1/8] feat: add Gemini CLI support via rtk init --gemini Introduce first-class support for Gemini CLI by adding a dedicated initialization mode. This allows Gemini users to benefit from RTK's token optimization with the same ease as Claude Code users. - Add --gemini flag to 'rtk init' command - Implement run_gemini_mode() in src/init.rs for global setup - Automated installation of the rewrite hook in ~/.gemini/hooks/ - Intelligent GEMINI.md patching using upsert_rtk_block (preserves user content) - Support for ~/.gemini/RTK.md slim instructions - Update 'rtk init --show' to display Gemini configuration status - Update 'rtk init -g --uninstall' to clean up Gemini artifacts - Add comprehensive automated test: scripts/test-gemini-init.sh - Update INSTALL.md and README.md with Gemini setup instructions Collaboration: - Vision & Prompt Architecture: Ousama Ben Younes - Technical Implementation: Gemini AI (Google) Co-authored-by: Gemini AI --- .github/workflows/security-check.yml | 8 + INSTALL.md | 21 +- README.md | 8 +- scripts/test-all.sh | 21 ++ scripts/test-gemini-init.sh | 91 ++++++ src/init.rs | 426 +++++++++++++++++++++++++-- src/main.rs | 7 +- 7 files changed, 556 insertions(+), 26 deletions(-) create mode 100755 scripts/test-gemini-init.sh diff --git a/.github/workflows/security-check.yml b/.github/workflows/security-check.yml index 7585930..ea9085c 100644 --- a/.github/workflows/security-check.yml +++ b/.github/workflows/security-check.yml @@ -118,6 +118,14 @@ jobs: fi echo "" >> $GITHUB_STEP_SUMMARY + - name: Build + run: cargo build + + - name: Smoke Tests + run: | + export PATH=$PATH:$(pwd)/target/debug + ./scripts/test-all.sh + - name: Summary verdict run: | echo "---" >> $GITHUB_STEP_SUMMARY diff --git a/INSTALL.md b/INSTALL.md index 55b32fd..fd84108 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -89,7 +89,26 @@ rtk init -g --no-patch # Print manual instructions instead rtk init --show # Check hook is installed and executable ``` -**Token savings**: ~99.5% reduction (2000 tokens → 10 tokens in context) +### Gemini CLI Setup + +RTK integrates with Gemini CLI via a **BeforeTool hook** that automatically rewrites commands: + +```bash +rtk init -g --gemini +# → Installs ~/.gemini/hooks/rtk-rewrite.sh +# → Creates ~/.gemini/RTK.md (command reference) +# → Creates ~/.gemini/GEMINI.md (usage guide) +# → Patches ~/.gemini/settings.json (registers hook) +``` + +**Verify installation:** +```bash +gemini /hooks # Should show "rtk-rewrite" under BeforeTool +``` + +**Token savings**: 70-90% reduction on git, npm, file operations. + +**How it works**: The hook intercepts `run_shell_command` tool calls and rewrites them to their `rtk` equivalents before execution. Gemini never sees the original command. **What is settings.json?** Claude Code's hook registry. RTK adds a PreToolUse hook that rewrites commands transparently. Without this, Claude won't invoke the hook automatically. diff --git a/README.md b/README.md index 3985432..d680abd 100644 --- a/README.md +++ b/README.md @@ -106,10 +106,12 @@ Download from [rtk-ai/releases](https://github.com/rtk-ai/rtk/releases): # 1. Verify installation rtk gain # Must show token stats, not "command not found" -# 2. Initialize for Claude Code (RECOMMENDED: hook-first mode) -rtk init --global +# 2. Initialize (RECOMMENDED: hook-first mode) +rtk init --global # For Claude Code +rtk init --global --gemini # For Gemini CLI # → Installs hook + creates slim RTK.md (10 lines, 99.5% token savings) -# → Follow printed instructions to add hook to ~/.claude/settings.json +# → For Claude: Follow instructions to patch ~/.claude/settings.json +# → For Gemini: Automatically patches ~/.gemini/settings.json # 3. Test it works rtk git status # Should show ultra-compact output diff --git a/scripts/test-all.sh b/scripts/test-all.sh index 74203f4..206c8fe 100755 --- a/scripts/test-all.sh +++ b/scripts/test-all.sh @@ -333,6 +333,27 @@ section "Config & Init" assert_ok "rtk config" rtk config assert_ok "rtk init --show" rtk init --show +section "Init Gemini (global)" + +# Setup temporary home for gemini test +TEST_HOME_GEMINI=$(mktemp -d) +OLD_HOME=$HOME +export HOME=$TEST_HOME_GEMINI + +assert_ok "rtk init -g --gemini" rtk init -g --gemini +if [ -f "$HOME/.gemini/GEMINI.md" ] && [ -f "$HOME/.gemini/settings.json" ]; then + PASS=$((PASS + 1)) + printf " ${GREEN}PASS${NC} %s\n" "rtk init gemini files exist" +else + FAIL=$((FAIL + 1)) + FAILURES+=("rtk init gemini files exist") + printf " ${RED}FAIL${NC} %s\n" "rtk init gemini files exist" +fi + +# Cleanup +export HOME=$OLD_HOME +rm -rf "$TEST_HOME_GEMINI" + # ── 22. Wget ───────────────────────────────────────── section "Wget" diff --git a/scripts/test-gemini-init.sh b/scripts/test-gemini-init.sh new file mode 100755 index 0000000..649af89 --- /dev/null +++ b/scripts/test-gemini-init.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# Test Gemini Initialization for RTK + +set -e + +# Setup temporary home to avoid polluting real home +TEST_HOME=$(mktemp -d) +export HOME=$TEST_HOME +echo "Using temporary HOME: $HOME" + +# Define colors +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' + +# Paths +RTK_BIN="./target/debug/rtk" +GEMINI_DIR="$HOME/.gemini" +GEMINI_MD="$GEMINI_DIR/GEMINI.md" +RTK_MD="$GEMINI_DIR/RTK.md" +HOOK_PATH="$GEMINI_DIR/hooks/rtk-rewrite.sh" + +echo "1. Testing Fresh Gemini Installation..." +$RTK_BIN init -g --gemini > /dev/null + +if [ -f "$GEMINI_MD" ] && [ -f "$RTK_MD" ] && [ -x "$HOOK_PATH" ]; then + echo -e "${GREEN}✅ Fresh installation files created successfully${NC}" +else + echo -e "${RED}❌ Fresh installation failed${NC}" + exit 1 +fi + +if grep -q "rtk git" "$GEMINI_MD"; then + echo -e "${GREEN}✅ GEMINI.md contains correct instructions${NC}" +else + echo -e "${RED}❌ GEMINI.md content is wrong${NC}" + exit 1 +fi + +echo "2. Testing Upsert (Preserve User Content)..." +# Create a file with user content and an old RTK block +cat > "$GEMINI_MD" < +OLD RTK CONTENT + + +End of user file. +EOF + +$RTK_BIN init -g --gemini > /dev/null + +if grep -q "My custom notes here." "$GEMINI_MD" && grep -q "End of user file." "$GEMINI_MD"; then + echo -e "${GREEN}✅ User content preserved during update${NC}" +else + echo -e "${RED}❌ User content LOST during update${NC}" + exit 1 +fi + +if grep -q "rtk git" "$GEMINI_MD" && ! grep -q "OLD RTK CONTENT" "$GEMINI_MD"; then + echo -e "${GREEN}✅ RTK block updated correctly${NC}" +else + echo -e "${RED}❌ RTK block update failed${NC}" + exit 1 +fi + +echo "2b. Testing settings.json Hook Registration..." +if grep -q '"name": "rtk-rewrite"' "$GEMINI_DIR/settings.json"; then + echo -e "${GREEN}✅ Hook registered in settings.json${NC}" +else + echo -e "${RED}❌ Hook NOT registered in settings.json${NC}" + exit 1 +fi + +echo "3. Testing Uninstall..." +$RTK_BIN init -g --uninstall > /dev/null + +if [ ! -f "$GEMINI_MD" ] && [ ! -f "$RTK_MD" ] && [ ! -f "$HOOK_PATH" ]; then + echo -e "${GREEN}✅ Uninstall cleaned up all Gemini files${NC}" +else + echo -e "${RED}❌ Uninstall failed to clean up${NC}" + exit 1 +fi + +echo "" +echo -e "${GREEN}✨ ALL GEMINI INIT TESTS PASSED ✨${NC}" + +# Cleanup +rm -rf "$TEST_HOME" diff --git a/src/init.rs b/src/init.rs index 961e4ac..a528247 100644 --- a/src/init.rs +++ b/src/init.rs @@ -7,6 +7,93 @@ use tempfile::NamedTempFile; // Embedded hook script (guards before set -euo pipefail) const REWRITE_HOOK: &str = include_str!("../hooks/rtk-rewrite.sh"); +const REWRITE_GEMINI_HOOK: &str = r##"#!/usr/bin/env bash +# ~/.gemini/hooks/rtk-rewrite.sh +# Hook BeforeTool pour réécrire les commandes shell en leur équivalent rtk + +# Lire l'input JSON depuis stdin +input=$(cat) + +# Extraire la commande via jq +tool_name=$(echo "$input" | jq -r '.tool_name // ""') +tool_input=$(echo "$input" | jq -r '.tool_input // {}') + +# Si ce n'est pas run_shell_command, ignorer +if [ "$tool_name" != "run_shell_command" ]; then + echo '{"decision": "allow"}' + exit 0 +fi + +# Extraire la commande shell +command=$(echo "$tool_input" | jq -r '.command // ""') + +# Log debug (stderr seulement) +# echo "[rtk-rewrite] Original command: $command" >&2 + +# Mappings de commandes (identique à la logique Claude Code) +case "$command" in + git\ status*|git\ diff*|git\ log*|git\ add*|git\ commit*|git\ push*|git\ pull*|git\ branch*|git\ fetch*|git\ stash*) + new_cmd="rtk ${command}" + ;; + cat\ *) + new_cmd="rtk read ${command#cat }" + ;; + "grep "*|"rg "*) + new_cmd="rtk ${command}" + ;; + ls*) + new_cmd="rtk ls${command#ls}" + ;; + npm\ run\ *|npm\ test*|pnpm\ test*) + new_cmd="rtk ${command}" + ;; + tsc*|npx\ tsc*|pnpm\ tsc*) + new_cmd="rtk tsc" + ;; + eslint*|npx\ eslint*|pnpm\ lint*) + new_cmd="rtk lint" + ;; + vitest*|playwright*|prettier*) + new_cmd="rtk ${command}" + ;; + docker\ ps*|docker\ images*|docker\ logs*) + new_cmd="rtk docker ${command#docker }" + ;; + kubectl\ get*|kubectl\ logs*) + new_cmd="rtk kubectl ${command#kubectl }" + ;; + curl\ *|wget\ *) + new_cmd="rtk ${command}" + ;; + rtk\ *) + # Déjà une commande rtk, ne pas réécrire + echo '{"decision": "allow"}' + exit 0 + ;; + *) + # Commande non reconnue, laisser passer + echo '{"decision": "allow"}' + exit 0 + ;; +esac + +# Log de la transformation (stderr) +# echo "[rtk-rewrite] Rewritten to: $new_cmd" >&2 + +# Retourner le JSON avec la commande modifiée +cat < "##; +const GEMINI_MD: &str = r##" +# RTK Usage Guide for Gemini CLI + +## What is RTK? +RTK (Rust Token Killer) reduces AI token consumption by 70-90% on command outputs. + +## How it works with Gemini CLI +RTK is integrated via a **BeforeTool hook** that automatically rewrites shell commands. +Configuration: `~/.gemini/settings.json` → `hooks.BeforeTool` + +## Command Mappings (Auto-Rewrite) +When you (or Gemini) run these commands, they're automatically rewritten: + +### Git +- `git status/diff/log/add/commit/push/pull/branch/fetch/stash` → `rtk git ` + +### Files +- `cat ` → `rtk read ` +- `grep/rg ` → `rtk grep ` +- `ls` → `rtk ls` + +### JavaScript/TypeScript +- `npm run