Skip to content

About

Simple but efficient enough memory extension for Pi.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

pi-workspace-memory

Project-scoped Markdown memory for the Pi coding agent — durable state, reports, and research as an auxiliary channel, independent from task-management systems.

Overview

 Pi session
 │
 ├─ session_start ──▶ snapshot memory context
 ├─ each turn ──────▶ inject: 5 newest state + 5 newest event records
 │                    (non-sensitive, maxTokens budget)
 │                    modes: message-append | system-prompt | off
 │
 ├─ tools — the whole lifecycle in 4
 │   memory_search ── scan content/metadata; no query = paged list + merge hints
 │   memory_read ──── @id or path → full | summary | knowledge
 │   memory_write ─── create | overwrite (+diff) | merge via supersedes
 │   memory_delete ── remove one record + reconcile metadata
 │
 └─ storage — per Git workspace (slug = git-root basename), local-only
     ~/.pi/memory-md/projects/<slug>/
     ├── records/         state.*.md  event.*.md   ← source of truth
     ├── .catalog.json    slim rebuildable index    (derived)
     └── .concepts.json   concepts + alias map      (derived)
  • state = still-current facts; event = append-only reports. Writes are identity-addressed: memory_write(path="events/foo.md", kind="event") stores records/event.foo.md (legacy state//events/ paths remain readable).
  • Cross-session updates: when another session (or any external write) changes project memory, the next turn appends a compact + ~ − notice listing changed ids (message-append mode). Append-only, so the system prompt and cached prefix stay byte-identical.
  • No init tool, no git layer: the first memory_write creates records/ and the written record only; reads never create a project directory. Renaming a project folder means moving projects/<slug>/ by hand.

Install

pi install git:github.com/sting8k/pi-workspace-memory

Local development does not need node_modules — runtime frontmatter support is built in:

pi -e /absolute/path/to/pi-workspace-memory/index.ts

Agent workflow

START: memory_search({ query: "task keywords", searchIn: "all" })
READ:  memory_read({ path: "@event.or-state-id", view: "knowledge" })
END:   memory_write({ path: "events/<topic>.md", kind: "event", summary, claims or facts })

Default to event; use state only for knowledge still true tomorrow. Update state in place instead of dated snapshots. Write durable findings only.

Write semantics

memory_write generates compact Markdown from structured fields — summary, concepts, claims, facts (JSON scalars/arrays; nested objects flatten to dotted keys), relations (@id links to existing records in the same project; unknown or self targets are rejected), notes (loaded only in full view). One machine-checkable facts block per record:

<!-- memory:facts:v1 -->
runtime.supported_modes = ["git", "npm", "local"]
relation.follow_up -> @event.next-investigation
<!-- /memory:facts -->
  • Field limits — a write is rejected when its summary exceeds 300 chars or its description exceeds 160; longer text belongs in claims, facts, notes, or content. Only the input of that write is checked, so already-stored long values stay readable. States hold current truth (overwrite in place); events record milestones, decisions, or results, not per-action logs.
  • Overwrite diff — replacing an existing record returns a compact line diff; fresh creates keep the plain response.
  • Sensitive — sensitive: true blocks auto-injection; secret-shaped values are flagged with a warning, not rejected. Detection follows gitleaks-style rules (provider token prefixes such as ghp_/sk-ant-/AKIA, private key blocks, credentials in URLs, and key = <high-entropy value> assignments); words like "password" or "ssh" alone do not count, so credentials written in prose need an explicit sensitive: true. The flag is recomputed on overwrite.
  • Supersedes (manual compaction) — supersedes: ["@a", "@b"] validates every ID before touching disk, writes the distilled record, then marks each target supersededBy. Hiding is derived at read time: a record stays hidden only while its superseder exists, deleting the superseder resurrects it, and writing to a hidden record clears the marker.
  • Pre-write dedup (state creates only) — an ID-family match (-v2, -final, date suffix, …) routes to an overwrite; concept containment (one set contains the other) or a similar ID (all distinctive ID tokens of one appear in the other, ignoring the project name and tokens most states share, e.g. arena vs arena-meta) rejects with a hint naming up to 3 similar records. forceCreate: true or a non-empty supersedes bypasses both. Events are never deduped.
  • Dated state IDs refused — dates belong to events; the error suggests kind: "event" or forceCreate: true.

memory_search hides superseded records by default (includeSuperseded: true to list them); list mode (no query) shows records newest first, limit (default 50, max 200) per page with a ready-to-copy next-page call, and on page 1 appends up to 5 cluster warnings — state records sharing a concept with 4+ members, never events — each with a ready-to-copy merge call. memory_read reads hidden records and appends a supersede note. In full and knowledge views it also lists "Referenced by": live records whose relations point at this one, grouped by relation name (up to 20). The catalog rebuilds entirely from frontmatter; no migration needed.

Configuration

~/.pi/agent/settings.json:

{
  "pi-workspace-memory": {
    "enabled": true,
    "localPath": "~/.pi/memory-md",
    "injection": "message-append"
  }
}

injection controls how memory reaches the agent:

  • message-append (default): on the first turn, a hidden message carries the project memory list; later turns append a compact + ~ − notice only when another session changed memory. The system prompt never changes, so provider prompt caching keeps working. The list itself is a session-start snapshot; /memory-refresh re-sends it.
  • system-prompt: the list is rebuilt from disk every turn and appended to the system prompt, so mid-session writes show up immediately, at the cost of prompt-cache misses whenever memory changes. No cross-session notices (the list is already current).
  • off: nothing is injected; the tools and commands still work, and the agent finds memory only through memory_search / memory_read.

The legacy pi-memory-md key is still honored as a fallback.

Development

npm test
npx tsc --noEmit
npx biome check <changed-files>

License

MIT

About

Simple but efficient enough memory extension for Pi.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages