How Claude-Mem Context Injection Selects Observations for AI Prompts
Claude-Mem's context injection system selects observations by filtering SQLite records against user-defined type and concept whitelists, then limits results by recency and count before rendering them into the prompt.
Claude-Mem, an open-source context management system for AI coding assistants, automatically prepends relevant historical observations to every prompt through its context injection pipeline. Understanding how this system determines which observations to include helps developers optimize their context windows and ensure critical code history surfaces at the right time. The selection process follows a rigorous three-stage pipeline implemented in TypeScript, querying a local SQLite store to retrieve only the most relevant records.
The Three-Stage Selection Pipeline
The context injection mechanism operates through three distinct phases: configuration loading, database querying, and context assembly. Each phase is implemented in specific service files within the src/services/context/ directory.
Stage 1: Loading Configuration with ContextConfigLoader
The pipeline begins in src/services/context/ContextConfigLoader.ts, where the ContextConfigLoader class reads user-level settings from ~/.claude-mem/settings.json. For non-code modes, it falls back to the active mode's definition to determine which observation types and concepts are relevant.
The configuration exposes three critical parameters that drive selection:
observationTypes– A set of allowed observation types (e.g., code-change, research)observationConcepts– A set of allowed concepts (e.g., bug-fix, refactor)totalObservationCount– The maximum number of observations to fetch
These parameters can also be overridden via environment variables: CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES, CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS, and CLAUDE_MEM_CONTEXT_OBSERVATIONS.
Stage 2: Querying the SQLite Store
Once configuration is loaded, src/services/context/ObservationCompiler.ts executes the selection logic through its queryObservations method (or queryObservationsMulti when working with multiple projects in a worktree). This method translates the configuration into a parameterized SQL query against the SQLite observations table.
The query implements a precise filtering hierarchy:
SELECT … FROM observations
WHERE project = ?
AND type IN ( …place‑holders… )
AND EXISTS (
SELECT 1 FROM json_each(concepts)
WHERE value IN ( …place‑holders… )
)
ORDER BY created_at_epoch DESC
LIMIT ?
The selection criteria operate as follows:
- Project filter guarantees only observations from the current repository (or all worktree projects) are considered
- Type filter (
type IN (…)) restricts rows to theobservationTypesset from configuration - Concept filter (
EXISTS (…)) keeps only rows whoseconceptsJSON array contains at least one entry fromobservationConcepts - Ordering by
created_at_epoch DESCprioritizes the newest observations - Limit enforces the
totalObservationCountmaximum
Stage 3: Assembling the Final Context String
The final stage occurs in src/services/context/ContextBuilder.ts, where the generateContext method orchestrates the process. It calls the query methods, receives the filtered observation list, and passes it through a renderer pipeline (header → timeline → summary → prior-message → footer).
Crucially, the renderer does not add or drop observations; it simply formats the list that the SQL query already limited. This ensures deterministic context injection based strictly on the configuration parameters and recency.
Practical Implementation Examples
Basic Context Generation
To generate context programmatically (which is what the Cursor hook ultimately runs):
import { generateContext } from './src/services/context/ContextBuilder.js';
// Automatically reads settings, picks current project, returns markdown context
const ctx = await generateContext();
console.log(ctx);
Customizing Observation Filters via Environment Variables
Restrict context injection to specific observation types and concepts while limiting the count:
export CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES=bug-fix,refactor
export CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS=critical,performance
export CLAUDE_MEM_CONTEXT_OBSERVATIONS=5
When claude-mem cursor install is active, the beforeSubmitPrompt hook calls the same /api/context/inject endpoint, ensuring these filters apply automatically before every prompt.
Worktree Support: Multi-Project Context Injection
To include observations from both a parent repository and a child worktree:
await generateContext({
projects: ['parent-repo', 'child-worktree'],
cwd: process.cwd()
});
This internally uses queryObservationsMulti to pull data from both SQLite stores and merges results chronologically.
Debugging the SQL Query
To inspect the exact SQL generated for your configuration:
import { queryObservations } from './src/services/context/ObservationCompiler.js';
import { SessionStore } from './src/services/sqlite/SessionStore.js';
import { loadContextConfig } from './src/services/context/ContextConfigLoader.js';
import { getProjectName } from './src/utils/project-name.js';
const db = new SessionStore();
const cfg = loadContextConfig();
const project = getProjectName(process.cwd());
const sql = db.db.prepare(`
SELECT id, type, concepts FROM observations
WHERE project = ?
AND type IN (${Array.from(cfg.observationTypes).map(() => '?').join(',')})
AND EXISTS (SELECT 1 FROM json_each(concepts) WHERE value IN (${Array.from(cfg.observationConcepts).map(() => '?').join(',')}))
ORDER BY created_at_epoch DESC
LIMIT ?
`).source;
console.log(sql);
Summary
- Claude-Mem context injection selects observations through a three-stage pipeline: configuration loading, SQL filtering, and context assembly.
- Configuration in
ContextConfigLoader.tsdefines allowedobservationTypes,observationConcepts, andtotalObservationCountvia settings files or environment variables. - Filtering in
ObservationCompiler.tsexecutes parameterized SQL that filters by project, type whitelist, JSON concept array intersection, and recency before applying a hard limit. - Rendering in
ContextBuilder.tsformats the pre-filtered list without modification, ensuring deterministic context windows. - Worktree support enables multi-project context injection by merging results from multiple SQLite stores chronologically.
Frequently Asked Questions
How does Claude-Mem prioritize which observations to include when the limit is reached?
Claude-Mem prioritizes observations strictly by recency. The SQL query in ObservationCompiler.ts orders results by created_at_epoch DESC before applying the LIMIT clause defined by totalObservationCount. This ensures the newest observations matching your type and concept filters appear in the context window, while older entries are excluded once the maximum count is reached.
Can I include observations from multiple repositories in a single prompt?
Yes, Claude-Mem supports worktree and multi-project contexts through the queryObservationsMulti method. When calling generateContext() with a projects array containing multiple project identifiers, the system queries each project's SQLite store separately and merges the results chronologically by created_at_epoch. This allows parent repositories and child worktrees to contribute relevant historical context to a single prompt.
What happens if no observations match the configured filters?
If the SQL query returns zero rows matching the specified observationTypes, observationConcepts, and project filters, the context injection pipeline continues with an empty observation list. The ContextBuilder.ts renderer formats this empty list into the context string, resulting in a context block that contains only the header, footer, and structural elements without specific historical observations. The prompt still functions normally, simply without injected historical context.
How do environment variables interact with the settings.json configuration?
Environment variables act as overrides to the ~/.claude-mem/settings.json configuration. The ContextConfigLoader.ts service checks for CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES, CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS, and CLAUDE_MEM_CONTEXT_OBSERVATIONS at runtime. If present, these values supersede the corresponding entries in the JSON settings file. For non-code modes, the active mode's definition may also supply default type and concept sets that serve as fallbacks when neither environment variables nor explicit settings are defined.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →