memoir-cli 3.6.1 → 3.7.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.
@@ -0,0 +1,296 @@
1
+ // Session state: the canonical source of truth for "what are we working on"
2
+ // across sessions and machines. Rendered into CLAUDE.md (and other tools) as a
3
+ // pinned block at the top, guaranteed to load.
4
+ //
5
+ // File: ~/.config/memoir/session.json
6
+ // See CLAUDE.md pinned block for how this gets displayed.
7
+
8
+ import fs from 'fs-extra';
9
+ import path from 'path';
10
+ import os from 'os';
11
+ import crypto from 'crypto';
12
+
13
+ const home = os.homedir();
14
+ const CONFIG_DIR = path.join(home, '.config', 'memoir');
15
+ const SESSION_PATH = path.join(CONFIG_DIR, 'session.json');
16
+ const MACHINE_ID_PATH = path.join(CONFIG_DIR, 'machine.id');
17
+
18
+ export const SCHEMA_VERSION = 1;
19
+
20
+ // Maximum items kept in each list before oldest entries rotate into history.
21
+ // Prevents unbounded growth of the live pinned block.
22
+ const MAX_GOALS = 3;
23
+ const MAX_NEXT = 8;
24
+ const MAX_QUESTIONS = 5;
25
+ const MAX_DECISIONS_RECENT = 10;
26
+ const MAX_HISTORY = 30;
27
+
28
+ // ── Machine identity ─────────────────────────────────────────────
29
+
30
+ // Stable per-machine identifier. Persisted once, reused forever.
31
+ // We pair a UUID (stable across hostname changes) with a human label (hostname)
32
+ // for display — "mac-mini (abc1234)".
33
+ export async function getMachineId() {
34
+ try {
35
+ if (await fs.pathExists(MACHINE_ID_PATH)) {
36
+ const id = (await fs.readFile(MACHINE_ID_PATH, 'utf8')).trim();
37
+ if (id) return { id, label: os.hostname() };
38
+ }
39
+ } catch {}
40
+
41
+ const id = crypto.randomUUID();
42
+ await fs.ensureDir(CONFIG_DIR);
43
+ await fs.writeFile(MACHINE_ID_PATH, id);
44
+ return { id, label: os.hostname() };
45
+ }
46
+
47
+ // ── Schema ───────────────────────────────────────────────────────
48
+
49
+ function emptySession() {
50
+ return {
51
+ version: SCHEMA_VERSION,
52
+ created_at: new Date().toISOString(),
53
+ updated_at: new Date().toISOString(),
54
+ machines: {}, // { [machineId]: { label, last_seen } }
55
+ current: {
56
+ goals: [], // { text, machine_id, set_on }
57
+ next_actions: [], // { text, machine_id, added, completed? }
58
+ open_questions: [],// { text, machine_id, asked }
59
+ decisions: [], // { text, why?, rejected?, machine_id, date }
60
+ },
61
+ history: [], // { date, machine_id, summary, files_touched, duration_min? }
62
+ };
63
+ }
64
+
65
+ // ── Read / write ─────────────────────────────────────────────────
66
+
67
+ // Atomic read with graceful recovery from corrupted JSON.
68
+ export async function readSession() {
69
+ if (!await fs.pathExists(SESSION_PATH)) return emptySession();
70
+
71
+ try {
72
+ const raw = await fs.readFile(SESSION_PATH, 'utf8');
73
+ const parsed = JSON.parse(raw);
74
+ return migrateIfNeeded(parsed);
75
+ } catch (err) {
76
+ // Corrupted — preserve it for inspection, start fresh.
77
+ const backup = `${SESSION_PATH}.corrupted-${Date.now()}`;
78
+ try { await fs.copy(SESSION_PATH, backup); } catch {}
79
+ return emptySession();
80
+ }
81
+ }
82
+
83
+ // Atomic write: write to tmp, rename. Prevents torn writes on crash.
84
+ export async function writeSession(state) {
85
+ await fs.ensureDir(CONFIG_DIR);
86
+ state.updated_at = new Date().toISOString();
87
+ const tmp = `${SESSION_PATH}.tmp-${process.pid}`;
88
+ await fs.writeFile(tmp, JSON.stringify(state, null, 2));
89
+ await fs.move(tmp, SESSION_PATH, { overwrite: true });
90
+ }
91
+
92
+ function migrateIfNeeded(state) {
93
+ if (state && state.version === SCHEMA_VERSION) return state;
94
+ // Future versions: add migration steps here.
95
+ // For now, if version mismatch, merge defaults to fill gaps.
96
+ const fresh = emptySession();
97
+ return {
98
+ ...fresh,
99
+ ...state,
100
+ version: SCHEMA_VERSION,
101
+ current: { ...fresh.current, ...(state?.current || {}) },
102
+ machines: { ...fresh.machines, ...(state?.machines || {}) },
103
+ history: Array.isArray(state?.history) ? state.history : [],
104
+ };
105
+ }
106
+
107
+ // ── Machine registration ────────────────────────────────────────
108
+
109
+ async function touchMachine(state) {
110
+ const { id, label } = await getMachineId();
111
+ state.machines[id] = {
112
+ label,
113
+ last_seen: new Date().toISOString(),
114
+ };
115
+ return id;
116
+ }
117
+
118
+ // ── Mutators ────────────────────────────────────────────────────
119
+
120
+ export async function addGoal(text) {
121
+ const state = await readSession();
122
+ const machineId = await touchMachine(state);
123
+ state.current.goals.unshift({
124
+ text,
125
+ machine_id: machineId,
126
+ set_on: new Date().toISOString(),
127
+ });
128
+ state.current.goals = state.current.goals.slice(0, MAX_GOALS);
129
+ await writeSession(state);
130
+ return state;
131
+ }
132
+
133
+ export async function addNext(text) {
134
+ const state = await readSession();
135
+ const machineId = await touchMachine(state);
136
+ // Dedupe by text (case-insensitive)
137
+ const normalized = text.trim().toLowerCase();
138
+ const exists = state.current.next_actions.some(a => a.text.trim().toLowerCase() === normalized);
139
+ if (!exists) {
140
+ state.current.next_actions.push({
141
+ text,
142
+ machine_id: machineId,
143
+ added: new Date().toISOString(),
144
+ });
145
+ state.current.next_actions = state.current.next_actions.slice(-MAX_NEXT);
146
+ }
147
+ await writeSession(state);
148
+ return state;
149
+ }
150
+
151
+ export async function completeNext(textOrIndex) {
152
+ const state = await readSession();
153
+ await touchMachine(state);
154
+ let idx = -1;
155
+ if (typeof textOrIndex === 'number') {
156
+ idx = textOrIndex;
157
+ } else {
158
+ const normalized = String(textOrIndex).trim().toLowerCase();
159
+ idx = state.current.next_actions.findIndex(a => a.text.trim().toLowerCase().includes(normalized));
160
+ }
161
+ if (idx >= 0) {
162
+ state.current.next_actions.splice(idx, 1);
163
+ }
164
+ await writeSession(state);
165
+ return state;
166
+ }
167
+
168
+ export async function addNote(text, opts = {}) {
169
+ const state = await readSession();
170
+ const machineId = await touchMachine(state);
171
+ const decision = {
172
+ text,
173
+ machine_id: machineId,
174
+ date: new Date().toISOString(),
175
+ };
176
+ if (opts.why) decision.why = opts.why;
177
+ if (opts.rejected) decision.rejected = opts.rejected;
178
+ state.current.decisions.unshift(decision);
179
+ state.current.decisions = state.current.decisions.slice(0, MAX_DECISIONS_RECENT);
180
+ await writeSession(state);
181
+ return state;
182
+ }
183
+
184
+ export async function addQuestion(text) {
185
+ const state = await readSession();
186
+ const machineId = await touchMachine(state);
187
+ state.current.open_questions.push({
188
+ text,
189
+ machine_id: machineId,
190
+ asked: new Date().toISOString(),
191
+ });
192
+ state.current.open_questions = state.current.open_questions.slice(-MAX_QUESTIONS);
193
+ await writeSession(state);
194
+ return state;
195
+ }
196
+
197
+ // Roll up the current state into a history entry. Use at session end / push.
198
+ // Does not clear `current` — these are "the working set," not per-session scratch.
199
+ export async function recordSessionEnd({ summary, filesTouched = [], durationMin = null } = {}) {
200
+ const state = await readSession();
201
+ const machineId = await touchMachine(state);
202
+ state.history.unshift({
203
+ date: new Date().toISOString(),
204
+ machine_id: machineId,
205
+ summary: summary || '',
206
+ files_touched: filesTouched.slice(0, 20),
207
+ duration_min: durationMin,
208
+ });
209
+ state.history = state.history.slice(0, MAX_HISTORY);
210
+ await writeSession(state);
211
+ return state;
212
+ }
213
+
214
+ // ── Cross-machine merge ─────────────────────────────────────────
215
+
216
+ // Merge a remote session (from another machine's backup) into local.
217
+ // Never clobbers — unions lists, dedupes by text, keeps newest timestamp.
218
+ // Machine entries accumulate so we can show "last seen on X".
219
+ export function mergeSessions(local, remote) {
220
+ if (!remote) return local;
221
+ if (!local) local = { ...remote };
222
+
223
+ const merged = {
224
+ version: SCHEMA_VERSION,
225
+ created_at: earliest(local.created_at, remote.created_at),
226
+ updated_at: latest(local.updated_at, remote.updated_at),
227
+ machines: { ...remote.machines, ...local.machines }, // local wins for same machine
228
+ current: {
229
+ goals: unionByText(local.current?.goals, remote.current?.goals, 'set_on', MAX_GOALS),
230
+ next_actions: unionByText(local.current?.next_actions, remote.current?.next_actions, 'added', MAX_NEXT),
231
+ open_questions: unionByText(local.current?.open_questions, remote.current?.open_questions, 'asked', MAX_QUESTIONS),
232
+ decisions: unionByText(local.current?.decisions, remote.current?.decisions, 'date', MAX_DECISIONS_RECENT),
233
+ },
234
+ history: mergeHistory(local.history, remote.history),
235
+ };
236
+
237
+ // machines: union last_seen per id (take the newer)
238
+ for (const [id, entry] of Object.entries(remote.machines || {})) {
239
+ const existing = merged.machines[id];
240
+ if (!existing || new Date(entry.last_seen) > new Date(existing.last_seen)) {
241
+ merged.machines[id] = entry;
242
+ }
243
+ }
244
+
245
+ return merged;
246
+ }
247
+
248
+ function unionByText(a = [], b = [], dateField, cap) {
249
+ const byText = new Map();
250
+ for (const item of [...a, ...b]) {
251
+ if (!item || !item.text) continue;
252
+ const key = item.text.trim().toLowerCase();
253
+ const existing = byText.get(key);
254
+ if (!existing || new Date(item[dateField] || 0) > new Date(existing[dateField] || 0)) {
255
+ byText.set(key, item);
256
+ }
257
+ }
258
+ return Array.from(byText.values())
259
+ .sort((x, y) => new Date(y[dateField] || 0) - new Date(x[dateField] || 0))
260
+ .slice(0, cap);
261
+ }
262
+
263
+ function mergeHistory(a = [], b = []) {
264
+ const seen = new Set();
265
+ const all = [...a, ...b].filter(h => h && h.date);
266
+ // Dedupe by (date + machine_id + summary) — the three keys that make a session unique
267
+ const unique = all.filter(h => {
268
+ const key = `${h.date}|${h.machine_id}|${(h.summary || '').slice(0, 50)}`;
269
+ if (seen.has(key)) return false;
270
+ seen.add(key);
271
+ return true;
272
+ });
273
+ return unique
274
+ .sort((x, y) => new Date(y.date) - new Date(x.date))
275
+ .slice(0, MAX_HISTORY);
276
+ }
277
+
278
+ function earliest(a, b) {
279
+ if (!a) return b;
280
+ if (!b) return a;
281
+ return new Date(a) < new Date(b) ? a : b;
282
+ }
283
+
284
+ function latest(a, b) {
285
+ if (!a) return b;
286
+ if (!b) return a;
287
+ return new Date(a) > new Date(b) ? a : b;
288
+ }
289
+
290
+ // ── Paths (exported for tests + other modules) ──────────────────
291
+
292
+ export const paths = {
293
+ config: CONFIG_DIR,
294
+ session: SESSION_PATH,
295
+ machineId: MACHINE_ID_PATH,
296
+ };
@@ -1,26 +0,0 @@
1
- ---
2
- name: Bug report
3
- about: Something isn't working
4
- title: ''
5
- labels: bug
6
- assignees: ''
7
- ---
8
-
9
- **What happened?**
10
- A clear description of the bug.
11
-
12
- **Steps to reproduce**
13
- 1. Run `memoir ...`
14
- 2. See error
15
-
16
- **Expected behavior**
17
- What you expected to happen.
18
-
19
- **Environment**
20
- - OS: [e.g. macOS 15, Windows 11, Ubuntu 24]
21
- - Node: [e.g. 20.11.0]
22
- - memoir version: [e.g. 3.2.2]
23
- - AI tools: [e.g. Claude Code, Cursor]
24
-
25
- **Logs / screenshots**
26
- Paste any error output here.
@@ -1,16 +0,0 @@
1
- ---
2
- name: Feature request
3
- about: Suggest an idea for memoir
4
- title: ''
5
- labels: enhancement
6
- assignees: ''
7
- ---
8
-
9
- **What would you like?**
10
- A clear description of the feature.
11
-
12
- **Why?**
13
- What problem does this solve for you?
14
-
15
- **Alternatives considered**
16
- Any workarounds you've tried.
package/CONTRIBUTING.md DELETED
@@ -1,47 +0,0 @@
1
- # Contributing to memoir
2
-
3
- Thanks for your interest in contributing! memoir is open source and welcomes contributions.
4
-
5
- ## Quick start
6
-
7
- ```bash
8
- git clone https://github.com/camgitt/memoir.git
9
- cd memoir
10
- npm install
11
- node bin/memoir.js status
12
- ```
13
-
14
- ## What to work on
15
-
16
- - **New tool adapters** — add support for more AI tools in `src/tools/`
17
- - **MCP improvements** — enhance the MCP server in `src/mcp.js`
18
- - **Bug fixes** — check [open issues](https://github.com/camgitt/memoir/issues)
19
- - **Documentation** — improve README, add examples
20
-
21
- ## Submitting changes
22
-
23
- 1. Fork the repo
24
- 2. Create a branch (`git checkout -b feature/my-feature`)
25
- 3. Make your changes
26
- 4. Test locally: `npm test`
27
- 5. Commit and push
28
- 6. Open a PR with a clear description
29
-
30
- ## Code style
31
-
32
- - ES modules (`import`/`export`)
33
- - No TypeScript (plain JS)
34
- - Keep dependencies minimal
35
-
36
- ## Adding a tool adapter
37
-
38
- See `src/tools/claude.js` for an example. Each adapter exports:
39
- - `name` — display name
40
- - `icon` — emoji
41
- - `source` — path to the tool's config directory
42
- - `files` — specific files to sync (if `customExtract` is true)
43
- - `filter` — function to include/exclude files
44
-
45
- ## Questions?
46
-
47
- Open an issue or start a discussion.
package/demo.svg DELETED
@@ -1,201 +0,0 @@
1
- <svg viewBox="0 0 700 1260" xmlns="http://www.w3.org/2000/svg" width="700" preserveAspectRatio="xMidYMid meet">
2
- <defs>
3
- <style>
4
- @keyframes fadeIn {
5
- 0% { opacity: 0; }
6
- 5% { opacity: 1; }
7
- 100% { opacity: 1; }
8
- }
9
-
10
- @keyframes blink {
11
- 0%, 49%, 100% { opacity: 1; }
12
- 50%, 99% { opacity: 0; }
13
- }
14
-
15
- .terminal-bg { fill: #1e1e2e; }
16
-
17
- .chrome-bar { fill: #2d2d44; }
18
- .chrome-dot-red { fill: #ff5f56; }
19
- .chrome-dot-yellow { fill: #ffbd2e; }
20
- .chrome-dot-green { fill: #27c93f; }
21
-
22
- .title { font-size: 24px; font-weight: bold; fill: #22d3ee; font-family: 'JetBrains Mono', monospace; }
23
- .subtitle { font-size: 14px; fill: #71717a; font-family: 'JetBrains Mono', monospace; }
24
- .command { font-size: 16px; fill: #e4e4e7; font-family: 'JetBrains Mono', monospace; }
25
- .prompt { font-size: 16px; fill: #7c6ef0; font-family: 'JetBrains Mono', monospace; font-weight: bold; }
26
- .success { font-size: 16px; fill: #4ade80; font-family: 'JetBrains Mono', monospace; }
27
- .comment { font-size: 14px; fill: #eab308; font-family: 'JetBrains Mono', monospace; }
28
- .dim { font-size: 14px; fill: #71717a; font-family: 'JetBrains Mono', monospace; }
29
- .error { font-size: 16px; fill: #ef4444; font-family: 'JetBrains Mono', monospace; }
30
-
31
- .line-1 { animation: fadeIn 0.3s ease-out 0.0s both; }
32
- .line-2 { animation: fadeIn 0.3s ease-out 0.3s both; }
33
- .line-3 { animation: fadeIn 0.3s ease-out 0.6s both; }
34
- .line-4 { animation: fadeIn 0.3s ease-out 0.9s both; }
35
- .line-5 { animation: fadeIn 0.3s ease-out 1.2s both; }
36
- .line-6 { animation: fadeIn 0.3s ease-out 1.5s both; }
37
- .line-7 { animation: fadeIn 0.3s ease-out 1.8s both; }
38
- .line-8 { animation: fadeIn 0.3s ease-out 2.1s both; }
39
- .line-9 { animation: fadeIn 0.3s ease-out 2.4s both; }
40
- .line-10 { animation: fadeIn 0.3s ease-out 2.7s both; }
41
- .line-11 { animation: fadeIn 0.3s ease-out 3.0s both; }
42
- .line-12 { animation: fadeIn 0.3s ease-out 3.3s both; }
43
- .line-13 { animation: fadeIn 0.3s ease-out 3.6s both; }
44
- .line-14 { animation: fadeIn 0.3s ease-out 3.9s both; }
45
- .line-15 { animation: fadeIn 0.3s ease-out 4.2s both; }
46
- .line-16 { animation: fadeIn 0.3s ease-out 4.5s both; }
47
- .line-17 { animation: fadeIn 0.3s ease-out 4.8s both; }
48
- .line-18 { animation: fadeIn 0.3s ease-out 5.1s both; }
49
- .line-19 { animation: fadeIn 0.3s ease-out 5.4s both; }
50
- .line-20 { animation: fadeIn 0.3s ease-out 5.7s both; }
51
- .line-21 { animation: fadeIn 0.3s ease-out 6.0s both; }
52
- .line-22 { animation: fadeIn 0.3s ease-out 6.3s both; }
53
- .line-23 { animation: fadeIn 0.3s ease-out 6.6s both; }
54
- .line-24 { animation: fadeIn 0.3s ease-out 6.9s both; }
55
- .line-25 { animation: fadeIn 0.3s ease-out 7.2s both; }
56
- .line-26 { animation: fadeIn 0.3s ease-out 7.5s both; }
57
- .line-27 { animation: fadeIn 0.3s ease-out 7.8s both; }
58
- .line-28 { animation: fadeIn 0.3s ease-out 8.1s both; }
59
- .line-29 { animation: fadeIn 0.3s ease-out 8.4s both; }
60
- .line-30 { animation: fadeIn 0.3s ease-out 8.7s both; }
61
- .line-31 { animation: fadeIn 0.3s ease-out 9.0s both; }
62
- .line-32 { animation: fadeIn 0.3s ease-out 9.3s both; }
63
- .line-33 { animation: fadeIn 0.3s ease-out 9.6s both; }
64
- .line-34 { animation: fadeIn 0.3s ease-out 9.9s both; }
65
- .line-35 { animation: fadeIn 0.3s ease-out 10.2s both; }
66
- .line-36 { animation: fadeIn 0.3s ease-out 10.5s both; }
67
- .line-37 { animation: fadeIn 0.3s ease-out 10.8s both; }
68
- .line-38 { animation: fadeIn 0.3s ease-out 11.1s both; }
69
- .line-39 { animation: fadeIn 0.3s ease-out 11.4s both; }
70
- .line-40 { animation: fadeIn 0.3s ease-out 11.7s both; }
71
- .line-41 { animation: fadeIn 0.3s ease-out 12.0s both; }
72
- .line-42 { animation: fadeIn 0.3s ease-out 12.3s both; }
73
- .line-43 { animation: fadeIn 0.3s ease-out 12.6s both; }
74
- .line-44 { animation: fadeIn 0.3s ease-out 12.9s both; }
75
-
76
- .cursor { animation: blink 0.8s infinite 13.5s; }
77
- </style>
78
- </defs>
79
-
80
- <!-- Background -->
81
- <rect class="terminal-bg" width="700" height="1260" rx="12" ry="12"/>
82
-
83
- <!-- Chrome bar -->
84
- <rect class="chrome-bar" width="700" height="30"/>
85
- <circle class="chrome-dot-red" cx="20" cy="15" r="6"/>
86
- <circle class="chrome-dot-yellow" cx="50" cy="15" r="6"/>
87
- <circle class="chrome-dot-green" cx="80" cy="15" r="6"/>
88
-
89
- <!-- Content -->
90
- <g class="line-1">
91
- <text class="title" x="20" y="80">memoir</text>
92
- <text class="subtitle" x="180" y="80">— Your AI remembers everything.</text>
93
- </g>
94
-
95
- <text class="command line-2" x="20" y="120">npm install -g memoir-cli</text>
96
-
97
- <text class="comment line-3" x="20" y="170"># See what AI tools are on this machine</text>
98
-
99
- <g class="line-4">
100
- <text class="prompt" x="20" y="210">❯ </text>
101
- <text class="command" x="50" y="210">memoir status</text>
102
- </g>
103
-
104
- <text class="subtitle line-5" x="20" y="260">memoir v3.2.0</text>
105
-
106
- <text class="subtitle line-6" x="20" y="300">Detected AI tools:</text>
107
-
108
- <g class="line-7">
109
- <text class="success" x="20" y="340">✔ </text>
110
- <text class="command" x="50" y="340">Claude Code ~/.claude/ (settings, memory, CLAUDE.md)</text>
111
- </g>
112
-
113
- <g class="line-8">
114
- <text class="success" x="20" y="375">✔ </text>
115
- <text class="command" x="50" y="375">Gemini CLI ~/.gemini/ (config, GEMINI.md)</text>
116
- </g>
117
-
118
- <g class="line-9">
119
- <text class="success" x="20" y="410">✔ </text>
120
- <text class="command" x="50" y="410">Cursor .cursorrules (settings, keybindings)</text>
121
- </g>
122
-
123
- <g class="line-10">
124
- <text class="success" x="20" y="445">✔ </text>
125
- <text class="command" x="50" y="445">Codex ~/.codex/ (config, AGENTS.md)</text>
126
- </g>
127
-
128
- <g class="line-11">
129
- <text class="success" x="20" y="480">✔ </text>
130
- <text class="command" x="50" y="480">Aider ~/.aider.conf.yml</text>
131
- </g>
132
-
133
- <text class="subtitle line-12" x="20" y="520">5 tools found on this machine.</text>
134
-
135
- <text class="comment line-13" x="20" y="570"># Back up everything in one command</text>
136
-
137
- <g class="line-14">
138
- <text class="prompt" x="20" y="610">❯ </text>
139
- <text class="command" x="50" y="610">memoir push</text>
140
- </g>
141
-
142
- <g class="line-15">
143
- <text class="success" x="20" y="655">✔ </text>
144
- <text class="command" x="50" y="655">AI memory backed up (5 tools, 23 files)</text>
145
- </g>
146
-
147
- <g class="line-16">
148
- <text class="success" x="20" y="690">✔ </text>
149
- <text class="command" x="50" y="690">Session context captured</text>
150
- </g>
151
-
152
- <g class="line-17">
153
- <text class="success" x="20" y="725">✔ </text>
154
- <text class="command" x="50" y="725">Workspace: 44 projects (17 git, 23 bundled)</text>
155
- </g>
156
-
157
- <g class="line-18">
158
- <text class="dim" x="20" y="765">🔒 E2E encrypted · pushed in 3.2s</text>
159
- </g>
160
-
161
- <text class="comment line-19" x="20" y="815"># Simulate switching machines...</text>
162
-
163
- <g class="line-20">
164
- <text class="error" x="20" y="855">[wiped all AI configs]</text>
165
- </g>
166
-
167
- <text class="comment line-21" x="20" y="905"># Restore on the new machine</text>
168
-
169
- <g class="line-22">
170
- <text class="prompt" x="20" y="945">❯ </text>
171
- <text class="command" x="50" y="945">memoir restore --yes</text>
172
- </g>
173
-
174
- <g class="line-23">
175
- <text class="success" x="20" y="990">✔ </text>
176
- <text class="command" x="50" y="990">AI memory restored (Claude, Gemini, Cursor, Codex, Aider)</text>
177
- </g>
178
-
179
- <g class="line-24">
180
- <text class="success" x="20" y="1025">✔ </text>
181
- <text class="command" x="50" y="1025">44 projects cloned &amp; unpacked</text>
182
- </g>
183
-
184
- <g class="line-25">
185
- <text class="success" x="20" y="1060">✔ </text>
186
- <text class="command" x="50" y="1060">Uncommitted changes applied</text>
187
- </g>
188
-
189
- <g class="line-26">
190
- <text class="success" x="20" y="1095">✔ </text>
191
- <text class="command" x="50" y="1095">Session context injected — AI picks up mid-conversation</text>
192
- </g>
193
-
194
- <text class="subtitle line-27" x="20" y="1140">Done. All AI memory restored in seconds.</text>
195
-
196
- <text class="command line-28" x="20" y="1180">npm install -g memoir-cli</text>
197
-
198
- <!-- Blinking cursor -->
199
- <text class="command cursor" x="20" y="1220">_</text>
200
-
201
- </svg>
package/server.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
- "name": "io.github.camgitt/memoir",
4
- "description": "Persistent memory for AI coding tools via MCP. Remembers across sessions and machines.",
5
- "repository": {
6
- "url": "https://github.com/camgitt/memoir",
7
- "source": "github"
8
- },
9
- "version": "3.2.2",
10
- "packages": [
11
- {
12
- "registryType": "npm",
13
- "identifier": "memoir-cli",
14
- "version": "3.2.2",
15
- "transport": {
16
- "type": "stdio"
17
- }
18
- }
19
- ]
20
- }