On this page
Invocation and flags
This page is the reference for pekit's command line: the grammar, how flag values are written, the flags every command accepts, and how pekit decides which recipe to run. For what each command does and how it selects targets, see Commands and targets.
Grammar
pekit [global flags] <command> [args and flags]
pekit [global flags] workspace [workspace flags] <command> [args and flags]
There is exactly one command per invocation, drawn from build, test,
install, package, publish, clean, and workspace. Anything before the
command that is not a recognised flag is an error (unknown_command /
unknown_flag); a missing command is missing_command.
Flags may appear before or after the command. Pekit parses flags the same way on both sides of the command word, so these are equivalent:
$ pekit --dry-run build
$ pekit build --dry-run
The one structural exception is workspace, whose tail is parsed specially: the
workspace-only flags (--fail-fast, --jobs) must come before the delegated
command, and everything after the delegated command is parsed as an ordinary
invocation of that command. See Workspaces.
-- stops flag parsing
A bare -- ends flag parsing. Every token after it is captured verbatim and
treated as a positional selector:
$ pekit build -- ./tools ./libs
Positional selectors may not begin with -. A leading-- token before -- is
read as a flag (and rejected as unknown if it is not one); after -- it is still
rejected as a selector with the diagnostic "use -- before selector-like
values". Use -- for selectors that merely look option-like (for example a
path), not to smuggle a leading dash into a selector.
Flag value forms
Flags fall into three shapes, and the shape decides how a value may be written.
| Shape | Examples | How a value is written |
|---|---|---|
| No-value | --dry-run, --quiet, --verbose, --json, --allow-unused |
Never takes a value. --flag=x is an error (unexpected_flag_value). |
| Required-value | --recipe, --workspace, --version (-V), --env, --keyring |
--flag value or --flag=value. |
| Optional-value | --local, --prefer-local, --no-build |
Value only via --flag=value. The bare form is valid and means "empty value". |
Required-value flags accept either the separated form (--recipe path) or the
= form (--recipe=path). With the separated form pekit consumes the next token
as the value — unless that token is missing, is --, or itself begins with -,
in which case the flag errors with missing_flag_value. With the = form an
empty value (--recipe=) is rejected the same way.
Optional-value flags take a value only through =. They never consume the
following token. So:
$ pekit build --local # --local with an empty value
$ pekit build --local=/src/mypkg # --local with a value
$ pekit build --local /src/mypkg # --local (empty) + /src/mypkg as a selector
-V is an alias for --version and behaves identically (it is a required-value
flag; it is not a version-print flag — pekit has no version banner and no
--help).
Per-key keyring values use a dotted-path form, --keyring.<path>=<value>, which
always requires the = and a non-empty path.
Global flags
Seven flags are parsed in any position regardless of command; six are accepted
by every command, and the seventh, --workspace, is only valid with the
workspace command. The
rest of pekit's flags (--version/-V, --latest, --local, --env,
--keyring, --all, and so on) are command-specific and are covered on
Commands and targets.
| Flag | Effect |
|---|---|
--recipe <path|dir> |
Use this recipe instead of searching upward from the cwd. |
--workspace <path|dir> |
Use this workspace file/root. Only valid with the workspace command. |
--allow-unused |
Downgrade "command does not support this recognised flag" from an error to a suppressed notice. |
--dry-run |
Build the plan and stop before any side effect. |
--quiet |
Suppress routine progress; print only warnings and result events. |
--verbose |
Emit extra staging/progress events. |
--json |
Emit machine-readable NDJSON events instead of the human log. |
Mutual-exclusion rules
Validation rejects contradictory combinations up front:
| Rejected combination | Diagnostic |
|---|---|
--quiet with --verbose |
--quiet and --verbose cannot be used together |
--quiet with --json |
--quiet and --json cannot be used together |
--recipe with --workspace |
--recipe and --workspace cannot be used together |
--workspace without the workspace command |
--workspace can only be used with the workspace command |
More than one of --version / --latest / --all-versions |
mutually exclusive |
--local with --prefer-local |
mutually exclusive |
--output-only with --target-only (clean) |
mutually exclusive |
--allow-unused
Every command declares which flags it accepts. Passing a recognised flag that
the effective command does not support is normally an error
(unsupported_flag), for example version selection on clean, or --all on
build. --allow-unused turns each such case into a non-fatal notice: pekit
records it and emits an unused_suppressed event at the start of the run instead
of aborting.
--allow-unused only relaxes the "recognised-but-unused" check. It does not
excuse:
- unknown flags (
unknown_flag) or unknown commands (unknown_command); - malformed flag values — missing values, empty
=values, or=valueon a no-value flag; - the mutual-exclusion rules above;
- bad selectors (a selector beginning with
-, or more selectors than the command accepts).
This is what lets you fan a shared set of flags across a whole workspace even when some members' commands ignore some of the flags.
--dry-run
--dry-run runs the same planning pipeline as a real invocation — it locates and
loads the recipe, resolves the version selector, and prepares source state — but
stops at every side-effecting boundary instead of crossing it. In place of the
action pekit emits a plan event:
- a target that would run emits
target_plan("would run target") and does not execute the command or touch the stage directory; cleanthat would delete managed output emitsclean_plan("would remove managed output") and removes nothing;package/publishemit their plan events rather than writing or shipping artifacts.
Use --dry-run to inspect exactly what a command would do. Combine it with
--json to get the plan as a single structured object (below).
Output renderers
The renderer is chosen from the flags. --json selects the JSON renderer;
otherwise the human renderer is used, and --quiet puts it in quiet mode.
Human (default)
Pekit's own events print as timestamped lines, [15:04:05] message, optionally
prefixed with a context tag (member, command, target, package, version) when the
event carries one. Live output from the targets pekit runs is streamed through
with a [member target version] prefix, preserving each line's original stream —
stdout stays on stdout, stderr stays on stderr. Fatal errors are written to
stderr.
--quiet
Quiet mode keeps the human format but drops routine progress, emitting only
warning, artifact, publish, and workspace_summary events. Target output
is unaffected. --quiet cannot be combined with --verbose or --json.
--verbose
Verbose mode adds extra events (such as per-target stage preparation) to the human log. It is otherwise the human renderer.
--json
--json emits newline-delimited JSON — one event object per line — to stdout, so
the run can be consumed by another program. Each object has a type and a
timestamp plus whatever context fields apply; target output arrives as
target_output events carrying the stream and text. Errors are emitted as an
error event.
--dry-run --json is special-cased: instead of streaming events, pekit buffers
them and, at the end, prints a single plan object:
{"type":"plan","time":"...","command":"build","workspace":false,"events":[ ... ]}
The events array holds the buffered plan events; command is the effective
command and workspace is whether the run went through the workspace command.
Exit codes
Pekit uses two exit codes only:
| Code | Meaning |
|---|---|
0 |
Success. |
1 |
Any error — bad invocation, missing recipe, failed build, and so on. |
There are no finer-grained codes, and there is no --help or help command:
this reference and Commands and targets
are the manual.
How a recipe is located
Pekit resolves exactly one recipe per (non-workspace) run:
--recipe <path|dir>— if given, this wins. A directory means<dir>/pekit.toml; a file path is used as-is. A missing target ismissing_recipe.- A remote positional — if
--recipewas not given and the first positional argument looks like a remote locator, it is taken as the recipe (and dropped from the selector list). - Walking up — otherwise pekit searches from the current directory upward
for the nearest
pekit.toml, stopping at the filesystem root (not_foundif none is found).
Once the recipe root is known, and unless --workspace was supplied, pekit also
walks upward from that root for a workspace.pekit.toml; if one is found the
recipe is treated as a member of that workspace.
Remote recipe locators
A remote locator is a recipe reference that points at a git repository rather
than a local path. A value is treated as remote when it does not start with .
or / and it either begins with github.com/, contains ://, ends with .git,
or contains a // subdirectory separator. Examples:
$ pekit build github.com/acme/tools
$ pekit build github.com/acme/tools//subdir@v1.2.0
$ pekit build https://git.example.org/acme/tools.git//pkg@main
The locator may carry a //subdir to select a directory inside the repository and
an @ref (branch, tag, or commit) to pin a revision. When pekit sees a remote
locator — whether as the positional or as the value of --recipe — it
materialises it: it mirror-clones the repository into a per-user cache under
the system temp directory (fetching updates if already cached), checks out the
resolved commit into a clean working tree, and then treats the checkout (plus any
//subdir) as the recipe root. From there the run proceeds exactly as it would
for a local recipe.