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:
- nur.cli.main(argv=None)[source]#
Entry point: parse args with Typer, returning an int instead of exiting.
Typer/Click raise
SystemExitfor help, version, and every command exit; catching it here keepsmain’s int contract so it stays usable from tests and embedders. Passthrough args are handed to the command viactx.obj.
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
Execution (nur.core.execution)#
Runs a resolved task’s command, propagating its exit code.
- class nur.core.execution.ProcessRunner[source]#
Bases:
objectRuns 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
FileNotFoundErrorescape as a traceback, report a controlled error and return127.
Shell (nur.core.shell)#
Quotes literal arguments for the shell that runs shell tasks.
- 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
echoprint 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.
Models (nur.core.models)#
Core data types shared across discovery, the registry, and execution.
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, rake, grunt, gulp, ant, turbo, nx, workspace).
- nur.core.providers.npm.resolve_pm(cwd)[source]#
Return the package manager owning
cwd(npmwhen unknown).- Parameters:
cwd (Path)
- Return type:
- nur.core.providers.just.parse_justfile(text, source_file='justfile')[source]#
Extract recipes from
justfiletext without executing anything.Deliberately does NOT shell out to
just --dump: a pure text parse needs nojustbinary 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).
- 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 dynamicsh: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 resolvingincludes:or generated tasks.
- 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, whoseidkeys can be unrelated configuration.
- class nur.core.providers.tox.ToxProvider[source]#
Bases:
objectTask provider for tox test automation configurations.
- nur.core.providers.moon.parse_moon(text, source_file='moon.yml')[source]#
Parse a moon project’s
moon.ymltext 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.
- 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/.
- class nur.core.providers.vscode.VsCodeProvider[source]#
Bases:
objectTask provider for VS Code’s version 2.0.0 tasks configuration.
- nur.core.providers.nox.parse_noxfile(text, source_file='noxfile.py')[source]#
Extract nox sessions from text by AST parsing, never importing it.
nox --listimports and executesnoxfile.pyto 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), andif/tryblocks made of those, where araiseends its branch. Sessions register only from top-leveldefstatements. 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.parametrizevariants are surfaced under their base name (nox -s <name>still runs every variant).
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.
- nur.core.providers.rake.parse_rakefile(text, source_file='Rakefile')[source]#
Extract literal task candidates without evaluating Ruby behavior.
Direct task/multitask declarations and literal namespaces are supported. Parentheses and handler-free begin wrappers are transparent. Other scopes are opaque. Parser errors skip the file; Ruby semantic validity, execution order, method overrides and successful task registration are not inferred.
Discover Turborepo tasks by parsing turbo.json, without running turbo.
- nur.core.providers.turbo.parse_turbo(text, source_file='turbo.json')[source]#
Parse a workspace root’s
turbo.jsontext without executing anything.Deliberately does NOT run
turbo run(or--dry-run) to enumerate tasks: that boots Turborepo’s daemon and resolves the package graph, which can run repository-controlled code during discovery. Reading the task keys directly is safe, at the cost of not verifying that a memberpackage.jsonactually implements each script.definitionstays empty becauseturbo.jsonholds orchestration config (dependsOn,outputs), not the command itself.
Discover Nx shared tasks by parsing nx.json, without running nx.
- nur.core.providers.nx.parse_nx(text, source_file='nx.json')[source]#
Parse a workspace root’s
nx.jsontext without executing anything.Deliberately does NOT run
nx show projectsto enumerate tasks: that loads Nx plugins and infers targets, which can run repository-controlled code during discovery. Reading thetargetDefaultskeys directly is safe, at the cost of missing per-project-only targets. Only literal target names become tasks: executor keys (@nx/js:tsc) and globs (e2e-ci--**/**) cannot be run vianx run-many -tand are skipped, while array-form defaults (filtered entries for one target) are accepted like plain objects. Results are syntactic candidates, not a promise that every project implements them.
Discover Grunt tasks by parsing Gruntfile.js, without running Grunt.
- nur.core.providers.grunt.parse_gruntfile(text, source_file='Gruntfile.js')[source]#
Extract literal Grunt task candidates without evaluating JavaScript.
Direct
grunt.registerTask/grunt.registerMultiTaskcalls (and theirgrunt.task.*aliases) with a literal name are supported, at any nesting level — conditional guards are not evaluated, so guarded declarations are still listed. Task bodies and alias lists stay opaque; plugin-loaded tasks (loadNpmTasks), renamed tasks, external task files, computed/dynamic names, andtask:targetspellings are skipped. Parser errors skip the file. Results are syntactic task candidates, not a promise that Grunt will register or successfully execute each task.
Discover Gulp tasks by parsing gulpfile.js, without running Gulp.
- nur.core.providers.gulp.parse_gulpfile(text, source_file='gulpfile.js')[source]#
Extract literal Gulp task candidates without evaluating JavaScript.
Supported: literal
gulp.task('name', ...)/ baretask('name', ...)calls, CommonJSexports.name/module.exports.name/exports['name']assignments, and ESMexport function/export const/export default/export {a as b}forms.<task>.descriptionstring assignments supply optional metadata. Function references (task(build)),series()/parallel()composition, computed keys, object-literalmodule.exports = {...}, re-exports, and task bodies stay opaque. Conditional guards are not evaluated. Parser errors skip the file. Results are syntactic task candidates, not a promise that Gulp will register or run each task.
Discover Apache Ant targets by parsing build.xml, without running Ant.
- nur.core.providers.ant.parse_build_xml(text, source_file='build.xml')[source]#
Extract runnable entry names from
build.xmlwithout executing anything.Deliberately does NOT shell out to
ant -p: the file is declarative XML, so<target name=...>attributes can be read directly and a task body (a sequence of XML tasks, not shell commands) never runs during discovery.<extension-point name=...>entries are listed the same way: they are target-like states with a name and an optional description, each running asant <name>. Descriptions come from thedescriptionattribute; every entry is listed, including ones without a description (whichant -phides as internal). Names are kept verbatim — Ant permits spaces in target names andargv_basepasses the name as a single token, so normalizing would change the target’s identity. Only a missing name or an actually empty one is skipped (Ant rejects the latter); anything else, including whitespace-only names, stays listed exactly as Ant accepts it. Names beginning with a hyphen are skipped: Ant parses such arguments as options, so they cannot run viaant <name>(the conventional internal-target form); the check mirrors Ant’s ownstartsWith("-")on the same verbatim token, so padded variants (`` -init) stay listed. Only core-namespace ``<target>/<extension-point>elements count: other namespaces are ordinary task/type uses, not entry points. Documents whose root is not an Ant<project>element are skipped: Ant rejects them before installing any target. Task bodies stay opaque, sodefinitionis empty. Imported/included build files,macrodef-defined behaviour,if/unlessconditions,dependsordering, and${property}interpolation are not resolved; names are left literal. Parser errors skip the file. Files with a<!DOCTYPE>declaration are skipped as well: it is the only carrier of<!ENTITY>definitions, whose expansion is unbounded, and genuine Ant build files never need one. Detection is an expat pre-scan that aborts at the first declaration, so no entity is ever expanded during discovery. Results are syntactic task candidates, not a promise that Ant will accept or successfully execute each target.Byte input is preferred: ElementTree honours the XML encoding declaration only for bytes; a str carrying one is skipped as unparsable. Byte input in an encoding outside the stdlib’s native set (multibyte ones like Shift_JIS, UTF-32, or EBCDIC variants, which Ant accepts) is transcoded to UTF-8 first.
Discover workspace member scripts by parsing configs, without traversal tools.
- nur.core.providers.workspace.member_scripts(cwd)[source]#
Discover workspace member scripts without executing anything.
Globs come from
pnpm-workspace.yaml(packages),package.json(workspaces), andlerna.json(packages, defaulting topackages/*unlessuseWorkspacesdefers topackage.json). Matching stays confined tocwd: absolute patterns, escapes, andnode_modules/.gitnever become members, and only directories with their ownpackage.jsonqualify. Member scripts are the same static JSON thenpmprovider reads; running delegates to the lockfile-resolved package manager scoped to the member.
- 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 asmask test lint. windows selects whetherpowershell/batch/cmdblocks count, as they do only in mask’s Windows build.
TUI (nur.tui)#
Textual terminal user interface for browsing and running tasks.
- class nur.tui.app.HeaderItem(prefix)[source]#
Bases:
ListItemInitialize a Widget.
- Parameters:
- 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.