Extend the-brain with custom scripts loaded at runtime
The-brain supported zero-config extensions — a .ts file could be placed in ~/.the-brain/extensions/, added to config.json's extensions array, and loaded on the next daemon restart.
⚠️ Extensions are disabled by default for security. You must explicitly enable each extension in config.json: "extensions": ["my-extension"].
Historical extension model. the-brain's core was a thin event bus + plugin manager. Harvesters, memory strategies, trainers, and notifications were extensions. The built-in starter pack used the same model.
# 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.