Upstream supersedes `@21st-dev/magic` with `@21st-dev/cli` (bin `21st`):
same endpoint, `21st login` in place of an API key, no MCP process loaded
into every session.
- install-plugins.sh Step 8.7: `npm i -g @21st-dev/cli` (pinned in
plugins.lock.json), staged `21st skills install`, TTY-only login offer,
pack disabled by default. update-all.sh 7.4 refreshes both.
- The documented `21st install-skill` cannot be used: the installer refuses
to follow a symlink on the target path and `~/.claude/skills` is one. The
install runs under a throwaway HOME and the result moves into
skills-external/21st-* (gitignored), symlinked on demand.
- toggle-external.sh manages `21st` as a pack (names globbed from
skills-external/21st-*, parked under plain names). `magic` is gone.
- The 5 design skills join design/web/web-full/full and MANAGED_EXTERNALS;
21st-registry and 21st-design-sync stay parked. MANAGED_MCPS is now empty
and profile.sh's dead magic branches are removed.
- Design gate: GATE-BLOCK gains `21st` (required-manual, magic's old slot)
and `21st-ui-build`; PATH repair extended to the npm global bin.
- settings.json: the 4 mcp__magic__* ask entries go; the outward-facing
21st verbs land in autoMode.soft_deny, the tier that holds under auto
mode (LRN-153).
- Docs: README, CLAUDE.global.md, design-gate.md, profile SKILL.md,
.env.example, .gitleaks.toml, link.sh. BDR-093, LRN-158.
Tests: profile-set-managed 17/17, make test green except 2 pre-existing
gitflow FAILs (gitleaks binary absent on this host), shellcheck clean.
Partition Claude skills by purpose: design, dev, qa, audit, minimal.
Toggles symlinks between skills/ and skills-disabled/ to keep only
the skills relevant to the current kind of work.
Trigger: "profile", "skill profile", "design profile", "qa profile",
"switch to design", "set profile", "active profile", "quel profil",
"profil design", "active les skills design", "désactive gstack",
"réduire le bruit gstack".
list | show <name> | current | apply <name> | set <name> | reset | gstack on|off | diff <a> <b>
Bash
Read
profile
Activate a curated subset of skills for a specific kind of work — instead of
carrying every gstack + personal skill in every session.
When to invoke
User asks to switch profile (set design, profile dev, quel profil actif).
User wants to see what's in a profile (profile show qa).
User wants to compare profiles (profile diff design qa).
User asks to "reduce gstack noise" or "only design skills".
Profiles available
Profile
Use case
web
Public website work — frontend + content + light dev
symlink move skills/ ↔ skills-disabled/<name> (no prefix)
external
symlink move skills/ ↔ skills-disabled/<name>
plugin@<marketplace>
claude plugin enable|disable <name>@<marketplace> (auto)
mcp
advisory — prints manual claude mcp add … command (no server is managed today: MANAGED_MCPS is empty since 21st.dev moved to a CLI)
cli
advisory only — reports installed/not-installed
Always-on plugins (security-guidance, superpowers) are
protected — set will refuse to disable them even if the profile omits them.
Managed plugins that set may disable when not in profile:
ui-ux-pro-max@ui-ux-pro-max-skill, plugin-dev@claude-code-plugins,
pr-review-toolkit@claude-code-plugins. Other plugins are never auto-toggled.
Managed externals (emil-design-eng, frontend-design,
design-motion-principles, impeccable, and the five 21st design skills
21st-ui-build, 21st-ui-explore, 21st-ui-review, 21st-cli-use,
21st-ai) follow the same symmetry (BDR-079): set enables them when the
profile lists them (from parked state, or from skills-external/ if the
symlink never existed) and parks them when it does not — e.g. set backend
after design work turns emil and the 21st pack off. darwin-skill,
21st-registry, 21st-design-sync and any other unlisted external are never
auto-touched. gstack works the same
all the way down: a profile listing gstack skills while the whole pack is
off (via toggle-external.sh) re-enables JUST those skills on demand.
Commands
# List available profiles
bash "$HOME/.claude/lib/profile.sh" list
# Show profile contents + per-skill status
bash "$HOME/.claude/lib/profile.sh" show <name>
# Detect which profile is currently active
bash "$HOME/.claude/lib/profile.sh" current
# Enable skills in profile (additive — keeps others enabled)
bash "$HOME/.claude/lib/profile.sh" apply <name>
# Enable only skills in profile (disables non-listed gstack skills)
bash "$HOME/.claude/lib/profile.sh"set <name>
# Re-enable every gstack skill (undo any set/apply) — resets active label to "none"
bash "$HOME/.claude/lib/profile.sh" reset
# Toggle gstack only, keeping the active-profile label intact
bash "$HOME/.claude/lib/profile.sh" gstack on # re-enable ALL gstack on top of current profile
bash "$HOME/.claude/lib/profile.sh" gstack off # disable gstack skills not in the active profile# Compare two profiles
bash "$HOME/.claude/lib/profile.sh" diff <a> <b>
Execution
Run lib/profile.sh with the user's arguments. If user passed nothing, default
to list. If user named a profile without a verb (e.g. "profile design"),
treat it as set <name> — but confirm first because set disables other gstack
skills.
State may be PARTIAL. Run current to show what actually took; name the failed item from the script's output
Offer reset as recovery to a known state; never blind-rerun set on top of partial state
Plugin/MCP leg fails (marketplace/network) while symlink leg succeeded
Report the split state explicitly + print the manual claude plugin/claude mcp command for the failed leg
—
current says none right after a successful set <name>
Contradiction — do not trust either; show the raw script output to the user
Known failure family (BLK: symlink resolution in cmd_current) — report, don't hand-patch
Output policy
After set / apply / reset / gstack on|off: show the count of skills
moved + tell the user to start a new Claude session to pick up the changes
(Claude scans skills/ at session start).
After current: report the active profile + match percentage.
After show: render the grouped output directly — no extra commentary unless
the user asks.
Tradeoffs to mention if asked
gstack skills still depend on ~/.claude/skills/gstack/bin/ for telemetry,
update-check, learnings — script doesn't touch that infra. Disabled skills
are just hidden from Claude Code's scanner; the gstack repo stays installed.
Profile changes DO toggle the managed Claude Code plugins (ui-ux-pro-max,
plugin-dev, pr-review-toolkit) and the managed external packs
(emil-design-eng, frontend-design, design-motion-principles, impeccable,
the 21st design skills) — in BOTH directions: set enables what the profile
lists and disables the managed leftovers it doesn't (BDR-008, BDR-079).
Anything outside those allowlists stays manual: claude plugin enable|disable, bash lib/toggle-external.sh enable|disable <tool>.
set is destructive in the sense that it disables non-listed gstack skills.
Use apply if the user wants additive behavior.