configuration reference

klint.yaml

Every option, generated live from klint.schema.json. Refreshed hourly.

root

string

Root directory used to resolve include paths and report relative file names. Defaults to the directory containing klint.config.json.

include

string[]

Glob patterns selecting which TypeScript files to lint. Prefix with ! to exclude. Defaults to ["."] which lints all .ts files under root. Example: ["src", "klint", "!**/node_modules/**"]

plugins

"sonar"[]

Named rule bundles to enable. Each plugin applies a default set of rules at "error" severity. Individual rules from the bundle can be overridden or silenced via the rules map. Available: "sonar".

rules

Record<string, "error" | "warn" | "off" | object>

Map of rule name → severity or options. Example: { "no-floating-promise": "error", "no-sync-in-async": { "severity": "warn", "include": ["src/hooks/**"] } }.

Every rule takes either a severity string or an options object. The first rule below is fully expanded as a reference; all rules follow the same shape.

no-unguarded-json-parse

"error" | "warn" | "off" | object

Flags JSON.parse() calls that aren't wrapped in try/catch — malformed input crashes the process.

valueserrorwarnoff
exampleyaml
no-unguarded-json-parse: error
[ options-object field ]

severity

"error" | "warn" | "off"

Rule severity. "error" exits with code 2; "warn" reports but exits 0; "off" silences.

valueserrorwarnoff
[ options-object field ]

include

string[]

Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: ["src/hooks/**", "!src/hooks/scripts/**"]

all rules
no-sync-in-async
Flags synchronous I/O (readFileSync, execSync, etc.) inside async functions — blocks the event loop when an async equivalent is available.
no-floating-promise
Flags Promise-returning expressions that aren't awaited or .catch()-handled — silent failures and unpredictable execution order.
no-misused-promises
Flags Promises passed where the type expects a non-thenable (e.g. an if condition, a void callback). The Promise's truthiness — not its resolved value — drives the branch.
no-async-predicate
Flags async functions passed to .filter/.some/.every/.find/.findIndex. Array methods coerce the returned promise to truthy, so every element appears to match.
no-date-equality
Flags Date comparisons with ==/===/!=/!== — these compare object identity, not the time value. Use .getTime() instead.
no-optional-chain-on-non-nullable
Flags ?. on receivers whose static type cannot be null or undefined. The optional chain is dead code and obscures the real shape.
no-object-in-template
Flags non-primitive objects interpolated into template literals — produces [object Object] unless toString() is overridden.
no-nested-template-literals
Flags template literals nested inside template literals — almost always unreadable; extract to a variable.
no-consecutive-array-push
Flags consecutive arr.push(x); arr.push(y); calls — combine into arr.push(x, y) or arr.push(...items).
no-string-match
Flags String#match — prefer .matchAll() for iteration or .test() for boolean checks. .match() returns different shapes depending on the /g flag.
sonar/prefer-string-replaceall
Flags .replace(/x/g, y) on strings — .replaceAll('x', y) is clearer and faster for all-occurrence replacements.
sonar/prefer-string-raw-regexp
Flags regex literals built from escape-heavy string concatenation — String.raw drops the double backslashes.
sonar/prefer-string-raw
Flags string literals with multiple backslash escapes — String.raw is more readable (Windows paths, regex source, etc.).
sonar/prefer-nullish-coalescing-assign
Flags explicit nullish assignment guards — x ??= y is the dedicated nullish assignment operator.
sonar/no-single-char-class
Flags single-character character classes in regex (e.g. /[a]/) — drop the brackets; the regex matches the same.
sonar/prefer-at
Flags negative-index access via arr[arr.length - 1].at(-1) reads cleaner for last-element access.

arch

object

Architecture-as-Code constraints — layers, import boundaries, forbidden patterns, singleton locations, comment budgets. Enforced by the arch engine alongside the rule set.

layers

Record<string, string[]>

Named architectural zones. Keys are arbitrary layer names; values are glob patterns matching the files in that layer. Other arch rules reference these names instead of repeating globs.

exampleyaml
layers:
  core:   ["src/hooks/lib/**", "src/tools/**"]
  skills: ["assets/skills/**"]
  dao:    ["src/dao/**"]

imports

object[]

Import-boundary rules. The dependency graph your README claims you have.

[ array item field ]

from

string | string[]

Source — a layer name or glob(s) for the files this rule applies to. Imports originating in from are checked against deny/allow.

[ array item field ]

deny

string | string[]

Layer name(s) or glob(s) the source is not allowed to import from. Mutually exclusive in spirit with allow (use one or the other).

[ array item field ]

allow

string | string[]

Allowlist of layer name(s) / glob(s) the source may import from. Anything not matching allow is denied.

[ array item field ]

deny-packages

string | string[]

Package specifier(s) the source is not allowed to import — npm packages, node: builtins, Python pip packages and stdlib modules, Swift system frameworks, external Rust crates, and C# namespaces or NuGet packages, which deny/allow cannot reach because they never resolve to a project file. Matching is per segment: / in TypeScript/JavaScript, so next also covers next/headers while nextra is unaffected; . in Python, Swift, and C#, so os also covers os.path while oscrypto is unaffected; and :: in Rust, so tokio also covers tokio::sync::Mutex while tokio_util is unaffected. Python, Swift, Rust, and C# sources are checked by the Rust engine.

[ array item field ]

type-only

string

When set to "allow", import type statements that would otherwise be denied are permitted. In Python, imports inside an if TYPE_CHECKING: block are exempted the same way. Useful for type-only references across layers.

[ array item field ]

message

string

Custom error message reported when the rule is violated. Shown in the agent's event stream alongside file:line.

[ array item field ]

severity

"error" | "warn"

Severity for the arch rule. "error" exits with code 2; "warn" reports but exits 0. (arch rules cannot be "off" — remove the entry instead.)

valueserrorwarn

forbidden

object | object[]

Forbidden-pattern rules. Block literal strings scoped to a layer.

singleton

object | object[]

Singleton-location rules. Pin a pattern to one file; every other touch is a violation.

maxLines

object[]

Per-file line-count limits. Cap how long files in a layer may grow, with different ceilings per scope.

[ array item field ]

limit

integer

Maximum number of physical lines a file may contain. Counts every line including blanks and comments. A file with more lines than this is a violation.

[ array item field ]

in

string | string[]

Layer name(s) or glob(s) the limit applies to. Files outside the scope are not checked.

[ array item field ]

message

string

Optional error message. Defaults to a message naming the limit (e.g. 'File exceeds the maximum of 300 lines').

[ array item field ]

severity

"error" | "warn"

Severity for the arch rule. "error" exits with code 2; "warn" reports but exits 0. (arch rules cannot be "off" — remove the entry instead.)

valueserrorwarn

Per-file comment-density ceilings. Cap the share of a file that may be explanatory comments; doc-comments are exempt by default.

[ array item field ]

limit

number

Maximum percentage of a file's physical lines that may be comment lines. Denominator is total physical lines (code + comments + blanks), the same count maxLines uses. A file above this ratio is a violation.

[ array item field ]

Count doc-comments toward the limit. Doc-comments are /** … */ JSDoc blocks (TS/JS), /// (Swift and C#), and /// or //! (Rust); ordinary //, /* */, and # comments are always counted. Defaults to false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python: docstrings are string expressions rather than comments, so they never count toward either limit.

[ array item field ]

ignore

string | string[]

Comment lines matching these patterns are structural, not prose — marker comments a build or codegen step reads, for example — and count toward neither limit. A literal substring by default; prefix with re: for a regex. Ignored lines still count in the density denominator (they are physical lines) and still hold a comment block together, so a marker cannot be used to split an over-tall block.

[ array item field ]

in

string | string[]

Layer name(s) or glob(s) the limit applies to. Files outside the scope are not checked.

[ array item field ]

message

string

Optional error message. Defaults to a message naming the measured density and the limit.

[ array item field ]

severity

"error" | "warn"

Severity for the arch rule. "error" exits with code 2; "warn" reports but exits 0. (arch rules cannot be "off" — remove the entry instead.)

valueserrorwarn

Per-block comment-height caps. Limit how many comment lines may stack together; doc-comments are exempt by default.

[ array item field ]

limit

integer

Maximum number of consecutive comment lines allowed in a single block. A run of more comment lines than this — e.g. a tall comment stacked above a statement — is a violation, reported at the first offending line.

[ array item field ]

Count doc-comments toward the limit. Doc-comments are /** … */ JSDoc blocks (TS/JS), /// (Swift and C#), and /// or //! (Rust); ordinary //, /* */, and # comments are always counted. Defaults to false, so documentation is exempt and only explanatory/inline comments are capped. Has no effect on Python: docstrings are string expressions rather than comments, so they never count toward either limit.

[ array item field ]

ignore

string | string[]

Comment lines matching these patterns are structural, not prose — marker comments a build or codegen step reads, for example — and count toward neither limit. A literal substring by default; prefix with re: for a regex. Ignored lines still count in the density denominator (they are physical lines) and still hold a comment block together, so a marker cannot be used to split an over-tall block.

[ array item field ]

in

string | string[]

Layer name(s) or glob(s) the limit applies to. Files outside the scope are not checked.

[ array item field ]

message

string

Optional error message. Defaults to a message naming the consecutive-line limit.

[ array item field ]

severity

"error" | "warn"

Severity for the arch rule. "error" exits with code 2; "warn" reports but exits 0. (arch rules cannot be "off" — remove the entry instead.)

valueserrorwarn