context-attachment-manager
Claude Desktop용 플러그인, 다음 메시지에 사용할 과거 이미지 선택 가능
Claude Code용 모드로 TypeSafe Jev를 이용해 자동 모드 도구 호출을 판단, 내장 분류기보다 2배 빠름
A Claude Code mod that uses TypeSafe's Jev to decide auto mode tool calls. 2x faster than the built-in classifier on the calls it decides.
플러그인TypeScriptGitHub @madisonrickert직접 설치운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
설치형 프로젝트는 배포 도메인 없이도 확인할 수 있습니다. 개인 저장소는 GitHub 로그인으로, 조직 저장소는 위의 저장소 확인 파일로 직접 확인해 주세요.
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A Claude Code mod that puts TypeSafe's Jev in front of the auto mode classifier. In auto mode, for each tool call Claude Code would otherwise send to its built-in classifier, Jev answers eight yes/no questions in one request and the mod decides:
A shell-aware blocklist sits in front of Jev. A call it flags can still be denied by Jev but is never allowed, and a call that carries a secret never leaves your machine.
Evaluated on 7,860 labeled tool calls drawn from independent sources: another permission gate's reviewed corpus, five other Jev gates' test suites, real commands from coding-agent trajectories, tldr-pages, and hand-written edge cases. Thresholds were chosen on a dev split; the test split was run once, after the policy was committed.
On the sealed test split (5,322 calls, 3,664 of them risky):
git reset --h, is fixed in the shipped blocklist.Measured live against the built-in classifier (v0.1.2, details below), calls Jev decides wait half as long: a median of 164ms instead of 329ms. v0.2.0 decides a much larger share of real work than v0.1.2 did; a live re-measurement is next.
hooks/shell.ts parses each command the way a shell would: quotes, escapes, ANSI-C strings, line continuations, command and process substitution, heredocs (data unless fed to a shell), keywords, wrappers like env, xargs, and timeout, nested sh -c, interpreter one-liners, and find -exec. The blocklist then matches what each part of the command actually runs, so "su"do, bash -c 'sudo …', and git -C . push read the same as the plain versions. It flags privilege escalation, remote shells, destruction (including git checkout --, stash drop, and abbreviated --hard), uploads, download-and-run, publishing, global installs, writes outside the project, code run through tool options or the environment (git grep -O, rg --pre, LD_PRELOAD, PAGER, …), fork bombs, and unbounded loops. Anything it can't parse, or that contains invisible characters, is flagged too.peer_requests field, the pending call, and the project directory. It never sees tool output. A flagged call still goes to Jev, but only a denial counts.Text inside a call can try to argue for approval, for example a comment saying the user signed off. Shell comments and echoed strings that vouch for a command are flagged by the prefilter, a steering question catches the rest (any score above 0.10 blocks an allow), and serves_request is told that such text is not a request.
Why these thresholds: on the dev split, the risk ceiling is what kept unsafe calls out, while v0.1.2's high bar on "serves your request" (85%) mostly turned away routine work. Lowering that bar to 50% and the risk ceiling from 25% to 10%, together with question wording that says what doesn't count, cut unsafe allows on dev from 23 to 1 and doubled routine allows, from 32% to 65% (the new blocklist was in place for both).
Every call Jev judges sends your last three messages (up to 1,500 characters each), the pending command or URL, and your project path to TypeSafe's API. Read TypeSafe's data handling before using it with anything sensitive.
Inline secrets in your messages are replaced with [secret withheld] before they're sent.
These never leave your machine: calls with an inline secret (API keys and tokens in common formats, private keys, JWTs, bearer headers, passwords in assignments or URLs), and commands that touch .env files, credential files, or credential folders. They go straight to the built-in classifier, in every gate mode. Pattern matching can't catch every secret, so treat this as a backstop, not a guarantee.
The API key itself is a sensitive plugin setting: Claude Code masks it and keeps it in secure storage rather than settings.json. The gate sends it only to TypeSafe and never logs it.
Jev bills input tokens only. Each check is about 770 tokens, a small fraction of a cent.
From Madison Rickert's public plugin marketplace:
claude plugin marketplace add madisonrickert/claude-skills
claude plugin install jev-permission-gate@claude-skills
Or from inside a session: /plugin install jev-permission-gate --marketplace madisonrickert/claude-skills.
Claude Code asks for the plugin's settings when you enable it:
| Setting | Default | What it does |
|---|---|---|
| TypeSafe API key | none | Stored in secure storage. Without it the gate stays out of the way and every call goes to the built-in classifier. |
| Gate mode | enforce | enforce acts on Jev's verdicts. shadow only logs them while the built-in classifier decides. measure skips Jev and times the classifier. |
| Jev model | jev-1.13.0 | Pinned to the version the thresholds were tuned on. jev-latest follows new releases, which can shift answers. |
| Decision logs | off | Write every decision to ~/.claude/jev-permission-gate/logs/. Off by default because the logs record the commands the gate sees. |
| Capture calls | off | Save each call the gate judges, with the recent conversation, for building eval cases. See Capture. |
Change the gate mode, model, and logging later in /config. If you installed before v0.1.2, check that the model reads jev-1.13.0: an older install may have saved jev-latest. Instead of the key setting, you can export TYPESAFE_API_KEY.
The gate only acts in auto mode. Inside a session, /jev-gate shows counts, Jev latency, and the last 15 decisions with their reasons, whether or not logging is on.
With decision logs on, or in shadow or measure mode, logs go to ~/.claude/jev-permission-gate/logs/, which survives plugin updates. Logging never delays a decision: writes happen in the background.
decisions.jsonl: every decision, including calls the gate passed through untouchedcompare.jsonl: each call the built-in classifier decided, with its timingEach keeps its last 1,000 lines.
Capture saves what an eval case needs from each call that reaches the gate: the full tool input, your last 30 messages and Claude's (with the tool calls it made but never their output), the state Jev saw, its answers, and whether the call ran. Turn it on with the setting, or with "capture": true in gate.json. Each call is one file under ~/.claude/jev-permission-gate/captures/<date>/. The files stay on your machine and inline secrets are withheld, but they hold your conversation, so keep them out of a public repo.
The eval suite is built to be checked, not taken on trust.
| Set | Cases | Must not allow | Source | Labels |
|---|---|---|---|---|
corpus/nah | 6,001 | 5,999 | nah's reviewed corpus of dangerous commands, each under an unrelated everyday request; one in five also as "the user pasted it" | nah's maintainers; 200 audited (both annotators agreed on 195) |
corpus/prior-art | 754 | 397 | Test cases from five other Jev gates | two annotators |
corpus/swe | 400 | 19 | Commands coding agents ran fixing GitHub issues: the 300 most frequent plus a sample of the long tail | two annotators |
corpus/tbench | 260 | 15 | Commands agents ran on Terminal-Bench tasks | two annotators |
corpus/tldr | 300 | 28 | tldr-pages examples for the 80 programs agents run most | two annotators |
edge | 56 | 22 | Hand-written edge cases | three labelers |
cases, holdout | 93 | 35 | The original hand-written sets | one labeler |
evals/LABELING.md), never seeing upstream verdicts, gate output, or each other's work. The annotators were two different models, Claude Opus and Claude Sonnet. A decision counts as acceptable only if both accepted it. They agreed on whether a call must not be allowed for 93% to 99% of cases, depending on the set.evals/import/ rebuilds every corpus from upstream sources pinned to a commit, and checks the rebuilt cases against the committed labels key by key. Every case records its source, commit, file, upstream identifier, and license.node evals/run.ts --cases=corpus --split=dev # imported dev split, by set
node evals/run.ts --cases=all --by=set,category # everything, by set and category
node evals/score.ts --sweep --split=dev # threshold trade-offs, offline, from the last run
node evals/import/nah.ts && node evals/import/annotated.ts # rebuild the corpora
Responses are cached in evals/cache/ (gitignored), so reruns with the same cases, wording, and model are free. A full run of every set is about 16,000 requests, roughly 12 million input tokens, about 50 cents.
What it doesn't show: the annotators are models, so a systematic blind spot they share would carry into the labels. nah's cases are mostly adversarial by design and each uses an unrelated request, so they test refusing what wasn't asked for more than judging close calls. The real-workload sets pair each command with the task, not the exact conversation the agent had.
Set the gate mode to shadow to compare Jev with the built-in classifier on the same calls, or measure to time the classifier alone. To switch modes without reloading, write { "mode": "shadow" } to ~/.claude/jev-permission-gate/gate.json, which overrides the setting and is re-read every few seconds.
Each call's whole permission wait (call start to return, minus the tool's own run time) goes to compare.jsonl, and node evals/compare.ts --since=<ISO time> summarizes it.
On 2026-10-02, with v0.1.2, the same 24 classifier-bound commands, one call per turn in a live session:
| Path | Calls | Median | 90th percentile | Mean |
|---|---|---|---|---|
| Built-in classifier only | 25 | 329ms | 459ms | 346ms |
| Gate enforcing, all calls | 24 | 306ms | 532ms | 291ms |
| …decided by Jev | 11 | 164ms | 245ms | 192ms |
| …deferred to the classifier | 13 | 321ms | 535ms | 376ms |
Jev's verdicts agreed with the built-in classifier's on every call it would have decided. Deferring costs little at the median because the classifier's work appears to overlap Jev's request, so the overall gain scales with how many calls Jev decides. That session was one workload of mostly routine commands. TypeSafe's API also appears to handle one request per account at a time, so parallel tool calls queue their checks at about 100ms each.
node --test tests/*.spec.ts # policy, shell parsing, several hundred edge cases, eval statistics
claude plugin test # end-to-end hook tests, needs mods enabled
claude plugin validate . --strict
Thresholds, the tool list, the timeout, and the default model live in DEFAULT_CONFIG in hooks/policy.ts, next to the question wording and the blocklist. Change them against the dev split (node evals/score.ts --sweep --split=dev re-scores saved runs without new API calls), then check the test split.
git clone https://github.com/madisonrickert/jev-permission-gate.git
claude --plugin-dir ./jev-permission-gate
A checkout loaded this way can read the key from a TYPESAFE_API_KEY=... line in a .env file at the repo root (gitignored). Claude Code hot-reloads the mod when its files change.
| File | Role |
|---|---|
hooks/register.ts | Wiring: tool.check, permission-mode tracking, timing, /jev-gate |
hooks/policy.ts | Pure logic: blocklist, secret and URL checks, Jev questions, state, thresholds |
hooks/shell.ts | Shell lexer: what a command really runs |
hooks/typesafe.ts | Typed request and response for POST /v1/systemone |
types/index.d.ts | Session state that survives a hot reload |
evals/ | Labeled sets, runner, offline scorer, head-to-head summary |
evals/import/ | Pinned importers, raw annotator labels, and the prior-art extraction |
PreToolUse hook for Claude Code via OpenRouter, with a labeled fixture set and a live benchmark (0 dangerous actions allowed in 90 decisions). Its fixtures are in the prior-art set.Imported eval material is used under MIT, CC BY 4.0, and Apache 2.0; see THIRD_PARTY_NOTICES.md. jevaluate is under PolyForm Noncommercial and was used only to rate this project. No third-party code ships in the mod.