@contextmirror/claude-memory 0.2.1 ā 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 +58 -0
- 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 +28 -3
- package/dist/types/index.d.ts +7 -1
- package/dist/types/index.js +35 -2
- package/landing/index.html +478 -169
- package/package.json +2 -1
- package/.claude/settings.local.json +0 -10
|
@@ -5,27 +5,61 @@ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
|
5
5
|
import { join, basename } from 'path';
|
|
6
6
|
import { simpleGit } from 'simple-git';
|
|
7
7
|
import { DEFAULT_SCAN_OPTIONS } from '../types/index.js';
|
|
8
|
+
import { loadExistingContext } from './contextGenerator.js';
|
|
8
9
|
/**
|
|
9
10
|
* Scan a directory for projects and extract information
|
|
10
11
|
*/
|
|
11
12
|
export async function scanProjects(options = {}) {
|
|
12
13
|
const opts = { ...DEFAULT_SCAN_OPTIONS, ...options };
|
|
13
14
|
const projects = [];
|
|
15
|
+
const errors = [];
|
|
14
16
|
console.log(`š Scanning ${opts.rootDir} for projects...`);
|
|
17
|
+
// Verify root directory exists
|
|
18
|
+
if (!existsSync(opts.rootDir)) {
|
|
19
|
+
console.error(`ā Directory does not exist: ${opts.rootDir}`);
|
|
20
|
+
console.error(` Check the path and try again.`);
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
15
23
|
const candidates = findProjectRoots(opts.rootDir, opts.maxDepth, opts.ignore);
|
|
16
|
-
|
|
24
|
+
if (candidates.length === 0) {
|
|
25
|
+
console.log(` No project directories found in ${opts.rootDir}`);
|
|
26
|
+
console.log(` Looking for: package.json, Cargo.toml, pyproject.toml, go.mod, or .git`);
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
// Load excluded projects from existing context
|
|
30
|
+
const existingContext = loadExistingContext();
|
|
31
|
+
const excludedProjects = new Set(existingContext?.excludedProjects || []);
|
|
32
|
+
// Filter out excluded projects
|
|
33
|
+
const filteredCandidates = candidates.filter(path => {
|
|
34
|
+
if (excludedProjects.has(path)) {
|
|
35
|
+
console.log(` ā Skipping excluded: ${basename(path)}`);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
});
|
|
40
|
+
console.log(` Found ${filteredCandidates.length} candidate directories\n`);
|
|
41
|
+
if (excludedProjects.size > 0) {
|
|
42
|
+
console.log(` (${excludedProjects.size} excluded)\n`);
|
|
43
|
+
}
|
|
44
|
+
for (const projectPath of filteredCandidates) {
|
|
17
45
|
try {
|
|
18
46
|
const info = await analyzeProject(projectPath);
|
|
19
47
|
if (info) {
|
|
20
48
|
projects.push(info);
|
|
21
|
-
console.log(` ā
|
|
49
|
+
console.log(` ā ${info.name} (${info.language}) - ${info.techStack.join(', ') || 'no stack detected'}`);
|
|
22
50
|
}
|
|
23
51
|
}
|
|
24
52
|
catch (err) {
|
|
25
|
-
|
|
53
|
+
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
|
|
54
|
+
errors.push({ path: projectPath, error: errorMsg });
|
|
55
|
+
console.warn(` ā Failed: ${basename(projectPath)} - ${errorMsg}`);
|
|
26
56
|
}
|
|
27
57
|
}
|
|
28
|
-
console.log(`\nš
|
|
58
|
+
console.log(`\nš Results:`);
|
|
59
|
+
console.log(` ā
Successfully scanned: ${projects.length}`);
|
|
60
|
+
if (errors.length > 0) {
|
|
61
|
+
console.log(` ā ļø Failed to scan: ${errors.length}`);
|
|
62
|
+
}
|
|
29
63
|
return projects;
|
|
30
64
|
}
|
|
31
65
|
/**
|
|
@@ -77,23 +111,33 @@ async function analyzeProject(projectPath) {
|
|
|
77
111
|
const name = detectProjectName(projectPath);
|
|
78
112
|
const description = detectDescription(projectPath);
|
|
79
113
|
const { language, techStack } = detectTechStack(projectPath);
|
|
80
|
-
// Git info
|
|
114
|
+
// Git info with timeout to prevent hanging on large/slow repos
|
|
81
115
|
let lastActivity = new Date().toISOString();
|
|
82
116
|
let currentBranch = 'unknown';
|
|
83
117
|
let isDirty = false;
|
|
84
118
|
if (existsSync(join(projectPath, '.git'))) {
|
|
85
119
|
try {
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
120
|
+
// 10 second timeout for git operations
|
|
121
|
+
const git = simpleGit(projectPath, { timeout: { block: 10000 } });
|
|
122
|
+
// Wrap in Promise.race for extra safety
|
|
123
|
+
const gitOps = async () => {
|
|
124
|
+
const log = await git.log({ maxCount: 1 });
|
|
125
|
+
if (log.latest) {
|
|
126
|
+
lastActivity = log.latest.date;
|
|
127
|
+
}
|
|
128
|
+
const status = await git.status();
|
|
129
|
+
currentBranch = status.current || 'unknown';
|
|
130
|
+
isDirty = !status.isClean();
|
|
131
|
+
};
|
|
132
|
+
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Git timeout')), 10000));
|
|
133
|
+
await Promise.race([gitOps(), timeout]);
|
|
94
134
|
}
|
|
95
|
-
catch {
|
|
96
|
-
// Git operations failed, use defaults
|
|
135
|
+
catch (err) {
|
|
136
|
+
// Git operations failed or timed out, use defaults
|
|
137
|
+
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
|
|
138
|
+
if (errorMsg.includes('timeout') || errorMsg.includes('Timeout')) {
|
|
139
|
+
console.warn(` ā ļø Git timeout for ${basename(projectPath)} - using defaults`);
|
|
140
|
+
}
|
|
97
141
|
}
|
|
98
142
|
}
|
|
99
143
|
// Extract insights from CLAUDE.md if it exists
|
|
@@ -25,12 +25,33 @@ const MCP_SERVER_CONFIG = {
|
|
|
25
25
|
* Run the setup wizard
|
|
26
26
|
*/
|
|
27
27
|
export async function runSetupWizard(options = {}) {
|
|
28
|
-
const { projectsDir
|
|
28
|
+
const { projectsDir: providedDir, skipMcp = false, skipBriefing = false, interactive = true, } = options;
|
|
29
29
|
console.log('');
|
|
30
30
|
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
31
31
|
console.log('ā š§ Claude Memory - Setup Wizard ā');
|
|
32
32
|
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
33
33
|
console.log('');
|
|
34
|
+
// Determine projects directory
|
|
35
|
+
let projectsDir = providedDir || guessProjectsDir();
|
|
36
|
+
if (!projectsDir) {
|
|
37
|
+
console.log('š Step 0: Locate your projects\n');
|
|
38
|
+
console.log(' Could not auto-detect your projects directory.');
|
|
39
|
+
console.log(' Common locations like ~/Projects, ~/Code, ~/dev were not found.\n');
|
|
40
|
+
if (interactive) {
|
|
41
|
+
const answer = await ask(' Enter your projects directory path: ');
|
|
42
|
+
if (!answer || !existsSync(answer)) {
|
|
43
|
+
console.log('\n ā Invalid directory. Please run:');
|
|
44
|
+
console.log(' claude-memory setup -d /path/to/your/projects');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
projectsDir = answer;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
console.log(' ā Cannot continue without a projects directory.');
|
|
51
|
+
console.log(' Run: claude-memory setup -d /path/to/your/projects');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
34
55
|
// Step 1: Find projects
|
|
35
56
|
console.log('š Step 1: Scanning for projects\n');
|
|
36
57
|
console.log(` Looking in: ${projectsDir}`);
|
|
@@ -164,6 +185,7 @@ export async function runSetupWizard(options = {}) {
|
|
|
164
185
|
}
|
|
165
186
|
/**
|
|
166
187
|
* Guess the user's projects directory
|
|
188
|
+
* Returns null if we can't find a reasonable guess (better than scanning home!)
|
|
167
189
|
*/
|
|
168
190
|
function guessProjectsDir() {
|
|
169
191
|
const home = homedir();
|
|
@@ -178,14 +200,17 @@ function guessProjectsDir() {
|
|
|
178
200
|
join(home, 'src'),
|
|
179
201
|
join(home, 'repos'),
|
|
180
202
|
join(home, 'git'),
|
|
203
|
+
join(home, 'workspace'),
|
|
204
|
+
join(home, 'Workspace'),
|
|
181
205
|
];
|
|
182
206
|
for (const dir of candidates) {
|
|
183
207
|
if (existsSync(dir)) {
|
|
184
208
|
return dir;
|
|
185
209
|
}
|
|
186
210
|
}
|
|
187
|
-
//
|
|
188
|
-
|
|
211
|
+
// Don't fall back to home - that scans way too much and causes issues
|
|
212
|
+
// Return null and let the caller handle it
|
|
213
|
+
return null;
|
|
189
214
|
}
|
|
190
215
|
/**
|
|
191
216
|
* Save memory config to ~/.claude-memory/config.json
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core types for Claude Memory
|
|
3
3
|
*/
|
|
4
|
+
/** Current schema version - increment when making breaking changes */
|
|
5
|
+
export declare const SCHEMA_VERSION = 1;
|
|
4
6
|
export interface ProjectInfo {
|
|
5
7
|
/** Absolute path to project root */
|
|
6
8
|
path: string;
|
|
@@ -43,14 +45,18 @@ export interface ProjectInsight {
|
|
|
43
45
|
discoveredAt: string;
|
|
44
46
|
}
|
|
45
47
|
export interface GlobalContext {
|
|
48
|
+
/** Schema version for migration support */
|
|
49
|
+
schemaVersion: number;
|
|
46
50
|
/** When the global context was last updated */
|
|
47
51
|
lastUpdated: string;
|
|
48
52
|
/** All scanned projects */
|
|
49
53
|
projects: ProjectInfo[];
|
|
50
|
-
/** Cross-project insights */
|
|
54
|
+
/** Cross-project insights (preserved across rescans) */
|
|
51
55
|
insights: GlobalInsight[];
|
|
52
56
|
/** User preferences/patterns observed */
|
|
53
57
|
userPatterns: UserPattern[];
|
|
58
|
+
/** Projects to exclude from scanning (by path) */
|
|
59
|
+
excludedProjects?: string[];
|
|
54
60
|
}
|
|
55
61
|
export interface GlobalInsight {
|
|
56
62
|
/** Insight content */
|
package/dist/types/index.js
CHANGED
|
@@ -1,10 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core types for Claude Memory
|
|
3
3
|
*/
|
|
4
|
+
/** Current schema version - increment when making breaking changes */
|
|
5
|
+
export const SCHEMA_VERSION = 1;
|
|
6
|
+
/**
|
|
7
|
+
* Get a sensible default projects directory
|
|
8
|
+
* Checks common locations, doesn't fall back to home
|
|
9
|
+
*/
|
|
10
|
+
function getDefaultProjectsDir() {
|
|
11
|
+
const home = process.env.HOME || '';
|
|
12
|
+
if (!home)
|
|
13
|
+
return './projects';
|
|
14
|
+
const candidates = [
|
|
15
|
+
`${home}/Projects`,
|
|
16
|
+
`${home}/Project`,
|
|
17
|
+
`${home}/projects`,
|
|
18
|
+
`${home}/Code`,
|
|
19
|
+
`${home}/code`,
|
|
20
|
+
`${home}/dev`,
|
|
21
|
+
];
|
|
22
|
+
// Import fs dynamically to avoid issues
|
|
23
|
+
try {
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
for (const dir of candidates) {
|
|
26
|
+
if (fs.existsSync(dir)) {
|
|
27
|
+
return dir;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// fs not available, use first candidate
|
|
33
|
+
}
|
|
34
|
+
// Default to ~/Projects (most common convention)
|
|
35
|
+
return `${home}/Projects`;
|
|
36
|
+
}
|
|
4
37
|
export const DEFAULT_SCAN_OPTIONS = {
|
|
5
|
-
rootDir:
|
|
38
|
+
rootDir: getDefaultProjectsDir(),
|
|
6
39
|
maxDepth: 2,
|
|
7
|
-
ignore: ['node_modules', '.git', 'dist', 'build', '__pycache__', 'target'],
|
|
40
|
+
ignore: ['node_modules', '.git', 'dist', 'build', '__pycache__', 'target', '.venv', 'venv', 'coverage', '.next'],
|
|
8
41
|
generateClaudeMd: false,
|
|
9
42
|
overwriteClaudeMd: false,
|
|
10
43
|
};
|