memoir-cli 2.0.2 → 2.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.
- package/bin/memoir.js +25 -0
- package/package.json +1 -1
- package/src/adapters/index.js +80 -0
- package/src/adapters/restore.js +219 -24
- package/SHOW_HN.md +0 -23
- package/demo.cast +0 -269
- package/demo.gif +0 -0
- package/demo.sh +0 -99
package/bin/memoir.js
CHANGED
|
@@ -17,6 +17,27 @@ import { createRequire } from 'module';
|
|
|
17
17
|
const require = createRequire(import.meta.url);
|
|
18
18
|
const { version: VERSION } = require('../package.json');
|
|
19
19
|
|
|
20
|
+
// Check for updates (non-blocking)
|
|
21
|
+
async function checkForUpdate() {
|
|
22
|
+
try {
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
25
|
+
const res = await fetch('https://registry.npmjs.org/memoir-cli/latest', { signal: controller.signal });
|
|
26
|
+
clearTimeout(timeout);
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
const latest = data.version;
|
|
29
|
+
if (latest && latest !== VERSION) {
|
|
30
|
+
console.log(
|
|
31
|
+
'\n' + boxen(
|
|
32
|
+
chalk.yellow(`Update available: ${VERSION} → ${chalk.green.bold(latest)}`) + '\n' +
|
|
33
|
+
chalk.gray('Run: ') + chalk.cyan('npm install -g memoir-cli'),
|
|
34
|
+
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, borderStyle: 'round', borderColor: 'yellow', dimBorder: true }
|
|
35
|
+
)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
} catch {}
|
|
39
|
+
}
|
|
40
|
+
|
|
20
41
|
// Show quick start when run with no args
|
|
21
42
|
if (process.argv.length <= 2) {
|
|
22
43
|
console.log('\n' + boxen(
|
|
@@ -171,4 +192,8 @@ program
|
|
|
171
192
|
}
|
|
172
193
|
});
|
|
173
194
|
|
|
195
|
+
program.hook('postAction', async () => {
|
|
196
|
+
await checkForUpdate();
|
|
197
|
+
});
|
|
198
|
+
|
|
174
199
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memoir-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Sync AI memory across devices. Back up and restore Claude, Gemini, Codex, Cursor, Copilot, Windsurf configs. Snapshot coding sessions and resume on another machine. Migrate instructions between AI assistants.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
package/src/adapters/index.js
CHANGED
|
@@ -219,6 +219,86 @@ export async function extractMemories(stagingDir, spinner, onlyFilter = null) {
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
// Scan for per-project AI config files
|
|
223
|
+
if (!onlyFilter || onlyFilter.some(f => 'projects'.includes(f))) {
|
|
224
|
+
spinner.text = `📁 Scanning for project-level AI configs...`;
|
|
225
|
+
|
|
226
|
+
const projectFiles = [
|
|
227
|
+
'CLAUDE.md', 'GEMINI.md', 'AGENTS.md', '.cursorrules',
|
|
228
|
+
'.github/copilot-instructions.md', '.windsurfrules',
|
|
229
|
+
'.aider.conf.yml', '.clinerules'
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
const skipDirs = new Set([
|
|
233
|
+
'node_modules', '.git', '.next', '.vercel', 'dist', 'build',
|
|
234
|
+
'__pycache__', '.venv', 'venv', '.cache', '.npm', '.bun',
|
|
235
|
+
'Library', '.Trash', 'Applications', 'Pictures', 'Music',
|
|
236
|
+
'Movies', 'Public', 'Downloads', '.local', '.cargo', '.rustup'
|
|
237
|
+
]);
|
|
238
|
+
|
|
239
|
+
const projectsDest = path.join(stagingDir, 'projects');
|
|
240
|
+
let projectCount = 0;
|
|
241
|
+
let projectFileCount = 0;
|
|
242
|
+
const projectNames = [];
|
|
243
|
+
|
|
244
|
+
// Walk home dir up to 3 levels deep looking for project markers
|
|
245
|
+
const scanDir = async (dir, depth = 0) => {
|
|
246
|
+
if (depth > 3) return;
|
|
247
|
+
let entries;
|
|
248
|
+
try {
|
|
249
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
250
|
+
} catch { return; }
|
|
251
|
+
|
|
252
|
+
// Check if this dir has any AI config files
|
|
253
|
+
const foundFiles = [];
|
|
254
|
+
for (const file of projectFiles) {
|
|
255
|
+
const filePath = path.join(dir, file);
|
|
256
|
+
if (await fs.pathExists(filePath)) {
|
|
257
|
+
foundFiles.push(file);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (foundFiles.length > 0 && dir !== home) {
|
|
262
|
+
// This is a project with AI configs
|
|
263
|
+
const projectName = path.basename(dir);
|
|
264
|
+
const projectDestDir = path.join(projectsDest, projectName);
|
|
265
|
+
await fs.ensureDir(projectDestDir);
|
|
266
|
+
|
|
267
|
+
for (const file of foundFiles) {
|
|
268
|
+
const src = path.join(dir, file);
|
|
269
|
+
const dest = path.join(projectDestDir, file);
|
|
270
|
+
await fs.ensureDir(path.dirname(dest));
|
|
271
|
+
await fs.copy(src, dest);
|
|
272
|
+
projectFileCount++;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
projectCount++;
|
|
276
|
+
projectNames.push(projectName);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Recurse into subdirectories
|
|
280
|
+
for (const entry of entries) {
|
|
281
|
+
if (!entry.isDirectory()) continue;
|
|
282
|
+
if (entry.name.startsWith('.') && entry.name !== '.github') continue;
|
|
283
|
+
if (skipDirs.has(entry.name)) continue;
|
|
284
|
+
await scanDir(path.join(dir, entry.name), depth + 1);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
await scanDir(home);
|
|
289
|
+
|
|
290
|
+
if (projectCount > 0) {
|
|
291
|
+
const size = await dirSize(projectsDest);
|
|
292
|
+
foundAny = true;
|
|
293
|
+
results.push({
|
|
294
|
+
adapter: { name: `Projects (${projectCount})`, icon: '📁' },
|
|
295
|
+
fileCount: projectFileCount,
|
|
296
|
+
size
|
|
297
|
+
});
|
|
298
|
+
spinner.text = `📁 ${chalk.green(`${projectCount} projects`)} ${chalk.gray(`(${projectFileCount} files, ${formatSize(size)})`)}`;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
222
302
|
// Print tree after scanning
|
|
223
303
|
if (results.length > 0) {
|
|
224
304
|
spinner.stop();
|
package/src/adapters/restore.js
CHANGED
|
@@ -5,27 +5,115 @@ import os from 'os';
|
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import { adapters } from '../adapters/index.js';
|
|
7
7
|
|
|
8
|
+
// Detect the local home key by looking at what Claude has ALREADY created
|
|
9
|
+
// on this machine, rather than trying to compute the encoding ourselves.
|
|
10
|
+
// Claude's path encoding varies across platforms and versions, so detection
|
|
11
|
+
// is the only reliable approach.
|
|
12
|
+
function detectLocalHomeKey(adapterSource) {
|
|
13
|
+
const localProjectsDir = path.join(adapterSource, 'projects');
|
|
14
|
+
if (!fs.existsSync(localProjectsDir)) return null;
|
|
15
|
+
|
|
16
|
+
const entries = fs.readdirSync(localProjectsDir)
|
|
17
|
+
.filter(e => fs.statSync(path.join(localProjectsDir, e)).isDirectory());
|
|
18
|
+
if (entries.length === 0) return null;
|
|
19
|
+
|
|
20
|
+
// Find dirs with a memory/ subfolder that aren't sub-projects of another dir
|
|
21
|
+
const candidates = entries.filter(entry => {
|
|
22
|
+
const hasMemory = fs.existsSync(path.join(localProjectsDir, entry, 'memory'));
|
|
23
|
+
if (!hasMemory) return false;
|
|
24
|
+
// A sub-project dir starts with another dir + '-'
|
|
25
|
+
const isSubProject = entries.some(other =>
|
|
26
|
+
other !== entry && entry.startsWith(other + '-')
|
|
27
|
+
);
|
|
28
|
+
return !isSubProject;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (candidates.length === 1) return candidates[0];
|
|
32
|
+
|
|
33
|
+
if (candidates.length > 1) {
|
|
34
|
+
// Multiple home-key candidates (e.g. encoding changed between Claude versions)
|
|
35
|
+
// Pick the most recently modified one — that's what Claude is actively using
|
|
36
|
+
return candidates.sort((a, b) => {
|
|
37
|
+
const aDir = path.join(localProjectsDir, a, 'memory');
|
|
38
|
+
const bDir = path.join(localProjectsDir, b, 'memory');
|
|
39
|
+
return fs.statSync(bDir).mtimeMs - fs.statSync(aDir).mtimeMs;
|
|
40
|
+
})[0];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// No dir has memory/ — fall back to shortest dir that's a prefix of others
|
|
44
|
+
const prefixDirs = entries.filter(entry =>
|
|
45
|
+
entries.some(other => other !== entry && other.startsWith(entry + '-'))
|
|
46
|
+
).sort((a, b) => a.length - b.length);
|
|
47
|
+
|
|
48
|
+
return prefixDirs[0] || entries[0];
|
|
49
|
+
}
|
|
50
|
+
|
|
8
51
|
// Claude CLI stores projects under paths like `projects/-Users-camarthur/`
|
|
9
|
-
// This
|
|
10
|
-
function
|
|
52
|
+
// This remaps ALL foreign machine dirs to match the current machine.
|
|
53
|
+
function remapProjectPaths(backupDir, adapterSource) {
|
|
11
54
|
const projectsDir = path.join(backupDir, 'projects');
|
|
12
|
-
if (!fs.existsSync(projectsDir)) return
|
|
55
|
+
if (!fs.existsSync(projectsDir)) return [];
|
|
13
56
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
57
|
+
const backupEntries = fs.readdirSync(projectsDir)
|
|
58
|
+
.filter(e => fs.statSync(path.join(projectsDir, e)).isDirectory());
|
|
59
|
+
if (backupEntries.length === 0) return [];
|
|
60
|
+
|
|
61
|
+
// Step 1: Detect the local home key from existing Claude dirs
|
|
62
|
+
let localHomeKey = detectLocalHomeKey(adapterSource);
|
|
63
|
+
|
|
64
|
+
// Step 2: Fallback — compute from homedir (only for fresh installs)
|
|
65
|
+
if (!localHomeKey) {
|
|
66
|
+
const home = os.homedir();
|
|
67
|
+
// Use the same encoding Claude uses: path with separators → dashes
|
|
68
|
+
localHomeKey = '-' + home.replace(/^\//, '').replace(/\\/g, '-').replace(/\//g, '-').replace(/:/g, '');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Step 3: Identify foreign home keys in the backup
|
|
72
|
+
// A "home key" is a dir that: has memory/, OR is a prefix of other dirs, AND is not a sub-project
|
|
73
|
+
const foreignHomeKeys = new Set();
|
|
20
74
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const newHomeKey = '-' + home.replace(/^\//, '').replace(/\\/g, '-').replace(/\//g, '-').replace(/:/g, '');
|
|
75
|
+
for (const entry of backupEntries) {
|
|
76
|
+
// Skip dirs that already belong to this machine
|
|
77
|
+
if (entry === localHomeKey || entry.startsWith(localHomeKey + '-')) continue;
|
|
25
78
|
|
|
26
|
-
|
|
79
|
+
// Is this a sub-project of another backup dir? Then skip — its parent handles it
|
|
80
|
+
const isSubProject = backupEntries.some(other =>
|
|
81
|
+
other !== entry && entry.startsWith(other + '-')
|
|
82
|
+
);
|
|
83
|
+
if (isSubProject) continue;
|
|
27
84
|
|
|
28
|
-
|
|
85
|
+
// Has memory/ subfolder = definitely a home key
|
|
86
|
+
const hasMemory = fs.existsSync(path.join(projectsDir, entry, 'memory'));
|
|
87
|
+
// Is a prefix of other dirs = likely a home key
|
|
88
|
+
const isPrefix = backupEntries.some(other =>
|
|
89
|
+
other !== entry && other.startsWith(entry + '-')
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (hasMemory || isPrefix) {
|
|
93
|
+
foreignHomeKeys.add(entry);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Step 4: Build remaps — remap each foreign home key and its sub-projects
|
|
98
|
+
const remaps = [];
|
|
99
|
+
const processed = new Set();
|
|
100
|
+
|
|
101
|
+
for (const foreignKey of foreignHomeKeys) {
|
|
102
|
+
// Find all dirs belonging to this foreign home key
|
|
103
|
+
for (const dir of backupEntries) {
|
|
104
|
+
if (processed.has(dir)) continue;
|
|
105
|
+
if (dir !== foreignKey && !dir.startsWith(foreignKey + '-')) continue;
|
|
106
|
+
|
|
107
|
+
processed.add(dir);
|
|
108
|
+
const suffix = dir.slice(foreignKey.length); // "" or "-alfred" etc.
|
|
109
|
+
const newName = localHomeKey + suffix;
|
|
110
|
+
if (dir !== newName) {
|
|
111
|
+
remaps.push({ oldName: dir, newName });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return remaps;
|
|
29
117
|
}
|
|
30
118
|
|
|
31
119
|
async function syncFiles(src, dest, changes) {
|
|
@@ -97,17 +185,24 @@ export async function restoreMemories(sourceDir, spinner, onlyFilter = null, aut
|
|
|
97
185
|
|
|
98
186
|
// Remap Claude project paths from source machine to this machine
|
|
99
187
|
if (adapter.name === 'Claude CLI') {
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
188
|
+
const remaps = remapProjectPaths(backupDir, adapter.source);
|
|
189
|
+
if (remaps.length > 0) {
|
|
102
190
|
spinner.stop();
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
191
|
+
for (const remap of remaps) {
|
|
192
|
+
console.log(chalk.gray(` Remapping: ${remap.oldName} → ${remap.newName}`));
|
|
193
|
+
const oldDir = path.join(backupDir, 'projects', remap.oldName);
|
|
194
|
+
const newDir = path.join(backupDir, 'projects', remap.newName);
|
|
195
|
+
if (await fs.pathExists(oldDir)) {
|
|
196
|
+
if (await fs.pathExists(newDir)) {
|
|
197
|
+
// Merge into existing directory
|
|
198
|
+
await syncFiles(oldDir, newDir, { added: [], updated: [], skipped: [] });
|
|
199
|
+
await fs.remove(oldDir);
|
|
200
|
+
} else {
|
|
201
|
+
await fs.move(oldDir, newDir);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
110
204
|
}
|
|
205
|
+
spinner.start();
|
|
111
206
|
}
|
|
112
207
|
}
|
|
113
208
|
|
|
@@ -168,6 +263,106 @@ export async function restoreMemories(sourceDir, spinner, onlyFilter = null, aut
|
|
|
168
263
|
}
|
|
169
264
|
}
|
|
170
265
|
|
|
266
|
+
// Restore per-project AI configs
|
|
267
|
+
const projectsDir = path.join(sourceDir, 'projects');
|
|
268
|
+
if (await fs.pathExists(projectsDir)) {
|
|
269
|
+
const projectEntries = await fs.readdir(projectsDir, { withFileTypes: true });
|
|
270
|
+
const projectDirs = projectEntries.filter(e => e.isDirectory() && e.name !== '.git');
|
|
271
|
+
|
|
272
|
+
if (projectDirs.length > 0) {
|
|
273
|
+
spinner.stop();
|
|
274
|
+
console.log('\n' + chalk.cyan(`📁 Found ${chalk.bold(projectDirs.length + ' project(s)')} with AI configs`));
|
|
275
|
+
|
|
276
|
+
// Try to find matching local project dirs
|
|
277
|
+
const home = os.homedir();
|
|
278
|
+
let totalRestored = 0;
|
|
279
|
+
|
|
280
|
+
for (const proj of projectDirs) {
|
|
281
|
+
const backupProjDir = path.join(projectsDir, proj.name);
|
|
282
|
+
const files = await fs.readdir(backupProjDir);
|
|
283
|
+
|
|
284
|
+
// Search for project on local machine (up to 3 levels deep)
|
|
285
|
+
let localProjDir = null;
|
|
286
|
+
const searchDirs = [home];
|
|
287
|
+
for (const searchDir of searchDirs) {
|
|
288
|
+
const candidate = path.join(searchDir, proj.name);
|
|
289
|
+
if (await fs.pathExists(candidate)) {
|
|
290
|
+
localProjDir = candidate;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
// Check one level deeper
|
|
294
|
+
try {
|
|
295
|
+
const entries = await fs.readdir(searchDir, { withFileTypes: true });
|
|
296
|
+
for (const e of entries) {
|
|
297
|
+
if (!e.isDirectory() || e.name.startsWith('.')) continue;
|
|
298
|
+
const deeper = path.join(searchDir, e.name, proj.name);
|
|
299
|
+
if (await fs.pathExists(deeper)) {
|
|
300
|
+
localProjDir = deeper;
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} catch {}
|
|
305
|
+
if (localProjDir) break;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (!localProjDir) {
|
|
309
|
+
console.log(chalk.gray(` ○ ${proj.name} — not found on this machine, skipping`));
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
console.log(chalk.white(` 📁 ${proj.name}`) + chalk.gray(` → ${localProjDir}`));
|
|
314
|
+
|
|
315
|
+
let confirm = true;
|
|
316
|
+
if (!autoYes) {
|
|
317
|
+
const answer = await inquirer.prompt([
|
|
318
|
+
{
|
|
319
|
+
type: 'confirm',
|
|
320
|
+
name: 'confirm',
|
|
321
|
+
message: `Restore AI configs to ${proj.name}?`,
|
|
322
|
+
default: true
|
|
323
|
+
}
|
|
324
|
+
]);
|
|
325
|
+
confirm = answer.confirm;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (confirm) {
|
|
329
|
+
for (const file of files) {
|
|
330
|
+
const src = path.join(backupProjDir, file);
|
|
331
|
+
const dest = path.join(localProjDir, file);
|
|
332
|
+
const stat = await fs.stat(src);
|
|
333
|
+
|
|
334
|
+
if (stat.isDirectory()) {
|
|
335
|
+
await fs.ensureDir(dest);
|
|
336
|
+
await syncFiles(src, dest, { added: [], updated: [], skipped: [] });
|
|
337
|
+
} else {
|
|
338
|
+
if (await fs.pathExists(dest)) {
|
|
339
|
+
const srcStat = await fs.stat(src);
|
|
340
|
+
const destStat = await fs.stat(dest);
|
|
341
|
+
if (srcStat.mtimeMs > destStat.mtimeMs) {
|
|
342
|
+
await fs.copy(src, dest);
|
|
343
|
+
console.log(chalk.yellow(` ↻ ${file}`) + chalk.gray(` (updated)`));
|
|
344
|
+
} else {
|
|
345
|
+
console.log(chalk.gray(` = ${file} (up to date)`));
|
|
346
|
+
}
|
|
347
|
+
} else {
|
|
348
|
+
await fs.ensureDir(path.dirname(dest));
|
|
349
|
+
await fs.copy(src, dest);
|
|
350
|
+
console.log(chalk.green(` + ${file}`) + chalk.gray(` (new)`));
|
|
351
|
+
}
|
|
352
|
+
totalRestored++;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (totalRestored > 0) {
|
|
359
|
+
allResults.push({ name: `Projects (${projectDirs.length})`, icon: '📁', dest: 'various', added: totalRestored, updated: 0 });
|
|
360
|
+
restoredAny = true;
|
|
361
|
+
}
|
|
362
|
+
spinner.start();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
171
366
|
// Final recap
|
|
172
367
|
if (allResults.length > 0) {
|
|
173
368
|
spinner.stop();
|
package/SHOW_HN.md
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
Title: Show HN: Memoir – Sync your AI CLI memory across devices and tools
|
|
2
|
-
|
|
3
|
-
URL: https://github.com/camgitt/memoir
|
|
4
|
-
|
|
5
|
-
Text:
|
|
6
|
-
|
|
7
|
-
I got tired of losing my AI setup every time I switched machines. My Claude Code rules, Gemini instructions, Cursor settings — all trapped in hidden dotfiles on one laptop.
|
|
8
|
-
|
|
9
|
-
Memoir is a CLI that backs up, restores, and translates your AI memory across devices and tools. It works with Claude Code, Gemini CLI, OpenAI Codex, Cursor, Copilot, Windsurf, and Aider.
|
|
10
|
-
|
|
11
|
-
Three commands:
|
|
12
|
-
|
|
13
|
-
memoir push # back up AI configs to GitHub or local folder
|
|
14
|
-
memoir restore # restore on a new machine
|
|
15
|
-
memoir migrate --from claude --to gemini # translate between tools
|
|
16
|
-
|
|
17
|
-
The migrate command uses Gemini to intelligently rewrite your instructions — not copy-paste, but actual translation that follows each tool's conventions.
|
|
18
|
-
|
|
19
|
-
It only syncs config and instruction files (settings.json, CLAUDE.md, GEMINI.md, etc). Never touches credentials, auth tokens, or .env files.
|
|
20
|
-
|
|
21
|
-
npm install -g memoir-cli
|
|
22
|
-
|
|
23
|
-
Built with Node.js. MIT licensed. Would love feedback on what tools or workflows to support next.
|
package/demo.cast
DELETED
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
{"version":3,"term":{"cols":44,"rows":24,"type":"xterm-256color","theme":{"fg":"#ffffff","bg":"#1e1e1e","palette":"#000000:#990000:#00a600:#999900:#0000b3:#b300b3:#00a6b3:#bfbfbf:#666666:#e60000:#00d900:#e6e600:#0000ff:#e600e6:#00e6e6:#e6e6e6"}},"timestamp":1772934225,"command":"bash demo.sh","env":{"SHELL":"/bin/zsh"}}
|
|
2
|
-
[0.036, "o", "\u001b[3J\u001b[H\u001b[2J"]
|
|
3
|
-
[0.000, "o", "\r\n"]
|
|
4
|
-
[0.000, "o", "\u001b[1;36m memoir — Your AI remembers everything. Sync it everywhere.\u001b[0m\r\n\u001b[0;90m https://github.com/camgitt/memoir\u001b[0m\r\n"]
|
|
5
|
-
[2.008, "o", "\r\n\u001b[1;33m# See what AI tools are on this machine\u001b[0m\r\n"]
|
|
6
|
-
[1.012, "o", "\r\n\u001b[1;32m❯\u001b[0m "]
|
|
7
|
-
[0.000, "o", "n"]
|
|
8
|
-
[0.051, "o", "o"]
|
|
9
|
-
[0.051, "o", "d"]
|
|
10
|
-
[0.051, "o", "e"]
|
|
11
|
-
[0.050, "o", " "]
|
|
12
|
-
[0.050, "o", "/"]
|
|
13
|
-
[0.049, "o", "U"]
|
|
14
|
-
[0.050, "o", "s"]
|
|
15
|
-
[0.048, "o", "e"]
|
|
16
|
-
[0.049, "o", "r"]
|
|
17
|
-
[0.048, "o", "s"]
|
|
18
|
-
[0.049, "o", "/"]
|
|
19
|
-
[0.049, "o", "c"]
|
|
20
|
-
[0.050, "o", "a"]
|
|
21
|
-
[0.051, "o", "m"]
|
|
22
|
-
[0.050, "o", "a"]
|
|
23
|
-
[0.049, "o", "r"]
|
|
24
|
-
[0.048, "o", "t"]
|
|
25
|
-
[0.047, "o", "h"]
|
|
26
|
-
[0.050, "o", "u"]
|
|
27
|
-
[0.049, "o", "r"]
|
|
28
|
-
[0.050, "o", "/"]
|
|
29
|
-
[0.050, "o", "m"]
|
|
30
|
-
[0.049, "o", "e"]
|
|
31
|
-
[0.050, "o", "m"]
|
|
32
|
-
[0.052, "o", "o"]
|
|
33
|
-
[0.049, "o", "i"]
|
|
34
|
-
[0.051, "o", "r"]
|
|
35
|
-
[0.050, "o", "/"]
|
|
36
|
-
[0.050, "o", "b"]
|
|
37
|
-
[0.047, "o", "i"]
|
|
38
|
-
[0.050, "o", "n"]
|
|
39
|
-
[0.043, "o", "/"]
|
|
40
|
-
[0.049, "o", "m"]
|
|
41
|
-
[0.049, "o", "e"]
|
|
42
|
-
[0.048, "o", "m"]
|
|
43
|
-
[0.049, "o", "o"]
|
|
44
|
-
[0.050, "o", "i"]
|
|
45
|
-
[0.048, "o", "r"]
|
|
46
|
-
[0.050, "o", "."]
|
|
47
|
-
[0.049, "o", "j"]
|
|
48
|
-
[0.049, "o", "s"]
|
|
49
|
-
[0.050, "o", " "]
|
|
50
|
-
[0.051, "o", "s"]
|
|
51
|
-
[0.050, "o", "t"]
|
|
52
|
-
[0.049, "o", "a"]
|
|
53
|
-
[0.049, "o", "t"]
|
|
54
|
-
[0.046, "o", "u"]
|
|
55
|
-
[0.049, "o", "s"]
|
|
56
|
-
[0.049, "o", "\r\n"]
|
|
57
|
-
[0.528, "o", "\r\n"]
|
|
58
|
-
[0.002, "o", "\u001b[2m\u001b[36m╭──────────────────────────────────────────╮\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[38;2;116;235;213mm\u001b[39m\u001b[38;2;116;192;235me\u001b[39m\u001b[38;2;116;127;235mm\u001b[39m\u001b[38;2;170;116;235mo\u001b[39m\u001b[38;2;235;116;235mi\u001b[39m\u001b[38;2;235;116;171mr\u001b[39m \u001b[38;2;236;126;116ms\u001b[39m\u001b[38;2;236;191;116mt\u001b[39m\u001b[38;2;215;236;116ma\u001b[39m\u001b[38;2;150;236;116mt\u001b[39m\u001b[38;2;116;236;148mu\u001b[39m\u001b[38;2;116;236;213ms\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[32m✔ Connected\u001b[39m\u001b[90m → \u001b[39m\u001b[36m/var/folders/hz/21xm5f\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[36mzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0E\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[36mpmHHv/memoir-backup\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b"]
|
|
59
|
-
[0.000, "o", "[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[1m\u001b[37mAI Tools\u001b[39m\u001b[22m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[32m✔ \u001b[39m\u001b[37mGemini CLI\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[32m✔ \u001b[39m\u001b[37mClaude CLI\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[32m✔ \u001b[39m\u001b[37mOpenAI Codex\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[90m○ Cursor\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[90m○ GitHub Copilot\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[90m○ Windsurf\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[32m✔ \u001b[39m\u001b[37mAider\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b["]
|
|
60
|
-
[0.000, "o", "2m\u001b[36m│\u001b[39m\u001b[22m \u001b[90m──────────────────────────────\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[37m4 tools ready to sync\u001b[39m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m│\u001b[39m\u001b[22m \u001b[2m\u001b[36m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[36m╰──────────────────────────────────────────╯\u001b[39m\u001b[22m\r\n\r\n"]
|
|
61
|
-
[1.510, "o", "\r\n\u001b[1;33m# Back up all AI memory in one command\u001b[0m\r\n"]
|
|
62
|
-
[1.010, "o", "\r\n\u001b[1;32m❯\u001b[0m n"]
|
|
63
|
-
[0.050, "o", "o"]
|
|
64
|
-
[0.049, "o", "d"]
|
|
65
|
-
[0.050, "o", "e"]
|
|
66
|
-
[0.049, "o", " "]
|
|
67
|
-
[0.047, "o", "/"]
|
|
68
|
-
[0.050, "o", "U"]
|
|
69
|
-
[0.049, "o", "s"]
|
|
70
|
-
[0.047, "o", "e"]
|
|
71
|
-
[0.049, "o", "r"]
|
|
72
|
-
[0.046, "o", "s"]
|
|
73
|
-
[0.050, "o", "/"]
|
|
74
|
-
[0.049, "o", "c"]
|
|
75
|
-
[0.048, "o", "a"]
|
|
76
|
-
[0.048, "o", "m"]
|
|
77
|
-
[0.048, "o", "a"]
|
|
78
|
-
[0.050, "o", "r"]
|
|
79
|
-
[0.046, "o", "t"]
|
|
80
|
-
[0.050, "o", "h"]
|
|
81
|
-
[0.047, "o", "u"]
|
|
82
|
-
[0.045, "o", "r"]
|
|
83
|
-
[0.046, "o", "/"]
|
|
84
|
-
[0.051, "o", "m"]
|
|
85
|
-
[0.049, "o", "e"]
|
|
86
|
-
[0.048, "o", "m"]
|
|
87
|
-
[0.048, "o", "o"]
|
|
88
|
-
[0.047, "o", "i"]
|
|
89
|
-
[0.047, "o", "r"]
|
|
90
|
-
[0.047, "o", "/"]
|
|
91
|
-
[0.048, "o", "b"]
|
|
92
|
-
[0.049, "o", "i"]
|
|
93
|
-
[0.049, "o", "n"]
|
|
94
|
-
[0.047, "o", "/"]
|
|
95
|
-
[0.050, "o", "m"]
|
|
96
|
-
[0.049, "o", "e"]
|
|
97
|
-
[0.049, "o", "m"]
|
|
98
|
-
[0.050, "o", "o"]
|
|
99
|
-
[0.046, "o", "i"]
|
|
100
|
-
[0.047, "o", "r"]
|
|
101
|
-
[0.049, "o", "."]
|
|
102
|
-
[0.047, "o", "j"]
|
|
103
|
-
[0.050, "o", "s"]
|
|
104
|
-
[0.047, "o", " "]
|
|
105
|
-
[0.049, "o", "p"]
|
|
106
|
-
[0.049, "o", "u"]
|
|
107
|
-
[0.047, "o", "s"]
|
|
108
|
-
[0.049, "o", "h"]
|
|
109
|
-
[0.050, "o", "\r\n"]
|
|
110
|
-
[0.441, "o", "\r\n"]
|
|
111
|
-
[0.007, "o", "\u001b[?25l"]
|
|
112
|
-
[0.000, "o", "\u001b[1G"]
|
|
113
|
-
[0.000, "o", "\u001b[1G"]
|
|
114
|
-
[0.000, "o", "\u001b[36m⠋\u001b[39m \u001b[90mScanning for AI tools...\u001b[39m"]
|
|
115
|
-
[0.008, "o", "\u001b[1G"]
|
|
116
|
-
[0.000, "o", "\u001b[0K"]
|
|
117
|
-
[0.000, "o", "\u001b[?25h"]
|
|
118
|
-
[0.000, "o", "\r\n"]
|
|
119
|
-
[0.000, "o", "\u001b[37m\u001b[1m Detected AI tools:\u001b[22m\u001b[39m\r\n\u001b[37m\u001b[1m\u001b[22m\u001b[39m\r\n ├─ 🔵 \u001b[36mGemini CLI\u001b[39m\u001b[90m 3 files, 237B\u001b[39m\r\n"]
|
|
120
|
-
[0.000, "o", " ├─ 🟣 \u001b[36mClaude CLI\u001b[39m\u001b[90m 3 files, 334B\u001b[39m\r\n ├─ 🟢 \u001b[36mOpenAI Codex\u001b[39m\u001b[90m 2 files, 90B\u001b[39m\r\n └─ 🔧 \u001b[36mAider\u001b[39m\u001b[90m 1 files, 49B\u001b[39m\r\n\r\n"]
|
|
121
|
-
[0.000, "o", "\u001b[?25l"]
|
|
122
|
-
[0.000, "o", "\u001b[1G\u001b[36m⠋\u001b[39m \u001b[90mUploading...\u001b[39m"]
|
|
123
|
-
[0.002, "o", "\u001b[1G\u001b[0K"]
|
|
124
|
-
[0.000, "o", "\u001b[?25h"]
|
|
125
|
-
[0.000, "o", "\u001b[32m✔\u001b[39m \u001b[32mSync complete! \u001b[39m\u001b[90m(Saved to /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/memoir-backup)\u001b[39m\r\n\u001b[1G"]
|
|
126
|
-
[0.000, "o", "\u001b[?25h"]
|
|
127
|
-
[0.002, "o", "\r\n\u001b[2m\u001b[32m╭──────────────────────────────────────────╮\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[38;2;116;235;213mB\u001b[39m\u001b[38;2;116;168;235ma\u001b[39m\u001b[38;2;153;116;235mc\u001b[39m\u001b[38;2;235;116;228mk\u001b[39m\u001b[38;2;235;116;138me\u001b[39m\u001b[38;2;236;183;116md\u001b[39m \u001b[38;2;199;236;116mu\u001b[39m\u001b[38;2;116;236;123mp\u001b[39m\u001b[38;2;116;236;213m!\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[36m✔ Gemini CLI\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[36m✔ Claude CLI\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[36m✔ OpenAI Codex\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[36m✔ Aider\u001b[39m \u001b[2m\u001b[32m│\u001b"]
|
|
128
|
-
[0.000, "o", "[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[37m9 files from 4 tools\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[90m→ /var/folders/hz/21xm5fzj4b93bbv25b\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[90m7jnjyr0000gn/T/tmp.GvJ0EpmHHv/memoir\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[90m-backup\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[90mRestore on another machine with:\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[90m\u001b[39m\u001b[36mmemoir restore\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m╰──────────────────────────────────────────╯\u001b[39m\u001b[2"]
|
|
129
|
-
[0.000, "o", "2m\r\n\r\n"]
|
|
130
|
-
[0.002, "o", "\u001b[?25h"]
|
|
131
|
-
[1.509, "o", "\r\n\u001b[1;33m# Now simulate switching to a new machine...\u001b[0m\r\n"]
|
|
132
|
-
[2.034, "o", "\u001b[1;31m [wiped all AI configs]\u001b[0m\r\n"]
|
|
133
|
-
[1.509, "o", "\r\n\u001b[1;33m# Restore everything on the new machine\u001b[0m\r\n"]
|
|
134
|
-
[1.010, "o", "\r\n\u001b[1;32m❯\u001b[0m "]
|
|
135
|
-
[0.000, "o", "n"]
|
|
136
|
-
[0.047, "o", "o"]
|
|
137
|
-
[0.047, "o", "d"]
|
|
138
|
-
[0.048, "o", "e"]
|
|
139
|
-
[0.049, "o", " "]
|
|
140
|
-
[0.050, "o", "/"]
|
|
141
|
-
[0.047, "o", "U"]
|
|
142
|
-
[0.050, "o", "s"]
|
|
143
|
-
[0.049, "o", "e"]
|
|
144
|
-
[0.046, "o", "r"]
|
|
145
|
-
[0.049, "o", "s"]
|
|
146
|
-
[0.050, "o", "/"]
|
|
147
|
-
[0.045, "o", "c"]
|
|
148
|
-
[0.050, "o", "a"]
|
|
149
|
-
[0.049, "o", "m"]
|
|
150
|
-
[0.048, "o", "a"]
|
|
151
|
-
[0.047, "o", "r"]
|
|
152
|
-
[0.047, "o", "t"]
|
|
153
|
-
[0.047, "o", "h"]
|
|
154
|
-
[0.047, "o", "u"]
|
|
155
|
-
[0.048, "o", "r"]
|
|
156
|
-
[0.046, "o", "/"]
|
|
157
|
-
[0.050, "o", "m"]
|
|
158
|
-
[0.049, "o", "e"]
|
|
159
|
-
[0.049, "o", "m"]
|
|
160
|
-
[0.047, "o", "o"]
|
|
161
|
-
[0.049, "o", "i"]
|
|
162
|
-
[0.045, "o", "r"]
|
|
163
|
-
[0.049, "o", "/"]
|
|
164
|
-
[0.046, "o", "b"]
|
|
165
|
-
[0.049, "o", "i"]
|
|
166
|
-
[0.050, "o", "n"]
|
|
167
|
-
[0.049, "o", "/"]
|
|
168
|
-
[0.050, "o", "m"]
|
|
169
|
-
[0.050, "o", "e"]
|
|
170
|
-
[0.049, "o", "m"]
|
|
171
|
-
[0.049, "o", "o"]
|
|
172
|
-
[0.049, "o", "i"]
|
|
173
|
-
[0.049, "o", "r"]
|
|
174
|
-
[0.050, "o", "."]
|
|
175
|
-
[0.048, "o", "j"]
|
|
176
|
-
[0.049, "o", "s"]
|
|
177
|
-
[0.049, "o", " "]
|
|
178
|
-
[0.051, "o", "r"]
|
|
179
|
-
[0.046, "o", "e"]
|
|
180
|
-
[0.050, "o", "s"]
|
|
181
|
-
[0.048, "o", "t"]
|
|
182
|
-
[0.050, "o", "o"]
|
|
183
|
-
[0.049, "o", "r"]
|
|
184
|
-
[0.050, "o", "e"]
|
|
185
|
-
[0.049, "o", " "]
|
|
186
|
-
[0.049, "o", "-"]
|
|
187
|
-
[0.049, "o", "-"]
|
|
188
|
-
[0.049, "o", "y"]
|
|
189
|
-
[0.048, "o", "e"]
|
|
190
|
-
[0.049, "o", "s"]
|
|
191
|
-
[0.044, "o", "\r\n"]
|
|
192
|
-
[0.446, "o", "\r\n"]
|
|
193
|
-
[0.007, "o", "\u001b[?25l"]
|
|
194
|
-
[0.000, "o", "\u001b[1G"]
|
|
195
|
-
[0.000, "o", "\u001b[1G"]
|
|
196
|
-
[0.001, "o", "\u001b[36m⠋\u001b[39m \u001b[90mFetching memories from local storage...\u001b[39m"]
|
|
197
|
-
[0.005, "o", "\u001b[1G"]
|
|
198
|
-
[0.000, "o", "\u001b[0K"]
|
|
199
|
-
[0.000, "o", "\u001b[?25h"]
|
|
200
|
-
[0.000, "o", "\r\n\u001b[36m🔵 Found backup for \u001b[1mGemini CLI\u001b[22m\u001b[39m\r\n"]
|
|
201
|
-
[0.000, "o", "\u001b[90m Will restore to: /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.gemini\u001b[39m\r\n"]
|
|
202
|
-
[0.000, "o", "\u001b[32m Auto-restoring Gemini CLI...\u001b[39m\r\n\u001b[?25l"]
|
|
203
|
-
[0.000, "o", "\u001b[1G"]
|
|
204
|
-
[0.000, "o", "\u001b[36m⠋\u001b[39m Fetching data from local directory: \u001b[36m/var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/memoir-backup\u001b[39m"]
|
|
205
|
-
[0.001, "o", "\u001b[1G\u001b[0K"]
|
|
206
|
-
[0.000, "o", "\u001b[1A\u001b[0K\u001b[1A\u001b[0K\u001b[?25h"]
|
|
207
|
-
[0.000, "o", "\u001b[32m\u001b[1m\u001b[22m\u001b[39m\r\n\u001b[32m\u001b[1m 🔵 Gemini CLI — 3 file(s) restored to \u001b[4m/var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.gemini\u001b[24m\u001b[22m\u001b[39m\r\n"]
|
|
208
|
-
[0.000, "o", "\u001b[32m + GEMINI.md\u001b[39m\u001b[90m (new)\u001b[39m\r\n"]
|
|
209
|
-
[0.000, "o", "\u001b[32m + projects.json\u001b[39m\u001b[90m (new)\u001b[39m\r\n"]
|
|
210
|
-
[0.000, "o", "\u001b[32m + settings.json\u001b[39m\u001b[90m (new)\u001b[39m\r\n\u001b[?25l"]
|
|
211
|
-
[0.000, "o", "\u001b[1G\u001b[36m⠋\u001b[39m Restoring \u001b[36mGemini CLI\u001b[39m to /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.gemini..."]
|
|
212
|
-
[0.000, "o", "\u001b[1G\u001b[0K\u001b[1A\u001b[0K\u001b[1A"]
|
|
213
|
-
[0.000, "o", "\u001b[0K\u001b[?25h"]
|
|
214
|
-
[0.000, "o", "\r\n\u001b[36m🟣 Found backup for \u001b[1mClaude CLI\u001b[22m\u001b[39m\r\n\u001b[90m Will restore to: /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.claude\u001b[39m\r\n"]
|
|
215
|
-
[0.000, "o", "\u001b[32m Auto-restoring Claude CLI...\u001b[39m\r\n"]
|
|
216
|
-
[0.000, "o", "\u001b[?25l"]
|
|
217
|
-
[0.000, "o", "\u001b[1G\u001b[36m⠋\u001b[39m Restoring \u001b[36mGemini CLI\u001b[39m to /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.gemini..."]
|
|
218
|
-
[0.001, "o", "\u001b[1G\u001b[0K\u001b[1A\u001b[0K\u001b[1A"]
|
|
219
|
-
[0.000, "o", "\u001b[0K"]
|
|
220
|
-
[0.000, "o", "\u001b[?25h"]
|
|
221
|
-
[0.000, "o", "\u001b[90m Remapping project path: -Users-dev → -var-folders-hz-21xm5fzj4b93bbv25b7jnjyr0000gn-T-tmp.GvJ0EpmHHv\u001b[39m\r\n\u001b[?25l"]
|
|
222
|
-
[0.000, "o", "\u001b[1G\u001b[36m⠋\u001b[39m Restoring \u001b[36mGemini CLI\u001b[39m to /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.gemini..."]
|
|
223
|
-
[0.001, "o", "\u001b[1G\u001b[0K\u001b[1A"]
|
|
224
|
-
[0.000, "o", "\u001b[0K\u001b[1A\u001b[0K"]
|
|
225
|
-
[0.000, "o", "\u001b[?25h"]
|
|
226
|
-
[0.001, "o", "\u001b[32m\u001b[1m\u001b[22m\u001b[39m\r\n\u001b[32m\u001b[1m 🟣 Claude CLI — 3 file(s) restored to \u001b[4m/var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.claude\u001b[24m\u001b[22m\u001b[39m\r\n\u001b[32m + projects/-var-folders-hz-21xm5fzj4b93bbv25b7jnjyr0000gn-T-tmp.GvJ0EpmHHv/webapp/CLAUDE.md\u001b[39m\u001b[90m (new)\u001b[39m\r\n\u001b[32m + projects/-var-folders-hz-21xm5fzj4b93bbv25b7jnjyr0000gn-T-tmp.GvJ0EpmHHv/webapp/memory/MEMORY.md\u001b[39m\u001b[90m (new)\u001b[39m\r\n\u001b[32m + settings.json\u001b[39m\u001b[90m (new)\u001b[39m\r\n\u001b[?25l\u001b[1G\u001b[36m⠋\u001b[39m Restoring \u001b[36mClaude CLI\u001b[39m to /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.claude..."]
|
|
227
|
-
[0.000, "o", "\u001b[1G\u001b[0K\u001b[1A\u001b[0K\u001b[1A\u001b[0K\u001b[?25h"]
|
|
228
|
-
[0.000, "o", "\r\n\u001b[36m🟢 Found backup for \u001b[1mOpenAI Codex\u001b[22m\u001b[39m\r\n\u001b[90m Will restore to: /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.codex\u001b[39m\r\n"]
|
|
229
|
-
[0.000, "o", "\u001b[32m Auto-restoring OpenAI Codex...\u001b[39m\r\n"]
|
|
230
|
-
[0.000, "o", "\u001b[?25l"]
|
|
231
|
-
[0.000, "o", "\u001b[1G\u001b[36m⠋\u001b[39m Restoring \u001b[36mClaude CLI\u001b[39m to /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.claude..."]
|
|
232
|
-
[0.000, "o", "\u001b[1G\u001b[0K\u001b[1A\u001b[0K\u001b[1A\u001b[0K\u001b[?25h"]
|
|
233
|
-
[0.000, "o", "\u001b[32m\u001b[1m\u001b[22m\u001b[39m\r\n\u001b[32m\u001b[1m 🟢 OpenAI Codex — 2 file(s) restored to \u001b[4m/var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.codex\u001b[24m\u001b[22m\u001b[39m\r\n\u001b[32m + config.json\u001b[39m\u001b[90m (new)\u001b[39m\r\n"]
|
|
234
|
-
[0.000, "o", "\u001b[32m + instructions.md\u001b[39m\u001b[90m (new)\u001b[39m\r\n\u001b[?25l"]
|
|
235
|
-
[0.001, "o", "\u001b[1G\u001b[36m⠋\u001b[39m Restoring \u001b[36mOpenAI Codex\u001b[39m to /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.codex..."]
|
|
236
|
-
[0.000, "o", "\u001b[1G\u001b[0K"]
|
|
237
|
-
[0.000, "o", "\u001b[1A\u001b[0K\u001b[1A"]
|
|
238
|
-
[0.000, "o", "\u001b[0K"]
|
|
239
|
-
[0.000, "o", "\u001b[?25h"]
|
|
240
|
-
[0.000, "o", "\r\n\u001b[36m🔧 Found backup for \u001b[1mAider\u001b[22m\u001b[39m\r\n\u001b[90m Will restore to: /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv\u001b[39m\r\n"]
|
|
241
|
-
[0.000, "o", "\u001b[32m Auto-restoring Aider...\u001b[39m\r\n"]
|
|
242
|
-
[0.000, "o", "\u001b[?25l"]
|
|
243
|
-
[0.000, "o", "\u001b[1G\u001b[36m⠋\u001b[39m Restoring \u001b[36mOpenAI Codex\u001b[39m to /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.codex..."]
|
|
244
|
-
[0.000, "o", "\u001b[1G\u001b[0K\u001b[1A\u001b[0K\u001b[1A"]
|
|
245
|
-
[0.000, "o", "\u001b[0K\u001b[?25h"]
|
|
246
|
-
[0.000, "o", "\u001b[32m\u001b[1m\u001b[22m\u001b[39m\r\n\u001b[32m\u001b[1m 🔧 Aider — 1 file(s) restored to \u001b[4m/var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv\u001b[24m\u001b[22m\u001b[39m\r\n"]
|
|
247
|
-
[0.000, "o", "\u001b[32m + .aider.conf.yml\u001b[39m\u001b[90m (new)\u001b[39m\r\n"]
|
|
248
|
-
[0.000, "o", "\u001b[?25l"]
|
|
249
|
-
[0.000, "o", "\u001b[1G"]
|
|
250
|
-
[0.000, "o", "\u001b[36m⠋\u001b[39m Restoring \u001b[36mOpenAI Codex\u001b[39m to /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.codex..."]
|
|
251
|
-
[0.000, "o", "\u001b[1G\u001b[0K\u001b[1A"]
|
|
252
|
-
[0.000, "o", "\u001b[0K\u001b[1A\u001b[0K"]
|
|
253
|
-
[0.000, "o", "\u001b[?25h"]
|
|
254
|
-
[0.000, "o", "\r\n\u001b[90m────────────────────────────────────────\u001b[39m\r\n"]
|
|
255
|
-
[0.000, "o", "\u001b[1m\u001b[37m\u001b[39m\u001b[22m\r\n\u001b[1m\u001b[37m Restore Summary:\u001b[39m\u001b[22m\r\n\u001b[1m\u001b[37m\u001b[39m\u001b[22m\r\n"]
|
|
256
|
-
[0.000, "o", " 🔵 \u001b[37mGemini CLI\u001b[39m\r\n\u001b[90m 3 file(s) → /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.gemini\u001b[39m\r\n"]
|
|
257
|
-
[0.000, "o", " 🟣 \u001b[37mClaude CLI\u001b[39m\r\n"]
|
|
258
|
-
[0.000, "o", "\u001b[90m 3 file(s) → /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.claude\u001b[39m\r\n"]
|
|
259
|
-
[0.000, "o", " 🟢 \u001b[37mOpenAI Codex\u001b[39m\r\n"]
|
|
260
|
-
[0.000, "o", "\u001b[90m 2 file(s) → /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv/.codex\u001b[39m\r\n 🔧 \u001b[37mAider\u001b[39m\r\n\u001b[90m 1 file(s) → /var/folders/hz/21xm5fzj4b93bbv25b7jnjyr0000gn/T/tmp.GvJ0EpmHHv\u001b[39m\r\n"]
|
|
261
|
-
[0.000, "o", "\r\n\u001b[1G"]
|
|
262
|
-
[0.000, "o", "\u001b[?25h"]
|
|
263
|
-
[0.001, "o", "\u001b[2m\u001b[32m╭──────────────────────────────────────────╮\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[38;2;116;235;213mD\u001b[39m\u001b[38;2;153;116;235mo\u001b[39m\u001b[38;2;235;116;138mn\u001b[39m\u001b[38;2;199;236;116me\u001b[39m\u001b[38;2;116;236;213m!\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[37mYour AI tools have their memories\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[37mback.\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[90mRestart your AI tools to pick up the\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[90mchanges.\u001b[39m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[32m│\u001b[39m\u001b[22m \u001b[2m\u001b[32m│\u001b[39m\u001b[22m\r\n\u001b[2m\u001b[3"]
|
|
264
|
-
[0.000, "o", "2m╰──────────────────────────────────────────╯\u001b[39m\u001b[22m\r\n\r\n"]
|
|
265
|
-
[0.001, "o", "\u001b[?25h"]
|
|
266
|
-
[1.508, "o", "\r\n\u001b[1;33m# All done. 11 files restored across 4 tools in seconds.\u001b[0m\r\n"]
|
|
267
|
-
[2.015, "o", "\r\n\u001b[1;32m npm install -g memoir-cli\u001b[0m\r\n"]
|
|
268
|
-
[0.000, "o", "\u001b[0;90m Works with Claude, Gemini, Codex, Cursor, Copilot, Windsurf, and Aider.\u001b[0m\r\n\r\n"]
|
|
269
|
-
[3.025, "x", "0"]
|
package/demo.gif
DELETED
|
Binary file
|
package/demo.sh
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Scripted demo for asciinema recording
|
|
3
|
-
# Usage: asciinema rec demo.cast -c "bash demo.sh"
|
|
4
|
-
|
|
5
|
-
# Simulate typing with a delay
|
|
6
|
-
type_cmd() {
|
|
7
|
-
local cmd="$1"
|
|
8
|
-
echo ""
|
|
9
|
-
echo -ne "\033[1;32m❯\033[0m "
|
|
10
|
-
for ((i=0; i<${#cmd}; i++)); do
|
|
11
|
-
echo -n "${cmd:$i:1}"
|
|
12
|
-
sleep 0.04
|
|
13
|
-
done
|
|
14
|
-
echo ""
|
|
15
|
-
sleep 0.3
|
|
16
|
-
eval "$cmd"
|
|
17
|
-
sleep 1.5
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
narrate() {
|
|
21
|
-
echo ""
|
|
22
|
-
echo -e "\033[1;33m# $1\033[0m"
|
|
23
|
-
sleep 1
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export FAKE_HOME=$(mktemp -d)
|
|
27
|
-
export BACKUP_DIR="$FAKE_HOME/memoir-backup"
|
|
28
|
-
MEMOIR_BIN="$(cd "$(dirname "$0")" && pwd)/bin/memoir.js"
|
|
29
|
-
|
|
30
|
-
# Pre-setup: create mock configs and memoir config silently
|
|
31
|
-
mkdir -p "$FAKE_HOME/.gemini"
|
|
32
|
-
echo '{ "theme": "dark", "model": "gemini-2.5-pro" }' > "$FAKE_HOME/.gemini/settings.json"
|
|
33
|
-
cat > "$FAKE_HOME/.gemini/GEMINI.md" << 'EOF'
|
|
34
|
-
# My Gemini Instructions
|
|
35
|
-
Always use TypeScript. Prefer functional patterns. Keep responses concise.
|
|
36
|
-
Never use `any` type. Always handle errors explicitly.
|
|
37
|
-
EOF
|
|
38
|
-
echo '{ "recent": ["/home/dev/webapp"] }' > "$FAKE_HOME/.gemini/projects.json"
|
|
39
|
-
|
|
40
|
-
mkdir -p "$FAKE_HOME/.claude/projects/-Users-dev/webapp/memory"
|
|
41
|
-
echo '{ "permissions": { "allow": ["Read", "Write", "Bash"] } }' > "$FAKE_HOME/.claude/settings.json"
|
|
42
|
-
cat > "$FAKE_HOME/.claude/projects/-Users-dev/webapp/CLAUDE.md" << 'EOF'
|
|
43
|
-
# Project: webapp
|
|
44
|
-
Next.js 15 app with Supabase backend. Always use server components.
|
|
45
|
-
Database schema is in /supabase/migrations. Run tests before committing.
|
|
46
|
-
EOF
|
|
47
|
-
cat > "$FAKE_HOME/.claude/projects/-Users-dev/webapp/memory/MEMORY.md" << 'EOF'
|
|
48
|
-
# Memory
|
|
49
|
-
- User prefers Tailwind CSS v4
|
|
50
|
-
- Auth is handled by Supabase Auth with Google OAuth
|
|
51
|
-
- Deploy target: Vercel
|
|
52
|
-
EOF
|
|
53
|
-
|
|
54
|
-
mkdir -p "$FAKE_HOME/.codex"
|
|
55
|
-
echo '{ "model": "codex-1", "approval": "auto" }' > "$FAKE_HOME/.codex/config.json"
|
|
56
|
-
echo 'Use Python 3.12. Follow PEP 8. Prefer pathlib.' > "$FAKE_HOME/.codex/instructions.md"
|
|
57
|
-
|
|
58
|
-
cat > "$FAKE_HOME/.aider.conf.yml" << 'EOF'
|
|
59
|
-
model: claude-opus-4-20250514
|
|
60
|
-
auto-commits: true
|
|
61
|
-
EOF
|
|
62
|
-
|
|
63
|
-
mkdir -p "$FAKE_HOME/.config/memoir"
|
|
64
|
-
echo "{ \"provider\": \"local\", \"localPath\": \"$BACKUP_DIR\" }" > "$FAKE_HOME/.config/memoir/config.json"
|
|
65
|
-
mkdir -p "$BACKUP_DIR"
|
|
66
|
-
|
|
67
|
-
export HOME="$FAKE_HOME"
|
|
68
|
-
|
|
69
|
-
clear
|
|
70
|
-
echo ""
|
|
71
|
-
echo -e "\033[1;36m memoir — Your AI remembers everything. Sync it everywhere.\033[0m"
|
|
72
|
-
echo -e "\033[0;90m https://github.com/camgitt/memoir\033[0m"
|
|
73
|
-
sleep 2
|
|
74
|
-
|
|
75
|
-
narrate "See what AI tools are on this machine"
|
|
76
|
-
type_cmd "node $MEMOIR_BIN status"
|
|
77
|
-
|
|
78
|
-
narrate "Back up all AI memory in one command"
|
|
79
|
-
type_cmd "node $MEMOIR_BIN push"
|
|
80
|
-
|
|
81
|
-
narrate "Now simulate switching to a new machine..."
|
|
82
|
-
sleep 1
|
|
83
|
-
rm -rf "$FAKE_HOME/.gemini" "$FAKE_HOME/.claude" "$FAKE_HOME/.codex"
|
|
84
|
-
rm -f "$FAKE_HOME/.aider.conf.yml"
|
|
85
|
-
echo -e "\033[1;31m [wiped all AI configs]\033[0m"
|
|
86
|
-
sleep 1.5
|
|
87
|
-
|
|
88
|
-
narrate "Restore everything on the new machine"
|
|
89
|
-
type_cmd "node $MEMOIR_BIN restore --yes"
|
|
90
|
-
|
|
91
|
-
narrate "All done. 11 files restored across 4 tools in seconds."
|
|
92
|
-
sleep 1
|
|
93
|
-
echo ""
|
|
94
|
-
echo -e "\033[1;32m npm install -g memoir-cli\033[0m"
|
|
95
|
-
echo -e "\033[0;90m Works with Claude, Gemini, Codex, Cursor, Copilot, Windsurf, and Aider.\033[0m"
|
|
96
|
-
echo ""
|
|
97
|
-
sleep 3
|
|
98
|
-
|
|
99
|
-
rm -rf "$FAKE_HOME"
|