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 |
| | | + deletion |
| v v guardrails 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 uncommitted/unpushed state left behind by a previous failed auto-push |
| Stop Hook | Auto-commits and pushes new/modified memory files after each Claude turn | Runs async; applies two guardrails (filename pattern + deletion block) before staging; rebases then pushes |
| 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 if a previous Stop left lingering state (auth failure, rebase conflict, guardrail-rejected files)
-
During work — Claude uses the
continuous-learningskill to write newlearning_*.md/decision_*.mdfiles -
Claude finishes a turn — the Stop hook collects dirty files, applies guardrails:
- Naming guardrail — any file failing
^memories/(learning|decision)_[a-zA-Z0-9_-]+\.md$halts everything until renamed - Deletion guardrail — deleted files are never auto-pushed (prevents accidental
rmwiping team knowledge), but coexisting additions/modifications still push - Staged files commit as
auto: memories from <host> <date>, rebase onto upstream, push
- Naming guardrail — 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 warning if previous state is stuck |
| memories_autopush.sh | Stop (async) |
Auto-commits and pushes dirty memory files; enforces filename pattern + deletion block |
| 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 | Git URL of the shared memories repo (SSH or HTTPS) | (required) |
| MEMORIES_BRANCH | Branch that holds the memory files and this pack | main |
shared-memories/
├── techpack.yaml # Manifest — defines all components
├── hooks/
│ ├── memories_pull.sh # SessionStart: pull + stuck-state warning
│ └── memories_autopush.sh # Stop: auto-commit + push (async)
└── 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.
When you legitimately want to remove stale memories (typically after running the memory-audit skill from mcs-cli/memory), do it manually:
git -C .claude/.memories-repo/memories commit -am "audit: remove stale memories"
git -C .claude/.memories-repo/memories pushThe deletion block 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.
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, either the previous push hit an auth/network issue (fix and wait for the next Stop), or guardrail-rejected files are sitting dirty (rename them). mcs doctor will tell you which.
- 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