SkillAgentSearch skills...

CacaoDocs

CacaoDocs is a lightweight Python package that effortlessly extracts API documentation directly from your code's docstrings

Install / Use

/learn @jhd3197/CacaoDocs

README

CacaoDocs

A documentation generator for Python projects, powered by Cacao.

CacaoDocs scans your Python source code, parses docstrings, and generates an interactive documentation app with WebSocket-driven reactivity. No static HTML — your docs are a live app.

Links

Installation

pip install cacaodocs

Quick Start

# Initialize config (optional)
cacaodocs init

# Build docs from your source directory
cacaodocs build ./src -o ./docs

# Serve the generated docs
cacaodocs serve ./docs

How Docstrings Work

CacaoDocs uses Google-style docstrings with one addition: a Type: directive that tells the parser what kind of thing you're documenting. If you don't specify a type, it defaults to function.

Here's the idea — you write normal docstrings, and CacaoDocs figures out what sections to parse based on the type:

| Type | What it's for | How it's detected | |------|--------------|-------------------| | function | Regular functions and methods | Default — no directive needed | | api | REST API endpoints | Auto-detected from decorators like @app.get, @router.post, @app.route | | class | Python classes | Detected from class definitions | | page | Markdown documentation pages | .md files in your source directory | | config | App settings and env vars | Set with Type: config | | event | Webhooks, signals, pub/sub | Set with Type: event | | Custom | Anything you define | Set with Type: your_type_name |

Function (default)

The most common type. Just write standard Google-style docstrings — no Type: directive needed:

def hash_password(password: str, salt: str = None) -> str:
    """Hash a password using bcrypt.

    Args:
        password (str): The plaintext password.
        salt (str): Optional salt. Generated if not provided.

    Returns:
        str: The hashed password string.

    Raises:
        ValueError: If password is empty or too short.

    Examples:
        >>> hash_password("mysecretpass")
        '$2b$12$...'
    """

Available sections: Args, Returns, Raises, Examples, Notes

API Endpoints

API endpoints are auto-detected from Flask, FastAPI, and Django REST decorators — you don't need to add Type: api. CacaoDocs sees @app.get(...) and knows it's an API endpoint, extracting the HTTP method and route path for you.

@app.get("/users/{user_id}")
def get_user(user_id: int):
    """Get a user by ID.

    Path Params:
        user_id (int): The user's unique identifier.

    Query Params:
        include (str): Comma-separated fields to include.

    Response (200):
        id (int): The user ID.
        name (str): Full name.
        email (str): Email address.

    Response (404):
        detail (str): User not found.
    """

Auto-detected decorators:

  • FastAPI: @app.get, @app.post, @router.put, @router.delete, @router.patch
  • Flask: @app.route, @blueprint.route
  • Django REST: @api_view

Available sections: Path Params, Query Params, Request Body, Headers, Response (NNN)

Config

For documenting application settings and environment variables. Use Type: config to enable config-specific parsing:

def load_settings():
    """Application settings.

    Type: config

    Fields:
        DEBUG (bool, default=False): Enable debug mode.
        SECRET_KEY (str, required, env=APP_SECRET): Secret key for signing.
        DATABASE_URL (str, required, env=DATABASE_URL): PostgreSQL connection string.
        PORT (int, default=8000): Server port.
    """

Each field supports modifiers inside the parentheses:

  • typestr, int, bool, etc.
  • default=value — Default value
  • required — Marks the field as required
  • env=VAR_NAME — Maps to an environment variable

Event

For documenting webhooks, signals, and event-driven patterns. Use Type: event:

def on_user_signup(data: dict):
    """User signup event.

    Type: event

    Trigger: When a new user completes registration.

    Payload:
        user_id (int): The new user ID.
        email (str): The user's email.
        plan (str): Selected subscription plan.
    """

Available sections: Trigger (inline directive), Payload

Custom Types

You can define your own doc types in cacao.yaml. Each custom type gets its own sections and display settings:

doc_types:
  cli_command:
    label: "CLI Command"
    icon: "terminal"
    sections:
      - name: "Usage"
        format: code
      - name: "Options"
        format: args
      - name: "Flags"
        format: args

  database_model:
    label: "Model"
    icon: "database"
    sections:
      - name: "Fields"
        format: args
      - name: "Indexes"
      - name: "Relations"

Then use them with the Type: directive:

def deploy():
    """Deploy the application.

    Type: cli_command

    Usage:
        deploy --env production --tag v1.2.3

    Options:
        env (str): Target environment.
        tag (str): Version tag to deploy.
    """

Configuration

Create a cacao.yaml in your project root:

title: "My Project"
description: "API Documentation"
version: "1.0.0"
theme: "dark"
github_url: "https://github.com/username/repo"

exclude_patterns:
  - "__pycache__"
  - ".venv"
  - "node_modules"

Deploy to GitHub Pages

CacaoDocs can export a static version of your docs and deploy them to GitHub Pages. Add this workflow to .github/workflows/docs.yml:

name: Deploy Docs to GitHub Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: true

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: |
          pip install cacaodocs
          pip install "cacao>=2.1.0"

      - name: Build docs
        run: cacaodocs build ./src -o ./_build

      - name: Export static site
        run: cacaodocs export ./_build -o ./dist --base-path /${{ github.event.repository.name }}

      - name: Upload Pages artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: dist

      - name: Deploy to GitHub Pages
        uses: actions/deploy-pages@v4

Setup steps:

  1. Go to your repo's Settings > Pages
  2. Set Source to GitHub Actions
  3. Push the workflow file to main — it will build and deploy automatically

Note: Change ./src in the build step to wherever your Python source code lives. The --base-path flag is needed so links work correctly under https://username.github.io/repo-name/.

The @doc() Decorator

Instead of (or in addition to) docstrings, you can use the @doc() decorator to define documentation as structured data. This avoids fragile docstring parsing entirely — CacaoDocs reads the keyword arguments directly from the AST.

from cacaodocs import doc

@doc(
    description="Fetch a user by ID.",
    args={"user_id": {"type": "int", "description": "The user's unique identifier"}},
    returns={"type": "User", "description": "The matching user"},
    raises={"NotFoundError": "If user doesn't exist"},
    deprecated="2.0",
    category="Users",
    version="1.0",
)
def get_user(user_id: int) -> User:
    ...

The decorator is a runtime no-op — it just returns your function unchanged. Zero overhead. CacaoDocs reads the keyword arguments at scan time via AST, so there's nothing to parse or misparse.

Decorator vs Docstring

You can use either approach, or both. When both exist, decorator values win.

# Decorator only — no docstring needed
@doc(description="Add two numbers.", args={"a": "First", "b": "Second"}, returns="The sum")
def add(a, b):
    return a + b

# Supplement mode — docstring provides the basics, decorator adds metadata
@doc(category="Utils", version="3.0", deprecated=True)
def helper():
    """Reticulate the splines.

    Args:
        count (int): Number of splines.
    """
    ...

Supported kwargs

General:

| Kwarg | Type | Description | |-------|------|-------------| | description | str | Summary text | | doc_type | str | Override type: "api", "config", "event", etc. | | deprecated | bool \| str | True or a since-version string like "2.0" | | category | str | Sidebar group name | | version | str | Version when added | | hidden | bool | Exclude from generated docs |

Function sections:

| Kwarg | Simple syntax | Full syntax | |-------|--------------|-------------| | args | {"name": "desc"} | {"name": {"type": "str", "description": "...", "default": "x"}} | | returns | "description" | {"type": "User", "description": "..."} | | raises | {"ValueError": "when bad"} | — | | examples | ["example()"] | — | | notes | ["Note text"] | — | | attributes | Same as args | Same as args |

API sections:

| Kwarg | Type | Description | |-------|------|-------------| | method | str | HTTP method ("GET", "POST", etc.) | | path | str | Route path ("/users/{id}") | | path_params | dict | Same format as args | | query_params | dict | Same format as args | | request_body | dict | Same format as args | | responses | dict | {200: "OK"} or {200: {"description": "...", "fields": {...}}} | | headers | dict | {"Auth": "desc"} or {"Auth": {"description": "...", "required": True}} |

Event sections:

| Kwarg | Type | Description | |-------|------|--------

Related Skills

View on GitHub
GitHub Stars9
CategoryDevelopment
Updated12d ago
Forks0

Languages

Python

Security Score

90/100

Audited on Mar 8, 2026

No findings