Files
claude/lib/fast-libs.sh
Bastien Chanot b7026e4bda feat(ctx7): coverage extension — fast-libs single source + reminder hook + executor briefs (BDR-078)
- 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)
2026-07-20 10:45:06 +02:00

66 lines
2.5 KiB
Bash

#!/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