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.31.6",
"version": "0.31.7",
"description": "Claude Code operations toolkit. Ten skills: inventory (read-only enumeration of the complete invocable surface \u2014 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 \u2014 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 \u2014 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 a bundled known-performance-issues reference; separates the three documented suspects \u2014 accumulated state, version regression, component bloat \u2014 and routes remediation out; reports, never mutates), observability (read locally captured telemetry \u2014 OTEL store, collector, hook-event JSONL, ccusage \u2014 with trend reports 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 \u2014 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 \u2014 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 \u2014 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 lives. Plus a family of seven advisory *-audit telemetry-emitter hooks (API errors, config changes, instruction loads, permission denials, pre-compaction, skill usage, tool failures) that emit the shared hook-telemetry envelope, and a reference sink that maps envelopes into the hook-events.jsonl the observability skill reads.",
"author": {
"name": "Melodic Software",
Expand Down
28 changes: 28 additions & 0 deletions plugins/claude-ops/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@
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.31.7]

### Fixed

- **`skills/plugins` predicted the wrong settings-write behavior for `converge`.** `converge.md`
Step 5 said `uninstall -s project` "can remove an `enabledPlugins` entry" from committed settings,
which reads as "a clean tree means nothing was written". It always writes: verified on Claude Code
2.1.228 with single calls against a clean tracked `.claude/settings.json`, it empties the map to
`"enabledPlugins": {}` rather than deleting the key, writes the key even into a file that never had
one, and rewrites the file in Claude Code's key order so unrelated sibling keys move. Step 5 now
checks every touched project unconditionally and classifies the diff as inert (empty map plus
reorder — recommend discarding, so a team-shared file carries no churn) or substantive (an entry
actually removed — the user decides). `scope-semantics.md` records install's and uninstall's
behavior as a section beside the update exemption, which was re-verified on the same version and
still holds.

### Added

- **`skills/plugins` records that project scope keys on the working directory, not the repository.**
Verified by uninstalling one id in a repo's main checkout and watching its `git worktree`'s record
for the same id survive. Two checkouts of one repo share a `.git` and a tracked
`.claude/settings.json` yet pin independently, so `converge` must keep them as separate rows with
separate `cd` targets — converging one never clears the other.
- **`sync.md` records one observation on `installed_plugins.json` write timing.** A 63-plugin
user-scope sweep on 2.1.228 had all 21 CLI-reported updates already visible to a post-sweep
re-read. Logged as a single data point that does not retire the `<new>` fallback, since it shows
only that the write landed before the re-read on that run.

## [0.31.6]

### Changed
Expand Down
28 changes: 23 additions & 5 deletions plugins/claude-ops/skills/plugins/context/converge.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ rows a bulk report collapses) — never construct the proposed command as a bare
running the bare form for a row whose `projectPath` isn't the current directory would silently
mutate — or fail against — the wrong repo's settings.

Project scope keys on the **working directory**, not the repository — verified on Claude Code
Comment thread
kyle-sexton marked this conversation as resolved.
Comment thread
kyle-sexton marked this conversation as resolved.
2.1.228 by uninstalling one id in a repo's main checkout and observing its git worktree's record for
the same id survive untouched. Two checkouts of one repo (a `git worktree`, sharing one `.git` and
one tracked `.claude/settings.json`) therefore hold independent `projectPath` records and pin
independently. Never collapse them into one row, and never assume converging one clears the other:
each needs its own `cd`.

Present every plugin's proposed strategy and exact CLI command(s) before running anything — do not
batch-apply. Per Brief Decision 6 (V1): confirm **every** pin individually, even when many plugins
share the same strategy — do not infer consent from one confirm to the next.
Expand All @@ -79,11 +86,22 @@ Step 1 if meaningful time has passed or another mutation already landed.
## Step 5 — Surface the resulting diff

After all confirmed mutations run, `git diff` (or the equivalent status check) any project's
committed `.claude/settings.json` that `-s project` mutations may have touched. Per
[scope-semantics.md](scope-semantics.md), a plain `claude plugin update -s project` does **not**
write committed settings — but `claude plugin uninstall -s project` (this action's actual mechanism)
can remove an `enabledPlugins` entry from it. Show the diff; never commit it. The user reviews and
commits (or discards) it through their own normal git workflow.
committed `.claude/settings.json` that `-s project` mutations touched — every one of them, not only
the ones expected to change. Per [scope-semantics.md](scope-semantics.md), `claude plugin uninstall
-s project` (this action's actual mechanism) **always** writes that file: it removes the id's
`enabledPlugins` entry, leaves `"enabledPlugins": {}` when that empties the map, writes the key even
into a file that never had one, and rewrites the whole file in Claude Code's key order. A clean tree
after an uninstall is the surprising outcome, not a dirty one — never predict "no diff" from the
absence of an `enabledPlugins` key and skip the check on that basis.

Classify each diff before showing it, because the two cases warrant opposite advice:

- **Inert** — only an empty `"enabledPlugins": {}` added and/or sibling keys reordered. No behavior
changes. Say so and recommend discarding it, so a tracked, team-shared file does not carry churn.
- **Substantive** — an actual `enabledPlugins` entry removed. That is a real change to what the
project enables for everyone who checks it out. Show it and leave the decision to the user.

Never commit either. The user reviews and commits (or discards) through their own git workflow.

## Non-interactive execution

Expand Down
26 changes: 23 additions & 3 deletions plugins/claude-ops/skills/plugins/context/scope-semantics.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,29 @@ probe — never from `list`/`details` text.
**Empirically verified** (hash-compared a real repo's `.claude/settings.json` and
`.claude/settings.local.json` before and after): `claude plugin update <id> -s project` updates only
the machine-local `installed_plugins.json` record. `enabledPlugins` carries no version — the
committed settings files are untouched by an update. `sync`'s in-repo update step is safe to run
without a settings-diff review; `converge`'s scope-*consolidation* is the one action that can add or
remove an `enabledPlugins` entry, and only that action surfaces a settings diff.
committed settings files are untouched by an update. Re-verified on Claude Code 2.1.228 under the
hardest available conditions: a tracked `.claude/settings.json` that a sibling `install -s project`
had just rewritten, reverted to clean, then updated — the update left it clean. `sync`'s in-repo
update step is therefore safe to run without a settings-diff review, and `converge` is the only
action this skill runs that surfaces a settings diff.
Comment thread
kyle-sexton marked this conversation as resolved.
Comment thread
kyle-sexton marked this conversation as resolved.

## `plugin install|uninstall -s project` DO write committed settings

**Empirically verified on Claude Code 2.1.228** — one call each, against a clean tracked
`.claude/settings.json`, git-diffed after every step:

- `claude plugin install <id> -s project` adds the id to `enabledPlugins` (value `true`) in the
committed file.
- `claude plugin uninstall <id> -s project` removes that entry but leaves `"enabledPlugins": {}`
behind — it empties the map rather than deleting the key, and it writes the key even into a
committed file that never had one.
- Both rewrite the whole file in Claude Code's own key order, so sibling keys unrelated to plugins
can move. The reorder is a serialization artifact of the write, not a semantic change.

Consequence for `converge`: an `uninstall -s project` against a project whose committed settings
carry no `enabledPlugins` entry still dirties the tracked file, with a diff that changes no
behavior — an empty map plus a key reorder. Expect that diff; it is not evidence an entry was
removed. [converge.md](converge.md) Step 5 classifies it.

## `/reload-plugins` — bare by default, `--force` for the MCP-cache-invalidation case

Expand Down
6 changes: 6 additions & 0 deletions plugins/claude-ops/skills/plugins/context/sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ an id and the post-sweep version is unchanged, report the CLI's reported value,
named none — never report `<old> → <old>`, and never count that id as not-updated. A report line
that says nothing changed for a plugin that did change is worse than one that admits it cannot tell.

One data point, not a licence to drop the fallback: on Claude Code 2.1.228 a 63-plugin user-scope
sweep had all 21 CLI-reported updates already reflected in a post-sweep `fleet-state.sh` re-read, so
source 3 agreed with source 2 on every id. That establishes the write landed before the re-read on
that run — not that it is synchronous per call, and not that it holds on another version. Keep
source 2 primary and keep the divergence handling above.

## Step 1 — Marketplace refresh

For each target marketplace (the resolved default, the named one, or every marketplace when the
Expand Down