← Commit history
README.md+49−1
@@ -1 +1,49 @@-# hook+# adom/hook++**Registry-native, cross-agent auto-update.** Keeps your `adom/core` apps and+skills current from [wiki.adom.inc](https://wiki.adom.inc) automatically, on every+Claude Code and Codex prompt. No git, no private repository.++![adom-hook updating core in the background on a prompt](docs/shot.png)++## Why++The old update path fetched a private GitHub repo on every prompt. That works for+insiders but cannot be shared, and it only runs under Claude. This hook checks the+public registry instead, so it works for any user and any agent.++## How it works++A `UserPromptSubmit` hook runs a throttled, bounded check (`adompkg 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.++| Layer | Updates | Cadence |+| --- | --- | --- |+| `adompkg` CLI | its own binary | ~2 h, on use |+| `adom/hook` (this) | installed apps + skills | ~30 min, on prompt |++## Install++```bash+adompkg install adom/hook+```++`adom/core` already depends on it, so a normal bootstrap wires it for you.++## Controls++| Env var | Effect |+| --- | --- |+| `ADOM_HOOK_INTERVAL=<seconds>` | check cadence (default `1800`) |+| `ADOM_HOOK_FORCE=1` | force a check on the next prompt |+| `ADOM_HOOK_DISABLE=1` | disable the hook |++## Uninstall++```bash+adompkg uninstall adom/hook+```++Unwires the hook from both `~/.claude/settings.json` and `~/.codex/hooks.json`.
SKILL.md+46−2
@@ -1,4 +1,48 @@ ----name: hook-description: hook+name: adom-hook+description: Registry-native cross-agent auto-update hook; keeps adom/core apps and skills current from wiki.adom.inc on every Claude Code and Codex prompt. ---++# adom-hook++The registry-native, cross-agent auto-update hook. It keeps your `adom/core` apps+and skills current from the wiki registry (wiki.adom.inc) without git or any+private repository.++## What it does++On every Claude Code and Codex prompt (`UserPromptSubmit`), a throttled check runs+`adompkg outdated`. When something installed is behind the registry, it applies the+update in the background (`adompkg update` + `adompkg install adom/core`) and prints+a one-line note into the model's context. It never blocks the prompt.++This is the registry-native replacement for the old gallia `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 layers of auto-update++- **CLI layer** -- `adompkg` self-updates its own binary on use (throttled ~2h).+- **Content layer (this hook)** -- keeps the installed packages (`adom/core`'s apps+  and skills) current, throttled ~30 min.++## Controls++- `ADOM_HOOK_INTERVAL=<seconds>` -- check cadence (default 1800).+- `ADOM_HOOK_FORCE=1` -- force a check on the next prompt (testing).+- `ADOM_HOOK_DISABLE=1` -- disable the hook entirely.++## Install++```+adompkg install adom/hook+```++It is also a dependency of `adom/core`, so a normal bootstrap installs it for you.+`adompkg uninstall adom/hook` unwires it from both agents.++## Note on Codex++The Codex wiring (`~/.codex/hooks.json`) targets Codex's `UserPromptSubmit` event+and its additionalContext channel. Verify against a live Codex install before+relying on it there; the Claude Code path is the verified one.
check-updates.shadded+50
@@ -0,0 +1,50 @@+#!/usr/bin/env bash+# adom auto-update hook -- registry-native and cross-agent.+#+# Keeps the adom/core apps and skills current from wiki.adom.inc. No git, no+# private repo. Runs under BOTH Claude Code and Codex `UserPromptSubmit`; its+# stdout becomes the model's additionalContext. Throttled and backgrounded so it+# never blocks a prompt or interrupts the agent.+#+# Tunables (env):+#   ADOM_HOOK_INTERVAL   seconds between checks (default 1800 = 30 min)+#   ADOM_HOOK_FORCE      any non-empty value forces a check this run+#   ADOM_HOOK_DISABLE    any non-empty value disables the hook entirely++[ -n "${ADOM_HOOK_DISABLE:-}" ] && exit 0++# A UserPromptSubmit hook can fire from a non-login shell whose PATH lacks+# ~/.local/bin (where the bootstrap installs adompkg). Without this the check+# silently no-ops ("command -v adompkg" fails). Put the CLI dir on PATH first.+export PATH="$HOME/.local/bin:$PATH"++STAMP="${HOME}/.adom/last-core-check"+INTERVAL="${ADOM_HOOK_INTERVAL:-1800}"+mkdir -p "$(dirname "$STAMP")" 2>/dev/null++now=$(date +%s 2>/dev/null || echo 0)+last=0+[ -f "$STAMP" ] && last=$(stat -c %Y "$STAMP" 2>/dev/null || echo 0)+if [ -z "${ADOM_HOOK_FORCE:-}" ] && [ "$last" -gt 0 ] && [ $(( now - last )) -lt "$INTERVAL" ]; then+  exit 0+fi+# Stamp the attempt up front: a slow or failed check must not re-fire every prompt.+touch "$STAMP" 2>/dev/null++command -v adompkg >/dev/null 2>&1 || exit 0++# Cheap, bounded staleness probe. `adompkg outdated --quiet` exits non-zero when+# an installed package is behind the registry.+if timeout 12 adompkg outdated --quiet >/dev/null 2>&1; then+  exit 0  # everything current+fi++# Behind: apply in the background (detached) so the prompt is never blocked.+# `adompkg update` bumps in-range versions; `install adom/core` re-resolves and+# pulls any newly added core deps. adompkg is resilient (skips needs-sudo+# packages with a note, idempotent on re-run).+( adompkg update >/dev/null 2>&1; adompkg install adom/core >/dev/null 2>&1 ) </dev/null >/dev/null 2>&1 &+disown 2>/dev/null++echo "adom: core skills/apps were behind wiki.adom.inc and are updating in the background now. They will be current within a moment. Continue with the user's request."+exit 0
docs/shot.pngadded
⋯ 1 unchanged line ⋯
docs/shot.svgadded+28
@@ -0,0 +1,28 @@+<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="600" viewBox="0 0 1000 600">+  <rect width="1000" height="600" rx="14" fill="#0d1117"/>+  <rect x="0" y="0" width="1000" height="44" rx="14" fill="#161b22"/>+  <rect x="0" y="30" width="1000" height="14" fill="#161b22"/>+  <circle cx="26" cy="22" r="6" fill="#ff5f56"/>+  <circle cx="48" cy="22" r="6" fill="#ffbd2e"/>+  <circle cx="70" cy="22" r="6" fill="#27c93f"/>+  <text x="500" y="27" text-anchor="middle" font-family="monospace" font-size="14" fill="#8b949e">adom-hook  -  UserPromptSubmit</text>+  <g font-family="monospace" font-size="19">+    <text x="30" y="92" fill="#8b949e"># every prompt (throttled ~30m): is core behind the registry?</text>+    <text x="30" y="128" fill="#58a6ff">$</text>+    <text x="56" y="128" fill="#c9d1d9">adompkg outdated --quiet</text>+    <text x="56" y="160" fill="#e3b341">  adom/screenshot-paste   2.1.0  ->  2.3.0</text>+    <text x="56" y="188" fill="#e3b341">  adom/wiki               4.0.1  ->  4.2.0</text>+    <text x="30" y="236" fill="#7ee787">adom: core skills/apps were behind wiki.adom.inc and are</text>+    <text x="30" y="262" fill="#7ee787">updating in the background now. They will be current shortly.</text>+    <text x="30" y="310" fill="#8b949e"># background, detached - the prompt is never blocked</text>+    <text x="30" y="342" fill="#58a6ff">$</text>+    <text x="56" y="342" fill="#c9d1d9">adompkg update &amp;&amp; adompkg install adom/core</text>+    <text x="56" y="374" fill="#c9d1d9">  updated adom/screenshot-paste -> 2.3.0</text>+    <text x="56" y="402" fill="#c9d1d9">  updated adom/wiki -> 4.2.0</text>+    <text x="56" y="430" fill="#7ee787">  adom/core is current (16 packages)</text>+    <text x="30" y="486" fill="#8b949e"># no git. no private repo. Claude + Codex. zero clicks.</text>+  </g>+  <rect x="0" y="540" width="1000" height="60" fill="#161b22"/>+  <text x="30" y="577" font-family="monospace" font-size="17" fill="#58a6ff">adom/hook</text>+  <text x="150" y="577" font-family="monospace" font-size="17" fill="#8b949e">registry-native cross-agent auto-update</text>+</svg>
install.sh+74−5
@@ -1,6 +1,75 @@-#!/bin/bash-set -e+#!/usr/bin/env bash+# Wire the adom auto-update hook into Claude Code and Codex, idempotently.+set -euo pipefail PKG_DIR="$(cd "$(dirname "$0")" && pwd)"-mkdir -p "$HOME/.claude/skills/hook"-[ -f "$PKG_DIR/SKILL.md" ] && cp "$PKG_DIR/SKILL.md" "$HOME/.claude/skills/hook/SKILL.md"-echo "Installed hook"++HOOK_DIR="${HOME}/.adom/hooks"+HOOK="${HOOK_DIR}/adom-core-update.sh"+mkdir -p "$HOOK_DIR"+install -m 0755 "$PKG_DIR/check-updates.sh" "$HOOK"+HOOK_CMD="bash $HOOK"++if ! command -v python3 >/dev/null 2>&1; then+  echo "  warning: python3 not found; cannot wire agent configs. Hook script staged at $HOOK." >&2+  exit 0+fi++# --- Claude Code: ~/.claude/settings.json -> hooks.UserPromptSubmit ---+CLAUDE="${HOME}/.claude/settings.json"+mkdir -p "$(dirname "$CLAUDE")"+python3 - "$CLAUDE" "$HOOK_CMD" <<'PY'+import json, os, sys+path, cmd = sys.argv[1], sys.argv[2]+try:+    with open(path) as f: d = json.load(f)+    if not isinstance(d, dict): d = {}+except Exception:+    d = {}+ups = d.setdefault("hooks", {}).setdefault("UserPromptSubmit", [])+present = any(+    h.get("command") == cmd+    for g in ups if isinstance(g, dict)+    for h in g.get("hooks", []) if isinstance(h, dict)+)+if present:+    print("  adom-core-update already wired in ~/.claude/settings.json")+else:+    ups.append({"hooks": [{"type": "command", "command": cmd, "timeout": 15}]})+    with open(path, "w") as f: json.dump(d, f, indent=2)+    print("  wired adom-core-update into ~/.claude/settings.json")+PY++# --- Codex: ~/.codex/hooks.json -> hooks.UserPromptSubmit ---+# Codex mirrors Claude's hook events and additionalContext channel. This shape is+# best-effort; verify against a live Codex install before relying on it there.+CODEX="${HOME}/.codex/hooks.json"+mkdir -p "$(dirname "$CODEX")"+python3 - "$CODEX" "$HOOK_CMD" <<'PY'+import json, os, sys+path, cmd = sys.argv[1], sys.argv[2]+try:+    with open(path) as f: d = json.load(f)+    if not isinstance(d, dict): d = {}+except Exception:+    d = {}+ups = d.setdefault("hooks", {}).setdefault("UserPromptSubmit", [])+present = any(+    h.get("command") == cmd+    for g in ups if isinstance(g, dict)+    for h in g.get("hooks", []) if isinstance(h, dict)+)+if present:+    print("  adom-core-update already wired in ~/.codex/hooks.json")+else:+    ups.append({"hooks": [{"type": "command", "command": cmd}]})+    with open(path, "w") as f: json.dump(d, f, indent=2)+    print("  wired adom-core-update into ~/.codex/hooks.json")+PY++# Stage the skill doc for discovery.+if [ -f "$PKG_DIR/SKILL.md" ]; then+  mkdir -p "$HOME/.claude/skills/adom-hook"+  cp "$PKG_DIR/SKILL.md" "$HOME/.claude/skills/adom-hook/SKILL.md"+fi++echo "Installed adom-hook (auto-update wired for Claude + Codex; checks every ${ADOM_HOOK_INTERVAL:-1800}s)"
package.json+29−1
@@ -1 +1,29 @@-{"slug": "hook", "version": "1.0.1", "type": "app", "org": "adom", "org_id": "681d18d2a224243b0d62c9b2", "description": "Periodic update check hook. Warns when newer versions of installed packages are available.", "dependencies": {}, "scripts": {"install": "./install.sh", "uninstall": "./uninstall.sh"}}\ No newline at end of file+{+  "slug": "hook",+  "version": "1.1.2",+  "type": "app",+  "org": "adom",+  "bundled": true,+  "description": "Registry-native cross-agent auto-update hook. Keeps adom/core apps and skills current from wiki.adom.inc on every Claude Code and Codex prompt, in the background.",+  "dependencies": {},+  "scripts": {+    "install": "./install.sh",+    "uninstall": "./uninstall.sh"+  },+  "tags": ["hook", "auto-update", "bootstrap", "claude-code", "codex", "registry"],+  "discovery_triggers": [+    "auto update skills and apps",+    "keep core packages current",+    "core packages are behind",+    "adompkg update hook",+    "claude code update hook",+    "codex update hook",+    "registry auto update"+  ],+  "hero": {+    "headline": "Always current. Zero clicks.",+    "subhead": "Registry-native auto-update for Claude + Codex",+    "screenshot": "docs/shot.png",+    "accent": "#7ee787"+  }+}
page.json+32−8
@@ -1,18 +1,42 @@ {   "slug": "hook",-  "version": "1.0.1",+  "version": "1.1.2",   "type": "app",   "org": "adom",-  "org_id": "681d18d2a224243b0d62c9b2",-  "description": "Periodic update check hook. Warns when newer versions of installed packages are available.",+  "bundled": true,+  "description": "Registry-native cross-agent auto-update hook. Keeps adom/core apps and skills current from wiki.adom.inc on every Claude Code and Codex prompt, in the background.",   "dependencies": {},   "scripts": {     "install": "./install.sh",     "uninstall": "./uninstall.sh"   },-  "integrity": "sha256-7b2484a6096f147bb768e964d77c9acb12f25d8824a54cfc6f635da6a1b6344c",-  "org_name": "adom",-  "signature": "AS0NRxP+HUE/7TM+BxUbv/hdxg6hKXu6vpNtfxKqR72fqxHQgySks99V4aXPUWjpzvY1mD9kYVdldFlu9wTYDA==",-  "signing_key_id": "fa82062fd5524493",-  "_type": "app"+  "tags": [+    "hook",+    "auto-update",+    "bootstrap",+    "claude-code",+    "codex",+    "registry"+  ],+  "discovery_triggers": [+    "auto update skills and apps",+    "keep core packages current",+    "core packages are behind",+    "adompkg update hook",+    "claude code update hook",+    "codex update hook",+    "registry auto update"+  ],+  "hero": {+    "type": "billboard",+    "headline": "Always current. Zero clicks.",+    "subhead": "Registry-native auto-update for Claude + Codex",+    "screenshot": "docs/shot.png",+    "accent": "#7ee787"+  },+  "visibility": "public",+  "author": {+    "name": "Kyle Bergstedt",+    "email": "[email protected]"+  } }\ No newline at end of file
uninstall.sh+37−4
@@ -1,4 +1,37 @@-#!/bin/bash-set -e-rm -rf "$HOME/.claude/skills/hook"-echo "Uninstalled hook"+#!/usr/bin/env bash+# Unwire the adom auto-update hook from Claude Code and Codex.+set -euo pipefail+HOOK="${HOME}/.adom/hooks/adom-core-update.sh"+HOOK_CMD="bash $HOOK"++unwire() {+  local path="$1"+  [ -f "$path" ] || return 0+  command -v python3 >/dev/null 2>&1 || return 0+  python3 - "$path" "$HOOK_CMD" <<'PY'+import json, sys+path, cmd = sys.argv[1], sys.argv[2]+try:+    with open(path) as f: d = json.load(f)+except Exception:+    sys.exit(0)+ups = d.get("hooks", {}).get("UserPromptSubmit")+if isinstance(ups, list):+    kept = []+    for g in ups:+        if isinstance(g, dict):+            g["hooks"] = [h for h in g.get("hooks", []) if not (isinstance(h, dict) and h.get("command") == cmd)]+            if g.get("hooks"):+                kept.append(g)+        else:+            kept.append(g)+    d["hooks"]["UserPromptSubmit"] = kept+    with open(path, "w") as f: json.dump(d, f, indent=2)+PY+}++unwire "${HOME}/.claude/settings.json"+unwire "${HOME}/.codex/hooks.json"+rm -f "$HOOK"+rm -rf "${HOME}/.claude/skills/adom-hook"+echo "Removed adom-hook (unwired from Claude + Codex)"