#!/bin/bash # Session Start Hook - Project Detection & Context Loading # Detects project type, sets environment variables, provides actionable next steps set -euo pipefail # Read input (not used much for SessionStart, but good practice) input=$(cat) # Get project directory PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}" ENV_FILE="${CLAUDE_ENV_FILE:-}" # Initialize detection results PROJECT_TYPE="unknown" HAS_CORE_CLI="false" DETECTED_FEATURES="" GIT_STATUS="" # Check for core CLI if command -v core &>/dev/null; then HAS_CORE_CLI="true" fi # Detect Go project if [[ -f "$PROJECT_DIR/go.mod" ]]; then PROJECT_TYPE="go" MODULE_NAME=$(head -1 "$PROJECT_DIR/go.mod" | sed 's/module //') DETECTED_FEATURES="$DETECTED_FEATURES go-module" # Check for specific Go patterns [[ -d "$PROJECT_DIR/cmd" ]] && DETECTED_FEATURES="$DETECTED_FEATURES cmd-pattern" [[ -d "$PROJECT_DIR/internal" ]] && DETECTED_FEATURES="$DETECTED_FEATURES internal-pkg" [[ -f "$PROJECT_DIR/Makefile" ]] && DETECTED_FEATURES="$DETECTED_FEATURES makefile" [[ -f "$PROJECT_DIR/go.work" ]] && DETECTED_FEATURES="$DETECTED_FEATURES workspace" fi # Detect PHP/Laravel project if [[ -f "$PROJECT_DIR/composer.json" ]]; then PROJECT_TYPE="php" [[ -f "$PROJECT_DIR/artisan" ]] && PROJECT_TYPE="laravel" [[ -d "$PROJECT_DIR/app/Http" ]] && DETECTED_FEATURES="$DETECTED_FEATURES laravel-http" fi # Detect Node.js project if [[ -f "$PROJECT_DIR/package.json" ]]; then [[ "$PROJECT_TYPE" == "unknown" ]] && PROJECT_TYPE="nodejs" [[ -f "$PROJECT_DIR/next.config.js" || -f "$PROJECT_DIR/next.config.mjs" ]] && PROJECT_TYPE="nextjs" [[ -f "$PROJECT_DIR/nuxt.config.ts" ]] && PROJECT_TYPE="nuxt" [[ -f "$PROJECT_DIR/tailwind.config.js" || -f "$PROJECT_DIR/tailwind.config.ts" ]] && DETECTED_FEATURES="$DETECTED_FEATURES tailwind" fi # Detect Rust project if [[ -f "$PROJECT_DIR/Cargo.toml" ]]; then PROJECT_TYPE="rust" fi # Detect Python project if [[ -f "$PROJECT_DIR/pyproject.toml" || -f "$PROJECT_DIR/setup.py" || -f "$PROJECT_DIR/requirements.txt" ]]; then [[ "$PROJECT_TYPE" == "unknown" ]] && PROJECT_TYPE="python" fi # Detect crypto/blockchain projects if [[ -d "$PROJECT_DIR/src/cryptonote_core" || -f "$PROJECT_DIR/cryptonote_config.h" ]]; then PROJECT_TYPE="cryptonote" DETECTED_FEATURES="$DETECTED_FEATURES blockchain crypto" fi # Check for Lethean-specific if [[ "$PROJECT_DIR" == *"lethean"* || -f "$PROJECT_DIR/.lethean" ]]; then DETECTED_FEATURES="$DETECTED_FEATURES lethean-project" fi # Check for Host UK repos if [[ "$PROJECT_DIR" == *"host-uk"* || "$PROJECT_DIR" == *"hostuk"* ]]; then DETECTED_FEATURES="$DETECTED_FEATURES host-uk-project" fi # Detect git info GIT_BRANCH="" GIT_REMOTE="" GIT_DIRTY="false" GIT_UNPUSHED="false" if [[ -d "$PROJECT_DIR/.git" ]]; then GIT_BRANCH=$(git -C "$PROJECT_DIR" branch --show-current 2>/dev/null || echo "") GIT_REMOTE=$(git -C "$PROJECT_DIR" remote get-url origin 2>/dev/null || echo "") DETECTED_FEATURES="$DETECTED_FEATURES git" # Check for uncommitted changes if [[ -n $(git -C "$PROJECT_DIR" status --porcelain 2>/dev/null) ]]; then GIT_DIRTY="true" DETECTED_FEATURES="$DETECTED_FEATURES uncommitted-changes" fi # Check for unpushed commits if [[ -n "$GIT_BRANCH" ]]; then UNPUSHED=$(git -C "$PROJECT_DIR" log origin/"$GIT_BRANCH"..HEAD --oneline 2>/dev/null | wc -l || echo "0") if [[ "$UNPUSHED" -gt 0 ]]; then GIT_UNPUSHED="true" DETECTED_FEATURES="$DETECTED_FEATURES unpushed-commits:$UNPUSHED" fi fi # Detect if it's a Gitea repo [[ "$GIT_REMOTE" == *"forge.lthn.ai"* ]] && DETECTED_FEATURES="$DETECTED_FEATURES forge-hosted" fi # Persist environment variables for the session if [[ -n "$ENV_FILE" ]]; then { echo "export PROJECT_TYPE=\"$PROJECT_TYPE\"" echo "export HAS_CORE_CLI=\"$HAS_CORE_CLI\"" echo "export DETECTED_FEATURES=\"$DETECTED_FEATURES\"" echo "export GIT_DIRTY=\"$GIT_DIRTY\"" [[ -n "$GIT_BRANCH" ]] && echo "export GIT_BRANCH=\"$GIT_BRANCH\"" } >> "$ENV_FILE" fi # Build context message for Claude CONTEXT_MSG="**Project Context**\\n" CONTEXT_MSG+="Type: \`$PROJECT_TYPE\` | Core CLI: $HAS_CORE_CLI" [[ -n "$GIT_BRANCH" ]] && CONTEXT_MSG+=" | Branch: \`$GIT_BRANCH\`" [[ "$GIT_DIRTY" == "true" ]] && CONTEXT_MSG+=" | ⚠️ Uncommitted changes" [[ "$GIT_UNPUSHED" == "true" ]] && CONTEXT_MSG+=" | 📤 Unpushed commits" CONTEXT_MSG+="\\n" # Add actionable next steps based on project type and core CLI if [[ "$HAS_CORE_CLI" == "true" ]]; then CONTEXT_MSG+="\\n**Core CLI Commands:**\\n" case "$PROJECT_TYPE" in go) CONTEXT_MSG+="| Task | Command |\\n" CONTEXT_MSG+="|------|---------|\\n" CONTEXT_MSG+="| Fix everything | \`core go qa --fix\` |\\n" CONTEXT_MSG+="| Quick check (no tests) | \`core go qa quick\` |\\n" CONTEXT_MSG+="| Pre-commit | \`core go qa pre-commit\` |\\n" CONTEXT_MSG+="| Full QA + coverage | \`core go qa --coverage --threshold=80\` |\\n" CONTEXT_MSG+="| PR ready | \`core go qa pr\` |\\n" CONTEXT_MSG+="| Only tests | \`core go qa --only=test\` |\\n" CONTEXT_MSG+="| Tests with race | \`core go qa --race\` |\\n" CONTEXT_MSG+="| Check changed files | \`core go qa --changed\` |\\n" if [[ "$DETECTED_FEATURES" == *"workspace"* ]]; then CONTEXT_MSG+="| Workspace sync | \`core go work sync\` |\\n" fi CONTEXT_MSG+="| Build release | \`core build\` |\\n" CONTEXT_MSG+="| Security scan | \`core security alerts\` |\\n" ;; php|laravel) CONTEXT_MSG+="| Task | Command |\\n" CONTEXT_MSG+="|------|---------|\\n" CONTEXT_MSG+="| Fix everything | \`core php qa --fix\` |\\n" CONTEXT_MSG+="| Quick check | \`core php qa --quick\` |\\n" CONTEXT_MSG+="| Full QA | \`core php qa --full\` |\\n" CONTEXT_MSG+="| Run tests | \`core php test\` |\\n" CONTEXT_MSG+="| Format code | \`core php fmt\` |\\n" CONTEXT_MSG+="| Static analysis | \`core php stan\` |\\n" CONTEXT_MSG+="| Security audit | \`core php audit\` |\\n" if [[ "$PROJECT_TYPE" == "laravel" ]]; then CONTEXT_MSG+="| Start dev | \`core php dev\` |\\n" CONTEXT_MSG+="| Deploy | \`core php deploy\` |\\n" fi ;; nodejs|nextjs|nuxt) CONTEXT_MSG+="| Task | Command |\\n" CONTEXT_MSG+="|------|---------|\\n" CONTEXT_MSG+="| Build project | \`core build\` |\\n" CONTEXT_MSG+="| Security scan | \`core security alerts\` |\\n" ;; *) CONTEXT_MSG+="| Task | Command |\\n" CONTEXT_MSG+="|------|---------|\\n" CONTEXT_MSG+="| Environment check | \`core doctor\` |\\n" CONTEXT_MSG+="| Repo health | \`core dev health\` |\\n" CONTEXT_MSG+="| CI status | \`core dev ci\` |\\n" CONTEXT_MSG+="| Security alerts | \`core security alerts\` |\\n" ;; esac # Git workflow commands (always available) if [[ "$DETECTED_FEATURES" == *"git"* ]]; then CONTEXT_MSG+="\\n**Git Workflow:**\\n" CONTEXT_MSG+="| Task | Command |\\n" CONTEXT_MSG+="|------|---------|\\n" CONTEXT_MSG+="| Multi-repo health | \`core git health\` |\\n" CONTEXT_MSG+="| Smart commit | \`core git commit\` |\\n" CONTEXT_MSG+="| Pull all repos | \`core git pull\` |\\n" CONTEXT_MSG+="| Push all repos | \`core git push\` |\\n" if [[ "$GIT_DIRTY" == "true" ]]; then CONTEXT_MSG+="| ⚠️ You have uncommitted changes |\\n" fi if [[ "$GIT_UNPUSHED" == "true" ]]; then CONTEXT_MSG+="| 📤 Push pending: \`core git push\` |\\n" fi fi # Suggested first action based on state CONTEXT_MSG+="\\n**Suggested First Action:**\\n" if [[ "$GIT_DIRTY" == "true" && "$PROJECT_TYPE" == "go" ]]; then CONTEXT_MSG+="\`core go qa --fix && core git commit\` - Fix issues and commit\\n" elif [[ "$PROJECT_TYPE" == "go" ]]; then CONTEXT_MSG+="\`core go qa --fix\` - Ensure code is clean\\n" elif [[ "$PROJECT_TYPE" == "php" || "$PROJECT_TYPE" == "laravel" ]]; then CONTEXT_MSG+="\`core php qa --fix\` - Ensure code is clean\\n" else CONTEXT_MSG+="\`core doctor\` - Check environment is ready\\n" fi else # No core CLI - provide manual commands CONTEXT_MSG+="\\n**Note:** \`core\` CLI not found. Install for enhanced workflow.\\n" case "$PROJECT_TYPE" in go) CONTEXT_MSG+="Manual: \`go fmt ./... && go vet ./... && go test ./...\`\\n" ;; php|laravel) CONTEXT_MSG+="Manual: \`composer test\`\\n" ;; nodejs|nextjs|nuxt) CONTEXT_MSG+="Manual: Check \`package.json\` scripts\\n" ;; esac fi # CryptoNote-specific warnings if [[ "$PROJECT_TYPE" == "cryptonote" || "$DETECTED_FEATURES" == *"crypto"* ]]; then CONTEXT_MSG+="\\n**⚠️ CryptoNote Project:**\\n" CONTEXT_MSG+="- Consensus-critical code - changes may fork the network\\n" CONTEXT_MSG+="- Review cryptonote-archive plugin for protocol specs\\n" CONTEXT_MSG+="- Test thoroughly on testnet before mainnet\\n" fi # KB suggestions based on context — "Know kung fu?" KB_HINT="" case "$PROJECT_TYPE" in go) [[ "$DETECTED_FEATURES" == *"host-uk"* ]] && KB_HINT="go lethean-specs" [[ -z "$KB_HINT" ]] && KB_HINT="go" ;; php|laravel) KB_HINT="php" ;; cryptonote) KB_HINT="cryptonote lethean-specs" ;; *) [[ "$DETECTED_FEATURES" == *"lethean"* ]] && KB_HINT="lethean-specs lethean-tech" [[ "$DETECTED_FEATURES" == *"host-uk"* && -z "$KB_HINT" ]] && KB_HINT="go infra" ;; esac if [[ -n "$KB_HINT" ]]; then CONTEXT_MSG+="\\n**Know kung fu?** \`/learn $KB_HINT\`\\n" fi # Output JSON response (escape for JSON) ESCAPED_MSG=$(echo -e "$CONTEXT_MSG" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | tr '\n' ' ' | sed 's/ */ /g') cat << EOF { "continue": true, "suppressOutput": false, "systemMessage": "$ESCAPED_MSG" } EOF