---
name: ccc-update
description: "Click-first plugin update walkthrough. Detects installed vs latest version, runs the marketplace-update sequence, reminds you to restart, and verifies the new version + hooks are live."
model: sonnet
effort: low
allowed-tools:
  - Bash
  - AskUserQuestion
argument-hint: ""
---

# $ccc-update — Update CC Commander

**CC Commander** · $ccc-update · Detect → update → restart → verify

The end-user counterpart to `$ccc-doctor` (which also detects staleness, but
as one row in a bigger diagnostic report) — this skill does ONE thing:
get the user onto the latest version, correctly, in the fewest steps.

> Looking for the vendor-submodule updater instead (`vendor/*` git
> submodules under this repo, maintainer-only)? That's `$ccc-upgrade` — a
> different tool with a similarly-spelled name. This skill (`$ccc-update`)
> is for end users updating the installed plugin itself.

## Step 1 — Detect installed vs latest

```bash
CLONE="$HOME/.claude/plugins/marketplaces/commander-hub"
if [ ! -d "$CLONE" ]; then
  CLONE=$(node -e "
    try {
      const d=JSON.parse(require('fs').readFileSync(process.env.HOME+'/.claude/plugins/known_marketplaces.json','utf8'));
      const m=d['commander-hub'];
      if(m&&m.installLocation) process.stdout.write(m.installLocation);
    } catch(e) {}
  " 2>/dev/null || echo "")
fi
[ -z "$CLONE" ] && CLONE="n/a"
[ "$CLONE" != "n/a" ] && [ ! -d "$CLONE" ] && CLONE="n/a"

INSTALLED_JSON="$HOME/.claude/plugins/installed_plugins.json"
INSTALLED_VERSION="n/a"
if [ -f "$INSTALLED_JSON" ]; then
  INSTALLED_VERSION=$(node -e "
    try {
      const d = JSON.parse(require('fs').readFileSync('$INSTALLED_JSON','utf8'));
      const k = Object.keys(d.plugins||{}).find(k => k.startsWith('commander@'));
      process.stdout.write(k && d.plugins[k][0] ? (d.plugins[k][0].version||'n/a') : 'n/a');
    } catch(e) { process.stdout.write('n/a'); }
  " 2>/dev/null || echo "n/a")
fi

REMOTE_VERSION="n/a"
[ "$CLONE" != "n/a" ] && REMOTE_VERSION=$(node "$CLONE/commander/update-check.js" --remote-only 2>/dev/null || echo "n/a")

echo "CLONE=$CLONE"
echo "INSTALLED_VERSION=$INSTALLED_VERSION"
echo "REMOTE_VERSION=$REMOTE_VERSION"
```

Report the result plainly: `Installed: vX.Y.Z · Latest: vA.B.C`.

- If `REMOTE_VERSION` is `n/a`: say you couldn't reach GitHub (offline or
  rate-limited) and stop — nothing else to do until network is back.
- If `INSTALLED_VERSION` equals `REMOTE_VERSION` (or is newer): say
  "You're already on the latest version — nothing to do" and stop.
- Otherwise, continue to Step 2.

## Step 2 — Confirm and run the update

Call `AskUserQuestion`:

```yaml
question: "Update CC Commander vINSTALLED_VERSION -> vREMOTE_VERSION?"
header: "CC Commander Update"
multiSelect: false
options:
  - label: "Update now"
    description: "Runs the marketplace-update sequence, then tells you to restart."
    preview: "claude plugin marketplace update commander-hub && claude plugin update commander"
  - label: "Not now"
    description: "Skip — you can run $ccc-update again anytime."
```

On "Update now", tell the user to run these two commands themselves (this
skill does NOT and cannot invoke `/plugin` commands on your behalf — they
are Claude Code CLI/Desktop built-ins, not something a skill can shell out
to):

```
/plugin marketplace update commander-hub
/plugin update commander
```

(CLI-only equivalent, if you're scripting: `claude plugin marketplace update commander-hub && claude plugin update commander`.)

## Step 3 — Restart reminder

> ⚠️ **A restart is required.** `/plugin update` stages the new version —
> it does not hot-swap the running session. Fully quit Claude Code /
> Cowork Desktop (Cmd+Q, not just closing the window) and reopen, or start
> a new CLI session.

## Step 4 — Verify after restart

Once the user confirms they've restarted, verify the **installed runtime** —
NOT the marketplace clone. A successful `marketplace update` with a
failed/unapplied `plugin update` leaves the clone showing the new version
while the old runtime keeps running; probing the clone would falsely declare
victory.

```bash
# The client's own record of what is INSTALLED (authoritative). The cache
# installPath IS the plugin source directly — no commander/cowork-plugin
# wrapper like the clone has.
RUNTIME_SRC=$(node -e "
  try {
    const j=JSON.parse(require('fs').readFileSync(process.env.HOME+'/.claude/plugins/installed_plugins.json','utf8'));
    const rows=(j.plugins&&(j.plugins['commander@commander-hub']||j.plugins['commander']))||[];
    const r=Array.isArray(rows)?rows[0]:rows;
    if(r&&r.installPath) process.stdout.write(r.installPath);
  } catch(e) {}
" 2>/dev/null || echo "")
{ [ -z "$RUNTIME_SRC" ] || [ ! -d "$RUNTIME_SRC" ]; } && RUNTIME_SRC="n/a"

NEW_VERSION="n/a"
[ "$RUNTIME_SRC" != "n/a" ] && NEW_VERSION=$(node -e "
  try { process.stdout.write(JSON.parse(require('fs').readFileSync('$RUNTIME_SRC/.claude-plugin/plugin.json','utf8')).version||'n/a'); }
  catch(e) { process.stdout.write('n/a'); }
" 2>/dev/null || echo "n/a")
echo "NEW_VERSION=$NEW_VERSION"

# Prove the hook chain in the INSTALLED runtime actually runs.
[ "$RUNTIME_SRC" != "n/a" ] && echo '{}' | node "$RUNTIME_SRC/hooks/session-end.js" | head -c 200
```

- `NEW_VERSION` (the installed runtime) should now equal `REMOTE_VERSION`
  from Step 1. If the CLONE shows the new version but `NEW_VERSION` is still
  old, say exactly that: the marketplace refreshed but the plugin update
  didn't apply (or the app wasn't fully restarted) — re-run
  `claude plugin update commander` and restart again. If it
  doesn't, the update didn't apply — most likely cause: Desktop wasn't
  fully quit (a closed window keeps the old process, and the old process
  keeps the old plugin loaded). Point the user at `install-recovery.mdx`
  or `$ccc-doctor` for the deeper cache-clearing sequence.
- The hook pipe-test should print one JSON line (e.g.
  `{"continue":true,...}`). No output or a crash means the updated clone's
  hooks aren't wired — again, `$ccc-doctor` is the next step.

## Step 5 — Optional: never do this by hand again

If the user updates often, offer the one-time `autoUpdate` toggle (same
consent-gated flow `$ccc-doctor` offers — see that skill for the exact
backup + edit + verify sequence). Point them there rather than duplicating
the mutation logic in two places.

## Anti-patterns — DO NOT

- Never tell the user to `git pull` — there is no working tree in a
  marketplace install.
- Never claim to have run `/plugin update` yourself — skills cannot invoke
  Claude Code CLI built-ins; always hand the exact commands to the user.
- Never skip the restart reminder — `/plugin update` without a restart is
  the #1 cause of "I updated but nothing changed" reports.
- Never claim success from Step 2 alone — verification (Step 4) is what
  actually confirms the update landed.

---

> ⚙️ **Fable contract:** plan before build · verifier ≠ worker · prove before alarm · loops need gates · leave durable state — `rules/fable-method.md`

> (On Codex, present these options as a numbered list and ask the user to reply with a number — AskUserQuestion is Claude-only.)
