Skip to content

Plugin-Activated Main-Agent Override and Bin/ PATH Injection

A plugin's settings.json swaps the main thread agent and its bin/ 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.json take priority over settings declared in plugin.json. Unknown keys are silently ignored." (Create plugins)
  • Per-session: "the agent field in settings.json is now honored for dispatched sessions, with --agent <name> to override it" (Claude Code changelog, v2.1.157).
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 agent field 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 ships settings.json: { "agent": "permissive-helper" } swaps every developer's main thread without per-developer consent. Review the plugin's agents/<name>.md tool 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 shipping bin/deploy resolve 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.json as 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.agent replaces 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.json keys override the same keys declared in plugin.json; unknown keys silently no-op
  • --agent <name> on the CLI overrides the plugin-activated agent for the current session
  • bin/ publishes bare-name executables onto the Bash tool's PATH for the plugin's enabled lifetime
  • bin/ names are un-namespaced — colliding executables resolve by first-on-PATH, silently
  • The bundled pairing plus enabledPlugins force-install is a permission-expansion vector that deserves pre-rollout review
Feedback