@drkmttr/adhere 0.21.2 → 0.24.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 +28 -1552
- package/package.json +10 -7
- package/presets/README.md +9 -1
- package/presets/alchemy/runtime/bind-tag-not-implementation/RULE.md +1 -0
- package/presets/effect/data/brand-meaningful-primitives/RULE.md +1 -0
- package/presets/effect/data/variants-are-tagged-unions/RULE.md +1 -0
- package/presets/typescript/types/derive-dont-restate/RULE.md +1 -0
- package/skills/adhere/SKILL.md +214 -30
- package/skills/adhere-fix/SKILL.md +1 -1
- package/skills/adhere-setup/SKILL.md +49 -16
- package/src/cli.ts +216 -25
- package/src/config.ts +73 -4
- package/src/create/ask.ts +255 -9
- package/src/create/examples.ts +94 -0
- package/src/create/look.ts +62 -19
- package/src/create/oxlint-trial.ts +464 -0
- package/src/create/oxlint.ts +1012 -0
- package/src/create/trial-review.ts +625 -0
- package/src/create/trial.ts +663 -0
- package/src/create/wizard.ts +131 -95
- package/src/filters.ts +86 -0
- package/src/init.ts +169 -40
- package/src/install.ts +295 -93
- package/src/instructions.ts +34 -12
- package/src/markdown.ts +21 -2
- package/src/oxlint/catalog.json +7844 -0
- package/src/oxlint/catalog.ts +72 -0
- package/src/oxlint/shared.ts +140 -0
- package/src/readme-guide.ts +431 -0
- package/src/readme.ts +291 -0
- package/src/services/AdhereConfig.ts +36 -3
- package/src/services/AuditCache.ts +176 -169
- package/src/services/SourceWalker.ts +12 -5
- package/src/wording.ts +3 -3
- package/src/workflows/audit.ts +40 -7
- package/src/workflows/format.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drkmttr/adhere",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Lints a repository against the rules in .adhere/, judged by Jev.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,11 +34,14 @@
|
|
|
34
34
|
"typecheck": "tsc --noEmit && bun run lint",
|
|
35
35
|
"build": "bun build --compile --minify --sourcemap --define ADHERE_BUILD=\"\\\"$(git describe --tags --dirty --always)\\\"\" src/main.ts --asset ./presets --outfile dist/adhere",
|
|
36
36
|
"build:npm": "bun scripts/npm-packages.ts build",
|
|
37
|
+
"oxlint:catalog": "bun scripts/oxlint-catalog.ts",
|
|
38
|
+
"oxlint:catalog:check": "bun scripts/oxlint-catalog.ts --check",
|
|
37
39
|
"release": "release-it",
|
|
38
40
|
"prepare": "vp config"
|
|
39
41
|
},
|
|
40
42
|
"dependencies": {
|
|
41
|
-
"effect": "4.0.3"
|
|
43
|
+
"effect": "4.0.3",
|
|
44
|
+
"mdast-util-from-markdown": "^2.1.0"
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
44
47
|
"@effect/platform-bun": "4.0.3",
|
|
@@ -90,10 +93,10 @@
|
|
|
90
93
|
"--force"
|
|
91
94
|
],
|
|
92
95
|
"optionalDependencies": {
|
|
93
|
-
"@drkmttr/adhere-darwin-arm64": "0.
|
|
94
|
-
"@drkmttr/adhere-darwin-x64": "0.
|
|
95
|
-
"@drkmttr/adhere-linux-arm64": "0.
|
|
96
|
-
"@drkmttr/adhere-linux-x64": "0.
|
|
97
|
-
"@drkmttr/adhere-win32-x64": "0.
|
|
96
|
+
"@drkmttr/adhere-darwin-arm64": "0.24.0",
|
|
97
|
+
"@drkmttr/adhere-darwin-x64": "0.24.0",
|
|
98
|
+
"@drkmttr/adhere-linux-arm64": "0.24.0",
|
|
99
|
+
"@drkmttr/adhere-linux-x64": "0.24.0",
|
|
100
|
+
"@drkmttr/adhere-win32-x64": "0.24.0"
|
|
98
101
|
}
|
|
99
102
|
}
|
package/presets/README.md
CHANGED
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Every rule in adhere's built-in presets is listed below, with a one-line
|
|
4
4
|
summary. Each id links to the full rule, and is the id that reports,
|
|
5
|
-
`overrides`, and `adhere-ignore` comments use. [Presets](
|
|
5
|
+
`overrides`, and `adhere-ignore` comments use. [Presets](https://dm.sh/adhere/presets/)
|
|
6
6
|
says how to name a preset or one of its topics.
|
|
7
7
|
|
|
8
|
+
Some rules declare the context they need with `reads`, such as
|
|
9
|
+
`["symbols", "references"]` for a rule that compares declarations across
|
|
10
|
+
files. Enable `typeAware: true` or `--type-aware` for the compiler's
|
|
11
|
+
`symbols` and `references`. Without it, a bundled rule that declares either is
|
|
12
|
+
left out of the run, so a preset never judges with less context than its rules
|
|
13
|
+
declare. A project's own rule is different: it still runs, and lint warns that
|
|
14
|
+
it may be less accurate until a declared read is enabled.
|
|
15
|
+
|
|
8
16
|
## typescript
|
|
9
17
|
|
|
10
18
|
- **Source.** The TypeScript Handbook and the Google TypeScript Style Guide.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: When a runtime is split into a Tag class and a make Layer, other runtimes must import and bind only the Tag, and only the Stack provides the Layer; another Worker or Function must never import or provide that implementation Layer.
|
|
3
|
+
reads: ["symbols", "references"]
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
## Must
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: A primitive with semantic meaning in a domain type, such as an id, email, URL, or port in a service's contract, an event, or a stored record, must be declared as a branded schema, never as a bare string or number.
|
|
3
|
+
reads: ["symbols", "references"]
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
Two ids of different things are both strings, so nothing stops one being
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Variants of a domain type, alternatives that carry fields and that a service returns, stores, or decodes, must be tagged schemas such as Schema.TaggedClass or Schema.TaggedStruct members of a Schema.Union, never TypeScript object types joined by a tag field.
|
|
3
|
+
reads: ["symbols", "references"]
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
A tagged schema decodes each variant at the boundary and lets `Match.tag`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: A type that describes the same data as another type, a schema, a function, or a constant should be derived from it, with a utility type such as `Pick`, with `typeof` or `keyof`, or as a schema's inferred type, and should not restate its fields or values by hand.
|
|
3
3
|
level: warning
|
|
4
|
+
reads: ["symbols", "references"]
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
## Should
|
package/skills/adhere/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: adhere
|
|
3
|
-
description: Reference for adhere, the linter for coding conventions a normal linter cannot check, judged by TypeSafe AI's Jev. Covers its commands, the Markdown rule format and how to word a rule, the config, presets, shared
|
|
3
|
+
description: Reference for adhere, the linter for coding conventions a normal linter cannot check, judged by TypeSafe AI's Jev. Covers its commands, the Markdown rule format and how to word a rule, the config, presets, shared repos and lint packages, comments and suppressions, the cache, reading a report, and tuning a noisy rule. Use when writing or editing adhere rules, configuring adhere, reading its findings, or answering questions about it. To set adhere up in a repo, use adhere-setup; to fix findings, adhere-fix.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# adhere
|
|
@@ -19,11 +19,13 @@ first rules, first lint, and CI, and adhere-fix verifies and fixes findings.
|
|
|
19
19
|
## Commands
|
|
20
20
|
|
|
21
21
|
```sh
|
|
22
|
-
adhere init [--force] #
|
|
23
|
-
adhere init --shared org/repo #
|
|
22
|
+
adhere init [--force] # config, tsconfig, two example rules, and the dev dependency
|
|
23
|
+
adhere init --shared org/repo # rules, instructions, lint package, CI, and an alchemy stack
|
|
24
|
+
adhere postinstall # render .adhere/instructions/**/*.md into AGENTS.md
|
|
24
25
|
adhere login # save a TypeSafe AI API key; TYPESAFE_API_KEY takes precedence
|
|
25
26
|
adhere validate # rule wording, rules a linter could check, contradictions
|
|
26
|
-
adhere rules create [idea] #
|
|
27
|
+
adhere rules create [idea] # native rule, approved plugin, or rule file; org PR
|
|
28
|
+
adhere rules readme [org/repo] # refresh a shared repo README's managed guide
|
|
27
29
|
adhere lint # judge the working directory; exit 1 on an error finding
|
|
28
30
|
adhere lint --limit 0 # compiler-free counts/cache plan; no budgeting
|
|
29
31
|
adhere lint --limit 100 # first 100 files needing requests; all pending rules per file
|
|
@@ -33,16 +35,17 @@ adhere lint --preset effect # add a built-in rule set; the config becomes o
|
|
|
33
35
|
adhere lint --filter 'src/**' # read only matching files; repeat, and ! to leave out
|
|
34
36
|
adhere lint --threshold 0.9 # report at another threshold from the cache; judges nothing again
|
|
35
37
|
adhere lint --deny-warnings # fail on warnings too
|
|
36
|
-
adhere list org/repo # the rules in a GitHub repo's .adhere/rules
|
|
37
|
-
adhere install org/repo[/topic[/rule]][#ref]
|
|
38
|
+
adhere list org/repo # the rules in a GitHub repo's .adhere/rules/, with descriptions
|
|
39
|
+
adhere install org/repo[/topic[/rule]][#ref] [--all] # copy instructions and rules into .adhere/
|
|
38
40
|
adhere skill [docs|setup|fix] # print a skill
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
`adhere <command> --help` lists every flag. Version, help, skills, `validate`,
|
|
42
44
|
and planning with `--limit 0` do not start the native compiler. Default `lint`
|
|
43
|
-
is compiler-free
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
is compiler-free, and leaves out bundled preset rules that declare native
|
|
46
|
+
reads. With `typeAware: true` or `--type-aware`, it includes them and starts the
|
|
47
|
+
compiler only during execution, after confirmation, for pending judgments or
|
|
48
|
+
cached findings still needing location.
|
|
46
49
|
|
|
47
50
|
`--limit` now counts files, not rule checks; use the same flag without an extra
|
|
48
51
|
unit selector. Each request-bearing file consumes one slot for all its pending
|
|
@@ -59,11 +62,11 @@ native-context caveat before execution.
|
|
|
59
62
|
A repo's rules live in `.adhere/rules/`, a directory per rule holding a
|
|
60
63
|
`RULE.md`, or a `RULE.ts` (below). The directory's path is the rule id:
|
|
61
64
|
`.adhere/rules/data/brand-ports/RULE.md` is `data/brand-ports`. Other files in
|
|
62
|
-
a rule's directory are not rules
|
|
63
|
-
|
|
64
|
-
A `.adhere/rules/` in a subdirectory holds
|
|
65
|
-
subtree only, and the most specific rule with an id wins.
|
|
66
|
-
config in `.adhere/` are not rules.
|
|
65
|
+
a rule's directory are not rules, and only `RULE.md` or `RULE.ts` in a rule's
|
|
66
|
+
directory defines one: other Markdown in `.adhere/`, such as
|
|
67
|
+
`.adhere/instructions/`, is ignored. A `.adhere/rules/` in a subdirectory holds
|
|
68
|
+
rules for that subtree only, and the most specific rule with an id wins.
|
|
69
|
+
`cache/` and the config in `.adhere/` are not rules.
|
|
67
70
|
|
|
68
71
|
````md
|
|
69
72
|
---
|
|
@@ -118,12 +121,31 @@ Front matter besides `description`, all optional:
|
|
|
118
121
|
- `tests: only` for a rule about tests, `tests: include` for one that holds in
|
|
119
122
|
tests too. Otherwise rules skip `.test` and `.spec` files and files under
|
|
120
123
|
`test/`, `tests/`, `__tests__/`, and `fixtures/`.
|
|
124
|
+
- `include: {"paths":["src/**"],"content":["from\\s+[\"']effect[\"']"]}`
|
|
125
|
+
and `exclude: {"paths":["**/generated/**"],"content":["@generated"]}`,
|
|
126
|
+
JSON objects on one line, filter files locally before requests or cached
|
|
127
|
+
findings. TypeScript rules use ordinary objects with the same fields.
|
|
128
|
+
Paths are repository-root-relative globs, even for nested rules. Content
|
|
129
|
+
patterns are JavaScript regex source strings without delimiters or flags,
|
|
130
|
+
matched against original text including comments. Invalid regex refuses
|
|
131
|
+
loading. Entries within each list are alternatives; every nonempty include
|
|
132
|
+
list must match, and any exclude match vetoes the file. Empty lists impose
|
|
133
|
+
no restriction. Existing scope, test settings, and ignores still apply.
|
|
134
|
+
An import regex is not import resolution: check comments, alternative import
|
|
135
|
+
forms, and re-exports before narrowing a rule and silently missing violations.
|
|
121
136
|
- `appliesTo: ["a declared union type or schema"]` and
|
|
122
137
|
`excludeIf: ["a type that mirrors a third-party format"]`, JSON arrays on
|
|
123
138
|
one line, scope a rule without widening its description: a finding stands
|
|
124
139
|
only when Jev says the code that breaks the rule is as every `appliesTo`
|
|
125
140
|
describes and as no `excludeIf` does.
|
|
126
141
|
|
|
142
|
+
- `reads: ["symbols", "references"]` declares the context the rule needs, a
|
|
143
|
+
JSON array on one line. It enables nothing: the config's `reads` and
|
|
144
|
+
`typeAware` still choose what this run sends. A project's own rule still
|
|
145
|
+
runs and lint warns that it may be less accurate until a declared read is
|
|
146
|
+
enabled; a bundled preset rule that declares either native read is left out
|
|
147
|
+
unless `typeAware` is on.
|
|
148
|
+
|
|
127
149
|
A rule's directory can hold a `RULE.ts` instead, which default-exports
|
|
128
150
|
`defineRule({...})` from `@drkmttr/adhere`, with the fields a config's inline
|
|
129
151
|
rule takes:
|
|
@@ -174,9 +196,13 @@ Opting in with `typeAware: true` or `--type-aware` enables both `symbols` and
|
|
|
174
196
|
`references`, regardless of `reads`. Legacy native-reader entries in `reads`
|
|
175
197
|
alone do not enable the compiler; leaving them out does not disable it when
|
|
176
198
|
type awareness is on. Code alone requires `reads: []` and type awareness off.
|
|
177
|
-
Presets do not own or override this selection. A rule
|
|
178
|
-
Markdown front matter, `defineRule`, or an inline rule
|
|
179
|
-
|
|
199
|
+
Presets do not own or override this selection. A rule may declare what it
|
|
200
|
+
needs with `reads`, in Markdown front matter, `defineRule`, or an inline rule;
|
|
201
|
+
the declaration enables nothing. A bundled preset rule that declares `symbols`
|
|
202
|
+
or `references` is left out unless `typeAware` is on, so it is never judged
|
|
203
|
+
with less than it declares. A project's own rule still runs, and a declared
|
|
204
|
+
read the run does not enable is a warning that it may be less accurate until
|
|
205
|
+
enabled. The context maps are:
|
|
180
206
|
|
|
181
207
|
- `workspacePackages`: only packages matched by this file's literal imports,
|
|
182
208
|
as `{ canonicalPackageName: directory }`. Subpaths match the owning package;
|
|
@@ -302,17 +328,170 @@ https://github.com/darkmatter/adhere/tree/main/presets. A preset leaves out
|
|
|
302
328
|
what that ecosystem's linters check exactly. Quiet a preset rule with
|
|
303
329
|
`overrides` rather than copying it.
|
|
304
330
|
|
|
305
|
-
##
|
|
331
|
+
## Linter-checkable rules
|
|
332
|
+
|
|
333
|
+
A convention a linter can decide exactly does not belong in a `RULE.md`.
|
|
334
|
+
`adhere rules create` asks the coding agent for one of three routes:
|
|
335
|
+
|
|
336
|
+
- **A native Oxlint rule.** The agent answers `linter` as a plain rule-id
|
|
337
|
+
string, or null, and adhere checks it against its bundled Oxlint catalog at
|
|
338
|
+
decode time; unknown ids are rejected. It also supplies the rule's exact
|
|
339
|
+
option values, which the config puts after the severity: a rule whose
|
|
340
|
+
defaults enforce the idea prints `"error"`, while an option-dependent rule
|
|
341
|
+
prints `["error", ...options]`, because bare `"error"` would enforce
|
|
342
|
+
nothing. Banning lodash with `no-restricted-imports` becomes
|
|
343
|
+
`["error", { "paths": [{ "name": "lodash" }] }]`. A project rule prints the
|
|
344
|
+
exact config; an organization rule enables it in the shared `oxlint/rules.json`
|
|
345
|
+
and pushes a branch for a pull request with the README guide refreshed. In a
|
|
346
|
+
shared repo itself, "this project" writes `oxlint/rules.json` locally instead
|
|
347
|
+
of printing the config. A rule from a plugin Oxlint leaves off
|
|
348
|
+
by default also needs that plugin in `plugins`, whose list replaces Oxlint's
|
|
349
|
+
default set. A type-aware rule needs
|
|
350
|
+
`options.typeAware` and `oxlint-tsgolint`, which Vite+ includes.
|
|
351
|
+
- **A custom Oxlint JS plugin.** Only when the convention is visible in the
|
|
352
|
+
syntax tree and scopes: a JS plugin cannot see TypeScript types, other
|
|
353
|
+
files, or another parser's AST. The plugin must be plain ESM, default-export
|
|
354
|
+
its object, and import nothing; the agent reports each diagnostic's message
|
|
355
|
+
literally, so its valid and invalid fixtures can match exact diagnostics.
|
|
356
|
+
adhere shows the plugin and fixtures, and runs nothing until the user
|
|
357
|
+
approves. It then runs the fixtures with the project's own Oxlint RuleTester,
|
|
358
|
+
through `oxlint/plugins-dev` or `vite-plus/lint/plugins-dev`, so the project
|
|
359
|
+
needs its own Oxlint or Vite+ and Node 22 or newer on `PATH`; Oxlint's
|
|
360
|
+
RuleTester refuses Bun and older Node, there is no Bun fallback, and without
|
|
361
|
+
it nothing is written and no repair is attempted. adhere bundles no Oxlint.
|
|
362
|
+
One failure goes back to the agent to repair; the user approves the repaired
|
|
363
|
+
plugin, then it runs again. Nothing is written unless the tests pass.
|
|
364
|
+
- **An adhere rule**, for judgment, facts outside the file, or types.
|
|
365
|
+
|
|
366
|
+
Whichever route it takes, the draft is trialled on real files before anything
|
|
367
|
+
is written: the first 100 applicable files by default, the entire repo, or a
|
|
368
|
+
number the user types, with the plan's known cost shown first. The authoring
|
|
369
|
+
agent reads the saved report in `.adhere/trial/` and the source it names. After
|
|
370
|
+
the first completed adhere trial, it is explicitly asked whether path/content
|
|
371
|
+
filters would help: recommend exact entries or explain why none are needed,
|
|
372
|
+
including the risk of hiding real violations. The user accepts the draft,
|
|
373
|
+
reviews findings, or asks for a revision. Findings and examples take optional
|
|
374
|
+
notes; revisions receive those notes and the filter recommendation, are
|
|
375
|
+
re-approved, and rerun the same sample. Newly filtered files are reported as
|
|
376
|
+
skipped, not replaced. The initial cap counts files that pass filters.
|
|
377
|
+
Skipping is allowed, but the write prompt then says `(not trialled)` and the
|
|
378
|
+
closing line says `Not trialled: …`; nothing is called validated on a skipped,
|
|
379
|
+
partial, or stale run. Native settings and approved plugins are trialled too,
|
|
380
|
+
without adhere-only filter recommendations.
|
|
381
|
+
|
|
382
|
+
A project plugin and its checked-in test go to
|
|
383
|
+
`.adhere/oxlint/<plugin>.mjs` and `.adhere/oxlint/<plugin>.test.mjs`; the
|
|
384
|
+
`.mjs` keeps ESM in a `type: commonjs` package. adhere prints the config to add
|
|
385
|
+
to `oxlint.config.ts` or Vite+'s lint block:
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"jsPlugins": ["./.adhere/oxlint/no-foo.mjs"],
|
|
390
|
+
"rules": { "no-foo/no-foo": "error" }
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Run the test with Node 22 or newer: `node .adhere/oxlint/no-foo.test.mjs`.
|
|
395
|
+
|
|
396
|
+
A native project rule without options prints only its id:
|
|
397
|
+
|
|
398
|
+
```json
|
|
399
|
+
{ "rules": { "typescript/no-explicit-any": "error" } }
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
A shared repo keeps plugins in `oxlint/plugins/` as `.js`, with `.test.js` beside
|
|
403
|
+
them, because the shared package is `type: module`; it registers them in
|
|
404
|
+
`oxlint/rules.json`. The catalog follows adhere's toolchain, not the project's
|
|
405
|
+
installed Oxlint, so a suggestion may be missing on an older Oxlint or a newer
|
|
406
|
+
rule may be absent from the catalog; the plugin tests always use the project's
|
|
407
|
+
Oxlint. `adhere validate` reports rules left over that a linter could check.
|
|
408
|
+
|
|
409
|
+
## Shared repos
|
|
410
|
+
|
|
411
|
+
An organization keeps its instructions, rules, and lint settings in one repo,
|
|
412
|
+
scaffolded by `adhere init --shared org/repo`. Its `README.md` carries a
|
|
413
|
+
managed guide between `<!-- adhere:rules:start -->` and
|
|
414
|
+
`<!-- adhere:rules:end -->`: a contents list nesting every rule under its
|
|
415
|
+
topic, the rules grouped by topic with each bold statement and rule id opening
|
|
416
|
+
that rule's `RULE.md` verbatim: examples and explanation, then a source link
|
|
417
|
+
under a stable permalink, every enabled native setting in `oxlint/rules.json`
|
|
418
|
+
with its severity, and each custom plugin's enabled rules, linked to
|
|
419
|
+
`oxlint/plugins/<name>.js` when that is a local file. A `RULE.ts` is linked as
|
|
420
|
+
TypeScript source only: the guide never reads or runs it, and plugin code is
|
|
421
|
+
never loaded. Refresh it with `adhere rules readme [org/repo]` after renaming
|
|
422
|
+
or deleting a rule; it preserves prose outside the block, omits off lint
|
|
423
|
+
settings, and refuses malformed markers without writing.
|
|
424
|
+
`adhere rules create` refreshes it for local writes and stages it with
|
|
425
|
+
shared-repo branches.
|
|
426
|
+
|
|
427
|
+
The command chooses its target by argument, then by where it runs. A named
|
|
428
|
+
`org/repo` wins. Otherwise a working directory that is itself a shared repo
|
|
429
|
+
(`package.json` exporting `./oxlint`) is written in place. Anywhere else,
|
|
430
|
+
adhere reads the checkout's GitHub `origin` (`https`, `ssh`, or scp form) and
|
|
431
|
+
infers `<owner>/standards` (`acme/app` -> `acme/standards`, never `acme/app`);
|
|
432
|
+
a fork's owner is the fork's. That remote target is cloned at its default
|
|
433
|
+
branch, the changed block is committed alone on a fresh
|
|
434
|
+
`adhere/readme-<timestamp>` branch, pushed, and printed with a compare link.
|
|
435
|
+
No pull request is opened, the consumer checkout and the default branch stay
|
|
436
|
+
untouched, and an unchanged guide prints `No change, so nothing was pushed.`
|
|
437
|
+
without committing. A rules-only remote works, since the `./oxlint` export is
|
|
438
|
+
only required for the local write. A missing or non-GitHub `origin` fails and
|
|
439
|
+
names `adhere rules readme org/repo`; name the repo explicitly when the
|
|
440
|
+
organization's repo is not `<owner>/standards`.
|
|
441
|
+
|
|
442
|
+
Other repos copy the first two with `adhere install`:
|
|
443
|
+
|
|
444
|
+
```sh
|
|
445
|
+
adhere list org/repo # rules and descriptions; instructions are not listed
|
|
446
|
+
adhere install org/repo # choose instruction files, then rules, at a terminal
|
|
447
|
+
adhere install org/repo --all # everything, without a terminal (CI)
|
|
448
|
+
adhere install org/repo/data # choose among the data topic's rules
|
|
449
|
+
adhere install org/repo/data/ports # exactly that rule, without asking
|
|
450
|
+
adhere install org/repo#v1.0.0 --all # everything at tag v1.0.0
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
A bare repo chooses from instruction files and rules. A topic chooses only the
|
|
454
|
+
rules under it, never instructions, even a topic named `instructions`. An exact
|
|
455
|
+
rule id with no rule deeper under it is copied directly. Already-installed
|
|
456
|
+
items are preselected. A rerun skips what exists; `--force` replaces existing
|
|
457
|
+
files and rule directories, and deselecting never deletes them.
|
|
458
|
+
|
|
459
|
+
Instruction files land in `.adhere/instructions/org/repo/<path>`. Rules keep
|
|
460
|
+
their whole directory in `.adhere/rules/org/repo/`, so `data/brand-ports`
|
|
461
|
+
becomes the rule `org/repo/data/brand-ports`, and a copied `RULE.ts` runs
|
|
462
|
+
whenever lint does. Copies are the repo's own from then on. `adhere postinstall`
|
|
463
|
+
renders the instruction files into `AGENTS.md`; files whose path has at least
|
|
464
|
+
three segments come first (copied files, and local files two directories deep),
|
|
465
|
+
then files directly in the instructions directory, then files one directory
|
|
466
|
+
deep, each group sorted by path.
|
|
467
|
+
|
|
468
|
+
The shared `oxlint/` package is not copied. Pin it as a git dependency at a
|
|
469
|
+
repo-wide tag (there are no per-rule or per-package tags; one tag covers rules,
|
|
470
|
+
instructions, and lint together), then spread it into Vite+ or plain Oxlint:
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
// vite.config.ts
|
|
474
|
+
import sharedLint from "@org/repo/oxlint";
|
|
475
|
+
import { defineConfig } from "vite-plus";
|
|
476
|
+
|
|
477
|
+
export default defineConfig({
|
|
478
|
+
lint: { ...sharedLint, jsPlugins: [...sharedLint.jsPlugins, "./.adhere/oxlint/local.mjs"] },
|
|
479
|
+
});
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
```ts
|
|
483
|
+
// oxlint.config.ts: oxlint -c oxlint.config.ts
|
|
484
|
+
import sharedLint from "@org/repo/oxlint";
|
|
485
|
+
|
|
486
|
+
export default { ...sharedLint };
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
`oxlint/index.js` reads `oxlint/rules.json` and resolves plugin paths relative to
|
|
490
|
+
the package. Paths a project adds stay relative to its own config; project
|
|
491
|
+
plugins live in `.adhere/oxlint/`.
|
|
306
492
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
`adhere install org/repo`, or one topic or rule with
|
|
310
|
-
`adhere install org/repo/<topic>[/<rule>]`. Each rule's whole directory lands
|
|
311
|
-
in `.adhere/rules/org/repo/`, so `data/brand-ports` becomes the rule
|
|
312
|
-
`org/repo/data/brand-ports`, and a copied `RULE.ts` runs whenever lint does.
|
|
313
|
-
They are the repo's own rules from then on; a second install skips existing
|
|
314
|
-
rules unless `--force`, which replaces their directories, local edits and all. The source is cloned with git,
|
|
315
|
-
so a private repo needs git's credentials for GitHub.
|
|
493
|
+
The source is cloned with git, so a private repo needs git's credentials for
|
|
494
|
+
GitHub.
|
|
316
495
|
|
|
317
496
|
## Comments and suppressions
|
|
318
497
|
|
|
@@ -347,9 +526,10 @@ re-judges that rule; a threshold change re-judges nothing.
|
|
|
347
526
|
|
|
348
527
|
Dependencies, related source, reader selection, shared read-context contents,
|
|
349
528
|
and hook-owned external data are deliberately not fingerprinted. If `A.ts` imports
|
|
350
|
-
`B.ts`, changing `B.ts` does not invalidate `A.ts`. Changing config
|
|
351
|
-
`
|
|
352
|
-
stale dependency/context
|
|
529
|
+
`B.ts`, changing `B.ts` does not invalidate `A.ts`. Changing the config's
|
|
530
|
+
`reads` or `typeAware` re-judges every rule once; changing what those reads
|
|
531
|
+
find does not. This keeps planning cheap but accepts stale dependency/context
|
|
532
|
+
judgments.
|
|
353
533
|
|
|
354
534
|
`planAudit` reads/counts/hashes code, checks the cache, and makes code plus cheap
|
|
355
535
|
workspace estimates without compiler or `SourceReads` calls. It applies `--limit`
|
|
@@ -422,6 +602,10 @@ Per rule, read its findings and decide:
|
|
|
422
602
|
- Many hits at 0.80 to 0.90, mostly not violations: the description is too
|
|
423
603
|
broad. Narrow it, or scope it with `appliesTo` or `excludeIf`, before
|
|
424
604
|
raising its `threshold`: a threshold hides, a sentence explains.
|
|
605
|
+
- Hits in irrelevant files: consider local `include`/`exclude` path or content
|
|
606
|
+
filters before paying to judge them. Verify the boundary against real
|
|
607
|
+
violations and unflagged files; an absent import does not prove the library
|
|
608
|
+
is unused when the project uses wrappers or re-exports.
|
|
425
609
|
- Hits on code that follows the pattern through another API: the `must`
|
|
426
610
|
block is too specific. Use the repo's most general correct example.
|
|
427
611
|
- Right less than half the time and not fixable by wording: `level: warning`,
|
|
@@ -163,7 +163,7 @@ rules, and in the report give the user the counts and suggest one of:
|
|
|
163
163
|
`{ threshold: 0.9 }`;
|
|
164
164
|
- for a project rule, rewording it to say what code it is about and what is
|
|
165
165
|
out of scope, following the rule writing tips:
|
|
166
|
-
https://
|
|
166
|
+
https://dm.sh/adhere/writing-good-rules/#rule-writing-tips.
|
|
167
167
|
|
|
168
168
|
## 7. Report
|
|
169
169
|
|
|
@@ -37,12 +37,14 @@ wherever this skill says `adhere`, run it the manager's way, as in
|
|
|
37
37
|
|
|
38
38
|
With pnpm, at a workspace root, add `--ignore-workspace-root-check`.
|
|
39
39
|
|
|
40
|
-
Default lint reads code and `workspacePackages` without starting a compiler
|
|
40
|
+
Default lint reads code and `workspacePackages` without starting a compiler,
|
|
41
|
+
and leaves out bundled preset rules that declare `symbols` or `references`.
|
|
41
42
|
`typeAware` defaults to `false`; `typeAware: true` or `--type-aware` opts into
|
|
42
|
-
both `symbols` and `references`, regardless of `reads
|
|
43
|
-
workspace context only, defaulting to
|
|
44
|
-
reader entries alone do not opt in.
|
|
45
|
-
awareness off. Explain the extra
|
|
43
|
+
both `symbols` and `references`, regardless of `reads`, and includes those
|
|
44
|
+
rules. `reads` now controls workspace context only, defaulting to
|
|
45
|
+
`["workspacePackages"]`; legacy native reader entries alone do not opt in.
|
|
46
|
+
Code alone uses `reads: []` with type awareness off. Explain the extra
|
|
47
|
+
context/cost and **Ask** before opting in.
|
|
46
48
|
|
|
47
49
|
With type awareness enabled, native TypeScript starts only during execution,
|
|
48
50
|
after confirmation, when pending judgments or cached findings needing location
|
|
@@ -91,7 +93,10 @@ from and the evidence.
|
|
|
91
93
|
|
|
92
94
|
Keep only conventions that need judgment: about intent, shape, or placement.
|
|
93
95
|
Drop any a regex, an import check, or the type checker could flag every time,
|
|
94
|
-
and note where it belongs instead
|
|
96
|
+
and note where it belongs instead: a native Oxlint rule, or a convention a
|
|
97
|
+
small syntax-only Oxlint JS plugin could decide. `adhere rules create` routes
|
|
98
|
+
those two, and leaves the rest to adhere. Drop any a preset below already
|
|
99
|
+
covers.
|
|
95
100
|
|
|
96
101
|
**Presets.** Match the stack: `typescript` for TypeScript, `react` for React,
|
|
97
102
|
`security` for code that handles secrets, SQL, shell commands, or paths,
|
|
@@ -100,9 +105,10 @@ and note where it belongs instead. Drop any a preset below already covers.
|
|
|
100
105
|
https://github.com/darkmatter/adhere/tree/main/presets, to describe them and
|
|
101
106
|
to avoid drafting duplicates.
|
|
102
107
|
|
|
103
|
-
**A shared repo.** **Ask** whether the organization keeps adhere
|
|
104
|
-
repo. If it does, `adhere list org/repo` prints
|
|
105
|
-
descriptions
|
|
108
|
+
**A shared repo.** **Ask** whether the organization keeps adhere instructions,
|
|
109
|
+
rules, or lint settings in a repo. If it does, `adhere list org/repo` prints
|
|
110
|
+
its rules with their descriptions; its instruction files and `oxlint/` package
|
|
111
|
+
are separate, and `adhere install` offers the instruction files too.
|
|
106
112
|
|
|
107
113
|
## 3. Draft the repo's rules
|
|
108
114
|
|
|
@@ -131,7 +137,8 @@ Presets
|
|
|
131
137
|
From acme/standards
|
|
132
138
|
5. data/brand-ports A port must be a branded, range-checked integer, never a bare number.
|
|
133
139
|
Dropped as a linter's job
|
|
134
|
-
- "no default exports":
|
|
140
|
+
- "no default exports": native oxlint import/no-default-export
|
|
141
|
+
- "no local date formatting": custom syntax-only oxlint plugin
|
|
135
142
|
```
|
|
136
143
|
|
|
137
144
|
For a preset, offer the whole preset or its topics. Show a draft's full text
|
|
@@ -152,8 +159,30 @@ the wording of any. Revise drafts the user wants changed and show them again.
|
|
|
152
159
|
workspace context, not native maps. Only replace `reads` if the user chooses
|
|
153
160
|
to limit workspace context. If requested type awareness lacks compiler resources,
|
|
154
161
|
use the package launcher or a checkout; do not silently turn it off.
|
|
155
|
-
-
|
|
156
|
-
|
|
162
|
+
- Copy chosen shared instructions and rules: run `adhere install org/repo` and
|
|
163
|
+
let the user pick at the terminal, or add `--all` only when they chose
|
|
164
|
+
everything (the non-interactive path). Name an exact rule to copy it without
|
|
165
|
+
asking. Instruction files land in `.adhere/instructions/org/repo/` and rules
|
|
166
|
+
in `.adhere/rules/org/repo/`; `adhere postinstall` renders the instructions,
|
|
167
|
+
copied files first.
|
|
168
|
+
- **Ask** before adding the shared repo's lint package: pin it as a git
|
|
169
|
+
dependency at the repo-wide tag, then spread `@org/repo/oxlint` into the
|
|
170
|
+
project's Vite+ lint block or `oxlint.config.ts`. It is a package, not an
|
|
171
|
+
`adhere install` copy. Project plugins live in `.adhere/oxlint/`.
|
|
172
|
+
- For a chosen linter candidate, run `adhere rules create "<convention>"` and
|
|
173
|
+
follow its route. A native Oxlint rule must be one of adhere's catalog ids;
|
|
174
|
+
an unknown id is rejected. It must carry the rule's exact option values when
|
|
175
|
+
it takes options; adhere prints `["error", ...options]`, never a bare
|
|
176
|
+
`"error"` that would enforce nothing. It prints the config for the user's lint
|
|
177
|
+
config, and adhere does not edit it. A custom syntax-only plugin is shown with
|
|
178
|
+
valid and invalid examples and runs nothing until approved, then its tests
|
|
179
|
+
use the project's Oxlint through `oxlint/plugins-dev` or
|
|
180
|
+
`vite-plus/lint/plugins-dev`. That needs its own Oxlint or Vite+ and Node 22
|
|
181
|
+
or newer on `PATH`; Oxlint's RuleTester refuses Bun and older Node, and
|
|
182
|
+
without it nothing is written and no repair is attempted. One failed run goes
|
|
183
|
+
back to the agent for repair; the user approves the repaired plugin, and it
|
|
184
|
+
runs again. Project files go to `.adhere/oxlint/` as `.mjs`; shared files go
|
|
185
|
+
to `oxlint/plugins/` as `.js` on a branch for a pull request.
|
|
157
186
|
- Add `exclude` globs for generated or vendored code the default skips miss.
|
|
158
187
|
|
|
159
188
|
## 6. Validate
|
|
@@ -164,8 +193,10 @@ adhere validate
|
|
|
164
193
|
|
|
165
194
|
It reports rules worded against the tips, rules a linter could check, and
|
|
166
195
|
contradictions between rules that apply to the same files. Fix the wording
|
|
167
|
-
yourself. For a linter-checkable rule
|
|
168
|
-
|
|
196
|
+
yourself. For a linter-checkable rule, **Ask** whether to move it to a native
|
|
197
|
+
Oxlint rule or an approved syntax-only plugin, or keep it as an adhere rule.
|
|
198
|
+
For a contradiction, **Ask** which rule to keep and apply the change. Run it
|
|
199
|
+
again until it reports no contradictions.
|
|
169
200
|
|
|
170
201
|
## 7. The first lint
|
|
171
202
|
|
|
@@ -402,9 +433,11 @@ This needs the cache committed on the base branch first.
|
|
|
402
433
|
|
|
403
434
|
Tell the user:
|
|
404
435
|
|
|
405
|
-
- the rules imported, by source, and where they are
|
|
436
|
+
- the rules imported, by source, and where they are, plus any shared
|
|
437
|
+
instruction files copied and the `oxlint/` package pinned;
|
|
406
438
|
- the presets and shared repo named in the config or installed;
|
|
407
|
-
- the candidates
|
|
439
|
+
- the candidates routed to native Oxlint, an approved plugin, or an adhere
|
|
440
|
+
rule, and which config registers each;
|
|
408
441
|
- the last lint's findings, and what was fixed, made a warning, or suppressed;
|
|
409
442
|
- the CI workflow and whether the secret is set;
|
|
410
443
|
- what is left for them: pushing, setting the secret, fixing remaining findings.
|