refactor(#1679): ADR-1239 Phase B — collapse runtimeLabel chains into getRuntimeLabel [AC2 slice 1/6] (#1800)

* refactor(#1679): ADR-1239 Phase B — collapse runtimeLabel chains into getRuntimeLabel

Collapses the two duplicated runtimeLabel assignment chains in bin/install.js
(uninstall() and install()) into a single getRuntimeLabel(runtime) lookup in
src/runtime-name-policy.cts — a curated short-form label table, sibling to the
registry-derived getDirName precedent.

This is slice 1 of AC2 (regional residue-collapse in install.js) under
ADR-1239 Phase B / #1679. The install/uninstall console label was the add-a-host
tax poster child: a new runtime meant adding a label line to BOTH chains, and
they had drifted out of sync:
  - kimi:  install 'Kimi' / uninstall 'Kimi CLI'  -> canonical 'Kimi CLI'
  - cline: install 'Cline' / uninstall (omitted)  -> canonical 'Cline'

Each canonical value matches the majority chain AND the descriptor title.

Behavior:
- 14 of 16 runtime labels unchanged in both sites (zero observable change).
- 2 unifications (kimi-install, cline-uninstall) move toward consistency.
- Unknown/empty runtime id fails closed to 'Claude Code'.
- Raw-id lookup only (no alias expansion); callers pass canonicalized ids.

Voice: these SHORT UI labels are intentionally distinct from the descriptor
title (the long product name) which serves docs/registry display, not the
console. A future slice may relocate this to a runtime.label descriptor field.

Verification:
- TDD: tests/runtime-label-policy.test.cjs (golden map + drift guard + fallbacks)
- 16-runtime golden install parity: byte-identical (labels are stdout-only)
- 162-test neighbor cluster green; eslint + test-file-count + regression-names clean
- runtime === count in install.js: 129 -> 115 (-14, the uninstalled label chain)

* chore(changeset): add Changed fragment for runtimeLabel collapse (#1679)

PR #1800 touches bin/ → changeset-required gate. Mirrors the sibling
ADR-1239 Phase B slice (eager-elks-frolic): type Changed + docs-exempt
marker (internal refactor, no user-facing doc surface).
This commit is contained in:
Tom Boucher
2026-06-28 00:05:33 -04:00
committed by GitHub
parent 21f0b4316f
commit ad79e99fc9
4 changed files with 174 additions and 32 deletions

View File

@@ -151,3 +151,60 @@ export function getDirName(runtime: string): string {
if (typeof dir === 'string' && dir.length > 0) return dir;
return '.claude';
}
/**
* Curated short display labels for the install/uninstall console output, keyed
* by canonical runtime id. The SINGLE source of truth consumed by both
* `install()` and `uninstall()` in bin/install.js via `getRuntimeLabel`.
*
* Collapses the two duplicated `runtimeLabel` assignment chains that previously
* lived inline in bin/install.js (ADR-1239 Phase B, #1679) — the add-a-host tax:
* a new runtime meant remembering to add a label line in BOTH chains, and they
* had drifted out of sync (uninstall omitted `cline` and used a different
* `kimi` value than install). This table is the curated canonical resolution:
* - kimi: install 'Kimi' / uninstall 'Kimi CLI' → 'Kimi CLI' (majority + descriptor title)
* - cline: install 'Cline' / uninstall (omitted) → 'Cline' (majority + descriptor title)
*
* 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,
* not the install console. A future slice may relocate this to a
* `runtime.label` descriptor field; until then this table is the source.
*
* Lookup is RAW-ID only (no alias expansion) — callers pass an already-
* canonicalized runtime id, keeping the label surface explicit. Unknown/empty
* ids fall back to 'Claude Code' (the always-safe default, fail-closed).
*
* The drift-guard test (tests/runtime-label-policy.test.cjs) pins this table's
* id set to the capability-registry runtime id set, so adding/removing a runtime
* forces a deliberate update here.
*/
const RUNTIME_LABELS: Readonly<Record<string, string>> = {
claude: 'Claude Code',
opencode: 'OpenCode',
gemini: 'Gemini',
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',
};
/**
* Map a canonical runtime id to its short display label for the
* install/uninstall console output. Unknown/empty inputs fall back to
* 'Claude Code'. Sibling to `getDirName`; pure (no I/O).
*/
export function getRuntimeLabel(runtime: string): string {
if (!runtime) return 'Claude Code';
const label = RUNTIME_LABELS[runtime];
return typeof label === 'string' && label.length > 0 ? label : 'Claude Code';
}