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

@@ -105,7 +105,7 @@ they must be updated together. Document the new value's meaning in the matrix le
## 7. Fold an already-hardcoded host into the interface (worked example: `claude`)
Sections 1–6 cover a *green-field* host (`pi`, `antigravity` — a fresh descriptor + reference
Sections 1–6 cover a *green-field* host (`antigravity` — a fresh descriptor + reference
binding). This section covers the other case: a host that already has a **real production install**
driven by scattered `runtime === '<id>'` string-equality branches in `bin/install.js`, which you want
to move onto the Host-Integration Interface **without changing a single installed byte**. `claude`
@@ -150,20 +150,6 @@ yields the same truth value**, so behavior is unchanged and only the brittle cou
the host correctly, negotiation fails closed on a corrupted descriptor, and — with a source-grep behind
an `// allow-test-rule:` exemption — that **no `runtime === '<id>'` branch remains** in `bin/install.js`.
**Another completed worked example: `copilot` (#2099).** Copilot was already installing through the
declarative artifactLayout (not the direct `installRuntimeArtifacts` calls step 4 describes), so its
migration folded the *residual* hardcoded branches rather than the whole install path: the `.agent.md`
destination-suffix rename in `src/install-engine.cts` (→ `hostBehaviors.agentFileExtension`), two
uninstall side-effect branches in `bin/install.js` (→
`resolveInstallPlan(runtime).installSurface === 'copilot-instructions'`, already a live descriptor field
elsewhere in the same file), and two `skipSharedHooksInstall` gates (→
`hostBehaviors.skipSharedHooksInstall: true`). A dead legacy agent-converter dispatch arm — unreachable
because copilot was a member of the then-existing `_DESCRIPTOR_AGENTS_RUNTIMES` allow-list — was deleted
outright rather than re-gated, mirroring step 6's guard:
`tests/declarative-reference-copilot.test.cjs` source-greps both files for the retired `isCopilot` reads.
See the `copilot` section of the reference matrix for the full EoS migration note, including the two
upgrades (multi-event hook bus; negotiated `dispatch.background`) this PR adds.
> **`_DESCRIPTOR_AGENTS_RUNTIMES` no longer exists (#2875).** It was an allow-list naming the runtimes
> whose `agents` came from the descriptor; everything absent from it fell through to an inline
> `_hostBehaviors()` dispatch loop in `bin/install.js`. That loop and the set are both gone — the
@@ -174,7 +160,7 @@ upgrades (multi-event hook bus; negotiated `dispatch.background`) this PR adds.
> rather than reintroducing an inline branch. The three extension points added when the loop was
> removed are the pattern to follow: `hostBehaviors.agentFrontmatterExtensions` for injected
> frontmatter keys, per-agent model-override resolution threaded through the converter's options,
> and a named converter driven by descriptor data (hermes's branding rewrites are declared in
> and a named converter driven by descriptor data (branding rewrites are declared in
> `capability.json`, not hardcoded). All three exist because the descriptor pipeline lacked one
> thing: per-agent resolution context (`targetDir` + `agentName`).
>

View File

@@ -21,7 +21,7 @@ from your axes):
| Profile | Example hosts | Binding |
|---|---|---|
| `programmatic-cli` | OpenCode, pi | imperative adapter (`createImperativeAdapter`) |
| `programmatic-cli` | OpenCode | imperative adapter (`createImperativeAdapter`) |
| `declarative-cli` | Antigravity, Codex | declarative adapter (`createDeclarativeAdapter`) |
| `ide` | VS Code | imperative adapter + engine-owned hook bus + active model + sandboxed state |
@@ -68,7 +68,7 @@ consistency is the contract.
Map the six interface points (command, dispatch, model, hooks, state, artifact)
to your host's primitives (palette/chat, plugin API, provider calls, etc.). The
reference host-plugins (OpenCode, Antigravity, pi, VS Code — see Phase 5) are
reference host-plugins (OpenCode, Antigravity, VS Code — see Phase 5) are
canonical examples to mirror. Your host-plugin is now a self-contained artifact;
no msd-core source change is required.

View File

@@ -60,7 +60,7 @@ On the Claude runtime, fully-qualified Claude model IDs act as explicit generati
**Important for Codex and OpenCode:** Those runtimes embed the model into each agent's static config at install time rather than choosing it per spawn, so after editing `model_overrides` you must re-run the installer for the change to take effect:
```bash
npx @golem15/msd-core@latest --codex --global # or --opencode, --kilo, etc.
npx @golem15/msd-core@latest --codex --global # or --opencode, etc.
```
MSD will also warn you if you forget: workflow entry commands (`msd init plan-phase`, `msd init execute-phase`, etc.) detect when `.planning/config.json` or `~/.msd/defaults.json` is newer than your installed agent files and print a one-line stderr reminder naming the changed file and the re-install command. The check is read-only and runs only on `codex` and `opencode`; Claude Code resolves models at spawn time and is unaffected. (#1688)
@@ -187,7 +187,7 @@ quota / rate-limit failures; other failures keep the tier ladder. Leaving
## Using MSD on non-Anthropic runtimes
If you installed MSD for Codex, OpenCode, Antigravity CLI, or Kilo, the installer already set `resolve_model_ids: "omit"` in the shared config. This prevents unresolved Anthropic model IDs from leaking into those runtimes. Your install's recorded runtime identity (the `.msd-runtime` marker) tells MSD which runtime tier map to resolve instead, so runtime-native profile resolution still supplies any model and effort that the runtime adapter can transport. No manual setup is needed for the basic case.
If you installed MSD for Codex, OpenCode, or Antigravity CLI, the installer already set `resolve_model_ids: "omit"` in the shared config. This prevents unresolved Anthropic model IDs from leaking into those runtimes. Your install's recorded runtime identity (the `.msd-runtime` marker) tells MSD which runtime tier map to resolve instead, so runtime-native profile resolution still supplies any model and effort that the runtime adapter can transport. No manual setup is needed for the basic case.
### Codex routes tiers at spawn time when supported

View File

@@ -1,7 +1,7 @@
# How to connect a host to the MSD companion MCP server
This guide shows you how to make a MCP-capable host (Claude Code, Codex,
OpenCode, VS Code, Antigravity CLI, Cursor, Cline, Hermes, Augment Code) drive
OpenCode, VS Code, Antigravity CLI, Cursor) drive
MSD — run MSD
commands and read/write `.planning/` state — through the companion MCP server,
with no bespoke plugin.
@@ -31,13 +31,8 @@ host.
}
```
- **Claude Code / Codex / Cursor / Cline / Hermes** — under the host's
- **Claude Code / Codex / Cursor** — under the host's
`mcpServers` object (project or user config).
- **Augment Code** — under the `mcpServers` block of its own
`settings.json` (not a standalone MCP config file, unlike Antigravity)
— global at `~/.augment/settings.json`, project-local at
`.augment/settings.json`. MSD's installer configures this entry
automatically (`--augment` installs).
- **VS Code** — in the workspace MCP servers list.
- **Antigravity** — under the `mcpServers` block of its standalone
`mcp_config.json` profile (not embedded in `settings.json`) — global at
@@ -48,16 +43,13 @@ host.
- **OpenCode** — under the `mcp` key (**not** `mcpServers`), in
`~/.config/opencode/opencode.jsonc` (global) or `./opencode.json`
(project). The entry shape also differs — see below.
- **Kilo Code** — an OpenCode fork; also under the `mcp` key (**not**
`mcpServers`), in `~/.config/kilo/opencode.jsonc` (global) or
`./opencode.json` (project). Same entry shape as OpenCode.
Set `cwd` to the project whose `.planning/` you want MSD to manage — the server
resolves state paths against it.
### OpenCode / Kilo entry shape
### OpenCode entry shape
OpenCode (and Kilo, which shares OpenCode's config schema) use a
OpenCode uses a
`type`/`command`/`timeout` entry under the `mcp` key instead of the generic
`command`/`args`/`cwd` form above:

View File

@@ -89,7 +89,7 @@ Write the trade-off, not the translation.
Most callers never pass `stderrPayload` — fd 2 gets the same bytes as fd 1,
unchanged from before this migration. One shape needs it: `msd-write-guard.js`
guards a curated `.planning/` artifact, but Kimi's native hook bus reads
guards a curated `.planning/` artifact, but a host's native hook bus may read
**stderr verbatim back to the model** on exit 2. A full JSON object on stderr
would show the model a data structure instead of a sentence, so this call
site sends the complete decision to stdout and only the plain-text reason to

View File

@@ -98,14 +98,14 @@ Use explicit runtime ids when the Capability is intentionally narrower:
```json
"runtimeCompat": {
"supported": ["claude", "codex"],
"unsupported": ["kilo"],
"unsupported": ["zcode"],
"notes": {
"kilo": "Requires a hook surface Kilo does not expose yet."
"zcode": "Requires a hook surface ZCode does not expose yet."
}
}
```
`supported` is required and must be non-empty. `"*"` means every descriptor-backed runtime, including future first-party runtime descriptors, is compatible unless it is listed in `unsupported`. Explicit runtime ids and `notes` keys must match runtime Capability ids such as `claude`, `codex`, `opencode`, or `kilo`; typos fail `node scripts/gen-capability-registry.cjs --check`.
`supported` is required and must be non-empty. `"*"` means every descriptor-backed runtime, including future first-party runtime descriptors, is compatible unless it is listed in `unsupported`. Explicit runtime ids and `notes` keys must match runtime Capability ids such as `claude`, `codex`, or `opencode`; typos fail `node scripts/gen-capability-registry.cjs --check`.
## Add hooks
@@ -214,7 +214,7 @@ Runtime-specific facts belong in the runtime Capability declaration, not in a pa
- `configHome`: the global config root resolver, including env overrides and probes.
- `configHome.skillsHome`: optional separate base home for runtimes whose global skills root differs from the config root.
- `artifactLayout.global` and `artifactLayout.local`: command, agent, skill, and Kimi-agent destinations.
- `artifactLayout.global` and `artifactLayout.local`: command, agent, and skill destinations.
- `hooksSurface`, `hookEvents`, and `extendedHookEvents`: hook registration surface and event dialect.
- `installSurface`, `writesSharedSettings`, and `permissionWriter`: install-time config mutation behavior.

View File

@@ -59,8 +59,7 @@ instead of `origin/HEAD`, so the files your branch added are present inside each
worktree and the wave can run in parallel. It is honored by MSD-created worktrees by construction
and, as measured, by Claude Code's harness:
- **Runtimes where MSD itself runs `git worktree add <path> <start-point>`** (Codex, OpenCode,
Kimi, Kimi Code) — by construction; the check suppresses on it (`reason: "baseref-head"`).
- **Runtimes where MSD itself runs `git worktree add <path> <start-point>`** (Codex, OpenCode) — by construction; the check suppresses on it (`reason: "baseref-head"`).
- **Claude Code's `Agent(isolation="worktree")`** — because the harness reads the setting and
forks from `HEAD`. This was measured on current Claude Code from the project-local,
project-shared and user/global settings layers on macOS, Windows and Linux (#4588). Cursor,

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.

View File

@@ -62,7 +62,7 @@ Absence of a shadow warning is not always proof that nothing is shadowed. MSD Co
- Only one scope is installed for that runtime.
- Both scopes resolve to the *same* config directory (for example, `--config-dir` pointed both installs at the same place) — this is one physical install, not two.
- The runtime's global scope installs no trigger-bearing artifact kind at all (windsurf's global install is `agents`, which is never invoked by a `/msd-<name>` trigger) — there is nothing for the local scope to collide with.
- The runtime's global scope installs no trigger-bearing artifact kind at all (e.g. a runtime whose global install is `agents` only, which is never invoked by a `/msd-<name>` trigger) — there is nothing for the local scope to collide with.
- The other scope's manifest exists but declares zero files (an empty or partial install at that scope).
**Could not look (degraded, not verified)** — the check itself could not run, and its silence carries no claim either way:

View File

@@ -8,7 +8,7 @@
## What `installRuntimeArtifacts` now returns
`installRuntimeArtifacts` (`src/install-engine.cts:770`) no longer returns `void`. Every call — including the opencode/kilo combined-family path, which used to early-return `undefined` — now returns:
`installRuntimeArtifacts` (`src/install-engine.cts:770`) no longer returns `void`. Every call — including the opencode family path, which used to early-return `undefined` — now returns:
```
{
@@ -26,9 +26,9 @@
## Migrating a probing test group
The worked example is the qwen group in `tests/install.test.cjs` (`describe('install/uninstall — qwen …')`, test `'installs MSD into ./.qwen and removes it cleanly'`). The pattern:
The worked example is a per-runtime probing group in `tests/install.test.cjs` (an `install/uninstall — <runtime> …` group whose test installs MSD into the local runtime dir and removes it cleanly). The pattern:
1. Run the real install as before (`install(false, 'qwen')`), unchanged.
1. Run the real install as before (`install(false, '<runtime>')`), unchanged.
2. Immediately after, call `installRuntimeArtifacts` again directly with the same `runtime`/`targetDir`/`scope`/resolved profile. Because the tree the first call wrote is already installed, this second call is an idempotent re-run (prune + rewrite converges to the same on-disk result) — it does no new writes, but it surfaces the same executed-plan value the first call's caller (`bin/install.js`) discarded.
3. Replace each `fs.existsSync(destPath)` probe that was checking a *destination directory's existence* with one `assert.deepStrictEqual` against the relevant `plan.kinds` entries — keyed by `kind`, read off `destDir`.
4. Leave every other probe exactly where it was (see the next section).
@@ -39,7 +39,7 @@ The worked example is the qwen group in `tests/install.test.cjs` (`describe('ins
Before converting a probing group, list every fact its existing `fs.existsSync`/`fs.readFileSync` calls establish. Convert only the facts the plan's per-kind contract actually covers. A migration that asserts *less* than the probes it replaces looks like a simplification and is a regression — a shrinking assertion surface with no visible signal that coverage was dropped.
In the qwen migration, nine facts were enumerated. Only **two** moved to the value assertion — that `skills` and `agents` kinds wrote to the expected `destDir`. The other **seven** were deliberately retained as `fs` probes, because they sit outside the plan's per-kind contract (one `destDir` per kind, not a file list, and not everything the surrounding `install()`/`uninstall()` functions do):
In that migration, nine facts were enumerated. Only **two** moved to the value assertion — that `skills` and `agents` kinds wrote to the expected `destDir`. The other **seven** were deliberately retained as `fs` probes, because they sit outside the plan's per-kind contract (one `destDir` per kind, not a file list, and not everything the surrounding `install()`/`uninstall()` functions do):
- the specific nested `SKILL.md` file existing at its stem-level path (finer-grained than a per-kind `destDir`)
- the `msd-core/VERSION` file, written by `install()`'s own copy step, not by `installRuntimeArtifacts`

View File

@@ -41,7 +41,7 @@ An `uncovered` finding has exactly two legitimate resolutions. Pick the first on
Open `eslint.config.mjs` and add the path to the `files:` array of the block whose ruleset fits:
- CommonJS Node code (`scripts/`, `eslint-rules/`, `bin/lib/`, `pi/`, `examples/`, `vscode/`) → the **CommonJS Node** block.
- CommonJS Node code (`scripts/`, `eslint-rules/`, `bin/lib/`, `examples/`, `vscode/`) → the **CommonJS Node** block.
- Test code → the `tests/**/*.cjs` block.
- TypeScript sources → the `src/**/*.cts` block.

View File

@@ -8,7 +8,7 @@
## Decide which reviewers to use
MSD Core can route review requests to any combination of: Claude (separate session), Codex CLI, CodeRabbit, OpenCode, Qwen Code, Cursor, Antigravity CLI, Ollama, LM Studio, llama.cpp, and Kimi Code.
MSD Core can route review requests to any combination of: Claude (separate session), Codex CLI, CodeRabbit, OpenCode, Cursor, Antigravity CLI, Ollama, LM Studio, and llama.cpp.
That list is not fixed. Each of those is a declared reviewer lane, and a capability can ship its own — see [Ship a reviewer lane in your capability](ship-a-reviewer-lane.md). To see exactly which lanes your installation has, run `msd-tools review-lane sections`.
@@ -104,7 +104,7 @@ Compare two reviewers' verdicts only after checking what actually produced each
If a reviewer's entry reads `unknown`, pin it: set `review.models.<slug>` for that lane (the key suffix is not always the lane's slug — Antigravity's is `review.models.agy`) so the next run records `pinned`. See [Code-review CLI routing](../CONFIGURATION.md#code-review-cli-routing) for the full key table.
Some `unknown` values are expected, not a bug to chase: lanes that accept no model at all (`cursor`, `qwen`, `coderabbit`), and any lane whose CLI didn't disclose one on this run. `pinned` is a certain value; `banner` and `transcript` are recovered from third-party CLI output and can degrade to `unknown` after an upstream release changes that output.
Some `unknown` values are expected, not a bug to chase: lanes that accept no model at all (`cursor`, `coderabbit`), and any lane whose CLI didn't disclose one on this run. `pinned` is a certain value; `banner` and `transcript` are recovered from third-party CLI output and can degrade to `unknown` after an upstream release changes that output.
A `models:` entry like `gpt-5.6-sol (reasoning=high)` is not a formatting quirk: the `(reasoning=<level>)` suffix reflects a reasoning effort MSD itself applied to that lane, driven by your `effort.*` config — not the CLI's own default.
@@ -156,7 +156,6 @@ This runs `plan-phase → review → replan → re-review` up to three cycles (d
| You have Antigravity already installed | `--agy` is always a good starting reviewer |
| You want free multi-reviewer coverage | `--agy` (Google credentials) + `--claude` |
| Your project is OpenAI-heavy | add `--codex` for an OpenAI-model perspective |
| You want GitHub Copilot's model | add `--opencode` |
| You want to avoid API costs entirely | configure Ollama with a local model and use `--ollama` |
| You need maximum coverage before a release | `/msd-plan-review-convergence N --all` |
| You're iterating quickly and want fast feedback | pick one CLI: `/msd-review --phase N --agy` |

View File

@@ -28,7 +28,7 @@ MSD will:
Before reporting completion, the installer checks each MSD-managed script and interpreter path written into runtime configuration, resolving the interpreter against the current install-time `PATH`. This only proves the path resolves now — a hook fired later under a different, more restricted `PATH` (e.g. a GUI launcher) can still fail even after this check passes.
If a script is missing, unreadable, has the wrong file type, lacks a required execute permission, or its interpreter cannot be resolved, the update fails and reports every invalid path, each tagged with what happens to that runtime's config next. For Claude Code and other settings.json-based runtimes, this check runs before the update writes settings.json, so nothing new is persisted (an earlier settings.json/settings.local.json migration, if one applied, is the one exception and stays applied) — reported as "not persisted". For Codex, this reverts config.toml/hooks.json along with the rest of that runtime's pre-install snapshot (skills/, agents/, msd-core/VERSION) — reported as "reverted". For Cursor, Windsurf, Kimi, and Cline, the runtime's config file is already written earlier in the update, ahead of this check, so a failure is reported but that file is left in place, broken — reported as "NOT reverted". Fix the reported path problem and rerun `/msd-update` — do not restart into the incomplete update.
If a script is missing, unreadable, has the wrong file type, lacks a required execute permission, or its interpreter cannot be resolved, the update fails and reports every invalid path, each tagged with what happens to that runtime's config next. For Claude Code and other settings.json-based runtimes, this check runs before the update writes settings.json, so nothing new is persisted (an earlier settings.json/settings.local.json migration, if one applied, is the one exception and stays applied) — reported as "not persisted". For Codex, this reverts config.toml/hooks.json along with the rest of that runtime's pre-install snapshot (skills/, agents/, msd-core/VERSION) — reported as "reverted". For Cursor, the runtime's config file is already written earlier in the update, ahead of this check, so a failure is reported but that file is left in place, broken — reported as "NOT reverted". Fix the reported path problem and rerun `/msd-update` — do not restart into the incomplete update.
Restart your runtime after a successful update to pick up new commands and agents.