Plugin-Activated Main-Agent Override and Bin/ PATH Injection¶
A plugin's
settings.jsonswaps the main thread agent and itsbin/directory injects executables onto the Bash tool's PATH.
A plugin's root settings.json activates one of its bundled agents as the main thread; the same plugin's bin/ directory adds executables to the Bash tool's PATH for the plugin's enabled lifetime. Enabling the plugin reshapes the agent without editing global settings; disabling reverses both legs. The contract requires Claude Code v2.1.157 or later — earlier versions silently ignored the agent field.
The Two Contracts¶
settings.json.agent swaps the main thread¶
A plugin places settings.json at its root. "Plugins can include a settings.json file at the plugin root to apply default configuration when the plugin is enabled. Currently, only the agent and subagentStatusLine keys are supported." (Create plugins — Ship default settings)
{
"agent": "security-reviewer"
}
"Setting agent activates one of the plugin's custom agents as the main thread, applying its system prompt, tool restrictions, and model" (Create plugins). The named agent must exist as agents/<name>.md inside the plugin.
bin/ injects executables onto PATH¶
A bin/ directory at the plugin root publishes executables to the Bash tool's environment: "bin/ — Executables added to the Bash tool's PATH. Files here are invokable as bare commands in any Bash tool call while the plugin is enabled" (Plugins reference — File locations). Executables are exposed under their filename — there is no plugin-name: namespace.
Precedence and Per-Session Override¶
- Plugin-internal: "Settings from
settings.jsontake priority oversettingsdeclared inplugin.json. Unknown keys are silently ignored." (Create plugins) - Per-session: "the
agentfield insettings.jsonis now honored for dispatched sessions, with--agent <name>to override it" (Claude Code changelog, v2.1.157).
How It Differs From Related Primitives¶
| Primitive | Activation | Lifetime | What it changes |
|---|---|---|---|
settings.json.agent (plugin) |
Plugin enabled | Plugin enabled | Main thread agent — prompt, tools, model |
| Sub-agents | Claude delegates | Per delegation | Isolated worker context — does not replace main |
| Managed Settings Drop-In | Admin push | Until policy change | Org-wide configuration fragments |
bin/ PATH injection |
Plugin enabled | Plugin enabled | Bash tool's PATH |
| Plugin Background Monitors | Plugin enabled (auto-arm) | Session | Stdout-to-notification stream |
Distinct from sub-agents because it replaces (not augments) the default personality, and from managed settings because scope is plugin enable/disable, not policy lifetime.
Why It Works¶
The pattern collapses two operations — swap the agent personality and make a set of binaries available — into one declarative file structure that activates atomically when the plugin is enabled and reverses when it is disabled. Atomicity is load-bearing: agent override and binaries appear together, not in two install steps that could partially fail. The plugin-scoped bin/ lives inside ${CLAUDE_PLUGIN_ROOT} and is added to PATH only while the plugin is enabled (Plugins reference), so /plugin disable removes both the agent and the PATH entry without per-developer cleanup.
When This Backfires¶
- Pre-v2.1.157 the
agentfield is silently dead. Dispatched sessions did not honour it until that release (Claude Code changelog). On older clients the plugin loads, the binaries land on PATH, and the main thread stays the default — half the contract ships with no error. - A managed-enabled bundle is a silent permission expansion. An admin force-installing a plugin via
enabledPlugins(Plugin marketplaces) where the plugin shipssettings.json: { "agent": "permissive-helper" }swaps every developer's main thread without per-developer consent. Review the plugin'sagents/<name>.mdtool allowlist and model before org-wide enablement — see Enterprise-Managed Plugin Governance for Agent CLIs. - Bin names collide silently.
bin/entries are bare command names with no namespace. Two enabled plugins shippingbin/deployresolve to whichever appears first on PATH; the developer thinks they're running one tool and they're running the other. - Only two keys are recognised. Treating
settings.jsonas a general per-plugin config file produces no error and no effect (Create plugins). - Composability is reduced. A consumer who wants the activated agent but a different binary set has to fork the plugin — the bundle locks the two together.
- Marketplace injection threatens both legs at once. PromptArmor and SentinelOne marketplace-injection disclosures target plugin-supplied code; a tampered bundle swaps the agent personality and adds attacker-controlled binaries to PATH in one install.
Example¶
A security-reviewer plugin that activates a hardened review agent and ships the binaries the review workflow shells out to.
security-reviewer-plugin/settings.json:
{ "agent": "security-reviewer" }
security-reviewer-plugin/agents/security-reviewer.md (restricted tool allowlist, pinned model):
---
name: security-reviewer
description: Security-focused review of diffs against an approved-action policy
model: sonnet
tools: [Read, Grep, Glob, Bash]
---
You are a security reviewer. Read the diff with Read+Grep+Glob.
Run only `semgrep-scan`, `gitleaks-scan`, and `trivy-scan` via Bash.
Refuse other shell commands. Report findings with severity.
Plugin layout — agent + binaries in one bundle:
security-reviewer-plugin/
├── .claude-plugin/plugin.json
├── settings.json
├── agents/security-reviewer.md
└── bin/
├── semgrep-scan
├── gitleaks-scan
└── trivy-scan
/plugin enable security-reviewer swaps the main thread to the security-reviewer agent and makes the three scanners callable as bare commands. /plugin disable reverts both. Mid-session, claude --agent default restores the default agent without disabling the plugin if the PATH bundle is still wanted.
Key Takeaways¶
settings.json.agentreplaces the main thread with one of the plugin's bundled agents — prompt, tool restrictions, and model are all applied- Requires Claude Code v2.1.157 or later; earlier versions ignore the field with no error
settings.jsonkeys override the same keys declared inplugin.json; unknown keys silently no-op--agent <name>on the CLI overrides the plugin-activated agent for the current sessionbin/publishes bare-name executables onto the Bash tool's PATH for the plugin's enabled lifetimebin/names are un-namespaced — colliding executables resolve by first-on-PATH, silently- The bundled pairing plus
enabledPluginsforce-install is a permission-expansion vector that deserves pre-rollout review
Related¶
- Plugin Background Monitors — sibling primitive: declarative supervision auto-armed at session start from a plugin manifest
- Sub-Agents — the agent definition format the
settings.json.agentkey activates - Managed Settings Drop-In Directory — orthogonal admin contract for distributing settings without merge conflicts
- Extension Points — decision framework for choosing between CLAUDE.md, rules, skills, hooks, subagents, MCP, and plugins
- Enterprise-Managed Plugin Governance for Agent CLIs — the
enabledPluginsforce-install surface that turns plugin-activated overrides into org-wide policy - Plugin and Extension Packaging — the broader distribution model that bundles
settings.jsonandbin/alongside agents, skills, hooks, and MCP servers