[!NOTE] Status: experimental. The command definitions and shell integrations may change before 1.0.
The last line is not output. It is your next prompt, already filled in. jany prints one shell-quoted line on stdout, and the wrapper from jany --init zsh puts it on the command line. Read it, edit it if you like, press Enter.
|
jany generalises jind (jev × find) and jurl (jev × curl) into one binary. Each command is a definition directory — three files, no Rust — and adding a command is a job for an LLM skill, not a new crate.
How it works
- rules — the unambiguous shapes are decided by data in
schema.toml: paths, globs,8080:80,KEY=value,+7d,>10M, table words likefiles/delete/background. If every word resolves, jany never leaves your machine. - jev — anything left over goes to jev (TypeSafe System One, via OpenRouter) in one request: "what is the role of each word?" plus the extra questions the schema declares (which unit? at least or at most? a typo of which table word?). jev only picks from fixed choices and returns probabilities; it never generates the command.
- repair — fixes what jev cannot see word by word: attach
daysto7, letexceptclaim the names after it, pairfirst_name amandainto key and value. - assemble.sh — the command's own script (stdin JSON → stdout JSON) turns the role-tagged words into
argv. It also says how risky the result is. - output — the line goes to stdout, everything else to stderr. When the words can't be turned into a command (unresolved, or below a confidence floor), jany exits non-zero and puts
jany <command> --hinton the prompt instead, with the reason as a comment. Adangerousresult (find-delete) is previewed first with a read-only run. Anunsafeone (anything that changes state: curlPOST/DELETE,docker run) gets a one-line note, andautorunleaves it on the prompt.
One jev call is 200–700 ms and under $0.0001.
Setup
# Before the release, install from this checkout:
jany --init does three things: prints the wrapper function, installs the built-in definitions (find, curl, docker run) into ~/.config/jany/cmd/, and installs the /jany-register and /jany-update skills into ~/.agents/skills/ (linked from ~/.claude/skills/ and ~/.codex/skills/ when those exist). It never overwrites a definition that is already there. The skills are in English by default; jany --init zsh --locale ja installs the Japanese one (put the flag in your rc line, since --init rewrites the skill on every shell start).
In zsh the wrapper also shows a dim hint of what you can still say after jany <command> (the definition's [[placeholders]]), e.g. jany find src → <file|dir> <*.log> <older than N days> <delete|count>. It never calls jev. [suggest] enabled = false in ~/.config/jany/config.toml turns it off; JANY_SUGGEST=0 / 1 overrides that for one shell. bash and fish don't have it.
[cmd.<name>] autorun = true in ~/.config/jany/config.toml lets the zsh wrapper run the line instead of putting it on the prompt, but only when the rules decided every word and the definition calls it risk "none": no jev, no words after --, no raw -x flags, no preview or pipe. jany <command> -- --help and -- --version with nothing else also run. autorun_also = ["pnpm install"] lets lines that start with those words run even when the definition calls them "unsafe" (compared word by word on the final argv, so jany pnpm install react, which becomes pnpm add react, does not match; "dangerous" never runs). The line is shown on stderr and still goes into your history. Anything else goes on the prompt as before. Off by default, and bash / fish always put the line on the prompt.
OPENROUTER_API_KEY in the environment takes precedence; a key saved by jind setup or jurl setup is picked up too. Tested on macOS with zsh.
Usage
jany <command> [words ...] [flags] [-- passthrough args]
| flag | |
|---|---|
--explain |
per-word role, confidence, and whether a rule or jev decided it (stderr) |
--no-jev |
offline only; unresolved words are an error |
--hint |
what you can say to <command> (its roles) and examples from its cases.toml (stderr) |
-- … |
passed through untouched (what that means is up to the command: find options, curl flags, the container command for docker run) |
jany's own actions are flags, so <command> is always the tool's name:
jany --list |
the definitions found, with an example each |
jany --test find |
run a definition's cases.toml (jev answers are mocked) |
/jany-register tar |
create and fill ~/.config/jany/cmd/tar/ with the agent skill |
jany --update |
after upgrading jany: replace the built-ins you have not edited, list what the others lack, and install the skills that are missing |
/jany-update tar |
add only what a definition lacks, with the agent skill; existing rules and cases stay |
jany --init zsh|bash|fish |
the wrapper, plus built-ins and the skill (--locale en|ja, default en) |
jany --setup |
save the API key |
Adding a command
A definition lives in ~/.config/jany/cmd/<name>[/<sub>]/:
| file | |
|---|---|
schema.toml |
roles (what jev may choose from), word tables, rules, questions for jev, repair steps, risk settings |
assemble.sh |
role-tagged tokens in, {argv, preview, risk, pipe, error} out; any language, bash + jq is enough |
cases.toml |
words → expected argv, with jev's answers written down; jany --test refuses answers to questions jany did not ask |
The skill reads a reference of every schema key and the two worked examples (find, curl) before writing. The rule of thumb from jind and jurl carries over: cover the 80 % you actually type, pass the rest through after --, and do not trust an assemble.sh that has no cases.
Updating definitions
A new jany can bring new definition features (such as [[placeholders]]) and updated built-ins, but jany --init never touches a definition that is already in place. After upgrading, run:
jany --update knows a built-in is unedited when every file matches a version jany has shipped (examples/released.txt). An edited built-in is left alone and listed, like your own definitions; /jany-update merges the new parts into it and keeps your edits. jany --update also installs /jany-update itself when it is missing (in the language of the installed /jany-register, or --locale en|ja); skill files already there are left alone.
Config (optional)
~/.config/jany/config.toml
[]
= "typesafe/jev-1.13"
= 0.5 # below this, no command: exit non-zero and offer `--hint`
[]
= false # no dim hint in zsh (JANY_SUGGEST=0/1 overrides it)
[] # overrides the schema's [defaults]
= "text/plain"
[]
= "~/Downloads"
[]
= true # zsh: run rule-only, risk "none" lines right away
= ["pnpm install"] # ...and these, even when "unsafe"
Where it is weak
- The same weaknesses as jind and jurl: a bare word (
log,app) can be two roles at once, and jev is often only 0.6–0.9 sure. Write the unambiguous form (*.log) to skip jev. - Commands whose vocabulary does not close — arbitrary SQL, jq programs, ffmpeg filter graphs — are not a fit. Cover the common forms and pass the rest through.
- Every word, including ones the rules already decided, is sent to jev as context. Roles that may carry secrets (curl headers, docker
KEY=value) declare amaskso only a placeholder goes out.