feat(lib): vendor-skills helper, five MengTo scroll skills pinned
lib/vendor-skills.sh: vendor_pinned_skills <lock-key> [refresh], list or dict lock shapes, lock read via python argv, traversal and charset guard on lock values, VENDOR_BASE_URL honoured only as file:// (hermetic suite), per-file tmp+mv, refresh skips a skill never installed. install-plugins.sh Step 8e and update-all.sh 7.3 call it for agent-skills and mengto-skills. Vendored at a965851: scroll-world-storytelling, build-threejs-scroll-worlds (+5 references), scroll-scrubbed-visual-sequence, scroll-scrubbed-word- reveal, scroll-progress-timeline; text files only. Registered in link.sh, .gitignore, toggle-external, profile.sh and the design/web/web-full/full profiles, which also list site-motion (personal). Suite: 8 cases.
This commit is contained in:
+17
@@ -68,6 +68,11 @@ skills/frontend-design
|
|||||||
skills/ci-cd-and-automation
|
skills/ci-cd-and-automation
|
||||||
skills/deprecation-and-migration
|
skills/deprecation-and-migration
|
||||||
skills/observability-and-instrumentation
|
skills/observability-and-instrumentation
|
||||||
|
skills/scroll-world-storytelling
|
||||||
|
skills/build-threejs-scroll-worlds
|
||||||
|
skills/scroll-scrubbed-visual-sequence
|
||||||
|
skills/scroll-scrubbed-word-reveal
|
||||||
|
skills/scroll-progress-timeline
|
||||||
|
|
||||||
# Impeccable — NOT a symlink: `impeccable skills install --scope=global`
|
# Impeccable — NOT a symlink: `impeccable skills install --scope=global`
|
||||||
# writes the skill dir (and its ~15 MB engine binary) straight in through the
|
# writes the skill dir (and its ~15 MB engine binary) straight in through the
|
||||||
@@ -189,6 +194,18 @@ skills-external/observability-and-instrumentation/
|
|||||||
skills-external/deprecation-and-migration/
|
skills-external/deprecation-and-migration/
|
||||||
skills-external/ci-cd-and-automation/
|
skills-external/ci-cd-and-automation/
|
||||||
|
|
||||||
|
# Mengto scroll-choreography skills (MengTo/Skills) — machine-owned,
|
||||||
|
# curl'd at the commit pinned in plugins.lock.json ("mengto-skills" entry)
|
||||||
|
# by install-plugins.sh Step 8e (when absent) and re-fetched at the SAME
|
||||||
|
# commit by update-all.sh, through the shared lib/vendor-skills.sh helper.
|
||||||
|
# Not vendored: this is a pin, not a tracked snapshot — bump the commit
|
||||||
|
# deliberately to pick up an upstream edit.
|
||||||
|
skills-external/scroll-world-storytelling/
|
||||||
|
skills-external/build-threejs-scroll-worlds/
|
||||||
|
skills-external/scroll-scrubbed-visual-sequence/
|
||||||
|
skills-external/scroll-scrubbed-word-reveal/
|
||||||
|
skills-external/scroll-progress-timeline/
|
||||||
|
|
||||||
# 21st.dev skill pack — machine-owned: `21st skills install` output, staged by
|
# 21st.dev skill pack — machine-owned: `21st skills install` output, staged by
|
||||||
# install-plugins.sh Step 8.7 (the installer refuses to write through the
|
# install-plugins.sh Step 8.7 (the installer refuses to write through the
|
||||||
# ~/.claude/skills symlink, so it runs under a throwaway HOME and the skills
|
# ~/.claude/skills symlink, so it runs under a throwaway HOME and the skills
|
||||||
|
|||||||
+24
-50
@@ -90,22 +90,6 @@ print(v)
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Read a pinned commit sha from plugins.lock.json (agent-skills style entries
|
|
||||||
# — no "version", a "commit" field instead). Prints the sha, or "" if the
|
|
||||||
# entry or the field is absent.
|
|
||||||
# Usage: pinned_commit "agent-skills" → prints the commit sha or ""
|
|
||||||
pinned_commit() {
|
|
||||||
local key="$1"
|
|
||||||
if [ -f "$REPO/plugins.lock.json" ] && command -v python3 &>/dev/null; then
|
|
||||||
python3 -c "
|
|
||||||
import json, sys
|
|
||||||
with open(sys.argv[1]) as f:
|
|
||||||
d = json.load(f)
|
|
||||||
print(d.get(sys.argv[2], {}).get('commit', ''))
|
|
||||||
" "$REPO/plugins.lock.json" "$key" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# DETECT OS
|
# DETECT OS
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -915,41 +899,29 @@ else
|
|||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── Step 8e: Agent Skills (addyosmani/agent-skills, pinned commit) ──
|
# ── Step 8e: Agent Skills (addyosmani/agent-skills) + Mengto scroll
|
||||||
# Three dev-lifecycle skills vendored the emil-design-eng way (curl →
|
# skills (MengTo/Skills) — both commit-pinned, vendored the emil-design-eng
|
||||||
# skills-external/<name>/SKILL.md, symlinked by link.sh) but COMMIT-pinned
|
# way (curl → skills-external/<name>/, symlinked by link.sh) through the
|
||||||
# instead of tracking main: the sha lives in plugins.lock.json ("agent-skills"
|
# shared lib/vendor-skills.sh helper. Shas/paths/file-lists live in
|
||||||
# entry), never hardcoded here.
|
# plugins.lock.json ("agent-skills" / "mengto-skills" entries), never
|
||||||
echo "── Step 8e: Agent Skills (addyosmani/agent-skills) ─────────"
|
# hardcoded here.
|
||||||
|
echo "── Step 8e: Agent Skills + Mengto scroll skills (pinned commit) ──"
|
||||||
echo ""
|
echo ""
|
||||||
AGENT_SKILLS_NAMES=(observability-and-instrumentation deprecation-and-migration ci-cd-and-automation)
|
# shellcheck source=lib/vendor-skills.sh disable=SC1091
|
||||||
AGENT_SKILLS_SHA=$(pinned_commit "agent-skills")
|
source "$REPO/lib/vendor-skills.sh"
|
||||||
if [ -z "$AGENT_SKILLS_SHA" ]; then
|
EXT_SKILL_NAMES=(observability-and-instrumentation deprecation-and-migration
|
||||||
err "agent-skills: no commit pinned in plugins.lock.json — add an \"agent-skills\" entry with a \"commit\" field"
|
ci-cd-and-automation scroll-world-storytelling build-threejs-scroll-worlds
|
||||||
else
|
scroll-scrubbed-visual-sequence scroll-scrubbed-word-reveal
|
||||||
for _as_skill in "${AGENT_SKILLS_NAMES[@]}"; do
|
scroll-progress-timeline)
|
||||||
_as_dir="$REPO/skills-external/$_as_skill"
|
vendor_pinned_skills agent-skills
|
||||||
_as_url="https://raw.githubusercontent.com/addyosmani/agent-skills/$AGENT_SKILLS_SHA/skills/$_as_skill/SKILL.md"
|
vendor_pinned_skills mengto-skills
|
||||||
mkdir -p "$_as_dir"
|
for _ext_skill in "${EXT_SKILL_NAMES[@]}"; do
|
||||||
if [ -f "$_as_dir/SKILL.md" ]; then
|
if [ -L "$HOME/.claude/skills/$_ext_skill" ]; then
|
||||||
ok "$_as_skill already downloaded"
|
ok "$_ext_skill symlink OK"
|
||||||
else
|
else
|
||||||
info "Downloading SKILL.md from addyosmani/agent-skills ($_as_skill)..."
|
info "Symlinking $_ext_skill — will be created by link.sh"
|
||||||
if curl -fsSL "$_as_url" -o "$_as_dir/SKILL.md.tmp" \
|
fi
|
||||||
&& mv "$_as_dir/SKILL.md.tmp" "$_as_dir/SKILL.md"; then
|
done
|
||||||
ok "$_as_skill installed"
|
|
||||||
else
|
|
||||||
rm -f "$_as_dir/SKILL.md.tmp"
|
|
||||||
err "$_as_skill download failed — try: curl -fsSL $_as_url -o $_as_dir/SKILL.md"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ -L "$HOME/.claude/skills/$_as_skill" ]; then
|
|
||||||
ok "$_as_skill symlink OK"
|
|
||||||
else
|
|
||||||
info "Symlinking $_as_skill — will be created by link.sh"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -1240,6 +1212,7 @@ echo " 🔄 frontend-design — distinctive frontend interfaces, anti-AI-
|
|||||||
echo " 🔄 impeccable — /impeccable design verbs + 45-rule deterministic detector (npx impeccable detect)"
|
echo " 🔄 impeccable — /impeccable design verbs + 45-rule deterministic detector (npx impeccable detect)"
|
||||||
echo " 🔄 design-motion-principles — motion/animation design, 3-designer lens (kylezantos)"
|
echo " 🔄 design-motion-principles — motion/animation design, 3-designer lens (kylezantos)"
|
||||||
echo " 🔄 agent-skills trio — observability-and-instrumentation, deprecation-and-migration, ci-cd-and-automation (curl → symlink, pinned commit)"
|
echo " 🔄 agent-skills trio — observability-and-instrumentation, deprecation-and-migration, ci-cd-and-automation (curl → symlink, pinned commit)"
|
||||||
|
echo " 🔄 mengto scroll skills — scroll-world-storytelling, build-threejs-scroll-worlds, scroll-scrubbed-visual-sequence, scroll-scrubbed-word-reveal, scroll-progress-timeline (curl → symlink, pinned commit)"
|
||||||
echo " 🔄 darwin-skill — autonomous skill optimizer (npx skills, ~/.agents/skills/)"
|
echo " 🔄 darwin-skill — autonomous skill optimizer (npx skills, ~/.agents/skills/)"
|
||||||
echo " 🔄 21st skill pack — 21st.dev CLI skills; design ones follow the profile (full by default), publishing ones on demand (toggle: lib/toggle-external.sh enable 21st)"
|
echo " 🔄 21st skill pack — 21st.dev CLI skills; design ones follow the profile (full by default), publishing ones on demand (toggle: lib/toggle-external.sh enable 21st)"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -1249,6 +1222,7 @@ echo " Emil Design Eng at: ~/.claude/skills/emil-design-eng/ (symlink → skill
|
|||||||
echo " Frontend Design at: ~/.claude/skills/frontend-design/ (symlink → skills-external)"
|
echo " Frontend Design at: ~/.claude/skills/frontend-design/ (symlink → skills-external)"
|
||||||
echo " Design Motion Principles at: ~/.claude/skills/design-motion-principles/ (symlink → skills-external)"
|
echo " Design Motion Principles at: ~/.claude/skills/design-motion-principles/ (symlink → skills-external)"
|
||||||
echo " Agent Skills trio at: ~/.claude/skills/{observability-and-instrumentation,deprecation-and-migration,ci-cd-and-automation}/ (symlink → skills-external)"
|
echo " Agent Skills trio at: ~/.claude/skills/{observability-and-instrumentation,deprecation-and-migration,ci-cd-and-automation}/ (symlink → skills-external)"
|
||||||
|
echo " Mengto scroll skills at: ~/.claude/skills/{scroll-world-storytelling,build-threejs-scroll-worlds,scroll-scrubbed-visual-sequence,scroll-scrubbed-word-reveal,scroll-progress-timeline}/ (symlink → skills-external)"
|
||||||
echo " npx skills at: ~/.agents/skills/ (symlinked into ~/.claude/skills/)"
|
echo " npx skills at: ~/.agents/skills/ (symlinked into ~/.claude/skills/)"
|
||||||
echo ""
|
echo ""
|
||||||
echo " → Restart Claude Code — plugins load automatically"
|
echo " → Restart Claude Code — plugins load automatically"
|
||||||
|
|||||||
+9
-1
@@ -82,6 +82,11 @@ MANAGED_EXTERNALS=(
|
|||||||
observability-and-instrumentation
|
observability-and-instrumentation
|
||||||
deprecation-and-migration
|
deprecation-and-migration
|
||||||
ci-cd-and-automation
|
ci-cd-and-automation
|
||||||
|
scroll-world-storytelling
|
||||||
|
build-threejs-scroll-worlds
|
||||||
|
scroll-scrubbed-visual-sequence
|
||||||
|
scroll-scrubbed-word-reveal
|
||||||
|
scroll-progress-timeline
|
||||||
)
|
)
|
||||||
|
|
||||||
# MCP servers that are toggle-managed by `set`, both ways (enable AND
|
# MCP servers that are toggle-managed by `set`, both ways (enable AND
|
||||||
@@ -761,7 +766,10 @@ NOTE:
|
|||||||
(emil-design-eng, frontend-design, design-motion-principles, impeccable,
|
(emil-design-eng, frontend-design, design-motion-principles, impeccable,
|
||||||
the five 21st design skills, the agent-skills trio
|
the five 21st design skills, the agent-skills trio
|
||||||
observability-and-instrumentation/deprecation-and-migration/
|
observability-and-instrumentation/deprecation-and-migration/
|
||||||
ci-cd-and-automation). Anything outside those allowlists stays
|
ci-cd-and-automation, the five Mengto scroll skills
|
||||||
|
scroll-world-storytelling/build-threejs-scroll-worlds/
|
||||||
|
scroll-scrubbed-visual-sequence/scroll-scrubbed-word-reveal/
|
||||||
|
scroll-progress-timeline). Anything outside those allowlists stays
|
||||||
advisory — run "claude plugin enable|disable" or
|
advisory — run "claude plugin enable|disable" or
|
||||||
"bash lib/toggle-external.sh enable|disable <tool>" yourself.
|
"bash lib/toggle-external.sh enable|disable <tool>" yourself.
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -31,6 +31,16 @@ frontend-design external
|
|||||||
design-motion-principles external
|
design-motion-principles external
|
||||||
impeccable external
|
impeccable external
|
||||||
|
|
||||||
|
# External: Mengto scroll-choreography skills (curl, commit-pinned)
|
||||||
|
scroll-world-storytelling external
|
||||||
|
build-threejs-scroll-worlds external
|
||||||
|
scroll-scrubbed-visual-sequence external
|
||||||
|
scroll-scrubbed-word-reveal external
|
||||||
|
scroll-progress-timeline external
|
||||||
|
|
||||||
|
# Personal: motion implementation companion (skills/site-motion)
|
||||||
|
site-motion personal
|
||||||
|
|
||||||
# External: 21st.dev pack — CLI-driven (no MCP, no API key). 21st-registry
|
# External: 21st.dev pack — CLI-driven (no MCP, no API key). 21st-registry
|
||||||
# and 21st-design-sync are publishing flows; installed but left parked.
|
# and 21st-design-sync are publishing flows; installed but left parked.
|
||||||
21st-ui-build external
|
21st-ui-build external
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ impeccable external
|
|||||||
observability-and-instrumentation external
|
observability-and-instrumentation external
|
||||||
deprecation-and-migration external
|
deprecation-and-migration external
|
||||||
ci-cd-and-automation external
|
ci-cd-and-automation external
|
||||||
|
scroll-world-storytelling external
|
||||||
|
build-threejs-scroll-worlds external
|
||||||
|
scroll-scrubbed-visual-sequence external
|
||||||
|
scroll-scrubbed-word-reveal external
|
||||||
|
scroll-progress-timeline external
|
||||||
ui-ux-pro-max plugin@ui-ux-pro-max-skill
|
ui-ux-pro-max plugin@ui-ux-pro-max-skill
|
||||||
# pr-review-toolkit REMOVED from full (audit 2026-07-02 #12): heaviest
|
# pr-review-toolkit REMOVED from full (audit 2026-07-02 #12): heaviest
|
||||||
# single plugin cost (~2.2k tokens of agent descriptions/session), useful
|
# single plugin cost (~2.2k tokens of agent descriptions/session), useful
|
||||||
@@ -99,6 +104,9 @@ ui-ux-pro-max plugin@ui-ux-pro-max-skill
|
|||||||
21st-cli-use external
|
21st-cli-use external
|
||||||
21st-ai external
|
21st-ai external
|
||||||
|
|
||||||
|
# Personal: motion implementation companion (skills/site-motion)
|
||||||
|
site-motion personal
|
||||||
|
|
||||||
# === CLIs (advisory) =================================================
|
# === CLIs (advisory) =================================================
|
||||||
21st cli
|
21st cli
|
||||||
ctx7 cli
|
ctx7 cli
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ emil-design-eng external
|
|||||||
frontend-design external
|
frontend-design external
|
||||||
design-motion-principles external
|
design-motion-principles external
|
||||||
impeccable external
|
impeccable external
|
||||||
|
scroll-world-storytelling external
|
||||||
|
build-threejs-scroll-worlds external
|
||||||
|
scroll-scrubbed-visual-sequence external
|
||||||
|
scroll-scrubbed-word-reveal external
|
||||||
|
scroll-progress-timeline external
|
||||||
21st-ui-build external
|
21st-ui-build external
|
||||||
21st-ui-explore external
|
21st-ui-explore external
|
||||||
21st-ui-review external
|
21st-ui-review external
|
||||||
@@ -56,6 +61,9 @@ impeccable external
|
|||||||
21st-ai external
|
21st-ai external
|
||||||
ui-ux-pro-max plugin@ui-ux-pro-max-skill
|
ui-ux-pro-max plugin@ui-ux-pro-max-skill
|
||||||
|
|
||||||
|
# Personal: motion implementation companion (skills/site-motion)
|
||||||
|
site-motion personal
|
||||||
|
|
||||||
# === CLIs ============================================================
|
# === CLIs ============================================================
|
||||||
21st cli
|
21st cli
|
||||||
ctx7 cli
|
ctx7 cli
|
||||||
|
|||||||
@@ -38,6 +38,16 @@ frontend-design external
|
|||||||
design-motion-principles external
|
design-motion-principles external
|
||||||
impeccable external
|
impeccable external
|
||||||
|
|
||||||
|
# External: Mengto scroll-choreography skills (curl, commit-pinned)
|
||||||
|
scroll-world-storytelling external
|
||||||
|
build-threejs-scroll-worlds external
|
||||||
|
scroll-scrubbed-visual-sequence external
|
||||||
|
scroll-scrubbed-word-reveal external
|
||||||
|
scroll-progress-timeline external
|
||||||
|
|
||||||
|
# Personal: motion implementation companion (skills/site-motion)
|
||||||
|
site-motion personal
|
||||||
|
|
||||||
# External: 21st.dev pack (publishing flows 21st-registry / -design-sync
|
# External: 21st.dev pack (publishing flows 21st-registry / -design-sync
|
||||||
# stay parked)
|
# stay parked)
|
||||||
21st-ui-build external
|
21st-ui-build external
|
||||||
|
|||||||
Executable
+144
@@ -0,0 +1,144 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# lib/tests/vendor-skills.test.sh — lib/vendor-skills.sh's vendor_pinned_skills():
|
||||||
|
# list-shape and dict-shape lock entries, a file:// VENDOR_BASE_URL fixture
|
||||||
|
# tree (VENDOR_SKILLS_REPO_OVERRIDE points skills-external/ + the lock at a
|
||||||
|
# throwaway repo), tmp+mv semantics (a missing upstream file leaves no dest
|
||||||
|
# and no tmp), skip-when-present, refresh overwriting a stale copy, a
|
||||||
|
# non-file:// VENDOR_BASE_URL override being ignored (warn, default URL),
|
||||||
|
# and a "../evil" lock file being rejected before any fetch.
|
||||||
|
set -u
|
||||||
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
LIB="$ROOT/lib/vendor-skills.sh"
|
||||||
|
pass=0; fail=0
|
||||||
|
|
||||||
|
check_bool() {
|
||||||
|
local name="$1" ok="$2"
|
||||||
|
if [ "$ok" = 1 ]; then pass=$((pass+1)); echo "PASS $name"
|
||||||
|
else fail=$((fail+1)); echo "FAIL $name"; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
|
||||||
|
FIXTURE_REPO="$WORK/repo"
|
||||||
|
UPSTREAM="$WORK/upstream"
|
||||||
|
mkdir -p "$FIXTURE_REPO/skills-external"
|
||||||
|
|
||||||
|
# Lock: "list-key" mirrors the agent-skills bare-list shape (file defaults
|
||||||
|
# to SKILL.md, path defaults to "skills"). "dict-key" mirrors the
|
||||||
|
# mengto-skills explicit shape (a references/ file, an explicit path).
|
||||||
|
# "fail-key" names a file that is never placed in $UPSTREAM. "missing-key"
|
||||||
|
# names a skill never installed locally (no skills-external/ dir), to
|
||||||
|
# prove refresh skips it instead of installing it. "traversal-key" names
|
||||||
|
# a well-behaved SKILL.md alongside a "../evil" file, to prove the whole
|
||||||
|
# key is rejected before either one is fetched.
|
||||||
|
cat > "$FIXTURE_REPO/plugins.lock.json" <<'JSON'
|
||||||
|
{
|
||||||
|
"list-key": {
|
||||||
|
"source": "https://github.com/acme/list-repo",
|
||||||
|
"commit": "abc123",
|
||||||
|
"skills": ["skill-list-a"]
|
||||||
|
},
|
||||||
|
"dict-key": {
|
||||||
|
"source": "https://github.com/acme/dict-repo",
|
||||||
|
"commit": "def456",
|
||||||
|
"path": "somewhere/nested",
|
||||||
|
"skills": {"skill-dict-a": ["SKILL.md", "references/notes.md"]}
|
||||||
|
},
|
||||||
|
"fail-key": {
|
||||||
|
"source": "https://github.com/acme/fail-repo",
|
||||||
|
"commit": "789fail",
|
||||||
|
"skills": ["skill-fail-a"]
|
||||||
|
},
|
||||||
|
"missing-key": {
|
||||||
|
"source": "https://github.com/acme/missing-repo",
|
||||||
|
"commit": "111missing",
|
||||||
|
"skills": ["skill-missing-a"]
|
||||||
|
},
|
||||||
|
"traversal-key": {
|
||||||
|
"source": "https://github.com/acme/traversal-repo",
|
||||||
|
"commit": "222trav",
|
||||||
|
"skills": {"skill-trav-a": ["SKILL.md", "../evil"]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
mkdir -p "$UPSTREAM/abc123/skills/skill-list-a"
|
||||||
|
echo v1 > "$UPSTREAM/abc123/skills/skill-list-a/SKILL.md"
|
||||||
|
mkdir -p "$UPSTREAM/def456/somewhere/nested/skill-dict-a/references"
|
||||||
|
echo "dict skill" > "$UPSTREAM/def456/somewhere/nested/skill-dict-a/SKILL.md"
|
||||||
|
echo "dict notes" > "$UPSTREAM/def456/somewhere/nested/skill-dict-a/references/notes.md"
|
||||||
|
# fail-key: 789fail/skills/skill-fail-a/SKILL.md deliberately absent.
|
||||||
|
# missing-key: no $UPSTREAM tree at all — refresh must skip it on the
|
||||||
|
# missing skills-external/ dir alone, before ever reaching curl.
|
||||||
|
# traversal-key: no $UPSTREAM tree either — the "../evil" file must be
|
||||||
|
# rejected by the lock reader itself, before any URL is built.
|
||||||
|
|
||||||
|
export VENDOR_SKILLS_REPO_OVERRIDE="$FIXTURE_REPO"
|
||||||
|
export VENDOR_BASE_URL="file://$UPSTREAM"
|
||||||
|
# shellcheck source=../vendor-skills.sh disable=SC1091
|
||||||
|
source "$LIB"
|
||||||
|
|
||||||
|
# ── LIST_SHAPE ────────────────────────────────────────────────────────────
|
||||||
|
vendor_pinned_skills list-key >/dev/null 2>&1
|
||||||
|
dest="$FIXTURE_REPO/skills-external/skill-list-a/SKILL.md"
|
||||||
|
check_bool LIST_SHAPE \
|
||||||
|
"$([ -f "$dest" ] && [ "$(cat "$dest")" = v1 ] && echo 1 || echo 0)"
|
||||||
|
|
||||||
|
# ── DICT_SHAPE ────────────────────────────────────────────────────────────
|
||||||
|
vendor_pinned_skills dict-key >/dev/null 2>&1
|
||||||
|
d1="$FIXTURE_REPO/skills-external/skill-dict-a/SKILL.md"
|
||||||
|
d2="$FIXTURE_REPO/skills-external/skill-dict-a/references/notes.md"
|
||||||
|
check_bool DICT_SHAPE \
|
||||||
|
"$([ -f "$d1" ] && [ "$(cat "$d2")" = "dict notes" ] && echo 1 || echo 0)"
|
||||||
|
|
||||||
|
# ── FAIL_LEAVES_NOTHING ───────────────────────────────────────────────────
|
||||||
|
out="$(vendor_pinned_skills fail-key 2>&1)"
|
||||||
|
fdest="$FIXTURE_REPO/skills-external/skill-fail-a/SKILL.md"
|
||||||
|
check_bool FAIL_LEAVES_NOTHING "$([ ! -e "$fdest" ] && [ ! -e "$fdest.tmp" ] \
|
||||||
|
&& printf '%s' "$out" | grep -q 'not all files landed' && echo 1 || echo 0)"
|
||||||
|
|
||||||
|
# ── SKIP_PRESENT — upstream changes, a plain re-run keeps the old copy ───
|
||||||
|
echo v2-upstream-changed > "$UPSTREAM/abc123/skills/skill-list-a/SKILL.md"
|
||||||
|
vendor_pinned_skills list-key >/dev/null 2>&1
|
||||||
|
check_bool SKIP_PRESENT "$([ "$(cat "$dest")" = v1 ] && echo 1 || echo 0)"
|
||||||
|
|
||||||
|
# ── REFRESH_OVERWRITES — same changed upstream, refresh picks it up ─────
|
||||||
|
vendor_pinned_skills list-key refresh >/dev/null 2>&1
|
||||||
|
check_bool REFRESH_OVERWRITES \
|
||||||
|
"$([ "$(cat "$dest")" = v2-upstream-changed ] && echo 1 || echo 0)"
|
||||||
|
|
||||||
|
# ── REFRESH_SKIPS_MISSING — refresh never installs a skill that has no
|
||||||
|
# skills-external/<name> dir yet; it prints the standard "not installed"
|
||||||
|
# skip line and never touches curl (no $UPSTREAM/111missing/ exists).
|
||||||
|
mdest="$FIXTURE_REPO/skills-external/skill-missing-a"
|
||||||
|
out="$(vendor_pinned_skills missing-key refresh 2>&1)"
|
||||||
|
check_bool REFRESH_SKIPS_MISSING "$([ ! -e "$mdest" ] \
|
||||||
|
&& printf '%s' "$out" | \
|
||||||
|
grep -qF 'skill-missing-a not installed — skipping (run: make plugin)' \
|
||||||
|
&& echo 1 || echo 0)"
|
||||||
|
|
||||||
|
# ── OVERRIDE_NON_FILE_IGNORED — a non-file:// VENDOR_BASE_URL is ignored:
|
||||||
|
# a warn names the variable and the default raw.githubusercontent.com
|
||||||
|
# prefix is used instead. list-key's SKILL.md is already vendored (v2,
|
||||||
|
# from REFRESH_OVERWRITES above), so this probe never touches curl
|
||||||
|
# either way — the assertion is the warn line, and that the file:// mode
|
||||||
|
# used everywhere else in this suite (asserted by the six cases above
|
||||||
|
# and below) keeps working.
|
||||||
|
out="$(VENDOR_BASE_URL="https://evil.example.com" \
|
||||||
|
vendor_pinned_skills list-key 2>&1)"
|
||||||
|
check_bool OVERRIDE_NON_FILE_IGNORED \
|
||||||
|
"$(printf '%s' "$out" | grep -q 'VENDOR_BASE_URL ignored' \
|
||||||
|
&& echo 1 || echo 0)"
|
||||||
|
|
||||||
|
# ── REJECTS_TRAVERSAL — a lock entry naming a "../evil" file is rejected
|
||||||
|
# whole by the lock reader: nothing is fetched for the key, so not even
|
||||||
|
# its well-behaved SKILL.md lands, and nothing lands outside the skill's
|
||||||
|
# own directory either.
|
||||||
|
out="$(vendor_pinned_skills traversal-key 2>&1)"
|
||||||
|
rc=$?
|
||||||
|
tdir="$FIXTURE_REPO/skills-external/skill-trav-a"
|
||||||
|
outside="$FIXTURE_REPO/skills-external/evil"
|
||||||
|
check_bool REJECTS_TRAVERSAL "$([ "$rc" -ne 0 ] && [ ! -e "$tdir" ] \
|
||||||
|
&& [ ! -e "$outside" ] && echo 1 || echo 0)"
|
||||||
|
|
||||||
|
echo "PASS=$pass FAIL=$fail"
|
||||||
|
[ "$fail" -eq 0 ]
|
||||||
+16
-4
@@ -24,6 +24,9 @@
|
|||||||
# observability-and-instrumentation, deprecation-and-migration,
|
# observability-and-instrumentation, deprecation-and-migration,
|
||||||
# ci-cd-and-automation — the agent-skills trio, same single-symlink shape
|
# ci-cd-and-automation — the agent-skills trio, same single-symlink shape
|
||||||
# as emil-design-eng (commit-pinned instead of main-branch tracking)
|
# as emil-design-eng (commit-pinned instead of main-branch tracking)
|
||||||
|
# scroll-world-storytelling, build-threejs-scroll-worlds,
|
||||||
|
# scroll-scrubbed-visual-sequence, scroll-scrubbed-word-reveal,
|
||||||
|
# scroll-progress-timeline — the Mengto scroll skills, same shape
|
||||||
#
|
#
|
||||||
# For fine-grained activation (only design skills, only qa skills, only
|
# For fine-grained activation (only design skills, only qa skills, only
|
||||||
# audit skills, etc.) instead of all-or-nothing gstack toggling, use:
|
# audit skills, etc.) instead of all-or-nothing gstack toggling, use:
|
||||||
@@ -44,7 +47,10 @@ err() { echo -e "${RED}✗${NC} $1"; }
|
|||||||
|
|
||||||
# All non-plugin tools this script can toggle.
|
# All non-plugin tools this script can toggle.
|
||||||
MANAGED_TOOLS=(gstack emil-design-eng darwin-skill 21st
|
MANAGED_TOOLS=(gstack emil-design-eng darwin-skill 21st
|
||||||
observability-and-instrumentation deprecation-and-migration ci-cd-and-automation)
|
observability-and-instrumentation deprecation-and-migration ci-cd-and-automation
|
||||||
|
scroll-world-storytelling build-threejs-scroll-worlds
|
||||||
|
scroll-scrubbed-visual-sequence scroll-scrubbed-word-reveal
|
||||||
|
scroll-progress-timeline)
|
||||||
|
|
||||||
# Prints the skill names that belong to the "21st" pack. Source of truth:
|
# Prints the skill names that belong to the "21st" pack. Source of truth:
|
||||||
# skills-external/21st-* — the `21st skills install` run in install-plugins.sh
|
# skills-external/21st-* — the `21st skills install` run in install-plugins.sh
|
||||||
@@ -80,7 +86,9 @@ status_tool() {
|
|||||||
done < <(gstack_skills)
|
done < <(gstack_skills)
|
||||||
echo "disabled"
|
echo "disabled"
|
||||||
;;
|
;;
|
||||||
emil-design-eng|observability-and-instrumentation|deprecation-and-migration|ci-cd-and-automation)
|
emil-design-eng|observability-and-instrumentation|deprecation-and-migration|ci-cd-and-automation| \
|
||||||
|
scroll-world-storytelling|build-threejs-scroll-worlds|scroll-scrubbed-visual-sequence| \
|
||||||
|
scroll-scrubbed-word-reveal|scroll-progress-timeline)
|
||||||
[ -d "$REPO/skills-external/$tool" ] || { echo "missing"; return; }
|
[ -d "$REPO/skills-external/$tool" ] || { echo "missing"; return; }
|
||||||
[ -e "$SKILLS_DIR/$tool" ] && echo "enabled" || echo "disabled"
|
[ -e "$SKILLS_DIR/$tool" ] && echo "enabled" || echo "disabled"
|
||||||
;;
|
;;
|
||||||
@@ -119,7 +127,9 @@ disable_tool() {
|
|||||||
done < <(gstack_skills)
|
done < <(gstack_skills)
|
||||||
ok "gstack disabled ($moved symlinks moved)"
|
ok "gstack disabled ($moved symlinks moved)"
|
||||||
;;
|
;;
|
||||||
emil-design-eng|darwin-skill|observability-and-instrumentation|deprecation-and-migration|ci-cd-and-automation)
|
emil-design-eng|darwin-skill|observability-and-instrumentation|deprecation-and-migration| \
|
||||||
|
ci-cd-and-automation|scroll-world-storytelling|build-threejs-scroll-worlds| \
|
||||||
|
scroll-scrubbed-visual-sequence|scroll-scrubbed-word-reveal|scroll-progress-timeline)
|
||||||
if [ -e "$SKILLS_DIR/$tool" ]; then
|
if [ -e "$SKILLS_DIR/$tool" ]; then
|
||||||
rm -rf "${DISABLED_DIR:?}/${tool:?}"
|
rm -rf "${DISABLED_DIR:?}/${tool:?}"
|
||||||
mv "$SKILLS_DIR/$tool" "$DISABLED_DIR/$tool"
|
mv "$SKILLS_DIR/$tool" "$DISABLED_DIR/$tool"
|
||||||
@@ -169,7 +179,9 @@ enable_tool() {
|
|||||||
ok "gstack enabled ($moved symlinks restored)"
|
ok "gstack enabled ($moved symlinks restored)"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
emil-design-eng|darwin-skill|observability-and-instrumentation|deprecation-and-migration|ci-cd-and-automation)
|
emil-design-eng|darwin-skill|observability-and-instrumentation|deprecation-and-migration| \
|
||||||
|
ci-cd-and-automation|scroll-world-storytelling|build-threejs-scroll-worlds| \
|
||||||
|
scroll-scrubbed-visual-sequence|scroll-scrubbed-word-reveal|scroll-progress-timeline)
|
||||||
local src
|
local src
|
||||||
case "$tool" in
|
case "$tool" in
|
||||||
darwin-skill) src="$HOME/.agents/skills/$tool" ;;
|
darwin-skill) src="$HOME/.agents/skills/$tool" ;;
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================
|
||||||
|
# lib/vendor-skills.sh — shared curl-vendoring for commit-pinned skills
|
||||||
|
#
|
||||||
|
# One helper, `vendor_pinned_skills <lock-key> [refresh]`, replaces the
|
||||||
|
# inline curl loops install-plugins.sh (Step 8e) and update-all.sh (7.3)
|
||||||
|
# used to carry separately for the addyosmani/agent-skills trio. Both
|
||||||
|
# scripts source this file and call it once per plugins.lock.json entry
|
||||||
|
# ("agent-skills", "mengto-skills", …) — no sha ever hardcoded here.
|
||||||
|
#
|
||||||
|
# Lock entry shape (plugins.lock.json):
|
||||||
|
# "<key>": {
|
||||||
|
# "source": "https://github.com/<owner>/<repo>",
|
||||||
|
# "commit": "<sha>",
|
||||||
|
# "path": "<repo-relative dir holding the skill folders>", # optional
|
||||||
|
# "skills": ["<name>", ...] | {"<name>": ["<file>", ...], ...}
|
||||||
|
# }
|
||||||
|
# `skills` as a bare list defaults every named skill to `["SKILL.md"]` and
|
||||||
|
# `path` to "skills" (the agent-skills shape); `skills` as a dict carries an
|
||||||
|
# explicit per-skill file list (references/*, etc.) and `path` is required.
|
||||||
|
#
|
||||||
|
# Raw URL: https://raw.githubusercontent.com/<owner>/<repo>/<sha>/<path>/
|
||||||
|
# <skill>/<file>. VENDOR_BASE_URL overrides the "https://…/<repo>" prefix
|
||||||
|
# (everything before "/<sha>/…") ONLY when it starts with "file://" (the
|
||||||
|
# hermetic suite's fixture form); any other non-empty value is ignored —
|
||||||
|
# a warn names the variable and the default raw.githubusercontent.com
|
||||||
|
# prefix is used, so a stray value in the caller's environment can never
|
||||||
|
# silently redirect a real install/update run.
|
||||||
|
#
|
||||||
|
# Per file: tmp + mv, tmp removed on failure. A file already at its
|
||||||
|
# destination is skipped unless refresh="refresh". A skill counts as
|
||||||
|
# vendored only when every one of its listed files landed; otherwise err
|
||||||
|
# names the file and the manual curl to retry it.
|
||||||
|
#
|
||||||
|
# Every skill name and file path from the lock is rejected — before any
|
||||||
|
# URL is built or any file fetched — if it contains "..", starts with
|
||||||
|
# "/", or holds a character outside [A-Za-z0-9._/-]; this guards against
|
||||||
|
# a lock entry walking a fetch outside skills-external/<skill>/.
|
||||||
|
#
|
||||||
|
# No `set -euo pipefail` here (mirrors lib/detect-plugins.sh): a sourced
|
||||||
|
# lib must not change the caller's shell options.
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
VENDOR_REPO="${VENDOR_SKILLS_REPO_OVERRIDE:-$(cd -P \
|
||||||
|
"$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||||
|
|
||||||
|
# Fallback color helpers when sourced standalone (e.g. the test suite) —
|
||||||
|
# skip anything the caller (install-plugins.sh / update-all.sh) already
|
||||||
|
# defines, so the same ok/warn/info/err instances keep being used.
|
||||||
|
if ! declare -F ok >/dev/null 2>&1; then
|
||||||
|
GREEN='\033[0;32m'; NC='\033[0m'
|
||||||
|
ok() { echo -e "${GREEN}✓${NC} $1"; }
|
||||||
|
fi
|
||||||
|
if ! declare -F info >/dev/null 2>&1; then
|
||||||
|
BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
|
info() { echo -e "${BLUE}→${NC} $1"; }
|
||||||
|
fi
|
||||||
|
if ! declare -F warn >/dev/null 2>&1; then
|
||||||
|
YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
|
warn() { echo -e "${YELLOW}⚠${NC} $1"; }
|
||||||
|
fi
|
||||||
|
if ! declare -F err >/dev/null 2>&1; then
|
||||||
|
RED='\033[0;31m'; NC='\033[0m'
|
||||||
|
err() { echo -e "${RED}✗${NC} $1"; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
# _vendor_read_lock <lockfile> <key> <base_override> — prints, on
|
||||||
|
# success: line 1: "<url_base>" (the raw-file URL up to and including
|
||||||
|
# <path>, using <base_override> when non-empty) line 2: "<sha>" then one
|
||||||
|
# "<skill>\t<file1> <file2> …" line per skill (sorted). <base_override>
|
||||||
|
# is a plain argv string — the caller (vendor_pinned_skills) already
|
||||||
|
# checked it is either empty or a "file://" value, never the raw
|
||||||
|
# VENDOR_BASE_URL.
|
||||||
|
# rc 1 when the key, its commit or its skills are absent (nothing
|
||||||
|
# printed), or when a skill name or file path fails the traversal/
|
||||||
|
# character check (prints one "INVALID\t<offending value>" line, nothing
|
||||||
|
# else — no URL is built and no file is fetched for that lock key).
|
||||||
|
# Reads the lock path, key and base override via argv — never
|
||||||
|
# string-spliced into the script.
|
||||||
|
_vendor_read_lock() {
|
||||||
|
python3 - "$1" "$2" "$3" <<'PY'
|
||||||
|
import json, re, sys
|
||||||
|
|
||||||
|
SAFE = re.compile(r'^[A-Za-z0-9._/-]+$')
|
||||||
|
|
||||||
|
|
||||||
|
def unsafe(value):
|
||||||
|
return ".." in value or value.startswith("/") or not SAFE.match(value)
|
||||||
|
|
||||||
|
|
||||||
|
lockfile, key, base_override = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||||
|
with open(lockfile) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
entry = data.get(key, {})
|
||||||
|
sha = entry.get("commit", "")
|
||||||
|
owner_repo = entry.get("source", "").rstrip("/").rsplit("github.com/", 1)[-1]
|
||||||
|
path = entry.get("path", "skills")
|
||||||
|
skills = entry.get("skills", {})
|
||||||
|
if isinstance(skills, list):
|
||||||
|
skills = {name: ["SKILL.md"] for name in skills}
|
||||||
|
if not sha or not owner_repo or not skills:
|
||||||
|
sys.exit(1)
|
||||||
|
for name, files in skills.items():
|
||||||
|
if unsafe(name):
|
||||||
|
print(f"INVALID\t{name}")
|
||||||
|
sys.exit(1)
|
||||||
|
for file in files:
|
||||||
|
if unsafe(file):
|
||||||
|
print(f"INVALID\t{file}")
|
||||||
|
sys.exit(1)
|
||||||
|
base = base_override or f"https://raw.githubusercontent.com/{owner_repo}"
|
||||||
|
print(f"{base}/{sha}/{path}")
|
||||||
|
print(sha)
|
||||||
|
for name in sorted(skills):
|
||||||
|
print(f"{name}\t{' '.join(skills[name])}")
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
# _vendor_fetch_file <url> <dest> <refresh> — tmp + mv; tmp removed on
|
||||||
|
# failure. Skips an existing dest unless refresh="refresh". rc 0 on
|
||||||
|
# success or skip, rc 1 (with an err naming the manual curl) on failure.
|
||||||
|
_vendor_fetch_file() {
|
||||||
|
local url="$1" dest="$2" refresh="$3"
|
||||||
|
[ -f "$dest" ] && [ "$refresh" != "refresh" ] && return 0
|
||||||
|
mkdir -p "$(dirname "$dest")"
|
||||||
|
local tmp="$dest.tmp"
|
||||||
|
if curl -fsSL "$url" -o "$tmp" 2>/dev/null && mv "$tmp" "$dest"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
rm -f "$tmp"
|
||||||
|
err "$dest download failed — try: curl -fsSL $url -o $dest"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# _vendor_install_skill <url_base> <skill> <files> <dest_root> <refresh> —
|
||||||
|
# fetches every listed file under <dest_root>/<skill>/, from
|
||||||
|
# <url_base>/<skill>/<file>. rc 0 only when all of them landed (existing
|
||||||
|
# or freshly fetched).
|
||||||
|
_vendor_install_skill() {
|
||||||
|
local url_base="$1" skill="$2" files="$3" dest_root="$4" refresh="$5"
|
||||||
|
local file landed=0 total=0
|
||||||
|
for file in $files; do
|
||||||
|
total=$((total + 1))
|
||||||
|
_vendor_fetch_file "$url_base/$skill/$file" "$dest_root/$skill/$file" \
|
||||||
|
"$refresh" && landed=$((landed + 1))
|
||||||
|
done
|
||||||
|
[ "$landed" -eq "$total" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# _vendor_report_lock_error <lock_key> <lock_out> — turns a failed
|
||||||
|
# _vendor_read_lock into the right err line: a rejected name/path when
|
||||||
|
# <lock_out> carries the "INVALID\t<value>" marker, the generic
|
||||||
|
# no-commit-pinned hint otherwise.
|
||||||
|
_vendor_report_lock_error() {
|
||||||
|
local lock_key="$1" lock_out="$2" msg
|
||||||
|
if [[ "$lock_out" == INVALID$'\t'* ]]; then
|
||||||
|
msg="$lock_key: rejected '${lock_out#INVALID$'\t'}'"
|
||||||
|
msg="$msg — path traversal or disallowed characters"
|
||||||
|
err "$msg"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
local hint="add an entry with a \"commit\" field"
|
||||||
|
err "$lock_key: no commit/skills pinned in plugins.lock.json — $hint"
|
||||||
|
}
|
||||||
|
|
||||||
|
# vendor_pinned_skills <lock-key> [refresh] — vendors every skill listed
|
||||||
|
# under plugins.lock.json's <lock-key> entry into skills-external/<name>/.
|
||||||
|
# Pass "refresh" as the second arg to re-fetch files already present (at
|
||||||
|
# the same pinned commit — never advances the pin). In refresh mode, a
|
||||||
|
# skill whose skills-external/<name>/ directory does not exist yet is
|
||||||
|
# skipped (the standard "not installed — skipping" info line, no fetch) —
|
||||||
|
# refresh keeps installed skills current, it never installs a new one;
|
||||||
|
# install mode (no refresh) still creates it.
|
||||||
|
vendor_pinned_skills() {
|
||||||
|
local lock_key="$1" refresh="${2:-}"
|
||||||
|
local lockfile="$VENDOR_REPO/plugins.lock.json" lock_out lock_rc
|
||||||
|
local base_override="${VENDOR_BASE_URL:-}"
|
||||||
|
if [ -n "$base_override" ] && [[ "$base_override" != file://* ]]; then
|
||||||
|
warn "VENDOR_BASE_URL ignored (must start with file://): $base_override"
|
||||||
|
base_override=""
|
||||||
|
fi
|
||||||
|
lock_out="$(_vendor_read_lock "$lockfile" "$lock_key" "$base_override")"
|
||||||
|
lock_rc=$?
|
||||||
|
if [ "$lock_rc" -ne 0 ]; then
|
||||||
|
_vendor_report_lock_error "$lock_key" "$lock_out"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local url_base sha dest_root="$VENDOR_REPO/skills-external"
|
||||||
|
url_base="$(sed -n '1p' <<<"$lock_out")"
|
||||||
|
sha="$(sed -n '2p' <<<"$lock_out")"
|
||||||
|
local skill files
|
||||||
|
while IFS=$'\t' read -r skill files; do
|
||||||
|
[ -n "$skill" ] || continue
|
||||||
|
if [ "$refresh" = "refresh" ] && [ ! -d "$dest_root/$skill" ]; then
|
||||||
|
info "$skill not installed — skipping (run: make plugin)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if _vendor_install_skill "$url_base" "$skill" "$files" \
|
||||||
|
"$dest_root" "$refresh"; then
|
||||||
|
ok "$skill vendored (pinned @ ${sha:0:7})"
|
||||||
|
else
|
||||||
|
err "$skill: not all files landed (see the download-failed lines above)"
|
||||||
|
fi
|
||||||
|
done < <(tail -n +3 <<<"$lock_out")
|
||||||
|
}
|
||||||
@@ -94,7 +94,10 @@ fi
|
|||||||
# skills/ (and its agents into agents/) at --scope=global, so there is no
|
# skills/ (and its agents into agents/) at --scope=global, so there is no
|
||||||
# skills-external/ copy to symlink. See install-plugins.sh Step 8d.
|
# skills-external/ copy to symlink. See install-plugins.sh Step 8d.
|
||||||
EXTERNAL_SKILLS=(emil-design-eng frontend-design design-motion-principles
|
EXTERNAL_SKILLS=(emil-design-eng frontend-design design-motion-principles
|
||||||
observability-and-instrumentation deprecation-and-migration ci-cd-and-automation)
|
observability-and-instrumentation deprecation-and-migration ci-cd-and-automation
|
||||||
|
scroll-world-storytelling build-threejs-scroll-worlds
|
||||||
|
scroll-scrubbed-visual-sequence scroll-scrubbed-word-reveal
|
||||||
|
scroll-progress-timeline)
|
||||||
for _ext_skill in "${EXTERNAL_SKILLS[@]}"; do
|
for _ext_skill in "${EXTERNAL_SKILLS[@]}"; do
|
||||||
if [ -d "$REPO/skills-external/$_ext_skill" ]; then
|
if [ -d "$REPO/skills-external/$_ext_skill" ]; then
|
||||||
if [ -L "$CLAUDE/skills/$_ext_skill" ] && [ "$(readlink "$CLAUDE/skills/$_ext_skill")" = "$REPO/skills-external/$_ext_skill" ]; then
|
if [ -L "$CLAUDE/skills/$_ext_skill" ] && [ "$(readlink "$CLAUDE/skills/$_ext_skill")" = "$REPO/skills-external/$_ext_skill" ]; then
|
||||||
|
|||||||
+15
-1
@@ -48,7 +48,21 @@
|
|||||||
"commit": "2686b620fc1fed2e8f60c704839c766b8594c6b6",
|
"commit": "2686b620fc1fed2e8f60c704839c766b8594c6b6",
|
||||||
"skills": ["observability-and-instrumentation", "deprecation-and-migration", "ci-cd-and-automation"],
|
"skills": ["observability-and-instrumentation", "deprecation-and-migration", "ci-cd-and-automation"],
|
||||||
"managed_by": "curl",
|
"managed_by": "curl",
|
||||||
"note": "Three dev-lifecycle skills from addyosmani/agent-skills, vendored the emil-design-eng way but COMMIT-pinned (not main-branch tracking): each lands in skills-external/<name>/SKILL.md (gitignored, symlinked by link.sh), install-plugins.sh Step 8e curls all three at this commit when absent, update-all.sh re-fetches at the SAME commit on every run (a pin, not an auto-advance). Bump the commit deliberately to pick up upstream changes; the scripts read it from here, never hardcode it."
|
"note": "Three dev-lifecycle skills from addyosmani/agent-skills, vendored the emil-design-eng way but COMMIT-pinned (not main-branch tracking): each lands in skills-external/<name>/SKILL.md (gitignored, symlinked by link.sh), install-plugins.sh Step 8e curls all three at this commit when absent, update-all.sh re-fetches at the SAME commit on every run (a pin, not an auto-advance). Bump the commit deliberately to pick up upstream changes; the scripts read it from here, never hardcode it. Both install-plugins.sh and update-all.sh vendor this entry through lib/vendor-skills.sh's vendor_pinned_skills() — the shared helper also used by the \"mengto-skills\" entry below."
|
||||||
|
},
|
||||||
|
"mengto-skills": {
|
||||||
|
"source": "https://github.com/MengTo/Skills",
|
||||||
|
"commit": "a965851e27dc179e693fde1bee94457a64e1a7a5",
|
||||||
|
"path": "agent-skills/web-design",
|
||||||
|
"skills": {
|
||||||
|
"scroll-world-storytelling": ["SKILL.md", "REFERENCES.md"],
|
||||||
|
"build-threejs-scroll-worlds": ["SKILL.md", "references/kage-anatomy.md", "references/quality-and-qa.md", "references/realtime-architecture.md", "references/scroll-conductor.js", "references/world-bible.md"],
|
||||||
|
"scroll-scrubbed-visual-sequence": ["SKILL.md", "REFERENCES.md"],
|
||||||
|
"scroll-scrubbed-word-reveal": ["SKILL.md", "REFERENCES.md"],
|
||||||
|
"scroll-progress-timeline": ["SKILL.md", "REFERENCES.md"]
|
||||||
|
},
|
||||||
|
"managed_by": "curl",
|
||||||
|
"note": "Five scroll-choreography skills from MengTo/Skills (agent-skills/web-design), vendored the agent-skills way but with an explicit per-skill file list (SKILL.md + REFERENCES.md, or references/*.md + references/scroll-conductor.js for build-threejs-scroll-worlds) instead of the SKILL.md-only default. Never vendored: demo/, agents/, or any binary asset upstream ships alongside each skill. Text-only, byte-for-byte copies (Codex-isms in the source text stay). Bump the commit deliberately to pick up an upstream edit; install-plugins.sh Step 8e and update-all.sh 7.3 both read it from here via lib/vendor-skills.sh's vendor_pinned_skills(), never hardcoded."
|
||||||
},
|
},
|
||||||
"impeccable": {
|
"impeccable": {
|
||||||
"source": "npm:impeccable",
|
"source": "npm:impeccable",
|
||||||
|
|||||||
+9
-29
@@ -379,37 +379,17 @@ else
|
|||||||
info "design-motion-principles not installed — skipping"
|
info "design-motion-principles not installed — skipping"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 7.3. Update Agent Skills (addyosmani/agent-skills, pinned commit) ──
|
# ── 7.3. Update Agent Skills + Mengto scroll skills (pinned commit) ──
|
||||||
|
# Both re-fetched at the SAME pinned commit (never advances the pin) via
|
||||||
|
# the shared lib/vendor-skills.sh helper — see install-plugins.sh Step 8e.
|
||||||
echo ""
|
echo ""
|
||||||
echo "── Updating Agent Skills (addyosmani/agent-skills)..."
|
echo "── Updating Agent Skills (addyosmani/agent-skills)..."
|
||||||
AGENT_SKILLS_SHA=""
|
# shellcheck source=lib/vendor-skills.sh disable=SC1091
|
||||||
if [ -f "$REPO/plugins.lock.json" ] && command -v python3 &>/dev/null; then
|
source "$REPO/lib/vendor-skills.sh"
|
||||||
AGENT_SKILLS_SHA=$(python3 -c "
|
vendor_pinned_skills agent-skills refresh
|
||||||
import json, sys
|
echo ""
|
||||||
with open(sys.argv[1]) as f:
|
echo "── Updating Mengto scroll skills (MengTo/Skills)..."
|
||||||
d = json.load(f)
|
vendor_pinned_skills mengto-skills refresh
|
||||||
print(d.get(sys.argv[2], {}).get('commit', ''))
|
|
||||||
" "$REPO/plugins.lock.json" "agent-skills" 2>/dev/null || true)
|
|
||||||
fi
|
|
||||||
AGENT_SKILLS_NAMES=(observability-and-instrumentation deprecation-and-migration ci-cd-and-automation)
|
|
||||||
if [ -z "$AGENT_SKILLS_SHA" ]; then
|
|
||||||
warn "agent-skills: no commit pinned in plugins.lock.json — skipping"
|
|
||||||
else
|
|
||||||
for _as_skill in "${AGENT_SKILLS_NAMES[@]}"; do
|
|
||||||
_as_dir="$REPO/skills-external/$_as_skill"
|
|
||||||
if [ ! -d "$_as_dir" ]; then
|
|
||||||
info "$_as_skill not installed — skipping (run: make plugin)"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
_as_url="https://raw.githubusercontent.com/addyosmani/agent-skills/$AGENT_SKILLS_SHA/skills/$_as_skill/SKILL.md"
|
|
||||||
if curl -fsSL "$_as_url" -o "$_as_dir/SKILL.md.tmp" \
|
|
||||||
&& mv "$_as_dir/SKILL.md.tmp" "$_as_dir/SKILL.md"; then
|
|
||||||
ok "$_as_skill re-fetched at pinned commit"
|
|
||||||
else
|
|
||||||
warn "$_as_skill update failed"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Impeccable (design detector + skill + subagents) ──
|
# ── Impeccable (design detector + skill + subagents) ──
|
||||||
# Global scope: the installer writes through the ~/.claude/{skills,agents}
|
# Global scope: the installer writes through the ~/.claude/{skills,agents}
|
||||||
|
|||||||
Reference in New Issue
Block a user