@pablozrrrr/jevguard 0.1.0 → 0.2.2

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
@@ -27,10 +27,13 @@ specific policy, and leaves planning, code generation, and correction with the
27
27
  coding agent.
28
28
 
29
29
  > [!WARNING]
30
- > JevGuard is in active development. The current release targets OpenCode `1.18.31`,
31
- > evaluates every rule block declared in `.jev/rules.md` plus the `SCOPE-CREEP` and
32
- > `COMPLEXITY` built-ins, and runs in observe mode only: it reports results but never
33
- > alters the agent context or blocks a task.
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.
34
37
 
35
38
  ## Why JevGuard
36
39
 
@@ -122,7 +125,7 @@ malformed answer fails only its own check.
122
125
  `SCOPE-CREEP` asks whether the change contains material functional, behavioral,
123
126
  architectural, dependency, configuration, documentation, or refactoring work the task
124
127
  did not request and that is not reasonably necessary to complete it. It uses fixed
125
- `error` thresholds (`40%`/`70%`), independent of `.jev/config.yaml`.
128
+ `error` thresholds (`65%`/`90%`), independent of `.jev/config.yaml`.
126
129
 
127
130
  `COMPLEXITY` asks whether the change introduces material complexity disproportionate
128
131
  to, or not reasonably necessary for, completing the task, such as unnecessary
@@ -140,32 +143,99 @@ Reviews run in the background. The idle event returns as soon as the serialized
140
143
  attribution step is scheduled; policy reads, Jev calls, and presentation never sit on
141
144
  the agent's critical path.
142
145
 
146
+ ## Safe auto-propose remediation
147
+
148
+ Reviews are observe-only by default: a review never injects feedback into the agent
149
+ context, adds a session message, prompts a session, blocks a turn, or changes your
150
+ code. A separate, configurable path can turn a review result into a proposal, and it is
151
+ delivered entirely by the same server plugin.
152
+
153
+ When `.jev/config.yaml` enables remediation (it is enabled by default) and a review
154
+ contains any `FAIL`, the plugin, after presenting the review, sends one aggregate
155
+ proposal request to a hidden `jevguard-proposer` subagent:
156
+
157
+ - The request aggregates every `FAIL` finding in the review — local `error` rules and
158
+ the `SCOPE-CREEP` built-in — together with the turn's task and its **complete, safe,
159
+ full attributed patch**. `COMPLEXITY` is advisory and never fails, and `warning`
160
+ rules never fail.
161
+ - The full attributed patch must pass the same safety policy as the review. If any
162
+ attributed file is blocked or the patch is oversized, the plugin records no proposal
163
+ at all — even when a rule-scoped `FAIL` exists — because there is no complete safe
164
+ 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.
173
+
174
+ The plugin creates at most one proposal per evaluated turn, and the child session is
175
+ excluded from review for the plugin lifetime, so a proposal can never recurse or
176
+ trigger a second one. A host, model, or toast failure is contained and never changes
177
+ 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
179
+ `error` log entry carrying only a typed reason code, never any task, diff, finding,
180
+ path, or credential.
181
+
182
+ The proposer is a strategy step, not an apply step: it never edits code, and nothing
183
+ 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.
187
+
188
+ The proposal prompt treats the task, paths, diff, rules, and findings as untrusted
189
+ data, and requires a separate explicit confirmation before proposing changes to tests,
190
+ configuration, or dependencies. No secret is ever carried, and no payload, task, diff,
191
+ or secret is logged.
192
+
143
193
  ## Install
144
194
 
145
- Once `@pablozrrrr/jevguard` is published to npm, the intended install is one line in
146
- `opencode.json`:
195
+ 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:
198
+
199
+ ```sh
200
+ # global config: ~/.config/opencode/opencode.json
201
+ jevguard install
202
+
203
+ # project config: ./opencode.json
204
+ jevguard install --project
205
+ ```
206
+
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
211
+ `PATH`, run it through `bunx`:
212
+
213
+ ```sh
214
+ bunx --package @pablozrrrr/jevguard jevguard install
215
+ ```
216
+
217
+ The manual alternative is one entry in `opencode.json`:
147
218
 
148
219
  ```json
149
220
  { "plugin": ["@pablozrrrr/jevguard"] }
150
221
  ```
151
222
 
152
- OpenCode then resolves the package from the npm registry or its cache and loads it
153
- on the next start. Adding the entry only loads the plugin; it does **not** put the
154
- `jevguard` CLI on `PATH` (see [Install the CLI](#install-the-cli)). Restart
155
- OpenCode after changing the plugin list.
223
+ OpenCode resolves the package from the npm registry or its cache and loads it on the
224
+ next start. Adding the entry only loads the plugin; it does **not** put the
225
+ `jevguard` CLI on `PATH` (see [Install the CLI](#install-the-cli)). Restart OpenCode
226
+ after changing the plugin list.
156
227
 
157
228
  > [!IMPORTANT]
158
- > `@pablozrrrr/jevguard` is **not published to npm yet**. The one-line entry above is
159
- > future behavior and will not resolve until the package is released. Before
160
- > publication, load the plugin as a local artifact plugin instead.
229
+ > A bare plugin entry resolves only from the npm registry. To run an unreleased
230
+ > local build, load the packed tarball as a local artifact plugin instead.
161
231
 
162
232
  ### Build the local artifact
163
233
 
164
234
  The artifact ships the plugin as a **locally packed tarball** that bundles the
165
235
  private `@jevguard/core` and `@jevguard/opencode-adapter` workspace code, so a
166
236
  clean consumer installs one tarball and never resolves a workspace link or a
167
- private registry package. It also packages the `jevguard-rules` skill and its
168
- private rule validator under `skills/jevguard-rules/`.
237
+ private registry package. It also packages the `jevguard-rules` and `jev-init`
238
+ skills and their private validators under `skills/<name>/`.
169
239
 
170
240
  Build the tarball from this repository:
171
241
 
@@ -184,12 +254,19 @@ The packed manifest depends only on public runtime packages
184
254
  the tarball therefore needs npm registry access for those packages; the artifact
185
255
  does not vendor them and does not promise an offline install.
186
256
 
187
- ### Load the local plugin before publication
257
+ ### Load the plugin
188
258
 
189
- OpenCode `1.18.31` resolves a **bare** `opencode.json` `plugin` entry from the npm
190
- registry or its cache, not from the consumer's `node_modules`. Until the package is
191
- published, install the tarball into the project's `.opencode` directory and load it
192
- through a local plugin shim:
259
+ OpenCode `1.18.32` resolves a **bare** `opencode.json` `plugin` entry from the npm
260
+ registry or its cache:
261
+
262
+ ```json
263
+ {
264
+ "plugin": ["@pablozrrrr/jevguard"]
265
+ }
266
+ ```
267
+
268
+ To run an unreleased local build instead, install the packed tarball into the
269
+ project's `.opencode` directory and load it through a local plugin shim:
193
270
 
194
271
  ```sh
195
272
  cd /path/to/consumer/.opencode
@@ -203,16 +280,28 @@ export { JevGuardPlugin } from "@pablozrrrr/jevguard";
203
280
 
204
281
  OpenCode loads `.opencode/plugins/` with its bundled Bun runtime. The shim runs as
205
282
  a local plugin module and resolves `@pablozrrrr/jevguard` from
206
- `.opencode/node_modules`. Do **not** add `@pablozrrrr/jevguard` to the `opencode.json`
207
- `plugin` list before publication; that bare entry does not resolve locally.
283
+ `.opencode/node_modules`. Use either the bare `plugin` entry or the local shim, not
284
+ both.
285
+
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.
208
290
 
209
291
  ### Author rules with the bundled skill
210
292
 
211
- The package ships a `jevguard-rules` OpenCode skill beside the plugin. On startup
212
- the plugin's `config` hook appends the installed skill directory to the host's
213
- `skills.paths`, so the skill is discovered without a manual `opencode.json` entry.
214
- Skill discovery is read at OpenCode startup: restart OpenCode after installing or
215
- updating the package so the skill appears.
293
+ The package ships two OpenCode skills beside the plugin: `jevguard-rules` to author
294
+ rules, and `jev-init` to bootstrap a policy where none exists. On startup the plugin's
295
+ `config` hook appends the installed skill directories to the host's `skills.paths`, so
296
+ the skills are discovered without a manual `opencode.json` entry. Skill discovery is
297
+ read at OpenCode startup: restart OpenCode after installing or updating the package so
298
+ the skills appear.
299
+
300
+ `jev-init` runs only when neither `.jev/rules.md` nor `.jev/config.yaml` exists. It
301
+ reads a bounded inventory of the repository, keeps rules only from the user's explicit
302
+ constraints and strongly evidenced local conventions, records provenance in a preamble,
303
+ and previews both candidate files before requiring explicit confirmation naming both
304
+ exact paths. Once a policy exists, every rule change belongs to `jevguard-rules`.
216
305
 
217
306
  Policy content is different. `.jev/rules.md` and `.jev/config.yaml` are read once
218
307
  per attributed turn, so editing rule text takes effect on the next reviewed turn
@@ -223,8 +312,8 @@ Jev.
223
312
 
224
313
  ### Install the CLI
225
314
 
226
- Loading the plugin does not expose the `jevguard` binary on `PATH`. Once the
227
- package is published, run the login command without a global install:
315
+ Loading the plugin does not expose the `jevguard` binary on `PATH`. Run the login
316
+ command without a global install:
228
317
 
229
318
  ```sh
230
319
  bunx --package @pablozrrrr/jevguard jevguard login
@@ -237,7 +326,7 @@ bun install --global @pablozrrrr/jevguard
237
326
  jevguard login
238
327
  ```
239
328
 
240
- Until the package is published, install the local tarball globally instead:
329
+ To run an unreleased local build, install the packed tarball globally instead:
241
330
 
242
331
  ```sh
243
332
  bun install --global /path/to/pablozrrrr-jevguard-<version>.tgz
@@ -246,9 +335,7 @@ jevguard login
246
335
 
247
336
  Known limitations:
248
337
 
249
- - `@pablozrrrr/jevguard` has no registry release yet, so the one-line `opencode.json`
250
- plugin entry does not resolve until the package is published.
251
- - Target host is OpenCode `1.18.31`. Exact `1.18.31` runtime smoke is still
338
+ - Target host is OpenCode `1.18.32`. Exact `1.18.32` runtime smoke is still
252
339
  pending; a local `1.18.28` run worked.
253
340
  - The plugin and CLI run on Bun, and the packed `jevguard` bin keeps a Bun
254
341
  shebang. Install and run the artifact with the Bun runtime.
@@ -288,7 +375,7 @@ safely or completely evaluate that rule.
288
375
 
289
376
  ## Implemented behavior
290
377
 
291
- The current release implements the full local, observe-only path:
378
+ The current release implements the full local review path:
292
379
 
293
380
  - OpenCode V1 plugin loads.
294
381
  - A completed assistant response is detected.
@@ -330,9 +417,18 @@ The current release implements the full local, observe-only path:
330
417
  carries every entry's outcome, raw probability, or reason, including the built-in
331
418
  results and the synthetic review-level entry when there is one. The fixed result
332
419
  order is rules in source order, then `SCOPE-CREEP`, then `COMPLEXITY`.
333
-
334
- No feedback is injected into the agent session. No remediation is attempted. No
335
- task is blocked.
420
+ - 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
424
+ (local `error` rules and the `SCOPE-CREEP` built-in), the task, and the complete,
425
+ safe, full attributed patch; a blocked or oversized patch records no proposal. A
426
+ notifier or host failure never affects the review, presentation, or later turns.
427
+
428
+ The review itself injects no feedback into the agent session, prompts no session, and
429
+ blocks no task. The proposal above is a strategy only; nothing is applied, and
430
+ the user copies its manual apply instruction into their normal coding agent, whose
431
+ ordinary turn is reviewed normally.
336
432
 
337
433
  ## Roadmap
338
434
 
@@ -357,6 +453,17 @@ V0.9 Local feedback and calibration
357
453
  V1 Claude Code and Codex adapters
358
454
  ```
359
455
 
456
+ 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
459
+ future, unattended corrective loop; the proposer is a bounded, strategy-only step, not
460
+ that loop.
461
+
462
+ The `V0.4` `jev-init` skill is implemented: when no policy exists it bootstraps
463
+ `.jev/rules.md` and `.jev/config.yaml` from the user's explicit constraints and
464
+ strongly evidenced local conventions, records each rule's provenance, and writes both
465
+ files only after validating the confirmed candidates.
466
+
360
467
  CI and pull-request policy review come after V1, using the same versioned rules.
361
468
 
362
469
  ## Architecture
@@ -376,9 +483,10 @@ plugin → opencode-adapter → core
376
483
 
377
484
  ## Status
378
485
 
379
- The multi-rule, multi-built-in, observe-only review is implemented. It loads in
380
- OpenCode, attributes one completed turn, parses every rule block in `.jev/rules.md`,
381
- asks Jev once per applicable rule and once per built-in batch, runs the `SCOPE-CREEP`
486
+ The multi-rule, multi-built-in, background and observe-only review is implemented. It
487
+ loads in OpenCode, attributes one completed turn, parses every rule block in
488
+ `.jev/rules.md`, asks Jev once per applicable rule and once per built-in batch, runs
489
+ the `SCOPE-CREEP`
382
490
  and `COMPLEXITY` built-ins over the complete attributed patch behind one shared
383
491
  concurrency limit, applies the local gate to each, and presents one aggregate result
384
492
  as a transient TUI toast and a structured log entry.
@@ -386,9 +494,13 @@ as a transient TUI toast and a structured log entry.
386
494
  The local, private tarball (`pnpm artifact:build`, `pnpm artifact:pack`) packages
387
495
  that slice so a clean consumer can install it without workspace links.
388
496
 
389
- OpenCode sees only transient toasts and structured logs. JevGuard does not inject
390
- anything into the agent context, does not add session messages, and does not block
391
- a task. Real-host validation against exact OpenCode `1.18.31` is still pending.
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.
499
+ The review does not inject anything into the agent context, prompt the source session,
500
+ add session messages, or block a task. The proposal is a separate, configurable,
501
+ 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.
392
504
 
393
505
  ## Development
394
506