From b7026e4bdaab2e8a8eb9bbe9d6629a4f3806c781 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Mon, 20 Jul 2026 10:45:06 +0200 Subject: [PATCH] =?UTF-8?q?feat(ctx7):=20coverage=20extension=20=E2=80=94?= =?UTF-8?q?=20fast-libs=20single=20source=20+=20reminder=20hook=20+=20exec?= =?UTF-8?q?utor=20briefs=20(BDR-078)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - lib/fast-libs.sh: detect/cache-status verbs, JS+Python manifests, 7-day cache freshness, LC_ALL=C sort — replaces 3 hardcoded lists (ship-feature 0c, init-project 5c, onboard 3.5) - hooks/ctx7-reminder.sh: once-per-session UserPromptSubmit nudge when the project carries fast-libs and .ctx7-cache/ is missing/stale - find-docs: before-writing-code trigger + cache-first rule; dist is machine-owned (gitignored) so the durable patch lives in install-plugins.sh STEP ctx7 (idempotent, grep-guarded) - feater/bugfixer briefs: fast-lib docs rule (fresh cache read, else 2-topic ctx7 fetch, else NOTES cache miss + proceed) - tests: lib/tests/fast-libs.test.sh (11 checks); shellcheck + full make test green (review-guards 5/0) --- .claude/memory/decisions.md | 4 +++ .claude/memory/journal.md | 3 ++ .claude/tasks/TODO.md | 20 +++++++++++ CHANGELOG.md | 3 ++ agents/bugfixer.md | 6 ++++ agents/feater.md | 6 ++++ hooks/ctx7-reminder.sh | 63 ++++++++++++++++++++++++++++++++++ install-plugins.sh | 40 ++++++++++++++++++++++ lib/fast-libs.sh | 65 ++++++++++++++++++++++++++++++++++++ lib/tests/fast-libs.test.sh | 52 +++++++++++++++++++++++++++++ settings.json | 6 ++++ skills/init-project/SKILL.md | 4 ++- skills/onboard/SKILL.md | 11 +++--- skills/ship-feature/SKILL.md | 3 +- 14 files changed, 277 insertions(+), 9 deletions(-) create mode 100644 hooks/ctx7-reminder.sh create mode 100644 lib/fast-libs.sh create mode 100644 lib/tests/fast-libs.test.sh diff --git a/.claude/memory/decisions.md b/.claude/memory/decisions.md index cb36a38..1bb5deb 100644 --- a/.claude/memory/decisions.md +++ b/.claude/memory/decisions.md @@ -1061,3 +1061,7 @@ Reverses the BDR-066 rejected alternative "opus pins on audit agents (session-in ### BDR-077 — Model-tiering v2: 4-tier explicit routing, mode-based splits, no-inherit dispatches [accepted] (2026-07-19) Supersedes BDR-076 scope + amends BDR-066. Doctrine: session model (Fable) = main-loop reflection/orchestration/planning/logic ONLY; main-loop retention criteria = interactive | conversation-context access | orchestration decision | dispatch overhead > step cost. NOTHING dispatched inherits: typed agents = frontmatter pin, built-ins = `model=` at every call site (`fable` for skill-runner reflection children, else complexity tier). Spike+smoke proven: `model:"fable"` resolves claude-fable-5 (enum-validated, loud fail, no silent fallback); call-site override BEATS a typed pin (sonnet-pinned verifier ran haiku). Fail-safe pin rule: mixed-mode agents keep the HIGH tier as pin, overrides go DOWN — forgotten override over-tiers (cost), never downgrades judgment. Mode-based splits (commit-changer precedent generalized; file splits rejected): doc-syncer audit(opus)/patch(sonnet) — ALSO fixed a latent defect: /doc dispatched an agent whose STEP 8 interactive gate could never fire; gates hoisted to a DISPATCHER PROTOCOL section; handover-doc-writer synthesize(opus)/render(sonnet) via run-scoped `.audit/handover-draft-.md` + DRAFT COMPLETE sentinel; seo/geo collect(sonnet)/judge(OPUS PIN)/template(sonnet) via `.audit/*-signals-.md` + COLLECTION COMPLETE + fail-closed judge + dispatcher ERROR contract (mute/ERROR judge NEVER carried into templating; retry once, escalate). File split only for a genuinely new role: plugin-probe (sonnet, facts-only) + plugin-advisor repinned opus reasoner (fail-closed on missing PROBE REPORT) + lib/plugin-gate.md (checkpoint + apply gate, doc-commit ×N include pattern). Inline→dispatch conversions: scaffolder, onboarder, doc-commit steps ×5 flows — their sonnet pins were INERT since creation, now live; CHANGE SUMMARY crosses the doc dispatch into doc-commit (LRN-126 wire). Tier moves: validator-analyzer opus→sonnet (deterministic runner); commit-changer propose=opus/apply=pin; ship-feature/init-project code-review dispatches = opus explicit (WAS an inherit leak); client-handover-writer's 7 skill-runners = model:"fable". Every wave shipped an IN-WAVE planted-input smoke as its merge gate — all PASSED disk-verified. Census §12-18 (125 pass; one vacuous line-wrapped lock self-caught = LRN-093 live). 6 waves, branches feature/model-tiering-w1..w6, merged on user standing signal. Plan: challenged 3 blind lenses + 1 confirmation (1 BLOCKER closed by spike, 8 MAJORs + 8 MINORs closed by named changes, 0 deferred). Refs: `.claude/tasks/plans/2026-07-19-model-tiering-v2-{analysis,plan}.md`. + +### BDR-078 — ctx7 coverage: central fast-libs list + once-per-session reminder hook; every code path covered [accepted] (2026-07-20) +Refines BDR-053 (single surface). Audit 2026-07-20: coverage PARTIAL — find-docs fired on user doc-questions only; ship-feature 0c / init-project 5c pre-fetched; /feat //bugfix executors + ad-hoc coding NEVER consulted ctx7; fast-libs list hardcoded 3× (drift risk). 4 closures shipped: (a) find-docs description += BEFORE-writing-code trigger (fast-moving lib, even without doc question, unless fresh cache) + cache-first rule in body (tee fetched docs to .ctx7-cache/); (b) feater+bugfixer briefs += fast-lib docs rule — read fresh `.ctx7-cache/*.md`, else `npx ctx7@latest` fetch max 2 topics, else `ctx7 cache miss: ` in NOTES + proceed (executors lack Skill tool → Bash path); (c) hooks/ctx7-reminder.sh UserPromptSubmit — ONE fire/session (sentinel on session_id), only when project manifest carries fast-libs; reports cache state; skips turns; always exit 0; (d) lib/fast-libs.sh = SINGLE SOURCE (detect / cache-status verbs, JS package.json anchored full-key match + Python requirements/pyproject, 7-day freshness, LC_ALL=C sort locale-independent) consumed by hook + 3 pipeline skills + 2 briefs. 2nd session surface DELIBERATE, not a BDR-053 reversal: 053 killed a 490-tok ALWAYS-ON rule duplicate; hook costs ~0 quiet, 1 line once when fast-libs present. Alternatives rejected: PreToolUse Edit/Write gate (fires per-edit = noise); description-only fix (probabilistic, executors unreachable). Tests: lib/tests/fast-libs.test.sh 11 checks (anchored/near-miss/py/none, cache fresh/stale/missing, hook fire/sentinel/quiet×2); shellcheck + full make test green. Branch feature/ctx7-coverage, unmerged (human gate). +Amendment (same session): skills/find-docs = machine-owned dist (gitignored, ctx7 regenerates on fresh clone) → durable copy of closure (a) lives in install-plugins.sh STEP ctx7 (idempotent grep-guarded python patch, fixture-verified); live SKILL.md carries the same edit uncommitted by design. diff --git a/.claude/memory/journal.md b/.claude/memory/journal.md index 1cb9ec8..1b6d311 100644 --- a/.claude/memory/journal.md +++ b/.claude/memory/journal.md @@ -413,3 +413,6 @@ rules: ## 2026-07-19 - BDR-076: dispatched judgment agents pinned opus (analyzer, plan-challenger, seo/geo/validator-analyzer + 6 onboard general-purpose dispatches); Fable now = inline orchestration/reflection only. interviewer + client-handover-writer left unpinned (inline-load, pin inert). Local opus-4-8 session pin dropped from settings.local.json. Census §11 added (61 pass), loops-light 35, make test green. feature/opus-pin-audit-agents, UNMERGED. - BDR-077 model-tiering v2 SHIPPED: 6 waves (W0 baseline merge → W1 no-inherit+fable skill-runners → W2 plugin split + doc two-mode + inert-pin conversions → W3 tier moves → W4 handover two-mode → W5 seo/geo 3-mode pipelines → W6 doctrine sweep). Plan challenged 4 passes (1 BLOCKER closed by fable spike). Per-wave planted-input smokes disk-verified. Census 125/0, make test green throughout. [[BDR-077]] [[LRN-137]]. + +## 2026-07-20 +- ctx7 coverage audit (user ask "ctx7 appelé à chaque techno ?") → verdict PARTIAL. 4 gaps: find-docs question-only, /feat //bugfix executors blind, ad-hoc coding uncovered, fast-libs hardcoded 3×. All 4 closed → BDR-078 (fast-libs.sh single source + ctx7-reminder hook + description trigger + executor-brief rule). fast-libs test 11/0, make test + review-guards green. feature/ctx7-coverage, UNMERGED. diff --git a/.claude/tasks/TODO.md b/.claude/tasks/TODO.md index 854fab2..a41d64b 100644 --- a/.claude/tasks/TODO.md +++ b/.claude/tasks/TODO.md @@ -1,5 +1,25 @@ # TODO +## 2026-07-20 — ctx7 coverage extension (feature/ctx7-coverage, BDR-078) +Close the 4 gaps from the ctx7 coverage audit: /feat //bugfix + ad-hoc coding +never consult ctx7; fast-libs list hardcoded 3×; zero deterministic backstop. +- [x] (d) `lib/fast-libs.sh` — single source of truth: `detect` + + `cache-status` verbs; JS (package.json exact/scoped keys) + Python; + 7-day cache freshness. LC_ALL=C sort (locale-independent order). +- [x] (c) `hooks/ctx7-reminder.sh` — UserPromptSubmit, once-per-session + sentinel, fires only when fast-libs detected; settings.json + registration (2nd ctx7 surface, deliberate refinement of BDR-053). +- [x] (a) find-docs description — before-writing-code trigger (fast-moving + libs, even without a doc question) + cache-first rule in body. +- [x] (b) feater.md + bugfixer.md — fast-lib docs rule (read fresh cache, + else ctx7 fetch max 2 topics, else NOTES cache miss + proceed). +- [x] consumers → lib: ship-feature STEP 0c, init-project STEP 5c, onboard + STEP 3.5 detection blocks point at fast-libs.sh. +- [x] `lib/tests/fast-libs.test.sh` (lib verbs + hook fire/sentinel/quiet) + — 11/0, auto-discovered by the make test glob. +- [x] Gate: shellcheck + make test green (review-guards 5/0). BDR-078 + + journal + CHANGELOG done. Committed on branch, NO merge (human gate). + ## 2026-07-19 — Opus-pin dispatched judgment agents (branch feature/opus-pin-audit-agents) Goal: session model (Fable) = orchestration + inline reflection ONLY. diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d6bc80..1cc3b6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). ## [Unreleased] +### Added +- **ctx7 coverage extension (BDR-078)** — the "consult current docs before coding against a fast-moving lib" doctrine now covers every code path, not just the two big pipelines. (1) `lib/fast-libs.sh`: single source of truth for fast-lib detection (`detect` / `cache-status` verbs; JS package.json anchored keys + Python requirements/pyproject; 7-day `.ctx7-cache/` freshness; locale-independent sort), replacing three hardcoded lists (`/ship-feature` STEP 0c, `/init-project` STEP 5c, `/onboard` STEP 3.5). (2) `hooks/ctx7-reminder.sh`: once-per-session UserPromptSubmit nudge when the project carries fast-libs and the cache is missing/stale — closes the ad-hoc-coding gap. (3) find-docs description extended with a before-writing-code trigger + a cache-first rule (read fresh cache, tee fetched docs back into it). (4) feater/bugfixer executor briefs gain the fast-lib docs rule (read fresh cache, else 2-topic `npx ctx7@latest` fetch, else report `ctx7 cache miss` and proceed). Second deliberate ctx7 surface — a scoped refinement of BDR-053's single-surface rule, not a reversal. + ## [1.1.0] — 2026-07-16 ### Added diff --git a/agents/bugfixer.md b/agents/bugfixer.md index 3a7ecbc..c1771ab 100644 --- a/agents/bugfixer.md +++ b/agents/bugfixer.md @@ -36,6 +36,12 @@ Every choice was made in the plan or is a NEED-DECISION to report. before reporting. - Follow existing code patterns and CLAUDE.md limits (function size, params, no global state). Keep the fix minimal — no "while we're here" cleanups. +- Fast-moving libs (`bash ~/.claude/lib/fast-libs.sh detect .` — React, + Next.js, Prisma…): before touching their APIs, read a fresh + `.ctx7-cache/*.md` if present; else fetch targeted docs, max 2 + topics (`npx ctx7@latest library ""` then `docs ""`). + ctx7 unavailable → add `ctx7 cache miss: ` to NOTES and proceed on + model knowledge. Stable techs skip this entirely. - FORBIDDEN: `git commit`, branch ops, push, merge, new dependencies, security/verifier dispatch, editing `.claude/**` or memory registries, user questions (you cannot ask — report instead), attribution trailers of any kind. diff --git a/agents/feater.md b/agents/feater.md index 9027847..6d43318 100644 --- a/agents/feater.md +++ b/agents/feater.md @@ -47,6 +47,12 @@ report below is optional on this path (the dispatcher needs the edit applied suite incrementally; run it fully before reporting. - Follow existing code patterns and CLAUDE.md limits (function size, params, no global state). Match comment density and naming. +- Fast-moving libs (`bash ~/.claude/lib/fast-libs.sh detect .` — React, + Next.js, Prisma…): before coding against their APIs, read a fresh + `.ctx7-cache/*.md` if present; else fetch targeted docs, max 2 + topics (`npx ctx7@latest library ""` then `docs ""`). + ctx7 unavailable → add `ctx7 cache miss: ` to NOTES and proceed on + model knowledge. Stable techs (C, SQL, POSIX sh…) skip this entirely. - FORBIDDEN: `git commit`, branch ops, push, merge, new dependencies, editing `.claude/**` or memory registries, user questions (you cannot ask — report instead), attribution trailers of any kind. diff --git a/hooks/ctx7-reminder.sh b/hooks/ctx7-reminder.sh new file mode 100644 index 0000000..a201f40 --- /dev/null +++ b/hooks/ctx7-reminder.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# ctx7-reminder.sh +# +# UserPromptSubmit hook. When the current project uses fast-moving libs +# (lib/fast-libs.sh) it injects ONE reminder per session to consult ctx7 +# (find-docs skill) before coding against their APIs, pointing at the +# .ctx7-cache/ state. Closes the ad-hoc-coding gap: find-docs' description +# fires on doc *questions* and ship-feature/init-project pre-fetch, but +# nothing covered a plain "add a useEffect here" prompt (BDR-078; second +# deliberate ctx7 surface, scoped refinement of BDR-053 single-surface). +# +# Soft nudge: always exits 0, never blocks. Stable-tech projects (no +# manifest, or no fast-lib match) stay silent. + +set -euo pipefail + +input="$(cat)" + +field() { # $1=json key — extracted from hook stdin, empty on failure + printf '%s' "$input" | python3 -c \ + "import sys,json; print(json.load(sys.stdin).get('$1',''))" \ + 2>/dev/null || true +} + +prompt="$(field prompt)" +case "$prompt" in + ''*) exit 0 ;; # harness turn, not a user request +esac + +cwd="$(field cwd)" +[ -n "$cwd" ] || cwd="$PWD" + +# Cheap bail-out before any lib work: no manifest → no fast-libs. +[ -f "$cwd/package.json" ] || [ -f "$cwd/requirements.txt" ] \ + || [ -f "$cwd/pyproject.toml" ] || exit 0 + +# One fire per session: the doctrine holds for the whole session, +# repeating it on every prompt would be token spam. +session_id="$(field session_id)" +sentinel="${TMPDIR:-/tmp}/.ctx7-reminder-${session_id:-nosession}" +[ -e "$sentinel" ] && exit 0 + +# Resolve the lib next to this hook (repo layout), fall back to the +# installed copy — both paths exist through the link.sh symlinks. +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +libsh="${script_dir}/../lib/fast-libs.sh" +[ -f "$libsh" ] || libsh="${HOME}/.claude/lib/fast-libs.sh" +[ -f "$libsh" ] || exit 0 + +libs="$(bash "$libsh" detect "$cwd" 2>/dev/null || true)" +[ -n "$libs" ] || exit 0 + +status="$(bash "$libsh" cache-status "$cwd" 2>/dev/null || true)" +: > "$sentinel" || true +list="$(printf '%s' "$libs" | tr '\n' ' ' | sed 's/ *$//')" + +if [ "$status" = "fresh" ]; then + printf '📚 Fast-moving libs in this project (%s) — fresh .ctx7-cache/ present: read the matching cache file before relying on their APIs.\n' "$list" +else + printf '📚 Fast-moving libs in this project (%s) — .ctx7-cache/ %s: consult ctx7 (find-docs skill) before writing code against their APIs. Stable techs need nothing.\n' "$list" "${status:-missing}" +fi + +exit 0 diff --git a/install-plugins.sh b/install-plugins.sh index a8525f7..4610ef4 100644 --- a/install-plugins.sh +++ b/install-plugins.sh @@ -644,6 +644,46 @@ if command -v ctx7 &>/dev/null; then # (~490 tok/session, job1 F10). Purge it unconditionally so re-runs and # manual `ctx7 setup` invocations stay rule-free. rm -f "$HOME/.claude/rules/context7.md" + # BDR-078: re-apply the coverage extension to the generated skill — the + # before-writing-code trigger (description) + the cache-first rule (body). + # The dist is machine-owned (gitignored, regenerated on fresh clones), so + # the durable copy of this patch lives HERE. Idempotent: grep-guarded. + _fd="$HOME/.claude/skills/find-docs/SKILL.md" + if [ -f "$_fd" ] && ! grep -q 'fast-libs.sh detect' "$_fd"; then + if python3 - "$_fd" <<'PY' +import sys +p = sys.argv[1] +s = open(p, encoding="utf-8").read() +DESC = """ + Also use BEFORE writing or modifying code that uses a fast-moving library + (anything `bash ~/.claude/lib/fast-libs.sh detect .` reports — React, + Next.js, Prisma, Tailwind, Astro, Svelte…), even when the user asked for + code rather than documentation — unless a fresh `.ctx7-cache/` file already + covers the API involved. Stable technologies (C, C++98, POSIX shell, SQL…) + need no lookup.""" +BODY = """ +## Cache first + +Before any fetch, check the project's `.ctx7-cache/` +(`bash ~/.claude/lib/fast-libs.sh cache-status .`): a fresh (<7 days) +`*.md` may already answer — read it instead of calling ctx7. When a +`docs` call supports code you are about to write, save the output for the +next consumer: +`npx ctx7@latest docs "" | tee .ctx7-cache/-.md`. +""" +i = s.index("\n---", 3) # closing frontmatter fence +s = s[:i] + "\n" + DESC + s[i:] +m = "using the Context7 CLI.\n" # intro line under the H1 +j = s.index(m) + len(m) if m in s else len(s) +s = s[:j] + BODY + s[j:] +open(p, "w", encoding="utf-8").write(s) +PY + then + ok "find-docs skill extended (BDR-078 fast-libs trigger + cache-first)" + else + warn "find-docs BDR-078 patch failed — re-run 'make plugin' or patch by hand" + fi + fi info "Standalone usage: ctx7 docs /vercel/next.js \"middleware\"" fi diff --git a/lib/fast-libs.sh b/lib/fast-libs.sh new file mode 100644 index 0000000..c0f2f7a --- /dev/null +++ b/lib/fast-libs.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# fast-libs.sh — single source of truth for "fast-moving library" detection. +# +# Fast-moving = API churns faster than model training data (React, Next.js, +# Prisma…) → consult ctx7 (find-docs) before coding against it. Stable techs +# (C, C++98, POSIX sh, SQL…) never match: no ctx7 needed (BDR-078). +# +# Consumers: hooks/ctx7-reminder.sh, /ship-feature STEP 0c, /init-project +# STEP 5c, /onboard STEP 3.5, feater/bugfixer executor briefs. +# +# Verbs: +# fast-libs.sh detect [dir] detected libs, one/line; exit 1 if none +# fast-libs.sh cache-status [dir] fresh|stale|missing; exit 0 only if fresh + +set -euo pipefail + +# Exact npm dependency keys (unscoped). Anchored full-key match — "react" +# must not drag react-icons along. +NPM_EXACT='next|react|react-dom|react-native|expo|prisma|supabase' +NPM_EXACT+='|drizzle-orm|astro|svelte|vue|nuxt|tailwindcss|vite|next-auth' +NPM_EXACT+='|motion|framer-motion|ai|openai|langchain|remix|fastify' +# Scoped npm orgs (@org/…). +NPM_SCOPED='prisma|supabase|astrojs|sveltejs|tanstack|clerk|anthropic-ai' +NPM_SCOPED+='|langchain|remix-run|nestjs|tailwindcss' +# Python distributions (requirements.txt / pyproject.toml). +PY_LIBS='fastapi|pydantic|sqlalchemy|langchain' + +CACHE_MAX_AGE_DAYS=7 + +npm_fast_libs() { # $1=dir — matching dependency keys, one per line + [ -f "$1/package.json" ] || return 0 + jq -r '((.dependencies // {}) + (.devDependencies // {})) | keys[]' \ + "$1/package.json" 2>/dev/null \ + | grep -E "^(${NPM_EXACT})\$|^@(${NPM_SCOPED})/" || true +} + +py_fast_libs() { # $1=dir — matching distributions, one per line + grep -hoiE "\b(${PY_LIBS})\b" \ + "$1/requirements.txt" "$1/pyproject.toml" 2>/dev/null \ + | tr '[:upper:]' '[:lower:]' | LC_ALL=C sort -u || true +} + +detect() { # $1=dir — union, sorted unique; exit 1 when empty + local libs + # LC_ALL=C: deterministic order whatever the caller's locale. + libs="$(printf '%s\n%s\n' "$(npm_fast_libs "$1")" "$(py_fast_libs "$1")" \ + | sed '/^$/d' | LC_ALL=C sort -u)" + [ -n "$libs" ] || return 1 + printf '%s\n' "$libs" +} + +cache_status() { # $1=dir — fresh|stale|missing; exit 0 only when fresh + [ -d "$1/.ctx7-cache" ] || { echo missing; return 1; } + if [ -n "$(find "$1/.ctx7-cache" -name '*.md' \ + -mtime "-${CACHE_MAX_AGE_DAYS}" -print -quit 2>/dev/null)" ]; then + echo fresh; return 0 + fi + echo stale; return 1 +} + +case "${1:-}" in + detect) detect "${2:-.}" ;; + cache-status) cache_status "${2:-.}" ;; + *) echo "usage: fast-libs.sh detect|cache-status [dir]" >&2; exit 2 ;; +esac diff --git a/lib/tests/fast-libs.test.sh b/lib/tests/fast-libs.test.sh new file mode 100644 index 0000000..9db21dc --- /dev/null +++ b/lib/tests/fast-libs.test.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# lib/tests/fast-libs.test.sh — fast-libs.sh verbs + ctx7-reminder hook (BDR-078) +set -u +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +L="$ROOT/lib/fast-libs.sh" +H="$ROOT/hooks/ctx7-reminder.sh" +pass=0; fail=0 +check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1)); + printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; } + +tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT + +# --- detect: JS fast-libs matched, stable deps ignored --- +mkdir -p "$tmp/js" +cat > "$tmp/js/package.json" <<'EOF' +{"dependencies":{"react":"^19","express":"^5","@tanstack/react-query":"^5"}, + "devDependencies":{"vite":"^6","lodash":"^4"}} +EOF +check T1-js-detect "$(bash "$L" detect "$tmp/js" | tr '\n' ' ')" \ + "@tanstack/react-query react vite " + +# react-icons must NOT ride the react match (anchored full-key) +mkdir -p "$tmp/near" +printf '{"dependencies":{"react-icons":"^5","express":"^5"}}' \ + > "$tmp/near/package.json" +check T2-near-miss "$(bash "$L" detect "$tmp/near" >/dev/null 2>&1; echo $?)" 1 + +# --- detect: python manifest + stable-tech project (exit 1) --- +mkdir -p "$tmp/py" +printf 'fastapi==0.115\nrequests>=2\n' > "$tmp/py/requirements.txt" +check T3-py-detect "$(bash "$L" detect "$tmp/py")" "fastapi" +mkdir -p "$tmp/cpp" +check T4-none "$(bash "$L" detect "$tmp/cpp" >/dev/null 2>&1; echo $?)" 1 + +# --- cache-status: missing / fresh / stale --- +check T5-missing "$(bash "$L" cache-status "$tmp/js" || true)" missing +mkdir -p "$tmp/js/.ctx7-cache"; touch "$tmp/js/.ctx7-cache/react-core.md" +check T6-fresh "$(bash "$L" cache-status "$tmp/js")" fresh +touch -d '10 days ago' "$tmp/js/.ctx7-cache/react-core.md" +check T7-stale "$(bash "$L" cache-status "$tmp/js" || true)" stale + +# --- hook: fires once per session, silent on stable projects --- +hook() { printf '{"prompt":"add a hook","session_id":"%s","cwd":"%s"}' \ + "$1" "$2" | TMPDIR="$tmp" bash "$H"; } +check H1-fires "$(hook s1 "$tmp/js" | grep -c 'Fast-moving')" 1 +check H2-once "$(hook s1 "$tmp/js" | wc -l)" 0 +check H3-cpp-quiet "$(hook s2 "$tmp/cpp" | wc -l)" 0 +check H4-notif-quiet \ + "$(printf '{"prompt":"x","session_id":"s3","cwd":"%s"}' \ + "$tmp/js" | TMPDIR="$tmp" bash "$H" | wc -l)" 0 + +printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ] diff --git a/settings.json b/settings.json index 7a09992..2ddaa29 100644 --- a/settings.json +++ b/settings.json @@ -281,6 +281,12 @@ "command": "bash ~/.claude/hooks/design-toolchain-reminder.sh", "timeout": 5, "statusMessage": "Checking design signals..." + }, + { + "type": "command", + "command": "bash ~/.claude/hooks/ctx7-reminder.sh", + "timeout": 5, + "statusMessage": "Checking fast-libs..." } ] } diff --git a/skills/init-project/SKILL.md b/skills/init-project/SKILL.md index e4832e2..4478abd 100644 --- a/skills/init-project/SKILL.md +++ b/skills/init-project/SKILL.md @@ -114,7 +114,9 @@ report carries the rendered README draft as `[CREATE-AUTO]`; re-dispatch that plan to write it. No stop (README bootstrap is unconditional). ## STEP 5c — CTX7 PRE-FETCH (if fast-libs detected) -If `fast-libs` signal was detected in STEP 0 (Next.js, React 18+, Prisma, Supabase, Drizzle, etc.): +If `fast-libs` signal was detected in STEP 0 — single source of truth: +`bash ~/.claude/lib/fast-libs.sh detect .` (Next.js, React, Prisma, +Supabase, Drizzle… — BDR-078): 1. Create `.ctx7-cache/` directory in project root. 2. For each detected fast-lib, fetch core docs: ```bash diff --git a/skills/onboard/SKILL.md b/skills/onboard/SKILL.md index 75ca55c..96f2c69 100644 --- a/skills/onboard/SKILL.md +++ b/skills/onboard/SKILL.md @@ -207,13 +207,10 @@ ls .ctx7-cache/ 2>/dev/null ``` ### Détection fast-libs -Parse manifests selon l'archétype : -- **nextjs-app-router** → chercher : next, react, prisma, @supabase/*, drizzle-orm, next-auth, @clerk/* -- **react-spa** → chercher : react, @tanstack/*, zustand, jotai -- **rest-api-node** → chercher : fastify, @nestjs/*, prisma, drizzle-orm -- **rest-api-python** → chercher : fastapi, pydantic, sqlalchemy (si ≥ 2.0) -- **astro-static** → chercher : astro, @astrojs/* -- **wordpress / cli-tool / library / dotfiles-meta / static-html** → souvent aucune fast-lib, audit léger +Source unique : `bash ~/.claude/lib/fast-libs.sh detect .` (deps JS/TS via +package.json + Python via requirements/pyproject — liste centralisée, +BDR-078). Archétypes wordpress / cli-tool / library / dotfiles-meta / +static-html → souvent aucune fast-lib, audit léger. ### Vérification cache Pour chaque fast-lib détectée : diff --git a/skills/ship-feature/SKILL.md b/skills/ship-feature/SKILL.md index 8f6adc4..4cb8602 100644 --- a/skills/ship-feature/SKILL.md +++ b/skills/ship-feature/SKILL.md @@ -52,7 +52,8 @@ ls .gsd/ROADMAP.md 2>/dev/null | head -1 STOP. ## STEP 0c — CTX7 CACHE CHECK (if fast-libs in project) -Check if the project uses fast-evolving libs (scan `package.json` for next, react, prisma, supabase, drizzle, expo): +Check if the project uses fast-moving libs — single source of truth: +`bash ~/.claude/lib/fast-libs.sh detect .` (exit 1 = none; BDR-078): 1. If `.ctx7-cache/` exists with recent files (<7 days old) → print `📚 ctx7 cache found: ` and continue. 2. If `.ctx7-cache/` missing or stale AND `ctx7` is installed AND fast-libs detected: ```bash