skrift — community skrift, smarter-dev, community, ide skills, Claude Code, Cursor, Windsurf

v1.0.0
GitHub

About this Skill

Perfect for Python Analysis Agents needing advanced async CMS capabilities with Litestar and SQLAlchemy. Help working with Skrift CMS codebases - a Python async CMS built on Litestar with WordPress-style conventions. Use for creating controllers, models, hooks, pages, and templates.

Smarter-Dev Smarter-Dev
[1]
[2]
Updated: 3/14/2026

Agent Capability Analysis

The skrift skill by Smarter-Dev is an open-source community AI agent skill for Claude Code and other IDE workflows, helping agents execute tasks with better context, repeatability, and domain-specific guidance.

Ideal Agent Persona

Perfect for Python Analysis Agents needing advanced async CMS capabilities with Litestar and SQLAlchemy.

Core Value

Empowers agents to build lightweight, scalable, and extensible content management systems using Litestar's async framework and SQLAlchemy's async database access, featuring WordPress-style template resolution and a hook/filter extensibility system.

Capabilities Granted for skrift

Developing custom CMS solutions with async Python
Building scalable website and Discord bot integrations for communities like Smarter Dev
Extending Skrift's core functionality with custom controllers, models, and services

! Prerequisites & Limits

  • Requires Python async environment
  • Dependent on Litestar and SQLAlchemy libraries
  • Needs configuration via app.yaml file
Labs Demo

Browser Sandbox Environment

⚡️ Ready to unleash?

Experience this Agent in a zero-setup browser environment powered by WebContainers. No installation required.

Boot Container Sandbox
SKILL.md
Readonly

Skrift CMS Development Guide

Skrift is a lightweight async Python CMS built on Litestar, featuring WordPress-style template resolution, a hook/filter extensibility system, and SQLAlchemy async database access.

Current Project State

Configuration: !cat app.yaml 2>/dev/null || echo "No app.yaml found"

Controllers: !ls skrift/controllers/*.py 2>/dev/null | head -10

Models: !ls skrift/db/models/*.py 2>/dev/null | head -10

Services: !ls skrift/db/services/*.py 2>/dev/null | head -10

Templates: !ls templates/*.html 2>/dev/null | head -10 || echo "No custom templates"

Quick Reference

Core Architecture

  • Framework: Litestar (async Python web framework)
  • Database: SQLAlchemy async with Advanced Alchemy
  • Templates: Jinja2 with WordPress-style template hierarchy + optional themes (see /skrift-theming)
  • Config: YAML (app.yaml) + environment variables (.env)
  • Auth: OAuth providers + role-based permissions (see /skrift-auth)
  • Forms: Pydantic-backed with CSRF (see /skrift-forms)
  • Hooks: WordPress-style extensibility (see /skrift-hooks)
  • Notifications: Real-time SSE with pluggable backends (see /skrift-notifications)
  • Observability: Optional Logfire tracing and structured logging (see /skrift-observability)
  • OAuth2 Server: Hub/spoke identity federation (see /skrift-oauth2)

AppDispatcher Pattern

                    ┌─────────────────────────────┐
                    │      AppDispatcher          │
                    │  (skrift/asgi.py)           │
                    └─────────────┬───────────────┘
                                  │
              ┌───────────────────┼───────────────────┐
              │                   │                   │
              ▼                   ▼                   ▼
     ┌────────────────┐   ┌────────────┐   ┌────────────────┐
     │   Setup App    │   │  /static   │   │   Main App     │
     │  (/setup/*)    │   │   Files    │   │  (everything)  │
     └────────────────┘   └────────────┘   └────────────────┘
  • setup_locked=False: /setup/* routes active, checks DB for setup completion
  • setup_locked=True: All traffic goes to main app, /setup/* returns 404
  • Main app is lazily created after setup completes (no restart needed)
  • Entry point: skrift.asgi:app (created by create_dispatcher())

Key Files

FilePurpose
skrift/asgi.pyAppDispatcher, app creation, middleware loading
skrift/config.pySettings management, YAML config loading
skrift/cli.pyCLI commands (serve, secret, db)
skrift/middleware/Security headers middleware
skrift/lib/hooks.pyWordPress-like hook/filter system
skrift/lib/template.pyTemplate resolution with fallbacks and theme support
skrift/lib/theme.pyTheme discovery, metadata parsing
skrift/db/base.pySQLAlchemy Base class (UUIDAuditBase)
skrift/forms/Form system (CSRF, validation, rendering)
skrift/auth/Guards, roles, permissions
skrift/lib/notifications.pyReal-time notification service (SSE)
skrift/lib/notification_backends.pyPluggable backends (InMemory, Redis, PgNotify)
skrift/lib/observability.pyLogfire observability facade (tracing, logging)
skrift/db/models/notification.pyStoredNotification model for DB-backed backends

Configuration System

.env (loaded early) → app.yaml (with $VAR interpolation) → Settings (Pydantic)

Environment-specific: app.yaml (production), app.dev.yaml (development), app.test.yaml (testing). Set via SKRIFT_ENV.

yaml
1db: 2 url: $DATABASE_URL 3 pool_size: 5 4 echo: false 5 schema: myschema # optional; PostgreSQL only — prefixes all tables 6 7auth: 8 redirect_base_url: "https://example.com" 9 providers: 10 google: 11 client_id: $GOOGLE_CLIENT_ID 12 client_secret: $GOOGLE_CLIENT_SECRET 13 scopes: ["openid", "email", "profile"] 14 15session: 16 cookie_domain: null 17 18theme: my-theme # default theme (overridden by admin UI) 19 20controllers: 21 - skrift.controllers.auth:AuthController 22 - skrift.controllers.web:WebController 23 24redis: 25 url: $REDIS_URL 26 prefix: "myapp" 27 28notifications: 29 backend: "" # empty = InMemoryBackend; or "module:ClassName" 30 31logfire: 32 enabled: true 33 service_name: my-site 34 console: true # prints spans to console 35 36security_headers: 37 content_security_policy: "default-src 'self'" 38 39middleware: 40 - myapp.middleware:create_logging_middleware

CLI Commands

bash
1skrift serve --reload --port 8080 2skrift serve --subdomain blog --port 8081 # serve single subdomain site 3skrift secret --write .env 4skrift db upgrade head 5skrift db downgrade -1 6skrift db revision -m "desc" --autogenerate

Database Layer

All models inherit from skrift.db.base.Base (provides id UUID, created_at, updated_at):

python
1from sqlalchemy import String, Text 2from sqlalchemy.orm import Mapped, mapped_column 3from skrift.db.base import Base 4 5class MyModel(Base): 6 __tablename__ = "my_models" 7 name: Mapped[str] = mapped_column(String(255), nullable=False) 8 description: Mapped[str | None] = mapped_column(Text, nullable=True)

Core Models

ModelTablePurpose
UserusersUser accounts
OAuthAccountoauth_accountsLinked OAuth providers (access_token, refresh_token)
RolerolesPermission roles
PagepagesContent pages
PageRevisionpage_revisionsContent history
SettingsettingsKey-value site settings
StoredNotificationstored_notificationsPersistent notifications with mode column (Redis/PgNotify backends)

Sessions injected via db_session: AsyncSession parameter in handlers.

Content Negotiation

Page views support Accept: text/markdown — returns raw page.content instead of rendered HTML. Works for all page types (WebController and page type factory routes).

Creating a Controller

python
1from litestar import Controller, get, post 2from litestar.response import Template as TemplateResponse 3from sqlalchemy.ext.asyncio import AsyncSession 4 5class MyController(Controller): 6 path = "/my-path" 7 8 @get("/") 9 async def list_items(self, db_session: AsyncSession) -> TemplateResponse: 10 items = await item_service.list_items(db_session) 11 return TemplateResponse("items/list.html", context={"items": items})

Register in app.yaml:

yaml
1controllers: 2 - myapp.controllers:MyController

Creating a Service

python
1from sqlalchemy import select 2from sqlalchemy.ext.asyncio import AsyncSession 3 4async def get_by_id(db_session: AsyncSession, item_id: UUID) -> MyModel | None: 5 result = await db_session.execute(select(MyModel).where(MyModel.id == item_id)) 6 return result.scalar_one_or_none() 7 8async def create_item(db_session: AsyncSession, name: str) -> MyModel: 9 item = MyModel(name=name) 10 db_session.add(item) 11 await db_session.commit() 12 await db_session.refresh(item) 13 return item

Template Resolution

WordPress-style hierarchy with fallbacks and optional theme support:

python
1from skrift.lib.template import Template 2 3# Tries: page-about.html -> page.html 4template = Template("page", "about") 5 6# Tries: post-news-2024.html -> post-news.html -> post.html 7template = Template("post", "news", "2024") 8 9# Without theme 10return template.render(TEMPLATE_DIR, page=page) 11 12# With theme (searches theme dirs first) 13return template.render(TEMPLATE_DIR, theme_name="my-theme", page=page)

Search order (with active theme):

  1. themes/<active>/templates/ (active theme)
  2. ./templates/ (project root — user overrides)
  3. skrift/templates/ (package — defaults)

Template globals: now(), site_name(), site_tagline(), site_copyright_holder(), site_copyright_start_year(), active_theme(), themes_available(). Filter: markdown.

For full theming details, see /skrift-theming.

Middleware

python
1from litestar.middleware import AbstractMiddleware 2 3class LoggingMiddleware(AbstractMiddleware): 4 async def __call__(self, scope, receive, send): 5 if scope["type"] == "http": 6 print(f"Request: {scope['method']} {scope['path']}") 7 await self.app(scope, receive, send) 8 9def create_logging_middleware(app): 10 return LoggingMiddleware(app=app)

Register in app.yaml:

yaml
1middleware: 2 - myapp.middleware:create_logging_middleware 3 - factory: myapp.middleware:create_rate_limit 4 kwargs: 5 requests_per_minute: 100

Security

skrift/middleware/security.py — ASGI middleware injecting CSP, HSTS, X-Frame-Options, etc. Configured via SecurityHeadersConfig. Pre-encoded at creation time. HSTS excluded in debug mode.

Static files: /static/ with same priority as templates (project root, then package).

Error Handling

Custom exception handlers in skrift/lib/exceptions.py. Templates: error.html, error-404.html, error-500.html.

Testing

python
1from litestar.testing import TestClient 2 3async def test_list_items(client, db_session): 4 item = await item_service.create(db_session, name="Test") 5 response = client.get("/items") 6 assert response.status_code == 200 7 assert "Test" in response.text

Related Skills

For deep-dive guidance on specific subsystems:

  • /skrift-hooks — Hook/filter extensibility, custom hook points, built-in hooks
  • /skrift-forms — Form system, CSRF, field customization, template rendering
  • /skrift-auth — OAuth flow, guard system, roles, permissions
  • /skrift-theming — Theme discovery, template/static resolution, per-request switching, RESOLVE_THEME hook
  • /skrift-notifications — SSE protocol, pluggable backends, group keys, dismiss patterns
  • /skrift-observability — Logfire integration, structured tracing, instrumentation
  • /skrift-oauth2 — OAuth2 Authorization Server, hub/spoke identity federation, Skrift provider

FAQ & Installation Steps

These questions and steps mirror the structured data on this page for better search understanding.

? Frequently Asked Questions

What is skrift?

Perfect for Python Analysis Agents needing advanced async CMS capabilities with Litestar and SQLAlchemy. Help working with Skrift CMS codebases - a Python async CMS built on Litestar with WordPress-style conventions. Use for creating controllers, models, hooks, pages, and templates.

How do I install skrift?

Run the command: npx killer-skills add Smarter-Dev/smarter-dev/skrift. It works with Cursor, Windsurf, VS Code, Claude Code, and 19+ other IDEs.

What are the use cases for skrift?

Key use cases include: Developing custom CMS solutions with async Python, Building scalable website and Discord bot integrations for communities like Smarter Dev, Extending Skrift's core functionality with custom controllers, models, and services.

Which IDEs are compatible with skrift?

This skill is compatible with Cursor, Windsurf, VS Code, Trae, Claude Code, OpenClaw, Aider, Codex, OpenCode, Goose, Cline, Roo Code, Kiro, Augment Code, Continue, GitHub Copilot, Sourcegraph Cody, and Amazon Q Developer. Use the Killer-Skills CLI for universal one-command installation.

Are there any limitations for skrift?

Requires Python async environment. Dependent on Litestar and SQLAlchemy libraries. Needs configuration via app.yaml file.

How To Install

  1. 1. Open your terminal

    Open the terminal or command line in your project directory.

  2. 2. Run the install command

    Run: npx killer-skills add Smarter-Dev/smarter-dev/skrift. The CLI will automatically detect your IDE or AI agent and configure the skill.

  3. 3. Start using the skill

    The skill is now active. Your AI agent can use skrift immediately in the current project.

Related Skills

Looking for an alternative to skrift or another community skill for your workflow? Explore these related open-source skills.

View All

openclaw-release-maintainer

Logo of openclaw
openclaw

Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

333.8k
0
AI

widget-generator

Logo of f
f

Generate customizable widget plugins for the prompts.chat feed system

149.6k
0
AI

flags

Logo of vercel
vercel

The React Framework

138.4k
0
Browser

pr-review

Logo of pytorch
pytorch

Tensors and Dynamic neural networks in Python with strong GPU acceleration

98.6k
0
Developer