claude-self-reflect 3.2.4 → 3.3.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.
Files changed (33) hide show
  1. package/.claude/agents/claude-self-reflect-test.md +595 -528
  2. package/.claude/agents/reflection-specialist.md +59 -3
  3. package/README.md +14 -5
  4. package/mcp-server/run-mcp.sh +49 -5
  5. package/mcp-server/src/app_context.py +64 -0
  6. package/mcp-server/src/config.py +57 -0
  7. package/mcp-server/src/connection_pool.py +286 -0
  8. package/mcp-server/src/decay_manager.py +106 -0
  9. package/mcp-server/src/embedding_manager.py +64 -40
  10. package/mcp-server/src/embeddings_old.py +141 -0
  11. package/mcp-server/src/models.py +64 -0
  12. package/mcp-server/src/parallel_search.py +371 -0
  13. package/mcp-server/src/project_resolver.py +5 -0
  14. package/mcp-server/src/reflection_tools.py +206 -0
  15. package/mcp-server/src/rich_formatting.py +196 -0
  16. package/mcp-server/src/search_tools.py +826 -0
  17. package/mcp-server/src/server.py +127 -1720
  18. package/mcp-server/src/temporal_design.py +132 -0
  19. package/mcp-server/src/temporal_tools.py +597 -0
  20. package/mcp-server/src/temporal_utils.py +384 -0
  21. package/mcp-server/src/utils.py +150 -67
  22. package/package.json +10 -1
  23. package/scripts/add-timestamp-indexes.py +134 -0
  24. package/scripts/check-collections.py +29 -0
  25. package/scripts/debug-august-parsing.py +76 -0
  26. package/scripts/debug-import-single.py +91 -0
  27. package/scripts/debug-project-resolver.py +82 -0
  28. package/scripts/debug-temporal-tools.py +135 -0
  29. package/scripts/delta-metadata-update.py +547 -0
  30. package/scripts/import-conversations-unified.py +53 -2
  31. package/scripts/precompact-hook.sh +33 -0
  32. package/scripts/streaming-watcher.py +1443 -0
  33. package/scripts/utils.py +39 -0
@@ -0,0 +1,196 @@
1
+ """Rich formatting for search results with emojis and enhanced display."""
2
+
3
+ import json
4
+ import time
5
+ from datetime import datetime, timezone
6
+ from typing import List, Dict, Any, Optional
7
+ import logging
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ def format_search_results_rich(
13
+ results: List[Dict],
14
+ query: str,
15
+ target_project: str,
16
+ collections_searched: int,
17
+ timing_info: Dict[str, float],
18
+ start_time: float,
19
+ brief: bool = False,
20
+ include_raw: bool = False,
21
+ indexing_status: Optional[Dict] = None
22
+ ) -> str:
23
+ """Format search results with rich formatting including emojis and performance metrics."""
24
+
25
+ # Initialize upfront summary
26
+ upfront_summary = ""
27
+
28
+ # Show result summary with emojis
29
+ if results:
30
+ score_info = "high" if results[0]['score'] >= 0.85 else "good" if results[0]['score'] >= 0.75 else "partial"
31
+ upfront_summary += f"🎯 RESULTS: {len(results)} matches ({score_info} relevance, top score: {results[0]['score']:.3f})\n"
32
+
33
+ # Show performance metrics
34
+ total_time = time.time() - start_time
35
+ indexing_info = ""
36
+ if indexing_status and indexing_status.get("percentage", 100) < 100.0:
37
+ indexing_info = f" | 📊 {indexing_status['indexed_conversations']}/{indexing_status['total_conversations']} indexed"
38
+ upfront_summary += f"⚡ PERFORMANCE: {int(total_time * 1000)}ms ({collections_searched} collections searched{indexing_info})\n"
39
+ else:
40
+ upfront_summary += f"❌ NO RESULTS: No conversations found matching '{query}'\n"
41
+
42
+ # Start XML format with upfront summary
43
+ result_text = upfront_summary + "\n<search>\n"
44
+
45
+ # Add indexing status if not fully baselined
46
+ if indexing_status and indexing_status.get("percentage", 100) < 95.0:
47
+ result_text += f' <info status="indexing" progress="{indexing_status["percentage"]:.1f}%" backlog="{indexing_status.get("backlog_count", 0)}">\n'
48
+ result_text += f' <message>📊 Indexing: {indexing_status["indexed_conversations"]}/{indexing_status["total_conversations"]} conversations ({indexing_status["percentage"]:.1f}% complete)</message>\n'
49
+ result_text += f" </info>\n"
50
+
51
+ # Add high-level result summary
52
+ if results:
53
+ # Count time-based results
54
+ now = datetime.now(timezone.utc)
55
+ today_count = 0
56
+ yesterday_count = 0
57
+ week_count = 0
58
+
59
+ for result in results:
60
+ timestamp_str = result.get('timestamp', '')
61
+ if timestamp_str:
62
+ try:
63
+ # Clean timestamp
64
+ timestamp_clean = timestamp_str.replace('Z', '+00:00') if timestamp_str.endswith('Z') else timestamp_str
65
+ timestamp_dt = datetime.fromisoformat(timestamp_clean)
66
+ if timestamp_dt.tzinfo is None:
67
+ timestamp_dt = timestamp_dt.replace(tzinfo=timezone.utc)
68
+
69
+ days_ago = (now - timestamp_dt).days
70
+ if days_ago == 0:
71
+ today_count += 1
72
+ elif days_ago == 1:
73
+ yesterday_count += 1
74
+ if days_ago <= 7:
75
+ week_count += 1
76
+ except:
77
+ pass
78
+
79
+ # Compact summary with key info
80
+ time_info = ""
81
+ if today_count > 0:
82
+ time_info = f"{today_count} today"
83
+ elif yesterday_count > 0:
84
+ time_info = f"{yesterday_count} yesterday"
85
+ elif week_count > 0:
86
+ time_info = f"{week_count} this week"
87
+ else:
88
+ time_info = "older results"
89
+
90
+ score_info = "high" if results[0]['score'] >= 0.85 else "good" if results[0]['score'] >= 0.75 else "partial"
91
+
92
+ result_text += f' <summary count="{len(results)}" relevance="{score_info}" recency="{time_info}" top-score="{results[0]["score"]:.3f}">\n'
93
+
94
+ # Short preview of top result
95
+ top_excerpt = results[0].get('excerpt', results[0].get('content', ''))[:100].strip()
96
+ if '...' not in top_excerpt:
97
+ top_excerpt += "..."
98
+ result_text += f' <preview>{top_excerpt}</preview>\n'
99
+ result_text += f" </summary>\n"
100
+ else:
101
+ result_text += f" <result-summary>\n"
102
+ result_text += f" <headline>No matches found</headline>\n"
103
+ result_text += f" <relevance>No conversations matched your query</relevance>\n"
104
+ result_text += f" </result-summary>\n"
105
+
106
+ # Add metadata
107
+ result_text += f" <meta>\n"
108
+ result_text += f" <q>{query}</q>\n"
109
+ result_text += f" <scope>{target_project if target_project != 'all' else 'all'}</scope>\n"
110
+ result_text += f" <count>{len(results)}</count>\n"
111
+ if results:
112
+ result_text += f" <range>{results[-1]['score']:.3f}-{results[0]['score']:.3f}</range>\n"
113
+
114
+ # Add performance metadata
115
+ total_time = time.time() - start_time
116
+ result_text += f" <perf>\n"
117
+ result_text += f" <ttl>{int(total_time * 1000)}</ttl>\n"
118
+ result_text += f" <emb>{int((timing_info.get('embedding_end', 0) - timing_info.get('embedding_start', 0)) * 1000)}</emb>\n"
119
+ result_text += f" <srch>{int((timing_info.get('search_all_end', 0) - timing_info.get('search_all_start', 0)) * 1000)}</srch>\n"
120
+ result_text += f" <cols>{collections_searched}</cols>\n"
121
+ result_text += f" </perf>\n"
122
+ result_text += f" </meta>\n"
123
+
124
+ # Add individual results
125
+ result_text += " <results>\n"
126
+ for i, result in enumerate(results):
127
+ result_text += f' <r rank="{i+1}">\n'
128
+ result_text += f" <s>{result['score']:.3f}</s>\n"
129
+ result_text += f" <p>{result.get('project_name', 'unknown')}</p>\n"
130
+
131
+ # Calculate relative time
132
+ timestamp_str = result.get('timestamp', '')
133
+ if timestamp_str:
134
+ try:
135
+ timestamp_clean = timestamp_str.replace('Z', '+00:00') if timestamp_str.endswith('Z') else timestamp_str
136
+ timestamp_dt = datetime.fromisoformat(timestamp_clean)
137
+ if timestamp_dt.tzinfo is None:
138
+ timestamp_dt = timestamp_dt.replace(tzinfo=timezone.utc)
139
+ now = datetime.now(timezone.utc)
140
+ days_ago = (now - timestamp_dt).days
141
+ if days_ago == 0:
142
+ time_str = "today"
143
+ elif days_ago == 1:
144
+ time_str = "yesterday"
145
+ else:
146
+ time_str = f"{days_ago}d"
147
+ result_text += f" <t>{time_str}</t>\n"
148
+ except:
149
+ result_text += f" <t>unknown</t>\n"
150
+
151
+ # Get excerpt/content
152
+ excerpt = result.get('excerpt', result.get('content', ''))
153
+
154
+ if not brief and excerpt:
155
+ # Extract title from first line of excerpt
156
+ excerpt_lines = excerpt.split('\n')
157
+ title = excerpt_lines[0][:80] + "..." if len(excerpt_lines[0]) > 80 else excerpt_lines[0]
158
+ result_text += f" <title>{title}</title>\n"
159
+
160
+ # Key finding - summarize the main point
161
+ key_finding = excerpt[:100] + "..." if len(excerpt) > 100 else excerpt
162
+ result_text += f" <key-finding>{key_finding.strip()}</key-finding>\n"
163
+
164
+ # Always include excerpt
165
+ if brief:
166
+ brief_excerpt = excerpt[:100] + "..." if len(excerpt) > 100 else excerpt
167
+ result_text += f" <excerpt>{brief_excerpt.strip()}</excerpt>\n"
168
+ else:
169
+ result_text += f" <excerpt><![CDATA[{excerpt}]]></excerpt>\n"
170
+
171
+ # Add conversation ID if present
172
+ if result.get('conversation_id'):
173
+ result_text += f" <cid>{result['conversation_id']}</cid>\n"
174
+
175
+ # Include raw data if requested
176
+ if include_raw and result.get('raw_payload'):
177
+ result_text += " <raw>\n"
178
+ payload = result['raw_payload']
179
+ result_text += f" <txt><![CDATA[{payload.get('text', '')}]]></txt>\n"
180
+ result_text += f" <id>{result.get('id', '')}</id>\n"
181
+ result_text += " </raw>\n"
182
+
183
+ # Add metadata fields if present
184
+ if result.get('files_analyzed'):
185
+ result_text += f" <files>{', '.join(result['files_analyzed'][:5])}</files>\n"
186
+ if result.get('tools_used'):
187
+ result_text += f" <tools>{', '.join(result['tools_used'][:5])}</tools>\n"
188
+ if result.get('concepts'):
189
+ result_text += f" <concepts>{', '.join(result['concepts'][:5])}</concepts>\n"
190
+
191
+ result_text += " </r>\n"
192
+
193
+ result_text += " </results>\n"
194
+ result_text += "</search>\n"
195
+
196
+ return result_text