projectmem
Memory framework
Created by Ripon Chandra Malo
Published in riponcm/projectmem. Publisher page
880 GitHub stars
What it is
Open-source coding agents memory. Records issues, attempts, fixes and decisions, then warns your agent before it repeats an approach that already failed. Native MCP server for Claude Code, Cursor, Antigravity and Codex. 100% local, no cloud, no telemetry. MIT.
AutoGineer overview, from the brain’s own files
projectmem is an open-source memory layer designed to provide AI coding agents with persistent, project-specific context. By recording issues, attempts, fixes, and decisions in a local .projectmem/ directory, it enables agents to learn from past sessions rather than starting from zero. It functions as a native MCP server, allowing tools like Claude Code, Cursor, and others to read memory and log work autonomously. Its signature feature is a pre-commit warning system that alerts the agent before it attempts a strategy that has already failed in the project's history, effectively reducing token waste and preventing recurring dead ends.
How it works
Memory model: Not stated
AutoGineer overview, from the brain’s own files
The system installs git hooks and a file watcher within your repository. It captures development events—such as issues, attempts, and decisions—and stores them as typed events in a local .projectmem/ folder. When an AI agent is active, it connects via an MCP server to query this memory. Before performing tasks, the agent can use the precheck tool to verify if a proposed approach has previously failed, ensuring it avoids repeating past mistakes.
| Folder | Role | Files |
|---|---|---|
| .github | agent-config | 1 |
| src | source-code | 43 |
| tests | tests | 27 |
| brand | folder | 12 |
The agent reads
- get_context evidence: README.md
The agent writes
- .gitignore evidence: README.md
Requirements
Runtimes: Python >=3.10
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Google Antigravity, Kiro, OpenCode, Amp, Roo Code, Cline, JetBrains Junie, Devin (ex-Windsurf)
Set it up
This brain runs code automatically: 1 script. Review src/projectmem/commands/init.py before opening the folder in Claude Code. No step below turns a hook on for you.
src/projectmem/commands/init.py:python src/projectmem/commands/init.py
- Not scanned by AutoGineerIts setup steps could not be read from the brain’s own files. https://github.com/riponcm/projectmem/tree/a405361b8d12cb773bb42ef0f64004623dbee764
- Not scanned by AutoGineerIts dependencies are installed by pip or uv when you run it and were not scanned by AutoGineer (3 declared). https://pypi.org/project/projectmem/
Created by Ripon Chandra Malo. Published in riponcm/projectmem. Licence: MIT License.
Usage
AutoGineer overview, from the brain’s own files
- Install the package via pip.
- Initialize memory in your project repository using the pjm init command.
- Configure your AI agent's MCP settings to point to the projectmem server.
- The agent automatically reads and writes to the memory layer during coding sessions.
- Use pjm doctor to verify the setup and project registration.
- Visualize project history and ROI using the pjm visualize command.
- “Fix the authentication bug in the login module.”
- The agent queries projectmem, identifies that a previous attempt to modify the auth controller failed, and tries a different approach instead.
- “What were the main decisions made regarding the database schema last week?”
- The agent retrieves the logged decisions from the project memory and summarizes the rationale behind the current schema structure.
Documentation by Ripon Chandra Malo
<!-- mcp-name: io.github.riponcm/projectmem -->
<div align="center"> <img src="https://raw.githubusercontent.com/projectmem/projectmemdoc/main/logo/projectmem-wordmark-800.png" alt="projectmem" width="420" />
<p> <a href="https://github.com/riponcm/projectmem/blob/main/CHANGELOG.md"><b>🎉 v0.3.5 is out</b></a> — precheck recognises a file however its path is spelled, and the summary stays small even when entries are long. <a href="https://github.com/riponcm/projectmem/blob/main/CHANGELOG.md">See what changed →</a> </p>
<p><b>We don't make AI smarter. We make it experienced.</b></p> <p><i><b>Coding agent memory</b> — the local-first memory + judgment layer for AI coding agents. One MCP server for every project. Stop re-reading your project every session. Stop repeating yesterday's bug.</i></p>
<p> <a href="https://pypi.org/project/projectmem/"><img src="https://img.shields.io/pypi/v/projectmem.svg?color=4c1d95&label=pypi" alt="PyPI version"></a> <a href="https://pypi.org/project/projectmem/"><img src="https://img.shields.io/pypi/pyversions/projectmem.svg?color=3b82f6" alt="Python Versions"></a> <a href="https://pepy.tech/project/projectmem"><img src="https://static.pepy.tech/personalized-badge/projectmem?period=month&units=abbreviation&left_color=grey&right_color=brightgreen&left_text=downloads%2Fmonth" alt="PyPI downloads per month"></a> <a href="https://github.com/riponcm/projectmem/stargazers"><img src="https://img.shields.io/github/stars/riponcm/projectmem?style=flat&color=f59e0b&label=stars" alt="GitHub stars"></a> <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-3b82f6.svg" alt="License: MIT"></a> <a href="https://arxiv.org/abs/2606.12329"><img src="https://img.shields.io/badge/arXiv-2606.12329-b31b1b.svg" alt="arXiv paper"></a> </p>
<p> <a href="https://ossdrop.com/tool/projectmem"><img src="https://ossdrop.com/badge/projectmem" width="250" height="56" alt="projectmem on OSSDrop" /></a> </p>
<p> <a href="https://projectmem.dev"><b>Website</b></a> • <a href="https://projectmem.dev/guide"><b>Guide</b></a> • <a href="https://projectmem.dev/demo"><b>Demo</b></a> • <a href="https://projectmem.dev/changelog"><b>Changelog</b></a> • <a href="https://arxiv.org/abs/2606.12329"><b>Paper</b></a> </p>
<br />
https://github.com/user-attachments/assets/e055988e-7253-4ba9-92fc-95d3d4bcd763
<br /><br />
<img src="https://raw.githubusercontent.com/projectmem/projectmemdoc/main/screenshots/dashboard-overview-0.3.0.png" alt="projectmem dashboard — coding agent memory for one project: memory card, failure heatmap, ROI and case files" width="860" /> <p><sub><code>pjm visualize</code> — every case your project solved, what failed on the way, and what it saved. Generated locally from <code>.projectmem/events.jsonl</code>.</sub></p> </div>
🚀 Start here — five minutes, once
*Five minutes if you follow along here. Want to be shown instead — every command, the exact output it prints back, and the dashboards at the end? Take the complete setup guide.*
New to projectmem, or upgrading from 0.1.x / 0.2.x? Since 0.3.0 one MCP server serves every project, so this is the last time you configure anything.
1. Install or update
pip install -U projectmem2. Find the projects you already have
pjm doctorIt looks where code lives —
~/Developer,~/code,~/projects, your cloud folders, and every drive on Windows — and lists projects with memory that aren't registered yet. Anything it missed, add by hand:pjm project register "/Users/you/Developer/repos/ossdrop"3. Register them
pjm doctor --fix4. Point your AI at all of them with one config
"mcpServers": { "projectmem": { "command": "/absolute/path/to/python", "args": ["-m", "projectmem.mcp_server"] } }No
--root, nocwd— that's what makes it serve everything. Per-client instructions (Claude Desktop, Claude Code, Cursor, Antigravity, Codex) are in MCP Integration;pjm initprints this block with your own Python path filled in. Then fully restart the client — MCP servers only load on a cold start.5. Check your work
pjm doctorAdd
--onlineif you also want it to tell you when a newer projectmem is out — projectmem makes no network calls otherwise, and--autoturns that into a once-a-day check if you prefer.Run it again after editing the config. It flags any client still pinned to a single repo — the most common reason a new project is invisible to your agent.
All green? You're done. From here on it is one command per repo:
pjm initYour agent reads what the project already learned instead of rediscovering it, and writes down what it finds. Fewer tokens, no repeated dead ends, memory that outlives the session.
What is coding agent memory?
Coding agent memory is a persistent record of what happened while building a project — the issues hit, the approaches attempted, the fixes that worked and the decisions made — stored so an AI coding agent can read it at the start of a new session. Without it every session begins from zero.
projectmem is an open-source agent memory layer built for that job. It is
local-first: memory lives in a plain .projectmem/ directory inside your
repository, with no cloud, no account and no telemetry — the only network call
it can make is an update check you turn on yourself. A native MCP server
exposes 17 tools to Claude Code, Claude Desktop, Cursor, Antigravity and Codex,
so your agent reads memory and logs its work on its own.
Unlike chat-history memory tools, projectmem stores typed events — issues, attempts, fixes, decisions, notes — which is what makes its signature feature possible: a pre-commit warning that fires before you repeat an approach that already failed in this project.
pip install projectmem
cd your-project && pjm init🎬 Watch the demo
<p align="center"> <a href="https://youtu.be/pELGdXHj_Ls"> <img src="https://img.youtube.com/vi/pELGdXHj_Ls/maxresdefault.jpg" alt="projectmem — 60-second demo" width="720" /> </a> <br /> <em>Full screen-recorded tutorial- watch on YouTube</em> </p>
📚 Docs
| Doc | What's in it |
|---|---|
| Complete setup guide | The full walkthrough on the web — install, MCP setup per client, pjm doctor, your first logged issue, and both dashboards. Every terminal output is captured from a real run. |
| TUTORIAL.md | 15-minute step-by-step walkthrough — set up projectmem on your own project, watch the lifecycle, see the pre-commit warning fire. |
| CHANGELOG.md | Release history. Latest: v0.3.5 — precheck matches every spelling of a path, and long entries no longer bloat the summary. |
| Research paper (arXiv:2606.12329) | PROJECTMEM: A Local-First, Event-Sourced Memory and Judgment Layer for AI Coding Agents — the peer-readable version: design, Memory-as-Governance framing, capability comparison, and the 207-event dogfooding study. |
| LICENSE | MIT |
The Problem
Every new AI session starts from zero. Claude, Cursor, Aider — they all forget yesterday's decisions, repeat failed debugging attempts, and burn millions of tokens reconstructing context from raw source files.
The model isn't the problem. The architecture is. Stateless models need a memory cortex.
The Solution
projectmem is the local-first memory + judgment layer that sits above your AI tools. It captures every failed attempt, decision, and gotcha — then injects that experience back into future AI sessions. Git tracks what changed. projectmem tracks why it changed, what was tried, and what failed.
Install
First time here? → The complete setup guide walks the whole path end to end: install, connecting Claude Desktop, Claude Code, Cursor, Codex or Antigravity, checking it with
pjm doctor, and reading your memory back through the dashboards — with the real terminal output at every step.
Three commands to a project that remembers:
pip install projectmem
cd your-project
pjm initThat's it. pjm init installs three git hooks (pre-commit warnings, post-commit classification, post-merge tracking), auto-starts a real-time file watcher, inherits cross-project memory if available, and creates .projectmem/. Capture is active from minute one.
The canonical command is
projectmem. Apjmalias is installed for speed.
✨ New in 0.3.5 — warnings for every spelling, and a summary that stays small
Precheck recognises a file however its path is spelled. Events store
project-relative paths, so asking about ./src/auth.py, an absolute path, a
path through a symlinked folder or a Windows C:\ path matched nothing, and
precheck called a file with known failures "Safe to modify". Every spelling now
gets the same warnings, and checking is faster on large logs.
get_issue("1") and get_issue("#1") find issue 0001.
(#26)
Long entries no longer bloat the summary. 0.3.4 capped how many entries the summary shows, not how long each one is. Records are now clipped at 300 characters, and a fixed issue takes one line. On a real 238-event project the summary went from 8,974 tokens to 3,458. (#29, #30)
Upgrading? Run pjm init once in each project. The instructions now ask
agents for shorter records, and pjm init refreshes AI_INSTRUCTIONS.md,
keeping your previous copy as AI_INSTRUCTIONS.md.bak.
The #26 fixes were contributed by @vishalhabib99, and #29 was reported and fixed by @bigfree.
✨ New in 0.3.4 — memory that stays small, and warnings you can trust
The summary no longer grows forever. Notes were capped, but Recent issues and Decisions listed everything ever logged, so on an aged project reading the memory cost more than reading the code. Every open issue is still shown — the newest 20 in full, older ones by id — while fixed issues are capped at the newest 10 and decisions at the newest 15, each with a line saying what was left out and where to find it. On a test project with 25 issues and 60 decisions the summary went from about 2,900 tokens to about 1,100. (#19)
Commits are filed under the code they change. Auto-capture used the first
changed path in git's sort order, which was usually the regenerated
.projectmem/summary.md — so valid memories were flagged stale and the real
file lost its history. The location is now the source file the commit is about,
with deletions, renames, merges and a repository's first commit handled.
(#20)
Stale warnings no longer depend on your time zone. A 0.3.3 speed-up compared
git's local-time commit dates with UTC event times as text: west of UTC, recent
stale memories were never flagged; east of UTC, fresh ones were flagged at once.
Both sides are compared in UTC now, and events already filed under
.projectmem/ stop warning without your log being rewritten.
The guidance names the tools' real arguments. It gave precheck_file and
get_issue the wrong argument names (path and id, where the tools take
file_path and issue_id) and showed search_events without its required
query, so an agent copying the call got a validation error. A test
now checks every call shown to an agent against the tool's actual signature.
(#18)
Upgrading? Run pjm init once in each project. Older projects kept the
AI_INSTRUCTIONS.md and git hooks of the release that created them, so fixes to
either never arrived. pjm init now refreshes both — your previous instructions
are kept as AI_INSTRUCTIONS.md.bak, and anything of yours outside
projectmem's block in a hook is left untouched.
Also: a mistyped issue id no longer breaks every later command, pjm precheck
stops warning about projectmem's own files, and Python 3.13 and 3.14 are
supported.
Fixes for #18 and #20 were contributed by Rohan Tiwari, and the first cap for #19 by hylin. #18 and #19 were reported by @medium-effort, #20 by @bigfree.
✨ New in 0.3.3 — precheck stops getting slower, and Windows works end to end
pjm precheck runs before every edit — the instructions tell agents to call it
first — so its cost is paid constantly. It was taking 26 seconds on a
1,200-event project, and getting worse every week, because it ran one
git log per event to answer a question about a single file. It now makes one
call per distinct file, bounded by the oldest event citing it.
| events | before | after |
|---|---|---|
| 100 | 2,210 ms | 48 ms |
| 400 | 8,905 ms | 51 ms |
| 1,500 | ~33 s, 1,501 git processes | 82 ms, 2 processes |
Latency is now flat rather than linear in project age. Results matched a reimplementation of the old algorithm, including across merge commits — except for a time-zone bug this change introduced, which 0.3.4 fixes.
A retired decision no longer resurfaces. supersedes has existed since
0.1.4, but the two surfaces an agent actually reads during work — get_context
and precheck_file — were not filtering it. You could retire a decision and
still be told about it. Both filter now, and AI_INSTRUCTIONS.md finally
documents how to retire one, which is why models never called it.
Your project is named in the bridge file, and AGENTS.md is written too.
One server serves every project, so a call naming none is refused rather than
guessed at — but nothing told the agent the name, so it learned it from the
error and retried, every session. The name is in the bridge now. It goes into
AGENTS.md as well as CLAUDE.md, because Antigravity and Codex never read
the latter.
Six Windows reports are closed. Git hooks shipped a bash shebang that
Git for Windows often cannot resolve — and an unresolvable shebang does not
skip the hook, it aborts your commit. The baked binary path lost its
backslashes to shell escaping. The venv fallback looked in bin/, which
cannot exist there. pjm watch --daemon could not be seen or stopped. And a
box-drawing character in the output killed pjm init outright on a cp1252
console — after it had already created everything, so the command both did its
work and reported failure.
Every one was fixed by running projectmem on the machines that reported it — Windows 11, and three MCP clients (Antigravity, Claude, and Codex) — not by reading the code. Reported by @medium-effort, who also contributed the 0.3.2 Windows daemon support.
✨ New in 0.3.2 — Windows, properly
pjm watch --daemon crashed on Windows with AttributeError: module 'os' has no attribute 'fork'. It now spawns a detached worker instead of forking, so
background watching works on every platform.
Fixing that uncovered a second bug hiding behind it. Liveness was checked with
os.kill(pid, 0) — a POSIX idiom that does not port, because on Windows
os.kill routes to TerminateProcess and signal 0 is not a check at all. The
watcher could not be seen or stopped there, and each pjm watch --daemon leaked
another process. Both are fixed.
Windows daemon support was contributed by @medium-effort (#13).
pjm doctor also got two things. It now tells you to quit your AI client
before editing its config — those files hold the app's own preferences too, so
a running client can rewrite the whole thing on exit and restore the --root
you just removed. And it remembers what it saw last time, so a config that was
clean and is pinned again gets named as a revert rather than looking like doctor
being flaky. Local files only; nothing leaves your machine.
✨ New in 0.3.1 — know when to upgrade
Both dashboards now show which version generated the page, with a check for
updates link beside it. The page makes no request until you click — PyPI's
public JSON is fetched straight from your browser and nothing about your machine
is sent. On the command line, pjm doctor --online checks once and
pjm doctor --auto remembers to check daily; both are off unless you ask.
✨ New in 0.3.0 — one server, many projects
Until now an MCP config was tied to one repository: eleven projects meant eleven
server entries and eleven restarts. 0.3.0 serves every registered project from
a single server. Paste the config once; every repo you pjm init afterwards is
reachable from it.
pjm project list # what this server can reach
pjm project use ossdrop # the default when a call names no projectlog_issue(summary="stars come back empty", project="ossdrop")
→ Logged issue #0019 → ossdrop: stars come back emptyEvery write names the project it landed in — in a shared server, the dangerous
failure is not "nothing works", it is a write that succeeds against the wrong
repo. Existing --root configs keep working untouched, and a pinned server now
refuses to write anywhere else even when asked.
Also in 0.3.0:
Fixed: the MCP server was broken on fresh installs. mcp 2.0 renamed
FastMCPand left the old import path raising — since 2026-07-28 every newpip install projectmemgot a server that died at import. Caught and fixed by @VIVAAN-DHAWAN.Security: stored XSS in
pjm visualize. Event summaries reached the DOM unescaped, and git commit messages become event summaries — so a crafted commit in a branch you pulled could run script in your dashboard. Every sink is escaped now.A rebuilt dashboard — a shareable Memory Card, case files with the full issue → attempt → fix chain, an effort treemap, per-file dossiers, and a global view that opens with where you left off.
Registry migration is automatic: the 0.2.x list of paths is converted on first
read, with a .bak kept beside it.
✨ New in 0.2.0 — the workspace release
0.1.6 made one project's memory something you could watch. 0.2.0 lifts that to your whole workspace — and closes the gap between what happened (memory) and what your code is (structure).
🌐 Global dashboard —
pjm dashboardis one page over every project you'vepjm init-ed: total issues captured, fixes confirmed, dead-ends prevented, tokens saved, a grade per project, and a "needs attention" list. Click any card to open that repo's own dashboard, generated fresh. It's a global view, not a global store — each repo's.projectmem/is aggregated at read time and never leaves its folder. Default is serverless (a static snapshot); add--servefor a tiny, ephemeral live server where the Refresh button re-reads your files — no background daemon, Ctrl+C stops it.🧬 Structure & relations —
pjm map --build(run automatically atpjm init) walks your codebase and, for Python, resolves imports into a real dependency graph. The Project Map's Graph and Flow views now render actual files and the import edges between them. The cache (structure.json) is derived from code, gitignored, and never committed — code is only ever read.🔥 Failure heat on structure (the combo) — the one view a pure code-grapher can't draw and a pure memory tool can't either: files with repeated failed attempts glow red, laid directly over the real import graph. Structure comes from the code, heat comes from your memory, and they meet only in the renderer.
🗂️
plan.md— a new editable intent file: ideas and plans, what you mean to do — deliberately not the event log.events.jsonl → summary.mdrecords what happened;plan.mdrecords what you intend. The AI reads it at session start and edits it directly; a plan never becomes an event.pjm plan/pjm plan "idea"/ MCPget_plan().
Everything stays 100% local — the global dashboard is a read-time aggregate, never a central honeypot of your code's history.
<p align="center"> <img src="https://raw.githubusercontent.com/riponcm/projectmem/main/brand/dashboard-global.png" alt="projectmem global dashboard — every project in one read-time view" width="880" /> <br /><em>Global Dashboard — every <code>pjm init</code>-ed project in one view: grades, issues, savings, and a "needs attention" list, aggregated at read time. Each card opens that repo's own dashboard.</em> </p>
The visualization suite (shipped in 0.1.6)
Your project's memory is also something you can watch — and share.
🎬 Showoff — a dashboard tab with three animated story scenes, all rendered from your real event log: Story Replay (watch your project's history build itself, node by node), Orbit (files orbit the project, events orbit their file), and Universe (your project as a rotating galaxy — every bright star is a real issue, attempt, fix, or decision; click one for its full details).
⏺ Built-in recorder — hit REC (10–60 s) and Showoff downloads a
.webmclip of the animation, rendered 100% locally with a "made with projectmem" badge. Your debugging story, ready for a tweet or a standup.🗺️ Flow — the Project Map's default view: a layered flowchart reading
PROJECT → DIRECTORIES → FILES → WHAT HAPPENED → MEMORY. Files with repeated failures glow red along their path, every file shows its outcome chips, and everything flows into theevents.jsonlcylinder. Tree and Graph views are one click away.🧵 Time Spine — the Timeline's default view: a real-time axis you scroll, with problems branching left (issues, failed attempts) and knowledge branching right (fixes, decisions, notes). Hover any card and its whole issue thread lights up. The classic list remains as "Details".
<p align="center"> <img src="https://raw.githubusercontent.com/riponcm/projectmem/main/brand/dashboard-showoff-universe.png" alt="Showoff — your project as a rotating galaxy, every star a real event" width="800" /> <br /><em>Showoff · Universe — every bright star is a real event from this project's memory</em> </p>
<p align="center"> <img src="https://raw.githubusercontent.com/riponcm/projectmem/main/brand/dashboard-projectmap-flow.png" alt="Flow — layered project map from project to memory" width="800" /> <br /><em>Project Map · Flow — what happened, file by file, flowing into append-only memory</em> </p>
<p align="center"> <img src="https://raw.githubusercontent.com/riponcm/projectmem/main/brand/dashboard-timeline-spine.png" alt="Time Spine — problems branch left, knowledge branches right" width="800" /> <br /><em>Timeline · Time Spine — problems on the left, knowledge on the right, real time down the middle</em> </p>
Why You'll Love It
Pre-Commit Warnings —
pjm precheckwarns you before you commit if you're about to repeat a failed approach, modify a high-churn file, or touch an unresolved issue. It works from this project's own recorded failures, which is why it needs the memory layer underneath. The warning now lists the dead ends themselves ("What already failed here: ✗ tried CSS contain:layout"), andpjm precheck --snooze 2hsilences it politely — the snooze is itself logged, so even the silence is audited.Stale-Memory Detection (new in 0.1.4) — projectmem never deletes a memory. Every decision that cites a file is cross-checked against that file's git history — when the file has moved on, the memory is flagged ("predates 7 commits to auth.py — confirm or supersede") and a human decides. Retire it cleanly with
pjm decision "new way" --supersedes <id>: the old event stays in the log, tagged, forever.Session-Start Briefing (new in 0.1.4) —
pjm briefanswers "where was I?" in one screen: active warnings, possibly-stale memories, open issues, recent decisions, stack gotchas, and your prevention score with a week-over-week delta.Memory for agents without MCP (new in 0.1.4) —
pjm export --claude-mdcompiles live decisions, gotchas, and a "Do NOT retry — these already failed" list into a marked block in CLAUDE.md (or.cursorrules). Copilot, plain Claude, any agent that reads the file inherits your project's judgment.Smart Context Injection —
pjm wrap claude(or cursor/aider) injects a token-budgeted memory block into your AI before the session opens. Your AI starts experienced, not blank.Estimated ROI Score —
pjm scoreoutputs a letter grade (A+ → F) with the tool's own estimates of debugging hours saved, tokens avoided, and dollars saved. CI-friendly JSON output and shields.io badge for your README.Cross-Project Memory — Lessons learned in one repo follow you forever. Library gotchas, decisions, and patterns live in
~/.projectmem/global/and are inherited by new projects. A project with a manifest (pyproject.toml,requirements.txt,package.json,Cargo.toml,go.mod) is offered only the lessons for its stack.Real-time File Watcher — Background daemon detects rapid edits to the same file (debugging sessions) between commits. Battery-aware, gitignore-aware, auto-started by
pjm init.Native MCP Server — Plugs into Claude Desktop, Cursor, Antigravity, Codex, and any MCP-compatible tool. 17 native tools force the AI to read context, check files for known failures, read your
plan.md, and log work automatically. Verified end-to-end in all four clients at first release; the 0.3.3 acceptance suite was re-run in Antigravity, Claude, and Codex.Interactive Dashboard (expanded in 0.1.6) —
pjm visualizeopens a six-tab local dashboard: Overview, Story Map (failure heatmap with collapse/focus controls), ROI Dashboard, Project Map (Flow / Tree / Graph, now over your real code structure), Timeline (Time Spine / Details), and Showoff — animated story scenes with a built-in video recorder.One MCP server for every project (new in 0.3.0) — configure your client once instead of once per repository. Calls name their project (
project="ossdrop"), or fall back to the active one; every write reports which repo it landed in, and a pinned--rootserver refuses to write outside its own. Existing single-project setups are untouched.Global Dashboard (new in 0.2.0) —
pjm dashboardis one cross-project view over every repo you'vepjm init-ed: grades, issues, savings, and per-project drill-in. A global view, never a global store — each repo's memory is aggregated at read time and never leaves its folder. Serverless by default;--servefor an ephemeral live server (Ctrl+C to stop).Code Structure + Judgment (new in 0.2.0) —
pjm map --buildreads your codebase into a real import graph, and the Project Map overlays failure heat from your event log on top: the files that keep breaking, glowing red over the structure that actually connects them. The structure cache is derived from code and gitignored — never committed.Intent, separate from memory (new in 0.2.0) —
plan.mdholds ideas and plans (what you mean to do), kept deliberately apart from the append-only event log (what happened).pjm plan, or the MCPget_plan(); the AI edits it directly and a plan never becomes an event.100% Local — No cloud, no telemetry, no accounts. Your code, your memory, your machine.
How It Compares
| Capability | projectmem | claude-mem | agentmemory | mem0 | Letta |
|---|---|---|---|---|---|
| Core focus | Memory + judgment | Session capture | Memory engine | Chat / user memory | Agent harness |
| Warns before a commit from a file's own failure history | ✅ | ❌ | ❌ | ❌ | ❌ |
| Checks memory for staleness against git history | ✅ | ❌ | 🟡 links sessions to commits; time decay | ❌ | ❌ |
| Failed attempts as typed events | ✅ worked / partial / failed | 🟡 bugfix type | 🟡 captures tool failures | ❌ | ❌ |
| Keeps superseded memories (no deletion) | ✅ | ? | 🟡 versioned, but decay can evict | ✅ add-only since v3 | ✅ git-versioned memory |
| Cross-project lessons | ✅ stack-scoped with a manifest | 🟡 opt-in flag | 🟡 cross-project search | 🟡 user-scoped | 🟡 per agent |
| Runs locally with no LLM API key | ✅ | 🟡 local store, LLM compression | ✅ keyless mode | ❌ self-host needs a key | 🟡 local mode |
| Native MCP server | ✅ 17 tools | ✅ | ✅ 54 tools | 🟡 hosted only | ❌ MCP client |
| Price · licence | Free · MIT | Free · Apache-2.0, paid Pro | Free · Apache-2.0 | Free tier · Apache-2.0, paid cloud | Free tier · Apache-2.0, paid cloud |
<sub>✅ yes · 🟡 partial · ❌ not found in the project's own docs · ? not documented. Snapshot taken 2026-10-02 from each project's README, docs and changelog; design capabilities, not benchmark results. Corrections welcome — open an issue.</sub>
<sub>There is no database and nothing you have to keep running: the MCP server is a stdio subprocess your AI client spawns, and everything else is plain files. The only server anywhere is the optional pjm dashboard --serve, an ephemeral local viewer you start and stop with Ctrl+C.</sub>
🚧 Upcoming
Import your existing memory —
pjm import(planned for 0.4.0) will migrate history from mem0, agentmemory, Letta, and Claude session logs into projectmem. It maps only to the core event vocabulary — issues, attempts, fixes, decisions, notes — so signal comes in and another tool's clutter stays out. Your judgment history moves with you.
Want a source supported? Open an issue and tell us what you're migrating from.
How AI Reads Your Memory (Token Efficiency)
The architecture is built around one rule: AI reads small, distilled files. Tools generate them from the big raw log.
Measured on 0.3.5 with the cl100k_base tokenizer, on nine real projects of
9 to 238 events. At session start an agent reads three files.
AI_INSTRUCTIONS.md is about 3,300 tokens and costs the same in every project.
summary.md ranged from about 400 to 3,500 tokens, and PROJECT_MAP.md from
about 100 to 900. A synthetic worst case with 40 open issues and long entries in
every record brings the summary to about 6,300. The baseline and pjm wrap rows
are estimates.
| Access mode | Tokens / session | How it works |
|---|---|---|
| No projectmem (baseline) | 5,000 – 20,000+ | AI re-reads source files every session |
| Universal Mode (markdown) | ~3,700 – 7,600 | AI reads 3 distilled files once |
| MCP Mode (recommended) | ~3,700 – 7,600 at session start | AI calls get_instructions(), get_summary() and get_project_map(), then get_issue(issue_id) or precheck_file(file_path) only when relevant |
pjm wrap (pre-injection) | 500 – 2,000 | Pre-generated, you set the budget |
AI never reads events.jsonl directly. That file is for tools (pjm score, pjm context, pjm wrap). Tools distill the raw log into compact AI-readable summaries.
One server, many projects
Since 0.3.0 a single MCP server serves every project you have registered. Paste
the config once and every repo you pjm init afterwards is reachable from it —
no second entry, no restart.
pjm project list # what this server can reach
pjm project use ossdrop # the default when a call names no project
pjm project alias ossdrop odYour agent picks the project per call:
log_issue(summary="stars come back empty", project="ossdrop")
→ Logged issue #0019 → ossdrop: stars come back emptyEvery write says where it landed. That echo is the point: in a one-project setup a misconfigured server simply fails, but a shared server can succeed against the wrong repository, which corrupts two audit trails at once. If the name in the reply is not the project you meant, stop.
How a call is routed, highest first:
| Source | Notes | |
|---|---|---|
| 1 | --root at startup | A boundary, not a default. A pinned server refuses to write elsewhere, even when asked. |
| 2 | project="…" on the call | id, alias or path. An unknown name is an error. |
| 3 | The client's workspace root | Planned for 0.4.0 — the resolver supports it, but MCP clients don't pass their roots to it yet. |
| 4 | The active project | pjm project use <name>. |
| 5 | The working directory | Walks up looking for .projectmem/, like git. |
| 6 | — | Refuses, and lists what is registered. It never guesses. |
Once client roots are wired in, they will outrank the active project on purpose: the root is where you are now, the active project is a mode you set days ago. When they disagree, the stale one is the wrong answer.
Single-repo setups are untouched — pjm init --mcp-config-single still prints
the pinned config, and an existing --root entry keeps working exactly as before.
MCP Integration (Recommended)
For: Claude Desktop, Cursor, Antigravity, Codex — and any tool with native MCP support. The MCP server forces the AI to read memory and log every action automatically.
Since 0.3.0 you configure this once, not once per repository. The block below has no --root: the server serves every project you have registered, and each call resolves its own. Paste it, and every repo you pjm init from then on is reachable — no second entry, no restart.
"mcpServers": {
"projectmem": {
"command": "/opt/anaconda3/bin/python",
"args": ["-m", "projectmem.mcp_server"]
}
}Upgrading with projects you already have? The registry only ever recorded
projects you ran pjm init on since it existed (0.2.0), so anything older is
missing — and global mode routes through the registry. One command sorts it out:
pjm doctor # what's unregistered, what's stale, what's still pinned
pjm doctor --fix # register what it foundIt looks in the places code actually lives — ~/Developer, ~/code, ~/src,
~/projects and friends, plus every fixed drive on Windows, where projects sit
on D:\ and E:\ as often as under your home folder. To point it somewhere
specific:
pjm doctor --path ~/work --path /Volumes/ssd --fix
pjm project scan D:\ E:\ --depth 3 # the same walk, without the other checksNothing is scanned until you run it, and nothing is written without --fix.
After an upgrade the CLI mentions pjm doctor once — a wheel install can't run
code, so the first command you type is the only place to say it.
With one project registered, that is the whole setup — there is only one place a call can go. With several, your AI passes project="<name>", or you set a default with pjm project use <name>. pjm init prints this block with your own Python path already filled in.
Upgrading from 0.2.x? Your existing --root entry keeps working exactly as before, and a pinned server now refuses to write outside its own repo even if asked. Replace it with the block above when you want one server for everything.
The 3-minute workflow (let your AI do the setup)
Install + init.
pip install projectmem, thencdinto your project and runpjm init— or simply ask your AI to run it.Ask your AI to set up the projectmem MCP server for you — it can edit the client's config file itself. (It needs permission to do that: use Auto / accept-edits mode, or approve the file edit when asked. The exact config per client is in the sections below if you'd rather paste it by hand.)
Restart the AI tool so the MCP server loads, then start your session with this prompt:
Hi — I use projectmem as this project's memory. Before anything else,
call get_instructions(), then get_summary(), then get_project_map() to
load what we already know. As we work, log issues, attempts
(failed/worked), fixes, decisions, and notes with the projectmem tools,
and call precheck_file(file_path) before you edit a file. Ideas and plans go
in plan.md via get_plan() — never as events.Strictly speaking this prompt is optional — with the MCP server installed correctly the AI discovers the memory on its own. But saying it makes capture noticeably more consistent, so we recommend it.
Repeat for every project:
pjm init+ the same kickoff prompt.Coming back after closing the window? Open with a one-line reminder — "Reminder: we use projectmem as memory here." — and the whole setup carries on where you left off.
Prefer to wire it up by hand? The exact, verified config for each client follows.
Claude Desktop
Easiest — open the config from the UI:
macOS: Claude menu →
Settings…→Developertab → Local MCP servers → Edit Config.Windows / Linux: same path expected (
Settings → Developer → Edit Config) — open an issue if your platform differs and we'll update this.
If you prefer the raw file path: ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows, ~/.config/Claude/claude_desktop_config.json on Linux (or $XDG_CONFIG_HOME/Claude/ if you have moved it). pjm init prints the right one for the machine you run it on.
Paste this block:
"mcpServers": {
"projectmem": {
"command": "/opt/anaconda3/bin/python",
"args": ["-m", "projectmem.mcp_server"]
}
}Two things to know about this block:
Use the absolute path to
python(e.g./opt/anaconda3/bin/python, or runwhich pythonto find yours). Claude Desktop subprocesses don't inherit your shellPATH, so bare"python"often fails.You no longer need the
cwdfield, and you never could rely on it. Claude Desktop's current build (with the Epitaxy / Cowork workspace system) silently ignorescwd— the server ends up running withcwd=/and can't find.projectmem/. That is why older releases needed--root. The registry replaces it: the server finds projects by name, not by where it happens to be running.
<details> <summary>Pin this server to a single repo instead</summary>
"mcpServers": {
"projectmem": {
"command": "/opt/anaconda3/bin/python",
"args": [
"-m", "projectmem.mcp_server",
"--root", "/absolute/path/to/your/project"
]
}
}A pinned server serves exactly that repository and refuses to write anywhere else, even when asked — the stricter choice if you want a hard boundary. pjm init --mcp-config-single prints this form.
</details>
Then fully quit Claude Desktop (Cmd+Q on Mac) and reopen — MCP servers only initialize on cold start.
Cursor
Two ways to register the MCP server — pick whichever fits your workflow:
Global (recommended): Cursor menu →
Settings…→ left sidebar Tools & MCPs → Installed MCP Servers → Add Custom MCP. Paste the JSON below.Per-project: drop the JSON into
<project-root>/.cursor/mcp.json— only active when that project is open.
{
"mcpServers": {
"projectmem": {
"command": "/opt/anaconda3/bin/python",
"args": ["-m", "projectmem.mcp_server"]
}
}
}Two things to know about this block (same gotchas as Claude Desktop):
Use the absolute path to
python(runwhich pythonto find yours). Cursor subprocesses don't reliably inherit your shellPATH.Don't bother with the
cwdfield. Cursor — like Claude Desktop — silently ignores it: the server ends up running withcwd=~. Since 0.3.0 that no longer matters, because projects are found by name in the registry rather than by where the server runs.
Registered globally, one entry covers every project. Per-project .cursor/mcp.json still works if you prefer the server to exist only when that repo is open — add "--root", "/absolute/path/to/your/project" to args there to pin it.
Then fully quit Cursor (Cmd+Q on Mac) and reopen. projectmem also auto-discovers .projectmem/ by walking up from CWD (like git does for .git/), and honors PROJECTMEM_ROOT and a --root <path> CLI argument.
Antigravity
Antigravity (Google's AI IDE) speaks standard MCP.
Easiest — open the config from the UI:
Open the Agent window (the chat panel on the right).
Click the ⋯ Additional Options button in the panel header.
Choose MCP Servers → Manage MCP Servers → Add new (or Edit Config).
The raw file is at ~/.gemini/antigravity/mcp_config.json if you prefer editing it directly.
Paste this block:
{
"mcpServers": {
"projectmem": {
"command": "python",
"args": ["-m", "projectmem.mcp_server"]
}
}
}Antigravity does honor the cwd field, so adding "cwd": "/absolute/path/to/your/project" works — but it ties the server to that one repo. Leave it out and the same entry serves every registered project.
Then fully quit Antigravity (Cmd+Q on Mac) and reopen — MCP servers only initialize on cold start. All 17 projectmem tools register identically to Claude Desktop / Cursor.
Codex
Codex stores MCP config as TOML (not JSON) in ~/.codex/config.toml. There's a UI form at Settings → MCP Servers → Add MCP Server, but during cross-client verification the form's Save button didn't reliably persist — the file-edit path is faster and more reliable.
Easiest — edit ~/.codex/config.toml directly:
Append this block (preserves any existing config):
[mcp_servers.projectmem]
command = "/opt/anaconda3/bin/python"
args = ["-m", "projectmem.mcp_server"]Three things to know about this block:
Use the absolute path to
python(runwhich pythonto find yours). Codex subprocesses don't reliably inherit your shellPATH.You no longer need
--rootorcwd. Earlier releases passed--rootas defense in depth (thecwdfield does appear to work in Codex, unlike Claude Desktop and Cursor). Since 0.3.0 the registry makes both unnecessary — add"--root", "/absolute/path/to/your/project"toargsonly if you want this server locked to a single repo.Set your reasoning effort to
mediumor higher. On low-reasoning Codex skipsget_instructionsfrom the session-start trio, which can cause the AI to miss the Setup Mode workflow rules. Medium+ honors the full trio automatically.
Validate the TOML:
python -c "import tomllib; tomllib.load(open('/Users/<you>/.codex/config.toml','rb')); print('OK')"Should print OK. If not, the parser tells you the offending line.
Then fully quit Codex (Cmd+Q on Mac) and reopen. Same cold-start rule as every other MCP client. Codex MCP servers spawn lazily on the first tool call in a chat session — if you don't see the process in ps aux right after reopening, send any message to a Codex chat and check again.
Reasoning-effort note: Codex's mode selector is at the bottom of the chat input. Set it to medium (not low) for the full session-start trio behavior. Once set, it persists per-session.
First-run permission prompts
On first use in any MCP-capable client (Claude Desktop, Cursor, Antigravity, Codex), your AI will ask permission before each projectmem tool call. This is expected security behavior — MCP clients require explicit consent for every new tool. Approve each tool once and the prompt won't reappear for that session.
Other MCP Tools
Any MCP-compatible client works — point your tool at
python -m projectmem.mcp_server and either set cwd to your project
root or rely on the parent-walk auto-discovery.
MCP Tools Exposed
All 17 tools your AI can call. Every repo tool takes an optional
project argument — see One server, many projects:
Read-side (12 tools):
| Tool | When to use |
|---|---|
get_instructions() | Start of every session — load workflow rules |
get_summary() | Start and end — distilled project memory |
get_project_map() | Start — understand repo structure |
get_plan() | Read plan.md — the ideas + plans (intent), separate from the event log |
precheck_file(file_path) | Before editing any file — surface failure history |
get_issue(issue_id) | Read one specific issue's full history by ID |
search_events(query) | Plain-text search across all logged events |
get_context(tokens, focus) | Token-budgeted memory block with optional focus filter |
get_score() | A+→F prevention score + ROI numbers |
get_global_gotchas(library) | Cross-project library lessons inherited from past repos |
list_projects() | See every registered project, and which one is active |
current_project() | Check which project a call would resolve to, without writing anything |
Write-side (5 tools):
| Tool | When to use |
|---|---|
log_issue(summary, location) | Immediately when encountering a bug |
record_attempt(summary, outcome) | Immediately after each fix attempt (outcome: failed/partial/worked) |
record_fix(summary) | After confirming a fix resolves the issue |
add_decision(summary, supersedes?) | When making architectural / design decisions; pass supersedes to retire a stale decision without losing history |
add_note(summary) | When discovering gotchas, setup details, or constraints |
CLI Reference
Core memory
| Command | Purpose |
|---|---|
pjm init | Initialize memory + auto-install hooks + inherit global memory |
pjm log <text> | Start a new issue / debugging session |
pjm attempt <text> [--failed|--worked] | Record a fix attempt outcome |
pjm fix <text> [--issue <id>] | Record the confirmed fix and close the issue — --issue targets a specific one (new in 0.1.5) |
pjm decision <text> [--supersedes <id>] | Record an architectural decision; optionally retire a prior one (old event stays in the log, tagged) |
pjm note <text> | Record durable context or a gotcha |
pjm plan ["idea"] | Print plan.md (ideas + plans); with text, append an idea. Intent, not an event (new in 0.2.0) |
pjm show | Print the current summary |
pjm search <query> [--failed-only] | Plain-text search across all events; --failed-only lists the project's dead ends |
pjm brief | One-screen session-start briefing: warnings, stale memories, open issues, decisions, score |
pjm export [--claude-md|--cursor] | Compile live memory into CLAUDE.md / .cursorrules for agents without MCP |
Intelligence layer
| Command | Purpose |
|---|---|
pjm watch [--daemon|--stop|--status] | Real-time file churn watcher |
pjm precheck [--snooze 2h|--unsnooze] | Warn about repeating failed approaches before commit; snooze politely (audited) when needed |
pjm wrap <agent> | Inject token-budgeted memory into Claude/Cursor/Aider |
pjm context [--tokens N] | Generate token-budgeted project context |
pjm score [--format text|json|badge] | Letter-grade prevention score |
pjm global <action> | Manage cross-project memory |
Projects (global MCP)
| Command | Purpose |
|---|---|
pjm doctor [--fix] [--path P] [--online] | Find unregistered projects, stale entries and pinned client configs. --online also asks PyPI for the newest release; --auto remembers to check daily (new in 0.3.0) |
pjm project list | Every project this server can reach, and which one is active (new in 0.3.0) |
pjm project scan <dirs> [--depth N] [--dry-run] | Walk for projects with memory and register them |
pjm project register [path] [--alias a] | Add a project that already has memory (pjm init registers automatically) |
pjm project use [name] | Set the default project for calls that name none; omit the name to clear it |
pjm project alias <name> <alias> | Give a project a shorter name |
pjm project tag <name> <tag> [--remove] | Tag a project |
pjm project remove <name> | Forget a project — its repo and .projectmem/ are untouched |
Visualization & utility
| Command | Purpose |
|---|---|
pjm visualize | Open the six-tab local dashboard (Overview, Story Map, ROI, Project Map, Timeline, Showoff) |
pjm dashboard [--serve] [--port N] | Cross-project global dashboard over every pjm init-ed repo; default writes a static snapshot, --serve runs an ephemeral live server (Ctrl+C to stop) (new in 0.2.0) |
pjm map [--build] | Print the Project Map; --build (re)builds the code structure + import graph into structure.json (a derived, gitignored cache) (new in 0.2.0) |
pjm stats | Token ROI summary in the terminal |
pjm backfill | Auto-populate memory from git history |
pjm hooks install|uninstall | Manage git hooks manually |
pjm regenerate | Rebuild summary.md from events.jsonl |
Use
--at "file.py:42"with any logging command to attach precise location metadata.
plan.md — intent, kept separate from memory
pjm init scaffolds a .projectmem/plan.md: your ideas and plans — what you mean to do, in plain Markdown (Ideas · Active plans · Next · Someday · Shipped). It's the one file that is deliberately not the event log:
events.jsonl → summary.mdrecords what happened (append-only, never rewritten).plan.mdrecords what you intend — and you (or the AI) edit it directly, likePROJECT_MAP.md.
Your AI reads it at session start via get_plan() and updates it in place: adding ideas, checking items off, moving finished work down to Shipped. A plan is never logged as an event, so intent stays cleanly out of your memory's audit trail. pjm plan prints it; pjm plan "auto-batch the exporter" appends an idea. It's committed (not gitignored) so intent is shared with your team.
Example: Pre-Commit Warnings in Action
$ git commit -m "switch auth to JWT"
projectmem: Pre-Commit Check
─────────────────────────────────────────────
src/auth/middleware.py
WARN What already failed here (2 attempts):
✗ tried switching to JWT middleware (2d ago)
✗ patched session timeout to 60min (5d ago)
WARN HIGH CHURN: 5 changes in last 30 days
WARN 1 possibly-stale memory cites this file
decision [evt_9db5a3f8…] "auth uses session
cookies, 30min timeout" — predates 7 commits
Confirm it still holds, or retire it:
pjm decision "..." --supersedes <id>
─────────────────────────────────────────────
3 warning(s). Review before committing.Need it quiet for a refactor sprint? pjm precheck --snooze 2h — warnings pause, the pause itself is logged, and every commit shows one dim line so silence is never mistaken for a clean check.
Privacy & Security
By default, projectmem commits the distilled files (summary.md, PROJECT_MAP.md, AI_INSTRUCTIONS.md, issues/) and gitignores the raw log + runtime files (events.jsonl, watch.pid, watch.log). This means your teammate's AI inherits your team's knowledge automatically — just git clone and the AI already knows what your team learned.
Want total privacy? Add a single line .projectmem/ to your .gitignore. Nothing leaves your machine.
Full security policy and threat model: SECURITY.md · Privacy & Security guide
Design Principles
Local-first — No cloud, no telemetry, and no network calls unless you turn on the update check. Your data never leaves your machine.
Project-scoped — Memory lives in the repo. When the code moves, the memory moves.
AI-tool-agnostic — Works natively via MCP, or universally via Markdown instructions. Any AI tool, any workflow.
Built With
projectmem stands on the shoulders of these excellent open-source projects:
Typer — the CLI framework that makes
pjmfeel ergonomicModel Context Protocol — Anthropic's open spec that lets AI agents talk to local tools
watchdog — cross-platform filesystem event monitoring (the heart of
pjm watch)D3.js — the interactive visualizations in
pjm visualize
Research & Citation
projectmem is described in a peer-readable research paper:
PROJECTMEM: A Local-First, Event-Sourced Memory and Judgment Layer for AI Coding Agents Ripon Chandra Malo, Tong Qiu — University of Utah arXiv:2606.12329 · cs.AI
The paper introduces the Memory-as-Governance framing — memory that doesn't merely answer the agent but acts on its next action — and reports the design, the deterministic pre-commit judgment gate, a capability comparison against 12 contemporary memory systems, and a two-month, 207-event dogfooding study across 10 real projects.
If projectmem is useful in your research or writing, please cite:
@misc{malo2026projectmem,
title = {PROJECTMEM: A Local-First, Event-Sourced Memory and
Judgment Layer for AI Coding Agents},
author = {Malo, Ripon Chandra and Qiu, Tong},
year = {2026},
eprint = {2606.12329},
archivePrefix = {arXiv},
primaryClass = {cs.AI},
url = {https://arxiv.org/abs/2606.12329}
}License
MIT — free for personal, commercial, and enterprise use forever.
Help Us Reach More Developers
We don't need money. We need you.
projectmem was started by one developer and is now shaped by contributors from the open-source community. Every star, every share, and every contribution helps the project survive and grow.
Star the repo — takes one click, helps massively with discovery
Share on X / LinkedIn — tell other devs they don't have to keep paying AI to relearn their codebase
Open an issue — bug, feature request, or just feedback
Contribute code — PRs welcome; for anything large, open an issue first
Using
projectmemat work or in a commercial product? Reach out to [e-mail address on the upstream page](mailto:e-mail address on the upstream page) so we know who's shipping with us. It's free — we just love hearing about it.
Stars and shares matter more than money — but if you really want to: sponsor on GitHub →
<div align="center"> <sub>Built with care by the open-source community. Every contribution, no matter how small, makes a difference.</sub> </div>
CHANGELOG.md
Changelog
0.3.5
Two community fixes: precheck recognises a file however its path is spelled, and the summary stays small even when entries are long.
Fixed
precheck_filecalled a file with known failures "Safe to modify" when its path was spelled differently. Events store project-relative paths, so./src/auth.py, an absolute path, a path through a symlinked folder, a backslash path or a WindowsC:\path matched nothing. Every path, the one being checked and the ones in the log, is now made project-relative before matching, and two spellings of one file give one set of warnings. A decision logged at an absolute path is now also judged stale for the right file. Matching got faster as well: checking 5 files against a 3,000-event log went from 663 ms to 163 ms. (#26)get_issuemissed unpadded ids and passed raw input to a file glob.get_issue("1")andget_issue("#1")now find issue 0001, aspjm fixdoes, and backfilledlegacy_…ids work. Input containing*,?or[is refused instead of matching some other issue. (#26)summary.mdcould still cost ~9,000–12,000 tokens on an aged project. 0.3.4 capped how many entries the summary shows, but not how long each one is. Each record is now clipped at 300 characters, with line breaks folded first and locations kept whole. A fixed issue is one line (id, title, location), and its details are oneget_issuecall away. Open issues still show their last 3 failed or partial attempts. On a real 238-event project, the summary went from 8,974 to 3,458 tokens. (#29, #30)precheck_file("")reported the whole log. An empty string is part of every summary, so a blank path matched every failed attempt and open issue. Blank paths are now ignored, and whitespace around a path is trimmed.A whitespace-only location rendered as
[]in the summary and hid the fix's location. It now counts as no location.
Changed
AI_INSTRUCTIONS.mdasks for shorter records: issue titles under 140 characters, everything else under 300. The five write tools say the same in their argument descriptions. Because the template changed, runpjm initonce in each project. It refreshes the file and keeps your previous copy asAI_INSTRUCTIONS.md.bak, so merge back anything you added by hand.Token figures corrected. The guidance said
get_summarycosts "a few hundred to ~1,500 tokens". That was measured on short synthetic entries, and a real project with long entries was far above it (#29). It now says a few hundred on a young project and a few thousand on an aged one. The README's per-session estimate also left outget_instructions, which agents read first in every session and which is about 3,300 tokens on its own. The table now counts it and gives measured figures: on nine real projects, the three files read at session start total about 3,700 to 7,600 tokens.
The fixes for #26 were contributed by @vishalhabib99, and #29 was reported and fixed by @bigfree.
0.3.4
Three reports, three community fixes, and six more bugs found by checking them. Each report below was reproduced end to end before it was fixed, and each fix was reviewed by someone other than its author.
Fixed
The guidance named arguments the tools reject.
AI_INSTRUCTIONS.md, theCLAUDE.md/AGENTS.mdbridge and the MCP server's own instructions saidprecheck_file(path); the argument isfile_path, so an agent that copied the call got a validation error. The README had the same mistake forget_issue(id)(it isissue_id). A test now reads every call shown on every surface and checks it against the tool's real signature, so a rename can no longer leave the docs behind. (#18, #22)summary.mdgrew without limit. Notes were capped at 10, but Recent issues and Decisions listed everything ever logged, so on an aged project the summary cost more to read than the code it describes. Every open issue is still listed — it is the one thing the next session must not miss — while fixed issues are capped at the newest 10 and decisions at the newest 15, each with a line saying how many were left out and where to find them. Issue ids sort as numbers, so ids past 9999 no longer sort the newest to the bottom. Past 20 open issues, the older ones are listed by id on a single line instead of in full, so a neglected backlog cannot regrow the summary either. (#19, #25)Auto-captured commits were filed under
.projectmem/summary.md. The location was the first changed path in git's sort order, and.projectmem/sorts first, so most commits pointed at the regenerated summary; the staleness check then called valid memories stale and the real file lost its history. The location is now the file the commit is about: deleted paths are skipped, renames use the new name, the first commit and merge commits list their files, non-ASCII paths are unquoted, and source code is preferred over tests, docs, build files, assets and lockfiles. (#20, #23)Staleness depended on the time zone — a regression in 0.3.3. The 0.3.3 speed-up compared git's local-offset commit times with UTC event times as plain strings. West of UTC, recent stale memories were never flagged; east of UTC, fresh ones were flagged at once. Both sides are now compared in UTC.
Already-polluted logs stop warning without being rewritten. Events filed under
.projectmem/before this release are no longer stale-checked, and neither are auto-captured fix and note records — they record what a commit did, which cannot go stale. Auto-captured decisions (Refactor:,Breaking change:) still are. Nothing to run.One mistyped issue id broke every later write.
pjm attempt --issue 0099(orrecord_attemptwith a wrongissue_id) appended an attempt with no issue behind it, and the summary rebuild after every following command raisedStopIteration. Unknown ids are now refused before anything is written, ids are normalised the waypjm fixdoes it, a stale active-issue marker is ignored and cleared, and the summary skips a malformed group instead of crashing.pjm precheckwarned about projectmem's own files. Staging the regenerated summary printed "HIGH CHURN … may indicate an unresolved architectural issue". Memory files are left out of the check, andprecheck_fileon one now says to use the log tools instead of calling it safe to edit.Fixes to the git hooks never reached existing projects.
pjm initskipped any hook it had already installed, so the 0.3.3 hook fixes for Windows and GitHub Desktop only reached new projects. Re-runningpjm initnow refreshes projectmem's block in place and leaves everything else in the hook untouched. The hook also records thepjmthat raninit, not whichever one PATH finds first.
Changed
pjm initrefreshes an outdatedAI_INSTRUCTIONS.md. It was written only when missing, so every existing project kept the guidance of the release that created it — including the wrong argument above. Re-runpjm initonce: the file is brought up to date, your previous copy is saved asAI_INSTRUCTIONS.md.bak(gitignored), and your inherited global-memory block is kept. A file already at the current version is never touched.The guidance states what reading memory costs. It said a
get_summarycall costs ~500 tokens. Measured on the capped summary it is about 250 tokens on a young project and about 1,100 on an aged one, more with many open issues, so it now says "a few hundred to ~1,500".pjm initno longer writessummary_size_limit_kbtoconfig.toml. Nothing ever read it.After upgrading, the CLI says to run
pjm initin each project, since that is what refreshes the instructions file and the git hooks.
Docs
The README,
llms.txt,SECURITY.mdand the tutorial were checked line by line against the code. Fixed: the MCP tool count (17, not 15 or 8), links that broke on PyPI, a link to a contributing guide that did not exist, an exampleprecheckline the command never prints, token figures now labelled as estimates, the client-workspace-root routing step marked as planned (it is not wired up yet), and claims that no other tool offers pre-commit warnings or keeps superseded memories — the comparison table was rebuilt from each project's own docs as of 2026-10-02.
Fixes for #18 and #20 were contributed by Rohan Tiwari and the first cap for #19 by hylin. #18 and #19 were reported by @medium-effort and #20 by @bigfree.
0.3.3
Three reports from Windows, and the tool no longer freezes as your project grows. Every fix below was found by running projectmem on a real machine, not by reading the code.
Fixed
pjm watch --daemoncould not be seen or stopped on Windows, and leaked a process per run. A hook whose shebang cannot be resolved does not get skipped — git aborts the commit. The hooks shipped#!/usr/bin/env bash, which depends on PATH; Git for Windows maps/bin/shto its own shell, and under GitHub Desktop bash often is not there. The snippet body only ever used[ ],command -vand$( ), so bash bought nothing. Two more faults fed the same failure: the baked binary path went in raw, and on Windows the shell ate its backslashes (C:\Users\riponbecameC:\Usersipon), so the-xtest failed and the hook silently fell through to its PATH lookup — it only ever worked by accident; and the venv fallback looked in<prefix>/bin/pjm, which cannot exist on Windows. (#16)get_contextdescribed the wrong directory.generate_contextdefaults its root toPath.cwd(), and the MCP server was the only caller that omitted it — both CLI call sites pass it. For an MCP server, cwd is wherever the client launched the process, so the git status and architecture came from the agent harness rather than your project. (#15)A decorative character killed the command on a Windows console.
pjm initcreated.projectmem/, the git hooks,CLAUDE.mdandAGENTS.md, then raisedUnicodeEncodeErrorprinting a box-drawing rule and exited 1 — so the command both did its work and reported failure, which is the worst outcome for any script checking the exit code. A stock Windows install runs the console on cp1252, whereU+2550has no mapping, and writing it raises rather than degrades. The reported line was only the first one reached: 11 of the 14 glyphs projectmem prints to a terminal are unencodable there. Output streams are now set to replace what they cannot encode, so no glyph can abort a command, and the glyphs themselves fall back to ASCII when the stream cannot carry them — cp1252 output reads as deliberate rather than as a row of?. UTF-8 terminals are unchanged, and the web dashboard keeps its real characters.Prose punctuation came out as black diamonds on the same console. A second report against the fix above. An em dash is cp1252-encodable (
0x97), so neither the ASCII fallbacks nor the replacement net touched it — and it still rendered wrong, because Python wrote0x97and a terminal set to UTF-8 read that as an invalid lead byte and drewU+FFFD. Encodable was never the right test. On a stream that cannot carry Unicode, projectmem now emits no non-ASCII byte at all, so nothing downstream has anything to misread.precheck_fileinvented warnings about every project but the server's own. The tool resolved the project root to read events, then called_analyze_fileswithout it, so staleness asked "does this cited file exist?" relative to the MCP server's working directory. In global mode that is the agent harness, so a four-file precheck on an untouched repo came back with 373 phantom "cited file no longer exists" warnings._analyze_fileshad the same gap one level down — it took the root but called_git_recent_changeswithout it, sogit logran in the wrong repository, and outside a repository git fails and the churn count silently fell back to the event log, reporting churn the project did not have. Both surface only when the root differs from cwd, which is every MCP client that does not chdir into the project and never the CLI — which is whypjm precheckwas always correct.Expected errors were reported as crashes.
pjm fixwith no open issue printed a clear message and then a full traceback underneath it.typer.Exitescapedmain()uncaught, so Python printed the chained cause.
Faster
pjm precheckwas taking 26 seconds on a 1,200-event project, and getting worse as the project aged. It ran onegit logper event — 1,201 subprocesses — to answer a question about a single file, and the instructions tell agents to call it before modifying anything. Three faults compounded: staleness was computed across the whole event log and then filtered down to the file being checked; the memo key was(file, timestamp), which looks right but never hits because real events carry distinct timestamps; and nothing bounded the git walk. It now makes onegit logper distinct file, bounded by the oldest event citing it, and counts in memory.events before after 100 2,210 ms 48 ms 400 8,905 ms 51 ms 1,500 ~33 s, 1,501 git processes 82 ms, 2 processes Latency is now flat rather than linear in project age. The project-wide path used by
pjm briefand the dashboard went from 26,294 ms to 450 ms. Results are unchanged — verified against a reimplementation of the old algorithm, including across merge commits.
Changed
Your project is named in
CLAUDE.md. One server serves every project, so a tool call that names none is refused rather than guessed at — but nothing ever told the agent the name, so it learned it from the error and retried. That round trip happened every session. The bridge now carries the registered name and the exact argument to pass. (#14)AGENTS.mdis written alongsideCLAUDE.md. The 0.3.3 fix for #14 put the project name inCLAUDE.md— which Antigravity and Codex never read, so for those clients the fix did not exist.pjm initnow writes both files with identical content, and the guidance no longer asks the agent to look the project up before starting: it names the active project first and says calls that omitprojectland there. (#14)Retiring a decision is discoverable.
supersedeshas existed since 0.1.4 and the summary renderer honours it, butAI_INSTRUCTIONS.mdmentioned it zero times in 12,503 characters, so models never called it andsummary.mdaccumulated decisions that contradicted each other. It is documented now on all three surfaces an agent may read, and each says what it does to the summary rather than only that the argument exists. (#17)
Windows daemon support in 0.3.2 was contributed by @medium-effort, who also reported every issue fixed in this release.
0.3.2
Windows works properly now. pjm watch --daemon crashed there, and fixing the crash uncovered a second bug one function away that had been hiding behind it. Both are fixed. pjm doctor also learned to notice when a fix you made gets undone.
Fixed
pjm watch --daemoncrashed on Windows withAttributeError: module 'os' has no attribute 'fork'. Daemonisation used the POSIX-onlyos.fork()andos.setsid(); it now spawns a detached worker withsubprocess.Popen—DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOWon Windows,start_new_session=Trueelsewhere. Contributed by @medium-effort (#12, #13).The watcher could not be seen or stopped on Windows, and leaked processes.
_running_pid()probed liveness withos.kill(pid, 0). That is the POSIX idiom and does not port: on Windowsos.killroutes toTerminateProcess, so signal 0 is not a liveness check and fails whether or not the process exists.pjm watch --statusalways said "not running", the PID file was deleted as stale while the worker ran on,--stopfound nothing to stop, and every further--daemonslipped past the already-running guard and started another one. Liveness now usesOpenProcess+GetExitCodeProcesson Windows, and access-denied counts as alive — a process you cannot open still exists, and calling it dead is what orphans it. This bug predates the crash above and was only invisible because nothing got far enough to hit it.pjm doctorreported a clean bill of health on a Store-installed Claude Desktop. A Microsoft Store (MSIX) install runs in an AppContainer, where the app's writes to%APPDATA%are redirected into the package'sLocalCache. Inside the container the config still appears at%APPDATA%\Claude; from any ordinary process it does not exist there at all. Doctor found nothing and printed green while two servers pinned with--rootwere running — worse than missing the problem, because it ends the investigation. Store installs are now discovered under%LOCALAPPDATA%\Packages\Claude_*and labelled Claude Desktop (Store), since the file is at a path most users have never seen. Found on Windows against a 0.3.2 build.Client config paths now read
%APPDATA%and%LOCALAPPDATA%rather than assuming they resolve under the home directory. The old assumption is wrong on any machine with a redirected profile — roaming profiles, OneDrive Known Folder Move, most managed corporate setups.A config that cannot be read is no longer counted as fine. Unreadable configs — permissions, a lock, a broken link — were skipped silently, which made "I could not look" indistinguishable from "I looked and it is fine". They now get their own line.
pjm doctoralso checks the project you are standing in. A per-repo.mcp.jsonor.cursor/mcp.jsonpins just as effectively as a global config, and was never examined.--at src/foo.pycounted for nothing. Every place that turns an event location into a file path usedlocation.split(":")[0], which had two faults. A bare path has no colon, sopjm score's file-coverage component ignored it — up to 20 points lost in silence, and weaker churn detection inpjm precheck— even though--at's own help text invites that form. And a Windows path carries a drive colon, soC:\src\foo.py:42resolved to a phantom file calledC. Both are fixed in one shared parser now used by score, precheck, staleness, brief, context and export.--at src/foo.pyand--at src/foo.py:42finally count the same, and aClassName.methodlocation is still correctly not a file.Global memory ignored
$PROJECTMEM_HOME.global_dir()read a constant fixed at import fromPath.home(), while the registry resolved the environment variable per call. Sinceappend_event()auto-promotes library mentions into the global store, a test or fixture running under an isolated home still wrote invented gotchas into the real~/.projectmem/global/. It now resolves per call, the same way the registry does.Two tests only passed on POSIX. One asserted a raw path inside JSON output where the product correctly emits escaped backslashes; the other set
$HOME, which Windows ignores in favour of%USERPROFILE%, so the fixture home went unused and the real profile was scanned. All nineteen$HOMEfixtures now go through one portable helper.
Changed
pjm doctortells you to quit the client before editing its config. These files hold the app's own preferences too, so a running client can rewrite the whole file from the copy it loaded at startup and restore the--rootyou just removed. Doctor was giving advice that silently fails while the app is open.pjm doctornotices when a fix has been undone. It remembers the previous verdict for each client config, so a config that was clean last run and is pinned again now gets named, with the time it was last clean — instead of looking flaky for reporting a problem you know you already fixed. It records every config on disk that mentions projectmem, including the ones that are already fine, so the very first run after upgrading is enough to catch a clobber straight afterwards. Local files only; no process inspection, and nothing leaves your machine.
0.3.1
Know when there is something to upgrade to — without giving up the promise. projectmem says it makes no network calls, and that is a reason people choose it. So update checking is opt-in everywhere, and nothing about your machine is ever sent: the request is a plain GET of the same public JSON file pip install reads.
In the dashboards
Both pjm visualize and pjm dashboard now show which version generated the page — the project dashboard in the sidebar under the star button, the global one in the footer. Beside it is a check for updates link. The page makes no request until you click it, and the result is one of up to date, X available ↗ (linking to PyPI), or could not reach PyPI, which changes nothing else on the page.
On the command line
pjm doctor # shows your version and how to check
pjm doctor --online # one-off check
pjm doctor --auto # remember to check, at most once a day--auto is off by default and can be turned off again with --no-auto. When on, the result is cached, so an enabled check costs one request a day.
Fixed
Version comparison stripped every non-digit, which turned 1.0.0rc1 into (1,0,1) and sorted a release candidate above the final release — someone on 1.0.0 would have been told to upgrade to 1.0.0rc1. Leading digits only now.
0.3.0
One server, many projects. Until now an MCP config was tied to one repository: eleven projects meant eleven server entries and eleven restarts. 0.3.0 serves every registered project from a single server — paste the config once, and every repo you pjm init afterwards is reachable from it. Still local-first: no central store, no telemetry, no cross-project memory retrieval. The event schema is unchanged.
New: global MCP mode
Every repo tool takes an optional project argument (a registered id, alias, or path), and two new tools — list_projects and current_project — let an agent see the names and check where a write would land before making it. 17 tools total.
Resolution happens per call and is never cached, in this order: a pinned --root, then an explicit project, then the client's workspace root, the active project, the working directory, and finally the sole registered project when there is only one. When none of those apply it raises an error listing what is registered — it never guesses.
Two properties worth knowing:
A pinned
--rootis a boundary, not a default. A server configured for one repository refuses to write anywhere else, even when a call explicitly names another project. Existing 0.2.x configs keep working exactly as before.Every write names the project it landed in. In a single-project setup a misconfigured server simply fails; a shared server can succeed against the wrong repository, which corrupts two audit trails at once. The echo makes that visible on the call that caused it.
New: the project registry and pjm project
~/.projectmem/projects.json grows from a list of paths into schema v1 records with ids, aliases, tags and an active selection. Migration is automatic on first read, in place, keeping a .bak. pjm dashboard and pjm init are unaffected.
pjm project list | register | use | alias | tag | remove manage it. Removing a project from the registry never touches its repo or its .projectmem/.
Fixed: the MCP server was broken on fresh installs
mcp 2.0.0 (2026-07-28) renamed FastMCP to MCPServer and left mcp.server.fastmcp behind as a module that raises on import. With an unpinned mcp>=0.1.0, every pip install projectmem after that date got a server that died at import — markdown mode kept working, the tools did not. Reported and fixed by @VIVAAN-DHAWAN; pyproject.toml now declares mcp>=1.0,<3.
Fixed: stored XSS in pjm visualize
Event summaries reached the DOM unescaped. Since pjm's auto-capture turns git commit messages into event summaries, a crafted commit in a branch you pulled could execute script in your dashboard — a page that embeds your entire event log. Every sink is escaped now, and a </script> in an event no longer breaks the page.
The dashboard, rebuilt
pjm visualize opens on a shareable Memory Card (grade, tokens and hours saved, downloadable as a PNG) above Case files — every problem with its full issue → attempt → fix chain, one click from the Timeline or the Story Map. The Story Map gains an effort treemap and story lanes alongside the force graph, and any file opens a dossier: precheck_file, rendered.
pjm dashboard now opens with where you left off — your last working session reconstructed from event timestamps, what it was, and what it left behind — plus an attention band across every project and a state badge per card.
Also
pjm initprints the shared MCP config by default;--mcp-config-singleprints the pinned form. It also warns when an existing client config still pins projectmem to one repo.pjm dashboardhonours$PROJECTMEM_HOMEfor its output directory, so a sandboxed run no longer overwrites the real dashboard.
0.2.0
The workspace release: memory grows from one project to your whole machine — and finally meets your code's real structure. 0.1.6 made a single project's memory something you could watch; 0.2.0 lifts that to every project at once, and closes the gap between what happened (memory) and what the code is (structure). Still local-first: no central store, no telemetry, no new required dependencies, and the event schema is unchanged — the six core event types (issue, hypothesis, attempt, fix, decision, note) are exactly as before.
New: pjm dashboard — the cross-project global view
One page over every project you've pjm init-ed: total issues captured, fixes confirmed, dead-ends prevented, tokens/dollars saved, a grade per project, issues-over-time, and a "needs attention" list. Click any project card to open that repo's own dashboard, generated fresh from its current files, with a "← All projects" link back.
Crucially this is a global view, not a global store. Nothing is centralized — the dashboard aggregates each repo's own .projectmem/ at read time and never copies memory out of its folder. A tiny registry at ~/.projectmem/projects.json just records where your projects are (override with $PROJECTMEM_HOME).
Default is serverless —
pjm dashboardwrites a self-contained static snapshot and opens it. Re-run to refresh. The page labels itself a snapshot so it never overclaims.--serveis live —pjm dashboard --serveruns a tiny, ephemeral local HTTP server: the Refresh button re-reads your files, and each project card renders on demand from its latest state. No background daemon — it exists only while the command runs, and Ctrl+C (orkill) stops it cleanly.--portto choose the port.
New: code structure & relations — pjm map --build
Memory knew your history; now projectmem also reads your structure. pjm map --build (run automatically after pjm init) walks the codebase and, for Python, resolves imports into a real dependency graph, written to a derived, gitignored .projectmem/structure.json cache — code is only ever read, never modified. The Project Map's Graph and Flow views now render actual files and the import edges between them, not just files that happen to appear in the event log.
New: failure heat on the structure graph (the combo)
The one view neither a pure code-grapher nor a pure memory tool can draw: real structure with real judgment on top. Files with repeated failed attempts in your event log glow red — with a dashed ring past a threshold — laid directly over the structure/import graph, and hovering shows the failed attempts. Structure comes from the code, heat comes from memory, and they meet only in the renderer.
New: plan.md — an editable intent file, separate from memory
A new .projectmem/plan.md scaffolded at init: ideas and plans — what you mean to do, with Ideas / Active plans / Next / Someday / Shipped sections. It is deliberately not the event log: events.jsonl → summary.md records what happened; plan.md records what you intend. The AI reads it at session start and edits it directly (like PROJECT_MAP.md); a plan never becomes an event. pjm plan prints it, pjm plan "idea" appends one, and the MCP get_plan() tool exposes it. Committed (not gitignored) so intent is shared with the team.
Also
New tests cover the registry, the global dashboard (static +
--serve), the structure extractor, and the plan.md feature (135 total).The structure cache (
structure.json) is added to the consumer.gitignoreautomatically — it is derived from code and never needs committing.
0.1.6
The visualization release: your project's memory is now something you can watch — and share. The dashboard grows from four tabs to six, every new view is rendered from the same real event log, and nothing new is required: zero new dependencies, no schema changes, no CLI changes. Includes the Story Map readability controls contributed by @hanley-development (#7, which also folds in #6 — thanks!).
New: Showoff — animated story scenes with a built-in recorder
A new dashboard tab with three animated scenes, all driven by your real events: Story Replay (the project's history builds itself node by node on a force canvas — auto-loops, scrubber, live captions), Orbit (files orbit the project, events orbit their file), and Universe (the project as a rotating spiral galaxy; real events blink as bright stars). Click any node for its real details — the field dims, a halo pulses, and its connections light up; click again to release. Play/pause and speed controls throughout, and the animation only runs while the tab is open.
REC 10–60 s records the stage via the browser's own MediaRecorder and downloads a .webm — rendered 100% locally, with a toggleable "made with projectmem" badge baked into the video. Recording stops safely if you navigate away. (X/Twitter prefers mp4; most other platforms accept webm directly.)
New: Flow — the Project Map's default view
A layered left-to-right flowchart of the project story: PROJECT → DIRECTORIES → FILES → WHAT HAPPENED → MEMORY. Files with 3+ failures are outlined red with red flow lines, every file carries its outcome chips (failed / fixed / decisions / notes), and everything flows into an events.jsonl cylinder — the append-only memory, drawn as what it is. Zoom and pan like the other views; auto-fits on open. Tree and Graph remain one click away, and a new Hide details toggle collapses the PROJECT_MAP.md pane to give any view the full width. No code parsing — it is a pure layout over the same event graph the Story Map uses.
New: Time Spine — the Timeline's default view
A central real-time axis you scroll: day pills, "Nh quiet" gap markers, and the story branching into two meaningful sides — problems on the left (issues; failed / partial / worked attempts), knowledge on the right (fixes, decisions, notes). Hover any card and its entire issue thread stays lit while everything else dims — you can watch one bug's story thread down through time. The classic list view remains as Details.
From PR #7 (@hanley-development): Story Map readability controls
Collapse dense files (10+ events) into a draggable bubble, collapse directories with click-to-drill-down, expand all, and reset focus. Label noise reduced; file-focus mode dims unrelated nodes; file-only locations (no :line) now link correctly (folds in #6). Windows-style paths normalized.
Also
README gains a "New in 0.1.6" showcase with dashboard screenshots.
8 new tests pin the new template surface (115 total).
0.1.5
A reliability release: same features, fewer ways to break. Three community fixes (thanks @hanley-development) make projectmem safer to run everywhere — you can now close a specific issue by ID, MCP git calls can't hang a stdio session, and pjm brief / pjm precheck no longer crash on non-UTF-8 Windows consoles. No new dependencies, no schema changes.
Fixed: pjm fix --issue / record_fix(issue_id=…) — close the issue you mean
pjm fix and the MCP record_fix previously closed whatever issue was "current," so fixing an older issue after a newer one was logged could silently close the wrong one and write that wrong link into the append-only log. You can now target a specific issue: pjm fix --issue 0001 "…" (IDs normalize: 1/001/0001), and the active-issue marker is cleared only when it matches. Plain pjm fix is unchanged. Closes #3.
Fixed: MCP git helpers no longer hang stdio sessions
Some MCP tool paths spawned git subprocesses that inherited the server's stdin — which, in a stdio MCP session, is the JSON-RPC transport itself, so a child could block the whole server (notably on Windows). Git calls now run with detached stdin (DEVNULL) and bounded timeouts. CLI behavior is unchanged; adds stdio regression tests.
Fixed: console output is encoding-safe
pjm brief and pjm precheck print emoji and box-drawing characters that crash on Windows cp1252 consoles and git hooks (UnicodeEncodeError). Output is now sanitized to the active console encoding (UTF-8 unchanged; limited consoles degrade gracefully), and separators/markers fall back to ASCII. Decorative output can no longer abort a command or a hook.
0.1.4
The accountable-judgment release: memory that flags its own staleness instead of silently trusting (or deleting) it — plus a dashboard that opens on an all-at-a-glance Overview. Six small features (~150 lines, no new dependencies, no schema breaks) sharpen what makes projectmem different: it never deletes a memory, it tells you when one may have gone stale, it lets you retire decisions without losing history, it lists what already failed before you try it again, it briefs you at session start, it snoozes politely when it's wrong, and it exports its judgment to CLAUDE.md for agents that don't speak MCP. Also bumps the version (the __init__.py / pyproject.toml mismatch is corrected to a single 0.1.4).
New: stale-memory detection — flag, never delete
Other memory tools silently decay or delete old memories (and collect bug reports about wanted memories disappearing). projectmem now does the opposite: every decision/fix/note that cites a file is cross-referenced against that file's git history, and when the file has changed substantially since the memory was logged (3+ commits, or the file no longer exists), pjm precheck and the MCP precheck_file tool flag it — "decision [evt_…] predates 4 commits to auth.py — confirm it still holds, or retire it". Nothing is hidden, nothing is removed; a human (or agent) decides. Deterministic git log counts; no embeddings, no daemon.
New: superseded-decision marking — retire without rewriting history
pjm decision "switch to argon2" --supersedes <event-id> (also on the MCP add_decision tool) records a new decision that retires an old one. The old event stays physically in events.jsonl (append-only, always), drops out of summary.md, and shows up in pjm search tagged (superseded). Search output now prints event ids so the reference is one copy-paste away. A bad reference fails before anything is written. Together with stale detection this completes the non-destructive answer to memory decay: detect staleness → supersede explicitly → never lose history.
New: pjm brief — the session-start briefing
One screen that answers "where was I?": active failure warnings by file, possibly-stale memories, open issues, the latest live decisions, stack-relevant gotchas from global memory, and the prevention score with a week-over-week delta. Composes data the other commands already compute; runs in milliseconds; nothing leaves the machine.
New: precheck snooze — the polite off-switch
pjm precheck --snooze 2h (forms: 30m, 2h, 1d) silences pre-commit warnings for a bounded window instead of pushing annoyed users to --no-verify (which silences forever and leaves no trace). The snooze is itself logged to memory — even the silence is audited — and while active, every commit prints one dim line saying warnings are snoozed, so a silenced warning is never mistaken for a clean check. --unsnooze restores warnings early; expired markers clean themselves up.
New: failed approaches listed in precheck output
The pre-commit warning (and MCP precheck_file) now lists the dead ends themselves — "What already failed here: ✗ tried CSS contain:layout (2w ago) ✗ debounced the handler (2w ago)" — instead of just a count. The data was always in the log; now it's at the decision point. pjm search --failed-only lists the project's full catalogue of dead ends.
New: pjm export --claude-md — judgment for agents without MCP
Compiles live memory — current decisions (with stale flags), known gotchas, and a "Do NOT retry — these already failed" section — into a marked, auto-regenerated block inside CLAUDE.md (--cursor also writes .cursorrules; --stdout previews). Any agent that reads the file inherits the project's judgment with zero MCP setup. The block is replaced in place on re-run; the rest of the file — including the pjm init MCP bridge block — is never touched. Superseded decisions are excluded; possibly-stale ones are flagged, not hidden.
Fixed: walk-up discovery no longer mistakes the global store for a project
Running any pjm write command from a directory under $HOME with no initialized project used to walk up, land on ~/.projectmem/ (the machine-wide global store), misread it as project memory, and silently accrete events into it — then crash on commands that expected an issues/ directory. Discovery (both the CWD check and the walk-up) now only accepts an initialized project dir — one containing the config.toml that pjm init always writes and the global store never has. Found by dogfooding 0.1.4 on this very repo.
Also fixed (caught by the 0.1.4 testing playground)
pjm precheck payment.py auth.py— checking named files now works from the CLI. The module docstring had advertised a--filesoption that was never wired up; files are now a positional argument (staged files remain the default).pjm search payment.pynow matches thelocationfield, so per-file lookups behave like precheck: attempts logged with--at payment.pyare findable by filename (previously search only scanned summary/notes/files).
Event schema
One new optional field: supersedes (event id) on decision events. Existing logs parse unchanged; older projectmem versions ignore the field. The 14-tool MCP surface is unchanged (add_decision gained an optional parameter; search_events/precheck_file outputs got richer). 23 new tests (81 total).
pjm visualize gets an Overview landing and a light "product" redesign — the whole dashboard now matches the projectmem brand, and the new first screen shows all four lenses at a glance. Opening pjm visualize used to drop you straight into the Story Map force graph and make you tab around to assemble a mental picture. 0.1.4 adds an Overview tab (now the default) that puts the four lenses — failure heatmap, ROI, project structure, and timeline — in a single 2×2 glance, and re-themes the entire dashboard from the old dark palette to a clean light theme drawn from the poster/brand colors.
New: Overview tab (default landing)
The first thing you see is now a calm, all-at-once summary instead of a graph you have to interpret:
Story Map → failure heatmap. The top files ranked by effort burned (failed attempts ×3 + mentions), bar colour deepening from blue to red with failure intensity. The one chart that answers "where is this project bleeding time?" without a single click.
ROI Dashboard → headline cards + a prevention-grade gauge. Tokens saved, debugging hours saved, USD saved, plus a real A+→F semicircle gauge bound to
pjm score(same single ROI model — no second source of truth). The gauge colour tracks the grade band.Project Map → compact node graph. A 10-node summary of
PROJECT_MAP.mdwith folders larger, and a red dashed ring on any file that has recorded failures — so structure and pain show up together.Timeline → swimlanes. issue / attempt / fix / decision as four horizontal lanes of dots across the project's real date range, with an auto-scaled month axis.
Each panel has an open ↗ link that jumps to the full interactive tab.
Light "product" re-theme across every tab
The dashboard's CSS variables were re-grounded in the projectmem brand palette (navy header, blue/teal/coral accents on a light surface). Story Map, ROI, Project Map (tree + graph), and Timeline were all audited for dark-only colours — invisible-on-light graph labels and the dark legend/tooltip cards are fixed — so the four detail tabs and the new Overview now read as one coherent app. The grade/score for the gauge is injected via a new {{SCORE_DATA}} payload built from calculate_score. No data-model or CLI-surface changes; pjm visualize flags are unchanged.
0.1.3
Six focused improvements: schema enrichment, secret redaction, the conda/venv hook fix (L-047), stack-aware PROJECT_MAP (L-048), MCP config printed at end of init (L-049), and a silent post-commit auto-capture (L-050). A metadata pass that lifts Glama tool-quality scores from 75% with one B-tool to a projected ~90% all-A; a privacy guardrail that scrubs accidentally-pasted credentials before they hit disk; a regression fix that restores the pre-commit warning for every conda / pyenv / venv user; and two pjm init UX additions that remove the two biggest first-run friction points — "what is this project?" and "how do I wire this up?".
pjm init now pre-populates PROJECT_MAP.md from your stack (L-048)
Before 0.1.3, PROJECT_MAP.md was a Status: not created yet placeholder that an AI session had to fill in by re-reading every manifest and folder — exactly the kind of token-burn projectmem is supposed to prevent. pjm init now reads pyproject.toml / package.json / Cargo.toml / go.mod directly and writes an actual map: project description, stack tags + frameworks + key libraries, main folders, and entry points. The Setup-Mode prompt becomes refining the map, not building it from zero. Skip with --no-stack-detect. Safety: only overwrites when the current map still contains the placeholder marker — a human- or AI-edited map is never clobbered.
pjm init now prints the MCP client config block at the end (L-049)
Every new user used to ask the same question right after pjm init: "OK, what JSON do I paste, and where?" The README had it, but six clicks deep. Now init ends with a copy-pasteable config block (absolute sys.executable baked in to dodge the Claude-Desktop / Cursor PATH-inheritance gotcha) plus the on-disk config-file paths for Claude Desktop, Cursor, Antigravity (legacy IDE), and Codex (TOML reminder included). Skip with --no-mcp-config. Notes the Antigravity v2 path may differ — the v1 path is the verified one.
Post-commit auto-capture no longer prints over the shell prompt (L-050)
The post-commit / post-merge hooks run pjm _auto-capture in the background (with &). Before 0.1.3 the snippet redirected only stderr (2>/dev/null), so the success line — "[projectmem] Auto-captured: …" — printed to stdout after git commit had already returned the prompt to the user. Visually it looked like the terminal was stuck waiting for input; users would press Ctrl-C to "recover" when in fact their keystrokes were already being captured by the shell. Now both streams are redirected (>/dev/null 2>&1 &), so the capture stays silent. Verify it ran via pjm show if you want to see the event.
Pre-commit warning silently no-op under conda / venv (L-047, fixed)
Pre-commit warning silently no-op under conda / venv (L-047, fixed)
The killer feature — git commit warning you about repeating a failed approach — was silently broken for the majority of Python users since 0.1.1. The installed hook relied on command -v pjm to find the binary at commit time. Git invokes hooks via a non-interactive bash, which does not run .zshrc / .bashrc, so conda / pyenv / venv PATH modifications were absent and the lookup quietly returned nothing. The hook ran, found no pjm, exited 0, and produced no output. Users saw a normal commit and assumed projectmem had no warning to give. In fact it had the warning, ready, in memory — and no way to deliver it.
The fix: install_hooks now resolves the absolute path to pjm (via shutil.which, falling back to sys.prefix/bin/pjm) at install time and bakes it into the hook as PJM_BIN="/abs/path/to/pjm". A runtime command -v fallback remains for the rare case where the install-time binary was later moved. The hook script's if [ -d ".projectmem" ] && [ -n "$PJM_BIN" ] guard means a stale path still degrades gracefully instead of erroring at commit time.
Verified end-to-end with a regression test that simulates git's non-interactive hook environment (stripped PATH, no shell init), and with a real git commit in a conda-installed setup — the warning now appears reliably. Users who installed projectmem before 0.1.3 need to run pjm hooks install once after upgrading to refresh the baked path.
Secret redaction on write (new)
projectmem stores event text verbatim in .projectmem/events.jsonl — that's the local-first promise. The flip side is that a careless paste ("the bug repros when I set OPENAI_API_KEY=sk-...") used to land that key on disk in plain text, often in a file that's then committed to git. Starting in 0.1.3, storage.append_event runs a conservative pattern scrubber across the event's user-supplied text fields (summary, notes, command, git_message, location) before anything touches disk. Matches are replaced with [REDACTED:<kind>] and a one-line stderr notice fires so the user knows redaction happened.
Patterns covered: OpenAI / Anthropic / OpenRouter sk-… keys, GitHub classic tokens (ghp_…, gho_…, ghu_…, ghs_…, ghr_…) and fine-grained PATs (github_pat_…), AWS access key IDs (AKIA…), Google API keys (AIza…), Slack tokens (xox[abprs]-…), Stripe live/test keys (sk_live_…, pk_live_…, etc.), JWTs (eyJ…), Bearer tokens, and PEM private-key block headers. Patterns are intentionally narrow — anchored to recognisable prefixes with minimum-length requirements — so ordinary debugging prose ("tried contain: layout", "forgot password reset flow") is never touched. 29 new tests pin both the true-positive and false-positive behavior.
Default on. Escape hatch: PROJECTMEM_NO_REDACT=1 skips scrubbing entirely (for debugging the redactor itself or for trusted offline contexts). Redaction is wrapped in a defensive try/except so a scrubber bug never blocks the primary write path — better a logged secret than a lost event in a tool whose job is logging.
MCP tool schema enrichment
No behavior changes; this gives every tool's parameters real description fields in the JSON schema (via Pydantic Field annotations through FastMCP), plus a one-line side-effect / read-only callout in each tool's docstring.
The Glama tool-quality evaluator was flagging Parameters at 1-2/5 across the entire surface because parameters had only names and types — agents had to guess what summary, location, outcome, library, tokens, focus, query, limit, issue_id, and file_path actually meant. They now have explicit descriptions and, where useful, schema constraints:
search_events.limitnow enforces1 ≤ limit ≤ 100in the schema.record_attempt.outcomenow enforces the pattern^(worked|failed|partial)$— invalid outcomes are rejected at the schema layer, not silently coerced.get_context.tokensnow enforces100 ≤ tokens ≤ 20000.
Every tool also gained one short docstring line stating side effects (e.g., "Appends an issue event to .projectmem/events.jsonl, creates an issue file in .projectmem/issues/, updates summary.md, and marks this issue as the active one") or its read-only nature. The MCP instructions= block was already strong and was left alone.
What didn't change: function bodies, return values, defaults, parameter names, parameter order, the 14-tool surface, the CLI, storage layout, hooks, watcher, and the test suite (still 12/12 passing). All existing MCP client configurations continue to work without modification.
Bumped to 0.1.3 to publish the richer schema to both PyPI and the official MCP Registry. Detailed per-tool analysis and rollback plan in report/GLAMA_QUALITY_IMPROVEMENT_PLAN.md.
0.1.2
Metadata-only republish to satisfy the official MCP Registry's package-ownership check. Added the line mcp-name: io.github.riponcm/projectmem to README.md so the registry can verify the GitHub namespace owner (riponcm) controls the published PyPI package. No code or behavior changes.
0.1.1
First stable public release. v0.0.6 was the intelligence layer; v0.1.1 is the polish + cross-client verification + cross-project memory wiring that makes it ready for general use. 46 lessons logged from soft-launch dogfooding and live verification, a 22-item batch polish-pass + 6 follow-up fixes, plus end-to-end verification across all 4 major MCP clients (Antigravity, Claude Desktop, Cursor, Codex) and across 3 language ecosystems (JavaScript, Python, Go) for cross-project memory.
This is the version we're comfortable putting on real PyPI. The CLI surface, MCP tool list, event schema, and .projectmem/ layout are stable from here — future minor versions (0.2.0, etc.) will add features, not break the existing contract.
Cross-project memory — wiring restored (the big one)
The "Cross-Project Knowledge" diagram on the landing page promised library gotchas to propagate machine-wide. Verification revealed it shipped half-built — three concrete gaps fixed before 0.1.1:
Auto-promote never fired on writes (L-043) —
auto_promote_eventexisted inglobal_memory.pybut no write path called it. Everyrecord_attempt/add_decision/add_note(MCP and CLI) silently skipped global promotion. Wired intostorage.append_eventso every write surface now promotes consistently. Plus word-boundary library matching (no more "gin" inside "imagineering") + stack-filter (a vite project mentioning "next" in plain English no longer creates a fake Next.js gotcha).Library set was JS/Python-only (L-045) — the hardcoded
PROMOTABLE_LIBRARIESset covered React/Vue/Next/Vite/FastAPI/Django and not much else. Go, Rust, Java, Ruby, .NET, mobile — all silently dropped at promotion time. Replaced with a self-curating cache at~/.projectmem/global/.promotable.json: every librarydetect_stackever sees in a manifest on this machine becomes promotable. A Go user'sgindecisions now propagate exactly like a React user'sviteones.Every library mention was treated as a gotcha (L-046) —
add_decision("Use FastAPI for this project")used to pollute the global store with project-local setup choices. Now there's an explicit signal filter: failed/partial attempts always promote (the outcome is the signal); decisions/notes only promote when their summary opens withgotcha:/lesson:/warning:/caution:/pitfall:/avoid:/don't/do not/never/bug:. Result on the test cycle: signal-to-noise went from 14% to 100%.
End-to-end verification across globaltest/proj-react, proj-next, proj-python, and proj-go: a vite gotcha logged in proj-react surfaces in proj-next with source_project attribution, stays out of proj-python and proj-go's responses, and a gin gotcha logged in proj-go promotes correctly under the new library cache + signal filter. Full results in report/CROSS_PROJECT_TEST_PLAN.md.
Bug fixes (the launch blockers)
MCP stdio integrity (L-009 + L-010) — write tools used to corrupt the JSON-RPC stream via
typer.echo, and one bad call would kill the entire session. Every tool body now runs inside a stdout-suppression context + a@safe_toolexception wrapper. Five consecutive write-tool calls survive cleanly in any client.MCP project-root discovery (L-005) — server used to fail with "No .projectmem directory found" when the MCP client launched it from its own CWD. New parent-walk fallback (like git does for
.git/), plus a--rootflag andPROJECTMEM_ROOTenv var for explicit pinning.Silent issue misattribution (L-027a) —
pjm attemptafter apjm fixused to silently attach to whatever issue was still open. Now uses a.projectmem/.current_issuemarker, a 5-minute time-fence on the fallback, and an explicit--issue <id>flag.Partial attempts dropped from summary (L-027b) —
summary.mdonly surfacedfailedattempts;partialoutcomes vanished even though they contained valuable signal. Now both render.Project purpose stuck on placeholder (L-037) —
summary.md's Project purpose section never escaped its init placeholder. Now auto-syncs fromPROJECT_MAP.md's## Project purposesection on every regeneration.
Behavioral fixes
AI workflow alignment (L-028 / L-031 / L-036) — three surfaces used to tell the AI different things about the session-start trio (MCP
instructions=field,CLAUDE.mdbridge,AI_INSTRUCTIONS.mdtemplate). All three now mirror each other:get_instructions→get_summary→get_project_map, plus the "never edit.projectmem/files directly via filesystem write" rule.AI_INSTRUCTIONS.md rewrite (L-036) — was CLI-only and out of sync with MCP. Now lists both MCP tools and CLI commands per trigger, distinguishes Setup Mode vs Maintenance Mode by concrete placeholder phrases (not "files populated"), and gives AI clients an imperative 6-step Setup procedure.
pjm initwrites a CLAUDE.md bridge (L-004f) — marker-bounded block at project root, idempotent on re-init. AI clients (Claude Code, Antigravity, Cursor) honor the memory layer by default.
Quality of life
ROI surfaces reconciled (L-025d) —
pjm statsandpjm scoreused to report differenttokens_savednumbers.pjm statsis now a thin presentation layer overscore.calculate_score— single source of truth.pjm score --verboseworks (L-025a) — was a no-op; now appends per-component event detail so you can audit exactly why the score is what it is.pjm stats --format json(L-025b) — CI-friendly JSON output matchingpjm score's format flag.pjm visualize --output / --no-open(L-024b) — choose where the HTML lands, skip auto-open (CI / headless).pjm search --regex(L-027c) — opt-in regex / OR-pattern search.pjm attempt --issue <id> / --auto-issue— explicit issue attribution + auto-creation of an implicit parent issue when none is open.pjm wrapFile Gotchas filter (L-022a) — auto-backfill events used to pollute the gotchas section with the same generic note per file. Filtered out.pjm globalergonomics (L-026b) —pjm global add "..." --library Xauto-routes toadd-gotcha. Plus--format jsononlist/detect(L-026c).Framework detection word-boundary fix (L-026a) —
pjm global detectno longer flagsgin(Go framework) when scanning a React project witheslint-plugin-react(substring match onplugin). Word-boundary regex now.Timestamp normalization (L-024a) —
pjm visualize's Timeline used to show "INVALID DATE" on auto-backfill events. All events normalized to ISO-Zulu on write + defensive parser in the dashboard.HIGH CHURN counter via git log (L-023a) — was reading from event log (stale); now sources from
git log --since=N.days.ago(live).
Cross-client MCP verification
All four major 2026 MCP clients tested end-to-end against a real project:
Antigravity — first client dogfooded; entire v0.0.6 bug list was found here, batch fix landed, every category re-verified.
Claude Desktop — must use Auto mode (Plan mode bypasses MCP), pass project root via
--rootin args (thecwdJSON field is silently ignored in current builds), worktree mode requires init inside the worktree.Cursor — same
--rootworkaround for the cwd-ignored bug. Per-project.cursor/mcp.jsonsupported.Codex — config is TOML at
~/.codex/config.toml(not JSON), UI Save button can silently fail (edit the file directly), set reasoning effort tomediumor higher for the full session-start trio.
Docs
README hero + demo images now hosted on a separate public asset repo (
github.com/projectmem/projectmemdoc) and referenced viaraw.githubusercontent.com. PyPI's README renderer can fetch them regardless of the projectmem repo's visibility. Animated GIF (8-frame, Safari-safe) replaces the SVG that PyPI rendered inconsistently.All 4 MCP clients' UI navigation paths documented in README + Guide (Settings → Developer → ..., Settings → Tools & MCPs → ..., etc.).
First-run permission prompts callout — documented as normal MCP-client behavior, not a bug.
Stale MCP server process troubleshooting — common gotcha when iterating on MCP config; documented diagnostic commands + recovery.
Carried over to v0.0.8 backlog
L-038 (
pjm watchduplicate churn events for one incident) — known cosmetic noise; documented fix queued for v0.0.8 polish.Universal AI Bridge (
pjm bridge install,pjm doctor) — multi-bridgepjm initwriting.cursor/rules/,.github/copilot-instructions.md,AGENTS.md, etc.
0.0.6
projectmem transforms from a passive memory logger into an active intelligence layer. Zero-friction capture, intelligent injection, provable ROI, cross-project knowledge.
Major Features
Auto-Capture Engine — git hooks now classify commits into the right event types automatically.
revertbecomes a failed attempt,fix:becomes a fix event,feat:becomes a note,BREAKINGbecomes a decision. Zero manual logging required for common cases.Pre-Commit Warnings (
pjm precheck) — the killer feature: warns you BEFORE you commit if you're about to repeat a failed approach, modify a high-churn file, or touch an unresolved issue. No other AI tool can do this — it requires the memory layer underneath.Smart Context Injection (
pjm wrap) — wraps your AI agent (Claude, Cursor, Aider) and auto-injects a token-budgeted context block before the session starts. Inject intoCLAUDE.md,.cursorrules, or clipboard.Failure Prevention Score (
pjm score) — quantifiable ROI metric with letter grade (A+ through F). Tracks failed approaches on record, decisions documented, debugging hours saved, tokens saved, USD saved. Outputs as terminal display, JSON for CI, or shields.io badge for README.Context Budget Optimizer (
pjm context) — generate token-budgeted project memory tailored to file focus and time window. Four compression levels (full / compressed / ultra / emergency). Git-aware — boosts events for files currently being worked on.Cross-Project Global Memory (
pjm global) — knowledge that follows the developer across projects. Stores patterns, library gotchas, and stack preferences in~/.projectmem/global/. Auto-detects stack onpjm init(Python, JS, Rust, Go, Java) and inherits relevant gotchas. Export/import for team sharing.
Enhancements
Auto-installed hooks on
pjm init— git hooks are installed automatically. No need to rememberpjm hooks install. Opt out with--no-hooks.Three hooks now installed:
post-commit,post-merge, andpre-commit(forpjm precheck).Safe hook installation — appends to existing hooks with clearly-marked snippets. Never overwrites. Clean uninstall removes only projectmem's section.
Visualization overhaul for auto-capture:
New header stat: auto-captured event count
AUTObadge on auto-captured events in TimelineNew ROI cards: Manual / Auto-captured / Would Be Lost / Auto-capture Rate
New Capture Sources donut chart (git commits vs reverts vs manual)
New File Churn heatmap (top 10 files by activity, color-coded severity)
Manual/Auto filter pills in Timeline
Dashed borders + transparency on auto-captured nodes in Story Map
Event model extension:
auto_captured,capture_source,capture_confidence,git_message(backward compatible).AI_INSTRUCTIONS.md template updated with auto-capture awareness section telling AI agents what's auto-captured vs what still needs manual logging.
Hidden
_auto-capturecommand for internal use by git hooks.
Real-Time File Watcher (pjm watch)
New command:
pjm watch [--daemon|--stop|--status]— opt-in real-time file watcher that detects high churn (4+ edits to the same file within 10 min) and auto-logs them as churn-detector events.Auto-starts on
pjm initin interactive terminals — zero-touch experience. Skipped in CI/CD, piped output, and non-TTY environments to avoid zombie daemons.Battery-aware: idles when no activity, gitignore-aware, single-instance lock via PID file at
.projectmem/watch.pid, graceful SIGTERM shutdown.Project Map tree view: new horizontal dendrogram (D3 cluster + bezier links) with zoom/pan, toggleable against the existing force-graph view.
Opt-out flag:
pjm init --no-watchfor power users or battery-conscious environments.
Zero-Touch Setup
Auto-backfill on
pjm init— automatically ingests the last 20 git commits as classified events. Fresh repos = silent no-op. Existing repos = instant dashboard with real data. Opt out withpjm init --no-backfill.Auto-installed git hooks + auto-started watcher + auto-backfilled history + auto-inherited global memory all happen in a single
pjm initcall. Frompip installto active memory in two commands.
MCP Server Expansion (8 → 14 tools)
Native MCP server now exposes intelligence-layer capabilities to AI agents, not just raw memory:
precheck_file(path)— AI can self-check a file's failure history before proposing changes (turns memory into proactive judgment).get_issue(id)— lazy-load one specific issue file for token efficiency.search_events(query, limit)— plain-text search over the event log instead of loading the full summary.get_score()— AI can report the prevention score with hours/tokens/dollars saved.get_context(tokens, focus)— AI requests an on-demand token-budgeted context block.get_global_gotchas(library)— AI queries cross-project memory for library-specific lessons.
Existing 8 tools (get_summary, log_issue, record_attempt, record_fix, add_decision, add_note, get_instructions, get_project_map) unchanged.
Privacy & Security
SECURITY.mdat repo root with vulnerability disclosure policy and threat model.Privacy & Security section in the user guide explaining the team-memory-via-git pattern, local-first guarantees, prompt-injection considerations, and uninstall path.
Cleaner gitignore default — only
events.jsonl,watch.pid,watch.logare ignored by default, allowingsummary.md/PROJECT_MAP.md/AI_INSTRUCTIONS.mdto be shared via git. Opt into total privacy by adding.projectmem/to.gitignore.
Dependencies
watchdog>=4.0promoted from optional to required dependency — required for the auto-started file watcher. Adds ~70KB to install size.
Breaking Changes
None — v0.0.6 is purely additive. Existing events.jsonl files continue to work without modification.
0.0.4
Major Feature: Complete overhaul of
viz.htmlinto a stunning, single-page Tabbed Dashboard (Story Map, ROI Dashboard, Project Map, Timeline).Major Feature: Automated D3.js Architecture Graph generation—
pjm visualizenow natively parses your MarkdownPROJECT_MAP.mdinto an interactive node graph with zero extra AI tokens.Enhancement: Upgraded dashboard aesthetic to a high-end, soothing "Midnight Blue & Indigo" professional palette to reduce developer eye strain.
Enhancement: Added explicit documentation and guarantees in the README for configuring native MCP vs Custom System Prompts for 100% hands-free workflows.
0.0.3
Major Feature: Native MCP Server (
pjm-mcp) for direct integration with Claude Desktop and Cursor.Major Feature: Interactive D3.js visualization (
pjm visualize) showing project story and technical debt heatmap.Major Feature: Auto-backfill (
pjm backfill) to ingest git history into project memory.Major Feature: Token ROI Dashboard (
pjm stats) to calculate and visualize AI tokens saved.Major Feature: 3-Level Auto-Tracking system for hands-free memory management:
Level 1: Trigger-based
AI_INSTRUCTIONS.mdwith MANDATORY rules that force AI agents to log work automatically.Level 2: MCP server with built-in system prompt and proactive tool descriptions (MANDATORY/IMMEDIATELY language).
Level 3: Auto-capture Git Hooks that log
revert,fix:,feat:, andBREAKINGcommits passively.
Enhancement: Added
pjmalias globally to prevent conflicts with other system tools.Enhancement: Added location metadata (
--at) support to all logging commands.Enhancement: Added
get_instructions()MCP tool so AI agents can read project rules natively.Enhancement: Added "Maintenance Mode" logic to
AI_INSTRUCTIONS.mdto prevent redundant structural mapping.Fix: Corrected JavaScript syntax error in D3.js forceLink chain that prevented visualization rendering.
0.0.2
Add
.projectmem/AI_INSTRUCTIONS.mdduring initialization.Add
.projectmem/PROJECT_MAP.mdas the AI-created structural map placeholder.Add
pm instructionsto print the project AI memory protocol.Add
pm mapto print the project map.Improve the initial
summary.mdso new projects are not blank.
0.0.1
Initial local MVP scaffold.
94 files · 87 not served here
- .github/FUNDING.yml (not served here)
- .gitignore (not served here)
- ATTRIBUTION.md
- AUTOGINEER-RUNS.md
- CHANGELOG.md
- CITATION.cff (not served here)
- LICENSE
- README.md
- SECURITY.md
- TUTORIAL.md
- brand/dashboard-global.png (not served here)
- brand/dashboard-projectmap-flow.png (not served here)
- brand/dashboard-showoff-universe.png (not served here)
- brand/dashboard-timeline-spine.png (not served here)
- brand/demo-precheck.svg (not served here)
- brand/projectmem-mark-1024.png (not served here)
- brand/projectmem-mark-2048.png (not served here)
- brand/projectmem-mark-512.png (not served here)
- brand/projectmem-wordmark-1600.png (not served here)
- brand/projectmem-wordmark-2400.png (not served here)
- brand/projectmem-wordmark-400.png (not served here)
- brand/projectmem-wordmark-800.png (not served here)
- llms.txt (not served here)
- pyproject.toml (not served here)
- src/projectmem/__init__.py (not served here)
- src/projectmem/cli.py (not served here)
- src/projectmem/commands/__init__.py (not served here)
- src/projectmem/commands/attempt.py (not served here)
- src/projectmem/commands/auto_capture.py (not served here)
- src/projectmem/commands/backfill.py (not served here)
- src/projectmem/commands/brief.py (not served here)
- src/projectmem/commands/context.py (not served here)
- src/projectmem/commands/dashboard.py (not served here)
- src/projectmem/commands/decision.py (not served here)
- src/projectmem/commands/doctor.py (not served here)
- src/projectmem/commands/export.py (not served here)
- src/projectmem/commands/fix.py (not served here)
- src/projectmem/commands/global_cmd.py (not served here)
- src/projectmem/commands/hooks.py (not served here)
- src/projectmem/commands/init.py (not served here)
- src/projectmem/commands/instructions.py (not served here)
- src/projectmem/commands/log.py (not served here)
- src/projectmem/commands/map.py (not served here)
- src/projectmem/commands/note.py (not served here)
- src/projectmem/commands/plan.py (not served here)
- src/projectmem/commands/precheck.py (not served here)
- src/projectmem/commands/project.py (not served here)
- src/projectmem/commands/regenerate.py (not served here)
- src/projectmem/commands/score.py (not served here)
- src/projectmem/commands/search.py (not served here)
- src/projectmem/commands/show.py (not served here)
- src/projectmem/commands/stats.py (not served here)
- src/projectmem/commands/visualize.py (not served here)
- src/projectmem/commands/watch.py (not served here)
- src/projectmem/commands/wrap.py (not served here)
- src/projectmem/global_memory.py (not served here)
- src/projectmem/glyphs.py (not served here)
- src/projectmem/mcp_server.py (not served here)
- src/projectmem/models.py (not served here)
- src/projectmem/project_registry.py (not served here)
- src/projectmem/redaction.py (not served here)
- src/projectmem/resolver.py (not served here)
- src/projectmem/search.py (not served here)
- src/projectmem/staleness.py (not served here)
- src/projectmem/storage.py (not served here)
- src/projectmem/structure.py (not served here)
- src/projectmem/summary.py (not served here)
- tests/conftest.py (not served here)
- tests/test_agent_guidance.py (not served here)
- tests/test_attempt.py (not served here)
- tests/test_auto_capture.py (not served here)
- tests/test_client_config_discovery.py (not served here)
- tests/test_dashboard.py (not served here)
- tests/test_global_mcp.py (not served here)
- tests/test_hooks_path.py (not served here)
- tests/test_init.py (not served here)
- tests/test_init_ux.py (not served here)
- tests/test_instructions_refresh.py (not served here)
- tests/test_location_parsing.py (not served here)
- tests/test_log.py (not served here)
- tests/test_mcp_server.py (not served here)
- tests/test_plan.py (not served here)
- tests/test_redaction.py (not served here)
- tests/test_search.py (not served here)
- tests/test_showoff.py (not served here)
- tests/test_staleness_performance.py (not served here)
- tests/test_staleness_repair.py (not served here)
- tests/test_structure.py (not served here)
- tests/test_summary.py (not served here)
- tests/test_v014_features.py (not served here)
- tests/test_visualize.py (not served here)
- tests/test_watch.py (not served here)
- tests/test_windows_console_encoding.py (not served here)
- tests/test_windows_hooks_and_context.py (not served here)
Advantages
- Prevents agents from repeating failed debugging attempts by providing pre-commit warnings. evidence: README.md
- Local-first architecture ensures no data leaves your machine, with no cloud or telemetry. evidence: README.md
- Reduces token usage by providing persistent context that outlives individual AI sessions. evidence: README.md
Limitations
- Requires manual initialization for each project repository. evidence: README.md
- Memory is stored locally, meaning it is not shared across different machines unless the repository is synced. evidence: README.md
Security
Risk level: Low risk
This brain runs
MCP servers (2)
README.md(projectmem):/opt/anaconda3/bin/python -m projectmem.mcp_serverREADME.md(projectmem):python -m projectmem.mcp_server
Install scripts (1)
src/projectmem/commands/init.py:python src/projectmem/commands/init.py
- Low risk: scripts-present (src/projectmem/commands/init.py)
Attribution and licence
- Created by
- Ripon Chandra Malo
- Declared authors
- Ripon Chandra Malo (declared in pyproject.toml)
- Ripon Chandra Malo (declared in CITATION.cff)
- Publisher
- Ripon Chandra Malo (Community)
- Source
- riponcm/projectmem · commit a405361
- Licence
- MIT License (files served here)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Ripon Chandra Malo.
Install from source
87 of its files are not served here, so there is no ZIP. riponcm/projectmem at a405361