Lin-H/CoAgent

An interactive AI coding agent built in Rust

★ 0Forks 0RustGitHub ↗Compare

README

Cogent

An interactive AI coding agent built in Rust — inspired by Claude Code.

demo

Features

  • Interactive REPL + full-screen TUI (ratatui/crossterm)
  • Multi-model support via OpenAI-compatible API (DeepSeek, etc.)
  • Built-in tool system: read/write/edit files, bash, grep, glob, git, web fetch
  • MCP (Model Context Protocol) integration via rmcp
  • Skill system: extensible via SKILL.md files
  • Session persistence (JSONL) with resume support
  • Sub-agent spawning with configurable depth limit
  • Auto compaction when conversation exceeds threshold

Prerequisites

  • Rust 2024 edition (MSRV: as specified by Cargo.toml)
  • A compatible LLM API endpoint (OpenAI-compatible)
  • An API key from your model provider (e.g. DeepSeek)

Build & Run

cargo build --release

# Basic usage (API key via CLI flag)
cargo run -- --api-key sk-xxxxxxxxxxxx

# Or use environment variable
export COGENT_API_KEY=sk-xxxxxxxxxxxx
cargo run

# With session resume
cargo run --resume

Configuration

Cogent reads configuration from three sources, in descending priority:

  1. CLI arguments — highest priority, overrides everything
  2. Environment variables — fallback when CLI flag is absent
  3. Config file — persistent defaults, lowest priority

Quick start with DeepSeek

The default config targets DeepSeek (deepseek-chat model, https://api.deepseek.com/v1 base URL). You only need to provide your API key.

Option A — Environment variable (recommended)

# Linux / macOS
export COGENT_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx

# Windows (PowerShell)
$env:COGENT_API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxx"

cargo run

Option B — CLI flag

cargo run -- --api-key sk-xxxxxxxxxxxxxxxxxxxxxxxx

Option C — Config file

Create a config.toml at the platform config directory (see Config file location):

model = "deepseek-chat"
base_url = "https://api.deepseek.com/v1"
api_key = "sk-xxxxxxxxxxxxxxxxxxxxxxxx"

Then simply:

cargo run

Config file location

The config file is resolved via the directories crate (using the data_dir for the cogent project):

Platform Path
Linux ~/.local/share/cogent/config.toml
macOS ~/Library/Application Support/cogent/config.toml
Windows %APPDATA%\cogent\data\config.toml (e.g. C:\Users\YourName\AppData\Roaming\cogent\data\config.toml)

Full config file reference

# ── Model ──────────────────────────────────────────
model = "deepseek-chat"
base_url = "https://api.deepseek.com/v1"
api_key = "sk-xxxxxxxxxxxxxxxxxxxxxxxx"       # 必填(也可通过 COGENT_API_KEY 环境变量设置)

# ── Shell ──────────────────────────────────────────
# None = platform default ($SHELL on Unix, pwsh.exe on Windows)
# shell = "/bin/bash"

# ── Tool execution ─────────────────────────────────
max_concurrent_tools = 8

# ── Approval policy ────────────────────────────────
# always_approve = no prompts (yolo mode)
# ask_every_shell = prompt on every shell command
# allowlist_ask = auto-approve allowlisted tools, prompt for the rest (default)
approval_policy = "allowlist_ask"
tool_allowlist = [
    "read_file",
    "list_dir",
    "grep_files",
    "git_status",
    "git_diff",
    "git_log",
    "git_show",
]

# ── Auto compaction ────────────────────────────────
# When total messages exceed this threshold, cogent automatically
# summarizes older messages to keep context manageable.
# 0 = disable auto compaction. Default: 50
auto_compact_threshold = 50

# ── System prompt (optional) ───────────────────────
# Override the default system prompt with your own instructions:
# system_prompt = "You are a Rust expert. Always use clippy."

All CLI arguments & environment variables

CLI flag Env var Description
--model <NAME> COGENT_MODEL Model name (default: deepseek-chat)
--base-url <URL> COGENT_BASE_URL API base URL (default: https://api.deepseek.com/v1)
--api-key <KEY> COGENT_API_KEY API key (Bearer token)
--cwd <PATH> COGENT_CWD Working directory (default: current dir)
--resume <ID> — Resume a session by ID (or prefix)

Priority: CLI flag > environment variable > config file > built-in default.

Using other OpenAI-compatible providers

Cogent works with any OpenAI-compatible API endpoint. Just set a different model and base_url:

# OpenAI
cargo run -- --model gpt-4o --base-url https://api.openai.com/v1 --api-key sk-xxx

# Local Ollama (no API key needed)
cargo run -- --model llama3 --base-url http://localhost:11434/v1

# vLLM / LM Studio
cargo run -- --model my-model --base-url http://localhost:8000/v1

TUI Commands

Command Description
/help List available commands
/tools List registered tools
/history Show conversation history
/compact Manually trigger compaction
/clear Clear conversation history
/quit Exit (also Ctrl+C)

Debugging

Cogent is a full-screen TUI app (crossterm raw mode + alternate screen). println!/print! writes to stdout which is owned by the TUI renderer — output is swallowed or corrupts the screen. Always use stderr for diagnostics.

Option 1 — tracing macros (recommended, already configured)

The project initializes tracing-subscriber with output to stderr (or a file via --log-file). Use these in code:

tracing::debug!("variable value: {:?}", my_var);
tracing::info!("reached this point");
tracing::warn!("session write failed: {e}");  // already used in the codebase

Via --log-file (most reliable on Windows)

$env:RUST_LOG = "cogent=debug"
cargo run -- --log-file cogent.log

Via stderr redirect

Run with a log level and redirect stderr to a file (stdout stays on the terminal for TUI):

# PowerShell
$env:RUST_LOG = "cogent=debug"    # or cogent=trace for maximum detail
cargo run 2>cogent.log

# In another terminal — live follow:
Get-Content cogent.log -Wait
# Linux / macOS
export RUST_LOG=cogent=debug
cargo run 2>cogent.log

# In another terminal:
tail -f cogent.log

Levels from coarsest to finest: error > warn > info > debug > trace. cogent=debug shows only this project's logs; cogent=trace shows everything including stream-level details.

Option 2 — eprintln! (quick temporary debug)

For throwaway debugging without tracing, at least use eprintln! instead of println!:

eprintln!("debug: x = {:?}", x);

Same stderr redirect:

cargo run 2>cogent.log

Why stdout doesn't work

enable_raw_mode() + EnterAlternateScreen (crossterm) takes over the entire terminal:

  • stdout is exclusively used for TUI rendering — print! will corrupt the display or be invisible
  • stderr is a separate stream and can be redirected to a file without affecting the TUI

Quick reference

Macro Target Visible during TUI? Use case
println! / print! stdout ❌ swallowed / corrupts Never use
eprintln! stderr ✅ via redirect Quick temporary debug
tracing::debug! etc. stderr ✅ via redirect Structured logging, filterable by level

Architecture

See DESIGN.md for the full design document.

License

MIT

Contributors

Lin-H

Issues