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] [--db pgx|bun]
├── new module <name> [--surface <name>]
├── new migration <name>
├── add surface <module> <surface> [--db <name>]
├── add domain <module> [--db <name>]
├── add handler <module> <surface> <operation> --method GET --path "/x/{id}"
├── add auth [--module auth] [--social discord,facebook,apple,google]
├── 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
└── version

A quick per-command orientation:

  • new module generates a module: entry point, first surface, contract, plus the module root package and the module-level repository/ skeleton.
  • add surface adds another surface to an existing module.
  • add handler adds one operation to an existing surface.
  • add auth scaffolds a complete auth module (DB-backed RBAC schema, register/verify-email/login/refresh/logout/password-reset endpoints, plus a social login layer) and wires authentication automatically: it inserts api.Use(server.IdentityMiddleware(cfg.JWT)) into every module entry point at the gpsystem:api-middleware anchor, and records auth: <module> in gpsystem.yaml, so modules generated later render the line themselves. On an older project whose main.go lacks the anchor, it prints the line to paste in instead of failing. See Authentication.
  • add event creates an event and wires an outbox dispatcher into the module.
  • add listener subscribes to an event (--queue routes it to a named asynq queue).
  • add job schedules a recurring handler.
    add event / add listener / add job scaffold background work for the cmd/worker binary (see the worker concepts); every project includes the worker, so these commands are ready to use right away.
  • 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.
  • add worker adds the worker layer (cmd/worker, per-module listeners//jobs/ subpackages, the outbox migration) to a project that doesn't have one yet.
  • new migration creates a numbered goose SQL migration in the migrations/ directory.
  • 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 domain adds the module root package's domain files (<module>.go, errors.go) and the module-level repository/ skeleton to a module that doesn't have them yet (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). new project additionally accepts --replace/--replace-dev, a contributor/dev-mode feature for pointing generated go.mod replace directives at local module checkouts; ordinary consumer projects never need them, see new project: contributor mode.

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:surfacessurfaces/register.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)
gpsystem:api-middlewarecmd/<module>/main.goadd auth (api.Use(server.IdentityMiddleware(cfg.JWT)) on every module's api router)

Two further anchors are seams for your own code rather than generator targets: gpsystem:middleware (in the server.Run option list, where WithStack/WithMiddleware options go; see the framework server) and gpsystem:surface-middleware (the first line of each surface's Route block, the place for route-group middleware like r.Use(server.RequireRole("admin"))).

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
framework: github.com/gp-system/framework@v0.0.0
db: pgx
auth: auth                     # the module add auth generated; absent until it runs
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); manifests without them load unchanged. auth appears once add auth has run and names the module it generated: new module reads it to render the identity-middleware line into new entry points automatically.

gpsystem generates net/http (chi) backends only: there is no engine choice and no --engine flag.
Copyright © 2026