Building Automated Code Review Systems with Claude Code Hooks

Building Automated Code Review Systems with Claude Code Hooks

Complete guide from hook-based coding rule configuration to CI/CD integration for production-ready automated review processes

Overview

AI-powered coding assistants dramatically improve productivity, but ensuring consistent code quality and rule compliance remains a challenging task. Claude Code’s Hook System provides a powerful solution to this problem.

Hooks are scripts that automatically execute at specific workflow stages, allowing you to insert custom validation logic at various points such as code writing, file saving, pre/post-commit, and more. This enables complete automation of code review, testing, security scanning, and compliance checking.

What This Article Covers

  • Core concepts and operating principles of the Hook system
  • Various Hook types and use cases
  • Implementing automated coding rule validation
  • Building automated code review processes
  • CI/CD pipeline integration strategies
  • Practical examples and best practices

Understanding the Hook System

What is a Hook?

Claude Code Hooks are user-defined scripts that execute at specific points in the workflow. Similar to Git hooks but specialized for Claude’s AI coding workflow.

graph LR
    A[Claude Task Starts] --> B{Pre Hook}
    B -->|Pass| C[Claude Executes Task]
    B -->|Fail| D[Task Aborted]
    C --> E{Post Hook}
    E -->|Pass| F[Task Complete]
    E -->|Fail| G[Rollback/Warning]

Hook Execution Mechanism

Hooks control Claude’s behavior through exit codes:

# Success (continue task)
exit 0

# Failure (abort task)
exit 1

# Warning (continue but show warning)
exit 2

Hook Directory Structure

.claude/
└── hooks/
    ├── pre-file-write.sh      # Execute before file save
    ├── post-file-write.py     # Execute after file save
    ├── pre-commit.sh          # Execute before commit
    ├── post-commit.py         # Execute after commit
    └── code-review.js         # Custom review Hook

Hook Configuration and Setup

1. Creating a Basic Hook

Let’s start with the simplest hook:

#!/bin/bash
# .claude/hooks/pre-file-write.sh

# Hook input data is passed as JSON
input=$(cat)

# Extract file path
file_path=$(echo "$input" | jq -r '.file_path')

echo "Checking file: $file_path"

# Protect sensitive files
if [[ "$file_path" == *".env"* ]] || [[ "$file_path" == *"credentials"* ]]; then
    echo "Error: Cannot modify sensitive files"
    exit 1
fi

# Success
exit 0

2. Setting Execution Permissions

Hook scripts must be executable:

chmod +x .claude/hooks/pre-file-write.sh

# Grant execution permissions to all hooks
chmod +x .claude/hooks/*.sh
chmod +x .claude/hooks/*.py

3. Hook Data Structure

Claude passes context information to hooks in JSON format:

{
  "file_path": "src/components/Button.tsx",
  "operation": "write",
  "content": "...",
  "metadata": {
    "timestamp": "2025-10-29T10:30:00Z",
    "user": "developer@example.com"
  }
}

Automated Coding Rule Validation

1. TypeScript Type Check Hook

#!/bin/bash
# .claude/hooks/typescript-check.sh

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

# Check TypeScript files only
if [[ "$file_path" != *.ts ]] && [[ "$file_path" != *.tsx ]]; then
    exit 0
fi

echo "Running TypeScript type check..."

# Run type check
npx tsc --noEmit "$file_path" 2>&1 | tee /tmp/tsc-output.txt

if [ ${PIPESTATUS[0]} -ne 0 ]; then
    echo "❌ Type check failed"
    cat /tmp/tsc-output.txt
    exit 1
fi

echo "✅ Type check passed"
exit 0

2. ESLint Linting Hook

#!/usr/bin/env python3
# .claude/hooks/eslint-check.py

import sys
import json
import subprocess

def main():
    # Read input data
    input_data = json.loads(sys.stdin.read())
    file_path = input_data.get('file_path', '')

    # Check JavaScript/TypeScript files only
    if not (file_path.endswith('.js') or
            file_path.endswith('.ts') or
            file_path.endswith('.jsx') or
            file_path.endswith('.tsx')):
        sys.exit(0)

    print(f"Running ESLint on {file_path}...")

    # Run ESLint
    result = subprocess.run(
        ['npx', 'eslint', file_path, '--format', 'json'],
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        lint_results = json.loads(result.stdout)

        # Output error summary
        for file_result in lint_results:
            for message in file_result.get('messages', []):
                severity = 'Error' if message['severity'] == 2 else 'Warning'
                print(f"{severity}: {message['message']} "
                      f"(line {message['line']}, col {message['column']})")

        sys.exit(1)

    print("✅ ESLint passed")
    sys.exit(0)

if __name__ == '__main__':
    main()

3. Automatic Code Formatting

#!/bin/bash
# .claude/hooks/post-file-write.sh

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

# Supported file extensions
if [[ "$file_path" =~ \.(js|ts|jsx|tsx|json|css|scss)$ ]]; then
    echo "Auto-formatting $file_path with Prettier..."

    npx prettier --write "$file_path"

    if [ $? -eq 0 ]; then
        echo "✅ Formatted successfully"
    else
        echo "⚠️  Formatting failed, but continuing..."
    fi
fi

exit 0

Automated Code Review Process

1. Comprehensive Code Review Hook

#!/bin/bash
# .claude/hooks/comprehensive-review.sh

set -e

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

echo "🔍 Starting comprehensive code review for $file_path"

# Step-by-step validation
declare -a checks=(
    "Security scan"
    "Type checking"
    "Linting"
    "Test coverage"
    "Documentation check"
)

# 1. Security scan
echo "🔒 ${checks[0]}..."
if command -v semgrep &> /dev/null; then
    semgrep --config=auto "$file_path" --quiet
fi

# 2. Type check
echo "📝 ${checks[1]}..."
if [[ "$file_path" =~ \.(ts|tsx)$ ]]; then
    npx tsc --noEmit "$file_path"
fi

# 3. Linting
echo "✨ ${checks[2]}..."
if [[ "$file_path" =~ \.(js|ts|jsx|tsx)$ ]]; then
    npx eslint "$file_path"
fi

# 4. Test coverage check
echo "🧪 ${checks[3]}..."
test_file="${file_path/src/tests}"
test_file="${test_file/.ts/.test.ts}"

if [ ! -f "$test_file" ]; then
    echo "⚠️  Warning: No test file found at $test_file"
    # Just warn and continue
fi

# 5. Documentation check
echo "📚 ${checks[4]}..."
if [[ "$file_path" =~ \.(ts|tsx|js|jsx)$ ]]; then
    # Check JSDoc comments
    if ! grep -q "\/\*\*" "$file_path"; then
        echo "⚠️  Warning: No JSDoc comments found"
    fi
fi

echo "✅ Code review completed successfully"
exit 0

2. SOX/SOC2 Audit Trail Hook

#!/usr/bin/env python3
# .claude/hooks/audit-trail.py

import sys
import json
import hashlib
from datetime import datetime
import os

AUDIT_LOG = '.claude/audit/trail.jsonl'

def main():
    # Input data
    input_data = json.loads(sys.stdin.read())

    # Create audit log directory
    os.makedirs(os.path.dirname(AUDIT_LOG), exist_ok=True)

    # Create audit entry
    audit_entry = {
        'timestamp': datetime.utcnow().isoformat(),
        'operation': input_data.get('operation', 'unknown'),
        'file_path': input_data.get('file_path', ''),
        'user': os.environ.get('USER', 'unknown'),
        'content_hash': hashlib.sha256(
            input_data.get('content', '').encode()
        ).hexdigest(),
        'metadata': input_data.get('metadata', {})
    }

    # Append log in JSONL format
    with open(AUDIT_LOG, 'a') as f:
        f.write(json.dumps(audit_entry) + '\n')

    print(f"✅ Audit trail recorded: {audit_entry['timestamp']}")
    sys.exit(0)

if __name__ == '__main__':
    main()

3. Pull Request Auto-Validation

#!/bin/bash
# .claude/hooks/pr-validation.sh

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

echo "🔍 PR Validation Checks"

# Checklist
declare -A checks=(
    ["Tests"]="npm test"
    ["Build"]="npm run build"
    ["Type Check"]="npm run typecheck"
    ["Lint"]="npm run lint"
)

failed=0

for check_name in "${!checks[@]}"; do
    echo ""
    echo "Running: $check_name"

    if eval "${checks[$check_name]}" > /tmp/check-output.txt 2>&1; then
        echo "✅ $check_name passed"
    else
        echo "❌ $check_name failed"
        cat /tmp/check-output.txt
        failed=1
    fi
done

if [ $failed -eq 1 ]; then
    echo ""
    echo "❌ PR validation failed. Please fix the issues before committing."
    exit 1
fi

echo ""
echo "✅ All PR validation checks passed"
exit 0

CI/CD Integration Strategies

1. GitHub Actions Integration

# .github/workflows/claude-hooks.yml
name: Claude Code Hooks

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  run-hooks:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Make hooks executable
        run: chmod +x .claude/hooks/*.sh

      - name: Run pre-commit hooks
        run: |
          for file in $(git diff --name-only origin/main); do
            if [ -f ".claude/hooks/pre-commit.sh" ]; then
              echo "{\"file_path\": \"$file\"}" | .claude/hooks/pre-commit.sh
            fi
          done

      - name: Run code review hook
        run: |
          for file in $(git diff --name-only origin/main); do
            if [ -f ".claude/hooks/code-review.sh" ]; then
              echo "{\"file_path\": \"$file\"}" | .claude/hooks/code-review.sh
            fi
          done

2. N8N Workflow Automation

Send hook execution results to N8N for notification automation:

#!/bin/bash
# .claude/hooks/notify-n8n.sh

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

# N8N webhook URL (from environment variable)
WEBHOOK_URL="${N8N_WEBHOOK_URL}"

if [ -z "$WEBHOOK_URL" ]; then
    echo "Warning: N8N_WEBHOOK_URL not set"
    exit 0
fi

# Create notification payload
payload=$(cat <<EOF
{
  "event": "code_review_completed",
  "file": "$file_path",
  "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "status": "success"
}
EOF
)

# Send to N8N
curl -X POST "$WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d "$payload" \
  --silent

exit 0

3. Telegram Notification Integration

#!/usr/bin/env python3
# .claude/hooks/telegram-notify.py

import sys
import json
import os
import requests

def send_telegram_message(message):
    bot_token = os.environ.get('TELEGRAM_BOT_TOKEN')
    chat_id = os.environ.get('TELEGRAM_CHAT_ID')

    if not bot_token or not chat_id:
        print("Warning: Telegram credentials not set")
        return

    url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
    payload = {
        'chat_id': chat_id,
        'text': message,
        'parse_mode': 'Markdown'
    }

    try:
        requests.post(url, json=payload, timeout=5)
    except Exception as e:
        print(f"Warning: Failed to send Telegram notification: {e}")

def main():
    input_data = json.loads(sys.stdin.read())
    file_path = input_data.get('file_path', 'unknown')

    message = f"""
🔍 *Code Review Completed*

📁 File: `{file_path}`
✅ All checks passed
🕐 {input_data.get('metadata', {}).get('timestamp', 'N/A')}
"""

    send_telegram_message(message)
    sys.exit(0)

if __name__ == '__main__':
    main()

Practical Examples and Patterns

1. Gradual Hook Adoption Strategy

Applying all hooks at once can slow down your workflow. A gradual adoption strategy:

graph TD
    A[Phase 1: Non-destructive Hooks] --> B[Phase 2: Warning-level Hooks]
    B --> C[Phase 3: Blocking Hooks]

    A1[Code formatting] --> A
    A2[Audit logging] --> A

    B1[Linting warnings] --> B
    B2[Test coverage check] --> B

    C1[Enforce type check] --> C
    C2[Enforce security scan] --> C

Phase 1 Implementation:

#!/bin/bash
# .claude/hooks/phase1-gentle.sh

input=$(cat)

# Always succeed but provide information
echo "ℹ️  Code formatting applied"
echo "ℹ️  Audit trail recorded"

exit 0

Phase 2 Implementation:

#!/bin/bash
# .claude/hooks/phase2-warnings.sh

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

# Run linting but continue even if it fails
npx eslint "$file_path" || echo "⚠️  Linting issues found"

# Exit with warning code
exit 2

Phase 3 Implementation:

#!/bin/bash
# .claude/hooks/phase3-blocking.sh

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

# Abort on type check failure
npx tsc --noEmit "$file_path"

if [ $? -ne 0 ]; then
    echo "❌ Type check failed - blocking operation"
    exit 1
fi

exit 0

2. Conditional Hook Execution

You don’t need to run all hooks on all files:

#!/bin/bash
# .claude/hooks/conditional-hooks.sh

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

# Conditional hook execution
case "$file_path" in
    *.ts|*.tsx)
        echo "Running TypeScript checks..."
        .claude/hooks/typescript-check.sh <<< "$input"
        ;;
    *.py)
        echo "Running Python checks..."
        .claude/hooks/python-check.sh <<< "$input"
        ;;
    *.md)
        echo "Running Markdown lint..."
        .claude/hooks/markdown-lint.sh <<< "$input"
        ;;
    *)
        echo "No specific checks for this file type"
        ;;
esac

exit 0

3. Hook Performance Optimization

If hooks are too slow, they impair the development experience:

#!/bin/bash
# .claude/hooks/optimized-hook.sh

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

# Set timeout (5 seconds)
TIMEOUT=5

# Parallel execution
(
    timeout $TIMEOUT npx eslint "$file_path" &
    timeout $TIMEOUT npx prettier --check "$file_path" &
    wait
) 2>/dev/null

if [ $? -eq 124 ]; then
    echo "⚠️  Hook timeout - skipping detailed checks"
    exit 2
fi

exit 0

4. Caching for Optimization

#!/bin/bash
# .claude/hooks/cached-checks.sh

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')
content=$(echo "$input" | jq -r '.content')

# Generate content hash
content_hash=$(echo "$content" | sha256sum | cut -d' ' -f1)
cache_dir=".claude/cache"
cache_file="$cache_dir/$content_hash"

mkdir -p "$cache_dir"

# Check cache
if [ -f "$cache_file" ]; then
    cache_result=$(cat "$cache_file")
    echo "✅ Using cached result: $cache_result"
    exit 0
fi

# Perform actual checks
echo "Running checks..."
npx eslint "$file_path"

if [ $? -eq 0 ]; then
    echo "passed" > "$cache_file"
    exit 0
else
    echo "failed" > "$cache_file"
    exit 1
fi

Best Practices and Tips

1. Hook Design Principles

Apply SOLID principles to Hooks:

  • Single Responsibility: One hook, one responsibility
  • Open/Closed: Open for extension, closed for modification
  • Liskov Substitution: Hooks should be interchangeable
  • Interface Segregation: Require only necessary data
  • Dependency Inversion: Depend on abstractions, not concrete implementations

2. Error Handling Strategy

#!/bin/bash
# .claude/hooks/error-handling.sh

set -euo pipefail  # Abort immediately on error

input=$(cat)

# Error log file
ERROR_LOG=".claude/logs/hook-errors.log"
mkdir -p "$(dirname "$ERROR_LOG")"

# Error handler
handle_error() {
    local exit_code=$?
    local line_num=$1

    echo "Error on line $line_num (exit code: $exit_code)" | tee -a "$ERROR_LOG"

    # Log error details
    echo "Input data:" >> "$ERROR_LOG"
    echo "$input" >> "$ERROR_LOG"
    echo "---" >> "$ERROR_LOG"

    exit 1
}

# Set error trap
trap 'handle_error $LINENO' ERR

# Hook logic...
echo "Executing hook logic..."

exit 0

3. Writing Testable Hooks

#!/bin/bash
# .claude/hooks/testable-hook.sh

# Support test mode
TEST_MODE=${TEST_MODE:-false}

if [ "$TEST_MODE" = "true" ]; then
    # Test input data
    input='{"file_path": "test.ts", "content": "// test"}'
else
    # Actual input data
    input=$(cat)
fi

# Execute logic
file_path=$(echo "$input" | jq -r '.file_path')
echo "Processing: $file_path"

exit 0

Testing hooks:

# Run test
TEST_MODE=true .claude/hooks/testable-hook.sh

# Test with actual data
echo '{"file_path": "src/app.ts"}' | .claude/hooks/testable-hook.sh

4. Documentation Template

#!/bin/bash
# .claude/hooks/example-hook.sh

# Hook Information
# Name: Example Hook
# Purpose: Perform basic validation before file save
# Trigger: pre-file-write
# Exit Codes:
#   0 - Success (continue task)
#   1 - Error (abort task)
#   2 - Warning (continue but show warning)
#
# Input JSON Schema:
# {
#   "file_path": "string",
#   "operation": "string",
#   "content": "string",
#   "metadata": {}
# }
#
# Environment Variables:
#   HOOK_DEBUG - Set to "true" for verbose output
#
# Dependencies:
#   - jq (JSON parser)
#   - bash 4.0+
#
# Author: Your Name
# Last Updated: 2025-10-29

# Debug mode
DEBUG=${HOOK_DEBUG:-false}

if [ "$DEBUG" = "true" ]; then
    set -x
fi

# Hook logic...
input=$(cat)
echo "Hook executed successfully"

exit 0

5. Security Considerations

#!/bin/bash
# .claude/hooks/secure-hook.sh

set -euo pipefail

input=$(cat)

# 1. Input validation
if ! echo "$input" | jq empty 2>/dev/null; then
    echo "Error: Invalid JSON input"
    exit 1
fi

# 2. Prevent path injection
file_path=$(echo "$input" | jq -r '.file_path')

# Only allow absolute paths or safely normalize relative paths
if [[ "$file_path" =~ \.\. ]]; then
    echo "Error: Path traversal detected"
    exit 1
fi

# 3. Prevent logging sensitive data
# Don't log content
echo "Processing file: $(basename "$file_path")"

# 4. Validate environment variables
if [ -n "${GITHUB_TOKEN:-}" ]; then
    echo "Warning: Sensitive env var detected, masking in logs"
fi

# 5. Handle temporary files safely
temp_file=$(mktemp)
trap "rm -f $temp_file" EXIT

# Hook logic...

exit 0

Troubleshooting Guide

Common Issues and Solutions

1. Hook Not Executing

Symptom: Hook scripts don’t execute at all

Solution:

# Check execution permissions
ls -la .claude/hooks/

# Grant execution permissions
chmod +x .claude/hooks/*.sh

# Check hook directory
cat .claude/settings.json | jq '.hooks'

2. Slow Hooks

Symptom: Hook execution significantly slows down workflow

Solution:

# Measure hook execution time
time echo '{"file_path": "test.ts"}' | .claude/hooks/slow-hook.sh

# Optimize with parallel execution
# Before: Sequential execution (slow)
check1.sh && check2.sh && check3.sh

# After: Parallel execution (fast)
check1.sh & check2.sh & check3.sh & wait

3. Hook Debugging

#!/bin/bash
# .claude/hooks/debug-hook.sh

# Save debug output to file
DEBUG_LOG=".claude/logs/hook-debug.log"
mkdir -p "$(dirname "$DEBUG_LOG")"

{
    echo "=== Hook Debug Log ==="
    echo "Timestamp: $(date)"
    echo "Input:"
    cat
} | tee -a "$DEBUG_LOG"

# Parse input data
input=$(tail -n 1 "$DEBUG_LOG")

echo "Parsed input: $input" >> "$DEBUG_LOG"

exit 0

4. JSON Parsing Errors

#!/bin/bash
# .claude/hooks/safe-json-parsing.sh

input=$(cat)

# Check if jq is installed
if ! command -v jq &> /dev/null; then
    echo "Error: jq is not installed"
    exit 1
fi

# Validate JSON
if ! echo "$input" | jq empty 2>/dev/null; then
    echo "Error: Invalid JSON input"
    echo "Received: $input"
    exit 1
fi

# Safely extract values
file_path=$(echo "$input" | jq -r '.file_path // "unknown"')

exit 0

Real-World Scenario: Enterprise Environment Setup

Complete Hook System Architecture

graph TB
    A[Claude Task Starts] --> B{Pre-file-write Hook}

    B --> C[Security Scan]
    B --> D[Type Check]
    B --> E[Linting]

    C --> F{All Checks Pass?}
    D --> F
    E --> F

    F -->|Yes| G[Save File]
    F -->|No| H[Abort Task]

    G --> I{Post-file-write Hook}

    I --> J[Formatting]
    I --> K[Audit Log]
    I --> L[Send Notification]

    J --> M[Complete]
    K --> M
    L --> M

Integrated Hook Script

#!/bin/bash
# .claude/hooks/enterprise-review.sh

set -euo pipefail

input=$(cat)
file_path=$(echo "$input" | jq -r '.file_path')

LOG_DIR=".claude/logs/$(date +%Y-%m-%d)"
mkdir -p "$LOG_DIR"

echo "🚀 Enterprise Code Review Pipeline"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# Step 1: Security scan
echo "🔒 Security Scan..."
if command -v semgrep &> /dev/null; then
    semgrep --config=auto "$file_path" --json > "$LOG_DIR/security.json"
    echo "✅ Security scan completed"
else
    echo "⚠️  Semgrep not installed, skipping"
fi

# Step 2: Static analysis
echo "📊 Static Analysis..."
if [[ "$file_path" =~ \.(ts|tsx)$ ]]; then
    npx tsc --noEmit "$file_path" 2>&1 | tee "$LOG_DIR/typecheck.log"
    echo "✅ Type check completed"
fi

# Step 3: Code quality
echo "✨ Code Quality Check..."
if command -v sonar-scanner &> /dev/null; then
    sonar-scanner -Dsonar.sources="$file_path" > "$LOG_DIR/sonar.log"
    echo "✅ SonarQube analysis completed"
fi

# Step 4: Test coverage
echo "🧪 Test Coverage..."
npm run test:coverage -- "$file_path" > "$LOG_DIR/coverage.log" 2>&1 || true

# Step 5: Audit trail
echo "📝 Audit Trail..."
python3 .claude/hooks/audit-trail.py <<< "$input"

# Step 6: Result summary
echo ""
echo "📋 Review Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "File: $file_path"
echo "Timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "Logs: $LOG_DIR/"
echo ""
echo "✅ All checks passed"

exit 0

Conclusion

Claude Code Hook system is a powerful tool that ensures consistency, quality, and security in AI-powered coding workflows. Here’s a summary of what we covered:

Key Takeaways

  1. Hooks are the core of workflow automation

    • Insert user-defined logic at specific points
    • Control Claude’s behavior with exit codes
    • Support various languages and tools
  2. Gradual adoption is key

    • Phase 1: Non-destructive hooks (informational)
    • Phase 2: Warning-level hooks (point out issues)
    • Phase 3: Blocking hooks (abort operations)
  3. Performance and security considerations

    • Improve speed with parallel execution
    • Prevent duplicate work with caching
    • Input validation and safe handling
  4. Practical application patterns

    • Improve efficiency with conditional execution
    • Extend automation with CI/CD integration
    • Ensure transparency with notification systems

Next Steps

  1. Set up basic hooks in your project
  2. Implement automated coding rule validation
  3. Integrate with CI/CD pipeline
  4. Expand to the entire team and improve

With effective use of the hook system, code quality improves, review time decreases, and compliance is automated. Start with small hooks and gradually expand.

References

Read in Other Languages

Was this helpful?

Your support helps me create better content. Buy me a coffee! ☕

About the Author

JK

Kim Jangwook

Full-Stack Developer specializing in AI/LLM

Building AI agent systems, LLM applications, and automation solutions with 10+ years of web development experience. Sharing practical insights on Claude Code, MCP, and RAG systems.