Files
agent-skills/bin/link.sh
T
naps62andClaude Opus 5 5e95a7337f feat(week-review): spawn weekly via systemd timer
Fridays 17:00 Europe/Lisbon, pinned in the unit because the machine
clock is UTC. Creates an Agent of Empires session in a fresh
week-review/<ISO week> worktree, sends it /week-review, notifies ntfy.

Interactive, not `claude -p`: the skill asks which carry-over issues to
take and what to apply, so an unattended run would stall or decide on
its own. link.sh links the units but does not enable them, since
enabling on every machine spawns one session per box for the same week.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SC9zwjErud3jfeYVEkf2Zd
2026-08-01 15:51:34 +00:00

88 lines
3.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Symlink centralized skills/commands/hooks/context into each agent's config dir.
# For non-Nix machines (NixOS boxes use nix/home.nix instead).
# Idempotent. Backs up any pre-existing real dir once to <name>.bak.
set -euo pipefail
REPO="$(cd "$(dirname "$0")/.." && pwd)"
# targets: agent config skill roots. Claude Code reads ~/.claude/skills,
# Codex reads ~/.agents/skills. Both consume the same SKILL.md dirs.
CLAUDE_SKILLS="$HOME/.claude/skills"
CODEX_SKILLS="$HOME/.agents/skills"
CLAUDE_CMDS="$HOME/.claude/commands" # commands are Claude-only; Codex ignores
CLAUDE_HOOKS="$HOME/.claude/hooks" # hooks are Claude-only
CLAUDE_HOME="$HOME/.claude" # CLAUDE.md fragments, pulled in via @name.md
CLAUDE_RULES="$HOME/.claude/rules" # path-scoped rules
CODEX_HOME="$HOME/.codex" # Codex global config root
# Per-FILE links, never a whole-dir link: ~/.claude/hooks and ~/.claude itself hold
# machine-local files this repo does not own, and a dir symlink would hide them.
# backups go OUTSIDE the skill dirs — a *.bak left inside gets discovered as a
# duplicate skill by both Claude Code and Codex.
BACKUP="$HOME/.agent-skills-backup"
link() { # link <src> <dst>
local src="$1" dst="$2"
if [ -e "$dst" ] && [ ! -L "$dst" ]; then
mkdir -p "$BACKUP"
mv "$dst" "$BACKUP/$(basename "$(dirname "$dst")")-$(basename "$dst").bak"
echo "backed up existing $dst -> $BACKUP/"
fi
ln -sfn "$src" "$dst"
echo "linked $dst -> $src"
}
mkdir -p "$CLAUDE_SKILLS" "$CODEX_SKILLS" "$CLAUDE_CMDS" "$CLAUDE_HOOKS" "$CLAUDE_RULES" "$CODEX_HOME"
for d in "$REPO"/skills/*/; do
name="$(basename "$d")"
link "$d" "$CLAUDE_SKILLS/$name"
link "$d" "$CODEX_SKILLS/$name"
done
for f in "$REPO"/commands/*.md; do
[ -e "$f" ] || continue
link "$f" "$CLAUDE_CMDS/$(basename "$f")"
done
for f in "$REPO"/hooks/*.py "$REPO"/hooks/*.sh; do
[ -e "$f" ] || continue
link "$f" "$CLAUDE_HOOKS/$(basename "$f")"
done
# code-comments.md is path-scoped and belongs in rules/, not here — linking it
# into ~/.claude/ as well would load it unconditionally and defeat the scoping.
for f in "$REPO"/claude-md/*.md; do
[ -e "$f" ] || continue
[ "$(basename "$f")" = "code-comments.md" ] && continue
link "$f" "$CLAUDE_HOME/$(basename "$f")"
done
# Path-scoped rules load only when Claude reads a matching file.
link "$REPO/claude-md/code-comments.md" "$CLAUDE_RULES/code-comments.md"
rm -f "$CLAUDE_HOME/code-comments.md" "$HOME/.agents/AGENTS.md"
# Entry files: machine-local sections plus @imports of the shared fragments.
# Codex supports @path the same as CLAUDE.md, so neither tool needs a copy.
mkdir -p "$CODEX_HOME"
link "$REPO/entry/CLAUDE.md" "$CLAUDE_HOME/CLAUDE.md"
link "$REPO/entry/codex-AGENTS.md" "$CODEX_HOME/AGENTS.md"
rm -f "$CODEX_HOME/RTK.md"
# Linked but never enabled: enabling on every machine would spawn one review
# session per box for the same week.
if [ -d /run/systemd/system ]; then
mkdir -p "$HOME/.config/systemd/user"
for f in "$REPO"/systemd/*.service "$REPO"/systemd/*.timer; do
[ -e "$f" ] || continue
link "$f" "$HOME/.config/systemd/user/$(basename "$f")"
done
fi
echo "done."
echo "hooks still need wiring in ~/.claude/settings.json — see hooks/README.md"
echo "weekly review timer (one machine only):"
echo " systemctl --user daemon-reload && systemctl --user enable --now week-review.timer"