@drkmttr/adhere 0.15.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +313 -131
- package/bin/adhere.js +19 -4
- package/package.json +34 -12
- package/skills/adhere/SKILL.md +180 -31
- package/skills/adhere-setup/SKILL.md +142 -16
- package/src/cli.ts +56 -23
- package/src/config.ts +62 -16
- package/src/index.ts +14 -0
- package/src/markdown.ts +9 -5
- package/src/providers/cloudflare.ts +84 -0
- package/src/providers/jev.ts +66 -0
- package/src/providers/openai.ts +89 -0
- package/src/providers/post.ts +53 -0
- package/src/providers/provider.ts +131 -0
- package/src/providers/providers.test.ts +276 -0
- package/src/providers/systemOne.ts +95 -0
- package/src/services/AdhereConfig.ts +2 -4
- package/src/services/Credentials.ts +101 -51
- package/src/services/Jev.http.ts +184 -171
- package/src/services/Jev.provider.test.ts +483 -0
- package/src/services/Jev.reads.test.ts +516 -0
- package/src/services/Jev.ts +276 -120
- package/src/services/NativeLsp.ts +289 -0
- package/src/services/SourceReads.test.ts +1289 -0
- package/src/services/SourceReads.ts +460 -0
- package/src/services/SourceWalker.ts +26 -59
- package/src/workflows/audit.ts +513 -267
- package/src/workflows/format.ts +74 -17
- package/src/workspaces.test.ts +73 -0
- package/src/workspaces.ts +91 -0
package/bin/adhere.js
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* `adhere` on PATH. Runs the executable compiled for this machine, which the
|
|
4
4
|
* install fetched as the optional dependency `@drkmttr/adhere-<platform>-<arch>`
|
|
5
|
-
* (see scripts/npm-packages.ts).
|
|
5
|
+
* (see scripts/npm-packages.ts). For semantic reads, passes the native TypeScript
|
|
6
|
+
* compiler package resolved from that executable's dependencies to the bundled
|
|
7
|
+
* SDK. A missing compiler does not block plain commands; the reads adapter
|
|
8
|
+
* validates it when needed. A checkout has no executable installed, so there it
|
|
6
9
|
* runs src/main.ts with Bun instead.
|
|
7
10
|
*/
|
|
8
11
|
import { spawnSync } from "node:child_process";
|
|
@@ -24,8 +27,20 @@ const resolveExecutable = () => {
|
|
|
24
27
|
}
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
const
|
|
28
|
-
|
|
30
|
+
const resolveCompilerPackage = (prebuilt) => {
|
|
31
|
+
try {
|
|
32
|
+
return createRequire(prebuilt).resolve(`@typescript/typescript-${platform}/package.json`);
|
|
33
|
+
} catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const run = (command, commandArgs, compilerPackage) => {
|
|
39
|
+
const env = { ...process.env };
|
|
40
|
+
// This private handoff must not let an inherited value redirect the compiler.
|
|
41
|
+
delete env.ADHERE_TYPESCRIPT_PACKAGE;
|
|
42
|
+
if (compilerPackage) env.ADHERE_TYPESCRIPT_PACKAGE = compilerPackage;
|
|
43
|
+
const result = spawnSync(command, commandArgs, { stdio: "inherit", env });
|
|
29
44
|
if (result.error) throw result.error;
|
|
30
45
|
if (result.signal) {
|
|
31
46
|
// A killed process says nothing itself, so say what killed it.
|
|
@@ -39,7 +54,7 @@ const run = (command, commandArgs) => {
|
|
|
39
54
|
};
|
|
40
55
|
|
|
41
56
|
const prebuilt = resolveExecutable();
|
|
42
|
-
if (prebuilt) run(prebuilt, args);
|
|
57
|
+
if (prebuilt) run(prebuilt, args, resolveCompilerPackage(prebuilt));
|
|
43
58
|
else if (existsSync(new URL("../.git", import.meta.url)))
|
|
44
59
|
run("bun", [fileURLToPath(new URL("../src/main.ts", import.meta.url)), ...args]);
|
|
45
60
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drkmttr/adhere",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Lints a repository against the rules in .adhere/, judged by Jev.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,8 @@
|
|
|
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
|
-
"release": "release-it"
|
|
37
|
+
"release": "release-it",
|
|
38
|
+
"prepare": "vp config"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"effect": "4.0.0-rc.118"
|
|
@@ -44,12 +45,27 @@
|
|
|
44
45
|
"@types/bun": "^1.4.2",
|
|
45
46
|
"@types/node": "^24.13.6",
|
|
46
47
|
"release-it": "^21.1.0",
|
|
47
|
-
"typescript": "7.0.
|
|
48
|
-
"vite
|
|
49
|
-
"
|
|
48
|
+
"typescript": "7.1.0-dev.20261009.1",
|
|
49
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
|
|
50
|
+
"vite-plus": "1.1.0",
|
|
51
|
+
"vitest": "5.0.3"
|
|
50
52
|
},
|
|
51
53
|
"overrides": {
|
|
52
|
-
"@effect/platform-node-shared": "4.0.0-rc.118"
|
|
54
|
+
"@effect/platform-node-shared": "4.0.0-rc.118",
|
|
55
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
|
|
56
|
+
"vitest": "5.0.3"
|
|
57
|
+
},
|
|
58
|
+
"devEngines": {
|
|
59
|
+
"packageManager": {
|
|
60
|
+
"name": "bun",
|
|
61
|
+
"version": "1.4.2",
|
|
62
|
+
"onFail": "download"
|
|
63
|
+
},
|
|
64
|
+
"runtime": {
|
|
65
|
+
"name": "node",
|
|
66
|
+
"version": "24.21.0",
|
|
67
|
+
"onFail": "download"
|
|
68
|
+
}
|
|
53
69
|
},
|
|
54
70
|
"release-it": {
|
|
55
71
|
"$schema": "https://unpkg.com/release-it@21.1.0/schema/release-it.json",
|
|
@@ -64,14 +80,20 @@
|
|
|
64
80
|
},
|
|
65
81
|
"npm": {
|
|
66
82
|
"allowSameVersion": true,
|
|
67
|
-
"publish": false
|
|
83
|
+
"publish": false,
|
|
84
|
+
"versionArgs": [
|
|
85
|
+
"--force"
|
|
86
|
+
]
|
|
68
87
|
}
|
|
69
88
|
},
|
|
89
|
+
"versionArgs": [
|
|
90
|
+
"--force"
|
|
91
|
+
],
|
|
70
92
|
"optionalDependencies": {
|
|
71
|
-
"@drkmttr/adhere-darwin-arm64": "0.
|
|
72
|
-
"@drkmttr/adhere-darwin-x64": "0.
|
|
73
|
-
"@drkmttr/adhere-linux-arm64": "0.
|
|
74
|
-
"@drkmttr/adhere-linux-x64": "0.
|
|
75
|
-
"@drkmttr/adhere-win32-x64": "0.
|
|
93
|
+
"@drkmttr/adhere-darwin-arm64": "0.18.0",
|
|
94
|
+
"@drkmttr/adhere-darwin-x64": "0.18.0",
|
|
95
|
+
"@drkmttr/adhere-linux-arm64": "0.18.0",
|
|
96
|
+
"@drkmttr/adhere-linux-x64": "0.18.0",
|
|
97
|
+
"@drkmttr/adhere-win32-x64": "0.18.0"
|
|
76
98
|
}
|
|
77
99
|
}
|
package/skills/adhere/SKILL.md
CHANGED
|
@@ -24,7 +24,8 @@ adhere init --shared org/repo # a repo of rules other repos install, with CI
|
|
|
24
24
|
adhere login # save a TypeSafe AI API key; TYPESAFE_API_KEY takes precedence
|
|
25
25
|
adhere validate # rule wording, rules a linter could check, contradictions
|
|
26
26
|
adhere lint # judge the working directory; exit 1 on an error finding
|
|
27
|
-
adhere lint --limit 0 #
|
|
27
|
+
adhere lint --limit 0 # compiler-free counts/cache plan; no budgeting
|
|
28
|
+
adhere lint --limit 100 # first 100 files needing requests; all pending rules per file
|
|
28
29
|
adhere lint --yes # send without asking, as in CI
|
|
29
30
|
adhere lint --preset effect # add a built-in rule set; the config becomes optional
|
|
30
31
|
adhere lint --filter 'src/**' # read only matching files; repeat, and ! to leave out
|
|
@@ -35,7 +36,20 @@ adhere install org/repo[/topic[/rule]][#ref] # copy them into .adhere/rules/or
|
|
|
35
36
|
adhere skill [docs|setup|fix] # print a skill
|
|
36
37
|
```
|
|
37
38
|
|
|
38
|
-
`adhere <command> --help` lists every flag.
|
|
39
|
+
`adhere <command> --help` lists every flag. Version, help, skills, `validate`,
|
|
40
|
+
and planning with `--limit 0` do not start the native compiler. Default `lint`
|
|
41
|
+
starts it only during execution, after confirmation, for pending judgments or
|
|
42
|
+
cached findings still needing location.
|
|
43
|
+
|
|
44
|
+
`--limit` now counts files, not rule checks; use the same flag without an extra
|
|
45
|
+
unit selector. Each request-bearing file consumes one slot for all its pending
|
|
46
|
+
rules and cached-location work. Cached-locate-only files count; fully cached and
|
|
47
|
+
already-located files report without consuming slots. After the cap, both judge
|
|
48
|
+
and locate work wait. Known cheap-budget skips refill from later paths; native
|
|
49
|
+
or HTTP-size skips after selection consume a slot without runtime refill.
|
|
50
|
+
A file can generate judge, locate, split-batch, and private hook requests, so
|
|
51
|
+
this is not an HTTP-request or cost cap. Confirm the estimated cost and its
|
|
52
|
+
native-context caveat before execution.
|
|
39
53
|
|
|
40
54
|
## Rule files
|
|
41
55
|
|
|
@@ -106,28 +120,21 @@ Front matter besides `description`, all optional:
|
|
|
106
120
|
one line, scope a rule without widening its description: a finding stands
|
|
107
121
|
only when Jev says the code that breaks the rule is as every `appliesTo`
|
|
108
122
|
describes and as no `excludeIf` does.
|
|
109
|
-
- `reads: ["workspacePackages"]`, a JSON array on one line, names what Jev
|
|
110
|
-
reads for the rule beside the code, each under that name. There is one so
|
|
111
|
-
far, `workspacePackages`: each of the repository's own packages, by name,
|
|
112
|
-
with its directory, read from the root `package.json`'s `workspaces` or
|
|
113
|
-
`pnpm-workspace.yaml`. Use it for a rule that turns on whether an import is
|
|
114
|
-
of the repository's own package or an installed one, and name
|
|
115
|
-
`workspacePackages` in the description. A name adhere does not have refuses
|
|
116
|
-
the run. The rule costs a request of its own per file, and a changed
|
|
117
|
-
workspace judges it again.
|
|
118
123
|
|
|
119
124
|
A rule's directory can hold a `RULE.ts` instead, which default-exports
|
|
120
125
|
`defineRule({...})` from `@drkmttr/adhere`, with the fields a config's inline
|
|
121
126
|
rule takes:
|
|
122
127
|
`description`, `must`, `never`, and the rest. Its `appendState(state, file, Bun)`
|
|
123
128
|
runs right before each request for the rule, and what it returns is spread
|
|
124
|
-
over the request's state
|
|
125
|
-
|
|
129
|
+
over the request's state: `code`, the file's sections as Jev reads them,
|
|
130
|
+
plus the shared context selected by the config's `reads`. Use it to give Jev what the file cannot show, such
|
|
126
131
|
as a schema another file holds, and name the key it adds in the description.
|
|
127
132
|
Nothing it returns is checked, so it can break its own rule. The rule gets
|
|
128
|
-
requests of its own,
|
|
129
|
-
|
|
130
|
-
|
|
133
|
+
requests of its own, repeating the shared context. Hook-owned external data
|
|
134
|
+
changes are not tracked by the cache; neither are built-in read-context contents
|
|
135
|
+
or reader selection. Judgments are redone when own code, model, rule, matcher,
|
|
136
|
+
or hook source changes, or the user clears the cache. Helpers it imports go
|
|
137
|
+
beside it in its directory.
|
|
131
138
|
|
|
132
139
|
A rule a regex, an import check, or the type checker could flag every time
|
|
133
140
|
belongs in that tool, not adhere. `adhere validate` reports rules that look
|
|
@@ -145,7 +152,8 @@ import { defineConfig } from "@drkmttr/adhere";
|
|
|
145
152
|
export default defineConfig({
|
|
146
153
|
presets: ["typescript", "security"], // built-in rule sets, or topics such as "effect/basics"
|
|
147
154
|
threshold: 0.8, // default 0.8
|
|
148
|
-
sufficiencyThreshold: 0.6, // below it, a finding warns the file may not show enough
|
|
155
|
+
sufficiencyThreshold: 0.6, // below it, a finding warns the file and shared context may not show enough
|
|
156
|
+
reads: ["workspacePackages", "symbols", "references"], // default; omitting this field keeps all three
|
|
149
157
|
exclude: ["**/generated/**"], // files no rule judges
|
|
150
158
|
includeComments: false, // true only for rules about comments
|
|
151
159
|
overrides: {
|
|
@@ -155,6 +163,108 @@ export default defineConfig({
|
|
|
155
163
|
});
|
|
156
164
|
```
|
|
157
165
|
|
|
166
|
+
Top-level `reads` selects shared context for every audited file and all its
|
|
167
|
+
rules, including presets. `resolveConfig` defaults to
|
|
168
|
+
`["workspacePackages", "symbols", "references"]` when the field is omitted,
|
|
169
|
+
even without a config file. An explicit array replaces the default: `reads: []`
|
|
170
|
+
is the code-only opt-out, and a subset limits context to those readers. Presets
|
|
171
|
+
do not own or override this selection. Rule-level `reads`, in Markdown front
|
|
172
|
+
matter, `defineRule`, or inline rules, is rejected with guidance to move it to
|
|
173
|
+
the config. The available names are:
|
|
174
|
+
|
|
175
|
+
- `workspacePackages`: only packages matched by this file's literal imports,
|
|
176
|
+
as `{ canonicalPackageName: directory }`. Subpaths match the owning package;
|
|
177
|
+
the discovered workspace catalog is internal and never sent. The cheap lexer
|
|
178
|
+
recognizes static/type imports, re-exports, dynamic import and require calls,
|
|
179
|
+
including plain literal templates in calls. Computed modules/template
|
|
180
|
+
substitutions are skipped and `require` binding is not resolved: this is
|
|
181
|
+
cheap filtering, not semantic module resolution. No matches yields `{}`.
|
|
182
|
+
- `symbols`: `Record<string, string>`, mapping names to compiler-rendered type
|
|
183
|
+
blocks plus compiler documentation and JSDoc tags. Distinct same-name binding
|
|
184
|
+
and narrowing types stay distinct; docs/tags appear once per canonical symbol
|
|
185
|
+
per file. This is not literal IDE hover text: there are no IDE labels or
|
|
186
|
+
selected-call overload presentation. Unused local declarations may have type
|
|
187
|
+
blocks too. Library types/docs can appear even when their source is excluded
|
|
188
|
+
from snippets.
|
|
189
|
+
- `references`: `{ Name: { pathname: declarationSnippet } }` for used symbols'
|
|
190
|
+
canonical outgoing alias-target declarations. Each snippet is the smallest
|
|
191
|
+
complete AST declaration scope, with no extra surrounding lines: full function
|
|
192
|
+
bodies; whole arrow/object initializer variable statements, including
|
|
193
|
+
`export const` and semicolons; whole methods/getters/setters, not their classes;
|
|
194
|
+
and whole class/interface/type-alias declarations. Repeated/overlapping scopes
|
|
195
|
+
merge; disjoint scopes join with `…`. This is syntactic selection, not recursive
|
|
196
|
+
helper-call expansion: an imported function's called helpers are not added
|
|
197
|
+
automatically. Indentation and comment/directive policy remain; associated
|
|
198
|
+
JSDoc stays when `includeComments` is true; compiler docs/tags in `symbols`
|
|
199
|
+
remain available regardless.
|
|
200
|
+
Current-file, `.d.ts`, `.d.mts`, `.d.cts`, `node_modules`, and outside-root
|
|
201
|
+
exclusions remain. There are no counts, incoming callers, structured locations,
|
|
202
|
+
unresolved lists, full-file collection, or completeness metadata.
|
|
203
|
+
|
|
204
|
+
Default npm-installed lint uses `symbols` and `references` with adhere's pinned
|
|
205
|
+
native TypeScript compiler through the npm/Bun launcher. No root `tsconfig.json`
|
|
206
|
+
or root file-list membership is required. During execution, native TypeScript
|
|
207
|
+
opens files with pending judgments or cached findings needing location and
|
|
208
|
+
discovers each file's configured project, including package
|
|
209
|
+
projects under solution roots. Unconfigured or excluded files fall back to
|
|
210
|
+
best-effort inferred context, not a refusal. These are complete outgoing AST
|
|
211
|
+
declaration scopes and compiler type/docs strings, not a complete
|
|
212
|
+
workspace-reference graph.
|
|
213
|
+
|
|
214
|
+
`SourceReads.prepare(paths)` registers metadata only: no file I/O, compiler/SDK
|
|
215
|
+
import, spawn, snapshot, or semantic query, regardless of how many paths are registered.
|
|
216
|
+
The first real read lazily starts one TypeScript `--lsp --stdio` process,
|
|
217
|
+
pinned with its SDK to exactly `7.1.0-dev.20261009.1`. This prerelease contains
|
|
218
|
+
the tuple-reference serialization fix from [microsoft/TypeScript#64080](https://github.com/microsoft/TypeScript/issues/64080);
|
|
219
|
+
the SDK and native executable must match that exact version. `NativeLsp` owns lifecycle only; it makes no `textDocument/hover` calls.
|
|
220
|
+
Types and documentation come directly from the compiler SDK through the public
|
|
221
|
+
API attached with `API.fromLSPConnection` to that process's API pipe. Symbol/type
|
|
222
|
+
queries are batched; independent SDK requests run in chunks of at most 8 within
|
|
223
|
+
the current document transaction.
|
|
224
|
+
|
|
225
|
+
Only one native transaction owns a permit at a time. It receives the as-written
|
|
226
|
+
text after normalized-code/ignore active-plan validation, opens the current
|
|
227
|
+
document with `didOpen` version 1, uses `textDocument/documentSymbol` as the
|
|
228
|
+
readiness barrier, and calls
|
|
229
|
+
the public, parameterless `API.getCurrentLanguageServerSnapshot()` per read. Success sends `didClose`, performs
|
|
230
|
+
guarded snapshot disposal, and clears the AST cache; native failure closes the
|
|
231
|
+
session under a watchdog. There is no audit-wide immutable snapshot and no
|
|
232
|
+
preload/freeze of registered files. Only current-read text is pinned; keep live
|
|
233
|
+
dependencies/configs stable during the audit.
|
|
234
|
+
|
|
235
|
+
SDK/write phases and LSP requests have 30-second deadlines and reject on compiler
|
|
236
|
+
exit. Waiting readers are interruptible; the active owner is uninterruptible
|
|
237
|
+
for resource safety. A stalled phase is bounded, with a further 2-second cleanup
|
|
238
|
+
watchdog escalating to `SIGKILL`; this is not a total audit-duration guarantee.
|
|
239
|
+
Native context is serial, but HTTP execution retains concurrency 8.
|
|
240
|
+
|
|
241
|
+
Even one pending file may require parsing its selected project; warm monorepo
|
|
242
|
+
program graphs can still consume substantial time/memory. Bounding active
|
|
243
|
+
documents/snapshots does not make total memory or model payload size constant,
|
|
244
|
+
and oversized contexts are still possible. Context is queried just
|
|
245
|
+
before judge/locate and reused locally. Planning never calls `SourceReads`.
|
|
246
|
+
Source checkouts use their development compiler dependency. Raw GitHub
|
|
247
|
+
binaries still have no compiler resources: their lint requires explicit config
|
|
248
|
+
`reads: []` or `reads: ["workspacePackages"]` for execution; use the package
|
|
249
|
+
launcher for the full default. Plan-only `--limit 0` needs no compiler or API key.
|
|
250
|
+
|
|
251
|
+
Rules without `appendState` share requests and pay for shared input context
|
|
252
|
+
once per request, not once per rule. Context repeats on request splitting,
|
|
253
|
+
locating findings, and hook requests. Compiler documentation and complete declaration
|
|
254
|
+
scopes add default token/privacy cost, including source from files not judged.
|
|
255
|
+
Complete scopes may contain substantially more source/tokens than fixed-line
|
|
256
|
+
excerpts; function bodies are not peeked or capped to seven lines.
|
|
257
|
+
Compiler-provided docs and JSDoc tags can remain present with ordinary comments
|
|
258
|
+
stripped. Omit `symbols` to disable type/docs context, `references` to disable
|
|
259
|
+
snippets, or use `reads: []` for code-only built-in context.
|
|
260
|
+
Plan token/cost estimates include code and cheap workspace metadata only, not
|
|
261
|
+
native context or hook-added data. Actual cost and request splits may therefore
|
|
262
|
+
be higher. Shared state plus any one question must fit 32k tokens, and state
|
|
263
|
+
plus all questions in a request must fit 64k, with 1k reserved for overhead.
|
|
264
|
+
Execution splits question batches, checks full budgets before HTTP, and skips
|
|
265
|
+
oversized files instead of silently truncating type/docs strings or complete scopes. Only
|
|
266
|
+
`appendState` forces a private rule request group.
|
|
267
|
+
|
|
158
268
|
`overrides` changes any rule by the id the report shows, a preset's with the
|
|
159
269
|
preset first, without copying it; an id no rule has refuses the run. `rules`
|
|
160
270
|
in the config, inline or as a directory of rule directories, replaces
|
|
@@ -189,10 +299,12 @@ so a private repo needs git's credentials for GitHub.
|
|
|
189
299
|
|
|
190
300
|
## Comments and suppressions
|
|
191
301
|
|
|
192
|
-
|
|
193
|
-
claiming code is safe hid real violations in the evals.
|
|
194
|
-
|
|
195
|
-
|
|
302
|
+
adhere blanks ordinary comments in judged code and declaration snippets, since
|
|
303
|
+
comments claiming code is safe hid real violations in the evals. Compiler
|
|
304
|
+
documentation and JSDoc tags in `symbols` are separate and can remain even with
|
|
305
|
+
`includeComments: false`. A comment containing `@adhere` is a note Jev does read:
|
|
306
|
+
use it for a checked fact the code relies on that the file cannot show, with
|
|
307
|
+
how you know.
|
|
196
308
|
|
|
197
309
|
```ts
|
|
198
310
|
// @adhere DeleteActivity succeeds on a missing activity; probed 2026-09-25.
|
|
@@ -210,20 +322,56 @@ file. Name several rules with commas.
|
|
|
210
322
|
|
|
211
323
|
## Cache and API key
|
|
212
324
|
|
|
213
|
-
Judgments are cached in `.adhere/cache/`, keyed by each file's
|
|
214
|
-
|
|
215
|
-
and a committed cache gives the team and CI the same findings without
|
|
216
|
-
again.
|
|
217
|
-
|
|
218
|
-
|
|
325
|
+
Judgments are cached in `.adhere/cache/`, keyed by each file's normalized own code and
|
|
326
|
+
model/rule/matcher/hook-source fingerprints. Commit it: judging uses paid
|
|
327
|
+
requests, and a committed cache gives the team and CI the same findings without
|
|
328
|
+
paying again. Changed own code re-judges every rule for that file; an edited rule
|
|
329
|
+
re-judges that rule; a threshold change re-judges nothing.
|
|
330
|
+
|
|
331
|
+
Dependencies, related source, reader selection, shared read-context contents,
|
|
332
|
+
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` does
|
|
334
|
+
not refresh its cached answers either. This keeps planning cheap but accepts
|
|
335
|
+
stale dependency/context judgments.
|
|
336
|
+
|
|
337
|
+
`planAudit` reads/counts/hashes code, checks the cache, and makes code plus cheap
|
|
338
|
+
workspace estimates without compiler or `SourceReads` calls. It applies `--limit`
|
|
339
|
+
to request-bearing files in path order before budgeting all pending rules and
|
|
340
|
+
cached-location work for each selected file. Rule/cache check totals remain
|
|
341
|
+
overall metrics; deferred/waiting counts are files. Deferred files remain
|
|
342
|
+
unbudgeted until selected and can later be found too large. `--limit 0` skips
|
|
343
|
+
budgeting, native reads, and HTTP execution.
|
|
344
|
+
Source reload/hash work is bounded to 8 files; plans retain compact metadata and
|
|
345
|
+
estimates, not all file contents. Only sampled-linter files need a planning
|
|
346
|
+
reread. After confirmation, execution reloads request-bearing files and cached
|
|
347
|
+
finding excerpts, preparing full native context only for pending or
|
|
348
|
+
cached-unlocated files just before judge/locate. Every reload validates normalized
|
|
349
|
+
code and ignore directives against the active plan; changes refuse with rerun
|
|
350
|
+
guidance, without adding persistent-cache keys. A fully cached and located run
|
|
351
|
+
starts no compiler and needs no API key or Jev requests.
|
|
352
|
+
|
|
353
|
+
For an explicit refresh, from the audited working directory:
|
|
354
|
+
|
|
355
|
+
```sh
|
|
356
|
+
rm -rf .adhere/cache
|
|
357
|
+
adhere lint --limit 0 # compiler-free counts/cache plan; budgeting is skipped
|
|
358
|
+
adhere lint # confirm fresh execution; actual context costs may be higher
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
This removes judgments and linter tallies and can cause new paid requests. Do
|
|
362
|
+
not clear it automatically when dependencies or readers change. Clear explicitly
|
|
363
|
+
if the user wants to compare fresh compiler type/docs and declaration payloads rather than cached
|
|
364
|
+
answers from an older format. Mark it generated to keep it out of diffs:
|
|
219
365
|
|
|
220
366
|
```text
|
|
221
367
|
.adhere/cache/** linguist-generated -diff
|
|
222
368
|
```
|
|
223
369
|
|
|
224
370
|
The key comes from `TYPESAFE_API_KEY` or from `adhere login`. lint prints a
|
|
225
|
-
plan with
|
|
226
|
-
terminal.
|
|
371
|
+
compiler-free plan with estimated requests/cost before sending and asks at a
|
|
372
|
+
terminal. Output and prompt warn that native context is omitted and may increase
|
|
373
|
+
actual tokens, cost, and request splits. Only confirmed execution prepares context
|
|
374
|
+
and enforces full budgets before HTTP.
|
|
227
375
|
|
|
228
376
|
## Reading a report
|
|
229
377
|
|
|
@@ -241,8 +389,9 @@ line; `×` is an error, `⚠` a warning. `confidence` is Jev's probability that
|
|
|
241
389
|
the file breaks the rule. On a terminal it is amber near its rule's
|
|
242
390
|
threshold, within a quarter of the room above it (0.80 to 0.84 at a threshold
|
|
243
391
|
of 0.80), and green above that: a rule whose findings are mostly amber turns
|
|
244
|
-
on where its threshold sits. `context` is its probability that the file
|
|
245
|
-
enough to decide
|
|
392
|
+
on where its threshold sits. `context` is its probability that the file and
|
|
393
|
+
shared context show enough to decide; all three readers are included by default.
|
|
394
|
+
It is colored the same way around the sufficiency threshold, 0.6
|
|
246
395
|
by default: amber from 0.50 to 0.69, red below. Below 0.6 the finding also
|
|
247
396
|
carries `warning: this file may not show enough to check this rule`. It turns
|
|
248
397
|
on something outside the file, and was usually false in the evals: check that
|
|
@@ -37,10 +37,23 @@ 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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
package
|
|
40
|
+
Default lint enables `workspacePackages`, `symbols`, and `references`, and
|
|
41
|
+
starts native TypeScript only during execution, after confirmation, when
|
|
42
|
+
pending judgments or cached findings needing location require context. It discovers each
|
|
43
|
+
file's configured project, including package projects under solution roots,
|
|
44
|
+
and falls back to inferred context for unconfigured or excluded files. No root
|
|
45
|
+
`tsconfig.json` or literal root file-list membership is required; do not block
|
|
46
|
+
setup on either. `.adhere/tsconfig.json` only types the config and rules.
|
|
47
|
+
Planning, `--limit 0`, version, help, skills, and validate do not start the
|
|
48
|
+
compiler.
|
|
49
|
+
|
|
50
|
+
In a repo without a `package.json`, inspect any `adhere` on PATH with
|
|
51
|
+
`adhere --version`, then **Ask** whether to use the npm/Bun package launcher
|
|
52
|
+
for the full default or a raw executable with deliberately reduced context.
|
|
53
|
+
The raw release (`adhere-Windows-x86_64.exe` on Windows) needs no Node or
|
|
54
|
+
package manager, but its lint requires explicit config `reads: []` or
|
|
55
|
+
`reads: ["workspacePackages"]`. Do not silently disable readers to make it
|
|
56
|
+
work. Only fetch it after the user chooses that alternative:
|
|
44
57
|
|
|
45
58
|
```sh
|
|
46
59
|
curl -fsSL --create-dirs -o ~/.local/bin/adhere \
|
|
@@ -48,9 +61,9 @@ curl -fsSL --create-dirs -o ~/.local/bin/adhere \
|
|
|
48
61
|
chmod +x ~/.local/bin/adhere
|
|
49
62
|
```
|
|
50
63
|
|
|
51
|
-
Validate and lint need a TypeSafe AI API key: `TYPESAFE_API_KEY`,
|
|
52
|
-
by `adhere login`, which the user runs; never ask for the key in
|
|
53
|
-
it.
|
|
64
|
+
Validate and lint's paid requests need a TypeSafe AI API key: `TYPESAFE_API_KEY`,
|
|
65
|
+
or one saved by `adhere login`, which the user runs; never ask for the key in
|
|
66
|
+
chat or print it. Compiler-free planning with `--limit 0` needs no key.
|
|
54
67
|
|
|
55
68
|
## 2. Find candidates
|
|
56
69
|
|
|
@@ -124,7 +137,12 @@ the wording of any. Revise drafts the user wants changed and show them again.
|
|
|
124
137
|
- Without a config, run `adhere init`. It also writes two example rules under
|
|
125
138
|
`.adhere/rules/style/`; delete them unless the user chose them.
|
|
126
139
|
- Copy each chosen draft to `.adhere/rules/<topic>/<slug>/RULE.md`.
|
|
127
|
-
- Add chosen presets, or topics, to `presets` in `.adhere/config.ts`.
|
|
140
|
+
- Add chosen presets, or topics, to `presets` in `.adhere/config.ts`. Presets
|
|
141
|
+
use the audit-wide readers; they do not own or override `reads`.
|
|
142
|
+
- Leave `reads` omitted for the full default. Do not add `reads: []` to the
|
|
143
|
+
normal setup recommendation or as a workaround for missing compiler
|
|
144
|
+
resources. Only set an explicit replacement array if the user chooses to
|
|
145
|
+
disable or limit readers.
|
|
128
146
|
- Install chosen shared rules: `adhere install org/repo/<id>` for each, or
|
|
129
147
|
`adhere install org/repo` for all of them. They land in `.adhere/rules/org/repo/`.
|
|
130
148
|
- Add `exclude` globs for generated or vendored code the default skips miss.
|
|
@@ -146,15 +164,115 @@ which to keep, and apply it. Run it again until it reports no contradictions.
|
|
|
146
164
|
adhere lint --limit 0
|
|
147
165
|
```
|
|
148
166
|
|
|
149
|
-
This shows
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
167
|
+
This shows a compiler-free plan and judges nothing: overall files/rules/cache
|
|
168
|
+
check metrics, and files deferred for a later run. Planning reads/counts/hashes
|
|
169
|
+
own code and checks cached answers; `--limit 0` skips budgeting, native reads,
|
|
170
|
+
and HTTP execution. A positive limit counts files needing requests, not rule
|
|
171
|
+
checks. Only selected files' pending rules and cached-location work are budgeted;
|
|
172
|
+
deferred/waiting counts are files, which can later be found too large. Planning never calls
|
|
173
|
+
`SourceReads` or resolves native symbols/references. Explain the output/prompt
|
|
174
|
+
caveat: estimates omit native context and hook-added data, so actual tokens,
|
|
175
|
+
cost, and request splits may be higher. Source reload/hash work is bounded to
|
|
176
|
+
8 files; plans retain compact metadata, not all source text. Files are reloaded
|
|
177
|
+
for execution; if normalized code or ignore
|
|
178
|
+
directives changed since planning, rerun rather than using the old plan.
|
|
179
|
+
|
|
180
|
+
Tell the user that compiler type/docs strings and outgoing declaration snippets add
|
|
181
|
+
source/documentation sharing and token cost by default, including complete
|
|
182
|
+
scopes from files outside the audit filter. Whole declaration bodies can use more
|
|
183
|
+
tokens than fixed-line excerpts; budget overflow skips the file instead of
|
|
184
|
+
capping bodies to seven lines or silently truncating them. Compiler-provided
|
|
185
|
+
documentation and JSDoc tags can remain even when ordinary code comments are
|
|
186
|
+
stripped; omitting `symbols` disables type/docs context and omitting
|
|
187
|
+
`references` disables snippets. The global workspace catalog is not sent: only
|
|
188
|
+
this file's matched literal imports appear under canonical package keys.
|
|
189
|
+
Offer an explicit reader subset or code-only opt-out if they want less context;
|
|
190
|
+
never apply one silently. Neither reader selection nor dependency/shared-context
|
|
191
|
+
changes invalidate cached judgments. If they want fresh answers under changed
|
|
192
|
+
context, **Ask** before clearing `.adhere/cache/`; do not clear it automatically.
|
|
193
|
+
|
|
194
|
+
On a large repo, offer a subtree such as `--filter 'src/**'`, or a file cap
|
|
195
|
+
such as `--limit 100`: the first 100 files needing requests in path order, with
|
|
196
|
+
all pending rules per selected file. Judging plus cached-unlocated work in one
|
|
197
|
+
file uses one slot; a cached-locate-only file also counts. Fully cached and
|
|
198
|
+
already-located files report without using slots. After the cap, both judge and
|
|
199
|
+
locate work wait. Known cheap-budget skips refill from later files, but native
|
|
200
|
+
or HTTP-size skips after selection consume a slot without runtime refill.
|
|
201
|
+
|
|
202
|
+
Explain that a file cap is not an HTTP-request or token/cost cap: each file can
|
|
203
|
+
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:
|
|
153
205
|
|
|
154
206
|
```sh
|
|
155
207
|
adhere lint --yes
|
|
156
208
|
```
|
|
157
209
|
|
|
210
|
+
`SourceReads.prepare(paths)` registers eligible-path metadata only, with no
|
|
211
|
+
file I/O, compiler/SDK import, spawn, snapshot, or semantic query, even for a large list.
|
|
212
|
+
Only after confirmation do pending judgments or cached findings still needing
|
|
213
|
+
location trigger actual per-file native reads just before judge/locate. Native
|
|
214
|
+
transactions are serial through one permit; HTTP execution remains concurrent
|
|
215
|
+
at 8. Returned context is reused locally and full budgets are enforced before
|
|
216
|
+
HTTP, with oversized files skipped and no silent truncation. Shared state plus
|
|
217
|
+
one question must fit 32k tokens and state plus all questions must fit 64k per
|
|
218
|
+
request, with 1k reserved for overhead. The SDK may still parse a full selected
|
|
219
|
+
project, and additional monorepo programs may increase startup time and memory.
|
|
220
|
+
|
|
221
|
+
`symbols` remains `Record<string, string>`: compiler-rendered distinct type
|
|
222
|
+
blocks for same-name binding/narrowing contexts, with compiler documentation
|
|
223
|
+
and JSDoc tags once per canonical symbol per file. It does not promise literal
|
|
224
|
+
IDE labels or selected-call overload presentation. Symbol/type queries are
|
|
225
|
+
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. `references` maps used names to canonical outgoing declarations
|
|
227
|
+
by pathname, selecting the smallest complete AST scope without extra surrounding
|
|
228
|
+
lines. Functions include full bodies; arrow/object initializer variables include
|
|
229
|
+
whole variable statements with `export const` and semicolons. Methods, getters,
|
|
230
|
+
and setters include their whole member, not the entire class; classes,
|
|
231
|
+
interfaces, and type aliases include the whole declaration. Repeated/overlapping
|
|
232
|
+
scopes merge and disjoint scopes join with `…`. This is syntactic scope selection,
|
|
233
|
+
not recursive helper-call expansion: helpers called by an imported function are
|
|
234
|
+
not automatically expanded. Scopes preserve indentation and the comment/directive
|
|
235
|
+
policy, including associated JSDoc when `includeComments` is true; compiler
|
|
236
|
+
docs/tags in `symbols` remain available regardless. Current-file, declaration-file, `node_modules`, and
|
|
237
|
+
outside-root exclusions are unchanged. There are no incoming callers, counts, structured locations,
|
|
238
|
+
unresolved lists, full-file collection, or completeness markers.
|
|
239
|
+
|
|
240
|
+
The first actual read lazily starts one native TypeScript LSP process with its
|
|
241
|
+
public SDK API pipe; both are pinned to exactly `7.1.0-dev.20261009.1` and must
|
|
242
|
+
match. This prerelease contains the tuple-reference serialization fix from
|
|
243
|
+
[microsoft/TypeScript#64080](https://github.com/microsoft/TypeScript/issues/64080). `NativeLsp` owns lifecycle only and makes
|
|
244
|
+
no `textDocument/hover` calls. Each read uses the passed as-written text after
|
|
245
|
+
normalized-code/ignore active-plan validation, opens only that document at
|
|
246
|
+
version 1, uses `textDocument/documentSymbol` as its readiness barrier, and calls
|
|
247
|
+
the public, parameterless `API.getCurrentLanguageServerSnapshot()`.
|
|
248
|
+
Success closes the document, disposes that snapshot under a guarded phase, and
|
|
249
|
+
clears AST caches; native failure closes the session under the watchdog.
|
|
250
|
+
|
|
251
|
+
Keep dependencies/configs stable: only current-read text is pinned, not every
|
|
252
|
+
registered file or an immutable global audit snapshot. Warm compiler project
|
|
253
|
+
graphs can still be large and slow to load; neither total memory nor payload
|
|
254
|
+
size is constant, and contexts may still exceed model budgets.
|
|
255
|
+
|
|
256
|
+
SDK/write phases and LSP requests have 30-second deadlines and reject on compiler
|
|
257
|
+
exit. Queued readers can be interrupted; the active owner is uninterruptible
|
|
258
|
+
for resource safety. A stalled phase is bounded and cleanup adds a 2-second
|
|
259
|
+
`SIGKILL` watchdog. These are phase/cleanup bounds, not a whole-audit timeout.
|
|
260
|
+
|
|
261
|
+
If the user explicitly chose a cache refresh, run from the audited working
|
|
262
|
+
directory, using the repo's package launcher as above:
|
|
263
|
+
|
|
264
|
+
```sh
|
|
265
|
+
rm -rf .adhere/cache
|
|
266
|
+
adhere lint --limit 0
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Show the fresh counts/cache plan; `--limit 0` skips budgeting. For selected
|
|
270
|
+
execution work, explain that its estimate omits native context and may understate
|
|
271
|
+
cost/splits, then **Ask** before paid execution. Deleting the cache removes judgments and linter tallies; it can cause
|
|
272
|
+
new paid requests. Use this explicitly if the user wants to compare fresh
|
|
273
|
+
compiler type/docs and declaration payloads with older cached answers. A change in `B.ts` does not
|
|
274
|
+
otherwise invalidate cached judgments for an unchanged importing `A.ts`.
|
|
275
|
+
|
|
158
276
|
Walk the user through the report:
|
|
159
277
|
|
|
160
278
|
1. How to read a finding: the rule id, the description, its confidence and
|
|
@@ -165,7 +283,8 @@ Walk the user through the report:
|
|
|
165
283
|
3. For each noisy rule, propose one change from the adhere skill's "Tuning a
|
|
166
284
|
rule": narrow the wording, scope it, raise its threshold, make it a
|
|
167
285
|
warning, or turn it off. **Ask**, apply what the user accepts, and run
|
|
168
|
-
lint again.
|
|
286
|
+
lint again. With unchanged own code and model, only the edited rules are
|
|
287
|
+
judged again; dependency/context changes alone are not tracked.
|
|
169
288
|
|
|
170
289
|
CI fails on any error finding, so lint must exit 0 before step 8. **Ask** how
|
|
171
290
|
to get there: fix the real findings now with the adhere-fix skill
|
|
@@ -212,9 +331,16 @@ jobs:
|
|
|
212
331
|
|
|
213
332
|
Pin the versions the repo already uses: bun's `bun-version`, Node's
|
|
214
333
|
`node-version` from `.nvmrc` or `engines`, and pnpm's from `packageManager`,
|
|
215
|
-
which `pnpm/action-setup` reads.
|
|
216
|
-
|
|
217
|
-
|
|
334
|
+
which `pnpm/action-setup` reads. Keep optional dependencies enabled so adhere's
|
|
335
|
+
platform package and its compiler dependency are installed. Existing project
|
|
336
|
+
configs and dependencies supply configured context when available; files with
|
|
337
|
+
no configured project or excluded from one use inferred context. Do not require
|
|
338
|
+
or generate a root `tsconfig.json` merely to make default lint run. In a repo
|
|
339
|
+
without a `package.json`, use the package launcher for the full default.
|
|
340
|
+
Only skip setup/install and fetch the raw executable if the user explicitly
|
|
341
|
+
chose and committed `reads: []` or `reads: ["workspacePackages"]` locally.
|
|
342
|
+
That is a reduced-context alternative, not an equivalent default installation.
|
|
343
|
+
Fetch the executable at the version the user ran:
|
|
218
344
|
|
|
219
345
|
```yaml
|
|
220
346
|
- run: |
|