English | 中文
BE PI OR BETTER THAN PI
Inspired by Claude Code, Codex, OpenCode, and Pi — but built as a Rust-native
| Interface | Preview |
|---|---|
| CLI (TUI) | ![]() |
| Web / Desktop | ![]() |
A Rust-built AI coding agent platform.
AstrCode is a full-stack AI coding assistant with a Rust workspace and a React + TypeScript frontend. It features an agent loop with tool execution, a streaming SSE-based multi-provider LLM layer (Anthropic and OpenAI-compatible providers), typed authoring APIs for bundled and disk IPC subprocess extensions, background pre-warm, health checks, and a startup event channel, a persistent MCP process pool (reusing long-lived connections across turns), built-in web search and URL fetch tools, context window management with auto-compaction, an eval framework for automated benchmarking, and multiple interfaces: a terminal TUI, Web frontend, Tauri desktop app, HTTP/SSE API, and ACP (Agent Client Protocol) adapter.
- Installation
- Configuration (Recommended Before First Run)
- Quick Start
- Architecture
- Crates
- Key Design Decisions
- Running Modes
- Further Reading
- Distribution
- Acknowledgments
- License
npm i @whatevertogo/astrcodeThe @whatevertogo/astrcode npm package provides pre-built binaries for Linux, macOS, and Windows (x64 + arm64). After installation, the astrcode command will be available globally.
Package: @whatevertogo/astrcode
See Quick Start below for building from source.
AstrCode requires LLM provider and API key configuration to function properly. It is recommended to complete the following configuration before the first run.
| File | Path | Purpose |
|---|---|---|
| Main config | ~/.astrcode/config.toml |
LLM providers, models, runtime parameters |
| Project config | <workspace>/.astrcode/config.toml |
Project-level overrides (optional) |
| Global MCP | ~/.astrcode/mcp.json |
MCP server configuration |
| Project MCP | <workspace>/.astrcode/mcp.json |
Project-level MCP configuration (optional) |
Example ~/.astrcode/config.toml:
version = "1"
activeProfile = "deepseek"
activeModel = "deepseek-v4-flash"
activeSmallProfile = "deepseek"
activeSmallModel = "deepseek-v4-flash"
[[profiles]]
name = "deepseek"
providerKind = "deepseek"
baseUrl = "https://api.deepseek.com"
apiKey = "env:DEEPSEEK_API_KEY"
wireFormat = "openai_chat_completions"
authScheme = "bearer"
[[profiles.models]]
id = "deepseek-v4-flash"
maxTokens = 393216
contextLimit = 1000000
modelOptions = { thinking = { enabled = true } }
[[profiles]]
name = "openai"
providerKind = "openai"
baseUrl = "https://api.openai.com/v1"
apiKey = "env:OPENAI_API_KEY"
wireFormat = "openai_responses"
authScheme = "bearer"
[[profiles.models]]
id = "gpt-4.1"
maxTokens = 16384
contextLimit = 128000
[[profiles]]
name = "anthropic"
providerKind = "anthropic"
baseUrl = "https://api.anthropic.com/v1"
wireFormat = "anthropic_messages"
authScheme = "x_api_key"
apiKey = "env:ANTHROPIC_API_KEY"
[[profiles.models]]
id = "claude-sonnet-4-6"
maxTokens = 64000
contextLimit = 1000000API Key Note: Use apiKey = "env:VARIABLE_NAME" to reference environment variables instead of writing keys directly in the configuration file.
Set the corresponding environment variables beforehand:
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."
export DEEPSEEK_API_KEY="sk-..."~/.astrcode/mcp.json registers external MCP tool servers. Both stdio (subprocess) and HTTP transports are supported:
Stdio example:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"],
"env": {}
}
}
}HTTP example:
{
"mcpServers": {
"web-reader": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}Field descriptions:
| Field | Required | Description |
|---|---|---|
command |
Yes (stdio) | Command to start the MCP server |
args |
No | Command line arguments array |
env |
No | Environment variables to pass to the process |
cwd |
No | Working directory (validated to be within workspace in project-level config) |
type |
No | Transport type: "stdio" (default) or "http" |
url |
Yes (http) | MCP server HTTP endpoint |
headers |
No | Custom HTTP headers for the MCP endpoint |
MCP servers start at extension initialization and persist across turns via a long-lived process pool. Global servers (~/.astrcode/mcp.json) are pre-warmed at startup; project-level servers (<workspace>/.astrcode/mcp.json) are pre-warmed in the background when a session is created or resumed. The first turn only blocks if the background pre-warm has not yet completed.
Extensions can be enabled or disabled via ~/.astrcode/config.toml. By default, all extensions are enabled except memory and channels, which are disabled by default.
version = "1"
[runtime.extensionStates]
"astrcode.memory" = true
"astrcode-channels" = trueTo enable the memory extension, set "astrcode.memory": true under runtime.extensionStates.
To enable the channels extension, set "astrcode-channels": true there and configure Telegram under extensions. See the Configuration Guide.
Telegram channels are configured under extensions.astrcode-channels.telegram; keep
allowedChatIds populated unless you explicitly set allowAllChats to true.
[extensions.astrcode-channels.telegram]
enabled = true
botTokenEnv = "TELEGRAM_BOT_TOKEN"
allowedChatIds = ["123456789"]First-party extensions are wired through astrcode-bundled-extensions. Authors of new extensions should depend on astrcode-extension-sdk rather than internal host crates.
| Extension | Crate | Description |
|---|---|---|
| Agent Tools | astrcode-extension-agent-tools |
Sub-agent delegation, agent discovery |
| MCP | astrcode-extension-mcp |
MCP protocol client with persistent process pool, background pre-warm, inflight merge |
| Skill | astrcode-extension-skill |
Slash-command skill discovery and dispatch |
| Todo Tool | astrcode-extension-todo-tool |
Progress tracking todo list tool |
| Mode | astrcode-extension-mode |
Agent running mode switching (Code / Plan), with Exit Gate, plan artifact persistence, keybinding & status item registration |
| Goal | astrcode-extension-goal |
Codex-style session goal tracking, token budgets, and automatic continuation |
| Memory | astrcode-extension-memory |
Project-scoped markdown memory storage (disabled by default) |
| Channels | astrcode-extension-channels |
Telegram channel bridge for using AstrCode from an external chat (disabled by default) |
| Web Tools | astrcode-extension-web-tools |
Built-in web-search and fetch-url tools (DuckDuckGo default; Brave/Serper optional) |
Configure Web Tools under extensions.astrcode-web-tools (enabled by default):
[extensions.astrcode-web-tools.search]
provider = "duckduckgo"
braveApiKeyEnv = "BRAVE_API_KEY"See Configuration Guide for full options.
# 1. Build backend
cargo build
# 2. Create config directory and config file
mkdir -p ~/.astrcode
cat > ~/.astrcode/config.toml << 'EOF'
version = "1"
activeProfile = "openai"
activeModel = "gpt-4o"
activeSmallProfile = "openai"
activeSmallModel = "gpt-4o-mini"
[[profiles]]
name = "openai"
providerKind = "openai"
wireFormat = "openai_chat_completions"
authScheme = "bearer"
baseUrl = "https://api.openai.com/v1"
apiKey = "env:OPENAI_API_KEY"
[[profiles.models]]
id = "gpt-4o"
maxTokens = 128000
contextLimit = 128000
[[profiles.models]]
id = "gpt-4o-mini"
maxTokens = 128000
contextLimit = 128000
EOF
# 3. Set API key environment variable
export OPENAI_API_KEY="your-api-key-here"
# 4. Run interactive terminal UI
cargo run -- tui
# Headless single-shot execution
cargo run -- exec "explain the agent loop architecture"
# HTTP/SSE server
cargo run -- server
# Web frontend (dev server)
cd frontend && npm ci && npm run dev
# Tauri desktop app (dev mode)
cd frontend && npm ci && npm run tauri:dev
# Eval framework (requires dev-mode feature)
cargo run --features dev-mode -- evalAstrCode uses a TOML-based configuration system stored in ~/.astrcode/config.toml. The configuration supports multiple LLM providers, model selection, runtime behavior tuning, and project-level overrides.
Key configuration features:
- Multi-provider support (Anthropic and OpenAI-compatible providers)
- Separate small LLM configuration for extensions (e.g., memory extraction)
- Project-level config overrides via
.astrcode/config.toml - Environment variable substitution for API keys (
env:VAR_NAME) - Runtime behavior tuning (timeouts, retries, compaction, agent limits)
- Compact circuit breaker and optional predictive compact
For detailed configuration documentation, see Configuration Guide.
┌──────────┐ ┌──────────────────────┐ ┌───────────┐
│ TUI │ │ Web / Tauri Frontend │ │ ACP Client│
│ (ratatui)│ │ React 19 + TypeScript │ │ (stdio) │
└────┬─────┘ └────────┬─────────────┘ └─────┬─────┘
│ │ SSE / JSON-RPC │ ACP JSON-RPC
│ stdio │ │ over stdio
└────────┬────────┘──────────────────────┘
┌─────┴──────┐
│astrcode-cli│ TUI / exec / server launcher
└─────┬──────┘
│
┌─────┴──────┐
│astrcode- │ Session management, JSON-RPC + HTTP handler
│server │ ACP adapter, transport, concurrency control
└─────┬──────┘
│
┌─────┴───────┐
│astrcode- │ Agent loop core: turn runner, tool pipeline
│session │ LLM stream, context compaction orchestration
└─────┬───────┘
┌───────────┼───────────┐
│ │ │
┌────────┴───┐ ┌─────┴─────┐ ┌───┴──────────┐
│ astrcode-ai│ │astrcode- │ │ astrcode- │
│ │ │context │ │ extensions │
│ Anthropic │ │Token │ │Host runtime │
│ OpenAI │ │budget and │ │hooks + S5R │
│ compatible │ │compaction │ │capabilities │
│ SSE+retry │ │ │ │ │
└────────────┘ └───────────┘ └──────┬───────┘
│
┌──────────────────┴─────────┐
│ Extension layer │
│ bundled-extensions │
│ astrcode-extension-coding │
│ mode · goal · skill · todo │
│ agent-tools · mcp · memory │
│ channels · web-tools + IPC │
└────────────────────────────┘
┌─────────────────────────────────────┐
│ Shared layer │
│ core · protocol · storage · log │
│ client │
└─────────────────────────────────────┘
The Cargo workspace under crates/ contains 28 crates, plus src-tauri/ as the desktop shell (29 workspace members total). Crates are grouped by architectural layer (details in Architecture).
| Crate | Description |
|---|---|
astrcode-core |
Shared domain types, traits, config system, and prompt composition |
astrcode-session-projection |
Pure durable-event reducer and session read model |
astrcode-protocol |
JSON-RPC 2.0 wire types, commands, events, and HTTP/UI DTOs |
| Crate | Description |
|---|---|
astrcode-ai |
Multi-provider LLM layer (Anthropic and OpenAI-compatible providers), SSE streaming, and retry |
astrcode-storage |
JSONL event log, snapshots, config persistence, and file locking |
astrcode-context |
Token estimation, context window budgeting, auto-compact, and prompt engine |
astrcode-log |
File rotation, stderr output, and env-filter logging |
| Crate | Description |
|---|---|
astrcode-session |
Agent loop: turn runner, tool pipeline, LLM stream, compact orchestration, and runtime services |
| Crate | Description |
|---|---|
astrcode-extension-sdk |
Extension authoring API plus the shared S5R wire, framing, peer, and host-operation contract |
astrcode-extension-worker |
S5R subprocess worker runtime, handler dispatch, and remote typed HostClient |
astrcode-extensions |
Host-side extension lifecycle, hook dispatch, capability gating, and disk IPC loader |
astrcode-bundled-extensions |
Composition root that registers all first-party extension crates |
astrcode-extension-agent-tools |
Sub-agent delegation and agent discovery (Claude Code compatible) |
astrcode-extension-coding |
Eight first-party tools—read, read_tool_result, write, edit, patch, glob, grep, and shell—using only SDK host capabilities |
astrcode-extension-mcp |
MCP client: stdio/HTTP transports, persistent process pool, pre-warm, and health checks |
astrcode-extension-skill |
Slash-command skill discovery and Skill tool dispatch |
astrcode-extension-todo-tool |
Progress-tracking todo list tool |
astrcode-extension-mode |
Code / Plan mode switching, exit gate, plan artifact, keybindings, and status bar |
astrcode-extension-ask-user |
Structured user questions, pending interaction state, and protected replies |
astrcode-extension-goal |
Codex-style session goals, token budgets, and automatic continuation |
astrcode-extension-memory |
Project-scoped markdown memory (disabled by default) |
astrcode-extension-channels |
Telegram channel bridge (disabled by default) |
astrcode-extension-web-tools |
Web search and URL fetch tools with SSRF guards and fetch cache |
| Crate | Description |
|---|---|
astrcode-server |
Session manager, JSON-RPC/HTTP/ACP handlers, transport, HTTP projection, and SSE |
astrcode-client |
Typed JSON-RPC client, transport abstraction, and stream subscription |
| Crate | Description |
|---|---|
astrcode-cli |
CLI entry: TUI (ratatui), headless exec, and server launcher |
src-tauri/ |
Tauri v2 desktop shell: sidecar management, single-instance coordination, and native dialogs |
| Crate | Description |
|---|---|
astrcode-eval |
Benchmark runner: HTTP server control, event-log metrics, and structured reports |
The agent loop (astrcode-session) follows a phased pipeline pattern:
- Prepare context — token budget check, auto-compact if needed
- Build provider request — hook dispatch, message assembly, collect tools (MCP tools served from pre-warmed cache; deferred tools activated via
tool_search_tool) - Stream LLM response — SSE parsing, UTF-8 safe decoding, event accumulation
- Execute tools — parallel batch execution with pre/post hooks, result persistence
- Loop or return — tool calls loop back; text-only responses terminate
The agent supports running mode switching (Code / Plan). Plan mode restricts tools to read-only and plan management, enforces an exit gate (self-review checklist + required heading validation), and persists the plan artifact to <session>/plan/plan.md. Mode instructions are injected via BeforeProviderRequest, preserving the system prompt KV cache.
The ToolPipeline struct owns tool preprocessing, parallel scheduling, and result persistence. The SharedTurnContext struct carries session-level identifiers. consume_llm_stream returns a StreamOutcome enum (Complete | ToolCalls) that makes the loop body read as a linear sequence of named phases.
astrcode-ai supports Anthropic's native Messages API and OpenAI-compatible Chat Completions and Responses APIs. Key components:
Utf8StreamDecoder— handles multi-byte UTF-8 boundaries and bad-byte recovery across TCP chunksSseLineReader— generic SSE line buffering (reusable across all providers)RetryPolicy— exponential backoff with jitter for 429/5xx errors
When conversation history approaches 83.5% of the model's context limit, astrcode-context triggers automatic compaction:
- LLM-backed compaction (model generates a structured 9-section summary) runs by default for both auto and manual compact
- On LLM failure (network error, parse error, timeout), the system falls back to deterministic rule-based summarization
- A compact circuit breaker temporarily skips auto-compact after consecutive LLM failures, with configurable cooldown
- Optional predictive compact estimates turn token growth and compacts before the context window is exceeded
- Compact results are persisted with CAS conflict detection; concurrent writes fail safely instead of corrupting history
- Compact transcripts are persisted as snapshots for debugging
- Post-compact context restoration re-reads recent files and preserves agent/skill/tool state
- Incremental compact — when a summary already exists, new compaction merges new information rather than rewriting from scratch
Tools run in parallel batches (up to 5 concurrent). The pipeline:
- Prepare — parse JSON args (with repair for malformed LLM output), check visibility, dispatch
PreToolUsehooks - Execute — parallel batch via
JoinSet, sequential tools flush the batch first - Commit — dispatch
PostToolUsefor completed executions, persist large results, enforce message budget, and emit distinct completed / failed / cancelled events
Large tool results are automatically persisted to disk and replaced with preview summaries to stay within the message character budget. Each tool declares an ExecutionMode: read-only tools (glob/grep/read) are marked Parallel, writing tools (edit/write/shell) are marked Sequential.
The extension system (astrcode-extensions) is a core architectural pillar, not an afterthought:
- Extension trait — each extension declares hook subscriptions, contributes tools and slash commands, handles lifecycle events
- Extension authoring — bundled extensions use
astrcode-extension-sdk; disk S5R extensions useastrcode-extension-worker; neither couples to host-internal crates - Capability declarations — bundled extensions declare capabilities in
Extension::manifest(); disk IPC extensions declare them duringextension/initialize; the runtime authorizesastrcode.*invokes viaHostRouter - Namespaced session state — session-scoped extension state is stored under
<session>/extension_data/<extension-id>/, keeping the session root owned by the host - Hook modes —
Blocking(can modify input/output),NonBlocking(fire-and-forget),Advisory(observe-only); lifecycle Blocking is limited to turn-entry gates - Keybinding registration — extensions register keyboard shortcuts (e.g.
Shift+Tabfor mode toggle) viaRegistrar::keybinding() - Status bar items — extensions contribute status bar entries (e.g. current mode indicator) with runtime updates via
StatusItemUpdatenotifications - Disk s5r extensions — stdio length-prefixed frames + JSON
WireMessage(protocol.s5r+commandinextension.json); workerInitialize,handler.invoke, and capability-scopedastrcode.*invoke. See docs/extension-system.md - Extension runtime — session spawning with depth limits, tool registration queue, priority-based dispatch
- Lifecycle hooks —
SessionStart/SessionResume/SessionShutdown,TurnStart/TurnEnd/TurnAborted,PreToolUse/PostToolUse,BeforeProviderRequest/AfterProviderResponse,PreCompact/PostCompact,PromptBuild,UserPromptSubmit - Extension runtime APIs —
Extension::start()receives an extension-scopedExtensionStartContextwith config, attributed paths, typed host clients, events, managed tasks, and cancellation; session-only operations remain unavailable until a session-scoped handler call.Extension::stop()receivesStopReason;health()andon_config_changed()support health probes and hot config reloads - Active health checks —
ExtensionRunner::check_health()provides an on-demand sampling API; polling strategy is decided by the host - Startup event channel —
bind_startup_event_channel()binds a process-level event channel so extensions can emit custom events duringstart()
The ACP adapter (astrcode-server::acp) bridges the standard Agent Client Protocol to astrcode's internal command/broadcast architecture:
- Stdio JSON-RPC server implementing Initialize / NewSession / Prompt / Cancel
- Real-time event streaming via broadcast channel to ACP
SessionNotification - Deterministic event flushing with completion oneshot for turn lifecycle
- Designed for IDE extensions and editor integrations
AstrCode follows a session-first event-sourcing pattern:
- EventLog is the single source of truth — all state changes are immutable, append-only events
- Session is a projection — reconstructed by replaying from the event log; fork = replay from a specific sequence number
- Agent is stateless —
TurnRunneris discarded after each turn; state lives in the event log - Recovery is replay — if the agent crashes, the session is intact; simply re-project from the event log
System prompt assembly follows a pipeline pattern:
Identity → System → Task Guidelines → Communication → Environment
→ User Rules → Project Rules → Tool Summary → Extension → Additional
Stable sections (Identity, System, Task Guidelines) come first to leverage prompt cache prefix matching. Users can customize via ~/.astrcode/IDENTITY.md (identity override) and project-level AGENTS.md (project rules, searched upward from working directory).
| Mode | Command | Description |
|---|---|---|
| TUI | cargo run -- tui |
Interactive terminal UI with message history, tool display, slash commands, status bar |
| Exec | cargo run -- exec "prompt" |
Headless single-shot execution, supports --jsonl |
| Server | cargo run -- server [--addr 0.0.0.0:3847] |
HTTP/SSE server with JSON-RPC, session management, real-time event streaming |
| ACP | cargo run -- acp |
ACP stdio adapter for IDE/editor integration |
| Eval | cargo run --features dev-mode -- eval |
Run evaluation benchmarks (requires dev-mode feature) |
| Web | cd frontend && npm run dev |
Browser-based chat interface connected to the server via SSE |
| Desktop | cd frontend && npm run tauri:dev |
Tauri desktop app (auto-launches server as sidecar) |
Keyboard Shortcuts:
| Key | Action |
|---|---|
Enter |
Submit prompt / accept slash command selection |
Shift+Enter / Alt+Enter |
Insert newline |
Esc |
Close slash palette / stop streaming turn |
Tab |
Complete slash command selection |
Shift+Tab |
Trigger extension-registered keybinding |
Ctrl+A / Ctrl+E |
Move to start / end of line |
Ctrl+U / Ctrl+K |
Delete before / after cursor |
Ctrl+W |
Delete previous word |
Ctrl+C |
Quit (with confirmation) |
Slash Commands:
| Command | Description |
|---|---|
/new |
Create a fresh session |
/resume <id> or /r <id> |
Resume a previous session |
/sessions or /ls |
Open session picker |
/compact |
Compact the current session context |
/help or /? |
Show command help |
/quit or /q |
Exit astrcode |
Extensions can register additional slash commands and keybindings at runtime.
| Document | Description |
|---|---|
| Architecture | Event-sourcing, server layers, compact, prompt pipeline, tools, extensions |
| Configuration Guide | Full config.toml reference |
| Extension System | Built-in vs disk IPC extensions, host capabilities |
| Extension Author Guide | Disk s5r extension development guide |
| UI Render Spec | Structured rendering protocol for tool results |
| Release Guide | Version sync, release workflows, and npm/GitHub distribution |
| TODO | Project roadmap and pending items |
Pre-built binaries are available for Linux, macOS, and Windows (x86_64 + aarch64) via GitHub Releases on every version tag. Manual releases should use the Release workflow so version metadata, tags, npm packages, and GitHub assets stay in sync. The weekly workflow publishes a patch release only when commits landed since the previous version.
NPM Package: @whatevertogo/astrcode
See Release Guide for the release checklist.
This project drew inspiration and design patterns from several open-source projects:
- Claude Code — tool execution pipeline, system prompt design, compact design
- OpenCode — the frontend-backend separation (HTTP/SSE + JSON-RPC) references OpenCode's architecture.
- Codex CLI — TUI layout and terminal UI design borrow from Codex's approach to rendering agent interactions in the terminal.
AGPL-3.0

