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

@@ -129,11 +129,11 @@ The installer writes four surfaces under `~/.config/opencode/` (XDG) or `~/.open
**MSD safety hooks on OpenCode.** OpenCode does not register lifecycle hooks the way Claude Code does (its `hooksSurface` is `none`), so MSD's prompt-injection guard, read-before-edit guard, injection scanner, and context monitor would otherwise be inert. The bundled plugin (`plugins/msd-core.js`) closes that gap: OpenCode auto-discovers `plugins/*.{ts,js}` files under its config directory at startup and the adapter bridges OpenCode's event bus (`tool.execute.before`/`after`, `session.created`, `file.edited`) onto MSD's existing hook scripts, spawning them as subprocesses. No `opencode.json` entry is needed — the plugin is loaded by directory auto-discovery (the config `plugin` array is for npm packages only). A blocking hook aborts the tool call; an advisory hook surfaces its message without blocking.
**Your plugin directory is pinned to CommonJS (accepted trade-off, #2544).** MSD's adapter is a CommonJS `.js` file, and Node decides a `.js` file's module type by walking up for the nearest `package.json`. So the installer writes a minimal `{"type":"commonjs"}` marker into the plugin directory itself — `plugins/package.json` on OpenCode and Kilo, `extensions/package.json` on pi. It is written only when MSD actually stages its adapter there, it never overwrites a `package.json` MSD did not write, and uninstall removes only its own.
**Your plugin directory is pinned to CommonJS (accepted trade-off, #2544).** MSD's adapter is a CommonJS `.js` file, and Node decides a `.js` file's module type by walking up for the nearest `package.json`. So the installer writes a minimal `{"type":"commonjs"}` marker into the plugin directory itself — `plugins/package.json` on OpenCode. It is written only when MSD actually stages its adapter there, it never overwrites a `package.json` MSD did not write, and uninstall removes only its own.
The trade-off: that marker shadows your config root for **every** `.js` file in that directory, not just MSD's. If you author your own plugins as ESM `.js` and rely on a `"type": "module"` at the config root, they will stop resolving as ESM. This is deliberate — it is strictly narrower than the pre-#2544 behavior, which wrote the marker over `<configRoot>/package.json` itself and destroyed whatever was there — but it is a real constraint rather than a pure improvement, which is why it is stated here.
**Mitigation:** author your own plugins as `.ts`. OpenCode and Kilo compile plugin TypeScript with Bun, and a `package.json` `type` field does not affect `.ts` resolution — so a `.ts` plugin is unaffected by the marker. Failing that, keep ESM plugins outside the auto-discovered directory and load them as npm packages via the config `plugin` array.
**Mitigation:** author your own plugins as `.ts`. OpenCode compiles plugin TypeScript with Bun, and a `package.json` `type` field does not affect `.ts` resolution — so a `.ts` plugin is unaffected by the marker. Failing that, keep ESM plugins outside the auto-discovered directory and load them as npm packages via the config `plugin` array.
**Override the install directory:**
@@ -143,22 +143,6 @@ OPENCODE_CONFIG_DIR=~/.config/opencode-alt npx @golem15/msd-core@latest --openco
---
### Kilo
```bash
npx @golem15/msd-core@latest --kilo --global
```
The installer writes the same three surfaces under `~/.config/kilo/` (XDG) or `~/.kilo/` as for OpenCode — flat commands in `command/`, subagents in `agents/`, and skills in `skills/<name>/SKILL.md` — since Kilo derives from OpenCode and shares its config schema and skill layout.
**Override the install directory:**
```bash
KILO_CONFIG_DIR=~/.config/kilo-alt npx @golem15/msd-core@latest --kilo --global
```
---
### Codex
```bash
@@ -183,110 +167,6 @@ All registered hooks are managed by MSD and are removed cleanly on `--uninstall`
---
### Kimi CLI
> **Support boundary — legacy `kimi-cli` vs Kimi Code.** This integration targets the legacy/Python `kimi-cli` custom-agent contract. The `kimi --agent-file <configRoot>/agents/msd.yaml` launch shown below is accepted by `kimi-cli`. The newer npm Kimi Code (`@moonshot-ai/kimi-code`, e.g. `0.11.0`) does **not** accept `--agent-file`; it discovers skills through fixed skill roots and `--skills-dir`. The generated `/skill:msd-*` skills work in both, but the custom-agent (`--agent-file`) surface is specific to legacy `kimi-cli`. For Kimi Code, point it at the installed skills root with `--skills-dir <configRoot>/skills` instead of using `--agent-file`.
```bash
npx @golem15/msd-core@latest --kimi --global
```
Skills land in Kimi's first existing generic user skills root:
- `~/.config/agents/skills/msd-*/SKILL.md` when `~/.config/agents/skills` already exists, or when neither generic root exists yet
- `~/.agents/skills/msd-*/SKILL.md` when `~/.agents/skills` already exists and `~/.config/agents/skills` does not
Start a new Kimi CLI session after install, then invoke MSD skills with `/skill:msd-*`, for example:
```text
/skill:msd-new-project
```
The installer also writes the MSD custom agent definition to the same selected config root: `<configRoot>/agents/msd.yaml` with its prompt at `<configRoot>/agents/msd.md`; subagents land under `<configRoot>/agents/subagents/msd-*.yaml` and `<configRoot>/agents/subagents/msd-*.md`.
Kimi custom agents do not auto-activate just because the files exist. Launch Kimi with the generated agent file when you want the MSD agent surface:
```bash
kimi --agent-file ~/.config/agents/agents/msd.yaml
```
If your machine already uses `~/.agents/skills` and does not have `~/.config/agents/skills`, MSD installs there instead and the launch command becomes:
```bash
kimi --agent-file ~/.agents/agents/msd.yaml
```
> **If you are on Kimi Code, use `--kimi-code`, not `--kimi` with a redirected config dir.** Since 1.10.0 (#2755) Kimi Code is its own runtime with its own hooks root:
>
> ```bash
> npx @golem15/msd-core@latest --kimi-code --global
> ```
>
> `--config-dir` and `KIMI_CONFIG_DIR` select the *skills* root only. They do **not** move the native `config.toml` that carries MSD's `[[hooks]]` block — that root is chosen by the runtime (`~/.kimi` for `--kimi` via `KIMI_SHARE_DIR`, `~/.kimi-code` for `--kimi-code` via `KIMI_CODE_HOME`). Running `--kimi --config-dir ~/.kimi-code` therefore puts your skills under `~/.kimi-code` while the hooks still land in `~/.kimi` — the exact split that left orphaned hooks behind before 1.10.0. See [Migrating from `--kimi` to `--kimi-code`](../migration/kimi-to-kimi-code.md), which also covers reclaiming artifacts an older install already wrote.
Kimi CLI also discovers user skills from the brand-specific `~/.kimi-code` directory. If you are genuinely on **Kimi CLI** and your setup is centered on `~/.kimi-code`, redirect its skills root explicitly:
```bash
npx @golem15/msd-core@latest --kimi --global --config-dir ~/.kimi-code
```
Then launch the generated agent from that directory:
```bash
kimi --agent-file ~/.kimi-code/agents/msd.yaml
```
For brand-specific scripted installs, use:
```bash
KIMI_CONFIG_DIR=~/.kimi-code npx @golem15/msd-core@latest --kimi --global
```
Avoid arbitrary `KIMI_CONFIG_DIR` roots unless your Kimi configuration also adds the matching `skills/` directory to Kimi's extra skill directories. MSD can write files there, but Kimi will not auto-discover skills outside its documented generic and brand-specific roots without that Kimi-side configuration.
`--kimi --local` is intentionally deferred and guarded in v1; use the global install path above for Kimi CLI.
**Hook coverage**
MSD wires its lifecycle hooks into Kimi's native `[[hooks]]` array in `config.toml` — by default `~/.kimi/config.toml` (overridable via Kimi's own `KIMI_SHARE_DIR` environment variable, a directory deliberately separate from the `~/.config/agents` skills root above). Kimi CLI's hooks system is documented as **Beta**. MSD's entries are wrapped in `# MSD Hooks BEGIN`/`# MSD Hooks END` marker comments, so reinstalling only ever rewrites MSD's own block and never touches hand-written `[[hooks]]` entries around it.
| Event | Hook | Purpose |
|---|---|---|
| `SessionStart` | `msd-check-update.js`, `msd-session-state.sh` | Update check and session-state bootstrap at session open |
| `PreToolUse` | `msd-prompt-guard.js`, `msd-read-guard.js`, `msd-worktree-path-guard.js`, `msd-workflow-guard.js`, `msd-secret-read-guard.js`, `msd-validate-commit.sh` | Prompt-injection guard, read-before-edit guidance, worktree path safety, workflow guard, secret-file read protection, and commit validation before tool calls |
| `PostToolUse` | `msd-context-monitor.js`, `msd-phase-boundary.sh`, `msd-read-injection-scanner.js`, `msd-graphify-update.sh` | Context window tracking, phase-boundary detection, read-time injection scanning, and graph updates after tool calls |
| `Stop` | `msd-context-monitor.js` | Context headroom tracking before the model stops |
| `PreCompact` | `msd-context-monitor.js` | Context headroom tracking before compaction |
| `SubagentStart` | `msd-context-monitor.js` | Inject context / MSD_AGENT_NAME awareness at subagent open |
| `SubagentStop` | `msd-context-monitor.js` | Context headroom tracking at subagent stop |
All registered hooks are managed by MSD and are removed cleanly on `--uninstall`.
---
### GitHub Copilot
```bash
npx @golem15/msd-core@latest --copilot --global
```
Skills land in `~/.copilot/`. MSD installs as agent `.md` files and repository instruction files.
MSD also wires Copilot's lifecycle hooks and instruction files:
- **`AGENTS.md`** (local installs) — written at the repository root, which GitHub Copilot CLI reads as primary instructions, alongside `copilot-instructions.md`.
- **Lifecycle hook** — a `sessionStart` hook config is written to `.github/hooks/msd-session.json` (local) or `~/.copilot/hooks/msd-session.json` (global). It is a self-contained inline `command` hook (no separate hook script to install), so it can never reference a missing script. The hook is advisory-only: at session start it surfaces whether the project has a `.planning/` workflow.
Both are removed (and any user-authored content preserved) on `--uninstall`.
**Override the install directory:**
```bash
COPILOT_CONFIG_DIR=~/.copilot-alt npx @golem15/msd-core@latest --copilot --global
```
---
### Cursor
```bash
@@ -303,137 +183,6 @@ CURSOR_CONFIG_DIR=~/.cursor-alt npx @golem15/msd-core@latest --cursor --global
---
### Windsurf / Devin Desktop
Windsurf has rebranded to **Devin Desktop**. Both runtime names are accepted — use either `--windsurf` or `--devin-desktop`.
```bash
npx @golem15/msd-core@latest --windsurf --global
# or equivalently:
npx @golem15/msd-core@latest --devin-desktop --global
```
Use a workspace install for Windsurf slash commands. Workspace installs write `/msd-*` commands as Windsurf workflow files under `.windsurf/workflows/`. Windsurf discovers those `.md` workflow files in Cascade and exposes them through the `/` menu. Global-scope Windsurf workflow installation is intentionally a no-op for now because global workflow locations are outside MSD's normal user-owned runtime config directory.
**Override the install directory:**
```bash
WINDSURF_CONFIG_DIR=~/.codeium/windsurf-alt npx @golem15/msd-core@latest --windsurf --global
```
---
### Cline
MSD gives Cline both skills (≥ v3.48.0) and the `.clinerules/` directory integration — no custom slash commands are registered.
```bash
# Global install (all projects — skills + rules directory)
npx @golem15/msd-core@latest --cline --global
# Local install (this project only — rules directory only)
npx @golem15/msd-core@latest --cline --local
```
MSD writes the [`.clinerules/` directory form](https://docs.cline.bot/customization/cline-rules):
- **`.clinerules/msd.md`** — the MSD rule file. Cline loads every `.md`/`.txt` file in
the `.clinerules/` directory automatically; no custom slash commands are registered.
- **`.clinerules/hooks/PreToolUse`** — a [lifecycle hook](https://cline.bot/blog/cline-v3-36-hooks)
(Cline v3.36+). It is an executable script that receives the tool-call context as JSON on
stdin and returns a JSON decision (`cancel` / `errorMessage` / `contextModification`). The
MSD hook guards `.planning/` artifacts from direct edits and otherwise allows the operation;
it fails open, so a hook error never blocks you. Cline runs hooks on macOS and Linux only.
**Global install additionally:**
- Emits each MSD command as **`~/.cline/skills/<name>/SKILL.md`**. Cline ≥ v3.48.0 loads
skills from `~/.cline/skills/` automatically — no configuration needed.
- Merges MSD instructions into **`~/.agents/AGENTS.md`**, the cross-tool global instruction
file Cline reads. The block is marker-delimited, so your own `AGENTS.md` content (and other
tools' entries) is preserved, and `--uninstall` strips only the MSD block.
**Local install** writes the `.clinerules/` directory into the current project only. No skills
directory is created for local scope.
> Cline's *global* hook directory (`~/Documents/Cline/Rules/Hooks/`) is not yet populated by the
> installer — project-scope hooks (`.clinerules/hooks/`) and the global `AGENTS.md` instruction
> target cover the common cases.
---
### CodeBuddy
```bash
npx @golem15/msd-core@latest --codebuddy --global
```
MSD installs four surfaces. Slash command definitions land in `~/.codebuddy/commands/msd-*.md` and appear as `/msd-help`, `/msd-phase`, `/msd-ship`, etc. in the `/` menu. Subagents land in `~/.codebuddy/agents/msd-*.md`. Skills land in `~/.codebuddy/skills/msd-*/SKILL.md` — emitted with `user-invocable: false` so they stay out of the `/` menu (the commands surface is the sole `/` entry point) and remain available for model invocation. CodeBuddy hooks are written to `settings.json`. No `mcp.json` is written: MSD ships no MCP server.
**Hook coverage**
MSD registers the following events automatically on install (Claude hook event dialect):
| Event | Hook | Purpose |
|---|---|---|
| `SessionStart` | `msd-check-update.js`, `msd-session-state.sh` | Update check, session orientation |
| `PreToolUse` | `msd-prompt-guard.js`, `msd-read-guard.js`, `msd-workflow-guard.js`, `msd-worktree-path-guard.js`, `msd-agent-isolation-guard.js`, `msd-secret-read-guard.js`, `msd-validate-commit.sh` | Prompt guard, read-before-edit, workflow + worktree safety, agent-dispatch isolation, secret-file read protection, commit validation |
| `PostToolUse` | `msd-context-monitor.js`, `msd-read-injection-scanner.js`, `msd-phase-boundary.sh`, `msd-graphify-update.sh` | Context monitoring, read-time scan, phase boundary detection |
| `SubagentStop` | `msd-context-monitor.js` | Context headroom tracking after subagent completion |
| `SubagentStart` | `msd-context-monitor.js` | Context headroom tracking at subagent start |
| `Stop` | `msd-context-monitor.js` | Context headroom tracking before model stop |
| `PreCompact` | `msd-context-monitor.js` | Context awareness before conversation compaction |
CodeBuddy's own [background sub-agent dispatch](https://www.codebuddy.ai/docs/cli/sub-agents) (`run_in_background: true`) is a caller-side invocation parameter, not something MSD's installed agent files control — there is no frontmatter field to set on MSD's agent artifacts to request it.
---
### Qwen Code
Qwen Code uses the same open skills standard as Claude Code 2.1.88+.
```bash
npx @golem15/msd-core@latest --qwen --global
```
Skills land in `~/.qwen/skills/msd-*/SKILL.md`.
MSD's main-loop skills are emitted with Qwen's optional numeric `priority` frontmatter field so the most-used workflows surface first in the `/skills` TUI list. Higher values sort earlier (per Qwen's skills spec), so core commands such as `/skills` for `new-project` (100), `plan-phase` (90), and `execute-phase` (85) appear above utility skills, which are left unset (default 0). This affects only the `/skills` list order — slash-command completion and `/help` remain alphabetical.
Subagents land in `~/.qwen/agents/msd-*.md` as native Qwen subagents, converted to Qwen's own `name:`/`description:`/`tools:` (YAML block list) frontmatter schema rather than Claude Code's.
**Override the install directory:**
```bash
QWEN_CONFIG_DIR=~/.qwen-alt npx @golem15/msd-core@latest --qwen --global
```
**Hook coverage**
Qwen Code supports 15 hook events. MSD registers the following events automatically on install:
| Event | Hook | Purpose |
|---|---|---|
| `SessionStart` | `msd-check-update.js`, `msd-session-state.sh` | Update check, session orientation |
| `PostToolUse` | `msd-context-monitor.js`, `msd-read-injection-scanner.js`, `msd-phase-boundary.sh`, `msd-graphify-update.sh` | Context monitoring, read-time scan, phase boundary detection |
| `PreToolUse` | `msd-prompt-guard.js`, `msd-read-guard.js`, `msd-workflow-guard.js`, `msd-worktree-path-guard.js`, `msd-agent-isolation-guard.js`, `msd-secret-read-guard.js`, `msd-validate-commit.sh` | Prompt guard, read-before-edit, workflow + worktree safety, agent-dispatch isolation, secret-file read protection, commit validation |
| `SubagentStop` | `msd-context-monitor.js` | Context headroom tracking after subagent completion |
| `SubagentStart` | `msd-context-monitor.js` | Context headroom tracking at subagent start |
| `Stop` | `msd-context-monitor.js` | Context headroom tracking before model stop |
| `PreCompact` | `msd-context-monitor.js` | Context awareness before conversation compaction |
---
### Augment Code
```bash
npx @golem15/msd-core@latest --augment --global
```
Skills land in `~/.augment/skills/` and slash command definitions land in `~/.augment/commands/`. MSD installs skills, agents, and commands (`/msd-phase`, `/msd-ship`, etc.). MSD's managed lifecycle hooks are registered into Augment's own `settings.json` `hooks` block (Claude hook event dialect, covering session-start, tool-use, and phase-boundary events) — no statusline ownership. #2097 also registers the MSD companion MCP server under `settings.json`'s `mcpServers.msd` (see [Connect a host to the MSD MCP server](connect-msd-mcp-server.md)).
---
### Antigravity
```bash
@@ -450,16 +199,6 @@ ANTIGRAVITY_CONFIG_DIR=~/.gemini/antigravity-alt npx @golem15/msd-core@latest --
---
### Trae
```bash
npx @golem15/msd-core@latest --trae --global
```
Skills land in `~/.trae/`. MSD installs skills, agents, and rule references.
---
### ZCode
```bash
@@ -478,30 +217,6 @@ MSD's hook-automation and native-MCP-registration integrations are not yet wired
---
### pi
```bash
npx @golem15/msd-core@latest --pi --global
```
**Override the install directory:**
```bash
PI_CODING_AGENT_DIR=~/.pi-alt/agent npx @golem15/msd-core@latest --pi --global
```
`PI_CODING_AGENT_DIR` is pi's own upstream override (`getAgentDir()` in pi's `config.ts`) for its global agent directory (`~/.pi/agent` by default) — MSD honors it so the install always lands where pi actually reads ([#3023](https://github.com/open-gsd/gsd-core/issues/3023)). pi also supports a `piConfig.configDir` field (`config.ts`'s `CONFIG_DIR_NAME`) that renames the `.pi` segment, but that field is read from pi's own installed `package.json`, not your project's — it is a white-label/rebranding hook for redistributed pi forks (it sits beside `piConfig.name`, which renames the app itself), not something an end user sets for their own project. MSD's pi descriptor does not target rebranded forks, so `PI_CODING_AGENT_DIR` remains the correct override for a stock pi install.
[pi](https://pi.dev) is a bun-runtime programmatic CLI whose extensions implement pi's own `ExtensionAPI` (`registerCommand`/`registerTool`/`registerProvider`/`pi.on`) rather than a settings-file or slash-markdown surface. MSD ships a single native-extension file:
- **Extension** → `~/.pi/agent/extensions/msd.js` (global) or `.pi/extensions/msd.js` (local)
The `.js` suffix is load-bearing: pi auto-discovers extensions by scanning that directory and keeping only names ending in `.ts` or `.js`, and it skips anything else **silently** — no error, no log line. MSD shipped the file as `msd.cjs` through 1.7.0, which pi therefore never loaded, so `/msd` never appeared ([#2470](https://github.com/open-gsd/gsd-core/issues/2470)). Upgrading removes the stale `msd.cjs`; if you had added a manual `extensions` entry in `~/.pi/agent/settings.json` as a workaround, you can drop it.
The extension registers a `/msd` command and a `msd_invoke` tool that dispatch MSD commands via a bounded subprocess call to `msd-core/bin/msd-tools.cjs` (no fully-populated in-process command-routing hub exists — see the matrix's Stage 2 note). This is a **plugin-only install**: pi has no shared-settings hook surface (`hooksSurface: none`) and, unlike Claude/OpenCode/Kilo, no host-read markdown surface at all — pi's `/msd` command is registered programmatically by the extension, not discovered from files, so MSD installs the extension plus its universal `msd-core/` engine payload and the shared `msd-hooks/`/`msd-hooks/lib/` bundle (spawned by the extension itself, not by any config-file hook bus), and does **not** write any `commands/`, `agents/`, or `skills/` directory for pi. The bundle lands under `msd-hooks/` rather than the `hooks/` name every other runtime uses because pi reserves `hooks/` for its own deprecated extension directory and warns on startup whenever that directory merely exists ([#3023](https://github.com/open-gsd/gsd-core/issues/3023)). The extension bridges MSD's `session_start`/`before_agent_start`/`session_before_compact`/`tool_call` lifecycle events to those staged `msd-hooks/` scripts as bounded, fail-open subprocesses, and steers pi's active model (`modelMode: active`) to a tier-resolved bare anthropic id via `pi.on('before_provider_request', ...)`. See the [`## pi`](../reference/host-integration-capability-matrix.md#pi) section of the host-integration capability matrix for the negotiated axes and citations.
---
## Local vs global install
All examples above use `--global`, which installs MSD once for your user account. To scope an install to a single project, replace `--global` with `--local`:
@@ -516,10 +231,10 @@ A local install writes into the `.claude/` directory at your project root. Local
## Installing prerelease editions (Next / Nightly / Insiders / Preview)
Prerelease editions of runtimes (Windsurf Next / Devin Desktop Next, Cursor Nightly, VS Code Insiders, Codex preview channels, etc.) read from a sibling config directory. Set the matching `*_CONFIG_DIR` env var before running the installer:
Prerelease editions of runtimes (Cursor Nightly, VS Code Insiders, Codex preview channels, etc.) read from a sibling config directory. Set the matching `*_CONFIG_DIR` env var before running the installer:
```bash
WINDSURF_CONFIG_DIR=~/.codeium/windsurf-next npx @golem15/msd-core@latest --windsurf --global
CURSOR_CONFIG_DIR=~/.cursor-nightly npx @golem15/msd-core@latest --cursor --global
```
Select the corresponding stable runtime in the installer prompt. MSD does not enumerate prerelease editions as separate named runtimes — they are best-effort via this env-var mechanism and are not separately tested in release CI.