A coding agentwritten in Go.

It ships as a single binary and talks to twelve model providers. Type /context and it shows every file and message in the model's window, with what each one costs.

source opens soon
git clone https://github.com/ya-nsh/loopcraft && cd loopcraft && make build
streamtool callsexecuteappend
Providers

Anthropic, OpenAI, Gemini, OpenRouter, Bedrock, Vertex AI, Azure, Copilot, Groq, Cerebras, xAI, Mistral

How the loop works

The model streams a reply. If it asked for tools, loopcraft runs them, appends the results and calls the model again. A reply with no tool calls ends the turn. The four moons on the ring at the top of the page are these four steps.

loop
1messages = [system, user]
2loop:
3│   response = model(messages, tools)       # stream
4│   messages.append(response)               # append
5│   if response has no tool calls: break
6│   for call in response.tool_calls:        # tool calls
7│   │   messages.append(execute(call))      # execute
  1. Stream The conversation is sent to the model and the reply streams back, including any thinking.
  2. Tool calls Tool calls in the reply are checked against their schemas and queued.
  3. Execute Read-only tools run in parallel and anything that writes runs one at a time. Each result is added to the conversation.
  4. Append Every message is written to the session file as it happens, so a session can be resumed or branched later.

Four built-in tools

Tool definitions are sent with every request, so the default set is kept small: the system prompt with all four comes to under 1,000 tokens. grep, find and ls can be added with --tools.

read

Files and images, with line numbers, offsets and limits.

func backoff(n int) time.Duration {
│ d := base << n
│ return d + jitter(d)
}

write

Creates missing directories and records a diff of the change.

package retry
 
import "time"
 
type Clock interface {
│ Now() time.Time
}

edit

Replaces an exact string. If the string appears more than once, the edit fails instead of guessing.

-     deadline := time.Now().Add(d)
+     deadline := c.Now().Add(d)

bash

Streams output while the command runs. On timeout it kills the whole process group, not just the shell.

go test ./...
ok   example.com/retry  0.412s

Features

Context view

/context lists the system prompt, tools, every loaded file and the conversation, with the token cost of each.

12 providers

Anthropic, OpenAI, Gemini, Bedrock, OpenRouter and seven more. You can switch model mid-conversation.

Branching sessions

/tree jumps back to an earlier message and continues from there, keeping the original branch.

Limits

Cap turns, tokens, cost or wall time. When a cap is reached the run ends cleanly.

Steering

Type while it works: Enter interrupts, Alt+Enter queues the message, Esc stops the run.

Extensions

Extensions are separate processes speaking JSON-RPC, so any language works. A crashed one is restarted.

Run modes

Interactive, print mode for scripts, a JSON event stream, or RPC over stdin and stdout.

Go SDK

The CLI is a thin layer over coding.Session, which you can import directly.

Compared with pi

Both harnesses were run on the same models and tasks. loopcraft starts in about a tenth of the time and uses much less memory. Model cost and the number of tasks solved came out about the same.

the harness itself
  • Start12× less

    print the version, exit

    loopcraft 15 ms
    pi 174 ms
  • One reply12× less

    start, ask, get an answer, exit

    loopcraft 20 ms
    pi 245 ms
  • 50 tool turns1.9× less

    bash, fifty times

    loopcraft 336 ms
    pi 642 ms
  • 100 file reads8.9× less

    peak memory, one session

    loopcraft 70 MB
    pi 624 MB
  • A real task4.6× less

    peak memory, median model run

    loopcraft 29 MB
    pi 134 MB
  • Install12× less

    one binary vs. node_modules and Node

    loopcraft 21 MB
    pi 255 MB

Against a scripted model that answers instantly, so all of the time and memory is the harness. Medians, lower is better. loopcraft f9c5c64 and pi 1.4.0; the real-task row comes from the model runs.

same model, same tasks
Tasks solved and cost per task for loopcraft and pi on four models, Aider polyglot, Python (34 exercises)
ModelSolved loopcraft · piCost per task loopcraft · pivs pi loopcraft cost
DeepSeek V4.1 Flash54 pairsloopcraft 57%, pi 46%loopcraft $0.0114, pi $0.012513% less
GLM-5.3 Flash31 pairsloopcraft 48%, pi 42%loopcraft $0.0026, pi $0.003934% less
GPT-6 Luna34 pairsloopcraft 44%, pi 41%loopcraft $0.0050, pi $0.002427% more
MiMo v2.6 Flash33 pairsloopcraft 21%, pi 36%loopcraft $0.0039, pi $0.00397% more
All four models152 pairsloopcraft 68, pi 64loopcraft $1.00, pi $1.00level

* Three loopcraft runs looped inside an edit until they hit the token cap. pi sent strict tool schemas and loopcraft did not; loopcraft does now, and a rerun with the fix is next. Without those three runs, cost was level.

The gaps in tasks solved are within chance (p = 0.6 over all 152 pairs). Cost per task is OpenRouter's own bill; “vs pi” compares the two exercise by exercise. loopcraft 7ee38b6 and pi 1.3.0, 18 October 2026.

How this was measured
  • Aider polyglot, Python (34 exercises). Each agent gets the instructions and a stub file; the tests stay hidden until it finishes, then grade the result.
  • Both agents run in an OS sandbox that can read only their own folder and reach only a local proxy. Without it, both went looking for the hidden tests and found them.
  • The proxy holds the key, pins one upstream provider per model (prices for one model vary twelvefold across providers), and records each request's tokens and cost.
  • Same model, same reasoning effort, same output cap for both. Pairs where either run broke or saw the tests are left out of both columns.
  • Model runs: loopcraft 7ee38b6 and pi 1.3.0, 18 October 2026. Harness timings: loopcraft f9c5c64 and pi 1.4.0, 25 October 2026. The runner is loopbench, in the loopcraft repository.

Try the CLI

This terminal runs in the browser and doesn't call a model. The --help and --version output is copied from the real binary.

Type help, or try loopcraft --help.

Use it from Go

The CLI is built on coding.Session, which handles settings, credentials, context files, sessions, retries and compaction. The program here is complete and compiles against the current SDK.

main.go
 1package main
 2
 3import (
 4│   "context"
 5│   "fmt"
 6│   "log"
 7│   
 8│   "github.com/ya-nsh/loopcraft/ai"
 9│   "github.com/ya-nsh/loopcraft/coding"
10)
11
12func main() {
13│   ctx := context.Background()
14│   
15│   s, err := coding.NewSession(
16│   │   ctx, coding.Options{},
17│   )
18│   if err != nil {
19│   │   log.Fatal(err)
20│   }
21│   defer s.Close()
22│   
23│   q := "Why is the retry test flaky?"
24│   err = s.Prompt(ctx, ai.UserMessage{
25│   │   Content: []ai.UserContent{
26│   │   │   ai.TextContent{Text: q},
27│   │   },
28│   })
29│   if err != nil {
30│   │   log.Fatal(err)
31│   }
32│   
33│   answer, _ := s.LastResponse()
34│   fmt.Println(answer.Text())
35}

Planned

Not built yet.

Install

source opens soon
git clone https://github.com/ya-nsh/loopcraft && cd loopcraft && make build

The repository is private for now. Building needs Go 1.25. Prebuilt binaries and a Homebrew tap are planned.