pi-obsidian-memory 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +189 -125
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,180 +1,244 @@
1
1
  # pi-obsidian-memory
2
2
 
3
- Long-term memory for the [pi coding agent](https://pi.dev), stored in [Obsidian](https://obsidian.md) vaults.
3
+ Long-term memory for the [pi coding agent](https://pi.dev), stored as Markdown notes in
4
+ [Obsidian](https://obsidian.md) vaults.
4
5
 
5
- Coding agents forget everything between sessions. This extension gives pi durable memory that changes its future
6
- behavior, that you can read and edit in Obsidian, and that lives in any folder you choose. Memory is two layers of
7
- dedicated vaults: one global vault about you (how to address you, preferences, patterns, lessons) and one vault per
8
- project. Notes are plain Markdown, written in English whatever language you speak, and never deleted: retired notes
9
- move to `archive/`. See [docs/VISION.md](docs/VISION.md) for the decisions behind it.
6
+ pi forgets everything when a session ends. With this package it keeps what matters (your preferences, the rules and
7
+ decisions of each project, lessons from past mistakes) and brings it back when it helps. You can read and edit every
8
+ note in Obsidian.
10
9
 
11
- > **Status:** runs on Windows, macOS and Linux; CI runs the unit tests on all three and the end-to-end suite runs on
12
- > Windows. Requires Node 22.19 or later.
10
+ ## Contents
13
11
 
14
- ## Install
12
+ 1. [Requirements](#requirements)
13
+ 2. [Install](#install)
14
+ 3. [Set up](#set-up)
15
+ 4. [How it works](#how-it-works)
16
+ 5. [Settings](#settings)
17
+ 6. [Where things are stored](#where-things-are-stored)
18
+ 7. [Privacy](#privacy)
19
+ 8. [Troubleshooting](#troubleshooting)
20
+ 9. [Known limits](#known-limits)
21
+ 10. [Development](#development)
22
+
23
+ ## Requirements
24
+
25
+ | What | Why | Required? |
26
+ |---|---|---|
27
+ | [pi](https://pi.dev) | Runs the package. Tested with pi 1.3.0. | Yes |
28
+ | Node 22.19 or later | pi runs the package on it. | Yes |
29
+ | A chat model configured in pi | Saves notes in the background (capture) and finds notes for each message (memory agent). Any model pi can use works; a small, cheap one is enough for the memory agent. | Yes |
30
+ | A TypeSafe API key (`TYPESAFE_API_KEY`) | Jev, TypeSafe's classifier, decides which notes help each message and checks shell commands (see [Guards](#guards)). Get a key at [typesafe.ai](https://docs.typesafe.ai). Price: $0.042 per million input tokens. | Strongly recommended |
31
+ | [Obsidian](https://obsidian.md) | Reading and editing the notes. | No: notes are plain Markdown |
32
+
33
+ **Without `TYPESAFE_API_KEY`:** no notes are added to your messages (pi tells you once per session), and every
34
+ `bash` or `powershell` call is refused, because the shell-edit guard cannot check it.
15
35
 
16
- From git:
36
+ ### Setting the TypeSafe key
37
+
38
+ Set the environment variable before starting pi:
17
39
 
18
40
  ```bash
19
- pi install git:github.com/DavyMassoneto/pi-obsidian-memory
41
+ # macOS / Linux (add it to ~/.bashrc or ~/.zshrc to keep it)
42
+ export TYPESAFE_API_KEY=your-key
43
+ ```
44
+
45
+ ```powershell
46
+ # Windows (kept for new terminals)
47
+ setx TYPESAFE_API_KEY "your-key"
20
48
  ```
21
49
 
22
- From a local clone (loaded from that path, not copied; pi does not install its dependencies, so do it first):
50
+ The key can also be pi's stored credential for the `typesafe` provider. It is never written to this package's config.
51
+
52
+ ## Install
23
53
 
24
54
  ```bash
25
- npm install --omit=dev --prefix ./pi-obsidian-memory
26
- pi install ./pi-obsidian-memory
55
+ pi install npm:pi-obsidian-memory
27
56
  ```
28
57
 
29
- Both write the package to `~/.pi/agent/settings.json`; add `--local` to write `.pi/settings.json` instead (pi reads it
30
- only after project trust is granted). You can also add the source yourself:
58
+ Then restart pi. To try it for one run without installing: `pi -e npm:pi-obsidian-memory`.
31
59
 
32
- ```json
33
- {
34
- "packages": ["git:github.com/DavyMassoneto/pi-obsidian-memory"]
35
- }
36
- ```
60
+ `pi list` shows installed packages; `pi update` updates them.
61
+
62
+ ## Set up
63
+
64
+ 1. **Create the global vault.** Run `/memory-setup` in pi. It asks:
65
+ - where the global vault goes: a new folder, or an existing folder (an Obsidian vault you already have works);
66
+ - which chat model saves memory (capture); the model in use is the default;
67
+ - which chat model runs the memory agent; the list is sorted by price, cheapest first.
68
+
69
+ It shows what each choice sends where, then writes the config and the vault.
70
+ 2. **Give each project its own vault.** Open pi in the project folder and run `/memory-init`. A folder without a
71
+ project vault uses the global vault only.
72
+ 3. **Use pi as usual.** Say "remember that …" to save something. The rest happens on its own.
73
+
74
+ Run `/memory-setup` again at any time to open the settings screen. Both commands need pi's interactive mode.
75
+
76
+ ## How it works
37
77
 
38
- `pi list` shows installed packages. To try it for one run without installing:
39
- `pi -e git:github.com/DavyMassoneto/pi-obsidian-memory`. Pi's own docs cover every option (`docs/packages.md`).
78
+ ### Two vaults
40
79
 
41
- ## Quick start
80
+ - **Global vault:** about you. How to address you, your preferences, your patterns, lessons that apply everywhere.
81
+ - **Project vault:** about one project. Its rules, decisions, facts, open issues.
42
82
 
43
- 1. `/memory-setup` chooses where the global vault lives (create a new one or attach an existing folder), asks which
44
- chat model to use for capture (the model in use is the default) and for the memory agent (a small, cheap one is
45
- enough; the list is sorted by price, cheapest first), shows the
46
- warnings that apply and writes the config. Run again, it is the settings screen: every setting, and the projects,
47
- one select at a time.
48
- 2. In a project folder, `/memory-init` registers it with its own vault (created when missing). An unregistered folder
49
- gets the global vault only.
83
+ Each note holds one point. Its file name is the statement (for example `pnpm/Use pnpm, never npm.md`), the body has
84
+ the detail, and the frontmatter says what kind of memory it is:
50
85
 
51
- Both commands need pi's UI. Until `/memory-setup` is done there is no memory, and session start says so. The config is
52
- `pi-obsidian-memory/config.json` in the pi agent directory, outside the vaults; every key is described in
53
- [docs/config.md](docs/config.md). Nothing needs editing by hand. Changing a vault leaves the old one as it was.
86
+ ```markdown
87
+ ---
88
+ id: 01JABCDE7XKQ2M9V3RTN8WPZ5F
89
+ type: rule
90
+ created: 2026-10-04
91
+ updated: 2026-10-04
92
+ dontForget: true
93
+ ---
54
94
 
55
- ## What works
95
+ Use pnpm for every install and script. In CI: `pnpm install --frozen-lockfile`.
96
+ ```
97
+
98
+ Types: `rule`, `procedure`, `constraint`, `fact`, `issue`, `question`, `decision`, `lesson`, `goal`, `risk`, `idea`,
99
+ `reference`. Notes are written in English. They are never deleted: a retired note moves to `archive/`. The full format
100
+ is in [docs/vault-format.md](docs/vault-format.md).
101
+
102
+ ### What happens during a session
103
+
104
+ 1. **Session start.** Every note marked `dontForget: true` is loaded into pi's system prompt, in full.
105
+ 2. **Each message.** Jev decides whether stored memory would help. If yes, the memory agent searches and reads the
106
+ vaults, Jev scores each note it found, and the notes that help are added after your message, within
107
+ `context.budgetChars` (12,000 characters by default). A note you changed since it was loaded is sent again.
108
+ 3. **In the background (capture).** Every 10 assistant turns or 15 tool calls, once the session has at least three of
109
+ your messages, a review reads the new part of the conversation and saves, updates, archives or replaces notes. What
110
+ was not reviewed yet is also saved before pi compacts the session and when it ends. Capture never asks you
111
+ anything: what it is unsure of is not saved. The cadence follows
112
+ [pi-hermes-memory](https://github.com/chandra447/pi-hermes-memory).
56
113
 
57
- ### Tools
114
+ You change notes in Obsidian whenever you want. pi must read a note before changing it, and a note you edited since
115
+ then is never overwritten.
58
116
 
59
- The LLM uses these; the ones that write act only on your explicit request ("remember this"), and nothing in the prompt
60
- nudges it to save on its own.
117
+ ### Tools the agent uses
61
118
 
62
119
  | Tool | Does |
63
120
  |---|---|
64
- | `memory_search` | Finds notes in the global vault, the project vault or both (words and meaning together) |
65
- | `memory_get` | Reads a note in full with its problems, a folder's index, or anything under `archive/` |
66
- | `memory_add` | Saves a new note when you ask to remember something |
67
- | `memory_update` | Changes a note you asked to change; keeps its identity |
121
+ | `memory_search` | Finds notes by words and by meaning, in the global vault, the project vault or both |
122
+ | `memory_get` | Reads a note, a folder's index, or anything under `archive/` |
123
+ | `memory_add` | Saves a new note |
124
+ | `memory_update` | Changes a note |
68
125
  | `memory_archive` | Moves a note to `archive/` |
69
- | `memory_succeed` | Replaces a note with a successor; the two link to each other |
126
+ | `memory_succeed` | Replaces a note with a new one; both link to each other |
70
127
 
71
- `memory_update`, `memory_archive` and `memory_succeed` first need a `memory_get` of the same note in the session, and
72
- refuse a note that changed on disk since (an edit you made in Obsidian is never overwritten). A shipped skill,
73
- `obsidian-memory`, teaches the agent the layout, note types and these rules; it is never written to a vault.
128
+ The bundled skill `obsidian-memory` teaches the agent where notes go and how to use these tools.
74
129
 
75
130
  ### Commands
76
131
 
77
132
  | Command | Does |
78
133
  |---|---|
79
- | `/memory-setup` | First setup (vault and models); afterwards the settings screen for every setting and the projects |
80
- | `/memory-init` | Registers the working folder as a project with its own vault |
81
- | `/memory-doctor` | Reports problems in the vaults and the config; offers to complete blank notes after you confirm |
82
- | `/memory-export` | Writes both vaults to a new `.zip` (never overwrites; refuses a folder inside a vault) |
83
-
84
- `/memory-doctor` lists invalid notes, blank notes, duplicate ids, leftover temporary files and folders over the
85
- notes-per-folder limit, then checks the chat model, the embeddings and the decision model. Every other problem is left
86
- as it is on disk.
87
-
88
- ### Memory in the conversation
89
-
90
- - **Don't forget.** Mark a note with `dontForget: true` in its frontmatter. Every such note, in both vaults, is loaded
91
- in full at session start, with no limit.
92
- - **Relevant memory per message.** On each message, a fast decision model (a classifier that answers yes/no
93
- probabilities; it never searches or generates) decides whether memory is needed. If so, the memory agent (a chat model)
94
- searches and reads, the decision model scores each candidate note, and the relevant ones are added after your message. Earlier
95
- messages are never rewritten, so the prompt cache holds; a loaded note that changed is added again, marked as
96
- replacing the earlier copy. A loaded note that is gone is reported as "the note was archived or deleted"; one still
97
- there but unusable, as "the note is still there but cannot be used (<problem>)"; one whose state is unknown, an
98
- invalid config included, as "the note cannot be read (<problem>)". Session start also loads relevant notes within
99
- `context.budgetChars` (12,000 by default). The decision model is Jev by default (by API; it needs `TYPESAFE_API_KEY`;
100
- $0.042 per million input tokens, per docs.typesafe.ai), working from TypeSafe's starting cutoffs (0.5/0.5). Laya,
101
- run locally, is an optional choice that needs explicit `gateThreshold` and `relevanceThreshold`: it ships none
102
- because its gate measured at chance (see [decision-thresholds](docs/decision-thresholds.md)), so with Laya and no
103
- cutoffs per-message memory is off and each session tells you. Setup steps are in
104
- [Running the decision model](docs/config.md#running-the-decision-model). If the decision model is unavailable
105
- nothing is added and you are told once per session; no other model takes over.
106
- - **Automatic capture.** As in pi-hermes-memory, a background review of the conversation saves, updates, archives and
107
- succeeds notes every `capture.nudgeInterval` assistant turns (10) or `capture.nudgeToolCalls` tool calls (15), once
108
- the session has three user messages. What was not yet reviewed is also reviewed before compaction and when the
109
- session ends, once it has `capture.flushMinTurns` user messages (6). It never asks you anything: what it is unsure
110
- of is not saved. Its judgement quality is not yet proven against a real model.
111
- - **Semantic search.** `memory_search` fuses words and meaning. The default is a local multilingual model (about
112
- 135 MB), downloaded when needed, without asking; OpenAI and Google are the alternatives. The index lives outside the
113
- vaults and is rebuilt from the notes whenever needed.
114
-
115
- ### Vault guard
116
-
117
- The LLM reaches vault files only through the memory tools. The extension refuses `read`, `write`, `edit`, `grep`, `find`
118
- and `ls` on a vault, `bash`/`powershell` commands that write a vault path out, and junctions and symlinks into a vault.
119
- You, and Obsidian, are not the LLM and stay free.
120
-
121
- Accepted as they are (your decision, 2026-10-17): reading a vault through a path the shell builds indirectly
122
- (variables, globs, scripts), tools of other extensions, and foreground subagents, which load no extensions. Creating
123
- hardlinks through the shell is refused by the shell-edit guard. Details in
124
- [docs/ROADMAP.md](docs/ROADMAP.md#2-memory-the-agent-can-read-vault-access-only).
125
-
126
- Paths are compared ignoring case on Windows and macOS and with case on Linux, whatever the volume: a case-sensitive
127
- macOS volume is still compared ignoring case, and a case-insensitive mount on Linux is still compared with case.
128
-
129
- ### Shell-edit guard
130
-
131
- Before every `bash`/`powershell` call, Jev (`jev-latest`, which needs `TYPESAFE_API_KEY`) scores whether the command
132
- edits a file through a redirection, an in-place edit, inline code, a script that writes, a copy or download that
133
- creates a file, or a hardlink. A score at or above its cutoff refuses the call and points to `edit` and `write`;
134
- when Jev cannot answer, the call is refused. The cutoffs and how they were measured are in
135
- [docs/shell-edit-cutoffs.md](docs/shell-edit-cutoffs.md).
134
+ | `/memory-setup` | First setup; afterwards, the settings screen |
135
+ | `/memory-init` | Gives the current project its own vault |
136
+ | `/memory-doctor` | Checks the vaults, the config, the models and the keys, and says what is wrong |
137
+ | `/memory-export` | Saves both vaults to a new `.zip` |
138
+
139
+ ### Guards
140
+
141
+ - **Vault guard.** The agent reaches the vaults only through the memory tools. pi's file tools (`read`, `write`,
142
+ `edit`, `grep`, `find`, `ls`) on a vault are refused, and so are shell commands that write a vault path out. You and
143
+ Obsidian are not affected.
144
+ - **Shell-edit guard.** Before every `bash` or `powershell` call, Jev checks whether the command edits a file: a
145
+ redirect, an in-place edit, inline code, a script that writes, a copy or download that creates a file, or a
146
+ hardlink. If so, the call is refused and the agent is told to use `edit` or `write`. When Jev cannot answer, the
147
+ call is refused.
136
148
 
137
149
  ### Dashboard
138
150
 
139
- Setup and init write `Memory.base` at each vault root when it is missing, with views for all notes, Don't forget and
140
- Archive (file name, folder, type, Don't forget, updated). Your edits to it are kept. It only displays.
151
+ Each vault gets a `Memory.base` file: an Obsidian view of all notes, the Don't forget notes and the archive. Your edits
152
+ to it are kept.
153
+
154
+ ## Settings
155
+
156
+ All settings are changed in the `/memory-setup` screen, or by asking pi. You never need to edit the file.
157
+
158
+ | Setting | Default | What it does |
159
+ |---|---|---|
160
+ | Global vault | chosen at setup | Folder of the global vault |
161
+ | Projects | none | Each project folder and its vault, added by `/memory-init` |
162
+ | Capture model | the model in use at setup | Chat model that saves memory |
163
+ | Memory agent model | chosen at setup | Chat model that searches and reads notes for each message |
164
+ | `capture.nudgeInterval` | 10 | Assistant turns between background reviews |
165
+ | `capture.nudgeToolCalls` | 15 | Tool calls between background reviews |
166
+ | `capture.flushMinTurns` | 6 | Your messages a session needs before it is saved at compaction or at the end |
167
+ | `capture.reviewEnabled` | on | Background review during the session |
168
+ | `capture.flushOnCompact` | on | Save before pi compacts the session |
169
+ | `capture.flushOnShutdown` | on | Save when the session ends |
170
+ | `context.budgetChars` | 12,000 | Characters of relevant notes added at session start and to each message (Don't forget notes do not count) |
171
+ | `limits.maxStatementChars` | 100 | Longest note name |
172
+ | `limits.maxBodyChars` | 1,000 | Longest note body |
173
+ | `limits.maxNotesPerFolder` | 20 | `/memory-doctor` suggests splitting a folder above this |
174
+ | Embeddings | local | Model for search by meaning: local (no key), OpenAI (`OPENAI_API_KEY`) or Google (`GEMINI_API_KEY`) |
175
+ | Decision model | Jev | Jev (TypeSafe, cutoffs 0.5/0.5) or Laya (local server, you set the cutoffs) |
176
+
177
+ Every field, the file format and running Laya locally are in [docs/config.md](docs/config.md).
178
+
179
+ ## Where things are stored
180
+
181
+ | What | Where |
182
+ |---|---|
183
+ | Notes | The vault folders you chose |
184
+ | Settings | `pi-obsidian-memory/config.json` in pi's agent folder (`~/.pi/agent` by default) |
185
+ | Search index | `pi-obsidian-memory/index/` in pi's agent folder; rebuilt from the notes when needed |
186
+ | Local search model | `pi-obsidian-memory/models/` in pi's agent folder; downloaded once, about 135 MB |
187
+
188
+ The vaults hold only notes, their `index.md` files, `_templates/` and `Memory.base`, so they can live in any synced
189
+ folder (OneDrive, iCloud, Dropbox).
141
190
 
142
191
  ## Privacy
143
192
 
144
- - Capture and the memory agent send conversation and note content to the chat model you choose.
145
- - Jev, the default decision model, sends your messages and candidate notes to TypeSafe; Laya, the optional local choice, runs on your machine.
146
- - Choosing a remote embeddings provider (OpenAI, Google) sends note content and queries to it.
147
- - `/memory-setup` warns about each of these before writing, as a plain warning with no question; nothing is asked at
148
- runtime, and no other provider ever takes the place of the one you chose.
149
- - Secrets are saved as written: the vault is yours, with no masking or blocking.
150
- - Credentials come from pi or the environment; `config.json` never holds a key.
193
+ - Capture and the memory agent send conversation and note content to the chat models you choose.
194
+ - Jev sends your messages, candidate notes and shell commands to TypeSafe.
195
+ - Search by meaning runs on your machine by default; OpenAI or Google receive note content and queries only if you
196
+ choose them.
197
+ - `/memory-setup` shows each of these before it saves.
198
+ - Notes are saved as written, without masking. The config file never holds a key.
151
199
 
152
- ## Development
200
+ ## Troubleshooting
201
+
202
+ Run `/memory-doctor` first: it checks the vaults, the config, the models and the keys, and says what is wrong. The only
203
+ thing it offers to change is a blank note, after asking you.
204
+
205
+ | You see | Do |
206
+ |---|---|
207
+ | "Memory is off" or "Run /memory-setup first" | Run `/memory-setup` |
208
+ | "No chat model is available" during setup | Connect a model in pi (`/login`), then run `/memory-setup` again |
209
+ | No relevant notes are ever added | Check that `TYPESAFE_API_KEY` is set, then run `/memory-doctor` |
210
+ | "Refused: the shell-edit guard could not check this command" | Same: the shell-edit guard needs Jev |
211
+ | "Memory capture did not finish: …" | The text after the colon names the cause, for example a model without a key |
212
+ | `No API key found for "<provider>"` in `/memory-doctor` | Log in to that provider in pi (`/login`) or set its key |
153
213
 
154
- Tested with pi `1.3.0` and TypeBox `1.3.27`, pinned as development dependencies. Pi provides both at runtime;
155
- following its package contract, they are wildcard peers, not bundled runtime dependencies.
214
+ ## Known limits
215
+
216
+ - Paths are compared ignoring case on Windows and macOS and with case on Linux, whatever the volume.
217
+ - The vault guard does not see a vault path the shell builds indirectly (variables, globs, scripts), tools of other
218
+ extensions, or foreground subagents.
219
+ - How well capture judges what to save is not yet measured against a real model.
220
+
221
+ ## Development
156
222
 
157
223
  ```bash
158
224
  npm install
159
225
  npm run lint
160
226
  npm run typecheck
161
227
  npm test # offline: pure tests and tests on a real temporary disk
162
- npm run test:e2e # the real pi over RPC, with a temporary agent folder and vaults
228
+ npm run test:e2e # the real pi over RPC, through OmniRoute
163
229
  npm run test:model # live tests: real models and network
164
230
  pi -e ./src/pi/extension.ts
165
231
  ```
166
232
 
167
- `npm test` needs no model and no network. `npm run test:e2e` uses the real Haiku (`cc/claude-haiku-5-5`) through
168
- OmniRoute, loaded in pi by `pi-omniroute-sync`, and the real Jev. It reads `OMNIROUTE_URL`, `OMNIROUTE_API_KEY` and
169
- `TYPESAFE_API_KEY` from the environment and runs one test file at a time. CI runs it on windows-latest (the secrets
170
- have the same names). The Laya tests use a real `laya-serve` from `tmp/laya-venv` and run only on request:
171
- `npx vitest run --project e2e-laya`.
233
+ `npm run test:e2e` runs one test per feature with the real `cc/claude-haiku-5-5` through OmniRoute
234
+ (`pi-omniroute-sync`) and the real Jev. It reads `OMNIROUTE_URL`, `OMNIROUTE_API_KEY` and `TYPESAFE_API_KEY` from the
235
+ environment. CI runs it on Windows, and lint, typecheck and `npm test` on Windows, macOS and Linux.
172
236
 
173
- `npm run test:model` downloads the local embedding model (about 135 MB) to a cache folder outside the repository
174
- (`%LOCALAPPDATA%\pi-obsidian-memory-bench\models`) and is not part of `npm test`.
237
+ Releases are automatic: a push to `main` with a `feat` or `fix` commit bumps the version, rewrites `CHANGELOG.md`,
238
+ publishes to npm and creates the GitHub release.
175
239
 
176
- The roadmap is in [docs/ROADMAP.md](docs/ROADMAP.md); the note format is in
177
- [docs/vault-format.md](docs/vault-format.md).
240
+ More: [vision](docs/VISION.md), [roadmap](docs/ROADMAP.md), [note format](docs/vault-format.md),
241
+ [settings](docs/config.md).
178
242
 
179
243
  ## License
180
244
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-obsidian-memory",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Long-term memory for the pi coding agent, stored in Obsidian vaults",
5
5
  "keywords": [
6
6
  "pi",