Skip to main content

Local Development

For people working on modelith itself — the binary, the docs, or the Claude Code plugin. For using the tool, start with the overview; for the format, see the schema reference. Agents working on the repo should also read CLAUDE.md, which holds the repo layout and the CI-breaking conventions summarized at the bottom of this page.

Prerequisites​

  • Go 1.26 — the binary and tooling are pure Go.
  • Task — the task runner (brew install go-task).
  • The claude CLI — only needed to validate/develop the plugin locally.
  • Node.js / npx — only needed for task mermaid-check (runs @mermaid-js/mermaid-cli via npx); not required for task check.

Building the binary​

task build # → ./bin/modelith
task install # go install into GOBIN (on your PATH)

During development you can also skip the build and run from source — go run ./cmd/modelith lint <file> — which is what the lint-models and render tasks do.

The Task workflow​

The repo uses Task. The one that matters before pushing:

task check

It runs the CI checks plus stricter local plugin validation. Run task with no arguments to list every target.

CommandWhat it does
task buildBuild the binary into ./bin/modelith.
task installgo install modelith into GOBIN.
task testRun unit tests with cross-package coverage (writes coverage.out).
task coverShow the per-function coverage report (run task test first).
task vetRun go vet.
task staticcheckRun staticcheck (pinned version).
task lint-modelsLint the example models (strict: completeness gaps are errors).
task renderRe-render the example models to Markdown.
task render-checkVerify the committed Markdown is up to date.
task validate-pluginValidate the plugin with claude plugin validate --strict (needs the claude CLI).
task mermaid-checkParse every emitted/committed Mermaid diagram with the real mermaid-cli (needs npx/node). Not part of task check — see below.
task checkCI parity (vet, staticcheck, golangci-lint, test, lint-models, render-check) plus validate-plugin. It does not run mermaid-check, so contributors without node/npm still get a green task check; CI runs the Mermaid parse check separately.
CI runs a lighter plugin check

task check runs claude plugin validate ./plugin --strict locally, which performs the full plugin validation. CI uses a lighter jq-based structural check for valid JSON, required manifest fields, and skill front matter, so the Go pipeline does not depend on the Claude Code CLI.

Developing the plugin locally​

modelith ships a Claude Code plugin under plugin/ (skills that author, lint, and load domain models). The plugin files live here, next to the binary they drive, so the skills and CLI version stay in lockstep.

To iterate on the plugin without publishing, point Claude Code straight at the plugin directory:

claude --plugin-dir /path/to/modelith/plugin

You can pass --plugin-dir more than once to load several plugins at once. The plugin's skills are namespaced under its name — invoke them as /modelith:domain-model-author, /modelith:domain-model-lint, etc. Changes to the plugin files are picked up on the next Claude Code session.

Already running the installed plugin? Uninstall it before testing locally

If modelith@claude-community is already installed, both copies load with identical skill names and you can't tell which is active. Uninstall the managed one before testing locally, and reinstall when you're done:

claude plugin uninstall modelith@claude-community
claude --plugin-dir /path/to/modelith/plugin # test
claude plugin install modelith@claude-community

This same --plugin-dir trick is the cleanest way to try the authoring skills in a consuming repo (one that holds a *.modelith.yaml) before publishing — launch the session there with --plugin-dir pointing at your modelith checkout, rather than copying skill files around.

Validate after any change to a manifest or skill:

task validate-plugin # or: claude plugin validate ./plugin --strict

Conventions that break CI (summary)​

The full contract lives in CLAUDE.md; the load-bearing ones:

  • The example is a golden fixture. After any change to the renderer or examples/example.modelith.yaml, run task render to regenerate the committed .md. task render-check / CI fails on drift, and there's a golden test in internal/render/markdown. The example must also lint clean under task lint-models (strict).
  • Schema ↔ structs stay in sync. internal/schema/v1/modelith.schema.json and internal/model/model.go are guarded by TestSchemaStructSync. Every object is additionalProperties: false.
  • The canonical schema URL appears in three places (schema $id, the Go URLFor/URL, the example header); TestURLConsistency fails on drift.
  • The binary, not the schema, owns supported versions. Adding a format version = a new vN/ schema + a registry entry in internal/schema; never mutate a shipped version.