Skip to content

feat(claude-code-plugin): move the Claude Code plugin to its own integration and ship it as 0.3.0 - #7106

Open
kartik-mem0 wants to merge 23 commits into
mainfrom
feature/memo-claude-plugin-v1
Open

feat(claude-code-plugin): move the Claude Code plugin to its own integration and ship it as 0.3.0#7106
kartik-mem0 wants to merge 23 commits into
mainfrom
feature/memo-claude-plugin-v1

Conversation

@kartik-mem0

Copy link
Copy Markdown
Contributor

Linked Issue

Closes #7105

Description

Moves the Claude Code plugin out of integrations/mem0-plugin/ into its own integration at integrations/claude-code-plugin/, and ships it as 0.3.0.

The old plugin served Claude Code, Cursor, Codex, Kimi, Antigravity and OpenCode from one directory over the remote MCP server at mcp.mem0.ai. Claude Code's plugin surface (lifecycle hooks, skills, agents, userConfig) had outgrown what a shared MCP-only plugin could express, so it gets its own package. integrations/mem0-plugin/ stays exactly as it is for the other five harnesses.

What the new plugin does:

  • Captures evidence locally through Claude Code lifecycle hooks and writes it to SQLite inside the hook budget.
  • Extracts memories in a detached background worker, so a remote call never sits in the hot path and extraction survives the session closing (handoff packets under pending/, recovered on the next session start).
  • Exposes one local MCP tool, search_memories, instead of the old nine remote ones.
  • Adds six /mem0:* skills and the mem0:sidekick agent.
  • Pure stdlib Python, nothing to install.

The core/ + adapters/claude/ split is deliberate: core/ is the engine, adapters/claude/ is harness glue. A future per-harness plugin starts by copying core/ and keeping the contract tests verbatim.

Also in this PR: .github/workflows/claude-code-plugin-checks.yml plus its ci-gate.yml wiring, both Claude marketplace manifests repointed at the new path, and the docs page rewritten for 0.3.0 with a deprecation note on the old one.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactor (no functional changes)
  • Documentation update

AI Assistance

  • No AI assistance
  • AI-assisted (autocomplete, or I asked a model questions while writing this)
  • AI-generated (an agent wrote most or all of this diff)
  • I can explain every line of this diff and how it interacts with the rest of the codebase, without asking an AI tool.

Breaking Changes

mem0@mem0-plugins now resolves to ./integrations/claude-code-plugin at 0.3.0 instead of ./integrations/mem0-plugin at 0.2.15. Anyone on the marketplace entry picks up the new plugin on their next update.

For users upgrading from 0.2.x:

  • Memories carry over. Nothing is re-extracted or lost.
  • The nine MCP tools collapse to one local search_memories.
  • Six slash commands are replaced by their /mem0:* equivalents.
  • ~/.mem0/settings.json and mem0.md are no longer read.
  • Environment variables changed; see "Upgrading from 0.2.x" in the plugin README.

Do not run mem0-plugin and claude-code-plugin at the same time in Claude Code, they both capture the same sessions.

Test Coverage

  • I added/updated unit tests
  • I added/updated integration tests
  • I tested manually (describe below)
  • No tests needed (explain why)

87 tests under integrations/claude-code-plugin/tests/, all passing locally in 2.9s. ruff check . clean. All five marketplace/plugin manifests parse. The contract tests are the piece that matters for the core/ split, they are what a future harness adapter has to keep passing verbatim.

End-to-end verification against the live Platform (dev/prod_preflight.py, needs MEM0_API_KEY) is tracked separately and is not claimed here.

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation if needed

deshraj and others added 5 commits August 24, 2026 10:57
…ons/claude-code-plugin 0.3.0

Local evidence capture via fail-open lifecycle hooks, checkpointed background
extraction through a detached worker with a durable retry queue, a local
read-only search_memories MCP tool, six /mem0:* skills, and the mem0:sidekick
agent. Pure-stdlib Python, zero runtime dependencies. The identity scheme is
byte-compatible with the 0.2.x plugin so existing memories carry over.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ugin

BREAKING CHANGE: the 0.2.x command surface is replaced by six /mem0:* skills
and one read-only MCP tool; memories and configuration env vars carry over.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… old plugin

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts:
#	.claude-plugin/marketplace.json
#	integrations/AGENTS.md
@mintlify

mintlify Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
mem0 🟢 Ready View Preview Aug 25, 2026, 12:24 PM

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci CI/CD workflows and repo automation integrations Agent and editor integrations plugin Editor and agent plugins (Claude Code, Cursor, Codex, OpenClaw, Pi Agent) labels Aug 25, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…ocument the 0.2.x losses

Findings from a review pass over the 0.3.0 plugin, with regression tests.

Code:
- `hook.py` detached the flush worker with `start_new_session=True`, which
  CPython ignores on Windows. Background extraction could therefore die with
  the Claude Code session on a platform that ships a native installer. Added
  `detached_process_kwargs()`, which returns
  `DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP` there and keeps
  `start_new_session` on POSIX.
- `recover_pending_handoffs()` relaunched every pending packet at once with no
  cap and no expiry. After a long outage a session start could `Popen` one
  worker per accumulated packet, each able to block for
  `MEM0_CODE_EXTRACTION_WAIT_SECONDS`. Now capped at 5 oldest-first per
  session, with a 7-day TTL and stale `.running` reclaim.
- `memory_core.doctor()` reached `sys` through `os.sys`. Imported it properly.

Docs:
- Added `docs/CONTRACT.md`, which `integrations/AGENTS.md` already told future
  plugin authors to read but which did not exist. It pins the core/adapter
  split, the `hook_input` keys core reads, the scope rules (including why
  `mcp_server.py` calls `resolve_repo` directly), and the contract tests.
- `test_contract_doc_names_only_tests_that_exist` guards that document against
  the same drift.
- README "Upgrading from 0.2.x" claimed no configuration is lost. It is: the
  whole `~/.mem0/settings.json` key set, the `mem0.md` policy sections, and 28
  `MEM0_*` variables. New "What stops working" subsection names them.
- `docs/llms.txt` still routed Claude Code to `integrations/mem0-plugin/` and
  its nine MCP tools.
- Root `marketplace.json` was missing its trailing newline.
Keep 0.3.0 scoped to the plugin itself. The contract document is a design
note for future per-harness plugins, not something this release needs, so it
stays a local working file until there is a second harness to write it
against.

Removes the document and `test_contract_doc_names_only_tests_that_exist`,
which guarded it.
Claude Code now installs 0.3.0 from integrations/claude-code-plugin, so the
0.2.x Claude manifest, hooks, and MCP config in this directory are dead weight
that can only conflict with it.

Removes the three Claude-Code-only files:

  .claude-plugin/plugin.json  Claude Code plugin manifest
  hooks/hooks.json            Claude Code lifecycle hooks
  .mcp.json                   Claude Code MCP server config

No scripts or skills are removed. Codex, Cursor, Kimi, Antigravity, and
OpenCode reach the same scripts through their own manifests, and Antigravity
still sets CLAUDE_PLUGIN_ROOT as the path variable the shared scripts read.

Telemetry stops claiming a claude-code surface: detect_platform() no longer
maps CLAUDECODE / CLAUDE_PLUGIN_ROOT to "claude-code", and the version lookup
reports "unknown" for a surface with no manifest instead of falling back to
the deleted .claude-plugin manifest.
…h wording

The search section claimed explicit searches never repeat a memory. Only the
automatic first-prompt search deduplicates; every explicit search runs fresh.
Hooks run on a 3-6 second budget and fire on every tool call, so recording and
sending are split. record() appends one JSON line to a local spool and returns;
a detached sender drains it in batched PostHog requests, primarily from the
flush worker that is already detached and already doing network I/O.

Nine events (install, session_start, context_injected, handoff_recovered,
search, sidekick, flush, forget, control) carry durations, counts, coarse
outcome buckets, and sha256-truncated repo/session hashes. Prompts, memory
text, queries, file paths, repository names, and API keys are never sent.

An atomic rename to telemetry-<pid>.sending claims a batch so exactly one
sender owns it; a crashed sender leaves its claim to be reclaimed rather than
losing events. Identity resolves to the account email when an API key is
configured, with a persisted anonymous id before that and a one-shot $identify
to merge the pre-auth history.

Opt out with MEM0_TELEMETRY=false. Pure stdlib, no new dependencies.
…ng memories

A wildcard was a legal value for the scope environment variables and nothing
questioned it. MEM0_CODE_USER_ID=* with a real repository returned other
users' memories, because the repository still satisfied the API's "one real
entity ID" rule and the wildcard did the rest. MEM0_PROJECT_ID=* leaked the
same way across repositories. Confirmed live against the platform.

Wildcards are now refused as identities when read from config, so resolution
falls through to the next source, and refused again on the outbound request in
search_memories and flush_session. The outbound guard is the load-bearing one:
guarding only the config read leaves callers that build a RepoContext directly
still able to send one.

/mem0:forget used DELETE /v1/memories/?user_id=&app_id=, which the platform
honours only on user_id, so forgetting one repository wiped that user's
memories in every repository. It now lists the ids matching both scopes and
deletes each by id, reports partial failures, and refuses an unscoped delete
outright. Verified live that selection picks up only the target scope and
leaves same-user-other-repo and other-user-same-repo memories untouched.

An empty session posted a zero-message request and logged the resulting 400.
Empty batches are now dropped and the flush is a silent no-op.

Adds 10 regression tests, each confirmed to fail without its fix (117 -> 127).
result.md records the 4,794 live cases behind these findings.
result.md is a local test report, not a shipped artifact.
…emory

Search always sent `AND [user_id, app_id]`, both required, so a memory was
only reachable when the searcher matched both the person who wrote it and the
repo it came from. That trapped team knowledge inside one person and personal
preferences inside one repo.

Add a `scope` argument that relaxes exactly one of the two conjuncts to the
`*` wildcard:

  repo (default)  AND [user_id, app_id]        unchanged behaviour
  team            AND [app_id, user_id: "*"]   everyone's memories, this repo
  mine            AND [user_id, app_id: "*"]   your memories, every repo
  all             OR [team, mine]              both

Every branch keeps at least one label pinned to something the searcher owns,
so no scope relaxes both conjuncts at once. A test asserts that property for
every entry in SEARCH_SCOPES. `*` stays a filter value and is never accepted
as an identity: _scope_value already rejects it on both the read and write
paths.

The write path is unchanged. Configurable via the `search_scope` option or
MEM0_CODE_SEARCH_SCOPE, defaulting to `repo`, and exposed as an enum on the
search_memories MCP tool and `/mem0:search --scope`.
`claude plugin validate --strict integrations/claude-code-plugin` failed
with `userConfig.search_scope: Unrecognized key: "enum"`. The Claude Code
plugin manifest validates each `userConfig` option against a strict
object that accepts exactly `type`, `title`, `description`, `required`,
`default`, `multiple`, `sensitive`, `min`, and `max`. There is no
enum, choices, options, or oneOf key to declare a closed set of strings
with, so any extra key fails the whole manifest rather than being
ignored.

Nothing changes at runtime. `search_scope()` already returns the
configured value only when it is one of the four known scopes and falls
back to `repo` otherwise, so the closed set was enforced by the engine
and never by the manifest. The allowed values stay documented in the
option's own description, which is the text the installer shows.
…cope

The plugin knew one kind of thing: what this repository is. It did not
know what it takes to work in this repository. A session that lost
twenty minutes to `pytest tests/payments` failing on an import error,
then found that `pip install -e .` first makes it pass, wrote nothing
that would stop the next session losing the same twenty minutes. That
lesson is not a fact about the code, so the repository extraction
prompt was right to drop it, and there was nowhere else for it to go.

Add a second write lane. A session in which a command failed sends one
extra add call carrying that session's commands and their short
results, scoped to `agent_id: "claude-code"` instead of the user, with
`agent_custom_instructions` asking for operating lessons: the failing
invocation, what it returned, and the invocation that worked. Mem0
routes instructions by entity, so `user_id` alone gets
`custom_instructions` and `agent_id` alone gets
`agent_custom_instructions`. That is what makes two extraction policies
possible in one project.

The split is by content, not by speaker. Sending both IDs on one call
would make the Platform partition by who spoke rather than by what was
learned, and would move existing repository knowledge onto the agent.
The agent lane carries the failure evidence only, is skipped entirely
when every command in a session succeeded, and can be switched off with
the new `agent_memory` option or `MEM0_CODE_AGENT_MEMORY`.

Reads stay one search. `_search_filters` widens the repo-local branches
from `AND [app_id, user_id]` to `AND [app_id, OR [user_id, agent_id]]`,
so `repo` and `team` return both lanes and the cross-repository `mine`
branch is untouched. `team` names the agent explicitly because a
wildcard matches any value but not a missing one, and an operating note
has no `user_id`. `_scoped_memory_ids` gets the same widening, so
`/mem0:forget` deletes the notes it can see.

Operating notes are short and numerous, so on the way out
`_reserve_agent_slots` gives them a fixed share of the result window
rather than letting rank alone decide: one slot in three when Claude
searched, two in five on automatic recall, backfilled in rank order
when either lane is short. Injected lines carry an `[operating note]`
label so Claude can tell a claim about the code from a claim about
running it. Memories written before this change carry no lane and count
as repository knowledge, so nothing needs migrating.

Verified end to end against the Platform: a session that hit an import
error and recovered wrote three memories across two lanes, the next
session's first prompt was injected with the operating note labelled,
and `forget --remote` deleted all three.
… persona tests

- narrow team/all scopes in folders without a git remote so strangers
  sharing a folder name never see each other
- forget deletes only the caller's memories; --include-operating-notes
  opts into wiping the shared agent lane
- keep one operating-note slot at small top_k, best score wins at top_k=1
- add min_score (default 0.15) filtered client-side, the API ignores
  threshold
- default search scope is team
- expose run_id on search_memories, the MCP tool and /mem0:search
- doctor flags a wildcard user_id
run_id is the session key on every write; a flush that arrives without a
session id now returns no-session-id and keeps the evidence local instead
of writing it under the unknown-session placeholder.
…al lanes

Every flush now writes project memory under agent_id (repository facts and
operating lessons, shared by everyone in the repository) and personal memory
under user_id (preferences that follow the user between repositories). The
team scope and its user_id wildcard are gone: repo (default) is the project
memory plus your preferences, mine is your preferences everywhere, all is
both. Folders without a git remote keep project memory under the user.
Forget deletes personal memory by default; --include-project-memory wipes
the shared lane.
Project memory is stamped agent_id=<repo slug> and app_id=<repo>/<directory>,
never a user_id; personal memory stays under user_id. Search scopes become
repo (default), dir and mine, each OR-ing the user's own memories, with
run_id and category nested as AND. Plain folders share under
local-<folder>-<path hash>. Adds live integration tests and a scoping
benchmark under dev/scoping_tests.
…ke forget reliable

- write the folder chain to metadata.dirs and filter dir scope by membership,
  so a parent directory sees its subdirectories and a child never sees the parent
- resolve symlinked cwd and git root while keeping legacy project_map lookups
- fold failed commands into the project lane, two add calls per flush
- skip memories that only name the repository or say nothing was learned
- list each forget branch separately to work around the list endpoint
  returning nothing for OR filters with an empty user_id branch
- run the production preflight in a throwaway repository and clean up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci CI/CD workflows and repo automation documentation Improvements or additions to documentation integrations Agent and editor integrations plugin Editor and agent plugins (Claude Code, Cursor, Codex, OpenClaw, Pi Agent)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move the Claude Code plugin to its own integration and ship it as 0.3.0

3 participants