From 8008d8233c6163df106b7012374f9b3879a2ef3b Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Mon, 20 Jul 2026 14:47:53 +0200 Subject: [PATCH 1/2] feat(profile): set symmetric on managed externals + MCPs (BDR-079) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MANAGED_EXTERNALS (emil-design-eng, frontend-design, design-motion-principles, impeccable) + MANAGED_MCPS (magic): cmd_set now trims both when the profile does not list them — design leftovers no longer survive a 'set backend' - cmd_set refactored to 4 symmetric trim helpers; nothing outside the MANAGED_* allowlists is ever auto-toggled (darwin-skill manual) - enable_skill external: from-source fallback (ln -sf skills-external/), mirrors toggle-external.sh - stale usage() NOTE + SKILL.md updated to the both-ways reality - hermetic test: 16 checks, fixture repo + fake claude shim (gstack on-demand, from-source, park/restore, magic add/remove, non-managed untouched); shellcheck + full make test green --- .claude/memory/decisions.md | 3 + .claude/memory/journal.md | 1 + .claude/tasks/TODO.md | 18 +++++ CHANGELOG.md | 3 + lib/profile.sh | 95 ++++++++++++++++++++++----- lib/tests/profile-set-managed.test.sh | 76 +++++++++++++++++++++ skills/profile/SKILL.md | 18 ++++- 7 files changed, 196 insertions(+), 18 deletions(-) create mode 100644 lib/tests/profile-set-managed.test.sh diff --git a/.claude/memory/decisions.md b/.claude/memory/decisions.md index 1bb5deb..8ed599c 100644 --- a/.claude/memory/decisions.md +++ b/.claude/memory/decisions.md @@ -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) 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/*.md`, else `npx ctx7@latest` fetch max 2 topics, else `ctx7 cache miss: ` 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 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. + +### 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/`, 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). diff --git a/.claude/memory/journal.md b/.claude/memory/journal.md index 1975d9f..1227885 100644 --- a/.claude/memory/journal.md +++ b/.claude/memory/journal.md @@ -417,3 +417,4 @@ rules: ## 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. - 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. diff --git a/.claude/tasks/TODO.md b/.claude/tasks/TODO.md index a41d64b..df65784 100644 --- a/.claude/tasks/TODO.md +++ b/.claude/tasks/TODO.md @@ -1,5 +1,23 @@ # 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/`) 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) 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. diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ed4240..a528426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). ## [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 ### Fixed diff --git a/lib/profile.sh b/lib/profile.sh index 3f20971..57d2e33 100755 --- a/lib/profile.sh +++ b/lib/profile.sh @@ -14,6 +14,9 @@ # - MCPs: delegated to lib/toggle-external.sh for known servers (magic), # advisory otherwise # - 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, # superpowers + rtk hook + .claude internal. The script refuses to disable @@ -61,6 +64,23 @@ MANAGED_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 # they're not in the profile. (Defensive: belt-and-suspenders alongside # MANAGED_PLUGINS allowlist.) @@ -271,6 +291,11 @@ enable_skill() { ok "enabled: $skill ($type)" 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 warn "missing: $skill ($type)" fi @@ -422,6 +447,48 @@ parked_gstack_count() { 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 ────────────────────────────────────────────── cmd_list() { @@ -506,24 +573,20 @@ cmd_apply() { cmd_set() { 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_not_in "$prof" # Disable managed plugins not in profile (PROTECTED_PLUGINS are excluded # by disable_skill itself — belt and suspenders). - local plugin_keep_file p plugin_name marketplace - plugin_keep_file="$(mktemp)" - read_profile "$prof" | awk -F'\t' '$2 ~ /^plugin@/ { sub(/^plugin@/, "", $2); print $1"@"$2 }' | sort -u > "$plugin_keep_file" - for p in "${MANAGED_PLUGINS[@]}"; do - if ! grep -qx "$p" "$plugin_keep_file"; then - plugin_name="${p%@*}" - marketplace="${p#*@}" - disable_skill "$plugin_name" "plugin@${marketplace}" - fi - done - rm -f "$plugin_keep_file" + disable_plugins_not_in "$prof" + + # Symmetry (BDR-079): a profile switch also parks the managed external + # packs and unregisters the managed MCPs the new profile does not need — + # design leftovers (emil, magic…) no longer survive a `set backend`. + disable_externals_not_in "$prof" + disable_mcps_not_in "$prof" # Enable everything listed in the profile. cmd_apply "$prof" @@ -679,9 +742,11 @@ EXAMPLES: bash lib/profile.sh reset # restore everything NOTE: - Plugin and MCP entries print advisory commands — they are NOT toggled - automatically. Run "claude plugin enable|disable" or "claude mcp add|remove" - yourself for those. + "set" toggles the MANAGED items automatically, both ways: plugins + (ui-ux-pro-max, plugin-dev, pr-review-toolkit), external packs + (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 } diff --git a/lib/tests/profile-set-managed.test.sh b/lib/tests/profile-set-managed.test.sh new file mode 100644 index 0000000..e0a3eea --- /dev/null +++ b/lib/tests/profile-set-managed.test.sh @@ -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" <> "\$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 ] diff --git a/skills/profile/SKILL.md b/skills/profile/SKILL.md index e986809..2f9edc8 100644 --- a/skills/profile/SKILL.md +++ b/skills/profile/SKILL.md @@ -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: `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 **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 @@ -117,8 +126,11 @@ bash "$HOME/.claude/lib/profile.sh" $ARGUMENTS 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 `magic` MCP — see the Mechanism table - above (BDR-008). Anything outside that managed set stays manual: - `claude plugin enable|disable`, `claude mcp add|remove`. + plugin-dev, pr-review-toolkit), the managed external packs (emil-design-eng, + frontend-design, design-motion-principles, impeccable) and the `magic` MCP — + 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. Use `apply` if the user wants additive behavior. From ecbe8abde701ac95fa66f8693b734fbec2bf9e0c Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Mon, 20 Jul 2026 16:29:31 +0200 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20profile=20list=20=C3=973=20+=20test?= =?UTF-8?q?=20glob=20+=20BDR-ID=20strip=20+=20layout=20tree=20=E2=86=92=20?= =?UTF-8?q?ARCHITECTURE.md=20=E2=80=94=20/doc=20clean=20pass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ARCHITECTURE.md | 33 +++++++++++++++++++++++++++++++++ README.md | 46 ++++++++++++---------------------------------- USAGE.md | 2 +- 3 files changed, 46 insertions(+), 35 deletions(-) create mode 100644 ARCHITECTURE.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..434a70c --- /dev/null +++ b/ARCHITECTURE.md @@ -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. diff --git a/README.md b/README.md index 081c9d1..62e052b 100644 --- a/README.md +++ b/README.md @@ -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. -``` -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) -``` +See [`ARCHITECTURE.md`](./ARCHITECTURE.md) for the full project layout and +structural principles (skills = entry points, agents = execution units, +templates = per-project scaffolding, graphify = codebase knowledge graph). -**Architecture principle:** -- `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) +### Agent model routing (model-tiering v2) Doctrine: the session model (Fable) does main-loop reflection ONLY — 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 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`). 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 -BDR-053, not reversing it (BDR-078). +carries fast-moving libs (`lib/fast-libs.sh`) — a scoped second surface, a +refinement of the single-surface rule, not a reversal. ```bash 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 | | `/geo` | GEO-only audit — AI-search visibility (ChatGPT, Perplexity, Claude, Gemini…) | | `/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 | > 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 on that command line, it materializes as a second plaintext copy outside `~/.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 — 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 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` -`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 never auto-execute. Don't allowlist `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 doctor # diagnostic 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 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-current # show the active profile make profile-reset # re-enable all gstack skills diff --git a/USAGE.md b/USAGE.md index 537fa5d..c20feec 100644 --- a/USAGE.md +++ b/USAGE.md @@ -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) | | `/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 | -| `/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, > pr-review-toolkit…) et marketplaces externes en ajoutent beaucoup d'autres —