CLI reference

Every metastack verb, with arguments, flags, and behavior.

Global flags

--config — path to metastack.yaml. Default: walk up from the current directory until found.

init

Scaffold a new metastack workspace in the current directory (or in ./<name> if a name is passed). Writes metastack.yaml and .gitignore. Refuses to overwrite either if they already exist. Does not run git init.

metastack init [name] [-i | --interactive]
name (positional, optional) — if provided, scaffold into ./<name> instead of the current directory. The directory is created if missing; an error is raised if it exists and is non-empty.
-i, --interactive — prompt for workspace name, starter repos, and which checks to enable. Default: false.

add <owner>/<repo>

Register a new managed repo and clone it. Appends an entry to metastack.yaml (preserving comments) and clones into ./repos/<name> via gh repo clone. Idempotent — if the repo name is already in config, the YAML edit is skipped but the clone is still attempted.

metastack add <owner>/<repo>
metastack add my-org/my-cli

clone

Clone any managed repos missing from the workspace. For each repo in metastack.yaml: skip if already cloned, otherwise gh repo clone <url> repos/<name>. Idempotent.

metastack clone

doctor

Verify all tools listed in metastack.yaml are present. Runs every check (does not stop after the first failure) so the full picture is visible in one pass. Exits non-zero if any required check fails.

metastack doctor

Output convention: = pass, = fail and required, = fail and optional.

exec -- <command>...

Run a shell command in each managed repo. Joins all positional args (after --) into a single bash command and runs it in each repo's directory. Per-repo non-zero exits are reported in the summary but do not abort the loop.

metastack exec -- <command>...
metastack exec -- git log -1 --oneline
metastack exec -- 'go test ./...'

fetch

Run git fetch --all --prune --quiet in each managed repo. Reports a per-repo / and a summary line; exits non-zero if any repo failed.

metastack fetch

pull

Run git pull --ff-only in each managed repo. Per-repo failures are reported; exits non-zero if any failed.

metastack pull

status

Print branch, ahead-behind vs upstream, and working-tree state per managed repo. Repos with no upstream show (no upstream); repos that aren't cloned are flagged.

metastack status

branch <name>

Create the branch <name> from the current HEAD in every managed repo and check it out. Repos that already have the branch are checked out (idempotent re-runs are safe). Fails fast if any in-scope repo has a dirty or untracked working tree — no refs are created in any repo if the pre-flight fails.

metastack branch <name> [--repo <a,b,...>]
--repo — comma-separated subset of managed repo names. Default: all repos in metastack.yaml.
metastack branch feat/billing
metastack branch feat/billing --repo api,web

tag [patch|minor|major]

For each managed repo: read the latest vX.Y.Z tag (baseline v0.0.0 if none), bump the chosen part, and create a new annotated tag locally. Default bump is patch. Refuses to overwrite an existing tag and fails fast if any in-scope repo has a dirty or untracked working tree.

metastack tag [patch|minor|major] [--repo <a,b,...>] [--push]
--repo — comma-separated subset of managed repo names. Default: all.
--push — push the new tags to origin after every in-scope repo has been tagged successfully. Tags are pushed in a second pass, so a mid-workspace failure leaves nothing pushed.
metastack tag minor                       # v0.1.0 -> v0.2.0 in every repo
metastack tag patch --repo api --push     # bump and publish a single repo

checkout [vX.Y.Z]

Detach each managed repo's HEAD onto an existing semver tag. With no argument, each repo lands on its own latest vX.Y.Z tag. With an argument, each repo checks out that exact tag if present; otherwise it falls back to the highest vX.Y.Z tag <= the requested version (per repo). Pass --exact to refuse the fallback and skip repos that lack the exact tag. Fails fast if any in-scope repo has a dirty or untracked working tree — no repo is moved if the pre-flight fails.

metastack checkout [vX.Y.Z] [--repo <a,b,...>] [--exact]
vX.Y.Z (positional, optional) — strict semver tag. A leading v is optional (1.2.3 is normalized to v1.2.3). Pre-release and build suffixes are rejected.
--repo — comma-separated subset of managed repo names. Default: all.
--exact — skip repos that don't have the exact requested tag (no <= fallback). Default: false.

Output markers: exact match, fell back to a lower tag, skipped or failed.

metastack checkout                        # latest tag per repo
metastack checkout v0.2.0                 # exact, or floor <= v0.2.0 per repo
metastack checkout v0.2.0 --exact         # skip repos lacking v0.2.0 exactly

checkout list

Inventory available semver tags per managed repo. Defaults to a compact table (REPO, LATEST, COUNT); use --all or --limit N to expand into per-repo blocks listing tags newest-first.

metastack checkout list [--repo <a,b,...>] [--all | --limit <n>]
--repo — comma-separated subset of managed repo names. Default: all.
--all — list every semver tag per repo (newest first). Default: false.
--limit — list the top N semver tags per repo (newest first). Implies the per-repo block view.
metastack checkout list                   # compact table
metastack checkout list --limit 5         # top 5 tags per repo
metastack checkout list --all --repo api  # every tag in one repo

update

Check for and install a new signed release. Fetches the manifest from the configured update channel, verifies the sha256 and ed25519 signature against the compiled-in trust list, and atomically replaces this binary.

metastack update [--check] [--snapshot | --latest | --version <version>]
--check — only check; don't download or apply.
--snapshot — fetch from the snapshot channel.
--latest — fetch from the latest channel.
--version — pin to a specific version (e.g. v0.1.0).

--snapshot, --latest, and --version are mutually exclusive.

Note: If metastack was installed via Homebrew, update defers to brew update && brew upgrade metastack instead of swapping the binary in place.