onesie

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 26, 2026 License: MIT

README

onesie

Possibly the most expressive Jev CLI.

onesie asks TypeSafe's Jev model questions about a piece of text and gives back a typed answer: how likely a yes is, which of your options fits, or where it sits on a scale. Text goes in on stdin, the answer comes out on stdout, and the exit code reports the result.

echo 'EVERYTHING IS DOWN, CALL ME NOW' | onesie 'does this convey urgency' -r
# 0.99

Install

Homebrew, on macOS or Linux:

brew install frodi-karlsson/tap/onesie

The install script, which checks the download against the release's checksums.txt, and against its build provenance when gh is logged in:

curl -fsSL https://raw.githubusercontent.com/frodi-karlsson/onesie/main/install.sh | sh

It installs to ~/.local/bin. Set ONESIE_INSTALL_DIR for another directory, and ONESIE_VERSION for a release other than the latest.

With Go:

go install github.com/frodi-karlsson/onesie/cmd/onesie@latest

macOS blocks a onesie binary downloaded through a browser, so install it with one of these three.

Then store a key with onesie auth set. TypeSafe is the default provider, and --provider openrouter or --provider berget asks through OpenRouter or Berget instead.

In Claude Code, the plugin can come first and walk you through the rest:

/plugin marketplace add frodi-karlsson/onesie
/plugin install onesie@onesie

Features

  • Three kinds of question. Yes or no, pick one of your options, or rate on a scale.
  • Several questions per request. Five questions cost about as much as one.
  • Answers as exit codes. --assert turns an answer into pass or fail for a script or CI, with a separate unsure result for a person to decide.
  • Thresholds from data. onesie calibrate shows what each threshold catches and misses on examples you have already labelled.
  • Unix pipes. Reads stdin, writes answers to stdout and errors to stderr, so it works with jq, grep and xargs.
  • Streams that resume. Reads JSONL, CSV or TSV a record at a time, several in parallel, and writes one answer per record in order. Rerun it with --resume after an interruption. With --id it asks only the records the file holds no answer for, and without it carries on after the last complete line. Records whose request is identical are asked once, and each still gets its own line.
  • Output for programs and people. JSON, CSV, a terminal table, or markdown for a PR comment.
  • Free dry runs. See the exact request before spending anything, with no key needed.
  • Mock answers. --mock or ONESIE_MOCK answers from a file, so a gate script's branches can be tested with no key and no network.
  • Response caching. --cache or ONESIE_CACHE=1 answers a repeated request from disk, so a script run again and again pays once for each distinct question.
  • Agent skills. A plugin for Claude Code and Codex, and the same skills for Cursor and Gemini.

Usage

Ask a question

A yes or no question answers with how likely the yes is.

echo 'The site is down and customers cannot pay' | onesie 'is this urgent'
# model jev-1.13.0
#
# answer  0.9700

-r prints only the answer.

echo 'The site is down and customers cannot pay' | onesie 'is this urgent' -r
# 0.96
Pick from options
echo 'I was charged twice for my order' | onesie 'who should handle this' \
    --pick billing,shipping,technical -r
# billing

Without -r, the table also shows how sure the model is of each option.

Rate on a scale

List the levels from lowest to highest.

echo 'Third time asking. Fix it or I am leaving.' | onesie 'how frustrated is the customer' \
    --rate calm,annoyed,furious -r
# furious

--desc describes an option or a level, which steers the answer. A scale needs every level described, or none.

Ask several questions at once

Give each question a name with --ask. They go out in one request.

onesie --ask urgent='is this urgent' \
       --ask team='who should handle this' --pick billing,shipping,technical \
       -o values < ticket.txt
# {"urgent":0.69,"team":"billing"}
Use the answer in a script

--assert sets the exit code: 0 when it holds, 1 when it does not. -q prints nothing.

onesie 'is this urgent' --assert 'answer.value > 0.8' -q < ticket.txt && page_on_call

Any other exit code means there was no answer, such as a bad key or a server that did not reply. REFERENCE.md lists them.

Recipes

Guard an agent's shell commands. Exit 0 runs the command, 1 blocks it and 7 asks a person.

onesie --ask d='does this destroy data' --ask c='does this send credentials' \
    --assert     'max(d.value, c.value) < 0.2' \
    --abstain-if 'max(d.value, c.value) < 0.8' \
    -q --state "$cmd"
case $? in
    0) eval "$cmd" ;;
    1) echo blocked ;;
    7) ask_the_user ;;
    *) echo 'no answer, blocked' ;;
esac

Fail a CI step when a pull request does not say why. Exit 1 means the description does not say why, and any other code means there was no answer.

status=0
onesie 'does this explain why the change is needed' --assert 'answer.value > 0.6' \
    -q --state "$PR_BODY" || status=$?
case $status in
    0) ;;
    1) echo 'the description does not say why'; exit 1 ;;
    *) echo "onesie gave no answer, exit $status"; exit 1 ;;
esac

Comment on a pull request. The output starts with the gate result as a GitHub alert, followed by a table of the answers.

onesie 'does this explain why the change is needed' --assert 'answer.value > 0.6' \
    -o markdown --state "$PR_BODY" | gh pr comment "$PR" --body-file -

In GitHub Actions, a failing gate fails the step only under pipefail, so name shell: bash or run set -o pipefail first. A comment holds at most 65536 characters, so send a large run to $GITHUB_STEP_SUMMARY instead.

Triage a spreadsheet. The same rows come back with a column per question.

onesie --ask urgent='does `body` convey urgency' -i csv -o csv --merge < tickets.csv > triaged.csv

Run a large batch you can resume. --map sends only the body, --id names each answer, and --resume skips every id the file already answers and asks the rest, failed ones included.

onesie 'is this urgent' -i jsonl -j 8 --map '.body' --id '.id' \
    --out answers.jsonl --resume < tickets.jsonl
jq -c 'select(.answer.value > 0.8)' answers.jsonl

Rank records by several questions. One --ask per dimension scores every record, and jq weighs the answers and sorts by the sum. .value is the yes/no probability, and .norm is the rate's position between 0 and 1. The weighting lives in jq, since --assert has no arithmetic, and select(has("error") | not) drops the records that failed.

onesie --ask impact='does this help many users' \
    --ask effort='how much work is this' --rate trivial,small,medium,large \
    -i jsonl --map '.body' --id '.id' -o json < ideas.jsonl |
    jq -s 'map(select(has("error") | not)) | map({id, score: (0.7 * .impact.value + 0.3 * (1 - .effort.norm))}) | sort_by(-.score)'

Pick a threshold from labelled tickets. Each row shows how many urgent tickets that threshold catches and how many it flags by mistake.

onesie calibrate --ask urgent='is this urgent' -i jsonl --map '.body' \
    --label urgent='.is_urgent' --id '.id' < labelled.jsonl
# urgent, yes/no: labelled 8, 4 yes, 4 no, 0 failed. AUC 1.00
# flagged means urgent.value >= cut
#
#   cut   flagged  catches           false alarms    right when flagged
#   ...
#   0.90        4  4/4 100% 51-100%  0/4  0%  0-49%  4/4 100% 51-100%
onesie 'is this urgent' --assert 'answer.value >= 0.9' -q --state "$body" && page_on_call

Fail CI when a calibrated gate drifts. Keep the answers file beside the labels, and a CI step checks the gate against it with --offline, which needs no key and costs nothing. Exit 1 means a requirement no longer holds, and exit 2 that the file no longer matches the questions, so regenerate it.

# once, and again whenever the questions change
onesie calibrate -f triage -i jsonl --map '.body' --label urgent='.is_urgent' --id '.id' \
    -m jev-1.13.0 --provider typesafe --out answers.jsonl --resume < labelled.jsonl
git add answers.jsonl answers.jsonl.onesie

# in CI
onesie calibrate -f triage -i jsonl --map '.body' --label urgent='.is_urgent' --id '.id' \
    -m jev-1.13.0 --provider typesafe --out answers.jsonl --resume --offline \
    --require 'urgent.catches >= 0.95' --require 'urgent.false_alarms <= 0.1 at abstain' \
    < labelled.jsonl

Try it before spending anything. --print-request prints the request without sending it and needs no key. -i request later sends those requests unchanged.

onesie 'is this urgent' --state 'the site is down' --print-request
onesie -i jsonl 'is this urgent' --print-request < tickets.jsonl > frozen.jsonl
onesie -i request < frozen.jsonl > answers.jsonl

Test a gate script without spending anything. ONESIE_MOCK makes every onesie call in the script answer from a file, so each branch can be tested with no key and the script unchanged.

cat > gate.sh <<'EOF'
onesie -f examples/questions/shell-safety.yaml -q --state "$1"
case $? in
    0) echo run ;;
    1) echo blocked ;;
    7) echo ask a person ;;
    *) echo 'no answer, blocked' ;;
esac
EOF
echo '{"destroys": 0.93, "secrets": 0.02, "network": 0.1}' > danger.json
echo '{"error": 503}' > outage.json
ONESIE_MOCK=danger.json sh gate.sh 'rm -rf /'   # blocked
ONESIE_MOCK=outage.json sh gate.sh 'ls'         # no answer, blocked

A script run many times on the same inputs can set ONESIE_CACHE=1 instead, so each distinct request is asked once and every later run answers it from disk.

Keep a question set in a file. --print-questions writes the questions to a file, and -f NAME loads it from any directory in the repository. Its first line names the schema onesie --print-schema prints, so an editor with the YAML language server checks the file as you edit it.

mkdir -p .onesie/questions
onesie --ask urgent='is this urgent' --ask team='who owns this' --pick billing,platform \
    --assert 'urgent.value < 0.9' --print-questions > .onesie/questions/triage.yaml
onesie -f triage -o values < ticket.txt
# {"assert":false,"urgent":0.97,"team":"billing"}, and exit 1, since this ticket is urgent

examples/README.md has starter sets for shell safety, prompt injection, personal data and moderation. Each file carries a calibrated gate. Copy one into .onesie/questions and -f shell-safety runs the whole gate, or pass the path, as in -f examples/questions/shell-safety.yaml.

Why no MCP server

onesie ships agent skills instead of an MCP server:

  • Validation. The CLI already checks every question, flag and assertion before any request, and an agent reads the same messages and exit codes a person does.
  • Documentation. onesie --help, -V and the error messages come from the binary, so they stay in step with it.
  • Discoverability. Skills have room for more prose than a tool description, such as worked recipes and the mistakes to avoid, and CI checks their examples against the binary.

Reference

REFERENCE.md covers gating, testing a gate, calibrating, streams and resume, keys and providers, exit codes and the rest of the flags.

Contributing

See CONTRIBUTING.md.

License

MIT. See LICENSE.

Directories

Path Synopsis
cmd
examplesanswers command
Command examplesanswers regenerates the starter sets' committed answers files from the live API, with the pinned model.
Command examplesanswers regenerates the starter sets' committed answers files from the live API, with the pinned model.
formulagen command
Command formulagen writes the Homebrew formula for a release from the release's checksums.txt.
Command formulagen writes the Homebrew formula for a release from the release's checksums.txt.
onesie command
Command onesie is the command line interface for TypeSafe Jev.
Command onesie is the command line interface for TypeSafe Jev.
proseblocks command
Command proseblocks reads a unified diff on stdin and writes the prose blocks of the Go and Markdown files that overlap an added line as JSON lines of {"id":"path:line","text":"..."}.
Command proseblocks reads a unified diff on stdin and writes the prose blocks of the Go and Markdown files that overlap an added line as JSON lines of {"id":"path:line","text":"..."}.
releaseprep command
Command releaseprep checks a release tag against the existing ones and bumps the plugin manifests to it.
Command releaseprep checks a release tag against the existing ones and bumps the plugin manifests to it.
skillcheck command
Command skillcheck dry runs every rule's bad and good example under skills/.
Command skillcheck dry runs every rule's bad and good example under skills/.
skilleval command
Command skilleval dry runs every onesie command an agent wrote during the last claude plugin eval run.
Command skilleval dry runs every onesie command an agent wrote during the last claude plugin eval run.
skillgen command
Command skillgen generates the per client skill files from skills/.
Command skillgen generates the per client skill files from skills/.
Package examples describes the starter question sets, so the tests and the tool that regenerates their answers read each set the same way.
Package examples describes the starter question sets, so the tests and the tool that regenerates their answers read each set the same way.
internal
answer
Package answer normalizes API answers into the shape the CLI prints and applies the per question policy.
Package answer normalizes API answers into the shape the CLI prints and applies the per question policy.
argv
Package argv records group local flags in the order pflag encounters them, so the question groups in a command line can be reconstructed after parsing.
Package argv records group local flags in the order pflag encounters them, so the question groups in a command line can be reconstructed after parsing.
assert
Package assert evaluates a boolean expression over a normalized record.
Package assert evaluates a boolean expression over a normalized record.
cache
Package cache stores API responses on disk under a sha256 key, with private modes, atomic writes, a lifetime for model aliases and a size cap enforced by least recently used eviction.
Package cache stores API responses on disk under a sha256 key, with private modes, atomic writes, a lifetime for model aliases and a size cap enforced by least recently used eviction.
calibrate
Package calibrate reads the labels a calibration run compares answers with, and scores and reports how well the answers agree with them.
Package calibrate reads the labels a calibration run compares answers with, and scores and reports how well the answers agree with them.
cli
Package cli assembles the onesie command tree.
Package cli assembles the onesie command tree.
creds
Package creds resolves, reads and writes the credential file that holds an API key when neither a flag nor the environment supplies one.
Package creds resolves, reads and writes the credential file that holds an API key when neither a flag nor the environment supplies one.
engine
Package engine runs one evaluation per record the source yields, bounded by concurrency and ordered by input position.
Package engine runs one evaluation per record the source yields, bounded by concurrency and ordered by input position.
formula
Package formula renders the Homebrew formula for a onesie release from the release's checksums.
Package formula renders the Homebrew formula for a onesie release from the release's checksums.
input
Package input resolves where a state comes from and reads it, so the detection rule lives in one place rather than being rediscovered at every call site.
Package input resolves where a state comes from and reads it, so the detection rule lives in one place rather than being rediscovered at every call site.
interrupt
Package interrupt lets a caller stop waiting on a read that has no deadline.
Package interrupt lets a caller stop waiting on a read that has no deadline.
jev
Package jev is a client for the TypeSafe System One API, ported from the MIT licensed @typesafe-ai/sdk JavaScript client.
Package jev is a client for the TypeSafe System One API, ported from the MIT licensed @typesafe-ai/sdk JavaScript client.
jq
Package jq compiles and runs the jq expressions that --map and --id take, through gojq.
Package jq compiles and runs the jq expressions that --map and --id take, through gojq.
limits
Package limits holds every API limit onesie enforces locally and every constant it defaults to, so a binary built against stale numbers is diagnosable through --version.
Package limits holds every API limit onesie enforces locally and every constant it defaults to, so a binary built against stale numbers is diagnosable through --version.
mock
Package mock reads a file of canned answers and checks it against the questions a run asks, so the run can answer from it with no key and no network.
Package mock reads a file of canned answers and checks it against the questions a run asks, so the run can answer from it with no key and no network.
output
Package output encodes a normalized record in each of the modes the CLI offers.
Package output encodes a normalized record in each of the modes the CLI offers.
plan
Package plan turns a recorded command line into a validated invocation, ready to run.
Package plan turns a recorded command line into a validated invocation, ready to run.
proseblocks
Package proseblocks finds the pieces of prose a change touches in Go and Markdown files: one block per comment group, paragraph or list item.
Package proseblocks finds the pieces of prose a change touches in Go and Markdown files: one block per comment group, paragraph or list item.
qfile
Package qfile loads a question file or a raw API request body into the ordered question slice the planner already consumes.
Package qfile loads a question file or a raw API request body into the ordered question slice the planner already consumes.
release
Package release checks a release tag against the existing ones and bumps the plugin manifests to it.
Package release checks a release tag against the existing ones and bumps the plugin manifests to it.
skillcheck
Package skillcheck dry runs every rule's bad and good example in a skill.
Package skillcheck dry runs every rule's bad and good example in a skill.
skilleval
Package skilleval dry runs every onesie command an agent wrote during a claude plugin eval run, since the eval has no grader that can run code.
Package skilleval dry runs every onesie command an agent wrote during a claude plugin eval run, since the eval has no grader that can run code.
skillgen
Package skillgen decodes and validates skill.json, the schema a generated skill is built from.
Package skillgen decodes and validates skill.json, the schema a generated skill is built from.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL