forked from bchanot/claude
feat(superpowers): vendor the 7 wired skills at v6.4.1, drop the plugin
plugins.lock.json gains a superpowers entry (obra/superpowers @ 5bf4e78, path skills, per-skill file lists, always_on) that lib/vendor-skills.sh fetches byte-for-byte: brainstorming, writing-plans, subagent-driven-development, test-driven-development, requesting-code-review, using-git-worktrees, writing-skills. install-plugins STEP 8e vendors it, update-all refreshes it at the pin, link.sh links the seven, .gitignore ignores them. The plugin is no longer installed or protected: its 8 other skills duplicated personal flows and its SessionStart injection cost ~900 tokens per start, clear and compact. detect_superpowers is one file test on the linked skill; doctor and session-start stop charging the injection. doctor-vendored gains an always_on class (third lock column) so always-on externals are link-checked instead of reported parked.
This commit is contained in:
+23
@@ -74,6 +74,15 @@ skills/scroll-scrubbed-visual-sequence
|
|||||||
skills/scroll-scrubbed-word-reveal
|
skills/scroll-scrubbed-word-reveal
|
||||||
skills/scroll-progress-timeline
|
skills/scroll-progress-timeline
|
||||||
|
|
||||||
|
# superpowers, vendored (plugins.lock.json 'superpowers')
|
||||||
|
skills/brainstorming
|
||||||
|
skills/writing-plans
|
||||||
|
skills/subagent-driven-development
|
||||||
|
skills/test-driven-development
|
||||||
|
skills/requesting-code-review
|
||||||
|
skills/using-git-worktrees
|
||||||
|
skills/writing-skills
|
||||||
|
|
||||||
# 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
|
||||||
# ~/.claude/skills symlink. Machine-owned, regenerated by make plugin/update.
|
# ~/.claude/skills symlink. Machine-owned, regenerated by make plugin/update.
|
||||||
@@ -206,6 +215,20 @@ skills-external/scroll-scrubbed-visual-sequence/
|
|||||||
skills-external/scroll-scrubbed-word-reveal/
|
skills-external/scroll-scrubbed-word-reveal/
|
||||||
skills-external/scroll-progress-timeline/
|
skills-external/scroll-progress-timeline/
|
||||||
|
|
||||||
|
# superpowers, vendored (plugins.lock.json 'superpowers') — machine-owned,
|
||||||
|
# curl'd at the commit pinned in plugins.lock.json 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/brainstorming/
|
||||||
|
skills-external/writing-plans/
|
||||||
|
skills-external/subagent-driven-development/
|
||||||
|
skills-external/test-driven-development/
|
||||||
|
skills-external/requesting-code-review/
|
||||||
|
skills-external/using-git-worktrees/
|
||||||
|
skills-external/writing-skills/
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
@@ -223,9 +223,9 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if detect_superpowers; then
|
if detect_superpowers; then
|
||||||
pass "Superpowers plugin detected"
|
pass "superpowers skills linked (brainstorming found); per-skill check under Vendored skills"
|
||||||
else
|
else
|
||||||
fail "Superpowers not detected — orchestrators (/init-project, /ship-feature) will fail"
|
fail "superpowers skills not linked — run: make plugin && make link"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if detect_context7; then
|
if detect_context7; then
|
||||||
@@ -417,10 +417,10 @@ SKILL_DESC_TOKENS=$((SKILL_DESC_CHARS / 4))
|
|||||||
# Plugin passive cost estimates (tokens) — session-start injections and
|
# Plugin passive cost estimates (tokens) — session-start injections and
|
||||||
# hook prompts that never show up as a skill description above. gstack,
|
# hook prompts that never show up as a skill description above. gstack,
|
||||||
# context7 (find-docs) and graphifyy dropped 2026-09-28 (skill-catalog
|
# context7 (find-docs) and graphifyy dropped 2026-09-28 (skill-catalog
|
||||||
# prune): their skills sit under ~/.claude/skills and are already counted
|
# prune); superpowers dropped the same day (tier 2, vendored instead):
|
||||||
# by the stats above — a separate constant here double-counted them.
|
# their skills sit under ~/.claude/skills and are already counted by the
|
||||||
|
# stats above — a separate constant here double-counted them.
|
||||||
PLUGIN_TOKENS=0
|
PLUGIN_TOKENS=0
|
||||||
if detect_superpowers 2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 1500)); fi
|
|
||||||
if detect_uiux_pro_max 2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 670)); fi
|
if detect_uiux_pro_max 2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 670)); fi
|
||||||
|
|
||||||
TOTAL_TOKENS=$((CLAUDE_MD_TOKENS + SKILL_DESC_TOKENS + PLUGIN_TOKENS))
|
TOTAL_TOKENS=$((CLAUDE_MD_TOKENS + SKILL_DESC_TOKENS + PLUGIN_TOKENS))
|
||||||
|
|||||||
@@ -118,8 +118,9 @@ esac
|
|||||||
|
|
||||||
# Quick passive token cost estimate
|
# Quick passive token cost estimate
|
||||||
# Only count plugins that are ACTIVE (detected as ON), not just installed
|
# Only count plugins that are ACTIVE (detected as ON), not just installed
|
||||||
|
# superpowers dropped 2026-09-28 (tier 2 of the skill-catalog prune): its
|
||||||
|
# 7 vendored skills are counted by the skill catalog, not a plugin cost.
|
||||||
_passive_t=0
|
_passive_t=0
|
||||||
detect_superpowers 2>/dev/null && _passive_t=$((_passive_t + 800))
|
|
||||||
|
|
||||||
# Token costs for toggle plugins — map display name to cost
|
# Token costs for toggle plugins — map display name to cost
|
||||||
declare -A _plugin_costs=(
|
declare -A _plugin_costs=(
|
||||||
|
|||||||
+19
-17
@@ -487,8 +487,8 @@ install_plugin() {
|
|||||||
# copies the plugin into ~/.claude/plugins/cache — it does NOT register
|
# copies the plugin into ~/.claude/plugins/cache — it does NOT register
|
||||||
# it in settings.json's enabledPlugins map. Without an explicit enable,
|
# it in settings.json's enabledPlugins map. Without an explicit enable,
|
||||||
# the plugin sits dormant. Use this for plugins that should be ALWAYS ON
|
# the plugin sits dormant. Use this for plugins that should be ALWAYS ON
|
||||||
# (security-guidance, superpowers). Idempotent: skips if already
|
# (security-guidance). Idempotent: skips if already present in
|
||||||
# present in enabledPlugins.
|
# enabledPlugins.
|
||||||
enable_plugin() {
|
enable_plugin() {
|
||||||
local name="$1"
|
local name="$1"
|
||||||
local source="$2"
|
local source="$2"
|
||||||
@@ -531,13 +531,10 @@ install_plugin "pr-review-toolkit" "claude-code-plugins"
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Superpowers (always on)
|
# Superpowers plugin removed 2026-09-28 (tier 2 of the skill-catalog prune):
|
||||||
info "Adding Superpowers marketplace..."
|
# its 7 wired skills are vendored in Step 8e (plugins.lock.json
|
||||||
claude plugin marketplace add obra/superpowers-marketplace 2>/dev/null || true
|
# 'superpowers'); a still-cached plugin is uninstalled by hand once
|
||||||
install_plugin "superpowers" "superpowers-marketplace"
|
# (claude plugin uninstall superpowers@superpowers-marketplace), never here
|
||||||
enable_plugin "superpowers" "superpowers-marketplace"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# UI/UX Pro Max (toggle)
|
# UI/UX Pro Max (toggle)
|
||||||
info "Adding UI/UX Pro Max marketplace..."
|
info "Adding UI/UX Pro Max marketplace..."
|
||||||
@@ -909,21 +906,25 @@ fi
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── Step 8e: Agent Skills (addyosmani/agent-skills) + Mengto scroll
|
# ── Step 8e: Agent Skills (addyosmani/agent-skills) + Mengto scroll
|
||||||
# skills (MengTo/Skills) — both commit-pinned, vendored the emil-design-eng
|
# skills (MengTo/Skills) + superpowers (obra/superpowers) — all
|
||||||
# way (curl → skills-external/<name>/, symlinked by link.sh) through the
|
# commit-pinned, vendored the emil-design-eng way (curl →
|
||||||
# shared lib/vendor-skills.sh helper. Shas/paths/file-lists live in
|
# skills-external/<name>/, symlinked by link.sh) through the shared
|
||||||
# plugins.lock.json ("agent-skills" / "mengto-skills" entries), never
|
# lib/vendor-skills.sh helper. Shas/paths/file-lists live in
|
||||||
# hardcoded here.
|
# plugins.lock.json ("agent-skills" / "mengto-skills" / "superpowers"
|
||||||
echo "── Step 8e: Agent Skills + Mengto scroll skills (pinned commit) ──"
|
# entries), never hardcoded here.
|
||||||
|
echo "── Step 8e: Agent Skills + Mengto scroll skills + superpowers (pinned commit) ──"
|
||||||
echo ""
|
echo ""
|
||||||
# shellcheck source=lib/vendor-skills.sh disable=SC1091
|
# shellcheck source=lib/vendor-skills.sh disable=SC1091
|
||||||
source "$REPO/lib/vendor-skills.sh"
|
source "$REPO/lib/vendor-skills.sh"
|
||||||
EXT_SKILL_NAMES=(observability-and-instrumentation deprecation-and-migration
|
EXT_SKILL_NAMES=(observability-and-instrumentation deprecation-and-migration
|
||||||
ci-cd-and-automation scroll-world-storytelling build-threejs-scroll-worlds
|
ci-cd-and-automation scroll-world-storytelling build-threejs-scroll-worlds
|
||||||
scroll-scrubbed-visual-sequence scroll-scrubbed-word-reveal
|
scroll-scrubbed-visual-sequence scroll-scrubbed-word-reveal
|
||||||
scroll-progress-timeline)
|
scroll-progress-timeline brainstorming writing-plans
|
||||||
|
subagent-driven-development test-driven-development
|
||||||
|
requesting-code-review using-git-worktrees writing-skills)
|
||||||
vendor_pinned_skills agent-skills
|
vendor_pinned_skills agent-skills
|
||||||
vendor_pinned_skills mengto-skills
|
vendor_pinned_skills mengto-skills
|
||||||
|
vendor_pinned_skills superpowers
|
||||||
for _ext_skill in "${EXT_SKILL_NAMES[@]}"; do
|
for _ext_skill in "${EXT_SKILL_NAMES[@]}"; do
|
||||||
if [ -L "$HOME/.claude/skills/$_ext_skill" ]; then
|
if [ -L "$HOME/.claude/skills/$_ext_skill" ]; then
|
||||||
ok "$_ext_skill symlink OK"
|
ok "$_ext_skill symlink OK"
|
||||||
@@ -1207,7 +1208,7 @@ echo ""
|
|||||||
echo " ALWAYS ON (installed at user scope):"
|
echo " ALWAYS ON (installed at user scope):"
|
||||||
echo " ✅ security-guidance — regex hints on Edit/Write + out-of-band LLM reviews on commit/push (Stop review off via ENABLE_STOP_REVIEW=0; quota, not context) [claude-code-plugins]"
|
echo " ✅ security-guidance — regex hints on Edit/Write + out-of-band LLM reviews on commit/push (Stop review off via ENABLE_STOP_REVIEW=0; quota, not context) [claude-code-plugins]"
|
||||||
echo " ✅ rtk — token compression hook (0 tokens)"
|
echo " ✅ rtk — token compression hook (0 tokens)"
|
||||||
echo " ✅ superpowers — brainstorm/plan/implement/debug workflow"
|
echo " ✅ superpowers skills — 7 vendored (brainstorming, writing-plans, subagent-driven-development, test-driven-development, requesting-code-review, using-git-worktrees, writing-skills), pinned v6.4.1, curl → symlink, no plugin, no session injection"
|
||||||
echo ""
|
echo ""
|
||||||
echo " TOGGLE (plugin state = settings.json enabledPlugins; skills/CLIs = profiles):"
|
echo " TOGGLE (plugin state = settings.json enabledPlugins; skills/CLIs = profiles):"
|
||||||
echo " 🔄 gstack — disabled by default (toggle: lib/toggle-external.sh enable gstack)"
|
echo " 🔄 gstack — disabled by default (toggle: lib/toggle-external.sh enable gstack)"
|
||||||
@@ -1232,6 +1233,7 @@ echo " Frontend Design at: ~/.claude/skills/frontend-design/ (symlink → skill
|
|||||||
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 " 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 " Superpowers skills at: ~/.claude/skills/{brainstorming,writing-plans,subagent-driven-development,test-driven-development,requesting-code-review,using-git-worktrees,writing-skills}/ (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"
|
||||||
|
|||||||
@@ -16,14 +16,10 @@ detect_rtk() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
detect_superpowers() {
|
detect_superpowers() {
|
||||||
# Fast check: filesystem (plugin cache)
|
# superpowers = 7 vendored skills since 2026-09-28; the plugin is gone.
|
||||||
local cache_dir="$HOME/.claude/plugins/cache"
|
# One file test on the linked vendored skill: proves vendored AND
|
||||||
if [ -d "$cache_dir" ]; then
|
# linked in one shot — no plugin cache glob, no `claude plugin list`.
|
||||||
compgen -G "$cache_dir"/*superpowers* &>/dev/null && return 0
|
[ -f "$HOME/.claude/skills/brainstorming/SKILL.md" ]
|
||||||
fi
|
|
||||||
# Slow fallback: CLI (only if fast check fails)
|
|
||||||
claude plugin list 2>/dev/null | grep -qi "superpowers" && return 0
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+53
-20
@@ -5,8 +5,8 @@
|
|||||||
# EXTERNAL_SKILLS array). doctor.sh's "GStack submodule" section only
|
# EXTERNAL_SKILLS array). doctor.sh's "GStack submodule" section only
|
||||||
# covers the gstack submodule — this covers the OTHER external skill
|
# covers the gstack submodule — this covers the OTHER external skill
|
||||||
# packs (emil-design-eng, the agent-skills trio, the five Mengto scroll
|
# packs (emil-design-eng, the agent-skills trio, the five Mengto scroll
|
||||||
# skills, and any name link.sh links with no lock entry at all, e.g.
|
# skills, the seven superpowers skills, and any name link.sh links with
|
||||||
# frontend-design, design-motion-principles).
|
# no lock entry at all, e.g. frontend-design, design-motion-principles).
|
||||||
#
|
#
|
||||||
# One entry point, `check_vendored_skills <repo> <claude_home>
|
# One entry point, `check_vendored_skills <repo> <claude_home>
|
||||||
# [profile_file]`, sourced and called by doctor.sh. Two things checked
|
# [profile_file]`, sourced and called by doctor.sh. Two things checked
|
||||||
@@ -21,7 +21,9 @@
|
|||||||
# is passed — the "could not resolve the active profile" case),
|
# is passed — the "could not resolve the active profile" case),
|
||||||
# the <claude_home>/skills/<name> symlink points at
|
# the <claude_home>/skills/<name> symlink points at
|
||||||
# <repo>/skills-external/<name>. A name absent from the profile is
|
# <repo>/skills-external/<name>. A name absent from the profile is
|
||||||
# reported parked, not failed.
|
# reported parked, not failed — unless its lock entry is
|
||||||
|
# "always_on": true (the superpowers entry is), in which case the
|
||||||
|
# symlink is checked regardless of the profile (see _dv_check_link).
|
||||||
#
|
#
|
||||||
# Lock parsing via python3 argv (never string-spliced) — same pattern as
|
# Lock parsing via python3 argv (never string-spliced) — same pattern as
|
||||||
# lib/vendor-skills.sh's _vendor_read_lock. link.sh's EXTERNAL_SKILLS
|
# lib/vendor-skills.sh's _vendor_read_lock. link.sh's EXTERNAL_SKILLS
|
||||||
@@ -61,11 +63,14 @@ fi
|
|||||||
|
|
||||||
# _dv_lock_expectations <lockfile> — prints "<name>\t<file>" for every
|
# _dv_lock_expectations <lockfile> — prints "<name>\t<file>" for every
|
||||||
# skill named under a plugins.lock.json entry whose "managed_by" is
|
# skill named under a plugins.lock.json entry whose "managed_by" is
|
||||||
# "curl": a bare list defaults each name to ["SKILL.md"]; a dict names
|
# "curl", plus a THIRD column "\t1" when that entry is "always_on": true
|
||||||
# its own per-skill file list; an entry with neither (the
|
# (the superpowers entry is) — read by _dv_is_always_on, ignored by the
|
||||||
# emil-design-eng single-file "path" shape) is itself the skill name,
|
# $1==n {print $2} awk in _dv_check_files: a bare list defaults each name
|
||||||
# file "SKILL.md" (the literal "path" value is upstream layout, not the
|
# to ["SKILL.md"]; a dict names its own per-skill file list; an entry
|
||||||
# local dest — never used here). Reads the lockfile via argv only.
|
# with neither (the emil-design-eng single-file "path" shape) is itself
|
||||||
|
# the skill name, file "SKILL.md" (the literal "path" value is upstream
|
||||||
|
# layout, not the local dest — never used here). Reads the lockfile via
|
||||||
|
# argv only.
|
||||||
# Every curl-managed entry's shape is validated ("skills" null, a list
|
# Every curl-managed entry's shape is validated ("skills" null, a list
|
||||||
# of str, or a dict of str -> list of str; "path" a str when present)
|
# of str, or a dict of str -> list of str; "path" a str when present)
|
||||||
# BEFORE it is used, so a malformed entry is the same clean failure as
|
# BEFORE it is used, so a malformed entry is the same clean failure as
|
||||||
@@ -118,12 +123,13 @@ for key, entry in data.items():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if not valid_skills(skills):
|
if not valid_skills(skills):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
suffix = "\t1" if entry.get("always_on") is True else ""
|
||||||
if skills is None:
|
if skills is None:
|
||||||
print(f"{key}\tSKILL.md")
|
print(f"{key}\tSKILL.md{suffix}")
|
||||||
continue
|
continue
|
||||||
for name, files in skill_files(skills).items():
|
for name, files in skill_files(skills).items():
|
||||||
for file in files:
|
for file in files:
|
||||||
print(f"{name}\t{file}")
|
print(f"{name}\t{file}{suffix}")
|
||||||
PY
|
PY
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,16 +202,30 @@ allowlist — skipped"
|
|||||||
[ "$all_ok" -eq 1 ]
|
[ "$all_ok" -eq 1 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
# _dv_check_link <claude_home> <repo> <name> <profile_file> — when
|
# _dv_is_always_on <name> <lock_out> — true when <lock_out> (the
|
||||||
# <profile_file> is non-empty and does not list <name>, reports it
|
# "<name>\t<file>[\t1]" lines from _dv_lock_expectations) carries the
|
||||||
# parked (info), not failed. Otherwise (listed, or no <profile_file> was
|
# always_on third column for <name>'s lock entry.
|
||||||
# passed — active profile could not be resolved, every external is then
|
_dv_is_always_on() {
|
||||||
# expected linked) checks the <claude_home>/skills/<name> symlink points
|
local name="$1" lock_out="$2"
|
||||||
# at <repo>/skills-external/<name>.
|
awk -F'\t' -v n="$name" '$1 == n && $3 == 1 { found=1 } \
|
||||||
|
END { exit !found }' <<< "$lock_out"
|
||||||
|
}
|
||||||
|
|
||||||
|
# _dv_check_link <claude_home> <repo> <name> <profile_file> <always_on> —
|
||||||
|
# when <always_on> is "1" (the name's lock entry is "always_on": true),
|
||||||
|
# the symlink is checked whatever <profile_file> says — never parked.
|
||||||
|
# Otherwise, when <profile_file> is non-empty and does not list <name>,
|
||||||
|
# reports it parked (info), not failed. Otherwise (listed, always_on, or
|
||||||
|
# no <profile_file> was passed — active profile could not be resolved,
|
||||||
|
# every external is then expected linked) checks the
|
||||||
|
# <claude_home>/skills/<name> symlink points at
|
||||||
|
# <repo>/skills-external/<name>.
|
||||||
_dv_check_link() {
|
_dv_check_link() {
|
||||||
local claude_home="$1" repo="$2" name="$3" profile_file="$4"
|
local claude_home="$1" repo="$2" name="$3" profile_file="$4" \
|
||||||
|
always_on="$5"
|
||||||
local link target label
|
local link target label
|
||||||
if [ -n "$profile_file" ] && ! _dv_profile_has "$profile_file" "$name"; then
|
if [ "$always_on" != "1" ] && [ -n "$profile_file" ] \
|
||||||
|
&& ! _dv_profile_has "$profile_file" "$name"; then
|
||||||
label="$(basename "$profile_file" .profile)"
|
label="$(basename "$profile_file" .profile)"
|
||||||
info "$name: parked by profile $label"
|
info "$name: parked by profile $label"
|
||||||
return
|
return
|
||||||
@@ -220,6 +240,20 @@ lib/profile.sh apply <profile>)"
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# _dv_check_name <repo> <claude_home> <name> <profile_file> <lock_out> —
|
||||||
|
# per-name dispatch for check_vendored_skills's loop: files first (the
|
||||||
|
# link check runs only when every expected file is present, same as
|
||||||
|
# before), then the symlink, passing _dv_is_always_on's verdict as
|
||||||
|
# _dv_check_link's 5th param.
|
||||||
|
_dv_check_name() {
|
||||||
|
local repo="$1" claude_home="$2" name="$3" profile_file="$4" lock_out="$5"
|
||||||
|
local always_on=""
|
||||||
|
_dv_is_always_on "$name" "$lock_out" && always_on=1
|
||||||
|
_dv_check_files "$repo" "$name" "$lock_out" \
|
||||||
|
&& _dv_check_link "$claude_home" "$repo" "$name" "$profile_file" \
|
||||||
|
"$always_on"
|
||||||
|
}
|
||||||
|
|
||||||
# check_vendored_skills <repo> <claude_home> [profile_file] — see the
|
# check_vendored_skills <repo> <claude_home> [profile_file] — see the
|
||||||
# file header. Either the lock or link.sh being unreadable (or a
|
# file header. Either the lock or link.sh being unreadable (or a
|
||||||
# malformed lock entry — _dv_lock_expectations rc 1) is a warn, never a
|
# malformed lock entry — _dv_lock_expectations rc 1) is a warn, never a
|
||||||
@@ -251,7 +285,6 @@ check skipped"
|
|||||||
item-name allowlist — skipped"
|
item-name allowlist — skipped"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
_dv_check_files "$repo" "$name" "$lock_out" \
|
_dv_check_name "$repo" "$claude_home" "$name" "$profile_file" "$lock_out"
|
||||||
&& _dv_check_link "$claude_home" "$repo" "$name" "$profile_file"
|
|
||||||
done <<< "$names"
|
done <<< "$names"
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-6
@@ -18,8 +18,10 @@
|
|||||||
# and MCPs in the MANAGED_* allowlists are disabled when the profile
|
# and MCPs in the MANAGED_* allowlists are disabled when the profile
|
||||||
# does not list them — nothing outside those lists is ever auto-toggled.
|
# does not list them — nothing outside those lists is ever auto-toggled.
|
||||||
#
|
#
|
||||||
# Always-on plugins (never toggled by `set`): security-guidance,
|
# Always-on plugins (never toggled by `set`): security-guidance + rtk
|
||||||
# superpowers + rtk hook + .claude internal. The script refuses to disable
|
# hook + .claude internal. superpowers is vendored skills now, not a
|
||||||
|
# plugin (never in PROTECTED_PLUGINS, never in MANAGED_EXTERNALS — same
|
||||||
|
# always-on class as darwin-skill). The script refuses to disable
|
||||||
# anything in PROTECTED_PLUGINS.
|
# anything in PROTECTED_PLUGINS.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
@@ -61,9 +63,10 @@ DEFAULT_PROFILE="full" # profile in force when none is selected (cache absent,
|
|||||||
source "$(dirname "${BASH_SOURCE[0]}")/gstack-removed.sh"
|
source "$(dirname "${BASH_SOURCE[0]}")/gstack-removed.sh"
|
||||||
|
|
||||||
# Plugins that are toggle-managed by `set`. Anything NOT in this list is
|
# Plugins that are toggle-managed by `set`. Anything NOT in this list is
|
||||||
# never auto-disabled — protects always-on plugins (security-guidance,
|
# never auto-disabled — protects always-on plugins (security-guidance;
|
||||||
# superpowers) and unrelated user plugins. Add a plugin here only when its
|
# superpowers is vendored skills now, not a plugin) and unrelated user
|
||||||
# enabled state is meaningfully driven by task type.
|
# plugins. Add a plugin here only when its enabled state is meaningfully
|
||||||
|
# driven by task type.
|
||||||
MANAGED_PLUGINS=(
|
MANAGED_PLUGINS=(
|
||||||
"ui-ux-pro-max@ui-ux-pro-max-skill"
|
"ui-ux-pro-max@ui-ux-pro-max-skill"
|
||||||
"plugin-dev@claude-code-plugins"
|
"plugin-dev@claude-code-plugins"
|
||||||
@@ -106,7 +109,6 @@ MANAGED_MCPS=()
|
|||||||
# MANAGED_PLUGINS allowlist.)
|
# MANAGED_PLUGINS allowlist.)
|
||||||
PROTECTED_PLUGINS=(
|
PROTECTED_PLUGINS=(
|
||||||
"security-guidance@claude-code-plugins"
|
"security-guidance@claude-code-plugins"
|
||||||
"superpowers@superpowers-marketplace"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; BLUE='\033[0;34m'; NC='\033[0m'
|
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
|
|||||||
@@ -17,9 +17,11 @@
|
|||||||
# "skills" is neither null/list/dict degrading the same way with no
|
# "skills" is neither null/list/dict degrading the same way with no
|
||||||
# Python traceback leaking (LOCK_MALFORMED_ENTRY, rc 0), the
|
# Python traceback leaking (LOCK_MALFORMED_ENTRY, rc 0), the
|
||||||
# profile-name allowlist rejecting a path-traversal value
|
# profile-name allowlist rejecting a path-traversal value
|
||||||
# (REJECTS_BAD_PROFILE_NAME), and the item-name allowlist rejecting a
|
# (REJECTS_BAD_PROFILE_NAME), the item-name allowlist rejecting a
|
||||||
# link.sh entry with a ".." segment — warned and skipped, not failed
|
# link.sh entry with a ".." segment — warned and skipped, not failed
|
||||||
# (REJECTS_BAD_NAME).
|
# (REJECTS_BAD_NAME), and an "always_on": true lock entry's name, absent
|
||||||
|
# from the profile and with no symlink, checked (and failed) instead of
|
||||||
|
# reported parked (ALWAYS_ON_LINK_CHECKED).
|
||||||
set -u
|
set -u
|
||||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
LIB="$ROOT/lib/doctor-vendored.sh"
|
LIB="$ROOT/lib/doctor-vendored.sh"
|
||||||
@@ -57,6 +59,11 @@ cat > "$REPO/plugins.lock.json" <<'JSON'
|
|||||||
"dict-entry": {
|
"dict-entry": {
|
||||||
"managed_by": "curl",
|
"managed_by": "curl",
|
||||||
"skills": {"dict-skill": ["SKILL.md", "references/notes.md"]}
|
"skills": {"dict-skill": ["SKILL.md", "references/notes.md"]}
|
||||||
|
},
|
||||||
|
"always-on-entry": {
|
||||||
|
"managed_by": "curl",
|
||||||
|
"always_on": true,
|
||||||
|
"skills": ["always-on-skill"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JSON
|
JSON
|
||||||
@@ -66,25 +73,27 @@ cat > "$REPO/link.sh" <<'SH'
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
EXTERNAL_SKILLS=(ok-skill missing-skill dict-skill
|
EXTERNAL_SKILLS=(ok-skill missing-skill dict-skill
|
||||||
active-nolink-skill active-wronglink-skill
|
active-nolink-skill active-wronglink-skill
|
||||||
parked-skill noprofile-skill)
|
parked-skill noprofile-skill always-on-skill)
|
||||||
SH
|
SH
|
||||||
|
|
||||||
# ── skills-external/ tree: every name's SKILL.md present, except
|
# ── skills-external/ tree: every name's SKILL.md present, except
|
||||||
# missing-skill (nothing at all) and dict-skill's references/notes.md.
|
# missing-skill (nothing at all) and dict-skill's references/notes.md.
|
||||||
|
# always-on-skill has its SKILL.md too — only its symlink is missing.
|
||||||
for n in ok-skill dict-skill active-nolink-skill active-wronglink-skill \
|
for n in ok-skill dict-skill active-nolink-skill active-wronglink-skill \
|
||||||
parked-skill noprofile-skill; do
|
parked-skill noprofile-skill always-on-skill; do
|
||||||
mkdir -p "$REPO/skills-external/$n"
|
mkdir -p "$REPO/skills-external/$n"
|
||||||
echo "v1" > "$REPO/skills-external/$n/SKILL.md"
|
echo "v1" > "$REPO/skills-external/$n/SKILL.md"
|
||||||
done
|
done
|
||||||
|
|
||||||
# ── claude_home symlinks: ok-skill correct, active-wronglink-skill
|
# ── claude_home symlinks: ok-skill correct, active-wronglink-skill
|
||||||
# points elsewhere, active-nolink-skill and noprofile-skill have none.
|
# points elsewhere, active-nolink-skill, noprofile-skill and
|
||||||
|
# always-on-skill have none.
|
||||||
ln -sf "$REPO/skills-external/ok-skill" "$CLAUDE_HOME/skills/ok-skill"
|
ln -sf "$REPO/skills-external/ok-skill" "$CLAUDE_HOME/skills/ok-skill"
|
||||||
mkdir -p "$WORK/elsewhere"
|
mkdir -p "$WORK/elsewhere"
|
||||||
ln -sf "$WORK/elsewhere" "$CLAUDE_HOME/skills/active-wronglink-skill"
|
ln -sf "$WORK/elsewhere" "$CLAUDE_HOME/skills/active-wronglink-skill"
|
||||||
|
|
||||||
# ── active.profile: lists everything EXCEPT parked-skill and
|
# ── active.profile: lists everything EXCEPT parked-skill,
|
||||||
# noprofile-skill (both proven absent from it).
|
# noprofile-skill and always-on-skill (all three proven absent from it).
|
||||||
cat > "$REPO/active.profile" <<'PROF'
|
cat > "$REPO/active.profile" <<'PROF'
|
||||||
# DESC: fixture profile
|
# DESC: fixture profile
|
||||||
ok-skill external
|
ok-skill external
|
||||||
@@ -132,6 +141,16 @@ check_bool SYMLINK_PARKED \
|
|||||||
grep -qF 'parked-skill: symlink missing/wrong' \
|
grep -qF 'parked-skill: symlink missing/wrong' \
|
||||||
&& echo 1 || echo 0)"
|
&& echo 1 || echo 0)"
|
||||||
|
|
||||||
|
# ── always-on-skill: absent from active.profile (same as parked-skill)
|
||||||
|
# but its lock entry is "always_on": true — checked (and failed, no
|
||||||
|
# symlink) instead of reported parked.
|
||||||
|
check_bool ALWAYS_ON_LINK_CHECKED \
|
||||||
|
"$(printf '%s' "$out1" | \
|
||||||
|
grep -qF 'always-on-skill: symlink missing/wrong' \
|
||||||
|
&& ! printf '%s' "$out1" | \
|
||||||
|
grep -qF 'always-on-skill: parked by profile' \
|
||||||
|
&& echo 1 || echo 0)"
|
||||||
|
|
||||||
# ── No profile file passed at all: noprofile-skill (absent from
|
# ── No profile file passed at all: noprofile-skill (absent from
|
||||||
# active.profile, parked above) must now be treated as expected-linked.
|
# active.profile, parked above) must now be treated as expected-linked.
|
||||||
out2="$(check_vendored_skills "$REPO" "$CLAUDE_HOME" 2>&1)"
|
out2="$(check_vendored_skills "$REPO" "$CLAUDE_HOME" 2>&1)"
|
||||||
|
|||||||
@@ -18,6 +18,9 @@
|
|||||||
# `skills` as a bare list defaults every named skill to `["SKILL.md"]` and
|
# `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
|
# `path` to "skills" (the agent-skills shape); `skills` as a dict carries an
|
||||||
# explicit per-skill file list (references/*, etc.) and `path` is required.
|
# explicit per-skill file list (references/*, etc.) and `path` is required.
|
||||||
|
# `"always_on": true` (optional) is ignored by this helper (fetch is the
|
||||||
|
# same either way) — lib/doctor-vendored.sh reads it to expect the
|
||||||
|
# entry's skills linked regardless of the active profile.
|
||||||
#
|
#
|
||||||
# Raw URL: https://raw.githubusercontent.com/<owner>/<repo>/<sha>/<path>/
|
# Raw URL: https://raw.githubusercontent.com/<owner>/<repo>/<sha>/<path>/
|
||||||
# <skill>/<file>. VENDOR_BASE_URL overrides the "https://…/<repo>" prefix
|
# <skill>/<file>. VENDOR_BASE_URL overrides the "https://…/<repo>" prefix
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ 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-world-storytelling build-threejs-scroll-worlds
|
||||||
scroll-scrubbed-visual-sequence scroll-scrubbed-word-reveal
|
scroll-scrubbed-visual-sequence scroll-scrubbed-word-reveal
|
||||||
scroll-progress-timeline)
|
scroll-progress-timeline brainstorming writing-plans
|
||||||
|
subagent-driven-development test-driven-development
|
||||||
|
requesting-code-review using-git-worktrees writing-skills)
|
||||||
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
|
||||||
|
|||||||
@@ -64,6 +64,23 @@
|
|||||||
"managed_by": "curl",
|
"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."
|
"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."
|
||||||
},
|
},
|
||||||
|
"superpowers": {
|
||||||
|
"source": "https://github.com/obra/superpowers",
|
||||||
|
"commit": "5bf4e78011075bcfc0dc295f0724994cd123ee71",
|
||||||
|
"path": "skills",
|
||||||
|
"skills": {
|
||||||
|
"brainstorming": ["SKILL.md", "spec-document-reviewer-prompt.md", "visual-companion.md", "scripts/frame-template.html", "scripts/helper.js", "scripts/server.cjs", "scripts/start-server.sh", "scripts/stop-server.sh"],
|
||||||
|
"writing-plans": ["SKILL.md", "plan-document-reviewer-prompt.md"],
|
||||||
|
"subagent-driven-development": ["SKILL.md", "implementer-prompt.md", "re-review-prompt.md", "task-reviewer-prompt.md", "scripts/review-package", "scripts/sdd-workspace", "scripts/task-brief"],
|
||||||
|
"test-driven-development": ["SKILL.md", "writing-good-tests.md"],
|
||||||
|
"requesting-code-review": ["SKILL.md", "code-reviewer.md"],
|
||||||
|
"using-git-worktrees": ["SKILL.md"],
|
||||||
|
"writing-skills": ["SKILL.md", "anthropic-best-practices.md", "examples/CLAUDE_MD_TESTING.md", "graphviz-conventions.dot", "persuasion-principles.md", "render-graphs.js", "testing-skills-with-subagents.md"]
|
||||||
|
},
|
||||||
|
"managed_by": "curl",
|
||||||
|
"always_on": true,
|
||||||
|
"note": "Seven superpowers skills vendored byte-for-byte at the v6.4.1 tag commit (obra/superpowers), always on (no profile lists them). Bump the commit deliberately. Scripts inside run as `bash scripts/<x>`, no exec bit needed. Upstream cross-references to the plugin prefix and to the 8 non-vendored skills stay in the text; CLAUDE.global.md Skill routing maps them."
|
||||||
|
},
|
||||||
"impeccable": {
|
"impeccable": {
|
||||||
"source": "npm:impeccable",
|
"source": "npm:impeccable",
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
|
|||||||
+7
-3
@@ -377,9 +377,10 @@ else
|
|||||||
info "design-motion-principles not installed — skipping"
|
info "design-motion-principles not installed — skipping"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 7.3. Update Agent Skills + Mengto scroll skills (pinned commit) ──
|
# ── 7.3. Update Agent Skills + Mengto scroll skills + superpowers
|
||||||
# Both re-fetched at the SAME pinned commit (never advances the pin) via
|
# (pinned commit) — all three re-fetched at the SAME pinned commit
|
||||||
# the shared lib/vendor-skills.sh helper — see install-plugins.sh Step 8e.
|
# (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)..."
|
||||||
# shellcheck source=lib/vendor-skills.sh disable=SC1091
|
# shellcheck source=lib/vendor-skills.sh disable=SC1091
|
||||||
@@ -388,6 +389,9 @@ vendor_pinned_skills agent-skills refresh
|
|||||||
echo ""
|
echo ""
|
||||||
echo "── Updating Mengto scroll skills (MengTo/Skills)..."
|
echo "── Updating Mengto scroll skills (MengTo/Skills)..."
|
||||||
vendor_pinned_skills mengto-skills refresh
|
vendor_pinned_skills mengto-skills refresh
|
||||||
|
echo ""
|
||||||
|
echo "── Updating superpowers skills (obra/superpowers)..."
|
||||||
|
vendor_pinned_skills superpowers refresh
|
||||||
|
|
||||||
# ── 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