litmus
litmus
Never let a test plan slip. litmus is a Claude Code plugin that watches your edits, works out which test types the change likely needs, and — depending on the cadence you pick — either quietly reminds you or holds the turn until you've triaged a plan. It ships language-agnostic guides for nine test types and a /tdd skill.
litmus is not a linter and it does not grade the tests you write. It sits one step earlier: making sure test planning doesn't get forgotten, and injecting the right writing rules as context when you do write tests. For grading written tests, reach for /code-review instead.
It belongs to no vendor, phones home to nothing, and collects no telemetry.
How it works — three axes
Axis | Question | What it is |
Hooks | When do I get reminded? | Detect source changes, accumulate them per session, and nudge for a test plan at the end of a turn. |
MCP server | What do I test, by which rules? | Serves the test-type map and per-type guides as tools ( |
Skill | How do I drive it? |
|
The guides themselves are nine language-agnostic docs in guides/ — per-type must-cover cases and writing principles. The MCP server is one way to read them; you can also open the files directly.
What you'll see
When you change source and end a turn, litmus reminds you. In advisory mode it's a non-blocking note:
🧪 litmus — 2 source file(s) changed (140 lines) this session without a test plan.
Test types this change likely touches:
- Integration Test (high) — API endpoint change; persistence / DB change
Load a type's rules with the litmus get_guide tool (e.g. get_guide "regression"),
or read guides/ directly. Run /tdd to drive it test-first.In block mode it holds the turn and walks you through a two-step triage instead:
STOP — litmus (block mode) requires a test plan before this turn ends.
Scope: 3 source file(s), 123 lines. Triage mode: FULL.
STEP 1 — TRIAGE (one markdown table, nothing else) ... then ask which types to detail.
STEP 2 — DESIGN (only the confirmed types) ... load each guide, extend existing tests.The suggested types are regex-based guesses (from file paths and diff signals), tagged
high/medium confidence — hints to confirm against the code, not verdicts. Editing a
test file, or only docs/config, triggers nothing.
Test type map
Four levels, nine types:
Level | Test type | Owner | Guide |
Code | Unit Test | Engineer | |
Code | Integration Test | Engineer | |
System | Sanity Test | Engineer | |
System | Functional Test | Engineer | |
System | Scenario Test | Engineer | |
System | Performance Test | Engineer | |
Regression | Regression Test | Engineer + PM | |
Product | Policy Test / UAT | PM | |
Product | Design / UX Test | PD |
Entry points by role
Role | Start here |
Engineer, changing logic | Unit ( |
Engineer, changing an API/endpoint | Integration ( |
Engineer, changing persistence/DB | Integration ( |
Engineer, changing the UI | Functional ( |
Engineer, before a release | Sanity ( |
PM / Product Owner | Policy Test / UAT ( |
Designer (PD) | Design / UX Test ( |
Install
Requires Node.js 18+ on your PATH — Claude Code runs the hooks and the MCP server with node.
/plugin marketplace add retemper/litmus
/plugin install litmus@retemperLocal development (point the marketplace at a local checkout instead):
/plugin marketplace add /path/to/litmus
/plugin install litmus@retemperRun /reload-plugins (or restart Claude Code) to apply. On first use, litmus asks you two questions (cadence + forcefulness) and saves your answers. Until then it runs in its quietest useful mode.
Updating
Self-hosted marketplaces don't auto-update by default, so new versions won't arrive on their own. Either update on demand:
/plugin update litmus@retemper…or enable auto-update once by adding the marketplace to your Claude Code settings.json:
{ "extraKnownMarketplaces": { "retemper": { "autoUpdate": true } } }With that set, litmus updates itself whenever Claude Code starts. (Your personal config in ~/.claude/litmus lives outside the plugin cache, so updates never touch your settings.)
Configuration
Two knobs plus two thresholds.
cadence — when to nudge:
Value | Behaviour |
| every turn that changed source |
| only when the change is large (see thresholds) |
| once per git branch |
| once a day |
| disabled |
forcefulness — how to nudge:
Value | Behaviour |
| a non-blocking system message |
| holds the turn and walks you through a 2-step triage |
Thresholds (only used by by-scale): minFiles (default 3) and minLines (default 80). A change at or under both is "light" and skipped by by-scale.
Where config comes from (highest precedence first)
Environment:
LITMUS_CADENCE,LITMUS_FORCEFULNESS,LITMUS_MIN_FILES,LITMUS_MIN_LINESProject:
.litmus.json(searched from the cwd up to 8 levels)Personal:
~/.claude/litmus/config.jsonUnset
Safe defaults — litmus never goes silently dead:
Fully unset →
by-scale+advisory(the quietest mode that still speaks up).If you set
cadencebut notforcefulness, forcefulness defaults toblock.To turn it off completely, set
cadence: "off".
Example .litmus.json:
{ "cadence": "by-scale", "forcefulness": "advisory", "minFiles": 3, "minLines": 80 }The block-mode workflow
In block mode the reminder isn't a warning — it's a 2-step triage that keeps a turn from dumping a wall of test cases:
Triage — one markdown table: each test type, needed? (✅/❌/🔶), a one-line reason, and any existing test path (you search first). Then it stops and asks which types to detail.
Design — only for the types you confirm, it reads that type's guide and writes cases — extending existing tests rather than duplicating them.
MCP tools
The plugin starts a small stdio MCP server (litmus) exposing:
Tool | Input | Returns |
| — | the four-level, nine-type map as a table |
|
| the full guide for that type |
|
| which guide(s) to read, scored by keyword |
README and each guide are also exposed as resources (litmus://README.md, litmus://01-unit-test.md, …). Everything is read from disk at call time, so editing a guide takes effect immediately — no rebuild.
Development
The MCP server lives in mcp/ (TypeScript). Because Claude Code clones plugins without running npm install, the server ships as a single self-contained bundle at mcp/bundle/litmus-mcp.mjs, which must be committed.
cd mcp
npm install
npm run smoke # parser checks against the real guides
npm run bundle # rebuild mcp/bundle/litmus-mcp.mjs (commit the result)
node test-handshake.mjs # spawn the bundle and exercise the MCP protocolHooks, guides, and the skill are plain files — no build step; edit and commit. The version lives only in .claude-plugin/plugin.json; the MCP server reads it from there at runtime, so it can never drift.
State & privacy
Volatile per-session change markers live in your OS temp dir and are cleaned after 24h.
Persistent config / onboarding / cadence state live in
~/.claude/litmus(or$LITMUS_DATA), outside the plugin cache so they survive updates.No network calls. No usage tracking. Nothing leaves your machine.
License
MIT © retemper