feat(update): /agent:update skill + heartbeat version-check#12
feat(update): /agent:update skill + heartbeat version-check#12crisandrews merged 2 commits intocrisandrews:mainfrom
Conversation
Companion to crisandrews#9 (which disables Claude Code's in-process auto-updater for service-mode installs). Without an explicit update path, daemonized agents silently fall behind. This PR closes that loop: 1. New `/agent:update` skill (skills/update/SKILL.md, user-invocable): - Detects installed Claude Code (`claude --version`) vs latest on npm (`npm view @anthropic-ai/claude-code version`). - Detects local ClawCode HEAD vs upstream/main HEAD (works against any git remote name; caller's `upstream` if present, falls back gracefully when the plugin isn't a git checkout). - Reports a compact comparison and, when updates are available, prints the safe procedure: explicit `npm install -g`, explicit `git pull`, explicit `systemctl --user restart` — i.e. the same atomic transition PR crisandrews#9 wants users to take. - Detection-only. Does NOT execute the install. The `claude` user typically can't write to `/usr/local/lib/node_modules/`, and even where it can, auto-applying updates from inside the runtime is exactly what we just disabled. 2. Heartbeat template gains an "Update check" bullet (templates/HEARTBEAT.md) that: - Day-gates network calls via `memory/.last-update-check` (npm + GitHub don't need polling every 30 min). - Per-version dedupe via `memory/.notified-versions.json` so each new version is announced exactly once, not every cycle. - Pings the user with one short line containing the safe command — mobile-friendly, no code-block walls. 3. Channel-aware output: skill flow handles WhatsApp / Telegram / CLI formatting differences and uses the appropriate `reply` tool when on a messaging channel. The skill is purely additive — agents that don't trigger it see no change. The HEARTBEAT.md change applies only to newly-created agents (existing agents have their own HEARTBEAT.md, which they can update by hand). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Hey JD, love it, detect-only is the right call. One thing before merge: the ClawCode version check fires on any upstream commit (step 4 does Rest looks solid. Thanks! |
…every upstream commit Per review on crisandrews#12 — the previous comparison tripped CW_UPDATE_AVAILABLE on any commit ahead on upstream/main, including docs/chore/ci. In practice that means a README tweak or a clone-count bump surfaces as "update available" and users learn to ignore the notification. Switch the comparison to reachable-release-tag: CW_UPDATE_AVAILABLE=1 only when CW_LOCAL_TAG and CW_UPSTREAM_TAG are both set, neither is "no-tag", and they differ. The raw commit hashes are still gathered and still shown in the output block for diagnostic purposes — they just don't drive the notify signal. Silent-check dedupe is keyed by CW_UPSTREAM_TAG, so each release is announced exactly once; pure upstream chore activity produces no pings at all.
|
Good catch. Switched Pushed as 17e0423. Much better this way. Thanks! |
…every upstream commit Per review on crisandrews#12, the previous comparison tripped CW_UPDATE_AVAILABLE on any commit ahead on upstream/main, including docs/chore/ci. In practice that means a README tweak or a clone-count bump surfaces as "update available" and users learn to ignore the notification. Switch the comparison to reachable-release-tag: CW_UPDATE_AVAILABLE=1 only when CW_LOCAL_TAG and CW_UPSTREAM_TAG are both set, neither is "no-tag", and they differ. The raw commit hashes are still gathered and still shown in the output block for diagnostic purposes. They just don't drive the notify signal. Silent-check dedupe is keyed by CW_UPSTREAM_TAG, so each release is announced exactly once; pure upstream chore activity produces no pings at all.
1fe0906 to
17e0423
Compare
Follow-up to crisandrews#16 with a clarified rationale for keeping the env var. The PTY wrap from crisandrews#9 fixes the SessionEnd-hook failure that turns graceful exit into code 1 and causes restart churn. DISABLE_AUTOUPDATER=1 addresses a different problem: Claude Code's in-process auto-updater regenerates files it manages mid-run, including the resume-on-restart wrapper script generated by crisandrews#7. A long-running daemon rewriting its own ExecStart target while live is a file-integrity issue, separate from the crash loop, and the PTY wrap does nothing for it. On the "don't modify Claude Code internals" principle from crisandrews#16: the principle stands, but DISABLE_AUTOUPDATER is a documented env var Claude Code exposes for this use case. Setting it is a supported interface, not a monkey-patch. The restored comment names the env var and the specific file-regeneration scenario inline so future readers see the intent. Also relevant to crisandrews#12 (/agent:update skill): the skill's explicit-manual- update flow assumes in-process auto-update is off in service mode. With auto-update running again, the skill competes with an updater that may silently rewrite service files behind the operator.
Summary of changes in this release (full detail in CHANGELOG.md): Added - Resume-on-restart wrapper for service mode (#7) - Service hardening defaults: HOME/TERM env, StartLimitBurst guard, persistent log path (#8) - /agent:update skill + heartbeat version-check with day-gate and per-version dedupe (#12) Fixed - WORKSPACE resolution so memory_search hits user's project dir, not plugin dir (#6, closes #5) - Linux systemd crash loop after Claude Code auto-updates mid-run — PTY wrap in ExecStart + DISABLE_AUTOUPDATER=1 for file-integrity (#9, #17/#18) - macOS launchd PTY wrap parity (#16) - Cross-user /agent:import discovery + post-import path sanity check (#10) Performance - reconcile-crons.sh fast-path on steady-state sessions (#11) Thanks to @JD2005L for the whole batch.
Summary
Companion to #9 — which disables Claude Code's in-process auto-updater for service-mode installs. Without an explicit update path, daemonized agents silently fall behind. This PR closes that loop with a detect-and-notify flow that doesn't introduce the very class of mid-run mutation #9 set out to prevent.
Two pieces:
1. New
/agent:updateskill (skills/update/SKILL.md)User-invocable. Triggers on
/agent:update,/update, "check for updates", "are there updates", "buscar actualizaciones".What it does:
claude --version) vs latest on npm (npm view @anthropic-ai/claude-code version)upstream/main(when the plugin is a git checkout)npm install -g, explicitgit pull, explicitsystemctl --user restart— the same atomic transition fix(service): wrap systemd ExecStart in PTY, disable auto-updater #9 wants users to takeWhat it deliberately doesn't do:
claudeuser typically can't write to/usr/local/lib/node_modules/(root-owned), and even where it can, auto-applying updates from inside the runtime is exactly what fix(service): wrap systemd ExecStart in PTY, disable auto-updater #9 disabled. The skill detects and reports; the operator runs the printed commands themselves.2. Heartbeat template gains an "Update check" bullet (
templates/HEARTBEAT.md)One line added. Heartbeat fires every 30 min, but the actual network calls are gated:
memory/.last-update-checkmtime — npm and GitHub don't need polling every 30 min, once per UTC day is plentymemory/.notified-versions.json— each new version is announced exactly once, not every cycleWhen a new version is detected, the heartbeat surfaces a short one-line ping with the safe command. Mobile-friendly, no code-block walls.
Why this pairs with #9
PR #9 turns off the unsafe automatic path. This PR provides the safe manual path and makes sure users don't forget about it. They make sense together; either one without the other is incomplete:
DISABLE_AUTOUPDATERknob isn't setI'd recommend landing them together. If they have to land separately, this one should land after #9 so the notification flow's first message isn't "you're 3 versions behind because we just turned off auto-update."
Permission constraints (worth flagging)
The skill is honest about what it can and can't do in different environments:
The "operator runs the command" pattern matches how a daemon should be operated — visible, explicit, audit-trail-able.
What's not changed
lib/— pure skill + template additionHEARTBEAT.mdfiles are untouched (the template change applies to newly-created agents). Existing users who want the check can copy the bullet into their ownHEARTBEAT.md.Test plan
/agent:updatefrom CLI: prints comparison block; prints update commands when versions differ; clean output when current/agent:updatevia Telegram: short response, uses*bold*not**bold**, no walls of bashupstream: ClawCode side reports unknown, Claude Code side still worksmemory/.last-update-checkmtime).notified-versions.json; subsequent heartbeats see X already-announced and stay quiet; new version Y triggers a new ping🤖 Generated with Claude Code