30 Commits
Author SHA1 Message Date
Bastien Chanot 18c37a5505 Merge bugfix/gitflow-finish-args into develop 2026-07-03 14:05:39 +02:00
Bastien ChanotandClaude Opus 4.8 4e7f6b0951 chore(memory): BLK-015 + LRN-089 + LRN-047 corrob + journal 2026-07-03
gitflow_finish arg-guard bugfix + 3 doctor false-warns. BLK-015 (finish
ignored args → merged current branch), LRN-089 (pass-through wrapper
deriving target from ambient state = silent contract violation), LRN-047
corroborated (doctor false-warns), TODO items closed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
2026-07-03 14:03:12 +02:00
Bastien ChanotandClaude Opus 4.8 706abff851 docs(changelog): note gitflow_finish arg-guard + doctor false-warn fixes under Unreleased
Historical entries describing the old "Pro session budget" framing left
intact — they record what shipped then; Keep a Changelog is append-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
2026-07-03 13:57:59 +02:00
Bastien ChanotandClaude Opus 4.8 6778b9fadd fix(doctor): kill 3 false-warns — cargo/RTK, dir-symlink children, token denominator
A doctor that cries false is a doctor you ignore (LRN-047). Three stale
checks fixed:
- cargo "(RTK unavailable)" -> honest optional info: RTK ships prebuilt
  (detect_rtk finds ~/.cargo/bin|~/.local/bin), cargo only builds it from
  source.
- check_symlink passes files reached via dir-level symlinks. hooks/,
  skills/, agents/, lib/, templates/ are directory symlinks, so a child
  like hooks/session-start.sh is a real file under $REPO, not a symlink
  itself. Now: PASS iff the canonical path lands in $REPO; a stray real
  copy still warns as drift.
- gstack check counts the 34 per-skill symlinks into skills-external/gstack/
  instead of a mythical skills/gstack link (link.sh deliberately removes
  that one -> "run link.sh" could never satisfy the old check).
- token budget vs the 200k default context window, not a bogus 11k
  "session budget" -- the old denominator was a category error producing a
  false "92% CRITICAL". Measured footprint ~11.4k post-audit (LRN-088) ->
  ~5% of context.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
2026-07-03 13:57:59 +02:00
Bastien ChanotandClaude Opus 4.8 d9fdd4cbdf fix(gitflow): gitflow_finish validates its named branch against HEAD
gitflow_finish ignored its <type> <name> args and always merged the
checked-out branch — `finish bugfix audit-bugs` run from
feature/audit-tokens merged the wrong branch (audit UX trap, 2026-07-02).
Args are now an optional safety ASSERTION: if present and != current
branch, refuse loudly (rc 2) instead of merging the wrong thing. No args
= unchanged (the only real caller, SKILL.md:36, passes none). +7 T12
regression assertions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
2026-07-03 13:57:59 +02:00
Bastien Chanot 615b21289e Merge chore/audit-capitalize into develop 2026-07-02 14:39:11 +02:00
Bastien ChanotandClaude Fable 5 85a5f4b53d chore(memory): LRN-087 + LRN-088 + LRN-077 corrob + BDR-026 incident + journal 2026-07-02
Audit session capitalization: presence-flag ≠ capability (rtk),
token verbosity-beats-cardinality, T6c transient-state green,
copies-of-secrets incident (magic key rotated).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:38:57 +02:00
Bastien Chanot 8cade82edb Merge feature/audit-hardening into develop 2026-07-02 14:35:56 +02:00
Bastien Chanot c6660c1c5f Merge bugfix/audit-bugs into develop 2026-07-02 14:35:31 +02:00
Bastien Chanot 0c353d2102 Merge feature/audit-tokens into develop 2026-07-02 14:32:59 +02:00
Bastien ChanotandClaude Fable 5 1aa9afe669 feat(tokens): compress the 10 fattest personal skill descriptions
6,416 → 4,243 chars (≈ −540 tokens/session, catalog loaded every
session). Kept per BDR-014/LRN-043: 'Use when' pattern, discriminating
FR+EN triggers, all 'For X → /Y' disambiguation lines. Cut: redundant
trigger synonyms, header/engine enumerations, prose the model derives.
find-docs excluded (ctx7-owned, regen clobbers — LRN-086); doc + geo
taken instead. All ≤ ~505 chars body (BDR-014 aspirational ceiling).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:31:38 +02:00
Bastien ChanotandClaude Fable 5 a73dff4edf feat(settings): rtk-wrapped allowlist + .env deny mirrors (audit #11 companion)
The rtk hook no longer auto-allows (audit-bugs branch): rewritten
commands are evaluated natively. Allow rules match the original forms
(grep *, ls *) not the rewritten ones — without explicit rules every
rewrite would fall to the classifier. Added the read-only rtk-wrapped
family, bare + absolute-path forms (the hook emits absolute paths when
PATH lacks the cargo dir): grep, ls, cat, head, tail, wc, diff, git
status/log/diff/show/branch. NOT find (rtk find could carry -exec rm —
native find-deny rules would not match the rtk prefix).
Deny mirrors guard the bypass the allowlist would open on hand-written
'rtk cat .env'-class commands: cat/grep/head/tail × .env, both prefixes.
Residual: exotic quoting may evade the mirrors — second curtain stays
the auto-mode classifier (BDR-004).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:29:53 +02:00
Bastien ChanotandClaude Fable 5 731ed95c98 feat(rtk): drop auto-allow — permission control returns to settings.json
The exit-0 branch emitted permissionDecision:allow, making rtk's internal
Rust registry a PARALLEL permission authority: a rewritten command
bypassed settings.json deny/ask entirely (audit #11). Both rewrite paths
now emit updatedInput only; the rewritten command goes through native
evaluation. Companion allow rules for read-only 'rtk <tool>' forms land
in settings.json (audit-hardening branch) to keep the safe majority
frictionless. Re-pinned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:28:31 +02:00
Bastien ChanotandClaude Fable 5 56bd5ff0c2 feat(tokens): pr-review-toolkit OFF by default — heaviest plugin, PR-only use
Measured: 6 agent descriptions = ~2.2k tokens injected EVERY session
(the single largest plugin contributor) for a toolkit useful only when
reviewing PRs. enabledPlugins → false; removed from full+backend
profiles (BDR-017 caveat already accepts full excluding rarely-used
items); audit.profile KEEPS it = profile reactivation channel.
Per-PR-session: claude plugin enable pr-review-toolkit@claude-code-plugins
(or bash lib/profile.sh apply audit); a later 'profile set full'
re-disables it via the MANAGED_PLUGINS lifecycle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:17:55 +02:00
Bastien ChanotandClaude Fable 5 a0d092ca9f chore(make): declare onboard in .PHONY
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:16:36 +02:00
Bastien ChanotandClaude Fable 5 9e534241f9 feat(settings): deny-list hardening pass (audit #20)
- rm -r / rm -fr denied (only -rf was; flag-order variants passed).
- python3 -c / python -c ask → deny: aligned with node -e / perl -e /
  ruby -e (arbitrary-interpreter class was incoherently split).
- git push <remote> +<ref> denied (refspec force carried no flag).
- --force-with-lease un-over-blocked: --force* split into --force /
  --force *, so the safer variant now falls to the git push ASK gate.
Deny 99 → 105, ask 19 → 18. Second curtain unchanged (auto-mode
classifier, BDR-004). doctor's deny sentinel tracks HEAD (LOT 1).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:16:36 +02:00
Bastien ChanotandClaude Fable 5 45a387c1dd feat(update-all): bun self-upgrade + documented non-update exclusions
Step 6.5: bun upgrade (guarded). Deliberate exclusions documented in
place: magic MCP (npx @latest resolves at invocation), graphify claude
install (rewrites curated configs — BDR-028 territory, manual only),
gsd (lock-pinned: make update reinstalls the pin, note added to
plugins.lock.json so the no-op is explicit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:16:36 +02:00
Bastien ChanotandClaude Fable 5 ed2408e742 fix(design-hook): tighten trigger regex — cut ultra-generic tokens
page/pages/form/menu/card/carte/style/look/screen/interface/color/shadow
fired on a large share of non-UI prompts (~200 tokens of reminder each;
measured 6 fires during a pure config audit, including on task
notifications). Specific compounds stay: formulaire, styling, stylesheet,
styliser, écran, couleur, palette… FR aesthetic words kept.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:16:16 +02:00
Bastien ChanotandClaude Fable 5 3a9b9e584d fix(install-plugins): gate success messages on real outcomes; drop plugin-dev
- gstack + graphify blocks printed unconditional ok after || warn —
  misleading-success (LRN-071 class). ok now gated on tracked outcome,
  loud warn otherwise.
- plugin-dev install dropped (audit #14): installed 2026-06-23, never
  enabled, pure disk weight; uninstalled from the machine, reinstall
  deliberately if plugin authoring becomes a need.
- Summary block truthfulness: header no longer claims 'start OFF' for
  plugins committed enabled; pr-review-toolkit token estimate ~300 →
  ~2.2k (measured, 6 agent descriptions); ui-ux ~400 → ~780 (measured);
  graphifyy row names the CLI (graphify).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:16:16 +02:00
Bastien ChanotandClaude Fable 5 6d72d0adc8 fix(session-start): truthful banner — derive ALWAYS_ON, label graphify, greedy split
- ALWAYS_ON derived from settings.json:enabledPlugins (true entries)
  minus toggle-owned names — the hardcoded pair under-reported newly
  enabled plugins (pr-review-toolkit enabled yet invisible). LRN-005.
- Display 'graphify' (the CLI/skill name); graphifyy stays the pipx
  package name everywhere it IS the package.
- Overflow split = greedy width-fill: the fixed 3-name cut overflowed
  line 1 and printed an empty line 2 with 3 long names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:16:02 +02:00
Bastien ChanotandClaude Fable 5 cca43cbe5a chore(agents): remove stale tracked seo-analyzer.md.bak + ignore *.bak
Pre-split (SEO/GEO) backup, 1097 diff lines vs live agent — dead weight
committed by accident. *.bak now gitignored (Editors block).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:16:02 +02:00
Bastien ChanotandClaude Fable 5 ca8df16885 fix(doctor): kill 3 permanent false sentinels (LRN-047 class)
- EXPECTED_DENY hardcoded 100 vs 99 real → derive from committed
  settings.json (HEAD): auto-tracks legit deny edits, still flags
  live-vs-committed divergence.
- EXPECTED_SKILLS required gstack 'health' (OFF by default, profile-
  managed): false warn on a default install with a wrong remedy —
  link.sh cannot restore gstack skills. Dropped; 'status' kept (repo-
  owned personal skill, git ls-files proven).
- disable-model-invocation check required a key BDR-019 stripped
  repo-wide (2026-06-09) → warned on every owned skill since.
  Inverted into a BDR-019 regression watch.
- pass message derives the skill list from the array (LRN-005 class:
  no hardcoded display drift).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:07:40 +02:00
Bastien ChanotandClaude Fable 5 17fb6dda43 fix(tests): run-reconcile T6c — oracle pointed at the removed parasite dir
$MEM/../skills resolved to .claude/skills/ (the LRN-042 parasite, removed
2026-06-30 by make plugin Step 8.5), not the real skills/. Green at build
time only because the parasite still existed — green-for-wrong-reason
(LRN-077 class); red ever since. Suite back to 20/20.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:07:31 +02:00
Bastien ChanotandClaude Fable 5 8e61d03c43 fix(session-start): update-check reads origin/main — dead since master→main migration
git show origin/master:version.txt fatal-ed since the gitflow migration
(2026-06-29): the 'update available' banner could never fire while a
synchronous git fetch was still paid every session for a discarded result.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:07:29 +02:00
Bastien ChanotandClaude Fable 5 f0b7e89468 fix(rtk): rtk resolution + absolute-path rewrite — compression was silently dead
rtk lives at ~/.cargo/bin but the hand-managed .bashrc lost the cargo
line: command -v failed in hook AND tool shell, so the hook no-op'd with
a stderr warn on every Bash call — input compression silently OFF.

- Resolve RTK_BIN by probing known install dirs (LRN-036 class).
- Substitute the ABSOLUTE path at the rewrite head: a bare 'rtk …'
  exits 127 in the tool shell, whose PATH the hook cannot fix (proven).
- Compound rewrites carrying further bare rtk segments pass through
  unrewritten: quoted text (commit messages) makes a global substitution
  unsafe — lose compression, never emit a command that 127s (proven:
  a commit chain 127'd mid-flow).
- detect_rtk probes the same dirs so the banner reports capability.
- Re-pinned .rtk-hook.sha256: the rtk BINARY verifies the hook against
  it at execution time and refuses a modified hook — the pin is live
  machinery, not a vestige; coupling documented in the header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:07:22 +02:00
Bastien Chanot fe9fa86bb2 Merge chore/lrn-086-ctx7-provenance into develop 2026-07-02 13:23:08 +02:00
Bastien ChanotandClaude Opus 4.8 f5961cb8d6 chore(memory): LRN-086 — external-tool skill provenance + gitignore/regen rule
ctx7 setup --claude --cli materializes find-docs (skill, symlinked into repo)
+ rules/context7.md (global, user-editable). login != setup. Rule: prove
provenance by mtime not repo grep; gitignore tool-generated skill + regen via
install-step; guard regen on absence when tool co-writes editable config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8bmCXTHNccS7KRV4gWXEF
2026-07-02 13:22:59 +02:00
Bastien Chanot 41ee5c54bf Merge bugfix/ctx7-auth-guidance into develop 2026-07-02 13:11:00 +02:00
Bastien ChanotandClaude Opus 4.8 01d8b8f65c feat(install-plugins): interactive ctx7 login + auto-setup, ignore find-docs
STEP 6 ctx7 auth, when anonymous:
- interactive TTY -> prompt [y/N] then run `ctx7 login`; non-interactive
  (CI/headless/re-run) keeps text guidance, never opens a browser or blocks.
- run `ctx7 setup --claude --cli` when the find-docs skill is absent, to
  (re)install CLI+Skills mode. Guarded on absence so a re-run never clobbers
  a customized ~/.claude/rules/context7.md.

gitignore skills/find-docs/: it is a ctx7-managed skill materialized by
`ctx7 setup --claude --cli` into ~/.claude/skills (symlink to repo skills/),
re-created on demand by Step 6 — not vendored here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8bmCXTHNccS7KRV4gWXEF
2026-07-02 13:09:51 +02:00
Bastien ChanotandClaude Opus 4.8 9ee2e9407e fix(install-plugins): auth-aware ctx7 guidance + drop obsolete MCP nudge
Step 6 printed its ctx7 hints unconditionally — telling already-authed users
to log in, and pointing at `ctx7 setup --claude` (MCP-adjacent). Anonymous mode
is fully functional (docs + library work without auth); auth only buys rate
limits, so setup was never required for ctx7 to work.

- Detect auth via an offline oracle: credentials.json presence (XDG-aware),
  no subprocess / network / browser — mirrors the idempotent-claude fix.
- Authenticated -> "ctx7 authenticated"; anonymous -> non-blocking guidance
  (works anonymously; `ctx7 login`, `--no-browser` for headless). The installer
  guides, never launches login/setup.
- Drop `ctx7 setup --claude`: leftover reopening MCP path, aligns w/ TODO:48
  CLI-only decision.

Verified: bash -n, shellcheck (no new findings), + simulated both auth states
(credentials.json present/absent) — correct branch each, credentials restored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBXkDr74N9whdiJyjzyVN
2026-07-01 23:36:21 +02:00
34 changed files with 503 additions and 1091 deletions
+10
View File
@@ -34,6 +34,7 @@ rules:
| BLK-012 | 2026-06-29 | gitflow_init half-applied: socle-commit failure swallowed → hook activated on partial run → re-run self-blocks | resolved |
| BLK-013 | 2026-06-30 | `make plugin` Error 127 — npm absent on apt-`nodejs` host (Step 4 gsd-pi aborts, Steps 5-10 + residual cleanup never run) | resolved (env) |
| BLK-014 | 2026-07-01 | `make install` aborts npm EEXIST on `~/.local/bin/claude` when claude already installed via native installer — no presence guard | resolved |
| BLK-015 | 2026-07-03 | `gitflow_finish` ignored its `<type> <name>` args → merged the CHECKED-OUT branch not the one named → wrong-branch merge (audit LOT3) | resolved |
---
@@ -179,3 +180,12 @@ rules:
- **Status**: resolved. Fix `8dc4027`, branch `bugfix/install-claude-idempotent`, pending merge validation.
- **Reference**: [[BLK-013]] npm prefix `~/.local` = contributing factor (npm bin over native bin). install-plugins.sh already pointed to code.claude.com (native) — install.sh was the npm outlier. Fresh-machine `elif npm` branch channel-consistency = open design question (potential BDR). Pattern → [[LRN-085]].
- **Update 2026-07-01**: MERGED `2393ca5` (bugfix/install-claude-idempotent → develop), pushed — supersedes "pending merge validation". The open channel-consistency question is RESOLVED by [[BDR-046]] (fresh install → native installer, npm dropped for claude); install.sh has no `elif npm` branch → nothing left to trancher.
## BLK-015 — `gitflow_finish` ignored its args, merged the CURRENT branch not the one asked
- **Date**: 2026-07-03
- **Friction**: audit 2026-07-02 — `gitflow.sh finish bugfix audit-bugs` run while checked out on `feature/audit-tokens` merged audit-tokens (LOT3), NOT audit-bugs. Final develop state identical (disjoint hunks) so no data damage, but the merge order was silently wrong. UX trap: the command LOOKS like it targets `bugfix/audit-bugs`.
- **Real cause**: CLI dispatch (`lib/gitflow.sh:257` `finish) gitflow_finish "$@"`) forwards args, but the function derived its source from `HEAD` (`git symbolic-ref`) and NEVER read `$1/$2` → the `<type> <name>` were silently dropped. Merge source = ambient state (checked-out branch), not the named target. Design intended finish to always operate on HEAD (human gate = "be on the branch"), but nothing enforced that passed args, if any, MATCH the branch you're on.
- **Solution**: `gitflow_finish [<type> <name>]` — args now an optional safety ASSERTION: present AND `"$req_type/$req_name" != "$br"` → error `operates on the current branch 'X', but you asked 'Y' — checkout 'Y' first`, rc 2. No args = behavior unchanged (only real caller `skills/gitflow/SKILL.md:36` + every test pass none → zero regression). +7 regression assertions (`gitflow-test.sh` T12, numbered to dodge collision with reconcile's own T6c).
- **Status**: resolved. Commit `d9fdd4c`, branch `bugfix/gitflow-finish-args`.
- **Reference**: journal 2026-07-02 (trap noted, not fixed) → fixed 2026-07-03. Pattern → [[LRN-089]] (pass-through wrapper deriving target from ambient state = silent contract violation).
+1
View File
@@ -477,6 +477,7 @@ rules:
- Secret in `repo/.env`, gitignored (status quo) — one `git add -f` or a `.gitignore` slip leaks it; the secret physically sits in the tree.
- Scripts read `~/.claude/.env` directly — makes the symlink redundant but rewrites every read path and loses repo-local visibility.
- **Reference**: `link.sh` `link_env()`, `.gitignore`, `lib/toggle-external.sh`, `install-plugins.sh`, `.env.example`, commits 131d0bc / f9cc866. Linked to [[BDR-025]] (magic's `MAGIC_API_KEY`, consumed by the gate's required-but-manual class).
- **Update 2026-07-02 (incident — copies of secrets)**: `claude mcp add --env` MATERIALIZES the key into `~/.claude.json` (`mcpServers.magic.env`) — a 2nd live copy OUTSIDE the `~/.claude/.env` canonical and outside the repo deny rules' reach. An audit query printed it into a session transcript → key rotated (21st.dev). Rule: secrets have COPIES (tool configs, transcripts, caches) — protect/audit the copies, not just the canonical; when inspecting MCP config, filter env fields (`jq 'del(.. | .env?)'`). Same audit: `~/.claude/.env` hardened 0664→0600.
---
+13
View File
@@ -295,3 +295,16 @@ rules:
- /reconcile show-only (claude repo, engine-verified): confronted TODO+registries vs git/fs. Real state = 1 actionable (install-plugins npm harden), 3 blocked-upstream (BLK-001 rtk / BLK-003 darwin / BLK-009 CC #21858, re-test on CC MAJ), 3 deferred-on-trigger, release-decision live (develop 20 ahead of v4.0.0). Engine false-flagged BLK-014 (last-status-wins caught Reference "open" vs Status resolved) — verified merged. "canal d'install" = already decided by BDR-046, NOT open; faunosteo/WARN-manuel = not in this repo.
- (c) TODO drift fixed: 7 `--help` WON'T-BUILD subtasks `[ ]`→`[-]` (chore/reconcile-todo-drift, 9c02406) → naive open-count 10→3, survivors all genuine deferred-open. Registries left read-only during reconcile (staleness deferred to this capitalize).
- (a) BLK-013 fix-forward BUILT: install-plugins.sh unconditional npm guard (corepack→distro→fatal), placed after `NODE_OK` short-circuit so node>=22-but-no-npm hosts don't skip it. shellcheck/`bash -n` clean, 1f2c1cc. Capitalize refreshed BLK-013 (NOT built→built), BLK-014 + BDR-046 (pending→merged) via append-only Update blocks. Both branches finished into develop.
## 2026-07-02
- Fable 5 exhaustive audit (read-only, 5 subagents + real suites): 24 findings — 5 bugs (rtk DEAD silently since .bashrc wipe → [[LRN-087]]; session-start update-check on gone origin/master; run-reconcile T6c parasite path → [[LRN-077]] corrob; doctor 3 false sentinels incl. BDR-019 contradiction), token overhead measured 14.6k/session → [[LRN-088]].
- 3 lots merged on explicit GO (suites green after each, reconcile 20/20 post-LOT1): bugfix/audit-bugs (rtk absolute-path heal + re-pin ×2, origin/main, T6c, doctor sentinels); feature/audit-hardening (.bak purge, banner ALWAYS_ON derived + graphify label, ok-gated installers, design-hook regex tightened, update-all bun+exclusions, deny 99→113 + rtk read-only allowlist + .env mirrors, cleanup batch, origin/HEAD→main); feature/audit-tokens (pr-review-toolkit OFF −2.2k tok, kept in audit.profile as reactivation channel; 10 descriptions compressed −540 tok).
- #11 rtk auto-allow DROPPED — permission control back in settings.json (rtk registry was a parallel authority bypassing deny/ask). #10 rules/context7.md deleted (−493 tok; find-docs survives, stable — regen keyed on its absence); faulty examples → upstream issue draft (upstash/context7, gh unauthenticated). plugin-dev uninstalled + dropped from installer.
- Incidents: magic API key printed into transcript from ~/.claude.json → rotated, [[BDR-026]] update (copies of secrets); gitflow_finish ignores its args (operates on CURRENT branch, lib/gitflow.sh:104) → LOT 3 merged first by mistake, final develop state identical (disjoint hunks) — UX trap noted, not fixed.
- Residuals (flagged, not built): doctor "Cargo not found (RTK unavailable)" parenthesis now misleading; doctor symlink-check false-warns on dir-level symlinks; doctor token constants stale; find-docs faulty examples ctx7-owned.
## 2026-07-03
- bugfix/gitflow-finish-args: `gitflow_finish` contract fix — args now optional safety ASSERTION (present + ≠ current branch → refuse rc2 "operates on current branch X, you asked Y — checkout Y first"); no-args unchanged (only real caller SKILL.md:36 + all tests pass none → zero regression). +7 T12 assertions. [[BLK-015]], [[LRN-089]]. Off-by-one caught at capitalize: next free BLK = 015 not 016 (gate proposal said 016) → gitflow.sh comment corrected pre-finish via soft-reset+redo of the 3 commits.
- Same branch, 3 doctor false-warns fixed ([[LRN-047]] corrob — a doctor that cries false is ignored): cargo "(RTK unavailable)" → optional info (RTK prebuilt, detect_rtk); check_symlink passes children of dir-level symlinks (hooks/session-start.sh); gstack counts 34 per-skill symlinks not a mythical skills/gstack link (link.sh removes it); token budget vs 200k context window not bogus 11k "session budget" → killed false "92% CRITICAL" (measured ~11.4k [[LRN-088]]; 200k confirmed by user — 1M pin revoked at audit #7, calibrate on default not the exceptional session).
- Suites green: gitflow 71/71 (+7), deterministic 13, doc-commit 32, doc-shape 19, reconcile 20, deploy-commit 13, release-candidate 5/5 tag-mode. doctor: 0 false-warn (1 legit survivor = gstack tracks branch=main advisory). shellcheck clean. T12 named to dodge collision with reconcile's own T6c (darwin path, audit #3).
- 3 atomic commits (fix gitflow / fix doctor / docs changelog Unreleased) + memory. finish bugfix→develop on GO; user pushes develop.
+39
View File
@@ -105,6 +105,10 @@ rules:
| LRN-083 | 2026-06-30 | subagents are an INVALID instrument for measuring main-loop spontaneous routing — SUBAGENT-STOP + delegated framing pin them to the no-route floor | any RED of whether the MAIN loop self-invokes; use fresh main-loop sessions, observe via the human |
| LRN-084 | 2026-07-01 | protection hook enforces PROD not the full branch-flow; exemption masked the rule-vs-guard divergence | a guard exempts a class / checks one predicate — verify it encodes full intent |
| LRN-085 | 2026-07-01 | Idempotent CLI install/update: `command -v` skip-if-present guard + detect channel (`npm ls -g` vs native symlink) before choosing updater; never `npm --force` over a bin npm doesn't own | any installer/updater for a CLI with >1 install channel |
| LRN-086 | 2026-07-02 | External-tool-generated skill: prove provenance by mtime (not repo grep), gitignore + regen via install-step; guard regen on ABSENCE when the tool co-writes a user-editable config | any untracked skill/dir a tool (ctx7, etc.) drops into the repo |
| LRN-087 | 2026-07-02 | presence-flag ≠ capability — rtk silently dead after .bashrc wipe; emitted commands need ABSOLUTE bin paths (they run in another shell); integrity pin = live machinery, re-pin on hook edit | any PATH-dependent capability + hand-managed shell profile; hooks emitting commands for another shell |
| LRN-088 | 2026-07-02 | token-cutting intuition inverts under measurement — verbosity beats cardinality (gstack 34 skills ≈ 592 tok vs pr-review 6 agents ≈ 2,183) | any "disable X to save tokens" — measure per-item bytes first; profiles toggle skills, not plugin payloads |
| LRN-089 | 2026-07-03 | pass-through wrapper (CLI `"$@"` → fn deriving target from ambient state: HEAD/cwd/env) silently ignores its args = silent contract violation; guard = args are an ASSERTION, refuse when they disagree with state | any dispatcher forwarding args to a callee that reads ambient state instead of the args |
---
@@ -869,6 +873,7 @@ rules:
- **pattern**: a baseline agent on a worktree named `wt-pre-reconcile` read "pre-reconcile" FROM THE DIR NAME and inferred staleness — reasoning for the WRONG reason (the name), not the right one (verify git). Fixtures + the GREEN test were re-frozen under NEUTRAL names so the engine reaches truth by querying git, never by reading a path hint.
- **meta — same symptom, distinct cause as [[LRN-074]]**: 074 = a COMMAND-ASSUMPTION (ugrep parsed `-9..` → false green); 077 = a LEAKY FIXTURE (name telegraphs the answer). Different mechanisms, SAME symptom: the test passes/fails for the wrong reason. Cross-cutting lesson = verify a test passes for the RIGHT reason, not merely that it passes — whether the false signal comes from an assumed command (074) or a leaky fixture (077).
- **future application**: name fixtures/paths neutrally; for any green, ask "did it pass because the subject did the work, or because something leaked the answer?"
- **corroboration 2026-07-02 (T6c)**: 3rd family member — test truth borrowed from TRANSIENT env state. run-reconcile T6c asserted `$MEM/../skills/darwin-skill` = `.claude/skills/` (the [[LRN-042]] parasite dir), not canonical `skills/`; born green because the parasite still existed, red since the same-day cleanup, unnoticed until the 2026-07-02 audit re-ran the suite ([[EVAL-011]]'s "20/20" silently 19/1 for 2 days). Oracles target CANONICAL paths (never derived `X/../Y`); re-run suites after ANY env cleanup tests may have silently depended on; "green at build" ≠ "green now".
## LRN-078 — semver number DERIVES from the change nature; "breaking" = requires a migration
- **Date**: 2026-06-30
@@ -926,3 +931,37 @@ rules:
- **Pattern**: (a) idempotent install step = `command -v <bin>` guard → skip-if-present with version echo, install only in `else`/`elif`. For a BINARY this IS a deterministic oracle (contrast [[LRN-054]]: conversation-state presence has none → don't skip-branch). (b) a CLI can ship via >1 channel (npm vs native). npm can't clobber a bin symlink it doesn't own → EEXIST; `npm --force` = wrong (npm itself says "recklessly", breaks native self-update). Detect channel first: `npm ls -g <pkg>` succeeds → npm-managed → npm; else native → `claude update` self-updater. (c) install ≠ update: first-time installer skips-if-present; the update script does the channel-aware upgrade.
- **Future application**: any installer/updater for a CLI reachable via multiple channels — guard with `command -v`, branch the updater on detected channel, never blind `--force` over a foreign-owned bin. Caveat [[LRN-036]]: `command -v` needs the bin dir on PATH in shelled-out/hook contexts.
- **Reference**: [[BLK-014]], mirrors RTK/GSD guard in install-plugins.sh. Related [[LRN-005]] (plugin enable idempotency), [[LRN-039]] (installer config drift).
---
## LRN-086 — External-tool-generated skill: prove provenance by mtime, gitignore + regen-on-absence (not unconditional) when the tool co-writes a user-editable config
- **Date**: 2026-07-02
- **Context**: `skills/find-docs/` showed untracked. `grep -rniE 'find-docs' --include='*.sh'` → 0 hits → wrongly read "hand-authored first-party skill, commit it". FALSE. Generator = external binary `ctx7 setup --claude --cli` (CLI+Skills mode), not any repo script. Oracle that flipped it: mtime `skills/find-docs/SKILL.md` (23:16:59.637) == ctx7 `~/.config/context7/credentials.json` write, same setup run → ctx7 co-created it. User held the correct premise; my repo-only grep was too narrow.
- **Pattern**: (a) provenance of an untracked artifact — a repo-script grep is BLIND to external-binary generators. Correlate its mtime with the tool's OWN files (creds/config) + read the tool's subcommands (`ctx7 setup --claude/--cli/--mcp`, `remove`) before deciding hand-authored vs tool-owned. (b) `ctx7 setup --claude --cli` writes TWO files 0.13s apart: `~/.claude/skills/find-docs/SKILL.md` (`~/.claude/skills` = symlink to repo `skills/` → lands IN repo) AND `~/.claude/rules/context7.md` (global config, real dir, NOT in repo, user-editable). (c) login ≠ setup: `ctx7 login` = auth/rate-limits only (help = only `--no-browser`), does NOT trigger setup. Orthogonal.
- **Rule**: tool-generated skill → gitignore it (like `skills-external/frontend-design/`) + regenerate via an install step, do NOT vendor. gitignore coherence: ignoring an artifact REQUIRES an install-step that regenerates it, else a fresh clone loses it. BUT when the same `setup` ALSO (re)writes a user-editable config, guard regen on ABSENCE (`[ ! -f .../find-docs/SKILL.md ]`) — an every-run `setup` would silently clobber that config once customized. Contrast frontend-design: unconditional re-sync is fine (its file is not user-editable).
- **Future application**: before gitignore-vs-commit on any untracked skill/dir, PROVE provenance (mtime + tool subcommands), never trust a repo grep alone. Tool-owned → gitignore + install-step regen; gate the regen on absence iff the generator co-writes anything the user may hand-edit. Reuses [[LRN-085]] presence-guard oracle (file presence = deterministic). See [[LRN-084]] (guard scope vs full intent), install-plugins.sh Step 6, commit `01d8b8f`.
## LRN-087 — presence-flag ≠ capability: rtk silently dead after .bashrc wipe
- **Date**: 2026-07-02
- **pattern**: binary installed + hook wired + registries say "always-on" ≠ capability LIVE. Hand-managed .bashrc restore dropped the cargo PATH line → `command -v rtk` failed in hook AND tool shell → hook warned+passed-through EVERY Bash call, input compression OFF ~9 days. Banner truthfully dropped rtk — but an ABSENT line is invisible signal, nobody noticed. Reality/registry gap held ([[BDR-006]]-era always-on belief survived).
- **fix shape (3 teeth)**: (1) consumer self-heals — probe known install dirs (`~/.cargo/bin`, `~/.local/bin`), never trust PATH ([[LRN-036]]); (2) an emitted/rewritten command executes in ANOTHER shell whose PATH the hook cannot fix → substitute the ABSOLUTE bin path at string head; compound rewrites with residual bare bin at a command position → pass through, never emit a 127 (global substitution unsafe: quoted text, e.g. commit messages, carries the same token at line start — proven live); (3) the rtk BINARY verifies its hook against `hooks/.rtk-hook.sha256` at execution and refuses a modified hook → every legit hook edit must re-pin. Pin = live machinery, NOT vestige — audit rec "delete it" REFUTED by execution ([[LRN-037]]).
- **future application**: any PATH-dependent capability + hand-managed shell profile → probe install dirs, absolute paths in emitted commands, verify capability END-TO-END; a status line that can silently disappear ≠ monitoring. Check for integrity pins before editing generated hooks.
- **Reference**: `hooks/rtk-rewrite.sh` (RTK_BIN + absolute-path substitution + compound pass-through), `lib/detect-plugins.sh` detect_rtk, branch bugfix/audit-bugs (audit 2026-07-02). [[BLK-001]] context. See [[LRN-036]], [[LRN-037]].
## LRN-088 — token-cutting intuition inverts under measurement: verbosity beats cardinality
- **Date**: 2026-07-02
- **pattern**: fixed per-session context overhead measured ~14.6k tok (audit 2026-07-02). The intuitive target (gstack, 34 skills) = only ~592 tok — terse one-liner descriptions. Real weights: CLAUDE.md 3,788 · personal skill descriptions ~3,488 (hand-written trigger lists, ~6× cost/skill vs gstack) · pr-review-toolkit agents 2,183 (6 agents, PR-only use) · superpowers session-inject 1,540 · context7 rule 493. Cutting by item-COUNT intuition misallocates effort ~4×.
- **actions taken**: pr-review-toolkit OFF by default (−2,183; audit.profile keeps it = reactivation channel), 10 fattest personal descriptions compressed 6,416→4,243 chars (−~540), context7 rule dropped for the find-docs skill (−493; skill body loads on-demand, stable — regen keyed on find-docs absence). Total ≈ −3.2k/session ≈ −22%.
- **future application**: before any "disable X to save tokens" → measure per-item bytes FIRST (frontmatter extraction, plugin cache); expect the fat where descriptions are hand-written rich, not where items are many. Profiles toggle SKILLS only — plugin payloads (agents/skills in cache) need `enabledPlugins`. [[LRN-080]] measure-first corroborated on a new axis (cost, not behavior).
- **Reference**: audit 2026-07-02 measurement + branch feature/audit-tokens. See [[BDR-014]], [[LRN-043]].
## LRN-089 — a pass-through wrapper whose callee reads ambient state silently ignores its args
- **Date**: 2026-07-03
- **pattern**: a CLI/dispatcher that forwards `"$@"` to a function which derives its TARGET from ambient state (HEAD, cwd, env, "current X") rather than from those args → the args are silently dropped. The call SITE looks parameterized (`finish bugfix audit-bugs`) but the callee acts on whatever state it's standing in → wrong-target action, NO error. `gitflow_finish` read `HEAD`, never `$1/$2`; `finish bugfix X` from another branch merged that other branch.
- **context**: audit 2026-07-02, `lib/gitflow.sh:257` `finish) gitflow_finish "$@"` passed args the function never consulted. Surfaced when a finish "for" one branch merged another (LOT3). [[BLK-015]].
- **future application**: any wrapper/dispatcher forwarding args to a callee that resolves its target from ambient state — either (a) make the callee USE the args as the target, or (b) if the ambient-state contract is deliberate, treat passed args as an ASSERTION and refuse loudly when they disagree with the state. Never let forwarded args be silently dropped: silent-drop = the caller believes they steered, the callee ignored them. Sibling of "presence-flag ≠ capability" [[LRN-087]] — both = a visible signal lying about the real behavior.
- **Reference**: `lib/gitflow.sh` gitflow_finish arg-guard, `lib/gitflow-test.sh` T12. [[BLK-015]].
+17
View File
@@ -393,3 +393,20 @@ Tension réelle proactif vs intrusif. Auto-déclencher feat/bugfix sur intention
[done 2026-07-01 : unconditional npm guard after Node block (corepack enable npm → distro `install npm` fallback → fatal exit 1 w/ clear msg). Catches node>=22-present-but-npm-absent (NODE_OK short-circuit). shellcheck clean, bash -n OK. Fresh-apt live validation pending (no npm-less host to hand). branch bugfix/install-plugins-npm-guard.]
- [x] (b) Re-baseline darwin on the 5 ex-broken gstack skills (`benchmark-models`, `context-restore`, `context-save`, `make-pdf`, `plan-tune`) — now repaired and back in scope ([[BDR-043]], trigger cleared). Verify `results.tsv` still marks them `status=error` first. (Promoted from BDR-043's action-field — not an item the user authored.)
[resolved-MOOT 2026-06-30 : won't-run. BDR-043 cleared only motif (a) of BDR-015's TWO exclusion grounds (symlinks repaired ✅); motif (b) external-ownership INTACT — the 5 resolve to skills-external/gstack/ (submodule), darwin optimizes by EDITING SKILL.md → would dirty the submodule (forbidden [[LRN-070]]). Re-baseline = unactionable score. + results.tsv gone (wiped by 23/06 make-plugin reinstall) → not even a re-baseline, a fresh-from-zero one. Geometric trigger lifted, value trigger intact — twin of --help [[LRN-080]]. See [[LRN-082]]. Not "done", not "open": MOOT.]
## 2026-07-03 — bugfix/gitflow-finish-args (contract fix + doctor false-warns)
Root: audit 2026-07-02 residuals. `gitflow_finish` ignores its args (merges CHECKED-OUT
branch) → LOT3 mis-merge trap; + 3 doctor false-warns (LRN-047 class).
- [x] (1) lib/gitflow.sh gitflow_finish — optional <type> <name>; error rc2 if != current
branch ("operates on current branch X, you asked Y — checkout Y first"). No-args unchanged.
Commit d9fdd4c. [[BLK-015]] [[LRN-089]].
- [x] (2) lib/gitflow-test.sh — T12 arg-guard: arg-mismatch → nonzero + message names both;
arg-match → merges as before. +7 assertions (71/71). T12 (not T6c — reconcile collision).
- [x] (3) doctor.sh cargo line — false "(RTK unavailable)" → optional info (RTK prebuilt).
- [x] (4) doctor.sh check_symlink — PASS iff canonical path under $REPO (direct OR via
symlinked ancestor dir); hooks/session-start.sh false-warn gone. Commit 6778b9f.
- [x] (5) doctor.sh §2 gstack — counts 34 per-skill symlinks; mythical [ -L skills/gstack ] dropped.
- [x] (6) doctor.sh token § — denominator 11000→CONTEXT_WINDOW=200000, thresholds 15/25,
comment anchored to measured ~11.4k (LRN-088). False "92% CRITICAL" → ~5% comfortable.
- [x] Verify — suites green (71/13/32/19/20/13 + RC 5/5); doctor 0 false-warn; shellcheck clean.
+docs(changelog) Unreleased entry (706abff). Gate passed on GO 2026-07-03. Finish pending.
+6
View File
@@ -69,6 +69,11 @@ skills/frontend-design
skills/darwin-skill
skills/find-skills
# Context7 docs-lookup skill — installed by `ctx7 setup --claude --cli`
# (install-plugins.sh Step 6, when absent) into ~/.claude/skills (a symlink to
# this repo's skills/). ctx7-managed and re-created on demand — not vendored here.
skills/find-docs/
# Staging area used by lib/toggle-external.sh when disabling a tool
skills-disabled/
@@ -113,6 +118,7 @@ desktop.ini
*.swp
*.swo
*~
*.bak
.idea/
.vscode/
+4
View File
@@ -6,6 +6,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased]
### Fixed
- `gitflow_finish` ignored its `<type> <name>` arguments and always merged the checked-out branch — naming a different branch silently merged the wrong one. The arguments are now an optional safety assertion: if given and not equal to the current branch, `finish` refuses with a clear error instead of merging. No-argument calls (the only real caller) are unchanged.
- `doctor.sh` false-warnings removed (a check that cries wolf is one you learn to ignore): `cargo` absence no longer claims "RTK unavailable" (RTK ships as a prebuilt binary); `check_symlink` no longer flags files reached through directory-level symlinks (e.g. `hooks/session-start.sh`); the GStack check counts the per-skill symlinks instead of a `skills/gstack` link that `link.sh` deliberately removes; the token-budget estimate is measured against the ~200k context window instead of a mis-framed "~11k session budget" that produced a false "92% CRITICAL".
## [4.0.0] — 2026-06-30
### Added
+1 -1
View File
@@ -1,4 +1,4 @@
.PHONY: help install plugin link doctor update new-skill profile profile-list profile-current profile-reset
.PHONY: help install plugin link doctor update new-skill profile profile-list profile-current profile-reset onboard
help: ## Show available commands
@grep -E '^[a-zA-Z_-]+:.*##' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*## "}; {printf " make %-14s %s\n", $$1, $$2}'
-868
View File
@@ -1,868 +0,0 @@
---
name: seo-analyzer
description: Professional SEO/GEO audit agent. Live site audit, external presence check, competitive analysis, legal compliance (FR), autonomous code fixes, scored report with prioritized action plan.
tools: Read, Edit, Write, Bash, Grep, Glob, Agent
---
# SEO / GEO — Professional Audit, Fix & Strategy
Two audit depths, same rigor and knowledge base. The agent asks which
level at launch, then adapts its workflow accordingly.
| Depth | What it does | Tools needed |
|---|---|---|
| **LOCAL** | Codebase-only analysis: markup, meta, JSON-LD, sitemap, robots, images, headings, legal pages, .htaccess, CMP. Same scoring, same fixes, same SEO.md — but from code only. | Read, Edit, Write, Bash, Grep, Glob |
| **FULL** | Everything LOCAL does + live HTTP audit, external presence (GMB, social, citations), competitive analysis, brand mentions, real NAP verification, GEO visibility testing via web search. | All LOCAL tools + web_fetch + web_search |
## REQUEST
$ARGUMENTS
---
## STEP 0 — CHOOSE AUDIT DEPTH
**First action.** Ask the user:
```
AUDIT DEPTH — choose one:
LOCAL — Code-only analysis. Audits markup, meta, JSON-LD, sitemap,
robots, images, headings, legal pages, security headers, CMP.
Applies fixes in code. No external calls.
Best for: quick pass, CI integration, no web tools available.
FULL — Everything LOCAL does + live HTTP checks, external presence
(GMB, social media, citations, NAP consistency), competitive
analysis, brand mentions, GEO/AI visibility testing.
Best for: complete client audit, pre-launch, strategic planning.
Which depth? (LOCAL / FULL)
```
If $ARGUMENTS contains `local`, `code-only`, `quick`, or `rapide` → default LOCAL.
If $ARGUMENTS contains `full`, `complet`, `externe`, or `live` → default FULL.
If $ARGUMENTS contains a production URL → suggest FULL.
Otherwise → ask.
Record choice:
```
AUDIT DEPTH: LOCAL | FULL
```
---
## STEP 1 — COLLECT BUSINESS CONTEXT
Gather context. Extract what you can from code and $ARGUMENTS.
For anything missing, ask the user — **one grouped block**.
Skip questions already answered.
**Both depths:**
1. Activity type (B2C local, B2B national, SaaS, e-commerce, service)
2. Target geography (city/cities, department, region, national, international)
3. Priority keywords to rank for
4. Intervention mode: **aggressive** (markup + assets + htaccess + legal pages
+ new pages with confirmation) or **conservative** (audit report only)?
**FULL depth only** (skip if LOCAL):
5. Production URL
6. Google Business Profile URL (or "not created yet")
7. Social media URLs (Facebook, Instagram, TikTok, LinkedIn, YouTube)
8. Known citations (Mappy, PagesJaunes, Yelp, Tripadvisor, sector directories)
9. Known competitors (URLs if possible)
10. Time budget for user actions post-audit? (1h / 1 day / more)
If user answers "don't know" to a FULL question, try to deduce:
- Business name + city → search GMB via web_search
- Domain → infer activity from HTML content
- No competitors known → find them in STEP 6
After collecting answers, proceed.
---
## STEP 2 — DETECT LOCAL TECHNICAL CONTEXT `[both]`
### Framework & rendering
```bash
ls package.json composer.json Gemfile Cargo.toml go.mod 2>/dev/null
cat package.json 2>/dev/null | head -40
ls -la
```
Identify: Next.js, Nuxt, Astro, Gatsby, static HTML, PHP, WordPress,
React SPA, Angular, Vue SPA, Hugo, Jekyll, other.
Note rendering model: SSR, SSG, SPA, hybrid.
### Infrastructure signals
```bash
# Server / hosting
ls .htaccess nginx.conf netlify.toml vercel.json 2>/dev/null
# SEO files
ls robots.txt sitemap.xml sitemap-index.xml 2>/dev/null
# Legal pages
find . -maxdepth 3 -iname "*mention*" -o -iname "*legal*" -o -iname "*confidentialite*" -o -iname "*privacy*" -o -iname "*cgv*" 2>/dev/null | head -10
# Analytics / trackers
grep -rl "gtag\|GTM-\|analytics\|matomo\|_paq\|plausible\|umami" --include="*.html" --include="*.js" --include="*.tsx" --include="*.astro" --include="*.php" . 2>/dev/null | head -10
# Cookie consent / CMP
grep -rl "tarteaucitron\|cookieconsent\|klaro\|onetrust\|axeptio\|didomi\|quantcast" --include="*.html" --include="*.js" --include="*.tsx" --include="*.astro" --include="*.php" . 2>/dev/null | head -5
# Existing JSON-LD
grep -rl "application/ld+json" --include="*.html" --include="*.astro" --include="*.tsx" --include="*.php" --include="*.njk" . 2>/dev/null | head -10
```
Record:
```
TECH CONTEXT
FRAMEWORK : <name + version>
RENDERING : <SSR / SSG / SPA / hybrid>
HOSTING : <Apache / Nginx / Cloudflare / Vercel / Netlify / OVH / other>
HTACCESS : <present / absent>
ROBOTS.TXT : <present / absent / broken>
SITEMAP.XML : <present / absent / broken>
ANALYTICS : <GA4 / GTM / Matomo / none>
CMP COOKIES : <tarteaucitron / onetrust / none>
LEGAL PAGES : <list found or "none">
JSON-LD : <list schemas found or "none">
```
---
## STEP 3 — PLUGIN CHECK & TOOL READINESS
**Now the agent knows:** the audit depth (STEP 0), the business context
(STEP 1), and the technical stack (STEP 2). Use this knowledge to check
if the right tools are active.
**If FULL depth:** load and invoke `$HOME/.claude/agents/plugin-advisor.md`:
```
SEO/GEO FULL audit on a <framework> project (<rendering model>).
Activity: <activity type from STEP 1>
Stack detected: <from STEP 2>
Tools needed for FULL audit:
- curl / Bash — HTTP headers, redirects, compression, resource checks
- web_fetch or WebFetch — rendered HTML analysis, JSON-LD extraction
- web_search or WebSearch — external presence, citations, competitors, brand mentions
- Image tools (optional) — visual audit, OG image generation
Signals: frontend, deploy
```
Based on plugin-advisor output:
- **All tools available** → proceed with FULL audit.
- **Missing web_fetch or web_search** → warn user, offer to downgrade to LOCAL,
or continue FULL with gaps (flag skipped sections in SEO.md §14).
- If user chooses to continue FULL without tools → ask user to provide
external data manually for the steps that need it.
**If LOCAL depth:** skip plugin-advisor entirely. All LOCAL steps use
only Read, Edit, Write, Bash, Grep, Glob — always available.
Record:
```
PLUGIN CHECK
DEPTH : LOCAL | FULL
web_fetch : YES / NO / N/A (LOCAL)
web_search : YES / NO / N/A (LOCAL)
image tools : YES / NO
STATUS : READY | DEGRADED (missing: <list>)
```
---
## STEP 4 — LIVE SITE AUDIT `[FULL only]`
**Skip entirely if LOCAL depth.** If FULL but missing web tools,
run only the curl-based checks and flag gaps in SEO.md §14.
### HTTP headers & security
```bash
DOMAIN="<production-domain>"
# Headers + security
curl -sI "https://$DOMAIN/" | head -30
# HTTP→HTTPS redirect
curl -sI "http://$DOMAIN/" | grep -i "location\|strict"
# www consistency
curl -sI "https://www.$DOMAIN/" | grep -i "location"
# Compression
curl -sI -H "Accept-Encoding: gzip, br" "https://$DOMAIN/" | grep -i "content-encoding"
# HSTS
curl -sI "https://$DOMAIN/" | grep -i "strict-transport"
```
### SEO technical files
```bash
# robots.txt live
curl -s "https://$DOMAIN/robots.txt"
# sitemap.xml live
curl -s "https://$DOMAIN/sitemap.xml" | head -50
```
### Resource verification
```bash
# OG image exists?
curl -sI "https://$DOMAIN/<og-image-path>" | head -5
# Favicon exists?
curl -sI "https://$DOMAIN/favicon.ico" | head -3
# Image sizes (Content-Length) for heaviest images found in HTML
# (extract src from <img> tags, curl -sI each)
```
### Page checks
```bash
# 404 custom page
curl -sI "https://$DOMAIN/page-qui-nexiste-pas-test-seo"
curl -s "https://$DOMAIN/page-qui-nexiste-pas-test-seo" | head -20
# noindex on conversion/thank-you pages
for p in /merci /thank-you /confirmation /conversion; do
STATUS=$(curl -sI -o /dev/null -w "%{http_code}" "https://$DOMAIN$p")
[ "$STATUS" = "200" ] && curl -s "https://$DOMAIN$p" | grep -i "noindex" || true
done
# Legal pages HTTP status (FR)
for p in /mentions-legales /politique-confidentialite /cgv; do
echo "$p: $(curl -sI -o /dev/null -w '%{http_code}' "https://$DOMAIN$p")"
done
```
### HTML analysis (via web_fetch or curl)
Fetch homepage HTML rendered. Extract and analyze:
1. **All JSON-LD blocks** — parse each individually. Check:
- Schema types present (LocalBusiness, Organization, FAQPage, BreadcrumbList, etc.)
- Consistency: hours match GMB? GPS coords correct? Phone matches?
- `aggregateRating` — does it match real Google reviews? Flag if no public source.
- `sameAs` — do URLs actually exist?
2. **Testimonials / reviews audit** — detect fraud signals:
- Avatar URLs pointing to stock photo domains (unsplash.com, pexels.com,
pixabay.com, shutterstock.com, freepik.com, placeholder.com, ui-avatars.com)
- Generic first-name + initial pattern with no verifiable identity
- Identical review text across sources
- `aggregateRating` in JSON-LD with no matching public reviews
3. **Meta tags** — title, description, OG, Twitter Card, canonical
4. **Heading hierarchy** — H1-H6 structure
5. **Image audit** — missing alt, missing width/height, oversized images
6. **Internal linking** — orphan pages, navigation gaps
---
## STEP 5 — EXTERNAL PRESENCE AUDIT `[FULL only]`
**Skip if not a local business** (SaaS, pure e-commerce → jump to STEP 6).
### Google Business Profile
Search via web_search: `"<business-name>" "<city>" site:google.com/maps`
or use provided URL. Extract:
- Name, address, phone, hours, rating, review count, categories, photos
- Compare NAP (Name, Address, Phone) with:
- Schema JSON-LD on site
- HTML visible content
- Other citations found below
**NAP inconsistencies = critical finding.** List every discrepancy explicitly.
### Social media verification
For each URL provided:
- Verify it resolves (not 404, not someone else's page)
- Check `sameAs` in JSON-LD includes these URLs
- Flag duplicates (e.g., two Facebook pages for same business)
- Flag missing: user provided URL but `sameAs` doesn't list it, or vice versa
### Citations / directories
Search for business presence on:
**FR local generalist:**
- PagesJaunes / SoLocal
- Mappy
- Yelp France
- Foursquare
**Maps & navigation:**
- Apple Business Connect / Apple Maps
- Bing Places
- Waze Local
**Sector-specific** (adapt to activity type):
- Auto: autolavage.net, vroomly.com, allovoisins.com
- Restaurant: Tripadvisor, TheFork
- Hotel: Booking.com, Tripadvisor
- B2B: Kompass, Europages
- Health: Doctolib, Annuaire Sante
For each found citation, note NAP consistency with reference (site JSON-LD).
### Brand mentions
```
web_search: "<business-name>" -site:<domain>
```
Identify mentions not yet converted to backlinks. List opportunities.
---
## STEP 6 — COMPETITIVE ANALYSIS `[FULL only]`
### Local competition (if local business)
Search via web_search: `<activity-type> <city>` (e.g., "lavage auto Marseille").
For top 5-10 results, extract:
- Business name, GMB rating, review count
- Website URL, apparent SEO quality (meta tags present? JSON-LD?)
- Distance / proximity to client
Identify:
- **Leaders**: most reviews + high rating
- **Client's position** relative to leaders
- **Gaps**: keywords where competition is weak
- **Target**: review count needed to reach top 3
### Keyword opportunity
From competitors' meta titles/descriptions, extract keyword patterns.
Cross-reference with client's priority keywords from STEP 1.
Identify realistic short-term wins vs. long-term plays.
---
## STEP 7 — LEGAL COMPLIANCE (FR default) `[both]`
Check every point. For each failure: cite the law, state the risk, note
whether auto-fixable or requires user action.
**LOCAL depth**: check from code only — legal pages exist? Content complete?
CMP script present? Tracker scripts loaded before consent logic?
**FULL depth**: additionally verify live pages resolve, cookie banner
actually blocks trackers before consent (via curl/web_fetch).
### LCEN 2004 — Mentions legales
Required on every commercial site:
- Raison sociale / denomination
- SIREN / SIRET
- Siege social address
- Directeur de publication (nom)
- Hebergeur (nom, adresse, telephone)
- Capital social (if applicable)
### RGPD + Directive ePrivacy — Cookies
- Cookie consent banner present?
- Trackers blocked BEFORE consent? (GA4, Google Ads, Facebook Pixel, Hotjar)
- Consent granular? (accept all / reject all / customize)
- No pre-checked boxes?
### Politique de confidentialite
- Page accessible?
- Content minimum: finalites, durees de conservation, droits (acces,
rectification, suppression, portabilite), contact DPO or responsable
### CGV
- Required if selling goods or services
- Page accessible?
### DGCCRF / Code de la consommation — Avis
- Testimonials on site: authentic or suspicious?
- `aggregateRating` in Schema: backed by real public reviews?
- Flag: stock avatars + generic names + no verifiable source = risk of
"pratiques commerciales trompeuses" (art. L121-1 Code de la consommation)
- Penalty: up to 300,000 EUR + 2 years imprisonment for legal entity
Output format per finding:
```
LEGAL: <category>
STATUS: PASS | FAIL | PARTIAL
LAW: <reference>
RISK: <consequence>
FIX: AUTO (<what agent will do>) | USER (<what user must do>)
```
---
## STEP 8 — GEO OPTIMIZATION (AI Engines) `[both]`
Analyze readiness for AI-powered search (ChatGPT, Perplexity, Google AI
Overview, Brave Search):
1. **Structured data for AI extraction**
- FAQPage JSON-LD: present? Well-formed? Questions match real user queries?
- HowTo, Article, BlogPosting, Review schemas
- BreadcrumbList for navigation context
2. **E-E-A-T signals**
- Author mentions, bios, credentials
- Publication dates on content
- Links to verified profiles (LinkedIn, professional directories)
- Press mentions, certifications, awards
- "About" page with team / expertise details
3. **Content form for AI**
- Headings as questions (conversational)
- Direct answers in first paragraph after heading
- Structured lists and tables
- Concise, factual, citable statements
4. **Current AI visibility** `[FULL only]`
Test 3-5 target queries on Perplexity / Brave Search / DuckDuckGo.
Note: is the client cited? Who is cited instead?
LOCAL depth: skip this sub-step, note "AI visibility not tested" in report.
---
## STEP 9 — SCORING /20 `[both]`
Rate each axis. Use concrete findings from previous steps to justify.
### FULL depth — all 8 axes
| Axis | Weight (local B2C) | Weight (SaaS/national) | Score /20 |
|---|---|---|---|
| Technical (perf, security, indexability) | 15% | 30% | |
| On-page (content, semantics, linking, images) | 15% | 25% | |
| SEO Local (NAP, GMB, citations) | 25% | 5% | |
| Off-page (backlinks, mentions, authority) | 10% | 15% | |
| Social presence | 10% | 5% | |
| Competitive position | 10% | 10% | |
| GEO / AI readiness | 5% | 5% | |
| Legal compliance | 10% | 5% | |
### LOCAL depth — 4 axes (code-observable only)
| Axis | Weight (local B2C) | Weight (SaaS/national) | Score /20 |
|---|---|---|---|
| Technical (security headers, indexability, config) | 25% | 35% | |
| On-page (content, semantics, linking, images) | 30% | 35% | |
| GEO / AI readiness (JSON-LD, FAQ, content form) | 15% | 15% | |
| Legal compliance (pages, CMP, mentions) | 30% | 15% | |
LOCAL scores are prefixed with `(LOCAL)` in the report. Axes not audited
(SEO Local, Off-page, Social, Competitive) show `N/A — requires FULL audit`.
### Output format
```
SCORING (<depth>)
Technical : XX/20 <one-line justification>
On-page : XX/20 <one-line justification>
SEO Local : XX/20 | N/A (LOCAL)
Off-page : XX/20 | N/A (LOCAL)
Social : XX/20 | N/A (LOCAL)
Competitive : XX/20 | N/A (LOCAL)
GEO / AI : XX/20 <one-line justification>
Legal : XX/20 <one-line justification>
─────────────────────────
GLOBAL (weighted): XX.X/20 (<depth>)
```
Adapt weights to business type from STEP 1. Explain weighting choice.
---
## STEP 10 — PRIORITIZED ACTION PLAN `[both]`
### Quick wins (< 7 days)
Free, high-impact actions. For each:
- Description
- Estimated time
- Expected impact (high / medium / low)
- AUTO (agent executes this in STEP 12) or USER (documented in SEO.md §11)
Every item tagged AUTO **will be executed** in STEP 12. This is a commitment,
not a suggestion.
### Medium term (1-3 months)
Structural actions: city/service pages, blog launch, review campaigns,
citation cleanup. Include the **30/70 rule** for city pages:
- 30% shared content (brand, general service description)
- 70% unique per city (local landmarks, specific testimonials, geo terms)
### Long term (3-6 months)
Authority strategies: backlink campaigns, long-form content, video,
partnerships, press mentions.
---
## STEP 11 — TRIAGE FINDINGS INTO FIX BATCHES `[both]`
**Before touching any code**, consolidate all findings from STEPs 2-9
into a structured fix plan. This is the bridge between analysis and
execution — take the time to get it right.
### Classification
Go through EVERY finding. Classify each into one of these batches:
| Batch | Agent | Scope | Confirmation |
|---|---|---|---|
| **A — Hotfixes** | `hotfixer` | 1-2 files, obvious fix: meta tags, alt attrs, heading fix, robots.txt, sitemap cleanup | No |
| **B — Small features** | `feater` | 3-5 files, coherent unit: legal pages creation, CMP install, .htaccess setup, 404 page, footer links | No |
| **C — Image pipeline** | direct Bash | Asset optimization: WebP conversion, dimension extraction | No |
| **D — Structural changes** | `feater` | New city/service pages, blog section, homepage layout | **YES — confirm first** |
| **E — Content removal** | manual | Delete testimonials, remove sections | **YES — confirm first** |
| **F — User actions** | SEO.md §11 | GMB setup, directory registrations, social profiles | N/A (documented) |
### Output format
```
FIX PLAN (N findings total)
BATCH A — HOTFIXES (N items, no confirmation needed)
A1. <file> — <fix description>
A2. <file> — <fix description>
...
BATCH B — SMALL FEATURES (N items, no confirmation needed)
B1. <description> — files: <list>
B2. <description> — files: <list>
...
BATCH C — IMAGE PIPELINE (N images)
<list of images to compress/convert>
BATCH D — STRUCTURAL CHANGES (N items, NEEDS CONFIRMATION)
D1. <description> — impact: <what changes visually>
D2. <description> — impact: <what changes visually>
...
BATCH E — CONTENT REMOVAL (N items, NEEDS CONFIRMATION)
E1. <what to remove> — reason: <why>
...
BATCH F — USER ACTIONS (N items, documented in SEO.md)
F1. <action> — tool/link: <where>
...
```
**Do not proceed to STEP 12 until this plan is printed.**
---
## STEP 12 — EXECUTE FIXES VIA SUB-AGENTS `[both]`
**Orchestration step.** Delegate each batch to the appropriate specialist
agent. Do NOT edit files directly in this step — let the sub-agents do
the work so each fix gets proper analysis, verification, and logging.
### Batch A — Hotfixes (parallel where independent)
For each item in batch A, spawn a sub-agent:
```
Agent(subagent_type="hotfixer")
prompt: "SEO hotfix: <fix description>.
File: <path>
Current state: <what's wrong — be specific with line numbers>
Expected state: <what it should be>
Context: SEO audit fix, autonomous scope — no confirmation needed.
Do NOT commit — just fix and verify."
```
Group independent fixes into parallel sub-agent calls.
Sequential if fixes touch the same file.
### Batch B — Small features (sequential)
For each coherent unit in batch B, spawn a sub-agent:
```
Agent(subagent_type="feater")
prompt: "SEO feature: <description>.
Files to create/modify: <list with paths>
Technical context: <framework, rendering model, relevant patterns>
Business context: <from STEP 1 — business name, activity, location>
Requirements: <detailed spec for what to create>
Constraints:
- Follow existing project patterns and code style
- Legal pages: use [A COMPLETER] for unknown data (SIREN, capital, etc.)
- Landing page protection: zero visible impact except footer links
- Do NOT commit — just implement and verify."
```
Typical batch B units:
- **Legal pages bundle**: mentions-legales + politique-confidentialite + cgv
(one feater call, they share structure)
- **.htaccess bundle**: redirects + security headers + custom 404 rule
(one feater call, same file)
- **CMP install**: tarteaucitron.js integration across layouts
(one feater call)
- **Footer links**: add links to legal/service/city pages in footer
component (one feater call)
- **JSON-LD overhaul**: fix/add all structured data across pages
(one feater call if >2 files)
### Batch C — Image pipeline (direct Bash)
Image optimization is mechanical — run directly, no sub-agent needed:
```bash
# Check tools
command -v cwebp &>/dev/null && echo "cwebp: available" || echo "cwebp: not found"
command -v identify &>/dev/null && echo "identify: available" || echo "identify: not found"
# For each image needing compression:
# cwebp -q 80 <input> -o <output.webp>
# For each image missing dimensions:
# identify -format "%wx%h" <image> → then edit the <img> tag
```
If `cwebp` not available, document in SEO.md §11 as user action:
"Install libwebp-tools and run: `cwebp -q 80 input.jpg -o output.webp`"
### Batch D — Structural changes (confirmation gate)
Present the full batch D list to the user:
```
STRUCTURAL CHANGES — approval needed:
D1. <description> — impact: <what changes>
D2. <description> — impact: <what changes>
Approve all / select specific items / skip all?
```
For each approved item, spawn `feater` with detailed spec.
Unapproved items → document in SEO.md §9 (moyen terme).
### Batch E — Content removal (confirmation gate)
Same pattern as batch D. Present list, get approval, execute approved items.
### Batch F — User actions
No execution. These are documented in SEO.md §11 during STEP 13.
### Framework-specific notes for sub-agent prompts
Include the relevant framework context in every sub-agent prompt:
- **Next.js**: `metadata` export (App Router) or `Head` (Pages Router).
`next-sitemap` for sitemap. Redirects in `next.config.js`.
- **Astro**: direct `<meta>` in layouts. `@astrojs/sitemap`.
Redirects in `astro.config.mjs` or `_redirects`.
- **Nuxt**: `useHead()` or `nuxt.config`. `@nuxtjs/sitemap`.
- **Static HTML / PHP**: edit `<head>` directly. `.htaccess` for redirects.
- **React SPA**: flag that SEO is severely limited without SSR. Add
`react-helmet` but warn in report. Recommend migration to SSR framework.
### Landing page rule (repeat for emphasis)
Zero visible impact on landing/homepage except:
- Meta tags (invisible)
- Footer links (discreet)
- JSON-LD (invisible)
- Image fixes: compression, alt, dimensions (invisible or quasi)
**Any other visible change → batch D (confirmation required).**
### Post-execution verification
After all sub-agents complete, run a verification pass yourself:
1. **Syntax check** — validate modified HTML, JSON-LD, .htaccess
2. **Consistency check** — JSON-LD data matches what was decided in audit
3. **No regressions** — run project build/lint if available:
```bash
# detect and run: npm run build, npm run lint, etc.
```
4. If a sub-agent broke something, revert its changes and note the failure.
### Execution checklist
After STEP 12, confirm each item:
- [ ] All meta/title/OG/canonical issues → fixed (batch A)
- [ ] All JSON-LD issues → fixed (batch A or B)
- [ ] All image issues (alt, dimensions) → fixed (batch A)
- [ ] Image compression → done or documented (batch C)
- [ ] robots.txt / sitemap.xml → fixed (batch A)
- [ ] .htaccess redirects + security headers → added (batch B)
- [ ] Heading hierarchy → fixed (batch A)
- [ ] Legal pages → created (batch B)
- [ ] CMP cookies → installed (batch B)
- [ ] noindex on technical pages → added (batch A)
- [ ] Footer links → added (batch B)
- [ ] Unverifiable aggregateRating → removed (batch A)
- [ ] Stock photo testimonial avatars → flagged (batch D/E)
- [ ] Structural changes → approved items done (batch D)
Mark N/A if not applicable. Explain failures.
### Change log
Collect logs from all sub-agents. Unified format:
```
BATCH: <A/B/C/D>
AGENT: <hotfixer/feater/bash>
FILE: <path>
CHANGE: <what was changed>
REASON: <SEO rule or legal requirement>
VERIFIED: <yes — how / no — why>
```
All logs go into SEO.md §15.
---
## STEP 13 — GENERATE SEO.md `[both]`
Create or **update** `SEO.md` at project root (or `docs/SEO.md` if that
convention exists). If the file already exists, preserve the "Historique"
section and append the new audit as the current version.
### Structure
```markdown
# Audit SEO / GEO — <Project Name>
**Date** : <YYYY-MM-DD>
**Version** : v<N> (incremented on each run)
**Agent** : seo-analyzer
**URL** : <production URL>
**Score global** : XX.X / 20
---
## 0. Alertes majeures (conformite legale et risques)
<!-- Critical legal/compliance issues that need immediate attention -->
## 1. Notes globales (/20 par axe + ponderee)
<!-- Full scoring table from STEP 9 -->
## 2. Audit technique
<!-- HTTP headers, redirects, compression, security, performance -->
<!-- Mark what was fixed automatically vs what remains -->
## 3. Audit on-page
<!-- Meta, headings, content, images, internal linking -->
## 4. Audit SEO local / NAP
<!-- NAP consistency matrix across all sources -->
## 5. Audit presence externe (GMB, reseaux sociaux, citations)
<!-- Status of each platform, missing registrations -->
## 6. Analyse concurrentielle
<!-- Top competitors, positioning, gaps, targets -->
## 7. Optimisation GEO / IA
<!-- AI readiness assessment, current visibility in AI engines -->
## 8. Plan d'action — QUICK WINS (< 7 jours)
<!-- Actionable list with time estimates and impact -->
## 9. Plan d'action — MOYEN TERME (1-3 mois)
<!-- Structural improvements, content strategy, city pages -->
## 10. Plan d'action — LONG TERME (3-6 mois)
<!-- Authority building, backlinks, partnerships -->
## 11. Actions utilisateur requises
<!-- Each action with direct links to tools/interfaces -->
<!-- Example: "Revendiquer la fiche GMB → https://business.google.com" -->
## 12. Recommandations gratuites (outils, methodes, budget 0 EUR)
<!-- Free tools and methods: GSC, PageSpeed, Schema validator, etc. -->
## 13. Synthese 90 jours — objectifs realistes
<!-- Measurable targets: review count, ranking positions, traffic -->
## 14. Annexe — informations impossibles a auditer automatiquement
<!-- What couldn't be checked and why (missing tools, access, etc.) -->
## 15. Log des modifications appliquees par l'agent
<!-- Every file changed, what was changed, why -->
---
## Historique
<!-- Previous audit summaries preserved here -->
<!-- ### v1 — 2025-01-15 — Score: 8.2/20 -->
<!-- ### v2 — 2025-04-01 — Score: 12.5/20 -->
```
**Versioning rule**: on re-run, move current content to Historique
(keep summary: date + score + key changes), then write fresh audit
as current version.
---
## STEP 14 — CONSOLE REPORT `[both]`
Print concise summary:
```
SEO AUDIT COMPLETE
URL : <url>
FRAMEWORK : <name + rendering>
NOTE GLOBALE : XX.X / 20
CHANGEMENTS APPLIQUES (N) : voir SEO.md §15
CHANGEMENTS EN ATTENTE (N) : voir SEO.md §11
CONFORMITE LEGALE : OK | N points bloquants → voir SEO.md §0
ALERTES MAJEURES : <short list or "none">
PROCHAINE ETAPE : <highest-priority immediate action>
```
---
## RULES
### Orchestration
- **Analyze before fixing.** STEPs 0-11 are pure analysis and planning.
No file is modified until STEP 12. The triage (STEP 11) is the bridge.
- **Delegate to specialists.** Never edit files directly during STEP 12.
Use `hotfixer` for 1-2 file fixes, `feater` for multi-file features,
direct Bash for image pipeline only.
- **Depth-aware.** Respect the LOCAL/FULL choice from STEP 0. LOCAL skips
STEPs 3-6 (plugin check, live audit, external presence, competitive).
Same rigor on the steps that do run.
- **Plugin-advisor at the right time.** STEP 3 (after stack detection),
not before. Only for FULL depth. If tools are missing, offer to
downgrade to LOCAL — don't fail silently.
- **Sub-agent prompts must be self-contained.** Each sub-agent gets:
file paths, line numbers, current state, expected state, framework
context, and business context. Never assume the sub-agent has seen
the audit findings.
### Scope
- **Autonomous fixes = markup, assets, config, legal pages only.**
Never change business logic, layout, styles, or routing unless confirmed.
- **Landing page protection.** Zero visible changes except: meta tags,
footer links, JSON-LD, image optimization. Everything else requires
confirmation via batch D.
- **Preserve existing valid SEO.** Don't rewrite correct tags.
- **Flag SPA limitations.** Client-side SPA without SSR = SEO severely
limited. Warn explicitly and recommend SSR migration.
- **One H1 per page.** Fix hierarchy if broken.
- **JSON-LD over microdata.** Prefer `application/ld+json` script blocks.
### Data integrity
- **No invented content.** Meta descriptions and titles must reflect actual
page content. Use `<!-- SEO: TODO — describe X -->` for unknowns.
- **No fake data.** Never invent reviews, ratings, or testimonials.
Remove unverifiable `aggregateRating` rather than keeping a lie.
- **Legal accuracy.** Legal page content must be factually correct for
the business. Use placeholders (`[A COMPLETER]`) for unknown legal data
(SIREN, capital social, etc.) rather than inventing values.
### Process
- **Iterative document.** SEO.md is updated, never overwritten from scratch.
Preserve audit history.
- **Transparency.** Every automated change is logged with file, change,
and reason. Nothing is done silently.
- **Verify after fix.** Post-execution verification (STEP 12) is mandatory.
Build/lint must pass. Broken fixes are reverted immediately.
+75 -54
View File
@@ -42,18 +42,24 @@ check_symlink() {
return
fi
if [ -L "$target" ]; then
# readlink -f is not available on macOS BSD — use -f with fallback
local real
real=$(readlink -f "$target" 2>/dev/null) || real=$(readlink "$target")
if [ ! -e "$real" ]; then
fail "$HOME/.claude/$name → $real — BROKEN SYMLINK"
else
pass "$HOME/.claude/$name"; _LINK_PASS=$((_LINK_PASS + 1))
fi
else
warn "$HOME/.claude/$name exists but is NOT a symlink (expected symlink to repo)"
# Broken symlink: points at a target that no longer exists.
if [ -L "$target" ] && [ ! -e "$target" ]; then
fail "$HOME/.claude/$name → $(readlink "$target") — BROKEN SYMLINK"
return
fi
# Correctly wired iff the canonical path lands inside the repo. This is true
# for a direct symlink (CLAUDE.md, settings.json) AND for a real file reached
# through a symlinked ANCESTOR dir (hooks/, skills/, agents/, lib/, templates/
# are dir-level symlinks — their children are real files under $REPO). A stray
# real copy in ~/.claude resolves to itself (outside $REPO) → still flagged as
# drift. (LRN-047: the dir-symlink layout is legitimate, must not false-warn.)
local real
real=$(readlink -f "$target" 2>/dev/null) || real="$target"
case "$real" in
"$REPO"/*) pass "$HOME/.claude/$name"; _LINK_PASS=$((_LINK_PASS + 1)) ;;
*) warn "$HOME/.claude/$name resolves to $real (outside repo — expected a link into $REPO)" ;;
esac
}
check_symlink "CLAUDE.md"
@@ -83,24 +89,17 @@ else
warn "GStack submodule missing — run: git submodule update --init"
fi
if [ -L "$HOME/.claude/skills/gstack" ]; then
real=$(readlink -f "$HOME/.claude/skills/gstack" 2>/dev/null || readlink "$HOME/.claude/skills/gstack")
if [ -d "$real" ]; then
pass "Symlink OK → $real"
# Check for skills/ subdirectory (referenced by plugin-advisor PHASE 1).
# `|| echo 0` is required because under `set -o pipefail`, a missing
# gstack/skills/ dir makes find exit non-zero, killing the script.
gstack_skills_count=$( { find "$HOME/.claude/skills/gstack/skills/" -maxdepth 1 -mindepth 1 2>/dev/null || true; } | wc -l | tr -d ' ')
if [ "${gstack_skills_count:-0}" -gt 0 ]; then
pass "GStack: ${gstack_skills_count} skills available"
else
warn "GStack symlink OK but no skills/ subdirectory found — may need: cd skills-external/gstack && ./setup"
fi
else
fail "Symlink broken → $real"
fi
# GStack skills are exposed as PER-SKILL symlinks directly under skills/ (browse,
# cso, review, …) pointing into skills-external/gstack/ — there is NO single
# skills/gstack symlink (link.sh deliberately removes it: it duplicated the
# top-level gstack SKILL.md alongside the per-skill entries). The bin/ +
# browse/dist/ helper links under skills/gstack/ are checked in §7 Consistency.
# `|| true` guards pipefail if skills/ is unexpectedly absent (checked above).
gstack_skill_links=$( { find "$HOME/.claude/skills/" -maxdepth 1 -type l -lname '*skills-external/gstack/*' 2>/dev/null || true; } | wc -l | tr -d ' ')
if [ "${gstack_skill_links:-0}" -gt 0 ]; then
pass "GStack: ${gstack_skill_links} skills linked (per-skill symlinks)"
else
warn "GStack not symlinked — run: bash link.sh"
warn "GStack skills not linked — run: cd skills-external/gstack && ./setup"
fi
echo ""
@@ -136,7 +135,10 @@ fi
if command -v cargo &>/dev/null; then
pass "Cargo $(cargo --version | awk '{print $2}')"
else
warn "Cargo not found (RTK unavailable)"
# Cargo does NOT gate RTK: RTK ships as a prebuilt binary and detect_rtk finds
# it via ~/.cargo/bin or ~/.local/bin (RTK status is shown under Plugins).
# Cargo is only the Rust toolchain to BUILD RTK from source → optional, info.
info "Cargo not found (optional — only needed to build RTK from source)"
fi
if command -v python3 &>/dev/null; then
@@ -213,11 +215,20 @@ print(len(d.get('permissions',{}).get('deny',[])))
if [ "$DENY_COUNT" = "?" ]; then
warn "Could not parse deny count (python3 unavailable or JSON parse error)"
else
EXPECTED_DENY=100
if [ "$DENY_COUNT" -eq "$EXPECTED_DENY" ] 2>/dev/null; then
pass "Deny rules: $DENY_COUNT"
# Expected = deny count in the last COMMITTED settings.json. A hardcoded
# number drifts on every legit deny-list edit (false-warned for weeks at
# 100 vs 99 — LRN-047 class); deriving from HEAD auto-tracks legit edits
# and still flags live-vs-committed divergence.
EXPECTED_DENY=$(git -C "$REPO" show HEAD:settings.json 2>/dev/null | python3 -c "
import json,sys
print(len(json.load(sys.stdin).get('permissions',{}).get('deny',[])))
" 2>/dev/null || echo "?")
if [ "$EXPECTED_DENY" = "?" ]; then
warn "Could not derive expected deny count from committed settings.json"
elif [ "$DENY_COUNT" -eq "$EXPECTED_DENY" ] 2>/dev/null; then
pass "Deny rules: $DENY_COUNT (matches committed settings.json)"
else
warn "Deny rules: $DENY_COUNT (expected $EXPECTED_DENY) — settings may have been manually modified"
warn "Deny rules: $DENY_COUNT (committed: $EXPECTED_DENY) — live settings diverge from last commit"
fi
fi
else
@@ -230,8 +241,12 @@ echo ""
# 6. Token budget estimate
# ────────────────────────────────────────────────────────────
echo "── Token budget estimate ──"
# Reference: Claude Code Pro plan ~11k tokens/5h session (session budget, not context window).
# Seuils: WARNING >15%, CRITICAL >30% of session budget.
# The passive footprint (CLAUDE.md + skill descriptions + plugin session-injects)
# loads into the CONTEXT WINDOW every session — it competes with the ~200k default
# context, NOT a per-session token quota (the old "~11k/5h budget" denominator was
# a category error → false "92% CRITICAL", LRN-047). Measured ~11.4k post-audit
# 2026-07-02 (LRN-088); the chars/4 sum below is a coarse proxy of that footprint.
# Thresholds: WARNING >15% of context (~30k), CRITICAL >25% (~50k).
CLAUDE_MD_CHARS=$(wc -c < "$REPO/CLAUDE.md" 2>/dev/null || echo 0)
CLAUDE_MD_TOKENS=$((CLAUDE_MD_CHARS / 4))
@@ -255,25 +270,25 @@ if detect_context7 2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 200));
if detect_graphifyy 2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 300)); fi
TOTAL_TOKENS=$((CLAUDE_MD_TOKENS + SKILL_DESC_TOKENS + PLUGIN_TOKENS))
SESSION_BUDGET=11000
PCT=$((TOTAL_TOKENS * 100 / SESSION_BUDGET))
CONTEXT_WINDOW=200000 # Claude Code default context window (conservative; 1M is opt-in)
PCT=$((TOTAL_TOKENS * 100 / CONTEXT_WINDOW))
echo ""
echo " CLAUDE.md: ~${CLAUDE_MD_TOKENS}t"
echo " Skill descriptions: ~${SKILL_DESC_TOKENS}t (${SKILL_COUNT} skills)"
echo " Plugin passive cost: ~${PLUGIN_TOKENS}t (active plugins)"
echo " ─────────────────────────────────────────"
info " Total: ~${TOTAL_TOKENS}t"
info " Session budget (Pro): ${SESSION_BUDGET}t"
info " Usage: ~${PCT}%"
info " Total: ~${TOTAL_TOKENS}t (measured ~11.4k post-audit, LRN-088)"
info " Context window: ${CONTEXT_WINDOW}t (default; 1M opt-in)"
info " Usage: ~${PCT}% of context"
echo ""
if [ "$PCT" -gt 30 ]; then
warn "CRITICAL: ${PCT}% of session budget — /plugin-check to disable unused plugins"
if [ "$PCT" -gt 25 ]; then
warn "CRITICAL: ~${PCT}% of the ${CONTEXT_WINDOW}t context — /plugin-check to disable unused plugins"
elif [ "$PCT" -gt 15 ]; then
warn "WARNING: ${PCT}% of session budget — consider disabling unused toggle plugins"
warn "WARNING: ~${PCT}% of the ${CONTEXT_WINDOW}t context — consider disabling unused toggle plugins"
else
pass "Budget: ${PCT}% (comfortable)"
pass "Budget: ~${PCT}% of context (comfortable)"
fi
# Per-file breakdown (skill bodies — loaded on demand, shown for awareness)
@@ -310,8 +325,11 @@ else
warn "gstack/browse/dist/ symlink missing — run: bash link.sh"
fi
# Check owned skills have disable-model-invocation (skip external/symlinked skills)
MISSING_DMI=()
# BDR-019 (2026-06-09) stripped disable-model-invocation repo-wide so the
# model/orchestrators can self-route. The old check required the key on
# every owned skill — permanent false-warn since. Inverted: warn if any
# owned skill REintroduces the key (regression watch on BDR-019).
PRESENT_DMI=()
for f in "$HOME/.claude/skills/"*/SKILL.md; do
[ -f "$f" ] || continue
dir=$(dirname "$f")
@@ -319,19 +337,22 @@ for f in "$HOME/.claude/skills/"*/SKILL.md; do
[ -L "$dir" ] && continue
[ -L "$f" ] && continue
name=$(basename "$dir")
if ! grep -q "disable-model-invocation" "$f" 2>/dev/null; then
MISSING_DMI+=("$name")
if grep -q "disable-model-invocation" "$f" 2>/dev/null; then
PRESENT_DMI+=("$name")
fi
done
if [ ${#MISSING_DMI[@]} -eq 0 ]; then
pass "All owned skills have disable-model-invocation"
if [ ${#PRESENT_DMI[@]} -eq 0 ]; then
pass "No owned skill carries disable-model-invocation (BDR-019)"
else
warn "Owned skills missing disable-model-invocation: ${MISSING_DMI[*]}"
warn "Owned skills reintroduce disable-model-invocation (BDR-019 regression): ${PRESENT_DMI[*]}"
fi
# Check expected skills are present
# Check expected skills are present. Repo-owned skills only: gstack skills
# (health, status, …) are OFF by default and toggled per profile — requiring
# them here false-warns on a default install, and "run link.sh" cannot
# restore them (they are profile-managed, not link.sh-managed).
EXPECTED_SKILLS=(
"analyze" "doc" "health" "init-project" "onboard" "plugin-check"
"analyze" "doc" "init-project" "onboard" "plugin-check"
"refactor" "ship-feature" "status"
)
MISSING_SKILLS=()
@@ -341,7 +362,7 @@ for skill in "${EXPECTED_SKILLS[@]}"; do
fi
done
if [ ${#MISSING_SKILLS[@]} -eq 0 ]; then
pass "All ${#EXPECTED_SKILLS[@]} expected skills present (analyze, doc, health, init-project, onboard, plugin-check, refactor, ship-feature, status)"
pass "All ${#EXPECTED_SKILLS[@]} expected skills present (${EXPECTED_SKILLS[*]})"
else
warn "Missing skills: ${MISSING_SKILLS[*]} — run: bash link.sh"
fi
+1 -1
View File
@@ -1 +1 @@
ef0d630994fd7ef5f2b84fb66cd6249c493bb8736bcacd4734d7c798125018fb rtk-rewrite.sh
871efa28daf7c06a9c9039a2875407e2536646f5d82f7e7a9c6a80dd3742929c rtk-rewrite.sh
+7 -4
View File
@@ -24,10 +24,13 @@ prompt="$(printf '%s' "$input" \
lc="$(printf '%s' "$prompt" | tr '[:upper:]' '[:lower:]')"
# UI/design build and review signals (FR + EN). Word boundaries (\b) avoid
# substring false matches like perform/platform/information. Some broad tokens
# (page, color, screen, card, menu) are kept deliberately for coverage — they
# over-fire on non-UI prompts, which is harmless: the reminder self-cancels.
pattern='design|redesign|refonte|refont|ui/ux|ux/ui|\bui\b|\bux\b|ui kit|design system|design-system|interface|frontend|front-end|front end|composant|component|\bnavbar\b|\bsidebar\b|\bmodal\b|\bbouton\b|\bbutton\b|\bcard\b|\bcarte\b|\bform\b|formulaire|\bhero\b|\bheader\b|\bfooter\b|\bmenu\b|dropdown|tooltip|\bbadge\b|\bchart\b|graphique|accordion|carousel|\bslider\b|landing|dashboard|homepage|home page|\baccueil\b|\bpage\b|\bpages\b|\bécran\b|\becran\b|\bscreen\b|portfolio|maquette|mockup|wireframe|prototype|\blook\b|\bjoli\b|\bjolie\b|\bbeau\b|\bbelle\b|esth[eé]tique|aesthetic|\bvisuel\b|\bvisual\b|embellir|fignol|peaufin|polish|styliser|\bstyle\b|styling|stylesheet|\bskin\b|charte graphique|\bbrand\b|branding|\blogo\b|favicon|ic[oô]ne|\bicon\b|\bcss\b|tailwind|shadcn|couleur|\bcolor\b|palette|gradient|d[eé]grad[eé]|\bshadow\b|\bombre\b|spacing|espacement|\bmarge\b|\bpadding\b|\bmargin\b|\bradius\b|arrondi|\bhover\b|dark mode|light mode|\btheme\b|th[eè]me|typograph|\bfont\b|\bfonts\b|font pairing|\bpolice\b|animation|\bmotion\b|transition|micro-interaction|keyframe|glassmorph|neumorph|claymorph|skeuomorph|brutalis|bento|minimalis|responsive|figma'
# substring false matches like perform/platform/information. Tightened
# 2026-07-02: ultra-generic English tokens (page, form, menu, card, style,
# look, screen, interface, color) fired on a large share of NON-UI prompts —
# ~200 tokens of reminder each time (measured: 6 fires during a pure config
# audit). Kept: unambiguous design vocabulary + FR aesthetic words; specific
# compounds (stylesheet, styling, formulaire, écran) still match.
pattern='design|redesign|refonte|refont|ui/ux|ux/ui|\bui\b|\bux\b|ui kit|design system|design-system|frontend|front-end|front end|composant|component|\bnavbar\b|\bsidebar\b|\bmodal\b|\bbouton\b|\bbutton\b|formulaire|\bhero\b|\bheader\b|\bfooter\b|dropdown|tooltip|\bbadge\b|\bchart\b|graphique|accordion|carousel|\bslider\b|landing|dashboard|homepage|home page|\baccueil\b|\bécran\b|\becran\b|portfolio|maquette|mockup|wireframe|prototype|\bjoli\b|\bjolie\b|\bbeau\b|\bbelle\b|esth[eé]tique|aesthetic|\bvisuel\b|\bvisual\b|embellir|fignol|peaufin|polish|styliser|styling|stylesheet|\bskin\b|charte graphique|\bbrand\b|branding|\blogo\b|favicon|ic[oô]ne|\bicon\b|\bcss\b|tailwind|shadcn|couleur|palette|gradient|d[eé]grad[eé]|\bombre\b|spacing|espacement|\bmarge\b|\bpadding\b|\bmargin\b|\bradius\b|arrondi|\bhover\b|dark mode|light mode|\btheme\b|th[eè]me|typograph|\bfont\b|\bfonts\b|font pairing|\bpolice\b|animation|\bmotion\b|transition|micro-interaction|keyframe|glassmorph|neumorph|claymorph|skeuomorph|brutalis|bento|minimalis|responsive|figma'
if printf '%s' "$lc" | grep -Eq "$pattern"; then
cat <<'EOF'
+55 -29
View File
@@ -7,8 +7,17 @@
# which is the single source of truth (src/discover/registry.rs).
# To add or change rewrite rules, edit the Rust registry — not this file.
#
# INTEGRITY PIN: the rtk binary verifies this file against
# hooks/.rtk-hook.sha256 at execution time and refuses to run on mismatch.
# ANY edit here must re-pin: (cd hooks && sha256sum rtk-rewrite.sh > .rtk-hook.sha256)
#
# Exit code protocol for `rtk rewrite`:
# 0 + stdout Rewrite found, no deny/ask rule matched → auto-allow
# 0 + stdout Rewrite found, no rtk deny/ask rule matched → rewrite. NO
# permissionDecision is emitted (auto-allow dropped 2026-07-02:
# it made rtk's registry a parallel permission authority that
# bypassed settings.json deny/ask). The REWRITTEN command goes
# through native evaluation; explicit `rtk <tool>` allow rules
# in settings.json keep read-only forms frictionless.
# 1 No RTK equivalent → pass through unchanged
# 2 Deny rule matched → pass through (Claude Code native deny handles it)
# 3 + stdout Ask rule matched → rewrite but let Claude Code prompt the user
@@ -18,14 +27,27 @@ if ! command -v jq &>/dev/null; then
exit 0
fi
if ! command -v rtk &>/dev/null; then
# PATH heal: hook/tool-shell PATH may lack the cargo bin dir (hand-managed
# ~/.bashrc can lose the cargo line — LRN-036 class). Resolve the ABSOLUTE
# binary path: the rewritten command executes in the tool shell, whose PATH
# the hook cannot fix — a bare `rtk …` rewrite would exit 127 there.
RTK_BIN="$(command -v rtk 2>/dev/null || true)"
RTK_ON_PATH=1
if [ -z "$RTK_BIN" ]; then
RTK_ON_PATH=0
for _d in "$HOME/.cargo/bin" "$HOME/.local/bin"; do
if [ -x "$_d/rtk" ]; then RTK_BIN="$_d/rtk"; break; fi
done
fi
if [ -z "$RTK_BIN" ]; then
echo "[rtk] WARNING: rtk is not installed or not in PATH. Hook cannot rewrite commands. Install: https://github.com/rtk-ai/rtk#installation" >&2
exit 0
fi
# Version guard: rtk rewrite was added in 0.23.0.
# Older binaries: warn once and exit cleanly (no silent failure).
RTK_VERSION=$(rtk --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
RTK_VERSION=$("$RTK_BIN" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
if [ -n "$RTK_VERSION" ]; then
MAJOR=$(echo "$RTK_VERSION" | cut -d. -f1)
MINOR=$(echo "$RTK_VERSION" | cut -d. -f2)
@@ -44,13 +66,13 @@ if [ -z "$CMD" ]; then
fi
# Delegate all rewrite + permission logic to the Rust binary.
REWRITTEN=$(rtk rewrite "$CMD" 2>/dev/null)
REWRITTEN=$("$RTK_BIN" rewrite "$CMD" 2>/dev/null)
EXIT_CODE=$?
case $EXIT_CODE in
0)
# Rewrite found, no permission rules matched — safe to auto-allow.
# If the output is identical, the command was already using RTK.
# Rewrite found. If the output is identical, the command was
# already using RTK — nothing to do.
[ "$CMD" = "$REWRITTEN" ] && exit 0
;;
1)
@@ -70,29 +92,33 @@ case $EXIT_CODE in
;;
esac
# When rtk is NOT on PATH, a bare `rtk …` rewrite exits 127 in the tool
# shell (whose PATH the hook cannot fix). Substitute the absolute path at
# the string head — the only position safe to rewrite. Compound commands
# (`a && b`) can carry further bare rtk segments we canNOT substitute
# safely (quoted text, e.g. commit messages, may contain the same
# pattern): if any remain at a command position, pass through unrewritten
# — lose the compression, never emit a command that 127s.
if [ "$RTK_ON_PATH" -eq 0 ]; then
case "$REWRITTEN" in
rtk\ *) REWRITTEN="$RTK_BIN ${REWRITTEN#rtk }" ;;
esac
if printf '%s' "$REWRITTEN" | grep -Eq '(^|[;&|][[:space:]]*)rtk[[:space:]]'; then
exit 0
fi
fi
ORIGINAL_INPUT=$(echo "$INPUT" | jq -c '.tool_input')
UPDATED_INPUT=$(echo "$ORIGINAL_INPUT" | jq --arg cmd "$REWRITTEN" '.command = $cmd')
if [ "$EXIT_CODE" -eq 3 ]; then
# Ask: rewrite the command, omit permissionDecision so Claude Code prompts.
jq -n \
--argjson updated "$UPDATED_INPUT" \
'{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"updatedInput": $updated
}
}'
else
# Allow: rewrite the command and auto-allow.
jq -n \
--argjson updated "$UPDATED_INPUT" \
'{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "RTK auto-rewrite",
"updatedInput": $updated
}
}'
fi
# Rewrite WITHOUT a permissionDecision (exit 0 and exit 3 alike): the
# rewritten command goes through Claude Code's native allow/deny/ask
# evaluation. Permission control lives in settings.json, not in rtk.
jq -n \
--argjson updated "$UPDATED_INPUT" \
'{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"updatedInput": $updated
}
}'
+31 -12
View File
@@ -49,10 +49,12 @@ TOGGLE_ACTIVE=()
TOGGLE_INACTIVE=()
for plugin in gstack uiux_pro_max plugin_dev context7 graphifyy; do
# Map function name to display name
# Map function name to display name. graphifyy = the pipx PACKAGE name
# (pypi:graphifyy); the CLI and skill are 'graphify' — display that.
case "$plugin" in
uiux_pro_max) display="ui-ux-pro-max" ;;
plugin_dev) display="plugin-dev" ;;
graphifyy) display="graphify" ;;
*) display="$plugin" ;;
esac
@@ -105,7 +107,7 @@ declare -A _plugin_costs=(
[ui-ux-pro-max]=400
[plugin-dev]=100
[context7]=200
[graphifyy]=300
[graphify]=300
)
for _p in "${TOGGLE_ACTIVE[@]}"; do
_cost="${_plugin_costs[$_p]:-0}"
@@ -129,20 +131,37 @@ echo "┌─ Claude Code config ────────────────
# the user sees the real picture instead of a misleading literal.
ALWAYS_ON=()
detect_rtk &>/dev/null && ALWAYS_ON+=("rtk")
plugin_enabled "security-guidance@claude-code-plugins" && ALWAYS_ON+=("security-guidance")
plugin_enabled "superpowers@superpowers-marketplace" && ALWAYS_ON+=("superpowers")
# Derive the plugin list from settings.json:enabledPlugins (true entries)
# instead of a hardcoded name pair — a hardcoded SET under-reports newly
# enabled plugins (pr-review-toolkit was enabled yet invisible). LRN-005
# class. Plugins owned by the toggle row below are excluded (dual display).
_toggle_owned=" gstack ui-ux-pro-max plugin-dev context7 graphify "
while IFS= read -r _pl; do
case "$_toggle_owned" in
*" $_pl "*) : ;;
*) ALWAYS_ON+=("$_pl") ;;
esac
done < <(grep -oE '"[A-Za-z0-9_-]+@[A-Za-z0-9_-]+"[[:space:]]*:[[:space:]]*true' "$HOME/.claude/settings.json" 2>/dev/null \
| sed -E 's/^"([^@]+)@.*$/\1/')
unset _toggle_owned _pl
ALWAYS_ON_STR="${ALWAYS_ON[*]:-none}"
# Same 40-char-width split policy as the toggle row below — keeps the
# right border aligned when 4 always-on plugins overflow the field.
# right border aligned on overflow. Greedy width-fill (not a fixed 3-name
# cut: 3 long names overflowed line 1 and left line 2 empty).
if [ "${#ALWAYS_ON_STR}" -le 40 ]; then
printf "│ ✅ ON : %-40s│\n" "$ALWAYS_ON_STR"
else
_ao_line1="${ALWAYS_ON[0]} ${ALWAYS_ON[1]} ${ALWAYS_ON[2]:-}"
_ao_rest=("${ALWAYS_ON[@]:3}")
_ao_line2="${_ao_rest[*]}"
printf "│ ✅ ON : %-40s│\n" "$_ao_line1"
printf "│ %-40s│\n" "$_ao_line2"
unset _ao_line1 _ao_line2 _ao_rest
_ao_l1=""; _ao_l2=""
for _ao_e in "${ALWAYS_ON[@]}"; do
if [ -z "$_ao_l2" ] && [ $(( ${#_ao_l1} + ${#_ao_e} + 1 )) -le 40 ]; then
_ao_l1="${_ao_l1:+$_ao_l1 }$_ao_e"
else
_ao_l2="${_ao_l2:+$_ao_l2 }$_ao_e"
fi
done
printf "│ ✅ ON : %-40s│\n" "$_ao_l1"
printf "│ %-40s│\n" "$_ao_l2"
unset _ao_l1 _ao_l2 _ao_e
fi
unset ALWAYS_ON ALWAYS_ON_STR
# Plugin display — all plugins shown, split across 2 lines if >4
@@ -182,7 +201,7 @@ printf "│ 📦 v%-45s│\n" "$CONFIG_VERSION"
# Version check: compare local vs remote (non-blocking)
_remote_ver=""
if [ -n "$REPO_DIR" ] && [ -d "$REPO_DIR/.git" ]; then
_remote_ver=$(cd "$REPO_DIR" 2>/dev/null && git fetch origin --quiet 2>/dev/null && git show origin/master:version.txt 2>/dev/null) || _remote_ver=""
_remote_ver=$(cd "$REPO_DIR" 2>/dev/null && git fetch origin --quiet 2>/dev/null && git show origin/main:version.txt 2>/dev/null) || _remote_ver=""
fi
if [ -n "$_remote_ver" ] && [ "$_remote_ver" != "$CONFIG_VERSION" ]; then
printf "│ 🔄 update available: v%-27s│\n" "$_remote_ver"
+65 -16
View File
@@ -363,9 +363,10 @@ if [ -d "$GSTACK_DIR" ]; then
gstack_bump_playwright_if_unsupported
info "Running GStack setup..."
_gstack_setup_ok=0
if [ -x "$GSTACK_DIR/setup" ]; then
if (cd "$GSTACK_DIR" && ./setup); then
: # setup succeeded
_gstack_setup_ok=1
else
warn "GStack ./setup failed — check output above"
fi
@@ -381,9 +382,13 @@ if [ -d "$GSTACK_DIR" ]; then
&& [ "$(bash "$REPO/lib/toggle-external.sh" status gstack 2>/dev/null)" = "enabled" ]; then
info "Disabling gstack by default (no context cost until enabled)..."
bash "$REPO/lib/toggle-external.sh" disable gstack >/dev/null
ok "gstack installed, disabled — enable with: bash lib/toggle-external.sh enable gstack"
fi
# Success message gated on the real setup outcome — an unconditional ok
# after a `|| warn` reads as success even when setup failed (LRN-071 class).
if [ "$_gstack_setup_ok" -eq 1 ]; then
ok "GStack ready (disabled by default — enable: bash lib/toggle-external.sh enable gstack)"
else
ok "GStack ready (submodule initialized, symlinks staged)"
warn "GStack NOT ready — ./setup did not complete (see warnings above)"
fi
# GStack shared infrastructure: bin/ (CLI tools) and browse/dist/ (compiled binary).
@@ -526,7 +531,9 @@ enable_plugin "security-guidance" "claude-code-plugins"
# (not in claude-code marketplace — it's a separate repo)
install_plugin "example-skills" "anthropic-agent-skills"
install_plugin "pr-review-toolkit" "claude-code-plugins"
install_plugin "plugin-dev" "claude-code-plugins"
# plugin-dev dropped 2026-07-02 (audit #14): installed 2026-06-23, never
# enabled, pure disk weight — reinstall deliberately if plugin authoring
# becomes a need: claude plugin install plugin-dev@claude-code-plugins
echo ""
@@ -573,11 +580,47 @@ else
err "ctx7 install failed — run manually: npm install -g ctx7"
fi
fi
# Suggest setup for Claude Code integration (optional — ctx7 also works standalone)
# ctx7 auth — detect, then offer login ONLY in an interactive TTY. A non-interactive
# run (CI / headless / re-run) must never open a browser or block on OAuth.
if command -v ctx7 &>/dev/null; then
info "Run 'ctx7 setup --claude' to configure Context7 for Claude Code"
info "Or use ctx7 standalone: ctx7 docs /vercel/next.js \"middleware\""
info "Free higher rate limits: ctx7 login (OAuth) or --api-key from context7.com/dashboard"
# Deterministic offline oracle: ctx7's OAuth token lives here (XDG-aware).
# Present => authenticated; absent => anonymous. No subprocess, no network, no browser.
ctx7_creds="${XDG_CONFIG_HOME:-$HOME/.config}/context7/credentials.json"
if [ -f "$ctx7_creds" ]; then
ok "ctx7 authenticated (full rate limits)"
else
info "ctx7 works anonymously — docs + library already usable, no auth required."
if [ -t 0 ] && [ -t 1 ]; then
# Interactive terminal: offer to log in now (opens a browser).
printf '%b' "${BLUE}→${NC} Authenticate ctx7 now for higher rate limits? [y/N] "
read -r ctx7_ans || ctx7_ans=""
if [[ "$ctx7_ans" =~ ^[Yy]([Ee][Ss])?$ ]]; then
if ctx7 login; then
ok "ctx7 authenticated (full rate limits)"
else
warn "ctx7 login did not finish — re-run 'ctx7 login' anytime"
fi
else
info "Skipped — authenticate later with: ctx7 login"
fi
else
# Non-interactive (CI / headless / re-run): never block — just guide.
info "For higher rate limits, authenticate: ctx7 login (opens a browser)"
info " headless: ctx7 login --no-browser (prints a URL to open yourself)"
fi
fi
# CLI + Skills mode: install the find-docs skill into ~/.claude/skills when
# absent (it is gitignored — ctx7 owns it, this regenerates it on a fresh
# clone). Guarded on absence so a re-run never clobbers a customized config
# (setup also (re)writes ~/.claude/rules/context7.md).
if [ ! -f "$HOME/.claude/skills/find-docs/SKILL.md" ]; then
if ctx7 setup --claude --cli -y </dev/null &>/dev/null; then
ok "ctx7 CLI + Skills configured (find-docs skill installed)"
else
warn "ctx7 setup failed — run manually: ctx7 setup --claude --cli"
fi
fi
info "Standalone usage: ctx7 docs /vercel/next.js \"middleware\""
fi
# ============================================================
@@ -596,11 +639,18 @@ else
fi
fi
if command -v graphify &>/dev/null; then
_graphify_ok=1
info "Running graphify install (dependencies)..."
graphify install 2>/dev/null || warn "graphify install failed — run manually"
graphify install 2>/dev/null || { warn "graphify install failed — run manually"; _graphify_ok=0; }
info "Configuring Claude Code integration..."
graphify claude install 2>/dev/null || warn "graphify claude install failed — run manually"
ok "Graphifyy configured for Claude Code"
graphify claude install 2>/dev/null || { warn "graphify claude install failed — run manually"; _graphify_ok=0; }
# Success message gated on the real outcome (LRN-071 class: an
# unconditional ok after `|| warn` lies when a step failed).
if [ "$_graphify_ok" -eq 1 ]; then
ok "Graphify configured for Claude Code"
else
warn "Graphify NOT fully configured — re-run the failed step manually"
fi
fi
echo ""
@@ -861,14 +911,13 @@ echo " ✅ security-guidance — PreToolUse security hook (0 tokens) [claud
echo " ✅ rtk — token compression hook (0 tokens)"
echo " ✅ superpowers — brainstorm/plan/implement/debug workflow"
echo ""
echo " TOGGLE (installed but start OFF — /plugin-check recommends when needed):"
echo " TOGGLE (plugin state = settings.json enabledPlugins; skills/CLIs = profiles):"
echo " 🔄 gstack — disabled by default (toggle: lib/toggle-external.sh enable gstack)"
echo " 🔄 gsd v2 — standalone CLI 'gsd' (gsd-pi, not a Claude Code plugin)"
echo " 🔄 plugin-dev — create plugins/skills (~100 tokens) [claude-code-plugins]"
echo " 🔄 pr-review-toolkit — /pr-review-toolkit:review-pr (~300 tokens) [claude-code-plugins]"
echo " 🔄 ui-ux-pro-max — user scope (~400 tokens)"
echo " 🔄 pr-review-toolkit — /review-pr + 6 PR agents (~2.2k tokens when enabled) [claude-code-plugins]"
echo " 🔄 ui-ux-pro-max — user scope (~780 tokens when enabled)"
echo " 🔄 context7 CLI — ctx7 (npm global, standalone or MCP setup)"
echo " 🔄 graphifyy — codebase knowledge graph (pipx, PreToolUse hook)"
echo " 🔄 graphifyy (CLI: graphify) — codebase knowledge graph (pipx, PreToolUse hook)"
echo " 🔄 emil-design-eng — UI polish, animations, component craft (curl → symlink)"
echo " 🔄 frontend-design — distinctive frontend interfaces, anti-AI-slop (anthropic-agent-skills)"
echo " 🔄 design-motion-principles — motion/animation design, 3-designer lens (kylezantos)"
+3 -1
View File
@@ -10,7 +10,9 @@
# --- Always-on plugins ---
detect_rtk() {
command -v rtk &>/dev/null
command -v rtk &>/dev/null && return 0
# PATH heal: hook/session PATH may lack the cargo bin dir (LRN-036 class)
[ -x "$HOME/.cargo/bin/rtk" ] || [ -x "$HOME/.local/bin/rtk" ]
}
detect_superpowers() {
+16
View File
@@ -156,6 +156,22 @@ if bash "$HERE/gitflow.sh" protected-base main; then ok "cli protected-bas
if bash "$HERE/gitflow.sh" protected-base feature/x; then no "cli protected-base feature (rc0?)"; else ok "cli protected-base feature → rc1"; fi
chk "cli base-for hotfix=main" '[ "$(bash "$HERE/gitflow.sh" base-for hotfix)" = main ]'
echo "T12 — finish arg-guard (named branch must equal current, else refuse)"
newrepo finargs; echo a>a; hookon; gitflow_init >/dev/null 2>&1
gitflow_start feature standon >/dev/null 2>&1; echo w>w.txt; git add w.txt; git commit -q -m w
# mismatch: standing on feature/standon but asking to finish bugfix/other → refuse
# shellcheck disable=SC2034 # mism_out/mism_rc are used in the deferred chk eval strings
mism_out="$(gitflow_finish bugfix other 2>&1)"; mism_rc=$?
chk "arg-mismatch → nonzero rc" "[ $mism_rc -ne 0 ]"
chk "arg-mismatch → HEAD untouched" '[ "$(git symbolic-ref --short HEAD)" = feature/standon ]'
chk "arg-mismatch → branch kept" 'git rev-parse --verify -q refs/heads/feature/standon >/dev/null'
chk "arg-mismatch → develop NOT merged" '! git log develop --oneline | grep -q "Merge feature/standon into develop"'
chk "arg-mismatch → message names both" 'printf "%s" "$mism_out" | grep -q "current branch" && printf "%s" "$mism_out" | grep -q "bugfix/other"'
# match: naming the current branch explicitly finishes exactly like the no-arg path
gitflow_finish feature standon >/dev/null 2>&1
chk "arg-match → merged into develop" 'git log develop --oneline | grep -q "Merge feature/standon into develop"'
chk "arg-match → branch deleted" '! git rev-parse --verify -q refs/heads/feature/standon >/dev/null'
echo
echo "==== RESULT: $PASS passed, $FAIL failed ===="
[ "$FAIL" -eq 0 ]
+16 -3
View File
@@ -97,11 +97,24 @@ _gitflow_delete() { # <branch>
git branch -q -d "$br" || { echo "gitflow: '$br' not fully merged — branch kept" >&2; return 5; }
}
# gitflow_finish → directed merge of the CURRENT branch per its type, then delete.
# WHEN to call this is the human gate (SKILL.md). This only performs the merge.
# gitflow_finish [<type> <name>] → directed merge of the CURRENT branch per its
# type, then delete. WHEN to call this is the human gate (SKILL.md).
#
# The merge source is ALWAYS the checked-out branch (HEAD) — that is the contract.
# The optional <type> <name> is a SAFETY ASSERTION, not a target selector: if you
# name a branch it MUST equal the current one, else finish refuses loudly instead
# of silently merging whatever you happen to be standing on. (Guards the audit UX
# trap: `finish bugfix audit-bugs` run from feature/audit-tokens merged the wrong
# branch — args were silently ignored. See BLK-015 / LRN-089.) No args = unchanged.
gitflow_finish() {
local br type
local br type req_type="${1:-}" req_name="${2:-}"
br="$(git symbolic-ref --short -q HEAD)" || { echo "gitflow_finish: detached HEAD" >&2; return 3; }
if [ -n "$req_type" ] || [ -n "$req_name" ]; then
[ "$req_type/$req_name" = "$br" ] || {
echo "gitflow_finish: operates on the current branch '$br', but you asked '$req_type/$req_name' — checkout '$req_type/$req_name' first (or run finish with no args)." >&2
return 2
}
fi
type="$(gitflow_branch_type "$br")"
case "$type" in
feature|bugfix|chore)
+3 -2
View File
@@ -34,8 +34,9 @@ guard
learn
retro
# Plugin: PR review toolkit (pre-merge audit)
pr-review-toolkit plugin@claude-code-plugins
# pr-review-toolkit removed (audit 2026-07-02 #12 — ~2.2k tokens, PR-only):
# enable per PR session via `bash lib/profile.sh apply audit` or
# claude plugin enable pr-review-toolkit@claude-code-plugins
# CLIs (advisory)
ctx7 cli
+6 -1
View File
@@ -79,7 +79,12 @@ emil-design-eng external
frontend-design external
design-motion-principles external
ui-ux-pro-max plugin@ui-ux-pro-max-skill
pr-review-toolkit plugin@claude-code-plugins
# pr-review-toolkit REMOVED from full (audit 2026-07-02 #12): heaviest
# single plugin cost (~2.2k tokens of agent descriptions/session), useful
# only when reviewing PRs. Reactivate per PR session:
# claude plugin enable pr-review-toolkit@claude-code-plugins
# or profile-based: bash lib/profile.sh apply audit (audit.profile keeps it;
# a later `set full` re-disables it — MANAGED_PLUGINS lifecycle).
magic mcp
# === CLIs (advisory) =================================================
+4 -1
View File
@@ -65,7 +65,10 @@ if has "$cand" "--help"; then ok "T5 surfaced --help candidate (BDR-001 ⇄ --he
echo; echo "=== T6 live oracle smoke — oracles QUERY real git/fs (not a name) ==="
if reconcile_oracle_merge_done "$REPO" "prune-memory"; then ok "T6a merge_done(prune-memory) via git log"; else no "T6a merge not found in git"; fi
if reconcile_oracle_sha_exists "$REPO" "be1dcef"; then ok "T6b sha_exists(be1dcef) via cat-file"; else no "T6b sha missing"; fi
dk="$MEM/../skills/darwin-skill"
# $REPO here = lib/ (see line 12) → lib/../skills = the real skills/ dir.
# Was "$MEM/../skills" = .claude/skills/ — the LRN-042 parasite dir, removed
# 2026-06-30 by make plugin Step 8.5: green-for-wrong-reason (LRN-077 class).
dk="$REPO/../skills/darwin-skill"
if reconcile_oracle_path_present "$dk"; then ok "T6c path_present(darwin-skill) via fs"; else no "T6c path absent"; fi
echo; echo "================ $pass GREEN / $fail RED ================"
+1 -1
View File
@@ -8,7 +8,7 @@
"gsd": {
"source": "npm:gsd-pi",
"version": "2.64.0",
"note": "Check latest at https://www.npmjs.com/package/gsd-pi before updating. GSD v2 is a standalone CLI (Pi SDK), not a Claude Code plugin. Run 'gsd' in terminal, not '/gsd' in Claude Code."
"note": "Check latest at https://www.npmjs.com/package/gsd-pi before updating. GSD v2 is a standalone CLI (Pi SDK), not a Claude Code plugin. Run 'gsd' in terminal, not '/gsd' in Claude Code. NOTE: update-all.sh honors this pin — 'make update' will NOT advance gsd past it; bump this version deliberately, then re-run."
},
"gstack": {
"source": "https://github.com/garrytan/gstack.git",
+43 -4
View File
@@ -46,6 +46,32 @@
"Bash(tr *)",
"Bash(cut *)",
"Bash(diff *)",
"Bash(rtk grep *)",
"Bash(*/rtk grep *)",
"Bash(rtk ls)",
"Bash(rtk ls *)",
"Bash(*/rtk ls)",
"Bash(*/rtk ls *)",
"Bash(rtk cat *)",
"Bash(*/rtk cat *)",
"Bash(rtk head *)",
"Bash(*/rtk head *)",
"Bash(rtk tail *)",
"Bash(*/rtk tail *)",
"Bash(rtk wc *)",
"Bash(*/rtk wc *)",
"Bash(rtk diff *)",
"Bash(*/rtk diff *)",
"Bash(rtk git status)",
"Bash(*/rtk git status)",
"Bash(rtk git log*)",
"Bash(*/rtk git log*)",
"Bash(rtk git diff*)",
"Bash(*/rtk git diff*)",
"Bash(rtk git show*)",
"Bash(*/rtk git show*)",
"Bash(rtk git branch*)",
"Bash(*/rtk git branch*)",
"Read(**/*.md)",
"Read(**/*.txt)",
"Read(**/*.json)",
@@ -63,9 +89,13 @@
"deny": [
"Bash(rm -rf *)",
"Bash(rm -rf /*)",
"Bash(rm -r *)",
"Bash(rm -fr *)",
"Bash(rmdir *)",
"Bash(git push --force*)",
"Bash(git push --force)",
"Bash(git push --force *)",
"Bash(git push -f*)",
"Bash(git push * +*)",
"Bash(git reset --hard*)",
"Bash(git clean -fd*)",
"Bash(sudo rm*)",
@@ -156,10 +186,20 @@
"Bash(source /dev/stdin)",
"Bash(mkfifo *)",
"Bash(node -e *)",
"Bash(python3 -c *)",
"Bash(python -c *)",
"Bash(xargs * .env*)",
"Bash(tar * .env*)",
"Bash(zip * .env*)",
"Bash(base64 .env*)"
"Bash(base64 .env*)",
"Bash(rtk cat *.env*)",
"Bash(*/rtk cat *.env*)",
"Bash(rtk grep * .env*)",
"Bash(*/rtk grep * .env*)",
"Bash(rtk head *.env*)",
"Bash(*/rtk head *.env*)",
"Bash(rtk tail *.env*)",
"Bash(*/rtk tail *.env*)"
],
"ask": [
"Bash(git push *)",
@@ -177,7 +217,6 @@
"WebFetch",
"Bash(xargs *)",
"Bash(sed *)",
"Bash(python3 -c *)",
"Bash(git stash pop*)",
"Bash(git stash drop*)",
"Bash(git stash clear)"
@@ -230,7 +269,7 @@
"ui-ux-pro-max@ui-ux-pro-max-skill": true,
"security-guidance@claude-code-plugins": true,
"superpowers@superpowers-marketplace": true,
"pr-review-toolkit@claude-code-plugins": true
"pr-review-toolkit@claude-code-plugins": false
},
"extraKnownMarketplaces": {
"claude-code-plugins": {
+7 -10
View File
@@ -1,16 +1,13 @@
---
name: audit-delta
description: |
Use when the user wants a recurring code audit scoped to everything that
changed since the previous audit run (full codebase on first run), on one
or more selectable axes: CLAUDE.md norm conformity, bugs/improvements,
dead code, security. NOT for one obvious bug (/hotfix, /bugfix), one-shot
full cleanup (/code-clean), full security posture (/cso), quality
dashboard (/health), or branch/PR diff review (/review, /code-review).
Triggers: "audit-delta", "audit since last run", "incremental audit",
"audit incrémental", "audit les changements", "audit ce qui a changé
depuis la dernière fois", "periodic audit", "audit périodique",
"re-run the audit", "relance l'audit", "audit conformité + sécurité".
Use when the user wants a recurring code audit scoped to changes since
the previous run (full codebase on first run), on selectable axes:
CLAUDE.md conformity, bugs, dead code, security. NOT one obvious bug
(/hotfix, /bugfix), one-shot cleanup (/code-clean), security posture
(/cso), dashboard (/health), branch diff (/review).
Triggers: "audit-delta", "incremental audit", "audit incrémental",
"audit ce qui a changé", "periodic audit", "relance l'audit".
argument-hint: "[axes among: conformity errors deadcode security — blank = asked]"
allowed-tools:
- Read
+8 -12
View File
@@ -1,18 +1,14 @@
---
name: capitalize
description: |
Use when about to /clear or /compact, or when closing a session, and the
conversation holds decisions, learnings, blockers, eval results, or
finished/new TODO items not yet written to `.claude/memory/` or
`.claude/tasks/TODO.md`. Plain invocation = pre-wipe flush; `--ritual` (or the
word "close"/"ritual" in the request) = end-of-session reflection mode. NOT
registry curation (that is /prune-memory).
Triggers: "capitalize", "capitalise", "before clear", "before compact",
"save before clear", "flush memory", "don't lose this", "what's not logged
yet", "avant de clear", "avant compact", "sauvegarde avant clear",
"capitalise ce qui manque", "close", "end session", "session close",
"ferme la session", "checkpoint memory", "what did we learn", "retro rapide",
"fin de journée".
Use when about to /clear or /compact, or closing a session, with
decisions, learnings, blockers, evals, or TODO changes not yet written
to .claude/memory/ or .claude/tasks/TODO.md. Plain = pre-wipe flush;
--ritual (or "close") = end-of-session reflection. NOT registry
curation (that is /prune-memory).
Triggers: "capitalize", "before clear/compact", "flush memory", "don't
lose this", "avant de clear/compact", "capitalise ce qui manque",
"close", "fin de journée", "checkpoint memory".
argument-hint: "[--ritual] (scans conversation + git + TODO against .claude/memory/; --ritual adds the 3-question reflection)"
allowed-tools:
- Read
+6 -8
View File
@@ -1,14 +1,12 @@
---
name: client-handover
description: |
Use when finalizing a project for non-technical client delivery — needs
final audits, deploy validation against live site, and a branded
deliverable (Markdown + HTML + PDF). Multi-agent orchestrator: dispatches
client-handover-writer which spawns parallel /seo + /harden subagents,
then /web-validate, then writes the deliverable.
Triggers: "client handover", "compte rendu client", "livraison client",
"rapport client", "deliverable", "summary for client", "handover doc",
"livrable", "ship and handover", "finaliser et livrer".
Use when finalizing a project for non-technical client delivery —
final audits, live-site validation, branded deliverable (MD + HTML +
PDF). Orchestrator: client-handover-writer spawns /seo + /harden in
parallel, then /web-validate, then writes the deliverable.
Triggers: "client handover", "livraison client", "rapport client",
"deliverable", "livrable", "finaliser et livrer".
argument-hint: [optional: language fr|en, --include-deploy, --skip-deploy, --skip-seo, --skip-audits, --skip-fix-loop, --max-iterations N, --audit-max-age <duration>, --output <path>]
allowed-tools:
- Read
+7 -7
View File
@@ -1,13 +1,13 @@
---
name: code-clean
description: |
Full codebase cleanup: dead code removal, style/norm enforcement, structural
issues. Two-phase workflow: audit first (read-only report), then execute
approved fixes only. Delegates refactoring to the refactorer agent.
Trigger: "code-clean", "clean up the code", "remove dead code",
"enforce code style", "cleanup", "nettoyage du code", "code hygiene".
For targeted refactoring without audit → use /refactor instead.
For bug fixes discovered during cleanup → logged to .claude/audits/BUGS-FOUND.md, not fixed here.
Full codebase cleanup: dead code, style/norm enforcement, structural
issues. Two-phase: read-only audit, then approved fixes only
(refactorer agent).
Triggers: "code-clean", "remove dead code", "cleanup", "nettoyage du
code", "code hygiene".
Targeted refactor without audit → /refactor. Bugs found → logged to
.claude/audits/BUGS-FOUND.md, not fixed here.
argument-hint: <file, directory, or blank for entire project>
allowed-tools:
- Read
+6 -7
View File
@@ -2,13 +2,12 @@
name: commit-change
version: 1.0.0
description: |
Analyze all changes since the last commit (staged, unstaged, untracked files)
and create well-structured commits grouped by logical unit. Use this skill
whenever the user says "commit my changes", "smart commit", "auto commit",
"commit everything", "analyse et commit", or any variation of wanting to
commit their pending work intelligently. Also trigger when the user has
been working on multiple things and wants to create clean, atomic commits
from their messy working directory. Works in any git repository.
Analyze all pending changes (staged, unstaged, untracked) and create
atomic commits grouped by logical unit, retracing the work. Any git
repository.
Triggers: "commit my changes", "smart commit", "auto commit", "commit
everything", "analyse et commit", or any variation of committing messy
pending work intelligently.
allowed-tools:
- Bash
- Read
+6 -8
View File
@@ -1,14 +1,12 @@
---
name: doc
description: |
Use when documentation may be out of sync with code — added features
missing from docs, removed features still documented, or README / INSTALL
/ DEPLOY / CHANGELOG drift detected. Stack-aware audit, cross-references
git history, patches approved items.
Triggers: "doc", "sync docs", "audit docs", "update readme", "check
documentation", "are docs up to date", "documentation drift", "stale docs",
"new feature not documented", "removed feature still in docs",
"create README", "should I have a DEPLOY doc".
Use when documentation may be out of sync with code — features
added/removed vs README / INSTALL / DEPLOY / CHANGELOG. Stack-aware
audit, cross-references git history, patches approved items.
Triggers: "doc", "sync docs", "update readme", "documentation drift",
"stale docs", "docs à jour ?", "create README", "should I have a
DEPLOY doc".
argument-hint: [leave empty for full audit, or list specific files/docs to check]
allowed-tools:
- Read
+5 -8
View File
@@ -2,14 +2,11 @@
name: geo
description: |
Use when a web project needs AI-search visibility audit — ChatGPT,
Perplexity, Claude, Gemini, AI Overviews, Copilot, Brave AI, DuckAssist,
You.com, Apple Intelligence. Standalone GEO; dispatches the geo-analyzer
agent.
Triggers: "geo", "AI search", "ChatGPT visibility", "Perplexity
optimisation", "llms.txt", "AI crawlers", "Google AI Overview",
"entity SEO", "Wikidata", "generative engine optimization",
"référencement IA", "optimisation IA".
For combined SEO+GEO → /seo.
Perplexity, Gemini, AI Overviews, Copilot… Standalone GEO; dispatches
the geo-analyzer agent.
Triggers: "geo", "AI search", "llms.txt", "AI crawlers", "entity SEO",
"Wikidata", "generative engine optimization", "référencement IA".
Combined SEO+GEO → /seo.
argument-hint: optional keywords/scope, e.g. "SaaS B2B content GEO" or "audit llms.txt et entity SEO"
allowed-tools:
- Read
+7 -15
View File
@@ -1,21 +1,13 @@
---
name: harden
description: |
Web hardening audit — transport (HTTPS/TLS, HTTP→HTTPS redirect, HSTS),
security headers (CSP, X-Frame-Options, X-Content-Type-Options,
Referrer-Policy, Permissions-Policy), cookie flags (Secure, HttpOnly,
SameSite), canonical URLs, custom 404, and server config hardening
(.htaccess, nginx.conf, netlify.toml, vercel.json, _headers, _redirects,
wrangler.toml). Dispatches the seo-analyzer agent with a STRICT scope
filter — no meta/OG/JSON-LD/sitemap/CWV/headings/alt/i18n noise.
Produces .claude/audits/HARDEN.md.
Trigger: "harden", "web hardening", "ssl audit", "https audit",
"hsts", "csp", "security headers", "http to https", "redirect audit",
"htaccess audit", "404 page", "canonical audit", "transport security",
"durcissement web", "audit sécurité web", "entêtes sécurité".
For full SEO audit (meta/OG/JSON-LD/sitemap/CWV) → use /seo.
For AI search / llms.txt / AI crawlers → use /geo.
For secrets / dependency CVEs / OWASP code-level → use /cso.
Web hardening audit — HTTPS/TLS, HSTS, security headers (CSP,
X-Frame-Options…), cookie flags, canonical, custom 404, server config
(.htaccess, nginx, netlify, vercel…). Strict scope: no
meta/OG/JSON-LD/sitemap noise. Report: .claude/audits/HARDEN.md.
Triggers: "harden", "security headers", "csp", "hsts", "https/ssl
audit", "redirect audit", "durcissement web", "entêtes sécurité".
Meta/sitemap/CWV → /seo. llms.txt/AI → /geo. Secrets/CVE/OWASP → /cso.
argument-hint: [URL] [--fix] [--local|--full] [--no-external]
allowed-tools:
- Read
+7 -10
View File
@@ -1,16 +1,13 @@
---
name: seo
description: |
Use when a web project needs SEO + GEO audit or optimization — classical
search (Google, Bing, DuckDuckGo) AND AI search (ChatGPT, Perplexity,
Claude, Gemini, AI Overviews, Copilot). Parallel multi-agent orchestrator:
dispatches seo-analyzer + geo-analyzer concurrently, merges envelopes into
.claude/audits/SEO.md.
Triggers: "seo", "referencement", "audit SEO", "meta tags",
"structured data", "JSON-LD", "sitemap", "robots.txt", "Google ranking",
"local SEO", "AI search", "GEO", "llms.txt", "ChatGPT visibility",
"Perplexity", "Google AI Overview".
For GEO only → /geo. For W3C/a11y → /web-validate. For bugs → /bugfix.
Use when a web project needs SEO + GEO audit or optimization —
classical search (Google, Bing) AND AI search (ChatGPT, Perplexity, AI
Overviews). Parallel orchestrator: dispatches seo-analyzer +
geo-analyzer concurrently, merges into .claude/audits/SEO.md.
Triggers: "seo", "referencement", "meta tags", "JSON-LD", "sitemap",
"robots.txt", "local SEO", "llms.txt", "ChatGPT visibility".
GEO only → /geo. W3C/a11y → /web-validate. Bugs → /bugfix.
argument-hint: optional keywords/scope, e.g. "local SEO plombier 91 94 77" or "SaaS B2B content strategy"
allowed-tools:
- Read
+6 -8
View File
@@ -1,14 +1,12 @@
---
name: web-validate
description: |
Use when a web project needs W3C HTML/CSS validity check or WCAG 2.1
accessibility audit. Dispatches the validator-analyzer agent with a
STRICT scope filter (no meta/OG/JSON-LD/CWV/security-header noise).
Triggers: "validate", "validation", "w3c", "html validity",
"css validity", "wcag", "accessibility", "a11y audit", "axe", "pa11y",
"wave", "validator.w3.org", "nu validator", "accessibilité",
"audit a11y", "audit wcag", "normes w3c", "conformité web".
For CSP/HSTS/404 → /harden. For meta/sitemap → /seo. For AI engines → /geo.
Use when a web project needs W3C HTML/CSS validity or WCAG 2.1
accessibility audit. Dispatches the validator-analyzer agent, strict
scope (no meta/security-header noise).
Triggers: "validate", "w3c", "wcag", "a11y", "accessibility", "axe",
"pa11y", "accessibilité", "conformité web".
CSP/HSTS/404 → /harden. Meta/sitemap → /seo. AI engines → /geo.
argument-hint: [URL] [--fix] [--local|--full] [--no-external]
allowed-tools:
- Read
+21
View File
@@ -227,6 +227,27 @@ else
info "graphifyy not installed — skipping"
fi
# ── 6.5. Update bun ──
echo ""
echo "── Updating bun..."
if command -v bun &>/dev/null; then
if bun upgrade >/dev/null 2>&1; then
ok "bun $(bun --version 2>/dev/null || echo '?') (self-upgrade)"
else
warn "bun upgrade failed — try manually: bun upgrade"
fi
else
info "bun not installed — skipping"
fi
# NOT updated here, deliberately (audit 2026-07-02):
# - magic MCP: registered as `npx -y @21st-dev/magic@latest` — npx resolves
# the latest release at every invocation, nothing to upgrade.
# - graphify Claude integration (`graphify claude install`): rewrites curated
# CLAUDE.md / .claude/settings.json (BDR-028 guard territory) — re-run
# MANUALLY only if a graphify upgrade changes its hook format.
# - gsd: pinned in plugins.lock.json — Step 4 reinstalls the PIN, it does not
# advance it. Bump the lock deliberately, then re-run.
# ── 7. Update Emil Design Engineering skill ──
echo ""
echo "── Updating Emil Design Engineering..."