claude-mpm 3.4.0__py3-none-any.whl → 3.4.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.
@@ -294,7 +294,7 @@ class SocketIOServer:
294
294
  self.app.router.add_get('/dashboard', self._handle_dashboard)
295
295
 
296
296
  # Add static file serving for web assets
297
- static_path = get_project_root() / 'src' / 'claude_mpm' / 'web' / 'static'
297
+ static_path = get_project_root() / 'src' / 'claude_mpm' / 'dashboard' / 'static'
298
298
  if static_path.exists():
299
299
  self.app.router.add_static('/static/', path=str(static_path), name='static')
300
300
 
@@ -348,7 +348,7 @@ class SocketIOServer:
348
348
 
349
349
  async def _handle_dashboard(self, request):
350
350
  """Serve the dashboard HTML file."""
351
- dashboard_path = get_project_root() / 'src' / 'claude_mpm' / 'web' / 'templates' / 'index.html'
351
+ dashboard_path = get_project_root() / 'src' / 'claude_mpm' / 'dashboard' / 'templates' / 'index.html'
352
352
  self.logger.info(f"Dashboard requested, looking for: {dashboard_path}")
353
353
  self.logger.info(f"Path exists: {dashboard_path.exists()}")
354
354
  if dashboard_path.exists():
@@ -648,8 +648,13 @@ class SocketIOServer:
648
648
  working_dir = file_dir
649
649
  self.logger.info(f"No git repo found, using file's directory: {working_dir}")
650
650
 
651
- if working_dir is None:
651
+ # Handle case where working_dir is None, empty string, or 'Unknown'
652
+ original_working_dir = working_dir
653
+ if not working_dir or working_dir == 'Unknown' or working_dir.strip() == '':
652
654
  working_dir = os.getcwd()
655
+ self.logger.info(f"[GIT-DIFF-DEBUG] working_dir was invalid ({repr(original_working_dir)}), using cwd: {working_dir}")
656
+ else:
657
+ self.logger.info(f"[GIT-DIFF-DEBUG] Using provided working_dir: {working_dir}")
653
658
 
654
659
  # For read-only git operations, we can work from any directory
655
660
  # by passing the -C flag to git commands instead of changing directories
@@ -999,32 +1004,47 @@ class SocketIOServer:
999
1004
  try:
1000
1005
  self.logger.info(f"[GIT-BRANCH-DEBUG] get_git_branch called with working_dir: {repr(working_dir)} (type: {type(working_dir)})")
1001
1006
 
1002
- # Handle case where working_dir is None, empty string, or 'Unknown'
1007
+ # Handle case where working_dir is None, empty string, or common invalid states
1003
1008
  original_working_dir = working_dir
1004
- if not working_dir or working_dir == 'Unknown' or working_dir.strip() == '':
1009
+ invalid_states = [
1010
+ None, '', 'Unknown', 'Loading...', 'Loading', 'undefined', 'null',
1011
+ 'Not Connected', 'Invalid Directory', 'No Directory'
1012
+ ]
1013
+
1014
+ if working_dir in invalid_states or (isinstance(working_dir, str) and working_dir.strip() == ''):
1005
1015
  working_dir = os.getcwd()
1006
1016
  self.logger.info(f"[GIT-BRANCH-DEBUG] working_dir was invalid ({repr(original_working_dir)}), using cwd: {working_dir}")
1007
1017
  else:
1008
1018
  self.logger.info(f"[GIT-BRANCH-DEBUG] Using provided working_dir: {working_dir}")
1019
+
1020
+ # Additional validation for obviously invalid paths
1021
+ if isinstance(working_dir, str):
1022
+ working_dir = working_dir.strip()
1023
+ # Check for null bytes or other invalid characters
1024
+ if '\x00' in working_dir:
1025
+ self.logger.warning(f"[GIT-BRANCH-DEBUG] working_dir contains null bytes, using cwd instead")
1026
+ working_dir = os.getcwd()
1009
1027
 
1010
1028
  # Validate that the directory exists and is a valid path
1011
1029
  if not os.path.exists(working_dir):
1012
- self.logger.warning(f"[GIT-BRANCH-DEBUG] Directory does not exist: {working_dir}")
1030
+ self.logger.info(f"[GIT-BRANCH-DEBUG] Directory does not exist: {working_dir} - responding gracefully")
1013
1031
  await self.sio.emit('git_branch_response', {
1014
1032
  'success': False,
1015
- 'error': f'Directory does not exist: {working_dir}',
1033
+ 'error': f'Directory not found',
1016
1034
  'working_dir': working_dir,
1017
- 'original_working_dir': original_working_dir
1035
+ 'original_working_dir': original_working_dir,
1036
+ 'detail': f'Path does not exist: {working_dir}'
1018
1037
  }, room=sid)
1019
1038
  return
1020
1039
 
1021
1040
  if not os.path.isdir(working_dir):
1022
- self.logger.warning(f"[GIT-BRANCH-DEBUG] Path is not a directory: {working_dir}")
1041
+ self.logger.info(f"[GIT-BRANCH-DEBUG] Path is not a directory: {working_dir} - responding gracefully")
1023
1042
  await self.sio.emit('git_branch_response', {
1024
1043
  'success': False,
1025
- 'error': f'Path is not a directory: {working_dir}',
1044
+ 'error': f'Not a directory',
1026
1045
  'working_dir': working_dir,
1027
- 'original_working_dir': original_working_dir
1046
+ 'original_working_dir': original_working_dir,
1047
+ 'detail': f'Path is not a directory: {working_dir}'
1028
1048
  }, room=sid)
1029
1049
  return
1030
1050
 
@@ -1140,6 +1160,163 @@ class SocketIOServer:
1140
1160
  'file_path': data.get('file_path', 'unknown')
1141
1161
  }, room=sid)
1142
1162
 
1163
+ @self.sio.event
1164
+ async def check_git_status(sid, data):
1165
+ """Check git status for a file to determine if git diff icons should be shown"""
1166
+ import subprocess
1167
+ try:
1168
+ file_path = data.get('file_path')
1169
+ working_dir = data.get('working_dir', os.getcwd())
1170
+
1171
+ self.logger.info(f"[GIT-STATUS-DEBUG] check_git_status called with file_path: {repr(file_path)}, working_dir: {repr(working_dir)}")
1172
+
1173
+ if not file_path:
1174
+ await self.sio.emit('git_status_response', {
1175
+ 'success': False,
1176
+ 'error': 'file_path is required',
1177
+ 'file_path': file_path
1178
+ }, room=sid)
1179
+ return
1180
+
1181
+ # Validate and sanitize working_dir
1182
+ original_working_dir = working_dir
1183
+ if not working_dir or working_dir == 'Unknown' or working_dir.strip() == '' or working_dir == '.':
1184
+ working_dir = os.getcwd()
1185
+ self.logger.info(f"[GIT-STATUS-DEBUG] working_dir was invalid ({repr(original_working_dir)}), using cwd: {working_dir}")
1186
+ else:
1187
+ self.logger.info(f"[GIT-STATUS-DEBUG] Using provided working_dir: {working_dir}")
1188
+
1189
+ # Check if the working directory exists and is a directory
1190
+ if not os.path.exists(working_dir):
1191
+ self.logger.warning(f"[GIT-STATUS-DEBUG] Directory does not exist: {working_dir}")
1192
+ await self.sio.emit('git_status_response', {
1193
+ 'success': False,
1194
+ 'error': f'Directory does not exist: {working_dir}',
1195
+ 'file_path': file_path,
1196
+ 'working_dir': working_dir,
1197
+ 'original_working_dir': original_working_dir
1198
+ }, room=sid)
1199
+ return
1200
+
1201
+ if not os.path.isdir(working_dir):
1202
+ self.logger.warning(f"[GIT-STATUS-DEBUG] Path is not a directory: {working_dir}")
1203
+ await self.sio.emit('git_status_response', {
1204
+ 'success': False,
1205
+ 'error': f'Path is not a directory: {working_dir}',
1206
+ 'file_path': file_path,
1207
+ 'working_dir': working_dir,
1208
+ 'original_working_dir': original_working_dir
1209
+ }, room=sid)
1210
+ return
1211
+
1212
+ # Check if this is a git repository
1213
+ self.logger.info(f"[GIT-STATUS-DEBUG] Checking if {working_dir} is a git repository")
1214
+ git_check = subprocess.run(
1215
+ ["git", "-C", working_dir, "rev-parse", "--git-dir"],
1216
+ capture_output=True,
1217
+ text=True
1218
+ )
1219
+
1220
+ if git_check.returncode != 0:
1221
+ self.logger.info(f"[GIT-STATUS-DEBUG] Not a git repository: {working_dir}")
1222
+ await self.sio.emit('git_status_response', {
1223
+ 'success': False,
1224
+ 'error': 'Not a git repository',
1225
+ 'file_path': file_path,
1226
+ 'working_dir': working_dir,
1227
+ 'original_working_dir': original_working_dir
1228
+ }, room=sid)
1229
+ return
1230
+
1231
+ # Determine if the file path should be made relative to git root
1232
+ file_path_for_git = file_path
1233
+ if os.path.isabs(file_path):
1234
+ # Get git root to make path relative if needed
1235
+ git_root_result = subprocess.run(
1236
+ ["git", "-C", working_dir, "rev-parse", "--show-toplevel"],
1237
+ capture_output=True,
1238
+ text=True
1239
+ )
1240
+
1241
+ if git_root_result.returncode == 0:
1242
+ git_root = git_root_result.stdout.strip()
1243
+ try:
1244
+ file_path_for_git = os.path.relpath(file_path, git_root)
1245
+ self.logger.info(f"[GIT-STATUS-DEBUG] Made file path relative to git root: {file_path_for_git}")
1246
+ except ValueError:
1247
+ # File is not under git root - keep original path
1248
+ self.logger.info(f"[GIT-STATUS-DEBUG] File not under git root, keeping original path: {file_path}")
1249
+ pass
1250
+
1251
+ # Check if the file exists
1252
+ full_path = file_path if os.path.isabs(file_path) else os.path.join(working_dir, file_path)
1253
+ if not os.path.exists(full_path):
1254
+ self.logger.warning(f"[GIT-STATUS-DEBUG] File does not exist: {full_path}")
1255
+ await self.sio.emit('git_status_response', {
1256
+ 'success': False,
1257
+ 'error': f'File does not exist: {file_path}',
1258
+ 'file_path': file_path,
1259
+ 'working_dir': working_dir,
1260
+ 'original_working_dir': original_working_dir
1261
+ }, room=sid)
1262
+ return
1263
+
1264
+ # Check git status for the file - this succeeds if git knows about the file
1265
+ # (either tracked, modified, staged, etc.)
1266
+ self.logger.info(f"[GIT-STATUS-DEBUG] Checking git status for file: {file_path_for_git}")
1267
+ git_status_result = subprocess.run(
1268
+ ["git", "-C", working_dir, "status", "--porcelain", file_path_for_git],
1269
+ capture_output=True,
1270
+ text=True
1271
+ )
1272
+
1273
+ self.logger.info(f"[GIT-STATUS-DEBUG] Git status result: returncode={git_status_result.returncode}, stdout={repr(git_status_result.stdout)}, stderr={repr(git_status_result.stderr)}")
1274
+
1275
+ # Also check if file is tracked by git (alternative approach)
1276
+ ls_files_result = subprocess.run(
1277
+ ["git", "-C", working_dir, "ls-files", file_path_for_git],
1278
+ capture_output=True,
1279
+ text=True
1280
+ )
1281
+
1282
+ is_tracked = ls_files_result.returncode == 0 and ls_files_result.stdout.strip()
1283
+ has_status = git_status_result.returncode == 0
1284
+
1285
+ self.logger.info(f"[GIT-STATUS-DEBUG] File tracking status: is_tracked={is_tracked}, has_status={has_status}")
1286
+
1287
+ # Success if git knows about the file (either tracked or has status changes)
1288
+ if is_tracked or has_status:
1289
+ self.logger.info(f"[GIT-STATUS-DEBUG] Git status check successful for {file_path}")
1290
+ await self.sio.emit('git_status_response', {
1291
+ 'success': True,
1292
+ 'file_path': file_path,
1293
+ 'working_dir': working_dir,
1294
+ 'original_working_dir': original_working_dir,
1295
+ 'is_tracked': is_tracked,
1296
+ 'has_changes': bool(git_status_result.stdout.strip()) if has_status else False
1297
+ }, room=sid)
1298
+ else:
1299
+ self.logger.info(f"[GIT-STATUS-DEBUG] File {file_path} is not tracked by git")
1300
+ await self.sio.emit('git_status_response', {
1301
+ 'success': False,
1302
+ 'error': 'File is not tracked by git',
1303
+ 'file_path': file_path,
1304
+ 'working_dir': working_dir,
1305
+ 'original_working_dir': original_working_dir,
1306
+ 'is_tracked': False
1307
+ }, room=sid)
1308
+
1309
+ except Exception as e:
1310
+ self.logger.error(f"[GIT-STATUS-DEBUG] Exception in check_git_status: {e}")
1311
+ import traceback
1312
+ self.logger.error(f"[GIT-STATUS-DEBUG] Stack trace: {traceback.format_exc()}")
1313
+ await self.sio.emit('git_status_response', {
1314
+ 'success': False,
1315
+ 'error': str(e),
1316
+ 'file_path': data.get('file_path', 'unknown'),
1317
+ 'working_dir': data.get('working_dir', 'unknown')
1318
+ }, room=sid)
1319
+
1143
1320
  @self.sio.event
1144
1321
  async def git_add_file(sid, data):
1145
1322
  """Add file to git tracking"""