Skip to content

How It Works ​

At the end of each Claude Code or Codex session, Somtum reads the session transcript and asks a small model to extract the parts worth keeping — decisions, bug fixes, things learned. Those observations are stored locally in SQLite, one store per project, shared by both agents. On every subsequent prompt, Somtum retrieves the most relevant memories and injects them into context.

Supported agents ​

AgentCaptureAuto-injectMCP toolsInstall
Claude CodeSessionEnd, PreCompactUserPromptSubmit (default; --no-cache to skip).mcp.jsonsomtum init
CodexSessionEnd, PreCompactUserPromptSubmit (default).codex/config.tomlsomtum init --agent codex
Any other MCP client——point it at somtum mcpmanual

Both agents send hooks the same JSON payload and accept the same reply, so one set of hook handlers serves both. Only transcript parsing differs: Claude Code writes ~/.claude/projects/**.jsonl, Codex writes ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl. Somtum detects the format automatically.

Extraction backends ​

Extraction uses the first available of: the Anthropic API (ANTHROPIC_API_KEY, or ANTHROPIC_AUTH_TOKEN + ANTHROPIC_BASE_URL for GLM and other compatible providers), the claude CLI, or the codex CLI. Before sending, Somtum keeps the conversation and trims tool output and tool-call arguments (whole files inside edits, for example) so you don't pay to send file contents the extractor doesn't need. Pin a backend with extraction.backend.

Memory lifecycle ​

┌─────────────────────────────────────────────────────────────┐
│               Claude Code or Codex session                  │
│          you code · debug · review · make decisions         │
└──────────────────────────────┬──────────────────────────────┘
                               │ SessionEnd / PreCompact
                               ▼
┌─────────────────────────────────────────────────────────────┐
│                     Capture Pipeline                        │
│                                                             │
│  session transcript ──► Haiku extracts observations         │
│                                                             │
│      decisions · bug fixes · learnings · commands           │
│                                                             │
│  PreCompact ─── writes warm-start file ──► next session     │
└──────────────────────────────┬──────────────────────────────┘
                               │ persisted locally
                               ▼
                 ┌─────────────────────────┐
                 │  ~/.somtum/projects/    │
                 │     <project-hash>/     │
                 │                         │
                 │  db.sqlite              │
                 │  index.md               │
                 │  memories/YYYY-MM/      │
                 └────────────┬────────────┘
                              │ every prompt (UserPromptSubmit)
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                   Auto-Inject Pipeline                      │
│                                                             │
│  1. Prompt cache lookup (exact + fuzzy match)               │
│  2. BM25 recall — top-k memories, min_relevance filter      │
│  3. Warm-start context (if session just compacted)          │
│  4. Token budget line prepended (show_budget=true)          │
│                                                             │
│      all injected as additionalContext automatically        │
└─────────────────────────────────────────────────────────────┘

Branches ​

Each memory records the git branch and commit it was captured on. When recalling, Somtum leaves out memories from another branch that still exists and hasn't been merged into yours, so two branches that make different decisions don't contradict each other in your context.

Memory fromRecalled on your branch?
Your current branchYes
The default branch (main, master)Yes
A branch merged into yoursYes
A deleted branch, or one whose upstream is gone (a merged pull request, including squash merges)Yes
Another branch that exists and isn't mergedNo
Another branch, captured before it had a commit of its ownNo, until that branch is deleted
Before 3.2, or outside a git repositoryYes

When two visible memories describe the same decision, only the newer is sent. Injected memories carry their age and, if they came from another branch, its name: [decision · 3 days ago · from feature/zustand]. A branch can only supersede its own memories, so an experiment can't overwrite a decision that still stands on main. For an abandoned branch, somtum forget --branch <name> drops its memories.

What gets captured — an example ​

You debug an auth bug and refactor a module. At session end, Somtum extracts something like:

json
[
  {
    "kind": "bugfix",
    "title": "JWT refresh loop caused by missing expiry check",
    "body": "The refresh token loop was triggered because we checked token.exp < Date.now() instead of token.exp < Date.now() / 1000. Unix timestamps are in seconds, not milliseconds.",
    "files": ["src/auth/refresh.ts"]
  },
  {
    "kind": "decision",
    "title": "Use pnpm workspaces — npm hoisting breaks shared types",
    "body": "Switched from npm to pnpm because npm's hoisting puts shared type packages in the wrong node_modules scope, breaking type inference across packages.",
    "files": ["package.json", "pnpm-workspace.yaml"]
  }
]

Next session, in either agent, when you ask "why are we using pnpm?" or open src/auth/refresh.ts, the agent gets these memories and already has the context.

Architecture ​

┌─────────────────────────────────────────────────────────────┐
│                 Claude Code / Codex / MCP client            │
└──────────┬──────────────────────────────┬───────────────────┘
           │ hooks                        │ MCP tools
           ▼                              ▼
┌─────────────────────┐         ┌──────────────────────────┐
│  Hooks              │         │   MCP Tools              │
│                     │         │                          │
│  UserPromptSubmit ──┼─cache──▶│ cache_lookup             │
│                   ──┼─inject─▶│ recall / get             │
│  SessionEnd ────────┼─capture▶│ remember / update        │
│  PreCompact ────────┼─warmst─▶│ forget                   │
│  PreToolUse (Read) ─┼─gate───▶│ stats                    │
│  (Claude Code only) │         │                          │
│                     │         │ report_false_hit          │
└──────────┬──────────┘         └────────────┬─────────────┘
           │                                 │
           ▼                                 ▼
┌─────────────────────────────────────────────────────────────┐
│                      Core (TypeScript)                      │
│                                                             │
│  ┌──────────────┐  ┌─────────────────┐  ┌───────────────┐  │
│  │ PromptCache  │  │  MemoryStore    │  │   Retriever   │  │
│  │              │  │                 │  │               │  │
│  │ exact hash   │  │ observations    │  │ bm25(default) │  │
│  │ fuzzy embed  │  │ scope: project  │  │ embeddings    │  │
│  │ fingerprint  │  │         global  │  │ index         │  │
│  │ false_hits   │  │       workspace │  │ hybrid        │  │
│  └──────────────┘  └─────────────────┘  └───────────────┘  │
└─────────────────────────────┬───────────────────────────────┘
                              │
                              ▼
                ┌─────────────────────────────┐
                │  SQLite WAL + ~/.somtum/     │
                └─────────────────────────────┘

Retrieval strategies ​

StrategyHow it worksBest forCost
bm25Keyword search over title + body + tags (SQLite FTS5, no external dependencies)Exact terms, offline setupsNear-zero
embeddingsSemantic similarity using a 30 MB local ONNX model (bge-small-en-v1.5, fully in-process)"What did we decide about auth?" style queries~5 ms at 10k memories
indexSends a compact memory catalog to Haiku; the model picks relevant IDsParaphrased or fuzzy queries1 Haiku API call
hybridBM25 + embeddings, merged by reciprocal rank fusionGeneral case (best recall)BM25 + embeddings, no model call

Default is bm25 — works offline, no setup required. Enable hybrid once you have embeddings downloaded.

To switch strategy:

bash
# Enable semantic search (downloads 30 MB model once)
npm install -g @xenova/transformers   # optional dependency, needed once
somtum config set retrieval.embeddings.enabled true
somtum reindex

# Switch to hybrid for best recall
somtum config set retrieval.strategy hybrid

See Configuration for the full options.

Memory kinds ​

Somtum captures observations in six categories:

KindDescription
decisionArchitectural or design choices and their rationale
learningThings discovered during debugging or exploration
bugfixA fix and its root cause
commandUseful CLI commands or workflows
file_summaryA summary of what a file or module does
otherAnything else worth remembering

Released under the MIT License.