commit 0bb3ec31a42e954db99494d332f47f9e7a23959a Author: crosstyan Date: Fri Mar 13 14:54:25 2026 +0800 init diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..aae0863 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,111 @@ +--- +name: python-style-preferences +description: Apply the user's preferred Python engineering style for new modules, refactors, reviews, and project scaffolding. Use when working on Python CLIs, async services, libraries, typed data pipelines, or tensor-heavy code that should standardize on uv, anyio, click, full type annotations, basedpyright, jaxtyping, and beartype. +--- + +# Python Style Preferences + +## Overview + +Follow these conventions whenever the task is primarily Python: + +- Use `uv` for dependency management, virtualenv creation, locking, and command execution. +- Prefer `anyio` over raw `asyncio` APIs. +- Build CLIs with `click`. +- Keep type annotations comprehensive enough for `basedpyright` to be useful by default. + +## Working Style + +1. Start with typed domain shapes. + +- Prefer `TypedDict` for reusable mapping-shaped payloads. +- Prefer `@dataclass(slots=True)` for reusable owned records and value objects. +- Avoid reusable bare `dict[str, object]` structures; keep ad hoc dicts local and short-lived. + +2. Keep entrypoints thin. + +- Put parsing and CLI wiring in `click` commands. +- Put business logic in typed functions and classes. +- For async CLIs, use a synchronous `click` entrypoint that calls `anyio.run(...)`. + +```python +from pathlib import Path + +import anyio +import click + + +@click.command() +@click.argument("config_path", type=click.Path(path_type=Path)) +def main(config_path: Path) -> None: + anyio.run(run, config_path) + + +async def run(config_path: Path) -> None: + ... +``` + +3. Use `anyio` primitives consistently. + +- Prefer `anyio.create_task_group()`, `anyio.fail_after()`, streams, and cancellation scopes. +- Do not mix `asyncio.create_task`, `asyncio.gather`, or manual event-loop management into `anyio` code unless an external integration forces it. +- When an `asyncio` boundary is unavoidable, isolate it and leave a short note. + +4. Treat typing as enforcement, not decoration. + +- Annotate public functions, methods, return values, and reusable internal helpers. +- Run `basedpyright` as the default type-checking pass and keep new code clean. +- Allow `cast(...)` only at trusted boundaries where runtime guarantees exist but inference cannot express them. Leave a short reason comment immediately above it. +- Allow `# type: ignore[...]` only when it is narrow and justified. Leave a short reason comment immediately above it. + +```python +# basedpyright cannot infer the validated plugin registry value type here. +runner = cast(Runner, registry[name]) + +# The vendor stub is wrong for this overload; runtime input is validated above. +value = vendor_api.load(path) # type: ignore[call-overload] +``` + +5. Use `jaxtyping` for arrays and tensors. + +Short summary: + +- Use `DType[ArrayType, "shape names"]`, for example `Float[np.ndarray, "batch channels"]`. +- Reuse axis names to express shared dimensions across arguments and returns. +- Use `...` for arbitrary extra axes and fixed integers for exact sizes. +- Prefer reusable aliases for common tensor shapes. +- Prefer concrete array types after normalization; use `ArrayLike` only at loose input boundaries. + +See `references/jaxtyping-summary.md` for the fuller cheat sheet. + +```python +import numpy as np +from beartype import beartype +from jaxtyping import Float, jaxtyped + +Batch = Float[np.ndarray, "batch channels"] + + +@jaxtyped(typechecker=beartype) +def normalize(x: Batch) -> Batch: + ... +``` + +6. Use runtime type checking where it pays for itself. + +- Prefer `beartype` together with `@jaxtyped(typechecker=beartype)` on stable boundaries, data adapters, numerical helpers, and test-targeted functions. +- Avoid decorating the hottest inner loops unless the cost is clearly acceptable. +- Avoid `from __future__ import annotations` or stringized annotations when runtime checking must inspect annotations directly. + +## Verification + +Use this order unless the project already has a stricter local workflow: + +1. `uv sync --dev` +2. `uv run basedpyright` +3. `uv run pytest` +4. `uv run python -m ` or a CLI smoke test when relevant + +## Reference Files + +- Read `references/jaxtyping-summary.md` when writing or reviewing array/tensor annotations. diff --git a/agents/openai.yaml b/agents/openai.yaml new file mode 100644 index 0000000..3bda1f2 --- /dev/null +++ b/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Python Style Preferences" + short_description: "Typed Python with uv, anyio, click" + default_prompt: "Apply the user's Python style preferences, typing rules, and validation workflow." diff --git a/references/jaxtyping-summary.md b/references/jaxtyping-summary.md new file mode 100644 index 0000000..194ac1a --- /dev/null +++ b/references/jaxtyping-summary.md @@ -0,0 +1,61 @@ +# Jaxtyping Summary + +Use this reference when the task involves NumPy, JAX, PyTorch, TensorFlow, MLX, or array-like inputs that should carry shape and dtype information. + +## Core syntax + +- Use `DType[array_type, "shape"]`. +- Examples: + - `Float[np.ndarray, "batch channels"]` + - `Int[np.ndarray, "persons"]` + - `Shaped[ArrayLike, "batch time features"]` + - `Float[Tensor, "... channels"]` + +## Shape rules + +- Reuse names to enforce equality across values: `"batch time"` with `"time features"`. +- Use fixed integers for exact sizes: `"3 3"`. +- Use `...` for zero or more anonymous axes. +- Use `*name` for a named variadic axis. +- Use `#name` when size `1` should also be accepted for broadcasting. +- Use `_` or `name=...` only for documentation when runtime enforcement is not wanted. + +## Array type guidance + +- Prefer concrete normalized types in core logic: + - `Float[np.ndarray, "..."]` + - `Float[torch.Tensor, "..."]` + - `Float[jax.Array, "..."]` +- Use `Shaped[ArrayLike, "..."]` or another broader input type only at ingestion boundaries. +- Create aliases for repeated shapes instead of rewriting them in every signature. + +```python +from jaxtyping import Float +import numpy as np + +FramePoints = Float[np.ndarray, "frames keypoints dims"] +``` + +## Runtime checking + +- Pair `jaxtyping` with `beartype` for runtime validation: + +```python +from beartype import beartype +from jaxtyping import Float, jaxtyped +import numpy as np + + +@jaxtyped(typechecker=beartype) +def center(x: Float[np.ndarray, "batch dims"]) -> Float[np.ndarray, "batch dims"]: + return x - x.mean(axis=0) +``` + +- Apply this at stable boundaries and in tests, not blindly on every hot loop. +- Avoid `from __future__ import annotations` when relying on runtime checking. + +## Practical defaults + +- Prefer meaningful axis names like `batch`, `frames`, `persons`, `keypoints`, `dims`, `channels`. +- Keep aliases near the module or domain where they are used. +- If static typing and runtime truth diverge, validate at runtime first, then use a commented `cast(...)` at the narrow boundary.