# maki

the efficient coder

I got frustrated with existing coding agents and hitting hourly/weekly token limits.
So I built Maki, a lightweight Rust TUI with some novel context token reduction techniques.
In benchmarks, it reduces cost by 2x and finishes them 2x faster too.

GitHub
Features
`curl -fsSL https://maki.sh/install.sh | sh` copied!
0:00 / 0:00

## Context efficiencyWhere tokens go

index

Parses 15 languages into skeletons: imports, type defs, function signatures with their line ranges. Adds 59 tok/turn but saves 224 tok/turn on reads, netting 165 tok/turn saved. My usage indicated reads are ~65% of all tokens, so this optimization is big.

code_execution

A sandboxed Python interpreter with memory and time limits. All tools are exposed as `async` functions, so the model can `asyncio.gather()` a bunch of reads, grep the results, and only return what matters. Intermediate data never reaches your context.

task

The model picks weak, medium, or strong for each subagent. Haiku-tier for grep-heavy research, opus-tier for architecture. Subagents can be read-only or have full tool access.

Lean system prompt

The system prompt, tool descriptions, and examples are short. When context gets too long, maki compacts history automatically: strips images, thinking blocks, and summarizes older turns.

## User experienceWhat you get

Rust TUI, 60 FPS

Native binary. No javascript runtime, no react. Even the splash screen animation uses SIMD. Syntax highlighting runs on a background thread pool so it never blocks your input. Fits well on small laptop screens.

Full visibility

Philosophy: don't hide anything. Token count, cost, and model are always in the status bar. Each subagent gets its own chat window you can flip through with Ctrl-N/P. Ctrl-F for fuzzy search. `/btw` runs a side query without touching the current session. `!` runs shell commands, `!!` runs them silently.

Sensible permissions

Bash commands are parsed with tree-sitter so maki knows what's actually being run. `git diff && rm -rf /` correctly flags both `git` and `rm`. Most agents only see `git`. Handles subshells, command substitution, pipes. Per-tool allow/deny rules, or `--yolo` to skip it all. SSRF protection on webfetch.

Sessions, memory, MCP

Long-term memory that persists across sessions. Tell maki to remember something, somtimes it picks things up on its own. Double-Escape to rewind. Plan mode restricts the agent to read-only. MCP servers over stdio or HTTP. Skills. 26 themes. Paste images. `--print` for headless (output is Claude Code-compatible).

See it in action

## index: read less, know more

Instead of reading full files, `index` parses with tree-sitter and returns a compact skeleton. The model sees the structure, then reads only the lines it needs.

main.rs

    use std::fs;
    use clap::Parser;
    use color_eyre::Result;

    #[derive(Parser)]
    struct Args {
        paths: Vec<PathBuf>,
        #[arg(short, long)]
        lines: bool,
    }

    fn count_words(text: &str) -> usize {
        text.split_whitespace().count()
    }

    fn count_lines(text: &str) -> usize {
        text.lines().count()
    }

    fn main() -> Result<()> {
        let args = Args::parse();
        for path in &args.paths {
            let text = fs::read_to_string(path)?;
            let n = if args.lines {
                count_lines(&text)
            } else {
                count_words(&text)
            };
            println!("{}: {n}", path.display());
        }
        Ok(())
    }

maki index main.rs

    imports: [1-3]
      clap::Parser, color_eyre::Result, std::fs

    types:
      #[derive(Parser)]
      struct Args [5-9]
        paths: Vec<PathBuf>
        lines: bool

    fns:
      count_words(text: &str) -> usize [11-13]
      count_lines(text: &str) -> usize [15-17]
      main() -> Result<()> [19-29]

29 lines -> 13 lines 55% smaller
See it in action

## code_execution: think inside the sandbox

Tools are exposed as `async` Python functions. The model writes a script, runs it sandboxed, and only the `print()` output enters your context.

script

    # find dead exports in a TS repo
    files = await glob(pattern='src/**/*.ts')
    srcs = await asyncio.gather(
        *[read(path=f) for f in files]
    )

    exports = {}
    imports = set()
    for f, src in zip(files, srcs):
        for m in re.finditer(r'^export \w+ (\w+)', src, re.M):
            exports[m.group(1)] = f
        for m in re.finditer(r'import\s*\{([^}]+)\}', src):
            imports.update(n.strip() for n in m.group(1).split(','))

    for name, f in exports.items():
        if name not in imports:
            print(f'{f}  {name}')

output

    src/lib/csv.ts       parseCsvLegacy
    src/auth/jwt.ts      signV1
    src/utils/phone.ts   formatE164

~40k tokens -> ~30 tokens 1300x reduction

## Providers

Set an API key env var, or use `maki auth login openai` for OAuth.

Anthropic OpenAI Google Copilot Ollama llama.cpp Mistral Z.AI DeepSeek Synthetic

Anything that speaks the OpenAI or Anthropic API works too. Write a short provider script. OpenAI oauth example included.