The private, workspace-local Ghost contributor CLI. After pnpm bootstrap, run:
pnpm gx --help
pnpm gx generate package email-utils --description "Email utilities for Ghost."
pnpm gx generate package parser --dir packages/email/parserRun a command group without a subcommand, such as pnpm gx generate, to see
that group's help. --help and -h show the same menu. Unknown commands still
exit with an error and show help for the relevant group.
Package generation uses packages/_template and the shared helpers in
tools/scripts/lib/. pnpm create-package remains a forwarding alias.
Run pnpm install after generating a package; custom destinations must be covered
by the workspace globs.
Run pnpm gx dev up, pnpm gx dev down, pnpm gx dev status and
pnpm gx dev gc to manage development stacks. Existing arguments still apply,
including up dev:analytics, down --all, down --orphans, status --json
and gc --yes. See development setup
for behavior and options.
pnpm dev:up, pnpm dev:down, pnpm dev:status and pnpm dev:gc remain direct
shortcuts to the same command files. The private session-end hook and shutdown
path use Node built-ins only. Run node tools/gx/dev-down.ts directly when
workspace dependencies are missing or stale; shutdown from the status view also
uses direct Node execution and supports checkouts with the older script paths.
Shared environment and infrastructure setup remain in tools/scripts.
Run pnpm gx generate golden-email <segment> <output-path> against a running Ghost dev
instance. Set GHOST_GOLDEN_POST_AUTH to an Admin API key in id:secret format;
GHOST_URL defaults to http://localhost:2368. The command writes preview HTML
and removes its temporary draft post. Output paths resolve from your current
directory.
The command lives in tools/gx; its golden-post fixture remains under Ghost tests.
Run pnpm gx generate migration <slug> from the repository root. The slug must
be kebab-case. The existing generator creates a timestamped migration in the
next minor-version folder and promotes package versions to RC when needed.
See database migrations for the
workflow.
Run pnpm gx generate service <name> to create a boot-owned TypeScript service.
Use lowercase kebab-case beginning with a letter, for example:
pnpm gx generate service delivery-receiptsIt creates a defineService root, a separate implementation with typed logging
injection, and a starter root test. It does not register the service anywhere:
lint, boundary lint and the inventory test apply to every service that is not
listed in the legacy service inventory. Existing service files, directories and
tests are never overwritten, and a name that is still listed as legacy is refused.
The command prints the remaining steps, including choosing the right place for
init() in boot. See creating a service
for the ownership, implementation and testing requirements.
Run pnpm gx db rebuild-development-tables after editing development-only table
schemas. This drops and recreates the listed tables, discarding their data.
Start the development database first. gx uses the existing development-environment
runner and the checkout's Ghost working directory; it does not start services.
All gx commands use TypeScript ES modules and the shared strict configuration. Core-dependent commands import Core's existing CommonJS modules through the source loader. The table-rebuilding logic remains in Core.
Run pnpm gx data generate [options] to populate a development database, or
pnpm gx data multi-sub-scenarios to seed named subscription-resolution
scenarios. See working with test data for
options and prerequisites. Existing pnpm reset:data variants and
pnpm seed:multi-sub-scenarios forward to these commands.
Both reuse the checkout's development-environment runner and Ghost working
directory. They use the source loader, with the data-generation engine remaining
in Core. The generator preserves its existing environment permissions and
options. node index.js generate-data now reports its gx replacement. Installed
trees ship a dependency-light entrypoint at
ghost/bin/generate-data.js. Both
entrypoints share Core's option validation and normalization; their environment
setup and logging stay separate. See the shared-tool pattern.
Run pnpm gx repl for an interactive shell with models/m and knex/k.
Clearing the REPL restores those bindings; exiting closes the connection.
Run pnpm gx data shift-dates to shift database dates up to the present day, or
pass --offset=<days> to choose an offset. This modifies all dateTime columns.
The default offset is the number of whole UTC days since the last member login.
It asks for confirmation unless --force is supplied. Both commands retain
their development/local environment restriction and use the same Core runner.
The former node index.js repl and node index.js timetravel entrypoints report
their gx replacements. The old ghost/core/cli framework has been removed.
Run pnpm gx benchmark automations-drain --numberOfRuns [n] --steps [wait,send_email,...] to measure how long it takes to drain the automations
queue.
Example:
pnpm gx benchmark automations-drain --numberOfRuns 1000 --steps wait,send_emailBenchmark boots Ghost, creates one free member per run, one automation, and pending runs. Timer starts when runs become ready and ends after all steps finish.
Wait steps use one millisecond per configured hour.
Email steps use configured development mail transport.
Automation and members remain in development database under Drain benchmark <automation-id> and member emails beginning benchmark-<automation-id>-.
Run pnpm gx benchmark recipient-preparation [members] [concurrency] to measure
email recipient preparation. Defaults are 500,000 members and concurrency 2;
start smaller with pnpm gx benchmark recipient-preparation 10000 2.
gx supplies the source loader and --expose-gc. The benchmark uses the configured
MySQL server, creates its own uniquely named database and drops it afterwards.
Its database user needs permission to create and drop databases. It prints JSON
metrics for preparing all members, rebuilding interrupted preparation and
preparing a label segment. See performance testing.
These temporary commands combine the legacy service inventory with the current Dependency Cruiser audit. Run them from the repository root:
| Command | Output |
|---|---|
pnpm gx services report |
Legacy and standard totals, ownership and remaining work |
pnpm gx services list |
Services, ownership, migration status and dependency counts |
pnpm gx services report email-address |
One legacy entry's notes, assessments, blockers and offending imports |
pnpm gx services dependencies |
Disallowed dependencies ranked by distinct importing files |
pnpm gx services audit |
Dependency Cruiser's full native report |
pnpm gx services verify email-address |
Pass or fail checks on a committed service change |
List a group with list --group <group>. Report tables show group IDs;
list --help lists all groups. For example:
pnpm gx services list --group straightforward-clear
pnpm gx services list --group needs-preparation
pnpm gx services list --group to-migrate
pnpm gx services list --group undecided
pnpm gx services list --group standard --jsonreport, list and dependencies accept --json for structured output.
services audit accepts the formatter's options, including
--output-type markdown and --output-type json --output-to /tmp/service-dependencies.json.
It replaces pnpm audit:services; services dependencies replaces
pnpm audit:services:summary without requiring jq.
services verify needs a clean tree on a branch that contains the fetched base.
It checks new files, audit SHAs, formatting, lint, types and boundaries. It runs
dependency-cruiser twice, resolving workspace packages to authored source and then
to the built output Ghost loads. Built output must add no service rule violation,
every part of an added allowance must match an import from a service, and each file
an added allowance admits must be named in a boundary regression. It then runs the
full unit suite, runs the changed unit tests shuffled and each one alone and, for a
standard service, confirms a boot test fails when boot stops waiting for its
init() but still sees it reject. A service is standard when the legacy service
inventory does not list it.
--static skips lint, types and tests. review lines are other inventory entries
that mention the service, for the
verify-service-change skill
to confirm. Run it again after every rebase or fix.
Counts are calculated from the checkout, not stored in the inventory. The report
shows one table with MIGRATION, COUNT and GROUP columns. The inventory
lists legacy services only: a service found by the audit and not listed is
Standard. Legacy entries are counted by disposition (Left to migrate,
Relocate, Split or Undecided). See the
services README for what each
disposition means.
Entries left to migrate have two progress groups:
- Needs preparation: either assessment is complex or identifies a possible model change.
- Straightforward: both assessments are straightforward, and the existing model fits. This can still include dependency violations and test gaps.
The to-migrate filter shows what's left to migrate:
needs preparation + straightforward. Relocations, splits and undecided
entries are listed by the relocate, split and undecided filters; their
notes and migration assessments explain the work needed.
Per-service reports show a legacy entry's optional futureHome, a suggested home
after this migration. It does not change the entry's group.
Lists and per-service JSON expose independent ownership and migrationStatus
fields instead of a combined category. A standard service has Standard in both
and no entry. Summary JSON retains labelled counts; the disposition counts sum
to Legacy entries, and Legacy entries + Standard services is every
service found. Diagnostic counts are overlapping subsets, not additional services.
Per-service reports show inventory notes before the migration assessments, so
planned ownership changes are visible alongside earlier assessments. Notes are
also included in JSON output as entry.notes.
Legacy entries absent from the dependency graph have unknown counts. Malformed
or incomplete assessments fail the report.
The not-analyzed diagnostic count is available in summary JSON and list
filters, but omitted from the text summary.
list counts distinct disallowed dependency targets per entry, with unresolved
and own-root imports in separate columns. dependencies counts distinct
importing implementation files per target, largest first; it excludes unresolved
and own-root findings. The native audit counts rule violations, so one import
can appear under more than one rule.
All service commands use the same full dependency-cruiser scan and
known-violations file as pnpm lint:boundaries. Reports retain known and new
findings and return success when violations are found; boundary lint fails on
new violations or stale entries. Invalid known-violations files fail both.
Violations in services with deferred enforcement appear in reports but do not
fail boundary lint.
The native audit output includes Core and app boundaries as well as services.
Legacy root index.js files are treated as wiring candidates. Computed paths
such as require(name) cannot be resolved by the static audit. No violations
does not mean ready to migrate: recorded blockers, runtime behavior and test
gaps still need review. See the
services guide
for the dependency policy.
The @internal/gx workspace gives pnpm and Nx a unit for lint and tests. It runs directly
from TypeScript source and has no build or package exports. Its test command
checks types as well as running the command tests. Run pnpm --filter @internal/gx lint and
pnpm --filter @internal/gx test to validate it. Keep contributor command
implementations here and reusable Core logic in its owning subsystem.
See tooling ownership before adding a command or relocating its implementation, shared helpers or installed-release functionality.