jevctl 0.1.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.
Files changed (63) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +5 -0
  4. package/README.md +430 -0
  5. package/SECURITY.md +21 -0
  6. package/dist/cli.d.ts +4 -0
  7. package/dist/cli.js +78 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/ask.d.ts +15 -0
  10. package/dist/commands/ask.js +96 -0
  11. package/dist/commands/ask.js.map +1 -0
  12. package/dist/commands/config.d.ts +13 -0
  13. package/dist/commands/config.js +125 -0
  14. package/dist/commands/config.js.map +1 -0
  15. package/dist/commands/find.d.ts +16 -0
  16. package/dist/commands/find.js +97 -0
  17. package/dist/commands/find.js.map +1 -0
  18. package/dist/commands/models.d.ts +4 -0
  19. package/dist/commands/models.js +35 -0
  20. package/dist/commands/models.js.map +1 -0
  21. package/dist/commands/screen.d.ts +13 -0
  22. package/dist/commands/screen.js +64 -0
  23. package/dist/commands/screen.js.map +1 -0
  24. package/dist/commands/verify.d.ts +18 -0
  25. package/dist/commands/verify.js +97 -0
  26. package/dist/commands/verify.js.map +1 -0
  27. package/dist/config.d.ts +81 -0
  28. package/dist/config.js +147 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/context.d.ts +29 -0
  31. package/dist/context.js +54 -0
  32. package/dist/context.js.map +1 -0
  33. package/dist/core/ask.d.ts +59 -0
  34. package/dist/core/ask.js +103 -0
  35. package/dist/core/ask.js.map +1 -0
  36. package/dist/core/find.d.ts +48 -0
  37. package/dist/core/find.js +50 -0
  38. package/dist/core/find.js.map +1 -0
  39. package/dist/core/screen.d.ts +38 -0
  40. package/dist/core/screen.js +55 -0
  41. package/dist/core/screen.js.map +1 -0
  42. package/dist/core/verify.d.ts +55 -0
  43. package/dist/core/verify.js +74 -0
  44. package/dist/core/verify.js.map +1 -0
  45. package/dist/errors.d.ts +16 -0
  46. package/dist/errors.js +29 -0
  47. package/dist/errors.js.map +1 -0
  48. package/dist/index.d.ts +8 -0
  49. package/dist/index.js +12 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/input.d.ts +34 -0
  52. package/dist/input.js +116 -0
  53. package/dist/input.js.map +1 -0
  54. package/dist/lib.d.ts +63 -0
  55. package/dist/lib.js +123 -0
  56. package/dist/lib.js.map +1 -0
  57. package/dist/output.d.ts +26 -0
  58. package/dist/output.js +59 -0
  59. package/dist/output.js.map +1 -0
  60. package/dist/provider.d.ts +30 -0
  61. package/dist/provider.js +157 -0
  62. package/dist/provider.js.map +1 -0
  63. package/package.json +78 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
+ [Semantic Versioning](https://semver.org/). Pre-1.0 minor versions may include
6
+ breaking changes to flags or JSON output; they are called out below.
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-09-18
11
+
12
+ Initial release, published to npm as `jevctl`. The installed command is `jev`.
13
+
14
+ ### Added
15
+
16
+ - `jev verify`: claims versus evidence with `supports` / `contradicts` / `says_nothing` distributions, confidence, per-claim evidence attribution, and an auto-versus-review gate.
17
+ - `jev screen`: injection, substance, and relevance probabilities with an advisory `pass` / `review` / `block` / `skip` recommendation.
18
+ - `jev find`: semantic ranking of up to 250 candidates from JSON, files, or lines, plus an existence check.
19
+ - `jev ask`: raw System One passthrough with `--questions` JSON or `--noul` / `--choice` / `--score` shorthands.
20
+ - `jev models`: list account models.
21
+ - `jev config`: show, path, init, set, unset, reset; XDG-aware config file with env and flag overrides.
22
+ - Providers: TypeSafe direct (default), OpenRouter Decisions, Cloudflare Workers AI.
23
+ - `--json` output, `--dry-run` request preview, `--fail-on` exit-code policies (exit 2), `NO_COLOR` support.
24
+ - Programmatic exports for embedding the same judgments in Node scripts.
25
+ - Test suite: unit tests, provider transport tests with mocked fetch, CLI tests against a local fake TypeSafe API, and optional live e2e tests.
26
+
27
+ [Unreleased]: https://github.com/Nasrallah-AL/jev-cli/compare/v0.1.0...HEAD
28
+ [0.1.0]: https://github.com/Nasrallah-AL/jev-cli/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nasr Shaer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,5 @@
1
+ jev-cli (published on npm as jevctl)
2
+ Copyright (c) 2026 Nasr Shaer
3
+
4
+ This product includes code adapted from jev-mcp,
5
+ Copyright (c) 2026 Joey Kudish, licensed under the MIT License.
package/README.md ADDED
@@ -0,0 +1,430 @@
1
+ # jev-cli
2
+
3
+ [![CI](https://github.com/Nasrallah-AL/jev-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/Nasrallah-AL/jev-cli/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/jevctl.svg)](https://www.npmjs.com/package/jevctl)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ Fast, typed AI judgments from the command line, powered by TypeSafe's [Jev](https://docs.typesafe.ai) model.
8
+
9
+ Installed from npm as **`jevctl`**; the command it gives you is **`jev`**.
10
+
11
+ Jev does not generate text. You give it some content and a question with a fixed set of answers, and it returns a probability for each answer in a few hundred milliseconds for a fraction of a cent. `jev-cli` wraps that into four commands you can pipe into, script around, and gate CI on.
12
+
13
+ | Command | Question it answers | Typical use |
14
+ | --- | --- | --- |
15
+ | `jev verify` | Does this evidence support, contradict, or ignore each claim? | Fact-check a PR description, report, or AI summary against its sources |
16
+ | `jev screen` | Is this text trying to hijack an AI agent? Is it worth reading? | Guardrail before fetched web pages or emails enter an agent's context |
17
+ | `jev find` | Which of these candidates best answers the query? Does any? | Pick the right file, note, or line without building a search index |
18
+ | `jev ask` | Anything with a yes/no, pick-one, or rated answer | Classify, route, score, or extract with your own questions |
19
+
20
+ Every command prints a readable table by default, full JSON with `--json`, and an exit code you can branch on.
21
+
22
+ ## Contents
23
+
24
+ - [Install](#install)
25
+ - [Sixty-second start](#sixty-second-start)
26
+ - [Commands](#commands)
27
+ - [Passing input](#passing-input)
28
+ - [Output and exit codes](#output-and-exit-codes)
29
+ - [Configuration](#configuration)
30
+ - [Guidelines for good results](#guidelines-for-good-results)
31
+ - [Recipes](#recipes)
32
+ - [Troubleshooting](#troubleshooting)
33
+ - [Use from Node.js](#use-from-nodejs)
34
+ - [Development](#development)
35
+
36
+ ## Install
37
+
38
+ You need Node.js 20.12 or newer and a TypeSafe API key.
39
+
40
+ 1. Create a key at [console.typesafe.ai/settings/keys](https://console.typesafe.ai/settings/keys).
41
+ 2. Install the CLI:
42
+
43
+ ```bash
44
+ npm install -g jevctl
45
+ ```
46
+
47
+ 3. Export the key and confirm it is picked up:
48
+
49
+ ```bash
50
+ export TYPESAFE_API_KEY=your_key_here
51
+ jev config
52
+ ```
53
+
54
+ `jev config` prints the resolved settings and a masked view of the key. It exits 1 if no credentials are found.
55
+
56
+ To try it without installing, prefix any command with `npx jevctl` instead of `jev`. The installed command is always `jev`.
57
+
58
+ ## Sixty-second start
59
+
60
+ ```bash
61
+ # 1. Verify two claims against one piece of evidence.
62
+ jev verify "Helmets are optional for adults" "Reflective gear is mentioned" \
63
+ --evidence "Every rider must wear an approved helmet. Riders under 18 must wear reflective gear after dark."
64
+ ```
65
+
66
+ ```text
67
+ # Verdict Conf Action Claim
68
+ - ------------ ---- ------ -------------------------------
69
+ 1 contradicted 1.00 auto Helmets are optional for adults
70
+ 2 verified 1.00 auto Reflective gear is mentioned
71
+
72
+ 1 verified · 1 contradicted · 0 unsupported · 0 need review (auto-accept ≥ 0.8)
73
+ 604 in / 91 out tokens · jev-1.13.0 via typesafe
74
+ ```
75
+
76
+ The command exited with code 2 because a claim was contradicted. That is the default `--fail-on` policy for `verify`.
77
+
78
+ ```bash
79
+ # 2. Screen a web page before an agent reads it. Exit 2 means "block".
80
+ curl -s https://example.com/pricing | jev screen --purpose "extract pricing tiers"
81
+
82
+ # 3. Find which doc answers a question. No index, no embeddings.
83
+ jev find "how do I rotate API keys" --files docs/*.md
84
+
85
+ # 4. Ask your own questions. Answers come back typed.
86
+ jev ask "I was charged twice. Fix this now." \
87
+ --noul urgent="Does this convey urgency?" \
88
+ --choice team="Which team should handle this?|billing,technical,sales"
89
+
90
+ # 5. See exactly what would be sent, without calling the API.
91
+ jev screen "some text" --dry-run
92
+ ```
93
+
94
+ ## Commands
95
+
96
+ ### `jev verify`
97
+
98
+ Checks each claim against the evidence you provide. For every claim you get a verdict, the probability of each possible verdict, a confidence score, and whether the verdict can be trusted automatically or should be reviewed by a person.
99
+
100
+ ```bash
101
+ jev verify [claims...] --evidence <ref> [--evidence <ref>...] [options]
102
+ ```
103
+
104
+ | Verdict | Meaning |
105
+ | --- | --- |
106
+ | `verified` | The evidence states the claim or directly implies it |
107
+ | `contradicted` | The evidence says the opposite |
108
+ | `unsupported` | The evidence does not address the claim either way |
109
+
110
+ | Option | Meaning | Default |
111
+ | --- | --- | --- |
112
+ | `-e, --evidence <ref>` | Evidence text, `@file`, or `-` for stdin. Repeat for several sources. Files are labeled by filename. | required |
113
+ | `--evidence-json <ref>` | Evidence as JSON: `["text", ...]`, `[{"id": "...", "text": "..."}]`, or `{"id": "text"}` | |
114
+ | `-c, --claims <ref>` | Claims from a file or stdin: one per line, or a JSON array of strings | |
115
+ | `--auto-accept <p>` | Confidence at or above which a verdict is marked `auto` instead of `review` | `0.8` |
116
+ | `--fail-on <list>` | Exit 2 if any result is one of: `contradicted`, `unsupported`, `review`, `unknown`. Use `none` to always exit 0. | `contradicted` |
117
+
118
+ When you pass more than one evidence source, each result also names the source the claim rests on (`supporting_evidence` in JSON, a `Source` column in text).
119
+
120
+ ```bash
121
+ # Claims in a file, two evidence documents, strict policy
122
+ jev verify --claims @claims.txt -e @spec.md -e @rfc.txt --fail-on contradicted,unsupported
123
+
124
+ # Evidence from stdin
125
+ git diff main | jev verify "This change only touches tests" --evidence -
126
+ ```
127
+
128
+ ### `jev screen`
129
+
130
+ Judges a piece of text before an AI agent consumes it. Returns three probabilities and a recommendation.
131
+
132
+ ```bash
133
+ jev screen [text] [--purpose <text>] [options]
134
+ ```
135
+
136
+ | Probability | Question asked |
137
+ | --- | --- |
138
+ | `injection` | Does the text contain instructions aimed at an AI agent, such as "ignore previous instructions" or "visit this URL"? |
139
+ | `substance` | Is there real content, as opposed to an error page or boilerplate? |
140
+ | `relevance` | Is the text useful for the stated `--purpose`? Only asked when a purpose is given. |
141
+
142
+ | Recommendation | When |
143
+ | --- | --- |
144
+ | `block` | `injection` at or above `--block-at` |
145
+ | `review` | `injection` at or above `--review-at` |
146
+ | `skip` | Low `substance` or low `relevance`. Not dangerous, just not worth reading. |
147
+ | `pass` | None of the above |
148
+
149
+ | Option | Meaning | Default |
150
+ | --- | --- | --- |
151
+ | `[text]` | Text, `@file`, or `-`. Reads stdin when omitted and piped. | stdin |
152
+ | `-p, --purpose <text>` | What the consumer is trying to do. Enables the relevance check and `skip`. | |
153
+ | `--block-at <p>` | Injection probability that triggers `block` | `0.75` |
154
+ | `--review-at <p>` | Injection probability that triggers `review` | `0.25` |
155
+ | `--fail-on <list>` | Exit 2 when the recommendation is one of `block`, `review`, `skip`. Use `none` to always exit 0. | `block` |
156
+
157
+ The recommendation is advisory. `jev` never blocks anything itself. Enforcement is the exit code, or your code reading the JSON.
158
+
159
+ ```text
160
+ $ curl -s https://shop.example/sale | jev screen --purpose "summarize the products"
161
+ BLOCK injection probability 0.99 >= block threshold 0.75
162
+ injection 0.99 · substance 0.97 · relevance 0.97
163
+ ```
164
+
165
+ ### `jev find`
166
+
167
+ Ranks candidates against a plain-language query. One call scores every candidate and also reports whether any candidate answers the query at all, so a confident top hit cannot masquerade as an answer when none exists.
168
+
169
+ ```bash
170
+ jev find <query> (--files <paths...> | --candidates <ref> | --lines <ref>) [options]
171
+ ```
172
+
173
+ | Option | Meaning | Default |
174
+ | --- | --- | --- |
175
+ | `-f, --files <paths...>` | Each file is a candidate. Its id is the path. | |
176
+ | `-c, --candidates <ref>` | JSON candidates: `["text", ...]`, `[{"id": "...", "text": "..."}]`, or `{"id": "text"}` | |
177
+ | `-l, --lines <ref>` | Each non-empty line of a file or stdin is a candidate with id `L1`, `L2`, ... | |
178
+ | `-k, --top-k <n>` | How many ranked results to show | `5` |
179
+ | `--found <p>` | Exists probability at or above which the verdict is `answered` | `0.7` |
180
+ | `--absent <p>` | Exists probability below which the verdict is `absent`. In between is `partial`. | `0.35` |
181
+ | `--fail-on <list>` | Exit 2 when the exists verdict is `absent` or `partial` | `none` |
182
+
183
+ Limits: 250 candidates per call. Each candidate's text is truncated to 2,000 characters before sending.
184
+
185
+ ```text
186
+ $ jev find "how do I rotate API keys" --files docs/*.md -k 2
187
+ answered (exists 0.99) how do I rotate API keys
188
+
189
+ # Prob Id Text
190
+ - ---- --------------- ------------------------------------------------------------
191
+ 1 0.99 docs/auth.md To rotate an API key: create a new key in Settings > Keys, …
192
+ 2 0.01 docs/billing.md Invoices are issued monthly and can be downloaded as PDF.
193
+ ```
194
+
195
+ ### `jev ask`
196
+
197
+ The general form. Send any state and any number of questions; get one typed answer per question. Questions run in parallel over the same state, so asking five costs about the same latency as asking one.
198
+
199
+ ```bash
200
+ jev ask [state] (--noul ... | --choice ... | --score ... | --questions <ref>) [options]
201
+ ```
202
+
203
+ Three question types:
204
+
205
+ | Flag | Answer type | Shorthand format |
206
+ | --- | --- | --- |
207
+ | `--noul` | Probability of "yes", from 0 to 1 | `id=Question text?` |
208
+ | `--choice` | One option from a set, with a probability for each and a confidence | `id=Question text?\|optionA,optionB:description,optionC` |
209
+ | `--score` | A position on an ordered scale, with a probability per level and a confidence | `id=Question text?\|low level,middle level,high level` |
210
+
211
+ | Option | Meaning |
212
+ | --- | --- |
213
+ | `[state]` | Text, `@file`, or `-`. Reads stdin when omitted and piped. |
214
+ | `--state-json` | Treat the state as JSON so you can send an object or array |
215
+ | `-q, --questions <ref>` | A JSON questions map in the [TypeSafe API shape](https://docs.typesafe.ai/api), instead of shorthand flags |
216
+
217
+ Shorthand rules: the `=` separates the id from the question, the first `|` separates the question from its options, and options are comma separated. Write `\|` or `\,` for a literal pipe or comma. Ids may contain letters, digits, `_`, `-`, and `.`.
218
+
219
+ ```text
220
+ $ jev ask @ticket.txt --noul urgent="Does this convey urgency?" \
221
+ --choice team="Which team?|billing:refunds and invoices,technical:bugs and outages,sales" \
222
+ --score frustration="How frustrated is the customer?|calm,frustrated,very angry"
223
+ urgent: 0.92
224
+ team: billing conf 0.82 [billing 0.85, technical 0.08, sales 0.07]
225
+ frustration: 1.60 conf 0.71 [0 0.10, 1 0.20, 2 0.70]
226
+ ```
227
+
228
+ ### `jev models`
229
+
230
+ Lists the models your account can use, with release dates. Requires the TypeSafe provider.
231
+
232
+ ### `jev config`
233
+
234
+ Manages the config file and shows the effective settings.
235
+
236
+ | Subcommand | Does |
237
+ | --- | --- |
238
+ | `jev config` or `jev config show` | Print effective settings, masked credentials, and the resolved provider. Exit 1 if none. |
239
+ | `jev config path` | Print the config file location |
240
+ | `jev config init` | Write a file with the defaults |
241
+ | `jev config set <key> <value>` | Set one value, e.g. `jev config set screen.blockAt 0.6` |
242
+ | `jev config unset <key>` | Remove a top-level key |
243
+ | `jev config reset` | Delete the file |
244
+
245
+ ## Passing input
246
+
247
+ Every place that accepts a value accepts three forms:
248
+
249
+ | You write | jev reads |
250
+ | --- | --- |
251
+ | `some text` | The literal text |
252
+ | `@path/to/file` | The file's contents |
253
+ | `-` | Standard input |
254
+ | `@@text` | The literal text `@text` (escape for values that start with `@`) |
255
+
256
+ Stdin is read once and shared, so only one argument per command can be `-`.
257
+
258
+ Lists (claims) accept one item per line or a JSON array of strings. Items with ids (evidence, candidates) accept a JSON array of strings, an array of `{id, text}` objects, or an object mapping id to text.
259
+
260
+ ## Output and exit codes
261
+
262
+ Text output is the default and is meant for humans. Add `--json` for scripts. The JSON always includes `command`, `model`, `provider`, and `usage` (token counts), plus the command's results. Field names are a stable contract and changes are noted in [CHANGELOG.md](CHANGELOG.md).
263
+
264
+ | Exit code | Meaning |
265
+ | --- | --- |
266
+ | `0` | Success, and no `--fail-on` condition matched |
267
+ | `1` | Usage, configuration, input, or network error. Details on stderr. |
268
+ | `2` | The command ran fine, but a `--fail-on` condition matched |
269
+
270
+ Code 2 is what lets `jev` act as a gate in `&&` chains, hooks, and CI steps.
271
+
272
+ ### Global options
273
+
274
+ Work before or after the subcommand.
275
+
276
+ | Option | Meaning |
277
+ | --- | --- |
278
+ | `--json` | JSON output. Same as `--format json`. |
279
+ | `-m, --model <name>` | Model to use, e.g. `jev-latest` or a pinned `jev-1.13.0` |
280
+ | `-P, --provider <name>` | `auto`, `typesafe`, `openrouter`, or `cloudflare` |
281
+ | `--timeout <ms>` | Per-request timeout. Default `30000`. |
282
+ | `--dry-run` | Print the exact request that would be sent, then exit 0 without calling the API |
283
+ | `-q, --quiet` | Omit the token usage footer in text output |
284
+ | `--no-color` | Disable colors. `NO_COLOR` and `FORCE_COLOR` environment variables are also honored. |
285
+
286
+ ## Configuration
287
+
288
+ Settings resolve in this order, later wins: built-in defaults, config file, environment variables, command-line flags.
289
+
290
+ The config file lives at `$JEV_CONFIG` if set, else `$XDG_CONFIG_HOME/jev/config.json`, else `~/.config/jev/config.json`. It never holds API keys.
291
+
292
+ ```json
293
+ {
294
+ "provider": "auto",
295
+ "model": "jev-latest",
296
+ "timeoutMs": 30000,
297
+ "format": "text",
298
+ "verify": { "autoAccept": 0.8 },
299
+ "screen": { "blockAt": 0.75, "reviewAt": 0.25 },
300
+ "find": { "topK": 5, "found": 0.7, "absent": 0.35 }
301
+ }
302
+ ```
303
+
304
+ ### Environment variables
305
+
306
+ | Variable | Purpose |
307
+ | --- | --- |
308
+ | `TYPESAFE_API_KEY` | TypeSafe API key. Recommended provider; used automatically when set. |
309
+ | `TYPESAFE_BASE_URL` | Alternate TypeSafe endpoint, for proxies or testing |
310
+ | `OPENROUTER_API_KEY` | OpenRouter key (`sk-or-...`). Used when no TypeSafe key is present. |
311
+ | `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` | Cloudflare Workers AI. Used when no other key is present. `JEV_CLOUDFLARE_API_TOKEN` takes precedence over `CLOUDFLARE_API_TOKEN` if you need separate credentials. |
312
+ | `JEV_PROVIDER` | Force `typesafe`, `openrouter`, or `cloudflare` |
313
+ | `JEV_MODEL` | Default model |
314
+ | `JEV_TIMEOUT_MS` | Default timeout |
315
+ | `JEV_FORMAT` | Default output format, `text` or `json` |
316
+ | `JEV_CONFIG` | Config file path |
317
+ | `JEV_DEBUG=1` | Print stack traces on errors |
318
+
319
+ ### Providers
320
+
321
+ | Provider | Notes |
322
+ | --- | --- |
323
+ | TypeSafe (direct) | Recommended. Lowest latency, `jev-latest` alias, pinned versions, automatic retries with backoff. |
324
+ | OpenRouter | Alpha endpoint. Serves pinned versions only, so `jev-latest` maps to `typesafe/jev-1.13`. Adds a network hop. |
325
+ | Cloudflare Workers AI | Serves the single `typesafe/jev` alias, no version pinning. Adds a network hop. |
326
+
327
+ ## Guidelines for good results
328
+
329
+ **Write claims as single, checkable statements.** "The API returns JSON and supports pagination" is two claims. Split them so each gets its own verdict.
330
+
331
+ **Give the model everything it needs, and only that.** Evidence should contain the passages the claims depend on. Very long evidence dilutes accuracy and costs more. For quote-level checks, locate the relevant passage in code first and send only that.
332
+
333
+ **Treat thresholds as starting points.** The defaults (`0.8` auto-accept, `0.75` block, `0.25` review, `0.7`/`0.35` exists) come from TypeSafe's published cookbooks. Run `jev` over a sample of your own data with `--json`, look at the distributions, and set thresholds that match the cost of a wrong answer in your situation. Pin a model version with `--model jev-1.13.0` once you have tuned them, so an alias update does not silently shift results.
334
+
335
+ **Read confidence correctly.** For `choice` and `score` answers, confidence measures how concentrated the probability distribution is. Low confidence means the options were close, not that the model is wrong. For `noul` answers there is no separate confidence; a value near `0.5` means "as likely yes as no".
336
+
337
+ **Keep policy in your code.** `jev` reports judgments. Whether to block, retry, escalate, or ignore is your decision, expressed through `--fail-on` or by reading the JSON.
338
+
339
+ **Mind what you send.** Everything you pass is sent to the configured provider. Do not include secrets or data you are not permitted to share. `--dry-run` shows the exact payload.
340
+
341
+ **Watch cost with the usage footer.** Each result reports input and output tokens. Only input tokens are billed. Batching several questions into one `jev ask` call is cheaper and faster than several calls.
342
+
343
+ **Prefer direct TypeSafe.** Proxies add latency and lag behind on model versions.
344
+
345
+ ## Recipes
346
+
347
+ **Gate a pull request on its own description.** Put each claim from the PR body on its own line in `claims.txt`, then:
348
+
349
+ ```bash
350
+ git diff origin/main...HEAD | jev verify --claims @claims.txt --evidence - --fail-on contradicted,unsupported
351
+ ```
352
+
353
+ **Guard an agent's web fetches.** Only pass content through if it is safe and relevant:
354
+
355
+ ```bash
356
+ page=$(curl -s "$url")
357
+ if printf '%s' "$page" | jev screen --purpose "$task" --fail-on block,review,skip -q; then
358
+ printf '%s' "$page" | my-agent --context -
359
+ fi
360
+ ```
361
+
362
+ **Route a support ticket in a shell script.**
363
+
364
+ ```bash
365
+ team=$(jev ask @ticket.txt --choice team="Which team?|billing,technical,sales" --json | jq -r .answers.team.choice)
366
+ ```
367
+
368
+ **Find the right file, then open it.**
369
+
370
+ ```bash
371
+ jev find "where is retry logic configured" --files src/**/*.ts --json | jq -r '.top[0].id' | xargs code
372
+ ```
373
+
374
+ **Check that a knowledge base actually answers a question** before handing the top hit to a user:
375
+
376
+ ```bash
377
+ jev find "$question" --lines @faq.txt --fail-on absent,partial --json
378
+ ```
379
+
380
+ ## Troubleshooting
381
+
382
+ | Symptom | Fix |
383
+ | --- | --- |
384
+ | `No credentials found` | Export `TYPESAFE_API_KEY`, then run `jev config` to confirm it is visible to the process |
385
+ | `HTTP 401` | The key is wrong or revoked. Check it at console.typesafe.ai and re-export. |
386
+ | `HTTP 429` | Rate limited. The TypeSafe provider retries automatically; if it persists, slow down or batch questions. |
387
+ | `Request timed out` | Raise `--timeout` or set `JEV_TIMEOUT_MS`. Large evidence takes longer. |
388
+ | `Expected ... on stdin but stdin is a terminal` | You used `-` without piping anything in. Pass text or `@file` instead. |
389
+ | `Too many candidates` | `find` accepts 250 per call. Pre-filter in code or split the set. |
390
+ | Colors in captured output | Add `--no-color` or set `NO_COLOR=1` |
391
+ | Want to see the request | Add `--dry-run` |
392
+ | Want a stack trace | Set `JEV_DEBUG=1` |
393
+
394
+ ## Use from Node.js
395
+
396
+ The same functions the CLI uses are exported, so you can embed judgments in a script without shelling out.
397
+
398
+ ```ts
399
+ import { createAsk, runVerify, runScreen, runFind } from "jevctl";
400
+
401
+ const ask = createAsk({ provider: "auto", model: "jev-latest", timeoutMs: 30_000 });
402
+
403
+ const verdicts = await runVerify(ask, {
404
+ claims: ["The ordinance mentions reflective gear."],
405
+ evidence: [{ id: "ordinance", text: ordinanceText }],
406
+ autoAccept: 0.8,
407
+ });
408
+ console.log(verdicts.results[0].verdict, verdicts.results[0].confidence);
409
+
410
+ const screened = await runScreen(ask, { text: pageHtml, purpose: "extract prices", blockAt: 0.75, reviewAt: 0.25 });
411
+ if (screened.recommendation.action === "block") throw new Error(screened.recommendation.reason);
412
+ ```
413
+
414
+ ## Development
415
+
416
+ ```bash
417
+ git clone https://github.com/Nasrallah-AL/jev-cli
418
+ cd jev-cli
419
+ npm install
420
+ npm run check # typecheck, lint, and tests. No API key needed.
421
+ npm run build # compiles to dist/
422
+ npm run dev -- screen "hello" --dry-run # run from source
423
+ npm run test:e2e # live API tests. Requires TYPESAFE_API_KEY.
424
+ ```
425
+
426
+ Tests never touch the network: CLI tests spawn the built binary against a local fake TypeSafe API. See [CONTRIBUTING.md](CONTRIBUTING.md) for layout and pull request expectations, and [SECURITY.md](SECURITY.md) to report a vulnerability.
427
+
428
+ ## License
429
+
430
+ [MIT](LICENSE)
package/SECURITY.md ADDED
@@ -0,0 +1,21 @@
1
+ # Security
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Please do not open a public issue for security problems. Use GitHub's private
6
+ vulnerability reporting on this repository ("Report a vulnerability" under the
7
+ Security tab), or email the maintainer listed in `package.json`. You should get
8
+ an acknowledgement within a few days.
9
+
10
+ ## Scope and notes
11
+
12
+ - jev-cli sends the text you pass it (claims, evidence, screened content,
13
+ candidates, state) to the configured provider: TypeSafe, OpenRouter, or
14
+ Cloudflare. Do not pass secrets or data you are not allowed to send to those
15
+ services. `--dry-run` shows exactly what would be sent.
16
+ - API keys are read from environment variables or, for the base URL only, the
17
+ config file. The config file never stores keys. `jev config` prints keys
18
+ masked.
19
+ - Third-party proxies (OpenRouter, Cloudflare) add a hop; direct TypeSafe is
20
+ the recommended default.
21
+ - Supported versions: the latest published minor release receives fixes.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ export declare function createProgram(): Command;
4
+ export declare function main(argv?: string[]): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ // jev: TypeSafe's Jev model at the command line.
3
+ import { createRequire } from "node:module";
4
+ import { Command } from "commander";
5
+ import { registerAsk } from "./commands/ask.js";
6
+ import { registerConfig } from "./commands/config.js";
7
+ import { registerFind } from "./commands/find.js";
8
+ import { registerModels } from "./commands/models.js";
9
+ import { registerScreen } from "./commands/screen.js";
10
+ import { registerVerify } from "./commands/verify.js";
11
+ import { buildContext } from "./context.js";
12
+ import { CliError, describeError, EXIT } from "./errors.js";
13
+ const require = createRequire(import.meta.url);
14
+ const { version, description } = require("../package.json");
15
+ export function createProgram() {
16
+ const program = new Command();
17
+ program
18
+ .name("jev")
19
+ .description(description)
20
+ .version(version, "-V, --version", "print the version")
21
+ .showHelpAfterError("(run with --help for usage)")
22
+ .showSuggestionAfterError()
23
+ .option("--json", "print results as JSON (same as --format json)")
24
+ .option("--format <fmt>", "output format: text or json")
25
+ .option("-m, --model <name>", "Jev model, e.g. jev-latest or jev-1.13.0")
26
+ .option("-P, --provider <name>", "auto, typesafe, openrouter, or cloudflare")
27
+ .option("--timeout <ms>", "per-request timeout in milliseconds")
28
+ .option("--no-color", "disable colored output")
29
+ .option("-q, --quiet", "omit the usage/model footer in text output")
30
+ .option("--dry-run", "print the request that would be sent and exit without calling the API")
31
+ .addHelpText("after", `
32
+ Exit codes:
33
+ 0 success
34
+ 1 usage, configuration, input, or transport error
35
+ 2 a --fail-on judgment condition matched (e.g. a contradicted claim)
36
+
37
+ Credentials (first found wins unless --provider is set):
38
+ TYPESAFE_API_KEY https://console.typesafe.ai/settings/keys
39
+ OPENROUTER_API_KEY (sk-or-...) OpenRouter Decisions API
40
+ CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID Cloudflare Workers AI
41
+
42
+ Docs: https://docs.typesafe.ai`);
43
+ const run = async (fn, cmd) => {
44
+ const flags = cmd.optsWithGlobals();
45
+ const ctx = buildContext(flags);
46
+ const code = await fn(ctx);
47
+ if (code !== EXIT.OK)
48
+ process.exitCode = code;
49
+ };
50
+ registerVerify(program, run);
51
+ registerScreen(program, run);
52
+ registerFind(program, run);
53
+ registerAsk(program, run);
54
+ registerModels(program, run);
55
+ registerConfig(program, run);
56
+ return program;
57
+ }
58
+ export async function main(argv = process.argv) {
59
+ const program = createProgram();
60
+ try {
61
+ await program.parseAsync(argv);
62
+ }
63
+ catch (err) {
64
+ const debug = process.env.JEV_DEBUG === "1";
65
+ if (err instanceof CliError) {
66
+ process.stderr.write(`jev: ${err.message}\n`);
67
+ process.exitCode = err.exitCode;
68
+ }
69
+ else {
70
+ process.stderr.write(`jev: ${describeError(err)}\n`);
71
+ process.exitCode = EXIT.ERROR;
72
+ }
73
+ if (debug && err instanceof Error && err.stack)
74
+ process.stderr.write(`${err.stack}\n`);
75
+ }
76
+ }
77
+ await main();
78
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandContext } from "../context.js";
3
+ import { type AskOutput, type RawQuestions } from "../core/ask.js";
4
+ export interface AskFlags {
5
+ questions?: string;
6
+ noul?: string[];
7
+ choice?: string[];
8
+ score?: string[];
9
+ stateJson?: boolean;
10
+ }
11
+ export declare function resolveState(positional: string | undefined, flags: AskFlags): unknown;
12
+ export declare function resolveQuestions(flags: AskFlags): RawQuestions;
13
+ export declare function askAction(positional: string | undefined, flags: AskFlags, ctx: CommandContext): Promise<number>;
14
+ export declare function renderAsk(out: AskOutput, ctx: CommandContext): string;
15
+ export declare function registerAsk(program: Command, run: (fn: (ctx: CommandContext) => Promise<number>, cmd: Command) => Promise<void>): void;