A minimal coding agent that runs tools, edits code, and works with any model provider.
Rust · OpenAI-compatible · JSONL sessions · single static binary
⠀⠀⠀⣴⡶⣖⡒⠒⠒⠒⣒⡶⣶⡄⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣄⣀
⠀⠀⡼⠙⡿⣄⡩⠽⠲⠭⣁⡼⣽⠙⣄⠀⠀⠀⠀⢀⣴⣿⠟⠛⠛⢿⣷⡄⠀⠀⠀⠀⢀⠀⠀⠀⣀⣀
⢀⣾⣴⣒⣏⣙⣆⣀⣀⣀⣞⣉⣟⣲⣼⣆⠀⠀⠀⣼⣿⠃⠀⠀⣀⣀⣁⣀⠀⣿⣿⡾⠿⠀⢴⡿⠟⠿⣿⣆⠀⢿⣿⡀⠀⣸⣿⠃
⠀⠻⡢⣄⢹⠀⠈⢦⢀⠞⠀⢰⢇⡤⡺⠃⠀⠀⠀⢻⣿⡄⠀⠀⠛⠛⣻⣿⠀⣿⣿⠀⠀⠀⣠⣴⣶⡶⣿⣿⠀⠈⣿⣧⢰⣿⠏
⠀⠀⠹⡌⢹⣦⡀⢨⢯⠀⣠⢾⠉⡰⠁⠀⠀⠀⠀⠈⠻⣿⣦⣤⣤⣾⡿⠋⠀⣿⣿⠀⠀⠐⣿⣧⣀⣴⣿⣿⠀⠀⠘⣿⣿⡟
⠀⠀⠀⠙⣎⡇⣨⢻⣴⢻⡁⡟⡼⠁⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠀⠀⠀⠉⠉⠀⠀⠀⠈⠉⠉⠁⠉⠉⠀⣀⣀⣿⡿⠁
⠀⠀⠀⠀⠘⣷⣟⣁⣀⣙⣷⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠟⠛⠁
curl -fsSL https://gray.alignment.id/install.sh | sh # stable
curl -fsSL https://gray.alignment.id/install.sh | sh -s -- beta # bleeding edge, rebuilt on every main pushor build from source:
cargo build --release -p graygray # interactive REPL — nothing forced at boot
echo "hi" | gray -p "one-line summary of this repo" # print mode for scripts
gray -c # resume your last sessionFirst run drops you straight at the prompt. Configure whenever you feel like it:
/provider |
pick a provider — free tier, API key, OAuth (xAI / Codex), or local |
/key openrouter |
paste an API key right in the CLI (input hidden), stored per-provider in ~/.gray/auth.json |
/model |
searchable model picker from the bundled models.dev catalog |
Any OpenAI-compatible endpoint works out of the box: OpenRouter, DeepSeek, Groq, OpenAI, ollama, vLLM, LM Studio — plus OAuth sign-in flows for xAI/Grok and Codex/ChatGPT accounts.
/new |
fresh conversation |
/model [id] |
switch or browse models |
/provider |
provider menu: free tier · API key · OAuth · local |
/key [provider] |
add or rotate a key without leaving the chat |
/compact [instructions] |
summarize context (auto-compacts when near limit) |
/usage |
session tokens & cost |
/permissions [mode] |
choose what gray is allowed to do — read-only · auto · full (Shift+Tab cycles auto → read-only → full) |
/feedback <text> |
save feedback locally + open a prefilled GitHub issue |
/context [tokens|auto] |
inspect or set window — e.g. 128k, 1m, auto to clear |
/agentsmd |
edit the system prompt in $EDITOR (show, reset too) |
/acp [agent] [prompt] |
run as an external ACP agent (claude, codex, cursor, opencode…) |
/thinking [level] |
reasoning effort |
/resume [id|--last|--all] |
resume a previous conversation |
/skills |
list skills (/skills:<name> [args] to run one) |
/plugin <subcommand> |
manage plugins (list, search, install, remove, update, enable, disable, check) |
/help, /quit |
you know these |
Slash commands autocomplete: Enter completes and fires, Tab inserts for editing. Suffixes too — e.g. /context r suggests reserve.
gray executes shell commands from the model. The destructive-command guard
(crates/gray-tools/src/bash.rs) blocks obvious foot-guns (rm -rf /, mkfs,
fork bombs, git reset --hard) after an allow-prompt — it is prefix-based and
not a sandbox: pipes, && chains, $(...), eval, xargs rm,
find -delete, python -c 'shutil.rmtree(...)' and curl … | sh pass through.
GRAY_GUARD_BYPASS=1 disables it entirely. There is no container or VM isolation:
run gray in a container/VM for untrusted work.
| subcommand | what it does |
|---|---|
gray resume [--last] [--all] [SESSION_ID] |
resume a previous conversation (picker, most-recent, or by id/prefix) |
gray gateway run|status|install|uninstall|invite|pairing |
messaging gateway daemon — run (foreground), status, install/uninstall (systemd user service, Linux-only), invite (OAuth2 invite URL), pairing approve|list|revoke (bind the owner without editing gateway.yaml) |
gray plugin <list|search|install|remove|update|enable|disable|check> |
manage plugins |
gray update |
update gray to the latest release |
Global flags: -p/--print (one-shot prompt mode), -c/--continue (reopen latest session), --session <ID> (resume by id), --acp <AGENT> (run as an external ACP agent, works with -p), --context-window <TOKENS> (e.g. 128000, 128k), --context-reserve, --context-keep, --dump-manifest (print merged plugin manifest as JSON and exit).
crates/
├── gray REPL · onboarding · config · TUI
├── gray-acp Agent Client Protocol client (external agents)
├── gray-core agent loop · events · messages
├── gray-cron cron scheduling · job store · ticker
├── gray-gateway Telegram/Discord/Slack gateway daemon
├── gray-markdown streaming markdown renderer for the TUI
├── gray-plugin plugin trait · manifest · profile loader
├── gray-provider OpenAI-compatible SSE streaming (+ retries)
├── gray-session JSONL session store with parent-id branching
└── gray-tools read · write · edit · bash · find · grep · ls · cron_tool · plugin loader (gray.yml profiles)
-
Plugin wire spec (methods, TTLs, manifest gates, host-emission audit): docs/protocol-v1.md.
-
Read-tool token bench (before/after,
est_tokens = bytes/4): docs/read-tool-bench.md. -
Make gray yours (skills, plugins, providers, config): docs/customize.md.
-
Streaming first — text deltas, tool calls, and usage arrive as typed events over SSE.
-
Sessions persist to
~/.gray/sessions/*.jsonl;-creopens the latest. -
Ctrl-C means cancel mid-turn (first press) and exit at the prompt; interrupted turns still persist what reached memory.
-
Logs go to
~/.gray/logs/gray.log— setGRAY_LOG=debugfor the firehose.
/acp turns gray into any external coding agent over the Agent Client
Protocol (same mechanism as T3 Code):
bare /acp opens a picker, /acp <agent> <prompt> delegates one-shot,
/acp list shows installed agents, /acp off returns to native.
gray -p 'prompt' --acp opencode works in print mode too.
Known agents (probed via which): codex (npx -y @zed-industries/codex-acp), claude (npx -y @zed-industries/claude-code-acp), opencode (opencode acp), cursor
(cursor-agent acp), gemini (gemini --experimental-acp), copilot
(copilot --acp), grok (grok agent stdio), goose/kimi/kiro.
Custom agents go in ~/.gray/acp.json (Zed agent_servers shape,
mode 0600 like gateway.yaml). Design doc: docs/ACP_PLAN.md.
Safety: permission requests are denied by default (read-only posture);
--yolo or GRAY_ACP_AUTO_APPROVE=1 auto-approves. The external agent's
own permission model applies — gray's bash guard does not run in ACP mode.
Context window resolves as: --context-window / GRAY_CONTEXT_WINDOW > auto-fetched provider value > LiteLLM model table > hardcoded fallback per model. Inspect with /context, set with /context 128k (or 1m, auto to clear).
When usage nears the limit (tokens > window − 16k reserve, pi parity), gray auto-compacts before the next turn by summarizing history into a 2-message summary (same flow as manual /compact). On context_length / max_tokens overflow errors it compacts and retries once. No flag needed — auto is the default; use /compact to force a manual summarization.
| var | meaning |
|---|---|
GRAY_HOME |
config root (default ~/.gray) |
GRAY_API_KEY / OPENAI_API_KEY |
API key (env beats stored keys) |
GRAY_MODEL, GRAY_BASE_URL |
defaults before ~/.gray/config.json is consulted |
GRAY_CONTEXT_WINDOW / --context-window |
override window in tokens — 128000, 128k, 1m, or auto to clear |
GRAY_LOG |
log level: error…trace (default info) |
GRAY_NO_UPDATE_CHECK=1 |
disable the startup update check |
GRAY_AUTO_UPDATE=1 |
background self-update, no prompt |
GRAY_INSTALL_DIR |
installer destination dir (overrides default ~/.local/bin; --system installs system-wide) |
GRAY_GUARD_BYPASS=1 |
disable the destructive-command guard entirely (CI/piped mode) |
GRAY_PERMISSION |
tool permission: ask (prompt before risky commands, default) or auto (no prompts; default in -p print mode) |
GRAY_PARALLEL_READS |
tool parallelism: 0 runs every tool sequentially (default runs read-only tools concurrently, input order preserved) |
GRAY_ACP_AUTO_APPROVE=1 |
auto-approve ACP permission requests (same as /acp <agent> --yolo) |
GRAY_ACP_ALLOW_ANY_PATH=1 |
allow ACP fs/* handlers outside the workspace (default: workspace only) |
GRAY_READ_DEDUP=0 |
disable the read dedup stub (repeat reads always return full content) |
| OS/arch | binary | notes |
|---|---|---|
| Linux x86_64 / aarch64 | musl-static | fully supported |
| macOS arm64 / x86_64 | Rust-static, not notarized | curl-installed binaries run fine; browser downloads may hit Gatekeeper quarantine |
| Windows | via WSL only | native Windows unsupported |
gray gateway install (systemd user service) is Linux-only. Single static binary —
"zero runtime deps" means no sidecar services; you still need sh, curl/wget,
tar, and sha256sum/shasum for the installer.
Stable in 1.x: CLI flags, session JSONL schema, plugin wire v1, ~/.gray layout. Not stable: TUI, internal crate APIs, gray-markdown.
The gateway (gray gateway) exposes gray over Telegram, Discord, and Slack —
meant to run as a daemon on a VPS. Foreground: gray gateway run;
persistent service (Linux-only): gray gateway install (systemd user service).
Config lives in ~/.gray/gateway.yaml, written 0600 (owner-only). Security
model is deny-by-default: nobody talks to the agent unless allowlisted.
Key config keys (under platforms.<telegram|discord|slack> plus top level):
| key | meaning |
|---|---|
platforms.<p>.enabled / .token |
turn the platform on; bot token |
platforms.<p>.allowed_users |
who may talk to the agent ("*" = everyone, only meaningful with dm_policy: open) |
platforms.<p>.dm_policy |
DM admission: pairing (default) or open |
group_per_user |
per-user threads in groups (default on) |
autostart |
auto-start the in-process gateway when gray launches — default off |
denied_tools |
extra tools blocked in gateway sessions (merged with the built-in deny set) |
streaming |
stream replies (default on) |
Pairing flow (no gateway.yaml edit needed): the user DMs the bot, gray prints
a code, the operator runs gray gateway pairing approve <platform> <CODE>;
pairing list shows pending + approved users, pairing revoke drops one.
Ideas and designs informed by the projects listed in THIRD_PARTY_NOTICES.md — thanks to those projects and their authors.
A naming note: cargo install gray belongs to another crate, so the install
path is the installer script above (or a source build); the binary stays gray.
MIT © 2026 vstaln