What It Does
Cortex turns your working memory into a searchable knowledge base. Every Claude Code session gets automatically recorded when it ends, then can be distilled and retrieved later.
- Automatic capture — a full report (commits, findings, decisions) is generated when a session ends and saved to the vault
- Semantic search — OpenAI embeddings + ChromaDB, with mixed Chinese/English query support
- Knowledge distillation — extracts hard-won lessons, internal conventions, and key decisions from raw session dumps
- Memory injection — detects the current repo at session start and offers to load related memory
Design philosophy: the vault is the source of truth (plain markdown + git); the vector store is just a rebuildable derived index.
Quick Start
1. Install the plugin
# From GitHub
/plugin marketplace add https://github.com/XBlueSky/cortexes.git#plugin
2. Install the cortex-vec CLI
The CLI is published on PyPI as
cortex-vec:
# Recommended: isolated tool install via uv
uv tool install cortex-vec
# Or with pip
pip install cortex-vec
Upgrade with uv tool upgrade cortex-vec (or pip install -U cortex-vec)
after a plugin update. /cortex:genesis checks for the CLI and offers this
install when it is missing. To run the unreleased development version
instead, install from the repo:
uv tool install "git+https://github.com/XBlueSky/cortexes.git@plugin#subdirectory=cortex-vec"
Requires the OPENAI_API_KEY environment variable (used for embeddings).
Without it, search degrades to BM25-only — see
Environment Variables.
3. Initialize
/cortex:genesis /path/to/your/vault
This sets the vault path and author info, and builds the semantic index.
4. Start using it
"save to cortex" → manually save knowledge
"check cortex" → semantic search over the vault
"distill" → extract knowledge from Raw/
"broadcast" → fuse new Raw content into existing pages
Website
Live docs and changelog: https://cortexes.pages.dev (Cloudflare Pages,
generated from .cc-marketspec/dist/manifest.json).
See site/README.md for local builds.
Commands & Skills
Commands
| Command | Description |
|---|---|
/cortex:genesis |
Initialize the vault — set path, author, rebuild the index |
/cortex:evolve |
Manually save knowledge to Notes or Projects (also writes log.md) |
/cortex:distill |
Distill Raw/ session records into Notes/Projects (map-first navigation + two-stage evaluation + pending-merge exit) |
/cortex:broadcast |
Fuse newly distilled content into related existing pages (llm-wiki-style ingest) |
/cortex:takeoff |
Hand-off batons — curate temporary, non-git hand-offs for a later session to resume, one per work line ([topic] / resume [topic] / done [topic] subcommands) |
Skills (auto-triggered)
| Skill | Trigger |
|---|---|
| cortex-evolve | "save to cortex", "note this down", "remember this" |
| cortex-distill | "distill", "clean up raw", "distill raw records" |
| cortex-broadcast | "broadcast", "merge pending-merge", "fuse this into the vault" |
| cortex-takeoff | "hand off", "takeoff", "hand off to next session", "context is running low" |
| cortex-query | "check cortex", "have I noted this before", "is this in cortex" |
Hooks
| Hook | Event | Behavior |
|---|---|---|
| Session Report | SessionEnd | On session end, filters the transcript through a TOML pipeline before writing to Raw/ |
| Memory Injection | SessionStart | Interactive menu — checks vault backlog status and asks what to do next |
Recording is automatic. Every session over 4 KB is written to your vault when it ends — there is no per-session prompt. Set
CORTEX_SKIP_RECORD=1to skip a session, and seePRIVACY.mdfor exactly what is captured, what is excluded, and what (if anything) leaves your machine.
Transcript Filter (0.9.0+)
Before writing to Raw/, the SessionEnd hook runs a TOML-driven filter pipeline that
strips tool output with no knowledge value (e.g. ls, volume listings,
repetitive build logs) — you can write custom filters per slash command so
what lands in Raw/ actually carries signal.
Architecture
cortex repo
├── plugin branch (orphan) ← the Claude Code plugin (this file lives here)
└── main branch ← Obsidian vault data
~/.cortex/
├── config.json ← settings produced by genesis
└── vectorstore/ ← ChromaDB semantic index (local only, not in git)
Vault Structure
Raw/YYYY/MM/DD/ ← session dumps (complete, distilled on demand)
Notes/<category>/ ← distilled technical knowledge
Projects/<repo-name>/ ← project notes organized by repo
_index.md ← vault-wide summary index
log.md ← chronological history of evolve/distill
Nothing machine-checks _index.md for consistency (reorganizing pages is the
one path that changes the index with no skill involved). See
docs/index-audit.md for the audit, and for the regex
traps that let a naive check pass while reporting the wrong set.
Data Flow
Every session:
SessionStart → surfaces available memory → user decides whether to load it
...work happens...
session ends → SessionEnd hook → filter → Raw/ (automatic, no prompt)
Anytime:
/cortex:evolve → Notes/Projects + _index.md + log.md + vector store
/cortex:query → vector search → precise file reads
Periodically:
/cortex:distill → Raw → Notes/Projects (+ pending-merge → broadcast)
/cortex:broadcast → pending-merge → fused into existing Notes/Projects
Retrieval Strategy
Hybrid retrieval (0.4.0+) — BM25 + vector dual streams, fused with Reciprocal Rank Fusion (RRF, k=60), default weights w_bm25=0.4 / w_vec=0.6:
- BM25 stream — exact lexical matching (function names, repo names,
issue IDs), with jieba CJK segmentation for mixed Chinese/English queries.
The index persists at
~/.cortex/bm25/, kept in sync with ChromaDB byrebuild/upsert/delete. - Vector stream — OpenAI
text-embedding-3-smallsemantic search, dual-vector (document body + bilingual summary), covering cases that are semantically close but lexically different. - Degradation strategy — without
OPENAI_API_KEY, or offline, this automatically falls back to BM25-only, with no dependency on the skill-layer grep fallback.
Other layers:
- Raw search (on demand) — only queries original session records when tracing history
cortex-vec CLI
The vault's semantic indexing tool, built on ChromaDB + OpenAI
text-embedding-3-small, paired with gpt-5.4-mini to generate a bilingual
summary as a second embedding (dual-vector) to improve recall for mixed
Chinese/English queries.
cortex-vec status # view index status
cortex-vec rebuild # full index rebuild
cortex-vec search "nginx certificate" # semantic search
cortex-vec search "oauth" --repo acme-core # filter by repo
cortex-vec search "sharing" --type project # filter by type
cortex-vec upsert Notes/Nginx/new.md # add/update a single document
cortex-vec delete Notes/Nginx/old.md # delete a document
Hybrid Retrieval (0.4.0+)
cortex-vec search now defaults to BM25 + vector RRF hybrid, balancing exact
lexical matches with semantic similarity:
cortex-vec search "nginx certificate" # hybrid (default)
cortex-vec search "nginx certificate" --no-bm25 # vector only (debug/eval)
cortex-vec search "nginx certificate" --no-vector # BM25 only (debug/eval)
cortex-vec status # shows both vector and BM25 entry counts
Distillation navigation (1.0.0+)
/cortex:distill drives these read-only commands to walk a Raw without
ever loading the whole file into context. A Raw is parsed once into a
gap-free, overlap-free source partition; raw-span is the only reader that
returns original text, and every page is hard-capped so an oversized session
distills across bounded continuations instead of overflowing context:
cortex-vec distill-queue --root <vault>/Raw --stat # per-Raw projected sizes before a batch
cortex-vec raw-view <raw.md> # budget-bounded L0–L3 projection
cortex-vec distill-plan start <raw.md> # open a coverage/budget plan → plan_id
cortex-vec raw-map <raw.md> --plan-id <id> # navigation cards (kind/size/range/anchors)
cortex-vec raw-span <raw.md> --plan-id <id> --span-id <n> # exact original text, one bounded page
cortex-vec distill-plan status --plan-id <id> # coverage + no-insight gate state
The per-Raw plan lives under $XDG_CACHE_HOME/cortex/distill-plans/ with an
active.json pointer enforcing one active Raw at a time (atomic writes,
user-only permissions, fail-closed on corruption or identity drift).
Raw snapshot reclaim (1.1.0+)
SessionEnd fires more than once per conversation (/clear, exit + --resume)
and re-filters the same growing transcript each time, so the earlier Raws are
strict prefixes of the latest one. The hook now removes those automatically;
this command is the manual form, and the only way to clean up a backlog
recorded before 1.1.0:
cortex-vec reclaim-superseded --root <vault>/Raw # list the whole queue's duplicates
cortex-vec reclaim-superseded --root <vault>/Raw --apply \
--vault <vault> # remove them (staged with git rm)
Only files in the undistilled queue are candidates — a Raw already carrying a
<!-- distilled: --> marker is never touched — and a candidate must be a
prefix of its survivor, so the failure mode is "duplicate stays", never
"content lost".
Retrieval Evaluation
# Step 1: have an LLM draft candidate queries; a human reviews and confirms
# gold paths before they can be used
cortex-vec eval propose --queries eval-data/cortex-vault-v1.jsonl
# Step 2: run all adapters, print NDJSON results, and write a markdown scorecard
cortex-vec eval run \
--queries eval-data/cortex-vault-v1.jsonl \
--adapters grep,vector,bm25,hybrid \
--k 5 \
--out docs/benchmarks/$(date +%Y-%m-%d)-cortex-vault-v1.md
Supported adapters: grep / vector / bm25 / hybrid.
Metrics: P@5 / R@5 / MRR / hit.
Advanced Retrieval (off by default)
The four enhancements below are all disabled by default and require explicit
configuration to enable. Always measure the P@5 / R@5 / MRR lift with
cortex-vec eval run before and after enabling any of them, before
deciding what's worth defaulting to on and what should be reverted.
Synonym expansion
Controlled by config retrieval.synonym_weight (0 = off; try 0.7). The
BM25 stream boosts documents that match a synonym by that weight, so "OAuth"
can match synonyms like "SSO / auth / authorization."
The synonym table lives at cortex-vec/src/cortex_vec/synonyms.py
(common Chinese/English technical terms included) and can be extended.
Wikilink graph-boost
Enabled via cortex-vec search --graph (or config retrieval.graph: true).
Treats "wikilink neighbors of matched results" as a third RRF stream
fused into the ranking — surfacing notes that are clearly linked from a hit
but don't directly match the query themselves (recovering neighbors that
vector/BM25 alone would miss).
Tunable parameters: retrieval.graph_hops (propagation hops),
retrieval.w_graph (the graph stream's weight in RRF, default 0.3),
retrieval.graph_top_k (how many top hits seed the BFS).
Uses rank-based RRF fusion (not additive boosting), so it's neutral for typical queries and doesn't hurt ranking — it only adds recall for "linked" queries. Measured: no difference on a 20-query general corpus with it on/off; R@5 0.50→0.667 on a wikilink-stress corpus.
LLM rerank
Enabled via cortex-vec search --rerank (or config retrieval.rerank: true). Calls OpenAI (model set by retrieval.rerank_model, default
gpt-5.4-mini) to re-rank the top retrieval.rerank_window (default 15)
hybrid results, replacing pure score ranking with LLM relevance judgment. Any
failure (API error / timeout) automatically falls back to the original RRF
order without affecting search availability.
max-per-repo diversification
Controlled by config retrieval.max_per_repo (0 = unlimited), caps how
many results from the same repo can appear in the top-k, preventing one
large repo from drowning out results from other sources.
Full retrieval config example
The advanced keys under retrieval in ~/.cortex/config.json and their
defaults:
{
"retrieval": {
"synonym_weight": 0,
"graph": false,
"graph_hops": 1,
"w_graph": 0.3,
"graph_top_k": 5,
"rerank": false,
"rerank_model": "gpt-5.4-mini",
"rerank_window": 15,
"max_per_repo": 0
}
}
Using it with CLI flags:
# Enable graph-boost + rerank (one-off test)
cortex-vec search "OAuth token" --graph --rerank
# Set synonym_weight, then run eval to confirm the lift
cortex-vec eval run \
--queries eval-data/cortex-vault-v1.jsonl \
--adapters hybrid \
--k 5 \
--out docs/benchmarks/$(date +%Y-%m-%d)-synonym-0.7.md
Configuration
~/.cortex/config.json (generated by genesis):
{
"vault_path": "/path/to/vault",
"author": "your-name",
"author_email": "[email protected]",
"git": {
"auto_commit": true,
"auto_push": false
}
}
Environment Variables
| Variable | Required | Description |
|---|---|---|
OPENAI_API_KEY |
No* | OpenAI API key for text-embedding-3-small. Required for rebuild/upsert/vector search; search automatically falls back to BM25-only without it |
CORTEX_VAULT_PATH |
No | Overrides vault_path from config.json |
CORTEX_SKIP_RECORD |
No | When set (e.g. =1), the SessionEnd hook skips recording this session into Raw/ — for launcher/probe sessions that carry no distill-worthy content |
CORTEX_NO_CLASSIFIER |
No | When set to 1, the transcript filter never calls the LLM classifier; oversized blocks are kept verbatim instead. Nothing is sent to Anthropic |
Dependencies
| Package | Purpose |
|---|---|
| ChromaDB | Semantic vector index |
| OpenAI | text-embedding-3-small embedding model |
| python-frontmatter | YAML frontmatter parsing |
| pysqlite3-binary | SQLite 3.35+ compatibility (needed when the system SQLite is too old) |
Install:
uv tool install cortex-vec
Privacy
Cortexes is local-first: your vault is Markdown on your own disk, the index is local, and the authors receive nothing — there is no server, no account, and no telemetry.
Two features do reach a remote service, both under your control. The
transcript filter sends oversized blocks (>12 KB, capped at 5 per session)
to Anthropic through your own Claude Code to classify them for compression —
disable with CORTEX_NO_CLASSIFIER=1. Semantic indexing sends vault page
content to OpenAI for embeddings — this only happens if you set
OPENAI_API_KEY, and without it retrieval runs entirely on local BM25.
PRIVACY.md documents every data flow in full: what a session
record contains, what is stripped before writing, where files live, git
commit and push behaviour, how to turn each feature off, and how to delete
your data.
Project Structure
commands/ Slash commands (/cortex:*)
skills/ Self-triggering skills
hooks/ SessionStart/SessionEnd lifecycle hooks
cortex-vec/ Python semantic indexing CLI
site/ Static site generator for cortexes.pages.dev
docs/ Design specs, plans, and benchmark reports
scripts/ Dev tooling (e.g. run-checks.sh)
tests/ Plugin-level tests
Contributing
Contributions are welcome — see CONTRIBUTING.md for dev setup, tests, and the PR process. Please also read the Code of Conduct. Security issues should be reported privately — see SECURITY.md.
Changelog
See CHANGELOG.md for version history.
License
Licensed under the Apache License 2.0 — see LICENSE for the
full text.
Copyright 2026 XBlueSky (see NOTICE).
No comments yet
Be the first to share your take.