Overview
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.Mailerinto every entry point.add notificationgenerates a module-owned, multi-channel notification (payload +Via+ per-channel content) and, on a project's first notification, wires a sharednotify.Senderinto every entry point plus thenotificationstable migration.add realtimescaffolds the dedicated, horizontally scalablecmd/realtimegateway that delivers notifications live to connected clients.new migrationcreates a numbered goose SQL migration in themigrations/directory;upgrade importsrewrites imports in your project after a kit package reorganization.add dbadds another, independent database connection to a module (its own pool, config andDependenciesfields, pgx or bun);add surface --dbbinds the module's shared repository skeleton to one.add coreretrofits a module generated before the rename with the sharedcore/and module-levelrepository/skeletons (purely additive, touches no anchors).add composeregeneratescompose.ymlstandalone (base stack + one service per manifest module);add dockerregenerates 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--forcethe run proceeds but prints anoverwriting <path>line for every existing file it replaces first, so a regeneration never clobbers files silently. - Generated
.gofiles 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
| Anchor | File | Inserted by |
|---|---|---|
gpsystem:modules | spec/typespec/main.tsp | new module / add surface |
gpsystem:imports, gpsystem:surfaces | register.go | new module / add surface |
gpsystem:operations | <surface>.tsp | add handler |
gpsystem:modules, gpsystem:deps-init(<module>) | cmd/worker/main.go | new module / add worker / add event |
gpsystem:listeners | listeners/register.go | add listener |
gpsystem:jobs | jobs/register.go | add job |
gpsystem:pools, gpsystem:closers | cmd/<module>/main.go, cmd/worker/main.go | add db (named connection's pool / closer), add mail (shared mailer construction), add notification (shared notifier + publisher construction/closer) |
gpsystem:config | internal/platform/config/config.go | add 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.go | add mail (Mailer: mailer,), add notification (Notifier: notifier,) |
gpsystem:topics | cmd/realtime/main.go | add realtime (shared topic-channel authorization) |
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.