Skip to content

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

bash
sickbay init

This generates a sickbay.config.ts listing every check that applies to your project:

ts
/** @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:

ts
/** @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:

ts
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:

  1. sickbay.config.ts
  2. sickbay.config.js
  3. sickbay.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:

bash
sickbay init --sync
# Added 3 new check(s): next-images, next-link, next-fonts

New 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:

bash
sickbay init --reset-config
# Regenerated sickbay.config.ts

Use 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:

ts
/** @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

CheckThresholdDefault
complexityPer-file-type { warn, critical } line countsVaries by type (e.g. components: 300/500)
coveragelines, functions (percentage)80, 80
jscpdwarnPercent, criticalPercent5, 20
eslintmaxErrors10
typescriptmaxErrors20
madgemaxCircular5
outdatedmaxOutdated15
todo-scannerpatterns (array of strings)['TODO', 'FIXME', 'HACK']
asset-sizeimageWarn, imageCritical, svgWarn, fontWarn, totalWarn, totalCritical (bytes)500KB, 2MB, 100KB, 500KB, 5MB, 10MB
source-map-explorerwarnSize, failSize (bytes)500KB, 1MB
license-checkerblocklist (array of SPDX IDs)GPL-2.0, GPL-3.0, AGPL-3.0, LGPL-2.1, LGPL-3.0, CC-BY-NC
gitstaleMonths, maxRemoteBranches6, 20

Checks not listed above are binary (enable/disable only).

File Type Thresholds for complexity

File TypeDefault WarnDefault Critical
react-component300500
custom-hook150250
node-service500800
route-file250400
ts-utility6001000
configNo limitNo limit
testNo limitNo limit
general400600

Path Exclusions

Exclude files from all checks or specific checks using glob patterns (picomatch syntax):

ts
/** @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:

ts
/** @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:

ts
/** @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 paths
  • match — 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's node-security, and a Next.js app's next-images all appear in the root config
  • Runs a monorepo baseline scan — scans all packages in parallel and saves the combined MonorepoReport as 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 only

Merge Semantics

When both root and package configs exist, they are merged with these rules:

FieldMerge behavior
checksPer-key override — package wins on collision
excludeAdditive — package paths are appended to root paths
weightsPer-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.

ts
// Root: sickbay.config.ts
/** @type {import('sickbay/config').SickbayConfig} */
export default {
  checks: {
    jscpd: false, // disabled globally
    coverage: {
      thresholds: { lines: 80 },
    },
  },
  exclude: ['src/generated/**'],
};
ts
// 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.

Released under the MIT License.