#!/usr/bin/env bash # skills-update: refresh omo's upstream-tracked skills. On flowstate-remote it runs daily from the # skills-update.timer systemd user unit; `skills-update --install-timer` writes and enables it. # # Vendored in this repo (.config/omo/skills, linked into ~/.omo/agent/skills), committed and pushed: # impeccable newest pbakaus/impeccable skill-v* release, re-vendored the way # .config/omo/skills/README.md describes (path rewrite, OmO routing line) # web-design-guidelines vercel-labs/agent-skills main, when its content changes # Local to this machine (~/.omo/agent/skills, not in git): # vercel-react-best-practices vercel-labs/agent-skills skills/react-best-practices # docx pdf pptx xlsx skill-creator anthropics/skills skills/ # deep-research came from Claude's skill sync and has no public upstream, so it is not updated. # # Only the skill paths are committed, and the push happens only when main was level with # origin/main, so unrelated local edits and unpushed commits are never published. A skill whose # update fails is restored to its previous copy, and the run exits non-zero. # # Env overrides (for testing): DOTFILES, SKILLS, XDG_STATE_HOME. set -uo pipefail DOTFILES=${DOTFILES:-$HOME/dotfiles} VENDORED=$DOTFILES/.config/omo/skills SKILLS=${SKILLS:-$HOME/.omo/agent/skills} STATE=${XDG_STATE_HOME:-$HOME/.local/state}/skills-update work=$(mktemp -d) trap 'rm -rf "$work"' EXIT failures=0 msg=() log() { printf '%s\n' "$*"; } fail() { log "FAIL: $*" failures=$((failures + 1)) } install_timer() { local dir=${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user mkdir -p "$dir" cat >"$dir/skills-update.service" <"$dir/skills-update.timer" </dev/null 2>&1 || log "impeccable: engine $engine download failed; the launcher retries on first use" msg+=("impeccable: $tag, engine $engine ($sha)") log "impeccable: vendored $tag (engine $engine)" } update_web_design_guidelines() { local sha src dst=$VENDORED/web-design-guidelines sha=$(head_sha vercel-labs/agent-skills main) || return 2 if [ "$sha" = "$(readme_rev web-design-guidelines)" ]; then log "web-design-guidelines: up to date" return 1 fi src=$(fetch vercel-labs/agent-skills "$sha") || return 2 src=$src/skills/web-design-guidelines [ -f "$src/SKILL.md" ] || return 2 rsync -a --delete "$src/" "$dst/" if ! git -C "$DOTFILES" status --porcelain -- "$dst" | grep -q .; then log "web-design-guidelines: content unchanged at ${sha:0:7}" return 1 fi sed -i -E "/^\| \`web-design-guidelines\` \|/s/\`[0-9a-f]{40}\`/\`$sha\`/" "$VENDORED/README.md" msg+=("web-design-guidelines: vercel-labs/agent-skills $sha") log "web-design-guidelines: updated to ${sha:0:7}" } update_vendored() { cd "$DOTFILES" || { fail "no dotfiles checkout at $DOTFILES"; return; } [ "$(git branch --show-current)" = main ] || { fail "dotfiles is not on main; vendored skills skipped"; return; } if git status --porcelain -- .config/omo/skills | grep -q .; then fail "uncommitted edits under .config/omo/skills; vendored skills skipped" return fi git fetch -q origin || { fail "git fetch failed; vendored skills skipped"; return; } local ahead behind name rc read -r ahead behind < <(git rev-list --left-right --count main...origin/main) if [ "$behind" -gt 0 ] && ! git merge -q --ff-only origin/main; then fail "dotfiles cannot fast-forward to origin/main; vendored skills skipped" return fi for name in impeccable web-design-guidelines; do cp "$VENDORED/README.md" "$work/README.md.before" "update_${name//-/_}" rc=$? if [ "$rc" -ge 2 ]; then git checkout -q -- ".config/omo/skills/$name" git clean -fdq -- ".config/omo/skills/$name" cp "$work/README.md.before" "$VENDORED/README.md" fail "$name: update failed (exit $rc); kept the vendored copy" fi done [ "${#msg[@]}" -gt 0 ] || return git add -- .config/omo/skills git commit -q -m "chore(omo): update vendored skills" -m "$(printf '%s\n' "${msg[@]}")" -- .config/omo/skills || { fail "dotfiles commit failed"; return; } if [ "$ahead" -gt 0 ]; then log "dotfiles: main had $ahead unpushed commits, so $(git rev-parse --short HEAD) stays local" elif git push -q origin main; then log "dotfiles: pushed $(git rev-parse --short HEAD)" else fail "dotfiles push failed; $(git rev-parse --short HEAD) is committed locally" fi } update_local() { local name=$1 repo=$2 path=$3 dst=$SKILLS/$1 sha src if [ -L "$dst" ]; then log "$name: links into the dotfiles, skipped" return fi sha=$(head_sha "$repo" main) || { fail "$name: cannot read $repo"; return; } if [ "$sha" = "$(cat "$STATE/$name" 2>/dev/null)" ]; then log "$name: up to date" return fi src=$(fetch "$repo" "$sha") || { fail "$name: download of $repo failed"; return; } src=$src/$path [ -f "$src/SKILL.md" ] || { fail "$name: $repo has no $path/SKILL.md"; return; } mkdir -p "$dst" if diff -rq "$src" "$dst" >/dev/null 2>&1; then log "$name: content unchanged at ${sha:0:7}" else rsync -a --delete "$src/" "$dst/" log "$name: updated to $repo@${sha:0:7}" fi printf '%s\n' "$sha" >"$STATE/$name" } mkdir -p "$STATE" update_vendored update_local vercel-react-best-practices vercel-labs/agent-skills skills/react-best-practices for name in docx pdf pptx xlsx skill-creator; do update_local "$name" anthropics/skills "skills/$name" done log "deep-research: no public upstream, not updated" [ "$failures" -eq 0 ]