Skip to content
gwmgwmgwmv1.10.0

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]].

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.

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.

# 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"

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.

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.