CLI Reference

Overview

How the gpsystem CLI works: tool directive, safety rules, anchors, manifest.

Consumer projects pin the CLI in go.mod with the Go 1.24+ tool directive (written by new project), so every teammate runs the same version:

go tool gpsystem <command>
gpsystem
├── new project <name> --module-path <path> [--dir] [--kit-replace] [--engine fiber|chi] [--db pgx|bun]
├── new module <name> [--surface <name>]
├── new migration <name>
├── add surface <module> <surface> [--db <name>]
├── add core <module> [--db <name>]
├── add handler <module> <surface> <operation> --method GET --path "/x/{id}"
├── add event <module> <name>
├── add listener <module> <event> <name> [--queue <q>]
├── add job <module> <name> --cron "0 3 * * *"
├── add mail <module> <name>
├── add notification <module> <name>
├── add worker
├── add realtime
├── add db <module> <name> [--connector pgx|bun]
├── add seeder <name>
├── add compose
├── add docker
├── upgrade templates [--force]
├── upgrade imports
└── version

add event / add listener / add job scaffold background work for the cmd/worker binary (see the worker concepts): add event creates an event and wires an outbox dispatcher into the module, add listener subscribes to one (--queue routes it to a named asynq queue), add job schedules a recurring handler. New projects include the worker; add worker retrofits it into a project generated before the feature existed. add mail generates a module-owned mail notifier (payload

  • templates) and, on a project's first notifier, wires a shared mail.Mailer into every entry point. add notification generates a module-owned, multi-channel notification (payload + Via + per-channel content) and, on a project's first notification, wires a shared notify.Sender into every entry point plus the notifications table migration. add realtime scaffolds the dedicated, horizontally scalable cmd/realtime gateway that delivers notifications live to connected clients. new migration creates a numbered goose SQL migration in the migrations/ directory; upgrade imports rewrites imports in your project after a kit package reorganization. add db adds another, independent database connection to a module (its own pool, config and Dependencies fields, pgx or bun); add surface --db binds the module's shared repository skeleton to one. add core retrofits a module generated before the rename with the shared core/ and module-level repository/ skeletons (purely additive, touches no anchors). add compose regenerates compose.yml standalone (base stack + one service per manifest module); add docker regenerates the Dockerfile/.dockerignore.

Global flags on every command: --dry-run (print the plan, write nothing) and --force (allow overwriting).

Safety rules

  • The full file plan is computed up front; if any target exists, the whole run aborts before writing anything (unless --force). No partial writes. With --force the run proceeds but prints an overwriting <path> line for every existing file it replaces first, so a regeneration never clobbers files silently.
  • Generated .go files are formatted with goimports; a template that renders invalid Go fails loudly.
  • Existing files are modified only at anchor comments, never anywhere else.
  • Insertions are idempotent: re-running a generator never duplicates a block.

Anchors

AnchorFileInserted by
gpsystem:modulesspec/typespec/main.tspnew module / add surface
gpsystem:imports, gpsystem:surfacesregister.gonew module / add surface
gpsystem:operations<surface>.tspadd handler
gpsystem:modules, gpsystem:deps-init(<module>)cmd/worker/main.gonew module / add worker / add event
gpsystem:listenerslisteners/register.goadd listener
gpsystem:jobsjobs/register.goadd job
gpsystem:pools, gpsystem:closerscmd/<module>/main.go, cmd/worker/main.goadd db (named connection's pool / closer), add mail (shared mailer construction), add notification (shared notifier + publisher construction/closer)
gpsystem:configinternal/platform/config/config.goadd db (named connection's config field), add mail (Mail smtp.Config), add realtime (Realtime realtime.Config)
gpsystem:dependencies-init, gpsystem:deps-init(<module>)cmd/<module>/main.go, cmd/worker/main.goadd mail (Mailer: mailer,), add notification (Notifier: notifier,)
gpsystem:topicscmd/realtime/main.goadd realtime (shared topic-channel authorization)
Removing an anchor comment makes the corresponding generator fail with a clear error instead of guessing where to insert. Keep them in place.

The manifest: gpsystem.yaml

Written by new project at the project root; the CLI finds the project by walking up to it.

version: 1
name: shop
module: github.com/acme/shop
kit: github.com/gp-system/gpsystem@v0.1.0
engine: fiber
db: pgx
templatesSHA: 4f2a…            # checksum of the installed oapi template set
modules:
  news:
    surfaces: [api, admin]
    dbs:
      - name: analytics
        connector: bun
    surfaceDBs:
      admin: analytics

It is the generators' source of truth for what exists (add surface news admin fails fast if the module is missing or the surface already exists), and it cross-checks module against go.mod. dbs/surfaceDBs are only present once a module has named connections (via add db); older manifests without them load unchanged. The pre-rename domains:/domainDBs: keys are still read and migrated to the new keys automatically on the next save.

Copyright © 2026