lanekeep 0.3.2 → 0.5.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 +286 -124
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -2,209 +2,352 @@
|
|
|
2
2
|
|
|
3
3
|
**Deterministic, AST-based architectural conformance checking for AI-generated and human-written code.**
|
|
4
4
|
|
|
5
|
+
[](https://crates.io/crates/lanekeep-cli)
|
|
6
|
+
[](https://www.npmjs.com/package/lanekeep)
|
|
7
|
+
[](https://pypi.org/project/lanekeep/)
|
|
8
|
+
[](https://github.com/fmsouza/lanekeep/actions/workflows/ci.yml)
|
|
5
9
|
[](#license)
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
lanekeep enforces the conventions that live in your team's heads and your reviewers' comments —
|
|
12
|
+
the ones a language model cannot infer from the code it is shown. Every rule is a codified answer
|
|
13
|
+
to **"the agent keeps doing this wrong."**
|
|
14
|
+
|
|
15
|
+
Checks **TypeScript, JavaScript, Python, Go and Rust**. Ships as a single static binary with no runtime
|
|
16
|
+
dependency.
|
|
10
17
|
|
|
11
18
|
---
|
|
12
19
|
|
|
13
|
-
##
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
Sixty seconds, from nothing to a rule catching something.
|
|
23
|
+
|
|
24
|
+
**1. Install** — whichever fits the project you are adding it to:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install --save-dev lanekeep
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
<details>
|
|
31
|
+
<summary>Python, Go, Homebrew, cargo, or a raw binary</summary>
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install lanekeep # Python
|
|
35
|
+
go get -tool github.com/fmsouza/lanekeep/cmd/lanekeep # Go
|
|
36
|
+
brew install fmsouza/tap/lanekeep # macOS / Linux, system-wide
|
|
37
|
+
cargo install lanekeep-cli # from source
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or download from the [releases page](https://github.com/fmsouza/lanekeep/releases).
|
|
41
|
+
|
|
42
|
+
</details>
|
|
43
|
+
|
|
44
|
+
**2. Scaffold a config and a first rule:**
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx lanekeep init
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
That writes two files, both runnable:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
lanekeep.json # what to check, and with which rules
|
|
54
|
+
lanekeep/rules/<starter>.ts # a worked example you can edit
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
It detects whether the project is Go, Python or TypeScript and scaffolds accordingly — the
|
|
58
|
+
right glob, a starter rule in that language, and a built-in worth having on.
|
|
59
|
+
|
|
60
|
+
**3. Check:**
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx lanekeep check
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
src/payment.ts:12:3 error [local/no-debugger] debugger statement
|
|
68
|
+
→ remove it before committing
|
|
69
|
+
|
|
70
|
+
✖ 1 error(s) across 1 file(s) checked
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> **If it says `0 file(s) checked`**, nothing matched the config's `include`. The scaffold starts
|
|
74
|
+
> with `src/**/*.{ts,tsx}` — widen it to wherever your code actually lives.
|
|
75
|
+
|
|
76
|
+
That is the whole loop. Everything below is detail.
|
|
77
|
+
|
|
78
|
+
---
|
|
14
79
|
|
|
15
|
-
|
|
16
|
-
lanekeep enforces *project-specific conventions* — the ones a language model has no way to infer
|
|
17
|
-
from the code it is shown, because they live in your team's heads and your reviewers' comments.
|
|
80
|
+
## What it is
|
|
18
81
|
|
|
19
|
-
|
|
82
|
+
lanekeep is not a linter in the ESLint sense. ESLint enforces language-level correctness; lanekeep
|
|
83
|
+
enforces *project-specific* conventions. The two do not overlap much, and lanekeep is not a
|
|
84
|
+
replacement for either your linter or your formatter.
|
|
20
85
|
|
|
21
|
-
Rules are TypeScript programs
|
|
86
|
+
Rules are TypeScript programs. Here is one checking **Go**:
|
|
22
87
|
|
|
23
88
|
```ts
|
|
24
89
|
import { defineRule } from 'lanekeep'
|
|
25
90
|
|
|
26
91
|
export default defineRule({
|
|
27
|
-
id: 'local/no-
|
|
92
|
+
id: 'local/no-fmt-println',
|
|
93
|
+
language: 'go',
|
|
28
94
|
severity: 'error',
|
|
29
95
|
|
|
30
96
|
card: {
|
|
31
|
-
message: '
|
|
32
|
-
remediation: '
|
|
33
|
-
examples: {
|
|
97
|
+
message: 'fmt.Println in library code',
|
|
98
|
+
remediation: 'use log/slog, so the output has a level and a destination',
|
|
99
|
+
examples: {
|
|
100
|
+
bad: 'fmt.Println("saved", count)',
|
|
101
|
+
good: 'slog.Info("saved", "count", count)',
|
|
102
|
+
},
|
|
34
103
|
},
|
|
35
104
|
|
|
36
105
|
// Matched in Rust, at native speed. Your code runs only on matches.
|
|
37
106
|
query: `
|
|
38
|
-
(
|
|
39
|
-
|
|
40
|
-
|
|
107
|
+
(call_expression
|
|
108
|
+
function: (selector_expression
|
|
109
|
+
operand: (identifier) @pkg
|
|
110
|
+
field: (field_identifier) @fn)) @call
|
|
41
111
|
`,
|
|
42
112
|
|
|
43
113
|
check(ctx, m) {
|
|
44
|
-
if (
|
|
45
|
-
if (
|
|
114
|
+
if (ctx.text(m.pkg) !== 'fmt') return
|
|
115
|
+
if (ctx.text(m.fn) !== 'Println') return
|
|
46
116
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
117
|
+
// The line that makes this a rule rather than a grep: a local variable
|
|
118
|
+
// named `fmt` is not the standard library package.
|
|
119
|
+
if (ctx.bindingKind(m.pkg) !== 'import') return
|
|
50
120
|
|
|
51
|
-
ctx.report(m.
|
|
121
|
+
ctx.report(m.call)
|
|
52
122
|
},
|
|
53
123
|
})
|
|
54
124
|
```
|
|
55
125
|
|
|
126
|
+
**Rules are TypeScript whatever they check** — that is one embedded language, not a JavaScript
|
|
127
|
+
bias. Rules need to be *programs*, because the conventions worth enforcing are too specific for
|
|
128
|
+
any fixed vocabulary of predicates, and one language keeps the sandbox, the cache and the host
|
|
129
|
+
API single-implementation.
|
|
130
|
+
|
|
131
|
+
**Configuration is not TypeScript.** `lanekeep.json` is plain data, so a Go or Python team never
|
|
132
|
+
writes a `.ts` file except when authoring an actual rule.
|
|
133
|
+
|
|
56
134
|
`check` is ordinary TypeScript. Loop, accumulate state, build data structures, read other files,
|
|
57
135
|
import shared helpers — there is no expressiveness ceiling and no DSL to learn beyond the query
|
|
58
136
|
that gates it.
|
|
59
137
|
|
|
60
|
-
|
|
138
|
+
**The card is not documentation.** `message`, `remediation` and `examples` are mandatory, because
|
|
139
|
+
they are what gets fed back to whoever has to act on the violation — increasingly an agent.
|
|
61
140
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
the
|
|
141
|
+
> **Editor types are not shipped yet.** `defineRule` and `defineConfig` resolve inside lanekeep's
|
|
142
|
+
> sandbox at run time, so rules execute correctly, but there is no published package supplying
|
|
143
|
+
> TypeScript definitions for the host API — you will not get autocomplete on `ctx` today.
|
|
144
|
+
> [`docs/architecture.md`](docs/architecture.md) §6 documents the full surface in the meantime.
|
|
65
145
|
|
|
66
|
-
|
|
146
|
+
## Supported languages
|
|
67
147
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- **Its output is read by a machine.** Violations are sorted deterministically, because an agent
|
|
71
|
-
that reads the output twice must not see reordering as change.
|
|
72
|
-
- **Every rule carries its own fix.** `message`, `remediation` and `examples` are mandatory
|
|
73
|
-
fields, not documentation — they are the rule card that gets fed back to the agent.
|
|
148
|
+
Each guide covers installing lanekeep in that ecosystem, what to put in the config, which
|
|
149
|
+
built-in rules apply, a worked custom rule, and the resolution behavior specific to it.
|
|
74
150
|
|
|
75
|
-
|
|
151
|
+
| Language | Guide | Extensions |
|
|
152
|
+
| --- | --- | --- |
|
|
153
|
+
| Go | **[Go guide](https://github.com/fmsouza/lanekeep/wiki/Go)** | `.go` |
|
|
154
|
+
| Python | **[Python guide](https://github.com/fmsouza/lanekeep/wiki/Python)** | `.py`, `.pyi` |
|
|
155
|
+
| Rust | **[Rust guide](https://github.com/fmsouza/lanekeep/wiki/Rust)** | `.rs` |
|
|
156
|
+
| TypeScript / JavaScript | **[TypeScript and JavaScript guide](https://github.com/fmsouza/lanekeep/wiki/TypeScript-and-JavaScript)** | `.ts`, `.mts`, `.cts`, `.tsx`, `.js`, `.mjs`, `.cjs`, `.jsx` |
|
|
76
157
|
|
|
77
|
-
|
|
78
|
-
|
|
158
|
+
Every one carries syntactic binding resolution, so a rule can ask where a name came from rather
|
|
159
|
+
than matching text — `ctx.bindingKind`, `ctx.resolvesToImport` and `ctx.isShadowed` answer for
|
|
160
|
+
all of them.
|
|
79
161
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
162
|
+
**The grammar is chosen by the file, not by the rule.** A rule declares which languages it
|
|
163
|
+
applies to and does not run on files of any other, defaulting to `['typescript', 'tsx']` when it
|
|
164
|
+
says nothing. That default is the one thing to get right on a non-TypeScript rule: omit
|
|
165
|
+
`language` on a Go rule and it silently never fires.
|
|
84
166
|
|
|
85
|
-
|
|
86
|
-
discover paths (globs, gitignore-aware)
|
|
87
|
-
└─> for each file, in parallel:
|
|
88
|
-
cache key ──hit──> validate tracked deps ──> cached violations + facts
|
|
89
|
-
└─miss─> path and raw-text gates reject before any parse
|
|
90
|
-
└─> parse ─> match queries in Rust
|
|
91
|
-
└─> invoke the TypeScript handler, per match only
|
|
92
|
-
└─> reduce phase: cross-file rules consume facts only, never parse trees
|
|
93
|
-
└─> filter suppressions ─> sort ─> report
|
|
94
|
-
```
|
|
167
|
+
## Configuration
|
|
95
168
|
|
|
96
|
-
|
|
169
|
+
`lanekeep.json`, at the project root. `lanekeep init` writes one for you, matched to the
|
|
170
|
+
project it finds.
|
|
97
171
|
|
|
98
|
-
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"$schema": "https://raw.githubusercontent.com/fmsouza/lanekeep/main/schema/lanekeep.schema.json",
|
|
99
175
|
|
|
100
|
-
|
|
176
|
+
"include": ["**/*.go"],
|
|
177
|
+
"exclude": ["**/*_test.go"],
|
|
101
178
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
179
|
+
"rules": [
|
|
180
|
+
"lanekeep/no-package-init",
|
|
181
|
+
{ "rule": "lanekeep/no-restricted-imports", "options": { "restrictions": [
|
|
182
|
+
{ "module": "database/sql", "from": ["!internal/store/**"], "reason": "go through the store package" }
|
|
183
|
+
] } },
|
|
184
|
+
"./lanekeep/rules/no-fmt-println.ts"
|
|
185
|
+
]
|
|
186
|
+
}
|
|
107
187
|
```
|
|
108
188
|
|
|
109
|
-
|
|
189
|
+
A string uses a rule as it comes; the object form calls it with options. `$schema` is what
|
|
190
|
+
gives you **completion and validation in your editor with nothing installed** — VS Code and
|
|
191
|
+
most others read it directly.
|
|
110
192
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
is pulled in as a dependency either way.
|
|
193
|
+
**Rules are TypeScript, configuration is not.** A rule is a program, and that is the point of
|
|
194
|
+
the tool; saying which rules to run is data. A Go or Python team should not have to write a
|
|
195
|
+
`.ts` file to do the second, which is why the config is JSON and only the rules are not.
|
|
115
196
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
npm launcher and the Homebrew formula say so rather than failing obscurely.
|
|
197
|
+
Rule ids are namespaced. `lanekeep/` is reserved for built-ins and `local/` needs no
|
|
198
|
+
declaration; any other prefix must be listed in `namespaces`, so a typo in an id is an error
|
|
199
|
+
rather than a rule that silently never runs.
|
|
120
200
|
|
|
121
|
-
|
|
201
|
+
Ten rules ship built in — four for TypeScript and JavaScript, two each for Python, Go and Rust. See
|
|
202
|
+
[`docs/built-in-rules.md`](docs/built-in-rules.md) for what each one checks and its options.
|
|
122
203
|
|
|
123
|
-
|
|
204
|
+
<details>
|
|
205
|
+
<summary>Configuring in TypeScript instead</summary>
|
|
124
206
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
→ use a named export, so the symbol has one name every importer must use
|
|
129
|
-
src/bad.ts:2:1 error [lanekeep/no-default-export] default export
|
|
130
|
-
→ use a named export, so the symbol has one name every importer must use
|
|
207
|
+
`lanekeep.config.ts` still works, and is the better choice when the config computes something
|
|
208
|
+
or shares a preset across repositories — composition is then ordinary `import`, with no
|
|
209
|
+
bespoke `extends` mechanism to learn.
|
|
131
210
|
|
|
132
|
-
|
|
211
|
+
```ts
|
|
212
|
+
import { defineConfig } from 'lanekeep'
|
|
213
|
+
import noDefaultExport from 'lanekeep/no-default-export'
|
|
214
|
+
import noDebugger from './lanekeep/rules/no-debugger'
|
|
215
|
+
|
|
216
|
+
export default defineConfig({
|
|
217
|
+
include: ['src/**/*.{ts,tsx}'],
|
|
218
|
+
rules: [noDefaultExport, noDebugger],
|
|
219
|
+
})
|
|
133
220
|
```
|
|
134
221
|
|
|
135
|
-
|
|
222
|
+
Both formats compile to the same thing before anything reads them, so they cannot differ in
|
|
223
|
+
behavior. `lanekeep.json` wins if a project somehow has both.
|
|
136
224
|
|
|
225
|
+
</details>
|
|
226
|
+
|
|
227
|
+
## Using it
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
lanekeep check # the whole project
|
|
231
|
+
lanekeep check --staged # only what is about to be committed
|
|
232
|
+
lanekeep check --since main # only what changed against a ref
|
|
233
|
+
lanekeep check --watch # re-check on every change, until Ctrl-C
|
|
234
|
+
lanekeep check --fix # apply the safe fixes, report what is left
|
|
235
|
+
lanekeep check --profile # where the run spent its time, per rule
|
|
236
|
+
lanekeep rules # what this project has configured
|
|
237
|
+
lanekeep explain <rule-id> # one rule's card, without opening its source
|
|
137
238
|
```
|
|
138
|
-
$ lanekeep check --fix
|
|
139
|
-
fixed 2 violation(s) in 2 file(s)
|
|
140
|
-
```
|
|
141
239
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
240
|
+
`--staged` and `--since` are intersected with the config's `include`/`exclude`, and both **skip
|
|
241
|
+
cross-file rules** — a whole-corpus rule over a subset gives a wrong answer rather than a smaller
|
|
242
|
+
one, so they are skipped and named on stderr instead of quietly producing one.
|
|
243
|
+
|
|
244
|
+
**Fixes.** Only a fix its rule marked as behavior-preserving is applied. Anything else is a
|
|
245
|
+
suggestion — shown, never written — because the cautious mistake costs a manual edit and the other
|
|
246
|
+
one rewrites your code silently.
|
|
145
247
|
|
|
146
|
-
Suppressions carry a mandatory reason and an optional expiry
|
|
147
|
-
|
|
148
|
-
than silently doing nothing:
|
|
248
|
+
**Suppressions** carry a mandatory reason and an optional expiry. A directive that does not work
|
|
249
|
+
says so, rather than silently doing nothing:
|
|
149
250
|
|
|
150
251
|
```ts
|
|
151
252
|
// lanekeep-ignore-next-line lanekeep/no-default-export reason: legacy entry point
|
|
152
253
|
export default parse
|
|
153
254
|
```
|
|
154
255
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
```
|
|
256
|
+
Run `lanekeep check --report-unused-suppressions` to find the ones that no longer silence
|
|
257
|
+
anything.
|
|
158
258
|
|
|
159
|
-
|
|
259
|
+
**Output.** `--format` takes `human` (default), `json` (versioned, stable schema), `sarif` (GitHub
|
|
260
|
+
code scanning) and `agent` — token-minimal, grouped by rule rather than by file, with each card
|
|
261
|
+
stated once instead of once per violation. Diagnostics always go to stderr, so piping into a
|
|
262
|
+
parser works even when something fails.
|
|
160
263
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
264
|
+
**Exit codes:** `0` clean, `1` violations found, `2` the checker could not run. A caller has to be
|
|
265
|
+
able to tell "your code has problems" from "the tool is broken". `--warn-only` reports violations
|
|
266
|
+
but exits `0`, for a phased rollout.
|
|
164
267
|
|
|
165
|
-
|
|
166
|
-
the problem:
|
|
268
|
+
## In CI, editors and agents
|
|
167
269
|
|
|
168
|
-
```
|
|
169
|
-
|
|
270
|
+
```bash
|
|
271
|
+
lanekeep check --staged # pre-commit
|
|
272
|
+
lanekeep check --format sarif # GitHub code scanning
|
|
273
|
+
lanekeep server # LSP, for any editor
|
|
274
|
+
lanekeep server --protocol mcp # MCP, for an agent host
|
|
170
275
|
```
|
|
171
276
|
|
|
172
|
-
|
|
277
|
+
MCP exposes three tools — `lanekeep_check`, `lanekeep_rules`, `lanekeep_explain` — so an agent
|
|
278
|
+
can ask what it broke and what the rule wants without shelling out and parsing text.
|
|
173
279
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
$ lanekeep rules --json
|
|
177
|
-
```
|
|
280
|
+
Worked examples for each, including SARIF upload and adopting on an existing codebase, are in
|
|
281
|
+
**[CI and Editors](https://github.com/fmsouza/lanekeep/wiki/CI-and-Editors)**.
|
|
178
282
|
|
|
179
|
-
|
|
283
|
+
## How it stays fast with programmable rules
|
|
284
|
+
|
|
285
|
+
The usual problem with a native tool that runs JavaScript plugins is the boundary between them:
|
|
286
|
+
dispatching into JS once per AST node means tens of thousands of crossings per file.
|
|
287
|
+
|
|
288
|
+
lanekeep dispatches once per **query match** instead. The tree-sitter query runs in Rust across a
|
|
289
|
+
single shared parse; only matches reach your handler. That is typically two to three orders of
|
|
290
|
+
magnitude fewer crossings, and it is the reason a Rust engine still earns its place once rules are
|
|
291
|
+
TypeScript.
|
|
180
292
|
|
|
181
293
|
```
|
|
182
|
-
|
|
183
|
-
|
|
294
|
+
discover paths (globs, gitignore-aware)
|
|
295
|
+
└─> for each file, in parallel:
|
|
296
|
+
cache key ──hit──> validate tracked deps ──> cached violations + facts
|
|
297
|
+
└─miss─> path and raw-text gates reject before any parse
|
|
298
|
+
└─> parse ─> match queries in Rust
|
|
299
|
+
└─> invoke the TypeScript handler, per match only
|
|
300
|
+
└─> reduce phase: cross-file rules consume facts only, never parse trees
|
|
301
|
+
└─> filter suppressions ─> sort ─> report
|
|
184
302
|
```
|
|
185
303
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
304
|
+
A warm run with no changes executes no JavaScript at all — every file is a cache hit.
|
|
305
|
+
|
|
306
|
+
Violations are always sorted by `(ruleId, file, line, column)`, and the sandbox withholds the clock
|
|
307
|
+
and randomness, so two runs over identical input produce byte-identical output. An agent reading
|
|
308
|
+
the output twice must not see reordering as change.
|
|
309
|
+
|
|
310
|
+
## Installing without a package manager
|
|
311
|
+
|
|
312
|
+
Prebuilt for macOS on Apple silicon, Linux on x86-64 and arm64, and Windows on x86-64. The Linux
|
|
313
|
+
binaries are built against **glibc 2.17**, so they run on anything from RHEL 7 onwards.
|
|
189
314
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
315
|
+
Intel macOS is not prebuilt — `cargo install lanekeep-cli` builds it from source, and both the npm
|
|
316
|
+
launcher and the Homebrew formula say so rather than failing obscurely.
|
|
317
|
+
|
|
318
|
+
**No runtime is required to run lanekeep**, even though rules are written in TypeScript. Node,
|
|
319
|
+
Python or Go is needed only to install it from that ecosystem, where it picks which binary to
|
|
320
|
+
fetch. Nothing is pulled in as a dependency any of those ways.
|
|
321
|
+
|
|
322
|
+
The Go package is a small launcher, because Go can only install and pin things written in Go: it
|
|
323
|
+
fetches the real binary on first use, verifies it against the release's published checksums, and
|
|
324
|
+
caches it. Set `LANEKEEP_BINARY` to an already-installed lanekeep and it fetches nothing.
|
|
196
325
|
|
|
197
326
|
## Documentation
|
|
198
327
|
|
|
328
|
+
**The [wiki](https://github.com/fmsouza/lanekeep/wiki) is the place to start** — it is task-shaped and organized by language.
|
|
329
|
+
|
|
330
|
+
| Page | Purpose |
|
|
331
|
+
| --- | --- |
|
|
332
|
+
| [Getting Started](https://github.com/fmsouza/lanekeep/wiki/Getting-Started) | Install and catch something, in about a minute |
|
|
333
|
+
| [Configuration](https://github.com/fmsouza/lanekeep/wiki/Configuration) | `lanekeep.json`, every field |
|
|
334
|
+
| [Writing Rules](https://github.com/fmsouza/lanekeep/wiki/Writing-Rules) | Rule anatomy and the full host API |
|
|
335
|
+
| [CI and Editors](https://github.com/fmsouza/lanekeep/wiki/CI-and-Editors) | Pre-commit, GitHub Actions, LSP, MCP |
|
|
336
|
+
| [Go](https://github.com/fmsouza/lanekeep/wiki/Go) · [Python](https://github.com/fmsouza/lanekeep/wiki/Python) · [Rust](https://github.com/fmsouza/lanekeep/wiki/Rust) · [TypeScript and JavaScript](https://github.com/fmsouza/lanekeep/wiki/TypeScript-and-JavaScript) | Per-language guides |
|
|
337
|
+
|
|
338
|
+
In-repo, versioned with the code:
|
|
339
|
+
|
|
199
340
|
| Document | Purpose |
|
|
200
341
|
| --- | --- |
|
|
201
342
|
| [`docs/architecture.md`](docs/architecture.md) | The full design: execution model, host API, cache, milestones |
|
|
202
343
|
| [`docs/built-in-rules.md`](docs/built-in-rules.md) | The rules lanekeep ships with, and their options |
|
|
203
344
|
| [`docs/cross-file-rules.md`](docs/cross-file-rules.md) | Writing a rule that needs a whole-corpus view |
|
|
204
|
-
| [`
|
|
345
|
+
| [`docs/adr/`](docs/adr/) | Decision records: why the design is the way it is |
|
|
205
346
|
| [`CONTRIBUTING.md`](CONTRIBUTING.md) | Setup, commands, and the pull request process |
|
|
347
|
+
| [`AGENTS.md`](AGENTS.md) | How to work in this repository — for coding agents and humans alike |
|
|
206
348
|
| [`SECURITY.md`](SECURITY.md) | Threat model and how to report a vulnerability |
|
|
207
349
|
| [`docs/releasing.md`](docs/releasing.md) | How a release is built, gated and published |
|
|
350
|
+
| [`CHANGELOG.md`](CHANGELOG.md) | What changed, per release |
|
|
208
351
|
|
|
209
352
|
## Security
|
|
210
353
|
|
|
@@ -217,10 +360,10 @@ Rules are executable code, so the posture is about confinement rather than absen
|
|
|
217
360
|
- **No network access.** Ever, in any mode, with no configuration that enables it.
|
|
218
361
|
- **Filesystem confinement.** Reads go through a tracked `ctx.readFile`, confined to the project
|
|
219
362
|
root. Writes happen only under `--fix`, only to matched files, only within reported ranges.
|
|
220
|
-
- **Bounded execution.** A per-invocation timeout, a
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
363
|
+
- **Bounded execution.** A per-invocation timeout, a global run budget and a per-runtime memory
|
|
364
|
+
ceiling, none disableable — a rule that hangs a pre-commit hook is indistinguishable from a
|
|
365
|
+
broken tool. Breaching any of them cancels the run and exits `2`, rather than reporting a partial
|
|
366
|
+
result as a clean one.
|
|
224
367
|
- **Deterministic by construction.** The sandbox withholds the clock and randomness, so a rule
|
|
225
368
|
cannot introduce nondeterminism even by accident.
|
|
226
369
|
|
|
@@ -228,14 +371,33 @@ This bounds blast radius and makes third-party rule sets reviewable. It is not a
|
|
|
228
371
|
someone who can already commit to the repository being checked. To report a vulnerability, see
|
|
229
372
|
[`SECURITY.md`](SECURITY.md).
|
|
230
373
|
|
|
374
|
+
## Project status
|
|
375
|
+
|
|
376
|
+
**Released and usable.** The current version is on [crates.io](https://crates.io/crates/lanekeep-cli),
|
|
377
|
+
[npm](https://www.npmjs.com/package/lanekeep), [PyPI](https://pypi.org/project/lanekeep/), Homebrew,
|
|
378
|
+
and as a Go module — one build feeding every channel, so the bytes are identical whichever you use.
|
|
379
|
+
|
|
380
|
+
It is **0.x**, and this repository treats that as semver does: a minor bump may break a public Rust
|
|
381
|
+
API. Rule authors are insulated from that — `ctx` methods and the config shape are additive — but
|
|
382
|
+
pin a version if you embed the crates.
|
|
383
|
+
|
|
384
|
+
Known gaps, stated rather than implied:
|
|
385
|
+
|
|
386
|
+
- **No editor types for rule authors yet** (above).
|
|
387
|
+
- **The performance budgets in [`docs/architecture.md`](docs/architecture.md) §15 are not met.**
|
|
388
|
+
They are targets, and that document says by how much and what the levers are. The tool is fast;
|
|
389
|
+
the numbers are simply ambitious.
|
|
390
|
+
- **No type-aware analysis**, by design. Binding resolution is syntactic — see §1 non-goals.
|
|
391
|
+
|
|
231
392
|
## Contributing
|
|
232
393
|
|
|
233
|
-
Contributions are welcome, particularly new built-in rules and new host API surface. Start
|
|
234
|
-
|
|
235
|
-
|
|
394
|
+
Contributions are welcome, particularly new built-in rules and new host API surface. Start with
|
|
395
|
+
[`CONTRIBUTING.md`](CONTRIBUTING.md) — `./scripts/setup-dev.sh` installs everything and wires the
|
|
396
|
+
git hooks, and `just check` is the same gate CI runs.
|
|
236
397
|
|
|
237
|
-
All work ships as squashed pull requests with
|
|
238
|
-
titles. `main` is protected and takes
|
|
398
|
+
All work ships as squashed pull requests with
|
|
399
|
+
[Conventional Commits](https://www.conventionalcommits.org/) titles. `main` is protected and takes
|
|
400
|
+
no direct pushes.
|
|
239
401
|
|
|
240
402
|
## License
|
|
241
403
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lanekeep",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Deterministic, AST-based architectural conformance checking",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"node": ">=18"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"@lanekeep/darwin-arm64": "0.
|
|
24
|
-
"@lanekeep/linux-arm64": "0.
|
|
25
|
-
"@lanekeep/linux-x64": "0.
|
|
26
|
-
"@lanekeep/win32-x64": "0.
|
|
23
|
+
"@lanekeep/darwin-arm64": "0.5.0",
|
|
24
|
+
"@lanekeep/linux-arm64": "0.5.0",
|
|
25
|
+
"@lanekeep/linux-x64": "0.5.0",
|
|
26
|
+
"@lanekeep/win32-x64": "0.5.0"
|
|
27
27
|
}
|
|
28
28
|
}
|