Docs

This page isn't translated yet

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

FlagDescriptionDefault
--fixApply fixable issues (interactive, git-safe)false
--forceAllow a dirty working tree when fixingfalse
--jsonOutput diagnostics as JSONfalse
--no-git-checkSkip git repo check (CI mode)true
--only <id>Run only this check (repeatable)[]
--skip <id>Skip this check (repeatable)[]
-y, --yesSkip confirmation promptsfalse

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

CheckWhat it checksSpeedAuto-fix
ai-configAI assistant configfastno
analytics-configAnalytics configfastno
artifacts-configConfig & artifactsfastyes; sync
cloudflare-configCloudflare configfastno
fluid-spacing-breakpointsFluid spacing above the top breakpointfastno
legacy-tokensLegacy tokensfastyes; migrate-tokens
lexical-patchLexical admin patchfastyes; lexical-patch
localization-policyContent localization field policyfastno
localize-status-schemaPer-locale publication statusslowno
missing-tokensMissing tokensfastyes; token-migration:${c.version}
obfuscate-build-stepClass obfuscation wiringfastno
package-manager-pinpnpm version pinfastyes
peer-depsPeer depsfastyes
pending-db-renamesDatabase table namesslowno
pending-migrationsUpgrade planfastyes
replaced-proxyCustomised src/proxy.ts replaced by an upgradefastno
stale-referencesToken referencesfastyes; sync
stale-token-artifactsToken artifact cachefastyes; sync
ui-patchAdmin language cache patchfastyes; ui-patch
version-driftCLI versionslowno

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 what systhema upgrade would 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 via DATABASE_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 _status lives. Per-locale publishing is on by default whenever locales is configured, which moves _status out of the shared column on pages, posts and components and into their _locales siblings. Connects read-only via DATABASE_URI (Postgres/SQLite) and reads the live columns, so it also catches the inverse: a project that opted out of localizeStatus against a database that already moved. See Per-locale publishing.

  • package-manager-pin: package.json has no exact pnpm packageManager (missing, or floating like pnpm@10), or pins a pnpm that cannot install pnpm-lock.yaml. All pnpm releases since 9 write lockfileVersion: '9.0'; the patch and package-extension hashes tell them apart. pnpm 9 writes base32/md5, pnpm 10 sha256 under hash:, pnpm 11 and later a bare sha256 per patch, and a frozen install on another format fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH. A lockfile without those hashes works with any of them. When package.json has a pnpm field, 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; until pnpm install writes it, a frozen install fails with ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE. Silent without a pnpm-lock.yaml at the project root, so a workspace sub-project is not checked.

  • stale-token-artifacts: The installed @systhemaui/core does not carry this project's tokens — .systhema/artifacts/tokens/*.json and node_modules/@systhemaui/core/dist/tmp/tokens/*.json disagree (differing bytes, a missing file, or a phantom file the project no longer defines). Typically an install that replaced the package after the last sync; 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/core is not installed.

  • ai-config: (payload) AI assistant misconfiguration across the merged dotenv set (.env, .env.development, .env.production, .env.local; later overrides earlier) — unknown SYSTHEMA_AI_PROVIDER, a keyed provider without SYSTHEMA_AI_API_KEY, or openai-compatible without SYSTHEMA_AI_BASE_URL / SYSTHEMA_AI_MODEL.

  • analytics-config: (payload) Analytics dashboard misconfiguration across the same merged dotenv set — unknown SYSTHEMA_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), both CLOUDFLARE_API_TOKEN and CLOUDFLARE_API_KEY set at once (ambiguous), or a non-boolean SYSTHEMA_CLOUDFLARE_AUTO_PURGE.

  • stale-references: The agent-facing TOON references at .systhema/references/tokens/ are missing, or _index.toon says a different @systhemaui/core wrote them than the one installed. Reference generation is the last step of sync and 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/core is not installed, or when the stamp cannot be read.

  • obfuscate-build-step: optimization.obfuscateClasses is enabled in systhema.config but no package.json script chains systhema-core obfuscate after 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: The spacing config uses a fluid %screen% rule, but some configured breakpoint above the narrowest responsiveSizing mode has no mode of its own (neither exported nor declared in customTokens.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 declared localization.policy. Until it does, content localization runs the legacy name-allowlist policy: form notification recipients, per-page noindex and redirect targets are shared across every locale.

  • replaced-proxy: src/proxy.ts is a stock Systhema gateway while src/proxy.ts.bak matches 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.