@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 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
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * License types for Claude Memory Pro
3
+ */
4
+ export {};