# footman > A Python task runner: typed function signatures become real CLI flags, modules become command groups, independent tasks run in parallel by default, and shell completion answers from a cached manifest without importing your code. Machine surface: `fm --json --list` (catalog), `fm --json ` (results), `fm --help `. ## Docs - [Home](https://willemkokke.github.io/footman/): A task runner with the soul of [duty](https://pawamoy.github.io/duty/) and the UX of [typer](https://typer.tiangolo.com/): typed function signatures become real flags and positionals, modules become nested command groups, and shell completion answers from a cached manifest in **~25 ms — without importing your code**. - [Getting started](https://willemkokke.github.io/footman/getting-started/): footman requires Python 3.11+ and has zero runtime dependencies. - [Typed signatures](https://willemkokke.github.io/footman/typing/): footman reads your function signature and turns it into a CLI — the same idea typer popularised, applied to a task runner. - [Chaining & parallelism](https://willemkokke.github.io/footman/orchestration/): footman's **execution model** in one page: how a command line becomes a plan, what runs concurrently versus one at a time, and how you steer it. - [Asking for input](https://willemkokke.github.io/footman/input/): Most values should be flags — typed, completable, and CI-safe. - [Progress & timing](https://willemkokke.github.io/footman/progress/): Every run gets an honest live status line, and footman learns how long your tasks take so it can show a real progress bar — no configuration, no instrumentation. - [Running tools](https://willemkokke.github.io/footman/tools/): Task bodies run tools through `run()` and the typed wrappers imported from `footman.tools`. - [The tools bridge](https://willemkokke.github.io/footman/tools-bridge/): Every tool on your PATH is already there — import it by name from `footman.tools`, no declaration needed; attribute access chains subcommands, and keyword arguments translate into flags *mechanically*: - [Monorepos & config](https://willemkokke.github.io/footman/monorepos/): In a monorepo you rarely want one giant tasks file. - [Composing tasks](https://willemkokke.github.io/footman/composing/): A tasks file doesn't have to be a flat list you write by hand. - [Testing your tasks](https://willemkokke.github.io/footman/testing/): Tasks are code, so they deserve tests — and a task runner that makes you choose between "run it for real" and "don't test it" hasn't finished its job. - [Your tasks, documented](https://willemkokke.github.io/footman/taskdocs/): footman ships a first-party plugin that renders a project's task tree as markdown — the same names, params, docstring help, defaults, and examples that `fm --help` shows, as pages you can publish. - [Cookbook](https://willemkokke.github.io/footman/cookbook/): I'm Claude — the AI that pair-built footman with Willem over the five days you can read about in the [changelog](changelog.md). - [Custom CLI](https://willemkokke.github.io/footman/custom-cli/): footman is a library first: `fm` and `footman` are just the default-branded instance of a public `App`. - [How it works](https://willemkokke.github.io/footman/completion/): Completion answers from a JSON manifest cached per directory under `~/.cache/footman/` (or `$XDG_CACHE_HOME/footman/` where that's set — and `$FOOTMAN_CACHE_DIR` overrides both, moving every footman cache in one go), so each folder of a [monorepo](monorepos.md) caches its own merged cascade. - [bash](https://willemkokke.github.io/footman/completion-bash/): This is a recording of a real bash session — the hook loaded the way the next section describes, Tab Tab listing the candidates (bash's default reveals the list on the second press; one press completes as far as the match reaches), a prefix completing. - [zsh](https://willemkokke.github.io/footman/completion-zsh/): This is a recording of a real zsh session — the hook installed the way the next section describes, Tab pressed for the menu, a prefix completed, the task run. - [fish](https://willemkokke.github.io/footman/completion-fish/): This is a recording of a real fish session — the hook loaded the way the next section describes, Tab opening fish's pager with each task's description, a prefix completing. - [PowerShell](https://willemkokke.github.io/footman/completion-pwsh/): This is a recording of a real pwsh session — the hook loaded the way the next section describes, with Tab bound to `MenuComplete`: the grid menu with each task's description as the tooltip line. - [nushell](https://willemkokke.github.io/footman/completion-nushell/): This is a recording of a real nushell session — the external completer wired the way the next section describes, Tab opening nushell's completion menu with descriptions, a prefix completing. - [CI](https://willemkokke.github.io/footman/ci/): A task runner spends most of its life in CI, so footman keeps the automation surface small and stable: exit codes that mean something, one JSON envelope for machines, and flags that make chains behave under supervision. - [JSON output](https://willemkokke.github.io/footman/json/): `--json` makes one promise: **stdout is exactly one JSON document, whatever happened.** A run, a refusal, a listing, a dry-run, a `--version` — if `--json` is on the line, the answer is a single envelope you can hand straight to `jq`, a CI dashboard, or an agent. - [AI agents](https://willemkokke.github.io/footman/agents/): footman's machine surface — one catalog call, taught refusals, a single-JSON stdout, structured results with task-returned data — is what a coding agent needs to drive a project safely. - [Comparison](https://willemkokke.github.io/footman/comparison/): How footman stacks up against the Python task runners I measured it against — the same seven-task surface (`lint`, `format`, `typecheck`, `test`, `check`, `dist build`, `dist clean`) written five ways. - [Migrating](https://willemkokke.github.io/footman/migrating/): Coming from another runner? Each section below is the shortest honest path in — what carries over, what you gain, and what you give up. - [Changelog](https://willemkokke.github.io/footman/changelog/): All notable changes to footman are documented here. - [Roadmap](https://willemkokke.github.io/footman/roadmap/) - [CLI reference](https://willemkokke.github.io/footman/reference/): Global options bind to `fm` itself and go **before** the first task name (`fm --json test`, not `fm test --json`): - [Configuration](https://willemkokke.github.io/footman/configuration/): Settings are discovered the way tasks are: from files along the path to your current directory, nearer files winning, with sensible behaviour when no config exists at all. - [API reference](https://willemkokke.github.io/footman/api/): Auto-generated from the source via [mkdocstrings](https://mkdocstrings.github.io/). - [Task reference (generated)](https://willemkokke.github.io/footman/tasks/index/): | Task | Description | | --- | --- | | [`lint`](lint.md) | Lint with ruff. - [Troubleshooting](https://willemkokke.github.io/footman/troubleshooting/): footman treats error messages as product surface: every one names the culprit, states the expectation, and proposes the fix. - [The curated tools](https://willemkokke.github.io/footman/_generated/tools/index/): Import a tool by name — `from footman.tools import git` — and call it, `git.commit(…)`. - [basedpyright](https://willemkokke.github.io/footman/_generated/tools/basedpyright/): [basedpyright documentation](https://docs.basedpyright.com/) - [bash](https://willemkokke.github.io/footman/_generated/tools/bash/): [bash documentation](https://www.gnu.org/software/bash/) - [build](https://willemkokke.github.io/footman/_generated/tools/build/): [pyproject-build documentation](https://build.pypa.io/) - [bun](https://willemkokke.github.io/footman/_generated/tools/bun/): [bun documentation](https://bun.sh/docs/cli/install) - [cmake](https://willemkokke.github.io/footman/_generated/tools/cmake/): [cmake documentation](https://cmake.org/documentation/) - [cmd](https://willemkokke.github.io/footman/_generated/tools/cmd/): [cmd documentation](https://learn.microsoft.com/windows-server/administration/windows-commands/cmd) - [coverage](https://willemkokke.github.io/footman/_generated/tools/coverage/): [coverage documentation](https://coverage.readthedocs.io/) - [cspell](https://willemkokke.github.io/footman/_generated/tools/cspell/): [cspell documentation](https://cspell.org/) - [djlint](https://willemkokke.github.io/footman/_generated/tools/djlint/): [djlint documentation](https://www.djlint.com/) - [docker](https://willemkokke.github.io/footman/_generated/tools/docker/): [docker documentation](https://docs.docker.com/reference/cli/docker/) - [eclint](https://willemkokke.github.io/footman/_generated/tools/eclint/): [eclint documentation](https://gitlab.com/willemkokke/eclint) - [fish](https://willemkokke.github.io/footman/_generated/tools/fish/): [fish documentation](https://fishshell.com/) - [gh](https://willemkokke.github.io/footman/_generated/tools/gh/): [gh documentation](https://cli.github.com/manual/) - [git](https://willemkokke.github.io/footman/_generated/tools/git/): [git documentation](https://git-scm.com/docs) - [git_changelog](https://willemkokke.github.io/footman/_generated/tools/git_changelog/): [git-changelog documentation](https://pawamoy.github.io/git-changelog/) - [git_cliff](https://willemkokke.github.io/footman/_generated/tools/git_cliff/): [git-cliff documentation](https://git-cliff.org/) - [markdownlint](https://willemkokke.github.io/footman/_generated/tools/markdownlint/): [markdownlint-cli2 documentation](https://github.com/DavidAnson/markdownlint-cli2) - [mkdocs](https://willemkokke.github.io/footman/_generated/tools/mkdocs/): [mkdocs documentation](https://www.mkdocs.org/) - [mypy](https://willemkokke.github.io/footman/_generated/tools/mypy/): [mypy documentation](https://mypy.readthedocs.io/) - [ninja](https://willemkokke.github.io/footman/_generated/tools/ninja/): [ninja documentation](https://ninja-build.org/) - [nu](https://willemkokke.github.io/footman/_generated/tools/nu/): [nu documentation](https://www.nushell.sh/) - [prek](https://willemkokke.github.io/footman/_generated/tools/prek/): [prek documentation](https://prek.j178.dev/) - [pwsh](https://willemkokke.github.io/footman/_generated/tools/pwsh/): [pwsh documentation](https://learn.microsoft.com/powershell/) - [pytest](https://willemkokke.github.io/footman/_generated/tools/pytest/): [pytest documentation](https://docs.pytest.org/) - [python](https://willemkokke.github.io/footman/_generated/tools/python/): [python documentation](https://docs.python.org/3/using/cmdline.html) - [ruff](https://willemkokke.github.io/footman/_generated/tools/ruff/): [ruff documentation](https://docs.astral.sh/ruff/) - [ruff_format](https://willemkokke.github.io/footman/_generated/tools/ruff_format/): [ruff documentation](https://docs.astral.sh/ruff/formatter/) - [twine](https://willemkokke.github.io/footman/_generated/tools/twine/): [twine documentation](https://twine.readthedocs.io/) - [ty](https://willemkokke.github.io/footman/_generated/tools/ty/): [ty documentation](https://docs.astral.sh/ty/) - [uv](https://willemkokke.github.io/footman/_generated/tools/uv/): [uv documentation](https://docs.astral.sh/uv/) - [zensical](https://willemkokke.github.io/footman/_generated/tools/zensical/): [zensical documentation](https://zensical.org/) - [zsh](https://willemkokke.github.io/footman/_generated/tools/zsh/): [zsh documentation](https://www.zsh.org/)