A mod for Letta Code to support semantic search of an agent's context repository.
0

Configure Feed

Select the types of activity you want to include in your feed.

JavaScript 100.0%
3 1 0

Clone this repository

https://git.vm.fail/cameron.stream/memfs-search https://git.vm.fail/did:plc:ll5woixehdm2aq4tqr7pkgcr
ssh://git@knot1.tangled.sh:2222/cameron.stream/memfs-search ssh://git@knot1.tangled.sh:2222/did:plc:ll5woixehdm2aq4tqr7pkgcr

For self-hosted knots, clone URLs may differ based on your setup.


README.md

Letta Code MemFS Search Mod#

A portable Letta Code mod that exposes an agent-callable memfs_search tool for searching the current agent's MemFS memory files.

MemFS is the local filesystem projection of an agent's memory. This mod lets an agent search that projection from inside Letta Code without shelling out manually. It is useful when the agent needs to answer questions like "what do you know about X?", find existing memory before writing new memory, or locate relevant reference files.

Features#

  • Agent-callable memfs_search tool.
  • Built-in keyword search over local markdown memory files.
  • Optional QMD-backed semantic and hybrid search.
  • QMD compatibility safeguards for QMD 2.5.x:
    • semantic/hybrid use structured queries
    • --no-rerank avoids surprise query-expansion/reranker model downloads
    • files-only output auto-detects --files vs --format files
  • Memory directory auto-detection from MEMORY_DIR or common local Letta paths.
  • Status action for diagnostics.
  • No API keys required.

Repository layout#

mods/memfs-search.mjs  # Letta Code mod
README.md              # This guide
LICENSE                # MIT

Install#

Copy the mod into the Letta Code mods directory:

mkdir -p ~/.letta/mods
cp mods/memfs-search.mjs ~/.letta/mods/memfs-search.mjs

Then reload Letta Code mods:

/reload

If a mod breaks startup or command handling, start Letta Code with mods disabled:

LETTA_DISABLE_MODS=1 letta
# or
letta --no-mods

Then remove or edit the mod file under ~/.letta/mods/.

Quick start#

After install and /reload, ask the agent to search memory, or call the tool directly:

{
  "query": "commit footer preferences",
  "mode": "keyword",
  "limit": 5
}

Check diagnostics:

{ "action": "status" }

Example status output:

MEMORY_DIR: /Users/me/.letta/lc-local-backend/memfs/agent-.../memory
exists: yes
qmd: available
markdown_files: 70

Tool reference#

Tool name:

memfs_search

Parameters:

Parameter Type Default Description
action search or status search Use status to inspect memory path and QMD availability.
query string required for search Search query.
mode keyword, semantic, hybrid keyword Search backend/method.
limit number 8 Maximum results. Clamped to 50.
files_only boolean false Return only matching file paths.
full boolean false Return larger/full file content snippets.

Search modes#

keyword#

Built in. No dependencies. Searches local markdown files directly.

Use this first when you have exact terms, names, paths, project names, phrases, or other literal clues.

{
  "query": "The Coil preferences",
  "mode": "keyword",
  "limit": 5
}

The keyword scorer prefers:

  • exact phrase matches
  • files matching more distinct query terms
  • path matches
  • core system/ memory when scores are close
  • shorter focused files when scores are close

semantic#

Requires QMD setup and embeddings. Uses vector search through a structured QMD query:

qmd query "vec: <query>" -c memory -n <limit> --no-rerank

Use this when you only have a vague concept and keyword search misses.

{
  "query": "how does Cameron like agents to handle long context summaries",
  "mode": "semantic",
  "limit": 5
}

hybrid#

Requires QMD setup and embeddings. Uses both lexical and vector search through a structured QMD query:

qmd query $'lex: <query>\nvec: <query>' -c memory -n <limit> --no-rerank

Use this when you want better recall than keyword but still want lexical matches included.

{
  "query": "agent profile image",
  "mode": "hybrid",
  "limit": 10
}

Why structured QMD queries?#

QMD 2.5.x can run query expansion or reranking for some commands, including plain qmd query and in practice qmd vsearch on some setups. That can trigger a large model download, for example a ~1.28 GB query-expansion GGUF file, which is not appropriate inside a short mod tool call.

This mod avoids that by using structured QMD query documents and --no-rerank:

# semantic
qmd query "vec: <query>" --no-rerank

# hybrid
qmd query $'lex: <query>\nvec: <query>' --no-rerank

That keeps searches on already-installed BM25/vector indexes and avoids surprise expansion/reranker downloads.

QMD setup#

Keyword mode works without QMD. Semantic and hybrid modes need QMD installed and indexed.

Install QMD:

npm install -g @tobilu/qmd
# or
bun install -g @tobilu/qmd

QMD currently needs a working Node runtime. On macOS, if SQLite native modules fail, install/rebuild under the Node version that runs QMD.

Create a memory collection:

qmd collection add "$MEMORY_DIR" --name memory --mask "**/*.md"

Add helpful context annotations:

qmd context add qmd://memory "Agent memory blocks — system prompt files and reference materials"
qmd context add qmd://memory/system "In-context memory blocks rendered in the system prompt every turn"
qmd context add qmd://memory/reference "Reference materials loaded on-demand via tools"

Generate embeddings:

qmd embed

First embed may download local embedding models. This is normal QMD setup work and should be done outside a mod tool call.

Verify:

qmd status
qmd search "test query" -c memory
qmd query "vec: test query" -c memory --no-rerank

Memory path detection#

The mod resolves the memory directory in this order:

  1. MEMORY_DIR, if set and existing.
  2. ~/.letta/lc-local-backend/memfs/<agent-id>/memory
  3. ~/.letta/agents/<agent-id>/memory

The agent id comes from the mod context (ctx.agent.id) or AGENT_ID.

If no memory path exists, action=status will show the attempted path and exists: no.

Examples#

Status#

{ "action": "status" }
{
  "query": "supervillain project architecture",
  "mode": "keyword",
  "limit": 5
}

Files only#

{
  "query": "profile image",
  "mode": "keyword",
  "files_only": true
}

Fuller snippets#

{
  "query": "Cameron preferences",
  "mode": "keyword",
  "full": true,
  "limit": 3
}
{
  "query": "what does the user care about in coding agents",
  "mode": "semantic",
  "limit": 5
}
{
  "query": "Signal channel debugging",
  "mode": "hybrid",
  "limit": 8
}

Privacy and safety#

This is trusted local code. It reads markdown files from the current agent's local memory filesystem projection and returns matching snippets/paths to the model.

It does not:

  • send memory to a cloud API
  • require API keys
  • mutate memory files
  • commit or push memory changes

Semantic and hybrid modes call local QMD. QMD embedding/search is local, but QMD may download local model files during setup. This mod is designed to avoid triggering large query-expansion/reranker downloads during search.

Troubleshooting#

MEMORY_DIR is not set or does not exist#

Run:

{ "action": "status" }

Check whether the inferred path exists. If not, set MEMORY_DIR before launching Letta Code or update the mod's fallback paths for your backend layout.

qmd: not found#

Keyword mode still works. For semantic/hybrid:

npm install -g @tobilu/qmd
qmd collection add "$MEMORY_DIR" --name memory --mask "**/*.md"
qmd embed

Then reload Letta Code:

/reload

QMD tries to download a huge query-expansion or reranker model#

Update this mod. Versions after commit 98e7ff2 use structured queries plus --no-rerank to avoid that behavior.

If you already have a partial .ipull download from a failed run, it is safe to remove the partial file from QMD's model/cache directory if QMD is not currently running.

QMD native SQLite / Node mismatch#

Symptoms may include errors like:

better_sqlite3.node was compiled against a different Node.js version

The mod tries to prefer the Node binary next to the qmd executable by prepending QMD's bin directory to PATH and unsetting BUN_INSTALL. If errors persist, reinstall or rebuild QMD under the Node version used to run QMD:

npm rebuild -g @tobilu/qmd
# or reinstall
npm install -g @tobilu/qmd

Semantic/hybrid returns no results but keyword works#

Check QMD index health:

qmd status
qmd collection list
qmd embed

Try a direct structured query:

qmd query "vec: commit footer preferences" -c memory --no-rerank

If this returns no results, QMD embeddings/indexing need attention, not the mod.

files_only returns weird output#

QMD versions differ. Some support --files; some document --format files. The mod auto-detects by inspecting qmd query --help. If output is still odd, run:

qmd query --help

and check the available output flags.

Development#

Validate syntax:

node --check mods/memfs-search.mjs

Install locally from this repo:

cp mods/memfs-search.mjs ~/.letta/mods/memfs-search.mjs

Reload Letta Code:

/reload

Manual smoke test from a Node script:

node - <<'NODE'
import mod from './mods/memfs-search.mjs';
let tool;
mod({ capabilities: { tools: true }, tools: { register(def) { tool = def; return () => {}; } } });
console.log(await tool.run({
  cwd: process.cwd(),
  agent: { id: process.env.AGENT_ID },
  args: { action: 'status' },
  signal: AbortSignal.timeout(10000),
}));
NODE

Limitations#

  • Only markdown files (*.md) are searched.
  • Keyword mode is simple local scoring, not a full search engine.
  • Semantic/hybrid modes depend on QMD being installed, configured, and embedded.
  • The mod is read-only; it does not write or reorganize memory.
  • It searches memory files, not conversation history/recall memory.

Roadmap#

Possible future improvements:

  • Slash command wrapper for manual /memfs-search usage.
  • Optional commands for QMD setup/status/reindex.
  • Better structured JSON output mode.
  • More robust QMD version detection.
  • Searching non-markdown memory assets where appropriate.