remember
Store project-specific conventions, decisions, and failures with reasons to build a persistent codebase memory that guides future development.
Instructions
Routes to the active/current project automatically when known. CALL IMMEDIATELY when user explicitly asks to remember/record something.
USER TRIGGERS:
"Remember this: [X]"
"Record this: [Y]"
"Save this for next time: [Z]"
DO NOT call unless user explicitly requests it.
HOW TO WRITE:
ONE convention per memory (if user lists 5 things, call this 5 times)
memory: 5-10 words (the specific rule)
reason: 1 sentence (why it matters)
Skip: one-time features, code examples, essays
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| type | Yes | Type of memory being recorded. Use "failure" for things that were tried and failed - prevents repeating the same mistakes. | |
| category | Yes | Broader category for filtering | |
| memory | Yes | What to remember (concise) | |
| reason | Yes | Why this matters or what breaks otherwise | |
| scope | No | Optional scope for this memory. Use { kind: "file", file } or { kind: "symbol", file, symbol }. | |
| project | No | Optional project selector for this call. Accepts a project root path, file path, file:// URI, or a relative subproject path under a configured root. | |
| project_directory | No | Deprecated compatibility alias for older clients. Prefer project. |
Implementation Reference
- src/tools/remember.ts:69-157 (handler)The main handler function that executes the 'remember' tool logic. It takes user args (type, category, memory, reason, optional scope), creates a SHA-256 content-hash ID, constructs a Memory object, and appends it to the memory file via appendMemoryFile. Returns success, duplicate info, or error.
export async function handle( args: Record<string, unknown>, ctx: ToolContext ): Promise<ToolResponse> { const args_typed = args as { type?: MemoryType; category: MemoryCategory; memory: string; reason: string; scope?: MemoryScope; }; const { type = 'decision', category, memory, reason } = args_typed; const scope = normalizeMemoryScope(args_typed.scope); try { const crypto = await import('crypto'); const memoryPath = ctx.paths.memory; const hashContent = buildMemoryIdentityParts({ type, category, memory, reason, scope }); const hash = crypto.createHash('sha256').update(hashContent).digest('hex'); const id = hash.substring(0, 12); const newMemory: Memory = { id, type, category, memory, reason, date: new Date().toISOString(), ...(scope && { scope }) }; const result = await appendMemoryFile(memoryPath, newMemory); if (result.status === 'duplicate') { return { content: [ { type: 'text', text: JSON.stringify( { status: 'info', message: 'This memory was already recorded.', memory: result.memory }, null, 2 ) } ] }; } return { content: [ { type: 'text', text: JSON.stringify( { status: 'success', message: 'Memory recorded successfully.', memory: result.memory }, null, 2 ) } ] }; } catch (error) { return { content: [ { type: 'text', text: JSON.stringify( { status: 'error', message: 'Failed to record memory.', error: error instanceof Error ? error.message : String(error) }, null, 2 ) } ] }; } } - src/tools/remember.ts:10-67 (schema)Input schema and tool definition for 'remember'. Defines the tool name, description with usage instructions, and inputSchema specifying type (convention/decision/gotcha/failure), category (tooling/architecture/testing/dependencies/conventions), memory (what to remember), reason, and optional scope (global/file/symbol). Required fields: type, category, memory, reason.
export const definition: Tool = { name: 'remember', description: 'CALL IMMEDIATELY when user explicitly asks to remember/record something.\n\n' + 'USER TRIGGERS:\n' + '- "Remember this: [X]"\n' + '- "Record this: [Y]"\n' + '- "Save this for next time: [Z]"\n\n' + 'DO NOT call unless user explicitly requests it.\n\n' + 'HOW TO WRITE:\n' + '- ONE convention per memory (if user lists 5 things, call this 5 times)\n' + '- memory: 5-10 words (the specific rule)\n' + '- reason: 1 sentence (why it matters)\n' + '- Skip: one-time features, code examples, essays', inputSchema: { type: 'object', properties: { type: { type: 'string', enum: ['convention', 'decision', 'gotcha', 'failure'], description: 'Type of memory being recorded. Use "failure" for things that were tried and failed - ' + 'prevents repeating the same mistakes.' }, category: { type: 'string', description: 'Broader category for filtering', enum: ['tooling', 'architecture', 'testing', 'dependencies', 'conventions'] }, memory: { type: 'string', description: 'What to remember (concise)' }, reason: { type: 'string', description: 'Why this matters or what breaks otherwise' }, scope: { type: 'object', description: 'Optional scope for this memory. Use { kind: "file", file } or { kind: "symbol", file, symbol }.', properties: { kind: { type: 'string', enum: ['global', 'file', 'symbol'] }, file: { type: 'string' }, symbol: { type: 'string' } } } }, required: ['type', 'category', 'memory', 'reason'] } }; - src/tools/index.ts:13-13 (registration)Import of the 'remember' tool's definition and handler (as d9/h9) in the tools index.
import { definition as d9, handle as h9 } from './remember.js'; - src/tools/index.ts:55-57 (registration)Tool registration: 'remember' definition (d9) is included in the TOOLS array and passed through withProjectSelector to add project routing properties.
export const TOOLS: Tool[] = [d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11].map( withProjectSelector ); - src/tools/index.ts:81-82 (registration)Dispatch routing: case 'remember' maps to h9 (the handler from remember.ts) in the dispatchTool switch statement.
case 'remember': return h9(args, ctx); - src/memory/store.ts:112-122 (helper)appendMemoryFile helper: reads existing memories from file, checks for duplicates by ID, appends the new memory if not duplicate, and writes back to disk.
export async function appendMemoryFile( memoryPath: string, memory: Memory ): Promise<{ status: 'added' | 'duplicate'; memory: Memory }> { const existing = await readMemoriesFile(memoryPath); const found = existing.find((m) => m.id === memory.id); if (found) return { status: 'duplicate', memory: found }; existing.push(memory); await writeMemoriesFile(memoryPath, existing); return { status: 'added', memory }; } - src/memory/store.ts:226-240 (helper)buildMemoryIdentityParts helper: constructs a deterministic string from memory fields (type, category, memory, reason, scope) used as input for SHA-256 hashing to generate a content-based ID.
export function buildMemoryIdentityParts(memory: { type: MemoryType; category: MemoryCategory; memory: string; reason: string; scope?: MemoryScope; }): string { const scopePart = !memory.scope || memory.scope.kind === 'global' ? 'global' : memory.scope.kind === 'file' ? `file:${normalizePathLike(memory.scope.file)}` : `symbol:${normalizePathLike(memory.scope.file)}:${memory.scope.symbol}`; return `${memory.type}:${memory.category}:${memory.memory}:${memory.reason}:${scopePart}`; } - src/memory/store.ts:31-54 (helper)normalizeMemoryScope helper: validates and normalizes the optional scope argument (global, file, or symbol) for the remember tool.
export function normalizeMemoryScope(raw: unknown): MemoryScope | undefined { if (!isRecord(raw)) return undefined; const kind = raw.kind; if (kind === 'global') { return { kind }; } if (kind === 'file' && typeof raw.file === 'string' && raw.file.trim()) { return { kind, file: normalizePathLike(raw.file.trim()) }; } if ( kind === 'symbol' && typeof raw.file === 'string' && raw.file.trim() && typeof raw.symbol === 'string' && raw.symbol.trim() ) { return { kind, file: normalizePathLike(raw.file.trim()), symbol: raw.symbol.trim() }; } return undefined; } - src/types/index.ts:563-568 (schema)MemoryCategory type definition: union of 'tooling', 'architecture', 'testing', 'dependencies', 'conventions'.
export type MemoryCategory = | 'tooling' // Build tools, package managers, linting, IDE config | 'architecture' // Layers, folder structure, module boundaries | 'testing' // Test frameworks, mocking strategies, coverage | 'dependencies' // Library choices, wrappers, versioning, package management | 'conventions'; // Naming, style, organization not captured in patterns - src/types/index.ts:573-577 (schema)MemoryType type definition: union of 'convention', 'decision', 'gotcha', 'failure'.
export type MemoryType = | 'convention' // Style, naming, component preferences | 'decision' // Architecture/tooling choices with rationale | 'gotcha' // Things that break and why | 'failure'; // Tried X, failed because Y — prevents repeating mistakes - src/types/index.ts:583-600 (schema)Memory interface: defines the shape of a stored memory record (id, type, category, memory, reason, date, source, scope).
export interface Memory { /** Content-based hash ID (first 12 chars of SHA-256) */ id: string; /** Type of knowledge: convention, decision, or gotcha */ type: MemoryType; /** Category for organization and filtering */ category: MemoryCategory; /** Brief description of what to remember */ memory: string; /** Why this decision was made - the rationale/context */ reason: string; /** ISO 8601 date when decision was recorded */ date: string; /** Source of the memory: 'user' (default) or 'git' (auto-extracted from commits) */ source?: 'user' | 'git'; /** Optional scope for file-specific or symbol-specific guidance */ scope?: MemoryScope; } - src/types/index.ts:602-605 (schema)MemoryScope type definition: union of {kind:'global'}, {kind:'file', file}, {kind:'symbol', file, symbol}.
export type MemoryScope = | { kind: 'global' } | { kind: 'file'; file: string } | { kind: 'symbol'; file: string; symbol: string };