TL;DR Start with
claude --plugin-dir ./pathand your local copy beats the installed plugin of the same name for that session. No uninstall, no version bump, no push just to see your own change.
You have a plugin installed, yours or your team's. You edit a skill, save, and want to watch it run. That is where the detour starts: commit, push, bump the version in plugin.json, update the marketplace, reinstall. Meanwhile the session you already have open keeps loading the old copy and never says a word about it.
The flag that skips the whole detour has been in the docs for a while, framed as "this is how you test the plugin you are building from scratch". The rule that makes it genuinely useful sits halfway down a long section: a copy loaded with --plugin-dir shadows an installed plugin of the same name for that session.
The proof, on my own machine
I have craft installed from my marketplace at 3.0.0, and the repo at ~/code/craft sitting at 3.1.0. The difference between them is one new skill, worktree, that only exists locally.
Start with --debug and Claude Code writes where each plugin came from into ~/.claude/debug/<session-id>.txt. Without the flag:
[DEBUG] Attempting to load skills from plugin craft default skillsPath:
/Users/juan.nunez/.claude/plugins/cache/craft/craft/3.0.0/skills
[DEBUG] Loaded 7 skills from plugin craft default directory
And pointed at my local copy:
[DEBUG] Loaded inline plugin from path: craft
[DEBUG] Plugin "craft" from --plugin-dir overrides installed version
[DEBUG] Attempting to load skills from plugin craft default skillsPath:
/Users/juan.nunez/code/craft/skills
[DEBUG] Loaded 8 skills from plugin craft default directory
That second line is the rule stated by the product itself, and that sentence is nowhere in the documentation. Seven skills against eight, and the eighth is worktree, the one I only have locally.
No duplicate namespace, no conflict: the local copy stands in for the installed one and the name is still craft. Close the session and everything is back as it was. Nothing was uninstalled, your settings.json was never touched, and the installed plugin is still there for every other session.
The one exception is a plugin your organization's managed settings force on or off. The flag cannot override those.
The loop while you edit
Inside the session, /reload-plugins picks up whatever you change without a restart: skills, agents, hooks, and the plugin's MCP and LSP servers.
Reloading is not free, though. Changing tool definitions invalidates the prefix cache, so the next turn reprocesses the whole conversation at full price. On a short session it does not matter; on a session that has been running for hours it shows. If you would rather see the number than guess, the /cost line names what broke your cache and why, and tool definitions changed is exactly this cause. The move is the usual one: batch a few edits and reload once, rather than reloading after every save.
A whole folder, not one plugin
If you keep several plugins together, you do not need to repeat the flag for each. Point it at the parent folder and it loads everything inside:
claude --plugin-dir ./plugins
I tried it with three throwaway plugins and one loose directory with no manifest, and the --debug log spells it out in a single line:
[DEBUG] --plugin-dir ./plugins is a folder of plugins: loading alpha, beta,
gamma; no manifest in notaplugin
[DEBUG] Loaded 3 directory-loaded plugins
It reads the top level only: every subfolder with a .claude-plugin/plugin.json loads as its own plugin, and anything without a manifest stays out without erroring. That is the reason to start with --debug the first time: a normal session never prints that line, so a plugin whose manifest you forgot simply does not show up. When something refuses to load, claude plugin validate tells you why.
In an interactive session it also watches the folder: drop a subfolder in and it loads as a new plugin on the spot, take one out and it unloads. Claude Code prints a line for each change. When applying one mid-conversation would break the cache, it holds the change and tells you to run /reload-plugins yourself.
A zip that is not on your disk
For something that already comes packaged, a CI artifact or a release candidate, there is the sibling flag:
claude --plugin-url https://example.com/my-plugin.zip
It is fetched at startup and lives for that session only. If the fetch fails or the archive is invalid, Claude Code still starts and leaves the error in the Errors tab of the /plugin manager. The same caution applies as with any other source: point it only at archives you control.
Both flags repeat to load several, and --plugin-url also takes space-separated URLs inside one quoted argument.
Reference
| How you load it | What comes in | When you reach for it |
|---|---|---|
--plugin-dir ./my-plugin |
That folder, as one plugin | You are editing one specific plugin |
--plugin-dir ./my-plugin.zip |
The zip, unpacked | Someone handed you a package |
--plugin-dir ./plugins |
Every subfolder with a manifest | You maintain several at once |
--plugin-url https://… |
A remote zip, that session only | CI artifact, release candidate |
/plugin install |
Permanent, in your config | You already trust it |
The first four never touch your configuration and vanish when you close the session. The one that really installs is the fifth, and from there the weight rides along in every message.
With this the development loop comes down to edit, /reload-plugins, look. The commit becomes the last step instead of the toll you pay to test. If you do not have a plugin yet, start with what one actually is; when you want to hand it out, the marketplace is the next step.
Official docs: Create plugins
Requirements
- Claude Code v2.1.265 or later to point
--plugin-dirat a folder of plugins. Pointed at a single plugin or a.zip, the flag has worked since earlier versions. - Everything above was run on v2.1.272. I verified
--plugin-urlby serving the zip from a local server, not from a public URL. - The live folder watch is interactive-session only, and that part comes from the documentation: I did not capture it.
- In the
--debugblocks I shortened the absolute paths from my own disk. Everything else is verbatim.