@drkmttr/adhere 0.18.0 → 0.18.1
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 +77 -55
- package/package.json +6 -6
- package/skills/adhere/SKILL.md +50 -33
- package/skills/adhere-setup/SKILL.md +71 -48
- package/src/cli.ts +13 -1
- package/src/config.ts +14 -3
- package/src/init.ts +22 -3
- package/src/install.ts +29 -23
- package/src/services/Jev.reads.test.ts +4 -2
- package/src/services/Jev.ts +1 -1
- package/src/workflows/audit.ts +2 -2
package/README.md
CHANGED
|
@@ -97,6 +97,7 @@ your rules.
|
|
|
97
97
|
- [Reading the report](#reading-the-report)
|
|
98
98
|
- [Rule format](#rule-format)
|
|
99
99
|
- [Config](#config)
|
|
100
|
+
- [Other Providers](#providers)
|
|
100
101
|
- [Running lint](#running-lint)
|
|
101
102
|
- [Comments and suppressions](#comments-and-suppressions)
|
|
102
103
|
- [Cache](#cache)
|
|
@@ -123,12 +124,14 @@ curl -fsSL --create-dirs -o ~/.local/bin/adhere \
|
|
|
123
124
|
chmod +x ~/.local/bin/adhere
|
|
124
125
|
```
|
|
125
126
|
|
|
126
|
-
- The npm package brings its own TypeScript compiler
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
- The npm package brings its own TypeScript compiler. Opt in with
|
|
128
|
+
`--type-aware` or `typeAware: true` to add both
|
|
129
|
+
[`symbols` and `references`](#what-jev-reads); type awareness is off by default.
|
|
130
|
+
No other compiler setup is needed.
|
|
131
|
+
- The standalone executable has no compiler. It runs the default, compiler-free
|
|
132
|
+
audit with workspace context, or code alone with `reads: []`. Type-aware audits
|
|
133
|
+
require the npm package or a checkout with its development dependencies.
|
|
134
|
+
It suits a global install that your agent config can always call.
|
|
132
135
|
- It is built for macOS and Linux, on arm64 and x64, and for Windows x64:
|
|
133
136
|
download `adhere-Windows-x86_64.exe` from the
|
|
134
137
|
[latest release](https://github.com/darkmatter/adhere/releases/latest).
|
|
@@ -354,13 +357,13 @@ writing a rule.
|
|
|
354
357
|
|
|
355
358
|
### What else the studies found
|
|
356
359
|
|
|
357
|
-
| Lesson | What the study found
|
|
358
|
-
| ------------------------------------------------------ |
|
|
359
|
-
| "Should" is for people, not a weaker check. | Rules written with "should" and with "must" scored nearly the same ([study](eval/studies/rule-vocabulary.md)).
|
|
360
|
-
| A wrong finding is usually the rule's fault. | Above 0.8, 31 of 35 wrong findings repeated within a rule: a scope its words reach past, or the same misreading again. Fixing the wording fixes them together. Below 0.8, one-off misreads grow, to about one finding in six at 0.6 to 0.7 ([study](eval/studies/presets.md)).
|
|
361
|
-
| Narrow a rule in its description. | Jev scores an `excludeIf` near 0.5 for real and false findings alike, so it thins a rule's findings about as much as a higher threshold ([study](eval/studies/presets.md)). Narrowing the description moves the judgment itself: scoping alchemy's idempotent-delete rule to providers' delete handlers dropped its findings elsewhere from 0.67–0.88 to 0.08–0.21, with nothing real lost ([study](eval/studies/idempotent-delete.md)).
|
|
362
|
-
| Beware rules that turn on what the file does not show. | The weakest preset rules hinged on facts outside the file: whether a client has a default timeout, whether a script is an entry point, whether a key is public ([study](eval/studies/presets.md)). Warning below a context of 0.7 marked one finding in five, and those were real about half the time, where the rest were real three times in four ([study](eval/studies/sufficiency.md)). Make such a rule a warning, or give Jev the fact with
|
|
363
|
-
| Jev believes comments. | Eight of nine real violations of the idempotent-delete rule carried a comment wrongly saying they were fine, and scored 0.31 to 0.62; without comments, all nine scored 0.81 or more ([study](eval/studies/idempotent-delete.md)). That is why adhere takes comments out. A fact Jev needs, such as an exemption, goes in an `@adhere` note, which stays ([study](eval/studies/comments.md)).
|
|
360
|
+
| Lesson | What the study found |
|
|
361
|
+
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
362
|
+
| "Should" is for people, not a weaker check. | Rules written with "should" and with "must" scored nearly the same ([study](eval/studies/rule-vocabulary.md)). |
|
|
363
|
+
| A wrong finding is usually the rule's fault. | Above 0.8, 31 of 35 wrong findings repeated within a rule: a scope its words reach past, or the same misreading again. Fixing the wording fixes them together. Below 0.8, one-off misreads grow, to about one finding in six at 0.6 to 0.7 ([study](eval/studies/presets.md)). |
|
|
364
|
+
| Narrow a rule in its description. | Jev scores an `excludeIf` near 0.5 for real and false findings alike, so it thins a rule's findings about as much as a higher threshold ([study](eval/studies/presets.md)). Narrowing the description moves the judgment itself: scoping alchemy's idempotent-delete rule to providers' delete handlers dropped its findings elsewhere from 0.67–0.88 to 0.08–0.21, with nothing real lost ([study](eval/studies/idempotent-delete.md)). |
|
|
365
|
+
| Beware rules that turn on what the file does not show. | The weakest preset rules hinged on facts outside the file: whether a client has a default timeout, whether a script is an entry point, whether a key is public ([study](eval/studies/presets.md)). Warning below a context of 0.7 marked one finding in five, and those were real about half the time, where the rest were real three times in four ([study](eval/studies/sufficiency.md)). Make such a rule a warning, or give Jev the fact with [`typeAware` or workspace `reads`](#what-jev-reads), an [`appendState`](#rules-as-typescript-files) hook, or an `@adhere` note. |
|
|
366
|
+
| Jev believes comments. | Eight of nine real violations of the idempotent-delete rule carried a comment wrongly saying they were fine, and scored 0.31 to 0.62; without comments, all nine scored 0.81 or more ([study](eval/studies/idempotent-delete.md)). That is why adhere takes comments out. A fact Jev needs, such as an exemption, goes in an `@adhere` note, which stays ([study](eval/studies/comments.md)). |
|
|
364
367
|
|
|
365
368
|
## Commands
|
|
366
369
|
|
|
@@ -570,7 +573,9 @@ finding its matchers dropped, with their scores.
|
|
|
570
573
|
|
|
571
574
|
### What Jev reads
|
|
572
575
|
|
|
573
|
-
|
|
576
|
+
By default, Jev reads the file's code and its imported workspace packages, with
|
|
577
|
+
no native compiler startup. Opt in with `typeAware: true` in the config or
|
|
578
|
+
`adhere lint --type-aware` to add both compiler context maps for every rule:
|
|
574
579
|
|
|
575
580
|
| Name | What Jev reads |
|
|
576
581
|
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -597,7 +602,7 @@ with `…` between them. For example, code importing `twice as scale` can receiv
|
|
|
597
602
|
}
|
|
598
603
|
```
|
|
599
604
|
|
|
600
|
-
When sent to Jev, each
|
|
605
|
+
When sent to Jev, each enabled `references`, `symbols`, and `workspacePackages`
|
|
601
606
|
map includes a reserved `__comment` explaining its contents. These comments are
|
|
602
607
|
included before `appendState` hooks and restored after hooks. Top-level guidance
|
|
603
608
|
also tells Jev that these maps are context only: judge the target `code`, not the
|
|
@@ -609,18 +614,25 @@ and JSDoc tags are first capped separately by `maxSymbolDocumentationLength`, al
|
|
|
609
614
|
256 by default; `0` omits documentation and tags. Smaller limits reduce request
|
|
610
615
|
size but can cut off type signatures, overloads, useful details, or examples.
|
|
611
616
|
|
|
612
|
-
|
|
617
|
+
`typeAware` defaults to `false`. When enabled, it adds **both** `symbols` and
|
|
618
|
+
`references`, regardless of `reads`. The `reads` setting now chooses workspace
|
|
619
|
+
context only: its default is `["workspacePackages"]`; `[]` omits that map.
|
|
620
|
+
Legacy `"symbols"` or `"references"` entries in `reads` alone do not enable native
|
|
621
|
+
context, and leaving them out does not disable it when `typeAware` is true.
|
|
613
622
|
|
|
614
623
|
```ts
|
|
615
624
|
// .adhere/config.ts
|
|
616
625
|
import { defineConfig } from "@drkmttr/adhere";
|
|
617
626
|
|
|
618
627
|
export default defineConfig({
|
|
619
|
-
|
|
628
|
+
typeAware: true, // opt in to both symbols and references
|
|
629
|
+
reads: [], // omit workspacePackages, not compiler context
|
|
620
630
|
});
|
|
621
631
|
```
|
|
622
632
|
|
|
623
|
-
`reads: []`
|
|
633
|
+
For code alone, use `reads: []` with type awareness off; do not pass
|
|
634
|
+
`--type-aware`. To retain workspace context without a compiler, leave the
|
|
635
|
+
settings at their defaults.
|
|
624
636
|
|
|
625
637
|
Each name is the key Jev reads it under, so a rule's description can name it in
|
|
626
638
|
backticks, as the questions name `code`:
|
|
@@ -657,10 +669,12 @@ import { parse } from "orders-core";
|
|
|
657
669
|
- What adhere has no name for, such as a file of the repository's, a rule in
|
|
658
670
|
TypeScript adds with [`appendState`](#rules-as-typescript-files).
|
|
659
671
|
|
|
660
|
-
**Cost and privacy.**
|
|
672
|
+
**Cost and privacy.** Opting into type awareness sends Jev more than the file it
|
|
661
673
|
judges: whole declarations from other files, plus compiler-provided documentation
|
|
662
|
-
and JSDoc tags in `symbols`, even with `includeComments` off.
|
|
663
|
-
|
|
674
|
+
and JSDoc tags in `symbols`, even with `includeComments` off. It also starts the
|
|
675
|
+
native compiler during execution. The plan's estimate leaves those tokens out.
|
|
676
|
+
Keep `typeAware` off to avoid that extra context and compiler work; changing
|
|
677
|
+
`reads` only changes workspace context.
|
|
664
678
|
|
|
665
679
|
<details>
|
|
666
680
|
<summary>Where the packages come from</summary>
|
|
@@ -720,7 +734,7 @@ export default defineRule({
|
|
|
720
734
|
- The state is what Jev reads beside each question: `code`, the file's sections
|
|
721
735
|
as Jev reads them, under their numbers from 1, as in
|
|
722
736
|
`{ code: { "1": "import …", "2": "export const …" } }`, beside what the
|
|
723
|
-
config [`reads`](#what-jev-reads).
|
|
737
|
+
config's [`reads` and `typeAware`](#what-jev-reads) enable.
|
|
724
738
|
- `file` is the file's absolute `path`, and its `contents` as written, comments
|
|
725
739
|
and all.
|
|
726
740
|
- Nothing the hook returns is checked, so it can change `code` too, or replace
|
|
@@ -779,7 +793,8 @@ export default defineConfig({
|
|
|
779
793
|
model: "jev-latest",
|
|
780
794
|
threshold: 0.8,
|
|
781
795
|
sufficiencyThreshold: 0.6,
|
|
782
|
-
reads: ["workspacePackages"
|
|
796
|
+
reads: ["workspacePackages"],
|
|
797
|
+
typeAware: true, // optional; false by default
|
|
783
798
|
presets: ["effect"],
|
|
784
799
|
maxSymbolDocumentationLength: 256,
|
|
785
800
|
maxSymbolLength: 256,
|
|
@@ -800,20 +815,21 @@ type UserId = typeof UserId.Type
|
|
|
800
815
|
});
|
|
801
816
|
```
|
|
802
817
|
|
|
803
|
-
| Key | Default
|
|
804
|
-
| ------------------------------ |
|
|
805
|
-
| `model` | `"jev-latest"`
|
|
806
|
-
| `threshold` | `0.8`
|
|
807
|
-
| `sufficiencyThreshold` | `0.6`
|
|
808
|
-
| `presets` | none
|
|
809
|
-
| `rules` | `.adhere/rules/`
|
|
810
|
-
| `exclude` | none
|
|
811
|
-
| `overrides` | none
|
|
812
|
-
| `includeComments` | `false`
|
|
813
|
-
| `reads` | `["workspacePackages"
|
|
814
|
-
| `
|
|
815
|
-
| `
|
|
816
|
-
| `
|
|
818
|
+
| Key | Default | Meaning |
|
|
819
|
+
| ------------------------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
820
|
+
| `model` | `"jev-latest"` | The model id sent to TypeSafe. |
|
|
821
|
+
| `threshold` | `0.8` | Report a rule when Jev's probability is above this. |
|
|
822
|
+
| `sufficiencyThreshold` | `0.6` | Below it, a finding warns that Jev may not see enough to decide. |
|
|
823
|
+
| `presets` | none | Built-in rule sets, or their topics. See [Presets](#presets). |
|
|
824
|
+
| `rules` | `.adhere/rules/` | Rules inline, as above, or a directory (see [Where rules live](#where-rules-live)). Either replaces `.adhere/rules/`: none of its rules, root or nested, is read then. |
|
|
825
|
+
| `exclude` | none | Globs, relative to the working directory, of files no rule judges. |
|
|
826
|
+
| `overrides` | none | Settings for a rule by its id. See [Overrides](#overrides). |
|
|
827
|
+
| `includeComments` | `false` | Send every comment to Jev. See [Comments](#comments). |
|
|
828
|
+
| `reads` | `["workspacePackages"]` | Chooses workspace context only. `[]` omits it; native read entries do not enable type awareness. See [What Jev reads](#what-jev-reads). |
|
|
829
|
+
| `typeAware` | `false` | Opt in to both compiler `symbols` and declaration `references`, regardless of `reads`. Also enabled by `--type-aware`. |
|
|
830
|
+
| `maxSymbolDocumentationLength` | `256` | Maximum characters of each symbol's combined documentation and JSDoc tags, before `maxSymbolLength` applies. A non-negative integer; `0` omits them. |
|
|
831
|
+
| `maxSymbolLength` | `256` | Maximum characters of each complete `symbols[name]` string, including type signatures and documentation. A non-negative integer; `0` leaves names with empty content. |
|
|
832
|
+
| `provider` | TypeSafe's Jev | Where the questions go instead, with its own model. See [Providers](#providers). |
|
|
817
833
|
|
|
818
834
|
- An invalid shape refuses the run.
|
|
819
835
|
- A config can import other files by relative path, but no packages besides
|
|
@@ -1122,6 +1138,7 @@ the default, `0.6`.
|
|
|
1122
1138
|
| `--preset <names>` | Add built-in rule sets, or their topics. The config becomes optional. See [Presets](#presets). |
|
|
1123
1139
|
| `--threshold <0 to 1>` | Replace the config's threshold. Per-rule thresholds still apply. |
|
|
1124
1140
|
| `--sufficiency-threshold <0 to 1>` | Replace the config's `sufficiencyThreshold`: a finding warns when its `context` is below it. |
|
|
1141
|
+
| `--type-aware` | Enable both compiler `symbols` and declaration `references`; off by default. |
|
|
1125
1142
|
| `--yes`, `-y` | Send the requests without asking first. |
|
|
1126
1143
|
| `--limit <files>` | Send requests for at most that many files. See [Limiting a run](#limiting-a-run). |
|
|
1127
1144
|
| `--rpm <requests>` | Send at most that many requests a minute. |
|
|
@@ -1177,8 +1194,9 @@ Estimates cover only code and workspace-package context; symbol/reference input,
|
|
|
1177
1194
|
- The cost is adhere's estimate of the input tokens times the model's price. Jev
|
|
1178
1195
|
charges only for input tokens: $0.042 a million for `jev-latest`, per
|
|
1179
1196
|
[TypeSafe AI's models page](https://docs.typesafe.ai/models) in
|
|
1180
|
-
September 2026.
|
|
1181
|
-
adhere reads as it sends each file, so a run
|
|
1197
|
+
September 2026. When type awareness is enabled, the estimate leaves out
|
|
1198
|
+
`symbols` and `references`, which adhere reads as it sends each file, so a run
|
|
1199
|
+
can cost more than it says.
|
|
1182
1200
|
- A run that stops loses nothing: files finished before it stopped are cached,
|
|
1183
1201
|
so running again continues from there.
|
|
1184
1202
|
|
|
@@ -1242,7 +1260,7 @@ it hashes or sends a file, or a declaration from
|
|
|
1242
1260
|
[`references`](#what-jev-reads), blanking it so every line keeps its number.
|
|
1243
1261
|
The report's excerpts still show them. Compiler-provided documentation and
|
|
1244
1262
|
JSDoc tags still reach Jev in the type/docs strings [`symbols`](#what-jev-reads)
|
|
1245
|
-
sends; these are not editor hovers.
|
|
1263
|
+
sends when type awareness is enabled; these are not editor hovers.
|
|
1246
1264
|
|
|
1247
1265
|
Jev takes what comments claim at their word, which hid real violations in the
|
|
1248
1266
|
studies: see [Writing good rules](#writing-good-rules).
|
|
@@ -1474,17 +1492,21 @@ about three bytes a token, and leaves 1k of room for what Jev wraps around it.
|
|
|
1474
1492
|
|
|
1475
1493
|
Names and layouts from older versions:
|
|
1476
1494
|
|
|
1477
|
-
| Version | What it had | What happens now
|
|
1478
|
-
| ----------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
1479
|
-
| before 0.7 | A rule's examples were `reference` and `avoid` in a config, and a fence could be tagged `avoid`. | They still read as `must` and `never`.
|
|
1480
|
-
| through 0.7 | The `effect` preset checked seven conventions a linter checks exactly. | They are left to `@effect/tsgo`. The notes on `effect` under [Presets](#presets) say how to turn them on.
|
|
1481
|
-
| before 0.13 | A rule was any `*.md` file in `.adhere/`, as `.adhere/data/brand-ports.md`. | A `*.md` file outside every rule's directory, but a `README.md`, refuses the run. The refusal says where each such file goes to keep its id, so `adhere-ignore` comments and `overrides` still name it.
|
|
1482
|
-
| before 0.13 | `.adhere/tsconfig.json` included only `config.ts`. | Make its `include` `["**/*.ts"]` to give rules written in TypeScript their types.
|
|
1483
|
-
| 0.13.1 | The workspace's packages had a key of their own, `includeWorkspacePackages: true`. | adhere no longer reads it. Jev reads the workspace's packages by default.
|
|
1484
|
-
| 0.13.1 and before | Prose around a `RULE.md`'s code was ignored. | Jev reads it after the description, as the rule's details, so a rule with prose is judged again once. Keep prose about the rule: why it holds, or where it does not apply.
|
|
1485
|
-
| 0.15.0 and before | A rule named what Jev reads beside its code, in `reads`. | `reads` is the config's, for every rule, and a rule that has it refuses the run: delete it.
|
|
1486
|
-
| 0.15.0 and before | `--limit` counted rule checks. | It counts files.
|
|
1487
|
-
| 0.14.0 and before | The `effect` preset had `services/test-layers-are-in-memory` and `services/operations-have-no-requirements`. | They are gone, and an `overrides` entry naming either refuses the run: remove it. `@effect/tsgo`'s `leakingRequirements` checks the second. `adhere-ignore` comments naming them suppress nothing.
|
|
1495
|
+
| Version | What it had | What happens now |
|
|
1496
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1497
|
+
| before 0.7 | A rule's examples were `reference` and `avoid` in a config, and a fence could be tagged `avoid`. | They still read as `must` and `never`. |
|
|
1498
|
+
| through 0.7 | The `effect` preset checked seven conventions a linter checks exactly. | They are left to `@effect/tsgo`. The notes on `effect` under [Presets](#presets) say how to turn them on. |
|
|
1499
|
+
| before 0.13 | A rule was any `*.md` file in `.adhere/`, as `.adhere/data/brand-ports.md`. | A `*.md` file outside every rule's directory, but a `README.md`, refuses the run. The refusal says where each such file goes to keep its id, so `adhere-ignore` comments and `overrides` still name it. |
|
|
1500
|
+
| before 0.13 | `.adhere/tsconfig.json` included only `config.ts`. | Make its `include` `["**/*.ts"]` to give rules written in TypeScript their types. |
|
|
1501
|
+
| 0.13.1 | The workspace's packages had a key of their own, `includeWorkspacePackages: true`. | adhere no longer reads it. Jev reads the workspace's packages by default. |
|
|
1502
|
+
| 0.13.1 and before | Prose around a `RULE.md`'s code was ignored. | Jev reads it after the description, as the rule's details, so a rule with prose is judged again once. Keep prose about the rule: why it holds, or where it does not apply. |
|
|
1503
|
+
| 0.15.0 and before | A rule named what Jev reads beside its code, in `reads`. | `reads` is the config's, for every rule, and a rule that has it refuses the run: delete it. Workspace context is on by default; compiler context requires `typeAware: true`. See [What Jev reads](#what-jev-reads). |
|
|
1504
|
+
| 0.15.0 and before | `--limit` counted rule checks. | It counts files. |
|
|
1505
|
+
| 0.14.0 and before | The `effect` preset had `services/test-layers-are-in-memory` and `services/operations-have-no-requirements`. | They are gone, and an `overrides` entry naming either refuses the run: remove it. `@effect/tsgo`'s `leakingRequirements` checks the second. `adhere-ignore` comments naming them suppress nothing. |
|
|
1506
|
+
|
|
1507
|
+
Configs that previously enabled compiler context with `reads: ["symbols", "references"]`
|
|
1508
|
+
must now opt in with `typeAware: true`, or use `adhere lint --type-aware`.
|
|
1509
|
+
That switch enables both maps; `reads` only controls workspace context.
|
|
1488
1510
|
|
|
1489
1511
|
## Development
|
|
1490
1512
|
|
|
@@ -1497,13 +1519,13 @@ adhere lint --preset effect
|
|
|
1497
1519
|
```
|
|
1498
1520
|
|
|
1499
1521
|
- In a checkout no platform package is installed, so `bin/adhere.js` runs
|
|
1500
|
-
`src/main.ts` with Bun instead
|
|
1501
|
-
|
|
1522
|
+
`src/main.ts` with Bun instead. Type-aware audits use the checkout's own
|
|
1523
|
+
TypeScript; the default audit does not start it.
|
|
1502
1524
|
- `bun run typecheck` runs `tsc` and the linter.
|
|
1503
1525
|
- `bun run test` runs the tests.
|
|
1504
1526
|
- `bun scripts/verify-npm.ts`, after `bun run build:npm`, installs the built
|
|
1505
|
-
packages in a scratch project and
|
|
1506
|
-
`references`. It needs Node 24 or later, and sends Jev nothing.
|
|
1527
|
+
packages in a scratch project and explicitly enables `typeAware: true` to
|
|
1528
|
+
check `symbols` and `references`. It needs Node 24 or later, and sends Jev nothing.
|
|
1507
1529
|
|
|
1508
1530
|
### Native executable
|
|
1509
1531
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drkmttr/adhere",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Lints a repository against the rules in .adhere/, judged by Jev.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -90,10 +90,10 @@
|
|
|
90
90
|
"--force"
|
|
91
91
|
],
|
|
92
92
|
"optionalDependencies": {
|
|
93
|
-
"@drkmttr/adhere-darwin-arm64": "0.18.
|
|
94
|
-
"@drkmttr/adhere-darwin-x64": "0.18.
|
|
95
|
-
"@drkmttr/adhere-linux-arm64": "0.18.
|
|
96
|
-
"@drkmttr/adhere-linux-x64": "0.18.
|
|
97
|
-
"@drkmttr/adhere-win32-x64": "0.18.
|
|
93
|
+
"@drkmttr/adhere-darwin-arm64": "0.18.1",
|
|
94
|
+
"@drkmttr/adhere-darwin-x64": "0.18.1",
|
|
95
|
+
"@drkmttr/adhere-linux-arm64": "0.18.1",
|
|
96
|
+
"@drkmttr/adhere-linux-x64": "0.18.1",
|
|
97
|
+
"@drkmttr/adhere-win32-x64": "0.18.1"
|
|
98
98
|
}
|
|
99
99
|
}
|
package/skills/adhere/SKILL.md
CHANGED
|
@@ -27,6 +27,7 @@ adhere lint # judge the working directory; exit 1 on an err
|
|
|
27
27
|
adhere lint --limit 0 # compiler-free counts/cache plan; no budgeting
|
|
28
28
|
adhere lint --limit 100 # first 100 files needing requests; all pending rules per file
|
|
29
29
|
adhere lint --yes # send without asking, as in CI
|
|
30
|
+
adhere lint --type-aware # opt in to both compiler symbols and declaration references
|
|
30
31
|
adhere lint --preset effect # add a built-in rule set; the config becomes optional
|
|
31
32
|
adhere lint --filter 'src/**' # read only matching files; repeat, and ! to leave out
|
|
32
33
|
adhere lint --threshold 0.9 # report at another threshold from the cache; judges nothing again
|
|
@@ -38,8 +39,9 @@ adhere skill [docs|setup|fix] # print a skill
|
|
|
38
39
|
|
|
39
40
|
`adhere <command> --help` lists every flag. Version, help, skills, `validate`,
|
|
40
41
|
and planning with `--limit 0` do not start the native compiler. Default `lint`
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
is compiler-free. With `typeAware: true` or `--type-aware`, it starts the compiler
|
|
43
|
+
only during execution, after confirmation, for pending judgments or cached
|
|
44
|
+
findings still needing location.
|
|
43
45
|
|
|
44
46
|
`--limit` now counts files, not rule checks; use the same flag without an extra
|
|
45
47
|
unit selector. Each request-bearing file consumes one slot for all its pending
|
|
@@ -127,7 +129,7 @@ rule takes:
|
|
|
127
129
|
`description`, `must`, `never`, and the rest. Its `appendState(state, file, Bun)`
|
|
128
130
|
runs right before each request for the rule, and what it returns is spread
|
|
129
131
|
over the request's state: `code`, the file's sections as Jev reads them,
|
|
130
|
-
plus the shared context
|
|
132
|
+
plus the shared context enabled by the config's `reads` and `typeAware`. Use it to give Jev what the file cannot show, such
|
|
131
133
|
as a schema another file holds, and name the key it adds in the description.
|
|
132
134
|
Nothing it returns is checked, so it can break its own rule. The rule gets
|
|
133
135
|
requests of its own, repeating the shared context. Hook-owned external data
|
|
@@ -153,7 +155,8 @@ export default defineConfig({
|
|
|
153
155
|
presets: ["typescript", "security"], // built-in rule sets, or topics such as "effect/basics"
|
|
154
156
|
threshold: 0.8, // default 0.8
|
|
155
157
|
sufficiencyThreshold: 0.6, // below it, a finding warns the file and shared context may not show enough
|
|
156
|
-
reads: ["workspacePackages"
|
|
158
|
+
reads: ["workspacePackages"], // default; [] omits workspace context
|
|
159
|
+
typeAware: false, // default; true or --type-aware enables both symbols and references
|
|
157
160
|
exclude: ["**/generated/**"], // files no rule judges
|
|
158
161
|
includeComments: false, // true only for rules about comments
|
|
159
162
|
overrides: {
|
|
@@ -163,14 +166,16 @@ export default defineConfig({
|
|
|
163
166
|
});
|
|
164
167
|
```
|
|
165
168
|
|
|
166
|
-
Top-level `reads`
|
|
167
|
-
rules, including presets.
|
|
168
|
-
`[
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
do not
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
Top-level `reads` chooses workspace context for every audited file and all its
|
|
170
|
+
rules, including presets. It defaults to `["workspacePackages"]`, even without
|
|
171
|
+
a config file; `reads: []` omits that map. `typeAware` defaults to `false`.
|
|
172
|
+
Opting in with `typeAware: true` or `--type-aware` enables both `symbols` and
|
|
173
|
+
`references`, regardless of `reads`. Legacy native-reader entries in `reads`
|
|
174
|
+
alone do not enable the compiler; leaving them out does not disable it when
|
|
175
|
+
type awareness is on. Code alone requires `reads: []` and type awareness off.
|
|
176
|
+
Presets do not own or override this selection. Rule-level `reads`, in Markdown
|
|
177
|
+
front matter, `defineRule`, or inline rules, is rejected with guidance to move
|
|
178
|
+
it to the config. The context maps are:
|
|
174
179
|
|
|
175
180
|
- `workspacePackages`: only packages matched by this file's literal imports,
|
|
176
181
|
as `{ canonicalPackageName: directory }`. Subpaths match the owning package;
|
|
@@ -184,10 +189,14 @@ the config. The available names are:
|
|
|
184
189
|
and narrowing types stay distinct; docs/tags appear once per canonical symbol
|
|
185
190
|
per file. This is not literal IDE hover text: there are no IDE labels or
|
|
186
191
|
selected-call overload presentation. Unused local declarations may have type
|
|
187
|
-
blocks too.
|
|
188
|
-
|
|
189
|
-
-
|
|
190
|
-
|
|
192
|
+
blocks too. Standard-library and Node/Bun built-ins and their methods are
|
|
193
|
+
omitted; project and third-party symbols remain, including built-in-name
|
|
194
|
+
shadows. Built-in type names can still appear inside meaningful signatures.
|
|
195
|
+
- `references`: a flat `Record<string, string>`, as
|
|
196
|
+
`{ "Name:path/to/file.ts": "declarationSnippet" }`, for declarations of symbols
|
|
197
|
+
used by the target code, not incoming call sites. Keys retain names as used,
|
|
198
|
+
including import aliases, and repository-relative pathname casing; alias targets
|
|
199
|
+
are resolved canonically. Each snippet is the smallest
|
|
191
200
|
complete AST declaration scope, with no extra surrounding lines: full function
|
|
192
201
|
bodies; whole arrow/object initializer variable statements, including
|
|
193
202
|
`export const` and semicolons; whole methods/getters/setters, not their classes;
|
|
@@ -201,8 +210,10 @@ the config. The available names are:
|
|
|
201
210
|
exclusions remain. There are no counts, incoming callers, structured locations,
|
|
202
211
|
unresolved lists, full-file collection, or completeness metadata.
|
|
203
212
|
|
|
204
|
-
|
|
205
|
-
native TypeScript compiler through the npm/Bun
|
|
213
|
+
When type awareness is enabled, npm-installed lint uses both `symbols` and
|
|
214
|
+
`references` with adhere's pinned native TypeScript compiler through the npm/Bun
|
|
215
|
+
launcher. Enabled context maps include a self-describing `__comment` in Jev's
|
|
216
|
+
state; top-level guidance identifies only `code` as the audit target. No root `tsconfig.json`
|
|
206
217
|
or root file-list membership is required. During execution, native TypeScript
|
|
207
218
|
opens files with pending judgments or cached findings needing location and
|
|
208
219
|
discovers each file's configured project, including package
|
|
@@ -243,27 +254,32 @@ program graphs can still consume substantial time/memory. Bounding active
|
|
|
243
254
|
documents/snapshots does not make total memory or model payload size constant,
|
|
244
255
|
and oversized contexts are still possible. Context is queried just
|
|
245
256
|
before judge/locate and reused locally. Planning never calls `SourceReads`.
|
|
246
|
-
|
|
247
|
-
binaries
|
|
248
|
-
|
|
249
|
-
|
|
257
|
+
Type-aware source checkouts use their development compiler dependency. Raw
|
|
258
|
+
GitHub binaries have no compiler resources, but run the default compiler-free
|
|
259
|
+
audit with workspace context without special configuration. Use `reads: []`
|
|
260
|
+
for code alone, with type awareness off; use the package launcher or a checkout
|
|
261
|
+
for type-aware execution. Plan-only `--limit 0` needs no compiler or API key.
|
|
250
262
|
|
|
251
263
|
Rules without `appendState` share requests and pay for shared input context
|
|
252
264
|
once per request, not once per rule. Context repeats on request splitting,
|
|
253
265
|
locating findings, and hook requests. Compiler documentation and complete declaration
|
|
254
|
-
scopes add
|
|
266
|
+
scopes add token/privacy cost only when type awareness is enabled, including
|
|
267
|
+
source from files not judged.
|
|
255
268
|
Complete scopes may contain substantially more source/tokens than fixed-line
|
|
256
269
|
excerpts; function bodies are not peeked or capped to seven lines.
|
|
257
270
|
Compiler-provided docs and JSDoc tags can remain present with ordinary comments
|
|
258
|
-
stripped.
|
|
259
|
-
|
|
271
|
+
stripped. Keep `typeAware` off to omit both native maps; `reads` controls only
|
|
272
|
+
workspace context. Use `reads: []` with type awareness off for code alone.
|
|
260
273
|
Plan token/cost estimates include code and cheap workspace metadata only, not
|
|
261
274
|
native context or hook-added data. Actual cost and request splits may therefore
|
|
262
275
|
be higher. Shared state plus any one question must fit 32k tokens, and state
|
|
263
276
|
plus all questions in a request must fit 64k, with 1k reserved for overhead.
|
|
264
|
-
Execution splits question batches
|
|
265
|
-
|
|
266
|
-
`
|
|
277
|
+
Execution splits question batches and checks full budgets before HTTP. On
|
|
278
|
+
context overflow, it retries once without `references` when that map is present,
|
|
279
|
+
keeping target code and `symbols` intact; a further overflow skips the file.
|
|
280
|
+
Complete declaration scopes are not shortened to fit. Symbol strings have
|
|
281
|
+
configurable `maxSymbolLength` and `maxSymbolDocumentationLength` limits, both
|
|
282
|
+
256 by default. Only `appendState` forces a private rule request group.
|
|
267
283
|
|
|
268
284
|
`overrides` changes any rule by the id the report shows, a preset's with the
|
|
269
285
|
preset first, without copying it; an id no rule has refuses the run. `rules`
|
|
@@ -330,8 +346,8 @@ re-judges that rule; a threshold change re-judges nothing.
|
|
|
330
346
|
|
|
331
347
|
Dependencies, related source, reader selection, shared read-context contents,
|
|
332
348
|
and hook-owned external data are deliberately not fingerprinted. If `A.ts` imports
|
|
333
|
-
`B.ts`, changing `B.ts` does not invalidate `A.ts`. Changing config `reads`
|
|
334
|
-
not refresh its cached answers either. This keeps planning cheap but accepts
|
|
349
|
+
`B.ts`, changing `B.ts` does not invalidate `A.ts`. Changing config `reads` or
|
|
350
|
+
`typeAware` does not refresh its cached answers either. This keeps planning cheap but accepts
|
|
335
351
|
stale dependency/context judgments.
|
|
336
352
|
|
|
337
353
|
`planAudit` reads/counts/hashes code, checks the cache, and makes code plus cheap
|
|
@@ -344,8 +360,8 @@ budgeting, native reads, and HTTP execution.
|
|
|
344
360
|
Source reload/hash work is bounded to 8 files; plans retain compact metadata and
|
|
345
361
|
estimates, not all file contents. Only sampled-linter files need a planning
|
|
346
362
|
reread. After confirmation, execution reloads request-bearing files and cached
|
|
347
|
-
finding excerpts, preparing
|
|
348
|
-
cached-unlocated files just before judge/locate. Every reload validates normalized
|
|
363
|
+
finding excerpts, preparing native context only when type awareness is enabled,
|
|
364
|
+
for pending or cached-unlocated files just before judge/locate. Every reload validates normalized
|
|
349
365
|
code and ignore directives against the active plan; changes refuse with rerun
|
|
350
366
|
guidance, without adding persistent-cache keys. A fully cached and located run
|
|
351
367
|
starts no compiler and needs no API key or Jev requests.
|
|
@@ -390,7 +406,8 @@ the file breaks the rule. On a terminal it is amber near its rule's
|
|
|
390
406
|
threshold, within a quarter of the room above it (0.80 to 0.84 at a threshold
|
|
391
407
|
of 0.80), and green above that: a rule whose findings are mostly amber turns
|
|
392
408
|
on where its threshold sits. `context` is its probability that the file and
|
|
393
|
-
shared context show enough to decide
|
|
409
|
+
shared context show enough to decide. Default context is `workspacePackages`;
|
|
410
|
+
`symbols` and `references` require `typeAware: true` or `--type-aware`.
|
|
394
411
|
It is colored the same way around the sufficiency threshold, 0.6
|
|
395
412
|
by default: amber from 0.50 to 0.69, red below. Below 0.6 the finding also
|
|
396
413
|
carries `warning: this file may not show enough to check this rule`. It turns
|
|
@@ -37,23 +37,30 @@ 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
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
Default lint reads code and `workspacePackages` without starting a compiler.
|
|
41
|
+
`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.
|
|
46
|
+
|
|
47
|
+
With type awareness enabled, native TypeScript starts only during execution,
|
|
48
|
+
after confirmation, when pending judgments or cached findings needing location
|
|
49
|
+
require context. It discovers each file's configured project, including package
|
|
50
|
+
projects under solution roots, and falls back to inferred context for
|
|
51
|
+
unconfigured or excluded files. No root `tsconfig.json` or literal root file-list
|
|
52
|
+
membership is required; do not block setup on either. `.adhere/tsconfig.json`
|
|
53
|
+
only types the config and rules. Planning, `--limit 0`, version, help, skills,
|
|
54
|
+
and validate do not start the compiler.
|
|
49
55
|
|
|
50
56
|
In a repo without a `package.json`, inspect any `adhere` on PATH with
|
|
51
57
|
`adhere --version`, then **Ask** whether to use the npm/Bun package launcher
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
or a raw executable for the same default compiler-free audit. The raw release
|
|
59
|
+
(`adhere-Windows-x86_64.exe` on Windows) needs no Node or package manager and
|
|
60
|
+
runs with workspace context without special configuration. It cannot run a
|
|
61
|
+
type-aware audit: use the package launcher or a checkout if the user opts in.
|
|
62
|
+
Do not silently turn off an explicitly chosen `typeAware` setting. Only fetch
|
|
63
|
+
the raw release after the user chooses that alternative:
|
|
57
64
|
|
|
58
65
|
```sh
|
|
59
66
|
curl -fsSL --create-dirs -o ~/.local/bin/adhere \
|
|
@@ -139,10 +146,11 @@ the wording of any. Revise drafts the user wants changed and show them again.
|
|
|
139
146
|
- Copy each chosen draft to `.adhere/rules/<topic>/<slug>/RULE.md`.
|
|
140
147
|
- Add chosen presets, or topics, to `presets` in `.adhere/config.ts`. Presets
|
|
141
148
|
use the audit-wide readers; they do not own or override `reads`.
|
|
142
|
-
- Leave `reads` omitted for
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
- Leave `reads` omitted for default workspace context and `typeAware` off
|
|
150
|
+
unless the user explicitly chooses type-aware execution. `reads: []` omits
|
|
151
|
+
workspace context, not native maps. Only replace `reads` if the user chooses
|
|
152
|
+
to limit workspace context. If requested type awareness lacks compiler resources,
|
|
153
|
+
use the package launcher or a checkout; do not silently turn it off.
|
|
146
154
|
- Install chosen shared rules: `adhere install org/repo/<id>` for each, or
|
|
147
155
|
`adhere install org/repo` for all of them. They land in `.adhere/rules/org/repo/`.
|
|
148
156
|
- Add `exclude` globs for generated or vendored code the default skips miss.
|
|
@@ -177,19 +185,21 @@ cost, and request splits may be higher. Source reload/hash work is bounded to
|
|
|
177
185
|
for execution; if normalized code or ignore
|
|
178
186
|
directives changed since planning, rerun rather than using the old plan.
|
|
179
187
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
scopes from files outside the audit filter.
|
|
183
|
-
tokens than fixed-line excerpts
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
this file's matched literal
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
If the user opts into type awareness, explain that both compiler type/docs
|
|
189
|
+
strings and outgoing declaration snippets add source/documentation sharing and
|
|
190
|
+
token cost, including complete scopes from files outside the audit filter.
|
|
191
|
+
Whole declaration bodies can use more tokens than fixed-line excerpts. Context
|
|
192
|
+
overflow gets one retry without `references`, keeping target code and `symbols`
|
|
193
|
+
intact; it can still overflow and skip the file. Declaration bodies are not
|
|
194
|
+
shortened to fit. Compiler documentation and JSDoc tags can remain even when
|
|
195
|
+
ordinary code comments are stripped, subject to the configured symbol limits.
|
|
196
|
+
The global workspace catalog is not sent: only this file's matched literal
|
|
197
|
+
imports appear under canonical package keys.
|
|
198
|
+
Offer type awareness only as an explicit opt-in, or `reads: []` with type
|
|
199
|
+
awareness off for code alone; never apply either silently. Neither `reads`,
|
|
200
|
+
`typeAware`, nor dependency/shared-context changes invalidate cached judgments.
|
|
201
|
+
If they want fresh answers under changed context, **Ask** before clearing
|
|
202
|
+
`.adhere/cache/`; do not clear it automatically.
|
|
193
203
|
|
|
194
204
|
On a large repo, offer a subtree such as `--filter 'src/**'`, or a file cap
|
|
195
205
|
such as `--limit 100`: the first 100 files needing requests in path order, with
|
|
@@ -201,7 +211,9 @@ or HTTP-size skips after selection consume a slot without runtime refill.
|
|
|
201
211
|
|
|
202
212
|
Explain that a file cap is not an HTTP-request or token/cost cap: each file can
|
|
203
213
|
produce judge, locate, split-batch, and private hook requests. **Ask** before
|
|
204
|
-
sending, then run the command below with only the user's chosen filter/file cap
|
|
214
|
+
sending, then run the command below with only the user's chosen filter/file cap
|
|
215
|
+
and `--type-aware` if they explicitly opted in, or retain `typeAware: true` in
|
|
216
|
+
their config:
|
|
205
217
|
|
|
206
218
|
```sh
|
|
207
219
|
adhere lint --yes
|
|
@@ -209,11 +221,14 @@ adhere lint --yes
|
|
|
209
221
|
|
|
210
222
|
`SourceReads.prepare(paths)` registers eligible-path metadata only, with no
|
|
211
223
|
file I/O, compiler/SDK import, spawn, snapshot, or semantic query, even for a large list.
|
|
212
|
-
Only after confirmation do pending judgments
|
|
213
|
-
location trigger actual per-file native reads
|
|
224
|
+
Only with type awareness enabled, and after confirmation, do pending judgments
|
|
225
|
+
or cached findings still needing location trigger actual per-file native reads
|
|
226
|
+
just before judge/locate. Native
|
|
214
227
|
transactions are serial through one permit; HTTP execution remains concurrent
|
|
215
228
|
at 8. Returned context is reused locally and full budgets are enforced before
|
|
216
|
-
HTTP
|
|
229
|
+
HTTP. Oversized context gets one retry without references when present, then
|
|
230
|
+
skips the file if it still does not fit; code and declaration bodies are not
|
|
231
|
+
shortened to fit. Shared state plus
|
|
217
232
|
one question must fit 32k tokens and state plus all questions must fit 64k per
|
|
218
233
|
request, with 1k reserved for overhead. The SDK may still parse a full selected
|
|
219
234
|
project, and additional monorepo programs may increase startup time and memory.
|
|
@@ -223,9 +238,13 @@ blocks for same-name binding/narrowing contexts, with compiler documentation
|
|
|
223
238
|
and JSDoc tags once per canonical symbol per file. It does not promise literal
|
|
224
239
|
IDE labels or selected-call overload presentation. Symbol/type queries are
|
|
225
240
|
batched, and independent SDK requests are chunked to at most 8 within the single
|
|
226
|
-
active native document/snapshot; HTTP work remains concurrent at 8.
|
|
227
|
-
|
|
228
|
-
|
|
241
|
+
active native document/snapshot; HTTP work remains concurrent at 8. Standard-library
|
|
242
|
+
and Node/Bun built-ins are omitted, but project and third-party names remain.
|
|
243
|
+
`references` is a flat `Record<string, string>` keyed by
|
|
244
|
+
`symbol:repository-relative pathname`, retaining import alias names and original
|
|
245
|
+
pathname casing. Values are declarations of symbols used by the target code,
|
|
246
|
+
not incoming call sites, selecting the smallest complete AST scope without extra
|
|
247
|
+
surrounding lines. Functions include full bodies; arrow/object initializer variables include
|
|
229
248
|
whole variable statements with `export const` and semicolons. Methods, getters,
|
|
230
249
|
and setters include their whole member, not the entire class; classes,
|
|
231
250
|
interfaces, and type aliases include the whole declaration. Repeated/overlapping
|
|
@@ -235,7 +254,10 @@ not automatically expanded. Scopes preserve indentation and the comment/directiv
|
|
|
235
254
|
policy, including associated JSDoc when `includeComments` is true; compiler
|
|
236
255
|
docs/tags in `symbols` remain available regardless. Current-file, declaration-file, `node_modules`, and
|
|
237
256
|
outside-root exclusions are unchanged. There are no incoming callers, counts, structured locations,
|
|
238
|
-
unresolved lists, full-file collection, or completeness markers.
|
|
257
|
+
unresolved lists, full-file collection, or completeness markers. Enabled context
|
|
258
|
+
maps include a self-describing `__comment`; top-level guidance identifies only
|
|
259
|
+
`code` as the audit target. Both native maps require the same type-awareness
|
|
260
|
+
opt-in; `reads` cannot select or disable them individually.
|
|
239
261
|
|
|
240
262
|
The first actual read lazily starts one native TypeScript LSP process with its
|
|
241
263
|
public SDK API pipe; both are pinned to exactly `7.1.0-dev.20261009.1` and must
|
|
@@ -332,15 +354,16 @@ jobs:
|
|
|
332
354
|
Pin the versions the repo already uses: bun's `bun-version`, Node's
|
|
333
355
|
`node-version` from `.nvmrc` or `engines`, and pnpm's from `packageManager`,
|
|
334
356
|
which `pnpm/action-setup` reads. Keep optional dependencies enabled so adhere's
|
|
335
|
-
platform package and its compiler dependency are installed.
|
|
336
|
-
configs and dependencies supply configured context when available;
|
|
337
|
-
no configured project or excluded from one use inferred context. Do not
|
|
338
|
-
or generate a root `tsconfig.json` merely to make default lint run.
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
chose
|
|
342
|
-
|
|
343
|
-
|
|
357
|
+
platform package and its compiler dependency are installed. For type-aware audits,
|
|
358
|
+
existing project configs and dependencies supply configured context when available;
|
|
359
|
+
files with no configured project or excluded from one use inferred context. Do not
|
|
360
|
+
require or generate a root `tsconfig.json` merely to make default lint run. Both
|
|
361
|
+
package launchers and raw executables support the default compiler-free audit,
|
|
362
|
+
including in repos without a `package.json`; no `reads` override is required.
|
|
363
|
+
If the user explicitly chose type-aware execution, use the package launcher or a
|
|
364
|
+
checkout; do not silently turn it off. For the default compiler-free audit, you can
|
|
365
|
+
instead skip runtime setup/install and fetch the raw executable at the version
|
|
366
|
+
the user ran:
|
|
344
367
|
|
|
345
368
|
```yaml
|
|
346
369
|
- run: |
|
package/src/cli.ts
CHANGED
|
@@ -59,6 +59,10 @@ import skill from "../skills/adhere/SKILL.md" with { type: "text" };
|
|
|
59
59
|
import fixSkill from "../skills/adhere-fix/SKILL.md" with { type: "text" };
|
|
60
60
|
import setupSkill from "../skills/adhere-setup/SKILL.md" with { type: "text" };
|
|
61
61
|
|
|
62
|
+
// @adhere This file owns CLI flags, progress output, and command wiring. Audit execution,
|
|
63
|
+
// scaffolding, rule installation, credential storage, and rule checks are implemented in
|
|
64
|
+
// their own modules imported above; the commands here connect them to the CLI.
|
|
65
|
+
|
|
62
66
|
class FindingsReported extends Schema.TaggedError<FindingsReported>()("FindingsReported", {
|
|
63
67
|
count: Schema.Finite,
|
|
64
68
|
}) {
|
|
@@ -114,6 +118,13 @@ const sufficiencyThreshold = Flag.Finite("sufficiency-threshold").pipe(
|
|
|
114
118
|
),
|
|
115
119
|
);
|
|
116
120
|
|
|
121
|
+
const typeAware = Flag.Boolean("type-aware").pipe(
|
|
122
|
+
Flag.optional,
|
|
123
|
+
Flag.withDescription(
|
|
124
|
+
"Include compiler-resolved symbols and declaration excerpts. Off by default.",
|
|
125
|
+
),
|
|
126
|
+
);
|
|
127
|
+
|
|
117
128
|
const limit = Flag.Int("limit").pipe(
|
|
118
129
|
Flag.filter(
|
|
119
130
|
(files) => files >= 0,
|
|
@@ -235,7 +246,7 @@ export const auditLayer = (flags: Flags, filter: ReadonlyArray<string> = []) =>
|
|
|
235
246
|
/** `adhere lint`: the audit. */
|
|
236
247
|
export const lintCommand = Command.make(
|
|
237
248
|
"lint",
|
|
238
|
-
{ preset, threshold, sufficiencyThreshold, yes, limit, rpm, filter, denyWarnings },
|
|
249
|
+
{ preset, threshold, sufficiencyThreshold, typeAware, yes, limit, rpm, filter, denyWarnings },
|
|
239
250
|
(input) =>
|
|
240
251
|
Effect.gen(function* () {
|
|
241
252
|
const stdio = yield* Stdio.Stdio;
|
|
@@ -298,6 +309,7 @@ export const lintCommand = Command.make(
|
|
|
298
309
|
presets: input.preset,
|
|
299
310
|
threshold: Option.getOrUndefined(input.threshold),
|
|
300
311
|
sufficiencyThreshold: Option.getOrUndefined(input.sufficiencyThreshold),
|
|
312
|
+
typeAware: Option.getOrUndefined(input.typeAware),
|
|
301
313
|
rpm: Option.getOrUndefined(input.rpm),
|
|
302
314
|
},
|
|
303
315
|
input.filter,
|
package/src/config.ts
CHANGED
|
@@ -220,8 +220,10 @@ export const AdhereConfig = Schema.Struct({
|
|
|
220
220
|
presets: Schema.Array(Schema.Literals(presetNames)).pipe(
|
|
221
221
|
Schema.withDecodingDefaultKey(Effect.succeed([])),
|
|
222
222
|
),
|
|
223
|
-
/**
|
|
223
|
+
/** Workspace context shared by every rule. Defaults to workspacePackages; [] disables it. Native read names do not enable type awareness. */
|
|
224
224
|
reads: Schema.optionalKey(Reads),
|
|
225
|
+
/** Include both compiler-resolved symbols and declaration excerpts. Defaults to false. */
|
|
226
|
+
typeAware: Schema.optionalKey(Schema.Boolean),
|
|
225
227
|
/** Maximum characters of each symbol's complete resolved content. Defaults to 256. */
|
|
226
228
|
maxSymbolLength: Schema.optionalKey(Schema.Natural),
|
|
227
229
|
/** Maximum characters of documentation and JSDoc tags per symbol. Defaults to 256; 0 omits them. */
|
|
@@ -333,7 +335,7 @@ export type Loaded<T extends { readonly rules?: unknown }> = Omit<T, "rules"> &
|
|
|
333
335
|
export const DEFAULT_MODEL = "jev-latest";
|
|
334
336
|
export const DEFAULT_THRESHOLD = 0.8;
|
|
335
337
|
export const DEFAULT_SUFFICIENCY_THRESHOLD = 0.6;
|
|
336
|
-
export const DEFAULT_READS: typeof Reads.Type = ["workspacePackages"
|
|
338
|
+
export const DEFAULT_READS: typeof Reads.Type = ["workspacePackages"];
|
|
337
339
|
export const DEFAULT_MAX_SYMBOL_DOCUMENTATION_LENGTH = 256;
|
|
338
340
|
export const DEFAULT_MAX_SYMBOL_LENGTH = 256;
|
|
339
341
|
|
|
@@ -355,6 +357,8 @@ export interface ResolvedConfig {
|
|
|
355
357
|
readonly includeComments?: boolean;
|
|
356
358
|
/** Built-in context shared by every rule, with the default applied. */
|
|
357
359
|
readonly reads: typeof Reads.Type;
|
|
360
|
+
/** Whether both native symbol and declaration context are enabled. */
|
|
361
|
+
readonly typeAware: boolean;
|
|
358
362
|
/** Maximum characters of each symbol's complete resolved content. */
|
|
359
363
|
readonly maxSymbolLength: number;
|
|
360
364
|
/** Maximum characters of documentation and JSDoc tags before the complete symbol limit applies. */
|
|
@@ -376,6 +380,8 @@ export interface Flags {
|
|
|
376
380
|
readonly sufficiencyThreshold?: number | undefined;
|
|
377
381
|
/** Throttles requests to Jev to at most this many a minute. */
|
|
378
382
|
readonly rpm?: number | undefined;
|
|
383
|
+
/** Replaces the config's native-context opt-in. */
|
|
384
|
+
readonly typeAware?: boolean | undefined;
|
|
379
385
|
}
|
|
380
386
|
|
|
381
387
|
/**
|
|
@@ -410,6 +416,7 @@ export const resolveConfig = (
|
|
|
410
416
|
});
|
|
411
417
|
const last = <K extends "model" | "threshold">(key: K) =>
|
|
412
418
|
applied.map((preset) => preset[key]).findLast((value) => value !== undefined);
|
|
419
|
+
const typeAware = flags.typeAware ?? config.typeAware ?? false;
|
|
413
420
|
return {
|
|
414
421
|
model: config.provider?.model ?? config.model ?? last("model") ?? DEFAULT_MODEL,
|
|
415
422
|
...(config.provider === undefined ? {} : { provider: config.provider }),
|
|
@@ -420,7 +427,11 @@ export const resolveConfig = (
|
|
|
420
427
|
...(flags.rpm === undefined ? {} : { rpm: flags.rpm }),
|
|
421
428
|
...(config.exclude === undefined ? {} : { exclude: config.exclude }),
|
|
422
429
|
...(config.includeComments === undefined ? {} : { includeComments: config.includeComments }),
|
|
423
|
-
|
|
430
|
+
typeAware,
|
|
431
|
+
reads: [
|
|
432
|
+
...(config.reads ?? DEFAULT_READS).filter((read) => read === "workspacePackages"),
|
|
433
|
+
...(typeAware ? (["symbols", "references"] as const) : []),
|
|
434
|
+
],
|
|
424
435
|
maxSymbolLength: config.maxSymbolLength ?? DEFAULT_MAX_SYMBOL_LENGTH,
|
|
425
436
|
maxSymbolDocumentationLength:
|
|
426
437
|
config.maxSymbolDocumentationLength ?? DEFAULT_MAX_SYMBOL_DOCUMENTATION_LENGTH,
|
package/src/init.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, relative } from "node:path";
|
|
4
4
|
import { CONFIG_FILES } from "#config.ts";
|
|
5
|
-
import { Effect } from "effect";
|
|
5
|
+
import { Effect, Schema } from "effect";
|
|
6
6
|
|
|
7
7
|
export interface InitResult {
|
|
8
8
|
readonly created: ReadonlyArray<string>;
|
|
@@ -305,6 +305,13 @@ const writeScaffoldFile = (root: string, path: string, contents: string, options
|
|
|
305
305
|
|
|
306
306
|
const PACKAGE = "@drkmttr/adhere";
|
|
307
307
|
|
|
308
|
+
const PackageManifest = Schema.fromJsonString(
|
|
309
|
+
Schema.Struct({
|
|
310
|
+
dependencies: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
|
|
311
|
+
devDependencies: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
|
|
312
|
+
}),
|
|
313
|
+
);
|
|
314
|
+
|
|
308
315
|
type PackageManager = "bun" | "pnpm" | "yarn" | "npm";
|
|
309
316
|
|
|
310
317
|
/** The first lockfile at the root names the package manager; npm when there is none. */
|
|
@@ -342,9 +349,21 @@ const addFlags: Record<PackageManager, ReadonlyArray<string>> = {
|
|
|
342
349
|
const installPackages = (root: string, shared: boolean) =>
|
|
343
350
|
Effect.tryPromise({
|
|
344
351
|
try: async (): Promise<Install> => {
|
|
345
|
-
const manifest = await readFile(join(root, "package.json"), "utf8").catch(
|
|
352
|
+
const manifest = await readFile(join(root, "package.json"), "utf8").catch(
|
|
353
|
+
(cause: unknown) => {
|
|
354
|
+
if (
|
|
355
|
+
typeof cause === "object" &&
|
|
356
|
+
cause !== null &&
|
|
357
|
+
"code" in cause &&
|
|
358
|
+
cause.code === "ENOENT"
|
|
359
|
+
) {
|
|
360
|
+
return undefined;
|
|
361
|
+
}
|
|
362
|
+
throw cause;
|
|
363
|
+
},
|
|
364
|
+
);
|
|
346
365
|
if (manifest === undefined) return { status: "no-package-json" };
|
|
347
|
-
const { dependencies, devDependencies } =
|
|
366
|
+
const { dependencies, devDependencies } = Schema.decodeUnknownSync(PackageManifest)(manifest);
|
|
348
367
|
if (
|
|
349
368
|
!shared &&
|
|
350
369
|
(dependencies?.[PACKAGE] !== undefined || devDependencies?.[PACKAGE] !== undefined)
|
package/src/install.ts
CHANGED
|
@@ -37,30 +37,35 @@ const failed = (what: string) => (cause: unknown) =>
|
|
|
37
37
|
const cloned = (source: Source) =>
|
|
38
38
|
Effect.acquireRelease(
|
|
39
39
|
Effect.tryPromise({
|
|
40
|
-
try:
|
|
41
|
-
|
|
42
|
-
const branch = source.ref === undefined ? [] : ["--branch", source.ref];
|
|
43
|
-
const url = `https://github.com/${source.repo}.git`;
|
|
44
|
-
await new Promise<void>((resolve, reject) => {
|
|
45
|
-
let stderr = "";
|
|
46
|
-
spawn("git", ["clone", "--quiet", "--depth", "1", ...branch, url, directory], {
|
|
47
|
-
stdio: ["inherit", "ignore", "pipe"],
|
|
48
|
-
})
|
|
49
|
-
.on("error", reject)
|
|
50
|
-
.on("exit", (code) =>
|
|
51
|
-
code === 0
|
|
52
|
-
? resolve()
|
|
53
|
-
: reject(new Error(stderr.trim() || `git exited with ${code}`)),
|
|
54
|
-
)
|
|
55
|
-
.stderr?.on("data", (chunk) => {
|
|
56
|
-
stderr += chunk;
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
return directory;
|
|
60
|
-
},
|
|
61
|
-
catch: failed(`Could not clone ${source.repo}`),
|
|
40
|
+
try: () => mkdtemp(join(tmpdir(), "adhere-install-")),
|
|
41
|
+
catch: failed(`Could not create a temporary directory for ${source.repo}`),
|
|
62
42
|
}),
|
|
63
43
|
(directory) => Effect.promise(() => rm(directory, { recursive: true, force: true })),
|
|
44
|
+
).pipe(
|
|
45
|
+
Effect.tap((directory) =>
|
|
46
|
+
Effect.tryPromise({
|
|
47
|
+
try: async () => {
|
|
48
|
+
const branch = source.ref === undefined ? [] : ["--branch", source.ref];
|
|
49
|
+
const url = `https://github.com/${source.repo}.git`;
|
|
50
|
+
await new Promise<void>((resolve, reject) => {
|
|
51
|
+
let stderr = "";
|
|
52
|
+
spawn("git", ["clone", "--quiet", "--depth", "1", ...branch, url, directory], {
|
|
53
|
+
stdio: ["inherit", "ignore", "pipe"],
|
|
54
|
+
})
|
|
55
|
+
.on("error", reject)
|
|
56
|
+
.on("exit", (code) =>
|
|
57
|
+
code === 0
|
|
58
|
+
? resolve()
|
|
59
|
+
: reject(new Error(stderr.trim() || `git exited with ${code}`)),
|
|
60
|
+
)
|
|
61
|
+
.stderr?.on("data", (chunk) => {
|
|
62
|
+
stderr += chunk;
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
catch: failed(`Could not clone ${source.repo}`),
|
|
67
|
+
}),
|
|
68
|
+
),
|
|
64
69
|
);
|
|
65
70
|
|
|
66
71
|
/**
|
|
@@ -103,7 +108,8 @@ export const listRules = (source: Source) =>
|
|
|
103
108
|
const rules = yield* rulesIn(source, yield* cloned(source));
|
|
104
109
|
return yield* Effect.forEach(rules, ({ id, name, directory }) =>
|
|
105
110
|
Effect.gen(function* () {
|
|
106
|
-
if (name === "RULE.ts")
|
|
111
|
+
if (name === "RULE.ts")
|
|
112
|
+
return { id, description: "(TypeScript, which list does not run)" };
|
|
107
113
|
const text = yield* Effect.tryPromise({
|
|
108
114
|
try: () => readFile(join(directory, name), "utf8"),
|
|
109
115
|
catch: failed(`Could not read ${id}`),
|
|
@@ -76,6 +76,7 @@ const recordedJev = (
|
|
|
76
76
|
Layer.provide([
|
|
77
77
|
Layer.succeed(FetchHttpClient.Fetch, fetch),
|
|
78
78
|
Layer.succeed(AdhereConfig, {
|
|
79
|
+
typeAware: false,
|
|
79
80
|
model: "jev-latest",
|
|
80
81
|
threshold: 0.8,
|
|
81
82
|
sufficiencyThreshold: 0.6,
|
|
@@ -493,9 +494,10 @@ describe("prepared shared Jev reads", () => {
|
|
|
493
494
|
expect(readState.code).toEqual({});
|
|
494
495
|
});
|
|
495
496
|
|
|
496
|
-
it("
|
|
497
|
+
it("uses only caller-prepared context in HTTP with workspace-only defaults", async () => {
|
|
497
498
|
const config = resolveConfig({ presets: [], rules: { plain } });
|
|
498
|
-
expect(config.reads).toEqual(["workspacePackages"
|
|
499
|
+
expect(config.reads).toEqual(["workspacePackages"]);
|
|
500
|
+
expect(config.typeAware).toBe(false);
|
|
499
501
|
const { sent, layer } = recordedJev({ unused: "packages/unused" }, config.reads);
|
|
500
502
|
await Effect.runPromise(
|
|
501
503
|
Effect.gen(function* () {
|
package/src/services/Jev.ts
CHANGED
|
@@ -141,7 +141,7 @@ export const tokensOf = (value: unknown): number => Math.ceil(bytesOf(value) / 3
|
|
|
141
141
|
|
|
142
142
|
const READ_COMMENTS: Readonly<Record<string, string>> = {
|
|
143
143
|
symbols:
|
|
144
|
-
"Compiler-
|
|
144
|
+
"Compiler-rendered types and documentation for symbols used in `code`; keys are symbol names. A declared type, assertion, or type predicate is only a compile-time claim, not proof of runtime validation. When `references` show an implementation, inspect its actual checks. A predicate that returns true, tests only truthiness, or checks only whether a value is an object does not validate the fields it claims.",
|
|
145
145
|
references:
|
|
146
146
|
"Declarations of symbols used in `code`. Each key is `symbol:pathname`: the symbol name as used in `code` followed by its repository-relative declaration path. Each value is a complete declaration excerpt.",
|
|
147
147
|
workspacePackages:
|
package/src/workflows/audit.ts
CHANGED
|
@@ -387,8 +387,8 @@ export const planAudit = (
|
|
|
387
387
|
? (config.workspacePackages ?? {})
|
|
388
388
|
: undefined;
|
|
389
389
|
const nativeReads: SourceReadRequest = {
|
|
390
|
-
symbols: config.
|
|
391
|
-
references: config.
|
|
390
|
+
symbols: config.typeAware,
|
|
391
|
+
references: config.typeAware,
|
|
392
392
|
includeComments: config.includeComments === true,
|
|
393
393
|
maxSymbolLength: config.maxSymbolLength,
|
|
394
394
|
maxSymbolDocumentationLength: config.maxSymbolDocumentationLength,
|