read
Files and images, with line numbers, offsets and limits.
func backoff(n int) time.Duration { │ d := base << n │ return d + jitter(d) }
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.
git clone https://github.com/ya-nsh/loopcraft && cd loopcraft && make buildAnthropic, OpenAI, Gemini, OpenRouter, Bedrock, Vertex AI, Azure, Copilot, Groq, Cerebras, xAI, Mistral
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.
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
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.
Files and images, with line numbers, offsets and limits.
func backoff(n int) time.Duration { │ d := base << n │ return d + jitter(d) }
Creates missing directories and records a diff of the change.
package retry import "time" type Clock interface { │ Now() time.Time }
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)
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
/context lists the system prompt, tools, every loaded file and the conversation, with the token cost of each.
Anthropic, OpenAI, Gemini, Bedrock, OpenRouter and seven more. You can switch model mid-conversation.
/tree jumps back to an earlier message and continues from there, keeping the original branch.
Cap turns, tokens, cost or wall time. When a cap is reached the run ends cleanly.
Type while it works: Enter interrupts, Alt+Enter queues the message, Esc stops the run.
Extensions are separate processes speaking JSON-RPC, so any language works. A crashed one is restarted.
Interactive, print mode for scripts, a JSON event stream, or RPC over stdin and stdout.
The CLI is a thin layer over coding.Session, which you can import directly.
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.
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.
| Model | Solved loopcraft · pi | Cost per task loopcraft · pi | vs pi loopcraft cost |
|---|---|---|---|
| DeepSeek V4.1 Flash54 pairs | loopcraft 57%, pi 46% | loopcraft $0.0114, pi $0.0125 | 13% less |
| GLM-5.3 Flash31 pairs | loopcraft 48%, pi 42% | loopcraft $0.0026, pi $0.0039 | 34% less |
| GPT-6 Luna*34 pairs | loopcraft 44%, pi 41% | loopcraft $0.0050, pi $0.0024 | 27% more |
| MiMo v2.6 Flash33 pairs | loopcraft 21%, pi 36% | loopcraft $0.0039, pi $0.0039 | 7% more |
| All four models152 pairs | loopcraft 68, pi 64 | loopcraft $1.00, pi $1.00 | level |
* 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.
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.
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.
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}
Not built yet.
git clone https://github.com/ya-nsh/loopcraft && cd loopcraft && make buildThe repository is private for now. Building needs Go 1.25. Prebuilt binaries and a Homebrew tap are planned.