From ct
Provides deep operational intuition for uv (Astral Python packaging/runtime): project model, lockfile semantics, workspaces, PEP 735 dependency groups, sources, build isolation, managed Python, and migration from pip/poetry/pipx.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ct:uv-pythonThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Concise operational pointers for uv, Astral's Rust-based packaging and runtime tool — replaces pip / pip-tools / virtualenv / pyenv / poetry / pipx in one binary.
Concise operational pointers for uv, Astral's Rust-based packaging and runtime tool — replaces pip / pip-tools / virtualenv / pyenv / poetry / pipx in one binary.
Assumes you already know pip basics, virtual envs, and pyproject.toml. This skill covers the uv-specific layer — project model, lockfile semantics, workspace layout, source resolution, build isolation, managed Python, and migration traps that LLMs gloss over because they look superficially like pip.
Load when the question is about:
pyproject.toml + uv.lock + .python-version) and the [tool.uv] / [tool.uv.sources] / [tool.uv.workspace] / [tool.uv.index] tablesuv lock, uv sync --locked vs --frozen, --upgrade-package, universal resolution, environment markers, requires-pythontool.uv.dev-dependenciesmembers globs, source inheritance, when to prefer path deps--no-build-isolation, dependency-metadata, wheels-only packages (PyTorch, flash-attn)uv python install/pin, python-preference, requires-python interplayuv tool install vs uvx (uv tool run), pipx migrationUV_LINK_MODE, UV_COMPILE_BYTECODE, --no-install-projectrequirements.in)# /// script block), .py.lock per-scriptDo NOT load for: generic Python coding, library API questions, asking what a virtual env is, or "how do I install a package" with no uv-specific friction.
uv init): --app (default; flat main.py, no [build-system], not installed into env), --package (adds src/, build-system, entry points; project IS installed), --lib (implies --package, adds py.typed). Default build backend since uv 0.8 is uv_build (uv_build>=0.11.7,<0.12) — pure-Python only; switch to hatchling for VCS versioning, native ext, build hooks. Pre-July-2025 default was hatchling.uv add <pkg> edits pyproject.toml AND relocks AND syncs. uv pip install <pkg> does none of those — it's a pip-shim that mutates .venv only. Mixing the two is the #1 newbie footgun: uv pip install packages disappear on the next uv sync because they aren't in uv.lock.uv lock re-resolves and writes uv.lock without touching .venv. Flags: --upgrade (relax all locked versions), --upgrade-package <pkg> (relax only one — preserves all other pins), --check (CI: fail if relock would change anything). uv lock --script foo.py writes foo.py.lock adjacent.uv sync reconciles .venv to match uv.lock. Default mode is exact: removes anything not in lock. Use --inexact to retain extras. Always installs the project as editable unless --no-install-project (deps only) or --no-install-workspace (workspace members excluded).uv sync --locked = "fail if uv.lock would change" (CI gate). uv sync --frozen = "skip the resolver entirely; use uv.lock as-is" — fastest, but errors if lock is missing/stale.uv run <cmd> = auto-sync THEN exec. For one-off invocations prefer uv run --frozen <cmd> in CI to skip resolution. uv run --no-project ignores pyproject.toml (pure script mode). uv run --isolated ignores caches/lockfile/sources.uvx is a hard alias for uv tool run. Not the same as uv run. uvx runs in a disposable cache env; uv run uses the project .venv.uv.lock is universal / cross-platform — one lockfile encodes resolutions for every platform/Python combination, gated by PEP 508 markers. This is fundamentally different from pip-tools which produces one requirements.txt per platform.requires-python in [project] AND [tool.uv.environments]. Narrow tool.uv.environments (e.g., drop Windows) when resolution fails because of Windows-only wheels-conflict — common with ML stacks.[tool.uv.required-environments] = "the lock MUST cover these markers". Forces the resolver to fail loudly if a wheel-only dep (e.g. PyTorch CUDA) lacks coverage for a stated platform.requires-python widened in pyproject.toml but lockfile not regenerated; --frozen will then fail at sync. Run uv lock to repair.uv.lock format is uv-private TOML — do not edit by hand. Commit it for apps; for libraries it's still recommended (it locks the dev env, not what consumers see).--prerelease allow even for transitive deps. Pip accepts pre-releases of transitive deps silently — common surprise on migration.[dependency-groups] (top-level, NOT under [tool.uv]). The legacy [tool.uv.dev-dependencies] is deprecated; uv merges both into the dev group during resolution but new code should use [dependency-groups].--group <name>, --no-group <name> (exclusion wins), --all-groups, --no-default-groups, --only-group. The dev group is special-cased and synced by default; flags --dev / --no-dev / --only-dev.[tool.uv] default-groups = ["dev", "test"]. CI prod install: uv sync --locked --no-default-groups (or set UV_NO_DEV=1 and use group-by-group).dev = [{include-group = "test"}, "ipython"].[project.optional-dependencies]) DO publish — that's the structural distinction. Use extras for "consumers can opt in", groups for "developer tooling".uv pip install --group <name> was added later; pre-uv 0.5 the pip shim couldn't see groups.[tool.uv.sources] and alternative indexestag/branch/rev/subdirectory/lfs), url, path, workspace ({ workspace = true }).git = "..." deps don't leak into PyPI metadata). This is intentional and a common confusion.httpx = [{ git = "...", marker = "sys_platform == 'darwin'" }, { index = "internal", marker = "sys_platform == 'linux'" }].[[tool.uv.index]] defines named indexes: name, url, default = true (only one allowed since uv 0.10), explicit = true (packages from this index require an explicit source mapping — the typical PyTorch pattern).--index-strategy unsafe-best-match walks ALL indexes for the highest version. Default is first-index (security: no dependency confusion). Required for some PyTorch CUDA setups; understand the supply-chain trade-off before flipping it.mypkg = { workspace = true }. The version comes from the member's own pyproject.toml, not the root.pyproject.toml: [tool.uv.workspace] members = ["packages/*"], optional exclude. Every matched dir must contain pyproject.toml..venv for the entire workspace, one uv.lock at the root.[tool.uv.sources] in the root applies to all members; a member-local [tool.uv.sources] for a given dependency fully overrides the root entry — markers and all are discarded.requires-python resolution; conflicting requires-python between members forces a single intersected version range.[tool.uv.workspace] when members must keep separate venvs / requires-python / conflicting deps.uv sync --package <member> syncs only that member's deps (still uses the workspace lock). uv run --package <member> <cmd> runs in the workspace venv but with that member as the project.uv python install 3.13 downloads python-build-standalone binaries to ${UV_PYTHON_INSTALL_DIR:-~/.local/share/uv/python}. Multiple versions coexist; uv python list shows them. uv python uninstall <ver>.uv python pin 3.13 writes .python-version (project-local). uv python pin --global 3.13 writes user-config default. Pin is honoured by uv tool install/run since uv 0.10.PATH (python, python3, python3.x) → Windows registry / Microsoft Store. First compatible wins, NOT newest — easy gotcha when python3.10 shadows a managed 3.13.python-preference (also UV_PYTHON_PREFERENCE): managed (default; prefer managed but accept system), only-managed (refuse system), system (prefer system), only-system (refuse downloads). Set only-system in containers shipping their own CPython to prevent surprise downloads.3.13t (free-threaded), 3.13d (debug). PyPy / GraalPy / Pyodide also supported — note since uv 0.10 their executables are named pypy3.10 etc., not generic python3.10.UV_PYTHON_DOWNLOADS=never disables. Useful in air-gapped CI.flash-attn/deepspeed-style packages that import torch at build time).extra-build-dependencies (uv 0.10+): augment the isolated env with extra packages. With match-runtime = true, uv injects the runtime version of the package into the build env — solves the torch-at-build-time problem cleanly.dependency-metadata: declare a package's metadata in pyproject.toml so the resolver doesn't need to build it for solving — only at install. Use for packages with stable, known metadata that are expensive to build.no-build-isolation-package = ["flash-attn"] (or --no-build-isolation globally): turn off isolation. You MUST then uv pip install <build-deps> first, or rely on what's in the env. Most fragile path; reach for it last.[tool.uv.required-environments] for wheels-only packages (PyTorch CUDA): forces the lock to verify wheel availability for declared markers; without it the lock can succeed but uv sync fails on a target machine.[tool.uv] conflicts = [[{ extra = "cpu" }, { extra = "cu128" }]]): tells the resolver these are mutually exclusive — required for the CPU/CUDA-extras pattern, otherwise universal resolution will try to satisfy both at once and fail.${UV_CACHE_DIR:-${XDG_CACHE_HOME:-~/.cache}/uv}. Cache MUST live on the same filesystem as .venv for hardlink installs to work — the most common Docker layering bug.UV_LINK_MODE values: clone (CoW reflinks; APFS/btrfs/xfs default — fastest, zero-copy), hardlink (Linux ext4 default), copy (slowest, always safe), symlink (rare). In multi-stage Docker builds set UV_LINK_MODE=copy or links break across mounts.UV_COMPILE_BYTECODE=1 runs compileall post-install; doubles install time but cuts cold-start for large apps. Standard in production Docker images, off in dev.uv cache clean nukes everything (or uv cache clean <pkg>). uv cache prune removes only unused entries — the routine maintenance command. uv cache prune --ci additionally drops pre-built wheels but keeps source-built wheels (rebuilding from source is more expensive than re-downloading).uv sync --locked --no-default-groups --group ci (or --no-dev if just dev exists). --locked gates the lockfile — fails if pyproject.toml drifted from uv.lock. Never use --frozen for the install step in CI unless you are also running uv lock --check separately; --frozen silently uses a stale lock.uv run --frozen <cmd> AFTER a successful uv sync --locked — skips re-resolution per command.astral-sh/setup-uv@v6 with enable-cache: true and cache-dependency-glob: "**/uv.lock". For matrix Python: pass python-version to the action OR set UV_PYTHON env var; do not also use actions/setup-python unless you set python-preference: only-system.ghcr.io/astral-sh/uv:<tag> (pin a digest for supply-chain), then uv sync --locked --no-install-project (deps layer), copy source, uv sync --locked (project layer). Always UV_LINK_MODE=copy and UV_COMPILE_BYTECODE=1 and UV_PYTHON_DOWNLOADS=never in containers; add .venv to .dockerignore.uv build then uv publish (supports PyPI trusted publishing via OIDC — no credentials in workflow).From poetry:
^1.2, ~1.2) are NOT PEP 440. uvx migrate-to-uv translates ^1.2 → >=1.2,<2, ~1.2 → >=1.2,<1.3. Read the diff — the rewrite is occasionally wrong for pre-1.0 (poetry treats ^0.x.y as >=0.x.y,<0.x+1).[tool.poetry.group.<name>.dependencies] → [dependency-groups] (PEP 735). The migrator offers four strategies (set-default-groups, include-in-dev, merge-into-dev, keep-existing); pick set-default-groups to retain semantics.poetry-dynamic-versioning plugin has no direct port — replace with uv-dynamic-versioning (separate project) OR switch build backend to hatchling + hatch-vcs. uv_build does not support dynamic versioning.poetry.lock and uv.lock are not interchangeable. Delete poetry.lock after migrating; do NOT try to import.tool.poetry.scripts → [project.scripts] (standard PEP 621).From pip-tools:
requirements.in → [project.dependencies]; dev requirements-dev.in → [dependency-groups] dev. uv add -r requirements.in -c requirements.txt preserves pinned versions during import. Strip leading -r requirements.in from the dev file before importing or you double-add.pip-compile foo.in -o foo.txt still works as uv pip compile foo.in -o foo.txt — useful for projects not yet ready to fully port to pyproject.toml.uv.lock replaces per-platform requirements-{linux,win,mac}.txt. If you need a per-platform export: uv export --format requirements.txt --python-platform <platform>.From pipx:
pipx install ruff → uv tool install ruff. pipx run ruff → uvx ruff (or uv tool run ruff).${UV_TOOL_DIR:-~/.local/share/uv/tools}. Not in your project venv. uv tool dir prints the path.uv tool install --with <extra> adds runtime extras to the tool env without exposing their executables. uv tool upgrade <tool> respects original constraints; reinstall to change them.uvx runs in a disposable cache env per invocation (cached, not regenerated, but uv cache clean wipes it). For a stable installed CLI use uv tool install.# /// script block:
# /// script
# requires-python = ">=3.12"
# dependencies = ["httpx", "rich"]
# ///
uv add --script foo.py 'httpx>=0.25' injects/edits the block. uv lock --script foo.py writes foo.py.lock adjacent (per-script lockfile, not uv.lock).uv run foo.py runs in an ephemeral env built from the script's metadata — does NOT touch the project venv. To force project mode, drop the metadata block; to force script mode inside a project use uv run --script foo.py (or shebang #!/usr/bin/env -S uv run --script).--with <pkg> adds a one-off dep without rewriting the metadata block — useful for ad-hoc REPL/debug.Official uv docs (docs.astral.sh/uv):
pyproject.toml[tool.uv.sources]uv tool, uvx)uv_build)[tool.uv.environments]Repo / changelog: astral-sh/uv on GitHub — read the CHANGELOG before pinning a version; 0.10 is the current major behavioural cut.
Migration tooling: migrate-to-uv (run as uvx migrate-to-uv) — supports poetry, pipenv, pip-tools, pip.
Community deep-dives:
uv vs poetry, uv vs pip, dependency-group semanticsBefore recommending a non-trivial uv configuration change (build-isolation off, unsafe-best-match, tool.uv.environments narrowing, --frozen in CI):
unsafe-best-match; stale lock for --frozen).extra-build-dependencies, --no-default-groups, named-required indexes) landed in 0.10. Run uv --version first.Universal resolution and build isolation are correctness defaults — disable them only with a stated reason.
npx claudepluginhub pvillega/claude-templates --plugin ctGuides uv for Python projects: dependency management with pyproject.toml, virtual environments, Python versions, PEP 723 scripts, tool installs, pip/poetry migrations, CI/CD, Docker.
Manages Python projects with Astral's uv: dependencies via pyproject.toml, PEP 723 scripts, virtualenvs, Python versions, tool installs, pip/poetry migrations, CI/CD, Docker setup.
Manages Python dependencies, virtual environments, and project workflows using the uv package manager. Use when setting up Python projects, installing packages, or migrating from pip/poetry.