Skip to content

3-lines-studio/mate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

56 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mate

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.

Features

  • TUI chat — streaming responses, syntax highlighting, session tree navigation
  • mate run — one-shot CLI prompting with stdin piping
  • Tool executionbash, read_file, write_file, edit_file, grep, glob, webfetch
  • Skills — markdown-based prompt templates loaded from directories (YAML frontmatter + body)
  • Prompt templates/template-name expansion 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 clean for housekeeping

Installation

Requires Go 1.26+.

go install github.com/3-lines-studio/mate/cmd/mate@latest

Configuration

Configuration 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

config.toml

[[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 = false

Secrets (secrets.toml)

API 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://..."

AGENTS.md / SYSTEM.md

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.

Usage

TUI

mate
mate -v                  # verbose: log provider communication
mate -m gpt-4o           # override model
mate --debug-prompts     # print system prompt and tool defs, then exit

The 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

CLI (mate run)

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"

Session cleanup

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

Flags

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

Skills are markdown files with YAML frontmatter stored in skill directories. They're loaded from:

  1. ./skills/ (project-level)
  2. ./.mate/skills/ (project-level, hidden)
  3. ~/.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.

Prompt templates

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 readability

Usage:

mate run "/review correctness, error handling" < pr.diff

Template placeholders: $1, $2, ... for positional args, $@ or $ARGUMENTS for all args joined.

Slack bot

Mate can run as a Slack bot responding to @-mentions and DMs.

Setup

  1. Create a Slack app with Socket Mode enabled
  2. Add scopes: app_mentions:read, chat:write, im:history, im:read, im:write
  3. Enable event subscriptions for app_mention and message.im
  4. Get the Bot Token (xoxb-...) and App-Level Token (xapp-...)
  5. 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.

Behavior

  • 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

Building custom agents

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
  • --local flag for terminal mode, or the configured interface (Slack, etc.)
  • Session management, subagents, skills, and prompt templates

ToolConfig

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 = "..."

License

MIT — see LICENSE.

About

There are many agent harnesses, but this one is mine.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages