Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

816 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

⛵️ lopi

ᨒᨚᨄᨗlopi: Konjo for "boat." Load it, sail it, trust it, bring it home.

Multi-agent Claude Code orchestrator, written in Rust. Runs concurrent agents in git-isolated branches, with retry loops, persistent memory, a TUI + web dashboard, and a native macOS app.

By KonjoAI · MIT licensed · v0.36.0 crates.io

lopi run     # queue and run a task
lopi watch   # live TUI of in-flight agents
lopi sail    # start the web dashboard (the Forge)
lopi tail    # stream logs
lopi dock    # list every task

What lopi is

You give lopi a goal — a failing test, a CI red, "fix the lint warnings in src/auth/." lopi hands it to a Claude Code agent, isolates the work on its own branch, and runs it through a scored loop: Plan → Implement → Test → Score → Retry → PR. Multiple agents run concurrently, each sandboxed to its own git branch and worktree, with automatic rollback if anything goes wrong.

It's the Rust successor to the OpenClaw-style single-agent Python prototype — rebuilt for concurrency, safety, and persistence. See LOPI_VS_OPENCLAW.md for the full feature-by-feature comparison.

Runs on your existing Claude subscription — for the worker itself. lopi drives the official claude CLI as a subprocess (claude -p) and deliberately scrubs any inherited ANTHROPIC_API_KEY/ANTHROPIC_BASE_URL env vars before every spawn, so the CLI always falls back to your on-disk ~/.claude subscription credentials rather than silently billing an API key. No separate API key is required to plan, implement, or fix.

As of Sprint F1, that's also true of the checker layers. The verifier (crates/lopi-agent/src/runner/verifier_runner.rs), LLM-judge acceptance (runner/eval_runner.rs), and post-mortem (runner/postmortem_runner.rs) all pick a backend automatically — a direct-API AnthropicClient (Runner::with_api) when one is configured, the claude CLI otherwise (crates/lopi-agent/src/verifier_cli.rs, crates/lopi-agent/src/runner/postmortem_cli.rs). Nothing in the built binary calls with_api (grep -rn "with_api" crates/lopi-orchestrator/ src/ still comes back empty), so the CLI backend is what every real deployment actually runs on — subscription auth, no separate API key, same as the worker. A checker session runs read-only (Write, Edit, MultiEdit, NotebookEdit, Bash, and sub-agent/plan-mode tools all denied — see .konjo/killtests/F1/KT-1.2.md) and as a fresh, never-resumed session, so it never sees the worker's own reasoning unless a caller explicitly opts into that (VerifierAgent::with_plan_context).

A checker session does not currently pass --bare — a live test in this sprint's own sandboxed session found --bare fails authentication outright there (.konjo/killtests/F1/KT-1.3.md), a stronger reason than "needs project context," and one that has not yet been re-verified on a plain local install. Until that's confirmed, a checker session loads hooks/CLAUDE.md/skills like a normal session, which costs somewhat more per grading pass than the brief's original --bare design assumed.

Two things sprint F1 explicitly did not ship, both gated on measurement the brief requires and this session couldn't run (see Requirements for why — the same attended, hardware-required corpus run Sprint F0's Phase 3 already deferred): a cheaper two-tier checker (cheap first pass, Opus escalation only on low confidence), and --append-system-prompt reaching worker sessions. Both remain tracked in NEXT_SESSION_PROMPT.md.

The one exception to all of the above is the Layer 5 stability gate: lopi run --stability-gate wires its own, separate AnthropicClient from ANTHROPIC_API_KEY at the CLI layer (src/run_command.rs) and requires a real API key for that specific gate. (A couple of standalone server components — see Configuration — can optionally take a direct Anthropic API key for their own use; that's unrelated to the above.)

Highlights

  • Concurrent, git-isolated agents — an AgentPool runs N agents at once (default 4), each on its own lopi/<task_id>-attempt-<n> branch and worktree. Off-limits path globs and automatic hard rollback on any scope violation keep runs contained.
  • Scored retry loop — a weighted composite score (tests + lint + a capped per-1000-line diff-size penalty) decides accept / retry / rollback. Failed attempts feed a SQLite pattern library so later re-plans are seeded with what already failed. Optional Reflexion-style adaptive retry and a Layer 5 stability gate (variance-checked plan sampling) are available per run.
  • Granular budget controls — per-run USD caps (--budget), named presets (quick/standard/deep/unlimited), token budgets, and a repo-level .lopi/loop.toml — because an unwired budget is how a session turns into a bill you didn't expect.
  • Three UI surfaces — a ratatui terminal TUI (lopi watch), a SvelteKit web dashboard ("the Forge," served by lopi sail), and a native SwiftUI macOS app (in macos/) that talks to the same REST + WebSocket API.
  • Remote control from your phone — the native macOS/iOS app talks to the same REST + WebSocket API lopi sail serves. (A Telegram bot previously filled this role; removed in Sprint S10, Phase 4 — see LEDGER.md. A Twilio WhatsApp handler exists in lopi-remote::whatsapp but is not wired to any CLI command and is unreachable from the built binary today — see docs/security/TRIFECTA_PATHS.md §1 row D and the CHANGELOG.)
  • Event-driven, not just manual — a GitHub webhook listener turns CI failures into auto-queued fix tasks; cron-style schedules ([[schedules]] in lopi.toml, editable live from the dashboard) handle recurring work like nightly lint sweeps.
  • Ships into Claude itself — a Claude Code plugin (lopi mcp-serve) and a Claude Desktop extension (MCPB, mcpb/) expose task submission and live stack status as MCP tools, with a clickable status widget inside Claude Desktop.
  • Self-improvinglopi spec extracts a repo's spec surface from its tests, lopi gap-fill turns coverage gaps into queued fix tasks, and lopi skill promote turns recurring lessons into reviewable SKILL.md drafts.

Requirements

To… You need
Run agents at all The claude CLI, logged into a Claude subscription (Pro, Max, Team, or Enterprise) — lopi drives it as a subprocess and never needs a separate API key
Build from source A stable Rust toolchain (edition 2021; verified with 1.89–1.94) and git
Build the Forge web dashboard Node.js 18+ and npm — optional; without it, lopi sail serves a placeholder page instead
Build the native macOS app Xcode 15+ and XcodeGen, macOS 14+ (only needed for macos/)

lopi itself runs on macOS or Linux; the native app is macOS-only. No database server, no Docker, no cloud account — everything (SQLite, git worktrees, the web dashboard) is local to the machine you run it on.

Quickstart

Via Homebrew (macOS/Linux):

brew install konjoai/lopi/lopi

Via crates.io:

cargo install lopi

From source:

git clone https://github.com/konjoai/lopi.git
cd lopi
cargo build --release
cp lopi.toml.example lopi.toml
./target/release/lopi run --goal "fix the failing test in src/foo.rs" --repo .

Just want to look around first? lopi demo fabricates a complete, synthetic dashboard — repos, tasks in every status, agent traffic, token counts, a quality trend, a couple of honest failures — and opens it. No config, no credentials, no real repo touched; every surface is watermarked 🧪 SYNTHETIC DATA.

lopi demo

lopi demo — Budget view, synthetic data, watermarked lopi demo — Loop Stacks view, synthetic data, watermarked

Screenshots above are from lopi demo --seed 42 — the seed is recorded so they're reproducible byte-for-byte. Every demo surface — including the GET /api/stats/GET /api/budget/breakdown JSON, the MCP widget payload, and the TUI (lopi watch --demo) — carries the same non-dismissible synthetic marker; see docs/MEASUREMENT.md.

Building the web dashboard is optional but recommended (cd web && npm install && npm run build) — without it, lopi sail serves a placeholder page instead of the Forge UI (the Homebrew formula builds it for you; cargo install ships without it — build from source if you want the Forge UI from a cargo install). See docs/RUNNING.md for the full build/run guide, including the native macOS app.

New to lopi? Start with recipes/ rather than a blank .lopi/loop.toml. Six ready-to-copy loop configs — fix-failing-test, lint-burndown, dependency-bump, flaky-test-hunter, doc-drift-check, triage-issues — each with a README explaining what it does, the simpler thing it beats, and its measured cost/duration from a real run. Copy one into .lopi/loop.toml, adjust it for your repo, run it.

CLI

lopi is a single binary. Bare lopi (no args) drops into an interactive REPL. The full surface:

Command What it does
lopi run --goal "<g>" --repo <path> Run one agent task, stream status to stdout
lopi bypass <goal…> Run with directory restrictions disabled (trusted envs only)
lopi watch Live TUI — agent status (--remote <ws>, --local, or --demo)
lopi sail Web dashboard + agent pool (single- or multi-repo)
lopi demo Generate (if absent) and open a fully synthetic dashboard — zero setup (--seed, --reset, --off, --path)
lopi tail / lopi dock Stream events / list all tasks
lopi cancel <id> / lopi resume --agent-id <id> Cancel / resume a task
lopi learn / lopi stability / lopi trust Browse mined patterns / stability ledger / trust stats
lopi schedules list Scheduled tasks + next run times
lopi loop show|validate --repo <path> Inspect / validate a repo's .lopi/loop.toml
lopi worktree list|gc Manage per-task git worktrees
lopi skill promote Promote recurring lessons into skill drafts
lopi gap-fill / lopi spec / lopi check Test-driven fix queue / spec surface / KCQF quality gate
lopi replay --task <id> Inspect a task's DAG trace and replay from a given stage
lopi diag Export a diagnostic snapshot (tasks, logs, audit, stability) as committable JSON
lopi mcp-serve Serve lopi's MCP tools over stdio — the Claude Code / Desktop entry point
lopi serve-webhooks Standalone GitHub webhook server (CI-failure → task injection)

Architecture

17 crates in a Cargo workspace:

Crate Role
lopi-core Shared types: Task, Score, LopiConfig
lopi-agent Claude Code subprocess wrapper, retry runner, and scoring
lopi-context Token-budget context window with phase-aware eviction
lopi-git Branch management + path diff validation for agent runs
lopi-memory SQLite-backed store for tasks, patterns, turn metrics, lessons
lopi-orchestrator Concurrent agent pool, priority task queue, scheduler
lopi-ui ratatui TUI + axum web/JSON API (the Forge)
lopi-remote A Twilio WhatsApp webhook handler; not wired to any CLI command and isn't reachable from the built binary today (the Telegram bot that used to live here was removed in Sprint S10, Phase 4)
lopi-webhook GitHub webhook receiver — CI-failure/PR/issue triage → tasks
lopi-mcp MCP client — lopi agents discovering and calling external tools
lopi-tools Durable tool registry (specs, timeouts, retry budgets)
lopi-skill Runtime registry of SKILL.md project knowledge
lopi-spec Spec surface extractor (tests → machine-readable coverage inventory)
lopi-ratelimit Token-bucket rate limiting + Anthropic concurrency controls
lopi-toon Token-Oriented Object Notation encoder (measured 3.3% fewer cl100k tokens than compact JSON on lopi's real prompt payloads — see crates/lopi-toon/benches/results/)
lopi-github Thin GitHub REST client for write operations (PRs, labels, comments)
lopi-demo Deterministic, seeded synthetic-store generator behind lopi demo — also used as a test fixture

Plus macos/ (native SwiftUI dashboard), web/ (SvelteKit source for the Forge), mcpb/ (Claude Desktop extension), and plugin/ (Claude Code plugin manifest).

Configuration

Copy lopi.toml.example to lopi.toml and edit. Key sections:

  • [lopi] — max concurrent agents, log level, SQLite DB path
  • [claude]claude CLI path and per-call timeout
  • [git] — allowed/forbidden directories, auto-PR toggle
  • [remote.telegram] — no longer wired to anything (Sprint S10, Phase 4); the section still parses for backward compatibility with an existing lopi.toml, but no code reads it. [remote.whatsapp] / Twilio credentials exist as config surface but the handler behind them is not wired to any CLI command — see Highlights above.
  • [web] — dashboard host/port/auth (see Security below)
  • [[schedules]] — cron-style recurring tasks (also editable live from the dashboard)

Per-repo budget and safety policy lives in .lopi/loop.toml (see lopi loop show|validate). lopi serve-webhooks accepts an optional standalone ANTHROPIC_API_KEY for its own automated triage; this is separate from, and unrelated to, how the core agent loop authenticates.

Scope

lopi is built for one operator, on one machine, alongside your Claude Code install. There is no multi-tenancy: no per-customer isolation, no subscription tiers, no hosted-service surface. lopi sail's dashboard/API authenticates the operator, not a set of separate users — see Security below. Multi-tenant or hosted deployment (serving multiple customers from one lopi instance) is explicitly out of scope and unsupported; the GitHub App OAuth + Stripe billing server that once shipped for that purpose (lopi serve-app) was removed in Sprint S12 for exactly this reason — see LEDGER.md. If you need to run lopi for more than one person, run one instance per person.

Safety

  • Git-isolated branches per attempt; base branch is never touched by rollback. The attempt's worktree is torn down automatically (crates/lopi-git/src/worktree.rs, RAII Drop), but the branch ref itself is not auto-deleted — it's reclaimed by a separate, manual lopi worktree gc sweep (crates/lopi-git/src/worktree.rs, WorktreeManager::gc/delete_stale_branches).
  • DiffChecker: off-limits glob patterns, full path scan before any change is accepted. Diff size itself is a capped scoring penalty (default 0.10 per 1 000 lines, capped at 0.30 total), not a hard line-count limit.
  • allow_self_modify: false by default (crates/lopi-core/src/config.rs) blocks lopi from modifying its own src//crates/ — but only on the lopi run CLI and REPL paths (src/run_command.rs, src/repl/actions.rs). The lopi sail web dashboard's task-creation API and the lopi_submit_task MCP tool do not currently check it; this is a real gap, not a documentation nuance, and is tracked for a follow-up sprint rather than silently left off this list.
  • A diff-scope violation triggers hard rollback (git reset --hard, crates/lopi-git/src/manager.rs) and marks the attempt RolledBack — but the task is retried afterward (with model escalation toward Opus on repeated failure), up to max_retries, same as any other failed attempt (crates/lopi-agent/src/runner/test_phase.rs, runner/run_loop.rs). It is not a terminal stop condition.
  • lopi never auto-merges. Every PR requires human review — from the dashboard or the macOS app.
  • A task originating from an untrusted source — a GitHub webhook (issue, CI failure, PR review) — is held for plan approval before the runner acts on it, regardless of its configured autonomy level. See docs/security/TRIFECTA_PATHS.md.

Security

lopi sail's dashboard/API is auth-required by default — it refuses to start without [web].auth_token (or LOPI_WEB_AUTH_TOKEN) unless you pass --insecure-no-auth explicitly, and that opt-out itself refuses to start on any non-loopback --host. CORS on /api/* defaults to an explicit origin allowlist (the local dev origins the web app uses); set [web].cors_allowed_origins for other origins, or [web].cors_permissive = true to opt out entirely. lopi serve-webhooks refuses to start without LOPI_WEBHOOK_SECRET unless LOPI_ALLOW_UNVERIFIED_WEBHOOK=1 is set. GET /api/tasks and GET /api/tasks/:id include a provenance field ("operator" or "untrusted") showing whether a run came from an authenticated human action (CLI, API, or a historical Telegram-sourced task — the transport was removed in Sprint S10, Phase 4, but old rows still deserialize and read as "operator") or an untrusted source (a GitHub webhook) — recorded and surfaced today, not yet gated on. Repo-supplied shell commands (.lopi/loop.toml gate/until/test_command) are untrusted by default for the same reason and refuse to run for an untrusted-sourced task (Sprint S10, Phase 0); MCP servers are allowlisted deny-by-default (Sprint S10, Phase 5). Full inventory and rationale: docs/security/TRIFECTA_PATHS.md and docs/security/EGRESS_SURFACE.md.

Measurement

Every number lopi displays states what it measures and where it came from — lopi reports what it can observe directly, and says so plainly when a number is unavailable rather than estimating without saying so or obtaining it through means the source didn't intend. No bypassing bot protection, no undocumented internal APIs, no stored third-party session credentials, ever, for a nicer statistic. Full policy, including how dollar estimates are versioned and degrade when stale: docs/MEASUREMENT.md.

Contributing / feedback

Issues and PRs welcome at github.com/konjoai/lopi.

License

MIT © KonjoAI

About

⛵️ High-performance Rust orchestrator for Claude Code agents. Runs concurrent agents in git-isolated branches with retry loops, priority queuing, SQLite memory, pattern mining, scheduled tasks, and live TUI + web dashboard. Remote control via Telegram.

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages