@konvert7/klint 0.36.0 → 0.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/klint.schema.json CHANGED
@@ -1,1275 +1,1099 @@
1
1
  {
2
+ "$id": "https://raw.githubusercontent.com/konvert7/klint/refs/tags/v0.37.1/klint.schema.json",
2
3
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://raw.githubusercontent.com/konvert7/klint/refs/tags/v0.36.0/klint.schema.json",
4
- "type": "object",
4
+ "additionalProperties": false,
5
+ "description": "klint configuration. Lives at klint.yaml (or klint.config.json) next to biome.json and knip.json.",
5
6
  "properties": {
6
7
  "$schema": {
7
8
  "description": "JSON Schema reference. Use ./klint.schema.json for local validation, or a version-tagged URL such as https://raw.githubusercontent.com/konvert7/klint/refs/tags/v0.29.0/klint.schema.json to pin the schema. A tagged URL naming a version other than the installed one raises a klint/schema-version warning.",
8
9
  "type": "string"
9
10
  },
10
- "root": {
11
- "description": "Root directory used to resolve include paths and report relative file names. Defaults to the directory containing klint.config.json.",
12
- "type": "string"
13
- },
14
- "include": {
15
- "description": "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/**\"]",
16
- "type": "array",
17
- "items": {
18
- "type": "string"
19
- }
20
- },
21
- "plugins": {
22
- "description": "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\".",
23
- "type": "array",
24
- "items": {
25
- "type": "string",
26
- "enum": [
27
- "sonar"
28
- ]
29
- }
30
- },
31
- "rules": {
32
- "description": "Map of rule name → severity or options. Example: { \"no-floating-promise\": \"error\", \"no-sync-in-async\": { \"severity\": \"warn\", \"include\": [\"src/hooks/**\"] } }.",
33
- "type": "object",
11
+ "arch": {
12
+ "additionalProperties": false,
13
+ "description": "Architecture-as-Code constraints — layers, import boundaries, forbidden patterns, singleton locations, comment budgets. Enforced by the arch engine alongside the rule set.",
34
14
  "properties": {
35
- "no-unguarded-json-parse": {
36
- "description": "Flags `JSON.parse()` calls that aren't wrapped in try/catch — malformed input crashes the process.",
37
- "examples": [
38
- "no-unguarded-json-parse: error"
39
- ],
40
- "anyOf": [
41
- {
42
- "type": "string",
43
- "enum": [
44
- "error",
45
- "warn",
46
- "off"
47
- ],
48
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
49
- },
50
- {
51
- "type": "object",
52
- "properties": {
53
- "severity": {
54
- "type": "string",
55
- "enum": [
56
- "error",
57
- "warn",
58
- "off"
59
- ],
60
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
61
- },
62
- "include": {
63
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
64
- "type": "array",
65
- "items": {
15
+ "forbidden": {
16
+ "description": "Forbidden-pattern rules. Block literal strings scoped to a layer.",
17
+ "items": {
18
+ "anyOf": [
19
+ {
20
+ "additionalProperties": false,
21
+ "properties": {
22
+ "in": {
23
+ "anyOf": [
24
+ {
25
+ "type": "string"
26
+ },
27
+ {
28
+ "items": {
29
+ "type": "string"
30
+ },
31
+ "type": "array"
32
+ }
33
+ ],
34
+ "description": "Layer name(s) or glob(s) the pattern is forbidden in. Files outside the scope are not checked."
35
+ },
36
+ "message": {
37
+ "description": "Required error message explaining why the pattern is forbidden. Shown to the agent so it can fix the call site.",
38
+ "type": "string"
39
+ },
40
+ "pattern": {
41
+ "description": "Pattern to search for, scanned per line. A literal substring by default (e.g. a console logging call). Prefix with `re:` to match a regular expression instead (e.g. `re:\\bp-\\[\\d+px\\]`). Regexes must use the common JS/RE2 subset — no lookaround or backreferences — so the TS and Rust engines agree. Note: a literal pattern that itself begins with `re:` cannot be expressed; such a value is always read as a regex.",
42
+ "type": "string"
43
+ },
44
+ "severity": {
45
+ "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)",
46
+ "enum": [
47
+ "error",
48
+ "warn",
49
+ null
50
+ ],
66
51
  "type": "string"
67
52
  }
68
- }
53
+ },
54
+ "required": [
55
+ "pattern",
56
+ "in",
57
+ "message"
58
+ ],
59
+ "type": "object"
69
60
  },
70
- "additionalProperties": false,
71
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
72
- }
73
- ]
74
- },
75
- "no-sync-in-async": {
76
- "description": "Flags synchronous I/O (`readFileSync`, `execSync`, etc.) inside async functions — blocks the event loop when an async equivalent is available.",
77
- "examples": [
78
- "no-sync-in-async: error",
79
- "no-sync-in-async:\n severity: warn\n include: [\"src/server/**\"]"
80
- ],
81
- "anyOf": [
82
- {
83
- "type": "string",
84
- "enum": [
85
- "error",
86
- "warn",
87
- "off"
88
- ],
89
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
90
- },
91
- {
92
- "type": "object",
93
- "properties": {
94
- "severity": {
95
- "type": "string",
96
- "enum": [
97
- "error",
98
- "warn",
99
- "off"
100
- ],
101
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
61
+ {
62
+ "additionalProperties": false,
63
+ "properties": {
64
+ "in": {
65
+ "anyOf": [
66
+ {
67
+ "type": "string"
68
+ },
69
+ {
70
+ "items": {
71
+ "type": "string"
72
+ },
73
+ "type": "array"
74
+ }
75
+ ],
76
+ "description": "Layer name(s) or glob(s) the element is forbidden in. Files outside the scope are not checked."
77
+ },
78
+ "jsx-element": {
79
+ "anyOf": [
80
+ {
81
+ "type": "string"
82
+ },
83
+ {
84
+ "items": {
85
+ "type": "string"
86
+ },
87
+ "type": "array"
88
+ }
89
+ ],
90
+ "description": "JSX intrinsic element name(s) to forbid (e.g. `button`, `input`, `label`). AST-matched on opening and self-closing tag names — robust to whitespace, attributes, and naming collisions like `<buttonGroup>`."
91
+ },
92
+ "message": {
93
+ "description": "Required error message explaining why the raw element is forbidden. Typically points at the design-system replacement (e.g. `Use <Button /> from @/components/ui/button`).",
94
+ "type": "string"
95
+ },
96
+ "severity": {
97
+ "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)",
98
+ "enum": [
99
+ "error",
100
+ "warn",
101
+ null
102
+ ],
103
+ "type": "string"
104
+ }
102
105
  },
103
- "include": {
104
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
105
- "type": "array",
106
- "items": {
106
+ "required": [
107
+ "jsx-element",
108
+ "in",
109
+ "message"
110
+ ],
111
+ "type": "object"
112
+ }
113
+ ],
114
+ "description": "A forbidden-pattern rule. Blocks either a literal substring (`pattern:`) or a JSX element (`jsx-element:`) inside specific layers.",
115
+ "examples": [
116
+ "arch:\n forbidden:\n - pattern: \"debugLog(\"\n in: core\n message: \"Leaks into the agent event stream\"",
117
+ "arch:\n forbidden:\n - jsx-element: [\"button\", \"input\", \"label\"]\n in: [\"src/app/**/*.tsx\", \"src/components/**/*.tsx\", \"!src/components/ui/**\"]\n message: \"Use the design-system primitives in @/components/ui/* instead of raw HTML elements.\""
118
+ ]
119
+ },
120
+ "type": "array"
121
+ },
122
+ "imports": {
123
+ "description": "Import-boundary rules. The dependency graph your README claims you have.",
124
+ "items": {
125
+ "additionalProperties": false,
126
+ "description": "An import-boundary rule. Restricts which layers a source can or cannot import from.",
127
+ "examples": [
128
+ "arch:\n imports:\n - from: skills\n deny: core\n message: \"Skills must be self-contained and portable\"\n severity: warn",
129
+ "arch:\n imports:\n - from: [\"src/dao/**\"]\n allow: [\"src/dao/**\", \"src/prisma/**\", \"src/types/**\"]"
130
+ ],
131
+ "properties": {
132
+ "allow": {
133
+ "anyOf": [
134
+ {
107
135
  "type": "string"
136
+ },
137
+ {
138
+ "items": {
139
+ "type": "string"
140
+ },
141
+ "type": "array"
108
142
  }
109
- }
143
+ ],
144
+ "description": "Allowlist of layer name(s) / glob(s) the source may import from. Anything not matching `allow` is denied."
110
145
  },
111
- "additionalProperties": false,
112
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
113
- }
114
- ]
115
- },
116
- "no-floating-promise": {
117
- "description": "Flags Promise-returning expressions that aren't awaited or `.catch()`-handled — silent failures and unpredictable execution order.",
118
- "examples": [
119
- "no-floating-promise: error"
120
- ],
121
- "anyOf": [
122
- {
123
- "type": "string",
124
- "enum": [
125
- "error",
126
- "warn",
127
- "off"
128
- ],
129
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
130
- },
131
- {
132
- "type": "object",
133
- "properties": {
134
- "severity": {
135
- "type": "string",
136
- "enum": [
137
- "error",
138
- "warn",
139
- "off"
140
- ],
141
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
142
- },
143
- "include": {
144
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
145
- "type": "array",
146
- "items": {
146
+ "deny": {
147
+ "anyOf": [
148
+ {
147
149
  "type": "string"
150
+ },
151
+ {
152
+ "items": {
153
+ "type": "string"
154
+ },
155
+ "type": "array"
148
156
  }
149
- }
157
+ ],
158
+ "description": "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)."
150
159
  },
151
- "additionalProperties": false,
152
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
153
- }
154
- ]
155
- },
156
- "no-misused-promises": {
157
- "description": "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.",
158
- "examples": [
159
- "no-misused-promises: error"
160
- ],
161
- "anyOf": [
162
- {
163
- "type": "string",
164
- "enum": [
165
- "error",
166
- "warn",
167
- "off"
168
- ],
169
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
170
- },
171
- {
172
- "type": "object",
173
- "properties": {
174
- "severity": {
175
- "type": "string",
176
- "enum": [
177
- "error",
178
- "warn",
179
- "off"
180
- ],
181
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
182
- },
183
- "include": {
184
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
185
- "type": "array",
186
- "items": {
160
+ "deny-packages": {
161
+ "anyOf": [
162
+ {
187
163
  "type": "string"
164
+ },
165
+ {
166
+ "items": {
167
+ "type": "string"
168
+ },
169
+ "type": "array"
188
170
  }
189
- }
171
+ ],
172
+ "description": "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."
190
173
  },
191
- "additionalProperties": false,
192
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
193
- }
194
- ]
195
- },
196
- "no-async-predicate": {
197
- "description": "Flags `async` functions passed to `.filter`/`.some`/`.every`/`.find`/`.findIndex`. Array methods coerce the returned promise to truthy, so every element appears to match.",
198
- "examples": [
199
- "no-async-predicate: error"
200
- ],
201
- "anyOf": [
202
- {
203
- "type": "string",
204
- "enum": [
205
- "error",
206
- "warn",
207
- "off"
208
- ],
209
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
210
- },
211
- {
212
- "type": "object",
213
- "properties": {
214
- "severity": {
215
- "type": "string",
216
- "enum": [
217
- "error",
218
- "warn",
219
- "off"
220
- ],
221
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
222
- },
223
- "include": {
224
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
225
- "type": "array",
226
- "items": {
174
+ "from": {
175
+ "anyOf": [
176
+ {
227
177
  "type": "string"
178
+ },
179
+ {
180
+ "items": {
181
+ "type": "string"
182
+ },
183
+ "type": "array"
228
184
  }
229
- }
185
+ ],
186
+ "description": "Source — a layer name or glob(s) for the files this rule applies to. Imports originating in `from` are checked against `deny`/`allow`."
230
187
  },
231
- "additionalProperties": false,
232
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
233
- }
234
- ]
188
+ "message": {
189
+ "description": "Custom error message reported when the rule is violated. Shown in the agent's event stream alongside file:line.",
190
+ "type": "string"
191
+ },
192
+ "severity": {
193
+ "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)",
194
+ "enum": [
195
+ "error",
196
+ "warn",
197
+ null
198
+ ],
199
+ "type": "string"
200
+ },
201
+ "type-only": {
202
+ "description": "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.",
203
+ "enum": [
204
+ "allow",
205
+ null
206
+ ],
207
+ "type": "string"
208
+ }
209
+ },
210
+ "required": [
211
+ "from"
212
+ ],
213
+ "type": "object"
214
+ },
215
+ "type": "array"
235
216
  },
236
- "no-date-equality": {
237
- "description": "Flags `Date` comparisons with `==`/`===`/`!=`/`!==` — these compare object identity, not the time value. Use `.getTime()` instead.",
217
+ "layers": {
218
+ "additionalProperties": {
219
+ "items": {
220
+ "type": "string"
221
+ },
222
+ "type": "array"
223
+ },
224
+ "description": "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.",
238
225
  "examples": [
239
- "no-date-equality: error"
226
+ "layers:\n core: [\"src/hooks/lib/**\", \"src/tools/**\"]\n skills: [\"assets/skills/**\"]\n dao: [\"src/dao/**\"]"
240
227
  ],
241
- "anyOf": [
242
- {
243
- "type": "string",
244
- "enum": [
245
- "error",
246
- "warn",
247
- "off"
248
- ],
249
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
250
- },
251
- {
252
- "type": "object",
253
- "properties": {
254
- "severity": {
255
- "type": "string",
256
- "enum": [
257
- "error",
258
- "warn",
259
- "off"
260
- ],
261
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
262
- },
263
- "include": {
264
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
265
- "type": "array",
266
- "items": {
228
+ "type": "object"
229
+ },
230
+ "maxCommentBlock": {
231
+ "description": "Per-block comment-height caps. Limit how many comment lines may stack together; doc-comments are exempt by default.",
232
+ "items": {
233
+ "additionalProperties": false,
234
+ "description": "A per-block comment-height cap. Limits how many comment lines may stack together, so comments stay terse and a long explanation becomes a well-named function instead. Doc-comments are exempt by default.",
235
+ "examples": [
236
+ "arch:\n maxCommentBlock:\n - limit: 2\n in: src/**\n message: \"Extract a named function instead of a comment block\""
237
+ ],
238
+ "properties": {
239
+ "countDocComments": {
240
+ "default": false,
241
+ "description": "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.",
242
+ "type": "boolean"
243
+ },
244
+ "ignore": {
245
+ "anyOf": [
246
+ {
267
247
  "type": "string"
248
+ },
249
+ {
250
+ "items": {
251
+ "type": "string"
252
+ },
253
+ "type": "array"
268
254
  }
269
- }
255
+ ],
256
+ "description": "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."
270
257
  },
271
- "additionalProperties": false,
272
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
273
- }
274
- ]
275
- },
276
- "no-optional-chain-on-non-nullable": {
277
- "description": "Flags `?.` on receivers whose static type cannot be `null` or `undefined`. The optional chain is dead code and obscures the real shape.",
278
- "examples": [
279
- "no-optional-chain-on-non-nullable: warn"
280
- ],
281
- "anyOf": [
282
- {
283
- "type": "string",
284
- "enum": [
285
- "error",
286
- "warn",
287
- "off"
288
- ],
289
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
290
- },
291
- {
292
- "type": "object",
293
- "properties": {
294
- "severity": {
295
- "type": "string",
296
- "enum": [
297
- "error",
298
- "warn",
299
- "off"
300
- ],
301
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
302
- },
303
- "include": {
304
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
305
- "type": "array",
306
- "items": {
258
+ "in": {
259
+ "anyOf": [
260
+ {
307
261
  "type": "string"
262
+ },
263
+ {
264
+ "items": {
265
+ "type": "string"
266
+ },
267
+ "type": "array"
308
268
  }
309
- }
269
+ ],
270
+ "description": "Layer name(s) or glob(s) the limit applies to. Files outside the scope are not checked."
310
271
  },
311
- "additionalProperties": false,
312
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
313
- }
314
- ]
315
- },
316
- "no-object-in-template": {
317
- "description": "Flags non-primitive objects interpolated into template literals — produces `[object Object]` unless `toString()` is overridden.",
318
- "examples": [
319
- "no-object-in-template: error"
320
- ],
321
- "anyOf": [
322
- {
323
- "type": "string",
324
- "enum": [
325
- "error",
326
- "warn",
327
- "off"
328
- ],
329
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
272
+ "limit": {
273
+ "description": "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.",
274
+ "format": "uint",
275
+ "minimum": 1,
276
+ "type": "integer"
277
+ },
278
+ "message": {
279
+ "description": "Optional error message. Defaults to a message naming the consecutive-line limit.",
280
+ "type": "string"
281
+ },
282
+ "severity": {
283
+ "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)",
284
+ "enum": [
285
+ "error",
286
+ "warn",
287
+ null
288
+ ],
289
+ "type": "string"
290
+ }
330
291
  },
331
- {
332
- "type": "object",
333
- "properties": {
334
- "severity": {
335
- "type": "string",
336
- "enum": [
337
- "error",
338
- "warn",
339
- "off"
340
- ],
341
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
342
- },
343
- "include": {
344
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
345
- "type": "array",
346
- "items": {
292
+ "required": [
293
+ "limit",
294
+ "in"
295
+ ],
296
+ "type": "object"
297
+ },
298
+ "type": "array"
299
+ },
300
+ "maxCommentDensity": {
301
+ "description": "Per-file comment-density ceilings. Cap the share of a file that may be explanatory comments; doc-comments are exempt by default.",
302
+ "items": {
303
+ "additionalProperties": false,
304
+ "description": "A per-file comment-density ceiling. Caps how much of a file may be explanatory comments — the honest way to push intent into code and names rather than prose (and to keep token cost down), while leaving doc-comments free by default.",
305
+ "examples": [
306
+ "arch:\n maxCommentDensity:\n - limit: 5\n in: src/**\n message: \"Encode intent in names, not comments\"",
307
+ "arch:\n maxCommentDensity:\n - limit: 10\n countDocComments: true\n in: src/**"
308
+ ],
309
+ "properties": {
310
+ "countDocComments": {
311
+ "default": false,
312
+ "description": "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.",
313
+ "type": "boolean"
314
+ },
315
+ "ignore": {
316
+ "anyOf": [
317
+ {
347
318
  "type": "string"
319
+ },
320
+ {
321
+ "items": {
322
+ "type": "string"
323
+ },
324
+ "type": "array"
348
325
  }
349
- }
326
+ ],
327
+ "description": "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."
350
328
  },
351
- "additionalProperties": false,
352
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
353
- }
354
- ]
355
- },
356
- "no-nested-template-literals": {
357
- "description": "Flags template literals nested inside template literals — almost always unreadable; extract to a variable.",
358
- "examples": [
359
- "no-nested-template-literals: warn"
360
- ],
361
- "anyOf": [
362
- {
363
- "type": "string",
364
- "enum": [
365
- "error",
366
- "warn",
367
- "off"
368
- ],
369
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
370
- },
371
- {
372
- "type": "object",
373
- "properties": {
374
- "severity": {
375
- "type": "string",
376
- "enum": [
377
- "error",
378
- "warn",
379
- "off"
380
- ],
381
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
382
- },
383
- "include": {
384
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
385
- "type": "array",
386
- "items": {
329
+ "in": {
330
+ "anyOf": [
331
+ {
387
332
  "type": "string"
333
+ },
334
+ {
335
+ "items": {
336
+ "type": "string"
337
+ },
338
+ "type": "array"
388
339
  }
389
- }
340
+ ],
341
+ "description": "Layer name(s) or glob(s) the limit applies to. Files outside the scope are not checked."
390
342
  },
391
- "additionalProperties": false,
392
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
393
- }
394
- ]
395
- },
396
- "no-consecutive-array-push": {
397
- "description": "Flags consecutive `arr.push(x); arr.push(y);` calls — combine into `arr.push(x, y)` or `arr.push(...items)`.",
398
- "examples": [
399
- "no-consecutive-array-push: warn"
400
- ],
401
- "anyOf": [
402
- {
403
- "type": "string",
404
- "enum": [
405
- "error",
406
- "warn",
407
- "off"
408
- ],
409
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
343
+ "limit": {
344
+ "description": "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.",
345
+ "exclusiveMinimum": 0,
346
+ "format": "double",
347
+ "maximum": 100,
348
+ "type": "number"
349
+ },
350
+ "message": {
351
+ "description": "Optional error message. Defaults to a message naming the measured density and the limit.",
352
+ "type": "string"
353
+ },
354
+ "severity": {
355
+ "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)",
356
+ "enum": [
357
+ "error",
358
+ "warn",
359
+ null
360
+ ],
361
+ "type": "string"
362
+ }
410
363
  },
411
- {
412
- "type": "object",
413
- "properties": {
414
- "severity": {
415
- "type": "string",
416
- "enum": [
417
- "error",
418
- "warn",
419
- "off"
420
- ],
421
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
422
- },
423
- "include": {
424
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
425
- "type": "array",
426
- "items": {
364
+ "required": [
365
+ "limit",
366
+ "in"
367
+ ],
368
+ "type": "object"
369
+ },
370
+ "type": "array"
371
+ },
372
+ "maxLines": {
373
+ "description": "Per-file line-count limits. Cap how long files in a layer may grow, with different ceilings per scope.",
374
+ "items": {
375
+ "additionalProperties": false,
376
+ "description": "A per-file line-count limit. Caps how long any file in scope may grow — the honest way to keep modules small without a formatter dependency.",
377
+ "examples": [
378
+ "arch:\n maxLines:\n - limit: 300\n in: src/**\n message: \"Split this module\"",
379
+ "arch:\n maxLines:\n - limit: 300\n in: src/**\n - limit: 600\n in: tests/**"
380
+ ],
381
+ "properties": {
382
+ "in": {
383
+ "anyOf": [
384
+ {
427
385
  "type": "string"
386
+ },
387
+ {
388
+ "items": {
389
+ "type": "string"
390
+ },
391
+ "type": "array"
428
392
  }
429
- }
393
+ ],
394
+ "description": "Layer name(s) or glob(s) the limit applies to. Files outside the scope are not checked."
430
395
  },
431
- "additionalProperties": false,
432
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
433
- }
434
- ]
396
+ "limit": {
397
+ "description": "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.",
398
+ "format": "uint",
399
+ "minimum": 1,
400
+ "type": "integer"
401
+ },
402
+ "message": {
403
+ "description": "Optional error message. Defaults to a message naming the limit (e.g. 'File exceeds the maximum of 300 lines').",
404
+ "type": "string"
405
+ },
406
+ "severity": {
407
+ "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)",
408
+ "enum": [
409
+ "error",
410
+ "warn",
411
+ null
412
+ ],
413
+ "type": "string"
414
+ }
415
+ },
416
+ "required": [
417
+ "limit",
418
+ "in"
419
+ ],
420
+ "type": "object"
421
+ },
422
+ "type": "array"
435
423
  },
436
- "no-string-match": {
437
- "description": "Flags `String#match` — prefer `.matchAll()` for iteration or `.test()` for boolean checks. `.match()` returns different shapes depending on the `/g` flag.",
438
- "examples": [
439
- "no-string-match: error"
440
- ],
424
+ "singleton": {
425
+ "description": "Singleton-location rules. Pin a pattern to one file; every other touch is a violation.",
426
+ "items": {
427
+ "anyOf": [
428
+ {
429
+ "additionalProperties": false,
430
+ "properties": {
431
+ "in": {
432
+ "anyOf": [
433
+ {
434
+ "type": "string"
435
+ },
436
+ {
437
+ "items": {
438
+ "type": "string"
439
+ },
440
+ "type": "array"
441
+ }
442
+ ],
443
+ "description": "Optional scope. Limit the scan to these layers/globs; defaults to all files in `include`."
444
+ },
445
+ "message": {
446
+ "type": "string"
447
+ },
448
+ "only": {
449
+ "description": "The single file path where this pattern is allowed. Every other occurrence becomes a violation.",
450
+ "type": "string"
451
+ },
452
+ "pattern": {
453
+ "description": "Pattern whose appearance is allowed only at one location, scanned per line. A literal substring by default (e.g. `process.env.API_KEY`). Prefix with `re:` to match a regular expression instead. Regexes must use the common JS/RE2 subset — no lookaround or backreferences — so the TS and Rust engines agree. Note: a literal pattern that itself begins with `re:` cannot be expressed; such a value is always read as a regex.",
454
+ "type": "string"
455
+ },
456
+ "severity": {
457
+ "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)",
458
+ "enum": [
459
+ "error",
460
+ "warn",
461
+ null
462
+ ],
463
+ "type": "string"
464
+ }
465
+ },
466
+ "required": [
467
+ "pattern",
468
+ "only",
469
+ "message"
470
+ ],
471
+ "type": "object"
472
+ },
473
+ {
474
+ "additionalProperties": false,
475
+ "properties": {
476
+ "in": {
477
+ "anyOf": [
478
+ {
479
+ "type": "string"
480
+ },
481
+ {
482
+ "items": {
483
+ "type": "string"
484
+ },
485
+ "type": "array"
486
+ }
487
+ ],
488
+ "description": "Either a single layer name / glob pattern or an array of them. Layer names defined in `arch.layers` are resolved to their glob lists."
489
+ },
490
+ "jsx-element": {
491
+ "anyOf": [
492
+ {
493
+ "type": "string"
494
+ },
495
+ {
496
+ "items": {
497
+ "type": "string"
498
+ },
499
+ "type": "array"
500
+ }
501
+ ],
502
+ "description": "JSX intrinsic element name(s) allowed only inside the `only` file. AST-matched on tag names."
503
+ },
504
+ "message": {
505
+ "type": "string"
506
+ },
507
+ "only": {
508
+ "description": "The single file path where the element is allowed (typically the design-system primitive that wraps it).",
509
+ "type": "string"
510
+ },
511
+ "severity": {
512
+ "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)",
513
+ "enum": [
514
+ "error",
515
+ "warn",
516
+ null
517
+ ],
518
+ "type": "string"
519
+ }
520
+ },
521
+ "required": [
522
+ "jsx-element",
523
+ "only",
524
+ "message"
525
+ ],
526
+ "type": "object"
527
+ }
528
+ ],
529
+ "description": "A singleton-location rule. Pins a pattern or JSX element to exactly one file — the only honest way to enforce a module of record or a design-system primitive.",
530
+ "examples": [
531
+ "arch:\n singleton:\n - pattern: \"process.env.API_KEY\"\n only: \"src/lib/auth.ts\"\n message: \"API key access must funnel through the auth module.\"",
532
+ "arch:\n singleton:\n - jsx-element: \"button\"\n only: \"src/components/ui/button.tsx\"\n in: [\"src/**/*.tsx\"]\n message: \"Raw <button> belongs only to the Button primitive.\""
533
+ ]
534
+ },
535
+ "type": "array"
536
+ }
537
+ },
538
+ "type": "object"
539
+ },
540
+ "include": {
541
+ "description": "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/**\"]",
542
+ "items": {
543
+ "type": "string"
544
+ },
545
+ "type": "array"
546
+ },
547
+ "plugins": {
548
+ "description": "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\".",
549
+ "items": {
550
+ "enum": [
551
+ "sonar"
552
+ ],
553
+ "type": "string"
554
+ },
555
+ "type": "array"
556
+ },
557
+ "root": {
558
+ "description": "Root directory used to resolve include paths and report relative file names. Defaults to the directory containing klint.config.json.",
559
+ "type": "string"
560
+ },
561
+ "rules": {
562
+ "additionalProperties": {
563
+ "anyOf": [
564
+ {
565
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
566
+ "enum": [
567
+ "error",
568
+ "warn",
569
+ "off"
570
+ ],
571
+ "type": "string"
572
+ },
573
+ {
574
+ "additionalProperties": false,
575
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
576
+ "properties": {
577
+ "include": {
578
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
579
+ "items": {
580
+ "type": "string"
581
+ },
582
+ "type": "array"
583
+ },
584
+ "severity": {
585
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
586
+ "enum": [
587
+ "error",
588
+ "warn",
589
+ "off",
590
+ null
591
+ ],
592
+ "type": "string"
593
+ }
594
+ },
595
+ "type": "object"
596
+ }
597
+ ]
598
+ },
599
+ "description": "Map of rule name → severity or options. Example: { \"no-floating-promise\": \"error\", \"no-sync-in-async\": { \"severity\": \"warn\", \"include\": [\"src/hooks/**\"] } }.",
600
+ "properties": {
601
+ "no-async-predicate": {
441
602
  "anyOf": [
442
603
  {
443
- "type": "string",
604
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
444
605
  "enum": [
445
606
  "error",
446
607
  "warn",
447
608
  "off"
448
609
  ],
449
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
610
+ "type": "string"
450
611
  },
451
612
  {
452
- "type": "object",
613
+ "additionalProperties": false,
614
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
453
615
  "properties": {
616
+ "include": {
617
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
618
+ "items": {
619
+ "type": "string"
620
+ },
621
+ "type": "array"
622
+ },
454
623
  "severity": {
455
- "type": "string",
624
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
456
625
  "enum": [
457
626
  "error",
458
627
  "warn",
459
- "off"
628
+ "off",
629
+ null
460
630
  ],
461
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
462
- },
463
- "include": {
464
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
465
- "type": "array",
466
- "items": {
467
- "type": "string"
468
- }
631
+ "type": "string"
469
632
  }
470
633
  },
471
- "additionalProperties": false,
472
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
634
+ "type": "object"
473
635
  }
636
+ ],
637
+ "description": "Flags `async` functions passed to `.filter`/`.some`/`.every`/`.find`/`.findIndex`. Array methods coerce the returned promise to truthy, so every element appears to match.",
638
+ "examples": [
639
+ "no-async-predicate: error"
474
640
  ]
475
641
  },
476
- "sonar/prefer-string-replaceall": {
477
- "description": "Flags `.replace(/x/g, y)` on strings — `.replaceAll('x', y)` is clearer and faster for all-occurrence replacements.",
478
- "examples": [
479
- "sonar/prefer-string-replaceall: error"
480
- ],
642
+ "no-consecutive-array-push": {
481
643
  "anyOf": [
482
644
  {
483
- "type": "string",
645
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
484
646
  "enum": [
485
647
  "error",
486
648
  "warn",
487
649
  "off"
488
650
  ],
489
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
651
+ "type": "string"
490
652
  },
491
653
  {
492
- "type": "object",
654
+ "additionalProperties": false,
655
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
493
656
  "properties": {
494
- "severity": {
495
- "type": "string",
496
- "enum": [
497
- "error",
498
- "warn",
499
- "off"
500
- ],
501
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
502
- },
503
657
  "include": {
504
658
  "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
505
- "type": "array",
506
659
  "items": {
507
660
  "type": "string"
508
- }
661
+ },
662
+ "type": "array"
663
+ },
664
+ "severity": {
665
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
666
+ "enum": [
667
+ "error",
668
+ "warn",
669
+ "off",
670
+ null
671
+ ],
672
+ "type": "string"
509
673
  }
510
674
  },
511
- "additionalProperties": false,
512
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
675
+ "type": "object"
513
676
  }
677
+ ],
678
+ "description": "Flags consecutive `arr.push(x); arr.push(y);` calls — combine into `arr.push(x, y)` or `arr.push(...items)`.",
679
+ "examples": [
680
+ "no-consecutive-array-push: warn"
514
681
  ]
515
682
  },
516
- "sonar/prefer-string-raw-regexp": {
517
- "description": "Flags regex literals built from escape-heavy string concatenation — `String.raw` drops the double backslashes.",
518
- "examples": [
519
- "sonar/prefer-string-raw-regexp: warn"
520
- ],
683
+ "no-nested-template-literals": {
521
684
  "anyOf": [
522
685
  {
523
- "type": "string",
686
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
524
687
  "enum": [
525
688
  "error",
526
689
  "warn",
527
690
  "off"
528
691
  ],
529
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
692
+ "type": "string"
530
693
  },
531
694
  {
532
- "type": "object",
695
+ "additionalProperties": false,
696
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
533
697
  "properties": {
698
+ "include": {
699
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
700
+ "items": {
701
+ "type": "string"
702
+ },
703
+ "type": "array"
704
+ },
534
705
  "severity": {
535
- "type": "string",
706
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
536
707
  "enum": [
537
708
  "error",
538
709
  "warn",
539
- "off"
710
+ "off",
711
+ null
540
712
  ],
541
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
542
- },
543
- "include": {
544
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
545
- "type": "array",
546
- "items": {
547
- "type": "string"
548
- }
713
+ "type": "string"
549
714
  }
550
715
  },
551
- "additionalProperties": false,
552
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
716
+ "type": "object"
553
717
  }
718
+ ],
719
+ "description": "Flags template literals nested inside template literals — almost always unreadable; extract to a variable.",
720
+ "examples": [
721
+ "no-nested-template-literals: warn"
554
722
  ]
555
723
  },
556
- "sonar/prefer-string-raw": {
557
- "description": "Flags string literals with multiple backslash escapes — `String.raw` is more readable (Windows paths, regex source, etc.).",
558
- "examples": [
559
- "sonar/prefer-string-raw: warn"
560
- ],
724
+ "no-string-match": {
561
725
  "anyOf": [
562
726
  {
563
- "type": "string",
727
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
564
728
  "enum": [
565
729
  "error",
566
730
  "warn",
567
731
  "off"
568
732
  ],
569
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
733
+ "type": "string"
570
734
  },
571
735
  {
572
- "type": "object",
736
+ "additionalProperties": false,
737
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
573
738
  "properties": {
739
+ "include": {
740
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
741
+ "items": {
742
+ "type": "string"
743
+ },
744
+ "type": "array"
745
+ },
574
746
  "severity": {
575
- "type": "string",
747
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
576
748
  "enum": [
577
749
  "error",
578
750
  "warn",
579
- "off"
751
+ "off",
752
+ null
580
753
  ],
581
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
582
- },
583
- "include": {
584
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
585
- "type": "array",
586
- "items": {
587
- "type": "string"
588
- }
754
+ "type": "string"
589
755
  }
590
756
  },
591
- "additionalProperties": false,
592
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
757
+ "type": "object"
593
758
  }
759
+ ],
760
+ "description": "Flags `String#match` — prefer `.matchAll()` for iteration or `.test()` for boolean checks. `.match()` returns different shapes depending on the `/g` flag.",
761
+ "examples": [
762
+ "no-string-match: error"
594
763
  ]
595
764
  },
596
- "sonar/prefer-nullish-coalescing-assign": {
597
- "description": "Flags explicit nullish assignment guards — `x ??= y` is the dedicated nullish assignment operator.",
598
- "examples": [
599
- "sonar/prefer-nullish-coalescing-assign: warn"
600
- ],
765
+ "no-sync-in-async": {
601
766
  "anyOf": [
602
767
  {
603
- "type": "string",
768
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
604
769
  "enum": [
605
770
  "error",
606
771
  "warn",
607
772
  "off"
608
773
  ],
609
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
774
+ "type": "string"
610
775
  },
611
776
  {
612
- "type": "object",
777
+ "additionalProperties": false,
778
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
613
779
  "properties": {
780
+ "include": {
781
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
782
+ "items": {
783
+ "type": "string"
784
+ },
785
+ "type": "array"
786
+ },
614
787
  "severity": {
615
- "type": "string",
788
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
616
789
  "enum": [
617
790
  "error",
618
791
  "warn",
619
- "off"
792
+ "off",
793
+ null
620
794
  ],
621
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
622
- },
623
- "include": {
624
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
625
- "type": "array",
626
- "items": {
627
- "type": "string"
628
- }
795
+ "type": "string"
629
796
  }
630
797
  },
631
- "additionalProperties": false,
632
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
798
+ "type": "object"
633
799
  }
800
+ ],
801
+ "description": "Flags synchronous I/O (`readFileSync`, `execSync`, etc.) inside async functions — blocks the event loop when an async equivalent is available.",
802
+ "examples": [
803
+ "no-sync-in-async: error",
804
+ "no-sync-in-async:\n severity: warn\n include: [\"src/server/**\"]"
634
805
  ]
635
806
  },
636
- "sonar/no-single-char-class": {
637
- "description": "Flags single-character character classes in regex (e.g. `/[a]/`) — drop the brackets; the regex matches the same.",
638
- "examples": [
639
- "sonar/no-single-char-class: warn"
640
- ],
807
+ "no-unguarded-json-parse": {
641
808
  "anyOf": [
642
809
  {
643
- "type": "string",
810
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
644
811
  "enum": [
645
812
  "error",
646
813
  "warn",
647
814
  "off"
648
815
  ],
649
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
816
+ "type": "string"
650
817
  },
651
818
  {
652
- "type": "object",
819
+ "additionalProperties": false,
820
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
653
821
  "properties": {
822
+ "include": {
823
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
824
+ "items": {
825
+ "type": "string"
826
+ },
827
+ "type": "array"
828
+ },
654
829
  "severity": {
655
- "type": "string",
830
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
656
831
  "enum": [
657
832
  "error",
658
833
  "warn",
659
- "off"
834
+ "off",
835
+ null
660
836
  ],
661
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
662
- },
663
- "include": {
664
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
665
- "type": "array",
666
- "items": {
667
- "type": "string"
668
- }
837
+ "type": "string"
669
838
  }
670
839
  },
671
- "additionalProperties": false,
672
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
840
+ "type": "object"
673
841
  }
842
+ ],
843
+ "description": "Flags `JSON.parse()` calls that aren't wrapped in try/catch — malformed input crashes the process.",
844
+ "examples": [
845
+ "no-unguarded-json-parse: error"
674
846
  ]
675
847
  },
676
- "sonar/prefer-at": {
677
- "description": "Flags negative-index access via `arr[arr.length - 1]` — `.at(-1)` reads cleaner for last-element access.",
678
- "examples": [
679
- "sonar/prefer-at: warn"
680
- ],
848
+ "sonar/no-single-char-class": {
681
849
  "anyOf": [
682
850
  {
683
- "type": "string",
851
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
684
852
  "enum": [
685
853
  "error",
686
854
  "warn",
687
855
  "off"
688
856
  ],
689
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
857
+ "type": "string"
690
858
  },
691
859
  {
692
- "type": "object",
693
- "properties": {
694
- "severity": {
695
- "type": "string",
696
- "enum": [
697
- "error",
698
- "warn",
699
- "off"
700
- ],
701
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
702
- },
703
- "include": {
704
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
705
- "type": "array",
706
- "items": {
707
- "type": "string"
708
- }
709
- }
710
- },
711
860
  "additionalProperties": false,
712
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
713
- }
714
- ]
715
- }
716
- },
717
- "additionalProperties": {
718
- "anyOf": [
719
- {
720
- "type": "string",
721
- "enum": [
722
- "error",
723
- "warn",
724
- "off"
725
- ],
726
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
727
- },
728
- {
729
- "type": "object",
730
- "properties": {
731
- "severity": {
732
- "type": "string",
733
- "enum": [
734
- "error",
735
- "warn",
736
- "off"
737
- ],
738
- "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences."
739
- },
740
- "include": {
741
- "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
742
- "type": "array",
743
- "items": {
744
- "type": "string"
745
- }
746
- }
747
- },
748
- "additionalProperties": false,
749
- "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files."
750
- }
751
- ]
752
- }
753
- },
754
- "arch": {
755
- "type": "object",
756
- "properties": {
757
- "layers": {
758
- "type": "object",
759
- "propertyNames": {
760
- "type": "string"
761
- },
762
- "additionalProperties": {
763
- "type": "array",
764
- "items": {
765
- "type": "string"
766
- }
767
- },
768
- "description": "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.",
769
- "examples": [
770
- "layers:\n core: [\"src/hooks/lib/**\", \"src/tools/**\"]\n skills: [\"assets/skills/**\"]\n dao: [\"src/dao/**\"]"
771
- ]
772
- },
773
- "imports": {
774
- "description": "Import-boundary rules. The dependency graph your README claims you have.",
775
- "type": "array",
776
- "items": {
777
- "type": "object",
778
- "properties": {
779
- "from": {
780
- "anyOf": [
781
- {
782
- "type": "string"
783
- },
784
- {
785
- "type": "array",
786
- "items": {
787
- "type": "string"
788
- }
789
- }
790
- ],
791
- "description": "Source — a layer name or glob(s) for the files this rule applies to. Imports originating in `from` are checked against `deny`/`allow`."
792
- },
793
- "deny": {
794
- "description": "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).",
795
- "anyOf": [
796
- {
797
- "type": "string"
798
- },
799
- {
800
- "type": "array",
801
- "items": {
802
- "type": "string"
803
- }
804
- }
805
- ]
806
- },
807
- "allow": {
808
- "description": "Allowlist of layer name(s) / glob(s) the source may import from. Anything not matching `allow` is denied.",
809
- "anyOf": [
810
- {
811
- "type": "string"
812
- },
813
- {
814
- "type": "array",
815
- "items": {
816
- "type": "string"
817
- }
818
- }
819
- ]
820
- },
821
- "deny-packages": {
822
- "description": "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.",
823
- "anyOf": [
824
- {
825
- "type": "string"
826
- },
827
- {
828
- "type": "array",
829
- "items": {
830
- "type": "string"
831
- }
832
- }
833
- ]
834
- },
835
- "type-only": {
836
- "description": "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.",
837
- "type": "string",
838
- "const": "allow"
839
- },
840
- "message": {
841
- "description": "Custom error message reported when the rule is violated. Shown in the agent's event stream alongside file:line.",
842
- "type": "string"
843
- },
844
- "severity": {
845
- "type": "string",
846
- "enum": [
847
- "error",
848
- "warn"
849
- ],
850
- "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)"
851
- }
852
- },
853
- "required": [
854
- "from"
855
- ],
856
- "additionalProperties": false,
857
- "description": "An import-boundary rule. Restricts which layers a source can or cannot import from.",
858
- "examples": [
859
- "arch:\n imports:\n - from: skills\n deny: core\n message: \"Skills must be self-contained and portable\"\n severity: warn",
860
- "arch:\n imports:\n - from: [\"src/dao/**\"]\n allow: [\"src/dao/**\", \"src/prisma/**\", \"src/types/**\"]"
861
- ]
862
- }
863
- },
864
- "forbidden": {
865
- "description": "Forbidden-pattern rules. Block literal strings scoped to a layer.",
866
- "type": "array",
867
- "items": {
868
- "anyOf": [
869
- {
870
- "type": "object",
871
- "properties": {
872
- "pattern": {
873
- "type": "string",
874
- "description": "Pattern to search for, scanned per line. A literal substring by default (e.g. a console logging call). Prefix with `re:` to match a regular expression instead (e.g. `re:\\bp-\\[\\d+px\\]`). Regexes must use the common JS/RE2 subset — no lookaround or backreferences — so the TS and Rust engines agree. Note: a literal pattern that itself begins with `re:` cannot be expressed; such a value is always read as a regex."
875
- },
876
- "in": {
877
- "anyOf": [
878
- {
879
- "type": "string"
880
- },
881
- {
882
- "type": "array",
883
- "items": {
884
- "type": "string"
885
- }
886
- }
887
- ],
888
- "description": "Layer name(s) or glob(s) the pattern is forbidden in. Files outside the scope are not checked."
889
- },
890
- "message": {
891
- "type": "string",
892
- "description": "Required error message explaining why the pattern is forbidden. Shown to the agent so it can fix the call site."
893
- },
894
- "severity": {
895
- "type": "string",
896
- "enum": [
897
- "error",
898
- "warn"
899
- ],
900
- "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)"
901
- }
902
- },
903
- "required": [
904
- "pattern",
905
- "in",
906
- "message"
907
- ],
908
- "additionalProperties": false
909
- },
910
- {
911
- "type": "object",
912
- "properties": {
913
- "jsx-element": {
914
- "anyOf": [
915
- {
916
- "type": "string"
917
- },
918
- {
919
- "type": "array",
920
- "items": {
921
- "type": "string"
922
- }
923
- }
924
- ],
925
- "description": "JSX intrinsic element name(s) to forbid (e.g. `button`, `input`, `label`). AST-matched on opening and self-closing tag names — robust to whitespace, attributes, and naming collisions like `<buttonGroup>`."
926
- },
927
- "in": {
928
- "anyOf": [
929
- {
930
- "type": "string"
931
- },
932
- {
933
- "type": "array",
934
- "items": {
935
- "type": "string"
936
- }
937
- }
938
- ],
939
- "description": "Layer name(s) or glob(s) the element is forbidden in. Files outside the scope are not checked."
940
- },
941
- "message": {
942
- "type": "string",
943
- "description": "Required error message explaining why the raw element is forbidden. Typically points at the design-system replacement (e.g. `Use <Button /> from @/components/ui/button`)."
944
- },
945
- "severity": {
946
- "type": "string",
947
- "enum": [
948
- "error",
949
- "warn"
950
- ],
951
- "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)"
952
- }
953
- },
954
- "required": [
955
- "jsx-element",
956
- "in",
957
- "message"
958
- ],
959
- "additionalProperties": false
960
- }
961
- ],
962
- "description": "A forbidden-pattern rule. Blocks either a literal substring (`pattern:`) or a JSX element (`jsx-element:`) inside specific layers.",
963
- "examples": [
964
- "arch:\n forbidden:\n - pattern: \"debugLog(\"\n in: core\n message: \"Leaks into the agent event stream\"",
965
- "arch:\n forbidden:\n - jsx-element: [\"button\", \"input\", \"label\"]\n in: [\"src/app/**/*.tsx\", \"src/components/**/*.tsx\", \"!src/components/ui/**\"]\n message: \"Use the design-system primitives in @/components/ui/* instead of raw HTML elements.\""
966
- ]
967
- }
968
- },
969
- "singleton": {
970
- "description": "Singleton-location rules. Pin a pattern to one file; every other touch is a violation.",
971
- "type": "array",
972
- "items": {
973
- "anyOf": [
974
- {
975
- "type": "object",
976
- "properties": {
977
- "pattern": {
978
- "type": "string",
979
- "description": "Pattern whose appearance is allowed only at one location, scanned per line. A literal substring by default (e.g. `process.env.API_KEY`). Prefix with `re:` to match a regular expression instead. Regexes must use the common JS/RE2 subset — no lookaround or backreferences — so the TS and Rust engines agree. Note: a literal pattern that itself begins with `re:` cannot be expressed; such a value is always read as a regex."
980
- },
981
- "only": {
982
- "type": "string",
983
- "description": "The single file path where this pattern is allowed. Every other occurrence becomes a violation."
984
- },
985
- "in": {
986
- "description": "Optional scope. Limit the scan to these layers/globs; defaults to all files in `include`.",
987
- "anyOf": [
988
- {
989
- "type": "string"
990
- },
991
- {
992
- "type": "array",
993
- "items": {
994
- "type": "string"
995
- }
996
- }
997
- ]
998
- },
999
- "message": {
1000
- "type": "string"
1001
- },
1002
- "severity": {
1003
- "type": "string",
1004
- "enum": [
1005
- "error",
1006
- "warn"
1007
- ],
1008
- "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)"
1009
- }
1010
- },
1011
- "required": [
1012
- "pattern",
1013
- "only",
1014
- "message"
1015
- ],
1016
- "additionalProperties": false
1017
- },
1018
- {
1019
- "type": "object",
1020
- "properties": {
1021
- "jsx-element": {
1022
- "anyOf": [
1023
- {
1024
- "type": "string"
1025
- },
1026
- {
1027
- "type": "array",
1028
- "items": {
1029
- "type": "string"
1030
- }
1031
- }
1032
- ],
1033
- "description": "JSX intrinsic element name(s) allowed only inside the `only` file. AST-matched on tag names."
1034
- },
1035
- "only": {
1036
- "type": "string",
1037
- "description": "The single file path where the element is allowed (typically the design-system primitive that wraps it)."
1038
- },
1039
- "in": {
1040
- "anyOf": [
1041
- {
1042
- "type": "string"
1043
- },
1044
- {
1045
- "type": "array",
1046
- "items": {
1047
- "type": "string"
1048
- }
1049
- }
1050
- ],
1051
- "description": "Either a single layer name / glob pattern or an array of them. Layer names defined in `arch.layers` are resolved to their glob lists."
1052
- },
1053
- "message": {
1054
- "type": "string"
1055
- },
1056
- "severity": {
1057
- "type": "string",
1058
- "enum": [
1059
- "error",
1060
- "warn"
1061
- ],
1062
- "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)"
1063
- }
1064
- },
1065
- "required": [
1066
- "jsx-element",
1067
- "only",
1068
- "message"
1069
- ],
1070
- "additionalProperties": false
1071
- }
1072
- ],
1073
- "description": "A singleton-location rule. Pins a pattern or JSX element to exactly one file — the only honest way to enforce a module of record or a design-system primitive.",
1074
- "examples": [
1075
- "arch:\n singleton:\n - pattern: \"process.env.API_KEY\"\n only: \"src/lib/auth.ts\"\n message: \"API key access must funnel through the auth module.\"",
1076
- "arch:\n singleton:\n - jsx-element: \"button\"\n only: \"src/components/ui/button.tsx\"\n in: [\"src/**/*.tsx\"]\n message: \"Raw <button> belongs only to the Button primitive.\""
1077
- ]
1078
- }
1079
- },
1080
- "maxLines": {
1081
- "description": "Per-file line-count limits. Cap how long files in a layer may grow, with different ceilings per scope.",
1082
- "type": "array",
1083
- "items": {
1084
- "type": "object",
1085
- "properties": {
1086
- "limit": {
1087
- "type": "integer",
1088
- "exclusiveMinimum": 0,
1089
- "maximum": 9007199254740991,
1090
- "description": "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."
1091
- },
1092
- "in": {
1093
- "anyOf": [
1094
- {
1095
- "type": "string"
1096
- },
1097
- {
1098
- "type": "array",
1099
- "items": {
1100
- "type": "string"
1101
- }
1102
- }
1103
- ],
1104
- "description": "Layer name(s) or glob(s) the limit applies to. Files outside the scope are not checked."
1105
- },
1106
- "message": {
1107
- "description": "Optional error message. Defaults to a message naming the limit (e.g. 'File exceeds the maximum of 300 lines').",
1108
- "type": "string"
1109
- },
1110
- "severity": {
1111
- "type": "string",
1112
- "enum": [
1113
- "error",
1114
- "warn"
1115
- ],
1116
- "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)"
1117
- }
1118
- },
1119
- "required": [
1120
- "limit",
1121
- "in"
1122
- ],
1123
- "additionalProperties": false,
1124
- "description": "A per-file line-count limit. Caps how long any file in scope may grow — the honest way to keep modules small without a formatter dependency.",
1125
- "examples": [
1126
- "arch:\n maxLines:\n - limit: 300\n in: src/**\n message: \"Split this module\"",
1127
- "arch:\n maxLines:\n - limit: 300\n in: src/**\n - limit: 600\n in: tests/**"
1128
- ]
1129
- }
1130
- },
1131
- "maxCommentDensity": {
1132
- "description": "Per-file comment-density ceilings. Cap the share of a file that may be explanatory comments; doc-comments are exempt by default.",
1133
- "type": "array",
1134
- "items": {
1135
- "type": "object",
1136
- "properties": {
1137
- "limit": {
1138
- "type": "number",
1139
- "exclusiveMinimum": 0,
1140
- "maximum": 100,
1141
- "description": "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."
1142
- },
1143
- "countDocComments": {
1144
- "description": "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.",
1145
- "type": "boolean"
1146
- },
1147
- "ignore": {
1148
- "description": "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.",
1149
- "anyOf": [
1150
- {
861
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
862
+ "properties": {
863
+ "include": {
864
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
865
+ "items": {
1151
866
  "type": "string"
1152
867
  },
1153
- {
1154
- "type": "array",
1155
- "items": {
1156
- "type": "string"
1157
- }
1158
- }
1159
- ]
868
+ "type": "array"
869
+ },
870
+ "severity": {
871
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
872
+ "enum": [
873
+ "error",
874
+ "warn",
875
+ "off",
876
+ null
877
+ ],
878
+ "type": "string"
879
+ }
1160
880
  },
1161
- "in": {
1162
- "anyOf": [
1163
- {
881
+ "type": "object"
882
+ }
883
+ ],
884
+ "description": "Flags single-character character classes in regex (e.g. `/[a]/`) — drop the brackets; the regex matches the same.",
885
+ "examples": [
886
+ "sonar/no-single-char-class: warn"
887
+ ]
888
+ },
889
+ "sonar/prefer-at": {
890
+ "anyOf": [
891
+ {
892
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
893
+ "enum": [
894
+ "error",
895
+ "warn",
896
+ "off"
897
+ ],
898
+ "type": "string"
899
+ },
900
+ {
901
+ "additionalProperties": false,
902
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
903
+ "properties": {
904
+ "include": {
905
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
906
+ "items": {
1164
907
  "type": "string"
1165
908
  },
1166
- {
1167
- "type": "array",
1168
- "items": {
1169
- "type": "string"
1170
- }
1171
- }
1172
- ],
1173
- "description": "Layer name(s) or glob(s) the limit applies to. Files outside the scope are not checked."
1174
- },
1175
- "message": {
1176
- "description": "Optional error message. Defaults to a message naming the measured density and the limit.",
1177
- "type": "string"
909
+ "type": "array"
910
+ },
911
+ "severity": {
912
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
913
+ "enum": [
914
+ "error",
915
+ "warn",
916
+ "off",
917
+ null
918
+ ],
919
+ "type": "string"
920
+ }
1178
921
  },
1179
- "severity": {
1180
- "type": "string",
1181
- "enum": [
1182
- "error",
1183
- "warn"
1184
- ],
1185
- "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)"
1186
- }
1187
- },
1188
- "required": [
1189
- "limit",
1190
- "in"
1191
- ],
1192
- "additionalProperties": false,
1193
- "description": "A per-file comment-density ceiling. Caps how much of a file may be explanatory comments — the honest way to push intent into code and names rather than prose (and to keep token cost down), while leaving doc-comments free by default.",
1194
- "examples": [
1195
- "arch:\n maxCommentDensity:\n - limit: 5\n in: src/**\n message: \"Encode intent in names, not comments\"",
1196
- "arch:\n maxCommentDensity:\n - limit: 10\n countDocComments: true\n in: src/**"
1197
- ]
1198
- }
922
+ "type": "object"
923
+ }
924
+ ],
925
+ "description": "Flags negative-index access via `arr[arr.length - 1]` — `.at(-1)` reads cleaner for last-element access.",
926
+ "examples": [
927
+ "sonar/prefer-at: warn"
928
+ ]
1199
929
  },
1200
- "maxCommentBlock": {
1201
- "description": "Per-block comment-height caps. Limit how many comment lines may stack together; doc-comments are exempt by default.",
1202
- "type": "array",
1203
- "items": {
1204
- "type": "object",
1205
- "properties": {
1206
- "limit": {
1207
- "type": "integer",
1208
- "exclusiveMinimum": 0,
1209
- "maximum": 9007199254740991,
1210
- "description": "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."
1211
- },
1212
- "countDocComments": {
1213
- "description": "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.",
1214
- "type": "boolean"
1215
- },
1216
- "ignore": {
1217
- "description": "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.",
1218
- "anyOf": [
1219
- {
930
+ "sonar/prefer-nullish-coalescing-assign": {
931
+ "anyOf": [
932
+ {
933
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
934
+ "enum": [
935
+ "error",
936
+ "warn",
937
+ "off"
938
+ ],
939
+ "type": "string"
940
+ },
941
+ {
942
+ "additionalProperties": false,
943
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
944
+ "properties": {
945
+ "include": {
946
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
947
+ "items": {
1220
948
  "type": "string"
1221
949
  },
1222
- {
1223
- "type": "array",
1224
- "items": {
1225
- "type": "string"
1226
- }
1227
- }
1228
- ]
950
+ "type": "array"
951
+ },
952
+ "severity": {
953
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
954
+ "enum": [
955
+ "error",
956
+ "warn",
957
+ "off",
958
+ null
959
+ ],
960
+ "type": "string"
961
+ }
1229
962
  },
1230
- "in": {
1231
- "anyOf": [
1232
- {
963
+ "type": "object"
964
+ }
965
+ ],
966
+ "description": "Flags explicit nullish assignment guards — `x ??= y` is the dedicated nullish assignment operator.",
967
+ "examples": [
968
+ "sonar/prefer-nullish-coalescing-assign: warn"
969
+ ]
970
+ },
971
+ "sonar/prefer-string-raw": {
972
+ "anyOf": [
973
+ {
974
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
975
+ "enum": [
976
+ "error",
977
+ "warn",
978
+ "off"
979
+ ],
980
+ "type": "string"
981
+ },
982
+ {
983
+ "additionalProperties": false,
984
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
985
+ "properties": {
986
+ "include": {
987
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
988
+ "items": {
1233
989
  "type": "string"
1234
990
  },
1235
- {
1236
- "type": "array",
1237
- "items": {
1238
- "type": "string"
1239
- }
1240
- }
1241
- ],
1242
- "description": "Layer name(s) or glob(s) the limit applies to. Files outside the scope are not checked."
991
+ "type": "array"
992
+ },
993
+ "severity": {
994
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
995
+ "enum": [
996
+ "error",
997
+ "warn",
998
+ "off",
999
+ null
1000
+ ],
1001
+ "type": "string"
1002
+ }
1243
1003
  },
1244
- "message": {
1245
- "description": "Optional error message. Defaults to a message naming the consecutive-line limit.",
1246
- "type": "string"
1004
+ "type": "object"
1005
+ }
1006
+ ],
1007
+ "description": "Flags string literals with multiple backslash escapes — `String.raw` is more readable (Windows paths, regex source, etc.).",
1008
+ "examples": [
1009
+ "sonar/prefer-string-raw: warn"
1010
+ ]
1011
+ },
1012
+ "sonar/prefer-string-raw-regexp": {
1013
+ "anyOf": [
1014
+ {
1015
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
1016
+ "enum": [
1017
+ "error",
1018
+ "warn",
1019
+ "off"
1020
+ ],
1021
+ "type": "string"
1022
+ },
1023
+ {
1024
+ "additionalProperties": false,
1025
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
1026
+ "properties": {
1027
+ "include": {
1028
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
1029
+ "items": {
1030
+ "type": "string"
1031
+ },
1032
+ "type": "array"
1033
+ },
1034
+ "severity": {
1035
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
1036
+ "enum": [
1037
+ "error",
1038
+ "warn",
1039
+ "off",
1040
+ null
1041
+ ],
1042
+ "type": "string"
1043
+ }
1247
1044
  },
1248
- "severity": {
1249
- "type": "string",
1250
- "enum": [
1251
- "error",
1252
- "warn"
1253
- ],
1254
- "description": "Severity for the arch rule. \"error\" exits with code 2; \"warn\" reports but exits 0. (arch rules cannot be \"off\" — remove the entry instead.)"
1255
- }
1045
+ "type": "object"
1046
+ }
1047
+ ],
1048
+ "description": "Flags regex literals built from escape-heavy string concatenation — `String.raw` drops the double backslashes.",
1049
+ "examples": [
1050
+ "sonar/prefer-string-raw-regexp: warn"
1051
+ ]
1052
+ },
1053
+ "sonar/prefer-string-replaceall": {
1054
+ "anyOf": [
1055
+ {
1056
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
1057
+ "enum": [
1058
+ "error",
1059
+ "warn",
1060
+ "off"
1061
+ ],
1062
+ "type": "string"
1256
1063
  },
1257
- "required": [
1258
- "limit",
1259
- "in"
1260
- ],
1261
- "additionalProperties": false,
1262
- "description": "A per-block comment-height cap. Limits how many comment lines may stack together, so comments stay terse and a long explanation becomes a well-named function instead. Doc-comments are exempt by default.",
1263
- "examples": [
1264
- "arch:\n maxCommentBlock:\n - limit: 2\n in: src/**\n message: \"Extract a named function instead of a comment block\""
1265
- ]
1266
- }
1064
+ {
1065
+ "additionalProperties": false,
1066
+ "description": "Rule options object. Omit severity to default to \"error\". Add include to scope the rule to specific files.",
1067
+ "properties": {
1068
+ "include": {
1069
+ "description": "Glob patterns scoping this rule to a subset of files. Prefix with ! to exclude. Example: [\"src/hooks/**\", \"!src/hooks/scripts/**\"]",
1070
+ "items": {
1071
+ "type": "string"
1072
+ },
1073
+ "type": "array"
1074
+ },
1075
+ "severity": {
1076
+ "description": "Rule severity. \"error\" exits with code 2; \"warn\" reports but exits 0; \"off\" silences.",
1077
+ "enum": [
1078
+ "error",
1079
+ "warn",
1080
+ "off",
1081
+ null
1082
+ ],
1083
+ "type": "string"
1084
+ }
1085
+ },
1086
+ "type": "object"
1087
+ }
1088
+ ],
1089
+ "description": "Flags `.replace(/x/g, y)` on strings — `.replaceAll('x', y)` is clearer and faster for all-occurrence replacements.",
1090
+ "examples": [
1091
+ "sonar/prefer-string-replaceall: error"
1092
+ ]
1267
1093
  }
1268
1094
  },
1269
- "additionalProperties": false,
1270
- "description": "Architecture-as-Code constraints — layers, import boundaries, forbidden patterns, singleton locations, comment budgets. Enforced by the arch engine alongside the rule set."
1095
+ "type": "object"
1271
1096
  }
1272
1097
  },
1273
- "additionalProperties": false,
1274
- "description": "klint configuration. Lives at klint.yaml (or klint.config.json) next to biome.json and knip.json."
1098
+ "type": "object"
1275
1099
  }