* feat(#1928): remove sunset gemini cli runtime, redirect to antigravity Google sunset Gemini CLI on 2026-06-18; Antigravity CLI is its official successor (already a first-class GSD runtime). Remove the gemini runtime from the enum (16->15), aliases, labels, config-home fragment, install path, converters (convertClaudeToGemini{Markdown,Toml,Agent}, convertSlashCommandsToGeminiMentions), capability descriptor, gemini-extension.json, RULESET.GEMINI.*, and the interactive menu (renumbered, no gap). --gemini now prints an explicit deprecation notice citing the 2026-06-18 sunset and redirects to --antigravity (no silent alias, per the issue's Hyrum's-Law rejection). Antigravity is preserved throughout: its GEMINI.md contextFileName, .gemini/antigravity config home, the shared convertGeminiToolName/claudeToGeminiTools tool vocabulary, and the 'gemini' hookEvents dialect it declares. GEMINI.md retargeted as Antigravity's context file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(#1928): backfill changeset PR number (#1996) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(#1928): drop Gemini CLI from issue templates (review nit) Removes the sunset Gemini CLI runtime from the two GitHub issue-template runtime lists that the removal PR missed, per @davesienkowski's review nit: - feature_request.yml: 'Applicable runtimes' checkbox (a user could otherwise request a feature for a runtime GSD no longer supports) - bug_report.yml: 'Runtime' dropdown + the stale ~/.gemini/settings.json retrieval-help line Leaves the post-removal templates fully consistent with the Antigravity redirect. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1132,7 +1132,7 @@ function cmdGenerateClaudeMd(cwd: string, options: CmdGenerateClaudeMdOptions, r
|
||||
// project-instruction-file`). Previously this was a codex-only override
|
||||
// (#3163) that left AGENTS-native runtimes (opencode/kilo/kimi) emitting
|
||||
// CLAUDE.md; copilot now resolves to .github/copilot-instructions.md, and
|
||||
// antigravity/gemini to GEMINI.md. GSD_RUNTIME env var takes precedence
|
||||
// antigravity to GEMINI.md. GSD_RUNTIME env var takes precedence
|
||||
// over config.runtime, mirroring detectRuntime().
|
||||
//
|
||||
// Non-claude runtimes always win over a stale `claude_md_path` (the #3163
|
||||
|
||||
@@ -1127,7 +1127,7 @@ function writeCopilotHookConfig(targetDir: string): string {
|
||||
//
|
||||
// MUTATES `settings` by reference — registers all GSD-managed hook entries
|
||||
// into settings.hooks.* for runtimes that use a settings.json hook surface
|
||||
// (Claude Code, Gemini, Antigravity, Qwen Code, and others).
|
||||
// (Claude Code, Antigravity, Qwen Code, and others).
|
||||
// Skipped entirely for runtimes whose hooksSurface descriptor field is 'none'
|
||||
// (opencode and kilo, which have their own hook surface).
|
||||
//
|
||||
@@ -1138,7 +1138,7 @@ function writeCopilotHookConfig(targetDir: string): string {
|
||||
//
|
||||
// @param settings - The settings object already read from disk. Mutated in place.
|
||||
// @param opts - Closure values the block read from install()'s scope.
|
||||
// runtime - runtime ID string (e.g. 'claude', 'gemini', 'qwen')
|
||||
// runtime - runtime ID string (e.g. 'claude', 'antigravity', 'qwen')
|
||||
// hooksSurface - descriptor hooksSurface field ('settings-json'|'none'|…); if !== 'none', hooks are written
|
||||
// isGlobal - true for global installs
|
||||
// targetDir - absolute path to the runtime config dir
|
||||
@@ -1290,7 +1290,7 @@ function applySettingsJsonHooks(settings: any, opts: ApplySettingsJsonHooksOpts)
|
||||
// Configure PreToolUse hook for prompt injection detection
|
||||
// ADR-857 phase 5f-2: drive dialect from opts.hookEvents (registry descriptor).
|
||||
// hookEvents='gemini' → BeforeTool; all others → PreToolUse.
|
||||
// Equivalence: hookEvents='gemini' iff runtime∈{gemini,antigravity} (same as old check).
|
||||
// Equivalence: hookEvents='gemini' iff runtime===antigravity (same as old check).
|
||||
const preToolEvent = hookEvents === 'gemini' ? 'BeforeTool' : 'PreToolUse';
|
||||
if (!settings.hooks[preToolEvent]) {
|
||||
settings.hooks[preToolEvent] = [];
|
||||
@@ -1583,9 +1583,14 @@ function applySettingsJsonHooks(settings: any, opts: ApplySettingsJsonHooksOpts)
|
||||
}
|
||||
// ── end SubagentStop / Stop / PreCompact events ────────────────────────────
|
||||
|
||||
// ── Gemini-only extended hook events (#776) ───────────────────────────────
|
||||
// Gemini CLI exposes several hook events beyond BeforeTool/AfterTool that
|
||||
// gsd previously did not register. Three high-value events are added here:
|
||||
// ── Extended hook events (#776; Gemini runtime removed #1928) ──────────────
|
||||
// The Gemini-3-backend dialect exposes several hook events beyond
|
||||
// BeforeTool/AfterTool. These were added for the now-removed Gemini CLI
|
||||
// runtime (#776). No currently supported runtime declares them —
|
||||
// Antigravity's descriptor carries `extendedHookEvents: []` — so this loop
|
||||
// is an inert, descriptor-driven seam: it no-ops for every present runtime
|
||||
// and re-activates automatically if a future runtime declares any of them.
|
||||
// Three high-value events would be wired here:
|
||||
//
|
||||
// BeforeAgent — fires after user submits a prompt, before the agent
|
||||
// plans. Wire gsd-context-monitor for context headroom
|
||||
@@ -1606,16 +1611,16 @@ function applySettingsJsonHooks(settings: any, opts: ApplySettingsJsonHooksOpts)
|
||||
// gsd hook use case at this time; deferred to a follow-on issue.
|
||||
//
|
||||
// Guard is now descriptor-driven: only events present in extendedEvents are wired.
|
||||
for (const geminiEvent of ['BeforeAgent', 'AfterAgent', 'BeforeModel']) {
|
||||
if (!extendedEvents.includes(geminiEvent)) continue;
|
||||
if (!Array.isArray(settings.hooks[geminiEvent])) {
|
||||
settings.hooks[geminiEvent] = [];
|
||||
for (const extendedEvent of ['BeforeAgent', 'AfterAgent', 'BeforeModel']) {
|
||||
if (!extendedEvents.includes(extendedEvent)) continue;
|
||||
if (!Array.isArray(settings.hooks[extendedEvent])) {
|
||||
settings.hooks[extendedEvent] = [];
|
||||
}
|
||||
const alreadyHasContextMonitor = settings.hooks[geminiEvent].some((entry: HookGroup) =>
|
||||
const alreadyHasContextMonitor = settings.hooks[extendedEvent].some((entry: HookGroup) =>
|
||||
entry.hooks && entry.hooks.some((h: HookEntry) => referencesHook(h as Record<string, unknown>, 'gsd-context-monitor'))
|
||||
);
|
||||
if (!alreadyHasContextMonitor && fs.existsSync(contextMonitorFile) && contextMonitorCommand) {
|
||||
settings.hooks[geminiEvent].push({
|
||||
settings.hooks[extendedEvent].push({
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
@@ -1624,12 +1629,12 @@ function applySettingsJsonHooks(settings: any, opts: ApplySettingsJsonHooksOpts)
|
||||
}
|
||||
]
|
||||
});
|
||||
console.log(` ${green}✓${reset} Configured ${geminiEvent} context monitor hook (Gemini)`);
|
||||
console.log(` ${green}✓${reset} Configured ${extendedEvent} context monitor hook`);
|
||||
} else if (!alreadyHasContextMonitor && !fs.existsSync(contextMonitorFile)) {
|
||||
console.warn(` ${yellow}⚠${reset} Skipped ${geminiEvent} hook — gsd-context-monitor.js not found at target`);
|
||||
console.warn(` ${yellow}⚠${reset} Skipped ${extendedEvent} hook — gsd-context-monitor.js not found at target`);
|
||||
}
|
||||
}
|
||||
// ── end Gemini-only extended hook events ──────────────────────────────────
|
||||
// ── end Antigravity-only extended hook events ──────────────────────────────
|
||||
|
||||
// ── FileChanged hook: hot-reload gsd config on .planning/config.json edits ─
|
||||
// Claude Code fires FileChanged when a watched file changes on disk. Wire
|
||||
|
||||
@@ -18,7 +18,6 @@ const FALLBACK_ALIASES: Readonly<Record<string, string[]>> = {
|
||||
claude: ['claude', 'claude-code', 'claude-cli'],
|
||||
opencode: ['opencode', 'open-code', 'opencode-cli'],
|
||||
kilo: ['kilo', 'kilo-cli'],
|
||||
gemini: ['gemini', 'gemini-cli', 'gemini-code'],
|
||||
codex: ['codex', 'codex-app', 'codex-cli', 'codex_desktop', 'codex-desktop'],
|
||||
copilot: ['copilot', 'copilot-cli', 'github-copilot'],
|
||||
antigravity: ['antigravity', 'antigravity-cli', 'antigravity-agent'],
|
||||
@@ -102,7 +101,7 @@ export function resolveRuntimeNameFromCandidates(...candidates: unknown[]): stri
|
||||
* claude → .claude/CLAUDE.md
|
||||
* codex, opencode, kilo, kimi → AGENTS.md
|
||||
* copilot → .github/copilot-instructions.md
|
||||
* antigravity, gemini → GEMINI.md
|
||||
* antigravity → GEMINI.md
|
||||
* unknown / future runtimes → AGENTS.md (safe cross-agent default)
|
||||
*
|
||||
* Source-of-truth references for each runtime's read path:
|
||||
@@ -114,7 +113,9 @@ export function resolveRuntimeNameFromCandidates(...candidates: unknown[]): stri
|
||||
* 'copilot-instructions' writes the same `.github/copilot-instructions.md`.)
|
||||
* - codex/opencode/kilo/kimi: AGENTS.md is the documented cross-agent
|
||||
* instruction file (agentsmd/agents.md convention).
|
||||
* - antigravity/gemini: GEMINI.md is Gemini CLI's contextFileName.
|
||||
* - antigravity: GEMINI.md is Antigravity CLI's contextFileName (the Gemini
|
||||
* CLI runtime that historically shared this file was removed — #1928;
|
||||
* Google sunset Gemini CLI 2026-06-18 and Antigravity CLI is its successor).
|
||||
*
|
||||
* Aliases are normalized via `canonicalizeRuntimeName` first, so inputs like
|
||||
* `codex-cli` resolve to `codex` → `AGENTS.md`. Replaces the prior codex-only
|
||||
@@ -125,7 +126,7 @@ export function getProjectInstructionFile(runtime: unknown): string {
|
||||
const canonical = canonicalizeRuntimeName(runtime);
|
||||
if (canonical === 'claude') return '.claude/CLAUDE.md';
|
||||
if (canonical === 'copilot') return '.github/copilot-instructions.md';
|
||||
if (canonical === 'antigravity' || canonical === 'gemini') return 'GEMINI.md';
|
||||
if (canonical === 'antigravity') return 'GEMINI.md';
|
||||
// codex, opencode, kilo, kimi, AND unknown/future runtimes all default to
|
||||
// root AGENTS.md (the safe cross-agent instruction file).
|
||||
return 'AGENTS.md';
|
||||
@@ -167,7 +168,7 @@ export function getDirName(runtime: string): string {
|
||||
*
|
||||
* Voice: these are the SHORT UI labels, intentionally distinct from the
|
||||
* descriptor `title` (the long product name — e.g. "OpenAI Codex CLI",
|
||||
* "GitHub Copilot", "Gemini CLI") which serves documentation/registry display,
|
||||
* "GitHub Copilot") which serves documentation/registry display,
|
||||
* not the install console. A future slice may relocate this to a
|
||||
* `runtime.label` descriptor field; until then this table is the source.
|
||||
*
|
||||
@@ -182,7 +183,6 @@ export function getDirName(runtime: string): string {
|
||||
const RUNTIME_LABELS: Readonly<Record<string, string>> = {
|
||||
claude: 'Claude Code',
|
||||
opencode: 'OpenCode',
|
||||
gemini: 'Gemini',
|
||||
kilo: 'Kilo',
|
||||
codex: 'Codex',
|
||||
copilot: 'Copilot',
|
||||
@@ -219,7 +219,7 @@ export function getRuntimeLabel(runtime: string): string {
|
||||
* bin/install.js (ADR-1239 Phase B / #1679, AC2 slice 2) — the add-a-host tax:
|
||||
* a new runtime meant remembering to add a branch here. Values are preserved
|
||||
* BYTE-FOR-BYTE from the prior chain; golden install parity asserts generated
|
||||
* hook output is unchanged across all 16 runtimes.
|
||||
* hook output is unchanged across all 15 runtimes.
|
||||
*
|
||||
* Two runtimes are intentionally absent (handled by the caller, NOT this table):
|
||||
* - `claude` → the default; falls through to `DEFAULT_FRAGMENT`.
|
||||
@@ -232,7 +232,6 @@ const DEFAULT_CONFIG_HOME_FRAGMENT = "'.claude'";
|
||||
const GLOBAL_CONFIG_HOME_FRAGMENTS: Readonly<Record<string, string>> = {
|
||||
copilot: "'.copilot'",
|
||||
opencode: "'.config', 'opencode'",
|
||||
gemini: "'.gemini'",
|
||||
kilo: "'.config', 'kilo'",
|
||||
codex: "'.codex'",
|
||||
cursor: "'.cursor'",
|
||||
@@ -266,7 +265,7 @@ export function getGlobalConfigHomeFragment(runtime: string): string {
|
||||
* removes).
|
||||
*/
|
||||
const RUNTIME_FLAG_IDS = Object.freeze([
|
||||
'opencode', 'kilo', 'gemini', 'codex', 'copilot', 'antigravity', 'cursor',
|
||||
'opencode', 'kilo', 'codex', 'copilot', 'antigravity', 'cursor',
|
||||
'windsurf', 'augment', 'trae', 'qwen', 'hermes', 'codebuddy', 'cline', 'kimi',
|
||||
] as const);
|
||||
|
||||
@@ -294,7 +293,6 @@ export function runtimeFlags(runtime: string): Readonly<Record<string, boolean>>
|
||||
*/
|
||||
const DEFAULT_NEW_PROJECT_COMMAND = '/gsd-new-project';
|
||||
const RUNTIME_NEW_PROJECT_COMMANDS: Readonly<Record<string, string>> = {
|
||||
gemini: '/gsd:new-project',
|
||||
codex: '$gsd-new-project',
|
||||
cursor: 'gsd-new-project (mention the skill name)',
|
||||
kimi: '/skill:gsd-new-project',
|
||||
|
||||
@@ -24,16 +24,20 @@ import childProcess from 'node:child_process';
|
||||
* runtime/shell combination.
|
||||
*
|
||||
* Current evidence-backed policy:
|
||||
* - Gemini on Windows requires `& ` for quoted node/bash runners.
|
||||
* - Claude Code on Windows does NOT: its hook commands execute under bash/Git
|
||||
* Bash and `& ` breaks there (#3413).
|
||||
* - Claude Code on Windows does NOT need it: its hook commands execute under
|
||||
* bash/Git Bash and `& ` breaks there (#3413).
|
||||
* - #1928: Gemini CLI — the ONLY runtime with a verified need for the `& `
|
||||
* prefix on Windows — was removed (Google sunset it 2026-06-18). No currently
|
||||
* supported runtime has a verified need, so this seam is now inert. It is
|
||||
* retained (not deleted) so a future runtime with a verified need is a
|
||||
* one-line re-enable, per the conservative policy below. Note: Antigravity —
|
||||
* the Gemini-backend successor — never matched the old `runtime === 'gemini'`
|
||||
* check, so its behavior (no prefix) is unchanged.
|
||||
*
|
||||
* Keep the policy conservative until another runtime has a verified need.
|
||||
*/
|
||||
export function hookCommandNeedsPowerShellCallOperator(opts: { platform?: string; runtime?: string } = {}): boolean {
|
||||
const platform = opts.platform || process.platform;
|
||||
const runtime = opts.runtime || 'generic';
|
||||
return platform === 'win32' && runtime === 'gemini';
|
||||
export function hookCommandNeedsPowerShellCallOperator(_opts: { platform?: string; runtime?: string } = {}): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,7 +100,7 @@ export function formatManagedHookScriptToken(scriptPath: string, opts: { platfor
|
||||
|
||||
export function projectLocalHookPrefix({ runtime = 'claude', dirName }: { runtime?: string; dirName?: string | null }): string | undefined | null {
|
||||
if (!dirName) return dirName;
|
||||
return (runtime === 'gemini' || runtime === 'antigravity')
|
||||
return (runtime === 'antigravity')
|
||||
? dirName
|
||||
: `"$CLAUDE_PROJECT_DIR"/${dirName}`;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ export const RUNTIME_DIRS: RuntimeDirEntry[] = [
|
||||
['antigravity', '.agent'], // local Antigravity install dir legacy (#503; backward-compat with pre-#791 installs)
|
||||
['windsurf', '.windsurf'], // local Windsurf workflow dir canonical (#1615; bin/install.js getDirName('windsurf'))
|
||||
['windsurf', '.devin'], // local Devin Desktop install dir legacy (#1085; backward-compat)
|
||||
['gemini', '.gemini'],
|
||||
['kilo', '.config/kilo'],
|
||||
['kilo', '.kilo'],
|
||||
['codex', '.codex'],
|
||||
@@ -86,7 +85,6 @@ export function inferPreferredRuntime({ fs, env, preferredConfigDir }: InferPref
|
||||
}
|
||||
if (env['CODEX_HOME']) return 'codex';
|
||||
if (env['ANTIGRAVITY_CONFIG_DIR']) return 'antigravity';
|
||||
if (env['GEMINI_CONFIG_DIR']) return 'gemini';
|
||||
if (env['KILO_CONFIG_DIR'] || env['KILO_CONFIG']) return 'kilo';
|
||||
if (env['OPENCODE_CONFIG_DIR'] || env['OPENCODE_CONFIG']) return 'opencode';
|
||||
if (env['CLAUDE_CONFIG_DIR']) return 'claude';
|
||||
@@ -104,7 +102,6 @@ export function envRuntimeDirs({ env, home }: EnvRuntimeDirsOpts): RuntimeDirEnt
|
||||
const ex = (v: string | undefined) => expandHome(v, home);
|
||||
if (env['CLAUDE_CONFIG_DIR']) out.push(['claude', ex(env['CLAUDE_CONFIG_DIR'])]);
|
||||
if (env['ANTIGRAVITY_CONFIG_DIR']) out.push(['antigravity', ex(env['ANTIGRAVITY_CONFIG_DIR'])]);
|
||||
if (env['GEMINI_CONFIG_DIR']) out.push(['gemini', ex(env['GEMINI_CONFIG_DIR'])]);
|
||||
if (env['KILO_CONFIG_DIR']) out.push(['kilo', ex(env['KILO_CONFIG_DIR'])]);
|
||||
else if (env['KILO_CONFIG']) out.push(['kilo', path.dirname(ex(env['KILO_CONFIG']))]);
|
||||
else if (env['XDG_CONFIG_HOME']) out.push(['kilo', path.join(ex(env['XDG_CONFIG_HOME']), 'kilo')]);
|
||||
|
||||
Reference in New Issue
Block a user