Go CLI coding assistant with TUI and Slack bot interfaces. Uses OpenAI-compatible LLM APIs for an agentic coding loop with tools — file operations, shell execution, code search, web fetching, and subagent delegation.
- TUI chat — streaming responses, syntax highlighting, session tree navigation
mate run— one-shot CLI prompting with stdin piping- Tool execution —
bash,read_file,write_file,edit_file,grep,glob,webfetch - Skills — markdown-based prompt templates loaded from directories (YAML frontmatter + body)
- Prompt templates —
/template-nameexpansion in prompts - Subagent delegation — offload work to child agents with different models/tools
- Slack bot — mention or DM the bot in Slack; streaming responses with cancellation
core.Run— framework for building custom agents with your own tools and interfaces- Session management — turn-based tree model, persisted to disk,
mate cleanfor housekeeping
Requires Go 1.26+.
go install github.com/3-lines-studio/mate/cmd/mate@latestConfiguration lives in ~/.config/mate/. The following files are read:
| File | Purpose |
|---|---|
config.toml |
Providers, models, agent settings, subagents |
AGENTS.md |
User-level conventions injected into the system prompt |
SYSTEM.md |
System-level prefix (overrides AGENTS.md position) |
secrets.toml |
API keys, tokens, service credentials |
[[providers]]
id = "openai"
base_url = "https://api.openai.com/v1"
[[models]]
id = "gpt-4o"
provider = "openai"
name = "gpt-4o"
description = "GPT-4o"
context_window = 128000
max_output_tokens = 16384
[[models]]
id = "claude-sonnet-4-20250514"
provider = "anthropic"
name = "claude-sonnet-4-20250514"
description = "Claude Sonnet 4"
context_window = 200000
max_output_tokens = 16384
thinking_type = "enabled"
reasoning_effort = "medium"
[agent]
model = "gpt-4o"
max_tool_rounds = 99
# tools = ["bash", "read_file"] # uncomment to restrict tools
# interfaces = ["slack", "local"] # order determines which interface starts
[[subagents]]
id = "thinker"
description = "Deep reasoning subagent"
model = "claude-sonnet-4-20250514"
tools = ["bash", "read_file", "write_file", "edit_file", "grep", "glob"]
prompt = "You are a deep-thinking reasoning subagent. Think step by step."
[session]
dir = "~/.config/mate/sessions"
[tui]
tools_expanded = false
show_thinking = falseAPI keys, tokens, and service credentials. Keys are matched by provider ID, not array position:
[providers]
openai = "sk-..."
anthropic = "sk-ant-..."
[slack]
bot_token = "xoxb-..."
app_token = "xapp-..."
[services.picsel]
connection_string = "postgres://..."These markdown files are injected into the system prompt. SYSTEM.md goes at the top (before the tool rules block). AGENTS.md from ~/.config/mate/ provides user-level conventions; AGENTS.md from the project root provides project-level conventions.
Example ~/.config/mate/AGENTS.md:
# Your conventions
- Prefer early returns over nested ifs.
- Use kebab-case for TypeScript, snake_case for Go.Example SYSTEM.md:
You are a senior software engineer. Write correct, idiomatic code.mate
mate -v # verbose: log provider communication
mate -m gpt-4o # override model
mate --debug-prompts # print system prompt and tool defs, then exitThe TUI shows:
- Streaming assistant responses with syntax-highlighted code blocks
- Tool execution progress (bash, file ops, grep/glob)
- Session tree sidebar (turn-based history, switch between branches)
- Token usage per turn
- Subagent delegation flow
mate run "fix the null pointer bug"
echo "refactor this" | mate run
mate run /review-pr < pr.diff
git diff main | mate run "summarize these changes"The --debug-prompts flag works with mate run too:
mate run --debug-prompts "hello"mate clean # list sessions
mate clean --all # delete all sessions
mate clean --older-than 7 # delete sessions older than 7 days
mate clean --dry-run # show what would be deleted| Flag | Applies to | Description |
|---|---|---|
-v, --verbose |
TUI, run, clean | Log provider communication to stderr |
--debug-prompts |
TUI, run | Print system prompts and tool definitions, then exit |
-m, --model |
TUI, run | Override the configured model (e.g. -m gpt-4o) |
Skills are markdown files with YAML frontmatter stored in skill directories. They're loaded from:
./skills/(project-level)./.mate/skills/(project-level, hidden)~/.config/mate/skills/(user-level)
Each skill lives in its own directory with a SKILL.md file. The LLM discovers skills via the list_skills tool and loads them with load_skill.
Example skills/react/SKILL.md:
---
name: react
description: React component patterns and best practices
tools:
- bash
- read_file
---
# React patterns
- Use functional components with hooks.
- Prefer composition over inheritance.Templates are .md files in ~/.config/mate/prompts/. They support YAML frontmatter for metadata and positional arguments. Use /template-name in any prompt to expand a template.
Example ~/.config/mate/prompts/review.md:
---
description: Review a pull request diff
argument-hint: <focus areas>
---
Review the following diff. Focus on: $@
- Bugs and logic errors
- Performance issues
- Security concerns
- Code style and readabilityUsage:
mate run "/review correctness, error handling" < pr.diffTemplate placeholders: $1, $2, ... for positional args, $@ or $ARGUMENTS for all args joined.
Mate can run as a Slack bot responding to @-mentions and DMs.
- Create a Slack app with Socket Mode enabled
- Add scopes:
app_mentions:read,chat:write,im:history,im:read,im:write - Enable event subscriptions for
app_mentionandmessage.im - Get the Bot Token (
xoxb-...) and App-Level Token (xapp-...) - Configure in
~/.config/mate/config.toml:
[agent]
interfaces = ["slack"]And add tokens to ~/.config/mate/secrets.toml:
[slack]
bot_token = "xoxb-..."
app_token = "xapp-..."When interfaces includes "slack" before "local", the Slack bot starts instead of the TUI. Run mate --local to force the local terminal interface.
- Thread-based sessions: each Slack thread gets its own session, persisted across restarts
- Streaming updates: the bot edits its "thinking" message in real-time as the LLM responds
- Cancellation: starting a new prompt in the same thread cancels the previous one (5s timeout)
- Session cache: LRU cache of 50 sessions, evicted on overflow
The core.Run framework lets you build your own CLI agent with custom tools and interfaces.
package main
import (
"context"
"encoding/json"
"github.com/3-lines-studio/mate/core"
"github.com/3-lines-studio/mate/tools"
)
func main() {
myTool := tools.DefineTool[struct {
Name string `json:"name"`
}](
"greet",
"Greet someone by name",
map[string]any{
"type": "object",
"properties": map[string]any{
"name": map[string]any{
"type": "string",
"description": "Name to greet",
},
},
"required": []string{"name"},
},
func(ctx context.Context, p struct{ Name string }) (string, error) {
return "Hello, " + p.Name + "!", nil
},
)
core.Run(core.Definition{
Name: "my-agent",
Tools: []tools.Tool{myTool},
})
}This gives you a complete CLI with:
- All standard tools (bash, read/write/edit, grep, glob)
- Config file at
~/.config/my-agent/config.toml --localflag for terminal mode, or the configured interface (Slack, etc.)- Session management, subagents, skills, and prompt templates
For tools that need runtime configuration (API keys, store directories), use ToolConfig:
core.Run(core.Definition{
Name: "scraper",
ToolConfig: func(services map[string]map[string]any, storeDir, configDir string) []tools.Tool {
return []tools.Tool{makeScraperTool(services["scraper"])}
},
})Services are configured in secrets.toml under [services]:
[services.scraper]
api_key = "..."MIT — see LICENSE.