claude-mpm 5.6.10__py3-none-any.whl → 5.6.33__py3-none-any.whl
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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/cli/commands/commander.py +174 -4
- claude_mpm/cli/parsers/commander_parser.py +43 -10
- claude_mpm/cli/startup.py +140 -20
- claude_mpm/cli/startup_display.py +2 -1
- claude_mpm/commander/__init__.py +6 -0
- claude_mpm/commander/adapters/__init__.py +32 -3
- claude_mpm/commander/adapters/auggie.py +260 -0
- claude_mpm/commander/adapters/base.py +98 -1
- claude_mpm/commander/adapters/claude_code.py +32 -1
- claude_mpm/commander/adapters/codex.py +237 -0
- claude_mpm/commander/adapters/example_usage.py +310 -0
- claude_mpm/commander/adapters/mpm.py +389 -0
- claude_mpm/commander/adapters/registry.py +204 -0
- claude_mpm/commander/api/app.py +32 -16
- claude_mpm/commander/api/routes/messages.py +11 -11
- claude_mpm/commander/api/routes/projects.py +20 -20
- claude_mpm/commander/api/routes/sessions.py +19 -21
- claude_mpm/commander/api/routes/work.py +86 -50
- claude_mpm/commander/api/schemas.py +4 -0
- claude_mpm/commander/chat/cli.py +42 -3
- claude_mpm/commander/config.py +5 -3
- claude_mpm/commander/core/__init__.py +10 -0
- claude_mpm/commander/core/block_manager.py +325 -0
- claude_mpm/commander/core/response_manager.py +323 -0
- claude_mpm/commander/daemon.py +215 -10
- claude_mpm/commander/env_loader.py +59 -0
- claude_mpm/commander/frameworks/base.py +4 -1
- claude_mpm/commander/instance_manager.py +124 -11
- claude_mpm/commander/memory/__init__.py +45 -0
- claude_mpm/commander/memory/compression.py +347 -0
- claude_mpm/commander/memory/embeddings.py +230 -0
- claude_mpm/commander/memory/entities.py +310 -0
- claude_mpm/commander/memory/example_usage.py +290 -0
- claude_mpm/commander/memory/integration.py +325 -0
- claude_mpm/commander/memory/search.py +381 -0
- claude_mpm/commander/memory/store.py +657 -0
- claude_mpm/commander/registry.py +10 -4
- claude_mpm/commander/runtime/monitor.py +32 -2
- claude_mpm/commander/work/executor.py +38 -20
- claude_mpm/commander/workflow/event_handler.py +25 -3
- claude_mpm/core/claude_runner.py +152 -0
- claude_mpm/core/config.py +3 -3
- claude_mpm/core/config_constants.py +74 -9
- claude_mpm/core/constants.py +56 -12
- claude_mpm/core/interactive_session.py +5 -4
- claude_mpm/core/logging_utils.py +4 -2
- claude_mpm/core/network_config.py +148 -0
- claude_mpm/core/oneshot_session.py +7 -6
- claude_mpm/core/output_style_manager.py +37 -7
- claude_mpm/core/socketio_pool.py +13 -5
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +1 -1
- claude_mpm/hooks/claude_hooks/event_handlers.py +284 -89
- claude_mpm/hooks/claude_hooks/hook_handler.py +81 -32
- claude_mpm/hooks/claude_hooks/installer.py +90 -28
- claude_mpm/hooks/claude_hooks/memory_integration.py +1 -1
- claude_mpm/hooks/claude_hooks/response_tracking.py +1 -1
- claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +2 -2
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +2 -2
- claude_mpm/hooks/claude_hooks/services/container.py +310 -0
- claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +2 -2
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +2 -2
- claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
- claude_mpm/hooks/templates/pre_tool_use_template.py +6 -6
- claude_mpm/scripts/claude-hook-handler.sh +3 -3
- claude_mpm/services/command_deployment_service.py +44 -26
- claude_mpm/services/hook_installer_service.py +77 -8
- claude_mpm/services/pm_skills_deployer.py +3 -2
- claude_mpm/skills/__init__.py +2 -1
- claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
- claude_mpm/skills/registry.py +295 -90
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/METADATA +5 -3
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/RECORD +91 -94
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/WHEEL +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.6.10.dist-info → claude_mpm-5.6.33.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""Entity extraction from conversation messages.
|
|
2
|
+
|
|
3
|
+
Extracts structured entities like files, functions, errors, and commands
|
|
4
|
+
from conversation content for enhanced search and filtering.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any, Dict, List
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class EntityType(Enum):
|
|
17
|
+
"""Types of entities that can be extracted.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
FILE: File path (e.g., "src/auth.py")
|
|
21
|
+
FUNCTION: Function or method name (e.g., "login()")
|
|
22
|
+
CLASS: Class name (e.g., "UserService")
|
|
23
|
+
ERROR: Error type or message (e.g., "ValueError")
|
|
24
|
+
COMMAND: Shell command (e.g., "pytest tests/")
|
|
25
|
+
URL: Web URL (e.g., "https://example.com")
|
|
26
|
+
PACKAGE: Package or module name (e.g., "requests")
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
FILE = "file"
|
|
30
|
+
FUNCTION = "function"
|
|
31
|
+
CLASS = "class"
|
|
32
|
+
ERROR = "error"
|
|
33
|
+
COMMAND = "command"
|
|
34
|
+
URL = "url"
|
|
35
|
+
PACKAGE = "package"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class Entity:
|
|
40
|
+
"""Extracted entity from conversation.
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
type: Entity type
|
|
44
|
+
value: Entity value (file path, function name, etc.)
|
|
45
|
+
context: Surrounding context (optional)
|
|
46
|
+
metadata: Additional metadata
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
>>> entity = Entity(
|
|
50
|
+
... type=EntityType.FILE,
|
|
51
|
+
... value="src/auth.py",
|
|
52
|
+
... context="Fix the login bug in auth.py"
|
|
53
|
+
... )
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
type: EntityType
|
|
57
|
+
value: str
|
|
58
|
+
context: str = ""
|
|
59
|
+
metadata: Dict[str, Any] = None
|
|
60
|
+
|
|
61
|
+
def __post_init__(self) -> None:
|
|
62
|
+
"""Initialize metadata if not provided."""
|
|
63
|
+
if self.metadata is None:
|
|
64
|
+
self.metadata = {}
|
|
65
|
+
|
|
66
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
67
|
+
"""Convert to dictionary for JSON serialization."""
|
|
68
|
+
return {
|
|
69
|
+
"type": self.type.value,
|
|
70
|
+
"value": self.value,
|
|
71
|
+
"context": self.context,
|
|
72
|
+
"metadata": self.metadata,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def from_dict(cls, data: Dict[str, Any]) -> "Entity":
|
|
77
|
+
"""Create Entity from dictionary."""
|
|
78
|
+
return cls(
|
|
79
|
+
type=EntityType(data["type"]),
|
|
80
|
+
value=data["value"],
|
|
81
|
+
context=data.get("context", ""),
|
|
82
|
+
metadata=data.get("metadata", {}),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class EntityExtractor:
|
|
87
|
+
"""Extracts entities from conversation messages.
|
|
88
|
+
|
|
89
|
+
Uses regex patterns to identify files, functions, errors, commands, etc.
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
>>> extractor = EntityExtractor()
|
|
93
|
+
>>> entities = extractor.extract("Fix the login bug in src/auth.py")
|
|
94
|
+
>>> entities[0].type
|
|
95
|
+
<EntityType.FILE: 'file'>
|
|
96
|
+
>>> entities[0].value
|
|
97
|
+
'src/auth.py'
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
# File patterns (common extensions and paths)
|
|
101
|
+
FILE_PATTERNS = [
|
|
102
|
+
r"\b[\w/\-\.]+\.(?:py|js|ts|tsx|jsx|java|cpp|c|h|go|rs|rb|php|cs|swift|kt|md|txt|json|yaml|yml|toml|xml|html|css|scss|sh|bash)\b",
|
|
103
|
+
r"\b(?:src|lib|tests?|scripts?|docs?|config)/[\w/\-\.]+\b",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
# Function patterns (with parens or common prefixes)
|
|
107
|
+
FUNCTION_PATTERNS = [
|
|
108
|
+
r"\b[a-z_][a-z0-9_]*\(\)",
|
|
109
|
+
r"\bdef\s+([a-z_][a-z0-9_]*)",
|
|
110
|
+
r"\bfunction\s+([a-z_][a-z0-9_]*)",
|
|
111
|
+
r"\basync\s+(?:def|function)\s+([a-z_][a-z0-9_]*)",
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
# Class patterns (PascalCase)
|
|
115
|
+
CLASS_PATTERNS = [
|
|
116
|
+
r"\bclass\s+([A-Z][a-zA-Z0-9_]*)",
|
|
117
|
+
r"\b([A-Z][a-zA-Z0-9_]+(?:Service|Controller|Manager|Handler|Repository|Model|View))\b",
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
# Error patterns
|
|
121
|
+
ERROR_PATTERNS = [
|
|
122
|
+
r"\b([A-Z][a-zA-Z]*Error)\b",
|
|
123
|
+
r"\b([A-Z][a-zA-Z]*Exception)\b",
|
|
124
|
+
r"error:\s+(.+?)(?:\n|$)",
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
# Command patterns (common CLI tools)
|
|
128
|
+
COMMAND_PATTERNS = [
|
|
129
|
+
r"\b(?:npm|yarn|pnpm|pip|poetry|cargo|go|make|docker|kubectl|git)\s+[\w\-]+",
|
|
130
|
+
r"\bpytest\s+[\w/\-\.]+",
|
|
131
|
+
r"\bpython\s+[\w/\-\.]+",
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
# URL patterns
|
|
135
|
+
URL_PATTERN = r"https?://[\w\.\-/\?=&#%]+"
|
|
136
|
+
|
|
137
|
+
# Package patterns
|
|
138
|
+
PACKAGE_PATTERNS = [
|
|
139
|
+
r"\bimport\s+([\w\.]+)",
|
|
140
|
+
r"\bfrom\s+([\w\.]+)\s+import",
|
|
141
|
+
r"\brequire\(['\"]([\w\-@/]+)['\"]",
|
|
142
|
+
r"\buse\s+([\w:]+)",
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
def extract(self, text: str) -> List[Entity]:
|
|
146
|
+
"""Extract all entities from text.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
text: Text to extract entities from
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
List of extracted entities
|
|
153
|
+
|
|
154
|
+
Example:
|
|
155
|
+
>>> entities = extractor.extract("Fix the login bug in src/auth.py")
|
|
156
|
+
"""
|
|
157
|
+
entities: List[Entity] = []
|
|
158
|
+
|
|
159
|
+
# Extract files
|
|
160
|
+
for pattern in self.FILE_PATTERNS:
|
|
161
|
+
for match in re.finditer(pattern, text):
|
|
162
|
+
entities.append(
|
|
163
|
+
Entity(
|
|
164
|
+
type=EntityType.FILE,
|
|
165
|
+
value=match.group(0),
|
|
166
|
+
context=self._get_context(text, match.start(), match.end()),
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Extract functions
|
|
171
|
+
for pattern in self.FUNCTION_PATTERNS:
|
|
172
|
+
for match in re.finditer(pattern, text):
|
|
173
|
+
# Use group 1 if capturing group exists, else group 0
|
|
174
|
+
value = match.group(1) if match.lastindex else match.group(0)
|
|
175
|
+
entities.append(
|
|
176
|
+
Entity(
|
|
177
|
+
type=EntityType.FUNCTION,
|
|
178
|
+
value=value.rstrip("()"), # Remove parens
|
|
179
|
+
context=self._get_context(text, match.start(), match.end()),
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Extract classes
|
|
184
|
+
for pattern in self.CLASS_PATTERNS:
|
|
185
|
+
for match in re.finditer(pattern, text):
|
|
186
|
+
value = match.group(1) if match.lastindex else match.group(0)
|
|
187
|
+
entities.append(
|
|
188
|
+
Entity(
|
|
189
|
+
type=EntityType.CLASS,
|
|
190
|
+
value=value,
|
|
191
|
+
context=self._get_context(text, match.start(), match.end()),
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Extract errors
|
|
196
|
+
for pattern in self.ERROR_PATTERNS:
|
|
197
|
+
for match in re.finditer(pattern, text):
|
|
198
|
+
value = match.group(1) if match.lastindex else match.group(0)
|
|
199
|
+
entities.append(
|
|
200
|
+
Entity(
|
|
201
|
+
type=EntityType.ERROR,
|
|
202
|
+
value=value.strip(),
|
|
203
|
+
context=self._get_context(text, match.start(), match.end()),
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Extract commands
|
|
208
|
+
for pattern in self.COMMAND_PATTERNS:
|
|
209
|
+
for match in re.finditer(pattern, text):
|
|
210
|
+
entities.append(
|
|
211
|
+
Entity(
|
|
212
|
+
type=EntityType.COMMAND,
|
|
213
|
+
value=match.group(0),
|
|
214
|
+
context=self._get_context(text, match.start(), match.end()),
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Extract URLs
|
|
219
|
+
for match in re.finditer(self.URL_PATTERN, text):
|
|
220
|
+
entities.append(
|
|
221
|
+
Entity(
|
|
222
|
+
type=EntityType.URL,
|
|
223
|
+
value=match.group(0),
|
|
224
|
+
context=self._get_context(text, match.start(), match.end()),
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Extract packages
|
|
229
|
+
for pattern in self.PACKAGE_PATTERNS:
|
|
230
|
+
for match in re.finditer(pattern, text):
|
|
231
|
+
value = match.group(1) if match.lastindex else match.group(0)
|
|
232
|
+
entities.append(
|
|
233
|
+
Entity(
|
|
234
|
+
type=EntityType.PACKAGE,
|
|
235
|
+
value=value,
|
|
236
|
+
context=self._get_context(text, match.start(), match.end()),
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Deduplicate entities (same type + value)
|
|
241
|
+
seen = set()
|
|
242
|
+
unique_entities = []
|
|
243
|
+
for entity in entities:
|
|
244
|
+
key = (entity.type, entity.value)
|
|
245
|
+
if key not in seen:
|
|
246
|
+
seen.add(key)
|
|
247
|
+
unique_entities.append(entity)
|
|
248
|
+
|
|
249
|
+
logger.debug("Extracted %d unique entities from text", len(unique_entities))
|
|
250
|
+
return unique_entities
|
|
251
|
+
|
|
252
|
+
def _get_context(self, text: str, start: int, end: int, window: int = 50) -> str:
|
|
253
|
+
"""Get surrounding context for entity.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
text: Full text
|
|
257
|
+
start: Entity start position
|
|
258
|
+
end: Entity end position
|
|
259
|
+
window: Characters to include before/after
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Context string with entity highlighted
|
|
263
|
+
"""
|
|
264
|
+
context_start = max(0, start - window)
|
|
265
|
+
context_end = min(len(text), end + window)
|
|
266
|
+
|
|
267
|
+
context = text[context_start:context_end]
|
|
268
|
+
|
|
269
|
+
# Truncate at sentence boundaries if possible
|
|
270
|
+
if context_start > 0 and ". " in context[:window]:
|
|
271
|
+
context = context.split(". ", 1)[1]
|
|
272
|
+
if context_end < len(text) and ". " in context[-window:]:
|
|
273
|
+
context = context.rsplit(". ", 1)[0] + "."
|
|
274
|
+
|
|
275
|
+
return context.strip()
|
|
276
|
+
|
|
277
|
+
def filter_by_type(
|
|
278
|
+
self, entities: List[Entity], entity_type: EntityType
|
|
279
|
+
) -> List[Entity]:
|
|
280
|
+
"""Filter entities by type.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
entities: List of entities to filter
|
|
284
|
+
entity_type: Type to filter for
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
Filtered list of entities
|
|
288
|
+
|
|
289
|
+
Example:
|
|
290
|
+
>>> files = extractor.filter_by_type(entities, EntityType.FILE)
|
|
291
|
+
"""
|
|
292
|
+
return [e for e in entities if e.type == entity_type]
|
|
293
|
+
|
|
294
|
+
def get_unique_values(
|
|
295
|
+
self, entities: List[Entity], entity_type: EntityType
|
|
296
|
+
) -> List[str]:
|
|
297
|
+
"""Get unique entity values for a type.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
entities: List of entities
|
|
301
|
+
entity_type: Type to extract values for
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
List of unique values
|
|
305
|
+
|
|
306
|
+
Example:
|
|
307
|
+
>>> files = extractor.get_unique_values(entities, EntityType.FILE)
|
|
308
|
+
['src/auth.py', 'tests/test_auth.py']
|
|
309
|
+
"""
|
|
310
|
+
return list({e.value for e in entities if e.type == entity_type})
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""Example usage of Commander memory system.
|
|
2
|
+
|
|
3
|
+
Demonstrates:
|
|
4
|
+
1. Capturing conversations from Project
|
|
5
|
+
2. Searching conversations semantically
|
|
6
|
+
3. Loading context for session resumption
|
|
7
|
+
4. Entity extraction and filtering
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
|
|
12
|
+
from ..models.project import Project, ProjectState, ThreadMessage
|
|
13
|
+
from .entities import EntityType
|
|
14
|
+
from .integration import MemoryIntegration
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def example_basic_usage():
|
|
18
|
+
"""Example 1: Basic conversation capture and search."""
|
|
19
|
+
print("\n=== Example 1: Basic Usage ===\n")
|
|
20
|
+
|
|
21
|
+
# Initialize memory integration
|
|
22
|
+
memory = MemoryIntegration.create()
|
|
23
|
+
|
|
24
|
+
# Create sample project with conversation
|
|
25
|
+
project = Project(
|
|
26
|
+
id="proj-example-123",
|
|
27
|
+
path="/Users/masa/Projects/example-app",
|
|
28
|
+
name="example-app",
|
|
29
|
+
state=ProjectState.IDLE,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Add sample conversation to project
|
|
33
|
+
project.thread = [
|
|
34
|
+
ThreadMessage(
|
|
35
|
+
id="msg-1",
|
|
36
|
+
role="user",
|
|
37
|
+
content="Fix the login authentication bug in src/auth.py",
|
|
38
|
+
),
|
|
39
|
+
ThreadMessage(
|
|
40
|
+
id="msg-2",
|
|
41
|
+
role="assistant",
|
|
42
|
+
content="I'll investigate the authentication bug. Let me read the auth.py file.",
|
|
43
|
+
),
|
|
44
|
+
ThreadMessage(
|
|
45
|
+
id="msg-3",
|
|
46
|
+
role="assistant",
|
|
47
|
+
content="Found the issue in UserService.authenticate() - the token validation was missing expiry check. Fixed it.",
|
|
48
|
+
),
|
|
49
|
+
ThreadMessage(
|
|
50
|
+
id="msg-4",
|
|
51
|
+
role="user",
|
|
52
|
+
content="Great! Can you also add tests for this fix?",
|
|
53
|
+
),
|
|
54
|
+
ThreadMessage(
|
|
55
|
+
id="msg-5",
|
|
56
|
+
role="assistant",
|
|
57
|
+
content="Added test_token_expiry_validation() in tests/test_auth.py. All tests passing.",
|
|
58
|
+
),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
# Capture conversation
|
|
62
|
+
conversation = await memory.capture_project_conversation(
|
|
63
|
+
project, instance_name="claude-code-1", session_id="sess-abc123"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
print(f"✅ Captured conversation: {conversation.id}")
|
|
67
|
+
print(f" Messages: {len(conversation.messages)}")
|
|
68
|
+
print(f" Summary: {conversation.summary}")
|
|
69
|
+
print(
|
|
70
|
+
f" Entities extracted: {len([e for msg in conversation.messages for e in msg.entities])}"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Search conversations
|
|
74
|
+
print("\n🔍 Searching for 'authentication bug'...")
|
|
75
|
+
results = await memory.search_conversations(
|
|
76
|
+
"authentication bug fix", project_id=project.id, limit=3
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
for i, result in enumerate(results, 1):
|
|
80
|
+
print(f"\n{i}. Score: {result.score:.3f}")
|
|
81
|
+
print(f" Conversation: {result.conversation.id}")
|
|
82
|
+
print(f" Snippet: {result.snippet[:100]}...")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
async def example_entity_search():
|
|
86
|
+
"""Example 2: Entity-based search and filtering."""
|
|
87
|
+
print("\n=== Example 2: Entity Search ===\n")
|
|
88
|
+
|
|
89
|
+
memory = MemoryIntegration.create()
|
|
90
|
+
|
|
91
|
+
# Create project with file references
|
|
92
|
+
project = Project(
|
|
93
|
+
id="proj-example-456",
|
|
94
|
+
path="/Users/masa/Projects/example-app",
|
|
95
|
+
name="example-app",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
project.thread = [
|
|
99
|
+
ThreadMessage(
|
|
100
|
+
id="msg-1",
|
|
101
|
+
role="user",
|
|
102
|
+
content="Update the UserService class in src/services/user_service.py",
|
|
103
|
+
),
|
|
104
|
+
ThreadMessage(
|
|
105
|
+
id="msg-2",
|
|
106
|
+
role="assistant",
|
|
107
|
+
content="I'll update UserService.create_user() to include email validation. Also updating tests/test_user_service.py.",
|
|
108
|
+
),
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
# Capture
|
|
112
|
+
conversation = await memory.capture_project_conversation(
|
|
113
|
+
project, instance_name="claude-code-2"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Extract entities
|
|
117
|
+
entities = []
|
|
118
|
+
for msg in conversation.messages:
|
|
119
|
+
entities.extend(msg.entities)
|
|
120
|
+
|
|
121
|
+
# Filter by type
|
|
122
|
+
files = memory.extractor.get_unique_values(
|
|
123
|
+
[memory.extractor.Entity.from_dict(e) for e in entities], EntityType.FILE
|
|
124
|
+
)
|
|
125
|
+
print(f"📁 Files mentioned: {files}")
|
|
126
|
+
|
|
127
|
+
classes = memory.extractor.get_unique_values(
|
|
128
|
+
[memory.extractor.Entity.from_dict(e) for e in entities], EntityType.CLASS
|
|
129
|
+
)
|
|
130
|
+
print(f"🏗️ Classes mentioned: {classes}")
|
|
131
|
+
|
|
132
|
+
# Search by entity
|
|
133
|
+
print("\n🔍 Finding conversations that mention 'src/services/user_service.py'...")
|
|
134
|
+
results = await memory.search.search_by_entities(
|
|
135
|
+
EntityType.FILE,
|
|
136
|
+
"src/services/user_service.py",
|
|
137
|
+
project_id=project.id,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
print(f"Found {len(results)} conversations mentioning this file")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def example_context_loading():
|
|
144
|
+
"""Example 3: Load context for session resumption."""
|
|
145
|
+
print("\n=== Example 3: Context Loading for Session Resumption ===\n")
|
|
146
|
+
|
|
147
|
+
memory = MemoryIntegration.create()
|
|
148
|
+
|
|
149
|
+
# Create multiple conversations (simulating historical work)
|
|
150
|
+
project = Project(
|
|
151
|
+
id="proj-example-789",
|
|
152
|
+
path="/Users/masa/Projects/example-app",
|
|
153
|
+
name="example-app",
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Conversation 1: Week ago
|
|
157
|
+
project.thread = [
|
|
158
|
+
ThreadMessage(
|
|
159
|
+
id="msg-1",
|
|
160
|
+
role="user",
|
|
161
|
+
content="Implement user registration with email verification",
|
|
162
|
+
),
|
|
163
|
+
ThreadMessage(
|
|
164
|
+
id="msg-2",
|
|
165
|
+
role="assistant",
|
|
166
|
+
content="Implemented registration in src/auth.py with email service integration",
|
|
167
|
+
),
|
|
168
|
+
]
|
|
169
|
+
await memory.capture_project_conversation(project, instance_name="claude-code-1")
|
|
170
|
+
|
|
171
|
+
# Conversation 2: Yesterday
|
|
172
|
+
project.thread = [
|
|
173
|
+
ThreadMessage(
|
|
174
|
+
id="msg-3",
|
|
175
|
+
role="user",
|
|
176
|
+
content="Fix the email verification bug - tokens not expiring",
|
|
177
|
+
),
|
|
178
|
+
ThreadMessage(
|
|
179
|
+
id="msg-4",
|
|
180
|
+
role="assistant",
|
|
181
|
+
content="Fixed token expiry check in src/auth.py and added tests",
|
|
182
|
+
),
|
|
183
|
+
]
|
|
184
|
+
await memory.capture_project_conversation(project, instance_name="claude-code-1")
|
|
185
|
+
|
|
186
|
+
# Load context for resumption
|
|
187
|
+
print("📖 Loading context for session resumption...")
|
|
188
|
+
context = await memory.load_context_for_session(
|
|
189
|
+
project.id, max_tokens=4000, limit_conversations=10
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
print(f"✅ Loaded context ({len(context)} chars):\n")
|
|
193
|
+
print(context[:500] + "...\n")
|
|
194
|
+
|
|
195
|
+
print(
|
|
196
|
+
"This context would be injected into the new session to provide historical awareness."
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
async def example_similarity_search():
|
|
201
|
+
"""Example 4: Find similar conversations."""
|
|
202
|
+
print("\n=== Example 4: Similarity Search ===\n")
|
|
203
|
+
|
|
204
|
+
memory = MemoryIntegration.create()
|
|
205
|
+
|
|
206
|
+
# Create project with multiple conversations
|
|
207
|
+
project = Project(
|
|
208
|
+
id="proj-example-999",
|
|
209
|
+
path="/Users/masa/Projects/example-app",
|
|
210
|
+
name="example-app",
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Reference conversation
|
|
214
|
+
project.thread = [
|
|
215
|
+
ThreadMessage(
|
|
216
|
+
id="msg-1",
|
|
217
|
+
role="user",
|
|
218
|
+
content="Fix the authentication bug in login flow",
|
|
219
|
+
),
|
|
220
|
+
ThreadMessage(
|
|
221
|
+
id="msg-2",
|
|
222
|
+
role="assistant",
|
|
223
|
+
content="Fixed token validation in src/auth.py",
|
|
224
|
+
),
|
|
225
|
+
]
|
|
226
|
+
ref_conv = await memory.capture_project_conversation(project)
|
|
227
|
+
|
|
228
|
+
# Similar conversation
|
|
229
|
+
project.thread = [
|
|
230
|
+
ThreadMessage(
|
|
231
|
+
id="msg-3",
|
|
232
|
+
role="user",
|
|
233
|
+
content="Update the login authentication to use OAuth",
|
|
234
|
+
),
|
|
235
|
+
ThreadMessage(
|
|
236
|
+
id="msg-4",
|
|
237
|
+
role="assistant",
|
|
238
|
+
content="Implemented OAuth in src/auth.py",
|
|
239
|
+
),
|
|
240
|
+
]
|
|
241
|
+
await memory.capture_project_conversation(project)
|
|
242
|
+
|
|
243
|
+
# Different conversation
|
|
244
|
+
project.thread = [
|
|
245
|
+
ThreadMessage(
|
|
246
|
+
id="msg-5",
|
|
247
|
+
role="user",
|
|
248
|
+
content="Add dark mode toggle to the UI",
|
|
249
|
+
),
|
|
250
|
+
ThreadMessage(
|
|
251
|
+
id="msg-6",
|
|
252
|
+
role="assistant",
|
|
253
|
+
content="Added dark mode CSS in styles/theme.css",
|
|
254
|
+
),
|
|
255
|
+
]
|
|
256
|
+
await memory.capture_project_conversation(project)
|
|
257
|
+
|
|
258
|
+
# Find similar
|
|
259
|
+
print(f"🔍 Finding conversations similar to: {ref_conv.id}")
|
|
260
|
+
similar = await memory.search.find_similar(ref_conv.id, limit=3)
|
|
261
|
+
|
|
262
|
+
for i, result in enumerate(similar, 1):
|
|
263
|
+
print(f"\n{i}. Similarity: {result.score:.3f}")
|
|
264
|
+
print(f" Conversation: {result.conversation.id}")
|
|
265
|
+
print(f" Summary: {result.conversation.summary}")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
async def main():
|
|
269
|
+
"""Run all examples."""
|
|
270
|
+
print("\n" + "=" * 60)
|
|
271
|
+
print("Commander Memory System - Example Usage")
|
|
272
|
+
print("=" * 60)
|
|
273
|
+
|
|
274
|
+
# Run examples
|
|
275
|
+
await example_basic_usage()
|
|
276
|
+
await example_entity_search()
|
|
277
|
+
await example_context_loading()
|
|
278
|
+
await example_similarity_search()
|
|
279
|
+
|
|
280
|
+
print("\n" + "=" * 60)
|
|
281
|
+
print("✅ All examples completed!")
|
|
282
|
+
print("=" * 60 + "\n")
|
|
283
|
+
|
|
284
|
+
print("📚 For more information, see:")
|
|
285
|
+
print(" - src/claude_mpm/commander/memory/README.md")
|
|
286
|
+
print(" - API documentation in each module")
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
if __name__ == "__main__":
|
|
290
|
+
asyncio.run(main())
|