@trlc/super-memory 1.0.0 → 1.1.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.
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Dashboard Command
3
+ * Starts a local web server with the Super Memory dashboard
4
+ */
5
+ import { spawn, execSync } from 'child_process';
6
+ import { existsSync, readFileSync } from 'fs';
7
+ import { join, dirname } from 'path';
8
+ import { homedir } from 'os';
9
+ import { fileURLToPath } from 'url';
10
+ import net from 'net';
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ // Get dashboard directory (relative to compiled output)
14
+ function getDashboardDir() {
15
+ // When running from dist/, dashboard is at ../dashboard
16
+ const fromDist = join(__dirname, '..', '..', 'dashboard');
17
+ if (existsSync(fromDist)) {
18
+ return fromDist;
19
+ }
20
+ // When running from src/, dashboard is at ../../dashboard
21
+ const fromSrc = join(__dirname, '..', '..', 'dashboard');
22
+ if (existsSync(fromSrc)) {
23
+ return fromSrc;
24
+ }
25
+ throw new Error('Dashboard directory not found');
26
+ }
27
+ // Get config
28
+ function loadConfig() {
29
+ const configPath = join(homedir(), '.openclaw', 'workspace', '.super-memory', 'config.json');
30
+ if (!existsSync(configPath)) {
31
+ return null;
32
+ }
33
+ try {
34
+ return JSON.parse(readFileSync(configPath, 'utf-8'));
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ // Check if a port is available
41
+ async function isPortAvailable(port) {
42
+ return new Promise((resolve) => {
43
+ const server = net.createServer();
44
+ server.once('error', () => resolve(false));
45
+ server.once('listening', () => {
46
+ server.close(() => resolve(true));
47
+ });
48
+ server.listen(port, '127.0.0.1');
49
+ });
50
+ }
51
+ // Find an available port
52
+ async function findAvailablePort(startPort) {
53
+ for (let port = startPort; port < startPort + 100; port++) {
54
+ if (await isPortAvailable(port)) {
55
+ return port;
56
+ }
57
+ }
58
+ return startPort; // fallback
59
+ }
60
+ // Open browser
61
+ function openBrowser(url) {
62
+ try {
63
+ const platform = process.platform;
64
+ let command;
65
+ if (platform === 'darwin') {
66
+ command = `open "${url}"`;
67
+ }
68
+ else if (platform === 'win32') {
69
+ command = `start "" "${url}"`;
70
+ }
71
+ else {
72
+ // Linux - try various commands
73
+ const browsers = ['xdg-open', 'sensible-browser', 'x-www-browser', 'gnome-open'];
74
+ for (const browser of browsers) {
75
+ try {
76
+ execSync(`which ${browser}`, { stdio: 'ignore' });
77
+ command = `${browser} "${url}"`;
78
+ break;
79
+ }
80
+ catch {
81
+ continue;
82
+ }
83
+ }
84
+ if (!command) {
85
+ console.log(`💡 Open your browser to: ${url}`);
86
+ return;
87
+ }
88
+ }
89
+ execSync(command, { stdio: 'ignore' });
90
+ }
91
+ catch {
92
+ console.log(`💡 Open your browser to: ${url}`);
93
+ }
94
+ }
95
+ export async function startDashboard(options = {}) {
96
+ const config = loadConfig();
97
+ console.log(`
98
+ 🦞 ============================================
99
+ SUPER MEMORY DASHBOARD
100
+ The Red Lobster Cartel
101
+ ============================================
102
+ `);
103
+ if (!config) {
104
+ console.log('⚠️ Not initialized yet. The dashboard will show setup instructions.');
105
+ console.log(' Run: super-memory init --license=YOUR_KEY\n');
106
+ }
107
+ const dashboardDir = getDashboardDir();
108
+ const packageJsonPath = join(dashboardDir, 'package.json');
109
+ const nodeModulesPath = join(dashboardDir, 'node_modules');
110
+ // Check if dependencies are installed
111
+ if (!existsSync(nodeModulesPath)) {
112
+ console.log('📦 Installing dashboard dependencies...');
113
+ try {
114
+ execSync('npm install', {
115
+ cwd: dashboardDir,
116
+ stdio: 'inherit',
117
+ });
118
+ console.log('✅ Dependencies installed\n');
119
+ }
120
+ catch (error) {
121
+ console.error('❌ Failed to install dependencies');
122
+ process.exit(1);
123
+ }
124
+ }
125
+ // Find available port
126
+ const defaultPort = options.port || 8765;
127
+ const port = await findAvailablePort(defaultPort);
128
+ if (port !== defaultPort) {
129
+ console.log(`⚠️ Port ${defaultPort} is in use, using ${port} instead\n`);
130
+ }
131
+ // Start the server
132
+ console.log('🚀 Starting dashboard server...\n');
133
+ const serverPath = join(dashboardDir, 'server.js');
134
+ const server = spawn('node', [serverPath], {
135
+ cwd: dashboardDir,
136
+ env: {
137
+ ...process.env,
138
+ SUPER_MEMORY_PORT: port.toString(),
139
+ },
140
+ stdio: 'inherit',
141
+ });
142
+ // Wait a bit for server to start
143
+ await new Promise(resolve => setTimeout(resolve, 1000));
144
+ const url = `http://localhost:${port}`;
145
+ // Open browser
146
+ if (!options.noBrowser) {
147
+ console.log(`🌐 Opening browser to ${url}\n`);
148
+ openBrowser(url);
149
+ }
150
+ // Handle server exit
151
+ server.on('close', (code) => {
152
+ if (code !== 0) {
153
+ console.error(`\n❌ Dashboard server exited with code ${code}`);
154
+ process.exit(code || 1);
155
+ }
156
+ });
157
+ // Handle SIGINT
158
+ process.on('SIGINT', () => {
159
+ console.log('\n\n👋 Shutting down dashboard...');
160
+ server.kill();
161
+ process.exit(0);
162
+ });
163
+ // Keep process alive
164
+ await new Promise(() => { });
165
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Flush Command
3
+ * Saves critical context before session compaction
4
+ */
5
+ import { loadConfig, getConvexUrl } from '../utils/config.js';
6
+ export async function cmdFlush(args) {
7
+ const config = loadConfig();
8
+ if (!config) {
9
+ console.error('❌ Not initialized. Run: super-memory init --license=YOUR_KEY');
10
+ process.exit(1);
11
+ }
12
+ // Get context from args or prompt
13
+ let context = args.find(arg => !arg.startsWith('--'));
14
+ let title = args.find(arg => arg.startsWith('--title='))?.split('=')[1];
15
+ if (!context) {
16
+ const readline = await import('readline');
17
+ const rl = readline.createInterface({
18
+ input: process.stdin,
19
+ output: process.stdout
20
+ });
21
+ context = await new Promise((resolve) => {
22
+ rl.question('📝 Enter context to flush (critical info to save): ', (answer) => {
23
+ rl.close();
24
+ resolve(answer.trim());
25
+ });
26
+ });
27
+ }
28
+ if (!context) {
29
+ console.error('❌ No context provided');
30
+ process.exit(1);
31
+ }
32
+ console.log('\n🚨 Flushing critical context...\n');
33
+ try {
34
+ const response = await fetch(`${getConvexUrl()}/api/mutation`, {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
+ body: JSON.stringify({
38
+ path: 'sync:flush',
39
+ args: {
40
+ licenseKey: config.licenseKey,
41
+ context,
42
+ title,
43
+ },
44
+ }),
45
+ });
46
+ if (!response.ok) {
47
+ throw new Error(`API error: ${response.status}`);
48
+ }
49
+ const result = await response.json();
50
+ if (result.status === 'error') {
51
+ throw new Error(result.errorMessage || 'Unknown error');
52
+ }
53
+ const data = result.value;
54
+ if (data.success) {
55
+ console.log('✅ Context flushed successfully!\n');
56
+ console.log(` Memory ID: ${data.memoryId}`);
57
+ console.log(` Title: ${title || 'Flushed Context'}`);
58
+ console.log('\n💡 This context is now safely stored in Layer 1 (Session)');
59
+ console.log(' and will be preserved even if your session context overflows.\n');
60
+ }
61
+ else {
62
+ console.error('❌ Failed to flush context');
63
+ process.exit(1);
64
+ }
65
+ }
66
+ catch (error) {
67
+ console.error(`❌ Error: ${error.message}`);
68
+ process.exit(1);
69
+ }
70
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Index Update Command
3
+ * Creates progressive memory index (~70% token savings)
4
+ * Now includes AUTO-CURATE automatically!
5
+ */
6
+ import { loadConfig, getConvexUrl } from '../utils/config.js';
7
+ import { cmdCurate } from './curate.js';
8
+ export async function cmdIndexUpdate(args = []) {
9
+ const config = loadConfig();
10
+ if (!config) {
11
+ console.error('❌ Not initialized. Run: super-memory init --license=YOUR_KEY');
12
+ process.exit(1);
13
+ }
14
+ const skipCurate = args.includes('--skip-curate');
15
+ console.log('\n📚 Updating memory index...\n');
16
+ try {
17
+ const response = await fetch(`${getConvexUrl()}/api/mutation`, {
18
+ method: 'POST',
19
+ headers: { 'Content-Type': 'application/json' },
20
+ body: JSON.stringify({
21
+ path: 'sync:indexUpdate',
22
+ args: { licenseKey: config.licenseKey },
23
+ }),
24
+ });
25
+ if (!response.ok) {
26
+ throw new Error(`API error: ${response.status}`);
27
+ }
28
+ const result = await response.json();
29
+ if (result.status === 'error') {
30
+ throw new Error(result.errorMessage || 'Unknown error');
31
+ }
32
+ const data = result.value;
33
+ if (data.success) {
34
+ console.log('✅ Index updated successfully!\n');
35
+ console.log(` Entries indexed: ${data.entriesAdded}`);
36
+ console.log(` Index size: ${data.indexSize.toLocaleString()} chars`);
37
+ console.log(` Estimated savings: ${data.estimatedSavings}`);
38
+ console.log('\n💡 The progressive index provides ~70% token savings');
39
+ console.log(' when searching and reviewing your memories.\n');
40
+ // AUTO-CURATE: Run automatically after index update
41
+ if (!skipCurate) {
42
+ console.log('────────────────────────────────────────');
43
+ console.log('🧠 Running AUTO-CURATE...\n');
44
+ await cmdCurate(['--silent']);
45
+ console.log('✅ Auto-curate completed! MEMORY.md updated.\n');
46
+ }
47
+ }
48
+ else {
49
+ console.error('❌ Failed to update index');
50
+ process.exit(1);
51
+ }
52
+ }
53
+ catch (error) {
54
+ console.error(`❌ Error: ${error.message}`);
55
+ process.exit(1);
56
+ }
57
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Maintenance Command
3
+ * Weekly review with smart suggestions
4
+ */
5
+ import { loadConfig, getConvexUrl } from '../utils/config.js';
6
+ export async function cmdMaintenance() {
7
+ const config = loadConfig();
8
+ if (!config) {
9
+ console.error('❌ Not initialized. Run: super-memory init --license=YOUR_KEY');
10
+ process.exit(1);
11
+ }
12
+ console.log('\n🔧 Running maintenance check...\n');
13
+ try {
14
+ const response = await fetch(`${getConvexUrl()}/api/query`, {
15
+ method: 'POST',
16
+ headers: { 'Content-Type': 'application/json' },
17
+ body: JSON.stringify({
18
+ path: 'dashboard:getMaintenance',
19
+ args: { licenseKey: config.licenseKey },
20
+ }),
21
+ });
22
+ if (!response.ok) {
23
+ throw new Error(`API error: ${response.status}`);
24
+ }
25
+ const result = await response.json();
26
+ if (result.status === 'error') {
27
+ throw new Error(result.errorMessage || 'Unknown error');
28
+ }
29
+ const data = result.value;
30
+ if (data.success) {
31
+ console.log('📊 Memory Statistics');
32
+ console.log('===================');
33
+ console.log(`Total Memories: ${data.stats.totalMemories}`);
34
+ console.log(`Average per Day: ${data.stats.avgMemoriesPerDay}`);
35
+ console.log(`Oldest Memory: ${data.stats.oldestMemoryDays} days old`);
36
+ console.log('');
37
+ console.log('Category Balance:');
38
+ console.log(` 🔴 Gotchas: ${data.stats.categoryBalance.gotcha}`);
39
+ console.log(` 🟡 Problem-Fix: ${data.stats.categoryBalance.problem}`);
40
+ console.log(` 🟤 Decisions: ${data.stats.categoryBalance.decision}`);
41
+ console.log(` 🟣 Discoveries: ${data.stats.categoryBalance.discovery}`);
42
+ console.log('');
43
+ if (data.suggestions.length === 0) {
44
+ console.log('✅ No maintenance suggestions - your memory is healthy!\n');
45
+ }
46
+ else {
47
+ console.log('🔍 Suggestions');
48
+ console.log('===============');
49
+ for (const suggestion of data.suggestions) {
50
+ const priorityEmoji = suggestion.priority === 'high' ? '🔴' :
51
+ suggestion.priority === 'medium' ? '🟡' : '🟢';
52
+ console.log(`${priorityEmoji} ${suggestion.title}`);
53
+ console.log(` ${suggestion.description}`);
54
+ console.log(` Action: ${suggestion.action}`);
55
+ console.log('');
56
+ }
57
+ }
58
+ }
59
+ else {
60
+ console.error('❌ Failed to get maintenance suggestions');
61
+ process.exit(1);
62
+ }
63
+ }
64
+ catch (error) {
65
+ console.error(`❌ Error: ${error.message}`);
66
+ process.exit(1);
67
+ }
68
+ }
package/dist/index.js CHANGED
@@ -9,8 +9,16 @@
9
9
  * super-memory search "query term"
10
10
  * super-memory sync
11
11
  * super-memory status
12
+ * super-memory dashboard [--port=8765] [--no-browser]
13
+ * super-memory curate [--days=7] [--dry-run] ← NEW!
12
14
  */
13
15
  import { execSync } from 'child_process';
16
+ import { startDashboard } from './commands/dashboard.js';
17
+ import { cmdIndexUpdate } from './commands/indexUpdate.js';
18
+ import { cmdFlush } from './commands/flush.js';
19
+ import { cmdMaintenance } from './commands/maintenance.js';
20
+ import { cmdCategorize } from './commands/categorize.js';
21
+ import { cmdCurate } from './commands/curate.js';
14
22
  import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'fs';
15
23
  import { homedir } from 'os';
16
24
  import { join } from 'path';
@@ -193,6 +201,45 @@ async function cmdInit(args) {
193
201
 
194
202
  ---
195
203
 
204
+ *Powered by The Red Lobster Cartel 🦞*
205
+ `);
206
+ }
207
+ // Create MEMORY.md (Long-Term Memory) - Layer 3
208
+ const baseDir = getBaseDir();
209
+ const longTermPath = join(baseDir, 'MEMORY.md');
210
+ if (!existsSync(longTermPath)) {
211
+ writeFileSync(longTermPath, `# 🧠 Long-Term Memory
212
+
213
+ **Installed:** ${today}
214
+ **Plan:** ${validation.plan}
215
+ **System:** Super Memory 5-Layer Architecture
216
+
217
+ ---
218
+
219
+ ## 👤 User Identity
220
+
221
+ *Add your personal information, preferences, and context here.*
222
+
223
+ ---
224
+
225
+ ## 🎯 Current Projects
226
+
227
+ *Track your active projects and their status.*
228
+
229
+ ---
230
+
231
+ ## 🔴 Gotchas & Learnings
232
+
233
+ *Important lessons, pitfalls, and discoveries.*
234
+
235
+ ---
236
+
237
+ ## 📊 System Status
238
+
239
+ **Last Updated:** ${today}
240
+
241
+ ---
242
+
196
243
  *Powered by The Red Lobster Cartel 🦞*
197
244
  `);
198
245
  }
@@ -390,6 +437,23 @@ COMMANDS:
390
437
 
391
438
  status Show status and stats
392
439
 
440
+ index-update Update progressive MEMORY_INDEX.md (Layer 4)
441
+ Also auto-curates to MEMORY.md
442
+
443
+ curate Auto-curate important entries to MEMORY.md (Layer 3)
444
+ Extracts gotchas, problems, decisions, discoveries
445
+ from recent daily logs automatically
446
+
447
+ flush <context> Save critical context before compaction
448
+
449
+ maintenance Run weekly review and get suggestions
450
+
451
+ categorize <id> Auto-categorize memory entry
452
+
453
+ dashboard Open web dashboard
454
+ --port=8765 Custom port
455
+ --no-browser Don't auto-open browser
456
+
393
457
  help Show this help
394
458
 
395
459
  EXAMPLES:
@@ -398,11 +462,22 @@ EXAMPLES:
398
462
  super-memory search "cors error"
399
463
  super-memory sync
400
464
  super-memory status
465
+ super-memory dashboard
466
+ super-memory dashboard --port=3000 --no-browser
401
467
 
402
468
  WEBSITE: https://theredlobstercartel.com/super-memory
403
469
  SUPPORT: support@theredlobstercartel.com
404
470
  `);
405
471
  }
472
+ // COMMAND: dashboard
473
+ async function cmdDashboard(args) {
474
+ const portArg = args.find(arg => arg.startsWith('--port='))?.split('=')[1];
475
+ const noBrowser = args.includes('--no-browser');
476
+ await startDashboard({
477
+ port: portArg ? parseInt(portArg, 10) : undefined,
478
+ noBrowser,
479
+ });
480
+ }
406
481
  // Main
407
482
  async function main() {
408
483
  const args = process.argv.slice(2);
@@ -424,6 +499,24 @@ async function main() {
424
499
  case 'status':
425
500
  await cmdStatus(cmdArgs);
426
501
  break;
502
+ case 'dashboard':
503
+ await cmdDashboard(cmdArgs);
504
+ break;
505
+ case 'index-update':
506
+ await cmdIndexUpdate(cmdArgs);
507
+ break;
508
+ case 'curate':
509
+ await cmdCurate(cmdArgs);
510
+ break;
511
+ case 'flush':
512
+ await cmdFlush(cmdArgs);
513
+ break;
514
+ case 'maintenance':
515
+ await cmdMaintenance();
516
+ break;
517
+ case 'categorize':
518
+ await cmdCategorize(cmdArgs);
519
+ break;
427
520
  case 'help':
428
521
  case '--help':
429
522
  case '-h':
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Config utilities
3
+ */
4
+ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
5
+ import { homedir } from 'os';
6
+ import { join } from 'path';
7
+ const CONVEX_URL = 'https://clear-lemming-473.convex.cloud';
8
+ export function getBaseDir() {
9
+ return join(homedir(), '.openclaw', 'workspace');
10
+ }
11
+ export function getMemoryDir() {
12
+ return join(getBaseDir(), 'memory');
13
+ }
14
+ export function getConfigDir() {
15
+ return join(getBaseDir(), '.super-memory');
16
+ }
17
+ export function getConfigPath() {
18
+ return join(getConfigDir(), 'config.json');
19
+ }
20
+ export function getConvexUrl() {
21
+ return CONVEX_URL;
22
+ }
23
+ export function loadConfig() {
24
+ const configPath = getConfigPath();
25
+ if (!existsSync(configPath)) {
26
+ return null;
27
+ }
28
+ try {
29
+ return JSON.parse(readFileSync(configPath, 'utf-8'));
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ export function saveConfig(config) {
36
+ const configDir = getConfigDir();
37
+ if (!existsSync(configDir)) {
38
+ mkdirSync(configDir, { recursive: true });
39
+ }
40
+ writeFileSync(getConfigPath(), JSON.stringify(config, null, 2));
41
+ }
42
+ export function isValidLicenseKey(key) {
43
+ return /^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(key);
44
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trlc/super-memory",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Super Memory CLI - AI-powered persistent memory by The Red Lobster Cartel",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -9,6 +9,8 @@
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "build": "tsc",
12
+ "postinstall": "cd dashboard && npm install --silent || true",
13
+ "dashboard": "node dashboard/server.js",
12
14
  "prepublishOnly": "npm run build",
13
15
  "test": "node dist/index.js --help"
14
16
  },