Configuration
Sickbay works out of the box with zero configuration. When you're ready to customize, create a sickbay.config.ts at your project root — or run sickbay init to generate one automatically.
Getting Started
sickbay initThis generates a sickbay.config.ts listing every check that applies to your project:
/** @type {import('sickbay/config').SickbayConfig} */
export default {
checks: {
// --- Dependencies ---
knip: true,
depcheck: true,
outdated: true,
// --- Code Quality ---
madge: true,
complexity: true,
// ... more checks based on your project
},
};The generated file is framework-aware — a React project sees react-perf, a Next.js project sees next-images, next-link, etc. Only checks that apply to your detected stack are listed.
Disabling Checks
Set any check to false to skip it:
/** @type {import('sickbay/config').SickbayConfig} */
export default {
checks: {
knip: true,
jscpd: false, // disable code duplication check
'react-perf': false, // disable React performance check
madge: true,
},
};Checks not listed in the config are enabled by default. The config is overrides-only — you don't need to list everything.
When custom configuration is active, Sickbay shows a notice in the CLI output and TUI dashboard.
Using defineConfig for Autocomplete
If you have sickbay installed as a devDependency, you can use the defineConfig helper for full IDE autocomplete:
import { defineConfig } from 'sickbay/config';
export default defineConfig({
checks: {
jscpd: false,
},
});This is optional — the JSDoc @type annotation generated by sickbay init provides the same autocomplete without needing an import.
Config File Formats
Sickbay looks for config files in this order:
sickbay.config.tssickbay.config.jssickbay.config.mjs
The file is executed at runtime using jiti — no build step required.
Keeping Config in Sync
When Sickbay adds new checks in an update, your existing config still works — unlisted checks run with defaults. But you lose the discoverability benefit of seeing all checks in one place.
sickbay init --sync
Appends newly available checks to your existing config without touching existing entries:
sickbay init --sync
# Added 3 new check(s): next-images, next-link, next-fontsNew checks are added as true (enabled) with // --- New: Category --- comment headers so you can easily spot them. Your existing thresholds, suppress rules, and disabled checks are preserved.
Run this after updating Sickbay (npm update sickbay) to pick up any new runners.
sickbay init --reset-config
Regenerates the config from scratch, overwriting the existing file:
sickbay init --reset-config
# Regenerated sickbay.config.tsUse this when your config has drifted too far and you want a clean start. All customizations (thresholds, suppress rules, disabled checks) will be lost.
Scan-Time Notifications
When Sickbay detects checks running that aren't listed in your config, it prints a notice:
Note: 3 check(s) running but not listed in your config: next-images, next-link, next-fonts.
Run `sickbay init --sync` to add them.This only appears when a config file exists. Zero-config users never see it.
Full Schema Reference
The config supports threshold overrides, path exclusions, scoring weight adjustments, and per-check suppression rules.
Threshold Overrides
Override the default thresholds for checks that support them:
/** @type {import('sickbay/config').SickbayConfig} */
export default {
checks: {
complexity: {
thresholds: {
'react-component': { warn: 400, critical: 600 },
'custom-hook': { warn: 200, critical: 350 },
},
},
coverage: {
thresholds: {
lines: 90,
functions: 85,
},
},
jscpd: {
thresholds: {
warnPercent: 10,
criticalPercent: 30,
},
},
},
};Configurable Checks
| Check | Threshold | Default |
|---|---|---|
complexity | Per-file-type { warn, critical } line counts | Varies by type (e.g. components: 300/500) |
coverage | lines, functions (percentage) | 80, 80 |
jscpd | warnPercent, criticalPercent | 5, 20 |
eslint | maxErrors | 10 |
typescript | maxErrors | 20 |
madge | maxCircular | 5 |
outdated | maxOutdated | 15 |
todo-scanner | patterns (array of strings) | ['TODO', 'FIXME', 'HACK'] |
asset-size | imageWarn, imageCritical, svgWarn, fontWarn, totalWarn, totalCritical (bytes) | 500KB, 2MB, 100KB, 500KB, 5MB, 10MB |
source-map-explorer | warnSize, failSize (bytes) | 500KB, 1MB |
license-checker | blocklist (array of SPDX IDs) | GPL-2.0, GPL-3.0, AGPL-3.0, LGPL-2.1, LGPL-3.0, CC-BY-NC |
git | staleMonths, maxRemoteBranches | 6, 20 |
Checks not listed above are binary (enable/disable only).
File Type Thresholds for complexity
| File Type | Default Warn | Default Critical |
|---|---|---|
react-component | 300 | 500 |
custom-hook | 150 | 250 |
node-service | 500 | 800 |
route-file | 250 | 400 |
ts-utility | 600 | 1000 |
config | No limit | No limit |
test | No limit | No limit |
general | 400 | 600 |
Path Exclusions
Exclude files from all checks or specific checks using glob patterns (picomatch syntax):
/** @type {import('sickbay/config').SickbayConfig} */
export default {
// Global — applies to all file-scanning checks
exclude: ['src/generated/**', 'src/legacy/**'],
checks: {
complexity: {
// Per-check — only complexity skips this path
exclude: ['src/migrations/**'],
},
},
};Global and per-check excludes are merged — a file matching either is skipped. Exclude patterns apply to checks that scan files directly: complexity, todo-scanner, secrets, react-perf, and asset-size.
Scoring Weights
Override how much each category contributes to the overall score:
/** @type {import('sickbay/config').SickbayConfig} */
export default {
weights: {
security: 0.4, // default: 0.30
dependencies: 0.25, // default: 0.25
'code-quality': 0.2, // default: 0.25
performance: 0.1, // default: 0.15
git: 0.05, // default: 0.05
},
};You only need to list categories you want to change. Omitted categories keep their defaults. All values are normalized proportionally — they don't need to sum to 1.
For example, setting security: 0.50 with all other defaults produces weights of roughly: security 42%, dependencies 21%, code-quality 21%, performance 12%, git 4%. Security gets the largest share; other categories shrink proportionally but keep their relative ratios.
Weight values must be greater than 0.
Suppression Rules
Suppress specific findings that are intentional or irrelevant:
/** @type {import('sickbay/config').SickbayConfig} */
export default {
checks: {
secrets: {
suppress: [
{ path: 'src/config.ts', match: 'NEXT_PUBLIC_', reason: 'public keys, not secrets' },
],
},
'npm-audit': {
suppress: [{ match: 'GHSA-xxxx', reason: 'not exploitable in our context' }],
},
},
};Each suppression rule requires a reason to prevent suppress-and-forget drift. Rules support:
path— glob pattern matching file pathsmatch— substring match against issue messages (case-insensitive)reason— required explanation (shown in reports)
When both path and match are provided, both must match (AND logic).
See Suppress Rules for matching details, dashboard integration, and examples.
Monorepo Support
In a monorepo, place sickbay.config.ts at the workspace root. It applies to all packages during sickbay scans.
Initializing a Monorepo
When you run sickbay init at a monorepo root, Sickbay detects the workspace and:
- Generates a config with all checks — unions checks across every package so a React app's
react-perf, a Node API'snode-security, and a Next.js app'snext-imagesall appear in the root config - Runs a monorepo baseline scan — scans all packages in parallel and saves the combined
MonorepoReportas the baseline
sickbay init --sync is also monorepo-aware — it detects new checks that apply to any package, not just the root.
Per-Package Config
Individual packages can have their own sickbay.config.ts that overrides the root config:
my-monorepo/
sickbay.config.ts # root config — applies to all packages
packages/
react-app/
sickbay.config.ts # overrides root for this package only
node-api/
sickbay.config.ts # overrides root for this package onlyMerge Semantics
When both root and package configs exist, they are merged with these rules:
| Field | Merge behavior |
|---|---|
checks | Per-key override — package wins on collision |
exclude | Additive — package paths are appended to root paths |
weights | Per-category override — package wins on collision |
Example: if the root config disables jscpd but a package config sets jscpd: true, that package will run the duplication check.
// Root: sickbay.config.ts
/** @type {import('sickbay/config').SickbayConfig} */
export default {
checks: {
jscpd: false, // disabled globally
coverage: {
thresholds: { lines: 80 },
},
},
exclude: ['src/generated/**'],
};// packages/react-app/sickbay.config.ts
/** @type {import('sickbay/config').SickbayConfig} */
export default {
checks: {
jscpd: true, // re-enable for this package
coverage: {
thresholds: { lines: 90 }, // stricter threshold
},
},
exclude: ['src/stories/**'], // added to root excludes
};Result for react-app: jscpd enabled, coverage lines threshold 90, excludes src/generated/** and src/stories/**.
All Checks
For a complete list of checks with scoring details, see Health Checks and Scoring System.