memoir-cli 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/memoir.js +18 -11
- package/package.json +1 -1
- package/src/commands/push.js +46 -12
- package/src/commands/restore.js +22 -8
- package/src/commands/status.js +33 -26
package/bin/memoir.js
CHANGED
|
@@ -8,16 +8,23 @@ 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
|
|
|
11
|
-
const VERSION = '1.
|
|
11
|
+
const VERSION = '1.2.0';
|
|
12
|
+
|
|
13
|
+
// Custom help banner
|
|
14
|
+
program.addHelpText('beforeAll', '\n' + boxen(
|
|
15
|
+
gradient.pastel.multiline(' memoir ') + '\n' +
|
|
16
|
+
chalk.gray(' Your AI remembers everything.'),
|
|
17
|
+
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, borderStyle: 'round', borderColor: 'cyan', dimBorder: true }
|
|
18
|
+
) + '\n');
|
|
12
19
|
|
|
13
20
|
program
|
|
14
21
|
.name('memoir')
|
|
15
|
-
.description('
|
|
22
|
+
.description(chalk.white('Sync your AI memory across every device.'))
|
|
16
23
|
.version(VERSION);
|
|
17
24
|
|
|
18
25
|
program
|
|
19
26
|
.command('init')
|
|
20
|
-
.description('
|
|
27
|
+
.description('Set up memoir with your storage provider')
|
|
21
28
|
.action(async () => {
|
|
22
29
|
try {
|
|
23
30
|
await initCommand();
|
|
@@ -30,7 +37,7 @@ program
|
|
|
30
37
|
program
|
|
31
38
|
.command('push')
|
|
32
39
|
.alias('remember')
|
|
33
|
-
.description('
|
|
40
|
+
.description('Back up your AI memory to the cloud')
|
|
34
41
|
.action(async () => {
|
|
35
42
|
try {
|
|
36
43
|
await pushCommand();
|
|
@@ -43,7 +50,7 @@ program
|
|
|
43
50
|
program
|
|
44
51
|
.command('restore')
|
|
45
52
|
.alias('pull')
|
|
46
|
-
.description('Restore your AI memory
|
|
53
|
+
.description('Restore your AI memory on this machine')
|
|
47
54
|
.action(async () => {
|
|
48
55
|
try {
|
|
49
56
|
await restoreCommand();
|
|
@@ -55,7 +62,7 @@ program
|
|
|
55
62
|
|
|
56
63
|
program
|
|
57
64
|
.command('status')
|
|
58
|
-
.description('
|
|
65
|
+
.description('See what AI tools are on this machine')
|
|
59
66
|
.action(async () => {
|
|
60
67
|
try {
|
|
61
68
|
await statusCommand();
|
|
@@ -67,13 +74,13 @@ program
|
|
|
67
74
|
|
|
68
75
|
program
|
|
69
76
|
.command('migrate')
|
|
70
|
-
.description('
|
|
77
|
+
.description('Translate memory between AI providers')
|
|
71
78
|
.action(() => {
|
|
72
79
|
console.log('\n' + boxen(
|
|
73
|
-
gradient.pastel('memoir migrate
|
|
74
|
-
chalk.white('
|
|
75
|
-
chalk.white('
|
|
76
|
-
chalk.cyan('
|
|
80
|
+
gradient.pastel(' memoir migrate ') + '\n\n' +
|
|
81
|
+
chalk.white('Instantly translate your context between') + '\n' +
|
|
82
|
+
chalk.white('Claude, Gemini, Codex, and more.') + '\n\n' +
|
|
83
|
+
chalk.cyan.bold('Coming soon.'),
|
|
77
84
|
{ padding: 1, borderStyle: 'round', borderColor: 'yellow', align: 'center' }
|
|
78
85
|
) + '\n');
|
|
79
86
|
});
|
package/package.json
CHANGED
package/src/commands/push.js
CHANGED
|
@@ -3,48 +3,82 @@ import fs from 'fs-extra';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
5
5
|
import ora from 'ora';
|
|
6
|
+
import boxen from 'boxen';
|
|
7
|
+
import gradient from 'gradient-string';
|
|
6
8
|
import { getConfig } from '../config.js';
|
|
7
|
-
import { extractMemories } from '../adapters/index.js';
|
|
9
|
+
import { extractMemories, adapters } from '../adapters/index.js';
|
|
8
10
|
import { syncToLocal, syncToGit } from '../providers/index.js';
|
|
9
11
|
|
|
10
12
|
export async function pushCommand() {
|
|
11
13
|
const config = await getConfig();
|
|
12
|
-
|
|
14
|
+
|
|
13
15
|
if (!config) {
|
|
14
|
-
console.log('
|
|
15
|
-
|
|
16
|
+
console.log('\n' + boxen(
|
|
17
|
+
chalk.red('✖ Not configured yet\n\n') +
|
|
18
|
+
chalk.white('Run ') + chalk.cyan.bold('memoir init') + chalk.white(' to get started.'),
|
|
19
|
+
{ padding: 1, borderStyle: 'round', borderColor: 'red' }
|
|
20
|
+
) + '\n');
|
|
16
21
|
return;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
console.log();
|
|
20
|
-
const spinner = ora('
|
|
25
|
+
const spinner = ora({ text: chalk.gray('Scanning for AI tools...'), spinner: 'dots' }).start();
|
|
21
26
|
|
|
22
|
-
// Create a temporary staging directory
|
|
23
27
|
const stagingDir = path.join(os.tmpdir(), `memoir-staging-${Date.now()}`);
|
|
24
28
|
await fs.ensureDir(stagingDir);
|
|
25
29
|
|
|
26
30
|
try {
|
|
27
|
-
spinner.text = 'Scanning system for AI configurations...';
|
|
28
|
-
|
|
29
|
-
// Pass spinner so adapter can update it
|
|
30
31
|
const foundAny = await extractMemories(stagingDir, spinner);
|
|
31
|
-
|
|
32
|
+
|
|
32
33
|
if (!foundAny) {
|
|
33
|
-
spinner.
|
|
34
|
+
spinner.stop();
|
|
35
|
+
console.log('\n' + boxen(
|
|
36
|
+
chalk.yellow('No AI tools detected on this machine.\n\n') +
|
|
37
|
+
chalk.gray('Supported: Claude, Gemini, Codex, Cursor, Copilot, Windsurf, Aider'),
|
|
38
|
+
{ padding: 1, borderStyle: 'round', borderColor: 'yellow' }
|
|
39
|
+
) + '\n');
|
|
34
40
|
return;
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
// Count what was found
|
|
44
|
+
const found = [];
|
|
45
|
+
for (const adapter of adapters) {
|
|
46
|
+
if (adapter.customExtract) {
|
|
47
|
+
for (const file of adapter.files) {
|
|
48
|
+
if (await fs.pathExists(path.join(adapter.source, file))) {
|
|
49
|
+
found.push(adapter.name);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} else if (await fs.pathExists(adapter.source)) {
|
|
54
|
+
found.push(adapter.name);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
spinner.text = chalk.gray('Uploading to ' + (config.provider === 'git' ? 'GitHub' : 'local storage') + '...');
|
|
59
|
+
|
|
37
60
|
if (config.provider === 'local' || config.provider.includes('local')) {
|
|
38
61
|
await syncToLocal(config, stagingDir, spinner);
|
|
39
62
|
} else if (config.provider === 'git' || config.provider.includes('git')) {
|
|
40
63
|
await syncToGit(config, stagingDir, spinner);
|
|
41
64
|
} else {
|
|
42
65
|
spinner.fail(chalk.red(`Unknown provider: ${config.provider}`));
|
|
66
|
+
return;
|
|
43
67
|
}
|
|
68
|
+
|
|
69
|
+
spinner.stop();
|
|
70
|
+
|
|
71
|
+
// Success output
|
|
72
|
+
const toolList = found.map(t => chalk.cyan(' ✔ ' + t)).join('\n');
|
|
73
|
+
console.log('\n' + boxen(
|
|
74
|
+
gradient.pastel(' Backed up! ') + '\n\n' +
|
|
75
|
+
toolList + '\n\n' +
|
|
76
|
+
chalk.gray(`${found.length} tool${found.length !== 1 ? 's' : ''} synced to ${config.provider === 'git' ? 'GitHub' : 'local storage'}`),
|
|
77
|
+
{ padding: 1, borderStyle: 'round', borderColor: 'green', dimBorder: true }
|
|
78
|
+
) + '\n');
|
|
44
79
|
} catch (error) {
|
|
45
80
|
spinner.fail(chalk.red('Sync failed: ') + error.message);
|
|
46
81
|
} finally {
|
|
47
|
-
// Clean up staging directory
|
|
48
82
|
await fs.remove(stagingDir);
|
|
49
83
|
}
|
|
50
84
|
}
|
package/src/commands/restore.js
CHANGED
|
@@ -3,22 +3,26 @@ import fs from 'fs-extra';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
5
5
|
import ora from 'ora';
|
|
6
|
+
import boxen from 'boxen';
|
|
7
|
+
import gradient from 'gradient-string';
|
|
6
8
|
import { getConfig } from '../config.js';
|
|
7
9
|
import { fetchFromLocal, fetchFromGit } from '../providers/restore.js';
|
|
8
10
|
|
|
9
11
|
export async function restoreCommand() {
|
|
10
12
|
const config = await getConfig();
|
|
11
|
-
|
|
13
|
+
|
|
12
14
|
if (!config) {
|
|
13
|
-
console.log('
|
|
14
|
-
|
|
15
|
+
console.log('\n' + boxen(
|
|
16
|
+
chalk.red('✖ Not configured yet\n\n') +
|
|
17
|
+
chalk.white('Run ') + chalk.cyan.bold('memoir init') + chalk.white(' to get started.'),
|
|
18
|
+
{ padding: 1, borderStyle: 'round', borderColor: 'red' }
|
|
19
|
+
) + '\n');
|
|
15
20
|
return;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
console.log();
|
|
19
|
-
const spinner = ora('
|
|
24
|
+
const spinner = ora({ text: chalk.gray('Fetching memories from ' + (config.provider === 'git' ? 'GitHub' : 'local storage') + '...'), spinner: 'dots' }).start();
|
|
20
25
|
|
|
21
|
-
// Create a temporary staging directory to hold the downloaded files
|
|
22
26
|
const stagingDir = path.join(os.tmpdir(), `memoir-restore-${Date.now()}`);
|
|
23
27
|
await fs.ensureDir(stagingDir);
|
|
24
28
|
|
|
@@ -34,16 +38,26 @@ export async function restoreCommand() {
|
|
|
34
38
|
return;
|
|
35
39
|
}
|
|
36
40
|
|
|
41
|
+
spinner.stop();
|
|
42
|
+
|
|
37
43
|
if (restored) {
|
|
38
|
-
|
|
44
|
+
console.log('\n' + boxen(
|
|
45
|
+
gradient.pastel(' Restored! ') + '\n\n' +
|
|
46
|
+
chalk.white('Your AI tools have their memories back.') + '\n' +
|
|
47
|
+
chalk.gray('They remember everything.'),
|
|
48
|
+
{ padding: 1, borderStyle: 'round', borderColor: 'green', dimBorder: true }
|
|
49
|
+
) + '\n');
|
|
39
50
|
} else {
|
|
40
|
-
|
|
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.'),
|
|
54
|
+
{ padding: 1, borderStyle: 'round', borderColor: 'yellow' }
|
|
55
|
+
) + '\n');
|
|
41
56
|
}
|
|
42
57
|
|
|
43
58
|
} catch (error) {
|
|
44
59
|
spinner.fail(chalk.red('Restore failed: ') + error.message);
|
|
45
60
|
} finally {
|
|
46
|
-
// Clean up staging directory
|
|
47
61
|
await fs.remove(stagingDir);
|
|
48
62
|
}
|
|
49
63
|
}
|
package/src/commands/status.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import boxen from 'boxen';
|
|
5
|
+
import gradient from 'gradient-string';
|
|
4
6
|
import { getConfig } from '../config.js';
|
|
5
7
|
import { adapters } from '../adapters/index.js';
|
|
6
8
|
|
|
@@ -10,47 +12,52 @@ export async function statusCommand() {
|
|
|
10
12
|
console.log();
|
|
11
13
|
|
|
12
14
|
// Config status
|
|
15
|
+
let configLine;
|
|
13
16
|
if (config) {
|
|
14
|
-
const provider = config.provider === 'git'
|
|
15
|
-
|
|
17
|
+
const provider = config.provider === 'git'
|
|
18
|
+
? chalk.cyan(config.gitRepo)
|
|
19
|
+
: chalk.cyan(config.localPath);
|
|
20
|
+
configLine = chalk.green('✔ Connected') + chalk.gray(' → ') + provider;
|
|
16
21
|
} else {
|
|
17
|
-
|
|
18
|
-
console.log();
|
|
19
|
-
return;
|
|
22
|
+
configLine = chalk.red('✖ Not configured') + chalk.gray(' → run ') + chalk.cyan('memoir init');
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
console.log();
|
|
23
|
-
|
|
24
25
|
// Detected tools
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
const lines = [];
|
|
27
27
|
let detected = 0;
|
|
28
|
+
|
|
28
29
|
for (const adapter of adapters) {
|
|
30
|
+
let found = false;
|
|
29
31
|
if (adapter.customExtract) {
|
|
30
|
-
let hasFiles = false;
|
|
31
32
|
for (const file of adapter.files) {
|
|
32
33
|
if (await fs.pathExists(path.join(adapter.source, file))) {
|
|
33
|
-
|
|
34
|
+
found = true;
|
|
34
35
|
break;
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
if (hasFiles) {
|
|
38
|
-
console.log(chalk.green(' ✔ ') + adapter.name);
|
|
39
|
-
detected++;
|
|
40
|
-
} else {
|
|
41
|
-
console.log(chalk.gray(' ○ ') + chalk.gray(adapter.name + ' — not found'));
|
|
42
|
-
}
|
|
43
38
|
} else {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
found = await fs.pathExists(adapter.source);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (found) {
|
|
43
|
+
lines.push(chalk.green(' ✔ ') + chalk.white(adapter.name));
|
|
44
|
+
detected++;
|
|
45
|
+
} else {
|
|
46
|
+
lines.push(chalk.gray(' ○ ' + adapter.name));
|
|
50
47
|
}
|
|
51
48
|
}
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
const summary = detected > 0
|
|
51
|
+
? chalk.white(`${detected} tool${detected !== 1 ? 's' : ''} ready to sync`)
|
|
52
|
+
: chalk.yellow('No AI tools detected');
|
|
53
|
+
|
|
54
|
+
console.log(boxen(
|
|
55
|
+
gradient.pastel(' memoir status ') + '\n\n' +
|
|
56
|
+
configLine + '\n\n' +
|
|
57
|
+
chalk.bold.white('AI Tools') + '\n' +
|
|
58
|
+
lines.join('\n') + '\n\n' +
|
|
59
|
+
chalk.gray('─'.repeat(30)) + '\n' +
|
|
60
|
+
summary,
|
|
61
|
+
{ padding: 1, borderStyle: 'round', borderColor: 'cyan', dimBorder: true }
|
|
62
|
+
) + '\n');
|
|
56
63
|
}
|