@konvert7/klint 0.37.1 → 1.0.0
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/README.md +49 -166
- package/bin/klint.js +47 -0
- package/klint.schema.json +1 -1
- package/klint.schema.yaml +1 -1
- package/package.json +15 -25
- package/cli/command.ts +0 -189
- package/cli/commands.ts +0 -90
- package/cli/help.ts +0 -21
- package/cli/install-skill.ts +0 -260
- package/cli/output.ts +0 -178
- package/cli/paths.ts +0 -20
- package/cli/rust-config.ts +0 -145
- package/cli/rust-engine.ts +0 -323
- package/cli/stats.ts +0 -92
- package/cli.ts +0 -214
- package/core/arch-comments.ts +0 -125
- package/core/arch.ts +0 -470
- package/core/ast.ts +0 -63
- package/core/config.schema.ts +0 -413
- package/core/fixer.ts +0 -47
- package/core/native-binary.ts +0 -123
- package/core/paths.ts +0 -9
- package/core/rule-helpers.ts +0 -76
- package/core/runner.ts +0 -196
- package/core/schema-version.ts +0 -46
- package/core/skill.ts +0 -164
- package/core/stats.ts +0 -160
- package/core/types.ts +0 -146
- package/plugins/index.ts +0 -6
- package/plugins/sonar.ts +0 -27
- package/rules/index.ts +0 -26
- package/rules/no-async-predicate.ts +0 -51
- package/rules/no-consecutive-array-push.ts +0 -61
- package/rules/no-date-equality.ts +0 -35
- package/rules/no-floating-promise.ts +0 -37
- package/rules/no-misused-promises.ts +0 -59
- package/rules/no-nested-template-literals.ts +0 -47
- package/rules/no-object-in-template.ts +0 -95
- package/rules/no-optional-chain-on-non-nullable.ts +0 -47
- package/rules/no-single-char-class.ts +0 -113
- package/rules/no-string-match.ts +0 -63
- package/rules/no-sync-in-async.ts +0 -43
- package/rules/no-unguarded-json-parse.ts +0 -35
- package/rules/prefer-at.ts +0 -46
- package/rules/prefer-nullish-coalescing-assign.ts +0 -111
- package/rules/prefer-string-raw-regexp.ts +0 -93
- package/rules/prefer-string-raw.ts +0 -39
- package/rules/prefer-string-replaceall.ts +0 -67
- package/skill/klint-rules/SKILL.md +0 -264
- package/tools/generate-homebrew-formula.ts +0 -64
- package/tools/prepare-native-release.ts +0 -166
- package/tools/prepare-nuget-release.ts +0 -62
- package/tools/prepare-python-release.ts +0 -76
- package/tools/semantic-release-crates.js +0 -96
- package/tools/semantic-release-native-npm.js +0 -121
- package/tools/semantic-release-root-npm.js +0 -87
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# klint
|
|
2
2
|
|
|
3
|
-
Architecture-as-Code linting for TypeScript, Python, and
|
|
3
|
+
Architecture-as-Code linting for TypeScript, Python, Swift, Rust, and C# projects, in one native binary.
|
|
4
4
|
|
|
5
|
-
Biome, oxlint, Ruff, and SwiftLint are excellent at syntax, style, and fast local correctness. klint handles the rules that need project context: layer boundaries, singleton ownership, forbidden patterns,
|
|
5
|
+
Biome, oxlint, Ruff, and SwiftLint are excellent at syntax, style, and fast local correctness. klint handles the rules that need project context: layer boundaries, singleton ownership, forbidden patterns, comment and file-size budgets, and repository policies that should block an AI agent or CI run before code lands.
|
|
6
6
|
|
|
7
7
|
Use klint when "please follow AGENTS.md" is not strong enough. Put the rule in `klint.yaml`, run `klint`, and make the constraint executable.
|
|
8
8
|
|
|
@@ -28,10 +28,10 @@ With Cargo (installs the `klint-rs` binary):
|
|
|
28
28
|
cargo install klint-rs
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
For TypeScript projects
|
|
31
|
+
For JavaScript and TypeScript projects (npm, pnpm, Yarn or Bun; Node.js 18+). The package runs the native binary for your platform, so no TypeScript or Bun runtime is involved:
|
|
32
32
|
|
|
33
33
|
```sh
|
|
34
|
-
|
|
34
|
+
npm install -D @konvert7/klint
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Add a script:
|
|
@@ -53,7 +53,7 @@ Then create `klint.yaml` in your project root.
|
|
|
53
53
|
include: ["src", "!**/node_modules/**"]
|
|
54
54
|
|
|
55
55
|
rules:
|
|
56
|
-
no-
|
|
56
|
+
no-async-predicate: error
|
|
57
57
|
no-unguarded-json-parse: error
|
|
58
58
|
no-sync-in-async:
|
|
59
59
|
severity: error
|
|
@@ -74,13 +74,13 @@ arch:
|
|
|
74
74
|
Run it:
|
|
75
75
|
|
|
76
76
|
```sh
|
|
77
|
-
|
|
77
|
+
npx klint
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
For machines and hooks:
|
|
81
81
|
|
|
82
82
|
```sh
|
|
83
|
-
|
|
83
|
+
npx klint --json
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
`--json` emits structured violations and exits `2` when errors are present.
|
|
@@ -112,15 +112,14 @@ klint is for rules that are too project-specific or context-heavy for a formatte
|
|
|
112
112
|
| Forbidden patterns | `console.log(` is blocked inside hook libraries |
|
|
113
113
|
| Raw JSX elements | `<button>` is blocked outside `src/components/ui/**` |
|
|
114
114
|
| File length | No file in `src/**` may exceed 300 lines |
|
|
115
|
-
|
|
|
116
|
-
| Repository policy | Custom `klint.rules.ts` rules run with the same severity/config system |
|
|
115
|
+
| Syntax mistakes | An `async` callback passed to `.filter()` always passes |
|
|
117
116
|
|
|
118
117
|
Rules give agents room to move quickly because the unsafe moves are blocked structurally.
|
|
119
118
|
|
|
120
119
|
## CLI
|
|
121
120
|
|
|
122
121
|
```sh
|
|
123
|
-
klint [--config <dir>] [--
|
|
122
|
+
klint [--config <dir>] [--fix] [--json] [--hook]
|
|
124
123
|
klint stats [--config <dir>]
|
|
125
124
|
klint install-skill [--agents <list>] [--symlink | --copy] [--force]
|
|
126
125
|
```
|
|
@@ -128,8 +127,6 @@ klint install-skill [--agents <list>] [--symlink | --copy] [--force]
|
|
|
128
127
|
| Flag | Description |
|
|
129
128
|
|------|-------------|
|
|
130
129
|
| `--config <dir>` | Directory containing `klint.yaml` or `klint.config.json` (default: cwd) |
|
|
131
|
-
| `--rules <file>` | Path to custom rules file (default: `<configDir>/klint.rules.ts` when present) |
|
|
132
|
-
| `--engine <name>` | Engine mode: `ts` (default), `rust`, `compare`, or `auto` |
|
|
133
130
|
| `--fix` | Apply auto-fixes for fixable violations in-place |
|
|
134
131
|
| `--json` | Emit structured JSON to stdout for agents and CI |
|
|
135
132
|
| `--hook` | Count this run as an agent hook run in `klint stats` |
|
|
@@ -142,7 +139,7 @@ klint install-skill
|
|
|
142
139
|
|
|
143
140
|
The skill is embedded in the native engine, so this works from every distribution — the npm package, the PyPI wheel, the NuGet tool, and the standalone binary — not just Node projects.
|
|
144
141
|
|
|
145
|
-
Run on a terminal without `--agents`, `--symlink`, or `--copy`, it asks which agents to install for and how to store the skill. That is true of every distribution
|
|
142
|
+
Run on a terminal without `--agents`, `--symlink`, or `--copy`, it asks which agents to install for and how to store the skill. That is true of every distribution, since they all run the same native binary. Passing any of those flags, or piping stdin (CI, scripts, hooks), skips the questions and uses the defaults.
|
|
146
143
|
|
|
147
144
|
### Shared layout (default)
|
|
148
145
|
|
|
@@ -190,7 +187,7 @@ klint stats (.klint/stats.json)
|
|
|
190
187
|
errors prevented 17 (caught by hook 11, direct 6)
|
|
191
188
|
|
|
192
189
|
by rule reported prevented
|
|
193
|
-
no-
|
|
190
|
+
no-async-predicate 29 9
|
|
194
191
|
arch/imports 20 5
|
|
195
192
|
no-string-match 12 3
|
|
196
193
|
```
|
|
@@ -201,49 +198,23 @@ Errors prevented is stricter: an error counts once, when a run reports it and a
|
|
|
201
198
|
|
|
202
199
|
`.klint/` contains a `.gitignore` with `*`, so git ignores the whole folder and nothing needs adding to your own `.gitignore`. Set `KLINT_NO_STATS=1` to stop recording. Stats are best effort: a read-only checkout never fails a lint run.
|
|
203
200
|
|
|
204
|
-
##
|
|
201
|
+
## Upgrading from klint 0.x
|
|
205
202
|
|
|
206
|
-
klint
|
|
203
|
+
klint 1.0 runs one native engine on every platform. The TypeScript engine is gone, and with it:
|
|
207
204
|
|
|
208
|
-
|
|
205
|
+
- **`--engine`, `--rules` and `--debug`.** Remove them from scripts and hooks.
|
|
206
|
+
- **Custom rules in `klint.rules.ts`.** The file is ignored, and klint warns with `klint/unsupported-rule` so you notice. Express the policy as an `arch` rule (`forbidden`, `singleton`, `imports`) where you can.
|
|
207
|
+
- **The rules that needed TypeScript's type checker.** They are ignored with a `klint/unsupported-rule` warning that names the replacement, when one exists. Turn them off in `klint.yaml` to silence it.
|
|
209
208
|
|
|
210
|
-
|
|
|
211
|
-
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
209
|
+
| Removed rule | Replacement |
|
|
210
|
+
|--------------|-------------|
|
|
211
|
+
| `no-floating-promise` | Biome `nursery/noFloatingPromises` |
|
|
212
|
+
| `no-misused-promises` | Biome `nursery/noMisusedPromises` |
|
|
213
|
+
| `no-object-in-template` | Biome `nursery/noBaseToString` |
|
|
214
|
+
| `no-date-equality` | none yet |
|
|
215
|
+
| `no-optional-chain-on-non-nullable` | none yet |
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
```sh
|
|
220
|
-
klint --engine auto
|
|
221
|
-
klint --engine rust --json # strict Rust-only run; rejects unsupported rules
|
|
222
|
-
klint --engine compare --json # parity check; rejects unsupported rules
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
Rust currently supports:
|
|
226
|
-
|
|
227
|
-
- `arch` rules: imports, forbidden patterns (literal + `re:` regex), singleton locations, `maxLines` file limits, comment budgets (`maxCommentDensity`, `maxCommentBlock`)
|
|
228
|
-
- `no-unguarded-json-parse`
|
|
229
|
-
- `no-sync-in-async`
|
|
230
|
-
- `no-nested-template-literals`
|
|
231
|
-
- `no-consecutive-array-push`
|
|
232
|
-
- `no-string-match`
|
|
233
|
-
- `sonar/no-single-char-class`
|
|
234
|
-
- `sonar/prefer-at`
|
|
235
|
-
- `sonar/prefer-nullish-coalescing-assign`
|
|
236
|
-
- `sonar/prefer-string-replaceall`
|
|
237
|
-
- `sonar/prefer-string-raw`
|
|
238
|
-
- `sonar/prefer-string-raw-regexp`
|
|
239
|
-
|
|
240
|
-
These rules remain TypeScript-owned because they need TypeScript semantic information:
|
|
241
|
-
|
|
242
|
-
- `no-floating-promise`
|
|
243
|
-
- `no-misused-promises`
|
|
244
|
-
- `no-date-equality`
|
|
245
|
-
- `no-optional-chain-on-non-nullable`
|
|
246
|
-
- `no-object-in-template`
|
|
217
|
+
The npm package needs Node.js 18+ and no longer installs `typescript`, `zod`, `yaml` or `@clack/prompts`.
|
|
247
218
|
|
|
248
219
|
## Configuration
|
|
249
220
|
|
|
@@ -257,10 +228,9 @@ plugins: [sonar]
|
|
|
257
228
|
|
|
258
229
|
rules:
|
|
259
230
|
no-unguarded-json-parse: error
|
|
260
|
-
no-
|
|
261
|
-
no-misused-promises: error
|
|
231
|
+
no-async-predicate: error
|
|
262
232
|
sonar/prefer-string-replaceall: warn
|
|
263
|
-
|
|
233
|
+
no-sync-in-async:
|
|
264
234
|
severity: error
|
|
265
235
|
include: ["src/server/**"]
|
|
266
236
|
|
|
@@ -432,7 +402,7 @@ arch:
|
|
|
432
402
|
message: "Libraries should return or throw, not terminate the process"
|
|
433
403
|
```
|
|
434
404
|
|
|
435
|
-
Match a **regular expression** instead of a literal by prefixing the pattern with `re:` — useful for value-shaped policies such as off-system utility classes. Regexes use the
|
|
405
|
+
Match a **regular expression** instead of a literal by prefixing the pattern with `re:` — useful for value-shaped policies such as off-system utility classes. Regexes use the RE2 subset (no lookaround or backreferences):
|
|
436
406
|
|
|
437
407
|
```yaml
|
|
438
408
|
arch:
|
|
@@ -497,7 +467,7 @@ arch:
|
|
|
497
467
|
in: ["tests/**"]
|
|
498
468
|
```
|
|
499
469
|
|
|
500
|
-
`maxLines`
|
|
470
|
+
`maxLines` applies to every language klint scans, with no formatter dependency.
|
|
501
471
|
|
|
502
472
|
### Comment Budgets
|
|
503
473
|
|
|
@@ -522,7 +492,7 @@ arch:
|
|
|
522
492
|
in: ["src/**"]
|
|
523
493
|
```
|
|
524
494
|
|
|
525
|
-
Set `countDocComments: true` on either rule to count doc-comments toward the limit as well.
|
|
495
|
+
Set `countDocComments: true` on either rule to count doc-comments toward the limit as well.
|
|
526
496
|
|
|
527
497
|
Some comments are machinery rather than prose — markers a build or codegen step reads, region pragmas — and counting them makes both limits measure the wrong thing. `ignore` exempts them, on either rule:
|
|
528
498
|
|
|
@@ -542,8 +512,7 @@ klint scans TypeScript/JavaScript, Python, Swift, Rust, and C#. Every architectu
|
|
|
542
512
|
|
|
543
513
|
| | TypeScript / JavaScript | Python | Swift | Rust | C# |
|
|
544
514
|
|---|---|---|---|---|---|
|
|
545
|
-
| **
|
|
546
|
-
| **Parser** | TypeScript compiler API (TS engine), tree-sitter (Rust engine) | tree-sitter | tree-sitter | tree-sitter | tree-sitter |
|
|
515
|
+
| **Parser** | tree-sitter | tree-sitter | tree-sitter | tree-sitter | tree-sitter |
|
|
547
516
|
| `arch.imports` — static | ✅ | ✅ | ✅ | ✅ `use` | ✅ `using` |
|
|
548
517
|
| `arch.imports` — dynamic | ✅ `import()` | ✅ `importlib.import_module`, `__import__` | — | — | — |
|
|
549
518
|
| `arch.imports` — re-exports | ✅ `export … from` | — | ✅ `@_exported import` | ✅ `pub use` | — |
|
|
@@ -557,18 +526,16 @@ klint scans TypeScript/JavaScript, Python, Swift, Rust, and C#. Every architectu
|
|
|
557
526
|
| `arch.maxLines` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
558
527
|
| `arch.maxCommentDensity` / `maxCommentBlock` | ✅ `//`, `/* */`, `/** */` docs | ✅ `#` | ✅ `//`, `/* */`, `///` and `/** */` docs | ✅ `//`, `/* */`, `///` and `//!` docs | ✅ `//`, `/* */`, `///` and `/** */` docs |
|
|
559
528
|
| Built-in rules and plugins | ✅ | ✗ | ✗ | ✗ | ✗ |
|
|
560
|
-
| Custom rules (`klint.rules.ts`) | ✅ | ✗ | ✗ | ✗ | ✗ |
|
|
561
|
-
| Type-aware rules | ✅ | ✗ | ✗ | ✗ | ✗ |
|
|
562
529
|
|
|
563
530
|
**✅** supported · **—** the language has no equivalent construct · **✗** not supported yet
|
|
564
531
|
|
|
565
532
|
A dash is not a gap. Swift has no dynamic import expression, no `import type`, and one module per declaration; Rust has no dynamic import and no type-only import; C# has no dynamic `using`, no type-only import, and one namespace per `using`. Those rows can never be filled. The `✗` rows are real limits: the rule set and custom rules are written against the TypeScript AST, and type-aware rules need a type checker that only the TS engine has.
|
|
566
533
|
|
|
567
|
-
Imports are read off the AST in every language, so a specifier written inside a comment or a string is never an import — including nested Swift block comments.
|
|
534
|
+
Imports are read off the AST in every language, so a specifier written inside a comment or a string is never an import — including nested Swift block comments.
|
|
568
535
|
|
|
569
536
|
### Python Architecture Checks
|
|
570
537
|
|
|
571
|
-
|
|
538
|
+
klint applies architecture rules to Python projects:
|
|
572
539
|
|
|
573
540
|
```yaml
|
|
574
541
|
include: ["src"]
|
|
@@ -616,7 +583,7 @@ virtual-environment analysis.
|
|
|
616
583
|
|
|
617
584
|
### Swift Architecture Checks
|
|
618
585
|
|
|
619
|
-
|
|
586
|
+
klint applies architecture rules to Swift projects:
|
|
620
587
|
|
|
621
588
|
```yaml
|
|
622
589
|
include: ["Sources"]
|
|
@@ -660,7 +627,7 @@ alongside `/** */`.
|
|
|
660
627
|
|
|
661
628
|
### Rust Architecture Checks
|
|
662
629
|
|
|
663
|
-
|
|
630
|
+
klint applies architecture rules to Rust crates:
|
|
664
631
|
|
|
665
632
|
```yaml
|
|
666
633
|
include: ["crates/**/*.rs", "!target/**"]
|
|
@@ -707,7 +674,7 @@ doc-comments.
|
|
|
707
674
|
|
|
708
675
|
### C# Architecture Checks
|
|
709
676
|
|
|
710
|
-
|
|
677
|
+
klint applies architecture rules to C# projects:
|
|
711
678
|
|
|
712
679
|
```yaml
|
|
713
680
|
include: ["src"]
|
|
@@ -754,19 +721,16 @@ doc-comments alongside `/** */`.
|
|
|
754
721
|
|
|
755
722
|
## Built-in Rules
|
|
756
723
|
|
|
757
|
-
| Rule |
|
|
758
|
-
|
|
759
|
-
| `no-unguarded-json-parse` |
|
|
760
|
-
| `no-sync-in-async` |
|
|
761
|
-
| `no-
|
|
762
|
-
| `no-
|
|
763
|
-
| `no-
|
|
764
|
-
| `no-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
| `no-nested-template-literals` | No | Template literals nested inside template literals |
|
|
768
|
-
| `no-consecutive-array-push` | No | Multiple consecutive `push()` calls that should be grouped |
|
|
769
|
-
| `no-string-match` | No | `String#match()` usage better expressed with clearer APIs |
|
|
724
|
+
| Rule | Description |
|
|
725
|
+
|------|-------------|
|
|
726
|
+
| `no-unguarded-json-parse` | `JSON.parse()` called outside a try/catch |
|
|
727
|
+
| `no-sync-in-async` | Sync filesystem calls inside async functions |
|
|
728
|
+
| `no-async-predicate` | Inline `async` function passed to `filter`, `some`, `every`, `find` or `findIndex`; without types, any receiver counts |
|
|
729
|
+
| `no-nested-template-literals` | Template literals nested inside template literals |
|
|
730
|
+
| `no-consecutive-array-push` | Multiple consecutive `push()` calls that should be grouped |
|
|
731
|
+
| `no-string-match` | `String#match()` usage better expressed with clearer APIs |
|
|
732
|
+
|
|
733
|
+
Rules that needed TypeScript's type checker were removed in 1.0; see [Upgrading from klint 0.x](#upgrading-from-klint-0x).
|
|
770
734
|
|
|
771
735
|
## Plugins
|
|
772
736
|
|
|
@@ -789,100 +753,19 @@ Agent hooks should run klint with `--json` so violations are machine-readable, a
|
|
|
789
753
|
// .agents/hooks/klint.ts
|
|
790
754
|
import { runHook } from "./run-hook";
|
|
791
755
|
|
|
792
|
-
const exitCode = runHook(["
|
|
756
|
+
const exitCode = runHook(["npx", "klint", "--json", "--hook"]);
|
|
793
757
|
process.exit(exitCode);
|
|
794
758
|
```
|
|
795
759
|
|
|
796
760
|
In CI, run the same command you use locally:
|
|
797
761
|
|
|
798
762
|
```sh
|
|
799
|
-
|
|
800
|
-
|
|
763
|
+
npm ci
|
|
764
|
+
npx klint
|
|
801
765
|
```
|
|
802
766
|
|
|
803
767
|
The important contract is simple: klint exits `0` when the project is clean, `2` when blocking errors exist, and can emit JSON for agents that need structured feedback.
|
|
804
768
|
|
|
805
|
-
## Custom Rules
|
|
806
|
-
|
|
807
|
-
Create `klint.rules.ts` next to `klint.yaml` and export a `Record<string, KlintRule>` as default. Each key is the rule name.
|
|
808
|
-
|
|
809
|
-
```ts
|
|
810
|
-
import { relative } from "node:path";
|
|
811
|
-
import type { KlintRule } from "@konvert7/klint/core/types";
|
|
812
|
-
|
|
813
|
-
const noForbiddenPattern: KlintRule = {
|
|
814
|
-
check({ files, root, fileContents }, violations) {
|
|
815
|
-
for (const file of files) {
|
|
816
|
-
const lines = (fileContents.get(file) ?? "").split("\n");
|
|
817
|
-
for (let i = 0; i < lines.length; i++) {
|
|
818
|
-
if (/forbidden-pattern/.test(lines[i])) {
|
|
819
|
-
violations.push({
|
|
820
|
-
file: relative(root, file),
|
|
821
|
-
line: i + 1,
|
|
822
|
-
message: "Explain what is wrong and how to fix it.",
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
},
|
|
828
|
-
};
|
|
829
|
-
|
|
830
|
-
export default {
|
|
831
|
-
"no-forbidden-pattern": noForbiddenPattern,
|
|
832
|
-
};
|
|
833
|
-
```
|
|
834
|
-
|
|
835
|
-
All exported custom rules run at `"error"` severity by default. Override severity or scope through `rules`:
|
|
836
|
-
|
|
837
|
-
```yaml
|
|
838
|
-
rules:
|
|
839
|
-
no-forbidden-pattern:
|
|
840
|
-
severity: warn
|
|
841
|
-
include: ["src/server/**"]
|
|
842
|
-
```
|
|
843
|
-
|
|
844
|
-
No separate registration step is required.
|
|
845
|
-
|
|
846
|
-
### Auto-fix Support
|
|
847
|
-
|
|
848
|
-
Add a `fix` field to a violation to make it auto-fixable with `--fix`. Fixes replace a line range with new text:
|
|
849
|
-
|
|
850
|
-
```ts
|
|
851
|
-
violations.push({
|
|
852
|
-
file: relative(root, file),
|
|
853
|
-
line: i + 1,
|
|
854
|
-
message: "Use foo() instead of bar().",
|
|
855
|
-
fix: {
|
|
856
|
-
startLine: i + 1,
|
|
857
|
-
endLine: i + 1,
|
|
858
|
-
replacement: lines[i].replace("bar()", "foo()"),
|
|
859
|
-
},
|
|
860
|
-
});
|
|
861
|
-
```
|
|
862
|
-
|
|
863
|
-
### Type-aware Rules
|
|
864
|
-
|
|
865
|
-
For rules that need the TypeScript AST, use `walkAst`:
|
|
866
|
-
|
|
867
|
-
```ts
|
|
868
|
-
import ts from "typescript";
|
|
869
|
-
import { walkAst } from "@konvert7/klint/core/ast";
|
|
870
|
-
import type { KlintRule } from "@konvert7/klint/core/types";
|
|
871
|
-
|
|
872
|
-
const noSpecificCall: KlintRule = {
|
|
873
|
-
check({ files, fileContents }, violations) {
|
|
874
|
-
for (const file of files) {
|
|
875
|
-
const content = fileContents.get(file) ?? "";
|
|
876
|
-
walkAst(file, content, (node) => {
|
|
877
|
-
if (ts.isCallExpression(node)) {
|
|
878
|
-
// Inspect node with the TypeScript AST.
|
|
879
|
-
}
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
},
|
|
883
|
-
};
|
|
884
|
-
```
|
|
885
|
-
|
|
886
769
|
## How It Fits
|
|
887
770
|
|
|
888
771
|
klint is not a replacement for Biome, oxlint, ESLint, knip, or TypeScript itself.
|
|
@@ -890,6 +773,6 @@ klint is not a replacement for Biome, oxlint, ESLint, knip, or TypeScript itself
|
|
|
890
773
|
- Use TypeScript for type correctness.
|
|
891
774
|
- Use Biome or oxlint for fast syntax and style checks.
|
|
892
775
|
- Use knip for unused files and exports.
|
|
893
|
-
- Use klint for architecture, agent constraints, project policy
|
|
776
|
+
- Use klint for architecture, agent constraints, and project policy.
|
|
894
777
|
|
|
895
778
|
The goal is a small, sharp gate: the rules your project cannot afford to leave as prose.
|
package/bin/klint.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
function isMuslLinux() {
|
|
7
|
+
if (process.platform !== "linux") return false;
|
|
8
|
+
process.report.excludeNetwork = true;
|
|
9
|
+
return !process.report.getReport().header.glibcVersionRuntime;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function packageArch() {
|
|
13
|
+
const windowsOnArmRunsX64 = process.platform === "win32" && process.arch === "arm64";
|
|
14
|
+
return windowsOnArmRunsX64 ? "x64" : process.arch;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const target = `${process.platform}-${packageArch()}${isMuslLinux() ? "-musl" : ""}`;
|
|
18
|
+
const nativePackage = `@konvert7/klint-${target}`;
|
|
19
|
+
const binaryName = process.platform === "win32" ? "klint-rs.exe" : "klint-rs";
|
|
20
|
+
|
|
21
|
+
function nativeBinary() {
|
|
22
|
+
try {
|
|
23
|
+
const manifest = createRequire(import.meta.url).resolve(`${nativePackage}/package.json`);
|
|
24
|
+
return join(dirname(manifest), "bin", binaryName);
|
|
25
|
+
} catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function fail(message) {
|
|
31
|
+
process.stderr.write(`klint: ${message}\n`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const binary = nativeBinary();
|
|
36
|
+
if (!binary) {
|
|
37
|
+
fail(
|
|
38
|
+
`no native binary for ${target}: ${nativePackage} is not installed. ` +
|
|
39
|
+
"Reinstall without --omit=optional, or use a supported platform " +
|
|
40
|
+
"(darwin-arm64, darwin-x64, linux-arm64, linux-x64, linux-x64-musl, win32-x64)."
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
|
|
45
|
+
if (result.error) fail(`could not run ${binary}: ${result.error.message}`);
|
|
46
|
+
if (result.signal) process.kill(process.pid, result.signal);
|
|
47
|
+
process.exit(result.status ?? 1);
|
package/klint.schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://raw.githubusercontent.com/konvert7/klint/refs/tags/
|
|
2
|
+
"$id": "https://raw.githubusercontent.com/konvert7/klint/refs/tags/v1.0.0/klint.schema.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"additionalProperties": false,
|
|
5
5
|
"description": "klint configuration. Lives at klint.yaml (or klint.config.json) next to biome.json and knip.json.",
|
package/klint.schema.yaml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
$id: https://raw.githubusercontent.com/konvert7/klint/refs/tags/
|
|
1
|
+
$id: https://raw.githubusercontent.com/konvert7/klint/refs/tags/v1.0.0/klint.schema.json
|
|
2
2
|
$schema: http://json-schema.org/draft-07/schema#
|
|
3
3
|
additionalProperties: false
|
|
4
4
|
description: klint configuration. Lives at klint.yaml (or klint.config.json) next to biome.json and knip.json.
|
package/package.json
CHANGED
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konvert7/klint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Architecture-as-Code linter for TypeScript. Enforce layer boundaries, import rules, and singleton patterns in YAML.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"klint": "
|
|
7
|
+
"klint": "bin/klint.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"
|
|
11
|
-
"cli",
|
|
12
|
-
"core",
|
|
13
|
-
"rules",
|
|
14
|
-
"plugins",
|
|
15
|
-
"skill",
|
|
16
|
-
"tools",
|
|
10
|
+
"bin",
|
|
17
11
|
"README.md",
|
|
18
12
|
"klint.schema.json",
|
|
19
13
|
"klint.schema.yaml"
|
|
20
14
|
],
|
|
21
15
|
"engines": {
|
|
22
|
-
"
|
|
16
|
+
"node": ">=18"
|
|
23
17
|
},
|
|
24
18
|
"keywords": [
|
|
25
19
|
"architecture",
|
|
@@ -38,8 +32,6 @@
|
|
|
38
32
|
"scripts": {
|
|
39
33
|
"type-check": "tsc --noEmit",
|
|
40
34
|
"test": "bun test",
|
|
41
|
-
"test:rust-engine": "bun test tests/rust-engine-cli.test.ts",
|
|
42
|
-
"test:auto-engine": "bun cli.ts --engine auto",
|
|
43
35
|
"pack:check": "bun tests/pack-check.ts",
|
|
44
36
|
"native:stage": "bun tests/stage-native-binary.ts",
|
|
45
37
|
"native:smoke": "bun tests/smoke-native-binary.ts",
|
|
@@ -52,7 +44,7 @@
|
|
|
52
44
|
"check": "biome check",
|
|
53
45
|
"check-write": "biome check --write",
|
|
54
46
|
"knip": "knip-bun",
|
|
55
|
-
"klint": "
|
|
47
|
+
"klint": "cargo run --quiet -p klint-rs --",
|
|
56
48
|
"jscpd": "jscpd --noTips",
|
|
57
49
|
"schema": "cargo run --quiet -p klint-rs --example generate_schema -- $npm_package_version",
|
|
58
50
|
"lint-staged": "lint-staged",
|
|
@@ -63,20 +55,16 @@
|
|
|
63
55
|
"bun run check"
|
|
64
56
|
]
|
|
65
57
|
},
|
|
66
|
-
"dependencies": {
|
|
67
|
-
"@clack/prompts": "^1.3.0",
|
|
68
|
-
"typescript": "^5.9.3",
|
|
69
|
-
"yaml": "^2.9.0",
|
|
70
|
-
"zod": "^4.4.3"
|
|
71
|
-
},
|
|
72
58
|
"optionalDependencies": {
|
|
73
|
-
"@konvert7/klint-darwin-arm64": "0.
|
|
74
|
-
"@konvert7/klint-darwin-x64": "0.
|
|
75
|
-
"@konvert7/klint-linux-
|
|
76
|
-
"@konvert7/klint-
|
|
59
|
+
"@konvert7/klint-darwin-arm64": "1.0.0",
|
|
60
|
+
"@konvert7/klint-darwin-x64": "1.0.0",
|
|
61
|
+
"@konvert7/klint-linux-arm64": "1.0.0",
|
|
62
|
+
"@konvert7/klint-linux-x64": "1.0.0",
|
|
63
|
+
"@konvert7/klint-linux-x64-musl": "1.0.0",
|
|
64
|
+
"@konvert7/klint-win32-x64": "1.0.0"
|
|
77
65
|
},
|
|
78
66
|
"devDependencies": {
|
|
79
|
-
"@biomejs/biome": "2.6.
|
|
67
|
+
"@biomejs/biome": "2.6.5",
|
|
80
68
|
"@commitlint/cli": "^20.5.3",
|
|
81
69
|
"@commitlint/config-conventional": "^20.5.3",
|
|
82
70
|
"@semantic-release/changelog": "^6.0.3",
|
|
@@ -88,7 +76,9 @@
|
|
|
88
76
|
"jscpd": "4.2.3",
|
|
89
77
|
"knip": "^6.9.0",
|
|
90
78
|
"lint-staged": "^15.5.2",
|
|
91
|
-
"semantic-release": "^25.0.3"
|
|
79
|
+
"semantic-release": "^25.0.3",
|
|
80
|
+
"typescript": "^5.9.3",
|
|
81
|
+
"yaml": "^2.9.0"
|
|
92
82
|
},
|
|
93
83
|
"directories": {
|
|
94
84
|
"test": "tests"
|