Claude-skill-registry flask-reviewer

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/flask-reviewer" ~/.claude/skills/majiayu000-claude-skill-registry-flask-reviewer && rm -rf "$T"
manifest: skills/data/flask-reviewer/SKILL.md
source content

Flask Reviewer Skill

Purpose

Reviews Flask projects for application structure, extension usage, and best practices.

When to Use

  • Flask project code review
  • Blueprint structure review
  • Extension configuration review
  • Request handling patterns
  • Flask API design

Project Detection

  • flask
    in requirements.txt/pyproject.toml
  • from flask import Flask
    imports
  • app.py
    or
    __init__.py
    with Flask()
  • blueprints/
    or
    routes/
    directory

Workflow

Step 1: Analyze Project

**Flask**: 3.0+
**Extensions**: Flask-SQLAlchemy, Flask-Login, Flask-WTF
**API**: Flask-RESTful / Flask-RESTX
**Database**: SQLAlchemy
**Template**: Jinja2

Step 2: Select Review Areas

AskUserQuestion:

"Which areas to review?"
Options:
- Full Flask review (recommended)
- Application structure
- Blueprint organization
- Extension configuration
- Security and validation
multiSelect: true

Detection Rules

Application Factory

CheckRecommendationSeverity
Global app instanceUse application factoryHIGH
Config in codeUse config classesMEDIUM
No extension initUse init_app patternMEDIUM
Circular importsUse factory + blueprintsHIGH
# BAD: Global app instance
# app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
db = SQLAlchemy(app)  # Tight coupling

# GOOD: Application factory
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def create_app(config_name="default"):
    app = Flask(__name__)
    app.config.from_object(config[config_name])

    db.init_app(app)

    from app.routes import main_bp, api_bp
    app.register_blueprint(main_bp)
    app.register_blueprint(api_bp, url_prefix="/api")

    return app

Blueprint Organization

CheckRecommendationSeverity
All routes in one fileSplit into blueprintsMEDIUM
No URL prefixAdd url_prefix to blueprintsLOW
Mixed concernsSeparate by domainMEDIUM
No init.py exportsExport blueprint properlyLOW
# GOOD: Blueprint structure
# app/routes/users.py
from flask import Blueprint, request, jsonify

users_bp = Blueprint("users", __name__, url_prefix="/users")

@users_bp.route("/", methods=["GET"])
def list_users():
    return jsonify(users=User.query.all())

@users_bp.route("/<int:user_id>", methods=["GET"])
def get_user(user_id):
    user = User.query.get_or_404(user_id)
    return jsonify(user=user.to_dict())

# app/routes/__init__.py
from app.routes.users import users_bp
from app.routes.products import products_bp

__all__ = ["users_bp", "products_bp"]

Request Handling

CheckRecommendationSeverity
No input validationUse marshmallow/pydanticHIGH
request.json without checkHandle None caseMEDIUM
No error handlersAdd @app.errorhandlerMEDIUM
Sync blocking callsConsider async or CeleryMEDIUM
# BAD: No validation
@app.route("/user", methods=["POST"])
def create_user():
    data = request.json  # Could be None!
    user = User(name=data["name"])  # KeyError risk
    return jsonify(user.to_dict())

# GOOD: With validation (marshmallow)
from marshmallow import Schema, fields, validate

class UserSchema(Schema):
    name = fields.Str(required=True, validate=validate.Length(min=1, max=100))
    email = fields.Email(required=True)

user_schema = UserSchema()

@app.route("/user", methods=["POST"])
def create_user():
    data = request.get_json()
    if not data:
        return jsonify(error="No JSON data"), 400

    errors = user_schema.validate(data)
    if errors:
        return jsonify(errors=errors), 400

    user = User(**user_schema.load(data))
    db.session.add(user)
    db.session.commit()
    return jsonify(user=user.to_dict()), 201

Configuration

CheckRecommendationSeverity
Secrets in codeUse environment variablesCRITICAL
No config classesUse config hierarchyMEDIUM
DEBUG=True in prodEnvironment-based configCRITICAL
No instance folderUse instance configLOW
# config.py
import os

class Config:
    SECRET_KEY = os.environ.get("SECRET_KEY") or "dev-key-change-me"
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = os.environ.get("DEV_DATABASE_URL") or \
        "sqlite:///dev.db"

class ProductionConfig(Config):
    DEBUG = False
    SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL")

    @classmethod
    def init_app(cls, app):
        # Production-specific initialization
        import logging
        from logging.handlers import RotatingFileHandler

        handler = RotatingFileHandler("app.log", maxBytes=10240, backupCount=10)
        handler.setLevel(logging.INFO)
        app.logger.addHandler(handler)

config = {
    "development": DevelopmentConfig,
    "production": ProductionConfig,
    "default": DevelopmentConfig,
}

Error Handling

CheckRecommendationSeverity
No custom error pagesAdd error handlersMEDIUM
Exception details in responseHide in productionHIGH
No loggingAdd structured loggingMEDIUM
# app/errors.py
from flask import jsonify, render_template

def register_error_handlers(app):
    @app.errorhandler(400)
    def bad_request(error):
        if request_wants_json():
            return jsonify(error="Bad request"), 400
        return render_template("errors/400.html"), 400

    @app.errorhandler(404)
    def not_found(error):
        if request_wants_json():
            return jsonify(error="Not found"), 404
        return render_template("errors/404.html"), 404

    @app.errorhandler(500)
    def internal_error(error):
        db.session.rollback()
        app.logger.error(f"Internal error: {error}")
        if request_wants_json():
            return jsonify(error="Internal server error"), 500
        return render_template("errors/500.html"), 500

def request_wants_json():
    return request.accept_mimetypes.best_match(
        ["application/json", "text/html"]
    ) == "application/json"

Response Template

## Flask Code Review Results

**Project**: [name]
**Flask**: 3.0 | **SQLAlchemy**: 2.0 | **Extensions**: Login, WTF

### Application Structure
| Status | File | Issue |
|--------|------|-------|
| HIGH | app.py | Global app instance - use factory |

### Blueprint Organization
| Status | File | Issue |
|--------|------|-------|
| MEDIUM | routes.py | 50+ routes - split into blueprints |

### Request Handling
| Status | File | Issue |
|--------|------|-------|
| HIGH | views.py:34 | No input validation on POST |

### Configuration
| Status | File | Issue |
|--------|------|-------|
| CRITICAL | config.py | SECRET_KEY hardcoded |

### Recommended Actions
1. [ ] Implement application factory pattern
2. [ ] Split routes into domain blueprints
3. [ ] Add marshmallow validation schemas
4. [ ] Move secrets to environment variables

Best Practices

  1. Factory Pattern: Always use create_app()
  2. Blueprints: Organize by domain/feature
  3. Validation: marshmallow or pydantic
  4. Config: Environment-based hierarchy
  5. Extensions: Use init_app pattern

Integration

  • python-reviewer
    : General Python patterns
  • security-scanner
    : Flask security audit
  • api-documenter
    : API documentation