Contributing#
Thanks for wanting to help. This page covers the development setup, the checks that have to pass, and how a release is cut.
Set up a development environment#
The project uses uv for environments and hatchling for builds:
git clone https://github.com/siddharth7113/sktime-cli
cd sktime-cli
uv sync
uv sync resolves sktime from PyPI. To test the CLI against unreleased
sktime instead, install your own checkout over the synced environment:
uv pip install -e ../sktime # a local sktime clone
uv pip install "sktime @ git+https://github.com/sktime/sktime.git@main"
Both override sktime in place, and the next uv sync puts the PyPI version
back. Keep the override out of pyproject.toml: a [tool.uv.sources] entry
there is committed, so it would reach everyone who clones the repository.
Install the pre-commit hooks once:
uv run pre-commit install
Run the checks#
Tests run the CLI in-process through Typer’s CliRunner:
uv run pytest
uv run pytest -n auto # in parallel
Tests that download data are marked network and are excluded by default
through addopts = "-m 'not network'" in pyproject.toml. To include them:
uv run pytest -m network
A session-scoped fixture points SKTIME_CLI_HOME at a temporary directory, so
the suite never touches your real cache.
Linting and formatting go through ruff, wired up in pre-commit:
uv run pre-commit run --all-files
Both the test suite and pre-commit run in CI on every push and pull request, across Python 3.10 to 3.14.
CI also pins the sktime axis, because a Python matrix alone cannot catch an
sktime release changing behaviour underneath the CLI. Two blocking jobs test
against the declared floor and the newest release. Two further jobs are
advisory (continue-on-error): one installs sktime from main, and one runs
the suite with -W error::FutureWarning, so an upstream deprecation shows up
while there is still time to act on it rather than when it is removed.
Build the documentation#
The docs are Sphinx with MyST Markdown. To build them locally:
uv sync --extra docs
uv run sphinx-build -b html docs docs/_build/html
Open docs/_build/html/index.html in a browser.
Read the Docs builds with fail_on_warning: true, so a warning locally is a
failed build there. Before you push a docs change, build it once and read the
warnings.
The CLI reference is generated from the live Typer application by a small
extension at docs/_ext/typer_cli.py. Don’t hand-edit command documentation:
change the help text in src/sktime_cli/commands/ and the reference follows.
Regenerate the terminal captures#
The terminal captures in the README and the docs are SVGs rendered from real CLI runs. The hero capture carries a CSS animation that replays the session. After changing CLI output, regenerate them:
uv run python docs/assets/generate.py
Conventions#
- Derive, don’t duplicate
Configuration comes from
pyproject.tomlandimportlib.metadatawherever it can. The optional-dependency list thatdoctorreports on is computed from package metadata rather than maintained by hand, and the docs read the version the same way.- Keep imports lazy
typeris the only third-party module imported at module level. Every sktime, pandas, numpy, rich, and platformdirs import is function-local, sosktime-cli --helpdoesn’t pay for sktime’s import time. A new module-level import of a heavy package is a regression.- Error codes are append-only
The codes in
_errors.pyare part of the published contract. Add codes, don’t rename or reuse them.- Every failure gets a hint
Where the CLI can know the fix, the error’s
hintfield states it. The test suite treats a missing hint on those paths as a bug.- Commands hold no domain logic
Modules under
commands/define the option surface and orchestrate. The logic belongs in the private modules they call. For the module map, see Architecture.
Where to read next#
Architecture for the repository layout and the path a command takes from argv to output.
Design for the decisions behind the state model, the output contract, the error model, and the spec engine.
Working around upstream sktime before you simplify code that looks unnecessarily defensive.
Releasing#
Releases are automated. Pushing a v* tag runs the test matrix, builds the
sdist and wheel, smoke-tests the wheel, and publishes to PyPI through trusted
publishing. The build job checks that the tag matches the version in
pyproject.toml, so bump the version in the same commit that you tag.