https://github.com/coder11125/io
io – AI coding agent for the terminal. Built in Rust
https://github.com/coder11125/io
anthropic gemini groq mistral openai rust serde tokio
Last synced: 2 days ago
JSON representation
io – AI coding agent for the terminal. Built in Rust
- Host: GitHub
- URL: https://github.com/coder11125/io
- Owner: coder11125
- License: mit
- Created: 2026-06-09T14:31:18.000Z (12 days ago)
- Default Branch: main
- Last Pushed: 2026-06-18T14:32:52.000Z (3 days ago)
- Last Synced: 2026-06-18T15:29:20.699Z (3 days ago)
- Topics: anthropic, gemini, groq, mistral, openai, rust, serde, tokio
- Language: Rust
- Homepage:
- Size: 2.33 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
io
AI coding agent for the terminal
Built in Rust — powered by 13 LLM providers
io is an AI coding assistant that runs directly in your terminal.
It reads, writes, edits, and understands code — with support for 13 LLM providers,
interactive sessions, tool execution, and permission sandboxing.
---
## Features
- **Multi-provider** — 13 LLM providers supported (Anthropic, OpenAI, Gemini, Groq, DeepSeek, Mistral, Ollama, Azure, Bedrock, OpenRouter, xAI, OpenCode Go, OpenCode Zen)
- **7 built-in tools** — `read`, `write`, `edit`, `bash`, `glob`, `grep`, `spawn_agent` for full codebase interaction
- **Cost tracking** — Built-in API cost calculation with `/cost` command for supported providers
- **Interactive & single-shot modes** — Full-screen REPL with splash screen, prompt bar, and scrollback
- **Color themes** — 12 built-in themes (dark/light) with live `/theme` switcher
- **Permission sandbox** — Allow/deny/prompt modes with granular bash command control and security-hardened command validation
- **Session persistence** — SQLite-backed conversation history, resume anytime
- **Streaming responses** — Real-time token-by-token streaming with live indicator
- **Fast & lightweight** — Built in Rust, minimal dependencies, no Node.js or Python required
- **Provider switching** — Change providers on the fly with `/connect` and `/model`
- **Project-level config** — Per-project `.io/config.toml` initialization with `io init`
- **Interactive picker** — Arrow-key menus for agents, providers, models, and themes
- **Agent cycling** — Tab key at empty prompt to swap agents mid-session
- **@file mentions** — Type `@path/to/file` to inline file or directory contents
- **Project context** — Reads `AGENTS.md` / `CLAUDE.md` from the project root and injects them as context so agents understand your conventions automatically
- **Smart write permissions** — Most agents (build, debug, docs, refactor, …) auto-approve `write`/`edit` calls; plan, git, review, security, and test always prompt
## Quick Start
```bash
# Install
cargo install --path .
# Start interactive mode
io
# Or run a single command
io "explain this codebase"
```
On first run, `io` creates a default configuration at `~/.io/config.toml`.
Use `/connect` inside the REPL to set up your preferred LLM provider.
## Security
The permission sandbox system provides defense-in-depth protection against command injection and unauthorized execution:
**Permission Modes**:
- `allow` — Auto-approve all tool executions
- `deny` — Block all tool executions
- `prompt` — Ask user for approval (default)
**Security Features**:
- **Semantic command analyzer**: Classifies every bash command as `Safe`, `Caution`, or `Destructive` before execution — `ls`, `git status`, `cargo check` are auto-allowed; `rm -rf`, `dd`, `git reset --hard` always prompt
- **Granular bash approvals**: "Always" approvals are command-specific (approving `ls -la` won't approve `rm -rf`)
- **Command normalization**: Handles path obfuscation (`/bin/rm` → `rm`, `r\m` → `rm`)
- **Injection prevention**: Detects command substitution (`$(rm)`, `` `rm` ``), chaining (`;`, `&`, `|`), and subshells
- **Conservative denylist**: Any dangerous token denies the entire command
- **Strict allowlist**: Requires all pipeline heads to be explicitly allowed
- **Expansion guard**: Commands containing `$`, backtick, or `(` bypass auto-allow (static analysis can't classify them safely)
**Command Safety Classifications**:
| Level | Examples | Behavior |
|---|---|---|
| `Safe` | `ls`, `cat`, `grep`, `git status`, `cargo check`, `find` (no `-delete`) | Auto-allowed in prompt mode |
| `Caution` | `mv`, `git commit`, `rm file.txt`, `sed -i`, `chmod` | Always prompts |
| `Destructive` | `rm -rf`, `dd`, `git reset --hard`, `git push --force`, `mkfs.*` | Always prompts with implied risk |
**Ecosystem-agnostic build tool classification** — subcommands are classified consistently across all package managers:
| Subcommand | Ecosystems | Level |
|---|---|---|
| `test`, `build`, `check`, `lint`, `fmt`, `doc`, `bench`, `audit` | npm/yarn/pnpm/bun, cargo, go, pip, mvn/gradle, dotnet, … | `Safe` — auto-allowed |
| `list`, `show`, `freeze`, `outdated`, `tree`, `info` | pip, npm, cargo, go mod | `Safe` — auto-allowed |
| `install`, `add`, `update`, `upgrade`, `remove` | all | `Caution` — prompts |
| `publish`, `deploy`, `release` | npm, cargo, mvn | `Caution` — prompts |
| `run ` | npm/yarn/pnpm/bun | Safe or Caution based on script name |
| `go mod download/verify/graph/why` | go | `Safe` — read-only module ops |
| `go mod tidy/edit` | go | `Caution` — rewrites go.sum |
`make`/`cmake`/`ninja` targets are always `Caution` — Makefile rules are opaque to static analysis.
**Defended Attack Vectors**:
- Path obfuscation, command substitution, environment injection
- Command chaining with `;`, `&`, `|`, `&&`, `||`
- Subshells, brace groups, HEREDOC, process substitution
## Modes
### Interactive REPL
```bash
io
```
Launches an interactive session with a streaming agent loop. Commands:
| Command | Description |
|---|---|---|
| `/help` | Show available commands |
| `/new` | Start a new session |
| `/agent` | Switch agent mode (build, plan, debug, refactor) |
| `/connect` | Set up a provider interactively (with live model fetching) |
| `/model` | Switch between configured providers |
| `/theme` | Switch UI color theme (12 themes) |
| `/cost` | Show API cost summary for the current session |
| `/compact` | Summarize and compress conversation history |
| `/exit`, `/quit`, `/q` | Exit the session |
| `!<cmd>` | Run a shell command directly |
Tab key at the empty prompt cycles through available full agents.
Type `@path/to/file` to expand file or directory contents inline.
The TUI features a splash screen with centered logo and command reference,
a fixed prompt bar at the bottom showing agent/model/provider and context usage,
mouse scrollback through session history, and streaming tool call visualization
with syntax-colored diffs. Long splash input is automatically truncated with `…`
so it never overflows the input box border. Thought blocks are rendered in the
active theme's accent and muted colors.
### Single-shot
```bash
io "summarize the changes in src/"
```
Runs one turn and prints the response, then exits.
### Flags
```bash
io --new # Start a fresh session (ignore history)
io --continue # Resume the last session
io --model anthropic # Override the default provider
```
## Supported Providers
| Provider | Config key | Default Model |
|---|---|---|
| **Anthropic** | `anthropic` | `claude-sonnet-4-20250514` |
| **OpenAI** | `openai` | `gpt-4o` |
| **Google Gemini** | `gemini` | `gemini-2.5-pro` |
| **Groq** | `groq` | `llama-3.3-70b-versatile` |
| **Ollama** | `ollama` | `llama3.2` |
| **Azure OpenAI** | `azure` | `gpt-4o` |
| **AWS Bedrock** | `bedrock` | `anthropic.claude-3-5-sonnet-20241022-v2:0` |
| **Mistral AI** | `mistral` | `mistral-large-latest` |
| **DeepSeek** | `deepseek` | `deepseek-chat` |
| **OpenRouter** | `openrouter` | `anthropic/claude-sonnet-4` |
| **xAI (Grok)** | `xai` | `grok-3-beta` |
| **OpenCode Go** | `opencode_go` | `deepseek-v3` |
| **OpenCode Zen** | `opencode_zen` | `opencode/claude-sonnet-4` |
## Configuration
Configuration is stored in `~/.io/config.toml` (global) and optionally `.io/config.toml` (per-project).
```toml
[provider]
default = "anthropic"
[provider.anthropic]
model = "claude-sonnet-4-20250514"
base_url = "https://api.anthropic.com/v1"
api_key_env = "ANTHROPIC_API_KEY"
[session]
auto_compact = true
memory_enabled = true
max_turns = 100
[permissions]
default = "prompt" # "allow" | "deny" | "prompt"
allowed_commands = []
denied_commands = ["rm", "sudo"]
# Optional: theme = "ocean" (default, ocean, rose, forest, sunset, mono, breeze, ink, dawn, sand, mint, dusk)
```
### API Keys
Keys are stored in `~/.io/keys.toml` (with `chmod 600` on Unix). You can also use environment variables:
```toml
# ~/.io/keys.toml
anthropic = "sk-ant-..."
openai = "sk-proj-..."
```
Or set `api_key_env` in `config.toml` to reference an environment variable.
### Commands
```bash
# View current config
io config show
# Modify config
io config set provider.default anthropic
io config set session.auto_compact true
io config set permissions.default allow
# Initialize io in the current project
io init
```
### Session Management
```bash
# List sessions
io session list
# Show session details
io session show <id>
# Delete a session
io session delete <id>
```
## Architecture
```
io/
├── io/ # CLI frontend (binary crate)
│ ├── Cargo.toml
│ └── src/
│ ├── main.rs # Entry point, CLI parsing, subcommand dispatch
│ ├── tui.rs # REPL orchestration: agent construction, slash command dispatch
│ ├── input.rs # Readline loops (REPL + splash), popup helpers, @file resolution
│ ├── stream.rs # Streaming event processing: ThinkParser, blink_and_print
│ ├── cost.rs # /cost report
│ ├── config_cmd.rs # `io config …` and `io init` handlers
│ ├── agent.rs # Agent switching (/agent command)
│ ├── connect.rs # Interactive provider setup wizard (13 providers)
│ └── model.rs # Provider switching (/model command)
│
├── io-tui/ # Terminal UI components (library crate)
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs # Re-exports: picker, readline, render, theme
│ ├── render.rs # Terminal rendering: markdown, diffs, splash, prompt bar, scrollback
│ ├── picker.rs # Terminal interactive picker (arrow keys, viewport)
│ ├── readline.rs # Custom readline with slash commands
│ └── theme.rs # Interactive theme picker
│
├── io-runtime/ # Core engine (library crate)
│ ├── Cargo.toml
│ ├── tests/
│ │ └── agent_loop.rs # 11 integration tests against a scripted mock provider
│ └── src/
│ ├── lib.rs # Crate root — re-exports public API + load_project_context()
│ ├── agent.rs # Agent loop: LLM completion + tool execution (sync & streaming)
│ ├── compact.rs # /compact + auto-compact summarization
│ ├── config.rs # Config/schema (TOML), KeyStore, provider config structs
│ ├── types.rs # Core data types — Session, Turn, ToolCallRecord, TurnUsage
│ ├── memory.rs # SQLite-backed session persistence (CRUD)
│ ├── sandbox.rs # Permission checker (allow/deny/prompt modes)
│ ├── command_safety.rs # Semantic command classifier (Safe/Caution/Destructive)
│ ├── pricing.rs # Per-token cost calculation for supported providers
│ ├── tools/ # Built-in tools (7 tools, each with unit tests)
│ │ ├── mod.rs # Tool trait, ToolRegistry, default_registry()
│ │ ├── read.rs # Read files with offset/limit
│ │ ├── write.rs # Write/create files, returns unified diff
│ │ ├── edit.rs # Replace first occurrence of text, returns unified diff
│ │ ├── bash.rs # Execute shell commands with timeout & workdir
│ │ ├── glob.rs # Find files by glob pattern (sorted by mtime)
│ │ ├── grep.rs # Search file contents with regex
│ │ └── spawn.rs # spawn_agent — delegate to a restricted sub-agent
│ │
│ └── provider/ # LLM provider implementations
│ ├── mod.rs # ProviderKind enum, CompletionModel trait, create_provider()
│ ├── anthropic.rs # Anthropic Claude
│ ├── openai.rs # OpenAI + 8 compat providers via OpenAICompatProvider
│ ├── gemini.rs # Google Gemini
│ ├── azure.rs # Azure OpenAI
│ └── bedrock.rs # AWS Bedrock
│
├── io-agents/ # Built-in agent definitions (library crate)
│ └── src/
│ ├── agent_config.rs # AgentConfig + ToolAccess + auto_allow_writes
│ └── builtin/ # build, plan, debug, explore, review, test,
│ # security, docs, git, refactor, general
│
├── .github/
│ └── workflows/
│ └── ci.yml # CI: fmt, clippy, build, test on push/PR
├── assets/
│ └── io.png # Logo
├── Cargo.toml # Workspace root (resolver = "3")
└── README.md
```
### How It Works
1. **Input** — User types a prompt (interactive or single-shot)
2. **Project context** — On startup, `load_project_context()` reads `AGENTS.md` / `CLAUDE.md` from the project root and passes the content to the agent; it is injected as a synthetic user/assistant message pair so the model always has project conventions in context
3. **Agent loop** — The agent iterates up to 20 turns: sends conversation history + system prompt + project context + tool specs to the LLM
4. **Tool execution** — If the LLM requests a tool call, the agent executes it via `ToolRegistry`, checks permissions via `PermissionChecker`, and feeds results back to the model
5. **Streaming** — In interactive mode, text deltas stream to the terminal as they arrive; tool starts and completions are rendered inline (with syntax-colored diffs for `write`/`edit`)
6. **Persistence** — Each turn (with tool call records and token usage) is saved to SQLite for session resumption
### Message Flow
```
User Input
│
▼
Agent.run_turn() / run_turn_streaming()
│
├── Build Messages (system prompt + history + new input)
│
├── CompletionModel.complete() / complete_stream()
│ │
│ ├── Text blocks → stream to user / accumulate
│ └── ToolUse blocks → execute tools
│ │
│ ├── PermissionChecker.check_tool()
│ ├── ToolRegistry.get().execute()
│ └── Feed ToolResult back to model
│
└── Save Turn to SQLite (SessionStore)
```
## Built-in Tools
| Tool | Description |
|---|---|
| **`read`** | Read files with optional `offset` (line number) and `limit` for partial reading |
| **`write`** | Write or create a file. Returns a unified diff of what changed |
| **`edit`** | Replace the first occurrence of `old_string` with `new_string` in a file. Returns a unified diff |
| **`bash`** | Execute shell commands with configurable `timeout` (ms) and `workdir` |
| **`glob`** | Find files by glob pattern, sorted by modification time (newest first) |
| **`grep`** | Search file contents with regex, optional file type filtering via `include` glob |
All tools implement the `Tool` trait with a JSON input schema exposed to the LLM, allowing autonomous discovery and use of parameters.
## Development
```bash
# Build
cargo build
# Run in development
cargo run -- "your prompt"
cargo run --
# Run tests (149 unit tests + 9 integration tests)
cargo test
# Add a new provider
# 1. Add a config struct in io-runtime/src/config.rs
# 2. Create a provider module in io-runtime/src/provider/
# 3. Add it to ProviderKind enum and create_provider() in provider/mod.rs
# 4. Add it to PROVIDERS array and match block in connect.rs
```
### Adding a New Tool
1. Create `io-runtime/src/tools/<name>.rs` implementing the `Tool` trait
2. Register it in `tools/mod.rs` (add module + register in `default_registry()`)
3. The LLM will discover it automatically via tool specs
## License
MIT