What I use to make Claude Code remember things between sessions.
I'm Misha. Solo founder. I run my whole company on Claude Code - sales, ops, finance, taxes, kids' school stuff, all of it. Not just code.
The problem: Claude Code forgets everything between sessions. Every morning, fresh start, no memory.
So I built this. Pupsik is the workspace I drop into ~/Desktop/claude/. It gives Claude a contact DB, semantic search across my notes, all my Gmail accounts in one call, WhatsApp read access, and a set of rules that stop it from doing dumb things.
It works for me every day. Putting it on GitHub because someone else probably has the same problem.
MIT. macOS-friendly. Local. No telemetry, no cloud sync, no SaaS dashboard.
git clone https://github.com/mishalyalin/pupsik.git
cd pupsik
bash install.sh # creates ~/Desktop/claude/, installs tools + rules + hooks
bash install_mcps.sh # builds the local Gmail / Calendar / WhatsApp MCPs
bash register_mcps.sh # tells Claude Code about themThen open a fresh Claude Code session in ~/Desktop/claude/ and ask:
> What's your 2-agent rule?
If Claude paraphrases it back, you're done. If it shrugs, the rules file didn't load - jump to Troubleshooting.
-
Contact graph DB (SQLite). Every person I deal with, their company, every interaction, every link between them. I run
contacts_db.py find "Steve"instead of digging through Gmail. There's a graph traversal too - "how do I get introduced to person X" returns a chain. -
Semantic search across 9 ChromaDB collections. My notes, briefings, journal, decisions, learnings, research, plus the contact DB. One query, all of it.
memory_search.py search "что было с Vendor-A в апреле"and it pulls the relevant chunks. -
Capture knowledge the second it happens.
note.py learning "Title" "body"writes a learning note and reindexes it in 50ms. Re-run the same title later and it upserts - one note per topic, kept current. Thedecision,research,world_knowledge, anduser_contextvariants do the same. World knowledge (VAT rates, regulatory limits, industry conventions) and user context (working style, schedule, environmental constraints) live as their own ChromaDB sub-collections, separate from prescriptivefeedback_*.mdrules. Cherry-picked from obra/private-journal-mcp. -
Multi-account Gmail / Calendar / WhatsApp MCPs. I have 3 Gmail accounts.
gmail_search_allsearches all of them in one call. Same for Calendar. WhatsApp is read-only on macOS but it pulls into the contact DB. -
tools/doctor.pyhealth-check + safe auto-fix. 13 deterministic checks across the workspace. Broken symlinks, stale lock files, ChromaDB orphan rows, oversized CLAUDE.md, dangling memory pointers.checkis read-only;fix-safeonly does safe repairs (never rewrites my prose);orphanslists unlinked entities for me to review. Cron-safe. -
Friction protocol.
note.py friction --severity blocker --phase X --message Ycaptures the moments when something's wrong but I don't have time to fix it now. Re-run the same phase + severity and the counter increments. After 3 hits, my morning briefing surfaces it loud. -
Optional contact-enrichment cron, 4 passes. Gmail signature mining for LinkedIn / Twitter / GitHub / website / phone. Then web search for missing LinkedIn URLs. Then a short bio + Instagram. Then Pass 4: it reads my email and WhatsApp correspondence with the contact and writes a 2-4 sentence private summary into
relationship_context. That field never leaves my local DB - not in any export, not in briefings (briefings reformulate, never quote), not in this repo. Telegram is never auto-read; if I want TG context for a specific contact, I paste the history into an ad-hoc prompt manually. Runs Sunday 06:00 if I enable it. -
Auto-compact hooks + 50% threshold.
PreCompactsaves session state to disk before Claude compacts.PostCompactreminds it to restore.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=50fires compaction at half-context instead of waiting until 95% and losing the plot. -
2-agent rule. Every real task spawns a Worker plus an independent Checker. Catches the bugs a single-agent pass misses. I learned this the hard way; it's now non-negotiable for anything I'd actually ship.
-
Architect proposals backlog. Every time the morning briefing's Architect Lens spots a structural issue, the proposal gets written to
memory/architect_proposals/latest.mdwith a status (open/applied/rejected/ etc). Small same-turn fixes apply immediately and log asapplied; bigger things wait for my sign-off asopenand the brief surfaces the top-3 each morning. Rejected proposals get a 90-day re-propose suppression so I stop seeing the same noise. Local-only; never goes in any public export. -
Date-aware session anchor.
tools/now.pyis the single source of truth for current datetime + IANA timezone, and a SessionStart hook injects⏰ NOW: YYYY-MM-DD (Weekday), HH:MM TZinto the top of every session. Kills the failure mode where Claude pattern-matches a stale date from prior context and thinks today is six months ago. -
Connection-aware memory graph.
tools/note_graph.pybuilds entity-mention edges across all my notes and surfaces 5-10 tight thematic clusters from the last 7 days.memory_search.py wake-upnow includes an "Active clusters" block so I see what's currently hot without asking. -
Rule retrieval on demand.
tools/rules.py search "<topic>"returns the FULL content of feedback rules that match - so when Claude is about to draft an outbound email or answer a status question, it pulls the actual verification protocols, not just the one-line pointer incritical-rules.md. Merges an optional alias manifest with semantic search; falls back gracefully if no manifest is present. -
Brand OS opt-in for customer-comms rules (API-first, local-CLI fallback).
tools/brand_os.pyis a thin bridge to an optional Brand OS - a versioned repo you maintain on your own GitHub that holds your brand voice, positioning canon, persuasion tactics (BE + Voss/NSTD + Cialdini-Sutherland), anti-patterns, evidence library, and a retrieval surface (Python CLI, HTTP API, or both). If you have one configured, the marketing-panel + outbound-email rules pull canon from it before drafting. If you don't, they fall back to the inline 21-tactic playbook + 3-lens panel spec shipped with the toolkit. Detection picks the best available mode:- API mode (preferred) - hit one server-side canon copy over HTTPS so every session (yours, your designer's, your social-media marketer's, every Claude session) shares the same canon. No
git clonedrift. Configured via~/.brand-os-credentials(mode 600, gitignored - see.brand-os-credentials.examplefor the format) or env varsBRAND_OS_API_URL+BRAND_OS_API_USER+BRAND_OS_API_PASS. Falls back to local CLI on network failure. - Local CLI mode (fallback) -
git cloneof the Brand OS repo on each user's machine; helper invokes the local Python CLI via subprocess. Detection chain:BRAND_OS_PATHenv var >~/.brand-ossymlink > auto-detect under~/Desktop/claude/projects/*-brand-os. - Not configured - silent + safe; the customer-comms rules fall back to inline canon.
A typical Brand OS is structured as a multi-layer canon (positioning anchors / persuasion-cocktail recipes / canon principles drawn from Behavioral Economics + Voss/NSTD + Cialdini-Sutherland + LLM SEO / a Vault of evidence rows tying each principle to a primary source). Its retrieval surface is whatever you build - a Python CLI works, and a small Flask wrapper that exposes the same retrieval as
/api/*JSON endpoints (e.g./api/icp,/api/search,/api/explain,/api/tactic/<name>,/api/for-vector/<key>,/api/for-stage/<name>,/api/canon,/api/list-tactics,/api/list-stages,/api/stats) is the patterntools/brand_os.pytargets in API mode. The value of a Brand OS: one URL to your designer, social-media marketer, copywriter, and any future Claude session - same brand tone, same banned words, same persuasion-cocktail recipes everywhere. Keep your Brand OS repo PRIVATE - the canon is your competitive advantage; only the bridge helper here is public. - API mode (preferred) - hit one server-side canon copy over HTTPS so every session (yours, your designer's, your social-media marketer's, every Claude session) shares the same canon. No
-
~/.claude/rules/critical-rules.mdauto-loads every session. This is where the MANDATORY rules live - the FIRST bullet is now "NEVER IMAGINE, ALWAYS VERIFY" (the parent of every verify-* rule), then contact DB before mentioning a person, never use em-dashes in my voice, never write Excel files (I don't use Office), all 3 Gmail accounts always, etc. -
27 generic feedback rules in
memory_templates/feedback_*.md. Each one is a thing I corrected Claude on enough times to make it permanent. Not opinion-shaped advice - corrected behaviour pinned to disk. -
5 agent role prompts (Architect, Discoverer, Packager, Migrator, Tester). I use them when a task warrants a team, not a solo run.
-
Third-party attribution discipline.
THIRD_PARTY_ATTRIBUTIONS.mdat the repo root tracks every pattern I borrowed from external OSS (currently: gbrain by Garry Tan, MIT). Source URL, author, license, what I took verbatim vs adapted vs added. -
autopermission mode by default. Accepts safe ops, prompts on writes / shell / risky calls. ReplacesbypassPermissionsas the recommendation. Less friction than full bypass, less risk of nuking things. -
Structural enforcement against the leak / drift classes that bite repeatedly.
.github/scripts/privacy-check.shPass 11 catches<author-handle>/<private-repo-suffix>combinations even in byline-allowlisted files like README and CHANGELOG (the leak class that byline allowlist misses by design).scripts/brand-os-visual-gate.shbyte-diffsdashboard/favicon.svgand themask-icon/theme-colorhexes indashboard/build.pyagainst the locked Brand OS visual spec at build time, then aborts the VPS push when drift is detected..githooks/pre-commitruns the privacy scan locally before the commit SHA is even minted (opt-in viabash scripts/install-git-hooks.sh, emergency bypass viaPUPSIK_SKIP_PRIVACY_CHECK=1 git commit). All three are opt-in by configuration: forks without the relevant private-patterns / Brand OS clone / hook install see no change.
HOW_IT_WORKS.md- the concept walkthrough. Start here if you want to understand the architecture before installing.UPGRADING.md- if you're already on an older version. What gets preserved, what gets replaced, how to roll back.MODULAR.md- if you have your own Claude Code setup and want individual pieces. Each component, its dependencies, install snippet.CONTRIBUTING.md- PR rules. Short version: no personal data, ever.THIRD_PARTY_ATTRIBUTIONS.md- what I borrowed from where.CHANGELOG.md- release notes.
- Node.js 18+ (
brew install nodeon macOS) - for the MCP servers. - Python 3.10+ (
brew install pythonon macOS) - for the tools. - Claude Code CLI on PATH - get it from claude.com/claude-code.
pip install chromadb- the installer handles this.
Optional:
- WhatsApp for Mac - only if you want the WhatsApp MCP. Skip it otherwise.
If you'd rather watch Claude do the install with diffs at every step:
- Open a fresh Claude Code session in any directory.
- Paste the contents of
SETUP_PROMPT.mdinto the chat. - Claude spawns 5 agents (architect, discoverer, packager, migrator, tester) and walks through it step by step, asking for approval before writing anything.
Slower than bash install.sh. More transparent. Pick whichever you prefer.
From inside your clone:
bash tools/update.shWhat it does:
git fetch origin/main. If you're up to date, exits silently.- Shows the new commits and the file diff before touching anything.
- Refuses to run if you have uncommitted local edits (pass
--forceto stash, update, restore). - Fast-forward only - never rewrites your local commits.
- Re-runs
bash install.sh --update-onlyto apply new tools / hooks / rules / feedback templates.
tools/{contacts_db,memory_search,note,doctor,enrichment_schema_migrate}.py(smart-merge - see below)~/.claude/rules/critical-rules.md(append-only smart merge)~/Desktop/claude/.claude/hooks/{pre,post}-compact.sh(smart-merge)memory_templates/feedback_*.md(smart-merge in your project memory directory)
- Your
CLAUDE.md - Your
data/contacts.db memory/learnings/,memory/decisions/,memory/journal/,memory/people/,memory/projects/briefings/,outputs/,research/- Any feedback rule you've personalised in your project memory directory
- Scheduled-task templates (those are opt-in - see UPGRADING.md for the install command)
In short: your data is safe. Only the tooling layer gets replaced.
update.sh is conservative. It never silently overwrites your edits.
For each managed file:
-
Identical to upstream - nothing happens.
-
You haven't modified it since last install - safely updated, your old copy backed up as
<file>.bak.<timestamp>. -
You modified it - the new upstream version drops side-by-side as
<file>.new. Your version stays untouched. Diff and merge:diff ~/Desktop/claude/tools/memory_search.py{,.new} # ...resolve, then either rm the .new file or replace the original
After the update, update.sh prints which .new files are waiting on you.
~/.claude/rules/critical-rules.md is special - it's never replaced. New rule references from the upstream template get appended at the bottom under a ## Updates from upstream <date> header. Your existing content stays put, including any rules you wrote yourself.
# Every Monday at 09:00 local. Adjust the path to wherever you cloned.
0 9 * * 1 cd ~/pupsik && bash tools/update.sh >> ~/pupsik/.update.log 2>&1
Every push to mishalyalin/pupsik runs the Privacy Check workflow - a multi-pattern grep that fails the build if anything privacy-sensitive sneaks into the diff (real names, real emails, phone numbers, IDs, project codenames, API tokens, oversize blobs). The script lives at .github/scripts/privacy-check.sh and runs locally too.
If you fork it, the same workflow runs on your fork.
The template at ~/Desktop/claude/CLAUDE.md has {{PLACEHOLDERS}}. Fill in your name, role, what you're working on. This is the file Claude reads first every session - it's the "I am Misha and I'm working on these projects" doc, in your version.
python3 ~/Desktop/claude/tools/contacts_db.py init
python3 ~/Desktop/claude/tools/contacts_db.py add "Alice Smith" \
--email alice@example.com --company "Acme Corp" --category "work"Or pull from WhatsApp once the MCP is up:
Ask Claude: "Run whatsapp_sync_to_contacts_db"
Follow docs/GOOGLE_CLOUD_SETUP.md. About 15 minutes, one-time.
Give Claude a real task (not a one-shot lookup) and watch it spawn multiple agents. See docs/AGENT_TEAM_RULE.md.
claudecommand not found - install the Claude Code CLI from claude.com/claude-code.- MCP servers fail to build - check
node --version(need 18+). Runnpm installin eachmcp-servers/*/dir manually to see the error. - WhatsApp MCP says "permission denied" - your terminal needs Full Disk Access. See
docs/WHATSAPP_SETUP.mdStep 2. - Gmail auth fails with "access blocked" - you didn't add yourself as a test user in the Google Cloud consent screen. See
docs/GOOGLE_CLOUD_SETUP.mdStep 3.7. - Compact hooks don't fire - check
~/.claude/settings.jsonis valid JSON and paths are absolute. Seedocs/COMPACT_SETUP.md. - Claude doesn't mention the 2-agent rule - confirm
~/Desktop/claude/CLAUDE.mdhas the rule section andmemory/feedback_always_two_agents.mdis in your project memory directory.
rm -rf ~/Desktop/claude/.claude/hooks ~/Desktop/claude/mcp-servers ~/code/mcp-servers
rm ~/Desktop/claude/tools/contacts_db.py ~/Desktop/claude/tools/memory_search.py
claude mcp remove multi-gmail
claude mcp remove multi-gcal
claude mcp remove whatsapp
# If you want to nuke everything:
rm ~/Desktop/claude/CLAUDE.md ~/Desktop/claude/data/contacts.dbThe Google Cloud project, OAuth credentials, and installed npm packages aren't touched - kill those manually if you want a clean slate.
PRs are welcome. The bar: changes should make sense to a fresh user who has never met any of the contributors. No personal data, ever. See CONTRIBUTING.md.
Full release notes in CHANGELOG.md.
- Pass 4 of the contact-enrichment cron - reads my email and WhatsApp correspondence with each contact and synthesizes a 2-4 sentence private
relationship_contextsummary. The field never leaves the local DB. Telegram is never auto-read - manual paste only, per the upstream rule. - Schema migration: 10 -> 11 columns.
enrichment_schema_migrate.pynow addsrelationship_contexton top of the original 10. Re-runs are safe. - Upgrade discipline closed.
tools/update.shandinstall.shnow smart-merge all 5 tools instead of just the original 3. Existing pupsik installs runningupdate.shwill pick updoctor.pyandenrichment_schema_migrate.pyautomatically. - UPGRADING.md rewritten. Per-release format with one-time steps + verification checks for each release back to Phase 2. Backfill recipe included for users who ran the 2026-05-08 cron before this update.
tools/doctor.py- 13 deterministic health checks acrosscheck/fix-safe/orphans. Cron-safe, never rewrites prose. Adapted from gbrain (Garry Tan, MIT).note.py frictionsubcommand - severity-tagged friction events (blocker/error/confused/nit). Counter-incremented on repeat.friction summary --days 7 --top 3for morning briefings. Adapted from gbrain.- Output Rules adapted from gbrain - 4 cross-cutting quality rules (Deterministic Links, No Slop, Exact Phrasing Preservation, Title Quality) attributed in
THIRD_PARTY_ATTRIBUTIONS.md. THIRD_PARTY_ATTRIBUTIONS.mdat repo root - central tracker for everything I borrowed.- Privacy hardening. 5 feedback templates re-generalised after the privacy-check pattern catalogue tightened.
bash .github/scripts/privacy-check.sh --include-untrackednow passes 10/10. - Em-dash style consistency. Mass sed-pass cleaned 90 em-dashes lurking in template prose and tooling docstrings.
feedback_short_dashes_only.mdis the rule.
- 9-collection ChromaDB indexer.
memory_search.pynow indexes 9 collections (briefings, outputs, journal, knowledge, research added on top of contacts, interactions, memory_files, chat_archives). Theknowledgecollection holds bothlearnings/anddecisions/in one searchable index. tools/note.py- capture a learning, decision, or research note in one command. Upserts by title - one note per topic, kept current.feedback_capture_knowledge.md- new MANDATORY rule. Tells Claude to callnote.pythe moment an insight surfaces, not at the end of the topic.- Idempotent reindex.
coll.upserteverywhere. Re-runningmemory_search.py indexis safe and cheap. - Surgical single-file reindex.
memory_search.py index --file <path>reindexes one file in ~50ms instead of rebuilding the whole thing.note.pyuses this automatically. - Concurrency-safe lockfile with stale TTL. Parallel reindex calls don't deadlock; stale locks self-recover.
- Diff-based stale-chunk pruning. When a file shrinks, old chunks come out of the index instead of lingering and polluting search.
For users on a previous version, see UPGRADING.md for the migration path.
I'm a solo founder running an early-stage company end to end through Claude Code. This toolkit is what makes that practical. I built it for me. I use it every day. Putting it on GitHub because someone else with the same setup probably wants the same fixes.
If you find it useful, a star helps others discover it. ⭐
MIT. See LICENSE.
The bundled MCP servers carry their own licenses (each mcp-servers/*/LICENSE where present - multi-gmail is MIT). Everything else here is MIT.