CLI

Modular command-line interface

7 min read 1429 words

Structure: Modular command-line interface with Milo CLI

Location:bengal/cli/directory with organized command modules

Architecture:

  • bengal/cli/milo_app.py- Root Milo app and lazy command registration

  • bengal/cli/milo_commands/- Annotated Milo command modules

  • bengal/output/templates/- Kida templates for terminal output

    • build.py- Build commands
    • serve.py- Development server
    • check.py- Author-facing validation checks
    • audit.py- Generated artifact audit
    • clean.py- Cleanup utilities
    • new.py- Content and theme creation

    Features:

  • Lazy command imports: Fast CLI startup with command modules loaded on demand

  • Kida output: Structured command results rendered through Milo/Kida templates

  • Bengal help templates: Root and group help dogfood Milo's registry with Bengal-owned Kida templates

  • Command state templates: Empty states, command lists, and command errors use reusable Bengal Kida templates

  • Shared output bridge: Semantic command messages, logger console events, and phase summaries route throughCLIOutput

  • Aggregated notices: Repeated warnings such as missing icons, unknown config entries, and URL collision claimants collapse into compact summaries

  • Milo built-ins:--llms-txt, shell completions, and MCP gateway modes are generated from the registered command tree

  • Error Handling: Beautiful tracebacks with context and locals

  • Extensibility: Easy to add new commands in separate modules

Milo Built-ins:

# Agent-readable command reference
bengal --llms-txt

# Shell completion scripts
bengal --completions zsh
bengal --completions bash
bengal --completions fish

# Expose Bengal commands through Milo's MCP transport
bengal --mcp

# Register or remove Bengal from the local Milo MCP gateway
bengal --mcp-install
bengal --mcp-uninstall

Core Commands

Build Commands {#build-commands}:

# Basic build (parallel by default)
bengal build

# Incremental build (uses cache for faster rebuilds)
bengal build --incremental

# Force sequential processing (disable auto-parallel)
bengal build --no-parallel

# Strict mode (fail on template errors, recommended for CI)
bengal build --strict

# Debug mode (full tracebacks, developer profile)
bengal build --debug

# Verbose output (show detailed phase timing and stats)
bengal build --verbose

# Fast mode (quiet output, max speed)
bengal build --fast

# ASCII-safe lifecycle output for CI logs
bengal build --style ci

# Memory-optimized for large sites (5K+ pages)
bengal build --memory-optimized

# Preview build without writing files (see what WOULD happen)
bengal build --dry-run

# Show detailed breakdown of why pages are rebuilt/skipped
bengal build --explain

# Machine-readable explain output (for tooling)
bengal build --explain --explain-json

Development Commands {#serve-commands}:

# Start development server with file watching
bengal serve

# Custom port
bengal serve --port 8080

# Disable file watching
bengal serve --no-watch

# Open browser automatically (default)
bengal serve --open-browser

# ASCII-safe server lifecycle output for CI or log capture
bengal serve --style ci

# Verbose output (show file watch events)
bengal serve --verbose

Inspection Commands:

# Explain how a page is built
bengal inspect page --page-path index

# Check internal and external links
bengal inspect links
bengal inspect links --internal-only
bengal inspect links --external-only

# Analyze site structure and link graph
bengal inspect graph

# Visualize dependency relationships
bengal debug deps

Example: bengal inspect graph Output

ᓚᘏᗢ Site Graph

Pages: 124
Links: 428
Orphans: 3

Refer to Graph Analysis for details and Analyze site connectivity for a guided walkthrough.

Performance Commands:

# Show recent build performance metrics
bengal inspect perf

# Show last N builds
bengal inspect perf --last 20

# Compare last two builds
bengal inspect perf --compare

# Export as JSON
bengal inspect perf --output-format json

Utility Commands:

# Validate source content and author-facing policy
bengal check

# Bounded ASCII-safe health report for CI logs
bengal check --style ci --limit 5

# Drill into one health finding code from the report
bengal check --focus H101-001 --suggestions

# Compatibility alias while older automation migrates
bengal health

# Audit generated artifacts after a build
bengal audit

# Bounded verdict-first audit report for CI logs
bengal audit --style ci --limit 5

# Drill into one finding code from the report
bengal audit --focus A101-001

# Clean output directory
bengal clean

# Clean cache and output
bengal clean --cache

# Create new site
bengal new site --name mysite

# Create new page
bengal new page --name post --section blog

# Show version
bengal --version

# Show help
bengal --help

Plugin Commands:

# List installed plugins and readiness
bengal plugin list

# Show capability details for one plugin
bengal plugin info my-plugin

# Validate plugins against capabilities Bengal currently wires
bengal plugin validate

Plugin introspection is intentionally conservative: it loads the same bengal.plugins entry points as a build, runs each plugin's register()method against a temporary registry, and reports capability counts plus whether each capability is actually integrated today.

Upgrade Commands:

# Check for updates and upgrade interactively
bengal upgrade

# Skip confirmation prompt
bengal upgrade -y

# Show what would be done without executing
bengal upgrade --dry-run

# Force upgrade even if already on latest
bengal upgrade --force

The upgrade command automatically detects how Bengal was installed (uv, pip, pipx, conda) and runs the appropriate upgrade command. It checks PyPI for the latest version with a 24-hour cache to avoid repeated network requests.

Command Registration

Commands are registered inbengal/cli/milo_app.py. The CLI uses Milo's lazy registration API so importingbengalor rendering root help does not import every command implementation:

from milo import CLI

cli = CLI(name="bengal", description="Static site generator for Python teams")

cli.lazy_command(
    "build",
    import_path="bengal.cli.milo_commands.build:build",
    description="Build your site",
    aliases=("b",),
    display_result=False,
)

new = cli.group("new", description="Create new site, theme, or content", aliases=("n",))
new.lazy_command(
    "site",
    import_path="bengal.cli.milo_commands.new:new_site",
    description="Create a new Bengal site",
    display_result=False,
)

Command Inventory

The public command inventory is generated from the Milo registry and guarded by unit tests against this documentation,--llms-txt, README command snippets, and runtime smoke coverage.

build
serve
clean
check
audit
health
fix
upgrade
codemod
new.site
new.theme
new.page
new.layout
new.partial
new.content-type
config.show
config.doctor
config.diff
config.init
config.inspect
theme.list
theme.info
theme.discover
theme.swizzle
theme.install
theme.validate
theme.new
theme.debug
theme.directives
theme.test
theme.assets
content.sources
content.fetch
content.collections
content.schemas
version.list
version.info
version.create
version.diff
i18n.init
i18n.extract
i18n.compile
i18n.sync
i18n.status
plugin.list
plugin.info
plugin.validate
inspect.page
inspect.links
inspect.graph
inspect.perf
debug.incremental
debug.delta
debug.deps
debug.migrate
debug.sandbox
cache.inputs
cache.hash

Root aliases are part of the contract: b for build, s and devfor serve, c for clean, and v for check. Group aliases are n for new andplugins for plugin.

health remains a top-level legacy alias for check. It is kept for existing automation, but new docs and examples should usecheck.

Themed Help

Bengal subclasses Milo'sCLI as BengalCLIso command results and root help render through the sharedCLIOutput bridge. Root bengal, bengal --help, and group invocations such asbengal newuse Bengal-owned Kida templates fed by Milo's command registry, while leaf command help is generated from the annotated command signatures.

$ bengal --help
bengal 0.3.2
Static site generator for Python teamsevery layer pure Python, scales with your cores

Core workflow
  build (b)               Build your site
  serve (s, dev)          Start dev server with hot reload
  check (v)               Validate your site

Site systems
  config                  Configuration management [group]
  theme                   Theme development, directives, and assets [group]

Command Output Templates

Command implementations should return structured dictionaries for automation and usebengal.output.get_cli_output()for human terminal output. Prefer command-state templates for user-facing branches:

  • command_empty.kidafor no-op, missing setup, or nothing-to-do states
  • command_list.kidafor lists with names, status, descriptions, or source metadata
  • command_error.kidafor command-choice errors before argparse usage leaks through
  • _report_primitives.kidafor verdict-first reports with meters and issue cards
  • Domain templates such asbuild_summary.kida, validation_report.kida, check_report.kida, scaffold_result.kida, clean_plan.kida, clean_result.kida, and artifact_audit.kidafor richer flows

The first parity pass covers plugin discovery, content sources/collections, cache input lists, theme lists/discovery, missing config states, and unknown root/group command errors. New command work should add the command to the output matrix intests/unit/cli/test_command_output_templates.pywhen it introduces a new terminal branch.

Build summaries, health checks, and artifact audit now follow the Milo output gallery pattern: the first screen states the verdict, details are bounded by --limit where findings can be long, --focus CODEdrills into one finding for check/audit,--style ci and --style asciiuse stable ASCII glyphs, and JSON-producing paths keep their existing structured envelopes for automation. Commands apply these styles withCLIOutput.output_mode(...), so direct in-process command calls restore the previous terminal mode after completion or early exit.

MCP Annotations

Command registration includes Milo MCP annotations where intent matters. Read-only commands such ascheck, audit, plugin list, config show, and cache inputs advertise readOnlyHint. File-writing or state-changing commands such asclean, fix, new, config init, theme swizzle, i18n compile, version create, upgrade, and codemod advertise destructiveHintand idempotentHintwhere applicable.