@contextmirror/claude-memory 0.2.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.
@@ -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 handleGetGlobalContext() {
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
- return 'No projects scanned yet. Run `claude-memory scan` first.';
243
+ lines.push('No projects scanned yet. Run `claude-memory scan` first.');
244
+ return lines.join('\n');
106
245
  }
107
- const lines = [
108
- `# Your Development Environment`,
109
- '',
110
- `Last scanned: ${new Date(context.lastUpdated).toLocaleString()}`,
111
- '',
112
- '## Active Projects',
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);
@@ -6,14 +6,17 @@
6
6
  * 2. Offers to generate CLAUDE.md for projects without one
7
7
  * 3. Auto-configures MCP in ~/.claude.json
8
8
  */
9
- import { readFileSync, writeFileSync, existsSync } from 'fs';
9
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
10
10
  import { join } from 'path';
11
11
  import { homedir } from 'os';
12
12
  import { createInterface } from 'readline';
13
13
  import { scanProjects } from '../scanner/projectScanner.js';
14
14
  import { generateGlobalContext, writeGlobalContext } from '../scanner/contextGenerator.js';
15
15
  import { generateBriefing, briefingToClaudeMd } from '../briefing/briefingGenerator.js';
16
+ import { DEFAULT_MEMORY_CONFIG } from '../types/index.js';
16
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');
17
20
  const MCP_SERVER_CONFIG = {
18
21
  command: 'claude-memory',
19
22
  args: ['mcp'],
@@ -104,6 +107,45 @@ export async function runSetupWizard(options = {}) {
104
107
  console.log(' }');
105
108
  }
106
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
+ }
107
149
  // Done!
108
150
  console.log('\n╔══════════════════════════════════════════════════════════╗');
109
151
  console.log('ā•‘ āœ… Setup Complete! ā•‘');
@@ -145,6 +187,16 @@ function guessProjectsDir() {
145
187
  // Fall back to home directory
146
188
  return home;
147
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
+ }
148
200
  /**
149
201
  * Configure MCP in ~/.claude.json
150
202
  */
@@ -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
+ }
@@ -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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contextmirror/claude-memory",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Cross-project memory for Claude Code - know about all your projects",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",