claude-mem 3.3.7 → 3.3.9

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.
Files changed (96) hide show
  1. package/README.md +183 -46
  2. package/dist/bin/cli.d.ts +2 -0
  3. package/dist/bin/cli.js +179 -0
  4. package/dist/commands/compress.d.ts +2 -0
  5. package/dist/commands/compress.js +27 -0
  6. package/dist/commands/hooks.d.ts +19 -0
  7. package/dist/commands/hooks.js +131 -0
  8. package/dist/commands/install.d.ts +2 -0
  9. package/dist/commands/install.js +836 -0
  10. package/dist/commands/load-context.d.ts +2 -0
  11. package/dist/commands/load-context.js +151 -0
  12. package/dist/commands/logs.d.ts +2 -0
  13. package/dist/commands/logs.js +76 -0
  14. package/dist/commands/migrate-to-jsonl.d.ts +5 -0
  15. package/dist/commands/migrate-to-jsonl.js +99 -0
  16. package/dist/commands/restore.d.ts +1 -0
  17. package/dist/commands/restore.js +20 -0
  18. package/dist/commands/status.d.ts +1 -0
  19. package/dist/commands/status.js +136 -0
  20. package/dist/commands/trash-empty.d.ts +3 -0
  21. package/dist/commands/trash-empty.js +56 -0
  22. package/dist/commands/trash-view.d.ts +1 -0
  23. package/dist/commands/trash-view.js +101 -0
  24. package/dist/commands/trash.d.ts +6 -0
  25. package/dist/commands/trash.js +49 -0
  26. package/dist/commands/uninstall.d.ts +2 -0
  27. package/dist/commands/uninstall.js +107 -0
  28. package/dist/constants.d.ts +271 -0
  29. package/dist/constants.js +199 -0
  30. package/dist/core/compression/TranscriptCompressor.d.ts +79 -0
  31. package/dist/core/compression/TranscriptCompressor.js +585 -0
  32. package/dist/core/orchestration/PromptOrchestrator.d.ts +165 -0
  33. package/dist/core/orchestration/PromptOrchestrator.js +182 -0
  34. package/dist/lib/time-utils.d.ts +5 -0
  35. package/dist/lib/time-utils.js +70 -0
  36. package/dist/prompts/constants.d.ts +126 -0
  37. package/dist/prompts/constants.js +161 -0
  38. package/dist/prompts/index.d.ts +10 -0
  39. package/dist/prompts/index.js +11 -0
  40. package/dist/prompts/templates/analysis/AnalysisTemplates.d.ts +13 -0
  41. package/dist/prompts/templates/analysis/AnalysisTemplates.js +94 -0
  42. package/dist/prompts/templates/context/ContextTemplates.d.ts +119 -0
  43. package/dist/prompts/templates/context/ContextTemplates.js +399 -0
  44. package/dist/prompts/templates/hooks/HookTemplates.d.ts +175 -0
  45. package/dist/prompts/templates/hooks/HookTemplates.js +394 -0
  46. package/dist/prompts/templates/hooks/HookTemplates.test.d.ts +7 -0
  47. package/dist/prompts/templates/hooks/HookTemplates.test.js +127 -0
  48. package/dist/shared/config.d.ts +4 -0
  49. package/dist/shared/config.js +41 -0
  50. package/dist/shared/error-handler.d.ts +22 -0
  51. package/dist/shared/error-handler.js +142 -0
  52. package/dist/shared/logger.d.ts +19 -0
  53. package/dist/shared/logger.js +51 -0
  54. package/dist/shared/paths.d.ts +28 -0
  55. package/dist/shared/paths.js +100 -0
  56. package/dist/shared/settings.d.ts +41 -0
  57. package/dist/shared/settings.js +81 -0
  58. package/dist/shared/types.d.ts +145 -0
  59. package/dist/shared/types.js +78 -0
  60. package/docs/STATUS.md +155 -0
  61. package/docs/chroma-backend-migration.md +161 -0
  62. package/docs/landing-page-outline.md +287 -0
  63. package/docs/multi-platform-builds.md +96 -0
  64. package/docs/plans/cloud-service-plan.md +274 -0
  65. package/docs/plans/fix-response-format-issue.md +61 -0
  66. package/docs/plans/restructure-session-hook-output.md +102 -0
  67. package/docs/plans/session-start-hook-investigation.md +45 -0
  68. package/docs/plans/src-reorganization-plan.md +181 -0
  69. package/docs/plans/terminal-effects-decision.md +22 -0
  70. package/docs/plans/terminal-effects-integration.md +82 -0
  71. package/docs/plans/trash-bin-feature-plan.md +240 -0
  72. package/docs/plans/trash-bin-minimal-plan.md +102 -0
  73. package/docs/reference/bun-single-executable.md +584 -0
  74. package/docs/reference/cc-output-styles.md +99 -0
  75. package/docs/reference/chroma-mcp-project-memory-example.md +80 -0
  76. package/docs/reference/chroma-mcp-team-example.md +92 -0
  77. package/docs/reference/claude-code/cc-hooks.md +787 -0
  78. package/docs/reference/claude-code/cc-status-line.md +202 -0
  79. package/docs/reference/claude-code/hook-configuration.md +173 -0
  80. package/docs/reference/claude-code/hook-responses.md +127 -0
  81. package/docs/reference/claude-code/hooks.md +175 -0
  82. package/docs/reference/claude-code/mcp-configuration.md +133 -0
  83. package/docs/reference/claude-code/session-start-hook.md +82 -0
  84. package/docs/reference/load-context-format-example.md +33 -0
  85. package/docs/reference/mcp-sdk/mcp-typescript-sdk-readme.md +1323 -0
  86. package/docs/reference/mcp-sdk/server-implementation.md +286 -0
  87. package/docs/reference/mcp-sdk/stdio-transport.md +345 -0
  88. package/docs/todos/fix-response-format-tasks.md +43 -0
  89. package/docs/todos/implementation-todos.md +280 -0
  90. package/docs/todos/restructure-hook-output-tasks.md +103 -0
  91. package/docs/todos/session-start-hook-fix.md +26 -0
  92. package/docs/todos/terminal-effects-tasks.md +42 -0
  93. package/docs/todos/trash-bin-implementation-todos.md +348 -0
  94. package/docs/todos/trash-bin-minimal-todos.md +27 -0
  95. package/package.json +56 -6
  96. package/claude-mem +0 -0
@@ -0,0 +1,2 @@
1
+ import { OptionValues } from 'commander';
2
+ export declare function loadContext(options?: OptionValues): Promise<void>;
@@ -0,0 +1,151 @@
1
+ import fs from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import { PathResolver } from '../shared/paths.js';
5
+ import { createCompletionMessage, createContextualError, createUserFriendlyError, formatTimeAgo, outputSessionStartContent } from '../prompts/templates/context/ContextTemplates.js';
6
+ function formatSize(bytes) {
7
+ if (bytes === 0)
8
+ return '0 B';
9
+ const k = 1024;
10
+ const sizes = ['B', 'KB', 'MB', 'GB'];
11
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
12
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
13
+ }
14
+ function getTrashStatus() {
15
+ const trashDir = join(homedir(), '.claude-mem', 'trash');
16
+ if (!fs.existsSync(trashDir)) {
17
+ return { folderCount: 0, fileCount: 0, totalSize: 0, isEmpty: true };
18
+ }
19
+ const items = fs.readdirSync(trashDir);
20
+ if (items.length === 0) {
21
+ return { folderCount: 0, fileCount: 0, totalSize: 0, isEmpty: true };
22
+ }
23
+ let folderCount = 0;
24
+ let fileCount = 0;
25
+ let totalSize = 0;
26
+ for (const item of items) {
27
+ const itemPath = join(trashDir, item);
28
+ const stats = fs.statSync(itemPath);
29
+ if (stats.isDirectory()) {
30
+ folderCount++;
31
+ }
32
+ else {
33
+ fileCount++;
34
+ }
35
+ totalSize += stats.size;
36
+ }
37
+ return { folderCount, fileCount, totalSize, isEmpty: false };
38
+ }
39
+ export async function loadContext(options = {}) {
40
+ const pathResolver = new PathResolver();
41
+ const indexPath = pathResolver.getIndexPath();
42
+ try {
43
+ // Check if index file exists
44
+ if (!fs.existsSync(indexPath)) {
45
+ if (options.format === 'session-start') {
46
+ console.log(createContextualError('NO_MEMORIES', options.project || 'this project'));
47
+ }
48
+ return;
49
+ }
50
+ const content = fs.readFileSync(indexPath, 'utf-8');
51
+ const lines = content.trim().split('\n').filter(line => line.trim());
52
+ if (lines.length === 0) {
53
+ if (options.format === 'session-start') {
54
+ console.log(createContextualError('NO_MEMORIES', options.project || 'this project'));
55
+ }
56
+ return;
57
+ }
58
+ // Parse JSONL format - each line is a JSON object
59
+ const jsonObjects = [];
60
+ for (const line of lines) {
61
+ try {
62
+ // Skip lines that don't look like JSON (could be legacy format)
63
+ if (!line.trim().startsWith('{')) {
64
+ continue;
65
+ }
66
+ const obj = JSON.parse(line);
67
+ jsonObjects.push(obj);
68
+ }
69
+ catch (e) {
70
+ // Skip malformed JSON lines
71
+ continue;
72
+ }
73
+ }
74
+ if (jsonObjects.length === 0) {
75
+ if (options.format === 'session-start') {
76
+ console.log(createContextualError('NO_MEMORIES', options.project || 'this project'));
77
+ }
78
+ return;
79
+ }
80
+ // Filter by project if specified
81
+ let filteredObjects = jsonObjects;
82
+ if (options.project) {
83
+ // Sanitize project name to match how it's stored (replace hyphens with underscores)
84
+ const sanitizedProject = options.project.replace(/-/g, '_');
85
+ // Filter objects by project field
86
+ filteredObjects = jsonObjects.filter(obj => obj.project === sanitizedProject || obj.project === options.project);
87
+ }
88
+ // Take more entries for session-start to get multiple overviews
89
+ const count = options.format === 'session-start' ? 30 : 10;
90
+ const recentObjects = filteredObjects.slice(-count);
91
+ if (options.format === 'session-start') {
92
+ // Find most recent timestamp for last session info
93
+ let lastSessionTime = 'recently';
94
+ const timestamps = recentObjects
95
+ .map(obj => {
96
+ // Get timestamp from JSON object
97
+ return obj.timestamp ? new Date(obj.timestamp) : null;
98
+ })
99
+ .filter(date => date !== null)
100
+ .sort((a, b) => b.getTime() - a.getTime());
101
+ if (timestamps.length > 0) {
102
+ lastSessionTime = formatTimeAgo(timestamps[0]);
103
+ }
104
+ // Use dual-stream output for session start formatting
105
+ outputSessionStartContent({
106
+ projectName: options.project || 'your project',
107
+ memoryCount: recentObjects.length,
108
+ lastSessionTime,
109
+ recentObjects
110
+ });
111
+ }
112
+ else if (options.format === 'json') {
113
+ console.log(JSON.stringify(recentObjects));
114
+ }
115
+ else {
116
+ // Default format - show completion message and entries
117
+ console.log(createCompletionMessage('Context loading', recentObjects.length, 'recent memories found'));
118
+ recentObjects.forEach((obj) => {
119
+ if (obj.type === 'memory') {
120
+ console.log(`${obj.text} | ${obj.document_id} | ${obj.keywords}`);
121
+ }
122
+ else if (obj.type === 'overview') {
123
+ console.log(`**Overview:** ${obj.content}`);
124
+ }
125
+ else if (obj.type === 'session') {
126
+ console.log(`# Session: ${obj.session_id} [${obj.timestamp}]`);
127
+ }
128
+ else {
129
+ console.log(JSON.stringify(obj));
130
+ }
131
+ });
132
+ }
133
+ // Display trash status if not empty (except for JSON format to avoid breaking JSON parsing)
134
+ if (options.format !== 'json') {
135
+ const trashStatus = getTrashStatus();
136
+ if (!trashStatus.isEmpty) {
137
+ const formattedSize = formatSize(trashStatus.totalSize);
138
+ console.log(`šŸ—‘ļø Trash – ${trashStatus.folderCount} folders | ${trashStatus.fileCount} files | ${formattedSize} – use \`$ claude-mem restore\` for more options.`);
139
+ }
140
+ }
141
+ }
142
+ catch (error) {
143
+ const errorMessage = error instanceof Error ? error.message : String(error);
144
+ if (options.format === 'session-start') {
145
+ console.log(createContextualError('CONNECTION_FAILED', errorMessage));
146
+ }
147
+ else {
148
+ console.log(createUserFriendlyError('Context loading', errorMessage, 'Check file permissions and try again'));
149
+ }
150
+ }
151
+ }
@@ -0,0 +1,2 @@
1
+ import { OptionValues } from 'commander';
2
+ export declare function logs(options?: OptionValues): Promise<void>;
@@ -0,0 +1,76 @@
1
+ import { readFileSync, readdirSync, statSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ // <Block> 1.1 ====================================
5
+ async function showLog(logPath, logType, tail) {
6
+ // <Block> 1.2 ====================================
7
+ try {
8
+ const content = readFileSync(logPath, 'utf8');
9
+ const lines = content.split('\n').filter(line => line.trim());
10
+ const displayLines = lines.slice(-tail);
11
+ console.log(`šŸ“‹ ${logType} Logs (last ${tail} lines):`);
12
+ console.log(` File: ${logPath}`);
13
+ console.log('');
14
+ // <Block> 1.3 ====================================
15
+ if (displayLines.length === 0) {
16
+ console.log(' No log entries found');
17
+ // </Block> =======================================
18
+ }
19
+ else {
20
+ displayLines.forEach(line => {
21
+ console.log(` ${line}`);
22
+ });
23
+ }
24
+ // </Block> =======================================
25
+ console.log('');
26
+ // </Block> =======================================
27
+ }
28
+ catch (error) {
29
+ // <Block> 1.4 ====================================
30
+ console.log(`āŒ Could not read ${logType.toLowerCase()} log: ${logPath}`);
31
+ // </Block> =======================================
32
+ }
33
+ // </Block> =======================================
34
+ }
35
+ // <Block> 2.1 ====================================
36
+ export async function logs(options = {}) {
37
+ // <Block> 2.2 ====================================
38
+ const logsDir = join(homedir(), '.claude-mem', 'logs');
39
+ const tail = parseInt(options.tail) || 20;
40
+ // </Block> =======================================
41
+ // Find most recent log file
42
+ try {
43
+ const files = readdirSync(logsDir);
44
+ const logFiles = files
45
+ .filter(f => f.startsWith('claude-mem-') && f.endsWith('.log'))
46
+ .map(f => ({
47
+ name: f,
48
+ path: join(logsDir, f),
49
+ mtime: statSync(join(logsDir, f)).mtime
50
+ }))
51
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
52
+ if (logFiles.length === 0) {
53
+ console.log('āŒ No log files found in ~/.claude-mem/logs/');
54
+ return;
55
+ }
56
+ // Show most recent log
57
+ await showLog(logFiles[0].path, 'Most Recent', tail);
58
+ if (options.all && logFiles.length > 1) {
59
+ console.log(`šŸ“š Found ${logFiles.length} total log files`);
60
+ }
61
+ }
62
+ catch (error) {
63
+ console.log('āŒ Could not read logs directory: ~/.claude-mem/logs/');
64
+ console.log(' Run a compression first to generate logs');
65
+ }
66
+ // <Block> 2.5 ====================================
67
+ if (options.follow) {
68
+ console.log('Following logs... (Press Ctrl+C to stop)');
69
+ // Basic follow implementation - would need more sophisticated watching in real usage
70
+ setInterval(() => {
71
+ // This would need proper file watching implementation
72
+ }, 1000);
73
+ }
74
+ // </Block> =======================================
75
+ // </Block> =======================================
76
+ }
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * One-time migration script to convert claude-mem-index.md to claude-mem-index.jsonl
4
+ */
5
+ export declare function migrateToJSONL(): void;
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * One-time migration script to convert claude-mem-index.md to claude-mem-index.jsonl
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { PathResolver } from '../shared/paths.js';
8
+ export function migrateToJSONL() {
9
+ const pathResolver = new PathResolver();
10
+ const oldIndexPath = path.join(pathResolver.getConfigDir(), 'claude-mem-index.md');
11
+ const newIndexPath = path.join(pathResolver.getConfigDir(), 'claude-mem-index.jsonl');
12
+ // Check if old index exists
13
+ if (!fs.existsSync(oldIndexPath)) {
14
+ console.log('No markdown index found to migrate');
15
+ return;
16
+ }
17
+ // Check if new index already exists
18
+ if (fs.existsSync(newIndexPath)) {
19
+ console.log('JSONL index already exists, skipping migration');
20
+ return;
21
+ }
22
+ console.log('Starting migration from MD to JSONL...');
23
+ const content = fs.readFileSync(oldIndexPath, 'utf-8');
24
+ const lines = content.split('\n').filter(line => line.trim());
25
+ const jsonlLines = [];
26
+ let currentSessionId = '';
27
+ let currentSessionTimestamp = '';
28
+ for (const line of lines) {
29
+ // Parse session headers: # Session: <id> [<timestamp>]
30
+ const sessionMatch = line.match(/^# Session:\s*([^\[]+)(?:\s*\[([^\]]+)\])?/);
31
+ if (sessionMatch) {
32
+ currentSessionId = sessionMatch[1].trim();
33
+ currentSessionTimestamp = sessionMatch[2]?.trim() || new Date().toISOString();
34
+ // Extract project from session ID (assuming format like <project>_<uuid>)
35
+ const projectMatch = currentSessionId.match(/^([^_]+)_/);
36
+ const project = projectMatch ? projectMatch[1] : 'unknown';
37
+ jsonlLines.push(JSON.stringify({
38
+ type: 'session',
39
+ session_id: currentSessionId,
40
+ timestamp: currentSessionTimestamp,
41
+ project
42
+ }));
43
+ continue;
44
+ }
45
+ // Parse overviews: **Overview:** <text>
46
+ const overviewMatch = line.match(/^\*\*Overview:\*\*\s*(.+)/);
47
+ if (overviewMatch) {
48
+ const overviewText = overviewMatch[1].trim();
49
+ // Extract project from current session ID
50
+ const projectMatch = currentSessionId.match(/^([^_]+)_/);
51
+ const project = projectMatch ? projectMatch[1] : 'unknown';
52
+ jsonlLines.push(JSON.stringify({
53
+ type: 'overview',
54
+ content: overviewText,
55
+ session_id: currentSessionId,
56
+ project,
57
+ timestamp: currentSessionTimestamp
58
+ }));
59
+ continue;
60
+ }
61
+ // Skip certain lines
62
+ if (line.startsWith('# NO SUMMARIES EXTRACTED')) {
63
+ continue;
64
+ }
65
+ // Parse memory entries (pipe-separated)
66
+ if (line.includes(' | ')) {
67
+ const parts = line.split(' | ').map(p => p.trim());
68
+ if (parts.length >= 3) {
69
+ const [text, document_id, keywords, timestamp, archive] = parts;
70
+ // Extract project from document_id (format: <project>_<session>_<number>)
71
+ const projectMatch = document_id?.match(/^([^_]+)_/);
72
+ const project = projectMatch ? projectMatch[1] : 'unknown';
73
+ jsonlLines.push(JSON.stringify({
74
+ type: 'memory',
75
+ text,
76
+ document_id: document_id || `${currentSessionId}_${Date.now()}`,
77
+ keywords: keywords || '',
78
+ session_id: currentSessionId,
79
+ project,
80
+ timestamp: timestamp || currentSessionTimestamp,
81
+ archive: archive || `${currentSessionId}.jsonl.archive`
82
+ }));
83
+ }
84
+ }
85
+ }
86
+ // Write JSONL file
87
+ fs.writeFileSync(newIndexPath, jsonlLines.join('\n') + '\n');
88
+ // Backup old index
89
+ const backupPath = oldIndexPath + '.backup';
90
+ fs.renameSync(oldIndexPath, backupPath);
91
+ console.log(`āœ… Migration complete!`);
92
+ console.log(` - Converted ${jsonlLines.length} entries`);
93
+ console.log(` - New index: ${newIndexPath}`);
94
+ console.log(` - Backup: ${backupPath}`);
95
+ }
96
+ // Run if called directly
97
+ if (import.meta.url === `file://${process.argv[1]}`) {
98
+ migrateToJSONL();
99
+ }
@@ -0,0 +1 @@
1
+ export declare function restore(): Promise<void>;
@@ -0,0 +1,20 @@
1
+ import { readdirSync, renameSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import * as p from '@clack/prompts';
5
+ export async function restore() {
6
+ const trashDir = join(homedir(), '.claude-mem', 'trash');
7
+ const files = readdirSync(trashDir);
8
+ if (files.length === 0) {
9
+ console.log('Trash is empty');
10
+ return;
11
+ }
12
+ const file = await p.select({
13
+ message: 'Select file to restore:',
14
+ options: files.map(f => ({ value: f, label: f }))
15
+ });
16
+ if (p.isCancel(file))
17
+ return;
18
+ renameSync(join(trashDir, file), join(process.cwd(), file));
19
+ console.log(`Restored ${file}`);
20
+ }
@@ -0,0 +1 @@
1
+ export declare function status(): Promise<void>;
@@ -0,0 +1,136 @@
1
+ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { homedir } from 'os';
4
+ import { execSync } from 'child_process';
5
+ import { fileURLToPath } from 'url';
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ export async function status() {
8
+ console.log('šŸ” Claude Memory System Status Check');
9
+ console.log('=====================================\n');
10
+ console.log('šŸ“‚ Installed Hook Scripts:');
11
+ const claudeMemHooksDir = join(homedir(), '.claude-mem', 'hooks');
12
+ const preCompactScript = join(claudeMemHooksDir, 'pre-compact.js');
13
+ const sessionStartScript = join(claudeMemHooksDir, 'session-start.js');
14
+ const sessionEndScript = join(claudeMemHooksDir, 'session-end.js');
15
+ const checkScript = (path, name) => {
16
+ if (existsSync(path)) {
17
+ console.log(` āœ… ${name}: Found at ${path}`);
18
+ }
19
+ else {
20
+ console.log(` āŒ ${name}: Not found at ${path}`);
21
+ }
22
+ };
23
+ checkScript(preCompactScript, 'pre-compact.js');
24
+ checkScript(sessionStartScript, 'session-start.js');
25
+ checkScript(sessionEndScript, 'session-end.js');
26
+ console.log('');
27
+ console.log('āš™ļø Settings Configuration:');
28
+ const checkSettings = (name, path) => {
29
+ if (!existsSync(path)) {
30
+ console.log(` ā­ļø ${name}: No settings file`);
31
+ return;
32
+ }
33
+ console.log(` šŸ“‹ ${name}: ${path}`);
34
+ try {
35
+ const settings = JSON.parse(readFileSync(path, 'utf8'));
36
+ const hasPreCompact = settings.hooks?.PreCompact?.some((matcher) => matcher.hooks?.some((hook) => hook.command?.includes('pre-compact.js') || hook.command?.includes('claude-mem')));
37
+ const hasSessionStart = settings.hooks?.SessionStart?.some((matcher) => matcher.hooks?.some((hook) => hook.command?.includes('session-start.js') || hook.command?.includes('claude-mem')));
38
+ const hasSessionEnd = settings.hooks?.SessionEnd?.some((matcher) => matcher.hooks?.some((hook) => hook.command?.includes('session-end.js') || hook.command?.includes('claude-mem')));
39
+ console.log(` PreCompact: ${hasPreCompact ? 'āœ…' : 'āŒ'}`);
40
+ console.log(` SessionStart: ${hasSessionStart ? 'āœ…' : 'āŒ'}`);
41
+ console.log(` SessionEnd: ${hasSessionEnd ? 'āœ…' : 'āŒ'}`);
42
+ }
43
+ catch (error) {
44
+ console.log(` āš ļø Could not parse settings`);
45
+ }
46
+ };
47
+ checkSettings('Global', join(homedir(), '.claude', 'settings.json'));
48
+ checkSettings('Project', join(process.cwd(), '.claude', 'settings.json'));
49
+ console.log('');
50
+ console.log('šŸ“¦ Compressed Transcripts:');
51
+ const claudeProjectsDir = join(homedir(), '.claude', 'projects');
52
+ if (existsSync(claudeProjectsDir)) {
53
+ try {
54
+ let compressedCount = 0;
55
+ let archiveCount = 0;
56
+ const searchDir = (dir, depth = 0) => {
57
+ if (depth > 3)
58
+ return;
59
+ const files = readdirSync(dir);
60
+ for (const file of files) {
61
+ const fullPath = join(dir, file);
62
+ const stats = statSync(fullPath);
63
+ if (stats.isDirectory() && !file.startsWith('.')) {
64
+ searchDir(fullPath, depth + 1);
65
+ }
66
+ else if (file.endsWith('.jsonl.compressed')) {
67
+ compressedCount++;
68
+ }
69
+ else if (file.endsWith('.jsonl.archive')) {
70
+ archiveCount++;
71
+ }
72
+ }
73
+ };
74
+ searchDir(claudeProjectsDir);
75
+ console.log(` Compressed files: ${compressedCount}`);
76
+ console.log(` Archive files: ${archiveCount}`);
77
+ }
78
+ catch (error) {
79
+ console.log(` āš ļø Could not scan projects directory`);
80
+ }
81
+ }
82
+ else {
83
+ console.log(` ā„¹ļø No Claude projects directory found`);
84
+ }
85
+ console.log('');
86
+ console.log('šŸ”§ Runtime Environment:');
87
+ const checkCommand = (cmd, name) => {
88
+ try {
89
+ const version = execSync(`${cmd} --version`, { encoding: 'utf8' }).trim();
90
+ console.log(` āœ… ${name}: ${version}`);
91
+ }
92
+ catch {
93
+ console.log(` āŒ ${name}: Not found`);
94
+ }
95
+ };
96
+ checkCommand('node', 'Node.js');
97
+ checkCommand('bun', 'Bun');
98
+ console.log('');
99
+ console.log('🧠 Chroma Storage Status:');
100
+ console.log(' āœ… Storage backend: Chroma MCP');
101
+ console.log(' šŸ“ Data location: ~/.claude-mem/chroma');
102
+ console.log(' šŸ” Features: Vector search, semantic similarity, document storage');
103
+ console.log('');
104
+ console.log('šŸ“Š Summary:');
105
+ const globalPath = join(homedir(), '.claude', 'settings.json');
106
+ const projectPath = join(process.cwd(), '.claude', 'settings.json');
107
+ let isInstalled = false;
108
+ let installLocation = 'Not installed';
109
+ try {
110
+ if (existsSync(globalPath)) {
111
+ const settings = JSON.parse(readFileSync(globalPath, 'utf8'));
112
+ if (settings.hooks?.PreCompact || settings.hooks?.SessionStart || settings.hooks?.SessionEnd) {
113
+ isInstalled = true;
114
+ installLocation = 'Global';
115
+ }
116
+ }
117
+ if (existsSync(projectPath)) {
118
+ const settings = JSON.parse(readFileSync(projectPath, 'utf8'));
119
+ if (settings.hooks?.PreCompact || settings.hooks?.SessionStart || settings.hooks?.SessionEnd) {
120
+ isInstalled = true;
121
+ installLocation = installLocation === 'Global' ? 'Global + Project' : 'Project';
122
+ }
123
+ }
124
+ }
125
+ catch { }
126
+ if (isInstalled) {
127
+ console.log(` āœ… Claude Memory System is installed (${installLocation})`);
128
+ console.log('');
129
+ console.log('šŸ’” To test: Use /compact in Claude Code');
130
+ }
131
+ else {
132
+ console.log(` āŒ Claude Memory System is not installed`);
133
+ console.log('');
134
+ console.log('šŸ’” To install: claude-mem install');
135
+ }
136
+ }
@@ -0,0 +1,3 @@
1
+ export declare function emptyTrash(options?: {
2
+ force?: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,56 @@
1
+ import { rmSync, readdirSync, existsSync, statSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import * as p from '@clack/prompts';
5
+ export async function emptyTrash(options = {}) {
6
+ const trashDir = join(homedir(), '.claude-mem', 'trash');
7
+ // Check if trash directory exists
8
+ if (!existsSync(trashDir)) {
9
+ p.log.info('šŸ—‘ļø Trash is already empty');
10
+ return;
11
+ }
12
+ try {
13
+ const files = readdirSync(trashDir);
14
+ if (files.length === 0) {
15
+ p.log.info('šŸ—‘ļø Trash is already empty');
16
+ return;
17
+ }
18
+ // Count items
19
+ let folderCount = 0;
20
+ let fileCount = 0;
21
+ for (const file of files) {
22
+ const filePath = join(trashDir, file);
23
+ const stats = statSync(filePath);
24
+ if (stats.isDirectory()) {
25
+ folderCount++;
26
+ }
27
+ else {
28
+ fileCount++;
29
+ }
30
+ }
31
+ // Confirm deletion unless --force flag is used
32
+ if (!options.force) {
33
+ const confirm = await p.confirm({
34
+ message: `Permanently delete ${folderCount} folders and ${fileCount} files from trash?`,
35
+ initialValue: false
36
+ });
37
+ if (p.isCancel(confirm) || !confirm) {
38
+ p.log.info('Cancelled - trash not emptied');
39
+ return;
40
+ }
41
+ }
42
+ // Delete all files in trash
43
+ const s = p.spinner();
44
+ s.start('Emptying trash...');
45
+ for (const file of files) {
46
+ const filePath = join(trashDir, file);
47
+ rmSync(filePath, { recursive: true, force: true });
48
+ }
49
+ s.stop(`šŸ—‘ļø Trash emptied - permanently deleted ${folderCount} folders and ${fileCount} files`);
50
+ }
51
+ catch (error) {
52
+ p.log.error('Failed to empty trash');
53
+ console.error(error);
54
+ process.exit(1);
55
+ }
56
+ }
@@ -0,0 +1 @@
1
+ export declare function viewTrash(): Promise<void>;
@@ -0,0 +1,101 @@
1
+ import { readdirSync, statSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import * as p from '@clack/prompts';
5
+ function parseTrashName(filename) {
6
+ const lastDotIndex = filename.lastIndexOf('.');
7
+ if (lastDotIndex === -1)
8
+ return { name: filename, timestamp: 0 };
9
+ const timestamp = parseInt(filename.substring(lastDotIndex + 1));
10
+ if (isNaN(timestamp))
11
+ return { name: filename, timestamp: 0 };
12
+ return {
13
+ name: filename.substring(0, lastDotIndex),
14
+ timestamp
15
+ };
16
+ }
17
+ function formatSize(bytes) {
18
+ if (bytes === 0)
19
+ return '0 B';
20
+ const k = 1024;
21
+ const sizes = ['B', 'KB', 'MB', 'GB'];
22
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
23
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
24
+ }
25
+ function getDirectorySize(dirPath) {
26
+ let size = 0;
27
+ const files = readdirSync(dirPath);
28
+ for (const file of files) {
29
+ const filePath = join(dirPath, file);
30
+ const stats = statSync(filePath);
31
+ if (stats.isDirectory()) {
32
+ size += getDirectorySize(filePath);
33
+ }
34
+ else {
35
+ size += stats.size;
36
+ }
37
+ }
38
+ return size;
39
+ }
40
+ export async function viewTrash() {
41
+ const trashDir = join(homedir(), '.claude-mem', 'trash');
42
+ try {
43
+ const files = readdirSync(trashDir);
44
+ if (files.length === 0) {
45
+ p.log.info('šŸ—‘ļø Trash is empty');
46
+ return;
47
+ }
48
+ const items = files.map(file => {
49
+ const filePath = join(trashDir, file);
50
+ const stats = statSync(filePath);
51
+ const { name, timestamp } = parseTrashName(file);
52
+ const size = stats.isDirectory() ? getDirectorySize(filePath) : stats.size;
53
+ return {
54
+ originalName: name,
55
+ trashedName: file,
56
+ size,
57
+ trashedAt: new Date(timestamp),
58
+ isDirectory: stats.isDirectory()
59
+ };
60
+ });
61
+ // Sort by date, newest first
62
+ items.sort((a, b) => b.trashedAt.getTime() - a.trashedAt.getTime());
63
+ // Display header
64
+ console.log('\nšŸ—‘ļø Trash Contents\n');
65
+ console.log('─'.repeat(80));
66
+ // Display items
67
+ let totalSize = 0;
68
+ let folderCount = 0;
69
+ let fileCount = 0;
70
+ for (const item of items) {
71
+ totalSize += item.size;
72
+ if (item.isDirectory) {
73
+ folderCount++;
74
+ }
75
+ else {
76
+ fileCount++;
77
+ }
78
+ const type = item.isDirectory ? 'šŸ“' : 'šŸ“„';
79
+ const date = item.trashedAt.toLocaleString();
80
+ const size = formatSize(item.size);
81
+ console.log(`${type} ${item.originalName}`);
82
+ console.log(` Size: ${size} | Trashed: ${date}`);
83
+ console.log(` ID: ${item.trashedName}`);
84
+ console.log();
85
+ }
86
+ // Display summary
87
+ console.log('─'.repeat(80));
88
+ console.log(`Total: ${folderCount} folders, ${fileCount} files (${formatSize(totalSize)})`);
89
+ console.log('\nTo restore files: claude-mem restore');
90
+ console.log('To empty trash: claude-mem trash empty');
91
+ }
92
+ catch (error) {
93
+ if (error.code === 'ENOENT') {
94
+ p.log.info('šŸ—‘ļø Trash is empty');
95
+ }
96
+ else {
97
+ p.log.error('Failed to read trash directory');
98
+ console.error(error);
99
+ }
100
+ }
101
+ }