Skip to content

Repository files navigation

Change Memory

English | Русский

Change Memory gives Claude Code compact, local memory of what changed in your project across sessions. It stores semantic summaries, changed files, unresolved issues, risks, and compressed patches without loading full diffs into context by default.

A git-native, fully offline, team-shareable change log for your AI agent. Zero tokens, zero cloud, zero telemetry — the change history is plain JSON that commits with your repo and travels to the next coder, with author attribution. Unlike conversation-memory plugins, Change Memory remembers what changed in the code and why, not what was said in the chat. See Change Memory vs other memory plugins.

Category: development Keywords: claude-code, mcp, agent-memory, git-diff, context-management, token-optimization, coding-agent


What it does

Long coding sessions burn tokens re-reading history. Change Memory keeps a tiny on-disk "map" of your project's changes and hands Claude a compact snapshot at the start of a session instead of full diffs. The full patches are stored locally, gzip-compressed, and loaded only when explicitly requested.

The guiding principle:

The agent gets a short change map, not the whole diff. Full patch files are loaded only on explicit request.

Why Claude Code users want it

  • Continuity across sessions — pick up where you left off without re-explaining.
  • Token efficiency — bootstrap context is budgeted (~700 tokens by default).
  • Avoid repeating work — search past fixes, risks, and unresolved items.
  • Local & private — no network, no telemetry. The semantic map can be committed to share with your team; raw diffs (patches) stay on your machine.
  • Team-friendly — the change history travels with the repo, with author attribution, so the next coder sees who changed what and why.

Change Memory vs other memory plugins

Most memory plugins for AI agents record the conversation / agent observations and compress them with an LLM (spending tokens) or store them in a cloud / paid service. Change Memory deliberately occupies a different niche: a diff-centric, offline, git-committed change log.

Change Memory Conversation-memory plugins (e.g. claude-mem) Cloud memory frameworks (e.g. Mem0, Zep)
Remembers What changed in the code, and why (git diffs + reason/risk) What the agent did/said during the session Facts, preferences, conversation history
Summarization Offline heuristic — no LLM, no tokens AI-compressed (consumes tokens / API) LLM + embeddings
Storage Local files (.change-memory/) Local DB (e.g. SQLite + vector) Mostly cloud / paid tiers
Network / telemetry None Varies (API for compression) Cloud by design
Team sharing Built-in — map commits to git, with author attribution Typically per-developer Per-account
Auditability Plain JSON, diffs in your PR Opaque store Remote service
Search Keyword Hybrid keyword + semantic Semantic / graph
Agents Claude Code Often multi-agent Many agents

When to choose Change Memory: you want a shared, auditable record of code changes that costs nothing to run, sends nothing off your machine, and lives in the repo — ideal for privacy-sensitive, regulated, or team settings.

When another tool fits better: you want rich AI-written session summaries, semantic/vector recall over conversation history, or one memory layer across many different agents. These are complementary — Change Memory can run alongside them.

How it works

The plugin ships three parts:

  1. Agent Skill (skills/change-memory) — teaches Claude when and how to use the memory (progressive disclosure: snapshot first, patches last).
  2. MCP server (mcp-server/) — local Node.js server exposing the memory tools.
  3. Slash commands (commands/) — quick entry points.

State lives in your project under .change-memory/:

.change-memory/
  index.json       # project metadata, unresolved items, budgets                  [shared]
  changes/         # one JSON file per captured change (incl. author)             [shared]
  summaries/       # archived history: archive_*.jsonl (data) + archive_*.md (view) [shared]
  session.md       # compact snapshot (mirrors get_session_context, regenerated)  [local]
  patches/         # gzip-compressed diffs of UNCOMMITTED drafts only (0.15+)      [local]
  auto-capture.json# per-machine fingerprint + auto-capture on/off toggle         [local]
  .gitignore       # written by init_memory: commits the map, ignores the rest

One file per change means two teammates capturing concurrently touch different paths — git merges the shared map without conflicts. (Pre-0.14 stores used a single changes.jsonl; it is migrated automatically on the first capture, with the original kept locally as changes.jsonl.migrated.) The "active files" and "recent changes" lists are derived from the history at read time, so index.json no longer accumulates conflict-prone state.

Since 0.15 the plugin does not duplicate git: records of committed work carry a commit_hash and read their diff from git diff-tree on demand, so patches/ only ever holds the current uncommitted drafts — disk usage stays flat no matter how long the history grows.

[shared] files are committed so teammates inherit the change history; [local] artifacts stay on your machine. See Team workflow below.

See examples/change-memory/ for a static sample of session.md and index.json so you can preview the format without a live install.

Install

  1. Add this plugin from your Claude Code marketplace (or install locally).
  2. The MCP server runs from compiled output at mcp-server/dist/index.js.
    • If you cloned the source, build it once: npm install (the prepare script runs npm run build automatically). Requires Node.js ≥ 18.
  3. Restart Claude Code so it picks up the bundled MCP server defined in .mcp.json.

Enabling the MCP server

.mcp.json registers the server for the plugin:

{
  "mcpServers": {
    "change-memory": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/mcp-server/dist/index.js"]
    }
  }
}

${CLAUDE_PLUGIN_ROOT} is provided by Claude Code and points at the installed plugin directory. No configuration is required.

Install for OpenAI Codex

The same repository doubles as a Codex plugin: .codex-plugin/plugin.json declares the manifest, the shared hooks/hooks.json matchers cover Codex's tool names (apply_patch, shell/Bash), and the skill ships identically.

  1. One-time: register the plugin in your personal Codex marketplace — ~/.agents/plugins/marketplace.json needs a change-memory entry with source ./plugins/change-memory (the Codex plugin-creator flow sets this up).

  2. Sync the plugin into ~/plugins/change-memory:

    npm run install:codex

    The script builds, copies .codex-plugin/, hooks/, skills/, mcp-server/dist/ plus production dependencies, and stamps the manifest version with a +codex.<timestamp> cachebuster.

  3. In Codex, run /plugins and (re)install Change Memory from the personal marketplace.

Codex runs installed plugins from its plugin cache, so the bundled skill instructs the agent to pass projectPath explicitly on every MCP call; the hooks are unaffected (they receive the project cwd on stdin).

Team workflow

Change Memory is built to travel with the repo so the next coder understands what changed and why — without you re-explaining it.

  • Commit the map. init_memory writes a .change-memory/.gitignore that commits index.json, changes/ and summaries/ (the semantic map) and ignores patches/, auto-capture.json and session.md (machine-local / heavy-binary). Just commit .change-memory/ as part of your normal git flow — the plugin never runs git writes itself.
  • Attribution. Each change records its author from git config user.name/user.email, so list_changes and show_change show who made it.
  • Fresh clone. A teammate who clones the repo runs /memory-session (get_session_context) and immediately gets the rebuilt snapshot from the committed map — no patches required. They can show_change any change's metadata; the raw diff (includePatch: true) is only available for changes captured on their own machine, since patches stay local.
  • Share patches too (opt-in). Run /memory share on (or configure({ sharePatches: true })) to commit patches/ as well, so teammates can load any change's diff. The flag is stored as share_patches in the committed index.json and the managed .gitignore is regenerated to track patches/; /memory share off reverts to local-only. You can also opt in at setup time with /memory-init share. Note this commits compressed patch blobs, which adds repo weight — leave it off unless the team wants full diffs.
  • Constraints & decisions travel too. The team constraints rendered in every session snapshot (/memory constraints <add|drop|list>configure({ addConstraints/removeConstraints })) and the durable decision log (/memory decision <add|drop|list>capture_change({ decisions/dropDecisions }), auto-linked to the change that embodied the ruling) both live in the committed index.json — commit .change-memory/ and every teammate's agent starts each session under the same rules, with the same settled rulings.

Prefer not to share? Delete the generated .change-memory/.gitignore and add .change-memory/ to your project's root .gitignore to keep everything machine-local instead.

Tools

Tool Purpose
init_memory Create .change-memory/ for the project.
capture_change Snapshot the current git diff (incl. untracked files) → compressed patch + semantic summary. With staged: true, captures git diff --cached instead — exactly what the next commit will contain. Accepts optional agent-authored llmSummary/llmRisk/llmType (host model, no network) and tags[]. unresolvedItems/resolveItems add or close entries in the Open Issues list (resolve matches by case-insensitive substring; works on a clean tree as an issues-only update). decisions/dropDecisions maintain the durable decision log the same way — a decision logged during a capture auto-links to the captured change. With enrichChangeId, applies the agent fields to an existing record in place (lazy enrichment) instead of capturing.
configure Adjust settings: autoCapture (per-machine toggle), sharePatches (team-wide via index.json), the numeric tunables maxBootstrapTokens/maxRecentChanges/autoCompactAfterChanges/autoCompactOlderThanDays, the coalesceDrafts boolean (rolling-draft toggle) and the team constraints via addConstraints/removeConstraints (all team-wide via index.json). Omit a field to leave it unchanged; omit everything to query — the report lists every setting, the live constraints and the decision log.
get_session_context Return the compact markdown snapshot. Never includes full diffs. Optional focus ("auth token refresh") spends the snapshot budget on changes relevant to a topic instead of the most recent ones; optional newSince (ISO timestamp) highlights what landed after that moment.
show_change Show one change's metadata; the full patch with includePatch: true, or a single file's hunk with file: "<substring>".
list_changes Compact table: id | type | file | summary. Optional file/type/branch/tag filters; pending: true lists records awaiting enrichment.
search_changes Field-weighted search (summary/tags weigh most) with a recency boost, whole-word matching and light suffix morphology in English ("eviction" finds evictStale, "migration" finds migrations/) and Russian («миграции» находит «миграция таблицы»), across summary, tags, reason, type, files, risk, tests. Each row shows its relevance score; matches far weaker than the best hit are dropped. Compacted history is searched too (an "Archived matches" section), and the tag filter spans archived history as well.
why_changed Oldest-to-newest intent timeline for one file: every recorded change that touched it, merged from active and archived history — date, type, id, commit, summary, risk flags, tags. Answers "why is this code the way it is" before you modify it; a bare filename resolves by suffix. Never includes diff content.
review_diff Facts-only review brief for the pending diff (working tree, or staged: true): per changed file — prior changes, risk flags, fragility signals (repeated fixes), plus matching open issues and related history ranked by the search scorer. The server assembles facts; the host model judges the diff against them.
summarize_branch PR-ready markdown summary of a branch's changes, grouped by type, with files/risks/tests; compacted (archived) records are included. With sinceRef (a tag/branch), scopes to <sinceRef>..HEAD — release notes since that ref. With since ("7d"/"48h"/"2w" or a date), allBranches: true and/or author, becomes a time-window digest — "what happened this week?".
compact_memory Archive old changes into a summary; keep recent ones; preserve patches.
doctor Report store health: schema version, unreadable records, orphaned/missing patches, a stale store.lock, un-migrated legacy changes.jsonl. With fix: true, deletes orphaned patches and a provably-dead lock.

Slash commands

Three core commands are top-level; the rest live under a single /memory dispatcher to keep the command surface small.

Command Action
/memory-init [share-patches] Initialize memory; add share to commit patches too.
/memory-capture [staged] [reason] Capture current changes; prefix with staged to capture only the staged set.
/memory-session Load the compact session context.
/memory show <changeId> [patch] Show a change (add patch for the diff).
/memory search <query> [limit] Search change history by keyword.
/memory pr [branch] [limit] [since <ref>] PR-ready summary of a branch's recorded changes; since <ref> scopes to <ref>..HEAD.
/memory digest [window] [author <name>] [branch <name>] Time-window digest of recorded work ("what did I do this week?"), across all branches unless one is named.
/memory why <path> [limit] Oldest-to-newest intent timeline for one file (active + archived history).
/memory review [staged] Facts-only review brief for the pending diff; the model adds the judgment.
/memory compact [olderThanDays] [keepRecent] Archive old changes; keep recent ones.
/memory auto <on|off|status> Turn automatic capture on/off (per-machine).
/memory share <on|off|status> Turn patch sharing on/off (team-wide).
/memory set [key] [value] Tune settings (bootstrap budget, snapshot length, compaction, draft coalescing); no args to list them.
/memory enrich [limit] Batch-upgrade heuristic-only auto-captures with agent-authored summaries/tags.
/memory issue <done|add> <text> Close (by substring) or add an Open Issues entry.
/memory constraints <add|drop|list> [text] Edit or list the team constraints rendered in every snapshot.
/memory decision <add|drop|list> [text] Log, retire or list settled decisions (durable rulings, not TODOs).
/memory doctor [fix] Report store health; fix repairs the safe issues (orphan patches, dead lock).

Automatic session bootstrap

When a session starts (or after /clear or context compaction), a bundled SessionStart hook injects the compact change snapshot into context automatically — the same budget-aware markdown get_session_context returns, never a full diff. You no longer need to run /memory-session by hand; it remains available for reloading mid-session. If memory isn't initialized for the project, the hook skips silently.

Before building the snapshot, the hook reconciles the history with git log (0.15): commits that landed outside Claude Code — terminal commits, pulls with teammates' work, rebases, GitHub-UI squash-merges — are backfilled as records, with the commit subject as an already-enriched summary, the conventional-commit prefix as the type and the commit author attributed. The scan is bounded (newest 50 commits, at most 10 records per run — a backlog converges over a few sessions); commits older than the store's creation are out of scope, and merge commits are skipped.

Automatic capture

Once the plugin is installed, Change Memory records changes automatically — you don't need to run /memory-capture by hand.

A bundled Claude Code hook (hooks/hooks.json) fires on PostToolUse for the Write, Edit, MultiEdit and NotebookEdit tools and runs the plugin's auto-capture command (mcp-server/dist/cli/autoCapture.js) directly — no MCP round-trip.

  • Manual capture still works. /memory-capture (and the capture_change tool) remain available — use them for a deliberate, named snapshot with a custom reason (staged: true still checkpoints exactly what the next commit will contain).
  • Commits are the primary record. A PostToolUse hook on Bash detects a finished git commit and records the commit itself: its message as an already-enriched summary (a conventional-commit prefix like feat:/fix: sets the change type) and — since 0.15 — no duplicated patch: the record carries the commit hash and show_change reads the diff back from git on demand. The working-tree draft on the same branch is subsumed by the commit record, so history holds one record per commit.
  • One rolling draft between commits (0.15). Auto-capture keeps a single evolving record of the uncommitted working tree per branch — each edit updates it in place while HEAD is unchanged, instead of appending time-sliced snapshots. A commit closes the draft (the commit record replaces it); the next edit opens a fresh one. Set coalesceDrafts: false via configure to disable and get one record per capture instead.
  • Commit messages are the enrichment. Because commits become records with their subjects as authored summaries (hook or reconciliation, see above), heuristic-only records are transient by design. The snapshot still lists any that remain under Awaiting Enrichment, and /memory enrich upgrades them deliberately — the 0.14 Stop-hook prompt (which cost a turn and often never fired) was removed in 0.15.
  • Secret-like files are never embedded. Untracked files named like secrets (.env*, id_rsa*, credentials.*, *.pem, *.key, *.p12/pfx) keep their header in the patch but their content is replaced with @@ skipped: likely secret @@ — important with sharePatches: on.
  • Secret-looking lines are redacted (0.14, extended in 0.15). On top of the name filter, every composed diff — tracked files included — gets a content pass: known token shapes (AWS/GitHub/Slack keys, PEM blocks, JWTs), credential-looking assignments (quoted and unquoted .env-style), and credentials embedded in URLs (postgres://user:password@host) are replaced with [change-memory: redacted likely secret] before the patch is stored, and the record carries a risk note. Env lookups ($VAR), ${VAR} templates and placeholders (<your-key>) never trigger it. The filter is precision-first by design — it catches well-known shapes, it is not a guarantee; keep real secrets out of the repo regardless.
  • Debounced. At most one capture per debounceMs window (default 30s), so a burst of edits produces a single snapshot, not dozens. The debounce check runs before any git work, so a debounced hook fire costs no diff at all.
  • Deduplicated. Auto-capture fingerprints the composed working-tree diff (tracked diff + untracked file contents). If the diff is unchanged since the last capture, it does nothing.
  • Never touches git state. It only reads git diff / git status and writes into .change-memory/. It does not git add, commit, checkout, or run any destructive git command.
  • Non-blocking. If memory isn't initialized, the path isn't a git repo, or the tree is clean, the hook skips silently and never interrupts your work.

Auto-capture keeps its bookkeeping in .change-memory/auto-capture.json (last_fingerprint, last_capture_at, last_change_id).

Project-root note: the hook passes ${CLAUDE_PROJECT_DIR} (the directory Claude Code was launched in) as projectPath. Open Claude Code at your git repository root, not a parent folder — if the repo is nested below the project root, auto-capture targets the wrong directory and silently skips.

Verify auto-capture is working

  1. Run /memory-init once in a git repo (auto-capture is a no-op until initialized).
  2. Ask Claude to edit a file. After the edit, check the latest entry:
    • list_changes (or /memory show) — a new chg_... with reason auto: ....
    • .change-memory/auto-capture.jsonlast_change_id updated.
  3. Ask for a second edit within 30s → no new entry (debounced/deduped). Wait >30s and edit again → a new entry appears.

Disable auto-capture (manual-only mode)

The simplest way is the per-machine toggle:

  • Run /memory auto off (calls configure). The flag is stored in the local, gitignored auto-capture.json, so it only affects your machine, never your teammates. /memory auto on re-enables it; /memory auto status reports the current state.

To hard-disable the hook for everyone (or as a fallback):

  • Remove or rename hooks/hooks.json in the installed plugin, or
  • Disable the plugin's hooks from /hooks / your Claude Code hook settings, or
  • Raise debounceMs in hooks/hooks.json to a large value to throttle it.

The MCP tools (including manual capture_change) are unaffected.

Example workflow

# New session
/memory-session          → Claude loads the compact snapshot

# ... you and Claude make changes ...

/memory-capture fixed token refresh on expiry
                            → chg_20260618_210712_... | fix | Ada <ada@example.com> | src/auth.ts

# Later, recall details
search_changes("token")     → finds the change
why_changed("src/app.ts")   → intent timeline, no diffs
show_change(chg_..., includePatch:false)  → metadata only
show_change(chg_..., includePatch:true)   → full diff, on demand
show_change(chg_..., file:"src/app.ts")   → just that file's hunk

Example .change-memory/session.md

# Session Context

Project: my-app

This is a compact memory snapshot for the coding agent.
It intentionally excludes full diffs to reduce token usage.

## Recent Changes

- chg_20260618_210712_de5c94c2 (Ada <ada@example.com>): Fix change: added 1,
  modified 1 file(s) in src (`src/auth.ts`). Reason: fix token refresh. Full
  patch stored locally.

## Active Files

- src/auth.ts

## Open Issues

- add tests for token expiry

## Decisions

- Refresh tokens rotate on every use — simpler revocation [chg_20260618_210712_de5c94c2]

## Constraints

- Keep AI context compact
- Do not include full diffs by default
- Load detailed patches only when explicitly needed

## Available Memory Tools

- show_change(changeId)
- list_changes()
- search_changes(query)
- capture_change()

Security model

This plugin is built to be safe for marketplace distribution:

  1. Local only. All state lives in .change-memory/ inside your project.

  2. No telemetry, no analytics, no phone-home.

  3. No external network calls. The server has no HTTP client. The optional richer summary (llmSummary/llmRisk/llmType on capture_change) is written by the host model (Claude Code) and passed in as plain text — the server itself never contacts an LLM and holds no API keys.

  4. No eval and no dynamic code execution.

  5. No arbitrary shell. The only external process is git, restricted to an allow-list of read-only argument vectors:

    • git diff
    • git diff --name-only
    • git diff --name-status
    • git status --porcelain [--untracked-files=all]
    • git rev-parse --is-inside-work-tree
    • git config user.name / git config user.email (read-only, for author attribution)

    Commands are executed via execFile (no shell), and user input is never interpolated into a command. Write operations (commit/add/checkout) are impossible by construction.

  6. No user code is modified or committed. Capture is read-only.

  7. Path traversal protection. Every file access is normalized and verified to stay inside the project root (ensureInsideRoot). Patches live only under .change-memory/patches/.

  8. Bounded reads. Untracked file content embedded in patches is capped (256 KB per file) and binary files are skipped.

Privacy model

  • Raw diffs never leave your machine. Patches are stored compressed locally and are gitignored by default; you control them.
  • The map that can be committed (summaries, file lists, reasons, authors, open issues) is intentionally diff-free — only what a teammate needs to follow the history, not the source itself.
  • The session snapshot deliberately excludes diffs to minimize what enters the model context.
  • Don't want to share anything? See Team workflow for how to keep the whole .change-memory/ directory machine-local.

MVP limitations

Not included in this first version:

  • Cloud sync / remote storage
  • External LLM summarization (the summarizer is heuristic and offline)
  • VSCode extension or web dashboard
  • Authentication
  • Telemetry
  • Arbitrary shell command execution

Troubleshooting

  • "No .change-memory found" — run /memory-init (or call init_memory).
  • "No changes to capture" — the working tree has no tracked changes and no untracked files. Make an edit first.
  • "Not a git repository"capture_change reads git diff; run inside a git repo (git init).
  • Server not loading — ensure mcp-server/dist/index.js exists; run npm install (which builds) and restart Claude Code.
  • Memory captured itself.change-memory/ is always excluded from capture (see getUntrackedFiles). The generated .change-memory/.gitignore already keeps patches and machine-local state out of commits.
  • Author shows (unknown) — set a git identity (git config user.name / git config user.email); pre-existing changes captured before this version have no recorded author.

Roadmap

See ROADMAP.md for what's shipped, what's next, and later ideas.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages