| name | pai-watch |
| description | Hourly upstream watcher for 4 PAI ecosystem repos (oh-my-claudecode, Personal_AI_Infrastructure, pai-anywhere, pai-review-mode). Fetches origin, computes impact score, writes JSON proposal to disk, posts mobile alert via pai-pulse. Use when user says check upstream, watch repos, what changed, upstream bump, propose upgrade. |
pai-watch skill
When to use
User intent:
- "check upstream for X" → manual one-shot watch
- "what changed in PAI / OMC this week" → recent commits summary
- "any upgrades pending" → list active proposals
- Automated via cron (default: hourly) — see
cron/README.md (Hermes jobs.json registration)
Algorithm
For each repo in PAI_WATCH_SOURCES (default 4):
- Validate repo name and resolve path (see Repo validation below).
GIT_CONFIG_GLOBAL=/dev/null git -C <repo-dir> fetch --quiet origin
- If fetch fails, log error and skip to next repo — do not abort.
- Determine the remote default tracking branch:
- Try
GIT_CONFIG_GLOBAL=/dev/null git -C <repo-dir> symbolic-ref refs/remotes/origin/HEAD and extract refs/remotes/origin/<branch>.
- If that fails, try
main; if missing, try master.
- If neither exists, skip the repo.
- Compare
HEAD vs upstream branch with GIT_CONFIG_GLOBAL=/dev/null git -C <repo-dir> rev-list --left-right HEAD..origin/<branch>.
- If diverged, get commit log
GIT_CONFIG_GLOBAL=/dev/null git -C <repo-dir> log --pretty="%h %s" HEAD..origin/<branch>.
- Compute impact score (0-100):
+5 per new commit
+25 per commit subject matching \b(breaking|major|incompatible|drop|remove)\b (case-insensitive)
+15 per commit touching critical paths: src/cli/, Algorithm/, Pulse/, install.sh, paths.env
- Cap at 100.
- If score ≥
PAI_WATCH_THRESHOLD (default 10):
- Ensure the output dir exists (
mkdir -p "$PAI_PROPOSALS_DIR"), then write the
JSON proposal to $PAI_PROPOSALS_DIR/<id>.json (id = <ISO-ts>-<repo>).
- Notify via
pai-pulse-send --message "<alert text>".
- Else log and skip.
Repo validation
Before running any git command, each repo name from PAI_WATCH_SOURCES must be validated:
PAI_PROJET_ROOT="${PAI_PROJET_ROOT:-/opt/pai-projet}"
for repo in $PAI_WATCH_SOURCES; do
if [[ ! "$repo" =~ ^[A-Za-z0-9._-]+$ ]]; then
echo "pai-watch: skipping unsafe repo name: $repo" >&2
continue
fi
repo_path="$(realpath -m "${PAI_PROJET_ROOT}/${repo}" 2>/dev/null || echo "")"
root_real="$(realpath -m "${PAI_PROJET_ROOT}" 2>/dev/null || echo "${PAI_PROJET_ROOT}")"
case "${repo_path}/" in
"${root_real}/"*) ;;
*) echo "pai-watch: path traversal refused for: $repo" >&2; continue ;;
esac
GIT_CONFIG_GLOBAL=/dev/null git -C "$repo_path" fetch --quiet origin
done
GIT_CONFIG_GLOBAL=/dev/null prevents a crafted ~/.gitconfig (or a repo-local
core.hooksPath inherited from global config) from executing arbitrary commands during fetch.
Proposal JSON schema
{ "schema": "pai.proposal.v1", "id": "2026-05-16T03-00-00Z-oh-my-claudecode", "repo": "oh-my-claudecode",
"targetSha": "abc1234567890abcdef...", "createdAt": "2026-05-16T03:00:00Z", "impactScore": 35,
"commits": "abc1234 feat: new ralph mode\ndef5678 BREAKING: drop legacy hooks", "status": "pending" }
Inputs
| Env var | Default | Purpose |
|---|
PAI_WATCH_SOURCES | "oh-my-claudecode Personal_AI_Infrastructure pai-anywhere pai-review-mode" | Space-separated repo dir names under $PAI_PROJET_ROOT. install.sh narrows this to repos that exist and writes it to pai-hermes.env. |
PAI_PROJET_ROOT | /opt/pai-projet | Root of the sub-project clones. The default rarely matches; install.sh auto-detects (parent of the checkout) and persists to pai-hermes.env. Verify on a new host. |
PAI_PROPOSALS_DIR | ${XDG_STATE_HOME:-$HOME/.local/state}/pai-hermes/proposals | Output dir under the Hermes user's state dir; created on demand. |
PAI_WATCH_THRESHOLD | 10 | Min impact score to propose |
PAI_PULSE_URL | http://127.0.0.1:31337 | Pulse for notify |
Cron entry
Register via Hermes — see cron/README.md. Job is stored in ~/.hermes/cron/jobs.json:
{ "name": "pai-watch", "schedule": { "kind": "cron", "expr": "0 * * * *" }, "skill": "pai-watch" }
Cost
ZERO AI cost. Pure git fetch + bash regex. No model invocation.
Caveats
- Under cron / the
execute_code sandbox, login-shell env may be absent — source "${HERMES_HOME:-$HOME/.hermes}/pai-hermes.env" at script start to load PAI_PROJET_ROOT, PAI_WATCH_SOURCES, PAI_PROPOSALS_DIR, etc. Don't assume inherited env.
- Requires
git fetch access (HTTPS or SSH) per source repo. Any per-repo git failure is non-fatal — one broken clone never aborts the run.
- SSH → HTTPS fallback (public repos only): if
git fetch returns 128 with Permission denied (publickey) and the repo is public, rewrite the origin to https://github.com/<org>/<repo>.git (git remote get-url/set-url) and retry. Private repos: skip.
- If
$PAI_PROPOSALS_DIR isn't writable, the proposal is silently skipped — verify via pai-doctor.
- Pulse
/notify failure is non-fatal — the proposal is still written to disk.
- Repo names with
.., leading -, or characters outside [A-Za-z0-9._-] are skipped.
- If
terminal is blocked by Tirith (pending_approval), replicate the pure-bash algorithm in execute_code via subprocess.run(["git", ...]) — identical results. Do not retry terminal unchanged.
Skill chain
pai-watch → writes proposals → calls pai-pulse-send to push mobile alert.
User then runs pai-watch list or pai-accept <id> to SHA-pin a proposal.
References
See references/failure-modes.md for observed git failure modes and handling logic.