Skip to content
Back to skills

Update

ASecurity

Update anti-hall to the latest released version and show the changelog delta. Use when the user says "update anti-hall", "/anti-hall:update", "upgrade the plugin", "is anti-hall up to date", or "check for an anti-hall update". Fast-forward-pulls the marketplace clone, mirrors the new version into the plugin cache, prints what changed, then has the user reload in-session via /reload-plugins (rarely, a harness build may require a restart — the skill says so when relevant).

  • 2 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added October 2, 2026
ai-agentsgoshellbashnodegit

Works with

  • claude code
  • cli
  • mcp

Security analysis

A100/100

Scanned October 3, 2026

npx -y skills add talas9/anti-hall --skill update --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Update?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Update
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/talas9-update/badge)](https://www.skillsdirectory.com/skills/talas9-update)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: update
description: Update anti-hall to the latest released version and show the changelog delta. Use when the user says "update anti-hall", "/anti-hall:update", "upgrade the plugin", "is anti-hall up to date", or "check for an anti-hall update". Fast-forward-pulls the marketplace clone, mirrors the new version into the plugin cache, prints what changed, then has the user reload in-session via /reload-plugins (rarely, a harness build may require a restart — the skill says so when relevant).
---

# Update

> **UPGRADE NOTE — user on 0.107.x or earlier:** tell them to run
> `claude plugin update anti-hall@anti-hall` once, then restart Claude Code. The 0.107.x
> `update.js` has no post-pull re-exec, so it cannot perform 0.108.0's harness
> registration and Claude Code keeps loading the old build until they do. From 0.108.0 on
> the helper registers the new build itself.

Brings anti-hall up to the latest released version **in place** and reports exactly
what changed. The hard part of a plugin self-update is honesty about what actually
takes effect in-session — this skill does not over-promise.

## How the install is laid out (VERIFIED — do not re-derive)

- **Marketplace clone** — `~/.claude/plugins/marketplaces/anti-hall/` — a real git
  checkout (origin = github.com/talas9/anti-hall). This is what we `git pull --ff-only`.
- **Version-pinned cache** — `~/.claude/plugins/cache/anti-hall/anti-hall/<version>/` —
  the manager's per-version copy that `/reload-plugins` resolves against.
- **Active version** — recorded by the harness in
  `~/.claude/plugins/installed_plugins.json` under key `anti-hall@anti-hall`. This file
  is **HARNESS-OWNED**: the helper reads it, it **never** writes it.
- **Version authority** — `plugins/anti-hall/.claude-plugin/plugin.json` in the clone.
  `CHANGELOG.md` (repo root) carries one `## <version>` section per release.
- **DevSwarm ingest daemon's baked script path** — `install-devswarm-ingest.js`'s daemon
  unit bakes the **marketplace clone's own `companion/devswarm-ingest.js`** (this is the
  ONE path this skill `git pull --ff-only`s in place — never a version-pinned cache dir,
  which is a NEW directory per release). That is what makes the daemon survive an update
  without crash-looping; see step 5's ingest-daemon heal below for the one-time repair of
  a daemon installed before this fix shipped.

## What the helper does (`skills/update/scripts/update.js`)

Pure Node ≥ 22 built-ins, macOS + Linux (Windows is not supported), fail-open
(report-don't-break). It makes **no writes outside the marketplace clone and stdout** —
the only filesystem mutation is copying the clone's `plugins/anti-hall/` into a **new**
`cache/.../<newver>/` dir (it never deletes or overwrites another version dir).

**(v0.94.0)** The stdout contract (the JSON status line + human summary) is unchanged, but
`update.js` now also prints a `[update] <stage> start` / `done <ms>ms` line to **stderr**
around each post-update stage (reconcile, fold, migrations, ...) so a slow stage is visible
while it runs instead of the script staying silent until every stage returns. Set
`ANTIHALL_UPDATE_QUIET=1` to suppress these lines (e.g. for a caller that captures stderr for
its own purposes).

1. Resolve paths (`ANTIHALL_MARKETPLACE_DIR` overrides the clone path for tests).
2. Read the INSTALLED version: `installed_plugins.json` → newest cache dir → clone
   `plugin.json` (first that resolves wins).
3. `git -C <clone> pull --ff-only` (via `execFileSync`, no shell). **Never** merges,
   rebases, or force-pulls. A **dirty tree** or a **non-fast-forward** divergence is a
   hard STOP with a clear message (exit 1) — it does not try to "fix" it. Offline / no
   git → reported, exit 0.
4. Read the NEW version from the (now-updated) clone `plugin.json`.
5. If `cache/.../<newver>/` is missing **and** the cache root exists, mirror the clone's
   plugin dir into it so `/reload-plugins` can resolve the new version.
   - **Ingest-daemon heal (auto, gated, fail-open — `healIngestDaemon`):** the helper
     ALSO attempts to heal the DevSwarm ingest daemon's launchd/systemd/cron unit
     in-process (no separate agent step needed for this part). **v0.86.0 — the heal
     now fires when EITHER this run synced new bytes into the cache OR the installed
     unit fails to classify `ok`.** Gating it on a cache sync alone made it
     unreachable in exactly the state it was written for: a daemon goes stale with NO
     version bump (the plugin manager relocates or `.bak`s the version-pinned cache
     dir the unit was baked from), and in that steady state the installed version
     already equals latest, `syncCache` no-ops, and the classifier that would have
     spotted the dangling script path never ran. The extra arm is read-only on a
     no-sync run (a unit enumeration plus a few `statSync`s — no spawn, no writes)
     and deliberately does NOT treat `absent` as "needs heal", so a no-op update can
     never first-install an opt-in daemon for a user who never enabled it.
     Root cause: `install-devswarm-ingest.js`'s daemon unit used to bake an install-time
     script path (`__dirname`) that could go stale across a plugin update — the plugin
     manager relocating the version-pinned cache dir the daemon was baked from, crash-
     looping the daemon. The installer now bakes the **git marketplace clone's own copy**
     of `devswarm-ingest.js` (the exact path this very update step just `git pull
     --ff-only`ed **in place**) so a fresh install never goes stale again — but a daemon
     installed *before* this fix shipped is still pointed at a path that may now be gone,
     and needs a one-time re-bake. `healIngestDaemon` runs ONLY under the same
     DevSwarm-session-only gate `hooks/lib/doctor-repair.js`'s own GATED ingest fix uses
     (`isDevswarmActive(env) && resolveWorktree(cwd) !== null`) and reuses that module's
     already-tested classify/detect helpers rather than re-deriving the logic; it re-runs
     the (freshly-pulled) installer only when the unit is genuinely `wrong-path` or
     `stale-script` — an `ok` or `absent` unit is left untouched. Reported as `ingestHeal:
     {attempted, healed, detail}` on the JSON status line. Gate-closed, nothing-to-heal,
     or an internal error are all reported and NEVER fatal to the update. This is
     independent of — and does not replace — step 7's broader, always-refresh install/
     refresh instruction below (which also covers a FIRST install and the supervisor).
   - **Harness re-registration (auto, fail-open — `harnessRegisterPostUpdate`,
     P0 — proven live):** Claude Code itself loads this plugin from
     `~/.claude/plugins/installed_plugins.json`'s
     `plugins["anti-hall@anti-hall"][].installPath` — a **HARNESS-OWNED** file
     this helper only ever reads. Every step above (pull + cache mirror)
     changes the marketplace clone and the cache dir, but does **nothing** to
     that pointer, so a session — including after a full app/Claude restart —
     keeps loading whatever version `installed_plugins.json` still names until
     the harness re-registers it itself. Whenever `installed_plugins.json`'s
     own recorded version is older than the newly-pulled version, the helper
     runs the harness's own `claude plugin update anti-hall@anti-hall`
     (`execFileSync`, ~20s timeout) to trigger that re-registration. **Never
     interactive**: if the harness reports it needs an `--accept-command
     <sha256>` confirmation (command-source installs) or the command fails for
     any reason, the helper does **not** retry with `--accept-command` — it
     reports the exact command for a human to run. Reported as
     `harnessRegistered: {attempted, ok, detail}` on the JSON status line and
     in the human summary. **On success, tell the user to run `/reload-plugins` to load it —
     field-verified (2026-10-01): after `claude plugin update`, a plain
     `/reload-plugins` (no restart) ran hooks and showed skill base directories
     from the new version dir and the wake-watch Monitor handed off to it.
     (The watcher's own `[wake-watch] handed off to <version>` line only means
     the watcher moved to the newer cached build. When the harness registry
     still names an older version the same line ends with `(cached only: …)` —
     the plugin update is then NOT registered and this session's hooks are
     unchanged.)
     (History: `claude plugin update --help` says "restart required to apply" and
     a 2026-09-24 test saw a reload not suffice; superseded.) Restart Claude Code
     only as the fallback: if a hook or skill path still shows the old version
     after the reload, or to re-run SessionStart-only injections (a reload may
     not re-run SessionStart hooks).**
     `installed_plugins.json` itself is still never written directly by this
     helper.
   - **Reconcile (auto, DevSwarm-session-only, fail-open — `reconcilePostUpdate`,
     v0.58.1):** every update run ALSO drains `node scripts/devswarm.js reconcile`
     in-process — no separate agent step needed for this part. `reconcile` (v0.58.0)
     drains every worktree registered in this project's shared store once, recovering
     messages stranded in a per-worktree native hivecontrol queue whose child never ran
     `inbox pull` itself (e.g. a worktree torn down before it drained). Previously a
     MANUAL-only verb; now auto-run whenever `isDevswarmActive(env)` — `DEVSWARM_REPO_ID`
     set, i.e. an actual DevSwarm session (do NOT trigger on machine-level
     descriptor/registry-file presence alone) — regardless of whether the cache actually
     synced this run (a stranded queue is unrelated to whether the plugin version
     changed — the same reasoning that, in v0.86.0, freed the ingest heal above from
     its own cache-sync gate). Safe to auto-run: idempotent (content-hash
     dedup — a re-run imports 0 new messages), lock-respecting (a worktree a live child
     is already draining is skipped via the per-id O_EXCL pull lock, never raced), and
     loss-free (a short-received batch fails loud rather than silently dropping
     messages — see `docs/KB-devswarm-hivecontrol.md` in the anti-hall source repository §8.8's `reconcile` row). Reported
     as `reconcile: {attempted, count, imported, results, detail}` on the JSON status
     line and as a per-worktree breakdown in the human summary. Gate-closed or an
     internal error are both reported and NEVER fatal to the update. The manual verb
     (`node scripts/devswarm.js reconcile`) stays available for an on-demand sweep
     outside an update.
   - **Promote unclaimed sessions (auto, fail-open — `promoteUnclaimedPostUpdate`,
     v0.90.0):** every update run also sweeps every descriptor for the `unclaimed:`
     forward migration, promoting a row to its real session id wherever an independent
     source (a heartbeat's own recorded `sessionId`) proves one — see
     `docs/KB-devswarm-hivecontrol.md` §40 for the sourcing rules this is a forward
     migration of.
   - **Overall post-pull budget (v0.96.0, D11-C, defect e7307778b614 — `postPullBudgetMs`):**
     a single wall-clock budget (`ANTIHALL_UPDATE_POSTPULL_BUDGET_MS`, default 90000ms;
     `0` = unlimited) now bounds every DevSwarm post-pull stage COMBINED — reconcile, fold,
     fold-all-stores, heal-orphan-partitions, fold-archived-rows, and heal-registry-rows —
     checked BEFORE each stage starts (never mid-stage). A stage that would start past the
     deadline is deferred WHOLE, reported as `deferred: true` on its stage result, and picked
     up on the NEXT `update`/`doctor` call rather than lost: every one of these stages is
     independently idempotent/resumable (fold/heal/fold-archived-rows persist their own
     resume markers; the one-time-per-version stages simply re-attempt next call). As of
     v0.96.1, the periodic supervisor sweep (`companion/devswarm-supervisor.js`) also picks
     up `fold-all-stores`, `heal-orphan-partitions`, `fold-archived-rows` and (v0.102.1)
     `heal-registry-rows` — one deferred
     stage per supervisor pass, within `ANTIHALL_SUPERVISOR_SWEEP_BUDGET_MS` (default
     20000ms) — so a machine that always exhausts the post-pull budget is no longer stuck
     waiting on the next explicit `update`/`doctor` call to make progress on those stages.
6. Extract the `CHANGELOG.md` sections strictly between installed (exclusive) and new
   (inclusive) and print them.
7. Emit a JSON status line + a human summary:
   `{installed, latest, updated, cacheSynced, ingestHeal, reconcile, harnessRegistered,
   action}` where `action` is `run /reload-plugins` | `already up to date` | an
   error/STOP detail, `ingestHeal` is `{attempted, healed, detail}` from step 5's
   auto-heal, `reconcile` is `{attempted, count, imported, results, detail}` from step
   5's reconcile auto-heal, and `harnessRegistered` is `{attempted, ok, detail}` from
   step 5's harness re-registration (all absent on a STOP/offline report — those paths
   never reach cache sync).

Modes:
- `node "${CLAUDE_PLUGIN_ROOT}/skills/update/scripts/update.js" --check` — `git fetch` + compare local vs remote
  `plugin.json` version. **No pull, no writes.** Answers "is anti-hall up to date?".
- `node "${CLAUDE_PLUGIN_ROOT}/skills/update/scripts/update.js"` — the full update above.

## Steps

1. Pick the mode from the user's words:
   - "is anti-hall up to date" / "check for an update" → `--check`
   - "update" / "upgrade anti-hall" → full update
2. The helper is a `node` script (a state change — it pulls, may copy into the
   cache, and runs migrations), so **run it directly in the main session, in the
   foreground, with a generous Bash `timeout`** (e.g. 600000) — never delegate it
   to a subagent: the main session holds the context needed to judge a migration
   failure or partial apply, and migrations/deploys are handled in-session, not
   handed to a cheaper model. command-guard allows exactly this invocation
   (optionally piped to a bounded sink). Run exactly one of:
   ```
   node "$HOME/.claude/plugins/marketplaces/anti-hall/plugins/anti-hall/skills/update/scripts/update.js" --check
   node "$HOME/.claude/plugins/marketplaces/anti-hall/plugins/anti-hall/skills/update/scripts/update.js"
   ```
   Never spawn a subagent for it (model-routing-guard blocks an update spawn;
   `guards.updateInSession`).
   **Always the MARKETPLACE CLONE's own copy, never `${CLAUDE_PLUGIN_ROOT}`**
   (the currently-LOADED, possibly-stale cache dir): a P0 field bug showed
   `${CLAUDE_PLUGIN_ROOT}` pulling a NEWER version, syncing the cache, then
   running the OLD cached version's own hardcoded post-pull stage list — a
   stage first added in the new release never ran until a second update or
   `doctor` call. The marketplace clone is always the newest copy right after
   `git pull --ff-only`, which `update.js` itself does as its first step, so
   this path is self-correcting even before that pull completes. (`update.js`
   also now re-execs the freshly-pulled version's own copy internally as a
   second line of defense — see its own `runPostPullReexec` — but the
   INVOCATION path here should never depend on that.)
   Read its JSON summary and the human summary; if a migration failed or a repair
   was only partly applied, the main session decides the follow-up (re-run, run
   `doctor --repair`, or report) — relay the output, do not paper over it.
3. Present the result to the user: the `installed → latest` versions, whether it
   updated, and the **changelog delta** (the printed `## <version>` sections).
4. If the helper hit a **STOP** (dirty clone / diverged branch) or reported
   offline / no-git, relay that message as-is — do **not** attempt a merge, rebase, or
   force-pull on the user's behalf. Tell them to resolve it in
   `~/.claude/plugins/marketplaces/anti-hall/`.
5. On a successful update, end with the message that matches `action` (the same
   rule `version-alert` and `doctor` state: `/reload-plugins` first in every case; a
   full restart only as the fallback when a hook or skill path still shows the old
   version afterwards, or to re-run SessionStart-only injections):

   - `action: run /reload-plugins` (cache synced, no registry change):

     > Run **/reload-plugins** now to load `<version>` in this session (if a hook
     > or skill path still shows the old version afterwards, restart Claude Code;
     > a restart is also how to re-run SessionStart-only injections, which a reload
     > may not). Statusline + hooks pick up changes automatically; /reload-plugins
     > refreshes the skill list and version label.
   - `action: run /reload-plugins — the harness now registers <version> …` (harness registry updated — see the harness-registration bullet above):
     same message; the registry is already updated, so the reload loads `<version>`.
   - `action: run manually: claude plugin update … — then run /reload-plugins …`:
     relay the command for the human to run, then the reload.
6. After pulling a new plugin version, run
   `node plugins/anti-hall/scripts/migrate-state.js` once per repo (idempotent,
   safe to re-run) to fold any legacy root-level `.anti-hall-progress.md` /
   `.anti-hall-history.md` files into the new dated `.anti-hall/history/`
   structure (copy-only; the originals are never touched). A GSD `.planning/`
   tree is NOT folded by default and never automatically: only the explicit
   `migrate-state.js --planning` copies it into
   `.anti-hall/history/legacy/planning/`, copy-only, and it skips git-tracked
   `.planning/`, linked/child worktrees and submodules. If `doctor` reports
   moved `.planning/` files (damage from the pre-0.108.5 automatic fold),
   restore them with the printed `git -C <worktree> checkout -- .planning`, or
   the opt-in `migrate-state.js --restore-planning --dir <worktree>`. Owner
   decision (2026-07-03): `.anti-hall/` is the intended destination for
   progress/handover state across all projects going forward.
7. Run the capability scan (`node plugins/anti-hall/scripts/capability-scan.js`)
   to find what's missing on **this machine** vs what this build **ships**.
   Read-only — it never installs anything. It reports each opt-in capability
   (companions under `companion/install-*.js`, statusline, pending state
   migrations) as `{name, available, active, how}`. Present a concise
   available-vs-active summary to the user:
   - `state-migrations` at `active: false` is already handled by step 6 above
     (it just ran) — no action needed, don't re-surface it as a gap.
   - **DevSwarm liveness supervisor** — **autonomously install-or-refresh it
     whenever this update is running inside a DevSwarm session, no offer, no
     ask.** Check `devswarm-detect`'s `isDevswarmActive(process.env)` — true
     only when `DEVSWARM_REPO_ID` is set, i.e. the current session really is a
     DevSwarm workspace (do NOT trigger on machine-level descriptor presence
     alone; the session might be running outside DevSwarm). If inside a
     DevSwarm session, run its `how` command
     (`node companion/install-devswarm-supervisor.js`) regardless of the
     capability scan's `active` value — run it **in-session, foreground** (it
     writes a launchd/systemd/cron job; installs are handled by the main session,
     which judges a failure — command-guard allows this exact installer). The installer is idempotent (`launchctl unload && load` on
     macOS / systemd reload on Linux), so this both first-installs when absent
     and refreshes an already-installed supervisor so the next sweep runs this
     build's code. REPORT it plainly ("DevSwarm session detected — installed/
     refreshed the liveness supervisor to `<version>`"). Fail-open: an install/
     refresh failure is reported, never fatal to the update. If NOT inside a
     DevSwarm session, do **not** install — just note it's available. (Safe to
     do unprompted: the supervisor's automatic sweep never kills — killing is
     only the separate on-demand `devswarm-recover` CLI. Defense in depth: the
     installed daemon is inert without work — it only ever acts on descriptors
     under `~/.anti-hall/devswarm/workspaces/`, so it no-ops when DevSwarm isn't
     actually running.)
   - **DevSwarm ingest daemon** — install-or-refresh it under the **same
     DevSwarm-session-only, no-offer, no-ask posture as the supervisor above**,
     in the SAME `isDevswarmActive(process.env)` branch. When inside a DevSwarm
     session, also run its `how` command
     (`node companion/install-devswarm-ingest.js`) regardless of the scan's
     `active` value — run it **in-session, foreground**
     (it writes a launchd/systemd/cron unit; installs are handled by the main
     session, which judges a failure — command-guard allows this exact installer). It is idempotent
     (`launchctl unload && load` on macOS / `systemctl --user daemon-reload` +
     `restart` on Linux), so it first-installs when absent and refreshes an
     already-installed daemon to this build's code. Unlike the supervisor (a
     periodic sweep), the ingest daemon runs **continuously** — the unit uses
     `KeepAlive`/`Restart=always` to re-exec it on exit; that is why it has a
     distinct label (`com.anti-hall.devswarm-ingest`) and log
     (`~/.anti-hall/devswarm-ingest.log`). It is the single native consumer that
     wraps `hivecontrol workspace monitor` and folds messages into the store;
     nothing else auto-starts it, so a DevSwarm session without it silently
     ingests nothing. Safe to install unprompted and idempotently: the daemon
     takes an O_EXCL single-consumer lock, so only ONE instance ever runs even if
     multiple installs race — a redundant install is a no-op. REPORT it plainly
     ("DevSwarm session detected — installed/refreshed the ingest daemon to
     `<version>`"). Fail-open: an install/refresh failure is reported, never
     fatal to the update. If NOT inside a DevSwarm session, do **not** install —
     just note it's available.
   - **Any other opt-in companion** at `active: false` (e.g. mcp-reaper — no
     active-integration signal) — just print its `how` command and let the user
     install it when they want.
   - `active: 'unknown'` — the probe couldn't determine state (fail-open);
     mention it as unverified rather than claiming either state.
   - `active: true` for everything — say so briefly; no gaps to report.

## Why /reload-plugins, and the honest edge

`/reload-plugins` is a built-in, user-typed command that reloads plugins, skills, hooks,
and agents in-session (documented in discover-plugins.md). Hooks are re-executed from
disk per event (so in-place edits take effect on the next event), but the entry point's
`__dirname` is bound at session start to the versioned cache dir, so a NEW plugin version
installed to a new cache dir remains invisible until `/reload-plugins` or a restart.
`/reload-plugins` is what refreshes the **skill list**, **version label**, and **cache-bound paths**.

**Honest edge:** field-verified 2026-10-01, `/reload-plugins` alone loaded a new
version dir (hooks, skill base directories, Monitor hand-off). If after it a hook or
skill path still shows the old version, the fallback is to **restart Claude Code**;
a restart is also how to re-run SessionStart-only injections (a reload may not
re-fire SessionStart hooks — not confirmed in the Claude Code docs). Do not claim more than this — the update on
disk is real either way; only the in-session refresh path varies by harness build.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.