From cf6db6ef887937acdc2b51841c52f4c5eac12584 Mon Sep 17 00:00:00 2001 From: kartik-mem0 Date: Thu, 21 May 2026 16:38:48 +0530 Subject: [PATCH] fix(mem0-plugin): use app_id entity scoping, fix tour, add branch tagging Tour was filtering on metadata.type which missed platform-categorized memories. Rewritten to use get_memories (list all) + group by platform categories field. All write hooks now use app_id as top-level entity param instead of metadata.project_id. Branch stamped on every write. --- mem0-plugin/scripts/auto_import.py | 24 +++-- .../scripts/capture_compact_summary.py | 16 ++-- mem0-plugin/scripts/on_pre_compact.py | 16 ++-- mem0-plugin/scripts/on_pre_compact.sh | 3 +- mem0-plugin/scripts/on_session_start.sh | 29 ++++-- mem0-plugin/scripts/on_stop.sh | 2 +- mem0-plugin/scripts/on_stop_codex.sh | 2 +- mem0-plugin/scripts/on_stop_cursor.sh | 2 +- mem0-plugin/scripts/on_task_completed.sh | 2 +- mem0-plugin/scripts/on_user_prompt.sh | 12 +-- mem0-plugin/skills/mem0-mcp/SKILL.md | 30 +++--- mem0-plugin/skills/mem0-onboard/SKILL.md | 20 +++- .../skills/mem0-switch-project/SKILL.md | 2 +- mem0-plugin/skills/mem0-tour/SKILL.md | 95 ++++++++++++++----- 14 files changed, 169 insertions(+), 86 deletions(-) diff --git a/mem0-plugin/scripts/auto_import.py b/mem0-plugin/scripts/auto_import.py index 4be62bd40..3c3cecb6f 100644 --- a/mem0-plugin/scripts/auto_import.py +++ b/mem0-plugin/scripts/auto_import.py @@ -22,7 +22,7 @@ import urllib.request sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from _identity import resolve_user_id -from _project import resolve_project_id +from _project import resolve_branch, resolve_project_id log = logging.getLogger("mem0-auto-import") log.setLevel(logging.DEBUG) @@ -77,8 +77,15 @@ def save_hashes(hashes: dict[str, str]) -> None: log.warning("Could not save hash store: %s", e) -def post_memory(api_key: str, content: str, user_id: str, filename: str, project_id: str) -> bool: +def post_memory(api_key: str, content: str, user_id: str, filename: str, project_id: str, branch: str = "") -> bool: """POST a project profile memory to the Mem0 REST API.""" + metadata = { + "type": "project_profile", + "file": filename, + "source": "auto-import", + } + if branch: + metadata["branch"] = branch body = { "messages": [ { @@ -87,12 +94,8 @@ def post_memory(api_key: str, content: str, user_id: str, filename: str, project } ], "user_id": user_id, - "metadata": { - "type": "project_profile", - "file": filename, - "project_id": project_id, - "source": "auto-import", - }, + "app_id": project_id, + "metadata": metadata, "infer": False, } @@ -128,8 +131,9 @@ def main() -> None: cwd = os.environ.get("MEM0_CWD", "").strip() or os.getcwd() user_id = resolve_user_id() project_id = resolve_project_id(cwd) + branch = resolve_branch(cwd) - log.debug("Auto-import started: cwd=%s project=%s user=%s", cwd, project_id, user_id) + log.debug("Auto-import started: cwd=%s project=%s user=%s branch=%s", cwd, project_id, user_id, branch) hashes = load_hashes() updated = False @@ -169,7 +173,7 @@ def main() -> None: log.debug("Cannot read %s: %s", filename, e) continue - if post_memory(api_key, content, user_id, filename, project_id): + if post_memory(api_key, content, user_id, filename, project_id, branch): hashes[hash_key] = current_hash updated = True # on API failure we don't update the hash — retry next session diff --git a/mem0-plugin/scripts/capture_compact_summary.py b/mem0-plugin/scripts/capture_compact_summary.py index 1f3960a61..44112933e 100644 --- a/mem0-plugin/scripts/capture_compact_summary.py +++ b/mem0-plugin/scripts/capture_compact_summary.py @@ -97,16 +97,18 @@ def find_compact_summary(lines: list[str]) -> str: def store_summary(api_key: str, summary: str, user_id: str, session_id: str, project_id: str = "", branch: str = "") -> bool: expires = (date.today() + timedelta(days=COMPACT_SUMMARY_EXPIRY_DAYS)).isoformat() + metadata = { + "type": "compact_summary", + "source": "session-start-compact", + "session_id": session_id, + } + if branch: + metadata["branch"] = branch body = { "messages": [{"role": "user", "content": summary}], "user_id": user_id, - "metadata": { - "type": "compact_summary", - "source": "session-start-compact", - "session_id": session_id, - "project_id": project_id, - "branch": branch, - }, + "app_id": project_id, + "metadata": metadata, "infer": False, "expiration_date": expires, } diff --git a/mem0-plugin/scripts/on_pre_compact.py b/mem0-plugin/scripts/on_pre_compact.py index 737cbb928..5466eae61 100755 --- a/mem0-plugin/scripts/on_pre_compact.py +++ b/mem0-plugin/scripts/on_pre_compact.py @@ -171,18 +171,20 @@ def build_content(state: dict, source: str) -> str: def store_memory(api_key: str, content: str, user_id: str, source: str, session_id: str = "", project_id: str = "", branch: str = "") -> bool: """Store session state as a memory via the Mem0 REST API.""" expires = (date.today() + timedelta(days=SESSION_STATE_EXPIRY_DAYS)).isoformat() + metadata = { + "type": "session_state", + "source": source, + "session_id": session_id, + } + if branch: + metadata["branch"] = branch body = { "messages": [ {"role": "user", "content": content} ], "user_id": user_id, - "metadata": { - "type": "session_state", - "source": source, - "session_id": session_id, - "project_id": project_id, - "branch": branch, - }, + "app_id": project_id, + "metadata": metadata, "expiration_date": expires, } diff --git a/mem0-plugin/scripts/on_pre_compact.sh b/mem0-plugin/scripts/on_pre_compact.sh index 38a503c5d..3448cf5f4 100755 --- a/mem0-plugin/scripts/on_pre_compact.sh +++ b/mem0-plugin/scripts/on_pre_compact.sh @@ -55,7 +55,8 @@ Tool call shape: add_memory( messages=[{"role":"user","content":""}], user_id="", - metadata={"type":"session_state","source":"pre-compaction","project_id":"","branch":""}, + app_id="", + metadata={"type":"session_state","source":"pre-compaction","branch":""}, infer=False, ) ``` diff --git a/mem0-plugin/scripts/on_session_start.sh b/mem0-plugin/scripts/on_session_start.sh index 927b0c868..e51edf14f 100755 --- a/mem0-plugin/scripts/on_session_start.sh +++ b/mem0-plugin/scripts/on_session_start.sh @@ -38,11 +38,11 @@ if command -v python3 >/dev/null 2>&1; then import json, os, urllib.request, urllib.error api_key = os.environ.get('MEM0_API_KEY', '') user_id = os.environ.get('MEM0_RESOLVED_USER_ID', 'default') -project_id = os.environ.get('MEM0_PROJECT_ID', '') +app_id = os.environ.get('MEM0_PROJECT_ID', '') body = json.dumps({ 'query': 'project context', 'user_id': user_id, - 'filters': {'AND': [{'user_id': user_id}, {'metadata': {'project_id': project_id}}]}, + 'filters': {'AND': [{'user_id': user_id}, {'app_id': app_id}]}, 'limit': 100, }).encode() req = urllib.request.Request( @@ -72,14 +72,30 @@ echo "## Mem0 Active" echo "" echo "\`user=$MEM0_RESOLVED_USER_ID | project=$MEM0_PROJECT_ID | branch=$MEM0_BRANCH | memories=$MEM0_COUNT\`" echo "" -echo "Always include \`user_id\` + \`metadata.project_id\` in every \`search_memories\` filter and \`add_memory\` call:" +echo "Always include \`user_id\` + \`app_id\` in every \`search_memories\` filter and \`add_memory\` call:" echo "- user_id: \`$MEM0_RESOLVED_USER_ID\`" -echo "- project_id: \`$MEM0_PROJECT_ID\`" -echo "- branch: \`$MEM0_BRANCH\` (include in session-state / compact-summary metadata)" +echo "- app_id: \`$MEM0_PROJECT_ID\` (project scope — passed as top-level \`app_id\`, NOT in metadata)" echo "" if [ "$SOURCE" = "startup" ]; then - cat <<'EOF' + # First-run detection: auto-trigger onboarding for new projects + _ONBOARD_MARKER="$HOME/.mem0/.onboarded_${MEM0_PROJECT_ID}" + if [ ! -f "$_ONBOARD_MARKER" ]; then + cat <<'EOF' +## Mem0 First Run — Automatic Onboarding + +This is the first time mem0 is running for this project. You MUST run the onboarding wizard NOW before doing anything else. + +Execute the `/mem0:onboard` skill immediately. This will: +1. Verify the API key +2. Detect and import project files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) +3. Install coding categories optimized for development +4. Show a summary of what was set up + +Do NOT skip this. Do NOT ask the user if they want to onboard. Just run `/mem0:onboard` now. +EOF + else + cat <<'EOF' ## Mem0 Session Bootstrap You have access to persistent memory via the mem0 MCP tools. Before doing anything else: @@ -90,6 +106,7 @@ You have access to persistent memory via the mem0 MCP tools. Before doing anythi IMPORTANT: Do NOT skip this step. Always bootstrap context first. EOF + fi # Auto-import declarative project files in background MEM0_CWD="$(echo "$INPUT" | jq -r '.cwd // "."' 2>/dev/null || echo ".")" \ diff --git a/mem0-plugin/scripts/on_stop.sh b/mem0-plugin/scripts/on_stop.sh index a555b8c33..f9987744f 100755 --- a/mem0-plugin/scripts/on_stop.sh +++ b/mem0-plugin/scripts/on_stop.sh @@ -54,7 +54,7 @@ Memories can be as detailed as needed — include full context, reasoning, code If nothing notable happened in this interaction, it's fine to skip. Only store genuinely useful learnings. -Always include `"project_id"` in the metadata of any memory you store. +Always include `app_id` (the active project_id from SessionStart) as a top-level parameter in every `add_memory` call. EOF # Capture transcript state in the background via Mem0 REST API diff --git a/mem0-plugin/scripts/on_stop_codex.sh b/mem0-plugin/scripts/on_stop_codex.sh index 645b471a6..983bac8de 100755 --- a/mem0-plugin/scripts/on_stop_codex.sh +++ b/mem0-plugin/scripts/on_stop_codex.sh @@ -51,7 +51,7 @@ ${REPORT_BLOCK}Before finishing, check if there are important learnings from thi Memories can be as detailed as needed — include full context, reasoning, code snippets, file paths, and examples. Longer, searchable memories are more valuable than vague one-liners. -Always include \`"project_id"\` in the metadata of any memory you store. +Always include \`app_id\` (the active project_id from SessionStart) as a top-level parameter in every \`add_memory\` call. If nothing notable happened in this interaction, it's fine to skip. Only store genuinely useful learnings. EOF diff --git a/mem0-plugin/scripts/on_stop_cursor.sh b/mem0-plugin/scripts/on_stop_cursor.sh index 90c41b2fe..3e441c557 100755 --- a/mem0-plugin/scripts/on_stop_cursor.sh +++ b/mem0-plugin/scripts/on_stop_cursor.sh @@ -35,7 +35,7 @@ ${REPORT_BLOCK}Before finishing, check if there are important learnings from thi 4. Did you learn anything about the user's preferences? -> Store with metadata \`{"type": "user_preference"}\` 5. Were there environment/setup discoveries? -> Store with metadata \`{"type": "environmental"}\` -Always include \`"project_id"\` in the metadata of any memory you store. +Always include \`app_id\` (the active project_id from session start) as a top-level parameter in every \`add_memory\` call. If nothing notable happened, it's fine to skip. Only store genuinely useful learnings. EOF diff --git a/mem0-plugin/scripts/on_task_completed.sh b/mem0-plugin/scripts/on_task_completed.sh index 7d839cdb2..8aa570794 100755 --- a/mem0-plugin/scripts/on_task_completed.sh +++ b/mem0-plugin/scripts/on_task_completed.sh @@ -31,7 +31,7 @@ Extract key learnings from this completed task and store them using the mem0 \`a Memories can be as detailed as needed — include full context, reasoning, code snippets, and examples. Only store genuinely useful learnings — skip if the task was trivial. -Include \`"project_id": "$MEM0_PROJECT_ID"\` in metadata for all memories. +Include \`app_id\` = \`"$MEM0_PROJECT_ID"\` as a top-level parameter in every \`add_memory\` call (not in metadata). EOF exit 0 diff --git a/mem0-plugin/scripts/on_user_prompt.sh b/mem0-plugin/scripts/on_user_prompt.sh index 2e7fc15e9..ddf38011e 100755 --- a/mem0-plugin/scripts/on_user_prompt.sh +++ b/mem0-plugin/scripts/on_user_prompt.sh @@ -60,12 +60,12 @@ improve your answer. The agent -- not this hook -- owns this decision. - Phrase queries as **nouns** ("auth module decisions"), not full sentences. - Filter shape: the root must be a logical operator (\`AND\` / \`OR\` / \`NOT\`) with an array, and metadata uses a **nested** object (not dotted keys). - Combine \`user_id\` with one \`metadata.type\` clause per call: - - \`{"AND": [{"user_id": "$USER_ID"}, {"metadata": {"project_id": "$MEM0_PROJECT_ID"}}, {"metadata": {"type": "decision"}}]}\` -- design / architecture - - \`{"AND": [{"user_id": "$USER_ID"}, {"metadata": {"project_id": "$MEM0_PROJECT_ID"}}, {"metadata": {"type": "anti_pattern"}}]}\` -- debugging, error handling - - \`{"AND": [{"user_id": "$USER_ID"}, {"metadata": {"project_id": "$MEM0_PROJECT_ID"}}, {"metadata": {"type": "user_preference"}}]}\` -- tooling, stack, style - - \`{"AND": [{"user_id": "$USER_ID"}, {"metadata": {"project_id": "$MEM0_PROJECT_ID"}}, {"metadata": {"type": "convention"}}]}\` -- established patterns - - Or scope with just \`{"AND": [{"user_id": "$USER_ID"}, {"metadata": {"project_id": "$MEM0_PROJECT_ID"}}]}\` when no metadata filter fits. + Combine \`user_id\` + \`app_id\` with one \`metadata.type\` clause per call: + - \`{"AND": [{"user_id": "$USER_ID"}, {"app_id": "$MEM0_PROJECT_ID"}, {"metadata": {"type": "decision"}}]}\` -- design / architecture + - \`{"AND": [{"user_id": "$USER_ID"}, {"app_id": "$MEM0_PROJECT_ID"}, {"metadata": {"type": "anti_pattern"}}]}\` -- debugging, error handling + - \`{"AND": [{"user_id": "$USER_ID"}, {"app_id": "$MEM0_PROJECT_ID"}, {"metadata": {"type": "user_preference"}}]}\` -- tooling, stack, style + - \`{"AND": [{"user_id": "$USER_ID"}, {"app_id": "$MEM0_PROJECT_ID"}, {"metadata": {"type": "convention"}}]}\` -- established patterns + - Or scope with just \`{"AND": [{"user_id": "$USER_ID"}, {"app_id": "$MEM0_PROJECT_ID"}]}\` when no metadata filter fits. - Empty results are normal -- proceed without context. EOF diff --git a/mem0-plugin/skills/mem0-mcp/SKILL.md b/mem0-plugin/skills/mem0-mcp/SKILL.md index b3c5a6387..9ae3fc3db 100644 --- a/mem0-plugin/skills/mem0-mcp/SKILL.md +++ b/mem0-plugin/skills/mem0-mcp/SKILL.md @@ -18,17 +18,16 @@ Decide whether persistent memory context would improve your response, then act a ## Project scoping -Every memory operation MUST be scoped to the current project: +Every memory operation MUST be scoped to the current project using `app_id` (entity-scoped memory): -- **On `add_memory`:** Always include `metadata.project_id` (the active project_id from SessionStart). -- **On `search_memories`:** Always include `{"metadata": {"project_id": ""}}` in the AND filter. -- **Session-state memories:** Also include `metadata.branch` (the active branch from SessionStart). +- **On `add_memory`:** Always pass `app_id=` as a **top-level parameter** (not in metadata). +- **On `search_memories`:** Always include `{"app_id": ""}` in the AND filter. Full filter template: ```python filters={"AND": [ {"user_id": ""}, - {"metadata": {"project_id": ""}}, + {"app_id": ""}, {"metadata": {"type": "decision"}} ]} ``` @@ -67,7 +66,7 @@ Two rules from the v2 filter spec: 1. The root **must** be a logical operator (`AND` / `OR` / `NOT`) with an array. A bare `{"user_id": "..."}` won't work. 2. Metadata uses a **nested** object, not a dotted key. `{"metadata": {"type": "decision"}}`, never `{"metadata.type": "decision"}`. Only top-level metadata keys are filterable. -Combine `user_id` with one metadata clause per call: +Combine `user_id` + `app_id` with one metadata clause per call: | `metadata.type` clause | Use for | |--------|---------| @@ -78,7 +77,7 @@ Combine `user_id` with one metadata clause per call: Full filter (replace `` and `` with the active values from SessionStart): ```python -filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "decision"}}]} +filters={"AND": [{"user_id": ""}, {"app_id": ""}, {"metadata": {"type": "decision"}}]} ``` ### Worked example @@ -91,16 +90,16 @@ search_memories(query="Refactor the auth module to use JWT") # Hits whatever shares words. Misses prior decisions and preferences. ``` -Do (parallel — substitute the active `user_id` and `project_id` for the placeholders): +Do (parallel — substitute the active `user_id` and `app_id` for the placeholders): ```python search_memories(query="auth module decisions", - filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "decision"}}]}) + filters={"AND": [{"user_id": ""}, {"app_id": ""}, {"metadata": {"type": "decision"}}]}) search_memories(query="JWT", - filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}]}) + filters={"AND": [{"user_id": ""}, {"app_id": ""}]}) search_memories(query="auth refactor failures", - filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "anti_pattern"}}]}) + filters={"AND": [{"user_id": ""}, {"app_id": ""}, {"metadata": {"type": "anti_pattern"}}]}) search_memories(query="auth", - filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "user_preference"}}]}) + filters={"AND": [{"user_id": ""}, {"app_id": ""}, {"metadata": {"type": "user_preference"}}]}) ``` ## After completing significant work @@ -114,6 +113,8 @@ Extract key learnings and store them using the `add_memory` tool: - **Environment/setup discoveries** -> Include metadata `{"type": "environmental"}` - **Conventions established** -> Include metadata `{"type": "convention"}` +Always include `"branch": ""` in the metadata object alongside `type`. The active branch is shown in the SessionStart banner. This enables branch-scoped filtering later (e.g., "what did we do on feature/auth-rewrite?"). + > `metadata.type` (which you set explicitly) and `categories` (which the platform auto-tags after the project's custom-category list — see `scripts/setup_coding_categories.py`) are complementary. Always set `metadata.type` for explicit filtering; the platform fills in `categories` on its own. Don't try to set `categories` on `add_memory` calls — per-request overrides aren't supported on the managed API. ### Expiration: high-churn vs durable @@ -133,7 +134,7 @@ When the user is asking about *current* state ("where were we", "what's the acti ```python # Last 90 days only -{"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "session_state"}}, {"created_at": {"gte": "<90 days ago, YYYY-MM-DD>"}}]} +{"AND": [{"user_id": ""}, {"app_id": ""}, {"metadata": {"type": "session_state"}}, {"created_at": {"gte": "<90 days ago, YYYY-MM-DD>"}}]} ``` Skip the recency filter when the user is asking about durable facts ("what conventions does this project use", "have we hit this bug before") — those are timeless and recency would hide them. @@ -148,7 +149,8 @@ When you've done the extraction work yourself — pre-compaction summaries, deci add_memory( messages=[{"role": "user", "content": ""}], user_id="", - metadata={"type": "decision"}, + app_id="", + metadata={"type": "decision", "branch": ""}, infer=False, ) ``` diff --git a/mem0-plugin/skills/mem0-onboard/SKILL.md b/mem0-plugin/skills/mem0-onboard/SKILL.md index 001abead6..7ea69fb17 100644 --- a/mem0-plugin/skills/mem0-onboard/SKILL.md +++ b/mem0-plugin/skills/mem0-onboard/SKILL.md @@ -31,7 +31,7 @@ echo "${MEM0_API_KEY:+SET}" || echo "NOT_SET" ## Step 2: Show identity Report the active identity to the user: -- Call `search_memories` with `query="project setup"`, `user_id=`, `filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}]}`, `limit=1` to verify connectivity. +- Call `search_memories` with `query="project setup"`, `user_id=`, `filters={"AND": [{"user_id": ""}, {"app_id": ""}]}`, `limit=1` to verify connectivity. - Print: `Connected. user=, project=, branch=` - If the search fails, troubleshoot the API key. @@ -51,7 +51,8 @@ If user says yes (or default): - Call `add_memory` with: - `messages=[{"role": "user", "content": "## Project Profile: \n\nProject: \n\n"}]` - `user_id=` - - `metadata={"type": "project_profile", "file": "", "project_id": "", "source": "onboard"}` + - `app_id=` + - `metadata={"type": "project_profile", "file": "", "source": "onboard", "branch": ""}` - `infer=False` ## Step 4: Install coding categories @@ -71,14 +72,23 @@ python3 "/scripts/setup_coding_categories.py" --apply If the script reports an error, show the error message and suggest checking the API key. -## Step 5: Summary +## Step 5: Mark project as onboarded + +Create a marker file so SessionStart won't re-trigger onboarding next session: + +```bash +mkdir -p ~/.mem0 && touch ~/.mem0/.onboarded_ +``` + +This is silent — no user-facing output needed. + +## Step 6: Summary Print a summary: ``` Onboarding complete. user_id: - project_id: - branch: + project_id: (app_id) imported: files categories: diff --git a/mem0-plugin/skills/mem0-switch-project/SKILL.md b/mem0-plugin/skills/mem0-switch-project/SKILL.md index 8ed2415f2..923b5edec 100644 --- a/mem0-plugin/skills/mem0-switch-project/SKILL.md +++ b/mem0-plugin/skills/mem0-switch-project/SKILL.md @@ -40,7 +40,7 @@ The user provides a project name as an argument: `/mem0:switch-project ` with the user's chosen project name.) 3. Verify by searching for existing memories: - - Call `search_memories` with `query="project"`, `filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}]}`, `limit=1` + - Call `search_memories` with `query="project"`, `filters={"AND": [{"user_id": ""}, {"app_id": ""}]}`, `limit=1` 4. Print: ``` diff --git a/mem0-plugin/skills/mem0-tour/SKILL.md b/mem0-plugin/skills/mem0-tour/SKILL.md index 2efc216bc..2bfc31555 100644 --- a/mem0-plugin/skills/mem0-tour/SKILL.md +++ b/mem0-plugin/skills/mem0-tour/SKILL.md @@ -13,34 +13,79 @@ Show the user what mem0 has stored for the current project. ## Execution -1. Run the following `search_memories` calls in parallel (all with the active `user_id` and `metadata.project_id`): +### Step 1: Fetch ALL memories for this project - - `query="architecture decisions"`, `filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "decision"}}]}`, `limit=5` - - `query="anti patterns failures"`, `filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "anti_pattern"}}]}`, `limit=5` - - `query="task learnings strategies"`, `filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "task_learning"}}]}`, `limit=5` - - `query="coding conventions"`, `filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "convention"}}]}`, `limit=5` - - `query="user preferences"`, `filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "user_preference"}}]}`, `limit=5` - - `query="project profile"`, `filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "project_profile"}}]}`, `limit=5` - - `query="tooling setup environment"`, `filters={"AND": [{"user_id": ""}, {"metadata": {"project_id": ""}}, {"metadata": {"type": "environmental"}}]}`, `limit=5` +Call `get_memories` with: +- `user_id=` +- `app_id=` -2. Group results by category. For each category with results, print: +This returns every memory scoped to the project — no semantic filtering, no missed results. - ``` - ## ( memories) - - (score: ) - - ... - ``` +If `get_memories` doesn't support `app_id` as a direct parameter, use: +- `filters={"AND": [{"user_id": ""}, {"app_id": ""}]}` -3. For categories with zero results, print: `: (empty)` +Pass `page_size=100` (or the maximum allowed) to get a full picture. -4. Print totals at the end: - ``` - --- - Total: memories across categories for project - ``` +### Step 2: Run supplementary semantic searches -5. If ALL categories are empty, print: - ``` - No memories stored yet for project . - Run /mem0:onboard to import project files, or start working — mem0 captures learnings automatically. - ``` +In parallel, run these `search_memories` calls to get relevance-ranked results for key topics: + +- `query="architecture decisions design choices"`, `filters={"AND": [{"user_id": ""}, {"app_id": ""}]}`, `limit=10` +- `query="bugs errors failures anti-patterns"`, `filters={"AND": [{"user_id": ""}, {"app_id": ""}]}`, `limit=10` +- `query="project setup tooling conventions preferences"`, `filters={"AND": [{"user_id": ""}, {"app_id": ""}]}`, `limit=10` + +**Do NOT filter by `metadata.type` in these calls.** The platform auto-assigns `categories` — filtering on `metadata.type` misses memories that were auto-categorized but don't have an explicit `metadata.type`. + +### Step 3: Merge and group + +Merge all results by memory ID (deduplicate). For each memory, determine its group using this priority: + +1. **Platform `categories` field** (array on each memory, auto-assigned by Mem0). Use the first category value. +2. **`metadata.type` field** (if present, set explicitly by hooks/agent). Use as fallback if no `categories`. +3. **"other"** bucket for memories with neither. + +Map category names to display names: + +| Platform category / metadata.type | Display name | +|---|---| +| `architecture decisions`, `architecture_decisions`, `decision` | Architecture Decisions | +| `anti patterns`, `anti_patterns`, `anti_pattern` | Anti-Patterns | +| `task learnings`, `task_learnings`, `task_learning` | Task Learnings | +| `coding conventions`, `coding_conventions`, `convention` | Coding Conventions | +| `user preferences`, `user_preferences`, `user_preference` | User Preferences | +| `project profile`, `project_profile` | Project Profile | +| `tooling setup`, `tooling_setup`, `environmental` | Tooling & Setup | +| `technology`, `professional_details` | Tooling & Setup | +| `session_state` | Session State | +| `compact_summary` | Compact Summaries | +| anything else | Other | + +### Step 4: Display results + +For each group that has results, print: + +``` +## ( memories) +- (score: ) +- ... +``` + +Show up to 5 memories per group. If a group has more than 5, show top 5 by recency (or similarity score if from a search call) and note `... and more`. + +For groups with zero results, skip them entirely — don't print empty groups. + +### Step 5: Print totals + +``` +--- +Total: unique memories across categories for project +Branch: +``` + +### Step 6: Empty state + +If zero memories found for this project, print: +``` +No memories stored yet for project . +Run /mem0:onboard to import project files, or start working — mem0 captures learnings automatically. +```