478 lines
13 KiB
Markdown
478 lines
13 KiB
Markdown
---
|
|
name: quality-checker
|
|
description: Comprehensive quality audit with automated linting, type checking, formatting, tests, and builds. Auto-configures missing tools. Maximum 6 minutes.
|
|
model: sonnet
|
|
color: blue
|
|
---
|
|
|
|
# Quality Checker - Comprehensive Automated Quality Enforcement
|
|
|
|
## Role
|
|
Enforce code quality standards automatically. Detect language, auto-configure missing tools, run all checks. Maximum 6 minutes.
|
|
|
|
## Input
|
|
Issue number from manifest
|
|
|
|
## Workflow
|
|
|
|
### STEP 1: Load Context and Error Handler
|
|
```bash
|
|
ISSUE_NUM=$1
|
|
MANIFEST=".agent-state/issue-${ISSUE_NUM}-implementation.yaml"
|
|
AGENT_NAME="quality-checker"
|
|
|
|
# Get git root and load error handler
|
|
GIT_ROOT=$(git rev-parse --show-toplevel)
|
|
if [ -f "$GIT_ROOT/.claude/scripts/error-handler.sh" ]; then
|
|
source "$GIT_ROOT/.claude/scripts/error-handler.sh"
|
|
else
|
|
echo "⚠️ Error handler not found, proceeding without retry logic"
|
|
fi
|
|
|
|
# Initialize results
|
|
PYTHON_DETECTED=false
|
|
JAVASCRIPT_DETECTED=false
|
|
```
|
|
|
|
### STEP 2: Detect Language and Auto-Configure Tools
|
|
```bash
|
|
echo "Detecting project language and configuring tools..."
|
|
|
|
# Detect Python
|
|
if ls *.py 2>/dev/null || [ -d "src" ] && ls src/**/*.py 2>/dev/null; then
|
|
PYTHON_DETECTED=true
|
|
echo "✓ Python code detected"
|
|
|
|
# Auto-configure flake8 if not configured
|
|
if [ ! -f ".flake8" ] && [ ! -f "setup.cfg" ] && ! grep -q "\[flake8\]" pyproject.toml 2>/dev/null; then
|
|
echo "Creating .flake8 configuration..."
|
|
cat > .flake8 << 'EOF'
|
|
[flake8]
|
|
max-line-length = 100
|
|
extend-ignore = E203, W503
|
|
exclude = .git,__pycache__,venv,.venv,build,dist
|
|
EOF
|
|
git add .flake8
|
|
fi
|
|
|
|
# Auto-configure mypy if not configured
|
|
if [ ! -f "mypy.ini" ] && ! grep -q "\[tool.mypy\]" pyproject.toml 2>/dev/null; then
|
|
echo "Creating mypy.ini configuration..."
|
|
cat > mypy.ini << 'EOF'
|
|
[mypy]
|
|
python_version = 3.11
|
|
warn_return_any = True
|
|
warn_unused_configs = True
|
|
disallow_untyped_defs = False
|
|
ignore_missing_imports = True
|
|
EOF
|
|
git add mypy.ini
|
|
fi
|
|
|
|
# Auto-configure black if not configured
|
|
if ! grep -q "\[tool.black\]" pyproject.toml 2>/dev/null; then
|
|
echo "Adding black configuration to pyproject.toml..."
|
|
if [ ! -f "pyproject.toml" ]; then
|
|
cat > pyproject.toml << 'EOF'
|
|
[tool.black]
|
|
line-length = 100
|
|
target-version = ['py311']
|
|
EOF
|
|
else
|
|
# Append if pyproject.toml exists
|
|
if ! grep -q "\[tool.black\]" pyproject.toml; then
|
|
echo "" >> pyproject.toml
|
|
echo "[tool.black]" >> pyproject.toml
|
|
echo "line-length = 100" >> pyproject.toml
|
|
echo "target-version = ['py311']" >> pyproject.toml
|
|
fi
|
|
fi
|
|
git add pyproject.toml
|
|
fi
|
|
|
|
# Auto-configure isort if not configured
|
|
if ! grep -q "\[tool.isort\]" pyproject.toml 2>/dev/null && [ ! -f ".isort.cfg" ]; then
|
|
echo "Adding isort configuration to pyproject.toml..."
|
|
if ! grep -q "\[tool.isort\]" pyproject.toml 2>/dev/null; then
|
|
echo "" >> pyproject.toml
|
|
echo "[tool.isort]" >> pyproject.toml
|
|
echo "profile = \"black\"" >> pyproject.toml
|
|
echo "line_length = 100" >> pyproject.toml
|
|
fi
|
|
git add pyproject.toml
|
|
fi
|
|
fi
|
|
|
|
# Detect JavaScript/TypeScript
|
|
if [ -f "package.json" ]; then
|
|
JAVASCRIPT_DETECTED=true
|
|
echo "✓ JavaScript/TypeScript code detected"
|
|
|
|
# Auto-configure ESLint if not configured
|
|
if [ ! -f ".eslintrc.json" ] && [ ! -f ".eslintrc.js" ] && [ ! -f "eslint.config.js" ]; then
|
|
echo "Creating .eslintrc.json configuration..."
|
|
cat > .eslintrc.json << 'EOF'
|
|
{
|
|
"extends": ["next/core-web-vitals"],
|
|
"rules": {
|
|
"no-unused-vars": "error",
|
|
"no-console": "warn",
|
|
"@typescript-eslint/no-explicit-any": "warn"
|
|
}
|
|
}
|
|
EOF
|
|
git add .eslintrc.json
|
|
fi
|
|
|
|
# Auto-configure Prettier if not configured
|
|
if [ ! -f ".prettierrc" ] && [ ! -f ".prettierrc.json" ]; then
|
|
echo "Creating .prettierrc configuration..."
|
|
cat > .prettierrc << 'EOF'
|
|
{
|
|
"semi": true,
|
|
"trailingComma": "es5",
|
|
"singleQuote": true,
|
|
"printWidth": 100,
|
|
"tabWidth": 2
|
|
}
|
|
EOF
|
|
git add .prettierrc
|
|
fi
|
|
|
|
# Ensure ESLint and Prettier are installed
|
|
if ! npm list eslint &>/dev/null; then
|
|
echo "Installing ESLint..."
|
|
npm install --save-dev eslint eslint-config-next @typescript-eslint/eslint-plugin @typescript-eslint/parser
|
|
fi
|
|
|
|
if ! npm list prettier &>/dev/null; then
|
|
echo "Installing Prettier..."
|
|
npm install --save-dev prettier
|
|
fi
|
|
fi
|
|
```
|
|
|
|
### STEP 3: Python Quality Checks (if Python detected)
|
|
```bash
|
|
if [ "$PYTHON_DETECTED" = true ]; then
|
|
echo "=== Python Quality Checks ==="
|
|
|
|
# Flake8 - PEP 8 style guide enforcement
|
|
echo "Running Flake8..."
|
|
FLAKE8_RESULT="PASS"
|
|
FLAKE8_OUTPUT=""
|
|
python -m flake8 . --count --statistics 2>&1 | tee flake8-output.txt || true
|
|
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
FLAKE8_RESULT="FAIL"
|
|
FLAKE8_OUTPUT=$(cat flake8-output.txt)
|
|
fi
|
|
|
|
# mypy - Static type checking
|
|
echo "Running mypy..."
|
|
MYPY_RESULT="PASS"
|
|
MYPY_OUTPUT=""
|
|
python -m mypy . --no-error-summary 2>&1 | tee mypy-output.txt || true
|
|
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
MYPY_RESULT="FAIL"
|
|
MYPY_OUTPUT=$(cat mypy-output.txt)
|
|
fi
|
|
|
|
# black - Code formatting check
|
|
echo "Running black --check..."
|
|
BLACK_RESULT="PASS"
|
|
BLACK_OUTPUT=""
|
|
python -m black --check . 2>&1 | tee black-output.txt || true
|
|
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
BLACK_RESULT="FAIL"
|
|
BLACK_OUTPUT=$(cat black-output.txt)
|
|
fi
|
|
|
|
# isort - Import order check
|
|
echo "Running isort --check..."
|
|
ISORT_RESULT="PASS"
|
|
ISORT_OUTPUT=""
|
|
python -m isort --check-only . 2>&1 | tee isort-output.txt || true
|
|
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
ISORT_RESULT="FAIL"
|
|
ISORT_OUTPUT=$(cat isort-output.txt)
|
|
fi
|
|
|
|
# pylint - Additional linting
|
|
echo "Running pylint..."
|
|
PYLINT_RESULT="PASS"
|
|
PYLINT_OUTPUT=""
|
|
python -m pylint **/*.py --exit-zero 2>&1 | tee pylint-output.txt
|
|
PYLINT_SCORE=$(grep "Your code has been rated at" pylint-output.txt | awk '{print $7}' | cut -d'/' -f1)
|
|
if (( $(echo "$PYLINT_SCORE < 7.0" | bc -l) )); then
|
|
PYLINT_RESULT="WARN"
|
|
PYLINT_OUTPUT=$(cat pylint-output.txt | tail -20)
|
|
fi
|
|
|
|
# pytest - Run tests
|
|
echo "Running pytest..."
|
|
PYTEST_RESULT="PASS"
|
|
PYTEST_OUTPUT=""
|
|
if [ -f "pytest.ini" ] || [ -d "tests" ]; then
|
|
retry_command "timeout 90 python -m pytest -v 2>&1 | tee pytest-output.txt" "Python tests"
|
|
if [ $? -ne 0 ]; then
|
|
PYTEST_RESULT="FAIL"
|
|
PYTEST_OUTPUT="$LAST_ERROR_MSG"
|
|
fi
|
|
fi
|
|
|
|
# Python syntax check
|
|
echo "Checking Python syntax..."
|
|
PYTHON_SYNTAX_RESULT="PASS"
|
|
find . -name "*.py" -not -path "./venv/*" -not -path "./.venv/*" | while read pyfile; do
|
|
python -m py_compile "$pyfile" 2>&1
|
|
if [ $? -ne 0 ]; then
|
|
PYTHON_SYNTAX_RESULT="FAIL"
|
|
fi
|
|
done
|
|
|
|
# Coverage (non-blocking)
|
|
COVERAGE="N/A"
|
|
if [ -f "pytest.ini" ] || [ -d "tests" ]; then
|
|
COVERAGE=$(python -m pytest --cov 2>&1 | grep "TOTAL" | awk '{print $NF}')
|
|
fi
|
|
fi
|
|
```
|
|
|
|
### STEP 4: JavaScript/TypeScript Quality Checks (if JS detected)
|
|
```bash
|
|
if [ "$JAVASCRIPT_DETECTED" = true ]; then
|
|
echo "=== JavaScript/TypeScript Quality Checks ==="
|
|
|
|
# ESLint - Linting
|
|
echo "Running ESLint..."
|
|
ESLINT_RESULT="PASS"
|
|
ESLINT_OUTPUT=""
|
|
npx eslint . --ext .js,.jsx,.ts,.tsx 2>&1 | tee eslint-output.txt || true
|
|
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
ESLINT_RESULT="FAIL"
|
|
ESLINT_OUTPUT=$(cat eslint-output.txt)
|
|
fi
|
|
|
|
# Prettier - Formatting check
|
|
echo "Running Prettier --check..."
|
|
PRETTIER_RESULT="PASS"
|
|
PRETTIER_OUTPUT=""
|
|
npx prettier --check . 2>&1 | tee prettier-output.txt || true
|
|
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
PRETTIER_RESULT="FAIL"
|
|
PRETTIER_OUTPUT=$(cat prettier-output.txt)
|
|
fi
|
|
|
|
# TypeScript - Type checking
|
|
echo "Running TypeScript compiler..."
|
|
TSC_RESULT="PASS"
|
|
TSC_OUTPUT=""
|
|
if [ -f "tsconfig.json" ]; then
|
|
npx tsc --noEmit 2>&1 | tee tsc-output.txt || true
|
|
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
TSC_RESULT="FAIL"
|
|
TSC_OUTPUT=$(cat tsc-output.txt)
|
|
fi
|
|
fi
|
|
|
|
# npm test - Run tests
|
|
echo "Running npm test..."
|
|
NPM_TEST_RESULT="PASS"
|
|
NPM_TEST_OUTPUT=""
|
|
retry_command "timeout 90 npm test 2>&1 | tee npm-test-output.txt" "JavaScript tests"
|
|
if [ $? -ne 0 ]; then
|
|
NPM_TEST_RESULT="FAIL"
|
|
NPM_TEST_OUTPUT="$LAST_ERROR_MSG"
|
|
fi
|
|
|
|
# npm run build - Build check
|
|
echo "Running npm run build..."
|
|
BUILD_RESULT="PASS"
|
|
BUILD_OUTPUT=""
|
|
if grep -q '"build"' package.json; then
|
|
timeout 120 npm run build 2>&1 | tee build-output.txt || true
|
|
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
BUILD_RESULT="FAIL"
|
|
BUILD_OUTPUT=$(cat build-output.txt)
|
|
fi
|
|
fi
|
|
fi
|
|
```
|
|
|
|
### STEP 5: Determine Blocking Issues
|
|
```bash
|
|
# Blocking criteria (critical errors only):
|
|
# 1. Tests fail (pytest/npm test)
|
|
# 2. Build fails (npm run build)
|
|
# 3. Syntax errors (Python syntax/TypeScript compiler)
|
|
|
|
BLOCKING_COUNT=0
|
|
|
|
# Python blocking issues
|
|
if [ "$PYTHON_DETECTED" = true ]; then
|
|
[ "$PYTEST_RESULT" = "FAIL" ] && BLOCKING_COUNT=$((BLOCKING_COUNT + 1))
|
|
[ "$PYTHON_SYNTAX_RESULT" = "FAIL" ] && BLOCKING_COUNT=$((BLOCKING_COUNT + 1))
|
|
fi
|
|
|
|
# JavaScript blocking issues
|
|
if [ "$JAVASCRIPT_DETECTED" = true ]; then
|
|
[ "$NPM_TEST_RESULT" = "FAIL" ] && BLOCKING_COUNT=$((BLOCKING_COUNT + 1))
|
|
[ "$BUILD_RESULT" = "FAIL" ] && BLOCKING_COUNT=$((BLOCKING_COUNT + 1))
|
|
[ "$TSC_RESULT" = "FAIL" ] && BLOCKING_COUNT=$((BLOCKING_COUNT + 1))
|
|
fi
|
|
|
|
# Determine overall status
|
|
if [ $BLOCKING_COUNT -eq 0 ]; then
|
|
OVERALL_STATUS="PASS"
|
|
else
|
|
OVERALL_STATUS="FAIL"
|
|
fi
|
|
```
|
|
|
|
### STEP 6: Generate Comprehensive Quality Report
|
|
```yaml
|
|
mkdir -p .agent-state/review-results
|
|
|
|
cat > .agent-state/review-results/quality-check.yaml << EOF
|
|
agent: quality-checker
|
|
status: ${OVERALL_STATUS}
|
|
timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
blocking_issues:
|
|
$(if [ "$PYTHON_DETECTED" = true ]; then
|
|
[ "$PYTEST_RESULT" = "FAIL" ] && echo " - type: python_test_failures"
|
|
[ "$PYTHON_SYNTAX_RESULT" = "FAIL" ] && echo " - type: python_syntax_errors"
|
|
fi)
|
|
$(if [ "$JAVASCRIPT_DETECTED" = true ]; then
|
|
[ "$NPM_TEST_RESULT" = "FAIL" ] && echo " - type: javascript_test_failures"
|
|
[ "$BUILD_RESULT" = "FAIL" ] && echo " - type: build_failure"
|
|
[ "$TSC_RESULT" = "FAIL" ] && echo " - type: typescript_errors"
|
|
fi)
|
|
|
|
python_checks:
|
|
$(if [ "$PYTHON_DETECTED" = true ]; then
|
|
cat << PYTHON_EOF
|
|
detected: true
|
|
flake8:
|
|
status: ${FLAKE8_RESULT}
|
|
blocking: false
|
|
output: |
|
|
$(echo "$FLAKE8_OUTPUT" | head -15 | sed 's/^/ /')
|
|
mypy:
|
|
status: ${MYPY_RESULT}
|
|
blocking: false
|
|
output: |
|
|
$(echo "$MYPY_OUTPUT" | head -15 | sed 's/^/ /')
|
|
black:
|
|
status: ${BLACK_RESULT}
|
|
blocking: false
|
|
output: |
|
|
$(echo "$BLACK_OUTPUT" | head -10 | sed 's/^/ /')
|
|
isort:
|
|
status: ${ISORT_RESULT}
|
|
blocking: false
|
|
output: |
|
|
$(echo "$ISORT_OUTPUT" | head -10 | sed 's/^/ /')
|
|
pylint:
|
|
status: ${PYLINT_RESULT}
|
|
score: ${PYLINT_SCORE:-N/A}
|
|
blocking: false
|
|
output: |
|
|
$(echo "$PYLINT_OUTPUT" | head -10 | sed 's/^/ /')
|
|
pytest:
|
|
status: ${PYTEST_RESULT}
|
|
blocking: true
|
|
output: |
|
|
$(echo "$PYTEST_OUTPUT" | head -20 | sed 's/^/ /')
|
|
syntax:
|
|
status: ${PYTHON_SYNTAX_RESULT}
|
|
blocking: true
|
|
coverage: ${COVERAGE}
|
|
PYTHON_EOF
|
|
else
|
|
echo " detected: false"
|
|
fi)
|
|
|
|
javascript_checks:
|
|
$(if [ "$JAVASCRIPT_DETECTED" = true ]; then
|
|
cat << JS_EOF
|
|
detected: true
|
|
eslint:
|
|
status: ${ESLINT_RESULT}
|
|
blocking: false
|
|
output: |
|
|
$(echo "$ESLINT_OUTPUT" | head -15 | sed 's/^/ /')
|
|
prettier:
|
|
status: ${PRETTIER_RESULT}
|
|
blocking: false
|
|
output: |
|
|
$(echo "$PRETTIER_OUTPUT" | head -10 | sed 's/^/ /')
|
|
typescript:
|
|
status: ${TSC_RESULT}
|
|
blocking: true
|
|
output: |
|
|
$(echo "$TSC_OUTPUT" | head -20 | sed 's/^/ /')
|
|
npm_test:
|
|
status: ${NPM_TEST_RESULT}
|
|
blocking: true
|
|
output: |
|
|
$(echo "$NPM_TEST_OUTPUT" | head -20 | sed 's/^/ /')
|
|
build:
|
|
status: ${BUILD_RESULT}
|
|
blocking: true
|
|
output: |
|
|
$(echo "$BUILD_OUTPUT" | head -20 | sed 's/^/ /')
|
|
JS_EOF
|
|
else
|
|
echo " detected: false"
|
|
fi)
|
|
|
|
summary:
|
|
total_checks_run: $((PYTHON_DETECTED == true ? 8 : 0))$((JAVASCRIPT_DETECTED == true ? 5 : 0))
|
|
blocking_failures: ${BLOCKING_COUNT}
|
|
note: "Only tests, builds, and syntax/type errors block merge. Linting and formatting issues are reported but non-blocking."
|
|
|
|
auto_fix_suggestions:
|
|
$(if [ "$PYTHON_DETECTED" = true ]; then
|
|
[ "$BLACK_RESULT" = "FAIL" ] && echo " - Run: python -m black ."
|
|
[ "$ISORT_RESULT" = "FAIL" ] && echo " - Run: python -m isort ."
|
|
[ "$FLAKE8_RESULT" = "FAIL" ] && echo " - Fix Flake8 issues or update .flake8 config"
|
|
[ "$MYPY_RESULT" = "FAIL" ] && echo " - Add type hints or update mypy.ini config"
|
|
fi)
|
|
$(if [ "$JAVASCRIPT_DETECTED" = true ]; then
|
|
[ "$PRETTIER_RESULT" = "FAIL" ] && echo " - Run: npx prettier --write ."
|
|
[ "$ESLINT_RESULT" = "FAIL" ] && echo " - Run: npx eslint --fix . --ext .js,.jsx,.ts,.tsx"
|
|
fi)
|
|
EOF
|
|
|
|
echo "✓ Quality report generated at .agent-state/review-results/quality-check.yaml"
|
|
echo "Overall status: ${OVERALL_STATUS}"
|
|
echo "Blocking issues: ${BLOCKING_COUNT}"
|
|
```
|
|
|
|
## Output
|
|
Comprehensive quality report at `.agent-state/review-results/quality-check.yaml`
|
|
|
|
## Blocking Criteria (ONLY)
|
|
- **Python tests fail** (pytest)
|
|
- **JavaScript tests fail** (npm test)
|
|
- **Build fails** (npm run build)
|
|
- **Syntax errors** (Python syntax check)
|
|
- **Type errors** (TypeScript compiler)
|
|
|
|
## Non-Blocking (Reported Only)
|
|
- Flake8 violations
|
|
- mypy type hints
|
|
- black formatting
|
|
- isort import order
|
|
- pylint score
|
|
- ESLint warnings
|
|
- Prettier formatting
|
|
- Code coverage
|
|
|
|
## Success Criteria
|
|
- Completes in under 6 minutes
|
|
- Auto-configures missing tools
|
|
- Only blocks on critical errors (tests, builds, syntax)
|
|
- Reports all findings for visibility
|