Tooling

CLI commands

The pkg/cmd package provides a command-line argument parser with subcommands, flags, and auto-generated help. The stanza CLI tool uses it for project management operations.

import "github.com/stanza-go/framework/pkg/cmd"

Building a CLI app

app := cmd.New("myapp",
    cmd.WithVersion("1.0.0"),
    cmd.WithDescription("My application CLI"),
)

app.Command("serve", "Start the server", func(c *cmd.Context) error {
    addr := c.String("addr")
    fmt.Printf("Listening on %s\n", addr)
    return nil
}, cmd.StringFlag("addr", ":8080", "Listen address"))

app.Command("migrate", "Run database migrations", func(c *cmd.Context) error {
    verbose := c.Bool("verbose")
    // ...
    return nil
}, cmd.BoolFlag("verbose", false, "Show migration details"))

app.Run(os.Args)

Running myapp serve --addr :3000 calls the serve handler with addr set to :3000.


Subcommands

Commands can have subcommands. Pass nil as the run function for grouping containers:

db := app.Command("db", "Database operations", nil)

db.Command("migrate", "Run migrations", func(c *cmd.Context) error {
    // ...
    return nil
})

db.Command("seed", "Seed initial data", func(c *cmd.Context) error {
    // ...
    return nil
})

Usage: myapp db migrate, myapp db seed.


Flag types

Four flag types are available, each as a CommandOption:

cmd.StringFlag("name", "default", "Description")
cmd.IntFlag("count", 10, "Description")
cmd.BoolFlag("verbose", false, "Description")
cmd.DurationFlag("timeout", 30*time.Second, "Description")

Flags are passed as --name=value or --name value. Bool flags can be set by presence alone (--verbose).


Context

The Context provides access to parsed flags and positional arguments:

app.Command("greet", "Greet someone", func(c *cmd.Context) error {
    name := c.String("name")       // flag value
    loud := c.Bool("loud")         // flag value
    timeout := c.Duration("timeout")

    if c.Has("name") {
        // flag was explicitly set on command line
    }

    args := c.Args()     // positional arguments after flags
    first := c.Arg(0)    // first positional arg, or ""

    return nil
},
    cmd.StringFlag("name", "world", "Who to greet"),
    cmd.BoolFlag("loud", false, "Shout the greeting"),
    cmd.DurationFlag("timeout", 5*time.Second, "Greeting timeout"),
)

Auto-generated help

Help is generated automatically from command names, descriptions, and flag definitions:

$ myapp --help
My application CLI

Usage:
  myapp <command> [flags]

Commands:
  serve     Start the server
  migrate   Run database migrations

$ myapp serve --help
Start the server

Usage:
  myapp serve [flags]

Flags:
  --addr string    Listen address (default ":8080")

The stanza CLI tool

The cli/ repository provides the stanza binary for project management. It's built with pkg/cmd.

stanza export

Exports the data directory as a zip archive:

# Export to auto-named file (stanza-export-<timestamp>.zip)
stanza export

# Export to specific path
stanza export --output backup.zip

# Export from custom data directory
stanza export --data-dir /path/to/data

The export is a byte-for-byte zip of the data directory. It includes the SQLite database, logs, uploads, and config.

stanza import

Restores a data directory from a previously exported zip:

# Import with confirmation prompt
stanza import backup.zip

# Import without confirmation
stanza import --force backup.zip

# Import to custom data directory
stanza import --data-dir /path/to/data backup.zip

Import validates that the archive contains database.sqlite and includes zip slip protection against path traversal attacks.

stanza logs

View and tail structured JSON log files:

# Show last 50 lines (default)
stanza logs

# Show last 100 lines
stanza logs --lines 100

# Follow new entries in real-time (like tail -f)
stanza logs --follow

# Filter by minimum level
stanza logs --level error

# Output raw JSON instead of formatted
stanza logs --json

# Read a specific rotated log file
stanza logs --file stanza-2026-03-21.log

# List available log files with sizes
stanza logs --list

# Disable colored output
stanza logs --no-color

In formatted mode (the default), JSON log entries are displayed as human-readable lines with colored log levels, timestamps reformatted to local time, and extra fields shown as key=value pairs. Raw JSON mode (--json) prints each line as-is.

The --level filter accepts debug, info, warn, or error. Only lines at or above the specified level are shown. Filtered lines do not count toward the --lines limit.

The --follow flag watches the log file for new content, checking every 300ms. Combine with --level to follow only errors in real-time:

stanza logs --follow --level error
FlagDefaultDescription
--lines50Number of lines to show
--followfalseFollow new log entries
--levelMinimum log level (debug, info, warn, error)
--filestanza.logLog file to read
--jsonfalseOutput raw JSON
--no-colorfalseDisable colored output
--listfalseList available log files
--data-dirOverride data directory

stanza status

Show a health summary of the data directory:

stanza status

# With custom data directory
stanza status --data-dir /data

# Disable colors (for scripts)
stanza status --no-color

Inspects the data directory and reports on each component:

  • Database — file size, WAL size, SHM presence, last modified time
  • Logs — number of log files, total size, most recent file
  • Uploads — file count and total size (recursive)
  • Backups — file count, total size, most recent backup
  • Config — whether config.yaml exists, its size and modification time

If the data directory does not exist, the command reports NOT FOUND without error.

FlagDefaultDescription
--no-colorfalseDisable colored output
--data-dirOverride data directory

stanza db

Show database statistics, table information, and migration history:

stanza db

# With custom data directory
stanza db --data-dir /data

Opens the database in read-only mode and displays:

  • Database info — file path, size, WAL size
  • Engine — SQLite version, journal mode, page size, page count, free pages
  • Tables — all tables with row counts
  • Migrations — total applied, last 5 migrations with version, name, and application date

The database is opened with PRAGMA query_only = true so the command never modifies data.

FlagDefaultDescription
--no-colorfalseDisable colored output
--data-dirOverride data directory

stanza backup

Create a consistent database backup using SQLite's VACUUM INTO:

stanza backup

# Custom output path
stanza backup --output /backups/daily.sqlite

# Gzip-compressed backup
stanza backup --compress

# Both
stanza backup --output /backups/daily.sqlite.gz --compress

Unlike stanza export (which zips the entire data directory), stanza backup creates a compacted, self-contained copy of just the database file. VACUUM INTO guarantees all WAL data is included — the backup is always consistent, even while the application is running.

With --compress, the backup is gzip-compressed after compaction. SQLite databases compress extremely well (often 10x reduction).

FlagDefaultDescription
--outputstanza-backup-{timestamp}.sqliteOutput file path
--compressfalseGzip-compress the backup
--data-dirOverride data directory

Data directory resolution

All commands resolve the data directory in this order:

  1. --data-dir flag (highest priority)
  2. DATA_DIR environment variable
  3. ~/.stanza/ (default)
Previous
Deployment