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.3.0
7 files changed
+455−22
README.md+17−2@@ -4,8 +4,6 @@ skills current from [wiki.adom.inc](https://wiki.adom.inc) automatically, on every Claude Code and Codex prompt. No git, no private repository. -- ## Why The old update path fetched a private GitHub repo on every prompt. That works for@@ -72,3 +70,20 @@ adom-wiki uninstall adom/hook ``` Unwires the hook from both `~/.claude/settings.json` and `~/.codex/hooks.json`.+++## claude-rc: Remote Control, kept alive++This package also installs `claude-rc`, which keeps Claude Code Remote Control (`claude+rc`) running on the container so you can open new AI threads on it from the Claude mobile+app or claude.ai/code whenever you want.++Nothing starts until you say yes: the next time you send a prompt in Claude Code or Codex,+the AI offers it once, and you answer in chat.++- **ah** (Hydrogen desktop container): a cron watchdog restarts the server within 2 minutes.+- **hc** (Hydrogen Cloud): no cron exists there, so the prompt hook restarts it on your next chat.++```+claude-rc status claude-rc enable claude-rc disable claude-rc logs+```
SKILL.md+80−1@@ -1,6 +1,13 @@ --- 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.+description: >-+ The two things adom wires into every Claude Code and Codex prompt: the registry-native+ auto-update hook (keeps adom/core apps and skills current from wiki.adom.inc), and+ claude-rc, which keeps Claude Remote Control alive so the user can open AI threads on+ this container from their phone. Read when packages are behind or an update failed, and+ when the user says claude rc, remote control, use my container from my phone, open a+ thread from my phone, claude-rc status, or you see an "adom: Claude Remote Control" line+ in your context. --- # adom-hook@@ -86,3 +93,75 @@ It is also a dependency of `adom/core`, so a normal bootstrap installs it for yo 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.+++# claude-rc: Claude Remote Control, kept alive++`claude rc` (Claude Code Remote Control) turns this container into a server the user can+open new Claude threads on from the Claude mobile app or https://claude.ai/code. It is a+plain foreground process with no built-in supervisor: it dies with the terminal that+started it and never comes back on its own. `claude-rc` is the keeper, and it rides in+this package because it needs exactly what this package already owns -- a+`UserPromptSubmit` hook and a cron entry.++## The consent flow (this is the part you participate in)++Installing only wires the OFFER. While the state is `unasked`, the prompt hook prints one+line into your context asking you to offer Remote Control to the user. Do it ONCE, in your+own words, before you get on with their actual request: it lets them open new AI threads+on this Adom Hydrogen container from their phone or claude.ai/code at any time, Adom keeps+it alive, and it needs their one-time OK. Then record their answer:++```bash+claude-rc enable # they said yes: registers supervision and launches now+claude-rc decline # they said no: the hook never offers again+```++**Never run `enable` without the user's yes.** Remote Control exposes this container's+sessions to their claude.ai account from any device; that is their call, not yours.++States: `unasked -> enabled | declined`, plus `disabled` (turned off later) and `failed`+(the server kept exiting; the hook reports that at most every 6 hours).++## ah vs hc: supervision differs, and you should say which the user has++- **ah** (Adom Hydrogen desktop container, `ADOM_DESKTOP_MODE=local`): cron exists, so+ `enable` registers `@reboot` and `*/2 * * * * claude-rc check`. The server comes back+ within two minutes even while the user is away -- this is what makes "open a thread from+ my phone whenever I want" actually true.+- **hc** (Hydrogen Cloud): no cron and no systemd, so THE PROMPT HOOK IS THE WATCHDOG. On+ every chat submit it relaunches a dead server and tells you in one line. If it dies+ while the user is away it stays down until their next prompt. Say so plainly rather than+ promising ah behaviour on hc.++systemd is not reachable from inside either container (no bus, no sudo), so it is never+used. Detection is `ADOM_DESKTOP_MODE=local` plus a live crond.++## Verbs++| verb | what |+|---|---|+| `claude-rc status` | state, environment, pid, session name, prereqs, last log lines |+| `claude-rc enable` | record consent, register supervision, launch now |+| `claude-rc decline` | the user said no; never offer again |+| `claude-rc disable` | stop the server, drop cron, stop watching |+| `claude-rc logs [n]` | tail `~/.adom/claude-rc.log` |+| `claude-rc check` | the watchdog itself (what cron and the hook call) |+| `claude-rc hook` | the UserPromptSubmit hook; never call it by hand |++## Facts worth knowing before you debug it++- `claude rc` skips its "Enable Remote Control? (y/n)" prompt when stdin is not a TTY, so+ the watchdog launches it headless with `setsid nohup ... </dev/null`.+- Prereqs: `claude` on PATH, a logged-in subscription account, and workspace trust+ accepted for `~/project` (`hasTrustDialogAccepted` in `~/.claude.json`). The hook only+ offers when these hold, which inside a running session they already do.+- The session name on claude.ai is `<container>-rc`; override with `CLAUDE_RC_NAME`. The+ server runs in `~/project` (`CLAUDE_RC_PROJECT_DIR`). `CLAUDE_RC_DISABLE=1` makes every+ verb a no-op.+- A restart is a NEW server. Sessions opened through the old one are still on disk and+ resumable with `claude --resume <id>`.+- Crash-loop guard: five launches within an hour flips the state to `failed` rather than+ spinning. The usual cause is a signed-out account. `claude-rc enable` retries.+- It dies with the container. On ah the `@reboot` line brings it back; on hc the next+ prompt does.
claude-rcadded+224@@ -0,0 +1,224 @@+#!/usr/bin/env bash+# claude-rc -- keep Claude Code Remote Control (`claude rc`) alive on an Adom container.+#+# One script, several verbs. Everything is idempotent and safe to call often.+#+# claude-rc hook the UserPromptSubmit hook. Fast + silent in the common case.+# Offers setup once (prints one line into the model's context),+# and on hc (no cron) doubles as the watchdog.+# claude-rc check the watchdog: launch `claude rc` if it is not running.+# claude-rc enable record consent, register cron on ah, launch now.+# claude-rc decline record "no thanks"; the hook never offers again.+# claude-rc disable stop the server, drop cron, stop watching (re-enable any time).+# claude-rc status where things stand (state, pid, name, env, last log lines).+# claude-rc logs tail the server log.+#+# Environments (see the ah-vs-hc note in SKILL.md):+# ah Adom Hydrogen desktop container: cron is available -> real 2-minute watchdog.+# hc Hydrogen Cloud: no cron, no systemd -> the prompt hook is the watchdog.+#+# Tunables (env):+# CLAUDE_RC_PROJECT_DIR directory the server runs in (default $HOME/project)+# CLAUDE_RC_NAME session name shown on claude.ai (default <container>-rc)+# CLAUDE_RC_DISABLE any non-empty value makes every verb a no-op+set -u+[ -n "${CLAUDE_RC_DISABLE:-}" ] && exit 0+export PATH="$HOME/.local/bin:$PATH"++STATE_DIR="$HOME/.adom"+STATE="$STATE_DIR/claude-rc.state"+LOG="$STATE_DIR/claude-rc.log"+SELF="$HOME/.local/bin/claude-rc"+PROJECT_DIR="${CLAUDE_RC_PROJECT_DIR:-$HOME/project}"+# Anchored to the START of the cmdline so a shell whose command text merely+# mentions "claude rc" (this script's own callers, a chat transcript) never matches.+PAT='^\S*/claude (rc|remote-control)( |$)'+mkdir -p "$STATE_DIR"++# ---- state file: KEY=VALUE lines -------------------------------------------+st_get() { [ -f "$STATE" ] && sed -n "s/^$1=//p" "$STATE" | tail -1; }+st_set() { # st_set KEY VALUE+ local tmp="$STATE.tmp.$$"+ { [ -f "$STATE" ] && grep -v "^$1=" "$STATE"; echo "$1=$2"; } > "$tmp" 2>/dev/null+ mv "$tmp" "$STATE"+}+now() { date +%s; }+state() { local s; s="$(st_get state)"; echo "${s:-unasked}"; }++# ---- environment -------------------------------------------------------------+is_ah() { [ "${ADOM_DESKTOP_MODE:-}" = "local" ]; }+has_cron() { command -v crontab >/dev/null 2>&1 && { pgrep -x cron >/dev/null 2>&1 || pgrep -x crond >/dev/null 2>&1; }; }+env_name() { if is_ah; then echo ah; else echo hc; fi; }+container_name() {+ local n="${ADOM_CONTAINER_NAME:-$(hostname 2>/dev/null || echo adom)}"+ echo "$n" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-\n' '-'+}+rc_name() { echo "${CLAUDE_RC_NAME:-$(container_name)-rc}"; }++running_pid() { pgrep -f "$PAT" | head -1; }+# TERM, wait up to 8s for a graceful exit, then KILL. `claude rc` takes a moment to+# tear down its relay connection, so a bare pkill + immediate pgrep still sees it.+stop_server() {+ pkill -f "$PAT" 2>/dev/null || return 0+ local i; for i in 1 2 3 4 5 6 7 8; do [ -z "$(running_pid)" ] && return 0; sleep 1; done+ pkill -9 -f "$PAT" 2>/dev/null || true; sleep 1+}++# Prerequisites `claude rc` needs: the CLI, a logged-in account, and the workspace+# trust dialog accepted for PROJECT_DIR. A UserPromptSubmit hook fires from inside a+# session so these normally already hold; the check matters for Codex callers.+prereqs_ok() {+ command -v claude >/dev/null 2>&1 || return 1+ [ -d "$PROJECT_DIR" ] || return 1+ command -v python3 >/dev/null 2>&1 || return 0+ python3 - "$HOME/.claude.json" "$PROJECT_DIR" <<'PY' 2>/dev/null+import json, sys+try:+ d = json.load(open(sys.argv[1]))+except Exception:+ sys.exit(1)+p = d.get("projects", {}).get(sys.argv[2], {})+sys.exit(0 if p.get("hasTrustDialogAccepted") else 1)+PY+}++# ---- cron (ah only) ----------------------------------------------------------+CRON_CMD="$SELF check"+cron_register() {+ has_cron || return 1+ { crontab -l 2>/dev/null | grep -vF "$SELF" || true+ echo "@reboot $CRON_CMD >/dev/null 2>&1"+ echo "*/2 * * * * $CRON_CMD >/dev/null 2>&1"; } | crontab - 2>/dev/null+}+cron_remove() {+ command -v crontab >/dev/null 2>&1 || return 0+ if crontab -l 2>/dev/null | grep -qF "$SELF"; then+ { crontab -l 2>/dev/null | grep -vF "$SELF" || true; } | crontab - 2>/dev/null+ fi+}+cron_registered() { crontab -l 2>/dev/null | grep -qF "$CRON_CMD"; }++# ---- launch / watchdog ---------------------------------------------------------+rotate_log() {+ [ -f "$LOG" ] && [ "$(stat -c %s "$LOG" 2>/dev/null || echo 0)" -gt 5000000 ] && mv "$LOG" "$LOG.1"+ return 0+}+launch() {+ cd "$PROJECT_DIR" || return 1+ rotate_log+ echo "[$(date -Is)] claude-rc: launching (name=$(rc_name), env=$(env_name))" >> "$LOG"+ setsid nohup claude rc --name "$(rc_name)" </dev/null >> "$LOG" 2>&1 &+ # crash-loop guard: remember launch times, keep the last hour's worth+ local t; t="$(now)"+ local hist; hist="$(st_get launches)"+ local kept=""; local x+ for x in ${hist//,/ }; do [ $((t - x)) -lt 3600 ] && kept="$kept,$x"; done+ kept="${kept#,}"; kept="${kept:+$kept,}$t"+ st_set launches "$kept"+ st_set last_launch "$t"+ local n; n=$(echo "$kept" | tr ',' '\n' | grep -c .)+ if [ "$n" -ge 5 ]; then+ st_set state failed+ st_set failed_at "$t"+ st_set fail_reason "$(tr -d '\r' < "$LOG" | grep -v '^\s*$' | grep -v 'claude-rc:' | tail -3 | tr '\n' ' ' | cut -c1-300)"+ echo "[$(date -Is)] claude-rc: $n launches within an hour; marking failed. Run 'claude-rc enable' to retry." >> "$LOG"+ return 2+ fi+ return 0+}+check() {+ [ "$(state)" = "enabled" ] || return 0+ if [ -n "$(running_pid)" ]; then return 0; fi+ launch; return $?+}++# ---- verbs ---------------------------------------------------------------------+cmd_hook() {+ case "$(state)" in+ enabled)+ if [ -z "$(running_pid)" ]; then+ if launch; then+ echo "adom: Claude Remote Control was not running on this container; relaunched it as '$(rc_name)'. It should reappear on claude.ai/code and the mobile app within a minute."+ else+ echo "adom: Claude Remote Control keeps exiting on this container and has been paused after repeated relaunches. Last output: $(st_get fail_reason). Tell the user; 'claude-rc logs' shows more and 'claude-rc enable' retries."+ fi+ fi ;;+ failed)+ # remind at most every 6h+ local last; last="$(st_get fail_reported)"; local t; t="$(now)"+ if [ -z "$last" ] || [ $((t - last)) -gt 21600 ]; then+ st_set fail_reported "$t"+ echo "adom: Claude Remote Control is paused on this container after it kept exiting (last output: $(st_get fail_reason)). Mention it to the user once; 'claude-rc enable' retries, 'claude-rc disable' turns it off."+ fi ;;+ unasked)+ prereqs_ok || exit 0+ # offer at most once per 24h in case the model never records an answer+ local asked; asked="$(st_get asked_at)"; local t; t="$(now)"+ if [ -z "$asked" ] || [ $((t - asked)) -gt 86400 ]; then+ st_set asked_at "$t"+ echo "adom: Claude Remote Control is not set up on this container yet. ONCE, before handling the user's request, offer it in your own words: it lets them open new AI threads on this Adom Hydrogen container from their phone (Claude mobile app) or claude.ai/code at any time, and Adom keeps the server alive automatically ($(env_name): $(if is_ah; then echo 'cron watchdog'; else echo 'checked on every prompt'; fi)). It needs their one-time OK. If they say yes run: claude-rc enable If they say no run: claude-rc decline Either way do not ask again."+ fi ;;+ *) : ;; # declined / disabled: silent+ esac+ exit 0+}++cmd_enable() {+ st_set state enabled+ st_set enabled_at "$(now)"+ st_set launches ""+ st_set name "$(rc_name)"+ local how="prompt-hook watchdog (hc: no cron here)"+ if is_ah || has_cron; then+ if cron_register; then how="cron watchdog every 2 min + @reboot (ah)"; else how="prompt-hook watchdog (cron unavailable)"; fi+ fi+ if [ -n "$(running_pid)" ]; then+ echo "claude-rc: enabled; server already running (pid $(running_pid)); supervision: $how"+ else+ launch || true+ sleep 4+ if [ -n "$(running_pid)" ]; then+ echo "claude-rc: enabled and running as '$(rc_name)' (pid $(running_pid)); supervision: $how. It appears on claude.ai/code and the Claude mobile app within about a minute."+ else+ echo "claude-rc: enabled, but the server exited right away. Last output:"; tr -d '\r' < "$LOG" | grep -v '^\s*$' | tail -5+ echo "Usually: not logged in ('claude' first), or workspace trust not accepted in $PROJECT_DIR."+ fi+ fi+}+cmd_decline() { st_set state declined; st_set declined_at "$(now)"; cron_remove; echo "claude-rc: declined. The hook will not offer again; 'claude-rc enable' turns it on later."; }+cmd_disable() {+ st_set state disabled; cron_remove+ local p; p="$(running_pid)"+ if [ -n "$p" ]; then+ stop_server+ echo "claude-rc: disabled; stopped server pid $p and removed cron entries."+ else+ echo "claude-rc: disabled; cron entries removed."+ fi+}+cmd_status() {+ local p; p="$(running_pid)"+ echo "state: $(state)"+ echo "env: $(env_name) (ADOM_DESKTOP_MODE=${ADOM_DESKTOP_MODE:-unset}, cron: $(if has_cron; then echo yes; else echo no; fi), cron registered: $(if cron_registered; then echo yes; else echo no; fi))"+ echo "server: $(if [ -n "$p" ]; then echo "running pid $p"; else echo "not running"; fi)"+ echo "name: $(rc_name)"+ echo "project dir: $PROJECT_DIR"+ echo "prereqs: $(if prereqs_ok; then echo ok; else echo "missing (claude on PATH + workspace trust accepted in $PROJECT_DIR)"; fi)"+ [ "$(state)" = failed ] && echo "fail reason: $(st_get fail_reason)"+ echo "log: $LOG"+ [ -f "$LOG" ] && { echo "--- last lines:"; tr -d '\r' < "$LOG" | grep -v '^\s*$' | tail -4; }+ return 0+}+cmd_logs() { [ -f "$LOG" ] && tr -d '\r' < "$LOG" | grep -v '^\s*$' | tail -${1:-40}; }++case "${1:-status}" in+ hook) cmd_hook ;;+ check) check ;;+ enable) cmd_enable ;;+ decline) cmd_decline ;;+ disable) cmd_disable ;;+ status) cmd_status ;;+ logs) cmd_logs "${2:-40}" ;;+ -h|--help|help) sed -n 2,24p "$0" ;;+ *) echo "claude-rc: unknown verb '$1' (hook|check|enable|decline|disable|status|logs)" >&2; exit 2 ;;+esac
install.sh+44−7@@ -12,6 +12,35 @@ mkdir -p "$HOOK_DIR" install -m 0755 "$PKG_DIR/check-updates.sh" "$HOOK" HOOK_CMD="bash $HOOK" +# --- claude-rc: the Remote Control keeper (adom/hook 1.3.0) --------------------+# Same machinery this package already owns (a UserPromptSubmit hook + cron), so it+# lives here rather than in a package of its own. It keeps `claude rc` alive so a+# user can open new AI threads on this container from the Claude mobile app or+# claude.ai/code. It is CONSENT-FIRST: installing only wires the offer; nothing+# runs until the user says yes in chat (see SKILL.md).+BIN_DIR="${HOME}/.local/bin"+RC="${BIN_DIR}/claude-rc"+RC_HOOK_CMD="$RC hook"+mkdir -p "$BIN_DIR"+install -m 0755 "$PKG_DIR/claude-rc" "$RC"++# Migrate the hand-rolled predecessor (a ~/.adom/hooks/claude-rc-watchdog.sh plus its+# own cron lines). Its presence IS consent -- the user asked for it explicitly -- so+# carry that over as enabled instead of re-asking.+RC_STATE="${HOME}/.adom/claude-rc.state"+LEGACY_RC="${HOOK_DIR}/claude-rc-watchdog.sh"+if [ -f "$LEGACY_RC" ] || crontab -l 2>/dev/null | grep -q "claude-rc-watchdog.sh"; then+ if command -v crontab >/dev/null 2>&1 && crontab -l 2>/dev/null | grep -q "claude-rc-watchdog.sh"; then+ { crontab -l 2>/dev/null | grep -v "claude-rc-watchdog.sh" || true; } | crontab - 2>/dev/null || true+ fi+ rm -f "$LEGACY_RC"+ if ! grep -q '^state=' "$RC_STATE" 2>/dev/null; then+ mkdir -p "$(dirname "$RC_STATE")"+ echo "state=enabled" >> "$RC_STATE"+ echo " migrated the pre-package claude-rc watchdog (consent carried over as enabled)"+ fi+fi+ # Drop the stale launcher file from older adom/hook installs (now adom-core-update.sh). rm -f "${HOOK_DIR}/check-updates.sh" 2>/dev/null || true @@ -20,12 +49,13 @@ if ! command -v python3 >/dev/null 2>&1; then exit 0 fi -# Wire HOOK_CMD into one agent config's hooks.UserPromptSubmit, removing any legacy-# auto-update hook first. $1 = config path, $2 = "1" to include a timeout field.+# Wire a command into one agent config's hooks.UserPromptSubmit, removing any legacy+# auto-update hook first. $1 = config path, $2 = "1" to include a timeout field,+# $3 = the command (defaults to the auto-update hook). wire() {- local path="$1" with_timeout="$2"+ local path="$1" with_timeout="$2" cmd="${3:-$HOOK_CMD}" mkdir -p "$(dirname "$path")"- python3 - "$path" "$HOOK_CMD" "$with_timeout" <<'PY'+ python3 - "$path" "$cmd" "$with_timeout" <<'PY' import json, sys path, cmd, with_timeout = sys.argv[1], sys.argv[2], sys.argv[3] == "1" try:@@ -67,14 +97,21 @@ if changed: with open(path, "w") as f: json.dump(d, f, indent=2) note = " (retired %d legacy hook(s))" % removed if removed else ""- print(" wired adom-core-update into %s%s" % (path, note))+ print(" wired %s into %s%s" % (cmd.split("/")[-1].split()[0], path, note)) else:- print(" adom-core-update already wired in %s" % path)+ print(" %s already wired in %s" % (cmd.split("/")[-1].split()[0], path)) PY } wire "${HOME}/.claude/settings.json" 1 wire "${HOME}/.codex/hooks.json" 0+wire "${HOME}/.claude/settings.json" 1 "$RC_HOOK_CMD"+wire "${HOME}/.codex/hooks.json" 0 "$RC_HOOK_CMD"++# Re-assert supervision when consent already exists (re-install, image bake, migration).+if grep -q '^state=enabled' "$RC_STATE" 2>/dev/null; then+ "$RC" enable || true+fi # --------------------------------------------------------------------------- # Headless trigger (adom/hook #1). UserPromptSubmit only fires inside Claude Code@@ -179,4 +216,4 @@ if [ -f "$PKG_DIR/SKILL.md" ]; then 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)"+echo "Installed adom-hook (auto-update wired for Claude + Codex; checks every ${ADOM_HOOK_INTERVAL:-1800}s; claude-rc staged, offer on next prompt)"
package.json+36−4@@ -1,10 +1,10 @@ { "slug": "hook",- "version": "1.2.4",+ "version": "1.3.0", "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.",+ "description": "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.", "dependencies": {}, "scripts": { "install": "./install.sh",@@ -16,7 +16,11 @@ "bootstrap", "claude-code", "codex",- "registry"+ "registry",+ "remote-control",+ "claude-rc",+ "mobile",+ "watchdog" ], "discovery_triggers": [ "auto update skills and apps",@@ -25,7 +29,14 @@ "adom-wiki update hook", "claude code update hook", "codex update hook",- "registry auto update"+ "registry auto update",+ "claude remote control",+ "claude rc",+ "open a thread from my phone",+ "use my container from my phone",+ "spawn claude threads from mobile",+ "remote control not running",+ "claude-rc status" ], "hero": { "headline": "Always current. Zero clicks.",@@ -38,6 +49,27 @@ "src": "check-updates.sh", "dest": "~/.adom/hooks/adom-core-update.sh", "mode": "0755"+ },+ {+ "src": "claude-rc",+ "dest": "~/.local/bin/claude-rc",+ "mode": "0755"+ }+ ],+ "changelog": "1.3.0: add claude-rc, which keeps Claude Code Remote Control (`claude rc`) alive so a user can open new AI threads on their container from the Claude mobile app or claude.ai/code. It rides in adom/hook because it needs exactly what this package already owns: a UserPromptSubmit hook and a cron entry. Consent-first -- installing only wires the offer, and the AI asks the user once. Supervision differs by environment: a 2-minute cron watchdog on ah, and the prompt hook itself on hc, which has neither cron nor systemd. No dependency changes.",+ "discovery_pitch": "Use when adom packages are behind or an update failed, and when the user asks about Claude Remote Control -- opening AI threads on this container from their phone or claude.ai/code.",+ "sample_prompts": [+ {+ "label": "Is remote control running?",+ "prompt": "is claude remote control running on this container?"+ },+ {+ "label": "Set it up",+ "prompt": "set up claude rc so I can open threads here from my phone"+ },+ {+ "label": "Am I up to date?",+ "prompt": "are my adom packages current with the wiki?" } ] }
page.json+44−5@@ -1,10 +1,10 @@ { "slug": "hook",- "version": "1.2.4",+ "version": "1.3.0", "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.",+ "description": "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.", "dependencies": {}, "scripts": { "install": "./install.sh",@@ -16,7 +16,11 @@ "bootstrap", "claude-code", "codex",- "registry"+ "registry",+ "remote-control",+ "claude-rc",+ "mobile",+ "watchdog" ], "discovery_triggers": [ "auto update skills and apps",@@ -25,7 +29,14 @@ "adom-wiki update hook", "claude code update hook", "codex update hook",- "registry auto update"+ "registry auto update",+ "claude remote control",+ "claude rc",+ "open a thread from my phone",+ "use my container from my phone",+ "spawn claude threads from mobile",+ "remote control not running",+ "claude-rc status" ], "hero": { "type": "billboard",@@ -38,5 +49,33 @@ "author": { "name": "Kyle Bergstedt", "email": "[email protected]"- }+ },+ "managed_files": [+ {+ "src": "check-updates.sh",+ "dest": "~/.adom/hooks/adom-core-update.sh",+ "mode": "0755"+ },+ {+ "src": "claude-rc",+ "dest": "~/.local/bin/claude-rc",+ "mode": "0755"+ }+ ],+ "changelog": "1.3.0: add claude-rc, which keeps Claude Code Remote Control (`claude rc`) alive so a user can open new AI threads on their container from the Claude mobile app or claude.ai/code. It rides in adom/hook because it needs exactly what this package already owns: a UserPromptSubmit hook and a cron entry. Consent-first -- installing only wires the offer, and the AI asks the user once. Supervision differs by environment: a 2-minute cron watchdog on ah, and the prompt hook itself on hc, which has neither cron nor systemd. No dependency changes.",+ "discovery_pitch": "Use when adom packages are behind or an update failed, and when the user asks about Claude Remote Control -- opening AI threads on this container from their phone or claude.ai/code.",+ "sample_prompts": [+ {+ "label": "Is remote control running?",+ "prompt": "is claude remote control running on this container?"+ },+ {+ "label": "Set it up",+ "prompt": "set up claude rc so I can open threads here from my phone"+ },+ {+ "label": "Am I up to date?",+ "prompt": "are my adom packages current with the wiki?"+ }+ ] }
uninstall.sh+10−3@@ -5,10 +5,10 @@ HOOK="${HOME}/.adom/hooks/adom-core-update.sh" HOOK_CMD="bash $HOOK" unwire() {- local path="$1"+ local path="$1" cmd="${2:-$HOOK_CMD}" [ -f "$path" ] || return 0 command -v python3 >/dev/null 2>&1 || return 0- python3 - "$path" "$HOOK_CMD" <<'PY'+ python3 - "$path" "$cmd" <<'PY' import json, sys path, cmd = sys.argv[1], sys.argv[2] try:@@ -33,6 +33,13 @@ PY unwire "${HOME}/.claude/settings.json" unwire "${HOME}/.codex/hooks.json" +# claude-rc rides in this package: stop it, drop its cron, unwire and remove it.+RC="${HOME}/.local/bin/claude-rc"+[ -x "$RC" ] && "$RC" disable >/dev/null 2>&1 || true+unwire "${HOME}/.claude/settings.json" "$RC hook"+unwire "${HOME}/.codex/hooks.json" "$RC hook"+rm -f "$RC" "${HOME}/.adom/claude-rc.state"+ # 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@@ -45,4 +52,4 @@ fi rm -f "$HOOK" rm -rf "${HOME}/.claude/skills/adom-hook" 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)"+echo "Removed adom-hook (auto-update and claude-rc unwired from Claude + Codex, cron entries dropped; ~/.adom/claude-rc.log kept)"