A Go-based autonomous AI coding agent with TUI + HTTP API. Works best with A²gent/caesar as control app.
# one-line install from GitHub (main branch)
curl -fsSL https://raw.githubusercontent.com/A2gent/brute/main/install-from-github.sh | bash
bruteThen configure your provider inside the agent (/provider) or in the web app Providers page.
The installer builds from current source and installs:
brute(primary CLI)
Manual install from a local clone is also supported:
git clone https://github.com/A2gent/brute.git
cd brute
./install.sh- Go 1.24+
justcommand runner (install)- API key for at least one remote provider (unless you use local LM Studio)
- macOS camera features: Xcode Command Line Tools (
xcode-select --install) - For local audio in web-app (Whisper/Piper bootstrap):
cmakeffmpegpkg-config- Install on macOS:
brew install cmake ffmpeg pkg-config
- Comprehensive tool system:
- File operations:
read,write,edit,replace_lines - Search:
glob,grep,find_files - Execution:
bashcommand execution - Media: screenshot capture and camera photo capture
- Extensible architecture for custom/server-backed tools
- Agentic loop: task -> LLM with tools -> tool execution -> result feedback -> repeat
- A2A bridge support: canonical message endpoint + outbound tunnel-based chat + agent-card discovery
- Multi-provider support: Anthropic Claude, Kimi, Google Gemini, LM Studio, OpenAI-compatible endpoints
- Auto-router and fallback chain support for reliability
- In-session provider/model switching support (web app flow)
- SQLite persistence for sessions, messages, jobs, integrations, and app settings
- Session resumption and parent/child session relationships
- Recurring jobs and project-aware session organization
- Interactive terminal UI with status bar, model display, token/context metrics, and session timer
- Multi-line input and command palette behavior
- Live message stream with tool call/result rendering
- REST API for web-app integration
- Session management endpoints (create/list/resume/manage)
- Speech and integration plumbing (including Whisper-related flows)
- Lightweight runtime footprint
- Context window tracking and management
- Structured logging and practical failure handling
Workflow files used by the web app are stored in the system-soul project under workflows/*.yaml.
Minimal structure:
id: custom-workflow
name: Custom workflow
description: Optional summary
entryNodeId: user
policy:
stopCondition: manual # manual | max_turns | consensus | judge | timebox
maxTurns: 12
timeboxMinutes: 20
# required when stopCondition: judge
# judgeNodeId: critic
nodes:
- id: user
label: User # optional; defaults to id
kind: user # optional; inferred when omitted
- id: worker_a
label: Research A
kind: subagent
ref: researcher-a # sub-agent id/name (optional if label resolves)
- id: critic
label: Critic
kind: subagent
ref: critic-agent
edges:
- from: user
to: worker_a
- from: worker_a
to: critic
mode: sequential # optional; inferred as parallel for fan-outValidation rules:
nodes[].idis required and must be unique.edges[].fromandedges[].tomust reference existing node ids.policy.stopCondition: judgerequirespolicy.judgeNodeId.policy.judgeNodeIdmust reference an existing node id.kindandrefare optional, but agent nodes should resolve to configured agents.- Loops are supported (for example
developer -> critic -> developer). - For looped graphs, use
policy.maxTurnsand/orpolicy.timeboxMinutesto cap iterations. - For
stopCondition: judge, the judge node should emitVERDICT: APPROVEDto end early; otherwise the loop continues until limits are reached.
# install + run from any directory
brute
# build only
just build
# API only (uses the local app/extension default port 5445)
brute server
# force a custom API port when needed
brute --port 8080By default, the embedded HTTP API binds to port 5445, which is also the default Caesar and Chrome extension endpoint.
Use --port 0 only when you explicitly want the OS to choose a random free port; in that case the selected URL is printed on startup (for example: HTTP API server running on http://0.0.0.0:49162).
Build image:
docker build -t a2gent-brute:latest -f Dockerfile .Run directly:
docker run --rm -it \
--name a2gent-brute \
--read-only \
--tmpfs /tmp:exec,size=256m \
-p 8080:8080 \
-v "$PWD":/workspace \
-v "$HOME/.a2gent-data":/data \
a2gent-brute:latest server --port 8080Run with compose helpers:
# API mode
just docker-api
# API mode with explicit LM Studio endpoint (useful for Tailscale IP)
just docker-api-lmstudio http://100.x.y.z:1234/v1
# interactive TUI mode (must run in a real terminal)
just docker-tui
# stop
just docker-api-down
# or
just docker-stopDocker notes:
/workspaceis the agent-visible project tree./datastores DB, logs, and config (AAGENT_DATA_PATH=/data).- Runtime image is Alpine-based and includes
ffmpeg. - Default LM Studio URL in compose is
http://host.docker.internal:1234/v1. - For Tailscale-hosted LM Studio, prefer direct Tailscale IP over MagicDNS hostname.
Requirement: install Apple container CLI: github.com/apple/container
# build image
just apple-build
# API mode
just apple-api
# API mode with explicit LM Studio endpoint (recommended for Tailscale)
LM_STUDIO_BASE_URL=http://100.x.y.z:1234/v1 just apple-api
# interactive TUI mode
just apple-tui
# stop running brute containers
just apple-stop
# stop Apple container runtime VM
just apple-system-stopCanonical location (single-folder layout with DB/logs):
| Location | Scope |
|---|---|
$AAGENT_DATA_PATH/config.json |
user-level |
Defaults:
AAGENT_DATA_PATH=~/.local/share/aagent- config:
~/.local/share/aagent/config.json - database:
~/.local/share/aagent/aagent.db - logs:
~/.local/share/aagent/logs/
Backward-compatible read fallbacks are still supported:
.aagent/config.json~/.config/aagent/config.json
The app loads .env from:
- current directory
~/.env
Provider/API keys are usually configured inside the agent UI and persisted to local settings. Environment variables are optional and mainly useful for headless/server workflows.
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
Anthropic key (legacy/API-compatible paths only; the Anthropic provider uses Claude Code CLI login) |
KIMI_API_KEY |
Kimi key |
GEMINI_API_KEY |
Gemini key |
OPENAI_API_KEY |
OpenAI-compatible key |
Common optional variables:
| Variable | Default | Description |
|---|---|---|
AAGENT_PROVIDER |
auto |
active provider (anthropic, kimi, gemini, lmstudio, auto-router) |
AAGENT_MODEL |
provider-specific | model override |
AAGENT_CLAUDE_CLI_PATH |
claude |
Claude Code CLI executable used by the Anthropic provider |
AAGENT_CLAUDE_CLI_PERMISSION_MODE |
acceptEdits |
Claude CLI permission mode for non-interactive runs; set to default, auto, or bypassPermissions if needed |
AAGENT_CLAUDE_CLI_NO_SESSION_PERSISTENCE |
true |
disable Claude CLI session persistence for isolated A2gent turns |
KIMI_BASE_URL |
https://api.kimi.com/coding/v1 |
Kimi endpoint |
GEMINI_BASE_URL |
https://generativelanguage.googleapis.com |
Gemini endpoint |
LM_STUDIO_BASE_URL |
http://localhost:1234/v1 |
LM Studio endpoint |
AAGENT_DATA_PATH |
~/.local/share/aagent |
data directory |
AAGENT_FALLBACK_PROVIDERS |
- | fallback chain list |
| Command | Description |
|---|---|
brute |
launch TUI |
brute "<task>" |
start with an initial task |
brute --continue <session-id> |
resume session |
brute session list |
list sessions |
brute logs |
show logs |
brute logs -f |
follow logs |
brute --port 8080 |
run with fixed API port |
Brute supports A2A communication in two modes:
- Protocol-style HTTP endpoint for inbound A2A messages
- Tunnel-backed outbound messaging to remote agents via Square
GET /.well-known/agent-card.jsonsupportedInterfaces[0].urlpoints to/a2a/messages/send
| Method | Path | Description |
|---|---|---|
POST |
/a2a/messages/send |
Handle canonical A2A message (content[]) and return final response |
POST |
/a2a/messages/send/stream |
SSE stream for inbound A2A message lifecycle (accepted, running, final message) |
| Method | Path | Description |
|---|---|---|
POST |
/a2a/outbound/sessions |
Create local outbound A2A session bound to target agent |
POST |
/a2a/outbound/sessions/{sessionID}/chat |
Send outbound A2A message (sync) |
POST |
/a2a/outbound/sessions/{sessionID}/chat/stream |
Send outbound A2A message (SSE progress stream) |
- Canonical requests use
content[]parts (text,image_url,image_base64) - Image-only and text+image requests are supported
- For compatibility, brute still understands legacy bridge fields (
task,images,result) used by existing tunnel/proxy flows
- Sessions are persisted in a single SQLite DB (
AAGENT_DATA_PATH/aagent.db). - Session fields include
id,agent_id,title,status, timestamps, optionalparent_idandjob_id. - Grouping available now: parent/child sessions and job sessions.
- Not currently in HTTP session API: first-class project/folder filtering.
DB path:
~/.local/share/aagent/aagent.db
# or
$AAGENT_DATA_PATH/aagent.dbMain tables:
sessionsmessagesrecurring_jobsjob_executionsapp_settingsintegrationsmcp_serversprojects
Quick query:
sqlite3 ~/.local/share/aagent/aagent.db
SELECT id, title, status, created_at FROM sessions ORDER BY created_at DESC LIMIT 10;just run # run with go run
just dev # API hot reload (air)
just build # build binary
just test # run tests
just fmt # go fmt
just lint # go vet# all tests
just test
# unit tests (race-enabled)
just test-unit
# integration tests (separate pipeline)
just test-integration
# unit tests with coverage output
just test-coverage
# one package
go test -v ./internal/tools/...Configure a provider in-agent first (/provider in TUI or Providers in web app).
If you run headless, set one provider key via env:
export KIMI_API_KEY=...
# or GEMINI_API_KEY / OPENAI_API_KEYFor the Anthropic provider, install and log in to Claude Code CLI instead of configuring an API key; see 11.3.
export AAGENT_PROVIDER=auto-router
export AAGENT_FALLBACK_PROVIDERS=anthropic,kimi,geminiThe Anthropic provider intentionally uses the local Claude Code CLI (claude) and your existing Claude login, not the Anthropic API. Ensure Claude Code is installed and available on PATH, or set:
export AAGENT_CLAUDE_CLI_PATH=/path/to/claudeA2gent runs Claude CLI in non-interactive mode with native Claude tools enabled and defaults to AAGENT_CLAUDE_CLI_PERMISSION_MODE=acceptEdits so Sonnet can inspect and edit files without a hidden permission prompt. If your environment requires a different Claude permission policy, override that variable.
Run TUI from a real interactive terminal (just docker-tui) or use API mode (just docker-api).
Use Tailscale IP, not MagicDNS hostname, e.g.:
just docker-api-lmstudio http://100.x.y.z:1234/v1aagent/
├── cmd/aagent/ # CLI entry point
├── internal/
│ ├── agent/ # orchestrator and loop
│ ├── config/ # config management
│ ├── llm/ # provider clients
│ ├── logging/ # logs
│ ├── session/ # session model + manager
│ ├── storage/ # SQLite store
│ ├── tools/ # built-in tools
│ └── tui/ # Bubble Tea UI
├── justfile
└── README.md
- Fork the repository.
- Create a branch (
git checkout -b feature/your-change). - Commit and push changes.
- Open a pull request.
License: MIT
| Channel | Contact |
|---|---|
| Founder Telegram | @tot_ra |
| X / Twitter | @tot_ra |
| Schedule Demo | https://calendly.com/artkurapov/30min |
artkurapov at gmail.com |