Search by

vielhuber / memhelper

Markdown-first memory layer for LLM agents — one call, transparent indexing and auto-extraction.

Maintainers

Package info

github.com/vielhuber/memhelper

pkg:composer/vielhuber/memhelper

Transparency log

Statistics

Installs: 122

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

1.3.4 2026-08-29 05:35 UTC

README

build status GitHub Tag Code Style License Last Commit PHP Version Support Packagist Downloads

🧠 memhelper 🧠

Markdown-first memory layer for LLM agents. Exposes a grab(query) MCP tool that returns the curated facts most relevant to a natural-language question. A separate supervisor worker handles all writes — refreshing the search index across every configured input source, distilling new sources via an LLM, and periodically compacting duplicates and obsolete entries. Memory entries are plain .md files with a tiny YAML frontmatter — readable, editable, git-versionable. Cross-references between entries are written as [[slug]] wiki-links and followed one hop at retrieval time, so a single query surfaces related neighbours automatically. Ranking fuses full-text relevance (bm25 over description, body and tags) with the entry's degree in the link graph and its recency, into a single positive score — higher is better. Entries carrying a valid_until date are hidden once it passes and dropped on the next worker tick.

installation

composer require vielhuber/memhelper
ai:
    provider: openai
    model: gpt-5
    api_key: sk-...
    cli_session_home: /persistent/harness/sessions/memory
    cli_auth_home: /persistent/harness/profiles/codex

output: /path/to/memory
max_source_bytes: 20000
existing_memory_limit: 200
injection_max_bytes: 12000

input_files:
    - /path/to/external/docs
    - /path/to/external/notes

input_dbs:
    - driver: sqlite
      path: /path/to/database.db
      include_tables: [chats_messages] # optional
      where: # optional
          chats_messages: role = 'user' AND status = 'completed'
      compaction_pause_when: SELECT COUNT(*) FROM jobs WHERE status = 'running' # optional

    - key: project-chats
      driver: sqlite
      path: /path/to/database.db
      query: |
          SELECT messages.id, projects.path AS scope, messages.role, messages.content
          FROM messages INNER JOIN projects ON projects.id = messages.project_id
      primary_key: id
      scope_column: scope
      content_columns: [role, content]

    - driver: mysql
      host: 127.0.0.1
      port: 3306
      user: root
      password:
      database: memhelper
      exclude_tables: [analytics_events] # optional

    - driver: postgres
      host: 127.0.0.1
      port: 5432
      user: root
      password:
      database: database

cli_session_home and cli_auth_home are only relevant when ai.provider is a CLI harness such as Codex, Claude Code or OpenCode. The session home isolates Memhelper's native CLI history and configuration. The auth home points at the persistent login profile that may be shared with other sessions. API providers ignore both options.

usage

library

use vielhuber\memhelper\memhelper;

$memory = new memhelper(
    configPath: '/path/to/config.yaml',
    logPath: '/var/log/memory.log'
);

$facts = $memory->grab(
    query: 'how is the user\'s dog named?',
    limit: 10
);
// → [['slug' => 'pet-roger', 'tags' => ['pet', 'dog'], 'description' => '...',
//     'body' => '...', 'sources' => ['dbrow:…'], 'score' => 1.0824, 'via' => null], ...]

$projectFacts = $memory->grab(
    scope: '/var/www/project',
    query: 'database migration',
    limit: 10,
);

$systemPrompt = $memory->inject(
    scope: '/var/www/project',
    context: 'Implement the next database migration.'
);

scope is an exact technical namespace, independent of semantic memory tags. Distillation, links, retrieval and compaction never cross scope boundaries. inject() performs no AI call; it reads the already curated FTS index, follows scoped links and returns a size-limited Markdown block. Without a scope, grab() continues to use only the global memory store.

memory entries

---
name: pet-roger
description: der Hund des Users
metadata:
    tags:
        - pet
        - dog
    sources:
        - dbrow:charly:chats_messages:42
    created: '2026-08-06T09:12:44+02:00'
    updated: '2026-08-06T09:12:44+02:00'
    valid_until: '2027-03-31'
---

Der Hund des Users heißt Roger. Roger gehört zu [[household-budget]].

created / updated are stamped by the worker; valid_until is optional and may be set by hand or by the LLM when a fact has a known end date. Everything else is free to edit — the worker picks changes up on the next tick.

worker

[program:memhelper-worker]
command=php /app/vendor/vielhuber/memhelper/bin/memhelper-worker /path/to/memory.yaml /var/log/memory.log
autostart=true
autorestart=true

tests

./vendor/bin/phpunit

mcp

{
    "mcpServers": {
        "memory": {
            "command": "php",
            "args": ["./vendor/bin/mcp-server.php"],
            "env": {
                "MEMHELPER_CONFIG": "/path/to/memory.yaml",
                "MEMHELPER_LOG": "/var/log/memory.log"
            }
        }
    }
}