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 plugins/claude-ops/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "claude-ops",
"version": "0.56.13",
"version": "0.56.14",
"description": "Claude Code operations toolkit. Twelve skills: audit-skill-visibility (audit whether each installed skill is actually VISIBLE to the model, and diagnose why most of a fleet never gets used: a skill is invisible when its description is dropped by Claude Code's skill-listing context budget, which sheds descriptions lowest-score-first so an unused skill loses the keywords that would let it be matched, from skills genuinely not wanted, from skills the run cannot observe at all; computes whether the listing overflows from documented settings, and withholds every cold verdict the data cannot support rather than reporting absence of data as absence of use), inventory (read-only enumeration of the complete invocable surface: every built-in CLI command with aliases and hidden/gated status, every bundled skill, and every component of every installed plugin across all marketplaces; reads the shipped binary because upstream publishes no built-in command list, and carries an integrity verdict so a drifted build reports counts as floors rather than silently short totals), audit-install-state (read-only audit of the machine-scope ~/.claude installation directory and ~/.claude.json: full inventory split into an authored surface and rolled-up bulk trees, product-managed retention vs genuinely unmanaged state, filename-scheme resolution before any process-liveness check, and deliberate/mid-experiment detection; reports, never deletes), audit-performance (read-only slowness-diagnostic capture run at the moment the machine or a session feels slow: CLI version, retention-sweep health including the silent unparsable-settings pause, a timed census walk of the install tree as a sweep-cost proxy, active-session and plugin-fleet counts, a process census, and the fan-out layer, which covers a load-labelled no-op spawn baseline, every hook that will fire bucketed per-tool-call versus per-turn with its invocation shape, the configured statusline, subagent concurrency and spawn-depth ceilings against documented defaults, whether running sessions predate the settings file they are judged by, and orphan attribution by parent liveness rather than age, plus on Windows a kernel-object census (Token objects against uptime, paged pool) that names a host-level leak beneath all four suspects; read against a bundled known-performance-issues reference that also records the causes tested and cleared; separates the four documented suspects of accumulated state, version regression, component bloat, and per-spawn fan-out cost, and routes remediation out; reports, never mutates, and never executes a discovered hook or statusline command), audit-native-overlap (map native Claude Code surfaces, namely built-in CLI commands, bundled skills, plugin-backed built-ins, and session-provided skills, against the current repo's plugin skills and agents, so a custom component never silently duplicates what Claude Code itself ships; bare invocation is a read-only overlap report carrying the extraction's integrity floors and a shared-listing-budget exposure section, verdicts are human-gated in a committed store rendered into a generated registry whose every row carries an observable recheck trigger, and only an explicit apply step bakes presence-gated native references into descriptions and Boundary sections), observability (read locally captured telemetry from the OTEL store, the collector, the per-session hook event log and hook-event JSONL, and ccusage, with trend reports, a per-session report of what fired, what was blocked and the event timeline, and store pruning), known-issues (search known Claude product GitHub bugs, check service health, maintain a persistent tracked-issue registry), changelog (ingest Claude Code changelog entries and integrate them into the current repo), plugins (bring a machine's plugin fleet current on demand: marketplace refresh, effective-scope updates including in-repo project/local installs, new-plugin install per policy, scope-divergence detection and explicit convergence), morning-brief (read-only gh-based operator morning view: queue-label counts, merge-ready PRs, parked decisions with their RECOMMENDED lines, and loop-lane telemetry freshness), lanes (start/restart/stop/status loop lanes as named background Claude Code sessions seeded from canonical prompt files, with per-lane model/effort, a repo-pull + marketplace-refresh launch step, and a consume-restarts action, an OS-schedulable reader that relaunches stopped lanes whose telemetry carries a restart_request), and a re-runnable setup action that settles where the known-issues registry, the skill-usage log and the hook log root live, places the root's self-ignoring guard, and detects retired conventions. Plus an opt-in, default-off per-session hook event log (one JSON line per hook event on every event the generated registry marks observable, written to <root>/sessions/<session_id>.jsonl, with SessionEnd retention by session count or age and an optional detached pre-prune command), a family of eight advisory *-audit hooks (API errors, config changes, instruction loads, permission denials, pre-compaction, skill usage, tool failures, and unsurfaced hook failures. The last also warns the user via systemMessage, since a hook that fails to launch enforces nothing and Claude Code surfaces the failure to nobody) that emit the shared hook-telemetry envelope, and a reference sink that routes envelopes under the same root: per session when the envelope carries a session id, else into the shared hook-events.jsonl the observability skill reads.",
"author": {
"name": "Melodic Software",
Expand Down
6 changes: 6 additions & 0 deletions plugins/claude-ops/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
All notable changes to the `claude-ops` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.56.14]

### Changed

- The plugins skill records the `--all` + `install_new: all` mass install. gotchas.md gains a section on why that combination installs every plugin in every known catalog, why the downgrade guard and `audit` both miss it, the `claude --bare` requirement for the revert, and the revert recipe. SKILL.md's marketplace-resolution list now warns that `all` multiplies the install policy across catalogs, and its `install_new` section requires a counted human confirmation before that combination runs. Documentation only; no script, output, or exit code changes.

## [0.56.13]

### Changed
Expand Down
18 changes: 17 additions & 1 deletion plugins/claude-ops/skills/plugins/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ No hardcoded marketplace name anywhere in this skill. Every action resolves its
`installed_plugins.json`'s install records, never a hardcoded name).
- `<marketplace-name>` argument → that marketplace only.
- `all` argument → every marketplace in `known_marketplaces.json`; per-marketplace failures are
reported inline and never abort the sweep (see [context/sync.md](context/sync.md)).
reported inline and never abort the sweep (see [context/sync.md](context/sync.md)). **`all` also
multiplies the `install_new` policy across every catalog**: under a rendered policy of `all` this
target installs every plugin published in every known marketplace. Confirm the resolved install
gap with a human first, per "userConfig: `install_new`" below.

## State inspection

Expand Down Expand Up @@ -270,6 +273,19 @@ schema has no `enum` type. Verified against the published schema), default `"ask
Any explicitly-set value other than these three is invalid; treat it as `ask` and note the invalid
value in the report.

**`all` is scoped to the marketplace target, and the target is what makes it safe or catastrophic.**
Against the default marketplace, the one whose fleet the operator curates, the gap is normally zero
or a handful and `all` is the intended convenience. Against a multi-marketplace target (`all`, or a
named third-party catalog) the same word means "install every plugin published in every catalog this
machine knows about." One observed run installed 2,231 plugins before it was killed; see
[context/gotchas.md](context/gotchas.md)'s "`--all` with `install_new: all` is a mass install of
every catalog". **When the marketplace target is `all` and the rendered policy is `all`, do not
proceed unattended:** resolve the total install gap first (one `audit all`, or `fleet-state.sh
--marketplace <name> --ids missing-user-install` per marketplace, which is the selector
`sync-run.sh` itself projects for Step 4), state the number, and get an explicit human yes. Treat the
configured value as written with the operator's own marketplace in mind, and downgrade to `ask` when
no human is present to receive the count.

**Configured value: `${user_config.install_new}`**. Claude Code text-substitutes a `userConfig`
value into this skill's content before the model sees the rendered skill, but **only when the key is
explicitly set** in user settings (`~/.claude/settings.json`), `--settings`, or managed settings: precedence managed → `--settings` → user. It is **not** "some `pluginConfigs` scope": a project's
Expand Down
64 changes: 64 additions & 0 deletions plugins/claude-ops/skills/plugins/context/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [`marketplace remove` is a bulk uninstall, not a declaration removal, and it deletes this skill's own run journal](#marketplace-remove-is-a-bulk-uninstall-not-a-declaration-removal-and-it-deletes-this-skills-own-run-journal)
- [Internal-schema drift: fail loud, never guess](#internal-schema-drift-fail-loud-never-guess)
- [Captured values on Windows carry `\r`: strip it before embedding in any command or JSON](#captured-values-on-windows-carry-r-strip-it-before-embedding-in-any-command-or-json)
- [`--all` with `install_new: all` is a mass install of every catalog, and nothing warns you](#--all-with-install_new-all-is-a-mass-install-of-every-catalog-and-nothing-warns-you)

Failure modes this skill is specifically built to avoid, and what breaks if the safeguard is
bypassed. Underlying facts are in [scope-semantics.md](scope-semantics.md). This file is the
Expand Down Expand Up @@ -339,3 +340,66 @@ with a parameter expansion and no `tr` process, **and** strip `\r` the same way
captured from any other source before embedding it in a `claude plugin` command or a JSON argument.
A `jq() { command jq "$@" | tr -d '\r'; }` wrapper gives the same guarantee at the price of a second
process per call. Don't rediscover this the hard way in a second script.

## `--all` with `install_new: all` is a mass install of every catalog, and nothing warns you

Observed 2026-09-14 on **Claude Code 2.1.270**, claude-ops 0.56.2. A `sync --all` run under
`install_new: all` installed **2,231** plugins at user scope in about 20 minutes, 1,953 of them
from `claude-community`, before the operator killed it partway through the alphabet. Nothing in the
run asked, and nothing in the report predicted the volume before Step 4 began.

**The two settings are individually reasonable and catastrophic together.** `install_new: all`
means "install every not-yet-installed catalog plugin." Against the default marketplace, the one
whose fleet the operator already curates, that is the intended convenience: the gap is normally
zero or a handful. Against `--all` it quantifies over *every marketplace in
`known_marketplaces.json`*, so the same word now means "install every plugin published in every
catalog this machine knows about," including large third-party ones nobody curated. The policy is
read once and applied per marketplace; it carries no notion that its blast radius just grew by
three orders of magnitude.

**Why the existing safeguards do not catch it.** The downgrade guard governs direction, not volume.
The `ask` branch is the only one that enumerates before acting, and `all` exists precisely to skip
it. `audit` would have predicted it, but `audit` is a separate invocation an operator reaching for
`--all` has no particular reason to run first. Step 4's report names what it installed only
*after* installing it.

**The rule, until the skill enforces a volume gate itself:** treat `--all` combined with a rendered
`install_new` of `all` as requiring an explicit human confirmation that names the number. Resolve
the per-marketplace install gap first (`fleet-state.sh --marketplace <name> --ids
missing-user-install` per marketplace (the selector `sync-run.sh` itself projects for Step 4), or
one `audit all`), present the total, and proceed only on a yes. An agent running this unattended
should downgrade its own effective policy to `ask` rather than assume the configured `all` was
written with the cross-marketplace case in mind: an operator sets that option while thinking about
the marketplace they maintain.

**Reverting is exact but slow.** The run journal records one `Successfully installed plugin: <id>`
line per install, so the revert set is `grep -oE "Successfully installed plugin: [^ ]+"` over
`journal.log`, deduplicated, and it is disjoint from the pre-existing fleet by construction. Verify
that disjointness against the run's own `pre-refresh.*.json` snapshots before uninstalling anything.
`claude plugin uninstall <id> -s user -y` removes both the `installed_plugins.json` record and the
user-scope `enabledPlugins` entry, but it costs ~6s per plugin, so a four-figure revert runs for
hours. There is no bulk uninstall verb; `marketplace remove` is not one (see its own section above,
Comment thread
kyle-sexton marked this conversation as resolved.
it also deletes this skill's run journal, which is the only record of what to revert).

**A plain `claude plugin uninstall` spawns a session that loads every still-enabled plugin**, so a
revert loop over a large set re-executes the very plugin set it is removing, once per iteration,
including their MCP servers. Run every uninstall as `claude --bare plugin uninstall <id> -s user -y`
instead; `--bare` skips hooks, LSP, plugin sync and the MCP boot, at roughly 15s per uninstall
rather than 6s. Until the revert drains, use `--bare` for unrelated shell work too, and do not run
`/reload-plugins`.

*Basis:* the absence of a bulk verb is `claude plugin uninstall --help`, whose complete option list
is `-h/--help`, `--json`, `--keep-data`, `--prune`, `-s/--scope` and nothing that takes more than
one `<plugin>`; the spawn-and-load behaviour and the `--bare` mitigation are direct observation
during the 2026-09-14 revert on this fleet: the host's node/bun process count climbed on every
plain uninstall and stayed flat at 73 across six consecutive `--bare` uninstalls; the ~6s and ~15s
figures are wall-clock from that same run and are machine- and plugin-count-dependent, so treat
them as orders of magnitude rather than constants. Neither the spawn behaviour nor `--bare` is
documented on
[plugins-reference](https://code.claude.com/docs/en/plugins-reference) or
[plugin-marketplaces](https://code.claude.com/docs/en/plugin-marketplaces), which is why this
record exists. *As of* 2026-09-15 on **Claude Code 2.1.272** (win32); the revert itself ran on
2.1.263–2.1.272 with no observed change in the behaviour. ***Recheck trigger:*** a bulk or
glob-accepting form appearing on `claude plugin uninstall --help`; any release note or
`plugins-reference` change touching what a non-interactive `plugin` subcommand loads at startup, or
documenting `--bare`; or an observed uninstall that does not spawn a plugin-loading session.
Loading