mirror of
https://github.com/Hestia-Homes/agentic-toolkit.git
synced 2026-06-08 11:37:26 +00:00
126 lines
4.7 KiB
Bash
Executable file
126 lines
4.7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Install Domna's curated skill set globally (~/.claude).
|
|
#
|
|
# Run from the root of the target repo:
|
|
# curl -fsSL https://raw.githubusercontent.com/Hestia-Homes/agentic-toolkit/main/setup.sh | bash
|
|
# Or, if you've cloned agentic-toolkit:
|
|
# bash /path/to/agentic-toolkit/setup.sh
|
|
#
|
|
# What this does:
|
|
# 1. Copies the pinned skills-lock.json from agentic-toolkit to ~/.claude.
|
|
# 2. Parses it and runs `skills add --global` per source so all skills land
|
|
# in ~/.claude/skills (user-level, available in every repo).
|
|
#
|
|
# Note: `skills experimental_install` would be the natural fit but it only
|
|
# restores at project scope — confirmed empirically that running it against
|
|
# a global lock prints "No project skills found" and exits 0, installing
|
|
# nothing. So we drive `skills add --global` from the lock instead.
|
|
#
|
|
# Logs verbose output to $HOME/.claude/agentic-toolkit-setup.log so failures
|
|
# under postCreateCommand or `| bash` are inspectable after the fact.
|
|
#
|
|
set -euo pipefail
|
|
|
|
# --- logging -------------------------------------------------------------------
|
|
CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
mkdir -p "$CLAUDE_DIR"
|
|
LOG_FILE="$CLAUDE_DIR/agentic-toolkit-setup.log"
|
|
# Tee everything (stdout + stderr) to the log so silent failures aren't silent.
|
|
exec > >(tee -a "$LOG_FILE") 2>&1
|
|
echo "=== agentic-toolkit setup.sh @ $(date -u +%FT%TZ) ==="
|
|
|
|
# Trap so a failure under set -e prints the failing line into the log.
|
|
trap 'rc=$?; echo "ERROR: setup.sh exited $rc at line $LINENO" >&2; exit $rc' ERR
|
|
|
|
# --- config --------------------------------------------------------------------
|
|
LOCK_URL="https://raw.githubusercontent.com/Hestia-Homes/agentic-toolkit/main/skills-lock.json"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd || true)"
|
|
LOCAL_LOCK="${SCRIPT_DIR:+$SCRIPT_DIR/skills-lock.json}"
|
|
AGENT_TARGET="claude-code"
|
|
DEST_LOCK="$CLAUDE_DIR/skills-lock.json"
|
|
|
|
# --- guards --------------------------------------------------------------------
|
|
if ! command -v npx >/dev/null 2>&1; then
|
|
echo "error: npx is required (install Node.js >= 20)." >&2
|
|
exit 1
|
|
fi
|
|
if ! command -v node >/dev/null 2>&1; then
|
|
echo "error: node is required to parse skills-lock.json." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# --- stage skills-lock.json into ~/.claude ------------------------------------
|
|
if [[ -n "$LOCAL_LOCK" && -f "$LOCAL_LOCK" ]]; then
|
|
echo "==> Copying local skills-lock.json -> $DEST_LOCK"
|
|
cp "$LOCAL_LOCK" "$DEST_LOCK"
|
|
else
|
|
echo "==> Fetching skills-lock.json -> $DEST_LOCK"
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl -fsSL "$LOCK_URL" -o "$DEST_LOCK"
|
|
elif command -v wget >/dev/null 2>&1; then
|
|
wget -qO "$DEST_LOCK" "$LOCK_URL"
|
|
else
|
|
echo "error: need curl or wget to fetch skills-lock.json." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# --- parse lock: write "source<TAB>skill1,skill2,..." to a tempfile -----------
|
|
GROUPS_FILE="$(mktemp -t skills-groups.XXXXXX)"
|
|
trap 'rm -f "$GROUPS_FILE"' EXIT
|
|
|
|
node --input-type=module -e "
|
|
import fs from 'node:fs';
|
|
const lock = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
|
|
const bySource = new Map();
|
|
for (const [name, entry] of Object.entries(lock.skills || {})) {
|
|
if (!entry || !entry.source) continue;
|
|
if (!bySource.has(entry.source)) bySource.set(entry.source, []);
|
|
bySource.get(entry.source).push(name);
|
|
}
|
|
const out = [...bySource.entries()]
|
|
.map(([src, names]) => src + '\t' + names.join(','))
|
|
.join('\n');
|
|
fs.writeFileSync(process.argv[2], out + (out ? '\n' : ''));
|
|
" "$DEST_LOCK" "$GROUPS_FILE"
|
|
|
|
if [[ ! -s "$GROUPS_FILE" ]]; then
|
|
echo "error: no skills found in $DEST_LOCK." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "==> Skill groups parsed from lock:"
|
|
cat "$GROUPS_FILE"
|
|
|
|
# --- install (globally, grouped by source) -------------------------------------
|
|
echo "==> Installing skills globally (~/.claude)"
|
|
# Read plan from fd 3 and redirect npx stdin to /dev/null. The skills CLI reads
|
|
# stdin even with --yes; without these guards it would consume the remaining
|
|
# lines of GROUPS_FILE and the loop would only run once (silently dropping
|
|
# every source after the first).
|
|
while IFS=$'\t' read -r SOURCE SKILLS <&3; do
|
|
[[ -z "$SOURCE" ]] && continue
|
|
echo "==> $SOURCE :: $SKILLS"
|
|
# The skills CLI expects --skill repeated per name, not a comma-joined string.
|
|
SKILL_ARGS=()
|
|
IFS=',' read -ra _NAMES <<< "$SKILLS"
|
|
for n in "${_NAMES[@]}"; do
|
|
[[ -z "$n" ]] && continue
|
|
SKILL_ARGS+=(--skill "$n")
|
|
done
|
|
npx --yes skills@latest add "$SOURCE" \
|
|
"${SKILL_ARGS[@]}" \
|
|
--agent "$AGENT_TARGET" \
|
|
--copy \
|
|
--global \
|
|
--yes < /dev/null
|
|
done 3< "$GROUPS_FILE"
|
|
|
|
echo "==> Done. Log: $LOG_FILE"
|
|
cat <<'EOF'
|
|
|
|
Next step:
|
|
Run /setup-matt-pocock-skills once per repo to record the issue tracker,
|
|
triage labels, and domain-doc layout.
|
|
EOF
|