ohmyjev 0.0.0-stage → 0.2.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 +362 -2
- package/adapters/omp.ts +667 -0
- package/adapters/pi.ts +521 -0
- package/adapters/shared.ts +274 -0
- package/extras/statusline_segment.py +62 -0
- package/hooks/jev.ts +68 -0
- package/hooks/policy.ts +440 -0
- package/package.json +232 -4
package/README.md
CHANGED
|
@@ -1,3 +1,363 @@
|
|
|
1
|
-
|
|
1
|
+
```
|
|
2
|
+
_ _
|
|
3
|
+
___ | |__ _ __ ___ _ _ (_) ___ __ __
|
|
4
|
+
/ _ \ | '_ \ | '_ ` _ \ | | | | | | / _ \ \ \ / /
|
|
5
|
+
| (_) || | | || | | | | || |_| | | || __/ \ V /
|
|
6
|
+
\___/ |_| |_||_| |_| |_| \__, | _/ | \___| \_/
|
|
7
|
+
|___/ |__/
|
|
8
|
+
```
|
|
2
9
|
|
|
3
|
-
|
|
10
|
+
<p align="center">
|
|
11
|
+
Guardrails for Claude Code, pi and omp, decided by <a href="https://typesafe.ai">Jev</a>.<br>
|
|
12
|
+
One mod that blocks destructive commands, catches prompt injection, pushes back on an unverified "done" and routes effort per turn.
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img alt="Claude Code 2.1.287+" src="https://img.shields.io/badge/Claude_Code-2.1.287%2B-d97757">
|
|
17
|
+
<img alt="Decided by Jev" src="https://img.shields.io/badge/decided_by-Jev-101315">
|
|
18
|
+
<img alt="pi and omp" src="https://img.shields.io/badge/also_for-pi_·_omp-86a893">
|
|
19
|
+
<img alt="MIT license" src="https://img.shields.io/badge/license-MIT-798186">
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://ohmyjev.xyz"><b>ohmyjev.xyz</b></a> ·
|
|
24
|
+
<a href="#quick-start">Quick start</a> ·
|
|
25
|
+
<a href="#what-you-get">What you get</a> ·
|
|
26
|
+
<a href="#install">Install</a> ·
|
|
27
|
+
<a href="#pi-and-omp">pi and omp</a> ·
|
|
28
|
+
<a href="#using-it">Using it</a> ·
|
|
29
|
+
<a href="#settings">Settings</a> ·
|
|
30
|
+
<a href="#privacy">Privacy</a> ·
|
|
31
|
+
<a href="#troubleshooting">Troubleshooting</a>
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
Jev is TypeSafe's decision model. You send it some state and a few typed questions, and it sends back probabilities
|
|
37
|
+
in about 300 ms for a fraction of a cent. That's fast and cheap enough to ask about every command your agent runs, so
|
|
38
|
+
ohmyjev does exactly that. It runs as in-process hooks inside Claude Code, which means no extra process and no server
|
|
39
|
+
to keep up.
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
export TYPESAFE_API_KEY=ts_... # put this in your shell profile
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Then start Claude Code and run:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
/plugin install ohmyjev --marketplace Novacon/ohmyjev
|
|
51
|
+
/jev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If `/jev` ends with `key: env TYPESAFE_API_KEY · typesafe · jev-1.13.0`, you're set.
|
|
55
|
+
|
|
56
|
+
## What you get
|
|
57
|
+
|
|
58
|
+
| Feature | What it does |
|
|
59
|
+
|---|---|
|
|
60
|
+
| **Bash gate** | Denies a command when Jev rates it irreversible at 0.6 or more, or destructive at 0.7 or more. |
|
|
61
|
+
| **Write gate** | Denies writes outside the repo and `allowPaths`, and writes that contain a real credential. The path check is plain code, not Jev, and it follows symlinks the same way the OS does. |
|
|
62
|
+
| **Exfil gate** | Denies a WebFetch or MCP call when Jev rates it 0.7 or more for sending your local data, files or credentials out. |
|
|
63
|
+
| **Policies** | Every gate also checks the call against your own rules in the `policies` setting. |
|
|
64
|
+
| **Injection screen** | When output from Bash, WebFetch, an MCP tool, or a Read outside the repo has instructions aimed at the model, it adds a note telling the model to treat that output as data. |
|
|
65
|
+
| **Done-check** | If the agent says it's done but nothing shows it ran a check, it blocks the stop once and tells the agent to verify. |
|
|
66
|
+
| **Router** | Asks Jev once per request for a tier, an effort level and a risk score. Then it raises or lowers effort and picks the model for general-purpose subagents. With no key it falls back to Claude Code's built-in classifier, which reports no confidence, so then it only ever routes up. |
|
|
67
|
+
| **Auto-compact** | When the task changes after a finished step and the context is at least 40% full, it compacts the conversation. The summary keeps the new request in full. |
|
|
68
|
+
| **`ask_jev`** | A tool the model can use to ask Jev about repo files or text without loading them into its own context. |
|
|
69
|
+
| **`/jev`** | Shows this session's Jev calls, errors, cost, median latency and denies, plus where the key comes from. It never prints the key itself. |
|
|
70
|
+
|
|
71
|
+
### How a tool call goes through it
|
|
72
|
+
|
|
73
|
+
```mermaid
|
|
74
|
+
flowchart LR
|
|
75
|
+
A[Claude calls a tool] --> B{ohmyjev asks Jev}
|
|
76
|
+
B -- clearly risky --> C[Denied, with the reason]
|
|
77
|
+
B -- fine or unsure --> D[Claude Code's normal permission flow]
|
|
78
|
+
B -- Jev down, slow or no key --> D
|
|
79
|
+
D --> E[Tool runs]
|
|
80
|
+
E --> F{Output has instructions<br>aimed at the model?}
|
|
81
|
+
F -- yes --> G[Model gets a<br>'treat as data' note]
|
|
82
|
+
F -- no --> H[Output as is]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Anything ohmyjev doesn't deny just goes on to Claude Code's normal permission flow. That covers middling answers, Jev
|
|
86
|
+
being down or slower than `timeoutMs` (1.5 s), a missing key, and any error inside ohmyjev. It never stops to ask you to approve
|
|
87
|
+
anything, so it works fine with agents running in bypass mode. Jev can be wrong though, so keep your deny rules in
|
|
88
|
+
`settings.json`.
|
|
89
|
+
|
|
90
|
+
## Install
|
|
91
|
+
|
|
92
|
+
### 1. Check what you need
|
|
93
|
+
|
|
94
|
+
- **Claude Code 2.1.287 or later.** Mods are still early access. Run `claude --version` to check.
|
|
95
|
+
- **A Jev key.** Either a TypeSafe key, or an OpenRouter key if you'd rather go through OpenRouter.
|
|
96
|
+
|
|
97
|
+
### 2. Install the plugin
|
|
98
|
+
|
|
99
|
+
Run this at the prompt of a terminal Claude Code session:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
/plugin install ohmyjev --marketplace Novacon/ohmyjev
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Answer `y` to add the marketplace, then pick a scope. User scope is the first option and turns ohmyjev on for every
|
|
106
|
+
session. Once it says `Installed ohmyjev`, the hooks are already running in that session, so there's nothing to
|
|
107
|
+
restart.
|
|
108
|
+
|
|
109
|
+
You can also do it from your shell:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
claude plugin marketplace add Novacon/ohmyjev
|
|
113
|
+
claude plugin install ohmyjev@ohmyjev
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 3. Add your key
|
|
117
|
+
|
|
118
|
+
You've got three options, in the order ohmyjev looks for them:
|
|
119
|
+
|
|
120
|
+
1. **The settings screen.** During the install, ohmyjev shows a screen with its options, including the TypeSafe
|
|
121
|
+
key. Claude Code keeps it in secure storage, not in a settings file.
|
|
122
|
+
2. **`TYPESAFE_API_KEY`** in your environment, for example in `~/.zshrc`.
|
|
123
|
+
3. **`OPENROUTER_API_KEY`** in your environment. ohmyjev then calls Jev through OpenRouter as `~typesafe/jev-latest`.
|
|
124
|
+
|
|
125
|
+
### 4. Check it works
|
|
126
|
+
|
|
127
|
+
Start a session and run `/jev`. The last line tells you where the key came from:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
key: env TYPESAFE_API_KEY · typesafe · jev-1.13.0
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If it says `key: none`, ohmyjev can't see a key. In that case every gate stays open, the router falls back to Claude
|
|
134
|
+
Code's built-in classifier (up only), and the status under the prompt shows `jev ⚠ no key`.
|
|
135
|
+
|
|
136
|
+
## pi and omp
|
|
137
|
+
|
|
138
|
+
The same checks run in [pi](https://pi.dev) and [omp](https://github.com/can1357/oh-my-pi) as a native extension.
|
|
139
|
+
They share the decision logic and the key lookup (`TYPESAFE_API_KEY`, then `OPENROUTER_API_KEY`) with the Claude Code
|
|
140
|
+
plugin, and write to the same `~/.ohmyjev` logs, so `/jev` and the statusline segment work the same way.
|
|
141
|
+
|
|
142
|
+
### omp
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
omp plugin install github:Novacon/ohmyjev
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Or from the marketplace this repo already serves: `/marketplace add Novacon/ohmyjev`, then
|
|
149
|
+
`/marketplace install ohmyjev@ohmyjev`. Restart the session after installing. Settings use the names in
|
|
150
|
+
[Settings](#settings):
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
omp plugin config list ohmyjev
|
|
154
|
+
omp plugin config set ohmyjev routeMainModel true
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The router's tiers default to omp's own model roles: `@smol`, `@default` and `@slow`.
|
|
158
|
+
|
|
159
|
+
### pi
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
pi install git:github.com/Novacon/ohmyjev
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
pi has no settings screen for extensions, so put yours under an `ohmyjev` key in `~/.pi/agent/settings.json`, or in
|
|
166
|
+
`.pi/settings.json` for one project (project values win):
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{ "ohmyjev": { "routeMainModel": true, "fastModel": "anthropic/claude-haiku-5-5" } }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### What differs
|
|
173
|
+
|
|
174
|
+
| | Claude Code | omp | pi |
|
|
175
|
+
|---|---|---|---|
|
|
176
|
+
| Bash gate | Bash | `bash`, `eval`, and `github` pushes and PRs | `bash`, `powershell` |
|
|
177
|
+
| Write gate | Write, Edit, NotebookEdit | `write`, `edit` (patches and renames included), `ast_edit` | `write`, `edit` |
|
|
178
|
+
| Exfil gate | WebFetch, MCP tools | `read` of a URL, MCP tools | MCP tools (pi has no fetch tool; `curl` goes through the bash gate) |
|
|
179
|
+
| Injection screen | Bash, WebFetch, MCP, Reads outside the repo | `bash`, `github`, URL reads, MCP, reads outside the repo | `bash`, `powershell`, MCP, reads outside the repo |
|
|
180
|
+
| Done-check | ✓ | ✓ | ✓ |
|
|
181
|
+
| Router effort | ✓ | ✓ (left alone while you're on `auto`) | ✓ |
|
|
182
|
+
| Router subagent model | general-purpose subagents | subagents on the default `task` role | none: pi has no subagents |
|
|
183
|
+
| Router without a key | Claude Code's built-in classifier, up only | off: omp has no separate classifier call for extensions | the cheapest model you've set up, up only |
|
|
184
|
+
| Decision lines | in the transcript | notifications | notifications |
|
|
185
|
+
| Status | under the prompt | in the footer | in the footer |
|
|
186
|
+
|
|
187
|
+
## Using it
|
|
188
|
+
|
|
189
|
+
Most of the time you won't notice it. Here's what it looks like when it does step in.
|
|
190
|
+
|
|
191
|
+
### When a command gets blocked
|
|
192
|
+
|
|
193
|
+
Claude gets the denial as the tool's result, along with an instruction not to work around it:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
ohmyjev blocked this: irreversible (0.95): nothing would restore what this removes or overwrites.
|
|
197
|
+
This block is final. Do not try to work around it with another command, another tool, a different path,
|
|
198
|
+
or an encoding that does the same thing. Stop and tell the user what was blocked and why.
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### `/jev`
|
|
202
|
+
|
|
203
|
+
Run it any time to see what this session has been up to. Here's an example:
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
jev ✓23 ⛔1 ↑opus/high
|
|
207
|
+
calls 23 · errors 0 · cost $0.000966 · p50 310ms
|
|
208
|
+
denies: Bash 1
|
|
209
|
+
Bash: irreversible (0.95): nothing would restore what this removes or overwrites
|
|
210
|
+
key: env TYPESAFE_API_KEY · typesafe · jev-1.13.0
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Your own rules
|
|
214
|
+
|
|
215
|
+
Put plain-English rules in the `policies` setting, separated by `;`. Every gate then asks Jev whether the call breaks
|
|
216
|
+
one of them:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
never touch the prod cluster; no deploys on Friday; don't edit migrations that already ran
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### `ask_jev`
|
|
223
|
+
|
|
224
|
+
The model can call this tool on its own when it wants a quick judgment without reading a pile of files. For example:
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"question": "Which of these files handles session login?",
|
|
229
|
+
"type": "choice",
|
|
230
|
+
"options": ["src/auth.ts", "src/session.ts", "src/routes.ts"],
|
|
231
|
+
"files": ["src/auth.ts", "src/session.ts", "src/routes.ts"]
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Jev reads the files, not the model. ohmyjev only sends files that are inside the repo, up to 8000 characters each and
|
|
236
|
+
80000 in total.
|
|
237
|
+
|
|
238
|
+
### In the transcript
|
|
239
|
+
|
|
240
|
+
Each routing decision shows up as a dim line in the transcript. The model never sees these lines. First what Jev
|
|
241
|
+
said, then what the router did with it:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
[ohmyjev] ready: Jev via typesafe (jev-1.13.0, key from env TYPESAFE_API_KEY)
|
|
245
|
+
[ohmyjev] jev: tier fast (0.41) · effort 0.4 (0.38) · risky 0.01 · 210ms
|
|
246
|
+
[ohmyjev] main loop kept opus/medium, wanted opus/low (confidence 0.38)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The last line is the router declining to act: it wanted to spend less, but 0.38 is under the 0.6 it takes to move
|
|
250
|
+
down. In a `claude -p` or SDK run the same lines arrive as `ui_log` messages and in the debug log. Turn them off with
|
|
251
|
+
`logDecisions`.
|
|
252
|
+
|
|
253
|
+
### Status line
|
|
254
|
+
|
|
255
|
+
ohmyjev pins its status under the prompt, so there's nothing to set up:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
jev ✓23 ⛔1 ↑opus/high 🗜2 Jev calls, denies, this turn's route, auto-compactions
|
|
259
|
+
jev ⚠ down Jev failed or timed out in the last 5 minutes, so the gates let calls through
|
|
260
|
+
jev ⚠ no key no key configured
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
If you'd rather have it in your own statusline script, copy `jev_segment()` from
|
|
264
|
+
[`extras/statusline_segment.py`](extras/statusline_segment.py) into it and add the segment wherever you like. It
|
|
265
|
+
reads one small file per session, so it's quick. Turn the built-in one off with `statusLine`.
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
jev = jev_segment(data.get("session_id")) # data = the statusline JSON from stdin
|
|
269
|
+
if jev:
|
|
270
|
+
parts.append(jev)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Settings
|
|
274
|
+
|
|
275
|
+
Run `/jev settings` to see every setting's current value (the key stays hidden). To change them, run
|
|
276
|
+
`/plugin configure ohmyjev@ohmyjev` inside Claude Code, then `/reload-plugins`.
|
|
277
|
+
|
|
278
|
+
| Setting | Default | What it changes |
|
|
279
|
+
|---|---|---|
|
|
280
|
+
| `bashGate`, `writeGate`, `exfilGate` | on | Turns each gate on or off. |
|
|
281
|
+
| `injectionScreen`, `screenReads` | on | Screens tool output, including Reads from outside the repo. |
|
|
282
|
+
| `doneCheck` | on | Pushes back on an unverified "done". |
|
|
283
|
+
| `routeEffort`, `routeSubagents` | on | Lets the router change effort and the subagent model. |
|
|
284
|
+
| `routeMainModel` | off | Also switches the main model. It's off because switching models throws away the prompt cache. |
|
|
285
|
+
| `routeWithoutKey` | on | With no key, routes with Claude Code's built-in classifier instead. It has no confidence, so it only routes up. |
|
|
286
|
+
| `autoCompact` | on | Compacts when the task changes. `compactMinPercent` (40) sets how full the context has to be first. |
|
|
287
|
+
| `askJev` | on | Gives the model the `ask_jev` tool. |
|
|
288
|
+
| `policies` | empty | Your own rules, separated by `;`. |
|
|
289
|
+
| `allowPaths` | `~/.claude;$TMPDIR;/tmp` | Places outside the repo where writes are allowed. ohmyjev ignores any entry with `..` in it. |
|
|
290
|
+
| `fastModel`, `balancedModel`, `deepModel` | `claude-haiku-5-5`, `claude-sonnet-5-5`, `claude-opus-5-5` | The model id for each router tier. |
|
|
291
|
+
| `jevModel` | `jev-1.13.0` | The Jev model asked through TypeSafe. |
|
|
292
|
+
| `timeoutMs` | 1500 | How long a gate, the screen or the router waits for Jev before letting the call through. |
|
|
293
|
+
| `logDecisions` | on | Shows each routing decision as a line in the transcript. |
|
|
294
|
+
| `statusLine` | on | Pins ohmyjev's status under the prompt. |
|
|
295
|
+
|
|
296
|
+
Each gate also has its own threshold setting. The defaults are the numbers in [What you get](#what-you-get).
|
|
297
|
+
|
|
298
|
+
## Logs
|
|
299
|
+
|
|
300
|
+
Every decision gets one line in `~/.ohmyjev/log/<session>.jsonl`, and only you can read it. The log holds the
|
|
301
|
+
verdict, Jev's probabilities, the latency and the cost, never your commands' output. ohmyjev doesn't wait on that
|
|
302
|
+
write, so if one fails you lose that line and nothing else.
|
|
303
|
+
|
|
304
|
+
## Privacy
|
|
305
|
+
|
|
306
|
+
With a key set, ohmyjev sends Jev (TypeSafe, or OpenRouter with an OpenRouter key) only what each decision needs, cut to
|
|
307
|
+
a fixed size:
|
|
308
|
+
|
|
309
|
+
| What asks | What it sends |
|
|
310
|
+
|---|---|
|
|
311
|
+
| Bash gate | The command (up to 16000 characters), the working directory and the tool call's description |
|
|
312
|
+
| Write gate | The file path and the new content (up to 16000 characters). Writes outside the repo and `allowPaths` are denied by code, before anything is sent |
|
|
313
|
+
| Exfil gate | The tool's name and its input (up to 4000 characters) |
|
|
314
|
+
| Injection screen | The tool's output (up to 6000 characters), from Bash, WebFetch, MCP tools and Reads outside the repo |
|
|
315
|
+
| Done-check | The current request (600), up to five earlier requests (200 each), up to 20 of this turn's tool calls with their input (200 each) and outcome, and Claude's last message (1500) |
|
|
316
|
+
| Router | The request (up to 1500 characters) |
|
|
317
|
+
| `ask_jev` | The model's question, any text it passes (up to 20000 characters) and the repo files it names (8000 each, 80000 in total). Files outside the repo are never sent |
|
|
318
|
+
|
|
319
|
+
When you've set `policies`, every gate sends those too. Turning a feature off stops its calls.
|
|
320
|
+
|
|
321
|
+
With no key, nothing goes to Jev. The router's built-in classifier sends the request (up to 1500 characters) to
|
|
322
|
+
Claude's own small model, over the same connection Claude Code already uses. In pi it goes to the cheapest model you
|
|
323
|
+
have set up, through pi's own provider. Logs and session files stay in `~/.ohmyjev`, readable only by you.
|
|
324
|
+
|
|
325
|
+
## Update or remove
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
claude plugin update ohmyjev@ohmyjev # then restart Claude Code
|
|
329
|
+
claude plugin uninstall ohmyjev@ohmyjev
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Troubleshooting
|
|
333
|
+
|
|
334
|
+
**The status says `jev ⚠ no key`.** ohmyjev can't find a key. Set `TYPESAFE_API_KEY` in the shell that starts
|
|
335
|
+
Claude Code, or enter the key with `/plugin configure ohmyjev@ohmyjev`.
|
|
336
|
+
|
|
337
|
+
**The status says `jev ⚠ down`.** A Jev call failed or took longer than `timeoutMs` (1.5 s) in the last 5 minutes. The
|
|
338
|
+
gates let calls through until Jev answers again. `/jev` shows the error count.
|
|
339
|
+
|
|
340
|
+
**Something got blocked that shouldn't have.** `/jev` shows the reason and Jev's numbers. Raise that gate's threshold
|
|
341
|
+
with `/plugin configure ohmyjev@ohmyjev`, or turn the gate off. Local MCP tools sometimes trip the exfil gate, and `exfilGate` is the switch for
|
|
342
|
+
that.
|
|
343
|
+
|
|
344
|
+
**Nothing seems to happen.** The first transcript line of a session should be `[ohmyjev] ready: …` or `[ohmyjev] no
|
|
345
|
+
Jev key: …`. If neither shows, run `claude --debug`: if a hook fails, the debug log has a line naming it and the
|
|
346
|
+
reason.
|
|
347
|
+
|
|
348
|
+
## Develop
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
git clone https://github.com/Novacon/ohmyjev && cd ohmyjev && bun install
|
|
352
|
+
claude --plugin-dir "$PWD" # run your working copy, and it writes the API types to .claude-plugin/types
|
|
353
|
+
omp -e ./adapters/omp.ts # or: pi -e ./adapters/pi.ts
|
|
354
|
+
bun run test && bun run typecheck && claude plugin validate .
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The decision logic lives in `hooks/policy.ts` and is pure, so you can test it with plain tables. `hooks/ohmyjev.ts`
|
|
358
|
+
wires it to Claude Code; `adapters/omp.ts` and `adapters/pi.ts` wire it to omp and pi through `adapters/shared.ts`.
|
|
359
|
+
The design and plans are in [`docs/superpowers`](docs/superpowers).
|
|
360
|
+
|
|
361
|
+
## Credits
|
|
362
|
+
|
|
363
|
+
The question rubrics come from [disler/ten-levels-of-jev](https://github.com/disler/ten-levels-of-jev). MIT license.
|