claude-mpm 3.2.1__py3-none-any.whl → 3.3.2__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/agents/INSTRUCTIONS.md +71 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +1 -1
- claude_mpm/agents/templates/engineer.json +1 -1
- claude_mpm/agents/templates/ops.json +1 -1
- claude_mpm/agents/templates/pm.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/research.json +1 -1
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/test_integration.json +112 -0
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/cli/commands/memory.py +575 -25
- claude_mpm/cli/commands/run.py +115 -14
- claude_mpm/cli/parser.py +76 -0
- claude_mpm/constants.py +5 -0
- claude_mpm/core/claude_runner.py +13 -11
- claude_mpm/core/session_manager.py +46 -0
- claude_mpm/core/simple_runner.py +13 -11
- claude_mpm/hooks/claude_hooks/hook_handler.py +2 -26
- claude_mpm/scripts/launch_socketio_dashboard.py +261 -0
- claude_mpm/services/agent_memory_manager.py +264 -23
- claude_mpm/services/memory_builder.py +491 -0
- claude_mpm/services/memory_optimizer.py +619 -0
- claude_mpm/services/memory_router.py +445 -0
- claude_mpm/services/socketio_server.py +389 -1
- claude_mpm-3.3.2.dist-info/METADATA +159 -0
- {claude_mpm-3.2.1.dist-info → claude_mpm-3.3.2.dist-info}/RECORD +31 -29
- claude_mpm/agents/templates/test-integration-agent.md +0 -34
- claude_mpm/core/websocket_handler.py +0 -233
- claude_mpm/services/websocket_server.py +0 -376
- claude_mpm-3.2.1.dist-info/METADATA +0 -432
- {claude_mpm-3.2.1.dist-info → claude_mpm-3.3.2.dist-info}/WHEEL +0 -0
- {claude_mpm-3.2.1.dist-info → claude_mpm-3.3.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.2.1.dist-info → claude_mpm-3.3.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.2.1.dist-info → claude_mpm-3.3.2.dist-info}/top_level.txt +0 -0
|
@@ -254,9 +254,31 @@ class SocketIOServer:
|
|
|
254
254
|
self.app = web.Application()
|
|
255
255
|
self.sio.attach(self.app)
|
|
256
256
|
|
|
257
|
+
# Add CORS middleware
|
|
258
|
+
import aiohttp_cors
|
|
259
|
+
cors = aiohttp_cors.setup(self.app, defaults={
|
|
260
|
+
"*": aiohttp_cors.ResourceOptions(
|
|
261
|
+
allow_credentials=True,
|
|
262
|
+
expose_headers="*",
|
|
263
|
+
allow_headers="*",
|
|
264
|
+
allow_methods="*"
|
|
265
|
+
)
|
|
266
|
+
})
|
|
267
|
+
|
|
257
268
|
# Add HTTP routes
|
|
258
269
|
self.app.router.add_get('/health', self._handle_health)
|
|
259
270
|
self.app.router.add_get('/status', self._handle_health)
|
|
271
|
+
self.app.router.add_get('/api/git-diff', self._handle_git_diff)
|
|
272
|
+
self.app.router.add_options('/api/git-diff', self._handle_cors_preflight)
|
|
273
|
+
|
|
274
|
+
# Add dashboard routes
|
|
275
|
+
self.app.router.add_get('/', self._handle_dashboard)
|
|
276
|
+
self.app.router.add_get('/dashboard', self._handle_dashboard)
|
|
277
|
+
|
|
278
|
+
# Add static file serving for web assets
|
|
279
|
+
static_path = get_project_root() / 'src' / 'claude_mpm' / 'web' / 'static'
|
|
280
|
+
if static_path.exists():
|
|
281
|
+
self.app.router.add_static('/static/', path=str(static_path), name='static')
|
|
260
282
|
|
|
261
283
|
# Register event handlers
|
|
262
284
|
self._register_events()
|
|
@@ -300,7 +322,335 @@ class SocketIOServer:
|
|
|
300
322
|
"port": self.port,
|
|
301
323
|
"host": self.host,
|
|
302
324
|
"clients_connected": len(self.clients)
|
|
325
|
+
}, headers={
|
|
326
|
+
'Access-Control-Allow-Origin': '*',
|
|
327
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
328
|
+
'Access-Control-Allow-Headers': 'Content-Type, Accept'
|
|
303
329
|
})
|
|
330
|
+
|
|
331
|
+
async def _handle_dashboard(self, request):
|
|
332
|
+
"""Serve the dashboard HTML file."""
|
|
333
|
+
dashboard_path = get_project_root() / 'src' / 'claude_mpm' / 'web' / 'templates' / 'index.html'
|
|
334
|
+
self.logger.info(f"Dashboard requested, looking for: {dashboard_path}")
|
|
335
|
+
self.logger.info(f"Path exists: {dashboard_path.exists()}")
|
|
336
|
+
if dashboard_path.exists():
|
|
337
|
+
return web.FileResponse(str(dashboard_path))
|
|
338
|
+
else:
|
|
339
|
+
return web.Response(text=f"Dashboard not found at: {dashboard_path}", status=404)
|
|
340
|
+
|
|
341
|
+
async def _handle_cors_preflight(self, request):
|
|
342
|
+
"""Handle CORS preflight requests."""
|
|
343
|
+
return web.Response(
|
|
344
|
+
status=200,
|
|
345
|
+
headers={
|
|
346
|
+
'Access-Control-Allow-Origin': '*',
|
|
347
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
348
|
+
'Access-Control-Allow-Headers': 'Content-Type, Accept, Authorization',
|
|
349
|
+
'Access-Control-Max-Age': '86400'
|
|
350
|
+
}
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
async def _handle_git_diff(self, request):
|
|
354
|
+
"""Handle git diff requests for file operations.
|
|
355
|
+
|
|
356
|
+
Expected query parameters:
|
|
357
|
+
- file: The file path to generate diff for
|
|
358
|
+
- timestamp: ISO timestamp of the operation (optional)
|
|
359
|
+
- working_dir: Working directory for git operations (optional)
|
|
360
|
+
"""
|
|
361
|
+
try:
|
|
362
|
+
# Extract query parameters
|
|
363
|
+
file_path = request.query.get('file')
|
|
364
|
+
timestamp = request.query.get('timestamp')
|
|
365
|
+
working_dir = request.query.get('working_dir', os.getcwd())
|
|
366
|
+
|
|
367
|
+
self.logger.info(f"Git diff API request: file={file_path}, timestamp={timestamp}, working_dir={working_dir}")
|
|
368
|
+
|
|
369
|
+
if not file_path:
|
|
370
|
+
self.logger.warning("Git diff request missing file parameter")
|
|
371
|
+
return web.json_response({
|
|
372
|
+
"success": False,
|
|
373
|
+
"error": "Missing required parameter: file"
|
|
374
|
+
}, status=400, headers={
|
|
375
|
+
'Access-Control-Allow-Origin': '*',
|
|
376
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
377
|
+
'Access-Control-Allow-Headers': 'Content-Type, Accept'
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
self.logger.debug(f"Git diff requested for file: {file_path}, timestamp: {timestamp}")
|
|
381
|
+
|
|
382
|
+
# Generate git diff using the _generate_git_diff helper
|
|
383
|
+
diff_result = await self._generate_git_diff(file_path, timestamp, working_dir)
|
|
384
|
+
|
|
385
|
+
self.logger.info(f"Git diff result: success={diff_result.get('success', False)}, method={diff_result.get('method', 'unknown')}")
|
|
386
|
+
|
|
387
|
+
return web.json_response(diff_result, headers={
|
|
388
|
+
'Access-Control-Allow-Origin': '*',
|
|
389
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
390
|
+
'Access-Control-Allow-Headers': 'Content-Type, Accept'
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
except Exception as e:
|
|
394
|
+
self.logger.error(f"Error generating git diff: {e}")
|
|
395
|
+
import traceback
|
|
396
|
+
self.logger.error(f"Git diff error traceback: {traceback.format_exc()}")
|
|
397
|
+
return web.json_response({
|
|
398
|
+
"success": False,
|
|
399
|
+
"error": f"Failed to generate git diff: {str(e)}"
|
|
400
|
+
}, status=500, headers={
|
|
401
|
+
'Access-Control-Allow-Origin': '*',
|
|
402
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
403
|
+
'Access-Control-Allow-Headers': 'Content-Type, Accept'
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
async def _generate_git_diff(self, file_path: str, timestamp: Optional[str] = None, working_dir: str = None):
|
|
407
|
+
"""Generate git diff for a specific file operation.
|
|
408
|
+
|
|
409
|
+
WHY: This method generates a git diff showing the changes made to a file
|
|
410
|
+
during a specific write operation. It uses git log and show commands to
|
|
411
|
+
find the most relevant commit around the specified timestamp.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
file_path: Path to the file relative to the git repository
|
|
415
|
+
timestamp: ISO timestamp of the file operation (optional)
|
|
416
|
+
working_dir: Working directory containing the git repository
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
dict: Contains diff content, metadata, and status information
|
|
420
|
+
"""
|
|
421
|
+
try:
|
|
422
|
+
# If file_path is absolute, determine its git repository
|
|
423
|
+
if os.path.isabs(file_path):
|
|
424
|
+
# Find the directory containing the file
|
|
425
|
+
file_dir = os.path.dirname(file_path)
|
|
426
|
+
if os.path.exists(file_dir):
|
|
427
|
+
# Try to find the git root from the file's directory
|
|
428
|
+
current_dir = file_dir
|
|
429
|
+
while current_dir != "/" and current_dir:
|
|
430
|
+
if os.path.exists(os.path.join(current_dir, ".git")):
|
|
431
|
+
working_dir = current_dir
|
|
432
|
+
self.logger.info(f"Found git repository at: {working_dir}")
|
|
433
|
+
break
|
|
434
|
+
current_dir = os.path.dirname(current_dir)
|
|
435
|
+
else:
|
|
436
|
+
# If no git repo found, use the file's directory
|
|
437
|
+
working_dir = file_dir
|
|
438
|
+
self.logger.info(f"No git repo found, using file's directory: {working_dir}")
|
|
439
|
+
|
|
440
|
+
if working_dir is None:
|
|
441
|
+
working_dir = os.getcwd()
|
|
442
|
+
|
|
443
|
+
# For read-only git operations, we can work from any directory
|
|
444
|
+
# by passing the -C flag to git commands instead of changing directories
|
|
445
|
+
original_cwd = os.getcwd()
|
|
446
|
+
try:
|
|
447
|
+
# We'll use git -C <working_dir> for all commands instead of chdir
|
|
448
|
+
|
|
449
|
+
# Check if this is a git repository
|
|
450
|
+
git_check = await asyncio.create_subprocess_exec(
|
|
451
|
+
'git', '-C', working_dir, 'rev-parse', '--git-dir',
|
|
452
|
+
stdout=asyncio.subprocess.PIPE,
|
|
453
|
+
stderr=asyncio.subprocess.PIPE
|
|
454
|
+
)
|
|
455
|
+
await git_check.communicate()
|
|
456
|
+
|
|
457
|
+
if git_check.returncode != 0:
|
|
458
|
+
return {
|
|
459
|
+
"success": False,
|
|
460
|
+
"error": "Not a git repository",
|
|
461
|
+
"file_path": file_path,
|
|
462
|
+
"working_dir": working_dir
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
# Get the absolute path of the file relative to git root
|
|
466
|
+
git_root_proc = await asyncio.create_subprocess_exec(
|
|
467
|
+
'git', '-C', working_dir, 'rev-parse', '--show-toplevel',
|
|
468
|
+
stdout=asyncio.subprocess.PIPE,
|
|
469
|
+
stderr=asyncio.subprocess.PIPE
|
|
470
|
+
)
|
|
471
|
+
git_root_output, _ = await git_root_proc.communicate()
|
|
472
|
+
|
|
473
|
+
if git_root_proc.returncode != 0:
|
|
474
|
+
return {"success": False, "error": "Failed to determine git root directory"}
|
|
475
|
+
|
|
476
|
+
git_root = git_root_output.decode().strip()
|
|
477
|
+
|
|
478
|
+
# Make file_path relative to git root if it's absolute
|
|
479
|
+
if os.path.isabs(file_path):
|
|
480
|
+
try:
|
|
481
|
+
file_path = os.path.relpath(file_path, git_root)
|
|
482
|
+
except ValueError:
|
|
483
|
+
# File is not under git root
|
|
484
|
+
pass
|
|
485
|
+
|
|
486
|
+
# If timestamp is provided, try to find commits around that time
|
|
487
|
+
if timestamp:
|
|
488
|
+
# Convert timestamp to git format
|
|
489
|
+
try:
|
|
490
|
+
from datetime import datetime
|
|
491
|
+
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
|
492
|
+
git_since = dt.strftime('%Y-%m-%d %H:%M:%S')
|
|
493
|
+
|
|
494
|
+
# Find commits that modified this file around the timestamp
|
|
495
|
+
log_proc = await asyncio.create_subprocess_exec(
|
|
496
|
+
'git', '-C', working_dir, 'log', '--oneline', '--since', git_since,
|
|
497
|
+
'--until', f'{git_since} +1 hour', '--', file_path,
|
|
498
|
+
stdout=asyncio.subprocess.PIPE,
|
|
499
|
+
stderr=asyncio.subprocess.PIPE
|
|
500
|
+
)
|
|
501
|
+
log_output, _ = await log_proc.communicate()
|
|
502
|
+
|
|
503
|
+
if log_proc.returncode == 0 and log_output:
|
|
504
|
+
# Get the most recent commit hash
|
|
505
|
+
commits = log_output.decode().strip().split('\n')
|
|
506
|
+
if commits and commits[0]:
|
|
507
|
+
commit_hash = commits[0].split()[0]
|
|
508
|
+
|
|
509
|
+
# Get the diff for this specific commit
|
|
510
|
+
diff_proc = await asyncio.create_subprocess_exec(
|
|
511
|
+
'git', '-C', working_dir, 'show', '--format=fuller', commit_hash, '--', file_path,
|
|
512
|
+
stdout=asyncio.subprocess.PIPE,
|
|
513
|
+
stderr=asyncio.subprocess.PIPE
|
|
514
|
+
)
|
|
515
|
+
diff_output, diff_error = await diff_proc.communicate()
|
|
516
|
+
|
|
517
|
+
if diff_proc.returncode == 0:
|
|
518
|
+
return {
|
|
519
|
+
"success": True,
|
|
520
|
+
"diff": diff_output.decode(),
|
|
521
|
+
"commit_hash": commit_hash,
|
|
522
|
+
"file_path": file_path,
|
|
523
|
+
"method": "timestamp_based",
|
|
524
|
+
"timestamp": timestamp
|
|
525
|
+
}
|
|
526
|
+
except Exception as e:
|
|
527
|
+
self.logger.warning(f"Failed to parse timestamp or find commits: {e}")
|
|
528
|
+
|
|
529
|
+
# Fallback: Get the most recent change to the file
|
|
530
|
+
log_proc = await asyncio.create_subprocess_exec(
|
|
531
|
+
'git', '-C', working_dir, 'log', '-1', '--oneline', '--', file_path,
|
|
532
|
+
stdout=asyncio.subprocess.PIPE,
|
|
533
|
+
stderr=asyncio.subprocess.PIPE
|
|
534
|
+
)
|
|
535
|
+
log_output, _ = await log_proc.communicate()
|
|
536
|
+
|
|
537
|
+
if log_proc.returncode == 0 and log_output:
|
|
538
|
+
commit_hash = log_output.decode().strip().split()[0]
|
|
539
|
+
|
|
540
|
+
# Get the diff for the most recent commit
|
|
541
|
+
diff_proc = await asyncio.create_subprocess_exec(
|
|
542
|
+
'git', '-C', working_dir, 'show', '--format=fuller', commit_hash, '--', file_path,
|
|
543
|
+
stdout=asyncio.subprocess.PIPE,
|
|
544
|
+
stderr=asyncio.subprocess.PIPE
|
|
545
|
+
)
|
|
546
|
+
diff_output, diff_error = await diff_proc.communicate()
|
|
547
|
+
|
|
548
|
+
if diff_proc.returncode == 0:
|
|
549
|
+
return {
|
|
550
|
+
"success": True,
|
|
551
|
+
"diff": diff_output.decode(),
|
|
552
|
+
"commit_hash": commit_hash,
|
|
553
|
+
"file_path": file_path,
|
|
554
|
+
"method": "latest_commit",
|
|
555
|
+
"timestamp": timestamp
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
# Try to show unstaged changes first
|
|
559
|
+
diff_proc = await asyncio.create_subprocess_exec(
|
|
560
|
+
'git', '-C', working_dir, 'diff', '--', file_path,
|
|
561
|
+
stdout=asyncio.subprocess.PIPE,
|
|
562
|
+
stderr=asyncio.subprocess.PIPE
|
|
563
|
+
)
|
|
564
|
+
diff_output, _ = await diff_proc.communicate()
|
|
565
|
+
|
|
566
|
+
if diff_proc.returncode == 0 and diff_output.decode().strip():
|
|
567
|
+
return {
|
|
568
|
+
"success": True,
|
|
569
|
+
"diff": diff_output.decode(),
|
|
570
|
+
"commit_hash": "unstaged_changes",
|
|
571
|
+
"file_path": file_path,
|
|
572
|
+
"method": "unstaged_changes",
|
|
573
|
+
"timestamp": timestamp
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
# Then try staged changes
|
|
577
|
+
diff_proc = await asyncio.create_subprocess_exec(
|
|
578
|
+
'git', '-C', working_dir, 'diff', '--cached', '--', file_path,
|
|
579
|
+
stdout=asyncio.subprocess.PIPE,
|
|
580
|
+
stderr=asyncio.subprocess.PIPE
|
|
581
|
+
)
|
|
582
|
+
diff_output, _ = await diff_proc.communicate()
|
|
583
|
+
|
|
584
|
+
if diff_proc.returncode == 0 and diff_output.decode().strip():
|
|
585
|
+
return {
|
|
586
|
+
"success": True,
|
|
587
|
+
"diff": diff_output.decode(),
|
|
588
|
+
"commit_hash": "staged_changes",
|
|
589
|
+
"file_path": file_path,
|
|
590
|
+
"method": "staged_changes",
|
|
591
|
+
"timestamp": timestamp
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
# Final fallback: Show changes against HEAD
|
|
595
|
+
diff_proc = await asyncio.create_subprocess_exec(
|
|
596
|
+
'git', '-C', working_dir, 'diff', 'HEAD', '--', file_path,
|
|
597
|
+
stdout=asyncio.subprocess.PIPE,
|
|
598
|
+
stderr=asyncio.subprocess.PIPE
|
|
599
|
+
)
|
|
600
|
+
diff_output, _ = await diff_proc.communicate()
|
|
601
|
+
|
|
602
|
+
if diff_proc.returncode == 0:
|
|
603
|
+
working_diff = diff_output.decode()
|
|
604
|
+
if working_diff.strip():
|
|
605
|
+
return {
|
|
606
|
+
"success": True,
|
|
607
|
+
"diff": working_diff,
|
|
608
|
+
"commit_hash": "working_directory",
|
|
609
|
+
"file_path": file_path,
|
|
610
|
+
"method": "working_directory",
|
|
611
|
+
"timestamp": timestamp
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
# Check if file is from a different repository
|
|
615
|
+
suggestions = [
|
|
616
|
+
"The file may not be tracked by git",
|
|
617
|
+
"The file may not have any committed changes",
|
|
618
|
+
"The timestamp may be outside the git history range"
|
|
619
|
+
]
|
|
620
|
+
|
|
621
|
+
if os.path.isabs(file_path) and not file_path.startswith(os.getcwd()):
|
|
622
|
+
current_repo = os.path.basename(os.getcwd())
|
|
623
|
+
file_repo = "unknown"
|
|
624
|
+
# Try to extract repository name from path
|
|
625
|
+
path_parts = file_path.split("/")
|
|
626
|
+
if "Projects" in path_parts:
|
|
627
|
+
idx = path_parts.index("Projects")
|
|
628
|
+
if idx + 1 < len(path_parts):
|
|
629
|
+
file_repo = path_parts[idx + 1]
|
|
630
|
+
|
|
631
|
+
suggestions.clear()
|
|
632
|
+
suggestions.append(f"This file is from the '{file_repo}' repository")
|
|
633
|
+
suggestions.append(f"The git diff viewer is running from the '{current_repo}' repository")
|
|
634
|
+
suggestions.append("Git diff can only show changes for files in the current repository")
|
|
635
|
+
suggestions.append("To view changes for this file, run the monitoring dashboard from its repository")
|
|
636
|
+
|
|
637
|
+
return {
|
|
638
|
+
"success": False,
|
|
639
|
+
"error": "No git history found for this file",
|
|
640
|
+
"file_path": file_path,
|
|
641
|
+
"suggestions": suggestions
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
finally:
|
|
645
|
+
os.chdir(original_cwd)
|
|
646
|
+
|
|
647
|
+
except Exception as e:
|
|
648
|
+
self.logger.error(f"Error in _generate_git_diff: {e}")
|
|
649
|
+
return {
|
|
650
|
+
"success": False,
|
|
651
|
+
"error": f"Git diff generation failed: {str(e)}",
|
|
652
|
+
"file_path": file_path
|
|
653
|
+
}
|
|
304
654
|
|
|
305
655
|
|
|
306
656
|
def _register_events(self):
|
|
@@ -406,6 +756,44 @@ class SocketIOServer:
|
|
|
406
756
|
|
|
407
757
|
# Re-broadcast to all other clients
|
|
408
758
|
await self.sio.emit('claude_event', data, skip_sid=sid)
|
|
759
|
+
|
|
760
|
+
@self.sio.event
|
|
761
|
+
async def get_git_branch(sid, working_dir=None):
|
|
762
|
+
"""Get the current git branch for a directory"""
|
|
763
|
+
import subprocess
|
|
764
|
+
try:
|
|
765
|
+
if not working_dir:
|
|
766
|
+
working_dir = os.getcwd()
|
|
767
|
+
|
|
768
|
+
# Run git command to get current branch
|
|
769
|
+
result = subprocess.run(
|
|
770
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
771
|
+
cwd=working_dir,
|
|
772
|
+
capture_output=True,
|
|
773
|
+
text=True
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
if result.returncode == 0:
|
|
777
|
+
branch = result.stdout.strip()
|
|
778
|
+
await self.sio.emit('git_branch_response', {
|
|
779
|
+
'success': True,
|
|
780
|
+
'branch': branch,
|
|
781
|
+
'working_dir': working_dir
|
|
782
|
+
}, room=sid)
|
|
783
|
+
else:
|
|
784
|
+
await self.sio.emit('git_branch_response', {
|
|
785
|
+
'success': False,
|
|
786
|
+
'error': 'Not a git repository',
|
|
787
|
+
'working_dir': working_dir
|
|
788
|
+
}, room=sid)
|
|
789
|
+
|
|
790
|
+
except Exception as e:
|
|
791
|
+
self.logger.error(f"Error getting git branch: {e}")
|
|
792
|
+
await self.sio.emit('git_branch_response', {
|
|
793
|
+
'success': False,
|
|
794
|
+
'error': str(e),
|
|
795
|
+
'working_dir': working_dir
|
|
796
|
+
}, room=sid)
|
|
409
797
|
|
|
410
798
|
async def _send_current_status(self, sid: str):
|
|
411
799
|
"""Send current system status to a client."""
|
|
@@ -695,4 +1083,4 @@ def stop_socketio_server():
|
|
|
695
1083
|
global _socketio_server
|
|
696
1084
|
if _socketio_server:
|
|
697
1085
|
_socketio_server.stop()
|
|
698
|
-
_socketio_server = None
|
|
1086
|
+
_socketio_server = None
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: claude-mpm
|
|
3
|
+
Version: 3.3.2
|
|
4
|
+
Summary: Claude Multi-agent Project Manager - Clean orchestration with ticket management
|
|
5
|
+
Home-page: https://github.com/bobmatnyc/claude-mpm
|
|
6
|
+
Author: Claude MPM Team
|
|
7
|
+
Author-email: bob@matsuoka.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: claude,orchestration,multi-agent,ticket-management
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: ai-trackdown-pytools>=1.4.0
|
|
23
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
|
+
Requires-Dist: python-dotenv>=0.19.0
|
|
25
|
+
Requires-Dist: rich>=13.0.0
|
|
26
|
+
Requires-Dist: click>=8.0.0
|
|
27
|
+
Requires-Dist: pexpect>=4.8.0
|
|
28
|
+
Requires-Dist: psutil>=5.9.0
|
|
29
|
+
Requires-Dist: requests>=2.25.0
|
|
30
|
+
Requires-Dist: flask>=3.0.0
|
|
31
|
+
Requires-Dist: flask-cors>=4.0.0
|
|
32
|
+
Requires-Dist: watchdog>=3.0.0
|
|
33
|
+
Requires-Dist: tree-sitter>=0.21.0
|
|
34
|
+
Requires-Dist: tree-sitter-language-pack>=0.8.0
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
39
|
+
Requires-Dist: black; extra == "dev"
|
|
40
|
+
Requires-Dist: flake8; extra == "dev"
|
|
41
|
+
Requires-Dist: mypy; extra == "dev"
|
|
42
|
+
Provides-Extra: monitor
|
|
43
|
+
Requires-Dist: python-socketio>=5.11.0; extra == "monitor"
|
|
44
|
+
Requires-Dist: aiohttp>=3.9.0; extra == "monitor"
|
|
45
|
+
Requires-Dist: python-engineio>=4.8.0; extra == "monitor"
|
|
46
|
+
Dynamic: author-email
|
|
47
|
+
Dynamic: home-page
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
Dynamic: requires-python
|
|
50
|
+
|
|
51
|
+
# Claude MPM - Multi-Agent Project Manager
|
|
52
|
+
|
|
53
|
+
A powerful orchestration framework for Claude Code that enables multi-agent workflows, session management, and real-time monitoring through an intuitive interface.
|
|
54
|
+
|
|
55
|
+
> **Quick Start**: See [QUICKSTART.md](QUICKSTART.md) to get running in 5 minutes!
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
- 🤖 **Multi-Agent System**: Automatically delegates tasks to specialized agents (PM, Research, Engineer, QA, Documentation, Security, Ops, Data Engineer)
|
|
60
|
+
- 🔄 **Session Management**: Resume previous sessions with `--resume`
|
|
61
|
+
- 📊 **Real-Time Monitoring**: Live dashboard with `--monitor` flag
|
|
62
|
+
- 📁 **Multi-Project Support**: Per-session working directories
|
|
63
|
+
- 🔍 **Git Integration**: View diffs and track changes across projects
|
|
64
|
+
- 🎯 **Smart Task Orchestration**: PM agent intelligently routes work to specialists
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Install from PyPI
|
|
70
|
+
pip install claude-mpm
|
|
71
|
+
|
|
72
|
+
# Or with monitoring support
|
|
73
|
+
pip install "claude-mpm[monitor]"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Basic Usage
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Interactive mode (recommended)
|
|
80
|
+
claude-mpm
|
|
81
|
+
|
|
82
|
+
# Non-interactive with task
|
|
83
|
+
claude-mpm run -i "analyze this codebase" --non-interactive
|
|
84
|
+
|
|
85
|
+
# With monitoring dashboard
|
|
86
|
+
claude-mpm run --monitor
|
|
87
|
+
|
|
88
|
+
# Resume last session
|
|
89
|
+
claude-mpm run --resume
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
For detailed usage, see [QUICKSTART.md](QUICKSTART.md)
|
|
93
|
+
|
|
94
|
+
## Key Capabilities
|
|
95
|
+
|
|
96
|
+
### Multi-Agent Orchestration
|
|
97
|
+
The PM agent automatically delegates work to specialized agents:
|
|
98
|
+
- **Research**: Codebase analysis and investigation
|
|
99
|
+
- **Engineer**: Implementation and coding
|
|
100
|
+
- **QA**: Testing and validation
|
|
101
|
+
- **Documentation**: Docs and guides
|
|
102
|
+
- **Security**: Security analysis
|
|
103
|
+
- **Ops**: Deployment and infrastructure
|
|
104
|
+
|
|
105
|
+
### Session Management
|
|
106
|
+
- All work is tracked in persistent sessions
|
|
107
|
+
- Resume any session with `--resume`
|
|
108
|
+
- Switch between projects with per-session directories
|
|
109
|
+
- View session history and activity
|
|
110
|
+
|
|
111
|
+
### Real-Time Monitoring
|
|
112
|
+
The `--monitor` flag opens a web dashboard showing:
|
|
113
|
+
- Live agent activity and delegations
|
|
114
|
+
- File operations with git diff viewer
|
|
115
|
+
- Tool usage and results
|
|
116
|
+
- Session management UI
|
|
117
|
+
|
|
118
|
+
See [docs/monitoring.md](docs/monitoring.md) for full monitoring guide.
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
## Documentation
|
|
122
|
+
|
|
123
|
+
- **[Quick Start Guide](QUICKSTART.md)** - Get running in 5 minutes
|
|
124
|
+
- **[Monitoring Dashboard](docs/monitoring.md)** - Real-time monitoring features
|
|
125
|
+
- **[Project Structure](docs/STRUCTURE.md)** - Codebase organization
|
|
126
|
+
- **[Deployment Guide](docs/DEPLOY.md)** - Publishing and versioning
|
|
127
|
+
- **[User Guide](docs/user/)** - Detailed usage documentation
|
|
128
|
+
- **[Developer Guide](docs/developer/)** - Architecture and API reference
|
|
129
|
+
|
|
130
|
+
## Recent Updates (v3.3.1)
|
|
131
|
+
|
|
132
|
+
### Session Working Directories
|
|
133
|
+
- Each session can now have its own working directory
|
|
134
|
+
- Git operations use the session's directory automatically
|
|
135
|
+
- Switch projects seamlessly without changing terminal directory
|
|
136
|
+
|
|
137
|
+
### Monitoring Improvements
|
|
138
|
+
- Fixed git diff viewer for cross-project files
|
|
139
|
+
- Auto-sync working directory from session data
|
|
140
|
+
- Improved error handling and display
|
|
141
|
+
|
|
142
|
+
See [CHANGELOG.md](CHANGELOG.md) for full history.
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
### Contributing
|
|
147
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
148
|
+
|
|
149
|
+
### Project Structure
|
|
150
|
+
See [docs/STRUCTURE.md](docs/STRUCTURE.md) for codebase organization.
|
|
151
|
+
|
|
152
|
+
### License
|
|
153
|
+
MIT License - see [LICENSE](LICENSE) file.
|
|
154
|
+
|
|
155
|
+
## Credits
|
|
156
|
+
|
|
157
|
+
- Based on [claude-multiagent-pm](https://github.com/kfsone/claude-multiagent-pm)
|
|
158
|
+
- Enhanced for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) integration
|
|
159
|
+
- Built with ❤️ by the Claude MPM community
|