Files
msd-core/docs/tutorials/embed-msd-in-a-new-host.md
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

3.0 KiB

Tutorial: embed MSD in a new host

This tutorial walks through embedding MSD's orchestration loop into a brand-new host end-to-end — from classifying the host, to composing the engine adapters, to running a MSD command through the embedded engine. It is the learning path that pairs with the how-to (steps) and the reference (spec).

You will build a minimal programmatic-cli host-plugin that invokes a MSD command via the embedded engine. No msd-core source changes.


Prerequisites

  • The MSD Host-Integration SDK entry importable in your host's runtime.
  • Your host's authoritative docs open (you'll declare axes from them, never infer).

Step 1 — Classify the host

Your host is a programmatic CLI with an event bus and a passive model (it can only receive prompts, not call a model for you). That is the programmatic-cli profile.

const SDK = require('@golem15/msd-core/sdk');
// Confirm the profile from your declared axes:
const profile = SDK.profileOf({ embeddingMode: 'imperative', runtime: 'node' });
// → 'programmatic-cli'

Step 2 — Compose the engine adapters

const hookBus = SDK.createHookBus({ bus: 'host' });     // your host fires events
const model   = SDK.createModelAdapter({ modelMode: 'passive' });
const stateIO = SDK.createStateIO({ io: 'filesystem' });
const adapter = SDK.createImperativeAdapter({ runtime: 'my-cli' });

Step 3 — Negotiate capabilities

const req = SDK.buildHandshakeRequest({
  embeddingMode: 'imperative', commandSurface: 'slash-file', modelMode: 'passive',
  hookBus: 'host', stateIO: 'filesystem', transport: 'mcp', runtime: 'node',
});
const { effective, points, warnings } = SDK.handleHandshakeRequest(req);
// `effective` is the negotiated capability set; `points` is per-interface-point
// degradation; `warnings` flags any axis the host omitted.

Step 4 — Run a MSD command through the embedded engine

The imperative adapter exposes the engine surface; your host binds its command surface (slash commands, palette, chat) to it. A user invoking /msd-phase in your host dispatches through the embedded engine exactly as it would in a first-party host — that is the parity the interface guarantees.

Step 5 — Verify parity

Assert that your host-plugin, built from the SDK entry alone, produces the same negotiated result as the in-process path. The SDK smoke test (tests/sdk-smoke.test.cjs) is the template — copy its structure for your host's own parity test.

Recap

You classified a host, declared its axes from authoritative docs, composed the adapters, negotiated capabilities over the (serialized) handshake, and ran a MSD command through the embedded engine — all without touching msd-core source. A new host is now a plugin you wrote.

Next