Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ Supported engines:

- `claude`
- `agy` (Antigravity CLI)
- `gemini` (deprecated — see note below)
- `codex`

Built-in defaults are applied when `engines.<name>.args` is not set.
Expand All @@ -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.

Expand Down
3 changes: 3 additions & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment on lines +125 to +126

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject Gemini entries that omit args

When a config contains engine = "gemini" and an empty [engines.gemini] table, TOML decoding creates a map entry whose Args is nil, so the earlier cfg.Engines[name] branch returns successfully before this migration guard is reached. In that scenario the removed built-in still runs as bare gemini with the prompt on stdin instead of producing the documented migration error; require the Gemini entry to contain an explicit args value before allowing it to bypass this guard.

Useful? React with 👍 / 👎.

}
return engine.CLI{Command: name, Args: nil}, name, nil
}

Expand Down
20 changes: 18 additions & 2 deletions internal/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
3 changes: 1 addition & 2 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}}"},
}

Expand Down Expand Up @@ -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
Expand Down
37 changes: 32 additions & 5 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
}

Expand Down
12 changes: 6 additions & 6 deletions internal/config/gitconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}

Expand All @@ -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)
Expand Down
10 changes: 5 additions & 5 deletions scripts/release-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down