Skip to main content

nur

CI Coverage Documentation Status PyPI - Version PyPI - Python Version License - MIT GitHub Stars Latest Commit

Checked with mypy linting - Ruff OpenSSF Scorecard GitHub Tag

Downloads Downloads/Month Downloads/Week

A script discovery and execution engine for your project's tasks.


Run nur in a project and it discovers the tasks your project already defines — from npm, Make, deno, composer, just, Taskfile, pre-commit, PDM/poe, tox, mise, cargo-make, moon, xc, VS Code tasks, nox, mask, and Invoke — then lets you run them from a TUI picker or directly from the command line. Discovery is limited to the current directory. See Features for the full list of source files.

Table of Contents

Installation

nur is a standalone command-line tool — install it into an isolated environment rather than as a project dependency:

uv tool install nur

Or run it without installing:

uvx nur

On macOS/Linux via Homebrew:

brew install hasansezertasan/tap/nur

On Windows via Scoop:

scoop bucket add hasansezertasan https://github.com/hasansezertasan/scoop-bucket
scoop install nur

To install the latest unreleased changes from source:

uv tool install git+https://github.com/hasansezertasan/nur

Or, from a clone:

uv tool install .

Verify the installation

The package root is importable after installation:

>>> import nur
>>> isinstance(nur.__version__, str)
True

Usage

Run nur from the root of a project that contains any supported task file.

TUI

Run with no arguments to open the interactive picker:

nur

Browse and run the discovered tasks in a three-pane Textual UI. Press q to exit.

CLI

nur test            # run a task by name (if unambiguous)
nur make:test       # run a task by its qualified name
nur test -- --watch # pass extra args through to the underlying runner
nur list            # print all discovered tasks
nur --version

Support ❤️

If you have any questions or need help, feel free to open an issue on the GitHub repository.

Motivation

Every project speaks a different task dialect — make test, npm run test, just test, task test, pdm run test, poe test, tox -e test, mise run test, xc test. nur gives you one command that discovers whatever a project already uses and runs it, with no config and no need to remember which runner lives where. Discovery is pure text/JSON/TOML parsing, so listing tasks never executes anything (no make -pRrq side effects).

Features

  • Zero-config discovery across eighteen providers, each parsed from a single source file in the current directory:

    Provider Prefix Source file
    npm npm package.json
    deno deno deno.json / deno.jsonc
    composer composer composer.json
    Make make Makefile
    PDM pdm pyproject.toml ([tool.pdm.scripts])
    poe poe pyproject.toml ([tool.poe.tasks])
    tox tox tox.ini, setup.cfg, pyproject.toml, or tox.toml
    just just justfile
    Taskfile task Taskfile.yml
    pre-commit pre-commit .pre-commit-config.yaml
    mise mise mise.toml (and variants — see below)
    cargo-make cargo-make Makefile.toml
    moon moon moon.yml
    xc xc README.md (see below)
    VS Code vscode .vscode/tasks.json (see below)
    Invoke invoke tasks.py (see below)
    nox nox noxfile.py (see below)
    mask mask maskfile.md (see below)

    tox reads the first applicable config file present, in priority order: tox.ini, setup.cfg ([tox:tox]), pyproject.toml ([tool.tox]), then tox.toml. It statically parses environments and never runs tox during discovery. mise reads the first config file present, in priority order: mise.local.toml, mise.toml, .mise.local.toml, .mise.toml, .config/mise.toml. xc reads its task section from README.md — the block marked with an <!-- xc-heading --> comment, or failing that a heading named Tasks (which is how nur discovers the tasks documented in this very file). composer reads the top-level scripts object from composer.json, surfacing only user-defined custom scripts (Composer's reserved lifecycle hooks like post-install-cmd are filtered out) and taking descriptions from the scripts-descriptions table when present. pre-commit reads only repos[].hooks[] and runs a selected hook with pre-commit run <id> --all-files; it never invokes pre-commit during discovery. vscode reads version 2.0.0 .vscode/tasks.json (JSONC) and surfaces shell and process tasks with a command (an untyped task runs as a process, as in VS Code), applying the current platform's overrides, inheriting the document-level type and, for tasks without their own command, its command and args, and resolving ${workspaceFolder}, ${workspaceFolderBasename}, ${pathSeparator}, and ${env:NAME}. Tasks nur cannot run faithfully are skipped: hidden tasks, tasks with a non-empty dependsOn, other variables, or options setting env, shell, or a cwd other than the project root. Shell tasks run through $SHELL -c on POSIX (falling back to /bin/sh) and cmd.exe on Windows; an object-form command is quoted as a single literal token.

    invoke parses tasks.py as a Python syntax tree without importing it or running inv --list. It discovers top-level functions with a single @task, @task(...), or @invoke.task decorator, including import aliases. Literal name= and list/tuple aliases= are supported; underscores become dashes following Invoke's default configuration. Tasks run as invoke <name> with extra CLI flags passed through, and the first docstring line is the description. Command bodies, required parameters, pre/post hooks, imported or dynamic tasks, conditional definitions, custom decorators, and explicit Collection wiring are not resolved. Explicit namespaces may therefore make the discovered bare names unavailable, and configuration disabling automatic dashes is not read. Names and aliases containing dots or beginning with a dash are excluded because Invoke interprets them as namespace paths or CLI options.

    nox parses noxfile.py as a Python syntax tree (never importing it, unlike nox --list) and surfaces top-level @nox.session-decorated functions, runnable as nox -s <name>, using an explicit string name= when given and the docstring's first line as the description. nox-uv's drop-in session decorator is recognised too, and python=[...] / @nox.parametrize variants appear under their base name (which runs every variant). Only straight-line noxfiles are trusted: module code may use imports, assignments, def/class, and if/try blocks made of those (such as try-import fallbacks and if sys.version_info < ...: raise guards); if TYPE_CHECKING: and if __name__ == "__main__": blocks are skipped. A noxfile that uses loops, with, match, assert, raise inside a try, or that edits nox.session or a namespace directly lists no sessions, rather than sessions nox may not register. Discovery is best-effort: it cannot know whether an import or an expression fails in nox's environment, and deliberately unusual module code (aliasing tricks, handler matching inside try, and similar) may still list a session nox rejects. Running such a session fails with nox's own error; nur never runs noxfile code to discover sessions. mask reads maskfile.md the way mask does: each heading after the title is a command, a deeper heading is its subcommand, the last fenced block under a heading is its script, and a > blockquote is its description. A subcommand is named by its path and runs with it, so ### test lint under ## test is the task test lint (nur "test lint" runs mask test lint). Task names are the shell words after mask, so one command whose own name has a space is quoted: ## deploy prod is the task 'deploy prod'. A heading becomes a task only when its last code block is fenced with a language tag, since mask needs the tag to pick an interpreter; powershell, batch, and cmd blocks count only on Windows, as in mask. Headings and fences inside HTML blocks (comments, <details>, and the like) are ignored. Fences nested in blockquotes are not recognised. Like mask, a heading's inline markup restarts its name, so ## a *b* c is the task b c.

  • CLI Application: run any discovered task by name or qualified prefix:name, with -- passthrough to the underlying runner.

  • TUI Application: interactive three-pane task picker built with Textual.

  • Safe by default: discovery parses files; it never shells out to a runner just to list tasks.

  • Type Safety: full type hints checked by mypy, basedpyright, ty, pyrefly, and zuban.

  • Code Quality: comprehensive linting and formatting with ruff, plus architecture-contract enforcement with import-linter.

  • Testing: pytest with coverage reporting and parallel execution.

  • Documentation: Sphinx documentation with the Shibuya theme, GitHub Pages deployment, and live per-PR documentation previews.

  • CI/CD: automated testing, building, and publishing across multiple platforms.

  • Security: CodeQL, OpenSSF Scorecard, dependency review, secret scanning (gitleaks), dependency auditing (pip-audit), GitHub Actions static analysis (zizmor — a blocking prek/CI gate plus a Security-tab dashboard, over hardened least-privilege workflows), and a CycloneDX SBOM attached to every release.

  • Managed .gitignore: kept in sync with the upstream github/gitignore templates by cobo, with a weekly drift check.

  • Modern Python: uv for dependency management, hatch for building.

Author 🫅

This project is maintained by Hasan Sezer Taşan, It's me 👋

Analysis

Contributing ❤️

Any contributions are welcome! Please follow the Contributing Guidelines to contribute to this project.

Development 🧰

Clone the repository and cd into the project directory:

git clone https://github.com/hasansezertasan/nur
cd nur

install

Install the dependencies:

uv sync

style

Run the style checks:

uv run --locked tox run -e style

ci

Run the CI pipeline:

uv run --locked tox run

docs-build

Build the documentation site:

uv run --locked tox run -e docs-build

docs-server

Start the live-reloading docs server:

uv run --locked tox run -e docs-server

docs-linkcheck

Check the documentation for broken links (also runs weekly in CI):

uv run --locked tox run -e docs-linkcheck

Releasing

Versioning and releases are automated with release-please, driven by Conventional Commit PR titles squash-merged into main. release-please maintains a release PR that bumps the version and CHANGELOG.md; merging it tags the release and publishes to PyPI. See the Contributing Guidelines for the commit conventions, and the Repository setup guide for one-time configuration and optional post-launch integrations such as a social preview, downstream packaging, and Repology.

Credits

This package was created with Copier and the hasansezertasan/copier-pyproject project template.

License 📜

This project is licensed under the MIT License.

Changelog 📝

For a detailed list of changes, see the GitHub Releases. A CHANGELOG.md is generated automatically by release-please on each release.

Metadata

Release files for nur 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nur 0.6.0
File Size Uploaded
nur-0.6.0.tar.gz 161.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nur 0.6.0
File Interpreter ABI Platform
nur-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 265.5 kB

Release files / nur-0.6.0.tar.gz

Download URL nur-0.6.0.tar.gz
Size 161.6 kB
Tags Source
SHA-256 checksum
How to use checksums
70e17ce98b1976e06d180f39819347aa7fe419eb53784892bb070e4f83bd1658
BLAKE2b-256 checksum
How to use checksums
36a64b4d6036541c2a89d02ee0a0e6639aa320debc240fb2e3c691894269fc44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / nur-0.6.0-py3-none-any.whl

Download URL nur-0.6.0-py3-none-any.whl
Size 103.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f5422ed0bbaff4f349d70fe7ab74a090f8502be13485db051f6dbc9a2a1cc46f
BLAKE2b-256 checksum
How to use checksums
6967697ddc67ea4fd32ba93fce9addd8337ef35bfb252c73ef56c0535c7144a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release history Release notifications | RSS feed

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page