claude-mpm 4.7.5__py3-none-any.whl → 4.7.6__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.
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 4.7.5
1
+ 4.7.6
@@ -374,7 +374,9 @@ class ConfigureCommand(BaseCommand):
374
374
  self.console.print(menu_panel)
375
375
  self.console.print()
376
376
 
377
- return Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="q")
377
+ choice = Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="q")
378
+ # Strip whitespace to handle leading/trailing spaces
379
+ return choice.strip().lower()
378
380
 
379
381
  def _manage_agents(self) -> None:
380
382
  """Agent management interface."""
@@ -425,7 +427,7 @@ class ConfigureCommand(BaseCommand):
425
427
  table.add_column("ID", style="dim", width=3)
426
428
  table.add_column("Name", style="cyan", width=22)
427
429
  table.add_column("Status", width=12)
428
- table.add_column("Description", style="white", width=45)
430
+ table.add_column("Description", style="bold cyan", width=45)
429
431
  table.add_column("Model/Tools", style="dim", width=20)
430
432
 
431
433
  for idx, agent in enumerate(agents, 1):
@@ -456,12 +458,11 @@ class ConfigureCommand(BaseCommand):
456
458
  except Exception:
457
459
  tools_display = "Default"
458
460
 
459
- # Truncate description for table display
460
- desc_display = (
461
- agent.description[:42] + "..."
462
- if len(agent.description) > 42
463
- else agent.description
464
- )
461
+ # Truncate description for table display with bright styling
462
+ if len(agent.description) > 42:
463
+ desc_display = f"[cyan]{agent.description[:42]}[/cyan][dim]...[/dim]"
464
+ else:
465
+ desc_display = f"[cyan]{agent.description}[/cyan]"
465
466
 
466
467
  table.add_row(str(idx), agent.name, status, desc_display, tools_display)
467
468
 
@@ -821,6 +822,61 @@ class ConfigureCommand(BaseCommand):
821
822
  with self.console.pager():
822
823
  self.console.print(syntax)
823
824
 
825
+ def _reset_agent_defaults(self, agents: List[AgentConfig]) -> None:
826
+ """Reset an agent to default enabled state and remove custom template.
827
+
828
+ This method:
829
+ - Prompts for agent ID
830
+ - Resets agent to enabled state
831
+ - Removes any custom template overrides
832
+ - Shows success/error messages
833
+ """
834
+ agent_id = Prompt.ask("Enter agent ID to reset to defaults")
835
+
836
+ try:
837
+ idx = int(agent_id) - 1
838
+ if 0 <= idx < len(agents):
839
+ agent = agents[idx]
840
+
841
+ # Confirm the reset action
842
+ if not Confirm.ask(
843
+ f"[yellow]Reset '{agent.name}' to defaults? This will:[/yellow]\n"
844
+ " - Enable the agent\n"
845
+ " - Remove custom template (if any)\n"
846
+ "[yellow]Continue?[/yellow]"
847
+ ):
848
+ self.console.print("[yellow]Reset cancelled.[/yellow]")
849
+ Prompt.ask("Press Enter to continue")
850
+ return
851
+
852
+ # Enable the agent
853
+ self.agent_manager.set_agent_enabled(agent.name, True)
854
+
855
+ # Remove custom template if exists
856
+ template_path = self._get_agent_template_path(agent.name)
857
+ if template_path.exists() and not str(template_path).startswith(
858
+ str(self.agent_manager.templates_dir)
859
+ ):
860
+ # This is a custom template, remove it
861
+ template_path.unlink(missing_ok=True)
862
+ self.console.print(
863
+ f"[green]✓ Removed custom template for '{agent.name}'[/green]"
864
+ )
865
+
866
+ self.console.print(
867
+ f"[green]✓ Agent '{agent.name}' reset to defaults![/green]"
868
+ )
869
+ self.console.print(
870
+ "[dim]Agent is now enabled with system template.[/dim]"
871
+ )
872
+ else:
873
+ self.console.print("[red]Invalid agent ID.[/red]")
874
+
875
+ except ValueError:
876
+ self.console.print("[red]Invalid input. Please enter a number.[/red]")
877
+
878
+ Prompt.ask("Press Enter to continue")
879
+
824
880
  def _view_agent_details(self, agents: List[AgentConfig]) -> None:
825
881
  """View detailed information about an agent."""
826
882
  agent_id = Prompt.ask("Enter agent ID to view")
@@ -1331,7 +1387,7 @@ class ConfigureCommand(BaseCommand):
1331
1387
  table.add_column("ID", style="dim", width=5)
1332
1388
  table.add_column("Agent", style="cyan", width=25)
1333
1389
  table.add_column("Status", width=15)
1334
- table.add_column("Description", style="white", width=45)
1390
+ table.add_column("Description", style="bold cyan", width=45)
1335
1391
 
1336
1392
  for idx, agent in enumerate(agents, 1):
1337
1393
  # Agent is ENABLED if NOT in disabled list
@@ -1341,11 +1397,13 @@ class ConfigureCommand(BaseCommand):
1341
1397
  if is_enabled
1342
1398
  else "[red]✗ Disabled[/red]"
1343
1399
  )
1344
- desc_display = (
1345
- agent.description[:42] + "..."
1346
- if len(agent.description) > 42
1347
- else agent.description
1348
- )
1400
+ # Format description with bright styling
1401
+ if len(agent.description) > 42:
1402
+ desc_display = (
1403
+ f"[cyan]{agent.description[:42]}[/cyan][dim]...[/dim]"
1404
+ )
1405
+ else:
1406
+ desc_display = f"[cyan]{agent.description}[/cyan]"
1349
1407
  table.add_row(str(idx), agent.name, status, desc_display)
1350
1408
 
1351
1409
  self.console.print(table)
@@ -1,13 +1,19 @@
1
1
  """Hook system for claude-mpm."""
2
2
 
3
3
  from .base_hook import BaseHook, HookContext, HookResult, HookType
4
+ from .kuzu_enrichment_hook import KuzuEnrichmentHook, get_kuzu_enrichment_hook
4
5
  from .kuzu_memory_hook import KuzuMemoryHook, get_kuzu_memory_hook
6
+ from .kuzu_response_hook import KuzuResponseHook, get_kuzu_response_hook
5
7
 
6
8
  __all__ = [
7
9
  "BaseHook",
8
10
  "HookContext",
9
11
  "HookResult",
10
12
  "HookType",
13
+ "KuzuEnrichmentHook",
11
14
  "KuzuMemoryHook",
15
+ "KuzuResponseHook",
16
+ "get_kuzu_enrichment_hook",
12
17
  "get_kuzu_memory_hook",
18
+ "get_kuzu_response_hook",
13
19
  ]
@@ -0,0 +1,263 @@
1
+ """
2
+ Kuzu-Memory Pre-Delegation Enrichment Hook
3
+ ==========================================
4
+
5
+ Enriches agent delegation context with relevant memories from kuzu-memory
6
+ before the agent receives the task. This is the READ side of bidirectional
7
+ enrichment.
8
+
9
+ WHY: Agents need access to relevant historical knowledge when performing tasks.
10
+ This hook retrieves memories from kuzu-memory and injects them into the
11
+ delegation context.
12
+
13
+ DESIGN DECISIONS:
14
+ - Priority 10 to run early, before other context modifications
15
+ - Reuses KuzuMemoryHook's retrieval methods for consistency
16
+ - Injects memories as a dedicated section in agent context
17
+ - Falls back gracefully if kuzu-memory is not available
18
+ """
19
+
20
+ import logging
21
+ from typing import Any, Dict, Optional
22
+
23
+ from claude_mpm.hooks.base_hook import HookContext, HookResult, PreDelegationHook
24
+ from claude_mpm.hooks.kuzu_memory_hook import get_kuzu_memory_hook
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class KuzuEnrichmentHook(PreDelegationHook):
30
+ """
31
+ Hook that enriches agent delegation context with kuzu-memory.
32
+
33
+ This hook:
34
+ 1. Extracts the task/prompt from delegation context
35
+ 2. Retrieves relevant memories from kuzu-memory
36
+ 3. Injects memories into agent context
37
+ 4. Formats memories for optimal agent understanding
38
+ """
39
+
40
+ def __init__(self):
41
+ """Initialize the kuzu-memory enrichment hook."""
42
+ super().__init__(name="kuzu_memory_enrichment", priority=10)
43
+
44
+ # Reuse the kuzu-memory hook instance for retrieval
45
+ self.kuzu_hook = get_kuzu_memory_hook()
46
+ self.enabled = self.kuzu_hook.enabled
47
+
48
+ if not self.enabled:
49
+ logger.info(
50
+ "Kuzu-memory enrichment hook disabled (kuzu-memory not available)"
51
+ )
52
+ else:
53
+ logger.info("Kuzu-memory enrichment hook enabled")
54
+
55
+ def validate(self, context: HookContext) -> bool:
56
+ """
57
+ Validate if hook should process this context.
58
+
59
+ Args:
60
+ context: Hook context to validate
61
+
62
+ Returns:
63
+ True if hook should execute
64
+ """
65
+ if not self.enabled:
66
+ return False
67
+
68
+ # Check base validation (enabled, correct hook type, has agent)
69
+ if not super().validate(context):
70
+ return False
71
+
72
+ # Must have agent and context data
73
+ if not context.data.get("agent"):
74
+ return False
75
+
76
+ return True
77
+
78
+ def execute(self, context: HookContext) -> HookResult:
79
+ """
80
+ Enrich delegation context with relevant memories.
81
+
82
+ Args:
83
+ context: Hook context containing delegation data
84
+
85
+ Returns:
86
+ HookResult with enriched context
87
+ """
88
+ if not self.enabled:
89
+ return HookResult(success=True, data=context.data, modified=False)
90
+
91
+ try:
92
+ # Extract query for memory retrieval
93
+ query = self._extract_query_from_context(context.data)
94
+
95
+ if not query:
96
+ logger.debug("No query extracted from context for memory retrieval")
97
+ return HookResult(success=True, data=context.data, modified=False)
98
+
99
+ # Retrieve relevant memories
100
+ memories = self.kuzu_hook._retrieve_memories(query)
101
+
102
+ if not memories:
103
+ logger.debug("No relevant memories found")
104
+ return HookResult(success=True, data=context.data, modified=False)
105
+
106
+ # Enrich context with memories
107
+ enriched_data = self._enrich_delegation_context(
108
+ context.data, memories, context.data.get("agent", "Agent")
109
+ )
110
+
111
+ logger.info(
112
+ f"Enriched delegation context with {len(memories)} memories for {context.data.get('agent')}"
113
+ )
114
+
115
+ return HookResult(
116
+ success=True,
117
+ data=enriched_data,
118
+ modified=True,
119
+ metadata={
120
+ "memories_added": len(memories),
121
+ "memory_source": "kuzu",
122
+ "agent": context.data.get("agent"),
123
+ },
124
+ )
125
+
126
+ except Exception as e:
127
+ logger.error(f"Error in kuzu enrichment hook: {e}")
128
+ # Don't fail the delegation if memory enrichment fails
129
+ return HookResult(
130
+ success=True,
131
+ data=context.data,
132
+ modified=False,
133
+ error=f"Memory enrichment failed: {e}",
134
+ )
135
+
136
+ def _extract_query_from_context(self, data: Dict[str, Any]) -> Optional[str]:
137
+ """
138
+ Extract query text for memory retrieval.
139
+
140
+ Args:
141
+ data: Delegation context data
142
+
143
+ Returns:
144
+ Query string or None
145
+ """
146
+ # Try various context fields
147
+ delegation_context = data.get("context", {})
148
+
149
+ # Handle string context
150
+ if isinstance(delegation_context, str):
151
+ return delegation_context
152
+
153
+ # Handle dict context
154
+ if isinstance(delegation_context, dict):
155
+ # Try common fields
156
+ for field in ["prompt", "task", "query", "user_request", "description"]:
157
+ if field in delegation_context:
158
+ value = delegation_context[field]
159
+ if isinstance(value, str):
160
+ return value
161
+
162
+ # If no specific field, join all string values
163
+ text_parts = [
164
+ str(v) for v in delegation_context.values() if isinstance(v, str)
165
+ ]
166
+ if text_parts:
167
+ return " ".join(text_parts)
168
+
169
+ # Fallback: try to get task or instruction directly
170
+ if "task" in data and isinstance(data["task"], str):
171
+ return data["task"]
172
+
173
+ if "instruction" in data and isinstance(data["instruction"], str):
174
+ return data["instruction"]
175
+
176
+ return None
177
+
178
+ def _enrich_delegation_context(
179
+ self, original_data: Dict[str, Any], memories: list, agent_name: str
180
+ ) -> Dict[str, Any]:
181
+ """
182
+ Enrich delegation context with memories.
183
+
184
+ Args:
185
+ original_data: Original delegation data
186
+ memories: Retrieved memories
187
+ agent_name: Name of the agent
188
+
189
+ Returns:
190
+ Enriched delegation data
191
+ """
192
+ # Format memories
193
+ memory_section = self._format_memory_section(memories, agent_name)
194
+
195
+ # Create enriched data
196
+ enriched_data = original_data.copy()
197
+
198
+ # Get existing context
199
+ delegation_context = enriched_data.get("context", {})
200
+ if isinstance(delegation_context, str):
201
+ delegation_context = {"prompt": delegation_context}
202
+
203
+ # Add memory section
204
+ if isinstance(delegation_context, dict):
205
+ # Prepend memory section to context
206
+ delegation_context["kuzu_memories"] = memory_section
207
+
208
+ # If there's a main prompt/task, prepend memory note
209
+ for field in ["prompt", "task", "instruction"]:
210
+ if field in delegation_context and isinstance(
211
+ delegation_context[field], str
212
+ ):
213
+ delegation_context[field] = (
214
+ f"{memory_section}\n\n{delegation_context[field]}"
215
+ )
216
+ break
217
+ else:
218
+ # If context is not dict, create new dict with memory
219
+ delegation_context = {
220
+ "kuzu_memories": memory_section,
221
+ "original_context": delegation_context,
222
+ }
223
+
224
+ enriched_data["context"] = delegation_context
225
+ enriched_data["_kuzu_enriched"] = True
226
+
227
+ return enriched_data
228
+
229
+ def _format_memory_section(self, memories: list, agent_name: str) -> str:
230
+ """
231
+ Format memories into a readable section.
232
+
233
+ Args:
234
+ memories: List of memory dictionaries
235
+ agent_name: Name of the agent
236
+
237
+ Returns:
238
+ Formatted memory section
239
+ """
240
+ memory_text = self.kuzu_hook._format_memories(memories)
241
+
242
+ return f"""
243
+ === RELEVANT KNOWLEDGE FROM KUZU MEMORY ===
244
+ {agent_name}, you have access to these relevant memories from the knowledge graph:
245
+
246
+ {memory_text}
247
+
248
+ INSTRUCTIONS: Review these memories before proceeding. Apply learned patterns and avoid known mistakes.
249
+ Use this knowledge to provide more informed and contextual responses.
250
+ ===========================================
251
+ """
252
+
253
+
254
+ # Create a singleton instance
255
+ _kuzu_enrichment_hook = None
256
+
257
+
258
+ def get_kuzu_enrichment_hook() -> KuzuEnrichmentHook:
259
+ """Get the singleton kuzu-memory enrichment hook instance."""
260
+ global _kuzu_enrichment_hook
261
+ if _kuzu_enrichment_hook is None:
262
+ _kuzu_enrichment_hook = KuzuEnrichmentHook()
263
+ return _kuzu_enrichment_hook
@@ -157,9 +157,13 @@ class KuzuMemoryHook(SubmitHook):
157
157
  List of relevant memory dictionaries
158
158
  """
159
159
  try:
160
- # Use kuzu-memory recall command
160
+ # Type narrowing: ensure kuzu_memory_cmd is not None before using
161
+ if self.kuzu_memory_cmd is None:
162
+ return []
163
+
164
+ # Use kuzu-memory recall command (v1.2.7+ syntax)
161
165
  result = subprocess.run(
162
- [self.kuzu_memory_cmd, "recall", query, "--format", "json"],
166
+ [self.kuzu_memory_cmd, "memory", "recall", query, "--format", "json"],
163
167
  capture_output=True,
164
168
  text=True,
165
169
  timeout=5,
@@ -168,10 +172,21 @@ class KuzuMemoryHook(SubmitHook):
168
172
  )
169
173
 
170
174
  if result.returncode == 0 and result.stdout:
171
- memories = json.loads(result.stdout)
172
- return memories if isinstance(memories, list) else []
173
-
174
- except (subprocess.TimeoutExpired, json.JSONDecodeError, Exception) as e:
175
+ try:
176
+ # Parse JSON with strict=False to handle control characters
177
+ data = json.loads(result.stdout, strict=False)
178
+ # v1.2.7 returns dict with 'memories' key, not array
179
+ if isinstance(data, dict):
180
+ memories = data.get("memories", [])
181
+ else:
182
+ memories = data if isinstance(data, list) else []
183
+ return memories
184
+ except json.JSONDecodeError as e:
185
+ logger.warning(f"Failed to parse kuzu-memory JSON output: {e}")
186
+ logger.debug(f"Raw output: {result.stdout[:200]}")
187
+ return [] # Graceful fallback
188
+
189
+ except (subprocess.TimeoutExpired, Exception) as e:
175
190
  logger.debug(f"Memory retrieval failed: {e}")
176
191
 
177
192
  return []
@@ -255,12 +270,12 @@ Note: Use the memories above to provide more informed and contextual responses.
255
270
  Returns:
256
271
  True if storage was successful
257
272
  """
258
- if not self.enabled:
273
+ if not self.enabled or self.kuzu_memory_cmd is None:
259
274
  return False
260
275
 
261
276
  try:
262
- # Use kuzu-memory remember command (synchronous)
263
- cmd = [self.kuzu_memory_cmd, "remember", content]
277
+ # Use kuzu-memory store command (v1.2.7+ syntax)
278
+ cmd = [self.kuzu_memory_cmd, "memory", "store", content]
264
279
 
265
280
  # Execute store command in project directory
266
281
  result = subprocess.run(
@@ -273,8 +288,10 @@ Note: Use the memories above to provide more informed and contextual responses.
273
288
  )
274
289
 
275
290
  if result.returncode == 0:
276
- logger.debug(f"Stored memory: {content[:50]}...")
291
+ logger.debug(f"Stored memory in kuzu: {content[:50]}...")
277
292
  return True
293
+ logger.warning(f"Failed to store memory in kuzu: {result.stderr}")
294
+ return False
278
295
 
279
296
  except Exception as e:
280
297
  logger.error(f"Failed to store memory: {e}")
@@ -0,0 +1,183 @@
1
+ """
2
+ Kuzu-Memory Response Learning Hook
3
+ ===================================
4
+
5
+ Captures assistant responses and extracts learnings to store in kuzu-memory.
6
+ This completes the bidirectional enrichment cycle:
7
+ - KuzuMemoryHook enriches prompts with memories (READ)
8
+ - KuzuResponseHook stores learnings from responses (WRITE)
9
+
10
+ WHY: To automatically capture and persist important information from agent
11
+ responses, enabling continuous learning across conversations.
12
+
13
+ DESIGN DECISIONS:
14
+ - Priority 80 to run late after main processing
15
+ - Reuses KuzuMemoryHook's storage methods for consistency
16
+ - Graceful degradation if kuzu-memory is not available
17
+ - Extracts structured learnings using patterns and AI
18
+ """
19
+
20
+ import logging
21
+ from typing import Any, Optional
22
+
23
+ from claude_mpm.hooks.base_hook import (
24
+ HookContext,
25
+ HookResult,
26
+ PostDelegationHook,
27
+ )
28
+ from claude_mpm.hooks.kuzu_memory_hook import get_kuzu_memory_hook
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class KuzuResponseHook(PostDelegationHook):
34
+ """
35
+ Hook that captures agent responses and stores learnings in kuzu-memory.
36
+
37
+ This hook:
38
+ 1. Processes agent responses after delegation completes
39
+ 2. Extracts important learnings and information
40
+ 3. Stores memories in kuzu-memory for future retrieval
41
+ 4. Tags memories for better categorization
42
+ """
43
+
44
+ def __init__(self):
45
+ """Initialize the kuzu-memory response learning hook."""
46
+ super().__init__(name="kuzu_response_learner", priority=80)
47
+
48
+ # Reuse the kuzu-memory hook instance for storage
49
+ self.kuzu_hook = get_kuzu_memory_hook()
50
+ self.enabled = self.kuzu_hook.enabled
51
+
52
+ if not self.enabled:
53
+ logger.info(
54
+ "Kuzu-memory response hook disabled (kuzu-memory not available)"
55
+ )
56
+ else:
57
+ logger.info("Kuzu-memory response learning hook enabled")
58
+
59
+ def validate(self, context: HookContext) -> bool:
60
+ """
61
+ Validate if hook should process this context.
62
+
63
+ Args:
64
+ context: Hook context to validate
65
+
66
+ Returns:
67
+ True if hook should execute
68
+ """
69
+ if not self.enabled:
70
+ return False
71
+
72
+ # Check base validation (enabled, correct hook type, has result)
73
+ if not super().validate(context):
74
+ return False
75
+
76
+ # Must have result data to extract learnings from
77
+ result_data = context.data.get("result")
78
+ if not result_data:
79
+ return False
80
+
81
+ return True
82
+
83
+ def execute(self, context: HookContext) -> HookResult:
84
+ """
85
+ Extract and store learnings from agent responses.
86
+
87
+ Args:
88
+ context: Hook context containing response data
89
+
90
+ Returns:
91
+ HookResult with success status and metadata
92
+ """
93
+ if not self.enabled:
94
+ return HookResult(success=True, data=context.data, modified=False)
95
+
96
+ try:
97
+ # Extract response content from various possible formats
98
+ result_data = context.data.get("result", {})
99
+ response_content = self._extract_response_content(result_data)
100
+
101
+ if not response_content:
102
+ logger.debug("No response content found for learning extraction")
103
+ return HookResult(success=True, data=context.data, modified=False)
104
+
105
+ # Extract and store learnings
106
+ count = self.kuzu_hook.extract_and_store_learnings(response_content)
107
+
108
+ if count > 0:
109
+ logger.info(f"Stored {count} learnings from agent response")
110
+ return HookResult(
111
+ success=True,
112
+ data=context.data,
113
+ modified=False,
114
+ metadata={"learnings_stored": count, "memory_backend": "kuzu"},
115
+ )
116
+
117
+ return HookResult(success=True, data=context.data, modified=False)
118
+
119
+ except Exception as e:
120
+ logger.error(f"Error in kuzu response hook: {e}")
121
+ # Don't fail the operation if learning extraction fails
122
+ return HookResult(
123
+ success=True,
124
+ data=context.data,
125
+ modified=False,
126
+ error=f"Learning extraction failed: {e}",
127
+ )
128
+
129
+ def _extract_response_content(self, result_data: Any) -> Optional[str]:
130
+ """
131
+ Extract response content from various result formats.
132
+
133
+ Args:
134
+ result_data: Result data in various possible formats
135
+
136
+ Returns:
137
+ Extracted response content as string, or None
138
+ """
139
+ if not result_data:
140
+ return None
141
+
142
+ # Handle dict format
143
+ if isinstance(result_data, dict):
144
+ # Try common response fields
145
+ for field in ["content", "text", "response", "output", "message"]:
146
+ if field in result_data:
147
+ content = result_data[field]
148
+ if isinstance(content, str):
149
+ return content
150
+ if isinstance(content, dict):
151
+ # Recursively extract from nested dict
152
+ return self._extract_response_content(content)
153
+
154
+ # If dict has no recognizable fields, try converting to string
155
+ return str(result_data)
156
+
157
+ # Handle string format
158
+ if isinstance(result_data, str):
159
+ return result_data
160
+
161
+ # Handle list format (concatenate items)
162
+ if isinstance(result_data, list):
163
+ items = []
164
+ for item in result_data:
165
+ extracted = self._extract_response_content(item)
166
+ if extracted:
167
+ items.append(extracted)
168
+ return "\n\n".join(items) if items else None
169
+
170
+ # Fallback to string conversion
171
+ return str(result_data) if result_data else None
172
+
173
+
174
+ # Create a singleton instance
175
+ _kuzu_response_hook = None
176
+
177
+
178
+ def get_kuzu_response_hook() -> KuzuResponseHook:
179
+ """Get the singleton kuzu-memory response hook instance."""
180
+ global _kuzu_response_hook
181
+ if _kuzu_response_hook is None:
182
+ _kuzu_response_hook = KuzuResponseHook()
183
+ return _kuzu_response_hook
@@ -495,7 +495,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
495
495
  base_cmd = config["mcp_command"]
496
496
  if len(base_cmd) > 0 and base_cmd[0] == config["package"]:
497
497
  # Simple case where first command is the package name
498
- mcp_command = ["pipx", "run", config["package"]] + base_cmd[1:]
498
+ mcp_command = ["pipx", "run", config["package"], *base_cmd[1:]]
499
499
  else:
500
500
  # Complex case - just try running the package with mcp arg
501
501
  mcp_command = ["pipx", "run", config["package"], "mcp"]
@@ -509,7 +509,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
509
509
  base_cmd = config["mcp_command"]
510
510
  if service_name == "kuzu-memory":
511
511
  # Special case for kuzu-memory with args
512
- mcp_command = ["pipx", "run", base_cmd[0]] + base_cmd[1:]
512
+ mcp_command = ["pipx", "run", base_cmd[0], *base_cmd[1:]]
513
513
  else:
514
514
  mcp_command = ["pipx", "run", *base_cmd]
515
515
  else:
@@ -550,7 +550,7 @@ class MCPServiceVerifier:
550
550
 
551
551
  try:
552
552
  # Build test command - add --help to test without side effects
553
- test_cmd = [command] + args[:2] if args else [command] # Include base args
553
+ test_cmd = [command, *args[:2]] if args else [command] # Include base args
554
554
  test_cmd.append("--help")
555
555
 
556
556
  result = subprocess.run(
@@ -41,8 +41,8 @@ class MemoryHookService(BaseService, MemoryHookInterface):
41
41
  These hooks ensure memory is properly managed and persisted.
42
42
 
43
43
  DESIGN DECISION: We register hooks for key lifecycle events:
44
- - Before Claude interaction: Load relevant memories
45
- - After Claude interaction: Save new memories
44
+ - Before Claude interaction: Load relevant memories (kuzu-memory + legacy)
45
+ - After Claude interaction: Save new memories (kuzu-memory + legacy)
46
46
  - On error: Ensure memory state is preserved
47
47
  """
48
48
  if not self.hook_service:
@@ -90,11 +90,93 @@ class MemoryHookService(BaseService, MemoryHookInterface):
90
90
  if success2:
91
91
  self.registered_hooks.append("memory_save")
92
92
 
93
- self.logger.debug("Memory hooks registered successfully")
93
+ self.logger.debug("Legacy memory hooks registered successfully")
94
+
95
+ # Register kuzu-memory hooks if available
96
+ self._register_kuzu_memory_hooks()
94
97
 
95
98
  except Exception as e:
96
99
  self.logger.warning(f"Failed to register memory hooks: {e}")
97
100
 
101
+ def _register_kuzu_memory_hooks(self):
102
+ """Register kuzu-memory bidirectional enrichment hooks.
103
+
104
+ WHY: Kuzu-memory provides persistent knowledge graph storage that works
105
+ across conversations. This enables:
106
+ 1. Delegation context enrichment with relevant memories (READ)
107
+ 2. Automatic learning extraction from responses (WRITE)
108
+
109
+ DESIGN DECISION: These hooks are separate from legacy memory hooks to
110
+ allow independent evolution and configuration. Both systems can coexist.
111
+ """
112
+ try:
113
+ # Check if kuzu-memory is enabled in config
114
+ from claude_mpm.core.config import Config
115
+
116
+ config = Config()
117
+ kuzu_config = config.get("memory.kuzu", {})
118
+ if isinstance(kuzu_config, dict):
119
+ kuzu_enabled = kuzu_config.get("enabled", True)
120
+ enrichment_enabled = kuzu_config.get("enrichment", True)
121
+ learning_enabled = kuzu_config.get("learning", True)
122
+ else:
123
+ # Default to enabled if config section doesn't exist
124
+ kuzu_enabled = True
125
+ enrichment_enabled = True
126
+ learning_enabled = True
127
+
128
+ if not kuzu_enabled:
129
+ self.logger.debug("Kuzu-memory disabled in configuration")
130
+ return
131
+
132
+ from claude_mpm.hooks import (
133
+ get_kuzu_enrichment_hook,
134
+ get_kuzu_response_hook,
135
+ )
136
+
137
+ # Get kuzu-memory hooks
138
+ enrichment_hook = get_kuzu_enrichment_hook()
139
+ learning_hook = get_kuzu_response_hook()
140
+
141
+ # Register enrichment hook (PreDelegationHook) if enabled
142
+ if enrichment_hook.enabled and enrichment_enabled:
143
+ success = self.hook_service.register_hook(enrichment_hook)
144
+ if success:
145
+ self.registered_hooks.append("kuzu_memory_enrichment")
146
+ self.logger.info(
147
+ "✅ Kuzu-memory enrichment enabled (prompts → memories)"
148
+ )
149
+ else:
150
+ self.logger.warning(
151
+ "Failed to register kuzu-memory enrichment hook"
152
+ )
153
+ elif not enrichment_enabled:
154
+ self.logger.debug("Kuzu-memory enrichment disabled in configuration")
155
+
156
+ # Register learning hook (PostDelegationHook) if enabled
157
+ if learning_hook.enabled and learning_enabled:
158
+ success = self.hook_service.register_hook(learning_hook)
159
+ if success:
160
+ self.registered_hooks.append("kuzu_response_learner")
161
+ self.logger.info(
162
+ "✅ Kuzu-memory learning enabled (responses → memories)"
163
+ )
164
+ else:
165
+ self.logger.warning("Failed to register kuzu-memory learning hook")
166
+ elif not learning_enabled:
167
+ self.logger.debug("Kuzu-memory learning disabled in configuration")
168
+
169
+ # If neither hook is enabled, kuzu-memory is not available
170
+ if not enrichment_hook.enabled and not learning_hook.enabled:
171
+ self.logger.debug(
172
+ "Kuzu-memory not available. Install with: pipx install kuzu-memory"
173
+ )
174
+
175
+ except ImportError as e:
176
+ self.logger.debug(f"Kuzu-memory hooks not available: {e}")
177
+ except Exception as e:
178
+ self.logger.warning(f"Failed to register kuzu-memory hooks: {e}")
179
+
98
180
  def _load_relevant_memories_hook(self, context):
99
181
  """Hook function to load relevant memories before Claude interaction.
100
182
 
@@ -14,7 +14,7 @@ import logging
14
14
  import subprocess
15
15
  import sys
16
16
  import time
17
- from typing import Dict, List, Optional, Set, Tuple
17
+ from typing import Any, Dict, List, Optional, Set, Tuple
18
18
 
19
19
  from packaging.requirements import InvalidRequirement, Requirement
20
20
 
@@ -72,7 +72,7 @@ class AgentDependencyLoader:
72
72
  Returns:
73
73
  Dictionary mapping agent IDs to their file paths
74
74
  """
75
- deployed_agents = {}
75
+ deployed_agents: Dict[str, Path] = {}
76
76
  claude_agents_dir = Path.cwd() / ".claude" / "agents"
77
77
 
78
78
  if not claude_agents_dir.exists():
@@ -409,7 +409,7 @@ class AgentDependencyLoader:
409
409
  Returns:
410
410
  Analysis results including missing and satisfied dependencies
411
411
  """
412
- results = {
412
+ results: Dict[str, Any] = {
413
413
  "agents": {},
414
414
  "summary": {
415
415
  "total_agents": len(self.deployed_agents),
@@ -422,7 +422,7 @@ class AgentDependencyLoader:
422
422
  }
423
423
 
424
424
  for agent_id, deps in self.agent_dependencies.items():
425
- agent_result = {
425
+ agent_result: Dict[str, Dict[str, List[str]]] = {
426
426
  "python": {"satisfied": [], "missing": [], "outdated": []},
427
427
  "system": {"satisfied": [], "missing": []},
428
428
  }
@@ -476,8 +476,8 @@ class AgentDependencyLoader:
476
476
  """
477
477
  import sys
478
478
 
479
- compatible = []
480
- incompatible = []
479
+ compatible: List[str] = []
480
+ incompatible: List[str] = []
481
481
 
482
482
  for dep in dependencies:
483
483
  try:
@@ -593,20 +593,37 @@ class AgentDependencyLoader:
593
593
  try:
594
594
  cmd = [sys.executable, "-m", "pip", "install"]
595
595
 
596
- # Check for PEP 668 managed environment
596
+ # Check environment and add appropriate flags
597
+ import os
597
598
  import sysconfig
598
599
 
599
- stdlib_path = sysconfig.get_path("stdlib")
600
- marker_file = Path(stdlib_path) / "EXTERNALLY-MANAGED"
601
- parent_marker = marker_file.parent.parent / "EXTERNALLY-MANAGED"
600
+ # Check if in virtualenv
601
+ in_virtualenv = (
602
+ (hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix)
603
+ or (hasattr(sys, "real_prefix"))
604
+ or (os.environ.get("VIRTUAL_ENV") is not None)
605
+ )
602
606
 
603
- if marker_file.exists() or parent_marker.exists():
604
- logger.warning(
605
- "PEP 668 managed environment detected. "
606
- "Installing with --break-system-packages --user flags. "
607
- "Consider using a virtual environment instead."
608
- )
609
- cmd.extend(["--break-system-packages", "--user"])
607
+ if in_virtualenv:
608
+ # In virtualenv - no special flags needed
609
+ logger.debug("Installing in virtualenv (no special flags)")
610
+ else:
611
+ # Check for PEP 668 managed environment
612
+ stdlib_path = sysconfig.get_path("stdlib")
613
+ marker_file = Path(stdlib_path) / "EXTERNALLY-MANAGED"
614
+ parent_marker = marker_file.parent.parent / "EXTERNALLY-MANAGED"
615
+
616
+ if marker_file.exists() or parent_marker.exists():
617
+ logger.warning(
618
+ "PEP 668 managed environment detected. "
619
+ "Installing with --break-system-packages flag. "
620
+ "Consider using a virtual environment instead."
621
+ )
622
+ cmd.append("--break-system-packages")
623
+ else:
624
+ # Normal system Python - use --user
625
+ cmd.append("--user")
626
+ logger.debug("Installing with --user flag")
610
627
 
611
628
  cmd.extend(compatible)
612
629
 
@@ -946,13 +963,16 @@ class AgentDependencyLoader:
946
963
 
947
964
  def check_deployed_agent_dependencies(
948
965
  auto_install: bool = False, verbose: bool = False
949
- ) -> None:
966
+ ) -> int:
950
967
  """
951
968
  Check dependencies for currently deployed agents.
952
969
 
953
970
  Args:
954
971
  auto_install: If True, automatically install missing Python dependencies
955
972
  verbose: If True, enable verbose logging
973
+
974
+ Returns:
975
+ Status code: 0 if all dependencies satisfied, 1 if missing dependencies
956
976
  """
957
977
  if verbose:
958
978
  logging.getLogger().setLevel(logging.DEBUG)
@@ -9,6 +9,7 @@ DESIGN DECISION: We implement exponential backoff for retries and provide
9
9
  multiple installation strategies (pip, conda, source) to maximize success rate.
10
10
  """
11
11
 
12
+ import os
12
13
  import re
13
14
  import subprocess
14
15
  import sys
@@ -80,6 +81,7 @@ class RobustPackageInstaller:
80
81
  self.use_cache = use_cache
81
82
  self.attempts: List[InstallAttempt] = []
82
83
  self.success_cache: Dict[str, bool] = {}
84
+ self.in_virtualenv = self._check_virtualenv()
83
85
  self.is_pep668_managed = self._check_pep668_managed()
84
86
  self.pep668_warning_shown = False
85
87
 
@@ -201,6 +203,35 @@ class RobustPackageInstaller:
201
203
  except Exception as e:
202
204
  return False, f"Unexpected error: {e!s}"
203
205
 
206
+ def _check_virtualenv(self) -> bool:
207
+ """
208
+ Check if running inside a virtual environment.
209
+
210
+ WHY: Virtual environments are already isolated and don't need
211
+ --user or --break-system-packages flags. In fact, using --user
212
+ in a virtualenv causes errors.
213
+
214
+ Returns:
215
+ True if in a virtualenv, False otherwise
216
+ """
217
+ # Multiple ways to detect virtualenv
218
+ return (
219
+ (
220
+ # venv creates sys.base_prefix
221
+ hasattr(sys, "base_prefix")
222
+ and sys.base_prefix != sys.prefix
223
+ )
224
+ or (
225
+ # virtualenv creates sys.real_prefix
226
+ hasattr(sys, "real_prefix")
227
+ )
228
+ or (
229
+ # VIRTUAL_ENV environment variable
230
+ os.environ.get("VIRTUAL_ENV")
231
+ is not None
232
+ )
233
+ )
234
+
204
235
  def _check_pep668_managed(self) -> bool:
205
236
  """
206
237
  Check if Python environment is PEP 668 externally managed.
@@ -211,6 +242,11 @@ class RobustPackageInstaller:
211
242
  Returns:
212
243
  True if PEP 668 managed, False otherwise
213
244
  """
245
+ # If in virtualenv, PEP 668 doesn't apply
246
+ if self.in_virtualenv:
247
+ logger.debug("Running in virtualenv, PEP 668 restrictions don't apply")
248
+ return False
249
+
214
250
  # Check for EXTERNALLY-MANAGED marker file
215
251
  stdlib_path = sysconfig.get_path("stdlib")
216
252
  marker_file = Path(stdlib_path) / "EXTERNALLY-MANAGED"
@@ -240,7 +276,7 @@ class RobustPackageInstaller:
240
276
  "Your Python installation is marked as externally managed (PEP 668).\n"
241
277
  "This typically means you're using a system Python managed by Homebrew, apt, etc.\n"
242
278
  "\n"
243
- "Installing packages with --break-system-packages --user flags...\n"
279
+ "Installing packages with --break-system-packages flag...\n"
244
280
  "\n"
245
281
  "RECOMMENDED: Use a virtual environment instead:\n"
246
282
  " python -m venv .venv\n"
@@ -255,8 +291,10 @@ class RobustPackageInstaller:
255
291
  """
256
292
  Build the installation command for a given strategy.
257
293
 
258
- WHY: PEP 668 support added to handle externally managed Python
259
- environments by adding appropriate flags when needed.
294
+ WHY: Proper environment detection ensures we use the right pip flags:
295
+ - Virtualenv: No special flags needed (already isolated)
296
+ - PEP 668 system: Use --break-system-packages only
297
+ - Normal system: Use --user for user-local install
260
298
 
261
299
  Args:
262
300
  package_spec: Package specification
@@ -267,14 +305,19 @@ class RobustPackageInstaller:
267
305
  """
268
306
  base_cmd = [sys.executable, "-m", "pip", "install"]
269
307
 
270
- # Add PEP 668 bypass flags if needed
271
- if self.is_pep668_managed:
308
+ # Determine appropriate flags based on environment
309
+ if self.in_virtualenv:
310
+ # In virtualenv - no special flags needed
311
+ logger.debug("Installing in virtualenv (no special flags)")
312
+ elif self.is_pep668_managed:
313
+ # System Python with PEP 668 - use --break-system-packages only
272
314
  self._show_pep668_warning()
273
- # Add flags to bypass PEP 668 restrictions
274
- base_cmd.extend(["--break-system-packages", "--user"])
275
- logger.debug(
276
- "Added --break-system-packages --user flags for PEP 668 environment"
277
- )
315
+ base_cmd.append("--break-system-packages")
316
+ logger.debug("Added --break-system-packages flag for PEP 668 environment")
317
+ else:
318
+ # Normal system Python - use --user for user-local install
319
+ base_cmd.append("--user")
320
+ logger.debug("Added --user flag for user-local installation")
278
321
 
279
322
  # Add cache control
280
323
  if not self.use_cache:
@@ -612,11 +655,16 @@ class RobustPackageInstaller:
612
655
  try:
613
656
  cmd = [sys.executable, "-m", "pip", "install"]
614
657
 
615
- # Add PEP 668 bypass flags if needed
616
- if self.is_pep668_managed:
658
+ # Add appropriate flags based on environment
659
+ if self.in_virtualenv:
660
+ logger.debug("Batch install in virtualenv (no special flags)")
661
+ elif self.is_pep668_managed:
617
662
  self._show_pep668_warning()
618
- cmd.extend(["--break-system-packages", "--user"])
619
- logger.debug("Added PEP 668 flags for batch installation")
663
+ cmd.append("--break-system-packages")
664
+ logger.debug("Added --break-system-packages for batch installation")
665
+ else:
666
+ cmd.append("--user")
667
+ logger.debug("Added --user flag for batch installation")
620
668
 
621
669
  cmd.extend(packages)
622
670
 
@@ -654,12 +702,18 @@ class RobustPackageInstaller:
654
702
  lines.append("INSTALLATION REPORT")
655
703
  lines.append("=" * 60)
656
704
 
657
- # Add PEP 668 status
658
- if self.is_pep668_managed:
659
- lines.append("")
705
+ # Add environment status
706
+ lines.append("")
707
+ if self.in_virtualenv:
708
+ lines.append("✓ Environment: Virtual Environment (isolated)")
709
+ lines.append(" No special pip flags needed")
710
+ elif self.is_pep668_managed:
660
711
  lines.append("⚠️ PEP 668 Managed Environment: YES")
661
- lines.append(" Installations used --break-system-packages --user flags")
712
+ lines.append(" Installations used --break-system-packages flag")
662
713
  lines.append(" Consider using a virtual environment for better isolation")
714
+ else:
715
+ lines.append("Environment: System Python")
716
+ lines.append(" Installations used --user flag for user-local install")
663
717
 
664
718
  # Summary
665
719
  total_attempts = len(self.attempts)
@@ -672,7 +726,7 @@ class RobustPackageInstaller:
672
726
  lines.append("")
673
727
 
674
728
  # Details by package
675
- packages = {}
729
+ packages: Dict[str, List[InstallAttempt]] = {}
676
730
  for attempt in self.attempts:
677
731
  if attempt.package not in packages:
678
732
  packages[attempt.package] = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.7.5
3
+ Version: 4.7.6
4
4
  Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
5
5
  Author-email: Bob Matsuoka <bob@matsuoka.com>
6
6
  Maintainer: Claude MPM Team
@@ -1,5 +1,5 @@
1
1
  claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
2
- claude_mpm/VERSION,sha256=S1_5LwX4BqRmj3UoBp6ODrFMvkFt5xvcn9wfpuFze9Y,6
2
+ claude_mpm/VERSION,sha256=gVLWgYgcXVc7Af4ejsObqWB0dhZ1OktjUoRmgOIPx1M,6
3
3
  claude_mpm/__init__.py,sha256=UCw6j9e_tZQ3kJtTqmdfNv7MHyw9nD1jkj80WurwM2g,2064
4
4
  claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
5
5
  claude_mpm/constants.py,sha256=cChN3myrAcF3jC-6DvHnBFTEnwlDk-TAsIXPvUZr_yw,5953
@@ -79,7 +79,7 @@ claude_mpm/cli/commands/analyze_code.py,sha256=yWZpG0aL4XlhcthtvbUqnFSlnvvseWO3V
79
79
  claude_mpm/cli/commands/cleanup.py,sha256=TukZoPVfAFSF4ICfKCQUibczDE73EJP8nbEbfuT8GhE,19768
80
80
  claude_mpm/cli/commands/cleanup_orphaned_agents.py,sha256=JR8crvgrz7Sa6d-SI-gKywok5S9rwc_DzDVk_h85sVs,4467
81
81
  claude_mpm/cli/commands/config.py,sha256=Yfi8WO-10_MYz2QipFw-yEzVvHKNQ6iSQXeyW5J85Cg,18559
82
- claude_mpm/cli/commands/configure.py,sha256=xmej2tajCm9J1jG3358vwpGHNWKJlVLQwD6B6bOdfh4,79973
82
+ claude_mpm/cli/commands/configure.py,sha256=22wvCVGeIUFk2mBvvG9Lhd-Jzq8GRLmGg0W9mndxMOY,82489
83
83
  claude_mpm/cli/commands/dashboard.py,sha256=4jPTmTl97DRNNJlYREWeE1iDdkct1uL-vv24MZn9fj4,11403
84
84
  claude_mpm/cli/commands/debug.py,sha256=YCfJ3aYf6hOCvLW_grdfINdEqI4RXVS28VJ7tkZBFS8,47115
85
85
  claude_mpm/cli/commands/doctor.py,sha256=nNKLZG3Qv_UsHNgrmetrWKgS7Pe2Jn5vq5aXyl60wKQ,7310
@@ -368,10 +368,12 @@ claude_mpm/experimental/__init__.py,sha256=R_aclOvWpvSTHWAx9QXyg9OIPVK2dXT5tQJhx
368
368
  claude_mpm/experimental/cli_enhancements.py,sha256=PfAt-SI-crBoE0Dtx1JecpS5_6OT_0apJbo28KS6HUI,11541
369
369
  claude_mpm/generators/__init__.py,sha256=rG8vwF_BjPmeMKvyMXpUA8uJ-7mtW2HTNfalZzgRlNk,153
370
370
  claude_mpm/generators/agent_profile_generator.py,sha256=yTEFdZPUt4lAfXlQuIIxzRwOrWMaJhEJ3Z6Ofm48Rlc,5740
371
- claude_mpm/hooks/__init__.py,sha256=rVD-1bitn454djWzRN3wNNcM7xrXU-YiufVFD2T4H0Y,303
371
+ claude_mpm/hooks/__init__.py,sha256=y-mfGUb4xTCENUIj4RfAhK2oJTIL0qMI2gDatdf52nc,567
372
372
  claude_mpm/hooks/base_hook.py,sha256=wKbT_0g3dhvkA48pTz4GJpZQw8URhaT0LpZnCc7CEas,5026
373
373
  claude_mpm/hooks/instruction_reinforcement.py,sha256=PnjfDSZ_72gbzHnRoug7qtXfpW5d1cxnmittpnPd2ws,11059
374
- claude_mpm/hooks/kuzu_memory_hook.py,sha256=S_yngWlwCey1GXoTQFQ6ce8SFnTWRIQe1Z_9RsLlBbU,11342
374
+ claude_mpm/hooks/kuzu_enrichment_hook.py,sha256=jghoEZX8fA6HZ1kM_5l93cuCyy-AMBjWp-nPW5EgaTk,8729
375
+ claude_mpm/hooks/kuzu_memory_hook.py,sha256=ockbKXXc69Yiryq2uWNCAUpSxtN8eqAec9i5BB1MRaI,12265
376
+ claude_mpm/hooks/kuzu_response_hook.py,sha256=iyVrsOrGpp-VFOjKC5GUnXro088Ftex-vHmfHsmAUv8,6136
375
377
  claude_mpm/hooks/memory_integration_hook.py,sha256=F8Hf35hmbmhxi-qHQJac4zoWIr60ob3PCHa4P_rbxO8,16635
376
378
  claude_mpm/hooks/tool_call_interceptor.py,sha256=tYUBJHjbtaI5-HSWcz0aeUW0CaiQPypuDOTULQ0BCNI,7506
377
379
  claude_mpm/hooks/validation_hooks.py,sha256=i5uOaXifItwJfIdjjLCxAwrkKdkmo2Qa4qMc5-dwsKw,6465
@@ -416,8 +418,8 @@ claude_mpm/services/exceptions.py,sha256=5lVZETr_6-xk0ItH7BTfYUiX5RlckS1e8ah_Ual
416
418
  claude_mpm/services/hook_installer_service.py,sha256=x3H3bFVlmhK4Ue1K279f44lwMEw3W1p3zoETGfjIH_w,19708
417
419
  claude_mpm/services/hook_service.py,sha256=I6JILbackBsdvrDNQ9TeGSB7XNqozNRP26T4E9_ROtU,15693
418
420
  claude_mpm/services/mcp_config_manager.py,sha256=W_zlFXzaQ64BoM4FuOJ47ywUUw0Rcf_u_ivvrpt2ZZY,67241
419
- claude_mpm/services/mcp_service_verifier.py,sha256=r1mNWmDT_-OtRuCbquCeDbT4lDc41jZxkZHNMutMw74,25604
420
- claude_mpm/services/memory_hook_service.py,sha256=pRlTClkRcw30Jhwbha4BC8IMdzKZxF8aWqf52JlntgY,11600
421
+ claude_mpm/services/mcp_service_verifier.py,sha256=xOo1SqbQ1J07TnJ_aNN7yCjna4UBZwnQQu6RrNCgoNc,25604
422
+ claude_mpm/services/memory_hook_service.py,sha256=BZhCYFpWV_7FguDL0MY4k8JOZJFmCbYWNMfDywM9G5Q,15304
421
423
  claude_mpm/services/monitor_build_service.py,sha256=8gWR9CaqgXdG6-OjOFXGpk28GCcJTlHhojkUYnMCebI,12160
422
424
  claude_mpm/services/orphan_detection.py,sha256=cEFE96FhC6EJSa53rUjbgjDj_E1_Ej-0KoYDQST22t0,26339
423
425
  claude_mpm/services/port_manager.py,sha256=83GJ1uSF9pXsECXHtf4M8jBHzOptdh3Lpt7t0GyY2Wc,22838
@@ -567,7 +569,7 @@ claude_mpm/services/diagnostics/checks/filesystem_check.py,sha256=V5HoHDYlSuoK2l
567
569
  claude_mpm/services/diagnostics/checks/installation_check.py,sha256=JdTqZreriAavN8gkHTaaKwclwnvfaRw87Np9mDfUXlk,19548
568
570
  claude_mpm/services/diagnostics/checks/instructions_check.py,sha256=VbgBorl0RpFvxKQ_SC1gibTmGSiXaKSp-vVZt6hbH1g,16290
569
571
  claude_mpm/services/diagnostics/checks/mcp_check.py,sha256=eG8BVWclwRRYh6G1ew1Z_DE1ksRirRxEy3fIe3cu58k,12174
570
- claude_mpm/services/diagnostics/checks/mcp_services_check.py,sha256=o9ex5LYHtqiqXlU0qORwsmH-p0-gr2pM5G1md2UGf10,43776
572
+ claude_mpm/services/diagnostics/checks/mcp_services_check.py,sha256=y6hpDMl3ZoT9WXIgxgbPaebgPqmgfiFruoed7224xos,43776
571
573
  claude_mpm/services/diagnostics/checks/monitor_check.py,sha256=hZk301gvM8XAnfcOdtzE-e9XMBW3hgmDwSVxVSmF9CY,10097
572
574
  claude_mpm/services/diagnostics/checks/startup_log_check.py,sha256=qQQI9Ezk7Sa5NpvJrtDY_0bWhoLPASZJn0hpNR8pEsI,12341
573
575
  claude_mpm/services/event_bus/__init__.py,sha256=ETCo4a6puIeyVWAv55uCDjjhzNyUwbVAHEcAVkVapx8,688
@@ -761,7 +763,7 @@ claude_mpm/tools/code_tree_builder.py,sha256=8EaW94NjEObraiW811dDoQ8xKixrzCr4YoL
761
763
  claude_mpm/tools/code_tree_events.py,sha256=10tf9ZGkPIXzWqYvwq0SIJdIOwKNnxgKWYOmRzVGeLc,13396
762
764
  claude_mpm/tools/socketio_debug.py,sha256=QpG_GoN9VY8OZ0tcg8bqIF2oB_ZbKTjGp0oD0xYmsFo,22226
763
765
  claude_mpm/utils/__init__.py,sha256=rkxNE0tDfKEN2lr2LLBuToNSvkF-yjF38607eUXL1vk,353
764
- claude_mpm/utils/agent_dependency_loader.py,sha256=DeqPJMFEjhmTzsSBNLrhjYpystjuRP_J05CluXKFo90,35685
766
+ claude_mpm/utils/agent_dependency_loader.py,sha256=A_OliEjBIwJn2e2N_7RLsh2zIhC5KdDLQTlMMV2DByc,36678
765
767
  claude_mpm/utils/common.py,sha256=6hcs3Y6lx596Oj4H3CEQId-NIn8XEZJw1Ud3GAKwXKA,14341
766
768
  claude_mpm/utils/config_manager.py,sha256=iWsPM0uo31Ela-XhFe9DIw3RuVxJf94_yFQ1HkibAlY,15876
767
769
  claude_mpm/utils/console.py,sha256=2Agd91xj5NSMgvfUFJIUwVnpghnKjrd8EYH7inF12M4,279
@@ -778,15 +780,15 @@ claude_mpm/utils/import_migration_example.py,sha256=sKlV-apfkHDJzzez6uubHXjx91YF
778
780
  claude_mpm/utils/imports.py,sha256=IGcW0W6V-2jTmkVlO-l0Zodo3LRA3zkaumD9J2qn7dE,6654
779
781
  claude_mpm/utils/log_cleanup.py,sha256=1d8K1V2uOFPGJJKFOk6E_AMj22sifAibOg-VQyGooWo,22301
780
782
  claude_mpm/utils/path_operations.py,sha256=gvd6Ye5rMFxIKnFzWwIL6jYy8wGkjSH3onYAan6zXkA,10869
781
- claude_mpm/utils/robust_installer.py,sha256=R-sg2XiQzVUl2Lk2eWhcDHiP0gbl3wJRLeK3_1Nw614,24898
783
+ claude_mpm/utils/robust_installer.py,sha256=ixif5ExezKr5eOtgoY7W629tcDm-lZtUe5_Igkq7ZYQ,27108
782
784
  claude_mpm/utils/session_logging.py,sha256=_6eoyCvVKhu2OhgRzC5FvMfFnD9et75lzCqARbd_i-k,3059
783
785
  claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalNmfX9KlM,10443
784
786
  claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
785
787
  claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
786
788
  claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
787
- claude_mpm-4.7.5.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
788
- claude_mpm-4.7.5.dist-info/METADATA,sha256=hq04JsKoSrZIIOVtawIlwapyPzZOkFxDl2BhsNcOFR0,17517
789
- claude_mpm-4.7.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
790
- claude_mpm-4.7.5.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
791
- claude_mpm-4.7.5.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
792
- claude_mpm-4.7.5.dist-info/RECORD,,
789
+ claude_mpm-4.7.6.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
790
+ claude_mpm-4.7.6.dist-info/METADATA,sha256=zrxNvScK7d0H2C78k6Wuq_mJ3rQHbjCWTfzoC0JvWNQ,17517
791
+ claude_mpm-4.7.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
792
+ claude_mpm-4.7.6.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
793
+ claude_mpm-4.7.6.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
794
+ claude_mpm-4.7.6.dist-info/RECORD,,