π΅π lapis β pencil, to write πͺ¨ lapis β stone, precious gem π§ LaPis β memory for Pi
Persistent memory for the Pi coding agent. One SQLite database, zero cloud dependencies, zero API keys.
curl -fsSL https://raw.githubusercontent.com/genegulanesjr/PiMemoryExtension/main/install.sh | bashRestart Pi and memory auto-wires on session start.
- Remembers across sessions β decisions, bugfixes, patterns, discoveries persist
- Auto-injects context β next session starts with relevant memories loaded
- Code indexing β web-tree-sitter (WASM) AST parses JS/TS/Go/Python/Rust/SQL files, searchable by issue description
- Trust scoring β memories linked to changed code lose trust; stable code boosts it
- Deduplication β trigram overlap prevents duplicate saves (β₯85% auto-merges, 60β84% warns)
- Workspaces β formal project isolation with create/list/archive
- Session lifecycle β auto-recovery of incomplete sessions, trust recovery on close
- Zero servers β single Node.js CLI + SQLite, called on demand by Pi. Zero Python dependency.
- Dream Cycle β every 10 sessions, cleans superseded, zero-recall, stale corrections, and replaced configs
- Update & delete β update memories in-place instead of spawning correction entries
| Command | Purpose |
|---|---|
save |
Save an observation (decision, bugfix, pattern, etc.) |
update --id |
Update an existing observation in-place by ID |
delete --id |
Soft-delete an observation by ID (recoverable) |
search |
FTS5 full-text search with hybrid ranking |
search --include-code |
Search both memories AND indexed code symbols |
context |
Load session context by project |
get |
Retrieve a single observation by ID |
| Command | Purpose |
|---|---|
index-repo --path |
Index a local folder with tree-sitter |
reindex-repo --repo |
Incremental reindex via mtime |
search-code --query |
FTS5 BM25 search over code symbols |
get-code-source --repo --file --name |
Byte-accurate source retrieval |
list-code-repos |
List indexed code repos |
remove-code-repo --repo |
Remove an indexed code repo |
| Command | Purpose |
|---|---|
import-graph --repo |
Import dependency graph with recursive traversal |
call-hierarchy --symbol --repo |
Call graph hierarchy (callers/callees) |
blast-radius --symbol --repo |
What breaks if a symbol changes |
dead-code --repo |
Find unused code |
complexity --repo |
Cyclomatic complexity per function |
outline --repo --file |
File symbol outline (classes, methods, standalone) |
churn --repo |
Git commit frequency metrics |
| Command | Purpose |
|---|---|
hotspots --repo |
Top symbols by complexity Γγ°γ€ churn (bug risk) |
cycles --repo |
Dependency cycles via Tarjan SCC |
importance --repo |
Symbol PageRank on call graph |
coupling --repo |
Afferent/efferent/instability per file |
extractable --repo |
Refactoring candidates |
hierarchy --symbol --repo |
Class hierarchy from parent_name |
signal-chains --repo |
Detect HTTP/CLI gateways and trace call chains |
layer-violations --repo |
Check import rules against declared architecture layers |
| Command | Purpose |
|---|---|
index-docs --path --name |
Index a markdown doc tree |
reindex-docs --repo |
Re-index a doc repo |
doc-search --query --repo |
Full-text search across doc sections |
doc-outline --repo --file |
Section hierarchy outline |
backlinks --repo --path |
Find all docs that link TO a given doc |
broken-links --repo |
Find broken internal doc links |
glossary --repo --term |
Look up glossary terms |
tutorial-path --section --repo |
Reconstruct ordered tutorial chain |
code-examples --query --repo |
Search fenced code blocks by content |
doc-orphans --repo |
Find sections with zero inbound links |
doc-coverage --repo |
Which code symbols have documentation coverage |
stale-pages --repo |
Find docs modified since last index |
doc-duplicates --repo |
Find duplicate sections by content hash |
| Command | Purpose |
|---|---|
list-workspaces |
List all workspaces |
create-workspace --name |
Create a workspace |
archive-workspace --name |
Archive a workspace (soft, data preserved) |
| Command | Purpose |
|---|---|
compact |
Prune dead links, decay trust, VACUUM, optimize FTS5 |
dream |
Dream Cycle β clean stale memories (not just old). Auto-runs every 10 sessions |
stats |
Database statistics |
list-projects |
List all known project names |
Unlike compact (housekeeping: vacuum, FTS optimize), the Dream Cycle targets staleness β information that is no longer accurate or useful:
| Phase | What it cleans | Why it's stale |
|---|---|---|
| Superseded | Memories with duplicate/supersedes relations |
A newer memory replaces it |
| Stale auto-progress | progress & accomplished types with zero recall |
Never useful, just noise |
| Stale auto-detected | Auto-detected decisions with zero recall + low trust | Pattern-matched junk never acted on |
| Stale corrections | Titles starting with "CORRECTION:" | Should've used update instead |
| Replaced configs | Superseded configs (e.g. "using frpc" β "switched to CF Tunnel") | Superseded setup info |
Age alone is NOT a signal. A 6-month-old valid decision stays. A 1-day-old superseded one goes.
Create ~/.pi/memory/config.jsonc to override defaults. The file supports JSONC (JSON with // and /* */ comments).
{
// Database file path (default: ~/.pi/memory/memory.db)
"db_path": "~/.pi/memory/memory.db",
// SQLite WAL autocheckpoint threshold (default: 1000)
"wal_autocheckpoint": 1000,
// SQLite busy timeout in milliseconds (default: 5000)
"busy_timeout_ms": 5000,
// Search ranking weights (must sum to 1.0)
"ranking": {
"fts_relevance": 0.4,
"recency": 0.3,
"trust": 0.15,
"recall": 0.15
},
// Deduplication thresholds (0.0 - 1.0)
"dedup": {
// Auto-merge duplicates at or above this similarity
"auto_merge_threshold": 0.85,
// Flag potential duplicates at or above this similarity
"warning_threshold": 0.60
},
// Run compaction every N sessions for a project (default: 5)
"compact_every_n_sessions": 5,
// Path to tier config file (default: ~/.pi/memory/tier.jsonc)
"tier_config_path": "~/.pi/memory/tier.jsonc"
}All options are optional β only include the ones you want to change. Missing values use built-in defaults.
- Node.js β₯ 22.5 (built-in
node:sqlite) or Node.js withbetter-sqlite3 - No Python dependency β code parsing uses web-tree-sitter (WASM) in-process
- No API keys or cloud services needed
JavaScript, TypeScript, TSX, Go, Python, Rust, SQL
The wire format (wire-format.js) uses compact encoding (columnar CSV with path interning, uniform-column hoisting, and internal-ID stripping) to reduce the token footprint of analysis responses inside Pi's context window.
Measured via bench/bench-tokens.js β runs real CLI commands against indexed repos, passes output through compactResponse(), and compares byte sizes.
| Tool | PiMemoryExtension | Aether (PCBuilder) |
|---|---|---|
| importance | 27% | 26% |
| hotspots | 48% | 0% |
| dead-code | 42% | 47% |
| coupling | 33% | 39% |
| extraction | 33% | 24% |
| cycles | 0% | 0% |
| import-graph | 24% | 20% |
| OVERALL | 36% | 37% |
| PiMemoryExtension | Aether (PCBuilder) | |
|---|---|---|
| Repo size | 38 files Β· 210 symbols | 154 files Β· 1,359 symbols |
| Raw JSON | 42.6 KB | 181.7 KB |
| Compact format | 27.1 KB | 114.3 KB |
| Bytes saved | 15.5 KB | 67.4 KB |
| Tokens saved | ~4,445 tokens | ~19,242 tokens |
Key findings:
- Dead-code sees the biggest gains (42β47%) β the
signalsandconfidencefields are uniform across rows and get hoisted, whilesymbol_idis stripped - Coupling benefits from prefix interning on shared file paths (33β39%)
- Larger repos trend higher β Aether (154 files, 1,359 symbols) edged out PiMemoryExtension (38 files) at 37% vs 36%
- Cycles and very small result sets show no savings (no homogeneous lists to encode)
All transforms are lossless round-trip β verified by 217 tests in test/wire-format.test.js.
Run it yourself:
node bench/bench-tokens.js
MIT