Merge feature/profile-managed-externals into develop
This commit is contained in:
@@ -1065,3 +1065,6 @@ Supersedes BDR-076 scope + amends BDR-066. Doctrine: session model (Fable) = mai
|
|||||||
### BDR-078 — ctx7 coverage: central fast-libs list + once-per-session reminder hook; every code path covered [accepted] (2026-07-20)
|
### BDR-078 — ctx7 coverage: central fast-libs list + once-per-session reminder hook; every code path covered [accepted] (2026-07-20)
|
||||||
Refines BDR-053 (single surface). Audit 2026-07-20: coverage PARTIAL — find-docs fired on user doc-questions only; ship-feature 0c / init-project 5c pre-fetched; /feat //bugfix executors + ad-hoc coding NEVER consulted ctx7; fast-libs list hardcoded 3× (drift risk). 4 closures shipped: (a) find-docs description += BEFORE-writing-code trigger (fast-moving lib, even without doc question, unless fresh cache) + cache-first rule in body (tee fetched docs to .ctx7-cache/); (b) feater+bugfixer briefs += fast-lib docs rule — read fresh `.ctx7-cache/<lib>*.md`, else `npx ctx7@latest` fetch max 2 topics, else `ctx7 cache miss: <lib>` in NOTES + proceed (executors lack Skill tool → Bash path); (c) hooks/ctx7-reminder.sh UserPromptSubmit — ONE fire/session (sentinel on session_id), only when project manifest carries fast-libs; reports cache state; skips <task-notification> turns; always exit 0; (d) lib/fast-libs.sh = SINGLE SOURCE (detect / cache-status verbs, JS package.json anchored full-key match + Python requirements/pyproject, 7-day freshness, LC_ALL=C sort locale-independent) consumed by hook + 3 pipeline skills + 2 briefs. 2nd session surface DELIBERATE, not a BDR-053 reversal: 053 killed a 490-tok ALWAYS-ON rule duplicate; hook costs ~0 quiet, 1 line once when fast-libs present. Alternatives rejected: PreToolUse Edit/Write gate (fires per-edit = noise); description-only fix (probabilistic, executors unreachable). Tests: lib/tests/fast-libs.test.sh 11 checks (anchored/near-miss/py/none, cache fresh/stale/missing, hook fire/sentinel/quiet×2); shellcheck + full make test green. Branch feature/ctx7-coverage, unmerged (human gate).
|
Refines BDR-053 (single surface). Audit 2026-07-20: coverage PARTIAL — find-docs fired on user doc-questions only; ship-feature 0c / init-project 5c pre-fetched; /feat //bugfix executors + ad-hoc coding NEVER consulted ctx7; fast-libs list hardcoded 3× (drift risk). 4 closures shipped: (a) find-docs description += BEFORE-writing-code trigger (fast-moving lib, even without doc question, unless fresh cache) + cache-first rule in body (tee fetched docs to .ctx7-cache/); (b) feater+bugfixer briefs += fast-lib docs rule — read fresh `.ctx7-cache/<lib>*.md`, else `npx ctx7@latest` fetch max 2 topics, else `ctx7 cache miss: <lib>` in NOTES + proceed (executors lack Skill tool → Bash path); (c) hooks/ctx7-reminder.sh UserPromptSubmit — ONE fire/session (sentinel on session_id), only when project manifest carries fast-libs; reports cache state; skips <task-notification> turns; always exit 0; (d) lib/fast-libs.sh = SINGLE SOURCE (detect / cache-status verbs, JS package.json anchored full-key match + Python requirements/pyproject, 7-day freshness, LC_ALL=C sort locale-independent) consumed by hook + 3 pipeline skills + 2 briefs. 2nd session surface DELIBERATE, not a BDR-053 reversal: 053 killed a 490-tok ALWAYS-ON rule duplicate; hook costs ~0 quiet, 1 line once when fast-libs present. Alternatives rejected: PreToolUse Edit/Write gate (fires per-edit = noise); description-only fix (probabilistic, executors unreachable). Tests: lib/tests/fast-libs.test.sh 11 checks (anchored/near-miss/py/none, cache fresh/stale/missing, hook fire/sentinel/quiet×2); shellcheck + full make test green. Branch feature/ctx7-coverage, unmerged (human gate).
|
||||||
Amendment (same session): skills/find-docs = machine-owned dist (gitignored, ctx7 regenerates on fresh clone) → durable copy of closure (a) lives in install-plugins.sh STEP ctx7 (idempotent grep-guarded python patch, fixture-verified); live SKILL.md carries the same edit uncommitted by design.
|
Amendment (same session): skills/find-docs = machine-owned dist (gitignored, ctx7 regenerates on fresh clone) → durable copy of closure (a) lives in install-plugins.sh STEP ctx7 (idempotent grep-guarded python patch, fixture-verified); live SKILL.md carries the same edit uncommitted by design.
|
||||||
|
|
||||||
|
### BDR-079 — profile `set` symmetric on managed externals + MCPs [accepted] (2026-07-20)
|
||||||
|
Audit (user ask "profile toggles externals both ways?"): ASYMMETRIC. Enable side OK — gstack on-demand from submodule when pack off (shared `skills-disabled/gstack__*` convention with toggle-external.sh, interoperable), externals restored from parked, magic delegated to toggle-external. Disable side MISSING: `cmd_set` trimmed only gstack + MANAGED_PLUGINS → `set backend` left emil/frontend-design/design-motion/impeccable active + magic registered; SKILL.md claimed both-ways toggle (true only at enable). Shipped: (1) `MANAGED_EXTERNALS` (emil-design-eng, frontend-design, design-motion-principles, impeccable = exact union of profile `external` usage; darwin-skill excluded — not task-type-driven) + `MANAGED_MCPS` (magic) allowlists, same doctrine as MANAGED_PLUGINS; (2) cmd_set refactored to 4 trim helpers (`disable_{gstack,plugins,externals,mcps}_not_in`) — symmetric, nothing outside allowlists ever auto-touched; (3) enable_skill external += from-source fallback (`ln -sf skills-external/<name>`, mirrors toggle-external) — closes the "missing symlink" warn; (4) stale usage() NOTE ("NOT toggled automatically") + SKILL.md fixed. Hermetic test profile-set-managed.test.sh 16 checks: fixture repo (both *_REPO_OVERRIDE), fake `claude` shim on PATH logging calls + flat-file MCP registry — gstack on-demand, external from-source, park/restore round-trip, magic add/remove calls, non-managed untouched. shellcheck + make test green. Branch feature/profile-managed-externals, unmerged (human gate).
|
||||||
|
|||||||
@@ -417,3 +417,4 @@ rules:
|
|||||||
## 2026-07-20
|
## 2026-07-20
|
||||||
- ctx7 coverage audit (user ask "ctx7 appelé à chaque techno ?") → verdict PARTIAL. 4 gaps: find-docs question-only, /feat //bugfix executors blind, ad-hoc coding uncovered, fast-libs hardcoded 3×. All 4 closed → BDR-078 (fast-libs.sh single source + ctx7-reminder hook + description trigger + executor-brief rule). fast-libs test 11/0, make test + review-guards green. feature/ctx7-coverage, UNMERGED.
|
- ctx7 coverage audit (user ask "ctx7 appelé à chaque techno ?") → verdict PARTIAL. 4 gaps: find-docs question-only, /feat //bugfix executors blind, ad-hoc coding uncovered, fast-libs hardcoded 3×. All 4 closed → BDR-078 (fast-libs.sh single source + ctx7-reminder hook + description trigger + executor-brief rule). fast-libs test 11/0, make test + review-guards green. feature/ctx7-coverage, UNMERGED.
|
||||||
- v1.2.0 cut + pushed (release-candidate flow: prep/finish via release-executor, tag on main 51b6572). CHANGELOG backfilled at prep: 10 entries added to Unreleased (plan-challenge, seo-data verbs, model-tiering v2, integrity pass, safe_fetch/url-guard) — was ctx7-only. /doc full post-release: README model-routing table v1→v2 reframe + ctx7 two-surface wording, chore/doc-sync-v1.2.0 merged. All pushed on explicit go.
|
- v1.2.0 cut + pushed (release-candidate flow: prep/finish via release-executor, tag on main 51b6572). CHANGELOG backfilled at prep: 10 entries added to Unreleased (plan-challenge, seo-data verbs, model-tiering v2, integrity pass, safe_fetch/url-guard) — was ctx7-only. /doc full post-release: README model-routing table v1→v2 reframe + ctx7 two-surface wording, chore/doc-sync-v1.2.0 merged. All pushed on explicit go.
|
||||||
|
- profile↔toggle-external audit (user) → enable side already symmetric (gstack on-demand LIVE), disable side missing → BDR-079: MANAGED_EXTERNALS+MANAGED_MCPS trim at set, external from-source fallback, 16-check hermetic test (claude shim). feature/profile-managed-externals, UNMERGED.
|
||||||
|
|||||||
@@ -1,5 +1,23 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
## 2026-07-20 — profile ↔ toggle-external symmetry (feature/profile-managed-externals, BDR-079)
|
||||||
|
Audit verdict: gstack on-demand + design enable already work; DISABLE side
|
||||||
|
missing — `set backend` leaves emil/frontend-design/design-motion/impeccable
|
||||||
|
active + magic registered. Doc claims auto-toggle both ways (only enable true).
|
||||||
|
- [x] profile.sh: `MANAGED_EXTERNALS` (emil-design-eng, frontend-design,
|
||||||
|
design-motion-principles, impeccable — union of profile usage) +
|
||||||
|
`MANAGED_MCPS` (magic) allowlists; cmd_set refactored to 4 trim
|
||||||
|
helpers (disable_{gstack,plugins,externals,mcps}_not_in).
|
||||||
|
- [x] profile.sh enable_skill external: from-source fallback
|
||||||
|
(`ln -sf skills-external/<name>`) mirroring toggle-external.
|
||||||
|
- [x] Texts: cmd_set info line, usage() NOTE (stale "NOT toggled
|
||||||
|
automatically"), header; skills/profile/SKILL.md Mechanism+tradeoffs.
|
||||||
|
- [x] Hermetic test lib/tests/profile-set-managed.test.sh — 16/0: gstack
|
||||||
|
on-demand, external from-source, park/restore round-trip, magic
|
||||||
|
add/remove via claude shim, non-managed untouched.
|
||||||
|
- [x] Gate: shellcheck OK + make test exit 0 (review-guards 5/0). BDR-079 +
|
||||||
|
journal + CHANGELOG done. Committed on branch, NO merge (human gate).
|
||||||
|
|
||||||
## 2026-07-20 — ctx7 coverage extension (feature/ctx7-coverage, BDR-078)
|
## 2026-07-20 — ctx7 coverage extension (feature/ctx7-coverage, BDR-078)
|
||||||
Close the 4 gaps from the ctx7 coverage audit: /feat //bugfix + ad-hoc coding
|
Close the 4 gaps from the ctx7 coverage audit: /feat //bugfix + ad-hoc coding
|
||||||
never consult ctx7; fast-libs list hardcoded 3×; zero deterministic backstop.
|
never consult ctx7; fast-libs list hardcoded 3×; zero deterministic backstop.
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Architecture — claude-config
|
||||||
|
|
||||||
|
Repo layout and structural principles. Command workflows live in
|
||||||
|
[`USAGE.md`](./USAGE.md); version history in [`CHANGELOG.md`](./CHANGELOG.md).
|
||||||
|
|
||||||
|
## Project layout
|
||||||
|
|
||||||
|
```
|
||||||
|
claude-config/
|
||||||
|
├── CLAUDE.global.md # Global coding preferences — deployed as ~/.claude/CLAUDE.md
|
||||||
|
├── CLAUDE.md # Project-scope instructions (this repo only)
|
||||||
|
├── settings.json # Global permissions (deny / ask / allow rules)
|
||||||
|
├── install.sh # Bootstrap: Claude Code CLI + auth + submodules + link + plugins
|
||||||
|
├── install-plugins.sh # One-shot installer: prerequisites + all plugins
|
||||||
|
├── link.sh # Symlinks this repo into ~/.claude/
|
||||||
|
├── doctor.sh # Setup diagnostic
|
||||||
|
├── update-all.sh # One-command update for all components
|
||||||
|
├── Makefile # Unified entry point: make install / doctor / update
|
||||||
|
├── plugins.lock.json # Version pinning for non-marketplace dependencies
|
||||||
|
├── hooks/ # Session start, statusline, RTK rewrite + ctx7 + design-toolchain reminders
|
||||||
|
├── agents/ # Execution units called by skills (never invoked directly)
|
||||||
|
├── skills/ # Entry points invoked via /skill-name
|
||||||
|
├── skills-external/ # Vendored skill packs (gstack submodule + installer-fetched design packs)
|
||||||
|
├── templates/ # Per-project templates (CLAUDE.md, settings, memory registries, deploy runbook, gitignore)
|
||||||
|
└── lib/ # Shared shell libs (gitflow, profiles, commit helpers, archetypes, tests)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture principles
|
||||||
|
|
||||||
|
- `skills/` = entry points you invoke via `/skill-name`
|
||||||
|
- `agents/` = execution units called by skills (never invoked directly by user)
|
||||||
|
- `templates/` = symlinked to `~/.claude/templates/` — copy into projects via `/onboard` or manually
|
||||||
|
- **Graphify** builds a knowledge graph of any codebase (`/graphify query`), producing a navigable wiki in `graphify-out/wiki/`. This map helps Claude understand project structure, find relevant code faster, and reason across files. Essential for large-scope tasks (multi-file features, complex bugs, architectural changes). Small tasks should skip it and read files directly.
|
||||||
@@ -6,6 +6,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Profile switches now toggle external packs and MCPs both ways (BDR-079)** — `profile.sh set` was asymmetric: it enabled what a profile listed (including gstack skills on demand when the whole pack is off, and the `magic` MCP) but never disabled the managed leftovers, so `set backend` after design work kept emil-design-eng / frontend-design / design-motion-principles / impeccable active and magic registered. `set` now trims managed externals (`MANAGED_EXTERNALS`) and managed MCPs (`MANAGED_MCPS`, delegated to `toggle-external.sh`) not listed in the profile — same allowlist doctrine as `MANAGED_PLUGINS`, nothing outside the allowlists is ever auto-touched (darwin-skill stays manual). Also: an `external` entry whose symlink never existed is now created from `skills-external/` (mirroring toggle-external's from-source path), and the stale "NOT toggled automatically" note in `profile.sh` usage was corrected. Covered by a hermetic 16-check test (`lib/tests/profile-set-managed.test.sh`) with a fake `claude` shim.
|
||||||
|
|
||||||
## [1.2.1] — 2026-07-20
|
## [1.2.1] — 2026-07-20
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -11,33 +11,11 @@ Global Claude Code configuration — agents, skills, plugins, and project templa
|
|||||||
|
|
||||||
This repo is your personal Claude Code setup, versioned and reproducible across machines.
|
This repo is your personal Claude Code setup, versioned and reproducible across machines.
|
||||||
|
|
||||||
```
|
See [`ARCHITECTURE.md`](./ARCHITECTURE.md) for the full project layout and
|
||||||
claude-config/
|
structural principles (skills = entry points, agents = execution units,
|
||||||
├── CLAUDE.global.md # Global coding preferences — deployed as ~/.claude/CLAUDE.md
|
templates = per-project scaffolding, graphify = codebase knowledge graph).
|
||||||
├── CLAUDE.md # Project-scope instructions (this repo only)
|
|
||||||
├── settings.json # Global permissions (deny / ask / allow rules)
|
|
||||||
├── install.sh # Bootstrap: Claude Code CLI + auth + submodules + link + plugins
|
|
||||||
├── install-plugins.sh # One-shot installer: prerequisites + all plugins
|
|
||||||
├── link.sh # Symlinks this repo into ~/.claude/
|
|
||||||
├── doctor.sh # Setup diagnostic
|
|
||||||
├── update-all.sh # One-command update for all components
|
|
||||||
├── Makefile # Unified entry point: make install / doctor / update
|
|
||||||
├── plugins.lock.json # Version pinning for non-marketplace dependencies
|
|
||||||
├── hooks/ # Session start, statusline, RTK rewrite + ctx7 + design-toolchain reminders
|
|
||||||
├── agents/ # Execution units called by skills (never invoked directly)
|
|
||||||
├── skills/ # Entry points invoked via /skill-name
|
|
||||||
├── skills-external/ # Vendored skill packs (gstack submodule + installer-fetched design packs)
|
|
||||||
├── templates/ # Per-project templates (CLAUDE.md, settings, memory registries, deploy runbook, gitignore)
|
|
||||||
└── lib/ # Shared shell libs (gitflow, profiles, commit helpers, archetypes, tests)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Architecture principle:**
|
### Agent model routing (model-tiering v2)
|
||||||
- `skills/` = entry points you invoke via `/skill-name`
|
|
||||||
- `agents/` = execution units called by skills (never invoked directly by user)
|
|
||||||
- `templates/` = symlinked to `~/.claude/templates/` — copy into projects via `/onboard` or manually
|
|
||||||
- **Graphify** builds a knowledge graph of any codebase (`/graphify query`), producing a navigable wiki in `graphify-out/wiki/`. This map helps Claude understand project structure, find relevant code faster, and reason across files. Essential for large-scope tasks (multi-file features, complex bugs, architectural changes). Small tasks should skip it and read files directly.
|
|
||||||
|
|
||||||
### Agent model routing (BDR-076/077 — model-tiering v2)
|
|
||||||
|
|
||||||
Doctrine: the session model (Fable) does main-loop reflection ONLY —
|
Doctrine: the session model (Fable) does main-loop reflection ONLY —
|
||||||
brainstorm, plan, contract, audit judgment, gates, loop decisions — enforced
|
brainstorm, plan, contract, audit judgment, gates, loop decisions — enforced
|
||||||
@@ -90,11 +68,11 @@ The plugins step logs to `install-YYYYMMDD-HHMMSS.log`.
|
|||||||
|
|
||||||
**Optional — Context7** (fast doc lookup for React / Next.js / Prisma…): the plugins
|
**Optional — Context7** (fast doc lookup for React / Next.js / Prisma…): the plugins
|
||||||
step installs the `ctx7` CLI and wires it into Claude Code. The doc-fetch surface is
|
step installs the `ctx7` CLI and wires it into Claude Code. The doc-fetch surface is
|
||||||
the `find-docs` skill alone (BDR-053 — the generated `rules/context7.md` is purged by
|
the `find-docs` skill alone (the generated `rules/context7.md` is purged by
|
||||||
design; if you run `ctx7 setup` manually, delete that rule or re-run `make plugin`).
|
design; if you run `ctx7 setup` manually, delete that rule or re-run `make plugin`).
|
||||||
A once-per-session `ctx7-reminder` hook nudges toward it when the current project
|
A once-per-session `ctx7-reminder` hook nudges toward it when the current project
|
||||||
carries fast-moving libs (`lib/fast-libs.sh`) — a scoped second surface refining
|
carries fast-moving libs (`lib/fast-libs.sh`) — a scoped second surface, a
|
||||||
BDR-053, not reversing it (BDR-078).
|
refinement of the single-surface rule, not a reversal.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ctx7 login # optional: OAuth / API key for higher rate limits
|
ctx7 login # optional: OAuth / API key for higher rate limits
|
||||||
@@ -160,7 +138,7 @@ a different package, ships its own conflicting `graphify` bin) — see
|
|||||||
| `/web-validate` | W3C HTML/CSS validity + WCAG 2.1 accessibility audit |
|
| `/web-validate` | W3C HTML/CSS validity + WCAG 2.1 accessibility audit |
|
||||||
| `/geo` | GEO-only audit — AI-search visibility (ChatGPT, Perplexity, Claude, Gemini…) |
|
| `/geo` | GEO-only audit — AI-search visibility (ChatGPT, Perplexity, Claude, Gemini…) |
|
||||||
| `/client-handover` | Final project delivery — audits + branded deliverable (Markdown / HTML / PDF) |
|
| `/client-handover` | Final project delivery — audits + branded deliverable (Markdown / HTML / PDF) |
|
||||||
| `/profile` | Activate a skill profile (design / dev / qa / audit / minimal) |
|
| `/profile` | Activate a skill profile (web / seo / web-full / full / backend / design / dev / qa / audit / minimal) |
|
||||||
| `/tour` | Grouped all-axes sweep — cleanup + security + reconcile + doc, fix and loop until clean |
|
| `/tour` | Grouped all-axes sweep — cleanup + security + reconcile + doc, fix and loop until clean |
|
||||||
|
|
||||||
> This table lists personal skills. Gstack skills (investigate, review, retro,
|
> This table lists personal skills. Gstack skills (investigate, review, retro,
|
||||||
@@ -236,7 +214,7 @@ See [`templates/settings/SETTINGS.md`](templates/settings/SETTINGS.md) for the f
|
|||||||
`~/.claude.json` (or the project's `.mcp.json`) — if you pass the real secret
|
`~/.claude.json` (or the project's `.mcp.json`) — if you pass the real secret
|
||||||
on that command line, it materializes as a second plaintext copy outside
|
on that command line, it materializes as a second plaintext copy outside
|
||||||
`~/.claude/.env`, invisible to the repo's `.gitignore`/allowlist reach (this
|
`~/.claude/.env`, invisible to the repo's `.gitignore`/allowlist reach (this
|
||||||
bit us once: job7/BDR-026).
|
bit us once).
|
||||||
|
|
||||||
Claude Code expands `${VAR}` and `${VAR:-default}` in `mcpServers` config —
|
Claude Code expands `${VAR}` and `${VAR:-default}` in `mcpServers` config —
|
||||||
in `env`, `command`, `args`, `url`, and `headers` — for both project (`.mcp.json`)
|
in `env`, `command`, `args`, `url`, and `headers` — for both project (`.mcp.json`)
|
||||||
@@ -273,7 +251,7 @@ can `POST` to it and that body is injected **verbatim** into the tool result
|
|||||||
the model consumes (job8 audit, `dist/utils/callback-server.js:36`). This is
|
the model consumes (job8 audit, `dist/utils/callback-server.js:36`). This is
|
||||||
in the third-party package's code, not this repo's config — **we don't patch
|
in the third-party package's code, not this repo's config — **we don't patch
|
||||||
it**. The mitigation lives entirely on our side: `settings.json`
|
it**. The mitigation lives entirely on our side: `settings.json`
|
||||||
`permissions.ask` explicitly lists all 4 `mcp__magic__*` tools ([[BDR-059]]),
|
`permissions.ask` explicitly lists all 4 `mcp__magic__*` tools,
|
||||||
so every call — builder included — requires a live confirmation and can
|
so every call — builder included — requires a live confirmation and can
|
||||||
never auto-execute. Don't allowlist
|
never auto-execute. Don't allowlist
|
||||||
`21st_magic_component_builder` or `21st_magic_component_refiner` (arbitrary
|
`21st_magic_component_builder` or `21st_magic_component_refiner` (arbitrary
|
||||||
@@ -299,10 +277,10 @@ make plugin # install plugins only
|
|||||||
make link # create/update symlinks into ~/.claude/
|
make link # create/update symlinks into ~/.claude/
|
||||||
make doctor # diagnostic
|
make doctor # diagnostic
|
||||||
make update # update Claude Code, config, submodules, plugins, and verify
|
make update # update Claude Code, config, submodules, plugins, and verify
|
||||||
make test # run deterministic tests (lib/tests/*.test.sh + lib/seo-data/*.test.sh + lib/gitflow-test.sh)
|
make test # run deterministic tests (lib/tests/*.test.sh + lib/seo-data/*.test.sh + lib/gitflow-test.sh + lib/tests/run-*.sh)
|
||||||
make onboard # onboard an existing project (run from its dir)
|
make onboard # onboard an existing project (run from its dir)
|
||||||
make seo-connect # connect a Google account for /seo FULL (OAuth consent)
|
make seo-connect # connect a Google account for /seo FULL (OAuth consent)
|
||||||
make profile cmd="set X" # activate a skill profile (design/dev/qa/audit/minimal/full)
|
make profile cmd="set X" # activate a skill profile (web/seo/web-full/full/backend/design/dev/qa/audit/minimal)
|
||||||
make profile-list # list skill profiles
|
make profile-list # list skill profiles
|
||||||
make profile-current # show the active profile
|
make profile-current # show the active profile
|
||||||
make profile-reset # re-enable all gstack skills
|
make profile-reset # re-enable all gstack skills
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ Tu veux...
|
|||||||
| `/pdf-translate` | Traduire un PDF vers une autre langue | Sortie HTML fidèle (images, layout, style préservés) |
|
| `/pdf-translate` | Traduire un PDF vers une autre langue | Sortie HTML fidèle (images, layout, style préservés) |
|
||||||
| `/impeccable` | Audit/polish design + détecteur anti-slop déterministe | 23 verbes ; `npx impeccable detect` (exit 0/2) |
|
| `/impeccable` | Audit/polish design + détecteur anti-slop déterministe | 23 verbes ; `npx impeccable detect` (exit 0/2) |
|
||||||
| `/tour` | Sweep groupé sur un ou plusieurs projets | Sécu + nettoyage + reconcile + doc, boucle jusqu'à un pass propre |
|
| `/tour` | Sweep groupé sur un ou plusieurs projets | Sécu + nettoyage + reconcile + doc, boucle jusqu'à un pass propre |
|
||||||
| `/profile` | Changer le profil de skills | design / dev / qa / audit / minimal |
|
| `/profile` | Changer le profil de skills | web / seo / web-full / full / backend / design / dev / qa / audit / minimal |
|
||||||
|
|
||||||
> Cette table couvre les skills personnels principaux. Les plugins (gstack,
|
> Cette table couvre les skills personnels principaux. Les plugins (gstack,
|
||||||
> pr-review-toolkit…) et marketplaces externes en ajoutent beaucoup d'autres —
|
> pr-review-toolkit…) et marketplaces externes en ajoutent beaucoup d'autres —
|
||||||
|
|||||||
+80
-15
@@ -14,6 +14,9 @@
|
|||||||
# - MCPs: delegated to lib/toggle-external.sh for known servers (magic),
|
# - MCPs: delegated to lib/toggle-external.sh for known servers (magic),
|
||||||
# advisory otherwise
|
# advisory otherwise
|
||||||
# - CLIs: advisory only (rtk, gsd, ctx7, graphify — installed externally)
|
# - CLIs: advisory only (rtk, gsd, ctx7, graphify — installed externally)
|
||||||
|
# - `set` is SYMMETRIC on managed items (BDR-079): plugins, external packs
|
||||||
|
# and MCPs in the MANAGED_* allowlists are disabled when the profile
|
||||||
|
# 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,
|
||||||
# superpowers + rtk hook + .claude internal. The script refuses to disable
|
# superpowers + rtk hook + .claude internal. The script refuses to disable
|
||||||
@@ -61,6 +64,23 @@ MANAGED_PLUGINS=(
|
|||||||
"pr-review-toolkit@claude-code-plugins"
|
"pr-review-toolkit@claude-code-plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# External skill packs that are toggle-managed by `set` — same allowlist
|
||||||
|
# doctrine as MANAGED_PLUGINS: listed here only when the enabled state is
|
||||||
|
# task-type-driven. `set` disables these when the profile does not list
|
||||||
|
# them; anything else external (e.g. darwin-skill) is never auto-touched.
|
||||||
|
MANAGED_EXTERNALS=(
|
||||||
|
emil-design-eng
|
||||||
|
frontend-design
|
||||||
|
design-motion-principles
|
||||||
|
impeccable
|
||||||
|
)
|
||||||
|
|
||||||
|
# MCP servers that are toggle-managed by `set`, both ways (enable AND
|
||||||
|
# disable), delegated to lib/toggle-external.sh. Same allowlist doctrine.
|
||||||
|
MANAGED_MCPS=(
|
||||||
|
magic
|
||||||
|
)
|
||||||
|
|
||||||
# Plugins that MUST stay enabled — `set` will refuse to disable these even if
|
# Plugins that MUST stay enabled — `set` will refuse to disable these even if
|
||||||
# they're not in the profile. (Defensive: belt-and-suspenders alongside
|
# they're not in the profile. (Defensive: belt-and-suspenders alongside
|
||||||
# MANAGED_PLUGINS allowlist.)
|
# MANAGED_PLUGINS allowlist.)
|
||||||
@@ -271,6 +291,11 @@ enable_skill() {
|
|||||||
ok "enabled: $skill ($type)"
|
ok "enabled: $skill ($type)"
|
||||||
elif [ -e "$SKILLS_DIR/$skill" ]; then
|
elif [ -e "$SKILLS_DIR/$skill" ]; then
|
||||||
:
|
:
|
||||||
|
elif [ "$type" = external ] && [ -d "$REPO/skills-external/$skill" ]; then
|
||||||
|
# Symlink never created (or hand-removed): recreate it from the
|
||||||
|
# vendored pack — mirrors toggle-external.sh's from-source path.
|
||||||
|
ln -sf "$REPO/skills-external/$skill" "$SKILLS_DIR/$skill"
|
||||||
|
ok "enabled: $skill (external, symlink created)"
|
||||||
else
|
else
|
||||||
warn "missing: $skill ($type)"
|
warn "missing: $skill ($type)"
|
||||||
fi
|
fi
|
||||||
@@ -422,6 +447,48 @@ parked_gstack_count() {
|
|||||||
find "$DISABLED_DIR" -maxdepth 1 -name 'gstack__*' 2>/dev/null | wc -l | tr -d ' '
|
find "$DISABLED_DIR" -maxdepth 1 -name 'gstack__*' 2>/dev/null | wc -l | tr -d ' '
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── `set` trim helpers — one per managed category ─────────────
|
||||||
|
# Each disables the managed items NOT listed in the given profile. Allowlist
|
||||||
|
# doctrine: only MANAGED_* entries are ever auto-disabled.
|
||||||
|
|
||||||
|
disable_plugins_not_in() {
|
||||||
|
local prof="$1" keep_file p plugin_name marketplace
|
||||||
|
keep_file="$(mktemp)"
|
||||||
|
read_profile "$prof" \
|
||||||
|
| awk -F'\t' '$2 ~ /^plugin@/ { sub(/^plugin@/, "", $2); print $1"@"$2 }' \
|
||||||
|
| sort -u > "$keep_file"
|
||||||
|
for p in "${MANAGED_PLUGINS[@]}"; do
|
||||||
|
if ! grep -qx "$p" "$keep_file"; then
|
||||||
|
plugin_name="${p%@*}"
|
||||||
|
marketplace="${p#*@}"
|
||||||
|
disable_skill "$plugin_name" "plugin@${marketplace}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
rm -f "$keep_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_externals_not_in() {
|
||||||
|
local prof="$1" keep_file x
|
||||||
|
keep_file="$(mktemp)"
|
||||||
|
read_profile "$prof" | awk -F'\t' '$2 == "external" { print $1 }' \
|
||||||
|
| sort -u > "$keep_file"
|
||||||
|
for x in "${MANAGED_EXTERNALS[@]}"; do
|
||||||
|
grep -qx "$x" "$keep_file" || disable_skill "$x" external
|
||||||
|
done
|
||||||
|
rm -f "$keep_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_mcps_not_in() {
|
||||||
|
local prof="$1" keep_file s
|
||||||
|
keep_file="$(mktemp)"
|
||||||
|
read_profile "$prof" | awk -F'\t' '$2 == "mcp" { print $1 }' \
|
||||||
|
| sort -u > "$keep_file"
|
||||||
|
for s in "${MANAGED_MCPS[@]}"; do
|
||||||
|
grep -qx "$s" "$keep_file" || disable_skill "$s" mcp
|
||||||
|
done
|
||||||
|
rm -f "$keep_file"
|
||||||
|
}
|
||||||
|
|
||||||
# ── Commands ──────────────────────────────────────────────
|
# ── Commands ──────────────────────────────────────────────
|
||||||
|
|
||||||
cmd_list() {
|
cmd_list() {
|
||||||
@@ -506,24 +573,20 @@ cmd_apply() {
|
|||||||
|
|
||||||
cmd_set() {
|
cmd_set() {
|
||||||
local prof="$1"
|
local prof="$1"
|
||||||
info "Setting profile: $prof (exclusive — disables non-listed gstack skills + managed plugins)"
|
info "Setting profile: $prof (exclusive — disables non-listed gstack skills + managed plugins/externals/MCPs)"
|
||||||
|
|
||||||
# Disable gstack-origin skills not in profile.
|
# Disable gstack-origin skills not in profile.
|
||||||
disable_gstack_not_in "$prof"
|
disable_gstack_not_in "$prof"
|
||||||
|
|
||||||
# Disable managed plugins not in profile (PROTECTED_PLUGINS are excluded
|
# Disable managed plugins not in profile (PROTECTED_PLUGINS are excluded
|
||||||
# by disable_skill itself — belt and suspenders).
|
# by disable_skill itself — belt and suspenders).
|
||||||
local plugin_keep_file p plugin_name marketplace
|
disable_plugins_not_in "$prof"
|
||||||
plugin_keep_file="$(mktemp)"
|
|
||||||
read_profile "$prof" | awk -F'\t' '$2 ~ /^plugin@/ { sub(/^plugin@/, "", $2); print $1"@"$2 }' | sort -u > "$plugin_keep_file"
|
# Symmetry (BDR-079): a profile switch also parks the managed external
|
||||||
for p in "${MANAGED_PLUGINS[@]}"; do
|
# packs and unregisters the managed MCPs the new profile does not need —
|
||||||
if ! grep -qx "$p" "$plugin_keep_file"; then
|
# design leftovers (emil, magic…) no longer survive a `set backend`.
|
||||||
plugin_name="${p%@*}"
|
disable_externals_not_in "$prof"
|
||||||
marketplace="${p#*@}"
|
disable_mcps_not_in "$prof"
|
||||||
disable_skill "$plugin_name" "plugin@${marketplace}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
rm -f "$plugin_keep_file"
|
|
||||||
|
|
||||||
# Enable everything listed in the profile.
|
# Enable everything listed in the profile.
|
||||||
cmd_apply "$prof"
|
cmd_apply "$prof"
|
||||||
@@ -679,9 +742,11 @@ EXAMPLES:
|
|||||||
bash lib/profile.sh reset # restore everything
|
bash lib/profile.sh reset # restore everything
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
Plugin and MCP entries print advisory commands — they are NOT toggled
|
"set" toggles the MANAGED items automatically, both ways: plugins
|
||||||
automatically. Run "claude plugin enable|disable" or "claude mcp add|remove"
|
(ui-ux-pro-max, plugin-dev, pr-review-toolkit), external packs
|
||||||
yourself for those.
|
(emil-design-eng, frontend-design, design-motion-principles, impeccable)
|
||||||
|
and the magic MCP. Anything outside those allowlists stays advisory —
|
||||||
|
run "claude plugin enable|disable" or "claude mcp add|remove" yourself.
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# lib/tests/profile-set-managed.test.sh — `set` symmetry on managed
|
||||||
|
# externals + MCPs, gstack on-demand, external from-source (BDR-079).
|
||||||
|
# Hermetic: fixture repo via *_REPO_OVERRIDE + fake `claude` on PATH.
|
||||||
|
set -u
|
||||||
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
pass=0; fail=0
|
||||||
|
check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1));
|
||||||
|
printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; }
|
||||||
|
|
||||||
|
FX="$(mktemp -d)"; trap 'rm -rf "$FX"' EXIT
|
||||||
|
mkdir -p "$FX/skills" "$FX/skills-disabled" "$FX/lib/profiles" "$FX/bin" \
|
||||||
|
"$FX/skills-external/emil-design-eng" "$FX/skills-external/other-ext"
|
||||||
|
for g in gs-a gs-b gs-c; do
|
||||||
|
mkdir -p "$FX/skills-external/gstack/$g"
|
||||||
|
touch "$FX/skills-external/gstack/$g/SKILL.md"
|
||||||
|
done
|
||||||
|
cp "$ROOT/lib/profile.sh" "$ROOT/lib/toggle-external.sh" "$FX/lib/"
|
||||||
|
printf 'MAGIC_API_KEY=test-secret-000\n' > "$FX/.env"
|
||||||
|
|
||||||
|
# Non-managed external, enabled from the start — must never be touched.
|
||||||
|
ln -s "$FX/skills-external/other-ext" "$FX/skills/other-ext"
|
||||||
|
|
||||||
|
cat > "$FX/lib/profiles/designish.profile" <<'EOF'
|
||||||
|
gs-a
|
||||||
|
gs-b
|
||||||
|
emil-design-eng external
|
||||||
|
magic mcp
|
||||||
|
EOF
|
||||||
|
cat > "$FX/lib/profiles/backendish.profile" <<'EOF'
|
||||||
|
gs-c
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Fake claude: logs every call; keeps MCP registry state in a flat file.
|
||||||
|
cat > "$FX/bin/claude" <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
FX="$FX"
|
||||||
|
echo "\$*" >> "\$FX/claude-calls.log"
|
||||||
|
case "\$1 \${2:-}" in
|
||||||
|
"mcp list") cat "\$FX/mcp-state" 2>/dev/null ;;
|
||||||
|
"mcp add") echo "magic: stub" > "\$FX/mcp-state" ;;
|
||||||
|
"mcp remove") : > "\$FX/mcp-state" ;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
chmod +x "$FX/bin/claude"
|
||||||
|
|
||||||
|
run() { PATH="$FX/bin:$PATH" PROFILE_REPO_OVERRIDE="$FX" \
|
||||||
|
TOGGLE_EXTERNAL_REPO_OVERRIDE="$FX" bash "$FX/lib/profile.sh" "$@"; }
|
||||||
|
|
||||||
|
# --- set designish: gstack on-demand + external from-source + magic on ---
|
||||||
|
run set designish >/dev/null 2>&1
|
||||||
|
check T1-gsa-on "$([ -e "$FX/skills/gs-a" ] && echo on || echo off)" on
|
||||||
|
check T2-gsb-on "$([ -e "$FX/skills/gs-b" ] && echo on || echo off)" on
|
||||||
|
check T3-gsc-off "$([ -e "$FX/skills/gs-c" ] && echo on || echo off)" off
|
||||||
|
check T4-emil-src "$([ -L "$FX/skills/emil-design-eng" ] && echo on || echo off)" on
|
||||||
|
check T5-magic-on "$(grep -c '^magic:' "$FX/mcp-state" 2>/dev/null)" 1
|
||||||
|
check T6-add-call "$(grep -c '^mcp add magic' "$FX/claude-calls.log")" 1
|
||||||
|
|
||||||
|
# --- set backendish: managed leftovers parked/unregistered ---
|
||||||
|
run set backendish >/dev/null 2>&1
|
||||||
|
check T7-gsc-on "$([ -e "$FX/skills/gs-c" ] && echo on || echo off)" on
|
||||||
|
check T8-gsa-park "$([ -e "$FX/skills-disabled/gstack__gs-a" ] && echo p || echo n)" p
|
||||||
|
check T9-emil-off "$([ -e "$FX/skills/emil-design-eng" ] && echo on || echo off)" off
|
||||||
|
check T10-emil-park "$([ -e "$FX/skills-disabled/emil-design-eng" ] && echo p || echo n)" p
|
||||||
|
check T11-magic-off "$(grep -c '^magic:' "$FX/mcp-state" 2>/dev/null || true)" 0
|
||||||
|
check T12-rm-call "$(grep -c '^mcp remove magic' "$FX/claude-calls.log")" 1
|
||||||
|
check T13-other-untouched "$([ -e "$FX/skills/other-ext" ] && echo on || echo off)" on
|
||||||
|
|
||||||
|
# --- back to designish: parked external restored (not re-sourced) ---
|
||||||
|
run set designish >/dev/null 2>&1
|
||||||
|
check T14-emil-back "$([ -e "$FX/skills/emil-design-eng" ] && echo on || echo off)" on
|
||||||
|
check T15-park-gone "$([ -e "$FX/skills-disabled/emil-design-eng" ] && echo p || echo n)" n
|
||||||
|
check T16-magic-back "$(grep -c '^magic:' "$FX/mcp-state" 2>/dev/null)" 1
|
||||||
|
|
||||||
|
printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ]
|
||||||
+15
-3
@@ -61,6 +61,15 @@ protected — `set` will refuse to disable them even if the profile omits them.
|
|||||||
**Managed plugins** that `set` may disable when not in profile:
|
**Managed plugins** that `set` may disable when not in profile:
|
||||||
`ui-ux-pro-max@ui-ux-pro-max-skill`, `plugin-dev@claude-code-plugins`,
|
`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.
|
`pr-review-toolkit@claude-code-plugins`. Other plugins are never auto-toggled.
|
||||||
|
**Managed externals** (`emil-design-eng`, `frontend-design`,
|
||||||
|
`design-motion-principles`, `impeccable`) and **managed MCPs** (`magic`)
|
||||||
|
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/unregisters them when it does not — e.g. `set
|
||||||
|
backend` after design work turns emil and magic off. `darwin-skill` 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
|
## Commands
|
||||||
|
|
||||||
@@ -117,8 +126,11 @@ bash "$HOME/.claude/lib/profile.sh" $ARGUMENTS
|
|||||||
update-check, learnings — script doesn't touch that infra. Disabled skills
|
update-check, learnings — script doesn't touch that infra. Disabled skills
|
||||||
are just hidden from Claude Code's scanner; the gstack repo stays installed.
|
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,
|
- Profile changes DO toggle the managed Claude Code plugins (ui-ux-pro-max,
|
||||||
plugin-dev, pr-review-toolkit) and the `magic` MCP — see the Mechanism table
|
plugin-dev, pr-review-toolkit), the managed external packs (emil-design-eng,
|
||||||
above (BDR-008). Anything outside that managed set stays manual:
|
frontend-design, design-motion-principles, impeccable) and the `magic` MCP —
|
||||||
`claude plugin enable|disable`, `claude mcp add|remove`.
|
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`, `claude mcp
|
||||||
|
add|remove`.
|
||||||
- `set` is destructive in the sense that it disables non-listed gstack skills.
|
- `set` is destructive in the sense that it disables non-listed gstack skills.
|
||||||
Use `apply` if the user wants additive behavior.
|
Use `apply` if the user wants additive behavior.
|
||||||
|
|||||||
Reference in New Issue
Block a user