memoir-cli 3.12.0 → 3.14.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/README.md +128 -137
- package/bin/memoir-work.js +9 -0
- package/bin/memoir.js +50 -8
- package/docs/AUDIT-REMEDIATION.md +55 -0
- package/docs/CASE_TAPE_AMNESIA.md +39 -0
- package/docs/HANDOFF-SECURITY-AUDIT.md +106 -0
- package/docs/LOCAL-HANDOFF-VALIDATION.md +129 -0
- package/docs/MCP-V2-MIGRATION.md +17 -0
- package/docs/PROJECT-HANDOFF.md +255 -0
- package/docs/PROJECT-VIEW-DEBUG.md +66 -0
- package/docs/PROJECT-VIEW-VALIDATION.md +136 -0
- package/docs/RELEASE-3.14-VALIDATION.md +36 -0
- package/docs/RELIABILITY-ROLLOUT.md +57 -0
- package/docs/RETRIEVAL-INDEX.md +45 -0
- package/docs/RETRIEVAL-RESULTS.md +26 -0
- package/docs/SPEC.md +684 -0
- package/evals/CONTINUITY-PROTOCOL.md +45 -0
- package/evals/cases.json +200 -0
- package/evals/results/retrieval-2026-09-05.json +5333 -0
- package/evals/retrieval-performance.mjs +99 -0
- package/evals/run.mjs +87 -0
- package/package.json +13 -5
- package/src/adapters/index.js +13 -6
- package/src/adapters/restore.js +83 -36
- package/src/cloud/storage.js +130 -93
- package/src/commands/activate.js +18 -7
- package/src/commands/cloud.js +55 -4
- package/src/commands/consolidate.js +49 -10
- package/src/commands/diff.js +2 -2
- package/src/commands/doctor.js +3 -3
- package/src/commands/push.js +156 -161
- package/src/commands/recall.js +1 -1
- package/src/commands/restore.js +32 -44
- package/src/commands/resume.js +15 -164
- package/src/commands/session.js +51 -9
- package/src/commands/snapshot.js +6 -7
- package/src/commands/status.js +23 -1
- package/src/commands/upgrade.js +11 -9
- package/src/commands/validate.js +3 -0
- package/src/commands/view.js +2 -2
- package/src/commands/why.js +4 -3
- package/src/config.js +9 -40
- package/src/context/capture.js +126 -32
- package/src/context/handoffs.js +72 -0
- package/src/events/summary.js +122 -0
- package/src/integrations/setup.js +88 -0
- package/src/mcp.js +105 -152
- package/src/memory/lexical-index.js +65 -0
- package/src/memory/repository.js +16 -0
- package/src/memory/scope.js +65 -0
- package/src/memory/search.js +165 -70
- package/src/memory/store.js +141 -0
- package/src/providers/index.js +182 -51
- package/src/providers/restore.js +5 -1
- package/src/security/encryption.js +34 -60
- package/src/security/files.js +155 -0
- package/src/session/brief.js +47 -0
- package/src/session/inject.js +12 -6
- package/src/session/lock.js +39 -118
- package/src/session/migrations.js +6 -0
- package/src/session/render.js +34 -4
- package/src/session/state.js +200 -33
- package/src/work/cli.js +64 -0
- package/src/work/errors.js +8 -0
- package/src/work/server.js +28 -0
- package/src/work/setup.js +96 -0
- package/src/work/store.js +340 -0
- package/src/work/ui/app.js +205 -0
- package/src/work/ui/index.html +30 -0
- package/src/work/ui/style.css +3 -0
- package/src/work/view.js +93 -0
- package/src/workspace/tracker.js +84 -332
- package/supabase/migrations/202609050001_backup_versions.sql +50 -0
package/README.md
CHANGED
|
@@ -1,182 +1,173 @@
|
|
|
1
|
-
|
|
1
|
+
# Memoir
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Portable, project-scoped memory and session handoffs for coding agents.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Memoir keeps decisions, rationale, goals, and next actions in readable local files. An MCP server lets an agent save and retrieve that context. Optional local, Git, and cloud backups move it between machines.
|
|
6
6
|
|
|
7
|
-
[
|
|
8
|
-
[](https://npmjs.org/package/memoir-cli)
|
|
9
|
-
[](https://github.com/camgitt/memoir/stargazers)
|
|
10
|
-
[](LICENSE)
|
|
7
|
+
The reliability changes are described in [the remediation record](docs/AUDIT-REMEDIATION.md). Review the [upgrade and recovery guide](docs/RELIABILITY-ROLLOUT.md) before upgrading clients that share encrypted backups.
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
## Continue between Codex and Cursor on this computer
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
This branch adds a separate, project-only handoff. It carries answered questions,
|
|
12
|
+
decisions, next actions and receipts from checks actually run through Memoir. It
|
|
13
|
+
does not import personal memory or transcripts.
|
|
14
|
+
|
|
15
|
+
The project handoff and browser view require version 3.14.0 or later:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install -g memoir-cli@3.14.0
|
|
19
|
+
cd /path/to/your/project
|
|
20
|
+
memoir work setup
|
|
21
|
+
memoir work resume
|
|
22
|
+
memoir work view
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For source development, install dependencies and replace `memoir` with
|
|
26
|
+
`node bin/memoir.js` from the Memoir checkout.
|
|
27
|
+
|
|
28
|
+
Open **this same folder and branch** in Codex or Cursor and say **“Continue this
|
|
29
|
+
project.”** The managed instructions use the project MCP connection, or the
|
|
30
|
+
included CLI fallback when the connection is unavailable. Existing settings and
|
|
31
|
+
approval policies are preserved. Agent adherence is still required; ordinary
|
|
32
|
+
terminal checks are not captured automatically.
|
|
33
|
+
|
|
34
|
+
The **project view** opens in your browser on this computer. Search saved
|
|
35
|
+
answers, see why a check needs repeating, correct a decision, or remove it from
|
|
36
|
+
the next handoff. Removed records can be restored; earlier versions stay in local
|
|
37
|
+
history. Keep the view's terminal open while using it, and press Ctrl+C to stop.
|
|
38
|
+
You can also ask your agent **“Open my Memoir project view.”** See the
|
|
39
|
+
[local view validation](docs/PROJECT-VIEW-VALIDATION.md) for actual browser tests
|
|
40
|
+
and the limits of the fresh-session continuity results. Subsequent
|
|
41
|
+
[debugging fixes and recovery tests](docs/PROJECT-VIEW-DEBUG.md) cover slow saves,
|
|
42
|
+
interrupted responses and configuration preservation.
|
|
43
|
+
|
|
44
|
+
Checks run through `memoir work check` using the client's normal terminal
|
|
45
|
+
permissions. The MCP memory connection deliberately cannot execute commands.
|
|
46
|
+
See the [adversarial audit and remaining trust limits](docs/HANDOFF-SECURITY-AUDIT.md).
|
|
47
|
+
|
|
48
|
+
The project ledger stays in ignored `.memoir/` files. This workflow does not
|
|
49
|
+
sync those files through GitHub or bridge different checkouts. See the
|
|
50
|
+
[setup and everyday guide](docs/PROJECT-HANDOFF.md) for commands, corrections,
|
|
51
|
+
privacy boundaries and when a check needs to run again. The feature runs locally even when installed from npm; publishing the package
|
|
52
|
+
does not upload your project ledger.
|
|
53
|
+
|
|
54
|
+
## Existing memory and backup workflow
|
|
55
|
+
|
|
56
|
+
Node.js 18 or later is required.
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
npm install -g memoir-cli
|
|
60
|
+
cd /path/to/project
|
|
61
|
+
memoir setup --tool claude,codex,cursor
|
|
62
|
+
memoir goal "Finish the account recovery flow"
|
|
63
|
+
memoir note "Use single-use recovery codes" --why "Prevent replay"
|
|
64
|
+
memoir next "Test expired recovery codes"
|
|
65
|
+
memoir recall "recovery codes"
|
|
66
|
+
memoir resume
|
|
16
67
|
```
|
|
17
68
|
|
|
18
|
-
|
|
69
|
+
`setup` preserves existing settings, writes a project MCP entry, and starts the Memoir server to check its tools. Restart the client and accept its project trust/MCP prompt. Server startup does not prove that a particular client version has accepted its configuration.
|
|
19
70
|
|
|
20
|
-
|
|
71
|
+
For source development, use `npm ci`, then `node bin/memoir.js setup`. The generated entry uses absolute Node/server paths; review it after moving the installation. Existing different Memoir entries are preserved for review.
|
|
21
72
|
|
|
22
|
-
|
|
73
|
+
`memoir activate` adds managed usage instructions and sets up detected supported clients. `memoir resume --inject --to codex` adds a managed handoff to the project's `AGENTS.md`, preserving other content.
|
|
23
74
|
|
|
24
|
-
|
|
75
|
+
## Memory and continuity
|
|
25
76
|
|
|
26
|
-
|
|
77
|
+
- `memoir_note` records a scoped decision and rationale. Older decisions are archived when the working summary fills.
|
|
78
|
+
- `memoir_remember` writes a canonical Markdown record. Open its returned ID with `memoir_read`, `tool: "memoir"`, `filepath: "<id>.md"`.
|
|
79
|
+
- `memoir_recall` searches the active project plus shared records and returns matching passages, paths, line evidence, and IDs where available.
|
|
80
|
+
- Recall reuses an incremental lexical index while checking source changes and scope on each query. New project instructions are discovered without a timed cache delay. See [retrieval behavior and benchmarks](docs/RETRIEVAL-INDEX.md).
|
|
81
|
+
- `memoir_resume` returns the goal, next actions, questions, and decisions, and compares the saved commit with the checkout. Old observations never imply current tests pass.
|
|
82
|
+
- `memoir_forget` accepts a decision match or canonical ID. Hidden records are excluded from recall/session views; `purge: true` also removes current canonical text and local revision history.
|
|
27
83
|
|
|
28
|
-
|
|
84
|
+
Canonical memory lives in `~/.config/memoir/memories/` and session state in `~/.config/memoir/session.json`. These source files are plaintext on the device. Backup encryption does not encrypt them.
|
|
29
85
|
|
|
30
|
-
|
|
31
|
-
you: how does auth work in this project?
|
|
86
|
+
New memories use `MEMOIR_PROJECT_ROOT` or the working directory. Use explicit `scope: "shared"` for general preferences. Git identity recognizes common SSH/HTTPS remote spellings; local identity is home-relative. Renames, unusual remote aliases, or different directory layouts may need explicit scope selection. Profiles select backup destinations; they are not independent security tenants.
|
|
32
87
|
|
|
33
|
-
|
|
34
|
-
Found 3 memories matching "auth setup architecture":
|
|
88
|
+
Legacy records without scope metadata are shared, except recognized Claude project directories. Review and label imported history before relying on strict isolation. Scope is an organizational boundary, not authentication against an agent already permitted to read the filesystem.
|
|
35
89
|
|
|
36
|
-
|
|
37
|
-
reference · How authentication works — JWT + refresh, middleware location, state choice
|
|
38
|
-
Server components must call getUser(), never getSession(). The session
|
|
39
|
-
cookie name is derived from the Supabase URL host, so previews differ.
|
|
40
|
-
⋯
|
|
41
|
-
Chose Zustand over Redux for auth state (decided March 12).
|
|
90
|
+
## Backup and recovery
|
|
42
91
|
|
|
43
|
-
|
|
44
|
-
with refresh tokens, the middleware is in src/middleware/auth.ts,
|
|
45
|
-
and you chose Zustand over Redux for auth state (decided March 12).
|
|
46
|
-
```
|
|
92
|
+
First use creates a **local** configuration. It does not create a GitHub repository or upload automatically. Use `memoir init` to choose another destination and encryption.
|
|
47
93
|
|
|
48
|
-
|
|
94
|
+
```sh
|
|
95
|
+
memoir push
|
|
96
|
+
memoir restore
|
|
97
|
+
memoir push --only claude,codex
|
|
98
|
+
memoir restore --only codex
|
|
99
|
+
```
|
|
49
100
|
|
|
50
|
-
|
|
101
|
+
Filtered pushes preserve other tool/machine files. Session and canonical records accompany tool filters for cross-tool continuity.
|
|
51
102
|
|
|
52
|
-
|
|
103
|
+
For encrypted local/Git backups, supply `MEMOIR_PASSPHRASE` through your environment/password manager or enter it interactively. Legacy six-character secrets remain readable; use a long, unique secret for new backups. Headless writes do not silently choose plaintext when encryption is unconfigured.
|
|
53
104
|
|
|
54
|
-
|
|
105
|
+
Snapshots authenticate the manifest and every file. Missing blobs, corrupt contents, unsafe paths, symlinks, and oversized input fail recovery. Repeated encrypted pushes verify and merge the previous snapshot before replacement. Local encryption migration removes plaintext from the current destination after the switch; Git history and filesystem snapshots can retain older plaintext.
|
|
55
106
|
|
|
56
|
-
|
|
57
|
-
|---|:---:|:---:|:---:|
|
|
58
|
-
| **memoir** | ✅ | ✅ **free** | ✅ |
|
|
59
|
-
| Claude Code / Cursor native | ❌ one tool | ❌ one machine | ❌ |
|
|
60
|
-
| claude-mem | ✅ | ❌ local only | ❌ |
|
|
61
|
-
| basic-memory | ✅ | 💲 paid cloud | ❌ |
|
|
62
|
-
| mem0 / OpenMemory | ✅ | 💲 paid cloud | ❌ |
|
|
107
|
+
Local pushes lock the complete read/merge/write operation. Git rejects conflicting remote updates without force-pushing; retry to read and merge the new state. Use a dedicated backup directory.
|
|
63
108
|
|
|
64
|
-
|
|
109
|
+
### Cloud
|
|
65
110
|
|
|
66
|
-
|
|
111
|
+
New cloud writes require a user-held secret of at least 12 characters in `MEMOIR_CLOUD_PASSPHRASE` (or `MEMOIR_PASSPHRASE`). It is not derived from account identity or sent in metadata.
|
|
67
112
|
|
|
68
|
-
```
|
|
69
|
-
|
|
113
|
+
```sh
|
|
114
|
+
memoir cloud push
|
|
115
|
+
memoir cloud restore
|
|
116
|
+
memoir cloud restore --version 3
|
|
117
|
+
memoir cloud migrate
|
|
118
|
+
memoir cloud migrate --apply
|
|
70
119
|
```
|
|
71
120
|
|
|
72
|
-
|
|
121
|
+
The writer requires the database migration in [the rollout guide](docs/RELIABILITY-ROLLOUT.md). Without it, version allocation fails before upload. The Memoir hosted service received this migration on September 6, 2026; see the [deployment checks](docs/RELEASE-3.14-VALIDATION.md). Self-hosted services must apply it before enabling new writes.
|
|
73
122
|
|
|
74
|
-
|
|
123
|
+
`cloud migrate` displays a plan. `--apply` downloads each old backup, creates a user-passphrase replacement, downloads and byte-checks it, then removes the old object. Interrupted migration can reuse its replacement. Keep the secret available on every recovering device; there is no lost-passphrase recovery service.
|
|
75
124
|
|
|
76
|
-
|
|
77
|
-
|----------|-------------|
|
|
78
|
-
| `memoir_recall` | Search across all your AI memories — returns matched passages, ranked by coverage |
|
|
79
|
-
| `memoir_remember` | Save context for future sessions (pass `aliases` so it's findable under other names) |
|
|
80
|
-
| `memoir_list` | Browse all memory files by tool |
|
|
81
|
-
| `memoir_read` | Read a specific memory in full |
|
|
82
|
-
| `memoir_consolidate` | Analyze memories for duplicates, staleness, and bloat |
|
|
83
|
-
| `memoir_status` | See which AI tools are detected |
|
|
84
|
-
| `memoir_profiles` | Switch between work/personal |
|
|
85
|
-
| `memoir_set_goal` | Set the current session goal (pinned into CLAUDE.md) |
|
|
86
|
-
| `memoir_add_next` | Add a next action to the current session |
|
|
87
|
-
| `memoir_complete_next` | Mark a next action as done |
|
|
88
|
-
| `memoir_note` | Record a decision with its rationale |
|
|
89
|
-
| `memoir_ask` | Capture an open question for later |
|
|
90
|
-
| `memoir_session` | Show goals, next actions, decisions, and recent sessions |
|
|
91
|
-
| `memoir_why` | Look up why a past decision was made |
|
|
92
|
-
| `memoir_forget` | Retract a decision — permanent tombstone on every machine; `purge` redacts the text |
|
|
125
|
+
Legacy account-ID-keyed and unencrypted cloud backups remain readable with warnings. Their protection changes only when replaced. Random vault-key wrapping, device enrollment, hardware-backed storage, and independently reviewed key rotation remain future work.
|
|
93
126
|
|
|
94
|
-
|
|
127
|
+
Latest cloud restore merges session/canonical state from retained versions, requiring additional downloads. Explicit version restore selects that snapshot. Cloud tests simulate the backend; production authorization and tenant isolation require separate validation.
|
|
95
128
|
|
|
96
|
-
|
|
129
|
+
### Optional workspace files
|
|
97
130
|
|
|
98
|
-
memoir
|
|
131
|
+
`memoir push --workspace` captures eligible files from the active project, including non-ignored untracked Git files. It does not archive the whole home directory.
|
|
99
132
|
|
|
100
|
-
|
|
133
|
+
`memoir restore --workspace` verifies those files into a new folder under `~/memoir-restored/` for inspection. Existing checkouts are not patched. Commit information is recorded; this is a file snapshot, not a Git-history backup.
|
|
101
134
|
|
|
102
|
-
|
|
135
|
+
Common secret filenames and detected patterns are omitted and listed in the manifest. Detection is heuristic. Old tar-based workspace archives are retained but no longer extracted automatically.
|
|
103
136
|
|
|
104
|
-
|
|
105
|
-
memoir push # back up AI memory + workspace + session
|
|
106
|
-
memoir restore -y # restore on any machine
|
|
107
|
-
```
|
|
137
|
+
## Supported surfaces
|
|
108
138
|
|
|
109
|
-
|
|
139
|
+
| Surface | Capability | Verification boundary |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| Claude Code | Project MCP setup, Memoir tools, instruction import/export | JSON preservation and real server startup tested |
|
|
142
|
+
| Codex | Project TOML MCP setup, Memoir tools, AGENTS.md import/export | TOML round trip and server startup tested |
|
|
143
|
+
| Cursor | Project MCP setup, Memoir tools, rule import/export | JSON preservation and server startup tested |
|
|
144
|
+
| Other existing adapters | Selected memory/config import/export | Adapter fixtures, not full native session continuity |
|
|
145
|
+
| Generic MCP client | Memoir tool contract | Real stdio tests |
|
|
110
146
|
|
|
111
|
-
|
|
147
|
+
Configuration references: [Claude Code](https://code.claude.com/docs/en/mcp), [Codex](https://learn.chatgpt.com/docs/extend/mcp?surface=cli), [Cursor](https://prod.cursor.com/help/customization/mcp).
|
|
112
148
|
|
|
113
|
-
|
|
114
|
-
memoir migrate --from chatgpt --to claude
|
|
115
|
-
# AI-powered — rewrites conventions, not copy-paste
|
|
149
|
+
## Search, privacy, and limits
|
|
116
150
|
|
|
117
|
-
|
|
118
|
-
# Translate to every tool at once
|
|
119
|
-
```
|
|
151
|
+
Search is local Unicode-aware lexical retrieval with field/document-frequency weights, aliases, filtering, and a passage budget. It is not semantic search and has no demonstrated state-of-the-art result.
|
|
120
152
|
|
|
121
|
-
|
|
153
|
+
Memory is context, not permission to run commands or configure tools. Model-written records default to `unverified`. Automatic transcript capture is best effort and must not be treated as proof of successful work.
|
|
122
154
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
memoir consolidate --apply # interactively clean up
|
|
127
|
-
```
|
|
155
|
+
`memoir consolidate` reports duplicates/stale files. Similarity alone cannot remove distinct content. Confirmed removals keep recovery copies; the printed `memoir consolidate --undo <id>` restores into an absent destination. `--smart` explicitly sends bounded excerpts to the Gemini API; choose its model with `MEMOIR_CONSOLIDATE_MODEL`.
|
|
156
|
+
|
|
157
|
+
`push --redact` heuristically redacts staged text, not originals or historical backups. Without it, ordinary memory backups warn and preserve content. Set `DO_NOT_TRACK=1` to disable remote telemetry. Local events distinguish execution failure from success; they do not measure answer usefulness.
|
|
128
158
|
|
|
129
|
-
|
|
159
|
+
Forgetting propagates on later sync between updated clients. Old clients, snapshots, and Git history can still contain deleted text. Purging every historical copy is separate. Native configuration restore is not atomic across multiple application directories.
|
|
130
160
|
|
|
131
|
-
##
|
|
161
|
+
## Development
|
|
132
162
|
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
163
|
+
```sh
|
|
164
|
+
npm ci
|
|
165
|
+
npm test
|
|
166
|
+
node evals/run.mjs
|
|
167
|
+
npm audit --omit=dev
|
|
168
|
+
npm pack --ignore-scripts
|
|
139
169
|
```
|
|
140
170
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|---------|-------------|
|
|
145
|
-
| `memoir activate` | Enable auto-recall in this project |
|
|
146
|
-
| `memoir deactivate` | Remove memoir from this project |
|
|
147
|
-
| `memoir push` | Back up AI memory + workspace + session |
|
|
148
|
-
| `memoir restore` | Restore everything on a new machine |
|
|
149
|
-
| `memoir status` | Show detected AI tools |
|
|
150
|
-
| `memoir migrate` | Translate memory between tools via AI |
|
|
151
|
-
| `memoir snapshot` | Capture current coding session |
|
|
152
|
-
| `memoir resume` | Pick up where you left off |
|
|
153
|
-
| `memoir encrypt` | Toggle E2E encryption |
|
|
154
|
-
| `memoir profile` | Manage profiles (personal/work) |
|
|
155
|
-
| `memoir cloud push` | Back up to memoir cloud |
|
|
156
|
-
| `memoir cloud restore` | Restore from memoir cloud |
|
|
157
|
-
| `memoir share` | Create encrypted shareable link |
|
|
158
|
-
| `memoir consolidate` | Find duplicates, stale memories, and bloat |
|
|
159
|
-
| `memoir recall` | Search memory exactly the way your AI does — see what it would be handed |
|
|
160
|
-
| `memoir why` | Look up decisions: what, why, what was rejected |
|
|
161
|
-
| `memoir forget` | Retract a decision (`--purge` to redact a leaked secret in place); refuses if ambiguous |
|
|
162
|
-
| `memoir validate` | Check session state + entry files against the format spec |
|
|
163
|
-
| `memoir doctor` | Diagnose issues |
|
|
164
|
-
| `memoir diff` | Show changes since last backup |
|
|
165
|
-
| `memoir view` | Preview what's in your backup |
|
|
166
|
-
| `memoir update` | Self-update to latest version |
|
|
167
|
-
|
|
168
|
-
## Security
|
|
169
|
-
|
|
170
|
-
- **E2E encryption** — AES-256-GCM with scrypt key derivation
|
|
171
|
-
- **Secret scanning** — API keys, tokens, passwords auto-redacted before sync
|
|
172
|
-
- **Local MCP server** — runs on your machine, no data sent externally
|
|
173
|
-
- **Zero-knowledge cloud** — encrypted before upload
|
|
174
|
-
|
|
175
|
-
## Links
|
|
176
|
-
|
|
177
|
-
- **Website:** [memoir.sh](https://memoir.sh)
|
|
178
|
-
- **npm:** [memoir-cli](https://npmjs.org/package/memoir-cli)
|
|
179
|
-
- **Issues:** [GitHub Issues](https://github.com/camgitt/memoir/issues)
|
|
180
|
-
- **Contributing:** [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
181
|
-
|
|
182
|
-
MIT Licensed
|
|
171
|
+
Tests use synthetic homes and local Git remotes. CI declares macOS/Linux/Windows with Node 18/20/22; shell suites skip Windows. See [remediation status](docs/AUDIT-REMEDIATION.md) and [release gates](docs/RELIABILITY-ROLLOUT.md) for verification limits.
|
|
172
|
+
|
|
173
|
+
[MIT license](LICENSE)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// This entry point does not load global configuration or emit telemetry.
|
|
3
|
+
import { workCli } from '../src/work/cli.js';
|
|
4
|
+
import { workErrorMessage } from '../src/work/errors.js';
|
|
5
|
+
try { await workCli(process.argv.slice(2)); }
|
|
6
|
+
catch (error) {
|
|
7
|
+
if (error.code === 'commander.helpDisplayed' || error.code === 'commander.help') process.exitCode = 0;
|
|
8
|
+
else { console.error(workErrorMessage(error)); process.exitCode = 1; }
|
|
9
|
+
}
|
package/bin/memoir.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// Delegate project work before legacy command dispatch and telemetry hooks.
|
|
3
|
+
if (process.argv[2] === 'work') {
|
|
4
|
+
const { spawnSync } = await import('node:child_process');
|
|
5
|
+
const { fileURLToPath } = await import('node:url');
|
|
6
|
+
const result = spawnSync(process.execPath, [fileURLToPath(new URL('./memoir-work.js', import.meta.url)), ...process.argv.slice(3)], { stdio: 'inherit' });
|
|
7
|
+
process.exit(result.status ?? 1);
|
|
8
|
+
}
|
|
9
|
+
import { cloudMigrateCommand } from '../src/commands/cloud.js';
|
|
10
|
+
import { setupCommand } from '../src/integrations/setup.js';
|
|
2
11
|
import { program } from 'commander';
|
|
3
12
|
import chalk from 'chalk';
|
|
4
13
|
import boxen from 'boxen';
|
|
@@ -39,6 +48,7 @@ import { autoRefreshCommand } from '../src/commands/auto-refresh.js';
|
|
|
39
48
|
import { validateCommand } from '../src/commands/validate.js';
|
|
40
49
|
import { hooksInstallCommand, hooksUninstallCommand, hooksStatusCommand } from '../src/commands/hooks.js';
|
|
41
50
|
import { capture as track, telemetryCommand } from '../src/telemetry.js';
|
|
51
|
+
import { appendEvent } from '../src/events/log.js';
|
|
42
52
|
import { createRequire } from 'module';
|
|
43
53
|
|
|
44
54
|
const require = createRequire(import.meta.url);
|
|
@@ -117,6 +127,7 @@ program
|
|
|
117
127
|
.option('--only <tools>', 'Only sync specific tools (comma-separated)')
|
|
118
128
|
.option('-p, --profile <name>', 'Use a specific profile')
|
|
119
129
|
.option('--redact', 'Strip detected secrets from synced files before they are backed up')
|
|
130
|
+
.option('--workspace', 'Also capture project workspaces; memory-only backups are the default')
|
|
120
131
|
.action(async (options) => {
|
|
121
132
|
try {
|
|
122
133
|
await pushCommand(options);
|
|
@@ -134,6 +145,7 @@ program
|
|
|
134
145
|
.option('-i, --interactive', 'Confirm each tool before restoring')
|
|
135
146
|
.option('-p, --profile <name>', 'Use a specific profile')
|
|
136
147
|
.option('--from <token>', 'Restore from a share link token')
|
|
148
|
+
.option('--workspace', 'Also restore explicitly captured workspaces')
|
|
137
149
|
.action(async (options) => {
|
|
138
150
|
try {
|
|
139
151
|
await restoreCommand(options);
|
|
@@ -173,18 +185,20 @@ program
|
|
|
173
185
|
|
|
174
186
|
// ── Session continuity ──────────────────────────────────────────
|
|
175
187
|
program
|
|
176
|
-
.command('goal
|
|
188
|
+
.command('goal [text...]')
|
|
177
189
|
.description('Set your current goal (pinned into CLAUDE.md, syncs across machines)')
|
|
178
|
-
.
|
|
179
|
-
|
|
190
|
+
.option('--done <match>', 'Retire a goal (substring match) — tombstoned so a sync cannot bring it back')
|
|
191
|
+
.action(async (text, options) => {
|
|
192
|
+
try { await goalCommand((text || []).join(' '), options); }
|
|
180
193
|
catch (err) { console.error(chalk.red('\n✖ Error:'), err.message); process.exit(1); }
|
|
181
194
|
});
|
|
182
195
|
|
|
183
196
|
program
|
|
184
|
-
.command('next
|
|
185
|
-
.description('Add a next action')
|
|
186
|
-
.
|
|
187
|
-
|
|
197
|
+
.command('next [text...]')
|
|
198
|
+
.description('Add a next action (the list holds 8; older ones are parked, never dropped)')
|
|
199
|
+
.option('--parked', 'List parked next-actions')
|
|
200
|
+
.action(async (text, options) => {
|
|
201
|
+
try { await nextCommand((text || []).join(' '), options); }
|
|
188
202
|
catch (err) { console.error(chalk.red('\n✖ Error:'), err.message); process.exit(1); }
|
|
189
203
|
});
|
|
190
204
|
|
|
@@ -234,6 +248,7 @@ program
|
|
|
234
248
|
|
|
235
249
|
program
|
|
236
250
|
.command('recall <query...>')
|
|
251
|
+
.option('--project <path>', 'Recall from this project (defaults to the working project)')
|
|
237
252
|
.description('Search your AI memory the way memoir_recall does — see exactly what your AI would be handed')
|
|
238
253
|
.option('--limit <n>', 'Max results (default 10)')
|
|
239
254
|
.option('--json', 'Machine-readable output')
|
|
@@ -364,6 +379,7 @@ program
|
|
|
364
379
|
|
|
365
380
|
program
|
|
366
381
|
.command('resume')
|
|
382
|
+
.option('--project <path>', 'Project directory for the resume brief')
|
|
367
383
|
.description('Pick up where you left off on another machine')
|
|
368
384
|
.option('--inject', 'Write the handoff where your AI tool will read it')
|
|
369
385
|
.option('--to <tool>', 'Target tool for injection (claude, gemini, cursor, codex)')
|
|
@@ -386,7 +402,10 @@ program
|
|
|
386
402
|
const data = await res.json();
|
|
387
403
|
const latest = data.version;
|
|
388
404
|
|
|
389
|
-
|
|
405
|
+
const currentParts = VERSION.split('.').map(Number);
|
|
406
|
+
const latestParts = String(latest).split('.').map(Number);
|
|
407
|
+
const newer = latestParts.some((n, i) => n > currentParts[i] && latestParts.slice(0, i).every((v, j) => v === currentParts[j]));
|
|
408
|
+
if (!newer) {
|
|
390
409
|
console.log('\n' + boxen(
|
|
391
410
|
chalk.green('✔ Already up to date!') + '\n' +
|
|
392
411
|
chalk.gray(`v${VERSION}`),
|
|
@@ -429,6 +448,16 @@ program
|
|
|
429
448
|
}
|
|
430
449
|
});
|
|
431
450
|
|
|
451
|
+
program
|
|
452
|
+
.command('setup')
|
|
453
|
+
.description('Configure and verify project MCP integration')
|
|
454
|
+
.option('--tool <clients>', 'claude, codex, cursor, all, or auto', 'auto')
|
|
455
|
+
.option('--project <path>', 'Project directory', process.cwd())
|
|
456
|
+
.action(async options => {
|
|
457
|
+
try { await setupCommand(options); }
|
|
458
|
+
catch (err) { console.error(err.message); process.exitCode = 1; }
|
|
459
|
+
});
|
|
460
|
+
|
|
432
461
|
program
|
|
433
462
|
.command('activate')
|
|
434
463
|
.description('Add memoir instructions to this project so your AI uses it automatically')
|
|
@@ -579,6 +608,15 @@ account
|
|
|
579
608
|
// Cloud sync
|
|
580
609
|
const cloud = program.command('cloud').description('Cloud backup and restore (Pro)');
|
|
581
610
|
|
|
611
|
+
cloud.command('migrate')
|
|
612
|
+
.description('Plan migration of legacy cloud encryption; --apply verifies each replacement')
|
|
613
|
+
.option('--apply', 'Create and verify replacements before deleting legacy backups')
|
|
614
|
+
.action(async options => {
|
|
615
|
+
try { await cloudMigrateCommand(options); }
|
|
616
|
+
catch (err) { console.error(chalk.red('Migration failed:'), err.message); process.exitCode = 1; }
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
|
|
582
620
|
cloud
|
|
583
621
|
.command('push')
|
|
584
622
|
.description('Back up your AI memory to the cloud')
|
|
@@ -708,6 +746,7 @@ projects
|
|
|
708
746
|
|
|
709
747
|
program
|
|
710
748
|
.command('consolidate')
|
|
749
|
+
.option('--undo <id>', 'Restore a locally archived consolidation file')
|
|
711
750
|
.alias('tidy')
|
|
712
751
|
.description('Analyze and clean up your AI memories — find duplicates, stale files, and contradictions')
|
|
713
752
|
.option('--smart', 'Use AI to analyze memories and suggest merges (requires Gemini API key)')
|
|
@@ -746,6 +785,9 @@ program.hook('postAction', async (thisCommand, actionCommand) => {
|
|
|
746
785
|
// Anonymous, opt-out usage event. postAction already awaits a network call
|
|
747
786
|
// (checkForUpdate), so this adds no perceived latency; no-op without a key.
|
|
748
787
|
try { await track('cli_command', { command: actionCommand?.name?.() || 'unknown' }); } catch {}
|
|
788
|
+
// Local twin (events.jsonl): command name only, so `memoir recall`/`why`
|
|
789
|
+
// reads are visible next to the writes and syncs already logged there.
|
|
790
|
+
try { await appendEvent('cli_command', { command: actionCommand?.name?.() || 'unknown' }); } catch {}
|
|
749
791
|
});
|
|
750
792
|
|
|
751
793
|
program.parse();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Audit remediation and remaining roadmap
|
|
2
|
+
|
|
3
|
+
Audit base: `037e7be7afbee85dde53a1a512125510417b8bd7` (source 3.13.3). This document describes the review branch. It does not mark the original multi-week roadmap complete.
|
|
4
|
+
|
|
5
|
+
The initial remediation measurements below describe `bf30217`. The subsequent [incremental retrieval index](RETRIEVAL-INDEX.md) adds canonical parse caching, scoped postings, per-query inventory checks, final source validation, and immediate project-file discovery. Its tests and larger matched performance comparison are separate from those original measurements.
|
|
6
|
+
|
|
7
|
+
## Findings and evidence
|
|
8
|
+
|
|
9
|
+
| Audit | Implemented in this branch | Remaining boundary |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| A01 Cloud key ownership | New user-passphrase format; legacy read warnings; plan/apply migration with download verification | Hosted rollout, independent protocol review, random vault keys/device enrollment |
|
|
12
|
+
| A02 File access | Portable path validation, adapter allowlists, no-follow reads, atomic safe writes in MCP/store | Concurrent hostile parent-directory swaps need further OS-level review |
|
|
13
|
+
| A03 Unsafe/incomplete restore | Validate manifests/paths/duplicates/size; require every blob; stage complete file sets | Native multi-root writes are not one transaction |
|
|
14
|
+
| A04 Repeated encrypted push | Authenticate prior snapshot, merge session/canonical records, preserve untouched files | Ten successive local encrypted cycles tested; hosted Git transport is CI/manual scope |
|
|
15
|
+
| A05 Partial sync/collisions | Additive plaintext Git updates; project identity suffix/manifest; consistent main-branch reads | Rename/remote aliases and legacy ambiguous mapping need review |
|
|
16
|
+
| A06 Forgetting | Consistent visibility; uncapped tombstones; canonical purge wins over stale history | Old clients and historical remote copies remain outside the guarantee |
|
|
17
|
+
| A07 Storage caps | Durable decision/goal/question archives and uncapped parked/completed/history data | No general operation journal/compaction protocol yet |
|
|
18
|
+
| A08 Encryption defaults | Local first-use configuration; headless encryption choice must be explicit; local migration replaces plaintext | Old Git/provider history can retain plaintext |
|
|
19
|
+
| A09 Locking | Never run unlocked on timeout; preserve living owner; lock full local backup cycle | Crash recovery of reaper locks; Git conflict retry is user-triggered |
|
|
20
|
+
| A10 Scope | Canonical project/shared metadata; filter before retrieval; neutral global instructions | Unlabelled legacy files shared; profiles are not isolated stores |
|
|
21
|
+
| A11 Save/recall | Canonical cross-tool store, direct session retrieval, ID-based expansion | Other clients' native memory is still import/export, not complete native continuity |
|
|
22
|
+
| A12 Setup | Claude/Codex/Cursor project config, preservation, real MCP server handshake | Real client trust/installation acceptance not automated here |
|
|
23
|
+
| A13 Retrieval | Unicode/CJK, IDF weights, lifecycle filtering, source lines, passage budget, incremental lexical index, scoped exhaustive reference | No disk-backed FTS/semantic index, held-out coding-utility benchmark, or SOTA claim |
|
|
24
|
+
| A14 Capture/consolidation | Scoped capture, unverified records, local consolidation archive/undo, bounded model request | Outcome-backed lessons and measured capture precision remain |
|
|
25
|
+
| A15 Workspace | Explicit selected-project file snapshot; secret omissions; hashes; separate recovery directory; disable old tar extraction | Omitted files/Git history require separate backup; detection is heuristic |
|
|
26
|
+
| A16 Release | Reviewable source candidate and installed-artifact smoke workflow | npm trusted publisher, version selection, publication, registry smoke |
|
|
27
|
+
| A17 Dependencies/tests | Dependency refresh, zero audit advisories at verification, adversarial suite, expanded CI matrix | Hosted matrix results must be checked before release |
|
|
28
|
+
| A18 Error/telemetry/cloud | Argument-array Git calls, propagated failures, success-labelled events, atomic version SQL | Production tenant policies, conditional cloud commit/retention races, task-outcome telemetry |
|
|
29
|
+
|
|
30
|
+
## Initial remediation evidence (bf30217)
|
|
31
|
+
|
|
32
|
+
Local verification passed 19/19 suites and the installed-tarball smoke workflow. The audit integration suite contains 26 groups and covers portable paths, symlinks, incomplete encryption, ciphertext swaps, cloud traversal, user-held cloud keys, locks, durable history/deletion, MCP boundaries, cross-tool recall, project isolation, Unicode, collision-safe exports, partial Git updates, repeated encryption, Git main/master selection and removal of plaintext from the current encrypted tree, purge versus stale history, client configuration, resume drift, verified migration, workspace recovery, and consolidation undo.
|
|
33
|
+
|
|
34
|
+
Development retrieval fixture: 14 synthetic records, 16 cases. Recall@5 and reciprocal rank were 1.0 on the 10 positive cases; all 6 abstention cases returned no results; no forbidden results. The unscoped substring control scored 0.8 recall@5 and leaked 6 forbidden results. This fixture was created after implementation. It is **not held out**, a comparison with the previous released engine, a competitor evaluation, or evidence of SOTA.
|
|
35
|
+
|
|
36
|
+
Observed MCP performance on macOS arm64 / Node 26.7.0, eight queries per corpus (first cold, seven warm), tiny synthetic adapter records:
|
|
37
|
+
|
|
38
|
+
| Files | Cold ms | Warm median ms | Warm max ms |
|
|
39
|
+
|---:|---:|---:|---:|
|
|
40
|
+
| 100 | 35.7 | 8.5 | 10.4 |
|
|
41
|
+
| 1,000 | 235.3 | 58.0 | 74.6 |
|
|
42
|
+
| 10,000 | 1,882.5 | 566.0 | 578.1 |
|
|
43
|
+
|
|
44
|
+
Batching independent checks reduced the initial safety-patch warm median at 10,000 files from 1,479.7 ms to 566.0 ms. Every file still undergoes boundary validation. This remains slower than the old unsafe path and misses the roadmap's 250 ms target at 10,000 records. The measured fixtures were adapted to valid project scope, so the old/new numbers are directional rather than a fully controlled benchmark.
|
|
45
|
+
|
|
46
|
+
## Next gates, in order
|
|
47
|
+
|
|
48
|
+
1. Review and merge this containment/continuity candidate only after final local and hosted CI evidence.
|
|
49
|
+
2. Validate real client acceptance and hosted cloud migration/tenant policies; configure publication and test the registry artifact.
|
|
50
|
+
3. Build an operation journal and conditional cloud commit/retention protocol; finish project aliases and legacy scope migration.
|
|
51
|
+
4. Freeze held-out coding-continuity tasks and a larger multilingual retrieval corpus. Compare the released version, lexical controls, and relevant systems under equal context/cost.
|
|
52
|
+
5. Extend the process-local lexical index with persistence or optional semantic candidates only where evaluation justifies them; measure memory and cold-start costs as well as warm latency.
|
|
53
|
+
6. Add evidence-backed reusable lessons, real first-use/returning-use measurements, and independent security/recovery review.
|
|
54
|
+
|
|
55
|
+
The original roadmap's comparative quality, adoption, and independent-review gates require evidence from real users and external systems. They cannot be completed by passing synthetic repository tests.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Case study: the /tape amnesia — and what memoir should do about it
|
|
2
|
+
|
|
3
|
+
**Date: 2026-08-07 · Source: first-party, Cam's own portfolio (AlgoThesis session) · Status: design input for the next memoir working session — read alongside the north-star spec ([memory: project_memoir_north_star_2026_08_06], continuity benchmark section).**
|
|
4
|
+
|
|
5
|
+
## What happened, exactly
|
|
6
|
+
|
|
7
|
+
`algothesis.ai/tape` is a fully-built, shipped product surface — the Wire news feed as a TikTok/Reels-style vertical swipe experience, deliberately designed as an A/B against the list view, with its own analytics dimension. It was real, live, and load-bearing for product strategy.
|
|
8
|
+
|
|
9
|
+
It existed in **zero** memory artifacts: not the global memory index, not any topic file, not the repo's CLAUDE.md, not the plans. Whichever session built it never wrote it down, and every later session inherited the hole. Then, in one afternoon:
|
|
10
|
+
|
|
11
|
+
1. A 3-agent "public experience" audit evaluated the product **without its best mobile surface** — and read as complete. Silent failure.
|
|
12
|
+
2. Cam half-remembered it ("TikTok reels format, flip up, video auto-generator?") and asked the assistant what he was thinking of.
|
|
13
|
+
3. The assistant searched memory for the words, found nothing, and **confidently told him the thing he built doesn't exist** — with search receipts, which made the wrong answer more convincing.
|
|
14
|
+
4. Cam doubted his own memory ("what am I thinking about?"). **The failure gaslit the person who was right.**
|
|
15
|
+
5. Recovery came only when he produced the literal URL. The assistant then found ~500 lines of shipped code.
|
|
16
|
+
|
|
17
|
+
## Why this case matters to memoir
|
|
18
|
+
|
|
19
|
+
- It's the product thesis in one sentence: **"The AI told me my own feature didn't exist."**
|
|
20
|
+
- The failure was *silent* (the audit looked complete), *compounding* (every downstream plan inherited it), and its human cost was *self-doubt*, not just rework. That's the emotional core memoir sells against — sharper than "it forgets your preferences."
|
|
21
|
+
- It's first-party and fully documented: timestamps, the wrong assertion, the recovery, the fix commits. Usable in marketing without hypotheticals.
|
|
22
|
+
|
|
23
|
+
## The three failure layers (each is a distinct memoir feature question)
|
|
24
|
+
|
|
25
|
+
1. **Capture never happened.** The build session shipped a whole surface and indexed nothing. Auto-capture is memoir's thesis, but note the *shape* of what was missed: not a "decision" in the conversational sense — a **shipped artifact**. Capture heuristics tuned on "we decided X" phrasing would likely have missed this too. → Possible fix: **artifact-aware capture** — at session end, diff what exists (new routes, new pages, new top-level components, new CLI commands) against what memory says exists, and prompt/write the delta. Route files and nav entries are cheap, high-signal proxies for "a surface now exists."
|
|
26
|
+
2. **Retrieval was lexical, concept was needed.** The search was "tiktok|reels|swipe"; the artifact's own vocabulary was "tape," "vertical feed," "scroll-snap," "panels." Zero overlap. → Possible fixes: embedding/concept recall as a fallback when lexical recall returns empty; and at *write* time, store aliases ("aka: reels-style, TikTok-format, vertical swipe") — capture-side synonym enrichment is cheaper and more reliable than smarter search.
|
|
27
|
+
3. **Some knowledge should never be recall-dependent.** "What surfaces does this product have" belongs in deterministic, always-loaded context (the repo CLAUDE.md), not in probabilistic memory. The fix applied was a hand-written "Surfaces" section in AlgoThesis's CLAUDE.md. → Possible memoir feature: a **product-map convention** — memoir offers to maintain a `## Surfaces` / inventory block in the project's always-loaded file, generated from the artifact-diff in (1). Memoir's job then isn't just remembering — it's *promoting* the right memories into deterministic context. That promotion step may be the genuinely novel feature here.
|
|
28
|
+
|
|
29
|
+
## The failure mode worth testing explicitly: confident denial
|
|
30
|
+
|
|
31
|
+
The dangerous behavior wasn't "no results" — it was converting *absence of memory* into *assertion of nonexistence*. A system that answered "I find nothing, but my capture may be incomplete — does it have a URL?" would have cost 30 seconds instead of a credibility hit. → Benchmark should score **denial** as a distinct, heavily-penalized outcome, separate from mere retrieval failure.
|
|
32
|
+
|
|
33
|
+
## Benchmark item sketch (for the continuity benchmark)
|
|
34
|
+
|
|
35
|
+
Seed a store with N sessions where one session builds a surface/feature and never indexes it (realistic: include the artifact in repo state, absent from memory). Later prompts: (a) "audit this product's surfaces"; (b) "I remember something like TikTok reels — what am I thinking of?" Score: recovered from repo signals / admitted uncertainty / **denied existence** (fail, weighted hardest). Variant: the human's cue uses different vocabulary than the artifact (the alias problem, layer 2).
|
|
36
|
+
|
|
37
|
+
## Where the full record lives
|
|
38
|
+
|
|
39
|
+
Global memory: `reference_memoir_tape_amnesia_case_2026_08_07.md` (assistant memory dir). Fix commits in algothesis: CLAUDE.md surface map `4f9365b`, plan v1.2 `/tape` revision `8fc76bd`.
|