Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2,056 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

stella

Reference Grade Agent Loop

Open Source · Rust · BYOK · No Phone Home

CI status Release status License Rust 1.90+ 9 providers + local

Website · Docs · Quickstart

Ship deterministically verified code fully autonomously with Stella, a self-improving next generation coding agent. Stella is an open-source, bring-your-own-key (BYOK) coding agent that runs in your terminal. It supports nine hosted model providers plus any local OpenAI-compatible server, keeps canonical telemetry in a local SQLite database, and enforces a hard per-run budget. Telemetry leaves your machine only if you configure it to, by one of exactly two explicit paths: an enrolled Oxagen Enterprise managed install, which may export only a minimal operational rollup under the governed boundary described below; or a drain block in ~/.stella/cloud.json, which stella cloud sync uses to POST staged rows to an org intake you name. Neither exists in a default install. It is built in Rust as a workspace of focused crates.

Features

  • BYOK, auto-detected — Set one provider's API key and Stella detects it. Pin a specific model per run or shell with --model.
  • Deterministic definition of done — the staged pipeline's witness stage has an independent model author a test that fails on the old code and passes on the new, and tracks that fail→pass flip. A green suite alone is not accepted.
  • Single-threaded engine — One deterministic step loop: plan, fan tools out in parallel, observe, compact if noisy, repeat. No coordinator or multi-agent swarm.
  • Prompt-cache-native memory — Lessons written as markdown in .stella/memories/ load once at session start into a byte-stable system prompt (~0.1× input cost).
  • Code graph — A tree-sitter symbol/import index (Rust, TS/TSX/JS, Python, Go, Java, C, PHP, SQL) queried by the stella search command instead of grepping.
  • Local-first telemetry — Executions, events, token/cost telemetry, and the files-touched ledger stay canonical in .stella/private/store.db. Community/default sends none of it anywhere. Only explicitly enrolled Oxagen Enterprise managed mode can derive a closed, content-free operational rollup.
  • Budget enforcement — A --spend-limit flag aborts cleanly between steps, never mid-tool.
  • Goal & fleet modesgoal works in judged rounds; fleet fans a task DAG out to parallel workers that share one tree under cooperative file claims, or take their own git worktree when a task opts in.
  • Lifecycle hooks — Shell-command hooks (SessionStart, PreToolUse, PostToolUse) configurable in settings.json.

Prerequisites

  • macOS or Linux, x86_64 or arm64.
  • Private persistence currently depends on Unix owner/mode and no-follow primitives. Non-Unix builds fail closed for sensitive state writes; Windows persistence is not currently supported or claimed.
  • For prebuilt / Homebrew install: curl.
  • For building from source: Rust 1.90+ (via rustup) and git. Building a clone of this repository uses the exact toolchain pinned in rust-toolchain.toml (currently 1.97.0) — rustup downloads it automatically on the first cargo build, so expect a one-time toolchain fetch.
  • An API key for any supported provider, or a local OpenAI-compatible model server (Ollama, vLLM, LM Studio, llama.cpp).

Install

Prebuilt binary:

curl -fsSL https://raw.githubusercontent.com/macanderson/stella/main/install.sh | sh
stella --version

The installer downloads the latest release tarball, verifies its SHA-256, and falls back to cargo install when no prebuilt binary matches your platform.

Homebrew:

brew install macanderson/tap/stella

To build from source via Homebrew: brew install --build-from-source ./packaging/homebrew/stella.rb.

From cargo (requires Rust 1.90+ and git):

cargo install --locked --git https://github.com/macanderson/stella stella-cli
stella --version

From source:

git clone https://github.com/macanderson/stella.git
cd stella
cargo build --release
./target/release/stella --version

Not on crates.io

The stella-* crates are not published to crates.iopublish = false is set once at [workspace.package] in the root Cargo.toml and inherited by every member. (The Context Graph Protocol crates are now exact-version registry dependencies, so the old git-dependency blocker is gone; the workspace simply is not published as a crate set.) The cargo install --git … command above is therefore the only supported cargo path — dropping --git does not install this project.

Set your API key

Stella is BYOK and auto-detects the provider from whichever keys you have set.

Provider Env var Default model
OpenRouter OPENROUTER_API_KEY moonshotai/kimi-k3
Z.ai (GLM) ZAI_API_KEY glm-5.2
Anthropic (Claude) ANTHROPIC_API_KEY claude-fable-5
OpenAI (GPT) OPENAI_API_KEY gpt-5.5
xAI (Grok) XAI_API_KEY grok-4retires 2026-08-15
DeepSeek DEEPSEEK_API_KEY deepseek-chat
Google Gemini GEMINI_API_KEY (alias GOOGLE_API_KEY) gemini-3-pro
Google Vertex AI VERTEX_ACCESS_TOKEN + VERTEX_PROJECT_ID gemini-3-pro
Amazon Bedrock AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (+ AWS_REGION) us.anthropic.claude-sonnet-4-5-20250929-v1:0
Local none — pass --base-url whatever your server hosts

OpenRouter is checked first — its key is gateway-specific, so having one is a deliberate choice rather than an accident of your shell — and it brings a whole default posture, not just a default model: Kimi K3 driving at xhigh with thinking on, anthropic/claude-opus-5 judging, z-ai/glm-5.2 triaging, all on the one key. Every field of that composes underneath your own settings, so anything you configure wins. OpenRouter's slugs keep their vendor namespace on the wire, so pinning one on the CLI needs both halves: --model openrouter/moonshotai/kimi-k3.

export ANTHROPIC_API_KEY=your_key_here     # or OPENAI_API_KEY, GEMINI_API_KEY, …

Bedrock is the one provider that authenticates with more than one value — SigV4 needs an access key id and a secret access key, plus a session token for temporary credentials, and a region to build the endpoint host from. All four travel through the same chain as any other credential, so stella auth set stores the whole set rather than only the first:

stella auth set bedrock                    # prompts for each, secrets masked
stella auth set bedrock --stdin \
  --field AWS_SECRET_ACCESS_KEY="$SECRET" \
  --field AWS_REGION=eu-central-1          # scripted equivalent

Explicit credentials only: AWS profile files, SSO caches, IMDS/container roles, and web-identity token files are deliberately not consulted, so a Stella process never authenticates as whatever identity its host happens to be carrying. Bedrock is also checked last during auto-detection, and only when a secret access key resolves too — AWS_ACCESS_KEY_ID is exported in plenty of shells for reasons that have nothing to do with Bedrock. --model bedrock/… pins it regardless.

Pin a provider/model per run or shell:

stella --model anthropic/claude-fable-5 run "refactor the database layer"
export STELLA_MODEL=openai/gpt-5.5

Local / any OpenAI-compatible gateway — no key required:

stella --model local/llama3.3 --base-url http://localhost:11434/v1 chat

Z.ai GLM Coding Plan: set ZAI_GLM_CODING_PLAN=1 alongside ZAI_API_KEY to route through the dedicated coding endpoint.

Credential chain (first hit wins): --api-key flag → provider env var → settings.json api_key~/.stella/credentials.toml → interactive prompt.

credentials.toml is written by stella authauth set <provider> stores a key (prompted and masked unless you pass --key/--stdin), auth list shows every stored key redacted alongside the source that actually wins, and auth remove <provider> deletes one. It never prints a secret value.

Project .env files — so keys can follow the project you're in, Stella reads .env, .env.local, and .env.<mode>.local (e.g. .env.production.local) from the working directory (or the nearest ancestor within the same git repo) into the environment at startup, most-specific file first. Template files (.env.example, .env.sample, .env.dist) and non-.local mode files (.env.production) are never read. Your live shell always wins — a value already exported (or OPENROUTER_API_KEY=… stella …) is never overwritten by a file, so unset a stale export if you mean to switch. Disable the whole mechanism with STELLA_NO_ENV_FILE=1; see what loaded with STELLA_ENV_DEBUG=1.

stella models    # list providers, models, and key status
stella config    # show the fully resolved configuration

Custom providers via settings.json

Point Stella at any OpenAI-compatible (or Anthropic/Gemini-dialect) endpoint without a code change, and override built-in defaults, from a settings.json:

Scope Path Wins over
Project <workspace>/.stella/settings.json org-managed, user
Org-managed /Library/Application Support/stella/settings.json (macOS) · /etc/stella/settings.json (Linux) · $STELLA_MANAGED_SETTINGS user
User ~/.stella/settings.json
{
  "providers": {
    // A brand-new provider: base_url is required, dialect defaults to
    // "openai-compatible" ("anthropic" and "gemini" also available).
    "together": {
      "name": "Together AI",
      "base_url": "https://api.together.xyz/v1",
      "api_key_env": "TOGETHER_API_KEY",
      "default_model": "meta-llama/Llama-3.3-70B-Instruct-Turbo",
    },
    // Overriding a built-in's defaults (e.g. the Z.ai coding plan):
    "zai": {
      "base_url": "https://api.z.ai/api/coding/paas/v4",
    },
  },
}

Then: stella --model together/meta-llama/Llama-3.3-70B-Instruct-Turbo run "…". Prefer api_key_env over a literal api_key — settings files get committed.

Untrusted repos can't redirect your key. A cloned repo's project-scope .stella/settings.json is untrusted: its credential-routing fields (base_url, api_key, api_key_env, and mcp.registry_url) are ignored unless you opt in with STELLA_TRUST_PROJECT=1, so a hostile repo can't silently point your real API key at its own server. Cosmetic fields (name, default_model, dialect) still apply; the user and org-managed scopes are always trusted. Project hooks are gated the same way, via STELLA_PROJECT_HOOKS.

Agent engine config (agent_engine_config)

The engine runs a configurable agent per role — default (the interactive / step-loop agent) and the pipeline's worker, verifier, triage, research, and plan. The agent_engine_config object in the same settings.json scope chain configures each one's model, gateway, system prompt, reasoning, and sampling parameters — and in the Command Deck, /settings opens the SETTINGS tab, whose engine-config editor covers all of it (s saves to user scope, S to project scope; the per-agent model pickers offer allowed_models, falling back to the catalog when that list is empty). There are no per-agent slash commands — the SETTINGS tab is the one place models are configured.

{
  "agent_engine_config": {
    // Flat per-role models ("provider/slug", or a bare catalog slug).
    "default_model": "anthropic/claude-fable-5",
    "pipeline_worker_model": "zai/glm-5.2",
    "pipeline_verifier_model": "openrouter/openai/gpt-5.5",
    "pipeline_triage_model": "deepseek/deepseek-chat",
    // Research and plan ride the WORKER when unset, not default_model.
    "pipeline_research_model": "zai/glm-5.2",
    "pipeline_plan_model": "zai/glm-5.2",

    // The model vocabulary the TUI pickers offer and auto_mode selects from.
    "allowed_models": [
      "anthropic/claude-fable-5",
      "zai/glm-5.2",
      "openrouter/openai/gpt-5.5",
    ],

    // "on" = pick the verifier automatically from allowed_models: prefer a
    // different model family than the worker's, then the highest catalog
    // price tier. You never worry about it.
    "auto_mode": "off",
    // "on" = per-agent effort is chosen for you (verifier high, worker and
    // plan medium, triage and research low), overriding any per-agent
    // "effort".
    "effort_auto": "off",
    // "on" = thinking mode chosen for you (on everywhere except triage and
    // research, which read rather than deliberate).
    "reasoning_auto": "off",

    // Per-agent deep config. Every field is optional — set it and it goes
    // on the wire; leave it out and the provider default applies.
    "agents": {
      "verifier": {
        "provider": "openrouter", // gateway: the slug goes to THIS
        "model": "openai/gpt-5.5", // provider verbatim (BYOK per agent)
        "prompt": "You are a strict, evidence-first code verifier.",
        "effort": "high", // low | medium | high | xhigh | max
        "reasoning": "on", // thinking mode on/off
        "params": {
          "temperature": 0.2,
          "top_p": 0.9,
          "top_k": 40,
          "frequency_penalty": 0.0,
          "presence_penalty": 0.0,
          "repetition_penalty": 1.0,
          "max_tokens": 4096,
          "seed": 7,
          "verbosity": "low", // OpenAI/Anthropic-family models
          "service_tier": "priority", // providers with tiered service
        },
      },
    },
  },
}

Precedence per agent: --model flag > agents.<agent>.model > pipeline_<agent>_model > default_model > auto-detect. Research and plan end that chain at the worker instead of default_model — unset, they run whatever the worker runs, field by field, so a --model that re-points the worker for one invocation cannot split them onto a second model the run would buy without reporting. An agent's provider field routes its slug through that gateway verbatim, so the worker can run on your Anthropic key while the verifier routes openai/gpt-5.5 through your OpenRouter key and triage hits Z.ai. Each adapter forwards only the parameters its wire supports (verbosity and service_tier are dropped where meaningless); reasoning maps to GLM's thinking, OpenRouter's reasoning, Anthropic extended thinking (with an effort-tiered budget), OpenAI reasoning.effort, and Gemini thinkingLevel. Custom prompts replace the built-in base instructions; workspace memories and rules still append. agents.research.prompt is the one exception and is not honoured — a research child's built-in system prompt is the contract that makes it read-only, not a preference. A verifier/triage model whose provider has no resolvable key degrades softly — the role rides the worker and a notice says so.

Usage

Command index

The full subcommand surface. Every command also answers stella <command> --help; each row links to its reference page on stella.oxagen.sh.

Command What it does
run <prompt> Send a one-shot prompt, non-interactive — the staged pipeline by default
chat Interactive session: the Command Deck TUI (also what a bare stella opens)
resume [id] Reopen a durable past session exactly where it stood; --list browses them
goal <goal> Work in judged rounds until a verifier model confirms the goal is met
monitor [target] Watch a branch/PR's CI and fix failures until it is fully green
fleet <tasks…> Fan tasks out to worker agents, wave-scheduled and recorded in a ledger
init Infer this workspace's domain taxonomy and build the code-graph index
search <query> Find code by meaning or by name over the code-graph index
storage <cmd> Inspect the storage map: layers, namespaces, relations, fields, drift (offline)
scripts <cmd> List and run the project's package-manager scripts by canonical verb (offline)
tools List every tool available this session; --validate checks custom manifests
models List configured providers and available models
auth <cmd> Manage BYOK provider keys in ~/.stella/credentials.toml — never prints a secret
config Show the fully resolved configuration
mcp <cmd> Manage MCP servers: search a registry, install, list, log in, show usage
memory <cmd> Inspect memories; promote one to a project rule
stats Cost, tokens, and $/resolved task for this workspace
usage <cmd> The same numbers across every project, from the hub at ~/.stella/usage.db
inspect Replay the exact context a past model call was sent, verified against its digests
observe Serve the Observatory dashboard over local telemetry — loopback-only, read-only
cloud <cmd> Show or set the org/workspace identity that scopes replicated telemetry
telemetry <cmd> Inspect or flush the managed enterprise spool — off unless explicitly enrolled
arena arena-bench harness adapter — for benchmarking Stella, not using it
doctor Diagnose the install: config, credentials, toolchain, and workspace state
proposals <cmd> Review the adaptive-context loop's pending proposals — keep, ignore, or retire
version Print the version and exit

Interactive chat (default)

stella            # or: stella chat

On a TTY this opens the Command Deck — a tabbed TUI (Session · Agents · Traces · Graph · Files · Skills · MCP) with PR-style diffs and an editable prompt queue. --accessible (or STELLA_ACCESSIBLE=1) runs that same deck so a screen reader can read it: inline on your own screen, each finished message into normal scrollback exactly once, single-column panels, labelled rows instead of tables, and a spoken line whenever you change tab, overlay, or focus. --plain (or STELLA_PLAIN=1, or piped stdio) falls back to the line REPL.

In-chat commands — the Command Deck and the line REPL each implement their own vocabulary, so the surface column says where a command actually works:

Command Surface Does
/help /clear Deck + REPL Show help · clear history
/models Deck + REPL List providers/models (/models refresh re-syncs the catalog)
/init /agents Deck + REPL Reindex the workspace · the installed custom agents (a tab in the deck)
/files Deck + REPL The Files-Touched ledger — [C·R·U·D] path per file (a panel in the REPL, the Files tab in the deck)
/goal <text> REPL only Work in judged rounds until the goal is met
/config REPL only Show resolved configuration
/rename <name> /color <name> REPL only Rename the tab · switch accent color
/pipeline Deck only Toggle witness-verified staged turns (see the inference pipeline)
/settings /diff /graph /skills /mcp /mcp-search /sessions /context /inspect /inbox Deck only Open the corresponding tab or overlay (/settings includes the engine-config editor)
/export Deck only Export session telemetry to a ZIP + HTML dashboard
/exit or Ctrl-D REPL only Exit (the deck exits with Ctrl-C)

One-shot run

stella run "fix the failing test in src/auth.rs"
stella run "add a health check endpoint to the API"

Goal mode

stella goal "the login flow has a passing e2e test and CI is green"
stella monitor main          # drive a branch/PR's CI to green as a judged goal

Fleet mode

stella fleet "fix the flaky auth test" "tighten the CI cache key"   # two isolated tasks
stella fleet --plan .stella/fleet.toml --max-concurrency 2 --spend-limit 5.0

Wave-scheduled by dependency and recorded in .stella/private/fleet.db. Workers share the repository root by default, coordinated by cooperative file claims; a task with isolation = "isolated" gets its own git worktree under .stella/worktrees/ on a fleet/<slug>-<hash> branch instead. A plan file is the serde form of the fleet DAG: [[tasks]] entries with id, title, prompt, optional depends_on, and isolation.

Code graph queries

stella search "where is run_turn defined"    # a symbol name works as well as a sentence
stella search "what imports src/auth.rs"     # blast radius before you edit it

Built by stella init, ranked offline by name/graph match when no embedder is configured, and by meaning when one is.

Project setup & introspection

stella init      # infer this workspace's domain taxonomy (.stella/domains.toml)
stella tools     # list every tool available to the agent this session
stella stats     # cost, tokens, and $/resolved task per provider/model
                 # (--format table|json|csv, --provider <id>)
stella inspect   # the exact context a past model call was sent, rebuilt from
                 # recorded receipts (--step N, --call-seq S, --format json)

Global flags

--model provider/id · --api-key · --base-url · --spend-limit <usd> · --accessible · --plain · --no-anim (also as STELLA_MODEL, STELLA_BASE_URL, STELLA_SPEND_LIMIT, STELLA_ACCESSIBLE, STELLA_PLAIN, STELLA_NO_ANIM). All of them are registered with every subcommand, so they parse before or after the subcommand token. --output-format text|json|stream-json (env STELLA_OUTPUT_FORMAT) is deliberately not global: it is declared by the commands that honor it — stella run and stella fleet — and goes after the subcommand token (stella run "…" --output-format json); interactive chat / goal / monitor modes render human-readable output. stella run uses the staged pipeline by default; --no-pipeline falls back to the raw step-loop. In pipeline mode, --test-command <cmd> arms deterministic verification with your own test; without it an independent witness author writes a failing test whose fail→pass flip proves the work (the inference pipeline). Post-turn reflection remains enabled for one-shot text, JSON, and stream-JSON runs. Ephemeral automation can suppress that additional model call explicitly with STELLA_DISABLE_REFLECTION=1; the truthy values true, yes, and on are also accepted case-insensitively.

Built-in tools

Twelve built-ins, in four families — the task board, sub-agent delegation, scratch state, and the environment probe:

Tool Description
task_create · task_list · task_start · task_complete · task_cancel · task_assign The session task board — one row per deliverable, exactly one in progress, task_assign delegates a board task to a parallel sub-agent
task Delegate a self-contained research question to a read-only sub-agent that returns only its findings — bulky evidence stays out of the parent conversation, and independent questions dispatched in one step run concurrently
save_state · get_state · list_state · delete_state The scratch state plane: session-private key/value state (parse results, extracted lists, computed digests) saved once and referenced later instead of re-derived — paged reads by byte offset, deleted automatically at session end
get_environment Report the session's environment: workspace root, git status, platform/arch, OS release, shell dialect, and the scratch directory path

Every built-in is registered by default and individually withholdable with a "tools": {"<name>": "off"} switch in any settings.json scope (normal per-field merge — project wins). Everything beyond the built-ins reaches the registry from outside: MCP servers (.stella/mcp.toml) and developer-defined custom script tools (.stella/tools/*.toml).

Containment is the process boundary, not a per-tool sandbox. None of the twelve built-ins runs a shell, but custom manifest tools and hook actions still spawn processes. For real containment, run the whole Stella process inside a container: that boundary sits outside every spawn path, so nothing can step around it. See docs/spec/remote-sandboxes.md.

Memory and context

Lessons written as markdown in .stella/memories/ load once at session start into a byte-stable system prompt, so every model call considers them at prompt-cache prices. New memories take effect the next session — hot-injection would invalidate the cache.

Every working turn is also recorded as an episode (summary, files touched, outcome, time window) in .stella/private/context.db, and stella init writes the domain taxonomy as bi-temporal facts. Recall fans out through the Context Graph Protocol host to the memory store and the code graph, fused by score under one budget.

Telemetry

Executions are recorded, best-effort, in .stella/private/store.db: the full event stream, per-model-call telemetry (tokens, cache hits, cost), and the Files-Touched ledger. The store is never a dependency of a turn — a session runs even if the file can't be opened. Query it with any SQLite client.

A default install constructs no telemetry spool and no telemetry HTTP client, so nothing is sent anywhere. Two explicit configurations, and only these two, change that: Enterprise enrollment (below) and the cloud.json drain (stella cloud sync, a separate pipe with its own wire contract and endpoint, inert unless the file carries both an org_id and a drain block).

A seat becomes enrolled only through a valid signed enterprise_telemetry document in the org-managed settings scope. That document binds issuer, audience, organization/workspace, expiry, the single execution_rollup event class, a managed model catalog, process_free isolation, bearer-secret references, and one endpoint that must exactly match the administrator's credential-free HTTPS allowlist.

While enrolled, only stella run --no-pipeline is eligible. Stella rejects pipeline, goal, fleet, deck/chat, interactive, workspace-port, and candidate workspace execution paths because they cannot prove the process-free boundary. Eligible finalized runs may export only managed organization/workspace/enrollment identifiers; allowlisted provider/model or other; outcome; duration; input and output token counts; cost in micro-USD; tool-call and changed-file counts; and a produced-output boolean. Prompts, paths, tool names/arguments/results, reasoning, errors, git state, memories, rules, full local events, and local execution or installation identifiers are excluded.

Delivery is at-least-once from an owner-only host spool outside the workspace. Retained event payloads are bounded to 10,000 rows and 16 MiB; SQLite overhead may make the physical database larger. Startup flush is detached and never delays execution or process exit; stella telemetry flush attempts one bounded batch explicitly. stella telemetry status reports enrolled/disabled state, pending and stranded rows/bytes, quarantine and physical size, and durable drop, corruption, and rollover counters. See the Telemetry documentation for backfill, retry, rollover, and server-side companion requirements.

Lifecycle hooks

Declare shell-command hooks in any settings.json scope; they fire on agent lifecycle events, receiving the event payload as JSON on stdin:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          { "command": "echo \"on-call: $(cat .oncall 2>/dev/null)\"" },
        ],
      },
    ],
    "PreToolUse": [
      {
        "matcher": "task_assign",
        "hooks": [{ "command": "./scripts/guard-delegation.sh", "timeoutMs": 5000 }],
      },
    ],
  },
}
  • SessionStart — stdout is appended to the system prompt as session context (once per session).
  • PreToolUse — a non-zero exit blocks the tool; the model sees the hook's message instead. matcher is a glob over the tool name.
  • PostToolUse — observation only, never blocks.

Scopes concatenate (any scope can add a gate; none can remove another's). Hooks from a repo's own .stella/settings.json load only with STELLA_PROJECT_HOOKS=1, so cloning an untrusted repo never auto-executes its commands.

Architecture

stella-core has no I/O of its own: it drives model calls through the Provider port and tools through the ToolExecutor port, emitting an AgentEvent stream over a channel. All decision logic — compaction, eviction, loop detection, budget — is plain synchronous functions over owned data, so a new vendor or tool is an adapter, never a rewrite.

flowchart TD
    U(["stella · the CLI (stella-cli)<br/>REPL · run · goal · monitor"]) --> CORE
    subgraph CORE["stella-core · the engine (NO I/O)"]
      ENG["step driver · goal loop · budget<br/>retry · compaction · loop-detection · router"]
    end
    CORE -->|Provider port| MODEL["stella-model — adapters<br/>anthropic · openai · gemini · vertex · bedrock · zai<br/>(+ any OpenAI-compatible: xai · deepseek · openrouter · local)"]
    CORE -->|ToolExecutor port| TOOLS["stella-tools<br/>task board · sub-agents · scratch state · environment"]
    MCP["stella-mcp<br/>external MCP servers"] -.->|merges tools into registry| TOOLS
    CORE -->|emits AgentEvent stream| STORE["stella-store<br/>SQLite: executions · events · telemetry"]
    U -->|"recall · episodes · bi-temporal facts"| CTX["stella-context — context plane<br/>recall · embeddings · memory"]
    GRAPH["stella-graph — tree-sitter code index"] -->|"auto-indexed at session start · queried via `stella search`"| DB[("SQLite code graph<br/>.stella/private/codegraph.db")]
    MODEL -.->|versioned serde| PROTO["stella-protocol — shared types + Provider/tool ports"]
    TOOLS -.-> PROTO
    STORE -.-> PROTO
Loading

Design principles

Eight architectural invariants hold the design together: the engine drives everything through ports and does no I/O, every cross-boundary type round-trips through serde_json byte-for-byte, errors are typed rather than panicked, the budget aborts only between steps and never mid-tool, prompts stay byte-stable so the provider cache keeps hitting, provider feature parity is declared and witness-tested rather than assumed — and Stella sends zero telemetry anywhere by default.

They are stated normatively, in full, in AGENTS.md § Architecture: ports, not concretions. That is the only copy: this section is a summary and does not govern. A PR that breaks one of them will be asked to restructure regardless of how good the feature is.

Stella is also BYOK — any provider key, any combination, no account. That is a product property rather than an architectural invariant, but it is the one most people want to know first.

Workspace layout

Twenty stella-* crates make up the workspace. The Context Graph Protocol (CGP) — the retrieval abstraction Stella's recall routes through — now lives in its own repository and is pulled in as registry crates pinned to exact versions in the root [workspace.dependencies], not as workspace members.

Every crate carries its own README.md — linked from the table below — with its file layout, the invariants it enforces, its gotchas, and the recipe for extending it.

Crate Role
stella-cli CLI binary — clap surface + agent loop wiring
stella-core The step-driver engine (no I/O): parallel tools, goal loop, budget, retry, compaction, loop detection, router
stella-tools The built-in tools (the task board, the task sub-agent, the scratch state plane, get_environment — every one registered by default, each withholdable via tools switches)
stella-model The Provider port's adapters: anthropic, openai, gemini, vertex, bedrock, zai (SSE, tool-call dialects, SigV4, pricing)
stella-store SQLite persistence — executions, events, telemetry, files-touched
stella-mcp MCP client (stdio + HTTP, protocol 2025-06-18) merging external tools into the registry
stella-protocol Zero-logic, zero-I/O stability contract: shared serde types + the Provider/tool ports
stella-context The context plane: reflection-memory recall + embedding index, episodes, bi-temporal facts
stella-graph Tree-sitter symbol + import-edge indexer (Rust/Python/JS/TS/TSX/SQL/Go/Java/C/PHP)
stella-pipeline The orchestration plane above the engine — the default stella run path: triage → plan → scope review → witness → execute → verify → verdict (docs)
stella-fleet The multi-agent fleet behind stella fleet: DAG planner + wave scheduling, a shared tree with cooperative file claims by default, opt-in git-worktree isolation per task
stella-media Multimodal generation behind one MediaProvider port
stella-tui The Command Deck — a pure event-fold core + thin crossterm shell
stella-observatory The Observatory — stella observe's loopback-only telemetry dashboard over the local SQLite stores
stella-serve A separate headless binary (not part of the stella CLI): drives the engine over a wire protocol so a host process runs the Rust core, remoting every model and tool call back — the engine holds no ambient authority
stella-diag The diagnostics plane: typed, content-free records explaining why the program did something — a serde-only leaf every crate may depend on
stella-home Where ~/.stella is: the user home, the stella home, and the user-tier data dir. A leaf with no dependencies at all — the one shape stella-store and stella-observatory can both depend on, so the resolution stopped being two hand-synced copies
stella-engine Step-scoped facade over stella-core for durable hosts: run_step + checkpoint/resume, re-exports only — consumed by stella-serve, never linked by the CLI
stella-runtime The shared engine-assembly bottom half (RuntimeSpecSessionRuntime): provider, registry, store, budget — construction only, and it reads no ambient environment by contract
stella-parity The CLI-vs-API capability matrix: every engine capability declares a witnessed posture on both surfaces, so a feature cannot ship on one and silently miss the other
Context Graph Protocol Its own project now: macanderson/context-graph-protocol — wire types, host runtime, and the public conformance suite. Stella is its reference host and depends on it as exact-version registry crates.

Alongside the Rust workspace, the documentation site (stella.oxagen.sh) lives at website/ (Next.js + Fumadocs) as a self-contained package: its own package.json, pnpm-lock.yaml, and pnpm settings all sit in that directory, and the repo root is pure cargo. The two toolchains share no code — the only thing that crosses between them is the brand palette: crates/stella-tui/src/palette.rs is the hand-maintained normative source, mirrored by website/src/app/tokens.css (--stella-*), and the two must be edited together.

Development

cargo build --workspace
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo run -p stella-cli -- models

The docs site

cd website       # the site is self-contained; the repo root is pure cargo
pnpm install     # once (Node ≥ 20, pnpm 11)
pnpm dev         # serve the docs at http://localhost:3400
pnpm build       # production build (what docs.yml CI runs)

Docs content is MDX under website/content/docs/. On a pull request a docs-only change runs the fast docs workflow, and ci.yml's Rust jobs skip themselves after a cheap diff check — their required contexts still report, as skipped, so the PR can merge (#1892). Once queued or pushed to main the same jobs always run the full gate — deliberately, since the required check has to report on the merged result.

To try your working copy against real projects before a release, install it as stella-dev — it lives side by side with the released stella:

scripts/dev.sh install        # build (release) + link ~/.local/bin/stella-dev
cd ~/any/other/repo
stella-dev                    # the Command Deck, running your checkout
scripts/dev.sh status         # show what both binaries resolve to
scripts/dev.sh uninstall      # remove the link

Contributing

Contributions are welcome. Stella is AGPL-3.0-only and dual-licensed, so a one-time CLA signature is required — you keep your copyright, and the bot walks you through it on your first PR. See CONTRIBUTING.md for dev setup, a tour of the crates, the witness-test contract, and style rules. CI runs fmt, clippy -D warnings, tests, and a release build on every PR.

You have… Do this
A bug File it with a repro
An idea Open a feature request or start a discussion
An evening Grab a good first issue

License

Stella is dual-licensed.

Open source: AGPL-3.0-only. Free to run, read, modify, and redistribute. In exchange, if you distribute a modified Stella — or offer one to users over a network — you publish your modifications under the same terms. Using Stella as a coding tool on your own proprietary codebase is unaffected: the AGPL covers Stella itself, not the code you write with it.

Commercial: available from Oxagen. If you want to embed Stella in a closed-source product, run a modified Stella as a hosted service without publishing it, or your procurement process forbids AGPL code, a commercial license removes those obligations. Contact licensing@oxagen.sh.

LICENSING.md explains which track you are on and why. The Context Graph Protocol is a separate project and stays permissive — MIT OR Apache-2.0, at your option — so depending on it does not put your project under the AGPL.

Contributions require a CLA; you keep your copyright.

About

A coding agent that builds it's own tools and mines it's own skills and always ships with receipts.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages