@persistmemory/cli 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +366 -0
- package/dist/bin.js +2863 -0
- package/dist/bin.js.map +7 -0
- package/dist/index.js +2853 -0
- package/dist/index.js.map +7 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PersistMemory
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# @persistmemory/cli
|
|
2
|
+
|
|
3
|
+
**`pm`** — capture and search your memory from a terminal.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@persistmemory/cli)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
9
|
+
Memory that persists across every model, tool and session you use — reachable from the one
|
|
10
|
+
place you already are.
|
|
11
|
+
|
|
12
|
+
- **Browser sign-in, OAuth 2.1 + PKCE** — your password is never typed into a terminal
|
|
13
|
+
- **One dependency-free binary** — the SDK is bundled in; a global install pulls down one thing
|
|
14
|
+
- **Scriptable** — `--output json|yaml|csv|tsv`, and JSON automatically when piped
|
|
15
|
+
- **Spaces** — keep work, personal and per-client memory apart
|
|
16
|
+
- **File requests** — approve, from your terminal, what an assistant is allowed to read
|
|
17
|
+
- **macOS, Linux, Windows** — the same `dist/bin.js` on all three
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Table of contents
|
|
22
|
+
|
|
23
|
+
1. [Install](#install)
|
|
24
|
+
2. [Sign in](#sign-in)
|
|
25
|
+
3. [Get an API key (CI and headless)](#get-an-api-key-ci-and-headless)
|
|
26
|
+
4. [First five minutes](#first-five-minutes)
|
|
27
|
+
5. [Spaces](#spaces)
|
|
28
|
+
6. [Commands](#commands)
|
|
29
|
+
7. [Output formats and scripting](#output-formats-and-scripting)
|
|
30
|
+
8. [Profiles](#profiles)
|
|
31
|
+
9. [The agent, and file requests](#the-agent-and-file-requests)
|
|
32
|
+
10. [Environment variables](#environment-variables)
|
|
33
|
+
11. [Where your files live](#where-your-files-live)
|
|
34
|
+
12. [Security](#security)
|
|
35
|
+
13. [Troubleshooting](#troubleshooting)
|
|
36
|
+
14. [Uninstall](#uninstall)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
**macOS and Linux**
|
|
43
|
+
```bash
|
|
44
|
+
curl -fsSL https://persistmemory.com/install.sh | sh
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Windows (PowerShell)**
|
|
48
|
+
```powershell
|
|
49
|
+
irm https://persistmemory.com/install.ps1 | iex
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Any platform, with npm**
|
|
53
|
+
```bash
|
|
54
|
+
npm install -g @persistmemory/cli
|
|
55
|
+
|
|
56
|
+
# or run it without installing anything
|
|
57
|
+
npx @persistmemory/cli auth login
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Node 20 or newer is the only requirement.
|
|
61
|
+
|
|
62
|
+
Piping a script into a shell is worth being careful about, so read ours first —
|
|
63
|
+
[install.sh](https://persistmemory.com/install.sh) and
|
|
64
|
+
[install.ps1](https://persistmemory.com/install.ps1). Both refuse to run rather than guessing
|
|
65
|
+
when they cannot find a supported platform or a recent enough Node, neither escalates to
|
|
66
|
+
`sudo` or Administrator on your behalf, and both tell you what to do instead when npm's global
|
|
67
|
+
directory is not yours to write to.
|
|
68
|
+
|
|
69
|
+
Two commands are installed: **`pm`** (what you will type) and **`persistmemory`** (the same
|
|
70
|
+
thing, for when `pm` collides with something else on your machine).
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Sign in
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pm auth login
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Your browser opens, you approve the scopes on a consent screen, and the terminal says who you
|
|
81
|
+
are. There is **no key to create and no password to paste** — that is the point.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pm auth status # who am I, and does the server still accept it
|
|
85
|
+
pm auth logout # forget the stored credential
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If you have no account yet, sign up at <https://persistmemory.com/signup> first (Google works),
|
|
89
|
+
then run `pm auth login`.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Get an API key (CI and headless)
|
|
94
|
+
|
|
95
|
+
A browser sign-in needs a browser. On a build agent or a server over SSH, use a key instead.
|
|
96
|
+
|
|
97
|
+
1. Sign in at <https://persistmemory.com/signin>.
|
|
98
|
+
2. Open **Settings** from the header, or go to <https://persistmemory.com/settings#keys>.
|
|
99
|
+
3. Under **API keys**, name the key after the machine or job that will use it — `ci`,
|
|
100
|
+
`deploy-bot`, `home-server` — not after yourself. The name is how you decide which one to
|
|
101
|
+
revoke later.
|
|
102
|
+
4. Press **Create** and copy it at once. It looks like `pm_live_` plus 43 characters, is stored
|
|
103
|
+
only as a hash, and cannot be shown to you again. A lost key is revoked and replaced.
|
|
104
|
+
5. Use it:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
export PERSISTMEMORY_API_KEY=pm_live_...
|
|
108
|
+
pm search "release checklist" --output json
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`PERSISTMEMORY_API_KEY` takes precedence over any stored login, which is what makes CI
|
|
112
|
+
predictable. Or store it in a profile instead:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pm auth login --api-key
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Revoke a key any time in **Settings → API keys → Revoke**; whatever was using it stops
|
|
119
|
+
working immediately. A key can read and write your memories and spaces — it cannot change your
|
|
120
|
+
password or mint another key.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## First five minutes
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pm auth login
|
|
128
|
+
|
|
129
|
+
pm remember "we chose Postgres with pgvector over Pinecone — cost and lock-in"
|
|
130
|
+
pm remember --file ./notes/architecture.md
|
|
131
|
+
git log -1 --format=%B | pm remember - # capture whatever is piped in
|
|
132
|
+
|
|
133
|
+
pm search "why postgres"
|
|
134
|
+
pm list memories --limit 20
|
|
135
|
+
pm get memory <id>
|
|
136
|
+
|
|
137
|
+
pm # just start talking
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Running `pm` with no arguments starts an interactive session: ask a question in plain
|
|
141
|
+
English and it answers out of your own memory.
|
|
142
|
+
|
|
143
|
+
> **`remember` is asynchronous.** It hands the text to the pipeline and returns. Extraction and
|
|
144
|
+
> consolidation happen afterwards, so a memory may take a moment to become searchable — and the
|
|
145
|
+
> same fact captured twice becomes one memory with two pieces of evidence, not two memories.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Spaces
|
|
150
|
+
|
|
151
|
+
A **space** is a boundary. Search inside one and you see only what is in it — that is how work
|
|
152
|
+
stays out of personal and one client stays out of another.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
pm list spaces # what you have, with their ids
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`--space` takes **space ids**, comma-separated — the ids `pm list spaces` prints, not the
|
|
159
|
+
display names:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# Capture into a space
|
|
163
|
+
pm remember "Acme's cutover is the 14th" --space space_a1b2c3
|
|
164
|
+
|
|
165
|
+
# Search only there
|
|
166
|
+
pm search "cutover date" --space space_a1b2c3
|
|
167
|
+
|
|
168
|
+
# Several at once
|
|
169
|
+
pm search "deadline" --space space_a1b2c3,space_d4e5f6
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Creating a space is not a CLI command yet. Make one with the SDK or the API:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { PersistMemory } from "@persistmemory/sdk";
|
|
176
|
+
const client = new PersistMemory({ apiKey: process.env.PERSISTMEMORY_API_KEY! });
|
|
177
|
+
const acme = await client.spaces.create({ name: "Acme", kind: "project" });
|
|
178
|
+
console.log(acme.id);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
curl -s https://api.persistmemory.com/api/v1/spaces \
|
|
183
|
+
-H "authorization: Bearer $PERSISTMEMORY_API_KEY" \
|
|
184
|
+
-H "content-type: application/json" \
|
|
185
|
+
-d '{"name":"Acme","kind":"project"}'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Leaving `--space` off searches everything you have.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Commands
|
|
193
|
+
|
|
194
|
+
| Command | What it does |
|
|
195
|
+
| --- | --- |
|
|
196
|
+
| `pm` | Start an interactive session and just ask |
|
|
197
|
+
| `pm chat` | The same, explicitly |
|
|
198
|
+
| `pm chat --resume <id>` | Pick up an earlier session |
|
|
199
|
+
| `pm auth login` | Sign in through the browser |
|
|
200
|
+
| `pm auth login --api-key` | Sign in with a key, for CI and headless machines |
|
|
201
|
+
| `pm auth status` | Who am I, and does the server still accept it |
|
|
202
|
+
| `pm auth logout` | Forget the stored credential |
|
|
203
|
+
| `pm remember <text>` | Capture text |
|
|
204
|
+
| `pm remember -` | Capture whatever is piped in |
|
|
205
|
+
| `pm remember --file <path>` | Capture a file's contents |
|
|
206
|
+
| `pm search <query>` | Search your memory |
|
|
207
|
+
| `pm list memories` | The most recent memories |
|
|
208
|
+
| `pm list spaces` | Your spaces |
|
|
209
|
+
| `pm get memory <id>` | One memory, in full |
|
|
210
|
+
| `pm status` | Is the service healthy |
|
|
211
|
+
| `pm requests` | File requests waiting for you to approve |
|
|
212
|
+
| `pm agent --root <dir>` | Answer file requests, reading only inside the folders you name |
|
|
213
|
+
|
|
214
|
+
Flags: `--output`, `--profile`, `--api-url`, `--limit`, `--space`, `--quiet`, `--version`,
|
|
215
|
+
`--help`. `pm --help` prints the whole thing, always up to date with the version you have.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Output formats and scripting
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
pm search "postgres" --output json | jq '.results[].memory.title'
|
|
223
|
+
pm list memories --output csv > memories.csv
|
|
224
|
+
pm list spaces --output yaml
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The default is a **table on a terminal and JSON when piped**, so a command you typed stays
|
|
228
|
+
readable and the same command in a script stays parseable — without you remembering a flag.
|
|
229
|
+
|
|
230
|
+
Exit codes are meaningful: `0` success, non-zero on failure, so `set -e` behaves.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Profiles
|
|
235
|
+
|
|
236
|
+
More than one account — your own and a work one, or production and staging:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
pm auth login --profile work
|
|
240
|
+
pm search "roadmap" --profile work
|
|
241
|
+
export PERSISTMEMORY_PROFILE=work # or set it for the shell
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Each profile keeps its own credential. `--profile` beats the environment variable, which beats
|
|
245
|
+
the stored default.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## The agent, and file requests
|
|
250
|
+
|
|
251
|
+
`pm agent` lets an assistant — in Claude, ChatGPT, Telegram, wherever you are — ask for a file
|
|
252
|
+
from *this* machine. It reads **only inside the folders you name**, and nothing is read until
|
|
253
|
+
you approve it.
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
pm agent --root ~/projects --root ~/Documents
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Then, from anywhere: *"get me the deploy checklist from my laptop"*. The request appears here:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
pm requests # what is waiting
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
and on the web at <https://persistmemory.com/dashboard/requests>.
|
|
266
|
+
|
|
267
|
+
**A request made by a model is never fulfilled automatically.** A live read is something you
|
|
268
|
+
approve, every time, because the thing asking may be repeating an instruction it found in
|
|
269
|
+
somebody else's email.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Environment variables
|
|
274
|
+
|
|
275
|
+
| Variable | Meaning |
|
|
276
|
+
| --- | --- |
|
|
277
|
+
| `PERSISTMEMORY_API_KEY` | A key, taking precedence over any stored login. This is what CI should set. |
|
|
278
|
+
| `PERSISTMEMORY_API_URL` | The server to talk to. Default `https://api.persistmemory.com` |
|
|
279
|
+
| `PERSISTMEMORY_PROFILE` | Which stored profile to use |
|
|
280
|
+
| `PERSISTMEMORY_HOME` | Where config, credentials and transcripts live. Default `~/.persistmemory` |
|
|
281
|
+
| `PERSISTMEMORY_CLIENT_ID` | Override the OAuth client id, for a self-hosted deployment |
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Where your files live
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
~/.persistmemory/
|
|
289
|
+
├── config.json profiles and the current one
|
|
290
|
+
├── credentials.json tokens — treat this like an SSH key
|
|
291
|
+
└── sessions/ transcripts of your interactive sessions
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Move all of it with `PERSISTMEMORY_HOME`.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Security
|
|
299
|
+
|
|
300
|
+
- **The browser holds the password, not the CLI.** Sign-in is OAuth 2.1 with PKCE. Your password
|
|
301
|
+
is never typed into a terminal and never passes through this program. What comes back is a
|
|
302
|
+
token scoped to what you approved on the consent screen.
|
|
303
|
+
- **A stolen code is worthless on its own.** The CLI invents a one-time secret, sends only its
|
|
304
|
+
hash to the browser, and reveals the secret when redeeming the code. Anything that intercepts
|
|
305
|
+
the redirect on a shared machine gets a code it cannot exchange.
|
|
306
|
+
- **The callback is loopback-only** — `http://127.0.0.1:<random port>`, listening for one
|
|
307
|
+
request and then closed.
|
|
308
|
+
- **Credentials are files with your permissions**, not entries in a shared keychain another
|
|
309
|
+
process can read without asking.
|
|
310
|
+
- **Nothing is read from your disk without you saying so.** See file requests, above.
|
|
311
|
+
|
|
312
|
+
Found a security problem? <mailto:hello@persistmemory.com>. Please do not open it in public.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Troubleshooting
|
|
317
|
+
|
|
318
|
+
**`pm: command not found` after `npm install -g`**
|
|
319
|
+
npm's global bin directory is not on your `PATH`. `npm bin -g` prints it; add that to your
|
|
320
|
+
shell profile. The install script sets this up for you, which is why it exists.
|
|
321
|
+
|
|
322
|
+
**`EACCES` during a global install**
|
|
323
|
+
Do not reach for `sudo`. Either use the install script, or point npm at a directory you own:
|
|
324
|
+
`npm config set prefix ~/.npm-global` and add `~/.npm-global/bin` to your `PATH`.
|
|
325
|
+
|
|
326
|
+
**`pm auth login` opens the browser and nothing happens**
|
|
327
|
+
The callback is on `127.0.0.1`. A VPN, a proxy that captures loopback, or a browser on a
|
|
328
|
+
*different machine* than the CLI will break it. On a remote box use `pm auth login --api-key`
|
|
329
|
+
instead — that is exactly what it is for.
|
|
330
|
+
|
|
331
|
+
**"Session expired"**
|
|
332
|
+
An OAuth token lasts an hour and renews itself; if renewal fails, `pm auth login` again. An API
|
|
333
|
+
key does not expire on a schedule — if a key stops working, it was revoked.
|
|
334
|
+
|
|
335
|
+
**`pm search` finds nothing you just captured**
|
|
336
|
+
`remember` is asynchronous. Give the pipeline a moment, then search again.
|
|
337
|
+
|
|
338
|
+
**Everything fails with a connection error**
|
|
339
|
+
`pm status` reports whether the service is reachable. Check `PERSISTMEMORY_API_URL` is not left
|
|
340
|
+
over from a local experiment.
|
|
341
|
+
|
|
342
|
+
**Node is too old**
|
|
343
|
+
`node --version` must be 20 or newer. The CLI refuses to run rather than failing later in a
|
|
344
|
+
confusing way.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Uninstall
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
npm uninstall -g @persistmemory/cli
|
|
352
|
+
rm -rf ~/.persistmemory # optional: credentials, config and transcripts
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Links
|
|
358
|
+
|
|
359
|
+
- CLI documentation — <https://persistmemory.com/docs/cli>
|
|
360
|
+
- TypeScript SDK — [`@persistmemory/sdk`](https://www.npmjs.com/package/@persistmemory/sdk)
|
|
361
|
+
- API keys — <https://persistmemory.com/settings#keys>
|
|
362
|
+
- Support — <mailto:hello@persistmemory.com>
|
|
363
|
+
|
|
364
|
+
## License
|
|
365
|
+
|
|
366
|
+
MIT © PersistMemory
|