CLI Reference
The CLI source lives at walle/cli/cli.sh in this repo and is synced into every consumer project (at scripts/@walle/cli.sh) by the website module. Inside a consumer, the canonical invocation is just walle-update; direct cli.sh calls are for more control.
Global flags
| Flag | Description |
|---|---|
-s, --source <path> |
Use a local walle clone instead of a release tag (dev / test) |
-w, --walle-version <tag> |
Pin a specific release tag (e.g. v0.1.0-beta). Default: latest published tag |
--dry-run |
Show the sync plan without writing anything. Supported by init, update, add |
--yes |
Skip confirmation prompts: a MAJOR version boundary (update) or adopting an existing non-walle directory (init) |
--no-devcontainer |
Skip .devcontainer/ scaffold at init. init only |
-h, --help |
Show usage |
init
Scaffold a new consumer project, or adopt an existing directory that isn’t one yet.
cli.sh init \
[--project-name <name>] \
[--dir-path <path>] \
[--modules website,ci,ai] \
[--walle-version vX.Y.Z | --source <path>] \
[--dry-run] \
[--yes] \
[--no-devcontainer]
--project-name is optional. Given, it creates <dir-path>/<project-name> (the classic
greenfield flow). Omitted, the target is --dir-path itself (default: current directory) — run
cd my-existing-project && cli.sh init to add walle to a project you already have.
Three outcomes depending on the target directory:
- Doesn’t exist → created and scaffolded, no prompt (same as today).
- Exists with a
.harness-walle/manifest.json→ refuses to run: “already a walle project — usecli.sh updateorcli.sh add <module>instead.” Nothing is written. - Exists without a
.harness-walle/manifest.json(“adoption”) → prints a warning and the sync plan (same shape as--dry-run), then asksProceed? [y/N]. Pass--yesto skip the prompt (for scripts/CI). Declining aborts with nothing written. Seed (starter) files are written only if absent — an adoption never overwrites a file already in the directory; MANAGED module paths (src/@walle/, etc.) are always synced as usual.
What it does (cases 1 and 3):
- Establishes the harness-coding base (unless
--no-harness-coding). - Resolves the source (latest published tag by default, or
--source/--walle-version). - Seeds the starter site from
walle/website/— every file not already present in the target. - Syncs each declared module’s MANAGED paths.
- Seeds each module’s SEED paths (written once if absent).
- Injects walle’s marker-bounded blocks into consumer-owned files.
What is managed, seed, or inject is declared in walle/walle.yml.
6. Writes .harness-walle/manifest.json, .harness-walle/config.yml (if absent), .harness-walle/lock, and (unless
config.yml’s docs: false) .harness-walle/docs/.
website is always required. Omit --modules to get just website.
Output: Project <name> initialized in <dir>.
Common errors:
| Error | Cause |
|---|---|
already a walle project |
Target has a .harness-walle/manifest.json already — use update/add instead |
aborted — no files were written |
Adoption prompt declined |
'website' is a mandatory module |
--modules specified without website |
unknown module '<m>' |
Module name not in website, ci, ai, backend, devcontainer |
update
Re-sync MANAGED paths in an existing consumer from a new walle release.
cli.sh update \
[-p | --project-path <path>] \
[--walle-version vX.Y.Z | --source <path>] \
[--dry-run] \
[--yes] \
[--no-deps-check]
Default project path: current directory.
What it does:
- Migrates any older layout first (see Migration below): a root
.walle.config.jsonor an old.walle/folder becomes.harness-walle/, and aschemaVersion: 2filesmap is reshaped to v3. - Reads
.harness-walle/manifest.json(stops if missing or unversioned). - Resolves the source.
- Checks for a MAJOR version boundary — stops unless
--yes. - Re-syncs MANAGED paths for all declared modules, including the
.vscode/settings.jsonand.vscode/extensions.jsonmarker blocks. - Does not touch SEED files, consumer configs, styles, pages, or content outside the
.vscode/marker blocks. - Rewrites
.harness-walle/manifest.jsonwith the newwalleVersionandupdatedAt, refreshes.harness-walle/lockand.harness-walle/docs/(unless disabled). - Reports any Walle-owned dependency drift in
package.json(which is seed-owned and never rewritten) so you can align it — seedeps. Disable with--no-deps-check.
Migration from older layouts
A consumer on any older layout is migrated automatically, before anything else — idempotent, no data lost, no manual action:
- a root
.walle.config.jsonfile →.harness-walle/manifest.json; - an old
.walle/state folder →.harness-walle/(dir renamed); - a
schemaVersion: 2filesmap (keyed by dest path → module) → v3 (keyed by module → array of dest paths), andschemaVersionbumped to 3.
In a consumer, prefer:
just walle-update
Common errors:
| Error | Cause |
|---|---|
no .harness-walle/manifest.json found |
Not a walle consumer or wrong --project-path |
unsupported manifest schemaVersion |
Manifest has no schemaVersion (v2/v3 are supported; v2 auto-migrates) — re-scaffold with init |
crosses a MAJOR boundary |
MAJOR version bump; re-run with --yes after reviewing CHANGELOG.md |
add
Add a new module to an existing consumer project.
cli.sh add <module> \
[-p | --project-path <path>] \
[--walle-version vX.Y.Z | --source <path>] \
[--dry-run]
What it does:
- Reads
.harness-walle/manifest.json. - Syncs the new module’s MANAGED paths.
- Writes the module’s SEED paths if they don’t exist yet (re-add leaves them intact).
- Appends the module to
modulesin.harness-walle/manifest.json.
If the module is already declared, add re-syncs its MANAGED paths without changing the module list.
Note for backend: API routes require SSR. If astro.ssr.enabled is not true in src/configs/app.json, the CLI emits a warning after adding the module.
Common errors:
| Error | Cause |
|---|---|
module name is required |
No module name passed |
unknown module '<m>' |
Module not in the valid set |
manifest v1 detected |
Consumer needs migration first |
check
Read-only validation of a consumer project.
cli.sh check [-p | --project-path <path>] [-s | --source <path>] [-v | --verbose]
What it checks:
.harness-walle/manifest.jsonexists and isschemaVersion: 2.walleVersionis a semver tag (vX.Y.ZorvX.Y.Z-prerelease) or"local".- When
walleVersionis a semver tag (not"local"), compares it against the latest published tag on GitHub and emits a warning if the consumer is behind. Silently skipped when the remote is unreachable (no network). - Manifest validates against
schemas/walle.config.schema.json. - Consumer configs pass
validate-configs.mjs(if present). - Reports presence/absence of each module’s SEED files (informational, never fails).
- Warns if
backendis active but SSR is off.
Pass --source <path> to also diff the consumer’s MANAGED paths against a local walle clone. Files marked ! are out of sync; a count and a suggested update command are shown at the end.
Pass --verbose to also print the full module catalog (all available modules, their MANAGED/SEED paths, and available component variants). Useful for onboarding and discovery.
Output on success:
✓ manifest v2 present (<name>)
✓ walleVersion pinned: v0.1.0-beta
✓ manifest valid against schema
✓ consumer configs valid
· seed present (website): README.md
check passed.
With --verbose:
Available modules:
website (required) — Astro site — @walle components, layouts, styles, config and CLI scripts
MANAGED : src/@walle, schemas, scripts/@walle
SEED : README.md, justfile.project, .husky/pre-commit, .husky/pre-push
ci — GitHub Actions workflows (test + deploy) under @walle
MANAGED : .github/workflows/actions/@walle
SEED : .github/workflows/test.yml, .github/workflows/deploy.yml
devcontainer (opt-out, default on) — DevContainer scaffold — opt-out at init, not tracked in modules[]
SEED : .devcontainer/devcontainer.json
...
Available component variants:
navbar : standard, minimal
footer : standard, minimal
With --source:
--- managed path diff (source: /path/to/harness-walle) ---
✓ in-sync: src/@walle/
! out-of-sync (3 file(s)): schemas/
3 managed path(s) out of sync — run: cli.sh update --source /path/to/harness-walle
Does not write anything.
deps
Report — and optionally align — Walle-owned dependency drift in a consumer’s package.json.
Inside a consumer the canonical form is just walle-deps (just walle-deps --apply to align); the
direct cli.sh invocation below is for more control.
cli.sh deps \
[-p | --project-path <path>] \
[--walle-version vX.Y.Z | --source <path>] \
[--apply]
Why it exists: package.json is a SEED file (see managed vs seed) — the
consumer owns it and adds their own dependencies, so update never rewrites it. deps bridges that
gap: it treats the resolved release’s seed package.json as the reference for Walle-owned
dependencies and compares the consumer’s versions against it. Dependencies the consumer added (absent
from the seed) are never reported or changed.
What it does:
- Without
--apply(default) — read-only report. Prints each Walle-owned dependency whose declared floor is below the seed’s, flags major gaps (which may carry breaking changes), and lists any Walle deps absent from yourpackage.json. Runs automatically at the end ofupdate(unless--no-deps-check). - With
--apply— rewrites only the behind Walle-owned entries inpackage.jsonto the seed’s ranges, preserving key order and leaving your own dependencies untouched. Then runyarn installto update the lockfile.
$ cli.sh deps --source /path/to/harness-walle
⚠ 2 Walle dependency(ies) in your package.json are behind the tested set
(Walle never rewrites package.json — it's yours):
package yours walle
astro ^7.0.6 ^7.1.3
vitest ^3.2.7 ^4.1.10 ⚠ major — check breaking changes
Align them: yarn up astro@^7.1.3 vitest@^4.1.10
Or run: walle deps --apply
The comparison uses the version floor of each range (^7.1.3 → 7.1.3), so it flags only genuine
downgrades, never cosmetic range differences. Each release also records its validated dependency set in
the CHANGELOG Dependencies section.