--bare: --plugin-dir loads plugins into a session with no Skill tool, and also re-enables all globally-installed plugins
Version: 2.1.222 (Claude Code), macOS
Two related surprises when combining --bare with --plugin-dir. Both are visible in the system/init event, so the repro needs no API key — the CLI emits init before it authenticates.
1. --bare has no Skill tool, so --plugin-dir can load skills that can never be invoked
Under --bare the built-in tool set is clamped to Bash, Edit, Read:
echo '{"mcpServers":{}}' > /tmp/mcp.json
claude -p hi --bare --output-format stream-json --verbose \
--strict-mcp-config --mcp-config /tmp/mcp.json \
--plugin-dir /path/to/a/plugin | head -1 | jq -c '.tools'
["Bash","Edit","Read"]
--tools default does not change it, and an explicit --tools "Skill,Read,Bash" yields ["Bash","Read"] — the requested set is intersected with the clamp, and Skill is dropped silently.
Meanwhile --plugin-dir does register the plugin: its commands and skills all appear in slash_commands. So a session can have a plugin's skills loaded and listed while holding no tool capable of invoking them. --bare's help text says "Skills still resolve via /skill-name", which is accurate but easy to read as "skills work here" — the model-initiated path is gone, and nothing says so.
This is not hypothetical for us: we run a behavioural eval that compares a session with a plugin against one without it, using --bare for isolation. Every measurement it produced was two runs of the base model, because no skill could fire in either arm. The transcripts looked healthy — the model simply used Bash to find / and cat the plugin's files off disk instead.
Suggestion: either include Skill in the --bare set when --plugin-dir / --plugin-url is present, or have an explicit --tools request naming Skill fail loudly rather than being silently intersected away.
2. --plugin-dir under --bare also re-enables every globally-installed plugin
--bare documents "skip … plugin sync". With it alone, no installed plugin loads — 48 slash commands, all built-ins. Add --plugin-dir and the operator's entire installed set comes back alongside the requested one:
| flags | slash commands |
|---|---|
| --bare | 48 |
| --bare --plugin-dir <one plugin> | 645 |
The extra 597 are 19 from the requested plugin and 578 from unrelated plugins installed in ~/.claude/plugins. --plugin-dir appears to switch the plugin subsystem on wholesale rather than adding one plugin to an otherwise-empty set.
For anyone using --plugin-dir to test a plugin in isolation, this quietly makes the session non-isolated — and it is invisible unless you diff slash_commands between the two configurations.
Repro: the table above is | head -1 | jq '.slash_commands | length' on the same command as in §1, with and without --plugin-dir.
Workaround, for anyone hitting this
Drop --bare and point CLAUDE_CONFIG_DIR at an empty directory instead. That gives isolation (no installed plugin, setting, hook or memory) and the full tool set including Skill, with the requested plugin the only one registered. The cost is that credentials are scoped to the config directory, so it needs ANTHROPIC_API_KEY rather than an interactive login.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗