@eleboucher/pi-memini
v0.7.2
Published
Shared cross-session memory for the Pi coding agent, backed by a memini service.
Readme
memini + Pi
Pi is an open-source coding agent with a first-class
extension API. Pi has no built-in
MCP — capabilities are added through extensions — so memini ships a native
extension in plugin/ that makes memory both automatic and
tool-callable, with no MCP layer required.
Recommended: the memory extension
What it wires:
before_agent_start— searches memini for the user's prompt and injects the matches as a persistent context message before the agent runs. It excludes this session's own captured turns (already in live context), so they aren't echoed back a turn behind; past sessions still recall.agent_end— once the agent finishes a prompt, stores the completed user/assistant turn back into memini (episodic, taggedpi, with the session id) so it can be recalled later.- Explicit tools — modeled on the tool set Claude Code gets from memini's
MCP server, registered natively via
pi.registerTool:memory_recall,memory_list,memory_remember,memory_forget. The model can call them on demand even though the automatic loop already runs.
Install
The extension is published to npm as
@eleboucher/pi-memini.
Pi has no init/scaffold command — extensions are just discovered from known
locations or declared in config. Pick one:
Project / global settings — add the package to settings.json
(.pi/settings.json for one project, or ~/.pi/agent/settings.json globally):
{
"packages": ["npm:@eleboucher/pi-memini"]
}Discovery folder — Pi auto-discovers and hot-reloads extensions in
~/.pi/agent/extensions/ (global) or .pi/extensions/ (project-local). Drop
the built dist/index.js (or the src/index.ts source) there.
Quick test — point Pi at a local checkout for one run:
pi -e ./integrations/pi/plugin/dist/index.jsConfigure
All config is via environment variables in the shell that launches Pi (secrets stay out of any file):
| Env var | Default | Purpose |
| -------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------- |
| MEMINI_BASE_URL | http://localhost:8080 | memini REST base URL |
| MEMINI_NAMESPACE | unset (server handshake decides) | machine-local namespace override; the offline escape hatch when the server is unreachable |
| MEMINI_HOME | unset | caller's personal namespace, sent as X-Memini-Home; unset = no home leg |
| MEMINI_RECALL | on | 0/false disables recall-before-turn |
| MEMINI_CAPTURE | on | 0/false disables capture-after-turn |
| MEMINI_RECALL_LIMIT | 3 | max memories injected per turn |
| MEMINI_INJECT_RECALL_MAX_TOK | 0 | hard ceiling on recall-block tokens (0 = unbounded); the tail is dropped with a footer |
| MEMINI_INJECT_RECALL_MIN_SCORE | 0 | fused-score floor (>=) sent as min_score to /v1/search |
| MEMINI_INJECT_LABELS | — | comma-separated bullet labels: tier, confidence, age |
| MEMINI_TIMEOUT_MS | 30000 | per-request timeout |
| MEMINI_FALLBACK | on | 0/false surfaces errors instead of degrading silently |
| MEMINI_API_KEY | — | bearer token, if memini needs auth (sent as Authorization: Bearer …) |
| MEMINI_REQUIRE_HTTPS | — | 1 refuses to send the token over plaintext HTTP |
The namespace itself is resolved by the memini server, not this extension:
at the first turn the extension performs the config handshake
(POST /v1/handshake), sending the project's facts (git remote, toplevel,
cwd basename) and using whatever the server resolves — a pin recorded for this
project, MEMINI_NAMESPACE if exported, or derivation from the facts (repo
name, then toplevel basename, then cwd basename). The result is memoized in
memory for ten minutes. When the server is unreachable, the extension degrades
to the same chain locally: MEMINI_NAMESPACE, else git/cwd derivation — which
is why the env var is best thought of as the offline escape hatch, not the
primary lever.
Commands
| Command | What it does |
| ------------------ | ------------------------------------------------------------------------------- |
| memini:status | Effective settings, the resolved namespace and where it came from, warnings |
| memini:namespace | Show, set, or clear the server-side namespace pin for this project |
memini:status exists because a list of values is not enough to debug a namespace
problem. It shows provenance (<- env vs <- server vs (default)), so a
MEMINI_NAMESPACE exported once from a shell profile — which pins every repo on
the machine to one namespace — shows up as a warning rather than as a mystery.
Secrets are redacted.
The namespace pin
memini:namespace # show the namespace and where it came from
memini:namespace acme/api # pin this project to acme/api
memini:namespace --clear # back to automatic resolutionThe pin lives on the memini server (PUT/DELETE /v1/pins), keyed by the
project's git remote and/or toplevel path — so it follows you across machines,
and every client that handshakes for this project (Claude Code, this extension,
memini doctor) resolves the same value.
A pin beats MEMINI_NAMESPACE at handshake time, deliberately: a globally
exported MEMINI_NAMESPACE is exactly the problem a pin exists to solve, so if
the environment won, the command would silently do nothing on the machines that
need it. Setting or clearing a pin takes effect on the next turn — the write
drops the extension's in-memory handshake memo, so there is no restart or
ten-minute wait.
Because pins are server-side, setting one needs the server reachable. For an
offline, machine-local override, export MEMINI_NAMESPACE instead.
Build & test
cd integrations/pi/plugin
npm install
npm run build # esbuild bundle -> dist/index.js
npm test # bundle test (node --test) + pure-helper unit tests (tsx --test)Alternative: MCP wire
Pi can also reach memini's memory_* tools over MCP, but — unlike Claude Code
or Codex — Pi has no native MCP client, so you first need an MCP extension for
Pi (e.g. the one prewired in the my-pi
distribution), then point it at memini's server: http://<host>:8080/mcp
(remote) or memini mcp (stdio). The native extension above is simpler and adds
the automatic recall/capture loop on top of the tools, so prefer it.
