Skip to content
Development
Skill

/python-design

Python design patterns for CLI scripts and utilities — type-first development, deep modules, complexity management, and red flags. Use when reading, writing, reviewing, or refactoring Python files, especially in .trellis/scripts/ or any CLI/scripting context. Also activate when

From plugin
trellis
15k18 skills5 agents6 commands

Claude Code

$ npx -y skills add mindfold-ai/trellis --skill python-design --agent claude-code

SKILL.md

python-design.SKILL.md
name: python-design
description: "Python design patterns for CLI scripts and utilities — type-first development, deep modules, complexity management, and red flags. Use when reading, writing, reviewing, or refactoring Python files, especially in .trellis/scripts/ or any CLI/scripting context. Also activate when planning module structure, deciding where to put new code, or doing code review."

Python Design for CLI Scripts

Design patterns and principles for writing maintainable Python CLI tools and utilities. Based on *A Philosophy of Software Design* (Ousterhout), adapted for scripting contexts.

When to Activate

  • Writing or modifying Python files
  • Planning module decomposition
  • Code review of Python changes
  • Refactoring scripts that feel "messy"
  • Adding a new subcommand or utility function

Core Thesis

**The central challenge is managing complexity, not adding features.**

Complexity is anything that makes code hard to understand or modify. It has three symptoms:

1. **Change Amplification** — A small change requires edits in many places 2. **Cognitive Load** — You must hold too much context to make a safe change 3. **Unknown Unknowns** — You don't know what you don't know (the most dangerous)

Complexity is incremental. It accumulates through hundreds of small decisions, not one catastrophic mistake. Therefore: **sweat the small stuff**.

---

Principle 1: Deep Modules

A module's value is the ratio of functionality hidden vs. interface exposed.

Deep module (good):          Shallow module (bad):
┌──────────┐                 ┌──────────────────────────┐
│ simple   │                 │ complex interface        │
│ interface│                 │ many params, many methods │
├──────────┤                 ├──────────────────────────┤
│          │                 │                          │
│  rich    │                 │  thin implementation     │
│  impl    │                 │                          │
│          │                 └──────────────────────────┘
│          │
└──────────┘

**Practical test**: If a caller must understand how the module works internally to use it correctly, the module is too shallow.

Example: Task Data Access

# Shallow — caller must know JSON structure, file paths, error handling
def _read_json_file(path: Path) -> dict:
    with open(path, encoding="utf-8") as f:
        return json.load(f)

# Every caller does this independently:
task_path = tasks_dir / name / "task.json"
data = _read_json_file(task_path)
title = data.get("title") or data.get("name", "")
status = data.get("status", "planning")
assignee = data.get("assignee", "")
# Deep — caller gets what they need, module hides JSON/path/parsing
@dataclass(frozen=True)
class TaskInfo:
    name: str
    title: str
    status: str
    assignee: str
    priority: str
    directory: Path

def load_task(tasks_dir: Path, name: str) -> TaskInfo | None:
    """Load task by directory name. Returns None if not found."""
    ...

def list_active_tasks(tasks_dir: Path) -> list[TaskInfo]:
    """List all non-archived tasks, sorted by priority."""
    ...

The deep version absorbs complexity: JSON parsing, field defaults, directory scanning, archive filtering. Callers just work with typed data.

---

Principle 2: Type-First Development

Types define contracts before implementation. This workflow catches design problems early:

1. **Define data shapes** — dataclass or TypedDict first 2. **Define function signatures** — parameter and return types 3. **Implement to satisfy types** — let the type checker guide completeness 4. **Validate at boundaries** — runtime checks only where data enters the system

Frozen Dataclasses for Internal Data

from dataclasses import dataclass
from typing import Literal

@dataclass(frozen=True)
class AgentRecord:
    agent_id: str
    task_name: str
    worktree_path: Path
    platform: Literal["claude", "codex", "cursor"]
    status: Literal["running", "done", "failed"]
    branch: str

Frozen dataclasses are immutable — no accidental mutation, safe to pass around.

TypedDict for External JSON Shapes

When the data comes from a file (task.json, config.yaml, registry.json), use TypedDict to document the expected shape:

from typing import TypedDict, Required, NotRequired

class TaskData(TypedDict):
    title: Required[str]
    status: Required[str]
    assignee: NotRequired[str]
    priority: NotRequired[str]
    parent: NotRequired[str]
    children: NotRequired[list[str]]

This eliminates scattered `.get("field", default)` calls — the shape is documented once.

NewType for Domain Primitives

When two strings mean different things, make the type system enforce it:

from typing import NewType

TaskName = NewType("TaskName", str)    # directory name like "03-10-v040"
BranchName = NewType("BranchName", str)  # git branch like "feat/v0.4.0"

def create_branch(task: TaskName) -> BranchName:
    return BranchName(f"task/{task}")

Discriminated Unions for State

When an entity can be in distinct states with different data:

@dataclass(frozen=True)
class Pending:
    status: Literal["pending"] = "pending"

@dataclass(frozen=True)
class Running:
    status: Literal["running"] = "running"
    pid: int
    worktree: Path

@dataclass(frozen=True)
class Completed:
    status: Literal["completed"] = "completed"
    branch: str
    commit: str

AgentState = Pending | Running | Completed

def handle(state: AgentState) -> None:
    match state:
        case Running(pid=pid, worktree=wt):
            check_process(pid)
        case Completed(branch=br):
            create_pr(br)
        case Pending():
            pass

The type checker ensures every state is handled. No more `if data.get("status") == "running"` with forgotten branches.

---

Principle 3: Information Hiding

Each module should encapsulate design decisions. When the same knowledge appears in multiple module

Read more
@mindfold-ai
Ships withtrellis

The best agent harness.

15k18 skills5 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
15.1k
Star history of mindfold-ai/trellis on GitHub, a chart by star-history.com

Other skills on trellis.

contribute

contribute

Guide for contributing to Trellis documentation and marketplace. Covers adding spec templates, marketplace skills, documentation…

@mindfold-ai@mindfold-aiInstall Skill
first-principles-thinking

first-principles-think…

Systematic first principles thinking for any problem domain. Use when the user says "analyze from first principles", "第一性原理",…

@mindfold-ai@mindfold-aiInstall Skill
gitnexus-cli

gitnexus-cli

Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki,…

@mindfold-ai@mindfold-aiInstall Skill
gitnexus-debugging

gitnexus-debugging

Use when the user is debugging a bug, tracing an error, or asking why something fails. Examples: \"Why is X failing?\", \"Where…

@mindfold-ai@mindfold-aiInstall Skill
gitnexus-exploring

gitnexus-exploring

Use when the user asks how code works, wants to understand architecture, trace execution flows, or explore unfamiliar parts of…

@mindfold-ai@mindfold-aiInstall Skill
gitnexus-guide

gitnexus-guide

Use when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or…

@mindfold-ai@mindfold-aiInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about python-design.

Which coding agents can use python-design?
python-design installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.