WHEN: Flask project review, Blueprint structure, extensions, request handling WHAT: Blueprint organization + Extension patterns + Request/response handling + Configuration + Testing WHEN NOT: FastAPI → fastapi-reviewer, Django → django-reviewer, General Python → python-reviewer
/plugin marketplace add physics91/claude-vibe/plugin install claude-vibe@physics91-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Reviews Flask projects for application structure, extension usage, and best practices.
flask in requirements.txt/pyproject.tomlfrom flask import Flask importsapp.py or __init__.py with Flask()blueprints/ or routes/ directory**Flask**: 3.0+
**Extensions**: Flask-SQLAlchemy, Flask-Login, Flask-WTF
**API**: Flask-RESTful / Flask-RESTX
**Database**: SQLAlchemy
**Template**: Jinja2
AskUserQuestion:
"Which areas to review?"
Options:
- Full Flask review (recommended)
- Application structure
- Blueprint organization
- Extension configuration
- Security and validation
multiSelect: true
| Check | Recommendation | Severity |
|---|---|---|
| Global app instance | Use application factory | HIGH |
| Config in code | Use config classes | MEDIUM |
| No extension init | Use init_app pattern | MEDIUM |
| Circular imports | Use factory + blueprints | HIGH |
# 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
| Check | Recommendation | Severity |
|---|---|---|
| All routes in one file | Split into blueprints | MEDIUM |
| No URL prefix | Add url_prefix to blueprints | LOW |
| Mixed concerns | Separate by domain | MEDIUM |
| No init.py exports | Export blueprint properly | LOW |
# 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"]
| Check | Recommendation | Severity |
|---|---|---|
| No input validation | Use marshmallow/pydantic | HIGH |
| request.json without check | Handle None case | MEDIUM |
| No error handlers | Add @app.errorhandler | MEDIUM |
| Sync blocking calls | Consider async or Celery | MEDIUM |
# 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
| Check | Recommendation | Severity |
|---|---|---|
| Secrets in code | Use environment variables | CRITICAL |
| No config classes | Use config hierarchy | MEDIUM |
| DEBUG=True in prod | Environment-based config | CRITICAL |
| No instance folder | Use instance config | LOW |
# 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,
}
| Check | Recommendation | Severity |
|---|---|---|
| No custom error pages | Add error handlers | MEDIUM |
| Exception details in response | Hide in production | HIGH |
| No logging | Add structured logging | MEDIUM |
# 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"
## 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
python-reviewer: General Python patternssecurity-scanner: Flask security auditapi-documenter: API documentationThis skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.