stynx-code
An interactive AI coding assistant for the terminal. Multi-provider, tool-using, fast.
____ _____ __ __ _ _ __ __
/ ___| |_ _|\ \ / /| \ | |\ \/ /
\___ \ | | \ V / | \| | \ /
___) | | | | | | |\ | / \
|____/ |_| |_| |_| \_|/_/\_\
c o d e
The product is stynx-code. The command you'll actually type is stynx.
What it is
A self-contained terminal app for getting work done with an LLM. Speaks Anthropic's Claude API natively and any OpenAI-compatible endpoint (DeepSeek, OpenAI, OpenRouter, Together, local Ollama, …) for cheap delegation. Runs an autonomous tool-using loop with bash / file edit / glob / grep / web fetch behind a permission system that prompts you only when you ask it to.
The engine runs concurrent-safe tools in parallel via tokio::spawn, sequential otherwise. Context is compacted automatically at 60% token threshold through a 4-stage pipeline (Auto → Micro → Session Memory → Full). File mutations are tracked on an undo stack. Provider overload triggers exponential backoff with up to 3 retries.
Highlights:
- Modern terminal UI — sidebar, command palette, model picker, session list, runtime theme switcher (rose-pine, catppuccin, tokyo-night, gruvbox), mouse + bracketed paste,
@-file mentions, multi-line input, slash-command popover with descriptions, toast notifications, colorized diff renderer for file edits. - Intern mode — Claude as the senior, one or more cheaper OpenAI-compat models (DeepSeek, OpenRouter, OpenAI, custom) as interns. Each intern shows up as its own
delegate_to_<name>tool the senior can pick from, or call directly via/intern <name> <task>. - Permission-first — Normal / Auto-accept / Plan / Bypass modes, per-tool allow rules, in-TUI confirmation modal (no terminal mode-switching).
- Skills as slash commands — drop a markdown file under
.claude/skills/and it appears as/your-skill. - Sessions — automatic per-project persistence at
~/.stynx-code/projects/<slug>/session.json. - Hooks —
session-start,pre-tool-use,post-tool-use,stopshell hooks for integrations.
Install
# from source
# with nix
The installed binary is stynx.
Quickstart
# interactive TUI (the main mode)
# one-shot prompt
# pipe mode — read stdin as the prompt
|
# JSON output for scripting
Configuration
Anthropic credentials
Resolved in order:
- macOS Keychain / Linux libsecret (the Claude Code OAuth token at
Claude Code-credentials) ~/.claude/.credentials.json~/.claude/settings.json→auth_tokenANTHROPIC_API_KEYenv var
Run /login inside stynx for instructions.
Interns (OpenAI-compatible providers)
You can configure any number of interns concurrently — DeepSeek and OpenRouter together, mixed providers, whatever you want. Each becomes its own delegate_to_<name> tool the senior model can pick from.
Option 1 — .env shorthand (zero settings.json edits):
# DeepSeek (legacy single-intern shortcut)
DEEPSEEK_API_KEY=sk-...
DEEPSEEK_MODEL=deepseek-chat # optional
# OpenRouter — declare multiple interns via name:model pairs
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_INTERNS=qwen-coder:qwen/qwen3-coder,haiku:anthropic/claude-haiku-4.5,kimi:moonshotai/kimi-k2,gemini-flash:google/gemini-2.5-flash,mimo:mimo-v2.5-pro
# Qwen / Alibaba DashScope (auto-registers a "qwen" intern; multi via QWEN_INTERNS)
QWEN_API_KEY=sk-...
QWEN_MODEL=qwen-plus # optional; qwen-max / qwen-turbo / qwen3-coder-plus
# QWEN_INTERNS=qwen-max:qwen-max,qwen-coder:qwen3-coder-plus
Option 2 — interns array in settings.json (full control):
provider shorthand resolves to:
| provider | base_url | default api key env |
|---|---|---|
deepseek |
https://api.deepseek.com/v1 |
DEEPSEEK_API_KEY |
openrouter |
https://openrouter.ai/api/v1 |
OPENROUTER_API_KEY |
openai |
https://api.openai.com/v1 |
OPENAI_API_KEY |
qwen |
https://dashscope-intl.aliyuncs.com/compatible-mode/v1 |
QWEN_API_KEY |
custom |
(set via base_url) |
(set via api_key_env) |
Copy .env.example to .env and fill in your API keys. Add .env to your .gitignore — stynx autoloads it on startup.
At launch you'll see one · intern ready: <name> (<provider> / <model>) line per intern that successfully resolved. Interns missing their API key are silently skipped (a WARN is emitted to logs).
Settings file
~/.claude/settings.json (global) and project-local settings get merged. Schema:
commit_attribution: false (the default) tells the assistant to omit AI/assistant attribution from commits — no Co-Authored-By: trailers, no 🤖 Generated with …. Set it to true if you want attribution back.
Run /config inside the session to see the merged view.
Daily flow
| Key | Action |
|---|---|
Enter |
Submit message |
Shift+Enter |
Newline (also Alt+Enter, Ctrl+J) |
Esc |
Interrupt streaming · or vim normal mode |
Ctrl+P |
Command palette |
Ctrl+S |
Session list |
Ctrl+M |
Switch model |
Ctrl+B |
Toggle sidebar |
Ctrl+T |
Toggle tool block details |
Shift+Tab |
Cycle permission mode |
Shift+↑/↓ |
Scroll messages by line (works while typing) |
PgUp/PgDn |
Scroll by page |
Ctrl+Alt+U/D |
Scroll messages by half page |
@ |
File-mention picker |
/ |
Slash-command popover |
Ctrl+C |
Quit |
Mouse scroll wheel works in any terminal with mouse capture.
Slash commands
/help show this help
/status git status
/version show stynx version
/quit, /exit exit
/model [name] switch model
/fast toggle fast (haiku) mode
/effort low|medium|high|max set effort
/think toggle extended thinking
/mode cycle permission mode
/plan [task] toggle plan mode (read-only tools only)
/compact summarize older turns to free up context
/cost show token usage and cost
/usage show plan limits (OAuth only)
/diff show git diff
/review review the current diff
/commit generate a commit message
/init create/update CLAUDE.md
/memory show CLAUDE.md
/add <path> pin a file into every message
/files list pinned files
/skills list available skills
/intern <task> hand work to the first intern
/intern <name> <task> hand work to a specific intern by name
/session list / load sessions
/rewind [n] remove last n exchanges
/undo [n] restore last n file edits
/export write transcript to disk
/copy copy last response to clipboard
/config show merged config
/permissions show allow / deny rules
/login, /logout credential management
!<command> run a shell command without leaving the TUI
Intern mode in 30 seconds
# .env — quick mix of providers
DEEPSEEK_API_KEY=sk-...
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_INTERNS=qwen-coder:qwen/qwen3-coder,haiku:anthropic/claude-haiku-4.5,kimi:moonshotai/kimi-k2,gemini-flash:google/gemini-2.5-flash,mimo:mimo-v2.5-pro
> refactor every println! in src/foo.rs to tracing::info!; hand it to the qwen-coder intern
The senior (Claude) calls delegate_to_qwen_coder (or any other registered intern) with a focused task description. The intern runs with a restricted toolset — bash, read, file_write, file_edit, glob, grep — and cannot spawn further sub-agents. It returns Summary / Files changed / Output. The senior reviews and integrates.
Direct invocation:
/intern # show available interns
/intern list every public function and one-line them # picks the first intern
/intern qwen-coder write a unit test for util::strip_ansi
When multiple interns are configured, the senior picks based on each tool's description — so write descriptions that say what each intern is good at (speed, cost, specialty). The intern's transcript is shown as a system message in the conversation, including any tool calls it made.
Terminal sessions
bash is a persistent shell, not a series of disposable subshells. The first call boots a long-lived bash --norc --noprofile process; every subsequent call runs through the same shell, so cd, export, sourced files, and function definitions survive across calls.
For long-running processes (dev servers, watchers, log tails), pass background: true:
// start a dev server
{"command": "bun run dev", "background": true} // → "started background process 'bg1'"
// check on it (only new output since last read)
{"status": "bg1"} // → "[running, 12s]\n..."
// read everything it has emitted
{"status": "bg1", "full": true}
// see all background processes
{"list": true}
// stop it
{"kill": "bg1"}
Foreground commands have a 120s default timeout (override with "timeout": <secs>). If you need longer, use background: true instead — a timed-out foreground command leaves the persistent shell in an unknown state.
Hooks
Each hook command receives the relevant JSON on stdin and writes text to stdout (mixed into the conversation as a system message). Configure under hooks in settings.json:
SessionStart— runs once when a session beginsPreToolUse— runs before each tool invocation; can short-circuit by exiting non-zeroPostToolUse— runs after each tool invocation; can post-process the resultStop— runs when the assistant finishes a turn
Each can have an optional matcher (substring against tool name) and a required command.
Available Tools
The engine provides a rich set of tools categorized as follows:
- File I/O:
read,file_write,file_edit - Discovery:
glob,grep - Shell:
bash(persistent session) - Web:
web_fetch,web_search - Tasks:
todo_read,todo_write - Background tasks:
task_create,task_get,task_list,task_stop,task_update,task_output - Scheduling:
cron_create,cron_delete - Interaction:
ask_user_question - Integration: MCP (dynamic tools loaded per server),
lsp - Skills:
skillinvocation - Misc:
notebook_edit,repl,send_message,sleep,synthetic_output,plan_mode
How it works
Engine loop
The QueryEngine runs up to N turns (default 200, configurable via max_turns). Each turn:
- Sends the conversation to the provider (with read-only tools only in plan mode)
- Streams the response and extracts tool calls
- Runs pre-tool hooks for each tool
- Executes concurrent-safe tools in parallel (via tokio::spawn); non-concurrent tools sequentially
- Runs post-tool hooks and collects results
- Adds results back to conversation
- Repeats if the model calls tools, or returns if the model stopped
At 60% token threshold, the engine automatically invokes the compactor to free up context.
Context compaction (4-stage pipeline)
When tokens exceed 60% of the limit:
- Auto — checks whether compaction is needed based on the token threshold
- Micro — truncates oversized individual tool results in-place
- Session Memory — extracts key memories before discarding content
- Full — sends older turns to the LLM for summarization, keeping the last 1–2 turns intact
The compactor preserves key decisions and context needed to continue.
File edit undo stack
Every file write/edit is tracked on an undo stack. Use /undo [n] to restore the last n file mutations.
Architecture
The project is structured as a 19-crate Rust workspace, each following Clean Architecture principles with domain/, application/, and infrastructure/ layers.
stynx-code-errors: Defines common application error types and results.stynx-code-types: Provides core traits and structs for tools, providers, messages, and permissions.stynx-code-tools: Implements over 50 tools, including file operations, shell commands, and task management.stynx-code-provider: Handles integrations with Anthropic SSE streaming and OpenAI-compatible providers.stynx-code-engine: The core query engine, managing the multi-turn agentic loop, tool execution, context compaction, hooks, and undo stack.stynx-code-server: Provides an Axum HTTP API for headless operation.stynx-code: The main executable, handling CLI parsing and orchestrating the TUI and agent/intern interactions.stynx-code-auth: Manages OAuth PKCE and API key credential resolution.stynx-code-permission: Implements interactive and configuration-driven permission gating for tools.stynx-code-commands: Handles slash-command expansion within the TUI.stynx-code-memory: Manages per-project session persistence.stynx-code-config: Loads and merges global and project-specific settings, including hook configurations.stynx-code-services: Provides analytics, an LSP bridge, rate limiting, token estimation, diagnostics, and notifications.stynx-code-compact: Implements the 4-stage conversation summarization pipeline.stynx-code-coordinator: Facilitates multi-agent communication and task management via a message bus.stynx-code-bridge: Handles inter-crate communication.stynx-code-plugins: Manages the lifecycle of plugins.stynx-code-skills: Loads both bundled and user-defined skills.stynx-code-tui: Implements theratatuiterminal user interface, including state management and rendering.
License
MIT