@contextmirror/claude-memory 0.2.0 → 0.2.2
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/dist/cli.js +168 -0
- package/dist/license/index.d.ts +39 -0
- package/dist/license/index.js +153 -0
- package/dist/license/types.d.ts +37 -0
- package/dist/license/types.js +4 -0
- package/dist/mcp/server.js +217 -12
- package/dist/scanner/contextGenerator.d.ts +7 -0
- package/dist/scanner/contextGenerator.js +97 -10
- package/dist/scanner/projectScanner.js +59 -15
- package/dist/setup/setupWizard.js +81 -4
- package/dist/types/index.d.ts +36 -1
- package/dist/types/index.js +41 -2
- package/landing/index.html +478 -169
- package/package.json +2 -1
- package/.claude/settings.local.json +0 -10
package/dist/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ import { scanProjects } from './scanner/projectScanner.js';
|
|
|
16
16
|
import { generateGlobalContext, writeGlobalContext, getMemoryDir } from './scanner/contextGenerator.js';
|
|
17
17
|
import { generateBriefing, briefingToClaudeMd } from './briefing/briefingGenerator.js';
|
|
18
18
|
import { runSetupWizard } from './setup/setupWizard.js';
|
|
19
|
+
import { activateLicense, deactivateLicense, getLicenseStatus } from './license/index.js';
|
|
19
20
|
const program = new Command();
|
|
20
21
|
program
|
|
21
22
|
.name('claude-memory')
|
|
@@ -35,6 +36,11 @@ program
|
|
|
35
36
|
maxDepth: parseInt(options.depth, 10),
|
|
36
37
|
generateClaudeMd: options.generateClaudeMd,
|
|
37
38
|
});
|
|
39
|
+
if (projects.length === 0) {
|
|
40
|
+
console.log('\n⚠️ No projects found. Context not updated.');
|
|
41
|
+
console.log(' Check the directory path and try again.');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
38
44
|
const context = generateGlobalContext(projects);
|
|
39
45
|
writeGlobalContext(context);
|
|
40
46
|
console.log('\n✅ Done! Global context updated.');
|
|
@@ -89,6 +95,106 @@ program
|
|
|
89
95
|
console.log(` CLAUDE.md: ${project.hasFiles.claudeMd ? '✓' : '✗'}`);
|
|
90
96
|
console.log(` README.md: ${project.hasFiles.readme ? '✓' : '✗'}`);
|
|
91
97
|
});
|
|
98
|
+
// Exclude command - exclude a project from scanning
|
|
99
|
+
program
|
|
100
|
+
.command('exclude')
|
|
101
|
+
.description('Exclude a project from future scans')
|
|
102
|
+
.argument('<project>', 'Project path or name')
|
|
103
|
+
.action((projectQuery) => {
|
|
104
|
+
const context = loadContext();
|
|
105
|
+
if (!context) {
|
|
106
|
+
console.log('No projects scanned yet. Run: claude-memory scan');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Find the project
|
|
110
|
+
const project = context.projects.find((p) => p.name.toLowerCase() === projectQuery.toLowerCase() || p.path.includes(projectQuery));
|
|
111
|
+
if (!project) {
|
|
112
|
+
// Maybe it's a direct path
|
|
113
|
+
if (existsSync(projectQuery)) {
|
|
114
|
+
addExclusion(projectQuery);
|
|
115
|
+
console.log(`✅ Excluded: ${projectQuery}`);
|
|
116
|
+
console.log(' This project will be skipped in future scans.');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
console.log(`Project not found: ${projectQuery}`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
addExclusion(project.path);
|
|
123
|
+
console.log(`✅ Excluded: ${project.name}`);
|
|
124
|
+
console.log(` Path: ${project.path}`);
|
|
125
|
+
console.log(' This project will be skipped in future scans.');
|
|
126
|
+
console.log(' Run `claude-memory include` to re-add it.');
|
|
127
|
+
});
|
|
128
|
+
// Include command - remove a project from exclusion list
|
|
129
|
+
program
|
|
130
|
+
.command('include')
|
|
131
|
+
.description('Remove a project from the exclusion list')
|
|
132
|
+
.argument('<project>', 'Project path or name')
|
|
133
|
+
.action((projectQuery) => {
|
|
134
|
+
const context = loadContext();
|
|
135
|
+
if (!context) {
|
|
136
|
+
console.log('No projects scanned yet. Run: claude-memory scan');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const excluded = context.excludedProjects || [];
|
|
140
|
+
const match = excluded.find(p => p.toLowerCase().includes(projectQuery.toLowerCase()));
|
|
141
|
+
if (!match) {
|
|
142
|
+
console.log(`Project not in exclusion list: ${projectQuery}`);
|
|
143
|
+
if (excluded.length > 0) {
|
|
144
|
+
console.log('\nCurrently excluded:');
|
|
145
|
+
excluded.forEach(p => console.log(` - ${p}`));
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
removeExclusion(match);
|
|
150
|
+
console.log(`✅ Removed from exclusion list: ${match}`);
|
|
151
|
+
console.log(' Run `claude-memory scan` to add it back to your projects.');
|
|
152
|
+
});
|
|
153
|
+
// Excluded command - list excluded projects
|
|
154
|
+
program
|
|
155
|
+
.command('excluded')
|
|
156
|
+
.description('List excluded projects')
|
|
157
|
+
.action(() => {
|
|
158
|
+
const context = loadContext();
|
|
159
|
+
const excluded = context?.excludedProjects || [];
|
|
160
|
+
if (excluded.length === 0) {
|
|
161
|
+
console.log('No excluded projects.');
|
|
162
|
+
console.log('Use `claude-memory exclude <project>` to exclude a project.');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
console.log('🚫 Excluded Projects\n');
|
|
166
|
+
excluded.forEach(p => console.log(` ${p}`));
|
|
167
|
+
console.log('\nUse `claude-memory include <project>` to re-add.');
|
|
168
|
+
});
|
|
169
|
+
function addExclusion(projectPath) {
|
|
170
|
+
const contextPath = join(getMemoryDir(), 'context.json');
|
|
171
|
+
if (!existsSync(contextPath))
|
|
172
|
+
return;
|
|
173
|
+
try {
|
|
174
|
+
const context = JSON.parse(readFileSync(contextPath, 'utf-8'));
|
|
175
|
+
context.excludedProjects = context.excludedProjects || [];
|
|
176
|
+
if (!context.excludedProjects.includes(projectPath)) {
|
|
177
|
+
context.excludedProjects.push(projectPath);
|
|
178
|
+
writeFileSync(contextPath, JSON.stringify(context, null, 2), 'utf-8');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
console.error('Failed to update exclusion list');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function removeExclusion(projectPath) {
|
|
186
|
+
const contextPath = join(getMemoryDir(), 'context.json');
|
|
187
|
+
if (!existsSync(contextPath))
|
|
188
|
+
return;
|
|
189
|
+
try {
|
|
190
|
+
const context = JSON.parse(readFileSync(contextPath, 'utf-8'));
|
|
191
|
+
context.excludedProjects = (context.excludedProjects || []).filter((p) => p !== projectPath);
|
|
192
|
+
writeFileSync(contextPath, JSON.stringify(context, null, 2), 'utf-8');
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
console.error('Failed to update exclusion list');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
92
198
|
// Briefing command - deep project analysis and CLAUDE.md generation
|
|
93
199
|
program
|
|
94
200
|
.command('briefing')
|
|
@@ -193,6 +299,68 @@ program
|
|
|
193
299
|
interactive: !options.nonInteractive,
|
|
194
300
|
});
|
|
195
301
|
});
|
|
302
|
+
// Activate command - activate a Pro license
|
|
303
|
+
program
|
|
304
|
+
.command('activate')
|
|
305
|
+
.description('Activate a Pro license key')
|
|
306
|
+
.argument('<key>', 'License key (format: CM-XXXX-XXXX-XXXX)')
|
|
307
|
+
.action(async (key) => {
|
|
308
|
+
console.log('Activating license...\n');
|
|
309
|
+
const result = await activateLicense(key);
|
|
310
|
+
if (!result.valid) {
|
|
311
|
+
console.log(`❌ ${result.error}`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
console.log('✅ License activated successfully!');
|
|
315
|
+
console.log(` Plan: Pro`);
|
|
316
|
+
if (result.license?.email) {
|
|
317
|
+
console.log(` Email: ${result.license.email}`);
|
|
318
|
+
}
|
|
319
|
+
console.log('\nPro features are now unlocked.');
|
|
320
|
+
});
|
|
321
|
+
// Status command - show license status
|
|
322
|
+
program
|
|
323
|
+
.command('status')
|
|
324
|
+
.description('Show license status')
|
|
325
|
+
.action(() => {
|
|
326
|
+
const status = getLicenseStatus();
|
|
327
|
+
console.log('🧠 Claude Memory License Status\n');
|
|
328
|
+
console.log(` Plan: ${status.plan === 'pro' ? 'Pro' : 'Free'}`);
|
|
329
|
+
if (status.isPro) {
|
|
330
|
+
if (status.email) {
|
|
331
|
+
console.log(` Email: ${status.email}`);
|
|
332
|
+
}
|
|
333
|
+
if (status.activatedAt) {
|
|
334
|
+
console.log(` Activated: ${new Date(status.activatedAt).toLocaleDateString()}`);
|
|
335
|
+
}
|
|
336
|
+
if (status.expiresAt) {
|
|
337
|
+
console.log(` Expires: ${new Date(status.expiresAt).toLocaleDateString()}`);
|
|
338
|
+
if (status.daysRemaining) {
|
|
339
|
+
console.log(` Days remaining: ${status.daysRemaining}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
console.log(` Status: Active`);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
console.log(` Status: ${status.expiresAt ? 'Expired' : 'Not activated'}`);
|
|
346
|
+
console.log('\n Upgrade at https://claude-memory.dev');
|
|
347
|
+
console.log(' Or run: claude-memory activate <your-key>');
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
// Deactivate command - remove license
|
|
351
|
+
program
|
|
352
|
+
.command('deactivate')
|
|
353
|
+
.description('Remove Pro license')
|
|
354
|
+
.action(() => {
|
|
355
|
+
const removed = deactivateLicense();
|
|
356
|
+
if (removed) {
|
|
357
|
+
console.log('✅ License removed.');
|
|
358
|
+
console.log(' You are now on the Free plan.');
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
console.log('No license to remove.');
|
|
362
|
+
}
|
|
363
|
+
});
|
|
196
364
|
function loadContext() {
|
|
197
365
|
const contextPath = join(getMemoryDir(), 'context.json');
|
|
198
366
|
if (!existsSync(contextPath)) {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License management for Claude Memory Pro
|
|
3
|
+
*/
|
|
4
|
+
import { License, LicenseStatus, LicenseValidationResult } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Load license from disk
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadLicense(): License | null;
|
|
9
|
+
/**
|
|
10
|
+
* Save license to disk
|
|
11
|
+
*/
|
|
12
|
+
export declare function saveLicense(license: License): void;
|
|
13
|
+
/**
|
|
14
|
+
* Check if user has Pro license
|
|
15
|
+
*/
|
|
16
|
+
export declare function isPro(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Validate a license key format
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateKeyFormat(key: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Activate a license key
|
|
23
|
+
*
|
|
24
|
+
* Phase 1: Offline validation (format check only)
|
|
25
|
+
* Phase 2: Will call LemonSqueezy API for server validation
|
|
26
|
+
*/
|
|
27
|
+
export declare function activateLicense(key: string): Promise<LicenseValidationResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Deactivate (remove) the current license
|
|
30
|
+
*/
|
|
31
|
+
export declare function deactivateLicense(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Get current license status
|
|
34
|
+
*/
|
|
35
|
+
export declare function getLicenseStatus(): LicenseStatus;
|
|
36
|
+
/**
|
|
37
|
+
* Get a user-friendly error message for Pro features
|
|
38
|
+
*/
|
|
39
|
+
export declare function getProFeatureMessage(featureName: string): string;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License management for Claude Memory Pro
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { homedir } from 'os';
|
|
7
|
+
const MEMORY_DIR = join(homedir(), '.claude-memory');
|
|
8
|
+
const LICENSE_PATH = join(MEMORY_DIR, 'license.json');
|
|
9
|
+
// License key format: CM-XXXX-XXXX-XXXX (alphanumeric)
|
|
10
|
+
const LICENSE_KEY_PATTERN = /^CM-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/i;
|
|
11
|
+
/**
|
|
12
|
+
* Ensure the memory directory exists
|
|
13
|
+
*/
|
|
14
|
+
function ensureMemoryDir() {
|
|
15
|
+
if (!existsSync(MEMORY_DIR)) {
|
|
16
|
+
mkdirSync(MEMORY_DIR, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load license from disk
|
|
21
|
+
*/
|
|
22
|
+
export function loadLicense() {
|
|
23
|
+
if (!existsSync(LICENSE_PATH)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const data = readFileSync(LICENSE_PATH, 'utf-8');
|
|
28
|
+
return JSON.parse(data);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Save license to disk
|
|
36
|
+
*/
|
|
37
|
+
export function saveLicense(license) {
|
|
38
|
+
ensureMemoryDir();
|
|
39
|
+
writeFileSync(LICENSE_PATH, JSON.stringify(license, null, 2), 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if user has Pro license
|
|
43
|
+
*/
|
|
44
|
+
export function isPro() {
|
|
45
|
+
const license = loadLicense();
|
|
46
|
+
if (!license) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (license.plan !== 'pro') {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
// Check expiration
|
|
53
|
+
if (license.expiresAt) {
|
|
54
|
+
const expiresAt = new Date(license.expiresAt);
|
|
55
|
+
if (expiresAt < new Date()) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Validate a license key format
|
|
63
|
+
*/
|
|
64
|
+
export function validateKeyFormat(key) {
|
|
65
|
+
return LICENSE_KEY_PATTERN.test(key);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Activate a license key
|
|
69
|
+
*
|
|
70
|
+
* Phase 1: Offline validation (format check only)
|
|
71
|
+
* Phase 2: Will call LemonSqueezy API for server validation
|
|
72
|
+
*/
|
|
73
|
+
export async function activateLicense(key) {
|
|
74
|
+
// Normalize key to uppercase
|
|
75
|
+
const normalizedKey = key.toUpperCase();
|
|
76
|
+
// Validate format
|
|
77
|
+
if (!validateKeyFormat(normalizedKey)) {
|
|
78
|
+
return {
|
|
79
|
+
valid: false,
|
|
80
|
+
error: 'Invalid license key format. Expected: CM-XXXX-XXXX-XXXX',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// TODO: Phase 2 - Call LemonSqueezy API to validate
|
|
84
|
+
// const response = await fetch('https://api.claude-memory.dev/validate', {
|
|
85
|
+
// method: 'POST',
|
|
86
|
+
// body: JSON.stringify({ key: normalizedKey }),
|
|
87
|
+
// });
|
|
88
|
+
// For now, accept any valid format (Phase 1 - offline)
|
|
89
|
+
const license = {
|
|
90
|
+
key: normalizedKey,
|
|
91
|
+
plan: 'pro',
|
|
92
|
+
activatedAt: new Date().toISOString(),
|
|
93
|
+
// No expiration for now
|
|
94
|
+
};
|
|
95
|
+
saveLicense(license);
|
|
96
|
+
return {
|
|
97
|
+
valid: true,
|
|
98
|
+
license,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Deactivate (remove) the current license
|
|
103
|
+
*/
|
|
104
|
+
export function deactivateLicense() {
|
|
105
|
+
if (!existsSync(LICENSE_PATH)) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
unlinkSync(LICENSE_PATH);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get current license status
|
|
118
|
+
*/
|
|
119
|
+
export function getLicenseStatus() {
|
|
120
|
+
const license = loadLicense();
|
|
121
|
+
if (!license) {
|
|
122
|
+
return {
|
|
123
|
+
isPro: false,
|
|
124
|
+
plan: 'free',
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
let daysRemaining;
|
|
128
|
+
let isExpired = false;
|
|
129
|
+
if (license.expiresAt) {
|
|
130
|
+
const expiresAt = new Date(license.expiresAt);
|
|
131
|
+
const now = new Date();
|
|
132
|
+
const diffMs = expiresAt.getTime() - now.getTime();
|
|
133
|
+
daysRemaining = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
|
|
134
|
+
isExpired = daysRemaining < 0;
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
isPro: license.plan === 'pro' && !isExpired,
|
|
138
|
+
plan: isExpired ? 'free' : license.plan,
|
|
139
|
+
email: license.email,
|
|
140
|
+
expiresAt: license.expiresAt,
|
|
141
|
+
daysRemaining: daysRemaining && daysRemaining > 0 ? daysRemaining : undefined,
|
|
142
|
+
activatedAt: license.activatedAt,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get a user-friendly error message for Pro features
|
|
147
|
+
*/
|
|
148
|
+
export function getProFeatureMessage(featureName) {
|
|
149
|
+
return `${featureName} is a Pro feature.
|
|
150
|
+
|
|
151
|
+
Upgrade at https://claude-memory.dev or activate your license:
|
|
152
|
+
claude-memory activate <your-key>`;
|
|
153
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License types for Claude Memory Pro
|
|
3
|
+
*/
|
|
4
|
+
export interface License {
|
|
5
|
+
/** License key (format: CM-XXXX-XXXX-XXXX) */
|
|
6
|
+
key: string;
|
|
7
|
+
/** Email associated with the license */
|
|
8
|
+
email?: string;
|
|
9
|
+
/** License plan */
|
|
10
|
+
plan: 'free' | 'pro';
|
|
11
|
+
/** When the license was activated */
|
|
12
|
+
activatedAt: string;
|
|
13
|
+
/** When the license expires (if applicable) */
|
|
14
|
+
expiresAt?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface LicenseStatus {
|
|
17
|
+
/** Whether the user has Pro features */
|
|
18
|
+
isPro: boolean;
|
|
19
|
+
/** Current plan */
|
|
20
|
+
plan: 'free' | 'pro';
|
|
21
|
+
/** Email associated with license */
|
|
22
|
+
email?: string;
|
|
23
|
+
/** Expiration date */
|
|
24
|
+
expiresAt?: string;
|
|
25
|
+
/** Days remaining until expiration */
|
|
26
|
+
daysRemaining?: number;
|
|
27
|
+
/** When activated */
|
|
28
|
+
activatedAt?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface LicenseValidationResult {
|
|
31
|
+
/** Whether the key is valid */
|
|
32
|
+
valid: boolean;
|
|
33
|
+
/** Error message if invalid */
|
|
34
|
+
error?: string;
|
|
35
|
+
/** License details if valid */
|
|
36
|
+
license?: License;
|
|
37
|
+
}
|