algocline
Research-grade reasoning for your LLM — as Pure Lua you can read, edit, and ship.
UCB1 exploration, self-reflection, multi-agent debate, chain-of-verification — each strategy is a single Lua file. Install with cargo install, customize anything, no framework lock-in.
What it does
LLMs are single-shot by default. Ask a question, get one answer, hope it's right. Research papers describe techniques that do better — iterating, scoring, selecting — but using them today means Python, framework lock-in, and API key management.
algocline makes these techniques immediately usable. Each algorithm is a Pure Lua file (100-350 lines) that runs inside your existing MCP host. No infrastructure. No setup beyond alc init.
Human ──→ LLM (MCP host) ──→ algocline ──→ Algorithm (Lua) ──→ alc.llm() ──→ back to LLM
↑ │
└──────────────┘
(loop until done)
Use existing algorithms with one call. Build your own by writing Lua. Share them via Git.
When to use
| What you want to do | What happens | Strategy |
|---|---|---|
| Catch what your code review missed | Draft → self-critique → revise. Found that std::fs::write is non-atomic (crash = data loss) after the first draft said "no issues" |
reflect |
| Make design decisions structurally, not by gut | Advocate, critic, and pragmatist debate. Resolved "do we need log rotation?" → "no, but add a display limit" in 4 rounds | panel |
| Fact-check LLM claims before trusting them | Auto-generates verification questions → checks each one. Escalated a path traversal from medium to high severity after confirming PathBuf::join() does zero validation |
cove |
| Break down a complex problem step by step | Builds reasoning chain incrementally. Structured Vec vs HashMap trade-offs into cache locality → memory overhead → API ergonomics | cot |
| Pick the best option from multiple candidates | Scores each candidate on your criteria, pairwise comparison | rank |
| Get a reliable answer, not a lucky one | Generates N answers → majority vote picks the most consistent one | sc |
Why algocline
| Approach | Limitation |
|---|---|
| Prompt engineering | Single-shot. No iteration, no scoring, no selection |
| DSPy / LangGraph | Python infra. Pip install. API keys. Framework lock-in |
| algocline | Zero infra. Pure Lua. Runs inside your existing MCP host. Use research-grade algorithms today, or write your own |
Quick start
1. Install
2. Initialize packages
This downloads the bundled package collection (UCB1, chain-of-thought, self-consistency, etc.) into ~/.algocline/packages/. Run alc init --force to overwrite existing packages.
It also distributes alc.d.lua — LuaCats type definitions for all alc.* StdLib functions — to ~/.algocline/types/alc.d.lua. This enables editor completion in any editor backed by lua-language-server. If .luarc.json is not present in the current directory, a setup tip is printed.
Note: If you skip this step, packages are auto-installed on first
alc_advicecall via MCP. But runningalc initupfront is recommended for faster first use and offline availability.
3. Add to your MCP config
By default, alc runs as an MCP stdio server — it is designed to be launched by an MCP client
such as Claude Code, not invoked directly from a shell. For one-shot maintenance tasks, use the
alc init and alc update subcommands. For ad-hoc shell access to algocline MCP tools, set up
an agent-block harness that starts alc as a child process and calls the MCP server directly;
running plain alc from a terminal will start the server in the foreground waiting for an MCP
client connection. Run alc --help for a summary of available subcommands.
Add algocline as an MCP server in your host's configuration (e.g. Claude Code's ~/.claude.json, Cursor's MCP settings, etc.):
Add an env object only when you need to set one of the environment
variables documented below.
After adding the config, restart your MCP host session so it picks up the new server.
Environment variables
| Variable | Description | Default |
|---|---|---|
ALC_LOG_DIR |
Directory for session transcript logs | ~/.algocline/logs |
ALC_LOG_LEVEL |
full (enable logging) or off (disable) |
full |
ALC_PACKAGES_PATH |
Additional package search paths (colon-separated). Takes priority over ~/.algocline/packages/ |
(none) |
ALC_PROJECT_ROOT |
Project root directory for project-local package resolution. When omitted, auto-detected by walking up from cwd to find alc.toml |
(auto-detect) |
ALC_POOL_STATE_DIR |
Directory for pool registry state (registry.json). Overrides the default ~/.algocline/state/pool/ |
~/.algocline/state/pool |
ALC_POOL_IDLE_TIMEOUT |
Idle timeout in seconds for pool worker subprocesses. Set to 0 to disable. Only relevant when using host_mode: true |
1800 |
ALC_CARD_SINKS |
Card subscriber backends (mirror destinations) as a |-separated URI list. v1 only accepts file:///absolute/path. Duplicate URIs are first-wins; malformed entries are logged and skipped. |
(none) |
Example: writing logs to a custom directory:
Optional: Install as a Claude Code plugin
If you use Claude Code, you can install algocline together with a curated set of development tools as a single plugin instead of editing ~/.claude.json by hand:
/plugin marketplace add ynishi/algocline
/plugin install alc@algocline
The plugin bundles:
algoclineandgit-readerMCP server entries (no manualmcp.jsonedit needed),- the
/alc-wakeand/alc-buildskills, - the
@alc-adviser/@alc-coder/@alc-refineragents for algocline package development.
See plugins/alc/README.md for details.
4. Use
Call algocline tools from your MCP host. The host LLM calls these tools on your behalf:
One-liner with an installed package:
alc_advice({ strategy: "ucb", task: "Design a rate limiter for a REST API" })
Or write your own Lua:
-- alc_run({ code: "..." })
local draft = alc.
local critique = alc.
local final = alc.
return
To pass environment variables to the Lua VM, use ctx.env:
alc_run({
code: "return { api_url = alc.env.API_URL }",
ctx: {
env: {
inject: { "API_URL": "https://example.com" }
}
}
})
ctx.env supports three sources merged in priority order (inject > dotenv > os.env):
inject— key/value map supplied inline (highest priority)dotenv— path to a.envfile relative to the project rootallow_os: true— expose the server's OS environment (opt-in, lowest priority)
An optional alc.toml [env.allow] allowlist filters which keys reach the Lua VM. Writes to alc.env always raise a runtime error — the snapshot is immutable for the lifetime of the run.
Ecosystem
algocline is a single binary, but the strategies and tools around it form a distributed ecosystem of Git-hosted package collections.
Repository structure
algocline — Engine (Rust binary, MCP server)
├── algocline-bundled-packages — 15 core strategies (cot, reflect, ucb, panel, …)
├── algocline-swarm-frame — Multi-agent orchestration (swarm_frame + plugins)
├── evalframe — Evaluation framework (graders, scenarios, Cards)
└── alc plugin — Claude Code skills + agents for pkg development
Each collection is an independent Git repo with a hub_index.json catalog. alc init installs the bundled collections; third-party collections install the same way via alc_pkg_install.
Hub: decentralized package discovery
There is no central registry. Each collection repo ships a hub_index.json that describes its packages. alc_hub_search queries across all cached indices.
alc_hub_search({ query: "reasoning" }) → matches from all registered sources
alc_hub_info({ name: "reflect" }) → metadata, Cards, aliases, stats
Author workflow
Write a strategy, test it locally, publish it for anyone to install:
Develop: alc_pkg_scaffold → write init.lua → alc_pkg_link → alc_pkg_test → iterate
Distribute: alc_hub_reindex → alc_hub_dist → git push
Consume: alc_hub_search → alc_pkg_install({ url: "github.com/you/repo" }) → alc_advice
During development, alc_pkg_link symlinks a local directory as a package — no publishing needed to iterate. When ready, push to Git and anyone can install with one command.
alc_hub_dist generates documentation in multiple projections from a single source:
| Projection | Output |
|---|---|
hub |
Package catalog page |
context7 |
Context7-compatible doc |
devin |
Devin-compatible doc |
luacats |
LuaCats type definitions |
narrative |
Human-readable narrative |
llms |
LLM-optimized reference |
lint |
Lint rule definitions |
The /alc-build skill and @alc-coder agent can scaffold, implement, and test packages end-to-end — strategy authoring works from natural language to a published collection.
Configuration
algocline reads configuration from layered TOML files plus environment overrides.
Files
| Layer | Path | Notes |
|---|---|---|
| User-global | ~/.algocline/config.toml (override with ALC_HOME) |
Created on first alc init with a commented template. |
| Project | <project>/alc.toml |
Shared (git-tracked). |
| Project (local) | <project>/alc.local.toml |
Worktree-scoped, git-ignored. Overrides alc.toml. |
[setting.<target>] tables
A generic per-target configuration table. Each <target> is a snake_case name
(for example journal, advisor). Field names and types are caller-defined;
algocline core does not impose a schema.
[]
= "/Users/me/journal.md"
= false
Resolution order
For each field, the highest-priority layer that defines it wins. Lower layers are not discarded — fields they uniquely define are still surfaced.
- Environment variable
ALC_SETTING_<TARGET>_<FIELD>(uppercase snake) - Project (
alc.local.toml→alc.toml) - User-global (
~/.algocline/config.toml) - Missing — field is absent from the response; the caller decides.
alc_setting_resolve MCP tool
// Tool call
{ "name": "alc_setting_resolve", "arguments": { "target": "journal" } }
// Response (JSON string)
{
"resolved": { "journal": { "path": "/Users/me/journal.md", "pkg": true } },
"sources": { "journal": { "path": "env", "pkg": "project" } }
}
Omit target to receive all configured targets in a single call.
Architecture
Three-Layer StdLib
Layer 0: Runtime Primitives (Rust → alc.*)
│ alc.llm(prompt, opts?) — Host LLM call via MCP Sampling
│ alc.llm_batch(items) — parallel LLM calls (single round-trip)
│ alc.fork(strategies, ctx, o?) — parallel multi-VM strategy execution
│ alc.json_encode/json_decode — serde_json bridge
│ alc.log(level, msg) — tracing bridge
│ alc.state.get/set/keys/delete — persistent key-value store (legacy single-namespace API)
│ alc.state.list(ns) — list keys under dispatched layout {state_root}/{ns}/*.json
│ alc.state.show(ns, key) — read full JSON content; typed error when key is absent
│ alc.state.reset(ns, key, o?) — atomically mutate state file (.bak + tempfile + rename)
│ alc.match_enum(text, cs, o?) — fuzzy enum match from LLM output
│ alc.match_bool(text) — yes/no normalizer for LLM output
│ alc.budget_remaining() — remaining budget (calls/time)
│ alc.progress(step, total, m?) — structured progress reporting
│ alc.env — host-owned readonly env snapshot (inject/dotenv/os); alc.env:use{...} declare-at-use proxy
│
Layer 1: Prelude Combinators (Lua → alc.*)
│ alc.cache(prompt, opts?) — memoized LLM call (session-scoped)
│ alc.parallel(items, fn, o?) — batch-parallel LLM over array
│ alc.map(items, fn) — transform each element
│ alc.reduce(items, fn, init) — fold to single value
│ alc.vote(answers) — majority aggregation
│ alc.filter(items, fn) — conditional selection
│ alc.json_extract(raw) — extract JSON from LLM output
│ alc.parse_number(text, pat?) — extract number from LLM output
│ alc.state.update(key, fn) — read-modify-write for state
│ alc.llm_safe(prompt, opts, d) — non-throwing LLM wrapper
│ alc.llm_json(prompt, opts?) — LLM call with JSON parse + retry
│ alc.fingerprint(str) — normalize + DJB2 hash (dedup)
│ alc.tuning(defaults, ctx) — config merge with deep merge
│ alc.budget_check() — boolean budget guard
│ alc.pipe(strategies, ctx) — sequential strategy pipeline
│ alc.eval(scenario, strategy) — evalframe facade (eval + Card)
│
Layer 2: Bundled Packages (require() from ~/.algocline/packages/)
cot — chain-of-thought [reasoning]
maieutic — maieutic prompting [reasoning]
reflect — self-reflection [reasoning]
calibrate — confidence calibration [reasoning]
sc — self-consistency (majority vote) [selection]
rank — pairwise ranking [selection]
triad — triad comparison [selection]
ucb — UCB1 hypothesis exploration [selection]
sot — skeleton-of-thought [generation]
decompose — task decomposition [generation]
distill — knowledge distillation [extraction]
cod — chain-of-density [extraction]
cove — chain-of-verification [validation]
factscore — factual precision scoring [validation]
panel — multi-perspective deliberation [synthesis]
Layer 0/1 are always available. Layer 2 packages are installed via alc init or alc_pkg_install from algocline-bundled-packages and loaded via require().
Packages declare their kind via M.meta.type = "runnable" (has M.run(ctx), usable with alc_advice) or M.meta.type = "library" (exposes reusable functions, loaded via require() from other packages). When M.meta.type is absent the type is auto-detected from whether M.run exists. alc_advice and alc_eval reject library packages with an explicit error.
Crate structure
algocline (bin: alc)
├── algocline-core — Domain types, EngineApi trait (transport-independent API surface)
├── algocline-engine — Lua VM executor, session registry, bridge
└── algocline-mcp — MCP tool handlers (alc_run, alc_advice, etc.)
Execution model
Each alc_run / alc_advice call spawns a dedicated Lua VM (OS thread + mlua instance). Concurrent sessions are fully isolated — each session's alc, ctx, and package.loaded live in their own VM, so parallel executions cannot interfere with each other.
alc.llm() is a cooperative yield. When Lua calls it, the VM pauses and returns the prompt to the MCP host. The host processes the prompt with its own LLM, then calls alc_continue with the response to resume execution.
alc_run(code)
→ Spawn dedicated VM → Lua executes → alc.llm("prompt") → VM pauses
→ returns { status: "needs_response", prompt: "...", session_id: "..." }
alc_continue({ session_id, response })
→ Lua resumes → ... → alc.llm("next prompt") → VM pauses again
→ ...repeat until Lua returns a final value → VM cleaned up
MCP Tools
| Tool | Description |
|---|---|
alc_run |
Execute Lua code with optional JSON context. Pass host_mode: true to run the session in a long-lived pool worker subprocess that survives MCP server restarts |
alc_continue |
Resume a paused execution with the host LLM's response. Automatically routes to a pool worker when the session was started with host_mode: true |
alc_v2_run |
(v2) Spawn an execution session via the ExecutionService API. Streams progress notifications when _meta.progressToken is present; supports MCP-level cancellation via notifications/cancelled. Returns { session_id, status, result?, error? } |
alc_v2_state |
(v2) Return the current ExecutionStateV2 snapshot for a session (read-only) |
alc_v2_resume |
(v2) Resume a paused session with a Single or Batch ResumePayload |
alc_v2_cancel |
(v2) Cancel a session with CancelCode::User; idempotent on already-terminal sessions |
alc_advice |
Apply an installed package by name. Returns a typed error for library packages (type = "library"); use alc_run with require() to call library APIs directly |
alc_card_analyze |
Run a Card analyzer pkg over a single Card. Loads the Card body and its samples.jsonl sidecar host-side and dispatches them to require(pkg).run(ctx) (default pkg = "card_analysis"). Returns a typed result { pattern, suggested_change, confidence } validated by the host before the MCP response is constructed — freeform pkg output is rejected with a typed error |
alc_pkg_link |
Link a local directory as a project-local package via symlink. Records path in alc.lock |
alc_pkg_unlink |
Remove a symlink created by alc_pkg_link (rejects real directories) |
alc_pkg_list |
List installed packages with metadata. Pass project_root to include project-local packages |
alc_pkg_install |
Install a package or collection from Git URL or local path. Pass force: true to overwrite an already-installed package. Response includes types_path (absolute path to alc.d.lua) |
alc_pkg_remove |
Remove an installed package from alc.toml + alc.lock. Pass project_root to target project scope |
alc_pkg_doctor |
Diagnose package state (read-only). Returns JSON with ten buckets: healthy / incomplete_pkg / installed_missing / symlink_dangling / path_missing / missing_meta (installed pkg whose init.lua lacks M.meta.name) / missing_hub_index (Collection-mode project root with 2+ pkg dirs but no hub_index.json) / spec_missing (installed pkg with spec/ but zero *_spec.lua files; opt-in spec discipline check) / stale_cache (hub cache file older than 3600s; refresh via alc_hub_search) / unregistered_pkg (physical dir under ~/.algocline/packages/ with init.lua but not registered in installed.json, alc.toml, or alc.local.toml; suggestion is an array<string> with install / link / remove / source-note options). Safe to invoke freely — no filesystem writes |
alc_pkg_test |
Run mlua-lspec tests against a package's spec directory (<pkg>/spec/*_spec.lua), a single code_file, or inline code. Returns JSON {passed, failed, pending, total, duration_ms, spec_files[], resolved_search_paths[], search_path_warnings?}. Exactly one of pkg / code_file / code must be provided. auto_search_paths (optional bool, default true) controls automatic prepending of directories from all three registry sources (installed ~/.algocline/packages/, alc.toml linked-global entries, alc.local.toml linked-variant entries) to the Lua VM's package.path, before any caller-supplied search_paths. Set to false to suppress all auto-resolution. resolved_search_paths is always present in the response (empty array when auto_search_paths: false) |
alc_init |
Initialize a project — creates alc.toml in the project root if absent |
alc_update |
Update packages declared in alc.toml by re-installing from their recorded sources |
alc_migrate |
Migrate a legacy alc.lock to the new alc.toml + alc.lock schema |
alc_eval |
Evaluate a strategy against a scenario (cases + graders). Returns a typed error for library packages |
alc_eval_history |
List past eval results, filter by strategy |
alc_eval_detail |
View a specific eval result in full detail |
alc_eval_compare |
Compare two eval results with Welch's t-test |
alc_note |
Add a note to a completed session's log |
alc_log_view |
View session logs (list or detail) |
alc_stats |
Aggregate usage stats across sessions (per-strategy) |
alc_status |
Query active session status, progress, and metrics |
alc_info |
Show server configuration and diagnostics |
alc_hub_search |
Search packages across remote Hub indices + local state |
alc_hub_info |
Show detailed info for a single package (metadata, Cards, aliases, stats) |
alc_hub_reindex |
Rebuild Hub index from locally installed packages |
alc_hub_dist |
Generate and distribute documentation for a package hub (hub, context7, devin, lint, lint_only, luacats, narrative, llms projections) |
alc_hub_gendoc |
Generate documentation for a single package (hub, context7, devin, lint, lint_only, luacats, narrative, llms projections) |
alc_pkg_scaffold |
Generate a minimal package skeleton with M.meta / M.run template and pre-filled alc_shapes_compat range |
alc_scenario_list |
List installed eval scenarios |
alc_scenario_show |
Show an installed scenario's content |
alc_scenario_install |
Install scenarios from Git URL or local path |
alc_pool_ensure |
Ensure a pool worker subprocess is running for a session; spawns one if absent. Returns {sid, sock, pid, status} |
alc_pool_status |
List active pool sessions with PID, socket path, and version. Also runs GC to evict stale entries and persists the updated registry |
alc_pool_stop |
Send SIGTERM to a named session's pool worker and remove its registry entry |
MCP Resources
algocline exposes structured data via MCP Resources — a standard capability that lets MCP clients read named files and catalog data without calling tools.
Resource URIs
| URI | Type | Description |
|---|---|---|
alc://types/alc.d.lua |
Fixed | LuaCats type definitions for alc.* StdLib (editor completion) |
alc://types/alc_shapes.d.lua |
Fixed | LuaCats definitions for alc_shapes shape library |
alc://hub/index |
Fixed | Aggregated hub package catalog (application/json) — merges all cached hub_index.json files across registered sources |
alc://packages/{name}/init.lua |
Template | Package init.lua source |
alc://packages/{name}/meta |
Template | Package metadata JSON |
alc://cards/{card_id} |
Template | Eval card JSON |
alc://scenarios/{name} |
Template | Scenario Lua source |
alc://eval/{result_id} |
Template | Eval result JSON |
alc://logs/{session_id} |
Template | Session transcript log |
Claude Code @-mention
When algocline is configured as an MCP server, Claude Code exposes all resources via @alc: mention syntax. Type @alc: in the input field to browse or tab-complete resource URIs:
@alc:alc://hub/index # read the full package catalog
@alc:alc://packages/reflect/init.lua # read the reflect package source
@alc:alc://types/alc.d.lua # read StdLib type definitions
Tab-completion for template arguments is supported (completion/complete). Typing @alc:alc://packages/ and pressing <TAB> returns installed package names filtered by prefix.
Caveat: Claude Code subagents launched with
subagent_typemay not inherit the parent session's MCP resource access. Read resources from the main agent context, not from within a delegated subagent.
alc://hub/index — aggregated package catalog
Reading alc://hub/index returns a JSON object with the union of all registered hub sources:
Individual source read failures are surfaced in "warnings" rather than failing the whole request (best-effort aggregate). On a clean install with no cached sources the response is {"schema_version":"hub_index/v0","packages":[]}.
MCP Prompts
algocline exposes each installed package as an MCP Prompt — a pre-built, parameterized instruction template that MCP clients can invoke directly.
How prompts are generated
prompts/list enumerates packages by reading alc.toml and ~/.algocline/packages/ on every request. Each installed package produces one prompt:
| Prompt field | Value |
|---|---|
name |
Package name (e.g. cot) |
title |
Name with first letter capitalised (e.g. Cot) |
description |
Package description from its metadata, if present |
arguments |
[{ name: "task", description: "Task to apply this strategy to", required: false }] |
prompts/get returns a single user-role text message. If the task argument is supplied, its value is substituted into the message at runtime before the response is returned.
Using prompts in Claude Code
When algocline is configured as an MCP server, prompts appear as slash commands in Claude Code. Type / followed by mcp__algocline__ to browse them:
/mcp__algocline__cot → apply chain-of-thought to a task
/mcp__algocline__reflect → apply self-reflection to a task
/mcp__algocline__panel → apply multi-perspective deliberation to a task
Each slash command optionally accepts a task argument. If omitted, the prompt message includes guidance for a general use.
Capability
algocline declares prompts: { listChanged: true } and fires
notifications/prompts/list_changed whenever a pkg-mutating operation
succeeds (alc_pkg_install, alc_pkg_remove, alc_pkg_link,
alc_pkg_unlink, alc_pkg_repair, alc_pkg_scaffold). MCP clients
that subscribe to this notification (e.g. Claude Code) will
automatically refresh their prompt list after each install or removal.
Host integration patterns
algocline's alc.llm() is a cooperative yield — it pauses the Lua VM and returns a prompt to the host. How the host handles this determines performance and quality.
Pattern 1: Manual loop (baseline)
The host LLM reads each prompt, generates a response, and calls alc_continue. Simple but requires one round-trip per alc.llm() call.
Host LLM → alc_advice → needs_response → Host reads prompt → Host generates response → alc_continue → repeat
Best for: Interactive exploration where you want to inspect each step.
Pattern 2: Autonomous agent delegation (recommended)
Delegate the entire strategy execution to a single agent that has MCP tool access. The agent calls alc_advice, handles every needs_response internally, and returns only the final result.
Host LLM → Agent(MCP-capable) → [alc_advice → needs_response → self-respond → alc_continue → ...] → final result
Best for: Production use. Zero host intervention. Fastest execution.
Example (Claude Code):
Agent(general-purpose) with prompt:
1. Call alc_advice(strategy="explore", task="...")
2. For each needs_response: generate a response following the system/prompt instructions
3. Call alc_continue with your response
4. Repeat until status="completed"
5. Return the final result
Known limitation: MCP tool permissions in subagents
Claude Code subagents may not inherit MCP tool permissions from the parent session's
settings.json. Ifalc_run/alc_continuecalls fail with permission errors inside a subagent, add the following to your~/.claude/settings.jsonunderpermissions.allow:"mcp__alc__*"If the issue persists, run the
alc_run/alc_continueloop directly from the main agent instead of delegating to a subagent.
Pattern 3: MCP Sampling (future)
When the MCP host supports server-initiated sampling, alc.llm() will resolve automatically without pausing. No agent delegation needed — the host responds inline.
Performance comparison
Benchmarked on the same task (UCB1 explore, 11 LLM calls):
| Pattern | Time | Host interventions | Notes |
|---|---|---|---|
| Manual (Opus) | 152s | 9 | High quality, manual effort |
| SubAgent relay (Haiku) | 224s | 9 | Agent startup overhead per call |
| SubAgent relay (Opus) | 442s | 9 | Same overhead, better quality |
| Autonomous agent | 69s | 0 | Single agent, full MCP access, no relay overhead |
The autonomous agent pattern eliminates relay overhead entirely. The agent handles the full alc_advice → alc_continue loop in-process, resulting in ~2x faster execution than even manual operation.
Strategy selection guide
| Strategy | Structure | Best for |
|---|---|---|
explore / ucb |
Generate hypotheses → UCB1 score → refine best | Open-ended questions, design decisions |
triad |
3-role debate (proponent/opponent/judge) | Comparative analysis, pro/con evaluation |
panel |
Multi-perspective deliberation + moderator | Complex problems needing diverse viewpoints |
verify / cove |
Draft → verify → revise | Factual accuracy, reducing hallucination |
reflect |
Generate → critique → revise loop | Iterative improvement |
ensemble / sc |
Multiple answers → majority vote | When consistency matters more than novelty |
rank |
Pairwise tournament ranking | Selecting best among candidates |
factscore |
Atomic claim decomposition + verification | Fact-checking, claim validation |
cod |
Iterative information densification | Summarization, compression |
Evaluating strategies
algocline includes a built-in evaluation framework powered by evalframe. Define scenarios with test cases and graders, then run them against any strategy to measure quality.
Define a scenario
A scenario is a Lua table with bindings (graders) and cases (input/expected pairs):
-- scenario.lua
local ef = require
return
Run an eval
alc_eval({ scenario: "...", strategy: "cove" })
Or point to a file:
alc_eval({ scenario_file: "/path/to/scenario.lua", strategy: "cove" })
The strategy is automatically wired as the provider — no boilerplate needed. Results include per-case scores, pass/fail status, and aggregate metrics.
Track and compare results
Eval results are persisted to ~/.algocline/evals/ automatically.
alc_eval_history({ strategy: "cove", limit: 10 }) # List past results
alc_eval_detail({ eval_id: "cove_1710672000" }) # Full result detail
alc_eval_compare({ eval_id_a: "...", eval_id_b: "..." }) # Welch's t-test
alc_eval_compare performs a Welch's t-test on the score distributions, reporting whether the difference between two runs is statistically significant.
Writing strategies
A strategy is a Lua file with an init.lua entry point:
-- my-strategy/init.lua
local M =
M. =
return M
Install it:
alc_pkg_install({ url: "github.com/you/my-strategy" })
Use it:
alc_advice({ strategy: "my-strategy", task: "..." })
Package management
Bundled packages
Bundled packages ship in two Hub Collection repositories (see Ecosystem for the full picture):
| Collection | Packages | Repo |
|---|---|---|
| algocline-bundled-packages | 15 core strategies (cot, reflect, ucb, panel, …) | Core reasoning/selection/validation |
| algocline-swarm-frame | swarm_frame, swarm_frame_algocline, swarm_aggregate_plugin | Multi-agent orchestration |
Install via CLI:
If you call alc_advice with a package that isn't installed, algocline automatically downloads the bundled collection from GitHub. But alc init upfront is recommended.
Install or update via MCP:
alc_pkg_install({ url: "github.com/ynishi/algocline-bundled-packages" })
alc_pkg_install({ url: "github.com/ynishi/algocline-bundled-packages", force: true }) # overwrite
Installing third-party packages
alc_pkg_install({ url: "github.com/user/my-strategy" })
alc_pkg_install expects the repository to use collection layout and ship a
hub_index.json at its root. Each subdirectory with an init.lua is installed
as a separate package. A single-package repo is fully supported — place
init.lua at <repo>/<pkg_name>/init.lua and generate hub_index.json with
alc_hub_dist (see docs/pkg-author-conventions.md §7).
Supported URL formats:
| Format | Example |
|---|---|
| GitHub shorthand | github.com/user/my-strategy |
| HTTPS | https://github.com/user/my-strategy.git |
| SSH | git@github.com:user/my-strategy.git |
| Local path (file://) | file:///path/to/my-strategy |
| Local path (absolute) | /path/to/my-strategy |
Optional parameters:
alc_pkg_install({ url: "/path/to/repo", force: true }) # overwrite if already installed
Managing packages
alc_pkg_list() # List installed packages with metadata
alc_pkg_remove({ name: "my-strategy" }) # Remove a package
Packages live in ~/.algocline/packages/. Each package is a directory with an init.lua.
Project-local packages
Project-local packages are managed via two files at the project root:
alc.toml— Package declarations (source of truth). Created byalc_initor automatically on firstalc_pkg_install.alc.lock— Resolved lockfile written by install/link operations.
Initialize a project:
alc_init({ project_root: "/path/to/project" })
Link a local directory as a project-scoped package via symlink (no copy):
alc_pkg_link({ path: "/path/to/my-strategy" })
This creates a symlink in ~/.algocline/packages/ and records the entry in alc.toml + alc.lock:
# alc.toml
[]
= "path"
= "/path/to/my-strategy"
Remove the symlink:
alc_pkg_unlink({ name: "my-strategy" })
Project root is auto-detected by walking up the directory tree to find alc.toml. You can also pass project_root explicitly.
Resolution order (highest priority first):
alc.lockpathentries — symlinked local directoriesALC_PACKAGES_PATH(environment)~/.algocline/packages/(global default)
Migrate an existing project using the old alc.lock schema:
alc_migrate({ project_root: "/path/to/project" })
Use project_root to activate project scope in other tools:
alc_pkg_list({ project_root: "/path/to/project" }) # Lists both project and global packages
alc_pkg_remove({ name: "my-strategy", project_root: "/path/to/project" }) # Removes from alc.toml + alc.lock
alc_update({ project_root: "/path/to/project" }) # Re-installs all alc.toml packages
Strategy development
Strategies are Pure Lua files. The host LLM can read, write, and execute them via alc_run — that's enough to iterate.
For debugging and testing, there are dedicated tools that run on the same mlua VM:
- mlua-probe — Lua debugger (MCP server). Breakpoints, stepping, variable inspection, expression evaluation
- mlua-lspec — BDD test framework (
describe/it/expect). Structured test results for CI/LLM consumption
Contributing
Bug reports and feature requests are welcome — please open an issue.
Pull requests are also appreciated. For larger changes, consider opening an issue first to discuss the approach.
Share your strategies
Writing a strategy package is straightforward: create init.lua, define M.meta and M.run(ctx), and you're done. If you build something useful, publish it as a Git repo and others can install it with:
alc_pkg_install({ url: "github.com/you/your-strategy" })
See Writing strategies and Package management for details.
License
MIT OR Apache-2.0