Agents forget. Fleets drift. Stella doesn't.
Open Source · Rust · BYOK · No Phone Home
Website · Docs · Quickstart
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. Community/default installs have zero telemetry egress. An explicitly enrolled Oxagen Enterprise managed install may export only a minimal operational rollup under the governed boundary described below. It is built in Rust as a workspace of focused crates.
- 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 —
verify_donereplays your new test files against the previous code in a shadow worktree atgit HEAD; the test must fail there and pass on your change. 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 saved with
save_memoryload once at session start into a byte-stable system prompt (~0.1× input cost). - Code graph — A tree-sitter symbol/import index (Rust, TS/JS, Python, SQL)
queried by the agent and the
stella graphcommand 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
--budgetflag aborts cleanly between steps, never mid-tool. - Goal & fleet modes —
goalworks in judged rounds;fleetfans 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 insettings.json.
- macOS or Linux,
x86_64orarm64. - 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 inrust-toolchain.toml(currently 1.97.0) — rustup downloads it automatically on the firstcargo 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).
- Optional:
ripgrepandfdonPATH(used by thegrep/globtools), andghfor the CI/issue tools.
Prebuilt binary:
curl -fsSL https://raw.githubusercontent.com/macanderson/stella/main/install.sh | sh
stella --versionThe 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/stellaTo 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 --versionFrom source:
git clone https://github.com/macanderson/stella.git
cd stella
cargo build --release
./target/release/stella --versionThe stella-* crates are not published to crates.io — publish = false is
set once at [workspace.package] in the root Cargo.toml and inherited by every
member. The blocker is structural: stella-context and stella-graph depend on
the Context Graph Protocol
crates by git rev, and crates.io forbids git dependencies in a published crate.
The cargo install --git … command above is therefore the only supported cargo
path — dropping --git does not install this project.
Stella is BYOK and auto-detects the provider from whichever keys you have set.
| Provider | Env var | Default model |
|---|---|---|
| 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-4 |
| DeepSeek | DEEPSEEK_API_KEY |
deepseek-chat |
| Google Gemini | GEMINI_API_KEY (alias GOOGLE_API_KEY) |
gemini-3-pro |
| OpenRouter | OPENROUTER_API_KEY |
auto |
| Google Vertex AI | VERTEX_ACCESS_TOKEN + VERTEX_PROJECT_ID |
gemini-3-pro |
| Amazon Bedrock | AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY |
Claude via Converse |
| Local | none — pass --base-url |
whatever your server hosts |
export ANTHROPIC_API_KEY=your_key_here # or OPENAI_API_KEY, GEMINI_API_KEY, …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.5Local / any OpenAI-compatible gateway — no key required:
stella --model local/llama3.3 --base-url http://localhost:11434/v1 chatZ.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 auth — auth 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 configurationPoint 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 |
— |
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.jsonis untrusted: its credential-routing fields (base_url,api_key,api_key_env, andmcp.registry_url) are ignored unless you opt in withSTELLA_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, viaSTELLA_PROJECT_HOOKS.
The engine runs four configurable agents — default (the interactive /
step-loop agent) and the pipeline's worker, judge, and triage.
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, /engine opens an editor popup for
all of it (s saves to user scope, S to project scope; /model-worker,
/model-judge, /model-triage, /model-default jump straight to a model
picker driven by allowed_models).
{
"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_judge_model": "openrouter/openai/gpt-5.5",
"pipeline_triage_model": "deepseek/deepseek-chat",
// 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 judge 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 (judge high, worker
// medium, triage low), overriding any per-agent "effort".
"effort_auto": "off",
// "on" = thinking mode chosen for you (on everywhere except triage).
"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": {
"judge": {
"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 judge.",
"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. An agent's
provider field routes its slug through that gateway verbatim, so the
worker can run on your Anthropic key while the judge 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. A judge/triage model whose
provider has no resolvable key degrades softly — the role rides the worker
and a notice says so.
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 judge 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 |
graph <op> <target> |
Query the code graph — definitions, references, imports, neighbors (offline) |
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 |
connect <cmd> |
Connect GitHub or Linear so the agent gains the issue toolset |
memory <cmd> |
Inspect memories through the citation loop; 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 |
version |
Print the version and exit |
stella # or: stella chatOn 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. --plain (or STELLA_PLAIN=1, or piped stdio) falls back to the line REPL.
In-chat commands:
| Command | Does |
|---|---|
/goal <text> |
Work in judged rounds until the goal is met |
/files |
Show the Files-Touched panel — [C·R·U·D] path per file |
/models /config |
List providers/models · show resolved configuration |
/rename <name> /color <name> |
Rename the tab · switch accent color |
/pipeline |
Toggle witness-verified staged turns (Command Deck; see the inference pipeline) |
/clear /help |
Clear history · show help |
/exit or Ctrl-D |
Exit |
stella run "fix the failing test in src/auth.rs"
stella run "add a health check endpoint to the API"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 goalstella fleet "fix the flaky auth test" "tighten the CI cache key" # two isolated tasks
stella fleet --plan .stella/fleet.toml --max-concurrency 2 --budget 5.0Wave-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.
stella graph definitions run_turn # where is this symbol defined?
stella graph importers src/auth.rs # which files import it?Built by stella init, answered offline, no API key needed.
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)--model provider/id · --api-key · --base-url · --budget <usd> ·
--output-format text|json|stream-json (also as STELLA_MODEL,
STELLA_BASE_URL, STELLA_BUDGET, STELLA_OUTPUT_FORMAT). The json /
stream-json formats are for headless one-shot stella run; 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.
| Tool | Description |
|---|---|
read_file · write_file · edit_file · delete_file |
File CRUD with surgical exact-substring edits |
apply_edits |
One transactional batch of exact-substring edits across many files — every edit validates first, and if any fails nothing is written (dry_run validates without writing) |
bash |
Run a shell command (timeout kill; trace: true echoes each line) — off by default, registered only with "tools": {"bash": "on"} in settings (any scope) |
grep · glob |
Regex content search (ripgrep) · glob file discovery (fd) |
graph_query |
Query the indexed code graph: symbol definitions/references, file imports/importers/neighborhood — auto-built at session start, refreshed live |
read_symbol |
Read a named symbol's exact source span, resolved through the code graph — no line-offset guessing; multiple definitions are listed, never silently picked |
build_project · run_tests |
Build/test with the workspace's toolchain (cargo/npm/go/make) |
diagnostics |
Fast typecheck: the toolchain's native machine-readable check (cargo check / tsc / eslint / ruff) parsed into structured file:line:col records, grouped by file |
run_lint · format_code |
The project's own linter/formatter (cargo clippy/fmt, or package.json lint/format scripts), spawned argv-style — no shell |
run_script |
Run a verb the project itself declares (Makefile target, package.json script, cargo alias); unknown names list the discovered vocabulary |
start_process · read_output · send_stdin · stop_process |
Long-running processes (dev servers, REPLs, watchers) from an argv vector — capped output ring, SIGTERM-then-kill stop, reaped at session end |
repo_status · repo_diff · repo_commit · repo_push · repo_pull · repo_rollback |
Vendor-neutral repository tools: structured status, hunk-level pending-change diffs for pre-commit self-review, pathspec-explicit commits, pushes that structurally refuse the default branch (never forced), fast-forward-only pulls, restore-named-paths rollback |
verify_done |
Replay new test files against git HEAD to prove the change works |
project_overview · gather_context |
Orient in the workspace in one pass · one deterministic context sweep (greps, globs, symbol lookups, bounded excerpts) saved as a reusable pack |
explorations · save_exploration |
Shared codebase maps — explore once, reuse everywhere |
save_memory · cite_memory |
Persist a lesson into every future session's system prompt · cite a recalled memory so it earns its place |
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 to a parallel sub-agent |
search_skills · install_skill · skill_search · tool_search · mcp_search |
Discovery at the session layer: search the public skills registry and install from it (with confirmation), search the skills already installed, or rank this session's tools / MCP servers instead of carrying all of them in the prompt |
ci_status |
CI runs + failure logs via gh |
screenshot |
Capture the screen as verification evidence |
web_fetch · web_extract_assets · web_download · web_search |
Read a URL as markdown/text/HTML · mine a page's stylesheets, scripts, and design tokens · download an asset into the workspace · ranked search results — off by default, opt in with "tools": {"web": "on"}; web_search additionally needs your own BRAVE_API_KEY or TAVILY_API_KEY |
generate_svg |
Validate, sanitize, and save an agent-authored SVG under .stella/artifacts/ — scripts, handlers, and external references stripped |
generate_image · generate_video · poll_video |
Text-to-image/video via your provider key, saved under .stella/artifacts/ — registered only when a media-capable key is set (video is behind a cost confirmation) |
ask_user |
Put a 2–6 option multiple-choice question to you when the decision is genuinely yours; a headless run gets a named error instead of a hang |
create_issue · update_issue · close_issue · search_issues · get_issue · list_labels · list_members · start_work_on_issue |
Issue tracking (GitHub/Linear) — registered only when a tracker is connected (stella connect github|linear, LINEAR_API_KEY, or gh auth) |
All file tools are workspace-root-pinned, and every read/write/edit/delete is
recorded in the Files-Touched ledger (shown per turn as [C·R·U·D] path, also
via /files).
Bash is opt-in. The default tool surface has no shell: the model works
through enumerable-argv tools (build/test/lint/format, run_script's
project-declared verbs, the process group, the repo_* tools). Enable bash
per user, org, or project by adding "tools": {"bash": "on"} to the
corresponding settings.json scope (normal per-field merge — project wins).
Opt-in bash sandbox: STELLA_BASH_SANDBOX=workspace-write confines bash
file writes to the workspace root plus the standard tmp dirs (network still
allowed); restricted additionally denies all network. Backends:
sandbox-exec (Seatbelt) on macOS, bwrap (bubblewrap) on Linux. This bounds
the blast radius of prompt injection — instructions hidden in a file the agent
reads can steer the model into running arbitrary commands. The tradeoff is
capability: the sandbox also blocks legitimate work (cargo writing
~/.cargo, npm/pip caches under $HOME, git push under restricted),
which is why the default is off. Fail-closed: an unknown value, a missing
bwrap, or an unsupported platform fails the tool call rather than silently
running unsandboxed.
Conditional tools: issue tools need LINEAR_API_KEY or a gh auth login;
graph_query needs the code-graph index (auto-built at session start);
generate_image needs ZAI_API_KEY or OPENAI_API_KEY. Without their
prerequisites, these tools are not registered.
The web tools are opt-in too. web_fetch, web_extract_assets, and
web_download register only with "tools": {"web": "on"} in a settings.json
scope; web_search additionally needs your own BRAVE_API_KEY or
TAVILY_API_KEY. They are the only built-ins that talk to a host other than
your model provider, which is why they are off until you say otherwise. See
the web tools.
Lessons saved with save_memory (or 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.
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.
Community/default mode constructs no enterprise spool or HTTP client and has
zero telemetry egress. 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.
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": "bash", "hooks": [{ "command": "./scripts/guard-bash.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.matcheris 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.
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/>CRUD · grep · glob · build · test · lint · scripts · processes · repo · verify_done · issues · CI · opt-in bash"]
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 `graph_query` + `stella graph`"| DB[("SQLite code graph<br/>.stella/private/codegraph.db")]
MODEL -.->|versioned serde| PROTO["stella-protocol — shared types + Provider/tool ports"]
TOOLS -.-> PROTO
STORE -.-> PROTO
- Ports, not concretions —
stella-corenever imports a provider SDK, a filesystem call, or a terminal library; it drives through traits. - No I/O in the engine — all decision logic is synchronous functions over owned data, so the whole engine is property-testable.
- Zero telemetry egress by default — Community/default makes no telemetry calls. Explicitly enrolled Oxagen Enterprise managed mode is the only governed exception, limited to its exact allowlisted HTTPS sink and minimal operational rollup. Your chosen model provider remains the normal BYOK network path.
- BYOK — any provider key, any combination, no account.
- Serde-first — every cross-boundary type round-trips through
serde_jsonbyte-for-byte. - Fail loud, recover gracefully — typed, named errors; never a bare string,
never a
panic. - Budget enforced at safe boundaries only — never mid-tool; an abort recommendation is acted on between steps.
Fifteen 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 a pinned git dependency, 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 (CRUD, grep/glob, build/test/lint/format, run_script, the process group, the repo_* tools, verify_done, issues, CI — plus the opt-in bash) |
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 → judge (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 — generate_svg always on; generate_image and generate_video/poll_video registered when a media-capable key is set (video behind a headless cost gate) |
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 |
| 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 via git. |
The repo is a monorepo: alongside the Rust workspace, the documentation
site (stella.oxagen.sh) lives at website/
(Next.js + Fumadocs), orchestrated by a pnpm workspace at the root. Rust
crates are deliberately not pnpm packages — cargo remains their build
system; the root package.json only bridges convenience scripts.
cargo build --workspace
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo run -p stella-cli -- modelspnpm 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 instead of the Rust gate; the
merge queue does not honor paths-ignore, so it still pays the full gate once
queued — 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 linkContributions 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 |
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 Apache-2.0 — depending on it does not put
your project under the AGPL.
Contributions require a CLA; you keep your copyright.
{ "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" } } }