A reliable coding-agent CLI in Go: Claude Code in capability, hexagonal in philosophy, extras shipped as plugins — with a built-in A/B eval harness for testing changes with real data.
Sigma's premise: an agent becomes reliable when its output is
constrained by layers you compose — sensors (observe),
guides (steer), and gates (permit / deny / validate). Every
layer is a small typed adapter behind a narrow port. It authenticates with
an OpenAI API key from the OPENAI_API_KEY environment variable.
git clone https://github.com/tacoda/sigma cd sigma make build # -> bin/sigma
Requires Go 1.25+ and an OpenAI API key exported as OPENAI_API_KEY.
sigma auth status # verify OPENAI_API_KEY is set sigma init # scaffold a starter .sigma/ charter sigma run "fix the failing test in internal/foo" # one-shot sigma chat # interactive TUI (--resume to continue)
run gates every mutating tool call for approval
(--yes auto-approves); read-only tools run without prompting.
.sigma/A charter is the config bundle the agent loads from .sigma/
(and ~/.sigma/ for user-global). It shapes behavior without
touching code:
@path imports)$ARGUMENTS expansion)SessionStart, PreToolUse, PostLLMResponse, Stop, …) with a JSONL audit sinkdefault / acceptEdits / plan / bypass), hook blocks, path rooting, git-worktree isolation, an OS sandbox (seatbelt / bwrap), and a code-health Stop gate that won't let a turn finish until tests/lint passHooks can be declarative YAML, shell commands, or in-process
Go callbacks — all on one bus. A Stop hook that exits non-zero
makes the agent keep working until it's fixed.
Sigma leans into layers as its architecture — a dedicated middleware
stack for an agent (Rack / Express / gRPC interceptors, but for a coding
agent). The loop is a thin core; every cross-cutting concern is a typed layer
wrapping one of three spines. A layer receives next and can
observe, transform, short-circuit, or wrap — so a
guard blocks, a cache returns early, redaction rewrites, a budget refuses.
compaction → prompt-gate → loopbudget → retry → llm… → ui → hooks → permission → execPlugins and charters contribute layers; ordering is the lever (canon guards before the sandbox, budget outermost). The stack is introspectable — agent behavior reads as a pipeline:
$ sigma layers turn spine (outer → inner): compaction → prompt-gate → loop model spine (outer → inner): budget → retry → llm tool spine (outer → inner): dry-run → ui → hooks → permission → exec
Because the stack is composed per charter, you can A/B a whole stack with the eval harness — measure, with statistical data, what a layer change does before and after. Sensors / guides / gates are what a layer does; the spines are where it plugs in.
canon is enabled by default: it contributes the engineering &
platform canon to the system prompt as a guide, and installs deterministic
guards that block canon violations at the tool boundary — sensitive-file
access, secrets or debug artifacts in written content, dangerous shell
(rm -rf, force-push, reset --hard,
--no-verify, piping downloads to a shell), and AI attribution in
commits.
Optional bundles enabled in settings.json:
telemetry_stats toolcode_health toolPlugins compose over the ports; they never touch the agent core.
A general A/B experimentation primitive: compare two variants (two charters, before/after) over a case suite, scored with programmatic checks, event-trace assertions, and an LLM judge, into a statistically-grounded HTML report.
sigma eval experiments/tighten-review/experiment.yaml # replay (fake, free, deterministic) sigma eval --live experiments/tighten-review/experiment.yaml # real agent; records transcripts
Runs are fake-by-default (replay a recorded transcript);
--live runs the real agent and records transcripts for future
replay. The engine is generic — higher layers embed it with their own
runner and scorers.
Ports & adapters throughout: the agent core depends only on narrow interfaces, wired at a single composition root. Status: early and evolving.