@drkmttr/adhere 0.21.2 → 0.23.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drkmttr/adhere",
3
- "version": "0.21.2",
3
+ "version": "0.23.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.21.2",
94
- "@drkmttr/adhere-darwin-x64": "0.21.2",
95
- "@drkmttr/adhere-linux-arm64": "0.21.2",
96
- "@drkmttr/adhere-linux-x64": "0.21.2",
97
- "@drkmttr/adhere-win32-x64": "0.21.2"
96
+ "@drkmttr/adhere-darwin-arm64": "0.23.0",
97
+ "@drkmttr/adhere-darwin-x64": "0.23.0",
98
+ "@drkmttr/adhere-linux-arm64": "0.23.0",
99
+ "@drkmttr/adhere-linux-x64": "0.23.0",
100
+ "@drkmttr/adhere-win32-x64": "0.23.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](../README.md#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
@@ -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 rules, 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.
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] # .adhere/config.ts, two example rules, and the dev dependency
23
- adhere init --shared org/repo # a repo of rules other repos install, with CI and an alchemy stack
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] # wizard: claude, codex, or omp drafts a RULE.md or RULE.ts from excerpts you judge; org rules pushed for a PR
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] # copy them into .adhere/rules/org/repo/
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. With `typeAware: true` or `--type-aware`, it starts the compiler
44
- only during execution, after confirmation, for pending judgments or cached
45
- findings still needing location.
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. A `*.md` rule elsewhere in `.adhere/`, as
63
- rules were once written, refuses the run with where it goes to keep its id.
64
- A `.adhere/rules/` in a subdirectory holds rules for that
65
- subtree only, and the most specific rule with an id wins. `cache/` and the
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
  ---
@@ -124,6 +127,13 @@ Front matter besides `description`, all optional:
124
127
  only when Jev says the code that breaks the rule is as every `appliesTo`
125
128
  describes and as no `excludeIf` does.
126
129
 
130
+ - `reads: ["symbols", "references"]` declares the context the rule needs, a
131
+ JSON array on one line. It enables nothing: the config's `reads` and
132
+ `typeAware` still choose what this run sends. A project's own rule still
133
+ runs and lint warns that it may be less accurate until a declared read is
134
+ enabled; a bundled preset rule that declares either native read is left out
135
+ unless `typeAware` is on.
136
+
127
137
  A rule's directory can hold a `RULE.ts` instead, which default-exports
128
138
  `defineRule({...})` from `@drkmttr/adhere`, with the fields a config's inline
129
139
  rule takes:
@@ -174,9 +184,13 @@ Opting in with `typeAware: true` or `--type-aware` enables both `symbols` and
174
184
  `references`, regardless of `reads`. Legacy native-reader entries in `reads`
175
185
  alone do not enable the compiler; leaving them out does not disable it when
176
186
  type awareness is on. Code alone requires `reads: []` and type awareness off.
177
- Presets do not own or override this selection. A rule-level `reads`, in
178
- Markdown front matter, `defineRule`, or an inline rule, has no effect. The
179
- context maps are:
187
+ Presets do not own or override this selection. A rule may declare what it
188
+ needs with `reads`, in Markdown front matter, `defineRule`, or an inline rule;
189
+ the declaration enables nothing. A bundled preset rule that declares `symbols`
190
+ or `references` is left out unless `typeAware` is on, so it is never judged
191
+ with less than it declares. A project's own rule still runs, and a declared
192
+ read the run does not enable is a warning that it may be less accurate until
193
+ enabled. The context maps are:
180
194
 
181
195
  - `workspacePackages`: only packages matched by this file's literal imports,
182
196
  as `{ canonicalPackageName: directory }`. Subpaths match the owning package;
@@ -302,17 +316,166 @@ https://github.com/darkmatter/adhere/tree/main/presets. A preset leaves out
302
316
  what that ecosystem's linters check exactly. Quiet a preset rule with
303
317
  `overrides` rather than copying it.
304
318
 
305
- ## Shared rules
319
+ ## Linter-checkable rules
320
+
321
+ A convention a linter can decide exactly does not belong in a `RULE.md`.
322
+ `adhere rules create` asks the coding agent for one of three routes:
323
+
324
+ - **A native Oxlint rule.** The agent answers `linter` as a plain rule-id
325
+ string, or null, and adhere checks it against its bundled Oxlint catalog at
326
+ decode time; unknown ids are rejected. It also supplies the rule's exact
327
+ option values, which the config puts after the severity: a rule whose
328
+ defaults enforce the idea prints `"error"`, while an option-dependent rule
329
+ prints `["error", ...options]`, because bare `"error"` would enforce
330
+ nothing. Banning lodash with `no-restricted-imports` becomes
331
+ `["error", { "paths": [{ "name": "lodash" }] }]`. A project rule prints the
332
+ exact config; an organization rule enables it in the shared `oxlint/rules.json`
333
+ and pushes a branch for a pull request with the README guide refreshed. In a
334
+ shared repo itself, "this project" writes `oxlint/rules.json` locally instead
335
+ of printing the config. A rule from a plugin Oxlint leaves off
336
+ by default also needs that plugin in `plugins`, whose list replaces Oxlint's
337
+ default set. A type-aware rule needs
338
+ `options.typeAware` and `oxlint-tsgolint`, which Vite+ includes.
339
+ - **A custom Oxlint JS plugin.** Only when the convention is visible in the
340
+ syntax tree and scopes: a JS plugin cannot see TypeScript types, other
341
+ files, or another parser's AST. The plugin must be plain ESM, default-export
342
+ its object, and import nothing; the agent reports each diagnostic's message
343
+ literally, so its valid and invalid fixtures can match exact diagnostics.
344
+ adhere shows the plugin and fixtures, and runs nothing until the user
345
+ approves. It then runs the fixtures with the project's own Oxlint RuleTester,
346
+ through `oxlint/plugins-dev` or `vite-plus/lint/plugins-dev`, so the project
347
+ needs its own Oxlint or Vite+ and Node 22 or newer on `PATH`; Oxlint's
348
+ RuleTester refuses Bun and older Node, there is no Bun fallback, and without
349
+ it nothing is written and no repair is attempted. adhere bundles no Oxlint.
350
+ One failure goes back to the agent to repair; the user approves the repaired
351
+ plugin, then it runs again. Nothing is written unless the tests pass.
352
+ - **An adhere rule**, for judgment, facts outside the file, or types.
353
+
354
+ Whichever route it takes, the draft is trialled on real files before anything
355
+ is written: the first 100 applicable files by default, the entire repo, or a
356
+ number the user types, with the plan's known cost shown first. The authoring
357
+ agent reads the saved report in `.adhere/trial/` and the source it names, then
358
+ the user accepts the findings, reviews each as correct, a false positive, or
359
+ unsure, or asks for a revision. Findings and examples take optional notes for
360
+ the agent; a revision is re-approved and reruns the same files, so the new
361
+ evidence is comparable. Skipping is allowed, but the write prompt then says
362
+ `(not trialled)` and the closing line says `Not trialled: …`; nothing is called
363
+ validated on a skipped, partial, or stale run. The trial runs for native
364
+ settings and approved plugins too.
365
+
366
+ A project plugin and its checked-in test go to
367
+ `.adhere/oxlint/<plugin>.mjs` and `.adhere/oxlint/<plugin>.test.mjs`; the
368
+ `.mjs` keeps ESM in a `type: commonjs` package. adhere prints the config to add
369
+ to `oxlint.config.ts` or Vite+'s lint block:
370
+
371
+ ```json
372
+ {
373
+ "jsPlugins": ["./.adhere/oxlint/no-foo.mjs"],
374
+ "rules": { "no-foo/no-foo": "error" }
375
+ }
376
+ ```
377
+
378
+ Run the test with Node 22 or newer: `node .adhere/oxlint/no-foo.test.mjs`.
379
+
380
+ A native project rule without options prints only its id:
381
+
382
+ ```json
383
+ { "rules": { "typescript/no-explicit-any": "error" } }
384
+ ```
385
+
386
+ A shared repo keeps plugins in `oxlint/plugins/` as `.js`, with `.test.js` beside
387
+ them, because the shared package is `type: module`; it registers them in
388
+ `oxlint/rules.json`. The catalog follows adhere's toolchain, not the project's
389
+ installed Oxlint, so a suggestion may be missing on an older Oxlint or a newer
390
+ rule may be absent from the catalog; the plugin tests always use the project's
391
+ Oxlint. `adhere validate` reports rules left over that a linter could check.
392
+
393
+ ## Shared repos
394
+
395
+ An organization keeps its instructions, rules, and lint settings in one repo,
396
+ scaffolded by `adhere init --shared org/repo`. Its `README.md` carries a
397
+ managed guide between `<!-- adhere:rules:start -->` and
398
+ `<!-- adhere:rules:end -->`: a contents list nesting every rule under its
399
+ topic, the rules grouped by topic with each bold statement and rule id opening
400
+ that rule's `RULE.md` verbatim: examples and explanation, then a source link
401
+ under a stable permalink, every enabled native setting in `oxlint/rules.json`
402
+ with its severity, and each custom plugin's enabled rules, linked to
403
+ `oxlint/plugins/<name>.js` when that is a local file. A `RULE.ts` is linked as
404
+ TypeScript source only: the guide never reads or runs it, and plugin code is
405
+ never loaded. Refresh it with `adhere rules readme [org/repo]` after renaming
406
+ or deleting a rule; it preserves prose outside the block, omits off lint
407
+ settings, and refuses malformed markers without writing.
408
+ `adhere rules create` refreshes it for local writes and stages it with
409
+ shared-repo branches.
410
+
411
+ The command chooses its target by argument, then by where it runs. A named
412
+ `org/repo` wins. Otherwise a working directory that is itself a shared repo
413
+ (`package.json` exporting `./oxlint`) is written in place. Anywhere else,
414
+ adhere reads the checkout's GitHub `origin` (`https`, `ssh`, or scp form) and
415
+ infers `<owner>/standards` (`acme/app` -> `acme/standards`, never `acme/app`);
416
+ a fork's owner is the fork's. That remote target is cloned at its default
417
+ branch, the changed block is committed alone on a fresh
418
+ `adhere/readme-<timestamp>` branch, pushed, and printed with a compare link.
419
+ No pull request is opened, the consumer checkout and the default branch stay
420
+ untouched, and an unchanged guide prints `No change, so nothing was pushed.`
421
+ without committing. A rules-only remote works, since the `./oxlint` export is
422
+ only required for the local write. A missing or non-GitHub `origin` fails and
423
+ names `adhere rules readme org/repo`; name the repo explicitly when the
424
+ organization's repo is not `<owner>/standards`.
425
+
426
+ Other repos copy the first two with `adhere install`:
427
+
428
+ ```sh
429
+ adhere list org/repo # rules and descriptions; instructions are not listed
430
+ adhere install org/repo # choose instruction files, then rules, at a terminal
431
+ adhere install org/repo --all # everything, without a terminal (CI)
432
+ adhere install org/repo/data # choose among the data topic's rules
433
+ adhere install org/repo/data/ports # exactly that rule, without asking
434
+ adhere install org/repo#v1.0.0 --all # everything at tag v1.0.0
435
+ ```
436
+
437
+ A bare repo chooses from instruction files and rules. A topic chooses only the
438
+ rules under it, never instructions, even a topic named `instructions`. An exact
439
+ rule id with no rule deeper under it is copied directly. Already-installed
440
+ items are preselected. A rerun skips what exists; `--force` replaces existing
441
+ files and rule directories, and deselecting never deletes them.
442
+
443
+ Instruction files land in `.adhere/instructions/org/repo/<path>`. Rules keep
444
+ their whole directory in `.adhere/rules/org/repo/`, so `data/brand-ports`
445
+ becomes the rule `org/repo/data/brand-ports`, and a copied `RULE.ts` runs
446
+ whenever lint does. Copies are the repo's own from then on. `adhere postinstall`
447
+ renders the instruction files into `AGENTS.md`; files whose path has at least
448
+ three segments come first (copied files, and local files two directories deep),
449
+ then files directly in the instructions directory, then files one directory
450
+ deep, each group sorted by path.
451
+
452
+ The shared `oxlint/` package is not copied. Pin it as a git dependency at a
453
+ repo-wide tag (there are no per-rule or per-package tags; one tag covers rules,
454
+ instructions, and lint together), then spread it into Vite+ or plain Oxlint:
455
+
456
+ ```ts
457
+ // vite.config.ts
458
+ import sharedLint from "@org/repo/oxlint";
459
+ import { defineConfig } from "vite-plus";
460
+
461
+ export default defineConfig({
462
+ lint: { ...sharedLint, jsPlugins: [...sharedLint.jsPlugins, "./.adhere/oxlint/local.mjs"] },
463
+ });
464
+ ```
465
+
466
+ ```ts
467
+ // oxlint.config.ts: oxlint -c oxlint.config.ts
468
+ import sharedLint from "@org/repo/oxlint";
469
+
470
+ export default { ...sharedLint };
471
+ ```
472
+
473
+ `oxlint/index.js` reads `oxlint/rules.json` and resolves plugin paths relative to
474
+ the package. Paths a project adds stay relative to its own config; project
475
+ plugins live in `.adhere/oxlint/`.
306
476
 
307
- An organization keeps rules in one repo's `.adhere/rules/`, scaffolded by
308
- `adhere init --shared org/repo`, and other repos copy them in with
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.
477
+ The source is cloned with git, so a private repo needs git's credentials for
478
+ GitHub.
316
479
 
317
480
  ## Comments and suppressions
318
481
 
@@ -347,9 +510,10 @@ re-judges that rule; a threshold change re-judges nothing.
347
510
 
348
511
  Dependencies, related source, reader selection, shared read-context contents,
349
512
  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 `reads` or
351
- `typeAware` does not refresh its cached answers either. This keeps planning cheap but accepts
352
- stale dependency/context judgments.
513
+ `B.ts`, changing `B.ts` does not invalidate `A.ts`. Changing the config's
514
+ `reads` or `typeAware` re-judges every rule once; changing what those reads
515
+ find does not. This keeps planning cheap but accepts stale dependency/context
516
+ judgments.
353
517
 
354
518
  `planAudit` reads/counts/hashes code, checks the cache, and makes code plus cheap
355
519
  workspace estimates without compiler or `SourceReads` calls. It applies `--limit`
@@ -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://github.com/darkmatter/adhere#rule-writing-tips.
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`. `reads` now controls
43
- workspace context only, defaulting to `["workspacePackages"]`; legacy native
44
- reader entries alone do not opt in. Code alone uses `reads: []` with type
45
- awareness off. Explain the extra context/cost and **Ask** before opting in.
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. Drop any a preset below already covers.
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 rules in a
104
- repo. If it does, `adhere list org/repo` prints its rules with their
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": eslint import/no-default-export
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
- - Install chosen shared rules: `adhere install org/repo/<id>` for each, or
156
- `adhere install org/repo` for all of them. They land in `.adhere/rules/org/repo/`.
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 or a contradiction, **Ask** the user
168
- which to keep, and apply it. Run it again until it reports no contradictions.
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 dropped as a linter's job, with where each belongs;
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.