diff --git a/uk/01-slash-commands/pr-slash-command.png b/uk/01-slash-commands/pr-slash-command.png new file mode 100644 index 0000000..789e5c2 Binary files /dev/null and b/uk/01-slash-commands/pr-slash-command.png differ diff --git a/uk/02-memory/memory-ask-claude.png b/uk/02-memory/memory-ask-claude.png new file mode 100644 index 0000000..b202227 Binary files /dev/null and b/uk/02-memory/memory-ask-claude.png differ diff --git a/uk/02-memory/memory-saved.png b/uk/02-memory/memory-saved.png new file mode 100644 index 0000000..1c16eb7 Binary files /dev/null and b/uk/02-memory/memory-saved.png differ diff --git a/uk/03-skills/.gitignore b/uk/03-skills/.gitignore new file mode 100644 index 0000000..28820ae --- /dev/null +++ b/uk/03-skills/.gitignore @@ -0,0 +1,5 @@ +# Local skill testing +.claude/ + +# Blog post outputs +blog-posts/ diff --git a/uk/03-skills/brand-voice/templates/email-template.txt b/uk/03-skills/brand-voice/templates/email-template.txt new file mode 100644 index 0000000..b335fa6 --- /dev/null +++ b/uk/03-skills/brand-voice/templates/email-template.txt @@ -0,0 +1,14 @@ +Тема: [Чітка, орієнтована на вигоду тема] + +Вітаю, [Ім'я]! + +[Вступ: Яка цінність для них] + +[Основна частина: Як це працює / Що вони отримають] + +[Конкретний приклад або вигода] + +[Заклик до дії: Чіткий наступний крок] + +З повагою, +[Ім'я] diff --git a/uk/03-skills/brand-voice/templates/social-post-template.txt b/uk/03-skills/brand-voice/templates/social-post-template.txt new file mode 100644 index 0000000..62e2578 --- /dev/null +++ b/uk/03-skills/brand-voice/templates/social-post-template.txt @@ -0,0 +1,4 @@ +[Хук: Привернути увагу в першому рядку] +[2-3 рядки: Цінність або цікавий факт] +[Заклик до дії: Посилання, питання або залучення] +[Емодзі: 1-2 максимум для візуального інтересу] diff --git a/uk/03-skills/brand-voice/tone-examples.md b/uk/03-skills/brand-voice/tone-examples.md new file mode 100644 index 0000000..da19aaa --- /dev/null +++ b/uk/03-skills/brand-voice/tone-examples.md @@ -0,0 +1,13 @@ +# Приклади тону бренду + +## Захоплива анонс +"Зекономте 8 годин на тиждень на код-рев'ю. Claude переглядає ваші PR автоматично." + +## Емпатична підтримка +"Ми знаємо, що деплої можуть бути стресовими. Claude бере тестування на себе, щоб вам не хвилюватися." + +## Впевнена презентація продукту +"Claude не просто пропонує код. Він розуміє вашу архітектуру і підтримує консистентність." + +## Освітній блог-пост +"Давайте розглянемо, як агенти покращують процеси код-рев'ю. Ось що ми дізналися..." diff --git a/uk/03-skills/code-review/scripts/analyze-metrics.py b/uk/03-skills/code-review/scripts/analyze-metrics.py new file mode 100644 index 0000000..e294973 --- /dev/null +++ b/uk/03-skills/code-review/scripts/analyze-metrics.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +import re +import sys + + +def analyze_code_metrics(code): + """Analyze code for common metrics.""" + + # Count functions + functions = len(re.findall(r"^def\s+\w+", code, re.MULTILINE)) + + # Count classes + classes = len(re.findall(r"^class\s+\w+", code, re.MULTILINE)) + + # Average line length + lines = code.split("\n") + avg_length = sum(len(l) for l in lines) / len(lines) if lines else 0 + + # Estimate complexity + complexity = len(re.findall(r"\b(if|elif|else|for|while|and|or)\b", code)) + + return { + "functions": functions, + "classes": classes, + "avg_line_length": avg_length, + "complexity_score": complexity, + } + + +if __name__ == "__main__": + with open(sys.argv[1]) as f: + code = f.read() + metrics = analyze_code_metrics(code) + for key, value in metrics.items(): + print(f"{key}: {value:.2f}") diff --git a/uk/03-skills/code-review/scripts/compare-complexity.py b/uk/03-skills/code-review/scripts/compare-complexity.py new file mode 100644 index 0000000..d4aba23 --- /dev/null +++ b/uk/03-skills/code-review/scripts/compare-complexity.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +""" +Compare cyclomatic complexity of code before and after changes. +Helps identify if refactoring actually simplifies code structure. +""" + +import re +import sys + + +class ComplexityAnalyzer: + """Analyze code complexity metrics.""" + + def __init__(self, code: str): + self.code = code + self.lines = code.split("\n") + + def calculate_cyclomatic_complexity(self) -> int: + """ + Calculate cyclomatic complexity using McCabe's method. + Count decision points: if, elif, else, for, while, except, and, or + """ + complexity = 1 # Base complexity + + # Count decision points + decision_patterns = [ + r"\bif\b", + r"\belif\b", + r"\bfor\b", + r"\bwhile\b", + r"\bexcept\b", + r"\band\b(?!$)", + r"\bor\b(?!$)", + ] + + for pattern in decision_patterns: + matches = re.findall(pattern, self.code) + complexity += len(matches) + + return complexity + + def calculate_cognitive_complexity(self) -> int: + """ + Calculate cognitive complexity - how hard is it to understand? + Based on nesting depth and control flow. + """ + cognitive = 0 + nesting_depth = 0 + + for line in self.lines: + # Track nesting depth + if re.search(r"^\s*(if|for|while|def|class|try)\b", line): + nesting_depth += 1 + cognitive += nesting_depth + elif re.search(r"^\s*(elif|else|except|finally)\b", line): + cognitive += nesting_depth + + # Reduce nesting when unindenting + if line and not line[0].isspace(): + nesting_depth = 0 + + return cognitive + + def calculate_maintainability_index(self) -> float: + """ + Maintainability Index ranges from 0-100. + > 85: Excellent + > 65: Good + > 50: Fair + < 50: Poor + """ + lines = len(self.lines) + cyclomatic = self.calculate_cyclomatic_complexity() + cognitive = self.calculate_cognitive_complexity() + + # Simplified MI calculation + mi = ( + 171 + - 5.2 * (cyclomatic / lines) + - 0.23 * (cognitive) + - 16.2 * (lines / 1000) + ) + + return max(0, min(100, mi)) + + def get_complexity_report(self) -> dict: + """Generate comprehensive complexity report.""" + return { + "cyclomatic_complexity": self.calculate_cyclomatic_complexity(), + "cognitive_complexity": self.calculate_cognitive_complexity(), + "maintainability_index": round(self.calculate_maintainability_index(), 2), + "lines_of_code": len(self.lines), + "avg_line_length": round( + sum(len(l) for l in self.lines) / len(self.lines), 2 + ) + if self.lines + else 0, + } + + +def compare_files(before_file: str, after_file: str) -> None: + """Compare complexity metrics between two code versions.""" + + with open(before_file) as f: + before_code = f.read() + + with open(after_file) as f: + after_code = f.read() + + before_analyzer = ComplexityAnalyzer(before_code) + after_analyzer = ComplexityAnalyzer(after_code) + + before_metrics = before_analyzer.get_complexity_report() + after_metrics = after_analyzer.get_complexity_report() + + print("=" * 60) + print("CODE COMPLEXITY COMPARISON") + print("=" * 60) + + print("\nBEFORE:") + print(f" Cyclomatic Complexity: {before_metrics['cyclomatic_complexity']}") + print(f" Cognitive Complexity: {before_metrics['cognitive_complexity']}") + print(f" Maintainability Index: {before_metrics['maintainability_index']}") + print(f" Lines of Code: {before_metrics['lines_of_code']}") + print(f" Avg Line Length: {before_metrics['avg_line_length']}") + + print("\nAFTER:") + print(f" Cyclomatic Complexity: {after_metrics['cyclomatic_complexity']}") + print(f" Cognitive Complexity: {after_metrics['cognitive_complexity']}") + print(f" Maintainability Index: {after_metrics['maintainability_index']}") + print(f" Lines of Code: {after_metrics['lines_of_code']}") + print(f" Avg Line Length: {after_metrics['avg_line_length']}") + + print("\nCHANGES:") + cyclomatic_change = ( + after_metrics["cyclomatic_complexity"] - before_metrics["cyclomatic_complexity"] + ) + cognitive_change = ( + after_metrics["cognitive_complexity"] - before_metrics["cognitive_complexity"] + ) + mi_change = ( + after_metrics["maintainability_index"] - before_metrics["maintainability_index"] + ) + loc_change = after_metrics["lines_of_code"] - before_metrics["lines_of_code"] + + print(f" Cyclomatic Complexity: {cyclomatic_change:+d}") + print(f" Cognitive Complexity: {cognitive_change:+d}") + print(f" Maintainability Index: {mi_change:+.2f}") + print(f" Lines of Code: {loc_change:+d}") + + print("\nASSESSMENT:") + if mi_change > 0: + print(" ✅ Code is MORE maintainable") + elif mi_change < 0: + print(" ⚠️ Code is LESS maintainable") + else: + print(" ➡️ Maintainability unchanged") + + if cyclomatic_change < 0: + print(" ✅ Complexity DECREASED") + elif cyclomatic_change > 0: + print(" ⚠️ Complexity INCREASED") + else: + print(" ➡️ Complexity unchanged") + + print("=" * 60) + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python compare-complexity.py ") + sys.exit(1) + + compare_files(sys.argv[1], sys.argv[2]) diff --git a/uk/03-skills/code-review/templates/finding-template.md b/uk/03-skills/code-review/templates/finding-template.md new file mode 100644 index 0000000..d1b99ea --- /dev/null +++ b/uk/03-skills/code-review/templates/finding-template.md @@ -0,0 +1,112 @@ +# Шаблон знахідки код-рев'ю + +Використовуйте цей шаблон для документування кожної знайденої проблеми під час код-рев'ю. + +--- + +## Проблема: [НАЗВА] + +### Серйозність +- [ ] Критична (блокує деплой) +- [ ] Висока (потрібно виправити перед мерджем) +- [ ] Середня (потрібно виправити незабаром) +- [ ] Низька (бажано виправити) + +### Категорія +- [ ] Безпека +- [ ] Продуктивність +- [ ] Якість коду +- [ ] Підтримуваність +- [ ] Тестування +- [ ] Патерн проєктування +- [ ] Документація + +### Розташування +**Файл:** `src/components/UserCard.tsx` + +**Рядки:** 45-52 + +**Функція/Метод:** `renderUserDetails()` + +### Опис проблеми + +**Що:** Опишіть суть проблеми. + +**Чому це важливо:** Поясніть вплив та чому це потрібно виправити. + +**Поточна поведінка:** Покажіть проблемний код або поведінку. + +**Очікувана поведінка:** Опишіть, що повинно відбуватися замість цього. + +### Приклад коду + +#### Поточний (проблемний) + +```typescript +// Shows the N+1 query problem +const users = fetchUsers(); +users.forEach(user => { + const posts = fetchUserPosts(user.id); // Query per user! + renderUserPosts(posts); +}); +``` + +#### Запропоноване виправлення + +```typescript +// Optimized with JOIN query +const usersWithPosts = fetchUsersWithPosts(); +usersWithPosts.forEach(({ user, posts }) => { + renderUserPosts(posts); +}); +``` + +### Аналіз впливу + +| Аспект | Вплив | Серйозність | +|--------|-------|-------------| +| Продуктивність | 100+ запитів для 20 користувачів | Висока | +| Досвід користувача | Повільне завантаження сторінки | Висока | +| Масштабованість | Ламається при масштабуванні | Критична | +| Підтримуваність | Складно дебажити | Середня | + +### Пов'язані проблеми + +- Аналогічна проблема в `AdminUserList.tsx` рядок 120 +- Пов'язаний PR: #456 +- Пов'язана issue: #789 + +### Додаткові ресурси + +- [N+1 Query Problem](https://en.wikipedia.org/wiki/N%2B1_problem) +- [Database Join Documentation](https://docs.example.com/joins) + +### Нотатки рецензента + +- Це поширений патерн у цій кодовій базі +- Варто додати це до гайду зі стилю коду +- Можливо, варто створити допоміжну функцію + +### Відповідь автора (для зворотного зв'язку) + +*Заповнюється автором коду:* + +- [ ] Виправлення реалізовано в коміті: `abc123` +- [ ] Статус виправлення: Завершено / В процесі / Потребує обговорення +- [ ] Питання або зауваження: (опишіть) + +--- + +## Статистика знахідок (для рецензента) + +При рев'ю кількох знахідок, відстежуйте: + +- **Всього знайдено проблем:** X +- **Критичних:** X +- **Високих:** X +- **Середніх:** X +- **Низьких:** X + +**Рекомендація:** ✅ Затвердити / ⚠️ Запросити зміни / 🔄 Потребує обговорення + +**Загальна якість коду:** 1-5 зірок diff --git a/uk/03-skills/code-review/templates/review-checklist.md b/uk/03-skills/code-review/templates/review-checklist.md new file mode 100644 index 0000000..774cfc0 --- /dev/null +++ b/uk/03-skills/code-review/templates/review-checklist.md @@ -0,0 +1,47 @@ +# Чеклист код-рев'ю + +## Чеклист безпеки +- [ ] Немає захардкоджених облікових даних або секретів +- [ ] Валідація введення для всіх даних від користувача +- [ ] Захист від SQL-ін'єкцій (параметризовані запити) +- [ ] CSRF-захист для операцій зі зміною стану +- [ ] Захист від XSS з правильним екрануванням +- [ ] Перевірка автентифікації на захищених ендпоінтах +- [ ] Перевірка авторизації для ресурсів +- [ ] Безпечне хешування паролів (bcrypt, argon2) +- [ ] Немає чутливих даних у логах +- [ ] HTTPS обов'язковий + +## Чеклист продуктивності +- [ ] Немає N+1 запитів +- [ ] Правильне використання індексів +- [ ] Кешування реалізовано де доцільно +- [ ] Немає блокуючих операцій в основному потоці +- [ ] Async/await використовується коректно +- [ ] Великі набори даних пагіновані +- [ ] Пул з'єднань до бази даних +- [ ] Регулярні вирази оптимізовані +- [ ] Немає зайвого створення об'єктів +- [ ] Витоки пам'яті запобігаються + +## Чеклист якості +- [ ] Функції < 50 рядків +- [ ] Зрозумілі назви змінних +- [ ] Немає дублювання коду +- [ ] Правильна обробка помилок +- [ ] Коментарі пояснюють ЧОМУ, а не ЩО +- [ ] Немає console.log у продакшені +- [ ] Перевірка типів (TypeScript/JSDoc) +- [ ] Принципи SOLID дотримані +- [ ] Патерни проєктування застосовані коректно +- [ ] Самодокументований код + +## Чеклист тестування +- [ ] Юніт-тести написані +- [ ] Граничні випадки покриті +- [ ] Сценарії помилок протестовані +- [ ] Інтеграційні тести є +- [ ] Покриття > 80% +- [ ] Немає нестабільних тестів +- [ ] Зовнішні залежності замоковані +- [ ] Зрозумілі назви тестів diff --git a/uk/03-skills/doc-generator/generate-docs.py b/uk/03-skills/doc-generator/generate-docs.py new file mode 100644 index 0000000..3a0f588 --- /dev/null +++ b/uk/03-skills/doc-generator/generate-docs.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +import ast + + +class APIDocExtractor(ast.NodeVisitor): + """Extract API documentation from Python source code.""" + + def __init__(self): + self.endpoints = [] + + def visit_FunctionDef(self, node): + """Extract function documentation.""" + if node.name.startswith("get_") or node.name.startswith("post_"): + doc = ast.get_docstring(node) + endpoint = { + "name": node.name, + "docstring": doc, + "params": [arg.arg for arg in node.args.args], + "returns": self._extract_return_type(node), + } + self.endpoints.append(endpoint) + self.generic_visit(node) + + def _extract_return_type(self, node): + """Extract return type from function annotation.""" + if node.returns: + return ast.unparse(node.returns) + return "Any" + + +def generate_markdown_docs(endpoints: list[dict]) -> str: + """Generate markdown documentation from endpoints.""" + docs = "# API Documentation\n\n" + + for endpoint in endpoints: + docs += f"## {endpoint['name']}\n\n" + docs += f"{endpoint['docstring']}\n\n" + docs += f"**Parameters**: {', '.join(endpoint['params'])}\n\n" + docs += f"**Returns**: {endpoint['returns']}\n\n" + docs += "---\n\n" + + return docs + + +if __name__ == "__main__": + import sys + + with open(sys.argv[1]) as f: + tree = ast.parse(f.read()) + + extractor = APIDocExtractor() + extractor.visit(tree) + + markdown = generate_markdown_docs(extractor.endpoints) + print(markdown) diff --git a/uk/03-skills/refactor/scripts/analyze-complexity.py b/uk/03-skills/refactor/scripts/analyze-complexity.py new file mode 100644 index 0000000..b8ca92c --- /dev/null +++ b/uk/03-skills/refactor/scripts/analyze-complexity.py @@ -0,0 +1,545 @@ +#!/usr/bin/env python3 +""" +Code Complexity Analyzer + +Analyzes code complexity metrics for Python, JavaScript, and TypeScript files. +Helps measure the impact of refactoring by comparing before/after metrics. + +Usage: + python analyze-complexity.py + python analyze-complexity.py # Compare mode + python analyze-complexity.py --dir # Analyze directory + +Metrics: + - Cyclomatic Complexity: Decision points in code + - Cognitive Complexity: How hard is it to understand + - Maintainability Index: Overall maintainability score (0-100) + - Lines of Code: Total lines + - Function Count: Number of functions/methods + - Average Function Length: Lines per function +""" + +import argparse +import os +import re +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, List, Optional + + +@dataclass +class FunctionMetrics: + """Metrics for a single function.""" + name: str + start_line: int + end_line: int + lines: int + cyclomatic_complexity: int + cognitive_complexity: int + parameter_count: int + + +@dataclass +class FileMetrics: + """Metrics for a file.""" + filename: str + lines_of_code: int + blank_lines: int + comment_lines: int + function_count: int + class_count: int + cyclomatic_complexity: int + cognitive_complexity: int + maintainability_index: float + avg_function_length: float + max_function_length: int + functions: List[FunctionMetrics] + + +class ComplexityAnalyzer: + """Analyze code complexity for multiple languages.""" + + # Patterns for different languages + PATTERNS = { + 'python': { + 'function': r'^\s*def\s+(\w+)\s*\(', + 'class': r'^\s*class\s+(\w+)', + 'decision': [r'\bif\b', r'\belif\b', r'\bfor\b', r'\bwhile\b', + r'\bexcept\b', r'\band\b(?!$)', r'\bor\b(?!$)', + r'\bcase\b', r'\btry\b'], + 'comment': r'^\s*#', + 'multiline_comment_start': r'^\s*["\'][\"\'][\"\']', + 'multiline_comment_end': r'["\'][\"\'][\"\']', + }, + 'javascript': { + 'function': r'(?:function\s+(\w+)|(\w+)\s*[=:]\s*(?:async\s+)?(?:function|\([^)]*\)\s*=>))', + 'class': r'class\s+(\w+)', + 'decision': [r'\bif\b', r'\belse\s+if\b', r'\bfor\b', r'\bwhile\b', + r'\bcatch\b', r'\b\?\b', r'\b&&\b', r'\b\|\|\b', + r'\bcase\b', r'\btry\b'], + 'comment': r'^\s*//', + 'multiline_comment_start': r'/\*', + 'multiline_comment_end': r'\*/', + }, + 'typescript': { + 'function': r'(?:function\s+(\w+)|(\w+)\s*[=:]\s*(?:async\s+)?(?:function|\([^)]*\)\s*=>))', + 'class': r'class\s+(\w+)', + 'decision': [r'\bif\b', r'\belse\s+if\b', r'\bfor\b', r'\bwhile\b', + r'\bcatch\b', r'\b\?\b', r'\b&&\b', r'\b\|\|\b', + r'\bcase\b', r'\btry\b'], + 'comment': r'^\s*//', + 'multiline_comment_start': r'/\*', + 'multiline_comment_end': r'\*/', + } + } + + def __init__(self, filepath: str): + self.filepath = filepath + self.filename = os.path.basename(filepath) + self.language = self._detect_language() + self.patterns = self.PATTERNS.get(self.language, self.PATTERNS['python']) + + with open(filepath, 'r', encoding='utf-8', errors='ignore') as f: + self.code = f.read() + self.lines = self.code.split('\n') + + def _detect_language(self) -> str: + """Detect programming language from file extension.""" + ext = os.path.splitext(self.filepath)[1].lower() + ext_map = { + '.py': 'python', + '.js': 'javascript', + '.jsx': 'javascript', + '.ts': 'typescript', + '.tsx': 'typescript', + } + return ext_map.get(ext, 'python') + + def calculate_cyclomatic_complexity(self, code: Optional[str] = None) -> int: + """ + Calculate cyclomatic complexity using McCabe's method. + CC = E - N + 2P where E=edges, N=nodes, P=connected components + Simplified: Count decision points + 1 + """ + if code is None: + code = self.code + + complexity = 1 # Base complexity + + for pattern in self.patterns['decision']: + matches = re.findall(pattern, code) + complexity += len(matches) + + return complexity + + def calculate_cognitive_complexity(self, code: Optional[str] = None) -> int: + """ + Calculate cognitive complexity. + Measures how hard it is to understand the code. + Accounts for nesting depth and control flow breaks. + """ + if code is None: + code = self.code + + lines = code.split('\n') + cognitive = 0 + nesting_depth = 0 + in_function = False + + for line in lines: + stripped = line.strip() + + # Track function boundaries + if re.search(self.patterns['function'], line): + in_function = True + nesting_depth = 0 + + # Increment for control flow structures + if re.search(r'\b(if|for|while|switch)\b', stripped): + nesting_depth += 1 + cognitive += nesting_depth # Nested structures cost more + + elif re.search(r'\b(elif|else if|else|catch|finally)\b', stripped): + cognitive += nesting_depth # Same level as parent + + # Track nesting through braces/indentation + if self.language in ['javascript', 'typescript']: + nesting_depth += stripped.count('{') - stripped.count('}') + nesting_depth = max(0, nesting_depth) + + # Bonus for breaks in linear flow + if re.search(r'\b(break|continue|return|throw)\b', stripped): + if nesting_depth > 1: + cognitive += 1 + + # Bonus for recursion + # (simplified: just look for function calling itself) + + return cognitive + + def calculate_maintainability_index(self) -> float: + """ + Calculate Maintainability Index (0-100). + Based on Halstead Volume, Cyclomatic Complexity, and Lines of Code. + + MI = max(0, (171 - 5.2*ln(V) - 0.23*CC - 16.2*ln(LOC)) * 100/171) + + Interpretation: + - 85-100: Highly maintainable + - 65-84: Moderately maintainable + - 50-64: Difficult to maintain + - 0-49: Very difficult to maintain + """ + import math + + loc = len([l for l in self.lines if l.strip()]) + cc = self.calculate_cyclomatic_complexity() + + # Simplified Halstead Volume approximation + # Count unique operators and operands + operators = len(re.findall(r'[+\-*/%=<>!&|^~]', self.code)) + operands = len(re.findall(r'\b\w+\b', self.code)) + volume = (operators + operands) * math.log2(max(1, operators + operands)) + + # Calculate MI + mi = 171 - 5.2 * math.log(max(1, volume)) - 0.23 * cc - 16.2 * math.log(max(1, loc)) + mi = max(0, min(100, mi * 100 / 171)) + + return round(mi, 2) + + def count_lines(self) -> Dict[str, int]: + """Count different types of lines.""" + total = len(self.lines) + blank = 0 + comment = 0 + in_multiline_comment = False + + for line in self.lines: + stripped = line.strip() + + # Check for multiline comments + if re.search(self.patterns['multiline_comment_start'], stripped): + in_multiline_comment = True + if re.search(self.patterns['multiline_comment_end'], stripped): + in_multiline_comment = False + comment += 1 + continue + + if in_multiline_comment: + comment += 1 + elif not stripped: + blank += 1 + elif re.match(self.patterns['comment'], stripped): + comment += 1 + + return { + 'total': total, + 'blank': blank, + 'comment': comment, + 'code': total - blank - comment + } + + def find_functions(self) -> List[FunctionMetrics]: + """Find all functions and calculate their individual metrics.""" + functions = [] + current_function = None + function_start = 0 + brace_depth = 0 + + for i, line in enumerate(self.lines): + # Check for function definition + match = re.search(self.patterns['function'], line) + if match: + # Save previous function if exists + if current_function: + func_code = '\n'.join(self.lines[function_start:i]) + functions.append(self._create_function_metrics( + current_function, function_start, i - 1, func_code + )) + + current_function = match.group(1) or match.group(2) if match.lastindex and match.lastindex > 1 else match.group(1) + function_start = i + brace_depth = 0 + + # Track braces for JS/TS + if self.language in ['javascript', 'typescript']: + brace_depth += line.count('{') - line.count('}') + + # Don't forget the last function + if current_function: + func_code = '\n'.join(self.lines[function_start:]) + functions.append(self._create_function_metrics( + current_function, function_start, len(self.lines) - 1, func_code + )) + + return functions + + def _create_function_metrics(self, name: str, start: int, end: int, code: str) -> FunctionMetrics: + """Create metrics for a single function.""" + lines = end - start + 1 + + # Count parameters (simplified) + param_match = re.search(r'\(([^)]*)\)', code.split('\n')[0]) + param_count = 0 + if param_match and param_match.group(1).strip(): + param_count = len([p for p in param_match.group(1).split(',') if p.strip()]) + + return FunctionMetrics( + name=name, + start_line=start + 1, + end_line=end + 1, + lines=lines, + cyclomatic_complexity=self.calculate_cyclomatic_complexity(code), + cognitive_complexity=self.calculate_cognitive_complexity(code), + parameter_count=param_count + ) + + def analyze(self) -> FileMetrics: + """Perform complete analysis of the file.""" + line_counts = self.count_lines() + functions = self.find_functions() + + # Count classes + class_count = len(re.findall(self.patterns['class'], self.code)) + + # Calculate averages + func_lengths = [f.lines for f in functions] if functions else [0] + avg_func_length = sum(func_lengths) / len(func_lengths) + max_func_length = max(func_lengths) + + return FileMetrics( + filename=self.filename, + lines_of_code=line_counts['code'], + blank_lines=line_counts['blank'], + comment_lines=line_counts['comment'], + function_count=len(functions), + class_count=class_count, + cyclomatic_complexity=self.calculate_cyclomatic_complexity(), + cognitive_complexity=self.calculate_cognitive_complexity(), + maintainability_index=self.calculate_maintainability_index(), + avg_function_length=round(avg_func_length, 1), + max_function_length=max_func_length, + functions=functions + ) + + +def print_metrics(metrics: FileMetrics, verbose: bool = False) -> None: + """Print metrics in a readable format.""" + print("=" * 60) + print(f"CODE COMPLEXITY ANALYSIS: {metrics.filename}") + print("=" * 60) + + print("\n📊 OVERVIEW") + print("-" * 40) + print(f" Lines of Code: {metrics.lines_of_code}") + print(f" Blank Lines: {metrics.blank_lines}") + print(f" Comment Lines: {metrics.comment_lines}") + print(f" Functions/Methods: {metrics.function_count}") + print(f" Classes: {metrics.class_count}") + + print("\n📈 COMPLEXITY METRICS") + print("-" * 40) + print(f" Cyclomatic Complexity: {metrics.cyclomatic_complexity}") + print(f" Cognitive Complexity: {metrics.cognitive_complexity}") + print(f" Maintainability Index: {metrics.maintainability_index}") + + # Interpret maintainability + mi = metrics.maintainability_index + if mi >= 85: + mi_label = "Highly maintainable ✅" + elif mi >= 65: + mi_label = "Moderately maintainable 🔶" + elif mi >= 50: + mi_label = "Difficult to maintain ⚠️" + else: + mi_label = "Very difficult to maintain ❌" + print(f" → {mi_label}") + + print("\n📐 FUNCTION METRICS") + print("-" * 40) + print(f" Avg Function Length: {metrics.avg_function_length} lines") + print(f" Max Function Length: {metrics.max_function_length} lines") + + if verbose and metrics.functions: + print("\n📋 FUNCTION DETAILS") + print("-" * 40) + for f in sorted(metrics.functions, key=lambda x: x.cyclomatic_complexity, reverse=True): + flag = " ⚠️" if f.cyclomatic_complexity > 10 or f.lines > 50 else "" + print(f" {f.name}() [lines {f.start_line}-{f.end_line}]{flag}") + print(f" - Lines: {f.lines}, CC: {f.cyclomatic_complexity}, " + f"Cognitive: {f.cognitive_complexity}, Params: {f.parameter_count}") + + print("\n" + "=" * 60) + + +def print_comparison(before: FileMetrics, after: FileMetrics) -> None: + """Print comparison between two analyses.""" + print("=" * 70) + print("CODE COMPLEXITY COMPARISON") + print("=" * 70) + + print(f"\n{'Metric':<30} {'Before':<15} {'After':<15} {'Change':<10}") + print("-" * 70) + + def fmt_change(before_val, after_val, lower_is_better=True): + diff = after_val - before_val + if lower_is_better: + symbol = "✅" if diff < 0 else ("⚠️" if diff > 0 else "➖") + else: + symbol = "✅" if diff > 0 else ("⚠️" if diff < 0 else "➖") + return f"{diff:+.1f} {symbol}" if isinstance(diff, float) else f"{diff:+d} {symbol}" + + metrics = [ + ("Lines of Code", before.lines_of_code, after.lines_of_code, True), + ("Function Count", before.function_count, after.function_count, False), + ("Class Count", before.class_count, after.class_count, False), + ("Cyclomatic Complexity", before.cyclomatic_complexity, after.cyclomatic_complexity, True), + ("Cognitive Complexity", before.cognitive_complexity, after.cognitive_complexity, True), + ("Maintainability Index", before.maintainability_index, after.maintainability_index, False), + ("Avg Function Length", before.avg_function_length, after.avg_function_length, True), + ("Max Function Length", before.max_function_length, after.max_function_length, True), + ] + + for name, b_val, a_val, lower_better in metrics: + change = fmt_change(b_val, a_val, lower_better) + print(f"{name:<30} {b_val:<15} {a_val:<15} {change:<10}") + + print("\n" + "=" * 70) + + # Overall assessment + print("\n🎯 ASSESSMENT") + print("-" * 40) + + improvements = 0 + regressions = 0 + + if after.maintainability_index > before.maintainability_index: + print(" ✅ Maintainability improved") + improvements += 1 + elif after.maintainability_index < before.maintainability_index: + print(" ⚠️ Maintainability decreased") + regressions += 1 + + if after.cyclomatic_complexity < before.cyclomatic_complexity: + print(" ✅ Complexity reduced") + improvements += 1 + elif after.cyclomatic_complexity > before.cyclomatic_complexity: + print(" ⚠️ Complexity increased") + regressions += 1 + + if after.avg_function_length < before.avg_function_length: + print(" ✅ Functions are smaller on average") + improvements += 1 + elif after.avg_function_length > before.avg_function_length: + print(" ⚠️ Functions grew larger on average") + regressions += 1 + + print(f"\n Summary: {improvements} improvements, {regressions} regressions") + print("=" * 70) + + +def analyze_directory(directory: str, verbose: bool = False) -> None: + """Analyze all supported files in a directory.""" + supported_extensions = ['.py', '.js', '.jsx', '.ts', '.tsx'] + files = [] + + for root, _, filenames in os.walk(directory): + for filename in filenames: + if any(filename.endswith(ext) for ext in supported_extensions): + files.append(os.path.join(root, filename)) + + if not files: + print(f"No supported files found in {directory}") + return + + print(f"Analyzing {len(files)} files in {directory}...\n") + + total_loc = 0 + total_cc = 0 + total_functions = 0 + all_metrics = [] + + for filepath in sorted(files): + try: + analyzer = ComplexityAnalyzer(filepath) + metrics = analyzer.analyze() + all_metrics.append(metrics) + + total_loc += metrics.lines_of_code + total_cc += metrics.cyclomatic_complexity + total_functions += metrics.function_count + + if verbose: + print_metrics(metrics, verbose=True) + else: + flag = " ⚠️" if metrics.maintainability_index < 65 else "" + print(f" {metrics.filename}: LOC={metrics.lines_of_code}, " + f"CC={metrics.cyclomatic_complexity}, MI={metrics.maintainability_index}{flag}") + except Exception as e: + print(f" Error analyzing {filepath}: {e}") + + print("\n" + "=" * 60) + print("SUMMARY") + print("=" * 60) + print(f" Files analyzed: {len(all_metrics)}") + print(f" Total lines of code: {total_loc}") + print(f" Total complexity: {total_cc}") + print(f" Total functions: {total_functions}") + + if all_metrics: + avg_mi = sum(m.maintainability_index for m in all_metrics) / len(all_metrics) + print(f" Avg maintainability: {avg_mi:.1f}") + + +def main(): + parser = argparse.ArgumentParser( + description='Analyze code complexity metrics', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s myfile.py Analyze single file + %(prog)s before.py after.py Compare two versions + %(prog)s --dir src/ Analyze directory + %(prog)s -v myfile.py Verbose output with function details + """ + ) + parser.add_argument('files', nargs='*', help='File(s) to analyze') + parser.add_argument('--dir', '-d', help='Directory to analyze') + parser.add_argument('--verbose', '-v', action='store_true', help='Show detailed function metrics') + parser.add_argument('--json', '-j', action='store_true', help='Output as JSON') + + args = parser.parse_args() + + if args.dir: + analyze_directory(args.dir, args.verbose) + elif len(args.files) == 1: + analyzer = ComplexityAnalyzer(args.files[0]) + metrics = analyzer.analyze() + + if args.json: + import json + print(json.dumps({ + 'filename': metrics.filename, + 'lines_of_code': metrics.lines_of_code, + 'cyclomatic_complexity': metrics.cyclomatic_complexity, + 'cognitive_complexity': metrics.cognitive_complexity, + 'maintainability_index': metrics.maintainability_index, + 'function_count': metrics.function_count, + 'avg_function_length': metrics.avg_function_length, + }, indent=2)) + else: + print_metrics(metrics, args.verbose) + elif len(args.files) == 2: + before_analyzer = ComplexityAnalyzer(args.files[0]) + after_analyzer = ComplexityAnalyzer(args.files[1]) + before_metrics = before_analyzer.analyze() + after_metrics = after_analyzer.analyze() + print_comparison(before_metrics, after_metrics) + else: + parser.print_help() + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/uk/03-skills/refactor/scripts/detect-smells.py b/uk/03-skills/refactor/scripts/detect-smells.py new file mode 100644 index 0000000..7ecd628 --- /dev/null +++ b/uk/03-skills/refactor/scripts/detect-smells.py @@ -0,0 +1,711 @@ +#!/usr/bin/env python3 +""" +Code Smell Detector + +Detects common code smells in Python, JavaScript, and TypeScript files. +Based on Martin Fowler's catalog of code smells. + +Usage: + python detect-smells.py + python detect-smells.py --dir + python detect-smells.py -v # Verbose with code snippets + +Detects: + - Long Method (>30 lines) + - Long Parameter List (>4 params) + - Duplicate Code (similar code blocks) + - Large Class (>300 lines, >10 methods) + - Dead Code (unused variables/functions) + - Complex Conditionals (deep nesting, long chains) + - Magic Numbers/Strings + - Feature Envy (methods using other class data heavily) + - Comments explaining what (not why) +""" + +import argparse +import os +import re +import sys +from dataclasses import dataclass, field +from enum import Enum +from typing import Dict, List, Optional, Set, Tuple +from collections import defaultdict + + +class SmellSeverity(Enum): + """Severity levels for code smells.""" + LOW = "Low" + MEDIUM = "Medium" + HIGH = "High" + CRITICAL = "Critical" + + +class SmellType(Enum): + """Types of code smells.""" + LONG_METHOD = "Long Method" + LONG_PARAMETER_LIST = "Long Parameter List" + DUPLICATE_CODE = "Duplicate Code" + LARGE_CLASS = "Large Class" + DEAD_CODE = "Dead Code" + COMPLEX_CONDITIONAL = "Complex Conditional" + MAGIC_NUMBER = "Magic Number/String" + FEATURE_ENVY = "Feature Envy" + EXCESSIVE_COMMENTS = "Excessive Comments" + DEEPLY_NESTED = "Deeply Nested Code" + PRIMITIVE_OBSESSION = "Primitive Obsession" + DATA_CLUMPS = "Data Clumps" + SWITCH_STATEMENT = "Switch Statement" + MESSAGE_CHAIN = "Message Chain" + + +@dataclass +class CodeSmell: + """Represents a detected code smell.""" + smell_type: SmellType + severity: SmellSeverity + location: str + line_start: int + line_end: int + description: str + suggestion: str + code_snippet: str = "" + + +@dataclass +class SmellReport: + """Report of all smells found in a file.""" + filename: str + smells: List[CodeSmell] = field(default_factory=list) + + @property + def critical_count(self) -> int: + return sum(1 for s in self.smells if s.severity == SmellSeverity.CRITICAL) + + @property + def high_count(self) -> int: + return sum(1 for s in self.smells if s.severity == SmellSeverity.HIGH) + + @property + def medium_count(self) -> int: + return sum(1 for s in self.smells if s.severity == SmellSeverity.MEDIUM) + + @property + def low_count(self) -> int: + return sum(1 for s in self.smells if s.severity == SmellSeverity.LOW) + + +class SmellDetector: + """Detect code smells in source files.""" + + # Thresholds (configurable) + THRESHOLDS = { + 'long_method_lines': 30, + 'very_long_method_lines': 50, + 'max_parameters': 4, + 'large_class_lines': 300, + 'large_class_methods': 10, + 'max_nesting_depth': 4, + 'long_chain_length': 3, + 'duplicate_min_lines': 5, + } + + def __init__(self, filepath: str): + self.filepath = filepath + self.filename = os.path.basename(filepath) + self.language = self._detect_language() + + with open(filepath, 'r', encoding='utf-8', errors='ignore') as f: + self.code = f.read() + self.lines = self.code.split('\n') + self.smells: List[CodeSmell] = [] + + def _detect_language(self) -> str: + """Detect programming language from file extension.""" + ext = os.path.splitext(self.filepath)[1].lower() + ext_map = { + '.py': 'python', + '.js': 'javascript', + '.jsx': 'javascript', + '.ts': 'typescript', + '.tsx': 'typescript', + } + return ext_map.get(ext, 'python') + + def detect_all(self) -> SmellReport: + """Run all smell detectors.""" + self._detect_long_methods() + self._detect_long_parameter_lists() + self._detect_large_class() + self._detect_complex_conditionals() + self._detect_magic_numbers() + self._detect_excessive_comments() + self._detect_deeply_nested() + self._detect_switch_statements() + self._detect_message_chains() + self._detect_duplicate_code() + self._detect_dead_code() + + return SmellReport(filename=self.filename, smells=self.smells) + + def _get_snippet(self, start: int, end: int, context: int = 2) -> str: + """Get code snippet with context.""" + actual_start = max(0, start - context) + actual_end = min(len(self.lines), end + context) + snippet_lines = [] + for i in range(actual_start, actual_end): + prefix = "→ " if start <= i < end else " " + snippet_lines.append(f"{i+1:4d} {prefix}{self.lines[i]}") + return '\n'.join(snippet_lines) + + def _detect_long_methods(self) -> None: + """Detect methods that are too long.""" + if self.language == 'python': + pattern = r'^\s*def\s+(\w+)\s*\([^)]*\):' + else: + pattern = r'(?:function\s+(\w+)|(\w+)\s*[=:]\s*(?:async\s+)?(?:function|\([^)]*\)\s*=>))' + + current_method = None + method_start = 0 + brace_depth = 0 + indent_level = 0 + + for i, line in enumerate(self.lines): + match = re.search(pattern, line) + if match: + # Check previous method if exists + if current_method: + method_lines = i - method_start + self._check_method_length(current_method, method_start, i - 1, method_lines) + + current_method = match.group(1) or (match.group(2) if match.lastindex and match.lastindex > 1 else None) + method_start = i + indent_level = len(line) - len(line.lstrip()) + + # Track end of Python functions by indentation + if self.language == 'python' and current_method: + if line.strip() and not line.strip().startswith('#'): + current_indent = len(line) - len(line.lstrip()) + if current_indent <= indent_level and i > method_start: + method_lines = i - method_start + self._check_method_length(current_method, method_start, i - 1, method_lines) + current_method = None + + # Check last method + if current_method: + method_lines = len(self.lines) - method_start + self._check_method_length(current_method, method_start, len(self.lines) - 1, method_lines) + + def _check_method_length(self, name: str, start: int, end: int, lines: int) -> None: + """Check if method is too long and add smell if so.""" + if lines > self.THRESHOLDS['very_long_method_lines']: + severity = SmellSeverity.HIGH + desc = f"Method '{name}' is {lines} lines (threshold: {self.THRESHOLDS['long_method_lines']})" + elif lines > self.THRESHOLDS['long_method_lines']: + severity = SmellSeverity.MEDIUM + desc = f"Method '{name}' is {lines} lines (threshold: {self.THRESHOLDS['long_method_lines']})" + else: + return + + self.smells.append(CodeSmell( + smell_type=SmellType.LONG_METHOD, + severity=severity, + location=f"{self.filename}:{start+1}-{end+1}", + line_start=start + 1, + line_end=end + 1, + description=desc, + suggestion="Apply Extract Method to break down into smaller functions", + code_snippet=self._get_snippet(start, min(start + 5, end), 0) + )) + + def _detect_long_parameter_lists(self) -> None: + """Detect functions with too many parameters.""" + if self.language == 'python': + pattern = r'def\s+(\w+)\s*\(([^)]*)\)' + else: + pattern = r'(?:function\s+(\w+)\s*\(([^)]*)\)|(\w+)\s*[=:]\s*(?:async\s+)?(?:function\s*)?\(([^)]*)\))' + + for i, line in enumerate(self.lines): + match = re.search(pattern, line) + if match: + # Safely extract groups + groups = match.groups() + func_name = groups[0] or (groups[2] if len(groups) > 2 else None) + params_str = groups[1] if len(groups) > 1 else "" + if not params_str and len(groups) > 3: + params_str = groups[3] or "" + + # Count parameters + if params_str.strip(): + params = [p.strip() for p in params_str.split(',') if p.strip()] + # Filter out 'self', 'cls' for Python + if self.language == 'python': + params = [p for p in params if p not in ('self', 'cls')] + param_count = len(params) + + if param_count > self.THRESHOLDS['max_parameters']: + severity = SmellSeverity.HIGH if param_count > 6 else SmellSeverity.MEDIUM + self.smells.append(CodeSmell( + smell_type=SmellType.LONG_PARAMETER_LIST, + severity=severity, + location=f"{self.filename}:{i+1}", + line_start=i + 1, + line_end=i + 1, + description=f"Function '{func_name}' has {param_count} parameters (max: {self.THRESHOLDS['max_parameters']})", + suggestion="Consider Introduce Parameter Object or Preserve Whole Object", + code_snippet=self._get_snippet(i, i + 1, 1) + )) + + def _detect_large_class(self) -> None: + """Detect classes that are too large.""" + if self.language == 'python': + class_pattern = r'^\s*class\s+(\w+)' + method_pattern = r'^\s+def\s+\w+' + else: + class_pattern = r'class\s+(\w+)' + method_pattern = r'(?:^\s+\w+\s*\([^)]*\)\s*\{|^\s+(?:async\s+)?\w+\s*=)' + + current_class = None + class_start = 0 + method_count = 0 + class_indent = 0 + + for i, line in enumerate(self.lines): + class_match = re.search(class_pattern, line) + if class_match: + # Check previous class + if current_class: + self._check_class_size(current_class, class_start, i - 1, method_count) + + current_class = class_match.group(1) + class_start = i + method_count = 0 + class_indent = len(line) - len(line.lstrip()) + + # Count methods in current class + if current_class and re.search(method_pattern, line): + method_count += 1 + + # Check last class + if current_class: + self._check_class_size(current_class, class_start, len(self.lines) - 1, method_count) + + def _check_class_size(self, name: str, start: int, end: int, methods: int) -> None: + """Check if class is too large.""" + lines = end - start + 1 + + issues = [] + severity = SmellSeverity.MEDIUM + + if lines > self.THRESHOLDS['large_class_lines']: + issues.append(f"{lines} lines (max: {self.THRESHOLDS['large_class_lines']})") + severity = SmellSeverity.HIGH + + if methods > self.THRESHOLDS['large_class_methods']: + issues.append(f"{methods} methods (max: {self.THRESHOLDS['large_class_methods']})") + if severity != SmellSeverity.HIGH: + severity = SmellSeverity.MEDIUM + + if issues: + self.smells.append(CodeSmell( + smell_type=SmellType.LARGE_CLASS, + severity=severity, + location=f"{self.filename}:{start+1}-{end+1}", + line_start=start + 1, + line_end=end + 1, + description=f"Class '{name}' is too large: {', '.join(issues)}", + suggestion="Apply Extract Class to split responsibilities", + code_snippet=self._get_snippet(start, start + 3, 0) + )) + + def _detect_complex_conditionals(self) -> None: + """Detect complex conditional expressions.""" + for i, line in enumerate(self.lines): + # Count logical operators in line + and_or_count = len(re.findall(r'\b(and|or|&&|\|\|)\b', line)) + + if and_or_count >= 3: + self.smells.append(CodeSmell( + smell_type=SmellType.COMPLEX_CONDITIONAL, + severity=SmellSeverity.MEDIUM, + location=f"{self.filename}:{i+1}", + line_start=i + 1, + line_end=i + 1, + description=f"Complex conditional with {and_or_count} logical operators", + suggestion="Apply Decompose Conditional or Consolidate Conditional Expression", + code_snippet=self._get_snippet(i, i + 1, 1) + )) + + def _detect_magic_numbers(self) -> None: + """Detect magic numbers and strings.""" + # Skip common acceptable values + acceptable = {'0', '1', '-1', '2', '100', 'true', 'false', 'null', 'None', '""', "''"} + + for i, line in enumerate(self.lines): + # Skip comments and imports + stripped = line.strip() + if stripped.startswith('#') or stripped.startswith('//') or \ + stripped.startswith('import') or stripped.startswith('from'): + continue + + # Find numeric literals (excluding in variable names) + numbers = re.findall(r'(? 2: + # Check if it's likely a magic number (in calculation or comparison) + if re.search(rf'[<>=+\-*/]\s*{re.escape(num)}|{re.escape(num)}\s*[<>=+\-*/]', line): + self.smells.append(CodeSmell( + smell_type=SmellType.MAGIC_NUMBER, + severity=SmellSeverity.LOW, + location=f"{self.filename}:{i+1}", + line_start=i + 1, + line_end=i + 1, + description=f"Magic number '{num}' - consider using a named constant", + suggestion="Replace magic number with named constant", + code_snippet=self._get_snippet(i, i + 1, 0) + )) + break # One magic number per line is enough + + def _detect_excessive_comments(self) -> None: + """Detect comments that explain 'what' instead of 'why'.""" + what_patterns = [ + r'#\s*(set|get|return|loop|iterate|check|if|increment|decrement)', + r'//\s*(set|get|return|loop|iterate|check|if|increment|decrement)', + ] + + for i, line in enumerate(self.lines): + for pattern in what_patterns: + if re.search(pattern, line, re.IGNORECASE): + self.smells.append(CodeSmell( + smell_type=SmellType.EXCESSIVE_COMMENTS, + severity=SmellSeverity.LOW, + location=f"{self.filename}:{i+1}", + line_start=i + 1, + line_end=i + 1, + description="Comment explains 'what' not 'why' - consider renaming or removing", + suggestion="Use Extract Method with descriptive name instead of comment", + code_snippet=self._get_snippet(i, i + 1, 0) + )) + break + + def _detect_deeply_nested(self) -> None: + """Detect deeply nested code blocks.""" + max_depth = 0 + current_depth = 0 + depth_start = 0 + + for i, line in enumerate(self.lines): + if self.language == 'python': + # Count by indentation + if line.strip(): + indent = len(line) - len(line.lstrip()) + depth = indent // 4 # Assume 4-space indent + if depth > current_depth: + if depth > max_depth: + max_depth = depth + if depth >= self.THRESHOLDS['max_nesting_depth']: + depth_start = i + current_depth = depth + else: + # Count braces + current_depth += line.count('{') - line.count('}') + if current_depth > max_depth: + max_depth = current_depth + if current_depth >= self.THRESHOLDS['max_nesting_depth']: + depth_start = i + + if max_depth >= self.THRESHOLDS['max_nesting_depth']: + self.smells.append(CodeSmell( + smell_type=SmellType.DEEPLY_NESTED, + severity=SmellSeverity.HIGH if max_depth > 5 else SmellSeverity.MEDIUM, + location=f"{self.filename}:{depth_start+1}", + line_start=depth_start + 1, + line_end=depth_start + 1, + description=f"Code nested {max_depth} levels deep (max: {self.THRESHOLDS['max_nesting_depth']})", + suggestion="Apply Replace Nested Conditional with Guard Clauses or Extract Method", + code_snippet=self._get_snippet(depth_start, depth_start + 5, 0) + )) + + def _detect_switch_statements(self) -> None: + """Detect switch statements that might need polymorphism.""" + if self.language == 'python': + # Python 3.10+ match statements or if/elif chains + pattern = r'^\s*(if|elif).*==.*:' + consecutive_conditions = 0 + chain_start = 0 + + for i, line in enumerate(self.lines): + if re.search(pattern, line): + if consecutive_conditions == 0: + chain_start = i + consecutive_conditions += 1 + else: + if consecutive_conditions >= 4: + self._add_switch_smell(chain_start, i - 1, consecutive_conditions) + consecutive_conditions = 0 + else: + # JavaScript/TypeScript switch + pattern = r'\bswitch\s*\(' + for i, line in enumerate(self.lines): + if re.search(pattern, line): + # Count cases + case_count = 0 + for j in range(i, min(i + 50, len(self.lines))): + case_count += len(re.findall(r'\bcase\b', self.lines[j])) + if case_count >= 4: + self._add_switch_smell(i, i + 1, case_count) + + def _add_switch_smell(self, start: int, end: int, cases: int) -> None: + """Add a switch statement smell.""" + self.smells.append(CodeSmell( + smell_type=SmellType.SWITCH_STATEMENT, + severity=SmellSeverity.MEDIUM, + location=f"{self.filename}:{start+1}", + line_start=start + 1, + line_end=end + 1, + description=f"Switch/case statement with {cases} cases - consider polymorphism", + suggestion="Apply Replace Conditional with Polymorphism", + code_snippet=self._get_snippet(start, start + 5, 0) + )) + + def _detect_message_chains(self) -> None: + """Detect long method chains (train wrecks).""" + chain_pattern = r'(\w+(?:\.\w+\([^)]*\)){3,})' + + for i, line in enumerate(self.lines): + matches = re.findall(chain_pattern, line) + for match in matches: + chain_length = match.count('.') + if chain_length >= self.THRESHOLDS['long_chain_length']: + self.smells.append(CodeSmell( + smell_type=SmellType.MESSAGE_CHAIN, + severity=SmellSeverity.MEDIUM, + location=f"{self.filename}:{i+1}", + line_start=i + 1, + line_end=i + 1, + description=f"Message chain with {chain_length} calls - violates Law of Demeter", + suggestion="Apply Hide Delegate to reduce coupling", + code_snippet=self._get_snippet(i, i + 1, 0) + )) + + def _detect_duplicate_code(self) -> None: + """Detect potential duplicate code blocks (simplified).""" + # Create line hashes for comparison + line_hashes: Dict[str, List[int]] = defaultdict(list) + + for i, line in enumerate(self.lines): + normalized = re.sub(r'\s+', ' ', line.strip()) + if len(normalized) > 20: # Only significant lines + line_hashes[normalized].append(i) + + # Find duplicates + for normalized, positions in line_hashes.items(): + if len(positions) >= 3: # At least 3 occurrences + self.smells.append(CodeSmell( + smell_type=SmellType.DUPLICATE_CODE, + severity=SmellSeverity.MEDIUM, + location=f"{self.filename}:{positions[0]+1}", + line_start=positions[0] + 1, + line_end=positions[0] + 1, + description=f"Potential duplicate code found {len(positions)} times", + suggestion="Apply Extract Method to eliminate duplication", + code_snippet=self._get_snippet(positions[0], positions[0] + 1, 0) + )) + + def _detect_dead_code(self) -> None: + """Detect potentially dead code (simplified).""" + # Look for common dead code patterns + patterns = [ + (r'^\s*#.*TODO.*delete', "TODO to delete"), + (r'^\s*#.*FIXME.*remove', "FIXME to remove"), + (r'^\s*//.*TODO.*delete', "TODO to delete"), + (r'^\s*//.*FIXME.*remove', "FIXME to remove"), + (r'^\s*if\s+False:', "Code behind 'if False'"), + (r'^\s*if\s*\(\s*false\s*\)', "Code behind 'if (false)'"), + ] + + for i, line in enumerate(self.lines): + for pattern, desc in patterns: + if re.search(pattern, line, re.IGNORECASE): + self.smells.append(CodeSmell( + smell_type=SmellType.DEAD_CODE, + severity=SmellSeverity.LOW, + location=f"{self.filename}:{i+1}", + line_start=i + 1, + line_end=i + 1, + description=f"Potential dead code: {desc}", + suggestion="Remove dead code", + code_snippet=self._get_snippet(i, i + 1, 0) + )) + + +def print_report(report: SmellReport, verbose: bool = False) -> None: + """Print smell report in readable format.""" + print("=" * 70) + print(f"CODE SMELL DETECTION REPORT: {report.filename}") + print("=" * 70) + + print(f"\n📊 SUMMARY") + print("-" * 40) + print(f" Total smells found: {len(report.smells)}") + print(f" Critical: {report.critical_count}") + print(f" High: {report.high_count}") + print(f" Medium: {report.medium_count}") + print(f" Low: {report.low_count}") + + if not report.smells: + print("\n✅ No code smells detected!") + print("=" * 70) + return + + # Group by type + by_type: Dict[SmellType, List[CodeSmell]] = defaultdict(list) + for smell in report.smells: + by_type[smell.smell_type].append(smell) + + print(f"\n📋 FINDINGS BY TYPE") + print("-" * 40) + + for smell_type, smells in sorted(by_type.items(), key=lambda x: -len(x[1])): + print(f"\n### {smell_type.value} ({len(smells)} found)") + + for smell in sorted(smells, key=lambda x: x.severity.value): + severity_icon = { + SmellSeverity.CRITICAL: "🔴", + SmellSeverity.HIGH: "🟠", + SmellSeverity.MEDIUM: "🟡", + SmellSeverity.LOW: "🟢", + }[smell.severity] + + print(f"\n {severity_icon} [{smell.severity.value}] {smell.location}") + print(f" {smell.description}") + print(f" 💡 {smell.suggestion}") + + if verbose and smell.code_snippet: + print(f"\n Code:") + for snippet_line in smell.code_snippet.split('\n'): + print(f" {snippet_line}") + + print("\n" + "=" * 70) + print("💡 RECOMMENDED ACTIONS") + print("-" * 40) + + if report.critical_count > 0: + print(" 1. Address CRITICAL issues immediately") + if report.high_count > 0: + print(" 2. Plan to fix HIGH severity issues this sprint") + if report.medium_count > 0: + print(" 3. Schedule MEDIUM issues for upcoming work") + if report.low_count > 0: + print(" 4. Fix LOW issues opportunistically") + + print("\n" + "=" * 70) + + +def analyze_directory(directory: str, verbose: bool = False) -> None: + """Analyze all supported files in a directory.""" + supported_extensions = ['.py', '.js', '.jsx', '.ts', '.tsx'] + files = [] + + for root, _, filenames in os.walk(directory): + for filename in filenames: + if any(filename.endswith(ext) for ext in supported_extensions): + files.append(os.path.join(root, filename)) + + if not files: + print(f"No supported files found in {directory}") + return + + print(f"Scanning {len(files)} files in {directory}...\n") + + total_smells = 0 + total_critical = 0 + total_high = 0 + files_with_smells = 0 + + for filepath in sorted(files): + try: + detector = SmellDetector(filepath) + report = detector.detect_all() + + if report.smells: + files_with_smells += 1 + total_smells += len(report.smells) + total_critical += report.critical_count + total_high += report.high_count + + flag = " 🔴" if report.critical_count else (" 🟠" if report.high_count else " 🟡") + print(f" {report.filename}: {len(report.smells)} smells{flag}") + + if verbose: + for smell in report.smells: + print(f" - [{smell.severity.value}] {smell.smell_type.value}: line {smell.line_start}") + else: + print(f" {report.filename}: ✅ Clean") + + except Exception as e: + print(f" Error analyzing {filepath}: {e}") + + print("\n" + "=" * 60) + print("SUMMARY") + print("=" * 60) + print(f" Files analyzed: {len(files)}") + print(f" Files with smells: {files_with_smells}") + print(f" Total smells found: {total_smells}") + print(f" Critical issues: {total_critical}") + print(f" High severity issues: {total_high}") + + +def main(): + parser = argparse.ArgumentParser( + description='Detect code smells in source files', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s myfile.py Analyze single file + %(prog)s --dir src/ Analyze directory + %(prog)s -v myfile.py Verbose with code snippets + """ + ) + parser.add_argument('file', nargs='?', help='File to analyze') + parser.add_argument('--dir', '-d', help='Directory to analyze') + parser.add_argument('--verbose', '-v', action='store_true', help='Show code snippets') + parser.add_argument('--json', '-j', action='store_true', help='Output as JSON') + + args = parser.parse_args() + + if args.dir: + analyze_directory(args.dir, args.verbose) + elif args.file: + detector = SmellDetector(args.file) + report = detector.detect_all() + + if args.json: + import json + smells_data = [{ + 'type': s.smell_type.value, + 'severity': s.severity.value, + 'location': s.location, + 'line_start': s.line_start, + 'line_end': s.line_end, + 'description': s.description, + 'suggestion': s.suggestion, + } for s in report.smells] + print(json.dumps({ + 'filename': report.filename, + 'total_smells': len(report.smells), + 'critical': report.critical_count, + 'high': report.high_count, + 'medium': report.medium_count, + 'low': report.low_count, + 'smells': smells_data + }, indent=2)) + else: + print_report(report, args.verbose) + else: + parser.print_help() + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/uk/05-mcp/README.md b/uk/05-mcp/README.md new file mode 100644 index 0000000..3e6d476 --- /dev/null +++ b/uk/05-mcp/README.md @@ -0,0 +1,1086 @@ + + + + + + + Claude How To + + +# MCP (Model Context Protocol) + +Ця папка містить вичерпну документацію та приклади конфігурацій MCP-серверів та їх використання з Claude Code. + +## Огляд + +MCP (Model Context Protocol) — це стандартизований спосіб доступу Claude до зовнішніх інструментів, API та джерел даних у реальному часі. На відміну від пам'яті, MCP забезпечує живий доступ до змінюваних даних. + +Ключові характеристики: +- Доступ до зовнішніх сервісів у реальному часі +- Синхронізація даних у реальному часі +- Розширювана архітектура +- Безпечна автентифікація +- Взаємодія на основі інструментів + +## Архітектура MCP + +```mermaid +graph TB + A["Claude"] + B["MCP Server"] + C["External Service"] + + A -->|Request: list_issues| B + B -->|Query| C + C -->|Data| B + B -->|Response| A + + A -->|Request: create_issue| B + B -->|Action| C + C -->|Result| B + B -->|Response| A + + style A fill:#e1f5fe,stroke:#333,color:#333 + style B fill:#f3e5f5,stroke:#333,color:#333 + style C fill:#e8f5e9,stroke:#333,color:#333 +``` + +## Екосистема MCP + +```mermaid +graph TB + A["Claude"] -->|MCP| B["Filesystem
MCP Server"] + A -->|MCP| C["GitHub
MCP Server"] + A -->|MCP| D["Database
MCP Server"] + A -->|MCP| E["Slack
MCP Server"] + A -->|MCP| F["Google Docs
MCP Server"] + + B -->|File I/O| G["Local Files"] + C -->|API| H["GitHub Repos"] + D -->|Query| I["PostgreSQL/MySQL"] + E -->|Messages| J["Slack Workspace"] + F -->|Docs| K["Google Drive"] + + style A fill:#e1f5fe,stroke:#333,color:#333 + style B fill:#f3e5f5,stroke:#333,color:#333 + style C fill:#f3e5f5,stroke:#333,color:#333 + style D fill:#f3e5f5,stroke:#333,color:#333 + style E fill:#f3e5f5,stroke:#333,color:#333 + style F fill:#f3e5f5,stroke:#333,color:#333 + style G fill:#e8f5e9,stroke:#333,color:#333 + style H fill:#e8f5e9,stroke:#333,color:#333 + style I fill:#e8f5e9,stroke:#333,color:#333 + style J fill:#e8f5e9,stroke:#333,color:#333 + style K fill:#e8f5e9,stroke:#333,color:#333 +``` + +## Методи встановлення MCP + +Claude Code підтримує кілька транспортних протоколів для підключення до MCP-серверів: + +### HTTP-транспорт (рекомендовано) + +```bash +# Базове HTTP-підключення +claude mcp add --transport http notion https://mcp.notion.com/mcp + +# HTTP з заголовком автентифікації +claude mcp add --transport http secure-api https://api.example.com/mcp \ + --header "Authorization: Bearer your-token" +``` + +### Stdio-транспорт (локальний) + +Для локально запущених MCP-серверів: + +```bash +# Локальний Node.js-сервер +claude mcp add --transport stdio myserver -- npx @myorg/mcp-server + +# Зі змінними оточення +claude mcp add --transport stdio myserver --env KEY=value -- npx server +``` + +### SSE-транспорт (застарілий) + +Транспорт Server-Sent Events застарілий на користь `http`, але все ще підтримується: + +```bash +claude mcp add --transport sse legacy-server https://example.com/sse +``` + +### Примітка для Windows + +На нативній Windows (не WSL) використовуйте `cmd /c` для команд npx: + +```bash +claude mcp add --transport stdio my-server -- cmd /c npx -y @some/package +``` + +### Автентифікація OAuth 2.0 + +Claude Code підтримує OAuth 2.0 для MCP-серверів, що його потребують. При підключенні до сервера з OAuth Claude Code обробляє весь потік автентифікації: + +```bash +# Підключення до MCP-сервера з OAuth (інтерактивний потік) +claude mcp add --transport http my-service https://my-service.example.com/mcp + +# Попередньо налаштовані облікові дані OAuth для неінтерактивного встановлення +claude mcp add --transport http my-service https://my-service.example.com/mcp \ + --client-id "your-client-id" \ + --client-secret "your-client-secret" \ + --callback-port 8080 +``` + +| Функція | Опис | +|---------|------| +| **Інтерактивний OAuth** | Використовуйте `/mcp` для запуску OAuth-потоку через браузер | +| **Попередньо налаштовані OAuth-клієнти** | Вбудовані OAuth-клієнти для популярних сервісів: Notion, Stripe та інших (v2.1.30+) | +| **Попередньо налаштовані облікові дані** | Прапорці `--client-id`, `--client-secret`, `--callback-port` для автоматизованого встановлення | +| **Зберігання токенів** | Токени зберігаються безпечно у системному keychain | +| **Step-up auth** | Підтримка step-up автентифікації для привілейованих операцій | +| **Кешування виявлення** | Метадані OAuth discovery кешуються для швидших перепідключень | +| **Перевизначення метаданих** | `oauth.authServerMetadataUrl` у `.mcp.json` для перевизначення стандартного OAuth metadata discovery | + +#### Перевизначення OAuth Metadata Discovery + +Якщо ваш MCP-сервер повертає помилки на стандартному ендпоінті OAuth metadata (`/.well-known/oauth-authorization-server`), але має робочий OIDC-ендпоінт, ви можете вказати Claude Code отримувати метадані OAuth з конкретного URL. Встановіть `authServerMetadataUrl` в об'єкті `oauth` конфігурації сервера: + +```json +{ + "mcpServers": { + "my-server": { + "type": "http", + "url": "https://mcp.example.com/mcp", + "oauth": { + "authServerMetadataUrl": "https://auth.example.com/.well-known/openid-configuration" + } + } + } +} +``` + +URL повинен використовувати `https://`. Ця опція потребує Claude Code v2.1.64 або новішої. + +### MCP-конектори Claude.ai + +MCP-сервери, налаштовані у вашому обліковому записі Claude.ai, автоматично доступні в Claude Code. Будь-які MCP-підключення, налаштовані через веб-інтерфейс Claude.ai, будуть доступні без додаткової конфігурації. + +MCP-конектори Claude.ai також доступні в режимі `--print` (v2.1.83+), що дозволяє неінтерактивне та скриптове використання. + +Для вимкнення MCP-серверів Claude.ai у Claude Code встановіть змінну оточення `ENABLE_CLAUDEAI_MCP_SERVERS` у `false`: + +```bash +ENABLE_CLAUDEAI_MCP_SERVERS=false claude +``` + +> **Примітка:** Ця функція доступна лише для користувачів, авторизованих через облікові записи Claude.ai. + +## Процес налаштування MCP + +```mermaid +sequenceDiagram + participant User + participant Claude as Claude Code + participant Config as Config File + participant Service as External Service + + User->>Claude: Type /mcp + Claude->>Claude: List available MCP servers + Claude->>User: Show options + User->>Claude: Select GitHub MCP + Claude->>Config: Update configuration + Config->>Claude: Activate connection + Claude->>Service: Test connection + Service-->>Claude: Authentication successful + Claude->>User: ✅ MCP connected! +``` + +## Пошук інструментів MCP + +Коли описи інструментів MCP перевищують 10% контекстного вікна, Claude Code автоматично вмикає пошук інструментів для ефективного вибору правильних інструментів без перевантаження контексту моделі. + +| Налаштування | Значення | Опис | +|-------------|----------|------| +| `ENABLE_TOOL_SEARCH` | `auto` (за замовч.) | Автоматично вмикається, коли описи інструментів перевищують 10% контексту | +| `ENABLE_TOOL_SEARCH` | `auto:` | Автоматично вмикається при користувацькому порозі `N` інструментів | +| `ENABLE_TOOL_SEARCH` | `true` | Завжди увімкнено незалежно від кількості інструментів | +| `ENABLE_TOOL_SEARCH` | `false` | Вимкнено; всі описи інструментів надсилаються повністю | + +> **Примітка:** Пошук інструментів потребує Sonnet 4 або новіший, або Opus 4 або новіший. Моделі Haiku не підтримують пошук інструментів. + +## Динамічне оновлення інструментів + +Claude Code підтримує сповіщення MCP `list_changed`. Коли MCP-сервер динамічно додає, видаляє або змінює доступні інструменти, Claude Code отримує оновлення та автоматично коригує список інструментів — без потреби перепідключення або перезапуску. + +## MCP Apps + +MCP Apps — перше офіційне розширення MCP, що дозволяє викликам інструментів MCP повертати інтерактивні UI-компоненти, які рендеряться безпосередньо в інтерфейсі чату. Замість текстових відповідей MCP-сервери можуть надавати інтерактивні дашборди, форми, візуалізації даних та багатокрокові воркфлови — все відображається прямо в розмові. + +## MCP Elicitation + +MCP-сервери можуть запитувати структурований ввід від користувача через інтерактивні діалоги (v2.1.49+). Це дозволяє MCP-серверу запитувати додаткову інформацію під час воркфлову — наприклад, підтвердження, вибір зі списку опцій або заповнення обов'язкових полів — додаючи інтерактивність до взаємодії з MCP-серверами. + +## Ліміт описів інструментів та інструкцій + +Починаючи з v2.1.84, Claude Code встановлює **ліміт 2 КБ** на описи та інструкції інструментів для кожного MCP-сервера. Це запобігає споживанню надмірного контексту окремими серверами з занадто багатослівними визначеннями інструментів. + +## MCP Prompts як слеш-команди + +MCP-сервери можуть надавати промпти, що відображаються як слеш-команди в Claude Code. Промпти доступні за конвенцією іменування: + +``` +/mcp____ +``` + +Наприклад, якщо сервер `github` надає промпт `review`, його можна викликати як `/mcp__github__review`. + +## Дедуплікація серверів + +Коли один і той самий MCP-сервер визначений на кількох рівнях (local, project, user), локальна конфігурація має пріоритет. Це дозволяє перевизначати налаштування MCP рівня проєкту або користувача локальними кастомізаціями без конфліктів. + +## MCP-ресурси через @-згадки + +Ви можете посилатися на MCP-ресурси безпосередньо в промптах через синтаксис `@`: + +``` +@server-name:protocol://resource/path +``` + +Наприклад, для посилання на конкретний ресурс бази даних: + +``` +@database:postgres://mydb/users +``` + +Це дозволяє Claude отримувати та включати вміст MCP-ресурсів у контекст розмови. + +## Рівні MCP + +Конфігурації MCP можна зберігати на різних рівнях з різним ступенем поширення: + +| Рівень | Розташування | Опис | Доступний для | Потребує підтвердження | +|--------|-------------|------|---------------|----------------------| +| **Local** (за замовч.) | `~/.claude.json` (під шляхом проєкту) | Приватний для поточного користувача, лише поточний проєкт (раніше називався `project`) | Лише ви | Ні | +| **Project** | `.mcp.json` | Комітиться в git-репозиторій | Члени команди | Так (при першому використанні) | +| **User** | `~/.claude.json` | Доступний у всіх проєктах (раніше називався `global`) | Лише ви | Ні | + +### Використання рівня Project + +Зберігайте конфігурації MCP, специфічні для проєкту, у `.mcp.json`: + +```json +{ + "mcpServers": { + "github": { + "type": "http", + "url": "https://api.github.com/mcp" + } + } +} +``` + +Члени команди побачать запит на підтвердження при першому використанні MCP проєкту. + +## Управління конфігурацією MCP + +### Додавання MCP-серверів + +```bash +# Додати HTTP-сервер +claude mcp add --transport http github https://api.github.com/mcp + +# Додати локальний stdio-сервер +claude mcp add --transport stdio database -- npx @company/db-server + +# Список всіх MCP-серверів +claude mcp list + +# Деталі конкретного сервера +claude mcp get github + +# Видалити MCP-сервер +claude mcp remove github + +# Скинути вибори підтвердження для проєкту +claude mcp reset-project-choices + +# Імпорт з Claude Desktop +claude mcp add-from-claude-desktop +``` + +## Таблиця доступних MCP-серверів + +| MCP-сервер | Призначення | Типові інструменти | Авторизація | Реальний час | +|-----------|------------|-------------------|-------------|-------------| +| **Filesystem** | Файлові операції | read, write, delete | Дозволи ОС | ✅ Так | +| **GitHub** | Управління репозиторіями | list_prs, create_issue, push | OAuth | ✅ Так | +| **Slack** | Командна комунікація | send_message, list_channels | Токен | ✅ Так | +| **Database** | SQL-запити | query, insert, update | Облікові дані | ✅ Так | +| **Google Docs** | Доступ до документів | read, write, share | OAuth | ✅ Так | +| **Asana** | Управління проєктами | create_task, update_status | API Key | ✅ Так | +| **Stripe** | Платіжні дані | list_charges, create_invoice | API Key | ✅ Так | +| **Memory** | Постійна пам'ять | store, retrieve, delete | Локальна | ❌ Ні | + +## Практичні приклади + +### Приклад 1: Конфігурація GitHub MCP + +**Файл:** `.mcp.json` (корінь проєкту) + +```json +{ + "mcpServers": { + "github": { + "command": "npx", + "args": ["@modelcontextprotocol/server-github"], + "env": { + "GITHUB_TOKEN": "${GITHUB_TOKEN}" + } + } + } +} +``` + +**Доступні інструменти GitHub MCP:** + +#### Управління Pull Request +- `list_prs` — список усіх PR у репозиторії +- `get_pr` — деталі PR, включаючи diff +- `create_pr` — створення нового PR +- `update_pr` — оновлення опису/назви PR +- `merge_pr` — мердж PR у main +- `review_pr` — додавання коментарів рев'ю + +**Приклад запиту:** +``` +/mcp__github__get_pr 456 + +# Повертає: +Title: Add dark mode support +Author: @alice +Description: Implements dark theme using CSS variables +Status: OPEN +Reviewers: @bob, @charlie +``` + +#### Управління Issue +- `list_issues` — список усіх issue +- `get_issue` — деталі issue +- `create_issue` — створення нового issue +- `close_issue` — закриття issue +- `add_comment` — додавання коментаря + +#### Інформація про репозиторій +- `get_repo_info` — деталі репозиторію +- `list_files` — структура файлового дерева +- `get_file_content` — читання вмісту файлу +- `search_code` — пошук по кодовій базі + +#### Операції з комітами +- `list_commits` — історія комітів +- `get_commit` — деталі конкретного коміту +- `create_commit` — створення нового коміту + +**Налаштування**: +```bash +export GITHUB_TOKEN="your_github_token" +# Або додайте через CLI: +claude mcp add --transport stdio github -- npx @modelcontextprotocol/server-github +``` + +### Підстановка змінних оточення в конфігурації + +Конфігурації MCP підтримують підстановку змінних оточення з резервними значеннями. Синтаксис `${VAR}` та `${VAR:-default}` працює в полях: `command`, `args`, `env`, `url` та `headers`. + +```json +{ + "mcpServers": { + "api-server": { + "type": "http", + "url": "${API_BASE_URL:-https://api.example.com}/mcp", + "headers": { + "Authorization": "Bearer ${API_KEY}", + "X-Custom-Header": "${CUSTOM_HEADER:-default-value}" + } + }, + "local-server": { + "command": "${MCP_BIN_PATH:-npx}", + "args": ["${MCP_PACKAGE:-@company/mcp-server}"], + "env": { + "DB_URL": "${DATABASE_URL:-postgresql://localhost/dev}" + } + } + } +} +``` + +Змінні розгортаються під час виконання: +- `${VAR}` — використовує змінну оточення, помилка якщо не встановлена +- `${VAR:-default}` — використовує змінну оточення, резервне значення якщо не встановлена + +### Приклад 2: Налаштування Database MCP + +**Конфігурація:** + +```json +{ + "mcpServers": { + "database": { + "command": "npx", + "args": ["@modelcontextprotocol/server-database"], + "env": { + "DATABASE_URL": "postgresql://user:pass@localhost/mydb" + } + } + } +} +``` + +**Приклад використання:** + +```markdown +User: Fetch all users with more than 10 orders + +Claude: I'll query your database to find that information. + +# Використання інструменту MCP database: +SELECT u.*, COUNT(o.id) as order_count +FROM users u +LEFT JOIN orders o ON u.id = o.user_id +GROUP BY u.id +HAVING COUNT(o.id) > 10 +ORDER BY order_count DESC; + +# Результати: +- Alice: 15 orders +- Bob: 12 orders +- Charlie: 11 orders +``` + +**Налаштування**: +```bash +export DATABASE_URL="postgresql://user:pass@localhost/mydb" +# Або додайте через CLI: +claude mcp add --transport stdio database -- npx @modelcontextprotocol/server-database +``` + +### Приклад 3: Мульти-MCP воркфлов + +**Сценарій: генерація щоденного звіту** + +```markdown +# Щоденний звіт з використанням кількох MCP + +## Налаштування +1. GitHub MCP — метрики PR +2. Database MCP — дані продажів +3. Slack MCP — публікація звіту +4. Filesystem MCP — збереження звіту + +## Воркфлов + +### Крок 1: Отримання даних GitHub +/mcp__github__list_prs completed:true last:7days + +### Крок 2: Запит до бази даних +SELECT COUNT(*) as sales, SUM(amount) as revenue +FROM orders +WHERE created_at > NOW() - INTERVAL '1 day' + +### Крок 3: Генерація звіту + +### Крок 4: Збереження у файлову систему + +### Крок 5: Публікація в Slack +``` + +### Приклад 4: Операції Filesystem MCP + +**Конфігурація:** + +```json +{ + "mcpServers": { + "filesystem": { + "command": "npx", + "args": ["@modelcontextprotocol/server-filesystem", "/home/user/projects"] + } + } +} +``` + +**Доступні операції:** + +| Операція | Команда | Призначення | +|---------|---------|------------| +| Список файлів | `ls ~/projects` | Показати вміст каталогу | +| Читання файлу | `cat src/main.ts` | Читання вмісту файлу | +| Запис файлу | `create docs/api.md` | Створення нового файлу | +| Редагування файлу | `edit src/app.ts` | Модифікація файлу | +| Пошук | `grep "async function"` | Пошук у файлах | +| Видалення | `rm old-file.js` | Видалення файлу | + +## MCP vs Пам'ять: матриця вибору + +```mermaid +graph TD + A["Need external data?"] + A -->|No| B["Use Memory"] + A -->|Yes| C["Does it change frequently?"] + C -->|No/Rarely| B + C -->|Yes/Often| D["Use MCP"] + + B -->|Stores| E["Preferences
Context
History"] + D -->|Accesses| F["Live APIs
Databases
Services"] + + style A fill:#fff3e0,stroke:#333,color:#333 + style B fill:#e1f5fe,stroke:#333,color:#333 + style C fill:#fff3e0,stroke:#333,color:#333 + style D fill:#f3e5f5,stroke:#333,color:#333 + style E fill:#e8f5e9,stroke:#333,color:#333 + style F fill:#e8f5e9,stroke:#333,color:#333 +``` + +## Патерн запит/відповідь + +```mermaid +sequenceDiagram + participant App as Claude + participant MCP as MCP Server + participant DB as Database + + App->>MCP: Request: "SELECT * FROM users WHERE id=1" + MCP->>DB: Execute query + DB-->>MCP: Result set + MCP-->>App: Return parsed data + App->>App: Process result + App->>App: Continue task + + Note over MCP,DB: Real-time access
No caching +``` + +## Змінні оточення + +Зберігайте конфіденційні облікові дані у змінних оточення: + +```bash +# ~/.bashrc або ~/.zshrc +export GITHUB_TOKEN="ghp_xxxxxxxxxxxxx" +export DATABASE_URL="postgresql://user:pass@localhost/mydb" +export SLACK_TOKEN="xoxb-xxxxxxxxxxxxx" +``` + +Потім посилайтесь на них у конфігурації MCP: + +```json +{ + "env": { + "GITHUB_TOKEN": "${GITHUB_TOKEN}" + } +} +``` + +## Claude як MCP-сервер (`claude mcp serve`) + +Сам Claude Code може працювати як MCP-сервер для інших додатків. Це дозволяє зовнішнім інструментам, редакторам та системам автоматизації використовувати можливості Claude через стандартний протокол MCP. + +```bash +# Запуск Claude Code як MCP-сервера через stdio +claude mcp serve +``` + +Інші додатки можуть підключатися до цього сервера як до будь-якого stdio-сервера MCP. Наприклад, додавання Claude Code як MCP-сервера в іншому екземплярі Claude Code: + +```bash +claude mcp add --transport stdio claude-agent -- claude mcp serve +``` + +Це корисно для побудови мультиагентних воркфловів, де один екземпляр Claude оркеструє інший. + +## Managed MCP Configuration (Enterprise) + +Для корпоративних розгортань IT-адміністратори можуть застосовувати політики MCP-серверів через конфігураційний файл `managed-mcp.json`. Цей файл забезпечує ексклюзивний контроль над дозволеними або заблокованими MCP-серверами на рівні організації. + +**Розташування:** +- macOS: `/Library/Application Support/ClaudeCode/managed-mcp.json` +- Linux: `~/.config/ClaudeCode/managed-mcp.json` +- Windows: `%APPDATA%\ClaudeCode\managed-mcp.json` + +**Функції:** +- `allowedMcpServers` — білий список дозволених серверів +- `deniedMcpServers` — чорний список заборонених серверів +- Підтримує зіставлення за назвою сервера, командою та URL-патернами +- Загальноорганізаційні політики MCP застосовуються перед конфігурацією користувача +- Запобігає неавторизованим підключенням серверів + +**Приклад конфігурації:** + +```json +{ + "allowedMcpServers": [ + { + "serverName": "github", + "serverUrl": "https://api.github.com/mcp" + }, + { + "serverName": "company-internal", + "serverCommand": "company-mcp-server" + } + ], + "deniedMcpServers": [ + { + "serverName": "untrusted-*" + }, + { + "serverUrl": "http://*" + } + ] +} +``` + +> **Примітка:** Коли і `allowedMcpServers`, і `deniedMcpServers` збігаються з сервером, правило заборони має пріоритет. + +## MCP-сервери плагінів + +Плагіни можуть включати власні MCP-сервери, які автоматично доступні при встановленні плагіна. MCP-сервери плагінів визначаються двома способами: + +1. **Окремий `.mcp.json`** — розміщення файлу `.mcp.json` у кореневому каталозі плагіна +2. **Вбудований у `plugin.json`** — визначення MCP-серверів безпосередньо у маніфесті плагіна + +Використовуйте змінну `${CLAUDE_PLUGIN_ROOT}` для посилання на шляхи відносно каталогу встановлення плагіна: + +```json +{ + "mcpServers": { + "plugin-tools": { + "command": "node", + "args": ["${CLAUDE_PLUGIN_ROOT}/dist/mcp-server.js"], + "env": { + "CONFIG_PATH": "${CLAUDE_PLUGIN_ROOT}/config.json" + } + } + } +} +``` + +## MCP, обмежений субагентом + +MCP-сервери можна визначати вбудовано у фронтматері агента через ключ `mcpServers:`, обмежуючи їх конкретним субагентом, а не всім проєктом. Це корисно, коли агенту потрібен доступ до конкретного MCP-сервера, який не потрібен іншим агентам у воркфлові. + +```yaml +--- +mcpServers: + my-tool: + type: http + url: https://my-tool.example.com/mcp +--- + +You are an agent with access to my-tool for specialized operations. +``` + +MCP-сервери, обмежені субагентом, доступні лише в контексті виконання цього агента і не поширюються на батьківського чи суміжних агентів. + +## Ліміти виводу MCP + +Claude Code встановлює ліміти на вивід інструментів MCP для запобігання переповненню контексту: + +| Ліміт | Поріг | Поведінка | +|-------|-------|----------| +| **Попередження** | 10 000 токенів | Відображається попередження про великий вивід | +| **Максимум за замовч.** | 25 000 токенів | Вивід обрізається за цим лімітом | +| **Збереження на диск** | 50 000 символів | Результати інструментів, що перевищують 50K символів, зберігаються на диск | + +Максимальний ліміт виводу налаштовується через змінну оточення `MAX_MCP_OUTPUT_TOKENS`: + +```bash +# Збільшити максимальний вивід до 50 000 токенів +export MAX_MCP_OUTPUT_TOKENS=50000 +``` + +## Розв'язання проблеми роздування контексту через виконання коду + +З масштабуванням MCP підключення до десятків серверів із сотнями або тисячами інструментів створює значну проблему: **роздування контексту** (context bloat). Це, мабуть, найбільша проблема MCP у масштабі, і інженерна команда Anthropic запропонувала елегантне рішення — використання виконання коду замість прямих викликів інструментів. + +> **Джерело**: [Code Execution with MCP: Building More Efficient Agents](https://www.anthropic.com/engineering/code-execution-with-mcp) — блог Anthropic Engineering + +### Проблема: два джерела марнування токенів + +**1. Визначення інструментів перевантажують контекстне вікно** + +Більшість MCP-клієнтів завантажують усі визначення інструментів наперед. При підключенні до тисяч інструментів модель повинна обробити сотні тисяч токенів, перш ніж прочитати запит користувача. + +**2. Проміжні результати споживають додаткові токени** + +Кожен проміжний результат інструменту проходить через контекст моделі. Наприклад, при перенесенні транскрипту зустрічі з Google Drive до Salesforce — повний транскрипт проходить через контекст **двічі**: при читанні та при записі в призначення. Дводинна зустріч може означати 50 000+ додаткових токенів. + +```mermaid +graph LR + A["Model"] -->|"Tool Call: getDocument"| B["MCP Server"] + B -->|"Full transcript (50K tokens)"| A + A -->|"Tool Call: updateRecord
(re-sends full transcript)"| B + B -->|"Confirmation"| A + + style A fill:#ffcdd2,stroke:#333,color:#333 + style B fill:#f3e5f5,stroke:#333,color:#333 +``` + +### Рішення: MCP-інструменти як код-API + +Замість передачі визначень інструментів та результатів через контекстне вікно, агент **пише код**, який викликає MCP-інструменти як API. Код виконується в ізольованому середовищі, і лише кінцевий результат повертається до моделі. + +```mermaid +graph LR + A["Model"] -->|"Writes code"| B["Code Execution
Environment"] + B -->|"Calls tools directly"| C["MCP Servers"] + C -->|"Data stays in
execution env"| B + B -->|"Only final result
(minimal tokens)"| A + + style A fill:#c8e6c9,stroke:#333,color:#333 + style B fill:#e1f5fe,stroke:#333,color:#333 + style C fill:#f3e5f5,stroke:#333,color:#333 +``` + +#### Як це працює + +MCP-інструменти представляються як файлове дерево типізованих функцій: + +``` +servers/ +├── google-drive/ +│ ├── getDocument.ts +│ └── index.ts +├── salesforce/ +│ ├── updateRecord.ts +│ └── index.ts +└── ... +``` + +Кожен файл інструменту містить типізовану обгортку: + +```typescript +// ./servers/google-drive/getDocument.ts +import { callMCPTool } from "../../../client.js"; + +interface GetDocumentInput { + documentId: string; +} + +interface GetDocumentResponse { + content: string; +} + +export async function getDocument( + input: GetDocumentInput +): Promise { + return callMCPTool( + 'google_drive__get_document', input + ); +} +``` + +Агент пише код для оркестрації інструментів: + +```typescript +import * as gdrive from './servers/google-drive'; +import * as salesforce from './servers/salesforce'; + +// Дані передаються безпосередньо між інструментами — ніколи через модель +const transcript = ( + await gdrive.getDocument({ documentId: 'abc123' }) +).content; + +await salesforce.updateRecord({ + objectType: 'SalesMeeting', + recordId: '00Q5f000001abcXYZ', + data: { Notes: transcript } +}); +``` + +**Результат: використання токенів знижується з ~150 000 до ~2 000 — зменшення на 98,7%.** + +### Ключові переваги + +| Перевага | Опис | +|---------|------| +| **Прогресивне розкриття** | Агент переглядає файлову систему для завантаження лише потрібних визначень інструментів | +| **Ефективність контексту** | Дані фільтруються/трансформуються в середовищі виконання перед поверненням до моделі | +| **Потужний контроль потоку** | Цикли, умови та обробка помилок виконуються в коді без повернення через модель | +| **Збереження конфіденційності** | Проміжні дані (персональні дані, чутливі записи) залишаються в середовищі виконання; ніколи не потрапляють у контекст моделі | +| **Збереження стану** | Агенти можуть зберігати проміжні результати у файли та будувати повторно використовувані функції | + +#### Приклад: фільтрація великих наборів даних + +```typescript +// Без виконання коду — всі 10 000 рядків проходять через контекст +// TOOL CALL: gdrive.getSheet(sheetId: 'abc123') +// -> повертає 10 000 рядків у контекст + +// З виконанням коду — фільтрація в середовищі виконання +const allRows = await gdrive.getSheet({ sheetId: 'abc123' }); +const pendingOrders = allRows.filter( + row => row["Status"] === 'pending' +); +console.log(`Found ${pendingOrders.length} pending orders`); +console.log(pendingOrders.slice(0, 5)); // Лише 5 рядків потрапляють до моделі +``` + +#### Приклад: цикл без повернення через модель + +```typescript +// Опитування для сповіщення про деплой — виконується цілком у коді +let found = false; +while (!found) { + const messages = await slack.getChannelHistory({ + channel: 'C123456' + }); + found = messages.some( + m => m.text.includes('deployment complete') + ); + if (!found) await new Promise(r => setTimeout(r, 5000)); +} +console.log('Deployment notification received'); +``` + +### Компроміси + +Виконання коду додає власну складність. Запуск коду, згенерованого агентом, потребує: + +- **Безпечного ізольованого середовища виконання** з відповідними лімітами ресурсів +- **Моніторингу та логування** виконаного коду +- Додаткових **інфраструктурних витрат** порівняно з прямими викликами інструментів + +Переваги — зменшення вартості токенів, нижча затримка, покращена композиція інструментів — слід зважити проти цих витрат на впровадження. Для агентів з кількома MCP-серверами прямі виклики інструментів можуть бути простішими. Для агентів у масштабі (десятки серверів, сотні інструментів) виконання коду — значне покращення. + +### MCPorter: середовище виконання для композиції MCP-інструментів + +[MCPorter](https://github.com/steipete/mcporter) — TypeScript-середовище виконання та CLI-інструментарій для виклику MCP-серверів без шаблонного коду, що допомагає зменшити роздування контексту через вибіркове надання інструментів та типізовані обгортки. + +**Що вирішує:** замість завантаження всіх визначень інструментів з усіх MCP-серверів наперед, MCPorter дозволяє виявляти, інспектувати та викликати конкретні інструменти за потребою — тримаючи контекст компактним. + +**Ключові функції:** + +| Функція | Опис | +|---------|------| +| **Zero-config discovery** | Автовиявлення MCP-серверів з Cursor, Claude, Codex або локальних конфігурацій | +| **Типізовані клієнти інструментів** | `mcporter emit-ts` генерує `.d.ts` інтерфейси та готові обгортки | +| **Composable API** | `createServerProxy()` надає інструменти як camelCase-методи з хелперами `.text()`, `.json()`, `.markdown()` | +| **Генерація CLI** | `mcporter generate-cli` перетворює будь-який MCP-сервер у CLI з фільтрацією `--include-tools` / `--exclude-tools` | +| **Приховування параметрів** | Опціональні параметри приховані за замовчуванням, зменшуючи багатослівність схеми | + +**Встановлення:** + +```bash +npx mcporter list # Без встановлення — виявлення серверів миттєво +pnpm add mcporter # Додати до проєкту +brew install steipete/tap/mcporter # macOS через Homebrew +``` + +**Приклад — композиція інструментів у TypeScript:** + +```typescript +import { createRuntime, createServerProxy } from "mcporter"; + +const runtime = await createRuntime(); +const gdrive = createServerProxy(runtime, "google-drive"); +const salesforce = createServerProxy(runtime, "salesforce"); + +// Дані передаються між інструментами без проходження через контекст моделі +const doc = await gdrive.getDocument({ documentId: "abc123" }); +await salesforce.updateRecord({ + objectType: "SalesMeeting", + recordId: "00Q5f000001abcXYZ", + data: { Notes: doc.text() } +}); +``` + +**Приклад — виклик інструменту через CLI:** + +```bash +# Виклик конкретного інструменту +npx mcporter call linear.create_comment issueId:ENG-123 body:'Looks good!' + +# Список доступних серверів та інструментів +npx mcporter list +``` + +MCPorter доповнює підхід з виконанням коду, описаний вище, надаючи інфраструктуру для виклику MCP-інструментів як типізованих API — спрощуючи утримання проміжних даних поза контекстом моделі. + +## Найкращі практики + +### Безпекові рекомендації + +#### Рекомендовано ✅ +- Використовуйте змінні оточення для всіх облікових даних +- Регулярно ротуйте токени та API-ключі (рекомендовано щомісяця) +- Використовуйте токени лише для читання, де можливо +- Обмежуйте область доступу MCP-серверів до мінімально необхідної +- Моніторте використання MCP-серверів та журнали доступу +- Використовуйте OAuth для зовнішніх сервісів, де доступно +- Впроваджуйте rate limiting для MCP-запитів +- Тестуйте MCP-підключення перед продакшен-використанням +- Документуйте всі активні MCP-підключення +- Тримайте пакети MCP-серверів оновленими + +#### Не рекомендовано ❌ +- Не хардкодьте облікові дані в конфігураційних файлах +- Не комітьте токени чи секрети в git +- Не поширюйте токени в командних чатах чи листах +- Не використовуйте персональні токени для командних проєктів +- Не надавайте зайвих дозволів +- Не ігноруйте помилки автентифікації +- Не відкривайте MCP-ендпоінти публічно +- Не запускайте MCP-сервери з правами root/admin +- Не кешуйте чутливі дані в журналах +- Не вимикайте механізми автентифікації + +### Найкращі практики конфігурації + +1. **Контроль версій**: тримайте `.mcp.json` у git, але використовуйте змінні оточення для секретів +2. **Мінімальні привілеї**: надавайте мінімальні дозволи для кожного MCP-сервера +3. **Ізоляція**: запускайте різні MCP-сервери в окремих процесах, де можливо +4. **Моніторинг**: логуйте всі MCP-запити та помилки для аудиту +5. **Тестування**: тестуйте всі конфігурації MCP перед розгортанням у продакшен + +### Поради щодо продуктивності + +- Кешуйте часто запитувані дані на рівні додатку +- Використовуйте конкретні MCP-запити для зменшення передачі даних +- Моніторте час відповіді MCP-операцій +- Розгляньте rate limiting для зовнішніх API +- Використовуйте пакетну обробку для кількох операцій + +## Інструкції з встановлення + +### Передумови +- Node.js та npm встановлені +- Claude Code CLI встановлений +- API-токени/облікові дані для зовнішніх сервісів + +### Покрокове налаштування + +1. **Додайте перший MCP-сервер** через CLI (приклад: GitHub): +```bash +claude mcp add --transport stdio github -- npx @modelcontextprotocol/server-github +``` + + Або створіть файл `.mcp.json` у корені проєкту: +```json +{ + "mcpServers": { + "github": { + "command": "npx", + "args": ["@modelcontextprotocol/server-github"], + "env": { + "GITHUB_TOKEN": "${GITHUB_TOKEN}" + } + } + } +} +``` + +2. **Встановіть змінні оточення:** +```bash +export GITHUB_TOKEN="your_github_personal_access_token" +``` + +3. **Протестуйте підключення:** +```bash +claude /mcp +``` + +4. **Використовуйте MCP-інструменти:** +```bash +/mcp__github__list_prs +/mcp__github__create_issue "Title" "Description" +``` + +### Встановлення конкретних сервісів + +**GitHub MCP:** +```bash +npm install -g @modelcontextprotocol/server-github +``` + +**Database MCP:** +```bash +npm install -g @modelcontextprotocol/server-database +``` + +**Filesystem MCP:** +```bash +npm install -g @modelcontextprotocol/server-filesystem +``` + +**Slack MCP:** +```bash +npm install -g @modelcontextprotocol/server-slack +``` + +## Усунення несправностей + +### MCP-сервер не знайдено +```bash +# Перевірте, чи встановлений MCP-сервер +npm list -g @modelcontextprotocol/server-github + +# Встановіть, якщо відсутній +npm install -g @modelcontextprotocol/server-github +``` + +### Помилка автентифікації +```bash +# Перевірте, чи встановлена змінна оточення +echo $GITHUB_TOKEN + +# Перевстановіть, якщо потрібно +export GITHUB_TOKEN="your_token" + +# Перевірте дозволи токена +# Дозволи GitHub-токена: https://github.com/settings/tokens +``` + +### Таймаут з'єднання +- Перевірте мережеве з'єднання: `ping api.github.com` +- Перевірте доступність API-ендпоінта +- Перевірте rate limits API +- Спробуйте збільшити таймаут у конфігурації +- Перевірте наявність фаєрволу або проксі + +### MCP-сервер аварійно завершується +- Перевірте журнали MCP-сервера: `~/.claude/logs/` +- Перевірте, що всі змінні оточення встановлені +- Перевірте дозволи файлів +- Спробуйте перевстановити пакет MCP-сервера +- Перевірте конфліктуючі процеси на тому ж порту + +## Пов'язані концепції + +### Пам'ять vs MCP +- **Пам'ять**: зберігає постійні, незмінні дані (налаштування, контекст, історія) +- **MCP**: доступ до живих, змінюваних даних (API, бази даних, сервіси реального часу) + +### Коли використовувати кожен +- **Пам'ять** для: налаштувань користувача, історії розмов, засвоєного контексту +- **MCP** для: поточних GitHub issue, живих запитів до бази даних, даних реального часу + +### Інтеграція з іншими функціями Claude +- Комбінуйте MCP з пам'яттю для збагаченого контексту +- Використовуйте MCP-інструменти в промптах для кращого міркування +- Поєднуйте кілька MCP для складних воркфловів + +## Додаткові ресурси + +- [Офіційна документація MCP](https://code.claude.com/docs/en/mcp) +- [Специфікація протоколу MCP](https://modelcontextprotocol.io/specification) +- [GitHub-репозиторій MCP](https://github.com/modelcontextprotocol/servers) +- [Доступні MCP-сервери](https://github.com/modelcontextprotocol/servers) +- [MCPorter](https://github.com/steipete/mcporter) — TypeScript-середовище та CLI для виклику MCP-серверів +- [Code Execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp) — блог Anthropic Engineering +- [Довідник CLI Claude Code](https://code.claude.com/docs/en/cli-reference) +- [Документація Claude API](https://docs.anthropic.com) + +--- +**Останнє оновлення**: 9 квітня 2026 +**Версія Claude Code**: 2.1.97 +**Сумісні моделі**: Claude Sonnet 4.6, Claude Opus 4.6, Claude Haiku 4.5 diff --git a/uk/05-mcp/database-mcp.json b/uk/05-mcp/database-mcp.json new file mode 100644 index 0000000..eaa832a --- /dev/null +++ b/uk/05-mcp/database-mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "database": { + "command": "npx", + "args": ["@modelcontextprotocol/server-database"], + "env": { + "DATABASE_URL": "postgresql://user:pass@localhost/mydb" + } + } + } +} diff --git a/uk/05-mcp/filesystem-mcp.json b/uk/05-mcp/filesystem-mcp.json new file mode 100644 index 0000000..c76fdf6 --- /dev/null +++ b/uk/05-mcp/filesystem-mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "filesystem": { + "command": "npx", + "args": ["@modelcontextprotocol/server-filesystem", "/home/user/projects"] + } + } +} diff --git a/uk/05-mcp/github-mcp.json b/uk/05-mcp/github-mcp.json new file mode 100644 index 0000000..0f62131 --- /dev/null +++ b/uk/05-mcp/github-mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "github": { + "command": "npx", + "args": ["@modelcontextprotocol/server-github"], + "env": { + "GITHUB_TOKEN": "${GITHUB_TOKEN}" + } + } + } +} diff --git a/uk/05-mcp/multi-mcp.json b/uk/05-mcp/multi-mcp.json new file mode 100644 index 0000000..62d8fa5 --- /dev/null +++ b/uk/05-mcp/multi-mcp.json @@ -0,0 +1,29 @@ +{ + "mcpServers": { + "github": { + "command": "npx", + "args": ["@modelcontextprotocol/server-github"], + "env": { + "GITHUB_TOKEN": "${GITHUB_TOKEN}" + } + }, + "database": { + "command": "npx", + "args": ["@modelcontextprotocol/server-database"], + "env": { + "DATABASE_URL": "${DATABASE_URL}" + } + }, + "slack": { + "command": "npx", + "args": ["@modelcontextprotocol/server-slack"], + "env": { + "SLACK_TOKEN": "${SLACK_TOKEN}" + } + }, + "filesystem": { + "command": "npx", + "args": ["@modelcontextprotocol/server-filesystem", "/home/user/projects"] + } + } +} diff --git a/uk/06-hooks/context-tracker-tiktoken.py b/uk/06-hooks/context-tracker-tiktoken.py new file mode 100644 index 0000000..11786bf --- /dev/null +++ b/uk/06-hooks/context-tracker-tiktoken.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +""" +Context Usage Tracker (tiktoken version) - Tracks token consumption per request. + +Uses UserPromptSubmit as "pre-message" hook and Stop as "post-response" hook +to calculate the delta in token usage for each request. + +This version uses tiktoken with p50k_base encoding for ~90-95% accuracy. +Requires: pip install tiktoken + +For a zero-dependency version, see context-tracker.py. + +Usage: + Configure both hooks to use the same script: + - UserPromptSubmit: saves current token count + - Stop: calculates delta and reports usage +""" +import json +import os +import sys +import tempfile + +try: + import tiktoken + + TIKTOKEN_AVAILABLE = True +except ImportError: + TIKTOKEN_AVAILABLE = False + print( + "Warning: tiktoken not installed. Install with: pip install tiktoken", + file=sys.stderr, + ) + +# Configuration +CONTEXT_LIMIT = 128000 # Claude's context window (adjust for your model) + + +def get_state_file(session_id: str) -> str: + """Get temp file path for storing pre-message token count, isolated by session.""" + return os.path.join(tempfile.gettempdir(), f"claude-context-{session_id}.json") + + +def count_tokens(text: str) -> int: + """ + Count tokens using tiktoken with p50k_base encoding. + + This provides ~90-95% accuracy compared to Claude's actual tokenizer. + Falls back to character estimation if tiktoken is not available. + + Note: Anthropic hasn't released an official offline tokenizer. + tiktoken with p50k_base is a reasonable approximation since both + Claude and GPT models use BPE (byte-pair encoding). + """ + if TIKTOKEN_AVAILABLE: + enc = tiktoken.get_encoding("p50k_base") + return len(enc.encode(text)) + else: + # Fallback to character estimation (~4 chars per token) + return len(text) // 4 + + +def read_transcript(transcript_path: str) -> str: + """Read and concatenate all content from transcript file.""" + if not transcript_path or not os.path.exists(transcript_path): + return "" + + content = [] + with open(transcript_path, "r") as f: + for line in f: + try: + entry = json.loads(line.strip()) + # Extract text content from various message formats + if "message" in entry: + msg = entry["message"] + if isinstance(msg.get("content"), str): + content.append(msg["content"]) + elif isinstance(msg.get("content"), list): + for block in msg["content"]: + if isinstance(block, dict) and block.get("type") == "text": + content.append(block.get("text", "")) + except json.JSONDecodeError: + continue + + return "\n".join(content) + + +def handle_user_prompt_submit(data: dict) -> None: + """Pre-message hook: Save current token count before request.""" + session_id = data.get("session_id", "unknown") + transcript_path = data.get("transcript_path", "") + + transcript_content = read_transcript(transcript_path) + current_tokens = count_tokens(transcript_content) + + # Save to temp file for later comparison + state_file = get_state_file(session_id) + with open(state_file, "w") as f: + json.dump({"pre_tokens": current_tokens}, f) + + +def handle_stop(data: dict) -> None: + """Post-response hook: Calculate and report token delta.""" + session_id = data.get("session_id", "unknown") + transcript_path = data.get("transcript_path", "") + + transcript_content = read_transcript(transcript_path) + current_tokens = count_tokens(transcript_content) + + # Load pre-message count + state_file = get_state_file(session_id) + pre_tokens = 0 + if os.path.exists(state_file): + try: + with open(state_file, "r") as f: + state = json.load(f) + pre_tokens = state.get("pre_tokens", 0) + except (json.JSONDecodeError, IOError): + pass + + # Calculate delta + delta_tokens = current_tokens - pre_tokens + remaining = CONTEXT_LIMIT - current_tokens + percentage = (current_tokens / CONTEXT_LIMIT) * 100 + + # Report usage (stderr so it doesn't interfere with hook output) + method = "tiktoken" if TIKTOKEN_AVAILABLE else "estimated" + print( + f"Context ({method}): ~{current_tokens:,} tokens " + f"({percentage:.1f}% used, ~{remaining:,} remaining)", + file=sys.stderr, + ) + if delta_tokens > 0: + print(f"This request: ~{delta_tokens:,} tokens", file=sys.stderr) + + +def main(): + data = json.load(sys.stdin) + event = data.get("hook_event_name", "") + + if event == "UserPromptSubmit": + handle_user_prompt_submit(data) + elif event == "Stop": + handle_stop(data) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/uk/06-hooks/context-tracker.py b/uk/06-hooks/context-tracker.py new file mode 100644 index 0000000..eb5daf9 --- /dev/null +++ b/uk/06-hooks/context-tracker.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +""" +Context Usage Tracker - Tracks token consumption per request. + +Uses UserPromptSubmit as "pre-message" hook and Stop as "post-response" hook +to calculate the delta in token usage for each request. + +This version uses character-based estimation (no dependencies). +For better accuracy, see context-tracker-tiktoken.py. + +Usage: + Configure both hooks to use the same script: + - UserPromptSubmit: saves current token count + - Stop: calculates delta and reports usage +""" +import json +import os +import sys +import tempfile + +# Configuration +CONTEXT_LIMIT = 128000 # Claude's context window (adjust for your model) + + +def get_state_file(session_id: str) -> str: + """Get temp file path for storing pre-message token count, isolated by session.""" + return os.path.join(tempfile.gettempdir(), f"claude-context-{session_id}.json") + + +def count_tokens_estimate(text: str) -> int: + """ + Estimate token count using character-based approximation. + + Uses ~4 characters per token ratio, which provides ~80-90% accuracy + for English text. Less accurate for code and non-English text. + """ + return len(text) // 4 + + +def read_transcript(transcript_path: str) -> str: + """Read and concatenate all content from transcript file.""" + if not transcript_path or not os.path.exists(transcript_path): + return "" + + content = [] + with open(transcript_path, "r") as f: + for line in f: + try: + entry = json.loads(line.strip()) + # Extract text content from various message formats + if "message" in entry: + msg = entry["message"] + if isinstance(msg.get("content"), str): + content.append(msg["content"]) + elif isinstance(msg.get("content"), list): + for block in msg["content"]: + if isinstance(block, dict) and block.get("type") == "text": + content.append(block.get("text", "")) + except json.JSONDecodeError: + continue + + return "\n".join(content) + + +def handle_user_prompt_submit(data: dict) -> None: + """Pre-message hook: Save current token count before request.""" + session_id = data.get("session_id", "unknown") + transcript_path = data.get("transcript_path", "") + + transcript_content = read_transcript(transcript_path) + current_tokens = count_tokens_estimate(transcript_content) + + # Save to temp file for later comparison + state_file = get_state_file(session_id) + with open(state_file, "w") as f: + json.dump({"pre_tokens": current_tokens}, f) + + +def handle_stop(data: dict) -> None: + """Post-response hook: Calculate and report token delta.""" + session_id = data.get("session_id", "unknown") + transcript_path = data.get("transcript_path", "") + + transcript_content = read_transcript(transcript_path) + current_tokens = count_tokens_estimate(transcript_content) + + # Load pre-message count + state_file = get_state_file(session_id) + pre_tokens = 0 + if os.path.exists(state_file): + try: + with open(state_file, "r") as f: + state = json.load(f) + pre_tokens = state.get("pre_tokens", 0) + except (json.JSONDecodeError, IOError): + pass + + # Calculate delta + delta_tokens = current_tokens - pre_tokens + remaining = CONTEXT_LIMIT - current_tokens + percentage = (current_tokens / CONTEXT_LIMIT) * 100 + + # Report usage (stderr so it doesn't interfere with hook output) + print( + f"Context (estimated): ~{current_tokens:,} tokens " + f"({percentage:.1f}% used, ~{remaining:,} remaining)", + file=sys.stderr, + ) + if delta_tokens > 0: + print(f"This request: ~{delta_tokens:,} tokens", file=sys.stderr) + + +def main(): + data = json.load(sys.stdin) + event = data.get("hook_event_name", "") + + if event == "UserPromptSubmit": + handle_user_prompt_submit(data) + elif event == "Stop": + handle_stop(data) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/uk/06-hooks/dependency-check.sh b/uk/06-hooks/dependency-check.sh new file mode 100644 index 0000000..c659b9f --- /dev/null +++ b/uk/06-hooks/dependency-check.sh @@ -0,0 +1,156 @@ +#!/bin/bash +# Check for known vulnerabilities in dependencies after manifest files are modified. +# Hook: PostToolUse:Write + +FILE=$1 + +if [ -z "$FILE" ]; then + echo "Usage: $0 " + exit 0 +fi + +# Use basename for matching — $1 may be an absolute path +BASENAME=$(basename "$FILE") + +# Only run when a dependency manifest is written +case "$BASENAME" in + package.json|package-lock.json|yarn.lock|pnpm-lock.yaml| \ + requirements.txt|Pipfile|Pipfile.lock|pyproject.toml| \ + go.mod|go.sum| \ + Cargo.toml|Cargo.lock| \ + Gemfile|Gemfile.lock| \ + composer.json|composer.lock| \ + pom.xml|build.gradle|build.gradle.kts) + echo "📦 Dependency manifest updated: $FILE — scanning for vulnerabilities..." + ;; + *) + exit 0 + ;; +esac + +ISSUES_FOUND=0 + +# ── npm / yarn / pnpm ──────────────────────────────────────────────────────── +if [[ "$BASENAME" == package*.json || "$BASENAME" == yarn.lock || "$BASENAME" == pnpm-lock.yaml ]]; then + if command -v npm &>/dev/null; then + echo "🔍 Running npm audit..." + if ! npm audit --audit-level=high --json 2>/dev/null | \ + python3 -c " +import sys, json +data = json.load(sys.stdin) +vulns = data.get('metadata', {}).get('vulnerabilities', {}) +high = vulns.get('high', 0) + vulns.get('critical', 0) +if high: + print(f' ⚠️ {high} high/critical npm vulnerabilities found. Run: npm audit fix') + sys.exit(1) +" 2>/dev/null; then + ISSUES_FOUND=1 + else + echo " ✅ No high/critical npm vulnerabilities" + fi + fi + + if command -v yarn &>/dev/null && [[ "$BASENAME" == yarn.lock ]]; then + echo "🔍 Running yarn audit..." + if ! yarn audit --level high --json 2>/dev/null | \ + grep -q '"type":"auditAdvisory"' 2>/dev/null; then + echo " ✅ No high yarn vulnerabilities" + else + echo " ⚠️ yarn audit found vulnerabilities. Run: yarn audit --level high" + ISSUES_FOUND=1 + fi + fi +fi + +# ── Python ─────────────────────────────────────────────────────────────────── +if [[ "$BASENAME" == requirements.txt || "$BASENAME" == Pipfile* || "$BASENAME" == pyproject.toml ]]; then + if command -v pip-audit &>/dev/null; then + echo "🔍 Running pip-audit..." + if pip-audit --format=json 2>/dev/null | \ + python3 -c " +import sys, json +data = json.load(sys.stdin) +vulns = [d for d in data.get('dependencies', []) if d.get('vulns')] +if vulns: + for dep in vulns: + for v in dep['vulns']: + print(f' ⚠️ {dep[\"name\"]} {dep[\"version\"]}: {v[\"id\"]} — {v[\"fix_versions\"]}') + sys.exit(1) +" 2>/dev/null; then + echo " ✅ No Python vulnerabilities found" + else + ISSUES_FOUND=1 + echo " Run: pip-audit for details" + fi + elif command -v safety &>/dev/null; then + echo "🔍 Running safety check..." + OUTPUT=$(safety check --short-report 2>&1) + EXIT_CODE=$? + if [ $EXIT_CODE -eq 0 ]; then + echo " ✅ No Python vulnerabilities found" + elif echo "$OUTPUT" | grep -qiE "vulnerability|CVE|insecure"; then + echo "$OUTPUT" + ISSUES_FOUND=1 + else + echo " ⚠️ safety check could not complete (network or config error)" >&2 + fi + fi +fi + +# ── Go ─────────────────────────────────────────────────────────────────────── +if [[ "$BASENAME" == go.mod || "$BASENAME" == go.sum ]]; then + if command -v govulncheck &>/dev/null; then + echo "🔍 Running govulncheck..." + OUTPUT=$(govulncheck ./... 2>&1) + EXIT_CODE=$? + if [ $EXIT_CODE -eq 0 ]; then + echo " ✅ No Go vulnerabilities found" + elif echo "$OUTPUT" | grep -q "Vulnerability #"; then + echo "$OUTPUT" + ISSUES_FOUND=1 + else + echo " ⚠️ govulncheck could not complete: $OUTPUT" >&2 + fi + fi +fi + +# ── Rust ───────────────────────────────────────────────────────────────────── +if [[ "$BASENAME" == Cargo.toml || "$BASENAME" == Cargo.lock ]]; then + if command -v cargo-audit &>/dev/null; then + echo "🔍 Running cargo audit..." + if ! cargo audit 2>/dev/null; then + ISSUES_FOUND=1 + else + echo " ✅ No Rust vulnerabilities found" + fi + fi +fi + +# ── Ruby ───────────────────────────────────────────────────────────────────── +if [[ "$BASENAME" == Gemfile || "$BASENAME" == Gemfile.lock ]]; then + if command -v bundler-audit &>/dev/null; then + echo "🔍 Running bundler-audit..." + bundler-audit check --update 2>/dev/null || ISSUES_FOUND=1 + fi +fi + +# ── Generic fallback: trivy ────────────────────────────────────────────────── +if command -v trivy &>/dev/null; then + echo "🔍 Running trivy fs scan..." + if ! trivy fs --exit-code 1 --severity HIGH,CRITICAL --quiet . 2>/dev/null; then + ISSUES_FOUND=1 + else + echo " ✅ trivy found no HIGH/CRITICAL issues" + fi +fi + +if [ "$ISSUES_FOUND" -eq 0 ]; then + echo "✅ Dependency check passed — no vulnerabilities detected" +else + echo "" + echo "⚠️ Vulnerabilities detected. Review and update dependencies before committing." + echo " This hook is advisory only and will not block your workflow." +fi + +# Always exit 0 — this hook warns but does not block +exit 0 diff --git a/uk/06-hooks/format-code.sh b/uk/06-hooks/format-code.sh new file mode 100644 index 0000000..9841b9e --- /dev/null +++ b/uk/06-hooks/format-code.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# Auto-format code after writing +# Hook: PostToolUse:Write +# +# Reads the target file path from stdin JSON and runs the appropriate formatter +# in-place on the file after Claude writes it. +# +# Compatible with: macOS, Linux, Windows (Git Bash) + +# Read JSON input from stdin (Claude Code hook protocol) +INPUT=$(cat) + +# Extract file_path using sed (compatible with all platforms) +FILE_PATH=$(echo "$INPUT" | sed -n 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + +if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then + exit 0 +fi + +# Detect file type and format accordingly +case "$FILE_PATH" in + *.js|*.jsx|*.ts|*.tsx) + if command -v prettier &> /dev/null; then + prettier --write "$FILE_PATH" 2>/dev/null + fi + ;; + *.py) + if command -v black &> /dev/null; then + black "$FILE_PATH" 2>/dev/null + fi + ;; + *.go) + if command -v gofmt &> /dev/null; then + gofmt -w "$FILE_PATH" 2>/dev/null + fi + ;; + *.rs) + if command -v rustfmt &> /dev/null; then + rustfmt "$FILE_PATH" 2>/dev/null + fi + ;; + *.java) + if command -v google-java-format &> /dev/null; then + google-java-format -i "$FILE_PATH" 2>/dev/null + fi + ;; +esac + +exit 0 diff --git a/uk/06-hooks/log-bash.sh b/uk/06-hooks/log-bash.sh new file mode 100644 index 0000000..2ca16d9 --- /dev/null +++ b/uk/06-hooks/log-bash.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Log all bash commands +# Hook: PostToolUse:Bash +# +# Reads the executed command from stdin JSON and logs it to a file. +# +# Compatible with: macOS, Linux, Windows (Git Bash) + +# Read JSON input from stdin (Claude Code hook protocol) +INPUT=$(cat) + +# Extract the bash command from tool_input +# Note: sed [^"]* stops at escaped quotes in JSON; for commands with double-quoted +# strings, only the portion up to the first \" will be captured — this is a known +# limitation of sed-based JSON parsing and is acceptable for logging purposes. +COMMAND=$(echo "$INPUT" | sed -n 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + +if [ -z "$COMMAND" ]; then + exit 0 +fi + +TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +LOGFILE="$HOME/.claude/bash-commands.log" + +# Create log directory if it doesn't exist +mkdir -p "$(dirname "$LOGFILE")" + +# Log the command +echo "[$TIMESTAMP] $COMMAND" >> "$LOGFILE" + +exit 0 diff --git a/uk/06-hooks/notify-team.sh b/uk/06-hooks/notify-team.sh new file mode 100644 index 0000000..1894218 --- /dev/null +++ b/uk/06-hooks/notify-team.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Send notifications on events +# Hook: PostToolUse (matcher: Bash) — run after bash commands; filter for git push in script logic +# Note: Claude Code has no native PostPush event. To trigger on git push, check the bash command +# string for "git push" using a matcher or conditional logic within this script. + +REPO_NAME=$(basename $(git rev-parse --show-toplevel 2>/dev/null) 2>/dev/null) +COMMIT_MSG=$(git log -1 --pretty=%B 2>/dev/null) +AUTHOR=$(git log -1 --pretty=%an 2>/dev/null) +BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) + +echo "📢 Sending notification to team..." + +# Slack webhook example (replace with your webhook URL) +SLACK_WEBHOOK="${SLACK_WEBHOOK_URL:-}" + +if [ -n "$SLACK_WEBHOOK" ]; then + curl -X POST "$SLACK_WEBHOOK" \ + -H 'Content-Type: application/json' \ + -d "{ + \"text\": \"New push to *$REPO_NAME*\", + \"attachments\": [{ + \"color\": \"good\", + \"fields\": [ + {\"title\": \"Branch\", \"value\": \"$BRANCH\", \"short\": true}, + {\"title\": \"Author\", \"value\": \"$AUTHOR\", \"short\": true}, + {\"title\": \"Commit\", \"value\": \"$COMMIT_MSG\"} + ] + }] + }" \ + --silent --output /dev/null + + echo "✅ Slack notification sent" +fi + +# Discord webhook example (replace with your webhook URL) +DISCORD_WEBHOOK="${DISCORD_WEBHOOK_URL:-}" + +if [ -n "$DISCORD_WEBHOOK" ]; then + curl -X POST "$DISCORD_WEBHOOK" \ + -H 'Content-Type: application/json' \ + -d "{ + \"content\": \"**New push to $REPO_NAME**\", + \"embeds\": [{ + \"title\": \"$COMMIT_MSG\", + \"color\": 3066993, + \"fields\": [ + {\"name\": \"Branch\", \"value\": \"$BRANCH\", \"inline\": true}, + {\"name\": \"Author\", \"value\": \"$AUTHOR\", \"inline\": true} + ] + }] + }" \ + --silent --output /dev/null + + echo "✅ Discord notification sent" +fi + +# Email notification example +EMAIL_TO="${TEAM_EMAIL:-}" + +if [ -n "$EMAIL_TO" ]; then + echo "New push to $REPO_NAME by $AUTHOR" | \ + mail -s "Git Push: $BRANCH" "$EMAIL_TO" + + echo "✅ Email notification sent" +fi + +exit 0 diff --git a/uk/06-hooks/pre-commit.sh b/uk/06-hooks/pre-commit.sh new file mode 100644 index 0000000..2e9b27a --- /dev/null +++ b/uk/06-hooks/pre-commit.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Run tests before commit +# Hook: PreToolUse (matcher: Bash) - checks if the command is a git commit +# Note: There is no "PreCommit" hook event. Use PreToolUse with a Bash matcher +# and inspect the command to detect git commit operations. + +echo "🧪 Running tests before commit..." + +# Check if package.json exists (Node.js project) +if [ -f "package.json" ]; then + if grep -q "\"test\":" package.json; then + npm test + if [ $? -ne 0 ]; then + echo "❌ Tests failed! Commit blocked." + exit 1 + fi + fi +fi + +# Check if pytest is available (Python project) +if [ -f "pytest.ini" ] || [ -f "setup.py" ]; then + if command -v pytest &> /dev/null; then + pytest + if [ $? -ne 0 ]; then + echo "❌ Tests failed! Commit blocked." + exit 1 + fi + fi +fi + +# Check if go.mod exists (Go project) +if [ -f "go.mod" ]; then + go test ./... + if [ $? -ne 0 ]; then + echo "❌ Tests failed! Commit blocked." + exit 1 + fi +fi + +# Check if Cargo.toml exists (Rust project) +if [ -f "Cargo.toml" ]; then + cargo test + if [ $? -ne 0 ]; then + echo "❌ Tests failed! Commit blocked." + exit 1 + fi +fi + +echo "✅ All tests passed! Proceeding with commit." +exit 0 diff --git a/uk/06-hooks/pre-tool-check.sh b/uk/06-hooks/pre-tool-check.sh new file mode 100644 index 0000000..932395e --- /dev/null +++ b/uk/06-hooks/pre-tool-check.sh @@ -0,0 +1,96 @@ +#!/bin/bash +# Pre-tool safety check for Bash commands +# Hook: PreToolUse (matcher: Bash) +# +# This hook runs before every Bash tool execution and blocks or warns on +# potentially destructive or high-risk shell commands. +# +# Setup: +# cp 06-hooks/pre-tool-check.sh ~/.claude/hooks/ +# chmod +x ~/.claude/hooks/pre-tool-check.sh +# +# Configure in ~/.claude/settings.json: +# { +# "hooks": { +# "PreToolUse": [ +# { +# "matcher": "Bash", +# "hooks": [ +# { +# "type": "command", +# "command": "~/.claude/hooks/pre-tool-check.sh" +# } +# ] +# } +# ] +# } +# } +# +# Input: JSON via stdin with the shape: +# { "tool_name": "Bash", "tool_input": { "command": "..." } } +# +# Output: Exit 0 to allow, exit 2 to block, or print JSON to modify behavior. + +# Read the full JSON input from stdin +INPUT=$(cat) + +# Extract the command using portable sed (compatible with macOS and Linux) +COMMAND=$(echo "$INPUT" | sed -n 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + +# Fall back to the raw input if extraction fails +if [ -z "$COMMAND" ]; then + COMMAND="$INPUT" +fi + +# ── Blocked patterns ────────────────────────────────────────────────────────── +# These commands are blocked unconditionally because they are almost always +# destructive and rarely intentional in an automated context. + +BLOCKED_PATTERNS=( + "rm -rf /" + "rm -rf \*" + "dd if=/dev/zero" + "dd if=/dev/random" + ":(){:|:&};:" # Fork bomb + "mkfs\." # Filesystem format + "format c:" # Windows disk format +) + +for pattern in "${BLOCKED_PATTERNS[@]}"; do + if echo "$COMMAND" | grep -qE "$pattern"; then + echo "❌ Blocked: Potentially destructive command detected: $pattern" + echo " Command: $COMMAND" + exit 2 + fi +done + +# ── Warning patterns ────────────────────────────────────────────────────────── +# These patterns are risky but may be intentional. Log a warning and allow. + +WARNING_PATTERNS=( + "rm -rf" + "git push --force" + "git reset --hard" + "git clean -f" + "chmod -R 777" + "sudo rm" + "DROP TABLE" + "DROP DATABASE" + "truncate" +) + +WARNINGS=0 +for pattern in "${WARNING_PATTERNS[@]}"; do + if echo "$COMMAND" | grep -qi "$pattern"; then + echo "⚠️ Warning: High-risk operation detected: $pattern" + WARNINGS=$((WARNINGS + 1)) + fi +done + +if [ "$WARNINGS" -gt 0 ]; then + echo " Command: $COMMAND" + echo " Proceeding — review the above warnings before continuing." +fi + +# ── Allow ───────────────────────────────────────────────────────────────────── +exit 0 diff --git a/uk/06-hooks/security-scan.sh b/uk/06-hooks/security-scan.sh new file mode 100644 index 0000000..81e3a5b --- /dev/null +++ b/uk/06-hooks/security-scan.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# Security scan on file write +# Hook: PostToolUse:Write +# +# Scans files for hardcoded secrets, API keys, and credentials. +# Outputs a non-blocking warning via additionalContext when issues are found. +# +# Compatible with: macOS, Linux, Windows (Git Bash) + +# Read JSON input from stdin (Claude Code hook protocol) +INPUT=$(cat) + +# Extract file_path using sed (compatible with all platforms including Windows Git Bash) +# Avoids grep -P (not available on Windows Git Bash) and python3 dependency +FILE_PATH=$(echo "$INPUT" | sed -n 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + +if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then + exit 0 +fi + +# Skip binary files, vendor dirs, and build artifacts +case "$FILE_PATH" in + *.png|*.jpg|*.jpeg|*.gif|*.svg|*.ico|*.woff|*.woff2|*.ttf|*.eot) exit 0 ;; + */node_modules/*|*/.git/*|*/dist/*|*/build/*) exit 0 ;; +esac + +ISSUES="" + +# Check for hardcoded passwords +# Handles both JSON format ("password": "value") and code format (password = 'value') +# Use \\n as separator — it is a valid JSON newline escape and passes through printf safely +if grep -qiE '"password"[[:space:]]*:[[:space:]]*"[^"]+"' "$FILE_PATH" 2>/dev/null; then + ISSUES="${ISSUES}- WARNING: Potential hardcoded password detected\\n" +elif grep -qiE '(password|passwd|pwd)[[:space:]]*=[[:space:]]*'"'"'[^'"'"']+'"'"'' "$FILE_PATH" 2>/dev/null; then + ISSUES="${ISSUES}- WARNING: Potential hardcoded password detected\\n" +fi + +# Check for hardcoded API keys +if grep -qiE '"(api[_-]?key|apikey|access[_-]?token)"[[:space:]]*:[[:space:]]*"[^"]+"' "$FILE_PATH" 2>/dev/null; then + ISSUES="${ISSUES}- WARNING: Potential hardcoded API key detected\\n" +fi + +# Check for hardcoded secrets and tokens +if grep -qiE '(secret|token)[[:space:]]*=[[:space:]]*['"'"'"][^'"'"'"]+['"'"'"]' "$FILE_PATH" 2>/dev/null; then + ISSUES="${ISSUES}- WARNING: Potential hardcoded secret or token detected\\n" +fi + +# Check for private keys +if grep -q "BEGIN.*PRIVATE KEY" "$FILE_PATH" 2>/dev/null; then + ISSUES="${ISSUES}- WARNING: Private key detected\\n" +fi + +# Check for AWS keys +if grep -qE "AKIA[0-9A-Z]{16}" "$FILE_PATH" 2>/dev/null; then + ISSUES="${ISSUES}- WARNING: AWS access key detected\\n" +fi + +# Scan with semgrep if available (stdout suppressed to avoid mixing with JSON output) +if command -v semgrep &> /dev/null; then + semgrep --config=auto "$FILE_PATH" --quiet >/dev/null 2>/dev/null +fi + +# Scan with trufflehog if available (stdout suppressed to avoid mixing with JSON output) +if command -v trufflehog &> /dev/null; then + trufflehog filesystem "$FILE_PATH" --only-verified --quiet >/dev/null 2>/dev/null +fi + +# If issues found, output as additionalContext (non-blocking warning) +# Use hookSpecificOutput format required by Claude Code PostToolUse protocol +if [ -n "$ISSUES" ]; then + # Escape file path for JSON (backslash and double-quote) + # ISSUES already uses \\n as separator (valid JSON escape) — only escape double-quotes + SAFE_PATH=$(printf '%s' "$FILE_PATH" | sed 's/\\/\\\\/g; s/"/\\"/g') + SAFE_ISSUES=$(printf '%s' "$ISSUES" | sed 's/"/\\"/g') + printf '{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "Security scan found issues in %s:\\n%sPlease review and use environment variables instead."}}' "$SAFE_PATH" "$SAFE_ISSUES" +fi + +exit 0 diff --git a/uk/06-hooks/validate-prompt.sh b/uk/06-hooks/validate-prompt.sh new file mode 100644 index 0000000..b2701f3 --- /dev/null +++ b/uk/06-hooks/validate-prompt.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Validate user prompts +# Hook: UserPromptSubmit +# +# Reads the user prompt from stdin JSON and blocks dangerous operations. +# +# Compatible with: macOS, Linux, Windows (Git Bash) + +# Read JSON input from stdin (Claude Code hook protocol) +INPUT=$(cat) + +# Extract the prompt text from JSON input +# Claude Code sends UserPromptSubmit with field "user_prompt" (falls back to "prompt") +PROMPT=$(echo "$INPUT" | sed -n 's/.*"user_prompt"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) +if [ -z "$PROMPT" ]; then + PROMPT=$(echo "$INPUT" | sed -n 's/.*"prompt"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) +fi + +if [ -z "$PROMPT" ]; then + exit 0 +fi + +# Check for dangerous operations +DANGEROUS_PATTERNS=( + "rm -rf /" + "delete database" + "drop database" + "format disk" + "dd if=" +) + +for pattern in "${DANGEROUS_PATTERNS[@]}"; do + if echo "$PROMPT" | grep -qi "$pattern"; then + printf '{"decision": "block", "reason": "Dangerous operation detected: %s"}' "$pattern" + exit 0 + fi +done + +# Check for production deployments +if echo "$PROMPT" | grep -qiE "(deploy|push).*production"; then + if [ ! -f ".deployment-approved" ]; then + echo '{"decision": "block", "reason": "Production deployment requires approval. Create .deployment-approved file to proceed."}' + exit 0 + fi +fi + +# Check for required context in certain operations +if echo "$PROMPT" | grep -qi "refactor"; then + if [ ! -d "tests" ] && [ ! -d "test" ]; then + printf '{"additionalContext": "Warning: Refactoring without tests may be risky. Consider writing tests first."}' + fi +fi + +exit 0 diff --git a/uk/07-plugins/devops-automation/.claude-plugin/plugin.json b/uk/07-plugins/devops-automation/.claude-plugin/plugin.json new file mode 100644 index 0000000..3b7bf75 --- /dev/null +++ b/uk/07-plugins/devops-automation/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "devops-automation", + "version": "1.0.0", + "description": "Complete DevOps automation for deployment, monitoring, and incident response", + "author": { + "name": "Community" + }, + "license": "MIT" +} diff --git a/uk/07-plugins/devops-automation/README.md b/uk/07-plugins/devops-automation/README.md new file mode 100644 index 0000000..d0226bd --- /dev/null +++ b/uk/07-plugins/devops-automation/README.md @@ -0,0 +1,107 @@ + + + Claude How To + + +# DevOps Automation Plugin + +Complete DevOps automation for deployment, monitoring, and incident response. + +## Features + +✅ Automated deployments +✅ Rollback procedures +✅ System health monitoring +✅ Incident response workflows +✅ Kubernetes integration + +## Installation + +```bash +/plugin install devops-automation +``` + +## What's Included + +### Slash Commands +- `/deploy` - Deploy to production or staging +- `/rollback` - Rollback to previous version +- `/status` - Check system health +- `/incident` - Handle production incidents + +### Subagents +- `deployment-specialist` - Deployment operations +- `incident-commander` - Incident coordination +- `alert-analyzer` - System health analysis + +### MCP Servers +- Kubernetes integration + +### Scripts +- `deploy.sh` - Deployment automation +- `rollback.sh` - Rollback automation +- `health-check.sh` - Health check utilities + +### Hooks +- `pre-deploy.js` - Pre-deployment validation +- `post-deploy.js` - Post-deployment tasks + +## Usage + +### Deploy to Staging +``` +/deploy staging +``` + +### Deploy to Production +``` +/deploy production +``` + +### Rollback +``` +/rollback production +``` + +### Check Status +``` +/status +``` + +### Handle Incident +``` +/incident +``` + +## Requirements + +- Claude Code 1.0+ +- Kubernetes CLI (kubectl) +- Cluster access configured + +## Configuration + +Set up your Kubernetes config: +```bash +export KUBECONFIG=~/.kube/config +``` + +## Example Workflow + +``` +User: /deploy production + +Claude: +1. Runs pre-deploy hook (validates kubectl, cluster connection) +2. Delegates to deployment-specialist subagent +3. Runs deploy.sh script +4. Monitors deployment progress via Kubernetes MCP +5. Runs post-deploy hook (waits for pods, smoke tests) +6. Provides deployment summary + +Result: +✅ Deployment complete +📦 Version: v2.1.0 +🚀 Pods: 3/3 ready +⏱️ Time: 2m 34s +``` diff --git a/uk/07-plugins/devops-automation/agents/alert-analyzer.md b/uk/07-plugins/devops-automation/agents/alert-analyzer.md new file mode 100644 index 0000000..d455e24 --- /dev/null +++ b/uk/07-plugins/devops-automation/agents/alert-analyzer.md @@ -0,0 +1,14 @@ +--- +name: alert-analyzer +description: Analyzes monitoring alerts and system metrics +tools: read, grep, bash +--- + +# Alert Analyzer + +Analyzes system health and alerts: +- Alert correlation +- Trend analysis +- Root cause identification +- Metric visualization +- Proactive issue detection diff --git a/uk/07-plugins/devops-automation/agents/deployment-specialist.md b/uk/07-plugins/devops-automation/agents/deployment-specialist.md new file mode 100644 index 0000000..47fe3f7 --- /dev/null +++ b/uk/07-plugins/devops-automation/agents/deployment-specialist.md @@ -0,0 +1,14 @@ +--- +name: deployment-specialist +description: Handles all deployment operations +tools: read, write, bash, grep +--- + +# Deployment Specialist + +Expert in deployment operations: +- Blue-green deployments +- Canary releases +- Rollback procedures +- Health checks +- Database migrations diff --git a/uk/07-plugins/devops-automation/agents/incident-commander.md b/uk/07-plugins/devops-automation/agents/incident-commander.md new file mode 100644 index 0000000..d73929c --- /dev/null +++ b/uk/07-plugins/devops-automation/agents/incident-commander.md @@ -0,0 +1,14 @@ +--- +name: incident-commander +description: Coordinates incident response +tools: read, write, bash, grep +--- + +# Incident Commander + +Manages incident response: +- Severity assessment +- Team coordination +- Status updates +- Resolution tracking +- Post-mortem facilitation diff --git a/uk/07-plugins/devops-automation/commands/deploy.md b/uk/07-plugins/devops-automation/commands/deploy.md new file mode 100644 index 0000000..4873ca3 --- /dev/null +++ b/uk/07-plugins/devops-automation/commands/deploy.md @@ -0,0 +1,15 @@ +--- +name: Deploy +description: Deploy application to production or staging +--- + +# Deploy Application + +Execute deployment workflow: + +1. Run pre-deployment checks +2. Build application +3. Run tests +4. Deploy to target environment +5. Run health checks +6. Notify team on Slack diff --git a/uk/07-plugins/devops-automation/commands/incident.md b/uk/07-plugins/devops-automation/commands/incident.md new file mode 100644 index 0000000..b5c0459 --- /dev/null +++ b/uk/07-plugins/devops-automation/commands/incident.md @@ -0,0 +1,16 @@ +--- +name: Incident Response +description: Handle production incidents with structured response +--- + +# Incident Response + +Structured incident response workflow: + +1. Create incident record +2. Assess severity and impact +3. Notify on-call team +4. Gather diagnostic information +5. Coordinate response efforts +6. Document resolution +7. Schedule post-mortem diff --git a/uk/07-plugins/devops-automation/commands/rollback.md b/uk/07-plugins/devops-automation/commands/rollback.md new file mode 100644 index 0000000..2eb20a3 --- /dev/null +++ b/uk/07-plugins/devops-automation/commands/rollback.md @@ -0,0 +1,14 @@ +--- +name: Rollback +description: Rollback to previous deployment +--- + +# Rollback Deployment + +Rollback to previous stable version: + +1. Identify previous deployment +2. Verify rollback target is healthy +3. Execute rollback procedure +4. Run health checks +5. Notify team diff --git a/uk/07-plugins/devops-automation/commands/status.md b/uk/07-plugins/devops-automation/commands/status.md new file mode 100644 index 0000000..6c2e670 --- /dev/null +++ b/uk/07-plugins/devops-automation/commands/status.md @@ -0,0 +1,15 @@ +--- +name: System Status +description: Check overall system health and status +--- + +# System Status Check + +Check system health across all services: + +1. Query Kubernetes pod status +2. Check database connections +3. Monitor API response times +4. Review error rates +5. Check resource utilization +6. Report overall health diff --git a/uk/07-plugins/devops-automation/hooks/post-deploy.js b/uk/07-plugins/devops-automation/hooks/post-deploy.js new file mode 100644 index 0000000..5c6537e --- /dev/null +++ b/uk/07-plugins/devops-automation/hooks/post-deploy.js @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +/** + * Post-deployment hook + * Runs after deployment completes + */ + +async function postDeploy() { + console.log('Running post-deployment tasks...'); + + const { execSync } = require('child_process'); + + // Wait for pods to be ready + console.log('Waiting for pods to be ready...'); + try { + execSync('kubectl wait --for=condition=ready pod -l app=myapp --timeout=300s', { + stdio: 'inherit' + }); + } catch (error) { + console.error('❌ Pods failed to become ready'); + process.exit(1); + } + + // Run smoke tests + console.log('Running smoke tests...'); + // Add your smoke test commands here + + console.log('✅ Post-deployment tasks complete'); +} + +postDeploy().catch(error => { + console.error('Post-deploy hook failed:', error); + process.exit(1); +}); diff --git a/uk/07-plugins/devops-automation/hooks/pre-deploy.js b/uk/07-plugins/devops-automation/hooks/pre-deploy.js new file mode 100644 index 0000000..98983a4 --- /dev/null +++ b/uk/07-plugins/devops-automation/hooks/pre-deploy.js @@ -0,0 +1,35 @@ +#!/usr/bin/env node + +/** + * Pre-deployment hook + * Validates environment and prerequisites before deployment + */ + +async function preDeploy() { + console.log('Running pre-deployment checks...'); + + const { execSync } = require('child_process'); + + // Check if kubectl is installed + try { + execSync('which kubectl', { stdio: 'pipe' }); + } catch (error) { + console.error('❌ kubectl not found. Please install Kubernetes CLI.'); + process.exit(1); + } + + // Check if connected to cluster + try { + execSync('kubectl cluster-info', { stdio: 'pipe' }); + } catch (error) { + console.error('❌ Not connected to Kubernetes cluster'); + process.exit(1); + } + + console.log('✅ Pre-deployment checks passed'); +} + +preDeploy().catch(error => { + console.error('Pre-deploy hook failed:', error); + process.exit(1); +}); diff --git a/uk/07-plugins/devops-automation/mcp/kubernetes-config.json b/uk/07-plugins/devops-automation/mcp/kubernetes-config.json new file mode 100644 index 0000000..3f3b842 --- /dev/null +++ b/uk/07-plugins/devops-automation/mcp/kubernetes-config.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "kubernetes": { + "command": "npx", + "args": ["@modelcontextprotocol/server-kubernetes"], + "env": { + "KUBECONFIG": "${KUBECONFIG}" + } + } + } +} diff --git a/uk/07-plugins/devops-automation/scripts/deploy.sh b/uk/07-plugins/devops-automation/scripts/deploy.sh new file mode 100644 index 0000000..9acf5bd --- /dev/null +++ b/uk/07-plugins/devops-automation/scripts/deploy.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +echo "🚀 Starting deployment..." + +# Load environment +ENV=${1:-staging} +echo "📦 Target environment: $ENV" + +# Pre-deployment checks +echo "✓ Running pre-deployment checks..." +npm run lint +npm test + +# Build +echo "🔨 Building application..." +npm run build + +# Deploy +echo "🚢 Deploying to $ENV..." +kubectl apply -f k8s/$ENV/ + +# Health check +echo "🏥 Running health checks..." +sleep 10 +curl -f http://api.$ENV.example.com/health + +echo "✅ Deployment complete!" diff --git a/uk/07-plugins/devops-automation/scripts/health-check.sh b/uk/07-plugins/devops-automation/scripts/health-check.sh new file mode 100644 index 0000000..2ded117 --- /dev/null +++ b/uk/07-plugins/devops-automation/scripts/health-check.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +echo "🏥 System Health Check" +echo "====================" + +ENV=${1:-production} + +# Check API +echo -n "API: " +if curl -sf http://api.$ENV.example.com/health > /dev/null; then + echo "✅ Healthy" +else + echo "❌ Unhealthy" +fi + +# Check Database +echo -n "Database: " +if pg_isready -h db.$ENV.example.com > /dev/null 2>&1; then + echo "✅ Healthy" +else + echo "❌ Unhealthy" +fi + +# Check Pods +echo -n "Kubernetes Pods: " +PODS_READY=$(kubectl get pods -n $ENV --no-headers | grep "Running" | wc -l) +PODS_TOTAL=$(kubectl get pods -n $ENV --no-headers | wc -l) +echo "$PODS_READY/$PODS_TOTAL ready" + +echo "====================" diff --git a/uk/07-plugins/devops-automation/scripts/rollback.sh b/uk/07-plugins/devops-automation/scripts/rollback.sh new file mode 100644 index 0000000..0fe0cc2 --- /dev/null +++ b/uk/07-plugins/devops-automation/scripts/rollback.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e + +echo "⏪ Starting rollback..." + +ENV=${1:-staging} +echo "📦 Target environment: $ENV" + +# Get previous deployment +PREVIOUS=$(kubectl rollout history deployment/app -n $ENV | tail -2 | head -1 | awk '{print $1}') +echo "🔄 Rolling back to revision: $PREVIOUS" + +# Execute rollback +kubectl rollout undo deployment/app -n $ENV + +# Wait for rollback +echo "⏳ Waiting for rollback to complete..." +kubectl rollout status deployment/app -n $ENV + +# Health check +echo "🏥 Running health checks..." +sleep 5 +curl -f http://api.$ENV.example.com/health + +echo "✅ Rollback complete!" diff --git a/uk/07-plugins/documentation/.claude-plugin/plugin.json b/uk/07-plugins/documentation/.claude-plugin/plugin.json new file mode 100644 index 0000000..b1d520d --- /dev/null +++ b/uk/07-plugins/documentation/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "documentation", + "version": "1.0.0", + "description": "Comprehensive documentation generation and maintenance", + "author": { + "name": "Community" + }, + "license": "MIT" +} diff --git a/uk/07-plugins/documentation/README.md b/uk/07-plugins/documentation/README.md new file mode 100644 index 0000000..0934147 --- /dev/null +++ b/uk/07-plugins/documentation/README.md @@ -0,0 +1,119 @@ + + + Claude How To + + +# Documentation Plugin + +Comprehensive documentation generation and maintenance for your project. + +## Features + +✅ API documentation generation +✅ README creation and updates +✅ Documentation synchronization +✅ Code comment improvements +✅ Example generation + +## Installation + +```bash +/plugin install documentation +``` + +## What's Included + +### Slash Commands +- `/generate-api-docs` - Generate API documentation +- `/generate-readme` - Create or update README +- `/sync-docs` - Sync docs with code changes +- `/validate-docs` - Validate documentation + +### Subagents +- `api-documenter` - API documentation specialist +- `code-commentator` - Code comment improvements +- `example-generator` - Code example creation + +### Templates +- `api-endpoint.md` - API endpoint documentation template +- `function-docs.md` - Function documentation template +- `adr-template.md` - Architecture Decision Record template + +### MCP Servers +- GitHub integration for documentation syncing + +## Usage + +### Generate API Documentation +``` +/generate-api-docs +``` + +### Create README +``` +/generate-readme +``` + +### Sync Documentation +``` +/sync-docs +``` + +### Validate Documentation +``` +/validate-docs +``` + +## Requirements + +- Claude Code 1.0+ +- GitHub access (optional) + +## Example Workflow + +``` +User: /generate-api-docs + +Claude: +1. Scans all API endpoints in /src/api/ +2. Delegates to api-documenter subagent +3. Extracts function signatures and JSDoc +4. Organizes by module/endpoint +5. Uses api-endpoint.md template +6. Generates comprehensive markdown docs +7. Includes curl, JavaScript, and Python examples + +Result: +✅ API documentation generated +📄 Files created: + - docs/api/users.md + - docs/api/auth.md + - docs/api/products.md +📊 Coverage: 23/23 endpoints documented +``` + +## Templates Usage + +### API Endpoint Template +Use for documenting REST API endpoints with full examples. + +### Function Documentation Template +Use for documenting individual functions/methods. + +### ADR Template +Use for documenting architectural decisions. + +## Configuration + +Set up GitHub token for documentation syncing: +```bash +export GITHUB_TOKEN="your_github_token" +``` + +## Best Practices + +- Keep documentation close to code +- Update docs with code changes +- Include practical examples +- Validate regularly +- Use templates for consistency diff --git a/uk/07-plugins/documentation/agents/api-documenter.md b/uk/07-plugins/documentation/agents/api-documenter.md new file mode 100644 index 0000000..487c5a4 --- /dev/null +++ b/uk/07-plugins/documentation/agents/api-documenter.md @@ -0,0 +1,14 @@ +--- +name: api-documenter +description: API documentation specialist +tools: read, write, grep +--- + +# API Documenter + +Creates comprehensive API documentation: +- Endpoint documentation +- Parameter descriptions +- Response schemas +- Code examples (curl, JS, Python) +- Error codes diff --git a/uk/07-plugins/documentation/agents/code-commentator.md b/uk/07-plugins/documentation/agents/code-commentator.md new file mode 100644 index 0000000..a039443 --- /dev/null +++ b/uk/07-plugins/documentation/agents/code-commentator.md @@ -0,0 +1,14 @@ +--- +name: code-commentator +description: Code comment and inline documentation specialist +tools: read, write, edit +--- + +# Code Commentator + +Improves code documentation: +- JSDoc/docstring comments +- Inline explanations +- Parameter descriptions +- Return type documentation +- Usage examples diff --git a/uk/07-plugins/documentation/agents/example-generator.md b/uk/07-plugins/documentation/agents/example-generator.md new file mode 100644 index 0000000..1cd1c7e --- /dev/null +++ b/uk/07-plugins/documentation/agents/example-generator.md @@ -0,0 +1,14 @@ +--- +name: example-generator +description: Code example and tutorial specialist +tools: read, write +--- + +# Example Generator + +Creates practical code examples: +- Getting started guides +- Common use cases +- Integration examples +- Best practices +- Troubleshooting scenarios diff --git a/uk/07-plugins/documentation/commands/generate-api-docs.md b/uk/07-plugins/documentation/commands/generate-api-docs.md new file mode 100644 index 0000000..a5bda46 --- /dev/null +++ b/uk/07-plugins/documentation/commands/generate-api-docs.md @@ -0,0 +1,15 @@ +--- +name: Generate API Documentation +description: Generate comprehensive API documentation from source code +--- + +# API Documentation Generator + +Generate complete API documentation: + +1. Scan API endpoints +2. Extract function signatures and JSDoc +3. Organize by module/endpoint +4. Create markdown with examples +5. Include request/response schemas +6. Add error documentation diff --git a/uk/07-plugins/documentation/commands/generate-readme.md b/uk/07-plugins/documentation/commands/generate-readme.md new file mode 100644 index 0000000..119310a --- /dev/null +++ b/uk/07-plugins/documentation/commands/generate-readme.md @@ -0,0 +1,15 @@ +--- +name: Generate README +description: Create or update project README +--- + +# README Generator + +Generate comprehensive README: + +1. Project overview and description +2. Installation instructions +3. Usage examples +4. API documentation links +5. Contributing guidelines +6. License information diff --git a/uk/07-plugins/documentation/commands/sync-docs.md b/uk/07-plugins/documentation/commands/sync-docs.md new file mode 100644 index 0000000..9681cc8 --- /dev/null +++ b/uk/07-plugins/documentation/commands/sync-docs.md @@ -0,0 +1,14 @@ +--- +name: Sync Documentation +description: Sync documentation with code changes +--- + +# Documentation Sync + +Synchronize documentation with codebase: + +1. Detect code changes +2. Identify outdated documentation +3. Update affected docs +4. Verify examples still work +5. Update version numbers diff --git a/uk/07-plugins/documentation/commands/validate-docs.md b/uk/07-plugins/documentation/commands/validate-docs.md new file mode 100644 index 0000000..bd41f57 --- /dev/null +++ b/uk/07-plugins/documentation/commands/validate-docs.md @@ -0,0 +1,14 @@ +--- +name: Validate Documentation +description: Validate documentation for completeness and accuracy +--- + +# Documentation Validation + +Validate documentation quality: + +1. Check for broken links +2. Verify code examples +3. Ensure completeness +4. Check formatting +5. Validate against actual code diff --git a/uk/07-plugins/documentation/mcp/github-docs-config.json b/uk/07-plugins/documentation/mcp/github-docs-config.json new file mode 100644 index 0000000..0f62131 --- /dev/null +++ b/uk/07-plugins/documentation/mcp/github-docs-config.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "github": { + "command": "npx", + "args": ["@modelcontextprotocol/server-github"], + "env": { + "GITHUB_TOKEN": "${GITHUB_TOKEN}" + } + } + } +} diff --git a/uk/07-plugins/documentation/templates/adr-template.md b/uk/07-plugins/documentation/templates/adr-template.md new file mode 100644 index 0000000..8cc3fc3 --- /dev/null +++ b/uk/07-plugins/documentation/templates/adr-template.md @@ -0,0 +1,39 @@ +# ADR [Number]: [Title] + +## Status +[Proposed | Accepted | Deprecated | Superseded] + +## Context +What is the issue that we're seeing that is motivating this decision or change? + +## Decision +What is the change that we're proposing and/or doing? + +## Consequences +What becomes easier or more difficult to do because of this change? + +### Positive +- Benefit 1 +- Benefit 2 + +### Negative +- Drawback 1 +- Drawback 2 + +### Neutral +- Consideration 1 +- Consideration 2 + +## Alternatives Considered +What other options were considered and why were they not chosen? + +### Alternative 1 +Description and reason for not choosing. + +### Alternative 2 +Description and reason for not choosing. + +## References +- Related ADRs +- External documentation +- Discussion links diff --git a/uk/07-plugins/documentation/templates/api-endpoint.md b/uk/07-plugins/documentation/templates/api-endpoint.md new file mode 100644 index 0000000..33c3996 --- /dev/null +++ b/uk/07-plugins/documentation/templates/api-endpoint.md @@ -0,0 +1,101 @@ +# [METHOD] /api/v1/[endpoint] + +## Description +Brief explanation of what this endpoint does. + +## Authentication +Required authentication method (e.g., Bearer token). + +## Parameters + +### Path Parameters +| Name | Type | Required | Description | +|------|------|----------|-------------| +| id | string | Yes | Resource ID | + +### Query Parameters +| Name | Type | Required | Description | +|------|------|----------|-------------| +| page | integer | No | Page number (default: 1) | +| limit | integer | No | Items per page (default: 20) | + +### Request Body +```json +{ + "field": "value" +} +``` + +## Responses + +### 200 OK +```json +{ + "success": true, + "data": { + "id": "123", + "name": "Example" + } +} +``` + +### 400 Bad Request +```json +{ + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Invalid input" + } +} +``` + +### 404 Not Found +```json +{ + "success": false, + "error": { + "code": "NOT_FOUND", + "message": "Resource not found" + } +} +``` + +## Examples + +### cURL +```bash +curl -X GET "https://api.example.com/api/v1/endpoint" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" +``` + +### JavaScript +```javascript +const response = await fetch('/api/v1/endpoint', { + headers: { + 'Authorization': 'Bearer token', + 'Content-Type': 'application/json' + } +}); +const data = await response.json(); +``` + +### Python +```python +import requests + +response = requests.get( + 'https://api.example.com/api/v1/endpoint', + headers={'Authorization': 'Bearer token'} +) +data = response.json() +``` + +## Rate Limits +- 1000 requests per hour for authenticated users +- 100 requests per hour for public endpoints + +## Related Endpoints +- [GET /api/v1/related](#) +- [POST /api/v1/related](#) diff --git a/uk/07-plugins/documentation/templates/function-docs.md b/uk/07-plugins/documentation/templates/function-docs.md new file mode 100644 index 0000000..b1e137d --- /dev/null +++ b/uk/07-plugins/documentation/templates/function-docs.md @@ -0,0 +1,50 @@ +# Function: `functionName` + +## Description +Brief description of what the function does. + +## Signature +```typescript +function functionName(param1: Type1, param2: Type2): ReturnType +``` + +## Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| param1 | Type1 | Yes | Description of param1 | +| param2 | Type2 | No | Description of param2 | + +## Returns +**Type**: `ReturnType` + +Description of what is returned. + +## Throws +- `Error`: When invalid input is provided +- `TypeError`: When wrong type is passed + +## Examples + +### Basic Usage +```typescript +const result = functionName('value1', 'value2'); +console.log(result); +``` + +### Advanced Usage +```typescript +const result = functionName( + complexParam1, + { option: true } +); +``` + +## Notes +- Additional notes or warnings +- Performance considerations +- Best practices + +## See Also +- [Related Function](#) +- [API Documentation](#) diff --git a/uk/07-plugins/pr-review/.claude-plugin/plugin.json b/uk/07-plugins/pr-review/.claude-plugin/plugin.json new file mode 100644 index 0000000..9e953c4 --- /dev/null +++ b/uk/07-plugins/pr-review/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "pr-review", + "version": "1.0.0", + "description": "Complete PR review workflow with security, testing, and docs", + "author": { + "name": "Anthropic" + }, + "license": "MIT" +} diff --git a/uk/07-plugins/pr-review/README.md b/uk/07-plugins/pr-review/README.md new file mode 100644 index 0000000..9fe1c3b --- /dev/null +++ b/uk/07-plugins/pr-review/README.md @@ -0,0 +1,91 @@ + + + Claude How To + + +# PR Review Plugin + +Complete PR review workflow with security, testing, and documentation checks. + +## Features + +✅ Security analysis +✅ Test coverage checking +✅ Documentation verification +✅ Code quality assessment +✅ Performance impact analysis + +## Installation + +```bash +/plugin install pr-review +``` + +## What's Included + +### Slash Commands +- `/review-pr` - Comprehensive PR review +- `/check-security` - Security-focused review +- `/check-tests` - Test coverage analysis + +### Subagents +- `security-reviewer` - Security vulnerability detection +- `test-checker` - Test coverage analysis +- `performance-analyzer` - Performance impact evaluation + +### MCP Servers +- GitHub integration for PR data + +### Hooks +- `pre-review.js` - Pre-review validation + +## Usage + +### Basic PR Review +``` +/review-pr +``` + +### Security Check Only +``` +/check-security +``` + +### Test Coverage Check +``` +/check-tests +``` + +## Requirements + +- Claude Code 1.0+ +- GitHub access +- Git repository + +## Configuration + +Set up your GitHub token: +```bash +export GITHUB_TOKEN="your_github_token" +``` + +## Example Workflow + +``` +User: /review-pr + +Claude: +1. Runs pre-review hook (validates git repo) +2. Fetches PR data via GitHub MCP +3. Delegates security review to security-reviewer subagent +4. Delegates testing to test-checker subagent +5. Delegates performance to performance-analyzer subagent +6. Synthesizes all findings +7. Provides comprehensive review report + +Result: +✅ Security: No critical issues found +⚠️ Testing: Coverage is 65%, recommend 80%+ +✅ Performance: No significant impact +📝 Recommendations: Add tests for edge cases +``` diff --git a/uk/07-plugins/pr-review/agents/performance-analyzer.md b/uk/07-plugins/pr-review/agents/performance-analyzer.md new file mode 100644 index 0000000..2f70938 --- /dev/null +++ b/uk/07-plugins/pr-review/agents/performance-analyzer.md @@ -0,0 +1,13 @@ +--- +name: performance-analyzer +description: Performance impact analysis +tools: read, grep, bash +--- + +# Performance Analyzer + +Evaluates performance impact of changes: +- Algorithm complexity +- Database query efficiency +- Memory usage +- Caching opportunities diff --git a/uk/07-plugins/pr-review/agents/security-reviewer.md b/uk/07-plugins/pr-review/agents/security-reviewer.md new file mode 100644 index 0000000..b5b2a16 --- /dev/null +++ b/uk/07-plugins/pr-review/agents/security-reviewer.md @@ -0,0 +1,13 @@ +--- +name: security-reviewer +description: Security-focused code review +tools: read, grep, diff +--- + +# Security Reviewer + +Specializes in finding security vulnerabilities: +- Authentication/authorization issues +- Data exposure +- Injection attacks +- Secure configuration diff --git a/uk/07-plugins/pr-review/agents/test-checker.md b/uk/07-plugins/pr-review/agents/test-checker.md new file mode 100644 index 0000000..90a065d --- /dev/null +++ b/uk/07-plugins/pr-review/agents/test-checker.md @@ -0,0 +1,13 @@ +--- +name: test-checker +description: Test coverage and quality analysis +tools: read, bash, grep +--- + +# Test Checker + +Analyzes test coverage and quality: +- Coverage percentage +- Missing test cases +- Test quality assessment +- Edge case identification diff --git a/uk/07-plugins/pr-review/commands/check-security.md b/uk/07-plugins/pr-review/commands/check-security.md new file mode 100644 index 0000000..58d5a68 --- /dev/null +++ b/uk/07-plugins/pr-review/commands/check-security.md @@ -0,0 +1,14 @@ +--- +name: Security Check +description: Run security-focused code review +--- + +# Security Check + +Perform focused security analysis on code changes: + +1. Authentication/authorization checks +2. Data exposure risks +3. Injection vulnerabilities +4. Cryptographic weaknesses +5. Sensitive data in logs diff --git a/uk/07-plugins/pr-review/commands/check-tests.md b/uk/07-plugins/pr-review/commands/check-tests.md new file mode 100644 index 0000000..6674562 --- /dev/null +++ b/uk/07-plugins/pr-review/commands/check-tests.md @@ -0,0 +1,14 @@ +--- +name: Test Coverage Check +description: Verify test coverage and quality +--- + +# Test Coverage Check + +Analyze test coverage and quality: + +1. Check test coverage percentage +2. Identify untested code paths +3. Review test quality +4. Suggest missing test cases +5. Verify edge cases are covered diff --git a/uk/07-plugins/pr-review/commands/review-pr.md b/uk/07-plugins/pr-review/commands/review-pr.md new file mode 100644 index 0000000..0078be3 --- /dev/null +++ b/uk/07-plugins/pr-review/commands/review-pr.md @@ -0,0 +1,14 @@ +--- +name: Review PR +description: Start comprehensive PR review with security and testing checks +--- + +# PR Review + +This command initiates a complete pull request review including: + +1. Security analysis +2. Test coverage verification +3. Documentation updates +4. Code quality checks +5. Performance impact assessment diff --git a/uk/07-plugins/pr-review/hooks/pre-review.js b/uk/07-plugins/pr-review/hooks/pre-review.js new file mode 100644 index 0000000..2127709 --- /dev/null +++ b/uk/07-plugins/pr-review/hooks/pre-review.js @@ -0,0 +1,37 @@ +#!/usr/bin/env node + +/** + * Pre-review hook + * Runs before starting PR review to ensure prerequisites are met + */ + +async function preReview() { + console.log('Running pre-review checks...'); + + // Check if git repository + const { execSync } = require('child_process'); + try { + execSync('git rev-parse --git-dir', { stdio: 'pipe' }); + } catch (error) { + console.error('❌ Not a git repository'); + process.exit(1); + } + + // Check for uncommitted changes + try { + const status = execSync('git status --porcelain', { encoding: 'utf-8' }); + if (status.trim()) { + console.warn('⚠️ Warning: Uncommitted changes detected'); + } + } catch (error) { + console.error('❌ Failed to check git status'); + process.exit(1); + } + + console.log('✅ Pre-review checks passed'); +} + +preReview().catch(error => { + console.error('Pre-review hook failed:', error); + process.exit(1); +}); diff --git a/uk/07-plugins/pr-review/mcp/github-config.json b/uk/07-plugins/pr-review/mcp/github-config.json new file mode 100644 index 0000000..0f62131 --- /dev/null +++ b/uk/07-plugins/pr-review/mcp/github-config.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "github": { + "command": "npx", + "args": ["@modelcontextprotocol/server-github"], + "env": { + "GITHUB_TOKEN": "${GITHUB_TOKEN}" + } + } + } +} diff --git a/uk/09-advanced-features/config-examples.json b/uk/09-advanced-features/config-examples.json new file mode 100644 index 0000000..a67f08c --- /dev/null +++ b/uk/09-advanced-features/config-examples.json @@ -0,0 +1,267 @@ +{ + "description": "Example Claude Code configurations for different use cases", + + "examples": { + "development": { + "name": "Development Environment", + "description": "Configuration for active development work", + "config": { + "general": { + "model": "claude-sonnet-4-6", + "temperature": 0.7 + }, + "planning": { + "autoEnter": true, + "complexityThreshold": 3, + "requireApproval": true + }, + "permissions": { + "mode": "unrestricted" + }, + "backgroundTasks": { + "enabled": true, + "maxConcurrentTasks": 3 + }, + "hooks": { + "PreToolUse:Write": "prettier --write ${file_path}", + "PostToolUse:Write": "eslint ${file_path}" + } + } + }, + + "code_review": { + "name": "Code Review Mode", + "description": "Configuration for reviewing code without modifications", + "config": { + "general": { + "model": "claude-sonnet-4-6", + "temperature": 0.3 + }, + "permissions": { + "mode": "plan" + }, + "extendedThinking": { + "enabled": true, + "showThinkingProcess": true + }, + "planning": { + "autoEnter": false + } + } + }, + + "learning": { + "name": "Learning Mode", + "description": "Configuration for learning and experimentation", + "config": { + "general": { + "model": "claude-sonnet-4-6", + "temperature": 0.5 + }, + "permissions": { + "mode": "confirm" + }, + "extendedThinking": { + "enabled": true, + "showThinkingProcess": true + }, + "planning": { + "autoEnter": true, + "requireApproval": true, + "showTimeEstimates": true + }, + "checkpoints": { + "autoCheckpoint": true + } + } + }, + + "production": { + "name": "Production Deployment", + "description": "Configuration for production operations with safety checks", + "config": { + "general": { + "model": "claude-opus-4", + "temperature": 0.1 + }, + "permissions": { + "mode": "confirm", + "requireConfirmationFor": ["Bash", "Git", "Write", "Edit"] + }, + "hooks": { + "PreToolUse": "~/.claude/hooks/pre-commit.sh" + }, + "checkpoints": { + "autoCheckpoint": true + }, + "planning": { + "autoEnter": true, + "requireApproval": true + } + } + }, + + "ci_cd": { + "name": "CI/CD Pipeline", + "description": "Configuration for automated CI/CD operations", + "config": { + "general": { + "model": "claude-sonnet-4-6", + "temperature": 0 + }, + "permissions": { + "mode": "unrestricted" + }, + "headless": { + "exitOnError": true, + "verbose": true, + "timeout": 3600 + }, + "logging": { + "level": "debug", + "file": "./ci-claude.log" + }, + "planning": { + "autoEnter": false, + "requireApproval": false + } + } + }, + + "security_audit": { + "name": "Security Audit", + "description": "Configuration for security-focused code analysis", + "config": { + "general": { + "model": "claude-opus-4", + "temperature": 0.2 + }, + "permissions": { + "mode": "plan" + }, + "extendedThinking": { + "enabled": true, + "showThinkingProcess": true, + "minThinkingTime": 10 + }, + "hooks": { + "PostToolUse:Read": "~/.claude/hooks/security-scan.sh ${file_path}" + } + } + }, + + "performance_optimization": { + "name": "Performance Optimization", + "description": "Configuration for performance analysis and optimization", + "config": { + "general": { + "model": "claude-sonnet-4-6", + "temperature": 0.4 + }, + "planning": { + "autoEnter": true, + "requireApproval": true + }, + "backgroundTasks": { + "enabled": true, + "maxConcurrentTasks": 5 + }, + "checkpoints": { + "autoCheckpoint": true + } + } + }, + + "pair_programming": { + "name": "Pair Programming", + "description": "Configuration for collaborative development", + "config": { + "general": { + "model": "claude-sonnet-4-6", + "temperature": 0.6 + }, + "permissions": { + "mode": "confirm" + }, + "planning": { + "autoEnter": true, + "requireApproval": true, + "showTimeEstimates": true + }, + "extendedThinking": { + "enabled": true, + "showThinkingProcess": true + }, + "ui": { + "compactMode": false, + "showProgress": true + } + } + }, + + "refactoring": { + "name": "Large Refactoring", + "description": "Configuration for major refactoring work", + "config": { + "general": { + "model": "claude-opus-4", + "temperature": 0.3 + }, + "planning": { + "autoEnter": true, + "requireApproval": true, + "showTimeEstimates": true + }, + "checkpoints": { + "autoCheckpoint": true + }, + "hooks": { + "PreToolUse:Edit": "~/.claude/hooks/backup-file.sh ${file_path}", + "PostToolUse:Edit": "npm test -- --findRelatedTests ${file_path}" + }, + "permissions": { + "mode": "confirm" + } + } + }, + + "autonomous": { + "name": "Autonomous Development", + "description": "Configuration for autonomous work with auto mode safety checks", + "config": { + "general": { + "model": "claude-sonnet-4-6", + "temperature": 0.5 + }, + "permissions": { + "mode": "auto" + }, + "sandbox": { + "enabled": true, + "failIfUnavailable": false + }, + "backgroundTasks": { + "enabled": true, + "maxConcurrentTasks": 3 + } + } + }, + + "documentation": { + "name": "Documentation Writing", + "description": "Configuration for writing documentation", + "config": { + "general": { + "model": "claude-sonnet-4-6", + "temperature": 0.7 + }, + "permissions": { + "mode": "unrestricted" + }, + "hooks": { + "PostToolUse:Write": "markdownlint ${file_path}" + } + } + } + } +} diff --git a/uk/09-advanced-features/setup-auto-mode-permissions.py b/uk/09-advanced-features/setup-auto-mode-permissions.py new file mode 100644 index 0000000..a677314 --- /dev/null +++ b/uk/09-advanced-features/setup-auto-mode-permissions.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +""" +setup-auto-mode-permissions.py + +Seed ~/.claude/settings.json with a conservative baseline of safe permissions +for Claude Code. The default set is read-only and local-inspection oriented; +optional flags let you widen the allowlist for editing, test execution, git +write operations, package installs, and GitHub CLI writes. + +Usage: + python3 setup-auto-mode-permissions.py + python3 setup-auto-mode-permissions.py --dry-run + python3 setup-auto-mode-permissions.py --include-edits --include-tests +""" + +from __future__ import annotations + +import argparse +import json +import tempfile +from pathlib import Path +from typing import Iterable + +SETTINGS_PATH = Path.home() / ".claude" / "settings.json" + +# Core baseline: read-only inspection and low-risk local shell commands. +CORE_PERMISSIONS = [ + "Read(*)", + "Glob(*)", + "Grep(*)", + "Agent(*)", + "Skill(*)", + "WebSearch(*)", + "WebFetch(*)", + "Bash(ls:*)", + "Bash(pwd:*)", + "Bash(which:*)", + "Bash(echo:*)", + "Bash(cat:*)", + "Bash(head:*)", + "Bash(tail:*)", + "Bash(wc:*)", + "Bash(sort:*)", + "Bash(uniq:*)", + "Bash(find:*)", + "Bash(dirname:*)", + "Bash(basename:*)", + "Bash(realpath:*)", + "Bash(file:*)", + "Bash(stat:*)", + "Bash(diff:*)", + "Bash(md5sum:*)", + "Bash(sha256sum:*)", + "Bash(date:*)", + "Bash(env:*)", + "Bash(printenv:*)", + "Bash(git status:*)", + "Bash(git log:*)", + "Bash(git diff:*)", + "Bash(git branch:*)", + "Bash(git show:*)", + "Bash(git rev-parse:*)", + "Bash(git remote -v:*)", + "Bash(git remote get-url:*)", + "Bash(git stash list:*)", +] + +# Optional but still local: file edits and task bookkeeping. +EDITING_PERMISSIONS = [ + "Edit(*)", + "Write(*)", + "NotebookEdit(*)", + "TaskCreate(*)", + "TaskUpdate(*)", +] + +# Optional dev/test commands. These can still execute arbitrary project scripts, +# so keep them opt-in rather than part of the default baseline. +TEST_AND_BUILD_PERMISSIONS = [ + "Bash(npm test:*)", + "Bash(cargo test:*)", + "Bash(go test:*)", + "Bash(pytest:*)", + "Bash(python3 -m pytest:*)", + "Bash(make:*)", + "Bash(cmake:*)", +] + +# Optional local git write operations. History-rewriting commands stay out of +# the default baseline because they are easy to misuse. +GIT_WRITE_PERMISSIONS = [ + "Bash(git add:*)", + "Bash(git commit:*)", + "Bash(git checkout:*)", + "Bash(git switch:*)", + "Bash(git stash:*)", + "Bash(git tag:*)", +] + +# Optional dependency/package commands. These are intentionally excluded from +# the default baseline because they can execute project hooks or fetch code. +PACKAGE_MANAGER_PERMISSIONS = [ + "Bash(npm ci:*)", + "Bash(npm install:*)", + "Bash(pip install:*)", + "Bash(pip3 install:*)", +] + +# Optional GitHub CLI write access. +GITHUB_WRITE_PERMISSIONS = [ + "Bash(gh pr create:*)", +] + +# Optional extra GitHub CLI read access. +GITHUB_READ_PERMISSIONS = [ + "Bash(gh pr view:*)", + "Bash(gh pr list:*)", + "Bash(gh issue view:*)", + "Bash(gh issue list:*)", + "Bash(gh repo view:*)", +] + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Seed Claude Code settings with a conservative permission baseline." + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Preview the rules that would be added without writing settings.json", + ) + parser.add_argument( + "--include-edits", + action="store_true", + help="Add file-editing permissions (Edit/Write/NotebookEdit/TaskCreate/TaskUpdate)", + ) + parser.add_argument( + "--include-tests", + action="store_true", + help="Add local build/test commands such as pytest, cargo test, and make", + ) + parser.add_argument( + "--include-git-write", + action="store_true", + help="Add local git mutation commands such as add, commit, checkout, and stash", + ) + parser.add_argument( + "--include-packages", + action="store_true", + help="Add package install commands such as npm ci, npm install, and pip install", + ) + parser.add_argument( + "--include-gh-write", + action="store_true", + help="Add GitHub CLI write permissions such as gh pr create", + ) + parser.add_argument( + "--include-gh-read", + action="store_true", + help="Add GitHub CLI read permissions such as gh pr view and gh repo view", + ) + return parser.parse_args() + + +def load_settings(path: Path) -> dict: + if not path.exists(): + return {} + + try: + with path.open() as f: + settings = json.load(f) + except json.JSONDecodeError as exc: + raise SystemExit(f"Invalid JSON in {path}: {exc}") from exc + + if not isinstance(settings, dict): + raise SystemExit(f"Expected {path} to contain a JSON object.") + + return settings + + +def build_permissions(args: argparse.Namespace) -> list[str]: + permissions = list(CORE_PERMISSIONS) + + if args.include_edits: + permissions.extend(EDITING_PERMISSIONS) + + if args.include_tests: + permissions.extend(TEST_AND_BUILD_PERMISSIONS) + + if args.include_git_write: + permissions.extend(GIT_WRITE_PERMISSIONS) + + if args.include_packages: + permissions.extend(PACKAGE_MANAGER_PERMISSIONS) + + if args.include_gh_write: + permissions.extend(GITHUB_WRITE_PERMISSIONS) + + if args.include_gh_read: + permissions.extend(GITHUB_READ_PERMISSIONS) + + return permissions + + +def append_unique(existing: list, new_items: Iterable[str]) -> list[str]: + seen = set(existing) + added: list[str] = [] + for item in new_items: + if item not in seen: + existing.append(item) + seen.add(item) + added.append(item) + return added + + +def atomic_write_json(path: Path, payload: dict) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + with tempfile.NamedTemporaryFile( + "w", + encoding="utf-8", + dir=str(path.parent), + delete=False, + ) as tmp: + json.dump(payload, tmp, indent=2) + tmp.write("\n") + tmp_path = Path(tmp.name) + + tmp_path.replace(path) + + +def main() -> None: + args = parse_args() + permissions_to_add = build_permissions(args) + + settings = load_settings(SETTINGS_PATH) + permissions = settings.setdefault("permissions", {}) + + if not isinstance(permissions, dict): + raise SystemExit("Expected permissions to be a JSON object.") + + allow = permissions.setdefault("allow", []) + if not isinstance(allow, list): + raise SystemExit("Expected permissions.allow to be a JSON array.") + + added = append_unique(allow, permissions_to_add) + + if not added: + print("Nothing to add — all selected rules already present.") + return + + print(f"{'Would add' if args.dry_run else 'Adding'} {len(added)} rule(s):") + for rule in added: + print(f" + {rule}") + + if args.dry_run: + print("\nDry run — no changes written.") + return + + atomic_write_json(SETTINGS_PATH, settings) + print(f"\nDone. {len(added)} rule(s) added to {SETTINGS_PATH}") + + +if __name__ == "__main__": + main() diff --git a/uk/CHANGELOG.md b/uk/CHANGELOG.md new file mode 100644 index 0000000..7a87b86 --- /dev/null +++ b/uk/CHANGELOG.md @@ -0,0 +1,154 @@ +# Changelog + +## v2.3.0 — 2026-04-07 + +### Features + +- build and publish EPUB artifacts per language (90e9c30) @Thiên Toán +- add missing pre-tool-check.sh hook to 06-hooks (b511ed1) @JiayuWang +- add Chinese translations in zh/ directory (89e89d4) @Luong NGUYEN +- Add performance-optimizer subagent and dependency-check hook (f53d080) @qk + +### Bug Fixes + +- Windows Git Bash compatibility + stdin JSON protocol (2cbb10c) @Luong NGUYEN +- correct autoCheckpoint config documentation in 08-checkpoints (749c79f) @JiayuWang +- embed SVG images instead of replacing with placeholders (1b16709) @Thiên Toán +- nested code fence rendering in memory README (ce24423) @Zhaoshan Duan +- apply review fixes dropped by squash merge (34259ca) @Luong NGUYEN +- make hook scripts compatible with Windows Git Bash and use stdin JSON protocol (107153d) @binyu li + +### Documentation + +- sync all tutorials with latest Claude Code docs (April 2026) (72d3b01) @Luong NGUYEN +- add Chinese language link to language switcher (6cbaa4d) @Luong NGUYEN +- add language switcher between English and Vietnamese (100c45e) @Luong NGUYEN +- add GitHub #1 Trending badge (0ca8c37) @Luong NGUYEN +- Introduce cc-context-stats for context zone monitoring (d41b335) @Luong NGUYEN +- Introduce luongnv89/skills collection and luongnv89/asm skill manager (7e3c0b6) @Luong NGUYEN +- Update README stats to reflect current GitHub metrics (5,900+ stars, 690+ forks) (5001525) @Luong NGUYEN +- Update README stats to reflect current GitHub metrics (3,900+ stars, 460+ forks) (9cb92d6) @Luong NGUYEN + +### Refactoring + +- replace Kroki HTTP dependency with local mmdc rendering (e76bbe4) @Luong NGUYEN +- shift quality checks to pre-commit, CI as 2nd pass (6d1e0ae) @Luong NGUYEN +- narrow auto-mode permissions baseline (2790fb2) @Luong NGUYEN +- Replace auto-adapt hook with one-time permissions setup script (995a5d6) @Luong NGUYEN + +### Other + +- shift-left quality gates — add mypy to pre-commit, fix CI failures (699fb39) @Luong NGUYEN +- Add Vietnamese (Tiếng Việt) Localization (a70777e) @Thiên Toán + +**Full Changelog**: https://github.com/luongnv89/claude-howto/compare/v2.2.0...v2.3.0 + +--- + +## v2.2.0 — 2026-03-26 + +### Documentation + +- Sync all tutorials and references with Claude Code v2.1.84 (f78c094) @luongnv89 + - Update slash commands to 55+ built-in + 5 bundled skills, mark 3 deprecated + - Expand hook events from 18 to 25, add `agent` hook type (now 4 types) + - Add Auto Mode, Channels, Voice Dictation to advanced features + - Add `effort`, `shell` skill frontmatter fields; `initialPrompt`, `disallowedTools` agent fields + - Add WebSocket MCP transport, elicitation, 2KB tool cap + - Add plugin LSP support, `userConfig`, `${CLAUDE_PLUGIN_DATA}` + - Update all reference docs (CATALOG, QUICK_REFERENCE, LEARNING-ROADMAP, INDEX) +- Rewrite README as landing-page-structured guide (32a0776) @luongnv89 + +### Bug Fixes + +- Add missing cSpell words and README sections for CI compliance (93f9d51) @luongnv89 +- Add `Sandboxing` to cSpell dictionary (b80ce6f) @luongnv89 + +**Full Changelog**: https://github.com/luongnv89/claude-howto/compare/v2.1.1...v2.2.0 + +--- + +## v2.1.1 — 2026-03-13 + +### Bug Fixes + +- Remove dead marketplace link failing CI link checks (3fdf0d6) @luongnv89 +- Add `sandboxed` and `pycache` to cSpell dictionary (dc64618) @luongnv89 + +**Full Changelog**: https://github.com/luongnv89/claude-howto/compare/v2.1.0...v2.1.1 + +--- + +## v2.1.0 — 2026-03-13 + +### Features + +- Add adaptive learning path with self-assessment and lesson quiz skills (1ef46cd) @luongnv89 + - `/self-assessment` — interactive proficiency quiz across 10 feature areas with personalized learning path + - `/lesson-quiz [lesson]` — per-lesson knowledge check with 8-10 targeted questions + +### Bug Fixes + +- Update broken URLs, deprecations, and outdated references (8fe4520) @luongnv89 +- Fix broken links in resources and self-assessment skill (7a05863) @luongnv89 +- Use tilde fences for nested code blocks in concepts guide (5f82719) @VikalpP +- Add missing words to cSpell dictionary (8df7572) @luongnv89 + +### Documentation + +- Phase 5 QA — fix consistency, URLs, and terminology across docs (00bbe4c) @luongnv89 +- Complete Phases 3-4 — new feature coverage and reference doc updates (132de29) @luongnv89 +- Add MCPorter runtime to MCP context bloat section (ef52705) @luongnv89 +- Add missing commands, features, and settings across 6 guides (4bc8f15) @luongnv89 +- Add style guide based on existing repo conventions (84141d0) @luongnv89 +- Add self-assessment row to guide comparison table (8fe0c96) @luongnv89 +- Add VikalpP to contributors list for PR #7 (d5b4350) @luongnv89 +- Add self-assessment and lesson-quiz skill references to README and roadmap (d5a6106) @luongnv89 + +### New Contributors + +- @VikalpP made their first contribution in #7 + +**Full Changelog**: https://github.com/luongnv89/claude-howto/compare/v2.0.0...v2.1.0 + +--- + +## v2.0.0 — 2026-02-01 + +### Features + +- Sync all documentation with Claude Code February 2026 features (487c96d) + - Update 26 files across all 10 tutorial directories and 7 reference documents + - Add documentation for **Auto Memory** — persistent learnings per project + - Add documentation for **Remote Control**, **Web Sessions**, and **Desktop App** + - Add documentation for **Agent Teams** (experimental multi-agent collaboration) + - Add documentation for **MCP OAuth 2.0**, **Tool Search**, and **Claude.ai Connectors** + - Add documentation for **Persistent Memory** and **Worktree Isolation** for subagents + - Add documentation for **Background Subagents**, **Task List**, **Prompt Suggestions** + - Add documentation for **Sandboxing** and **Managed Settings** (Enterprise) + - Add documentation for **HTTP Hooks** and 7 new hook events + - Add documentation for **Plugin Settings**, **LSP Servers**, and Marketplace updates + - Add documentation for **Summarize from Checkpoint** rewind option + - Document 17 new slash commands (`/fork`, `/desktop`, `/teleport`, `/tasks`, `/fast`, etc.) + - Document new CLI flags (`--worktree`, `--from-pr`, `--remote`, `--teleport`, `--teammate-mode`, etc.) + - Document new environment variables for auto memory, effort levels, agent teams, and more + +### Design + +- Redesign logo to compass-bracket mark with minimal palette (20779db) + +### Bug Fixes / Corrections + +- Update model names: Sonnet 4.5 → **Sonnet 4.6**, Opus 4.5 → **Opus 4.6** +- Fix permission mode names: replace fictional "Unrestricted/Confirm/Read-only" with actual `default`/`acceptEdits`/`plan`/`dontAsk`/`bypassPermissions` +- Fix hook events: remove fictional `PreCommit`/`PostCommit`/`PrePush`, add real events (`SubagentStart`, `WorktreeCreate`, `ConfigChange`, etc.) +- Fix CLI syntax: replace `claude-code --headless` with `claude -p` (print mode) +- Fix checkpoint commands: replace fictional `/checkpoint save/list/rewind/diff` with actual `Esc+Esc` / `/rewind` interface +- Fix session management: replace fictional `/session list/new/switch/save` with real `/resume`/`/rename`/`/fork` +- Fix plugin manifest format: migrate `plugin.yaml` → `.claude-plugin/plugin.json` +- Fix MCP config paths: `~/.claude/mcp.json` → `.mcp.json` (project) / `~/.claude.json` (user) +- Fix documentation URLs: `docs.claude.com` → `docs.anthropic.com`; remove fictional `plugins.claude.com` +- Remove fictional configuration fields across multiple files +- Update all "Last Updated" dates to February 2026 + +**Full Changelog**: https://github.com/luongnv89/claude-howto/compare/20779db...v2.0.0 diff --git a/uk/CODE_OF_CONDUCT.md b/uk/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..bc44f72 --- /dev/null +++ b/uk/CODE_OF_CONDUCT.md @@ -0,0 +1,224 @@ + + + + +# Кодекс поведінки контриб'юторів + +## Наше зобов'язання + +Ми зобов'язуємося забезпечити привітну та надихаючу спільноту для всіх. Ми запевняємо, що участь у нашій спільноті є вільною від переслідувань для кожного, незалежно від віку, статури, касти, інвалідності, етнічної приналежності, статевих ознак, гендерної ідентичності та вираження, рівня досвіду, освіти, соціально-економічного статусу, національності, зовнішнього вигляду, раси, релігії або сексуальної ідентичності та орієнтації. + +Ми прагнемо забезпечити позитивне, інклюзивне та безпечне середовище, де всі контриб'ютори відчувають повагу, цінність та можливість внести свій найкращий вклад. + +## Наші стандарти + +Приклади поведінки, що сприяє створенню позитивного середовища: + +### Будьте поважними +- Використовуйте привітну та інклюзивну мову +- Поважайте різні думки, погляди та досвід +- Приймайте конструктивну критику з гідністю +- Поважайте культурні та мовні відмінності +- Визнавайте та цінуйте різноманітне походження + +### Будьте колаборативними +- Працюйте разом над вирішенням конфліктів +- Відзначайте та визнавайте внески інших +- Допомагайте іншим вчитися та зростати +- Діліться знаннями та ставте запитання +- Підтримуйте колег по спільноті + +### Будьте професійними +- Тримайте обговорення зосередженими та продуктивними +- Уникайте принизливих або образливих зауважень +- Залишайтесь у темі в issues та обговореннях +- Використовуйте чітку та поважну комунікацію +- Припускайте добрі наміри в повідомленнях інших + +### Будьте інклюзивними +- Вітайте нових контриб'юторів +- Допомагайте адаптуватися людям, новим у проєкті +- Надавайте конструктивний зворотний зв'язок +- Залучайте людей з різними перспективами +- Створюйте простір для тихих голосів + +## Неприйнятна поведінка + +Наступні дії вважаються переслідуванням та є неприйнятними в нашій спільноті: + +### Переслідування та дискримінація +- Образливі коментарі щодо гендеру, гендерної ідентичності та вираження, сексуальної орієнтації, інвалідності, психічного захворювання, нейро(а)типовості, зовнішнього вигляду, статури, віку, раси або релігії +- Небажані коментарі щодо життєвого вибору та практик людини +- Навмисне неправильне вживання займенників або використання «мертвих» або відкинутих імен +- Безпідставні або не по темі сексуальні зображення або поведінка +- Фізичний контакт та імітований фізичний контакт без згоди або після прохання припинити + +### Зловживання +- Погрози насильством або агресивна мова, спрямована проти іншої людини +- Підбурювання до насильства або агресивна мова щодо будь-якої особи +- Навмисне залякування +- Переслідування або стеження +- Переслідування через повторні контакти +- Будь-яка інша поведінка, яку можна обґрунтовано вважати неприйнятною + +### Онлайн-переслідування +- Систематичне порушення онлайн-обговорень +- Публікація приватної інформації інших (доксинг) +- Небажана сексуальна увага або домагання +- Тролінг або навмисне порушення +- Спам або повторна реклама +- Поширення дезінформації + +### Помста +- Вжиття карних заходів проти когось за повідомлення про порушення +- Несправедливе ставлення до когось через повідомлення про неприйнятну поведінку +- Погрози повідомити про когось у відповідь + +## Область застосування + +Цей Кодекс поведінки застосовується до: +- Усіх просторів спільноти (GitHub issues, pull requests, обговорення тощо) +- Репозиторіїв та документації проєкту +- Подій та зібрань, організованих проєктом або від його імені +- Публічних просторів, де особи представляють проєкт або спільноту +- Приватних комунікацій, коли вони стосуються справ проєкту + +Кодекс поведінки застосовується до всіх просторів проєкту, а також коли особа представляє проєкт або спільноту в публічних просторах. Приклади представлення проєкту або спільноти включають використання офіційної електронної адреси проєкту, публікації через офіційний акаунт у соціальних мережах або діяльність як призначений представник на онлайн- або офлайн-заході. + +## Застосування + +Лідери спільноти відповідають за роз'яснення та дотримання наших стандартів прийнятної поведінки та вживатимуть відповідних та справедливих коригувальних дій у відповідь на будь-яку поведінку, яку вони вважають неприйнятною, загрозливою, образливою або шкідливою. + +### Повідомлення про порушення + +Якщо ви стали свідком або жертвою неприйнятної поведінки, або маєте будь-які інші занепокоєння, будь ласка, повідомте: + +1. **Email**: Зв'яжіться з мейнтейнерами проєкту +2. **GitHub Issues**: Функція приватного повідомлення (якщо доступна) +3. **Пряме повідомлення**: Зв'яжіться з мейнтейнером проєкту напряму + +Вкажіть: +- Вашу контактну інформацію +- Імена залучених осіб (якщо вам комфортно ділитися) +- Опис інциденту +- Контекст та відповідні посилання +- Як інцидент вплинув на вас + +**Усі повідомлення зберігатимуться конфіденційно.** + +### Розслідування та реагування + +Лідери спільноти: +- Переглядатимуть та розслідуватимуть усі скарги оперативно та справедливо +- Зберігатимуть конфіденційність щодо репортера +- Повідомлятимуть репортера про статус його повідомлення +- Вживатимуть відповідних дій на основі результатів + +Можливі дії: +- Приватне попередження або консультація +- Публічні вибачення (за згодою порушника) +- Тимчасове або постійне видалення з просторів спільноти +- Втрата привілеїв контриб'ютора +- Ескалація на GitHub або інші платформи за потреби + +### Процес апеляції + +Якщо ви вважаєте, що рішення було прийнято помилково, ви можете оскаржити: +1. Надавши нову інформацію або контекст +2. Запросивши перегляд кількома мейнтейнерами +3. Звернувшись протягом 30 днів після початкового рішення + +## Настанови для шанобливої незгоди + +Розбіжності є природними в технічних спільнотах. Ось як не погоджуватись шанобливо: + +### Робіть +- ✅ Зосереджуйтесь на ідеї, а не на людині +- ✅ Ставте уточнюючі запитання +- ✅ Визнавайте слушні моменти в протилежних поглядах +- ✅ Використовуйте «Я»-висловлювання («Я не погоджуюсь, тому що...») +- ✅ Посилайтесь на джерела та факти +- ✅ Дякуйте іншим за їхню перспективу + +### Не робіть +- ❌ Переходьте на особистості +- ❌ Використовуйте зневажливу мову («Це дурниця») +- ❌ Припускайте погані наміри +- ❌ Піднімайте непов'язані минулі конфлікти +- ❌ Підвищуйте тон або ставайте ворожими +- ❌ Навмисно спотворюйте погляди інших + +### Приклад шанобливої незгоди +``` +❌ Погано: "That's a dumb idea and you clearly don't understand the project." + +✅ Добре: "I see your point, but I'm concerned this approach might +not scale well with larger projects. Have you considered the +performance implications shown in this benchmark? I'd love to +hear your thoughts on this." +``` + +## Будування мостів + +Ми віримо в силу діалогу та примирення: + +- Якщо хтось вказує на вашу поведінку, слухайте та рефлексуйте +- Будьте готові вибачитися, якщо завдали шкоди +- Підтримуйте інших у навчанні та зростанні +- Визнавайте, що люди роблять помилки +- Зосереджуйтесь на рішеннях, а не на звинуваченнях +- Припускайте добрі наміри, зберігаючи відповідальність + +## Переваги дотримання цього кодексу + +Підтримуючи шанобливу та інклюзивну спільноту, ми: +- Залучаємо різноманітних контриб'юторів з різними перспективами +- Створюємо кращі рішення через співпрацю +- Будуємо міцніші стосунки +- Робимо проєкт привітнішим для новачків +- Виробляємо якіснішу документацію та приклади +- Формуємо культуру навчання та зростання + +## Спеціальні міркування + +### Для мейнтейнерів +- Подавайте приклад у дотриманні цього Кодексу +- Оперативно реагуйте на повідомлення +- Будьте прозорими щодо рішень +- Допомагайте справедливо вирішувати конфлікти +- Створюйте безпечне середовище для всіх + +### Для контриб'юторів +- Припускайте добрі наміри мейнтейнерів та інших контриб'юторів +- Надавайте конструктивний зворотний зв'язок +- Підтримуйте новачків +- Допомагайте дотримуватись стандартів спільноти +- Беріть відповідальність за культуру спільноти + +### Для всіх +- Пам'ятайте, що за кожним повідомленням стоїть людина +- Робіть перерви, якщо відчуваєте фрустрацію +- Звертайтесь до лідерів спільноти, якщо потрібна підтримка +- Допомагайте створювати спільноту, частиною якої хочете бути + +## Атрибуція + +Цей Кодекс поведінки адаптовано з: +- [Contributor Covenant](https://www.contributor-covenant.org/) +- [Mozilla Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/) +- [Python Community Code of Conduct](https://www.python.org/psf/conduct/) + +## Запитання? + +Якщо у вас є запитання щодо цього Кодексу поведінки, зверніться до мейнтейнерів проєкту. + +## Історія версій + +- **Версія 1.0** (Січень 2026) — Початковий Кодекс поведінки + +--- + +**Дякуємо за допомогу в створенні привітної, інклюзивної та шанобливої спільноти!** 🌟 + +--- +**Останнє оновлення**: Квітень 2026 diff --git a/uk/CONTRIBUTING.md b/uk/CONTRIBUTING.md new file mode 100644 index 0000000..68d8b49 --- /dev/null +++ b/uk/CONTRIBUTING.md @@ -0,0 +1,385 @@ + + + + + + + Claude How To + + +# Внесок у Claude How To + +Дякуємо за ваш інтерес до участі в цьому проєкті! Цей довідник допоможе вам зрозуміти, як ефективно зробити внесок. + +## Про цей проєкт + +Claude How To — це візуальний, заснований на прикладах довідник з Claude Code. Ми надаємо: +- **Mermaid-діаграми**, що пояснюють як працюють функції +- **Готові до продакшену шаблони**, які можна використовувати одразу +- **Реальні приклади** з контекстом та найкращими практиками +- **Прогресивні шляхи навчання** від початківця до просунутого + +## Типи внесків + +### 1. Нові приклади або шаблони +Додавайте приклади для існуючих функцій (слеш-команди, навички, хуки тощо): +- Код, готовий до копіювання та вставки +- Чіткі пояснення як це працює +- Сценарії використання та переваги +- Поради з усунення проблем + +### 2. Покращення документації +- Уточнення заплутаних розділів +- Виправлення помилок та граматики +- Додавання відсутньої інформації +- Покращення прикладів коду + +### 3. Гайди з функцій +Створюйте гайди для нових функцій Claude Code: +- Покрокові туторіали +- Архітектурні діаграми +- Поширені патерни та анти-патерни +- Реальні робочі процеси + +### 4. Звіти про помилки +Повідомляйте про проблеми: +- Опишіть, що ви очікували +- Опишіть, що сталося насправді +- Вкажіть кроки для відтворення +- Додайте версію Claude Code та ОС + +### 5. Відгуки та пропозиції +Допоможіть покращити довідник: +- Запропонуйте кращі пояснення +- Вкажіть на прогалини в покритті +- Порекомендуйте нові розділи або реорганізацію + +## Початок роботи + +### 1. Форк та клонування +```bash +git clone https://github.com/luongnv89/claude-howto.git +cd claude-howto +``` + +### 2. Створення гілки +Використовуйте описову назву гілки: +```bash +git checkout -b add/feature-name +git checkout -b fix/issue-description +git checkout -b docs/improvement-area +``` + +### 3. Налаштування середовища + +Pre-commit хуки запускають ті ж перевірки, що й CI, локально перед кожним комітом. Усі чотири перевірки повинні пройти перед прийняттям PR. + +**Необхідні залежності:** + +```bash +# Python tooling (uv is the package manager for this project) +pip install uv +uv venv +source .venv/bin/activate +uv pip install -r scripts/requirements-dev.txt + +# Markdown linter (Node.js) +npm install -g markdownlint-cli + +# Mermaid diagram validator (Node.js) +npm install -g @mermaid-js/mermaid-cli + +# Install pre-commit and activate hooks +uv pip install pre-commit +pre-commit install +``` + +**Перевірка налаштування:** + +```bash +pre-commit run --all-files +``` + +Хуки, що запускаються при кожному коміті: + +| Хук | Що перевіряє | +|-----|-------------| +| `markdown-lint` | Форматування та структуру Markdown | +| `cross-references` | Відносні посилання, якорі, блоки коду | +| `mermaid-syntax` | Усі блоки ` ```mermaid ` коректно парсяться | +| `link-check` | Зовнішні URL-адреси доступні | +| `build-epub` | EPUB генерується без помилок (при змінах `.md`) | + +## Структура каталогів + +``` +├── 01-slash-commands/ # Ярлики, ініційовані користувачем +├── 02-memory/ # Приклади постійного контексту +├── 03-skills/ # Повторно використовувані можливості +├── 04-subagents/ # Спеціалізовані AI-асистенти +├── 05-mcp/ # Приклади Model Context Protocol +├── 06-hooks/ # Автоматизація на основі подій +├── 07-plugins/ # Пакетні функції +├── 08-checkpoints/ # Знімки сесій +├── 09-advanced-features/ # Планування, мислення, фони +├── 10-cli/ # Довідник CLI +├── scripts/ # Скрипти збірки та утиліт +└── README.md # Основний довідник +``` + +## Як зробити внесок прикладів + +### Додавання слеш-команди +1. Створіть файл `.md` в `01-slash-commands/` +2. Включіть: + - Чіткий опис що вона робить + - Сценарії використання + - Інструкції з встановлення + - Приклади використання + - Поради з налаштування +3. Оновіть `01-slash-commands/README.md` + +### Додавання навички +1. Створіть каталог в `03-skills/` +2. Включіть: + - `SKILL.md` — основна документація + - `scripts/` — допоміжні скрипти за потреби + - `templates/` — шаблони промптів + - Приклади використання в README +3. Оновіть `03-skills/README.md` + +### Додавання субагента +1. Створіть файл `.md` в `04-subagents/` +2. Включіть: + - Призначення та можливості агента + - Структуру системного промпту + - Приклади використання + - Приклади інтеграції +3. Оновіть `04-subagents/README.md` + +### Додавання конфігурації MCP +1. Створіть файл `.json` в `05-mcp/` +2. Включіть: + - Пояснення конфігурації + - Необхідні змінні оточення + - Інструкції з налаштування + - Приклади використання +3. Оновіть `05-mcp/README.md` + +### Додавання хука +1. Створіть файл `.sh` в `06-hooks/` +2. Включіть: + - Shebang та опис + - Чіткі коментарі, що пояснюють логіку + - Обробку помилок + - Міркування безпеки +3. Оновіть `06-hooks/README.md` + +## Настанови з написання + +### Стиль Markdown +- Використовуйте чіткі заголовки (H2 для секцій, H3 для підсекцій) +- Тримайте абзаци короткими та зосередженими +- Використовуйте маркеровані списки +- Включайте блоки коду з вказівкою мови +- Додавайте порожні рядки між секціями + +### Приклади коду +- Робіть приклади готовими до копіювання та вставки +- Коментуйте неочевидну логіку +- Включайте і прості, і просунуті версії +- Показуйте реальні сценарії використання +- Виділяйте потенційні проблеми + +### Документація +- Пояснюйте «чому», а не тільки «що» +- Вказуйте передумови +- Додавайте розділи з усунення проблем +- Посилайтесь на пов'язані теми +- Зберігайте дружність до початківців + +### JSON/YAML +- Використовуйте правильні відступи (2 або 4 пробіли послідовно) +- Додавайте коментарі, що пояснюють конфігурацію +- Включайте приклади валідації + +### Діаграми +- Використовуйте Mermaid коли можливо +- Тримайте діаграми простими та читабельними +- Додавайте текстовий опис під діаграмами для доступності +- Посилайтесь на відповідні розділи + +## Настанови для комітів + +Дотримуйтесь формату conventional commits: +``` +type(scope): description + +[optional body] +``` + +Типи: +- `feat`: Нова функція або приклад +- `fix`: Виправлення помилки +- `docs`: Зміни документації +- `refactor`: Реструктуризація коду +- `style`: Зміни форматування +- `test`: Додавання або зміни тестів +- `chore`: Збірка, залежності тощо + +Приклади: +``` +feat(slash-commands): Add API documentation generator +docs(memory): Improve personal preferences example +fix(README): Correct table of contents link +docs(skills): Add comprehensive code review skill +``` + +## Перед відправкою + +### Чеклист +- [ ] Код відповідає стилю та конвенціям проєкту +- [ ] Нові приклади включають чітку документацію +- [ ] README файли оновлені (локальний та кореневий) +- [ ] Немає чутливої інформації (API-ключі, облікові дані) +- [ ] Приклади протестовані та працюють +- [ ] Посилання перевірені та коректні +- [ ] Файли мають правильні дозволи (скрипти виконувані) +- [ ] Повідомлення коміту чітке та описове + +### Локальне тестування +```bash +# Run all pre-commit checks (same checks as CI) +pre-commit run --all-files + +# Review your changes +git diff +``` + +## Процес Pull Request + +1. **Створіть PR з чітким описом**: + - Що додає/виправляє? + - Чому це потрібно? + - Пов'язані issues (якщо є) + +2. **Включіть відповідні деталі**: + - Нова функція? Включіть сценарії використання + - Документація? Поясніть покращення + - Приклади? Покажіть до/після + +3. **Посилайтесь на issues**: + - Використовуйте `Closes #123` для автоматичного закриття пов'язаних issues + +4. **Будьте терплячими з рев'ю**: + - Мейнтейнери можуть запропонувати покращення + - Ітеруйте на основі зворотного зв'язку + - Остаточне рішення за мейнтейнерами + +## Процес код-рев'ю + +Рецензенти перевірять: +- **Точність**: Чи працює як описано? +- **Якість**: Чи готово до продакшену? +- **Консистентність**: Чи відповідає патернам проєкту? +- **Документація**: Чи чітко та повно? +- **Безпека**: Чи є вразливості? + +## Повідомлення про проблеми + +### Звіти про помилки +Включіть: +- Версію Claude Code +- Операційну систему +- Кроки для відтворення +- Очікувану поведінку +- Фактичну поведінку +- Скріншоти (за потреби) + +### Запити на функції +Включіть: +- Сценарій використання або проблему, що вирішується +- Запропоноване рішення +- Альтернативи, які ви розглядали +- Додатковий контекст + +### Проблеми з документацією +Включіть: +- Що незрозуміло або відсутнє +- Запропоновані покращення +- Приклади або посилання + +## Політики проєкту + +### Чутлива інформація +- Ніколи не комітьте API-ключі, токени або облікові дані +- Використовуйте значення-заповнювачі в прикладах +- Включайте `.env.example` для конфігураційних файлів +- Документуйте необхідні змінні оточення + +### Якість коду +- Тримайте приклади зосередженими та читабельними +- Уникайте надмірного ускладнення рішень +- Включайте коментарі для неочевидної логіки +- Тестуйте ретельно перед відправкою + +### Інтелектуальна власність +- Оригінальний контент належить автору +- Проєкт використовує освітню ліцензію +- Поважайте існуючі авторські права +- Вказуйте атрибуцію за потреби + +## Отримання допомоги + +- **Запитання**: Відкрийте обговорення в GitHub Issues +- **Загальна допомога**: Перевірте існуючу документацію +- **Допомога з розробки**: Перегляньте подібні приклади +- **Код-рев'ю**: Позначте мейнтейнерів у PR + +## Визнання + +Контриб'юторів відзначають у: +- Секції контриб'юторів README.md +- Сторінці контриб'юторів GitHub +- Історії комітів + +## Безпека + +При внесенні прикладів та документації, будь ласка, дотримуйтесь безпечних практик кодування: + +- **Ніколи не захардкоджуйте секрети або API-ключі** — використовуйте змінні оточення +- **Попереджайте про наслідки безпеки** — виділяйте потенційні ризики +- **Використовуйте безпечні значення за замовчуванням** — увімкніть функції безпеки за замовчуванням +- **Валідуйте введення** — показуйте правильну валідацію та санітизацію введення +- **Включайте нотатки безпеки** — документуйте міркування безпеки + +Щодо проблем безпеки, див. [SECURITY.md](SECURITY.md) для нашого процесу повідомлення про вразливості. + +## Кодекс поведінки + +Ми зобов'язуємося забезпечити привітну та інклюзивну спільноту. Будь ласка, прочитайте [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) для повних стандартів спільноти. + +Коротко: +- Будьте поважними та інклюзивними +- Приймайте зворотний зв'язок з гідністю +- Допомагайте іншим вчитися та зростати +- Уникайте переслідувань або дискримінації +- Повідомляйте про проблеми мейнтейнерам + +Усі контриб'ютори повинні дотримуватися цього кодексу та ставитися одне до одного з добротою та повагою. + +## Ліцензія + +Роблячи внесок у цей проєкт, ви погоджуєтесь, що ваші внески будуть ліцензовані за ліцензією MIT. Деталі див. у файлі [LICENSE](LICENSE). + +## Запитання? + +- Перевірте [README](README.md) +- Перегляньте [LEARNING-ROADMAP.md](LEARNING-ROADMAP.md) +- Подивіться існуючі приклади +- Відкрийте issue для обговорення + +Дякуємо за ваш внесок! 🙏 + +--- +**Останнє оновлення**: Квітень 2026 diff --git a/uk/SECURITY.md b/uk/SECURITY.md new file mode 100644 index 0000000..d079c96 --- /dev/null +++ b/uk/SECURITY.md @@ -0,0 +1,338 @@ + + + + +# Політика безпеки + +## Огляд + +Безпека проєкту Claude How To є важливою для нас. Цей документ описує наші практики безпеки та пояснює, як відповідально повідомляти про вразливості. + +## Підтримувані версії + +Ми надаємо оновлення безпеки для наступних версій: + +| Версія | Статус | Підтримка до | +|--------|--------|--------------| +| Остання (main) | ✅ Активна | Поточна + 6 місяців | +| Релізи 1.x | ✅ Активна | До наступної мажорної версії | + +**Примітка**: Як освітній проєкт-довідник, ми зосереджуємося на підтримці актуальних найкращих практик та безпеки документації, а не на традиційній підтримці версій. Оновлення застосовуються безпосередньо до гілки main. + +## Практики безпеки + +### Безпека коду + +1. **Управління залежностями** + - Усі Python-залежності зафіксовані в `requirements.txt` + - Регулярні оновлення через dependabot та ручний перегляд + - Сканування безпеки за допомогою Bandit при кожному коміті + - Pre-commit хуки для перевірок безпеки + +2. **Якість коду** + - Лінтинг за допомогою Ruff виявляє поширені проблеми + - Перевірка типів за допомогою mypy запобігає вразливостям, пов'язаним з типами + - Pre-commit хуки забезпечують дотримання стандартів + - Усі зміни переглядаються перед злиттям + +3. **Контроль доступу** + - Захист гілки `main` + - Обов'язковий рев'ю перед мерджем + - Статусні перевірки повинні пройти перед мерджем + - Обмежений доступ на запис до репозиторію + +### Безпека документації + +1. **Ніяких секретів у прикладах** + - Усі API-ключі в прикладах є заповнювачами + - Облікові дані ніколи не захардкоджені + - Файли `.env.example` показують необхідні змінні + - Чіткі попередження щодо управління секретами + +2. **Найкращі практики безпеки** + - Приклади демонструють безпечні патерни + - Попередження безпеки виділені в документації + - Посилання на офіційні гайди безпеки + - Обробка облікових даних обговорюється у відповідних розділах + +3. **Перегляд контенту** + - Вся документація перевіряється на проблеми безпеки + - Міркування безпеки в настановах для контриб'юторів + - Валідація зовнішніх посилань та посилань + +### Безпека залежностей + +1. **Сканування** + - Bandit сканує весь Python-код на вразливості + - Перевірка вразливостей залежностей через GitHub security alerts + - Регулярні ручні аудити безпеки + +2. **Оновлення** + - Патчі безпеки застосовуються оперативно + - Мажорні версії оцінюються ретельно + - Changelog включає оновлення, пов'язані з безпекою + +3. **Прозорість** + - Оновлення безпеки задокументовані в комітах + - Розкриття вразливостей обробляється відповідально + - Публічні рекомендації безпеки за потреби + +## Повідомлення про вразливість + +### Проблеми безпеки, які нас цікавлять + +Ми цінуємо повідомлення про: +- **Вразливості коду** в скриптах або прикладах +- **Вразливості залежностей** у Python-пакетах +- **Проблеми криптографії** в будь-яких прикладах коду +- **Недоліки автентифікації/авторизації** в документації +- **Ризики витоку даних** у прикладах конфігурації +- **Вразливості ін'єкцій** (SQL, команди тощо) +- **Проблеми SSRF/XXE/обхід шляхів** + +### Проблеми безпеки поза межами + +Це виходить за рамки цього проєкту: +- Вразливості в самому Claude Code (повідомляйте Anthropic) +- Проблеми із зовнішніми сервісами або бібліотеками (повідомляйте upstream) +- Соціальна інженерія або навчання користувачів (не стосується цього довідника) +- Теоретичні вразливості без підтвердження концепції +- Вразливості в залежностях, повідомлені через офіційні канали + +## Як повідомити + +### Приватне повідомлення (рекомендовано) + +**Для чутливих проблем безпеки використовуйте приватне повідомлення про вразливості GitHub:** + +1. Перейдіть: https://github.com/luongnv89/claude-howto/security/advisories +2. Натисніть "Report a vulnerability" +3. Заповніть деталі вразливості +4. Вкажіть: + - Чіткий опис вразливості + - Уражений компонент (файл, розділ, приклад) + - Потенційний вплив + - Кроки для відтворення (якщо можливо) + - Запропоноване виправлення (якщо є) + +**Що відбувається далі:** +- Ми підтвердимо отримання протягом 48 годин +- Ми дослідимо та оцінимо серйозність +- Ми працюватимемо з вами над розробкою виправлення +- Ми узгодимо графік розкриття +- Ми вкажемо вас у рекомендації безпеки (якщо ви не бажаєте анонімності) + +### Публічне повідомлення + +Для нечутливих проблем або тих, що вже є публічними: + +1. **Створіть GitHub Issue** з міткою `security` +2. Вкажіть: + - Назва: `[SECURITY]` з коротким описом + - Детальний опис + - Уражений файл або розділ + - Потенційний вплив + - Запропоноване виправлення + +## Процес реагування на вразливості + +### Оцінка (24 години) + +1. Ми підтверджуємо отримання повідомлення +2. Ми оцінюємо серйозність за [CVSS v3.1](https://www.first.org/cvss/v3.1/specification-document) +3. Ми визначаємо, чи входить це в область застосування +4. Ми зв'язуємося з вами з початковою оцінкою + +### Розробка (1-7 днів) + +1. Ми розробляємо виправлення +2. Ми переглядаємо та тестуємо виправлення +3. Ми створюємо рекомендацію безпеки +4. Ми готуємо нотатки до випуску + +### Розкриття (залежно від серйозності) + +**Критична (CVSS 9.0-10.0)** +- Виправлення випускається негайно +- Публічна рекомендація видається +- 24-годинне попереднє повідомлення репортерам + +**Висока (CVSS 7.0-8.9)** +- Виправлення випускається протягом 48-72 годин +- 5-денне попереднє повідомлення репортерам +- Публічна рекомендація при випуску + +**Середня (CVSS 4.0-6.9)** +- Виправлення випускається в наступному регулярному оновленні +- Публічна рекомендація при випуску + +**Низька (CVSS 0.1-3.9)** +- Виправлення включається в наступне регулярне оновлення +- Рекомендація при випуску + +### Публікація + +Ми публікуємо рекомендації безпеки, що включають: +- Опис вразливості +- Уражені компоненти +- Оцінку серйозності (CVSS-бал) +- Версію виправлення +- Обхідні рішення (якщо є) +- Подяку репортеру (з дозволу) + +## Найкращі практики для репортерів + +### Перед повідомленням + +- **Перевірте проблему**: Чи можете ви відтворити її стабільно? +- **Шукайте існуючі issues**: Чи вже повідомлено про це? +- **Перевірте документацію**: Чи є настанови щодо безпечного використання? +- **Тестуйте виправлення**: Чи працює ваше запропоноване виправлення? + +### При повідомленні + +- **Будьте конкретними**: Вказуйте точні шляхи файлів та номери рядків +- **Додавайте контекст**: Чому це проблема безпеки? +- **Покажіть вплив**: Що міг би зробити зловмисник? +- **Надайте кроки**: Як ми можемо відтворити? +- **Запропонуйте виправлення**: Як би ви це виправили? + +### Після повідомлення + +- **Будьте терплячими**: Ми маємо обмежені ресурси +- **Будьте на зв'язку**: Відповідайте на подальші запитання швидко +- **Зберігайте конфіденційність**: Не розголошуйте публічно до виправлення +- **Дотримуйтесь координації**: Слідуйте нашому графіку розкриття + +## Заголовки безпеки та конфігурація + +### Безпека репозиторію + +- **Захист гілок**: Основна гілка потребує 2 схвалень для змін +- **Статусні перевірки**: Усі CI/CD перевірки повинні пройти +- **CODEOWNERS**: Призначені рецензенти для ключових файлів +- **Підписані коміти**: Рекомендовано для контриб'юторів + +### Безпека розробки + +```bash +# Install pre-commit hooks +pre-commit install + +# Run security scans locally +bandit -c pyproject.toml -r scripts/ +mypy scripts/ --ignore-missing-imports +ruff check scripts/ +``` + +### Безпека залежностей + +```bash +# Check for known vulnerabilities +pip install safety +safety check + +# Or use pip-audit +pip install pip-audit +pip-audit +``` + +## Настанови безпеки для контриб'юторів + +### При написанні прикладів + +1. **Ніколи не захардкоджуйте секрети** + ```python + # ❌ Bad + api_key = "sk-1234567890" + + # ✅ Good + api_key = os.getenv("API_KEY") + ``` + +2. **Попереджайте про наслідки безпеки** + ```markdown + ⚠️ **Security Note**: Never commit `.env` files to git. + Add to `.gitignore` immediately. + ``` + +3. **Використовуйте безпечні значення за замовчуванням** + - Увімкнення автентифікації за замовчуванням + - Використання HTTPS де можливо + - Валідація та санітизація введення + - Використання параметризованих запитів + +4. **Документуйте міркування безпеки** + - Пояснюйте, чому безпека важлива + - Показуйте безпечні vs. небезпечні патерни + - Посилайтесь на авторитетні джерела + - Розміщуйте попередження на видному місці + +### При перегляді внесків + +1. **Перевіряйте на відкриті секрети** + - Сканування на поширені патерни (api_key=, password=) + - Перегляд конфігураційних файлів + - Перевірка змінних оточення + +2. **Перевіряйте безпечні практики кодування** + - Відсутність захардкоджених облікових даних + - Правильна валідація введення + - Безпечна автентифікація/авторизація + - Безпечна робота з файлами + +3. **Тестуйте наслідки безпеки** + - Чи можна це зловживати? + - Який найгірший сценарій? + - Чи є граничні випадки? + +## Ресурси безпеки + +### Офіційні стандарти +- [OWASP Top 10](https://owasp.org/www-project-top-ten/) +- [CWE Top 25](https://cwe.mitre.org/top25/) +- [CVSS Calculator](https://www.first.org/cvss/calculator/3.1) + +### Безпека Python +- [Python Security Advisories](https://www.python.org/dev/security/) +- [PyPI Security](https://pypi.org/help/#security) +- [Bandit Documentation](https://bandit.readthedocs.io/) + +### Управління залежностями +- [OWASP Dependency Check](https://owasp.org/www-project-dependency-check/) +- [GitHub Security Alerts](https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts) + +### Загальна безпека +- [Anthropic Security](https://www.anthropic.com/) +- [GitHub Security Best Practices](https://docs.github.com/en/code-security) + +## Архів рекомендацій безпеки + +Минулі рекомендації безпеки доступні на вкладці [GitHub Security Advisories](https://github.com/luongnv89/claude-howto/security/advisories). + +## Контакти + +Для запитань, пов'язаних з безпекою, або для обговорення практик безпеки: + +1. **Приватне повідомлення про безпеку**: Використовуйте приватне повідомлення про вразливості GitHub +2. **Загальні питання безпеки**: Відкрийте обговорення з тегом `[SECURITY]` +3. **Відгуки про політику безпеки**: Створіть issue з міткою `security` + +## Подяки + +Ми цінуємо дослідників безпеки та учасників спільноти, які допомагають підтримувати безпеку цього проєкту. Контриб'юторів, які відповідально повідомляють про вразливості, буде відзначено в наших рекомендаціях безпеки (якщо вони не бажають анонімності). + +## Оновлення політики + +Ця політика безпеки переглядається та оновлюється: +- При виявленні нових вразливостей +- При розвитку найкращих практик безпеки +- При зміні обсягу проєкту +- Щорічно як мінімум + +**Останнє оновлення**: Квітень 2026 +**Наступний перегляд**: Квітень 2027 + +--- + +Дякуємо за допомогу в забезпеченні безпеки Claude How To! 🔒 diff --git a/uk/STYLE_GUIDE.md b/uk/STYLE_GUIDE.md new file mode 100644 index 0000000..82050ac --- /dev/null +++ b/uk/STYLE_GUIDE.md @@ -0,0 +1,637 @@ + + + + + + + Claude How To + + +# Гайд зі стилю + +> Конвенції та правила форматування для внеску в Claude How To. Дотримуйтесь цього гайду, щоб тримати контент консистентним, професійним та зручним для підтримки. + +--- + +## Зміст + +- [Іменування файлів та каталогів](#іменування-файлів-та-каталогів) +- [Структура документа](#структура-документа) +- [Заголовки](#заголовки) +- [Форматування тексту](#форматування-тексту) +- [Списки](#списки) +- [Таблиці](#таблиці) +- [Блоки коду](#блоки-коду) +- [Посилання та перехресні посилання](#посилання-та-перехресні-посилання) +- [Діаграми](#діаграми) +- [Використання емодзі](#використання-емодзі) +- [YAML Frontmatter](#yaml-frontmatter) +- [Зображення та медіа](#зображення-та-медіа) +- [Тон та голос](#тон-та-голос) +- [Повідомлення комітів](#повідомлення-комітів) +- [Чеклист для авторів](#чеклист-для-авторів) + +--- + +## Іменування файлів та каталогів + +### Каталоги уроків + +Каталоги уроків використовують **двозначний числовий префікс** з **kebab-case** описом: + +``` +01-slash-commands/ +02-memory/ +03-skills/ +04-subagents/ +05-mcp/ +``` + +Номер відображає порядок навчального шляху від початківця до просунутого. + +### Імена файлів + +| Тип | Конвенція | Приклади | +|-----|-----------|---------| +| **README уроку** | `README.md` | `01-slash-commands/README.md` | +| **Файл функції** | Kebab-case `.md` | `code-reviewer.md`, `generate-api-docs.md` | +| **Shell-скрипт** | Kebab-case `.sh` | `format-code.sh`, `validate-input.sh` | +| **Конфіг-файл** | Стандартні назви | `.mcp.json`, `settings.json` | +| **Файл пам'яті** | З префіксом области | `project-CLAUDE.md`, `personal-CLAUDE.md` | +| **Документи верхнього рівня** | UPPER_CASE `.md` | `CATALOG.md`, `QUICK_REFERENCE.md`, `CONTRIBUTING.md` | +| **Зображення** | Kebab-case | `pr-slash-command.png`, `claude-howto-logo.svg` | + +### Правила + +- Використовуйте **нижній регістр** для всіх імен файлів та каталогів (крім документів верхнього рівня як `README.md`, `CATALOG.md`) +- Використовуйте **дефіси** (`-`) як розділювачі слів, ніколи підкреслення або пробіли +- Тримайте назви описовими, але стислими + +--- + +## Структура документа + +### Кореневий README + +Кореневий `README.md` дотримується порядку: + +1. Логотип (елемент `` з варіантами dark/light) +2. Заголовок H1 +3. Вступна цитата (однорядкова ціннісна пропозиція) +4. Секція "Чому цей довідник?" з таблицею порівняння +5. Горизонтальна лінія (`---`) +6. Зміст +7. Каталог функцій +8. Швидка навігація +9. Навчальний шлях +10. Секції функцій +11. Початок роботи +12. Найкращі практики / Усунення проблем +13. Внесок / Ліцензія + +### README уроку + +Кожен `README.md` уроку дотримується порядку: + +1. Заголовок H1 (напр., `# Slash Commands`) +2. Короткий вступний абзац +3. Таблиця швидкого довідника (опціонально) +4. Архітектурна діаграма (Mermaid) +5. Детальні секції (H2) +6. Практичні приклади (нумеровані, 4-6 прикладів) +7. Найкращі практики (таблиці Do's and Don'ts) +8. Усунення проблем +9. Пов'язані гайди / Офіційна документація +10. Метадані документа у футері + +### Файл функції/прикладу + +Окремі файли функцій (напр., `optimize.md`, `pr.md`): + +1. YAML frontmatter (якщо потрібно) +2. Заголовок H1 +3. Призначення / опис +4. Інструкції з використання +5. Приклади коду +6. Поради з налаштування + +### Розділювачі секцій + +Використовуйте горизонтальні лінії (`---`) для відділення основних регіонів документа: + +```markdown +--- + +## Нова основна секція +``` + +Розміщуйте їх після вступної цитати та між логічно окремими частинами документа. + +--- + +## Заголовки + +### Ієрархія + +| Рівень | Використання | Приклад | +|--------|-------------|---------| +| `#` H1 | Заголовок сторінки (один на документ) | `# Slash Commands` | +| `##` H2 | Основні секції | `## Best Practices` | +| `###` H3 | Підсекції | `### Adding a Skill` | +| `####` H4 | Під-підсекції (рідко) | `#### Configuration Options` | + +### Правила + +- **Один H1 на документ** — лише заголовок сторінки +- **Ніколи не пропускайте рівні** — не стрибайте з H2 на H4 +- **Тримайте заголовки стислими** — прагніть до 2-5 слів +- **Використовуйте sentence case** — великі літери тільки для першого слова та власних назв (виняток: назви функцій залишаються як є) +- **Додавайте емодзі-префікси тільки в кореневому README** для заголовків секцій (див. [Використання емодзі](#використання-емодзі)) + +--- + +## Форматування тексту + +### Виділення + +| Стиль | Коли використовувати | Приклад | +|-------|---------------------|---------| +| **Жирний** (`**text**`) | Ключові терміни, мітки в таблицях, важливі поняття | `**Installation**:` | +| *Курсив* (`*text*`) | Перше згадування технічного терміну, назви книг/документів | `*frontmatter*` | +| `Код` (`` `text` ``) | Імена файлів, команди, значення конфігурації, посилання на код | `` `CLAUDE.md` `` | + +### Цитати для виносок + +Використовуйте цитати з жирними префіксами для важливих нотаток: + +```markdown +> **Note**: Custom slash commands have been merged into skills since v2.0. + +> **Important**: Never commit API keys or credentials. + +> **Tip**: Combine memory with skills for maximum effectiveness. +``` + +Підтримувані типи виносок: **Note**, **Important**, **Tip**, **Warning**. + +### Абзаци + +- Тримайте абзаци короткими (2-4 речення) +- Додавайте порожній рядок між абзацами +- Починайте з ключового моменту, потім надавайте контекст +- Пояснюйте «чому», а не тільки «що» + +--- + +## Списки + +### Ненумеровані списки + +Використовуйте дефіси (`-`) з 2-пробіловим відступом для вкладеності: + +```markdown +- First item +- Second item + - Nested item + - Another nested item + - Deep nested (avoid going deeper than 3 levels) +- Third item +``` + +### Нумеровані списки + +Використовуйте нумеровані списки для послідовних кроків, інструкцій та ранжованих елементів: + +```markdown +1. First step +2. Second step + - Sub-point detail + - Another sub-point +3. Third step +``` + +### Описові списки + +Використовуйте жирні мітки для списків ключ-значення: + +```markdown +- **Performance bottlenecks** - identify O(n^2) operations, inefficient loops +- **Memory leaks** - find unreleased resources, circular references +- **Algorithm improvements** - suggest better algorithms or data structures +``` + +### Правила + +- Підтримуйте консистентні відступи (2 пробіли на рівень) +- Додавайте порожній рядок перед та після списку +- Тримайте елементи списку паралельними за структурою +- Уникайте вкладеності глибше 3 рівнів + +--- + +## Таблиці + +### Стандартний формат + +```markdown +| Column 1 | Column 2 | Column 3 | +|----------|----------|----------| +| Data | Data | Data | +``` + +### Поширені патерни таблиць + +**Порівняння функцій (3-4 колонки):** + +```markdown +| Feature | Invocation | Persistence | Best For | +|---------|-----------|------------|----------| +| **Slash Commands** | Manual (`/cmd`) | Session only | Quick shortcuts | +| **Memory** | Auto-loaded | Cross-session | Long-term learning | +``` + +**Do's and Don'ts:** + +```markdown +| Do | Don't | +|----|-------| +| Use descriptive names | Use vague names | +| Keep files focused | Overload a single file | +``` + +**Швидкий довідник:** + +```markdown +| Aspect | Details | +|--------|---------| +| **Purpose** | Generate API documentation | +| **Scope** | Project-level | +| **Complexity** | Intermediate | +``` + +### Правила + +- **Жирний для заголовків таблиці**, коли вони є мітками рядків (перша колонка) +- Вирівнюйте пайпи для читабельності у вихідному коді (опціонально, але бажано) +- Тримайте вміст комірок стислим; використовуйте посилання для деталей +- Використовуйте `форматування коду` для команд та шляхів файлів у комірках + +--- + +## Блоки коду + +### Теги мов + +Завжди вказуйте тег мови для підсвічування синтаксису: + +| Мова | Тег | Використання | +|------|-----|-------------| +| Shell | `bash` | Команди CLI, скрипти | +| Python | `python` | Код Python | +| JavaScript | `javascript` | Код JS | +| TypeScript | `typescript` | Код TS | +| JSON | `json` | Конфігураційні файли | +| YAML | `yaml` | Frontmatter, конфіг | +| Markdown | `markdown` | Приклади Markdown | +| SQL | `sql` | Запити до бази даних | +| Звичайний текст | (без тегу) | Очікуваний вивід, дерева каталогів | + +### Конвенції + +```bash +# Comment explaining what the command does +claude mcp add notion --transport http https://mcp.notion.com/mcp +``` + +- Додавайте **рядок коментаря** перед неочевидними командами +- Робіть всі приклади **готовими до копіювання та вставки** +- Показуйте **і прості, і просунуті** версії де доречно +- Включайте **очікуваний вивід** коли це допомагає розумінню (використовуйте блок коду без тегу) + +### Блоки встановлення + +Використовуйте цей патерн для інструкцій з встановлення: + +```bash +# Copy files to your project +cp 01-slash-commands/*.md .claude/commands/ +``` + +### Багатокрокові робочі процеси + +```bash +# Step 1: Create the directory +mkdir -p .claude/commands + +# Step 2: Copy the templates +cp 01-slash-commands/*.md .claude/commands/ + +# Step 3: Verify installation +ls .claude/commands/ +``` + +--- + +## Посилання та перехресні посилання + +### Внутрішні посилання (відносні) + +Використовуйте відносні шляхи для всіх внутрішніх посилань: + +```markdown +[Slash Commands](01-slash-commands/) +[Skills Guide](03-skills/) +[Memory Architecture](02-memory/#memory-architecture) +``` + +З каталогу уроку назад до кореня або сусіда: + +```markdown +[Back to main guide](../README.md) +[Related: Skills](../03-skills/) +``` + +### Зовнішні посилання (абсолютні) + +Використовуйте повні URL з описовим текстом якоря: + +```markdown +[Anthropic's official documentation](https://code.claude.com/docs/en/overview) +``` + +- Ніколи не використовуйте "click here" або "this link" як текст якоря +- Використовуйте описовий текст, що має сенс поза контекстом + +### Якорі секцій + +Посилайтесь на секції в тому ж документі за допомогою GitHub-style якорів: + +```markdown +[Feature Catalog](#-feature-catalog) +[Best Practices](#best-practices) +``` + +### Патерн пов'язаних гайдів + +Завершуйте уроки секцією пов'язаних гайдів: + +```markdown +## Related Guides + +- [Slash Commands](../01-slash-commands/) - Quick shortcuts +- [Memory](../02-memory/) - Persistent context +- [Skills](../03-skills/) - Reusable capabilities +``` + +--- + +## Діаграми + +### Mermaid + +Використовуйте Mermaid для всіх діаграм. Підтримувані типи: + +- `graph TB` / `graph LR` — архітектура, ієрархія, потік +- `sequenceDiagram` — потоки взаємодії +- `timeline` — хронологічні послідовності + +### Стильові конвенції + +Застосовуйте консистентні кольори за допомогою блоків стилів: + +```mermaid +graph TB + A["Component A"] --> B["Component B"] + B --> C["Component C"] + + style A fill:#e1f5fe,stroke:#333,color:#333 + style B fill:#fce4ec,stroke:#333,color:#333 + style C fill:#e8f5e9,stroke:#333,color:#333 +``` + +**Палітра кольорів:** + +| Колір | Hex | Використання | +|-------|-----|-------------| +| Світло-блакитний | `#e1f5fe` | Основні компоненти, входи | +| Світло-рожевий | `#fce4ec` | Обробка, проміжне ПЗ | +| Світло-зелений | `#e8f5e9` | Виходи, результати | +| Світло-жовтий | `#fff9c4` | Конфігурація, опціональне | +| Світло-фіолетовий | `#f3e5f5` | Користувацький інтерфейс | + +### Правила + +- Використовуйте `["Label text"]` для міток вузлів (дозволяє спеціальні символи) +- Використовуйте `
` для переносу рядків у мітках +- Тримайте діаграми простими (максимум 10-12 вузлів) +- Додавайте короткий текстовий опис під діаграмою для доступності +- Використовуйте зверху вниз (`TB`) для ієрархій, зліва направо (`LR`) для робочих процесів + +--- + +## Використання емодзі + +### Де використовуються емодзі + +Емодзі використовуються **рідко та цілеспрямовано** — тільки в певних контекстах: + +| Контекст | Емодзі | Приклад | +|----------|--------|---------| +| Заголовки секцій кореневого README | Іконки категорій | `## 📚 Learning Path` | +| Індикатори рівня навичок | Кольорові кола | 🟢 Початківець, 🔵 Середній, 🔴 Просунутий | +| Do's and Don'ts | Галочки/хрестики | ✅ Робіть це, ❌ Не робіть це | +| Рейтинги складності | Зірки | ⭐⭐⭐ | + +### Стандартний набір емодзі + +| Емодзі | Значення | +|--------|---------| +| 📚 | Навчання, гайди, документація | +| ⚡ | Початок роботи, швидкий довідник | +| 🎯 | Функції, швидкий довідник | +| 🎓 | Навчальні шляхи | +| 📊 | Статистика, порівняння | +| 🚀 | Встановлення, швидкі команди | +| 🟢 | Рівень початківця | +| 🔵 | Середній рівень | +| 🔴 | Просунутий рівень | +| ✅ | Рекомендована практика | +| ❌ | Уникати / анти-патерн | +| ⭐ | Одиниця рейтингу складності | + +### Правила + +- **Ніколи не використовуйте емодзі в основному тексті** або абзацах +- **Використовуйте емодзі в заголовках тільки** в кореневому README (не в README уроків) +- **Не додавайте декоративних емодзі** — кожне емодзі повинно нести значення +- Тримайте використання емодзі консистентним з таблицею вище + +--- + +## YAML Frontmatter + +### Файли функцій (навички, команди, агенти) + +```yaml +--- +name: unique-identifier +description: What this feature does and when to use it +allowed-tools: Bash, Read, Grep +--- +``` + +### Опціональні поля + +```yaml +--- +name: my-feature +description: Brief description +argument-hint: "[file-path] [options]" +allowed-tools: Bash, Read, Grep, Write, Edit +model: opus # opus, sonnet, or haiku +disable-model-invocation: true # User-only invocation +user-invocable: false # Hidden from user menu +context: fork # Run in isolated subagent +agent: Explore # Agent type for context: fork +--- +``` + +### Правила + +- Розміщуйте frontmatter на самому початку файлу +- Використовуйте **kebab-case** для поля `name` +- Тримайте `description` в одне речення +- Включайте тільки необхідні поля + +--- + +## Зображення та медіа + +### Патерн логотипу + +Усі документи, що починаються з логотипу, використовують елемент `` для підтримки dark/light режимів: + +```html + + + Claude How To + +``` + +### Скріншоти + +- Зберігайте у відповідному каталозі уроку (напр., `01-slash-commands/pr-slash-command.png`) +- Використовуйте kebab-case імена файлів +- Включайте описовий alt-текст +- Віддавайте перевагу SVG для діаграм, PNG для скріншотів + +### Правила + +- Завжди вказуйте alt-текст для зображень +- Тримайте розмір файлів зображень розумним (< 500KB для PNG) +- Використовуйте відносні шляхи для посилань на зображення +- Зберігайте зображення в тому ж каталозі, що й документ, який на них посилається, або в `assets/` для спільних зображень + +--- + +## Тон та голос + +### Стиль написання + +- **Професійний, але доступний** — технічна точність без перевантаження жаргоном +- **Активний стан** — "Create a file", а не "A file should be created" +- **Прямі інструкції** — "Run this command", а не "You might want to run this command" +- **Дружній до початківців** — припускаємо, що читач новий у Claude Code, але не новий у програмуванні + +### Принципи контенту + +| Принцип | Приклад | +|---------|---------| +| **Показуйте, а не розповідайте** | Надавайте працюючі приклади, а не абстрактні описи | +| **Прогресивна складність** | Починайте просто, додавайте глибину в наступних секціях | +| **Пояснюйте «чому»** | "Use memory for... because...", а не просто "Use memory for..." | +| **Готове до копіювання** | Кожен блок коду повинен працювати при безпосередній вставці | +| **Реальний контекст** | Використовуйте практичні сценарії, а не штучні приклади | + +### Словник + +- Використовуйте "Claude Code" (не "Claude CLI" або "the tool") +- Використовуйте "skill" (не "custom command" — застарілий термін) +- Використовуйте "lesson" або "guide" для нумерованих секцій +- Використовуйте "example" для окремих файлів функцій + +--- + +## Повідомлення комітів + +Дотримуйтесь [Conventional Commits](https://www.conventionalcommits.org/): + +``` +type(scope): description +``` + +### Типи + +| Тип | Використання | +|-----|-------------| +| `feat` | Нова функція, приклад або гайд | +| `fix` | Виправлення помилки, корекція, зламане посилання | +| `docs` | Покращення документації | +| `refactor` | Реструктуризація без зміни поведінки | +| `style` | Тільки зміни форматування | +| `test` | Додавання або зміни тестів | +| `chore` | Збірка, залежності, CI | + +### Скоупи + +Використовуйте назву уроку або області файлу як скоуп: + +``` +feat(slash-commands): Add API documentation generator +docs(memory): Improve personal preferences example +fix(README): Correct table of contents link +docs(skills): Add comprehensive code review skill +``` + +--- + +## Метадані документа у футері + +README уроків завершуються блоком метаданих: + +```markdown +--- +**Last Updated**: March 2026 +**Claude Code Version**: 2.1.97 +**Compatible Models**: Claude Sonnet 4.6, Claude Opus 4.6, Claude Haiku 4.5 +``` + +- Використовуйте формат місяць + рік (напр., "March 2026") +- Оновлюйте версію при зміні функцій +- Перелічуйте всі сумісні моделі + +--- + +## Чеклист для авторів + +Перед відправкою контенту перевірте: + +- [ ] Імена файлів/каталогів використовують kebab-case +- [ ] Документ починається з заголовку H1 (один на файл) +- [ ] Ієрархія заголовків коректна (без пропущених рівнів) +- [ ] Усі блоки коду мають теги мов +- [ ] Приклади коду готові до копіювання та вставки +- [ ] Внутрішні посилання використовують відносні шляхи +- [ ] Зовнішні посилання мають описовий текст якоря +- [ ] Таблиці правильно відформатовані +- [ ] Емодзі відповідають стандартному набору (якщо використовуються) +- [ ] Mermaid-діаграми використовують стандартну палітру кольорів +- [ ] Немає чутливої інформації (API-ключі, облікові дані) +- [ ] YAML frontmatter валідний (якщо використовується) +- [ ] Зображення мають alt-текст +- [ ] Абзаци короткі та зосереджені +- [ ] Секція пов'язаних гайдів посилається на відповідні уроки +- [ ] Повідомлення коміту відповідає формату conventional commits + +--- +**Останнє оновлення**: Квітень 2026 diff --git a/uk/TRANSLATION_QUEUE.md b/uk/TRANSLATION_QUEUE.md index 179040b..f49043e 100644 --- a/uk/TRANSLATION_QUEUE.md +++ b/uk/TRANSLATION_QUEUE.md @@ -2,9 +2,9 @@ # Прогрес перекладу українською -**Загальний прогрес:** 5/67 файлів (7%) +**Загальний прогрес:** 15/67 файлів (22%) -**Статус:** 🚧 В ПРОЦЕСІ — P1 ядро завершено ✅ +**Статус:** 🚧 В ПРОЦЕСІ — P1 ядро ✅ P2 модулі ✅ ## Пріоритет 1 — Ядро (5 файлів) ✅ @@ -22,18 +22,18 @@ | Модуль | Рядків | Статус | Дата | |--------|--------|--------|------| -| 01 slash-commands | 565 | ⏳ | | -| 02 memory | 1156 | ⏳ | | -| 03 skills | 811 | ⏳ | | -| 04 subagents | 1142 | ⏳ | | -| 05 mcp | 1113 | ⏳ | | -| 06 hooks | 1170 | ⏳ | | -| 07 plugins | 949 | ⏳ | | -| 08 checkpoints | 320 | ⏳ | | -| 09 advanced | 1945 | ⏳ | | -| 10 cli | 837 | ⏳ | | +| 01 slash-commands | 565 | ✅ | 2026-04-09 | +| 02 memory | 1156 | ✅ | 2026-04-09 | +| 03 skills | 811 | ✅ | 2026-04-09 | +| 04 subagents | 1142 | ✅ | 2026-04-09 | +| 05 mcp | 1113 | ✅ | 2026-04-09 | +| 06 hooks | 1170 | ✅ | 2026-04-09 | +| 07 plugins | 949 | ✅ | 2026-04-09 | +| 08 checkpoints | 320 | ✅ | 2026-04-09 | +| 09 advanced | 1945 | ✅ | 2026-04-09 | +| 10 cli | 837 | ✅ | 2026-04-09 | -**Прогрес P2:** 0/10 (0%) +**Прогрес P2:** 10/10 (100%) ✅ ## Пріоритет 3 — Приклади (47 файлів) · P4 — Допоміжні (5 файлів)