hermes - 💡(How to fix) Fix Feature: Project-scoped memory — filter built-in MEMORY.md by cwd/project context

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Fix Action

Fix / Workaround

mem_block = self._memory_store.format_for_system_prompt("memory", project_scope)

The `_find_project_root()` helper walks up from cwd looking for known markers, returning the first match. This is consistent with how `build_context_files_prompt()` already discovers AGENTS.md.
3. New config option
```yaml
memory:
  memory_enabled: true
  user_profile_enabled: true
  project_scoping: true     # new: default false for backward compatibility

When project_scoping: false → current behavior (all entries, no filter). When true → enable cwd-based filtering as described. 4. User-facing convention Users tag memory entries with prefixes:

[global] SSH alias aliyun-hermes
[global] Custom provider must use chat_completions
[project:cert-manager] Local port 8650, cloud 8660
[project:invoice] Script at ~/.hermes/scripts/process_invoices.py

These tags are just text in the entry content — no schema migration needed. The memory() tool already supports arbitrary text. Users can add tags manually or via a CLI helper. Files Changed File Change tools/memory_tool.py format_for_system_prompt() gets project_scope param + filter logic tools/memory_tool.py _render_block() optionally filters by scope run_agent.py Detect project scope from cwd, pass to format_for_system_prompt() hermes_cli/config.py Add memory.project_scoping config key agent/prompt_builder.py (optional) Add cwd-based scope detection helper Total: ~100 lines of changes, zero new dependencies. Alternatives Considered External memory provider (Honcho/Mem0) — overkill for simple text-based filtering. The built-in MEMORY.md is simpler, more portable, and works without any backend. Per-project MEMORY.md files — could load ~/.hermes/memories/cert-manager/MEMORY.md, but requires a new file-per-project convention and breaks the single-file mental model. Tag-based filtering preserves the existing flat file. Metadata field on entries — proper structured metadata ({"tags": ["project:xxx"]}) is more elegant but requires schema migration, a new tool API, and backward compat. Text-prefix tagging can be added today without any tool changes. Compatibility Entries without [global] or [project:xxx] prefix → always included (backward compatible) project_scoping: false (default) → no behavior change Gateway sessions without TERMINAL_CWD → no scope detected, all entries included Related Current workaround documented in project-memory-loader skill: ~/.hermes/scripts/load_project_context.py + per-project .hermes-memory.md files build_context_files_prompt() in run_agent.py already does cwd-based AGENTS.md discovery — this proposal follows the same pattern

Code Example

def format_for_system_prompt(self, target: str, project_scope: str = "") -> Optional[str]:
    if not project_scope:
        return self._system_prompt_snapshot.get(target, None)
    
    entries = self._entries_for(target)
    # If project_scope is set, only include [global] + matching [project:<scope>] entries
    filtered = [
        e for e in entries
        if e.startswith("[global]") or e.startswith(f"[project:{project_scope}]")
    ]
    content = ENTRY_DELIMITER.join(filtered) if filtered else ""
    return f"...{header}...{content}" if content else None

---

# Detect project scope from TERMINAL_CWD
project_scope = ""
_terminal_cwd = os.getenv("TERMINAL_CWD") or os.getcwd()
_project_root = _find_project_root(_terminal_cwd)  # checks for .hermes-memory.md, AGENTS.md, .git
if _project_root:
    project_scope = os.path.basename(_project_root)

mem_block = self._memory_store.format_for_system_prompt("memory", project_scope)

---

memory:
  memory_enabled: true
  user_profile_enabled: true
  project_scoping: true     # new: default false for backward compatibility

---

[global] SSH alias aliyun-hermes
[global] Custom provider must use chat_completions
[project:cert-manager] Local port 8650, cloud 8660
[project:invoice] Script at ~/.hermes/scripts/process_invoices.py
RAW_BUFFERClick to expand / collapse

title: "Feature: Project-scoped memory — filter built-in MEMORY.md by cwd/project context" labels: ["enhancement", "memory"] Summary Currently, the built-in MemoryStore (MEMORY.md / USER.md) injects all entries into the system prompt every session, regardless of what project the agent is working on. This wastes tokens and pollutes context: when working on project A, the agent still carries project B's paths, ports, and conventions. This proposal adds lightweight project scoping to the built-in memory, inspired by Codex's per-project isolation model. Motivation A real example from daily use: Metric Before After (with this proposal) Memory entries injected 11 entries, mixed cert-manager / invoice / Codex / global 7 entries, only [global] tagged Token waste per session ~2,144 chars of mixed content ~705 chars of pure-global content Context pollution cert-manager paths appear while debugging invoices Project-specific context loaded on demand Without scoping, every session pays the full cross-project memory tax. With it, token waste drops by ~65% for any single-project session. Proposed Solution

  1. Add a project_scope parameter to MemoryStore In tools/memory_tool.py, the format_for_system_prompt() method currently takes only target ("memory" or "user"). Add an optional project_scope: str = "" parameter: When project_scope is empty → current behavior (inject all entries) When project_scope is set → filter entries by tag prefix, e.g. [global] or [project:cert-manager] The filtering is simple text-prefix matching — no schema change needed:
def format_for_system_prompt(self, target: str, project_scope: str = "") -> Optional[str]:
    if not project_scope:
        return self._system_prompt_snapshot.get(target, None)
    
    entries = self._entries_for(target)
    # If project_scope is set, only include [global] + matching [project:<scope>] entries
    filtered = [
        e for e in entries
        if e.startswith("[global]") or e.startswith(f"[project:{project_scope}]")
    ]
    content = ENTRY_DELIMITER.join(filtered) if filtered else ""
    return f"...{header}...{content}" if content else None
  1. Detect project scope at session start In run_agent.py, where format_for_system_prompt() is called (~line 5377):
# Detect project scope from TERMINAL_CWD
project_scope = ""
_terminal_cwd = os.getenv("TERMINAL_CWD") or os.getcwd()
_project_root = _find_project_root(_terminal_cwd)  # checks for .hermes-memory.md, AGENTS.md, .git
if _project_root:
    project_scope = os.path.basename(_project_root)

mem_block = self._memory_store.format_for_system_prompt("memory", project_scope)

The _find_project_root() helper walks up from cwd looking for known markers, returning the first match. This is consistent with how build_context_files_prompt() already discovers AGENTS.md. 3. New config option

memory:
  memory_enabled: true
  user_profile_enabled: true
  project_scoping: true     # new: default false for backward compatibility

When project_scoping: false → current behavior (all entries, no filter). When true → enable cwd-based filtering as described. 4. User-facing convention Users tag memory entries with prefixes:

[global] SSH alias aliyun-hermes
[global] Custom provider must use chat_completions
[project:cert-manager] Local port 8650, cloud 8660
[project:invoice] Script at ~/.hermes/scripts/process_invoices.py

These tags are just text in the entry content — no schema migration needed. The memory() tool already supports arbitrary text. Users can add tags manually or via a CLI helper. Files Changed File Change tools/memory_tool.py format_for_system_prompt() gets project_scope param + filter logic tools/memory_tool.py _render_block() optionally filters by scope run_agent.py Detect project scope from cwd, pass to format_for_system_prompt() hermes_cli/config.py Add memory.project_scoping config key agent/prompt_builder.py (optional) Add cwd-based scope detection helper Total: ~100 lines of changes, zero new dependencies. Alternatives Considered External memory provider (Honcho/Mem0) — overkill for simple text-based filtering. The built-in MEMORY.md is simpler, more portable, and works without any backend. Per-project MEMORY.md files — could load ~/.hermes/memories/cert-manager/MEMORY.md, but requires a new file-per-project convention and breaks the single-file mental model. Tag-based filtering preserves the existing flat file. Metadata field on entries — proper structured metadata ({"tags": ["project:xxx"]}) is more elegant but requires schema migration, a new tool API, and backward compat. Text-prefix tagging can be added today without any tool changes. Compatibility Entries without [global] or [project:xxx] prefix → always included (backward compatible) project_scoping: false (default) → no behavior change Gateway sessions without TERMINAL_CWD → no scope detected, all entries included Related Current workaround documented in project-memory-loader skill: ~/.hermes/scripts/load_project_context.py + per-project .hermes-memory.md files build_context_files_prompt() in run_agent.py already does cwd-based AGENTS.md discovery — this proposal follows the same pattern

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix Feature: Project-scoped memory — filter built-in MEMORY.md by cwd/project context