Skip to Content

Code Guidelines

TL;DR

Skills follow strict typing (Python 3.10+ syntax, never Any), use Pydantic models for structured data, keep functions under 20 lines, catch specific exceptions, and use ruff + mypy for linting and type checking.

Return values

All commands return a dict. The framework wraps it:

# Success return {"domain": "github.com", "days_left": 42} # β†’ {"ok": true, "domain": "github.com", "days_left": 42} # Error (raise exception) raise ValueError("Invalid domain") # β†’ {"ok": false, "error": "Invalid domain"}

Never return raw strings or print to stdout. Always return structured data.

Type annotations

Complete type annotations are required. Use Python 3.10+ syntax:

# Good def check(domain: str, timeout: int = 10) -> dict[str, str | int]: ... # Bad β€” missing annotations def check(domain, timeout=10): ... # Bad β€” using Any def check(domain: str) -> Any: ...

Pydantic models

Use BaseModel for structured data instead of raw dicts:

from pydantic import BaseModel class CertInfo(BaseModel): domain: str issuer: str days_left: int valid: bool @skill.command async def check(domain: str = Arg(help="Domain")) -> dict: info = CertInfo( domain=domain, issuer="Let's Encrypt", days_left=42, valid=True, ) return info.model_dump()

Function style

  • Keep functions under 20 lines
  • Use guard clauses over deep nesting
  • Prefix private helpers with _
  • Constants: SCREAMING_SNAKE_CASE
  • No mutable default arguments
  • No global mutable state
# Good β€” guard clause async def check(domain: str) -> dict: if not domain: raise ValueError("Domain required") result = await _fetch_cert(domain) return result.model_dump() # Bad β€” deep nesting async def check(domain: str) -> dict: if domain: try: result = await _fetch_cert(domain) if result: return result.model_dump() else: raise ValueError("No result") except Exception: raise else: raise ValueError("Domain required")

Error handling

Catch specific exceptions, never bare except::

# Good try: resp = await client.get(url) except httpx.TimeoutException: raise ValueError(f"Timeout connecting to {url}") except httpx.ConnectError: raise ValueError(f"Cannot connect to {url}") # Bad try: resp = await client.get(url) except: raise ValueError("Something went wrong")

Dependencies

Keep dependencies minimal. Prefer stdlib when possible. Always pin minimum versions:

dependencies = [ "cmdop-skill", "httpx>=0.27", "pydantic>=2.0", ]

Linting and type checking

[tool.ruff] line-length = 100 [tool.mypy] strict = true

Run:

make lint # ruff check make format # ruff format

Best practices

  • Confirm destructive actions β€” always ask before deleting or modifying
  • Idempotent operations β€” running twice should produce the same result
  • Progressive disclosure β€” simple output by default, --verbose for details
  • Errors as JSON β€” return structured errors, not stack traces
  • Audit trail β€” log significant actions for debugging
Last updated on