PyPilot for Zed
When you clone a Python repository and run an install, it often fails because a dependency lacks a prebuilt wheel for your interpreter. PyPilot calculates which Python versions the project's dependencies actually support, configures a virtual environment on that version, and identifies the specific package causing any constraint conflict.
Every resolution relies on PyPI package metadata and local lookup tables. The tool runs locally without API keys or external services.
Documentation: https://pypilot-dev.github.io/pypilot/, with the guide, the engine CLI, VS Code, Zed, the Python package, pixi and the reference.
Features marked (since 1.3.0) need engine 1.3.0 or newer.
The problem it solves
A package's requires_python field is often incomplete. For example, mediapipe 0.10.9 declares >=3.8, yet only ships wheels for cp39 through cp312. If you try installing it on Python 3.13, pip searches for an unavailable source distribution and terminates with a metadata error that never mentions Python version incompatibility.
PyPilot checks both signals. It takes requires_python as an initial range, then intersects that range with the interpreter versions that provide prebuilt wheels for your operating system and CPU architecture. After evaluating each dependency in the workspace, the resulting intersection represents the interpreters that can run the entire project. When this intersection is empty, PyPilot names the conflicting packages and shows their incompatible requirements.
Naming the two conflicting packages matters more than applying an automated workaround, because a dependency collision is where package manager error output is least helpful.
What else it catches
Outdated or removed APIs
Package metadata verifies whether a package installs, but cannot verify whether your code runs against that release. For example, mediapipe 0.10.35 installs cleanly on modern CPython releases and then crashes on module 'mediapipe' has no attribute 'solutions' because Google removed the legacy API. PyPilot inspects the installed package on disk, flagging references like mp.solutions in your editor while you write code instead of waiting for runtime failures.
GPU driver and CUDA alignment
For pip-installed frameworks, the host NVIDIA display driver determines compatibility rather than the system CUDA toolkit. Wheels package their own CUDA runtime, and display drivers remain backward compatible. PyPilot inspects nvidia-smi, maps the driver to its highest supported CUDA runtime, and selects the matching PyTorch index (such as cu124 instead of a mismatched default). Systems without a dedicated GPU use CPU wheels, while Apple Silicon devices receive MPS guidance.
Pinned version constraints
Pinned dependencies restrict the wheel search space. A declaration like mediapipe==0.10.14 is evaluated against wheels published for that specific release (which ended at Python 3.12), rather than the wider matrix of newer releases.
Jupyter notebooks
In VS Code, imports inside .ipynb cells get the same diagnostics and quick fixes as .py files; IPython magics and shell escapes (%pip, !ls) are ignored. A notebook can only run in an environment that has ipykernel, so in projects with notebooks PyPilot adds ipykernel>=6.31.0,<7 to .venv (ipykernel 7.x can make VS Code notebooks hang, and 7.0.0 cannot connect on Python 3.14) whenever it sets up the environment or installs a package (as a dev dependency in uv projects), and warns when it is missing.
Installing several packages at once
Select several unresolved imports and open the quick fixes: one action installs all of them in a single step, instead of one action per package. "Install all N missing packages in this file" (or "in this notebook") covers the rest of the document.
Environment management with uv and pip
PyPilot supports both uv and standard pip for managing virtual environments.
Project initialization and uv mode
When package_manager = "uv" (the default):
- If the project directory lacks a
pyproject.toml, PyPilot runsuv init --bareto create one, with.python-versionset to the Python it chose andrequires-pythonstarting at the oldest Python every dependency supports (3.9 at the lowest). Nothing else is added to the folder. - When PyPilot builds
.venvon a Python version, it pins the project to that version, so a lateruv syncoruv addkeeps the environment rather than rebuilding it on an older pin or a globaluv python pin. - If a
requirements.txtfile exists during setup, PyPilot reads its dependencies and runsuv addto migrate them directly into the newpyproject.toml. - Running
pypilot install <package>invokesuv add <package>, adding the dependency to the project manifest and syncing the environment.
Pip fallback and freeze synchronization
When uv is unavailable on the system or package_manager = "pip" is explicitly configured:
- PyPilot creates the virtual environment using Python's standard
venvmodule. - Dependencies are installed using
pip install. - After each installation or dependency modification, PyPilot runs
pip freeze > requirements.txtto update the file with pinned versions. This keeps your dependencies documented and reproducible even without a lockfile manager.
pixi
pixi installs packages from conda-forge and PyPI into a per-project environment, with a lock file. Guide: pixi and PyPilot.
Why pixi helps
pip and uv install Python packages. A dependency without a wheel for your platform or Python is built from source, and that build often needs things outside Python that pip and uv cannot install:
- System libraries such as GDAL, PROJ, GEOS or libGL. conda-forge ships them as packages next to the Python bindings, at matching versions.
- Compilers. conda-forge has prebuilt binaries of packages that have no wheel for your platform or Python, so no C, C++ or Fortran toolchain is needed.
- The CUDA toolkit. pip wheels carry only the CUDA runtime; code with
.cusources needsnvcc, which conda-forge provides per project (cuda-nvcc). - More than Python. One manifest covers Python together with R, C++ or Rust.
- One lock file for every OS.
pixi.lockpins native dependencies too, for every platform listed in the manifest. - Several environments (for example
defaultandcuda) in one project.
pixi projects
Projects managed by pixi (a pixi.toml, or [tool.pixi.workspace] in pyproject.toml) are detected automatically, whatever package_manager says. PyPilot then works through pixi and never creates a .venv, runs uv or writes pixi.lock:
- The environment is pixi's:
.pixi/envs/default, or another one chosen withpixi_environmentinpypilot.toml,--pixi-env <name>for one run, orpypilot.pixiEnvironmentin VS Code (since 1.3.0). Its Python version, installed packages and GPU builds are read from there, so import diagnostics and findings are accurate. - Setup runs
pixi install; installing a package runspixi add --pypi <package>; moving to another Python runspixi add python=X.Y. - PyPI dependencies are checked against the environment's Python. When one has no wheel for it, PyPilot names the package and the fix (
pixi add python=3.11), and turns pixi's own install errors into the same plain explanation. - The recommended Python respects the manifest's
pythonspec, and doctor lists conda and PyPI dependencies separately. - (since 1.3.0) Doctor lists every environment with its features, Python and dependencies, and reports environments on different Pythons, a
pixi.lockthat no longer matches the manifest (checked withpixi lock --check --dry-run, which writes nothing), and PyPI dependencies with no wheel for one of the manifest'splatforms, which would breakpixi lockon every machine. - If pixi is not installed, PyPilot says so and changes nothing. (since 1.3.0) It offers to install it (
pypilot install-pixi, or the editor's "Install pixi" button), using pixi's official installer.
When PyPilot suggests pixi (since 1.3.0)
In a pip or uv project, PyPilot recognises walls pip and uv cannot climb and conda-forge can, and raises a finding that suggests pixi:
- a dependency with no installable wheel here whose build needs native libraries that conda-forge packages (about 190 packages, from GDAL, cartopy and fiona to dlib and faiss);
- a failed pip or uv build whose output names a missing header,
*-configtool or library (gdal.h, say); - CUDA sources (
.cu,.cuh) with nonvcconPATH,CUDA_HOMEorCUDA_PATH; - a repository that mixes Python with R, Rust or CMake/Meson C++ (Info only).
It only suggests: nothing is converted until you run the migration. pypilot check <package> shows the conda-forge alternative when a package fails. Details: the advisory.
A requirements.txt with numpy, requests and gdal on Windows:
$ pypilot doctor
...
── Findings ─────────────────────────────────────────────
[WARN ] GDAL 3.13.3 needs native libraries pip cannot install
GDAL 3.13.3 has no wheel for Windows x64, so pip and uv build it from source. GDAL's Python bindings must be compiled against a system GDAL of exactly the same version (gdal-config), which pip cannot install. pixi installs gdal 3.13.3 from conda-forge with its native libraries (libgdal, proj, geos), built for win-64 and Python 3.15. Run `pypilot migrate-to-pixi` to set the project up with pixi.
$ pypilot migrate-to-pixi
Dry run: nothing was written.
--- /dev/null
+++ b/pixi.toml
@@ -0,0 +1,13 @@
+# Written by `pypilot migrate-to-pixi` from requirements.txt (left untouched).
+# Review it, then run `pixi install`.
+
+[workspace]
+name = "geo"
+channels = ["conda-forge"]
+platforms = ["win-64"]
+
+[dependencies]
+python = "3.15.*"
+numpy = "*"
+requests = "*"
+gdal = "*"
Would write ...\geo\pixi.toml: python 3.15.*, 3 conda-forge and 0 PyPI dependencies.
Next: pypilot migrate-to-pixi --writeMoving a project to pixi (since 1.3.0)
pypilot migrate-to-pixi previews the pixi.toml it would write for a pip or uv project (requirements.txt and [project.dependencies]); --write creates it, and --write --install then runs pixi install. pypilot migrate-conda --to pixi does the same from an environment.yml (channels, conda-only packages such as gdal or cudatoolkit, and the pip: section as [pypi-dependencies]). Step by step: Migrating to pixi.
- It writes a separate
pixi.tomland never edits or deletespyproject.toml,requirements.txtorenvironment.yml, so CI and pip keep working. pixi preferspixi.tomlwhen both exist. - Each PyPI dependency packaged on conda-forge (per prefix.dev's PyPI-to-conda mapping, cached for
data_refresh_days) and built for every target platform goes to[dependencies]under its conda name (torchbecomespytorch); the rest go to[pypi-dependencies], with the reason as a comment. Version specifiers carry over only when conda can express them exactly; otherwise the dependency stays on PyPI. Offline, everything stays on PyPI. pythonis the version PyPilot recommends, within what the conda-forge builds support. Platforms default to the current one; add more with--platform linux-64(repeatable).- Environment markers, editable installs and
-rincludes are listed in the file as comments to add by hand. package_manager = "pixi"makessetupwrite thepixi.tomlthe same way on a project without a pixi manifest, then runpixi install.- In Zed, the advisory's notification has an Initialize pixi project button: it says what would be written and asks first (Write and install, Write pixi.toml or Cancel). The tasks
pypilot: preview migration to pixi (pixi.toml)andpypilot: migrate to pixi (write pixi.toml and install)do the same. In VS Code, use PyPilot: Initialize pixi project and PyPilot: Install pixi.
Installation
Install PyPilot from Zed's extension panel. When you open a Python folder, the extension automatically inspects the environment and opens a notification if action is required.
Local development build
To test the extension locally:
- Build and install the helper binary with
cargo install --path helper. - In Zed, open the command palette and run
zed: install dev extension, selecting theextensiondirectory.
The extension checks PATH first, running your local binary instead of fetching a release from GitHub.
Architecture
PyPilot splits work between two components:
- A WebAssembly shim in
extension/that runs inside Zed. It identifies the host platform, downloads the precompiled helper binary for that architecture, and registers it with Zed as an LSP server. - A native Rust binary in
helper/that runs all analysis, environment orchestration, dependency parsing, and LSP communication.
Because the editor extension contains no business logic, changes in Zed extension APIs do not affect environment resolution or compatibility checking.
Repository layout
pypilot/
├── extension/ Zed WebAssembly shim (extension.toml and src/lib.rs)
├── helper/ Native helper binary
│ ├── src/
│ │ ├── main.rs CLI command dispatch
│ │ ├── cli/ Subcommand entry points
│ │ ├── lsp/ Language server protocol implementation
│ │ ├── core/ Virtual environment management, uv and pip drivers, solver
│ │ ├── pypi/ Package metadata parser and local cache
│ │ └── matrix/ Driver and framework compatibility tables
│ ├── data/ Bundled JSON tables for NVIDIA and frameworks
│ └── tests/ Fixtures and offline test suites
├── tasks/ Zed task templates
└── .github/workflows/ CI and release automation workflowsCommands
Zed extensions execute external actions through tasks. To use PyPilot commands inside Zed, copy tasks/windows.json (Windows) or tasks/macos-linux.json (macOS, Linux) into .zed/tasks.json within your workspace or into your global Zed tasks configuration, then run them with task: spawn. The tasks use a pypilot on your PATH when there is one, and otherwise the copy the extension downloaded, so they work without installing anything else.
The helper binary also runs directly in any shell:
pypilot doctor Inspect the environment and print a status report
(--fix applies only the fixes the findings call for, like
the editor's Fix environment)
pypilot setup Configure the virtual environment and install dependencies
pypilot check <package> Check whether a package runs on the current Python version
(accepts a requirement, e.g. mediapipe==0.10.14)
pypilot explain [pkg] Show why: requires_python vs the wheels per platform for a
package, or what blocks each Python in the project (--json)
pypilot install <pkg> Install a package and update the project manifest
pypilot fix python Rebuild the virtual environment on a compatible Python version
pypilot fix cuda Re-pin torch and tensorflow to the system driver build
pypilot fix requirements Fix obvious mistakes in requirements.txt and add undeclared
imports (yaml to PyYAML, sklearn to scikit-learn)
pypilot update-data Update the bundled driver and framework data tables
(--check: compare with main without writing; exit 0 current,
1 stale, 2 network error)
pypilot migrate-conda Convert environment.yml into pyproject.toml
(--to pixi: into pixi.toml; a preview unless --write)
pypilot migrate-to-pixi Preview a pixi.toml for this project (--write, --install,
--platform <p>)
pypilot install-pixi Install pixi with its official installer
pypilot logs Print the local log's path (--tail [N]: the last N entries)
pypilot completions <sh> Print a completion script (bash, zsh, fish, powershell)
pypilot version Engine version, commit, target and data tables
pypilot lsp Start the language server (launched automatically by Zed)explain, migrate-to-pixi, install-pixi, logs, completions and version, doctor --fix, update-data --check, migrate-conda --to pixi and the options below are new in 1.3.0. fix requirements and the targeted doctor --fix are new in 1.4.0.
--jsonon every mode butlsp: one stable JSON document on stdout, for scripts and bug reports. Fields are only ever added, never renamed or removed; they are listed in USAGE.md.--markdownondoctorandcheck: the editor's report, ready to paste into an issue.--dry-runonsetup,install,fix python,fix cudaanddoctor --fix: prints every command it would run and every file it would change, with the reason, and changes nothing. The plan comes from the same code path as the real run.explainshows why a package or the project is limited to the Pythons it is.logs: every CLI command and editor action appends one JSON line to a local log (rotated at 1 MB, secrets masked, nothing sent anywhere;PYPILOT_NO_LOG=1turns it off).completions, for examplesource <(pypilot completions bash).
More in USAGE.md and the command reference.
Running pypilot doctor or pypilot setup from a Zed task also writes a request file to the cache directory. The running language server reads this file and presents the results as interactive editor notifications.
What doctor --fix does
Since 1.4.0 doctor --fix fixes what is wrong and nothing else. It does not rebuild the environment for every problem.
- A healthy project is left alone.
- Each finding gets its own fix:
uv sync --lockedfor drift or a missing package,uv lockfor a stale lock, a reinstall of one half-installed package, a repair of a damaged venv on the Python it had, a higherrequires-python, or an edit to one requirement. - The Python moves only when a dependency forces it, and the output names that dependency. An existing
.python-versionpin wins otherwise. The Python a fix picks is written to.python-version. - Every file PyPilot edits is named. An edit that leaves the project unresolvable is rolled back.
- Conflicts are resolved before anything changes: an unsatisfiable pin gets a relaxed pin that uv has checked, a typo gets "did you mean", a version that does not exist gets the nearest releases, and a package with no build for this OS gets an environment marker.
- Known breakages come from
known_breakages.json, a data table refreshed like the others: compiled packages that break under numpy 2, uncapped dependencies that later removed an API, packages that shadow each other, yanked pins, stdlib modules removed in newer Pythons, and removed APIs your own code uses (reported by file and line, with the replacement). PyPilot never edits your source code. - If
requires-pythoncan never be met (>=3.16), it is reported as an error and nothing is built. - pixi, conda
environment.yml, Poetry andvenv/orenv/projects are fixed with their own tools.
--dry-run lists exactly what the real run does. See What doctor --fix fixes for the full table. In a test of 54 deliberately broken environments, 45 got the right targeted fix, none got a wrong one and none were missed.
Configuration
PyPilot stores default configuration in your platform config directory. Projects can override these options by creating a .zed/pypilot.toml or pypilot.toml file in the workspace root:
package_manager = "uv" # "auto", "uv", "uv-pip", "pip" or "pixi". Pip mode does not invoke uv.
notifications = "problems-only" # "all", "problems-only", or "off"
auto_check_on_open = true
data_refresh_days = 7 # Set to 0 to disable network updates.
pixi_environment = "default" # pixi projects: which .pixi/envs/<name> (since 1.3.0)
diagnostics_scope = "openFiles" # or "workspace" (since 1.3.0)package_manager: Selects how PyPilot manages the environment.auto(the default) uses uv when it is installed and pip otherwise;uvis a uv project (pyproject.toml+uv.lock);uv-pipis uv's pip interface onrequirements.txt;pipispython -m venv+ pip;piximanages the project with pixi. In pip mode, PyPilot cannot download new Python interpreter versions automatically; if an incompatible version is detected, it informs you which Python version to install manually. A pixi project (apixi.tomlor[tool.pixi.workspace]) is always managed with pixi, whatever this says; withpixion a project without a pixi manifest, setup first writespixi.tomlfrom the project's dependencies (aspypilot migrate-to-pixi --writedoes), then runspixi install.notifications: Controls alert frequency.problems-onlywarns about errors and version mismatches.allincludes informational alerts like selected CUDA builds.offsuppresses editor notifications. Diagnostics and code actions in active editor buffers remain enabled unless set tooff.data_refresh_days: The refresh interval for the bundled driver and framework matrices. PyPilot ships with embedded JSON tables for offline use. If a background check fails, it falls back to the embedded data.pixi_environment: The pixi environment PyPilot treats as the project's (--pixi-env <name>overrides it for one run). Ignored outside pixi projects.diagnostics_scope:openFileschecks imports in open files only.workspacealso scans the project's.pyfiles and notebooks in the background (skipping virtual environments, build directories and whatever.gitignoreexcludes), so problems show up for files you have not opened.
All settings: configuration reference.
Other editors
pypilot lsp is a plain LSP server over stdio. helper/src/lsp/PROTOCOL.md documents everything an editor integration needs (settings, commands, messages, diagnostics, code actions), with snippets for Neovim, Helix and Emacs.
Release integrity
Since engine 1.3.0, each release carries SHA256SUMS for its four archives, a CycloneDX SBOM of the engine (pypilot-helper.cdx.json) and GitHub build-provenance attestations, so a download can be checked with sha256sum -c SHA256SUMS --ignore-missing or gh attestation verify <file> -R pypilot-dev/pypilot. The VS Code extension and the pypilot-cli package pin one engine release and its hashes and refuse a download that does not match. See Security & release integrity.
Building from source
cargo test -p pypilot-helper
cargo build -p pypilot-helper --release
rustup target add wasm32-wasip2
cargo build -p pypilot-zed --target wasm32-wasip2 --releaseTests run offline against recorded PyPI fixtures. The platform is always passed in, never read from the host, so Linux (x64, arm64, glibc and musl), macOS (x64, arm64) and Windows (x64, arm64) are all checked on any machine; helper/tests/platforms.rs covers the cases where they differ.
python scripts/check_data.py reports whether the bundled tables in helper/data/ have fallen behind PyPI, the PyTorch wheel indexes and NVIDIA's driver table. Run it before changing code and see AGENTS.md for how to update them.
License
PyPilot uses two licenses corresponding to its components:
helper/is licensed under AGPL-3.0-or-later. See helper/LICENSE.extension/is licensed under Apache-2.0. See LICENSE. This complies with the Zed extension registry requirements for extension frontends.