From a144f57e1b5944ae0f22e07e5303a5ddde3b881e Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Sat, 2 May 2026 11:23:34 +0000 Subject: [PATCH] =?UTF-8?q?v0.2.0:=20split=20=E2=80=94=20public=20framewor?= =?UTF-8?q?k=20only,=20runbook=20fetched=20from=20private=20host?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The agent and skill prose used to ship inside this package, which exposed home-infra topology (hosts, IPs, AINs, container layout, /opt/herding cred paths) to anyone with the public package URL. This release moves to a fetcher model: - Public package ships only the plumbing: claude-his-fetch + claude-his-install. - Runbook content lives on a private host at $HIS_CONTEXT_HOST:/opt/his-context/. - claude-his-fetch (rsync over SSH) populates ~/.cache/claude-his-agent/. - claude-his-install symlinks ~/.claude/agents/his.md + ~/.claude/skills/his into the cache. History rewritten — the previous tree contained sensitive operational details. A bundle of the pre-rewrite tree is preserved out-of-band by the maintainer. --- README.md | 99 ++++++++++++++++++++++++++++++++++++++++++ bin/claude-his-fetch | 31 +++++++++++++ bin/claude-his-install | 26 +++++++++++ 3 files changed, 156 insertions(+) create mode 100644 README.md create mode 100755 bin/claude-his-fetch create mode 100755 bin/claude-his-install diff --git a/README.md b/README.md new file mode 100644 index 0000000..50a1002 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +# claude-his-agent + +A Claude Code subagent + skill for a personal home-infrastructure assistant +("His" — Home Infrastructure Specialist). Designed so a sibling Claude on any +host in your fleet can summon a single, consistent infra runbook instead of +re-learning your topology every session. + +## Distribution model + +This package ships **only the plumbing** — a fetcher and an installer. The +actual runbook (agent prompt + skill cheatsheet) lives on a private host that +you control and is pulled into a per-user cache the first time you run +`claude-his-install`. None of the infra context is in the public package or +its git history. + +``` +public package (this repo) → /usr/bin/claude-his-fetch + /usr/bin/claude-his-install + /usr/share/doc/claude-his-agent/README.md + +private host (you set up) → /opt/his-context/agent.md +($HIS_CONTEXT_HOST, default hertz) /opt/his-context/skill/SKILL.md + /opt/his-context/scripts/... + +per-user cache (after fetch) → ~/.cache/claude-his-agent/ +~/.claude wiring (after install) → ~/.claude/agents/his.md -> cache/agent.md + ~/.claude/skills/his -> cache/skill +``` + +`claude-his-fetch` is a thin `rsync -a -e ssh "$HOST:$PATH" "$CACHE/"`. The +trust boundary is your existing SSH key auth on `$HIS_CONTEXT_HOST`. + +## Install + +```bash +# Debian (trixie/bookworm): +sudo apt install claude-his-agent +claude-his-install + +# Arch / ALARM: +sudo pacman -S claude-his-agent +claude-his-install +``` + +`claude-his-install` runs `claude-his-fetch` if the cache is empty, then +symlinks `~/.claude/agents/his.md` + `~/.claude/skills/his` to the cache. + +## Refresh + +Re-run `claude-his-fetch` whenever the runbook on the private host changes. +A weekly cron is reasonable: + +```cron +0 4 * * 0 $HOME/.local/bin/claude-his-fetch >/dev/null 2>&1 +``` + +(or just call `/usr/bin/claude-his-fetch` from any path the user can read.) + +## Configuration + +| Env var | Default | Purpose | +|----------------------|--------------------------------------|---------| +| `HIS_CONTEXT_HOST` | `hertz` | SSH target serving the context blob | +| `HIS_CONTEXT_PATH` | `/opt/his-context/` | Source dir on the host (trailing slash) | +| `HIS_CONTEXT_CACHE` | `~/.cache/claude-his-agent` | Local cache dir | + +`$HIS_CONTEXT_HOST` resolves through the user's `~/.ssh/config` and known_hosts, +so an alias like `Host hertz / HostName 192.0.2.10 / User youruser / IdentityFile ~/.ssh/id_ed25519` +keeps the rest of the stack hostname-agnostic. + +## Setting up the private host + +On `$HIS_CONTEXT_HOST`, lay out: + +``` +/opt/his-context/ +├── agent.md # subagent system prompt (the "take over" form) +├── skill/ +│ └── SKILL.md # skill cheatsheet (the "lookup" form) +└── scripts/ # any helpers the runbook references + └── ... +``` + +Owner whichever account the fleet logs in as; mode `0644` for files, `0755` +for dirs. The directory does not need to be world-readable — `claude-his-fetch` +authenticates as the SSH user. + +## Source + +Public framework: `git.reauktion.de/marfrit/claude-his-agent` +(this repo — no infra context, safe to mirror). + +Bug reports / runbook gaps for the *private* runbook stay private; for the +public plumbing (fetcher, installer, build), file against this repo's issues. + +## License + +Personal infra tooling; framework code is permissive (see LICENSE if shipped). +The runbook content (private) is not redistributable. diff --git a/bin/claude-his-fetch b/bin/claude-his-fetch new file mode 100755 index 0000000..536e38a --- /dev/null +++ b/bin/claude-his-fetch @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# claude-his-fetch — pull current His context from a private host over SSH. +# The public package ships zero infrastructure context; the runbook lives on a +# host you control and is fetched into a per-user cache that ~/.claude/ symlinks +# to. Re-run periodically (cron, or on demand) to refresh. +set -euo pipefail + +HOST="${HIS_CONTEXT_HOST:-hertz}" +SRC="${HIS_CONTEXT_PATH:-/opt/his-context/}" +CACHE="${HIS_CONTEXT_CACHE:-${HOME}/.cache/claude-his-agent}" + +mkdir -p "${CACHE}" + +echo "claude-his-fetch: ${HOST}:${SRC} -> ${CACHE}" + +if ! command -v rsync >/dev/null 2>&1; then + echo "claude-his-fetch: rsync not found; install rsync (apt/pacman) and retry" >&2 + exit 3 +fi + +# Reuses the user's existing SSH credentials; trust boundary is whatever +# already authorises the user on $HIS_CONTEXT_HOST. +rsync -a --delete -e ssh "${HOST}:${SRC}" "${CACHE}/" + +# Sanity — these are the two files ~/.claude symlinks expect to exist. +test -r "${CACHE}/agent.md" || { echo "fetch failed: missing agent.md" >&2; exit 2; } +test -r "${CACHE}/skill/SKILL.md" || { echo "fetch failed: missing skill/SKILL.md" >&2; exit 2; } + +agent_bytes=$(wc -c <"${CACHE}/agent.md") +skill_bytes=$(wc -c <"${CACHE}/skill/SKILL.md") +echo "claude-his-fetch: OK (${agent_bytes}B agent, ${skill_bytes}B skill)" diff --git a/bin/claude-his-install b/bin/claude-his-install new file mode 100755 index 0000000..aa8348c --- /dev/null +++ b/bin/claude-his-install @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# claude-his-install — wire ~/.claude/{agents,skills}/his to the fetched context cache. +# Runs claude-his-fetch first if the cache is empty. +set -euo pipefail + +CACHE="${HIS_CONTEXT_CACHE:-${HOME}/.cache/claude-his-agent}" + +if [ ! -r "${CACHE}/agent.md" ] || [ ! -r "${CACHE}/skill/SKILL.md" ]; then + echo "claude-his-install: cache empty, running claude-his-fetch first..." + claude-his-fetch +fi + +DEST="${HOME}/.claude" +mkdir -p "${DEST}/agents" "${DEST}/skills" + +ln -sfn "${CACHE}/agent.md" "${DEST}/agents/his.md" +ln -sfn "${CACHE}/skill" "${DEST}/skills/his" + +cat < ${CACHE}/agent.md + ${DEST}/skills/his -> ${CACHE}/skill + +Refresh by re-running 'claude-his-fetch' (or cron it weekly). +Override host/path via HIS_CONTEXT_HOST / HIS_CONTEXT_PATH / HIS_CONTEXT_CACHE. +EOF