Configuration
The metastack.yaml schema — single source of truth for a workspace.
metastack finds the workspace by walking up from the current directory until it sees a metastack.yaml. You can override that with --config <path> on any verb.
Schema
Two required top-level keys (repos, checks, both sequences) and one optional scalar, provider:
# optional: gh (default) | glab | git
provider: gh
repos:
- name: my-cli
url: my-org/my-cli
- name: my-daemon
url: my-org/my-daemon
checks:
- name: gh
command: gh auth status
required: true
- name: git
command: git --version
required: true
- name: go
command: go version
min_version: "1.25"
required: true
- name: docker
command: docker --version
required: false
provider
Which tool metastack clone shells out to. Set it once at the top level as the workspace default, and on any individual repo entry to override it. Unset means gh, so existing configs keep working unchanged.
gh— GitHub CLI:gh repo clone <url> repos/<name>.urlis<owner>/<repo>or any URLghaccepts.glab— GitLab CLI:glab repo clone <url> repos/<name>.urlis<group>/<project>or any URLglabaccepts.git— plaingit clone <url> repos/<name>.urlmust be a full ssh or https clone URL.
A mixed workspace:
provider: gh
repos:
- name: metastack
url: StackCube/metastack # inherits gh
- name: infra
url: infra-group/infra
provider: glab # per-repo override
- name: vendored
url: git@gitea.internal:tools/vendored.git
provider: git
Any other value fails when the config is loaded, before any verb does work. metastack init --provider glab writes a starter config with the matching glab auth status check; metastack add --provider git <url> records the override on the new entry.
repos
Each entry registers one managed repo. metastack clones into ./repos/<name> from the workspace root.
name(string) — local directory name underrepos/. Doesn't have to match the repo's upstream name, but matching is the convention.url(string) — where to clone from.<owner>/<repo>for theghandglabproviders; a full ssh or https clone URL forgit. See provider.provider(string, optional) — overrides the workspaceproviderfor this repo:gh,glab, orgit.
checks
Each entry declares one tool that metastack doctor verifies. The check passes if command exits zero (and, if min_version is set, the version parsed from the command's stdout meets the floor).
name(string) — display name indoctoroutput.command(string) — shell command to execute as the smoke test. Non-zero exit = fail.min_version(string, optional) — semver-ish version floor. Parsed from the command's stdout; the check fails if the parsed version is below this.required(bool) —truemeans a failure is reported as✗anddoctorexits non-zero.falsemeans a failure is reported as⚠anddoctorstill exits zero.
Editing the file
metastack add edits metastack.yaml in place using a YAML node round-trip that preserves comments and formatting. Hand-edits are also fine — metastack clone reconciles whatever you put in there.
.gitignore written by metastack init excludes repos/ from the workspace's own git history. Managed repos live in their own remotes — they're cloned siblings, not submodules.