memoir-cli 1.4.5 → 1.5.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/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ Old Claude Instructions
package/TODO.md ADDED
@@ -0,0 +1,39 @@
1
+ # memoir Roadmap & TODO
2
+
3
+ This list tracks planned features, identified bugs, and architectural improvements for the `memoir` CLI.
4
+
5
+ ## 🔴 High Priority: Security, Reliability & Bug Fixes
6
+ - [ ] **`memoir doctor` Command:** Implement a diagnostic utility to verify:
7
+ - [ ] Correct installation of supported AI tools (Claude, Cursor, etc.).
8
+ - [ ] File permissions for all memory directories.
9
+ - [ ] Git connectivity and API key validity (Gemini).
10
+ - [ ] Environment variable health.
11
+ - [ ] **Secret & PII Guard:** Add a pre-push scan to detect API keys or PII in memory files. Implement a `redact` flag.
12
+ - [ ] **Linux Path Support:** Add path detection for Cursor and Windsurf on Linux (currently macOS/Windows only).
13
+ - [ ] **Aider Local Discovery:** Update the Aider adapter to look for `.aider/` in the current project repo, not just global config.
14
+ - [ ] **Robust Claude Pathing:** Replace fragile string-replacement in `src/tools/claude.js` with more reliable hashing/matching for `~/.claude/projects`.
15
+ - [ ] **Add `-y` / `--yes` Flags:** Enable non-interactive mode for `push`, `restore`, and `migrate` to support automation.
16
+
17
+ ## 🟡 Medium Priority: UX & Workflow
18
+ - [ ] **Local LLM Migration (Privacy):**
19
+ - [ ] Add support for **Ollama** and **LM Studio** as translation engines in `migrate`.
20
+ - [ ] Add a `--local` flag to `migrate` to bypass external APIs.
21
+ - [ ] **`memoir watch` (Background Sync):** Create a lightweight daemon to detect changes in local memory files and trigger auto-sync.
22
+ - [ ] **Project Bootstrapping (`memoir init --template`):** Seed new projects with "Golden Rule" templates (e.g., "Strict TypeScript," "React/Tailwind Best Practices").
23
+ - [ ] **Interactive Merge/Diff:** Replace "Overwrite/Append" in `migrate` with a side-by-side diff view.
24
+ - [ ] **Silent Mode:** Add a `--silent` flag to suppress all output except errors.
25
+
26
+ ## 🟢 Low Priority: Intelligence & Advanced Features
27
+ - [ ] **Unified Memory Format (UMF):** Architect an internal JSON schema to represent "Coding Context" to simplify adding new tool adapters.
28
+ - [ ] **Cross-Tool Search:** Implement `memoir search <query>` to find specific instructions across all tool backups.
29
+ - [ ] **Context Compression:** AI-powered `memoir optimize` command to summarize long instruction files and save tokens.
30
+ - [ ] **Organization Sync:** Support for shared "Team Memories" stored in a central repository.
31
+ - [ ] **Memory Analytics:** `memoir stats` to visualize the growth and "personality" of your AI instructions over time.
32
+
33
+ ---
34
+
35
+ ## 📝 Technical Observations & Bug Log
36
+ * **Circular Dependency:** `package.json` lists `memoir-cli` as its own dependency. Needs cleanup.
37
+ * **Git Performance:** `push` currently clones the entire repo every time. Optimize with `git clone --depth 1` or local cache.
38
+ * **Performance:** Refactor `fs.readFileSync` inside loops (e.g., in `src/tools/claude.js`) to use `fs.promises` for better handling of large projects.
39
+ * **CLI Friction:** The `migrate` command's interactive prompt for multiple files can be tedious; batching confirmations would improve UX.
package/bin/memoir.js CHANGED
@@ -14,6 +14,22 @@ import { createRequire } from 'module';
14
14
  const require = createRequire(import.meta.url);
15
15
  const { version: VERSION } = require('../package.json');
16
16
 
17
+ // Show quick start when run with no args
18
+ if (process.argv.length <= 2) {
19
+ console.log('\n' + boxen(
20
+ gradient.pastel.multiline(' memoir ') + '\n' +
21
+ chalk.gray(' Your AI remembers everything.') + '\n\n' +
22
+ 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' +
27
+ chalk.gray(`v${VERSION}`),
28
+ { padding: 1, borderStyle: 'round', borderColor: 'cyan', dimBorder: true }
29
+ ) + '\n');
30
+ process.exit(0);
31
+ }
32
+
17
33
  // Custom help banner
18
34
  program.addHelpText('beforeAll', '\n' + boxen(
19
35
  gradient.pastel.multiline(' memoir ') + '\n' +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memoir-cli",
3
- "version": "1.4.5",
3
+ "version": "1.5.1",
4
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.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -55,7 +55,7 @@
55
55
  "fs-extra": "^11.2.0",
56
56
  "gradient-string": "^3.0.0",
57
57
  "inquirer": "^9.2.15",
58
- "memoir-cli": "^1.4.4",
58
+
59
59
  "open": "^11.0.0",
60
60
  "ora": "^7.0.1"
61
61
  }
@@ -1,4 +1,5 @@
1
1
  import fs from 'fs-extra';
2
+ import nodeFs from 'node:fs';
2
3
  import path from 'path';
3
4
  import os from 'os';
4
5
  import chalk from 'chalk';
@@ -14,18 +15,42 @@ export const adapters = [
14
15
  icon: '🔵',
15
16
  source: path.join(home, '.gemini'),
16
17
  filter: (src) => {
18
+ const geminiDir = path.join(home, '.gemini');
19
+ const rel = path.relative(geminiDir, src);
20
+ if (src === geminiDir) return true;
21
+ // Only sync config/settings files — skip caches, history, auth, sandbox, etc.
22
+ const allowed = ['settings.json', 'projects.json', 'state.json', 'installation_id', 'trustedFolders.json', '.gitignore', 'GEMINI.md'];
17
23
  const basename = path.basename(src);
18
- const ignored = ['.git', 'oauth_creds.json', 'google_accounts.json', 'tmp', 'history'];
19
- return !ignored.includes(basename);
24
+ return allowed.includes(basename) && !rel.includes(path.sep);
20
25
  }
21
26
  },
22
27
  {
23
28
  name: 'Claude CLI',
24
29
  icon: '🟣',
25
30
  source: path.join(home, '.claude'),
26
- filter: (src) => {
31
+ filter: (src, dest) => {
27
32
  const basename = path.basename(src);
28
- return !basename.endsWith('.key') && basename !== '.env';
33
+ const claudeDir = path.join(home, '.claude');
34
+ const rel = path.relative(claudeDir, src);
35
+ // Root dir itself
36
+ if (src === claudeDir) return true;
37
+ // Only allow these top-level dirs
38
+ const topDir = rel.split(path.sep)[0];
39
+ const allowedDirs = ['projects', 'settings'];
40
+ const allowedFiles = ['settings.json', 'settings.local.json'];
41
+ // Allow specific top-level config files
42
+ if (!rel.includes(path.sep) && allowedFiles.includes(basename)) return true;
43
+ // Allow projects dir (contains memory .md files)
44
+ if (topDir === 'projects') {
45
+ // Allow directory traversal
46
+ try { if (nodeFs.statSync(src).isDirectory()) return true; } catch {}
47
+ // Only sync memory markdown files
48
+ return basename.endsWith('.md');
49
+ }
50
+ // Allow settings dir
51
+ if (topDir === 'settings') return true;
52
+ // Block everything else
53
+ return false;
29
54
  }
30
55
  },
31
56
  {
@@ -33,9 +58,13 @@ export const adapters = [
33
58
  icon: '🟢',
34
59
  source: path.join(home, '.codex'),
35
60
  filter: (src) => {
61
+ const codexDir = path.join(home, '.codex');
62
+ const rel = path.relative(codexDir, src);
63
+ if (src === codexDir) return true;
36
64
  const basename = path.basename(src);
37
- const ignored = ['.git', 'sessions', 'cache'];
38
- return !ignored.includes(basename) && !basename.endsWith('.key') && basename !== '.env';
65
+ // Only sync config files
66
+ const allowed = ['config.json', 'settings.json', 'instructions.md'];
67
+ return allowed.includes(basename) && !rel.includes(path.sep);
39
68
  }
40
69
  },
41
70
  {
@@ -45,9 +74,19 @@ export const adapters = [
45
74
  ? path.join(appData, 'Cursor', 'User')
46
75
  : path.join(home, 'Library', 'Application Support', 'Cursor', 'User'),
47
76
  filter: (src) => {
77
+ const cursorDir = isWin
78
+ ? path.join(appData, 'Cursor', 'User')
79
+ : path.join(home, 'Library', 'Application Support', 'Cursor', 'User');
80
+ const rel = path.relative(cursorDir, src);
81
+ if (src === cursorDir) return true;
48
82
  const basename = path.basename(src);
49
- const ignored = ['globalStorage', 'workspaceStorage', 'CachedData', 'Cache', 'GPUCache', 'logs', 'History', 'Backups', 'snippets'];
50
- return !ignored.includes(basename);
83
+ // Only sync settings and keybindings not extensions, cache, storage
84
+ const allowed = ['settings.json', 'keybindings.json', 'rules'];
85
+ const topDir = rel.split(path.sep)[0];
86
+ if (allowed.includes(basename) && !rel.includes(path.sep)) return true;
87
+ // Allow rules directory (cursor rules)
88
+ if (topDir === 'rules') return true;
89
+ return false;
51
90
  }
52
91
  },
53
92
  {
@@ -57,9 +96,14 @@ export const adapters = [
57
96
  ? path.join(appData, 'GitHub Copilot')
58
97
  : path.join(home, '.config', 'github-copilot'),
59
98
  filter: (src) => {
99
+ const copilotDir = isWin
100
+ ? path.join(appData, 'GitHub Copilot')
101
+ : path.join(home, '.config', 'github-copilot');
102
+ if (src === copilotDir) return true;
60
103
  const basename = path.basename(src);
61
- const ignored = ['hosts.json', 'apps.json', 'versions.json'];
62
- return !ignored.includes(basename);
104
+ // Only sync config skip auth tokens and version files
105
+ const allowed = ['settings.json', 'config.json'];
106
+ return allowed.includes(basename);
63
107
  }
64
108
  },
65
109
  {
@@ -69,9 +113,18 @@ export const adapters = [
69
113
  ? path.join(appData, 'Windsurf', 'User')
70
114
  : path.join(home, 'Library', 'Application Support', 'Windsurf', 'User'),
71
115
  filter: (src) => {
116
+ const windsurfDir = isWin
117
+ ? path.join(appData, 'Windsurf', 'User')
118
+ : path.join(home, 'Library', 'Application Support', 'Windsurf', 'User');
119
+ const rel = path.relative(windsurfDir, src);
120
+ if (src === windsurfDir) return true;
72
121
  const basename = path.basename(src);
73
- const ignored = ['workspaceStorage', 'CachedData', 'Cache', 'GPUCache', 'logs', 'History', 'Backups', 'memories', 'snippets'];
74
- return !ignored.includes(basename);
122
+ // Only sync settings and keybindings
123
+ const allowed = ['settings.json', 'keybindings.json', 'rules'];
124
+ const topDir = rel.split(path.sep)[0];
125
+ if (allowed.includes(basename) && !rel.includes(path.sep)) return true;
126
+ if (topDir === 'rules') return true;
127
+ return false;
75
128
  }
76
129
  },
77
130
  {
@@ -5,7 +5,30 @@ import os from 'os';
5
5
  import inquirer from 'inquirer';
6
6
  import { adapters } from '../adapters/index.js';
7
7
 
8
- async function copyMissing(src, dest, changes) {
8
+ // Claude CLI stores projects under paths like `projects/-Users-camarthur/`
9
+ // This converts the path from the backup machine to match the current machine
10
+ function remapProjectPath(backupDir, adapterSource) {
11
+ const projectsDir = path.join(backupDir, 'projects');
12
+ if (!fs.existsSync(projectsDir)) return null;
13
+
14
+ const entries = fs.readdirSync(projectsDir);
15
+ // Find the backed-up home dir key (e.g., "-Users-camarthur")
16
+ const oldHomeKey = entries.find(e => {
17
+ return fs.statSync(path.join(projectsDir, e)).isDirectory();
18
+ });
19
+ if (!oldHomeKey) return null;
20
+
21
+ // Build the current machine's home dir key
22
+ // Claude uses the homedir path with / replaced by - and leading -
23
+ const home = os.homedir();
24
+ const newHomeKey = '-' + home.replace(/^\//, '').replace(/\\/g, '-').replace(/\//g, '-').replace(/:/g, '');
25
+
26
+ if (oldHomeKey === newHomeKey) return null; // Same machine, no remap needed
27
+
28
+ return { oldHomeKey, newHomeKey };
29
+ }
30
+
31
+ async function syncFiles(src, dest, changes) {
9
32
  const entries = await fs.readdir(src, { withFileTypes: true });
10
33
  for (const entry of entries) {
11
34
  const srcPath = path.join(src, entry.name);
@@ -13,10 +36,18 @@ async function copyMissing(src, dest, changes) {
13
36
 
14
37
  if (entry.isDirectory()) {
15
38
  await fs.ensureDir(destPath);
16
- await copyMissing(srcPath, destPath, changes);
39
+ await syncFiles(srcPath, destPath, changes);
17
40
  } else {
18
41
  if (await fs.pathExists(destPath)) {
19
- changes.skipped.push(destPath);
42
+ // Compare modification times — update if backup is newer
43
+ const srcStat = await fs.stat(srcPath);
44
+ const destStat = await fs.stat(destPath);
45
+ if (srcStat.mtimeMs > destStat.mtimeMs) {
46
+ await fs.copy(srcPath, destPath);
47
+ changes.updated.push(destPath);
48
+ } else {
49
+ changes.skipped.push(destPath);
50
+ }
20
51
  } else {
21
52
  await fs.copy(srcPath, destPath);
22
53
  changes.added.push(destPath);
@@ -27,6 +58,7 @@ async function copyMissing(src, dest, changes) {
27
58
 
28
59
  export async function restoreMemories(sourceDir, spinner) {
29
60
  let restoredAny = false;
61
+ const allResults = [];
30
62
 
31
63
  for (const adapter of adapters) {
32
64
  const backupDir = path.join(sourceDir, adapter.name.toLowerCase().replace(/ /g, '-'));
@@ -34,61 +66,103 @@ export async function restoreMemories(sourceDir, spinner) {
34
66
  if (await fs.pathExists(backupDir)) {
35
67
  spinner.stop();
36
68
 
37
- console.log('\n' + chalk.yellow(`⚠ Found backup for ${chalk.bold(adapter.name)}.`));
69
+ console.log('\n' + chalk.cyan(`${adapter.icon} Found backup for ${chalk.bold(adapter.name)}`));
70
+ console.log(chalk.gray(` Will restore to: ${adapter.source}`));
38
71
  const { confirm } = await inquirer.prompt([
39
72
  {
40
73
  type: 'confirm',
41
74
  name: 'confirm',
42
- message: `Restore ${adapter.name} memory? (only adds missing files, won't overwrite)`,
43
- default: false
75
+ message: `Restore ${adapter.name}?`,
76
+ default: true
44
77
  }
45
78
  ]);
46
79
 
47
80
  spinner.start();
48
81
 
49
82
  if (confirm) {
50
- const changes = { added: [], skipped: [] };
83
+ const changes = { added: [], updated: [], skipped: [] };
84
+
85
+ // Remap Claude project paths from source machine to this machine
86
+ if (adapter.name === 'Claude CLI') {
87
+ const remap = remapProjectPath(backupDir, adapter.source);
88
+ if (remap) {
89
+ spinner.stop();
90
+ console.log(chalk.gray(` Remapping project path: ${remap.oldHomeKey} → ${remap.newHomeKey}`));
91
+ spinner.start();
92
+ // Rename the directory in staging so it restores to the right place
93
+ const oldDir = path.join(backupDir, 'projects', remap.oldHomeKey);
94
+ const newDir = path.join(backupDir, 'projects', remap.newHomeKey);
95
+ if (await fs.pathExists(oldDir) && !(await fs.pathExists(newDir))) {
96
+ await fs.move(oldDir, newDir);
97
+ }
98
+ }
99
+ }
51
100
 
52
101
  if (adapter.customExtract) {
53
102
  const files = await fs.readdir(backupDir);
54
103
  for (const file of files) {
55
- const dest = path.join(adapter.source, file);
56
- if (await fs.pathExists(dest)) {
57
- changes.skipped.push(dest);
104
+ const destFile = path.join(adapter.source, file);
105
+ if (await fs.pathExists(destFile)) {
106
+ const srcStat = await fs.stat(path.join(backupDir, file));
107
+ const destStat = await fs.stat(destFile);
108
+ if (srcStat.mtimeMs > destStat.mtimeMs) {
109
+ await fs.copy(path.join(backupDir, file), destFile);
110
+ changes.updated.push(destFile);
111
+ } else {
112
+ changes.skipped.push(destFile);
113
+ }
58
114
  } else {
59
- await fs.copy(path.join(backupDir, file), dest);
60
- changes.added.push(dest);
115
+ await fs.copy(path.join(backupDir, file), destFile);
116
+ changes.added.push(destFile);
61
117
  }
62
118
  }
63
119
  } else {
64
- spinner.text = `Restoring ${chalk.cyan(adapter.name)} memory to ${adapter.source}...`;
120
+ spinner.text = `Restoring ${chalk.cyan(adapter.name)} to ${adapter.source}...`;
65
121
  await fs.ensureDir(adapter.source);
66
- await copyMissing(backupDir, adapter.source, changes);
122
+ await syncFiles(backupDir, adapter.source, changes);
67
123
  }
68
124
 
69
125
  // Show summary of changes
70
126
  spinner.stop();
71
- if (changes.added.length > 0) {
72
- console.log(chalk.green.bold(`\n ✔ ${adapter.name} ${changes.added.length} file(s) added:`));
127
+ const totalChanged = changes.added.length + changes.updated.length;
128
+ if (totalChanged > 0) {
129
+ console.log(chalk.green.bold(`\n ${adapter.icon} ${adapter.name} — ${totalChanged} file(s) restored to ${chalk.underline(adapter.source)}`));
73
130
  for (const f of changes.added) {
74
- console.log(chalk.green(` + ${f}`));
131
+ console.log(chalk.green(` + ${path.basename(f)}`) + chalk.gray(` (new)`));
132
+ }
133
+ for (const f of changes.updated) {
134
+ console.log(chalk.yellow(` ↻ ${path.basename(f)}`) + chalk.gray(` (updated)`));
75
135
  }
76
136
  }
77
137
  if (changes.skipped.length > 0) {
78
- console.log(chalk.gray(` ⏭ ${changes.skipped.length} file(s) already existed (kept yours)`));
138
+ console.log(chalk.gray(` ⏭ ${changes.skipped.length} file(s) already up to date`));
79
139
  }
80
- if (changes.added.length === 0 && changes.skipped.length > 0) {
81
- console.log(chalk.gray(` Nothing new to add you're already up to date.`));
140
+ if (totalChanged === 0) {
141
+ console.log(chalk.gray(` ${adapter.name} — already up to date`));
82
142
  }
83
143
  spinner.start();
84
144
 
145
+ allResults.push({ name: adapter.name, icon: adapter.icon, dest: adapter.source, added: changes.added.length, updated: changes.updated.length });
85
146
  restoredAny = true;
86
147
  } else {
87
- spinner.info(chalk.gray(`Skipped restoring ${adapter.name}.`));
148
+ spinner.info(chalk.gray(`Skipped ${adapter.name}.`));
88
149
  spinner.start();
89
150
  }
90
151
  }
91
152
  }
92
153
 
154
+ // Final recap
155
+ if (allResults.length > 0) {
156
+ spinner.stop();
157
+ console.log('\n' + chalk.gray('─'.repeat(40)));
158
+ console.log(chalk.bold.white('\n Restore Summary:\n'));
159
+ for (const r of allResults) {
160
+ const count = r.added + r.updated;
161
+ console.log(` ${r.icon} ${chalk.white(r.name)}`);
162
+ console.log(chalk.gray(` ${count} file(s) → ${r.dest}`));
163
+ }
164
+ console.log('');
165
+ }
166
+
93
167
  return restoredAny;
94
168
  }
@@ -67,38 +67,23 @@ export async function initCommand() {
67
67
  }]);
68
68
  config.localPath = localPath;
69
69
  } else {
70
- // Pre-fill username if detected, just ask for repo name
71
- const prompts = [];
72
-
73
- if (detectedUser) {
74
- console.log(chalk.gray(` GitHub user: ${chalk.cyan(detectedUser)}`));
75
- prompts.push({
70
+ const answers = await inquirer.prompt([
71
+ {
72
+ type: 'input',
73
+ name: 'username',
74
+ message: 'GitHub username:',
75
+ default: detectedUser || undefined,
76
+ validate: (input) => input.trim() ? true : 'Required'
77
+ },
78
+ {
76
79
  type: 'input',
77
80
  name: 'repo',
78
- message: `Repo name (${detectedUser}/???):`,
81
+ message: 'Repo name:',
79
82
  default: 'ai-memory',
80
83
  validate: (input) => input.trim() ? true : 'Required'
81
- });
82
- } else {
83
- prompts.push(
84
- {
85
- type: 'input',
86
- name: 'username',
87
- message: 'GitHub username:',
88
- validate: (input) => input.trim() ? true : 'Required'
89
- },
90
- {
91
- type: 'input',
92
- name: 'repo',
93
- message: 'Repo name:',
94
- default: 'ai-memory',
95
- validate: (input) => input.trim() ? true : 'Required'
96
- }
97
- );
98
- }
99
-
100
- const answers = await inquirer.prompt(prompts);
101
- const username = (answers.username || detectedUser).trim();
84
+ }
85
+ ]);
86
+ const username = answers.username.trim();
102
87
  const repo = answers.repo.trim();
103
88
 
104
89
  config.gitRepo = `https://github.com/${username}/${repo}.git`;
@@ -68,12 +68,34 @@ export async function pushCommand() {
68
68
 
69
69
  spinner.stop();
70
70
 
71
+ // Count total files
72
+ let totalFiles = 0;
73
+ for (const adapter of adapters) {
74
+ const adapterDir = path.join(stagingDir, adapter.name.toLowerCase().replace(/ /g, '-'));
75
+ if (await fs.pathExists(adapterDir)) {
76
+ const countDir = async (dir) => {
77
+ let c = 0;
78
+ const entries = await fs.readdir(dir, { withFileTypes: true });
79
+ for (const e of entries) {
80
+ if (e.isDirectory()) c += await countDir(path.join(dir, e.name));
81
+ else c++;
82
+ }
83
+ return c;
84
+ };
85
+ totalFiles += await countDir(adapterDir);
86
+ }
87
+ }
88
+
89
+ const dest = config.provider === 'git' ? config.gitRepo : config.localPath;
90
+
71
91
  // Success output
72
92
  const toolList = found.map(t => chalk.cyan(' ✔ ' + t)).join('\n');
73
93
  console.log('\n' + boxen(
74
94
  gradient.pastel(' Backed up! ') + '\n\n' +
75
95
  toolList + '\n\n' +
76
- chalk.gray(`${found.length} tool${found.length !== 1 ? 's' : ''} synced to ${config.provider === 'git' ? 'GitHub' : 'local storage'}`),
96
+ chalk.white(`${totalFiles} files from ${found.length} tool${found.length !== 1 ? 's' : ''}`) + '\n' +
97
+ chalk.gray(`→ ${dest}`) + '\n\n' +
98
+ chalk.gray('Restore on another machine with: ') + chalk.cyan('memoir restore'),
77
99
  { padding: 1, borderStyle: 'round', borderColor: 'green', dimBorder: true }
78
100
  ) + '\n');
79
101
  } catch (error) {
@@ -41,16 +41,20 @@ export async function restoreCommand() {
41
41
  spinner.stop();
42
42
 
43
43
  if (restored) {
44
- console.log('\n' + boxen(
45
- gradient.pastel(' Restored! ') + '\n\n' +
44
+ console.log(boxen(
45
+ gradient.pastel(' Done! ') + '\n\n' +
46
46
  chalk.white('Your AI tools have their memories back.') + '\n' +
47
- chalk.gray('They remember everything.'),
47
+ chalk.gray('Restart your AI tools to pick up the changes.'),
48
48
  { padding: 1, borderStyle: 'round', borderColor: 'green', dimBorder: true }
49
49
  ) + '\n');
50
50
  } else {
51
51
  console.log('\n' + boxen(
52
- chalk.yellow('No memories were restored.\n\n') +
53
- chalk.gray('Run ') + chalk.cyan('memoir push') + chalk.gray(' on another machine first.'),
52
+ chalk.yellow('Nothing was restored.\n\n') +
53
+ chalk.white('This can happen if:\n') +
54
+ chalk.gray(' 1. You haven\'t run ') + chalk.cyan('memoir push') + chalk.gray(' on another machine yet\n') +
55
+ chalk.gray(' 2. You skipped all the restore prompts\n') +
56
+ chalk.gray(' 3. The backup repo is empty\n\n') +
57
+ chalk.gray('Try: ') + chalk.cyan('memoir view') + chalk.gray(' to see what\'s in your backup'),
54
58
  { padding: 1, borderStyle: 'round', borderColor: 'yellow' }
55
59
  ) + '\n');
56
60
  }