Review Python code ensuring strict type safety with Python 3.12+ conventions. Use when reviewing Python code, writing new Python code, or refactoring existing Python code.
/plugin marketplace add jack-michaud/faire/plugin install jack-michaud-jack-software-jack-software@jack-michaud/faireThis skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/EXAMPLES.mdexamples/example-1-mixed-syntax/expected.mdexamples/example-1-mixed-syntax/input.pyexamples/example-1-mixed-syntax/scenario.mdexamples/example-2-untyped-code/expected.mdexamples/example-2-untyped-code/input.pyexamples/example-2-untyped-code/scenario.mdexamples/example-3-complex-generics/expected.mdexamples/example-3-complex-generics/input.pyexamples/example-3-complex-generics/scenario.mdThis skill provides systematic Python code review with emphasis on Python 3.12+ type annotations. It enforces strict typing and built-in generic types (list, dict, type) instead of deprecated typing equivalents (List, Dict, Type).
Scope: This skill focuses exclusively on type annotations, not code logic, design patterns, performance, or general quality.
Focus only on type annotation issues.
Check every function, method, and variable for proper type annotations:
# ✅ CORRECT - Modern Python 3.12 syntax
def process_items(items: list[str], count: int) -> dict[str, int]:
result: dict[str, int] = {}
for item in items[:count]:
result[item] = len(item)
return result
# ❌ INCORRECT - Missing types
def process_items(items, count):
result = {}
for item in items[:count]:
result[item] = len(item)
return result
# ❌ INCORRECT - Old typing module syntax
from typing import List, Dict
def process_items(items: List[str], count: int) -> Dict[str, int]:
...
Required checks:
-> None if no return)typing for List, Dict, Set, Tuple, TypeVerify usage of built-in generics instead of typing module equivalents:
Use these (Python 3.12+):
list[T] not List[T]dict[K, V] not Dict[K, V]set[T] not Set[T]tuple[T, ...] not Tuple[T, ...]type[T] not Type[T]Import from typing:
Any, Optional, Union, Callable, Protocol, TypeVar, GenericLiteral, TypedDict, NotRequired, Requiredoverload, final, override# ✅ CORRECT - Modern syntax
from typing import Protocol, TypeVar
T = TypeVar('T')
class Container(Protocol):
def get_items(self) -> list[str]: ...
def merge_dicts(a: dict[str, int], b: dict[str, int]) -> dict[str, int]:
return {**a, **b}
# ❌ INCORRECT - Old typing module imports
from typing import List, Dict, Protocol
def merge_dicts(a: Dict[str, int], b: Dict[str, int]) -> Dict[str, int]:
return {**a, **b}
Check for proper use of modern union syntax:
# ✅ CORRECT - Python 3.10+ union syntax
def find_user(user_id: int) -> dict[str, str] | None:
return users.get(user_id)
def process(value: int | str | float) -> str:
return str(value)
# ❌ INCORRECT - Old Optional/Union syntax
from typing import Optional, Union
def find_user(user_id: int) -> Optional[dict[str, str]]:
return users.get(user_id)
def process(value: Union[int, str, float]) -> str:
return str(value)
Ensure no untyped code exists:
Check these locations:
# ✅ CORRECT - Fully typed
class UserService:
_cache: dict[int, str]
def __init__(self, cache: dict[int, str] | None = None) -> None:
self._cache = cache or {}
def get_user(self, user_id: int) -> str | None:
return self._cache.get(user_id)
# ❌ INCORRECT - Untyped attribute
class UserService:
def __init__(self, cache=None):
self._cache = cache or {}
def get_user(self, user_id):
return self._cache.get(user_id)
Run static type checker:
mypy --strict your_module.py
Configure in pyproject.toml:
[tool.mypy]
strict = true
python_version = "3.12"
List, Dict, Set, Tuple, Type from typing| syntax, not Union[]X | None, not Optional[X]mypy --strict passes without errors# type: ignore comments without justificationBefore (❌):
from typing import Dict, List, Optional
def get_users(limit=10, offset=0):
users = fetch_from_db(limit, offset)
return {"users": users, "count": len(users)}
def create_user(data):
user_id = save_to_db(data)
return {"id": user_id}
After (✅):
from typing import TypedDict
class UserResponse(TypedDict):
users: list[dict[str, str]]
count: int
class CreateResponse(TypedDict):
id: int
def get_users(limit: int = 10, offset: int = 0) -> UserResponse:
users: list[dict[str, str]] = fetch_from_db(limit, offset)
return {"users": users, "count": len(users)}
def create_user(data: dict[str, str]) -> CreateResponse:
user_id: int = save_to_db(data)
return {"id": user_id}
Before (❌):
from typing import List, Dict, Callable
def transform_data(data, transformers):
result = []
for item in data:
for transformer in transformers:
item = transformer(item)
result.append(item)
return result
After (✅):
from typing import Callable, TypeVar
T = TypeVar('T')
def transform_data(
data: list[T],
transformers: list[Callable[[T], T]]
) -> list[T]:
result: list[T] = []
for item in data:
for transformer in transformers:
item = transformer(item)
result.append(item)
return result
Before (❌):
from typing import Generic, TypeVar, List, Optional
T = TypeVar('T')
class Container(Generic[T]):
def __init__(self):
self._items = []
def add(self, item):
self._items.append(item)
def get_all(self):
return self._items
After (✅):
from typing import Generic, TypeVar
T = TypeVar('T')
class Container(Generic[T]):
_items: list[T]
def __init__(self) -> None:
self._items = []
def add(self, item: T) -> None:
self._items.append(item)
def get_all(self) -> list[T]:
return self._items
❌ Don't: Import List, Dict, Set, Tuple, Type from typing module
list, dict, set, tuple, type with generic syntax❌ Don't: Use Optional[X] or Union[X, Y] syntax
X | None and X | Y union syntax❌ Don't: Leave any function, method, or class attribute untyped
❌ Don't: Use # type: ignore without explanation
❌ Don't: Use Any as a shortcut to avoid thinking about types
❌ Don't: Skip type annotations on simple functions
❌ Don't: Mix old and new typing syntax in the same codebase
Follow instructions in examples/EXAMPLES.md.
Remember: No untyped code. Use Python 3.12+ built-in generics. Type everything.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.