Run shell commands from natural-language requests:
jst find all files bigger than 500 mb in ~/downloadsbrew install yoavf/tap/jstEach GitHub release includes builds for:
- macOS (signed and notarized universal binary for Apple Silicon and Intel)
- Linux x86-64
- Windows x86-64
Download the archive for your platform and put jst (or jst.exe on Windows)
somewhere on your PATH. For example, on macOS or Linux:
install -m 755 jst-*/jst ~/.local/bin/jstFor WSL, use the Linux archive and install it inside your WSL distribution. Use
the Windows archive when running jst from native Command Prompt or PowerShell.
SHA-256 checksums are published beside every release artifact.
JST prints the generated command and immediately executes it. Commands that match the local destructive-command denylist, or whose model-described effects indicate deletion, privilege use, process changes, software installation, remote changes, or downloaded-code execution, require confirmation.
→ find /Users/me/downloads -type f -size +500M
Use --yolo to skip all safety confirmations:
jst --yolo remove all stopped docker containersUse --dry to print a generated command and exit without running it:
jst --dry show the current directoryUse -i or --interactive to inspect and refine a command before anything
runs:
$ jst -i show me the 10 largest files in this folder
→ du -ah . | sort -hr | head -n 10
Run it? [y]es [n]o [w]hy [a]sk AI [e]dit › w
du -ah . measure every entry (“files in this folder”)
| sort -hr order sizes largest first (“largest”)
| head -n 10 keep the first ten results (“show me the 10”)
Effects: reads local data.
Run it? [y]es [n]o [w]hy [a]sk AI [e]dit › a
✦ What should AI change? files only, not directories
→ find . -type f -exec du -h {} + | sort -hr | head -n 10
Run it? [y]es [n]o [w]hy [a]sk AI [e]dit ›Each change is translated again with the original request and current command
as context, and its effects are recalculated before the revised command is
shown. Choose e to edit the current command inline, prefilled with the cursor
at the end. Arrow keys, Home, End, Delete, and Backspace work normally. Enter
approves the edited command for execution. Manual edits stay entirely local and
never call AI. If an edit matches JST's local destructive-command denylist, JST
shows the warning and asks again instead of running silently.
Pressing Escape while entering an AI change or editing the command discards
that draft and returns to the action menu. Empty input, n, and q abort
safely.
Interactive mode asks the model for detailed explanation metadata up front, so
choosing w does not require another request. --interactive and --dry
cannot be combined with --yolo.
If a server does not support structured explanations, JST falls back to the standalone prose explanation.
Use --status to check the configured JST server without calling the model:
$ jst --status
Server: ok
Primary model: provider/primary-model
Fallback model: provider/fallback-model
Calls today: 123
Calls all time: 4567The usage totals are anonymous aggregates and display as unavailable when the server's stats store is disabled or temporarily unreachable.
By default, the CLI sends translation requests to the hosted JST server. The
proxy keeps provider credentials out of the distributed binary and lets JST
change models, prompts, and provider settings without requiring users to
install a new CLI release. The generated command is still checked locally
before execution. The complete proxy source lives in
crates/server; JST is open source end to end.
The hosted server currently applies these safeguards:
- 1,000 translations per anonymous installation in a fixed 30-day window.
- 20 translations per minute per client IP at the Fly proxy.
- 100 translations per client IP and 5,000 globally per fixed 24-hour window.
- A 256-request concurrency cap, 512-byte prompts and revision instructions, 8 KiB request bodies, bounded model outputs, and a five-second timeout for each primary or fallback model attempt.
- Strict OS and shell metadata validation, with provider details hidden from clients and provider outages identified separately from JST server errors.
- Rate-limit response headers for each active quota.
The website’s “try it now” flow uses the real hosted translator through a
separate POST /demo endpoint, then executes the returned command entirely in
the browser with pinned uutils WebAssembly binaries and an in-memory WASI
filesystem. The browser runtime is deliberately narrower than the CLI:
- More than 50 Rust coreutils plus
find,grep,diff, andcmpmay run. File utilities can modify only the disposable in-memory workspace. Simple pipelines and bounded input redirection are supported; output redirects, substitutions, loops, nested shells, process-spawning flags, and other shell control syntax are rejected by the server, page, and sandbox. - Every terminal session gets a small fake filesystem. Its in-memory state
persists between commands so a directory created with
mkdircan be listed afterward. Reset, page exit, timeout, or excessive output destroys it. No host files, processes, environment variables, or credentials are mounted. - The WASI shim exposes no sockets or host syscalls. Each command is stopped after six seconds or 32 KiB of output.
- Output is rendered as text after terminal controls and bidirectional display controls are escaped.
The website stores a random browser UUID locally and sends it only as a soft quota identifier. The hosted service applies independent rolling browser, per-minute, client IP, and global limits. Exact quotas are intentionally not shown in the terminal. IP and global caps remain the spending backstop because a user can clear browser storage and receive a new UUID.
DEMO_ALLOWED_ORIGINS is a comma-separated exact origin allowlist for the
browser endpoint. DEMO_MONTHLY_REQUEST_LIMIT,
DEMO_REQUESTS_PER_MINUTE, DEMO_DAILY_REQUESTS_PER_IP, and
DEMO_GLOBAL_DAILY_REQUEST_LIMIT configure its independent quota namespace.
Each numeric limit accepts 0 to disable it.
The CLI creates a random installation ID in its config directory and sends it
with translation requests. The server stores only a hash of that ID; older
clients fall back to a Fly-provided IP address. When
UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN are set, quota counters
are atomically enforced in the shared Redis store across every server instance.
Without Upstash, the server falls back to per-process in-memory counters that
reset when the process restarts. This is a best-effort spending brake, not
identity: deleting the installation ID bypasses it.
You do not have to use the hosted proxy. The bundled server works with any OpenAI-compatible chat-completions API. For example, using OpenRouter:
LLM_API_URL=https://openrouter.ai/api/v1/chat/completions \
LLM_API_KEY=... \
LLM_MODEL=google/gemma-4-26b-a4b-it \
LLM_FALLBACK_MODEL=microsoft/phi-4 \
cargo run --release -p jst-serverThen point the CLI at it:
JST_API_URL=http://127.0.0.1:8080/translate jst find large filesjst --status derives the sibling /status endpoint from JST_API_URL. Set
JST_STATUS_URL when a custom deployment exposes status at a different URL.
The server listens on PORT (default 8080).
MAX_CONCURRENT_TRANSLATIONS optionally limits simultaneous provider calls.
MONTHLY_REQUEST_LIMIT controls the 30-day quota; set it to 0 to disable
anonymous usage tracking on your own server.
REQUESTS_PER_MINUTE, DAILY_REQUESTS_PER_IP, and
GLOBAL_DAILY_REQUEST_LIMIT control the short-term, daily client-IP, and global
daily limits. Each accepts 0 to disable it. The bundled implementation trusts
Fly's Fly-Client-IP header; self-hosters should only enable IP limits behind a
proxy that overwrites that header rather than accepting it from clients.
Configure UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN to share both
rate-limit counters and aggregate usage statistics across server instances.
The /stats snapshot includes browser-toolbox miss totals and the most common
missing base commands. Prompts, arguments, paths, and complete generated
commands are never retained.
LLM_API_KEY is optional for local APIs that do not require authentication.
LLM_FALLBACK_MODEL optionally selects a model to try when LLM_MODEL fails.
Alternatively, JST_API_URL can point directly to any service implementing
JST's /translate JSON contract.
GitHub Actions runs formatting, build, tests, and Clippy on every pull request
and push to main.
cargo test --workspace
cargo build --workspaceThe site is served from docs/. Its browser sandbox bundle is generated from
site/; the pinned uutils binaries and their checksums are documented in
docs/assets/uutils/ATTRIBUTION.md:
npm ci
npm run build:demo
npm run dev:demoThe production host applies the COOP/COEP headers in docs/_headers as
additional browser isolation, even though this single-threaded WASI runtime
does not require SharedArrayBuffer.
The reusable model benchmark compares command generation, effect classification, parse reliability, and latency directly against an OpenAI-compatible provider without touching hosted JST usage statistics.