Extend the-brain with custom scripts loaded at runtime
the-brain supports zero-config extensions β drop a .ts file into ~/.the-brain/extensions/, add its name to config.json's extensions array, and it loads on the next daemon restart.
β οΈ Extensions are disabled by default for security. You must explicitly enable each extension in config.json: "extensions": ["my-extension"].
π‘ Extensions are the product. the-brain's core is a thin event bus + plugin manager. Everything that makes it useful β harvesters, memory strategies, trainers, notifications β is an extension. The built-in starter pack are extensions too. Build yours and grow the ecosystem.
# Run a registered extension command (daemon process only)the-brain ext <command> [args...]# List all available extension commandsthe-brain ext
Note:the-brain ext runs in the CLI process, but extensions live in the daemon process. For extensions that need CLI access, use a standalone runner (see Hermes extension example below).
The @the-brain-dev/plugin-harvester-hermes plugin harvests conversations from Hermes Agent. The example below shows key extension-programming techniques used to build this harvester.
What it did:
Auto-harvester β hooked harvester:poll (every 30s), read ~/.hermes/state.db, paired userβassistant messages, emitted harvester:newData into the pipeline
On-interaction tagging β tagged memories with hermesChannel, hermesModel, and token counts
CLI commands β accessible via a standalone runner
CLI usage (standalone):
# Stats β shows harvested count, session breakdown, live DB stats~/.the-brain/bin/hermes-ext stats# Manual harvest β forces a scan of Hermes state.db~/.the-brain/bin/hermes-ext harvest# Context export β enhanced brain context for Hermes memory injection~/.the-brain/bin/hermes-ext context~/.the-brain/bin/hermes-ext context --json
Key techniques demonstrated:
export default function (brain) { // 1. Persist state between daemon restarts async function loadState() { try { return JSON.parse(await Bun.file(STATE_FILE).text()); } catch (_) { return defaults; } } // 2. Open external SQLite databases (brain.openDatabase) function openDb() { return brain.openDatabase("/path/to/external.db", true); // read-only } // 3. Auto-harvest on every daemon poll cycle brain.hook("harvester:poll", async function () { var interactions = doHarvest(state); if (!interactions.length) return; var emitted = await emitAll(interactions); // -> harvester:newData saveState(updatedState); }); // 4. Register CLI commands (via standalone runner) brain.registerCommand("hermes", async function (args) { ... });}
Why a standalone runner?brain.registerCommand() registers commands in the daemon process's memory. The CLI process (the-brain ext) has its own copy. The standalone runner (~/.the-brain/bin/hermes-ext) loads the extension with a minimal BrainAPI and dispatches directly β no daemon dependency for quick queries.