@drkmttr/adhere 0.17.5 → 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 +125 -64
- 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 +27 -3
- package/src/init.ts +22 -3
- package/src/install.ts +29 -23
- package/src/services/Jev.http.ts +20 -2
- package/src/services/Jev.provider.test.ts +280 -4
- package/src/services/Jev.reads.test.ts +99 -20
- package/src/services/Jev.ts +76 -12
- package/src/services/SourceReads.test.ts +290 -41
- package/src/services/SourceReads.ts +52 -29
- package/src/workflows/audit.ts +82 -11
|
@@ -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,14 @@ 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),
|
|
227
|
+
/** Maximum characters of each symbol's complete resolved content. Defaults to 256. */
|
|
228
|
+
maxSymbolLength: Schema.optionalKey(Schema.Natural),
|
|
229
|
+
/** Maximum characters of documentation and JSDoc tags per symbol. Defaults to 256; 0 omits them. */
|
|
230
|
+
maxSymbolDocumentationLength: Schema.optionalKey(Schema.Natural),
|
|
225
231
|
/**
|
|
226
232
|
* A record of rules, or a directory path relative to the config file,
|
|
227
233
|
* which holds its rules as `.adhere/rules/` does. Unset: the rules in
|
|
@@ -329,7 +335,9 @@ export type Loaded<T extends { readonly rules?: unknown }> = Omit<T, "rules"> &
|
|
|
329
335
|
export const DEFAULT_MODEL = "jev-latest";
|
|
330
336
|
export const DEFAULT_THRESHOLD = 0.8;
|
|
331
337
|
export const DEFAULT_SUFFICIENCY_THRESHOLD = 0.6;
|
|
332
|
-
export const DEFAULT_READS: typeof Reads.Type = ["workspacePackages"
|
|
338
|
+
export const DEFAULT_READS: typeof Reads.Type = ["workspacePackages"];
|
|
339
|
+
export const DEFAULT_MAX_SYMBOL_DOCUMENTATION_LENGTH = 256;
|
|
340
|
+
export const DEFAULT_MAX_SYMBOL_LENGTH = 256;
|
|
333
341
|
|
|
334
342
|
/** A config with its presets folded in and every default applied. */
|
|
335
343
|
export interface ResolvedConfig {
|
|
@@ -349,6 +357,12 @@ export interface ResolvedConfig {
|
|
|
349
357
|
readonly includeComments?: boolean;
|
|
350
358
|
/** Built-in context shared by every rule, with the default applied. */
|
|
351
359
|
readonly reads: typeof Reads.Type;
|
|
360
|
+
/** Whether both native symbol and declaration context are enabled. */
|
|
361
|
+
readonly typeAware: boolean;
|
|
362
|
+
/** Maximum characters of each symbol's complete resolved content. */
|
|
363
|
+
readonly maxSymbolLength: number;
|
|
364
|
+
/** Maximum characters of documentation and JSDoc tags before the complete symbol limit applies. */
|
|
365
|
+
readonly maxSymbolDocumentationLength: number;
|
|
352
366
|
/**
|
|
353
367
|
* The workspace's packages, each name with its directory from the working
|
|
354
368
|
* directory, read from its manifests on every run when the config's reads
|
|
@@ -366,6 +380,8 @@ export interface Flags {
|
|
|
366
380
|
readonly sufficiencyThreshold?: number | undefined;
|
|
367
381
|
/** Throttles requests to Jev to at most this many a minute. */
|
|
368
382
|
readonly rpm?: number | undefined;
|
|
383
|
+
/** Replaces the config's native-context opt-in. */
|
|
384
|
+
readonly typeAware?: boolean | undefined;
|
|
369
385
|
}
|
|
370
386
|
|
|
371
387
|
/**
|
|
@@ -400,6 +416,7 @@ export const resolveConfig = (
|
|
|
400
416
|
});
|
|
401
417
|
const last = <K extends "model" | "threshold">(key: K) =>
|
|
402
418
|
applied.map((preset) => preset[key]).findLast((value) => value !== undefined);
|
|
419
|
+
const typeAware = flags.typeAware ?? config.typeAware ?? false;
|
|
403
420
|
return {
|
|
404
421
|
model: config.provider?.model ?? config.model ?? last("model") ?? DEFAULT_MODEL,
|
|
405
422
|
...(config.provider === undefined ? {} : { provider: config.provider }),
|
|
@@ -410,7 +427,14 @@ export const resolveConfig = (
|
|
|
410
427
|
...(flags.rpm === undefined ? {} : { rpm: flags.rpm }),
|
|
411
428
|
...(config.exclude === undefined ? {} : { exclude: config.exclude }),
|
|
412
429
|
...(config.includeComments === undefined ? {} : { includeComments: config.includeComments }),
|
|
413
|
-
|
|
430
|
+
typeAware,
|
|
431
|
+
reads: [
|
|
432
|
+
...(config.reads ?? DEFAULT_READS).filter((read) => read === "workspacePackages"),
|
|
433
|
+
...(typeAware ? (["symbols", "references"] as const) : []),
|
|
434
|
+
],
|
|
435
|
+
maxSymbolLength: config.maxSymbolLength ?? DEFAULT_MAX_SYMBOL_LENGTH,
|
|
436
|
+
maxSymbolDocumentationLength:
|
|
437
|
+
config.maxSymbolDocumentationLength ?? DEFAULT_MAX_SYMBOL_DOCUMENTATION_LENGTH,
|
|
414
438
|
};
|
|
415
439
|
};
|
|
416
440
|
|
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}`),
|
package/src/services/Jev.http.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { Credentials } from "#services/Credentials.ts";
|
|
|
14
14
|
import {
|
|
15
15
|
type Body,
|
|
16
16
|
type ComparedRule,
|
|
17
|
+
describeState,
|
|
17
18
|
edgeKey,
|
|
18
19
|
conflictBody,
|
|
19
20
|
contradictBody,
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
type Rules,
|
|
37
38
|
sufficiencyKey,
|
|
38
39
|
tokensOf,
|
|
40
|
+
logRequestPayload,
|
|
39
41
|
} from "#services/Jev.ts";
|
|
40
42
|
import {
|
|
41
43
|
Duration,
|
|
@@ -87,7 +89,7 @@ const hooked = (
|
|
|
87
89
|
return Effect.tryPromise({
|
|
88
90
|
try: async () => ({
|
|
89
91
|
...body,
|
|
90
|
-
state: { ...body.state, ...(await appendState(body.state, file, bun)) },
|
|
92
|
+
state: describeState({ ...body.state, ...(await appendState(body.state, file, bun)) }),
|
|
91
93
|
}),
|
|
92
94
|
catch: (cause) => refused(`the appendState of ${id} failed: ${messageOf(cause)}`),
|
|
93
95
|
});
|
|
@@ -190,6 +192,7 @@ export const JevLive = Layer.effect(Jev)(
|
|
|
190
192
|
const limiter = yield* RateLimiter.RateLimiter;
|
|
191
193
|
|
|
192
194
|
const provider: Provider = config.provider ?? jev({ model: config.model, fetch });
|
|
195
|
+
const previewed = new Set<string>();
|
|
193
196
|
// The key it asks with, when it names one `adhere login` saves. Read when
|
|
194
197
|
// a request needs it, not in the layer: a run with nothing pending needs none.
|
|
195
198
|
const apiKey: Effect.Effect<string | undefined, JevUnavailable> =
|
|
@@ -226,8 +229,23 @@ export const JevLive = Layer.effect(Jev)(
|
|
|
226
229
|
yield* Effect.logDebug(
|
|
227
230
|
`${kind}: asking ${provider.model} ${questions} ${questions === 1 ? "question" : "questions"}, about ${tokensOf(body)} tokens`,
|
|
228
231
|
);
|
|
232
|
+
if ((kind === "judge" || kind === "locate") && !previewed.has(kind)) {
|
|
233
|
+
previewed.add(kind);
|
|
234
|
+
yield* logRequestPayload(
|
|
235
|
+
`${kind}: first ${kind === "locate" ? "match-to-lines " : ""}request payload`,
|
|
236
|
+
() => body,
|
|
237
|
+
"Debug",
|
|
238
|
+
);
|
|
239
|
+
}
|
|
229
240
|
const [elapsed, answered] = yield* Effect.timed(
|
|
230
|
-
askProvider(provider, body.state, questionsOf(body.questions), Answers, key, pace)
|
|
241
|
+
askProvider(provider, body.state, questionsOf(body.questions), Answers, key, pace).pipe(
|
|
242
|
+
Effect.tapErrorTag("JevOverflow", () =>
|
|
243
|
+
logRequestPayload(
|
|
244
|
+
`${kind}: too long request payload (provider overflow)`,
|
|
245
|
+
() => body,
|
|
246
|
+
),
|
|
247
|
+
),
|
|
248
|
+
),
|
|
231
249
|
);
|
|
232
250
|
yield* Effect.logDebug(
|
|
233
251
|
`${kind}: ${provider.model} answered in ${Math.round(Duration.toMillis(elapsed))} ms`,
|