Modules#

An overview of the packages that make up nur. The API reference below is generated automatically from the source docstrings.

CLI (nur.cli)#

Typer command-line interface: discovers project tasks and either runs one directly or launches the TUI.

CLI package for the project.

nur.cli.format_list(registry)[source]#

Render the discovered tasks grouped by provider prefix.

Parameters:

registry (Registry)

Return type:

str

nur.cli.main(argv=None)[source]#

Entry point: parse args with Typer, returning an int instead of exiting.

Typer/Click raise SystemExit for help, version, and every command exit; catching it here keeps main’s int contract so it stays usable from tests and embedders. Passthrough args are handed to the command via ctx.obj.

Parameters:

argv (list[str] | None)

Return type:

int

nur.cli.split_passthrough(argv)[source]#

Split argv on the first -- into task args and passthrough args.

Parameters:

argv (list[str])

Return type:

tuple[list[str], list[str]]

Discovery (nur.core.discovery)#

Walks the working directory and builds the task registry from every supported source file.

Registry (nur.core.registry)#

Holds discovered tasks and resolves a query to a single task, with ambiguity and close-match handling.

exception nur.core.registry.AmbiguousTaskError(name, candidates)[source]#

Bases: ResolutionError

Parameters:
Return type:

None

exception nur.core.registry.ResolutionError[source]#

Bases: Exception

Base class for name-resolution failures.

exception nur.core.registry.UnknownTaskError(query, suggestions)[source]#

Bases: ResolutionError

Parameters:
Return type:

None

Execution (nur.core.execution)#

Runs a resolved task’s command, propagating its exit code.

class nur.core.execution.ProcessRunner[source]#

Bases: object

Runs a command with combined output captured and streamed line-by-line.

Blocking; intended to be driven from a background thread by the TUI.

nur.core.execution.run_direct(task, extra_args, cwd)[source]#

Run a task with inherited stdio; return the child’s exit code.

A natively-parsed provider (npm/PDM/poe) can surface a task whose runner binary is not installed. Rather than letting FileNotFoundError escape as a traceback, report a controlled error and return 127.

Parameters:
Return type:

int

Shell (nur.core.shell)#

Quotes literal arguments for the shell that runs shell tasks.

nur.core.shell.quote(s)[source]#

Return a shell-escaped version of the string s.

nur.core.shell.quote_for_cmd(argument)[source]#

Quote one argument for cmd.exe and the MSVC argv parser, only if needed.

Plain tokens stay bare, since cmd.exe built-ins such as echo print quotes verbatim. Anything with whitespace, quotes, or cmd.exe operators such as & or > is double-quoted so it stays one literal token. cmd.exe expands %NAME% even inside quotes, so each % is emitted as ^% between quoted pieces: the caret keeps it literal, and the quote characters around it stop the text from naming a real variable.

Parameters:

argument (str)

Return type:

str

Models (nur.core.models)#

Core data types shared across discovery, the registry, and execution.

class nur.core.models.Provider(*args, **kwargs)[source]#

Bases: Protocol

class nur.core.models.Task(name: 'str', prefix: 'str', argv_base: 'tuple[str, ...]', description: 'str | None' = None, definition: 'str' = '', source_file: 'str' = '', passthrough_prefix: 'tuple[str, ...]' = (), run_in_shell: 'bool' = False)[source]#

Bases: object

Parameters:

Providers (nur.core.providers)#

One module per supported task source (make, npm, deno, composer, just, taskfile, pre-commit, pdm, poe, tox, mise, cargo-make, moon, xc, VS Code tasks, nox, mask, invoke).

nur.core.providers.make.parse_targets(text)[source]#

Extract target names from Makefile text without executing anything.

Deliberately does NOT shell out to make: the database dump (make -pRrq) still evaluates $(shell ...) / != assignments while reading the file, so a repository’s Makefile could run arbitrary commands merely by discovering/listing tasks. Text parsing is safe, at the cost of not resolving include directives or computed targets.

Parameters:

text (str)

Return type:

list[str]

nur.core.providers.just.parse_justfile(text, source_file='justfile')[source]#

Extract recipes from justfile text without executing anything.

Deliberately does NOT shell out to just --dump: a pure text parse needs no just binary on PATH and cannot run recipe bodies. Descriptions come from the comment immediately preceding a recipe (skipping any attribute lines) or from an explicit [doc('...')] attribute. Grammar fidelity is intentionally partial: imports, string interpolation, and computed names are not resolved (see docs/adr/0001).

Parameters:
  • text (str)

  • source_file (str)

Return type:

list[Task]

nur.core.providers.task.parse_taskfile(text, source_file='Taskfile.yml')[source]#

Parse a Taskfile’s YAML text without executing anything.

Deliberately does NOT run task --list: go-task evaluates dynamic sh: variables while loading/compiling the file, so listing could run repository-controlled commands during discovery. Reading the YAML directly is safe, at the cost of not resolving includes: or generated tasks.

Parameters:
  • text (str)

  • source_file (str)

Return type:

list[Task]

nur.core.providers.pre_commit.parse_pre_commit_config(text, source_file='.pre-commit-config.yaml')[source]#

Parse a pre-commit config without invoking pre-commit.

Only repos[].hooks[] declares hooks. In particular, this deliberately does not walk arbitrary YAML mappings, whose id keys can be unrelated configuration.

Parameters:
  • text (str)

  • source_file (str)

Return type:

list[Task]

class nur.core.providers.tox.ToxProvider[source]#

Bases: object

Task provider for tox test automation configurations.

detect(cwd)[source]#

Check whether a supported tox configuration exists in the directory.

Parameters:

cwd (Path)

Return type:

bool

discover(cwd)[source]#

Discover runnable tox environments as tasks from configuration.

Parameters:

cwd (Path)

Return type:

list[Task]

nur.core.providers.moon.parse_moon(text, source_file='moon.yml')[source]#

Parse a moon project’s moon.yml text without executing anything.

Deliberately does NOT run moon query tasks: enumerating tasks that way boots moon’s toolchain and resolves inherited config, which can run repository-controlled commands during discovery. Reading the YAML directly is safe, at the cost of not resolving inherited tasks from .moon/ or project-qualified <project>:<task> addressing across the monorepo.

Parameters:
  • text (str)

  • source_file (str)

Return type:

list[Task]

nur.core.providers.xc.parse_xc(text, source_file='README.md')[source]#

Parse xc tasks out of a markdown text without executing anything.

Deliberately does NOT run xc -s: xc executes task scripts, and listing should never be able to run repository-controlled commands. The grammar is documented at https://xcfile.dev/task-syntax/task-list/.

Parameters:
  • text (str)

  • source_file (str)

Return type:

list[Task]

class nur.core.providers.vscode.VsCodeProvider[source]#

Bases: object

Task provider for VS Code’s version 2.0.0 tasks configuration.

detect(cwd)[source]#

Check whether a supported VS Code tasks file exists.

Parameters:

cwd (Path)

Return type:

bool

discover(cwd)[source]#

Discover shell and process tasks nur can run as VS Code would.

Parameters:

cwd (Path)

Return type:

list[Task]

nur.core.providers.nox.parse_noxfile(text, source_file='noxfile.py')[source]#

Extract nox sessions from text by AST parsing, never importing it.

nox --list imports and executes noxfile.py to build its registry, so discovery reads @nox.session-decorated functions from the syntax tree instead. It only trusts straight-line noxfiles: module code may use imports, plain statements (assignment, del, expression, pass, def, class), and if/try blocks made of those, where a raise ends its branch. Sessions register only from top-level def statements. Anything else (loops, with, match, assert, except*, a star import from another module) makes nox’s registry depend on control flow nur does not follow, so nothing is listed rather than a session nox may not register. python=[...] and @nox.parametrize variants are surfaced under their base name (nox -s <name> still runs every variant).

Parameters:
  • text (str)

  • source_file (str)

Return type:

list[Task]

Discover Invoke tasks by parsing Python, without importing project code.

nur.core.providers.invoke.parse_tasks(text, source_file='tasks.py')[source]#

Read top-level decorated functions and literal names/aliases.

Imports are tracked in source order, never executed. Nested/conditional definitions, custom decorators, computed metadata and Collection wiring are not resolved. Task bodies and pre/post hooks remain Invoke’s concern.

Parameters:
  • text (str)

  • source_file (str)

Return type:

list[Task]

nur.core.providers.mask.parse_mask(text, source_file='maskfile.md', *, windows=False)[source]#

Parse mask commands out of a maskfile text without executing anything.

Mirrors mask’s own parser (https://github.com/jacobdeichert/mask): headings are commands, nested headings are subcommands, the last fenced block under a heading is its script, and the last blockquote its description. A subcommand is named by its path (test lint) and runs as mask test lint. windows selects whether powershell/batch/ cmd blocks count, as they do only in mask’s Windows build.

Parameters:
Return type:

list[Task]

TUI (nur.tui)#

Textual terminal user interface for browsing and running tasks.

class nur.tui.app.HeaderItem(prefix)[source]#

Bases: ListItem

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • prefix (str)

can_focus = False#

Widget may receive focus.

can_focus_children = True#

Widget’s children may receive focus.

class nur.tui.app.NurApp(cwd, scan)[source]#

Bases: App[None]

Create an instance of an app.

Parameters:
  • driver_class – Driver class or None to auto-detect. This will be used by some Textual tools.

  • css_path – Path to CSS or None to use the CSS_PATH class variable. To load multiple CSS files, pass a list of strings or paths which will be loaded in order.

  • watch_css – Reload CSS if the files changed. This is set automatically if you are using textual run with the dev switch.

  • ansi_color – Allow ANSI colors if True, or convert ANSI colors to RGB if False, None to use “ansi” parameter from themes.

  • cwd (Path)

  • scan (Callable[[], Registry])

Raises:

CssPathError – When the supplied CSS path(s) are an unexpected type.

CSS_PATH = 'app.tcss'#

File paths to load CSS from.

BINDINGS = [('q', 'quit', 'quit'), ('escape', 'quit', 'quit'), ('slash', 'focus_filter', 'filter'), ('j', 'next', 'down'), ('k', 'prev', 'up'), ('r', 'run', 'run'), Binding(key='ctrl+c', action='interrupt', description='interrupt', show=True, key_display=None, priority=True, tooltip='', id=None, system=False, group=None), ('question_mark', 'toggle_help', 'help')]#

The default key bindings.

compose()[source]#

Yield child widgets for a container.

This method should be implemented in a subclass.

Return type:

Iterable[Widget]

async action_quit()[source]#

An [action](/guide/actions) to quit the app as soon as possible.

Return type:

None

class nur.tui.app.TaskItem(task)[source]#

Bases: ListItem

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • task (Task)

can_focus = False#

Widget may receive focus.

can_focus_children = True#

Widget’s children may receive focus.