Skip to content

enkinvsh/crystallized

Repository files navigation

c r y s t a l l i z e d

Memory that grows. Identity that forms. Auth that works.

Quick Start · Why · How · FAQ · Troubleshooting · Русский

MIT License Platform Python 3.11+ MCP


Crystallized — persistent memory and identity for opencode

Crystallized is a persistent memory MCP server for opencode, the AI coding agent — it gives your agent long-term memory across sessions. It combines three storage layers — Redis facts, ChromaDB semantic vector search, and markdown documents — under power-law memory decay, so important context stays loud while old noise fades and nothing is deleted. On top of memory it forms an evolving agent identity and ships first-party Anthropic authentication, letting you use your Claude Max plan in opencode without third-party client detection.

What you get

  • Three-layer memory: Redis for instant facts, ChromaDB for semantic search across sessions, filesystem for structured documents
  • Automatic memory injection: every prompt gets enriched with relevant context from previous conversations
  • Agent identity: beliefs, focus areas, and observations start empty and crystallize over time through work
  • Memory decay: power-law fading; important things stay loud, old noise goes quiet, nothing is deleted
  • First-party auth: OAuth tokens extracted from Claude.app; your Max plan, not the $200 third-party credit pool
  • Sisyphus orchestration: oh-my-openagent with parallel agents, skill loading, structured delegation

Requirements

  • macOS (primary) or Linux
  • Python 3.11+
  • Claude.app: installed and logged in with your Max account
  • Homebrew on macOS

Quick start

# Quit Claude.app first (Cmd+Q)
git clone https://github.com/enkinvsh/crystallized.git
cd crystallized
./install.sh
opencode

The installer handles Redis, Python deps, opencode CLI, memory server, config, and auth extraction.

Reproducible installs

The repository tracks memory/uv.lock so every install resolves to the same Python dependency versions. To refresh the lockfile after editing memory/pyproject.toml, run uv lock inside the memory/ directory and commit the updated lockfile.

What install.sh does

  • Checks prerequisites (Git, Python 3.11+, Homebrew on macOS, jq).
  • Installs and starts Redis via Homebrew (or apt on Linux), or shares an existing local Redis on port 6379.
  • Installs the uv Python package manager from astral.sh.
  • Installs the opencode CLI from GitHub releases if it is not already on PATH.
  • Deploys the memory MCP server scripts to ~/.config/opencode/memory/.
  • Installs Python dependencies into ~/.config/opencode/memory/.venv via uv sync --frozen.
  • Seeds identity templates (beliefs, focus, observations, journal) into ~/.config/opencode/memory/notes/.
  • Writes or merges ~/.config/opencode/opencode.json with the memory MCP entry and pre-prompt hooks. Before merging, it backs up the existing file.
  • On macOS, extracts your Claude.app OAuth tokens via Keychain and writes them to ~/.local/share/opencode/auth.json.

What install.sh does NOT do

  • Does not modify Claude.app or its files.
  • Does not change your shell rc files. If opencode is not on PATH after install, the installer prints a one-line export PATH=... hint for you to add yourself.
  • Does not phone home. No telemetry, no analytics, no remote logging.
  • Does not work on Windows. WSL is not tested.
  • Does not detect Claude.app outside /Applications/Claude.app. If you installed Claude to a non-default location, run python3 auth/extract_token.py manually.

Caveats

  • The Keychain may prompt for your macOS login password during auth extraction. Pick "Always Allow" to skip future prompts.
  • Linux skips the automatic auth step. You need to extract tokens from a Mac, or use an API key directly, or accept third-party routing.
  • The installer assumes a single-user Mac. Multi-user shared installs are not supported.
  • ChromaDB cold start can take 10 to 30 seconds on the first MCP call while the sentence-transformer model downloads.

Optional runtime environment variables

All runtime environment variables are optional. If you do not set them, the defaults preserve v1.0 behavior.

Variable Purpose
REDIS_URL Full Redis connection URL.
REDIS_HOST Redis host when REDIS_URL is not set.
REDIS_PORT Redis port when REDIS_URL is not set.
REDIS_DB Redis database number.
OPENCODE_MEMORY_SOCKET MCP socket path for memory hooks.
OPENCODE_MEMORY_NOTES_DIR Notes directory for saved documents and identity files.
OPENCODE_MEMORY_CHROMA_DIR ChromaDB persistence directory.

Upgrading from v1.0

Pull the new code, then re-run ./install.sh. The installer backs up opencode.json before merging the new memory hooks and MCP config.

The new environment variables are optional. If you do not set them, the defaults preserve v1.0 behavior.

If something breaks, run ./uninstall.sh, then re-run ./install.sh.

See CHANGELOG.md for full release notes.

Why opencode flags third-party clients (and how Crystallized fixes it)

Anthropic detects third-party clients and routes their API calls to a separate $200 credit pool instead of your Max subscription. Community auth plugins obtain OAuth tokens with a third-party client_id, so every request gets flagged.

Crystallized extracts tokens directly from Claude.app. These carry Claude's own client_id, so Anthropic treats your opencode sessions as first-party. Max plan limits apply normally.

How it works

Three-layer memory architecture (Redis + ChromaDB + filesystem)

memory-inject.py runs as a pre-prompt hook on every message. It searches all three layers for relevant context and prepends it:

Layer Engine Purpose
Facts Redis Names, decisions, preferences, instant key/value lookups
Semantic ChromaDB Vector similarity across everything the agent ever remembered
Documents Filesystem Architecture notes, checklists, session summaries

Decay runs on a power-law schedule. Memories are never deleted, they get quieter.

Identity

own-voice.py injects the agent's evolving identity into each prompt. Beliefs, focus areas, and observations start as empty files and fill up as the agent works. The personality is earned through experience, not configured upfront.

Authentication

auth/extract_token.py decrypts Claude.app's Electron safeStorage (AES-128-CBC via macOS Keychain), extracts OAuth tokens, and writes them to opencode's auth.json. Token refresh is handled by opencode internally, no auth plugin at runtime.

Architecture

~/.config/opencode/
├── opencode.json              # MCP servers, plugins
└── memory/
    ├── server.py              # MCP memory server (Redis + ChromaDB + fs)
    ├── memory-inject.py       # Pre-prompt hook: context injection
    ├── own-voice.py           # Pre-prompt hook: identity injection
    ├── pyproject.toml         # Python dependencies
    ├── chroma_db/             # Vector database (generated)
    ├── notes/self/            # Agent identity (generated)
    │   ├── beliefs.md
    │   ├── focus.md
    │   └── observations.md
    └── identity.json          # Volume map (generated)

~/.local/share/opencode/
└── auth.json                  # OAuth tokens (from Claude.app)

Troubleshooting

"Third-party apps" error, wrong token. Quit Claude.app, then:

python3 auth/extract_token.py

Try each index if you have multiple workspaces.

Memory MCP is red, redis-cli ping should return PONG. Also verify the uv path in opencode.json is absolute. The installer handles this, but manual edits can break it.

Keychain access denied, needs GUI terminal, not pure SSH. Or unlock first:

security unlock-keychain ~/Library/Keychains/login.keychain-db

Linux, auth extraction is macOS-only (Claude.app). Bring tokens from a Mac, use an API key directly, or accept third-party routing.

Development

The memory server is a standalone Python package managed with uv. Run the test suite (67 pytest tests covering facts, semantic search, decay, and identity):

cd memory && uv run pytest tests/ -q

Lint with ruff before sending a change:

uv run ruff check .

Release notes live in CHANGELOG.md; the threat model and disclosure policy live in SECURITY.md.

FAQ

Does opencode forget context between sessions? By default, yes — each session starts cold. Crystallized fixes this: it persists facts, semantic memories, and documents to disk and re-injects the relevant ones into every prompt, so the agent carries long-term memory across sessions.

Will this work with other MCP clients, or only opencode? The memory server is a standard Model Context Protocol server, so any MCP client can connect to it. The installer, pre-prompt hooks, and identity injection are tailored to opencode, so other clients get the memory tools but not the automatic context injection.

Is my data sent anywhere? No. Memory lives entirely on your machine — Redis, ChromaDB, and markdown files on local disk. There is no telemetry, no analytics, and no remote logging. See SECURITY.md for the full threat model.

Why does Anthropic route opencode to a $200 credit pool? Anthropic detects third-party clients by their OAuth client_id and bills them against a separate credit pool instead of your subscription. Crystallized extracts first-party tokens from Claude.app, so opencode is treated as first-party and your Claude Max plan limits apply normally.

Does memory grow unbounded? No. Memory decays on a power-law schedule: entries get quieter over time unless they are reinforced. Nothing is ever deleted — old noise just stops surfacing while important context stays loud.

Is there Windows support? No. Crystallized targets macOS (primary) and Linux. WSL is untested, and auth extraction depends on macOS Keychain.

License

MIT

About

Persistent memory MCP server for opencode — Redis facts, ChromaDB semantic search, document storage, power-law memory decay, agent identity. Plus first-party Claude.app OAuth: use your Claude Max plan without third-party detection. One-command install.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors