memoir-cli 3.4.0 → 3.5.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/bin/memoir.js CHANGED
@@ -53,41 +53,18 @@ async function checkForUpdate() {
53
53
  } catch {}
54
54
  }
55
55
 
56
- // Show quick start when run with no args
56
+ // When run with no args: auto-push (zero-config)
57
57
  if (process.argv.length <= 2) {
58
- console.log('\n' + boxen(
59
- gradient.pastel.multiline(' memoir ') + '\n' +
60
- chalk.gray(' Your AI remembers everything.') + '\n\n' +
61
- chalk.white.bold('Quick Start:') + '\n' +
62
- chalk.cyan(' memoir init ') + chalk.gray('— first-time setup') + '\n' +
63
- chalk.cyan(' memoir push ') + chalk.gray('— back up your AI memory') + '\n' +
64
- chalk.cyan(' memoir restore ') + chalk.gray('— restore on a new machine') + '\n' +
65
- chalk.cyan(' memoir snapshot ') + chalk.gray('— capture your current session') + '\n' +
66
- chalk.cyan(' memoir resume ') + chalk.gray('— pick up where you left off') + '\n' +
67
- chalk.cyan(' memoir status ') + chalk.gray('— see detected AI tools') + '\n' +
68
- chalk.cyan(' memoir profile ') + chalk.gray('— manage profiles (personal/work)') + '\n' +
69
- chalk.cyan(' memoir projects ') + chalk.gray('— see all your projects at a glance') + '\n' +
70
- chalk.cyan(' memoir activate ') + chalk.gray('— enable auto-recall in this project') + '\n' +
71
- chalk.cyan(' memoir encrypt ') + chalk.gray('— toggle E2E encryption') + '\n' +
72
- chalk.cyan(' memoir update ') + chalk.gray('— update to latest version') + '\n' +
73
- chalk.cyan(' memoir upgrade ') + chalk.gray('— view plans & upgrade') + '\n\n' +
74
- chalk.white.bold('Cloud (Pro):') + '\n' +
75
- chalk.cyan(' memoir login ') + chalk.gray('— sign in to memoir cloud') + '\n' +
76
- chalk.cyan(' memoir cloud push ') + chalk.gray('— back up to the cloud') + '\n' +
77
- chalk.cyan(' memoir cloud restore ') + chalk.gray('— restore from cloud') + '\n' +
78
- chalk.cyan(' memoir share ') + chalk.gray('— share memory via secure link') + '\n' +
79
- chalk.cyan(' memoir history ') + chalk.gray('— view backup versions') + '\n\n' +
80
- chalk.gray(' Tip: use --profile work to sync a specific profile') + '\n\n' +
81
- chalk.gray(`v${VERSION}`),
82
- { padding: 1, borderStyle: 'round', borderColor: 'cyan', dimBorder: true }
83
- ) + '\n');
84
- process.exit(0);
58
+ // Pass 'push' as the command so Commander routes to pushCommand
59
+ process.argv.push('push');
85
60
  }
86
61
 
87
62
  // Custom help banner
88
63
  program.addHelpText('beforeAll', '\n' + boxen(
89
64
  gradient.pastel.multiline(' memoir ') + '\n' +
90
- chalk.gray(' Your AI remembers everything.'),
65
+ chalk.gray(' Your AI remembers everything.') + '\n\n' +
66
+ chalk.white.bold('Zero-config:') + ' just run ' + chalk.cyan('memoir') + ' or ' + chalk.cyan('npx memoir-cli') + '\n' +
67
+ chalk.gray('Auto-detects your GitHub, creates a private repo, and backs up.'),
91
68
  { padding: { top: 0, bottom: 0, left: 1, right: 1 }, borderStyle: 'round', borderColor: 'cyan', dimBorder: true }
92
69
  ) + '\n');
93
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memoir-cli",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "mcpName": "io.github.camgitt/memoir",
5
5
  "description": "MCP server that gives Claude, Cursor, and Gemini long-term memory across sessions. Your AI remembers your codebase, decisions, and preferences — across tools and machines.",
6
6
  "main": "src/index.js",
@@ -5,7 +5,7 @@ import os from 'os';
5
5
  import ora from 'ora';
6
6
  import boxen from 'boxen';
7
7
  import gradient from 'gradient-string';
8
- import { getConfig } from '../config.js';
8
+ import { getConfig, autoSetup } from '../config.js';
9
9
  import { extractMemories, adapters } from '../adapters/index.js';
10
10
  import { syncToLocal, syncToGit } from '../providers/index.js';
11
11
  import inquirer from 'inquirer';
@@ -17,15 +17,22 @@ import { scanWorkspace } from '../workspace/tracker.js';
17
17
  import { promptActivate } from './activate.js';
18
18
 
19
19
  export async function pushCommand(options = {}) {
20
- const config = await getConfig(options.profile);
20
+ let config = await getConfig(options.profile);
21
21
 
22
22
  if (!config) {
23
- console.log('\n' + boxen(
24
- chalk.red(' Not configured yet\n\n') +
25
- chalk.white('Run ') + chalk.cyan.bold('memoir init') + chalk.white(' to get started.'),
26
- { padding: 1, borderStyle: 'round', borderColor: 'red' }
27
- ) + '\n');
28
- return;
23
+ // Zero-config: auto-detect GitHub user, create repo, save config
24
+ const setupSpinner = ora({ text: chalk.gray('Setting up memoir automatically...'), spinner: 'dots' }).start();
25
+ config = await autoSetup();
26
+ if (config) {
27
+ setupSpinner.succeed(chalk.green('Auto-configured') + chalk.gray(` → ${config.gitRepo}`));
28
+ } else {
29
+ setupSpinner.fail(chalk.red('Could not detect GitHub username'));
30
+ console.log('\n' + boxen(
31
+ chalk.white('Run ') + chalk.cyan.bold('memoir init') + chalk.white(' to set up manually.'),
32
+ { padding: 1, borderStyle: 'round', borderColor: 'yellow' }
33
+ ) + '\n');
34
+ return;
35
+ }
29
36
  }
30
37
 
31
38
  console.log();
@@ -6,7 +6,7 @@ import ora from 'ora';
6
6
  import boxen from 'boxen';
7
7
  import gradient from 'gradient-string';
8
8
  import inquirer from 'inquirer';
9
- import { getConfig } from '../config.js';
9
+ import { getConfig, autoSetup } from '../config.js';
10
10
  import { fetchFromLocal, fetchFromGit } from '../providers/restore.js';
11
11
  import { decryptDirectory, verifyPassphrase } from '../security/encryption.js';
12
12
  import { detectLocalHomeKey } from '../adapters/restore.js';
@@ -23,15 +23,21 @@ export async function restoreCommand(options = {}) {
23
23
  return restoreFromShare(options);
24
24
  }
25
25
 
26
- const config = await getConfig(options.profile);
26
+ let config = await getConfig(options.profile);
27
27
 
28
28
  if (!config) {
29
- console.log('\n' + boxen(
30
- chalk.red('✖ Not configured yet\n\n') +
31
- chalk.white('Run ') + chalk.cyan.bold('memoir init') + chalk.white(' to get started.'),
32
- { padding: 1, borderStyle: 'round', borderColor: 'red' }
33
- ) + '\n');
34
- return;
29
+ const setupSpinner = ora({ text: chalk.gray('Setting up memoir automatically...'), spinner: 'dots' }).start();
30
+ config = await autoSetup();
31
+ if (config) {
32
+ setupSpinner.succeed(chalk.green('Auto-configured') + chalk.gray(` → ${config.gitRepo}`));
33
+ } else {
34
+ setupSpinner.fail(chalk.red('Could not detect GitHub username'));
35
+ console.log('\n' + boxen(
36
+ chalk.white('Run ') + chalk.cyan.bold('memoir init') + chalk.white(' to set up manually.'),
37
+ { padding: 1, borderStyle: 'round', borderColor: 'yellow' }
38
+ ) + '\n');
39
+ return;
40
+ }
35
41
  }
36
42
 
37
43
  console.log();
package/src/config.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import { execFileSync } from 'child_process';
4
5
 
5
6
  const CONFIG_DIR = process.platform === 'win32'
6
7
  ? path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'memoir')
@@ -114,6 +115,50 @@ export async function deleteProfile(name) {
114
115
  await saveConfig(raw);
115
116
  }
116
117
 
118
+ // Zero-config auto-setup: detect GitHub user, create repo, save config, return it
119
+ export async function autoSetup() {
120
+ // Try gh CLI first, then git config
121
+ let username = '';
122
+ try {
123
+ username = execFileSync('gh', ['api', 'user', '--jq', '.login'], { encoding: 'utf8', timeout: 5000 }).trim();
124
+ } catch {
125
+ try {
126
+ username = execFileSync('git', ['config', '--global', 'user.name'], { encoding: 'utf8' }).trim();
127
+ } catch {}
128
+ }
129
+
130
+ if (!username) return null; // Can't auto-setup without a username
131
+
132
+ const repo = 'ai-memory';
133
+ const gitRepo = `https://github.com/${username}/${repo}.git`;
134
+
135
+ // Try to create the repo if it doesn't exist (best-effort)
136
+ try {
137
+ execFileSync('gh', ['repo', 'view', `${username}/${repo}`], { stdio: 'ignore', timeout: 5000 });
138
+ } catch {
139
+ try {
140
+ execFileSync('gh', ['repo', 'create', `${username}/${repo}`, '--private', '--description', 'AI memory backup (memoir-cli)'], { stdio: 'ignore', timeout: 10000 });
141
+ } catch {
142
+ // If gh isn't available, user will need to create repo manually — that's fine, syncToGit will handle it
143
+ }
144
+ }
145
+
146
+ const config = {
147
+ version: 2,
148
+ activeProfile: 'default',
149
+ profiles: {
150
+ default: {
151
+ provider: 'git',
152
+ gitRepo,
153
+ encrypt: false // Skip encryption for zero-config — user can enable later with `memoir encrypt`
154
+ }
155
+ }
156
+ };
157
+
158
+ await saveConfig(config);
159
+ return config.profiles.default;
160
+ }
161
+
117
162
  export async function getGeminiApiKey() {
118
163
  const raw = await getRawConfig();
119
164
  return raw?.geminiApiKey || process.env.GEMINI_API_KEY || null;