Communitygithub.com

django-security

Django 安全最佳實踐:身份驗證、授權、CSRF 防護、SQL 注入預防、XSS 防範及安全部署設定。適用於審查 Django 身份驗證、授權、輸入處理或部署設定時。

django-security 是什麼?

這是一個專門為 Django 應用程式設計的安全性指導技能,旨在幫助開發人員防範常見的網路漏洞。它涵蓋了從開發到部署的完整安全生命週期,包括身份驗證與授權機制(如自訂使用者模型、基於角色的存取控制 RBAC)、CSRF 防護、SQL 注入預防(透過 Django ORM 與參數化查詢)、XSS 防範(模板轉義與安全字串處理)以及安全的檔案上傳驗證。此外,技能還提供了生產環境的安全設定範例,如強制 HTTPS、HSTS 標頭、安全的 Cookie 設定與速率限制,確保 API 和網頁應用程式在部署時具備堅實的安全基礎。此技能與所有支援 SKILL.md 規範的 AI 代理平台相容,如 Claude Code、Cursor 和 Codex,開發者可以將其作為安全審查清單或自動化安全建議的來源。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/django-security

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Django 安全最佳實踐

全面的 Django 應用程式安全指南,協助防範常見漏洞。

啟用時機

  • 設定 Django 身份驗證與授權
  • 實作使用者權限與角色
  • 配置生產環境安全設定
  • 審查 Django 應用程式的安全性問題
  • 將 Django 應用程式部署到生產環境

核心安全設定

生產環境設定配置

# settings/production.py
import os

DEBUG = False  # 關鍵:在生產環境中永不設定為 True

ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')

# 安全標頭
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000  # 1 年
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'

# HTTPS 與 Cookie
SESSION_COOKIE_HTTPONLY = True
CSRF_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_SAMESITE = 'Lax'

# 金鑰(必須透過環境變數設定)
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
if not SECRET_KEY:
    raise ImproperlyConfigured('需要設定 DJANGO_SECRET_KEY 環境變數')

# 密碼驗證
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        'OPTIONS': {
            'min_length': 12,
        }
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

身份驗證

自訂使用者模型

# apps/users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    """自訂使用者模型以提升安全性。"""

    email = models.EmailField(unique=True)
    phone = models.CharField(max_length=20, blank=True)

    USERNAME_FIELD = 'email'  # 使用電子郵件作為使用者名稱
    REQUIRED_FIELDS = ['username']

    class Meta:
        db_table = 'users'
        verbose_name = '使用者'
        verbose_name_plural = '使用者'

    def __str__(self):
        return self.email

# settings/base.py
AUTH_USER_MODEL = 'users.User'

密碼雜湊

# Django 預設使用 PBKDF2。若要增強安全性:
PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

工作階段管理

# 工作階段配置
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 或 'db'
SESSION_CACHE_ALIAS = 'default'
SESSION_COOKIE_AGE = 3600 * 24 * 7  # 1 週
SESSION_SAVE_EVERY_REQUEST = False
SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 更好的使用者體驗,但安全性較低

授權

權限

# models.py
from django.db import models
from django.contrib.auth.models import Permission

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    class Meta:
        permissions = [
            ('can_publish', '可以發布文章'),
            ('can_edit_others', '可以編輯他人的文章'),
        ]

    def user_can_edit(self, user):
        """檢查使用者是否可以編輯此文章。"""
        return self.author == user or user.has_perm('app.can_edit_others')

# views.py
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import UpdateView

class PostUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
    model = Post
    permission_required = 'app.can_edit_others'
    raise_exception = True  # 回傳 403 而非重新導向

    def get_queryset(self):
        """僅允許使用者編輯自己的文章。"""
        return Post.objects.filter(author=self.request.user)

自訂權限

# permissions.py
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """僅允許擁有者編輯物件。"""

    def has_object_permission(self, request, view, obj):
        # 任何請求皆允許讀取權限
        if request.method in permissions.SAFE_METHODS:
            return True

        # 僅允許擁有者寫入權限
        return obj.author == request.user

class IsAdminOrReadOnly(permissions.BasePermission):
    """允許管理員執行所有操作,其他人僅能讀取。"""

    def has_permission(self, request, view):
        if request.method in permissions.SAFE_METHODS:
            return True
        return request.user and request.user.is_staff

class IsVerifiedUser(permissions.BasePermission):
    """僅允許已驗證的使用者。"""

    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated and request.user.is_verified

基於角色的存取控制 (RBAC)

# models.py
from django.contrib.auth.models import AbstractUser, Group

class User(AbstractUser):
    ROLE_CHOICES = [
        ('admin', '管理員'),
        ('moderator', '版主'),
        ('user', '一般使用者'),
    ]
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='user')

    def is_admin(self):
        return self.role == 'admin' or self.is_superuser

    def is_moderator(self):
        return self.role in ['admin', 'moderator']

# Mixins
class AdminRequiredMixin:
    """要求管理員角色的 Mixin。"""

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated or not request.user.is_admin():
            from django.core.exceptions import PermissionDenied
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)

SQL 注入預防

Django ORM 保護

# 良好:Django ORM 自動跳脫參數
def get_user(username):
    return User.objects.get(username=username)  # 安全

# 良好:使用參數搭配 raw()
def search_users(query):
    return User.objects.raw('SELECT * FROM users WHERE username = %s', [query])

# 不良:切勿直接拼接使用者輸入
def get_user_bad(username):
    return User.objects.raw(f'SELECT * FROM users WHERE username = {username}')  # 有漏洞!

# 良好:使用 filter 搭配適當跳脫
def get_users_by_email(email):
    return User.objects.filter(email__iexact=email)  # 安全

# 良好:使用 Q 物件處理複雜查詢
from django.db.models import Q
def search_users_complex(query):
    return User.objects.filter(
        Q(username__icontains=query) |
        Q(email__icontains=query)
    )  # 安全

使用 raw() 的額外安全性

# 如果必須使用原始 SQL,請務必使用參數
User.objects.raw(
    'SELECT * FROM users WHERE email = %s AND status = %s',
    [user_input_email, status]
)

XSS 防範

模板轉義

{# Django 預設會自動跳脫變數 - 安全 #}
{{ user_input }}  {# 已跳脫的 HTML #}

{# 僅對受信任的內容明確標記為安全 #}
{{ trusted_html|safe }}  {# 未跳脫 #}

{# 使用模板過濾器處理安全的 HTML #}
{{ user_input|escape }}  {# 與預設相同 #}
{{ user_input|striptags }}  {# 移除所有 HTML 標籤 #}

{# JavaScript 跳脫 #}
<script>
    var username = {{ username|escapejs }};
</script>

安全字串處理

from django.utils.safestring import mark_safe
from django.utils.html import escape

# 不良:切勿在未跳脫的情況下將使用者輸入標記為安全
def render_bad(user_input):
    return mark_safe(user_input)  # 有漏洞!

# 良好:先跳脫,再標記為安全
def render_good(user_input):
    return mark_safe(escape(user_input))

# 良好:使用 format_html 處理含變數的 HTML
from django.utils.html import format_html

def greet_user(username):
    return format_html('<span class="user">{}</span>', escape(username))

HTTP 標頭

# settings.py
SECURE_CONTENT_TYPE_NOSNIFF = True  # 防止 MIME 嗅探
SECURE_BROWSER_XSS_FILTER = True  # 啟用 XSS 過濾器
X_FRAME_OPTIONS = 'DENY'  # 防止點擊劫持

# 自訂中介軟體
from django.conf import settings

class SecurityHeaderMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response['X-Content-Type-Options'] = 'nosniff'
        response['X-Frame-Options'] = 'DENY'
        response['X-XSS-Protection'] = '1; mode=block'
        response['Content-Security-Policy'] = "default-src 'self'"
        return response

CSRF 防護

預設 CSRF 防護

# settings.py - CSRF 預設為啟用
CSRF_COOKIE_SECURE = True  # 僅透過 HTTPS 發送
CSRF_COOKIE_HTTPONLY = True  # 防止 JavaScript 存取
CSRF_COOKIE_SAMESITE = 'Lax'  # 在某些情況下防止 CSRF
CSRF_TRUSTED_ORIGINS = ['https://example.com']  # 信任的網域

# 模板使用
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">提交</button>
</form>

# AJAX 請求
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

fetch('/api/endpoint/', {
    method: 'POST',
    headers: {
        'X-CSRFToken': getCookie('csrftoken'),
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(data)
});

豁免檢視(請謹慎使用)

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt  # 僅在絕對必要時使用!
def webhook_view(request):
    # 來自外部服務的 Webhook
    pass

檔案上傳安全性

檔案驗證

import os
import magic  # pip install python-magic
from django.core.exceptions import ValidationError

ALLOWED_MIMES = {
    'image/jpeg', 'image/png', 'image/gif', 'application/pdf',
}

MIME_TO_EXTENSIONS = {
    'image/jpeg': {'.jpg', '.jpeg'},
    'image/png': {'.png'},
    'image/gif': {'.gif'},
    'application/pdf': {'.pdf'},
}

def validate_file_type(value):
    """使用魔術位元組驗證檔案類型,並交叉檢查副檔名。"""
    mime = magic.from_buffer(value.read(2048), mime=True)
    value.seek(0)

    if mime not in ALLOWED_MIMES:
        raise ValidationError('不支援的檔案類型。')

    ext = os.path.splitext(value.name)[1].lower()
    if ext not in MIME_TO_EXTENSIONS.get(mime, set()):
        raise ValidationError('檔案副檔名與檔案內容不符。')

def validate_file_size(value):
    """驗證檔案大小(最大 5MB)。"""
    if value.size > 5 * 1024 * 1024:
        raise ValidationError('檔案過大。最大大小為 5MB。')

# models.py
class Document(models.Model):
    file = models.FileField(
        upload_to='documents/',
        validators=[validate_file_type, validate_file_size]
    )

對於難以安裝 libmagic 的環境(例如精簡容器),可使用純 Python 的 filetype 套件作為替代方案:

import os
from django.core.exceptions import ValidationError

import filetype  # pip install filetype

ALLOWED_MIMES = {
    'image/jpeg', 'image/png', 'image/gif', 'application/pdf',
}

MIME_TO_EXTENSIONS = {
    'image/jpeg': {'.jpg', '.jpeg'},
    'image/png': {'.png'},
    'image/gif': {'.gif'},
    'application/pdf': {'.pdf'},
}

def validate_file_type(value):
    """使用魔術位元組驗證檔案類型。"""
    kind = filetype.guess(value.read(2048))
    value.seek(0)

    if kind is None or kind.mime not in ALLOWED_MIMES:
        raise ValidationError('不支援的檔案類型。')

    ext = os.path.splitext(value.name)[1].lower()
    if ext not in MIME_TO_EXTENSIONS.get(kind.mime, set()):
        raise ValidationError('檔案副檔名與檔案內容不符。')

安全的檔案儲存

# settings.py
MEDIA_ROOT = '/var/www/media/'
MEDIA_URL = '/media/'

# 在生產環境中使用獨立的網域來提供媒體檔案
MEDIA_DOMAIN = 'https://media.example.com'

# 不要直接提供使用者上傳的檔案
# 使用 whitenoise 或 CDN 來處理靜態檔案
# 使用獨立的伺服器或 S3 來處理媒體檔案

API 安全性

速率限制

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day',
        'upload': '10/hour',
    }
}

# 自訂限制器
from rest_framework.throttling import UserRateThrottle

class BurstRateThrottle(UserRateThrottle):
    scope = 'burst'
    rate = '60/min'

class SustainedRateThrottle(UserRateThrottle):
    scope = 'sustained'
    rate = '1000/day'

API 身份驗證

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

# views.py
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def api_endpoint(request):
    # 您的 API 邏輯
    pass

安全的序列化器

# serializers.py
from rest_framework import serializers
from .models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'bio', 'avatar']
        read_only_fields = ['id']

class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, min_length=12)
    password2 = serializers.CharField(write_only=True, min_length=12)

    class Meta:
        model = User
        fields = ['username', 'email', 'password', 'password2']

    def validate_email(self, value):
        """驗證電子郵件是否唯一。"""
        if User.objects.filter(email=value).exists():
            raise serializers.ValidationError('此電子郵件已被使用。')
        return value

    def validate(self, data):
        if data['password'] != data['password2']:
            raise serializers.ValidationError('密碼不符。')
        return data

    def create(self, validated_data):
        validated_data.pop('password2')
        user = User.objects.create_user(**validated_data)
        return user

稽核與日誌

啟用日誌

# settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'file': {
            'level': 'ERROR',
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/errors.log',
            'formatter': 'verbose',
        },
        'security_file': {
            'level': 'WARNING',
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/security.log',
            'formatter': 'verbose',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'ERROR',
            'propagate': True,
        },
        'django.security': {
            'handlers': ['security_file'],
            'level': 'WARNING',
            'propagate': False,
        },
    },
}

記錄安全事件

import logging

logger = logging.getLogger('django.security')

def login_attempt(request, user, successful):
    if successful:
        logger.info(f'使用者 {user.email} 從 IP {request.META.get("REMOTE_ADDR")} 成功登入')
    else:
        logger.warning(f'登入嘗試失敗 - 使用者 {user.email} 從 IP {request.META.get("REMOTE_ADDR")}')

def suspicious_activity(request, description):
    logger.critical(f'可疑活動 - IP: {request.META.get("REMOTE_ADDR")}, 說明: {description}')

安全部署

Nginx 配置

# /etc/nginx/sites-available/django

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    # 現代 SSL 配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # 安全標頭
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location /static/ {
        alias /var/www/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    location /media/ {
        alias /var/www/media/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# 將 HTTP 重新導向到 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

Docker 安全

# Dockerfile
FROM python:3.11-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# 使用非 root 使用者
RUN useradd -m -u 1000 appuser

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY --chown=appuser:appuser . .

USER appuser

EXPOSE 8000

CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4", "--timeout", "120"]

環境變數

# .env.production
DJANGO_SECRET_KEY='your-secure-secret-key-here'
DATABASE_URL='postgresql://user:password@localhost:5432/dbname'
ALLOWED_HOSTS='example.com,www.example.com'
REDIS_URL='redis://localhost:6379/0'
EMAIL_HOST_USER='[email protected]'
EMAIL_HOST_PASSWORD='your-email-password'

Gunicorn 配置

# gunicorn.conf.py
import multiprocessing

bind = '0.0.0.0:8000'
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
timeout = 120
keepalive = 5
accesslog = '/var/log/gunicorn/access.log'
errorlog = '/var/log/gunicorn/error.log'
loglevel = 'warning'

測試安全性

安全測試命令

# 執行 Django 安全檢查
python manage.py check --deploy

# 使用 Bandit 進行靜態分析
pip install bandit
bandit -r . --exclude tests,venv

# 使用 Safety 檢查相依性漏洞
pip install safety
safety check

# 使用 SQLMap 測試 SQL 注入(僅在授權的測試環境中)
sqlmap -u "https://example.com/search?q=test" --batch

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

相關技能