@contextmirror/claude-memory 0.4.2 → 0.4.3

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,187 @@
1
+ /**
2
+ * Code search utility for Pro feature
3
+ */
4
+ import { readdirSync, readFileSync, statSync } from 'fs';
5
+ import { join, extname, relative } from 'path';
6
+ import { IGNORE_DIRS } from '../constants.js';
7
+ const DEFAULT_OPTIONS = {
8
+ maxResults: 50,
9
+ caseSensitive: false,
10
+ contextLines: 1,
11
+ };
12
+ // File extensions to search
13
+ const SEARCHABLE_EXTENSIONS = new Set([
14
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
15
+ '.py', '.pyw',
16
+ '.rs',
17
+ '.go',
18
+ '.java', '.kt', '.scala',
19
+ '.c', '.cpp', '.h', '.hpp',
20
+ '.rb',
21
+ '.php',
22
+ '.swift',
23
+ '.cs',
24
+ '.vue', '.svelte',
25
+ '.json', '.yaml', '.yml', '.toml',
26
+ '.md', '.txt',
27
+ '.sql',
28
+ '.sh', '.bash', '.zsh',
29
+ '.css', '.scss', '.sass', '.less',
30
+ '.html', '.htm',
31
+ '.xml',
32
+ ]);
33
+ // Max file size to search (1MB)
34
+ const MAX_FILE_SIZE = 1024 * 1024;
35
+ /**
36
+ * Check if a file should be searched based on extension
37
+ */
38
+ function shouldSearchFile(filePath, filePattern) {
39
+ const ext = extname(filePath).toLowerCase();
40
+ // If a pattern is specified, use glob-like matching
41
+ if (filePattern) {
42
+ if (filePattern.startsWith('*.')) {
43
+ const patternExt = filePattern.slice(1);
44
+ return ext === patternExt;
45
+ }
46
+ return filePath.includes(filePattern);
47
+ }
48
+ return SEARCHABLE_EXTENSIONS.has(ext);
49
+ }
50
+ /**
51
+ * Search for a pattern in a file
52
+ */
53
+ function searchFile(filePath, query, options) {
54
+ const results = [];
55
+ try {
56
+ const stat = statSync(filePath);
57
+ if (stat.size > MAX_FILE_SIZE) {
58
+ return results;
59
+ }
60
+ const content = readFileSync(filePath, 'utf-8');
61
+ const lines = content.split('\n');
62
+ const searchQuery = options.caseSensitive ? query : query.toLowerCase();
63
+ for (let i = 0; i < lines.length; i++) {
64
+ const line = lines[i];
65
+ const searchLine = options.caseSensitive ? line : line.toLowerCase();
66
+ if (searchLine.includes(searchQuery)) {
67
+ const contextLines = options.contextLines || 1;
68
+ const beforeLines = lines.slice(Math.max(0, i - contextLines), i);
69
+ const afterLines = lines.slice(i + 1, Math.min(lines.length, i + 1 + contextLines));
70
+ results.push({
71
+ line: i + 1,
72
+ content: line.trim(),
73
+ context: {
74
+ before: beforeLines.join('\n'),
75
+ after: afterLines.join('\n'),
76
+ },
77
+ });
78
+ }
79
+ }
80
+ }
81
+ catch {
82
+ // Skip files that can't be read
83
+ }
84
+ return results;
85
+ }
86
+ /**
87
+ * Recursively search a directory
88
+ */
89
+ function searchDirectory(dir, query, projectName, projectPath, options, results) {
90
+ if (results.length >= (options.maxResults || DEFAULT_OPTIONS.maxResults)) {
91
+ return;
92
+ }
93
+ try {
94
+ const entries = readdirSync(dir);
95
+ for (const entry of entries) {
96
+ if (results.length >= (options.maxResults || DEFAULT_OPTIONS.maxResults)) {
97
+ return;
98
+ }
99
+ if (IGNORE_DIRS.includes(entry) || entry.startsWith('.')) {
100
+ continue;
101
+ }
102
+ const fullPath = join(dir, entry);
103
+ try {
104
+ const stat = statSync(fullPath);
105
+ if (stat.isDirectory()) {
106
+ searchDirectory(fullPath, query, projectName, projectPath, options, results);
107
+ }
108
+ else if (stat.isFile() && shouldSearchFile(fullPath, options.filePattern)) {
109
+ const fileResults = searchFile(fullPath, query, options);
110
+ for (const result of fileResults) {
111
+ if (results.length >= (options.maxResults || DEFAULT_OPTIONS.maxResults)) {
112
+ return;
113
+ }
114
+ results.push({
115
+ project: projectName,
116
+ projectPath,
117
+ file: fullPath,
118
+ relativePath: relative(projectPath, fullPath),
119
+ line: result.line,
120
+ content: result.content,
121
+ context: result.context,
122
+ });
123
+ }
124
+ }
125
+ }
126
+ catch {
127
+ // Skip inaccessible files
128
+ }
129
+ }
130
+ }
131
+ catch {
132
+ // Skip inaccessible directories
133
+ }
134
+ }
135
+ /**
136
+ * Search for code across multiple projects
137
+ */
138
+ export function searchCode(projects, query, options = {}) {
139
+ const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
140
+ const results = [];
141
+ for (const project of projects) {
142
+ if (results.length >= mergedOptions.maxResults) {
143
+ break;
144
+ }
145
+ searchDirectory(project.path, query, project.name, project.path, mergedOptions, results);
146
+ }
147
+ return results;
148
+ }
149
+ /**
150
+ * Format search results for display
151
+ */
152
+ export function formatSearchResults(results, query) {
153
+ if (results.length === 0) {
154
+ return `No results found for "${query}".`;
155
+ }
156
+ const lines = [
157
+ `# Code Search Results for "${query}"`,
158
+ '',
159
+ `Found ${results.length} result${results.length === 1 ? '' : 's'}:`,
160
+ '',
161
+ ];
162
+ // Group by project
163
+ const byProject = new Map();
164
+ for (const result of results) {
165
+ const existing = byProject.get(result.project) || [];
166
+ existing.push(result);
167
+ byProject.set(result.project, existing);
168
+ }
169
+ for (const [project, projectResults] of byProject) {
170
+ lines.push(`## ${project}`);
171
+ lines.push('');
172
+ for (const result of projectResults) {
173
+ lines.push(`### ${result.relativePath}:${result.line}`);
174
+ lines.push('```');
175
+ if (result.context.before) {
176
+ lines.push(result.context.before);
177
+ }
178
+ lines.push(`> ${result.content}`);
179
+ if (result.context.after) {
180
+ lines.push(result.context.after);
181
+ }
182
+ lines.push('```');
183
+ lines.push('');
184
+ }
185
+ }
186
+ return lines.join('\n');
187
+ }
package/package.json CHANGED
@@ -1,12 +1,28 @@
1
1
  {
2
2
  "name": "@contextmirror/claude-memory",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Cross-project memory for Claude Code - know about all your projects",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
7
7
  "bin": {
8
8
  "claude-memory": "dist/cli.js"
9
9
  },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/contextmirror/claude-memory.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/contextmirror/claude-memory/issues"
16
+ },
17
+ "homepage": "https://claude-memory.dev",
18
+ "engines": {
19
+ "node": ">=18.0.0"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
10
26
  "scripts": {
11
27
  "build": "tsc",
12
28
  "dev": "tsc --watch",
@@ -34,6 +50,9 @@
34
50
  ],
35
51
  "author": "Nathan",
36
52
  "license": "MIT",
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
37
56
  "dependencies": {
38
57
  "@modelcontextprotocol/sdk": "^1.0.0",
39
58
  "commander": "^12.1.0",