claude-mpm 4.0.9__py3-none-any.whl → 4.0.11__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/BUILD_NUMBER +1 -0
- claude_mpm/VERSION +1 -1
- claude_mpm/core/framework_loader.py +36 -20
- claude_mpm/core/interactive_session.py +2 -2
- claude_mpm/dashboard/static/js/socket-client.js +270 -3
- claude_mpm/hooks/claude_hooks/hook_handler.py +3 -1
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +3 -3
- claude_mpm/services/agents/memory/agent_memory_manager.py +1 -1
- claude_mpm/services/socketio/handlers/connection.py +284 -1
- claude_mpm/services/socketio/server/broadcaster.py +276 -10
- claude_mpm/services/socketio/server/main.py +15 -1
- claude_mpm/services/version_service.py +18 -11
- {claude_mpm-4.0.9.dist-info → claude_mpm-4.0.11.dist-info}/METADATA +1 -1
- {claude_mpm-4.0.9.dist-info → claude_mpm-4.0.11.dist-info}/RECORD +18 -17
- {claude_mpm-4.0.9.dist-info → claude_mpm-4.0.11.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.9.dist-info → claude_mpm-4.0.11.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.9.dist-info → claude_mpm-4.0.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.9.dist-info → claude_mpm-4.0.11.dist-info}/top_level.txt +0 -0
claude_mpm/BUILD_NUMBER
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
287
|
claude_mpm/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.0.
|
|
1
|
+
4.0.10
|
|
@@ -721,31 +721,47 @@ Extract tickets from these patterns:
|
|
|
721
721
|
Path.home() / ".claude" / "agents", # User's system agents
|
|
722
722
|
]
|
|
723
723
|
|
|
724
|
-
|
|
725
|
-
|
|
724
|
+
# Collect agents from all directories with proper precedence
|
|
725
|
+
# Project agents override user agents with the same name
|
|
726
|
+
all_agents = {} # key: agent_id, value: (agent_data, priority)
|
|
727
|
+
|
|
728
|
+
for priority, potential_dir in enumerate(agents_dirs):
|
|
726
729
|
if potential_dir.exists() and any(potential_dir.glob("*.md")):
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
730
|
+
self.logger.debug(f"Found agents directory at: {potential_dir}")
|
|
731
|
+
|
|
732
|
+
# Collect agents from this directory
|
|
733
|
+
for agent_file in potential_dir.glob("*.md"):
|
|
734
|
+
if agent_file.name.startswith("."):
|
|
735
|
+
continue
|
|
736
|
+
|
|
737
|
+
# Parse agent metadata
|
|
738
|
+
agent_data = self._parse_agent_metadata(agent_file)
|
|
739
|
+
if agent_data:
|
|
740
|
+
agent_id = agent_data["id"]
|
|
741
|
+
# Only add if not already present (project has priority 0, user has priority 1)
|
|
742
|
+
# Lower priority number wins (project > user)
|
|
743
|
+
if agent_id not in all_agents or priority < all_agents[agent_id][1]:
|
|
744
|
+
all_agents[agent_id] = (agent_data, priority)
|
|
745
|
+
self.logger.debug(f"Added/Updated agent {agent_id} from {potential_dir} (priority {priority})")
|
|
746
|
+
|
|
747
|
+
if not all_agents:
|
|
748
|
+
self.logger.warning(f"No agents found in any location: {agents_dirs}")
|
|
734
749
|
return self._get_fallback_capabilities()
|
|
735
|
-
|
|
750
|
+
|
|
751
|
+
# Log agent collection summary
|
|
752
|
+
project_agents = [aid for aid, (_, pri) in all_agents.items() if pri == 0]
|
|
753
|
+
user_agents = [aid for aid, (_, pri) in all_agents.items() if pri == 1]
|
|
754
|
+
|
|
755
|
+
if project_agents:
|
|
756
|
+
self.logger.info(f"Loaded {len(project_agents)} project agents: {', '.join(sorted(project_agents))}")
|
|
757
|
+
if user_agents:
|
|
758
|
+
self.logger.info(f"Loaded {len(user_agents)} user agents: {', '.join(sorted(user_agents))}")
|
|
759
|
+
|
|
736
760
|
# Build capabilities section
|
|
737
761
|
section = "\n\n## Available Agent Capabilities\n\n"
|
|
738
762
|
|
|
739
|
-
#
|
|
740
|
-
deployed_agents = []
|
|
741
|
-
for agent_file in agents_dir.glob("*.md"):
|
|
742
|
-
if agent_file.name.startswith("."):
|
|
743
|
-
continue
|
|
744
|
-
|
|
745
|
-
# Parse agent metadata
|
|
746
|
-
agent_data = self._parse_agent_metadata(agent_file)
|
|
747
|
-
if agent_data:
|
|
748
|
-
deployed_agents.append(agent_data)
|
|
763
|
+
# Extract just the agent data (drop priority info) and sort
|
|
764
|
+
deployed_agents = [agent_data for agent_data, _ in all_agents.values()]
|
|
749
765
|
|
|
750
766
|
if not deployed_agents:
|
|
751
767
|
return self._get_fallback_capabilities()
|
|
@@ -469,9 +469,9 @@ class InteractiveSession:
|
|
|
469
469
|
def _show_available_agents(self) -> bool:
|
|
470
470
|
"""Show available agents in the system."""
|
|
471
471
|
try:
|
|
472
|
-
from claude_mpm.cli import
|
|
472
|
+
from claude_mpm.cli.utils import get_agent_versions_display
|
|
473
473
|
|
|
474
|
-
agent_versions =
|
|
474
|
+
agent_versions = get_agent_versions_display()
|
|
475
475
|
|
|
476
476
|
if agent_versions:
|
|
477
477
|
print(agent_versions)
|
|
@@ -20,14 +20,33 @@ class SocketClient {
|
|
|
20
20
|
// Connection state
|
|
21
21
|
this.isConnected = false;
|
|
22
22
|
this.isConnecting = false;
|
|
23
|
+
this.lastConnectTime = null;
|
|
24
|
+
this.disconnectTime = null;
|
|
23
25
|
|
|
24
26
|
// Event processing
|
|
25
27
|
this.events = [];
|
|
26
28
|
this.sessions = new Map();
|
|
27
29
|
this.currentSessionId = null;
|
|
28
30
|
|
|
31
|
+
// Event queue for disconnection periods
|
|
32
|
+
this.eventQueue = [];
|
|
33
|
+
this.maxQueueSize = 100;
|
|
34
|
+
|
|
35
|
+
// Retry configuration
|
|
36
|
+
this.retryAttempts = 0;
|
|
37
|
+
this.maxRetryAttempts = 3;
|
|
38
|
+
this.retryDelays = [1000, 2000, 4000]; // Exponential backoff
|
|
39
|
+
this.pendingEmissions = new Map(); // Track pending emissions for retry
|
|
40
|
+
|
|
41
|
+
// Health monitoring
|
|
42
|
+
this.lastPingTime = null;
|
|
43
|
+
this.lastPongTime = null;
|
|
44
|
+
this.pingTimeout = 40000; // 40 seconds (server sends every 30s)
|
|
45
|
+
this.healthCheckInterval = null;
|
|
46
|
+
|
|
29
47
|
// Start periodic status check as fallback mechanism
|
|
30
48
|
this.startStatusCheckFallback();
|
|
49
|
+
this.startHealthMonitoring();
|
|
31
50
|
}
|
|
32
51
|
|
|
33
52
|
/**
|
|
@@ -88,8 +107,21 @@ class SocketClient {
|
|
|
88
107
|
setupSocketHandlers() {
|
|
89
108
|
this.socket.on('connect', () => {
|
|
90
109
|
console.log('Connected to Socket.IO server');
|
|
110
|
+
const previouslyConnected = this.isConnected;
|
|
91
111
|
this.isConnected = true;
|
|
92
112
|
this.isConnecting = false;
|
|
113
|
+
this.lastConnectTime = Date.now();
|
|
114
|
+
this.retryAttempts = 0; // Reset retry counter on successful connect
|
|
115
|
+
|
|
116
|
+
// Calculate downtime if this is a reconnection
|
|
117
|
+
if (this.disconnectTime && previouslyConnected === false) {
|
|
118
|
+
const downtime = (Date.now() - this.disconnectTime) / 1000;
|
|
119
|
+
console.log(`Reconnected after ${downtime.toFixed(1)}s downtime`);
|
|
120
|
+
|
|
121
|
+
// Flush queued events after reconnection
|
|
122
|
+
this.flushEventQueue();
|
|
123
|
+
}
|
|
124
|
+
|
|
93
125
|
this.notifyConnectionStatus('Connected', 'connected');
|
|
94
126
|
|
|
95
127
|
// Emit connect callback
|
|
@@ -106,12 +138,25 @@ class SocketClient {
|
|
|
106
138
|
console.log('Disconnected from server:', reason);
|
|
107
139
|
this.isConnected = false;
|
|
108
140
|
this.isConnecting = false;
|
|
141
|
+
this.disconnectTime = Date.now();
|
|
142
|
+
|
|
143
|
+
// Calculate uptime
|
|
144
|
+
if (this.lastConnectTime) {
|
|
145
|
+
const uptime = (Date.now() - this.lastConnectTime) / 1000;
|
|
146
|
+
console.log(`Connection uptime was ${uptime.toFixed(1)}s`);
|
|
147
|
+
}
|
|
148
|
+
|
|
109
149
|
this.notifyConnectionStatus(`Disconnected: ${reason}`, 'disconnected');
|
|
110
150
|
|
|
111
151
|
// Emit disconnect callback
|
|
112
152
|
this.connectionCallbacks.disconnect.forEach(callback =>
|
|
113
153
|
callback(reason)
|
|
114
154
|
);
|
|
155
|
+
|
|
156
|
+
// Start auto-reconnect if it was an unexpected disconnect
|
|
157
|
+
if (reason === 'transport close' || reason === 'ping timeout') {
|
|
158
|
+
this.scheduleReconnect();
|
|
159
|
+
}
|
|
115
160
|
});
|
|
116
161
|
|
|
117
162
|
this.socket.on('connect_error', (error) => {
|
|
@@ -124,13 +169,20 @@ class SocketClient {
|
|
|
124
169
|
this.addEvent({
|
|
125
170
|
type: 'connection.error',
|
|
126
171
|
timestamp: new Date().toISOString(),
|
|
127
|
-
data: {
|
|
172
|
+
data: {
|
|
173
|
+
error: errorMsg,
|
|
174
|
+
url: this.socket.io.uri,
|
|
175
|
+
retry_attempt: this.retryAttempts
|
|
176
|
+
}
|
|
128
177
|
});
|
|
129
178
|
|
|
130
179
|
// Emit error callback
|
|
131
180
|
this.connectionCallbacks.error.forEach(callback =>
|
|
132
181
|
callback(errorMsg)
|
|
133
182
|
);
|
|
183
|
+
|
|
184
|
+
// Schedule reconnect with backoff
|
|
185
|
+
this.scheduleReconnect();
|
|
134
186
|
});
|
|
135
187
|
|
|
136
188
|
// Primary event handler - this is what the server actually emits
|
|
@@ -143,6 +195,18 @@ class SocketClient {
|
|
|
143
195
|
this.addEvent(transformedEvent);
|
|
144
196
|
});
|
|
145
197
|
|
|
198
|
+
// Add ping/pong handlers for health monitoring
|
|
199
|
+
this.socket.on('ping', (data) => {
|
|
200
|
+
// console.log('Received ping from server');
|
|
201
|
+
this.lastPingTime = Date.now();
|
|
202
|
+
|
|
203
|
+
// Send pong response immediately
|
|
204
|
+
this.socket.emit('pong', {
|
|
205
|
+
timestamp: data.timestamp,
|
|
206
|
+
client_time: Date.now()
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
146
210
|
// Session and event handlers (legacy/fallback)
|
|
147
211
|
this.socket.on('session.started', (data) => {
|
|
148
212
|
this.addEvent({ type: 'session', subtype: 'started', timestamp: new Date().toISOString(), data });
|
|
@@ -235,13 +299,144 @@ class SocketClient {
|
|
|
235
299
|
this.isConnecting = false;
|
|
236
300
|
}
|
|
237
301
|
|
|
302
|
+
/**
|
|
303
|
+
* Emit an event with retry support
|
|
304
|
+
* @param {string} event - Event name
|
|
305
|
+
* @param {any} data - Event data
|
|
306
|
+
* @param {Object} options - Options for retry behavior
|
|
307
|
+
*/
|
|
308
|
+
emitWithRetry(event, data = null, options = {}) {
|
|
309
|
+
const {
|
|
310
|
+
maxRetries = 3,
|
|
311
|
+
retryDelays = [1000, 2000, 4000],
|
|
312
|
+
onSuccess = null,
|
|
313
|
+
onFailure = null
|
|
314
|
+
} = options;
|
|
315
|
+
|
|
316
|
+
const emissionId = `${event}_${Date.now()}_${Math.random()}`;
|
|
317
|
+
|
|
318
|
+
const attemptEmission = (attemptNum = 0) => {
|
|
319
|
+
if (!this.socket || !this.socket.connected) {
|
|
320
|
+
// Queue for later if disconnected
|
|
321
|
+
if (attemptNum === 0) {
|
|
322
|
+
this.queueEvent(event, data);
|
|
323
|
+
console.log(`Queued ${event} for later emission (disconnected)`);
|
|
324
|
+
if (onFailure) onFailure('disconnected');
|
|
325
|
+
}
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
// Attempt emission
|
|
331
|
+
this.socket.emit(event, data);
|
|
332
|
+
console.log(`Emitted ${event} successfully`);
|
|
333
|
+
|
|
334
|
+
// Remove from pending
|
|
335
|
+
this.pendingEmissions.delete(emissionId);
|
|
336
|
+
|
|
337
|
+
if (onSuccess) onSuccess();
|
|
338
|
+
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error(`Failed to emit ${event} (attempt ${attemptNum + 1}):`, error);
|
|
341
|
+
|
|
342
|
+
if (attemptNum < maxRetries - 1) {
|
|
343
|
+
const delay = retryDelays[attemptNum] || retryDelays[retryDelays.length - 1];
|
|
344
|
+
console.log(`Retrying ${event} in ${delay}ms...`);
|
|
345
|
+
|
|
346
|
+
// Store pending emission
|
|
347
|
+
this.pendingEmissions.set(emissionId, {
|
|
348
|
+
event,
|
|
349
|
+
data,
|
|
350
|
+
attemptNum: attemptNum + 1,
|
|
351
|
+
scheduledTime: Date.now() + delay
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
setTimeout(() => attemptEmission(attemptNum + 1), delay);
|
|
355
|
+
} else {
|
|
356
|
+
console.error(`Failed to emit ${event} after ${maxRetries} attempts`);
|
|
357
|
+
this.pendingEmissions.delete(emissionId);
|
|
358
|
+
if (onFailure) onFailure('max_retries_exceeded');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
attemptEmission();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Queue an event for later emission
|
|
368
|
+
* @param {string} event - Event name
|
|
369
|
+
* @param {any} data - Event data
|
|
370
|
+
*/
|
|
371
|
+
queueEvent(event, data) {
|
|
372
|
+
if (this.eventQueue.length >= this.maxQueueSize) {
|
|
373
|
+
// Remove oldest event if queue is full
|
|
374
|
+
const removed = this.eventQueue.shift();
|
|
375
|
+
console.warn(`Event queue full, dropped oldest event: ${removed.event}`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
this.eventQueue.push({
|
|
379
|
+
event,
|
|
380
|
+
data,
|
|
381
|
+
timestamp: Date.now()
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Flush queued events after reconnection
|
|
387
|
+
*/
|
|
388
|
+
flushEventQueue() {
|
|
389
|
+
if (this.eventQueue.length === 0) return;
|
|
390
|
+
|
|
391
|
+
console.log(`Flushing ${this.eventQueue.length} queued events...`);
|
|
392
|
+
const events = [...this.eventQueue];
|
|
393
|
+
this.eventQueue = [];
|
|
394
|
+
|
|
395
|
+
// Emit each queued event with a small delay between them
|
|
396
|
+
events.forEach((item, index) => {
|
|
397
|
+
setTimeout(() => {
|
|
398
|
+
if (this.socket && this.socket.connected) {
|
|
399
|
+
this.socket.emit(item.event, item.data);
|
|
400
|
+
console.log(`Flushed queued event: ${item.event}`);
|
|
401
|
+
}
|
|
402
|
+
}, index * 100); // 100ms between each event
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Schedule a reconnection attempt with exponential backoff
|
|
408
|
+
*/
|
|
409
|
+
scheduleReconnect() {
|
|
410
|
+
if (this.retryAttempts >= this.maxRetryAttempts) {
|
|
411
|
+
console.log('Max reconnection attempts reached, stopping auto-reconnect');
|
|
412
|
+
this.notifyConnectionStatus('Reconnection failed', 'disconnected');
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const delay = this.retryDelays[this.retryAttempts] || this.retryDelays[this.retryDelays.length - 1];
|
|
417
|
+
this.retryAttempts++;
|
|
418
|
+
|
|
419
|
+
console.log(`Scheduling reconnect attempt ${this.retryAttempts}/${this.maxRetryAttempts} in ${delay}ms...`);
|
|
420
|
+
this.notifyConnectionStatus(`Reconnecting in ${delay/1000}s...`, 'connecting');
|
|
421
|
+
|
|
422
|
+
setTimeout(() => {
|
|
423
|
+
if (!this.isConnected && this.port) {
|
|
424
|
+
console.log(`Attempting reconnection ${this.retryAttempts}/${this.maxRetryAttempts}...`);
|
|
425
|
+
this.connect(this.port);
|
|
426
|
+
}
|
|
427
|
+
}, delay);
|
|
428
|
+
}
|
|
429
|
+
|
|
238
430
|
/**
|
|
239
431
|
* Request server status
|
|
240
432
|
*/
|
|
241
433
|
requestStatus() {
|
|
242
434
|
if (this.socket && this.socket.connected) {
|
|
243
435
|
console.log('Requesting server status...');
|
|
244
|
-
this.
|
|
436
|
+
this.emitWithRetry('request.status', null, {
|
|
437
|
+
maxRetries: 2,
|
|
438
|
+
retryDelays: [500, 1000]
|
|
439
|
+
});
|
|
245
440
|
}
|
|
246
441
|
}
|
|
247
442
|
|
|
@@ -258,7 +453,13 @@ class SocketClient {
|
|
|
258
453
|
event_types: options.event_types || []
|
|
259
454
|
};
|
|
260
455
|
console.log('Requesting event history...', params);
|
|
261
|
-
this.
|
|
456
|
+
this.emitWithRetry('get_history', params, {
|
|
457
|
+
maxRetries: 3,
|
|
458
|
+
retryDelays: [1000, 2000, 3000],
|
|
459
|
+
onFailure: (reason) => {
|
|
460
|
+
console.error(`Failed to request history: ${reason}`);
|
|
461
|
+
}
|
|
462
|
+
});
|
|
262
463
|
} else {
|
|
263
464
|
console.warn('Cannot request history: not connected to server');
|
|
264
465
|
}
|
|
@@ -554,6 +755,43 @@ class SocketClient {
|
|
|
554
755
|
};
|
|
555
756
|
}
|
|
556
757
|
|
|
758
|
+
/**
|
|
759
|
+
* Start health monitoring
|
|
760
|
+
* Detects stale connections and triggers reconnection
|
|
761
|
+
*/
|
|
762
|
+
startHealthMonitoring() {
|
|
763
|
+
this.healthCheckInterval = setInterval(() => {
|
|
764
|
+
if (this.isConnected && this.lastPingTime) {
|
|
765
|
+
const timeSinceLastPing = Date.now() - this.lastPingTime;
|
|
766
|
+
|
|
767
|
+
if (timeSinceLastPing > this.pingTimeout) {
|
|
768
|
+
console.warn(`No ping from server for ${timeSinceLastPing/1000}s, connection may be stale`);
|
|
769
|
+
|
|
770
|
+
// Force reconnection
|
|
771
|
+
if (this.socket) {
|
|
772
|
+
console.log('Forcing reconnection due to stale connection...');
|
|
773
|
+
this.socket.disconnect();
|
|
774
|
+
setTimeout(() => {
|
|
775
|
+
if (this.port) {
|
|
776
|
+
this.connect(this.port);
|
|
777
|
+
}
|
|
778
|
+
}, 1000);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}, 10000); // Check every 10 seconds
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Stop health monitoring
|
|
787
|
+
*/
|
|
788
|
+
stopHealthMonitoring() {
|
|
789
|
+
if (this.healthCheckInterval) {
|
|
790
|
+
clearInterval(this.healthCheckInterval);
|
|
791
|
+
this.healthCheckInterval = null;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
557
795
|
/**
|
|
558
796
|
* Start periodic status check as fallback mechanism
|
|
559
797
|
* This ensures the UI stays in sync with actual socket state
|
|
@@ -615,6 +853,35 @@ class SocketClient {
|
|
|
615
853
|
}
|
|
616
854
|
}
|
|
617
855
|
|
|
856
|
+
/**
|
|
857
|
+
* Clean up resources
|
|
858
|
+
*/
|
|
859
|
+
destroy() {
|
|
860
|
+
this.stopHealthMonitoring();
|
|
861
|
+
if (this.socket) {
|
|
862
|
+
this.socket.disconnect();
|
|
863
|
+
this.socket = null;
|
|
864
|
+
}
|
|
865
|
+
this.eventQueue = [];
|
|
866
|
+
this.pendingEmissions.clear();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Get connection metrics
|
|
871
|
+
* @returns {Object} Connection metrics
|
|
872
|
+
*/
|
|
873
|
+
getConnectionMetrics() {
|
|
874
|
+
return {
|
|
875
|
+
isConnected: this.isConnected,
|
|
876
|
+
uptime: this.lastConnectTime ? (Date.now() - this.lastConnectTime) / 1000 : 0,
|
|
877
|
+
lastPing: this.lastPingTime ? (Date.now() - this.lastPingTime) / 1000 : null,
|
|
878
|
+
queuedEvents: this.eventQueue.length,
|
|
879
|
+
pendingEmissions: this.pendingEmissions.size,
|
|
880
|
+
retryAttempts: this.retryAttempts
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
618
885
|
// ES6 Module export
|
|
619
886
|
export { SocketClient };
|
|
620
887
|
export default SocketClient;
|
|
@@ -903,7 +903,9 @@ def main():
|
|
|
903
903
|
)
|
|
904
904
|
# Always output continue action to not block Claude
|
|
905
905
|
print(json.dumps({"action": "continue"}))
|
|
906
|
-
|
|
906
|
+
# Only exit if this is a signal handler call, not atexit
|
|
907
|
+
if signum is not None:
|
|
908
|
+
sys.exit(0)
|
|
907
909
|
|
|
908
910
|
# Register cleanup handlers
|
|
909
911
|
signal.signal(signal.SIGTERM, cleanup_handler)
|
|
@@ -45,12 +45,12 @@ export CLAUDE_MPM_HOOK_DEBUG="true"
|
|
|
45
45
|
|
|
46
46
|
# Debug log (optional)
|
|
47
47
|
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] PYTHONPATH: $PYTHONPATH" >> /tmp/hook-wrapper.log
|
|
48
|
-
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Running: $PYTHON_CMD
|
|
48
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Running: $PYTHON_CMD -m claude_mpm.hooks.claude_hooks.hook_handler" >> /tmp/hook-wrapper.log
|
|
49
49
|
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] SOCKETIO_PORT: $CLAUDE_MPM_SOCKETIO_PORT" >> /tmp/hook-wrapper.log
|
|
50
50
|
|
|
51
|
-
# Run the Python hook handler with error handling
|
|
51
|
+
# Run the Python hook handler as a module with error handling
|
|
52
52
|
# Use exec to replace the shell process, but wrap in error handling
|
|
53
|
-
if ! "$PYTHON_CMD"
|
|
53
|
+
if ! "$PYTHON_CMD" -m claude_mpm.hooks.claude_hooks.hook_handler "$@" 2>/tmp/hook-error.log; then
|
|
54
54
|
# If the Python handler fails, always return continue to not block Claude
|
|
55
55
|
echo '{"action": "continue"}'
|
|
56
56
|
# Log the error for debugging
|
|
@@ -82,7 +82,7 @@ class AgentMemoryManager(MemoryServiceInterface):
|
|
|
82
82
|
self._logger_name = None
|
|
83
83
|
|
|
84
84
|
self.config = config or Config()
|
|
85
|
-
self.project_root = get_path_manager().
|
|
85
|
+
self.project_root = get_path_manager().project_root
|
|
86
86
|
# Use current working directory by default, not project root
|
|
87
87
|
self.working_directory = working_directory or Path(os.getcwd())
|
|
88
88
|
self.memories_dir = self.working_directory / ".claude-mpm" / "memories"
|