Home

Loop


Sometime around 2014 – 2015, while still in school, I was tinkering around with websites. I had just gotten a good grasp of HTML, CSS, and Javascript, and I really wanted to build out an operating system. Or well, a website that looks and feels like an operating system, but runs on the browser.

The goal behind this project, at least at that time, was so that people could easily make single-page websites (web apps were not a term back then 😅) and then list them on the loop app store, letting anyone download, use and run them. There were a lot of obstacles around this, especially when 2015-me was dead scared of remote servers, internet was still scarce and expensive in India, and I had to make a gigabyte of data allowance run for a month.

The app store did not happen. But it opened my mind to the possibility of bundling a toolmaker/editor (in a big way, inspired from Nintendo’s Mario maker) with loop so that everything remains on one platform.



Fast forward, late 2023 ⎯ the AI wave is happening, I was having fun playing around with a bit of rust and building umpteen toys and side projects, and also contemplating on whether me studying computer science, or constantly building tools, toys, and other small artifacts now that I don’t need to remember every syntax or spend time thinking how to name the next variable because I actually had a lot of fun doing it.

The conclusion, after a lot of introspection, was that I was definitely having fun. If I was building a ton of fun pieces just to play with them, without bothering about monetization or going viral, or god forbid, even sidelining the whole post-your-work-in-public philosophy, then it had to be purely for the joy of crafting a beautiful piece of software, right? Right.

I started tinkering again with rust. I was simply trying to build a tool for myself that can parse the necessary bits from the response of an LLM and patch the modified bits of code in my local files. The initial prototype worked pretty well, multi-edit files were a breeze : the 2 tools of write and read were good enough for me.

And then I started thinking - what if I start plugging in the other methods for giving bigger answers, fetching tools by using their semantic description, and if someday, this tool gets a HUD or a screen to show what it is building and how and where it is plugging things in, it almost as if you have the liberty to add fairy lights and dream catchers or anything you want to your own computer. This project is not there yet, but it made sense to revive the old loop name while building out this CLI tool.

So I built loop cli ⎯ a minimalist, rust-native agent harness (agent and harness were not big enough terms yet, so I was calling it a semantic circuit navigator 😬)

Loop gives you a single binary that turns any LLM into a coding agent with filesystem access, tool execution, persistent memory, and git-aware version control — all from your terminal.

loop init      # one-time setup: pick models, add API keys
loop           # start the interactive REPL
loop run -p "fix the failing test in src/lib.rs"   # one-shot mode
loop manual    # full command reference
34 source files · ~6,600 lines of Rust · 7.3 MB release binary

Why Loop?

Most agent harnesses are either heavyweight frameworks that take over your stack, or thin wrappers around a single provider’s API. Loop is neither:

  • Provider-agnostic — Switch between Claude, GPT-4o, Gemini, Llama (Groq), local Gemma (Ollama), and any OpenRouter model slug
  • Memory that persists — Checkpoints, directives, and semantic search survive across sessions
  • Learns from your corrections — When you tell it “no, do it this way,” Loop records the directive and references it in future runs
  • Git-native — Optionally commits every accepted change with an LLM-generated semantic commit message
  • MCP-enabled — Connect to any Model Context Protocol server to extend the agent with external tools
  • Extensible — Write plugins in any language, or let the agent write them for itself
  • Single binary — No runtime, no daemon, no node_modules. Just loop.

Getting Started


Prerequisites

  • At least one LLM API key (Anthropic, OpenAI, Google, Groq, or OpenRouter) — or Ollama for fully local operation
  • Charm-style colors, badges, and rounded panels are built in with the Rust Lip Gloss port. Optional: Gum and Glow add an enhanced header and Markdown rendering (brew install gum glow).

Install from Source

git clone https://github.com/yourname/loop-cli.git
cd loop-cli
cargo install --path .

This installs the loop binary to ~/.cargo/bin/.

First Run

loop init

The interactive wizard walks you through:

  1. Select providers — multi-select from Anthropic, OpenAI, Gemini, Groq, Ollama, and OpenRouter
  1. Enter API keys — masked input, encrypted in the operating system credential vault (macOS Keychain, Windows Credential Manager, or Linux Secret Service)
  1. Pick a default model — the model used when you just type loop
  1. Add instruction files — point to .md files with project-specific context
  1. Tool approval mode — require confirmation before file writes (recommended)
  1. Git auto-commit — opt in to automatic commits on accepted changes
  1. MCP servers — connect to external tool servers (filesystem, GitHub, databases, etc.)

After setup, just type:

loop

You’re now in the interactive REPL. Type what you want done, and Loop will read your files, plan an approach, make changes, and verify the results.

Quick Examples

loop ▸ list the project structure and explain the architecture
loop ▸ fix the compilation error in src/parser.rs
loop ▸ add input validation to the create_user endpoint
loop ▸ review src/auth/ for security issues
loop ▸ refactor the parser and verify the patch -t2

Capabilities


Six LLM Providers

ProviderModelsHow
AnthropicClaude Sonnet 4, Haiku 3.5API key
OpenAIGPT-4o, GPT-4o-miniAPI key
Google GeminiGemini 2.5 Pro, 2.5 FlashAPI key
GroqLlama 3.3 70BAPI key
OllamaGemma 3 (local)Local install, no API key needed
OpenRouterAny supported provider/model slugVerified OpenRouter API key

All providers implement a unified trait. Switching models requires changing one line in ~/.loop/config.toml.

During loop init, Loop verifies an OpenRouter key before asking for the full model slug, such as anthropic/claude-sonnet-4 or google/gemini-2.5-pro.

API keys are never serialized to config.toml. Loop keeps provider/model metadata there with owner-only permissions and retrieves credentials into process memory only when needed. Existing plaintext keys are migrated to the operating system credential vault and removed from the TOML file on the next load.

Six Built-in Tools

ToolTypeDescription
readRead-onlyRead file contents with optional line range
writeMutatingCreate or overwrite files (auto-creates parent dirs)
editMutatingSurgical find-and-replace (exact string match)
multi_editMutatingBatch edits across multiple files in one atomic call
bashMutatingExecute shell commands with timeout and output truncation
list_dirRead-onlyRecursive directory listing with sizes

Mutating tools require user approval by default. Disable with “YOLO mode” during loop init.

Markdown Checkpoints

When you exit (/quit) or hit the iteration limit, Loop saves a checkpoint as a structured .md file:

## ✅ DONE (completed — do NOT repeat)
-[x] Read src/main.rs and understood the CLI dispatch
-[x] Fixed the off-by-one error in pagination

## 🔄 DOING (context at suspension)
Was implementing the new validation layer. Had just written
the schema module and was about to wire it into the handler.

## 📋 NEXT (pending tasks — execute in order)
1.Wire validation into the create_user handler
2.Add tests for edge cases
3.Run the full test suite

On your next loop session, you’re asked if you want to resume — the agent picks up exactly where it left off, knowing what’s done, what’s in progress, and what’s next.

This was necessary to bake in back in the day because models had not gotten their escape velocity entirely yet, context windows were small, network timeouts and other errors often prevented a task from getting completed and you had to revert to a previous version (if you were using version control locally, that is, else it’s all gone - pfffft), figure out how to prune the context, and then start again.

Directives System

When you tell Loop something specific — “no, use HashMap not BTreeMap” or “fix it by adding a null check before the dereference” — Loop detects this as a directive and records:

  • Fingerprint: a short identifier like null-deref-auth-handler
  • What you said: the exact instruction
  • What was done: the action taken
  • Outcome: ✅ Worked / ❌ Didn’t work / ⚠️ Partial

These are stored in ~/.loop/directives.md. On every future run, Loop searches this file for relevant past directives and injects them into context. If something didn’t work before, the agent is explicitly warned not to repeat it.

Semantic Memory Search

At startup, Loop builds a TF-IDF index over all saved memories:

  • All directives (~/.loop/directives.md)
  • All checkpoints (~/.loop/checkpoints/*.md)
  • All skill files (~/.loop/skills/*.md)

Before each task, it performs cosine similarity search against the user’s input and injects the top-3 relevant memories into the system prompt. No external API needed — runs entirely locally.

Git Auto-Checkpoint

When enabled and operating inside a git repository:

  1. You accept a file change (write/edit)
  1. Loop detects unstaged changes
  1. Asks the LLM to generate a Conventional Commits message
  1. Stages and commits automatically
  ⚡ edit path="src/api/list.rs", ...
  ▸ Execute edit? Yes
  ✓ Successfully edited src/api/list.rs
  📝 git: fix(api): correct 0-indexed pagination offset
Requirement: The working directory must have git init already run. Loop will not initialize git for you — it respects your existing workflow.

Plugin System

Loop discovers any executable named loop-plugin-* on $PATH or in ~/.loop/plugins/:

# Discovery: returns JSON manifest
loop-plugin-docker --manifest

# Execution: params on stdin, result on stdout
echo '{"image":"nginx"}' | loop-plugin-docker --execute docker_run

The registry refreshes at the start of every task and inference turn. Plugins installed by you, or written and compiled by the agent with bash and write, become available without restarting Loop.

Skill Routing

Skills are .md files in ~/.loop/skills/ with trigger keywords. Loop routes each user input to the best-matching skill:

  • general.md — default coding assistant
  • debug.md — triggered by “fix”, “bug”, “error”, “crash”
  • review.md — triggered by “review”, “audit”, “analyze”

You can add custom skills — any .md file in the skills directory with the right header format will be auto-discovered.

MCP Server Integration

Loop speaks the Model Context Protocol — connect to any MCP server to extend the agent with external tools:

loop init
─ Configure MCP servers? Yes
─ Server name: filesystem
─ Command to start server: npx
─ Arguments: -y @modelcontextprotocol/server-filesystem /home/user/projects
  ✓ 'filesystem' connected — 11 tools found
    • read_file
    • write_file
    • list_directory
    ...

On connection, Loop:

  1. Spawns the MCP server via stdio transport
  1. Sends initialize + tools/list JSON-RPC messages
  1. Caches tool definitions to ~/.loop/mcp/<server>.json
  1. Wraps each MCP tool as a native Loop Tool trait implementation

Cached definitions are loaded on every loop startup. MCP tools are namespaced as mcp__<server>__<tool> and reconnect to their configured server when called.

Inspect, refresh, or test MCP independently of an LLM:

loop mcp list
loop mcp refresh
loop mcp call --server filesystem --tool list_directory --arguments '{"path":"."}'

Server commands, arguments, and environment variables live in the mcp_servers entries in ~/.loop/config.toml, alongside the rest of Loop’s configuration.

Charm Terminal Rendering

Loop uses the Rust port of Charm’s Lip Gloss for its built-in palette, highlighted badges, rounded panels, command manual, and REPL status views. When the gum and glow commands are available, Gum supplies an enhanced header and Glow renders assistant Markdown and direct MCP results. The full-screen dashboard uses a matching Ratatui theme.

Animated Thinking & Real-time Tokens

While the LLM is thinking, Loop shows an animated spinner with live token usage:

  ⏳ pondering · 2.3s │ 12.4k↓ 350↑

The spinner cycles through 4 frame styles (DNA helix, orbit, braille wave, bar wave) and rotates flavor text (“thinking”, “reasoning”, “analyzing”, “synthesizing”…). The ↓ and ↑ counters show cumulative input/output tokens in real time.

Iterative Thinking Mode

Append a thinking suffix to a REPL query or one-shot prompt to request self-review passes:

loop ▸ fix the checkout race and validate the patch -t
loop ▸ redesign this parser without changing its public API -t3
  • t and t1 run one pass; t2 and t3 run two or three. Values above three are rejected. Loop first completes the task, scans relevant saved directives, asks the model to generate concrete review questions about correctness, code behavior, edge cases, maintainability, patches, and validation, then feeds that guidance into another tool-capable implementation cycle. Intermediate prose stays hidden and the final pass returns one consolidated answer. Each level adds at least two model calls and therefore increases latency and API usage.

Parallel Task Execution

When you give Loop a request with multiple independent subtasks, it automatically decomposes and runs them in parallel:

loop ▸ add input validation to create_user, update_user, and delete_user endpoints

  📋 Plan: Three independent endpoint modifications (3 parallel tasks)

  ⚡ Executing 3 independent subtasks in parallel:

  [1] → Add validation to create_user
  [2] → Add validation to update_user
  [3] → Add validation to delete_user

  ⠇ [1] Add validation to create_user · tool: edit · 2.1s │ 4.2k↓ 180↑
  ◑ [2] Add validation to update_user · thinking · 1.8s │ 3.9k↓ 150↑
  ◁ [3] Add validation to delete_user · tool: read · 1.5s │ 3.1k↓ 90↑

How it works:

  1. Planning — The LLM analyzes the request and returns a structured JSON plan
  1. Dependency check — Only truly independent tasks (no data dependencies) are parallelized
  1. Parallel dispatch — Each subtask gets its own tokio task with independent inference loop
  1. Live progress — Each task gets a different spinner style (DNA helix, orbit, braille, wave, dots) so you can visually track each one
  1. Merge — Results are merged back into the main conversation context
Safety: Tasks with depends_on set are excluded from parallel execution. Simple/singular requests skip planning entirely — no overhead.

Command Reference


CLI Commands

loop                         Start the interactive REPL (default)
loop init                    Setup wizard: models, API keys, instructions, git
loop run -p "<prompt>"       One-shot mode: execute a single prompt and exit
loop run -p "<prompt> -t2"   One-shot mode with two self-review passes
loop manual                  Full command reference
loop man                     Alias for loop manual
loop --help                  Show CLI help
loop --version               Print version

REPL Commands

/help       Show REPL commands
/status     Show session status: model, tokens, context, files, directives
/tools      List all available tools with descriptions
/model      Show the active model
/clear      Clear conversation context and start fresh
/quit       Save checkpoint and exit (/exit, /q also work)

Architecture


┌─────────────────────────────────────────────────────────┐
│                         CLI Layer                        │
│  loop init  │  loop (REPL)  │  loop run  │  loop manual │
└──────┬──────┴──────┬────────┴─────┬──────┴──────────────┘
       │             │              │
┌──────▼─────────────▼──────────────▼─────────────────────┐
│                    Engine (Outer Loop)                    │
│                                                          │
│  ┌──────────┐  ┌───────────┐  ┌──────────┐  ┌────────┐ │
│  │ Directive │  │  Memory   │  │   Skill  │  │  Git   │ │
│  │Classifier │  │  Index    │  │  Router  │  │Checkpt │ │
│  └─────┬────┘  └─────┬─────┘  └────┬─────┘  └───┬────┘ │
│        │             │              │             │      │
│  ┌─────▼─────────────▼──────────────▼─────┐      │      │
│  │           Inner Inference Loop          │      │      │
│  │    LLM ←→ Tool Calls ←→ Observations   │      │      │
│  └─────────────────┬───────────────────────┘      │      │
│                    │                               │      │
└────────────────────┼───────────────────────────────┼──────┘
                     │                               │
       ┌─────────────▼──────────────┐    ┌───────────▼──────┐
       │       Tool Registry        │    │   git add/commit  │
       │ read│write│edit│bash│ls_dir│    │  (LLM message)    │
       └────────────────────────────┘    └──────────────────┘

Double-Loop Design

  • Outer Loop — orchestrates the full task: classifies input, searches memories, selects skill, manages context, handles checkpointing, records directives, triggers git commits
  • Inner Loop — drives LLM inference: sends messages → receives response → executes tool calls → feeds results back → repeats until the model says “done” or hits the iteration cap

Data Flow (Per Task)

sequenceDiagram
    participant User
    participant Classifier
    participant MemoryIndex
    participant Engine
    participant LLM
    participant Tools
    participant Git
    participant Directives

    User->>Engine: "Fix the auth bug"
    Engine->>Classifier: classify(input)
    Classifier-->>Engine: is_directive=true, category=BugFix
    Engine->>MemoryIndex: search(input, top_k=3)
    MemoryIndex-->>Engine: relevant past memories
    Engine->>Directives: search_keywords(input)
    Directives-->>Engine: matching directives
    Engine->>LLM: complete(messages + injected context)
    LLM-->>Engine: tool_call: read("src/auth.rs")
    Engine->>Tools: execute
    Tools-->>Engine: file contents
    Engine->>LLM: complete(messages + result)
    LLM-->>Engine: tool_call: edit(fix bug)
    Engine->>User: ⚡ Execute edit? [Y/n]
    User-->>Engine: Yes
    Engine->>Tools: execute edit
    Engine->>Git: commit with semantic message
    Engine->>User: 📌 Outcome? [Worked/Didn't/Partial/Skip]
    User-->>Engine: Worked
    Engine->>Directives: save directive

File Locations

PathPurpose
~/.loop/config.tomlProvider/model metadata and settings (no API keys)
~/.loop/checkpoints/*.mdSession checkpoints (Done / Doing / Next)
~/.loop/checkpoints/*.jsonMachine-readable checkpoint companions
~/.loop/directives.mdRecorded directives and outcomes
~/.loop/directives.jsonMachine-readable directive store
~/.loop/skills/*.mdSkill profiles (general, debug, review, custom)
~/.loop/mcp/*.jsonCached MCP tool definitions
~/.loop/plugins/Plugin directory

Project Structure

src/
├── main.rs                 # CLI entry point (clap dispatch)
├── error.rs                # Unified error types
├── cli/
│   ├── init.rs             # Setup wizard (animated)
│   ├── repl.rs             # Interactive REPL + one-shot mode
│   ├── manual.rs           # loop manual / loop man
│   └── animation.rs        # ASCII art, thinking spinners, token counters
├── config/
│   ├── mod.rs              # Load/save config, path helpers
│   └── types.rs            # Config structs & defaults
├── provider/
│   ├── mod.rs              # LlmProvider trait & factory
│   ├── anthropic.rs        # Claude (Messages API)
│   ├── openai.rs           # GPT-4o (Chat Completions)
│   ├── gemini.rs           # Gemini (Generative Language API)
│   ├── groq.rs             # Llama (OpenAI-compatible)
│   └── ollama.rs           # Gemma (local, Ollama API)
├── tools/
│   ├── mod.rs              # Tool trait & registry
│   ├── read.rs             # File reading
│   ├── write.rs            # File writing
│   ├── edit.rs             # Surgical string replacement
│   ├── multi_edit.rs       # Atomic batch edits across files
│   ├── bash.rs             # Shell execution
│   └── list_dir.rs         # Directory listing
├── engine/
│   ├── mod.rs              # Double-loop engine core
│   └── parallel.rs         # Task planner + parallel executor
├── mcp/
│   └── mod.rs              # MCP client, tool cache, Tool adapter
├── memory/
│   └── mod.rs              # Context management & auto-compaction
├── checkpoint/
│   └── mod.rs              # Tri-state checkpoints (.md + .json)
├── directives/
│   ├── mod.rs              # Directive store & persistence
│   ├── classifier.rs       # Input classifier (bug/correction/workaround)
│   └── embeddings.rs       # TF-IDF semantic search index
├── git/
│   └── mod.rs              # Git auto-checkpoint & commit
├── router/
│   └── mod.rs              # Keyword skill router
└── plugin/
    └── mod.rs              # CLI plugin discovery & execution

Roadmap


  • MCP integration — stdio JSON-RPC client, tool caching, Tool trait adapter
  • OpenRouter provider — verified API key and user-selected model slug
  • Iterative thinking mode — directive-aware self-review with t through t3
  • Real-time token display — animated thinking spinner with live token counters
  • Multi-file edit tool — atomic batch edits across files via multi_edit
  • Parallel execution — auto-decompose + parallel dispatch for independent subtasks
  • Streaming responses — token-by-token display in the REPL
  • Richer TUI — interactive ratatui dashboard with split panes and status bar (/status)
  • Embedding model upgrade — use a local embedding model (via Ollama) for semantic search instead of TF-IDF
  • Plugin hot-reload — detect new plugins before each task and inference turn
  • Session history — browse and search past sessions