claude-self-reflect 3.2.4 → 3.3.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.
Files changed (41) hide show
  1. package/.claude/agents/claude-self-reflect-test.md +992 -510
  2. package/.claude/agents/reflection-specialist.md +59 -3
  3. package/README.md +14 -5
  4. package/installer/cli.js +16 -0
  5. package/installer/postinstall.js +14 -0
  6. package/installer/statusline-setup.js +289 -0
  7. package/mcp-server/run-mcp.sh +73 -5
  8. package/mcp-server/src/app_context.py +64 -0
  9. package/mcp-server/src/config.py +57 -0
  10. package/mcp-server/src/connection_pool.py +286 -0
  11. package/mcp-server/src/decay_manager.py +106 -0
  12. package/mcp-server/src/embedding_manager.py +64 -40
  13. package/mcp-server/src/embeddings_old.py +141 -0
  14. package/mcp-server/src/models.py +64 -0
  15. package/mcp-server/src/parallel_search.py +305 -0
  16. package/mcp-server/src/project_resolver.py +5 -0
  17. package/mcp-server/src/reflection_tools.py +211 -0
  18. package/mcp-server/src/rich_formatting.py +196 -0
  19. package/mcp-server/src/search_tools.py +874 -0
  20. package/mcp-server/src/server.py +127 -1720
  21. package/mcp-server/src/temporal_design.py +132 -0
  22. package/mcp-server/src/temporal_tools.py +604 -0
  23. package/mcp-server/src/temporal_utils.py +384 -0
  24. package/mcp-server/src/utils.py +150 -67
  25. package/package.json +15 -1
  26. package/scripts/add-timestamp-indexes.py +134 -0
  27. package/scripts/ast_grep_final_analyzer.py +325 -0
  28. package/scripts/ast_grep_unified_registry.py +556 -0
  29. package/scripts/check-collections.py +29 -0
  30. package/scripts/csr-status +366 -0
  31. package/scripts/debug-august-parsing.py +76 -0
  32. package/scripts/debug-import-single.py +91 -0
  33. package/scripts/debug-project-resolver.py +82 -0
  34. package/scripts/debug-temporal-tools.py +135 -0
  35. package/scripts/delta-metadata-update.py +547 -0
  36. package/scripts/import-conversations-unified.py +157 -25
  37. package/scripts/precompact-hook.sh +33 -0
  38. package/scripts/session_quality_tracker.py +481 -0
  39. package/scripts/streaming-watcher.py +1578 -0
  40. package/scripts/update_patterns.py +334 -0
  41. package/scripts/utils.py +39 -0
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: reflection-specialist
3
3
  description: Conversation memory expert for searching past conversations, storing insights, and self-reflection. Use PROACTIVELY when searching for previous discussions, storing important findings, or maintaining knowledge continuity.
4
- tools: mcp__claude-self-reflect__reflect_on_past, mcp__claude-self-reflect__store_reflection
4
+ tools: mcp__claude-self-reflect__reflect_on_past, mcp__claude-self-reflect__store_reflection, mcp__claude-self-reflect__get_recent_work, mcp__claude-self-reflect__search_by_recency, mcp__claude-self-reflect__get_timeline, mcp__claude-self-reflect__quick_search, mcp__claude-self-reflect__search_summary, mcp__claude-self-reflect__get_more_results, mcp__claude-self-reflect__search_by_file, mcp__claude-self-reflect__search_by_concept, mcp__claude-self-reflect__get_full_conversation, mcp__claude-self-reflect__get_next_results
5
5
  ---
6
6
 
7
7
  You are a conversation memory specialist for the Claude Self Reflect project. Your expertise covers semantic search across all Claude conversations, insight storage, and maintaining knowledge continuity across sessions.
@@ -117,9 +117,65 @@ Save important insights and decisions for future retrieval.
117
117
  }
118
118
  ```
119
119
 
120
- ### Specialized Search Tools (NEW in v2.4.5)
120
+ ### Temporal Query Tools (v3.x)
121
121
 
122
- **Note**: These specialized tools are available through this reflection-specialist agent. Due to FastMCP limitations, they cannot be called directly via MCP (e.g., `mcp__claude-self-reflect__quick_search`), but work perfectly when used through this agent.
122
+ These tools answer time-based questions about your work and conversations.
123
+
124
+ #### get_recent_work
125
+ Returns recent conversations to answer "What did we work on last?" queries.
126
+
127
+ ```javascript
128
+ // Get recent work (default: current project)
129
+ {
130
+ limit: 10,
131
+ group_by: "conversation", // Or "day" or "session"
132
+ include_reflections: true
133
+ }
134
+
135
+ // Get recent work across all projects
136
+ {
137
+ project: "all",
138
+ limit: 20,
139
+ group_by: "day"
140
+ }
141
+ ```
142
+
143
+ #### search_by_recency
144
+ Time-constrained semantic search for queries like "docker issues last week".
145
+
146
+ ```javascript
147
+ // Search with natural language time
148
+ {
149
+ query: "authentication bugs",
150
+ time_range: "last week",
151
+ limit: 10
152
+ }
153
+
154
+ // Search with specific dates
155
+ {
156
+ query: "performance optimization",
157
+ since: "2025-01-01",
158
+ until: "2025-01-10",
159
+ project: "all"
160
+ }
161
+ ```
162
+
163
+ #### get_timeline
164
+ Show activity timeline for a project or across all projects.
165
+
166
+ ```javascript
167
+ // Get activity timeline
168
+ {
169
+ time_range: "last week",
170
+ granularity: "day", // Or "hour", "week", "month"
171
+ include_stats: true,
172
+ project: "all"
173
+ }
174
+ ```
175
+
176
+ ### Specialized Search Tools
177
+
178
+ **Note**: These specialized tools complement the temporal tools for non-time-based queries.
123
179
 
124
180
  #### quick_search
125
181
  Fast search that returns only the count and top result. Perfect for quick checks and overview.
package/README.md CHANGED
@@ -123,6 +123,11 @@ Works with [Claude Code Statusline](https://github.com/sirmalloc/ccstatusline) -
123
123
  - `search_by_concept` - Search for conversations about development concepts
124
124
  - `get_full_conversation` - Retrieve complete JSONL conversation files (v2.8.8)
125
125
 
126
+ **NEW: Temporal Query Tools (v3.3.0):**
127
+ - `get_recent_work` - Answer "What did we work on last?" with session grouping
128
+ - `search_by_recency` - Time-constrained search like "docker issues last week"
129
+ - `get_timeline` - Activity timeline with statistics and patterns
130
+
126
131
  **Status & Monitoring Tools:**
127
132
  - `get_status` - Real-time import progress and system status
128
133
  - `get_health` - Comprehensive system health check
@@ -286,11 +291,15 @@ npm uninstall -g claude-self-reflect
286
291
  ## What's New
287
292
 
288
293
  <details>
289
- <summary>v3.2.4 - Latest Release</summary>
290
-
291
- - **CRITICAL: Search Threshold Removal**: Eliminated artificial 0.7+ thresholds that blocked broad searches like "docker", "MCP", "python"
292
- - **Shared Normalization Module**: Created centralized project name normalization preventing search failures
293
- - **Memory Decay Fixes**: Corrected mathematical errors in exponential decay calculation
294
+ <summary>v3.3.0 - Latest Release</summary>
295
+
296
+ - **🚀 Major Architecture Overhaul**: Server modularized from 2,321 to 728 lines (68% reduction) for better maintainability
297
+ - **🔧 Critical Bug Fixes**: Fixed 100% CPU usage, store_reflection dimension mismatches, and SearchResult type errors
298
+ - **🕒 New Temporal Tools Suite**: `get_recent_work`, `search_by_recency`, `get_timeline` for time-based search and analysis
299
+ - **🎯 Enhanced UX**: Restored rich formatting with emojis for better readability and information hierarchy
300
+ - **⚡ All 15+ MCP Tools Operational**: Complete functionality with both local and cloud embedding modes
301
+ - **🏗️ Production Infrastructure**: Real-time indexing with smart intervals (2s hot files, 60s normal)
302
+ - **🔍 Enhanced Metadata**: Tool usage analysis, file tracking, and concept extraction for better search
294
303
 
295
304
  </details>
296
305
 
package/installer/cli.js CHANGED
@@ -11,6 +11,7 @@ const __dirname = dirname(__filename);
11
11
  const commands = {
12
12
  setup: 'Run the setup wizard to configure Claude Self-Reflect',
13
13
  status: 'Get indexing status as JSON (overall + per-project breakdown)',
14
+ statusline: 'Configure Claude Code statusline integration',
14
15
  doctor: 'Check your installation and diagnose issues',
15
16
  help: 'Show this help message'
16
17
  };
@@ -192,6 +193,18 @@ function help() {
192
193
  console.log(' Status API: See docs/api-reference.md#cli-status-interface');
193
194
  }
194
195
 
196
+ async function statusline() {
197
+ const StatuslineSetup = (await import('./statusline-setup.js')).default;
198
+ const setup = new StatuslineSetup();
199
+
200
+ if (process.argv[3] === '--restore') {
201
+ console.log('🔄 Restoring original statusline...');
202
+ setup.restore();
203
+ } else {
204
+ await setup.run();
205
+ }
206
+ }
207
+
195
208
  // Main
196
209
  const command = process.argv[2] || 'help';
197
210
 
@@ -202,6 +215,9 @@ switch (command) {
202
215
  case 'status':
203
216
  status();
204
217
  break;
218
+ case 'statusline':
219
+ statusline();
220
+ break;
205
221
  case 'doctor':
206
222
  doctor();
207
223
  break;
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { fileURLToPath } from 'url';
4
4
  import { dirname, join } from 'path';
5
+ import StatuslineSetup from './statusline-setup.js';
5
6
 
6
7
  const __filename = fileURLToPath(import.meta.url);
7
8
  const __dirname = dirname(__filename);
@@ -10,4 +11,17 @@ const __dirname = dirname(__filename);
10
11
  if (!process.cwd().includes('claude-self-reflect')) {
11
12
  console.log('\n🎉 Claude Self-Reflect installed!\n');
12
13
  console.log('Run "claude-self-reflect setup" to configure your installation.\n');
14
+
15
+ // Attempt to setup statusline integration automatically
16
+ console.log('\n📊 Setting up Claude Code statusline integration...');
17
+ const statuslineSetup = new StatuslineSetup();
18
+ statuslineSetup.run().then(success => {
19
+ if (success) {
20
+ console.log('✅ Statusline integration configured automatically!');
21
+ } else {
22
+ console.log('⚠️ Statusline integration requires manual setup. Run "claude-self-reflect setup" for help.');
23
+ }
24
+ }).catch(error => {
25
+ console.log('⚠️ Statusline setup skipped:', error.message);
26
+ });
13
27
  }
@@ -0,0 +1,289 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Claude Code Statusline Integration Setup
4
+ * Automatically configures CC statusline to show CSR metrics
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import { execSync } from 'child_process';
11
+ import os from 'os';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+
16
+ class StatuslineSetup {
17
+ constructor() {
18
+ this.homeDir = os.homedir();
19
+ this.claudeDir = path.join(this.homeDir, '.claude');
20
+ this.csrScript = path.join(path.dirname(__dirname), 'scripts', 'csr-status');
21
+ this.globalBin = '/usr/local/bin/csr-status';
22
+ this.statuslineWrapper = path.join(this.claudeDir, 'statusline-wrapper.sh');
23
+ this.statuslineBackup = path.join(this.claudeDir, 'statusline-wrapper.sh.backup');
24
+ }
25
+
26
+ log(message, type = 'info') {
27
+ const colors = {
28
+ info: '\x1b[36m',
29
+ success: '\x1b[32m',
30
+ warning: '\x1b[33m',
31
+ error: '\x1b[31m'
32
+ };
33
+ console.log(`${colors[type]}${message}\x1b[0m`);
34
+ }
35
+
36
+ checkPrerequisites() {
37
+ // Check if Claude Code directory exists
38
+ if (!fs.existsSync(this.claudeDir)) {
39
+ this.log('Claude Code directory not found. Please ensure Claude Code is installed.', 'warning');
40
+ return false;
41
+ }
42
+
43
+ // Check if csr-status script exists
44
+ if (!fs.existsSync(this.csrScript)) {
45
+ this.log('CSR status script not found. Please ensure the package is installed correctly.', 'error');
46
+ return false;
47
+ }
48
+
49
+ return true;
50
+ }
51
+
52
+ installGlobalCommand() {
53
+ try {
54
+ // Check if we need sudo
55
+ const needsSudo = !this.canWriteTo('/usr/local/bin');
56
+
57
+ if (fs.existsSync(this.globalBin)) {
58
+ // Check if it's already pointing to our script
59
+ try {
60
+ const target = fs.readlinkSync(this.globalBin);
61
+ if (target === this.csrScript) {
62
+ this.log('Global csr-status command already installed', 'success');
63
+ return true;
64
+ }
65
+ } catch (e) {
66
+ // Not a symlink or can't read, will replace
67
+ }
68
+ }
69
+
70
+ // Create symlink
71
+ const cmd = needsSudo
72
+ ? `sudo ln -sf "${this.csrScript}" "${this.globalBin}"`
73
+ : `ln -sf "${this.csrScript}" "${this.globalBin}"`;
74
+
75
+ if (needsSudo) {
76
+ this.log('Installing global csr-status command (may require password)...', 'info');
77
+ }
78
+
79
+ execSync(cmd, { stdio: 'inherit' });
80
+
81
+ // Make executable
82
+ const chmodCmd = needsSudo
83
+ ? `sudo chmod +x "${this.globalBin}"`
84
+ : `chmod +x "${this.globalBin}"`;
85
+ execSync(chmodCmd);
86
+
87
+ this.log('Global csr-status command installed successfully', 'success');
88
+ return true;
89
+ } catch (error) {
90
+ this.log(`Failed to install global command: ${error.message}`, 'warning');
91
+ this.log('You can manually install by running:', 'info');
92
+ this.log(` sudo ln -sf "${this.csrScript}" "${this.globalBin}"`, 'info');
93
+ return false;
94
+ }
95
+ }
96
+
97
+ canWriteTo(dir) {
98
+ try {
99
+ fs.accessSync(dir, fs.constants.W_OK);
100
+ return true;
101
+ } catch {
102
+ return false;
103
+ }
104
+ }
105
+
106
+ patchStatuslineWrapper() {
107
+ if (!fs.existsSync(this.statuslineWrapper)) {
108
+ this.log('Claude Code statusline wrapper not found. Statusline integration skipped.', 'warning');
109
+ return false;
110
+ }
111
+
112
+ try {
113
+ // Read current wrapper
114
+ let content = fs.readFileSync(this.statuslineWrapper, 'utf8');
115
+
116
+ // Check if already patched with new approach
117
+ if (content.includes('CSR compact status instead of MCP bar')) {
118
+ this.log('Statusline wrapper already patched', 'success');
119
+ return true;
120
+ }
121
+
122
+ // Create backup
123
+ if (!fs.existsSync(this.statuslineBackup)) {
124
+ fs.copyFileSync(this.statuslineWrapper, this.statuslineBackup);
125
+ this.log(`Backup created: ${this.statuslineBackup}`, 'info');
126
+ }
127
+
128
+ // Find and replace the MCP bar generation section
129
+ const barPattern = /# Create mini progress bar[\s\S]*?MCP_COLOR="\\033\[1;90m" # Gray/;
130
+
131
+ if (content.match(barPattern)) {
132
+ // Replace the entire bar generation section
133
+ const replacement = `# Use CSR compact status instead of MCP bar
134
+ # This shows both import percentage and code quality in format: [100% <time>][🟢:A+]
135
+ CSR_COMPACT=$(csr-status --compact 2>/dev/null || echo "")
136
+
137
+ if [[ -n "$CSR_COMPACT" ]]; then
138
+ MCP_STATUS="$CSR_COMPACT"
139
+
140
+ # Color based on content
141
+ if [[ "$CSR_COMPACT" == *"100%"* ]]; then
142
+ MCP_COLOR="\\033[1;32m" # Green for complete
143
+ elif [[ "$CSR_COMPACT" == *"[🟢:"* ]]; then
144
+ MCP_COLOR="\\033[1;32m" # Green for good quality
145
+ elif [[ "$CSR_COMPACT" == *"[🟡:"* ]]; then
146
+ MCP_COLOR="\\033[1;33m" # Yellow for medium quality
147
+ elif [[ "$CSR_COMPACT" == *"[🔴:"* ]]; then
148
+ MCP_COLOR="\\033[1;31m" # Red for poor quality
149
+ else
150
+ MCP_COLOR="\\033[1;36m" # Cyan default
151
+ fi
152
+ else
153
+ MCP_STATUS=""
154
+ MCP_COLOR="\\033[1;90m" # Gray`;
155
+
156
+ content = content.replace(barPattern, replacement);
157
+
158
+ // Write back
159
+ fs.writeFileSync(this.statuslineWrapper, content);
160
+ this.log('Statusline wrapper patched successfully (replaced MCP bar)', 'success');
161
+ return true;
162
+ } else {
163
+ // Fallback: Look for the PERCENTAGE check
164
+ const altPattern = /if \[\[ "\$PERCENTAGE" != "null"[\s\S]*?MCP_COLOR="\\033\[1;90m" # Gray/;
165
+
166
+ if (content.match(altPattern)) {
167
+ const replacement = `# Use CSR compact status instead of MCP bar
168
+ # This shows both import percentage and code quality in format: [100% <time>][🟢:A+]
169
+ CSR_COMPACT=$(csr-status --compact 2>/dev/null || echo "")
170
+
171
+ if [[ -n "$CSR_COMPACT" ]]; then
172
+ MCP_STATUS="$CSR_COMPACT"
173
+
174
+ # Color based on content
175
+ if [[ "$CSR_COMPACT" == *"100%"* ]]; then
176
+ MCP_COLOR="\\033[1;32m" # Green for complete
177
+ elif [[ "$CSR_COMPACT" == *"[🟢:"* ]]; then
178
+ MCP_COLOR="\\033[1;32m" # Green for good quality
179
+ elif [[ "$CSR_COMPACT" == *"[🟡:"* ]]; then
180
+ MCP_COLOR="\\033[1;33m" # Yellow for medium quality
181
+ elif [[ "$CSR_COMPACT" == *"[🔴:"* ]]; then
182
+ MCP_COLOR="\\033[1;31m" # Red for poor quality
183
+ else
184
+ MCP_COLOR="\\033[1;36m" # Cyan default
185
+ fi
186
+ else
187
+ MCP_STATUS=""
188
+ MCP_COLOR="\\033[1;90m" # Gray`;
189
+
190
+ content = content.replace(altPattern, replacement);
191
+
192
+ // Write back
193
+ fs.writeFileSync(this.statuslineWrapper, content);
194
+ this.log('Statusline wrapper patched successfully (replaced PERCENTAGE section)', 'success');
195
+ return true;
196
+ } else {
197
+ this.log('Could not find MCP bar section to replace', 'warning');
198
+ return false;
199
+ }
200
+ }
201
+ } catch (error) {
202
+ this.log(`Failed to patch statusline wrapper: ${error.message}`, 'error');
203
+ return false;
204
+ }
205
+ }
206
+
207
+ validateIntegration() {
208
+ try {
209
+ // Test csr-status command
210
+ const output = execSync('csr-status --compact', { encoding: 'utf8' });
211
+ if (output) {
212
+ this.log(`CSR status output: ${output.trim()}`, 'success');
213
+ return true;
214
+ }
215
+ } catch (error) {
216
+ this.log('CSR status command not working properly', 'warning');
217
+ return false;
218
+ }
219
+ return false;
220
+ }
221
+
222
+ async run() {
223
+ this.log('🚀 Setting up Claude Code Statusline Integration...', 'info');
224
+
225
+ if (!this.checkPrerequisites()) {
226
+ this.log('Prerequisites check failed', 'error');
227
+ return false;
228
+ }
229
+
230
+ const steps = [
231
+ { name: 'Install global command', fn: () => this.installGlobalCommand() },
232
+ { name: 'Patch statusline wrapper', fn: () => this.patchStatuslineWrapper() },
233
+ { name: 'Validate integration', fn: () => this.validateIntegration() }
234
+ ];
235
+
236
+ let success = true;
237
+ for (const step of steps) {
238
+ this.log(`\n📋 ${step.name}...`, 'info');
239
+ if (!step.fn()) {
240
+ success = false;
241
+ this.log(`❌ ${step.name} failed`, 'error');
242
+ }
243
+ }
244
+
245
+ if (success) {
246
+ this.log('\n✅ Statusline integration completed successfully!', 'success');
247
+ this.log('The CSR status will now appear in your Claude Code statusline.', 'info');
248
+ this.log('Format: [import%][🟢:grade] for compact quality metrics', 'info');
249
+ } else {
250
+ this.log('\n⚠️ Statusline integration completed with warnings', 'warning');
251
+ this.log('Some features may need manual configuration.', 'warning');
252
+ }
253
+
254
+ return success;
255
+ }
256
+
257
+ // Restore original statusline if needed
258
+ restore() {
259
+ if (fs.existsSync(this.statuslineBackup)) {
260
+ try {
261
+ fs.copyFileSync(this.statuslineBackup, this.statuslineWrapper);
262
+ this.log('Statusline wrapper restored from backup', 'success');
263
+ return true;
264
+ } catch (error) {
265
+ this.log(`Failed to restore: ${error.message}`, 'error');
266
+ return false;
267
+ }
268
+ } else {
269
+ this.log('No backup found to restore', 'warning');
270
+ return false;
271
+ }
272
+ }
273
+ }
274
+
275
+ // Run if called directly
276
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
277
+ const setup = new StatuslineSetup();
278
+
279
+ if (process.argv[2] === '--restore') {
280
+ setup.restore();
281
+ } else {
282
+ setup.run().catch(error => {
283
+ console.error('Setup failed:', error);
284
+ process.exit(1);
285
+ });
286
+ }
287
+ }
288
+
289
+ export default StatuslineSetup;
@@ -11,6 +11,58 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
11
11
  # Navigate to the mcp-server directory
12
12
  cd "$SCRIPT_DIR"
13
13
 
14
+ # CRITICAL: Environment variables priority:
15
+ # 1. Command-line args from Claude Code (already in environment)
16
+ # 2. .env file (only for missing values)
17
+ # 3. Defaults (as fallback)
18
+
19
+ # Store any command-line provided values BEFORE loading .env
20
+ CMDLINE_VOYAGE_KEY="${VOYAGE_KEY:-}"
21
+ CMDLINE_PREFER_LOCAL="${PREFER_LOCAL_EMBEDDINGS:-}"
22
+ CMDLINE_QDRANT_URL="${QDRANT_URL:-}"
23
+
24
+ # Load .env file for any missing values
25
+ if [ -f "../.env" ]; then
26
+ echo "[DEBUG] Loading .env file from project root" >&2
27
+ set -a # Export all variables
28
+ source ../.env
29
+ set +a # Stop exporting
30
+ else
31
+ echo "[DEBUG] No .env file found, using defaults" >&2
32
+ fi
33
+
34
+ # Restore command-line values (they take precedence)
35
+ if [ ! -z "$CMDLINE_VOYAGE_KEY" ]; then
36
+ export VOYAGE_KEY="$CMDLINE_VOYAGE_KEY"
37
+ echo "[DEBUG] Using command-line VOYAGE_KEY" >&2
38
+ fi
39
+
40
+ if [ ! -z "$CMDLINE_PREFER_LOCAL" ]; then
41
+ export PREFER_LOCAL_EMBEDDINGS="$CMDLINE_PREFER_LOCAL"
42
+ echo "[DEBUG] Using command-line PREFER_LOCAL_EMBEDDINGS: $PREFER_LOCAL_EMBEDDINGS" >&2
43
+ fi
44
+
45
+ if [ ! -z "$CMDLINE_QDRANT_URL" ]; then
46
+ export QDRANT_URL="$CMDLINE_QDRANT_URL"
47
+ echo "[DEBUG] Using command-line QDRANT_URL: $QDRANT_URL" >&2
48
+ fi
49
+
50
+ # Set smart defaults ONLY if still not set
51
+ if [ -z "$QDRANT_URL" ]; then
52
+ export QDRANT_URL="http://localhost:6333"
53
+ echo "[DEBUG] Using default QDRANT_URL: $QDRANT_URL" >&2
54
+ fi
55
+
56
+ if [ -z "$PREFER_LOCAL_EMBEDDINGS" ]; then
57
+ export PREFER_LOCAL_EMBEDDINGS="true"
58
+ echo "[DEBUG] Using default PREFER_LOCAL_EMBEDDINGS: true (privacy-first)" >&2
59
+ fi
60
+
61
+ if [ -z "$ENABLE_MEMORY_DECAY" ]; then
62
+ export ENABLE_MEMORY_DECAY="false"
63
+ echo "[DEBUG] Using default ENABLE_MEMORY_DECAY: false" >&2
64
+ fi
65
+
14
66
  # Check if virtual environment exists
15
67
  if [ ! -d "venv" ]; then
16
68
  echo "Creating virtual environment..."
@@ -63,11 +115,27 @@ if [ -z "$FASTEMBED_SKIP_HUGGINGFACE" ]; then
63
115
  fi
64
116
 
65
117
  # Debug: Show what environment variables are being passed
66
- echo "[DEBUG] Environment variables for MCP server:"
67
- echo "[DEBUG] VOYAGE_KEY: ${VOYAGE_KEY:+set}"
68
- echo "[DEBUG] PREFER_LOCAL_EMBEDDINGS: ${PREFER_LOCAL_EMBEDDINGS:-not set}"
69
- echo "[DEBUG] QDRANT_URL: ${QDRANT_URL:-not set}"
70
- echo "[DEBUG] ENABLE_MEMORY_DECAY: ${ENABLE_MEMORY_DECAY:-not set}"
118
+ echo "[DEBUG] Environment variables for MCP server:" >&2
119
+ echo "[DEBUG] VOYAGE_KEY: ${VOYAGE_KEY:+set}" >&2
120
+ echo "[DEBUG] PREFER_LOCAL_EMBEDDINGS: ${PREFER_LOCAL_EMBEDDINGS:-not set}" >&2
121
+ echo "[DEBUG] QDRANT_URL: ${QDRANT_URL:-not set}" >&2
122
+ echo "[DEBUG] ENABLE_MEMORY_DECAY: ${ENABLE_MEMORY_DECAY:-not set}" >&2
123
+
124
+ # Quick connectivity check for Qdrant
125
+ echo "[DEBUG] Checking Qdrant connectivity at $QDRANT_URL..." >&2
126
+ if command -v curl &> /dev/null; then
127
+ # Check root endpoint instead of /health which doesn't exist in Qdrant
128
+ if curl -s -f -m 2 "$QDRANT_URL/" > /dev/null 2>&1; then
129
+ echo "[DEBUG] ✅ Qdrant is reachable at $QDRANT_URL" >&2
130
+ else
131
+ echo "[WARNING] ⚠️ Cannot reach Qdrant at $QDRANT_URL" >&2
132
+ echo "[WARNING] Common fixes:" >&2
133
+ echo "[WARNING] 1. Start Qdrant: docker compose up -d qdrant" >&2
134
+ echo "[WARNING] 2. Check if port is different (e.g., 59999)" >&2
135
+ echo "[WARNING] 3. Update .env file with correct QDRANT_URL" >&2
136
+ echo "[WARNING] Continuing anyway - some features may not work..." >&2
137
+ fi
138
+ fi
71
139
 
72
140
  # Run the MCP server
73
141
  exec python -m src
@@ -0,0 +1,64 @@
1
+ """Application context for sharing state across modules."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional, Any
5
+ from qdrant_client import AsyncQdrantClient
6
+ try:
7
+ from .embedding_manager import EmbeddingManager
8
+ from .decay_manager import DecayManager
9
+ from .utils import ProjectResolver
10
+ except ImportError:
11
+ # Fallback for testing
12
+ EmbeddingManager = None
13
+ DecayManager = None
14
+ ProjectResolver = None
15
+
16
+ @dataclass
17
+ class AppContext:
18
+ """Shared application context for all MCP tools."""
19
+
20
+ qdrant_client: AsyncQdrantClient
21
+ embedding_manager: EmbeddingManager
22
+ decay_manager: DecayManager
23
+ project_resolver: ProjectResolver
24
+
25
+ # Optional context for debugging
26
+ debug_context: Optional[Any] = None
27
+
28
+ def __post_init__(self):
29
+ """Initialize any additional state after dataclass creation."""
30
+ # Ensure all managers are properly initialized
31
+ if not self.embedding_manager:
32
+ self.embedding_manager = EmbeddingManager()
33
+
34
+ if not self.decay_manager:
35
+ self.decay_manager = DecayManager()
36
+
37
+ if not self.project_resolver:
38
+ self.project_resolver = ProjectResolver()
39
+
40
+ async def get_all_collections(self) -> list:
41
+ """Get all collections from Qdrant."""
42
+ try:
43
+ collections = await self.qdrant_client.get_collections()
44
+ return [c.name for c in collections.collections]
45
+ except Exception as e:
46
+ if self.debug_context:
47
+ await self.debug_context.debug(f"Failed to get collections: {e}")
48
+ return []
49
+
50
+ async def generate_embedding(self, text: str, embedding_type: Optional[str] = None):
51
+ """Generate embedding using the embedding manager."""
52
+ # The embedding_manager.embed method is synchronous, not async
53
+ embeddings = self.embedding_manager.embed(text, input_type="document")
54
+ if embeddings and len(embeddings) > 0:
55
+ return embeddings[0]
56
+ return None
57
+
58
+ def get_current_project(self) -> Optional[str]:
59
+ """Get current project from resolver."""
60
+ return self.project_resolver.get_current_project()
61
+
62
+ def normalize_project_name(self, project_name: str) -> str:
63
+ """Normalize project name using resolver."""
64
+ return self.project_resolver.normalize_project_name(project_name)
@@ -0,0 +1,57 @@
1
+ """Configuration and environment constants for Claude Self-Reflect MCP server."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from dotenv import load_dotenv
6
+
7
+ # Load environment variables
8
+ load_dotenv()
9
+
10
+ # API Keys
11
+ VOYAGE_API_KEY = os.getenv('VOYAGE_API_KEY', '')
12
+ QDRANT_URL = os.getenv('QDRANT_URL', 'http://localhost:6333')
13
+
14
+ # Embedding Configuration
15
+ PREFER_LOCAL_EMBEDDINGS = os.getenv('PREFER_LOCAL_EMBEDDINGS', 'true').lower() == 'true'
16
+ VOYAGE_MODEL = "voyage-3-lite"
17
+ LOCAL_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
18
+
19
+ # Decay Configuration
20
+ USE_DECAY = os.getenv('USE_DECAY', 'false').lower() == 'true'
21
+ DECAY_SCALE_DAYS = int(os.getenv('DECAY_SCALE_DAYS', '90'))
22
+ DECAY_WEIGHT = float(os.getenv('DECAY_WEIGHT', '0.3'))
23
+ USE_NATIVE_DECAY = os.getenv('USE_NATIVE_DECAY', 'false').lower() == 'true'
24
+
25
+ # Search Configuration
26
+ DEFAULT_SEARCH_LIMIT = 5
27
+ MAX_SEARCH_LIMIT = 100
28
+ DEFAULT_MIN_SCORE = 0.3
29
+
30
+ # Memory Management
31
+ MAX_RESULTS_PER_COLLECTION = int(os.getenv('MAX_RESULTS_PER_COLLECTION', '10'))
32
+ MAX_TOTAL_RESULTS = int(os.getenv('MAX_TOTAL_RESULTS', '1000'))
33
+ MAX_MEMORY_MB = int(os.getenv('MAX_MEMORY_MB', '500'))
34
+
35
+ # Connection Pool Configuration
36
+ POOL_SIZE = int(os.getenv('QDRANT_POOL_SIZE', '10'))
37
+ POOL_MAX_OVERFLOW = int(os.getenv('QDRANT_POOL_OVERFLOW', '5'))
38
+ POOL_TIMEOUT = float(os.getenv('QDRANT_POOL_TIMEOUT', '30.0'))
39
+ RETRY_ATTEMPTS = int(os.getenv('QDRANT_RETRY_ATTEMPTS', '3'))
40
+ RETRY_DELAY = float(os.getenv('QDRANT_RETRY_DELAY', '1.0'))
41
+
42
+ # Performance Configuration
43
+ MAX_CONCURRENT_SEARCHES = int(os.getenv('MAX_CONCURRENT_SEARCHES', '10'))
44
+ ENABLE_PARALLEL_SEARCH = os.getenv('ENABLE_PARALLEL_SEARCH', 'true').lower() == 'true'
45
+
46
+ # Paths
47
+ CLAUDE_PROJECTS_PATH = Path.home() / '.claude' / 'projects'
48
+ CONFIG_PATH = Path.home() / '.claude-self-reflect' / 'config'
49
+
50
+ # Collection Naming
51
+ VOYAGE_SUFFIX = '_voyage'
52
+ LOCAL_SUFFIX = '_local'
53
+
54
+ # Logging
55
+ import logging
56
+ logging.basicConfig(level=logging.INFO)
57
+ logger = logging.getLogger(__name__)