* 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>
129 lines
6.0 KiB
JavaScript
129 lines
6.0 KiB
JavaScript
'use strict';
|
|
/**
|
|
* Drift-guard + collapse: getRuntimeLabel must be the SINGLE source of truth for
|
|
* the install/uninstall console display label, replacing the two duplicated
|
|
* `runtimeLabel` assignment chains that previously lived in bin/install.js
|
|
* (uninstall() and install()) — the add-a-host tax ADR-1239 Phase B (#1679)
|
|
* eliminates.
|
|
*
|
|
* Verifies:
|
|
* 1. For every known runtime id, getRuntimeLabel(id) equals the hardcoded
|
|
* golden expected map — a pinned oracle that catches BOTH table bugs AND
|
|
* unintended drift (adding/removing a runtime forces a deliberate
|
|
* golden-map update here).
|
|
* 2. getRuntimeLabel('unknown') and getRuntimeLabel('') fall back to
|
|
* 'Claude Code' (the always-safe default).
|
|
* 3. The golden id set EXACTLY equals the capability-registry runtime id set
|
|
* (adding/removing a runtime forces a golden update here).
|
|
*
|
|
* Voice: these are the SHORT UI labels used in the install/uninstall console
|
|
* output, intentionally distinct from the descriptor `title` (the long product
|
|
* name, e.g. "OpenAI Codex CLI", "GitHub Copilot") which serves
|
|
* documentation/registry display. Two prior-chain inconsistencies are resolved
|
|
* by this canonical map (both move toward the majority + descriptor value):
|
|
* - kimi: install said 'Kimi', uninstall said 'Kimi CLI' → canonical 'Kimi CLI'
|
|
* - cline: install said 'Cline', uninstall omitted it (→ 'Claude Code') → canonical 'Cline'
|
|
*
|
|
* ADR-1239 Phase B (#1679).
|
|
* Behavioral tests only: assert on returned values, no source-grep.
|
|
*/
|
|
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const runtimeNamePolicy = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
|
|
const registry = require('../gsd-core/bin/lib/capability-registry.cjs');
|
|
|
|
const { getRuntimeLabel, getRuntimeNewProjectCommand } = runtimeNamePolicy;
|
|
|
|
// Golden oracle: hardcoded expected map of all 15 runtime ids to their short
|
|
// install/uninstall display label. A pinned expected value in a TEST is correct
|
|
// — the test IS the oracle (non-circular). Only PRODUCTION code should derive
|
|
// dynamically. If this map diverges from getRuntimeLabel output, either the
|
|
// table is wrong OR the registry changed — both require a deliberate golden-map
|
|
// update here.
|
|
const GOLDEN_LABEL_MAP = {
|
|
claude: 'Claude Code',
|
|
opencode: 'OpenCode',
|
|
kilo: 'Kilo',
|
|
codex: 'Codex',
|
|
copilot: 'Copilot',
|
|
antigravity: 'Antigravity',
|
|
cursor: 'Cursor',
|
|
windsurf: 'Windsurf',
|
|
augment: 'Augment',
|
|
trae: 'Trae',
|
|
qwen: 'Qwen Code',
|
|
hermes: 'Hermes Agent',
|
|
kimi: 'Kimi CLI',
|
|
codebuddy: 'CodeBuddy',
|
|
cline: 'Cline',
|
|
};
|
|
|
|
test('getRuntimeLabel: golden map matches for all 15 known runtime ids', () => {
|
|
for (const [id, expected] of Object.entries(GOLDEN_LABEL_MAP)) {
|
|
const actual = getRuntimeLabel(id);
|
|
assert.strictEqual(
|
|
actual,
|
|
expected,
|
|
`getRuntimeLabel('${id}') diverged from golden.\n` +
|
|
` actual: ${JSON.stringify(actual)}\n` +
|
|
` expected: ${JSON.stringify(expected)}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('drift guard: registry runtime id set EXACTLY equals the golden map (adding/removing a runtime forces a golden update)', () => {
|
|
// Without this, a newly-added runtime would pass (its label never checked) and
|
|
// removing `claude` could pass via the 'Claude Code' fallback. Pin the set
|
|
// both ways — mirroring the getDirName drift guard.
|
|
const registryIds = Object.keys(registry.runtimes).sort();
|
|
const goldenIds = Object.keys(GOLDEN_LABEL_MAP).sort();
|
|
assert.deepEqual(registryIds, goldenIds,
|
|
'registry.runtimes id set must exactly match GOLDEN_LABEL_MAP — update the golden map when adding/removing a runtime');
|
|
});
|
|
|
|
test('getRuntimeLabel fallback: unknown runtime returns "Claude Code"', () => {
|
|
assert.strictEqual(getRuntimeLabel('unknown'), 'Claude Code',
|
|
'getRuntimeLabel("unknown") must return "Claude Code" (default fallback)');
|
|
});
|
|
|
|
test('getRuntimeLabel fallback: empty string returns "Claude Code"', () => {
|
|
assert.strictEqual(getRuntimeLabel(''), 'Claude Code',
|
|
'getRuntimeLabel("") must return "Claude Code" (empty-input fallback)');
|
|
});
|
|
|
|
test('getRuntimeLabel fallback: alias is NOT auto-expanded (raw id match only)', () => {
|
|
// getRuntimeLabel is a raw-id lookup, not alias-aware (unlike canonicalizeRuntimeName).
|
|
// Callers pass an already-canonicalized runtime id. An alias must fall back to
|
|
// the default rather than silently matching — this keeps the label surface
|
|
// explicit and prevents a future alias from changing console output by accident.
|
|
assert.strictEqual(getRuntimeLabel('claude-code'), 'Claude Code',
|
|
'getRuntimeLabel("claude-code") must return the default "Claude Code" (raw-id match only; aliases are not expanded)');
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// getRuntimeNewProjectCommand (ADR-1239 Phase B / #1679 AC2) — the per-runtime
|
|
// /gsd-new-project invocation syntax for the post-install next-step message.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const GOLDEN_COMMAND_MAP = {
|
|
// 3 real overrides (the rest use the default):
|
|
codex: '$gsd-new-project',
|
|
cursor: 'gsd-new-project (mention the skill name)',
|
|
kimi: '/skill:gsd-new-project',
|
|
};
|
|
const DEFAULT_CMD = '/gsd-new-project';
|
|
|
|
test('getRuntimeNewProjectCommand: the 3 overrides + the default for the other 12 runtimes', () => {
|
|
for (const [id, expected] of Object.entries(GOLDEN_COMMAND_MAP)) {
|
|
assert.strictEqual(getRuntimeLabel ? getRuntimeNewProjectCommand(id) : null, expected, `override ${id}`);
|
|
}
|
|
// sanity: getRuntimeNewProjectCommand is imported alongside getRuntimeLabel above
|
|
});
|
|
|
|
test('getRuntimeNewProjectCommand: claude/unknown/empty + the 12 non-override runtimes → default', () => {
|
|
for (const id of ['claude', 'opencode', 'kilo', 'copilot', 'antigravity', 'windsurf', 'augment', 'trae', 'cline', 'qwen', 'hermes', 'codebuddy', 'unknown', '']) {
|
|
assert.strictEqual(getRuntimeNewProjectCommand(id), DEFAULT_CMD, `runtime '${id}' must return the default command`);
|
|
}
|
|
});
|