memoir-cli 2.1.1 ā 2.4.0
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 +91 -113
- package/bin/memoir.js +137 -12
- package/package.json +7 -3
- package/src/adapters/index.js +75 -1
- package/src/commands/diff.js +2 -2
- package/src/commands/doctor.js +223 -0
- package/src/commands/init.js +19 -1
- package/src/commands/profile.js +199 -0
- package/src/commands/push.js +4 -2
- package/src/commands/restore.js +1 -1
- package/src/commands/resume.js +1 -1
- package/src/commands/snapshot.js +1 -1
- package/src/commands/status.js +19 -11
- package/src/commands/view.js +2 -2
- package/src/config.js +94 -7
- package/src/tools/chatgpt.js +24 -0
- package/src/tools/cline.js +72 -0
- package/src/tools/continuedev.js +55 -0
- package/src/tools/index.js +5 -1
- package/src/tools/zed.js +50 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# memoir
|
|
4
4
|
**Your AI Remembers Everything. Sync It Everywhere.**
|
|
5
5
|
|
|
6
6
|
[](https://npmjs.org/package/memoir-cli)
|
|
@@ -14,186 +14,164 @@
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## The Problem
|
|
18
18
|
|
|
19
|
-
You spend weeks teaching your
|
|
19
|
+
You spend weeks teaching your AI tools how you code ā your preferred patterns, project context, coding standards.
|
|
20
20
|
|
|
21
|
-
Then
|
|
21
|
+
Then you switch laptops. Or try a new AI tool. Or want your team on the same page.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
All that context is trapped in hidden dotfiles on one machine.
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## The Solution
|
|
26
26
|
|
|
27
|
-
`memoir`
|
|
28
|
-
|
|
29
|
-
No locked-in SaaS, no lost context, no complex shell scripts. Switch from Claude to Gemini in one command.
|
|
30
|
-
|
|
31
|
-
### Supported Integrations
|
|
32
|
-
- [x] **Gemini CLI**
|
|
33
|
-
- [x] **Claude Code**
|
|
34
|
-
- [x] **OpenAI Codex CLI**
|
|
35
|
-
- [x] **Cursor**
|
|
36
|
-
- [x] **GitHub Copilot**
|
|
37
|
-
- [x] **Windsurf**
|
|
38
|
-
- [x] **Aider**
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## š ļø Installation
|
|
43
|
-
|
|
44
|
-
Install globally via npm so you can use it anywhere on your machine:
|
|
27
|
+
`memoir` extracts, backs up, restores, and **translates** your AI memory across any computer and any tool. One command to save. One command to restore. One command to translate between tools.
|
|
45
28
|
|
|
46
29
|
```bash
|
|
47
30
|
npm install -g memoir-cli
|
|
48
31
|
```
|
|
49
32
|
|
|
50
|
-
|
|
33
|
+
### Supported Tools (11)
|
|
34
|
+
| Tool | Config synced |
|
|
35
|
+
|------|--------------|
|
|
36
|
+
| **ChatGPT** | `CHATGPT.md` ā custom instructions, preferences |
|
|
37
|
+
| **Claude Code** | `~/.claude/` ā settings, projects, memory files |
|
|
38
|
+
| **Gemini CLI** | `~/.gemini/` ā settings, GEMINI.md |
|
|
39
|
+
| **OpenAI Codex** | `~/.codex/` ā config, instructions |
|
|
40
|
+
| **Cursor** | Settings, keybindings, rules |
|
|
41
|
+
| **GitHub Copilot** | Config, settings |
|
|
42
|
+
| **Windsurf** | Settings, keybindings, rules |
|
|
43
|
+
| **Zed** | Settings, keymap, tasks |
|
|
44
|
+
| **Cline** | Settings, rules |
|
|
45
|
+
| **Continue.dev** | Config, rules |
|
|
46
|
+
| **Aider** | `.aider.conf.yml`, system prompt |
|
|
47
|
+
|
|
48
|
+
Plus **per-project configs**: automatically finds `CLAUDE.md`, `GEMINI.md`, `CHATGPT.md`, `.cursorrules`, `AGENTS.md` across all your projects.
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
Run the setup wizard. We'll help you securely link a private GitHub repository or a local sync folder.
|
|
50
|
+
---
|
|
54
51
|
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
### 1. Initialize
|
|
55
55
|
```bash
|
|
56
56
|
memoir init
|
|
57
|
+
# Walks you through setup ā GitHub repo or local folder
|
|
58
|
+
# Auto-creates a private repo if you have gh CLI
|
|
57
59
|
```
|
|
58
60
|
|
|
59
|
-
### 2.
|
|
60
|
-
Just had a great session? Save your AI's learned context to the cloud:
|
|
61
|
-
|
|
61
|
+
### 2. Back up your memory
|
|
62
62
|
```bash
|
|
63
63
|
memoir push
|
|
64
|
-
# or simply use the alias:
|
|
65
|
-
memoir remember
|
|
66
64
|
```
|
|
67
65
|
|
|
68
|
-
### 3. Restore
|
|
69
|
-
Got a new machine? Pull your brain down instantly:
|
|
70
|
-
|
|
66
|
+
### 3. Restore on a new machine
|
|
71
67
|
```bash
|
|
72
68
|
memoir restore
|
|
73
|
-
# or:
|
|
74
|
-
memoir pull
|
|
75
69
|
```
|
|
76
70
|
|
|
77
|
-
### 4. Translate
|
|
78
|
-
Switch AI tools without losing context. Memoir uses Gemini AI to intelligently rewrite your memory files for any supported tool:
|
|
79
|
-
|
|
71
|
+
### 4. Translate between tools
|
|
80
72
|
```bash
|
|
81
73
|
memoir migrate --from claude --to gemini
|
|
82
|
-
#
|
|
83
|
-
memoir migrate
|
|
74
|
+
# AI-powered translation ā not copy-paste, real rewriting
|
|
84
75
|
```
|
|
85
76
|
|
|
86
|
-
Your Claude instructions become a proper `GEMINI.md` ā not a copy-paste, but a real translation that follows each tool's conventions.
|
|
87
|
-
|
|
88
77
|
---
|
|
89
78
|
|
|
90
|
-
##
|
|
79
|
+
## All Commands
|
|
91
80
|
|
|
92
81
|
| Command | What it does |
|
|
93
82
|
|---------|-------------|
|
|
94
|
-
| `memoir init` | Setup wizard ā
|
|
95
|
-
| `memoir push` |
|
|
96
|
-
| `memoir restore` |
|
|
97
|
-
| `memoir status` | Show
|
|
98
|
-
| `memoir
|
|
99
|
-
| `memoir
|
|
83
|
+
| `memoir init` | Setup wizard ā GitHub or local, upload or download |
|
|
84
|
+
| `memoir push` | Back up all AI configs |
|
|
85
|
+
| `memoir restore` | Restore on a new machine (non-destructive) |
|
|
86
|
+
| `memoir status` | Show detected AI tools |
|
|
87
|
+
| `memoir doctor` | Diagnose issues, scan for secrets |
|
|
88
|
+
| `memoir view` | Preview what's in your backup |
|
|
89
|
+
| `memoir diff` | Show changes since last backup |
|
|
90
|
+
| `memoir migrate` | Translate memory between tools via AI |
|
|
91
|
+
| `memoir snapshot` | Capture current coding session |
|
|
92
|
+
| `memoir resume` | Pick up where you left off |
|
|
93
|
+
| `memoir profile` | Manage profiles (personal/work) |
|
|
94
|
+
| `memoir update` | Self-update to latest version |
|
|
100
95
|
|
|
101
96
|
---
|
|
102
97
|
|
|
103
|
-
##
|
|
98
|
+
## Profiles
|
|
99
|
+
|
|
100
|
+
Separate personal and work configs ā different repos, different tools.
|
|
104
101
|
|
|
105
|
-
### New laptop setup
|
|
106
102
|
```bash
|
|
107
|
-
#
|
|
108
|
-
memoir
|
|
103
|
+
memoir profile create work # set up work profile with its own repo
|
|
104
|
+
memoir profile create personal # personal side projects
|
|
109
105
|
|
|
110
|
-
#
|
|
111
|
-
memoir
|
|
112
|
-
# All your .claude/, .gemini/, .cursorrules configs restored in 30 seconds
|
|
113
|
-
```
|
|
106
|
+
memoir push --profile work # sync only work configs
|
|
107
|
+
memoir restore --profile personal
|
|
114
108
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
memoir migrate --from claude --to gemini
|
|
109
|
+
memoir profile list # see all profiles
|
|
110
|
+
memoir profile switch work # change default
|
|
118
111
|
```
|
|
119
|
-
Your CLAUDE.md + Claude memory files get intelligently rewritten as a proper GEMINI.md ā not a copy-paste, but a real translation that follows Gemini's conventions.
|
|
120
112
|
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
# Team lead writes CLAUDE.md, then generates for everyone else:
|
|
124
|
-
memoir migrate --from claude --to cursor
|
|
125
|
-
memoir migrate --from claude --to copilot
|
|
126
|
-
memoir migrate --from claude --to codex
|
|
127
|
-
```
|
|
113
|
+
Each profile can filter which tools to sync, so your personal side project memory never mixes with work.
|
|
128
114
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
memoir migrate --from claude --to codex
|
|
133
|
-
memoir migrate --from claude --to cursor
|
|
134
|
-
memoir migrate --from claude --to windsurf
|
|
135
|
-
memoir migrate --from claude --to aider
|
|
136
|
-
```
|
|
137
|
-
Use one tool as the source of truth, propagate to all others.
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Common Workflows
|
|
138
118
|
|
|
139
|
-
###
|
|
119
|
+
### New laptop setup
|
|
140
120
|
```bash
|
|
141
|
-
|
|
142
|
-
|
|
121
|
+
# Old machine
|
|
122
|
+
memoir push
|
|
123
|
+
|
|
124
|
+
# New machine
|
|
125
|
+
memoir init # ā Download ā same GitHub repo
|
|
126
|
+
memoir restore # All configs restored in seconds
|
|
143
127
|
```
|
|
144
128
|
|
|
145
|
-
###
|
|
129
|
+
### Translate between tools
|
|
146
130
|
```bash
|
|
147
|
-
memoir migrate --from
|
|
148
|
-
#
|
|
149
|
-
# ā Overwrite / Append / Skip
|
|
150
|
-
# Append adds a dated separator so you keep your existing instructions
|
|
131
|
+
memoir migrate --from chatgpt --to claude
|
|
132
|
+
# Your ChatGPT custom instructions become a proper CLAUDE.md
|
|
151
133
|
```
|
|
152
134
|
|
|
153
|
-
###
|
|
135
|
+
### Fan out to every tool
|
|
154
136
|
```bash
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
# Next morning, different machine
|
|
159
|
-
memoir pull
|
|
137
|
+
memoir migrate --from chatgpt --to all
|
|
138
|
+
# One source of truth, every tool gets its own format
|
|
160
139
|
```
|
|
161
140
|
|
|
162
|
-
###
|
|
141
|
+
### Daily sync
|
|
163
142
|
```bash
|
|
164
|
-
memoir
|
|
165
|
-
#
|
|
143
|
+
memoir push # end of day
|
|
144
|
+
memoir restore # next morning, different machine
|
|
166
145
|
```
|
|
167
146
|
|
|
168
147
|
---
|
|
169
148
|
|
|
170
|
-
##
|
|
149
|
+
## Security
|
|
171
150
|
|
|
172
|
-
|
|
151
|
+
Memoir **only** syncs config files, instructions, and memory markdown. It never touches credentials, API keys, `.env` files, or auth tokens.
|
|
173
152
|
|
|
174
|
-
|
|
153
|
+
Run `memoir doctor` to see exactly what would be synced and scan for accidental secrets before pushing.
|
|
175
154
|
|
|
176
155
|
---
|
|
177
156
|
|
|
178
|
-
##
|
|
157
|
+
## Roadmap
|
|
179
158
|
|
|
180
|
-
**
|
|
181
|
-
-
|
|
182
|
-
-
|
|
183
|
-
-
|
|
159
|
+
- **Universal format** ā write one `MEMOIR.md`, generate all tool-specific configs
|
|
160
|
+
- **Cloud sync** ā no GitHub needed, encrypted backups
|
|
161
|
+
- **Teams** ā shared coding standards across your whole team
|
|
162
|
+
- **Templates** ā community-shared AI tool configs
|
|
184
163
|
|
|
185
164
|
---
|
|
186
165
|
|
|
187
|
-
##
|
|
166
|
+
## Contributing
|
|
188
167
|
|
|
189
|
-
|
|
168
|
+
Contributions welcome ā especially new tool adapters and migration improvements.
|
|
190
169
|
|
|
191
|
-
1. Fork the
|
|
192
|
-
2. Create your
|
|
193
|
-
3. Commit
|
|
194
|
-
4.
|
|
195
|
-
5. Open a Pull Request
|
|
170
|
+
1. Fork the repo
|
|
171
|
+
2. Create your branch (`git checkout -b feature/my-feature`)
|
|
172
|
+
3. Commit and push
|
|
173
|
+
4. Open a PR
|
|
196
174
|
|
|
197
|
-
##
|
|
175
|
+
## License
|
|
198
176
|
|
|
199
|
-
|
|
177
|
+
MIT
|
package/bin/memoir.js
CHANGED
|
@@ -7,11 +7,13 @@ import { initCommand } from '../src/commands/init.js';
|
|
|
7
7
|
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
|
+
import { doctorCommand } from '../src/commands/doctor.js';
|
|
10
11
|
import { viewCommand } from '../src/commands/view.js';
|
|
11
12
|
import { diffCommand } from '../src/commands/diff.js';
|
|
12
13
|
import { migrateCommand } from '../src/commands/migrate.js';
|
|
13
14
|
import { snapshotCommand } from '../src/commands/snapshot.js';
|
|
14
15
|
import { resumeCommand } from '../src/commands/resume.js';
|
|
16
|
+
import { profileListCommand, profileCreateCommand, profileSwitchCommand, profileDeleteCommand } from '../src/commands/profile.js';
|
|
15
17
|
import { createRequire } from 'module';
|
|
16
18
|
|
|
17
19
|
const require = createRequire(import.meta.url);
|
|
@@ -26,11 +28,17 @@ async function checkForUpdate() {
|
|
|
26
28
|
clearTimeout(timeout);
|
|
27
29
|
const data = await res.json();
|
|
28
30
|
const latest = data.version;
|
|
29
|
-
if
|
|
31
|
+
// Only notify if remote is actually newer (not just different)
|
|
32
|
+
const isNewer = (a, b) => {
|
|
33
|
+
const [a1, a2, a3] = a.split('.').map(Number);
|
|
34
|
+
const [b1, b2, b3] = b.split('.').map(Number);
|
|
35
|
+
return a1 > b1 || (a1 === b1 && a2 > b2) || (a1 === b1 && a2 === b2 && a3 > b3);
|
|
36
|
+
};
|
|
37
|
+
if (latest && isNewer(latest, VERSION)) {
|
|
30
38
|
console.log(
|
|
31
39
|
'\n' + boxen(
|
|
32
40
|
chalk.yellow(`Update available: ${VERSION} ā ${chalk.green.bold(latest)}`) + '\n' +
|
|
33
|
-
chalk.gray('Run: ') + chalk.cyan('
|
|
41
|
+
chalk.gray('Run: ') + chalk.cyan('memoir update'),
|
|
34
42
|
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, borderStyle: 'round', borderColor: 'yellow', dimBorder: true }
|
|
35
43
|
)
|
|
36
44
|
);
|
|
@@ -49,8 +57,10 @@ if (process.argv.length <= 2) {
|
|
|
49
57
|
chalk.cyan(' memoir restore ') + chalk.gray('ā restore on a new machine') + '\n' +
|
|
50
58
|
chalk.cyan(' memoir snapshot ') + chalk.gray('ā capture your current session') + '\n' +
|
|
51
59
|
chalk.cyan(' memoir resume ') + chalk.gray('ā pick up where you left off') + '\n' +
|
|
52
|
-
chalk.cyan(' memoir status ') + chalk.gray('ā see detected AI tools') + '\n
|
|
53
|
-
chalk.
|
|
60
|
+
chalk.cyan(' memoir status ') + chalk.gray('ā see detected AI tools') + '\n' +
|
|
61
|
+
chalk.cyan(' memoir profile ') + chalk.gray('ā manage profiles (personal/work)') + '\n' +
|
|
62
|
+
chalk.cyan(' memoir update ') + chalk.gray('ā update to latest version') + '\n\n' +
|
|
63
|
+
chalk.gray(' Tip: use --profile work to sync a specific profile') + '\n\n' +
|
|
54
64
|
chalk.gray(`v${VERSION}`),
|
|
55
65
|
{ padding: 1, borderStyle: 'round', borderColor: 'cyan', dimBorder: true }
|
|
56
66
|
) + '\n');
|
|
@@ -85,7 +95,8 @@ program
|
|
|
85
95
|
.command('push')
|
|
86
96
|
.alias('remember')
|
|
87
97
|
.description('Back up your AI memory to the cloud')
|
|
88
|
-
.option('--only <tools>', 'Only sync specific tools (comma-separated
|
|
98
|
+
.option('--only <tools>', 'Only sync specific tools (comma-separated)')
|
|
99
|
+
.option('-p, --profile <name>', 'Use a specific profile')
|
|
89
100
|
.action(async (options) => {
|
|
90
101
|
try {
|
|
91
102
|
await pushCommand(options);
|
|
@@ -99,8 +110,9 @@ program
|
|
|
99
110
|
.command('restore')
|
|
100
111
|
.alias('pull')
|
|
101
112
|
.description('Restore your AI memory on this machine')
|
|
102
|
-
.option('--only <tools>', 'Only restore specific tools (comma-separated
|
|
113
|
+
.option('--only <tools>', 'Only restore specific tools (comma-separated)')
|
|
103
114
|
.option('-y, --yes', 'Skip confirmation prompts (restore all)')
|
|
115
|
+
.option('-p, --profile <name>', 'Use a specific profile')
|
|
104
116
|
.action(async (options) => {
|
|
105
117
|
try {
|
|
106
118
|
await restoreCommand(options);
|
|
@@ -113,9 +125,24 @@ program
|
|
|
113
125
|
program
|
|
114
126
|
.command('status')
|
|
115
127
|
.description('See what AI tools are on this machine')
|
|
116
|
-
.
|
|
128
|
+
.option('-p, --profile <name>', 'Use a specific profile')
|
|
129
|
+
.action(async (options) => {
|
|
117
130
|
try {
|
|
118
|
-
await statusCommand();
|
|
131
|
+
await statusCommand(options);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.error(chalk.red('\nā Error:'), err.message);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
program
|
|
139
|
+
.command('doctor')
|
|
140
|
+
.alias('diagnose')
|
|
141
|
+
.description('Diagnose common issues with your memoir setup')
|
|
142
|
+
.option('-p, --profile <name>', 'Use a specific profile')
|
|
143
|
+
.action(async (options) => {
|
|
144
|
+
try {
|
|
145
|
+
await doctorCommand(options);
|
|
119
146
|
} catch (err) {
|
|
120
147
|
console.error(chalk.red('\nā Error:'), err.message);
|
|
121
148
|
process.exit(1);
|
|
@@ -126,9 +153,10 @@ program
|
|
|
126
153
|
.command('view')
|
|
127
154
|
.alias('ls')
|
|
128
155
|
.description('Preview what files are in your backup')
|
|
129
|
-
.
|
|
156
|
+
.option('-p, --profile <name>', 'Use a specific profile')
|
|
157
|
+
.action(async (options) => {
|
|
130
158
|
try {
|
|
131
|
-
await viewCommand();
|
|
159
|
+
await viewCommand(options);
|
|
132
160
|
} catch (err) {
|
|
133
161
|
console.error(chalk.red('\nā Error:'), err.message);
|
|
134
162
|
process.exit(1);
|
|
@@ -139,9 +167,10 @@ program
|
|
|
139
167
|
.command('diff')
|
|
140
168
|
.alias('changes')
|
|
141
169
|
.description('Show what changed since your last backup')
|
|
142
|
-
.
|
|
170
|
+
.option('-p, --profile <name>', 'Use a specific profile')
|
|
171
|
+
.action(async (options) => {
|
|
143
172
|
try {
|
|
144
|
-
await diffCommand();
|
|
173
|
+
await diffCommand(options);
|
|
145
174
|
} catch (err) {
|
|
146
175
|
console.error(chalk.red('\nā Error:'), err.message);
|
|
147
176
|
process.exit(1);
|
|
@@ -154,6 +183,7 @@ program
|
|
|
154
183
|
.description('Capture your current coding session for handoff')
|
|
155
184
|
.option('--smart', 'Use AI to generate a better summary (requires Gemini API key)')
|
|
156
185
|
.option('--goal <goal>', 'What you want to do next (goal-directed handoff)')
|
|
186
|
+
.option('-p, --profile <name>', 'Use a specific profile')
|
|
157
187
|
.action(async (options) => {
|
|
158
188
|
try {
|
|
159
189
|
await snapshotCommand(options);
|
|
@@ -168,6 +198,7 @@ program
|
|
|
168
198
|
.description('Pick up where you left off on another machine')
|
|
169
199
|
.option('--inject', 'Write the handoff where your AI tool will read it')
|
|
170
200
|
.option('--to <tool>', 'Target tool for injection (claude, gemini, cursor, codex)')
|
|
201
|
+
.option('-p, --profile <name>', 'Use a specific profile')
|
|
171
202
|
.action(async (options) => {
|
|
172
203
|
try {
|
|
173
204
|
await resumeCommand(options);
|
|
@@ -177,6 +208,46 @@ program
|
|
|
177
208
|
}
|
|
178
209
|
});
|
|
179
210
|
|
|
211
|
+
program
|
|
212
|
+
.command('update')
|
|
213
|
+
.alias('upgrade')
|
|
214
|
+
.description('Update memoir to the latest version')
|
|
215
|
+
.action(async () => {
|
|
216
|
+
try {
|
|
217
|
+
const res = await fetch('https://registry.npmjs.org/memoir-cli/latest');
|
|
218
|
+
const data = await res.json();
|
|
219
|
+
const latest = data.version;
|
|
220
|
+
|
|
221
|
+
if (latest === VERSION) {
|
|
222
|
+
console.log('\n' + boxen(
|
|
223
|
+
chalk.green('ā Already up to date!') + '\n' +
|
|
224
|
+
chalk.gray(`v${VERSION}`),
|
|
225
|
+
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, borderStyle: 'round', borderColor: 'green', dimBorder: true }
|
|
226
|
+
) + '\n');
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
console.log('\n' + chalk.cyan(`Updating memoir ${VERSION} ā ${chalk.green.bold(latest)}...`) + '\n');
|
|
231
|
+
|
|
232
|
+
const { execSync } = await import('child_process');
|
|
233
|
+
const execPath = process.argv[1] || '';
|
|
234
|
+
const useBun = execPath.includes('.bun') || process.env.BUN_INSTALL;
|
|
235
|
+
const cmd = useBun ? 'bun install -g memoir-cli' : 'npm install -g memoir-cli';
|
|
236
|
+
|
|
237
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
238
|
+
|
|
239
|
+
console.log('\n' + boxen(
|
|
240
|
+
gradient.pastel(' Updated! ') + '\n\n' +
|
|
241
|
+
chalk.white(`memoir ${VERSION} ā ${chalk.green.bold(latest)}`),
|
|
242
|
+
{ padding: 1, borderStyle: 'round', borderColor: 'green', dimBorder: true }
|
|
243
|
+
) + '\n');
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.error(chalk.red('\nā Update failed:'), err.message);
|
|
246
|
+
console.log(chalk.gray('Try manually: ') + chalk.cyan('npm install -g memoir-cli'));
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
180
251
|
program
|
|
181
252
|
.command('migrate')
|
|
182
253
|
.description('Translate memory between AI tools (Claude, Gemini, Codex, Cursor, etc.)')
|
|
@@ -192,6 +263,60 @@ program
|
|
|
192
263
|
}
|
|
193
264
|
});
|
|
194
265
|
|
|
266
|
+
// Profile management
|
|
267
|
+
const profile = program.command('profile').description('Manage profiles (personal, work, etc.)');
|
|
268
|
+
|
|
269
|
+
profile
|
|
270
|
+
.command('list')
|
|
271
|
+
.alias('ls')
|
|
272
|
+
.description('List all profiles')
|
|
273
|
+
.action(async () => {
|
|
274
|
+
try {
|
|
275
|
+
await profileListCommand();
|
|
276
|
+
} catch (err) {
|
|
277
|
+
console.error(chalk.red('\nā Error:'), err.message);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
profile
|
|
283
|
+
.command('create <name>')
|
|
284
|
+
.description('Create a new profile')
|
|
285
|
+
.action(async (name) => {
|
|
286
|
+
try {
|
|
287
|
+
await profileCreateCommand(name);
|
|
288
|
+
} catch (err) {
|
|
289
|
+
console.error(chalk.red('\nā Error:'), err.message);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
profile
|
|
295
|
+
.command('switch <name>')
|
|
296
|
+
.alias('use')
|
|
297
|
+
.description('Switch to a profile')
|
|
298
|
+
.action(async (name) => {
|
|
299
|
+
try {
|
|
300
|
+
await profileSwitchCommand(name);
|
|
301
|
+
} catch (err) {
|
|
302
|
+
console.error(chalk.red('\nā Error:'), err.message);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
profile
|
|
308
|
+
.command('delete <name>')
|
|
309
|
+
.alias('rm')
|
|
310
|
+
.description('Delete a profile')
|
|
311
|
+
.action(async (name) => {
|
|
312
|
+
try {
|
|
313
|
+
await profileDeleteCommand(name);
|
|
314
|
+
} catch (err) {
|
|
315
|
+
console.error(chalk.red('\nā Error:'), err.message);
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
195
320
|
program.hook('postAction', async () => {
|
|
196
321
|
await checkForUpdate();
|
|
197
322
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memoir-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Sync AI memory across devices. Back up and restore Claude, Gemini, Codex, Cursor, Copilot, Windsurf configs. Snapshot coding sessions and resume on another machine. Migrate instructions between AI assistants.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
"copilot",
|
|
36
36
|
"windsurf",
|
|
37
37
|
"aider",
|
|
38
|
+
"zed",
|
|
39
|
+
"cline",
|
|
40
|
+
"continue-dev",
|
|
41
|
+
"profiles",
|
|
38
42
|
"ai-memory",
|
|
39
43
|
"ai-tools",
|
|
40
44
|
"dotfiles",
|
|
@@ -42,6 +46,7 @@
|
|
|
42
46
|
"claude-code",
|
|
43
47
|
"gemini-cli",
|
|
44
48
|
"openai",
|
|
49
|
+
"chatgpt",
|
|
45
50
|
"ai-assistant",
|
|
46
51
|
"coding-assistant",
|
|
47
52
|
"context-sync",
|
|
@@ -58,7 +63,6 @@
|
|
|
58
63
|
"fs-extra": "^11.2.0",
|
|
59
64
|
"gradient-string": "^3.0.0",
|
|
60
65
|
"inquirer": "^9.2.15",
|
|
61
|
-
|
|
62
|
-
"ora": "^7.0.1"
|
|
66
|
+
"ora": "^7.0.1"
|
|
63
67
|
}
|
|
64
68
|
}
|
package/src/adapters/index.js
CHANGED
|
@@ -127,6 +127,80 @@ export const adapters = [
|
|
|
127
127
|
return false;
|
|
128
128
|
}
|
|
129
129
|
},
|
|
130
|
+
{
|
|
131
|
+
name: 'Zed',
|
|
132
|
+
icon: 'š¶',
|
|
133
|
+
source: isWin
|
|
134
|
+
? path.join(appData, 'Zed')
|
|
135
|
+
: path.join(home, '.config', 'zed'),
|
|
136
|
+
filter: (src) => {
|
|
137
|
+
const zedDir = isWin
|
|
138
|
+
? path.join(appData, 'Zed')
|
|
139
|
+
: path.join(home, '.config', 'zed');
|
|
140
|
+
const rel = path.relative(zedDir, src);
|
|
141
|
+
if (src === zedDir) return true;
|
|
142
|
+
const basename = path.basename(src);
|
|
143
|
+
// Skip known heavy/non-config directories
|
|
144
|
+
const skipDirs = ['extensions', 'themes', 'logs', 'db', 'copilot', 'node', 'languages'];
|
|
145
|
+
const topDir = rel.split(path.sep)[0];
|
|
146
|
+
if (skipDirs.includes(topDir)) return false;
|
|
147
|
+
// Only sync specific config files in root
|
|
148
|
+
const allowed = ['settings.json', 'keymap.json', 'tasks.json'];
|
|
149
|
+
if (allowed.includes(basename) && !rel.includes(path.sep)) return true;
|
|
150
|
+
// Allow .md files in root
|
|
151
|
+
if (basename.endsWith('.md') && !rel.includes(path.sep)) return true;
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'Cline',
|
|
157
|
+
icon: 'š¤',
|
|
158
|
+
source: isWin
|
|
159
|
+
? path.join(appData, 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev')
|
|
160
|
+
: path.join(home, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev'),
|
|
161
|
+
filter: (src) => {
|
|
162
|
+
const clineDir = isWin
|
|
163
|
+
? path.join(appData, 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev')
|
|
164
|
+
: path.join(home, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev');
|
|
165
|
+
const rel = path.relative(clineDir, src);
|
|
166
|
+
if (src === clineDir) return true;
|
|
167
|
+
const basename = path.basename(src);
|
|
168
|
+
const topDir = rel.split(path.sep)[0];
|
|
169
|
+
// Skip known heavy/non-config directories
|
|
170
|
+
const skipDirs = ['tasks', 'checkpoints', '.cache', 'images'];
|
|
171
|
+
if (skipDirs.includes(topDir)) return false;
|
|
172
|
+
// Allow settings/ and rules/ directories
|
|
173
|
+
if (topDir === 'settings' || topDir === 'rules') return true;
|
|
174
|
+
// Allow .md files in root
|
|
175
|
+
if (basename.endsWith('.md') && !rel.includes(path.sep)) return true;
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'Continue.dev',
|
|
181
|
+
icon: 'š',
|
|
182
|
+
source: isWin
|
|
183
|
+
? path.join(process.env.USERPROFILE || home, '.continue')
|
|
184
|
+
: path.join(home, '.continue'),
|
|
185
|
+
filter: (src) => {
|
|
186
|
+
const continueDir = isWin
|
|
187
|
+
? path.join(process.env.USERPROFILE || home, '.continue')
|
|
188
|
+
: path.join(home, '.continue');
|
|
189
|
+
const rel = path.relative(continueDir, src);
|
|
190
|
+
if (src === continueDir) return true;
|
|
191
|
+
const basename = path.basename(src);
|
|
192
|
+
// Skip known heavy/non-config directories
|
|
193
|
+
const skipDirs = ['sessions', 'dev_data', 'logs', 'index', 'cache', 'types'];
|
|
194
|
+
const topDir = rel.split(path.sep)[0];
|
|
195
|
+
if (skipDirs.includes(topDir)) return false;
|
|
196
|
+
// Only sync specific config files in root
|
|
197
|
+
const allowed = ['config.json', 'config.ts', 'config.yaml', '.continuerules'];
|
|
198
|
+
if (allowed.includes(basename) && !rel.includes(path.sep)) return true;
|
|
199
|
+
// Allow .md files in root
|
|
200
|
+
if (basename.endsWith('.md') && !rel.includes(path.sep)) return true;
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
130
204
|
{
|
|
131
205
|
name: 'Aider',
|
|
132
206
|
icon: 'š§',
|
|
@@ -224,7 +298,7 @@ export async function extractMemories(stagingDir, spinner, onlyFilter = null) {
|
|
|
224
298
|
spinner.text = `š Scanning for project-level AI configs...`;
|
|
225
299
|
|
|
226
300
|
const projectFiles = [
|
|
227
|
-
'CLAUDE.md', 'GEMINI.md', 'AGENTS.md', '.cursorrules',
|
|
301
|
+
'CLAUDE.md', 'GEMINI.md', 'CHATGPT.md', 'AGENTS.md', '.cursorrules',
|
|
228
302
|
'.github/copilot-instructions.md', '.windsurfrules',
|
|
229
303
|
'.aider.conf.yml', '.clinerules'
|
|
230
304
|
];
|
package/src/commands/diff.js
CHANGED
|
@@ -50,8 +50,8 @@ function simpleDiff(oldText, newText) {
|
|
|
50
50
|
return output;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export async function diffCommand() {
|
|
54
|
-
const config = await getConfig();
|
|
53
|
+
export async function diffCommand(options = {}) {
|
|
54
|
+
const config = await getConfig(options.profile);
|
|
55
55
|
if (!config) {
|
|
56
56
|
console.log(chalk.red('\nā Not configured yet. Run: memoir init\n'));
|
|
57
57
|
return;
|