ops-update
GitHub用于升级本地 claude-ops 插件至最新版本。通过每日检查或技能调用触发检测,经用户确认后执行安装并清理缓存,支持通知或自动更新模式。
Trigger Scenarios
Install
npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-update -g -y
SKILL.md
Frontmatter
{
"name": "ops-update",
"description": "OPS on-demand: This skill should be used when the user asks to \"update ops plugin\", \"upgrade…",
"allowed-tools": [
"Bash",
"Read",
"AskUserQuestion"
],
"argument-hint": "[--dry-run|--force|--to X.Y.Z|--no-prune|--no-patches|--no-rewrite|--no-localsync]"
}
OPS ► UPDATE — one-command local plugin upgrade
Load ops-rules before acting. Public repo (no personal data). Outbound: one draft → one approval → one send. If AskUserQuestion / Workflow are missing, follow Rule 10 in ops-rules (Hermes: numbered options / two-turn Telegram card; delegate_task).
Upgrades the local claude-ops plugin to the newest version published in the
ops-marketplace catalogue, then leaves the box clean: no stale cache dirs, no
dangling version-pinned paths.
Automatic daily check (detect only, never auto-installs)
bin/ops-update-check runs daily from the ops daemon (update-check service)
and answers one question: is a newer version published? It writes the verdict to
~/.claude/state/ops-update/update-available.json and exits 3 when an
update exists, 0 when current.
It never installs anything. Detection and application are deliberately split:
a background job that swapped the plugin out mid-session would break a working
install at the worst possible moment. Applying is always ops-update, run on
the user's word.
When you see that an update is available — because the state file says so, or because the user asks — surface it once and offer to apply it:
"${CLAUDE_PLUGIN_ROOT}/bin/ops-update-check" --json # current verdict, throttled to daily
"${CLAUDE_PLUGIN_ROOT}/bin/ops-update-check" --force # recheck now, ignoring the throttle
Then a single AskUserQuestion: [Update now] [Show what changed] [Not now].
Only on Update now do you run bin/ops-update. Never chain the two, and never
apply an update the user has not just agreed to in that exchange.
Checking on every skill call
The daily cron is the only thing that ever ran the check, so a box without the
daemon (Linux, or launchd never set up) heard about a new version exactly never.
bin/ops-pretool-skill-update closes that: a PreToolUse hook on ^Skill$
re-uses the same throttled check whenever an ops skill is invoked, so the first
skill call of the day says what the daily cron would have said.
Behaviour is set by auto_update in preferences.json (or $OPS_AUTO_UPDATE):
| Mode | What happens on a stale install |
|---|---|
off |
nothing |
notify (default) |
the session is told it is behind, and to run /ops:ops-update |
auto |
also runs bin/ops-update in the background, then asks for a reload |
auto is the one exception to the split above, and it stays opt-in for the
reason stated there: the running session keeps the old version until it reloads,
so the upgrade lands detached, behind a lock, and never prunes the tree the
session is rooted in. Turning it on is the user's decision, not yours — never
switch a box to auto on your own initiative.
Flags: --json (verdict on stdout), --no-fetch (compare against the catalogue
already on disk, no network), --force (ignore the once-a-day throttle),
--quiet (write state, print nothing — how the daemon runs it). Override the
cadence with $OPS_UPDATE_CHECK_INTERVAL in seconds.
The workhorse is ${CLAUDE_PLUGIN_ROOT}/bin/ops-update. It runs a 9-step loop:
- Refresh catalogue —
claude plugin marketplace update ops-marketplace(git-pulls the clone). - Resolve target — newest version from the refreshed
marketplace.json(or--to X.Y.Z). - Update plugin —
claude plugin update ops@ops-marketplace, with a force-reinstall fallback (rmcache +claude plugin install) for the Claude Code bug whereupdatereports "already latest" while the cache stays stale (anthropics/claude-code#61954). - Reapply patches — runs idempotent scripts in
scripts/cache-patches/against the new cache (empty when all fixes are upstream — the desired state). - Prune — deletes every old
cache/ops-marketplace/ops/<ver>/except the new one. - Rewrite — fixes stale
cache/.../ops/<oldver>/paths in live configs/scripts/systemd units only (never logs, memory, or transcripts — those use${CLAUDE_PLUGIN_ROOT}at runtime so they self-resolve). - Migrate — runs
ops-post-update-migrate(idempotent, per-version). It also maintains a stablecache/.../ops/current/directory (rsynced from the new version and repointed ininstalled_plugins.json) so Claude Code GC'ing the old versioned dir mid-session never causes "Plugin directory does not exist" hook errors. - Local sync — if a linked local source checkout of this repo is present under
~/Projects, fast-forwards itsmaintoorigin/mainso a dev clone never silently drifts behind the published release. Acts only on a cleanmain(never clobbers uncommitted WIP, a feature branch, or unpushed commits); a no-op when no checkout exists. Skip with--no-localsync. - Report — old→new, what changed, and that a restart /
/reload-pluginsis needed to load it. - Companions —
bin/ops-updatestep 9 runsscripts/install-companions.shagainstplugin-dependencies.json. Every companion withrequired: trueis co-installed when missing and updated on every ops-update:- desktop-act —
/ops:desktop+ captcha cascade - gsd —
/ops:flowproject mode,/ops:projects,/ops:go - gstack — skills clone for
/ops:flowad-hoc (/spec/review/qa/ship) - superpowers — merge / orchestrate / triage checkpoints
- feature-dev —
/ops:ops-feature-devSkip only with--no-companionsorOPS_SKIP_COMPANIONS=1.
- desktop-act —
# manual companion pass
bash "${CLAUDE_PLUGIN_ROOT}/scripts/install-companions.sh"
bash "${CLAUDE_PLUGIN_ROOT}/scripts/install-companions.sh --status"
# skip from ops-update:
${CLAUDE_PLUGIN_ROOT}/bin/ops-update --no-companions
How to run it
Steps 5–6 are destructive (prune + rewrite), so always dry-run first, show the plan, confirm, then apply (Rule 5).
1. Dry-run and show the plan
${CLAUDE_PLUGIN_ROOT}/bin/ops-update --dry-run
Present the output: current → target version, which cache versions would be
pruned, which files would be rewritten. If the dry-run shows
already on <ver> and nothing to prune/rewrite, tell the user the box is
already current and stop (offer --force only if they suspect a stale cache).
"already on ✓ marketplace catalogue refreshed even when the underlying git pull
silently did nothing, so step 2 resolves a stale target and the whole run
no-ops. ~/.claude/plugins/marketplaces/ops-marketplace is a real checkout of
this repo; a dirty worktree (a local edit, stray .bak files) blocks the
fast-forward. Verified 2026-09-05: it sat 28 commits behind on v3.10.3 while
v3.10.5 was published, and the update reported the box current.
Whenever the target version does not match what you just released, check the
clone before reaching for --force:
cd ~/.claude/plugins/marketplaces/ops-marketplace
git fetch -q origin
git status -sb # ahead/behind AND porcelain lines
grep -o '"version": *"[^"]*"' .claude-plugin/marketplace.json | head -1
Repair: diff each modified file against origin/main first — a local edit that
is byte-identical to upstream is safe to drop, anything else is real work that
must be salvaged before you touch it. Then clean the tree,
git merge --ff-only origin/main, and re-run the dry-run. The target version
should now be the published one.
2. Confirm
Use AskUserQuestion before applying:
Upgrade local claude-ops <CUR> → <NEW>? (prunes N old cache versions, rewrites M files)
[Apply upgrade]
[Force re-materialise cache] ← only if same-version stale-cache is suspected
[Cancel]
3. Apply
${CLAUDE_PLUGIN_ROOT}/bin/ops-update # or: --force
Stream the step-by-step output. On success, surface the final line verbatim:
Restart Claude Code (or run
/reload-plugins) to load v<NEW>.
The running session will NOT see the new version until reload — this is a Claude Code constraint, not a failure.
Flags
| Flag | Effect |
|---|---|
--dry-run |
Report only; change nothing. Always run this first. |
--force |
Force-reinstall even when the CLI claims "already latest" (bug #61954). |
--to X.Y.Z |
Target a specific version instead of the catalogue's newest. |
--no-prune |
Keep old cache versions. |
--no-patches |
Skip the cache-patch reapply step. |
--no-rewrite |
Skip the stale-version-path rewrite step. |
--no-localsync |
Skip fast-forwarding a linked local source checkout's main. |
--no-companions |
Skip required companion co-install/update (desktop-act, gsd, gstack, superpowers, feature-dev). |
Mobile / SSH (Rule 7)
The bin auto-detects a non-TTY and drops colour; its output is already line-per-fact, so relay it as-is — no tables, no banners.
Notes
- Idempotent. Re-running on an already-current box is a near no-op (resolve →
"already on
" → nothing to prune/rewrite/migrate). - Public repo / no secrets (Rule 0): the script reads only
$HOME/.claude/pluginsstate; it writes no personal data. - To publish a new version first, see
${CLAUDE_PLUGIN_ROOT}/bin/ops-release(bumpsplugin.json+marketplace.json+CHANGELOG, opens the release PR, tags).ops-releaseships it;ops-updatepulls it down locally.
Version History
-
057e72d
Current 2026-09-22 01:19
新增 PreToolUse 钩子以在每次技能调用时检查更新,修复旧版未读取状态文件导致无法感知更新的缺陷,并优化自动更新时的缓存清理逻辑以防止脚本中断。
-
7ea9622
2026-09-08 19:38
文档化了 ops-release 超时导致重复 PR 和 ops-update 因脏工作区导致静默失败的两个故障模式及修复方案
-
6f1e3e0
2026-08-27 09:33
优化专家发现上下文加载性能,修复 Bash 引导状态及系统调用读取问题,解决文件竞态条件,修正市场身份名称,并移除自身路由候选项以避免循环引用。
-
5751870
2026-08-19 11:50
新增每日自动检测机制(仅检测不安装),分离检测与应用流程以保障稳定性,增加用户确认交互环节。
- 64bad13 2026-08-12 09:02


