Functions
loadout ships 14 shell functions. Unlike aliases, these take arguments, branch, and loop — p resolves a path under your project root, msh parses flags, venv finds and activates an environment.
Every one is documented individually in the function catalog, generated from the shipped metadata.
Why they are functions
Section titled “Why they are functions”Two of them have no choice. p changes the shell’s directory and activate changes its environment, and a child process cannot do either to its parent. A compiled subcommand could print a cd for you to evaluate, but then it is not a command, it is a string you have to remember to eval.
The other twelve could be subcommands of the binary. They are not, because the generator already exists: once the tool emits shell for p, emitting shell for msh too costs nothing and keeps every capability in one place, discoverable the same way.
The trade is that function bodies are shell, and shell has to work in both dialects loadout supports. That constraint is real and has bitten — see Shell portability.
What each one needs
Section titled “What each one needs”Every function declares its requirements once:
pw: requires: - binary: code - config: workspacesThat single declaration drives three things, which is why they cannot disagree:
loadout doctorreports it when unmet.loadout describe pwprints the setup instructions.- The web interface shows a green, amber or red indicator.
Three kinds of requirement exist:
| Form | Means |
|---|---|
code | The binary must be on PATH |
path:{{project_root}} | The path must exist, after your configuration is substituted |
config:workspaces | That configuration section must be non-empty |
Calling one that is not ready
Section titled “Calling one that is not ready”You get a real error naming what is missing, not a silent failure:
pw: no workspaces configured add one under `workspaces:` in ~/.config/loadout/config.yaml run `loadout describe pw` for the full requirementsThis is the payoff for generating an entry rather than dropping it. An alias that vanishes because a tool is missing gives you “command not found” and no explanation.
The two that change your shell
Section titled “The two that change your shell”p changes directory to a project under your project root.
p # go to the projects rootp acme/api # go to <root>/acme/apiactivate sources a Python virtual environment, searching your venv_root and the current directory.
Both run in your shell rather than as a subprocess. That is not a design preference, it is the only thing that works.
Functions that take flags use getopts, and every one of them resets OPTIND first:
msh -m apimsh -k mykey -u ubuntu -p 2222msh -h # full flag referenceThe reset matters more than it looks. zsh localises OPTIND per function; bash does not. Without an explicit local OPTIND=1, the second call to a function in a bash session silently ignores its flags — no error, just the defaults. Every shipped function that parses flags has it.
Discovering them
Section titled “Discovering them”loadout catalog # includes functionsloadout describe msh # summary, usage, requirements, config it readsloadout describe # list everything describabledescribe resolves requirements against the machine you run it on, so it tells you what is actually missing here rather than what could theoretically be missing.