cmx-core
Embeddable core for installing agent skills across platforms. Extracted from cmx, the context-mixer CLI.
CLI tools that ship a companion agent skill use this crate to install it, instead of hand-rolling file copies into hard-wired paths:
use ProductionContext;
use ;
let skill = single_md;
// The version passed here is stamped into the installed SKILL.md's
// metadata.version — the bundled file needs no version of its own.
let installer = new;
let prod_ctx = claude?;
let ctx = prod_ctx.ctx;
let plan = installer.plan?;
println!; // dry-run: names every file and destination
let report = installer.apply?;
println!; // summary: platform, action, destination, version
Context
ProductionContext::claude() is the one-call default for Claude Code tools. It is
equivalent to ProductionContext::from_env(Platform::Claude).
The platform argument passed to from_env sets the default platform binding for
lock-file and path resolution (which cmx-lock*.json file is primary, which install
directory is the default). It does not set the config root directory — that is
always $HOME/.config/context-mixer — and it does not determine which platforms
a skill installs to. Installation targets are resolved at plan time from the cmx config
and existing lock files on the machine.
One version, declared once
You declare your version exactly once — the string passed to ToolIdentity. cmx-core
records it in the lock entry and reconciles the installed SKILL.md frontmatter's
metadata.version to match, automatically, at plan/apply time. You do not hand-roll
a frontmatter stamper, and the bundled SKILL.md needs no version placeholder — whatever
metadata.version it carries (or doesn't) is overwritten with the ToolIdentity version
on write.
This closes a gap that used to force every embedder to stamp the frontmatter itself:
the lockfile tracks the version, but readers like cmx doctor / cmx list parse it back
out of the installed SKILL.md. If the two disagree, the skill reports a wrong (or
missing) version. cmx-core now keeps them in lockstep on the community-standard
metadata.version key.
The reconciliation is surgical and idempotent: it rewrites only the version line
(preserving folded description blocks, comments, and key order), removes any shadowing
top-level version:, and produces byte-identical output on a re-install, so it composes
cleanly with the skip/drift guards below. cmx-lock.json / cmx-lock-<platform>.json
remain the source of truth for tracking; the frontmatter is kept consistent with it.
remove() semantics
remove() deletes the installed skill directory and clears the tool's entry from the
platform lock file(s). It intentionally leaves the shared cmx-lock.json file on disk
— that file is shared with other tools and cmx itself. The RemoveReport display
output notes this explicitly.
Multi-file bundles
Skills with more than one file build the bundle from SkillFile::text entries; relative paths (including subdirectories) are preserved under the installed skill directory:
let skill = from_files;
Testing your integration
The test-support feature exposes test_support::TestContext, an in-memory context for exercising your init command without touching the real filesystem. Because features unify across a dependency graph, enable it from [dev-dependencies] by repeating the dependency with the feature:
[]
= { = "0.1" }
[]
= { = "0.1", = ["test-support"] }
TargetAction is non-exhaustive
TargetAction is marked #[non_exhaustive]: new variants may be added in future
minor releases without a breaking change. Embedders should render actions via the
Display impl on InstallPlan and Report, or branch on specific variants they care
about with a catch-all _ arm. The will_write() and is_blocked() helpers cover
the two most common branching points without requiring exhaustive matching.
What you get
- Platform-aware destinations — knows the skill directories for Claude, Codex, Cursor, Copilot, and ten other agent platforms, at global or project scope (global by default).
- cmx integration — on a cmx-managed machine, the skill is registered as a tracked artifact that
cmx doctor,cmx list, andcmx updateall understand. Without cmx, the install still works and still records a lock entry: the lockfile format is the integration contract, so a later cmx arrival finds everything tracked instead of orphaned. - Standardized version guard — older installed → update; same version with identical content → skip; newer installed → refuse unless forced.
- Plan/apply — every mutation previews precisely before it happens;
applyperforms exactly whatplanreported. Both returnDisplay-able types for consistent CLI output across all embedding tools. - Drift detection —
DriftedSkiptargets (same version, content edited locally) are preserved and rendered distinctly in the report so users see "local edits preserved" rather than a silent skip. - Uninstall —
remove()deletes the installed files and cleans up the lock entry.
The design and fleet-wide roadmap (ports to Python, TypeScript, and beyond) live in EMBEDDING.md.
License
MIT