claude-code - 💡(How to fix) Fix Plugin manifest should support `permissions.allow` for bundled console scripts

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

A plugin that ships its own console-script binaries (e.g., this one bundles five: agent-coherence-status, agent-coherence-track, agent-coherence-untrack, agent-coherence-coordinator, agent-coherence-migrate-deny) has no current Claude Code mechanism to pre-allow those binaries in the Bash permission system. Every plugin slash command that invokes a bundled binary fires a per-invocation Bash permission prompt that the operator must approve. Plugin-shipped permission allowlists would close the gap.

Error Message

A plugin slash command (commands/status.md) declares allowed-tools: ["Bash"] in frontmatter and invokes a bundled binary. Operator invokes the skill; Bash permission prompt fires for the binary; operator must Allow once / Allow always or Deny per invocation.

Root Cause

Plugins that need any bundled binary at all (any storage/state primitive, any orchestrator, any local-server pattern) hit this gap. Without the platform mechanism, every plugin author re-implements the same operator workaround and every operator re-discovers the same friction. The current "skill allowed-tools: [Bash]" surface implies plugin authors can declare what their skills need, but only at coarse-grained tool granularity — the per-command gate then trips invisibly.

Plugin-shipped allowlists scoped to the plugin's own surface preserve the trust boundary (operator still controls what the plugin can do; plugin still self-declares its own binary set) while eliminating the per-invocation friction.

Fix Action

Fix / Workaround

The plugin-author intent expressed by the skill's allowed-tools: ["Bash"] is "this skill may use the Bash tool" — but the per-command permission gate still trips because the bundled binary isn't in the workspace's permissions.allow set. The operator has to maintain that set per-workspace per-plugin, and re-discover the workaround every time they install the plugin in a new workspace.

The only working operator-side workaround is documenting in the plugin README that users should add the allowlist to .claude/settings.local.json:

Allow plugins to ship a permissions.allow array in .claude-plugin/plugin.json, scoped to commands matching binaries the plugin actually ships (the bin/ directory's contents, plus any console-script entry points). Allowlist entries are merged into the active permission set when the plugin is enabled, removed when disabled. Per-binary granularity already matches the existing Bash(command:*) pattern from .claude/settings.json:

Code Example

{
  "permissions": {
    "allow": [
      "Bash(agent-coherence-status:*)",
      "Bash(agent-coherence-track:*)",
      "Bash(agent-coherence-untrack:*)",
      "Bash(agent-coherence-coordinator:*)",
      "Bash(agent-coherence-migrate-deny:*)"
    ]
  }
}

---

// .claude-plugin/plugin.json
{
  "name": "my-plugin",
  "version": "1.0.0",
  "permissions": {
    "allow": [
      "Bash(my-tool:*)",
      "Bash(my-tool-status:*)"
    ]
  }
}
RAW_BUFFERClick to expand / collapse

Summary

A plugin that ships its own console-script binaries (e.g., this one bundles five: agent-coherence-status, agent-coherence-track, agent-coherence-untrack, agent-coherence-coordinator, agent-coherence-migrate-deny) has no current Claude Code mechanism to pre-allow those binaries in the Bash permission system. Every plugin slash command that invokes a bundled binary fires a per-invocation Bash permission prompt that the operator must approve. Plugin-shipped permission allowlists would close the gap.

Observed behavior

A plugin slash command (commands/status.md) declares allowed-tools: ["Bash"] in frontmatter and invokes a bundled binary. Operator invokes the skill; Bash permission prompt fires for the binary; operator must Allow once / Allow always or Deny per invocation.

Screenshot (Phase E broad-beta monitoring 2026-05-26 — operator-side, v0.2.0 on the marketplace):

[attach the screenshot showing the rejected agent-coherence-status invocation]

The plugin-author intent expressed by the skill's allowed-tools: ["Bash"] is "this skill may use the Bash tool" — but the per-command permission gate still trips because the bundled binary isn't in the workspace's permissions.allow set. The operator has to maintain that set per-workspace per-plugin, and re-discover the workaround every time they install the plugin in a new workspace.

What I tried

I authored the plugin and worked through every documented surface for shipping a default allowlist with it:

  1. Adding permissions.allow to .claude-plugin/plugin.json — no schema field, silently ignored.
  2. Adding permissions.allow to plugin-root settings.json — per the plugins-reference doc: "Default configuration applied when the plugin is enabled. Only the agent and subagentStatusLine keys are currently supported" (emphasis added).
  3. Putting per-command granularity in skill frontmatterallowed-tools: [Bash(my-tool:*)] shape isn't accepted; the field is coarse-grained tool names only.
  4. Shipping .claude/settings.json packaged with the plugin.claude/ is gitignored by convention and isn't distributed with the plugin.
  5. Using the bin/ directory — correctly adds the binary to PATH (helpful for distribution!) but doesn't interact with the permission system.

The only working operator-side workaround is documenting in the plugin README that users should add the allowlist to .claude/settings.local.json:

{
  "permissions": {
    "allow": [
      "Bash(agent-coherence-status:*)",
      "Bash(agent-coherence-track:*)",
      "Bash(agent-coherence-untrack:*)",
      "Bash(agent-coherence-coordinator:*)",
      "Bash(agent-coherence-migrate-deny:*)"
    ]
  }
}

This works but places friction on every operator individually.

Proposed shape (minimum viable)

Allow plugins to ship a permissions.allow array in .claude-plugin/plugin.json, scoped to commands matching binaries the plugin actually ships (the bin/ directory's contents, plus any console-script entry points). Allowlist entries are merged into the active permission set when the plugin is enabled, removed when disabled. Per-binary granularity already matches the existing Bash(command:*) pattern from .claude/settings.json:

// .claude-plugin/plugin.json
{
  "name": "my-plugin",
  "version": "1.0.0",
  "permissions": {
    "allow": [
      "Bash(my-tool:*)",
      "Bash(my-tool-status:*)"
    ]
  }
}

Scope intentionally narrow:

  • Only permissions.allow — not permissions.deny; plugin-shipped denies could be safety-relevant but also become a misuse vector. Defer.
  • Only commands matching binaries the plugin ships — validated against bin/ and console-script declarations. Plugins can't pre-allow Bash(rm -rf:*) or anything outside their own surface.
  • Operator can still override — workspace-level .claude/settings.json permissions.deny always wins over plugin-shipped permissions.allow.

Why this matters

Plugins that need any bundled binary at all (any storage/state primitive, any orchestrator, any local-server pattern) hit this gap. Without the platform mechanism, every plugin author re-implements the same operator workaround and every operator re-discovers the same friction. The current "skill allowed-tools: [Bash]" surface implies plugin authors can declare what their skills need, but only at coarse-grained tool granularity — the per-command gate then trips invisibly.

Plugin-shipped allowlists scoped to the plugin's own surface preserve the trust boundary (operator still controls what the plugin can do; plugin still self-declares its own binary set) while eliminating the per-invocation friction.

Not asking for

  • General-purpose plugin-shipped Bash allowlists (e.g., Bash(curl:*)) — those should stay operator-controlled
  • permissions.deny at the plugin level — different threat model; defer
  • A model for cross-plugin permission inheritance — single plugin, single allowlist scope

Context

Authoring agent-coherence-plugin — a coordination-protocol plugin shipping 5 bundled binaries. v0.2.0 live on the marketplace 2026-05-24; Phase E broad-beta monitoring window through 2026-06-08 surfaced the operator-UX gap from a real install attempt.

Happy to put together a small reference plugin that demonstrates the proposed shape if useful for the review thread.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING