Reference: beach-config CLI

beach-config is the single CLI entry point for every Beach configuration operation. Seven subcommands in v1, each with --help for its own flags.

beach-config init

Scaffold a config/ directory in the current project. Lays down the umbrella, the functional and environmental files, three model files (haiku.yaml, sonnet.yaml, opus.yaml), and the conventional subdirectories (actors/, channels/, tools/).

beach-config init [options]

  --root <path>          Where to write the scaffold. Default ./config
  --inline               Use the inline layout (one functional file).
                         Default is split (one file per actor / channel / tool / model).
  --with-prod-overlay    Lay down beach.environment.prod.yaml.
  --with-secrets         Lay down a stub beach.secrets.env (gitignored).
  --with-starter         Lay down a starter sample: one example actor (`concierge`),
                         one tool (`example-lookup`), one streaming channel (`sse`)
                         pre-wired for `@cool-ai/beach-starter`'s request-reply pipeline.
                         The actor, tool, and channel are placeholders — replace them
                         with the application's real concierge and tools.
  --force                Overwrite existing files. Default false.
  --help                 Show this help.

Idempotent: the second run reports every file as already-existing and skips it; pass --force to refresh templates.

A team starting from @cool-ai/beach-starter typically runs beach-config init --with-starter to get both the config tree and a worked example of an actor, tool, and channel for the starter's request-reply pipeline.

beach-config validate

CI-friendly check. Walks the config tree, validates every file against its JSON Schema, applies the cross-axis rules (no ${VAR} in functional files; deployment overlay restricted to its allowlist; secrets-shape rules), and reports per-file accounting.

beach-config validate [options]

  --root <path>              Config root to validate. Default ./config
  --with-app-schema <path>   JSON Schema (or YAML) to validate every `app:`
                             block against. Recommendation: ship one with
                             every Beach application that has consumer-
                             defined `app:` content. Why: brings consumer
                             fields under the same per-file checking Beach
                             gives its own fields.
  --json                     Emit JSON instead of human-readable.
  --help                     Show this help.

Exits 0 when no errors, 1 on any error. Warnings do not affect the exit code. Wire into CI as a pre-deploy check.

The --with-app-schema flag is the Beach-side answer to "how do I get my domain-specific config validated?". The schema may target the top-level app: block on the functional file, the per-instance app: blocks on actor / channel / tool files, or both — Beach walks every app: block it finds and validates each against the schema. Authors typically write one umbrella schema with a oneOf over the locations they care about.

beach-config tree

Print the inventory of the loaded config. Sections are rendered in canonical order: functional, environment, models, actors, channels, tools, protocols. Models show their matches[]; actors show their kind, model alias, tool count, and any cascade overrides; tools are grouped by scope.

beach-config tree [options]

  --root <path>      Config root. Default ./config
  --section <name>   Restrict to one section (e.g. actors).
  --depth <n>        Collapse below depth N. 0 = section headers only.
  --diff <env>       Diff base vs the named deployment overlay.
  --diff <a>,<b>     Diff overlay <a> vs overlay <b>.
  --json             Emit JSON instead of text.
  --help             Show this help.

Diff mode marks changed/added/removed nodes with ~/+/- glyphs and prefixes the output with # diff: <base> → <target>. Useful in deploy review for "what does prod change that staging does not".

beach-config explain <key>

Walk the cascade for a single dotted-path key. Reports each layer's contribution and the source file it came from. The output answers two questions at once: what is this value? and why is it that?

beach-config explain <key> [options]

  --root <path>        Config root. Default ./config
  --deployment <env>   Apply the named per-environment overlay.
  --json               Emit JSON instead of text.
  --help               Show this help.

Examples:

beach-config explain actors.concierge.maxTokens
beach-config explain actors.triage.inject.maxInjectTokens
beach-config explain tools.search-destinations.timeoutSeconds
beach-config explain router.onCascadeSuppressed

For collection keys (actors.<id>.<field>, tools..., channels..., models...), the cascade walk is four layers deep: instance → perKind/perModel → defaults → Beach hard-coded default. For scalar keys (e.g. router.onCascadeSuppressed), the value is reported at the named path with no cascade walk.

beach-config split <section>

Convert inline:<section> in beach.functional.yaml into one file per entry under config/<section>/. The functional file is rewritten to drop the inline block and add an includeDir: directive.

beach-config split <section> [options]

  --root <path>   Config root. Default ./config
  --force         Overwrite existing per-thing files.
  --help          Show this help.

<section> is one of actors, channels, tools, models. The operation is the inverse of beach-config merge. Neither operation changes the validated config object the application sees.

beach-config merge <section>

Convert config/<section>/*.yaml back into an inline block in beach.functional.yaml. Per-thing files are removed; the directory is left in place.

beach-config merge <section> [options]

  --root <path>   Config root. Default ./config
  --force         Overwrite existing inline entries on collision.
  --help          Show this help.

A team that started with the split layout and decided five actors fit neatly inline runs beach-config merge actors to consolidate.

beach-config update-models

Refresh the shipped model files in config/models/. Compares each shipped template against the team's local copy and proposes one of three actions per file:

  • identical — silently skipped.
  • missing — offer to add it.
  • differs — offer to merge matches[] (recommended; appends new versions to the team's existing array, leaves customisations intact), overwrite the team's file, or skip.
beach-config update-models [options]

  --root <path>          Config root. Default ./config
  --yes                  Non-interactive. Apply --strategy to every
                         differing file.
  --strategy <name>      Used with --yes. One of:
                           merge-matches  (default; recommended)
                           overwrite      (replace the team's file)
                           skip           (leave the team's file alone)
  --help                 Show this help.

Interactive by default; --yes is for CI. The matches-only merge is the common case: Anthropic ships a new Haiku version, the team has tightened maxInjectTokens and switched headStrategy to list-prefix, and the update appends the new version to matches[] without disturbing either customisation.

Exit codes

  • 0 — success.
  • 1 — operation failed (validation error, file not found, parse error, schema violation, etc.).
  • 2 — invalid CLI invocation (unknown flag, missing argument, invalid section name).

Related