memoir-cli 1.5.2 → 2.0.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/README.md +2 -0
- package/SHOW_HN.md +23 -0
- package/bin/memoir.js +52 -4
- package/demo.cast +269 -0
- package/demo.gif +0 -0
- package/demo.sh +99 -0
- package/package.json +7 -4
- package/src/adapters/restore.js +25 -14
- package/src/commands/diff.js +221 -0
- package/src/commands/restore.js +4 -2
- package/src/commands/resume.js +166 -0
- package/src/commands/snapshot.js +382 -0
- package/src/providers/restore.js +4 -4
- package/CLAUDE.md +0 -1
- package/TODO.md +0 -39
package/README.md
CHANGED
package/SHOW_HN.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
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/bin/memoir.js
CHANGED
|
@@ -8,7 +8,10 @@ import { pushCommand } from '../src/commands/push.js';
|
|
|
8
8
|
import { restoreCommand } from '../src/commands/restore.js';
|
|
9
9
|
import { statusCommand } from '../src/commands/status.js';
|
|
10
10
|
import { viewCommand } from '../src/commands/view.js';
|
|
11
|
+
import { diffCommand } from '../src/commands/diff.js';
|
|
11
12
|
import { migrateCommand } from '../src/commands/migrate.js';
|
|
13
|
+
import { snapshotCommand } from '../src/commands/snapshot.js';
|
|
14
|
+
import { resumeCommand } from '../src/commands/resume.js';
|
|
12
15
|
import { createRequire } from 'module';
|
|
13
16
|
|
|
14
17
|
const require = createRequire(import.meta.url);
|
|
@@ -20,10 +23,12 @@ if (process.argv.length <= 2) {
|
|
|
20
23
|
gradient.pastel.multiline(' memoir ') + '\n' +
|
|
21
24
|
chalk.gray(' Your AI remembers everything.') + '\n\n' +
|
|
22
25
|
chalk.white.bold('Quick Start:') + '\n' +
|
|
23
|
-
chalk.cyan(' memoir init
|
|
24
|
-
chalk.cyan(' memoir push
|
|
25
|
-
chalk.cyan(' memoir restore
|
|
26
|
-
chalk.cyan(' memoir
|
|
26
|
+
chalk.cyan(' memoir init ') + chalk.gray('— first-time setup') + '\n' +
|
|
27
|
+
chalk.cyan(' memoir push ') + chalk.gray('— back up your AI memory') + '\n' +
|
|
28
|
+
chalk.cyan(' memoir restore ') + chalk.gray('— restore on a new machine') + '\n' +
|
|
29
|
+
chalk.cyan(' memoir snapshot ') + chalk.gray('— capture your current session') + '\n' +
|
|
30
|
+
chalk.cyan(' memoir resume ') + chalk.gray('— pick up where you left off') + '\n' +
|
|
31
|
+
chalk.cyan(' memoir status ') + chalk.gray('— see detected AI tools') + '\n\n' +
|
|
27
32
|
chalk.gray(' Tip: use --only claude,gemini to sync specific tools') + '\n\n' +
|
|
28
33
|
chalk.gray(`v${VERSION}`),
|
|
29
34
|
{ padding: 1, borderStyle: 'round', borderColor: 'cyan', dimBorder: true }
|
|
@@ -74,6 +79,7 @@ program
|
|
|
74
79
|
.alias('pull')
|
|
75
80
|
.description('Restore your AI memory on this machine')
|
|
76
81
|
.option('--only <tools>', 'Only restore specific tools (comma-separated: claude,gemini,codex,cursor,copilot,windsurf,aider)')
|
|
82
|
+
.option('-y, --yes', 'Skip confirmation prompts (restore all)')
|
|
77
83
|
.action(async (options) => {
|
|
78
84
|
try {
|
|
79
85
|
await restoreCommand(options);
|
|
@@ -108,6 +114,48 @@ program
|
|
|
108
114
|
}
|
|
109
115
|
});
|
|
110
116
|
|
|
117
|
+
program
|
|
118
|
+
.command('diff')
|
|
119
|
+
.alias('changes')
|
|
120
|
+
.description('Show what changed since your last backup')
|
|
121
|
+
.action(async () => {
|
|
122
|
+
try {
|
|
123
|
+
await diffCommand();
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.error(chalk.red('\n✖ Error:'), err.message);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
program
|
|
131
|
+
.command('snapshot')
|
|
132
|
+
.alias('handoff')
|
|
133
|
+
.description('Capture your current coding session for handoff')
|
|
134
|
+
.option('--smart', 'Use AI to generate a better summary (requires Gemini API key)')
|
|
135
|
+
.option('--goal <goal>', 'What you want to do next (goal-directed handoff)')
|
|
136
|
+
.action(async (options) => {
|
|
137
|
+
try {
|
|
138
|
+
await snapshotCommand(options);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.error(chalk.red('\n✖ Error during snapshot:'), err.message);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
program
|
|
146
|
+
.command('resume')
|
|
147
|
+
.description('Pick up where you left off on another machine')
|
|
148
|
+
.option('--inject', 'Write the handoff where your AI tool will read it')
|
|
149
|
+
.option('--to <tool>', 'Target tool for injection (claude, gemini, cursor, codex)')
|
|
150
|
+
.action(async (options) => {
|
|
151
|
+
try {
|
|
152
|
+
await resumeCommand(options);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
console.error(chalk.red('\n✖ Error during resume:'), err.message);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
111
159
|
program
|
|
112
160
|
.command('migrate')
|
|
113
161
|
.description('Translate memory between AI tools (Claude, Gemini, Codex, Cursor, etc.)')
|
package/demo.cast
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
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
ADDED
|
Binary file
|
package/demo.sh
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
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"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memoir-cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Sync
|
|
3
|
+
"version": "2.0.1",
|
|
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",
|
|
7
7
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"start": "node bin/memoir.js",
|
|
20
|
-
"test": "
|
|
20
|
+
"test": "bash test-local.sh"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"ai",
|
|
@@ -44,7 +44,10 @@
|
|
|
44
44
|
"openai",
|
|
45
45
|
"ai-assistant",
|
|
46
46
|
"coding-assistant",
|
|
47
|
-
"context-sync"
|
|
47
|
+
"context-sync",
|
|
48
|
+
"session-handoff",
|
|
49
|
+
"snapshot",
|
|
50
|
+
"resume"
|
|
48
51
|
],
|
|
49
52
|
"author": "camgitt",
|
|
50
53
|
"license": "MIT",
|
package/src/adapters/restore.js
CHANGED
|
@@ -56,7 +56,7 @@ async function syncFiles(src, dest, changes) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export async function restoreMemories(sourceDir, spinner, onlyFilter = null) {
|
|
59
|
+
export async function restoreMemories(sourceDir, spinner, onlyFilter = null, autoYes = false) {
|
|
60
60
|
let restoredAny = false;
|
|
61
61
|
const allResults = [];
|
|
62
62
|
|
|
@@ -74,14 +74,21 @@ export async function restoreMemories(sourceDir, spinner, onlyFilter = null) {
|
|
|
74
74
|
|
|
75
75
|
console.log('\n' + chalk.cyan(`${adapter.icon} Found backup for ${chalk.bold(adapter.name)}`));
|
|
76
76
|
console.log(chalk.gray(` Will restore to: ${adapter.source}`));
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
|
|
78
|
+
let confirm = true;
|
|
79
|
+
if (!autoYes) {
|
|
80
|
+
const answer = await inquirer.prompt([
|
|
81
|
+
{
|
|
82
|
+
type: 'confirm',
|
|
83
|
+
name: 'confirm',
|
|
84
|
+
message: `Restore ${adapter.name}?`,
|
|
85
|
+
default: true
|
|
86
|
+
}
|
|
87
|
+
]);
|
|
88
|
+
confirm = answer.confirm;
|
|
89
|
+
} else {
|
|
90
|
+
console.log(chalk.green(` Auto-restoring ${adapter.name}...`));
|
|
91
|
+
}
|
|
85
92
|
|
|
86
93
|
spinner.start();
|
|
87
94
|
|
|
@@ -131,20 +138,24 @@ export async function restoreMemories(sourceDir, spinner, onlyFilter = null) {
|
|
|
131
138
|
// Show summary of changes
|
|
132
139
|
spinner.stop();
|
|
133
140
|
const totalChanged = changes.added.length + changes.updated.length;
|
|
141
|
+
const relPath = (f) => path.relative(adapter.source, f);
|
|
134
142
|
if (totalChanged > 0) {
|
|
135
143
|
console.log(chalk.green.bold(`\n ${adapter.icon} ${adapter.name} — ${totalChanged} file(s) restored to ${chalk.underline(adapter.source)}`));
|
|
136
144
|
for (const f of changes.added) {
|
|
137
|
-
console.log(chalk.green(` + ${
|
|
145
|
+
console.log(chalk.green(` + ${relPath(f)}`) + chalk.gray(` (new)`));
|
|
138
146
|
}
|
|
139
147
|
for (const f of changes.updated) {
|
|
140
|
-
console.log(chalk.yellow(` ↻ ${
|
|
148
|
+
console.log(chalk.yellow(` ↻ ${relPath(f)}`) + chalk.gray(` (updated)`));
|
|
141
149
|
}
|
|
142
150
|
}
|
|
143
151
|
if (changes.skipped.length > 0) {
|
|
144
|
-
console.log(chalk.gray(` ⏭ ${changes.skipped.length} file(s) already up to date
|
|
152
|
+
console.log(chalk.gray(` ⏭ ${changes.skipped.length} file(s) already up to date:`));
|
|
153
|
+
for (const f of changes.skipped) {
|
|
154
|
+
console.log(chalk.gray(` = ${relPath(f)}`));
|
|
155
|
+
}
|
|
145
156
|
}
|
|
146
|
-
if (totalChanged === 0) {
|
|
147
|
-
console.log(chalk.gray(` ✔ ${adapter.name} —
|
|
157
|
+
if (totalChanged === 0 && changes.skipped.length === 0) {
|
|
158
|
+
console.log(chalk.gray(` ✔ ${adapter.name} — nothing to restore`));
|
|
148
159
|
}
|
|
149
160
|
spinner.start();
|
|
150
161
|
|