| title | Cross-system contracts |
|---|---|
| slug | cross |
| summary | External-surface contracts: LSP, CLI, .mdsmith.yml, generated markers, plugin manifest, distribution shims. Public APIs. |
mdsmith's external surfaces are public APIs. Each one has its own spec doc; this page only covers the cross-cutting architecture — how the surfaces relate, which way the dependencies point, and what versioning policy ties them together.
The solid-architecture skill reads this page in design and audit modes.
Each row points at the package that owns the contract. The surface's own spec (flags, schema, marker syntax, install steps) lives where the table's "Spec doc" column says.
| Boundary | Owner in repo | Spec doc | Consumers |
|---|---|---|---|
| LSP wire protocol | internal/lsp |
CLI reference: lsp |
VS Code extension, other editors |
| CLI flags + exit codes | cmd/mdsmith |
CLI reference | shell scripts, CI, git hooks |
.mdsmith.yml schema |
internal/config |
Conventions | every project using mdsmith |
| Generated section markers | internal/archetype/gensection |
Generated sections | every project's Markdown files |
| Claude plugin manifest (published) | editors/claude-code/.claude-plugin/plugin.json |
Install: Claude plugin | end users via Claude Code marketplace |
| Claude plugin manifest (contributors) | editors/claude-code-dev/.claude-plugin/plugin.json |
editors/claude-code-dev/README.md | mdsmith contributors |
| Claude marketplace listing | .claude-plugin/marketplace.json |
Install: Claude plugin | Claude Code marketplace |
| Claude skill definitions | .claude/skills/*/SKILL.md |
proto | Claude Code sessions |
| npm package shim | npm/mdsmith/ |
Install: npm | Node users |
| PyPI wheel shim | python/ |
Install: PyPI | Python users |
| asdf / mise plugin | external repos | Install: asdf / mise | language-tool users |
VS Code contributes |
editors/vscode/package.json |
VS Code integration | the extension host |
Treat each surface as a public API. mdsmith is at major 0 today, so strict SemVer does not bind us yet — but breaks must be deliberate and noted in the changelog so downstream consumers can keep up. Once we hit 1.0 a break is a SemVer-major event.
The Go binary is the source of truth. Every external surface adapts to it; the binary does not adapt to them.
Concretely:
- The LSP server exposes capabilities; clients subscribe.
- Distribution shims forward args to the binary.
- Plugin manifests declare an invocation.
- The VS Code extension consumes the LSP server and the binary.
None of them re-implement domain logic.
If a shim must translate something (platform-specific binary download, JSON re-shape, fallback to a bundled copy), that translation lives in the shim and is unit-tested there. It is not allowed to reach into the binary's internals.
A consumer of one surface should not be forced to install another. The npm shim consumer should not need the LSP server; the VS Code extension consumer should not need the Claude plugin. Keep each surface narrow and self-contained.
When the binary gains a feature, ask which surface(s) need to expose it. Resist exposing new fields on every output envelope "in case someone wants them".
Every shim is a substitute for invoking
the mdsmith binary directly: same
exit codes, same stdout / stderr
formats, same flag parsing.
A shim that adds a flag the binary does not have is a Liskov violation. Push the flag down to the binary, or drop it from the shim.
When a file resolves to several kinds that each
declare a required-structure schema, the
schemas compose. Three rules apply:
- Frontmatter conjoins. A key required by any
kind is required. Shared keys intersect with
CUE
&. - Sections merge. Scopes with the same heading text combine their child lists. Other scopes append in input order.
- The stricter
closed:wins.
Multiple kinds can layer schemas. For example,
directive-rule-readme builds on top of
rule-readme. See the
Schemas guide for a
worked example.
Today mdsmith is at major 0; the rules below describe the contract we are converging on. Each surface's own spec doc says how the rule applies to it.
- The CLI follows SemVer. Adding a flag is minor; renaming or removing one is major. Behavior changes that turn a passing run into a failing run are major.
- LSP capabilities can be additive without bumping major. Removing a capability is major.
- The
.mdsmith.ymlschema is additive within a major. Removing a field requires a deprecation window or a major bump. Renaming a field is removing one and adding another. - Generated section markers are
forever. Once a marker syntax is
shipped,
mdsmithmust continue to parse it (possibly with a deprecation diagnostic) until the next major. - Plugin manifests follow the host's versioning. Treat the declared servers, commands, and hooks as the visible interface.
The audit watches for these cross-system anti-patterns:
- A shim that parses Markdown to decide which args to pass.
- A consumer reading
.mdsmith.ymldirectly instead of asking the binary or the LSP server. The schema is owned byinternal/config; everything else consumes it throughmdsmith. - A plugin command wrapping a shell
call with bespoke logic. The logic
belongs in a
mdsmithsubcommand. - A field added to one surface that belongs in another. File-wide configuration lives in the YAML; per-run overrides live on the CLI.
- A response shape that varies by output mode. The shape is the contract; if a mode wants different data, define a different mode.
- A breaking change to a shipped surface with no deprecation window.
- A new LSP capability the VS Code extension is expected to use but does not yet opt into.
- Identify which existing surface(s) it composes with. If none, ask whether the new surface really needs to exist or could be served by an existing one.
- Write down the contract — what data
flows in and out, in what format.
Store it under
docs/reference/ordocs/guides/and link it from the boundaries table above. - Add a test that locks the contract inside the binary (a CLI flag test, an LSP fixture). Without a contract test, the surface drifts under refactor pressure.
- Decide the versioning policy and record it in the new doc.
- Confirm the surface follows the dependency-inversion rule: it adapts to the binary; the binary does not adapt to it.
- Duplicate logic across shims → push
the logic into a
mdsmithsubcommand consumed by all shims. - A field that lives in two contracts (e.g. CLI + YAML) and means different things → split into two fields with distinct names.
- A capability that exists only in one editor → either generalize it to LSP or scope it to that editor's package, never both half-formed.
- A test that touches three contracts → split into three tests, one per contract.