when: predicates
The when: field on a lifecycle hook ([[hooks.*]]), and on the legacy [[bootstrap.command]], conditionalises shell steps. Predicates compose with boolean operators, so a single step can express “run bun install if package.json exists AND bun is on $PATH, but never in CI”.
The same grammar applies to every [[hooks.<phase>]] block (pre_create, post_create, pre_bootstrap, post_bootstrap, pre_remove, post_remove). The examples below use [[bootstrap.command]], but when = "…" reads identically on [[hooks.post_create]].
Supported atoms
Section titled “Supported atoms”| Atom | True when … |
|---|---|
file_exists:<path> |
<worktree>/<path> resolves on disk |
cmd_exists:<binary> |
<binary> resolves on $PATH (which lookup) |
env_set:<NAME> |
std::env::var(NAME) returns Ok (the var is defined, possibly empty) |
env_eq:<NAME>=<value> |
NAME is set and its value equals <value> exactly |
glob_exists:<pattern> |
at least one path under the worktree matches <pattern> (supports **) |
Atoms are case-sensitive. Whitespace around the colon and around boolean operators is tolerated. Unknown keywords default to true so old configs keep running while gwm doctor (check #3) surfaces them as Warning.
Boolean composition
Section titled “Boolean composition”| Operator | Meaning | Precedence |
|---|---|---|
! |
NOT | highest |
&& |
AND | middle |
|| |
OR | lowest |
So !a && b || c parses as ((!a) && b) || c, the same as in most languages.
Whitespace around operators is tolerated, but operators themselves must be exactly !, &&, ||: no not, and, or, no Unicode.
Examples
Section titled “Examples”# Run only when composer.json exists at the worktree root.[[bootstrap.command]]name = "composer install"run = "composer install --no-interaction --prefer-dist"when = "file_exists:composer.json"
# Prefer bun if available, otherwise fall back to npm — and never in CI.[[bootstrap.command]]name = "install (bun)"run = "bun install"when = "file_exists:package.json && cmd_exists:bun && !env_set:CI"
[[bootstrap.command]]name = "install (npm fallback)"run = "npm ci"when = "file_exists:package.json && !cmd_exists:bun && !env_set:CI"
# Build docs only when there's something to build and we're not in CI.[[bootstrap.command]]name = "build docs"run = "./scripts/full-build.sh"when = "glob_exists:docs/**/*.md && !env_set:CI"
# Apply staging-only seeds when APP_ENV is exactly "staging".[[bootstrap.command]]name = "staging seed"run = "php artisan db:seed --class=StagingSeeder"when = "file_exists:artisan && env_eq:APP_ENV=staging"
# Allow a long-running prep step only when the user opts in via env.[[bootstrap.command]]name = "warm up cache"run = "./scripts/warmup.sh"when = "env_eq:GWM_WARMUP=1"Omitted when:
Section titled “Omitted when:”A step without when: runs unconditionally, equivalent to when = "true" (which is not a literal you can type; just omit the field). Common for housekeeping commands like git lfs pull that are cheap and always safe.
Doctor coverage
Section titled “Doctor coverage”gwm doctor check #3 (``when predicates supported) parses every [[bootstrap.command]].when and surfaces unknown keywords:
! `when` predicates supported 1 unsupported keyword: file_exits → did you mean file_exists?Note the ! (Warning): the offending command still runs (defaults to true), so the bootstrap is not blocked, but the user sees the typo at config-time instead of waiting for a confused failure.
Related
Section titled “Related”- Bootstrap pipeline: where the
[[bootstrap.command]]step sits .gwm.tomlschema: the full field listing- Integrations →
gwm doctor: checks #2 (guard refs) and #3 (predicate grammar)