Lok
Declarative multi-LLM orchestration. Define workflows in TOML, run them against multiple backends, get synthesized results.
What It Is
- Multi-backend queries: Ask the same question to Claude, Codex, Gemini, and Ollama in parallel, then synthesize or vote on the results
- Declarative workflows: TOML files that define multi-step LLM pipelines with dependencies, retries, and error handling
- Backend abstraction: Swap
backend = "claude"forbackend = "ollama"without changing your workflow logic
What It's Not
- Not an agent: Lok doesn't make decisions or write code. It runs queries and returns results. Use it with an agent (Claude Code, Cursor, etc.) that acts on the output.
- Not a wrapper for one LLM: If you only use Claude, you don't need lok. The value is in multi-backend orchestration and consensus.
Quick Start
Example lok doctor output when backends are configured:
Checking backends...
✓ codex - ready
✓ gemini - ready
✓ claude - ready
✓ 3 backend(s) ready.
Prerequisites
Lok wraps existing LLM CLI tools. Install the ones you want to use:
| Backend | Install | Notes |
|---|---|---|
| Codex | npm install -g @openai/codex |
Fast code analysis |
| Gemini | npm install -g @google/gemini-cli |
Deep security audits |
| Claude | claude.ai/download | Claude Code CLI |
| Ollama | ollama.ai | Local models, no API keys |
For issue/PR workflows, you also need:
| Tool | Install | Used by |
|---|---|---|
| gh | cli.github.com | lok run fix, lok run review-pr |
Run lok doctor to see which backends are detected. Core commands (lok ask,
lok hunt, lok audit) work without gh.
Commands
Analysis
Code Review
Issue Management
Multi-Agent Modes
Workflows
Utilities
Workflows
Workflows are TOML files that define multi-step LLM pipelines. Steps can depend on previous steps and run in parallel when possible.
# .lok/workflows/example.toml
= "example"
[[]]
= "scan"
= "codex"
= "Find obvious issues in this codebase"
[[]]
= "deep-dive"
= "gemini"
= ["scan"]
= "Investigate these findings: {{ steps.scan.output }}"
[[]]
= "comment"
= ["deep-dive"]
= "gh issue comment 123 --body '{{ steps.deep-dive.output }}'"
Workflow Resolution
Lok searches for workflows in this order (first match wins):
- Project:
.lok/workflows/{name}.toml - User:
~/.config/lok/workflows/{name}.toml - Embedded: Built into the lok binary
This means you can override any built-in workflow by creating your own version at the project or user level.
Built-in Workflows
Lok ships with these workflows embedded in the binary:
| Workflow | Description |
|---|---|
diff |
Review git changes with multiple backends |
explain |
Explain codebase structure and architecture |
audit |
Security audit with multiple backends |
hunt |
Bug hunt with multiple backends |
Run lok workflow list to see all available workflows. Built-in workflows show
as "(built-in)", which you can override by creating your own version:
# Create override:
Consensus and Error Handling
For multi-backend steps, you can require consensus and handle partial failures.
Workflow-level defaults apply to all steps (steps can override):
= "my-workflow"
= true # All steps continue on failure by default
= 300000 # All steps get 5 minute timeout by default
[[]]
= "fast_step"
= "codex"
= 60000 # Override: this step gets 1 minute
= "Quick analysis..."
[[]]
= "critical_step"
= "claude"
= false # Override: this step must succeed
= "Important work..."
Step-level consensus for multi-backend synthesis:
[[]]
= "propose_claude"
= "claude"
= "Propose a fix..."
[[]]
= "propose_codex"
= "codex"
= "Propose a fix..."
[[]]
= "debate"
= "claude"
= ["propose_claude", "propose_codex", "propose_gemini"]
= 2 # Need at least 2/3 backends to succeed
= "Synthesize the proposals: {{ steps.propose_claude.output }}..."
When min_deps_success is set:
- Step runs if at least N dependencies succeeded
- Failed dependencies with
continue_on_errorpass their error output to the prompt - Logs "consensus reached (2/3 succeeded)" when threshold is met
This prevents wasted tokens when one backend times out or hits rate limits.
Hard vs Soft Failures
Steps fail in two ways:
- Hard failure: Step fails and workflow stops. This is the default behavior.
- Soft failure: Step fails but workflow continues. Enabled with
continue_on_error = true.
When a soft failure occurs, the error message is passed to dependent steps instead of the normal output. This lets downstream steps handle the failure gracefully:
[[]]
= "risky_step"
= "gemini"
= true # Soft failure - workflow continues
= "..."
[[]]
= "handler"
= ["risky_step"]
= """
{% if "error" in steps.risky_step.output %}
Handle the error: {{ steps.risky_step.output }}
{% else %}
Process result: {{ steps.risky_step.output }}
{% endif %}
"""
Retries
Steps can retry on transient failures with exponential backoff:
[[]]
= "flaky_backend"
= "gemini"
= 3 # Retry up to 3 times (default: 0)
= 2000 # Start with 2 second delay (default: 1000ms)
= "..."
The delay doubles after each retry: 2s, 4s, 8s. Retries help with rate limits
and temporary network issues. After all retries are exhausted, the step fails
normally (hard or soft depending on continue_on_error).
Agentic Features
Workflows can apply code edits and verify them:
[[]]
= "fix"
= "claude"
= true
= "cargo build"
= """
Fix this issue. Output JSON:
{"edits": [{"file": "src/main.rs", "old": "...", "new": "..."}]}
"""
How apply_edits works:
- Parses JSON from LLM output looking for
{"edits": [...]} - For each edit, finds
oldtext infileand replaces withnew - If
verifyis set, runs the command after edits - If verification fails, the step fails (edits remain applied)
Risks and failure modes:
- File not found: Edit fails if the target file doesn't exist
- Text not found: Edit fails if
oldtext isn't in the file - Ambiguous match: Edit fails if
oldtext appears multiple times - Partial application: If edit 3 of 5 fails, edits 1-2 remain applied
Automatic rollback with git-agent:
If git-agent is installed and initialized, lok automatically creates a checkpoint before applying edits. If edits fail or verification fails, lok rolls back to the checkpoint.
# Install git-agent
# Initialize in your project
# Now lok will auto-checkpoint before apply_edits
When git-agent is active, you'll see:
→ Applying edits...
✓ git-agent checkpoint created
✓ Applied 3 edit(s)
verify: cargo build
✗ Verification failed: ...
↩ Rolled back via git-agent
Without git-agent, lok still works but won't auto-rollback.
Recommendations:
- Use git-agent for automatic rollback on failures
- Start with
verifycommands to catch bad edits early - Review LLM output before running with
--applyin production - Keep
oldtext specific enough to match exactly once
Structured Output
Workflows can produce JSON output for programmatic consumption. Use the
output_format field to control how LLM responses are parsed:
[[]]
= "analyze"
= "codex"
= "json" # Parse output as JSON
= "Return findings as JSON array..."
Output format options:
text(default): Raw text outputjson: Parse as JSON objectjson_array: Parse as JSON arrayjsonl: Parse as newline-delimited JSON
Downstream steps can access parsed fields:
[[]]
= "report"
= ["analyze"]
= "Summarize: {{ steps.analyze.output.findings }}"
Configuration
Works without config. For customization, create lok.toml or
~/.config/lok/lok.toml:
[]
= true
= 300
# Wrap shell commands for isolated environments (NixOS, Docker)
# command_wrapper = "nix-shell --run '{cmd}'"
# command_wrapper = "docker exec dev sh -c '{cmd}'"
[]
= true
= "codex"
= ["exec", "--json", "-s", "read-only"]
[]
= true
= "http://localhost:11434"
= "qwen2.5-coder:7b"
[]
= true
= 24
Command Wrapper (NixOS/Docker)
If you use isolated environments, shell commands in workflows may fail due to
missing dependencies. Use command_wrapper to wrap all shell commands:
[]
# For NixOS with nix-shell
= "nix-shell --run '{cmd}'"
# For Docker
= "docker exec dev sh -c '{cmd}'"
# For direnv
= "direnv exec . {cmd}"
The {cmd} placeholder is replaced with the actual command.
Backend Strengths
| Backend | Best For | Speed |
|---|---|---|
| Codex | Code patterns, N+1, dead code | Fast |
| Gemini | Security audits, deep analysis | Slow (thorough) |
| Claude | Orchestration, reasoning | Medium |
| Ollama | Local/private, no rate limits | Varies |
Real World Results
Lok found 25 bugs in its own codebase, then found a real bug in Discourse (35k stars) that became a merged PR.
Why "Lok"?
Swedish/German: Short for "lokomotiv" (locomotive). The conductor sends trained models down the tracks.
Sanskrit/Hindi: "lok" means "world" or "people", as in "Lok Sabha" (People's Assembly). Multiple agents working as a collective.
License
MIT