CLI tool for sending prompts to multiple LLM models via OpenRouter API in parallel.
- Single binary, zero runtime dependencies
- Parallel execution to all enabled models
- Interactive TUI for model selection (
orx init) - JSON with Comments (JSONC) configuration
- Structured JSON output with cost tracking
- Retry with backoff for transient failures
- Verbose mode for HTTP debugging
- Graceful shutdown on SIGINT/SIGTERM
- File content inclusion with structured delimiters
brew install iatsiuk/tap/orxgit clone https://github.com/iatsiuk/orx-cli.git
cd orx-cli
make buildDownload binary from GitHub Releases page.
-
Get an API key from OpenRouter
-
Generate config (interactive TUI):
orx init
# Or use static template without TUI:
orx init --template- Set API key:
export OPENROUTER_API_KEY="sk-or-v1-..."- Run:
echo "Explain TCP vs UDP" | orxorx [flags] < prompt.txt
orx init [--output path] [--template]
orx usage
Flags:
-c, --config string Path to config file (default: ~/.config/orx.json)
-t, --timeout int Global timeout in seconds (default: 600)
--token string OpenRouter API key (default: $OPENROUTER_API_KEY)
-s, --system string System prompt
-p, --prompt-file string Read prompt from file instead of stdin
-f, --file strings File paths to include (can be repeated)
--max-file-size string Max size per file (default: "64KB")
--max-tokens int Max estimated tokens in files (default: 100000)
--verbose Dump HTTP request/response to stderr
--base-url string Override API base URL (e.g. https://openrouter.ai/api/v1)
-h, --help Show help
-v, --version Show version
Init Flags:
-o, --output string Output path (default: ~/.config/orx.json)
--template Generate static template without interactive TUI
Subcommands:
init Interactive model selection and configuration file generation
usage Show API key usage and limits
Re-running orx init when a config already exists pre-selects previously enabled
models in the TUI. After confirming:
- Selected models are written as
"enabled": true - Deselected models are written as
"enabled": false(not removed) - Models from the old config absent from the API are preserved as
"enabled": false - If the existing config fails to load, the TUI starts with no pre-selection
- Existing user-configured parameters (temperature, reasoning, etc.) are preserved
After model selection, if any selected models support reasoning (o1, o3, deepseek-r1, claude with extended thinking, etc.), a second TUI screen appears for setting reasoning effort per model:
+--[ Reasoning Effort ]-----------------------+
| anthropic/claude-4-opus effort: [high] |
| deepseek/deepseek-r1 effort: (skip) |
| openai/o3 effort: [medium] |
+----------------------------------------------+
Space: cycle effort Enter: done Esc: skip all
- Space cycles effort: (skip) -> none -> minimal -> low -> medium -> high -> xhigh -> (skip)
- Enter confirms selections; models with (skip) keep
reasoningin// available:comment - Esc skips reasoning setup for all models (does not cancel init)
- Previously configured effort values are pre-loaded from existing config
# Prompt via stdin
echo "What is recursion?" | orx
# Prompt from file
orx -p question.txt
# Custom config and timeout
orx -c ~/custom.json -t 300 < prompt.txt
# Debug mode
orx --verbose < prompt.txt
# Include files for context
echo "Review this code" | orx -f main.go -f config.go
# With custom limits
orx -f largefile.go --max-file-size 1MB --max-tokens 200000 -p prompt.txtInclude file contents in prompts using -f/--file. Files are wrapped in delimiters for clear boundaries.
echo "Explain this code" | orx -f main.go -f utils.go[FILES]
===== BEGIN FILE =====
path: main.go
----- BEGIN CONTENT -----
<file content>
----- END CONTENT -----
===== END FILE =====
- Binary files are automatically skipped (listed in OMITTED FILES section)
- Oversized files (>
--max-file-size) are skipped - Token limit (
--max-tokens) applies to total file content - Token estimation: ~4 characters per token
Standard JSON config with optional // comments support. Default location: ~/.config/orx.json
| Parameter | Description | Range |
|---|---|---|
temperature |
Controls randomness | 0.0-2.0 |
top_p |
Nucleus sampling | 0.0-1.0 |
top_k |
Limits token choices | 0+ |
min_p |
Minimum probability threshold | 0.0-1.0 |
top_a |
Top-A sampling | 0.0-1.0 |
max_tokens |
Maximum response length | 1+ |
max_completion_tokens |
Maximum completion tokens | 1+ |
frequency_penalty |
Reduces repetition | -2.0 to 2.0 |
presence_penalty |
Encourages new topics | -2.0 to 2.0 |
repetition_penalty |
Penalizes repeated tokens | 0.0-2.0 |
seed |
Deterministic generation seed | 0+ |
stop |
Stop sequence(s) | string or array |
See OpenRouter docs for full parameter list.
For models with reasoning support (o1, o3, deepseek-r1, etc.):
{
"name": "o1",
"model": "openai/o1",
"enabled": true,
"reasoning": {
"enabled": true, // Enable reasoning
"effort": "high", // "none" | "minimal" | "low" | "medium" | "high" | "xhigh"
"max_tokens": 4096, // Max reasoning tokens (mutually exclusive with effort)
"exclude": false, // Hide reasoning from response
"summary": "auto" // "auto" | "concise" | "detailed"
},
"include_reasoning": true
}Control model provider selection per model:
{
"name": "gpt-4o",
"model": "openai/gpt-4o",
"enabled": true,
"provider": {
"order": ["Azure", "OpenAI"], // Preferred provider order
"allow_fallbacks": true, // Allow fallback to other providers
"require_parameters": false, // Only use providers supporting all parameters
"data_collection": "deny" // "allow" | "deny"
}
}JSON output to stdout:
{
"results": [
{
"name": "gpt-4o",
"status": "success",
"content": "Response text...",
"duration_ms": 2345,
"cost": 0.00125
},
{
"name": "claude-sonnet",
"status": "error",
"error": "timeout after 600s",
"duration_ms": 600000
}
],
"total_duration_ms": 600000,
"total_cost": 0.00125,
"successful": 1,
"failed": 1
}Progress output to stderr:
gpt-4o - [requesting]
claude-sonnet - [requesting]
gpt-4o - [done (2.3s), /tmp/orx-a1b2c3d4-...json]
claude-sonnet - [error]
| Code | Meaning |
|---|---|
| 0 | Success (all models succeeded or user cancelled) |
| 1 | Partial failure (at least one succeeded) |
| 2 | All models failed |
| 3 | Configuration or input error |
orx-ralphex-review is a companion script that integrates orx with ralphex as a custom code review backend.
orxon PATHjqfor JSON parsinggitawk(POSIX standard, available on all Unix-like systems)
Installed automatically alongside orx via Homebrew:
brew install iatsiuk/tap/orxOr download from GitHub Releases (included in the archive).
In your ralphex config:
external_review_tool = custom
custom_review_script = orx-ralphex-reviewThe script uses the default orx config (~/.config/orx.json) and OPENROUTER_API_KEY from the environment.
- Reads the prompt file passed by ralphex
- Extracts the
git diffcommand from the prompt - Runs
git diff --no-ext-diff --no-textconvand passes the output to orx - Formats the JSON response as plain text for ralphex to evaluate
=== model-name-1 ===
- file:line - description of issue
=== model-name-2 ===
- file:line - description of issue
MIT
{ "models": [ { "name": "gpt-4o", "model": "openai/gpt-4o", "enabled": true, "temperature": 0.7, "max_tokens": 4096 }, { "name": "claude-sonnet", "model": "anthropic/claude-sonnet-4-20250514", "enabled": true, "max_tokens": 8192 }, { "name": "deepseek-r1", "model": "deepseek/deepseek-r1", "enabled": false, "include_reasoning": true, // Include thinking in response "max_tokens": 8192 } ] }