@contextmirror/claude-memory 0.4.2 → 0.4.4
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/LICENSE +21 -0
- package/README.md +12 -1
- package/dist/__tests__/codeSearch.test.d.ts +1 -0
- package/dist/__tests__/codeSearch.test.js +65 -0
- package/dist/__tests__/license.test.d.ts +1 -0
- package/dist/__tests__/license.test.js +28 -0
- package/dist/__tests__/stalenessDetector.test.d.ts +1 -0
- package/dist/__tests__/stalenessDetector.test.js +60 -0
- package/dist/__tests__/updateChecker.test.d.ts +1 -0
- package/dist/__tests__/updateChecker.test.js +43 -0
- package/dist/cli.js +9 -2
- package/dist/constants.d.ts +25 -0
- package/dist/constants.js +53 -0
- package/dist/license/index.d.ts +7 -3
- package/dist/license/index.js +73 -15
- package/dist/mcp/server.js +25 -18
- package/dist/utils/codeSearch.d.ts +32 -0
- package/dist/utils/codeSearch.js +187 -0
- package/package.json +20 -1
- package/landing/index.html +0 -764
|
@@ -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.
|
|
3
|
+
"version": "0.4.4",
|
|
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/nayballs/claude-memory.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/nayballs/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",
|