@pablozrrrr/jevguard 0.2.2 → 0.3.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 CHANGED
@@ -1,6 +1,11 @@
1
1
  # JevGuard
2
2
 
3
3
  > **Semantic policy engine for coding agents.**
4
+ >
5
+ > JevGuard reviews every change an AI coding agent makes against the rules of your
6
+ > repository, written in plain Markdown, and reports `PASS`, `WARN`, or `FAIL` per
7
+ > rule. It runs in the background for OpenCode and Claude Code, never blocks the
8
+ > agent, and never edits your code.
4
9
 
5
10
  ![JevGuard: semantic policy engine for coding agents](https://raw.githubusercontent.com/pablozr/JevGuard/main/27b864b2-3f64-4211-99dd-26764c549d03.png)
6
11
 
@@ -8,6 +13,30 @@ JevGuard checks the code an agent just changed against the policies that matter
8
13
  your repository. It uses Jev for a narrow semantic judgment per rule, then applies
9
14
  your local deterministic gate to return `PASS`, `WARN`, or `FAIL`.
10
15
 
16
+ | Host | Package | Status |
17
+ | --- | --- | --- |
18
+ | [OpenCode](https://opencode.ai) | `@pablozrrrr/jevguard` | Available, targets OpenCode `1.18.32` |
19
+ | [Claude Code](https://claude.com/claude-code) | `@pablozrrrr/jevguard-claude-code` | Available through this repository's plugin marketplace |
20
+ | Codex | — | Planned |
21
+
22
+ ## Quick start
23
+
24
+ ```sh
25
+ # 1. Store your Jev key in the OS credential store (shared by both hosts)
26
+ bunx --package @pablozrrrr/jevguard jevguard login
27
+
28
+ # 2a. OpenCode: register the plugin in opencode.json
29
+ bunx --package @pablozrrrr/jevguard jevguard install
30
+
31
+ # 2b. Claude Code: install from the marketplace
32
+ claude plugin marketplace add pablozr/JevGuard
33
+ claude plugin install jevguard@jevguard
34
+ ```
35
+
36
+ Then add your rules to `.jev/rules.md` (in OpenCode, the bundled `jev-init` skill can
37
+ bootstrap them) and keep working with your agent as usual. Details are in
38
+ [Install](#install) and the [Claude Code guide](./docs/configuration.md#claude-code).
39
+
11
40
  ```text
12
41
  agent completes a turn
13
42
  ↓
@@ -27,13 +56,20 @@ specific policy, and leaves planning, code generation, and correction with the
27
56
  coding agent.
28
57
 
29
58
  > [!WARNING]
30
- > JevGuard is in active development. The current release targets OpenCode `1.18.32`
31
- > and evaluates every rule block declared in `.jev/rules.md` plus the `SCOPE-CREEP`
32
- > and `COMPLEXITY` built-ins. Reviews are background and observe-only: they report
33
- > results but never alter the agent context or block a task. When remediation is
34
- > enabled and a review has any `FAIL`, the plugin asks a hidden `jevguard-proposer`
35
- > subagent for a proposal in an isolated child session and shows a generic toast; the
36
- > proposer never edits code.
59
+ > JevGuard is in active development. The OpenCode plugin targets OpenCode `2.2.x`
60
+ > (minimum `2.2.2`, `>=2.2.2 <2.3`) through the V2 plugin API and evaluates every rule
61
+ > block declared in `.jev/rules.md` plus the `SCOPE-CREEP` and `COMPLEXITY` built-ins.
62
+ > Reviews are background and observe-only: they report results but never alter the
63
+ > agent context or block a task. When remediation is enabled and a review has any
64
+ > `FAIL`, the plugin asks for a proposal in an isolated, deny-all child session; the
65
+ > proposal never edits code. See [OpenCode 2.2.x limitations](#opencode-22x-limitations).
66
+ > On Claude Code and OpenCode, a user who opts in with `mode: remediate` in their own
67
+ > `claude-code.yaml` or `opencode.yaml` gets a bounded chain of at most 3 visible
68
+ > correction messages to the agent; see
69
+ > [Remediation proposals](docs/configuration.md#remediation-proposals) and
70
+ > [OpenCode `mode: remediate`](docs/configuration.md#opencode-mode-remediate-bounded-chain).
71
+ > The Claude Code plugin is distributed through this repository's marketplace; see the
72
+ > [Claude Code guide](./docs/configuration.md#claude-code).
37
73
 
38
74
  ## Why JevGuard
39
75
 
@@ -52,6 +88,8 @@ JevGuard makes those questions versioned, scoped, and machine-actionable.
52
88
 
53
89
  severity: error
54
90
  scope: backend/**
91
+ scope: !backend/generated/**
92
+ evidence: code
55
93
 
56
94
  ### Rule
57
95
 
@@ -66,6 +104,15 @@ A controller performs domain decisions, calculations, or state mutations directl
66
104
  Validation, HTTP mapping and delegation to services.
67
105
  ```
68
106
 
107
+ `scope:` may repeat. Each line adds an inclusion glob, and a leading `!` adds an
108
+ exclusion, so a path is in scope when it matches at least one inclusion and no
109
+ exclusion. `evidence:` chooses which files the rule is evaluated against — `code`
110
+ (the default), `docs`, or `any`. Because the default is `code`, prose documentation
111
+ (`.md`, `.mdx`, `.txt`) no longer participates in a rule's evidence unless the rule
112
+ opts in with `evidence: docs` or `evidence: any`; this is intentional. Config and
113
+ data formats (`.json`, `.yaml`, `.toml`, `.xml`, `.csv`, and others) stay in the
114
+ `code` class, so config rules keep their coverage.
115
+
69
116
  The rule stays in the repository beside the code it governs. `Allowed` is a real
70
117
  exception, not a suggestion. JevGuard evaluates it as part of the same rule.
71
118
 
@@ -86,11 +133,18 @@ applicable rules
86
133
  one semantic judgment per rule
87
134
  ```
88
135
 
89
- If complete attributed evidence is unavailable, too large, invalid, or contains a
90
- blocked sensitive file, JevGuard marks the affected rule `UNAVAILABLE`. It never
91
- turns incomplete evidence into a reassuring verdict. Evidence problems are scoped
92
- to the rules they affect: a rule whose applicable files are all safe still runs
93
- even when another rule's evidence is blocked.
136
+ If complete attributed evidence is unavailable, invalid, or contains a blocked
137
+ sensitive file, JevGuard marks the affected rule `UNAVAILABLE`. It never turns
138
+ incomplete evidence into a reassuring verdict. Evidence problems are scoped to the
139
+ rules they affect: a rule whose applicable files are all safe still runs even when
140
+ another rule's evidence is blocked.
141
+
142
+ Oversized evidence is no longer refused outright. A scoped diff above
143
+ `maxDiffLength` is split, in attributed file order, into one slice per file; an
144
+ individually oversized file is split into its ordered unified-diff hunks with the file
145
+ preamble repeated in every slice. Nothing is truncated, summarized, or dropped: an
146
+ unparseable oversized file or an indivisible oversized hunk makes the whole rule
147
+ `UNAVAILABLE/OVERSIZED_DIFF`.
94
148
 
95
149
  Some failures happen before any policy rule is evaluated. If the attributed diff
96
150
  cannot be built, the review is a single synthetic `UNAVAILABLE` entry with no rule
@@ -114,7 +168,15 @@ policy that translates it into an outcome.
114
168
  | `warning` | `< 60%` | `≥ 60%` | never |
115
169
 
116
170
  Those thresholds are per repository rule and locally configurable in
117
- `.jev/config.yaml`.
171
+ `.jev/config.yaml`. Slicing does not change them: each slice is gated independently,
172
+ the rule outcome is the highest slice outcome (`FAIL > WARN > PASS`), and the rule's
173
+ `violationProbability` is the maximum across its slices. `PASS` requires every planned
174
+ slice to pass. A rule may produce at most 16 slices, and slicing may add at most 32
175
+ repository-rule Jev calls per turn beyond one per applicable rule; a rule that does not
176
+ fit is `UNAVAILABLE/SLICE_LIMIT_EXCEEDED` with no Jev call and later rules still use the
177
+ remaining budget. Any failed, blocked, or out-of-range slice makes the whole rule
178
+ `UNAVAILABLE`, and no sibling probability is presented as a verdict. The built-ins do
179
+ not slice.
118
180
 
119
181
  JevGuard also runs two product-owned built-ins on every attributed turn,
120
182
  `SCOPE-CREEP` and `COMPLEXITY`. Both receive the turn's complete, safe attributed
@@ -139,9 +201,12 @@ concurrency limit caps the plugin instance at two Jev requests in flight, counti
139
201
  built-in batch as a single request. Results join the same aggregate in a fixed order:
140
202
  rules in source order, then `SCOPE-CREEP`, then `COMPLEXITY`.
141
203
 
142
- Reviews run in the background. The idle event returns as soon as the serialized
143
- attribution step is scheduled; policy reads, Jev calls, and presentation never sit on
144
- the agent's critical path.
204
+ Reviews run in the background. The unit of review is one session execution, from
205
+ `session.execution.started` to `session.execution.succeeded`; the event handler returns
206
+ as soon as the serialized attribution step is scheduled, so policy reads, Jev calls,
207
+ and presentation never sit on the agent's critical path. An `interrupted` or `failed`
208
+ execution is not reviewed: the plugin discards it and logs `JevGuard skipped ·
209
+ interrupted|failed`.
145
210
 
146
211
  ## Safe auto-propose remediation
147
212
 
@@ -152,7 +217,7 @@ delivered entirely by the same server plugin.
152
217
 
153
218
  When `.jev/config.yaml` enables remediation (it is enabled by default) and a review
154
219
  contains any `FAIL`, the plugin, after presenting the review, sends one aggregate
155
- proposal request to a hidden `jevguard-proposer` subagent:
220
+ proposal request to an isolated child session:
156
221
 
157
222
  - The request aggregates every `FAIL` finding in the review — local `error` rules and
158
223
  the `SCOPE-CREEP` built-in — together with the turn's task and its **complete, safe,
@@ -162,28 +227,33 @@ proposal request to a hidden `jevguard-proposer` subagent:
162
227
  attributed file is blocked or the patch is oversized, the plugin records no proposal
163
228
  at all — even when a rule-scoped `FAIL` exists — because there is no complete safe
164
229
  evidence to send. It never falls back to a repository or global diff.
165
- - The request is sent in an isolated child session parented to the source session. The
166
- `jevguard-proposer` subagent has wildcard-deny permissions and wildcard-disabled
167
- tools, so it cannot call a tool, edit a file, or produce a patch. It returns a
168
- strategy and one manual apply instruction only.
169
- - `remediation.model` is optional. When set in `.jev/config.yaml`, it selects the
170
- proposer model; when it is absent, the proposer inherits the host's model.
171
- - A generic toast reports that the proposal is ready in the child session. It carries
172
- no rule, task, diff, finding, or credential.
230
+ - The request is sent in an isolated child session parented to the source session.
231
+ The child session is created with wildcard-deny permissions, so it cannot call a
232
+ tool, edit a file, or produce a patch. It returns a strategy and one manual apply
233
+ instruction only. No agent is registered for it.
234
+ - The child session always gets an explicit model: `remediation.model` from
235
+ `.jev/config.yaml` when set, otherwise the model of the source session. When neither
236
+ is known, no proposal is created and the plugin logs
237
+ `REMEDIATION_PROPOSAL_SKIPPED_NO_MODEL`.
238
+ - A proposal that does not finish within 120 seconds is interrupted and logged as
239
+ `REMEDIATION_PROPOSAL_TIMEOUT`.
240
+ - The server plugin has no toast or TUI channel on OpenCode `2.2.x`, so readiness is
241
+ reported through the structured log only. It carries no rule, task, diff, finding,
242
+ or credential.
173
243
 
174
244
  The plugin creates at most one proposal per evaluated turn, and the child session is
175
245
  excluded from review for the plugin lifetime, so a proposal can never recurse or
176
246
  trigger a second one. A host, model, or toast failure is contained and never changes
177
247
  the review. A contained proposal failure — an invalid model specifier, a child-session
178
- failure, or a prompt failure — shows a generic error toast and writes one structured
248
+ failure, a prompt failure, or a timeout — writes one structured
179
249
  `error` log entry carrying only a typed reason code, never any task, diff, finding,
180
250
  path, or credential.
181
251
 
182
252
  The proposer is a strategy step, not an apply step: it never edits code, and nothing
183
253
  is applied automatically. You read the proposal, copy its manual apply instruction
184
- into your normal coding agent, and apply it there. That apply is an ordinary completed
185
- assistant turn, so the server plugin reviews it normally on the next idle — exactly
186
- like any other turn.
254
+ into your normal coding agent, and apply it there. That apply is an ordinary
255
+ execution, so the server plugin reviews it normally when it succeeds — exactly like any
256
+ other turn.
187
257
 
188
258
  The proposal prompt treats the task, paths, diff, rules, and findings as untrusted
189
259
  data, and requires a separate explicit confirmation before proposing changes to tests,
@@ -193,8 +263,14 @@ or secret is logged.
193
263
  ## Install
194
264
 
195
265
  Installing `@pablozrrrr/jevguard` does **not** register it with OpenCode by itself.
196
- OpenCode only loads plugins listed in `plugin: [...]` in `opencode.json`, so register
197
- the plugin from the installed CLI:
266
+ OpenCode `2.2.x` only loads plugins listed in `plugins: [...]` in `opencode.json`.
267
+
268
+ > [!WARNING]
269
+ > The `jevguard install` command below still writes the OpenCode `1.x` `plugin` key.
270
+ > Until it is migrated, register the plugin on `2.2.x` with the manual `plugins`
271
+ > entry shown after it.
272
+
273
+ The installer is run from the installed CLI:
198
274
 
199
275
  ```sh
200
276
  # global config: ~/.config/opencode/opencode.json
@@ -204,10 +280,12 @@ jevguard install
204
280
  jevguard install --project
205
281
  ```
206
282
 
207
- The installer asks for confirmation, edits only the `plugin` array, and is
208
- idempotent. It only edits a `plugin` array of plain strings, so OpenCode's
209
- `[["pkg", { "options": {} }]]` tuple form is a known limitation: the installer leaves
210
- the file unchanged and prints the manual snippet instead. If `jevguard` is not on
283
+ The installer asks for confirmation, edits only the `plugins` array, and is
284
+ idempotent. It never writes the legacy `plugin` key, but an entry already there counts
285
+ as installed. It only edits a `plugins` array of package strings or
286
+ `{ "package": "..." }` objects; any other shape is left unchanged and the installer
287
+ prints the manual snippet instead. `jevguard doctor` also checks that
288
+ `opencode --version` is in the supported range `>=2.2.2 <2.3`. If `jevguard` is not on
211
289
  `PATH`, run it through `bunx`:
212
290
 
213
291
  ```sh
@@ -217,7 +295,7 @@ bunx --package @pablozrrrr/jevguard jevguard install
217
295
  The manual alternative is one entry in `opencode.json`:
218
296
 
219
297
  ```json
220
- { "plugin": ["@pablozrrrr/jevguard"] }
298
+ { "plugins": ["@pablozrrrr/jevguard"] }
221
299
  ```
222
300
 
223
301
  OpenCode resolves the package from the npm registry or its cache and loads it on the
@@ -232,9 +310,9 @@ after changing the plugin list.
232
310
  ### Build the local artifact
233
311
 
234
312
  The artifact ships the plugin as a **locally packed tarball** that bundles the
235
- private `@jevguard/core` and `@jevguard/opencode-adapter` workspace code, so a
236
- clean consumer installs one tarball and never resolves a workspace link or a
237
- private registry package. It also packages the `jevguard-rules` and `jev-init`
313
+ private `@jevguard/core`, `@jevguard/node-infra` and `@jevguard/opencode-adapter`
314
+ workspace code, so a clean consumer installs one tarball and never resolves a
315
+ workspace link or a private registry package. It also packages the `jevguard-rules` and `jev-init`
238
316
  skills and their private validators under `skills/<name>/`.
239
317
 
240
318
  Build the tarball from this repository:
@@ -256,12 +334,12 @@ does not vendor them and does not promise an offline install.
256
334
 
257
335
  ### Load the plugin
258
336
 
259
- OpenCode `1.18.32` resolves a **bare** `opencode.json` `plugin` entry from the npm
337
+ OpenCode `2.2.x` resolves a **bare** `opencode.json` `plugins` entry from the npm
260
338
  registry or its cache:
261
339
 
262
340
  ```json
263
341
  {
264
- "plugin": ["@pablozrrrr/jevguard"]
342
+ "plugins": ["@pablozrrrr/jevguard"]
265
343
  }
266
344
  ```
267
345
 
@@ -275,18 +353,36 @@ bun add /path/to/pablozrrrr-jevguard-<version>.tgz
275
353
 
276
354
  ```ts
277
355
  // .opencode/plugins/jevguard.ts
278
- export { JevGuardPlugin } from "@pablozrrrr/jevguard";
356
+ export { default } from "@pablozrrrr/jevguard";
279
357
  ```
280
358
 
281
359
  OpenCode loads `.opencode/plugins/` with its bundled Bun runtime. The shim runs as
282
360
  a local plugin module and resolves `@pablozrrrr/jevguard` from
283
- `.opencode/node_modules`. Use either the bare `plugin` entry or the local shim, not
361
+ `.opencode/node_modules`. Use either the bare `plugins` entry or the local shim, not
284
362
  both.
285
363
 
286
- Remediation is built into the same server plugin: the `config` hook registers the
287
- hidden `jevguard-proposer` subagent, so no second entrypoint or shim is needed. The
288
- server plugin reviews and reports turns, and — when remediation is enabled and a review
289
- fails — creates the proposal child session automatically.
364
+ The package's default export is the V2 plugin object (`{ id: "jevguard", setup }`).
365
+ Remediation is built into the same server plugin, so no second entrypoint or shim is
366
+ needed: it reviews and reports executions, and — when remediation is enabled and a
367
+ review fails — creates the proposal child session automatically. The bundled skills are
368
+ registered through the host skill editor (`skill.transform`).
369
+
370
+ ### Claude Code
371
+
372
+ Inside Claude Code:
373
+
374
+ ```text
375
+ /plugin marketplace add pablozr/JevGuard
376
+ /plugin install jevguard@jevguard
377
+ ```
378
+
379
+ The Claude Code plugin reviews each closed unit of work in a detached background
380
+ worker, shows the result in the session (status line, transcript line, and a toast on
381
+ `FAIL`) without sending it to the model, and records it in the same local history.
382
+ Remediation proposals are off by default and are enabled from your user config, never
383
+ from the repository. It needs `node` `>=22.13.0`, `npm`, and `git` on `PATH`. See the
384
+ [Claude Code guide](./docs/configuration.md#claude-code) for what is reviewed, the
385
+ in-session display, and remediation.
290
386
 
291
387
  ### Author rules with the bundled skill
292
388
 
@@ -335,11 +431,36 @@ jevguard login
335
431
 
336
432
  Known limitations:
337
433
 
338
- - Target host is OpenCode `1.18.32`. Exact `1.18.32` runtime smoke is still
339
- pending; a local `1.18.28` run worked.
434
+ - Target host is OpenCode `2.2.x` (minimum `2.2.2`). The plugin does not load on
435
+ `2.1.x` or `1.x`. A real-host smoke on `2.2.2` (2026-11-02) covered observe
436
+ and `remediate` (FAIL review, deny-all proposer, attempt 1/2, `RESOLVED`).
437
+ - OpenCode `2.2.2` does not install dependencies for a `file:` plugin spec. Use
438
+ the registry package; a local `file:` build needs its dependencies installed
439
+ next to it.
340
440
  - The plugin and CLI run on Bun, and the packed `jevguard` bin keeps a Bun
341
441
  shebang. Install and run the artifact with the Bun runtime.
342
442
 
443
+ ### OpenCode 2.2.x limitations
444
+
445
+ - **Minimum version `2.2.2`.** The plugin declares `engines.opencode` `>=2.2.2 <2.3`
446
+ and is pinned to the `2.2.x` plugin API.
447
+ - **Git-ignored files are out of scope.** The execution snapshot comes from the host's
448
+ working-tree diff, which leaves out files ignored by Git; a write the diff cannot
449
+ show makes the execution `UNAVAILABLE` (`INCOMPLETE_FILE_DIFF`) instead of reviewing
450
+ a partial patch.
451
+ - **Interrupted or failed executions are not reviewed.** Their writes stay on disk and
452
+ count as pre-existing changes for the next execution's snapshot, so they are not
453
+ attributed to it.
454
+ - **`opencode run --standalone`.** The review runs after the execution succeeds; a
455
+ standalone run can exit before it finishes. JevGuard reviews reliably in the TUI or
456
+ with `opencode serve` plus `opencode run --server`.
457
+ - **Concurrent executions.** When two sessions run at once, or an execution starts
458
+ while the previous one is still being finished, the review is `UNAVAILABLE`
459
+ (`CONCURRENT_SESSIONS`, `BOUNDARY_RACE`) instead of mixing changes. Nested child
460
+ sessions count as part of their parent execution.
461
+ - **No toast or TUI routing.** The server plugin reports through structured logs
462
+ written to the host's stderr capture.
463
+
343
464
  ## Connect Jev
344
465
 
345
466
  For local use, connect once with the installed command:
@@ -364,8 +485,8 @@ JevGuard FAIL
364
485
  pass 1 · warn 1 · fail 1 · skipped 0 · unavailable 0
365
486
  ```
366
487
 
367
- One toast per turn shows the aggregate outcome and the outcome counts; the
368
- structured log carries every rule's result, its raw violation probability or typed
488
+ The structured log (and, on hosts with a toast channel, one toast per turn) shows
489
+ the aggregate outcome and the outcome counts; the structured log carries every rule's result, its raw violation probability or typed
369
490
  reason, and its scoped paths. The counts include every entry, including the single
370
491
  synthetic entry a review-level failure produces.
371
492
 
@@ -373,6 +494,93 @@ synthetic entry a review-level failure produces.
373
494
  rule had no applicable change to evaluate. `UNAVAILABLE` means JevGuard could not
374
495
  safely or completely evaluate that rule.
375
496
 
497
+ ## Review history
498
+
499
+ JevGuard keeps a bounded, local, content-free history of your reviews so you can see
500
+ how turns fared without grepping the host log. Every review appends the same safe
501
+ structured-log projection plus an ISO 8601 timestamp to one JSON Lines file:
502
+
503
+ ```text
504
+ <XDG_DATA_HOME or ~/.local/share>/jevguard/reviews.jsonl
505
+ ```
506
+
507
+ The file holds the aggregate counts, each result's rule or check ID, its outcome, and
508
+ its typed reason or raw probability — the same allowlist as the structured log. It
509
+ never holds a task, diff, prompt, model output, credential, or environment value. It
510
+ is local-only, bounded (it rewrites itself once it passes 5 MB, keeping the newest
511
+ records), and safe to delete at any time; the next review recreates it.
512
+
513
+ ```sh
514
+ # human-readable summary
515
+ jevguard report
516
+
517
+ # the same aggregation as JSON
518
+ jevguard report --json
519
+ ```
520
+
521
+ The report prints aggregated counts, rule IDs, check IDs, and reason codes only —
522
+ never file paths, tasks, diffs, or credentials. A missing or empty history is a
523
+ normal empty report, not an error.
524
+
525
+ ### Rule feedback
526
+
527
+ Tell JevGuard whether a `FAIL` was right. The answer is stored locally and only
528
+ feeds the calibration shown by `jevguard report`; it never changes a rule, a
529
+ threshold, or what the model returns.
530
+
531
+ ```sh
532
+ # unanswered rule FAILs, newest first, numbered
533
+ jevguard feedback
534
+
535
+ # answer one: right = a real violation, wrong = a false alarm
536
+ jevguard feedback 1 wrong
537
+ ```
538
+
539
+ Numbers stay fixed to the last listing. Built-in checks are not covered. See
540
+ [configuration](./docs/configuration.md)
541
+ for details.
542
+
543
+ ## Diagnose your setup
544
+
545
+ `jevguard doctor` checks your local setup without touching the network or changing
546
+ anything:
547
+
548
+ ```sh
549
+ jevguard doctor
550
+ jevguard doctor --json
551
+ ```
552
+
553
+ It reports `OK`, `WARN` or `FAIL` for the credential source, `.jev/rules.md`,
554
+ `.jev/config.yaml` and the OpenCode plugin registration, each with a hint when
555
+ something is wrong. It never prints the API key or any part of it — only whether the
556
+ key comes from the OS credential store or the `TYPESAFE_API_KEY` override. It exits
557
+ `0` unless a check fails (warnings are fine) and `1` when one does. The project root is
558
+ the first parent directory of the current directory that contains `.git`, or the
559
+ current directory when there is none. It does not check Claude Code installs yet.
560
+
561
+ ## Check rules on demand
562
+
563
+ `jevguard rules check` runs your repository rules against a diff you provide, without
564
+ waiting for an agent turn:
565
+
566
+ ```sh
567
+ git diff --cached | jevguard rules check --diff - --task "Add rate limiting to login"
568
+ jevguard rules check --diff change.diff --rule ARCH-001 --json
569
+ ```
570
+
571
+ It evaluates the repository rules over the diff with the same scope filtering,
572
+ evidence safety, slicing and gate thresholds as a review, but runs no built-in checks,
573
+ starts no remediation and writes nothing to the review history. The input must be
574
+ plain `git diff` output (or a plain `---`/`+++` unified diff) with `a/` and `b/`
575
+ prefixes; anything else is refused whole, never partly evaluated (see
576
+ [configuration](./docs/configuration.md#cli-diagnostics-and-on-demand-rule-checks)).
577
+ `--task` is optional; without it a neutral default task is sent. It prints each rule's outcome, probability, severity and
578
+ scoped paths, never the task or the diff. Exit codes: `0` completed with no `FAIL` and
579
+ no `UNAVAILABLE`; `1` at least one rule `FAIL`ed; `2` it could not check completely
580
+ (invalid arguments, an unreadable or unsupported diff, an unknown `--rule`, or an
581
+ `UNAVAILABLE` result). See [configuration](./docs/configuration.md#cli-diagnostics-and-on-demand-rule-checks)
582
+ for the full contract.
583
+
376
584
  ## Implemented behavior
377
585
 
378
586
  The current release implements the full local review path:
@@ -383,10 +591,13 @@ The current release implements the full local review path:
383
591
  - Every `## <RULE-ID>` block in `.jev/rules.md` is parsed in source order. Each
384
592
  block is validated independently, so one invalid block does not suppress its
385
593
  valid siblings, and every occurrence of a duplicated ID is invalid.
386
- - Each valid rule is processed independently. It asks Jev at most once, and only
387
- when the gate config is valid, the rule is applicable, and its scoped evidence is
388
- complete and safe; it produces one typed violation probability or an operational
389
- outcome.
594
+ - Each valid rule is processed independently. Its complete scoped evidence is planned
595
+ locally, then every planned slice across all rules is dispatched through one batched
596
+ Jev entry that resolves the credential once and uses bounded concurrency; each slice
597
+ asks one Noul. The rule result is the deterministic maximum of its slice judgments,
598
+ and a fitting rule still issues exactly one call. The rule asks Jev only when the
599
+ gate config is valid, the rule is applicable, and its scoped evidence is complete and
600
+ safe; it produces one typed violation probability or an operational outcome.
390
601
  - A failure before rule evaluation — no attributed diff, unreadable policy files, or
391
602
  a missing `.jev/rules.md` — is reported by the rule lane as one synthetic
392
603
  `UNAVAILABLE` entry with no rule ID, not one result per declared rule. When the
@@ -401,26 +612,28 @@ The current release implements the full local review path:
401
612
  - Both built-ins are sent as one batch request with two independent named answers.
402
613
  One malformed or missing answer fails only its own check; the valid sibling still
403
614
  gates. A failed or malformed batch envelope makes both checks `UNAVAILABLE`.
404
- - The built-in batch runs concurrently with the sequential rule lane. One shared FIFO
405
- concurrency limit allows at most two Jev requests in flight across the plugin
406
- instance, counting the batch as one request. With no attributed patch a built-in is
407
- `SKIPPED`; blocked or oversized evidence is `UNAVAILABLE`; a rule, policy-load, or
408
- config failure never suppresses the batch, and one built-in's answer never
409
- suppresses the other.
410
- - Reviews run in the background. The idle event resolves as soon as the serialized
411
- attribution step is scheduled, so policy reads, Jev calls, and presentation do not
412
- block the agent.
615
+ - The built-in batch runs concurrently with the rule lane. The rule lane dispatches
616
+ one batched request for all of the turn's planned slices with a bounded adapter
617
+ concurrency cap of four; the built-in batch keeps its own single request. One shared
618
+ FIFO concurrency limit allows at most two Jev entries in flight across the plugin
619
+ instance, counting the rule batch and the built-in batch as one entry each. With no
620
+ attributed patch a built-in is `SKIPPED`; blocked or oversized evidence is
621
+ `UNAVAILABLE`; a rule, policy-load, or config failure never suppresses the batch, and
622
+ one built-in's answer never suppresses the other.
623
+ - Reviews run in the background. The `session.execution.succeeded` handler returns
624
+ as soon as the serialized attribution step is scheduled, so policy reads, Jev calls,
625
+ and presentation do not block the agent.
413
626
  - The local gate maps each rule to `PASS`, `WARN`, or `FAIL`; a rule with no
414
627
  applicable scope is `SKIPPED`, and a rule that cannot be safely evaluated is
415
628
  `UNAVAILABLE`.
416
- - One aggregate TUI toast and one structured log entry report the turn. The log
629
+ - One structured log entry reports the turn (the `2.2.x` server plugin has no toast
630
+ channel). The log
417
631
  carries every entry's outcome, raw probability, or reason, including the built-in
418
632
  results and the synthetic review-level entry when there is one. The fixed result
419
633
  order is rules in source order, then `SCOPE-CREEP`, then `COMPLEXITY`.
420
634
  - After presentation, when remediation is enabled and the review contains any `FAIL`,
421
- at most one aggregate proposal request is sent to the hidden `jevguard-proposer`
422
- subagent in an isolated child session parented to the source session, and a generic
423
- toast reports that the proposal is ready. The request carries every `FAIL` finding
635
+ at most one aggregate proposal request is sent to an isolated, deny-all child
636
+ session parented to the source session, with an explicit model. The request carries every `FAIL` finding
424
637
  (local `error` rules and the `SCOPE-CREEP` built-in), the task, and the complete,
425
638
  safe, full attributed patch; a blocked or oversized patch records no proposal. A
426
639
  notifier or host failure never affects the review, presentation, or later turns.
@@ -432,30 +645,25 @@ ordinary turn is reviewed normally.
432
645
 
433
646
  ## Roadmap
434
647
 
435
- The multi-rule, scope-creep, and complexity slices are implemented. Planned work
436
- beyond them:
648
+ Implemented so far: multi-rule reviews, the `SCOPE-CREEP` and `COMPLEXITY`
649
+ built-ins, the `jev-init` policy bootstrap, local review history with feedback and
650
+ calibration (`jevguard report`, `jevguard feedback`), on-demand checks
651
+ (`jevguard rules check`, `jevguard doctor`), and the Claude Code plugin. Planned work:
437
652
 
438
653
  ```text
439
- V0.3 Remaining built-in semantic check: test adequacy
440
- ↓
441
- V0.4 jev-init: evidence-based policy bootstrap
442
- ↓
443
- V0.5 On-demand evidence and explanations
444
- ↓
445
- V0.6 Opt-in, bounded auto-remediation
446
- ↓
447
- V0.7 Observe / enforce / remediate modes
448
- ↓
449
- V0.8 Rule packs
450
- ↓
451
- V0.9 Local feedback and calibration
452
- ↓
453
- V1 Claude Code and Codex adapters
654
+ V0.3 Remaining built-in semantic check: test adequacy planned
655
+ V0.4 jev-init: evidence-based policy bootstrap done
656
+ V0.5 On-demand evidence and explanations partial (rules check)
657
+ V0.6 Opt-in, bounded auto-remediation planned
658
+ V0.7 Observe / enforce / remediate modes planned
659
+ V0.8 Rule packs planned
660
+ V0.9 Local feedback and calibration done
661
+ V1 Claude Code and Codex adapters Claude Code done, Codex planned
454
662
  ```
455
663
 
456
664
  The auto-propose slice is implemented: after a review with a `FAIL`, an enabled
457
- configuration asks the hidden `jevguard-proposer` subagent for a strategy in an
458
- isolated child session, and never applies a change. The `V0.6` entry remains the
665
+ configuration asks for a strategy in an isolated, deny-all child session, and never
666
+ applies a change. The `V0.6` entry remains the
459
667
  future, unattended corrective loop; the proposer is a bounded, strategy-only step, not
460
668
  that loop.
461
669
 
@@ -468,17 +676,24 @@ CI and pull-request policy review come after V1, using the same versioned rules.
468
676
 
469
677
  ## Architecture
470
678
 
471
- JevGuard is a pnpm TypeScript monorepo. The core has no dependency on OpenCode or
472
- its runtime, which keeps the evaluation model portable to future adapters.
679
+ JevGuard is a pnpm TypeScript monorepo. The core has no dependency on OpenCode,
680
+ Claude Code, or their runtimes, which keeps the evaluation model portable across
681
+ host adapters.
473
682
 
474
683
  ```text
475
684
  packages/
476
685
  ├── core/ domain, policy, evaluation, gate
686
+ ├── claude-code-adapter/ Claude Code tool-attributed units, hook and review worker
687
+ ├── claude-code-plugin/ Claude Code plugin composition and artifact
688
+ ├── node-infra/ shared Node infra: credentials, Jev transport, policy, history
477
689
  ├── opencode-adapter/ OpenCode attribution and presentation
478
690
  ├── plugin/ OpenCode composition root and CLI
479
691
  └── testkit/ fixtures and contract helpers
480
692
 
481
- plugin → opencode-adapter → core
693
+ plugin → opencode-adapter → node-infra → core
694
+ plugin ────────────────────→ node-infra
695
+ claude-code-plugin → claude-code-adapter → node-infra → core
696
+ testkit ──────────────────────────────→ core
482
697
  ```
483
698
 
484
699
  ## Status
@@ -489,18 +704,23 @@ loads in OpenCode, attributes one completed turn, parses every rule block in
489
704
  the `SCOPE-CREEP`
490
705
  and `COMPLEXITY` built-ins over the complete attributed patch behind one shared
491
706
  concurrency limit, applies the local gate to each, and presents one aggregate result
492
- as a transient TUI toast and a structured log entry.
707
+ as a structured log entry.
493
708
 
494
709
  The local, private tarball (`pnpm artifact:build`, `pnpm artifact:pack`) packages
495
710
  that slice so a clean consumer can install it without workspace links.
496
711
 
497
- OpenCode sees only transient toasts, structured logs, and — when remediation is
498
- enabled and a review has a `FAIL` — one proposal child session and a generic toast.
712
+ OpenCode sees only structured logs and — when remediation is enabled and a review
713
+ has a `FAIL` — one proposal child session.
499
714
  The review does not inject anything into the agent context, prompt the source session,
500
715
  add session messages, or block a task. The proposal is a separate, configurable,
501
716
  strategy-only workflow delivered by the server plugin; it never edits code and never
502
- applies a change. Real-host validation against exact OpenCode `1.18.32` is still
503
- pending.
717
+ applies a change. A real-host smoke on OpenCode `2.2.2` passed; see
718
+ [OpenCode 2.2.x limitations](#opencode-22x-limitations).
719
+
720
+ The Claude Code plugin reuses the same core, policy files, gate, and local history. It
721
+ reviews closed units of work in a detached worker and shows results in the session
722
+ without sending them to the model; see the
723
+ [Claude Code guide](./docs/configuration.md#claude-code).
504
724
 
505
725
  ## Development
506
726