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.

webhook

Event-Driven

Subscribe to memory lifecycle events and augment them with editor context.

build

TypeScript SDK

Full type safety with the @rekall/editor-hooks package.

publish

Publishable

Register your plugin with Rekall to share with other users.

Editor Hooks Framework

Install the editor hooks package to get started:

Terminal
npm install @rekall/editor-hooks

The package provides a RekallPlugin class that you use to define your integration:

plugin.ts
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 hooks
plugin.on('memoryCreate', (memory, context) => {
// Your custom logic here
});
// Initialize the plugin
plugin.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).

1
Event triggered

A Rekall operation begins (create, search, update, delete, etc.)

2
Pre-hooks execute

Registered hooks run in order, each receiving the (possibly modified) data from the previous hook.

3
Operation executes

The Rekall operation runs with the (potentially modified) data.

4
Post-hooks execute

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 metadata
memory.metadata.sourceFile = context.editor.activeFile;
memory.metadata.cursorPosition = context.editor.cursorPosition;
// Show a notification in the editor
context.editor.notify(`Memory stored: ${memory.content.slice(0, 50)}...`);
return memory; // Return modified memory
});

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 project
if (!query.context) {
query.context = context.project.name;
}
// Boost results related to the current file
query.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 context
hookCtx.editor.statusBar.set(
'rekall-context',
`Memory: ${newContext.name}`
);
// Pre-fetch recent memories for the new context
hookCtx.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 trail
console.log(`Memory ${memory.id} updated:`, changes);
// Refresh any displayed search results
context.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 memory
context.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 entities
const 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-plugin
npm init -y
npm install @rekall/editor-hooks typescript
npx tsc --init

2. Define the plugin manifest

package.json (relevant fields)
{
"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 mode
npx rekall-plugin dev
# Run the test suite
npx 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:

Terminal
# Authenticate with the Rekall registry
npx rekall-plugin login
# Validate your plugin
npx rekall-plugin validate
# Publish to the registry
npx 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:

src/index.ts - Complete example
1import { RekallPlugin, type Memory, type HookContext } from '@rekall/editor-hooks';
2
3const 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});
9
10// Enrich memories with editor metadata
11plugin.on('memoryCreate', (memory: Memory, ctx: HookContext) => {
12 const editor = ctx.editor;
13
14 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 };
21
22 ctx.editor.notify(`Rekall: Memory stored`, 'info');
23 return memory;
24});
25
26// Scope searches to current workspace
27plugin.on('search', (query, ctx: HookContext) => {
28 if (!query.context) {
29 query.context = ctx.editor.workspaceName;
30 }
31 return query;
32});
33
34// Update status bar on context change
35plugin.on('contextSwitch', (newCtx, _oldCtx, hookCtx: HookContext) => {
36 hookCtx.editor.statusBar.set(
37 'rekall',
38 `$(brain) ${newCtx.name}`,
39 'Rekall memory context'
40 );
41});
42
43// Track entity extraction for code navigation
44plugin.on('entityExtract', (entities, memory, ctx: HookContext) => {
45 const codeSymbols = ctx.editor.getDocumentSymbols();
46
47 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 }
60
61 return entities;
62});
63
64// Initialize
65plugin.init({
66 apiKey: process.env.REKALL_API_KEY!,
67 context: 'auto', // Auto-detect from workspace
68});
69
70export default plugin;

Next Steps

Rekall
rekall