Declarative multi-LLM orchestration. Define workflows in TOML, run them against multiple backends, get synthesized results.
- 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
- 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.
cargo install lokomotiv # Package is "lokomotiv", binary is "lok"
lok doctor # Check what backends are available
lok ask "Explain this code" # Query all available backends
lok hunt . # Find bugs in current directoryExample lok doctor output when backends are configured:
Checking backends...
✓ codex - ready
✓ gemini - ready
✓ claude - ready
✓ 3 backend(s) ready.
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.
lok ask "Find N+1 queries" # Query all backends
lok ask -b codex "Find dead code" # Specific backend
lok hunt . # Bug hunt (multiple prompts)
lok hunt --issues # Bug hunt + create GitHub issues
lok audit . # Security audit
lok explain # Explain codebase structurelok diff # Review staged changes
lok diff main..HEAD # Review branch vs main
lok run review-pr 123 # Multi-backend PR review + commentlok run fix 123 # Analyze issue, propose fix, comment
lok ci 123 # Analyze CI failureslok debate "Should we use async here?" # Backends argue and refine
lok spawn "Build a REST API" # Break into parallel subtasks
lok conduct "Find and fix perf issues" # Fully autonomouslok run workflow-name # Run a workflow
lok workflow list # List available workflowslok doctor # Check installation
lok backends # List configured backends
lok suggest "task" # Suggest best backend for task
lok init # Create config fileWorkflows 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
name = "example"
[[steps]]
name = "scan"
backend = "codex"
prompt = "Find obvious issues in this codebase"
[[steps]]
name = "deep-dive"
backend = "gemini"
depends_on = ["scan"]
prompt = "Investigate these findings: {{ steps.scan.output }}"
[[steps]]
name = "comment"
depends_on = ["deep-dive"]
shell = "gh issue comment 123 --body '{{ steps.deep-dive.output }}'"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.
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:
lok workflow list # Shows: diff (built-in)
# Create override:
mkdir -p .lok/workflows
lok run diff > /dev/null # See what it does, then customize:
cat > .lok/workflows/diff.toml << 'EOF'
name = "diff"
description = "My custom diff review"
# ... your custom steps
EOF
lok workflow list # Now shows: diff (local)For multi-backend steps, you can require consensus and handle partial failures.
Workflow-level defaults apply to all steps (steps can override):
name = "my-workflow"
continue_on_error = true # All steps continue on failure by default
timeout = 300000 # All steps get 5 minute timeout by default
[[steps]]
name = "fast_step"
backend = "codex"
timeout = 60000 # Override: this step gets 1 minute
prompt = "Quick analysis..."
[[steps]]
name = "critical_step"
backend = "claude"
continue_on_error = false # Override: this step must succeed
prompt = "Important work..."Step-level consensus for multi-backend synthesis:
[[steps]]
name = "propose_claude"
backend = "claude"
prompt = "Propose a fix..."
[[steps]]
name = "propose_codex"
backend = "codex"
prompt = "Propose a fix..."
[[steps]]
name = "debate"
backend = "claude"
depends_on = ["propose_claude", "propose_codex", "propose_gemini"]
min_deps_success = 2 # Need at least 2/3 backends to succeed
prompt = "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.
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:
[[steps]]
name = "risky_step"
backend = "gemini"
continue_on_error = true # Soft failure - workflow continues
prompt = "..."
[[steps]]
name = "handler"
depends_on = ["risky_step"]
prompt = """
{% if "error" in steps.risky_step.output %}
Handle the error: {{ steps.risky_step.output }}
{% else %}
Process result: {{ steps.risky_step.output }}
{% endif %}
"""Steps can retry on transient failures with exponential backoff:
[[steps]]
name = "flaky_backend"
backend = "gemini"
retries = 3 # Retry up to 3 times (default: 0)
retry_delay = 2000 # Start with 2 second delay (default: 1000ms)
prompt = "..."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).
Workflows can apply code edits and verify them:
[[steps]]
name = "fix"
backend = "claude"
apply_edits = true
verify = "cargo build"
prompt = """
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
cargo install --git https://github.com/ducks/git-agent
# Initialize in your project
git-agent init
git-agent begin "Working on feature X"
# Now lok will auto-checkpoint before apply_edits
lok run my-workflow # Creates checkpoint, applies edits, rolls back on failureWhen 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
Workflows can produce JSON output for programmatic consumption. Use the
output_format field to control how LLM responses are parsed:
[[steps]]
name = "analyze"
backend = "codex"
output_format = "json" # Parse output as JSON
prompt = "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:
[[steps]]
name = "report"
depends_on = ["analyze"]
prompt = "Summarize: {{ steps.analyze.output.findings }}"Works without config. For customization, create lok.toml or
~/.config/lok/lok.toml:
[defaults]
parallel = true
timeout = 300
# Wrap shell commands for isolated environments (NixOS, Docker)
# command_wrapper = "nix-shell --run '{cmd}'"
# command_wrapper = "docker exec dev sh -c '{cmd}'"
[backends.codex]
enabled = true
command = "codex"
args = ["exec", "--json", "-s", "read-only"]
[backends.ollama]
enabled = true
command = "http://localhost:11434"
model = "qwen2.5-coder:7b"
[cache]
enabled = true
ttl_hours = 24If you use isolated environments, shell commands in workflows may fail due to
missing dependencies. Use command_wrapper to wrap all shell commands:
[defaults]
# For NixOS with nix-shell
command_wrapper = "nix-shell --run '{cmd}'"
# For Docker
command_wrapper = "docker exec dev sh -c '{cmd}'"
# For direnv
command_wrapper = "direnv exec . {cmd}"The {cmd} placeholder is replaced with the actual command.
| 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 |
Lok found 25 bugs in its own codebase, then found a real bug in Discourse (35k stars) that became a merged PR.
lok hunt ~/dev/discourse --issues -y # Found hardlink limit bugSwedish/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.
MIT