@konvert7/klint 0.38.0 → 1.0.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.
Files changed (56) hide show
  1. package/README.md +49 -166
  2. package/bin/klint.js +47 -0
  3. package/klint.schema.json +1 -1
  4. package/klint.schema.yaml +1 -1
  5. package/package.json +15 -25
  6. package/cli/command.ts +0 -189
  7. package/cli/commands.ts +0 -90
  8. package/cli/help.ts +0 -21
  9. package/cli/install-skill.ts +0 -260
  10. package/cli/output.ts +0 -178
  11. package/cli/paths.ts +0 -20
  12. package/cli/rust-config.ts +0 -145
  13. package/cli/rust-engine.ts +0 -323
  14. package/cli/stats.ts +0 -92
  15. package/cli.ts +0 -214
  16. package/core/arch-comments.ts +0 -125
  17. package/core/arch.ts +0 -470
  18. package/core/ast.ts +0 -63
  19. package/core/config.schema.ts +0 -413
  20. package/core/fixer.ts +0 -47
  21. package/core/native-binary.ts +0 -123
  22. package/core/paths.ts +0 -9
  23. package/core/rule-helpers.ts +0 -76
  24. package/core/runner.ts +0 -196
  25. package/core/schema-version.ts +0 -46
  26. package/core/skill.ts +0 -164
  27. package/core/stats.ts +0 -160
  28. package/core/types.ts +0 -146
  29. package/plugins/index.ts +0 -6
  30. package/plugins/sonar.ts +0 -27
  31. package/rules/index.ts +0 -26
  32. package/rules/no-async-predicate.ts +0 -51
  33. package/rules/no-consecutive-array-push.ts +0 -61
  34. package/rules/no-date-equality.ts +0 -35
  35. package/rules/no-floating-promise.ts +0 -37
  36. package/rules/no-misused-promises.ts +0 -59
  37. package/rules/no-nested-template-literals.ts +0 -47
  38. package/rules/no-object-in-template.ts +0 -95
  39. package/rules/no-optional-chain-on-non-nullable.ts +0 -47
  40. package/rules/no-single-char-class.ts +0 -113
  41. package/rules/no-string-match.ts +0 -63
  42. package/rules/no-sync-in-async.ts +0 -43
  43. package/rules/no-unguarded-json-parse.ts +0 -35
  44. package/rules/prefer-at.ts +0 -46
  45. package/rules/prefer-nullish-coalescing-assign.ts +0 -111
  46. package/rules/prefer-string-raw-regexp.ts +0 -93
  47. package/rules/prefer-string-raw.ts +0 -39
  48. package/rules/prefer-string-replaceall.ts +0 -67
  49. package/skill/klint-rules/SKILL.md +0 -264
  50. package/tools/generate-homebrew-formula.ts +0 -64
  51. package/tools/prepare-native-release.ts +0 -166
  52. package/tools/prepare-nuget-release.ts +0 -62
  53. package/tools/prepare-python-release.ts +0 -76
  54. package/tools/semantic-release-crates.js +0 -96
  55. package/tools/semantic-release-native-npm.js +0 -121
  56. 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 Swift projects.
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, custom repository policies, and type-aware TypeScript checks that should block an AI agent or CI run before code lands.
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 using Bun:
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
- bun add -d @konvert7/klint
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-floating-promise: error
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
- bun run klint
77
+ npx klint
78
78
  ```
79
79
 
80
80
  For machines and hooks:
81
81
 
82
82
  ```sh
83
- bun run klint -- --json
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
- | Type-aware mistakes | Promise-returning calls cannot be silently discarded |
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>] [--rules <file>] [--engine ts|rust|compare|auto] [--fix] [--json] [--hook]
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: the npm CLI prompts through `@clack/prompts`, and the native engine — which the wheel, the NuGet tool, and the standalone binary all wrap — prompts through `inquire`. Passing any of those flags, or piping stdin (CI, scripts, hooks), skips the questions and uses the defaults.
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-floating-promise 29 9
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
- ## Engines
201
+ ## Upgrading from klint 0.x
205
202
 
206
- klint currently ships a TypeScript engine and an experimental Rust engine. The Rust path is built for portable architecture checks and syntax-local rules; type-aware checks stay in TypeScript for now.
203
+ klint 1.0 runs one native engine on every platform. The TypeScript engine is gone, and with it:
207
204
 
208
- Maintainer migration notes live in [`docs/rust-engine.md`](docs/rust-engine.md).
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
- | Engine | What it does | When to use it |
211
- |--------|--------------|----------------|
212
- | `ts` | Runs the full TypeScript implementation. This is the default. | Maximum compatibility |
213
- | `rust` | Runs only rules supported by the Rust engine. Unsupported TS-only rules are rejected. | Strict Rust smoke tests and native-engine debugging |
214
- | `compare` | Runs TS and Rust on the same supported config and fails if JSON output differs. Requires `--json`. | Parity verification while porting rules |
215
- | `auto` | Runs Rust-supported rules in Rust, TS-only rules and unsupported plugin defaults in TypeScript, then merges output. | Recommended experimental migration path |
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
- Examples:
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-floating-promise: error
261
- no-misused-promises: error
231
+ no-async-predicate: error
262
232
  sonar/prefer-string-replaceall: warn
263
- my-custom-rule:
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 common JS/RE2 subset (no lookaround or backreferences) so the TS and Rust engines agree:
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` is enforced in both the TS and Rust engines and applies to every language klint scans, with no formatter dependency.
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. Both rules run in the TS and Rust engines; Python (`#`), Swift (`//`, `/* */`), and C# (`//`, `/* */`) files are enforced by the Rust engine.
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
- | **Engines** | `ts`, `rust` | `rust` | `rust` | `rust` | `rust` |
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. Only file discovery differs: the TS engine walks `.ts`/`.tsx`/`.js`/`.jsx`/`.mts`/`.cts`, so Python, Swift, Rust, and C# files reach klint through the Rust engine, which `auto` mode selects for them automatically.
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
- The Rust engine can apply architecture rules to Python projects:
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
- The Rust engine can apply architecture rules to Swift projects:
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
- The Rust engine can apply architecture rules to Rust crates:
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
- The Rust engine can apply architecture rules to C# projects:
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 | Type-aware | Description |
758
- |------|------------|-------------|
759
- | `no-unguarded-json-parse` | No | `JSON.parse()` called outside a try/catch |
760
- | `no-sync-in-async` | No | Sync filesystem calls inside async functions |
761
- | `no-floating-promise` | Yes | Promise-returning call whose result is discarded |
762
- | `no-misused-promises` | Yes | Async function passed where a sync callback is expected |
763
- | `no-async-predicate` | Yes | Async predicate passed to array filtering/search methods |
764
- | `no-date-equality` | Yes | Date values compared by object identity |
765
- | `no-optional-chain-on-non-nullable` | Yes | Optional chaining on values TypeScript knows are non-nullable |
766
- | `no-object-in-template` | Yes | Object values interpolated into template strings |
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(["bun", "run", "klint", "--", "--json", "--hook"]);
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
- bun install
800
- bun run klint
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, and custom type-aware checks.
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/v0.38.0/klint.schema.json",
2
+ "$id": "https://raw.githubusercontent.com/konvert7/klint/refs/tags/v1.0.1/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/v0.38.0/klint.schema.json
1
+ $id: https://raw.githubusercontent.com/konvert7/klint/refs/tags/v1.0.1/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.38.0",
3
+ "version": "1.0.1",
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": "cli.ts"
7
+ "klint": "bin/klint.js"
8
8
  },
9
9
  "files": [
10
- "cli.ts",
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
- "bun": ">=1.3.0"
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": "bun cli.ts --engine auto",
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.38.0",
74
- "@konvert7/klint-darwin-x64": "0.38.0",
75
- "@konvert7/klint-linux-x64": "0.38.0",
76
- "@konvert7/klint-win32-x64": "0.38.0"
59
+ "@konvert7/klint-darwin-arm64": "1.0.1",
60
+ "@konvert7/klint-darwin-x64": "1.0.1",
61
+ "@konvert7/klint-linux-arm64": "1.0.1",
62
+ "@konvert7/klint-linux-x64": "1.0.1",
63
+ "@konvert7/klint-linux-x64-musl": "1.0.1",
64
+ "@konvert7/klint-win32-x64": "1.0.1"
77
65
  },
78
66
  "devDependencies": {
79
- "@biomejs/biome": "2.6.4",
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"