i18n(uk): add missing files, translate P4 root docs

- Copy code/image/config files across all modules
- Translate brand-voice and code-review templates
- Translate CONTRIBUTING, CODE_OF_CONDUCT, SECURITY, STYLE_GUIDE
- Copy CHANGELOG as-is (technical log)

Ref: luongnv89/claude-howto#63
This commit is contained in:
Evgenij I
2026-04-09 23:59:59 +03:00
parent c0d400b21b
commit 1a567be793
75 changed files with 7039 additions and 13 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

+5
View File
@@ -0,0 +1,5 @@
# Local skill testing
.claude/
# Blog post outputs
blog-posts/
@@ -0,0 +1,14 @@
Тема: [Чітка, орієнтована на вигоду тема]
Вітаю, [Ім'я]!
[Вступ: Яка цінність для них]
[Основна частина: Як це працює / Що вони отримають]
[Конкретний приклад або вигода]
[Заклик до дії: Чіткий наступний крок]
З повагою,
[Ім'я]
@@ -0,0 +1,4 @@
[Хук: Привернути увагу в першому рядку]
[2-3 рядки: Цінність або цікавий факт]
[Заклик до дії: Посилання, питання або залучення]
[Емодзі: 1-2 максимум для візуального інтересу]
+13
View File
@@ -0,0 +1,13 @@
# Приклади тону бренду
## Захоплива анонс
"Зекономте 8 годин на тиждень на код-рев'ю. Claude переглядає ваші PR автоматично."
## Емпатична підтримка
"Ми знаємо, що деплої можуть бути стресовими. Claude бере тестування на себе, щоб вам не хвилюватися."
## Впевнена презентація продукту
"Claude не просто пропонує код. Він розуміє вашу архітектуру і підтримує консистентність."
## Освітній блог-пост
"Давайте розглянемо, як агенти покращують процеси код-рев'ю. Ось що ми дізналися..."
@@ -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}")
@@ -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 <before_file> <after_file>")
sys.exit(1)
compare_files(sys.argv[1], sys.argv[2])
@@ -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 зірок
@@ -0,0 +1,47 @@
# Чеклист код-рев'ю
## Чеклист безпеки
- [ ] Немає захардкоджених облікових даних або секретів
- [ ] Валідація введення для всіх даних від користувача
- [ ] Захист від SQL-ін'єкцій (параметризовані запити)
- [ ] CSRF-захист для операцій зі зміною стану
- [ ] Захист від XSS з правильним екрануванням
- [ ] Перевірка автентифікації на захищених ендпоінтах
- [ ] Перевірка авторизації для ресурсів
- [ ] Безпечне хешування паролів (bcrypt, argon2)
- [ ] Немає чутливих даних у логах
- [ ] HTTPS обов'язковий
## Чеклист продуктивності
- [ ] Немає N+1 запитів
- [ ] Правильне використання індексів
- [ ] Кешування реалізовано де доцільно
- [ ] Немає блокуючих операцій в основному потоці
- [ ] Async/await використовується коректно
- [ ] Великі набори даних пагіновані
- [ ] Пул з'єднань до бази даних
- [ ] Регулярні вирази оптимізовані
- [ ] Немає зайвого створення об'єктів
- [ ] Витоки пам'яті запобігаються
## Чеклист якості
- [ ] Функції < 50 рядків
- [ ] Зрозумілі назви змінних
- [ ] Немає дублювання коду
- [ ] Правильна обробка помилок
- [ ] Коментарі пояснюють ЧОМУ, а не ЩО
- [ ] Немає console.log у продакшені
- [ ] Перевірка типів (TypeScript/JSDoc)
- [ ] Принципи SOLID дотримані
- [ ] Патерни проєктування застосовані коректно
- [ ] Самодокументований код
## Чеклист тестування
- [ ] Юніт-тести написані
- [ ] Граничні випадки покриті
- [ ] Сценарії помилок протестовані
- [ ] Інтеграційні тести є
- [ ] Покриття > 80%
- [ ] Немає нестабільних тестів
- [ ] Зовнішні залежності замоковані
- [ ] Зрозумілі назви тестів
@@ -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)
@@ -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 <file>
python analyze-complexity.py <before_file> <after_file> # Compare mode
python analyze-complexity.py --dir <directory> # 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()
@@ -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 <file>
python detect-smells.py --dir <directory>
python detect-smells.py -v <file> # 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'(?<![a-zA-Z_])\b(\d+\.?\d*)\b(?![a-zA-Z_])', line)
for num in numbers:
if num not in acceptable and float(num) > 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()
+1086
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["@modelcontextprotocol/server-database"],
"env": {
"DATABASE_URL": "postgresql://user:pass@localhost/mydb"
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["@modelcontextprotocol/server-filesystem", "/home/user/projects"]
}
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
+29
View File
@@ -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"]
}
}
}
+149
View File
@@ -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()
+126
View File
@@ -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()
+156
View File
@@ -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 <file_path>"
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
+49
View File
@@ -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
+31
View File
@@ -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
+68
View File
@@ -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
+50
View File
@@ -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
+96
View File
@@ -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
+78
View File
@@ -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
+54
View File
@@ -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
@@ -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"
}
+107
View File
@@ -0,0 +1,107 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="../../resources/logos/claude-howto-logo-dark.svg">
<img alt="Claude How To" src="../../resources/logos/claude-howto-logo.svg">
</picture>
# 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
```
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
});
@@ -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);
});
@@ -0,0 +1,11 @@
{
"mcpServers": {
"kubernetes": {
"command": "npx",
"args": ["@modelcontextprotocol/server-kubernetes"],
"env": {
"KUBECONFIG": "${KUBECONFIG}"
}
}
}
}
@@ -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!"
@@ -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 "===================="
@@ -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!"
@@ -0,0 +1,9 @@
{
"name": "documentation",
"version": "1.0.0",
"description": "Comprehensive documentation generation and maintenance",
"author": {
"name": "Community"
},
"license": "MIT"
}
+119
View File
@@ -0,0 +1,119 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="../../resources/logos/claude-howto-logo-dark.svg">
<img alt="Claude How To" src="../../resources/logos/claude-howto-logo.svg">
</picture>
# 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,11 @@
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
@@ -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
@@ -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](#)
@@ -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](#)
@@ -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"
}
+91
View File
@@ -0,0 +1,91 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="../../resources/logos/claude-howto-logo-dark.svg">
<img alt="Claude How To" src="../../resources/logos/claude-howto-logo.svg">
</picture>
# 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
```
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
});
@@ -0,0 +1,11 @@
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
@@ -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}"
}
}
}
}
}
@@ -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()
+154
View File
@@ -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
+224
View File
@@ -0,0 +1,224 @@
<!-- i18n-source: CODE_OF_CONDUCT.md -->
<!-- i18n-source-sha: 63a1416 -->
<!-- i18n-date: 2026-04-09 -->
# Кодекс поведінки контриб'юторів
## Наше зобов'язання
Ми зобов'язуємося забезпечити привітну та надихаючу спільноту для всіх. Ми запевняємо, що участь у нашій спільноті є вільною від переслідувань для кожного, незалежно від віку, статури, касти, інвалідності, етнічної приналежності, статевих ознак, гендерної ідентичності та вираження, рівня досвіду, освіти, соціально-економічного статусу, національності, зовнішнього вигляду, раси, релігії або сексуальної ідентичності та орієнтації.
Ми прагнемо забезпечити позитивне, інклюзивне та безпечне середовище, де всі контриб'ютори відчувають повагу, цінність та можливість внести свій найкращий вклад.
## Наші стандарти
Приклади поведінки, що сприяє створенню позитивного середовища:
### Будьте поважними
- Використовуйте привітну та інклюзивну мову
- Поважайте різні думки, погляди та досвід
- Приймайте конструктивну критику з гідністю
- Поважайте культурні та мовні відмінності
- Визнавайте та цінуйте різноманітне походження
### Будьте колаборативними
- Працюйте разом над вирішенням конфліктів
- Відзначайте та визнавайте внески інших
- Допомагайте іншим вчитися та зростати
- Діліться знаннями та ставте запитання
- Підтримуйте колег по спільноті
### Будьте професійними
- Тримайте обговорення зосередженими та продуктивними
- Уникайте принизливих або образливих зауважень
- Залишайтесь у темі в 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
+385
View File
@@ -0,0 +1,385 @@
<!-- i18n-source: CONTRIBUTING.md -->
<!-- i18n-source-sha: 63a1416 -->
<!-- i18n-date: 2026-04-09 -->
<picture>
<source media="(prefers-color-scheme: dark)" srcset="resources/logos/claude-howto-logo-dark.svg">
<img alt="Claude How To" src="resources/logos/claude-howto-logo.svg">
</picture>
# Внесок у 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
+338
View File
@@ -0,0 +1,338 @@
<!-- i18n-source: SECURITY.md -->
<!-- i18n-source-sha: 63a1416 -->
<!-- i18n-date: 2026-04-09 -->
# Політика безпеки
## Огляд
Безпека проєкту 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! 🔒
+637
View File
@@ -0,0 +1,637 @@
<!-- i18n-source: STYLE_GUIDE.md -->
<!-- i18n-source-sha: 63a1416 -->
<!-- i18n-date: 2026-04-09 -->
<picture>
<source media="(prefers-color-scheme: dark)" srcset="resources/logos/claude-howto-logo-dark.svg">
<img alt="Claude How To" src="resources/logos/claude-howto-logo.svg">
</picture>
# Гайд зі стилю
> Конвенції та правила форматування для внеску в 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. Логотип (елемент `<picture>` з варіантами 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"]` для міток вузлів (дозволяє спеціальні символи)
- Використовуйте `<br/>` для переносу рядків у мітках
- Тримайте діаграми простими (максимум 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` в одне речення
- Включайте тільки необхідні поля
---
## Зображення та медіа
### Патерн логотипу
Усі документи, що починаються з логотипу, використовують елемент `<picture>` для підтримки dark/light режимів:
```html
<picture>
<source media="(prefers-color-scheme: dark)" srcset="resources/logos/claude-howto-logo-dark.svg">
<img alt="Claude How To" src="resources/logos/claude-howto-logo.svg">
</picture>
```
### Скріншоти
- Зберігайте у відповідному каталозі уроку (напр., `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
+13 -13
View File
@@ -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 файлів)