An AI-powered coding assistant built with the Eino framework and a TUI interface.
This project extracts and ports the core concepts from Zed Editor's native agent system:
- Native Agent: AI coding assistant with ReAct loop (via Eino ADK)
- Model Configuration: Multiple LLM provider support (OpenAI, Anthropic, Ollama)
- ACP Protocol: Agent Communication Protocol for session management
- Coding Tools: File operations, terminal commands, search, and more
- TUI Interface: Interactive terminal user interface with a fixed bottom input line and scrollable conversation area
sword-agent/
├── cmd/sword-agent/ # CLI entry point
│ └── main.go
├── internal/
│ ├── acp/ # ACP Protocol (Agent Communication Protocol)
│ │ ├── types.go # Core types (Message, Session, Stream)
│ │ └── server.go # ACP server and connection management
│ ├── agent/ # Core agent (Eino-based)
│ │ ├── agent.go # CodingAgent - main agent with ReAct loop
│ │ └── models.go # LLM provider integration
│ ├── config/ # Configuration management
│ │ └── config.go # ConfigManager, profiles, models
│ ├── tools/ # Coding tools
│ │ ├── impl.go # Tool implementations
│ │ └── eino_tools.go # Eino tool wrappers
│ └── tui/ # Terminal user interface
│ └── tui.go # REPL TUI
├── Makefile
├── go.mod
└── go.sum
- Go 1.26+ (required by dependencies)
- API keys for your chosen LLM provider
make buildmake installThis overwrites ~/.local/bin/sword-agent and installs
~/.local/bin/sword-agent-mcp on every install, then updates your shell
startup file with a managed PATH block. You can override the install
directory or rc file:
make install INSTALL_DIR="$HOME/bin"
CODING_AGENT_SHELL_RC="$HOME/.zshrc" make installRestart your shell after installing, or source the rc file printed by the installer.
export OPENAI_API_KEY="sk-..." # For OpenAI models
export ANTHROPIC_API_KEY="sk-..." # For Anthropic/Claude models# Interactive TUI mode
./sword-agent
# Installed command
sword-agent
# Single query mode
./sword-agent -q "read the main.go file"
# Serve ACP JSON-RPC over stdio
./sword-agent -acp-stdio
# Serve a Codex-compatible MCP bridge for sword-agent ACP
./sword-agent-mcp --agent sword-agent
# Specify a model
./sword-agent -model "gpt-4o"
# List available models
./sword-agent -list-models| Tool | Description |
|---|---|
read_file |
Read file contents |
write_file |
Write content to a new file |
edit_file |
Edit files via precise text replacement |
run_terminal |
Execute terminal commands |
bash |
Execute bash commands |
grep |
Search files with regex patterns |
list_directory |
Explore directory structure |
glob |
Find files by glob pattern |
find_path |
Find files by name pattern |
fetch |
Make HTTP requests to URLs; responses over 16KiB are previewed inline and saved under tool-cache/fetch |
move_path |
Move or rename files/directories |
delete_path |
Delete files/directories |
create_directory |
Create new directories |
copy_path |
Copy files or directories |
diagnostics |
Read language-server diagnostics |
find_references |
Find symbol references via LSP |
go_to_definition |
Resolve symbol definitions via LSP |
rename_symbol |
Rename symbols via LSP |
create_thread |
Create an isolated child session |
spawn_agent |
Run a sub-task in a child session and return the result |
skill |
Load reusable instructions from SKILL.md files in the Agent Skills catalog |
| Provider | Config Key | Env Var |
|---|---|---|
| OpenAI | openai |
OPENAI_API_KEY |
| Anthropic (Claude) | anthropic |
ANTHROPIC_API_KEY |
| Google (Gemini) | google |
GEMINI_API_KEY |
| DeepSeek | deepseek |
DEEPSEEK_API_KEY |
| DeepSeek WebSearch | deepseek_search |
DEEPSEEK_SEARCH_API_KEY |
| Mistral | mistral |
MISTRAL_API_KEY |
| Amazon Bedrock | amazon-bedrock |
AWS credentials |
| xAI (Grok) | x_ai |
XAI_API_KEY |
| OpenRouter | openrouter |
OPENROUTER_API_KEY |
| Vercel AI Gateway | vercel_ai_gateway |
VERCEL_AI_GATEWAY_API_KEY |
| Ollama (local) | ollama |
- |
| LM Studio (local) | lmstudio |
- |
| OpenAI Compatible | openai_compatible |
OPENAI_COMPATIBLE_API_KEY |
| Anthropic Compatible | anthropic_compatible |
ANTHROPIC_COMPATIBLE_API_KEY |
Configuration is stored at ~/.config/sword-agent/sword-agent.json. You can manage:
- Models: Add/remove LLM providers
- Profiles: Named configurations with different model/tool combinations
- Tool Permissions: Control which tools are enabled per profile
Internal debug logging is disabled by default. To capture provider / stream
diagnostics, start the CLI with CODING_AGENT_DEBUG_LOG=/path/to/debug.log.
Use CODING_AGENT_DEBUG_LOG=stderr to print internal logs to stderr.
web_search can use DeepSeek's hosted Anthropic-compatible WebSearch backend.
This uses an independent search key so the search tool does not automatically
inherit your normal DeepSeek model API key.
Set it in the TUI:
/key deepseek_search <your DeepSeek Search API key>
Or use an environment variable:
export DEEPSEEK_SEARCH_API_KEY="<your DeepSeek Search API key>"The configuration file field is:
{
"providers": {
"deepseek": {
"search_api_key": "<your DeepSeek Search API key>"
}
}
}Optional environment variables:
export DEEPSEEK_SEARCH_BASE_URL="https://api.deepseek.com/anthropic"
export DEEPSEEK_SEARCH_MODEL="deepseek-v4-flash"You can verify the hosted WebSearch endpoint directly with curl:
curl -sS https://api.deepseek.com/anthropic/v1/messages \
-H "Authorization: Bearer ${DEEPSEEK_SEARCH_API_KEY}" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "deepseek-v4-flash",
"max_tokens": 1200,
"messages": [
{
"role": "user",
"content": "Search the web for best Rust tutorials. Return titles and URLs."
}
],
"tools": [
{
"type": "web_search_20250305",
"name": "web_search",
"max_uses": 3
}
],
"tool_choice": {
"type": "tool",
"name": "web_search"
}
}' | jq '.content'When configured, web_search uses DeepSeek hosted WebSearch. If no DeepSeek
search key is configured, it falls back to the built-in DuckDuckGo lite search.
If a search key is configured but the DeepSeek request fails, web_search
returns that DeepSeek error instead of silently falling back.
DeepSeek/Anthropic search results may include an encrypted_content field for
server-tool continuation. sword-agent treats WebSearch as a client-side tool,
so it intentionally discards that encrypted field and only returns readable
titles, URLs, and summaries to avoid polluting the conversation context.
sword-agent-mcp exposes a typed MCP server that starts
sword-agent -acp-stdio lazily and forwards tool calls through ACP:
[mcp_servers.sword_agent_acp]
command = "/home/projectdata/other/einoresearch/sword-agent/sword-agent-mcp"
args = ["--agent", "sword-agent"]
startup_timeout_sec = 20
tool_timeout_sec = 600
enabled = trueThe MCP server intentionally does not fix a global working directory.
Codex passes work_dir in each tool call, for example:
{
"work_dir": "/home/projectdata/other/einoresearch/sword-agent",
"prompt": "读取 README 并总结可用命令"
}Available MCP tools:
sword_agent_new_sessionsword_agent_promptsword_agent_list_sessionssword_agent_load_sessionsword_agent_compact_sessionsword_agent_close_session
By default the bridge rejects ACP session/request_permission prompts to avoid
unexpected file writes. For a trusted call, pass "permission_policy": "allow"
to sword_agent_prompt, or start the server with
--permission-policy allow.
While in the TUI:
/help- Show available commands/clear- Clear the terminal only; keep the current conversation context/new- Start a new conversation with an empty session context/resume [session-id]- Open a conversation picker (↑/↓ + Enter), or switch directly/undo- Open a user-message picker and remove the selected input plus everything after it/compact- Summarize older conversation context into a compact handoff/rewind- Restore the latest file checkpoint for the current conversation/close [session-id]- Close a conversation; when omitted, close current/delete <session-id>- Alias for closing a saved conversation/model [name]- Switch model/provider [name]- List models for a provider/profile [name]- Switch profile/key <provider> <key>- Set API key for a provider/key deepseek_search <key>- Set the independent DeepSeek WebSearch key/exit- ExitCtrl+C- Cancel the running turn first; press again while idle to exit
The REPL keeps one active session at a time. Messages, assistant tool calls,
tool results, and final answers are recorded in that session and sent back on
the next turn, while /new creates a separate empty session. This mirrors the
basic Zed Agent thread model: context is continuous inside one thread and
isolated between threads. Sessions are persisted under sessions/ next to the
main config file: sessions/index.json tracks project/session metadata, and
each conversation is stored as its own JSON file under the project bucket. The
old top-level sessions.json is migrated on first load and then left in place
as a legacy backup. /resume only shows conversations for the current project
directory. Use /close [session-id] to remove a conversation from the active
session index.
File-modifying tools record in-memory checkpoints before they write. /rewind
restores the latest checkpoint for the active conversation and truncates the
conversation messages after that checkpoint.
Use /undo when you sent the wrong prompt and want to return to an earlier
user input before rewriting it. ACP/JSON-RPC clients can also call
session/truncate with a message_id (or user_message_id) to remove that
user message and all later messages without restoring files. Use
session/rewind when file checkpoint restoration is required, and
session/retry to continue the latest interrupted context.
Long conversations are compacted automatically before they approach the model
context window, using the same handoff-summary strategy as Zed Agent. You can
also type /compact to compact manually. Configure the threshold in
sword-agent.json:
{
"auto_compact": {
"enabled": true,
"threshold": "90%"
}
}threshold accepts a percentage ("90%"), used tokens ("100000"), or
remaining tokens ("-20000").
- Session IDs are generated from a monotonic counter, so closing one session will not cause the next session to overwrite another live session.
- Sessions are saved to a local JSON store using atomic file replacement, so process restarts do not erase conversation context.
- Stored messages carry stable message IDs, which lets ACP clients truncate a conversation at an exact user-message boundary.
- Turns inside the same session are queued. Concurrent sends cannot build a prompt from stale history.
- Closing a session cancels the active turn before removing it from the session map.
- Canceling a turn keeps the session and its previous history intact. This is
what the REPL does when
Ctrl+Cis pressed during streaming output. - The active work directory is injected per turn, so tools and prompt context stay aligned even when different sessions use different directories.
AGENTS.mdorAgents.mdin the work directory is injected transiently into the model input. It is not persisted into conversation history.- Agent Skills are discovered each turn from built-ins,
~/.agents/skills, and<work_dir>/.agents/skills. The model sees a compact catalog and uses theskilltool to load fullSKILL.mdinstructions on demand. create_threadcreates a real isolated session.spawn_agentcreates a child session, runs the task, and returns the child result to the parent turn.run_terminalsupports terminal lifecycle actions. The default action runs a command and waits for completion;action=startreturns aterminal_idfor a long-running process, andaction=read,action=wait,action=kill, andaction=releasemanage that terminal. Terminals created during a turn are recorded in checkpoints so/rewindcan release terminals from reverted work.
This project extracts and ports the following from Zed's agent system:
- NativeAgent → CodingAgent: The core agent orchestrator
- AgentConnection → ACP types: Protocol layer for agent communication
- AgentSettings → ConfigManager: Model and profile configuration
- Tools: File operations, terminal, search tools
- Agent Panel → TUI: Terminal-based user interface
Key differences from Zed's implementation:
- Uses Eino framework (Go) instead of Rust
- Uses REPL TUI instead of GPUI
- Includes an in-process ACP API and a newline-delimited stdio JSON-RPC
transport via
-acp-stdio; advanced Zed ACP capabilities such as auth, session modes, and config options are still simplified - Modular architecture with clear separation of concerns