diff --git a/hooks/hooks.json b/hooks/hooks.json index 2d62dcd..d40bde1 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -1,9 +1,18 @@ { "hooks": { - "Stop": [{ + "SessionStart": [{ "hooks": [{ - "type": "prompt", - "prompt": "--- skill detect ---\nRead the user's prompt. Detect what language, framework, tool, or topic they are asking about. If it matches any hub-guide skill concept (see available skills), activate that skill's rules for your response.\n\nDetection is concept-based, not keyword-based — works regardless of what language the user speaks.\n---" + "type": "command", + "command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/scripts/detect-project.sh\"", + "timeout": 10 + }] + }], + "PreToolUse": [{ + "matcher": "Write|Edit", + "hooks": [{ + "type": "command", + "command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/scripts/detect-file-type.sh\" \"$TOOL_INPUT\"", + "timeout": 10 }] }] } diff --git a/hooks/scripts/detect-file-type.sh b/hooks/scripts/detect-file-type.sh new file mode 100755 index 0000000..2244903 --- /dev/null +++ b/hooks/scripts/detect-file-type.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# hub-guide: detect file extension from tool input and suggest language-specific rules +# Runs before Write/Edit to inject best-practice context + +TOOL_INPUT="$1" + +# Extract file path from tool input (looks for "file_path" in JSON) +FILE_PATH=$(echo "$TOOL_INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/') +EXT="${FILE_PATH##*.}" + +case "$EXT" in + ts|tsx) + echo "📐 [hub-guide] TypeScript file — apply typescript skill rules (strict types, no-any, proper generics)" + ;; + py) + echo "📐 [hub-guide] Python file — apply python skill rules (type hints, PEP 8, no wildcard imports)" + ;; + rs) + echo "📐 [hub-guide] Rust file — apply rust skill rules (ownership, error handling, clippy clean)" + ;; + go) + echo "📐 [hub-guide] Go file — apply go skill rules (idiomatic Go, interfaces, error handling)" + ;; + js|jsx) + echo "📐 [hub-guide] JavaScript file — apply typescript skill rules (ESM, modern JS)" + ;; + dockerfile|Dockerfile) + echo "📐 [hub-guide] Dockerfile — apply docker skill rules (multi-stage, minimal layers, no root)" + ;; + yml|yaml) + echo "📐 [hub-guide] YAML file — check ci-cd and docker skill rules" + ;; +esac diff --git a/hooks/scripts/detect-project.sh b/hooks/scripts/detect-project.sh new file mode 100755 index 0000000..f4d6a1f --- /dev/null +++ b/hooks/scripts/detect-project.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# hub-guide: detect project type and suggest relevant skills +# Runs at SessionStart to prime best-practice skills + +PROJECT_DIR="$(pwd)" +SKILLS="" + +# Helper: check if a package.json field contains a dependency name +has_dep() { + local name="$1" + grep -q "\"$name\"" package.json 2>/dev/null +} + +# Monorepo indicators +if [ -f "pnpm-workspace.yaml" ] || [ -f "lerna.json" ] || [ -f "nx.json" ] || [ -f "rush.json" ] || [ -f "turborepo.json" ]; then + SKILLS="$SKILLS, monorepo" +fi + +# TypeScript / JavaScript +if [ -f "tsconfig.json" ] || [ -f "jsconfig.json" ]; then + SKILLS="$SKILLS, typescript" + # Framework detection from package.json dependencies + if has_dep "next"; then SKILLS="$SKILLS, nextjs"; fi + if has_dep "elysia"; then SKILLS="$SKILLS, elysiajs"; fi + if has_dep "hono"; then SKILLS="$SKILLS, hono-backend"; fi + if has_dep "drizzle-orm"; then SKILLS="$SKILLS, drizzle-database"; fi +fi + +# React (detect even without tsconfig) +if [ -f "vite.config.ts" ] || [ -f "vite.config.js" ] || has_dep "react" 2>/dev/null; then + # avoid double-adding if already caught by typescript block + case "$SKILLS" in *react-frontend*) ;; *) SKILLS="$SKILLS, react-frontend" ;; esac +fi + +# Next.js config file — catches JS-only projects too +if [ -f "next.config.js" ] || [ -f "next.config.ts" ] || [ -f "next.config.mjs" ]; then + case "$SKILLS" in *nextjs*) ;; *) SKILLS="$SKILLS, nextjs" ;; esac +fi + +# Python +if [ -f "pyproject.toml" ] || [ -f "setup.py" ] || [ -f "setup.cfg" ] || [ -f "requirements.txt" ] || [ -f "Pipfile" ] || [ -f "poetry.lock" ] || [ -f "uv.lock" ]; then + SKILLS="$SKILLS, python" +fi + +# Rust +if [ -f "Cargo.toml" ]; then + SKILLS="$SKILLS, rust" +fi + +# Go +if [ -f "go.mod" ]; then + SKILLS="$SKILLS, go" +fi + +# Docker +if [ -f "Dockerfile" ] || [ -f "docker-compose.yml" ] || [ -f "compose.yml" ] || ls Dockerfile.* 2>/dev/null | grep -q .; then + SKILLS="$SKILLS, docker" +fi + +# CI/CD +if [ -d ".github/workflows" ] || [ -f ".gitlab-ci.yml" ] || [ -f "Jenkinsfile" ] || [ -f "bitbucket-pipelines.yml" ]; then + SKILLS="$SKILLS, ci-cd" +fi + +# Always-active core skills (loaded every session) +CORE="engineering-principles, clean-code, clean-architecture, testing, error-handling, security, git-workflow, api-design" + +SKILLS="${SKILLS#, }" # strip leading ", " + +echo "📐 [hub-guide] detected: ${PROJECT_DIR}" +echo "📐 [hub-guide] core skills: ${CORE}" +if [ -n "$SKILLS" ]; then + echo "📐 [hub-guide] project-specific: ${SKILLS}" +fi +echo "📐 [hub-guide] Apply these best-practice rules throughout this session." +echo "📐 [hub-guide] When in doubt about intent or approach — ask instead of assuming." +echo "📐 [hub-guide] All skills work regardless of your spoken language — they trigger from code context and project files, not just English keywords."