readability-cli 0.8.4__tar.gz → 0.8.6__tar.gz

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 (30) hide show
  1. {readability_cli-0.8.4 → readability_cli-0.8.6}/.github/workflows/ci.yml +1 -1
  2. {readability_cli-0.8.4 → readability_cli-0.8.6}/.github/workflows/publish.yml +2 -2
  3. {readability_cli-0.8.4 → readability_cli-0.8.6}/.github/workflows/update-guides.yml +2 -2
  4. readability_cli-0.8.6/.prettierignore +2 -0
  5. {readability_cli-0.8.4 → readability_cli-0.8.6}/PKG-INFO +88 -89
  6. {readability_cli-0.8.4 → readability_cli-0.8.6}/README.md +87 -88
  7. {readability_cli-0.8.4 → readability_cli-0.8.6}/configs/biome-default.json +5 -0
  8. {readability_cli-0.8.4 → readability_cli-0.8.6}/pyproject.toml +1 -1
  9. {readability_cli-0.8.4 → readability_cli-0.8.6}/readability.py +31 -10
  10. {readability_cli-0.8.4 → readability_cli-0.8.6}/test_readability.py +85 -2
  11. {readability_cli-0.8.4 → readability_cli-0.8.6}/uv.lock +1 -1
  12. {readability_cli-0.8.4 → readability_cli-0.8.6}/.gitignore +0 -0
  13. {readability_cli-0.8.4 → readability_cli-0.8.6}/.python-version +0 -0
  14. {readability_cli-0.8.4 → readability_cli-0.8.6}/LICENSE +0 -0
  15. {readability_cli-0.8.4 → readability_cli-0.8.6}/configs/pyrefly.toml +0 -0
  16. {readability_cli-0.8.4 → readability_cli-0.8.6}/configs/ruff.toml +0 -0
  17. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/Rguide.md +0 -0
  18. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/cppguide.md +0 -0
  19. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/csharp-style.md +0 -0
  20. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/docguide-style.md +0 -0
  21. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/go-guide.md +0 -0
  22. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/htmlcssguide.md +0 -0
  23. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/javaguide.md +0 -0
  24. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/jsguide.md +0 -0
  25. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/jsoncstyleguide.md +0 -0
  26. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/objcguide.md +0 -0
  27. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/pyguide.md +0 -0
  28. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/shellguide.md +0 -0
  29. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/tsguide.md +0 -0
  30. {readability_cli-0.8.4 → readability_cli-0.8.6}/guides/vimscriptguide.md +0 -0
@@ -16,7 +16,7 @@ jobs:
16
16
  - name: Set up Python
17
17
  uses: astral-sh/setup-uv@v7
18
18
  with:
19
- python-version: '3.14'
19
+ python-version: "3.14"
20
20
 
21
21
  - name: Install dependencies
22
22
  run: uv sync
@@ -2,7 +2,7 @@ name: Publish
2
2
 
3
3
  on:
4
4
  push:
5
- tags: ['v*']
5
+ tags: ["v*"]
6
6
 
7
7
  jobs:
8
8
  publish:
@@ -17,7 +17,7 @@ jobs:
17
17
  - name: Set up Python
18
18
  uses: astral-sh/setup-uv@v7
19
19
  with:
20
- python-version: '3.14'
20
+ python-version: "3.14"
21
21
 
22
22
  - name: Build distribution
23
23
  run: uv build
@@ -2,7 +2,7 @@ name: Update Style Guides
2
2
 
3
3
  on:
4
4
  schedule:
5
- - cron: '0 0 * * 0' # Weekly on Sunday at midnight
5
+ - cron: "0 0 * * 0" # Weekly on Sunday at midnight
6
6
  workflow_dispatch: # Ad-hoc trigger
7
7
 
8
8
  jobs:
@@ -30,4 +30,4 @@ jobs:
30
30
  uses: stefanzweifel/git-auto-commit-action@v5
31
31
  with:
32
32
  commit_message: "chore: update style guides [skip ci]"
33
- file_pattern: 'guides/*.md'
33
+ file_pattern: "guides/*.md"
@@ -0,0 +1,2 @@
1
+ # Synced upstream guides remain byte-for-byte faithful to their source.
2
+ guides/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: readability-cli
3
- Version: 0.8.4
3
+ Version: 0.8.6
4
4
  Summary: A CLI to lint, format, and type-check code with Google-style defaults, and pull Google style guides in markdown format.
5
5
  Project-URL: Homepage, https://github.com/owahltinez/readability
6
6
  Project-URL: Repository, https://github.com/owahltinez/readability
@@ -17,22 +17,21 @@ Description-Content-Type: text/markdown
17
17
  A CLI tool that keeps code aligned with Google style conventions. It runs the
18
18
  right linters, formatters, and type checkers for your project with sensible
19
19
  defaults, and serves the official Google style guides in Markdown format. This
20
- is ideal for AI agents or developers who want consistent code quality checks
21
- and quick access to style conventions without browsing HTML pages.
20
+ is ideal for AI agents or developers who want consistent code quality checks and
21
+ quick access to style conventions without browsing HTML pages.
22
22
 
23
23
  ## Features
24
24
 
25
25
  - **Linting & Formatting**: A `check` command that automatically detects and
26
26
  runs relevant tools (Ruff, Pyrefly, Biome, Prettier, gofmt) for your project.
27
- - **Sensible Defaults**: Bundled Google-style configurations for Ruff,
28
- Pyrefly, and Biome are used automatically when a project does not define its
29
- own.
27
+ - **Sensible Defaults**: Google-style defaults for Ruff, Pyrefly, Biome, and
28
+ Prettier are used automatically when a project does not define its own.
30
29
  - **Style Guides**: A `guide` command that fetches the latest Google style
31
- guides (Python, Shell, C++, Java, JS/TS, Go, etc.) converted to Markdown,
32
- and outlines, addresses, and searches them by section rather than serving
33
- 200 KB to be read whole.
34
- - **Offline Mode**: Local caching of style guides for fast, offline access,
35
- kept fresh with a single `sync` command.
30
+ guides (Python, Shell, C++, Java, JS/TS, Go, etc.) converted to Markdown, and
31
+ outlines, addresses, and searches them by section rather than serving 200 KB
32
+ to be read whole.
33
+ - **Offline Mode**: Local caching of style guides for fast, offline access, kept
34
+ fresh with a single `sync` command.
36
35
 
37
36
  ## Quick Start
38
37
 
@@ -95,38 +94,39 @@ readability check . --fix
95
94
 
96
95
  ### Supported Tools
97
96
 
98
- | Tool | Supported Extensions | Config Needed | Fetched By |
99
- |------|----------------------|---------------|------------|
100
- | **Ruff** | `.py` | none, bundled | `uvx` |
101
- | **Pyrefly** | `.py` | none, bundled | `uvx` |
102
- | **Biome** | `.js`, `.ts`, `.jsx`, `.tsx`, `.json`, `.jsonc`, `.css`, `.html` | none, bundled | `npx` |
103
- | **Prettier** | `.js`, `.ts`, `.jsx`, `.tsx`, `.json`, `.css`, `.scss`, `.html`, `.md`, `.yml`, `.yaml` | `.prettierrc*` or `prettier.config.*` | `npx` |
104
- | **gofmt** | `.go` | `go.mod` | — |
97
+ | Tool | Supported Extensions | Config Needed | Fetched By |
98
+ | ------------ | --------------------------------------------------------------------------------------- | --------------------------------- | ---------- |
99
+ | **Ruff** | `.py` | none, bundled | `uvx` |
100
+ | **Pyrefly** | `.py` | none, bundled | `uvx` |
101
+ | **Biome** | `.js`, `.ts`, `.jsx`, `.tsx`, `.json`, `.jsonc`, `.css`, `.html` | none, bundled | `npx` |
102
+ | **Prettier** | `.js`, `.ts`, `.jsx`, `.tsx`, `.json`, `.css`, `.scss`, `.html`, `.md`, `.yml`, `.yaml` | none for Markdown, YAML, and SCSS | `npx` |
103
+ | **gofmt** | `.go` | `go.mod` | — |
105
104
 
106
105
  **You do not need to install any of these.** Each is resolved in order: a
107
- project-local install (`node_modules/.bin`, `.venv/bin`) first, then whatever
108
- is on your `PATH`, and only then fetched by `uvx` or `npx`. The runners cache
109
- what they download, so the fetch happens once per machine and later runs are
110
- served from disk and work offline.
106
+ project-local install (`node_modules/.bin`, `.venv/bin`) first, then whatever is
107
+ on your `PATH`, and only then fetched by `uvx` or `npx`. The runners cache what
108
+ they download, so the fetch happens once per machine and later runs are served
109
+ from disk and work offline.
111
110
 
112
- That keeps this package at ~4 MB rather than the ~54 MB it would take to
113
- carry Ruff and Pyrefly itself — a cost that would fall on everyone using only
114
- `guide`. An installed copy always wins over a fetched one, so a project that
115
- pinned a version is linted against the rules it chose.
111
+ That keeps this package at ~4 MB rather than the ~54 MB it would take to carry
112
+ Ruff and Pyrefly itself — a cost that would fall on everyone using only `guide`.
113
+ An installed copy always wins over a fetched one, so a project that pinned a
114
+ version is linted against the rules it chose.
116
115
 
117
116
  Fetched tools carry a minimum version, enough to understand the bundled
118
117
  configurations, and no maximum. A ceiling would freeze anyone who installed
119
- nothing at whatever was current when this package shipped, and tie its
120
- releases to Ruff's — which has published seventeen minor series, roughly one
121
- every six weeks. If you need a fixed version, install it: an installed tool
122
- always wins over a fetched one.
118
+ nothing at whatever was current when this package shipped, and tie its releases
119
+ to Ruff's — which has published seventeen minor series, roughly one every six
120
+ weeks. If you need a fixed version, install it: an installed tool always wins
121
+ over a fetched one.
123
122
 
124
- Set `UV_OFFLINE=1` to forbid fetching. A tool that then cannot be reached
125
- fails the run rather than passing it.
123
+ Set `UV_OFFLINE=1` to forbid fetching. A tool that then cannot be reached fails
124
+ the run rather than passing it.
126
125
 
127
126
  Ruff, Pyrefly, and Biome ship with bundled configurations, so they run on any
128
- file they handle without the project arranging anything. Prettier and gofmt
129
- bring no defaults here, so they wait until the project asks with a config file.
127
+ file they handle without the project arranging anything. Prettier does the same
128
+ for Markdown, YAML, and SCSS; a Prettier configuration opts its overlapping web
129
+ formats into Prettier too. Gofmt waits for a `go.mod` file.
130
130
 
131
131
  A tool that could not be reached at all is never skipped quietly:
132
132
 
@@ -141,8 +141,8 @@ $ readability check src/
141
141
  Error: Every tool for 1 path(s) is missing, so nothing was verified.
142
142
  ```
143
143
 
144
- That second case needs neither the tool nor a runner to be present, which is
145
- the state a container image is usually in.
144
+ That second case needs neither the tool nor a runner to be present, which is the
145
+ state a container image is usually in.
146
146
 
147
147
  `check` exits non-zero on findings, tool failures, and when every applicable
148
148
  tool is absent, so it can gate CI without a clean exit meaning required tools
@@ -165,27 +165,28 @@ if report.findings or report.failed or not report.ran:
165
165
  ```
166
166
 
167
167
  The returned `CheckReport` records only whether findings occurred and which
168
- tools ran, were skipped, or failed. Detailed tool findings are still written
169
- as each tool runs. Every path is validated before any tool runs, and a missing
170
- one raises `FileNotFoundError` rather than being misreported as a finding.
171
- Relative paths remain relative to the process working directory;
172
- `project_root` controls configuration discovery only.
168
+ tools ran, were skipped, or failed. Detailed tool findings are still written as
169
+ each tool runs. Every path is validated before any tool runs, and a missing one
170
+ raises `FileNotFoundError` rather than being misreported as a finding. Relative
171
+ paths remain relative to the process working directory; `project_root` controls
172
+ configuration discovery only.
173
173
 
174
174
  ### Default Configurations
175
175
 
176
176
  Ruff and Pyrefly defaults follow the
177
177
  [Google Python style guide](https://google.github.io/styleguide/pyguide.html):
178
- 80-column lines, Google docstrings, import ordering, and full type checking.
179
- The Biome default applies the 80-column lines and two-space indentation of the
178
+ 80-column lines, Google docstrings, import ordering, and full type checking. The
179
+ Biome default applies the 80-column lines and two-space indentation of the
180
180
  [Google JavaScript style guide](https://google.github.io/styleguide/jsguide.html)
181
- and enables Biome's recommended lint rules. These defaults apply only when the
182
- project does not define its own configuration. To override them, add a
183
- `[tool.ruff]` or `[tool.pyrefly]` section to your `pyproject.toml`, or a
184
- dedicated `ruff.toml`, `pyrefly.toml`, `biome.json`, or `biome.jsonc` any
185
- project-level configuration takes full precedence. The bundled Biome file
186
- intentionally uses only settings supported by both Biome 1.x and 2.x, so a
187
- project-local installation remains authoritative without creating a schema
188
- mismatch.
181
+ and enables Biome's recommended lint rules. Prettier wraps prose at 80 columns,
182
+ following the
183
+ [Google Markdown style guide](https://google.github.io/styleguide/docguide/style.html#character-line-limit).
184
+ These defaults apply only when the project does not define its own
185
+ configuration. To override them, add a `[tool.ruff]` or `[tool.pyrefly]` section
186
+ to your `pyproject.toml`, or a dedicated `ruff.toml`, `pyrefly.toml`,
187
+ `biome.json`, `biome.jsonc`, or Prettier configuration any project-level
188
+ configuration takes full precedence. The bundled Biome file requires Biome 2.5
189
+ or later, matching the version floor used by the fallback runner.
189
190
 
190
191
  ## Style Guides
191
192
 
@@ -211,17 +212,17 @@ readability guide python --full | grep -i f-string
211
212
 
212
213
  A guide can exceed 200 KB, so the outline is what a bare invocation prints:
213
214
  listing the sections and fetching the one you need beats reading the lot.
214
- Nothing is written to disk, which is what a coding agent should do rather
215
- than redirecting a guide into the repository it is working on — use a shell
216
- redirect if you do want a copy.
215
+ Nothing is written to disk, which is what a coding agent should do rather than
216
+ redirecting a guide into the repository it is working on — use a shell redirect
217
+ if you do want a copy.
217
218
 
218
- `REF` and `--full` cannot be combined; that is refused rather than resolved
219
- by a precedence rule you would have to know.
219
+ `REF` and `--full` cannot be combined; that is refused rather than resolved by a
220
+ precedence rule you would have to know.
220
221
 
221
222
  ### Navigating a Guide
222
223
 
223
- The outline gives each section an index to fetch it by, and flags the ones
224
- large enough to be worth knowing about first:
224
+ The outline gives each section an index to fetch it by, and flags the ones large
225
+ enough to be worth knowing about first:
225
226
 
226
227
  ```bash
227
228
  $ readability guide cpp
@@ -235,28 +236,28 @@ Google C++ Style Guide
235
236
  # 140 sections · print one: readability guide cpp 4.5.1
236
237
  ```
237
238
 
238
- Sizes appear only at 1200 words and above — the 4% of sections expensive
239
- enough that you would want warning. They include subsections, since that is
240
- what the reference returns. The trailing line goes to stderr, so it never
241
- contaminates a piped outline.
239
+ Sizes appear only at 1200 words and above — the 4% of sections expensive enough
240
+ that you would want warning. They include subsections, since that is what the
241
+ reference returns. The trailing line goes to stderr, so it never contaminates a
242
+ piped outline.
242
243
 
243
244
  A section reference can be any of the following:
244
245
 
245
- | Reference | Example |
246
- |-----------|---------|
247
- | Section index, as shown by the outline | `2.2.4` |
248
- | Heading text, case-insensitive, or its slug | `"function comments"` |
246
+ | Reference | Example |
247
+ | ------------------------------------------- | ---------------------- |
248
+ | Section index, as shown by the outline | `2.2.4` |
249
+ | Heading text, case-insensitive, or its slug | `"function comments"` |
249
250
  | A parent-scoped path, spaces around the `>` | `"Imports > Decision"` |
250
251
 
251
- Whole matches are preferred; a reference that matches nothing in full is
252
- retried as a substring of the heading text.
252
+ Whole matches are preferred; a reference that matches nothing in full is retried
253
+ as a substring of the heading text.
253
254
 
254
255
  Three of the shipped guides — Python, JavaScript, and Java — number their own
255
256
  sections, and those numbers are the index. A rule cited from the outline then
256
- matches the published guide exactly, including where the guide skips a
257
- number: the Python guide has no 2.15 at all, so its 2.16 is listed as 2.16
258
- rather than renumbered. The other eleven guides number nothing, so their
259
- index comes from each heading's position in the tree.
257
+ matches the published guide exactly, including where the guide skips a number:
258
+ the Python guide has no 2.15 at all, so its 2.16 is listed as 2.16 rather than
259
+ renumbered. The other eleven guides number nothing, so their index comes from
260
+ each heading's position in the tree.
260
261
 
261
262
  Either way the index is unique, which is what makes a repeated heading
262
263
  addressable — `Definition`, `Pros`, `Cons`, and `Decision` appear under every
@@ -279,8 +280,8 @@ Error: 'Decision' matches 19 headings in the 'python' guide. Repeat with one of:
279
280
 
280
281
  A guide discusses plenty that no heading is named after, so a reference that
281
282
  matches nothing is usually a vocabulary mismatch rather than a mistake. The
282
- sections that mention the words are reported, which turns the miss into the
283
- next command:
283
+ sections that mention the words are reported, which turns the miss into the next
284
+ command:
284
285
 
285
286
  ```bash
286
287
  $ readability guide python f-string
@@ -290,22 +291,21 @@ It appears in these sections:
290
291
  3.10.1 Python Style Rules > Strings > Logging
291
292
  ```
292
293
 
293
- This locates a rule; it does not search text. For that, pipe `--full` to
294
- `grep`, which you already know and which brings its own `-i`, `-A`, `-c` and
295
- the rest:
294
+ This locates a rule; it does not search text. For that, pipe `--full` to `grep`,
295
+ which you already know and which brings its own `-i`, `-A`, `-c` and the rest:
296
296
 
297
297
  ```bash
298
298
  readability guide python --full | grep -in -A2 "f-string"
299
299
  ```
300
300
 
301
- Content goes to stdout and diagnostics to stderr, so every form is safe to
302
- pipe. Headings inside fenced code blocks are ignored, which matters for the
303
- Shell and Python guides where `#` starts a comment.
301
+ Content goes to stdout and diagnostics to stderr, so every form is safe to pipe.
302
+ Headings inside fenced code blocks are ignored, which matters for the Shell and
303
+ Python guides where `#` starts a comment.
304
304
 
305
305
  ### Supported Languages
306
306
 
307
- Run `readability guide` with no language for the full list and which guides
308
- are cached:
307
+ Run `readability guide` with no language for the full list and which guides are
308
+ cached:
309
309
 
310
310
  ```bash
311
311
  $ readability guide
@@ -338,17 +338,16 @@ readability sync
338
338
 
339
339
  ### Offline Mode
340
340
 
341
- The tool stores local copies of the style guides in the `guides/` directory
342
- and the `guide` command uses these local files when they exist. The bundled
343
- copies are automatically synchronized weekly from the official
341
+ The tool stores local copies of the style guides in the `guides/` directory and
342
+ the `guide` command uses these local files when they exist. The bundled copies
343
+ are automatically synchronized weekly from the official
344
344
  [Google Style Guides](https://google.github.io/styleguide/) repository via
345
- GitHub Actions, and you can refresh your local cache at any time with the
346
- `sync` command.
345
+ GitHub Actions, and you can refresh your local cache at any time with the `sync`
346
+ command.
347
347
 
348
348
  You can override the default `guides/` directory by setting the
349
349
  `READABILITY_CACHE` environment variable. This is useful if you want to store
350
- the guides in a specific location or share them across different
351
- installations:
350
+ the guides in a specific location or share them across different installations:
352
351
 
353
352
  ```bash
354
353
  export READABILITY_CACHE=/path/to/my/guides
@@ -3,22 +3,21 @@
3
3
  A CLI tool that keeps code aligned with Google style conventions. It runs the
4
4
  right linters, formatters, and type checkers for your project with sensible
5
5
  defaults, and serves the official Google style guides in Markdown format. This
6
- is ideal for AI agents or developers who want consistent code quality checks
7
- and quick access to style conventions without browsing HTML pages.
6
+ is ideal for AI agents or developers who want consistent code quality checks and
7
+ quick access to style conventions without browsing HTML pages.
8
8
 
9
9
  ## Features
10
10
 
11
11
  - **Linting & Formatting**: A `check` command that automatically detects and
12
12
  runs relevant tools (Ruff, Pyrefly, Biome, Prettier, gofmt) for your project.
13
- - **Sensible Defaults**: Bundled Google-style configurations for Ruff,
14
- Pyrefly, and Biome are used automatically when a project does not define its
15
- own.
13
+ - **Sensible Defaults**: Google-style defaults for Ruff, Pyrefly, Biome, and
14
+ Prettier are used automatically when a project does not define its own.
16
15
  - **Style Guides**: A `guide` command that fetches the latest Google style
17
- guides (Python, Shell, C++, Java, JS/TS, Go, etc.) converted to Markdown,
18
- and outlines, addresses, and searches them by section rather than serving
19
- 200 KB to be read whole.
20
- - **Offline Mode**: Local caching of style guides for fast, offline access,
21
- kept fresh with a single `sync` command.
16
+ guides (Python, Shell, C++, Java, JS/TS, Go, etc.) converted to Markdown, and
17
+ outlines, addresses, and searches them by section rather than serving 200 KB
18
+ to be read whole.
19
+ - **Offline Mode**: Local caching of style guides for fast, offline access, kept
20
+ fresh with a single `sync` command.
22
21
 
23
22
  ## Quick Start
24
23
 
@@ -81,38 +80,39 @@ readability check . --fix
81
80
 
82
81
  ### Supported Tools
83
82
 
84
- | Tool | Supported Extensions | Config Needed | Fetched By |
85
- |------|----------------------|---------------|------------|
86
- | **Ruff** | `.py` | none, bundled | `uvx` |
87
- | **Pyrefly** | `.py` | none, bundled | `uvx` |
88
- | **Biome** | `.js`, `.ts`, `.jsx`, `.tsx`, `.json`, `.jsonc`, `.css`, `.html` | none, bundled | `npx` |
89
- | **Prettier** | `.js`, `.ts`, `.jsx`, `.tsx`, `.json`, `.css`, `.scss`, `.html`, `.md`, `.yml`, `.yaml` | `.prettierrc*` or `prettier.config.*` | `npx` |
90
- | **gofmt** | `.go` | `go.mod` | — |
83
+ | Tool | Supported Extensions | Config Needed | Fetched By |
84
+ | ------------ | --------------------------------------------------------------------------------------- | --------------------------------- | ---------- |
85
+ | **Ruff** | `.py` | none, bundled | `uvx` |
86
+ | **Pyrefly** | `.py` | none, bundled | `uvx` |
87
+ | **Biome** | `.js`, `.ts`, `.jsx`, `.tsx`, `.json`, `.jsonc`, `.css`, `.html` | none, bundled | `npx` |
88
+ | **Prettier** | `.js`, `.ts`, `.jsx`, `.tsx`, `.json`, `.css`, `.scss`, `.html`, `.md`, `.yml`, `.yaml` | none for Markdown, YAML, and SCSS | `npx` |
89
+ | **gofmt** | `.go` | `go.mod` | — |
91
90
 
92
91
  **You do not need to install any of these.** Each is resolved in order: a
93
- project-local install (`node_modules/.bin`, `.venv/bin`) first, then whatever
94
- is on your `PATH`, and only then fetched by `uvx` or `npx`. The runners cache
95
- what they download, so the fetch happens once per machine and later runs are
96
- served from disk and work offline.
92
+ project-local install (`node_modules/.bin`, `.venv/bin`) first, then whatever is
93
+ on your `PATH`, and only then fetched by `uvx` or `npx`. The runners cache what
94
+ they download, so the fetch happens once per machine and later runs are served
95
+ from disk and work offline.
97
96
 
98
- That keeps this package at ~4 MB rather than the ~54 MB it would take to
99
- carry Ruff and Pyrefly itself — a cost that would fall on everyone using only
100
- `guide`. An installed copy always wins over a fetched one, so a project that
101
- pinned a version is linted against the rules it chose.
97
+ That keeps this package at ~4 MB rather than the ~54 MB it would take to carry
98
+ Ruff and Pyrefly itself — a cost that would fall on everyone using only `guide`.
99
+ An installed copy always wins over a fetched one, so a project that pinned a
100
+ version is linted against the rules it chose.
102
101
 
103
102
  Fetched tools carry a minimum version, enough to understand the bundled
104
103
  configurations, and no maximum. A ceiling would freeze anyone who installed
105
- nothing at whatever was current when this package shipped, and tie its
106
- releases to Ruff's — which has published seventeen minor series, roughly one
107
- every six weeks. If you need a fixed version, install it: an installed tool
108
- always wins over a fetched one.
104
+ nothing at whatever was current when this package shipped, and tie its releases
105
+ to Ruff's — which has published seventeen minor series, roughly one every six
106
+ weeks. If you need a fixed version, install it: an installed tool always wins
107
+ over a fetched one.
109
108
 
110
- Set `UV_OFFLINE=1` to forbid fetching. A tool that then cannot be reached
111
- fails the run rather than passing it.
109
+ Set `UV_OFFLINE=1` to forbid fetching. A tool that then cannot be reached fails
110
+ the run rather than passing it.
112
111
 
113
112
  Ruff, Pyrefly, and Biome ship with bundled configurations, so they run on any
114
- file they handle without the project arranging anything. Prettier and gofmt
115
- bring no defaults here, so they wait until the project asks with a config file.
113
+ file they handle without the project arranging anything. Prettier does the same
114
+ for Markdown, YAML, and SCSS; a Prettier configuration opts its overlapping web
115
+ formats into Prettier too. Gofmt waits for a `go.mod` file.
116
116
 
117
117
  A tool that could not be reached at all is never skipped quietly:
118
118
 
@@ -127,8 +127,8 @@ $ readability check src/
127
127
  Error: Every tool for 1 path(s) is missing, so nothing was verified.
128
128
  ```
129
129
 
130
- That second case needs neither the tool nor a runner to be present, which is
131
- the state a container image is usually in.
130
+ That second case needs neither the tool nor a runner to be present, which is the
131
+ state a container image is usually in.
132
132
 
133
133
  `check` exits non-zero on findings, tool failures, and when every applicable
134
134
  tool is absent, so it can gate CI without a clean exit meaning required tools
@@ -151,27 +151,28 @@ if report.findings or report.failed or not report.ran:
151
151
  ```
152
152
 
153
153
  The returned `CheckReport` records only whether findings occurred and which
154
- tools ran, were skipped, or failed. Detailed tool findings are still written
155
- as each tool runs. Every path is validated before any tool runs, and a missing
156
- one raises `FileNotFoundError` rather than being misreported as a finding.
157
- Relative paths remain relative to the process working directory;
158
- `project_root` controls configuration discovery only.
154
+ tools ran, were skipped, or failed. Detailed tool findings are still written as
155
+ each tool runs. Every path is validated before any tool runs, and a missing one
156
+ raises `FileNotFoundError` rather than being misreported as a finding. Relative
157
+ paths remain relative to the process working directory; `project_root` controls
158
+ configuration discovery only.
159
159
 
160
160
  ### Default Configurations
161
161
 
162
162
  Ruff and Pyrefly defaults follow the
163
163
  [Google Python style guide](https://google.github.io/styleguide/pyguide.html):
164
- 80-column lines, Google docstrings, import ordering, and full type checking.
165
- The Biome default applies the 80-column lines and two-space indentation of the
164
+ 80-column lines, Google docstrings, import ordering, and full type checking. The
165
+ Biome default applies the 80-column lines and two-space indentation of the
166
166
  [Google JavaScript style guide](https://google.github.io/styleguide/jsguide.html)
167
- and enables Biome's recommended lint rules. These defaults apply only when the
168
- project does not define its own configuration. To override them, add a
169
- `[tool.ruff]` or `[tool.pyrefly]` section to your `pyproject.toml`, or a
170
- dedicated `ruff.toml`, `pyrefly.toml`, `biome.json`, or `biome.jsonc` any
171
- project-level configuration takes full precedence. The bundled Biome file
172
- intentionally uses only settings supported by both Biome 1.x and 2.x, so a
173
- project-local installation remains authoritative without creating a schema
174
- mismatch.
167
+ and enables Biome's recommended lint rules. Prettier wraps prose at 80 columns,
168
+ following the
169
+ [Google Markdown style guide](https://google.github.io/styleguide/docguide/style.html#character-line-limit).
170
+ These defaults apply only when the project does not define its own
171
+ configuration. To override them, add a `[tool.ruff]` or `[tool.pyrefly]` section
172
+ to your `pyproject.toml`, or a dedicated `ruff.toml`, `pyrefly.toml`,
173
+ `biome.json`, `biome.jsonc`, or Prettier configuration any project-level
174
+ configuration takes full precedence. The bundled Biome file requires Biome 2.5
175
+ or later, matching the version floor used by the fallback runner.
175
176
 
176
177
  ## Style Guides
177
178
 
@@ -197,17 +198,17 @@ readability guide python --full | grep -i f-string
197
198
 
198
199
  A guide can exceed 200 KB, so the outline is what a bare invocation prints:
199
200
  listing the sections and fetching the one you need beats reading the lot.
200
- Nothing is written to disk, which is what a coding agent should do rather
201
- than redirecting a guide into the repository it is working on — use a shell
202
- redirect if you do want a copy.
201
+ Nothing is written to disk, which is what a coding agent should do rather than
202
+ redirecting a guide into the repository it is working on — use a shell redirect
203
+ if you do want a copy.
203
204
 
204
- `REF` and `--full` cannot be combined; that is refused rather than resolved
205
- by a precedence rule you would have to know.
205
+ `REF` and `--full` cannot be combined; that is refused rather than resolved by a
206
+ precedence rule you would have to know.
206
207
 
207
208
  ### Navigating a Guide
208
209
 
209
- The outline gives each section an index to fetch it by, and flags the ones
210
- large enough to be worth knowing about first:
210
+ The outline gives each section an index to fetch it by, and flags the ones large
211
+ enough to be worth knowing about first:
211
212
 
212
213
  ```bash
213
214
  $ readability guide cpp
@@ -221,28 +222,28 @@ Google C++ Style Guide
221
222
  # 140 sections · print one: readability guide cpp 4.5.1
222
223
  ```
223
224
 
224
- Sizes appear only at 1200 words and above — the 4% of sections expensive
225
- enough that you would want warning. They include subsections, since that is
226
- what the reference returns. The trailing line goes to stderr, so it never
227
- contaminates a piped outline.
225
+ Sizes appear only at 1200 words and above — the 4% of sections expensive enough
226
+ that you would want warning. They include subsections, since that is what the
227
+ reference returns. The trailing line goes to stderr, so it never contaminates a
228
+ piped outline.
228
229
 
229
230
  A section reference can be any of the following:
230
231
 
231
- | Reference | Example |
232
- |-----------|---------|
233
- | Section index, as shown by the outline | `2.2.4` |
234
- | Heading text, case-insensitive, or its slug | `"function comments"` |
232
+ | Reference | Example |
233
+ | ------------------------------------------- | ---------------------- |
234
+ | Section index, as shown by the outline | `2.2.4` |
235
+ | Heading text, case-insensitive, or its slug | `"function comments"` |
235
236
  | A parent-scoped path, spaces around the `>` | `"Imports > Decision"` |
236
237
 
237
- Whole matches are preferred; a reference that matches nothing in full is
238
- retried as a substring of the heading text.
238
+ Whole matches are preferred; a reference that matches nothing in full is retried
239
+ as a substring of the heading text.
239
240
 
240
241
  Three of the shipped guides — Python, JavaScript, and Java — number their own
241
242
  sections, and those numbers are the index. A rule cited from the outline then
242
- matches the published guide exactly, including where the guide skips a
243
- number: the Python guide has no 2.15 at all, so its 2.16 is listed as 2.16
244
- rather than renumbered. The other eleven guides number nothing, so their
245
- index comes from each heading's position in the tree.
243
+ matches the published guide exactly, including where the guide skips a number:
244
+ the Python guide has no 2.15 at all, so its 2.16 is listed as 2.16 rather than
245
+ renumbered. The other eleven guides number nothing, so their index comes from
246
+ each heading's position in the tree.
246
247
 
247
248
  Either way the index is unique, which is what makes a repeated heading
248
249
  addressable — `Definition`, `Pros`, `Cons`, and `Decision` appear under every
@@ -265,8 +266,8 @@ Error: 'Decision' matches 19 headings in the 'python' guide. Repeat with one of:
265
266
 
266
267
  A guide discusses plenty that no heading is named after, so a reference that
267
268
  matches nothing is usually a vocabulary mismatch rather than a mistake. The
268
- sections that mention the words are reported, which turns the miss into the
269
- next command:
269
+ sections that mention the words are reported, which turns the miss into the next
270
+ command:
270
271
 
271
272
  ```bash
272
273
  $ readability guide python f-string
@@ -276,22 +277,21 @@ It appears in these sections:
276
277
  3.10.1 Python Style Rules > Strings > Logging
277
278
  ```
278
279
 
279
- This locates a rule; it does not search text. For that, pipe `--full` to
280
- `grep`, which you already know and which brings its own `-i`, `-A`, `-c` and
281
- the rest:
280
+ This locates a rule; it does not search text. For that, pipe `--full` to `grep`,
281
+ which you already know and which brings its own `-i`, `-A`, `-c` and the rest:
282
282
 
283
283
  ```bash
284
284
  readability guide python --full | grep -in -A2 "f-string"
285
285
  ```
286
286
 
287
- Content goes to stdout and diagnostics to stderr, so every form is safe to
288
- pipe. Headings inside fenced code blocks are ignored, which matters for the
289
- Shell and Python guides where `#` starts a comment.
287
+ Content goes to stdout and diagnostics to stderr, so every form is safe to pipe.
288
+ Headings inside fenced code blocks are ignored, which matters for the Shell and
289
+ Python guides where `#` starts a comment.
290
290
 
291
291
  ### Supported Languages
292
292
 
293
- Run `readability guide` with no language for the full list and which guides
294
- are cached:
293
+ Run `readability guide` with no language for the full list and which guides are
294
+ cached:
295
295
 
296
296
  ```bash
297
297
  $ readability guide
@@ -324,17 +324,16 @@ readability sync
324
324
 
325
325
  ### Offline Mode
326
326
 
327
- The tool stores local copies of the style guides in the `guides/` directory
328
- and the `guide` command uses these local files when they exist. The bundled
329
- copies are automatically synchronized weekly from the official
327
+ The tool stores local copies of the style guides in the `guides/` directory and
328
+ the `guide` command uses these local files when they exist. The bundled copies
329
+ are automatically synchronized weekly from the official
330
330
  [Google Style Guides](https://google.github.io/styleguide/) repository via
331
- GitHub Actions, and you can refresh your local cache at any time with the
332
- `sync` command.
331
+ GitHub Actions, and you can refresh your local cache at any time with the `sync`
332
+ command.
333
333
 
334
334
  You can override the default `guides/` directory by setting the
335
335
  `READABILITY_CACHE` environment variable. This is useful if you want to store
336
- the guides in a specific location or share them across different
337
- installations:
336
+ the guides in a specific location or share them across different installations:
338
337
 
339
338
  ```bash
340
339
  export READABILITY_CACHE=/path/to/my/guides
@@ -10,5 +10,10 @@
10
10
  "rules": {
11
11
  "recommended": true
12
12
  }
13
+ },
14
+ "html": {
15
+ "parser": {
16
+ "interpolation": true
17
+ }
13
18
  }
14
19
  }
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "readability-cli"
3
- version = "0.8.4"
3
+ version = "0.8.6"
4
4
  description = "A CLI to lint, format, and type-check code with Google-style defaults, and pull Google style guides in markdown format."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1288,27 +1288,27 @@ def _should_run_tool(
1288
1288
  Returns:
1289
1289
  True if the tool should run, False otherwise.
1290
1290
  """
1291
- # A tool shipping bundled defaults needs nothing from the project, so
1292
- # requiring a config file made those defaults unreachable by exactly the
1293
- # projects they exist for: an empty pyproject.toml was the difference
1294
- # between checking a file and checking nothing. Tools without defaults
1295
- # still wait to be asked, since there is no sane way to run them.
1296
- has_trigger = _bundled_config(tool["name"]).exists() or any(
1291
+ # Project configuration opts a tool into every extension it supports;
1292
+ # otherwise it handles only the formats with safe readability defaults.
1293
+ has_project_config = any(
1297
1294
  (project_root / t).exists() for t in tool["trigger"]
1298
1295
  )
1296
+ extensions = (
1297
+ tool["extensions"] if has_project_config else tool["default_extensions"]
1298
+ )
1299
1299
 
1300
1300
  # Files must match one of the tool's supported extensions.
1301
1301
  if path.is_file():
1302
- return has_trigger and path.suffix in tool["extensions"]
1302
+ return path.suffix in extensions
1303
1303
 
1304
- if not has_trigger:
1304
+ if not extensions:
1305
1305
  return False
1306
1306
 
1307
1307
  # A directory trigger alone is not evidence that the tool checked a file.
1308
1308
  # Several tools deliberately exit zero when nothing matches, which would
1309
1309
  # otherwise turn an unsupported-only tree into a reported clean result.
1310
1310
  return any(
1311
- candidate.is_file() and candidate.suffix in tool["extensions"]
1311
+ candidate.is_file() and candidate.suffix in extensions
1312
1312
  for candidate in path.rglob("*")
1313
1313
  )
1314
1314
 
@@ -1518,6 +1518,11 @@ def _get_tool_definitions(
1518
1518
  pyrefly = _tool_command("pyrefly", project_root)
1519
1519
  biome = _tool_command("biome", project_root)
1520
1520
  prettier = _tool_command("prettier", project_root)
1521
+ prettier_defaults = [
1522
+ "--print-width=80",
1523
+ "--prose-wrap=always",
1524
+ "--config-precedence=prefer-file",
1525
+ ]
1521
1526
 
1522
1527
  return [
1523
1528
  {
@@ -1554,6 +1559,7 @@ def _get_tool_definitions(
1554
1559
  ],
1555
1560
  "trigger": ["pyproject.toml", "ruff.toml", ".ruff.toml"],
1556
1561
  "extensions": [".py"],
1562
+ "default_extensions": [".py"],
1557
1563
  },
1558
1564
  {
1559
1565
  # Type checker only: it reports findings but cannot fix or format
@@ -1567,6 +1573,7 @@ def _get_tool_definitions(
1567
1573
  **({"cwd": project_root} if pyrefly_project_mode else {}),
1568
1574
  "trigger": ["pyproject.toml", "pyrefly.toml"],
1569
1575
  "extensions": [".py"],
1576
+ "default_extensions": [".py"],
1570
1577
  },
1571
1578
  {
1572
1579
  "name": "biome",
@@ -1611,18 +1618,30 @@ def _get_tool_definitions(
1611
1618
  ".css",
1612
1619
  ".html",
1613
1620
  ],
1621
+ "default_extensions": [
1622
+ ".js",
1623
+ ".ts",
1624
+ ".jsx",
1625
+ ".tsx",
1626
+ ".json",
1627
+ ".jsonc",
1628
+ ".css",
1629
+ ".html",
1630
+ ],
1614
1631
  },
1615
1632
  {
1616
1633
  "name": "prettier",
1617
1634
  "check_format": [
1618
1635
  *prettier,
1619
1636
  "--check",
1637
+ *prettier_defaults,
1620
1638
  "--no-error-on-unmatched-pattern",
1621
1639
  path_str,
1622
1640
  ],
1623
1641
  "format": [
1624
1642
  *prettier,
1625
1643
  "--write",
1644
+ *prettier_defaults,
1626
1645
  "--no-error-on-unmatched-pattern",
1627
1646
  path_str,
1628
1647
  ],
@@ -1648,6 +1667,7 @@ def _get_tool_definitions(
1648
1667
  ".yml",
1649
1668
  ".yaml",
1650
1669
  ],
1670
+ "default_extensions": [".md", ".yml", ".yaml", ".scss"],
1651
1671
  },
1652
1672
  {
1653
1673
  "name": "go fmt",
@@ -1655,6 +1675,7 @@ def _get_tool_definitions(
1655
1675
  "format": ["go", "fmt", path_str],
1656
1676
  "trigger": ["go.mod"],
1657
1677
  "extensions": [".go"],
1678
+ "default_extensions": [],
1658
1679
  },
1659
1680
  ]
1660
1681
 
@@ -1741,7 +1762,7 @@ def _tool_checked_files(
1741
1762
  if tool_name != "biome":
1742
1763
  return True
1743
1764
  output = f"{result.stdout or ''}\n{result.stderr or ''}"
1744
- # Biome 1.x and 2.x use these summaries for an unmatched target.
1765
+ # Biome 2.x uses these summaries for an unmatched target.
1745
1766
  zero_file_summaries = ("Checked 0 files", "Formatted 0 files")
1746
1767
  return not any(summary in output for summary in zero_file_summaries)
1747
1768
 
@@ -653,6 +653,7 @@ def test_bundled_default_configs_are_valid() -> None:
653
653
  "lineWidth": 80,
654
654
  }
655
655
  assert biome_config["linter"]["rules"]["recommended"] is True
656
+ assert biome_config["html"]["parser"]["interpolation"] is True
656
657
 
657
658
  # The ruff defaults follow the Google Python style guide
658
659
  ruff_config = tomllib.loads(_bundled_config("ruff").read_text())
@@ -1978,6 +1979,88 @@ def test_check_runs_biome_without_a_project_config(
1978
1979
  )
1979
1980
 
1980
1981
 
1982
+ @pytest.mark.parametrize("extension", (".md", ".yml", ".yaml", ".scss"))
1983
+ @patch("shutil.which")
1984
+ @patch("subprocess.run")
1985
+ def test_check_runs_prettier_for_unsupported_biome_formats_without_config(
1986
+ mock_run: MagicMock,
1987
+ mock_which: MagicMock,
1988
+ tmp_path: Path,
1989
+ extension: str,
1990
+ ) -> None:
1991
+ """Prettier covers its default formats without project configuration."""
1992
+ mock_which.side_effect = lambda name: name
1993
+ mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
1994
+
1995
+ runner = CliRunner()
1996
+ with runner.isolated_filesystem(temp_dir=tmp_path):
1997
+ Path(f"document{extension}").touch()
1998
+
1999
+ result = runner.invoke(cli, ["check", f"document{extension}"])
2000
+
2001
+ assert result.exit_code == 0
2002
+ commands = [invocation.args[0] for invocation in mock_run.call_args_list]
2003
+ assert len(commands) == 1
2004
+ assert commands[0][0] == "prettier"
2005
+
2006
+
2007
+ def test_prettier_uses_google_markdown_defaults(tmp_path: Path) -> None:
2008
+ """CLI defaults wrap Markdown while deferring to project configuration."""
2009
+ tools = {
2010
+ tool["name"]: tool
2011
+ for tool in _get_tool_definitions(Path("document.md"), tmp_path)
2012
+ }
2013
+
2014
+ for command_name in ("check_format", "format"):
2015
+ command = tools["prettier"][command_name]
2016
+ assert "--print-width=80" in command
2017
+ assert "--prose-wrap=always" in command
2018
+ assert "--config-precedence=prefer-file" in command
2019
+
2020
+
2021
+ @patch("shutil.which")
2022
+ @patch("subprocess.run")
2023
+ def test_check_runs_prettier_for_configless_markdown_directory(
2024
+ mock_run: MagicMock, mock_which: MagicMock, tmp_path: Path
2025
+ ) -> None:
2026
+ """Default applicability also works when checking a directory."""
2027
+ mock_which.side_effect = lambda name: name
2028
+ mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
2029
+
2030
+ runner = CliRunner()
2031
+ with runner.isolated_filesystem(temp_dir=tmp_path):
2032
+ Path("docs").mkdir()
2033
+ Path("docs/guide.md").touch()
2034
+
2035
+ result = runner.invoke(cli, ["check", "docs"])
2036
+
2037
+ assert result.exit_code == 0
2038
+ commands = [invocation.args[0] for invocation in mock_run.call_args_list]
2039
+ assert len(commands) == 1
2040
+ assert commands[0][0] == "prettier"
2041
+
2042
+
2043
+ @patch("shutil.which")
2044
+ @patch("subprocess.run")
2045
+ def test_prettier_project_config_opts_in_javascript(
2046
+ mock_run: MagicMock, mock_which: MagicMock, tmp_path: Path
2047
+ ) -> None:
2048
+ """A project configuration retains Prettier's existing broad coverage."""
2049
+ mock_which.side_effect = lambda name: name
2050
+ mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
2051
+
2052
+ runner = CliRunner()
2053
+ with runner.isolated_filesystem(temp_dir=tmp_path):
2054
+ Path(".prettierrc").touch()
2055
+ Path("script.js").touch()
2056
+
2057
+ result = runner.invoke(cli, ["check", "script.js"])
2058
+
2059
+ assert result.exit_code == 0
2060
+ commands = [invocation.args[0][0] for invocation in mock_run.call_args_list]
2061
+ assert commands.count("prettier") == 1
2062
+
2063
+
1981
2064
  @patch("shutil.which")
1982
2065
  @patch("subprocess.run")
1983
2066
  def test_bundled_tools_do_not_claim_an_unsupported_directory(
@@ -1989,7 +2072,7 @@ def test_bundled_tools_do_not_claim_an_unsupported_directory(
1989
2072
 
1990
2073
  runner = CliRunner()
1991
2074
  with runner.isolated_filesystem(temp_dir=tmp_path):
1992
- Path("notes.md").touch()
2075
+ Path("notes.txt").touch()
1993
2076
 
1994
2077
  result = runner.invoke(cli, ["check", "."])
1995
2078
 
@@ -2023,7 +2106,7 @@ def test_biome_zero_file_result_is_not_reported_as_clean(
2023
2106
  Path(".gitignore").write_text("node_modules/\n")
2024
2107
  Path("node_modules/example").mkdir(parents=True)
2025
2108
  Path("node_modules/example/index.js").touch()
2026
- Path("README.md").touch()
2109
+ Path("README.txt").touch()
2027
2110
 
2028
2111
  for extra_args, expected_calls in (([], 2), (["--fix"], 3)):
2029
2112
  mock_run.side_effect = (
@@ -194,7 +194,7 @@ wheels = [
194
194
 
195
195
  [[package]]
196
196
  name = "readability-cli"
197
- version = "0.8.4"
197
+ version = "0.8.6"
198
198
  source = { editable = "." }
199
199
  dependencies = [
200
200
  { name = "beautifulsoup4" },
File without changes