Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Browse and manage with `/plugin`. To refresh after updates: `/plugin marketplace
| [`docs-hygiene`](plugins/docs-hygiene) | Skills | Documentation-hygiene toolkit of five skills: `/docs-hygiene:compress` (flavor-trim markdown behind a semantic-diff safety net), `/docs-hygiene:declutter` (classify markdown noise, read-only), `/docs-hygiene:extract-ssot` (deduplicate repeated content into a single source of truth), `/docs-hygiene:encapsulation-audit` (detect citations into skill-private surfaces), and `/docs-hygiene:rename-references` (sweep stale references after renames). |
| [`fable-5-playbook`](plugins/fable-5-playbook) | Skill | Ships Claude Fable 5's operating doctrine as an on-demand knowledge skill — core standing instructions plus twelve trigger-routed chapters (calibration, reasoning moves, planning, debugging, orchestration, verification, recovery, trust boundaries, and more) and an Opus-adaptation chapter for non-Fable models. |
| [`firecrawl`](plugins/firecrawl) | Skill | Web scraping, search, crawling, URL discovery, browser interaction, and local file parsing through the `firecrawl-cli` binary — results written to disk and read back selectively to keep large pages out of context, plus a gated maintainer update flow tracking the upstream CLI and skill source. |
| [`claude-config-audit`](plugins/claude-config-audit) | Skills | Keeps a repo's Claude Code configuration healthy via three report-first audit skills: `settings-audit` (config files vs upstream truth, with live plugin-drift detection), `memory-health` (CLAUDE.md / rules / auto-memory against official-doc criteria), and `automation-deep-dive` (evidence-gated verdicts on automation gaps). |
| [`claude-config-audit`](plugins/claude-config-audit) | Skills | Keeps a repo's Claude Code configuration healthy via four report-first audit skills: `settings-audit` (config files vs upstream truth, with live plugin-drift detection), `memory-health` (CLAUDE.md / rules / auto-memory against official-doc criteria), `automation-deep-dive` (evidence-gated verdicts on automation gaps), and `permission-hygiene` (allow-rule / allowed-tools grants for auto-mode durability and machine portability). |
| [`work-items`](plugins/work-items) | Skill | Manages development work items through a provider-neutral tracker seam (GitHub the bound adapter today): dashboard, taxonomy-labeled creation, a race-safe assignee-plus-lease claim protocol for multi-agent pickup, recurring-schedule checks, TODO scanning, stale-lease auditing, plan decomposition into vertical slices, and structured triage. |
| [`discovery`](plugins/discovery) | Skills | Structured discovery before changes. Ships four skills: `/discovery:explore` and `/discovery:explore-deep` (six-dimension codebase exploration, inline or in a forked subagent) plus `/discovery:research` and `/discovery:research-deep` (three-phase multi-source external research with source tiers, falsification, and recency gates). |
| [`playwright`](plugins/playwright) | Skill | Live E2E browser automation through Microsoft's `@playwright/cli` — named sessions, accessibility-ref snapshots (click/fill by ref), screenshots, console/network capture, mocking, tracing, video, and auth state, with artifacts written to disk so only paths enter context, plus Windows/orchestrator overlays and a gated maintainer update flow tracking the upstream npm skill. |
Expand Down
17 changes: 17 additions & 0 deletions docs/conventions/permission-rule-hygiene/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Permission Rule Hygiene Convention — Changelog

Notable changes to the permission-rule-hygiene convention. The convention states a principle and three
anti-patterns; it is enforced by the `claude-config-audit` plugin's `permission-hygiene` skill (checks
P1/P2/P3), whose detector and criteria version independently of this document.

## 1.0 — 2026-07-14

Initial published convention.

- Principle: the operative allow-rule for a guarded code-execution helper must be a narrow,
machine-independent, bare-command rule the operator adds to user-global settings.
- Anti-pattern 1 — interpreter-wildcard / blanket allow rules dropped in auto mode.
- Anti-pattern 2 — hardcoded absolute machine/user paths (Bash rules match literally, no expansion).
- Anti-pattern 3 — assuming a skill or plugin can self-grant an auto-mode-gated action class.
- Correct pattern: bare command on the Bash tool PATH (pre-plugin PATH shim, post-migration plugin
`bin/`) allowed narrowly by bare name, with an operator-setup boundary note.
131 changes: 131 additions & 0 deletions docs/conventions/permission-rule-hygiene/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Permission Rule Hygiene Convention

A marketplace-wide convention for writing Claude Code permission grants that actually take effect —
specifically, grants for the auto-mode-gated action classes (arbitrary code execution) that a skill,
command, or plugin wants to run without a prompt.

The principle: **the operative allow-rule for a guarded code-execution helper must be a narrow,
machine-independent, bare-command rule that the operator adds to user-global settings — never an
interpreter-wildcard grant, never a hardcoded machine path, and never a self-granted rule a skill or
plugin ships expecting it to work.** The three anti-patterns below each break that in a different way;
the [correct pattern](#the-correct-pattern) fixes all three at once.

Enforced by the [`permission-hygiene`](../../../plugins/claude-config-audit/skills/permission-hygiene)
skill in the `claude-config-audit` plugin, which scans skill/command/agent frontmatter `allowed-tools`
and `settings.json` / `settings.local.json` `permissions.allow` and flags each anti-pattern (checks
P1/P2/P3).

## Why this convention exists

Running this convention's own detector against this marketplace surfaced six pre-existing
interpreter/runner-led frontmatter grants (shapes like `Bash(bash <script>:*)`, `Bash(bash <dir>/*)`,
and `Bash(npx:*)`) across unrelated plugins — none of them the portable bare-name pattern, and the
broad forms among them (a globbed script target, a package runner) are exactly what auto mode drops.
When a grant is dropped the failure is silent: it parses, looks correct, and does nothing the moment
the session enters auto mode, so the action falls through to the classifier and can be denied even when
the operator intended to pre-approve it. A convention plus an enforceable check is the durable fix.

## Anti-pattern 1 — interpreter-wildcard / blanket allow rules (dropped in auto mode)

On entering [auto mode](https://code.claude.com/docs/en/permission-modes#eliminate-prompts-with-auto-mode),
Claude Code **drops broad allow rules that grant arbitrary code execution**. Per the official decision
order:

> On entering auto mode, broad allow rules that grant arbitrary code execution are dropped:
> Blanket `Bash(*)` or `PowerShell(*)`; Wildcarded interpreters like `Bash(python*)`; Package-manager
> run commands; `Agent` allow rules. Narrow rules like `Bash(npm test)` carry over. Dropped rules are
> restored when you leave auto mode.
> — [permission-modes](https://code.claude.com/docs/en/permission-modes#eliminate-prompts-with-auto-mode)
> ("How the classifier evaluates actions")

The [auto-mode configuration reference](https://code.claude.com/docs/en/auto-mode-config#route-all-shell-commands-through-the-classifier)
restates it and adds that `autoMode.classifyAllShell: true` suspends even the narrow shell allow rules:

> Auto mode suspends only the broad rules that grant arbitrary code execution, such as `Bash(*)` or
> wildcarded interpreters.

So a frontmatter grant such as `allowed-tools: ['Bash(python "*helper.py":*)']`, or any
`Bash(python*)` / `Bash(node*)` / `Bash(bash *)` / `Bash(*.py:*)` / `Bash(sh -c*)` / package-manager
runner (`npx`, `uvx`, `pipx run`, `pnpm dlx`, …), silently grants nothing under auto mode. The action
then depends entirely on the classifier. Empirically, a guarded merge helper granted this way was
denied even when invoked bare.

A **bare package-manager wildcard** — `Bash(npm:*)`, `Bash(npm *)`, `Bash(pnpm:*)`, `Bash(yarn:*)` —
is the same anti-pattern: it reads like a scoped grant but permits arbitrary execution (`npm exec`,
`npm run <anything>`, lifecycle scripts), so it is interpreter/runner-led rather than the bare-name
pattern and is flagged. The doc's dropped-category wording enumerates "package-manager run commands";
this bare form is broader than — not narrower than — that category, so it is treated as the same
authoring anti-pattern with the same fix. A **fixed** package-manager subcommand (`Bash(npm test)`,
`Bash(npm run build)`) carries no wildcard, carries over into auto mode, and is not flagged.

`Agent` allow rules (both bare `Agent` and scoped `Agent(...)`) are dropped the same way, and are
flagged too — but unlike a shell helper they have no bare-command-on-PATH analog to re-scope to.
Remove or re-scope the rule, or run the sub-agent action outside auto mode.

## Anti-pattern 2 — hardcoded absolute machine/user paths

Bash permission rules match the command string **literally**. Per
[permissions](https://code.claude.com/docs/en/permissions#wildcard-patterns), a Bash rule is a glob
over the literal command — there is no `~`, `$HOME`, or environment-variable expansion (the `~/` and
`//` home/absolute anchors documented under
[Read and Edit](https://code.claude.com/docs/en/permissions#read-and-edit) are gitignore-style path
anchors for the file tools, not shell-command expansion). A rule like
`Bash(/c/Users/<name>/.agents/skills/merge/x.sh:*)`:

- breaks on any other machine or username, and after the skill migrates into a plugin (the install
path changes), and
- leaks a username into version control.

The one substitution that *is* expanded in `allowed-tools` is `${CLAUDE_PROJECT_DIR}` (Claude Code
v2.1.196+, per [skills](https://code.claude.com/docs/en/skills)) — but that anchors to the consuming
project, not to a portable command, so it still isn't the right tool for a shared code-execution
helper.

## Anti-pattern 3 — assuming a skill or plugin can self-grant

Three official constraints mean the operative allow-rule cannot be shipped by the skill or plugin:

- **Skill `allowed-tools` is skill-scoped and (per anti-pattern 1) ineffective for auto-mode-gated
action classes.** It "grants permission for the listed tools while the skill is active … It does not
restrict which tools are available" —
[skills](https://code.claude.com/docs/en/skills) — but auto mode still drops the broad/interpreter
shapes.
- **A plugin cannot ship permission rules.** A plugin's `settings.json` supports "Only the `agent` and
`subagentStatusLine` keys" —
[plugins-reference](https://code.claude.com/docs/en/plugins-reference) (Settings row). A
`permissions` block placed there is inert.
- **An agent editing its own settings to self-grant is blocked.** `defaultMode: "auto"` is ignored
from project/local settings "so a repository cannot grant itself auto mode" and `.claude/` writes are
a protected path routed to the classifier —
[permission-modes](https://code.claude.com/docs/en/permission-modes#eliminate-prompts-with-auto-mode).

So the operative rule must be added **by the operator** to user-global
`~/.claude/settings.json`.

## The correct pattern

Expose the guarded helper as a **stable bare command on the Bash tool's PATH**, then allow that bare
name narrowly:

1. **Put the helper on PATH under a stable bare name.**
- Pre-plugin: a small PATH shim in a directory already on your PATH (e.g. `~/.local/bin`, if it is
on your PATH) that delegates through `$HOME` to the skill's self-locating wrapper.
- Post-migration: the plugin's `bin/` directory — "Executables added to the Bash tool's `PATH` …
as bare commands in any Bash tool call while the plugin is enabled" —
[plugins-reference](https://code.claude.com/docs/en/plugins-reference) (Executables row).
- Wrappers self-locate their real directory (e.g. `readlink -f`) so they work via direct, shim, or
symlink invocation.
2. **Allow the bare name, narrowly.** `Bash(babysit_merge.sh:*)` — a narrow rule that carries over
into auto mode exactly like `Bash(npm test)`, is machine/username-independent, and is identical
before and after plugin migration.
3. **State the operator-setup boundary.** The skill/plugin documents an "Operator setup" note telling
the operator to add the bare-name rule once to `~/.claude/settings.json`, and never relies on
interpreter-wildcard `allowed-tools` for auto-mode-gated actions.

## Sources

- Auto-mode drop behavior and decision order — [permission-modes](https://code.claude.com/docs/en/permission-modes#eliminate-prompts-with-auto-mode)
- `classifyAllShell`, narrow-rule carryover — [auto-mode-config](https://code.claude.com/docs/en/auto-mode-config#route-all-shell-commands-through-the-classifier)
- Literal matching, wildcard / `:*` semantics, process-wrapper stripping — [permissions](https://code.claude.com/docs/en/permissions#permission-rule-syntax)
- `allowed-tools` scope and `${CLAUDE_PROJECT_DIR}` substitution — [skills](https://code.claude.com/docs/en/skills)
- Plugin `bin/` on PATH and the `agent`/`subagentStatusLine`-only `settings.json` — [plugins-reference](https://code.claude.com/docs/en/plugins-reference)
6 changes: 3 additions & 3 deletions plugins/claude-config-audit/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "claude-config-audit",
"version": "0.2.0",
"description": "Three audit skills for a repo's Claude Code configuration: settings-audit (settings.json / .mcp.json / hooks / plugins / permissions drift), memory-health (CLAUDE.md, rules, and auto-memory against official-doc criteria), and automation-deep-dive (evidence-gated verdicts on automation gaps).",
"version": "0.3.0",
"description": "Four audit skills for a repo's Claude Code configuration: settings-audit (settings.json / .mcp.json / hooks / plugins / permissions drift), memory-health (CLAUDE.md, rules, and auto-memory against official-doc criteria), automation-deep-dive (evidence-gated verdicts on automation gaps), and permission-hygiene (allow-rule / allowed-tools grants for auto-mode durability and portability).",
"author": {
"name": "Melodic Software",
"email": "info@melodicsoftware.com"
},
"license": "MIT",
"keywords": ["settings", "configuration", "memory", "claude-md", "hooks", "plugins", "automation", "audit", "maintenance", "skill"]
"keywords": ["settings", "configuration", "memory", "claude-md", "hooks", "plugins", "permissions", "automation", "audit", "maintenance", "skill"]
}
25 changes: 22 additions & 3 deletions plugins/claude-config-audit/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# claude-config-audit

A Claude Code plugin bundling three audit skills for one cohesive capability: keeping a repo's Claude
A Claude Code plugin bundling four audit skills for one cohesive capability: keeping a repo's Claude
Code configuration healthy. Each skill answers a different question about the same surface:

| Skill | Question it answers |
|---|---|
| `/claude-config-audit:settings-audit` | Are the configuration FILES (`settings.json`, `settings.local.json`, `.mcp.json`, hooks, plugins, permissions) correct against upstream truth? |
| `/claude-config-audit:memory-health` | Is the instruction/memory layer (`CLAUDE.md`, `CLAUDE.local.md`, `.claude/rules/`, auto-memory) healthy against official-doc criteria? |
| `/claude-config-audit:automation-deep-dive` | Is the configured automation SET the right set — are there genuine gaps, judged against the enforcement hierarchy? |
| `/claude-config-audit:permission-hygiene` | Are the permission GRANTS (`allowed-tools`, `permissions.allow`) portable and durable — do they survive auto mode, work across machines, and live where they can take effect? |

All three default to report-only; mutations (`--fix`, the `fix` action, `--implement`) require
explicit opt-in and per-item user approval.
All default to report-only; mutations (`--fix`, the `fix` action, `--implement`) require explicit
opt-in and per-item user approval. `permission-hygiene` is report-only (its correct remediation is
operator-manual).

## What each skill does

Expand Down Expand Up @@ -58,6 +60,23 @@ verdict is REJECT — a clean bill of health is a valid outcome.
/claude-config-audit:automation-deep-dive --implement # implement user-approved items
```

### permission-hygiene

Audits permission GRANTS (not file correctness — that is `settings-audit`) for the failure modes that
make a grant silently do nothing: interpreter-wildcard / blanket rules that Claude Code drops on
entering auto mode, hardcoded absolute machine/user paths (Bash rules match literally, no expansion),
and inert plugin self-grants. A deterministic detector scans skill/command/agent frontmatter
`allowed-tools` and `settings.json` / `settings.local.json` `permissions.allow`, and recommends the
bare-command-on-PATH pattern. The principle and citations live in the marketplace
[permission-rule-hygiene convention](../../docs/conventions/permission-rule-hygiene/README.md).
Report-only.

```shell
/claude-config-audit:permission-hygiene # full grant audit
/claude-config-audit:permission-hygiene frontmatter # allowed-tools only
/claude-config-audit:permission-hygiene settings # permissions.allow only
```

## Consumer conventions

The skills read the consuming repo's own `CLAUDE.md` / `.claude/rules/` for project-specific policy:
Expand Down
Loading
Loading