@contextmirror/claude-memory 0.1.0 → 0.2.0

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 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
- // Init command - shows how to generate CLAUDE.md
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 - CLAUDE.md Generator\n');
97
- console.log(`📁 Project: ${projectPath}\n`);
98
- // Quick analysis of current project
99
- const projects = await scanProjects({
100
- rootDir: projectPath,
101
- maxDepth: 0,
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
- const project = projects[0];
109
- console.log(`✅ Found project: ${project.name}`);
110
- console.log(` Language: ${project.language}`);
111
- console.log(` Stack: ${project.techStack.join(', ')}`);
112
- console.log(` Has CLAUDE.md: ${project.hasFiles.claudeMd ? 'Yes' : 'No'}`);
113
- console.log('\n📝 To generate a CLAUDE.md file:');
114
- console.log(' 1. Open this project in Claude Code');
115
- console.log(' 2. Ask Claude: "Generate a CLAUDE.md for this project"');
116
- console.log(' 3. Claude will use the MCP tools to analyze and write it');
117
- console.log('\n💡 This uses your existing Claude session - no extra API costs!');
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)) {
@@ -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,196 @@
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 } 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
+ const CLAUDE_JSON_PATH = join(homedir(), '.claude.json');
17
+ const MCP_SERVER_CONFIG = {
18
+ command: 'claude-memory',
19
+ args: ['mcp'],
20
+ };
21
+ /**
22
+ * Run the setup wizard
23
+ */
24
+ export async function runSetupWizard(options = {}) {
25
+ const { projectsDir = guessProjectsDir(), skipMcp = false, skipBriefing = false, interactive = true, } = options;
26
+ console.log('');
27
+ console.log('╔══════════════════════════════════════════════════════════╗');
28
+ console.log('║ 🧠 Claude Memory - Setup Wizard ║');
29
+ console.log('╚══════════════════════════════════════════════════════════╝');
30
+ console.log('');
31
+ // Step 1: Find projects
32
+ console.log('📍 Step 1: Scanning for projects\n');
33
+ console.log(` Looking in: ${projectsDir}`);
34
+ const projects = await scanProjects({
35
+ rootDir: projectsDir,
36
+ maxDepth: 2,
37
+ });
38
+ if (projects.length === 0) {
39
+ console.log('\n ⚠️ No projects found.');
40
+ console.log(` Make sure you have projects in ${projectsDir}`);
41
+ console.log(' Or run: claude-memory scan /path/to/your/projects');
42
+ return;
43
+ }
44
+ console.log(`\n ✅ Found ${projects.length} project(s):\n`);
45
+ const projectsWithoutClaudeMd = projects.filter(p => !p.hasFiles.claudeMd);
46
+ const projectsWithClaudeMd = projects.filter(p => p.hasFiles.claudeMd);
47
+ for (const p of projectsWithClaudeMd) {
48
+ console.log(` ✓ ${p.name} (${p.techStack.join(', ')}) - Has CLAUDE.md`);
49
+ }
50
+ for (const p of projectsWithoutClaudeMd) {
51
+ console.log(` ○ ${p.name} (${p.techStack.join(', ')}) - No CLAUDE.md`);
52
+ }
53
+ // Step 2: Generate CLAUDE.md for projects without one
54
+ if (!skipBriefing && projectsWithoutClaudeMd.length > 0) {
55
+ console.log('\n📍 Step 2: Generate CLAUDE.md files\n');
56
+ if (interactive) {
57
+ const answer = await ask(` Generate CLAUDE.md for ${projectsWithoutClaudeMd.length} project(s)? (Y/n) `);
58
+ if (answer.toLowerCase() !== 'n') {
59
+ for (const project of projectsWithoutClaudeMd) {
60
+ console.log(`\n 📝 Generating for ${project.name}...`);
61
+ try {
62
+ const briefing = await generateBriefing({ projectPath: project.path });
63
+ const markdown = briefingToClaudeMd(briefing);
64
+ const outputPath = join(project.path, 'CLAUDE.md');
65
+ writeFileSync(outputPath, markdown, 'utf-8');
66
+ console.log(` ✅ Created: ${outputPath}`);
67
+ }
68
+ catch (err) {
69
+ console.log(` ❌ Failed: ${err}`);
70
+ }
71
+ }
72
+ }
73
+ }
74
+ else {
75
+ console.log(` Skipping CLAUDE.md generation (non-interactive mode)`);
76
+ console.log(` Run: claude-memory briefing /path/to/project`);
77
+ }
78
+ }
79
+ else if (projectsWithoutClaudeMd.length === 0) {
80
+ console.log('\n📍 Step 2: Generate CLAUDE.md files\n');
81
+ console.log(' ✅ All projects already have CLAUDE.md');
82
+ }
83
+ // Step 3: Save global context
84
+ console.log('\n📍 Step 3: Saving global context\n');
85
+ const context = generateGlobalContext(projects);
86
+ writeGlobalContext(context);
87
+ console.log(' ✅ Context saved to ~/.claude-memory/');
88
+ // Step 4: Configure MCP
89
+ if (!skipMcp) {
90
+ console.log('\n📍 Step 4: Configuring Claude Code integration\n');
91
+ const mcpConfigured = configureMcp();
92
+ if (mcpConfigured) {
93
+ console.log(' ✅ MCP server added to ~/.claude.json');
94
+ }
95
+ else {
96
+ console.log(' ⚠️ Could not auto-configure MCP');
97
+ console.log(' Add this to your Claude Code settings manually:');
98
+ console.log('');
99
+ console.log(' "mcpServers": {');
100
+ console.log(' "claude-memory": {');
101
+ console.log(' "command": "claude-memory",');
102
+ console.log(' "args": ["mcp"]');
103
+ console.log(' }');
104
+ console.log(' }');
105
+ }
106
+ }
107
+ // Done!
108
+ console.log('\n╔══════════════════════════════════════════════════════════╗');
109
+ console.log('║ ✅ Setup Complete! ║');
110
+ console.log('╚══════════════════════════════════════════════════════════╝');
111
+ console.log('');
112
+ console.log(' Next steps:');
113
+ console.log(' 1. Restart Claude Code to load the MCP server');
114
+ console.log(' 2. Ask Claude: "What projects do I have?"');
115
+ console.log(' 3. Claude now knows about all your projects!');
116
+ console.log('');
117
+ console.log(' Commands:');
118
+ console.log(' • claude-memory scan [dir] - Re-scan projects');
119
+ console.log(' • claude-memory list - Show known projects');
120
+ console.log(' • claude-memory briefing - Generate CLAUDE.md');
121
+ console.log('');
122
+ }
123
+ /**
124
+ * Guess the user's projects directory
125
+ */
126
+ function guessProjectsDir() {
127
+ const home = homedir();
128
+ const candidates = [
129
+ join(home, 'Projects'),
130
+ join(home, 'Project'),
131
+ join(home, 'projects'),
132
+ join(home, 'Code'),
133
+ join(home, 'code'),
134
+ join(home, 'Development'),
135
+ join(home, 'dev'),
136
+ join(home, 'src'),
137
+ join(home, 'repos'),
138
+ join(home, 'git'),
139
+ ];
140
+ for (const dir of candidates) {
141
+ if (existsSync(dir)) {
142
+ return dir;
143
+ }
144
+ }
145
+ // Fall back to home directory
146
+ return home;
147
+ }
148
+ /**
149
+ * Configure MCP in ~/.claude.json
150
+ */
151
+ function configureMcp() {
152
+ try {
153
+ let config = {};
154
+ // Read existing config
155
+ if (existsSync(CLAUDE_JSON_PATH)) {
156
+ const content = readFileSync(CLAUDE_JSON_PATH, 'utf-8');
157
+ config = JSON.parse(content);
158
+ }
159
+ // Check if already configured
160
+ const mcpServers = config.mcpServers || {};
161
+ if (mcpServers['claude-memory']) {
162
+ console.log(' ℹ️ MCP server already configured');
163
+ return true;
164
+ }
165
+ // Add our config
166
+ mcpServers['claude-memory'] = MCP_SERVER_CONFIG;
167
+ config.mcpServers = mcpServers;
168
+ // Write back
169
+ writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(config, null, 2), 'utf-8');
170
+ return true;
171
+ }
172
+ catch (err) {
173
+ return false;
174
+ }
175
+ }
176
+ /**
177
+ * Simple async prompt
178
+ */
179
+ function ask(question) {
180
+ const rl = createInterface({
181
+ input: process.stdin,
182
+ output: process.stdout,
183
+ });
184
+ return new Promise((resolve) => {
185
+ rl.question(question, (answer) => {
186
+ rl.close();
187
+ resolve(answer);
188
+ });
189
+ });
190
+ }
191
+ /**
192
+ * Check if this is running as a postinstall script
193
+ */
194
+ export function isPostInstall() {
195
+ return process.env.npm_lifecycle_event === 'postinstall';
196
+ }