Orchestration layer for multiple LLM backends. Run it in your project directory and it coordinates multiple LLMs to analyze your code, find bugs, and propose fixes.
An agentic LLM (Claude Code, Gemini CLI, etc.) acts as the conductor, running lok commands and implementing the results.
1. Conductor: Runs lok run fix 123
2. Lok analyzes: Fetches issue, queries Claude + Codex, synthesizes proposals
3. Lok comments: Posts consensus proposal to the GitHub issue
4. Conductor: Reads the proposal, implements the fix, pushes a PR
5. Human: Reviews and approves
The conductor is the brain (an agentic LLM). Lok is the orchestra (multiple specialized backends). The human provides oversight.
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