diff --git a/README.md b/README.md index b76f3da..dc23698 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,6 @@ Supported engines: - `claude` - `agy` (Antigravity CLI) -- `gemini` (deprecated — see note below) - `codex` Built-in defaults are applied when `engines..args` is not set. @@ -142,9 +141,9 @@ For `claude`, defaults include: - `-p --model haiku` - `--settings "{\"attribution\":{\"commit\":\"\",\"pr\":\"\"}}"` (prevents automatic `Co-authored-by` metadata) -If no engine is configured, auto-detection tries commands in this order: `claude` → `agy` → `gemini` → `codex`. The first available command is used. +If no engine is configured, auto-detection tries commands in this order: `claude` → `agy` → `codex`. The first available command is used. -> **Gemini CLI deprecation:** Google is transitioning Gemini CLI to Antigravity CLI (`agy`). After **June 18, 2026**, Gemini CLI stops serving requests for Google AI Pro/Ultra subscribers and free users. Gemini CLI continues to work for users on Gemini Code Assist Standard/Enterprise or paid API keys. If you previously used `engine = "gemini"`, switch to `engine = "agy"`. +> **Gemini CLI removal:** Google replaced Gemini CLI with Antigravity CLI (`agy`), and Gemini CLI stopped serving requests for Google AI Pro/Ultra subscribers and free users on **June 18, 2026**. `gemini` is no longer a built-in engine: it is not auto-detected (with only Gemini CLI installed you get `no engine configured`), and `engine = "gemini"` without explicit arguments now fails with a message pointing at `agy`. Switch to `engine = "agy"`, or keep running Gemini CLI by setting `engines.gemini.args` yourself. Any other engine name is treated as a direct command and executed with the prompt on stdin. diff --git a/internal/app/app.go b/internal/app/app.go index 303ef64..b5a970d 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -122,6 +122,9 @@ func selectEngine(cfg config.Config) (engine.Engine, string, error) { if args, ok := config.DefaultEngineArgs[name]; ok { return engine.CLI{Command: name, Args: args}, strings.Join(append([]string{name}, args...), " "), nil } + if name == "gemini" { + return nil, "", fmt.Errorf("engine %q is no longer built in: Gemini CLI was replaced by Antigravity CLI. Use engine = \"agy\", or set engines.gemini.args explicitly", name) + } return engine.CLI{Command: name, Args: nil}, name, nil } diff --git a/internal/app/app_test.go b/internal/app/app_test.go index 61e4836..9516e48 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -158,16 +158,32 @@ func TestSelectEngineCursorAgentDefault(t *testing.T) { } } -func TestSelectEngineGeminiDefault(t *testing.T) { +func TestSelectEngineGeminiWithoutArgsFails(t *testing.T) { cfg := config.Default() cfg.DefaultEngine = "gemini" cfg.Engines = map[string]config.EngineConfig{} + _, _, err := selectEngine(cfg) + if err == nil { + t.Fatalf("selectEngine error = nil, want a migration error") + } + if !strings.Contains(err.Error(), "agy") { + t.Fatalf("selectEngine error = %v, want it to suggest agy", err) + } +} + +func TestSelectEngineGeminiWithExplicitArgs(t *testing.T) { + cfg := config.Default() + cfg.DefaultEngine = "gemini" + cfg.Engines = map[string]config.EngineConfig{ + "gemini": {Args: []string{"-p", "{{prompt}}"}}, + } + _, command, err := selectEngine(cfg) if err != nil { t.Fatalf("selectEngine error: %v", err) } - if command != "gemini -m gemini-2.5-flash -p {{prompt}}" { + if command != "gemini -p {{prompt}}" { t.Fatalf("command = %q", command) } } diff --git a/internal/config/config.go b/internal/config/config.go index 7f3ff44..5453b7d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -55,7 +55,6 @@ var DefaultEngineArgs = map[string][]string{ "codex": {"exec", "--model", "gpt-5.6-luna"}, "claude": {"-p", "--model", "haiku", "--settings", "{\"attribution\":{\"commit\":\"\",\"pr\":\"\"}}", "--no-session-persistence"}, "cursor-agent": {"-p"}, - "gemini": {"-m", "gemini-2.5-flash", "-p", "{{prompt}}"}, "agy": {"-p", "{{prompt}}"}, } @@ -354,7 +353,7 @@ func LoadPromptPreset(name string) (string, error) { } func autodetectEngine() string { - candidates := []string{"claude", "agy", "gemini", "codex"} + candidates := []string{"claude", "agy", "codex"} for _, name := range candidates { if _, err := exec.LookPath(name); err == nil { return name diff --git a/internal/config/config_test.go b/internal/config/config_test.go index df51b19..2930754 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -65,7 +65,7 @@ func TestAutodetectEngineOrder(t *testing.T) { t.Fatalf("mkdir bin: %v", err) } makeExecutable(t, binDir, "codex") - makeExecutable(t, binDir, "gemini") + makeExecutable(t, binDir, "agy") makeExecutable(t, binDir, "claude") t.Setenv("PATH", binDir) @@ -82,14 +82,35 @@ func TestAutodetectEngineOrder(t *testing.T) { }) } -func TestAutodetectEnginePrefersAgyOverGemini(t *testing.T) { +func TestAutodetectEngineIgnoresGemini(t *testing.T) { configHome := t.TempDir() binDir := filepath.Join(configHome, "bin") if err := os.MkdirAll(binDir, 0o755); err != nil { t.Fatalf("mkdir bin: %v", err) } - makeExecutable(t, binDir, "codex") makeExecutable(t, binDir, "gemini") + + t.Setenv("PATH", binDir) + t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + + withDir(t, t.TempDir(), func() { + cfg, err := Load() + if err != nil { + t.Fatalf("Load error: %v", err) + } + if cfg.DefaultEngine != "" { + t.Fatalf("DefaultEngine = %q, want empty (gemini is no longer autodetected)", cfg.DefaultEngine) + } + }) +} + +func TestAutodetectEnginePrefersAgyOverCodex(t *testing.T) { + configHome := t.TempDir() + binDir := filepath.Join(configHome, "bin") + if err := os.MkdirAll(binDir, 0o755); err != nil { + t.Fatalf("mkdir bin: %v", err) + } + makeExecutable(t, binDir, "codex") makeExecutable(t, binDir, "agy") t.Setenv("PATH", binDir) @@ -106,6 +127,12 @@ func TestAutodetectEnginePrefersAgyOverGemini(t *testing.T) { }) } +func TestDefaultEngineArgsHasNoGemini(t *testing.T) { + if _, ok := DefaultEngineArgs["gemini"]; ok { + t.Fatalf("DefaultEngineArgs still contains gemini") + } +} + func makeExecutable(t *testing.T, dir, name string) { t.Helper() path := filepath.Join(dir, name) @@ -134,7 +161,7 @@ func TestLoadRepoConfigOverrides(t *testing.T) { t.Fatalf("mkdir config dir: %v", err) } configPath := filepath.Join(configDir, "config.toml") - if err := os.WriteFile(configPath, []byte("engine = 'gemini'\n"), 0o644); err != nil { + if err := os.WriteFile(configPath, []byte("engine = 'agy'\n"), 0o644); err != nil { t.Fatalf("write xdg config: %v", err) } @@ -576,7 +603,7 @@ func TestNonHiddenRepoConfigPrecedence(t *testing.T) { t.Fatalf("write non-hidden config: %v", err) } hidden := filepath.Join(repo, ".git-ai-commit.toml") - if err := os.WriteFile(hidden, []byte("engine = 'gemini'\n"), 0o644); err != nil { + if err := os.WriteFile(hidden, []byte("engine = 'agy'\n"), 0o644); err != nil { t.Fatalf("write hidden config: %v", err) } diff --git a/internal/config/gitconfig_test.go b/internal/config/gitconfig_test.go index a39269a..164a014 100644 --- a/internal/config/gitconfig_test.go +++ b/internal/config/gitconfig_test.go @@ -258,9 +258,9 @@ func TestGitConfigLocalOverridesRepoToml(t *testing.T) { isolateGitConfig(t) t.Setenv("XDG_CONFIG_HOME", t.TempDir()) - // Repo TOML sets engine to "gemini" + // Repo TOML sets engine to "agy" repoConfig := filepath.Join(repo, ".git-ai-commit.toml") - if err := os.WriteFile(repoConfig, []byte("engine = 'gemini'\n"), 0o644); err != nil { + if err := os.WriteFile(repoConfig, []byte("engine = 'agy'\n"), 0o644); err != nil { t.Fatalf("write repo config: %v", err) } trustRepoConfig(t, repo, repoConfig) @@ -285,14 +285,14 @@ func TestGitConfigLocalOverridesUserToml(t *testing.T) { repo := initTestRepo(t) isolateGitConfig(t) - // User TOML sets engine to "gemini" + // User TOML sets engine to "agy" configHome := t.TempDir() t.Setenv("XDG_CONFIG_HOME", configHome) configDir := filepath.Join(configHome, "git-ai-commit") if err := os.MkdirAll(configDir, 0o755); err != nil { t.Fatalf("mkdir config dir: %v", err) } - if err := os.WriteFile(filepath.Join(configDir, "config.toml"), []byte("engine = 'gemini'\n"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(configDir, "config.toml"), []byte("engine = 'agy'\n"), 0o644); err != nil { t.Fatalf("write user config: %v", err) } @@ -316,9 +316,9 @@ func TestGitConfigGlobalOverriddenByUserToml(t *testing.T) { tmp := t.TempDir() t.Setenv("GIT_CONFIG_NOSYSTEM", "1") - // Global git config sets engine to "gemini" + // Global git config sets engine to "agy" globalCfg := filepath.Join(tmp, "global.gitconfig") - if err := os.WriteFile(globalCfg, []byte("[ai-commit]\n\tengine = gemini\n"), 0o644); err != nil { + if err := os.WriteFile(globalCfg, []byte("[ai-commit]\n\tengine = agy\n"), 0o644); err != nil { t.Fatalf("write global config: %v", err) } t.Setenv("GIT_CONFIG_GLOBAL", globalCfg) diff --git a/scripts/release-check.sh b/scripts/release-check.sh index 2793159..361dd03 100755 --- a/scripts/release-check.sh +++ b/scripts/release-check.sh @@ -8,7 +8,7 @@ TIMESTAMP=$(date +%Y%m%d-%H%M%S) REPORT_DIR="$REPORT_BASE/acceptance-$TIMESTAMP" SUMMARY="$REPORT_DIR/summary.md" -ENGINES="claude gemini codex" +ENGINES="claude agy codex" PRESETS="conventional default gitmoji karma" mkdir -p "$REPORT_DIR" @@ -44,12 +44,12 @@ engine = "claude" args = ["-p", "--model", "haiku"] EOC ;; - gemini) + agy) cat > "$tmp_cfg/git-ai-commit/config.toml" <<'EOC' -engine = "gemini" +engine = "agy" -[engines.gemini] -args = ["-m", "gemini-2.5-flash", "-p", "{{prompt}}"] +[engines.agy] +args = ["-p", "{{prompt}}"] EOC ;; codex)