Custom Editors
Build your own Rekall integration using the editor hooks framework. Hook into memory creation, search, context switches, and more with a simple lifecycle API.
Overview
The Rekall editor hooks framework provides a standardized way to build custom editor integrations. If your editor or tool is not yet supported by a first-party plugin, you can use this framework to create one. The hooks API follows an event-driven pattern, letting you intercept and augment Rekall operations with editor-specific context.
Event-Driven
Subscribe to memory lifecycle events and augment them with editor context.
TypeScript SDK
Full type safety with the @rekall/editor-hooks package.
Publishable
Register your plugin with Rekall to share with other users.
Editor Hooks Framework
Install the editor hooks package to get started:
npm install @rekall/editor-hooks
The package provides a RekallPlugin class that you use to define your integration:
import { RekallPlugin } from '@rekall/editor-hooks';const plugin = new RekallPlugin({name: 'my-editor-plugin',version: '1.0.0',description: 'Rekall integration for My Editor',author: 'Your Name',});// Register hooksplugin.on('memoryCreate', (memory, context) => {// Your custom logic here});// Initialize the pluginplugin.init({apiKey: process.env.REKALL_API_KEY,context: 'my-project',});export default plugin;
Hook Lifecycle
Hooks are executed in the order they are registered. Each hook receives the relevant data and a context object containing editor state and Rekall client methods. Hooks can modify the data (by returning a new value) or perform side effects (returning void).
A Rekall operation begins (create, search, update, delete, etc.)
Registered hooks run in order, each receiving the (possibly modified) data from the previous hook.
The Rekall operation runs with the (potentially modified) data.
Post-operation hooks run with the result, enabling UI updates and side effects.
Cancellation
Some hooks can cancel the operation by returning false. For example, returning false from onMemoryDelete will prevent the deletion.
Available Hooks
onMemoryCreate
Triggered when a new memory is created. Use this to enrich memories with editor-specific metadata, update UI indicators, or synchronize with editor state.
Signature: (memory: Memory, context: HookContext) => Memory | void
plugin.on('memoryCreate', (memory, context) => {// Add the current file path as metadatamemory.metadata.sourceFile = context.editor.activeFile;memory.metadata.cursorPosition = context.editor.cursorPosition;// Show a notification in the editorcontext.editor.notify(`Memory stored: ${memory.content.slice(0, 50)}...`);return memory; // Return modified memory});
onSearch
Triggered when a memory search is performed. Use this to add editor context to search queries, filter results based on the current project, or display results in the editor.
Signature: (query: SearchQuery, context: HookContext) => SearchQuery | void
plugin.on('search', (query, context) => {// Automatically scope search to current projectif (!query.context) {query.context = context.project.name;}// Boost results related to the current filequery.boost = {field: 'metadata.sourceFile',value: context.editor.activeFile,weight: 1.5,};return query;});
onContextSwitch
Triggered when the memory context changes (e.g., switching between projects or hives). Use this to update the editor UI, load project-specific settings, or pre-fetch relevant memories.
Signature: (newContext: MemoryContext, oldContext: MemoryContext, hookCtx: HookContext) => void
plugin.on('contextSwitch', (newContext, oldContext, hookCtx) => {// Update the status bar with current contexthookCtx.editor.statusBar.set('rekall-context',`Memory: ${newContext.name}`);// Pre-fetch recent memories for the new contexthookCtx.rekall.search({context: newContext.name,limit: 10,sort: 'recent',});});
onMemoryUpdate
Triggered when an existing memory is updated. Use this to track changes, update cached results, or notify the user of memory modifications.
Signature: (memory: Memory, changes: Partial<Memory>, context: HookContext) => void
plugin.on('memoryUpdate', (memory, changes, context) => {// Log the update for audit trailconsole.log(`Memory ${memory.id} updated:`, changes);// Refresh any displayed search resultscontext.editor.refreshPanel('rekall-search');});
onMemoryDelete
Triggered when a memory is deleted. Use this to clean up any editor-side references, remove inline annotations, or confirm deletion with the user.
Signature: (memoryId: string, context: HookContext) => boolean | void
plugin.on('memoryDelete', (memoryId, context) => {// Remove any inline annotations for this memorycontext.editor.removeAnnotations({ memoryId });// Return false to cancel the deletion// return false;});
onEntityExtract
Triggered when entities are extracted from memory content. Use this to add editor-aware entity detection, link entities to code symbols, or build project-specific knowledge graphs.
Signature: (entities: Entity[], memory: Memory, context: HookContext) => Entity[] | void
plugin.on('entityExtract', (entities, memory, context) => {// Add code symbols as entitiesconst symbols = context.editor.getSymbols(memory.metadata.sourceFile);for (const symbol of symbols) {if (memory.content.includes(symbol.name)) {entities.push({name: symbol.name,type: 'code_symbol',properties: {kind: symbol.kind,file: symbol.file,line: symbol.line,},});}}return entities;});
Building a Custom Plugin
Follow these steps to build a complete editor plugin:
1. Scaffold the project
mkdir rekall-myeditor-plugin && cd rekall-myeditor-pluginnpm init -ynpm install @rekall/editor-hooks typescriptnpx tsc --init
2. Define the plugin manifest
{"name": "rekall-myeditor-plugin","version": "1.0.0","rekall": {"type": "editor-plugin","editor": "myeditor","hooks": ["memoryCreate","search","contextSwitch"],"permissions": ["memory:read","memory:write","search:read"]}}
3. Implement your hooks
Create a src/index.ts file and register your hooks using the RekallPlugin class as shown above.
4. Test locally
# Run the plugin in development modenpx rekall-plugin dev# Run the test suitenpx rekall-plugin test
Registering with Rekall
Once your plugin is ready, you can publish it to the Rekall plugin registry so other users can install it:
# Authenticate with the Rekall registrynpx rekall-plugin login# Validate your pluginnpx rekall-plugin validate# Publish to the registrynpx rekall-plugin publish
Private plugins
You can also keep plugins private and install them directly via npm without publishing to the Rekall registry. This is useful for organization-specific integrations.
Example Implementation
Here is a complete example of a custom editor plugin that adds file-aware memory creation, scoped search, and status bar indicators:
1import { RekallPlugin, type Memory, type HookContext } from '@rekall/editor-hooks';23const plugin = new RekallPlugin({4 name: 'rekall-vscode-lite',5 version: '1.0.0',6 description: 'Lightweight Rekall plugin for VS Code',7 author: 'Your Team',8});910// Enrich memories with editor metadata11plugin.on('memoryCreate', (memory: Memory, ctx: HookContext) => {12 const editor = ctx.editor;1314 memory.metadata = {15 ...memory.metadata,16 sourceFile: editor.activeFile,17 workspace: editor.workspaceName,18 language: editor.activeLanguage,19 selection: editor.selection?.text?.slice(0, 200),20 };2122 ctx.editor.notify(`Rekall: Memory stored`, 'info');23 return memory;24});2526// Scope searches to current workspace27plugin.on('search', (query, ctx: HookContext) => {28 if (!query.context) {29 query.context = ctx.editor.workspaceName;30 }31 return query;32});3334// Update status bar on context change35plugin.on('contextSwitch', (newCtx, _oldCtx, hookCtx: HookContext) => {36 hookCtx.editor.statusBar.set(37 'rekall',38 `$(brain) ${newCtx.name}`,39 'Rekall memory context'40 );41});4243// Track entity extraction for code navigation44plugin.on('entityExtract', (entities, memory, ctx: HookContext) => {45 const codeSymbols = ctx.editor.getDocumentSymbols();4647 for (const symbol of codeSymbols) {48 if (memory.content.includes(symbol.name)) {49 entities.push({50 name: symbol.name,51 type: 'code_symbol',52 properties: {53 kind: symbol.kind,54 file: ctx.editor.activeFile,55 range: symbol.range,56 },57 });58 }59 }6061 return entities;62});6364// Initialize65plugin.init({66 apiKey: process.env.REKALL_API_KEY!,67 context: 'auto', // Auto-detect from workspace68});6970export default plugin;
