taste-lint 0.0.1 → 0.0.4
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 +32 -111
- package/data/{rules → rule-drafts}/craft/craft-affordance-mismatch.yaml +2 -1
- package/data/{rules → rule-drafts}/craft/craft-virtualize-large-lists.yaml +2 -1
- package/data/rules/typography/typography-title-case-heading.yaml +6 -4
- package/dist/cli.js +291 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -14
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,141 +1,62 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Taste Lint
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Catch AI slop before you ship.**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Scan your project with local checks and [Jev by TypeSafe AI](https://docs.typesafe.ai/introduction).
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.npmjs.com/package/taste-lint"><img alt="npm version" src="https://img.shields.io/npm/v/taste-lint?style=flat&colorA=000000&colorB=000000" /></a>
|
|
11
|
+
<a href="https://github.com/mblode/taste-lint/blob/main/LICENSE.md"><img alt="MIT license" src="https://img.shields.io/npm/l/taste-lint?style=flat&colorA=000000&colorB=000000" /></a>
|
|
12
|
+
</p>
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
</div>
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
## Install
|
|
14
17
|
|
|
15
18
|
```bash
|
|
16
|
-
|
|
17
|
-
export AI_GATEWAY_API_KEY="your-vercel-ai-gateway-key"
|
|
18
|
-
|
|
19
|
-
# Run from your project directory
|
|
20
|
-
taste-lint scan .
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
No taste-lint account or configuration file is required. Model usage is billed to your Vercel AI Gateway account. Taste-lint reads the key from your environment and sends selected text and rule context directly to the gateway.
|
|
24
|
-
|
|
25
|
-
Preview scope and estimated cost with `taste-lint scan . --dry-run`. Try local checks without a key using `taste-lint scan . --mechanical-only`. The default scan focuses on product interfaces; use `--profile writing` for documentation or `--profile instructions` for agent instructions.
|
|
26
|
-
|
|
27
|
-
Semantic rules remain advisory until calibrated against independent holdout evidence. Mechanical checks can fail a run immediately.
|
|
28
|
-
|
|
29
|
-
## Where the rules come from
|
|
30
|
-
|
|
31
|
-
[Agent Skills](https://github.com/mblode/agent-skills) provides reusable instructions for coding agents. [Taste Training](https://blode.co/taste-training) teaches people to spot and fix problems in typography, copy, interaction, and motion. Taste-lint turns applicable guidance into repeatable checks, with source references attached to individual rules.
|
|
32
|
-
|
|
33
|
-
## Example findings
|
|
34
|
-
|
|
35
|
-
```text
|
|
36
|
-
PASS: 161 active rules in data/rules
|
|
37
|
-
|
|
38
|
-
notes.md
|
|
39
|
-
[MINOR] typography-straight-quotes (p=1.00) notes.md:3:1
|
|
40
|
-
Straight quotes in rendered copy
|
|
41
|
-
2 matches: "'", "'"
|
|
42
|
-
Fix: Replace with the matching curly mark. Opening after whitespace or at the start, closing otherwise; an apostrophe is always the right single quote.
|
|
43
|
-
[MINOR?] copywriting-claim-without-evidence (p=0.95) notes.md:3:1
|
|
44
|
-
Quality claimed, nothing the reader could check
|
|
45
|
-
3 matches: "fast", "powerful", "seamless"; p=0.95
|
|
46
|
-
Fix: Replace the adjective with the mechanism, the number or the standard it stands for. If none exists, cut the sentence.
|
|
47
|
-
|
|
48
|
-
Units: 3 | Rules: 161 | Act: 1 | Review: 4 | Unknown: 0
|
|
49
|
-
Jev: 2 requests, 0 cached answers, 7238 input tokens, $0.0003
|
|
50
|
-
FAIL - 1 finding in the act band
|
|
19
|
+
npx taste-lint@latest init
|
|
51
20
|
```
|
|
52
21
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- **Severity:** how bad the finding is if real, `major` or `minor`. Set by the rule, never by the model.
|
|
56
|
-
- **Band:** how sure the tool is. `act` fails the run (mechanical hits, or Jev at or above the rule's act threshold), `review` prints a note with a `?`, below that is silent.
|
|
57
|
-
- **Cost:** eligible questions about one unit are batched into requests. Preview estimated cost with `--dry-run`; runs report usage and reuse cached answers. Current rates are listed in the [Vercel AI Gateway model catalog](https://vercel.com/ai-gateway/models).
|
|
58
|
-
|
|
59
|
-
## Rule packs
|
|
60
|
-
|
|
61
|
-
- **Typography:** straight quotes, dashes, ellipses, primes and units from `typography-audit`, plus size, weight, tracking and line-height checks over resolved Tailwind classes or computed styles.
|
|
62
|
-
- **Copywriting:** claims without evidence, vague errors, friction CTAs, hedges and register shifts from `docs-writing` and the ui-design copy guideline, and Every's published AI-tell checker: 19 of its 21 questions, one rule each, with phrase candidates from the MIT `cw-ai-check` skill where it has them. Not ported: `uniform_cadence` (sentence-length arithmetic) and `formatting_overuse` (needs headings and bullets a paragraph never sees). The two authorship verdicts are excluded on purpose: taste-lint reports defects, not authorship.
|
|
63
|
-
- **Interaction and craft:** the static checks of `ui-design/rules` as whole-file patterns (focus traps, error and empty states, target size, i18n, lazy loading) plus the shadcn/lint class hygiene rules (raw palette colours, arbitrary values, interpolated class strings).
|
|
64
|
-
- **Motion and product:** the `ui-animation` flag-on-sight table (ease-in, linear easing, transitions over 300ms, `transition-all`, entrances from scale zero, no reduced-motion variant) and the two deterministic `product-design` rules.
|
|
22
|
+
Requires Node 24.11 or later. Run from your project directory to install locally and add check scripts.
|
|
65
23
|
|
|
66
|
-
|
|
24
|
+
## Quickstart
|
|
67
25
|
|
|
68
|
-
|
|
26
|
+
Create a [Vercel AI Gateway key](https://vercel.com/docs/ai-gateway/authentication-and-byok/api-keys). From your project directory, set your key and run:
|
|
69
27
|
|
|
70
28
|
```bash
|
|
71
|
-
|
|
29
|
+
export AI_GATEWAY_API_KEY="your-vercel-ai-gateway-key"
|
|
30
|
+
npm run taste
|
|
72
31
|
```
|
|
73
32
|
|
|
74
|
-
|
|
33
|
+
No account or config for taste-lint. AI checks send selected text and rule context to Vercel AI Gateway, billed to your account. Answers are cached for repeat runs.
|
|
75
34
|
|
|
76
|
-
|
|
35
|
+
Run `npm run check:taste` for local checks without a key. Use your package manager in place of npm. Add `--agent` to init for agent instructions, or `--dry-run` to preview setup.
|
|
77
36
|
|
|
78
|
-
|
|
79
|
-
import { runLint } from "taste-lint";
|
|
37
|
+
## What it checks
|
|
80
38
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
39
|
+
- **Product interfaces:** copy, typography, interaction, and motion in JSX, TSX, and CSS.
|
|
40
|
+
- **Writing:** Markdown, MDX, and READMEs with `--profile writing`.
|
|
41
|
+
- **Agent instructions:** AGENTS.md and skills with `--profile instructions`.
|
|
84
42
|
|
|
85
|
-
|
|
43
|
+
Rules draw on [Agent Skills](https://github.com/mblode/agent-skills) and [Taste Training](https://blode.co/taste-training). Local checks handle measurable rules. Jev judges meaning and returns probabilities. Uncalibrated AI rules stay advisory; active findings can fail a run.
|
|
86
44
|
|
|
87
|
-
##
|
|
45
|
+
## Useful options
|
|
88
46
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
47
|
+
| Option | What it does |
|
|
48
|
+
| ------------------- | ---------------------------------------------------- |
|
|
49
|
+
| `--dry-run` | Preview scope and estimated cost without model calls |
|
|
50
|
+
| `--mechanical-only` | Run local checks without an API key |
|
|
51
|
+
| `--output json` | Save findings for scripts and agents |
|
|
52
|
+
| `--output sarif` | Export findings for code review tools |
|
|
92
53
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
## Options
|
|
96
|
-
|
|
97
|
-
| Flag | Default | Description |
|
|
98
|
-
| --- | --- | --- |
|
|
99
|
-
| `--dry-run` | | Plan and print units, requests and estimated cost without calling Jev |
|
|
100
|
-
| `--mechanical-only` | | Skip every Jev-backed rule; no key needed |
|
|
101
|
-
| `--only <ids>` | | Comma-separated rule ids |
|
|
102
|
-
| `--exclude <globs>` | | Comma-separated globs to skip, added to `taste-lint.config.json` |
|
|
103
|
-
| `--fail-on <severity>` | `minor` | Lowest severity that fails the run |
|
|
104
|
-
| `--fix` | | Apply deterministic fixes (curly quotes, ellipsis, multiplication sign, unit spaces) to act-band findings |
|
|
105
|
-
| `--output <format>` | `tty` | `tty`, `json` or `sarif` |
|
|
106
|
-
| `--url <url>` | | Lint a rendered page through style-capture |
|
|
107
|
-
|
|
108
|
-
`taste-lint eval` scores every rule against its labelled corpus (precision, recall, Wilson intervals, a calibration table) and `taste-lint tune` picks act thresholds from the dev split, promoting a rule only when the fixed threshold also clears the precision lower bound on an independent holdout. Both label classes, enough evaluated items, complete scoring, and source/text separation are required. `taste-lint eval coverage` reports class balance and split leakage without API calls; add `--output json` to coverage or evaluation for structured results.
|
|
54
|
+
Run `taste-lint scan --help` for all options. See the [scan guide](https://github.com/mblode/taste-lint/blob/main/docs/SCANS.md), [usage reference](https://github.com/mblode/taste-lint/blob/main/docs/USAGE.md), and [changelog](https://github.com/mblode/taste-lint/blob/main/CHANGELOG.md) for more.
|
|
109
55
|
|
|
110
56
|
## License
|
|
111
57
|
|
|
112
58
|
MIT
|
|
113
59
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
The default text report summarizes scope, top rules and up to 30 examples. Use `--verbose` for the full list; every completed or incomplete run also saves a complete JSON report and prints its path. JSON v1 keeps the original grouped `findings`; `ruleFindings` preserves every rule's evidence and `coverage` counts eligible checks. `summary.failing` respects `--fail-on`, and determines the exit code together with run completeness.
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
taste-lint lint --root ../my-site apps/web --dry-run
|
|
120
|
-
taste-lint lint --root ../my-site apps/web --progress --output json > audit.json
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Progress goes to stderr. A fully cached run needs no API key. An incomplete report includes a retry command that reuses successful answers. Cost is reported from known usage, excluding any unreported provider billing for failures.
|
|
124
|
-
|
|
125
|
-
Configuration is optional at `taste-lint.config.json` in the scan root. Its editor schema ships at `node_modules/taste-lint/data/config.schema.json`. Unknown fields and invalid types fail before evaluation. Select the scope explicitly: documentation and agent instructions remain included when you request a whole repository.
|
|
126
|
-
|
|
127
|
-
```json
|
|
128
|
-
{
|
|
129
|
-
"$schema": "./node_modules/taste-lint/data/config.schema.json",
|
|
130
|
-
"exclude": ["docs/archive/**"],
|
|
131
|
-
"docTypes": [
|
|
132
|
-
{ "glob": "apps/web/content/writing/**/*.mdx", "type": "explanation" }
|
|
133
|
-
]
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
See [TypeSafe contracts](docs/TYPESAFE.md) and the [review-rule evaluation](docs/evaluations/blode-co-review.md) for the implementation evidence and current calibration limits.
|
|
138
|
-
|
|
139
|
-
See [skill packs](docs/SKILL-PACKS.md) for repository checks, architecture policy, personal-writing context, and source discovery.
|
|
60
|
+
---
|
|
140
61
|
|
|
141
|
-
|
|
62
|
+
Crafted by [<img src="https://blode.co/avatar-circle.png" width="20" align="top" alt="" />](https://blode.co) [Matthew Blode](https://blode.co)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# Disabled after real-project review: this pattern lacks evidence of a defect.
|
|
1
2
|
# Ported from mblode/agent-skills skills/ui-design/rules/slop-affordance-mismatch.md by scripts/port-rules.ts.
|
|
2
3
|
# Keys listed in handWritten are preserved on re-run; everything else is regenerated.
|
|
3
4
|
id: craft-affordance-mismatch
|
|
@@ -20,4 +21,4 @@ mechanical:
|
|
|
20
21
|
severity: minor
|
|
21
22
|
fix:
|
|
22
23
|
hint: A card lifts on hover, the cursor turns into a hand, the border brightens, and clicking does nothing. The user has already committed to the click by the time they learn it was a decoration; they click again, assume the page is broken or slow, and then trust the next hover state less. This is worse t
|
|
23
|
-
status:
|
|
24
|
+
status: draft
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# Disabled after real-project review: this pattern lacks evidence of a defect.
|
|
1
2
|
# Ported from mblode/agent-skills skills/ui-design/rules/perf-virtualize-large-lists.md by scripts/port-rules.ts.
|
|
2
3
|
# Keys listed in handWritten are preserved on re-run; everything else is regenerated.
|
|
3
4
|
id: craft-virtualize-large-lists
|
|
@@ -21,4 +22,4 @@ mechanical:
|
|
|
21
22
|
severity: minor
|
|
22
23
|
fix:
|
|
23
24
|
hint: Large lists (roughly >50 visible items) should use virtualization/windowing. Rendering the whole dataset stalls scrolling and grows memory with the list.
|
|
24
|
-
status:
|
|
25
|
+
status: draft
|
|
@@ -14,13 +14,13 @@ scope:
|
|
|
14
14
|
- "**/README.md"
|
|
15
15
|
unit: [heading, jsx-text, attr-string]
|
|
16
16
|
mechanical:
|
|
17
|
-
#
|
|
18
|
-
regex: "
|
|
19
|
-
flags:
|
|
17
|
+
# Whole-label candidate: embedded capitalised names in a sentence are not Title Case.
|
|
18
|
+
regex: "^(?:[A-Z][a-z]+|of|the|and|for|to|a|in|on|with)(?: (?:[A-Z][a-z]+|of|the|and|for|to|a|in|on|with)){2,}[?!:.]?$"
|
|
19
|
+
flags: u
|
|
20
20
|
question:
|
|
21
21
|
context: [role]
|
|
22
22
|
instructions: >-
|
|
23
|
-
The TEXT is a heading, button label, tab or field label. Return true when most of its words start with a capital letter because it is set in Title Case, not because those words are names of people, products, places or organisations. Return false when the capitals are proper nouns or acronyms, when the TEXT is a single word, or when it is an ordinary sentence with one capital at the start.
|
|
23
|
+
Evaluate capitalisation of ordinary words only. Ignore the initial word and all words belonging to a product, person, place or organisation name. An imperative followed by a product name is sentence case, even when every word begins with a capital. If the supplied text does not establish that a capitalised phrase is ordinary words rather than a name, return false. The TEXT is a heading, button label, tab or field label. Return true when most of its words start with a capital letter because it is set in Title Case, not because those words are names of people, products, places or organisations. Return false when the capitals are proper nouns or acronyms, when the TEXT is a single word, or when it is an ordinary sentence with one capital at the start.
|
|
24
24
|
criteria:
|
|
25
25
|
true:
|
|
26
26
|
what: Ordinary words capitalised as a heading style.
|
|
@@ -34,6 +34,8 @@ question:
|
|
|
34
34
|
- "Connect Google Drive and Microsoft Teams"
|
|
35
35
|
- "Manage your team settings"
|
|
36
36
|
- "New York office hours"
|
|
37
|
+
- "Open Cedar Notes"
|
|
38
|
+
- "Ask River Desk"
|
|
37
39
|
thresholds: { review: 0.4, act: 0.75 }
|
|
38
40
|
severity: minor
|
|
39
41
|
fix:
|