The hygiene command walks every **/*.ts manifest under a scan root (default manifests/), runs each through schema validation, per-step preflight, and per-skill manifest-level checks, and emits a grouped markdown + JSON report. Fully offline — no instance required. Runs in a few seconds for 100-ish manifests and exits non-zero on any error, so it drops cleanly into CI or a pre-commit gate.
hygiene
Offline manifest sweep — schema + preflight + per-skill checks across all manifests.
sn hygiene [--dir <path>] [--json] [--output <path>] Sweep every manifest through static validation checks.
Flags
| Flag | Default | Description |
|---|---|---|
| --dir <path> | — | Scan root. Defaults to `manifests/`. |
| --json | — | Emit machine-readable JSON instead of the grouped markdown report. |
| --output <path> | — | Write the report to this file instead of stdout. |
Examples
sn hygieneScanning manifests/ …
✓ 94 manifests clean
✗ 5 manifests with errors
✗ manifests/test-catalog-variables/manifest.ts
step create_item_var_q1 (catalog.create_item_option):
required field `question_text` missing
step rules_populate (decision-table.rulesPopulateAllElements):
declared result element `approval_level` never populated in any rule
(…)
5 error(s) across 11 step(s). Exit 1.
sn hygiene --dir manifests --json --output reports/hygiene.jsonScanning manifests/ …
✓ 12 manifests clean
✗ 0 manifests with errors
Report: reports/hygiene.json
Hygiene is a strict superset of sn lint run across a directory: same preflight engine, plus per-skill manifestChecks that catch cross-step invariants (e.g. decision-table result elements declared but never populated). Use sn lint <manifest.ts> when iterating on a single manifest; use sn hygiene as the batch gate.
SN_SUPPRESS_UI_PATHS=1 silences the clickable instance URLs in sn execute, sn tracked, and sn drift output — useful when capturing clean logs for review. It doesn't affect sn hygiene (which is offline).