app
hook
Public Made by Adomby adom
Registry-native cross-agent auto-update hook, plus claude-rc, the keeper for Claude Remote Control. Keeps adom/core apps and skills current from wiki.adom.inc on every Claude Code and Codex prompt, and keeps `claude rc` alive so users can open AI threads on their container from the Claude mobile app or claude.ai/code.
← Commit history
Publish 1.1.8
6 files changed
+95−8
README.md+19−5@@ -14,15 +14,29 @@ public registry instead, so it works for any user and any agent. ## How it works -A `UserPromptSubmit` hook runs a throttled, bounded check (`adom-wiki outdated`,-~every 30 min). When an installed package is behind the registry, it applies the-update **in the background** and drops a one-line note into the model's context. It-never blocks your prompt.+A throttled, bounded check (`adom-wiki outdated`, ~every 30 min). When an installed+package is behind the registry, it applies the update **in the background** and+drops a one-line note into the model's context. It never blocks your prompt.++Two triggers are wired, sharing one throttle stamp (so a double fire is a no-op):++| Trigger | Where it matters |+| --- | --- |+| `UserPromptSubmit` (Claude Code / Codex) | interactive containers -- updates as you type |+| cron (`*/30` + `@reboot`) | **headless** lite/service containers -- no prompts, so this is the only trigger they get, and it's where prod releases run |++If the image has no `crontab` (or `crond` isn't running), install says so; have the+app call `~/.adom/hooks/adom-core-update.sh` on its own interval instead (throttled++ idempotent, safe to call often).++Under cron, stdout goes to mail or `/dev/null`, so the durable record is+`~/.adom/last-update.status` (`running`, or the last exit code) and+`~/.adom/last-update.log`. | Layer | Updates | Cadence | | --- | --- | --- | | `adom-wiki` CLI | its own binary | ~2 h, on use |-| `adom/hook` (this) | installed apps + skills | ~30 min, on prompt |+| `adom/hook` (this) | installed apps + skills | ~30 min, on prompt **or** cron | ## Install
SKILL.md+27@@ -20,6 +20,33 @@ This is the registry-native replacement for the old `git fetch` hook: same "stay current automatically" behavior, but sourced from the public registry so it works for any user and any agent, with no private-GitHub dependency. +## Two triggers (interactive + headless)++Installing wires BOTH, and they share one throttle stamp, so a double fire is a+no-op:++- **`UserPromptSubmit`** (Claude Code / Codex) -- fast: an interactive container+ updates the moment you type.+- **cron** (`*/30` + `@reboot`) -- the headless trigger. A lite/service container+ has no prompts, so without this it never updates and packages rot until someone+ SSHes in -- which is exactly where prod releases run. Cron is registered+ unconditionally (not only when Claude is absent), so an interactive box that sits+ idle for a day still gets a backstop.++If the image has no `crontab`, or `crond` is not running, install says so. In that+case have the app call `~/.adom/hooks/adom-core-update.sh` on its own interval --+it is throttled and idempotent, so calling it often is safe.++**Reading the result on a headless box:** stdout is meaningless under cron (it+becomes mail or `/dev/null`). The durable record is:++- `~/.adom/last-update.status` -- `running`, or the last exit code (`0` = fine).+- `~/.adom/last-update.log` -- the last update's output.++A failed update is also reported to the next interactive prompt, and the CLI+version each container reports to the registry answers "is the fleet current?"+fleet-wide (`GET /api/admin/clients?name=adom-wiki-cli`) without SSHing anywhere.+ ## Two layers of auto-update - **CLI layer** -- `adom-wiki` self-updates its own binary on use (throttled ~2h).
install.sh+35@@ -76,6 +76,41 @@ PY wire "${HOME}/.claude/settings.json" 1 wire "${HOME}/.codex/hooks.json" 0 +# ---------------------------------------------------------------------------+# Headless trigger (adom/hook #1). UserPromptSubmit only fires inside Claude Code+# / Codex, so a lite/service container -- arguably where currency matters MOST,+# since that is where prod releases run -- has no trigger at all and rots until+# someone SSHes in.+#+# Register cron ALWAYS, not only when Claude is absent. The script's throttle+# stamp (ADOM_HOOK_INTERVAL, default 30m) makes a second trigger a no-op, so this+# is strictly additive: an interactive box keeps the fast prompt trigger AND gains+# a backstop for when it sits idle for a day. Auto-detecting "no Claude here"+# would leave that idle gap open and adds a branch that can guess wrong.+#+# Note stdout is meaningless under cron (it becomes mail or /dev/null) -- the+# observable surface for a headless box is the status/log file the script writes+# (~/.adom/last-update.status, ~/.adom/last-update.log), plus the CLI version it+# reports to the registry on each update.+CRON_LINE_INTERVAL="*/30 * * * * $HOOK_CMD >/dev/null 2>&1"+CRON_LINE_REBOOT="@reboot $HOOK_CMD >/dev/null 2>&1"+if command -v crontab >/dev/null 2>&1; then+ # Idempotent: strip any prior adom-core-update entries, then re-add both.+ if { crontab -l 2>/dev/null | grep -v "adom-core-update.sh" || true; \+ echo "$CRON_LINE_INTERVAL"; echo "$CRON_LINE_REBOOT"; } | crontab - 2>/dev/null; then+ echo " registered cron for headless updates (*/30 + @reboot)"+ if ! pgrep -x crond >/dev/null 2>&1 && ! pgrep -x cron >/dev/null 2>&1; then+ echo " warning: crond is NOT running, so the schedule will not fire. Start cron in this image," >&2+ echo " or have the app call '$HOOK' on its own loop." >&2+ fi+ else+ echo " warning: could not write the crontab; this container will only update on a Claude/Codex prompt." >&2+ fi+else+ echo " note: no crontab in this image, so there is no headless trigger. Either install cron, or have the"+ echo " app call '$HOOK' on its own interval (it is throttled + idempotent, safe to call often)."+fi+ # Retire a gallia statusline if one is wired (gallia is gone; revert to default). # Targeted: only removed when the statusline command points at gallia. python3 - "${HOME}/.claude/settings.json" <<'PY'
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "hook",- "version": "1.1.7",+ "version": "1.1.8", "type": "app", "org": "adom", "bundled": true,
page.json+1−1@@ -1,6 +1,6 @@ { "slug": "hook",- "version": "1.1.7",+ "version": "1.1.8", "type": "app", "org": "adom", "bundled": true,
uninstall.sh+12−1@@ -32,6 +32,17 @@ PY unwire "${HOME}/.claude/settings.json" unwire "${HOME}/.codex/hooks.json"++# Drop the headless cron trigger too, or uninstall would leave entries pointing at+# a script it just deleted (cron would then log a failure every 30 minutes).+if command -v crontab >/dev/null 2>&1; then+ if crontab -l 2>/dev/null | grep -q "adom-core-update.sh"; then+ { crontab -l 2>/dev/null | grep -v "adom-core-update.sh" || true; } | crontab - 2>/dev/null \+ && echo " removed the adom-core-update cron entries"+ fi+fi+ rm -f "$HOOK" rm -rf "${HOME}/.claude/skills/adom-hook"-echo "Removed adom-hook (unwired from Claude + Codex)"+rm -f "${HOME}/.adom/last-update.status" "${HOME}/.adom/last-update.log" "${HOME}/.adom/last-core-check"+echo "Removed adom-hook (unwired from Claude + Codex, cron entries dropped)"