@konvert7/klint 0.19.0 → 0.19.2

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 CHANGED
@@ -98,7 +98,7 @@ Rules give agents room to move quickly because the unsafe moves are blocked stru
98
98
  ## CLI
99
99
 
100
100
  ```sh
101
- klint [--config <dir>] [--rules <file>] [--fix] [--json]
101
+ klint [--config <dir>] [--rules <file>] [--engine ts|rust|compare|auto] [--fix] [--json]
102
102
  klint install-skill [--agents <list>] [--symlink | --copy]
103
103
  ```
104
104
 
@@ -106,6 +106,7 @@ klint install-skill [--agents <list>] [--symlink | --copy]
106
106
  |------|-------------|
107
107
  | `--config <dir>` | Directory containing `klint.yaml` or `klint.config.json` (default: cwd) |
108
108
  | `--rules <file>` | Path to custom rules file (default: `<configDir>/klint.rules.ts` when present) |
109
+ | `--engine <name>` | Engine mode: `ts` (default), `rust`, `compare`, or `auto` |
109
110
  | `--fix` | Apply auto-fixes for fixable violations in-place |
110
111
  | `--json` | Emit structured JSON to stdout for agents and CI |
111
112
 
@@ -115,6 +116,43 @@ klint install-skill [--agents <list>] [--symlink | --copy]
115
116
  klint install-skill --agents claude,opencode,cursor,codex --copy
116
117
  ```
117
118
 
119
+ ## Engines
120
+
121
+ 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.
122
+
123
+ Maintainer migration notes live in [`docs/rust-engine.md`](docs/rust-engine.md).
124
+
125
+ | Engine | What it does | When to use it |
126
+ |--------|--------------|----------------|
127
+ | `ts` | Runs the full TypeScript implementation. This is the default. | Maximum compatibility |
128
+ | `rust` | Runs only rules supported by the Rust engine. Unsupported TS-only rules are rejected. | Strict Rust smoke tests and native-engine debugging |
129
+ | `compare` | Runs TS and Rust on the same supported config and fails if JSON output differs. Requires `--json`. | Parity verification while porting rules |
130
+ | `auto` | Runs Rust-supported rules in Rust, TS-only rules in TypeScript, then merges output. | Recommended experimental migration path |
131
+
132
+ Examples:
133
+
134
+ ```sh
135
+ klint --engine auto
136
+ klint --engine rust --json # strict Rust-only run; rejects unsupported TS-only rules
137
+ klint --engine compare --json # parity check; rejects unsupported TS-only rules
138
+ ```
139
+
140
+ Rust currently supports:
141
+
142
+ - `arch` rules: imports, forbidden patterns, singleton locations
143
+ - `no-unguarded-json-parse`
144
+ - `no-nested-template-literals`
145
+ - `no-consecutive-array-push`
146
+ - `no-string-match`
147
+
148
+ These rules remain TypeScript-owned because they need TypeScript semantic information:
149
+
150
+ - `no-floating-promise`
151
+ - `no-misused-promises`
152
+ - `no-date-equality`
153
+ - `no-optional-chain-on-non-nullable`
154
+ - `no-object-in-template`
155
+
118
156
  ## Configuration
119
157
 
120
158
  `klint.yaml` lives at your project root alongside tools like `biome.json`, `tsconfig.json`, and `knip.json`.
package/cli.ts CHANGED
@@ -610,6 +610,13 @@ function resolveRustEngineCommand(configDir: string): RustEngineCommand {
610
610
  return { bin: nativePackageBin, args };
611
611
  }
612
612
 
613
+ if (!isSourceCheckout()) {
614
+ process.stderr.write(
615
+ "klint: native Rust engine binary is unavailable for this package version\n"
616
+ );
617
+ process.exit(1);
618
+ }
619
+
613
620
  return {
614
621
  bin: "cargo",
615
622
  args: ["run", "--quiet", "-p", "klint-rs", "--", ...args],
@@ -1,4 +1,4 @@
1
- import { existsSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { createRequire } from "node:module";
3
3
  import { dirname, join } from "node:path";
4
4
 
@@ -44,6 +44,7 @@ interface NativeBinaryOptions {
44
44
  platform?: string;
45
45
  arch?: string;
46
46
  exists?: (path: string) => boolean;
47
+ readPackageJson?: (path: string) => { version?: string };
47
48
  resolvePackageJson?: (packageName: string) => string;
48
49
  }
49
50
 
@@ -63,6 +64,7 @@ export function resolveNativePackageBinary({
63
64
  platform = process.platform,
64
65
  arch = process.arch,
65
66
  exists = existsSync,
67
+ readPackageJson = readJsonFile,
66
68
  resolvePackageJson,
67
69
  }: NativeBinaryOptions): string | undefined {
68
70
  const nativePackage = nativePackageForPlatform(platform, arch);
@@ -74,11 +76,33 @@ export function resolveNativePackageBinary({
74
76
  resolvePackageJson
75
77
  );
76
78
  if (!packageJsonPath) return undefined;
79
+ if (!nativePackageVersionMatches(packageRoot, packageJsonPath, readPackageJson)) {
80
+ return undefined;
81
+ }
77
82
 
78
83
  const binaryPath = join(dirname(packageJsonPath), nativePackage.binaryPath);
79
84
  return exists(binaryPath) ? binaryPath : undefined;
80
85
  }
81
86
 
87
+ function nativePackageVersionMatches(
88
+ packageRoot: string,
89
+ nativePackageJsonPath: string,
90
+ readPackageJson: (path: string) => { version?: string }
91
+ ): boolean {
92
+ try {
93
+ const rootVersion = readPackageJson(join(packageRoot, "package.json")).version;
94
+ const nativeVersion = readPackageJson(nativePackageJsonPath).version;
95
+ if (!rootVersion || !nativeVersion) return true;
96
+ return rootVersion === nativeVersion;
97
+ } catch {
98
+ return true;
99
+ }
100
+ }
101
+
102
+ function readJsonFile(path: string): { version?: string } {
103
+ return JSON.parse(readFileSync(path, "utf-8")) as { version?: string };
104
+ }
105
+
82
106
  function resolveOptionalPackageJson(
83
107
  packageRoot: string,
84
108
  packageName: string,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konvert7/klint",
3
- "version": "0.19.0",
3
+ "version": "0.19.2",
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": {
@@ -62,10 +62,10 @@
62
62
  "zod": "^4.4.3"
63
63
  },
64
64
  "optionalDependencies": {
65
- "@konvert7/klint-darwin-arm64": "*",
66
- "@konvert7/klint-darwin-x64": "*",
67
- "@konvert7/klint-linux-x64": "*",
68
- "@konvert7/klint-win32-x64": "*"
65
+ "@konvert7/klint-darwin-arm64": "0.19.2",
66
+ "@konvert7/klint-darwin-x64": "0.19.2",
67
+ "@konvert7/klint-linux-x64": "0.19.2",
68
+ "@konvert7/klint-win32-x64": "0.19.2"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@biomejs/biome": "2.4.7",
@@ -2,6 +2,13 @@ import { spawnSync } from "node:child_process";
2
2
  import { readFileSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
 
5
+ const NATIVE_PACKAGE_NAMES = [
6
+ "@konvert7/klint-darwin-arm64",
7
+ "@konvert7/klint-darwin-x64",
8
+ "@konvert7/klint-linux-x64",
9
+ "@konvert7/klint-win32-x64",
10
+ ];
11
+
5
12
  export async function verifyConditions(_, context) {
6
13
  const cwd = context.cwd ?? process.cwd();
7
14
  const packageJson = readPackageJson(cwd);
@@ -21,6 +28,10 @@ export async function prepare(_, context) {
21
28
 
22
29
  const packageJson = readPackageJson(cwd);
23
30
  packageJson.version = version;
31
+ packageJson.optionalDependencies ??= {};
32
+ for (const packageName of NATIVE_PACKAGE_NAMES) {
33
+ packageJson.optionalDependencies[packageName] = version;
34
+ }
24
35
  writePackageJson(cwd, packageJson);
25
36
  }
26
37