If you develop a Claude Code [1] plugin and your users have it installed from the marketplace, you have a collision problem. Two plugins named tts. Two /say commands. Two MCP [2] servers. Claude Code sees both, and the collision behavior is undefined. That’s not a development workflow.
Here’s the pattern we’ve been using across seven plugins [3].
The -dev Suffix
The working tree uses name: "tts-dev" in plugin.json. The marketplace version uses name: "tts". Launch Claude Code with --plugin-dir . and both load simultaneously — dev commands appear at /tts-dev:say, prod at /tts:say. Every extension point namespaces cleanly: commands, MCP tools, skills, agents, hooks.
This gives you side-by-side testing. You can verify that the dev plugin works against the currently-installed prod version without uninstalling anything.
Launching Dev vs Prod
The --plugin-dir flag tells Claude Code to load a plugin directly from a local directory for the current session. Combined with the -dev naming convention, this gives you three launch modes:
# Both dev and prod — side-by-side testing (from the plugin's repo root)
claude --plugin-dir .
# Prod only — normal user experience
claude
# Dev only — uninstall the marketplace version first
claude plugin uninstall tts
claude --plugin-dir .
With --plugin-dir ., Claude Code loads the local plugin.json (which has the -dev name) alongside any marketplace-installed plugins. Both namespaces coexist: /tts-dev:say runs the working tree code, /tts:say runs the installed version. Without the flag, only marketplace plugins load — your users never see the -dev variants.
Release Workflow
The release workflow swaps the name to prod, tags the release, then restores the dev name:
release-plugin.sh— setsname: "tts"inplugin.json, commits, tagsrestore-dev-plugin.sh— setsname: "tts-dev", commits
This produces three sequential commits on main:
[release] → [prepare: name=tts] → [restore: name=tts-dev]
↑ tag v0.4.0 ↑ HEAD
The tag points to the prepare commit with the prod name. HEAD points to the restore commit with the dev name. The marketplace installs from the tag — pin source.ref in marketplace.json to the release tag so users always get the prod name.
Detection
punt audit detects drift automatically:
- Plugin name in
plugin.jsonmust end in-dev - Both
release-plugin.shandrestore-dev-plugin.shmust exist - Every prod command must have a
-devvariant
This catches drift after changes. If the dev plugin adds a new command but the prod variant is missing, audit fails.
Limits
The pattern requires maintaining parallel command files — one prod, one dev — which is mechanical but error-prone. punt audit catches drift after the fact. Prevention would be better than detection.
Other ecosystems handle dev/prod differently — npm has --tag, Docker has tag conventions. Claude Code plugins don’t have this yet. If Anthropic adds native dev/prod support, this pattern becomes unnecessary — and that would be a good outcome.
References
- Anthropic. “Claude Code.” 2024–present. docs.anthropic.com
- Anthropic. “Model Context Protocol.” 2024–present. modelcontextprotocol.io
- Punt Labs. “Distribution Standard.” github.com/punt-labs/punt-kit