Cortex is a turn-key coding agent for open-weight and local models.
Install one binary and run cortex in your project. Cortex finds a local
model server (Ollama, LM Studio, or llama.cpp) and selects a model. If no
server runs, it sets up a free hosted model on OpenRouter. You do not have
to write a configuration file.
Cortex keeps long sessions inside a small model's context window. It keeps
recent turns word for word and replaces older turns with a short outline.
Each outline line has a citation. The agent uses the recall tool to get
the exact messages back from disk.
The agent writes notes about decisions and constraints that it must keep.
Each turn starts with an index of these notes. Between sessions,
cortex learn reads the turn journal and saves the notes that the agent
did not write.
I use Cortex every day to develop Cortex, and Cortex also fixes its own
issues. See Cortex builds Cortex.
Cortex keeps all of its data in plain files under .cortex/. You can read,
edit, or delete these files.
Quick start · Cortex builds Cortex ·
How memory works ·
Project status · Roadmap
Cortex fixes a failing test with qwen3.8-27b on a local server:
A long session does not keep all old messages in the context window. When an old turn is necessary again, the agent recalls it:
you Fix the cache invalidation bug we discussed earlier.
cortex The relevant implementation turn is now in the session outline:
[@session/20260710-143210#m42-58]
I'll recall it before changing the cache behavior.
tool recall @session/20260710-143210#m42-58
cortex The earlier decision was to preserve the stable prompt prefix and
invalidate only the memory-index suffix. I'll verify that invariant
against the current code before editing it.
The outline does not replace the history. The transcript on disk keeps all messages. Cortex only adds to the transcript. It does not change or delete messages.
Type /context to see the context window. The map shows each part of the
window, the demotion watermark, and how much of the prompt the provider
served from its cache:
Cortex fixes issues in its own repository. A self-development loop runs these steps with no person at the keyboard:
- The loop selects an open issue from the queue.
- Cortex plans the change, edits the code, and runs the tests.
- Cortex opens a pull request.
- Claude reviews the pull request. Claude cannot edit the code.
- Cortex fixes each review finding, and the review starts again.
- A pull request merges only after the review approves it and CI passes.
From 2026-09-28 to 2026-10-07, the loop merged
49 pull requests.
Cortex wrote 88% of the commits in them. Claude wrote the other
commits, mostly to merge main into the branches and to fix tests. Cortex used two
open models: a 27B model on a local machine and a 177B model on a second
machine.
The reviews also find patterns in the mistakes that Cortex makes. We write each pattern as an issue, and the loop fixes it. These are some examples:
- #223: per-turn
checkpoints and the
/undocommand. - #212: after Cortex reads web content in a turn, more shell commands need your approval.
- #176:
edit_filefailed again and again because Cortex edited from an old view of the file. Now a failed edit shows the current text of the nearest lines.
Cortex treats the context window as a cache. Most old turns are not necessary for the next step. A summary of old turns loses the details that you can need later. Thus Cortex does not delete old turns or summarize them.
Cortex applies these rules:
- Recent turns stay in the window word for word.
- Older turns become one outline line each, with a citation.
- The
recalltool gets the exact messages for a citation from the transcript. - The start of the prompt stays the same from turn to turn. This lets the provider cache it, and it makes local models faster.
Durable memory is the decision of the agent. It is not automatic. The agent
writes a named note when a fact must stay after the session ends. Each turn
starts with an index of all notes. The cortex learn command runs a
background pass over the turn journal. It saves notes that the foreground
agent did not write.
Cortex does not use embeddings or a hidden database. Sessions, notes, the
journal, and citations are plain files under .cortex/.
Cortex operates on macOS and Linux. We do not build or test it on Windows. You also need a model, local or hosted. See Quick start.
Option 1: install script. The script installs the latest release binary
to ~/.local/bin. If no release binary is available, it uses
go install. You can read scripts/install.sh
before you run it.
curl -fsSL https://raw.githubusercontent.com/dereksantos/cortex/main/scripts/install.sh | bashOption 2: Go 1.26 or later.
go install github.com/dereksantos/cortex/cmd/cortex@latestOption 3: build from source.
git clone https://github.com/dereksantos/cortex.git
cd cortex
go build -o bin/cortex ./cmd/cortexMake sure that the binary runs:
cortex --version # or ./bin/cortex --versionThe cortex version command gives the same result. The version string
includes the short git revision of the build. To set a different version,
use this command:
go build -ldflags "-X main.Version=1.2.3" -o bin/cortex ./cmd/cortex.
If you use a coding agent, you can give it this prompt:
Install the Cortex coding harness (github.com/dereksantos/cortex) on this
machine:
1. Run: curl -fsSL https://raw.githubusercontent.com/dereksantos/cortex/main/scripts/install.sh | bash
(or `go install github.com/dereksantos/cortex/cmd/cortex@latest` if a
Go 1.26+ toolchain is available and you prefer it).
2. Verify `cortex --version` prints a version, and tell me what it printed.
3. Read the "Quick start" section of
https://raw.githubusercontent.com/dereksantos/cortex/main/README.md and
set up my backend. Ask me one question first: local model (Ollama,
LM Studio, or llama.cpp), or an OpenRouter API key? For a local model,
make sure the server is running with a model pulled; Cortex detects it
on first run, so no config file is needed.
4. Don't launch cortex yourself — it's an interactive REPL. Just tell me
it's ready and that I start it by running `cortex`.
curl -fsSL https://raw.githubusercontent.com/dereksantos/cortex/main/scripts/install.sh | bashFor other methods, see Install.
cd your-project
cortexOn the first run, Cortex looks for a model in this sequence:
-
An OpenRouter key in
OPENROUTER_API_KEYor the macOS Keychain. Cortex uses a free model from a curated list. -
A local model server: Ollama (port 11434), LM Studio (port 1234), or a llama.cpp server (port 8080). Cortex selects an installed model and asks one question:
Found Ollama with qwen2.5-coder:14b (32k context). Use it? [Y/n]Press Enter to accept.
-
The OpenRouter setup. Cortex asks for an OpenRouter API key. You can get a free key at https://openrouter.ai/keys. Cortex keeps the key in the macOS Keychain and selects a free model.
Cortex saves the result in ~/.cortex/config.json. Later runs use it and
do not ask again. When the model replies with a short greeting, the setup
is complete.
If the input is not a terminal (for example, cortex turn in a script),
Cortex uses an OpenRouter key or a local server without a question. If it
finds neither, it prints one line of help.
This is a good first prompt:
Study this repository, explain its architecture, and identify the smallest
high-value improvement. Don't change anything yet.
Cortex saves each session to .cortex/sessions/<id>.jsonl. To continue a
session, use this command:
cortex resumeIf you do not give a session id, resume opens a list of sessions:
- Type text to filter the list by prompt, session id, or model.
- Push Enter to continue the selected session.
- Push ESC to continue the latest session.
If you give a session id, Cortex continues that session. If the output is
not a color terminal (a pipe, NO_COLOR, or CORTEX_LOOP_RENDER=0),
Cortex continues the latest session and does not open the list.
Cortex selects a model by name and by size. It prefers coding models that
can call tools, and the largest model that fits the memory of the
computer. It uses the same model for the code role and the study role.
- If the server has no model that Cortex can use, Cortex prints the
command to get one, for example
ollama pull qwen2.5-coder:14b. Cortex does not download a model. - If the selected model has fewer than 7B parameters, Cortex tells you
that small models often fail at agent work. Press
nto use a free hosted model instead. - Ollama does not let Cortex set the context size for each request. Thus
Cortex creates a copy of the model with the correct context size, for
example
cortex-qwen2.5-coder:14b-32k. The copy uses the same model files.
See "Interactive first-run setup" in
docs/configuration.md.
With no models section, Cortex selects a free model from a curated list
(cmd/cortex/curated.go). The list prefers models with large context
windows and good coding results. If a model is retired or fails during a
session, Cortex changes to the next model on the list. It prints one line
with the old model, the new model, and the reason, and writes a journal
entry. The cortex model command shows recent entries. Cortex does not
change your configuration file. See
docs/model-self-healing.md and
network.self_heal in docs/configuration.md.
Set the models yourself
Write .cortex/config.json in the project, or ~/.cortex/config.json for
all projects.
Ollama. Make sure that Ollama uses a context size that is equal to
window or larger. If it does not, Ollama cuts long prompts. To do this,
start Ollama with OLLAMA_CONTEXT_LENGTH=32768 ollama serve, or use a
model that has its own num_ctx, such as the cortex- copy. At start,
Cortex prints a warning if the num_ctx of the model is smaller than
window or not set. Cortex cannot see OLLAMA_CONTEXT_LENGTH.
{
"backend": {
"type": "ollama",
"endpoint": "http://localhost:11434/v1"
},
"models": {
"code": { "model": "qwen2.5-coder:7b", "window": 32768 },
"study": { "model": "qwen2.5-coder:7b", "window": 32768 }
}
}OpenRouter. Put the API key in an environment variable. The
configuration file contains only the name of the variable. Leave out
models to use the curated free models.
{
"backend": {
"type": "openrouter",
"endpoint": "https://openrouter.ai/api/v1",
"key_env": "OPENROUTER_API_KEY"
},
"models": {
"code": { "model": "qwen/qwen3-coder", "window": 131072 },
"study": { "model": "qwen/qwen3-coder", "window": 131072 }
}
}When a backend is configured, Cortex can read this README and help you start. To get a short guide for your setup, use the Study subagent:
./bin/cortex study README.md \
"Explain how to start Cortex with my backend, then suggest a safe first task"To let the coding agent examine your checkout, use one headless turn:
./bin/cortex turn \
"Read README.md, inspect this checkout, and tell me whether I am ready to run Cortex. Do not change anything."flowchart LR
U[User turn] --> A[Coding agent]
H[Recent turns<br/>verbatim] --> A
O[Older turns<br/>cited outline] --> A
O -->|recall citation| T[Append-only transcript]
T -->|exact messages| A
N[Durable named notes] -->|note index| A
A -->|memory tools| N
A --> J[Per-turn journal]
A --> S[Bounded Study subagent]
S --> R[Targeted repository reads]
Cortex keeps three different records:
- Transcript: the full session history. Cortex saves every message. Demotion removes a message from the context window, but not from the transcript.
- Working set: the data that the model sees now. The first part of the prompt contains the system prompt, the outline, and the note index. This part stays the same between turns. The last part contains recent turns word for word. When the window becomes too full, Cortex moves the oldest full turns into the outline.
- Named notes: memory between sessions. The model decides which facts
to keep. It uses
memory_write,memory_read,memory_search, andmemory_forgetto manage the notes.
The recall tool connects the working set to the transcript. The
/compact command makes a summary of the session. It is a safety
procedure, not the primary memory method. Cortex also writes each turn to
an append-only journal. The study tool can read the journal. The
cortex learn command reads the journal to find new notes.
For the full design, read
docs/context-architecture.md,
docs/memory-tools.md, and
docs/learning-loop.md.
- Find code:
outline,grep,read_file, and thestudysubagent find the necessary code. They do not put the full repository into context. - Change code:
write_file,edit_file(orapply_patch), andremove_pathchange files.remove_pathoperates only in the workspace. - Make sure that changes work:
bashruns builds and tests. A risk gate examines each command first. - Search the web:
web_searchandfetch_urlgive limited access to public web pages. Only the coding agent can use them. - Remember: the
memory_*tools manage notes.recallgets the exact messages of old turns. - Manage context: the
context_*tools remove or merge outline lines. They also move the demotion watermarks, within limits. - Delegate: the
agentsubagent does one task. It reads, edits, and runsbashto make sure that the change works. Then it reports the result.
The Study subagent can only read. It uses outline, grep, and
read_file on small ranges. It cannot edit files, run commands, see the
conversation, or start other subagents.
The agent subagent can write files, edit files, and run bash. These
limits apply to it:
- It cannot start a subagent.
- It refuses Risky shell commands.
- It cannot use
recall, the memory tools, or the context tools.
The agent subagent uses the current coding model. To use a different
model, give the model argument. See docs/agent-tool.md
for the design. To disable the tool, set tools.enable_agent to false.
All tools
| Tool | Purpose |
|---|---|
read_file |
Reads a file or a range of lines. For a large file, it tells the agent to use Study, or it gives a Go declaration skeleton. It attaches a full image file (png, jpeg, gif, webp) for vision models. It refuses an image for a text-only model, for a file that is too large, or in a subagent. |
write_file |
Creates or replaces a file. |
edit_file |
Replaces exact text in a file. It also accepts whitespace differences and multiple edits at a time. If it fails, it gives the line numbers of the matches or the nearest text. If it succeeds, it shows the changed lines. A missing new_string is an error; deleting text takes an explicit empty new_string. |
apply_patch |
Edits files with a patch (*** Begin Patch … *** End Patch: add, update, move, or delete files). It replaces edit_file for model families trained on this format, such as gpt-oss, GPT-5, and Codex models (tools.edit_format). The model gets one of the two tools, never both. Each change is matched the same way as edit_file. |
study |
Reads a large file or directory and gives a summary for a goal. |
agent |
Gives one task to a subagent that can read, edit, and run bash. |
outline |
Shows the structure of a project or a file without its full contents. |
grep |
Finds text and gives a limited list of file:line:text results. |
bash |
Runs a shell command after the risk gate examines it. |
remove_path |
Deletes a path in the workspace. It refuses the workspace root, .git, and .cortex. |
web_search |
Searches the public web. It gives titles, URLs, and short extracts. |
fetch_url |
Gets limited text from a public HTTP(S) URL. It refuses private and local addresses. |
scan_landscape |
Finds agent harnesses and local model runtimes in your home directory. It only checks if they exist. It does not read projects. It writes a landscape note. |
memory_write, memory_read |
Creates or updates a named note, or reads a full note. |
memory_search, memory_forget |
Finds notes by keyword, or deletes a note. |
recall |
Gets the exact messages for an outline citation. With budget, it gives a short summary. |
context_evict |
Removes an outline line from the working set. The transcript does not change. |
context_merge |
Merges adjacent demoted turns into one outline line. |
context_adjust_watermarks |
Moves the demotion watermarks, within limits. |
CLI and REPL commands
cortex start the interactive REPL
cortex --version | cortex version print the version
cortex completion <bash|zsh|fish> print a shell completion script
cortex resume [id] continue a session; with no id, select one
from a list (or the latest when not a TTY)
cortex turn [--session id] [--plan] [--json] <input...>
run one headless turn; --plan makes a plan,
then runs each step as a separate turn (as /plan)
cortex study <path> [goal...] run the read-only Study subagent
cortex learn [--project <name>] run one learning pass over the journal
cortex change <start|commit|status> manage one git change at a time
cortex serve [--port <n>] start the local web UI (loopback only)
cortex scan [--json] [--root <path>] [--register]
find projects in the configured roots
cortex project <add|list|remove> manage the project registry
cortex project trust <add|remove|list>
manage the trusted workspaces (the
post-edit hook runs only in these)
cortex project commands [--json] [--project <name>]
show the format, lint, test, and build
commands of the project
cortex discord start the Discord adapter
cortex study-eval run the Study acceptance test
cortex model [--json] show the code and study models, the models
that the backend serves, and a models
block for the RAM of this machine
With cortex turn, stdout contains only the answer. The resume banner and the
session id go to stderr.
cortex serve starts a local web UI for your projects and sessions. This
image shows a real session. The agent reads main.go, adds a helper, and
writes table-driven tests:
| REPL command | Purpose |
|---|---|
/help |
Shows the list of commands. |
/context |
Shows a map of the context window and the cache use of the last request. |
/compact |
Makes a summary of the session now, to free context. |
/plan <task> |
Makes a plan in one turn, then runs each step as a separate turn. This is the same as cortex turn --plan. If a step cannot reproduce a reported problem, its reply starts with "Not reproduced:" and the evidence. |
/clear |
Starts a new session. |
/undo [N] |
Reverts the file changes of the Nth most recent turn (the default is 1). It shows the changed files and tells the model about the revert. |
/sessions |
Shows a list of saved sessions. Type to filter. Enter continues a session. ESC closes the list. |
/last |
Shows all tool calls of the last turn in full, with diffs and output. Ctrl-O does the same. |
/memory |
Shows the saved notes. You can read them, but not change them. The agent changes notes with the memory tools. |
/model [name] |
Changes the coding model. With no name, it shows a list. |
/hook off|format|all |
Decreases the post-edit hook level for this session. It cannot increase the level. With no argument, it shows the current level. |
/quit, /exit |
Stops Cortex. Ctrl-D also stops Cortex. |
In the REPL, Tab completes slash commands, /model ids, and @path file
mentions. Alt-Enter adds a new line. An @path mention attaches the file
to the turn:
- Cortex puts a small file into the turn as text.
- For a large file, Cortex attaches an outline. These are the same rules
as
read_file. - An
@is a mention only at the start of a word. Email addresses and names such as@types/nodestay as text. - If the path is not a file that Cortex can read, the text does not change.
To save or delete a note, tell the agent in plain language. The agent uses
the memory_* tools.
Cortex reads the configuration in this sequence. A later layer overrides the fields of an earlier layer:
~/.cortex/config.json(or$CORTEX_HOME/config.json)- The nearest
.cortex/config.jsonin the project - The
CORTEX_BACKENDenvironment variable (endpoint only)
{
"backend": {
"type": "openrouter",
"endpoint": "https://openrouter.ai/api/v1",
"key_env": "OPENROUTER_API_KEY"
},
"models": {
"code": { "model": "qwen/qwen3-coder", "window": 131072 },
"study": { "model": "deepseek/deepseek-r1" }
}
}There are two model roles. The code role is the coding agent. The study
role is the Study subagent and the summarizer. With an openrouter backend
and no models section, Cortex selects a free curated model for the two
roles. docs/configuration.md is the reference for
all tools.* settings, environment variables, and API key options.
Cortex keeps its data on your computer. If you use a hosted model, Cortex sends prompts and some tool results to the provider.
Cortex keeps its data under .cortex/. In a git workspace, the first
session writes .cortex/.gitignore with one line, *. Thus git add -A
does not add transcripts, the journal, or notes. Cortex does not change
your own .gitignore.
- Cortex puts each shell command into one of three classes: Safe, Risky, or Blocked. You must approve a Risky command. A headless session refuses it.
remove_pathdeletes only in the workspace. It cannot delete the workspace root,.git, or.cortex.fetch_urlaccepts only public HTTP(S) addresses. It examines each redirect and limits the size of the response.- Study can only read. It has limits on iterations, output tokens, bytes read, and time. It also stops when it makes no progress.
- Demotion does not delete data. The transcript keeps the full history.
See SECURITY.md to report a vulnerability.
We continue to evaluate the working-memory design. Deterministic tests use the real turn path. They make sure that:
- each session message is in the context window or can be recalled with a citation;
- the working set stays within its configured budget;
- demotion keeps each tool call together with its result;
- outline labels and citations stay correct after folds and resumes;
- the start of the prompt stays the same between normal turns, for cache reuse;
- notes stay between sessions, and the agent can update and delete them;
- Study can only read, and it stays within its limits.
Live evaluations against real models also test recall, prompt size, memory, and Study accuracy. An environment variable enables them. These evaluations have limits. The context runs are short. Study results change with the model and backend. Recall is more difficult when the outline does not show that an old turn is relevant.
For the methods and results, read
docs/context-architecture.md,
docs/memory-tools.md, and
docs/study-subagent.md.
Available now
- interactive, headless, resumed, and Discord sessions;
- a two-part context window with exact recall;
- notes that the model manages, a journal of each turn, and a background
learning pass (
cortex learn); - model self-healing: Cortex finds failures, identifies their cause, and changes to a curated free model;
- tools to code, find code, run commands, search the web, and manage context;
- different models for the
codeandstudyroles, on OpenAI-compatible backends.
In progress
- installation and release packages;
- support for more models and backends, and reliable tool calls;
- evaluation of recall in long sessions;
- stable configuration and context policy;
- learning methods other than the journal pass.
Not planned now
- a full browser or a tool that runs JavaScript on web pages;
- the old retrieval, rerank, and distill pipeline;
- claims of better model quality before longer evaluations exist.
Earlier versions included a daemon, a dashboard, a larger CLI, and a
Claude Code integration. We removed them to focus on one binary. Their
history is in docs/archive.md.
go build ./cmd/cortex
go test ./...
go vet ./...
./scripts/check.sh # gofmt, go vet, and golangci-lintThese are the main packages:
cmd/cortex/ binary, REPL, adapters, session setup, shared loop
internal/agent/ tool-call types and loop limits
internal/cache/ the two-part working set
internal/tools/ tool declarations and implementations
internal/outline/ structure maps of projects and files
internal/memory/ the note store
internal/journal/ the append-only event log
pkg/llm/ Anthropic, Ollama, OpenRouter, and OpenAI-compatible providers
pkg/config/ layered configuration
CLAUDE.md: guide for agents and contributorsROADMAP.md: direction and statusdocs/configuration.md: all configuration fields,tools.*settings, and environment variablesdocs/context-architecture.md: the two-part context window and citation recalldocs/memory-tools.md: notes that the model managesdocs/learning-loop.md: the background learning passdocs/model-self-healing.md: model discovery, fallback, and diagnosisdocs/engine-unification.md: the shared agent loopdocs/study-subagent.md: the read-only Study subagentdocs/archive.md: the project before the current design


