feat(mods): model-router active in every session — skills-dir link, mods suite, doctor section, CLAUDE.md

Tracked relative symlink skills/model-router -> ../mods/model-router: Claude
Code loads the mod in place as model-router@skills-dir wherever link.sh
links ~/.claude/skills (no CLAUDE_CODE_PLUGIN_DIRS: absolute paths in the
tracked settings.json). Engine-laid mods/*/tsconfig.json gitignored.
lib/tests/mods.test.sh: manifest name, link target, claude plugin validate
and test per mod, capability-probed, time-bounded, SKIP with reason.
doctor.sh: fail-soft Mods section (link by -ef, one guarded plugin list).
CLAUDE.md: mods/ section (loading, per-machine enabled:false switch,
dev-copy shadowing, tests).
This commit is contained in:
bchanot
2026-10-09 10:51:13 +02:00
parent 24e180ade0
commit 6430ac65ec
5 changed files with 172 additions and 0 deletions
+58
View File
@@ -155,6 +155,64 @@ unset _dv_active_profile _dv_profile_file
echo ""
# ────────────────────────────────────────────────────────────
# 2c. Mods (mods/<name>/ plugins, loaded through the tracked
# skills/<name> symlink as <name>@skills-dir). Fail-soft: a missing link
# is info (the user may have removed it on purpose), never an error.
# ────────────────────────────────────────────────────────────
echo "── Mods ──"
# Prints enabled|disabled|absent|unknown for $1 read from the JSON on stdin;
# always exits 0 so a bad payload cannot abort doctor under set -e.
mod_state() {
python3 -c '
import json, sys
try:
rows = json.load(sys.stdin)
row = [r for r in rows if r.get("id") == sys.argv[1] + "@skills-dir"]
print("absent" if not row else
"enabled" if row[0].get("enabled") is True else "disabled")
except Exception:
print("unknown")
' "$1" 2>/dev/null || true
}
_mods_list=""
if command -v claude &>/dev/null; then
if ! _mods_list=$(claude plugin list --json 2>/dev/null); then
warn "mods: claude plugin list failed — load state not checked"
_mods_list=""
fi
fi
_mods_seen=0
for _mod_manifest in "$REPO"/mods/*/.claude-plugin/plugin.json; do
[ -f "$_mod_manifest" ] || continue
_mods_seen=$((_mods_seen + 1))
_mod=$(basename "$(dirname "$(dirname "$_mod_manifest")")")
_mod_link="$HOME/.claude/skills/$_mod"
if ! { [ -L "$_mod_link" ] || [ -e "$_mod_link" ]; }; then
info "mod $_mod: not linked (skills/$_mod absent) — git checkout skills/$_mod if wanted"
continue
fi
if [ "$_mod_link" -ef "$REPO/mods/$_mod" ]; then
pass "mod $_mod: loading link ~/.claude/skills/$_mod"
else
warn "mod $_mod: ~/.claude/skills/$_mod does not resolve to $REPO/mods/$_mod"
fi
[ -n "$_mods_list" ] || continue
case "$(printf '%s' "$_mods_list" | mod_state "$_mod")" in
enabled) pass "mod $_mod: enabled as $_mod@skills-dir" ;;
disabled) warn "mod $_mod: disabled (\"$_mod@skills-dir\": false in enabledPlugins)" ;;
absent) warn "mod $_mod: not listed as @skills-dir — run: claude plugin validate mods/$_mod (policy, manifest or name conflict)" ;;
*) warn "mod $_mod: claude plugin list output not understood" ;;
esac
done
[ "$_mods_seen" -gt 0 ] || info "no mods"
unset _mods_list _mods_seen _mod_manifest _mod _mod_link
echo ""
# ── Playwright browsers (read-only report; NOT nested under gstack — 2 of
# the 3 registered installs are gsd-pi, not gstack) ──
echo "── Playwright browsers ──"