Catalyst Code includes a persistent self-learning layer that lets the AI remember facts, conventions, and workflows across sessions. This is not a hidden background process — the model reflects and persists explicitly, in band with the conversation, so every save is explainable and reviewable.
For a full design discussion, see docs/SELF_LEARNING.md.
Save a durable fact:
That's a great convention. Let me remember it.
→ memory(name: "repository-conventions", content: "…", scope: "workspace")
Recall what's already known:
When you start a new session, every stored memory is automatically injected into the system prompt. No manual recall needed.
Learn about a new repository:
/index
Deliberately reflect:
/reflect
Memories are markdown files with YAML frontmatter stored on disk:
---
name: project-conventions
description: Coding conventions for this repository
type: convention
importance: high
---
## Naming
- Rust modules use snake_case file names.
- Public API functions are documented with doc comments.
## Testing
- Integration tests go in `tests/` directory.
- Use table-driven tests for error cases.| Field | Values | Description |
|---|---|---|
name |
string | Unique memory name (identifier) |
description |
string | One-line summary shown in the memory catalog |
type |
convention / decision / gotcha / architecture / note / preference / user / identity |
Category |
importance |
high / normal / low |
Prominence in the prompt |
scope |
workspace / global |
Visibility boundary |
pinned |
true / false |
Always included regardless of relevance |
A MEMORY.md file is maintained automatically in each memory directory. It
lists all memories with their name, type, and description — this is what the
model sees in the system prompt catalog.
Stored at ~/.config/catalyst-code/memory/<workspace-hash>/ — scoped to the
current project directory. Different projects have entirely separate memory
stores.
Use for: project conventions, architecture decisions, repository-specific gotchas, local build knowledge.
Stored at ~/.config/catalyst-code/memory/global/ — shared across all
projects. A global fact is available regardless of which workspace is open.
Use for: the user's name, preferred tech stack, harness conventions, identity facts that apply to every project.
Choose the scope when saving:
memory(name: "my-fact", content: "…", scope: "workspace")— defaultmemory(name: "my-fact", content: "…", scope: "global")— cross-repo
Save a quick memory from the TUI. Opens a modal where you type the fact to store. The core saves it as a new workspace memory.
List all stored memories. The TUI displays the catalog: each memory's name, type, description, and importance.
Open the memory picker to select and delete a memory. Deletion is permanent
(removes the .md file from disk).
Run /index when you first open an unfamiliar repository. The core:
-
Walks source files (skipping
target/,node_modules/, binaries). -
Extracts declarations with tree-sitter (ast-grep); falls back to line scanners.
-
Writes
files.jsonl/symbols.jsonl/relations.jsonlplusdigest.md. -
Saves a pinned workspace
repo-indexmemory from that digest. -
Later turns see the digest via
knowledgecontext and the per-turn context pack.
/index --incremental reuses unchanged file hashes. A schema bump forces a full rebuild.
Run /reflect at any point (not just turn end) to:
- Review the work done so far in the current session.
- Distill durable facts (conventions, decisions, gotchas, architecture).
- Save them via the
memorytool. - Optionally write a skill under
.catalyst-code/skills/.
Like /index, this is exempt from auto-reflect.
At the end of every non-trivial turn (at least one tool call, by default), the core injects a reflect nudge into the model's completion prompt:
[auto-reflect] Before you write your completion summary, reflect on this turn. (1) If you learned a durable convention, architecture fact, decision, or gotcha, persist it with the
memorytool… (2) If you just performed a reusable workflow, consider writing a skill…
After reflection, the model must write a user-facing completion summary
before finish. If it tries to exit with only tool calls (or an empty
finish), the core emits summary_required and re-prompts up to twice so the
user still gets a final message.
| Config key | Default | Description |
|---|---|---|
auto_reflect |
true |
Enable auto-reflect on turn completion |
auto_reflect_min_tool_calls |
1 |
Minimum tool calls for auto-reflect to trigger |
The nudge also includes recurring pattern detection — if the same file categories (e.g. "edit|core/src/*.rs") have been modified 2+ times across sessions, the pattern is included in the nudge so the model knows to write a skill.
- Deduplication: On each save, a memory with the same
nameis updated, not duplicated. Useappendaction to add to an existing topic memory. - Importance: Set
importance: highfor durable facts that should survive compaction or pruning. The store auto-pinsconvention,decision,gotcha, andidentitytype memories. - Catalog: The system prompt carries a capped MEMORY CATALOG (name + one-line
description). Full text is fetched on demand via
memoryactionget.
A skill is a markdown file at .catalyst-code/skills/<name>/SKILL.md with
YAML frontmatter and a body describing when and how to perform a reusable
workflow:
---
name: api-endpoint-creation
description: Add a new HTTP endpoint following this repo's conventions
triggers: [add endpoint, new route, add api]
---
## When to use
You are adding a new HTTP route/handler.
## Steps
1. Read an existing endpoint in src/routes/ to mirror its structure.
2. Add the handler + route registration.
3. Add the request/response types.
4. Add a test mirroring tests/routes/.
## Example
…- Read from the prompt:
/skill:<name>references a skill that the model has already seen. If the skill is in.catalyst-code/skills/, the model can also read it withread_file. - The
pi-subagentsskill is kept as a short stub in the orchestrator system prompt; the full playbook is opt-in via/skill:pi-subagents. - After auto-reflect, the model may write a new skill if a recurring pattern is detected.
A skill should be written when:
- You performed a reusable workflow (same shape 2+ times).
- No existing skill covers it.
- The workflow benefits from a structured approach rather than model improvisation.
Skills are advisory prompt fragments, not executable code. If you need deterministic side effects, write a plugin instead.
Every session starts by injecting all stored memories into the stable system prompt. This prompt is built once on turn 1 and refreshed only when memories or skills change, preserving the provider's prefix cache.
The injection includes:
- Memory catalog: each memory's name and one-line description (capped to avoid overflowing the prompt).
- Relevant memory tails: if the current prompt matches keywords in stored memories, those memories' full text is included.
After any memory mutation (save/append/forget), the injection is refreshed so the next turn uses the updated catalog.
The model can call the memory tool directly during a turn. It is classified
ReadOnly (no approval gate needed) because it only touches
~/.config/catalyst-code/memory/, never the workspace.
| Action | Description |
|---|---|
save |
Create a new memory. Requires name and content. Optional: type, description, scope, importance, force. |
append |
Append content to an existing memory (same name). If the memory doesn't exist, creates it. |
list |
Show the memory catalog (all names + descriptions for the current scope). |
forget |
Delete a memory by id (name). |
get |
Retrieve the full text of a single memory by id. |
consolidate |
Merge near-duplicate memories. |
stats |
Show recall quality metrics (hit rate, cache efficiency). |
{
"action": "save",
"name": "project-build-system",
"content": "This repo uses Cargo workspaces with a root workspace Cargo.toml.\n`cargo build -p core` builds only the core crate.",
"type": "architecture",
"scope": "workspace",
"importance": "high"
}The knowledge tool provides read-only access to structured codebase
intelligence. It is classified ReadOnly and returns compact strings.
| Action | Description |
|---|---|
context |
Current workspace context (structure, entry points) |
search |
Search the codebase index for symbols or text |
symbol |
Resolve a symbol to its definition location |
related |
Find related files or modules (change coupling) |
episodes |
Retrieve past coding episodes relevant to the query |
preferences |
User's project-level preferences |
rejected |
Previously rejected approaches (avoid repeating mistakes) |
coverage |
Documentation coverage report |
explain |
Explain a file or function from the index |
tests |
Find tests for a given source file or symbol |
All actions are fail-open ��� they return a best-effort string even when the index is incomplete.
The session_stop plugin hook fires at the end of each turn. This is the
deterministic seam for side-effecting reflection — if you want to run
custom learning logic (e.g. writing a telemetry record or a derived memory)
without trusting the model's prompt compliance, implement a plugin that
hooks session_stop.
For the full design rationale, read docs/SELF_LEARNING.md.
Key decisions:
- Memory is the AI's notebook, not the user's codebase. Memory files live in
~/.config/catalyst-code/memory/, never in the workspace, so mutations are ReadOnly from the workspace perspective. - Learning is prompt-driven, not a hidden daemon. The model reflects in-band where the user can see and steer it.
- Markdown, not a vector database. A markdown file on disk beats a vector
database until retrieval measurably fails. The upgrade path to embedding-based
retrieval is documented but deferred (see
src/embed.rs). - Skills are prompt fragments, not workflows. A skill advises the model; a plugin executes deterministic side effects.