forgedev 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +38 -0
- package/LICENSE +21 -0
- package/README.md +246 -0
- package/bin/devforge.js +4 -0
- package/package.json +33 -0
- package/src/claude-configurator.js +260 -0
- package/src/cli.js +119 -0
- package/src/composer.js +214 -0
- package/src/doctor-checks.js +743 -0
- package/src/doctor-prompts.js +295 -0
- package/src/doctor.js +281 -0
- package/src/guided.js +315 -0
- package/src/index.js +148 -0
- package/src/init-mode.js +134 -0
- package/src/prompts.js +155 -0
- package/src/recommender.js +186 -0
- package/src/scanner.js +368 -0
- package/src/uat-generator.js +189 -0
- package/src/utils.js +57 -0
- package/templates/auth/jwt-custom/backend/app/api/auth.py.template +45 -0
- package/templates/auth/jwt-custom/backend/app/api/deps.py.template +16 -0
- package/templates/auth/jwt-custom/backend/app/core/security.py.template +34 -0
- package/templates/auth/nextauth/src/app/api/auth/[...nextauth]/route.ts.template +3 -0
- package/templates/auth/nextauth/src/lib/auth.ts.template +30 -0
- package/templates/auth/nextauth/src/middleware.ts.template +14 -0
- package/templates/backend/fastapi/backend/Dockerfile.template +12 -0
- package/templates/backend/fastapi/backend/app/__init__.py +0 -0
- package/templates/backend/fastapi/backend/app/api/__init__.py +0 -0
- package/templates/backend/fastapi/backend/app/api/health.py.template +32 -0
- package/templates/backend/fastapi/backend/app/core/__init__.py +0 -0
- package/templates/backend/fastapi/backend/app/core/config.py.template +25 -0
- package/templates/backend/fastapi/backend/app/core/errors.py +37 -0
- package/templates/backend/fastapi/backend/app/core/retry.py +32 -0
- package/templates/backend/fastapi/backend/app/main.py.template +58 -0
- package/templates/backend/fastapi/backend/app/models/__init__.py +0 -0
- package/templates/backend/fastapi/backend/app/schemas/__init__.py +0 -0
- package/templates/backend/fastapi/backend/pyproject.toml.template +19 -0
- package/templates/backend/fastapi/backend/requirements.txt.template +14 -0
- package/templates/base/.gitignore.template +29 -0
- package/templates/base/README.md.template +25 -0
- package/templates/claude-code/agents/code-quality-reviewer.md +41 -0
- package/templates/claude-code/agents/production-readiness.md +55 -0
- package/templates/claude-code/agents/security-reviewer.md +41 -0
- package/templates/claude-code/agents/spec-validator.md +34 -0
- package/templates/claude-code/agents/uat-validator.md +37 -0
- package/templates/claude-code/claude-md/base.md +33 -0
- package/templates/claude-code/claude-md/fastapi.md +12 -0
- package/templates/claude-code/claude-md/fullstack.md +12 -0
- package/templates/claude-code/claude-md/nextjs.md +11 -0
- package/templates/claude-code/commands/audit-security.md +11 -0
- package/templates/claude-code/commands/audit-spec.md +9 -0
- package/templates/claude-code/commands/audit-wiring.md +17 -0
- package/templates/claude-code/commands/done.md +19 -0
- package/templates/claude-code/commands/generate-prd.md +45 -0
- package/templates/claude-code/commands/generate-uat.md +35 -0
- package/templates/claude-code/commands/help.md +26 -0
- package/templates/claude-code/commands/next.md +20 -0
- package/templates/claude-code/commands/optimize-claude-md.md +31 -0
- package/templates/claude-code/commands/pre-pr.md +19 -0
- package/templates/claude-code/commands/run-uat.md +21 -0
- package/templates/claude-code/commands/status.md +24 -0
- package/templates/claude-code/commands/verify-all.md +11 -0
- package/templates/claude-code/hooks/polyglot.json +36 -0
- package/templates/claude-code/hooks/python.json +36 -0
- package/templates/claude-code/hooks/scripts/autofix-polyglot.sh +16 -0
- package/templates/claude-code/hooks/scripts/autofix-python.sh +14 -0
- package/templates/claude-code/hooks/scripts/autofix-typescript.sh +14 -0
- package/templates/claude-code/hooks/scripts/guard-protected-files.sh +21 -0
- package/templates/claude-code/hooks/typescript.json +36 -0
- package/templates/claude-code/skills/ai-prompts/SKILL.md +43 -0
- package/templates/claude-code/skills/fastapi/SKILL.md +38 -0
- package/templates/claude-code/skills/nextjs/SKILL.md +39 -0
- package/templates/claude-code/skills/playwright/SKILL.md +37 -0
- package/templates/claude-code/skills/security-api/SKILL.md +47 -0
- package/templates/claude-code/skills/security-web/SKILL.md +41 -0
- package/templates/database/prisma-postgres/.env.example +1 -0
- package/templates/database/prisma-postgres/prisma/schema.prisma.template +18 -0
- package/templates/database/sqlalchemy-postgres/.env.example +1 -0
- package/templates/database/sqlalchemy-postgres/backend/alembic/env.py.template +40 -0
- package/templates/database/sqlalchemy-postgres/backend/alembic/versions/.gitkeep +0 -0
- package/templates/database/sqlalchemy-postgres/backend/alembic.ini.template +36 -0
- package/templates/database/sqlalchemy-postgres/backend/app/db/__init__.py +0 -0
- package/templates/database/sqlalchemy-postgres/backend/app/db/base.py +5 -0
- package/templates/database/sqlalchemy-postgres/backend/app/db/session.py.template +48 -0
- package/templates/frontend/nextjs/next.config.ts.template +7 -0
- package/templates/frontend/nextjs/package.json.template +41 -0
- package/templates/frontend/nextjs/postcss.config.mjs +7 -0
- package/templates/frontend/nextjs/src/app/api/health/route.ts.template +10 -0
- package/templates/frontend/nextjs/src/app/globals.css +1 -0
- package/templates/frontend/nextjs/src/app/layout.tsx.template +22 -0
- package/templates/frontend/nextjs/src/app/page.tsx.template +10 -0
- package/templates/frontend/nextjs/src/lib/db.ts.template +40 -0
- package/templates/frontend/nextjs/src/lib/errors.ts +28 -0
- package/templates/frontend/nextjs/src/lib/utils.ts +6 -0
- package/templates/frontend/nextjs/tsconfig.json +23 -0
- package/templates/infra/docker-compose/docker-compose.yml.template +19 -0
- package/templates/testing/playwright/e2e/example.spec.ts.template +15 -0
- package/templates/testing/playwright/playwright.config.ts.template +22 -0
- package/templates/testing/vitest/src/__tests__/example.test.ts.template +12 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from datetime import datetime, timedelta, timezone
|
|
2
|
+
|
|
3
|
+
from jose import JWTError, jwt
|
|
4
|
+
from passlib.context import CryptContext
|
|
5
|
+
|
|
6
|
+
from app.core.config import settings
|
|
7
|
+
|
|
8
|
+
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
9
|
+
|
|
10
|
+
SECRET_KEY = "change-me-in-production" # TODO: Move to settings/env
|
|
11
|
+
ALGORITHM = "HS256"
|
|
12
|
+
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|
16
|
+
return pwd_context.verify(plain_password, hashed_password)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def hash_password(password: str) -> str:
|
|
20
|
+
return pwd_context.hash(password)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
|
|
24
|
+
to_encode = data.copy()
|
|
25
|
+
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
|
|
26
|
+
to_encode.update({"exp": expire})
|
|
27
|
+
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def decode_access_token(token: str) -> dict | None:
|
|
31
|
+
try:
|
|
32
|
+
return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
|
33
|
+
except JWTError:
|
|
34
|
+
return None
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import NextAuth from 'next-auth';
|
|
2
|
+
import CredentialsProvider from 'next-auth/providers/credentials';
|
|
3
|
+
|
|
4
|
+
export const { handlers, signIn, signOut, auth } = NextAuth({
|
|
5
|
+
providers: [
|
|
6
|
+
CredentialsProvider({
|
|
7
|
+
name: 'Credentials',
|
|
8
|
+
credentials: {
|
|
9
|
+
email: { label: 'Email', type: 'email' },
|
|
10
|
+
password: { label: 'Password', type: 'password' },
|
|
11
|
+
},
|
|
12
|
+
async authorize(credentials) {
|
|
13
|
+
// TODO: Implement actual authentication logic
|
|
14
|
+
// Replace with database lookup and password verification
|
|
15
|
+
if (!credentials?.email || !credentials?.password) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
id: '1',
|
|
20
|
+
email: credentials.email as string,
|
|
21
|
+
name: 'User',
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
}),
|
|
25
|
+
],
|
|
26
|
+
session: { strategy: 'jwt' },
|
|
27
|
+
pages: {
|
|
28
|
+
signIn: '/login',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { auth } from '@/lib/auth';
|
|
2
|
+
|
|
3
|
+
export default auth((req) => {
|
|
4
|
+
const isLoggedIn = !!req.auth;
|
|
5
|
+
const isOnProtectedRoute = req.nextUrl.pathname.startsWith('/dashboard');
|
|
6
|
+
|
|
7
|
+
if (isOnProtectedRoute && !isLoggedIn) {
|
|
8
|
+
return Response.redirect(new URL('/login', req.nextUrl));
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const config = {
|
|
13
|
+
matcher: ['/dashboard/:path*'],
|
|
14
|
+
};
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from fastapi import APIRouter
|
|
2
|
+
from sqlalchemy import text
|
|
3
|
+
|
|
4
|
+
from app.db.session import async_session
|
|
5
|
+
|
|
6
|
+
router = APIRouter()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@router.get("/health")
|
|
10
|
+
async def health_check():
|
|
11
|
+
return {
|
|
12
|
+
"status": "ok",
|
|
13
|
+
"service": "{{PROJECT_NAME}}",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@router.get("/healthz")
|
|
18
|
+
async def health_check_deep():
|
|
19
|
+
"""Deep health check — verifies database connectivity."""
|
|
20
|
+
try:
|
|
21
|
+
async with async_session() as session:
|
|
22
|
+
await session.execute(text("SELECT 1"))
|
|
23
|
+
db_status = "connected"
|
|
24
|
+
except Exception:
|
|
25
|
+
db_status = "disconnected"
|
|
26
|
+
|
|
27
|
+
status = "ok" if db_status == "connected" else "degraded"
|
|
28
|
+
return {
|
|
29
|
+
"status": status,
|
|
30
|
+
"service": "{{PROJECT_NAME}}",
|
|
31
|
+
"checks": {"database": db_status},
|
|
32
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from pydantic_settings import BaseSettings
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Settings(BaseSettings):
|
|
5
|
+
# Application
|
|
6
|
+
app_name: str = "{{PROJECT_NAME_PASCAL}}"
|
|
7
|
+
debug: bool = False
|
|
8
|
+
|
|
9
|
+
# Database
|
|
10
|
+
database_url: str = "postgresql+asyncpg://postgres:postgres@localhost:5432/{{PROJECT_NAME_SNAKE}}"
|
|
11
|
+
|
|
12
|
+
# CORS
|
|
13
|
+
cors_origins: list[str] = ["http://localhost:3000"]
|
|
14
|
+
|
|
15
|
+
# External service timeouts (seconds)
|
|
16
|
+
external_service_timeout: float = 30.0
|
|
17
|
+
ai_service_timeout: float = 60.0
|
|
18
|
+
|
|
19
|
+
# Rate limiting
|
|
20
|
+
rate_limit_per_minute: int = 60
|
|
21
|
+
|
|
22
|
+
model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
settings = Settings()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class AppError(Exception):
|
|
2
|
+
"""Structured application error — never leaks stack traces to clients."""
|
|
3
|
+
|
|
4
|
+
def __init__(
|
|
5
|
+
self,
|
|
6
|
+
message: str,
|
|
7
|
+
status_code: int = 500,
|
|
8
|
+
code: str = "INTERNAL_ERROR",
|
|
9
|
+
):
|
|
10
|
+
self.message = message
|
|
11
|
+
self.status_code = status_code
|
|
12
|
+
self.code = code
|
|
13
|
+
super().__init__(message)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NotFoundError(AppError):
|
|
17
|
+
def __init__(self, resource: str, resource_id: str):
|
|
18
|
+
super().__init__(
|
|
19
|
+
message=f"{resource} not found: {resource_id}",
|
|
20
|
+
status_code=404,
|
|
21
|
+
code="NOT_FOUND",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ValidationError(AppError):
|
|
26
|
+
def __init__(self, message: str):
|
|
27
|
+
super().__init__(message=message, status_code=422, code="VALIDATION_ERROR")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AuthenticationError(AppError):
|
|
31
|
+
def __init__(self, message: str = "Authentication required"):
|
|
32
|
+
super().__init__(message=message, status_code=401, code="AUTHENTICATION_ERROR")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AuthorizationError(AppError):
|
|
36
|
+
def __init__(self, message: str = "Insufficient permissions"):
|
|
37
|
+
super().__init__(message=message, status_code=403, code="AUTHORIZATION_ERROR")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import functools
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def with_retry(max_retries: int = 3, base_delay: float = 1.0, max_delay: float = 30.0):
|
|
9
|
+
"""Decorator for retrying async functions with exponential backoff."""
|
|
10
|
+
|
|
11
|
+
def decorator(func):
|
|
12
|
+
@functools.wraps(func)
|
|
13
|
+
async def wrapper(*args, **kwargs):
|
|
14
|
+
last_exception = None
|
|
15
|
+
for attempt in range(1, max_retries + 1):
|
|
16
|
+
try:
|
|
17
|
+
return await func(*args, **kwargs)
|
|
18
|
+
except Exception as e:
|
|
19
|
+
last_exception = e
|
|
20
|
+
if attempt == max_retries:
|
|
21
|
+
break
|
|
22
|
+
delay = min(base_delay * (2 ** (attempt - 1)), max_delay)
|
|
23
|
+
logger.warning(
|
|
24
|
+
f"{func.__name__} attempt {attempt}/{max_retries} failed: {e}. "
|
|
25
|
+
f"Retrying in {delay:.1f}s..."
|
|
26
|
+
)
|
|
27
|
+
await asyncio.sleep(delay)
|
|
28
|
+
raise last_exception
|
|
29
|
+
|
|
30
|
+
return wrapper
|
|
31
|
+
|
|
32
|
+
return decorator
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
2
|
+
from fastapi import FastAPI, Request
|
|
3
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
4
|
+
from fastapi.responses import JSONResponse
|
|
5
|
+
|
|
6
|
+
from app.api.health import router as health_router
|
|
7
|
+
from app.core.config import settings
|
|
8
|
+
from app.core.errors import AppError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@asynccontextmanager
|
|
12
|
+
async def lifespan(app: FastAPI):
|
|
13
|
+
# Startup: connect to database with retry
|
|
14
|
+
from app.db.session import engine, connect_with_retry
|
|
15
|
+
|
|
16
|
+
await connect_with_retry(engine)
|
|
17
|
+
yield
|
|
18
|
+
# Shutdown: close database connections gracefully
|
|
19
|
+
await engine.dispose()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
app = FastAPI(
|
|
23
|
+
title="{{PROJECT_NAME_PASCAL}}",
|
|
24
|
+
version="0.1.0",
|
|
25
|
+
lifespan=lifespan,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# CORS
|
|
29
|
+
app.add_middleware(
|
|
30
|
+
CORSMiddleware,
|
|
31
|
+
allow_origins=settings.cors_origins,
|
|
32
|
+
allow_credentials=True,
|
|
33
|
+
allow_methods=["*"],
|
|
34
|
+
allow_headers=["*"],
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Routes
|
|
38
|
+
app.include_router(health_router, tags=["health"])
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Global exception handler — never leak stack traces
|
|
42
|
+
@app.exception_handler(AppError)
|
|
43
|
+
async def app_error_handler(request: Request, exc: AppError):
|
|
44
|
+
return JSONResponse(
|
|
45
|
+
status_code=exc.status_code,
|
|
46
|
+
content={"error": {"code": exc.code, "message": exc.message}},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@app.exception_handler(Exception)
|
|
51
|
+
async def global_exception_handler(request: Request, exc: Exception):
|
|
52
|
+
import logging
|
|
53
|
+
|
|
54
|
+
logging.exception("Unhandled exception")
|
|
55
|
+
return JSONResponse(
|
|
56
|
+
status_code=500,
|
|
57
|
+
content={"error": {"code": "INTERNAL_ERROR", "message": "An unexpected error occurred"}},
|
|
58
|
+
)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{{PROJECT_NAME}}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
requires-python = ">=3.11"
|
|
5
|
+
|
|
6
|
+
[tool.ruff]
|
|
7
|
+
target-version = "py311"
|
|
8
|
+
line-length = 100
|
|
9
|
+
|
|
10
|
+
[tool.ruff.lint]
|
|
11
|
+
select = ["E", "F", "I", "N", "W", "UP", "B", "SIM"]
|
|
12
|
+
|
|
13
|
+
[tool.pytest.ini_options]
|
|
14
|
+
asyncio_mode = "auto"
|
|
15
|
+
testpaths = ["tests"]
|
|
16
|
+
|
|
17
|
+
[tool.pyright]
|
|
18
|
+
pythonVersion = "3.11"
|
|
19
|
+
typeCheckingMode = "basic"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
fastapi>=0.115.0
|
|
2
|
+
uvicorn[standard]>=0.34.0
|
|
3
|
+
sqlalchemy[asyncio]>=2.0.38
|
|
4
|
+
asyncpg>=0.30.0
|
|
5
|
+
pydantic>=2.11.0
|
|
6
|
+
pydantic-settings>=2.8.0
|
|
7
|
+
alembic>=1.15.0
|
|
8
|
+
python-jose[cryptography]>=3.4.0
|
|
9
|
+
passlib[bcrypt]>=1.7.4
|
|
10
|
+
httpx>=0.28.0
|
|
11
|
+
pytest>=8.3.0
|
|
12
|
+
pytest-asyncio>=0.25.0
|
|
13
|
+
ruff>=0.11.0
|
|
14
|
+
pyright>=1.1.400
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Environment
|
|
5
|
+
.env
|
|
6
|
+
.env.local
|
|
7
|
+
.env.*.local
|
|
8
|
+
|
|
9
|
+
# Build output
|
|
10
|
+
dist/
|
|
11
|
+
.next/
|
|
12
|
+
out/
|
|
13
|
+
build/
|
|
14
|
+
|
|
15
|
+
# IDE
|
|
16
|
+
.vscode/
|
|
17
|
+
.idea/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
|
|
21
|
+
# OS
|
|
22
|
+
.DS_Store
|
|
23
|
+
Thumbs.db
|
|
24
|
+
|
|
25
|
+
# Testing
|
|
26
|
+
coverage/
|
|
27
|
+
test-results/
|
|
28
|
+
playwright-report/
|
|
29
|
+
{{EXTRA_IGNORES}}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
{{STACK_DESCRIPTION}}
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
{{SETUP_COMMANDS}}
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Available Scripts
|
|
12
|
+
|
|
13
|
+
{{AVAILABLE_SCRIPTS}}
|
|
14
|
+
|
|
15
|
+
## Project Structure
|
|
16
|
+
|
|
17
|
+
See `CLAUDE.md` for the complete project map and development conventions.
|
|
18
|
+
|
|
19
|
+
## Testing
|
|
20
|
+
|
|
21
|
+
See `docs/uat/` for acceptance testing templates and checklists.
|
|
22
|
+
|
|
23
|
+
## License
|
|
24
|
+
|
|
25
|
+
MIT
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
disallowedTools:
|
|
3
|
+
- Write
|
|
4
|
+
- Edit
|
|
5
|
+
- MultiEdit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Code Quality Reviewer
|
|
9
|
+
|
|
10
|
+
You are a code quality reviewer for {{PROJECT_NAME_PASCAL}}.
|
|
11
|
+
Stack: {{STACK_SUMMARY}}
|
|
12
|
+
|
|
13
|
+
## Review all changed files for:
|
|
14
|
+
|
|
15
|
+
### Code Quality
|
|
16
|
+
- [ ] Functions have single responsibility
|
|
17
|
+
- [ ] No unused imports or variables
|
|
18
|
+
- [ ] No hardcoded values that should be config/env
|
|
19
|
+
- [ ] Error cases handled with descriptive messages
|
|
20
|
+
- [ ] No console.log / print statements left in production code
|
|
21
|
+
|
|
22
|
+
### Patterns & Conventions
|
|
23
|
+
- [ ] Follows project conventions from CLAUDE.md
|
|
24
|
+
- [ ] Uses existing utilities instead of reimplementing
|
|
25
|
+
- [ ] Consistent naming conventions
|
|
26
|
+
- [ ] Proper type annotations (TypeScript/Python type hints)
|
|
27
|
+
|
|
28
|
+
### Performance
|
|
29
|
+
- [ ] No N+1 queries
|
|
30
|
+
- [ ] No unnecessary re-renders (React) or redundant DB calls
|
|
31
|
+
- [ ] Proper use of async/await
|
|
32
|
+
- [ ] No blocking operations in request handlers
|
|
33
|
+
|
|
34
|
+
### Maintainability
|
|
35
|
+
- [ ] Code is self-documenting (clear names, simple logic)
|
|
36
|
+
- [ ] Complex logic has explanatory comments
|
|
37
|
+
- [ ] No dead code or commented-out blocks
|
|
38
|
+
- [ ] Functions are reasonably sized (< 50 lines)
|
|
39
|
+
|
|
40
|
+
## Output
|
|
41
|
+
For each issue: **File** | **Line** | **Severity** (critical/warning/info) | **Issue** | **Fix**
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
disallowedTools:
|
|
3
|
+
- Write
|
|
4
|
+
- Edit
|
|
5
|
+
- MultiEdit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Production Readiness Reviewer
|
|
9
|
+
|
|
10
|
+
You verify that {{PROJECT_NAME_PASCAL}} is ready for production deployment.
|
|
11
|
+
Stack: {{STACK_SUMMARY}}
|
|
12
|
+
Read-only. Never modify code.
|
|
13
|
+
|
|
14
|
+
## Checklist
|
|
15
|
+
|
|
16
|
+
### Health & Observability
|
|
17
|
+
- [ ] Health check endpoint exists and returns structured response
|
|
18
|
+
- [ ] Deep health check verifies database connectivity
|
|
19
|
+
- [ ] Structured logging configured (not console.log/print)
|
|
20
|
+
- [ ] Error tracking integration ready
|
|
21
|
+
|
|
22
|
+
### Resilience
|
|
23
|
+
- [ ] Graceful shutdown handler registered
|
|
24
|
+
- [ ] Database connection retry with exponential backoff
|
|
25
|
+
- [ ] External service timeouts configured
|
|
26
|
+
- [ ] Rate limiting on public endpoints
|
|
27
|
+
- [ ] Circuit breaker for external dependencies (if applicable)
|
|
28
|
+
|
|
29
|
+
### Security
|
|
30
|
+
- [ ] No hardcoded secrets in codebase
|
|
31
|
+
- [ ] `.env.example` documents all required environment variables
|
|
32
|
+
- [ ] CORS configured for production origins
|
|
33
|
+
- [ ] Security headers set
|
|
34
|
+
- [ ] Authentication on all protected routes
|
|
35
|
+
|
|
36
|
+
### Data
|
|
37
|
+
- [ ] Database migrations are up to date
|
|
38
|
+
- [ ] No raw SQL (use ORM)
|
|
39
|
+
- [ ] Sensitive data encrypted at rest
|
|
40
|
+
- [ ] Backup strategy documented
|
|
41
|
+
|
|
42
|
+
### Deployment
|
|
43
|
+
- [ ] Dockerfile exists and builds successfully
|
|
44
|
+
- [ ] CI/CD pipeline configured
|
|
45
|
+
- [ ] Environment-specific configuration (dev/staging/prod)
|
|
46
|
+
- [ ] Build produces no warnings
|
|
47
|
+
|
|
48
|
+
### Testing
|
|
49
|
+
- [ ] Unit tests pass
|
|
50
|
+
- [ ] E2E tests pass
|
|
51
|
+
- [ ] Test coverage adequate for critical paths
|
|
52
|
+
- [ ] UAT scenarios documented
|
|
53
|
+
|
|
54
|
+
## Output
|
|
55
|
+
For each item: **Category** | **Check** | **Status** (PASS/FAIL/N/A) | **Details**
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
disallowedTools:
|
|
3
|
+
- Write
|
|
4
|
+
- Edit
|
|
5
|
+
- MultiEdit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Security Reviewer
|
|
9
|
+
|
|
10
|
+
You are a security reviewer for {{PROJECT_NAME_PASCAL}}.
|
|
11
|
+
Stack: {{STACK_SUMMARY}}
|
|
12
|
+
Read-only. Never modify code.
|
|
13
|
+
|
|
14
|
+
## Review all changed files for:
|
|
15
|
+
|
|
16
|
+
### Authentication & Authorization
|
|
17
|
+
- [ ] Protected routes check authentication
|
|
18
|
+
- [ ] Authorization checks before sensitive operations
|
|
19
|
+
- [ ] No hardcoded credentials or tokens
|
|
20
|
+
- [ ] Session/token expiration configured
|
|
21
|
+
|
|
22
|
+
### Input Validation
|
|
23
|
+
- [ ] All user input validated before use
|
|
24
|
+
- [ ] SQL injection prevention (parameterized queries/ORM)
|
|
25
|
+
- [ ] XSS prevention (proper escaping/sanitization)
|
|
26
|
+
- [ ] File upload validation (type, size, extension)
|
|
27
|
+
|
|
28
|
+
### Data Exposure
|
|
29
|
+
- [ ] No sensitive data in logs
|
|
30
|
+
- [ ] No secrets in error responses
|
|
31
|
+
- [ ] No stack traces exposed to clients
|
|
32
|
+
- [ ] API responses don't leak internal fields
|
|
33
|
+
|
|
34
|
+
### Configuration
|
|
35
|
+
- [ ] Secrets in environment variables, not code
|
|
36
|
+
- [ ] CORS properly configured (not wildcard in production)
|
|
37
|
+
- [ ] Security headers set
|
|
38
|
+
- [ ] HTTPS enforced
|
|
39
|
+
|
|
40
|
+
## Output
|
|
41
|
+
For each finding: **File** | **Line** | **Severity** (critical/high/medium/low) | **Vulnerability** | **Remediation**
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
disallowedTools:
|
|
3
|
+
- Write
|
|
4
|
+
- Edit
|
|
5
|
+
- MultiEdit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Spec Validator
|
|
9
|
+
|
|
10
|
+
You validate that the implementation matches the specification.
|
|
11
|
+
Read-only. Never modify code.
|
|
12
|
+
|
|
13
|
+
## Process
|
|
14
|
+
1. Read the spec file provided as argument
|
|
15
|
+
2. For each requirement in the spec:
|
|
16
|
+
a. Search the codebase for the implementation
|
|
17
|
+
b. Verify the implementation matches the requirement
|
|
18
|
+
c. Check for edge cases mentioned in the spec
|
|
19
|
+
|
|
20
|
+
## Output: Traceability Matrix
|
|
21
|
+
|
|
22
|
+
| Spec Requirement | Status | Implementation File | Notes |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| [requirement] | IMPLEMENTED / MISSING / PARTIAL | [file:line] | [details] |
|
|
25
|
+
|
|
26
|
+
## Summary
|
|
27
|
+
- Total requirements: X
|
|
28
|
+
- Implemented: X
|
|
29
|
+
- Partial: X
|
|
30
|
+
- Missing: X
|
|
31
|
+
- Coverage: X%
|
|
32
|
+
|
|
33
|
+
## Gaps
|
|
34
|
+
List any requirements that are MISSING or PARTIAL with details on what's missing.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
disallowedTools:
|
|
3
|
+
- Write
|
|
4
|
+
- Edit
|
|
5
|
+
- MultiEdit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# UAT Validator
|
|
9
|
+
|
|
10
|
+
You are a QA engineer validating UAT scenarios for {{PROJECT_NAME_PASCAL}}.
|
|
11
|
+
Read-only. Never modify code.
|
|
12
|
+
|
|
13
|
+
## Process
|
|
14
|
+
1. Read `docs/uat/UAT_TEMPLATE.md`
|
|
15
|
+
2. For each UAT scenario:
|
|
16
|
+
a. Verify the feature exists in the codebase
|
|
17
|
+
b. Check if there's a corresponding automated test
|
|
18
|
+
c. If automated test exists, verify it covers the scenario's steps
|
|
19
|
+
d. Flag gaps: scenarios without tests, tests without scenarios
|
|
20
|
+
|
|
21
|
+
## Output: Traceability Matrix
|
|
22
|
+
|
|
23
|
+
| UAT ID | Feature | Has Automated Test? | Test File | Coverage |
|
|
24
|
+
|---|---|---|---|---|
|
|
25
|
+
| UAT-001 | [feature] | YES/NO | [file:line] | FULL/PARTIAL/NONE |
|
|
26
|
+
|
|
27
|
+
## Summary
|
|
28
|
+
- Total scenarios: X
|
|
29
|
+
- Automated: X
|
|
30
|
+
- Manual only: X
|
|
31
|
+
- Coverage: X%
|
|
32
|
+
|
|
33
|
+
## Gaps
|
|
34
|
+
List scenarios that need automated tests, prioritized by UAT priority (P0 first).
|
|
35
|
+
|
|
36
|
+
## Recommendations
|
|
37
|
+
Suggest specific test implementations for uncovered P0 scenarios.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# {{PROJECT_NAME_PASCAL}}
|
|
2
|
+
|
|
3
|
+
## WHAT
|
|
4
|
+
- {{STACK_SUMMARY}}
|
|
5
|
+
{{DIR_MAP}}
|
|
6
|
+
|
|
7
|
+
## HOW
|
|
8
|
+
- Lint: `{{LINT_COMMAND}}`
|
|
9
|
+
- Type check: `{{TYPE_CHECK_COMMAND}}`
|
|
10
|
+
- Test: `{{TEST_COMMAND}}`
|
|
11
|
+
- Build: `{{BUILD_COMMAND}}`
|
|
12
|
+
- Dev: `{{DEV_COMMAND}}`
|
|
13
|
+
|
|
14
|
+
## RULES
|
|
15
|
+
- Never commit `.env` files or secrets
|
|
16
|
+
- Never modify migration files directly — generate new migrations instead
|
|
17
|
+
- All API responses use structured error format: `{ error: { code, message } }`
|
|
18
|
+
- Never leak stack traces to clients
|
|
19
|
+
- Health check endpoints must always be available
|
|
20
|
+
- Write tests for new features before marking them complete
|
|
21
|
+
|
|
22
|
+
{{STACK_SPECIFIC_RULES}}
|
|
23
|
+
|
|
24
|
+
## Skills
|
|
25
|
+
Framework-specific knowledge is in `.claude/skills/` — reference these for deep patterns:
|
|
26
|
+
{{SKILLS_LIST}}
|
|
27
|
+
|
|
28
|
+
## Completion Protocol
|
|
29
|
+
Before marking any task complete:
|
|
30
|
+
1. Run lint: `{{LINT_COMMAND}}`
|
|
31
|
+
2. Run type check: `{{TYPE_CHECK_COMMAND}}`
|
|
32
|
+
3. Run tests: `{{TEST_COMMAND}}`
|
|
33
|
+
4. Verify no `.env` files or secrets in changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
## FastAPI Conventions
|
|
2
|
+
- Pydantic v2 for all request/response schemas (use model_config, not class Config)
|
|
3
|
+
- SQLAlchemy 2.0 async style — use `select()` not `query()`, `async with session` not `session.query`
|
|
4
|
+
- Dependency injection via `Depends()` for DB sessions, auth, etc.
|
|
5
|
+
- All endpoints return Pydantic models — never return raw dicts
|
|
6
|
+
- Use `@asynccontextmanager` lifespan for startup/shutdown
|
|
7
|
+
- Database session via `get_db()` dependency (backend/app/db/session.py)
|
|
8
|
+
- Error responses use `AppError` classes (backend/app/core/errors.py)
|
|
9
|
+
- Use `with_retry` decorator for external service calls
|
|
10
|
+
- Alembic for migrations: `alembic revision --autogenerate -m "description"`
|
|
11
|
+
- Environment config via Pydantic Settings (backend/app/core/config.py)
|
|
12
|
+
- Ruff for linting, pyright for type checking
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
## Polyglot Full-Stack Conventions
|
|
2
|
+
- `frontend/` contains the Next.js application — follow Next.js conventions above
|
|
3
|
+
- `backend/` contains the FastAPI application — follow FastAPI conventions above
|
|
4
|
+
- Frontend and backend communicate via REST API only
|
|
5
|
+
- API base URL configured in frontend via `NEXT_PUBLIC_API_URL` environment variable
|
|
6
|
+
- Shared types: define API contracts in backend Pydantic schemas, mirror in frontend TypeScript types
|
|
7
|
+
- Never import between frontend and backend directories
|
|
8
|
+
- Docker Compose orchestrates both services + PostgreSQL
|
|
9
|
+
- Frontend runs on port 3000, backend on port 8000
|
|
10
|
+
- Database owned by backend — frontend never accesses DB directly
|
|
11
|
+
- Authentication: NextAuth on frontend, JWT validation on backend
|
|
12
|
+
- E2E tests in root `e2e/` directory test the full stack together
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## Next.js Conventions
|
|
2
|
+
- Use App Router (src/app/) — no pages/ directory
|
|
3
|
+
- Server Components by default; add 'use client' only when needed (event handlers, hooks, browser APIs)
|
|
4
|
+
- Use server actions for mutations, not API routes
|
|
5
|
+
- Colocate loading.tsx, error.tsx, not-found.tsx with page.tsx
|
|
6
|
+
- Use `@/` path alias for imports from src/
|
|
7
|
+
- Tailwind CSS for styling — use `cn()` from `@/lib/utils` for conditional classes
|
|
8
|
+
- Prisma client accessed via `@/lib/db` singleton (includes retry + graceful shutdown)
|
|
9
|
+
- Form validation with Zod schemas shared between client and server
|
|
10
|
+
- Images via `next/image`, links via `next/link`
|
|
11
|
+
- Environment variables: NEXT_PUBLIC_ prefix for client-side, plain for server-side
|