├── .claude ├── hooks │ ├── skill-activation-prompt.sh │ ├── error-handling-reminder.sh │ ├── package.json │ ├── tsconfig.json │ ├── trigger-build-resolver.sh │ ├── stop-build-check-enhanced.sh │ ├── README.md │ ├── skill-activation-prompt.ts │ ├── post-tool-use-tracker.sh │ ├── tsc-check.sh │ └── error-handling-reminder.ts ├── settings.local.json ├── commands │ ├── route-research-for-testing.md │ ├── dev-docs-update.md │ └── dev-docs.md ├── settings.json ├── agents │ ├── auto-error-resolver.md │ ├── frontend-error-fixer.md │ ├── refactor-planner.md │ ├── plan-reviewer.md │ ├── documentation-architect.md │ ├── web-research-specialist.md │ ├── auth-route-tester.md │ ├── code-architecture-reviewer.md │ ├── auth-route-debugger.md │ ├── code-refactor-master.md │ └── README.md └── skills │ ├── skill-developer │ ├── PATTERNS_LIBRARY.md │ ├── ADVANCED.md │ ├── HOOK_MECHANISMS.md │ └── TRIGGER_TYPES.md │ ├── backend-dev-guidelines │ ├── resources │ │ ├── database-patterns.md │ │ ├── middleware-guide.md │ │ ├── testing-guide.md │ │ ├── configuration.md │ │ ├── async-and-errors.md │ │ └── sentry-and-monitoring.md │ └── SKILL.md │ └── frontend-dev-guidelines │ └── resources │ ├── routing-guide.md │ ├── common-patterns.md │ └── styling-guide.md ├── .gitignore └── LICENSE /.claude/hooks/skill-activation-prompt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd "$CLAUDE_PROJECT_DIR/.claude/hooks" 5 | cat | npx tsx skill-activation-prompt.ts 6 | -------------------------------------------------------------------------------- /.claude/settings.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "allow": [ 4 | "Bash(for file in /root/git/claude-code-infrastructure-showcase/.claude/skills/frontend-dev-guidelines/resources/*.md)", 5 | "Bash(done)" 6 | ], 7 | "deny": [], 8 | "ask": [] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.claude/hooks/error-handling-reminder.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Skip if environment variable is set 4 | if [ -n "$SKIP_ERROR_REMINDER" ]; then 5 | exit 0 6 | fi 7 | 8 | # Get the directory of this script 9 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 10 | cd "$SCRIPT_DIR" 11 | 12 | cat | npx tsx error-handling-reminder.ts 13 | -------------------------------------------------------------------------------- /.claude/hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "claude-hooks", 3 | "version": "1.0.0", 4 | "description": "TypeScript hooks for Claude Code skill auto-activation", 5 | "private": true, 6 | "type": "module", 7 | "scripts": { 8 | "check": "tsc --noEmit", 9 | "test": "tsx skill-activation-prompt.ts < test-input.json" 10 | }, 11 | "dependencies": { 12 | "@types/node": "^20.11.0", 13 | "tsx": "^4.7.0", 14 | "typescript": "^5.3.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.claude/hooks/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "lib": ["ES2022"], 7 | "outDir": "./dist", 8 | "rootDir": ".", 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "allowSyntheticDefaultImports": true, 15 | "types": ["node"] 16 | }, 17 | "include": ["*.ts"], 18 | "exclude": ["node_modules", "dist"] 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | pnpm-debug.log* 7 | 8 | # Environment variables 9 | .env 10 | .env.local 11 | .env.*.local 12 | config.ini 13 | sentry.ini 14 | 15 | # Logs 16 | logs/ 17 | *.log 18 | pm2-logs/ 19 | 20 | # Build outputs 21 | dist/ 22 | build/ 23 | *.tsbuildinfo 24 | 25 | # IDE 26 | .vscode/ 27 | .idea/ 28 | *.swp 29 | *.swo 30 | *~ 31 | 32 | # OS 33 | .DS_Store 34 | Thumbs.db 35 | 36 | # Testing 37 | coverage/ 38 | .nyc_output/ 39 | 40 | # Temporary files 41 | tmp/ 42 | temp/ 43 | *.tmp 44 | 45 | # Database 46 | *.sqlite 47 | *.db 48 | 49 | # Prisma 50 | .env 51 | prisma/.env 52 | -------------------------------------------------------------------------------- /.claude/commands/route-research-for-testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Map edited routes & launch tests 3 | argument-hint: "[/extra/path …]" 4 | allowed-tools: Bash(cat:*), Bash(awk:*), Bash(grep:*), Bash(sort:*), Bash(xargs:*), Bash(sed:*) 5 | model: sonnet 6 | --- 7 | 8 | ## Context 9 | 10 | Changed route files this session (auto-generated): 11 | 12 | !cat "$CLAUDE_PROJECT_DIR/.claude/tsc-cache"/\*/edited-files.log \ 13 | | awk -F: '{print $2}' \ 14 | | grep '/routes/' \ 15 | | sort -u 16 | 17 | User-specified additional routes: `$ARGUMENTS` 18 | 19 | ## Your task 20 | 21 | Follow the numbered steps **exactly**: 22 | 23 | 1. Combine the auto list with `$ARGUMENTS`, dedupe, and resolve any prefixes 24 | defined in `src/app.ts`. 25 | 2. For each final route, output a JSON record with the path, method, expected 26 | request/response shapes, and valid + invalid payload examples. 27 | 3. **Now call the `Task` tool** using: 28 | 29 | ```json 30 | { 31 | "tool": "Task", 32 | "parameters": { 33 | "description": "route smoke tests", 34 | "prompt": "Run the auth-route-tester sub-agent on the JSON above." 35 | } 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Claude Code Infrastructure Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.claude/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "enableAllProjectMcpServers": true, 3 | "enabledMcpjsonServers": [ 4 | "mysql", 5 | "sequential-thinking", 6 | "playwright" 7 | ], 8 | "permissions": { 9 | "allow": [ 10 | "Edit:*", 11 | "Write:*", 12 | "MultiEdit:*", 13 | "NotebookEdit:*", 14 | "Bash:*" 15 | ], 16 | "defaultMode": "acceptEdits" 17 | }, 18 | "hooks": { 19 | "UserPromptSubmit": [ 20 | { 21 | "hooks": [ 22 | { 23 | "type": "command", 24 | "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/skill-activation-prompt.sh" 25 | } 26 | ] 27 | } 28 | ], 29 | "PostToolUse": [ 30 | { 31 | "matcher": "Edit|MultiEdit|Write", 32 | "hooks": [ 33 | { 34 | "type": "command", 35 | "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-use-tracker.sh" 36 | } 37 | ] 38 | } 39 | ], 40 | "Stop": [ 41 | { 42 | "hooks": [ 43 | { 44 | "type": "command", 45 | "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/tsc-check.sh" 46 | }, 47 | { 48 | "type": "command", 49 | "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/trigger-build-resolver.sh" 50 | } 51 | ] 52 | } 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.claude/commands/dev-docs-update.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Update dev documentation before context compaction 3 | argument-hint: Optional - specific context or tasks to focus on (leave empty for comprehensive update) 4 | --- 5 | 6 | We're approaching context limits. Please update the development documentation to ensure seamless continuation after context reset. 7 | 8 | ## Required Updates 9 | 10 | ### 1. Update Active Task Documentation 11 | For each task in `/dev/active/`: 12 | - Update `[task-name]-context.md` with: 13 | - Current implementation state 14 | - Key decisions made this session 15 | - Files modified and why 16 | - Any blockers or issues discovered 17 | - Next immediate steps 18 | - Last Updated timestamp 19 | 20 | - Update `[task-name]-tasks.md` with: 21 | - Mark completed tasks as ✅ 22 | - Add any new tasks discovered 23 | - Update in-progress tasks with current status 24 | - Reorder priorities if needed 25 | 26 | ### 2. Capture Session Context 27 | Include any relevant information about: 28 | - Complex problems solved 29 | - Architectural decisions made 30 | - Tricky bugs found and fixed 31 | - Integration points discovered 32 | - Testing approaches used 33 | - Performance optimizations made 34 | 35 | ### 3. Update Memory (if applicable) 36 | - Store any new patterns or solutions in project memory/documentation 37 | - Update entity relationships discovered 38 | - Add observations about system behavior 39 | 40 | ### 4. Document Unfinished Work 41 | - What was being worked on when context limit approached 42 | - Exact state of any partially completed features 43 | - Commands that need to be run on restart 44 | - Any temporary workarounds that need permanent fixes 45 | 46 | ### 5. Create Handoff Notes 47 | If switching to a new conversation: 48 | - Exact file and line being edited 49 | - The goal of current changes 50 | - Any uncommitted changes that need attention 51 | - Test commands to verify work 52 | 53 | ## Additional Context: $ARGUMENTS 54 | 55 | **Priority**: Focus on capturing information that would be hard to rediscover or reconstruct from code alone. -------------------------------------------------------------------------------- /.claude/commands/dev-docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Create a comprehensive strategic plan with structured task breakdown 3 | argument-hint: Describe what you need planned (e.g., "refactor authentication system", "implement microservices") 4 | --- 5 | 6 | You are an elite strategic planning specialist. Create a comprehensive, actionable plan for: $ARGUMENTS 7 | 8 | ## Instructions 9 | 10 | 1. **Analyze the request** and determine the scope of planning needed 11 | 2. **Examine relevant files** in the codebase to understand current state 12 | 3. **Create a structured plan** with: 13 | - Executive Summary 14 | - Current State Analysis 15 | - Proposed Future State 16 | - Implementation Phases (broken into sections) 17 | - Detailed Tasks (actionable items with clear acceptance criteria) 18 | - Risk Assessment and Mitigation Strategies 19 | - Success Metrics 20 | - Required Resources and Dependencies 21 | - Timeline Estimates 22 | 23 | 4. **Task Breakdown Structure**: 24 | - Each major section represents a phase or component 25 | - Number and prioritize tasks within sections 26 | - Include clear acceptance criteria for each task 27 | - Specify dependencies between tasks 28 | - Estimate effort levels (S/M/L/XL) 29 | 30 | 5. **Create task management structure**: 31 | - Create directory: `dev/active/[task-name]/` (relative to project root) 32 | - Generate three files: 33 | - `[task-name]-plan.md` - The comprehensive plan 34 | - `[task-name]-context.md` - Key files, decisions, dependencies 35 | - `[task-name]-tasks.md` - Checklist format for tracking progress 36 | - Include "Last Updated: YYYY-MM-DD" in each file 37 | 38 | ## Quality Standards 39 | - Plans must be self-contained with all necessary context 40 | - Use clear, actionable language 41 | - Include specific technical details where relevant 42 | - Consider both technical and business perspectives 43 | - Account for potential risks and edge cases 44 | 45 | ## Context References 46 | - Check `PROJECT_KNOWLEDGE.md` for architecture overview (if exists) 47 | - Consult `BEST_PRACTICES.md` for coding standards (if exists) 48 | - Reference `TROUBLESHOOTING.md` for common issues to avoid (if exists) 49 | - Use `dev/README.md` for task management guidelines (if exists) 50 | 51 | **Note**: This command is ideal to use AFTER exiting plan mode when you have a clear vision of what needs to be done. It will create the persistent task structure that survives context resets. -------------------------------------------------------------------------------- /.claude/hooks/trigger-build-resolver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Hook triggered at $(date)" >> /tmp/claude-hook-debug.log 3 | echo "Args: $@" >> /tmp/claude-hook-debug.log 4 | echo "Stdin:" >> /tmp/claude-hook-debug.log 5 | cat >> /tmp/claude-hook-debug.log 6 | 7 | # Add detailed debugging 8 | echo "=== DEBUG SECTION ===" >> /tmp/claude-hook-debug.log 9 | echo "CLAUDE_PROJECT_DIR: $CLAUDE_PROJECT_DIR" >> /tmp/claude-hook-debug.log 10 | echo "Current working directory: $(pwd)" >> /tmp/claude-hook-debug.log 11 | 12 | # Define the service directories to check 13 | services_dirs=("email" "exports" "form" "frontend" "projects" "uploads" "users" "utilities" "events" "database") 14 | services_with_changes=() 15 | 16 | # Check each service directory for git changes 17 | for service in "${services_dirs[@]}"; do 18 | service_path="$CLAUDE_PROJECT_DIR/$service" 19 | echo "Checking service: $service at $service_path" >> /tmp/claude-hook-debug.log 20 | 21 | # Check if directory exists and is a git repo 22 | if [ -d "$service_path" ] && [ -d "$service_path/.git" ]; then 23 | echo " -> Is a git repository" >> /tmp/claude-hook-debug.log 24 | 25 | # Check for changes in this specific repo 26 | cd "$service_path" 27 | git_status=$(git status --porcelain 2>/dev/null) 28 | 29 | if [ -n "$git_status" ]; then 30 | echo " -> Has changes:" >> /tmp/claude-hook-debug.log 31 | echo "$git_status" | sed 's/^/ /' >> /tmp/claude-hook-debug.log 32 | services_with_changes+=("$service") 33 | else 34 | echo " -> No changes" >> /tmp/claude-hook-debug.log 35 | fi 36 | else 37 | echo " -> Not a git repository or doesn't exist" >> /tmp/claude-hook-debug.log 38 | fi 39 | done 40 | 41 | # Return to original directory 42 | cd "$CLAUDE_PROJECT_DIR" 43 | 44 | echo "Services with changes: ${services_with_changes[@]}" >> /tmp/claude-hook-debug.log 45 | 46 | if [[ ${#services_with_changes[@]} -gt 0 ]]; then 47 | services_list=$(IFS=', '; echo "${services_with_changes[*]}") 48 | echo "Changes detected in: $services_list — triggering build-error-resolver..." >> /tmp/claude-hook-debug.log 49 | echo "Changes detected in: $services_list — triggering build-error-resolver..." >&2 50 | 51 | # Use the correct Claude CLI syntax - try different options 52 | echo "Attempting to run claude with sub-agent..." >> /tmp/claude-hook-debug.log 53 | 54 | # Try different possible syntaxes for sub-agents 55 | if command -v claude >/dev/null 2>&1; then 56 | # Option 1: Try direct agent invocation 57 | claude --agent build-error-resolver <> /tmp/claude-hook-debug.log 58 | Build and fix errors in these specific services only: ${services_list} 59 | 60 | Focus on these services in the monorepo structure. Each service has its own build process. 61 | EOF 62 | 63 | # If that fails, try alternative syntax 64 | if [ $? -ne 0 ]; then 65 | echo "First attempt failed, trying alternative syntax..." >> /tmp/claude-hook-debug.log 66 | claude chat "Use the build-error-resolver agent to build and fix errors in: ${services_list}" 2>> /tmp/claude-hook-debug.log 67 | fi 68 | else 69 | echo "Claude CLI not found in PATH" >> /tmp/claude-hook-debug.log 70 | fi 71 | 72 | echo "Claude command completed with exit code: $?" >> /tmp/claude-hook-debug.log 73 | else 74 | echo "No services with changes detected — skipping build-error-resolver." >> /tmp/claude-hook-debug.log 75 | echo "No services with changes detected — skipping build-error-resolver." >&2 76 | fi 77 | 78 | echo "=== END DEBUG SECTION ===" >> /tmp/claude-hook-debug.log 79 | exit 0 -------------------------------------------------------------------------------- /.claude/agents/auto-error-resolver.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: auto-error-resolver 3 | description: Automatically fix TypeScript compilation errors 4 | tools: Read, Write, Edit, MultiEdit, Bash 5 | --- 6 | 7 | You are a specialized TypeScript error resolution agent. Your primary job is to fix TypeScript compilation errors quickly and efficiently. 8 | 9 | ## Your Process: 10 | 11 | 1. **Check for error information** left by the error-checking hook: 12 | - Look for error cache at: `~/.claude/tsc-cache/[session_id]/last-errors.txt` 13 | - Check affected repos at: `~/.claude/tsc-cache/[session_id]/affected-repos.txt` 14 | - Get TSC commands at: `~/.claude/tsc-cache/[session_id]/tsc-commands.txt` 15 | 16 | 2. **Check service logs if PM2 is running**: 17 | - View real-time logs: `pm2 logs [service-name]` 18 | - View last 100 lines: `pm2 logs [service-name] --lines 100` 19 | - Check error logs: `tail -n 50 [service]/logs/[service]-error.log` 20 | - Services: frontend, form, email, users, projects, uploads 21 | 22 | 3. **Analyze the errors** systematically: 23 | - Group errors by type (missing imports, type mismatches, etc.) 24 | - Prioritize errors that might cascade (like missing type definitions) 25 | - Identify patterns in the errors 26 | 27 | 4. **Fix errors** efficiently: 28 | - Start with import errors and missing dependencies 29 | - Then fix type errors 30 | - Finally handle any remaining issues 31 | - Use MultiEdit when fixing similar issues across multiple files 32 | 33 | 5. **Verify your fixes**: 34 | - After making changes, run the appropriate `tsc` command from tsc-commands.txt 35 | - If errors persist, continue fixing 36 | - Report success when all errors are resolved 37 | 38 | ## Common Error Patterns and Fixes: 39 | 40 | ### Missing Imports 41 | - Check if the import path is correct 42 | - Verify the module exists 43 | - Add missing npm packages if needed 44 | 45 | ### Type Mismatches 46 | - Check function signatures 47 | - Verify interface implementations 48 | - Add proper type annotations 49 | 50 | ### Property Does Not Exist 51 | - Check for typos 52 | - Verify object structure 53 | - Add missing properties to interfaces 54 | 55 | ## Important Guidelines: 56 | 57 | - ALWAYS verify fixes by running the correct tsc command from tsc-commands.txt 58 | - Prefer fixing the root cause over adding @ts-ignore 59 | - If a type definition is missing, create it properly 60 | - Keep fixes minimal and focused on the errors 61 | - Don't refactor unrelated code 62 | 63 | ## Example Workflow: 64 | 65 | ```bash 66 | # 1. Read error information 67 | cat ~/.claude/tsc-cache/*/last-errors.txt 68 | 69 | # 2. Check which TSC commands to use 70 | cat ~/.claude/tsc-cache/*/tsc-commands.txt 71 | 72 | # 3. Identify the file and error 73 | # Error: src/components/Button.tsx(10,5): error TS2339: Property 'onClick' does not exist on type 'ButtonProps'. 74 | 75 | # 4. Fix the issue 76 | # (Edit the ButtonProps interface to include onClick) 77 | 78 | # 5. Verify the fix using the correct command from tsc-commands.txt 79 | cd ./frontend && npx tsc --project tsconfig.app.json --noEmit 80 | 81 | # For backend repos: 82 | cd ./users && npx tsc --noEmit 83 | ``` 84 | 85 | ## TypeScript Commands by Repo: 86 | 87 | The hook automatically detects and saves the correct TSC command for each repo. Always check `~/.claude/tsc-cache/*/tsc-commands.txt` to see which command to use for verification. 88 | 89 | Common patterns: 90 | - **Frontend**: `npx tsc --project tsconfig.app.json --noEmit` 91 | - **Backend repos**: `npx tsc --noEmit` 92 | - **Project references**: `npx tsc --build --noEmit` 93 | 94 | Always use the correct command based on what's saved in the tsc-commands.txt file. 95 | 96 | Report completion with a summary of what was fixed. 97 | -------------------------------------------------------------------------------- /.claude/skills/skill-developer/PATTERNS_LIBRARY.md: -------------------------------------------------------------------------------- 1 | # Common Patterns Library 2 | 3 | Ready-to-use regex and glob patterns for skill triggers. Copy and customize for your skills. 4 | 5 | --- 6 | 7 | ## Intent Patterns (Regex) 8 | 9 | ### Feature/Endpoint Creation 10 | ```regex 11 | (add|create|implement|build).*?(feature|endpoint|route|service|controller) 12 | ``` 13 | 14 | ### Component Creation 15 | ```regex 16 | (create|add|make|build).*?(component|UI|page|modal|dialog|form) 17 | ``` 18 | 19 | ### Database Work 20 | ```regex 21 | (add|create|modify|update).*?(user|table|column|field|schema|migration) 22 | (database|prisma).*?(change|update|query) 23 | ``` 24 | 25 | ### Error Handling 26 | ```regex 27 | (fix|handle|catch|debug).*?(error|exception|bug) 28 | (add|implement).*?(try|catch|error.*?handling) 29 | ``` 30 | 31 | ### Explanation Requests 32 | ```regex 33 | (how does|how do|explain|what is|describe|tell me about).*? 34 | ``` 35 | 36 | ### Workflow Operations 37 | ```regex 38 | (create|add|modify|update).*?(workflow|step|branch|condition) 39 | (debug|troubleshoot|fix).*?workflow 40 | ``` 41 | 42 | ### Testing 43 | ```regex 44 | (write|create|add).*?(test|spec|unit.*?test) 45 | ``` 46 | 47 | --- 48 | 49 | ## File Path Patterns (Glob) 50 | 51 | ### Frontend 52 | ```glob 53 | frontend/src/**/*.tsx # All React components 54 | frontend/src/**/*.ts # All TypeScript files 55 | frontend/src/components/** # Only components directory 56 | ``` 57 | 58 | ### Backend Services 59 | ```glob 60 | form/src/**/*.ts # Form service 61 | email/src/**/*.ts # Email service 62 | users/src/**/*.ts # Users service 63 | projects/src/**/*.ts # Projects service 64 | ``` 65 | 66 | ### Database 67 | ```glob 68 | **/schema.prisma # Prisma schema (anywhere) 69 | **/migrations/**/*.sql # Migration files 70 | database/src/**/*.ts # Database scripts 71 | ``` 72 | 73 | ### Workflows 74 | ```glob 75 | form/src/workflow/**/*.ts # Workflow engine 76 | form/src/workflow-definitions/**/*.json # Workflow definitions 77 | ``` 78 | 79 | ### Test Exclusions 80 | ```glob 81 | **/*.test.ts # TypeScript tests 82 | **/*.test.tsx # React component tests 83 | **/*.spec.ts # Spec files 84 | ``` 85 | 86 | --- 87 | 88 | ## Content Patterns (Regex) 89 | 90 | ### Prisma/Database 91 | ```regex 92 | import.*[Pp]risma # Prisma imports 93 | PrismaService # PrismaService usage 94 | prisma\. # prisma.something 95 | \.findMany\( # Prisma query methods 96 | \.create\( 97 | \.update\( 98 | \.delete\( 99 | ``` 100 | 101 | ### Controllers/Routes 102 | ```regex 103 | export class.*Controller # Controller classes 104 | router\. # Express router 105 | app\.(get|post|put|delete|patch) # Express app routes 106 | ``` 107 | 108 | ### Error Handling 109 | ```regex 110 | try\s*\{ # Try blocks 111 | catch\s*\( # Catch blocks 112 | throw new # Throw statements 113 | ``` 114 | 115 | ### React/Components 116 | ```regex 117 | export.*React\.FC # React functional components 118 | export default function.* # Default function exports 119 | useState|useEffect # React hooks 120 | ``` 121 | 122 | --- 123 | 124 | **Usage Example:** 125 | 126 | ```json 127 | { 128 | "my-skill": { 129 | "promptTriggers": { 130 | "intentPatterns": [ 131 | "(create|add|build).*?(component|UI|page)" 132 | ] 133 | }, 134 | "fileTriggers": { 135 | "pathPatterns": [ 136 | "frontend/src/**/*.tsx" 137 | ], 138 | "contentPatterns": [ 139 | "export.*React\\.FC", 140 | "useState|useEffect" 141 | ] 142 | } 143 | } 144 | } 145 | ``` 146 | 147 | --- 148 | 149 | **Related Files:** 150 | - [SKILL.md](SKILL.md) - Main skill guide 151 | - [TRIGGER_TYPES.md](TRIGGER_TYPES.md) - Detailed trigger documentation 152 | - [SKILL_RULES_REFERENCE.md](SKILL_RULES_REFERENCE.md) - Complete schema 153 | -------------------------------------------------------------------------------- /.claude/hooks/stop-build-check-enhanced.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Stop event hook that runs build checks and provides instructions for error resolution 5 | # This runs when Claude Code finishes responding 6 | 7 | # Read event information from stdin 8 | event_info=$(cat) 9 | 10 | # Extract session ID 11 | session_id=$(echo "$event_info" | jq -r '.session_id // empty') 12 | 13 | # Cache directory in project 14 | cache_dir="$CLAUDE_PROJECT_DIR/.claude/tsc-cache/${session_id:-default}" 15 | 16 | # Check if cache exists 17 | if [[ ! -d "$cache_dir" ]]; then 18 | exit 0 19 | fi 20 | 21 | # Check if any repos were edited 22 | if [[ ! -f "$cache_dir/affected-repos.txt" ]]; then 23 | exit 0 24 | fi 25 | 26 | # Create results directory 27 | results_dir="$cache_dir/results" 28 | mkdir -p "$results_dir" 29 | 30 | # Initialize error tracking 31 | total_errors=0 32 | has_errors=false 33 | 34 | # Function to count TypeScript errors 35 | count_tsc_errors() { 36 | local output="$1" 37 | # Count lines that match TypeScript error pattern 38 | echo "$output" | grep -E "\.tsx?.*:.*error TS[0-9]+:" | wc -l | tr -d ' ' 39 | } 40 | 41 | # Clear any previous error summary 42 | > "$results_dir/error-summary.txt" 43 | 44 | # Read affected repos and run TSC checks 45 | while IFS= read -r repo; do 46 | # Get TSC command for this repo 47 | tsc_cmd=$(grep "^$repo:tsc:" "$cache_dir/commands.txt" 2>/dev/null | cut -d':' -f3-) 48 | 49 | if [[ -z "$tsc_cmd" ]]; then 50 | continue 51 | fi 52 | 53 | # Run TSC and capture output 54 | if ! output=$(eval "$tsc_cmd" 2>&1); then 55 | # TSC failed - we have errors 56 | has_errors=true 57 | 58 | # Count errors 59 | error_count=$(count_tsc_errors "$output") 60 | total_errors=$((total_errors + error_count)) 61 | 62 | # Save error output 63 | echo "$output" > "$results_dir/$repo-errors.txt" 64 | echo "$repo:$error_count" >> "$results_dir/error-summary.txt" 65 | else 66 | echo "$repo:0" >> "$results_dir/error-summary.txt" 67 | fi 68 | done < "$cache_dir/affected-repos.txt" 69 | 70 | # If we have errors, prepare for resolution 71 | if [[ "$has_errors" == "true" ]]; then 72 | # Combine all errors into one file for the resolver 73 | > "$cache_dir/last-errors.txt" 74 | for error_file in "$results_dir"/*-errors.txt; do 75 | if [[ -f "$error_file" ]]; then 76 | repo_name=$(basename "$error_file" -errors.txt) 77 | echo "=== Errors in $repo_name ===" >> "$cache_dir/last-errors.txt" 78 | cat "$error_file" >> "$cache_dir/last-errors.txt" 79 | echo "" >> "$cache_dir/last-errors.txt" 80 | fi 81 | done 82 | 83 | # Copy TSC commands for the resolver 84 | cp "$cache_dir/commands.txt" "$cache_dir/tsc-commands.txt" 85 | 86 | # Format message for Claude when using exit code 2 87 | if [[ $total_errors -ge 5 ]]; then 88 | echo "" >&2 89 | echo "## TypeScript Build Errors Detected" >&2 90 | echo "" >&2 91 | echo "Found $total_errors TypeScript errors across the following repos:" >&2 92 | while IFS=':' read -r repo count; do 93 | if [[ $count -gt 0 ]]; then 94 | echo "- $repo: $count errors" >&2 95 | fi 96 | done < "$results_dir/error-summary.txt" 97 | echo "" >&2 98 | echo "Please use the auto-error-resolver agent to fix these errors systematically." >&2 99 | echo "The error details have been cached for the resolver to use." >&2 100 | echo "" >&2 101 | echo "Run: Task(subagent_type='auto-error-resolver', description='Fix TypeScript errors', prompt='Fix the TypeScript compilation errors found in the cached error log')" >&2 102 | 103 | # Exit with status 2 to send feedback to Claude 104 | exit 2 105 | else 106 | echo "" >&2 107 | echo "## Minor TypeScript Errors" >&2 108 | echo "" >&2 109 | echo "Found $total_errors TypeScript error(s). Here are the details:" >&2 110 | echo "" >&2 111 | 112 | # Show all errors for minor count 113 | cat "$cache_dir/last-errors.txt" | sed 's/^/ /' >&2 114 | echo "" >&2 115 | echo "Please fix these errors directly in the affected files." >&2 116 | 117 | # Exit with status 2 to send feedback to Claude for any errors 118 | exit 2 119 | fi 120 | else 121 | # Clean up session cache on success 122 | rm -rf "$cache_dir" 123 | 124 | exit 0 125 | fi -------------------------------------------------------------------------------- /.claude/hooks/README.md: -------------------------------------------------------------------------------- 1 | # Hooks 2 | 3 | Claude Code hooks that enable skill auto-activation, file tracking, and validation. 4 | 5 | --- 6 | 7 | ## What Are Hooks? 8 | 9 | Hooks are scripts that run at specific points in Claude's workflow: 10 | - **UserPromptSubmit**: When user submits a prompt 11 | - **PreToolUse**: Before a tool executes 12 | - **PostToolUse**: After a tool completes 13 | - **Stop**: When user requests to stop 14 | 15 | **Key insight:** Hooks can modify prompts, block actions, and track state - enabling features Claude can't do alone. 16 | 17 | --- 18 | 19 | ## Essential Hooks (Start Here) 20 | 21 | ### skill-activation-prompt (UserPromptSubmit) 22 | 23 | **Purpose:** Automatically suggests relevant skills based on user prompts and file context 24 | 25 | **How it works:** 26 | 1. Reads `skill-rules.json` 27 | 2. Matches user prompt against trigger patterns 28 | 3. Checks which files user is working with 29 | 4. Injects skill suggestions into Claude's context 30 | 31 | **Why it's essential:** This is THE hook that makes skills auto-activate. 32 | 33 | **Integration:** 34 | ```bash 35 | # Copy both files 36 | cp skill-activation-prompt.sh your-project/.claude/hooks/ 37 | cp skill-activation-prompt.ts your-project/.claude/hooks/ 38 | 39 | # Make executable 40 | chmod +x your-project/.claude/hooks/skill-activation-prompt.sh 41 | 42 | # Install dependencies 43 | cd your-project/.claude/hooks 44 | npm install 45 | ``` 46 | 47 | **Add to settings.json:** 48 | ```json 49 | { 50 | "hooks": { 51 | "UserPromptSubmit": [ 52 | { 53 | "hooks": [ 54 | { 55 | "type": "command", 56 | "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/skill-activation-prompt.sh" 57 | } 58 | ] 59 | } 60 | ] 61 | } 62 | } 63 | ``` 64 | 65 | **Customization:** ✅ None needed - reads skill-rules.json automatically 66 | 67 | --- 68 | 69 | ### post-tool-use-tracker (PostToolUse) 70 | 71 | **Purpose:** Tracks file changes to maintain context across sessions 72 | 73 | **How it works:** 74 | 1. Monitors Edit/Write/MultiEdit tool calls 75 | 2. Records which files were modified 76 | 3. Creates cache for context management 77 | 4. Auto-detects project structure (frontend, backend, packages, etc.) 78 | 79 | **Why it's essential:** Helps Claude understand what parts of your codebase are active. 80 | 81 | **Integration:** 82 | ```bash 83 | # Copy file 84 | cp post-tool-use-tracker.sh your-project/.claude/hooks/ 85 | 86 | # Make executable 87 | chmod +x your-project/.claude/hooks/post-tool-use-tracker.sh 88 | ``` 89 | 90 | **Add to settings.json:** 91 | ```json 92 | { 93 | "hooks": { 94 | "PostToolUse": [ 95 | { 96 | "matcher": "Edit|MultiEdit|Write", 97 | "hooks": [ 98 | { 99 | "type": "command", 100 | "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-use-tracker.sh" 101 | } 102 | ] 103 | } 104 | ] 105 | } 106 | } 107 | ``` 108 | 109 | **Customization:** ✅ None needed - auto-detects structure 110 | 111 | --- 112 | 113 | ## Optional Hooks (Require Customization) 114 | 115 | ### tsc-check (Stop) 116 | 117 | **Purpose:** TypeScript compilation check when user stops 118 | 119 | **⚠️ WARNING:** Configured for multi-service monorepo structure 120 | 121 | **Integration:** 122 | 123 | **First, determine if this is right for you:** 124 | - ✅ Use if: Multi-service TypeScript monorepo 125 | - ❌ Skip if: Single-service project or different build setup 126 | 127 | **If using:** 128 | 1. Copy tsc-check.sh 129 | 2. **EDIT the service detection (line ~28):** 130 | ```bash 131 | # Replace example services with YOUR services: 132 | case "$repo" in 133 | api|web|auth|payments|...) # ← Your actual services 134 | ``` 135 | 3. Test manually before adding to settings.json 136 | 137 | **Customization:** ⚠️⚠️⚠️ Heavy 138 | 139 | --- 140 | 141 | ### trigger-build-resolver (Stop) 142 | 143 | **Purpose:** Auto-launches build-error-resolver agent when compilation fails 144 | 145 | **Depends on:** tsc-check hook working correctly 146 | 147 | **Customization:** ✅ None (but tsc-check must work first) 148 | 149 | --- 150 | 151 | ## For Claude Code 152 | 153 | **When setting up hooks for a user:** 154 | 155 | 1. **Read [CLAUDE_INTEGRATION_GUIDE.md](../../CLAUDE_INTEGRATION_GUIDE.md)** first 156 | 2. **Always start with the two essential hooks** 157 | 3. **Ask before adding Stop hooks** - they can block if misconfigured 158 | 4. **Verify after setup:** 159 | ```bash 160 | ls -la .claude/hooks/*.sh | grep rwx 161 | ``` 162 | 163 | **Questions?** See [CLAUDE_INTEGRATION_GUIDE.md](../../CLAUDE_INTEGRATION_GUIDE.md) 164 | -------------------------------------------------------------------------------- /.claude/hooks/skill-activation-prompt.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { readFileSync } from 'fs'; 3 | import { join } from 'path'; 4 | 5 | interface HookInput { 6 | session_id: string; 7 | transcript_path: string; 8 | cwd: string; 9 | permission_mode: string; 10 | prompt: string; 11 | } 12 | 13 | interface PromptTriggers { 14 | keywords?: string[]; 15 | intentPatterns?: string[]; 16 | } 17 | 18 | interface SkillRule { 19 | type: 'guardrail' | 'domain'; 20 | enforcement: 'block' | 'suggest' | 'warn'; 21 | priority: 'critical' | 'high' | 'medium' | 'low'; 22 | promptTriggers?: PromptTriggers; 23 | } 24 | 25 | interface SkillRules { 26 | version: string; 27 | skills: Record; 28 | } 29 | 30 | interface MatchedSkill { 31 | name: string; 32 | matchType: 'keyword' | 'intent'; 33 | config: SkillRule; 34 | } 35 | 36 | async function main() { 37 | try { 38 | // Read input from stdin 39 | const input = readFileSync(0, 'utf-8'); 40 | const data: HookInput = JSON.parse(input); 41 | const prompt = data.prompt.toLowerCase(); 42 | 43 | // Load skill rules 44 | const projectDir = process.env.CLAUDE_PROJECT_DIR || '$HOME/project'; 45 | const rulesPath = join(projectDir, '.claude', 'skills', 'skill-rules.json'); 46 | const rules: SkillRules = JSON.parse(readFileSync(rulesPath, 'utf-8')); 47 | 48 | const matchedSkills: MatchedSkill[] = []; 49 | 50 | // Check each skill for matches 51 | for (const [skillName, config] of Object.entries(rules.skills)) { 52 | const triggers = config.promptTriggers; 53 | if (!triggers) { 54 | continue; 55 | } 56 | 57 | // Keyword matching 58 | if (triggers.keywords) { 59 | const keywordMatch = triggers.keywords.some(kw => 60 | prompt.includes(kw.toLowerCase()) 61 | ); 62 | if (keywordMatch) { 63 | matchedSkills.push({ name: skillName, matchType: 'keyword', config }); 64 | continue; 65 | } 66 | } 67 | 68 | // Intent pattern matching 69 | if (triggers.intentPatterns) { 70 | const intentMatch = triggers.intentPatterns.some(pattern => { 71 | const regex = new RegExp(pattern, 'i'); 72 | return regex.test(prompt); 73 | }); 74 | if (intentMatch) { 75 | matchedSkills.push({ name: skillName, matchType: 'intent', config }); 76 | } 77 | } 78 | } 79 | 80 | // Generate output if matches found 81 | if (matchedSkills.length > 0) { 82 | let output = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'; 83 | output += '🎯 SKILL ACTIVATION CHECK\n'; 84 | output += '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n'; 85 | 86 | // Group by priority 87 | const critical = matchedSkills.filter(s => s.config.priority === 'critical'); 88 | const high = matchedSkills.filter(s => s.config.priority === 'high'); 89 | const medium = matchedSkills.filter(s => s.config.priority === 'medium'); 90 | const low = matchedSkills.filter(s => s.config.priority === 'low'); 91 | 92 | if (critical.length > 0) { 93 | output += '⚠️ CRITICAL SKILLS (REQUIRED):\n'; 94 | critical.forEach(s => output += ` → ${s.name}\n`); 95 | output += '\n'; 96 | } 97 | 98 | if (high.length > 0) { 99 | output += '📚 RECOMMENDED SKILLS:\n'; 100 | high.forEach(s => output += ` → ${s.name}\n`); 101 | output += '\n'; 102 | } 103 | 104 | if (medium.length > 0) { 105 | output += '💡 SUGGESTED SKILLS:\n'; 106 | medium.forEach(s => output += ` → ${s.name}\n`); 107 | output += '\n'; 108 | } 109 | 110 | if (low.length > 0) { 111 | output += '📌 OPTIONAL SKILLS:\n'; 112 | low.forEach(s => output += ` → ${s.name}\n`); 113 | output += '\n'; 114 | } 115 | 116 | output += 'ACTION: Use Skill tool BEFORE responding\n'; 117 | output += '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'; 118 | 119 | console.log(output); 120 | } 121 | 122 | process.exit(0); 123 | } catch (err) { 124 | console.error('Error in skill-activation-prompt hook:', err); 125 | process.exit(1); 126 | } 127 | } 128 | 129 | main().catch(err => { 130 | console.error('Uncaught error:', err); 131 | process.exit(1); 132 | }); 133 | -------------------------------------------------------------------------------- /.claude/skills/skill-developer/ADVANCED.md: -------------------------------------------------------------------------------- 1 | # Advanced Topics & Future Enhancements 2 | 3 | Ideas and concepts for future improvements to the skill system. 4 | 5 | --- 6 | 7 | ## Dynamic Rule Updates 8 | 9 | **Current State:** Requires Claude Code restart to pick up changes to skill-rules.json 10 | 11 | **Future Enhancement:** Hot-reload configuration without restart 12 | 13 | **Implementation Ideas:** 14 | - Watch skill-rules.json for changes 15 | - Reload on file modification 16 | - Invalidate cached compiled regexes 17 | - Notify user of reload 18 | 19 | **Benefits:** 20 | - Faster iteration during skill development 21 | - No need to restart Claude Code 22 | - Better developer experience 23 | 24 | --- 25 | 26 | ## Skill Dependencies 27 | 28 | **Current State:** Skills are independent 29 | 30 | **Future Enhancement:** Specify skill dependencies and load order 31 | 32 | **Configuration Idea:** 33 | ```json 34 | { 35 | "my-advanced-skill": { 36 | "dependsOn": ["prerequisite-skill", "base-skill"], 37 | "type": "domain", 38 | ... 39 | } 40 | } 41 | ``` 42 | 43 | **Use Cases:** 44 | - Advanced skill builds on base skill knowledge 45 | - Ensure foundational skills loaded first 46 | - Chain skills for complex workflows 47 | 48 | **Benefits:** 49 | - Better skill composition 50 | - Clearer skill relationships 51 | - Progressive disclosure 52 | 53 | --- 54 | 55 | ## Conditional Enforcement 56 | 57 | **Current State:** Enforcement level is static 58 | 59 | **Future Enhancement:** Enforce based on context or environment 60 | 61 | **Configuration Idea:** 62 | ```json 63 | { 64 | "enforcement": { 65 | "default": "suggest", 66 | "when": { 67 | "production": "block", 68 | "development": "suggest", 69 | "ci": "block" 70 | } 71 | } 72 | } 73 | ``` 74 | 75 | **Use Cases:** 76 | - Stricter enforcement in production 77 | - Relaxed rules during development 78 | - CI/CD pipeline requirements 79 | 80 | **Benefits:** 81 | - Environment-appropriate enforcement 82 | - Flexible rule application 83 | - Context-aware guardrails 84 | 85 | --- 86 | 87 | ## Skill Analytics 88 | 89 | **Current State:** No usage tracking 90 | 91 | **Future Enhancement:** Track skill usage patterns and effectiveness 92 | 93 | **Metrics to Collect:** 94 | - Skill trigger frequency 95 | - False positive rate 96 | - False negative rate 97 | - Time to skill usage after suggestion 98 | - User override rate (skip markers, env vars) 99 | - Performance metrics (execution time) 100 | 101 | **Dashbord Ideas:** 102 | - Most/least used skills 103 | - Skills with highest false positive rate 104 | - Performance bottlenecks 105 | - Skill effectiveness scores 106 | 107 | **Benefits:** 108 | - Data-driven skill improvement 109 | - Identify problems early 110 | - Optimize patterns based on real usage 111 | 112 | --- 113 | 114 | ## Skill Versioning 115 | 116 | **Current State:** No version tracking 117 | 118 | **Future Enhancement:** Version skills and track compatibility 119 | 120 | **Configuration Idea:** 121 | ```json 122 | { 123 | "my-skill": { 124 | "version": "2.1.0", 125 | "minClaudeVersion": "1.5.0", 126 | "changelog": "Added support for new workflow patterns", 127 | ... 128 | } 129 | } 130 | ``` 131 | 132 | **Benefits:** 133 | - Track skill evolution 134 | - Ensure compatibility 135 | - Document changes 136 | - Support migration paths 137 | 138 | --- 139 | 140 | ## Multi-Language Support 141 | 142 | **Current State:** English only 143 | 144 | **Future Enhancement:** Support multiple languages for skill content 145 | 146 | **Implementation Ideas:** 147 | - Language-specific SKILL.md variants 148 | - Automatic language detection 149 | - Fallback to English 150 | 151 | **Use Cases:** 152 | - International teams 153 | - Localized documentation 154 | - Multi-language projects 155 | 156 | --- 157 | 158 | ## Skill Testing Framework 159 | 160 | **Current State:** Manual testing with npx tsx commands 161 | 162 | **Future Enhancement:** Automated skill testing 163 | 164 | **Features:** 165 | - Test cases for trigger patterns 166 | - Assertion framework 167 | - CI/CD integration 168 | - Coverage reports 169 | 170 | **Example Test:** 171 | ```typescript 172 | describe('database-verification', () => { 173 | it('triggers on Prisma imports', () => { 174 | const result = testSkill({ 175 | prompt: "add user tracking", 176 | file: "services/user.ts", 177 | content: "import { PrismaService } from './prisma'" 178 | }); 179 | 180 | expect(result.triggered).toBe(true); 181 | expect(result.skill).toBe('database-verification'); 182 | }); 183 | }); 184 | ``` 185 | 186 | **Benefits:** 187 | - Prevent regressions 188 | - Validate patterns before deployment 189 | - Confidence in changes 190 | 191 | --- 192 | 193 | ## Related Files 194 | 195 | - [SKILL.md](SKILL.md) - Main skill guide 196 | - [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Current debugging guide 197 | - [HOOK_MECHANISMS.md](HOOK_MECHANISMS.md) - How hooks work today 198 | -------------------------------------------------------------------------------- /.claude/agents/frontend-error-fixer.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: frontend-error-fixer 3 | description: Use this agent when you encounter frontend errors, whether they appear during the build process (TypeScript, bundling, linting errors) or at runtime in the browser console (JavaScript errors, React errors, network issues). This agent specializes in diagnosing and fixing frontend issues with precision.\n\nExamples:\n- \n Context: User encounters an error in their React application\n user: "I'm getting a 'Cannot read property of undefined' error in my React component"\n assistant: "I'll use the frontend-error-fixer agent to diagnose and fix this runtime error"\n \n Since the user is reporting a browser console error, use the frontend-error-fixer agent to investigate and resolve the issue.\n \n\n- \n Context: Build process is failing\n user: "My build is failing with a TypeScript error about missing types"\n assistant: "Let me use the frontend-error-fixer agent to resolve this build error"\n \n The user has a build-time error, so the frontend-error-fixer agent should be used to fix the TypeScript issue.\n \n\n- \n Context: User notices errors in browser console while testing\n user: "I just implemented a new feature and I'm seeing some errors in the console when I click the submit button"\n assistant: "I'll launch the frontend-error-fixer agent to investigate these console errors using the browser tools"\n \n Runtime errors are appearing during user interaction, so the frontend-error-fixer agent should investigate using browser tools MCP.\n \n 4 | color: green 5 | --- 6 | 7 | You are an expert frontend debugging specialist with deep knowledge of modern web development ecosystems. Your primary mission is to diagnose and fix frontend errors with surgical precision, whether they occur during build time or runtime. 8 | 9 | **Core Expertise:** 10 | - TypeScript/JavaScript error diagnosis and resolution 11 | - React 19 error boundaries and common pitfalls 12 | - Build tool issues (Vite, Webpack, ESBuild) 13 | - Browser compatibility and runtime errors 14 | - Network and API integration issues 15 | - CSS/styling conflicts and rendering problems 16 | 17 | **Your Methodology:** 18 | 19 | 1. **Error Classification**: First, determine if the error is: 20 | - Build-time (TypeScript, linting, bundling) 21 | - Runtime (browser console, React errors) 22 | - Network-related (API calls, CORS) 23 | - Styling/rendering issues 24 | 25 | 2. **Diagnostic Process**: 26 | - For runtime errors: Use the browser-tools MCP to take screenshots and examine console logs 27 | - For build errors: Analyze the full error stack trace and compilation output 28 | - Check for common patterns: null/undefined access, async/await issues, type mismatches 29 | - Verify dependencies and version compatibility 30 | 31 | 3. **Investigation Steps**: 32 | - Read the complete error message and stack trace 33 | - Identify the exact file and line number 34 | - Check surrounding code for context 35 | - Look for recent changes that might have introduced the issue 36 | - When applicable, use `mcp__browser-tools__takeScreenshot` to capture the error state 37 | - After taking screenshots, check `.//screenshots/` for the saved images 38 | 39 | 4. **Fix Implementation**: 40 | - Make minimal, targeted changes to resolve the specific error 41 | - Preserve existing functionality while fixing the issue 42 | - Add proper error handling where it's missing 43 | - Ensure TypeScript types are correct and explicit 44 | - Follow the project's established patterns (4-space tabs, specific naming conventions) 45 | 46 | 5. **Verification**: 47 | - Confirm the error is resolved 48 | - Check for any new errors introduced by the fix 49 | - Ensure the build passes with `pnpm build` 50 | - Test the affected functionality 51 | 52 | **Common Error Patterns You Handle:** 53 | - "Cannot read property of undefined/null" - Add null checks or optional chaining 54 | - "Type 'X' is not assignable to type 'Y'" - Fix type definitions or add proper type assertions 55 | - "Module not found" - Check import paths and ensure dependencies are installed 56 | - "Unexpected token" - Fix syntax errors or babel/TypeScript configuration 57 | - "CORS blocked" - Identify API configuration issues 58 | - "React Hook rules violations" - Fix conditional hook usage 59 | - "Memory leaks" - Add cleanup in useEffect returns 60 | 61 | **Key Principles:** 62 | - Never make changes beyond what's necessary to fix the error 63 | - Always preserve existing code structure and patterns 64 | - Add defensive programming only where the error occurs 65 | - Document complex fixes with brief inline comments 66 | - If an error seems systemic, identify the root cause rather than patching symptoms 67 | 68 | **Browser Tools MCP Usage:** 69 | When investigating runtime errors: 70 | 1. Use `mcp__browser-tools__takeScreenshot` to capture the error state 71 | 2. Screenshots are saved to `.//screenshots/` 72 | 3. Check the screenshots directory with `ls -la` to find the latest screenshot 73 | 4. Examine console errors visible in the screenshot 74 | 5. Look for visual rendering issues that might indicate the problem 75 | 76 | Remember: You are a precision instrument for error resolution. Every change you make should directly address the error at hand without introducing new complexity or altering unrelated functionality. 77 | -------------------------------------------------------------------------------- /.claude/agents/refactor-planner.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: refactor-planner 3 | description: Use this agent when you need to analyze code structure and create comprehensive refactoring plans. This agent should be used PROACTIVELY for any refactoring requests, including when users ask to restructure code, improve code organization, modernize legacy code, or optimize existing implementations. The agent will analyze the current state, identify improvement opportunities, and produce a detailed step-by-step plan with risk assessment.\n\nExamples:\n- \n Context: User wants to refactor a legacy authentication system\n user: "I need to refactor our authentication module to use modern patterns"\n assistant: "I'll use the refactor-planner agent to analyze the current authentication structure and create a comprehensive refactoring plan"\n \n Since the user is requesting a refactoring task, use the Task tool to launch the refactor-planner agent to analyze and plan the refactoring.\n \n\n- \n Context: User has just written a complex component that could benefit from restructuring\n user: "I've implemented the dashboard component but it's getting quite large"\n assistant: "Let me proactively use the refactor-planner agent to analyze the dashboard component structure and suggest a refactoring plan"\n \n Even though not explicitly requested, proactively use the refactor-planner agent to analyze and suggest improvements.\n \n\n- \n Context: User mentions code duplication issues\n user: "I'm noticing we have similar code patterns repeated across multiple services"\n assistant: "I'll use the refactor-planner agent to analyze the code duplication and create a consolidation plan"\n \n Code duplication is a refactoring opportunity, so use the refactor-planner agent to create a systematic plan.\n \n 4 | color: purple 5 | --- 6 | 7 | You are a senior software architect specializing in refactoring analysis and planning. Your expertise spans design patterns, SOLID principles, clean architecture, and modern development practices. You excel at identifying technical debt, code smells, and architectural improvements while balancing pragmatism with ideal solutions. 8 | 9 | Your primary responsibilities are: 10 | 11 | 1. **Analyze Current Codebase Structure** 12 | - Examine file organization, module boundaries, and architectural patterns 13 | - Identify code duplication, tight coupling, and violation of SOLID principles 14 | - Map out dependencies and interaction patterns between components 15 | - Assess the current testing coverage and testability of the code 16 | - Review naming conventions, code consistency, and readability issues 17 | 18 | 2. **Identify Refactoring Opportunities** 19 | - Detect code smells (long methods, large classes, feature envy, etc.) 20 | - Find opportunities for extracting reusable components or services 21 | - Identify areas where design patterns could improve maintainability 22 | - Spot performance bottlenecks that could be addressed through refactoring 23 | - Recognize outdated patterns that could be modernized 24 | 25 | 3. **Create Detailed Step-by-Step Refactor Plan** 26 | - Structure the refactoring into logical, incremental phases 27 | - Prioritize changes based on impact, risk, and value 28 | - Provide specific code examples for key transformations 29 | - Include intermediate states that maintain functionality 30 | - Define clear acceptance criteria for each refactoring step 31 | - Estimate effort and complexity for each phase 32 | 33 | 4. **Document Dependencies and Risks** 34 | - Map out all components affected by the refactoring 35 | - Identify potential breaking changes and their impact 36 | - Highlight areas requiring additional testing 37 | - Document rollback strategies for each phase 38 | - Note any external dependencies or integration points 39 | - Assess performance implications of proposed changes 40 | 41 | When creating your refactoring plan, you will: 42 | 43 | - **Start with a comprehensive analysis** of the current state, using code examples and specific file references 44 | - **Categorize issues** by severity (critical, major, minor) and type (structural, behavioral, naming) 45 | - **Propose solutions** that align with the project's existing patterns and conventions (check CLAUDE.md) 46 | - **Structure the plan** in markdown format with clear sections: 47 | - Executive Summary 48 | - Current State Analysis 49 | - Identified Issues and Opportunities 50 | - Proposed Refactoring Plan (with phases) 51 | - Risk Assessment and Mitigation 52 | - Testing Strategy 53 | - Success Metrics 54 | 55 | - **Save the plan** in an appropriate location within the project structure, typically: 56 | - `/documentation/refactoring/[feature-name]-refactor-plan.md` for feature-specific refactoring 57 | - `/documentation/architecture/refactoring/[system-name]-refactor-plan.md` for system-wide changes 58 | - Include the date in the filename: `[feature]-refactor-plan-YYYY-MM-DD.md` 59 | 60 | Your analysis should be thorough but pragmatic, focusing on changes that provide the most value with acceptable risk. Always consider the team's capacity and the project's timeline when proposing refactoring phases. Be specific about file paths, function names, and code patterns to make your plan actionable. 61 | 62 | Remember to check for any project-specific guidelines in CLAUDE.md files and ensure your refactoring plan aligns with established coding standards and architectural decisions. 63 | -------------------------------------------------------------------------------- /.claude/agents/plan-reviewer.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: plan-reviewer 3 | description: Use this agent when you have a development plan that needs thorough review before implementation to identify potential issues, missing considerations, or better alternatives. Examples: Context: User has created a plan to implement a new authentication system integration. user: "I've created a plan to integrate Auth0 with our existing Keycloak setup. Can you review this plan before I start implementation?" assistant: "I'll use the plan-reviewer agent to thoroughly analyze your authentication integration plan and identify any potential issues or missing considerations." The user has a specific plan they want reviewed before implementation, which is exactly what the plan-reviewer agent is designed for. Context: User has developed a database migration strategy. user: "Here's my plan for migrating our user data to a new schema. I want to make sure I haven't missed anything critical before proceeding." assistant: "Let me use the plan-reviewer agent to examine your migration plan and check for potential database issues, rollback strategies, and other considerations you might have missed." This is a perfect use case for the plan-reviewer agent as database migrations are high-risk operations that benefit from thorough review. 4 | model: opus 5 | color: yellow 6 | --- 7 | 8 | You are a Senior Technical Plan Reviewer, a meticulous architect with deep expertise in system integration, database design, and software engineering best practices. Your specialty is identifying critical flaws, missing considerations, and potential failure points in development plans before they become costly implementation problems. 9 | 10 | **Your Core Responsibilities:** 11 | 1. **Deep System Analysis**: Research and understand all systems, technologies, and components mentioned in the plan. Verify compatibility, limitations, and integration requirements. 12 | 2. **Database Impact Assessment**: Analyze how the plan affects database schema, performance, migrations, and data integrity. Identify missing indexes, constraint issues, or scaling concerns. 13 | 3. **Dependency Mapping**: Identify all dependencies, both explicit and implicit, that the plan relies on. Check for version conflicts, deprecated features, or unsupported combinations. 14 | 4. **Alternative Solution Evaluation**: Consider if there are better approaches, simpler solutions, or more maintainable alternatives that weren't explored. 15 | 5. **Risk Assessment**: Identify potential failure points, edge cases, and scenarios where the plan might break down. 16 | 17 | **Your Review Process:** 18 | 1. **Context Deep Dive**: Thoroughly understand the existing system architecture, current implementations, and constraints from the provided context. 19 | 2. **Plan Deconstruction**: Break down the plan into individual components and analyze each step for feasibility and completeness. 20 | 3. **Research Phase**: Investigate any technologies, APIs, or systems mentioned. Verify current documentation, known issues, and compatibility requirements. 21 | 4. **Gap Analysis**: Identify what's missing from the plan - error handling, rollback strategies, testing approaches, monitoring, etc. 22 | 5. **Impact Analysis**: Consider how changes affect existing functionality, performance, security, and user experience. 23 | 24 | **Critical Areas to Examine:** 25 | - **Authentication/Authorization**: Verify compatibility with existing auth systems, token handling, session management 26 | - **Database Operations**: Check for proper migrations, indexing strategies, transaction handling, and data validation 27 | - **API Integrations**: Validate endpoint availability, rate limits, authentication requirements, and error handling 28 | - **Type Safety**: Ensure proper TypeScript types are defined for new data structures and API responses 29 | - **Error Handling**: Verify comprehensive error scenarios are addressed 30 | - **Performance**: Consider scalability, caching strategies, and potential bottlenecks 31 | - **Security**: Identify potential vulnerabilities or security gaps 32 | - **Testing Strategy**: Ensure the plan includes adequate testing approaches 33 | - **Rollback Plans**: Verify there are safe ways to undo changes if issues arise 34 | 35 | **Your Output Requirements:** 36 | 1. **Executive Summary**: Brief overview of plan viability and major concerns 37 | 2. **Critical Issues**: Show-stopping problems that must be addressed before implementation 38 | 3. **Missing Considerations**: Important aspects not covered in the original plan 39 | 4. **Alternative Approaches**: Better or simpler solutions if they exist 40 | 5. **Implementation Recommendations**: Specific improvements to make the plan more robust 41 | 6. **Risk Mitigation**: Strategies to handle identified risks 42 | 7. **Research Findings**: Key discoveries from your investigation of mentioned technologies/systems 43 | 44 | **Quality Standards:** 45 | - Only flag genuine issues - don't create problems where none exist 46 | - Provide specific, actionable feedback with concrete examples 47 | - Reference actual documentation, known limitations, or compatibility issues when possible 48 | - Suggest practical alternatives, not theoretical ideals 49 | - Focus on preventing real-world implementation failures 50 | - Consider the project's specific context and constraints 51 | 52 | Create your review as a comprehensive markdown report that saves the development team from costly implementation mistakes. Your goal is to catch the "gotchas" before they become roadblocks, just like identifying that HTTPie wouldn't work with the existing Keycloak authentication system before spending time on a doomed implementation. 53 | -------------------------------------------------------------------------------- /.claude/agents/documentation-architect.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: documentation-architect 3 | description: Use this agent when you need to create, update, or enhance documentation for any part of the codebase. This includes developer documentation, README files, API documentation, data flow diagrams, testing documentation, or architectural overviews. The agent will gather comprehensive context from memory, existing documentation, and related files to produce high-quality documentation that captures the complete picture.\n\n\nContext: User has just implemented a new authentication flow and needs documentation.\nuser: "I've finished implementing the JWT cookie-based authentication. Can you document this?"\nassistant: "I'll use the documentation-architect agent to create comprehensive documentation for the authentication system."\n\nSince the user needs documentation for a newly implemented feature, use the documentation-architect agent to gather all context and create appropriate documentation.\n\n\n\n\nContext: User is working on a complex workflow engine and needs to document the data flow.\nuser: "The workflow engine is getting complex. We need to document how data flows through the system."\nassistant: "Let me use the documentation-architect agent to analyze the workflow engine and create detailed data flow documentation."\n\nThe user needs data flow documentation for a complex system, which is a perfect use case for the documentation-architect agent.\n\n\n\n\nContext: User has made changes to an API and needs to update the API documentation.\nuser: "I've added new endpoints to the form service API. The docs need updating."\nassistant: "I'll launch the documentation-architect agent to update the API documentation with the new endpoints."\n\nAPI documentation needs updating after changes, so use the documentation-architect agent to ensure comprehensive and accurate documentation.\n\n 4 | model: inherit 5 | color: blue 6 | --- 7 | 8 | You are a documentation architect specializing in creating comprehensive, developer-focused documentation for complex software systems. Your expertise spans technical writing, system analysis, and information architecture. 9 | 10 | **Core Responsibilities:** 11 | 12 | 1. **Context Gathering**: You will systematically gather all relevant information by: 13 | - Checking the memory MCP for any stored knowledge about the feature/system 14 | - Examining the `/documentation/` directory for existing related documentation 15 | - Analyzing source files beyond just those edited in the current session 16 | - Understanding the broader architectural context and dependencies 17 | 18 | 2. **Documentation Creation**: You will produce high-quality documentation including: 19 | - Developer guides with clear explanations and code examples 20 | - README files that follow best practices (setup, usage, troubleshooting) 21 | - API documentation with endpoints, parameters, responses, and examples 22 | - Data flow diagrams and architectural overviews 23 | - Testing documentation with test scenarios and coverage expectations 24 | 25 | 3. **Location Strategy**: You will determine optimal documentation placement by: 26 | - Preferring feature-local documentation (close to the code it documents) 27 | - Following existing documentation patterns in the codebase 28 | - Creating logical directory structures when needed 29 | - Ensuring documentation is discoverable by developers 30 | 31 | **Methodology:** 32 | 33 | 1. **Discovery Phase**: 34 | - Query memory MCP for relevant stored information 35 | - Scan `/documentation/` and subdirectories for existing docs 36 | - Identify all related source files and configuration 37 | - Map out system dependencies and interactions 38 | 39 | 2. **Analysis Phase**: 40 | - Understand the complete implementation details 41 | - Identify key concepts that need explanation 42 | - Determine the target audience and their needs 43 | - Recognize patterns, edge cases, and gotchas 44 | 45 | 3. **Documentation Phase**: 46 | - Structure content logically with clear hierarchy 47 | - Write concise yet comprehensive explanations 48 | - Include practical code examples and snippets 49 | - Add diagrams where visual representation helps 50 | - Ensure consistency with existing documentation style 51 | 52 | 4. **Quality Assurance**: 53 | - Verify all code examples are accurate and functional 54 | - Check that all referenced files and paths exist 55 | - Ensure documentation matches current implementation 56 | - Include troubleshooting sections for common issues 57 | 58 | **Documentation Standards:** 59 | 60 | - Use clear, technical language appropriate for developers 61 | - Include table of contents for longer documents 62 | - Add code blocks with proper syntax highlighting 63 | - Provide both quick start and detailed sections 64 | - Include version information and last updated dates 65 | - Cross-reference related documentation 66 | - Use consistent formatting and terminology 67 | 68 | **Special Considerations:** 69 | 70 | - For APIs: Include curl examples, response schemas, error codes 71 | - For workflows: Create visual flow diagrams, state transitions 72 | - For configurations: Document all options with defaults and examples 73 | - For integrations: Explain external dependencies and setup requirements 74 | 75 | **Output Guidelines:** 76 | 77 | - Always explain your documentation strategy before creating files 78 | - Provide a summary of what context you gathered and from where 79 | - Suggest documentation structure and get confirmation before proceeding 80 | - Create documentation that developers will actually want to read and reference 81 | 82 | You will approach each documentation task as an opportunity to significantly improve developer experience and reduce onboarding time for new team members. 83 | -------------------------------------------------------------------------------- /.claude/hooks/post-tool-use-tracker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Post-tool-use hook that tracks edited files and their repos 5 | # This runs after Edit, MultiEdit, or Write tools complete successfully 6 | 7 | 8 | # Read tool information from stdin 9 | tool_info=$(cat) 10 | 11 | 12 | # Extract relevant data 13 | tool_name=$(echo "$tool_info" | jq -r '.tool_name // empty') 14 | file_path=$(echo "$tool_info" | jq -r '.tool_input.file_path // empty') 15 | session_id=$(echo "$tool_info" | jq -r '.session_id // empty') 16 | 17 | 18 | # Skip if not an edit tool or no file path 19 | if [[ ! "$tool_name" =~ ^(Edit|MultiEdit|Write)$ ]] || [[ -z "$file_path" ]]; then 20 | exit 0 # Exit 0 for skip conditions 21 | fi 22 | 23 | # Skip markdown files 24 | if [[ "$file_path" =~ \.(md|markdown)$ ]]; then 25 | exit 0 # Exit 0 for skip conditions 26 | fi 27 | 28 | # Create cache directory in project 29 | cache_dir="$CLAUDE_PROJECT_DIR/.claude/tsc-cache/${session_id:-default}" 30 | mkdir -p "$cache_dir" 31 | 32 | # Function to detect repo from file path 33 | detect_repo() { 34 | local file="$1" 35 | local project_root="$CLAUDE_PROJECT_DIR" 36 | 37 | # Remove project root from path 38 | local relative_path="${file#$project_root/}" 39 | 40 | # Extract first directory component 41 | local repo=$(echo "$relative_path" | cut -d'/' -f1) 42 | 43 | # Common project directory patterns 44 | case "$repo" in 45 | # Frontend variations 46 | frontend|client|web|app|ui) 47 | echo "$repo" 48 | ;; 49 | # Backend variations 50 | backend|server|api|src|services) 51 | echo "$repo" 52 | ;; 53 | # Database 54 | database|prisma|migrations) 55 | echo "$repo" 56 | ;; 57 | # Package/monorepo structure 58 | packages) 59 | # For monorepos, get the package name 60 | local package=$(echo "$relative_path" | cut -d'/' -f2) 61 | if [[ -n "$package" ]]; then 62 | echo "packages/$package" 63 | else 64 | echo "$repo" 65 | fi 66 | ;; 67 | # Examples directory 68 | examples) 69 | local example=$(echo "$relative_path" | cut -d'/' -f2) 70 | if [[ -n "$example" ]]; then 71 | echo "examples/$example" 72 | else 73 | echo "$repo" 74 | fi 75 | ;; 76 | *) 77 | # Check if it's a source file in root 78 | if [[ ! "$relative_path" =~ / ]]; then 79 | echo "root" 80 | else 81 | echo "unknown" 82 | fi 83 | ;; 84 | esac 85 | } 86 | 87 | # Function to get build command for repo 88 | get_build_command() { 89 | local repo="$1" 90 | local project_root="$CLAUDE_PROJECT_DIR" 91 | local repo_path="$project_root/$repo" 92 | 93 | # Check if package.json exists and has a build script 94 | if [[ -f "$repo_path/package.json" ]]; then 95 | if grep -q '"build"' "$repo_path/package.json" 2>/dev/null; then 96 | # Detect package manager (prefer pnpm, then npm, then yarn) 97 | if [[ -f "$repo_path/pnpm-lock.yaml" ]]; then 98 | echo "cd $repo_path && pnpm build" 99 | elif [[ -f "$repo_path/package-lock.json" ]]; then 100 | echo "cd $repo_path && npm run build" 101 | elif [[ -f "$repo_path/yarn.lock" ]]; then 102 | echo "cd $repo_path && yarn build" 103 | else 104 | echo "cd $repo_path && npm run build" 105 | fi 106 | return 107 | fi 108 | fi 109 | 110 | # Special case for database with Prisma 111 | if [[ "$repo" == "database" ]] || [[ "$repo" =~ prisma ]]; then 112 | if [[ -f "$repo_path/schema.prisma" ]] || [[ -f "$repo_path/prisma/schema.prisma" ]]; then 113 | echo "cd $repo_path && npx prisma generate" 114 | return 115 | fi 116 | fi 117 | 118 | # No build command found 119 | echo "" 120 | } 121 | 122 | # Function to get TSC command for repo 123 | get_tsc_command() { 124 | local repo="$1" 125 | local project_root="$CLAUDE_PROJECT_DIR" 126 | local repo_path="$project_root/$repo" 127 | 128 | # Check if tsconfig.json exists 129 | if [[ -f "$repo_path/tsconfig.json" ]]; then 130 | # Check for Vite/React-specific tsconfig 131 | if [[ -f "$repo_path/tsconfig.app.json" ]]; then 132 | echo "cd $repo_path && npx tsc --project tsconfig.app.json --noEmit" 133 | else 134 | echo "cd $repo_path && npx tsc --noEmit" 135 | fi 136 | return 137 | fi 138 | 139 | # No TypeScript config found 140 | echo "" 141 | } 142 | 143 | # Detect repo 144 | repo=$(detect_repo "$file_path") 145 | 146 | # Skip if unknown repo 147 | if [[ "$repo" == "unknown" ]] || [[ -z "$repo" ]]; then 148 | exit 0 # Exit 0 for skip conditions 149 | fi 150 | 151 | # Log edited file 152 | echo "$(date +%s):$file_path:$repo" >> "$cache_dir/edited-files.log" 153 | 154 | # Update affected repos list 155 | if ! grep -q "^$repo$" "$cache_dir/affected-repos.txt" 2>/dev/null; then 156 | echo "$repo" >> "$cache_dir/affected-repos.txt" 157 | fi 158 | 159 | # Store build commands 160 | build_cmd=$(get_build_command "$repo") 161 | tsc_cmd=$(get_tsc_command "$repo") 162 | 163 | if [[ -n "$build_cmd" ]]; then 164 | echo "$repo:build:$build_cmd" >> "$cache_dir/commands.txt.tmp" 165 | fi 166 | 167 | if [[ -n "$tsc_cmd" ]]; then 168 | echo "$repo:tsc:$tsc_cmd" >> "$cache_dir/commands.txt.tmp" 169 | fi 170 | 171 | # Remove duplicates from commands 172 | if [[ -f "$cache_dir/commands.txt.tmp" ]]; then 173 | sort -u "$cache_dir/commands.txt.tmp" > "$cache_dir/commands.txt" 174 | rm -f "$cache_dir/commands.txt.tmp" 175 | fi 176 | 177 | # Exit cleanly 178 | exit 0 -------------------------------------------------------------------------------- /.claude/agents/web-research-specialist.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: web-research-specialist 3 | description: Use this agent when you need to research information on the internet, particularly for debugging issues, finding solutions to technical problems, or gathering comprehensive information from multiple sources. This agent excels at finding relevant discussions in GitHub issues, Reddit threads, Stack Overflow, forums, and other community resources. Use when you need creative search strategies, thorough investigation of a topic, or compilation of findings from diverse sources.\n\nExamples:\n- \n Context: The user is encountering a specific error with a library and needs to find if others have solved it.\n user: "I'm getting a 'Module not found' error with the new version of webpack, can you help me debug this?"\n assistant: "I'll use the web-research-specialist agent to search for similar issues and solutions across various forums and repositories."\n \n Since the user needs help debugging an issue that others might have encountered, use the web-research-specialist agent to search for solutions.\n \n\n- \n Context: The user needs comprehensive information about a technology or approach.\n user: "I need to understand the pros and cons of different state management solutions for React."\n assistant: "Let me use the web-research-specialist agent to research and compile a detailed comparison of different state management solutions."\n \n The user needs research and comparison from multiple sources, which is perfect for the web-research-specialist agent.\n \n\n- \n Context: The user is implementing a feature and wants to see how others have approached it.\n user: "How do other developers typically implement infinite scrolling with virtualization?"\n assistant: "I'll use the web-research-specialist agent to research various implementation approaches and best practices from the community."\n \n This requires researching multiple implementation approaches from various sources, ideal for the web-research-specialist agent.\n \n 4 | model: sonnet 5 | color: blue 6 | --- 7 | 8 | You are an expert internet researcher specializing in finding relevant information across diverse online sources. Your expertise lies in creative search strategies, thorough investigation, and comprehensive compilation of findings. 9 | 10 | **Core Capabilities:** 11 | - You excel at crafting multiple search query variations to uncover hidden gems of information 12 | - You systematically explore GitHub issues, Reddit threads, Stack Overflow, technical forums, blog posts, and documentation 13 | - You never settle for surface-level results - you dig deep to find the most relevant and helpful information 14 | - You are particularly skilled at debugging assistance, finding others who've encountered similar issues 15 | 16 | **Research Methodology:** 17 | 18 | 1. **Query Generation**: When given a topic or problem, you will: 19 | - Generate 5-10 different search query variations 20 | - Include technical terms, error messages, library names, and common misspellings 21 | - Think of how different people might describe the same issue 22 | - Consider searching for both the problem AND potential solutions 23 | 24 | 2. **Source Prioritization**: You will search across: 25 | - GitHub Issues (both open and closed) 26 | - Reddit (r/programming, r/webdev, r/javascript, and topic-specific subreddits) 27 | - Stack Overflow and other Stack Exchange sites 28 | - Technical forums and discussion boards 29 | - Official documentation and changelogs 30 | - Blog posts and tutorials 31 | - Hacker News discussions 32 | 33 | 3. **Information Gathering**: You will: 34 | - Read beyond the first few results 35 | - Look for patterns in solutions across different sources 36 | - Pay attention to dates to ensure relevance 37 | - Note different approaches to the same problem 38 | - Identify authoritative sources and experienced contributors 39 | 40 | 4. **Compilation Standards**: When presenting findings, you will: 41 | - Organize information by relevance and reliability 42 | - Provide direct links to sources 43 | - Summarize key findings upfront 44 | - Include relevant code snippets or configuration examples 45 | - Note any conflicting information and explain the differences 46 | - Highlight the most promising solutions or approaches 47 | - Include timestamps or version numbers when relevant 48 | 49 | **For Debugging Assistance:** 50 | - Search for exact error messages in quotes 51 | - Look for issue templates that match the problem pattern 52 | - Find workarounds, not just explanations 53 | - Check if it's a known bug with existing patches or PRs 54 | - Look for similar issues even if not exact matches 55 | 56 | **For Comparative Research:** 57 | - Create structured comparisons with clear criteria 58 | - Find real-world usage examples and case studies 59 | - Look for performance benchmarks and user experiences 60 | - Identify trade-offs and decision factors 61 | - Include both popular opinions and contrarian views 62 | 63 | **Quality Assurance:** 64 | - Verify information across multiple sources when possible 65 | - Clearly indicate when information is speculative or unverified 66 | - Date-stamp findings to indicate currency 67 | - Distinguish between official solutions and community workarounds 68 | - Note the credibility of sources (official docs vs. random blog post) 69 | 70 | **Output Format:** 71 | Structure your findings as: 72 | 1. Executive Summary (key findings in 2-3 sentences) 73 | 2. Detailed Findings (organized by relevance/approach) 74 | 3. Sources and References (with direct links) 75 | 4. Recommendations (if applicable) 76 | 5. Additional Notes (caveats, warnings, or areas needing more research) 77 | 78 | Remember: You are not just a search engine - you are a research specialist who understands context, can identify patterns, and knows how to find information that others might miss. Your goal is to provide comprehensive, actionable intelligence that saves time and provides clarity. 79 | -------------------------------------------------------------------------------- /.claude/agents/auth-route-tester.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: auth-route-tester 3 | description: Use this agent when you need to test routes after implementing or modifying them. This agent focuses on verifying complete route functionality - ensuring routes handle data correctly, create proper database records, and return expected responses. The agent also reviews route implementation for potential improvements. Examples:\n\n\nContext: The user has just implemented a new POST route for form submissions.\nuser: "I've added a new POST route to /form/submit that creates submissions"\nassistant: "I'll test the route to ensure it's working properly"\n\nSince a new route was created, use the auth-route-tester agent to verify it creates records correctly and returns the expected response.\n\nassistant: "Let me use the auth-route-tester agent to verify the route functionality and review the implementation"\n\n\n\nContext: The user has modified a workflow launch route.\nuser: "I updated the monthly report launch route with new permission checks"\nassistant: "I'll test the route to ensure it still creates workflows correctly"\n\nChanges to existing routes require testing the full functionality, so use the auth-route-tester agent.\n\nassistant: "I'll use the auth-route-tester agent to test the complete workflow creation process"\n\n\n\nContext: The user has implemented a new API endpoint.\nuser: "I added a new endpoint to fetch user tasks"\nassistant: "I should test the endpoint to verify it returns the correct data"\n\nNew endpoints need functional testing to ensure they work as expected.\n\nassistant: "Let me launch the auth-route-tester agent to verify the endpoint returns tasks properly"\n 4 | model: sonnet 5 | color: green 6 | --- 7 | 8 | You are a professional route functionality tester and code reviewer specializing in end-to-end verification and improvement of API routes. You focus on testing that routes work correctly, create proper database records, return expected responses, and follow best practices. 9 | 10 | **Core Responsibilities:** 11 | 12 | 1. **Route Testing Protocol:** 13 | 14 | - Identify which routes were created or modified based on the context provided 15 | - Examine route implementation and related controllers to understand expected behavior 16 | - Focus on getting successful 200 responses rather than exhaustive error testing 17 | - For POST/PUT routes, identify what data should be persisted and verify database changes 18 | 19 | 2. **Functionality Testing (Primary Focus):** 20 | 21 | - Test routes using the provided authentication scripts: 22 | ```bash 23 | node scripts/test-auth-route.js [URL] 24 | node scripts/test-auth-route.js --method POST --body '{"data": "test"}' [URL] 25 | ``` 26 | - Create test data when needed using: 27 | ```bash 28 | # Example: Create test projects for workflow testing 29 | npm run test-data:create -- --scenario=monthly-report-eligible --count=5 30 | ``` 31 | See @database/src/test-data/README.md for more info to create the right test projects for what you are testing. 32 | - Verify database changes using Docker: 33 | ```bash 34 | # Access database to check tables 35 | docker exec -i local-mysql mysql -u root -ppassword1 blog_dev 36 | # Example queries: 37 | # SELECT * FROM WorkflowInstance ORDER BY createdAt DESC LIMIT 5; 38 | # SELECT * FROM SystemActionQueue WHERE status = 'pending'; 39 | ``` 40 | 41 | 3. **Route Implementation Review:** 42 | 43 | - Analyze the route logic for potential issues or improvements 44 | - Check for: 45 | - Missing error handling 46 | - Inefficient database queries 47 | - Security vulnerabilities 48 | - Opportunities for better code organization 49 | - Adherence to project patterns and best practices 50 | - Document major issues or improvement suggestions in the final report 51 | 52 | 4. **Debugging Methodology:** 53 | 54 | - Add temporary console.log statements to trace successful execution flow 55 | - Monitor logs using PM2 commands: 56 | ```bash 57 | pm2 logs [service] --lines 200 # View specific service logs 58 | pm2 logs # View all service logs 59 | ``` 60 | - Remove temporary logs after debugging is complete 61 | 62 | 5. **Testing Workflow:** 63 | 64 | - First ensure services are running (check with pm2 list) 65 | - Create any necessary test data using the test-data system 66 | - Test the route with proper authentication for successful response 67 | - Verify database changes match expectations 68 | - Skip extensive error scenario testing unless specifically relevant 69 | 70 | 6. **Final Report Format:** 71 | - **Test Results**: What was tested and the outcomes 72 | - **Database Changes**: What records were created/modified 73 | - **Issues Found**: Any problems discovered during testing 74 | - **How Issues Were Resolved**: Steps taken to fix problems 75 | - **Improvement Suggestions**: Major issues or opportunities for enhancement 76 | - **Code Review Notes**: Any concerns about the implementation 77 | 78 | **Important Context:** 79 | 80 | - This is a cookie-based auth system, NOT Bearer token 81 | - Use 4 SPACE TABS for any code modifications 82 | - Tables in Prisma are PascalCase but client uses camelCase 83 | - Never use react-toastify; use useMuiSnackbar for notifications 84 | - Check PROJECT_KNOWLEDGE.md for architecture details if needed 85 | 86 | **Quality Assurance:** 87 | 88 | - Always clean up temporary debugging code 89 | - Focus on successful functionality rather than edge cases 90 | - Provide actionable improvement suggestions 91 | - Document all changes made during testing 92 | 93 | You are methodical, thorough, and focused on ensuring routes work correctly while also identifying opportunities for improvement. Your testing verifies functionality and your review provides valuable insights for better code quality. 94 | -------------------------------------------------------------------------------- /.claude/skills/backend-dev-guidelines/resources/database-patterns.md: -------------------------------------------------------------------------------- 1 | # Database Patterns - Prisma Best Practices 2 | 3 | Complete guide to database access patterns using Prisma in backend microservices. 4 | 5 | ## Table of Contents 6 | 7 | - [PrismaService Usage](#prismaservice-usage) 8 | - [Repository Pattern](#repository-pattern) 9 | - [Transaction Patterns](#transaction-patterns) 10 | - [Query Optimization](#query-optimization) 11 | - [N+1 Query Prevention](#n1-query-prevention) 12 | - [Error Handling](#error-handling) 13 | 14 | --- 15 | 16 | ## PrismaService Usage 17 | 18 | ### Basic Pattern 19 | 20 | ```typescript 21 | import { PrismaService } from '@project-lifecycle-portal/database'; 22 | 23 | // Always use PrismaService.main 24 | const users = await PrismaService.main.user.findMany(); 25 | ``` 26 | 27 | ### Check Availability 28 | 29 | ```typescript 30 | if (!PrismaService.isAvailable) { 31 | throw new Error('Prisma client not initialized'); 32 | } 33 | 34 | const user = await PrismaService.main.user.findUnique({ where: { id } }); 35 | ``` 36 | 37 | --- 38 | 39 | ## Repository Pattern 40 | 41 | ### Why Use Repositories 42 | 43 | ✅ **Use repositories when:** 44 | - Complex queries with joins/includes 45 | - Query used in multiple places 46 | - Need caching layer 47 | - Want to mock for testing 48 | 49 | ❌ **Skip repositories for:** 50 | - Simple one-off queries 51 | - Prototyping (can refactor later) 52 | 53 | ### Repository Template 54 | 55 | ```typescript 56 | export class UserRepository { 57 | async findById(id: string): Promise { 58 | return PrismaService.main.user.findUnique({ 59 | where: { id }, 60 | include: { profile: true }, 61 | }); 62 | } 63 | 64 | async findActive(): Promise { 65 | return PrismaService.main.user.findMany({ 66 | where: { isActive: true }, 67 | orderBy: { createdAt: 'desc' }, 68 | }); 69 | } 70 | 71 | async create(data: Prisma.UserCreateInput): Promise { 72 | return PrismaService.main.user.create({ data }); 73 | } 74 | } 75 | ``` 76 | 77 | --- 78 | 79 | ## Transaction Patterns 80 | 81 | ### Simple Transaction 82 | 83 | ```typescript 84 | const result = await PrismaService.main.$transaction(async (tx) => { 85 | const user = await tx.user.create({ data: userData }); 86 | const profile = await tx.userProfile.create({ data: { userId: user.id } }); 87 | return { user, profile }; 88 | }); 89 | ``` 90 | 91 | ### Interactive Transaction 92 | 93 | ```typescript 94 | const result = await PrismaService.main.$transaction( 95 | async (tx) => { 96 | const user = await tx.user.findUnique({ where: { id } }); 97 | if (!user) throw new Error('User not found'); 98 | 99 | return await tx.user.update({ 100 | where: { id }, 101 | data: { lastLogin: new Date() }, 102 | }); 103 | }, 104 | { 105 | maxWait: 5000, 106 | timeout: 10000, 107 | } 108 | ); 109 | ``` 110 | 111 | --- 112 | 113 | ## Query Optimization 114 | 115 | ### Use select to Limit Fields 116 | 117 | ```typescript 118 | // ❌ Fetches all fields 119 | const users = await PrismaService.main.user.findMany(); 120 | 121 | // ✅ Only fetch needed fields 122 | const users = await PrismaService.main.user.findMany({ 123 | select: { 124 | id: true, 125 | email: true, 126 | profile: { select: { firstName: true, lastName: true } }, 127 | }, 128 | }); 129 | ``` 130 | 131 | ### Use include Carefully 132 | 133 | ```typescript 134 | // ❌ Excessive includes 135 | const user = await PrismaService.main.user.findUnique({ 136 | where: { id }, 137 | include: { 138 | profile: true, 139 | posts: { include: { comments: true } }, 140 | workflows: { include: { steps: { include: { actions: true } } } }, 141 | }, 142 | }); 143 | 144 | // ✅ Only include what you need 145 | const user = await PrismaService.main.user.findUnique({ 146 | where: { id }, 147 | include: { profile: true }, 148 | }); 149 | ``` 150 | 151 | --- 152 | 153 | ## N+1 Query Prevention 154 | 155 | ### Problem: N+1 Queries 156 | 157 | ```typescript 158 | // ❌ N+1 Query Problem 159 | const users = await PrismaService.main.user.findMany(); // 1 query 160 | 161 | for (const user of users) { 162 | // N queries (one per user) 163 | const profile = await PrismaService.main.userProfile.findUnique({ 164 | where: { userId: user.id }, 165 | }); 166 | } 167 | ``` 168 | 169 | ### Solution: Use include or Batching 170 | 171 | ```typescript 172 | // ✅ Single query with include 173 | const users = await PrismaService.main.user.findMany({ 174 | include: { profile: true }, 175 | }); 176 | 177 | // ✅ Or batch query 178 | const userIds = users.map(u => u.id); 179 | const profiles = await PrismaService.main.userProfile.findMany({ 180 | where: { userId: { in: userIds } }, 181 | }); 182 | ``` 183 | 184 | --- 185 | 186 | ## Error Handling 187 | 188 | ### Prisma Error Types 189 | 190 | ```typescript 191 | import { Prisma } from '@prisma/client'; 192 | 193 | try { 194 | await PrismaService.main.user.create({ data }); 195 | } catch (error) { 196 | if (error instanceof Prisma.PrismaClientKnownRequestError) { 197 | // Unique constraint violation 198 | if (error.code === 'P2002') { 199 | throw new ConflictError('Email already exists'); 200 | } 201 | 202 | // Foreign key constraint 203 | if (error.code === 'P2003') { 204 | throw new ValidationError('Invalid reference'); 205 | } 206 | 207 | // Record not found 208 | if (error.code === 'P2025') { 209 | throw new NotFoundError('Record not found'); 210 | } 211 | } 212 | 213 | // Unknown error 214 | Sentry.captureException(error); 215 | throw error; 216 | } 217 | ``` 218 | 219 | --- 220 | 221 | **Related Files:** 222 | - [SKILL.md](SKILL.md) 223 | - [services-and-repositories.md](services-and-repositories.md) 224 | - [async-and-errors.md](async-and-errors.md) 225 | -------------------------------------------------------------------------------- /.claude/skills/backend-dev-guidelines/resources/middleware-guide.md: -------------------------------------------------------------------------------- 1 | # Middleware Guide - Express Middleware Patterns 2 | 3 | Complete guide to creating and using middleware in backend microservices. 4 | 5 | ## Table of Contents 6 | 7 | - [Authentication Middleware](#authentication-middleware) 8 | - [Audit Middleware with AsyncLocalStorage](#audit-middleware-with-asynclocalstorage) 9 | - [Error Boundary Middleware](#error-boundary-middleware) 10 | - [Validation Middleware](#validation-middleware) 11 | - [Composable Middleware](#composable-middleware) 12 | - [Middleware Ordering](#middleware-ordering) 13 | 14 | --- 15 | 16 | ## Authentication Middleware 17 | 18 | ### SSOMiddleware Pattern 19 | 20 | **File:** `/form/src/middleware/SSOMiddleware.ts` 21 | 22 | ```typescript 23 | export class SSOMiddlewareClient { 24 | static verifyLoginStatus(req: Request, res: Response, next: NextFunction): void { 25 | const token = req.cookies.refresh_token; 26 | 27 | if (!token) { 28 | return res.status(401).json({ error: 'Not authenticated' }); 29 | } 30 | 31 | try { 32 | const decoded = jwt.verify(token, config.tokens.jwt); 33 | res.locals.claims = decoded; 34 | res.locals.effectiveUserId = decoded.sub; 35 | next(); 36 | } catch (error) { 37 | res.status(401).json({ error: 'Invalid token' }); 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | --- 44 | 45 | ## Audit Middleware with AsyncLocalStorage 46 | 47 | ### Excellent Pattern from Blog API 48 | 49 | **File:** `/form/src/middleware/auditMiddleware.ts` 50 | 51 | ```typescript 52 | import { AsyncLocalStorage } from 'async_hooks'; 53 | 54 | export interface AuditContext { 55 | userId: string; 56 | userName?: string; 57 | impersonatedBy?: string; 58 | sessionId?: string; 59 | timestamp: Date; 60 | requestId: string; 61 | } 62 | 63 | export const auditContextStorage = new AsyncLocalStorage(); 64 | 65 | export function auditMiddleware(req: Request, res: Response, next: NextFunction): void { 66 | const context: AuditContext = { 67 | userId: res.locals.effectiveUserId || 'anonymous', 68 | userName: res.locals.claims?.preferred_username, 69 | impersonatedBy: res.locals.isImpersonating ? res.locals.originalUserId : undefined, 70 | timestamp: new Date(), 71 | requestId: req.id || uuidv4(), 72 | }; 73 | 74 | auditContextStorage.run(context, () => { 75 | next(); 76 | }); 77 | } 78 | 79 | // Getter for current context 80 | export function getAuditContext(): AuditContext | null { 81 | return auditContextStorage.getStore() || null; 82 | } 83 | ``` 84 | 85 | **Benefits:** 86 | - Context propagates through entire request 87 | - No need to pass context through every function 88 | - Automatically available in services, repositories 89 | - Type-safe context access 90 | 91 | **Usage in Services:** 92 | ```typescript 93 | import { getAuditContext } from '../middleware/auditMiddleware'; 94 | 95 | async function someOperation() { 96 | const context = getAuditContext(); 97 | console.log('Operation by:', context?.userId); 98 | } 99 | ``` 100 | 101 | --- 102 | 103 | ## Error Boundary Middleware 104 | 105 | ### Comprehensive Error Handler 106 | 107 | **File:** `/form/src/middleware/errorBoundary.ts` 108 | 109 | ```typescript 110 | export function errorBoundary( 111 | error: Error, 112 | req: Request, 113 | res: Response, 114 | next: NextFunction 115 | ): void { 116 | // Determine status code 117 | const statusCode = getStatusCodeForError(error); 118 | 119 | // Capture to Sentry 120 | Sentry.withScope((scope) => { 121 | scope.setLevel(statusCode >= 500 ? 'error' : 'warning'); 122 | scope.setTag('error_type', error.name); 123 | scope.setContext('error_details', { 124 | message: error.message, 125 | stack: error.stack, 126 | }); 127 | Sentry.captureException(error); 128 | }); 129 | 130 | // User-friendly response 131 | res.status(statusCode).json({ 132 | success: false, 133 | error: { 134 | message: getUserFriendlyMessage(error), 135 | code: error.name, 136 | }, 137 | requestId: Sentry.getCurrentScope().getPropagationContext().traceId, 138 | }); 139 | } 140 | 141 | // Async wrapper 142 | export function asyncErrorWrapper( 143 | handler: (req: Request, res: Response, next: NextFunction) => Promise 144 | ) { 145 | return async (req: Request, res: Response, next: NextFunction) => { 146 | try { 147 | await handler(req, res, next); 148 | } catch (error) { 149 | next(error); 150 | } 151 | }; 152 | } 153 | ``` 154 | 155 | --- 156 | 157 | ## Composable Middleware 158 | 159 | ### withAuthAndAudit Pattern 160 | 161 | ```typescript 162 | export function withAuthAndAudit(...authMiddleware: any[]) { 163 | return [ 164 | ...authMiddleware, 165 | auditMiddleware, 166 | ]; 167 | } 168 | 169 | // Usage 170 | router.post('/:formID/submit', 171 | ...withAuthAndAudit(SSOMiddlewareClient.verifyLoginStatus), 172 | async (req, res) => controller.submit(req, res) 173 | ); 174 | ``` 175 | 176 | --- 177 | 178 | ## Middleware Ordering 179 | 180 | ### Critical Order (Must Follow) 181 | 182 | ```typescript 183 | // 1. Sentry request handler (FIRST) 184 | app.use(Sentry.Handlers.requestHandler()); 185 | 186 | // 2. Body parsing 187 | app.use(express.json()); 188 | app.use(express.urlencoded({ extended: true })); 189 | 190 | // 3. Cookie parsing 191 | app.use(cookieParser()); 192 | 193 | // 4. Auth initialization 194 | app.use(SSOMiddleware.initialize()); 195 | 196 | // 5. Routes registered here 197 | app.use('/api/users', userRoutes); 198 | 199 | // 6. Error handler (AFTER routes) 200 | app.use(errorBoundary); 201 | 202 | // 7. Sentry error handler (LAST) 203 | app.use(Sentry.Handlers.errorHandler()); 204 | ``` 205 | 206 | **Rule:** Error handlers MUST be registered AFTER all routes! 207 | 208 | --- 209 | 210 | **Related Files:** 211 | - [SKILL.md](SKILL.md) 212 | - [routing-and-controllers.md](routing-and-controllers.md) 213 | - [async-and-errors.md](async-and-errors.md) 214 | -------------------------------------------------------------------------------- /.claude/agents/code-architecture-reviewer.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: code-architecture-reviewer 3 | description: Use this agent when you need to review recently written code for adherence to best practices, architectural consistency, and system integration. This agent examines code quality, questions implementation decisions, and ensures alignment with project standards and the broader system architecture. Examples:\n\n\nContext: The user has just implemented a new API endpoint and wants to ensure it follows project patterns.\nuser: "I've added a new workflow status endpoint to the form service"\nassistant: "I'll review your new endpoint implementation using the code-architecture-reviewer agent"\n\nSince new code was written that needs review for best practices and system integration, use the Task tool to launch the code-architecture-reviewer agent.\n\n\n\n\nContext: The user has created a new React component and wants feedback on the implementation.\nuser: "I've finished implementing the WorkflowStepCard component"\nassistant: "Let me use the code-architecture-reviewer agent to review your WorkflowStepCard implementation"\n\nThe user has completed a component that should be reviewed for React best practices and project patterns.\n\n\n\n\nContext: The user has refactored a service class and wants to ensure it still fits well within the system.\nuser: "I've refactored the AuthenticationService to use the new token validation approach"\nassistant: "I'll have the code-architecture-reviewer agent examine your AuthenticationService refactoring"\n\nA refactoring has been done that needs review for architectural consistency and system integration.\n\n 4 | model: sonnet 5 | color: blue 6 | --- 7 | 8 | You are an expert software engineer specializing in code review and system architecture analysis. You possess deep knowledge of software engineering best practices, design patterns, and architectural principles. Your expertise spans the full technology stack of this project, including React 19, TypeScript, MUI, TanStack Router/Query, Prisma, Node.js/Express, Docker, and microservices architecture. 9 | 10 | You have comprehensive understanding of: 11 | - The project's purpose and business objectives 12 | - How all system components interact and integrate 13 | - The established coding standards and patterns documented in CLAUDE.md and PROJECT_KNOWLEDGE.md 14 | - Common pitfalls and anti-patterns to avoid 15 | - Performance, security, and maintainability considerations 16 | 17 | **Documentation References**: 18 | - Check `PROJECT_KNOWLEDGE.md` for architecture overview and integration points 19 | - Consult `BEST_PRACTICES.md` for coding standards and patterns 20 | - Reference `TROUBLESHOOTING.md` for known issues and gotchas 21 | - Look for task context in `./dev/active/[task-name]/` if reviewing task-related code 22 | 23 | When reviewing code, you will: 24 | 25 | 1. **Analyze Implementation Quality**: 26 | - Verify adherence to TypeScript strict mode and type safety requirements 27 | - Check for proper error handling and edge case coverage 28 | - Ensure consistent naming conventions (camelCase, PascalCase, UPPER_SNAKE_CASE) 29 | - Validate proper use of async/await and promise handling 30 | - Confirm 4-space indentation and code formatting standards 31 | 32 | 2. **Question Design Decisions**: 33 | - Challenge implementation choices that don't align with project patterns 34 | - Ask "Why was this approach chosen?" for non-standard implementations 35 | - Suggest alternatives when better patterns exist in the codebase 36 | - Identify potential technical debt or future maintenance issues 37 | 38 | 3. **Verify System Integration**: 39 | - Ensure new code properly integrates with existing services and APIs 40 | - Check that database operations use PrismaService correctly 41 | - Validate that authentication follows the JWT cookie-based pattern 42 | - Confirm proper use of the WorkflowEngine V3 for workflow-related features 43 | - Verify API hooks follow the established TanStack Query patterns 44 | 45 | 4. **Assess Architectural Fit**: 46 | - Evaluate if the code belongs in the correct service/module 47 | - Check for proper separation of concerns and feature-based organization 48 | - Ensure microservice boundaries are respected 49 | - Validate that shared types are properly utilized from /src/types 50 | 51 | 5. **Review Specific Technologies**: 52 | - For React: Verify functional components, proper hook usage, and MUI v7/v8 sx prop patterns 53 | - For API: Ensure proper use of apiClient and no direct fetch/axios calls 54 | - For Database: Confirm Prisma best practices and no raw SQL queries 55 | - For State: Check appropriate use of TanStack Query for server state and Zustand for client state 56 | 57 | 6. **Provide Constructive Feedback**: 58 | - Explain the "why" behind each concern or suggestion 59 | - Reference specific project documentation or existing patterns 60 | - Prioritize issues by severity (critical, important, minor) 61 | - Suggest concrete improvements with code examples when helpful 62 | 63 | 7. **Save Review Output**: 64 | - Determine the task name from context or use descriptive name 65 | - Save your complete review to: `./dev/active/[task-name]/[task-name]-code-review.md` 66 | - Include "Last Updated: YYYY-MM-DD" at the top 67 | - Structure the review with clear sections: 68 | - Executive Summary 69 | - Critical Issues (must fix) 70 | - Important Improvements (should fix) 71 | - Minor Suggestions (nice to have) 72 | - Architecture Considerations 73 | - Next Steps 74 | 75 | 8. **Return to Parent Process**: 76 | - Inform the parent Claude instance: "Code review saved to: ./dev/active/[task-name]/[task-name]-code-review.md" 77 | - Include a brief summary of critical findings 78 | - **IMPORTANT**: Explicitly state "Please review the findings and approve which changes to implement before I proceed with any fixes." 79 | - Do NOT implement any fixes automatically 80 | 81 | You will be thorough but pragmatic, focusing on issues that truly matter for code quality, maintainability, and system integrity. You question everything but always with the goal of improving the codebase and ensuring it serves its intended purpose effectively. 82 | 83 | Remember: Your role is to be a thoughtful critic who ensures code not only works but fits seamlessly into the larger system while maintaining high standards of quality and consistency. Always save your review and wait for explicit approval before any changes are made. 84 | -------------------------------------------------------------------------------- /.claude/skills/backend-dev-guidelines/resources/testing-guide.md: -------------------------------------------------------------------------------- 1 | # Testing Guide - Backend Testing Strategies 2 | 3 | Complete guide to testing backend services with Jest and best practices. 4 | 5 | ## Table of Contents 6 | 7 | - [Unit Testing](#unit-testing) 8 | - [Integration Testing](#integration-testing) 9 | - [Mocking Strategies](#mocking-strategies) 10 | - [Test Data Management](#test-data-management) 11 | - [Testing Authenticated Routes](#testing-authenticated-routes) 12 | - [Coverage Targets](#coverage-targets) 13 | 14 | --- 15 | 16 | ## Unit Testing 17 | 18 | ### Test Structure 19 | 20 | ```typescript 21 | // services/userService.test.ts 22 | import { UserService } from './userService'; 23 | import { UserRepository } from '../repositories/UserRepository'; 24 | 25 | jest.mock('../repositories/UserRepository'); 26 | 27 | describe('UserService', () => { 28 | let service: UserService; 29 | let mockRepository: jest.Mocked; 30 | 31 | beforeEach(() => { 32 | mockRepository = { 33 | findByEmail: jest.fn(), 34 | create: jest.fn(), 35 | } as any; 36 | 37 | service = new UserService(); 38 | (service as any).userRepository = mockRepository; 39 | }); 40 | 41 | afterEach(() => { 42 | jest.clearAllMocks(); 43 | }); 44 | 45 | describe('create', () => { 46 | it('should throw error if email exists', async () => { 47 | mockRepository.findByEmail.mockResolvedValue({ id: '123' } as any); 48 | 49 | await expect( 50 | service.create({ email: 'test@test.com' }) 51 | ).rejects.toThrow('Email already in use'); 52 | }); 53 | 54 | it('should create user if email is unique', async () => { 55 | mockRepository.findByEmail.mockResolvedValue(null); 56 | mockRepository.create.mockResolvedValue({ id: '123' } as any); 57 | 58 | const user = await service.create({ 59 | email: 'test@test.com', 60 | firstName: 'John', 61 | lastName: 'Doe', 62 | }); 63 | 64 | expect(user).toBeDefined(); 65 | expect(mockRepository.create).toHaveBeenCalledWith( 66 | expect.objectContaining({ 67 | email: 'test@test.com' 68 | }) 69 | ); 70 | }); 71 | }); 72 | }); 73 | ``` 74 | 75 | --- 76 | 77 | ## Integration Testing 78 | 79 | ### Test with Real Database 80 | 81 | ```typescript 82 | import { PrismaService } from '@project-lifecycle-portal/database'; 83 | 84 | describe('UserService Integration', () => { 85 | let testUser: any; 86 | 87 | beforeAll(async () => { 88 | // Create test data 89 | testUser = await PrismaService.main.user.create({ 90 | data: { 91 | email: 'test@test.com', 92 | profile: { create: { firstName: 'Test', lastName: 'User' } }, 93 | }, 94 | }); 95 | }); 96 | 97 | afterAll(async () => { 98 | // Cleanup 99 | await PrismaService.main.user.delete({ where: { id: testUser.id } }); 100 | }); 101 | 102 | it('should find user by email', async () => { 103 | const user = await userService.findByEmail('test@test.com'); 104 | expect(user).toBeDefined(); 105 | expect(user?.email).toBe('test@test.com'); 106 | }); 107 | }); 108 | ``` 109 | 110 | --- 111 | 112 | ## Mocking Strategies 113 | 114 | ### Mock PrismaService 115 | 116 | ```typescript 117 | jest.mock('@project-lifecycle-portal/database', () => ({ 118 | PrismaService: { 119 | main: { 120 | user: { 121 | findMany: jest.fn(), 122 | findUnique: jest.fn(), 123 | create: jest.fn(), 124 | update: jest.fn(), 125 | }, 126 | }, 127 | isAvailable: true, 128 | }, 129 | })); 130 | ``` 131 | 132 | ### Mock Services 133 | 134 | ```typescript 135 | const mockUserService = { 136 | findById: jest.fn(), 137 | create: jest.fn(), 138 | update: jest.fn(), 139 | } as jest.Mocked; 140 | ``` 141 | 142 | --- 143 | 144 | ## Test Data Management 145 | 146 | ### Setup and Teardown 147 | 148 | ```typescript 149 | describe('PermissionService', () => { 150 | let instanceId: number; 151 | 152 | beforeAll(async () => { 153 | // Create test post 154 | const post = await PrismaService.main.post.create({ 155 | data: { title: 'Test Post', content: 'Test', authorId: 'test-user' }, 156 | }); 157 | instanceId = post.id; 158 | }); 159 | 160 | afterAll(async () => { 161 | // Cleanup 162 | await PrismaService.main.post.delete({ 163 | where: { id: instanceId }, 164 | }); 165 | }); 166 | 167 | beforeEach(() => { 168 | // Clear caches 169 | permissionService.clearCache(); 170 | }); 171 | 172 | it('should check permissions', async () => { 173 | const hasPermission = await permissionService.checkPermission( 174 | 'user-id', 175 | instanceId, 176 | 'VIEW_WORKFLOW' 177 | ); 178 | expect(hasPermission).toBeDefined(); 179 | }); 180 | }); 181 | ``` 182 | 183 | --- 184 | 185 | ## Testing Authenticated Routes 186 | 187 | ### Using test-auth-route.js 188 | 189 | ```bash 190 | # Test authenticated endpoint 191 | node scripts/test-auth-route.js http://localhost:3002/form/api/users 192 | 193 | # Test with POST data 194 | node scripts/test-auth-route.js http://localhost:3002/form/api/users POST '{"email":"test@test.com"}' 195 | ``` 196 | 197 | ### Mock Authentication in Tests 198 | 199 | ```typescript 200 | // Mock auth middleware 201 | jest.mock('../middleware/SSOMiddleware', () => ({ 202 | SSOMiddlewareClient: { 203 | verifyLoginStatus: (req, res, next) => { 204 | res.locals.claims = { 205 | sub: 'test-user-id', 206 | preferred_username: 'testuser', 207 | }; 208 | next(); 209 | }, 210 | }, 211 | })); 212 | ``` 213 | 214 | --- 215 | 216 | ## Coverage Targets 217 | 218 | ### Recommended Coverage 219 | 220 | - **Unit Tests**: 70%+ coverage 221 | - **Integration Tests**: Critical paths covered 222 | - **E2E Tests**: Happy paths covered 223 | 224 | ### Run Coverage 225 | 226 | ```bash 227 | npm test -- --coverage 228 | ``` 229 | 230 | --- 231 | 232 | **Related Files:** 233 | - [SKILL.md](SKILL.md) 234 | - [services-and-repositories.md](services-and-repositories.md) 235 | - [complete-examples.md](complete-examples.md) 236 | -------------------------------------------------------------------------------- /.claude/hooks/tsc-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TSC Hook with Visible Output 4 | # Uses stderr for visibility in Claude Code main interface 5 | 6 | CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$HOME/project}" 7 | HOOK_INPUT=$(cat) 8 | SESSION_ID="${session_id:-default}" 9 | CACHE_DIR="$HOME/.claude/tsc-cache/$SESSION_ID" 10 | 11 | # Create cache directory 12 | mkdir -p "$CACHE_DIR" 13 | 14 | # Extract tool name and input 15 | TOOL_NAME=$(echo "$HOOK_INPUT" | jq -r '.tool_name // ""') 16 | TOOL_INPUT=$(echo "$HOOK_INPUT" | jq -r '.tool_input // {}') 17 | 18 | # Function to get repo for a file 19 | get_repo_for_file() { 20 | local file_path="$1" 21 | local relative_path="${file_path#$CLAUDE_PROJECT_DIR/}" 22 | 23 | if [[ "$relative_path" =~ ^([^/]+)/ ]]; then 24 | local repo="${BASH_REMATCH[1]}" 25 | case "$repo" in 26 | email|exports|form|frontend|projects|uploads|users|utilities|events|database) 27 | echo "$repo" 28 | return 0 29 | ;; 30 | esac 31 | fi 32 | echo "" 33 | return 1 34 | } 35 | 36 | # Function to detect the correct TSC command for a repo 37 | get_tsc_command() { 38 | local repo_path="$1" 39 | cd "$repo_path" 2>/dev/null || return 1 40 | 41 | if [ -f "tsconfig.app.json" ]; then 42 | echo "npx tsc --project tsconfig.app.json --noEmit" 43 | elif [ -f "tsconfig.build.json" ]; then 44 | echo "npx tsc --project tsconfig.build.json --noEmit" 45 | elif [ -f "tsconfig.json" ]; then 46 | if grep -q '"references"' tsconfig.json 2>/dev/null; then 47 | if [ -f "tsconfig.app.json" ]; then 48 | echo "npx tsc --project tsconfig.app.json --noEmit" 49 | elif [ -f "tsconfig.src.json" ]; then 50 | echo "npx tsc --project tsconfig.src.json --noEmit" 51 | else 52 | echo "npx tsc --build --noEmit" 53 | fi 54 | else 55 | echo "npx tsc --noEmit" 56 | fi 57 | else 58 | echo "npx tsc --noEmit" 59 | fi 60 | } 61 | 62 | # Function to run TSC check 63 | run_tsc_check() { 64 | local repo="$1" 65 | local repo_path="$CLAUDE_PROJECT_DIR/$repo" 66 | local cache_file="$CACHE_DIR/$repo-tsc-cmd.cache" 67 | 68 | cd "$repo_path" 2>/dev/null || return 1 69 | 70 | # Get or cache the TSC command for this repo 71 | local tsc_cmd 72 | if [ -f "$cache_file" ] && [ -z "$FORCE_DETECT" ]; then 73 | tsc_cmd=$(cat "$cache_file") 74 | else 75 | tsc_cmd=$(get_tsc_command "$repo_path") 76 | echo "$tsc_cmd" > "$cache_file" 77 | fi 78 | 79 | eval "$tsc_cmd" 2>&1 80 | } 81 | 82 | # Only process file modification tools 83 | case "$TOOL_NAME" in 84 | Write|Edit|MultiEdit) 85 | # Extract file paths 86 | if [ "$TOOL_NAME" = "MultiEdit" ]; then 87 | FILE_PATHS=$(echo "$TOOL_INPUT" | jq -r '.edits[].file_path // empty') 88 | else 89 | FILE_PATHS=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty') 90 | fi 91 | 92 | # Collect repos that need checking (only for TS/JS files) 93 | REPOS_TO_CHECK=$(echo "$FILE_PATHS" | grep -E '\.(ts|tsx|js|jsx)$' | while read -r file_path; do 94 | if [ -n "$file_path" ]; then 95 | repo=$(get_repo_for_file "$file_path") 96 | [ -n "$repo" ] && echo "$repo" 97 | fi 98 | done | sort -u | tr '\n' ' ') 99 | 100 | # Trim whitespace 101 | REPOS_TO_CHECK=$(echo "$REPOS_TO_CHECK" | xargs) 102 | 103 | if [ -n "$REPOS_TO_CHECK" ]; then 104 | ERROR_COUNT=0 105 | ERROR_OUTPUT="" 106 | FAILED_REPOS="" 107 | 108 | # Output to stderr for visibility 109 | echo "⚡ TypeScript check on: $REPOS_TO_CHECK" >&2 110 | 111 | for repo in $REPOS_TO_CHECK; do 112 | echo -n " Checking $repo... " >&2 113 | 114 | # Run the check and capture output 115 | CHECK_OUTPUT=$(run_tsc_check "$repo" 2>&1) 116 | CHECK_EXIT_CODE=$? 117 | 118 | # Check for TypeScript errors in output 119 | if [ $CHECK_EXIT_CODE -ne 0 ] || echo "$CHECK_OUTPUT" | grep -q "error TS"; then 120 | echo "❌ Errors found" >&2 121 | ERROR_COUNT=$((ERROR_COUNT + 1)) 122 | FAILED_REPOS="$FAILED_REPOS $repo" 123 | ERROR_OUTPUT="${ERROR_OUTPUT} 124 | 125 | === Errors in $repo === 126 | $CHECK_OUTPUT" 127 | else 128 | echo "✅ OK" >&2 129 | fi 130 | done 131 | 132 | # If errors were found, show them and save for agent 133 | if [ $ERROR_COUNT -gt 0 ]; then 134 | # Save error information for the agent 135 | echo "$ERROR_OUTPUT" > "$CACHE_DIR/last-errors.txt" 136 | echo "$FAILED_REPOS" > "$CACHE_DIR/affected-repos.txt" 137 | 138 | # Save the TSC commands used for each repo 139 | echo "# TSC Commands by Repo" > "$CACHE_DIR/tsc-commands.txt" 140 | for repo in $FAILED_REPOS; do 141 | cmd=$(cat "$CACHE_DIR/$repo-tsc-cmd.cache" 2>/dev/null || echo "npx tsc --noEmit") 142 | echo "$repo: $cmd" >> "$CACHE_DIR/tsc-commands.txt" 143 | done 144 | 145 | # Output to stderr for visibility 146 | { 147 | echo "" 148 | echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 149 | echo "🚨 TypeScript errors found in $ERROR_COUNT repo(s): $FAILED_REPOS" 150 | echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 151 | echo "" 152 | echo "👉 IMPORTANT: Use the auto-error-resolver agent to fix the errors" 153 | echo "" 154 | echo "WE DO NOT LEAVE A MESS BEHIND" 155 | echo "Error Preview:" 156 | echo "$ERROR_OUTPUT" | grep "error TS" | head -10 157 | echo "" 158 | if [ $(echo "$ERROR_OUTPUT" | grep -c "error TS") -gt 10 ]; then 159 | echo "... and $(($(echo "$ERROR_OUTPUT" | grep -c "error TS") - 10)) more errors" 160 | fi 161 | } >&2 162 | 163 | # Exit with code 1 to make stderr visible 164 | exit 1 165 | fi 166 | fi 167 | ;; 168 | esac 169 | 170 | # Cleanup old cache directories (older than 7 days) 171 | find "$HOME/.claude/tsc-cache" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \; 2>/dev/null || true 172 | 173 | exit 0 -------------------------------------------------------------------------------- /.claude/agents/auth-route-debugger.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: auth-route-debugger 3 | description: Use this agent when you need to debug authentication-related issues with API routes, including 401/403 errors, cookie problems, JWT token issues, route registration problems, or when routes are returning 'not found' despite being defined. This agent specializes in the your project application's Keycloak/cookie-based authentication patterns.\n\nExamples:\n- \n Context: User is experiencing authentication issues with an API route\n user: "I'm getting a 401 error when trying to access the /api/workflow/123 route even though I'm logged in"\n assistant: "I'll use the auth-route-debugger agent to investigate this authentication issue"\n \n Since the user is having authentication problems with a route, use the auth-route-debugger agent to diagnose and fix the issue.\n \n \n- \n Context: User reports a route is not being found despite being defined\n user: "The POST /form/submit route returns 404 but I can see it's defined in the routes file"\n assistant: "Let me launch the auth-route-debugger agent to check the route registration and potential conflicts"\n \n Route not found errors often relate to registration order or naming conflicts, which the auth-route-debugger specializes in.\n \n \n- \n Context: User needs help testing an authenticated endpoint\n user: "Can you help me test if the /api/user/profile endpoint is working correctly with authentication?"\n assistant: "I'll use the auth-route-debugger agent to test this authenticated endpoint properly"\n \n Testing authenticated routes requires specific knowledge of the cookie-based auth system, which this agent handles.\n \n 4 | color: purple 5 | --- 6 | 7 | You are an elite authentication route debugging specialist for the your project application. You have deep expertise in JWT cookie-based authentication, Keycloak/OpenID Connect integration, Express.js route registration, and the specific SSO middleware patterns used in this codebase. 8 | 9 | ## Core Responsibilities 10 | 11 | 1. **Diagnose Authentication Issues**: Identify root causes of 401/403 errors, cookie problems, JWT validation failures, and middleware configuration issues. 12 | 13 | 2. **Test Authenticated Routes**: Use the provided testing scripts (`scripts/get-auth-token.js` and `scripts/test-auth-route.js`) to verify route behavior with proper cookie-based authentication. 14 | 15 | 3. **Debug Route Registration**: Check app.ts for proper route registration, identify ordering issues that might cause route conflicts, and detect naming collisions between routes. 16 | 17 | 4. **Memory Integration**: Always check the project-memory MCP for previous solutions to similar issues before starting diagnosis. Update memory with new solutions after resolving issues. 18 | 19 | ## Debugging Workflow 20 | 21 | ### Initial Assessment 22 | 23 | 1. First, retrieve relevant information from memory about similar past issues 24 | 2. Identify the specific route, HTTP method, and error being encountered 25 | 3. Gather any payload information provided or inspect the route handler to determine required payload structure 26 | 27 | ### Check Live Service Logs (PM2) 28 | 29 | When services are running with PM2, check logs for authentication errors: 30 | 31 | 1. **Real-time monitoring**: `pm2 logs form` (or email, users, etc.) 32 | 2. **Recent errors**: `pm2 logs form --lines 200` 33 | 3. **Error-specific logs**: `tail -f form/logs/form-error.log` 34 | 4. **All services**: `pm2 logs --timestamp` 35 | 5. **Check service status**: `pm2 list` to ensure services are running 36 | 37 | ### Route Registration Checks 38 | 39 | 1. **Always** verify the route is properly registered in app.ts 40 | 2. Check the registration order - earlier routes can intercept requests meant for later ones 41 | 3. Look for route naming conflicts (e.g., `/api/:id` before `/api/specific`) 42 | 4. Verify middleware is applied correctly to the route 43 | 44 | ### Authentication Testing 45 | 46 | 1. Use `scripts/test-auth-route.js` to test the route with authentication: 47 | 48 | - For GET requests: `node scripts/test-auth-route.js [URL]` 49 | - For POST/PUT/DELETE: `node scripts/test-auth-route.js --method [METHOD] --body '[JSON]' [URL]` 50 | - Test without auth to confirm it's an auth issue: `--no-auth` flag 51 | 52 | 2. If route works without auth but fails with auth, investigate: 53 | - Cookie configuration (httpOnly, secure, sameSite) 54 | - JWT signing/validation in SSO middleware 55 | - Token expiration settings 56 | - Role/permission requirements 57 | 58 | ### Common Issues to Check 59 | 60 | 1. **Route Not Found (404)**: 61 | 62 | - Missing route registration in app.ts 63 | - Route registered after a catch-all route 64 | - Typo in route path or HTTP method 65 | - Missing router export/import 66 | - Check PM2 logs for startup errors: `pm2 logs [service] --lines 500` 67 | 68 | 2. **Authentication Failures (401/403)**: 69 | 70 | - Expired tokens (check Keycloak token lifetime) 71 | - Missing or malformed refresh_token cookie 72 | - Incorrect JWT secret in form/config.ini 73 | - Role-based access control blocking the user 74 | 75 | 3. **Cookie Issues**: 76 | - Development vs production cookie settings 77 | - CORS configuration preventing cookie transmission 78 | - SameSite policy blocking cross-origin requests 79 | 80 | ### Testing Payloads 81 | 82 | When testing POST/PUT routes, determine required payload by: 83 | 84 | 1. Checking the route handler for expected body structure 85 | 2. Looking for validation schemas (Zod, Joi, etc.) 86 | 3. Reviewing any TypeScript interfaces for the request body 87 | 4. Checking existing tests for example payloads 88 | 89 | ### Documentation Updates 90 | 91 | After resolving an issue: 92 | 93 | 1. Update memory with the problem, solution, and any patterns discovered 94 | 2. If it's a new type of issue, update the troubleshooting documentation 95 | 3. Include specific commands used and configuration changes made 96 | 4. Document any workarounds or temporary fixes applied 97 | 98 | ## Key Technical Details 99 | 100 | - The SSO middleware expects a JWT-signed refresh token in the `refresh_token` cookie 101 | - User claims are stored in `res.locals.claims` including username, email, and roles 102 | - Default dev credentials: username=testuser, password=testpassword 103 | - Keycloak realm: yourRealm, Client: your-app-client 104 | - Routes must handle both cookie-based auth and potential Bearer token fallbacks 105 | 106 | ## Output Format 107 | 108 | Provide clear, actionable findings including: 109 | 110 | 1. Root cause identification 111 | 2. Step-by-step reproduction of the issue 112 | 3. Specific fix implementation 113 | 4. Testing commands to verify the fix 114 | 5. Any configuration changes needed 115 | 6. Memory/documentation updates made 116 | 117 | Always test your solutions using the authentication testing scripts before declaring an issue resolved. 118 | -------------------------------------------------------------------------------- /.claude/agents/code-refactor-master.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: code-refactor-master 3 | description: Use this agent when you need to refactor code for better organization, cleaner architecture, or improved maintainability. This includes reorganizing file structures, breaking down large components into smaller ones, updating import paths after file moves, fixing loading indicator patterns, and ensuring adherence to project best practices. The agent excels at comprehensive refactoring that requires tracking dependencies and maintaining consistency across the entire codebase.\n\n\nContext: The user wants to reorganize a messy component structure with large files and poor organization.\nuser: "This components folder is a mess with huge files. Can you help refactor it?"\nassistant: "I'll use the code-refactor-master agent to analyze the component structure and create a better organization scheme."\n\nSince the user needs help with refactoring and reorganizing components, use the code-refactor-master agent to analyze the current structure and propose improvements.\n\n\n\n\nContext: The user has identified multiple components using early returns with loading indicators instead of proper loading components.\nuser: "I noticed we have loading returns scattered everywhere instead of using LoadingOverlay"\nassistant: "Let me use the code-refactor-master agent to find all instances of early return loading patterns and refactor them to use the proper loading components."\n\nThe user has identified a pattern that violates best practices, so use the code-refactor-master agent to systematically find and fix all occurrences.\n\n\n\n\nContext: The user wants to break down a large component file into smaller, more manageable pieces.\nuser: "The Dashboard.tsx file is over 2000 lines and becoming unmaintainable"\nassistant: "I'll use the code-refactor-master agent to analyze the Dashboard component and extract it into smaller, focused components."\n\nThe user needs help breaking down a large component, which requires careful analysis of dependencies and proper extraction - perfect for the code-refactor-master agent.\n\n 4 | model: opus 5 | color: cyan 6 | --- 7 | 8 | You are the Code Refactor Master, an elite specialist in code organization, architecture improvement, and meticulous refactoring. Your expertise lies in transforming chaotic codebases into well-organized, maintainable systems while ensuring zero breakage through careful dependency tracking. 9 | 10 | **Core Responsibilities:** 11 | 12 | 1. **File Organization & Structure** 13 | - You analyze existing file structures and devise significantly better organizational schemes 14 | - You create logical directory hierarchies that group related functionality 15 | - You establish clear naming conventions that improve code discoverability 16 | - You ensure consistent patterns across the entire codebase 17 | 18 | 2. **Dependency Tracking & Import Management** 19 | - Before moving ANY file, you MUST search for and document every single import of that file 20 | - You maintain a comprehensive map of all file dependencies 21 | - You update all import paths systematically after file relocations 22 | - You verify no broken imports remain after refactoring 23 | 24 | 3. **Component Refactoring** 25 | - You identify oversized components and extract them into smaller, focused units 26 | - You recognize repeated patterns and abstract them into reusable components 27 | - You ensure proper prop drilling is avoided through context or composition 28 | - You maintain component cohesion while reducing coupling 29 | 30 | 4. **Loading Pattern Enforcement** 31 | - You MUST find ALL files containing early returns with loading indicators 32 | - You replace improper loading patterns with LoadingOverlay, SuspenseLoader, or PaperWrapper's built-in loading indicator 33 | - You ensure consistent loading UX across the application 34 | - You flag any deviation from established loading best practices 35 | 36 | 5. **Best Practices & Code Quality** 37 | - You identify and fix anti-patterns throughout the codebase 38 | - You ensure proper separation of concerns 39 | - You enforce consistent error handling patterns 40 | - You optimize performance bottlenecks during refactoring 41 | - You maintain or improve TypeScript type safety 42 | 43 | **Your Refactoring Process:** 44 | 45 | 1. **Discovery Phase** 46 | - Analyze the current file structure and identify problem areas 47 | - Map all dependencies and import relationships 48 | - Document all instances of anti-patterns (especially early return loading) 49 | - Create a comprehensive inventory of refactoring opportunities 50 | 51 | 2. **Planning Phase** 52 | - Design the new organizational structure with clear rationale 53 | - Create a dependency update matrix showing all required import changes 54 | - Plan component extraction strategy with minimal disruption 55 | - Identify the order of operations to prevent breaking changes 56 | 57 | 3. **Execution Phase** 58 | - Execute refactoring in logical, atomic steps 59 | - Update all imports immediately after each file move 60 | - Extract components with clear interfaces and responsibilities 61 | - Replace all improper loading patterns with approved alternatives 62 | 63 | 4. **Verification Phase** 64 | - Verify all imports resolve correctly 65 | - Ensure no functionality has been broken 66 | - Confirm all loading patterns follow best practices 67 | - Validate that the new structure improves maintainability 68 | 69 | **Critical Rules:** 70 | - NEVER move a file without first documenting ALL its importers 71 | - NEVER leave broken imports in the codebase 72 | - NEVER allow early returns with loading indicators to remain 73 | - ALWAYS use LoadingOverlay, SuspenseLoader, or PaperWrapper's loading for loading states 74 | - ALWAYS maintain backward compatibility unless explicitly approved to break it 75 | - ALWAYS group related functionality together in the new structure 76 | - ALWAYS extract large components into smaller, testable units 77 | 78 | **Quality Metrics You Enforce:** 79 | - No component should exceed 300 lines (excluding imports/exports) 80 | - No file should have more than 5 levels of nesting 81 | - All loading states must use approved loading components 82 | - Import paths should be relative within modules, absolute across modules 83 | - Each directory should have a clear, single responsibility 84 | 85 | **Output Format:** 86 | When presenting refactoring plans, you provide: 87 | 1. Current structure analysis with identified issues 88 | 2. Proposed new structure with justification 89 | 3. Complete dependency map with all files affected 90 | 4. Step-by-step migration plan with import updates 91 | 5. List of all anti-patterns found and their fixes 92 | 6. Risk assessment and mitigation strategies 93 | 94 | You are meticulous, systematic, and never rush. You understand that proper refactoring requires patience and attention to detail. Every file move, every component extraction, and every pattern fix is done with surgical precision to ensure the codebase emerges cleaner, more maintainable, and fully functional. 95 | -------------------------------------------------------------------------------- /.claude/skills/backend-dev-guidelines/resources/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration Management - UnifiedConfig Pattern 2 | 3 | Complete guide to managing configuration in backend microservices. 4 | 5 | ## Table of Contents 6 | 7 | - [UnifiedConfig Overview](#unifiedconfig-overview) 8 | - [NEVER Use process.env Directly](#never-use-processenv-directly) 9 | - [Configuration Structure](#configuration-structure) 10 | - [Environment-Specific Configs](#environment-specific-configs) 11 | - [Secrets Management](#secrets-management) 12 | - [Migration Guide](#migration-guide) 13 | 14 | --- 15 | 16 | ## UnifiedConfig Overview 17 | 18 | ### Why UnifiedConfig? 19 | 20 | **Problems with process.env:** 21 | - ❌ No type safety 22 | - ❌ No validation 23 | - ❌ Hard to test 24 | - ❌ Scattered throughout code 25 | - ❌ No default values 26 | - ❌ Runtime errors for typos 27 | 28 | **Benefits of unifiedConfig:** 29 | - ✅ Type-safe configuration 30 | - ✅ Single source of truth 31 | - ✅ Validated at startup 32 | - ✅ Easy to test with mocks 33 | - ✅ Clear structure 34 | - ✅ Fallback to environment variables 35 | 36 | --- 37 | 38 | ## NEVER Use process.env Directly 39 | 40 | ### The Rule 41 | 42 | ```typescript 43 | // ❌ NEVER DO THIS 44 | const timeout = parseInt(process.env.TIMEOUT_MS || '5000'); 45 | const dbHost = process.env.DB_HOST || 'localhost'; 46 | 47 | // ✅ ALWAYS DO THIS 48 | import { config } from './config/unifiedConfig'; 49 | const timeout = config.timeouts.default; 50 | const dbHost = config.database.host; 51 | ``` 52 | 53 | ### Why This Matters 54 | 55 | **Example of problems:** 56 | ```typescript 57 | // Typo in environment variable name 58 | const host = process.env.DB_HSOT; // undefined! No error! 59 | 60 | // Type safety 61 | const port = process.env.PORT; // string! Need parseInt 62 | const timeout = parseInt(process.env.TIMEOUT); // NaN if not set! 63 | ``` 64 | 65 | **With unifiedConfig:** 66 | ```typescript 67 | const port = config.server.port; // number, guaranteed 68 | const timeout = config.timeouts.default; // number, with fallback 69 | ``` 70 | 71 | --- 72 | 73 | ## Configuration Structure 74 | 75 | ### UnifiedConfig Interface 76 | 77 | ```typescript 78 | export interface UnifiedConfig { 79 | database: { 80 | host: string; 81 | port: number; 82 | username: string; 83 | password: string; 84 | database: string; 85 | }; 86 | server: { 87 | port: number; 88 | sessionSecret: string; 89 | }; 90 | tokens: { 91 | jwt: string; 92 | inactivity: string; 93 | internal: string; 94 | }; 95 | keycloak: { 96 | realm: string; 97 | client: string; 98 | baseUrl: string; 99 | secret: string; 100 | }; 101 | aws: { 102 | region: string; 103 | emailQueueUrl: string; 104 | accessKeyId: string; 105 | secretAccessKey: string; 106 | }; 107 | sentry: { 108 | dsn: string; 109 | environment: string; 110 | tracesSampleRate: number; 111 | }; 112 | // ... more sections 113 | } 114 | ``` 115 | 116 | ### Implementation Pattern 117 | 118 | **File:** `/blog-api/src/config/unifiedConfig.ts` 119 | 120 | ```typescript 121 | import * as fs from 'fs'; 122 | import * as path from 'path'; 123 | import * as ini from 'ini'; 124 | 125 | const configPath = path.join(__dirname, '../../config.ini'); 126 | const iniConfig = ini.parse(fs.readFileSync(configPath, 'utf-8')); 127 | 128 | export const config: UnifiedConfig = { 129 | database: { 130 | host: iniConfig.database?.host || process.env.DB_HOST || 'localhost', 131 | port: parseInt(iniConfig.database?.port || process.env.DB_PORT || '3306'), 132 | username: iniConfig.database?.username || process.env.DB_USER || 'root', 133 | password: iniConfig.database?.password || process.env.DB_PASSWORD || '', 134 | database: iniConfig.database?.database || process.env.DB_NAME || 'blog_dev', 135 | }, 136 | server: { 137 | port: parseInt(iniConfig.server?.port || process.env.PORT || '3002'), 138 | sessionSecret: iniConfig.server?.sessionSecret || process.env.SESSION_SECRET || 'dev-secret', 139 | }, 140 | // ... more configuration 141 | }; 142 | 143 | // Validate critical config 144 | if (!config.tokens.jwt) { 145 | throw new Error('JWT secret not configured!'); 146 | } 147 | ``` 148 | 149 | **Key Points:** 150 | - Read from config.ini first 151 | - Fallback to process.env 152 | - Default values for development 153 | - Validation at startup 154 | - Type-safe access 155 | 156 | --- 157 | 158 | ## Environment-Specific Configs 159 | 160 | ### config.ini Structure 161 | 162 | ```ini 163 | [database] 164 | host = localhost 165 | port = 3306 166 | username = root 167 | password = password1 168 | database = blog_dev 169 | 170 | [server] 171 | port = 3002 172 | sessionSecret = your-secret-here 173 | 174 | [tokens] 175 | jwt = your-jwt-secret 176 | inactivity = 30m 177 | internal = internal-api-token 178 | 179 | [keycloak] 180 | realm = myapp 181 | client = myapp-client 182 | baseUrl = http://localhost:8080 183 | secret = keycloak-client-secret 184 | 185 | [sentry] 186 | dsn = https://your-sentry-dsn 187 | environment = development 188 | tracesSampleRate = 0.1 189 | ``` 190 | 191 | ### Environment Overrides 192 | 193 | ```bash 194 | # .env file (optional overrides) 195 | DB_HOST=production-db.example.com 196 | DB_PASSWORD=secure-password 197 | PORT=80 198 | ``` 199 | 200 | **Precedence:** 201 | 1. config.ini (highest priority) 202 | 2. process.env variables 203 | 3. Hard-coded defaults (lowest priority) 204 | 205 | --- 206 | 207 | ## Secrets Management 208 | 209 | ### DO NOT Commit Secrets 210 | 211 | ```gitignore 212 | # .gitignore 213 | config.ini 214 | .env 215 | sentry.ini 216 | *.pem 217 | *.key 218 | ``` 219 | 220 | ### Use Environment Variables in Production 221 | 222 | ```typescript 223 | // Development: config.ini 224 | // Production: Environment variables 225 | 226 | export const config: UnifiedConfig = { 227 | database: { 228 | password: process.env.DB_PASSWORD || iniConfig.database?.password || '', 229 | }, 230 | tokens: { 231 | jwt: process.env.JWT_SECRET || iniConfig.tokens?.jwt || '', 232 | }, 233 | }; 234 | ``` 235 | 236 | --- 237 | 238 | ## Migration Guide 239 | 240 | ### Find All process.env Usage 241 | 242 | ```bash 243 | grep -r "process.env" blog-api/src/ --include="*.ts" | wc -l 244 | ``` 245 | 246 | ### Migration Example 247 | 248 | **Before:** 249 | ```typescript 250 | // Scattered throughout code 251 | const timeout = parseInt(process.env.OPENID_HTTP_TIMEOUT_MS || '15000'); 252 | const keycloakUrl = process.env.KEYCLOAK_BASE_URL; 253 | const jwtSecret = process.env.JWT_SECRET; 254 | ``` 255 | 256 | **After:** 257 | ```typescript 258 | import { config } from './config/unifiedConfig'; 259 | 260 | const timeout = config.keycloak.timeout; 261 | const keycloakUrl = config.keycloak.baseUrl; 262 | const jwtSecret = config.tokens.jwt; 263 | ``` 264 | 265 | **Benefits:** 266 | - Type-safe 267 | - Centralized 268 | - Easy to test 269 | - Validated at startup 270 | 271 | --- 272 | 273 | **Related Files:** 274 | - [SKILL.md](SKILL.md) 275 | - [testing-guide.md](testing-guide.md) 276 | -------------------------------------------------------------------------------- /.claude/agents/README.md: -------------------------------------------------------------------------------- 1 | # Agents 2 | 3 | Specialized agents for complex, multi-step tasks. 4 | 5 | --- 6 | 7 | ## What Are Agents? 8 | 9 | Agents are autonomous Claude instances that handle specific complex tasks. Unlike skills (which provide inline guidance), agents: 10 | - Run as separate sub-tasks 11 | - Work autonomously with minimal supervision 12 | - Have specialized tool access 13 | - Return comprehensive reports when complete 14 | 15 | **Key advantage:** Agents are **standalone** - just copy the `.md` file and use immediately! 16 | 17 | --- 18 | 19 | ## Available Agents (10) 20 | 21 | ### code-architecture-reviewer 22 | **Purpose:** Review code for architectural consistency and best practices 23 | 24 | **When to use:** 25 | - After implementing a new feature 26 | - Before merging significant changes 27 | - When refactoring code 28 | - To validate architectural decisions 29 | 30 | **Integration:** ✅ Copy as-is 31 | 32 | --- 33 | 34 | ### code-refactor-master 35 | **Purpose:** Plan and execute comprehensive refactoring 36 | 37 | **When to use:** 38 | - Reorganizing file structures 39 | - Breaking down large components 40 | - Updating import paths after moves 41 | - Improving code maintainability 42 | 43 | **Integration:** ✅ Copy as-is 44 | 45 | --- 46 | 47 | ### documentation-architect 48 | **Purpose:** Create comprehensive documentation 49 | 50 | **When to use:** 51 | - Documenting new features 52 | - Creating API documentation 53 | - Writing developer guides 54 | - Generating architectural overviews 55 | 56 | **Integration:** ✅ Copy as-is 57 | 58 | --- 59 | 60 | ### frontend-error-fixer 61 | **Purpose:** Debug and fix frontend errors 62 | 63 | **When to use:** 64 | - Browser console errors 65 | - TypeScript compilation errors in frontend 66 | - React errors 67 | - Build failures 68 | 69 | **Integration:** ⚠️ May reference screenshot paths - update if needed 70 | 71 | --- 72 | 73 | ### plan-reviewer 74 | **Purpose:** Review development plans before implementation 75 | 76 | **When to use:** 77 | - Before starting complex features 78 | - Validating architectural plans 79 | - Identifying potential issues early 80 | - Getting second opinion on approach 81 | 82 | **Integration:** ✅ Copy as-is 83 | 84 | --- 85 | 86 | ### refactor-planner 87 | **Purpose:** Create comprehensive refactoring strategies 88 | 89 | **When to use:** 90 | - Planning code reorganization 91 | - Modernizing legacy code 92 | - Breaking down large files 93 | - Improving code structure 94 | 95 | **Integration:** ✅ Copy as-is 96 | 97 | --- 98 | 99 | ### web-research-specialist 100 | **Purpose:** Research technical issues online 101 | 102 | **When to use:** 103 | - Debugging obscure errors 104 | - Finding solutions to problems 105 | - Researching best practices 106 | - Comparing implementation approaches 107 | 108 | **Integration:** ✅ Copy as-is 109 | 110 | --- 111 | 112 | ### auth-route-tester 113 | **Purpose:** Test authenticated API endpoints 114 | 115 | **When to use:** 116 | - Testing routes with JWT cookie auth 117 | - Validating endpoint functionality 118 | - Debugging authentication issues 119 | 120 | **Integration:** ⚠️ Requires JWT cookie-based auth 121 | 122 | --- 123 | 124 | ### auth-route-debugger 125 | **Purpose:** Debug authentication issues 126 | 127 | **When to use:** 128 | - Auth failures 129 | - Token issues 130 | - Cookie problems 131 | - Permission errors 132 | 133 | **Integration:** ⚠️ Requires JWT cookie-based auth 134 | 135 | --- 136 | 137 | ### auto-error-resolver 138 | **Purpose:** Automatically fix TypeScript compilation errors 139 | 140 | **When to use:** 141 | - Build failures with TypeScript errors 142 | - After refactoring that breaks types 143 | - Systematic error resolution needed 144 | 145 | **Integration:** ⚠️ May need path updates 146 | 147 | --- 148 | 149 | ## How to Integrate an Agent 150 | 151 | ### Standard Integration (Most Agents) 152 | 153 | **Step 1: Copy the file** 154 | ```bash 155 | cp showcase/.claude/agents/agent-name.md \\ 156 | your-project/.claude/agents/ 157 | ``` 158 | 159 | **Step 2: Verify (optional)** 160 | ```bash 161 | # Check for hardcoded paths 162 | grep -n "~/git/\|/root/git/\|/Users/" your-project/.claude/agents/agent-name.md 163 | ``` 164 | 165 | **Step 3: Use it** 166 | Ask Claude: "Use the [agent-name] agent to [task]" 167 | 168 | That's it! Agents work immediately. 169 | 170 | --- 171 | 172 | ### Agents Requiring Customization 173 | 174 | **frontend-error-fixer:** 175 | - May reference screenshot paths 176 | - Ask user: "Where should screenshots be saved?" 177 | - Update paths in agent file 178 | 179 | **auth-route-tester / auth-route-debugger:** 180 | - Require JWT cookie authentication 181 | - Update service URLs from examples 182 | - Customize for user's auth setup 183 | 184 | **auto-error-resolver:** 185 | - May have hardcoded project paths 186 | - Update to use `$CLAUDE_PROJECT_DIR` or relative paths 187 | 188 | --- 189 | 190 | ## When to Use Agents vs Skills 191 | 192 | | Use Agents When... | Use Skills When... | 193 | |-------------------|-------------------| 194 | | Task requires multiple steps | Need inline guidance | 195 | | Complex analysis needed | Checking best practices | 196 | | Autonomous work preferred | Want to maintain control | 197 | | Task has clear end goal | Ongoing development work | 198 | | Example: "Review all controllers" | Example: "Creating a new route" | 199 | 200 | **Both can work together:** 201 | - Skill provides patterns during development 202 | - Agent reviews the result when complete 203 | 204 | --- 205 | 206 | ## Agent Quick Reference 207 | 208 | | Agent | Complexity | Customization | Auth Required | 209 | |-------|-----------|---------------|---------------| 210 | | code-architecture-reviewer | Medium | ✅ None | No | 211 | | code-refactor-master | High | ✅ None | No | 212 | | documentation-architect | Medium | ✅ None | No | 213 | | frontend-error-fixer | Medium | ⚠️ Screenshot paths | No | 214 | | plan-reviewer | Low | ✅ None | No | 215 | | refactor-planner | Medium | ✅ None | No | 216 | | web-research-specialist | Low | ✅ None | No | 217 | | auth-route-tester | Medium | ⚠️ Auth setup | JWT cookies | 218 | | auth-route-debugger | Medium | ⚠️ Auth setup | JWT cookies | 219 | | auto-error-resolver | Low | ⚠️ Paths | No | 220 | 221 | --- 222 | 223 | ## For Claude Code 224 | 225 | **When integrating agents for a user:** 226 | 227 | 1. **Read [CLAUDE_INTEGRATION_GUIDE.md](../../CLAUDE_INTEGRATION_GUIDE.md)** 228 | 2. **Just copy the .md file** - agents are standalone 229 | 3. **Check for hardcoded paths:** 230 | ```bash 231 | grep "~/git/\|/root/" agent-name.md 232 | ``` 233 | 4. **Update paths if found** to `$CLAUDE_PROJECT_DIR` or `.` 234 | 5. **For auth agents:** Ask if they use JWT cookie auth first 235 | 236 | **That's it!** Agents are the easiest components to integrate. 237 | 238 | --- 239 | 240 | ## Creating Your Own Agents 241 | 242 | Agents are markdown files with optional YAML frontmatter: 243 | 244 | ```markdown 245 | # Agent Name 246 | 247 | ## Purpose 248 | What this agent does 249 | 250 | ## Instructions 251 | Step-by-step instructions for autonomous execution 252 | 253 | ## Tools Available 254 | List of tools this agent can use 255 | 256 | ## Expected Output 257 | What format to return results in 258 | ``` 259 | 260 | **Tips:** 261 | - Be very specific in instructions 262 | - Break complex tasks into numbered steps 263 | - Specify exactly what to return 264 | - Include examples of good output 265 | - List available tools explicitly 266 | 267 | --- 268 | 269 | ## Troubleshooting 270 | 271 | ### Agent not found 272 | 273 | **Check:** 274 | ```bash 275 | # Is agent file present? 276 | ls -la .claude/agents/[agent-name].md 277 | ``` 278 | 279 | ### Agent fails with path errors 280 | 281 | **Check for hardcoded paths:** 282 | ```bash 283 | grep "~/\|/root/\|/Users/" .claude/agents/[agent-name].md 284 | ``` 285 | 286 | **Fix:** 287 | ```bash 288 | sed -i 's|~/git/.*project|$CLAUDE_PROJECT_DIR|g' .claude/agents/[agent-name].md 289 | ``` 290 | 291 | --- 292 | 293 | ## Next Steps 294 | 295 | 1. **Browse agents above** - Find ones useful for your work 296 | 2. **Copy what you need** - Just the .md file 297 | 3. **Ask Claude to use them** - "Use [agent] to [task]" 298 | 4. **Create your own** - Follow the pattern for your specific needs 299 | 300 | **Questions?** See [CLAUDE_INTEGRATION_GUIDE.md](../../CLAUDE_INTEGRATION_GUIDE.md) 301 | -------------------------------------------------------------------------------- /.claude/skills/backend-dev-guidelines/resources/async-and-errors.md: -------------------------------------------------------------------------------- 1 | # Async Patterns and Error Handling 2 | 3 | Complete guide to async/await patterns and custom error handling. 4 | 5 | ## Table of Contents 6 | 7 | - [Async/Await Best Practices](#asyncawait-best-practices) 8 | - [Promise Error Handling](#promise-error-handling) 9 | - [Custom Error Types](#custom-error-types) 10 | - [asyncErrorWrapper Utility](#asyncerrorwrapper-utility) 11 | - [Error Propagation](#error-propagation) 12 | - [Common Async Pitfalls](#common-async-pitfalls) 13 | 14 | --- 15 | 16 | ## Async/Await Best Practices 17 | 18 | ### Always Use Try-Catch 19 | 20 | ```typescript 21 | // ❌ NEVER: Unhandled async errors 22 | async function fetchData() { 23 | const data = await database.query(); // If throws, unhandled! 24 | return data; 25 | } 26 | 27 | // ✅ ALWAYS: Wrap in try-catch 28 | async function fetchData() { 29 | try { 30 | const data = await database.query(); 31 | return data; 32 | } catch (error) { 33 | Sentry.captureException(error); 34 | throw error; 35 | } 36 | } 37 | ``` 38 | 39 | ### Avoid .then() Chains 40 | 41 | ```typescript 42 | // ❌ AVOID: Promise chains 43 | function processData() { 44 | return fetchData() 45 | .then(data => transform(data)) 46 | .then(transformed => save(transformed)) 47 | .catch(error => { 48 | console.error(error); 49 | }); 50 | } 51 | 52 | // ✅ PREFER: Async/await 53 | async function processData() { 54 | try { 55 | const data = await fetchData(); 56 | const transformed = await transform(data); 57 | return await save(transformed); 58 | } catch (error) { 59 | Sentry.captureException(error); 60 | throw error; 61 | } 62 | } 63 | ``` 64 | 65 | --- 66 | 67 | ## Promise Error Handling 68 | 69 | ### Parallel Operations 70 | 71 | ```typescript 72 | // ✅ Handle errors in Promise.all 73 | try { 74 | const [users, profiles, settings] = await Promise.all([ 75 | userService.getAll(), 76 | profileService.getAll(), 77 | settingsService.getAll(), 78 | ]); 79 | } catch (error) { 80 | // One failure fails all 81 | Sentry.captureException(error); 82 | throw error; 83 | } 84 | 85 | // ✅ Handle errors individually with Promise.allSettled 86 | const results = await Promise.allSettled([ 87 | userService.getAll(), 88 | profileService.getAll(), 89 | settingsService.getAll(), 90 | ]); 91 | 92 | results.forEach((result, index) => { 93 | if (result.status === 'rejected') { 94 | Sentry.captureException(result.reason, { 95 | tags: { operation: ['users', 'profiles', 'settings'][index] } 96 | }); 97 | } 98 | }); 99 | ``` 100 | 101 | --- 102 | 103 | ## Custom Error Types 104 | 105 | ### Define Custom Errors 106 | 107 | ```typescript 108 | // Base error class 109 | export class AppError extends Error { 110 | constructor( 111 | message: string, 112 | public code: string, 113 | public statusCode: number, 114 | public isOperational: boolean = true 115 | ) { 116 | super(message); 117 | this.name = this.constructor.name; 118 | Error.captureStackTrace(this, this.constructor); 119 | } 120 | } 121 | 122 | // Specific error types 123 | export class ValidationError extends AppError { 124 | constructor(message: string) { 125 | super(message, 'VALIDATION_ERROR', 400); 126 | } 127 | } 128 | 129 | export class NotFoundError extends AppError { 130 | constructor(message: string) { 131 | super(message, 'NOT_FOUND', 404); 132 | } 133 | } 134 | 135 | export class ForbiddenError extends AppError { 136 | constructor(message: string) { 137 | super(message, 'FORBIDDEN', 403); 138 | } 139 | } 140 | 141 | export class ConflictError extends AppError { 142 | constructor(message: string) { 143 | super(message, 'CONFLICT', 409); 144 | } 145 | } 146 | ``` 147 | 148 | ### Usage 149 | 150 | ```typescript 151 | // Throw specific errors 152 | if (!user) { 153 | throw new NotFoundError('User not found'); 154 | } 155 | 156 | if (user.age < 18) { 157 | throw new ValidationError('User must be 18+'); 158 | } 159 | 160 | // Error boundary handles them 161 | function errorBoundary(error, req, res, next) { 162 | if (error instanceof AppError) { 163 | return res.status(error.statusCode).json({ 164 | error: { 165 | message: error.message, 166 | code: error.code 167 | } 168 | }); 169 | } 170 | 171 | // Unknown error 172 | Sentry.captureException(error); 173 | res.status(500).json({ error: { message: 'Internal server error' } }); 174 | } 175 | ``` 176 | 177 | --- 178 | 179 | ## asyncErrorWrapper Utility 180 | 181 | ### Pattern 182 | 183 | ```typescript 184 | export function asyncErrorWrapper( 185 | handler: (req: Request, res: Response, next: NextFunction) => Promise 186 | ) { 187 | return async (req: Request, res: Response, next: NextFunction) => { 188 | try { 189 | await handler(req, res, next); 190 | } catch (error) { 191 | next(error); 192 | } 193 | }; 194 | } 195 | ``` 196 | 197 | ### Usage 198 | 199 | ```typescript 200 | // Without wrapper - error can be unhandled 201 | router.get('/users', async (req, res) => { 202 | const users = await userService.getAll(); // If throws, unhandled! 203 | res.json(users); 204 | }); 205 | 206 | // With wrapper - errors caught 207 | router.get('/users', asyncErrorWrapper(async (req, res) => { 208 | const users = await userService.getAll(); 209 | res.json(users); 210 | })); 211 | ``` 212 | 213 | --- 214 | 215 | ## Error Propagation 216 | 217 | ### Proper Error Chains 218 | 219 | ```typescript 220 | // ✅ Propagate errors up the stack 221 | async function repositoryMethod() { 222 | try { 223 | return await PrismaService.main.user.findMany(); 224 | } catch (error) { 225 | Sentry.captureException(error, { tags: { layer: 'repository' } }); 226 | throw error; // Propagate to service 227 | } 228 | } 229 | 230 | async function serviceMethod() { 231 | try { 232 | return await repositoryMethod(); 233 | } catch (error) { 234 | Sentry.captureException(error, { tags: { layer: 'service' } }); 235 | throw error; // Propagate to controller 236 | } 237 | } 238 | 239 | async function controllerMethod(req, res) { 240 | try { 241 | const result = await serviceMethod(); 242 | res.json(result); 243 | } catch (error) { 244 | this.handleError(error, res, 'controllerMethod'); // Final handler 245 | } 246 | } 247 | ``` 248 | 249 | --- 250 | 251 | ## Common Async Pitfalls 252 | 253 | ### Fire and Forget (Bad) 254 | 255 | ```typescript 256 | // ❌ NEVER: Fire and forget 257 | async function processRequest(req, res) { 258 | sendEmail(user.email); // Fires async, errors unhandled! 259 | res.json({ success: true }); 260 | } 261 | 262 | // ✅ ALWAYS: Await or handle 263 | async function processRequest(req, res) { 264 | try { 265 | await sendEmail(user.email); 266 | res.json({ success: true }); 267 | } catch (error) { 268 | Sentry.captureException(error); 269 | res.status(500).json({ error: 'Failed to send email' }); 270 | } 271 | } 272 | 273 | // ✅ OR: Intentional background task 274 | async function processRequest(req, res) { 275 | sendEmail(user.email).catch(error => { 276 | Sentry.captureException(error); 277 | }); 278 | res.json({ success: true }); 279 | } 280 | ``` 281 | 282 | ### Unhandled Rejections 283 | 284 | ```typescript 285 | // ✅ Global handler for unhandled rejections 286 | process.on('unhandledRejection', (reason, promise) => { 287 | Sentry.captureException(reason, { 288 | tags: { type: 'unhandled_rejection' } 289 | }); 290 | console.error('Unhandled Rejection:', reason); 291 | }); 292 | 293 | process.on('uncaughtException', (error) => { 294 | Sentry.captureException(error, { 295 | tags: { type: 'uncaught_exception' } 296 | }); 297 | console.error('Uncaught Exception:', error); 298 | process.exit(1); 299 | }); 300 | ``` 301 | 302 | --- 303 | 304 | **Related Files:** 305 | - [SKILL.md](SKILL.md) 306 | - [sentry-and-monitoring.md](sentry-and-monitoring.md) 307 | - [complete-examples.md](complete-examples.md) 308 | -------------------------------------------------------------------------------- /.claude/hooks/error-handling-reminder.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { readFileSync, existsSync } from 'fs'; 3 | import { join } from 'path'; 4 | 5 | interface HookInput { 6 | session_id: string; 7 | transcript_path: string; 8 | cwd: string; 9 | permission_mode: string; 10 | hook_event_name: string; 11 | } 12 | 13 | interface EditedFile { 14 | path: string; 15 | tool: string; 16 | timestamp: string; 17 | } 18 | 19 | interface SessionTracking { 20 | edited_files: EditedFile[]; 21 | } 22 | 23 | function getFileCategory(filePath: string): 'backend' | 'frontend' | 'database' | 'other' { 24 | // Frontend detection 25 | if (filePath.includes('/frontend/') || 26 | filePath.includes('/client/') || 27 | filePath.includes('/src/components/') || 28 | filePath.includes('/src/features/')) return 'frontend'; 29 | 30 | // Backend detection (common service directories) 31 | if (filePath.includes('/src/controllers/') || 32 | filePath.includes('/src/services/') || 33 | filePath.includes('/src/routes/') || 34 | filePath.includes('/src/api/') || 35 | filePath.includes('/server/')) return 'backend'; 36 | 37 | // Database detection 38 | if (filePath.includes('/database/') || 39 | filePath.includes('/prisma/') || 40 | filePath.includes('/migrations/')) return 'database'; 41 | 42 | return 'other'; 43 | } 44 | 45 | function shouldCheckErrorHandling(filePath: string): boolean { 46 | // Skip test files, config files, and type definitions 47 | if (filePath.match(/\.(test|spec)\.(ts|tsx)$/)) return false; 48 | if (filePath.match(/\.(config|d)\.(ts|tsx)$/)) return false; 49 | if (filePath.includes('types/')) return false; 50 | if (filePath.includes('.styles.ts')) return false; 51 | 52 | // Check for code files 53 | return filePath.match(/\.(ts|tsx|js|jsx)$/) !== null; 54 | } 55 | 56 | function analyzeFileContent(filePath: string): { 57 | hasTryCatch: boolean; 58 | hasAsync: boolean; 59 | hasPrisma: boolean; 60 | hasController: boolean; 61 | hasApiCall: boolean; 62 | } { 63 | if (!existsSync(filePath)) { 64 | return { hasTryCatch: false, hasAsync: false, hasPrisma: false, hasController: false, hasApiCall: false }; 65 | } 66 | 67 | const content = readFileSync(filePath, 'utf-8'); 68 | 69 | return { 70 | hasTryCatch: /try\s*\{/.test(content), 71 | hasAsync: /async\s+/.test(content), 72 | hasPrisma: /prisma\.|PrismaService|findMany|findUnique|create\(|update\(|delete\(/i.test(content), 73 | hasController: /export class.*Controller|router\.|app\.(get|post|put|delete|patch)/.test(content), 74 | hasApiCall: /fetch\(|axios\.|apiClient\./i.test(content), 75 | }; 76 | } 77 | 78 | async function main() { 79 | try { 80 | // Read input from stdin 81 | const input = readFileSync(0, 'utf-8'); 82 | const data: HookInput = JSON.parse(input); 83 | 84 | const { session_id } = data; 85 | const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd(); 86 | 87 | // Check for edited files tracking 88 | const cacheDir = join(process.env.HOME || '/root', '.claude', 'tsc-cache', session_id); 89 | const trackingFile = join(cacheDir, 'edited-files.log'); 90 | 91 | if (!existsSync(trackingFile)) { 92 | // No files edited this session, no reminder needed 93 | process.exit(0); 94 | } 95 | 96 | // Read tracking data 97 | const trackingContent = readFileSync(trackingFile, 'utf-8'); 98 | const editedFiles = trackingContent 99 | .trim() 100 | .split('\n') 101 | .filter(line => line.length > 0) 102 | .map(line => { 103 | const [timestamp, tool, path] = line.split('\t'); 104 | return { timestamp, tool, path }; 105 | }); 106 | 107 | if (editedFiles.length === 0) { 108 | process.exit(0); 109 | } 110 | 111 | // Categorize files 112 | const categories = { 113 | backend: [] as string[], 114 | frontend: [] as string[], 115 | database: [] as string[], 116 | other: [] as string[], 117 | }; 118 | 119 | const analysisResults: Array<{ 120 | path: string; 121 | category: string; 122 | analysis: ReturnType; 123 | }> = []; 124 | 125 | for (const file of editedFiles) { 126 | if (!shouldCheckErrorHandling(file.path)) continue; 127 | 128 | const category = getFileCategory(file.path); 129 | categories[category].push(file.path); 130 | 131 | const analysis = analyzeFileContent(file.path); 132 | analysisResults.push({ path: file.path, category, analysis }); 133 | } 134 | 135 | // Check if any code that needs error handling was written 136 | const needsAttention = analysisResults.some( 137 | ({ analysis }) => 138 | analysis.hasTryCatch || 139 | analysis.hasAsync || 140 | analysis.hasPrisma || 141 | analysis.hasController || 142 | analysis.hasApiCall 143 | ); 144 | 145 | if (!needsAttention) { 146 | // No risky code patterns detected, skip reminder 147 | process.exit(0); 148 | } 149 | 150 | // Display reminder 151 | console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); 152 | console.log('📋 ERROR HANDLING SELF-CHECK'); 153 | console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); 154 | 155 | // Backend reminders 156 | if (categories.backend.length > 0) { 157 | const backendFiles = analysisResults.filter(f => f.category === 'backend'); 158 | const hasTryCatch = backendFiles.some(f => f.analysis.hasTryCatch); 159 | const hasPrisma = backendFiles.some(f => f.analysis.hasPrisma); 160 | const hasController = backendFiles.some(f => f.analysis.hasController); 161 | 162 | console.log('⚠️ Backend Changes Detected'); 163 | console.log(` ${categories.backend.length} file(s) edited\n`); 164 | 165 | if (hasTryCatch) { 166 | console.log(' ❓ Did you add Sentry.captureException() in catch blocks?'); 167 | } 168 | if (hasPrisma) { 169 | console.log(' ❓ Are Prisma operations wrapped in error handling?'); 170 | } 171 | if (hasController) { 172 | console.log(' ❓ Do controllers use BaseController.handleError()?'); 173 | } 174 | 175 | console.log('\n 💡 Backend Best Practice:'); 176 | console.log(' - All errors should be captured to Sentry'); 177 | console.log(' - Use appropriate error helpers for context'); 178 | console.log(' - Controllers should extend BaseController\n'); 179 | } 180 | 181 | // Frontend reminders 182 | if (categories.frontend.length > 0) { 183 | const frontendFiles = analysisResults.filter(f => f.category === 'frontend'); 184 | const hasApiCall = frontendFiles.some(f => f.analysis.hasApiCall); 185 | const hasTryCatch = frontendFiles.some(f => f.analysis.hasTryCatch); 186 | 187 | console.log('💡 Frontend Changes Detected'); 188 | console.log(` ${categories.frontend.length} file(s) edited\n`); 189 | 190 | if (hasApiCall) { 191 | console.log(' ❓ Do API calls show user-friendly error messages?'); 192 | } 193 | if (hasTryCatch) { 194 | console.log(' ❓ Are errors displayed to the user?'); 195 | } 196 | 197 | console.log('\n 💡 Frontend Best Practice:'); 198 | console.log(' - Use your notification system for user feedback'); 199 | console.log(' - Error boundaries for component errors'); 200 | console.log(' - Display user-friendly error messages\n'); 201 | } 202 | 203 | // Database reminders 204 | if (categories.database.length > 0) { 205 | console.log('🗄️ Database Changes Detected'); 206 | console.log(` ${categories.database.length} file(s) edited\n`); 207 | console.log(' ❓ Did you verify column names against schema?'); 208 | console.log(' ❓ Are migrations tested?\n'); 209 | } 210 | 211 | console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); 212 | console.log('💡 TIP: Disable with SKIP_ERROR_REMINDER=1'); 213 | console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); 214 | 215 | process.exit(0); 216 | } catch (err) { 217 | // Silently fail - this is just a reminder, not critical 218 | process.exit(0); 219 | } 220 | } 221 | 222 | main().catch(() => process.exit(0)); 223 | -------------------------------------------------------------------------------- /.claude/skills/frontend-dev-guidelines/resources/routing-guide.md: -------------------------------------------------------------------------------- 1 | # Routing Guide 2 | 3 | TanStack Router implementation with folder-based routing and lazy loading patterns. 4 | 5 | --- 6 | 7 | ## TanStack Router Overview 8 | 9 | **TanStack Router** with file-based routing: 10 | - Folder structure defines routes 11 | - Lazy loading for code splitting 12 | - Type-safe routing 13 | - Breadcrumb loaders 14 | 15 | --- 16 | 17 | ## Folder-Based Routing 18 | 19 | ### Directory Structure 20 | 21 | ``` 22 | routes/ 23 | __root.tsx # Root layout 24 | index.tsx # Home route (/) 25 | posts/ 26 | index.tsx # /posts 27 | create/ 28 | index.tsx # /posts/create 29 | $postId.tsx # /posts/:postId (dynamic) 30 | comments/ 31 | index.tsx # /comments 32 | ``` 33 | 34 | **Pattern**: 35 | - `index.tsx` = Route at that path 36 | - `$param.tsx` = Dynamic parameter 37 | - Nested folders = Nested routes 38 | 39 | --- 40 | 41 | ## Basic Route Pattern 42 | 43 | ### Example from posts/index.tsx 44 | 45 | ```typescript 46 | /** 47 | * Posts route component 48 | * Displays the main blog posts list 49 | */ 50 | 51 | import { createFileRoute } from '@tanstack/react-router'; 52 | import { lazy } from 'react'; 53 | 54 | // Lazy load the page component 55 | const PostsList = lazy(() => 56 | import('@/features/posts/components/PostsList').then( 57 | (module) => ({ default: module.PostsList }), 58 | ), 59 | ); 60 | 61 | export const Route = createFileRoute('/posts/')({ 62 | component: PostsPage, 63 | // Define breadcrumb data 64 | loader: () => ({ 65 | crumb: 'Posts', 66 | }), 67 | }); 68 | 69 | function PostsPage() { 70 | return ( 71 | 75 | ); 76 | } 77 | 78 | export default PostsPage; 79 | ``` 80 | 81 | **Key Points:** 82 | - Lazy load heavy components 83 | - `createFileRoute` with route path 84 | - `loader` for breadcrumb data 85 | - Page component renders content 86 | - Export both Route and component 87 | 88 | --- 89 | 90 | ## Lazy Loading Routes 91 | 92 | ### Named Export Pattern 93 | 94 | ```typescript 95 | import { lazy } from 'react'; 96 | 97 | // For named exports, use .then() to map to default 98 | const MyPage = lazy(() => 99 | import('@/features/my-feature/components/MyPage').then( 100 | (module) => ({ default: module.MyPage }) 101 | ) 102 | ); 103 | ``` 104 | 105 | ### Default Export Pattern 106 | 107 | ```typescript 108 | import { lazy } from 'react'; 109 | 110 | // For default exports, simpler syntax 111 | const MyPage = lazy(() => import('@/features/my-feature/components/MyPage')); 112 | ``` 113 | 114 | ### Why Lazy Load Routes? 115 | 116 | - Code splitting - smaller initial bundle 117 | - Faster initial page load 118 | - Load route code only when navigated to 119 | - Better performance 120 | 121 | --- 122 | 123 | ## createFileRoute 124 | 125 | ### Basic Configuration 126 | 127 | ```typescript 128 | export const Route = createFileRoute('/my-route/')({ 129 | component: MyRoutePage, 130 | }); 131 | 132 | function MyRoutePage() { 133 | return
My Route Content
; 134 | } 135 | ``` 136 | 137 | ### With Breadcrumb Loader 138 | 139 | ```typescript 140 | export const Route = createFileRoute('/my-route/')({ 141 | component: MyRoutePage, 142 | loader: () => ({ 143 | crumb: 'My Route Title', 144 | }), 145 | }); 146 | ``` 147 | 148 | Breadcrumb appears in navigation/app bar automatically. 149 | 150 | ### With Data Loader 151 | 152 | ```typescript 153 | export const Route = createFileRoute('/my-route/')({ 154 | component: MyRoutePage, 155 | loader: async () => { 156 | // Can prefetch data here 157 | const data = await api.getData(); 158 | return { crumb: 'My Route', data }; 159 | }, 160 | }); 161 | ``` 162 | 163 | ### With Search Params 164 | 165 | ```typescript 166 | export const Route = createFileRoute('/search/')({ 167 | component: SearchPage, 168 | validateSearch: (search: Record) => { 169 | return { 170 | query: (search.query as string) || '', 171 | page: Number(search.page) || 1, 172 | }; 173 | }, 174 | }); 175 | 176 | function SearchPage() { 177 | const { query, page } = Route.useSearch(); 178 | // Use query and page 179 | } 180 | ``` 181 | 182 | --- 183 | 184 | ## Dynamic Routes 185 | 186 | ### Parameter Routes 187 | 188 | ```typescript 189 | // routes/users/$userId.tsx 190 | 191 | export const Route = createFileRoute('/users/$userId')({ 192 | component: UserPage, 193 | }); 194 | 195 | function UserPage() { 196 | const { userId } = Route.useParams(); 197 | 198 | return ; 199 | } 200 | ``` 201 | 202 | ### Multiple Parameters 203 | 204 | ```typescript 205 | // routes/posts/$postId/comments/$commentId.tsx 206 | 207 | export const Route = createFileRoute('/posts/$postId/comments/$commentId')({ 208 | component: CommentPage, 209 | }); 210 | 211 | function CommentPage() { 212 | const { postId, commentId } = Route.useParams(); 213 | 214 | return ; 215 | } 216 | ``` 217 | 218 | --- 219 | 220 | ## Navigation 221 | 222 | ### Programmatic Navigation 223 | 224 | ```typescript 225 | import { useNavigate } from '@tanstack/react-router'; 226 | 227 | export const MyComponent: React.FC = () => { 228 | const navigate = useNavigate(); 229 | 230 | const handleClick = () => { 231 | navigate({ to: '/posts' }); 232 | }; 233 | 234 | return ; 235 | }; 236 | ``` 237 | 238 | ### With Parameters 239 | 240 | ```typescript 241 | const handleNavigate = () => { 242 | navigate({ 243 | to: '/users/$userId', 244 | params: { userId: '123' }, 245 | }); 246 | }; 247 | ``` 248 | 249 | ### With Search Params 250 | 251 | ```typescript 252 | const handleSearch = () => { 253 | navigate({ 254 | to: '/search', 255 | search: { query: 'test', page: 1 }, 256 | }); 257 | }; 258 | ``` 259 | 260 | --- 261 | 262 | ## Route Layout Pattern 263 | 264 | ### Root Layout (__root.tsx) 265 | 266 | ```typescript 267 | import { createRootRoute, Outlet } from '@tanstack/react-router'; 268 | import { Box } from '@mui/material'; 269 | import { CustomAppBar } from '~components/CustomAppBar'; 270 | 271 | export const Route = createRootRoute({ 272 | component: RootLayout, 273 | }); 274 | 275 | function RootLayout() { 276 | return ( 277 | 278 | 279 | 280 | {/* Child routes render here */} 281 | 282 | 283 | ); 284 | } 285 | ``` 286 | 287 | ### Nested Layouts 288 | 289 | ```typescript 290 | // routes/dashboard/index.tsx 291 | export const Route = createFileRoute('/dashboard/')({ 292 | component: DashboardLayout, 293 | }); 294 | 295 | function DashboardLayout() { 296 | return ( 297 | 298 | 299 | 300 | {/* Nested routes */} 301 | 302 | 303 | ); 304 | } 305 | ``` 306 | 307 | --- 308 | 309 | ## Complete Route Example 310 | 311 | ```typescript 312 | /** 313 | * User profile route 314 | * Path: /users/:userId 315 | */ 316 | 317 | import { createFileRoute } from '@tanstack/react-router'; 318 | import { lazy } from 'react'; 319 | import { SuspenseLoader } from '~components/SuspenseLoader'; 320 | 321 | // Lazy load heavy component 322 | const UserProfile = lazy(() => 323 | import('@/features/users/components/UserProfile').then( 324 | (module) => ({ default: module.UserProfile }) 325 | ) 326 | ); 327 | 328 | export const Route = createFileRoute('/users/$userId')({ 329 | component: UserPage, 330 | loader: () => ({ 331 | crumb: 'User Profile', 332 | }), 333 | }); 334 | 335 | function UserPage() { 336 | const { userId } = Route.useParams(); 337 | 338 | return ( 339 | 340 | 341 | 342 | ); 343 | } 344 | 345 | export default UserPage; 346 | ``` 347 | 348 | --- 349 | 350 | ## Summary 351 | 352 | **Routing Checklist:** 353 | - ✅ Folder-based: `routes/my-route/index.tsx` 354 | - ✅ Lazy load components: `React.lazy(() => import())` 355 | - ✅ Use `createFileRoute` with route path 356 | - ✅ Add breadcrumb in `loader` function 357 | - ✅ Wrap in `SuspenseLoader` for loading states 358 | - ✅ Use `Route.useParams()` for dynamic params 359 | - ✅ Use `useNavigate()` for programmatic navigation 360 | 361 | **See Also:** 362 | - [component-patterns.md](component-patterns.md) - Lazy loading patterns 363 | - [loading-and-error-states.md](loading-and-error-states.md) - SuspenseLoader usage 364 | - [complete-examples.md](complete-examples.md) - Full route examples -------------------------------------------------------------------------------- /.claude/skills/skill-developer/HOOK_MECHANISMS.md: -------------------------------------------------------------------------------- 1 | # Hook Mechanisms - Deep Dive 2 | 3 | Technical deep dive into how the UserPromptSubmit and PreToolUse hooks work. 4 | 5 | ## Table of Contents 6 | 7 | - [UserPromptSubmit Hook Flow](#userpromptsubmit-hook-flow) 8 | - [PreToolUse Hook Flow](#pretooluse-hook-flow) 9 | - [Exit Code Behavior (CRITICAL)](#exit-code-behavior-critical) 10 | - [Session State Management](#session-state-management) 11 | - [Performance Considerations](#performance-considerations) 12 | 13 | --- 14 | 15 | ## UserPromptSubmit Hook Flow 16 | 17 | ### Execution Sequence 18 | 19 | ``` 20 | User submits prompt 21 | ↓ 22 | .claude/settings.json registers hook 23 | ↓ 24 | skill-activation-prompt.sh executes 25 | ↓ 26 | npx tsx skill-activation-prompt.ts 27 | ↓ 28 | Hook reads stdin (JSON with prompt) 29 | ↓ 30 | Loads skill-rules.json 31 | ↓ 32 | Matches keywords + intent patterns 33 | ↓ 34 | Groups matches by priority (critical → high → medium → low) 35 | ↓ 36 | Outputs formatted message to stdout 37 | ↓ 38 | stdout becomes context for Claude (injected before prompt) 39 | ↓ 40 | Claude sees: [skill suggestion] + user's prompt 41 | ``` 42 | 43 | ### Key Points 44 | 45 | - **Exit code**: Always 0 (allow) 46 | - **stdout**: → Claude's context (injected as system message) 47 | - **Timing**: Runs BEFORE Claude processes prompt 48 | - **Behavior**: Non-blocking, advisory only 49 | - **Purpose**: Make Claude aware of relevant skills 50 | 51 | ### Input Format 52 | 53 | ```json 54 | { 55 | "session_id": "abc-123", 56 | "transcript_path": "/path/to/transcript.json", 57 | "cwd": "/root/git/your-project", 58 | "permission_mode": "normal", 59 | "hook_event_name": "UserPromptSubmit", 60 | "prompt": "how does the layout system work?" 61 | } 62 | ``` 63 | 64 | ### Output Format (to stdout) 65 | 66 | ``` 67 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 68 | 🎯 SKILL ACTIVATION CHECK 69 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 70 | 71 | 📚 RECOMMENDED SKILLS: 72 | → project-catalog-developer 73 | 74 | ACTION: Use Skill tool BEFORE responding 75 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 76 | ``` 77 | 78 | Claude sees this output as additional context before processing the user's prompt. 79 | 80 | --- 81 | 82 | ## PreToolUse Hook Flow 83 | 84 | ### Execution Sequence 85 | 86 | ``` 87 | Claude calls Edit/Write tool 88 | ↓ 89 | .claude/settings.json registers hook (matcher: Edit|Write) 90 | ↓ 91 | skill-verification-guard.sh executes 92 | ↓ 93 | npx tsx skill-verification-guard.ts 94 | ↓ 95 | Hook reads stdin (JSON with tool_name, tool_input) 96 | ↓ 97 | Loads skill-rules.json 98 | ↓ 99 | Checks file path patterns (glob matching) 100 | ↓ 101 | Reads file for content patterns (if file exists) 102 | ↓ 103 | Checks session state (was skill already used?) 104 | ↓ 105 | Checks skip conditions (file markers, env vars) 106 | ↓ 107 | IF MATCHED AND NOT SKIPPED: 108 | Update session state (mark skill as enforced) 109 | Output block message to stderr 110 | Exit with code 2 (BLOCK) 111 | ELSE: 112 | Exit with code 0 (ALLOW) 113 | ↓ 114 | IF BLOCKED: 115 | stderr → Claude sees message 116 | Edit/Write tool does NOT execute 117 | Claude must use skill and retry 118 | IF ALLOWED: 119 | Tool executes normally 120 | ``` 121 | 122 | ### Key Points 123 | 124 | - **Exit code 2**: BLOCK (stderr → Claude) 125 | - **Exit code 0**: ALLOW 126 | - **Timing**: Runs BEFORE tool execution 127 | - **Session tracking**: Prevents repeated blocks in same session 128 | - **Fail open**: On errors, allows operation (don't break workflow) 129 | - **Purpose**: Enforce critical guardrails 130 | 131 | ### Input Format 132 | 133 | ```json 134 | { 135 | "session_id": "abc-123", 136 | "transcript_path": "/path/to/transcript.json", 137 | "cwd": "/root/git/your-project", 138 | "permission_mode": "normal", 139 | "hook_event_name": "PreToolUse", 140 | "tool_name": "Edit", 141 | "tool_input": { 142 | "file_path": "/root/git/your-project/form/src/services/user.ts", 143 | "old_string": "...", 144 | "new_string": "..." 145 | } 146 | } 147 | ``` 148 | 149 | ### Output Format (to stderr when blocked) 150 | 151 | ``` 152 | ⚠️ BLOCKED - Database Operation Detected 153 | 154 | 📋 REQUIRED ACTION: 155 | 1. Use Skill tool: 'database-verification' 156 | 2. Verify ALL table and column names against schema 157 | 3. Check database structure with DESCRIBE commands 158 | 4. Then retry this edit 159 | 160 | Reason: Prevent column name errors in Prisma queries 161 | File: form/src/services/user.ts 162 | 163 | 💡 TIP: Add '// @skip-validation' comment to skip future checks 164 | ``` 165 | 166 | Claude receives this message and understands it needs to use the skill before retrying the edit. 167 | 168 | --- 169 | 170 | ## Exit Code Behavior (CRITICAL) 171 | 172 | ### Exit Code Reference Table 173 | 174 | | Exit Code | stdout | stderr | Tool Execution | Claude Sees | 175 | |-----------|--------|--------|----------------|-------------| 176 | | 0 (UserPromptSubmit) | → Context | → User only | N/A | stdout content | 177 | | 0 (PreToolUse) | → User only | → User only | **Proceeds** | Nothing | 178 | | 2 (PreToolUse) | → User only | → **CLAUDE** | **BLOCKED** | stderr content | 179 | | Other | → User only | → User only | Blocked | Nothing | 180 | 181 | ### Why Exit Code 2 Matters 182 | 183 | This is THE critical mechanism for enforcement: 184 | 185 | 1. **Only way** to send message to Claude from PreToolUse 186 | 2. stderr content is "fed back to Claude automatically" 187 | 3. Claude sees the block message and understands what to do 188 | 4. Tool execution is prevented 189 | 5. Critical for enforcement of guardrails 190 | 191 | ### Example Conversation Flow 192 | 193 | ``` 194 | User: "Add a new user service with Prisma" 195 | 196 | Claude: "I'll create the user service..." 197 | [Attempts to Edit form/src/services/user.ts] 198 | 199 | PreToolUse Hook: [Exit code 2] 200 | stderr: "⚠️ BLOCKED - Use database-verification" 201 | 202 | Claude sees error, responds: 203 | "I need to verify the database schema first." 204 | [Uses Skill tool: database-verification] 205 | [Verifies column names] 206 | [Retries Edit - now allowed (session tracking)] 207 | ``` 208 | 209 | --- 210 | 211 | ## Session State Management 212 | 213 | ### Purpose 214 | 215 | Prevent repeated nagging in the same session - once Claude uses a skill, don't block again. 216 | 217 | ### State File Location 218 | 219 | `.claude/hooks/state/skills-used-{session_id}.json` 220 | 221 | ### State File Structure 222 | 223 | ```json 224 | { 225 | "skills_used": [ 226 | "database-verification", 227 | "error-tracking" 228 | ], 229 | "files_verified": [] 230 | } 231 | ``` 232 | 233 | ### How It Works 234 | 235 | 1. **First edit** of file with Prisma: 236 | - Hook blocks with exit code 2 237 | - Updates session state: adds "database-verification" to skills_used 238 | - Claude sees message, uses skill 239 | 240 | 2. **Second edit** (same session): 241 | - Hook checks session state 242 | - Finds "database-verification" in skills_used 243 | - Exits with code 0 (allow) 244 | - No message to Claude 245 | 246 | 3. **Different session**: 247 | - New session ID = new state file 248 | - Hook blocks again 249 | 250 | ### Limitation 251 | 252 | The hook cannot detect when the skill is *actually* invoked - it just blocks once per session per skill. This means: 253 | 254 | - If Claude doesn't use the skill but makes a different edit, it won't block again 255 | - Trust that Claude follows the instruction 256 | - Future enhancement: detect actual Skill tool usage 257 | 258 | --- 259 | 260 | ## Performance Considerations 261 | 262 | ### Target Metrics 263 | 264 | - **UserPromptSubmit**: < 100ms 265 | - **PreToolUse**: < 200ms 266 | 267 | ### Performance Bottlenecks 268 | 269 | 1. **Loading skill-rules.json** (every execution) 270 | - Future: Cache in memory 271 | - Future: Watch for changes, reload only when needed 272 | 273 | 2. **Reading file content** (PreToolUse) 274 | - Only when contentPatterns configured 275 | - Only if file exists 276 | - Can be slow for large files 277 | 278 | 3. **Glob matching** (PreToolUse) 279 | - Regex compilation for each pattern 280 | - Future: Compile once, cache 281 | 282 | 4. **Regex matching** (Both hooks) 283 | - Intent patterns (UserPromptSubmit) 284 | - Content patterns (PreToolUse) 285 | - Future: Lazy compile, cache compiled regexes 286 | 287 | ### Optimization Strategies 288 | 289 | **Reduce patterns:** 290 | - Use more specific patterns (fewer to check) 291 | - Combine similar patterns where possible 292 | 293 | **File path patterns:** 294 | - More specific = fewer files to check 295 | - Example: `form/src/services/**` better than `form/**` 296 | 297 | **Content patterns:** 298 | - Only add when truly necessary 299 | - Simpler regex = faster matching 300 | 301 | --- 302 | 303 | **Related Files:** 304 | - [SKILL.md](SKILL.md) - Main skill guide 305 | - [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Debug hook issues 306 | - [SKILL_RULES_REFERENCE.md](SKILL_RULES_REFERENCE.md) - Configuration reference 307 | -------------------------------------------------------------------------------- /.claude/skills/skill-developer/TRIGGER_TYPES.md: -------------------------------------------------------------------------------- 1 | # Trigger Types - Complete Guide 2 | 3 | Complete reference for configuring skill triggers in Claude Code's skill auto-activation system. 4 | 5 | ## Table of Contents 6 | 7 | - [Keyword Triggers (Explicit)](#keyword-triggers-explicit) 8 | - [Intent Pattern Triggers (Implicit)](#intent-pattern-triggers-implicit) 9 | - [File Path Triggers](#file-path-triggers) 10 | - [Content Pattern Triggers](#content-pattern-triggers) 11 | - [Best Practices Summary](#best-practices-summary) 12 | 13 | --- 14 | 15 | ## Keyword Triggers (Explicit) 16 | 17 | ### How It Works 18 | 19 | Case-insensitive substring matching in user's prompt. 20 | 21 | ### Use For 22 | 23 | Topic-based activation where user explicitly mentions the subject. 24 | 25 | ### Configuration 26 | 27 | ```json 28 | "promptTriggers": { 29 | "keywords": ["layout", "grid", "toolbar", "submission"] 30 | } 31 | ``` 32 | 33 | ### Example 34 | 35 | - User prompt: "how does the **layout** system work?" 36 | - Matches: "layout" keyword 37 | - Activates: `project-catalog-developer` 38 | 39 | ### Best Practices 40 | 41 | - Use specific, unambiguous terms 42 | - Include common variations ("layout", "layout system", "grid layout") 43 | - Avoid overly generic words ("system", "work", "create") 44 | - Test with real prompts 45 | 46 | --- 47 | 48 | ## Intent Pattern Triggers (Implicit) 49 | 50 | ### How It Works 51 | 52 | Regex pattern matching to detect user's intent even when they don't mention the topic explicitly. 53 | 54 | ### Use For 55 | 56 | Action-based activation where user describes what they want to do rather than the specific topic. 57 | 58 | ### Configuration 59 | 60 | ```json 61 | "promptTriggers": { 62 | "intentPatterns": [ 63 | "(create|add|implement).*?(feature|endpoint)", 64 | "(how does|explain).*?(layout|workflow)" 65 | ] 66 | } 67 | ``` 68 | 69 | ### Examples 70 | 71 | **Database Work:** 72 | - User prompt: "add user tracking feature" 73 | - Matches: `(add).*?(feature)` 74 | - Activates: `database-verification`, `error-tracking` 75 | 76 | **Component Creation:** 77 | - User prompt: "create a dashboard widget" 78 | - Matches: `(create).*?(component)` (if component in pattern) 79 | - Activates: `frontend-dev-guidelines` 80 | 81 | ### Best Practices 82 | 83 | - Capture common action verbs: `(create|add|modify|build|implement)` 84 | - Include domain-specific nouns: `(feature|endpoint|component|workflow)` 85 | - Use non-greedy matching: `.*?` instead of `.*` 86 | - Test patterns thoroughly with regex tester (https://regex101.com/) 87 | - Don't make patterns too broad (causes false positives) 88 | - Don't make patterns too specific (causes false negatives) 89 | 90 | ### Common Pattern Examples 91 | 92 | ```regex 93 | # Database Work 94 | (add|create|implement).*?(user|login|auth|feature) 95 | 96 | # Explanations 97 | (how does|explain|what is|describe).*? 98 | 99 | # Frontend Work 100 | (create|add|make|build).*?(component|UI|page|modal|dialog) 101 | 102 | # Error Handling 103 | (fix|handle|catch|debug).*?(error|exception|bug) 104 | 105 | # Workflow Operations 106 | (create|add|modify).*?(workflow|step|branch|condition) 107 | ``` 108 | 109 | --- 110 | 111 | ## File Path Triggers 112 | 113 | ### How It Works 114 | 115 | Glob pattern matching against the file path being edited. 116 | 117 | ### Use For 118 | 119 | Domain/area-specific activation based on file location in the project. 120 | 121 | ### Configuration 122 | 123 | ```json 124 | "fileTriggers": { 125 | "pathPatterns": [ 126 | "frontend/src/**/*.tsx", 127 | "form/src/**/*.ts" 128 | ], 129 | "pathExclusions": [ 130 | "**/*.test.ts", 131 | "**/*.spec.ts" 132 | ] 133 | } 134 | ``` 135 | 136 | ### Glob Pattern Syntax 137 | 138 | - `**` = Any number of directories (including zero) 139 | - `*` = Any characters within a directory name 140 | - Examples: 141 | - `frontend/src/**/*.tsx` = All .tsx files in frontend/src and subdirs 142 | - `**/schema.prisma` = schema.prisma anywhere in project 143 | - `form/src/**/*.ts` = All .ts files in form/src subdirs 144 | 145 | ### Example 146 | 147 | - File being edited: `frontend/src/components/Dashboard.tsx` 148 | - Matches: `frontend/src/**/*.tsx` 149 | - Activates: `frontend-dev-guidelines` 150 | 151 | ### Best Practices 152 | 153 | - Be specific to avoid false positives 154 | - Use exclusions for test files: `**/*.test.ts` 155 | - Consider subdirectory structure 156 | - Test patterns with actual file paths 157 | - Use narrower patterns when possible: `form/src/services/**` not `form/**` 158 | 159 | ### Common Path Patterns 160 | 161 | ```glob 162 | # Frontend 163 | frontend/src/**/*.tsx # All React components 164 | frontend/src/**/*.ts # All TypeScript files 165 | frontend/src/components/** # Only components directory 166 | 167 | # Backend Services 168 | form/src/**/*.ts # Form service 169 | email/src/**/*.ts # Email service 170 | users/src/**/*.ts # Users service 171 | 172 | # Database 173 | **/schema.prisma # Prisma schema (anywhere) 174 | **/migrations/**/*.sql # Migration files 175 | database/src/**/*.ts # Database scripts 176 | 177 | # Workflows 178 | form/src/workflow/**/*.ts # Workflow engine 179 | form/src/workflow-definitions/**/*.json # Workflow definitions 180 | 181 | # Test Exclusions 182 | **/*.test.ts # TypeScript tests 183 | **/*.test.tsx # React component tests 184 | **/*.spec.ts # Spec files 185 | ``` 186 | 187 | --- 188 | 189 | ## Content Pattern Triggers 190 | 191 | ### How It Works 192 | 193 | Regex pattern matching against the file's actual content (what's inside the file). 194 | 195 | ### Use For 196 | 197 | Technology-specific activation based on what the code imports or uses (Prisma, controllers, specific libraries). 198 | 199 | ### Configuration 200 | 201 | ```json 202 | "fileTriggers": { 203 | "contentPatterns": [ 204 | "import.*[Pp]risma", 205 | "PrismaService", 206 | "\\.findMany\\(", 207 | "\\.create\\(" 208 | ] 209 | } 210 | ``` 211 | 212 | ### Examples 213 | 214 | **Prisma Detection:** 215 | - File contains: `import { PrismaService } from '@project/database'` 216 | - Matches: `import.*[Pp]risma` 217 | - Activates: `database-verification` 218 | 219 | **Controller Detection:** 220 | - File contains: `export class UserController {` 221 | - Matches: `export class.*Controller` 222 | - Activates: `error-tracking` 223 | 224 | ### Best Practices 225 | 226 | - Match imports: `import.*[Pp]risma` (case-insensitive with [Pp]) 227 | - Escape special regex chars: `\\.findMany\\(` not `.findMany(` 228 | - Patterns use case-insensitive flag 229 | - Test against real file content 230 | - Make patterns specific enough to avoid false matches 231 | 232 | ### Common Content Patterns 233 | 234 | ```regex 235 | # Prisma/Database 236 | import.*[Pp]risma # Prisma imports 237 | PrismaService # PrismaService usage 238 | prisma\. # prisma.something 239 | \.findMany\( # Prisma query methods 240 | \.create\( 241 | \.update\( 242 | \.delete\( 243 | 244 | # Controllers/Routes 245 | export class.*Controller # Controller classes 246 | router\. # Express router 247 | app\.(get|post|put|delete|patch) # Express app routes 248 | 249 | # Error Handling 250 | try\s*\{ # Try blocks 251 | catch\s*\( # Catch blocks 252 | throw new # Throw statements 253 | 254 | # React/Components 255 | export.*React\.FC # React functional components 256 | export default function.* # Default function exports 257 | useState|useEffect # React hooks 258 | ``` 259 | 260 | --- 261 | 262 | ## Best Practices Summary 263 | 264 | ### DO: 265 | ✅ Use specific, unambiguous keywords 266 | ✅ Test all patterns with real examples 267 | ✅ Include common variations 268 | ✅ Use non-greedy regex: `.*?` 269 | ✅ Escape special characters in content patterns 270 | ✅ Add exclusions for test files 271 | ✅ Make file path patterns narrow and specific 272 | 273 | ### DON'T: 274 | ❌ Use overly generic keywords ("system", "work") 275 | ❌ Make intent patterns too broad (false positives) 276 | ❌ Make patterns too specific (false negatives) 277 | ❌ Forget to test with regex tester (https://regex101.com/) 278 | ❌ Use greedy regex: `.*` instead of `.*?` 279 | ❌ Match too broadly in file paths 280 | 281 | ### Testing Your Triggers 282 | 283 | **Test keyword/intent triggers:** 284 | ```bash 285 | echo '{"session_id":"test","prompt":"your test prompt"}' | \ 286 | npx tsx .claude/hooks/skill-activation-prompt.ts 287 | ``` 288 | 289 | **Test file path/content triggers:** 290 | ```bash 291 | cat <<'EOF' | npx tsx .claude/hooks/skill-verification-guard.ts 292 | { 293 | "session_id": "test", 294 | "tool_name": "Edit", 295 | "tool_input": {"file_path": "/path/to/test/file.ts"} 296 | } 297 | EOF 298 | ``` 299 | 300 | --- 301 | 302 | **Related Files:** 303 | - [SKILL.md](SKILL.md) - Main skill guide 304 | - [SKILL_RULES_REFERENCE.md](SKILL_RULES_REFERENCE.md) - Complete skill-rules.json schema 305 | - [PATTERNS_LIBRARY.md](PATTERNS_LIBRARY.md) - Ready-to-use pattern library 306 | -------------------------------------------------------------------------------- /.claude/skills/backend-dev-guidelines/resources/sentry-and-monitoring.md: -------------------------------------------------------------------------------- 1 | # Sentry Integration and Monitoring 2 | 3 | Complete guide to error tracking and performance monitoring with Sentry v8. 4 | 5 | ## Table of Contents 6 | 7 | - [Core Principles](#core-principles) 8 | - [Sentry Initialization](#sentry-initialization) 9 | - [Error Capture Patterns](#error-capture-patterns) 10 | - [Performance Monitoring](#performance-monitoring) 11 | - [Cron Job Monitoring](#cron-job-monitoring) 12 | - [Error Context Best Practices](#error-context-best-practices) 13 | - [Common Mistakes](#common-mistakes) 14 | 15 | --- 16 | 17 | ## Core Principles 18 | 19 | **MANDATORY**: All errors MUST be captured to Sentry. No exceptions. 20 | 21 | **ALL ERRORS MUST BE CAPTURED** - Use Sentry v8 with comprehensive error tracking across all services. 22 | 23 | --- 24 | 25 | ## Sentry Initialization 26 | 27 | ### instrument.ts Pattern 28 | 29 | **Location:** `src/instrument.ts` (MUST be first import in server.ts and all cron jobs) 30 | 31 | **Template for Microservices:** 32 | 33 | ```typescript 34 | import * as Sentry from '@sentry/node'; 35 | import * as fs from 'fs'; 36 | import * as path from 'path'; 37 | import * as ini from 'ini'; 38 | 39 | const sentryConfigPath = path.join(__dirname, '../sentry.ini'); 40 | const sentryConfig = ini.parse(fs.readFileSync(sentryConfigPath, 'utf-8')); 41 | 42 | Sentry.init({ 43 | dsn: sentryConfig.sentry?.dsn, 44 | environment: process.env.NODE_ENV || 'development', 45 | tracesSampleRate: parseFloat(sentryConfig.sentry?.tracesSampleRate || '0.1'), 46 | profilesSampleRate: parseFloat(sentryConfig.sentry?.profilesSampleRate || '0.1'), 47 | 48 | integrations: [ 49 | ...Sentry.getDefaultIntegrations({}), 50 | Sentry.extraErrorDataIntegration({ depth: 5 }), 51 | Sentry.localVariablesIntegration(), 52 | Sentry.requestDataIntegration({ 53 | include: { 54 | cookies: false, 55 | data: true, 56 | headers: true, 57 | ip: true, 58 | query_string: true, 59 | url: true, 60 | user: { id: true, email: true, username: true }, 61 | }, 62 | }), 63 | Sentry.consoleIntegration(), 64 | Sentry.contextLinesIntegration(), 65 | Sentry.prismaIntegration(), 66 | ], 67 | 68 | beforeSend(event, hint) { 69 | // Filter health checks 70 | if (event.request?.url?.includes('/healthcheck')) { 71 | return null; 72 | } 73 | 74 | // Scrub sensitive headers 75 | if (event.request?.headers) { 76 | delete event.request.headers['authorization']; 77 | delete event.request.headers['cookie']; 78 | } 79 | 80 | // Mask emails for PII 81 | if (event.user?.email) { 82 | event.user.email = event.user.email.replace(/^(.{2}).*(@.*)$/, '$1***$2'); 83 | } 84 | 85 | return event; 86 | }, 87 | 88 | ignoreErrors: [ 89 | /^Invalid JWT/, 90 | /^JWT expired/, 91 | 'NetworkError', 92 | ], 93 | }); 94 | 95 | // Set service context 96 | Sentry.setTags({ 97 | service: 'form', 98 | version: '1.0.1', 99 | }); 100 | 101 | Sentry.setContext('runtime', { 102 | node_version: process.version, 103 | platform: process.platform, 104 | }); 105 | ``` 106 | 107 | **Critical Points:** 108 | - PII protection built-in (beforeSend) 109 | - Filter non-critical errors 110 | - Comprehensive integrations 111 | - Prisma instrumentation 112 | - Service-specific tagging 113 | 114 | --- 115 | 116 | ## Error Capture Patterns 117 | 118 | ### 1. BaseController Pattern 119 | 120 | ```typescript 121 | // Use BaseController.handleError 122 | protected handleError(error: unknown, res: Response, context: string, statusCode = 500): void { 123 | Sentry.withScope((scope) => { 124 | scope.setTag('controller', this.constructor.name); 125 | scope.setTag('operation', context); 126 | scope.setUser({ id: res.locals?.claims?.userId }); 127 | Sentry.captureException(error); 128 | }); 129 | 130 | res.status(statusCode).json({ 131 | success: false, 132 | error: { message: error instanceof Error ? error.message : 'Error occurred' } 133 | }); 134 | } 135 | ``` 136 | 137 | ### 2. Workflow Error Handling 138 | 139 | ```typescript 140 | import { SentryHelper } from '../utils/sentryHelper'; 141 | 142 | try { 143 | await businessOperation(); 144 | } catch (error) { 145 | SentryHelper.captureOperationError(error, { 146 | operationType: 'POST_CREATION', 147 | entityId: 123, 148 | userId: 'user-123', 149 | operation: 'createPost', 150 | }); 151 | throw error; 152 | } 153 | ``` 154 | 155 | ### 3. Service Layer Error Handling 156 | 157 | ```typescript 158 | try { 159 | await someOperation(); 160 | } catch (error) { 161 | Sentry.captureException(error, { 162 | tags: { 163 | service: 'form', 164 | operation: 'someOperation' 165 | }, 166 | extra: { 167 | userId: currentUser.id, 168 | entityId: 123 169 | } 170 | }); 171 | throw error; 172 | } 173 | ``` 174 | 175 | --- 176 | 177 | ## Performance Monitoring 178 | 179 | ### Database Performance Tracking 180 | 181 | ```typescript 182 | import { DatabasePerformanceMonitor } from '../utils/databasePerformance'; 183 | 184 | const result = await DatabasePerformanceMonitor.withPerformanceTracking( 185 | 'findMany', 186 | 'UserProfile', 187 | async () => { 188 | return await PrismaService.main.userProfile.findMany({ take: 5 }); 189 | } 190 | ); 191 | ``` 192 | 193 | ### API Endpoint Spans 194 | 195 | ```typescript 196 | router.post('/operation', async (req, res) => { 197 | return await Sentry.startSpan({ 198 | name: 'operation.execute', 199 | op: 'http.server', 200 | attributes: { 201 | 'http.method': 'POST', 202 | 'http.route': '/operation' 203 | } 204 | }, async () => { 205 | const result = await performOperation(); 206 | res.json(result); 207 | }); 208 | }); 209 | ``` 210 | 211 | --- 212 | 213 | ## Cron Job Monitoring 214 | 215 | ### Mandatory Pattern 216 | 217 | ```typescript 218 | #!/usr/bin/env node 219 | import '../instrument'; // FIRST LINE after shebang 220 | import * as Sentry from '@sentry/node'; 221 | 222 | async function main() { 223 | return await Sentry.startSpan({ 224 | name: 'cron.job-name', 225 | op: 'cron', 226 | attributes: { 227 | 'cron.job': 'job-name', 228 | 'cron.startTime': new Date().toISOString(), 229 | } 230 | }, async () => { 231 | try { 232 | // Cron job logic here 233 | } catch (error) { 234 | Sentry.captureException(error, { 235 | tags: { 236 | 'cron.job': 'job-name', 237 | 'error.type': 'execution_error' 238 | } 239 | }); 240 | console.error('[Cron] Error:', error); 241 | process.exit(1); 242 | } 243 | }); 244 | } 245 | 246 | main().then(() => { 247 | console.log('[Cron] Completed successfully'); 248 | process.exit(0); 249 | }).catch((error) => { 250 | console.error('[Cron] Fatal error:', error); 251 | process.exit(1); 252 | }); 253 | ``` 254 | 255 | --- 256 | 257 | ## Error Context Best Practices 258 | 259 | ### Rich Context Example 260 | 261 | ```typescript 262 | Sentry.withScope((scope) => { 263 | // User context 264 | scope.setUser({ 265 | id: user.id, 266 | email: user.email, 267 | username: user.username 268 | }); 269 | 270 | // Tags for filtering 271 | scope.setTag('service', 'form'); 272 | scope.setTag('endpoint', req.path); 273 | scope.setTag('method', req.method); 274 | 275 | // Structured context 276 | scope.setContext('operation', { 277 | type: 'workflow.complete', 278 | workflowId: 123, 279 | stepId: 456 280 | }); 281 | 282 | // Breadcrumbs for timeline 283 | scope.addBreadcrumb({ 284 | category: 'workflow', 285 | message: 'Starting step completion', 286 | level: 'info', 287 | data: { stepId: 456 } 288 | }); 289 | 290 | Sentry.captureException(error); 291 | }); 292 | ``` 293 | 294 | --- 295 | 296 | ## Common Mistakes 297 | 298 | ```typescript 299 | // ❌ Swallowing errors 300 | try { 301 | await riskyOperation(); 302 | } catch (error) { 303 | // Silent failure 304 | } 305 | 306 | // ❌ Generic error messages 307 | throw new Error('Error occurred'); 308 | 309 | // ❌ Exposing sensitive data 310 | Sentry.captureException(error, { 311 | extra: { password: user.password } // NEVER 312 | }); 313 | 314 | // ❌ Missing async error handling 315 | async function bad() { 316 | fetchData().then(data => processResult(data)); // Unhandled 317 | } 318 | 319 | // ✅ Proper async handling 320 | async function good() { 321 | try { 322 | const data = await fetchData(); 323 | processResult(data); 324 | } catch (error) { 325 | Sentry.captureException(error); 326 | throw error; 327 | } 328 | } 329 | ``` 330 | 331 | --- 332 | 333 | **Related Files:** 334 | - [SKILL.md](SKILL.md) 335 | - [routing-and-controllers.md](routing-and-controllers.md) 336 | - [async-and-errors.md](async-and-errors.md) 337 | -------------------------------------------------------------------------------- /.claude/skills/backend-dev-guidelines/SKILL.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: backend-dev-guidelines 3 | description: Comprehensive backend development guide for Node.js/Express/TypeScript microservices. Use when creating routes, controllers, services, repositories, middleware, or working with Express APIs, Prisma database access, Sentry error tracking, Zod validation, unifiedConfig, dependency injection, or async patterns. Covers layered architecture (routes → controllers → services → repositories), BaseController pattern, error handling, performance monitoring, testing strategies, and migration from legacy patterns. 4 | --- 5 | 6 | # Backend Development Guidelines 7 | 8 | ## Purpose 9 | 10 | Establish consistency and best practices across backend microservices (blog-api, auth-service, notifications-service) using modern Node.js/Express/TypeScript patterns. 11 | 12 | ## When to Use This Skill 13 | 14 | Automatically activates when working on: 15 | - Creating or modifying routes, endpoints, APIs 16 | - Building controllers, services, repositories 17 | - Implementing middleware (auth, validation, error handling) 18 | - Database operations with Prisma 19 | - Error tracking with Sentry 20 | - Input validation with Zod 21 | - Configuration management 22 | - Backend testing and refactoring 23 | 24 | --- 25 | 26 | ## Quick Start 27 | 28 | ### New Backend Feature Checklist 29 | 30 | - [ ] **Route**: Clean definition, delegate to controller 31 | - [ ] **Controller**: Extend BaseController 32 | - [ ] **Service**: Business logic with DI 33 | - [ ] **Repository**: Database access (if complex) 34 | - [ ] **Validation**: Zod schema 35 | - [ ] **Sentry**: Error tracking 36 | - [ ] **Tests**: Unit + integration tests 37 | - [ ] **Config**: Use unifiedConfig 38 | 39 | ### New Microservice Checklist 40 | 41 | - [ ] Directory structure (see [architecture-overview.md](architecture-overview.md)) 42 | - [ ] instrument.ts for Sentry 43 | - [ ] unifiedConfig setup 44 | - [ ] BaseController class 45 | - [ ] Middleware stack 46 | - [ ] Error boundary 47 | - [ ] Testing framework 48 | 49 | --- 50 | 51 | ## Architecture Overview 52 | 53 | ### Layered Architecture 54 | 55 | ``` 56 | HTTP Request 57 | ↓ 58 | Routes (routing only) 59 | ↓ 60 | Controllers (request handling) 61 | ↓ 62 | Services (business logic) 63 | ↓ 64 | Repositories (data access) 65 | ↓ 66 | Database (Prisma) 67 | ``` 68 | 69 | **Key Principle:** Each layer has ONE responsibility. 70 | 71 | See [architecture-overview.md](architecture-overview.md) for complete details. 72 | 73 | --- 74 | 75 | ## Directory Structure 76 | 77 | ``` 78 | service/src/ 79 | ├── config/ # UnifiedConfig 80 | ├── controllers/ # Request handlers 81 | ├── services/ # Business logic 82 | ├── repositories/ # Data access 83 | ├── routes/ # Route definitions 84 | ├── middleware/ # Express middleware 85 | ├── types/ # TypeScript types 86 | ├── validators/ # Zod schemas 87 | ├── utils/ # Utilities 88 | ├── tests/ # Tests 89 | ├── instrument.ts # Sentry (FIRST IMPORT) 90 | ├── app.ts # Express setup 91 | └── server.ts # HTTP server 92 | ``` 93 | 94 | **Naming Conventions:** 95 | - Controllers: `PascalCase` - `UserController.ts` 96 | - Services: `camelCase` - `userService.ts` 97 | - Routes: `camelCase + Routes` - `userRoutes.ts` 98 | - Repositories: `PascalCase + Repository` - `UserRepository.ts` 99 | 100 | --- 101 | 102 | ## Core Principles (7 Key Rules) 103 | 104 | ### 1. Routes Only Route, Controllers Control 105 | 106 | ```typescript 107 | // ❌ NEVER: Business logic in routes 108 | router.post('/submit', async (req, res) => { 109 | // 200 lines of logic 110 | }); 111 | 112 | // ✅ ALWAYS: Delegate to controller 113 | router.post('/submit', (req, res) => controller.submit(req, res)); 114 | ``` 115 | 116 | ### 2. All Controllers Extend BaseController 117 | 118 | ```typescript 119 | export class UserController extends BaseController { 120 | async getUser(req: Request, res: Response): Promise { 121 | try { 122 | const user = await this.userService.findById(req.params.id); 123 | this.handleSuccess(res, user); 124 | } catch (error) { 125 | this.handleError(error, res, 'getUser'); 126 | } 127 | } 128 | } 129 | ``` 130 | 131 | ### 3. All Errors to Sentry 132 | 133 | ```typescript 134 | try { 135 | await operation(); 136 | } catch (error) { 137 | Sentry.captureException(error); 138 | throw error; 139 | } 140 | ``` 141 | 142 | ### 4. Use unifiedConfig, NEVER process.env 143 | 144 | ```typescript 145 | // ❌ NEVER 146 | const timeout = process.env.TIMEOUT_MS; 147 | 148 | // ✅ ALWAYS 149 | import { config } from './config/unifiedConfig'; 150 | const timeout = config.timeouts.default; 151 | ``` 152 | 153 | ### 5. Validate All Input with Zod 154 | 155 | ```typescript 156 | const schema = z.object({ email: z.string().email() }); 157 | const validated = schema.parse(req.body); 158 | ``` 159 | 160 | ### 6. Use Repository Pattern for Data Access 161 | 162 | ```typescript 163 | // Service → Repository → Database 164 | const users = await userRepository.findActive(); 165 | ``` 166 | 167 | ### 7. Comprehensive Testing Required 168 | 169 | ```typescript 170 | describe('UserService', () => { 171 | it('should create user', async () => { 172 | expect(user).toBeDefined(); 173 | }); 174 | }); 175 | ``` 176 | 177 | --- 178 | 179 | ## Common Imports 180 | 181 | ```typescript 182 | // Express 183 | import express, { Request, Response, NextFunction, Router } from 'express'; 184 | 185 | // Validation 186 | import { z } from 'zod'; 187 | 188 | // Database 189 | import { PrismaClient } from '@prisma/client'; 190 | import type { Prisma } from '@prisma/client'; 191 | 192 | // Sentry 193 | import * as Sentry from '@sentry/node'; 194 | 195 | // Config 196 | import { config } from './config/unifiedConfig'; 197 | 198 | // Middleware 199 | import { SSOMiddlewareClient } from './middleware/SSOMiddleware'; 200 | import { asyncErrorWrapper } from './middleware/errorBoundary'; 201 | ``` 202 | 203 | --- 204 | 205 | ## Quick Reference 206 | 207 | ### HTTP Status Codes 208 | 209 | | Code | Use Case | 210 | |------|----------| 211 | | 200 | Success | 212 | | 201 | Created | 213 | | 400 | Bad Request | 214 | | 401 | Unauthorized | 215 | | 403 | Forbidden | 216 | | 404 | Not Found | 217 | | 500 | Server Error | 218 | 219 | ### Service Templates 220 | 221 | **Blog API** (✅ Mature) - Use as template for REST APIs 222 | **Auth Service** (✅ Mature) - Use as template for authentication patterns 223 | 224 | --- 225 | 226 | ## Anti-Patterns to Avoid 227 | 228 | ❌ Business logic in routes 229 | ❌ Direct process.env usage 230 | ❌ Missing error handling 231 | ❌ No input validation 232 | ❌ Direct Prisma everywhere 233 | ❌ console.log instead of Sentry 234 | 235 | --- 236 | 237 | ## Navigation Guide 238 | 239 | | Need to... | Read this | 240 | |------------|-----------| 241 | | Understand architecture | [architecture-overview.md](architecture-overview.md) | 242 | | Create routes/controllers | [routing-and-controllers.md](routing-and-controllers.md) | 243 | | Organize business logic | [services-and-repositories.md](services-and-repositories.md) | 244 | | Validate input | [validation-patterns.md](validation-patterns.md) | 245 | | Add error tracking | [sentry-and-monitoring.md](sentry-and-monitoring.md) | 246 | | Create middleware | [middleware-guide.md](middleware-guide.md) | 247 | | Database access | [database-patterns.md](database-patterns.md) | 248 | | Manage config | [configuration.md](configuration.md) | 249 | | Handle async/errors | [async-and-errors.md](async-and-errors.md) | 250 | | Write tests | [testing-guide.md](testing-guide.md) | 251 | | See examples | [complete-examples.md](complete-examples.md) | 252 | 253 | --- 254 | 255 | ## Resource Files 256 | 257 | ### [architecture-overview.md](architecture-overview.md) 258 | Layered architecture, request lifecycle, separation of concerns 259 | 260 | ### [routing-and-controllers.md](routing-and-controllers.md) 261 | Route definitions, BaseController, error handling, examples 262 | 263 | ### [services-and-repositories.md](services-and-repositories.md) 264 | Service patterns, DI, repository pattern, caching 265 | 266 | ### [validation-patterns.md](validation-patterns.md) 267 | Zod schemas, validation, DTO pattern 268 | 269 | ### [sentry-and-monitoring.md](sentry-and-monitoring.md) 270 | Sentry init, error capture, performance monitoring 271 | 272 | ### [middleware-guide.md](middleware-guide.md) 273 | Auth, audit, error boundaries, AsyncLocalStorage 274 | 275 | ### [database-patterns.md](database-patterns.md) 276 | PrismaService, repositories, transactions, optimization 277 | 278 | ### [configuration.md](configuration.md) 279 | UnifiedConfig, environment configs, secrets 280 | 281 | ### [async-and-errors.md](async-and-errors.md) 282 | Async patterns, custom errors, asyncErrorWrapper 283 | 284 | ### [testing-guide.md](testing-guide.md) 285 | Unit/integration tests, mocking, coverage 286 | 287 | ### [complete-examples.md](complete-examples.md) 288 | Full examples, refactoring guide 289 | 290 | --- 291 | 292 | ## Related Skills 293 | 294 | - **database-verification** - Verify column names and schema consistency 295 | - **error-tracking** - Sentry integration patterns 296 | - **skill-developer** - Meta-skill for creating and managing skills 297 | 298 | --- 299 | 300 | **Skill Status**: COMPLETE ✅ 301 | **Line Count**: < 500 ✅ 302 | **Progressive Disclosure**: 11 resource files ✅ 303 | -------------------------------------------------------------------------------- /.claude/skills/frontend-dev-guidelines/resources/common-patterns.md: -------------------------------------------------------------------------------- 1 | # Common Patterns 2 | 3 | Frequently used patterns for forms, authentication, DataGrid, dialogs, and other common UI elements. 4 | 5 | --- 6 | 7 | ## Authentication with useAuth 8 | 9 | ### Getting Current User 10 | 11 | ```typescript 12 | import { useAuth } from '@/hooks/useAuth'; 13 | 14 | export const MyComponent: React.FC = () => { 15 | const { user } = useAuth(); 16 | 17 | // Available properties: 18 | // - user.id: string 19 | // - user.email: string 20 | // - user.username: string 21 | // - user.roles: string[] 22 | 23 | return ( 24 |
25 |

Logged in as: {user.email}

26 |

Username: {user.username}

27 |

Roles: {user.roles.join(', ')}

28 |
29 | ); 30 | }; 31 | ``` 32 | 33 | **NEVER make direct API calls for auth** - always use `useAuth` hook. 34 | 35 | --- 36 | 37 | ## Forms with React Hook Form 38 | 39 | ### Basic Form 40 | 41 | ```typescript 42 | import { useForm } from 'react-hook-form'; 43 | import { zodResolver } from '@hookform/resolvers/zod'; 44 | import { z } from 'zod'; 45 | import { TextField, Button } from '@mui/material'; 46 | import { useMuiSnackbar } from '@/hooks/useMuiSnackbar'; 47 | 48 | // Zod schema for validation 49 | const formSchema = z.object({ 50 | username: z.string().min(3, 'Username must be at least 3 characters'), 51 | email: z.string().email('Invalid email address'), 52 | age: z.number().min(18, 'Must be 18 or older'), 53 | }); 54 | 55 | type FormData = z.infer; 56 | 57 | export const MyForm: React.FC = () => { 58 | const { showSuccess, showError } = useMuiSnackbar(); 59 | 60 | const { register, handleSubmit, formState: { errors } } = useForm({ 61 | resolver: zodResolver(formSchema), 62 | defaultValues: { 63 | username: '', 64 | email: '', 65 | age: 18, 66 | }, 67 | }); 68 | 69 | const onSubmit = async (data: FormData) => { 70 | try { 71 | await api.submitForm(data); 72 | showSuccess('Form submitted successfully'); 73 | } catch (error) { 74 | showError('Failed to submit form'); 75 | } 76 | }; 77 | 78 | return ( 79 |
80 | 86 | 87 | 94 | 95 | 102 | 103 | 106 | 107 | ); 108 | }; 109 | ``` 110 | 111 | --- 112 | 113 | ## Dialog Component Pattern 114 | 115 | ### Standard Dialog Structure 116 | 117 | From BEST_PRACTICES.md - All dialogs should have: 118 | - Icon in title 119 | - Close button (X) 120 | - Action buttons at bottom 121 | 122 | ```typescript 123 | import { Dialog, DialogTitle, DialogContent, DialogActions, Button, IconButton } from '@mui/material'; 124 | import { Close, Info } from '@mui/icons-material'; 125 | 126 | interface MyDialogProps { 127 | open: boolean; 128 | onClose: () => void; 129 | onConfirm: () => void; 130 | } 131 | 132 | export const MyDialog: React.FC = ({ open, onClose, onConfirm }) => { 133 | return ( 134 | 135 | 136 | 137 | 138 | 139 | Dialog Title 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | {/* Content here */} 149 | 150 | 151 | 152 | 153 | 156 | 157 | 158 | ); 159 | }; 160 | ``` 161 | 162 | --- 163 | 164 | ## DataGrid Wrapper Pattern 165 | 166 | ### Wrapper Component Contract 167 | 168 | From BEST_PRACTICES.md - DataGrid wrappers should accept: 169 | 170 | **Required Props:** 171 | - `rows`: Data array 172 | - `columns`: Column definitions 173 | - Loading/error states 174 | 175 | **Optional Props:** 176 | - Toolbar components 177 | - Custom actions 178 | - Initial state 179 | 180 | ```typescript 181 | import { DataGridPro } from '@mui/x-data-grid-pro'; 182 | import type { GridColDef } from '@mui/x-data-grid-pro'; 183 | 184 | interface DataGridWrapperProps { 185 | rows: any[]; 186 | columns: GridColDef[]; 187 | loading?: boolean; 188 | toolbar?: React.ReactNode; 189 | onRowClick?: (row: any) => void; 190 | } 191 | 192 | export const DataGridWrapper: React.FC = ({ 193 | rows, 194 | columns, 195 | loading = false, 196 | toolbar, 197 | onRowClick, 198 | }) => { 199 | return ( 200 | toolbar : undefined }} 205 | onRowClick={(params) => onRowClick?.(params.row)} 206 | // Standard configuration 207 | pagination 208 | pageSizeOptions={[25, 50, 100]} 209 | initialState={{ 210 | pagination: { paginationModel: { pageSize: 25 } }, 211 | }} 212 | /> 213 | ); 214 | }; 215 | ``` 216 | 217 | --- 218 | 219 | ## Mutation Patterns 220 | 221 | ### Update with Cache Invalidation 222 | 223 | ```typescript 224 | import { useMutation, useQueryClient } from '@tanstack/react-query'; 225 | import { useMuiSnackbar } from '@/hooks/useMuiSnackbar'; 226 | 227 | export const useUpdateEntity = () => { 228 | const queryClient = useQueryClient(); 229 | const { showSuccess, showError } = useMuiSnackbar(); 230 | 231 | return useMutation({ 232 | mutationFn: ({ id, data }: { id: number; data: any }) => 233 | api.updateEntity(id, data), 234 | 235 | onSuccess: (result, variables) => { 236 | // Invalidate affected queries 237 | queryClient.invalidateQueries({ queryKey: ['entity', variables.id] }); 238 | queryClient.invalidateQueries({ queryKey: ['entities'] }); 239 | 240 | showSuccess('Entity updated'); 241 | }, 242 | 243 | onError: () => { 244 | showError('Failed to update entity'); 245 | }, 246 | }); 247 | }; 248 | 249 | // Usage 250 | const updateEntity = useUpdateEntity(); 251 | 252 | const handleSave = () => { 253 | updateEntity.mutate({ id: 123, data: { name: 'New Name' } }); 254 | }; 255 | ``` 256 | 257 | --- 258 | 259 | ## State Management Patterns 260 | 261 | ### TanStack Query for Server State (PRIMARY) 262 | 263 | Use TanStack Query for **all server data**: 264 | - Fetching: useSuspenseQuery 265 | - Mutations: useMutation 266 | - Caching: Automatic 267 | - Synchronization: Built-in 268 | 269 | ```typescript 270 | // ✅ CORRECT - TanStack Query for server data 271 | const { data: users } = useSuspenseQuery({ 272 | queryKey: ['users'], 273 | queryFn: () => userApi.getUsers(), 274 | }); 275 | ``` 276 | 277 | ### useState for UI State 278 | 279 | Use `useState` for **local UI state only**: 280 | - Form inputs (uncontrolled) 281 | - Modal open/closed 282 | - Selected tab 283 | - Temporary UI flags 284 | 285 | ```typescript 286 | // ✅ CORRECT - useState for UI state 287 | const [modalOpen, setModalOpen] = useState(false); 288 | const [selectedTab, setSelectedTab] = useState(0); 289 | ``` 290 | 291 | ### Zustand for Global Client State (Minimal) 292 | 293 | Use Zustand only for **global client state**: 294 | - Theme preference 295 | - Sidebar collapsed state 296 | - User preferences (not from server) 297 | 298 | ```typescript 299 | import { create } from 'zustand'; 300 | 301 | interface AppState { 302 | sidebarOpen: boolean; 303 | toggleSidebar: () => void; 304 | } 305 | 306 | export const useAppState = create((set) => ({ 307 | sidebarOpen: true, 308 | toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })), 309 | })); 310 | ``` 311 | 312 | **Avoid prop drilling** - use context or Zustand instead. 313 | 314 | --- 315 | 316 | ## Summary 317 | 318 | **Common Patterns:** 319 | - ✅ useAuth hook for current user (id, email, roles, username) 320 | - ✅ React Hook Form + Zod for forms 321 | - ✅ Dialog with icon + close button 322 | - ✅ DataGrid wrapper contracts 323 | - ✅ Mutations with cache invalidation 324 | - ✅ TanStack Query for server state 325 | - ✅ useState for UI state 326 | - ✅ Zustand for global client state (minimal) 327 | 328 | **See Also:** 329 | - [data-fetching.md](data-fetching.md) - TanStack Query patterns 330 | - [component-patterns.md](component-patterns.md) - Component structure 331 | - [loading-and-error-states.md](loading-and-error-states.md) - Error handling -------------------------------------------------------------------------------- /.claude/skills/frontend-dev-guidelines/resources/styling-guide.md: -------------------------------------------------------------------------------- 1 | # Styling Guide 2 | 3 | Modern styling patterns for using MUI v7 sx prop, inline styles, and theme integration. 4 | 5 | --- 6 | 7 | ## Inline vs Separate Styles 8 | 9 | ### Decision Threshold 10 | 11 | **<100 lines: Inline styles at top of component** 12 | 13 | ```typescript 14 | import type { SxProps, Theme } from '@mui/material'; 15 | 16 | const componentStyles: Record> = { 17 | container: { 18 | p: 2, 19 | display: 'flex', 20 | flexDirection: 'column', 21 | }, 22 | header: { 23 | mb: 2, 24 | borderBottom: '1px solid', 25 | borderColor: 'divider', 26 | }, 27 | // ... more styles 28 | }; 29 | 30 | export const MyComponent: React.FC = () => { 31 | return ( 32 | 33 | 34 |

Title

35 |
36 |
37 | ); 38 | }; 39 | ``` 40 | 41 | **>100 lines: Separate `.styles.ts` file** 42 | 43 | ```typescript 44 | // MyComponent.styles.ts 45 | import type { SxProps, Theme } from '@mui/material'; 46 | 47 | export const componentStyles: Record> = { 48 | container: { ... }, 49 | header: { ... }, 50 | // ... 100+ lines of styles 51 | }; 52 | 53 | // MyComponent.tsx 54 | import { componentStyles } from './MyComponent.styles'; 55 | 56 | export const MyComponent: React.FC = () => { 57 | return ...; 58 | }; 59 | ``` 60 | 61 | ### Real Example: UnifiedForm.tsx 62 | 63 | **Lines 48-126**: 78 lines of inline styles (acceptable) 64 | 65 | ```typescript 66 | const formStyles: Record> = { 67 | gridContainer: { 68 | height: '100%', 69 | maxHeight: 'calc(100vh - 220px)', 70 | }, 71 | section: { 72 | height: '100%', 73 | maxHeight: 'calc(100vh - 220px)', 74 | overflow: 'auto', 75 | p: 4, 76 | }, 77 | // ... 15 more style objects 78 | }; 79 | ``` 80 | 81 | **Guideline**: User is comfortable with ~80 lines inline. Use your judgment around 100 lines. 82 | 83 | --- 84 | 85 | ## sx Prop Patterns 86 | 87 | ### Basic Usage 88 | 89 | ```typescript 90 | 91 | Content 92 | 93 | ``` 94 | 95 | ### With Theme Access 96 | 97 | ```typescript 98 | theme.palette.primary.main, 102 | color: (theme) => theme.palette.primary.contrastText, 103 | borderRadius: (theme) => theme.shape.borderRadius, 104 | }} 105 | > 106 | Themed Box 107 | 108 | ``` 109 | 110 | ### Responsive Styles 111 | 112 | ```typescript 113 | 120 | Responsive Layout 121 | 122 | ``` 123 | 124 | ### Pseudo-Selectors 125 | 126 | ```typescript 127 | 141 | Interactive Box 142 | 143 | ``` 144 | 145 | --- 146 | 147 | ## MUI v7 Patterns 148 | 149 | ### Grid Component (v7 Syntax) 150 | 151 | ```typescript 152 | import { Grid } from '@mui/material'; 153 | 154 | // ✅ CORRECT - v7 syntax with size prop 155 | 156 | 157 | Left Column 158 | 159 | 160 | Right Column 161 | 162 | 163 | 164 | // ❌ WRONG - Old v6 syntax 165 | 166 | {/* OLD - Don't use */} 167 | Content 168 | 169 | 170 | ``` 171 | 172 | **Key Change**: `size={{ xs: 12, md: 6 }}` instead of `xs={12} md={6}` 173 | 174 | ### Responsive Grid 175 | 176 | ```typescript 177 | 178 | 179 | Responsive Column 180 | 181 | 182 | ``` 183 | 184 | ### Nested Grids 185 | 186 | ```typescript 187 | 188 | 189 | 190 | 191 | Nested 1 192 | 193 | 194 | Nested 2 195 | 196 | 197 | 198 | 199 | 200 | Sidebar 201 | 202 | 203 | ``` 204 | 205 | --- 206 | 207 | ## Type-Safe Styles 208 | 209 | ### Style Object Type 210 | 211 | ```typescript 212 | import type { SxProps, Theme } from '@mui/material'; 213 | 214 | // Type-safe styles 215 | const styles: Record> = { 216 | container: { 217 | p: 2, 218 | // Autocomplete and type checking work here 219 | }, 220 | }; 221 | 222 | // Or individual style 223 | const containerStyle: SxProps = { 224 | p: 2, 225 | display: 'flex', 226 | }; 227 | ``` 228 | 229 | ### Theme-Aware Styles 230 | 231 | ```typescript 232 | const styles: Record> = { 233 | primary: { 234 | color: (theme) => theme.palette.primary.main, 235 | backgroundColor: (theme) => theme.palette.primary.light, 236 | '&:hover': { 237 | backgroundColor: (theme) => theme.palette.primary.dark, 238 | }, 239 | }, 240 | customSpacing: { 241 | padding: (theme) => theme.spacing(2), 242 | margin: (theme) => theme.spacing(1, 2), // top/bottom: 1, left/right: 2 243 | }, 244 | }; 245 | ``` 246 | 247 | --- 248 | 249 | ## What NOT to Use 250 | 251 | ### ❌ makeStyles (MUI v4 pattern) 252 | 253 | ```typescript 254 | // ❌ AVOID - Old Material-UI v4 pattern 255 | import { makeStyles } from '@mui/styles'; 256 | 257 | const useStyles = makeStyles((theme) => ({ 258 | root: { 259 | padding: theme.spacing(2), 260 | }, 261 | })); 262 | ``` 263 | 264 | **Why avoid**: Deprecated, v7 doesn't support it well 265 | 266 | ### ❌ styled() Components 267 | 268 | ```typescript 269 | // ❌ AVOID - styled-components pattern 270 | import { styled } from '@mui/material/styles'; 271 | 272 | const StyledBox = styled(Box)(({ theme }) => ({ 273 | padding: theme.spacing(2), 274 | })); 275 | ``` 276 | 277 | **Why avoid**: sx prop is more flexible and doesn't create new components 278 | 279 | ### ✅ Use sx Prop Instead 280 | 281 | ```typescript 282 | // ✅ PREFERRED 283 | 289 | Content 290 | 291 | ``` 292 | 293 | --- 294 | 295 | ## Code Style Standards 296 | 297 | ### Indentation 298 | 299 | **4 spaces** (not 2, not tabs) 300 | 301 | ```typescript 302 | const styles: Record> = { 303 | container: { 304 | p: 2, 305 | display: 'flex', 306 | flexDirection: 'column', 307 | }, 308 | }; 309 | ``` 310 | 311 | ### Quotes 312 | 313 | **Single quotes** for strings (project standard) 314 | 315 | ```typescript 316 | // ✅ CORRECT 317 | const color = 'primary.main'; 318 | import { Box } from '@mui/material'; 319 | 320 | // ❌ WRONG 321 | const color = "primary.main"; 322 | import { Box } from "@mui/material"; 323 | ``` 324 | 325 | ### Trailing Commas 326 | 327 | **Always use trailing commas** in objects and arrays 328 | 329 | ```typescript 330 | // ✅ CORRECT 331 | const styles = { 332 | container: { p: 2 }, 333 | header: { mb: 1 }, // Trailing comma 334 | }; 335 | 336 | const items = [ 337 | 'item1', 338 | 'item2', // Trailing comma 339 | ]; 340 | 341 | // ❌ WRONG - No trailing comma 342 | const styles = { 343 | container: { p: 2 }, 344 | header: { mb: 1 } // Missing comma 345 | }; 346 | ``` 347 | 348 | --- 349 | 350 | ## Common Style Patterns 351 | 352 | ### Flexbox Layout 353 | 354 | ```typescript 355 | const styles = { 356 | flexRow: { 357 | display: 'flex', 358 | flexDirection: 'row', 359 | alignItems: 'center', 360 | gap: 2, 361 | }, 362 | flexColumn: { 363 | display: 'flex', 364 | flexDirection: 'column', 365 | gap: 1, 366 | }, 367 | spaceBetween: { 368 | display: 'flex', 369 | justifyContent: 'space-between', 370 | alignItems: 'center', 371 | }, 372 | }; 373 | ``` 374 | 375 | ### Spacing 376 | 377 | ```typescript 378 | // Padding 379 | p: 2 // All sides 380 | px: 2 // Horizontal (left + right) 381 | py: 2 // Vertical (top + bottom) 382 | pt: 2, pr: 1 // Specific sides 383 | 384 | // Margin 385 | m: 2, mx: 2, my: 2, mt: 2, mr: 1 386 | 387 | // Units: 1 = 8px (theme.spacing(1)) 388 | p: 2 // = 16px 389 | p: 0.5 // = 4px 390 | ``` 391 | 392 | ### Positioning 393 | 394 | ```typescript 395 | const styles = { 396 | relative: { 397 | position: 'relative', 398 | }, 399 | absolute: { 400 | position: 'absolute', 401 | top: 0, 402 | right: 0, 403 | }, 404 | sticky: { 405 | position: 'sticky', 406 | top: 0, 407 | zIndex: 1000, 408 | }, 409 | }; 410 | ``` 411 | 412 | --- 413 | 414 | ## Summary 415 | 416 | **Styling Checklist:** 417 | - ✅ Use `sx` prop for MUI styling 418 | - ✅ Type-safe with `SxProps` 419 | - ✅ <100 lines: inline; >100 lines: separate file 420 | - ✅ MUI v7 Grid: `size={{ xs: 12 }}` 421 | - ✅ 4 space indentation 422 | - ✅ Single quotes 423 | - ✅ Trailing commas 424 | - ❌ No makeStyles or styled() 425 | 426 | **See Also:** 427 | - [component-patterns.md](component-patterns.md) - Component structure 428 | - [complete-examples.md](complete-examples.md) - Full styling examples --------------------------------------------------------------------------------