refactor: drop 12 runtimes, keep Claude, Codex, OpenCode, Cursor, ZCode, Antigravity

Removes kilo, kimi, kimi-code, copilot, windsurf, augment, trae, qwen, hermes,
cline, codebuddy and pi end to end: capability descriptors, installer branches
and converters (bin/install.js 14.9k -> 11.2k lines), TypeScript converters,
hook surfaces and runtime homes, review lanes qwen/kimi-code, the two pi
migrations, Kimi payload normalization in the hook guards, dead hostBehaviors
vocabulary, launcher home probes, fixtures, runtime-specific tests and the
prose that presented them as supported.

Installer output for the six kept runtimes is byte-identical to before the
prune. The Kimi tool-vocabulary tests in workflow-guard, read-guard and
read-injection-scanner are left in place pending a decision.
This commit is contained in:
Jakub Zych
2026-10-06 20:02:40 +02:00
parent 12ee75a509
commit 6cfa0c55d2
509 changed files with 1869 additions and 47737 deletions

View File

@@ -42,20 +42,18 @@
* entries and MISSED a real leak into `<live>/skills/msd-dev-preferences/`.
*
* KNOWN GAP — a leak into a file MSD does not own (e.g. mutating the host's own
* `.claude.json`, `settings.json`, `hooks.json`, `kilo.json`, `opencode.json`) is
* `.claude.json`, `settings.json`, `hooks.json`, `opencode.json`) is
* outside this guard by construction. Closing it would require watching shared
* files, which is the false-positive trap above.
*
* NAMED RESIDUALS — stated rather than implied, because two successive rounds
* asserted this list was complete and both were refuted. Still NOT watched:
* - `agents/subagents/**` (kimi stages `subagents/msd-executor.yaml` under an
* UNPREFIXED intermediate dir, so no prefix scan of `agents/` reaches it);
* - the loose capability generators copied to `<root>/scripts/*.cjs`
* (`fix-slash-commands.cjs` is watched by name; the generators are not);
* - `extensions/package.json` and `plugins/package.json` — CommonJS markers in
* dirs MSD fills but does not own, so they fall under the shared-ground rule
* below rather than being watched;
* - the shared-hooks bundle in a NON-registry root's `<root>/hooks/` (kimi) —
* - the shared-hooks bundle in a NON-registry root's `<root>/hooks/` —
* see resolveExtraWatchTargets; closing it is a layout decision.
* DELIBERATELY not watched, which is a different thing from missed: `hooks/lib`,
* `hooks/package.json`, `scripts/lib` and `scripts/changeset`. The installer
@@ -125,7 +123,7 @@ const NON_REGISTRY_ARTIFACT_PARENTS = ['hooks', 'plugins', 'scripts', 'extension
/**
* Prefixes used for a parent with no registry-declared one. BOTH forms are the
* point: MSD writes `msd-`-hyphen artifacts (`hooks/msd-check-update.js`) AND
* bare `msd.`-dotted ones (pi's `extensions/msd.js`), and a lone `msd-` sees
* bare `msd.`-dotted ones (`plugins/msd.js`), and a lone `msd-` sees
* only the first.
*/
const DEFAULT_ARTIFACT_PREFIXES = ['msd-', 'msd.'];
@@ -176,13 +174,13 @@ function deriveArtifactTargets(runtimes) {
const dest = layout?.destSubpath;
if (typeof dest !== 'string' || !dest) continue;
const last = dest.split('/').pop() || '';
// A destination whose own final segment is MSD's (hermes' `skills/msd`) is
// A destination whose own final segment is MSD's (`skills/msd`) is
// owned wholesale — that directory is ours, not shared.
if (last.startsWith('msd')) { owned.add(dest); continue; }
// The layout declares its OWN prefix, and it varies: kimi's `kimi-agents`
// layout declares `msd` (no hyphen) and writes `agents/msd.yaml` +
// `agents/msd.md`, invisible to a fixed `msd-` scan. The same destSubpath
// also carries different prefixes across runtimes, so a parent maps to a SET.
// The layout declares its OWN prefix, and it may vary: a layout declaring
// `msd` (no hyphen) would be invisible to a fixed `msd-` scan. The same
// destSubpath also carries different prefixes across runtimes, so a
// parent maps to a SET.
const declared = typeof layout?.prefix === 'string' && layout.prefix
? [layout.prefix]
: DEFAULT_ARTIFACT_PREFIXES;
@@ -214,12 +212,10 @@ function artifactTargets(deps = {}) {
/**
* The file MSD writes into a NON-REGISTRY config home.
*
* Both current descriptors are Kimi's — Kimi CLI's `~/.kimi` (KIMI_SHARE_DIR) and
* Kimi Code's `~/.kimi-code` (KIMI_CODE_HOME) — and MSD writes its native
* `[[hooks]]` block into `config.toml` in each, so the single filename below holds
* for both. NAMED RESIDUAL: this assumes every non-registry descriptor is written
* the same way. That assumption is now load-bearing rather than vacuous — it is
* carrying two descriptors, not one — and a future descriptor whose owned file
* NON_REGISTRY_CONFIG_HOME_DESCRIPTORS is empty today (its former entries were
* native `config.toml` hook homes of since-retired runtimes), so this filename
* is currently unused. NAMED RESIDUAL: this assumes every non-registry
* descriptor is written the same way; a future descriptor whose owned file
* differs needs a per-descriptor mapping here. The consequence of getting it wrong
* is under-watching (a missed leak), not a false positive, so it fails in the quiet
* direction and is called out rather than left to be discovered.
@@ -339,8 +335,8 @@ function resolveLiveConfigRoots(deps = {}) {
* #2665 round 3: resolveLiveConfigRoots enumerates getGlobalConfigDir per registry
* runtime plus grok. A live write surface that is not a config ROOT is invisible to
* that shape, so a leak on one passed through this guard — the PR's own safety net —
* silently. There are THREE today ($MSD_HOME/.msd, plus one config.toml per entry in
* NON_REGISTRY_CONFIG_HOME_DESCRIPTORS, which #2755 took from one entry to two):
* silently. Today: $MSD_HOME/.msd, plus one config.toml per entry in
* NON_REGISTRY_CONFIG_HOME_DESCRIPTORS (currently none):
*
* $MSD_HOME/.msd — MSD's user-owned store (consent.json, defaults.json, capability
* overlays). Watched WHOLESALE: unlike ~/.claude this root is
@@ -348,9 +344,8 @@ function resolveLiveConfigRoots(deps = {}) {
* SCOPE above does not apply and an ownership filter would only
* narrow the guard for nothing.
* <non-registry home>/config.toml — the file MSD writes its native [[hooks]] block
* into, one per NON_REGISTRY_CONFIG_HOME_DESCRIPTORS entry: Kimi
* CLI's ~/.kimi (KIMI_SHARE_DIR) and, since #2755, Kimi Code's
* ~/.kimi-code (KIMI_CODE_HOME). The INVERSE case: those roots
* into, one per NON_REGISTRY_CONFIG_HOME_DESCRIPTORS entry
* (none today). The INVERSE case: those roots
* belong to their products, so the root is never watched
* wholesale. This is the KNOWN GAP above accepted deliberately
* in one direction — MSD demonstrably writes these files
@@ -384,19 +379,18 @@ function resolveExtraWatchTargets(deps = {}) {
resolveConfigHomeFromDescriptor,
} = require(path.join(libDir, 'runtime-homes.cjs'));
// ITERATE the descriptor array rather than naming one resolver. Calling
// resolveKimiHooksTomlDir directly would cover one of today's two entries and
// silently miss tomorrow's — the same partial-enumeration defect that put
// KIMI_SHARE_DIR outside the scrub set in the first place, reintroduced one
// layer over. TEST_ENV_BASE derives its keys from this array; deriving the
// guard's paths from it keeps the two halves from drifting apart.
// ITERATE the descriptor array rather than naming one resolver — a
// partial enumeration would silently miss a future entry. TEST_ENV_BASE
// derives its keys from this array; deriving the guard's paths from it
// keeps the two halves from drifting apart.
//
// Thread the SAME injected env/home used above: resolving bare would read
// process.env and os.homedir() regardless of `deps`, leaving the seam
// untestable and the targets resolved against different worlds.
for (const descriptor of NON_REGISTRY_CONFIG_HOME_DESCRIPTORS) {
// The fallback leg, per the note above: a child that blanks KIMI_SHARE_DIR /
// KIMI_CODE_HOME writes to the HOME-derived root instead of the ambient one.
// The fallback leg, per the note above: a child that blanks the
// descriptor's env var writes to the HOME-derived root instead of the
// ambient one.
const fallbackDir = resolveConfigHomeFromDescriptor(descriptor, { env: {}, home: homedir() });
if (typeof fallbackDir === 'string' && fallbackDir.length > 0) {
targets.push(path.resolve(path.join(fallbackDir, NON_REGISTRY_OWNED_FILE)));
@@ -405,12 +399,11 @@ function resolveExtraWatchTargets(deps = {}) {
// The root belongs to the runtime, so it is never watched wholesale — only
// the named file below.
//
// NAMED RESIDUAL (#2665, found pre-push while rebasing): config.toml is NOT
// the only thing MSD writes here. bin/install.js also calls
// installSharedHooksBundle(kimiHooksRoot), which populates <root>/hooks/
// with MSD's hook scripts and a CommonJS marker. That subtree is UNWATCHED,
// so a suite-produced leak of a hook bundle into a developer's real ~/.kimi
// or ~/.kimi-code passes this guard silently. Closing it needs a layout
// NAMED RESIDUAL (#2665, found pre-push while rebasing): config.toml may
// NOT be the only thing MSD writes here — an installer may also populate
// <root>/hooks/ with MSD's hook scripts and a CommonJS marker. That
// subtree is UNWATCHED, so a suite-produced leak of a hook bundle into a
// developer's real non-registry root passes this guard silently. Closing it needs a layout
// decision, not one more path: the same reason getGlobalSkillsBase is a
// deliberate non-target above. Under-watching fails quiet, like the
// NON_REGISTRY_OWNED_FILE residual it sits beside.
@@ -507,8 +500,8 @@ function snapshotLiveConfig(roots, extraTargets = [], limits = {}) {
for (const root of roots) {
for (const entry of MSD_OWNED_ENTRIES) record(path.join(root, entry));
// Paths whose own name is MSD's, nested inside a shared root (hermes'
// `skills/msd`, `hooks/lib`, `scripts/lib`, …) — no prefix rule sees these.
// Paths whose own name is MSD's, nested inside a shared root
// (`skills/msd`, `hooks/lib`, `scripts/lib`, …) — no prefix rule sees these.
for (const entry of watchOwned) record(path.join(root, ...entry.split('/')));
// Shared dirs: enumerate only msd-prefixed children. A child that appears