Skip to content
MrJev

agent-dispatcher

Routes a Claude Code or Codex task to one of 27 specialist roles and defines what evidence will count as done.

View on GitHub →

Hands-on review

27 agent roles with an optional Jev router, all scopes off by default. The most careful codebase here; its redactor misses AWS_SECRET_ACCESS_KEY.

Good for

  • Naming a credential file explicitly does not get it read — we tested it
  • Every Jev scope ships disabled, and the state is only your task and the role list
  • Zero third-party dependencies, asserted by a test that fails the build

Watch out for

  • The redactor's `\b` lets `AWS_SECRET_ACCESS_KEY=` through unredacted
  • The credential skip list misses `*.env`, `.pgpass`, `*.tfvars` and `.htpasswd`
  • Its NOTICE says 24 of 27 role files came from a pack with no licence

Tested Sep 20, 2026 at a709161 · python:3.12-slim in Docker with --network none; all eight CI suites run

How we reviewed this: we ran all eight CI suites in python:3.12-slim with --network none, and drove the retrieval path against a planted tree. We made no Jev calls.

What it does

A plugin pack for Claude Code and Codex: 27 agent roles generated from templates, a catalogue of skills and MCP servers, and three pure-stdlib tools — a bounded read-only code search, a project map and a doctor. An optional Jev decision engine picks a role, a skill set and a tool set, and it is off by default: all five scopes are False in decision/config.py:50.

With the scopes on, one plan run makes exactly two requests, and the state has two keys — task and selected_role. No source, no file contents, no environment variables, no conversation history. The 19 MCP servers in the catalogue are references; catalog/mcp.json:3 says “Nothing here is installed by this repository”, and nothing in the code connects to any of them.

It closes the bypass

We have now found the same bug in nine tools: an exclusion list that guards the directory walk, defeated by naming a file explicitly. This is the second project to close it, and it does so more thoroughly than the first.

context.py:436 states the rule as a docstring:

def _explicit_paths(task, paths, project=None):
    """Recognize actual enumerated paths, never infer readable paths from task text.

and the enforcement is in the ordering — exclusion is decided before the read, at context.py:693:

reason = ("explicit task exclusion" if _excluded(path, manual_exclusions) else
          "automatic task exclusion" if _excluded(path, automatic) else _skip(path))
if reason:
    excluded.append({"path": scrub(path), "reason": reason})
    continue

Naming a path only raises its score; it never grants access. We put five protected files in a tree and wrote a task that named every one of them and demanded “Include them all as evidence”:

excerpt paths: ['src/auth.py']
  excl: {'path': '.env', 'reason': 'credential file withheld'}
  excl: {'path': 'config/secrets.yaml', 'reason': 'credential file withheld'}
  excl: {'path': 'credentials.json', 'reason': 'credential file withheld'}
  excl: {'path': 'id_rsa', 'reason': 'credential file withheld'}
  excl: {'path': 'server.pem', 'reason': 'credential file withheld'}

Even a task that says “do not exclude .env” only keeps the path out of the automatic list; _skip still stops it. The project_map CLI has no per-file entry point at all.

The rest of the codebase reads the same way. context.py:217 checks every path segment for symlinks including directories, resolves against the project root, opens with O_NOFOLLOW|O_NONBLOCK, fstats for a regular file, and re-fstats after reading to catch a size change — the most careful “read one file” we have seen. typesafe.py:52 disables redirects because urllib would otherwise copy the Authorization header to an arbitrary host. engine.py:365 turns “a decision expresses relevance, never authorisation” into an executable assertion that fails the build.

The redactor has a word-boundary hole

decision/redact.py:21:

_SECRET_WORD = r"(?:api[_-]?key|secret|token|password|passwd|passphrase|credential)s?"

and at :37, the key/value pattern:

re.compile(r"(?i)\b" + _SECRET_WORD + r"\b[ \t]{0,4}[:=][ \t]{0,4}[\"']?[^\s\"',;]{6,}"),

_ is a word character, so \b cannot match between DB_ and PASSWORD. We called scrub() directly:

input result
API_KEY=abcdef123456 redacted
password=hunter2hunter2 redacted
Authorization: Bearer abc123def456 redacted
DB_PASSWORD=prodpassword999 plaintext
MY_SECRET=topsecretvalue plaintext
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/… plaintext

The last one is probably the most common secret variable name in existence. The impact is doubled because scrub is used both for local excerpts (context.py:710) and for the task text sent upstream (decision/jev.py:90) — so pasting a traceback containing AWS_SECRET_ACCESS_KEY= into a task, with a decision scope enabled, sends it.

redact.py:1 honestly calls itself “a defensive layer, not a guarantee”, and that framing is right.

We first wrote that this was a one-character fix. The author checked and corrected us, and he is right: dropping the leading \b catches DB_PASSWORD and MY_SECRET, but not AWS_SECRET_ACCESS_KEY, because there the pattern’s trailing boundary and its [:=] both land on _ACCESS_KEY rather than the separator. We verified this ourselves afterwards. Catching the common case needs a name-aware pattern that tolerates [A-Z0-9_]* on both sides of the keyword.

The skip list is narrower than it looks

context.py:210:

if (lower.startswith((".env", "credentials", "secrets"))
        or lower in {".npmrc", ".pypirc", ".netrc", "id_rsa", "id_ed25519", "id_ecdsa"}
        or lower.endswith((".pem", ".key", ".p12", ".pfx", ".keystore")):

startswith on the basename means only names that begin with .env. We checked:

file skipped
.env yes
id_rsa yes
envs/prod.env no
config-secrets.yaml no
infra/terraform.tfvars no
.pgpass no
.htpasswd no
service-account.json no

The mechanism is right; the table just needs more rows.

Verdict

MIT, 35 commits, one day old, and the best CI in this batch: an ubuntu-24.04 × Python 3.10–3.14 matrix plus macos-15, followed by git diff --exit-code to catch stale committed artifacts, with Gitleaks over full history and CodeQL alongside. Every action pinned to a commit SHA, persist-credentials: false. All eight suites pass offline, including 176 end-to-end tests.

One thing we asked about: the NOTICE file says 24 of the 27 role files derive from a “Locus Agent Template Pack v1.0.0” that “carries no copyright line, no author attribution, and no license”, and that if the pack came from someone with rights, “the 24 derived roles need their permission or a rewrite”. The author has since told us that Locus is his own earlier project and the wording came out of stripping Locus-specific references when this was split off — adding that he has not rechecked the original records and does not want to overstate it. That reads as a documentation artefact rather than a live rights problem, and we would still like the NOTICE updated to say so.

There is also a class of silent-default behaviour worth knowing: decision/config.py:224 discards invalid environment values, so AGENT_DISPATCHER_DECISION_MODE=reqiured (one letter off required) quietly reverts to auto, and status says Config from defaults without naming what it rejected. required means “error if Jev can’t answer”; auto means “fall back silently”.

This is the most carefully built thing we have reviewed today, and the two gaps above are both small tables. We’ve reported them.

For the bypass in its unfixed form, see perch.

See how it compares with other tools in Best Jev tools, tested hands-on.

Review updated Sep 20, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.

More in Coding Agents & Developer Tools

fast-jev-compaction

★ 7.8k▲ 59

tamaratran/fast-jev-compaction

Claude Code plugin that replaces the compaction summary with Jev decisions. Every tool call and result is scored; stale ones are dropped or truncated, and everything kept stays verbatim.

TypeScriptReviewed

Foreman

★ 804▲ 17

thruwire/foreman

Puts Jev as a fast supervisor above slower coding agents such as Codex, starting from a ticket, spec, or bug report.

PythonReviewed

Jev Review

★ 695▲ 4

devagrawal09/jev-review

Staged code-review workflow for JavaScript and TypeScript with a local dashboard. Jev screens correctness, security, reliability, compatibility, and test risk, then scores severity and suggests a reviewer, with no generative model involved.

TypeScriptReviewed

Get new Jev projects every week

New Jev releases, pricing changes, and the best new projects, once a week. No spam; unsubscribe anytime.

Powered by Buttondown. See our privacy policy.