Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 39 additions & 6 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,49 @@
# pre-commit (https://pre-commit.com) hooks. Both review the staged changes
# (`--base HEAD`) and need TYPESAFE_API_KEY or a key saved by `jevgate auth login`.
# pre-commit (https://pre-commit.com) and prek hooks. They need
# TYPESAFE_API_KEY (or OPENROUTER_API_KEY, AI_GATEWAY_API_KEY) or a key saved by
# `jevgate auth login`. When JevGate cannot finish (no key, an outage, 60
# seconds gone), the commit or push goes ahead and it says so; `verbose` makes
# pre-commit print that even though the hook passed.
#
# The push hooks judge what each push sends; install that hook type too, with
# `pre-commit install --hook-type pre-push` or
# `default_install_hook_types: [pre-commit, pre-push]` in your config.
- id: jevgate
name: JevGate
description: Review staged changes with JevGate, built from source by pre-commit (needs a Rust toolchain)
entry: jevgate check --base HEAD
description: Judge what is staged before each commit, with jevgate built from source by pre-commit (needs a Rust toolchain)
entry: jevgate check --staged
language: rust
pass_filenames: false
require_serial: true
verbose: true
stages: [pre-commit]
minimum_pre_commit_version: "3.2.0"
- id: jevgate-system
name: JevGate
description: Review staged changes with the jevgate already on PATH (Homebrew, install.sh or cargo)
entry: jevgate check --base HEAD
description: Judge what is staged before each commit, with the jevgate already on PATH (Homebrew, install.sh, npm or cargo)
entry: jevgate check --staged
language: system
pass_filenames: false
require_serial: true
verbose: true
stages: [pre-commit]
minimum_pre_commit_version: "3.2.0"
- id: jevgate-push
name: JevGate
description: Judge what each push sends, with jevgate built from source by pre-commit (needs a Rust toolchain)
entry: jevgate check --pre-push
language: rust
pass_filenames: false
require_serial: true
verbose: true
stages: [pre-push]
minimum_pre_commit_version: "3.2.0"
- id: jevgate-push-system
name: JevGate
description: Judge what each push sends, with the jevgate already on PATH (Homebrew, install.sh, npm or cargo)
entry: jevgate check --pre-push
language: system
pass_filenames: false
require_serial: true
verbose: true
stages: [pre-push]
minimum_pre_commit_version: "3.2.0"
24 changes: 23 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ Notable changes to JevGate. Versions follow [Semantic Versioning](https://semver

## [Unreleased]

## [0.31.0] - 2026-09-28

0.31.0 brings the gate to the roadmap's third moment, the commit: Git hooks judge what a push sends or a commit records, read from Git, and a check that cannot finish lets the change through and says so. Releases now stage the npm package for the maintainer's approval.

### The commit moment

A Git hook runs the gate before each push or commit, on what the push sends or the commit records, read from Git and not from the working tree. A check that cannot finish lets the change through and says so, where it held a commit until its retries ran out, and `jevgate init --git-hook` writes the hook. On the last commits of 83 corpus projects, a check like the push of that commit took 1.0 s at the median, 5.6 s at the 95th percentile and 8.4 s at most, uncached, for $0.097 in all; the default gate stopped 12 of them, all on function-simplification reviews, 8 in the maintainer's own repositories. pre-commit, prek, lefthook, husky and the hook `init` writes each ran end to end in a scratch repository: a review stopped the commit and the push, and an HTTP 402 let both through with the notice.

- `check --pre-push` judges what a push sends. It reads the refs Git passes a pre-push hook on stdin, the ref pre-commit and prek pass as `PRE_COMMIT_TO_REF`, or on a terminal the current branch, and compares each pushed commit with the last commit on its first-parent line that a remote already has, as Qlty's pre-push check compares (qltysh/qlty#2867): a branch pushed before with its last push, a new branch with where it leaves the remote's history, and a rebased branch with where it leaves that history now. A push that merges in commits a remote has is compared with them merged as Git merges them, conflict markers and all: after `git merge origin/main`, the first-parent comparison judged main's changes as the push's own, and this one judges the push's commits and how it resolved the conflicts. Files are read as committed, not as the working tree holds them. Deleted refs are skipped, one check runs for each distinct change, and a branch none of whose history is on a remote is not checked, with a line saying so.
- `check --staged` judges what a commit records: the index against HEAD, never untracked files. `--base HEAD`, which the pre-commit hooks ran until now, judged the working tree and untracked files instead, so a file staged in part with `git add -p` was judged with lines the commit did not hold, and an untracked file could stop a commit it was not part of. The files the change touched are read from the index Git commits (`GIT_INDEX_FILE`, a temporary index for `commit -a` or `git commit PATHS`), and so are the recheck that a request's source has not changed and the `jevgate: allow` comments, which read the working tree and would have stopped or misplaced a partly staged file's findings. A commit that concludes a merge is judged for how the conflicts were resolved; before the first commit, every staged file is new.
- A run that cannot finish lets a commit or a push through, loudly. `on_incomplete = "pass" | "fail"` in `jevgate.toml`, or `--on-incomplete`, decides: `pass` by default with `--staged` and `--pre-push`, `fail` for every other check, so CI still exits 2 on partial evidence. For want of a key, after an HTTP 402, with a provider that stopped answering, at a budget, on a configuration that does not load or a Git failure, the check exits 0 and its last lines on stderr say the commit or push was not checked, and why. Interrupting it still stops the commit.
- Budgets: `--max-seconds` or `max_seconds` stops the asking at a deadline, 60 seconds by default for the two hooks: no request starts and no retry waits past it, and an attempt under way gets only the time left, where a provider that failed every attempt held a run of 100 requests for 8 minutes. The 60 seconds are 7 times the slowest of the 83 last commits above. `--max-cost` or `max_cost` stops it before the estimated spend passes a number of dollars: each request is priced from its size before it is first sent, a retry is not priced again, and stderr says when 75% and 90% are spent. Either leaves the run incomplete, with the answers received cached. After a provider failure that passes with time, the hooks' checks of the next five minutes ask nothing and use only cached answers, as the agent hook does, so an outage holds one commit or push, not each one.
- On a terminal, a hook's check that has run for a second offers to skip itself: Enter lets the change through, saying it was not checked. It is offered only when a person reads stderr as it comes, never through an agent's pipe, where the terminal belongs to whoever runs the agent.
- A stopped commit or push ends with what to do next, for the person and for a coding agent that ran `git commit` or `git push`: fix the findings; a person who judges one acceptable can allow it or baseline it; an agent never bypasses the hook with `--no-verify`, an allow comment or the baseline.
- `jevgate init --git-hook pre-push|pre-commit` writes `.git/hooks/pre-push` or `pre-commit`, never over a hook it did not write, and `--remove` takes out its own. Where pre-commit, prek or lefthook wrote the hook, or `core.hooksPath` (husky's) holds the hooks, it says what to add there instead. The hook lets the change through, saying so, when `jevgate` is not on the PATH.
- pre-commit hooks: `jevgate` and `jevgate-system` run `check --staged` before each commit only, where they ran `check --base HEAD` at every stage installed; the new `jevgate-push` and `jevgate-push-system` run `check --pre-push`. All four are `verbose`, so pre-commit prints the notice of a check that passed without finishing, and need pre-commit 3.2 or later.
- The report says what a hook's check judged: `"staged": true` for `--staged`, and the pushed commit in `pushed_revision` for `--pre-push`; the headline reads `staged lines since 1a2b3c4` or `changed lines from 1a2b3c4 to 9f8e7d6`.
- [Git hooks](https://tech-byte-frontier.github.io/jevgate/git-hooks.html) is a new page, with recipes for pre-commit, prek, lefthook and husky.

### Releases

- Releases stage the npm package, `@tech-byte-frontier/jevgate`, from the release workflow through npm's trusted publishing, with no token and with provenance; each version goes live when the maintainer approves it on npmjs.com. 0.30.0's was published by hand.

## [0.30.0] - 2026-09-28
Expand Down Expand Up @@ -427,7 +448,8 @@ These changes come from running 0.11.0 on six open-source repositories it had ne

- First release: the maintainability CLI.

[Unreleased]: https://github.com/Tech-Byte-Frontier/jevgate/compare/v0.30.0...HEAD
[Unreleased]: https://github.com/Tech-Byte-Frontier/jevgate/compare/v0.31.0...HEAD
[0.31.0]: https://github.com/Tech-Byte-Frontier/jevgate/compare/v0.30.0...v0.31.0
[0.30.0]: https://github.com/Tech-Byte-Frontier/jevgate/compare/v0.25.0...v0.30.0
[0.25.0]: https://github.com/Tech-Byte-Frontier/jevgate/compare/v0.24.1...v0.25.0
[0.24.1]: https://github.com/Tech-Byte-Frontier/jevgate/compare/v0.24.0...v0.24.1
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "jevgate"
version = "0.30.0"
version = "0.31.0"
edition = "2024"
rust-version = "1.90"
description = "Code-review gate for CI and coding agents: asks TypeSafe Jev small questions about functions, files, tests and docs, and reports maintainability, test, security and documentation findings with locations and how often findings like them were right"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ jobs:
- uses: Tech-Byte-Frontier/jevgate-action@v1
with:
api-key: ${{ secrets.TYPESAFE_API_KEY }}
version: 0.30.0
version: 0.31.0
```

It reviews only what the pull request changed (the functions, tests and comments on changed lines, and copies where either copy changed), annotates each finding on its line and writes a job summary; unchanged code is answered from the cache for free. [Continuous integration](https://tech-byte-frontier.github.io/jevgate/ci.html) covers pre-commit, other CI systems, pull requests from forks, budgets and a gate policy the change cannot edit.
It reviews only what the pull request changed (the functions, tests and comments on changed lines, and copies where either copy changed), annotates each finding on its line and writes a job summary; unchanged code is answered from the cache for free. [Continuous integration](https://tech-byte-frontier.github.io/jevgate/ci.html) covers other CI systems, pull requests from forks, budgets and a gate policy the change cannot edit, and [Git hooks](https://tech-byte-frontier.github.io/jevgate/git-hooks.html) run the same gate before each push or commit (`jevgate init --git-hook pre-push`), with recipes for pre-commit, prek, lefthook and husky.

## Output and exit codes

Expand Down
30 changes: 30 additions & 0 deletions jevgate.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@
}
]
},
"OnIncomplete": {
"description": "What a run that could not finish exits with.",
"oneOf": [
{
"const": "pass",
"description": "Exit 0, saying on stderr that the change was not checked, and why",
"type": "string"
},
{
"const": "fail",
"description": "Exit 2",
"type": "string"
}
]
},
"Question": {
"additionalProperties": false,
"description": "A question as a configuration writes it.",
Expand Down Expand Up @@ -485,6 +500,11 @@
"minimum": 1,
"type": "integer"
},
"max_cost": {
"description": "Ceiling on a check's estimated spend in dollars; what is left unasked leaves the run incomplete. Flags can only lower it. Default: unlimited.",
"exclusiveMinimum": 0,
"type": "number"
},
"max_file_bytes": {
"description": "Files larger than this are reported as needs-context, never truncated. Default: 262144.",
"minimum": 1,
Expand All @@ -495,10 +515,20 @@
"minimum": 1,
"type": "integer"
},
"max_seconds": {
"description": "Ceiling on the seconds a check asks for; what is left unasked leaves the run incomplete. Flags can only lower it. Default: 60 with `--staged` and `--pre-push`, else unlimited.",
"maximum": 86400,
"minimum": 1,
"type": "integer"
},
"model": {
"description": "Model, as the key's provider names it; a pinned version keeps results repeatable. `--model` overrides it. Default: `jev-1.13.0` with a TypeSafe key, `typesafe/jev-1.13` with an OpenRouter key, `typesafe-ai/jev` with a Vercel AI Gateway key.",
"type": "string"
},
"on_incomplete": {
"$ref": "#/$defs/OnIncomplete",
"description": "What a run that cannot finish exits with, like `--on-incomplete`: `pass` (exit 0, saying so on stderr) or `fail` (exit 2). Default: `pass` with `--staged` and `--pre-push`, else `fail`."
},
"question": {
"description": "Custom questions: a yes/no question per convention, asked of each unit it names, whose yes is a finding. `.jevgate/questions/` holds one per file, named by its id.",
"items": {
Expand Down
2 changes: 1 addition & 1 deletion npm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tech-byte-frontier/jevgate",
"version": "0.30.0",
"version": "0.31.0",
"description": "JevGate, the code-review gate for CI and coding agents: this package downloads the matching release binary, checks its SHA-256, caches it and runs it",
"keywords": [
"code-review",
Expand Down
2 changes: 1 addition & 1 deletion plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "jevgate",
"displayName": "JevGate",
"version": "0.30.0",
"version": "0.31.0",
"description": "JevGate in the agent's loop: checks each edit and the end of each turn, keeps Claude working while findings fail the gate, and adds JevGate's MCP tools and a skill for acting on findings. Runs the jevgate command, 0.27 or later, which you install separately.",
"author": {
"name": "Tech Byte Frontier",
Expand Down
1 change: 1 addition & 0 deletions site/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# Using JevGate

- [Continuous integration](ci.md)
- [Git hooks](git-hooks.md)
- [Coding agents](coding-agents.md)
- [Configuration](configuration.md)
- [Custom questions](custom-questions.md)
Expand Down
Loading
Loading