plugin-update · diff
git:20261016.7b6a8d9 to git:20261017.43a1a80
156 added, 117 removed. Audit A to A.
---
name: plugin-update
- description: Checks, installs, updates Claude Code plugins. Triggers - update plugins, check versions, обнови плагины.
+ description: Updates every installed brewcode plugin and mod in one run; shows versions before -> after, what changed and the next steps. Triggers - update plugins, check versions, обнови плагины.
user-invocable: true
disable-model-invocation: true
- argument-hint: "[prompt] [check|update|all]"
- allowed-tools: [Read, Bash, AskUserQuestion, Write, WebFetch]
+ argument-hint: "[prompt] [update|check|all]"
+ allowed-tools: [Read, Bash, AskUserQuestion]
model: sonnet
+ license: MIT
---
# Brewcode Plugin Update
- > Check/install/update brewcode, brewdoc, brewtools, brewui; check/update the
- > brewmod-placeholders and brewmod-plan-effort mods only when installed, never install them. The
- > `/plugin` UI, reload and marketplace-command acceptance are user handoffs, never invented tool actions.
+ **First output:** the PLAN block below, as plain text, before any tool call.
+ > Updates every installed claude-brewcode plugin and mod at its own scope without asking what to update,
+ > offers what is missing in one question, then prints one report: versions before -> after, what changed
+ > for you, numbered next steps. `/reload-plugins` and the `/plugin` UI are user handoffs, never tool actions.
+
## Prompt contract
`$ARGUMENTS` is one free-form RU/EN prompt; the mode token may sit anywhere in it.
1. Mode: an explicit mode token wins outright; else the most distinct whole-word keyword hits
- (table below). No hit or empty -> `interactive`.
- 2. Scope: prose that is not a mode is still input: read it for which plugins/scope it names. Naming
- a mod that is not installed never installs it: report it as not installed.
- 3. Ask: outcome-changing ambiguity between `update` and `all` -> ONE `AskUserQuestion` before any
- work. `check` asks nothing (read-only). `interactive` asks its own per-phase `AskUserQuestion`
- gates — that IS the mode, not an extra clarifying question.
+ (table below). Tie with `check` -> `check`; `update` vs `all` -> the keyword that comes first.
+ No hit or empty -> `update`.
+ 2. Scope: always every installed claude-brewcode plugin and mod, in every scope; the prompt never
+ narrows the update (user ruling: updating is automatic). A missing plugin or mod named in the
+ prompt is still only an offer: the Phase 3 question (`update`) or `all`.
+ 3. Ask: at most ONE `AskUserQuestion` per run, at most 2 questions, only in `update` and only when
+ Phase 2 lists offers; after the PLAN block, before the first change. The update itself is never
+ asked about. `check` asks nothing; `all` takes every offer without asking (the word is consent).
- Print once, before Phase 3 (the first phase that can mutate); `check` prints it immediately before
- its Phase 2 report instead (labels and plan values English, INPUT verbatim):
+ Your reply starts with this block, filled from the prompt alone, then Phase 0 runs. Print it once, in
+ every mode, `check` included (labels and plan values English, INPUT verbatim). A block only in your
+ thinking or only inside a tool call does not count:
```
PLAN — brewtools:plugin-update
INPUT: <arguments verbatim, or "(empty)">
MODE: <resolved> — <explicit | matched keyword: X | default>
- SCOPE: <resolved plugins / flags>
- DO: <2-5 imperative bullets>
+ SCOPE: every installed claude-brewcode plugin and mod, every scope; missing ones <update: one question | all: installed | check: listed>
+ DO: <2-5 imperative bullets, in phase order>
RESULT: <what the user ends up holding>
```
- ## Argument Handling
+ ## Modes
| Mode | EN keywords | RU keywords | Mutates? | Behavior |
- |------|-------------|--------------|----------|----------|
- | `interactive` | *(empty)* | *(empty)* | yes (asks first) | all 6 phases with AskUserQuestion gates |
- | `check` | `check`, `status` | `проверь`, `статус` | no | Phases 0-2 + 6 (status table, reload notice), no prompts |
- | `update` | `update`, `upgrade` | `обнови`, `обновление` | yes | Phases 0-4 + 6, non-interactive "Update all" |
- | `all` | `all`, `everything`, `full` | `всё`, `полностью` | yes | Phases 0-6 non-interactive |
-
- Never read the first word of a sentence as a mode or plugin id.
+ |------|-------------|-------------|----------|----------|
+ | `update` | *(empty)*, `update`, `upgrade` | `обнови`, `обновление` | yes | Phases 0-6: updates every outdated installed row; asks once, only when offers exist |
+ | `check` | `check`, `status` | `проверь`, `статус` | no | Phases 0-2, then the Phase 6 check report; no question, no marketplace refresh |
+ | `all` | `all`, `everything`, `full` | `всё`, `полностью` | yes | `update` + every offer installed without asking + other marketplaces' rows + prune |
## Critical Rules
- - Run every `claude plugin ...` command via the Bash tool and show its full output.
+ - Run every `claude plugin ...` command via the Bash tool, one plugin name per call; show the full
+ output of every update and install.
- Never suggest `--plugin-dir` to end users: it is a dev-only flag.
- - Print the reload notice at the end of every run, no-op runs included.
+ - Scratch: Phase 0 makes ONE private `mktemp -d` dir and prints `SCRATCH <path>`; every later `<dir>`
+ is that literal path (shell variables do not survive between Bash calls). Never pick a dir by glob.
+ Phase 6 removes it after the report.
+ - `scripts/update-flow.mjs` builds the plan and the report from `references/update-flow.md` (data:
+ §3, strings: §5). Print the report verbatim; never re-word it, re-order it or add a table of your own.
+ - Markers in anything you print: 🟢 done, 🔵 info, offer or next step, 🔴 failed, ⚪ unchanged or not
+ installed. Fence marker lines (`✅ … OK`, `❌ … degraded`, `❌ … FAILED`, `SCRATCH <dir>`) are for you
+ only: never copy one into a message.
+ - Mods need Claude Code 2.1.301 or newer: below that, `plan` offers no mod and the report prints
+ its update-Claude-Code-first line. Never install a mod outside `offers`.
+ - The report's Next steps start with `/reload-plugins` on every `update`/`all` run, no-op runs
+ included; never print a separate reload notice.
- AskUserQuestion: option lists only, no free-text fields.
- ## Phase 0 — Discover Installed Plugins
+ ## Phase 0 — Inventory
- **PRIMARY** (CC 2.1.163+) — **EXECUTE** using Bash tool:
+ The PLAN block is already above in your reply, as plain text; not yet -> print it now, before this first tool call.
+
+ **EXECUTE** using Bash tool:
```bash
- unset CLAUDECODE && claude plugin list --json && echo "✅ list OK" || echo "❌ list FAILED"
+ D="$(mktemp -d "${TMPDIR:-/tmp}/plugin-update.XXXXXX")" && echo "SCRATCH $D" && echo "✅ scratch OK" || echo "❌ scratch FAILED"
```
- A valid JSON array, empty included, is the authoritative inventory; use each row's `id`, `version`
- (may be `"unknown"`), `scope`, `enabled`, `projectPath` (`project`/`local` rows; other projects too).
- Ids ending `@inline`, `@skills-dir` or `@synced` are not marketplace installs.
+ > **STOP if ❌** — report the error; nothing has changed.
- **FALLBACK** (CC < 2.1.163, error or invalid JSON) — **EXECUTE** using Bash tool:
+ **PRIMARY** (CC 2.1.163+) — **EXECUTE** using Bash tool, `<dir>` = the SCRATCH path:
```bash
- bash "${CLAUDE_SKILL_DIR}/scripts/discover-plugins.sh" && echo "✅ discover OK" || echo "❌ discover FAILED"
+ unset CLAUDECODE && claude --version > "<dir>/cc.txt"
+ claude plugin list --json > "<dir>/before.json" && echo "✅ list OK" || echo "❌ list FAILED"
```
- Both fail -> inventory unknown (!=missing plugins): report versions/status and the reload notice,
- skip install/update/prune. Partition: `suite = {brewcode, brewdoc, brewtools, brewui}`, `other` =
- the rest. Mods (never installed by this skill) = `{brewmod-placeholders, brewmod-plan-effort}`
- (ids `<mod>@claude-brewcode`): a mod is in play only when Phase 0 lists it; `other` excludes them. Details: [references/discovery.md](references/discovery.md).
-
- ## Phase 1 — Fetch Latest Versions
+ A valid JSON array, empty included, is the authoritative inventory; `plan` reads each row's `id`, `version`
+ (may be `"unknown"`), `scope`, `enabled`, `projectPath` (`project`/`local` rows; other projects too).
+ Ids ending `@inline`, `@skills-dir` or `@synced` are not marketplace installs and never update.
- **EXECUTE** using Bash tool:
+ **FALLBACK** (CC < 2.1.163: ❌ above, or Phase 2 reports `"error":"no-inventory"`) — **EXECUTE** using Bash tool:
```bash
- bash "${CLAUDE_SKILL_DIR}/scripts/fetch-latest-versions.sh" && echo "✅ fetch OK" || echo "❌ fetch FAILED"
+ bash "${CLAUDE_SKILL_DIR}/scripts/discover-plugins.sh" > "<dir>/before.json" && echo "✅ discover OK" || echo "❌ discover FAILED"
```
- The output keys cover the suite and both mods. Merge with Phase 0. Fetch failure -> report it, mark latest unknown, stay read-only; never infer
- missing/outdated.
-
- ## Phase 2 — Status Table
-
- Render `| Plugin | Installed | Latest | Status |`, one row per suite plugin (missing installed
- version `—`); status ✅ current, ⬇️ update available, ❌ missing, ❓ unknown. Add one row per
- installed mod; a mod that is not installed gets no row and never shows ❌. List `other` plugins
- below it with versions.
-
- `check`: PLAN goes before this table; then jump to Phase 6.
+ Both fail -> inventory unknown (!=missing plugins): Phase 2 offers nothing and updates nothing.
+ Details: [references/discovery.md](references/discovery.md).
- ## Phase 2b — Token-Cost Table (Optional)
+ ## Phase 1 — Latest Versions
- Precheck:
+ `update` / `all` — **EXECUTE** using Bash tool, marketplace refresh first:
```bash
- claude plugin details --help >/dev/null 2>&1 && echo "available" || echo "skip"
+ claude plugin marketplace update claude-brewcode && echo "✅ marketplace update OK" || echo "❌ marketplace update FAILED"
```
- `skip` → Phase 3. Else run `claude plugin details <plugin>@claude-brewcode` per installed suite
- plugin or mod; render `| Plugin | Skills | Agents | Hooks | Always-on tokens |` from its
- `Component inventory` counts and `Projected token cost` `Always-on`. Missing field `—`; failure →
- `❓` row. Never blocks.
-
- ## Phase 3 — Install Missing
-
- PLAN block first (SCOPE = missing/outdated plugins). Missing suite plugins: `all` installs them,
- `update` skips install. Mods are never installed here, in any mode, even when named in the prompt.
+ Then read the refreshed clone: versions, auto-update flag, release notes path; no network:
+ ```bash
+ node "${CLAUDE_PLUGIN_ROOT}/skills/plugin-update/scripts/update-flow.mjs" latest > "<dir>/latest.json" && echo "✅ latest OK" || echo "❌ latest FAILED"
+ ```
- **AskUserQuestion** (interactive, missing plugins only):
+ `check`, or either ❌ above — **EXECUTE** using Bash tool (read-only web fetch, replaces `latest.json`):
+ ```bash
+ bash "${CLAUDE_SKILL_DIR}/scripts/fetch-latest-versions.sh" > "<dir>/latest.json" && echo "✅ fetch OK" || echo "❌ fetch FAILED"
+ ```
- Question: "Install missing brewcode plugins?"
- Options: "Install all missing" / "Install selected" / "Skip install" (suite plugins only; mods are never offered)
+ Fetch ❌, or ✅ with every version `unknown` (offline) -> latest unknown: Phase 2 reports
+ `"error":"no-latest"`; never infer missing or outdated plugins from it. The report never calls a row
+ with an unknown latest current: its cell is `⚪ latest unknown` and, with no failed row, the title is
+ `## ⚪ brewcode: latest versions unknown (offline?)` (`check`: `## 🔵 brewcode check: latest versions unknown`).
- "Install selected" asks per plugin, options `["Install", "Skip"]`.
+ ## Phase 2 — Plan
- **EXECUTE** (idempotent):
+ **EXECUTE** using Bash tool, `<mode>` = the resolved mode:
```bash
- claude plugin marketplace add https://github.com/kochetkov-ma/claude-brewcode && echo "✅ marketplace add OK" || echo "⚠️ marketplace add warning (may already exist)"
+ node "${CLAUDE_PLUGIN_ROOT}/skills/plugin-update/scripts/update-flow.mjs" plan "<dir>" --mode "<mode>" && echo "✅ plan OK" || echo "❌ plan degraded"
```
- Then per plugin:
- ```bash
- claude plugin install <plugin>@claude-brewcode && echo "✅ install <plugin> OK" || echo "❌ install <plugin> FAILED"
- ```
+ One JSON line (`references/update-flow.md` §4):
- Reference: [references/install-prompt.md](references/install-prompt.md).
+ | Key | Use |
+ |---|---|
+ | `update[]` | rows Phase 4 updates, in this order (mods first, placeholders first): `id`, `scope`, `projectPath`, `before`, `latest`; `orphan: true` = a `project`/`local` row whose directory is gone |
+ | `offers.mods[]` | missing mods, `name` + `value`; empty below Claude Code `offers.ccMin`; `migration` set = the codewords moved to this mod |
+ | `offers.plugins[]` | missing suite plugins, `name` + `value` |
+ | `scopes[]` | each distinct installed `scope` + `projectPath`, any marketplace, `user` first: the Phase 5 prune entries |
+ | `error` | `no-inventory` -> Phase 0 FALLBACK, then this phase again; still `no-inventory` -> Phase 6. `no-latest` -> `update[]` holds every claude-brewcode row; each update is a no-op when current |
- ## Phase 4 — Update Outdated
+ rc 2 = a placeholder left unsubstituted: fix the command and re-run. `check` -> Phase 6.
- **AskUserQuestion** (interactive only; `update` and `all` auto-pick "Update all"):
+ ## Phase 3 — One Question
- Question: "Update brewcode plugin suite?"
- Options: "Update all" / "Update suite only" / "Update selected" / "Skip updates"
+ The PLAN block is already printed (your first output): never repeat it. Only in `update` with a non-empty
+ `offers.mods` or `offers.plugins` -> ONE `AskUserQuestion`, `multiSelect: true`, one question per non-empty
+ list, in this order:
- "Update selected" asks per outdated plugin, options `["Update", "Skip"]`.
+ | Question | Header | Options |
+ |---|---|---|
+ | `Install the missing mods?` | `Mods` | per `offers.mods[]`, in order: label `<name>`, description `<value>`; `migration` set -> description `<value> - replaces the codewords you used`; then `Skip` |
+ | `Install the missing plugins?` | `Plugins` | up to 3 `offers.plugins[]`: label `<name>`, description `<value>`; more than 3 -> one option `Install all N`, description the names; then `Skip` |
- Update set from that answer only: "Update all" = outdated suite + outdated installed mods + every `other` marketplace row
- (latest unknown; update is a no-op when current); "Update suite only" = outdated suite + outdated installed mods; "Update
- selected" = rows answered `Update`; "Skip updates" = empty → Phase 5. Rows keep their Phase 0 `id`,
- `scope` (`user | project | local | managed`; missing → `user`) and `projectPath`; non-marketplace rows
- are reported, never updated.
+ Install set = the chosen options. `Skip`, alone or with others, or no answer (declined, headless) ->
+ nothing from that question; the report then prints each open offer's install command. `all` -> every
+ offer, no question. No offers -> no question.
- **EXECUTE** marketplace refresh first:
- ```bash
- claude plugin marketplace update claude-brewcode && echo "✅ marketplace update OK" || echo "❌ marketplace update FAILED"
- ```
+ ## Phase 4 — Update, Then Install
- Then one command per update-set row. `user` / `managed` rows:
+ Updates first: one command per Phase 2 `update[]` row, in order; an empty `update[]` runs none. Rows keep
+ their `id`, `scope` (`user | project | local | managed`) and `projectPath`. `user` / `managed` rows:
```bash
claude plugin update "<id>" --scope "<scope>" && echo "✅ update <id> OK" || echo "❌ update <id> FAILED"
```
`project` / `local` rows resolve from the cwd, so each runs from its own `projectPath`, never the
session cwd (the subshell keeps the session cwd):
```bash
(cd "<projectPath>" && claude plugin update "<id>" --scope "<scope>") && echo "✅ update <id> (<scope> <projectPath>) OK" || echo "❌ update <id> (<scope> <projectPath>) FAILED"
```
`projectPath` absent or not a directory: run nothing for that row; report `orphan <scope> install: <id> at <projectPath>` with the removal command `mkdir -p "<projectPath>" && cd "<projectPath>" && claude plugin uninstall "<id>" --scope "<scope>"`.
Always pass the discovered `--scope`: CLIs before 2.1.281 read omission as `user`. On failure: report the exact error and continue. Reference: [references/update-commands.md](references/update-commands.md), [references/update-prompt.md](references/update-prompt.md).
- ## Phase 5 — Auto-Update Toggle (Optional)
+ Then installs: the Phase 3 install set (`all`: every offer), in `offers` order, mods first, one name
+ per call. Phase 1 marketplace update ❌ -> add the marketplace first:
+ ```bash
+ claude plugin marketplace add https://github.com/kochetkov-ma/claude-brewcode && echo "✅ marketplace add OK" || echo "❌ marketplace add FAILED"
+ ```
- Interactive and `all` only. Default OFF for a third-party marketplace like claude-brewcode. Only
- switch: `/plugin` -> Marketplaces -> claude-brewcode -> **Enable auto-update**; no CLI sets it, so
- never patch `settings.json` or `known_marketplaces.json` - instruct the user. Details: [references/autoupdate-research.md](references/autoupdate-research.md).
+ Per name:
+ ```bash
+ claude plugin install <plugin>@claude-brewcode && echo "✅ install <plugin> OK" || echo "❌ install <plugin> FAILED"
+ ```
- **AskUserQuestion** (interactive only):
+ Manual install prompt for a fresh machine: [references/install-prompt.md](references/install-prompt.md).
- Question: "Enable auto-update for claude-brewcode marketplace?"
- Options: "Enable via /plugin UI" / "Skip"
+ ## Phase 5 — Prune, Then the After Snapshot
- ## Phase 5b — Prune Orphaned Plugin Dependencies
+ Prune: `all` only; `update` goes straight to the snapshot. Prune removes only auto-installed
+ dependencies nothing still requires. Run it once per Phase 2 `scopes[]` entry, in order: `user` from the
+ session cwd (`<projectPath>` = `.`), `project` / `local` from the entry's own `projectPath` (the scope
+ resolves from the cwd, as in Phase 4). `projectPath` not a directory -> skip the entry (Phase 4 reports
+ the orphan). Any other scope (`managed`, `session`, ...) -> report it unsupported, run nothing.
- Interactive and `all` only; `check` and `update` skip it (mode table).
+ **EXECUTE** using Bash tool per entry, substituting `<scope>` and `<projectPath>`:
+ ```bash
+ (cd "<projectPath>" && if claude plugin prune --help >/dev/null 2>&1; then claude plugin prune --dry-run --scope "<scope>" || echo "❌ prune --dry-run (<scope> <projectPath>) FAILED"; else echo "skipped: claude plugin prune unavailable"; fi)
+ ```
- Prune removes only auto-installed dependencies nothing still requires. Preview once per distinct
- Phase 0 scope in `user|project|local`; report managed/session/synced as unsupported.
+ Show the listed orphans. Empty list or `Nothing to prune` -> the next entry. Otherwise prune that exact
+ list (typing `all` was the consent):
- **EXECUTE** using Bash tool, substituting `<scope>`:
```bash
- claude plugin prune --help >/dev/null 2>&1 && claude plugin prune --dry-run --scope "<scope>" || echo "skipped: claude plugin prune unavailable"
+ (cd "<projectPath>" && claude plugin prune --scope "<scope>" -y) && echo "✅ prune (<scope> <projectPath>) OK" || echo "❌ prune (<scope> <projectPath>) FAILED"
```
- Show the listed orphans. Empty list or `Nothing to prune` → Phase 6. Otherwise ask ONCE on that
- exact list (AskUserQuestion, options `["Prune listed", "Skip"]`; `all` auto-picks "Prune listed"),
- then:
+ Keep `-y`: without a TTY, bare prune removes nothing yet exits 0 - never count that as pruned. No
+ `prune` in the CLI: skip notice. After the last entry: the snapshot.
+ `update` / `all` — **EXECUTE** using Bash tool (Phase 0 used the FALLBACK -> run that fence into
+ `<dir>/after.json` instead):
```bash
- claude plugin prune --scope "<scope>" -y && echo "✅ prune OK" || echo "❌ prune FAILED"
+ unset CLAUDECODE && claude plugin list --json > "<dir>/after.json" && echo "✅ list OK" || echo "❌ list FAILED"
```
- Keep `-y`: without a TTY, bare prune removes nothing yet exits 0 - never count that as pruned. No
- `prune` in the CLI: skip notice.
+ A ❌ keeps the file: the report then opens with a 🔴 line that the after list could not be read, is
+ titled `## 🔴 brewcode: update result unknown (plugin list unreadable)` and shows the versions from
+ before: After `—`, an outdated row `🔴 not verified, latest Y`.
- ## Phase 6 — Reload Notice & Final Report
+ ## Phase 6 — Report
- Print this notice ([references/reload-notice.md](references/reload-notice.md)):
+ **EXECUTE** using Bash tool, from the project root (the report reads its `.claude/` and the guide INDEX):
+ ```bash
+ (cd "${CLAUDE_PROJECT_DIR}" && node "${CLAUDE_PLUGIN_ROOT}/skills/plugin-update/scripts/update-flow.mjs" report "<dir>") && echo "✅ report OK" || echo "❌ report degraded"
+ ```
- > ⚠️ **Reload plugins to activate updates.**
- > Preferred: run `/reload-plugins` in this session.
- > Fallback: type `exit`, then run `claude` again.
+ Then remove the scratch dir (the report is already in the tool output), `<dir>` = the SCRATCH path:
+ ```bash
+ case "<dir>" in */plugin-update.*) rm -rf -- "<dir>" && echo "✅ scratch removed OK" || echo "❌ scratch removed FAILED" ;; *) echo "❌ scratch removed FAILED: not a SCRATCH path" ;; esac
+ ```
- Then a final summary listing the plugins installed, updated and skipped this run, and every error.
- If a mod is not installed, add one line: mod `<mod>` not installed; it needs Claude Code 2.1.301+ (run `claude update`, then restart Claude Code (type `exit`, then run `claude`)), then `claude plugin install <mod>@claude-brewcode`; never run it.
+ Your final message starts with the report text, character for character: no preamble, no re-worded bullet.
+ Report text, every mode (`check` included) = the report fence output above its `✅ report OK`/`❌ report
+ degraded` line; that line, the scratch-removed line and `SCRATCH <dir>` are never printed, not even last.
+ Print the report text verbatim as the answer: title, table (before -> after), lines
+ for missing mods and plugins (the Claude Code 2.1.301 line, the codewords **Moved** line, install
+ commands), **What changed for you** between the versions, **Next steps** (1 `/reload-plugins`;
+ 2 `/brewcode:setup-status upgrade`, then in each other project listed; 3 `/brewdoc:my-claude refresh`
+ when an installation guide is stale) and the auto-update hint. Below it add only `🔴 <command>: <first
+ error line>` per ❌ of the Phase 1 marketplace update, of a Phase 4 command and of a Phase 5 prune or
+ its preview, and each orphan's Phase 4 report line with its removal command (its table cell is
+ `⚪ orphan install`, never a failure).
+
+ `❌ report degraded` with markdown above it (rc 1: a latest version unknown, or the after list
+ unreadable) is still the complete report: print that markdown verbatim, without the `❌` line, then the
+ 🔴 lines; never replace it with a generic failure message.
+
+ Auto-update stays a user switch (`/plugin` -> Marketplaces -> claude-brewcode): never patch
+ `settings.json` or `known_marketplaces.json`. Details: [references/autoupdate-research.md](references/autoupdate-research.md).