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 CHANGED
@@ -8,6 +8,8 @@
8
8
 
9
9
  *Never lose your AI's context again. Sync and translate your AI memory across every device and tool.*
10
10
 
11
+ ![memoir demo](demo.gif)
12
+
11
13
  </div>
12
14
 
13
15
  ---
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 ') + chalk.gray('— first-time setup') + '\n' +
24
- chalk.cyan(' memoir push ') + chalk.gray('— back up your AI memory') + '\n' +
25
- chalk.cyan(' memoir restore ') + chalk.gray('— restore on a new machine') + '\n' +
26
- chalk.cyan(' memoir status ') + chalk.gray('— see detected AI tools') + '\n\n' +
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": "1.5.2",
4
- "description": "Sync and translate AI memory across devices and tools. Back up Claude, Gemini, Codex, Cursor, Copilot, Windsurf, and Aider configs. Migrate instructions between AI coding assistants with one command.",
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": "echo \"Error: no test specified\" && exit 1"
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",
@@ -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
- const { confirm } = await inquirer.prompt([
78
- {
79
- type: 'confirm',
80
- name: 'confirm',
81
- message: `Restore ${adapter.name}?`,
82
- default: true
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(` + ${path.basename(f)}`) + chalk.gray(` (new)`));
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(` ↻ ${path.basename(f)}`) + chalk.gray(` (updated)`));
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} — already up to date`));
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