lintlane 2.5.0 → 4.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 CHANGED
@@ -1,321 +1,154 @@
1
1
  # lintlane
2
2
 
3
- Pre-push checks for React apps. One command sets up a shared
4
- [Biome](https://biomejs.dev) lint/format preset, git hooks, a Conventional
5
- Commits workflow and a CI workflow; a local dashboard lets you run the same
6
- checks your CI will run — and see exactly what would fail — before you push.
7
-
8
- **What you get**
9
-
10
- - A shared Biome preset (`base`, `strict`, `next`) with auto-detection.
11
- - A pre-commit hook that formats and lints staged files.
12
- - Conventional Commits, enforced and guided: a step-by-step prompt, a
13
- commit-message check, a pre-push check and a CI check.
14
- - A GitHub Actions workflow running `biome ci`.
15
- - `lintlane ui`: a local dashboard with a Lint tab (errors and fixes as a
16
- diff) and a CI tab (lint, typecheck, build and tests run locally).
3
+ A code-quality workflow for JavaScript, React and Next.js projects. lintlane
4
+ **orchestrates proven tools instead of reimplementing them**: one command wires
5
+ them together, one config drives the checks everywhere, and a local dashboard
6
+ shows what your CI will say before you push.
7
+
8
+ | Job | Tool |
9
+ | ------------------------------------ | --------------------------------------------------------- |
10
+ | Formatting and linting | [Biome](https://biomejs.dev) |
11
+ | Staged files (partial staging safe) | [lint-staged](https://github.com/lint-staged/lint-staged) |
12
+ | Git hooks | [Husky](https://typicode.github.io/husky) |
13
+ | Guided Conventional Commits | [Commitizen](https://commitizen.github.io/cz-cli/) |
14
+ | Commit message validation | [commitlint](https://commitlint.js.org) |
15
+ | Setup, checks, CI, doctor, dashboard | **lintlane** |
17
16
 
18
17
  ## Contents
19
18
 
20
19
  - [Quick start](#quick-start)
21
- - [Commands at a glance](#commands-at-a-glance)
22
20
  - [Requirements](#requirements)
23
- - [`init`](#init--one-time-project-setup)
24
- - [`check`, `check:fix`, `ci`](#check-checkfix-ci--run-biome)
25
- - [Commit workflow](#commit-workflow)
26
- - [`commit`](#commit--guided-conventional-commit)
27
- - [`check-commit`](#check-commit--validate-commit-messages)
28
- - [Hooks: how a bad commit gets caught](#hooks-how-a-bad-commit-gets-caught)
29
- - [Settings](#settings)
30
- - [Skipping a check](#skipping-a-check)
31
- - [Dashboard (`ui`)](#dashboard-ui)
21
+ - [Commands](#commands)
22
+ - [`init`](#init--one-time-setup)
23
+ - [Checks: one config for CLI, dashboard and CI](#checks-one-config-for-cli-dashboard-and-ci)
24
+ - [Git hooks](#git-hooks)
25
+ - [Commits](#commits)
26
+ - [`doctor`](#doctor)
27
+ - [Dashboard](#dashboard-ui)
32
28
  - [Presets](#presets)
33
- - [Manual setup (without the CLI)](#manual-setup-without-the-cli)
29
+ - [Migrating from lintlane 3.x](#migrating-from-lintlane-3x)
34
30
  - [Troubleshooting](#troubleshooting)
35
- - [Reference](#reference)
31
+ - [Security](#security)
32
+ - [Limitations](#limitations)
36
33
  - [Developing lintlane](#developing-lintlane)
37
34
 
38
35
  ## Quick start
39
36
 
40
37
  ```sh
41
- # 1. In your React project (a git repository with a package.json):
42
- bunx lintlane init
38
+ # In a git repository with a package.json. Preview first, if you like:
39
+ npx lintlane init --dry-run
40
+ npx lintlane init
43
41
 
44
- # 2. Fix what Biome can fix, see what's left:
45
- bunx lintlane check:fix
46
- bunx lintlane check
47
-
48
- # 3. Open the dashboard to review issues and run your CI checks locally:
49
- bunx lintlane ui
42
+ npx lintlane doctor # verify the setup
43
+ npx lintlane run # Biome + typecheck + build + tests, like CI
44
+ npx lintlane ui # the dashboard
50
45
  ```
51
46
 
52
- From then on, `git commit` is guided and checked, and every push is checked
53
- before it leaves your machine.
54
-
55
- > The first `bunx lintlane init` downloads the package. After that it's
56
- > installed in your project, so `bunx lintlane …` (or `npx lintlane …`,
57
- > `bun run lintlane …`) runs your local copy without touching the network.
58
-
59
- ## Commands at a glance
60
-
61
- | Command | What it does |
62
- | ----------------------------------------------- | --------------------------------------------------------------- |
63
- | `lintlane init [flags]` | One-time setup: installs, writes config, scripts, hooks and CI. |
64
- | `lintlane check [paths]` | Run `biome check` (lint + format), no changes. |
65
- | `lintlane check:fix [paths]` | Run `biome check --write`: apply safe fixes. |
66
- | `lintlane ci [paths]` | Run `biome ci --reporter=github` (what CI runs). |
67
- | `lintlane commit [flags] [-- <git args>]` | Guided Conventional Commit (alias `c`). |
68
- | `lintlane check-commit [flags]` | Validate commit messages (a message, a file or a git range). |
69
- | `lintlane ui [flags]` | Open the local dashboard (Lint + CI tabs). |
70
- | `lintlane -h` / `commit -h` / `check-commit -h` | Show help. |
71
-
72
- Every command except `init` runs from the project root. Examples below use
73
- `bunx lintlane …`; if you've installed it, `bun run lintlane …`,
74
- `npx lintlane …` or `node_modules/.bin/lintlane …` work too.
75
-
76
47
  ## Requirements
77
48
 
78
- - **Bun 1.2 or newer** to run the CLI (`init`, `check`, `check:fix`, `ci`,
79
- `commit`, `check-commit`, `ui`). The CLI is a Bun script.
80
- - **Your project does not need to use Bun.** `init` detects npm, pnpm, Yarn or
81
- Bun and uses that package manager.
82
- - **Git hooks only need Node** (18 or newer): they're plain Node scripts with no
83
- extra dependencies, so teammates don't need Bun to commit or push.
84
- - **Biome 2.2 up to (not including) 3.** It's a peer dependency; `init` installs it.
85
- - **Git**, for the commit workflow.
86
- - **macOS or Linux** for the guided prompt on a plain `git commit` (it reads the
87
- terminal through `/dev/tty`). Elsewhere that prompt is skipped; messages are
88
- still validated, and `lintlane commit` still works. Not tested on Windows.
49
+ - **Node.js 22 or newer.** Any package manager: npm, pnpm, Yarn or Bun
50
+ (`npx`, `pnpm dlx`, `yarn dlx`, `bunx`). `init` detects which one you use.
51
+ - **Biome 2.2 up to (not including) 3**, a peer dependency that `init` installs.
52
+ - **Git**.
53
+ - macOS or Linux for the guided prompt on a plain `git commit` (it reads the
54
+ terminal through `/dev/tty`). Windows is untested; see [Limitations](#limitations).
89
55
 
90
- ## `init` — one-time project setup
56
+ ## Commands
91
57
 
92
- ```sh
93
- bunx lintlane init
94
- ```
58
+ | Command | What it does |
59
+ | --------------------------- | ----------------------------------------------------------------------------------------- |
60
+ | `init [--dry-run]` | Install and configure everything. Safe to re-run; never overwrites your config. |
61
+ | `doctor [--json]` | Diagnose the setup. Read-only. Exit code 1 only if something is broken. |
62
+ | `run [checks…]` | Run the project's checks in order (`--list`, `--keep-going`). |
63
+ | `check`, `check:fix`, `ci` | Run `biome check`, `biome check --write`, `biome ci`. |
64
+ | `commit` (`c`) | Guided Conventional Commit, run by Commitizen. Git flags go after `--`. |
65
+ | `check-commit` | Validate messages with commitlint: `--message`, `--commit-msg-file`, `--rev-range`, `-d`. |
66
+ | `ui [--port n] [--no-open]` | The local dashboard. |
95
67
 
96
- `init` installs `@biomejs/biome`, `lintlane` and (unless `--no-hooks`)
97
- `husky`, then writes:
98
-
99
- | File / change | Purpose |
100
- | ----------------------------- | ----------------------------------------------------------------- |
101
- | `biome.json` | Extends the preset. If one exists, it only tells you what to add. |
102
- | `package.json` scripts | `check`, `check:fix`, and `prepare` (runs husky). |
103
- | `.husky/pre-commit` | Biome on staged files, auto-fixing what it can. |
104
- | `.husky/commit-msg` | Rejects messages that aren't Conventional Commits. |
105
- | `.husky/prepare-commit-msg` | Guided prompt when you run a plain `git commit`. |
106
- | `.husky/pre-push` | Checks every commit about to be pushed. |
107
- | `.github/workflows/biome.yml` | `biome ci`, plus a commit-message check on pull requests. |
108
-
109
- ### Flags
110
-
111
- | Flag | Effect |
112
- | ------------------ | ----------------------------------------------------------------------- |
113
- | `--preset <name>` | `base`, `strict` or `next`; overrides auto-detection. |
114
- | `--no-hooks` | Skip husky and every git hook (also leaves the commit check out of CI). |
115
- | `--no-commit-lint` | Keep the pre-commit hook; skip the commit-message hooks and CI check. |
116
- | `--no-ci` | Skip the GitHub Actions workflow. |
117
- | `--skip-install` | Print the install command instead of running it. |
118
- | `-h`, `--help` | Show usage. |
68
+ ## `init` — one-time setup
119
69
 
120
70
  ```sh
121
- bunx lintlane init # everything, preset auto-detected
122
- bunx lintlane init --preset strict # force a preset
123
- bunx lintlane init --no-commit-lint # lint hook only, no commit rules
124
- bunx lintlane init --no-hooks --no-ci # just biome.json and scripts
125
- bunx lintlane init --skip-install # you install packages yourself
126
- bunx lintlane init --preset next --no-ci
71
+ npx lintlane init [--dry-run] [--preset base|strict|next] [--no-hooks] [--no-commit-lint] [--no-ci] [--skip-install]
127
72
  ```
128
73
 
129
- ### Preset auto-detection
130
-
131
- Without `--preset`, `init` reads your `package.json` dependencies:
74
+ It installs `@biomejs/biome`, `lintlane` and (with hooks) `husky` as exact dev
75
+ dependencies — only those that are missing, so a Biome or Husky version you
76
+ already pin is never bumped — then adds only what is missing:
132
77
 
133
- | Found in dependencies | Preset used |
134
- | ------------------------------------------------- | ----------- |
135
- | `next` | `next` |
136
- | `@tanstack/react-start`, `@tanstack/start` | `base` |
137
- | `@react-router/dev` (React Router framework mode) | `base` |
138
- | `@remix-run/react`, `@remix-run/node` | `base` |
139
- | `react` (no framework above) | `base` |
140
- | none of the above | `base` |
78
+ - `biome.json` extending the detected preset (Next.js → `next`; React, TanStack
79
+ Start, React Router, Remix → `base`; `strict` only when you ask).
80
+ - `check`, `check:fix` and `prepare` scripts.
81
+ - a `lint-staged` entry in `package.json` that runs Biome on staged files.
82
+ - `commitlint.config.mjs` and `config.commitizen` in `package.json`.
83
+ - `.husky/pre-commit`, `commit-msg`, `prepare-commit-msg`, `pre-push`.
84
+ - `.github/workflows/biome.yml`, generated from your [checks](#checks-one-config-for-cli-dashboard-and-ci).
141
85
 
142
- Only Next.js has its own Biome lint domain, so it's the only framework that
143
- changes which preset is used. `init` says what it detected either way.
144
- `strict` is never auto-selected; pass `--preset strict` when you want it.
86
+ **`--dry-run`** prints every action (including the install command) and changes
87
+ nothing. **Re-running** is safe: whatever exists is reported and left alone. An
88
+ untouched hook from an older lintlane is upgraded; an edited one is never
89
+ rewritten, only reported. Existing lint-staged, commitlint and Commitizen
90
+ configuration is kept as it is.
145
91
 
146
- ### Package manager detection
92
+ The package manager comes from the `packageManager` field, then a lockfile, then
93
+ the tool that launched lintlane, then npm.
147
94
 
148
- `init` reads the `packageManager` field in `package.json`, otherwise whichever
149
- lockfile is present (`bun.lock`, `package-lock.json`, `pnpm-lock.yaml`,
150
- `yarn.lock`), and falls back to Bun. It uses that package manager's install
151
- command and writes matching CI setup steps.
95
+ ## Checks: one config for CLI, dashboard and CI
152
96
 
153
- ### Safe to re-run
97
+ `lintlane run`, the dashboard's CI tab and the generated workflow all read the
98
+ same list, detected from `package.json`:
154
99
 
155
- `init` never overwrites an existing `biome.json`/`biome.jsonc`, script, hook or
156
- workflow file; it prints a `!` warning for whatever already exists and writes
157
- only what's missing. That makes it the way to **upgrade**: update the package,
158
- then run `init` again to pick up hooks added in newer versions.
100
+ | id | Runs |
101
+ | ----------- | ------------------------------------------------------------------------------------------------------------------- |
102
+ | `install` | Frozen-lockfile install (off by default locally) |
103
+ | `biome` | `biome ci .` |
104
+ | `typecheck` | script `typecheck`, `type-check`, `check-types` or `tsc`; else `tsc --noEmit` locally when a `tsconfig.json` exists |
105
+ | `build` | script `build` |
106
+ | `test` | script `test` (npm's placeholder is ignored) |
159
107
 
160
- ## `check`, `check:fix`, `ci` — run Biome
161
-
162
- These wrap the Biome binary installed in your project, so you never need
163
- Biome's own flags:
164
-
165
- ```sh
166
- bunx lintlane check # biome check . (read-only)
167
- bunx lintlane check:fix # biome check --write . (applies safe fixes)
168
- bunx lintlane ci # biome ci --reporter=github . (CI-formatted output)
108
+ Tune it in `package.json`:
169
109
 
170
- bunx lintlane check src/ # limit to paths
171
- bunx lintlane check:fix src/components/Button.tsx
172
- bunx lintlane ci src/ tests/
173
- ```
174
-
175
- The exit code is Biome's, so they work in scripts and CI. If Biome isn't
176
- installed yet they say so and point you to `init`.
177
-
178
- `init` also adds plain `check` and `check:fix` scripts to `package.json` that
179
- call `biome` directly, so `npm run check` (or `pnpm`/`yarn`/`bun run check`)
180
- works without Bun:
181
-
182
- ```sh
183
- npm run check
184
- npm run check:fix
185
- ```
186
-
187
- ## Commit workflow
188
-
189
- `init` sets up [Conventional Commits](https://www.conventionalcommits.org)
190
- (`type(scope): subject`), which keeps history readable and works with changelog
191
- and release tooling. The `commit` and `check-commit` commands and flags follow
192
- [Commitizen](https://commitizen-tools.github.io/commitizen/) where they overlap
193
- (`check-commit` is Commitizen's `cz check`; it isn't called `check` because
194
- `lintlane check` runs Biome).
195
-
196
- **The format**
197
-
198
- ```text
199
- type(scope): subject
200
-
201
- optional body
202
-
203
- optional footer, e.g. Closes #123
204
- BREAKING CHANGE: what breaks and how to migrate
110
+ ```json
111
+ {
112
+ "lintlane": {
113
+ "checks": { "build": false, "test": "test:unit" }
114
+ }
115
+ }
205
116
  ```
206
117
 
207
- - **Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`,
208
- `build`, `ci`, `chore`, `revert`.
209
- - The scope is optional. The subject is required, shouldn't end in a period,
210
- and the header must stay under 100 characters.
211
- - A breaking change is marked with `!` (`feat(api)!: …`) plus a
212
- `BREAKING CHANGE:` footer.
213
- - Messages starting with `Merge`, `Revert`, `Pull request`, `fixup!`,
214
- `squash!` or `amend!` are left alone.
118
+ `false` turns a check off everywhere, a string names the script to run, `true`
119
+ keeps the detected one. Unknown ids or missing scripts are reported by `doctor`
120
+ and the dashboard instead of failing silently. Only script names come from
121
+ config, never command lines.
215
122
 
216
- ### `commit` — guided Conventional Commit
123
+ The workflow is written once by `init`; after changing checks, `lintlane doctor`
124
+ tells you which CI steps are missing.
217
125
 
218
- ```sh
219
- git add <files>
220
- bunx lintlane commit # alias: bunx lintlane c
221
- ```
126
+ ## Git hooks
222
127
 
223
- Asks seven questions — **type, scope, subject, body, breaking change, footer,
224
- confirm** — then runs `git commit` with the result, so your pre-commit and
225
- commit-msg hooks still run. It refuses if nothing is staged. Ctrl+C at any
226
- step cancels with no commit made. Invalid answers (an empty subject, a trailing
227
- period, an over-long header) are explained and asked again.
228
-
229
- | Flag | Effect |
230
- | -------------------------------- | -------------------------------------------------------------------- |
231
- | `--dry-run` | Show the message without committing. |
232
- | `--write-message-to-file <file>` | Write the message to a file (combine with `--dry-run`). |
233
- | `--retry` | Reuse your last message after a failed commit, without asking again. |
234
- | `--no-retry` | Always ask, even if `retryAfterFailure` is set. |
235
- | `-l`, `--message-length-limit` | Maximum header length (`0` = no limit). |
236
- | `-h`, `--help` | Show help for this command. |
237
- | `-- <git args>` | Everything after `--` is passed to `git commit`. |
128
+ Hooks are Husky scripts that call `lintlane hook <name>`, which runs the real tool:
238
129
 
239
- ```sh
240
- bunx lintlane commit # ask, then commit the staged changes
241
- bunx lintlane commit -- -a # stage all tracked changes, then commit
242
- bunx lintlane commit -- -a -S # …and sign the commit
243
- bunx lintlane commit -- -s # add a Signed-off-by line
244
- bunx lintlane commit -- -n # skip git hooks
245
- bunx lintlane commit --dry-run # just print the message
246
- bunx lintlane commit --dry-run --write-message-to-file msg.txt
247
- bunx lintlane commit -l 72 # stricter header limit for this commit
248
- ```
249
-
250
- **Retrying after a failed commit.** If a hook (for example Biome in pre-commit)
251
- rejects the commit after you've answered the questions, the message is kept.
252
- Fix the problem, stage the fix, then run `bunx lintlane commit --retry` — no
253
- re-typing. The saved message is cleared once a commit succeeds. Set
254
- `retryAfterFailure` (see [Settings](#settings)) to reuse it automatically.
130
+ | Hook | Does |
131
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
132
+ | `pre-commit` | **lint-staged** runs `biome check --write` on staged files. Partially staged files keep their unstaged edits; nothing you didn't stage is committed. A lint error blocks the commit and changes nothing. |
133
+ | `commit-msg` | **commitlint** validates the message. |
134
+ | `prepare-commit-msg` | A plain `git commit` opens the **Commitizen** prompt (skipped for `-m`, merges, amends, CI, no terminal, `LINTLANE_NO_PROMPT=1`). |
135
+ | `pre-push` | **commitlint** checks every commit being pushed, catching `--no-verify` and GUI clients. |
255
136
 
256
- ### `check-commit` — validate commit messages
137
+ Customize staged-file processing in the `lint-staged` key of `package.json` (or
138
+ any lint-staged config file). Bypass once with `git commit --no-verify`.
257
139
 
258
- Give it exactly one thing to check:
140
+ ## Commits
259
141
 
260
142
  ```sh
261
- bunx lintlane check-commit --message "feat: add login" # a message
262
- echo "feat: add login" | bunx lintlane check-commit # …or from stdin
263
- bunx lintlane check-commit --commit-msg-file .git/COMMIT_EDITMSG
264
- bunx lintlane check-commit --rev-range origin/main..HEAD # every commit in a range
265
- bunx lintlane check-commit -d # shorthand for <default branch>..HEAD
266
- ```
267
-
268
- | Flag | Effect |
269
- | ------------------------------ | ----------------------------------------------------- |
270
- | `--message <msg>` | Check this message (or pipe one in on stdin). |
271
- | `--commit-msg-file <path>` | Check the message in a file. |
272
- | `--rev-range <range>` | Check every commit in a git range, e.g. `main..HEAD`. |
273
- | `-d`, `--use-default-range` | Same as `--rev-range <default branch>..HEAD`. |
274
- | `--allow-abort` | Accept an empty message. |
275
- | `--allowed-prefixes <prefix>` | Skip messages starting with this (repeatable). |
276
- | `-l`, `--message-length-limit` | Maximum header length (`0` = no limit). |
277
-
278
- Exit codes: `0` everything passed, `1` a message was rejected, `2` a usage or
279
- git error. For a range, the output lists each bad commit with its hash and why.
280
-
281
- ### Hooks: how a bad commit gets caught
282
-
283
- Four layers, so a mistake is stopped somewhere:
284
-
285
- | Layer | Catches |
286
- | -------------------- | ------------------------------------------------------------------------------------------------------------------- |
287
- | `prepare-commit-msg` | A plain `git commit` (no `-m`): walks you through the steps instead of opening an empty editor. |
288
- | `commit-msg` | `git commit -m "test"`: rejects a message in the wrong format, and says why. |
289
- | `pre-push` | Commits that skipped the hooks (`--no-verify`, GUI clients, a fresh clone): checks every commit about to be pushed. |
290
- | CI (pull requests) | Anything that still got through: checks the commits in the PR. |
291
-
292
- ```text
293
- $ git commit -m "test"
294
-
295
- ✘ Commit message rejected
296
-
297
- • The header must look like "type(scope): subject", e.g. "feat(auth): add login form".
298
-
299
- Format: type(scope): subject
300
- Example: feat(auth): add login form
301
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
143
+ npx lintlane commit # guided prompt
144
+ npx lintlane commit -- -S # extra args go to git commit
145
+ npx lintlane check-commit --message "feat: add login"
146
+ npx lintlane check-commit --rev-range origin/main..HEAD
302
147
  ```
303
148
 
304
- Notes:
305
-
306
- - On a plain `git commit` the prompt's answer pre-fills your editor, and the
307
- message is validated again afterwards. `git commit --amend`, `-m` and `-F`
308
- skip the prompt but not the validation.
309
- - The prompt steps aside in CI, when `LINTLANE_NO_PROMPT` is set, and
310
- whenever there's no terminal (editors and GUI clients that commit for you).
311
- - Hooks run only once git hooks are installed. `init` does this, and teammates
312
- get them from `install` through the `prepare` script (`husky`).
313
- - The pre-push hook checks the commits being pushed (for a new branch: those
314
- not already on the remote). Tag pushes and branch deletions are ignored.
315
-
316
- ### Settings
317
-
318
- Optional, in your `package.json`:
149
+ Rules come from `commitlint.config.mjs`, which by default is
150
+ `export { default } from "lintlane/commitlint"`: Conventional Commits, plus the
151
+ optional block below. Replace the file with your own config any time.
319
152
 
320
153
  ```json
321
154
  {
@@ -323,119 +156,52 @@ Optional, in your `package.json`:
323
156
  "commit": {
324
157
  "types": ["feat", "fix", "chore"],
325
158
  "messageLengthLimit": 72,
326
- "allowedPrefixes": ["WIP"],
327
- "retryAfterFailure": true
159
+ "allowedPrefixes": ["WIP"]
328
160
  }
329
161
  }
330
162
  }
331
163
  ```
332
164
 
333
- | Key | Meaning |
334
- | -------------------- | ---------------------------------------------------------------------------------------------------------- |
335
- | `types` | Allowed types. A list of names, or `{ "feat": "A new feature" }` to give each a description in the prompt. |
336
- | `messageLengthLimit` | Maximum header length. `0` turns the check off. Default `100`. |
337
- | `allowedPrefixes` | Extra message prefixes to skip (added to the built-in ones). |
338
- | `retryAfterFailure` | Reuse your last message automatically after a failed commit. Default `false`. |
339
-
340
- The settings apply to the hooks, `commit` and `check-commit`. Command-line flags
341
- override them for that run.
342
-
343
- ### Skipping a check
344
-
345
- | To skip… | Do this |
346
- | ------------------------- | ----------------------------------------------------------------------- |
347
- | The commit hooks, once | `git commit --no-verify` (the pre-push hook and CI still check it). |
348
- | The pre-push check, once | `git push --no-verify` (CI still checks pull requests). |
349
- | Only the guided prompt | Set `LINTLANE_NO_PROMPT=1`. |
350
- | Commit rules for everyone | Re-run `init --no-commit-lint` on a fresh project, or delete the hooks. |
351
-
352
- ### Adding the CI step to an existing workflow
353
-
354
- `init` adds the commit-message check only to newly generated workflows. To add
355
- it to one you already have:
356
-
357
- ```yaml
358
- - uses: actions/checkout@v4
359
- with:
360
- fetch-depth: 0
361
- # …after your install step:
362
- - name: Check commit messages
363
- if: github.event_name == 'pull_request'
364
- run: node node_modules/lintlane/bin/check-commit.mjs --rev-range origin/${{ github.base_ref }}..HEAD
165
+ The guided prompt is Commitizen's standard `cz-conventional-changelog`
166
+ (`config.commitizen.path = "lintlane/presets/commitizen.cjs"`); configure it like
167
+ any Commitizen adapter.
168
+
169
+ ## `doctor`
170
+
171
+ ```sh
172
+ npx lintlane doctor # human-readable, with a fix for every problem
173
+ npx lintlane doctor --json # { ok, version, findings: [{ id, status, message, fix? }] }
365
174
  ```
366
175
 
176
+ It checks Node, `package.json`, git, the package manager, Biome (and whether it
177
+ is installed in the project, where lint-staged looks for it), lintlane, the
178
+ bundled tools, Husky and `core.hooksPath`, every hook (current, upgradable,
179
+ broken, not executable), lint-staged/commitlint/Commitizen config, your
180
+ `lintlane.checks`, and whether the CI workflow runs your checks. Statuses:
181
+ `pass`, `warn` (missing or out of date), `fail` (broken). It never changes anything.
182
+
367
183
  ## Dashboard (`ui`)
368
184
 
369
- `lintlane ui` starts a local dashboard in your browser so you can see what
370
- would fail before you push:
185
+ `npx lintlane ui` serves a dashboard on `127.0.0.1` and opens it.
371
186
 
372
- ```sh
373
- bunx lintlane ui # opens http://127.0.0.1:4321
374
- bunx lintlane ui --port 5000 # use another port
375
- bunx lintlane ui --no-open # print the URL without opening a browser
376
- ```
187
+ - **Lint tab:** Biome diagnostics by file, the proposed fix as a diff, apply
188
+ fixes per file or all at once. Errors from Biome (can't start, invalid output)
189
+ are shown with the underlying message; a second check can't start while one runs.
190
+ - **CI tab:** the same checks as `lintlane run` with live output, per-step status
191
+ and timing, Stop (kills the whole process tree) and stop-on-first-failure.
192
+ Configuration problems and `doctor` failures appear above the table.
377
193
 
378
- | Flag | Effect |
379
- | ------------ | ------------------------------------- |
380
- | `--port <n>` | Port to listen on (default `4321`). |
381
- | `--no-open` | Don't open the browser automatically. |
382
-
383
- It needs Biome installed in the project (run `init` first). Press Ctrl+C in the
384
- terminal to stop it. The ☀/☾ button in the header switches between light and
385
- dark mode; it follows your system setting until you click it, then remembers
386
- your choice.
387
-
388
- ### Lint tab
389
-
390
- Runs `biome check` and shows the result:
391
-
392
- - **Summary cards:** errors, warnings, files with issues, check time.
393
- - **File table:** each file's worst level, issue count, and whether Biome can fix
394
- it. Rows with errors are tinted. Search by file name, message or rule, and
395
- filter by All / Errors / Warnings.
396
- - **Detail panel:** click a file to see each issue (rule, line and column) and
397
- the proposed fix as a red/green diff, like `git diff`. Use ↑/↓ (or `j`/`k`) to
398
- move between files and `Esc` to close.
399
- - **Apply fix** writes the fix for one file; **Apply all fixes** applies them to
400
- every listed file in one batch. Nothing is written until you click, and the
401
- page re-checks afterwards.
402
-
403
- Only Biome's _safe_ fixes are applied (the same ones as `check:fix`). Issues
404
- with no automatic fix are labelled "Manual". The page lists the first 200 files
405
- with issues; if there are more, a notice says so and "Apply all fixes" only
406
- touches the files shown — click it again after the re-check to continue.
407
-
408
- ### CI tab
409
-
410
- Runs the checks your CI would run, locally. The steps are detected from your
411
- project:
412
-
413
- | Step | Runs | On by default |
414
- | ------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------- |
415
- | Install (frozen lockfile) | `bun install --frozen-lockfile`, `npm ci`, or the `pnpm`/`yarn` equivalent | No |
416
- | Biome CI (lint + format) | `biome ci .` — the same check the generated GitHub workflow runs | Yes |
417
- | Typecheck | your `typecheck` script (or `type-check`, `check-types`, `tsc`), else `tsc --noEmit` if there's a `tsconfig.json` | Yes |
418
- | Build | your `build` script | Yes |
419
- | Tests | your `test` script (skipped if it's npm's "no test specified" placeholder) | Yes |
420
-
421
- Tick the steps you want, then **Run CI**. Steps run in order with `CI=true`,
422
- and their output streams into the panel as they run. **Stop on first failure**
423
- (on by default, like real CI) skips the remaining steps after one fails. At the
424
- end a banner says either "All N steps passed — safe to push" or "CI would
425
- fail", naming the first failing step. **Stop** cancels the run and kills the
426
- running process.
427
-
428
- This is a script-based simulation, not a replica of your workflow: it runs the
429
- commands above, not the steps in `.github/workflows/*.yml`, and it doesn't
430
- reproduce CI's environment variables, secrets or Node version. A pass here is a
431
- strong signal, not a guarantee.
432
-
433
- ### Safety
434
-
435
- The server listens on `127.0.0.1` only. Every request needs a random token that
436
- is part of the URL printed at startup — opening the dashboard without it gives a
437
- 403. The page only sends step ids and file paths; the server decides which
438
- commands run and refuses paths outside the project.
194
+ ### Security
195
+
196
+ - Listens on loopback only, on a URL carrying a random per-run token; API calls
197
+ need the token in a header (compared in constant time).
198
+ - The `Host` header must be `127.0.0.1:<port>` or `localhost:<port>`, which
199
+ blocks DNS-rebinding attacks from web pages.
200
+ - The page is served with a Content-Security-Policy (per-response script nonce,
201
+ `default-src 'none'`, `frame-ancestors 'none'`), `nosniff` and `no-referrer`.
202
+ - The browser sends step ids and file paths, never commands. Paths are resolved
203
+ inside the project and anything outside is refused.
204
+ - Only the checks you configured run, with your project's own scripts.
439
205
 
440
206
  ## Presets
441
207
 
@@ -459,156 +225,88 @@ add your own ignores:
459
225
  To migrate an existing config into Biome first:
460
226
 
461
227
  ```sh
462
- bunx biome migrate eslint --write
463
- bunx biome migrate prettier --write
464
- ```
465
-
466
- ## Manual setup (without the CLI)
467
-
468
- **Install and configure**
469
-
470
- ```sh
471
- bun add -d -E @biomejs/biome lintlane
472
- npm install --save-dev --save-exact @biomejs/biome lintlane
473
- pnpm add --save-dev --save-exact @biomejs/biome lintlane
474
- yarn add --dev --exact @biomejs/biome lintlane
228
+ npx biome migrate eslint --write
229
+ npx biome migrate prettier --write
475
230
  ```
476
231
 
477
- Create `biome.json`, extending the preset you want:
478
-
479
- ```json
480
- { "extends": ["lintlane/biome"] }
481
- ```
482
-
483
- (`lintlane/biome/strict` or `lintlane/biome/next` for the others.)
484
- Add scripts:
485
-
486
- ```json
487
- "check": "biome check .",
488
- "check:fix": "biome check --write ."
489
- ```
490
-
491
- **Git hooks**
492
-
493
- ```sh
494
- bun add -d husky
495
- bunx husky init
496
- ```
497
-
498
- Put these in `.husky/`:
499
-
500
- `.husky/pre-commit`
501
-
502
- ```sh
503
- ./node_modules/.bin/biome check --write --staged --no-errors-on-unmatched --files-ignore-unknown=true
504
- git update-index --again
505
- ```
506
-
507
- `.husky/commit-msg`
508
-
509
- ```sh
510
- [ -f node_modules/lintlane/bin/commit-msg.mjs ] || exit 0
511
- node node_modules/lintlane/bin/commit-msg.mjs "$1"
512
- ```
513
-
514
- `.husky/prepare-commit-msg`
515
-
516
- ```sh
517
- [ -f node_modules/lintlane/bin/prepare-commit-msg.mjs ] || exit 0
518
- node node_modules/lintlane/bin/prepare-commit-msg.mjs "$1" "$2"
519
- ```
520
-
521
- `.husky/pre-push`
522
-
523
- ```sh
524
- [ -f node_modules/lintlane/bin/pre-push-check.mjs ] || exit 0
525
- node node_modules/lintlane/bin/pre-push-check.mjs "$1" "$2"
526
- ```
527
-
528
- Commits with errors Biome can't fix are blocked until you resolve them.
232
+ ## Migrating from lintlane 3.x
233
+
234
+ lintlane 4 replaces its own staged-file and commit code with the tools above.
235
+
236
+ 1. Upgrade: `npm install -D lintlane@4` (or your package manager's equivalent).
237
+ 2. Run `npx lintlane init --dry-run`, then `npx lintlane init`. Untouched 3.x
238
+ hooks are upgraded; a hook you edited is left alone and reported.
239
+ 3. Run `npx lintlane doctor`.
240
+
241
+ **Breaking changes**
242
+
243
+ - `lintlane` now has runtime dependencies (lint-staged, commitlint, Commitizen).
244
+ - `bin/pre-commit.mjs`, `commit-msg.mjs`, `prepare-commit-msg.mjs`,
245
+ `pre-push-check.mjs` are gone. Hooks that call them do nothing, which `doctor`
246
+ reports as a failure. `pre-commit.mjs --restore` is gone too: lint-staged
247
+ keeps its own backup stash.
248
+ - `lintlane commit` is Commitizen. `--write-message-to-file` and `-l` are gone;
249
+ `--dry-run`, `--retry`, `-a`, `-S` work as in Commitizen, `-- <git args>` as before.
250
+ - `check-commit`: `--allow-abort`, `--allowed-prefixes` and `-l` are gone; set
251
+ `lintlane.commit.allowedPrefixes` / `messageLengthLimit` or edit the commitlint config.
252
+ - Commit settings `retryAfterFailure` is gone (use `lintlane commit --retry`).
253
+ Custom `types` still apply to commitlint but no longer to the prompt; configure
254
+ the prompt through Commitizen.
255
+ - The CI workflow now runs every detected check (typecheck, tests, build), not
256
+ only Biome, and the commit check calls `lintlane check-commit`. Existing
257
+ workflows are not rewritten; turn checks off with `lintlane.checks`.
258
+ - Messages are formatted by commitlint, so wording of rejections changed.
529
259
 
530
260
  ## Troubleshooting
531
261
 
532
- **`bunx lintlane …` downloads the package each time, or says "command not found".**
533
- Outside a project that has it installed, `bunx` fetches `lintlane` from npm.
534
- Run `bunx lintlane init` in the project first (or install `lintlane` as a dev
535
- dependency), after which `bunx lintlane …` uses the local copy.
536
-
537
- **Hooks don't run.** Run your package manager's install (the `prepare` script
538
- runs husky), or run `node_modules/.bin/husky` once. Check
539
- `git config core.hooksPath` prints `.husky/_`.
540
-
541
- **The guided prompt doesn't appear on `git commit`.** It's skipped when `CI` or
542
- `LINTLANE_NO_PROMPT` is set, when you pass `-m`/`-F` or amend, when there's no
543
- terminal (GUI clients, some editors) and on systems without `/dev/tty`. Run
544
- `bunx lintlane commit` for the same steps. The message is validated regardless.
545
-
546
- **A commit was rejected and I lost my message.** Use
547
- `bunx lintlane commit --retry` if you committed through `lintlane commit`.
548
-
549
- **The pre-commit hook blocks my commit.** Biome found an error it can't fix.
550
- Run `bunx lintlane check` to see it, or open `bunx lintlane ui`. Fix it and
551
- commit again.
552
-
553
- **Push blocked: "commit message(s) don't follow Conventional Commits".** Reword
554
- the listed commits with `git rebase -i` (or `git commit --amend` for the latest),
555
- then push again.
556
-
557
- **`init` says a file already exists.** That's expected; `init` never overwrites.
558
- Delete the file and re-run `init` if you want lintlane's version.
559
-
560
- **The dashboard shows "Unauthorized" / 403.** Open the exact URL printed when
561
- `lintlane ui` started; it contains a one-time token.
562
-
563
- **Biome seems slow on a big project.** Add ignores for build output in your
564
- `biome.json` (for example `dist`, `.next`, `build`, `coverage`) so Biome doesn't
565
- scan them.
566
-
567
- ## Reference
568
-
569
- **Environment variables**
570
-
571
- | Variable | Effect |
572
- | -------------------- | --------------------------------------------------------------------------------- |
573
- | `LINTLANE_NO_PROMPT` | Set to any value to skip the guided prompt on `git commit`. |
574
- | `CI` | When set, the guided prompt is skipped. The CI tab sets it for the steps it runs. |
575
- | `NO_COLOR` | Turns off colors in the prompt. |
576
-
577
- **Exit codes**
578
-
579
- | Command | Codes |
580
- | -------------------------- | --------------------------------------------------------------------------------------------------- |
581
- | `check`, `check:fix`, `ci` | Biome's exit code. |
582
- | `commit` | `0` committed (or dry run), `1` cancelled, git's own exit code if the commit failed, `2` bad flags. |
583
- | `check-commit` | `0` valid, `1` rejected, `2` usage or git error. |
584
- | `ui` | Runs until you stop it. |
585
-
586
- **Files lintlane may create or use**
587
-
588
- | Path | What |
589
- | ------------------------------------------------------------------- | ---------------------------------------------- |
590
- | `biome.json` | Biome config extending a preset. |
591
- | `.husky/pre-commit`, `commit-msg`, `prepare-commit-msg`, `pre-push` | Git hooks. |
592
- | `.github/workflows/biome.yml` | CI workflow. |
593
- | `.git/lintlane-commit-msg` | Your last message, kept after a failed commit. |
262
+ Start with `npx lintlane doctor`; each problem comes with a fix.
263
+
264
+ | Symptom | Likely cause / fix |
265
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
266
+ | Hooks don't run | `core.hooksPath` isn't `.husky/_`. Run your package manager's `husky` (via `install`, which runs `prepare`). |
267
+ | "lintlane: node_modules/lintlane not found" | Dependencies aren't installed. Run your package manager's install. |
268
+ | `biome: command not found` in pre-commit | Biome isn't a dependency of the project. Install `@biomejs/biome`. |
269
+ | lint-staged: "no configuration found" | Add a `lint-staged` key (run `init`). |
270
+ | commitlint: "Please add rules" | Missing `commitlint.config.mjs` (run `init`). |
271
+ | `lintlane commit` says no adapter configured | Add `config.commitizen.path` (run `init`). |
272
+ | No prompt on `git commit` | Expected for `-m`, merges, amends, CI, GUI clients, Windows. Use `lintlane commit`. |
273
+ | Old hook silently does nothing | It calls a 3.x file that no longer exists. `init` upgrades untouched ones; see [Migrating](#migrating-from-lintlane-3x). |
274
+ | Dashboard says Forbidden | Open the exact URL printed by `lintlane ui` (it includes the token). |
275
+ | CI doesn't run a check | The workflow predates the check. Add the step, or disable the check under `lintlane.checks`. |
276
+
277
+ ## Limitations
278
+
279
+ - Windows isn't tested. The CLI avoids Unix-only APIs, but the guided prompt on
280
+ `git commit` is skipped there and CI here runs on Ubuntu and macOS only.
281
+ - The generated workflow targets GitHub Actions.
282
+ - The guided prompt uses the standard Conventional Commits adapter.
283
+ - lint-staged and commitlint behavior, including their config discovery, is theirs;
284
+ see their docs for advanced setups (monorepos, per-folder configs).
594
285
 
595
286
  ## Developing lintlane
596
287
 
597
- To try a local build in a test app before publishing:
288
+ Requires Node.js 22 or newer.
598
289
 
599
290
  ```sh
600
- # In the lintlane repo:
601
- npm pack --pack-destination /tmp/lintlane-pack # builds lintlane-<version>.tgz
602
-
603
- # In a throwaway app (a git repo with a package.json):
604
- bun add --dev --exact @biomejs/biome husky /tmp/lintlane-pack/lintlane-<version>.tgz
605
- node_modules/.bin/lintlane init --skip-install # the package isn't on npm yet
606
- node_modules/.bin/husky
291
+ npm test # unit + integration tests (about 40 seconds, no network)
292
+ npm run test:e2e # real npm / pnpm / yarn / bun installs (needs network)
293
+ npm run check # lint and format this repository
294
+ npm run verify:release [npm|pnpm|yarn|bun] # pack, install in a fresh project, init, doctor, real commits
607
295
  ```
608
296
 
609
- If you rebuild without changing the version, Bun may reuse its cached tarball:
610
- run `bun pm cache rm`, delete `node_modules/lintlane`, and reinstall (or
611
- bump the version first). `npm pack --dry-run` lists what would be published.
297
+ | File | What it covers |
298
+ | -------------------------------- | ------------------------------------------------------------------------------------------- |
299
+ | `test/hooks.test.mjs` | Real git: pre-commit (partial staging, failures), commit-msg, prepare-commit-msg, pre-push. |
300
+ | `test/setup.test.mjs` | `init --dry-run`, idempotency, upgrades from 3.x hooks, `doctor`. |
301
+ | `test/checks.test.mjs` | The shared checks list, generated CI, `lintlane run`. |
302
+ | `test/cli.test.mjs` | Running on Node without Bun, packaging, `init`. Package managers are **mocked** here. |
303
+ | `test/package-managers.test.mjs` | Package-manager detection and command generation (pure). |
304
+ | `test/ui.test.mjs` | Dashboard: auth, Host/CSP hardening, every API route, Stop. |
305
+ | `test/e2e.test.mjs` | **Real** package managers and the packed tarball. Needs `LINTLANE_E2E=1`. |
306
+
307
+ CI (`.github/workflows/ci.yml`) runs the tests on Ubuntu and macOS with Node 22
308
+ and 24, and `verify:release` for each package manager. To try a local build,
309
+ set `LINTLANE_INSTALL_SPEC` to a tarball from `npm pack` when running `init`.
612
310
 
613
311
  ## License
614
312