A tech pack that auto-syncs Claude Code's .claude/memories/ across a team via a dedicated shared git repo. Captures are handled by mcs-cli/memory (the continuous-learning skill + semantic retrieval); this pack shares those captures across the team without anyone remembering to commit or push.
Built for the mcs configuration engine.
identifier: shared-memories
requires: mcs >= 2026.4.12
You probably don't need this pack if your team commits .claude/memories/ directly into the project repo — normal git workflow already shares those memories across the team and this pack adds nothing.
This pack is useful when:
- You want memories in a dedicated repo, separate from project code — to avoid noising project PRs with memory-only diffs, to apply different access or review rules, or because the project's default-branch rulesets make small auto-commits painful.
- You run multiple related repos (microservices, mobile + web + backend, split client/server) that should share the same memory corpus — a learning about the auth contract is relevant to every service that talks to it, and a central memories repo lets all of them read/write the same KB.
- You want team memories to outlive individual project repos — short-lived prototypes, archived services, or repos that come and go shouldn't take institutional knowledge with them.
Claude Code's .claude/memories/ is great — you accumulate learning_*.md and decision_*.md files and Claude gets smarter about your codebase over time. But memories are per-engineer: when someone figures out a gnarly integration quirk or pins down a subtle architecture decision, only they benefit.
The obvious fix is a shared git repo. Two friction points kill adoption:
- Remembering to push. People forget. Memories sit on laptops for weeks.
- Branch protection on the shared repo. If every Claude turn needs a PR + ticket + approval, nobody will bother pushing their tiny observations.
This pack implements a closed-loop sharing system that pulls the latest team memories at session start and pushes new ones when Claude finishes a turn.
SHARED MEMORIES LOOP
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ SESSION │ │ TEAM KB │ │ WORK │ │ STOP │
│ START │────>│ PULL │────>│ SESSION │────>│ AUTO-PUSH │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
^ | | |
| | | filename guard |
| | | + configurable |
| v v push policy v
| ┌────────────────────────────────────────────────────┐
| │ <shared memories repo> │
| │ memories/ │
| │ learning_background_task_watchdog_timeout.md │
+─────────────│ learning_orm_batch_insert_memory_spike.md │
│ decision_architecture_mvvm_coordinators.md │
│ ... │
└────────────────────────────────────────────────────┘
Captures still come from mcs-cli/memory. This pack is the distribution layer that makes them team-shared.
| Piece | What | How |
|---|---|---|
| SessionStart Hook | Pulls the latest team memories at session start | git pull --ff-only against the shared checkout; also flags lingering uncommitted/unpushed state — a stuck auto-push in auto / full mode, or pending changes awaiting decision in review mode |
| Stop Hook | Handles new/modified memory files after each Claude turn per MEMORIES_AUTOPUSH_MODE (auto / full / review) |
Runs async; filename guardrail blocks bad names in every mode; mode dictates whether deletions auto-push, whether anything is committed at all, and (in review) prints the per-turn pending-changes report |
| PostToolUse Hook | Tells Claude in-conversation that a memory was saved in review mode, so it can mention pending review and invoke /approve-memories when the user confirms |
Fires sync after Write / Edit / MultiEdit to .claude/memories/; injects additionalContext into Claude's next decision step. Silent in auto and full |
| /approve-memories Slash Command | One shared approval surface both the user and Claude invoke identically — stages, commits, pulls --rebase, pushes everything pending under memories/ |
Re-runs the Stop-hook filename guardrail; takes an optional commit-message reason; works in every mode (primary use is review; also unblocks state stuck after a push failure in auto / full) |
| Sparse Checkout + Symlink | Keeps the shared repo invisible on disk | .claude/.memories-repo/ is a blobless single-branch sparse clone; .claude/memories is a symlink Claude Code reads from |
-
First
mcs sync— the configure script clones the shared repo sparsely into.claude/.memories-repo/, symlinks.claude/memoriesto it, and migrates any pre-existing local memories into the shared folder (conflicts are preserved for manual review) -
Session starts — the SessionStart hook fast-forwards the shared checkout and warns about any lingering state (auth failure / rebase conflict / guardrail-rejected files in
auto/full; pending review items inreview) -
During work — Claude uses the
continuous-learningskill to write newlearning_*.md/decision_*.mdfiles -
Claude finishes a turn — the Stop hook collects dirty files and dispatches by mode (
MEMORIES_AUTOPUSH_MODE, see Auto-Push Modes):- Naming guardrail (all modes) — any file failing
^memories/(learning|decision)_[a-zA-Z0-9_-]+\.md$halts everything until renamed auto(default) — adds/mods auto-pushed; deletions parked in the working tree for manual reviewfull— adds/mods AND deletions auto-pushed in one commitreview— nothing auto; the hook prints a per-file report and the user (or Claude, via the PostToolUse nudge) invokes/approve-memoriesto push, or runs the discard commands shown in the report
- Naming guardrail (all modes) — any file failing
-
Next session — teammates pull your new memories via SessionStart and the loop continues
| Hook | Event | What It Does |
|---|---|---|
| memories_pull.sh | SessionStart |
Fast-forwards the shared memories checkout; emits a mode-aware warning if previous state is stuck (or, in review mode, summarises pending review) |
| memories_autopush.sh | Stop (async) |
Dispatches by MEMORIES_AUTOPUSH_MODE mode (auto / full / review); filename guardrail applies in every mode |
| memories_announce.sh | PostToolUse (Write/Edit/MultiEdit) |
In review mode only, surfaces the just-written memory to Claude's context so it mentions pending review in chat. Silent in auto and full |
| Command | What It Does |
|---|---|
| /approve-memories | Stages, commits, pulls --rebase, and pushes everything pending under memories/. Primary entry point for review mode approval; also unblocks state stuck after a push failure in auto / full |
| Script | When | What It Does |
|---|---|---|
| configure-memories.sh | mcs sync |
Sparse-clones the shared repo, sets up the symlink, migrates any pre-existing .claude/memories/ into the shared folder |
| Check | What It Verifies |
|---|---|
| Shared memories setup | Sparse checkout exists and the symlink resolves to a live git repo (auto-fixable via mcs sync) |
| Shared memories remote access | Auth + network reachability via git ls-remote origin; warns (doesn't fail) on issues since local reads still work |
| Dep | Via |
|---|---|
| jq | brew |
- macOS
- Claude Code CLI
- mcs CLI
mcs-cli/memory(companion capture pack — produces thelearning_*.md/decision_*.mdfiles this pack shares)- A git repo the team can push to (SSH or HTTPS access)
# 1. Install mcs
brew install mcs-cli/tap/mcs
# 2. Register both packs (capture + share)
mcs pack add mcs-cli/memory
mcs pack add mcs-cli/shared-memories
# 3. Sync your project
cd ~/Developer/my-project
mcs sync
# 4. Verify everything is healthy
mcs doctorDuring mcs sync, you'll be prompted for:
| Prompt | What It Does | Default |
|---|---|---|
| MEMORIES_REPO_URL | Clone URL for the shared memories repo, e.g. git@github.com:org/memories.git |
(required) |
| MEMORIES_BRANCH | Branch that holds the memory files and this pack | main |
| MEMORIES_AUTOPUSH_MODE | Stop-hook behavior — auto (writes auto-pushed, deletions parked), full (writes + deletions auto-pushed), or review (nothing auto, per-turn report). See Auto-Push Modes. |
auto |
Install per-project, not globally. Run
mcs syncfrom each project's root (the directory that contains.claude/, not.claude/itself) — do not install this pack into your user-level~/.claude/directory.The hooks anchor on their own on-disk location and operate on a sibling
.memories-repo/working tree. A single global install would collapse every project onto one shared checkout, which breaks two things:
- Concurrent sessions race on the same working tree. If you run Claude in two projects at the same time, both Stop hooks try to stage, commit, rebase, and push against the same
.gitindex — you'll see interleaved commits, half-applied rebases, and dedupe state (reviewmode's.review-shown) stomped between sessions.- Project context bleeds across sessions. Memories Claude writes while working on project A would be auto-pushed under project B's session if B happens to end its turn first.
Per-project installs keep one independent clone per repo while still sharing the same remote, so the team-wide KB stays unified without the local race conditions.
shared-memories/
├── techpack.yaml # Manifest — defines all components
├── commands/
│ └── approve-memories.md # Slash command for review-mode approval
├── config/
│ └── settings.json # Templated env block — ships MEMORIES_AUTOPUSH_MODE
├── hooks/
│ ├── memories_pull.sh # SessionStart: pull + stuck-state warning
│ ├── memories_autopush.sh # Stop: auto-commit + push (async)
│ └── memories_announce.sh # PostToolUse: review-mode nudge to Claude (sync)
└── scripts/
├── configure-memories.sh # Sparse clone + symlink + migration
├── doctor-memories.sh # Setup health check
└── doctor-memories-remote.sh # Remote-access health check
On engineer disks, the pack materializes as:
<project>/.claude/
├── .memories-repo/ # sparse clone of MEMORIES_BRANCH
│ ├── README.md, LICENSE, etc. # any root-level files your repo ships
│ └── memories/
│ ├── learning_*.md
│ └── decision_*.md
└── memories -> .memories-repo/memories # symlink Claude Code reads
The clone uses --sparse --filter=blob:none --single-branch so only the memories/ subtree plus any root-level files your repo ships (README, LICENSE) materialize on disk (~1 MB typical). Every hook git call is scoped with -- memories/ pathspec, so root-level files are visible but never touched by the auto-commit/auto-push machinery — your teammates can edit the memories repo's README without tripping the guardrail.
Engineers who already have .claude/memories/ populated (from mcs-cli/memory, Claude Code's native memory, or manual use) are handled automatically on first mcs sync:
- The existing directory is moved aside to
.claude/.memories-migration-<timestamp>/ - The sparse clone + symlink are set up as normal
- Files from the backup are imported into the new shared folder, with the shared version winning on any filename conflict (your local copy stays in the backup dir for manual review)
- Well-named migrated files are auto-committed and pushed so they immediately become team knowledge
- If no conflicts remain, the backup dir is cleaned up automatically
If any step fails partway, an ERR trap restores the original folder from the backup — you're never left with a broken setup and no memories.
If your org enforces PR + ticket + approval on the default branch of your memories repo, every Claude Stop auto-pushing to it would turn each memory into a PR. That kills adoption.
Workaround: set MEMORIES_BRANCH to a side branch (e.g., memories) that no ruleset targets. Auto-push goes there; the default branch stays untouched and ruleset-compliant.
Free-push doesn't mean unprotected. Apply a repo-level ruleset to your side branch:
| Rule | What it blocks | Why |
|---|---|---|
non_fast_forward |
git push --force |
Prevents history rewrite that could erase content between reflog expiries |
deletion |
git push origin :<branch> |
Prevents catastrophic branch wipeout |
Normal commits (including ones that delete files via memory-audit) are unaffected, so the audit + manual-push flow still works.
The Stop hook's behavior is set during mcs sync via the MEMORIES_AUTOPUSH_MODE prompt. The chosen value is written to .claude/settings.local.json's env block (per-user / project-local). To change modes later, re-run mcs sync and pick a different value, or edit .claude/settings.local.json directly.
| Mode | Adds / Modifications | Deletions | When to use |
|---|---|---|---|
auto (default) |
auto-pushed | parked for manual review | Safe default for most teams. The asymmetric trust matches how memories are typically created vs. removed. |
full |
auto-pushed | auto-pushed | You trust your workflow — memory-audit runs are deliberate, no fat-finger risk. Skips the Intentional Deletion Workflow below. |
review |
not pushed | not pushed | You want to inspect every change before it propagates to the team. The hook prints a per-file report each turn end. |
Unset, empty, and unrecognized values fall through to auto (with a one-line warning for unrecognized values), so existing installs see zero behavior change.
In review mode, the hook prints a report on each turn end:
Shared memories [review mode]: <N> pending file(s) in memories/ and <M> unpushed commit(s)
+ NEW <file:///…/memories/learning_foo.md>
"<first non-empty line preview>"
~ MOD <file:///…/memories/decision_bar.md> (+3 -1)
Diff: git -C .claude/.memories-repo diff -- memories/decision_bar.md
- DEL memories/learning_old.md (last modified 3 weeks ago)
Recover: git -C .claude/.memories-repo checkout HEAD -- memories/learning_old.md
Approve all: /approve-memories [optional commit reason]
Discard local changes: …
/approve-memories (shipped by this pack) stages everything under memories/, commits as review: <reason> (default: approved memories from <host> <date>), pulls --rebase --autostash, and pushes. It re-runs the Stop-hook filename guardrail, so a stray wip.md blocks the push. Both you and Claude invoke the same command — no copy-paste of multi-line git incantations. Also unblocks auto / full state stuck from a previous push failure.
The same pending set is reported once per session — repeated turns within the session stay silent so the report doesn't spam every prompt. SessionStart resets the dedupe so unresolved changes re-surface in the next session instead of being buried forever.
In review mode, Claude is also told about each memory write through a separate PostToolUse hook (memories_announce.sh), so it can proactively mention pending review in the same turn and invoke /approve-memories when you confirm — without needing to wait for the terminal report. The terminal report and the in-conversation nudge are independent channels: the report goes to your terminal, the nudge goes to Claude's context. auto and full modes keep both channels silent.
Pull is always automatic regardless of mode — incoming team memories arrive at session start.
When you legitimately want to remove stale memories (typically after running the memory-audit skill from mcs-cli/memory), invoke the slash command:
/approve-memories audit cleanup
That stages the deletions, commits as review: audit cleanup, pulls --rebase, and pushes — same recipe the slash command uses for any other approval, with the filename guardrail re-applied.
The deletion block in auto mode is deliberate friction: audit is rare enough (monthly-ish) that requiring explicit human confirmation is cheap insurance against catastrophic local-delete-then-auto-push accidents. The slash command is that confirmation step in one keystroke.
If your workflow makes the friction unnecessary, set MEMORIES_AUTOPUSH_MODE=full to skip the manual step entirely — memory-audit's deletions will then auto-push alongside any other writes.
mcs pack validate . # verify techpack.yaml + file refs
mcs doctor # after sync: verify setup + remote access
git -C .claude/.memories-repo/memories log -1 # confirm auto-push landedIf the Stop hook silently refuses to push, the naming guardrail is likely rejecting a file. Run:
git -C .claude/.memories-repo/memories status # dirty files
git -C .claude/.memories-repo/memories ls-files --others # untracked filesAnything not matching memories/(learning|decision)_*.md needs renaming.
If SessionStart warns about lingering state:
- In
auto/fullmode the previous push hit an auth/network issue (fix and wait for the next Stop, or run/approve-memoriesto retry immediately), or guardrail-rejected files are sitting dirty (rename them).mcs doctorwill tell you which. - In
reviewmode the warning is expected — it lists pending changes awaiting your decision. End a turn to see the per-file report, then/approve-memoriesto push.
- MCS — the configuration engine
- Creating Tech Packs — guide for building your own
- Tech Pack Schema — full YAML reference
- Claude Code hooks
- Git sparse-checkout
- Git partial clone
MIT