@contextmirror/claude-memory 0.1.0 → 0.2.1
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/briefing/briefingGenerator.d.ts +72 -0
- package/dist/briefing/briefingGenerator.js +593 -0
- package/dist/cli.js +92 -23
- package/dist/mcp/server.js +159 -12
- package/dist/setup/setupWizard.d.ts +23 -0
- package/dist/setup/setupWizard.js +248 -0
- package/dist/types/index.d.ts +29 -0
- package/dist/types/index.js +6 -0
- package/landing/index.html +455 -0
- package/package.json +4 -2
package/dist/cli.js
CHANGED
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
* claude-memory mcp Start MCP server
|
|
10
10
|
*/
|
|
11
11
|
import { Command } from 'commander';
|
|
12
|
-
import { readFileSync, existsSync } from 'fs';
|
|
12
|
+
import { readFileSync, existsSync, writeFileSync } from 'fs';
|
|
13
13
|
import { join } from 'path';
|
|
14
14
|
import { homedir } from 'os';
|
|
15
15
|
import { scanProjects } from './scanner/projectScanner.js';
|
|
16
16
|
import { generateGlobalContext, writeGlobalContext, getMemoryDir } from './scanner/contextGenerator.js';
|
|
17
|
+
import { generateBriefing, briefingToClaudeMd } from './briefing/briefingGenerator.js';
|
|
18
|
+
import { runSetupWizard } from './setup/setupWizard.js';
|
|
17
19
|
const program = new Command();
|
|
18
20
|
program
|
|
19
21
|
.name('claude-memory')
|
|
@@ -87,34 +89,85 @@ program
|
|
|
87
89
|
console.log(` CLAUDE.md: ${project.hasFiles.claudeMd ? '✓' : '✗'}`);
|
|
88
90
|
console.log(` README.md: ${project.hasFiles.readme ? '✓' : '✗'}`);
|
|
89
91
|
});
|
|
90
|
-
//
|
|
92
|
+
// Briefing command - deep project analysis and CLAUDE.md generation
|
|
93
|
+
program
|
|
94
|
+
.command('briefing')
|
|
95
|
+
.description('Generate a detailed CLAUDE.md briefing for a project')
|
|
96
|
+
.argument('[directory]', 'Project directory', process.cwd())
|
|
97
|
+
.option('-o, --output <file>', 'Output file (default: CLAUDE.md in project)')
|
|
98
|
+
.option('--stdout', 'Print to stdout instead of file')
|
|
99
|
+
.option('--force', 'Overwrite existing CLAUDE.md')
|
|
100
|
+
.action(async (directory, options) => {
|
|
101
|
+
const projectPath = directory.startsWith('/') ? directory : join(process.cwd(), directory);
|
|
102
|
+
console.log('🧠 Claude Memory - Project Briefing\n');
|
|
103
|
+
console.log(`📁 Analyzing: ${projectPath}\n`);
|
|
104
|
+
// Check if project exists
|
|
105
|
+
if (!existsSync(projectPath)) {
|
|
106
|
+
console.log(`❌ Directory not found: ${projectPath}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Check for existing CLAUDE.md
|
|
110
|
+
const claudeMdPath = options.output || join(projectPath, 'CLAUDE.md');
|
|
111
|
+
if (existsSync(claudeMdPath) && !options.force && !options.stdout) {
|
|
112
|
+
console.log(`⚠️ CLAUDE.md already exists at ${claudeMdPath}`);
|
|
113
|
+
console.log(' Use --force to overwrite, or --stdout to preview');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
console.log('📊 Scanning project structure...');
|
|
118
|
+
const briefing = await generateBriefing({ projectPath });
|
|
119
|
+
console.log(` Name: ${briefing.name}`);
|
|
120
|
+
console.log(` Language: ${briefing.language}`);
|
|
121
|
+
console.log(` Stack: ${briefing.techStack.join(', ')}`);
|
|
122
|
+
console.log(` Entry points: ${briefing.entryPoints.length}`);
|
|
123
|
+
console.log(` Patterns detected: ${briefing.patterns.length}`);
|
|
124
|
+
console.log(` Dependencies: ${briefing.dependencies.length}`);
|
|
125
|
+
console.log('\n📝 Generating CLAUDE.md...');
|
|
126
|
+
const markdown = briefingToClaudeMd(briefing);
|
|
127
|
+
if (options.stdout) {
|
|
128
|
+
console.log('\n' + '='.repeat(60) + '\n');
|
|
129
|
+
console.log(markdown);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
writeFileSync(claudeMdPath, markdown, 'utf-8');
|
|
133
|
+
console.log(`\n✅ Written to: ${claudeMdPath}`);
|
|
134
|
+
}
|
|
135
|
+
console.log('\n💡 Pro tip: Run `claude-memory scan` to update your global context');
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
console.error('❌ Failed to generate briefing:', err);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
// Init command - alias for briefing (backwards compatibility)
|
|
91
142
|
program
|
|
92
143
|
.command('init')
|
|
93
|
-
.description('Generate CLAUDE.md for the current project')
|
|
144
|
+
.description('Generate CLAUDE.md for the current project (alias for briefing)')
|
|
94
145
|
.action(async () => {
|
|
146
|
+
// Just run the briefing command
|
|
95
147
|
const projectPath = process.cwd();
|
|
96
|
-
console.log('🧠 Claude Memory -
|
|
97
|
-
console.log(`📁
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (projects.length === 0) {
|
|
104
|
-
console.log('❌ No project detected in current directory');
|
|
105
|
-
console.log(' (Looking for package.json, Cargo.toml, pyproject.toml, or .git)');
|
|
148
|
+
console.log('🧠 Claude Memory - Project Briefing\n');
|
|
149
|
+
console.log(`📁 Analyzing: ${projectPath}\n`);
|
|
150
|
+
const claudeMdPath = join(projectPath, 'CLAUDE.md');
|
|
151
|
+
if (existsSync(claudeMdPath)) {
|
|
152
|
+
console.log(`⚠️ CLAUDE.md already exists`);
|
|
153
|
+
console.log(' Use `claude-memory briefing --force` to overwrite');
|
|
154
|
+
console.log(' Use `claude-memory briefing --stdout` to preview');
|
|
106
155
|
return;
|
|
107
156
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
157
|
+
try {
|
|
158
|
+
console.log('📊 Scanning project structure...');
|
|
159
|
+
const briefing = await generateBriefing({ projectPath });
|
|
160
|
+
console.log(` Name: ${briefing.name}`);
|
|
161
|
+
console.log(` Language: ${briefing.language}`);
|
|
162
|
+
console.log(` Stack: ${briefing.techStack.join(', ')}`);
|
|
163
|
+
console.log('\n📝 Generating CLAUDE.md...');
|
|
164
|
+
const markdown = briefingToClaudeMd(briefing);
|
|
165
|
+
writeFileSync(claudeMdPath, markdown, 'utf-8');
|
|
166
|
+
console.log(`\n✅ Written to: ${claudeMdPath}`);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
console.error('❌ Failed to generate briefing:', err);
|
|
170
|
+
}
|
|
118
171
|
});
|
|
119
172
|
// MCP command - start the MCP server
|
|
120
173
|
program
|
|
@@ -124,6 +177,22 @@ program
|
|
|
124
177
|
// Import and run the MCP server
|
|
125
178
|
await import('./mcp/server.js');
|
|
126
179
|
});
|
|
180
|
+
// Setup command - interactive setup wizard
|
|
181
|
+
program
|
|
182
|
+
.command('setup')
|
|
183
|
+
.description('Run the interactive setup wizard')
|
|
184
|
+
.option('-d, --directory <dir>', 'Projects directory to scan')
|
|
185
|
+
.option('--skip-mcp', 'Skip MCP configuration')
|
|
186
|
+
.option('--skip-briefing', 'Skip CLAUDE.md generation')
|
|
187
|
+
.option('--non-interactive', 'Run without prompts (useful for CI)')
|
|
188
|
+
.action(async (options) => {
|
|
189
|
+
await runSetupWizard({
|
|
190
|
+
projectsDir: options.directory,
|
|
191
|
+
skipMcp: options.skipMcp,
|
|
192
|
+
skipBriefing: options.skipBriefing,
|
|
193
|
+
interactive: !options.nonInteractive,
|
|
194
|
+
});
|
|
195
|
+
});
|
|
127
196
|
function loadContext() {
|
|
128
197
|
const contextPath = join(getMemoryDir(), 'context.json');
|
|
129
198
|
if (!existsSync(contextPath)) {
|
package/dist/mcp/server.js
CHANGED
|
@@ -13,6 +13,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextpro
|
|
|
13
13
|
import { readFileSync, existsSync, readdirSync, statSync, writeFileSync } from 'fs';
|
|
14
14
|
import { join } from 'path';
|
|
15
15
|
import { homedir } from 'os';
|
|
16
|
+
import { DEFAULT_MEMORY_CONFIG } from '../types/index.js';
|
|
16
17
|
const MEMORY_DIR = join(homedir(), '.claude-memory');
|
|
17
18
|
// Tool definitions
|
|
18
19
|
const tools = [
|
|
@@ -21,7 +22,12 @@ const tools = [
|
|
|
21
22
|
description: 'Get an overview of all projects the user is working on. Returns project names, descriptions, tech stacks, and recent activity. Use this at the start of conversations to understand the user\'s development environment.',
|
|
22
23
|
inputSchema: {
|
|
23
24
|
type: 'object',
|
|
24
|
-
properties: {
|
|
25
|
+
properties: {
|
|
26
|
+
cwd: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Current working directory. If provided, will check if this is a known project or a new project that needs setup.',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
25
31
|
required: [],
|
|
26
32
|
},
|
|
27
33
|
},
|
|
@@ -99,19 +105,150 @@ function loadContext() {
|
|
|
99
105
|
return null;
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
|
-
function
|
|
108
|
+
function loadConfig() {
|
|
109
|
+
const configPath = join(MEMORY_DIR, 'config.json');
|
|
110
|
+
if (!existsSync(configPath)) {
|
|
111
|
+
return DEFAULT_MEMORY_CONFIG;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return DEFAULT_MEMORY_CONFIG;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function saveConfig(config) {
|
|
121
|
+
const configPath = join(MEMORY_DIR, 'config.json');
|
|
122
|
+
config.lastUpdated = new Date().toISOString();
|
|
123
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check if a directory looks like a project (has project markers)
|
|
127
|
+
*/
|
|
128
|
+
function looksLikeProject(dir) {
|
|
129
|
+
try {
|
|
130
|
+
const entries = readdirSync(dir);
|
|
131
|
+
return (entries.includes('package.json') ||
|
|
132
|
+
entries.includes('Cargo.toml') ||
|
|
133
|
+
entries.includes('pyproject.toml') ||
|
|
134
|
+
entries.includes('go.mod') ||
|
|
135
|
+
entries.includes('.git') ||
|
|
136
|
+
entries.includes('requirements.txt'));
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Check if a path is under any of the watched directories
|
|
144
|
+
*/
|
|
145
|
+
function isUnderWatchedDir(path, watchedDirs) {
|
|
146
|
+
const normalizedPath = path.replace(/\/$/, '');
|
|
147
|
+
return watchedDirs.some(watchedDir => {
|
|
148
|
+
const normalizedWatched = watchedDir.replace(/\/$/, '');
|
|
149
|
+
return normalizedPath.startsWith(normalizedWatched);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Detect the status of the current working directory
|
|
154
|
+
*/
|
|
155
|
+
function detectCurrentDirectoryStatus(cwd, context, config) {
|
|
156
|
+
const normalizedCwd = cwd.replace(/\/$/, '');
|
|
157
|
+
// Check if it's a known project
|
|
158
|
+
if (context) {
|
|
159
|
+
const knownProject = context.projects.find(p => normalizedCwd === p.path.replace(/\/$/, '') ||
|
|
160
|
+
normalizedCwd.startsWith(p.path.replace(/\/$/, '') + '/'));
|
|
161
|
+
if (knownProject) {
|
|
162
|
+
return {
|
|
163
|
+
path: cwd,
|
|
164
|
+
status: 'known_project',
|
|
165
|
+
projectName: knownProject.name,
|
|
166
|
+
needsSetup: !knownProject.hasFiles.claudeMd,
|
|
167
|
+
suggestedAction: knownProject.hasFiles.claudeMd ? 'none' : 'offer_setup',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Check if it's under a watched directory
|
|
172
|
+
if (config.autoDetectNewProjects && isUnderWatchedDir(cwd, config.watchedDirs)) {
|
|
173
|
+
// It's under a watched dir but not a known project
|
|
174
|
+
if (looksLikeProject(cwd)) {
|
|
175
|
+
return {
|
|
176
|
+
path: cwd,
|
|
177
|
+
status: 'new_project',
|
|
178
|
+
needsSetup: true,
|
|
179
|
+
suggestedAction: 'offer_setup',
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
// Empty or non-project directory under watched area
|
|
184
|
+
return {
|
|
185
|
+
path: cwd,
|
|
186
|
+
status: 'new_project',
|
|
187
|
+
needsSetup: true,
|
|
188
|
+
suggestedAction: 'offer_setup',
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Not under watched directories
|
|
193
|
+
if (looksLikeProject(cwd)) {
|
|
194
|
+
return {
|
|
195
|
+
path: cwd,
|
|
196
|
+
status: 'outside_workspace',
|
|
197
|
+
needsSetup: false,
|
|
198
|
+
suggestedAction: 'offer_scan',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
path: cwd,
|
|
203
|
+
status: 'not_a_project',
|
|
204
|
+
needsSetup: false,
|
|
205
|
+
suggestedAction: 'none',
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function handleGetGlobalContext(cwd) {
|
|
103
209
|
const context = loadContext();
|
|
210
|
+
const config = loadConfig();
|
|
211
|
+
const lines = [];
|
|
212
|
+
// If cwd is provided, check the current directory status first
|
|
213
|
+
if (cwd) {
|
|
214
|
+
const cwdStatus = detectCurrentDirectoryStatus(cwd, context, config);
|
|
215
|
+
lines.push('# Current Directory Status');
|
|
216
|
+
lines.push('');
|
|
217
|
+
lines.push(`**Path:** ${cwdStatus.path}`);
|
|
218
|
+
lines.push(`**Status:** ${cwdStatus.status}`);
|
|
219
|
+
if (cwdStatus.projectName) {
|
|
220
|
+
lines.push(`**Project:** ${cwdStatus.projectName}`);
|
|
221
|
+
}
|
|
222
|
+
if (cwdStatus.needsSetup) {
|
|
223
|
+
lines.push(`**Needs Setup:** Yes`);
|
|
224
|
+
}
|
|
225
|
+
// Add actionable guidance for Claude
|
|
226
|
+
if (cwdStatus.suggestedAction === 'offer_setup') {
|
|
227
|
+
lines.push('');
|
|
228
|
+
lines.push('> 🆕 **New Project Detected!** This directory is not yet registered.');
|
|
229
|
+
lines.push('> Ask the user if they would like you to:');
|
|
230
|
+
lines.push('> 1. Generate a CLAUDE.md file for this project');
|
|
231
|
+
lines.push('> 2. Add this project to the memory index');
|
|
232
|
+
}
|
|
233
|
+
else if (cwdStatus.suggestedAction === 'offer_scan') {
|
|
234
|
+
lines.push('');
|
|
235
|
+
lines.push('> ℹ️ This looks like a project but is outside your watched directories.');
|
|
236
|
+
lines.push('> You may want to run `claude-memory scan` to add it.');
|
|
237
|
+
}
|
|
238
|
+
lines.push('');
|
|
239
|
+
lines.push('---');
|
|
240
|
+
lines.push('');
|
|
241
|
+
}
|
|
104
242
|
if (!context) {
|
|
105
|
-
|
|
243
|
+
lines.push('No projects scanned yet. Run `claude-memory scan` first.');
|
|
244
|
+
return lines.join('\n');
|
|
106
245
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
'',
|
|
114
|
-
];
|
|
246
|
+
lines.push('# Your Development Environment');
|
|
247
|
+
lines.push('');
|
|
248
|
+
lines.push(`Last scanned: ${new Date(context.lastUpdated).toLocaleString()}`);
|
|
249
|
+
lines.push('');
|
|
250
|
+
lines.push('## Active Projects');
|
|
251
|
+
lines.push('');
|
|
115
252
|
// Sort by activity
|
|
116
253
|
const sorted = [...context.projects].sort((a, b) => new Date(b.lastActivity).getTime() - new Date(a.lastActivity).getTime());
|
|
117
254
|
for (const p of sorted) {
|
|
@@ -123,6 +260,16 @@ function handleGetGlobalContext() {
|
|
|
123
260
|
lines.push(`- **Description:** ${p.description}`);
|
|
124
261
|
lines.push('');
|
|
125
262
|
}
|
|
263
|
+
// Add config info
|
|
264
|
+
if (config.watchedDirs.length > 0) {
|
|
265
|
+
lines.push('## Watched Directories');
|
|
266
|
+
lines.push('');
|
|
267
|
+
for (const dir of config.watchedDirs) {
|
|
268
|
+
lines.push(`- ${dir}`);
|
|
269
|
+
}
|
|
270
|
+
lines.push('');
|
|
271
|
+
lines.push(`Auto-detect new projects: ${config.autoDetectNewProjects ? 'Yes' : 'No'}`);
|
|
272
|
+
}
|
|
126
273
|
return lines.join('\n');
|
|
127
274
|
}
|
|
128
275
|
function handleGetProjectSummary(projectQuery) {
|
|
@@ -351,7 +498,7 @@ async function main() {
|
|
|
351
498
|
let result;
|
|
352
499
|
switch (name) {
|
|
353
500
|
case 'get_global_context':
|
|
354
|
-
result = handleGetGlobalContext();
|
|
501
|
+
result = handleGetGlobalContext(args.cwd);
|
|
355
502
|
break;
|
|
356
503
|
case 'get_project_summary':
|
|
357
504
|
result = handleGetProjectSummary(args.project);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup Wizard - Runs after npm install to configure claude-memory
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* 1. Scans for projects
|
|
6
|
+
* 2. Offers to generate CLAUDE.md for projects without one
|
|
7
|
+
* 3. Auto-configures MCP in ~/.claude.json
|
|
8
|
+
*/
|
|
9
|
+
interface SetupOptions {
|
|
10
|
+
projectsDir?: string;
|
|
11
|
+
skipMcp?: boolean;
|
|
12
|
+
skipBriefing?: boolean;
|
|
13
|
+
interactive?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run the setup wizard
|
|
17
|
+
*/
|
|
18
|
+
export declare function runSetupWizard(options?: SetupOptions): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Check if this is running as a postinstall script
|
|
21
|
+
*/
|
|
22
|
+
export declare function isPostInstall(): boolean;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup Wizard - Runs after npm install to configure claude-memory
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* 1. Scans for projects
|
|
6
|
+
* 2. Offers to generate CLAUDE.md for projects without one
|
|
7
|
+
* 3. Auto-configures MCP in ~/.claude.json
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { homedir } from 'os';
|
|
12
|
+
import { createInterface } from 'readline';
|
|
13
|
+
import { scanProjects } from '../scanner/projectScanner.js';
|
|
14
|
+
import { generateGlobalContext, writeGlobalContext } from '../scanner/contextGenerator.js';
|
|
15
|
+
import { generateBriefing, briefingToClaudeMd } from '../briefing/briefingGenerator.js';
|
|
16
|
+
import { DEFAULT_MEMORY_CONFIG } from '../types/index.js';
|
|
17
|
+
const CLAUDE_JSON_PATH = join(homedir(), '.claude.json');
|
|
18
|
+
const MEMORY_DIR = join(homedir(), '.claude-memory');
|
|
19
|
+
const CONFIG_PATH = join(MEMORY_DIR, 'config.json');
|
|
20
|
+
const MCP_SERVER_CONFIG = {
|
|
21
|
+
command: 'claude-memory',
|
|
22
|
+
args: ['mcp'],
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Run the setup wizard
|
|
26
|
+
*/
|
|
27
|
+
export async function runSetupWizard(options = {}) {
|
|
28
|
+
const { projectsDir = guessProjectsDir(), skipMcp = false, skipBriefing = false, interactive = true, } = options;
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log('╔══════════════════════════════════════════════════════════╗');
|
|
31
|
+
console.log('║ 🧠 Claude Memory - Setup Wizard ║');
|
|
32
|
+
console.log('╚══════════════════════════════════════════════════════════╝');
|
|
33
|
+
console.log('');
|
|
34
|
+
// Step 1: Find projects
|
|
35
|
+
console.log('📍 Step 1: Scanning for projects\n');
|
|
36
|
+
console.log(` Looking in: ${projectsDir}`);
|
|
37
|
+
const projects = await scanProjects({
|
|
38
|
+
rootDir: projectsDir,
|
|
39
|
+
maxDepth: 2,
|
|
40
|
+
});
|
|
41
|
+
if (projects.length === 0) {
|
|
42
|
+
console.log('\n ⚠️ No projects found.');
|
|
43
|
+
console.log(` Make sure you have projects in ${projectsDir}`);
|
|
44
|
+
console.log(' Or run: claude-memory scan /path/to/your/projects');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log(`\n ✅ Found ${projects.length} project(s):\n`);
|
|
48
|
+
const projectsWithoutClaudeMd = projects.filter(p => !p.hasFiles.claudeMd);
|
|
49
|
+
const projectsWithClaudeMd = projects.filter(p => p.hasFiles.claudeMd);
|
|
50
|
+
for (const p of projectsWithClaudeMd) {
|
|
51
|
+
console.log(` ✓ ${p.name} (${p.techStack.join(', ')}) - Has CLAUDE.md`);
|
|
52
|
+
}
|
|
53
|
+
for (const p of projectsWithoutClaudeMd) {
|
|
54
|
+
console.log(` ○ ${p.name} (${p.techStack.join(', ')}) - No CLAUDE.md`);
|
|
55
|
+
}
|
|
56
|
+
// Step 2: Generate CLAUDE.md for projects without one
|
|
57
|
+
if (!skipBriefing && projectsWithoutClaudeMd.length > 0) {
|
|
58
|
+
console.log('\n📍 Step 2: Generate CLAUDE.md files\n');
|
|
59
|
+
if (interactive) {
|
|
60
|
+
const answer = await ask(` Generate CLAUDE.md for ${projectsWithoutClaudeMd.length} project(s)? (Y/n) `);
|
|
61
|
+
if (answer.toLowerCase() !== 'n') {
|
|
62
|
+
for (const project of projectsWithoutClaudeMd) {
|
|
63
|
+
console.log(`\n 📝 Generating for ${project.name}...`);
|
|
64
|
+
try {
|
|
65
|
+
const briefing = await generateBriefing({ projectPath: project.path });
|
|
66
|
+
const markdown = briefingToClaudeMd(briefing);
|
|
67
|
+
const outputPath = join(project.path, 'CLAUDE.md');
|
|
68
|
+
writeFileSync(outputPath, markdown, 'utf-8');
|
|
69
|
+
console.log(` ✅ Created: ${outputPath}`);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
console.log(` ❌ Failed: ${err}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
console.log(` Skipping CLAUDE.md generation (non-interactive mode)`);
|
|
79
|
+
console.log(` Run: claude-memory briefing /path/to/project`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (projectsWithoutClaudeMd.length === 0) {
|
|
83
|
+
console.log('\n📍 Step 2: Generate CLAUDE.md files\n');
|
|
84
|
+
console.log(' ✅ All projects already have CLAUDE.md');
|
|
85
|
+
}
|
|
86
|
+
// Step 3: Save global context
|
|
87
|
+
console.log('\n📍 Step 3: Saving global context\n');
|
|
88
|
+
const context = generateGlobalContext(projects);
|
|
89
|
+
writeGlobalContext(context);
|
|
90
|
+
console.log(' ✅ Context saved to ~/.claude-memory/');
|
|
91
|
+
// Step 4: Configure MCP
|
|
92
|
+
if (!skipMcp) {
|
|
93
|
+
console.log('\n📍 Step 4: Configuring Claude Code integration\n');
|
|
94
|
+
const mcpConfigured = configureMcp();
|
|
95
|
+
if (mcpConfigured) {
|
|
96
|
+
console.log(' ✅ MCP server added to ~/.claude.json');
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
console.log(' ⚠️ Could not auto-configure MCP');
|
|
100
|
+
console.log(' Add this to your Claude Code settings manually:');
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(' "mcpServers": {');
|
|
103
|
+
console.log(' "claude-memory": {');
|
|
104
|
+
console.log(' "command": "claude-memory",');
|
|
105
|
+
console.log(' "args": ["mcp"]');
|
|
106
|
+
console.log(' }');
|
|
107
|
+
console.log(' }');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Step 5: Configure auto-detection
|
|
111
|
+
console.log('\n📍 Step 5: Configure new project detection\n');
|
|
112
|
+
if (interactive) {
|
|
113
|
+
console.log(' When you create a new folder in your projects directory,');
|
|
114
|
+
console.log(' Claude can automatically detect it and offer to set it up.');
|
|
115
|
+
console.log('');
|
|
116
|
+
const enableAutoDetect = await ask(' Enable auto-detection of new projects? (Y/n) ');
|
|
117
|
+
const config = {
|
|
118
|
+
...DEFAULT_MEMORY_CONFIG,
|
|
119
|
+
watchedDirs: [projectsDir],
|
|
120
|
+
autoDetectNewProjects: enableAutoDetect.toLowerCase() !== 'n',
|
|
121
|
+
promptForClaudeMd: true,
|
|
122
|
+
lastUpdated: new Date().toISOString(),
|
|
123
|
+
};
|
|
124
|
+
saveMemoryConfig(config);
|
|
125
|
+
if (config.autoDetectNewProjects) {
|
|
126
|
+
console.log(' ✅ Auto-detection enabled');
|
|
127
|
+
console.log(` 📁 Watching: ${projectsDir}`);
|
|
128
|
+
console.log('');
|
|
129
|
+
console.log(' When you start Claude in a new folder under this directory,');
|
|
130
|
+
console.log(' it will offer to generate a CLAUDE.md and add the project to memory.');
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.log(' ℹ️ Auto-detection disabled');
|
|
134
|
+
console.log(' You can manually add projects with: claude-memory scan');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// Non-interactive: enable with defaults
|
|
139
|
+
const config = {
|
|
140
|
+
...DEFAULT_MEMORY_CONFIG,
|
|
141
|
+
watchedDirs: [projectsDir],
|
|
142
|
+
autoDetectNewProjects: true,
|
|
143
|
+
promptForClaudeMd: true,
|
|
144
|
+
lastUpdated: new Date().toISOString(),
|
|
145
|
+
};
|
|
146
|
+
saveMemoryConfig(config);
|
|
147
|
+
console.log(' ✅ Auto-detection enabled (default)');
|
|
148
|
+
}
|
|
149
|
+
// Done!
|
|
150
|
+
console.log('\n╔══════════════════════════════════════════════════════════╗');
|
|
151
|
+
console.log('║ ✅ Setup Complete! ║');
|
|
152
|
+
console.log('╚══════════════════════════════════════════════════════════╝');
|
|
153
|
+
console.log('');
|
|
154
|
+
console.log(' Next steps:');
|
|
155
|
+
console.log(' 1. Restart Claude Code to load the MCP server');
|
|
156
|
+
console.log(' 2. Ask Claude: "What projects do I have?"');
|
|
157
|
+
console.log(' 3. Claude now knows about all your projects!');
|
|
158
|
+
console.log('');
|
|
159
|
+
console.log(' Commands:');
|
|
160
|
+
console.log(' • claude-memory scan [dir] - Re-scan projects');
|
|
161
|
+
console.log(' • claude-memory list - Show known projects');
|
|
162
|
+
console.log(' • claude-memory briefing - Generate CLAUDE.md');
|
|
163
|
+
console.log('');
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Guess the user's projects directory
|
|
167
|
+
*/
|
|
168
|
+
function guessProjectsDir() {
|
|
169
|
+
const home = homedir();
|
|
170
|
+
const candidates = [
|
|
171
|
+
join(home, 'Projects'),
|
|
172
|
+
join(home, 'Project'),
|
|
173
|
+
join(home, 'projects'),
|
|
174
|
+
join(home, 'Code'),
|
|
175
|
+
join(home, 'code'),
|
|
176
|
+
join(home, 'Development'),
|
|
177
|
+
join(home, 'dev'),
|
|
178
|
+
join(home, 'src'),
|
|
179
|
+
join(home, 'repos'),
|
|
180
|
+
join(home, 'git'),
|
|
181
|
+
];
|
|
182
|
+
for (const dir of candidates) {
|
|
183
|
+
if (existsSync(dir)) {
|
|
184
|
+
return dir;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Fall back to home directory
|
|
188
|
+
return home;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Save memory config to ~/.claude-memory/config.json
|
|
192
|
+
*/
|
|
193
|
+
function saveMemoryConfig(config) {
|
|
194
|
+
// Ensure directory exists
|
|
195
|
+
if (!existsSync(MEMORY_DIR)) {
|
|
196
|
+
mkdirSync(MEMORY_DIR, { recursive: true });
|
|
197
|
+
}
|
|
198
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Configure MCP in ~/.claude.json
|
|
202
|
+
*/
|
|
203
|
+
function configureMcp() {
|
|
204
|
+
try {
|
|
205
|
+
let config = {};
|
|
206
|
+
// Read existing config
|
|
207
|
+
if (existsSync(CLAUDE_JSON_PATH)) {
|
|
208
|
+
const content = readFileSync(CLAUDE_JSON_PATH, 'utf-8');
|
|
209
|
+
config = JSON.parse(content);
|
|
210
|
+
}
|
|
211
|
+
// Check if already configured
|
|
212
|
+
const mcpServers = config.mcpServers || {};
|
|
213
|
+
if (mcpServers['claude-memory']) {
|
|
214
|
+
console.log(' ℹ️ MCP server already configured');
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
// Add our config
|
|
218
|
+
mcpServers['claude-memory'] = MCP_SERVER_CONFIG;
|
|
219
|
+
config.mcpServers = mcpServers;
|
|
220
|
+
// Write back
|
|
221
|
+
writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(config, null, 2), 'utf-8');
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Simple async prompt
|
|
230
|
+
*/
|
|
231
|
+
function ask(question) {
|
|
232
|
+
const rl = createInterface({
|
|
233
|
+
input: process.stdin,
|
|
234
|
+
output: process.stdout,
|
|
235
|
+
});
|
|
236
|
+
return new Promise((resolve) => {
|
|
237
|
+
rl.question(question, (answer) => {
|
|
238
|
+
rl.close();
|
|
239
|
+
resolve(answer);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Check if this is running as a postinstall script
|
|
245
|
+
*/
|
|
246
|
+
export function isPostInstall() {
|
|
247
|
+
return process.env.npm_lifecycle_event === 'postinstall';
|
|
248
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -85,3 +85,32 @@ export interface ScanOptions {
|
|
|
85
85
|
overwriteClaudeMd: boolean;
|
|
86
86
|
}
|
|
87
87
|
export declare const DEFAULT_SCAN_OPTIONS: ScanOptions;
|
|
88
|
+
/**
|
|
89
|
+
* Configuration for claude-memory behavior
|
|
90
|
+
*/
|
|
91
|
+
export interface MemoryConfig {
|
|
92
|
+
/** Directories to watch for new projects */
|
|
93
|
+
watchedDirs: string[];
|
|
94
|
+
/** Whether to auto-detect new projects in watched dirs */
|
|
95
|
+
autoDetectNewProjects: boolean;
|
|
96
|
+
/** Whether to prompt for CLAUDE.md generation on new projects */
|
|
97
|
+
promptForClaudeMd: boolean;
|
|
98
|
+
/** When config was last updated */
|
|
99
|
+
lastUpdated: string;
|
|
100
|
+
}
|
|
101
|
+
export declare const DEFAULT_MEMORY_CONFIG: MemoryConfig;
|
|
102
|
+
/**
|
|
103
|
+
* Status of the current working directory
|
|
104
|
+
*/
|
|
105
|
+
export interface CurrentDirectoryStatus {
|
|
106
|
+
/** The path being checked */
|
|
107
|
+
path: string;
|
|
108
|
+
/** Status of this directory */
|
|
109
|
+
status: 'known_project' | 'new_project' | 'outside_workspace' | 'not_a_project';
|
|
110
|
+
/** Project name if it's a known project */
|
|
111
|
+
projectName?: string;
|
|
112
|
+
/** Whether this directory needs setup */
|
|
113
|
+
needsSetup?: boolean;
|
|
114
|
+
/** Suggested action for Claude to take */
|
|
115
|
+
suggestedAction?: 'none' | 'offer_setup' | 'offer_scan';
|
|
116
|
+
}
|
package/dist/types/index.js
CHANGED
|
@@ -8,3 +8,9 @@ export const DEFAULT_SCAN_OPTIONS = {
|
|
|
8
8
|
generateClaudeMd: false,
|
|
9
9
|
overwriteClaudeMd: false,
|
|
10
10
|
};
|
|
11
|
+
export const DEFAULT_MEMORY_CONFIG = {
|
|
12
|
+
watchedDirs: [],
|
|
13
|
+
autoDetectNewProjects: true,
|
|
14
|
+
promptForClaudeMd: true,
|
|
15
|
+
lastUpdated: new Date().toISOString(),
|
|
16
|
+
};
|