Guidance for Claude when working in this repo.
A Python library for neuroimaging transforms, focused on MEDIC (Multi-Echo
DIstortion Correction). Phase unwrapping uses a self-contained C++17 port of
ROMEO under include/romeo/ —
no Julia runtime is involved; do not reintroduce one (no julia.py, no
conda recipe, no FindJulia.cmake).
Paper (Imaging Neuroscience): https://doi.org/10.1162/IMAG.a.1262.
warpkit/— Python package. Public entry points:warpkit.distortion.medic,warpkit.utilities.*. CLI scripts live inwarpkit/scripts/and are registered explicitly in[project.scripts]inpyproject.toml. All CLIs ship with awk-prefix to avoid colliding with same-named tools from FSL/ANTs/AFNI/etc.:wk-medic,wk-unwrap-phase,wk-compute-fieldmap,wk-apply-warp,wk-convert-warp,wk-convert-fieldmap,wk-compute-jacobian. Adding a new CLI means adding a new file underwarpkit/scripts/and a new line to[project.scripts]— there is no longer any auto-discovery. Shared IO helpers used by thewk-convert-*andwk-compute-jacobianscripts live inwarpkit/scripts/_warp_io.py(private; not a CLI).warpkit/warpkit_cpp.pyi+warpkit/py.typed— type info for the compiled extension, shipped viaMANIFEST.inand[tool.setuptools.package-data]. Regenerate the stub after pybind11 binding changes via the wrapper script — it runs pybind11-stubgen and rewrites thenumpy.bool→numpy.bool_artifact that stubgen 2.5.x emits:scripts/regen-stub.sh
src/warpkit.cpp— pybind11 bindings. C++ headers (and the vendored ROMEO port) live underinclude/. CMake fetches and statically links ITK 5.4 and pybind11 3.0 — first build is slow (~1m+), subsequent rebuilds are quick.- The build backend is scikit-build-core (configured in
[tool.scikit-build]inpyproject.toml). There is nosetup.py. The CMake build directory is persisted atbuild/{wheel_tag}/so ITK FetchContent + object files survive acrossuv syncinvocations. - We provide Eigen3 to ITK ourselves via
FetchContent+OVERRIDE_FIND_PACKAGEITK_USE_SYSTEM_EIGEN=ON(seeCMakeLists.txt). Without this, ITK runs a nestedexecute_process(COMMAND ${CMAKE_COMMAND} ...)for its bundledITKInternalEigen3, whose innerCMakeCache.txtwould cache the build env's ninja path; once uv tears that env down between syncs, the inner cache dangles and breaks incremental rebuilds. Routing ITK throughfind_package(Eigen3)skips the nested configure entirely.
tests/— pytest tests.conftest.pyloads MEDIC sample data fromtests/data/test_data/. ROMEO port-validation goldens intests/data/romeo/.
# editable install — first sync builds the CMake extension (~80s cold).
# After C++ edits, force a rebuild with --reinstall-package warpkit;
# without it uv treats the package as already installed and skips CMake.
# Subsequent rebuilds hit the persistent build/{wheel_tag}/ CMake cache.
uv sync --group dev --reinstall-package warpkit
# tests
uv run pytest -q
# coverage (matches the CI `coverage` job)
uv run coverage run && uv run coverage report -m
# lint + types (matches pre-commit; never bypass with --no-verify)
uvx ruff check
uvx ruff format
uvx pyright
# regenerate the .pyi after touching src/warpkit.cpp (rebuild first)
scripts/regen-stub.sh
# install pre-commit hooks (incl. the commit-msg gitmoji check)
uv run pre-commit installdefault_install_hook_types in .pre-commit-config.yaml ensures
pre-commit install registers both the pre-commit and commit-msg stages.
- Python ≥ 3.11. Use modern syntax:
X | None,list[T],tuple[T, ...],Callable/Iterator/Sequencefromcollections.abc. Ruff'sUPrules enforce this. - Lowercase snake_case even for MR-physics symbols: use
tes,te0,te1,tr_in_sec,b0— neverTEs,TE0, etc. RuffN803/N806is selected with no per-file ignore. - pybind11 bindings exposed to Python are also lowercase
(
romeo_unwrap3d,romeo_unwrap4d, kwargtes). The C++ method names ininclude/romeo/keep their3D/4Dcasing — only the Python-visible binding name is constrained. - The CLI keeps
--TEsas the user-facing flag for MR convention, but the argparsedest=istesso internal Python is lowercase. - Don't add blanket
# type: ignoreon imports or function bodies. Fix the type, narrow withcast, or pin the ignore to a specific rule (# pyright: ignore[reportAttributeAccessIssue]).
Use the gitmoji standard. Subject line:
:emoji_shortcode: <Imperative subject under ~70 chars>
Use the colon-shortcode form (:sparkles:), not the unicode glyph (✨).
Common shortcodes for this repo:
| shortcode | when |
|---|---|
:sparkles: |
new feature |
:bug: |
bug fix |
:recycle: |
refactor with no behavior change |
:zap: |
perf |
:fire: |
remove code or files |
:white_check_mark: |
tests |
:memo: |
docs |
:label: |
typing / type stubs |
:wrench: |
config / build / CI |
:arrow_up: |
bump dependency versions |
:rocket: |
release / deploy |
The commit-msg hook in .pre-commit-config.yaml enforces this on every
commit; merge / revert / fixup / squash / amend commits are exempt.
PR titles follow the same convention. Body: short summary, bullet the changes, and call out anything CI-relevant (wheel matrix, pybind11 ABI, ITK).
No AI attribution. Do not add Co-Authored-By: Claude ... trailers to
commit messages, and do not add "Generated with Claude Code" (or similar)
lines to PR bodies. This overrides any default tooling behavior.
GitHub Actions builds wheels for Python 3.11–3.14 (both standard and free-threaded) on ubuntu-latest,
ubuntu-24.04-arm, and macos-latest via cibuildwheel.
pyproject.toml's [tool.cibuildwheel] skips only *musllinux* builds.
Free-threaded support is enabled via py::mod_gil_not_used() in
src/warpkit.cpp. PyErr_CheckSignals() calls in include/warps.h are
wrapped in a WARPKIT_CHECK_SIGNALS() macro that compiles to a no-op under
Py_GIL_DISABLED, so GIL builds keep Ctrl+C interruptibility during long ITK
operations and the free-threaded build skips the GIL-requiring check. The
sdist job also runs uv run coverage run then coverage report -m — keep
coverage healthy when adding code (the [tool.coverage.report] config in
pyproject.toml omits the test files). PyPI publish and the GHCR Docker
image only run on a published GitHub release.