systhema doctor
Project health checks, auto-fix, the nudge and the branded header.
On this page
One command to diagnose project health and auto-fix what's safely fixable. It runs a registry of checks, reports findings grouped by severity (errors → warnings → info), and exits with status 1 if any error-severity diagnostic is present.
systhema doctor # run all checks, print a grouped report
systhema doctor --fix # interactively apply fixable findings
systhema doctor --fix --yes --no-git-check # CI-friendly: skip prompts + git check
systhema doctor --json # machine-readable output (no mutation)
systhema doctor --only peer-deps # run a single check (repeatable)
systhema doctor --skip version-drift # skip a check (repeatable)OptionsLink to this section
| Flag | Description | Default |
|---|---|---|
--fix | Apply fixable issues (interactive, git-safe) | false |
--force | Allow a dirty working tree when fixing | false |
--json | Output diagnostics as JSON | false |
--no-git-check | Skip git repo check (CI mode) | true |
--only <id> | Run only this check (repeatable) | [] |
--skip <id> | Skip this check (repeatable) | [] |
-y, --yes | Skip confirmation prompts | false |
Flag behaviorLink to this section
--fix— apply auto-fixable findings. Runs a git-safety preflight (clean working tree, or--force/--no-git-check), then confirms each fix category interactively before delegating to the existing engines. Report-only findings print a fix hint instead.
ChecksLink to this section
| Check | What it checks | Speed | Auto-fix |
|---|---|---|---|
ai-config | AI assistant config | fast | no |
analytics-config | Analytics config | fast | no |
artifacts-config | Config & artifacts | fast | yes; sync |
cloudflare-config | Cloudflare config | fast | no |
fluid-spacing-breakpoints | Fluid spacing above the top breakpoint | fast | no |
legacy-tokens | Legacy tokens | fast | yes; migrate-tokens |
lexical-patch | Lexical admin patch | fast | yes; lexical-patch |
localization-policy | Content localization field policy | fast | no |
localize-status-schema | Per-locale publication status | slow | no |
missing-tokens | Missing tokens | fast | yes; token-migration:${c.version} |
obfuscate-build-step | Class obfuscation wiring | fast | no |
package-manager-pin | pnpm version pin | fast | yes |
peer-deps | Peer deps | fast | yes |
pending-db-renames | Database table names | slow | no |
pending-migrations | Upgrade plan | fast | yes |
replaced-proxy | Customised src/proxy.ts replaced by an upgrade | fast | no |
stale-references | Token references | fast | yes; sync |
stale-token-artifacts | Token artifact cache | fast | yes; sync |
ui-patch | Admin language cache patch | fast | yes; ui-patch |
version-drift | CLI version | slow | no |
Check behaviorLink to this section
-
---------------------------: ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
pending-migrations: The project is behind the installed CLI, or sits on the CLI's version with a codemod that still applies (an interrupted upgrade). Counts whatsysthema upgradewould run — a post-upgrade data hook is only counted on a real version step, since a re-run cannot tell whether it already ran. -
pending-db-renames: (payload) Legacy Systhema table names still live in the database — connects read-only viaDATABASE_URI(Postgres/SQLite) and compares the live catalog against the expected names. Catches projects that upgraded past the release window the rename shipped in, where the next schema push would hang on Drizzle's interactive create-or-rename prompt. See Database migrations. -
localize-status-schema: (payload) The database and the config disagree about where_statuslives. Per-locale publishing is on by default wheneverlocalesis configured, which moves_statusout of the shared column onpages,postsandcomponentsand into their_localessiblings. Connects read-only viaDATABASE_URI(Postgres/SQLite) and reads the live columns, so it also catches the inverse: a project that opted out oflocalizeStatusagainst a database that already moved. See Per-locale publishing. -
package-manager-pin:package.jsonhas no exact pnpmpackageManager(missing, or floating likepnpm@10), or pins a pnpm that cannot installpnpm-lock.yaml. All pnpm releases since 9 writelockfileVersion: '9.0'; the patch and package-extension hashes tell them apart. pnpm 9 writes base32/md5, pnpm 10 sha256 underhash:, pnpm 11 and later a bare sha256 per patch, and a frozen install on another format fails withERR_PNPM_LOCKFILE_CONFIG_MISMATCH. A lockfile without those hashes works with any of them. Whenpackage.jsonhas apnpmfield, pnpm 11 and later are flagged too: they ignore that field, which silently drops its overrides, patches and build allowlist. A pnpm 11 lockfile next to such a field is reported as installable by no pnpm. A pnpm 12 or later pin also needs the lockfile to record that exact version; untilpnpm installwrites it, a frozen install fails withERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE. Silent without apnpm-lock.yamlat the project root, so a workspace sub-project is not checked. -
stale-token-artifacts: The installed@systhemaui/coredoes not carry this project's tokens —.systhema/artifacts/tokens/*.jsonandnode_modules/@systhemaui/core/dist/tmp/tokens/*.jsondisagree (differing bytes, a missing file, or a phantom file the project no longer defines). Typically an install that replaced the package after the lastsync; token-derived option lists (themes, layout backgrounds) come from the package defaults until the mirror is refreshed. Silent when the project has no synced artifacts or@systhemaui/coreis not installed. -
ai-config: (payload) AI assistant misconfiguration across the merged dotenv set (.env,.env.development,.env.production,.env.local; later overrides earlier) — unknownSYSTHEMA_AI_PROVIDER, a keyed provider withoutSYSTHEMA_AI_API_KEY, oropenai-compatiblewithoutSYSTHEMA_AI_BASE_URL/SYSTHEMA_AI_MODEL. -
analytics-config: (payload) Analytics dashboard misconfiguration across the same merged dotenv set — unknownSYSTHEMA_ANALYTICS_PROVIDER, or a provider missing its required variables (site id, API key, Matomo host, GA4 service-account email/private key, Umami Cloud key or self-hosted host + username/password). -
cloudflare-config: (payload) Cloudflare integration misconfiguration across the same merged dotenv set — a Global API key without its account email (or vice versa), bothCLOUDFLARE_API_TOKENandCLOUDFLARE_API_KEYset at once (ambiguous), or a non-booleanSYSTHEMA_CLOUDFLARE_AUTO_PURGE. -
stale-references: The agent-facing TOON references at.systhema/references/tokens/are missing, or_index.toonsays a different@systhemaui/corewrote them than the one installed. Reference generation is the last step ofsyncand is deliberately non-fatal, so a failure there leaves last version's values and serialization in place with only a warning in the scrollback. Silent when the project has no synced artifacts, when@systhemaui/coreis not installed, or when the stamp cannot be read. -
obfuscate-build-step:optimization.obfuscateClassesis enabled insysthema.configbut nopackage.jsonscript chainssysthema-core obfuscateafter the build (the flag is inert on its own — silent no-op), and/or it is enabled on a PayloadCMS project at all (class obfuscation is prerender-only; the post-build pass refuses to run there). -
fluid-spacing-breakpoints: Thespacingconfig uses a fluid%screen%rule, but some configured breakpoint above the narrowestresponsiveSizingmode has no mode of its own (neither exported nor declared incustomTokens.responsiveSizing) — so that band keeps scaling the nearest lower breakpoint's ratios, without any bound at all above the widest declared mode. -
localization-policy: A Payload project with locales enabled that has not declaredlocalization.policy. Until it does, content localization runs the legacy name-allowlist policy: form notification recipients, per-pagenoindexand redirect targets are shared across every locale. -
replaced-proxy:src/proxy.tsis a stock Systhema gateway whilesrc/proxy.ts.bakmatches no proxy Systhema shipped: the likely trace of an upgrade before 1.7.5 that replaced a customised proxy with no managed-files ledger entry. Any rate limiting, CSP or security headers the backup set are no longer running.
Report-only findings (artifacts-config's missing-config case, pending-db-renames, localize-status-schema, ai-config, analytics-config, cloudflare-config, obfuscate-build-step, fluid-spacing-breakpoints, localization-policy, replaced-proxy, version-drift) print a hint rather than mutating anything.
artifacts-config, stale-token-artifacts and stale-references deliberately share one fix grouping: all three are cured by a single systhema-core sync, so when several fire the report collapses them to one entry and --fix prompts, and syncs, once.
pending-db-renames, localize-status-schema and version-drift are slow checks (two database connections and a registry lookup): they run in systhema doctor, but never in the one-line nudge other commands print. Both database checks stay silent when the database is unreachable or DATABASE_URI is unset, on MongoDB, and when the tables are not there yet; neither creates a missing SQLite file.
The nudgeLink to this section
Other commands — info, upgrade --dry-run, create, and the proxied sync / copy-files / generate-types / migrate-tokens — run only the fast (local) checks and print a single one-line nudge toward systhema doctor --fix when a project has fixable issues. The nudge never networks (the slow version-drift check is excluded), never throws, and is throttled by a 6-hour per-project cache at ~/.systhema/doctor-cache.json.
Silence it with any of:
SYSTHEMA_NO_DOCTOR=1— opt out entirely.CI— automatically suppressed in CI environments.--json— suppressed alongside any JSON output.
Branded headerLink to this section
The SYSTHEMA wordmark that appears above interactive command output is gated on stdout being a TTY. It is suppressed automatically when output is piped, redirected, or captured (e.g. by an agent), in CI, in --json mode, or when SYSTHEMA_NO_BANNER=1 is set. When run interactively — even with flags like --fix or --json omitted — the header is shown as normal. No flag is needed to control this: TTY detection handles it.